- INCLUDE "hardware.inc"
- INCLUDE "util.inc"
-
- SECTION "Map Data", WRAM0
-
- PAGEX:: DB ; X coordinate to enqueue map column at
- PAGEY:: DB ; Y coordinate to enqueue map row at
-
- LAST_SCX:: DB ; Value of SCX last frame
- LAST_SCY:: DB ; Value of SCY last frame
-
- PENDING_ROW_PTR:: DW ; Where to write pending row data (0 = no write)
- PENDING_ROW_DATA:: DS SCRN_VX_B ; Row to be written
-
- PENDING_COL_PTR:: DW ; Where to write pending column data (0 = no write)
- PENDING_COL_DATA: DS SCRN_VY_B ; Column to be written
-
- CURRENT_DATA_START::
- CURRENT_TILE_PTR:: DW ; Location of tile data
- CURRENT_TILE_SIZE:: DB ; Length of tile data (num_tiles * 8)
- CURRENT_MAP_PTR:: DW ; Location of map data
- CURRENT_MAP_COLLISION:: DW ; Location of map collision data
- CURRENT_MAP_WIDTH:: DB ; Width of map in tiles
- CURRENT_MAP_HEIGHT:: DB ; Height of map in tiles
- CURRENT_SPAWN_X:: DB ; X coordinate to spawn player at
- CURRENT_SPAWN_Y:: DB ; Y coordinate to spawn player at
- CURRENT_CAMERA_X:: DB ; X coordinate of camera (top left of viewport)
- CURRENT_CAMERA_Y:: DB ; Y coordinate of camera (top left of viewport)
- CURRENT_DATA_END::
-
- SECTION "Map Code", ROM0
-
- DEF INIT_SCX EQUS "((SCRN_VX - SCRN_X) / 2)"
- DEF INIT_SCY EQUS "((SCRN_VY - SCRN_Y) / 2)"
-
- ; Loads a map
- ; @param hl Pointer to map metadata
- Map_Load::
- ; Initialize scroll state
- ld a, INIT_SCX
- ld [rSCX], a
- ld [LAST_SCX], a
-
- ld a, INIT_SCY
- ld [rSCY], a
- ld [LAST_SCY], a
-
- ld a, 8
- ld [PAGEX], a
- ld [PAGEY], a
-
- ; Store metadata
- ld bc, CURRENT_DATA_START
- ld d, CURRENT_DATA_END - CURRENT_DATA_START
- call memcpy
-
- ; Write tiles to VRAM
- ld hl, CURRENT_TILE_PTR
- ld a, [hl+]
- ld c, a
- ld a, [hl+]
- ld b, a
-
- ld a, [CURRENT_TILE_SIZE]
- ld d, a
-
- ld hl, _VRAM
- MEMCPY hl, bc, d
-
- ; Write initial map data
- ld hl, _SCRN0
-
- ld a, [CURRENT_CAMERA_X]
- sub INIT_SCX / 8
- ld b, a
-
- ld a, [CURRENT_CAMERA_Y]
- sub INIT_SCY / 8
- ld c, a
-
- ld d, SCRN_VY_B
- .write_rows:
- call write_map_row
- inc c
-
- ld a, SCRN_VX_B
- ADD16 hl
-
- dec d
- jr nz, .write_rows
-
- ret
-
- ; Update map state based on SCX/SCY
- Map_Scroll::
- ; If SCY = PAGEY, write map row
- ld hl, PAGEY
- ld a, [rSCY]
- cp [hl]
- jr nz, .scroll_down_check
-
- ; B = CAMERA_X - SCX/8
- ld a, [rSCX]
- srl a
- srl a
- srl a
- ld b, a
- ld a, [CURRENT_CAMERA_X]
- sub b
- ld b, a
-
- ; C = CAMERA_Y - 2
- ld a, [CURRENT_CAMERA_Y]
- sub 2
- ld c, a
-
- ; HL = _SCRN0 + 32 * (SCY/8 - 2)
- ld a, [rSCY]
- sub 16
- srl a
- srl a
- srl a
- call get_row_ptr
-
- call enqueue_row_write
-
- ld a, [PAGEY]
- sub 8
- ld [PAGEY], a
-
- jp .done
-
- .scroll_down_check:
- ; Check SCY + (SCRN_Y + 16) = PAGEY
- ld a, [PAGEY]
- sub SCRN_Y + 16
- ld hl, rSCY
- cp [hl]
- jr nz, .scroll_left_check
-
- ld a, [PAGEY]
- srl a
- srl a
- srl a
- dec a
- call get_row_ptr
-
- ; B = CAMERA_X - SCX/8
- ld a, [rSCX]
- ld b, a
- ld a, [CURRENT_CAMERA_X]
- srl b
- srl b
- srl b
- sub b
- ld b, a
-
- ; C = CAMERA_Y + 18 + 1
- ld a, [CURRENT_CAMERA_Y]
- add SCRN_Y_B + 1
- ld c, a
-
- call enqueue_row_write
-
- ld a, [PAGEY]
- add 8
- ld [PAGEY], a
-
- jr .done
-
- .scroll_left_check:
- ; If SCX = PAGEX, write map col
- ld a, [PAGEX]
- ld hl, rSCX
- cp [hl]
- jr nz, .scroll_right_check
-
- ; HL = VRAM + PAGEX/8
- ld hl, _SCRN0
- ld a, [PAGEX]
- srl a
- srl a
- srl a
- dec a
- ADD16 hl
-
- ; B = CAMERA_X - 1
- ld a, [CURRENT_CAMERA_X]
- dec a
- ld b, a
-
- ; C = CAMERA_Y - SCY/8
- ld a, [rSCY]
- ld c, a
- ld a, [CURRENT_CAMERA_Y]
- srl c
- srl c
- srl c
- sub c
- ld c, a
-
- call enqueue_col_write
-
- ld a, [PAGEX]
- sub 8
- ld [PAGEX], a
-
- .scroll_right_check:
- ; If SCX = SCRN_X - PAGEX, write map col
- ; Check SCX + (SCRN_X + 16) = PAGEX
- ld a, [PAGEX]
- sub SCRN_X + 16
- ld hl, rSCX
- cp [hl]
- jr nz, .done
-
- ; HL = VRAM + PAGEX/8
- ld hl, _SCRN0
- ld a, [PAGEX]
- srl a
- srl a
- srl a
- dec a
- ADD16 hl
-
- ; B = CAMERA_X + 20
- ld a, [CURRENT_CAMERA_X]
- add SCRN_X_B + 1
- ld b, a
-
- ; C = CAMERA_Y - SCY/8
- ld a, [rSCY]
- ld c, a
- ld a, [CURRENT_CAMERA_Y]
- srl c
- srl c
- srl c
- sub c
- ld c, a
-
- call enqueue_col_write
-
- ld a, [PAGEX]
- add 8
- ld [PAGEX], a
-
- .done:
- ld hl, LAST_SCY
- ld a, [rSCY]
- ld [hl], a
-
- ret
-
- Map_Update::
- ; Skip row update if PENDING_ROW_PTR is 0
- ld a, [PENDING_ROW_PTR]
- ld c, a
- ld a, [PENDING_ROW_PTR + 1]
- ld b, a
- or c
- jr z, .update_col
-
- ld hl, PENDING_ROW_DATA
- ld d, SCRN_VX_B
- MEMCPY bc, hl, d
-
- .update_col:
- ; Skip column update if PENDING_COL_PTR is 0
- ld a, [PENDING_COL_PTR]
- ld c, a
- ld a, [PENDING_COL_PTR + 1]
- ld b, a
- or c
- ret z
-
- ld d, SCRN_VY_B
- ld hl, PENDING_COL_DATA
- .update_col_loop:
- ld a, [hl+]
- ld [bc], a
-
- ld a, SCRN_VX_B
- ADD16 bc
-
- dec d
- jr nz, .update_col_loop
-
- ret
-
- ; Computes the offset into map RAM
- ; @param a The map RAM y-coordinate
- ; @return hl Pointer into map RAM
- get_row_ptr:
- push de
- ld d, 0
- ld e, a
- REPT 5
- SLA16 de
- ENDR
- ld hl, _SCRN0
- add hl, de
- pop de
- ret
-
- ; Write a row of map data into row buffer
- ; @param b Map X coordinate (signed)
- ; @param c Map Y coordinate (signed)
- ; @param hl Where to write the row in map VRAM
- ; @destroy All registers
- enqueue_row_write:
- ; PENDING_ROW_PTR = HL
- ld a, l
- ld [PENDING_ROW_PTR], a
- ld a, h
- ld [PENDING_ROW_PTR + 1], a
-
- ; If Y < 0, write 0s
- bit 7, c
- jr nz, .zero_row
-
- ; If Y >= MAP_HEIGHT, write 0s
- ld a, [CURRENT_MAP_HEIGHT]
- dec a
- cp c
- jr c, .zero_row
-
- ; HL = CURRENT_MAP_PTR
- ld a, [CURRENT_MAP_PTR]
- ld l, a
- ld a, [CURRENT_MAP_PTR + 1]
- ld h, a
-
- ; HL = CURRENT_MAP_PTR + Y * MAP_WIDTH
- ld d, 0
- ld a, [CURRENT_MAP_WIDTH]
- ld e, a
- ld a, c
-
- .get_map_row_ptr:
- or a
- jr z, .copy_map_row
- add hl, de
- dec a
- jr .get_map_row_ptr
-
- .copy_map_row:
- ; C = BYTES_LEFT
- ld c, SCRN_VX_B
- ld de, PENDING_ROW_DATA
-
- ; Note: Can skip checking BYTES_LEFT > 0 in this loop. If there were
- ; SCRN_VX_B zeros to write, then Y would be 1 greater and we would have
- ; jumped into .zero_row before reaching this code
- .pad_left:
- ; Check X < 0
- bit 7, b
- jr z, .copy_middle
-
- ; *ROW++ = 0
- ld [de], a
- inc de
-
- ; X++, BYTES_LEFT--
- inc b
- dec c
- jr .pad_left
-
- .copy_middle:
- ; Check X < MAP_WIDTH
- ld a, [CURRENT_MAP_WIDTH]
- dec a
- cp b
- jr c, .pad_right
-
- ; Check BYTES_LEFT > 0
- ld a, c
- or a
- ret z
-
- ; *ROW++ = *MAP++
- ld a, [hl+]
- ld [de], a
- inc de
-
- ; X++, BYTES_LEFT--
- inc b
- dec c
- jr .copy_middle
-
- .pad_right:
- ; Check BYTES_LEFT > 0
- ld a, c
- or a
- ret z
-
- ; *ROW++ = 0
- xor a
- ld [de], a
- inc de
-
- ; X++, BYTES_LEFT--
- inc b
- dec c
- jr .pad_right
-
- .zero_row:
- ld hl, PENDING_ROW_DATA
- xor a
- ld c, SCRN_VX_B
- .zero_row_loop:
- ld [hl+], a
- dec c
- jr nz, .zero_row_loop
- ret
-
- ; NOTE: This works by enqueueing a row to write, and then immediately flushing
- ; the queue. The subroutine could be sped up by writing to map RAM directly, but
- ; this is simpler to write and saves on code size.
- ;
- ; Write a row of map data into map RAM.
- ; @param b Map X coordinate (signed)
- ; @param c Map Y coordinate (signed)
- ; @param hl Pointer into map VRAM
- write_map_row:
- push bc
- push de
- push hl
-
- call enqueue_row_write
-
- ld a, [PENDING_ROW_PTR]
- ld c, a
- ld a, [PENDING_ROW_PTR + 1]
- ld b, a
-
- ld hl, PENDING_ROW_DATA
- ld d, SCRN_VX_B
- MEMCPY bc, hl, d
-
- pop hl
- pop de
- pop bc
-
- ret
-
- ; Write a column of map data into column buffer
- ; @param b Map X coordinate (signed)
- ; @param c Map Y coordinate (signed)
- ; @param hl Where to write the column in map VRAM
- ; @destroy All registers
- enqueue_col_write:
- ; PENDING_COL_PTR = HL
- ld a, l
- ld [PENDING_COL_PTR], a
- ld a, h
- ld [PENDING_COL_PTR + 1], a
-
- ; If X < 0, write 0s
- bit 7, b
- jr nz, .zero_row
-
- ; If X >= MAP_WIDTH, write 0s
- ld a, [CURRENT_MAP_WIDTH]
- dec a
- cp c
- jr c, .zero_row
-
- ; HL = CURRENT_MAP_PTR
- ld a, [CURRENT_MAP_PTR]
- ld l, a
- ld a, [CURRENT_MAP_PTR + 1]
- ld h, a
-
- ; HL = CURRENT_MAP_PTR + Y * MAP_WIDTH + X
- ld d, 0
- ld a, [CURRENT_MAP_WIDTH]
- ld e, a
- ld a, c
-
- .get_map_col_ptr:
- or a
- jr z, .copy_map_column
- add hl, de
- dec a
- jr .get_map_col_ptr
-
- .copy_map_column:
- ld a, b
- ADD16 hl
-
- ; B = BYTES_LEFT
- ld b, SCRN_VY_B
- ld de, PENDING_COL_DATA
-
- ; Note: Can skip checking BYTES_LEFT > 0 in this loop. If there were
- ; SCRN_VY_B zeros to write, then X would be 1 greater and we would have
- ; jumped into .zero_row before reaching this code
- .pad_left:
- ; Check Y < 0
- bit 7, c
- jr z, .copy_middle
-
- ; *ROW++ = 0
- xor a
- ld [de], a
- inc de
-
- ; Y++, BYTES_LEFT--
- inc c
- dec b
- jr .pad_left
-
- .copy_middle:
- ; Check Y < MAP_HEIGHT
- ld a, [CURRENT_MAP_HEIGHT]
- dec a
- cp c
- jr c, .pad_right
-
- ; Check BYTES_LEFT > 0
- ld a, b
- or a
- ret z
-
- ; *ROW++ = *MAP
- ld a, [hl]
- ld [de], a
- inc de
-
- ; MAP += MAP_WIDTH
- ld a, [CURRENT_MAP_WIDTH]
- ADD16 hl
-
- ; Y++, BYTES_LEFT--
- inc c
- dec b
- jr .copy_middle
-
- .pad_right:
- ; Check BYTES_LEFT > 0
- ld a, b
- or a
- ret z
-
- ; *ROW++ = 0
- xor a
- ld [de], a
- inc de
-
- ; X++, BYTES_LEFT--
- inc c
- dec b
- jr .pad_right
-
- .zero_row:
- ld hl, PENDING_COL_DATA
- xor a
- ld c, SCRN_VY_B
- .zero_row_loop:
- ld [hl+], a
- dec c
- jr nz, .zero_row_loop
- ret
-
- ; Compute the distance (modulo 32) between two values
- ; @param b Value 1
- ; @param c Value 2
- mmd2:
- push bc
-
- ld a, b
- sub c
-
- cpl
- ld b, a
- cpl
-
- bit 7, b
- jr z, .0
-
- ld b, a
-
- .0:
- ld a, 32
- sub b
-
- cp b
- jr nc, .1
-
- pop bc
- ret
-
- .1:
- ld a, b
-
- pop bc
- ret
|