gb3dplatformer/sprobjs_lib.asm

203 lines
5.0 KiB
NASM

; Sprite Objects Library - by Eievui
;
; This is a small, lightweight library meant to facilitate the rendering of
; sprite objects, including Shadow OAM and OAM DMA, single-entry "simple" sprite
; objects, and Q12.4 fixed-point position metasprite rendering.
;
; The library is only 127 bytes of ROM0, 160 bytes of WRAM0 for Shadow OAM, and a
; single HRAM byte for tracking the current position in OAM.
;
; The library is relatively simple to use, with 4 steps to rendering:
; 1. Call InitSprObjLib during initilizations - This copies the OAMDMA function to
; HRAM.
; 2. Call ResetShadowOAM at the beginning of each frame - This hides all sprites
; and resets hOAMIndex, allowing you to render a new frame of sprites.
; 3. Call rendering functions - Push simple sprites or metasprites to Shadow OAM.
; 4. Wait for VBlank and call hOAMDMA - Copies wShadowOAM to the Game Boy's OAM in
; just 160 M-cycles. Make sure to pass HIGH(wShadowOAM) in the a register.
;
; Copyright 2021, Eievui
;
; 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 "hardware.inc"
SECTION "OAM DMA Code", ROM0
OAMDMACode::
LOAD "OAM DMA", HRAM
; Begin an OAM DMA, waiting 160 cycles for the DMA to finish.
; This quickly copies Shadow OAM to the Game Boy's OAM, allowing the PPU to draw
; the objects. hOAMDMA should be called once per frame near the end of your
; VBlank interrupt. While an OAM DMA is running no sprites objects can be drawn
; by the PPU, which makes it preferrable to run within the VBlank interrupt, but
; it can be run at any point if more than 40 sprite objects are needed.
; @param a: High byte of active Shadow OAM. Shadow OAM must be aligned to start
; at the beginning of a page (low byte == $00).
hOAMDMA::
ldh [rDMA], a
ld a, 40
.wait
dec a
jr nz, .wait
ret
ENDL
OAMDMACodeEnd::
SECTION "Initialize Sprite Object Library", ROM0
; Initializes the sprite object library, copying things such as the hOAMDMA
; function and reseting hOAMIndex
; @clobbers: a, bc, hl
InitSprObjLib::
; Copy OAM DMA.
ld b, OAMDMACodeEnd - OAMDMACode
ld c, LOW(hOAMDMA)
ld hl, OAMDMACode
.memcpy
ld a, [hli]
ldh [c], a
inc c
dec b
jr nz, .memcpy
xor a, a
ldh [hOAMIndex], a ; hOAMIndex must be reset before running ResetShadowOAM.
ret
SECTION "Reset Shadow OAM", ROM0
; Reset the Y positions of every sprite object that was used in the last frame,
; effectily hiding them, and reset hOAMIndex. Run this function each frame
; before rendering sprite objects.
; @clobbers: a, c, hl
ResetShadowOAM::
xor a, a ; clear carry
ldh a, [hOAMIndex]
rra
rra ; a / 4
and a, a
jr z, .skip
ld c, a
ld hl, wShadowOAM
xor a, a
.clearOAM
ld [hli], a
inc l
inc l
inc l
dec c
jr nz, .clearOAM
ldh [hOAMIndex], a
.skip
ret
SECTION "Render Simple Sprite", ROM0
; Render a single object, or sprite, to OAM.
; @param b: Y position
; @param c: X position
; @param d: Tile ID
; @param e: Tile Attribute
; @clobbers: hl
RenderSimpleSprite::
ld h, HIGH(wShadowOAM)
ldh a, [hOAMIndex]
ld l, a
ld a, b
add a, 16
ld [hli], a
ld a, c
add a, 8
ld [hli], a
ld a, d
ld [hli], a
ld a, e
ld [hli], a
ld a, l
ldh [hOAMIndex], a
ret
SECTION "Render Metasprite", ROM0
; Render a metasprite to OAM.
; @param bc: Q12.4 fixed-point Y position.
; @param de: Q12.4 fixed-point X position.
; @param hl: Pointer to current metasprite.
RenderMetasprite::
; Adjust Y and store in b.
ld a, c
rrc b
rra
rrc b
rra
rrc b
rra
rrc b
rra
ld b, a
; Adjust X and store in c.
ld a, e
rrc d
rra
rrc d
rra
rrc d
rra
rrc d
rra
ld c, a
; Load Shadow OAM pointer.
ld d, HIGH(wShadowOAM)
ldh a, [hOAMIndex]
ld e, a
; Now:
; bc - Y, X
; de - Shadow OAM
; hl - Metasprite
; Time to render!
.loop
; Load Y.
ld a, [hli]
add a, b
ld [de], a
inc e
; Load X.
ld a, [hli]
add a, c
ld [de], a
inc e
; Load Tile.
ld a, [hli]
ld [de], a
inc e
; Load Attribute.
ld a, [hli]
ld [de], a
inc e
; Check for null end byte.
ld a, [hl]
cp a, 128
jr nz, .loop
ld a, e
ldh [hOAMIndex], a
ret
SECTION "Shadow OAM", WRAM0, ALIGN[8]
wShadowOAM::
ds 160
SECTION "Shadow OAM Index", HRAM
; The current low byte of shadow OAM.
hOAMIndex::
db