- INCLUDE "game.inc"
- INCLUDE "hardware.inc"
- INCLUDE "util.inc"
-
- SECTION "Map Data", WRAM0
-
- DEF ROW_BUFFER_SIZE EQUS "SCRN_X_B + 4"
- DEF COL_BUFFER_SIZE EQUS "SCRN_Y_B + 4"
-
- PENDING_ROW_PTR:: DW ; Where to write pending row data (0 = no write)
- PENDING_ROW_DATA:: DS ROW_BUFFER_SIZE ; Row to be written
-
- PENDING_COL_PTR:: DW ; Where to write pending column data (0 = no write)
- PENDING_COL_DATA: DS COL_BUFFER_SIZE ; 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 a, INIT_SCY
- ld [rSCY], a
-
- ; Store metadata
- ld bc, CURRENT_DATA_START
- ld d, CURRENT_DATA_END - CURRENT_DATA_START
- call memcpy
-
- ; Move player to spawn point
- ld a, [CURRENT_CAMERA_X]
- ld b, a
- ld a, [CURRENT_SPAWN_X]
- sub b
- sla a
- sla a
- sla a
- ld [PLAYER_X], a
-
- ld a, [CURRENT_CAMERA_Y]
- ld b, a
- ld a, [CURRENT_SPAWN_Y]
- sub b
- sla a
- sla a
- sla a
- ld [PLAYER_Y], a
-
- ; 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 + TILE_INDEX_BACKGROUND * TILE_SIZE
- 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
-
-
-
- ; Scroll the map upwards
- ; @param d The amount to scroll by (0 < d < 8)
- ;
- ; SCY_T = SCY % 8;
- ; if CAMERA_Y = 0 {
- ; if SCY_T != 0 {
- ; SCY -= MIN(D, SCY_T);
- ; }
- ; return;
- ; }
- ; SCY -= D;
- ; if SCY_T - D < 0 {
- ; CAMERA_Y -= 1;
- ; Map_ScrollRow(CAMERA_X - 2, CAMERA_Y - 2);
- ; }
- Map_ScrollUp::
- ld a, [rSCY]
- and %111
-
- add sp, -1
- ld hl, sp + 0
- ld [hl], a
-
- ld a, [CURRENT_CAMERA_Y]
- or a
- jr nz, .scroll
-
- ld hl, sp + 0
- ld a, [hl]
- or a
- jr z, .done
-
- ld b, d
- call min
- ld d, a
- ld a, [rSCY]
- sub d
- ld [rSCY], a
- jr .done
-
- .scroll:
- ld a, [rSCY]
- sub d
- ld [rSCY], a
-
- ; Check SCY_T - D < 0
- ld hl, sp + 0
- ld a, [hl]
- sub d
- jr nc, .done
-
- ; CAMERA_Y -= 1
- ld a, [CURRENT_CAMERA_Y]
- dec a
- ld [CURRENT_CAMERA_Y], a
-
- ; B = CAMERA_X - 2
- ld a, [CURRENT_CAMERA_X]
- sub 2
- ld b, a
-
- ; C = CAMERA_Y - 2
- ld a, [CURRENT_CAMERA_Y]
- sub 2
- ld c, a
-
- ; MAP Y = SCY/8 - 2
- ld a, [rSCY]
- srln a, 3
- sub 2
-
- add sp, 1
- jr Map_ScrollRow
-
- .done:
- add sp, 1
- ret
-
-
-
- ; Map_ScrollDown(D)
- ;
- ; SCY_T = SCY % 8;
- ; if CAMERA_Y + 18 = MAP_HEIGHT {
- ; if SCY_T != 0 {
- ; SCY += MIN(D, 8 - SCY_T);
- ; }
- ; return;
- ; }
- ; SCY += D;
- ; if SCY_T + D >= 8 {
- ; CAMERA_Y += 1;
- ; Map_ScrollRow(CAMERA_X - 2, CAMERA_Y + 18 + 1);
- ; }
- Map_ScrollDown::
- ld a, [rSCY]
- and %111
-
- add sp, -1
- ld hl, sp + 0
- ld [hl], a
-
- ld a, [CURRENT_CAMERA_Y]
- add SCRN_Y_B
- ld hl, CURRENT_MAP_HEIGHT
- cp [hl]
- jr nz, .scroll
-
- ld hl, sp + 0
- ld a, [hl]
- or a
- jr z, .done
-
- sub 8
- cpl
-
- ld b, d
- call min
- ld d, a
- ld a, [rSCY]
- add d
- ld [rSCY], a
- jr .done
-
- .scroll:
- ld a, [rSCY]
- add d
- ld [rSCY], a
-
- ; Check SCY_T + D < 8
- ld hl, sp + 0
- ld a, [hl]
- add d
- cp 8
- jr c, .done
-
- ; CAMERA_Y += 1
- ld a, [CURRENT_CAMERA_Y]
- inc a
- ld [CURRENT_CAMERA_Y], a
-
- ; B = CAMERA_X - 2
- ld a, [CURRENT_CAMERA_X]
- sub 2
- ld b, a
-
- ; C = CAMERA_Y + 18 + 1
- ld a, [CURRENT_CAMERA_Y]
- add SCRN_Y_B + 1
- ld c, a
-
- ; MAP Y = SCY/8 + SCRN_Y_B + 1
- ld a, [rSCY]
- srln a, 3
- add SCRN_Y_B + 1
-
- add sp, 1
- jr Map_ScrollRow
-
- .done:
- add sp, 1
- ret
-
-
-
- ; Scroll in a new row
- ; @param a Map VRAM Y-coordinate
- ; @param b Data X-coordinate
- ; @param c Data Y-coordinate
- Map_ScrollRow:
- and %11111
- call get_row_ptr
-
- ld a, [rSCX]
- sub 16
- srl a
- srl a
- srl a
- ld e, a
- ld d, 0
- add hl, de
-
- call enqueue_row_write
- ret
-
-
-
- Map_ScrollLeft::
- ld a, [rSCX]
- and %111
-
- add sp, -1
- ld hl, sp + 0
- ld [hl], a
-
- ld a, [CURRENT_CAMERA_X]
- or a
- jr nz, .scroll
-
- ld hl, sp + 0
- ld a, [hl]
- or a
- jr z, .done
-
- ld b, d
- call min
- ld d, a
- ld a, [rSCX]
- sub d
- ld [rSCX], a
- jr .done
-
- .scroll:
- ld a, [rSCX]
- sub d
- ld [rSCX], a
-
- ; Check SCX_T - D < 0
- ld hl, sp + 0
- ld a, [hl]
- sub d
- jr nc, .done
-
- ld a, [CURRENT_CAMERA_X]
- dec a
- ld [CURRENT_CAMERA_X], a
-
- ld a, [rSCY]
- sub 16
- srl a
- srl a
- srl a
- call get_row_ptr
-
- ; B = CAMERA_X - 2
- ld a, [CURRENT_CAMERA_X]
- sub 2
- ld b, a
-
- ; E = VRAM + (SCX/8 - 2)
- ld a, [rSCX]
- srln a, 3
- sub 2
- ADD16 hl
-
- add sp, 1
- jr Map_ScrollColumn
-
- .done:
- add sp, 1
- ret
-
-
-
- Map_ScrollRight::
- ld a, [rSCX]
- and %111
-
- add sp, -1
- ld hl, sp + 0
- ld [hl], a
-
- ld a, [CURRENT_CAMERA_X]
- add SCRN_X_B
- ld hl, CURRENT_MAP_WIDTH
- cp [hl]
- jr nz, .scroll
-
- ld hl, sp + 0
- ld a, [hl]
- or a
- jr z, .done
-
- sub 8
- cpl
-
- ld b, d
- call min
- ld d, a
- ld a, [rSCX]
- add d
- ld [rSCX], a
- jr .done
-
- .scroll:
- ld a, [rSCX]
- add d
- ld [rSCX], a
-
- ; Check SCX_T + D < 8
- ld hl, sp + 0
- ld a, [hl]
- add d
- cp 8
- jr c, .done
-
- ; CAMERA_X += 1
- ld a, [CURRENT_CAMERA_X]
- inc a
- ld [CURRENT_CAMERA_X], a
-
- ld a, [rSCY]
- sub 16
- srl a
- srl a
- srl a
- call get_row_ptr
-
- ; B = CAMERA_X + 20 + 1
- ld a, [CURRENT_CAMERA_X]
- add SCRN_X_B + 1
- ld b, a
-
- ; E = VRAM + SCX/8 + SCRN_X_B + 1
- ld a, [rSCX]
- srl a
- srl a
- srl a
- add SCRN_X_B + 1
- and %11111
- ADD16 hl
-
- add sp, 1
- jr Map_ScrollColumn
-
- .done:
- add sp, 1
- ret
-
-
-
- Map_ScrollColumn:
- ; C = CAMERA_Y - 2
- ld a, [CURRENT_CAMERA_Y]
- sub 2
- ld c, a
-
- call enqueue_col_write
- 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, ROW_BUFFER_SIZE
- .copy_row:
- ld a, [hl+]
- ld [bc], a
- inc bc
-
- ; if BC % 32 == 0 (we just crossed a row boundary)
- ld a, c
- and %11111
- jr nz, .copy_row1
-
- ; BC -= 32 (reset back to beginning of row)
- ld a, c
- sub 32
- ld c, a
- ld a, b
- sbc 0
- ld b, a
-
- .copy_row1:
- dec d
- jr nz, .copy_row
-
- ; Reset PENDING_ROW_PTR
- xor a
- ld [PENDING_ROW_PTR], a
- ld [PENDING_ROW_PTR + 1], a
-
- .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, COL_BUFFER_SIZE
- ld hl, PENDING_COL_DATA
- .update_col_loop:
- ld a, [hl+]
- ld [bc], a
-
- ld a, SCRN_VX_B
- ADD16 bc
-
- ; If BC = 9c00, set BC = 9800
- CP16 bc, _SCRN0 + SCRN_VY_B * SCRN_VX_B
- jr nz, .update_col_loop_next
- ld b, HIGH(_SCRN0)
-
- .update_col_loop_next:
- dec d
- jr nz, .update_col_loop
-
- ; Reset PENDING_COL_PTR
- xor a
- ld [PENDING_COL_PTR], a
- ld [PENDING_COL_PTR + 1], a
-
- 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, ROW_BUFFER_SIZE
- ld de, PENDING_ROW_DATA
-
- ; If X > 0, increment map pointer by X
- bit 7, b
- jr nz, .pad_left
-
- ld a, b
- ADD16 hl
- jr .copy_middle
-
- .pad_left:
- ; Check X < 0
- bit 7, b
- jr z, .copy_middle
-
- ; Check BYTES_LEFT > 0
- ld a, c
- or a
- ret z
-
- ; *ROW++ = 0
- ld a, TILE_INDEX_BACKGROUND
- 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+]
- add TILE_INDEX_BACKGROUND
- 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
- ld a, TILE_INDEX_BACKGROUND
- 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, ROW_BUFFER_SIZE
- .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 init_row_write
-
- 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 b
- jr c, .zero_row
-
- ; HL = CURRENT_MAP_PTR + X
- ld a, [CURRENT_MAP_PTR]
- ld l, a
- ld a, [CURRENT_MAP_PTR + 1]
- ld h, a
- ld a, b
- ADD16 hl
-
- ; B = BYTES_LEFT
- ld b, COL_BUFFER_SIZE
- ld de, PENDING_COL_DATA
-
- .pad_left:
- ; Check Y < 0
- bit 7, c
- jr z, .adjust_hl
-
- ; Check BYTES_LEFT > 0
- ld a, b
- or a
- ret z
-
- ; *ROW++ = 0
- ld a, TILE_INDEX_BACKGROUND
- ld [de], a
- inc de
-
- ; Y++, BYTES_LEFT--
- inc c
- dec b
- jr .pad_left
-
- .adjust_hl:
- push de
-
- ; HL += Y * MAP_WIDTH
- ld d, 0
- ld a, [CURRENT_MAP_WIDTH]
- ld e, a
- ld a, c
-
- .get_map_col_ptr:
- or a
- jr z, .done_get_col_map_ptr
- add hl, de
- dec a
- jr .get_map_col_ptr
-
- .done_get_col_map_ptr:
- pop de
-
- .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]
- add TILE_INDEX_BACKGROUND
- 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
- ld a, TILE_INDEX_BACKGROUND
- 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, COL_BUFFER_SIZE
- .zero_row_loop:
- ld [hl+], a
- dec c
- jr nz, .zero_row_loop
- ret
-
-
-
- ; Basically enqueue_col_write but for entire row, used during map load
- ; TODO: Deduplicate
- ; @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
- init_row_write:
- ; 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
-
- ; DE = HL
- ld d, h
- ld e, l
-
- ; HL = CURRENT_MAP_PTR
- ld a, [CURRENT_MAP_PTR]
- ld l, a
- ld a, [CURRENT_MAP_PTR + 1]
- ld h, a
-
- push de
-
- ; 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:
- pop de
-
- ; C = BYTES_LEFT
- ld c, SCRN_VX_B
-
- ; If X > 0, increment map pointer by X
- ; TODO: Remove this branch by adding X before copy_middle
- bit 7, b
- jr nz, .pad_left
-
- ld a, b
- ADD16 hl
- jr .copy_middle
-
- .pad_left:
- ; Check X < 0
- bit 7, b
- jr z, .copy_middle
-
- ; Check BYTES_LEFT > 0
- ld a, c
- or a
- ret z
-
- ; *ROW++ = 0
- ld a, TILE_INDEX_BACKGROUND
- 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+]
- add TILE_INDEX_BACKGROUND
- 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
- ld a, TILE_INDEX_BACKGROUND
- ld [de], a
- inc de
-
- ; X++, BYTES_LEFT--
- inc b
- dec c
- jr .pad_right
-
- .zero_row:
- xor a
- ld c, SCRN_VX_B
- .zero_row_loop:
- ld [hl+], a
- dec c
- jr nz, .zero_row_loop
- ret
|