Add files
This commit is contained in:
111
src/home/header.asm
Normal file
111
src/home/header.asm
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
NB_ROM_BANKS = 2
|
||||
|
||||
SECTION "Header", ROM0[$100]
|
||||
|
||||
EntryPoint::
|
||||
ld b, $60
|
||||
jr LogoFade
|
||||
|
||||
dbr 0, $150 - $104
|
||||
|
||||
; Header ends here
|
||||
|
||||
LogoFade:
|
||||
xor a
|
||||
ldh [rAUDENA], a
|
||||
|
||||
.fadeLogo
|
||||
ld c, 7 ; Number of frames between each logo fade step
|
||||
.logoWait
|
||||
ld a, [rLY]
|
||||
cp a, SCRN_Y
|
||||
jr nc, .logoWait
|
||||
.waitVBlank
|
||||
ld a, [rLY]
|
||||
cp a, SCRN_Y
|
||||
jr c, .waitVBlank
|
||||
dec c
|
||||
jr nz, .logoWait
|
||||
; Shift all colors (fading the logo progressively)
|
||||
ld a, b
|
||||
rra
|
||||
rra
|
||||
and $FC ; Ensures a proper rotation and sets Z for final check
|
||||
ldh [rBGP], a
|
||||
ld b, a
|
||||
jr nz, .fadeLogo ; End if the palette is fully blank (flag set from `and $FC`)
|
||||
|
||||
; xor a
|
||||
ldh [rDIV], a
|
||||
|
||||
Reset::
|
||||
di
|
||||
ld sp, wStackBottom
|
||||
|
||||
xor a
|
||||
ldh [rAUDENA], a
|
||||
|
||||
.waitVBlank
|
||||
ld a, [rLY]
|
||||
cp SCRN_Y
|
||||
jr c, .waitVBlank
|
||||
xor a
|
||||
ldh [rLCDC], a
|
||||
|
||||
|
||||
; Perform some init
|
||||
; xor a
|
||||
ldh [rAUDENA], a
|
||||
|
||||
; Init HRAM
|
||||
; Also clears IE, but we're gonna overwrite it just after
|
||||
ld c, LOW(hClearStart)
|
||||
.clearHRAM
|
||||
xor a
|
||||
ld [$ff00+c], a
|
||||
inc c
|
||||
jr nz, .clearHRAM
|
||||
|
||||
; Copy OAM DMA routine
|
||||
ld hl, OAMDMA
|
||||
lb bc, OAMDMAEnd - OAMDMA, LOW(hOAMDMA)
|
||||
.copyOAMDMA
|
||||
ld a, [hli]
|
||||
ld [$ff00+c], a
|
||||
inc c
|
||||
dec b
|
||||
jr nz, .copyOAMDMA
|
||||
|
||||
ld a, LOW(hScanlineFXBuffer1)
|
||||
ldh [hWhichScanlineBuffer], a
|
||||
ld a, $FF
|
||||
ldh [hScanlineFXBuffer1], a
|
||||
ld a, STATF_LYC
|
||||
ldh [rSTAT], a
|
||||
|
||||
ld a, LCDCF_ON | LCDCF_BGON
|
||||
ldh [hLCDC], a
|
||||
ldh [rLCDC], a
|
||||
|
||||
ld a, IEF_VBLANK | IEF_LCDC
|
||||
ldh [rIE], a
|
||||
xor a
|
||||
ei ; Delayed until the next instruction: perfectly safe!
|
||||
ldh [rIF], a
|
||||
|
||||
|
||||
; Init code goes here
|
||||
|
||||
|
||||
|
||||
SECTION "OAM DMA routine", ROM0
|
||||
|
||||
OAMDMA:
|
||||
ldh [rDMA], a
|
||||
ld a, $28
|
||||
.wait
|
||||
dec a
|
||||
jr nz, .wait
|
||||
ret
|
||||
OAMDMAEnd:
|
||||
221
src/home/raster_fx.asm
Normal file
221
src/home/raster_fx.asm
Normal file
@@ -0,0 +1,221 @@
|
||||
|
||||
SECTION "Raster fx helper functions", ROM0
|
||||
|
||||
; Get a pointer to the currently free scanline buffer
|
||||
; @return a The pointer
|
||||
; @return c The pointer
|
||||
; @destroys a c
|
||||
GetFreeScanlineBuf::
|
||||
ldh a, [hWhichScanlineBuffer]
|
||||
xor LOW(hScanlineFXBuffer2) ^ LOW(hScanlineFXBuffer1)
|
||||
ld c, a
|
||||
ret
|
||||
|
||||
; Switches to the currently free scanline buffer
|
||||
; @return c A pointer to the newly freed buffer
|
||||
; @return b A pointer to the newly used buffer
|
||||
; @destroys a c
|
||||
SwitchScanlineBuf::
|
||||
call GetFreeScanlineBuf
|
||||
ldh [hWhichScanlineBuffer], a
|
||||
ld b, a
|
||||
xor LOW(hScanlineFXBuffer2) ^ LOW(hScanlineFXBuffer1)
|
||||
ld c, a
|
||||
ret
|
||||
|
||||
; Switches to the currently free scanline buffer, and copies it over to the other buffer
|
||||
; @destroys a c hl
|
||||
SwitchAndCopyScanlineBuf::
|
||||
call SwitchScanlineBuf
|
||||
ld l, b
|
||||
ld h, HIGH(hScanlineFXBuffer1)
|
||||
.loop
|
||||
ld a, [hli]
|
||||
ld [$ff00+c], a
|
||||
inc c
|
||||
inc a
|
||||
jr nz, .loop
|
||||
ret
|
||||
|
||||
|
||||
; Finds the effect applied to a given scanline
|
||||
; @param b The scanline being looked for
|
||||
; @return Zero Set if the lookup succeeded
|
||||
; @return c A pointer to the effect's scanline, otherwise to the terminating $FF byte
|
||||
; @destroys a c
|
||||
GetFXByScanline::
|
||||
call GetFreeScanlineBuf
|
||||
.lookup
|
||||
ld a, [$ff00+c]
|
||||
cp b
|
||||
ret nc ; Return if scanline was greater than or equal
|
||||
inc c
|
||||
inc c
|
||||
inc c
|
||||
jr .lookup
|
||||
|
||||
; Insert effect into the free buffer at the given scanline
|
||||
; The effect's scanline will have been set, and the other two values will be zero'd
|
||||
; @param b The scanline to insert the effect at
|
||||
; @return c A pointer to the new effect's port address
|
||||
; @return Zero Set if the scanline is already taken (in which case c still holds the correct value)
|
||||
; @destroys a bc hl
|
||||
InsertFX::
|
||||
call GetFreeScanlineBuf
|
||||
ld l, c
|
||||
ld h, HIGH(hScanlineFXBuffer1)
|
||||
scf ; Don't skip the check
|
||||
.lookForEnd
|
||||
ld a, [hli]
|
||||
inc l
|
||||
; C is inherited from previous `cp b`
|
||||
jr nc, .skip ; If we're already greater then the scanline, skip this
|
||||
cp b
|
||||
ret z ; End now if the scanline is already taken, because two FX can't be on the same line
|
||||
jr c, .skip ; Skip if we aren't the first greater than the scanline
|
||||
ld c, l ; Make c point to the target's value
|
||||
.skip
|
||||
inc l
|
||||
inc a
|
||||
jr nz, .lookForEnd
|
||||
; Write new terminator ($FF)
|
||||
ld [hl], h ; Bet you didn't expect this opcode to ever be used, eh?
|
||||
dec l
|
||||
.copy
|
||||
dec l
|
||||
dec l
|
||||
dec l
|
||||
ld a, [hli]
|
||||
inc l
|
||||
inc l
|
||||
ld [hld], a ; If we just copied the target scanline,
|
||||
ld a, l ; this points to the value
|
||||
cp c ; which we know the address of!
|
||||
jr nz, .copy
|
||||
; Move the pointer and init the new fx
|
||||
xor a
|
||||
ld [hld], a
|
||||
ld [hld], a
|
||||
ld [hl], b ; Write the desired scanline
|
||||
inc a ; Don't have Z set
|
||||
ret
|
||||
|
||||
|
||||
; Remove effect from the free buffer
|
||||
; @param b The targeted effect's scanline
|
||||
; @return Zero Set if the lookup succeeded
|
||||
; @destroys a c hl
|
||||
RemoveFX::
|
||||
call GetFXByScanline
|
||||
ret nz
|
||||
ld l, c
|
||||
ld h, HIGH(hScanlineFXBuffer1)
|
||||
inc c
|
||||
inc c
|
||||
inc c
|
||||
.copy
|
||||
; Copy scanline
|
||||
ld a, [$ff00+c]
|
||||
ld [hli], a
|
||||
inc a
|
||||
ret z ; End if we copied the terminator
|
||||
inc c
|
||||
; Copy port address
|
||||
ld a, [$ff00+c]
|
||||
ld [hli], a
|
||||
inc c
|
||||
; Copy value
|
||||
ld a, [$ff00+c]
|
||||
ld [hli], a
|
||||
inc c
|
||||
jr .copy
|
||||
|
||||
|
||||
; Add the textbox raster FX
|
||||
; Caution: overwrites the currently free fx buffer with the active (+textbox)
|
||||
; @param b The number of pixels of the textbox to display (0 closes it)
|
||||
; @destroys a bc de hl
|
||||
SetUpTextbox::
|
||||
ld h, HIGH(hWhichScanlineBuffer)
|
||||
|
||||
; Check if backup operations should be performed
|
||||
ldh a, [hIsTextboxActive]
|
||||
and a
|
||||
jr nz, .dontBackup
|
||||
ld a, b
|
||||
and a
|
||||
ret z ; Do nothing if the textbox is closed while it is closed (lol)
|
||||
ldh a, [hWhichScanlineBuffer]
|
||||
ld c, a
|
||||
ld l, LOW(hBackupScanlineFXBuffer)
|
||||
.backup
|
||||
ld a, [$ff00+c]
|
||||
inc c
|
||||
ld [hli], a
|
||||
inc a
|
||||
jr nz, .backup
|
||||
inc a ; ld a, 1
|
||||
ldh [hIsTextboxActive], a
|
||||
; Fall through, but this won't get executed
|
||||
.dontBackup
|
||||
ld a, b
|
||||
and a
|
||||
jr z, .restoreAndQuit
|
||||
|
||||
; Get pointers to buffers
|
||||
ldh a, [hWhichScanlineBuffer]
|
||||
ld c, a
|
||||
xor LOW(hScanlineFXBuffer2) ^ LOW(hScanlineFXBuffer1)
|
||||
ld l, a
|
||||
; Calculate scanline
|
||||
ld a, SCRN_Y
|
||||
sub b
|
||||
ld b, a
|
||||
|
||||
scf
|
||||
.copy
|
||||
ld a, [$ff00+c] ; Get scanline
|
||||
cp b
|
||||
jr nc, .insertTextbox
|
||||
inc c
|
||||
ld [hl], a
|
||||
ld a, [$ff00+c] ; Read port
|
||||
inc c
|
||||
inc c ; Skip value, jic
|
||||
and a ; Are we copying a textbox FX?
|
||||
jr z, .copy ; Abort
|
||||
inc l
|
||||
ld [hli], a
|
||||
dec c
|
||||
ld a, [$ff00+c] ; Read value
|
||||
inc c
|
||||
ld [hli], a
|
||||
jr .copy
|
||||
|
||||
.restoreAndQuit
|
||||
call GetFreeScanlineBuf
|
||||
.restore
|
||||
ld a, [hli]
|
||||
ld [$ff00+c], a
|
||||
inc c
|
||||
inc a
|
||||
jr nz, .restore
|
||||
; a = 0
|
||||
ldh [hIsTextboxActive], a
|
||||
ret
|
||||
|
||||
.insertTextbox
|
||||
; Place the textbox FX
|
||||
ld [hl], b
|
||||
inc l
|
||||
ld [hl], 0
|
||||
inc l
|
||||
ld a, b
|
||||
sub SCRN_Y
|
||||
ld [hli], a
|
||||
ld [hl], $FF ; Don't forget to terminate!
|
||||
|
||||
ldh a, [hWhichScanlineBuffer]
|
||||
xor LOW(hScanlineFXBuffer2) ^ LOW(hScanlineFXBuffer1)
|
||||
ldh [hWhichScanlineBuffer], a
|
||||
ret
|
||||
130
src/home/sgb.asm
Normal file
130
src/home/sgb.asm
Normal file
@@ -0,0 +1,130 @@
|
||||
|
||||
SECTION "SGB routines", ROM0
|
||||
|
||||
; Sends SGB packets to the SGB, assuming we're running on one.
|
||||
; @param hl Pointer to the packet data to be sent (can send any number of packets btw)
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return de Zero
|
||||
; @return bc Zero
|
||||
; @return a Zero
|
||||
SendPackets::
|
||||
ld a, [hl] ; Length
|
||||
and %111
|
||||
ret z
|
||||
ld c, a
|
||||
|
||||
.sendPacket
|
||||
call SendPacketNoDelay
|
||||
call SGBDelay ; Let the ICD chip rest a bit
|
||||
dec c
|
||||
jr nz, .sendPacket
|
||||
ret
|
||||
|
||||
|
||||
; Sends a SGB packet to the SGB to freeze the screen, assuming we're running on one.
|
||||
; Does not perform any delay after sending the packet.
|
||||
; Use only if you're not going to send another SGB packet in the next few frames.
|
||||
; You're likely to perform some decompression or smth after this
|
||||
; @param hl Pointer to the packet data to be sent.
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return b Zero
|
||||
; @return d Zero
|
||||
; @return a $30
|
||||
; @destroy e
|
||||
FreezeSGBScreen::
|
||||
ld hl, FreezeScreenPacket
|
||||
; Sends a SGB packet to the SGB, assuming it's running on one.
|
||||
; Does not perform any delay after sending the packet.
|
||||
; Unsuitable to send multi-packet packets.
|
||||
; Use only if you're not going to send another SGB packet in the next four frames.
|
||||
; Assumes the joypad won't be polled by interrupts during this time, but since the VBlank handler doesn't poll except when waited for, this is fine.
|
||||
; @param hl Pointer to the packet data to be sent.
|
||||
; @return hl Points to the end of the packet data
|
||||
; @return b Zero
|
||||
; @return d Zero
|
||||
; @return a $30
|
||||
; @destroy e
|
||||
SendPacketNoDelay::
|
||||
; Packet transmission begins by sending $00 then $30
|
||||
xor a
|
||||
ldh [rP1], a
|
||||
ld a, $30
|
||||
ldh [rP1], a
|
||||
|
||||
ld b, SGB_PACKET_SIZE
|
||||
.sendByte
|
||||
ld d, 8 ; 8 bits in a byte
|
||||
ld a, [hli] ; Read byte to send
|
||||
ld e, a
|
||||
|
||||
.sendBit
|
||||
ld a, $10 ; 1 bits are sent with $10
|
||||
rr e ; Rotate d and get its lower bit, two birds in one stone!
|
||||
jr c, .bitSet
|
||||
add a, a ; 0 bits are sent with $20
|
||||
.bitSet
|
||||
ldh [rP1], a
|
||||
ld a, $30 ; Terminate pulse
|
||||
ldh [rP1], a
|
||||
dec d
|
||||
jr nz, .sendBit
|
||||
|
||||
dec b
|
||||
jr nz, .sendByte
|
||||
|
||||
; Packets are terminated by a "STOP" 0 bit
|
||||
ld a, $20
|
||||
ldh [rP1], a
|
||||
ld a, $30
|
||||
ldh [rP1], a
|
||||
ret
|
||||
|
||||
SGBDelay::
|
||||
ld de, 7000 ; Magic value, apparently
|
||||
.loop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
dec de
|
||||
ld a, d
|
||||
or e
|
||||
jr nz, .loop
|
||||
ret
|
||||
|
||||
FreezeScreenPacket:
|
||||
sgb_packet MASK_EN, 1, 1
|
||||
|
||||
|
||||
; Fill the $9C00 tilemap with a pattern suitable for SGB _TRN
|
||||
; Also sets up the rendering parameters for the transfer
|
||||
; Finally, assumes the LCD is **off**
|
||||
; @return hl
|
||||
FillScreenWithSGBMap::
|
||||
xor a
|
||||
ldh [hSCY], a
|
||||
ldh [hSCX], a
|
||||
ld b, a ; ld b, 0
|
||||
ld hl, $9C00
|
||||
.writeRow
|
||||
ld c, SCRN_X_B
|
||||
.writeTile
|
||||
ld a, b
|
||||
ld [hli], a
|
||||
inc b
|
||||
jr z, .done
|
||||
dec c
|
||||
jr nz, .writeTile
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B
|
||||
ld l, a
|
||||
jr nc, .writeRow
|
||||
inc h
|
||||
jr .writeRow
|
||||
.done
|
||||
ld a, %11100100
|
||||
ldh [hBGP], a
|
||||
SetupSGBLCDC::
|
||||
ld a, LCDCF_ON | LCDCF_WINOFF | LCDCF_BG8000 | LCDCF_BG9C00 | LCDCF_OBJOFF | LCDCF_BGON
|
||||
ldh [rLCDC], a
|
||||
ldh [hLCDC], a
|
||||
ret
|
||||
89
src/home/unpb16.asm
Normal file
89
src/home/unpb16.asm
Normal file
@@ -0,0 +1,89 @@
|
||||
;
|
||||
; PB16 decompression for Game Boy
|
||||
; Copyright 2018 Damian Yerrick
|
||||
;
|
||||
; 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.
|
||||
;
|
||||
; This software has been modified by Eldred Habert (ISSOtm):
|
||||
; - Removal of .inc file
|
||||
; - Removal of variable allocation
|
||||
|
||||
|
||||
section "pb16", ROM0
|
||||
|
||||
; The PB16 format is a starting point toward efficient RLE image
|
||||
; codecs on Game Boy and Super NES.
|
||||
;
|
||||
; 0: Load a literal byte
|
||||
; 1: Repeat from 2 bytes ago
|
||||
|
||||
pb16_unpack_packet:
|
||||
; Read first bit of control byte. Treat B as a ring counter with
|
||||
; a 1 bit as the sentinel. Once the 1 bit reaches carry, B will
|
||||
; become 0, meaning the 8-byte packet is complete.
|
||||
ld a,[de]
|
||||
inc de
|
||||
scf
|
||||
rla
|
||||
ld b,a
|
||||
.byteloop:
|
||||
; If the bit from the control byte is clear, plane 0 is is literal
|
||||
jr nc,.p0_is_literal
|
||||
ldh a,[pb16_byte0]
|
||||
jr .have_p0
|
||||
.p0_is_literal:
|
||||
ld a,[de]
|
||||
inc de
|
||||
ldh [pb16_byte0],a
|
||||
.have_p0:
|
||||
ld [hl+],a
|
||||
|
||||
; Read next bit. If it's clear, plane 1 is is literal.
|
||||
ld a,c
|
||||
sla b
|
||||
jr c,.have_p1
|
||||
.p1_is_copy:
|
||||
ld a,[de]
|
||||
inc de
|
||||
ld c,a
|
||||
.have_p1:
|
||||
ld [hl+],a
|
||||
|
||||
; Read next bit of control byte
|
||||
sla b
|
||||
jr nz,.byteloop
|
||||
ret
|
||||
|
||||
;;
|
||||
; Unpacks 2*B packets from DE to HL, producing 8 bytes per packet.
|
||||
; About 127 cycles (2 scanlines) per 8-byte packet; filling CHR RAM
|
||||
; thus takes (6144/8)*127 = about 97536 cycles or 93 ms
|
||||
pb16_unpack_block::
|
||||
; Prefill with zeroes
|
||||
xor a
|
||||
ldh [pb16_byte0],a
|
||||
ld c,a
|
||||
.packetloop:
|
||||
push bc
|
||||
call pb16_unpack_packet
|
||||
call pb16_unpack_packet
|
||||
ld a,c
|
||||
pop bc
|
||||
ld c,a
|
||||
dec b
|
||||
jr nz,.packetloop
|
||||
ret
|
||||
192
src/home/utils.asm
Normal file
192
src/home/utils.asm
Normal file
@@ -0,0 +1,192 @@
|
||||
|
||||
|
||||
; Since all these functions are independent, declare each of them in individual sections
|
||||
; Lets the linker place them more liberally. Hooray!
|
||||
f: MACRO
|
||||
PURGE \1
|
||||
SECTION "Utility function \1", ROM0
|
||||
\1::
|
||||
ENDM
|
||||
|
||||
SECTION "Dummy section", ROM0 ; To have the first `Memcpy` declare properly
|
||||
|
||||
; Copies bc bytes of data from de to hl
|
||||
Memcpy::
|
||||
f Memcpy
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, Memcpy
|
||||
ret
|
||||
|
||||
; Copies a null-terminated string from de to hl, including the terminating NUL
|
||||
Strcpy::
|
||||
f Strcpy
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
and a
|
||||
jr nz, Strcpy
|
||||
ret
|
||||
|
||||
; Copies c bytes of data from de to hl in a LCD-safe manner
|
||||
LCDMemcpySmall::
|
||||
f LCDMemcpySmall
|
||||
ldh a, [rSTAT]
|
||||
and STATF_BUSY
|
||||
jr nz, LCDMemcpySmall
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec c
|
||||
jr nz, LCDMemcpySmall
|
||||
ret
|
||||
|
||||
; Copies bc bytes of data from de to hl in a LCD-safe manner
|
||||
LCDMemcpy::
|
||||
f LCDMemcpy
|
||||
ldh a, [rSTAT]
|
||||
and STATF_BUSY
|
||||
jr nz, LCDMemcpy
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, LCDMemcpy
|
||||
ret
|
||||
|
||||
; Sets c bytes of data at hl with the value in a
|
||||
LCDMemsetSmall::
|
||||
f LCDMemsetSmall
|
||||
ld b, a
|
||||
|
||||
; Sets c bytes of data at hl with the value in b
|
||||
LCDMemsetSmallFromB::
|
||||
; No f (...) because there's the slide-in above
|
||||
.loop
|
||||
ldh a, [rSTAT]
|
||||
and STATF_BUSY
|
||||
jr nz, .loop
|
||||
ld a, b
|
||||
ld [hli], a
|
||||
dec c
|
||||
jr nz, .loop
|
||||
ret
|
||||
|
||||
; Sets bc bytes of data at hl with the value in a
|
||||
LCDMemset::
|
||||
f LCDMemset
|
||||
ld d, a
|
||||
|
||||
; Sets bc bytes of data at hl with the value in d
|
||||
LCDMemsetFromD::
|
||||
; No f (...) because of the slide-in above
|
||||
.loop
|
||||
ldh a, [rSTAT]
|
||||
and STATF_BUSY
|
||||
jr nz, .loop
|
||||
ld a, d
|
||||
ld [hli], a
|
||||
dec bc
|
||||
ld a, b
|
||||
or c
|
||||
jr nz, .loop
|
||||
ret
|
||||
|
||||
|
||||
; Opens SRAM at some bank
|
||||
; @param a The bank's number
|
||||
; @return a CART_RAM_ENABLE, ie. $0A
|
||||
GetSRAMBank::
|
||||
f GetSRAMBank
|
||||
ld [rRAMB], a
|
||||
ld a, CART_RAM_ENABLE
|
||||
ld [rRAMG], a
|
||||
ret
|
||||
|
||||
; Closes SRAM
|
||||
; @return hl = rRAMB (I know, it sounds stupid)
|
||||
CloseSRAM::
|
||||
f CloseSRAM
|
||||
; Implementation note: MUST preserve the Z flag to avoid breaking the call to `PrintSRAMFailure`
|
||||
ld hl, rRAMG
|
||||
ld [hl], l ; ld [hl], 0
|
||||
ld h, HIGH(rRAMB)
|
||||
ld [hl], l ; Avoid unintentional unlocks corrupting saved data, switch to bank 0 (which is scratch)
|
||||
ret
|
||||
|
||||
|
||||
; Gets the Nth struct in an array of 'em
|
||||
; @param hl Array base
|
||||
; @param bc Size of a struct
|
||||
; @param a ID of the desired struct
|
||||
; @return hl Pointer to the struct's base
|
||||
; @destroys a
|
||||
GetNthStruct::
|
||||
f GetNthStruct
|
||||
and a
|
||||
ret z
|
||||
.next
|
||||
add hl, bc
|
||||
dec a
|
||||
jr nz, .next
|
||||
ret
|
||||
|
||||
|
||||
; Copies tiles into VRAM, using an unrolled loop to go faster
|
||||
; @param hl Destination (pointer)
|
||||
; @param de Source
|
||||
; @param c Number of tiles
|
||||
; @return hl, de Pointer to end of blocks
|
||||
; @return c 0
|
||||
; @destroys a
|
||||
Tilecpy::
|
||||
f Tilecpy
|
||||
REPT $10 / 2
|
||||
wait_vram
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
ENDR
|
||||
dec c
|
||||
jr nz, Tilecpy
|
||||
ret
|
||||
|
||||
|
||||
; Copies a tilemap to VRAM, assuming the LCD is off
|
||||
; @param hl Destination
|
||||
; @param de Source
|
||||
; @return hl, de Pointer to end of blocks
|
||||
; @return bc Zero
|
||||
; @return a Equal to h
|
||||
Mapcpy::
|
||||
f Mapcpy
|
||||
ld b, SCRN_Y_B
|
||||
.copyRow
|
||||
ld c, SCRN_X_B
|
||||
.copyTile
|
||||
ld a, [de]
|
||||
ld [hli], a
|
||||
inc de
|
||||
dec c
|
||||
jr nz, .copyTile
|
||||
ld a, l
|
||||
add a, SCRN_VX_B - SCRN_X_B
|
||||
ld l, a
|
||||
adc a, h
|
||||
sub l
|
||||
ld h, a
|
||||
dec b
|
||||
jr nz, .copyRow
|
||||
ret
|
||||
|
||||
|
||||
PURGE f
|
||||
307
src/home/vectors.asm
Normal file
307
src/home/vectors.asm
Normal file
@@ -0,0 +1,307 @@
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user