Skip to content

overworld spawns

Chris Frantz edited this page Jan 1, 2019 · 1 revision

Understanding Overworld Spawns

Misc Background Info

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.

Data Tables

There are a number of data tables which are used in calculating how to spawn the overword "demons".

Position Information

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

Tables Indexed on Terrain type

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

Code Analysis

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

Clone this wiki locally