# HG changeset patch # User Michael Pavone # Date 1662441505 25200 # Node ID eaaf28af3c94473ff70f229451f8aaed2da1910d # Parent d15c68157288a270876a133936d3341ecf19302c Implement VDP read latency and invalid write delays revealed by Ti_'s instruction timing ROM diff -r d15c68157288 -r eaaf28af3c94 genesis.c --- a/genesis.c Mon Sep 05 12:00:02 2022 -0700 +++ b/genesis.c Mon Sep 05 22:18:25 2022 -0700 @@ -634,7 +634,7 @@ } else if(vdp_port < 8) { vdp_run_context_full(v_context, context->current_cycle); before_cycle = v_context->cycles; - blocked = vdp_control_port_write(v_context, value); + blocked = vdp_control_port_write(v_context, value, context->current_cycle); if (blocked) { while (blocked) { while(v_context->flags & FLAG_DMA_RUN) { @@ -653,7 +653,7 @@ } if (blocked < 0) { - blocked = vdp_control_port_write(v_context, value); + blocked = vdp_control_port_write(v_context, value, context->current_cycle); } else { blocked = 0; } @@ -719,7 +719,7 @@ vdp_data_port_write(gen->vdp, value << 8 | value); } else if (vdp_port < 8) { vdp_run_context_full(gen->vdp, context->Z80_CYCLE); - vdp_control_port_write(gen->vdp, value << 8 | value); + vdp_control_port_write(gen->vdp, value << 8 | value, context->Z80_CYCLE); } else { fatal_error("Illegal write to HV Counter port %X\n", vdp_port); } @@ -752,10 +752,10 @@ #endif sync_components(context, 0); vdp_context * v_context = gen->vdp; - uint32_t before_cycle = v_context->cycles; + uint32_t before_cycle = context->current_cycle; if (vdp_port < 0x10) { if (vdp_port < 4) { - value = vdp_data_port_read(v_context); + value = vdp_data_port_read(v_context, &context->current_cycle, MCLKS_PER_68K); } else if(vdp_port < 8) { value = vdp_control_port_read(v_context); } else { @@ -767,13 +767,12 @@ } else { value = get_open_bus_value(&gen->header); } - if (v_context->cycles != before_cycle) { + if (context->current_cycle != before_cycle) { //printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); - context->current_cycle = v_context->cycles; //Lock the Z80 out of the bus until the VDP access is complete genesis_context *gen = context->system; gen->bus_busy = 1; - sync_z80(gen, v_context->cycles); + sync_z80(gen, context->current_cycle); gen->bus_busy = 0; } #ifdef REFRESH_EMULATION @@ -818,13 +817,17 @@ if (vdp_port < 0x10) { //These probably won't currently interact well with the 68K accessing the VDP vdp_run_context(gen->vdp, context->Z80_CYCLE); + uint32_t before = context->Z80_CYCLE; if (vdp_port < 4) { - ret = vdp_data_port_read(gen->vdp); + ret = vdp_data_port_read(gen->vdp, &context->Z80_CYCLE, MCLKS_PER_Z80); } else if (vdp_port < 8) { ret = vdp_control_port_read(gen->vdp); } else { ret = vdp_hv_counter_read(gen->vdp); } + if (context->Z80_CYCLE != before) { + gen->m68k->current_cycle += context->Z80_CYCLE - before; + } } else { //TODO: Figure out the correct value today ret = 0xFFFF; diff -r d15c68157288 -r eaaf28af3c94 vdp.c --- a/vdp.c Mon Sep 05 12:00:02 2022 -0700 +++ b/vdp.c Mon Sep 05 22:18:25 2022 -0700 @@ -40,6 +40,7 @@ #define VBLANK_START_H40 (LINE_CHANGE_H40+2) #define VBLANK_START_H32 (LINE_CHANGE_H32+2) #define FIFO_LATENCY 3 +#define READ_LATENCY 3 #define BORDER_TOP_V24 27 #define BORDER_TOP_V28 11 @@ -1019,6 +1020,11 @@ } break; + default: + if (!(context->cd & 4) && !start->partial && (context->regs[REG_MODE_2] & (BIT_128K_VRAM|BIT_MODE_5)) != (BIT_128K_VRAM|BIT_MODE_5)) { + start->partial = 1; + return; + } } context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1); if (context->fifo_read == context->fifo_write) { @@ -1040,7 +1046,7 @@ context->flags |= FLAG_READ_FETCHED; } - } else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING))) { + } else if (!(context->cd & 1) && !(context->flags & (FLAG_READ_FETCHED|FLAG_PENDING)) && context->read_latency <= context->cycles) { switch(context->cd & 0xF) { case VRAM_READ: @@ -3785,7 +3791,7 @@ return hv; } -int vdp_control_port_write(vdp_context * context, uint16_t value) +int vdp_control_port_write(vdp_context * context, uint16_t value, uint32_t cpu_cycle) { //printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { @@ -3805,6 +3811,9 @@ //Should these be taken care of here or after the first write? context->flags &= ~FLAG_READ_FETCHED; context->flags2 &= ~FLAG2_READ_PENDING; + if (!(context->cd & 1)) { + context->read_latency = cpu_cycle + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*READ_LATENCY; + } //printf("New Address: %X, New CD: %X\n", context->address, context->cd); if (context->cd & 0x20) { // @@ -3918,7 +3927,7 @@ uint16_t full_val = value << 8 | context->pending_byte; context->flags2 &= ~FLAG2_BYTE_PENDING; //TODO: Deal with fact that Vbus->VDP DMA doesn't do anything in PBC mode - vdp_control_port_write(context, full_val); + vdp_control_port_write(context, full_val, context->cycles); if (context->cd == VRAM_READ) { context->cd = VRAM_READ8; } @@ -4056,7 +4065,7 @@ return value; } -uint16_t vdp_data_port_read(vdp_context * context) +uint16_t vdp_data_port_read(vdp_context * context, uint32_t *cpu_cycle, uint32_t cpu_divider) { if (context->flags & FLAG_PENDING) { context->flags &= ~FLAG_PENDING; @@ -4081,10 +4090,27 @@ context->system->enter_debugger = 1; return context->prefetch; } + uint32_t starting_cycle = context->cycles; while (!(context->flags & FLAG_READ_FETCHED)) { vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)); } context->flags &= ~FLAG_READ_FETCHED; + //TODO: Make some logic analyzer captures to better characterize what's happening with read latency here + if (context->cycles != starting_cycle) { + uint32_t delta = context->cycles - *cpu_cycle; + uint32_t cpu_delta = delta / cpu_divider; + if (delta % cpu_divider) { + cpu_delta++; + } + *cpu_cycle += cpu_delta * cpu_divider; + if (*cpu_cycle - context->cycles < 2) { + context->read_latency = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*(READ_LATENCY - 1); + } else { + context->read_latency = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*READ_LATENCY; + } + } else { + context->read_latency = *cpu_cycle + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*(READ_LATENCY - 1); + } return context->prefetch; } @@ -4121,6 +4147,11 @@ idx = (idx+1) & (FIFO_SIZE-1); } while(idx != context->fifo_write); } + if (context->read_latency >= deduction) { + context->read_latency -= deduction; + } else { + context->read_latency = 0; + } } static uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context) diff -r d15c68157288 -r eaaf28af3c94 vdp.h --- a/vdp.h Mon Sep 05 12:00:02 2022 -0700 +++ b/vdp.h Mon Sep 05 22:18:25 2022 -0700 @@ -205,6 +205,7 @@ uint32_t pending_vint_start; uint32_t pending_hint_start; uint32_t top_offset; + uint32_t read_latency; uint16_t vsram[MAX_VSRAM_SIZE]; uint16_t vscroll_latch[2]; uint16_t vcounter; @@ -270,13 +271,13 @@ void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles); uint8_t vdp_load_gst(vdp_context * context, FILE * state_file); uint8_t vdp_save_gst(vdp_context * context, FILE * outfile); -int vdp_control_port_write(vdp_context * context, uint16_t value); +int vdp_control_port_write(vdp_context * context, uint16_t value, uint32_t cpu_cycle); void vdp_control_port_write_pbc(vdp_context * context, uint8_t value); int vdp_data_port_write(vdp_context * context, uint16_t value); void vdp_data_port_write_pbc(vdp_context * context, uint8_t value); void vdp_test_port_write(vdp_context * context, uint16_t value); uint16_t vdp_control_port_read(vdp_context * context); -uint16_t vdp_data_port_read(vdp_context * context); +uint16_t vdp_data_port_read(vdp_context * context, uint32_t *cpu_cycle, uint32_t cpu_divider); uint8_t vdp_data_port_read_pbc(vdp_context * context); void vdp_latch_hv(vdp_context *context); uint16_t vdp_hv_counter_read(vdp_context * context);