Cross Sections - brunovalads/yoshisisland-disassembly GitHub Wiki

Cross sections are graphical overlays over terrain to hide the contents inside, giving a "cavelike" effect. They can be found in stages 1-2 and 2-2, for example. The overlays turn transparent when Yoshi enters the cave area, and they turn back on upon exit (with a brief transitional period).
The overlay is on BG3 in 16x16 tile mode. Only BG3 tileset # $0A can activate them. If this is indeed the tileset, then they are loaded in on a per-column or per-row basis. That is, as each MAP16 column/row is spawned in, along with it, cross section tiles get loaded in as well. The routine at $108C9A takes care of this process; the first two conditions it checks are:
LDA $013E ; $108C9A |\
CMP #$0A ; $108C9D | | BG3 Tileset $0A
BEQ .check_new_spawn ; $108C9F |/ (Cross section)
.ret
RTL ; $108CA1 | if not, return
.check_new_spawn
LDA $77 ; $108CA2 |\ if neither new column nor
ORA $79 ; $108CA4 | | new row has been spawned,
BEQ .ret ; $108CA6 |/ return
$0077 and $0079 are flags that say "a new MAP16 column has been spawned", or row, respectively. Basically, when a new piece of the stage loads, also keep these cross section tiles synchronized to that as well.
Much like $700DAA and $700E2A, which hold VRAM tilemap information for the most recently loaded MAP16 column & row, the $702604 and $702644 tables hold tilemaps for the most recently loaded column & row of cross section tiles. In fact, these MAP16 column & row tables are used as a basis to govern which cross section tiles to display in BG3. The game iterates through both MAP16 and cross section new column/row tables simultaneously. Since BG1 foreground is 8x8 tiles, and in this context our cross section BG3 is using 16x16 tiles, this means that the game must iterate twice the amount when going through the BG1 8x8 tiles to move on to the next tile in the column/row. Since there are 4 8x8 tiles per BG3's 16x16, which one of the 4 does it use as the basis? The top left 8x8 tile out of the 4 in the MAP16 tile is the one used to govern which cross section tile to display.
First, the game reads the top-left tile's value, and masks it to get only the tile number:
to r10 ; $08BC50 |
add r8 ; $08BC51 | r10 = $2644 + tile
ldw (r1) ; $08BC52 |\ r11 = load top left 8x8 tile
move r11,r0 ; $08BC53 |/ from new row table
and r3 ; $08BC55 | mask tilemap to get tile # only
Then it tests whether the tile # is in between $0180 and $0200, and if not, load $01CE (which is just a blank tile). This is done because not every tile needs a cross section overlay, only some. This is the range that needs them:
bcc .store_blank_tile_row ; $08BC57 | | if tile > $0200 or tile < $0180
sub r5 ; $08BC59 | | then load $01CE (blank tile)
bcs .store_tile_row ; $08BC5A | |
from r9 ; $08BC5C |/
If we are in fact within the proper range, then the tile number for the cross section is determined by the MAP16 tile number masked with $007F, so the first 7 bits. We also take the vertical/horizontal flipping information directly from the MAP16 tilemap entry, leaving us with a tilemap like this: vh1000010ttttttt, where v and h and the first 7 bits of t come directly from the corresponding top-left 8x8 MAP16 tile, and the rest is priority bit set and palette = 000:
and r6 ; $08BC5E | | new row tilemap entry
bra .store_tile_row ; $08BC5F | | & $C07F | $2100
or r7 ; $08BC61 |/ vh1000010ttttttt
This tilemap information gets stored to the aforementioned cross-section tables, $702604 and $702644, for the entire new column or row. From there, they get added to the general-purpose DMA queue at $7E4800 to be DMA'd into the proper column or row of BG3 tilemaps, which begins at VRAM address $3400.