What I am doing currently, direct to screen with a clear list and a draw list
Code: Select all
mainloop:
IF TIMING
ld a, 7
out (#fe), a
ENDIF
REPT FRAMEDELAY
halt
ENDR
; all my draw routines modify SP to do fast 16 bit reads/writes so I need to stash the SP and restore it after drawing is done
; it means I can't use any CALLs or PUSH/POP like you usually would in my drawing
di
ld (savesp+1), sp
IF TIMING
xor a
out (#FE), a
ENDIF
ld hl, (clrListPtr)
nextClear:
ld a, (hl)
or a
jr z, doneClear
inc hl
ld (clrListPtr), hl ; first byte is pointer to a clear routine, which is aligned at an address multiple of 256 so I only need to store 1 byte
ex de, hl ; routine I jump to can get back the pointer to their data in DE
ld l, 0
ld h, a
jp (hl)
doneClear:
ld hl, clrList
ld (clrListPtr), hl
IF TIMING
ld a, 1
out (#fe), a
ENDIF
ld hl, (drawListPtr)
nextSprite:
ld a, (hl)
or a
jr z, doneDraw
inc hl
ld (drawListPtr), hl ; first byte is pointer to a draw routine, which is aligned at an address multiple of 256 so I only need to store 1 byte
ex de, hl ; routine I jump to can get back the pointer to their data in DE
ld l, 0
ld h, a
jp (hl)
doneDraw:
ld hl, drawList
ld (drawListPtr), hl
savesp:
ld sp, 0
ei
ld hl, (phase) ; this is a pointer to the routine that performs game logic, input etc.
jp (hl)
Each command sits on a 256 byte boundary so I only need to store 1 byte to jump to it (so #93 code is at address #9300 and so on).
The routine that is jumped to has DE pointing at the rest of the data for the command which they unpack and then do stuff with, e.g.
Code: Select all
ALIGN 256
clear16x8:
; attribs
;ld hl, (clrListPtr)
ex de, hl
ld e, (hl)
inc hl ; 6T
ld d, (hl) ; 7T
ld a, d ; 4T
xor ATTRIBS_MAGIC
ld l, e
ld h, a
dec l ; attrib address, should be safe to not cross a boundary
dec l
ld sp, hl
pop bc
ex de, hl
ld sp, hl
push bc
ld hl, (clrListPtr)
inc hl
inc hl
;ld (clrListPtr), hl
jp nextClear
parameters are the attribute address to clear (they do this by copying from a background attribs buffer instead of erasing any pixels, background attribs are at address #5800 which I can flip between normal attribs and background attribs via XORing the high byte with ATTRIBS_MAGIC (which has to be 1024 byte aligned for that to work).
Code: Select all
ATTRIBS_ADDR EQU #5800
ATTRIBS_MAGIC EQU #58^(attribs/256)
; background attribs
ALIGN 1024
attribs: BLOCK 768
Once the routine finishes they make sure HL is pointing to the next command in the list (it's terminated by a single byte of 0) and jp to the loop again.
So in my mainloop I do:
HALT
process clear and draw lists
jump to the game loop code for what phase we are currently in (playing, physics, scoring) which does all the logic and adds draw and clear commands to the lists for the next frame. So all the drawing for the previous frame is done before we even start deciding what to draw next frame.
repeat
Running screen... all drawing is done by the time the dark blue border is displayed. White border = waiting for VBlank. (EDIT: Except for drawing the collision minimap on right hand side of the screen... that's being drawn when the border is cyan. That's only used to debug though won't be in the game once everything works)