mirror of
https://github.com/mauiaaron/apple2.git
synced 2025-01-10 23:29:43 +00:00
First cut at a facility to do CPU tracing
* Also includes a test that traces booting the test disk
This commit is contained in:
parent
815ee171fc
commit
4c9c1fb62a
@ -83,7 +83,7 @@ src/font.c: src/font.txt genfont
|
|||||||
src/rom.c: genrom
|
src/rom.c: genrom
|
||||||
./genrom src/rom/apple_IIe.rom src/rom/slot6.rom > $@
|
./genrom src/rom/apple_IIe.rom src/rom/slot6.rom > $@
|
||||||
|
|
||||||
src/x86/glue.S: src/disk.c src/misc.c src/display.c src/vm.c @AUDIO_GLUE_C@
|
src/x86/glue.S: src/disk.c src/misc.c src/display.c src/vm.c src/cpu-supp.c @AUDIO_GLUE_C@
|
||||||
./src/x86/genglue $^ > $@
|
./src/x86/genglue $^ > $@
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
147
src/cpu-supp.c
147
src/cpu-supp.c
@ -41,6 +41,13 @@ uint8_t cpu65_flags_decode[256] = { 0 };
|
|||||||
void *cpu65_vmem_r[0x10000] = { 0 };
|
void *cpu65_vmem_r[0x10000] = { 0 };
|
||||||
void *cpu65_vmem_w[0x10000] = { 0 };
|
void *cpu65_vmem_w[0x10000] = { 0 };
|
||||||
|
|
||||||
|
#if CPU_TRACING
|
||||||
|
static int8_t opargs[3] = { 0 };
|
||||||
|
static int8_t nargs = 0;
|
||||||
|
static uint16_t current_pc = 0x0;
|
||||||
|
static FILE *cpu_trace_fp = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// 65c02 Opcode Jump Table
|
// 65c02 Opcode Jump Table
|
||||||
|
|
||||||
@ -639,3 +646,143 @@ void cpu65_uninterrupt(int reason)
|
|||||||
pthread_mutex_unlock(&irq_mutex);
|
pthread_mutex_unlock(&irq_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CPU_TRACING
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
CPU Tracing routines
|
||||||
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void cpu65_trace_begin(const char *trace_file) {
|
||||||
|
if (trace_file) {
|
||||||
|
cpu_trace_fp = fopen(trace_file, "w");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu65_trace_end(void) {
|
||||||
|
if (cpu_trace_fp) {
|
||||||
|
fflush(cpu_trace_fp);
|
||||||
|
fclose(cpu_trace_fp);
|
||||||
|
cpu_trace_fp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLUE_C_WRITE(cpu65_trace_prologue)
|
||||||
|
{
|
||||||
|
nargs = 0;
|
||||||
|
current_pc = cpu65_pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLUE_C_WRITE(cpu65_trace_arg)
|
||||||
|
{
|
||||||
|
assert(nargs <= 2);
|
||||||
|
opargs[nargs++] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLUE_C_WRITE(cpu65_trace_arg1)
|
||||||
|
{
|
||||||
|
assert(nargs <= 2);
|
||||||
|
opargs[2] = b;
|
||||||
|
++nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLUE_C_WRITE(cpu65_trace_arg2)
|
||||||
|
{
|
||||||
|
assert(nargs <= 2);
|
||||||
|
opargs[1] = b;
|
||||||
|
++nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLUE_C_WRITE(cpu65_trace_epilogue)
|
||||||
|
{
|
||||||
|
char fmt[64];
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
int8_t arg1 = opargs[1];
|
||||||
|
int8_t arg2 = opargs[2];
|
||||||
|
|
||||||
|
if (!cpu_trace_fp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nargs > 0);
|
||||||
|
assert(nargs <= 3);
|
||||||
|
|
||||||
|
#warning FIXME TODO ... need to refactor this and the debugger routines to use the same codepaths ...
|
||||||
|
switch (opcodes_65c02[cpu65_opcode].mode) {
|
||||||
|
case addr_implied:
|
||||||
|
case addr_accumulator: /* no arg */
|
||||||
|
sprintf(buf, "%04X: %02X %s %s", current_pc, cpu65_opcode, opcodes_65c02[cpu65_opcode].mnemonic, disasm_templates[opcodes_65c02[cpu65_opcode].mode]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case addr_immediate:
|
||||||
|
case addr_zeropage:
|
||||||
|
case addr_zeropage_x:
|
||||||
|
case addr_zeropage_y:
|
||||||
|
case addr_indirect:
|
||||||
|
case addr_indirect_x:
|
||||||
|
case addr_indirect_y: /* byte arg */
|
||||||
|
sprintf(fmt, "%04X: %02X%02X %s %s", current_pc, cpu65_opcode, (uint8_t)arg1, opcodes_65c02[cpu65_opcode].mnemonic, disasm_templates[opcodes_65c02[cpu65_opcode].mode]);
|
||||||
|
sprintf(buf, fmt, (uint8_t)arg1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case addr_absolute:
|
||||||
|
case addr_absolute_x:
|
||||||
|
case addr_absolute_y:
|
||||||
|
case addr_j_indirect:
|
||||||
|
case addr_j_indirect_x: /* word arg */
|
||||||
|
sprintf(fmt, "%04X: %02X%02X%02X %s %s", current_pc, cpu65_opcode, (uint8_t)arg1, (uint8_t)arg2, opcodes_65c02[cpu65_opcode].mnemonic, disasm_templates[opcodes_65c02[cpu65_opcode].mode]);
|
||||||
|
sprintf(buf, fmt, (uint8_t)arg2, (uint8_t)arg1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case addr_relative: /* offset */
|
||||||
|
sprintf(fmt, "%04X: %02X%02X %s %s", current_pc, cpu65_opcode, (uint8_t)arg1, opcodes_65c02[cpu65_opcode].mnemonic, disasm_templates[opcodes_65c02[cpu65_opcode].mode]);
|
||||||
|
if (arg1 < 0) {
|
||||||
|
sprintf(buf, fmt, current_pc + arg1 + 2, '-', (uint8_t)(-arg1));
|
||||||
|
} else {
|
||||||
|
sprintf(buf, fmt, current_pc + arg1 + 2, '+', (uint8_t)arg1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* shouldn't happen */
|
||||||
|
sprintf(buf, "invalid opcode mode");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char regs_buf[64];
|
||||||
|
sprintf(regs_buf, "EA:%04X SP:%02X X:%02X Y:%02X A:%02X", cpu65_ea, cpu65_sp, cpu65_x, cpu65_y, cpu65_a);
|
||||||
|
|
||||||
|
#define FLAGS_BUFSZ 9
|
||||||
|
char flags_buf[FLAGS_BUFSZ];
|
||||||
|
memset(flags_buf, '-', FLAGS_BUFSZ);
|
||||||
|
if (cpu65_f & C_Flag_6502) {
|
||||||
|
flags_buf[0]='C';
|
||||||
|
}
|
||||||
|
if (cpu65_f & X_Flag_6502) {
|
||||||
|
flags_buf[1]='X';
|
||||||
|
}
|
||||||
|
if (cpu65_f & I_Flag_6502) {
|
||||||
|
flags_buf[2]='I';
|
||||||
|
}
|
||||||
|
if (cpu65_f & V_Flag_6502) {
|
||||||
|
flags_buf[3]='V';
|
||||||
|
}
|
||||||
|
if (cpu65_f & B_Flag_6502) {
|
||||||
|
flags_buf[4]='B';
|
||||||
|
}
|
||||||
|
if (cpu65_f & D_Flag_6502) {
|
||||||
|
flags_buf[5]='D';
|
||||||
|
}
|
||||||
|
if (cpu65_f & Z_Flag_6502) {
|
||||||
|
flags_buf[6]='Z';
|
||||||
|
}
|
||||||
|
if (cpu65_f & N_Flag_6502) {
|
||||||
|
flags_buf[7]='N';
|
||||||
|
}
|
||||||
|
flags_buf[8] = '\0';
|
||||||
|
|
||||||
|
fprintf(cpu_trace_fp, "%s %s %s\n", buf, regs_buf, flags_buf);
|
||||||
|
fflush(cpu_trace_fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CPU_TRACING
|
||||||
|
|
||||||
|
@ -56,6 +56,11 @@ extern unsigned char cpu65_flags_decode[256];
|
|||||||
extern int16_t cpu65_cycle_count;
|
extern int16_t cpu65_cycle_count;
|
||||||
extern int16_t cpu65_cycles_to_execute;
|
extern int16_t cpu65_cycles_to_execute;
|
||||||
|
|
||||||
|
#if CPU_TRACING
|
||||||
|
void cpu65_trace_begin(const char *trace_file);
|
||||||
|
void cpu65_trace_end(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* !__ASSEMBLER__ */
|
#endif /* !__ASSEMBLER__ */
|
||||||
|
|
||||||
#define ResetSig 0x02
|
#define ResetSig 0x02
|
||||||
|
@ -121,6 +121,49 @@ TEST test_boot_disk_bytes() {
|
|||||||
PASS();
|
PASS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test is fairly abusive ... it creates an ~88MB file in $HOME
|
||||||
|
// ... but if it's correct, you're fairly assured the cpu/vm is working =)
|
||||||
|
#define EXPECTED_CPU_TRACE_FILE_SIZE 87611579
|
||||||
|
#define EXPECTED_CPU_TRACE_SHA "8DE74ED640E0CE4AB1AAC40E95BE9B8507A37434"
|
||||||
|
TEST test_boot_disk_cputrace() {
|
||||||
|
setup_boot_disk();
|
||||||
|
|
||||||
|
char *homedir = getenv("HOME");
|
||||||
|
char *output = NULL;
|
||||||
|
asprintf(&output, "%s/a2_cputrace.raw", homedir);
|
||||||
|
if (output) {
|
||||||
|
unlink(output);
|
||||||
|
cpu65_trace_begin(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOT_TO_DOS();
|
||||||
|
|
||||||
|
cpu65_trace_end();
|
||||||
|
c_eject_6(0);
|
||||||
|
|
||||||
|
do {
|
||||||
|
uint8_t md[SHA_DIGEST_LENGTH];
|
||||||
|
char mdstr[(SHA_DIGEST_LENGTH*2)+1];
|
||||||
|
|
||||||
|
FILE *fp = fopen(output, "r");
|
||||||
|
char *buf = malloc(EXPECTED_CPU_TRACE_FILE_SIZE);
|
||||||
|
if (fread(buf, 1, EXPECTED_CPU_TRACE_FILE_SIZE, fp) != EXPECTED_CPU_TRACE_FILE_SIZE) {
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
fclose(fp); fp = NULL;
|
||||||
|
SHA1(buf, EXPECTED_CPU_TRACE_FILE_SIZE, md);
|
||||||
|
FREE(buf);
|
||||||
|
|
||||||
|
sha1_to_str(md, mdstr);
|
||||||
|
ASSERT(strcmp(mdstr, EXPECTED_CPU_TRACE_SHA) == 0);
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
unlink(output);
|
||||||
|
FREE(output);
|
||||||
|
|
||||||
|
PASS();
|
||||||
|
}
|
||||||
|
|
||||||
TEST test_boot_disk() {
|
TEST test_boot_disk() {
|
||||||
setup_boot_disk();
|
setup_boot_disk();
|
||||||
|
|
||||||
@ -3294,6 +3337,8 @@ GREATEST_SUITE(test_suite_vm) {
|
|||||||
|
|
||||||
RUN_TESTp(test_boot_disk_bytes);
|
RUN_TESTp(test_boot_disk_bytes);
|
||||||
|
|
||||||
|
RUN_TESTp(test_boot_disk_cputrace);
|
||||||
|
|
||||||
RUN_TESTp(test_boot_disk);
|
RUN_TESTp(test_boot_disk);
|
||||||
|
|
||||||
RUN_TESTp(test_read_keyboard);
|
RUN_TESTp(test_read_keyboard);
|
||||||
|
@ -24,6 +24,25 @@
|
|||||||
#define DebugCurrOpcode SN(cpu65_opcode)
|
#define DebugCurrOpcode SN(cpu65_opcode)
|
||||||
#define DebugCycleCount SN(cpu65_opcycles)
|
#define DebugCycleCount SN(cpu65_opcycles)
|
||||||
|
|
||||||
|
#if CPU_TRACING
|
||||||
|
# define TRACE_PROLOGUE \
|
||||||
|
callLQ cpu65_trace_prologue;
|
||||||
|
# define TRACE_ARG \
|
||||||
|
callLQ cpu65_trace_arg;
|
||||||
|
# define TRACE_ARG1 \
|
||||||
|
callLQ cpu65_trace_arg1;
|
||||||
|
# define TRACE_ARG2 \
|
||||||
|
callLQ cpu65_trace_arg2;
|
||||||
|
# define TRACE_EPILOGUE \
|
||||||
|
callLQ cpu65_trace_epilogue;
|
||||||
|
#else
|
||||||
|
# define TRACE_PROLOGUE
|
||||||
|
# define TRACE_ARG
|
||||||
|
# define TRACE_ARG1
|
||||||
|
# define TRACE_ARG2
|
||||||
|
# define TRACE_EPILOGUE
|
||||||
|
#endif
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------
|
/* -------------------------------------------------------------------------
|
||||||
CPU (6502) Helper Routines
|
CPU (6502) Helper Routines
|
||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
@ -32,7 +51,8 @@
|
|||||||
movLQ PC_Reg_X, EffectiveAddr_X; \
|
movLQ PC_Reg_X, EffectiveAddr_X; \
|
||||||
incw PC_Reg; \
|
incw PC_Reg; \
|
||||||
SNX_PROLOGUE(cpu65_vmem_r); \
|
SNX_PROLOGUE(cpu65_vmem_r); \
|
||||||
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR);
|
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR); \
|
||||||
|
TRACE_ARG;
|
||||||
|
|
||||||
#define GetFromPC_W \
|
#define GetFromPC_W \
|
||||||
movLQ PC_Reg_X, EffectiveAddr_X; \
|
movLQ PC_Reg_X, EffectiveAddr_X; \
|
||||||
@ -41,11 +61,14 @@
|
|||||||
SNX_PROLOGUE(cpu65_vmem_r); \
|
SNX_PROLOGUE(cpu65_vmem_r); \
|
||||||
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR); \
|
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR); \
|
||||||
decw EffectiveAddr; \
|
decw EffectiveAddr; \
|
||||||
|
TRACE_ARG2; \
|
||||||
movb %al, %ah; \
|
movb %al, %ah; \
|
||||||
SNX_PROLOGUE(cpu65_vmem_r); \
|
SNX_PROLOGUE(cpu65_vmem_r); \
|
||||||
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR);
|
callLQ *SNX(cpu65_vmem_r,EffectiveAddr_X,SZ_PTR); \
|
||||||
|
TRACE_ARG1;
|
||||||
|
|
||||||
#define JumpNextInstruction \
|
#define JumpNextInstruction \
|
||||||
|
TRACE_PROLOGUE; \
|
||||||
GetFromPC_B \
|
GetFromPC_B \
|
||||||
movb %al, DebugCurrOpcode; \
|
movb %al, DebugCurrOpcode; \
|
||||||
movb $0, DebugCycleCount; \
|
movb $0, DebugCycleCount; \
|
||||||
@ -2105,6 +2128,7 @@ continue:
|
|||||||
movb SNX(cpu65__opcycles,_XAX,1), %al
|
movb SNX(cpu65__opcycles,_XAX,1), %al
|
||||||
addb DebugCycleCount, %al
|
addb DebugCycleCount, %al
|
||||||
movb %al, DebugCycleCount
|
movb %al, DebugCycleCount
|
||||||
|
TRACE_EPILOGUE
|
||||||
addw %ax, SN(cpu65_cycle_count)
|
addw %ax, SN(cpu65_cycle_count)
|
||||||
subl %eax, SN(gc_cycles_timer_0)
|
subl %eax, SN(gc_cycles_timer_0)
|
||||||
subl %eax, SN(gc_cycles_timer_1)
|
subl %eax, SN(gc_cycles_timer_1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user