raycast.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. /*
  2. * Raycaster, based on lodev engine tutorial
  3. * Plan:
  4. * - Improve collision detection:
  5. * - During movement function,
  6. * - Check if new pos has distance from possible wall hor and ver
  7. * - Make rendering variables global with prefix
  8. * - Convert rendering function to assembly
  9. * - Add sprites (as this is done after walls)
  10. */
  11. #define word char
  12. #include "lib/math.c"
  13. #include "lib/stdlib.c"
  14. #include "lib/sys.c"
  15. #include "lib/gfx.c"
  16. #include "lib/fp.c"
  17. // Note: these are also hardcoded in the render assembly, so update there as well!
  18. #define FB_ADDR 0xD00000
  19. #define screenWidth 160
  20. #define screenHeight 120
  21. #define texWidth 64
  22. #define texHeight 64
  23. #define mapWidth 24
  24. #define mapHeight 24
  25. // Input
  26. #define BTN_LEFT 256
  27. #define BTN_RIGHT 257
  28. #define BTN_UP 258
  29. #define BTN_DOWN 259
  30. // Colors
  31. #define COLOR_RED 224
  32. #define COLOR_DARK_RED 96
  33. #define COLOR_GREEN 28
  34. #define COLOR_DARK_GREEN 12
  35. #define COLOR_BLUE 3
  36. #define COLOR_DARK_BLUE 2
  37. #define COLOR_WHITE 0xFF
  38. #define COLOR_GREY 0xB6
  39. #define COLOR_YELLOW 0xFC
  40. #define COLOR_DARK_YELLOW 0x90
  41. // Data
  42. /*
  43. - LUTdirX
  44. - LUTdirY
  45. - LUTplaneX
  46. - LUTplaney
  47. - texture
  48. */
  49. #include "data/raydat.c"
  50. // Framebuffer. fb[Y][X] (bottom right is [239][319])
  51. char (*fb)[320] = (char (*)[320])FB_ADDR;
  52. word worldMap[mapWidth][mapHeight] = {
  53. {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7},
  54. {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
  55. {4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
  56. {4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7},
  57. {4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 7},
  58. {4, 0, 4, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 0, 7, 7, 7, 7, 7},
  59. {4, 0, 5, 0, 0, 0, 0, 2, 0, 1, 0, 1, 0, 1, 0, 2, 7, 0, 0, 0, 7, 7, 7, 1},
  60. {4, 0, 6, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 7, 0, 0, 0, 0, 0, 0, 8},
  61. {4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 1},
  62. {4, 0, 8, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 8},
  63. {4, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 7, 7, 7, 1},
  64. {4, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 1},
  65. {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
  66. {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4},
  67. {6, 6, 6, 6, 6, 6, 0, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6},
  68. {4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 6, 0, 6, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3},
  69. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
  70. {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
  71. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
  72. {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 2},
  73. {4, 0, 0, 5, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2},
  74. {4, 0, 6, 0, 6, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 5, 0, 0, 2, 0, 0, 0, 2},
  75. {4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 0, 6, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2},
  76. {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3}};
  77. word frameDone = 0;
  78. // Global render variables
  79. word drawStart = 0;
  80. word drawEnd = 0;
  81. word texNum = 0;
  82. word side = 0; // was a NorthSouth or a EastWest wall hit?
  83. // x and y start position
  84. fixed_point_t RAY_posX;
  85. fixed_point_t RAY_posY;
  86. // initial direction vector
  87. fixed_point_t RAY_dirX;
  88. fixed_point_t RAY_dirY;
  89. // the 2d raycaster version of camera plane
  90. fixed_point_t RAY_planeX;
  91. fixed_point_t RAY_planeY;
  92. // Render entire screen
  93. // Registers (global):
  94. // r1 x current vertical line (x of screen)
  95. void RAYFX_renderScreen()
  96. {
  97. // reg 4 5 6 7 (args) and 2 3 (retval) are safe
  98. asm(
  99. // backdup registers
  100. "push r1\n"
  101. "push r8\n"
  102. "push r9\n"
  103. "push r10\n"
  104. "push r11\n"
  105. "push r12\n"
  106. "push r13\n"
  107. "push r14\n"
  108. "push r15\n"
  109. );
  110. asm(
  111. // r1: current vertical line (x of screen)
  112. "load32 0 r1\n"
  113. // for each vertical line (x)
  114. "RAYFX_screenXloop:\n"
  115. // cameraX
  116. "addr2reg LUTcameraX r15\n" // r15 = LUTcameraX
  117. "add r15 r1 r15\n" // r15 = LUTcameraX[x] addr
  118. "read 0 r15 r15\n" // r15 = LUTcameraX[x] value
  119. // r2 rayDirx [FP] x dir of ray
  120. // r3 rayDiry [FP] y dir of ray
  121. // r2: rayDirX
  122. "addr2reg RAY_dirX r14\n" // r14 = RAY_dirX addr
  123. "read 0 r14 r14\n" // r14 = RAY_dirX value
  124. "addr2reg RAY_planeX r13\n" // r13 = RAY_planeX addr
  125. "read 0 r13 r13\n" // r13 = RAY_planeX value
  126. "multfp r13 r15 r2\n" // r2 = FP_Mult(RAY_planeX, cameraX)
  127. "add r2 r14 r2\n" // r2 += RAY_dirX
  128. // r3: rayDirY
  129. "addr2reg RAY_dirY r14\n" // r14 = RAY_dirY addr
  130. "read 0 r14 r14\n" // r14 = RAY_dirY value
  131. "addr2reg RAY_planeY r13\n" // r13 = RAY_planeY addr
  132. "read 0 r13 r13\n" // r13 = RAY_planeY value
  133. "multfp r13 r15 r3\n" // r3 = FP_Mult(RAY_planeY, cameraX)
  134. "add r3 r14 r3\n" // r3 += RAY_dirY
  135. // r4: deltaDistX
  136. "load32 1 r15\n" // r15 = 1
  137. "shiftl r15 16 r15\n" // r15 = FP(1)
  138. "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
  139. "write 0 r14 r15\n" // write a (fp1) to divider
  140. "write 1 r14 r2\n" // write b (rayDirX) to divider and perform division
  141. "read 1 r14 r4\n" // read result to r4
  142. "shiftrs r4 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
  143. "xor r4 r13 r4\n" // r4 = (x ^ y)
  144. "sub r4 r13 r4\n" // r4 -= y (result of abs)
  145. // r5: deltaDistY (uses r14 and r15 from above)
  146. "write 0 r14 r15\n" // write a (fp1) to divider
  147. "write 1 r14 r3\n" // write b (rayDirY) to divider and perform division
  148. "read 1 r14 r5\n" // read result to r5
  149. "shiftrs r5 31 r13\n" // r13 = y = x >>(signed) 31 (start of abs)
  150. "xor r5 r13 r5\n" // r5 = (x ^ y)
  151. "sub r5 r13 r5\n" // r5 -= y (result of abs)
  152. // if (rayDirX < 0), else
  153. // r6: stepX
  154. // r7: stepY
  155. // r8: sideDistX
  156. // r9: sideDistY
  157. // r10: mapX
  158. // r11: mapY
  159. "blts r2 r0 2\n" // if (rayDirX < 0) skip next line
  160. "jump RAYFX_rayDirXge0\n" // goto RAYFX_rayDirXge0
  161. "load32 -1 r6\n" // r6 = stepX = -1
  162. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  163. "read 0 r15 r15\n" // r15 = RAY_posX value
  164. "shiftrs r15 16 r10\n" // mapX = int(RAY_posX)
  165. "load32 0xFFFF r14\n" // r14 = decimal part mask
  166. "and r15 r14 r15\n" // r15 = RAY_posX decimal part
  167. "multfp r15 r4 r8\n" // sideDistX = RAY_posX decimal part * deltaDistX
  168. "jump RAYFX_rayDirXltEnd\n" // skip the else part
  169. "RAYFX_rayDirXge0:\n"
  170. "load32 1 r6\n" // stepX = 1
  171. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  172. "read 0 r15 r15\n" // r15 = RAY_posX value
  173. "shiftrs r15 16 r10\n" // r10 (mapX) = r15 >> 16 (to int)
  174. "add r10 1 r14\n" // r14 = int(RAY_posX) + 1
  175. "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
  176. "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
  177. "multfp r15 r4 r8\n" // sideDistX = RAY_posX-ish * deltaDistX
  178. "RAYFX_rayDirXltEnd:\n"
  179. // if (rayDirY < 0), else
  180. "blts r3 r0 2\n" // if (rayDirY < 0) skip next line
  181. "jump RAYFX_rayDirYge0\n" // goto RAYFX_rayDirYge0
  182. "load32 -1 r7\n" // r7 = stepY = -1
  183. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  184. "read 0 r15 r15\n" // r15 = RAY_posY value
  185. "shiftrs r15 16 r11\n" // mapY = int(RAY_posY)
  186. "load32 0xFFFF r14\n" // r14 = decimal part mask
  187. "and r15 r14 r15\n" // r15 = RAY_posY decimal part
  188. "multfp r15 r5 r9\n" // sideDistY = RAY_posY decimal part * deltaDistY
  189. "jump RAYFX_rayDirYltEnd\n" // skip the else part
  190. "RAYFX_rayDirYge0:\n"
  191. "load32 1 r7\n" // stepY = 1
  192. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  193. "read 0 r15 r15\n" // r15 = RAY_posY value
  194. "shiftrs r15 16 r11\n" // r11 (mapY) = r15 >> 16 (to int)
  195. "add r11 1 r14\n" // r14 = int(RAY_posY) + 1
  196. "shiftl r14 16 r14\n" // r14 <<=16 (to FP)
  197. "sub r14 r15 r15\n" // r15 = r14 - RAY_posX
  198. "multfp r15 r5 r9\n" // sideDistY = RAY_posX-ish * deltaDistY
  199. "RAYFX_rayDirYltEnd:\n"
  200. // DDA (uses tmp r15)
  201. "load32 0 r15\n" // hit = 0
  202. "RAYFX_DDAwhileNoHit:\n" // while (hit == 0)
  203. "beq r15 r0 2\n" // while check
  204. "jump RAYFX_DDAwhileNoHitDone\n"
  205. // if (sideDistX < sideDistY), else
  206. "blts r8 r9 2\n" // if (sideDistX < sideDistY) skip next line
  207. "jump RAYFX_sideDistXltY\n" // goto RAYFX_sideDistXltY
  208. "add r8 r4 r8\n" // sideDistX += deltaDistX;
  209. "add r10 r6 r10\n" // mapX += stepX;
  210. "addr2reg side r14\n" // side;
  211. "write 0 r14 r0\n" // side = 0;
  212. "jump RAYFX_sideDistXltYend\n" // skip the else part
  213. "RAYFX_sideDistXltY:\n"
  214. "add r9 r5 r9\n" // sideDistY += deltaDistY;
  215. "add r11 r7 r11\n" // mapY += stepY;
  216. "addr2reg side r14\n" // side;
  217. "load32 1 r13\n" // 1
  218. "write 0 r14 r13\n" // side = 1;
  219. "RAYFX_sideDistXltYend:\n"
  220. "addr2reg worldMap r14\n" // r14 = worldMap addr;
  221. "multu r10 24 r13\n" // r13 = mapX offset using mapHeight
  222. "add r13 r11 r13\n" // r13 += mapY offset
  223. "add r13 r14 r14\n" // r14 = worldMap[mapX][mapY] addr
  224. "read 0 r14 r14\n" // r14 = worldMap[mapX][mapY] value
  225. "bles r14 r0 2\n" // skip next instruction if worldMap[mapX][mapY] <= 0
  226. "load 1 r15\n" // hit = 1
  227. "jump RAYFX_DDAwhileNoHit\n" // return to while loop start
  228. "RAYFX_DDAwhileNoHitDone:\n"
  229. // From this point, r10 r11 mapx mapy (and r15) are free
  230. // texNum = worldMap[mapX][mapY] - 1;
  231. // assumes worldMap[mapX][mapY] value is still in r14
  232. "addr2reg texNum r13\n" // r13 = texNum addr;
  233. "sub r14 1 r14\n" // r14 = worldMap[mapX][mapY] - 1
  234. "write 0 r13 r14\n" // write texNum;
  235. // r10: perpWallDist (FP)
  236. "addr2reg side r15\n" // side addr
  237. "read 0 r15 r15\n" // side value
  238. // if (side == 0), else
  239. "bne r15 r0 3\n" // if (side != 0) skip next two lines
  240. "sub r8 r4 r10\n" // perpWallDist = (sideDistX - deltaDistX); (match)
  241. "jumpo 2\n" // skip else part
  242. "sub r9 r5 r10\n" // perpWallDist = (sideDistY - deltaDistY); (else)
  243. // From this point sideDistX&Y and deltaDistX&Y are free
  244. // this frees up regs 4 5 8 and 9
  245. // leaving to use: 4, 5, 8, 9, 11, 12, 13, 14, 15
  246. // r4: lineHeight
  247. "load32 120 r15\n" // r15 = screenHeight
  248. "shiftl r15 16 r15\n" // r15 = FP(screenHeight)
  249. "load32 0xC02742 r14\n" // r14 = addr fpdiv_writea
  250. "write 0 r14 r15\n" // write a (FP(screenHeight) to divider
  251. "write 1 r14 r10\n" // write b (perpWallDist) to divider and perform division
  252. "read 1 r14 r4\n" // read result to r4
  253. "shiftrs r4 16 r4\n" // r4 = lineHeight = int(r4)
  254. "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
  255. "load32 -1 r14\n" // r14 = -1
  256. "mults r15 r14 r15\n" // r15 = -r15
  257. "load32 60 r14\n" // r14 = screenHeight >> 1
  258. "add r14 r15 r15\n" // r15 = drawStart value (r14+r15)
  259. "bges r15 r0 2\n" // skip next line if drawStart >= 0
  260. "load 0 r15\n" // set drawStart to 0 if < 0
  261. "addr2reg drawStart r14\n" // r14 = drawStart addr
  262. "write 0 r14 r15\n" // write drawStart value
  263. // skip render if start > 118
  264. "load32 118 r14\n"
  265. "blts r15 r14 2 \n"
  266. "jump RAYFX_skipRenderLine\n"
  267. "shiftrs r4 1 r15\n" // r15 = lineHeight >> 1
  268. "load32 60 r14\n" // r14 = screenHeight >> 1
  269. "add r14 r15 r15\n" // r15 = drawEnd value (r14+r15)
  270. "load32 120 r14\n" // r14 = screenHeight
  271. "blts r15 r14 2\n" // skip next line if drawEnd < screenHeight
  272. "load 120 r15\n" // set drawEnd to screenHeight - 1
  273. "addr2reg drawEnd r14\n" // r14 = drawEnd addr
  274. "write 0 r14 r15\n" // write drawEnd value
  275. // texture calculations
  276. // r8: side
  277. // r5: wallX
  278. "addr2reg side r8\n" // r8 = side addr
  279. "read 0 r8 r8\n" // r8 = side value
  280. "bne r8 r0 7\n" // if side != 0, goto else part (addr2reg is two instructions!)
  281. "addr2reg RAY_posY r15\n" // r15 = RAY_posY addr
  282. "read 0 r15 r15\n" // r15 = RAY_posY value
  283. "multfp r10 r3 r14\n" // r14 = perpWallDist * rayDirY
  284. "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posY
  285. "jumpo 5\n" // skip else
  286. // else
  287. "addr2reg RAY_posX r15\n" // r15 = RAY_posX addr
  288. "read 0 r15 r15\n" // r15 = RAY_posX value
  289. "multfp r10 r2 r14\n" // r14 = perpWallDist, rayDirX
  290. "add r14 r15 r5\n" // r5 = wallX = r14 + RAY_posX
  291. "load32 0xFFFF r15\n" // r15 = floormask
  292. "and r5 r15 r5\n" // wallX-=floor(wallX)
  293. // r6: texX
  294. "load32 64 r15\n" // r15 = texWidth
  295. "shiftl r15 16 r14\n" // r14 = FP(texWidth)
  296. "multfp r14 r5 r6\n" // r6 = FP_Mult(wallX, FP_intToFP(texWidth))
  297. "shiftrs r6 16 r6\n" // r6 = int(r6)
  298. "bne r8 r0 4\n" // skip if side != 0
  299. "bles r2 r0 3\n" // skip if rayDirX <= 0
  300. "sub r15 r6 r6\n" // texX = texWidth - texX
  301. "sub r6 1 r6\n" // texX -= 1
  302. "beq r8 r0 4\n" // skip if side == 0
  303. "bges r3 r0 3\n" // skip if rayDirY >= 0
  304. "sub r15 r6 r6\n" // texX = texWidth - texX
  305. "sub r6 1 r6\n" // texX -= 1
  306. // r2 and r3 are free now (no rayDirX&Y needed)
  307. // r5: step
  308. "load32 64 r15\n" // r15 = texHeight
  309. "shiftl r15 16 r15\n" // r15 = FP(texHeight)
  310. "shiftl r4 16 r14\n" // r14 = FP(lineHeight)
  311. "load32 0xC02742 r13\n" // r13 = addr fpdiv_writea
  312. "write 0 r13 r15\n" // write a (FP(texHeight) to divider
  313. "write 1 r13 r14\n" // write b (FP(lineHeight)) to divider and perform division
  314. "read 1 r13 r5\n" // read result to r5
  315. // r4: texPos
  316. "addr2reg drawStart r15\n" // r15 = drawStart addr
  317. "read 0 r15 r15\n" // r15 = drawStart value
  318. "sub r15 60 r15\n" // r15 -= (screenHeight >> 1)
  319. "shiftrs r4 1 r14\n" // r14 = lineHeight >> 1
  320. "add r15 r14 r15\n" // r15 += lineHeight >> 1
  321. "shiftl r15 16 r15\n" // r15 = FP(r15)
  322. "multfp r15 r5 r4\n" // r4 = r15 * step
  323. "shiftl r1 0 r7\n" // move x to r7
  324. // Render vertical line in pixel plane with textures
  325. // Registers:
  326. // r1 first pixel addr VRAM addr of first pixel in line (top pixel)
  327. // r2 (a2r) drawStart Starting pixel of wall
  328. // r3 (a2r) drawEnd Ending pixel of wall
  329. // r4 texPos Starting texture coordinate
  330. // r5 step How much to increase the texture coordinate per screen pixel
  331. // r6 texX X coordinate on the texture
  332. // r7 x X position of line to render
  333. // r8 current FB pos Current framebuffer position in VRAM (top to bottom)
  334. // r9 end loop FB pos Last framebuffer position in VRAM of current loop
  335. // r10 texY Y coordinate on the texture
  336. // r11 gp Used as temp reg in calculations
  337. // r12 texture[texNum] Texture array of texture to draw
  338. // r13 color Pixel color
  339. // r14 ceil or floor col Ceiling or floor color
  340. // r15 side North South or East West wall side
  341. "push r1\n" // backup x loop var
  342. "addr2reg drawStart r2 ; r2 = drawStart addr\n"
  343. "read 0 r2 r2 ; r2 = drawStart value\n"
  344. "addr2reg drawEnd r3 ; r3 = drawEnd addr\n"
  345. "read 0 r3 r3 ; r3 = drawEnd value\n"
  346. "load32 0xD00000 r1 ; r1 = framebuffer addr\n"
  347. "add r1 r7 r1 ; r1 = first pixel in line (fb+x)\n"
  348. "or r0 r1 r8 ; r8 = current pixel\n"
  349. "multu r2 320 r9 ; r9 = drawStart VRAM offset\n"
  350. "add r9 r1 r9 ; r9 = last FB pos of before wall\n"
  351. "addr2reg texNum r12 ; r12 = texNum addr\n"
  352. "read 0 r12 r12 ; r12 = texNum value\n"
  353. "multu r12 4096 r12 ; r12 = texture offset (64*64 per texture)\n"
  354. "addr2reg texture r11 ; r11 = texture array\n"
  355. "add r12 r11 r12 ; r12 = texture[texNum]\n"
  356. "load32 0x0082F0 r14 ; r14 = ceiling color\n"
  357. "addr2reg side r15 ; r15 = side addr\n"
  358. "read 0 r15 r15 ; r15 = side value\n"
  359. "ble r2 r0 5 ; skip ceiling if wall starts at first pixel\n"
  360. // draw until start
  361. "RAYFX_drawVlineLoopCeiling:\n"
  362. " write 0 r8 r14 ; write ceiling pixel\n"
  363. " add r8 320 r8 ; go to next line pixel\n"
  364. " bge r8 r9 2 ; keep looping until reached wall\n"
  365. " jump RAYFX_drawVlineLoopCeiling\n"
  366. "multu r3 320 r9 ; r9 = drawEnd VRAM offset\n"
  367. "add r9 r1 r9 ; r9 = last FB pos of wall\n"
  368. "load32 8355711 r2 ; r2 = mask for darken color\n"
  369. // draw until floor
  370. "RAYFX_drawVlineLoopWall:\n"
  371. " shiftrs r4 16 r11 ; r11 = texY = FPtoInt(texPos)\n"
  372. " and r11 63 r11 ; r11 = r11 & (texHeight-1)\n"
  373. " add r4 r5 r4 ; texPos += step\n"
  374. " multu r11 64 r11 ; r11 = texHeight * texY \n"
  375. " add r11 r6 r11 ; r11 += texX\n"
  376. " add r11 r12 r13 ; r13 = addr of color in texture array\n"
  377. " read 0 r13 r13 ; r13 = pixel color\n"
  378. " beq r15 r0 3 ; skip next two lines if not side of wall is hit\n"
  379. " shiftrs r13 1 r13\n" // r13 >> 1
  380. " and r13 r2 r13 ; r13 & darken color mask\n"
  381. " write 0 r8 r13 ; write texture pixel\n"
  382. " add r8 320 r8 ; go to next line pixel\n"
  383. " bge r8 r9 2 ; keep looping until reached floor\n"
  384. " jump RAYFX_drawVlineLoopWall\n"
  385. "load32 120 r11\n"
  386. "bge r3 r11 10 ; skip floor if wall ends at bottom of screen\n"
  387. "load32 119 r9 ; r9 = last y position\n"
  388. "multu r9 320 r9 ; r9 = screen end VRAM offset\n"
  389. "add r9 r1 r9 ; r9 = last FB pos of line\n"
  390. "load32 0x9E9E9E r14 ; r14 = floor color\n"
  391. "; draw until end of screen\n"
  392. "RAYFX_drawVlineLoopFloor:\n"
  393. " write 0 r8 r14 ; write floor pixel\n"
  394. " add r8 320 r8 ; go to next line pixel\n"
  395. " bgt r8 r9 2 ; keep looping until reached end of screen\n"
  396. " jump RAYFX_drawVlineLoopFloor\n"
  397. "pop r1\n" // restore x loop var
  398. "RAYFX_skipRenderLine:\n"
  399. "add r1 1 r1\n" // r1 = x++
  400. "load32 160 r15\n" // r15 = stop x loop (screenWidth)
  401. "bge r1 r15 2\n" // keep looping until reached final vline (x) of screen
  402. "jump RAYFX_screenXloop\n"
  403. );
  404. asm(
  405. // restore registers
  406. "pop r15\n"
  407. "pop r14\n"
  408. "pop r13\n"
  409. "pop r12\n"
  410. "pop r11\n"
  411. "pop r10\n"
  412. "pop r9\n"
  413. "pop r8\n"
  414. "pop r1\n"
  415. );
  416. }
  417. int main() {
  418. // clear screen from text
  419. GFX_clearWindowtileTable();
  420. GFX_clearWindowpaletteTable();
  421. GFX_clearBGtileTable();
  422. GFX_clearBGpaletteTable();
  423. GFX_setHalfRes(1);
  424. // x and y start position
  425. RAY_posX = FP_intToFP(15);
  426. RAY_posY = FP_StringToFP("11.5");
  427. // initial direction vector
  428. RAY_dirX = LUTdirX[0];
  429. RAY_dirY = LUTdirY[0];
  430. // the 2d raycaster version of camera plane
  431. RAY_planeX = LUTplaneX[0];
  432. RAY_planeY = LUTplaneY[0];
  433. // rotation angle (loops at 360)
  434. word rotationAngle = 0;
  435. word rotationSpeed = 0; // degrees per frame
  436. fixed_point_t moveSpeed = 0;
  437. fixed_point_t movePadding = FP_StringToFP("0.15");
  438. word quitGame = 0;
  439. word prevMillis = millis();
  440. while (!quitGame) {
  441. // wait until at least one frame has passed
  442. while (!frameDone);
  443. frameDone = 0;
  444. // render screen
  445. RAYFX_renderScreen();
  446. // calculate and draw FPS
  447. word currentMillis = millis();
  448. word renderMillis = currentMillis - prevMillis;
  449. prevMillis = currentMillis;
  450. word fps = FP_FPtoInt(FP_Div(FP_intToFP(1000), FP_intToFP(renderMillis)));
  451. char buffer[11];
  452. itoa(fps, buffer);
  453. // clear fps digits (assumes fps <= 999)
  454. asm(
  455. "push r1\n"
  456. "load32 0xC01420 r1 ; r1 = vram addr\n"
  457. "write 0 r1 r0 ; write char to vram\n"
  458. "write 1 r1 r0 ; write char to vram\n"
  459. "write 2 r1 r0 ; write char to vram\n"
  460. "pop r1\n"
  461. );
  462. GFX_printWindowColored(buffer, strlen(buffer), 0, 0);
  463. // adjust movement and rotation speed based on FPS
  464. moveSpeed = FP_Mult(FP_Div(FP_intToFP(renderMillis), FP_intToFP(1000)), FP_intToFP(3));
  465. rotationSpeed = renderMillis >> 1;
  466. // check which button is held
  467. if (bdos_usbkey_held('a')) {
  468. // both camera direction and camera plane must be rotated
  469. rotationAngle -= rotationSpeed;
  470. if (rotationAngle < 0) {
  471. rotationAngle += 1440;
  472. }
  473. RAY_dirX = LUTdirX[rotationAngle];
  474. RAY_dirY = LUTdirY[rotationAngle];
  475. RAY_planeX = LUTplaneX[rotationAngle];
  476. RAY_planeY = LUTplaneY[rotationAngle];
  477. } else if (bdos_usbkey_held('d')) {
  478. // both camera direction and camera plane must be rotated
  479. rotationAngle += rotationSpeed;
  480. if (rotationAngle >= 1440) {
  481. rotationAngle -= 1440;
  482. }
  483. RAY_dirX = LUTdirX[rotationAngle];
  484. RAY_dirY = LUTdirY[rotationAngle];
  485. RAY_planeX = LUTplaneX[rotationAngle];
  486. RAY_planeY = LUTplaneY[rotationAngle];
  487. }
  488. if (bdos_usbkey_held('w')) {
  489. word worldMapX = FP_FPtoInt(RAY_posX + FP_Mult(RAY_dirX, moveSpeed + movePadding));
  490. word worldMapY = FP_FPtoInt(RAY_posY);
  491. if (worldMap[worldMapX][worldMapY] == 0) {
  492. RAY_posX += FP_Mult(RAY_dirX, moveSpeed);
  493. }
  494. worldMapX = FP_FPtoInt(RAY_posX);
  495. worldMapY = FP_FPtoInt(RAY_posY + FP_Mult(RAY_dirY, moveSpeed + movePadding));
  496. if (worldMap[worldMapX][worldMapY] == 0) {
  497. RAY_posY += FP_Mult(RAY_dirY, moveSpeed);
  498. }
  499. } else if (bdos_usbkey_held('s')) {
  500. word worldMapX = FP_FPtoInt(RAY_posX - FP_Mult(RAY_dirX, moveSpeed + movePadding));
  501. word worldMapY = FP_FPtoInt(RAY_posY);
  502. if (worldMap[worldMapX][worldMapY] == 0) {
  503. RAY_posX -= FP_Mult(RAY_dirX, moveSpeed);
  504. }
  505. worldMapX = FP_FPtoInt(RAY_posX);
  506. worldMapY = FP_FPtoInt(RAY_posY - FP_Mult(RAY_dirY, moveSpeed + movePadding));
  507. if (worldMap[worldMapX][worldMapY] == 0) {
  508. RAY_posY -= FP_Mult(RAY_dirY, moveSpeed);
  509. }
  510. } else if (bdos_usbkey_held('e')) {
  511. word worldMapX = FP_FPtoInt(RAY_posX + FP_Mult(RAY_planeX, moveSpeed + movePadding));
  512. word worldMapY = FP_FPtoInt(RAY_posY);
  513. if (worldMap[worldMapX][worldMapY] == 0) {
  514. RAY_posX += FP_Mult(RAY_planeX, moveSpeed);
  515. }
  516. worldMapX = FP_FPtoInt(RAY_posX);
  517. worldMapY = FP_FPtoInt(RAY_posY + FP_Mult(RAY_planeY, moveSpeed + movePadding));
  518. if (worldMap[worldMapX][worldMapY] == 0) {
  519. RAY_posY += FP_Mult(RAY_planeY, moveSpeed);
  520. }
  521. } else if (bdos_usbkey_held('q')) {
  522. word worldMapX = FP_FPtoInt(RAY_posX - FP_Mult(RAY_planeX, moveSpeed + movePadding));
  523. word worldMapY = FP_FPtoInt(RAY_posY);
  524. if (worldMap[worldMapX][worldMapY] == 0) {
  525. RAY_posX -= FP_Mult(RAY_planeX, moveSpeed);
  526. }
  527. worldMapX = FP_FPtoInt(RAY_posX);
  528. worldMapY = FP_FPtoInt(RAY_posY - FP_Mult(RAY_planeY, moveSpeed + movePadding));
  529. if (worldMap[worldMapX][worldMapY] == 0) {
  530. RAY_posY -= FP_Mult(RAY_planeY, moveSpeed);
  531. }
  532. }
  533. if (hid_checkfifo()) {
  534. word c = hid_fiforead();
  535. if (c == 27) // escape
  536. {
  537. // clean up and exit
  538. GFX_clearPXframebuffer();
  539. GFX_clearWindowtileTable();
  540. GFX_clearWindowpaletteTable();
  541. GFX_clearBGtileTable();
  542. GFX_clearBGpaletteTable();
  543. GFX_setHalfRes(0);
  544. quitGame = 1;
  545. }
  546. else if (c == '1')
  547. {
  548. GFX_setHalfRes(0);
  549. }
  550. else if (c == '2')
  551. {
  552. GFX_setHalfRes(1);
  553. }
  554. }
  555. }
  556. return 'q';
  557. }
  558. void interrupt() {
  559. // Handle all interrupts
  560. word i = get_int_id();
  561. switch (i) {
  562. case INTID_TIMER1:
  563. timer1Value = 1; // Notify ending of timer1
  564. break;
  565. case INTID_GPU:
  566. frameDone++;
  567. break;
  568. }
  569. }