;; This example demonstrates how to draw a sprite on an overscan ;; screen. ;; ;; The overscan screen used here is made using the "32K" overscan ;; method. ;; this has been organised so problem is on left side! ;; avoiding the need for a special scr_next_byte ;; ;; Our screen is located between &0000-&7fff, with a scroll adjustment. ;; This scroll adjustment allows: ;; 1. we can keep lower firmware jumpblock at &0000-&0040 ;; 2. the "problem" address is located on left side (this is where "scr next byte" translates &07ff->&4000 ;; which means we don't need special case for this and we can draw sprites faster. org &8000 ;; turn off listing; remove for pasmo assembler nolist scr_set_mode equ &bc0e txt_output equ &bb5a mc_wait_flyback equ &bd19 km_test_key equ &bb1e scr_height_chars equ 35 ;; scr height in chars (best value to fill screen) char_height_lines equ 8 scr_width_chars equ 48 ;; width of screen in chars (best value for crtc type 2) scr_offset equ 208 ;; scr offset - setup so that the "bad" address ;; is located on the left side of the screen ;; this simplifies "scr next byte" and means sprites ;; can be drawn a bit quicker sprite_height equ 16 ;; sprite height in lines sprite_width_pixels equ 16 ;; sprite width in mode 0 pixels sprite_width_bytes equ sprite_width_pixels/2 ;; sprite width in bytes ;;----------------------------------------------------------------------------------------------------- start: ;; set mode to 0 xor a call scr_set_mode ;; setup crtc ld hl,crtc_vals call set_crtc ;; make table of screen addresses for the start of each scanline call make_scr_table ;; draw sprite using XOR to the screen call draw_sprite_cur_coords ;;--------------------------------------------------- main_loop: ;; wait for VSYNC call mc_wait_flyback ;; get current coords and store them in prev coords ;; we compare new coords to previous coords to determine ;; if we should erase and then redraw the sprite. ;; this avoids continuous flicker if the sprite is not moving ;; however, the sprite can still disapear when moving around ;; the screen because there is a time when the sprite has been ;; deleted, the monitor has drawn it when it has been deleted ;; and we then draw the new version too late. ld hl,(x_coord) ld (prev_x_coord),hl ld hl,(y_coord) ld (prev_y_coord),hl ;; check keys call do_keys ;; wait for next interrupt ;; this ensures our code takes longer than the vsync time ;; so we can always wait just for the start of the vsync halt ;; draw the sprite if it has moved call redraw_sprite jp main_loop ;;----------------------------------------------------------------------------------------------------- redraw_sprite: ;; x coord changed? ld hl,(prev_x_coord) ld bc,(x_coord) or a sbc hl,bc ld a,h or l jr nz,rs1 ;; y coord changed? ld hl,(prev_y_coord) ld bc,(y_coord) or a sbc hl,bc ld a,h or l jr nz,rs1 ;; no change in x or y, so no update ret ;;----------------------------------------------------------------------------------------------------- rs1: ;; erase in old pos ld hl,(prev_x_coord) ld de,(prev_y_coord) call draw_sprite draw_sprite_cur_coords: ;; draw in new pos ld hl,(x_coord) ld de,(y_coord) call draw_sprite ret ;;----------------------------------------------------------------------------------------------------- ;; initialise CRTC ;; HL = address of values ;; R0,R1,R2,R3... R13 set_crtc: ld bc,&bc00 set_crtc_vals: out (c),c inc b ld a,(hl) out (c),a dec b inc hl inc c ld a,c cp 14 jr nz,set_crtc_vals ret ;;----------------------------------------------------------------------------------------------------- ;; initialise a table make_scr_table: ld hl,208*2 ld bc,scr_height_chars*char_height_lines ld ix,scr_table mst1: ld (ix+0),l ld (ix+1),h call scr_next_line inc ix inc ix dec bc ld a,b or c jr nz,mst1 ret ;;----------------------------------------------------------------------------------------------------- scr_table: defs scr_height_chars*char_height_lines*2 ;;----------------------------------------------------------------------------------------------------- ;; input conditions: ;; HL = x byte coordinate (0-((scr_width_chars*2)-1)) ;; DE = y coordinate (0-((scr_height_chars*char_height_lines)-1)) ;; output conditions: ;; HL = screen address get_scr_addr: push bc ex de,hl add hl,hl ld bc,scr_table add hl,bc ld a,(hl) inc hl ld h,(hl) ld l,a add hl,de pop bc ret ;;----------------------------------------------------------------------------------------------------- ;; crtc values crtc_vals: defb &3f ;; R0 - Horizontal Total defb scr_width_chars ;; R1 - Horizontal Displayed defb 48 ;; R2 - Horizontal Sync Position defb &86 ;; R3 - Horizontal and Vertical Sync Widths defb &26 ;; R4 - Vertical Total defb 0 ;; R5 - Vertical Adjust defb scr_height_chars ;; R6 - Vertical Displayed defb 35 ;; R7 - Vertical Sync Position defb 0 ;; R8 - Interlace defb char_height_lines-1 ;; R9 - Max Raster defb 0 ;; R10 - Cursor (not used) defb 0 ;; R11 - Cursor (not used) defb &0c ;; R12 - Screen start (also includes bits for enabling 32K) defb scr_offset ;; R13 - Screen start ;;----------------------------------------------------------------------------------------------------- ;; input conditions: ;; HL = screen address ;; output conditions: ;; HL = screen address (next byte to right) scr_next_byte: inc hl ret ;;----------------------------------------------------------------------------------------------------- ;; input conditions: ;; HL = screen address ;; output conditions: ;; HL = screen address (next scanline down) ;; scr_next_line: ;; go down next scan line ld a,h add a,8 ld h,a ;; check if we should go to next char line and &38 ret nz ;; remove effect of last add ld a,h sub &8 ld h,a ;; add on amount to go to next char line ld a,l add a,scr_width_chars*2 ld l,a ld a,h adc a,&00 ld h,a ;; if we overflowed to next 16k the result will be 0 and &38 ret z ;; we didn't overflow adjust to go back into 1st 16k ld a,h sub &38 ld h,a ret ;;----------------------------------------------------------------------------------------------------- draw_sprite: call get_scr_addr ld de,sprite_pixels ld b,sprite_height ld c,sprite_width_bytes dsl: push bc push hl dsw: ld a,(de) xor (hl) ;; XOR to screen, XOR to remove ld (hl),a inc de call scr_next_byte dec c jr nz,dsw pop hl call scr_next_line pop bc djnz dsl ret ;;----------------------------------------------------------------------------------------------------- ;; sprite width_bytes*sprite_height filled with one value sprite_pixels: defs sprite_width_bytes*sprite_height,&ff ;;----------------------------------------------------------------------------------------------------- ;; check keyboard ;; ;; cursor keys do_keys: ld a,0*8+0 call km_test_key jr nz,move_up ld a,0*8+1 call km_test_key jr nz,move_right ld a,0*8+2 call km_test_key jr nz,move_down ld a,1*8+0 call km_test_key jr nz,move_left ret ;;----------------------------------------------------------------------------------------------------- move_up: ld hl,(y_coord) ld a,h or l ret z dec hl ld (y_coord),hl ret ;;----------------------------------------------------------------------------------------------------- move_down: ;; avoid going off bottom by checking for bottommost y position ld hl,scr_height_chars*char_height_lines ld bc,sprite_height or a sbc hl,bc ld c,l ld b,h ld hl,(y_coord) or a sbc hl,bc ret p ld hl,(y_coord) inc hl ld (y_coord),hl ret ;;----------------------------------------------------------------------------------------------------- move_left: ;; avoiding going off left side ld hl,(x_coord) ld a,h or l ret z dec hl ld (x_coord),hl ret ;;----------------------------------------------------------------------------------------------------- move_right: ;; avoid going off right side by checking for rightmost x coordinate ld hl,scr_width_chars*2 ld bc,sprite_width_bytes or a sbc hl,bc ld c,l ld b,h ld hl,(x_coord) or a sbc hl,bc ret p ld hl,(x_coord) inc hl ld (x_coord),hl ret ;;----------------------------------------------------------------------------------------------------- prev_x_coord: defw 0 prev_y_coord: defw 0 x_coord: defw 0 y_coord: defw 0 ;; make this executable for pasmo, uncomment for pasmo assembler ;;end start