OK, this is my fastest to render all 28x20 UDGs with no skips, but leaving the 2-character border. You can see from the border switch how many scan lines are left at the bottom of the second frame. The main row loop is fractured so that the last UDG on the row is done separately by a duplicate of the rendering code. And there are a few duplicates of the ends of the loops depending on branching, so the code doesn't have to jump back again to the main code. Can interleaved UDG data do it quicker?
Code: Select all
;================================================================
; Constants
;================================================================
ENTRY_POINT EQU 32768
CHAR_MAP EQU 40000 ; 40000..40767, 768 bytes or more
UDGs EQU 41000 ; 41000..43304, 2304 bytes
;EMPTY_UDGs EQU 0
UDG_TABLE EQU 43520 ; 43520..44031, 512 bytes, 43520=170x256
SCREEN EQU 16384
ATTRS EQU 22528
SCREEN_X_OFFSET EQU 2
SCREEN_Y_OFFSET EQU 2
SCREEN_OFFSET EQU SCREEN_X_OFFSET + (32 * SCREEN_Y_OFFSET)
MAP_COLS EQU 32
MAP_ROWS EQU 24
WINDOW_COLS EQU 28
WINDOW_ROWS EQU 20
WINDOW_X_OFFSET EQU 2
WINDOW_Y_OFFSET EQU 2
WINDOW_OFFSET EQU WINDOW_X_OFFSET + (MAP_COLS * WINDOW_Y_OFFSET)
;================================================================
; Entry / Exit
;================================================================
org ENTRY_POINT
exx ; Alternate registers
push hl ; Save HL' then restore before exit
push iy ; Save IY then restore before exit
ld a,0
test_loop
ex af,af'
halt
ld a,2
out (254),a
call do_render
ld a,7
out (254),a
ex af,af'
dec a
jp nz,test_loop
pop iy ; Restore IY for BASIC
pop hl ; Restore HL' for BASIC
exx
ret ; And return.
;================================================================
; THIS IS THE MAIN FUNCTION
;================================================================
do_render
di ; No interrupts please, we're messing with the stack
ld (restore_stack+1),sp ; POKE the stack address directly into the code for later
exx
ld de,ATTRS + SCREEN_OFFSET ; Attribute address in DE'
ld b,UDG_TABLE / 256 ; UDG Table base in B'
exx
ld hl,SCREEN + SCREEN_OFFSET ; Screen pointer in HL
ld bc,CHAR_MAP + WINDOW_OFFSET ; Character Map pointer in BC
ld ixh,WINDOW_ROWS ; Row loop counter in IXH
rows_loop
ld a,(bc) ; Get first byte from Character Map (address in BC)
exx ; Alternate registers
ld c,WINDOW_COLS*2-2 ; Column loop counter in C'. Gets doubled-DECced by the LDI. Deliberately one short
columns_loop
ld h,b
ld l,a ; Form table address in HL'
ld a,(hl)
inc h
ld h,(hl)
ld l,a ; Get specific address of UDG from table, again in HL'
ldi ; Copy one attribute, Hl'->DE', affects BC'
ld sp,hl ; Point stack at rest of UDG data
exx ; Normal registers
ld a,h ; Save screen pointer, Hi-byte
pop de ; Fetch 2 bytes of UDG data
ld (hl),e ; Write one
inc h ; then
ld (hl),d ; the other...
inc h
pop de
ld (hl),e
inc h
ld (hl),d
inc h
pop de
ld (hl),e
inc h
ld (hl),d
inc h
pop de
ld (hl),e
inc h
ld (hl),d
ld h,a ; Screen pointer back to start of character
inc l ; Then on to next character
inc bc ; Move to next byte in Character Map
ld a,(bc) ; Get next byte from Character Map (redundant on last loop)
exx ; Alternate registers
dec c
jp nz,columns_loop ; Repeat, column loop counter in C'
inc c ; One DEC left in the tank for the LDI below...
; After the loop, still one more UDG to render:
ld h,b
ld l,a ; Form table address in HL'
ld a,(hl)
inc h
ld h,(hl)
ld l,a ; Get specific address of UDG from table, again in HL'
ldi ; Copy one attribute, Hl'->DE', affects BC' but not enough to affect B'
ld sp,hl ; Point stack at rest of UDG data
exx ; Normal registers
ld a,h ; Save screen pointer, Hi-byte
pop de ; Fetch 2 bytes of UDG data
ld (hl),e ; Write one
inc h ; then
ld (hl),d ; the other...
inc h
pop de
ld (hl),e
inc h
ld (hl),d
inc h
pop de
ld (hl),e
inc h
ld (hl),d
inc h
pop de
ld (hl),e
inc h
ld (hl),d
ld h,a ; Screen pointer back to start of character
draw_pixels_next_row
ld a,l
add a,32-WINDOW_COLS+1 ; Add right + left border skip to Screen pointer in HL
ld l,a
exx ; Alternate registers
ld e,a ; Lo-byte of Attribute pointer DE' is the same value
jr c,wrap_block ; If that ADD up there rolled over then the hi-bytes need adding to
exx ; Back to regular registers
ld a,c
add a,32-WINDOW_COLS+1 ; Add right + left border skip to Character Map pointer in BC'
ld c,a
jr c,wrap_map
dec ixh
jp nz,rows_loop ; Repeat, row loop counter in IXH
restore_stack
ld sp,0000 ; Stack restore address will be POKEd in here
ei ; Interrupts back on, now stack is restored
ret
; We could do this in-line and skip over it with JP NC, but that alwasys takes 10 cycles to execute
; JR C only takes 7 cycles if it doesn't branch (12 if it does), and it only branches here twice in
; 20 rows. So even though there's an extra 10 cycles penalty at the end here to jump back again, we
; save (18 * 3) - ((5 + 10) * 2) = 24 cycles
wrap_block
inc d ; Adjust Hi-byte of Attribute pointer DE'
exx ; Back to regular registers
ld a,h
add a,8
ld h,a ; Adjust Hi-byte of Screen pointer HL
ld a,c
add a,32-WINDOW_COLS+1 ; Add right + left border skip to Character Map pointer in BC'
ld c,a
jr c,wrap_map
dec ixh
jp nz,rows_loop ; Repeat, row loop counter in IXH
jp restore_stack
wrap_map
inc b ; Adjust Hi-byte of Character Map pointer BC'
dec ixh
jp nz,rows_loop ; Repeat, row loop counter in IXH
jp restore_stack
;================================================================
; Character map, anywhere in memory:
;================================================================
org CHAR_MAP
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,0,0,0,0,0,0, 4,5,0,0,0,1,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,3,4,5,4,5,0, 1,0,3,4,5,1,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,1,1,1,1,7,3, 1,3,1,6,5,1,5,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,1,6,7,6,7,0, 1,0,1,6,7,1,1,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,6,7,0,0,0,0,6, 7,0,0,0,0,0,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,0,0,8,0,0,0, 0,0,0,0,0,0,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,0,0,0,0,0,0, 4,5,0,0,0,1,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,3,4,5,4,5,0, 1,0,3,4,5,1,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,1,1,1,1,7,3, 1,3,1,6,5,1,5,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,1,6,7,6,7,0, 1,0,1,6,7,1,1,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,6,7,0,0,0,0,6, 7,0,0,0,0,0,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,0,0,8,0,0,0, 0,0,0,0,0,0,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,0,0,0,0,0,0, 4,5,0,0,0,1,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,3,4,5,4,5,0, 1,0,3,4,5,1,0,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,0,1,1,1,1,7,3, 1,3,1,6,5,1,5,8, 0,8,0,8,0,2,2,2
defb 2,2,2,8,0,8,0,8, 0,0,1,6,7,6,7,0, 1,0,1,6,7,1,1,0, 8,0,8,0,8,2,2,2
defb 2,2,2,0,8,0,8,0, 8,6,7,0,0,0,0,6, 7,0,0,0,0,0,0,8, 0,8,0,8,0,2,2,2
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
defb 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2
;================================================================
; UDGs, anywhere in memory:
;================================================================
org UDGs
; Attribute, then data.
defb 00000000b, 00000000b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b
defb 01011011b, 00000000b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b,00000000b
defb 01110000b, 11001100b,01100110b,00110011b,10011001b,11001100b,01100110b,00110011b,10011001b
defb 01000011b, 00111100b,01111110b,11111111b,11111111b,11111111b,11111111b,01111110b,00111100b
defb 01000011b, 00000000b,00000011b,00001111b,00011111b,00111111b,00111111b,01111111b,01111111b
defb 01000011b, 00000000b,11000000b,11110000b,11111000b,11111100b,11111100b,11111110b,11111110b
defb 01000011b, 01111111b,01111111b,00111111b,00111111b,00011111b,00001111b,00000011b,00000000b
defb 01000011b, 11111110b,11111110b,11111100b,11111100b,11111000b,11110000b,11000000b,00000000b
defb 00000001b, 00000000b,00000000b,00100100b,00011000b,00011000b,00100100b,00000000b,00000000b
defs 9*(256-9),0
;================================================================
; UDG table, must be on a 256-byte boundary:
;================================================================
org UDG_TABLE
; Addresses of the remainder of the 256 UDGs, just the Lo-bytes first:
REPT 256, udg_id
defb (UDGs + (udg_id * 9)) & 255
ENDM
; Followed by the Hi-bytes, 256b higher in memory:
REPT 256, udg_id
defb (UDGs + (udg_id * 9)) / 256
ENDM
;================================================================
end ENTRY_POINT
;================================================================