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.
 
 
 
 
 

527 lines
14 KiB

  1. /* Copyright (C) 2014-2017 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 FLAG_CONTROL 0x01
  8. #define FLAG_FRAME_INT 0x02
  9. #define FLAG_LINE_INT 0x04
  10. #define FLAG_SPR_OVF 0x08
  11. #define FLAG_SPR_COL 0x10
  12. #define CODE_VRAM_READ 0
  13. #define CODE_VRAM_WRITE 1
  14. #define CODE_REG_WRITE 2
  15. #define CODE_CRAM_WRITE 3
  16. #define COLBUF_BG_PRIORITY 0x10
  17. #define COLBUF_OPAQUE_SPRITE 0x20
  18. /*
  19. Initialize the Video Display Processor (VDP).
  20. The VDP will write to its pixels array whenever it draws a scanline. It
  21. defaults to NULL, but you should set it to something if you want to see its
  22. output.
  23. */
  24. void vdp_init(VDP *vdp)
  25. {
  26. vdp->pixels = NULL;
  27. vdp->vram = cr_malloc(sizeof(uint8_t) * VDP_VRAM_SIZE);
  28. vdp->cram = cr_malloc(sizeof(uint8_t) * VDP_CRAM_SIZE);
  29. }
  30. /*
  31. Free memory previously allocated by the VDP.
  32. */
  33. void vdp_free(VDP *vdp)
  34. {
  35. free(vdp->vram);
  36. free(vdp->cram);
  37. }
  38. /*
  39. Power on the VDP, setting up initial state.
  40. */
  41. void vdp_power(VDP *vdp)
  42. {
  43. memset(vdp->vram, 0x00, VDP_VRAM_SIZE);
  44. memset(vdp->cram, 0x00, VDP_CRAM_SIZE);
  45. vdp->regs[0x00] = 0x00;
  46. vdp->regs[0x01] = 0x00;
  47. vdp->regs[0x02] = 0xFF;
  48. vdp->regs[0x03] = 0xFF;
  49. vdp->regs[0x04] = 0xFF;
  50. vdp->regs[0x05] = 0xFF;
  51. vdp->regs[0x06] = 0xFF;
  52. vdp->regs[0x07] = 0x00;
  53. vdp->regs[0x08] = 0x00;
  54. vdp->regs[0x09] = 0x00;
  55. vdp->regs[0x0A] = 0x01;
  56. vdp->h_counter = 0;
  57. vdp->v_counter = 0;
  58. vdp->v_count_jump = false;
  59. vdp->flags = 0;
  60. vdp->control_code = 0;
  61. vdp->control_addr = 0;
  62. vdp->line_count = 0x01;
  63. vdp->read_buf = 0;
  64. vdp->cram_latch = 0;
  65. }
  66. /*
  67. Return whether line-completion interrupts are enabled.
  68. */
  69. static bool should_line_interrupt(const VDP *vdp)
  70. {
  71. return vdp->regs[0x00] & 0x10;
  72. }
  73. /*
  74. Return whether frame-completion interrupts are enabled.
  75. */
  76. static bool should_frame_interrupt(const VDP *vdp)
  77. {
  78. return vdp->regs[0x01] & 0x20;
  79. }
  80. /*
  81. Return whether the display is on or if the backdrop color should be used.
  82. */
  83. static bool is_display_visible(const VDP *vdp)
  84. {
  85. return vdp->regs[0x01] & 0x40;
  86. }
  87. /*
  88. Get the height of all sprites in patterns (1 pattern = 8x8 pixels).
  89. */
  90. static uint8_t get_sprite_height(const VDP *vdp)
  91. {
  92. return (vdp->regs[0x01] & 0x02) ? 2 : 1;
  93. }
  94. /*
  95. Return the base address of the pattern name table.
  96. */
  97. static uint16_t get_pnt_base(const VDP *vdp)
  98. {
  99. return (vdp->regs[0x02] & 0x0E) << 10;
  100. }
  101. /*
  102. Return the base address of the sprite attribute table.
  103. */
  104. static uint16_t get_sat_base(const VDP *vdp)
  105. {
  106. return (vdp->regs[0x05] & 0x7E) << 7;
  107. }
  108. /*
  109. Return the global offset of the sprite generator table.
  110. */
  111. static uint16_t get_sgt_offset(const VDP *vdp)
  112. {
  113. return (vdp->regs[0x06] & 0x04) << 6;
  114. }
  115. /*
  116. Return the backdrop color as a CRAM index (in palette 1).
  117. */
  118. static uint8_t get_backdrop_color(const VDP *vdp)
  119. {
  120. return vdp->regs[0x07] & 0x0F;
  121. }
  122. /*
  123. Return the horizontal background scroll value.
  124. */
  125. static uint8_t get_bg_hscroll(const VDP *vdp)
  126. {
  127. return vdp->regs[0x08];
  128. }
  129. /*
  130. Return the vertical background scroll value.
  131. */
  132. static uint8_t get_bg_vscroll(const VDP *vdp)
  133. {
  134. return vdp->regs[0x09];
  135. }
  136. /*
  137. Return the packed background tile at the given row and column.
  138. */
  139. static uint16_t get_background_tile(const VDP *vdp, uint8_t row, uint8_t col)
  140. {
  141. uint8_t *pnt = vdp->vram + get_pnt_base(vdp);
  142. uint16_t index = row * 32 + col;
  143. return pnt[2 * index] + (pnt[2 * index + 1] << 8);
  144. }
  145. /*
  146. Get the CRAM color index of the given (row, col) in the given pattern.
  147. */
  148. static uint8_t read_pattern(const VDP *vdp, uint16_t pattern,
  149. uint8_t row, uint8_t col)
  150. {
  151. uint8_t *planes = &vdp->vram[32 * pattern + 4 * row];
  152. return ((planes[0] >> (7 - col)) & 1) +
  153. (((planes[1] >> (7 - col)) & 1) << 1) +
  154. (((planes[2] >> (7 - col)) & 1) << 2) +
  155. (((planes[3] >> (7 - col)) & 1) << 3);
  156. }
  157. /*
  158. Return the BGR444 color at the given CRAM index.
  159. The index should be between 0 and 15, as there are 16 colors per palette.
  160. */
  161. static uint16_t get_color(const VDP *vdp, uint8_t index, bool palette)
  162. {
  163. uint8_t offset = 2 * (index + 16 * palette);
  164. return vdp->cram[offset] + (vdp->cram[offset + 1] << 8);
  165. }
  166. /*
  167. Draw a pixel onto our pixel array at the given coordinates.
  168. The color should be in BGR444 format, as returned by get_color().
  169. */
  170. static void draw_pixel(VDP *vdp, uint8_t y, uint8_t x, uint16_t color)
  171. {
  172. uint8_t r = 0x11 * (color & 0x000F);
  173. uint8_t g = 0x11 * ((color & 0x00F0) >> 4);
  174. uint8_t b = 0x11 * ((color & 0x0F00) >> 8);
  175. uint32_t argb = (0xFF << 24) + (r << 16) + (g << 8) + b;
  176. vdp->pixels[y * 160 + x] = argb;
  177. }
  178. /*
  179. Draw the background of the current scanline.
  180. */
  181. static void draw_background(VDP *vdp, uint8_t *colbuf)
  182. {
  183. uint8_t src_row = (vdp->v_counter + get_bg_vscroll(vdp)) % (28 << 3);
  184. uint8_t dst_row = vdp->v_counter - 0x18;
  185. uint8_t vcell = src_row >> 3;
  186. uint8_t hcell, col;
  187. uint8_t start_col = get_bg_hscroll(vdp) >> 3;
  188. uint8_t fine_scroll = get_bg_hscroll(vdp) % 8;
  189. for (col = 5; col < 20 + 6; col++) {
  190. hcell = (32 - start_col + col) % 32;
  191. uint16_t tile = get_background_tile(vdp, vcell, hcell);
  192. uint16_t pattern = tile & 0x01FF;
  193. bool palette = tile & 0x0800;
  194. bool priority = tile & 0x1000;
  195. bool vflip = tile & 0x0400;
  196. bool hflip = tile & 0x0200;
  197. uint8_t vshift = vflip ? (7 - src_row % 8) : (src_row % 8), hshift;
  198. uint8_t pixel, index;
  199. int16_t dst_col;
  200. uint16_t color;
  201. for (pixel = 0; pixel < 8; pixel++) {
  202. dst_col = ((col - 6) << 3) + pixel + fine_scroll;
  203. if (dst_col < 0 || dst_col >= 160)
  204. continue;
  205. hshift = hflip ? (7 - pixel) : pixel;
  206. index = read_pattern(vdp, pattern, vshift, hshift);
  207. if (is_display_visible(vdp))
  208. color = get_color(vdp, index, palette);
  209. else
  210. color = get_color(vdp, get_backdrop_color(vdp), 1);
  211. draw_pixel(vdp, dst_row, dst_col, color);
  212. if (priority && index != 0)
  213. colbuf[dst_col] |= COLBUF_BG_PRIORITY;
  214. }
  215. }
  216. }
  217. /*
  218. Draw sprites in the current scanline.
  219. */
  220. static void draw_sprites(VDP *vdp, uint8_t *colbuf)
  221. {
  222. uint8_t *sat = vdp->vram + get_sat_base(vdp);
  223. uint8_t spritebuf[8], nsprites = 0, i;
  224. uint8_t height = get_sprite_height(vdp);
  225. for (i = 0; i < 64; i++) {
  226. uint8_t y = sat[i] + 1;
  227. if (y == 0xD0 + 1)
  228. break;
  229. if (vdp->v_counter >= y && vdp->v_counter < y + (height * 8)) {
  230. if (nsprites >= 8) {
  231. vdp->flags |= FLAG_SPR_OVF;
  232. break;
  233. }
  234. spritebuf[nsprites++] = i;
  235. }
  236. }
  237. uint8_t dst_row = vdp->v_counter - 0x18;
  238. while (nsprites-- > 0) {
  239. i = spritebuf[nsprites];
  240. uint8_t y = sat[i] + 1;
  241. uint8_t x = sat[0x80 + 2 * i];
  242. uint8_t vshift;
  243. uint16_t pattern;
  244. if (height == 1) {
  245. pattern = get_sgt_offset(vdp) + sat[0x80 + 2 * i + 1];
  246. vshift = vdp->v_counter - y;
  247. } else if (height == 2) {
  248. pattern = (get_sgt_offset(vdp) + sat[0x80 + 2 * i + 1]) & 0x1FE;
  249. pattern |= (vdp->v_counter - y) >> 3;
  250. vshift = (vdp->v_counter - y) % 8;
  251. } else {
  252. // TODO: sprite doubling
  253. }
  254. uint8_t pixel, index;
  255. uint16_t color;
  256. int16_t dst_col;
  257. for (pixel = 0; pixel < 8; pixel++) {
  258. dst_col = x + pixel - (6 << 3);
  259. if (dst_col < 0 || dst_col >= 160)
  260. continue;
  261. if (colbuf[dst_col] & COLBUF_BG_PRIORITY)
  262. continue;
  263. index = read_pattern(vdp, pattern, vshift, pixel);
  264. if (index == 0)
  265. continue;
  266. if (colbuf[dst_col] & COLBUF_OPAQUE_SPRITE)
  267. vdp->flags |= FLAG_SPR_COL;
  268. else
  269. colbuf[dst_col] |= COLBUF_OPAQUE_SPRITE;
  270. if (is_display_visible(vdp)) {
  271. color = get_color(vdp, index, 1);
  272. draw_pixel(vdp, dst_row, dst_col, color);
  273. }
  274. }
  275. }
  276. }
  277. /*
  278. Draw the current scanline.
  279. */
  280. static void draw_scanline(VDP *vdp)
  281. {
  282. if (!vdp->pixels)
  283. return;
  284. uint8_t colbuf[160] = {0x00};
  285. draw_background(vdp, colbuf);
  286. draw_sprites(vdp, colbuf);
  287. }
  288. /*
  289. Update the line counter, which triggers line interrupts.
  290. */
  291. static void update_line_counter(VDP *vdp)
  292. {
  293. if (vdp->v_counter < 0xC0) {
  294. if (vdp->line_count == 0x00) {
  295. vdp->flags |= FLAG_LINE_INT;
  296. vdp->line_count = vdp->regs[0x0A];
  297. } else {
  298. vdp->line_count--;
  299. }
  300. } else {
  301. vdp->line_count = vdp->regs[0x0A];
  302. }
  303. }
  304. /*
  305. Advance the V counter for the next scanline.
  306. */
  307. static void advance_scanline(VDP *vdp)
  308. {
  309. if (vdp->v_counter == 0xDA)
  310. vdp->v_count_jump = !vdp->v_count_jump;
  311. if (vdp->v_counter == 0xDA && vdp->v_count_jump)
  312. vdp->v_counter = 0xD5;
  313. else
  314. vdp->v_counter++;
  315. }
  316. /*
  317. Simulate one line within the VDP.
  318. */
  319. void vdp_simulate_line(VDP *vdp)
  320. {
  321. if (vdp->v_counter >= 0x18 && vdp->v_counter < 0xA8)
  322. draw_scanline(vdp);
  323. if (vdp->v_counter == 0xC0)
  324. vdp->flags |= FLAG_FRAME_INT;
  325. update_line_counter(vdp);
  326. advance_scanline(vdp);
  327. }
  328. /*
  329. Read a byte from the VDP's control port, revealing status flags.
  330. The status byte consists of:
  331. 7 6 5 4 3 2 1 0
  332. F 9S C * * * * *
  333. - F: Frame interrupt: set when the effective display area is completed
  334. - 9S: 9th sprite / Sprite overflow: more than eight sprites on a scanline
  335. - C: Sprite collision: two sprites have an overlapping pixel
  336. - *: Unused
  337. The control and line interrupt flags are also reset.
  338. */
  339. uint8_t vdp_read_control(VDP *vdp)
  340. {
  341. uint8_t status =
  342. (!!(vdp->flags & FLAG_FRAME_INT) << 7) +
  343. (!!(vdp->flags & FLAG_SPR_OVF) << 6) +
  344. (!!(vdp->flags & FLAG_SPR_COL) << 5);
  345. vdp->flags = 0;
  346. return status;
  347. }
  348. /*
  349. Read a byte from the VDP's data port.
  350. This will return the contents of the read buffer, and then fill the buffer
  351. with the VRAM at the current control address, before incrementing the
  352. control address. The control flag is also reset.
  353. */
  354. uint8_t vdp_read_data(VDP *vdp)
  355. {
  356. uint8_t buffer = vdp->read_buf;
  357. vdp->read_buf = vdp->vram[vdp->control_addr];
  358. vdp->control_addr = (vdp->control_addr + 1) & 0x3FFF;
  359. vdp->flags &= ~FLAG_CONTROL;
  360. return buffer;
  361. }
  362. /*
  363. Set the given VDP register.
  364. */
  365. static void write_reg(VDP *vdp, uint8_t reg, uint8_t byte)
  366. {
  367. vdp->regs[reg] = byte;
  368. }
  369. /*
  370. Write a byte into the VDP's control port.
  371. Depending on the status of the control flag, this will either update the
  372. lower byte of the control address, or the upper six bits of the control
  373. address and the control code. The flag is toggled by each control write,
  374. and reset by each control read and data read or write.
  375. If the control code indicates a VRAM read, the read buffer will be filled
  376. with the VRAM at the given control address, which is then incremented. If
  377. the code indicates a register write, the corresponding register
  378. (byte & 0x0F) will be written with the lower byte of the control address.
  379. */
  380. void vdp_write_control(VDP *vdp, uint8_t byte)
  381. {
  382. vdp->flags ^= FLAG_CONTROL;
  383. if (vdp->flags & FLAG_CONTROL) { // First byte
  384. vdp->control_addr = (vdp->control_addr & 0x3F00) + byte;
  385. return;
  386. }
  387. vdp->control_addr = ((byte & 0x3F) << 8) + (vdp->control_addr & 0xFF);
  388. vdp->control_code = byte >> 6;
  389. if (vdp->control_code == CODE_VRAM_READ) {
  390. vdp->read_buf = vdp->vram[vdp->control_addr];
  391. vdp->control_addr = (vdp->control_addr + 1) & 0x3FFF;
  392. } else if (vdp->control_code == CODE_REG_WRITE) {
  393. uint8_t reg = byte & 0x0F;
  394. if (reg <= VDP_REGS)
  395. write_reg(vdp, reg, vdp->control_addr & 0xFF);
  396. }
  397. }
  398. /*
  399. Write a byte into CRAM. Handles even/odd address latching.
  400. */
  401. static void write_cram(VDP *vdp, uint8_t byte)
  402. {
  403. if (!(vdp->control_addr % 2)) {
  404. vdp->cram_latch = byte;
  405. } else {
  406. vdp->cram[(vdp->control_addr - 1) & 0x3F] = vdp->cram_latch;
  407. vdp->cram[ vdp->control_addr & 0x3F] = byte & 0x0F;
  408. }
  409. }
  410. /*
  411. Write a byte into the VDP's data port.
  412. Depending on the control code, this either writes into the VRAM or CRAM at
  413. the current control address, which is then incremented. The control flag is
  414. also reset, and the read buffer is squashed.
  415. */
  416. void vdp_write_data(VDP *vdp, uint8_t byte)
  417. {
  418. if (vdp->control_code == CODE_CRAM_WRITE)
  419. write_cram(vdp, byte);
  420. else
  421. vdp->vram[vdp->control_addr] = byte;
  422. vdp->control_addr = (vdp->control_addr + 1) & 0x3FFF;
  423. vdp->flags &= ~FLAG_CONTROL;
  424. vdp->read_buf = byte;
  425. }
  426. /*
  427. Return whether the VDP is currently asserting an interrupt.
  428. */
  429. bool vdp_assert_irq(VDP *vdp)
  430. {
  431. return (vdp->flags & FLAG_FRAME_INT && should_frame_interrupt(vdp)) ||
  432. (vdp->flags & FLAG_LINE_INT && should_line_interrupt(vdp));
  433. }
  434. /*
  435. @DEBUG_LEVEL
  436. Print out all register values to stdout.
  437. */
  438. void vdp_dump_registers(const VDP *vdp)
  439. {
  440. const uint8_t *regs = vdp->regs;
  441. DEBUG("Dumping VDP register values:")
  442. // TODO: show flags
  443. DEBUG("- $00: 0x%02X (" BINARY_FMT ")", regs[0x00], BINARY_VAL(regs[0]))
  444. DEBUG("- $01: 0x%02X (" BINARY_FMT ")", regs[0x01], BINARY_VAL(regs[1]))
  445. DEBUG("- $02: 0x%02X (PNT: 0x%04X)", regs[0x02], get_pnt_base(vdp))
  446. DEBUG("- $03: 0x%02X (CT)", regs[0x03])
  447. DEBUG("- $04: 0x%02X (BPG)", regs[0x04])
  448. DEBUG("- $05: 0x%02X (SAT: 0x%04X)", regs[0x05], get_sat_base(vdp))
  449. DEBUG("- $06: 0x%02X (SGT: 0x%04X)", regs[0x06], get_sgt_offset(vdp) << 5)
  450. DEBUG("- $07: 0x%02X (BDC: 0x%02X)", regs[0x07], get_backdrop_color(vdp))
  451. DEBUG("- $08: 0x%02X (HS)", regs[0x08])
  452. DEBUG("- $09: 0x%02X (VS)", regs[0x09])
  453. DEBUG("- $0A: 0x%02X (LC)", regs[0x0A])
  454. }