;; This example demonstrates how to draw a compiled sprite on the screen.
;;
;; The compiled sprite code has been generated by a program.
;;
;; Each function draws 1 sprite. The function contains the instructions needed to draw that sprite.
;;
;; Compiled sprites are fast because you avoid unnecessary masking, reading from memory etc.
;; All the data is within each instruction.
;;
;; Compiled sprites are hard, perhaps impossible to clip.
;;

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_set_ink equ &bc32

;; these are not needed by the draw routines, only to erase the sprite after.
;; all sprites in this example are the same size
sprite_height equ 16							;; sprite height in lines
sprite_width_pixels equ 14						;; sprite width in mode 0 pixels
sprite_width_bytes equ sprite_width_pixels/2			;; sprite width in bytes

;;-----------------------------------------------------------------------------------------------------

start:
;; set mode to 0
ld a,0
call scr_set_mode


;; put something on the screen so we can see the sprite masking working
ld d,'A'
ld b,24
l2:
ld c,20
l1:
ld a,d
call txt_output
dec c
jr nz,l1
inc d
dec b
jr nz,l2

;; make table of screen addresses for the start of each scanline
call make_scr_table

;; draw sprite
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,(sprite_coords)
ld (prev_coords),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:
;; coords changed?
ld hl,(prev_coords)
ld bc,(sprite_coords)
or a
sbc hl,bc
ld a,h
or l
jr nz,rs1

;; no change in x or y, so no update
ret
;;-----------------------------------------------------------------------------------------------------

rs1:

;; restore background (where sprite used to be)
ld hl,(prev_coords)
ld de,sprite_background
ld b,sprite_height
ld c,sprite_width_bytes
call sprite_background_restore

draw_sprite_cur_coords:

;; store background pixels where sprite is now located
ld hl,(sprite_coords)
ld de,sprite_background
ld b,sprite_height
ld c,sprite_width_bytes
call sprite_background_store

ld hl,(sprite_coords)
call get_scr_addr

;; uncomment each line to draw each sprite
call sprite_0
;;call sprite_1
;;call sprite_2
ret
;;-----------------------------------------------------------------------------------------------------
;; initialise a table 

make_scr_table:
ld hl,&c000
ld b,200
ld ix,scr_table
mst1:
ld (ix+0),l
ld (ix+1),h
call scr_next_line
inc ix
inc ix
djnz mst1
ret

;;-----------------------------------------------------------------------------------------------------

scr_table:
defs 200*2

;;-----------------------------------------------------------------------------------------------------

;; input conditions:
;; H = x byte coordinate (0-((scr_width_chars*2)-1))
;; L = y coordinate (0-((scr_height_chars*char_height_lines)-1))
;; output conditions:
;; HL = screen address

get_scr_addr:
push bc
push de
ld d,0
ld e,h    ;; DE = x coordinate

ld h,0  ;; HL = y coordinate
add hl,hl
;; x2 for offset into table (2 bytes per y line)
ld bc,scr_table
add hl,bc
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
;; HL = screen address for start of line

;; now add on X
add hl,de
pop de
pop bc
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
ret nc
;; add on amount to go to next char line
ld a,l
add a,&50
ld l,a
ld a,h
adc a,&c0
ld h,a
ret




;;-----------------------------------------------------------------------------------------------------
;; 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:
;; do not move up if we are already on line 0
ld a,(y_coord)
or a
ret z
dec a
ld (y_coord),a
ret
;;-----------------------------------------------------------------------------------------------------

move_down:
;; avoid going off bottom by checking for bottom-most y position

ld a,(y_coord)
sub 200-sprite_height
ret nc      ;; greater than or equal to this pos then don't increment

ld a,(y_coord)
inc a
ld (y_coord),a
ret
;;-----------------------------------------------------------------------------------------------------

move_left:
;; avoiding going off left side
ld a,(x_coord)
or a
ret z
dec a
ld (x_coord),a
ret
;;-----------------------------------------------------------------------------------------------------

right_side equ (40*2)-sprite_width_bytes

move_right:
ld a,(x_coord)
sub right_side
ret nc

ld a,(x_coord)
inc a
ld (x_coord),a
ret
;;-----------------------------------------------------------------------------------------------------


;; H = x byte coord
;; L = y line
;; DE = address to store screen data
;; B = height
;; C = width
;; store a rectangle from the screen into a buffer
sprite_background_store:
call get_scr_addr

sprite_back_height:
push bc
push hl

sprite_back_width:
ld a,(hl)				;; read from screen
ld (de),a				;; store to buffer
inc hl
inc de
dec c
jr nz,sprite_back_width

pop hl
call scr_next_line
pop bc
djnz sprite_back_height
ret

;; H = x byte coord
;; L = y line
;; DE = address to store screen data
;; B = height
;; C = width
;;
;; restore a rectangle to the screen
sprite_background_restore:
call get_scr_addr

sprite_reback_height:
push bc
push hl

sprite_reback_width:
ld a,(de)					;; read from buffer
ld (hl),a					;; write to screen
inc hl
inc de
dec c
jr nz,sprite_reback_width

pop hl
call scr_next_line
pop bc
djnz sprite_reback_height
ret

;;--------------------------------------------------------------------------------------------------------------------------------
;; drawing function for sprite 0
;;
;; the sprite pixel data is "compiled" into the function.
;;
;; ld (hl),&cc for example is writing 2 pixels direct to the screen, the pixel data is &cc.
;;
;; ld a,(hl)
;; and &aa
;; or &44
;; ld (hl),a
;;
;; is masking the screen and writing some pixels
;;
;; inc hl is for skipping a byte containing 2 transparent pixels
;;

;; this code is only for this specific sprite, each sprite has it's own code
;; note the code is not optimised.
;; 
;; entry to each function:
;; HL = screen address

sprite_0:
push hl   ;; remember address of line
inc hl   
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
pop hl      ;; restore address of line
call scr_next_line ;; go to next line down
push hl
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&cc
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&cc
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&cc
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &44
ld (hl),a
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&cc
pop hl
call scr_next_line
push hl
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
pop hl
call scr_next_line
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld (hl),&cc
inc hl
ld a,(hl)
and &55
or &88
ld (hl),a
ret

;;--------------------------------------------------------------------------------------------------------------------------------
;; drawing function for sprite 1

sprite_1:
push hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
push hl
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
inc hl
ld (hl),&c0
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
pop hl
call scr_next_line
ld a,(hl)
and &aa
or &40
ld (hl),a
inc hl
ld (hl),&c0
inc hl
ld a,(hl)
and &55
or &80
ld (hl),a
ret

;;--------------------------------------------------------------------------------------------------------------------------------
;; drawing function for sprite 2

sprite_2:
push hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
ld a,(hl)
and &aa
or &04
ld (hl),a
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
inc hl
inc hl
inc hl
ld a,(hl)
and &aa
ld (hl),a
inc hl
ld (hl),&0c
pop hl
call scr_next_line
push hl
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
pop hl
call scr_next_line
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld (hl),&0c
inc hl
ld a,(hl)
and &55
or &08
ld (hl),a
ret


;;--------------------------------------------------------------------------------------------------------------------------------


;; a buffer to store screen behind sprite
sprite_background:
defs sprite_height*sprite_width_bytes

prev_coords:
prev_y_coord:
defb 0
prev_x_coord:
defb 0

sprite_coords:
y_coord:
defb 0
x_coord:
defb 0

;; make this executable for pasmo, uncomment for pasmo assembler
end start