INCLUDE "hardware.inc" INCLUDE "sprobjs_lib.asm" SECTION "Header", ROM0[$100] jp Init ds $150 - @, 0 ; Make room for the header SECTION "Main", ROM0 Init: ; Shut down audio circuitry ld a, 0 ld [rNR52], a ; Do not turn the LCD off outside of VBlank WaitVBlank: ld a, [rLY] cp 144 jp c, WaitVBlank ; Turn the LCD off ld a, 0 ld [rLCDC], a ; Initialize Sprite Object Library call InitSprObjLib ; Reset hardware OAM xor a, a ld b, 160 ld hl, _OAMRAM .resetOAM ld [hli], a dec b jr nz, .resetOAM ; Copy cat sprite ld de, GfxCat ld hl, $8000 ld bc, GfxCat.end - GfxCat call Memcopy ; Reset positions ld hl, wMetaspritePosition.x ld a, $10 ld [wMetaspritePosition.x], a ld a, $0 ld [wMetaspritePosition.x + 1], a ld hl, wMetaspritePosition.y ld a, $10 ld [wMetaspritePosition.y], a ld a, $01 ld [wMetaspritePosition.y + 1], a ld hl, wMetaspritePosition.z ld a, $00 ld [wMetaspritePosition.z], a ld a, $0 ld [wMetaspritePosition.z + 1], a ; Reset velocity ld hl, wMetaspriteVelocity.x ld a, $0 ld [wMetaspriteVelocity.x], a ld a, $0 ld [wMetaspriteVelocity.x + 1], a ld hl, wMetaspriteVelocity.y ld a, $0 ld [wMetaspriteVelocity.y], a ld a, $0 ld [wMetaspriteVelocity.y + 1], a ld hl, wMetaspriteVelocity.z ld a, $0 ld [wMetaspriteVelocity.z], a ld a, $0 ld [wMetaspriteVelocity.z + 1], a ld hl, wMetaspriteVelocity.boost ld a, $0 ld [wMetaspriteVelocity.boost], a ld [wMetaspriteVelocity.boosting], a ; Copy the tile data ld de, Tiles ld hl, $9000 ld bc, TilesEnd - Tiles call Memcopy ; Copy the tilemap ld de, Tilemap ld hl, $9800 ld bc, TilemapEnd - Tilemap call Memcopy ; Turn the LCD on ld a, LCDCF_ON | LCDCF_BGON | LCDCF_OBJON | LCDCF_OBJ8 ld [rLCDC], a ; During the first (blank) frame, initialize display registers ld a, %11100100 ld [rBGP], a ld [rOBP0], a ld [rOBP1], a ; Enable vblank interrupt ld a, IEF_VBLANK ldh [rIE], a ; Clear interrupt register to 0 xor a, a ldh [rIF], a ; Enable interrupts ei Main:: call ResetShadowOAM call UpdateJoypadState call HandleInput call HandlePhysics call HandleRender halt nop jp Main HandleInput:: ld a, [wJoypadState] bit PADB_LEFT, a jr nz, .leftend ld a, [wMetaspritePosition.x] add a, 16 ld b, a ld [wMetaspritePosition.x], a ld a, [wMetaspritePosition.x+1] adc 0 ld c, a ld [wMetaspritePosition.x+1], a .leftend: ld a, [wJoypadState] bit PADB_RIGHT, a jr nz, .rightend ld a, [wMetaspritePosition.x] sub a, 16 ld b, a ld [wMetaspritePosition.x], a ld a, [wMetaspritePosition.x+1] sbc 0 ld c, a ld [wMetaspritePosition.x+1], a .rightend: ld a, [wJoypadState] bit PADB_UP, a jr nz, .upend ld a, [wMetaspritePosition.y] add a, 16 ld b, a ld [wMetaspritePosition.y], a ld a, [wMetaspritePosition.y+1] adc 0 ld c, a ld [wMetaspritePosition.y+1], a .upend: ld a, [wJoypadState] bit PADB_DOWN, a jr nz, .downend ld a, [wMetaspritePosition.y] sub a, 16 ld b, a ld [wMetaspritePosition.y], a ld a, [wMetaspritePosition.y+1] sbc 0 ld c, a ld [wMetaspritePosition.y+1], a .downend: ; Skip jump code if not on ground ld a, [wMetaspritePosition.z] ld b, a ld a, [wMetaspritePosition.z + 1] or a, b jr nz, .jumpend ; While on ground, set boost ld a, $8 ld [wMetaspriteVelocity.boost], a ld a, [wJoypadPressed] bit PADB_A, a jr z, .boostclear ld a, [wMetaspriteVelocity.z] sub a, $20 ld b, a ld [wMetaspriteVelocity.z], a ld a, [wMetaspriteVelocity.z+1] sbc a, $00 ld c, a ld [wMetaspriteVelocity.z+1], a jr .boostend .jumpend: ld a, [wJoypadState] bit PADB_A, a jr z, .boostclear ld a, $FF ld [wMetaspriteVelocity.boosting], a jr .boostend .boostclear: ld a, $00 ld [wMetaspriteVelocity.boosting], a .boostend: ret HandleRender:: ; Render the player ; load de ld a, [wMetaspritePosition.x] ld e, a ld a, [wMetaspritePosition.x + 1] ld d, a ; load bc ld a, [wMetaspritePosition.z] ld c, a ld a, [wMetaspritePosition.y] add a, c ld c, a ld a, [wMetaspritePosition.z + 1] ld b, a ld a, [wMetaspritePosition.y + 1] adc a, b ld b, a ld hl, CatMetasprite call RenderMetasprite ; Render the shadow ; load de ld a, [wMetaspritePosition.x] ld e, a ld a, [wMetaspritePosition.x + 1] ld d, a ; load bc ld a, [wMetaspritePosition.y] ld c, a ld a, [wMetaspritePosition.y + 1] ld b, a ld hl, ShadowMetasprite call RenderMetasprite ret HandlePhysics:: ; Skip gravity if on ground ld a, [wMetaspritePosition.z] ld b, a ld a, [wMetaspritePosition.z + 1] or a, b jr z, .endGravity ; Skip gravity if boosting ld a, [wMetaspriteVelocity.boosting] ld b, a ld a, [wMetaspriteVelocity.boost] and a, b cp 0 jr z, .startGravity ld a, [wMetaspriteVelocity.boost] dec a ld [wMetaspriteVelocity.boost], a jr .endGravity .startGravity ; Apply gravity ld a, [wMetaspriteVelocity.z] add a, $04 ld [wMetaspriteVelocity.z], a ld a, [wMetaspriteVelocity.z+1] adc a, $00 ld [wMetaspriteVelocity.z+1], a .endGravity: ; Calculate new z, place in bc ld a, [wMetaspriteVelocity.z] ld c, a ld a, [wMetaspritePosition.z] add c ld c, a ld a, [wMetaspriteVelocity.z + 1] ld b, a ld a, [wMetaspritePosition.z + 1] adc b ld b, a ; If z is greater than or equal to 0, pos/vel to 0 jr nz, .endLanding jr nc, .endLanding .startLanding ld a, 0 ld [wMetaspritePosition.z], a ld [wMetaspritePosition.z+1], a ld [wMetaspriteVelocity.z], a ld [wMetaspriteVelocity.z+1], a ld b, a ld c, a .endLanding ; Update z position ld a, c ld [wMetaspritePosition.z], a ld a, b ld [wMetaspritePosition.z+1], a ret SECTION "Joypad Routine", ROM0 UpdateJoypadState:: ld hl, rP1 ld [hl], P1F_GET_BTN ; Read button state twice to ensure we get the proper state ld a, [hl] ld a, [hl] ld [hl], P1F_GET_DPAD cpl ; Inputs are active low - invert so it makes more sense and PADF_A | PADF_B | PADF_SELECT | PADF_START ld c, a ; Store lower 4 button bits in c ; On real hardware, rP1 needs to be read 8 times to ensure proper state is read ld b, 8 .dpadDebounceLoop: ld a, [hl] dec b jr nz, .dpadDebounceLoop ld [hl], P1F_GET_NONE ; Disable joypad inputs swap a ; Swap the nibbles to store dpad in upper 4 bits cpl ; invert the bits and PADF_RIGHT | PADF_LEFT | PADF_UP | PADF_DOWN or c ld c, a ; Compare with previously stored state ld hl, wJoypadState xor [hl] and c ld [wJoypadPressed], a ld a, c ld [wJoypadState], a ret SECTION "MemCopy Routine", ROM0 Memcopy:: dec bc inc b inc c .loop: ld a, [de] ld [hli], a inc de dec c jr nz, .loop dec b jr nz, .loop ret SECTION "VBlank Interrupt", ROM0[$0040] VBlankInterrupt: ; Store registers to prevent clobbering push af push bc push de push hl jp VBlankHandler SECTION "VBlank Handler", ROM0 VBlankHandler: ldh a, [hFrameCounter] /* bit 5, a ld a, %11100100 jr z, .skipCpl cpl ; Invert a .skipCpl: ldh [rBGP], a */ ld a, HIGH(wShadowOAM) call hOAMDMA ; Reset registers to orignal state pop hl pop de pop bc pop af reti SECTION "Frame Counter", HRAM hFrameCounter: db SECTION "Tile data", ROM0 Tiles: db $00,$ff, $00,$ff, $00,$ff, $00,$ff, $00,$ff, $00,$ff, $00,$ff, $00,$ff db $00,$ff, $00,$80, $00,$80, $00,$80, $00,$80, $00,$80, $00,$80, $00,$80 db $00,$ff, $00,$7e, $00,$7e, $00,$7e, $00,$7e, $00,$7e, $00,$7e, $00,$7e db $00,$ff, $00,$01, $00,$01, $00,$01, $00,$01, $00,$01, $00,$01, $00,$01 db $00,$ff, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00 db $00,$ff, $00,$7f, $00,$7f, $00,$7f, $00,$7f, $00,$7f, $00,$7f, $00,$7f db $00,$ff, $03,$fc, $00,$f8, $00,$f0, $00,$e0, $20,$c0, $00,$c0, $40,$80 db $00,$ff, $c0,$3f, $00,$1f, $00,$0f, $00,$07, $04,$03, $00,$03, $02,$01 db $00,$80, $00,$80, $7f,$80, $00,$80, $00,$80, $7f,$80, $7f,$80, $00,$80 db $00,$7e, $2a,$7e, $d5,$7e, $2a,$7e, $54,$7e, $ff,$00, $ff,$00, $00,$00 db $00,$01, $00,$01, $ff,$01, $00,$01, $01,$01, $fe,$01, $ff,$01, $00,$01 db $00,$80, $80,$80, $7f,$80, $80,$80, $00,$80, $ff,$80, $7f,$80, $80,$80 db $00,$7f, $2a,$7f, $d5,$7f, $2a,$7f, $55,$7f, $ff,$00, $ff,$00, $00,$00 db $00,$ff, $aa,$ff, $55,$ff, $aa,$ff, $55,$ff, $fa,$07, $fd,$07, $02,$07 db $00,$7f, $2a,$7f, $d5,$7f, $2a,$7f, $55,$7f, $aa,$7f, $d5,$7f, $2a,$7f db $00,$ff, $80,$ff, $00,$ff, $80,$ff, $00,$ff, $80,$ff, $00,$ff, $80,$ff db $40,$80, $00,$80, $7f,$80, $00,$80, $00,$80, $7f,$80, $7f,$80, $00,$80 db $00,$3c, $02,$7e, $85,$7e, $0a,$7e, $14,$7e, $ab,$7e, $95,$7e, $2a,$7e db $02,$01, $00,$01, $ff,$01, $00,$01, $01,$01, $fe,$01, $ff,$01, $00,$01 db $00,$ff, $80,$ff, $50,$ff, $a8,$ff, $50,$ff, $a8,$ff, $54,$ff, $a8,$ff db $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80 db $ff,$00, $ff,$00, $ff,$00, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e db $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $fe,$01 db $7f,$80, $ff,$80, $7f,$80, $ff,$80, $7f,$80, $ff,$80, $7f,$80, $ff,$80 db $ff,$00, $ff,$00, $ff,$00, $aa,$7f, $d5,$7f, $aa,$7f, $d5,$7f, $aa,$7f db $f8,$07, $f8,$07, $f8,$07, $80,$ff, $00,$ff, $aa,$ff, $55,$ff, $aa,$ff db $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $ff,$80, $7f,$80, $ff,$80 db $d5,$7f, $aa,$7f, $d5,$7f, $aa,$7f, $d5,$7f, $aa,$7f, $d5,$7f, $aa,$7f db $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $eb,$3c db $54,$ff, $aa,$ff, $54,$ff, $aa,$ff, $54,$ff, $aa,$ff, $54,$ff, $aa,$ff db $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $00,$ff db $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $2a,$ff db $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $80,$ff db $7f,$80, $ff,$80, $7f,$80, $ff,$80, $7f,$80, $ff,$80, $7f,$80, $aa,$ff db $ff,$00, $ff,$00, $ff,$00, $ff,$00, $ff,$00, $ff,$00, $ff,$00, $2a,$ff db $ff,$01, $fe,$01, $ff,$01, $fe,$01, $fe,$01, $fe,$01, $fe,$01, $80,$ff db $7f,$80, $ff,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $7f,$80, $00,$ff db $fe,$01, $fe,$01, $fe,$01, $fe,$01, $fe,$01, $fe,$01, $fe,$01, $80,$ff db $3f,$c0, $3f,$c0, $3f,$c0, $1f,$e0, $1f,$e0, $0f,$f0, $03,$fc, $00,$ff db $fd,$03, $fc,$03, $fd,$03, $f8,$07, $f9,$07, $f0,$0f, $c1,$3f, $82,$ff db $55,$ff, $2a,$7e, $54,$7e, $2a,$7e, $54,$7e, $2a,$7e, $54,$7e, $00,$7e db $01,$ff, $00,$01, $01,$01, $00,$01, $01,$01, $00,$01, $01,$01, $00,$01 db $54,$ff, $ae,$f8, $50,$f0, $a0,$e0, $60,$c0, $80,$c0, $40,$80, $40,$80 db $55,$ff, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00 db $55,$ff, $6a,$1f, $05,$0f, $02,$07, $05,$07, $02,$03, $03,$01, $02,$01 db $54,$ff, $80,$80, $00,$80, $80,$80, $00,$80, $80,$80, $00,$80, $00,$80 db $55,$ff, $2a,$1f, $0d,$07, $06,$03, $01,$03, $02,$01, $01,$01, $00,$01 db $55,$ff, $2a,$7f, $55,$7f, $2a,$7f, $55,$7f, $2a,$7f, $55,$7f, $00,$7f db $55,$ff, $aa,$ff, $55,$ff, $aa,$ff, $55,$ff, $aa,$ff, $55,$ff, $00,$ff db $15,$ff, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00, $00,$00 db $55,$ff, $6a,$1f, $0d,$07, $06,$03, $01,$03, $02,$01, $03,$01, $00,$01 db $54,$ff, $a8,$ff, $54,$ff, $a8,$ff, $50,$ff, $a0,$ff, $40,$ff, $00,$ff db $00,$7e, $2a,$7e, $d5,$7e, $2a,$7e, $54,$7e, $ab,$76, $dd,$66, $22,$66 db $00,$7c, $2a,$7e, $d5,$7e, $2a,$7e, $54,$7c, $ff,$00, $ff,$00, $00,$00 db $00,$01, $00,$01, $ff,$01, $02,$01, $07,$01, $fe,$03, $fd,$07, $0a,$0f db $00,$7c, $2a,$7e, $d5,$7e, $2a,$7e, $54,$7e, $ab,$7e, $d5,$7e, $2a,$7e db $00,$ff, $a0,$ff, $50,$ff, $a8,$ff, $54,$ff, $a8,$ff, $54,$ff, $aa,$ff db $dd,$62, $bf,$42, $fd,$42, $bf,$40, $ff,$00, $ff,$00, $f7,$08, $ef,$18 db $ff,$00, $ff,$00, $ff,$00, $ab,$7c, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e db $f9,$07, $fc,$03, $fd,$03, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $fe,$01 db $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7e, $d5,$7e, $ab,$7c db $f7,$18, $eb,$1c, $d7,$3c, $eb,$3c, $d5,$3e, $ab,$7e, $d5,$7e, $2a,$ff db $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $fe,$01, $ff,$01, $a2,$ff db $7f,$c0, $bf,$c0, $7f,$c0, $bf,$e0, $5f,$e0, $af,$f0, $57,$fc, $aa,$ff db $ff,$01, $fc,$03, $fd,$03, $fc,$03, $f9,$07, $f0,$0f, $c1,$3f, $82,$ff db $55,$ff, $2a,$ff, $55,$ff, $2a,$ff, $55,$ff, $2a,$ff, $55,$ff, $00,$ff db $45,$ff, $a2,$ff, $41,$ff, $82,$ff, $41,$ff, $80,$ff, $01,$ff, $00,$ff db $54,$ff, $aa,$ff, $54,$ff, $aa,$ff, $54,$ff, $aa,$ff, $54,$ff, $00,$ff db $15,$ff, $2a,$ff, $15,$ff, $0a,$ff, $15,$ff, $0a,$ff, $01,$ff, $00,$ff db $01,$ff, $80,$ff, $01,$ff, $80,$ff, $01,$ff, $80,$ff, $01,$ff, $00,$ff TilesEnd: SECTION "Tilemap", ROM0 Tilemap: db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $01, $02, $03, $01, $04, $03, $01, $05, $00, $01, $05, $00, $06, $04, $07, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $08, $09, $0a, $0b, $0c, $0d, $0b, $0e, $0f, $08, $0e, $0f, $10, $11, $12, $13, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $14, $15, $16, $17, $18, $19, $1a, $1b, $0f, $14, $1b, $0f, $14, $1c, $16, $1d, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $1e, $1f, $20, $21, $22, $23, $24, $22, $25, $1e, $22, $25, $26, $22, $27, $1d, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $01, $28, $29, $2a, $2b, $2c, $2d, $2b, $2e, $2d, $2f, $30, $2d, $31, $32, $33, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $08, $34, $0a, $0b, $11, $0a, $0b, $35, $36, $0b, $0e, $0f, $08, $37, $0a, $38, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $14, $39, $16, $17, $1c, $16, $17, $3a, $3b, $17, $1b, $0f, $14, $3c, $16, $1d, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $1e, $3d, $3e, $3f, $22, $27, $21, $1f, $20, $21, $22, $25, $1e, $22, $40, $1d, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $41, $42, $43, $44, $30, $33, $41, $45, $43, $41, $30, $43, $41, $30, $33, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, 0,0,0,0,0,0,0,0,0,0,0,0 TilemapEnd: SECTION "Graphics", ROM0 GfxCat: INCBIN "cat.2bpp" .end:: CatMetasprite: db 16, 8, 0, 0 db 12, 16, 0, 0 db 20, 20, 0, 0 db 24, 12, 0, 0 db 128 ShadowMetasprite: db 24, 12, 0, 0 db 128 SECTION "Position Vars", WRAM0 ; Q12.4 fixed-point X posiition wMetaspritePosition:: .x: dw .y: dw .z: dw ; Q4.4 fixed-point velocity wMetaspriteVelocity:: .x: dw .y: dw .z: dw .boost: db .boosting: db SECTION "Joypad Vars", WRAM0 wJoypadState: ds 1 wJoypadPressed: ds 1