Mercurial > repos > blastem
diff vdp.c @ 2686:05915f01046d
WIP attempt to move VDP rendering to a separate thread
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 31 Mar 2025 21:02:17 -0700 |
parents | da2e06c42d16 |
children |
line wrap: on
line diff
--- a/vdp.c Sun Mar 30 00:06:53 2025 -0700 +++ b/vdp.c Mon Mar 31 21:02:17 2025 -0700 @@ -156,16 +156,9 @@ static uint8_t static_table_init_done; -vdp_context *init_vdp_context(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type) +vdp_context *init_vdp_context_int(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type) { vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE); - if (headless) { - context->fb = malloc(512 * LINEBUF_SIZE * sizeof(pixel_t)); - context->output_pitch = LINEBUF_SIZE * sizeof(pixel_t); - } else { - context->cur_buffer = FRAMEBUFFER_ODD; - context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); - } context->sprite_draws = MAX_SPRITES_LINE; context->fifo_write = 0; context->fifo_read = -1; @@ -339,8 +332,200 @@ context->flags2 |= FLAG2_REGION_PAL; } update_video_params(context); + + return context; +} + +static uint32_t mode5_sat_address(vdp_context *context) +{ + uint32_t addr = context->regs[REG_SAT] << 9; + if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) { + addr &= 0xFFFF; + } + if (context->regs[REG_MODE_4] & BIT_H40) { + addr &= 0x1FC00; + } + return addr; +} + +static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value) +{ + if (context->regs[REG_MODE_2] & BIT_MODE_5) { + if (!(address & 4)) { + uint32_t sat_address = mode5_sat_address(context); + if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { + uint16_t cache_address = address - sat_address; + cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); + context->sat_cache[cache_address] = value >> 8; + context->sat_cache[cache_address^1] = value; + } + } + } +} + +void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value) +{ + if (context->regs[REG_MODE_2] & BIT_MODE_5) { + if (!(address & 4)) { + uint32_t sat_address = mode5_sat_address(context); + if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { + uint16_t cache_address = address - sat_address; + cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); + context->sat_cache[cache_address] = value; + } + } + } +} + +static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value) +{ + address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2); + address ^= 1; + //TODO: Support an option to actually have 128KB of VRAM + context->vdpmem[address] = value; +} + +static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value) +{ + if (context->regs[REG_MODE_2] & BIT_MODE_5) { + address &= 0xFFFF; + } else { + address = mode4_address_map[address & 0x3FFF]; + } + context->vdpmem[address] = value; +} + +#define VSRAM_DIRTY_BITS 0xF800 + +//rough estimate of slot number at which border display starts +#define BG_START_SLOT 6 + +static void update_color_map(vdp_context *context, uint16_t index, uint16_t value) +{ + context->colors[index] = context->color_map[value & CRAM_BITS]; + context->colors[index + SHADOW_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_SHADOW]; + context->colors[index + HIGHLIGHT_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_HILIGHT]; + if (context->type == VDP_GAMEGEAR) { + context->colors[index + MODE4_OFFSET] = context->color_map[value & 0xFFF]; + } else { + context->colors[index + MODE4_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_MODE4]; + } +} + +void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value) +{ + context->cram[addr] = value; + update_color_map(context, addr, value); +} + +static void write_cram(vdp_context * context, uint16_t address, uint16_t value) +{ + uint16_t addr; + if (context->regs[REG_MODE_2] & BIT_MODE_5) { + addr = (address/2) & (CRAM_SIZE-1); + } else if (context->type == VDP_GAMEGEAR) { + addr = (address/2) & 31; + } else { + addr = address & 0x1F; + value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00); + } + write_cram_internal(context, addr, value); + + if (context->output && context->hslot >= BG_START_SLOT && ( + context->vcounter < context->inactive_start + context->border_bot + || context->vcounter > 0x200 - context->border_top + )) { + uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2; + if (context->hslot < bg_end_slot) { + pixel_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET]; + context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color; + } + } +} + +#ifndef _WIN32 +static int vdp_render_thread_main(void *vcontext) +{ + vdp_context *context = vcontext; + event_out event; + for (;;) + { + event.autoinc = context->regs[REG_AUTOINC]; + uint8_t etype = mem_reader_next_event(&event); + if (etype == EVENT_EOF) { + break; + } + vdp_run_context(context, event.cycle); + switch (etype) + { + case EVENT_ADJUST: + vdp_adjust_cycles(context, event.address); + break; + case EVENT_VDP_REG: + context->regs[event.address] = event.value; + if (event.address == REG_MODE_4) { + context->double_res = (event.value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); + if (!context->double_res) { + context->flags2 &= ~FLAG2_EVEN_FIELD; + } + } + if (event.address == REG_MODE_1 || event.address == REG_MODE_2 || event.address == REG_MODE_4) { + update_video_params(context); + } + break; + case EVENT_VRAM_BYTE: + case EVENT_VRAM_BYTE_DELTA: + case EVENT_VRAM_BYTE_ONE: + case EVENT_VRAM_BYTE_AUTO: + vdp_check_update_sat_byte(context, event.address ^ 1, event.value); + write_vram_byte(context, event.address ^ 1, event.value); + break; + case EVENT_VRAM_WORD: + case EVENT_VRAM_WORD_DELTA: + vdp_check_update_sat(context, event.address, event.value); + write_vram_word(context, event.address, event.value); + break; + case EVENT_VDP_INTRAM: + if (event.address < 128) { + write_cram(context, event.address, event.value); + } else { + context->vsram[event.address&63] = event.value; + } + break; + } + } + return 0; +} +#endif + +static render_thread vdp_thread; +vdp_context *init_vdp_context(uint8_t region_pal, uint8_t has_max_vsram, uint8_t type) +{ + vdp_context *ret = init_vdp_context_int(region_pal, has_max_vsram, type); + vdp_context *context; +#ifndef _WIN32 + if (render_is_threaded_video()) { + context = ret->renderer = init_vdp_context_int(region_pal, has_max_vsram, type); + } else +#endif + { + context = ret; + } + if (headless) { + context->fb = malloc(512 * LINEBUF_SIZE * sizeof(pixel_t)); + context->output_pitch = LINEBUF_SIZE * sizeof(pixel_t); + } else { + context->cur_buffer = FRAMEBUFFER_ODD; + context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); + } context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * context->border_top); - return context; +#ifndef _WIN32 + if (ret->renderer) { + event_log_mem(); + render_create_thread(&vdp_thread, "vdp_render", vdp_render_thread_main, ret->renderer); + } +#endif + return ret; } void vdp_free(vdp_context *context) @@ -354,6 +539,12 @@ vdp_toggle_debug_view(context, i); } } +#ifndef _WIN32 + if (context->renderer) { + event_log_mem_stop(); + vdp_free(context->renderer); + } +#endif free(context); } @@ -520,18 +711,6 @@ } } -static uint32_t mode5_sat_address(vdp_context *context) -{ - uint32_t addr = context->regs[REG_SAT] << 9; - if (!(context->regs[REG_MODE_2] & BIT_128K_VRAM)) { - addr &= 0xFFFF; - } - if (context->regs[REG_MODE_4] & BIT_H40) { - addr &= 0x1FC00; - } - return addr; -} - void vdp_print_sprite_table(vdp_context * context) { if (context->type == VDP_GENESIS && context->regs[REG_MODE_2] & BIT_MODE_5) { @@ -957,54 +1136,6 @@ } } -#define VSRAM_DIRTY_BITS 0xF800 - -//rough estimate of slot number at which border display starts -#define BG_START_SLOT 6 - -static void update_color_map(vdp_context *context, uint16_t index, uint16_t value) -{ - context->colors[index] = context->color_map[value & CRAM_BITS]; - context->colors[index + SHADOW_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_SHADOW]; - context->colors[index + HIGHLIGHT_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_HILIGHT]; - if (context->type == VDP_GAMEGEAR) { - context->colors[index + MODE4_OFFSET] = context->color_map[value & 0xFFF]; - } else { - context->colors[index + MODE4_OFFSET] = context->color_map[(value & CRAM_BITS) | FBUF_MODE4]; - } -} - -void write_cram_internal(vdp_context * context, uint16_t addr, uint16_t value) -{ - context->cram[addr] = value; - update_color_map(context, addr, value); -} - -static void write_cram(vdp_context * context, uint16_t address, uint16_t value) -{ - uint16_t addr; - if (context->regs[REG_MODE_2] & BIT_MODE_5) { - addr = (address/2) & (CRAM_SIZE-1); - } else if (context->type == VDP_GAMEGEAR) { - addr = (address/2) & 31; - } else { - addr = address & 0x1F; - value = (value << 1 & 0xE) | (value << 2 & 0xE0) | (value & 0xE00); - } - write_cram_internal(context, addr, value); - - if (context->output && context->hslot >= BG_START_SLOT && ( - context->vcounter < context->inactive_start + context->border_bot - || context->vcounter > 0x200 - context->border_top - )) { - uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2; - if (context->hslot < bg_end_slot) { - pixel_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET]; - context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color; - } - } -} - static void vdp_advance_dma(vdp_context * context) { context->regs[REG_DMASRC_L] += 1; @@ -1021,53 +1152,6 @@ } } -static void vdp_check_update_sat(vdp_context *context, uint32_t address, uint16_t value) -{ - if (context->regs[REG_MODE_2] & BIT_MODE_5) { - if (!(address & 4)) { - uint32_t sat_address = mode5_sat_address(context); - if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { - uint16_t cache_address = address - sat_address; - cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); - context->sat_cache[cache_address] = value >> 8; - context->sat_cache[cache_address^1] = value; - } - } - } -} - -void vdp_check_update_sat_byte(vdp_context *context, uint32_t address, uint8_t value) -{ - if (context->regs[REG_MODE_2] & BIT_MODE_5) { - if (!(address & 4)) { - uint32_t sat_address = mode5_sat_address(context); - if(address >= sat_address && address < (sat_address + SAT_CACHE_SIZE*2)) { - uint16_t cache_address = address - sat_address; - cache_address = (cache_address & 3) | (cache_address >> 1 & 0x1FC); - context->sat_cache[cache_address] = value; - } - } - } -} - -static void write_vram_word(vdp_context *context, uint32_t address, uint16_t value) -{ - address = (address & 0x3FC) | (address >> 1 & 0xFC01) | (address >> 9 & 0x2); - address ^= 1; - //TODO: Support an option to actually have 128KB of VRAM - context->vdpmem[address] = value; -} - -static void write_vram_byte(vdp_context *context, uint32_t address, uint8_t value) -{ - if (context->regs[REG_MODE_2] & BIT_MODE_5) { - address &= 0xFFFF; - } else { - address = mode4_address_map[address & 0x3FFF]; - } - context->vdpmem[address] = value; -} - #define DMA_FILL 0x80 #define DMA_COPY 0xC0 #define DMA_TYPE_MASK 0xC0 @@ -2148,6 +2232,9 @@ } context->vcounter++; + if (context->renderer && context->vcounter == context->inactive_start) { + context->frame++; + } if (is_mode_5) { context->window_h_latch = context->regs[REG_WINDOW_H]; context->window_v_latch = context->regs[REG_WINDOW_V]; @@ -3111,6 +3198,48 @@ render_map_output(context->vcounter, column, context);\ CHECK_LIMIT +#define COLUMN_RENDER_BLOCK_PHONY(column, startcyc) \ + case startcyc:\ + CHECK_LIMIT\ + case ((startcyc+1)&0xFF):\ + external_slot(context);\ + CHECK_LIMIT\ + case ((startcyc+2)&0xFF):\ + CHECK_LIMIT\ + case ((startcyc+3)&0xFF):\ + CHECK_LIMIT\ + case ((startcyc+4)&0xFF):\ + CHECK_LIMIT\ + case ((startcyc+5)&0xFF):\ + read_sprite_x(context->vcounter, context);\ + CHECK_LIMIT\ + case ((startcyc+6)&0xFF):\ + CHECK_LIMIT\ + case ((startcyc+7)&0xFF):\ + CHECK_LIMIT + +#define COLUMN_RENDER_BLOCK_REFRESH_PHONY(column, startcyc) \ + case startcyc:\ + CHECK_LIMIT\ + case (startcyc+1):\ + /* refresh, so don't run dma src */\ + context->hslot++;\ + context->cycles += slot_cycles;\ + CHECK_ONLY\ + case (startcyc+2):\ + CHECK_LIMIT\ + case (startcyc+3):\ + CHECK_LIMIT\ + case (startcyc+4):\ + CHECK_LIMIT\ + case (startcyc+5):\ + read_sprite_x(context->vcounter, context);\ + CHECK_LIMIT\ + case (startcyc+6):\ + CHECK_LIMIT\ + case (startcyc+7):\ + CHECK_LIMIT + #define COLUMN_RENDER_BLOCK_MODE4(column, startcyc) \ case startcyc:\ OUTPUT_PIXEL_MODE4(startcyc)\ @@ -3183,6 +3312,12 @@ scan_sprite_table(context->vcounter, context);\ CHECK_LIMIT_HSYNC(slot) +#define SPRITE_RENDER_H40_PHONY(slot) \ + case slot:\ + render_sprite_cells( context);\ + scan_sprite_table(context->vcounter, context);\ + CHECK_LIMIT_HSYNC(slot) + //Note that the line advancement check will fail if BG_START_SLOT is > 6 //as we're bumping up against the hcounter jump #define SPRITE_RENDER_H32(slot) \ @@ -3220,6 +3355,19 @@ context->cycles += slot_cycles;\ CHECK_ONLY +#define SPRITE_RENDER_H32_PHONY(slot) \ + case slot:\ + render_sprite_cells( context);\ + scan_sprite_table(context->vcounter, context);\ + if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ + if (slot == 147) {\ + context->hslot = 233;\ + } else {\ + context->hslot++;\ + }\ + context->cycles += slot_cycles;\ + CHECK_ONLY + #define MODE4_CHECK_SLOT_LINE(slot) \ if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ if ((slot) == BG_START_SLOT + (256+HORIZ_BORDER)/2) {\ @@ -3707,6 +3855,161 @@ } } +static void vdp_h40_phony(vdp_context * context, uint32_t target_cycles) +{ + uint32_t const slot_cycles = MCLKS_SLOT_H40; + switch(context->hslot) + { + for (;;) + { + case 165: + if (context->state == PREPARING) { + external_slot(context); + } else { + render_sprite_cells(context); + } + CHECK_LIMIT + case 166: + if (context->state == PREPARING) { + external_slot(context); + } else { + render_sprite_cells(context); + } + if (context->vcounter == context->inactive_start) { + context->hslot++; + context->cycles += slot_cycles; + return; + } + CHECK_LIMIT + //sprite attribute table scan starts + case 167: + context->sprite_index = 0x80; + context->slot_counter = 0; + render_sprite_cells(context); + scan_sprite_table(context->vcounter, context); + CHECK_LIMIT + SPRITE_RENDER_H40_PHONY(168) + SPRITE_RENDER_H40_PHONY(169) + SPRITE_RENDER_H40_PHONY(170) + SPRITE_RENDER_H40_PHONY(171) + SPRITE_RENDER_H40_PHONY(172) + SPRITE_RENDER_H40_PHONY(173) + SPRITE_RENDER_H40_PHONY(174) + SPRITE_RENDER_H40_PHONY(175) + SPRITE_RENDER_H40_PHONY(176) + SPRITE_RENDER_H40_PHONY(177)//End of border? + SPRITE_RENDER_H40_PHONY(178) + SPRITE_RENDER_H40_PHONY(179) + SPRITE_RENDER_H40_PHONY(180) + SPRITE_RENDER_H40_PHONY(181) + SPRITE_RENDER_H40_PHONY(182) + SPRITE_RENDER_H40_PHONY(229) + //!HSYNC asserted + SPRITE_RENDER_H40_PHONY(230) + SPRITE_RENDER_H40_PHONY(231) + case 232: + external_slot(context); + CHECK_LIMIT_HSYNC(232) + SPRITE_RENDER_H40_PHONY(233) + SPRITE_RENDER_H40_PHONY(234) + SPRITE_RENDER_H40_PHONY(235) + SPRITE_RENDER_H40_PHONY(236) + SPRITE_RENDER_H40_PHONY(237) + SPRITE_RENDER_H40_PHONY(238) + SPRITE_RENDER_H40_PHONY(239) + SPRITE_RENDER_H40_PHONY(240) + SPRITE_RENDER_H40_PHONY(241) + SPRITE_RENDER_H40_PHONY(242) + SPRITE_RENDER_H40_PHONY(243) //provides "garbage" for border when plane A selected + case 244: + if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } + context->hslot++; + context->cycles += h40_hsync_cycles[14]; + CHECK_ONLY //provides "garbage" for border when plane A selected + //!HSYNC high + SPRITE_RENDER_H40_PHONY(245) + SPRITE_RENDER_H40_PHONY(246) + SPRITE_RENDER_H40_PHONY(247) //provides "garbage" for border when plane B selected + SPRITE_RENDER_H40_PHONY(248) //provides "garbage" for border when plane B selected + case 249: + CHECK_LIMIT + SPRITE_RENDER_H40_PHONY(250) + case 251: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 252: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 253: + CHECK_LIMIT + SPRITE_RENDER_H40_PHONY(254) + case 255: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 0: + scan_sprite_table(context->vcounter, context);//Just a guess + //seems like the sprite table scan fills a shift register + //values are FIFO, but unused slots precede used slots + //so we set cur_slot to slot_counter and let it wrap around to + //the beginning of the list + context->cur_slot = context->slot_counter; + context->sprite_x_offset = 0; + context->sprite_draws = MAX_SPRITES_LINE; + CHECK_LIMIT + COLUMN_RENDER_BLOCK_PHONY(2, 1) + COLUMN_RENDER_BLOCK_PHONY(4, 9) + COLUMN_RENDER_BLOCK_PHONY(6, 17) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(8, 25) + COLUMN_RENDER_BLOCK_PHONY(10, 33) + COLUMN_RENDER_BLOCK_PHONY(12, 41) + COLUMN_RENDER_BLOCK_PHONY(14, 49) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(16, 57) + COLUMN_RENDER_BLOCK_PHONY(18, 65) + COLUMN_RENDER_BLOCK_PHONY(20, 73) + COLUMN_RENDER_BLOCK_PHONY(22, 81) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(24, 89) + COLUMN_RENDER_BLOCK_PHONY(26, 97) + COLUMN_RENDER_BLOCK_PHONY(28, 105) + COLUMN_RENDER_BLOCK_PHONY(30, 113) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(32, 121) + COLUMN_RENDER_BLOCK_PHONY(34, 129) + COLUMN_RENDER_BLOCK_PHONY(36, 137) + COLUMN_RENDER_BLOCK_PHONY(38, 145) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(40, 153) + case 161: + external_slot(context); + CHECK_LIMIT + case 162: + external_slot(context); + CHECK_LIMIT + //sprite render to line buffer starts + case 163: + context->cur_slot = MAX_SPRITES_LINE-1; + memset(context->linebuf, 0, LINEBUF_SIZE); + context->flags &= ~FLAG_MASKED; + while (context->sprite_draws) { + context->sprite_draws--; + context->sprite_draw_list[context->sprite_draws].x_pos = 0; + } + render_sprite_cells(context); + CHECK_LIMIT + case 164: + render_sprite_cells(context); + if (context->flags & FLAG_DMA_RUN) { + run_dma_src(context, -1); + } + context->hslot++; + context->cycles += slot_cycles; + vdp_advance_line(context); + CHECK_ONLY + } + default: + context->hslot++; + context->cycles += slot_cycles; + return; + } +} + static void vdp_h32(vdp_context * context, uint32_t target_cycles) { uint16_t address; @@ -3925,6 +4228,150 @@ } } +static void vdp_h32_phony(vdp_context * context, uint32_t target_cycles) +{ + uint32_t const slot_cycles = MCLKS_SLOT_H32; + switch(context->hslot) + { + for (;;) + { + case 133: + if (context->state == PREPARING) { + external_slot(context); + } else { + render_sprite_cells(context); + } + CHECK_LIMIT + case 134: + if (context->state == PREPARING) { + external_slot(context); + } else { + render_sprite_cells(context); + } + if (context->vcounter == context->inactive_start) { + context->hslot++; + context->cycles += slot_cycles; + return; + } + CHECK_LIMIT + //sprite attribute table scan starts + case 135: + context->sprite_index = 0x80; + context->slot_counter = 0; + render_sprite_cells(context); + scan_sprite_table(context->vcounter, context); + CHECK_LIMIT + SPRITE_RENDER_H32_PHONY(136) + SPRITE_RENDER_H32_PHONY(137) + SPRITE_RENDER_H32_PHONY(138) + SPRITE_RENDER_H32_PHONY(139) + SPRITE_RENDER_H32_PHONY(140) + SPRITE_RENDER_H32_PHONY(141) + SPRITE_RENDER_H32_PHONY(142) + SPRITE_RENDER_H32_PHONY(143) + SPRITE_RENDER_H32_PHONY(144) + case 145: + external_slot(context); + CHECK_LIMIT + SPRITE_RENDER_H32_PHONY(146) + SPRITE_RENDER_H32_PHONY(147) + SPRITE_RENDER_H32_PHONY(233) + SPRITE_RENDER_H32_PHONY(234) + SPRITE_RENDER_H32_PHONY(235) + //HSYNC start + SPRITE_RENDER_H32_PHONY(236) + SPRITE_RENDER_H32_PHONY(237) + SPRITE_RENDER_H32_PHONY(238) + SPRITE_RENDER_H32_PHONY(239) + SPRITE_RENDER_H32_PHONY(240) + SPRITE_RENDER_H32_PHONY(241) + SPRITE_RENDER_H32_PHONY(242) + case 243: + external_slot(context); + CHECK_LIMIT + case 244: + CHECK_LIMIT //provides "garbage" for border when plane A selected + SPRITE_RENDER_H32_PHONY(245) + SPRITE_RENDER_H32_PHONY(246) + SPRITE_RENDER_H32_PHONY(247) //provides "garbage" for border when plane B selected + SPRITE_RENDER_H32_PHONY(248) //provides "garbage" for border when plane B selected + //!HSYNC high + case 249: + CHECK_LIMIT + SPRITE_RENDER_H32_PHONY(250) + case 251: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 252: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 253: + CHECK_LIMIT + case 254: + render_sprite_cells(context); + scan_sprite_table(context->vcounter, context); + CHECK_LIMIT + case 255: + scan_sprite_table(context->vcounter, context);//Just a guess + CHECK_LIMIT + case 0: + scan_sprite_table(context->vcounter, context);//Just a guess + //reverse context slot counter so it counts the number of sprite slots + //filled rather than the number of available slots + //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; + context->cur_slot = context->slot_counter; + context->sprite_x_offset = 0; + context->sprite_draws = MAX_SPRITES_LINE_H32; + CHECK_LIMIT + COLUMN_RENDER_BLOCK_PHONY(2, 1) + COLUMN_RENDER_BLOCK_PHONY(4, 9) + COLUMN_RENDER_BLOCK_PHONY(6, 17) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(8, 25) + COLUMN_RENDER_BLOCK_PHONY(10, 33) + COLUMN_RENDER_BLOCK_PHONY(12, 41) + COLUMN_RENDER_BLOCK_PHONY(14, 49) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(16, 57) + COLUMN_RENDER_BLOCK_PHONY(18, 65) + COLUMN_RENDER_BLOCK_PHONY(20, 73) + COLUMN_RENDER_BLOCK_PHONY(22, 81) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(24, 89) + COLUMN_RENDER_BLOCK_PHONY(26, 97) + COLUMN_RENDER_BLOCK_PHONY(28, 105) + COLUMN_RENDER_BLOCK_PHONY(30, 113) + COLUMN_RENDER_BLOCK_REFRESH_PHONY(32, 121) + case 129: + external_slot(context); + CHECK_LIMIT + case 130: { + external_slot(context); + CHECK_LIMIT + } + //sprite render to line buffer starts + case 131: + context->cur_slot = MAX_SPRITES_LINE_H32-1; + context->flags &= ~FLAG_MASKED; + while (context->sprite_draws) { + context->sprite_draws--; + context->sprite_draw_list[context->sprite_draws].x_pos = 0; + } + render_sprite_cells(context); + CHECK_LIMIT + case 132: + render_sprite_cells(context); + if (context->flags & FLAG_DMA_RUN) { + run_dma_src(context, -1); + } + context->hslot++; + context->cycles += slot_cycles; + vdp_advance_line(context); + CHECK_ONLY + } + default: + context->hslot++; + context->cycles += MCLKS_SLOT_H32; + } +} + static void vdp_h32_mode4(vdp_context * context, uint32_t target_cycles) { uint16_t address; @@ -4954,30 +5401,172 @@ } } +static void vdp_inactive_phony(vdp_context *context, uint32_t target_cycles, uint8_t is_h40, uint8_t mode_5) +{ + uint8_t buf_clear_slot, index_reset_slot, bg_end_slot, vint_slot, line_change, jump_start, jump_dest, latch_slot; + uint8_t index_reset_value, max_draws, max_sprites; + uint16_t vint_line, active_line; + + if (mode_5) { + if (is_h40) { + latch_slot = 165; + buf_clear_slot = 163; + index_reset_slot = 167; + bg_end_slot = BG_START_SLOT + LINEBUF_SIZE/2; + max_draws = MAX_SPRITES_LINE-1; + max_sprites = MAX_SPRITES_LINE; + index_reset_value = 0x80; + vint_slot = VINT_SLOT_H40; + line_change = LINE_CHANGE_H40; + jump_start = 182; + jump_dest = 229; + } else { + bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2; + max_draws = MAX_SPRITES_LINE_H32-1; + max_sprites = MAX_SPRITES_LINE_H32; + buf_clear_slot = 128; + index_reset_slot = 132; + index_reset_value = 0x80; + vint_slot = VINT_SLOT_H32; + line_change = LINE_CHANGE_H32; + jump_start = 147; + jump_dest = 233; + latch_slot = 243; + } + vint_line = context->inactive_start; + active_line = 0x1FF; + if (context->regs[REG_MODE_3] & BIT_VSCROLL) { + latch_slot = 220; + } + } else { + latch_slot = 220; + bg_end_slot = BG_START_SLOT + (256+HORIZ_BORDER)/2; + max_draws = MAX_DRAWS_H32_MODE4; + max_sprites = 8; + buf_clear_slot = 136; + index_reset_slot = 253; + index_reset_value = 0; + vint_line = context->inactive_start + 1; + vint_slot = VINT_SLOT_MODE4; + line_change = LINE_CHANGE_MODE4; + jump_start = 147; + jump_dest = 233; + if ((context->regs[REG_MODE_1] & BIT_MODE_4) || context->type != VDP_GENESIS) { + active_line = 0x1FF; + } else { + //never active unless either mode 4 or mode 5 is turned on + active_line = 0x200; + } + } + + while(context->cycles < target_cycles) + { + check_switch_inactive(context, is_h40); + //this will need some tweaking to properly interact with 128K mode, + //but this should be good enough for now + context->serial_address += 1024; + + if (context->hslot == buf_clear_slot) { + if (mode_5) { + context->cur_slot = max_draws; + } else if ((context->regs[REG_MODE_1] & BIT_MODE_4) || context->type == VDP_GENESIS) { + context->cur_slot = context->sprite_index = MAX_DRAWS_H32_MODE4-1; + context->sprite_draws = MAX_DRAWS_H32_MODE4; + } else { + context->sprite_draws = 0; + } + memset(context->linebuf, 0, LINEBUF_SIZE); + } else if (context->hslot == index_reset_slot) { + context->sprite_index = index_reset_value; + context->slot_counter = mode_5 ? 0 : max_sprites; + } else if (context->vcounter == vint_line && context->hslot == vint_slot) { + context->flags2 |= FLAG2_VINT_PENDING; + context->pending_vint_start = context->cycles; + } else if (context->vcounter == context->inactive_start && context->hslot == 1 && (context->regs[REG_MODE_4] & BIT_INTERLACE)) { + context->flags2 ^= FLAG2_EVEN_FIELD; + } + + if (!is_refresh(context, context->hslot)) { + external_slot(context); + if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) { + run_dma_src(context, context->hslot); + } + } + + if (is_h40) { + if (context->hslot >= HSYNC_SLOT_H40 && context->hslot < HSYNC_END_H40) { + context->cycles += h40_hsync_cycles[context->hslot - HSYNC_SLOT_H40]; + } else { + context->cycles += MCLKS_SLOT_H40; + } + } else { + context->cycles += MCLKS_SLOT_H32; + } + if (context->hslot == jump_start) { + context->hslot = jump_dest; + } else { + context->hslot++; + } + if (context->hslot == line_change) { + vdp_advance_line(context); + if (context->vcounter == active_line) { + context->state = PREPARING; + return; + } + } + } +} + void vdp_run_context_full(vdp_context * context, uint32_t target_cycles) { uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40; uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5; - while(context->cycles < target_cycles) - { - check_switch_inactive(context, is_h40); - - if (is_active(context)) { - if (mode_5) { - if (is_h40) { - vdp_h40(context, target_cycles); + if (context->renderer) { + while(context->cycles < target_cycles) + { + check_switch_inactive(context, is_h40); + + if (is_active(context)) { + if (mode_5) { + if (is_h40) { + vdp_h40_phony(context, target_cycles); + } else { + vdp_h32_phony(context, target_cycles); + } + } else if (context->regs[REG_MODE_1] & BIT_MODE_4) { + //TODO: phonyfy this + vdp_h32_mode4(context, target_cycles); + } else if (context->regs[REG_MODE_2] & BIT_M1) { + vdp_tms_text(context, target_cycles); } else { - vdp_h32(context, target_cycles); + vdp_tms_graphics(context, target_cycles); } - } else if (context->regs[REG_MODE_1] & BIT_MODE_4) { - vdp_h32_mode4(context, target_cycles); - } else if (context->regs[REG_MODE_2] & BIT_M1) { - vdp_tms_text(context, target_cycles); } else { - vdp_tms_graphics(context, target_cycles); + vdp_inactive_phony(context, target_cycles, is_h40, mode_5); } - } else { - vdp_inactive(context, target_cycles, is_h40, mode_5); + } + } else { + while(context->cycles < target_cycles) + { + check_switch_inactive(context, is_h40); + + if (is_active(context)) { + if (mode_5) { + if (is_h40) { + vdp_h40(context, target_cycles); + } else { + vdp_h32(context, target_cycles); + } + } else if (context->regs[REG_MODE_1] & BIT_MODE_4) { + vdp_h32_mode4(context, target_cycles); + } else if (context->regs[REG_MODE_2] & BIT_M1) { + vdp_tms_text(context, target_cycles); + } else { + vdp_tms_graphics(context, target_cycles); + } + } else { + vdp_inactive(context, target_cycles, is_h40, mode_5); + } } } }