ankano/src/vectors.asm

269 lines
6.6 KiB
NASM

; Copyright (C) 2018-2020 Eldred Habert
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software
; in a product, an acknowledgment in the product documentation would be
; appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
; misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.
INCLUDE "defines.asm"
SECTION "Vectors", ROM0[0]
NULL::
; This traps jumps to $0000, which is a common "default" pointer
; $FFFF is another one, but reads rIE as the instruction byte
; Thus, we put two `nop`s that may serve as operands, before soft-crashing
; The operand will always be 0, so even jumps will work fine. Nice!
nop
nop
rst Crash
ds $08 - @ ; 5 free bytes
; Waits for the next VBlank beginning
; Requires the VBlank handler to be able to trigger, otherwise will loop infinitely
; This means IME should be set, the VBlank interrupt should be selected in IE,
; and the LCD should be turned on.
; WARNING: Be careful if calling this with IME reset (`di`), if this was compiled
; with the `-h` flag, then a hardware bug is very likely to cause this routine to
; go horribly wrong.
; Note: the VBlank handler recognizes being called from this function (through `hVBlankFlag`),
; and does not try to save registers if so. To be safe, consider all registers to be destroyed.
; @destroy Possibly every register. The VBlank handler stops preserving anything when executed from this function
WaitVBlank::
ld a, 1
ldh [hVBlankFlag], a
.wait
halt
jr .wait
ds $10 - 1 - @ ; 0 free bytes
MemsetLoop:
ld a, d
; You probably don't want to use this for writing to VRAM while the LCD is on. See LCDMemset.
Memset::
ld [hli], a
ld d, a
dec bc
ld a, b
or c
jr nz, MemsetLoop
ret
ds $18 - @ ; 0 free bytes
MemcpySmall::
ld a, [de]
ld [hli], a
inc de
dec c
jr nz, MemcpySmall
ret
ds $20 - @ ; 1 free byte
MemsetSmall::
ld [hli], a
dec c
jr nz, MemsetSmall
ret
ds $28 - 3 - @ ; 0 free bytes
; Dereferences `hl` and jumps there
; All other registers are passed to the called code intact, except Z is reset
; Soft-crashes if the jump target is in RAM
; @param hl Pointer to an address to jump to
JumpToPtr::
ld a, [hli]
ld h, [hl]
ld l, a
; Jump to some address
; All registers are passed to the called code intact, except Z is reset
; (`jp CallHL` is equivalent to `jp hl`, but with the extra error checking on top)
; Soft-crashes if attempting to jump to RAM
; @param hl The address of the code to jump to
CallHL::
bit 7, h
error nz
jp hl
ds $30 - @ ; 3 free bytes
; Jumps to some address
; All registers are passed to the target code intact, except Z is reset
; (`jp CallDE` would be equivalent to `jp de` if that instruction existed)
; Soft-crashes if attempting to jump to RAM
; @param de The address of the code to jump to
CallDE::
bit 7, d
push de
ret z ; No jumping to RAM, boy!
rst Crash
ds $38 - @ ; 3 free bytes
; Perform a soft-crash. Prints debug info on-screen
Crash::
di ; Doing this as soon as possible to avoid interrupts messing up
jp HandleCrash
ds $40 - @
; VBlank handler
push af
ldh a, [hLCDC]
ldh [rLCDC], a
jp VBlankHandler
ds $48 - @
; STAT handler
reti
ds $50 - @
; Timer handler
reti
ds $58 - @
; Serial handler
reti
ds $60 - @
; Joypad handler (useless)
reti
SECTION "VBlank handler", ROM0
VBlankHandler:
ldh a, [hSCY]
ldh [rSCY], a
ldh a, [hSCX]
ldh [rSCX], a
ldh a, [hBGP]
ldh [rBGP], a
ldh a, [hOBP0]
ldh [rOBP0], a
ldh a, [hOBP1]
ldh [rOBP1], a
; OAM DMA can occur late in the handler, because it will still work even
; outside of VBlank. Sprites just will not appear on the scanline(s)
; during which it's running.
ldh a, [hOAMHigh]
and a
jr z, .noOAMTransfer
call hOAMDMA
xor a
ldh [hOAMHigh], a
.noOAMTransfer
; Put all operations that cannot be interrupted above this line
; For example, OAM DMA (can't jump to ROM in the middle of it),
; VRAM accesses (can't screw up timing), etc
ei
ldh a, [hVBlankFlag]
and a
jr z, .lagFrame
xor a
ldh [hVBlankFlag], a
ld c, LOW(rP1)
ld a, $20 ; Select D-pad
ldh [c], a
REPT 6
ldh a, [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
; If both are pressed, "unpress" them
inc b
inc b
inc b
.notLeftAndRight
swap b ; Put D-pad buttons in upper nibble
ld a, $10 ; Select buttons
ldh [c], a
REPT 6
ldh a, [c]
ENDR
; On SsAB held, soft-reset
and $0F
jr z, .perhapsReset
.dontReset
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
; Release joypad
ld a, $30
ldh [c], a
ldh a, [hHeldKeys]
cpl
and b
ldh [hPressedKeys], a
ld a, b
ldh [hHeldKeys], a
pop af ; Pop off return address as well to exit infinite loop
.lagFrame
pop af
ret
.perhapsReset
ldh a, [hCanSoftReset]
and a
jr z, .dontReset
jp Reset
SECTION "VBlank HRAM", HRAM
; DO NOT TOUCH THIS
; When this flag is set, the VBlank handler will assume the caller is `WaitVBlank`,
; and attempt to exit it. You don't want that to happen outside of that function.
hVBlankFlag:: db
; High byte of the address of the OAM buffer to use.
; When this is non-zero, the VBlank handler will write that value to rDMA, and
; reset it.
hOAMHigh:: db
; Shadow registers for a bunch of hardware regs.
; Writing to these causes them to take effect more or less immediately, so these
; are copied to the hardware regs by the VBlank handler, taking effect between frames.
; They also come in handy for "resetting" them if modifying them mid-frame for raster FX.
hLCDC:: db
hSCY:: db
hSCX:: db
hBGP:: db
hOBP0:: db
hOBP1:: db
; Keys that are currently being held, and that became held just this frame, respectively.
; Each bit represents a button, with that bit set == button pressed
; Button order: Down, Up, Left, Right, Start, select, B, A
; U+D and L+R are filtered out by software, so they will never happen
hHeldKeys:: db
hPressedKeys:: db
; If this is 0, pressing SsAB at the same time will not reset the game
hCanSoftReset:: db