;;-----------------------------------------------------------------------------------------------------
;;
;; This example demonstrates:
;; SCR NEXT BYTE, SCR PREV BYTE, SCR NEXT LINE, SCR PREV LINE
;; for moving around the screen using screen addresses
;;
;; SCR GET LOCATION to get address of top-left of screen (accounts for scrolling
;; and if screen location has changed (e.g. with SCR SET BASE)
;;
;; SCR PIXELS, SCR WRITE, SCR READ for accessing screen in a way that
;; works with upper roms enabled

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_next_byte equ &bc20
scr_prev_byte equ &bc23
scr_next_line equ &bc26
scr_prev_line equ &bc29
scr_get_location equ &bc0b
scr_pixels equ &bc5c
kl_l_rom_enable equ &b906
kl_l_rom_disable equ &b909
ram_lam equ &0020

;; for the indirections you need to have lower rom enabled
;; before you call them to call the default functions!
ind_scr_write equ &bde8
ind_scr_read equ &bde5

spr_height equ 16
spr_width_bytes equ 8

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

start:
;; set mode to 0
xor a
call scr_set_mode

;; write some chars to the screen
;; so we can see that restoring background works for sprites
ld bc,24*20
ld d,' '
l1:
inc d
ld a,d
cp &7f
jr nz,no_char_reset
ld d,' '
no_char_reset:
ld a,d
call txt_output
dec bc
ld a,b
or c
jr nz,l1

call scr_get_location
or h
ld h,a
;; HL = screen address of top-left of screen
ld (scr_addr),hl

;; copy screen behind sprite to a buffer
ld hl,(scr_addr)
ld b,spr_height
ld c,spr_width_bytes
ld de,buffer
call scr_to_mem

;; put sprite onto screen
ld hl,(scr_addr)
ld b,spr_height
ld c,spr_width_bytes
ld de,sprite_data
call mem_to_scr

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

main_loop:
;; wait for VSYNC
call mc_wait_flyback

;; restore screen
ld hl,(scr_addr)
ld b,spr_height
ld c,spr_width_bytes
ld de,buffer
call mem_to_scr

;; check keys
call do_keys

;; scr_addr has potentially been updated

;; copy new screen behind sprite to a buffer
ld hl,(scr_addr)
ld b,spr_height
ld c,spr_width_bytes
ld de,buffer
call scr_to_mem

;; put sprite onto screen
ld hl,(scr_addr)
ld b,spr_height
ld c,spr_width_bytes
ld de,sprite_data
call mem_to_scr

jp main_loop

;;-----------------------------------------------------------------------------------------------------
;; use firmware method SCR_PIXELS to write to ram
write_method_1:
ld b,a
ld c,&ff
call scr_pixels
ret

;;-----------------------------------------------------------------------------------------------------
;; writing always goes to RAM regardless of ROM enabled state
write_method_2:
ld (hl),a
ret

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

;; HL = screen address
;; DE = address of data
;; B = height in lines
;; C = width in bytes 
;; width/2 for mode 0
;; width/4 for mode 1
;; width/8 for mode 2
mem_to_scr:
m2s_height_loop:
push bc
push hl

m2s_width_loop:
push bc
ld a,(de)				;; pixels
inc de
;;call write_method_1
call write_method_2


;; increment screen address for next byte to right
call scr_next_byte		
pop bc
dec c
jr nz,m2s_width_loop

;; restore screen address of start of line
pop hl

;; go down to next line
call scr_next_line

pop bc
djnz m2s_height_loop
ret


;;-----------------------------------------------------------------------------------------------------
;; use SCR READ indirection

read_method_1:
;; enable lower rom to allow indirection to work
call kl_l_rom_enable
push af
;; read pixels
ld c,&ff
call ind_scr_read
;; write them
ld (de),a
inc de
pop af
;;restore lower rom state
call kl_l_rom_disable
ret

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

read_method_2:
rst 4				;; ram lam
ld (de),a
inc de
ret

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


;; copy a section of screen to a buffer
;; HL = screen address
;; B = height in lines
;; C = width in bytes 
;; width/2 for mode 0
;; width/4 for mode 1
;; width/8 for mode 2
scr_to_mem:
s2m_height_loop:
push bc
push hl

s2m_width_loop:
push bc

;;call read_method_1
call read_method_2

;; increment screen address for next byte to right
call scr_next_byte		
pop bc
dec c
jr nz,s2m_width_loop

;; restore screen address of start of line
pop hl

;; go down to next line
call scr_next_line

pop bc
djnz s2m_height_loop
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:
ld hl,(scr_addr)
call scr_prev_line
ld (scr_addr),hl
ret
;;-----------------------------------------------------------------------------------------------------

move_down:
ld hl,(scr_addr)
call scr_next_line
ld (scr_addr),hl
ret
;;-----------------------------------------------------------------------------------------------------

move_left:
ld hl,(scr_addr)
call scr_prev_byte
ld (scr_addr),hl
ret
;;-----------------------------------------------------------------------------------------------------

move_right:
ld hl,(scr_addr)
call scr_next_byte
ld (scr_addr),hl
ret

;;-----------------------------------------------------------------------------------------------------
;; background behind sprite that needs to be restored to screen to erase sprite

buffer:
defs spr_height*spr_width_bytes

;;-----------------------------------------------------------------------------------------------------
;; sprite pixels

sprite_data:
defs spr_height*spr_width_bytes,&ff

;;-----------------------------------------------------------------------------------------------------
;; screen address of sprite
scr_addr:
defw 0