308 lines
6.8 KiB
NASM
308 lines
6.8 KiB
NASM
|
|
SECTION "rst00", ROM0[$0000]
|
|
; Please do not call
|
|
; Traps execution errors (mostly to $FFFF / $0000)
|
|
rst00:
|
|
; Pad, in case we come from FFFF and read a 2-byte operand
|
|
nop
|
|
nop
|
|
jp NullExecError
|
|
|
|
SECTION "rst08", ROM0[$0008]
|
|
; Please call using `rst memcpy_small`
|
|
; Copies c bytes of data from de to hl
|
|
MemcpySmall:
|
|
ld a, [de]
|
|
ld [hli], a
|
|
inc de
|
|
dec c
|
|
jr nz, MemcpySmall
|
|
EmptyFunc::
|
|
ret
|
|
|
|
SECTION "rst10", ROM0[$0010]
|
|
; Please call using `rst memset_small`
|
|
; Sets c bytes at hl with the value in a
|
|
MemsetSmall:
|
|
ld [hli], a
|
|
dec c
|
|
jr nz, MemsetSmall
|
|
ret
|
|
|
|
SECTION "rst18", ROM0[$0017]
|
|
; Please do not call. Use `rst memset`, or, if absolutely needed, `call rst18`.
|
|
; Sets bc bytes at hl with the value in d
|
|
Memset:
|
|
ld a, d
|
|
; Please call using `rst memset`
|
|
; Sets bc bytes at hl with the value in a
|
|
rst18:
|
|
ld d, a
|
|
ld [hli], a
|
|
dec bc
|
|
ld a, b
|
|
or c
|
|
jr nz, Memset
|
|
ret
|
|
|
|
SECTION "rst20", ROM0[$0020]
|
|
; Please call using `rst bankswitch`
|
|
; Properly switches to a ROM bank
|
|
; @param a The ROM bank to switch to
|
|
; NOTE: only switches the lower 8 bytes, the upper bit (for 512 banks) is not considered
|
|
ROMbankswitch:
|
|
ldh [hCurROMBank], a
|
|
ld [rROMB0], a
|
|
ret
|
|
|
|
SECTION "rst28", ROM0[$0028]
|
|
; Please call using `rst call_hl`
|
|
; Jumps to hl. Use as a placeholder for `call hl`!
|
|
; Will error out if the target is in RAM
|
|
CallHL:
|
|
bit 7, h ; Prevent jumping into RAM (doesn't protec against returning to it, but hey :D)
|
|
jr nz, .err
|
|
jp hl
|
|
.err
|
|
jp HLJumpingError
|
|
|
|
SECTION "rst30", ROM0[$0030]
|
|
; Please call using `rst wait_vblank`
|
|
; Waits for the VBlank interrupt
|
|
; Note: if the interrupt occurs without being waited for, it will skip performing some actions
|
|
WaitVBlank:
|
|
xor a
|
|
ldh [hVBlankFlag], a
|
|
.waitVBlank
|
|
halt
|
|
jr z, .waitVBlank
|
|
ret
|
|
|
|
SECTION "rst38", ROM0[$0038]
|
|
; Please do not call
|
|
; Traps execution of the $FF byte (which serves as padding of the ROM)
|
|
rst38:
|
|
jp Rst38Error
|
|
|
|
|
|
SECTION "Interrupt vectors", ROM0[$0040]
|
|
|
|
transfer_reg: MACRO
|
|
ldh a, [h\1]
|
|
ldh [r\1], a
|
|
ENDM
|
|
|
|
; VBlank
|
|
push af
|
|
transfer_reg LCDC
|
|
jp VBlankHandler
|
|
|
|
; LCD
|
|
push af
|
|
push bc
|
|
ldh a, [hScanlineFXIndex]
|
|
ld c, a
|
|
ld a, [c] ; Get port ID
|
|
jr LCDHandler
|
|
|
|
; Timer
|
|
reti
|
|
ds 7
|
|
|
|
; Serial
|
|
reti
|
|
|
|
; Fit in a 7-byte function, too!
|
|
|
|
; Jumps immediately to de, no questions asked (except RAM targets?).
|
|
CallDE::
|
|
push de
|
|
bit 7, d
|
|
ret z
|
|
jp DEJumpingError
|
|
|
|
; Joypad
|
|
reti
|
|
|
|
|
|
LCDHandler:
|
|
ld b, a ; Save port ID for later
|
|
inc c
|
|
inc c
|
|
ld a, [c] ; Get next effect's scanline
|
|
dec a ; Compensate for processing time
|
|
ldh [rLYC], a ; Get set up (hopefully this should reset the interrupt trigger line)
|
|
ld a, c ; Point to next effect's port ID
|
|
inc a
|
|
ldh [hScanlineFXIndex], a
|
|
dec c
|
|
; Wait a bit to write during HBlank, to avoid gfx artifacts
|
|
ld a, 4
|
|
.waitMode0
|
|
dec a
|
|
jr nz, .waitMode0
|
|
|
|
; Check if we're trying to write to P1 ($FF*00*)
|
|
ld a, b
|
|
and a ; Note: `and $7F` can be used instead to have control on bit 7 (if ever needed)
|
|
; Perform common ops
|
|
ld a, [c] ; Get value
|
|
; rP1 is hardwired to instead perform textbox ops
|
|
jr nz, .notTextbox
|
|
|
|
ldh [rSCY], a ; Store value, which is actually for SCY (dat plot twist, eh?)
|
|
xor a
|
|
ldh [rSCX], a
|
|
ld c, LOW(rLCDC)
|
|
ldh a, [hLCDC] ; Retrieve LCDC value
|
|
and ~(LCDCF_WINON | LCDCF_BG8000 | LCDCF_OBJON)
|
|
or LCDCF_BG9C00
|
|
; Note: this is scrapped support for sprites on the textbox
|
|
; It was initially planned for JP diacritics.
|
|
; If for whatever reason, you need to re-activate this feature...
|
|
; ...uncomment this, and remove "LCDCF_OBJON" from above.
|
|
;
|
|
; ld [c], a ; Apply LCDC modification
|
|
; ; Perform OAM DMA to get textbox's sprites
|
|
; ; Luckily, sprites are hidden during DMA
|
|
; ; Also no sprites should be present on the textbox 1st row, hiding our trickery >:P
|
|
; ld a, HIGH(wTextboxOAM)
|
|
; call hOAMDMA
|
|
; ; Reload OAM on next frame
|
|
; ldh a, [hCurrentOAMBuffer]
|
|
; ldh [hOAMBuffer], a
|
|
; jr .onlyOneEffect
|
|
|
|
.notTextbox
|
|
ld c, b ; Retrieve port
|
|
res 7, c
|
|
ld [c], a ; Apply FX
|
|
bit 7, b
|
|
jr z, .onlyOneEffect
|
|
ldh a, [hSecondFXAddr]
|
|
ld c, a
|
|
ldh a, [hSecondFXValue]
|
|
ld [$ff00+c], a
|
|
.onlyOneEffect
|
|
pop bc
|
|
pop af
|
|
reti
|
|
|
|
|
|
SECTION "VBlank handler", ROM0
|
|
|
|
VBlankHandler:
|
|
push bc
|
|
|
|
; ============= Here are things that need to be updated, even on lag frames ==============
|
|
|
|
; Update IO from HRAM shadow
|
|
transfer_reg SCY
|
|
transfer_reg SCX
|
|
transfer_reg WY
|
|
transfer_reg WX
|
|
|
|
|
|
ldh a, [hWhichScanlineBuffer]
|
|
ld c, a
|
|
; Get first effect's scanline
|
|
ld a, [$ff00+c]
|
|
dec a ; Compensate for the processing time
|
|
; NOTE: this assumes no effect is scheduled on line 0
|
|
; This should never happen; instead, use the HRAM shadow regs (hSCY, etc.)
|
|
ldh [rLYC], a
|
|
inc c
|
|
ld a, c
|
|
ldh [hScanlineFXIndex], a
|
|
|
|
|
|
; Update OAM if needed
|
|
; Do this last so it will go through even without time
|
|
; This will simply cause sprites to not be displayed on the top few scanlines, but that's not as bad as palettes not loading at all, huh?
|
|
ldh a, [hOAMBufferHigh]
|
|
and a
|
|
jr z, .dontUpdateOAM
|
|
ld b, a
|
|
; Reset OAM buffer high vect
|
|
xor a
|
|
ldh [hOAMBufferHigh], a
|
|
; Perform DMA as specified
|
|
ld a, b
|
|
call hOAMDMA
|
|
.dontUpdateOAM
|
|
|
|
|
|
; ============== In case of lag, don't update further, to avoid breaking stuff ===============
|
|
|
|
ldh a, [hVBlankFlag]
|
|
and a
|
|
jr nz, .lagFrame
|
|
|
|
|
|
; Poll joypad and update regs
|
|
|
|
ld c, LOW(rP1)
|
|
ld a, $20 ; Select D-pad
|
|
ld [$ff00+c], a
|
|
REPT 6
|
|
ld a, [$ff00+c]
|
|
ENDR
|
|
or $F0 ; Set 4 upper bits (give them consistency)
|
|
ld b, a
|
|
|
|
; Filter impossible D-pad combinations
|
|
and $0C ; Filter only Down and Up
|
|
ld a, b
|
|
jr nz, .notUpAndDown
|
|
or $0C ; If both are pressed, "unpress" them
|
|
ld b, a
|
|
.notUpAndDown
|
|
and $03 ; Filter only Left and Right
|
|
jr nz, .notLeftAndRight
|
|
ld a, b
|
|
or $03 ; If both are pressed, "unpress" them
|
|
ld b, a
|
|
.notLeftAndRight
|
|
swap b ; Put D-pad buttons in upper nibble
|
|
|
|
ld a, $10 ; Select buttons
|
|
ld [$ff00+c], a
|
|
REPT 6
|
|
ld a, [$ff00+c]
|
|
ENDR
|
|
; On SsAB held, soft-reset
|
|
and $0F
|
|
jp z, Reset
|
|
|
|
or $F0 ; Set 4 upper bits
|
|
xor b ; Mix with D-pad bits, and invert all bits (such that pressed=1) thanks to "or $F0"
|
|
ld b, a
|
|
|
|
ldh a, [hHeldButtons]
|
|
cpl
|
|
and b
|
|
ldh [hPressedButtons], a
|
|
|
|
ld a, b
|
|
ldh [hHeldButtons], a
|
|
|
|
; Release joypad
|
|
ld a, $30
|
|
ld [$ff00+c], a
|
|
|
|
pop bc
|
|
pop af
|
|
|
|
|
|
; The main code was waiting for VBlank, so tell it it's OK by resetting Z
|
|
xor a
|
|
inc a ; Clear Z
|
|
ldh [hVBlankFlag], a ; Mark VBlank as ACK'd
|
|
reti
|
|
|
|
|
|
.lagFrame
|
|
pop bc
|
|
pop af
|
|
reti
|