Mercurial > repos > blastem
comparison vdp.c @ 451:b7c3b2d22858
Added support for saving savestates. Added gst savestate format test harness
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 26 Jul 2013 19:55:04 -0700 |
parents | b3cee2fe690b |
children | 608815ab4ff2 |
comparison
equal
deleted
inserted
replaced
448:e85a107e6ec0 | 451:b7c3b2d22858 |
---|---|
123 | 123 |
124 void render_sprite_cells(vdp_context * context) | 124 void render_sprite_cells(vdp_context * context) |
125 { | 125 { |
126 if (context->cur_slot >= context->sprite_draws) { | 126 if (context->cur_slot >= context->sprite_draws) { |
127 sprite_draw * d = context->sprite_draw_list + context->cur_slot; | 127 sprite_draw * d = context->sprite_draw_list + context->cur_slot; |
128 | 128 |
129 uint16_t dir; | 129 uint16_t dir; |
130 int16_t x; | 130 int16_t x; |
131 if (d->h_flip) { | 131 if (d->h_flip) { |
132 x = d->x_pos + 7; | 132 x = d->x_pos + 7; |
133 dir = -1; | 133 dir = -1; |
162 int16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & 0x1FF; | 162 int16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & 0x1FF; |
163 int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF; | 163 int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF; |
164 uint16_t link = context->vdpmem[address+3] & 0x7F; | 164 uint16_t link = context->vdpmem[address+3] & 0x7F; |
165 uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; | 165 uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; |
166 uint8_t pri = context->vdpmem[address + 4] >> 7; | 166 uint8_t pri = context->vdpmem[address + 4] >> 7; |
167 uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; | 167 uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; |
168 //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); | 168 //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); |
169 current_index = link; | 169 current_index = link; |
170 count++; | 170 count++; |
171 } while (current_index != 0 && count < 80); | 171 } while (current_index != 0 && count < 80); |
172 } | 172 } |
177 printf("**Mode Group**\n" | 177 printf("**Mode Group**\n" |
178 "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n" | 178 "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n" |
179 "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" | 179 "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" |
180 "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" | 180 "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" |
181 "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", | 181 "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", |
182 context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, | 182 context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, |
183 context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", | 183 context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", |
184 context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", | 184 context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", |
185 context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, | 185 context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, |
186 context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", | 186 context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", |
187 hscroll[context->regs[REG_MODE_3] & 0x3], | 187 hscroll[context->regs[REG_MODE_3] & 0x3], |
188 context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled"); | 188 context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled"); |
189 printf("\n**Table Group**\n" | 189 printf("\n**Table Group**\n" |
201 printf("\n**Misc Group**\n" | 201 printf("\n**Misc Group**\n" |
202 "07: %.2X | Backdrop Color: $%X\n" | 202 "07: %.2X | Backdrop Color: $%X\n" |
203 "0A: %.2X | H-Int Counter: %u\n" | 203 "0A: %.2X | H-Int Counter: %u\n" |
204 "0F: %.2X | Auto-increment: $%X\n" | 204 "0F: %.2X | Auto-increment: $%X\n" |
205 "10: %.2X | Scroll A/B Size: %sx%s\n", | 205 "10: %.2X | Scroll A/B Size: %sx%s\n", |
206 context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, | 206 context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, |
207 context->regs[REG_HINT], context->regs[REG_HINT], | 207 context->regs[REG_HINT], context->regs[REG_HINT], |
208 context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], | 208 context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], |
209 context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); | 209 context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); |
210 printf("\n**Internal Group**\n" | 210 printf("\n**Internal Group**\n" |
211 "Address: %X\n" | 211 "Address: %X\n" |
212 "CD: %X\n" | 212 "CD: %X\n" |
213 "Pending: %s\n", | 213 "Pending: %s\n", |
214 context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); | 214 context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); |
215 | 215 |
216 //TODO: Window Group, DMA Group | 216 //TODO: Window Group, DMA Group |
217 } | 217 } |
218 | 218 |
219 void scan_sprite_table(uint32_t line, vdp_context * context) | 219 void scan_sprite_table(uint32_t line, vdp_context * context) |
220 { | 220 { |
221 if (context->sprite_index && context->slot_counter) { | 221 if (context->sprite_index && context->slot_counter) { |
293 line++; | 293 line++; |
294 } | 294 } |
295 height *= 2; | 295 height *= 2; |
296 } | 296 } |
297 uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; | 297 uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; |
298 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; | 298 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; |
299 uint8_t pal_priority = (tileinfo >> 9) & 0x70; | 299 uint8_t pal_priority = (tileinfo >> 9) & 0x70; |
300 uint8_t row; | 300 uint8_t row; |
301 if (tileinfo & MAP_BIT_V_FLIP) { | 301 if (tileinfo & MAP_BIT_V_FLIP) { |
302 row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line; | 302 row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line; |
303 } else { | 303 } else { |
313 if (x) { | 313 if (x) { |
314 context->flags |= FLAG_CAN_MASK; | 314 context->flags |= FLAG_CAN_MASK; |
315 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { | 315 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { |
316 context->flags |= FLAG_MASKED; | 316 context->flags |= FLAG_MASKED; |
317 } | 317 } |
318 | 318 |
319 context->flags &= ~FLAG_DOT_OFLOW; | 319 context->flags &= ~FLAG_DOT_OFLOW; |
320 int16_t i; | 320 int16_t i; |
321 if (context->flags & FLAG_MASKED) { | 321 if (context->flags & FLAG_MASKED) { |
322 for (i=0; i < width && context->sprite_draws; i++) { | 322 for (i=0; i < width && context->sprite_draws; i++) { |
323 --context->sprite_draws; | 323 --context->sprite_draws; |
413 //Charles MacDonald's VDP doc says that the low byte gets written first | 413 //Charles MacDonald's VDP doc says that the low byte gets written first |
414 context->vdpmem[context->address] = context->dma_val; | 414 context->vdpmem[context->address] = context->dma_val; |
415 context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); | 415 context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); |
416 break; | 416 break; |
417 case CRAM_WRITE: | 417 case CRAM_WRITE: |
418 write_cram(context, context->address, context->dma_val); | 418 write_cram(context, context->address, context->dma_val); |
419 //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); | 419 //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); |
420 break; | 420 break; |
421 case VSRAM_WRITE: | 421 case VSRAM_WRITE: |
422 if (((context->address/2) & 63) < VSRAM_SIZE) { | 422 if (((context->address/2) & 63) < VSRAM_SIZE) { |
423 context->vsram[(context->address/2) & 63] = context->dma_val; | 423 context->vsram[(context->address/2) & 63] = context->dma_val; |
573 uint16_t line_offset, offset, mask; | 573 uint16_t line_offset, offset, mask; |
574 if (context->latched_mode & BIT_H40) { | 574 if (context->latched_mode & BIT_H40) { |
575 address &= 0xF000; | 575 address &= 0xF000; |
576 line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; | 576 line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; |
577 mask = 0x7F; | 577 mask = 0x7F; |
578 | 578 |
579 } else { | 579 } else { |
580 address &= 0xF800; | 580 address &= 0xF800; |
581 line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; | 581 line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; |
582 mask = 0x3F; | 582 mask = 0x3F; |
583 } | 583 } |
749 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); | 749 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); |
750 a_src = DBG_SRC_A; | 750 a_src = DBG_SRC_A; |
751 } | 751 } |
752 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); | 752 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); |
753 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); | 753 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); |
754 | 754 |
755 if (context->regs[REG_MODE_4] & BIT_HILIGHT) { | 755 if (context->regs[REG_MODE_4] & BIT_HILIGHT) { |
756 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { | 756 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { |
757 uint8_t pixel; | 757 uint8_t pixel; |
758 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); | 758 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); |
759 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); | 759 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); |
1400 inccycles = MCLKS_SLOT_H32; | 1400 inccycles = MCLKS_SLOT_H32; |
1401 slot = linecyc/MCLKS_SLOT_H32; | 1401 slot = linecyc/MCLKS_SLOT_H32; |
1402 } | 1402 } |
1403 if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) { | 1403 if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) { |
1404 //first sort-of active line is treated as 255 internally | 1404 //first sort-of active line is treated as 255 internally |
1405 //it's used for gathering sprite info for line | 1405 //it's used for gathering sprite info for line |
1406 line = (line - 1) & 0xFF; | 1406 line = (line - 1) & 0xFF; |
1407 | 1407 |
1408 //Convert to slot number | 1408 //Convert to slot number |
1409 if (context->latched_mode & BIT_H40){ | 1409 if (context->latched_mode & BIT_H40){ |
1410 vdp_h40(line, slot, context); | 1410 vdp_h40(line, slot, context); |
1411 } else { | 1411 } else { |
1412 vdp_h32(line, slot, context); | 1412 vdp_h32(line, slot, context); |
1787 } else if(int_num ==4) { | 1787 } else if(int_num ==4) { |
1788 context->flags2 &= ~FLAG2_HINT_PENDING; | 1788 context->flags2 &= ~FLAG2_HINT_PENDING; |
1789 } | 1789 } |
1790 } | 1790 } |
1791 | 1791 |
1792 #define GST_VDP_REGS 0xFA | |
1793 #define GST_VDP_MEM 0x12478 | |
1794 | |
1795 uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) | |
1796 { | |
1797 uint8_t tmp_buf[CRAM_SIZE*2]; | |
1798 fseek(state_file, GST_VDP_REGS, SEEK_SET); | |
1799 if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { | |
1800 fputs("Failed to read VDP registers from savestate\n", stderr); | |
1801 return 0; | |
1802 } | |
1803 context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); | |
1804 if (!context->double_res) { | |
1805 context->framebuf = context->oddbuf; | |
1806 } | |
1807 latch_mode(context); | |
1808 if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { | |
1809 fputs("Failed to read CRAM from savestate\n", stderr); | |
1810 return 0; | |
1811 } | |
1812 for (int i = 0; i < CRAM_SIZE; i++) { | |
1813 uint16_t value; | |
1814 context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; | |
1815 context->colors[i] = color_map[value & 0xEEE]; | |
1816 context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; | |
1817 context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; | |
1818 } | |
1819 if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { | |
1820 fputs("Failed to read VSRAM from savestate\n", stderr); | |
1821 return 0; | |
1822 } | |
1823 for (int i = 0; i < VSRAM_SIZE; i++) { | |
1824 context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; | |
1825 } | |
1826 fseek(state_file, GST_VDP_MEM, SEEK_SET); | |
1827 if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { | |
1828 fputs("Failed to read VRAM from savestate\n", stderr); | |
1829 return 0; | |
1830 } | |
1831 return 1; | |
1832 } | |
1833 | |
1834 void vdp_save_state(vdp_context * context, FILE * outfile) | |
1835 { | |
1836 uint8_t tmp_buf[CRAM_SIZE*2]; | |
1837 fseek(outfile, GST_VDP_REGS, SEEK_SET); | |
1838 fwrite(context->regs, 1, VDP_REGS, outfile); | |
1839 for (int i = 0; i < CRAM_SIZE; i++) { | |
1840 tmp_buf[i*2] = context->cram[i]; | |
1841 tmp_buf[i*2+1] = context->cram[i] >> 8; | |
1842 } | |
1843 fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile); | |
1844 for (int i = 0; i < VSRAM_SIZE; i++) { | |
1845 tmp_buf[i*2] = context->vsram[i]; | |
1846 tmp_buf[i*2+1] = context->vsram[i] >> 8; | |
1847 } | |
1848 fwrite(tmp_buf, 2, VSRAM_SIZE, outfile); | |
1849 fseek(outfile, GST_VDP_MEM, SEEK_SET); | |
1850 fwrite(context->vdpmem, 1, VRAM_SIZE, outfile); | |
1851 } | |
1852 |