-
Notifications
You must be signed in to change notification settings - Fork 3
overworld spawns
Zelda 2 drives many if its internal state machines by timers which are clocked by the NMI handler.
There is a block of fast timers at $501 - $50d which are clocked
by the NMI handler on every frame.
There is also a block of slow timers at $50e - $519 which are clocked by
the NMI handler every 20 frames.
There is a 9-byte linear feedback shift register (LFSR) at $51a - $523
which is the source of RNG in the game. The RNG is clocked on every frame.
The feedback bit is calculated as not (($51a bit 1) xor ($51b bit 1)),
and this bit is added at the MSB of $51a.
There are a number of data tables which are used in calculating how to spawn the overword "demons".
When enemies spawn on the overworld, there is a near and far spawn. The
positions are encoded in a table at $8229. If you think of the overworld
playfield as a 16x15 grid with Link at the center, then the high nybble
encodes the Y coordinate, while the low nybble encodes the X coordinate.
tables_overworld_demons
near_table:
.byt $58,$76,$98,$7A
far_table:
.byte $38,$74,$B8,$7C
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ||||||||||||||||
| 1 | ||||||||||||||||
| 2 | ||||||||||||||||
| 3 | F | |||||||||||||||
| 4 | ||||||||||||||||
| 5 | N | |||||||||||||||
| 6 | ||||||||||||||||
| 7 | F | N | N | F | ||||||||||||
| 8 | ||||||||||||||||
| 9 | N | |||||||||||||||
| A | ||||||||||||||||
| B | F | |||||||||||||||
| C | ||||||||||||||||
| D | ||||||||||||||||
| E |
The table at $8231 is used to find the index for the terrain type. The
index is typically held in the Y register and used to look up other per-terrain
parameters.
; Index of terrain types:
; xx, grass, desert, forest, swamp, grave, lava
L8231
.byt $00,$05,$04,$06,$07,$08,$0A
The table at $8238 encodes whether the spawn for a given terrain type
should be a near or far spawn.
; xx,grass=far, desert=far, forest=near, swamp=far, grave=near, lava=near
L8238
.byt $00,$01,$01,$00,$01,$00,$00
The table at $823f encodes the spawn timers per terrain type. This time
the length of time between spawns. The timer is in the slow timer group,
so these values represent time in thirds of a second.
; Terrain type to spawn timer values
; xx, grass, desert, forest, swamp, grave, lava
L823F
.byt $00,$20,$18,$18,$20,$09,$03
The table at $8246 encodes the spawn time-to-live per terrain type. This
is the length of time until the spawns disappear from the overworld. These
timers are in the slow timer group, so these values encode thirds of a
second.
; Terrain type to movement timers values(?)
; xx, grass, desert, forest, swamp, grave, lava
L8246
.byt $00,$0A,$0A,$18,$18,$30,$30
The table at $824d encodes the spawn pattern probabilities for each of the
terrain types. The game uses an RNG byte to check against these values.
; Timer values for spawn probabilties
; Probabilities for each slot are calculated by subtracting from the next
; higher value (or 256 if there isn't a next higher value) and dividing
; by 256:
; (k1-k0)/256, (k2-k1)/256, (k3-k2)/256, (256-k3)/256
L824D ;
grass: ; 0.375, 0.3125, 0.125, 0.1875
.byt $00,$60,$B0,$D0
desert: ; 0.375, 0.4375, 0.125, 0.0625
.byt $00,$60,$D0,$F0
forest: ; 0.375, 0.375, 0.125, 0.125
.byt $00,$60,$C0,$E0
swamp: ; 0.3125, 0.41796, 0.20703, 0.0625
.byt $00,$50,$BB,$F0
grave: ; 0.33984, 0.5, 0.1289, 0.03125
.byt $00,$57,$D7,$F8
lava: ; 0.33984, 0.5, 0.15625, 0.0039
.byt $00,$57,$D7,$FF
The spawn patterns are encoded as which demon to spawn in each of the positions (North, West, South, East):
L8265
;01 = Weak Demon
;02 = Strong Demon
;03 = Fairy
; N W S E
.byt $01,$02,$01,$01
.bye $01,$01,$01,$02
.byt $02,$01,$02,$01
.byt $01,$03,$01,$03
The original disassembly was by LemmyPi. I've re-commented it.
This first block of code determines if it is time to spawn, and whether we're on a terrain type which has spawns.
overworld1
lda $0706 ; Current region (0=West Hy, 1=DM/MZ, 2=East)
bne L828F ; Not region 0, regular spawns
lda $73 ; In region 0, are we in the north?
cmp #$3C ;
bcc L8293 ; Yes, timer spawns only.
L828F
lda $26 ; Get step counter
beq L8298 ; If zero, spawn
L8293
lda $0516 ; Check spawn timer
bne L82AF ; If zero, spawn
L8298
lda $73 ; Link Y
sta $01 ;
lda $74 ; Link X
sta $00 ;
jsr bank7_Overworld_Boundaries__Mountain_or_Water_Bank_1
lda $02 ; Terrain type into A
ldy #$06 ; terrain types length = 6 bytes
L82A7 ;
cmp L8231,y ; Are we on terrain type at Y
beq L82B0 ; Yes, spawn
dey ;
bne L82A7 ; Check next value
L82AF
rts ; No spawn, return
Having determined that we should spawn, reload the spawn timer and get the RNG values which will control the spawn.
The RNG value from $51C will control which of the 4 spawn positions
will be empty (spawns are always 3 demons, but in a random configuration
around link).
The RNG value from $51B selects the demon configuration via the probability
table at $824d.
L82B0
lda $26 ; Step value is zero?
beq L82BA ;
lda L823F,y ; No: reload spawn timer based on terrain type
sta $0516 ; Save timer value
L82BA
lda $051C ; Get RNG value
and #$03 ; value &= 3
sta $00 ; save
sty $0B ; save terrain type in $B
lda L8238,y ; Get near or far spawn value, based on terrain.
asl ; value = (value * 4) + 3
asl ;
ora #$03 ;
sta $09 ; save
ldx #$07 ; X = 7 (max possible spawns?)
stx $80 ; Spawn number/index
lda #$03 ; $A = 3
sta $0A ;
tya ; Terrain index into A
asl ;
asl ; Y = (terrain index * 4) - 1
tay ; Cuz table at $824D is 24 bytes long
dey ;
L82D9
lda $051B ; Get RNG vlaue
cmp L824D,y ; Cmp with table (index on terrain type)
bcs L82E4 ; if value >= table, got our index
dey ;
bne L82D9 ; Check next table value
L82E4
tya ;
and #$03 ;
asl ;
asl ;
ora #$03 ; Table index = (index & 3) * 4 + 3
sta $08 ; save
L82ED
lda $82,x ; Find an empty spawn slot
beq L82F7 ; empty: spawn
dec $80 ; decrement index
dex ;
bpl L82ED ; index = 0?
rts ; yes: no slots, no spawn.
Now that we know the spawn configuration, create the enemy list and set up
the X/Y coordinates. As mentioned earlier, when the random value from
$51c (now stored at $00) equals the spawn position index (in $0a),
that spawn will be skipped, thus creating the four different 3-spawn
positions we're all familiar with.
The spawn configuration is selected from the table at $8265 via the
probability selection in the previous step.
L82F7
lda $00 ; Get previously saved RNG value
cmp $0A ; If equal to value in $a, skip spawn
beq L8324 ;
ldy $09 ; Spawn pos index?
lda tables_overworld_demons,y ; Get position
and #$F0 ; Y coordinate in high nybble.
clc ;
adc $7F ;
sta $2A,x ; Save Y position.
lda tables_overworld_demons,y ; Get position
asl ; Shift X coord into high nybble.
asl ;
asl ;
asl ;
clc ;
adc $FD ;
sta $4E,x ; Save X position.
ldy $0B ; Reload terrain type into Y
lda L8246,y ; Get movement timer(?)
sta $050E,x ; Save timer value
ldy $08 ; Reload spawn probability table index
lda L8265,y ; convert to spawn type (1=weak, 2=strong, 3=fairy).
sta $82,x ; Save in spawn type slot
L8324
dec $08 ; Decrement spawn indicies
dec $09 ;
dec $0A ;
bmi L8331 ; negative? done with spawning
dex ; decrement spawn spawn slot index
stx $80 ; save
bpl L82ED ; spawn again
L8331
rts ; return