;; Example which draws a tilemap.
;;
;; A tilemap is an array of tile indexes. In the code it is stored as 
;; a 1-dimensional linear array, but is accessed like it is a 2d array.
;; e.g. tilemap[(tiley*tilemap_width_tiles)+tilex]
;;
;; The tile indexes define the index of the tile who's graphics should
;; be drawn.
;;
;; Each tile is 8x16 pixels and they are stored uncompressed in a form
;; that can be poked direct to the screen. This example is designed for
;; mode 0.
;;
;; In this example there are multiple tilemaps. These do not scroll.
;; A simple map is stored where each tilemap is part of the map. Pressing
;; the cursor keys moves you between each tilemap left, right up and down.
;;
;; This source released under GNU Library License v1 or later.
;;
;; Code by Kevin Thacker, 2010.
;; 
;; Tile graphics by Markus.

;; width of a tile
tile_width_bytes equ 8/2
;; height of a tile
tile_height equ 16
;; number of bytes used by tile (each tile uses same 
;; number of bytes and stored uncompressed in a form
;; that can be poked direct to screen)
tile_data_size equ tile_width_bytes*tile_height

;; number of tiles we handles
num_tiles equ 4

;; width of screen in pixels
scr_width_pixels equ 160
;; height of screen in pixels
scr_height_pixels equ 200

scr_width_bytes equ scr_width_pixels/2

;; width of screen in tiles
tilemap_width_tiles equ scr_width_bytes/tile_width_bytes

;; height of screen in tiles
tilemap_height_tiles equ scr_height_pixels/tile_height

;; index into directions for each tilemap
;; indicating which tilemap is to left/right/up/down of current
tilemap_dir_left equ 0
tilemap_dir_right equ tilemap_dir_left+1
tilemap_dir_up equ tilemap_dir_right+1
tilemap_dir_down equ tilemap_dir_up+1
tilemap_num_dirs equ tilemap_dir_down+1

tilemap_dir_invalid equ &ff

;; size of table holding tilemap ptrs (in bytes)
size_tilemap_ptrs equ end_tilemap_ptrs - tilemap_ptrs 

;; each address is 16-bit, so number of tiles is size in bytes /2
num_tilemaps equ size_tilemap_ptrs/2

;; firmware functions we use
scr_next_line equ &bc26
scr_set_mode equ &bc0e
scr_set_border equ &bc38
scr_set_ink equ &bc32
mc_wait_flyback equ &bd19
km_test_key equ &bb1e

org &1000
nolist

start:

;; set 16 colour mode 1
ld a,0
call scr_set_mode

;; set border to black
ld bc,0
call scr_set_border

;; set pens
ld hl,pens
ld b,16			;; 4 pens for mode 1
xor a			;; initial pen index
set_pens
push bc
push af
ld c,(hl)		;; B,C = inks for pen. If they are the same then no 
				;; flashing
ld b,c
inc hl
push hl
call scr_set_ink
pop hl
pop af
pop bc
inc a			;; increment pen index
djnz set_pens

call make_scr_table
call make_tile_ptr_table

xor a
ld (tilemap_cur),a
ld a,1
ld (tilemap_changed),a


;; the main loop which draws and updates sprite
main_loop:
call mc_wait_flyback


;; check keys and update sprite position
call check_keys
ld a,(tilemap_changed)
or a
jp z,main_loop
xor a
ld (tilemap_changed),a

call tilemap_draw

jp main_loop


;; check if a key has been pressed and perform action if it has
check_keys:
ld a,0					;; cursor up
call km_test_key
jp nz,tilemap_up
ld a,2					;; cursor down
call km_test_key
jp nz,tilemap_down
ld a,8					;; cursor left
call km_test_key
jp nz,tilemap_left
ld a,1					;; cursor right
call km_test_key
jp nz,tilemap_right
ret

get_tilemap_dirs:
ld a,(tilemap_cur)
ld l,a
ld h,0
add hl,hl			;; x2
add hl,hl			;; x2 
					;; = x4 (number of directions)
ld de,tilemap_dirs
add hl,de
push hl
pop ix
ret

;; based on current tilemap, move to next tilemap to left

tilemap_left:
call get_tilemap_dirs

ld a,(ix+tilemap_dir_left)
cp tilemap_dir_invalid
ret z
ld (tilemap_cur),a
ld a,1
ld (tilemap_changed),a
ret

;; based on current tilemap, move to next tilemap to right
tilemap_right:
call get_tilemap_dirs


ld a,(ix+tilemap_dir_right)
cp tilemap_dir_invalid
ret z
ld (tilemap_cur),a
ld a,1
ld (tilemap_changed),a
ret

;; based on current tilemap, move to next tilemap up
tilemap_up:
call get_tilemap_dirs


ld a,(ix+tilemap_dir_up)
cp tilemap_dir_invalid
ret z
ld (tilemap_cur),a
ld a,1
ld (tilemap_changed),a
ret

;; based on current tilemap, move to next tilemap to down

tilemap_down:
call get_tilemap_dirs


ld a,(ix+tilemap_dir_down)
cp tilemap_dir_invalid
ret z
ld (tilemap_cur),a
ld a,1
ld (tilemap_changed),a
ret

;; tilemap is drawn from left-to-right
;; top-to-bottom one tile at a time

tilemap_draw:

;; get address of tilemap data
ld a,(tilemap_cur)
ld l,a
ld h,0
add hl,hl
ld de,tilemap_ptrs
add hl,de
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
;; HL = ptr to tilemap indices
push hl
pop ix
;; IX = ptr to tilemap indices

;; H = X screen coord of top-left of tilemap
ld h,0
;; L = Y screen coord of top-left of tilemap
ld l,0

;; width and height of tilemap in tiles
ld b,tilemap_height_tiles
ld c,tilemap_width_tiles
tilemap_draw_y:
push hl
push bc

;; calc screen address
;; from x,y coord
call get_scr_addr

tilemap_draw_x:
;; read tile index
ld a,(ix+0)
inc ix
push bc
push hl
;; lookup ptr to tile pixels
call get_tile_ptr
;; now draw tile
call tile_draw
pop hl

;; HL = screen address
;; increment by width of tile in bytes
ld bc,tile_width_bytes
add hl,bc

pop bc

dec c
jr nz,tilemap_draw_x


pop bc
pop hl
;; H = x coordinate
;; L = y coordinate

;; update Y coordinate for next tilemap line
ld a,l
add a,tile_height
ld l,a
djnz tilemap_draw_y
ret

;; IN:
;; A = tile index
;; OUT:
;; DE = tile pixel data
get_tile_ptr:
push hl
ld l,a
ld h,0
add hl,hl
ld de,tile_ptr_table
add hl,de
ld e,(hl)
inc hl
ld d,(hl)
pop hl
ret



;; From our x,y coordinates generate
;; a screen address for top-left of sprite to be drawn at.

;; IN:
;; H = x byte coord
;; L = y line
;; OUT:
;; HL = screen address
get_scr_addr:
push bc
push de
ld c,h
ld h,0
add hl,hl
ld de,scr_addr_table
add hl,de
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
ld b,0
add hl,bc
pop de
pop bc
ret

;; generate table of screen addresses
;; one address per scanline. The address
;; is for the first byte of each line.
make_scr_table:
ld ix,scr_addr_table		;; address to store table
ld hl,&c000					;; start address of first scanline
ld b,200					;; number of scanlines on screen
mst1:
ld (ix+0),l
ld (ix+1),h
inc ix
inc ix
push bc
call scr_next_line
pop bc
djnz mst1
ret

;; generate table/array of tile pixel ptrs
;; so that we can access tile pixels quickly based
;; on tile index 
make_tile_ptr_table:
ld ix,tile_ptr_table
ld hl,tile_gfx				;; start of tile pixels (tile 0)
ld de,tile_data_size		;; size of each tile (all tiles same size in bytes)
ld b,num_tiles				;; number of tiles to generate information for
mtpt1:
ld (ix+0),l			
ld (ix+1),h
inc ix
inc ix
add hl,de
djnz mtpt1
ret

;; IN:
;; DE = address of tile pixel data
;; HL = screen address
tile_draw:
ld b,tile_height
ld c,tile_width_bytes

tile_draw_height:
push bc
push hl

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

pop hl
call scr_next_line
pop bc
djnz tile_draw_height
ret

;; table/array for screen addresses for each scan line
scr_addr_table:
defs 200*2

;; the inks for the pens
pens:
defb &00
defb &01
defb &01
defb &01
defb &02
defb &02
defb &06
defb &06
defb &0c
defb &0a
defb &0a
defb &0e
defb &18
defb &18
defb &1a
defb &1a
;; current screen id
tilemap_cur:
defb 0

;; indicates screen id has changed and screen needs redraw
tilemap_changed:
defb 0

;; directions for each screen, gives id of
;; screen to move to


;; map arranged like this
;;
;;       [3]
;;        |
;; [1] - [0] - [2]
;;        |
;;       [4]

tilemap_dirs:
;; tilemap 0
defb 1 					;; left
defb 2					;; right
defb 3					;; up
defb 4					;; down
;; tilemap 1
defb tilemap_dir_invalid ;; left
defb 0					 ;; right
defb tilemap_dir_invalid ;; up
defb tilemap_dir_invalid ;; down
;; tilemap 2
defb 0 					;; left
defb tilemap_dir_invalid ;; right
defb tilemap_dir_invalid ;; up
defb tilemap_dir_invalid ;; down
;; tilemap 3
defb tilemap_dir_invalid ;; left
defb tilemap_dir_invalid ;; right
defb tilemap_dir_invalid ;; up
defb 0					;; down
;; tilemap 4
defb tilemap_dir_invalid ;; left
defb tilemap_dir_invalid ;; right
defb 0					;; up
defb tilemap_dir_invalid ;; down

;; pointer to screen data
tilemap_ptrs:
defw tilemap0
defw tilemap1
defw tilemap2
defw tilemap3
defw tilemap4
end_tilemap_ptrs:

tile_ptr_table:
defs num_tiles*2

tilemap0:
;;    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
defb  0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0  ;; 0
defb  0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0  ;; 1
defb  0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0  ;; 2
defb  0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0  ;; 3
defb  0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0  ;; 4
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 5
defb  0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0  ;; 6
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 7
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 8
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0  ;; 9
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 10
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 11


tilemap1:
;;    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
defb  0, 2, 1, 1, 2, 0, 1, 1, 1, 2, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0  ;; 0
defb  0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0  ;; 1
defb  0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0  ;; 2
defb  0, 1, 0, 0, 0, 0, 1, 1, 1, 2, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0  ;; 3
defb  0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0  ;; 4
defb  0, 2, 1, 1, 2, 0, 1, 0, 0, 0, 0, 2, 1, 1, 2, 0, 0, 0, 0, 0  ;; 5
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 6
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 7
defb  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0  ;; 8
defb  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0  ;; 9
defb  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0  ;; 10
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 11

tilemap2:
;;    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 0
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 1
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 2
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 3
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 4
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 5
defb  0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 6
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 7
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 8
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 9
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 10
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 11

tilemap3:
;;    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
defb  1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1  ;; 0
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 1
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 2
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 3
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 4
defb  0, 3, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 3, 0  ;; 5
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 6
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 7
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 8
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 9
defb  0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0  ;; 10
defb  1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1  ;; 11

tilemap4:
;;    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
defb  0, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 3, 3, 3, 0, 3, 3, 3, 0, 0  ;; 0
defb  0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0  ;; 1
defb  0, 0, 0, 3, 0, 0, 0, 3, 3, 0, 0, 3, 3, 3, 0, 0, 3, 0, 0, 0  ;; 2
defb  0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 0, 0  ;; 3
defb  0, 0, 0, 3, 0, 0, 0, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, 0, 0, 0  ;; 4
defb  0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 5
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 6
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 7
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 8
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 9
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 10
defb  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  ;; 11

tile_gfx:
defb &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 
defb &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 
defb &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 
defb &00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00,&00 
defb &8c,&0c,&0c,&4c,&9a,&30,&30,&a4,&9a,&30,&30,&a4,&9a,&30,&30,&a4 
defb &da,&4b,&87,&a4,&9a,&f0,&f0,&24,&da,&4b,&87,&a4,&9a,&f0,&f0,&24 
defb &da,&4b,&87,&a4,&9a,&f0,&f0,&24,&da,&4b,&87,&a4,&9a,&f0,&f0,&24 
defb &da,&4b,&87,&a4,&9a,&30,&30,&a4,&9a,&30,&30,&a4,&cf,&cf,&cf,&ca 
defb &03,&03,&03,&57,&53,&f3,&f3,&f7,&53,&33,&33,&77,&53,&33,&33,&77 
defb &53,&b3,&73,&77,&53,&51,&a2,&f7,&53,&b3,&73,&77,&53,&33,&33,&77 
defb &53,&b3,&73,&77,&53,&51,&a2,&f7,&53,&a2,&51,&77,&53,&73,&b3,&77 
defb &53,&33,&33,&77,&53,&33,&33,&77,&53,&33,&33,&77,&bf,&3f,&3f,&7f 
defb &3c,&3c,&a8,&bc,&3c,&3c,&a8,&bc,&fc,&fc,&a8,&fc,&00,&00,&00,&00 
defb &fc,&a8,&fc,&fc,&3c,&a8,&bc,&3c,&fc,&a8,&fc,&fc,&00,&00,&00,&00 
defb &54,&fc,&fc,&54,&54,&3c,&7c,&54,&54,&fc,&fc,&54,&00,&00,&00,&00 
defb &fc,&a8,&fc,&fc,&3c,&a8,&bc,&3c,&fc,&a8,&fc,&fc,&00,&00,&00,&00
;; tiles