Mercurial > repos > blastem
comparison vdp.c @ 1120:e9369d6f0101
Somewhat broken implementation of Mode 4
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 27 Dec 2016 11:31:17 -0800 |
parents | 928a65750345 |
children | 1913f9c28003 |
comparison
equal
deleted
inserted
replaced
1119:55ea7f9a4e92 | 1120:e9369d6f0101 |
---|---|
11 #include "render.h" | 11 #include "render.h" |
12 #include "util.h" | 12 #include "util.h" |
13 | 13 |
14 #define NTSC_INACTIVE_START 224 | 14 #define NTSC_INACTIVE_START 224 |
15 #define PAL_INACTIVE_START 240 | 15 #define PAL_INACTIVE_START 240 |
16 #define MODE4_INACTIVE_START 192 | |
16 #define BUF_BIT_PRIORITY 0x40 | 17 #define BUF_BIT_PRIORITY 0x40 |
17 #define MAP_BIT_PRIORITY 0x8000 | 18 #define MAP_BIT_PRIORITY 0x8000 |
18 #define MAP_BIT_H_FLIP 0x800 | 19 #define MAP_BIT_H_FLIP 0x800 |
19 #define MAP_BIT_V_FLIP 0x1000 | 20 #define MAP_BIT_V_FLIP 0x1000 |
20 | 21 |
37 #define LINE_CHANGE_H32 132 | 38 #define LINE_CHANGE_H32 132 |
38 #define VBLANK_START_H40 (LINE_CHANGE_H40+2) | 39 #define VBLANK_START_H40 (LINE_CHANGE_H40+2) |
39 #define VBLANK_START_H32 (LINE_CHANGE_H32+2) | 40 #define VBLANK_START_H32 (LINE_CHANGE_H32+2) |
40 #define FIFO_LATENCY 3 | 41 #define FIFO_LATENCY 3 |
41 | 42 |
42 int32_t color_map[1 << 12]; | 43 static int32_t color_map[1 << 12]; |
44 static uint16_t mode4_address_map[0x4000]; | |
45 static uint32_t planar_to_chunky[256]; | |
43 static uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; | 46 static uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; |
44 | 47 |
45 static uint8_t debug_base[][3] = { | 48 static uint8_t debug_base[][3] = { |
46 {127, 127, 127}, //BG | 49 {127, 127, 127}, //BG |
47 {0, 0, 127}, //A | 50 {0, 0, 127}, //A |
83 r = levels[(color >> 1) & 0x7]; | 86 r = levels[(color >> 1) & 0x7]; |
84 } else if(color & FBUF_HILIGHT) { | 87 } else if(color & FBUF_HILIGHT) { |
85 b = levels[((color >> 9) & 0x7) + 7]; | 88 b = levels[((color >> 9) & 0x7) + 7]; |
86 g = levels[((color >> 5) & 0x7) + 7]; | 89 g = levels[((color >> 5) & 0x7) + 7]; |
87 r = levels[((color >> 1) & 0x7) + 7]; | 90 r = levels[((color >> 1) & 0x7) + 7]; |
91 } else if(color & FBUF_MODE4) { | |
92 b = levels[color >> 3 & 0xC]; | |
93 g = levels[(color >> 2 & 0x8) | (color >> 1 & 0x4)]; | |
94 r = levels[color << 1 & 0xC]; | |
88 } else { | 95 } else { |
89 b = levels[(color >> 8) & 0xE]; | 96 b = levels[(color >> 8) & 0xE]; |
90 g = levels[(color >> 4) & 0xE]; | 97 g = levels[(color >> 4) & 0xE]; |
91 r = levels[color & 0xE]; | 98 r = levels[color & 0xE]; |
92 } | 99 } |
93 color_map[color] = render_map_color(r, g, b); | 100 color_map[color] = render_map_color(r, g, b); |
101 } | |
102 for (uint16_t mode4_addr = 0; mode4_addr < 0x4000; mode4_addr++) | |
103 { | |
104 uint16_t mode5_addr = mode4_addr & 0x3DFD; | |
105 mode5_addr |= mode4_addr << 8 & 0x200; | |
106 mode5_addr |= mode4_addr >> 8 & 2; | |
107 mode4_address_map[mode4_addr] = mode5_addr; | |
108 } | |
109 for (uint32_t planar = 0; planar < 256; planar++) | |
110 { | |
111 uint32_t chunky = 0; | |
112 for (int bit = 7; bit >= 0; bit--) | |
113 { | |
114 chunky = chunky << 4; | |
115 chunky |= planar >> bit & 1; | |
116 } | |
117 planar_to_chunky[planar] = chunky; | |
94 } | 118 } |
95 color_map_init_done = 1; | 119 color_map_init_done = 1; |
96 } | 120 } |
97 for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++) | 121 for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++) |
98 { | 122 { |
192 context->flags2 |= FLAG2_SPRITE_COLLIDE; | 216 context->flags2 |= FLAG2_SPRITE_COLLIDE; |
193 } | 217 } |
194 } | 218 } |
195 x += dir; | 219 x += dir; |
196 } | 220 } |
221 } | |
222 } | |
223 | |
224 static void fetch_sprite_cells_mode4(vdp_context * context) | |
225 { | |
226 if (context->cur_slot >= context->sprite_draws) { | |
227 sprite_draw * d = context->sprite_draw_list + context->cur_slot; | |
228 uint32_t address = mode4_address_map[d->address & 0x3FFF]; | |
229 context->fetch_tmp[0] = context->vdpmem[address]; | |
230 context->fetch_tmp[1] = context->vdpmem[address + 1]; | |
231 } | |
232 } | |
233 | |
234 static void render_sprite_cells_mode4(vdp_context * context) | |
235 { | |
236 if (context->cur_slot >= context->sprite_draws) { | |
237 sprite_draw * d = context->sprite_draw_list + context->cur_slot; | |
238 uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1; | |
239 pixels |= planar_to_chunky[context->fetch_tmp[1]]; | |
240 uint32_t address = mode4_address_map[(d->address + 2) & 0x3FFF]; | |
241 pixels |= planar_to_chunky[context->vdpmem[address]] << 3; | |
242 pixels |= planar_to_chunky[context->vdpmem[address + 1]] << 2; | |
243 int x = d->x_pos & 0xFF; | |
244 for (int i = 28; i >= 0; i -= 4, x++) | |
245 { | |
246 if (context->linebuf[x]) { | |
247 context->flags2 |= FLAG2_SPRITE_COLLIDE; | |
248 } else { | |
249 context->linebuf[x] = pixels >> i & 0xF; | |
250 } | |
251 } | |
252 context->cur_slot--; | |
197 } | 253 } |
198 } | 254 } |
199 | 255 |
200 void vdp_print_sprite_table(vdp_context * context) | 256 void vdp_print_sprite_table(vdp_context * context) |
201 { | 257 { |
405 context->sprite_index = context->sat_cache[address+3] & 0x7F; | 461 context->sprite_index = context->sat_cache[address+3] & 0x7F; |
406 } | 462 } |
407 } | 463 } |
408 } | 464 } |
409 | 465 |
466 static void scan_sprite_table_mode4(uint32_t line, vdp_context * context) | |
467 { | |
468 if (context->sprite_index < MAX_SPRITES_FRAME_H32 && context->slot_counter) { | |
469 line += 1; | |
470 line &= 0xFF; | |
471 | |
472 uint32_t y = context->sat_cache[context->sprite_index]; | |
473 uint32_t size = (context->regs[REG_MODE_2] & BIT_SPRITE_SZ) ? 16 : 8; | |
474 if (y <= line && line < (y + size)) { | |
475 context->sprite_info_list[--(context->slot_counter)].size = size; | |
476 context->sprite_info_list[context->slot_counter].index = context->sprite_index; | |
477 context->sprite_info_list[context->slot_counter].y = y; | |
478 } | |
479 context->sprite_index++; | |
480 | |
481 if (context->sprite_index < MAX_SPRITES_FRAME_H32 && context->slot_counter) { | |
482 y = context->sat_cache[context->sprite_index]; | |
483 if (y <= line && line < (y + size)) { | |
484 context->sprite_info_list[--(context->slot_counter)].size = size; | |
485 context->sprite_info_list[context->slot_counter].index = context->sprite_index; | |
486 context->sprite_info_list[context->slot_counter].y = y; | |
487 } | |
488 context->sprite_index++; | |
489 } | |
490 | |
491 } | |
492 } | |
493 | |
410 static void read_sprite_x(uint32_t line, vdp_context * context) | 494 static void read_sprite_x(uint32_t line, vdp_context * context) |
411 { | 495 { |
412 if (context->cur_slot >= context->slot_counter) { | 496 if (context->cur_slot >= context->slot_counter) { |
413 if (context->sprite_draws) { | 497 if (context->sprite_draws) { |
414 line += 1; | 498 line += 1; |
482 context->flags |= FLAG_DOT_OFLOW; | 566 context->flags |= FLAG_DOT_OFLOW; |
483 } | 567 } |
484 } | 568 } |
485 } | 569 } |
486 | 570 |
487 static void write_cram(vdp_context * context, uint16_t address, uint16_t value) | 571 static void read_sprite_x_mode4(uint32_t line, vdp_context * context) |
488 { | 572 { |
489 uint16_t addr = (address/2) & (CRAM_SIZE-1); | 573 if (context->cur_slot >= context->slot_counter) { |
490 context->cram[addr] = value; | 574 if (context->sprite_draws) { |
491 context->colors[addr] = color_map[value & 0xEEE]; | 575 line += 1; |
492 context->colors[addr + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; | 576 line &= 0xFF; |
493 context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; | 577 |
578 uint32_t address = (context->regs[REG_SAT] << 7 & 0x3F00) + 0x80 + context->sprite_info_list[context->cur_slot].index * 2; | |
579 address = mode4_address_map[address]; | |
580 --context->sprite_draws; | |
581 uint32_t tile_address = context->vdpmem[address + 1] * 32 + (context->regs[REG_STILE_BASE] << 11 & 0x2000); | |
582 tile_address += (line - context->sprite_info_list[context->cur_slot].y)* 4; | |
583 context->sprite_draw_list[context->sprite_draws].x_pos = context->vdpmem[address]; | |
584 context->sprite_draw_list[context->sprite_draws].address = tile_address; | |
585 context->cur_slot--; | |
586 } else { | |
587 context->flags |= FLAG_DOT_OFLOW; | |
588 } | |
589 } | |
494 } | 590 } |
495 | 591 |
496 #define CRAM_BITS 0xEEE | 592 #define CRAM_BITS 0xEEE |
497 #define VSRAM_BITS 0x7FF | 593 #define VSRAM_BITS 0x7FF |
498 #define VSRAM_DIRTY_BITS 0xF800 | 594 #define VSRAM_DIRTY_BITS 0xF800 |
595 | |
596 void write_cram(vdp_context * context, uint16_t address, uint16_t value) | |
597 { | |
598 uint16_t addr; | |
599 if (context->regs[REG_MODE_2] & BIT_MODE_5) { | |
600 addr = (address/2) & (CRAM_SIZE-1); | |
601 } else { | |
602 addr = address & 0x1F; | |
603 value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00); | |
604 } | |
605 context->cram[addr] = value; | |
606 context->colors[addr] = color_map[value & CRAM_BITS]; | |
607 context->colors[addr + CRAM_SIZE] = color_map[(value & CRAM_BITS) | FBUF_SHADOW]; | |
608 context->colors[addr + CRAM_SIZE*2] = color_map[(value & CRAM_BITS) | FBUF_HILIGHT]; | |
609 context->colors[addr + CRAM_SIZE*3] = color_map[(value & CRAM_BITS) | FBUF_MODE4]; | |
610 } | |
499 | 611 |
500 static void vdp_advance_dma(vdp_context * context) | 612 static void vdp_advance_dma(vdp_context * context) |
501 { | 613 { |
502 context->regs[REG_DMASRC_L] += 1; | 614 context->regs[REG_DMASRC_L] += 1; |
503 if (!context->regs[REG_DMASRC_L]) { | 615 if (!context->regs[REG_DMASRC_L]) { |
513 } | 625 } |
514 } | 626 } |
515 | 627 |
516 void write_vram_byte(vdp_context *context, uint16_t address, uint8_t value) | 628 void write_vram_byte(vdp_context *context, uint16_t address, uint8_t value) |
517 { | 629 { |
518 if (!(address & 4)) { | 630 if (context->regs[REG_MODE_2] & BIT_MODE_5) { |
519 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9; | 631 if (!(address & 4)) { |
520 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { | 632 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9; |
521 uint16_t cache_address = address - sat_address; | 633 if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { |
522 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); | 634 uint16_t cache_address = address - sat_address; |
523 context->sat_cache[cache_address] = value; | 635 cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); |
524 } | 636 context->sat_cache[cache_address] = value; |
637 } | |
638 } | |
639 } else { | |
640 if (!(address & 0xC0)) { | |
641 uint16_t sat_address = context->regs[REG_SAT] << 7 & 0x3F00; | |
642 if (address >= sat_address && address < (sat_address + 0x40)) { | |
643 context->sat_cache[address-sat_address] = value; | |
644 } | |
645 } | |
646 address = mode4_address_map[address & 0x3FFF]; | |
525 } | 647 } |
526 context->vdpmem[address] = value; | 648 context->vdpmem[address] = value; |
527 } | 649 } |
528 | 650 |
529 static void external_slot(vdp_context * context) | 651 static void external_slot(vdp_context * context) |
554 break; | 676 break; |
555 case CRAM_WRITE: { | 677 case CRAM_WRITE: { |
556 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); | 678 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); |
557 if (start->partial == 1) { | 679 if (start->partial == 1) { |
558 uint16_t val; | 680 uint16_t val; |
559 if (start->address & 1) { | 681 if ((start->address & 1) && (context->regs[REG_MODE_2] & BIT_MODE_5)) { |
560 val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF) | start->value << 8; | 682 val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF) | start->value << 8; |
561 } else { | 683 } else { |
562 val = (context->cram[start->address >> 1 & (CRAM_SIZE-1)] & 0xFF00) | start->value; | 684 uint16_t address = (context->regs[REG_MODE_2] & BIT_MODE_5) ? start->address >> 1 & (CRAM_SIZE-1) : start->address & 0x1F; |
685 val = (context->cram[address] & 0xFF00) | start->value; | |
563 } | 686 } |
564 write_cram(context, start->address, val); | 687 write_cram(context, start->address, val); |
565 } else { | 688 } else { |
566 write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value); | 689 write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value); |
567 } | 690 } |
833 static void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context) | 956 static void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context) |
834 { | 957 { |
835 read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context); | 958 read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context); |
836 } | 959 } |
837 | 960 |
961 static void read_map_mode4(uint16_t column, uint32_t line, vdp_context * context) | |
962 { | |
963 uint32_t address = (context->regs[REG_SCROLL_A] & 0xE) << 10; | |
964 //add row | |
965 uint32_t vscroll = line; | |
966 if (column < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) { | |
967 vscroll += context->regs[REG_Y_SCROLL]; | |
968 } | |
969 if (vscroll > 223) { | |
970 vscroll -= 224; | |
971 } | |
972 address += (vscroll >> 3) * 2 * 32; | |
973 //add column | |
974 address += (((column << 3) + context->hscroll_a) >> 3) * 2; | |
975 //adjust for weird VRAM mapping in Mode 4 | |
976 address = mode4_address_map[address]; | |
977 context->col_1 = (context->vdpmem[address] << 8) | context->vdpmem[address+1]; | |
978 } | |
979 | |
838 static void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context) | 980 static void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context) |
839 { | 981 { |
840 uint16_t address; | 982 uint16_t address; |
841 uint16_t vflip_base; | 983 uint16_t vflip_base; |
842 if (context->double_res) { | 984 if (context->double_res) { |
882 } | 1024 } |
883 | 1025 |
884 static void render_map_3(vdp_context * context) | 1026 static void render_map_3(vdp_context * context) |
885 { | 1027 { |
886 render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context); | 1028 render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context); |
1029 } | |
1030 | |
1031 static void fetch_map_mode4(uint16_t col, uint32_t line, vdp_context *context) | |
1032 { | |
1033 //calculate pixel row to fetch | |
1034 uint32_t vscroll = line; | |
1035 if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) { | |
1036 vscroll += context->regs[REG_Y_SCROLL]; | |
1037 } | |
1038 if (vscroll > 223) { | |
1039 vscroll -= 224; | |
1040 } | |
1041 vscroll &= 7; | |
1042 if (context->col_1 & 0x400) { | |
1043 vscroll = 7 - vscroll; | |
1044 } | |
1045 | |
1046 uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4]; | |
1047 context->fetch_tmp[0] = context->vdpmem[address]; | |
1048 context->fetch_tmp[1] = context->vdpmem[address+1]; | |
887 } | 1049 } |
888 | 1050 |
889 static void render_map_output(uint32_t line, int32_t col, vdp_context * context) | 1051 static void render_map_output(uint32_t line, int32_t col, vdp_context * context) |
890 { | 1052 { |
891 if (line >= 240) { | 1053 if (line >= 240) { |
1034 } | 1196 } |
1035 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | 1197 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; |
1036 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; | 1198 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; |
1037 } | 1199 } |
1038 | 1200 |
1201 static void render_map_mode4(uint32_t line, int32_t col, vdp_context * context) | |
1202 { | |
1203 uint32_t vscroll = line; | |
1204 if (col < 24 || !(context->regs[REG_MODE_1] & BIT_VSCRL_LOCK)) { | |
1205 vscroll += context->regs[REG_Y_SCROLL]; | |
1206 } | |
1207 if (vscroll > 223) { | |
1208 vscroll -= 224; | |
1209 } | |
1210 vscroll &= 7; | |
1211 if (context->col_1 & 0x400) { | |
1212 //vflip | |
1213 vscroll = 7 - vscroll; | |
1214 } | |
1215 | |
1216 uint32_t pixels = planar_to_chunky[context->fetch_tmp[0]] << 1; | |
1217 pixels |= planar_to_chunky[context->fetch_tmp[1]]; | |
1218 | |
1219 uint32_t address = mode4_address_map[((context->col_1 & 0x1FF) * 32) + vscroll * 4 + 2]; | |
1220 pixels |= planar_to_chunky[context->vdpmem[address]] << 3; | |
1221 pixels |= planar_to_chunky[context->vdpmem[address+1]] << 2; | |
1222 | |
1223 int i, i_inc, i_limit; | |
1224 if (context->col_1 & 0x200) { | |
1225 //hflip | |
1226 i = 0; | |
1227 i_inc = 4; | |
1228 i_limit = 32; | |
1229 } else { | |
1230 i = 28; | |
1231 i_inc = -4; | |
1232 i_limit = -4; | |
1233 } | |
1234 uint8_t pal_priority = (context->col_1 >> 7 & 0x10) | (context->col_1 >> 6 & 0x40); | |
1235 for (uint8_t *dst = context->tmp_buf_a + context->buf_a_off; i != i_limit; i += i_inc, dst++) | |
1236 { | |
1237 *dst = (pixels >> i & 0xF) | pal_priority; | |
1238 } | |
1239 context->buf_a_off = (context->buf_a_off + 8) & 15; | |
1240 | |
1241 uint8_t bgcolor = 0x10 | (context->regs[REG_BG_COLOR] & 0xF) + CRAM_SIZE*3; | |
1242 uint32_t *dst = context->output + col * 8; | |
1243 if (context->debug < 2) { | |
1244 if (col || !(context->regs[REG_MODE_1] & BIT_COL0_MASK)) { | |
1245 uint8_t *sprite_src = context->linebuf + col * 8; | |
1246 if (context->regs[REG_MODE_1] & BIT_SPRITE_8PX) { | |
1247 sprite_src += 8; | |
1248 } | |
1249 uint8_t *bg_src = context->tmp_buf_a + (((col & 1) * 8 + (context->hscroll_a & 0x7)) & 0x15); | |
1250 for (int i = 0; i < 8; i++, bg_src++, sprite_src++) | |
1251 { | |
1252 if ((*bg_src & 0x4F) > 0x40) { | |
1253 //background plane has priority and is opaque | |
1254 if (context->debug) { | |
1255 *(dst++) = context->debugcolors[DBG_SRC_A]; | |
1256 } else { | |
1257 *(dst++) = context->colors[(*bg_src & 0x1F) + CRAM_SIZE*3]; | |
1258 } | |
1259 | |
1260 } else if (*sprite_src) { | |
1261 //sprite layer is opaque | |
1262 if (context->debug) { | |
1263 *(dst++) = context->debugcolors[DBG_SRC_S]; | |
1264 } else { | |
1265 *(dst++) = context->colors[*sprite_src | 0x10 + CRAM_SIZE*3]; | |
1266 } | |
1267 } else if (*bg_src & 0xF) { | |
1268 //background plane is opaque | |
1269 if (context->debug) { | |
1270 *(dst++) = context->debugcolors[DBG_SRC_A]; | |
1271 } else { | |
1272 *(dst++) = context->colors[(*bg_src & 0x1F) + CRAM_SIZE*3]; | |
1273 } | |
1274 } else { | |
1275 if (context->debug) { | |
1276 *(dst++) = context->debugcolors[DBG_SRC_BG]; | |
1277 } else { | |
1278 *(dst++) = context->colors[bgcolor]; | |
1279 } | |
1280 } | |
1281 } | |
1282 } else { | |
1283 for (int i = 0; i < 8; i++) | |
1284 { | |
1285 *(dst++) = context->colors[bgcolor]; | |
1286 } | |
1287 } | |
1288 } else if (context->debug == 2) { | |
1289 for (int i = 0; i < 8; i++) | |
1290 { | |
1291 *(dst++) = context->colors[CRAM_SIZE*3 + col]; | |
1292 } | |
1293 } else { | |
1294 uint32_t cell = (line / 8) * 32 + col; | |
1295 uint32_t address = cell * 32 + (line % 8) * 4; | |
1296 uint32_t m4_address = mode4_address_map[address & 0x3FFF]; | |
1297 uint32_t pixel = planar_to_chunky[context->vdpmem[m4_address]] << 1; | |
1298 pixel |= planar_to_chunky[context->vdpmem[m4_address + 1]]; | |
1299 m4_address = mode4_address_map[(address + 2) & 0x3FFF]; | |
1300 pixel |= planar_to_chunky[context->vdpmem[m4_address]] << 3; | |
1301 pixel |= planar_to_chunky[context->vdpmem[m4_address + 1]] << 2; | |
1302 if (context->debug_pal < 2) { | |
1303 for (int i = 28; i >= 0; i -= 4) | |
1304 { | |
1305 *(dst++) = context->colors[CRAM_SIZE*3 | (context->debug_pal << 4) | (pixel >> i & 0xF)]; | |
1306 } | |
1307 } else { | |
1308 for (int i = 28; i >= 0; i -= 4) | |
1309 { | |
1310 uint8_t value = (pixel >> i & 0xF) * 17; | |
1311 if (context->debug_pal == 3) { | |
1312 value = 255 - value; | |
1313 } | |
1314 *(dst++) = render_map_color(value, value, value); | |
1315 } | |
1316 } | |
1317 } | |
1318 } | |
1319 | |
1039 static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19}; | 1320 static uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19}; |
1040 | 1321 |
1041 static void vdp_advance_line(vdp_context *context) | 1322 static void vdp_advance_line(vdp_context *context) |
1042 { | 1323 { |
1043 context->vcounter++; | 1324 context->vcounter++; |
1137 render_map_3(context);\ | 1418 render_map_3(context);\ |
1138 CHECK_LIMIT\ | 1419 CHECK_LIMIT\ |
1139 case (startcyc+7):\ | 1420 case (startcyc+7):\ |
1140 render_map_output(context->vcounter, column, context);\ | 1421 render_map_output(context->vcounter, column, context);\ |
1141 CHECK_LIMIT | 1422 CHECK_LIMIT |
1423 | |
1424 #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \ | |
1425 case startcyc:\ | |
1426 read_map_mode4(column, context->vcounter, context);\ | |
1427 CHECK_LIMIT\ | |
1428 case ((startcyc+1)&0xFF):\ | |
1429 if (column & 1) {\ | |
1430 read_sprite_x_mode4(context->vcounter, context);\ | |
1431 } else {\ | |
1432 external_slot(context);\ | |
1433 }\ | |
1434 CHECK_LIMIT\ | |
1435 case ((startcyc+2)&0xFF):\ | |
1436 fetch_map_mode4(column, context->vcounter, context);\ | |
1437 CHECK_LIMIT\ | |
1438 case ((startcyc+3)&0xFF):\ | |
1439 render_map_mode4(context->vcounter, column, context);\ | |
1440 CHECK_LIMIT | |
1441 | |
1442 #define COLUMN_RENDER_BLOCK_REFRESH_MODE4(column, startcyc) \ | |
1443 case startcyc:\ | |
1444 read_map_mode4(column, context->vcounter, context);\ | |
1445 CHECK_LIMIT\ | |
1446 case (startcyc+1):\ | |
1447 /* refresh, no don't run dma src */\ | |
1448 context->hslot++;\ | |
1449 context->cycles += slot_cycles;\ | |
1450 CHECK_ONLY\ | |
1451 case (startcyc+2):\ | |
1452 fetch_map_mode4(column, context->vcounter, context);\ | |
1453 CHECK_LIMIT\ | |
1454 case (startcyc+3):\ | |
1455 render_map_mode4(context->vcounter, column, context);\ | |
1456 CHECK_LIMIT | |
1142 | 1457 |
1143 #define SPRITE_RENDER_H40(slot) \ | 1458 #define SPRITE_RENDER_H40(slot) \ |
1144 case slot:\ | 1459 case slot:\ |
1145 render_sprite_cells( context);\ | 1460 render_sprite_cells( context);\ |
1146 scan_sprite_table(context->vcounter, context);\ | 1461 scan_sprite_table(context->vcounter, context);\ |
1168 } else {\ | 1483 } else {\ |
1169 context->hslot++;\ | 1484 context->hslot++;\ |
1170 }\ | 1485 }\ |
1171 context->cycles += slot_cycles;\ | 1486 context->cycles += slot_cycles;\ |
1172 CHECK_ONLY | 1487 CHECK_ONLY |
1173 | 1488 |
1489 #define SPRITE_RENDER_H32_MODE4(slot) \ | |
1490 case slot:\ | |
1491 fetch_sprite_cells_mode4(context);\ | |
1492 scan_sprite_table(context->vcounter, context);\ | |
1493 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ | |
1494 if (slot == 147) {\ | |
1495 context->hslot = 233;\ | |
1496 } else {\ | |
1497 context->hslot++;\ | |
1498 }\ | |
1499 context->cycles += slot_cycles;\ | |
1500 CHECK_ONLY\ | |
1501 case (slot == 147 ? 233 : slot+1):\ | |
1502 render_sprite_cells_mode4(context);\ | |
1503 scan_sprite_table(context->vcounter, context);\ | |
1504 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ | |
1505 if (slot == 147) {\ | |
1506 context->hslot = 233;\ | |
1507 } else {\ | |
1508 context->hslot++;\ | |
1509 }\ | |
1510 context->cycles += slot_cycles;\ | |
1511 CHECK_ONLY | |
1174 | 1512 |
1175 static void vdp_h40(vdp_context * context, uint32_t target_cycles) | 1513 static void vdp_h40(vdp_context * context, uint32_t target_cycles) |
1176 { | 1514 { |
1177 uint16_t address; | 1515 uint16_t address; |
1178 uint32_t mask; | 1516 uint32_t mask; |
1476 context->hslot++; | 1814 context->hslot++; |
1477 context->cycles += MCLKS_SLOT_H32; | 1815 context->cycles += MCLKS_SLOT_H32; |
1478 } | 1816 } |
1479 } | 1817 } |
1480 | 1818 |
1819 static void vdp_h32_mode4(vdp_context * context, uint32_t target_cycles) | |
1820 { | |
1821 uint16_t address; | |
1822 uint32_t mask; | |
1823 uint32_t const slot_cycles = MCLKS_SLOT_H32; | |
1824 switch(context->hslot) | |
1825 { | |
1826 for (;;) | |
1827 { | |
1828 //sprite attribute table scan starts | |
1829 case 132: | |
1830 context->sprite_index = 0x80; | |
1831 //in theory, Mode 4 should only allow 8 sprites per line, but if we assume that thee | |
1832 //Genesis VDP uses the SAT cache for sprite scans in Mode 4 like it does in Mode 5, | |
1833 //there should be enough bandwidth for 16 like in Mode 5 (though only 128 pixels rather than 256) | |
1834 context->slot_counter = MAX_SPRITES_LINE_H32; | |
1835 fetch_sprite_cells_mode4(context); | |
1836 scan_sprite_table_mode4(context->vcounter, context); | |
1837 CHECK_LIMIT | |
1838 case 133: | |
1839 render_sprite_cells_mode4(context); | |
1840 scan_sprite_table_mode4(context->vcounter, context); | |
1841 CHECK_LIMIT | |
1842 SPRITE_RENDER_H32_MODE4(134) | |
1843 SPRITE_RENDER_H32_MODE4(136) | |
1844 SPRITE_RENDER_H32_MODE4(138) | |
1845 SPRITE_RENDER_H32_MODE4(140) | |
1846 case 142: | |
1847 external_slot(context); | |
1848 CHECK_LIMIT | |
1849 SPRITE_RENDER_H32_MODE4(143) | |
1850 SPRITE_RENDER_H32_MODE4(145) | |
1851 SPRITE_RENDER_H32_MODE4(147) | |
1852 //HSYNC start @233 | |
1853 SPRITE_RENDER_H32_MODE4(234) | |
1854 SPRITE_RENDER_H32_MODE4(236) | |
1855 SPRITE_RENDER_H32_MODE4(238) | |
1856 case 240: | |
1857 external_slot(context); | |
1858 CHECK_LIMIT | |
1859 case 241: | |
1860 if (context->regs[REG_MODE_1] & BIT_HSCRL_LOCK && context->vcounter < 16) { | |
1861 context->hscroll_a = 0; | |
1862 } else { | |
1863 context->hscroll_a = context->regs[REG_X_SCROLL]; | |
1864 } | |
1865 CHECK_LIMIT | |
1866 SPRITE_RENDER_H32_MODE4(242) | |
1867 SPRITE_RENDER_H32_MODE4(244) | |
1868 //!HSYNC high | |
1869 case 246: | |
1870 external_slot(context); | |
1871 CHECK_LIMIT | |
1872 case 247: | |
1873 fetch_sprite_cells_mode4(context); | |
1874 scan_sprite_table_mode4(context->vcounter, context); | |
1875 CHECK_LIMIT | |
1876 case 248: | |
1877 external_slot(context); | |
1878 scan_sprite_table_mode4(context->vcounter, context);//Just a guess | |
1879 CHECK_LIMIT | |
1880 case 249: | |
1881 external_slot(context); | |
1882 scan_sprite_table_mode4(context->vcounter, context);//Just a guess | |
1883 CHECK_LIMIT | |
1884 case 250: | |
1885 external_slot(context); | |
1886 CHECK_LIMIT | |
1887 case 251: | |
1888 render_sprite_cells_mode4(context); | |
1889 scan_sprite_table_mode4(context->vcounter, context); | |
1890 CHECK_LIMIT | |
1891 case 252: | |
1892 external_slot(context); | |
1893 scan_sprite_table_mode4(context->vcounter, context);//Just a guess | |
1894 CHECK_LIMIT | |
1895 case 253: | |
1896 external_slot(context); | |
1897 scan_sprite_table_mode4(context->vcounter, context);//Just a guess | |
1898 //reverse context slot counter so it counts the number of sprite slots | |
1899 //filled rather than the number of available slots | |
1900 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; | |
1901 context->cur_slot = MAX_SPRITES_LINE_H32-1; | |
1902 context->sprite_draws = MAX_DRAWS_H32_MODE4; | |
1903 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); | |
1904 CHECK_LIMIT | |
1905 COLUMN_RENDER_BLOCK_MODE4(0, 254) | |
1906 COLUMN_RENDER_BLOCK_MODE4(1, 2) | |
1907 COLUMN_RENDER_BLOCK_MODE4(2, 6) | |
1908 COLUMN_RENDER_BLOCK_MODE4(3, 10) | |
1909 COLUMN_RENDER_BLOCK_MODE4(4, 14) | |
1910 COLUMN_RENDER_BLOCK_MODE4(5, 18) | |
1911 COLUMN_RENDER_BLOCK_REFRESH_MODE4(6, 22) | |
1912 COLUMN_RENDER_BLOCK_MODE4(7, 26) | |
1913 COLUMN_RENDER_BLOCK_MODE4(8, 30) | |
1914 COLUMN_RENDER_BLOCK_MODE4(9, 34) | |
1915 COLUMN_RENDER_BLOCK_MODE4(10, 38) | |
1916 COLUMN_RENDER_BLOCK_MODE4(11, 42) | |
1917 COLUMN_RENDER_BLOCK_MODE4(12, 46) | |
1918 COLUMN_RENDER_BLOCK_MODE4(13, 50) | |
1919 COLUMN_RENDER_BLOCK_REFRESH_MODE4(14, 54) | |
1920 COLUMN_RENDER_BLOCK_MODE4(15, 58) | |
1921 COLUMN_RENDER_BLOCK_MODE4(16, 62) | |
1922 COLUMN_RENDER_BLOCK_MODE4(17, 66) | |
1923 COLUMN_RENDER_BLOCK_MODE4(18, 70) | |
1924 COLUMN_RENDER_BLOCK_MODE4(19, 74) | |
1925 COLUMN_RENDER_BLOCK_MODE4(20, 78) | |
1926 COLUMN_RENDER_BLOCK_MODE4(21, 82) | |
1927 COLUMN_RENDER_BLOCK_REFRESH_MODE4(22, 86) | |
1928 COLUMN_RENDER_BLOCK_MODE4(23, 90) | |
1929 COLUMN_RENDER_BLOCK_MODE4(24, 94) | |
1930 COLUMN_RENDER_BLOCK_MODE4(25, 98) | |
1931 COLUMN_RENDER_BLOCK_MODE4(26, 102) | |
1932 COLUMN_RENDER_BLOCK_MODE4(27, 106) | |
1933 COLUMN_RENDER_BLOCK_MODE4(28, 110) | |
1934 COLUMN_RENDER_BLOCK_MODE4(29, 114) | |
1935 COLUMN_RENDER_BLOCK_REFRESH_MODE4(30, 118) | |
1936 COLUMN_RENDER_BLOCK_MODE4(31, 122) | |
1937 case 126: | |
1938 external_slot(context); | |
1939 CHECK_LIMIT | |
1940 case 127: | |
1941 external_slot(context); | |
1942 CHECK_LIMIT | |
1943 //sprite render to line buffer starts | |
1944 case 128: | |
1945 context->cur_slot = MAX_DRAWS_H32_MODE4-1; | |
1946 memset(context->linebuf, 0, LINEBUF_SIZE); | |
1947 fetch_sprite_cells_mode4(context); | |
1948 CHECK_LIMIT | |
1949 case 129: | |
1950 render_sprite_cells_mode4(context); | |
1951 CHECK_LIMIT | |
1952 case 130: | |
1953 fetch_sprite_cells_mode4(context); | |
1954 CHECK_LIMIT | |
1955 case 131: | |
1956 render_sprite_cells_mode4(context); | |
1957 vdp_advance_line(context); | |
1958 if (context->vcounter == MODE4_INACTIVE_START) { | |
1959 context->hslot++; | |
1960 context->cycles += slot_cycles; | |
1961 return; | |
1962 } | |
1963 CHECK_LIMIT | |
1964 } | |
1965 default: | |
1966 context->hslot++; | |
1967 context->cycles += MCLKS_SLOT_H32; | |
1968 } | |
1969 } | |
1970 | |
1481 void latch_mode(vdp_context * context) | 1971 void latch_mode(vdp_context * context) |
1482 { | 1972 { |
1483 context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL; | 1973 context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL; |
1484 } | 1974 } |
1485 | 1975 |
1507 | 1997 |
1508 void vdp_run_context(vdp_context * context, uint32_t target_cycles) | 1998 void vdp_run_context(vdp_context * context, uint32_t target_cycles) |
1509 { | 1999 { |
1510 while(context->cycles < target_cycles) | 2000 while(context->cycles < target_cycles) |
1511 { | 2001 { |
1512 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START; | |
1513 //line 0x1FF is basically active even though it's not displayed | |
1514 uint8_t active_slot = context->vcounter < inactive_start || context->vcounter == 0x1FF; | |
1515 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40; | 2002 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40; |
1516 if (context->regs[REG_MODE_2] & DISPLAY_ENABLE && active_slot) { | 2003 uint8_t active_slot, mode_5; |
1517 if (is_h40) { | 2004 uint32_t inactive_start; |
1518 vdp_h40(context, target_cycles); | 2005 if (context->regs[REG_MODE_2] & BIT_MODE_5) { |
2006 //Mode 5 selected | |
2007 mode_5 = 1; | |
2008 inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START; | |
2009 //line 0x1FF is basically active even though it's not displayed | |
2010 active_slot = (context->vcounter < inactive_start || context->vcounter == 0x1FF) && (context->regs[REG_MODE_2] & DISPLAY_ENABLE); | |
2011 } else { | |
2012 mode_5 = 0; | |
2013 inactive_start = MODE4_INACTIVE_START; | |
2014 //display is effectively disabled if neither mode 5 nor mode 4 are selected | |
2015 active_slot = context->vcounter < inactive_start && (context->regs[REG_MODE_2] & DISPLAY_ENABLE) && (context->regs[REG_MODE_1] & BIT_MODE_4); | |
2016 } | |
2017 | |
2018 if (active_slot) { | |
2019 if (mode_5) { | |
2020 if (is_h40) { | |
2021 vdp_h40(context, target_cycles); | |
2022 } else { | |
2023 vdp_h32(context, target_cycles); | |
2024 } | |
1519 } else { | 2025 } else { |
1520 vdp_h32(context, target_cycles); | 2026 vdp_h32_mode4(context, target_cycles); |
1521 } | 2027 } |
1522 } else { | 2028 } else { |
1523 if (is_h40) { | 2029 if (is_h40) { |
1524 if (context->hslot == 161) { | 2030 if (context->hslot == 161) { |
1525 context->cur_slot = MAX_DRAWS-1; | 2031 context->cur_slot = MAX_DRAWS-1; |
1656 } else { | 2162 } else { |
1657 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); | 2163 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); |
1658 } | 2164 } |
1659 } | 2165 } |
1660 } else { | 2166 } else { |
2167 uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5; | |
1661 if ((value & 0xC000) == 0x8000) { | 2168 if ((value & 0xC000) == 0x8000) { |
1662 //Register write | 2169 //Register write |
1663 uint8_t reg = (value >> 8) & 0x1F; | 2170 uint8_t reg = (value >> 8) & 0x1F; |
1664 if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) { | 2171 if (reg < (mode_5 ? VDP_REGS : 0xA)) { |
1665 //printf("register %d set to %X\n", reg, value & 0xFF); | 2172 //printf("register %d set to %X\n", reg, value & 0xFF); |
1666 if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) { | 2173 if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) { |
1667 context->hv_latch = vdp_hv_counter_read(context); | 2174 context->hv_latch = vdp_hv_counter_read(context); |
1668 } | 2175 } |
1669 if (reg == REG_BG_COLOR) { | 2176 if (reg == REG_BG_COLOR) { |
1679 context->flags2 &= ~FLAG2_EVEN_FIELD; | 2186 context->flags2 &= ~FLAG2_EVEN_FIELD; |
1680 } | 2187 } |
1681 } | 2188 } |
1682 context->cd &= 0x3C; | 2189 context->cd &= 0x3C; |
1683 } | 2190 } |
1684 } else { | 2191 } else if (mode_5) { |
1685 context->flags |= FLAG_PENDING; | 2192 context->flags |= FLAG_PENDING; |
1686 context->address = (context->address &0xC000) | (value & 0x3FFF); | 2193 context->address = (context->address &0xC000) | (value & 0x3FFF); |
1687 context->cd = (context->cd &0x3C) | (value >> 14); | 2194 context->cd = (context->cd &0x3C) | (value >> 14); |
1688 //Should these be taken care of here or after the second write? | 2195 //Should these be taken care of here or after the second write? |
1689 //context->flags &= ~FLAG_READ_FETCHED; | 2196 //context->flags &= ~FLAG_READ_FETCHED; |
1690 //context->flags2 &= ~FLAG2_READ_PENDING; | 2197 //context->flags2 &= ~FLAG2_READ_PENDING; |
2198 } else { | |
2199 context->address = value & 0x3FFF; | |
2200 context->cd = value >> 14; | |
2201 context->flags &= ~FLAG_READ_FETCHED; | |
2202 context->flags2 &= ~FLAG2_READ_PENDING; | |
1691 } | 2203 } |
1692 } | 2204 } |
1693 return 0; | 2205 return 0; |
1694 } | 2206 } |
1695 | 2207 |
1736 if (context->fifo_read < 0) { | 2248 if (context->fifo_read < 0) { |
1737 context->fifo_read = context->fifo_write; | 2249 context->fifo_read = context->fifo_write; |
1738 } | 2250 } |
1739 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); | 2251 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); |
1740 context->address += context->regs[REG_AUTOINC]; | 2252 context->address += context->regs[REG_AUTOINC]; |
2253 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) { | |
2254 context->address++; | |
2255 } | |
1741 return 0; | 2256 return 0; |
1742 } | 2257 } |
1743 | 2258 |
1744 void vdp_data_port_write_pbc(vdp_context * context, uint8_t value) | 2259 void vdp_data_port_write_pbc(vdp_context * context, uint8_t value) |
1745 { | 2260 { |
1767 if (context->fifo_read < 0) { | 2282 if (context->fifo_read < 0) { |
1768 context->fifo_read = context->fifo_write; | 2283 context->fifo_read = context->fifo_write; |
1769 } | 2284 } |
1770 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); | 2285 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); |
1771 context->address += context->regs[REG_AUTOINC]; | 2286 context->address += context->regs[REG_AUTOINC]; |
2287 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) { | |
2288 context->address++; | |
2289 } | |
1772 } | 2290 } |
1773 | 2291 |
1774 void vdp_test_port_write(vdp_context * context, uint16_t value) | 2292 void vdp_test_port_write(vdp_context * context, uint16_t value) |
1775 { | 2293 { |
1776 //TODO: implement test register | 2294 //TODO: implement test register |