Improve timing conformance in Mockingboard

- Correctly calculate 7 cycles for handling IRQs
    - More frequent calls to MB_UpdateCycles()
    - Correctly calculate timing for MB_EndOfVideoFrame() when testing/tracing/debugging
This commit is contained in:
Aaron Culliney 2016-09-24 11:41:30 -07:00
parent 4fd6a87340
commit ccd05e52fe
5 changed files with 71 additions and 22 deletions

View File

@ -74,13 +74,12 @@
#define CPUStatsReset \
SYM(r1, cpu65_opcode); \
strb r0, [r1]; /* r0 should be next opcode */ \
eor r9, r9, r9; \
SYM(r1, cpu65_opcycles); \
strb r9, [r1]; \
SYM(r1, cpu65_rw); \
strb r9, [r1];
#define CPUStatsSetRead \
SYM(r1, cpu65_rw); \
ldrb r9, [r1]; \
@ -153,7 +152,8 @@
#define JumpNextInstruction \
TRACE_PROLOGUE \
GetFromPC_B \
CPUStatsReset \
SYM(r1, cpu65_opcode); \
strb r0, [r1]; /* r0 should be next opcode */ \
SYM(r1, cpu65__opcodes); \
ldr r1, [r1, r0, LSL PTR_SHIFT]; \
bx r1;
@ -2431,19 +2431,33 @@ continue:
add r9, r9, cycles_exe
str r9, [r1]
SYM(r1, cpu65_cycles_to_execute)
SYM(r1, irqCheckTimeout) // AppleWin : CheckInterruptSources()
ldr r9, [r1]
subs r9, r9, cycles_exe
str r9, [r1]
bmi irq_checkpoint
continue1: SYM(r1, cpu65_cycles_to_execute)
ldr r9, [r1]
subs r9, r9, cycles_exe
str r9, [r1]
bmi exit_cpu65_run
beq exit_cpu65_run
continue1: SYM(r1, cpu65__signal)
continue2: SYM(r1, cpu65__signal)
ldrb r0, [r1]
orrs r0, r0, r0
bne exception
CPUStatsReset
JumpNextInstruction
irq_checkpoint:
bl CALL(cpu_irqCheck);
SYM(r1, irqCheckTimeout)
eor r9, r9, r9
str r9, [r1]
b continue1
/* -------------------------------------------------------------------------
Exception handlers
------------------------------------------------------------------------- */
@ -2466,10 +2480,12 @@ ex_reset: eor r0, r0, r0
ldrh EffectiveAddr, [EffectiveAddr]
GetFromEA_W
mov PC_Reg, r0
CPUStatsReset
JumpNextInstruction
ex_irq: tst F_Reg, #I_Flag // Already interrupted?
beq 1f
CPUStatsReset
JumpNextInstruction // Yes (ignored) ...
1: TRACE_IRQ // No (handle IRQ) ...
mov r0, PC_Reg
@ -2486,6 +2502,11 @@ ex_irq: tst F_Reg, #I_Flag // Already interrupt
ldrh EffectiveAddr, [EffectiveAddr]
GetFromEA_W
mov PC_Reg, r0
CPUStatsReset
SYM(r1, cpu65_opcycles)
ldrb r0, [r1]
add r0, r0, #7 // IRQ handling will take additional 7 cycles
strb r0, [r1]
JumpNextInstruction
/* -------------------------------------------------------------------------
@ -2517,7 +2538,7 @@ ENTRY(cpu65_run)
eorne r0, r0, r0
STRBNE r0, [r1]
bne ex_reset
b continue1
b continue2
/* -------------------------------------------------------------------------
65c02 CPU processing loop exit point

View File

@ -73,6 +73,8 @@ void cpu65_trace_checkpoint(void);
#endif /* !__ASSEMBLER__ */
#define IRQ_CHECK_CYCLES 128
#define ResetSig 0x02
#define IRQ6522 0x08
#define IRQSpeech 0x10

View File

@ -54,6 +54,7 @@ unsigned long cycles_count_total = 0; // Running at spec ~1MHz, this w
int cycles_speaker_feedback = 0;
int32_t cpu65_cycles_to_execute = 0; // cycles-to-execute by cpu65_run()
int32_t cpu65_cycle_count = 0; // cycles currently excuted by cpu65_run()
int32_t irqCheckTimeout = IRQ_CHECK_CYCLES;
static int32_t cycles_checkpoint_count = 0;
static unsigned int g_dwCyclesThisFrame = 0;
@ -147,6 +148,8 @@ void reinitialize(void) {
#endif
cycles_count_total = 0;
g_dwCyclesThisFrame = 0;
irqCheckTimeout = IRQ_CHECK_CYCLES;
#if TESTING
extern unsigned long (*testing_getCyclesCount)(void);
if (testing_getCyclesCount) {
@ -343,7 +346,7 @@ cpu_runloop:
MB_StartOfCpuExecute();
if (is_debugging) {
debugging_cycles = cpu65_cycles_to_execute;
debugging_cycles = cpu65_cycles_to_execute;
}
do {
@ -354,10 +357,15 @@ cpu_runloop:
cpu65_cycle_count = 0;
cycles_checkpoint_count = 0;
cpu65_run(); // run emulation for cpu65_cycles_to_execute cycles ...
#if DEBUG_TIMING
dbg_cycles_executed += cpu65_cycle_count;
#endif
g_dwCyclesThisFrame += cpu65_cycle_count;
if (is_debugging) {
debugging_cycles -= cpu65_cycle_count;
timing_checkpoint_cycles();
if (c_debugger_should_break() || (debugging_cycles <= 0)) {
int err = 0;
if ((err = pthread_cond_signal(&dbg_thread_cond))) {
@ -370,18 +378,15 @@ cpu_runloop:
break;
}
}
if (emul_reinitialize) {
pthread_mutex_unlock(&interface_mutex);
goto cpu_runloop;
}
}
} while (is_debugging);
#if DEBUG_TIMING
dbg_cycles_executed += cpu65_cycle_count;
#endif
g_dwCyclesThisFrame += cpu65_cycle_count;
MB_UpdateCycles(); // update 6522s (NOTE: do this before updating cycles_count_total)
MB_UpdateCycles();
timing_checkpoint_cycles();
@ -536,6 +541,8 @@ void timing_stopCPU(void) {
}
unsigned int CpuGetCyclesThisVideoFrame(void) {
assert(pthread_self() == cpu_thread_id);
timing_checkpoint_cycles();
return g_dwCyclesThisFrame + cycles_checkpoint_count;
}
@ -546,11 +553,11 @@ void timing_checkpoint_cycles(void) {
const int32_t d = cpu65_cycle_count - cycles_checkpoint_count;
assert(d >= 0);
#if TESTING
unsigned long previous_cycles_count_total = cycles_count_total;
#endif
#if !TESTING
cycles_count_total += d;
#else
unsigned long previous_cycles_count_total = cycles_count_total;
cycles_count_total += d;
#if TESTING
if (UNLIKELY(cycles_count_total < previous_cycles_count_total)) {
extern void (*testing_cyclesOverflow)(void);
if (testing_cyclesOverflow) {

View File

@ -918,6 +918,12 @@ GLUE_C_READ(debug_illegal_bcd)
return 0;
}
GLUE_C_WRITE(cpu_irqCheck)
{
MB_UpdateCycles();
// TODO : other peripheral card interrupt handling
}
// ----------------------------------------------------------------------------
static void _initialize_iie_switches(void) {

View File

@ -90,12 +90,14 @@
CALL_IND(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR); \
TRACE_ARG1;
#define CPUStatsReset \
REG2MEM(movb, $0, cpu65_opcycles); \
REG2MEM(movb, $0, cpu65_rw);
#define JumpNextInstruction \
TRACE_PROLOGUE; \
GetFromPC_B \
REG2MEM(movb, %al, cpu65_opcode); \
REG2MEM(movb, $0, cpu65_opcycles); \
REG2MEM(movb, $0, cpu65_rw); \
JUMP_IND(cpu65__opcodes,_XAX,SZ_PTR);
#define GetFromEA_B \
@ -2160,14 +2162,21 @@ continue:
REG2MEM(addl, %eax, cpu65_cycle_count)
REG2MEM(subl, %eax, gc_cycles_timer_0)
REG2MEM(subl, %eax, gc_cycles_timer_1)
REG2MEM(subl, %eax, cpu65_cycles_to_execute)
REG2MEM(subl, %eax, irqCheckTimeout)
jl irq_checkpoint // AppleWin : CheckInterruptSources()
continue1: REG2MEM(subl, %eax, cpu65_cycles_to_execute)
jle exit_cpu65_run
continue1: xorLQ _XAX, _XAX
continue2: xorLQ _XAX, _XAX
MEM2REG(orb, cpu65__signal, %al)
jnz exception
CPUStatsReset
JumpNextInstruction
irq_checkpoint:
CALL_FN(callLQ, cpu_irqCheck, 0x4)
REG2MEM(movl, $IRQ_CHECK_CYCLES, irqCheckTimeout)
jmp continue1
/* -------------------------------------------------------------------------
Exception handlers
------------------------------------------------------------------------- */
@ -2184,10 +2193,12 @@ ex_reset: REG2MEM(movb, $0, cpu65__signal)
GetFromEA_W
movw %ax, PC_Reg
xorb %ah, %ah
CPUStatsReset
JumpNextInstruction
ex_irq: testb $I_Flag, F_Reg // Already interrupted?
jz 1f
CPUStatsReset
JumpNextInstruction // Yes (ignored) ...
1: TRACE_IRQ // No (handle IRQ) ...
movw PC_Reg, %ax
@ -2210,6 +2221,8 @@ ex_irq: testb $I_Flag, F_Reg // Already interrupt
GetFromEA_W
movw %ax, PC_Reg
xorb %ah, %ah
CPUStatsReset
REG2MEM(addb, $7, cpu65_opcycles); // IRQ handling will take additional 7 cycles
JumpNextInstruction
/* -------------------------------------------------------------------------
@ -2256,7 +2269,7 @@ ENTRY(cpu65_run)
#endif
REG2MEM(cmpb, $0, emul_reinitialize)
jnz enter_reinit
jmp continue1
jmp continue2
enter_reinit: REG2MEM(movb, $0, emul_reinitialize)
jmp ex_reset