31 July 2026: MainInit, the hardware setup
MainInit at $FD:C000 is matched: the 215 bytes the SNES runs right after the reset stub to put the hardware into a known state. The project now stands at 471 matched bytes.
What MainInit does
Out of reset the hardware is in a loosely defined state. MainInit puts everything into a safe, known setup before the game runs:
- CPU mode. Sets A to 8-bit (
SEP #$20) and X/Y to 16-bit (REP #$10), puts the stack pointer at$06FF(about 1.75 KB of stack below the NMI handler area), zeroes the data bank (PLB) and puts the Direct Page at$4200, the CPU register area. - CPU registers. Turns on FastROM (
MEMSEL = $01, which raises ROM access speed from 2.68 MHz to 3.58 MHz). Turns off NMI, IRQ and DMA. Zeroes the multiply and divide registers. - PPU setup. Moves the Direct Page to
$2100, the PPU register area, so every PPU write can use a short 2-byte direct-page instruction instead of a 3-byte absolute one. Then the full PPU init: forced blank on; sprites set up (8×8 and 16×16 sizes, base tile 0 with a gap of 3 pages); Mode 1 backgrounds; all scroll registers zeroed; VRAM mode; Mode 7 identity matrix; window registers; all background and sprite layers enabled; color data register; display size. - Jump to GameLoop. The last instruction is
JML $C0:000E, a 24-bit jump toGameLoopin bank$C0.
Power-on to GameLoop: the RESET vector, the 7-byte reset stub, the 215 bytes of MainInit, and the long jump to bank $C0.
Display Mode 1
BGMODE is set to $09: Mode 1 plus BG3 high priority. Mode 1 gives three tiled backgrounds. Giving BG3 high priority is the usual Mode 1 trick for drawing text and UI on top of everything else, sprites included.
Matching with direct-page writes
Bank $00 used absolute register addresses ($2100, $4200 and so on). MainInit doesn't. It sets the Direct Page to $2100 and then uses short direct-page addresses, so STA $00 with DP=$2100 writes to $2100 = INIDISP. That is smaller (2 bytes instead of 3), and it is why the original PPU init is so compact.
With the Direct Page pointed at a register block, a 2-byte store reaches the hardware: DP $4200 plus $0D is MEMSEL, DP $2100 plus $00 is INIDISP. The bytes are the ones in MainInit.
For matching, that means using the direct-page offsets ($00, $01, $02...) instead of the named constants from hardware.inc. The register names go in the comments.
An include guard for hardware.inc
Adding bankFD.asm to main.asm produced a "label redefined" error. Both bank00.asm and bankFD.asm include hardware.inc, so asar saw every register name twice.
The fix is an include guard, the same idea as #pragma once in C:
!hardware_inc ?= 0
if !hardware_inc == 0
!hardware_inc = 1
; ... all the register definitions ...
endif
?= sets the variable only if it isn't set yet. After the first include !hardware_inc = 1, and later includes skip the whole block.
Result
bankFD.asm matched byte-exact on the first try once the direct-page addressing was right. make diff reports 4,194,304 bytes identical. Total matched: 256 (bank $00) + 215 (bank $FD) = 471 bytes.
Next
The last instruction of MainInit, JML $C0:000E, points at GameLoop. That is the next target.