An emulator, assembler, and disassembler for the Sega Game Gear
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.
 
 
 
 
 

396 lines
11 KiB

  1. /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <string.h>
  4. #include <SDL.h>
  5. #include "vdp.h"
  6. #include "util.h"
  7. #define CODE_VRAM_READ 0
  8. #define CODE_VRAM_WRITE 1
  9. #define CODE_REG_WRITE 2
  10. #define CODE_CRAM_WRITE 3
  11. extern SDL_Renderer* renderer;
  12. /*
  13. Initialize the Video Display Processor (VDP).
  14. */
  15. void vdp_init(VDP *vdp)
  16. {
  17. vdp->vram = cr_malloc(sizeof(uint8_t) * VDP_VRAM_SIZE);
  18. vdp->cram = cr_malloc(sizeof(uint8_t) * VDP_CRAM_SIZE);
  19. memset(vdp->vram, 0x00, VDP_VRAM_SIZE);
  20. memset(vdp->cram, 0x00, VDP_CRAM_SIZE);
  21. }
  22. /*
  23. Free memory previously allocated by the VDP.
  24. */
  25. void vdp_free(VDP *vdp)
  26. {
  27. free(vdp->vram);
  28. }
  29. /*
  30. Power on the VDP, setting up initial state.
  31. */
  32. void vdp_power(VDP *vdp)
  33. {
  34. vdp->regs[0x00] = 0x00;
  35. vdp->regs[0x01] = 0x00;
  36. vdp->regs[0x02] = 0xFF;
  37. vdp->regs[0x03] = 0xFF;
  38. vdp->regs[0x04] = 0xFF;
  39. vdp->regs[0x05] = 0xFF;
  40. vdp->regs[0x06] = 0xFF;
  41. vdp->regs[0x07] = 0x00;
  42. vdp->regs[0x08] = 0x00;
  43. vdp->regs[0x09] = 0x00;
  44. vdp->regs[0x0A] = 0x01;
  45. vdp->h_counter = 0;
  46. vdp->v_counter = 0;
  47. vdp->v_count_jump = false;
  48. vdp->control_code = 0;
  49. vdp->control_addr = 0;
  50. vdp->control_flag = false;
  51. vdp->stat_int = vdp->stat_ovf = vdp->stat_col = 0;
  52. vdp->read_buf = 0;
  53. vdp->cram_latch = 0;
  54. }
  55. /*
  56. Return whether frame-completion interrupts are enabled.
  57. */
  58. static bool should_frame_interrupt(const VDP *vdp)
  59. {
  60. return vdp->regs[0x01] & 0x20;
  61. }
  62. /*
  63. Return the base address of the pattern name table.
  64. */
  65. static uint16_t get_pnt_base(const VDP *vdp)
  66. {
  67. return (vdp->regs[0x02] & 0x0E) << 10;
  68. }
  69. /*
  70. Return the base address of the sprite attribute table.
  71. */
  72. static uint16_t get_sat_base(const VDP *vdp)
  73. {
  74. return (vdp->regs[0x05] & 0x7E) << 7;
  75. }
  76. /*
  77. Return the base address of the sprite generator table.
  78. */
  79. static uint16_t get_sgt_base(const VDP *vdp)
  80. {
  81. return (vdp->regs[0x06] & 0x04) << 11;
  82. }
  83. /*
  84. Return the CRAM address of the backdrop color.
  85. */
  86. static uint8_t get_backdrop_addr(const VDP *vdp)
  87. {
  88. return ((vdp->regs[0x07] & 0x0F) << 1) + 0x20;
  89. }
  90. /*
  91. TODO: ...
  92. */
  93. static void draw_pixel(uint8_t y, uint8_t x, uint16_t color)
  94. {
  95. uint8_t r = 0x11 * (color & 0x000F);
  96. uint8_t g = 0x11 * ((color & 0x00F0) >> 4);
  97. uint8_t b = 0x11 * ((color & 0x0F00) >> 8);
  98. SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
  99. uint8_t i, j;
  100. for (i = 0; i < 3; i++) {
  101. for (j = 0; j < 3; j++)
  102. SDL_RenderDrawPoint(renderer, 3 * x + i, 3 * y + j);
  103. }
  104. }
  105. /*
  106. Draw the background of the current scanline.
  107. */
  108. static void draw_background(VDP *vdp)
  109. {
  110. uint8_t *pnt = vdp->vram + get_pnt_base(vdp);
  111. uint8_t row = (vdp->v_counter + vdp->regs[0x09]) % (28 * 8);
  112. uint8_t col;
  113. for (col = 6; col < 32 - 6; col++) {
  114. uint16_t index = (row >> 3) * 32 + col;
  115. uint16_t tile = pnt[2 * index] + (pnt[2 * index + 1] << 8);
  116. uint16_t pattern = tile & 0x01FF;
  117. bool palette = tile & 0x0800;
  118. bool priority = tile & 0x1000;
  119. bool vflip = tile & 0x0400;
  120. bool hflip = tile & 0x0200;
  121. uint8_t offy = vflip ? (7 - row % 8) : (row % 8);
  122. uint8_t *pixels = &vdp->vram[pattern * 32 + 4 * offy];
  123. uint8_t bp0 = pixels[0], bp1 = pixels[1],
  124. bp2 = pixels[2], bp3 = pixels[3];
  125. uint8_t idx, i, offx;
  126. uint16_t color;
  127. for (i = 0; i < 8; i++) {
  128. idx = ((bp0 >> i) & 1) +
  129. (((bp1 >> i) & 1) << 1) +
  130. (((bp2 >> i) & 1) << 2) +
  131. (((bp3 >> i) & 1) << 3);
  132. color = vdp->cram[2 * idx] + (vdp->cram[2 * idx + 1] << 8);
  133. offx = hflip ? (col * 8 + (7 - i)) : (col * 8 + i);
  134. draw_pixel(vdp->v_counter, offx, color);
  135. }
  136. }
  137. }
  138. /*
  139. Draw sprites in the current scanline.
  140. */
  141. static void draw_sprites(VDP *vdp)
  142. {
  143. uint8_t *sat = vdp->vram + get_sat_base(vdp);
  144. uint8_t spritebuf[8], nsprites = 0, i;
  145. for (i = 0; i < 64; i++) {
  146. uint8_t y = sat[i] - 1;
  147. if (vdp->v_counter >= y && vdp->v_counter < y + 8) {
  148. DEBUG("sprite draw!!!")
  149. // TODO
  150. }
  151. }
  152. }
  153. /*
  154. Draw the current scanline.
  155. */
  156. static void draw_scanline(VDP *vdp)
  157. {
  158. draw_background(vdp);
  159. draw_sprites(vdp);
  160. }
  161. /*
  162. Advance the V counter for the next scanline.
  163. */
  164. static void advance_scanline(VDP *vdp)
  165. {
  166. if (vdp->v_counter == 0xDA)
  167. vdp->v_count_jump = !vdp->v_count_jump;
  168. if (vdp->v_counter == 0xDA && vdp->v_count_jump)
  169. vdp->v_counter = 0xD5;
  170. else
  171. vdp->v_counter++;
  172. }
  173. /*
  174. Simulate one line within the VDP.
  175. */
  176. void vdp_simulate_line(VDP *vdp)
  177. {
  178. if (vdp->v_counter >= 0x18 && vdp->v_counter < 0xA8)
  179. draw_scanline(vdp);
  180. if (vdp->v_counter == 0xC0)
  181. vdp->stat_int = true;
  182. advance_scanline(vdp);
  183. }
  184. /*
  185. Read a byte from the VDP's control port, revealing status flags.
  186. The status byte consists of:
  187. 7 6 5 4 3 2 1 0
  188. F 9S C * * * * *
  189. - F: Interrupt flag: set when the effective display area is completed
  190. - 9S: 9th sprite / Sprite overflow: more than eight sprites on a scanline
  191. - C: Collision flag: two sprites have an overlapping pixel
  192. - *: Unused
  193. The control flag is also reset.
  194. */
  195. uint8_t vdp_read_control(VDP *vdp)
  196. {
  197. uint8_t status =
  198. (vdp->stat_int << 8) + (vdp->stat_ovf << 7) + (vdp->stat_col << 6);
  199. vdp->stat_int = vdp->stat_ovf = vdp->stat_col = 0;
  200. vdp->control_flag = false;
  201. return status;
  202. }
  203. /*
  204. Read a byte from the VDP's data port.
  205. This will return the contents of the read buffer, and then fill the buffer
  206. with the VRAM at the current control address, before incrementing the
  207. control address. The control flag is also reset.
  208. */
  209. uint8_t vdp_read_data(VDP *vdp)
  210. {
  211. uint8_t buffer = vdp->read_buf;
  212. vdp->read_buf = vdp->vram[vdp->control_addr];
  213. vdp->control_addr = (vdp->control_addr + 1) % 0x3FFF;
  214. vdp->control_flag = false;
  215. return buffer;
  216. }
  217. /*
  218. Write a byte into the VDP's control port.
  219. Depending on the status of the control flag, this will either update the
  220. lower byte of the control address, or the upper six bits of the control
  221. address and the control code. The flag is toggled by each control write,
  222. and reset by each control read and data read or write.
  223. If the control code indicates a VRAM read, the read buffer will be filled
  224. with the VRAM at the given control address, which is then incremented. If
  225. the code indicates a register write, the corresponding register
  226. (byte & 0x0F) will be written with the lower byte of the control address.
  227. */
  228. void vdp_write_control(VDP *vdp, uint8_t byte)
  229. {
  230. if (!vdp->control_flag) { // First byte
  231. vdp->control_addr = (vdp->control_addr & 0x3F00) + byte;
  232. vdp->control_flag = true;
  233. return;
  234. }
  235. vdp->control_addr = ((byte & 0x3F) << 8) + (vdp->control_addr & 0xFF);
  236. vdp->control_code = byte >> 6;
  237. if (vdp->control_code == CODE_VRAM_READ) {
  238. vdp->read_buf = vdp->vram[vdp->control_addr];
  239. vdp->control_addr = (vdp->control_addr + 1) % 0x3FFF;
  240. } else if (vdp->control_code == CODE_REG_WRITE) {
  241. uint8_t reg = byte & 0x0F;
  242. if (reg <= VDP_REGS)
  243. vdp->regs[reg] = vdp->control_addr & 0xFF;
  244. }
  245. vdp->control_flag = false;
  246. }
  247. /*
  248. Write a byte into CRAM. Handles even/odd address latching.
  249. */
  250. static void write_cram(VDP *vdp, uint8_t byte)
  251. {
  252. if (!(vdp->control_addr % 2)) {
  253. vdp->cram_latch = byte;
  254. } else {
  255. vdp->cram[(vdp->control_addr - 1) % 0x3F] = vdp->cram_latch;
  256. vdp->cram[ vdp->control_addr % 0x3F] = byte % 0x0F;
  257. }
  258. }
  259. /*
  260. Write a byte into the VDP's data port.
  261. Depending on the control code, this either writes into the VRAM or CRAM at
  262. the current control address, which is then incremented. The control flag is
  263. also reset, and the read buffer is squashed.
  264. */
  265. void vdp_write_data(VDP *vdp, uint8_t byte)
  266. {
  267. if (vdp->control_code == CODE_CRAM_WRITE)
  268. write_cram(vdp, byte);
  269. else
  270. vdp->vram[vdp->control_addr] = byte;
  271. vdp->control_addr = (vdp->control_addr + 1) % 0x3FFF;
  272. vdp->control_flag = false;
  273. vdp->read_buf = byte;
  274. }
  275. /*
  276. Return whether the VDP is currently asserting an interrupt.
  277. */
  278. bool vdp_assert_irq(VDP *vdp)
  279. {
  280. // TODO: line interrupts
  281. return vdp->stat_int && should_frame_interrupt(vdp);
  282. }
  283. /*
  284. @DEBUG_LEVEL
  285. Print out all register values to stdout.
  286. */
  287. void vdp_dump_registers(const VDP *vdp)
  288. {
  289. const uint8_t *regs = vdp->regs;
  290. DEBUG("Dumping VDP register values:")
  291. // TODO: show flags
  292. DEBUG("- $00: 0x%02X (" BINARY_FMT ")", regs[0x00], BINARY_VAL(regs[0]))
  293. DEBUG("- $01: 0x%02X (" BINARY_FMT ")", regs[0x01], BINARY_VAL(regs[1]))
  294. DEBUG("- $02: 0x%02X (PNT: 0x%04X)", regs[0x02], get_pnt_base(vdp))
  295. DEBUG("- $03: 0x%02X (CT)", regs[0x03])
  296. DEBUG("- $04: 0x%02X (BPG)", regs[0x04])
  297. DEBUG("- $05: 0x%02X (SAT: 0x%04X)", regs[0x05], get_sat_base(vdp))
  298. DEBUG("- $06: 0x%02X (SGT: 0x%04X)", regs[0x06], get_sgt_base(vdp))
  299. DEBUG("- $07: 0x%02X (BDC: 0x%02X)", regs[0x07], get_backdrop_addr(vdp))
  300. DEBUG("- $08: 0x%02X (HS)", regs[0x08])
  301. DEBUG("- $09: 0x%02X (VS)", regs[0x09])
  302. DEBUG("- $0A: 0x%02X (LC)", regs[0x0A])
  303. // TODO: remove me!
  304. DEBUG("Dumping CRAM:")
  305. for (uint8_t i = 0x00; i < 0x40; i += 0x10) {
  306. uint16_t w[8];
  307. for (uint8_t j = 0; j < 8; j++)
  308. w[j] = vdp->cram[i + j * 2] + (vdp->cram[i + j * 2 + 1] << 8);
  309. DEBUG("- %04X %04X %04X %04X %04X %04X %04X %04X",
  310. w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7])
  311. }
  312. return;
  313. DEBUG("Dumping PNT:")
  314. for (uint16_t i = 0; i < 28 * 32; i += 32) {
  315. uint16_t w[32];
  316. for (uint8_t j = 0; j < 32; j++)
  317. w[j] = vdp->vram[get_pnt_base(vdp) + 2 * (i + j)] +
  318. (vdp->vram[get_pnt_base(vdp) + 2 * (i + j) + 1] << 8);
  319. DEBUG("- %03X %03X %03X %03X %03X %03X %03X %03X"
  320. " %03X %03X %03X %03X %03X %03X %03X %03X"
  321. " %03X %03X %03X %03X %03X %03X %03X %03X"
  322. " %03X %03X %03X %03X %03X %03X %03X %03X",
  323. w[0x00], w[0x01], w[0x02], w[0x03], w[0x04], w[0x05], w[0x06], w[0x07],
  324. w[0x08], w[0x09], w[0x0A], w[0x0B], w[0x0C], w[0x0D], w[0x0E], w[0x0F],
  325. w[0x10], w[0x11], w[0x12], w[0x13], w[0x14], w[0x15], w[0x16], w[0x17],
  326. w[0x18], w[0x19], w[0x1A], w[0x1B], w[0x1C], w[0x1D], w[0x1E], w[0x1F])
  327. }
  328. DEBUG("Dumping PGT:")
  329. for (uint16_t i = 0; i < /* 512 */ 16; i++) {
  330. uint32_t w[8];
  331. for (uint8_t j = 0; j < 8; j++)
  332. w[j] = vdp->vram[32 * i + 4 * j] +
  333. (vdp->vram[32 * i + 4 * j + 1] << 8) +
  334. (vdp->vram[32 * i + 4 * j + 2] << 16) +
  335. (vdp->vram[32 * i + 4 * j + 3] << 24);
  336. DEBUG("- 0x%04X: %08X %08X %08X %08X %08X %08X %08X %08X", i,
  337. w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7])
  338. }
  339. }