You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

597 lines
9.9 KiB

2 years ago
2 years ago
2 years ago
  1. INCLUDE "hardware.inc"
  2. INCLUDE "util.inc"
  3. SECTION "Map Data", WRAM0
  4. PAGEX:: DB ; X coordinate to enqueue map column at
  5. PAGEY:: DB ; Y coordinate to enqueue map row at
  6. LAST_SCX:: DB ; Value of SCX last frame
  7. LAST_SCY:: DB ; Value of SCY last frame
  8. PENDING_ROW_PTR:: DW ; Where to write pending row data (0 = no write)
  9. PENDING_ROW_DATA:: DS SCRN_VX_B ; Row to be written
  10. PENDING_COL_PTR:: DW ; Where to write pending column data (0 = no write)
  11. PENDING_COL_DATA: DS SCRN_VY_B ; Column to be written
  12. CURRENT_DATA_START::
  13. CURRENT_TILE_PTR:: DW ; Location of tile data
  14. CURRENT_TILE_SIZE:: DB ; Length of tile data (num_tiles * 8)
  15. CURRENT_MAP_PTR:: DW ; Location of map data
  16. CURRENT_MAP_COLLISION:: DW ; Location of map collision data
  17. CURRENT_MAP_WIDTH:: DB ; Width of map in tiles
  18. CURRENT_MAP_HEIGHT:: DB ; Height of map in tiles
  19. CURRENT_SPAWN_X:: DB ; X coordinate to spawn player at
  20. CURRENT_SPAWN_Y:: DB ; Y coordinate to spawn player at
  21. CURRENT_CAMERA_X:: DB ; X coordinate of camera (top left of viewport)
  22. CURRENT_CAMERA_Y:: DB ; Y coordinate of camera (top left of viewport)
  23. CURRENT_DATA_END::
  24. SECTION "Map Code", ROM0
  25. DEF INIT_SCX EQUS "((SCRN_VX - SCRN_X) / 2)"
  26. DEF INIT_SCY EQUS "((SCRN_VY - SCRN_Y) / 2)"
  27. ; Loads a map
  28. ; @param hl Pointer to map metadata
  29. Map_Load::
  30. ; Initialize scroll state
  31. ld a, INIT_SCX
  32. ld [rSCX], a
  33. ld [LAST_SCX], a
  34. ld a, INIT_SCY
  35. ld [rSCY], a
  36. ld [LAST_SCY], a
  37. ld a, 8
  38. ld [PAGEX], a
  39. ld [PAGEY], a
  40. ; Store metadata
  41. ld bc, CURRENT_DATA_START
  42. ld d, CURRENT_DATA_END - CURRENT_DATA_START
  43. call memcpy
  44. ; Write tiles to VRAM
  45. ld hl, CURRENT_TILE_PTR
  46. ld a, [hl+]
  47. ld c, a
  48. ld a, [hl+]
  49. ld b, a
  50. ld a, [CURRENT_TILE_SIZE]
  51. ld d, a
  52. ld hl, _VRAM
  53. MEMCPY hl, bc, d
  54. ; Write initial map data
  55. ld hl, _SCRN0
  56. ld a, [CURRENT_CAMERA_X]
  57. sub INIT_SCX / 8
  58. ld b, a
  59. ld a, [CURRENT_CAMERA_Y]
  60. sub INIT_SCY / 8
  61. ld c, a
  62. ld d, SCRN_VY_B
  63. .write_rows:
  64. call write_map_row
  65. inc c
  66. ld a, SCRN_VX_B
  67. ADD16 hl
  68. dec d
  69. jr nz, .write_rows
  70. ret
  71. ; Update map state based on SCX/SCY
  72. Map_Scroll::
  73. ; If SCY = PAGEY, write map row
  74. ld hl, PAGEY
  75. ld a, [rSCY]
  76. cp [hl]
  77. jr nz, .scroll_down_check
  78. ; B = CAMERA_X - SCX/8
  79. ld a, [rSCX]
  80. srl a
  81. srl a
  82. srl a
  83. ld b, a
  84. ld a, [CURRENT_CAMERA_X]
  85. sub b
  86. ld b, a
  87. ; C = CAMERA_Y - 2
  88. ld a, [CURRENT_CAMERA_Y]
  89. sub 2
  90. ld c, a
  91. ; HL = _SCRN0 + 32 * (SCY/8 - 2)
  92. ld a, [rSCY]
  93. sub 16
  94. srl a
  95. srl a
  96. srl a
  97. call get_row_ptr
  98. call enqueue_row_write
  99. ld a, [PAGEY]
  100. sub 8
  101. ld [PAGEY], a
  102. jp .done
  103. .scroll_down_check:
  104. ; Check SCY + (SCRN_Y + 16) = PAGEY
  105. ld a, [PAGEY]
  106. sub SCRN_Y + 16
  107. ld hl, rSCY
  108. cp [hl]
  109. jr nz, .scroll_left_check
  110. ld a, [PAGEY]
  111. srl a
  112. srl a
  113. srl a
  114. dec a
  115. call get_row_ptr
  116. ; B = CAMERA_X - SCX/8
  117. ld a, [rSCX]
  118. ld b, a
  119. ld a, [CURRENT_CAMERA_X]
  120. srl b
  121. srl b
  122. srl b
  123. sub b
  124. ld b, a
  125. ; C = CAMERA_Y + 18 + 1
  126. ld a, [CURRENT_CAMERA_Y]
  127. add SCRN_Y_B + 1
  128. ld c, a
  129. call enqueue_row_write
  130. ld a, [PAGEY]
  131. add 8
  132. ld [PAGEY], a
  133. jr .done
  134. .scroll_left_check:
  135. ; If SCX = PAGEX, write map col
  136. ld a, [PAGEX]
  137. ld hl, rSCX
  138. cp [hl]
  139. jr nz, .scroll_right_check
  140. ; HL = VRAM + PAGEX/8
  141. ld hl, _SCRN0
  142. ld a, [PAGEX]
  143. srl a
  144. srl a
  145. srl a
  146. dec a
  147. ADD16 hl
  148. ; B = CAMERA_X - 1
  149. ld a, [CURRENT_CAMERA_X]
  150. dec a
  151. ld b, a
  152. ; C = CAMERA_Y - SCY/8
  153. ld a, [rSCY]
  154. ld c, a
  155. ld a, [CURRENT_CAMERA_Y]
  156. srl c
  157. srl c
  158. srl c
  159. sub c
  160. ld c, a
  161. call enqueue_col_write
  162. ld a, [PAGEX]
  163. sub 8
  164. ld [PAGEX], a
  165. .scroll_right_check:
  166. ; If SCX = SCRN_X - PAGEX, write map col
  167. ; Check SCX + (SCRN_X + 16) = PAGEX
  168. ld a, [PAGEX]
  169. sub SCRN_X + 16
  170. ld hl, rSCX
  171. cp [hl]
  172. jr nz, .done
  173. ; HL = VRAM + PAGEX/8
  174. ld hl, _SCRN0
  175. ld a, [PAGEX]
  176. srl a
  177. srl a
  178. srl a
  179. dec a
  180. ADD16 hl
  181. ; B = CAMERA_X + 20
  182. ld a, [CURRENT_CAMERA_X]
  183. add SCRN_X_B + 1
  184. ld b, a
  185. ; C = CAMERA_Y - SCY/8
  186. ld a, [rSCY]
  187. ld c, a
  188. ld a, [CURRENT_CAMERA_Y]
  189. srl c
  190. srl c
  191. srl c
  192. sub c
  193. ld c, a
  194. call enqueue_col_write
  195. ld a, [PAGEX]
  196. add 8
  197. ld [PAGEX], a
  198. .done:
  199. ld hl, LAST_SCY
  200. ld a, [rSCY]
  201. ld [hl], a
  202. ret
  203. Map_Update::
  204. ; Skip row update if PENDING_ROW_PTR is 0
  205. ld a, [PENDING_ROW_PTR]
  206. ld c, a
  207. ld a, [PENDING_ROW_PTR + 1]
  208. ld b, a
  209. or c
  210. jr z, .update_col
  211. ld hl, PENDING_ROW_DATA
  212. ld d, SCRN_VX_B
  213. MEMCPY bc, hl, d
  214. .update_col:
  215. ; Skip column update if PENDING_COL_PTR is 0
  216. ld a, [PENDING_COL_PTR]
  217. ld c, a
  218. ld a, [PENDING_COL_PTR + 1]
  219. ld b, a
  220. or c
  221. ret z
  222. ld d, SCRN_VY_B
  223. ld hl, PENDING_COL_DATA
  224. .update_col_loop:
  225. ld a, [hl+]
  226. ld [bc], a
  227. ld a, SCRN_VX_B
  228. ADD16 bc
  229. dec d
  230. jr nz, .update_col_loop
  231. ret
  232. ; Computes the offset into map RAM
  233. ; @param a The map RAM y-coordinate
  234. ; @return hl Pointer into map RAM
  235. get_row_ptr:
  236. push de
  237. ld d, 0
  238. ld e, a
  239. REPT 5
  240. SLA16 de
  241. ENDR
  242. ld hl, _SCRN0
  243. add hl, de
  244. pop de
  245. ret
  246. ; Write a row of map data into row buffer
  247. ; @param b Map X coordinate (signed)
  248. ; @param c Map Y coordinate (signed)
  249. ; @param hl Where to write the row in map VRAM
  250. ; @destroy All registers
  251. enqueue_row_write:
  252. ; PENDING_ROW_PTR = HL
  253. ld a, l
  254. ld [PENDING_ROW_PTR], a
  255. ld a, h
  256. ld [PENDING_ROW_PTR + 1], a
  257. ; If Y < 0, write 0s
  258. bit 7, c
  259. jr nz, .zero_row
  260. ; If Y >= MAP_HEIGHT, write 0s
  261. ld a, [CURRENT_MAP_HEIGHT]
  262. dec a
  263. cp c
  264. jr c, .zero_row
  265. ; HL = CURRENT_MAP_PTR
  266. ld a, [CURRENT_MAP_PTR]
  267. ld l, a
  268. ld a, [CURRENT_MAP_PTR + 1]
  269. ld h, a
  270. ; HL = CURRENT_MAP_PTR + Y * MAP_WIDTH
  271. ld d, 0
  272. ld a, [CURRENT_MAP_WIDTH]
  273. ld e, a
  274. ld a, c
  275. .get_map_row_ptr:
  276. or a
  277. jr z, .copy_map_row
  278. add hl, de
  279. dec a
  280. jr .get_map_row_ptr
  281. .copy_map_row:
  282. ; C = BYTES_LEFT
  283. ld c, SCRN_VX_B
  284. ld de, PENDING_ROW_DATA
  285. ; Note: Can skip checking BYTES_LEFT > 0 in this loop. If there were
  286. ; SCRN_VX_B zeros to write, then Y would be 1 greater and we would have
  287. ; jumped into .zero_row before reaching this code
  288. .pad_left:
  289. ; Check X < 0
  290. bit 7, b
  291. jr z, .copy_middle
  292. ; *ROW++ = 0
  293. ld [de], a
  294. inc de
  295. ; X++, BYTES_LEFT--
  296. inc b
  297. dec c
  298. jr .pad_left
  299. .copy_middle:
  300. ; Check X < MAP_WIDTH
  301. ld a, [CURRENT_MAP_WIDTH]
  302. dec a
  303. cp b
  304. jr c, .pad_right
  305. ; Check BYTES_LEFT > 0
  306. ld a, c
  307. or a
  308. ret z
  309. ; *ROW++ = *MAP++
  310. ld a, [hl+]
  311. ld [de], a
  312. inc de
  313. ; X++, BYTES_LEFT--
  314. inc b
  315. dec c
  316. jr .copy_middle
  317. .pad_right:
  318. ; Check BYTES_LEFT > 0
  319. ld a, c
  320. or a
  321. ret z
  322. ; *ROW++ = 0
  323. xor a
  324. ld [de], a
  325. inc de
  326. ; X++, BYTES_LEFT--
  327. inc b
  328. dec c
  329. jr .pad_right
  330. .zero_row:
  331. ld hl, PENDING_ROW_DATA
  332. xor a
  333. ld c, SCRN_VX_B
  334. .zero_row_loop:
  335. ld [hl+], a
  336. dec c
  337. jr nz, .zero_row_loop
  338. ret
  339. ; NOTE: This works by enqueueing a row to write, and then immediately flushing
  340. ; the queue. The subroutine could be sped up by writing to map RAM directly, but
  341. ; this is simpler to write and saves on code size.
  342. ;
  343. ; Write a row of map data into map RAM.
  344. ; @param b Map X coordinate (signed)
  345. ; @param c Map Y coordinate (signed)
  346. ; @param hl Pointer into map VRAM
  347. write_map_row:
  348. push bc
  349. push de
  350. push hl
  351. call enqueue_row_write
  352. ld a, [PENDING_ROW_PTR]
  353. ld c, a
  354. ld a, [PENDING_ROW_PTR + 1]
  355. ld b, a
  356. ld hl, PENDING_ROW_DATA
  357. ld d, SCRN_VX_B
  358. MEMCPY bc, hl, d
  359. pop hl
  360. pop de
  361. pop bc
  362. ret
  363. ; Write a column of map data into column buffer
  364. ; @param b Map X coordinate (signed)
  365. ; @param c Map Y coordinate (signed)
  366. ; @param hl Where to write the column in map VRAM
  367. ; @destroy All registers
  368. enqueue_col_write:
  369. ; PENDING_COL_PTR = HL
  370. ld a, l
  371. ld [PENDING_COL_PTR], a
  372. ld a, h
  373. ld [PENDING_COL_PTR + 1], a
  374. ; If X < 0, write 0s
  375. bit 7, b
  376. jr nz, .zero_row
  377. ; If X >= MAP_WIDTH, write 0s
  378. ld a, [CURRENT_MAP_WIDTH]
  379. dec a
  380. cp c
  381. jr c, .zero_row
  382. ; HL = CURRENT_MAP_PTR
  383. ld a, [CURRENT_MAP_PTR]
  384. ld l, a
  385. ld a, [CURRENT_MAP_PTR + 1]
  386. ld h, a
  387. ; HL = CURRENT_MAP_PTR + Y * MAP_WIDTH + X
  388. ld d, 0
  389. ld a, [CURRENT_MAP_WIDTH]
  390. ld e, a
  391. ld a, c
  392. .get_map_col_ptr:
  393. or a
  394. jr z, .copy_map_column
  395. add hl, de
  396. dec a
  397. jr .get_map_col_ptr
  398. .copy_map_column:
  399. ld a, b
  400. ADD16 hl
  401. ; B = BYTES_LEFT
  402. ld b, SCRN_VY_B
  403. ld de, PENDING_COL_DATA
  404. ; Note: Can skip checking BYTES_LEFT > 0 in this loop. If there were
  405. ; SCRN_VY_B zeros to write, then X would be 1 greater and we would have
  406. ; jumped into .zero_row before reaching this code
  407. .pad_left:
  408. ; Check Y < 0
  409. bit 7, c
  410. jr z, .copy_middle
  411. ; *ROW++ = 0
  412. xor a
  413. ld [de], a
  414. inc de
  415. ; Y++, BYTES_LEFT--
  416. inc c
  417. dec b
  418. jr .pad_left
  419. .copy_middle:
  420. ; Check Y < MAP_HEIGHT
  421. ld a, [CURRENT_MAP_HEIGHT]
  422. dec a
  423. cp c
  424. jr c, .pad_right
  425. ; Check BYTES_LEFT > 0
  426. ld a, b
  427. or a
  428. ret z
  429. ; *ROW++ = *MAP
  430. ld a, [hl]
  431. ld [de], a
  432. inc de
  433. ; MAP += MAP_WIDTH
  434. ld a, [CURRENT_MAP_WIDTH]
  435. ADD16 hl
  436. ; Y++, BYTES_LEFT--
  437. inc c
  438. dec b
  439. jr .copy_middle
  440. .pad_right:
  441. ; Check BYTES_LEFT > 0
  442. ld a, b
  443. or a
  444. ret z
  445. ; *ROW++ = 0
  446. xor a
  447. ld [de], a
  448. inc de
  449. ; X++, BYTES_LEFT--
  450. inc c
  451. dec b
  452. jr .pad_right
  453. .zero_row:
  454. ld hl, PENDING_COL_DATA
  455. xor a
  456. ld c, SCRN_VY_B
  457. .zero_row_loop:
  458. ld [hl+], a
  459. dec c
  460. jr nz, .zero_row_loop
  461. ret
  462. ; Compute the distance (modulo 32) between two values
  463. ; @param b Value 1
  464. ; @param c Value 2
  465. mmd2:
  466. push bc
  467. ld a, b
  468. sub c
  469. cpl
  470. ld b, a
  471. cpl
  472. bit 7, b
  473. jr z, .0
  474. ld b, a
  475. .0:
  476. ld a, 32
  477. sub b
  478. cp b
  479. jr nc, .1
  480. pop bc
  481. ret
  482. .1:
  483. ld a, b
  484. pop bc
  485. ret