I am in need of help creating a tile display routine for levels. I intend on LDIR'ing the level data into the workspace. It is laid out in 16x12 grid, with 0 a blank space, 01 a wall, 02 coin, thats all for now but there are more.
So, how do I go about displaying the levels?
I've got a sprite routine that LDIR's the sprite to its position as I do not need transparency for the tile display routine, but it uses up all the registers. How do I make a tile routine with it, or will I need another sprite routine?
I cant get my head around the example on the wiki, and thats in mode 0 anyway. I am working in mode 1, with a 16x16 tile (256x256 pixel) screen (the level area is 16x12 with the bottom 4 rows for the HUD).
Quote from: EgoTrip on 18:51, 10 February 13
[size=78%]I cant get my head around the example on the wiki, and thats in mode 0 anyway. I am working in mode 1, with a 16x16 tile (256x256 pixel) screen (the level area is 16x12 with the bottom 4 rows for the HUD).[/size]
If you're talking about Arnoldemu's routine (http://www.cpctech.org.uk/source/tilemap.html) then it would be a good idea to try and understand it because if you start using tilemaps then eventually you're also going to want to use them for collision detection etc and hence need a nice way of accessing them (such as the detailed 2d array/matrix approach in this example).
However, if you're just wanting to draw a screen from a tilemap, something quick and dirty like this might help you and be easier to understand:
org &4000
tile_start equ &6000
tile_size equ &40
tile_height equ 16 ; lines
tile_width equ 4 ; bytes
scr_set_mode equ &BC0E
scr_set_border equ &BC38
km_wait_key equ &BB18
ld a,1
call scr_set_mode ; change to MODE 1
call sub_init_crtc ; setup CRTC for 32x32 screen
ld bc,0
call scr_set_border ; BORDER 0
call sub_draw_tilemap ; draw the tilemap
call km_wait_key ; wait for keypress
ret ; and RETurn
sub_init_crtc: ld bc,&bc01 ; CRTC register 1
out (c),c
ld bc,&bd00+32 ; set width in characters
out (c),c
ld bc,&bc02 ; CRTC register 2
out (c),c
ld bc,&bd00+42 ; set horizontal position
out (c),c
ld bc,&bc06 ; CRTC register 6
out (c),c
ld bc,&bd00+32 ; set height in characters
out (c),c
ld bc,&bc07 ; CRTC regsiter 7
out (c),c
ld bc,&bd00+34 ; set vertical position
out (c),c
ret
sub_draw_tilemap: ld b,13 ; number of rows
main_loop: push bc ; preserve counter
ld b,16 ; number of tiles in a row
call row_loop
ld hl,(scr_addr_pointer) ; screen address two char lines down is &80
ld de,&40 ; we have already added &40 by printing a tile horizontal line
add hl,de ; so we only need to add &40 to get 2 char lines down
ld (scr_addr_pointer),hl
pop bc
djnz main_loop
ret
row_loop: push bc ; preserve counter
ld hl,(tilemap_01_pointer) ; load HL with tilemap
ld e,(hl) ; load E with tile number from tilemap
ld h,tile_size ; size of tile sprite
ld d,0 ; reset these ...
ld l,0 ; ... for multiply routine
call sub_multiply ; multiply together
ld de,tile_start ; to get the memory address of the tile we want
add hl,de ; HL equals tile memory address
ld de,(scr_addr_pointer)
ld b,tile_height
ld c,tile_width
call sub_sprite ; print the tile to the screen
ld hl,(tilemap_01_pointer) ; increase pointer to next tile in table
inc hl
ld (tilemap_01_pointer),hl ; and store it
ld hl,(scr_addr_pointer) ; increase pointer to next screen address for tile
inc hl
inc hl
inc hl
inc hl
ld (scr_addr_pointer),hl ; and store it
pop bc ; restore counter
djnz row_loop ; loop back if not all done
ret
sub_multiply: sla h ; optimised 1st iteration
jr nc,$+3
ld l,e
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
add hl,hl ; unroll 7 times
jr nc,$+3 ; ...
add hl,de ; ...
ret
sub_sprite: push de ; preserve screen address
push bc ; and the dimensions
ld b,0 ; BC now equals the width in bytes
ldir ; copy the bytes!
pop bc ; get back the dimensions
pop de ; and then the screen address
ex de,hl ; to move DE onto the next screen line,
call nline ; we use the routine to move HL onto the
ex de,hl ; the next line, but swap DE and HL either side
djnz sub_sprite ; if not all lines done, loop back
ret
nline: ld a,8
add a,h ; add 8 to H (the high byte)
ld h,a
ret nc ; return if no overflow
push de ; otherwise preserve DE
ld de,&C040
add hl,de ; add &C000+&50 to HL
pop de ; and get DE back again!
ret
scr_addr_pointer: defw &C000 ; sub_draw_tilemap
tilemap_01_pointer: defw tilemap_01 ; sub_draw_tilemap
tilemap_01: defb 06,08,08,08,06,01,06,08,06,00,00,00,00,00,00,00 ; sub_draw_tilemap
defb 07,00,00,00,00,03,00,00,06,08,08,08,08,08,08,06
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 06,00,00,00,00,03,00,00,00,00,00,00,00,00,00,06
defb 01,02,02,02,02,04,02,05,02,02,02,02,02,02,02,01
defb 06,00,00,00,00,00,00,03,00,00,00,00,00,00,00,06
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 06,08,08,08,08,08,06,01,06,08,08,08,08,08,08,06
org &6000 ; tile sprite data
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#f0,#f0,#f0,#f0
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #f0,#f0,#f0,#f0,#a5,#a5,#a5,#b4
db #c3,#0f,#0f,#1e,#87,#af,#af,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#5f,#5f,#1e,#87,#0f,#0f,#3c
db #d2,#5a,#5a,#5a,#f0,#f0,#f0,#f0
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #00,#00,#00,#00,#00,#00,#00,#00
db #f0,#f0,#f0,#f0,#a5,#a5,#a5,#a5
db #0f,#0f,#0f,#0f,#af,#af,#af,#af
db #ff,#ff,#ff,#ff,#ff,#ff,#ff,#ff
db #ff,#ff,#ff,#ff,#ff,#ff,#ff,#ff
db #5f,#5f,#5f,#5f,#0f,#0f,#0f,#0f
db #5a,#5a,#5a,#5a,#f0,#f0,#f0,#f0
db #00,#00,#00,#00,#00,#00,#00,#00
It basically takes a number from the tilemap (01,02,03 etc), then mutliplies it by the size of each tile sprite (&40 in this case) and this gives us the memory address of the tile (which start at &6000 onwards). Once we've got the address of the tile, we simply print it to the screen like any other sprite and move onto the next tile in the tilemap until we're finished.
Or...
If you're using a 32 x 32 character screen and the tiles are 16 x 16, everything can be nicely page-aligned because you're not going to go past &FF (i.e. 256, which is 16 x 16).
Something like this:
org &4000
tile_height equ 16 ; lines
tile_width equ 4 ; bytes
scr_set_mode equ &BC0E
scr_set_border equ &BC38
km_wait_key equ &BB18
ld a,1
call scr_set_mode ; MODE 1
call s_init_crtc ; setup CRTC for 32x32 screen
ld bc,0
call scr_set_border ; BORDER 0
call s_draw_tile_map ; draw the tilemap
call km_wait_key ; wait for keypress
ret ; and RETurn
s_init_crtc: ld bc,&bc01 ; CRTC register 1
out (c),c
ld bc,&bd00+32 ; set width in characters
out (c),c
ld bc,&bc02 ; CRTC register 2
out (c),c
ld bc,&bd00+42 ; set horizontal position
out (c),c
ld bc,&bc06 ; CRTC register 6
out (c),c
ld bc,&bd00+32 ; set height in characters
out (c),c
ld bc,&bc07 ; CRTC regsiter 7
out (c),c
ld bc,&bd00+34 ; set vertical position
out (c),c
ret
s_draw_tile_map: ld h,tile_map_01/256 ; load HL with start address of page-aligned...
ld l,&00 ; ...tile map table (16x16 tiles so 256 bytes in size)
ld b,16 ; number of rows
s_draw_tile_map_loop: push bc ; preserve row counter
s_draw_row: ld b,16 ; number of tiles in a row
s_draw_row_loop: push bc ; preserve tile counter
push hl ; preserve table position
ld a,(hl) ; load A with tile number from table
ld h,0 ; reset H
ld l,a ; load L with tile number
add hl,hl ; multiply it so we get the memory...
add hl,hl ; ...address for the tile sprite...
add hl,hl ; ...as they are &40 each in size
add hl,hl ; e.g. tile 6 is &40 x 6 = &0180
add hl,hl
add hl,hl
ld a,tile_sprites/256 ; load A with the start of the tile sprites (&4200)
add a,h ; and add it back to H (e.g. &0180 + &4200 = &4380)
ld h,a ; HL now holds tile sprite location we want to print
ld de,(scr_addr_pointer) ; DE is the screen address position
ld b,tile_height ; BC is the height and width
ld c,tile_width
call s_sprite ; print the tile to the screen
ld hl,(scr_addr_pointer) ; increase pointer to next screen address for tile
inc l ; which is 4 bytes away
inc l
inc l
inc l
ld (scr_addr_pointer),hl ; and store it
pop hl ; get table position back into HL
inc l ; and move on one position in table
pop bc ; restore tile counter
djnz s_draw_row_loop ; loop back to do another tile if row not finished
push hl ; preserve table position
ld hl,(scr_addr_pointer) ; screen address two char lines down is &80
ld de,&40 ; we have already added &40 by printing a tile horizontal line
add hl,de ; so we only need to add &40 to get 2 char lines down
ld (scr_addr_pointer),hl
pop hl ; get table position back into HL
pop bc ; restore row counter
djnz s_draw_tile_map_loop ; loop back to do another row if not finished
ret
s_sprite: push de ; preserve screen address
push bc ; and the dimensions
ld b,0 ; BC now equals the width in bytes
ldir ; copy the bytes!
pop bc ; get back the dimensions
pop de ; and then the screen address
ex de,hl ; to move DE onto the next screen line,
call s_sprite_nline ; we use the routine to move HL onto the
ex de,hl ; the next line, but swap DE and HL either side
djnz s_sprite ; if not all lines done, loop back
ret
s_sprite_nline: ld a,8
add a,h ; add 8 to H (the high byte)
ld h,a
ret nc ; return if no overflow
push de ; otherwise preserve DE
ld de,&C040
add hl,de ; add &C000+&40 to HL
pop de ; and get DE back again!
ret
scr_addr_pointer: defw &C000
align 256
tile_map_01: defb 06,08,08,08,06,01,06,08,06,00,00,00,00,00,00,00
defb 07,00,00,00,00,03,00,00,06,08,08,08,08,08,08,06
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,03,00,00,00,00,00,00,00,00,00,07
defb 06,00,00,00,00,03,00,00,00,00,00,00,00,00,00,06
defb 01,02,02,02,02,04,02,05,02,02,02,02,02,02,02,01
defb 06,00,00,00,00,00,00,03,00,00,00,00,00,00,00,06
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 07,00,00,00,00,00,00,03,00,00,00,00,00,00,00,07
defb 06,08,08,08,08,08,06,01,06,08,08,08,08,08,08,06
align 256
tile_sprites: db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #00,#00,#00,#00,#00,#00,#00,#00
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#f0,#f0,#f0,#f0
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #d7,#5f,#5f,#5e,#a7,#af,#af,#be
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#f0,#f0,#f0,#f0
db #f0,#f0,#f0,#f0,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #5f,#5f,#5f,#5f,#af,#af,#af,#af
db #f0,#f0,#f0,#f0,#a5,#a5,#a5,#b4
db #c3,#0f,#0f,#1e,#87,#af,#af,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#ff,#ff,#1e,#87,#ff,#ff,#bc
db #d3,#5f,#5f,#1e,#87,#0f,#0f,#3c
db #d2,#5a,#5a,#5a,#f0,#f0,#f0,#f0
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #21,#3f,#ef,#c0,#30,#7f,#cf,#48
db #00,#00,#00,#00,#00,#00,#00,#00
db #f0,#f0,#f0,#f0,#a5,#a5,#a5,#a5
db #0f,#0f,#0f,#0f,#af,#af,#af,#af
db #ff,#ff,#ff,#ff,#ff,#ff,#ff,#ff
db #ff,#ff,#ff,#ff,#ff,#ff,#ff,#ff
db #5f,#5f,#5f,#5f,#0f,#0f,#0f,#0f
db #5a,#5a,#5a,#5a,#f0,#f0,#f0,#f0
db #00,#00,#00,#00,#00,#00,#00,#00
The code is completely unoptimised as it's proof of concept, but it's still less sloppy than the first one I posted. :D
Thanks for the help, will reply if I get stuck, which I probably will.