Here's an idea for a vertically-scrolling level background, using columns made from just two background tiles (32 x 16 px each) and scrolling them at different rates.
The simplicity of the scene makes it very quick to draw from scratch, taking just under half a frame.
Code: Select all
; Fast Parallax vertical scrolling scene builder.
; ================================================
; HL = display file, points to end of current pixel row. Use LD SP, HL then PUSH the values.
; IX = graphics data pointer for rocks. Uses LD SP, IX and POP to get the values into BC & DE.
; IY = graphics data pointer for water. Also uses SP & POP but to D'E' & H'L'
; B'C' = used for arithmetic on IX & IY when next graphic required
; BC DE = current rock bitmaps
; D'E' H'L' = current water bitmaps
; A' = loop counter for display_file blocks
; A = used for arithmetic
display_file EQU 16384 + 2048 ; to keep things simple for now, try to stay ahead of the beam! ;)
InterruptVector_I EQU $BF ; doesn't matter... yet! but will when we create the interrupt vector
ORG 32768
; ===== Setup ===== ;
; CBA to set up IM2 yet, so just disable interrupts so we can use IY
DI
; --- Draw colour attributes --- ;
; - Rocks & Water
LD (smc_sp_attr), SP
LD IX, 22528 + 256 + 30 ; 1 block down, rhs
LD H, 6*8 +64 ; bright yellow
LD L, H
LD D, 5*8 ; cyan
LD E, D
LD BC, 32 ; add to IX to get next row
LD A, 16 ; row counter
attr_row:
LD SP, IX
PUSH HL
PUSH HL
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH DE
PUSH HL
PUSH HL
ADD IX, BC
DEC A
JP NZ, attr_row
smc_sp_attr EQU $+1
LD SP, 0
; --- Initialise offsets --- ;
XOR A
LD (tile1_offset_px), A
LD (tile2_offset_px), A
; ================= ;
; === MAIN LOOP === ;
; ================= ;
main:
; ===== Tile graphics ===== ;
; Calculate IX & IY based on offset values
; IX
LD A, (tile1_offset_px)
RLCA ; x2
RLCA ; x4 (4 bytes per px height)
AND 11111100b ; mask off any carried bits
LD B, 0
LD C, A
LD IX, tile_rock
ADD IX, BC
; IY
LD A, (tile2_offset_px)
RLCA ; x2
RLCA ; x4 (4 bytes per px height)
AND 11111100b ; mask off any carried bits
; LD B, 0
LD C, A
LD IY, tile_water
ADD IY, BC
; ===== Build the scene ===== ;
; Disable interrupts
; DI
; Store SP
LD (smc_sp), SP
; Use I to count number of pixels down
XOR A
LD I, A
do_gfx_pixel:
; Calculate display_file destination address
LD A, I
CP 8
JR NC, l_row_bottom
l_row_top:
LD L, 30 ; First PUSH should fill in +29 & +28
JP l_row_done
l_row_bottom
LD L, 32 + 30 ; as above, but a row down
l_row_done:
; Amount to add to H (0-7)
AND 7
ADD A, display_file / 256
LD H, A
; HL is now correct
; Get the graphics
LD SP, IX
POP BC
POP DE
EXX
LD SP, IY
POP DE
POP HL
; Advance graphics pointers to next position
LD BC, 4
ADD IX, BC
ADD IY, BC
EXX
; Loop counters
LD A, 2 ; 2 blocks
EX AF, AF' ; store counter
do_row:
; Get SP into position
LD SP, HL
; Rock x4 cells
PUSH DE
PUSH BC
; Water x20 cells
EXX
PUSH HL
PUSH DE
PUSH HL
PUSH DE
PUSH HL
PUSH DE
PUSH HL
PUSH DE
PUSH HL
PUSH DE
; Rock x4 cells
EXX
PUSH DE
PUSH BC
; Pixel row done!
; Move down 2 cells
LD A, L
ADD A, 64
LD L, A
JP NC, do_row
; Check loop counter - any blocks left to do?
EX AF, AF'
DEC A
JR Z, next_pixel_down
; Carry occurred, so L is in correct place but H isn't
EX AF, AF' ; store loop counter
LD A, H
ADD A, 8
LD H, A
JP do_row
next_pixel_down:
; Update our HL-adjuster
LD A, I
INC A
OUT (254), A ; stripes!
; Done if we've reached the 16th pixel
AND 15
JP Z, finish
; Next!
LD I, A
JP do_gfx_pixel
finish:
; Restore SP
smc_sp EQU $+1
LD SP, 0 ; updated via smc
; IMPORTANT! if using IM2, restore I here!
LD A, InterruptVector_I
LD I, A
; --- Move tiles --- ;
; Update tile offsets
; IX
LD A, (tile1_offset_px)
DEC A ; 1px
AND 15
LD (tile1_offset_px), A
; IY
LD A, (tile2_offset_px)
SUB 3 ; 3px
AND 15
LD (tile2_offset_px), A
; Black border
XOR A
OUT (254), A
; Wait for interrupt, then disable again
; IMPORTANT! if using IM0, restore IY first!
LD IY, $5C3A
EI
HALT ; 50fps
; HALT ; 25fps
DI
JP main
; data
tile1_offset_px DB 0
tile2_offset_px DB 0
; graphics
; priority: CHAR_X, CHAR_LINE, CHAR_Y
;
; n.b. each tile graphic is repeated (making it twice as tall) to avoid having to loop back when advancing IX & IY
tile_water:
; x1
DEFB 239,185,237,245,182,155,109,254
DEFB 84,211,108,166, 93,215,110,179
DEFB 84,245, 59,154,246,181,185, 22
DEFB 163,189,153,151,179, 26,211,213
DEFB 183,121,209,117, 55,233,243, 53
DEFB 126,203,187,124,108, 79, 55,238
DEFB 237,109,126,110,105, 89,110,253
DEFB 89,219,206,229,219,249,231,183
; x2
DEFB 239,185,237,245,182,155,109,254
DEFB 84,211,108,166, 93,215,110,179
DEFB 84,245, 59,154,246,181,185, 22
DEFB 163,189,153,151,179, 26,211,213
DEFB 183,121,209,117, 55,233,243, 53
DEFB 126,203,187,124,108, 79, 55,238
DEFB 237,109,126,110,105, 89,110,253
DEFB 89,219,206,229,219,249,231,183
tile_rock:
; x1
DEFB 224,192,192, 65,249,128,224,193
DEFB 207, 1,249,129,130, 1,159,129
DEFB 130, 1, 12, 1,130, 3, 24, 3
DEFB 134, 7,248, 15,223, 12, 28,127
DEFB 243,152, 15,207,224,240, 12, 3
DEFB 192,192, 8, 1,128, 64, 24, 1
DEFB 128, 64,126, 7,128, 96,195, 31
DEFB 192,127,129,243,192, 96,128, 97
; x2
DEFB 224,192,192, 65,249,128,224,193
DEFB 207, 1,249,129,130, 1,159,129
DEFB 130, 1, 12, 1,130, 3, 24, 3
DEFB 134, 7,248, 15,223, 12, 28,127
DEFB 243,152, 15,207,224,240, 12, 3
DEFB 192,192, 8, 1,128, 64, 24, 1
DEFB 128, 64,126, 7,128, 96,195, 31
DEFB 192,127,129,243,192, 96,128, 97