OXYGENHUB
# 2980b7b6d7cd enhanced-x16-emulator / tests / support / cpu_fixture.h
4.9 KiB · Objective-C · last changed chore(): initial commit 25 days ago source blame history raw
1
// Test fixture for the CPU core: a flat 64K memory and the handful of symbols
2
// fake6502.c expects the rest of the emulator to provide.
3
//
4
// The CPU is the cheapest part of the machine to test. fake6502.c defines its
5
// own registers, cycle counter and run state, includes no memory.h, and reaches
6
// outside itself for only six symbols -- all supplied here. So a CPU test links
7
// the real instruction tables against this flat memory and nothing else: no
8
// SDL, no ROM, no banking, no devices.
9
//
10
// One fixture covers all three instruction sets. There is no separate 65C816
11
// core: fake6502.c holds both optable_c02[] and optable_c816[] and swaps a
12
// single pointer inside reset6502(), so which CPU a scenario runs on is a
13
// parameter here rather than a different harness.
14
15
#ifndef X16_TEST_CPU_FIXTURE_H
16
#define X16_TEST_CPU_FIXTURE_H
17
18
#include <stdbool.h>
19
#include <stdint.h>
20
21
#include "cpu/fake6502.h"
22
#include "cpu/registers.h"
23
24
// registers.h defines the type but not the object, and the only declaration of
25
// it lives in glue.h, which reaches SDL. code_map.c and debug_core.c each
26
// declare it locally for the same reason; this does the same.
27
extern struct regs regs;
28
29
// The four CPU configurations the emulator can be in. Three of them change how
30
// wide a register is, which changes what an instruction does with it.
31
typedef enum {
32
	CPU_65C02,
33
	CPU_816_EMU,       // 65C816, emulation mode
34
	CPU_816_NATIVE_8,  // 65C816, native mode, 8-bit A/X/Y
35
	CPU_816_NATIVE_16, // 65C816, native mode, 16-bit A/X/Y
36
} cpu_mode_t;
37
38
const char *cpu_mode_name(cpu_mode_t mode);
39
40
// True where the mode treats A/X/Y as 16 bits.
41
bool cpu_mode_is_16bit(cpu_mode_t mode);
42
43
// The machine's memory, exposed directly so a test can seed operands and read
44
// results back without going through the CPU.
45
//
46
// 16 MB, indexed by the 65C816's full 24-bit address: (bank << 16) | offset.
47
// Bank 0 is the first 64K, so a 65C02 test indexes it exactly as before.
48
#define CPU_MEM_SIZE 0x1000000
49
extern uint8_t cpu_mem[CPU_MEM_SIZE];
50
51
// Seed a byte anywhere in the 24-bit space, recording it so the next reset
52
// clears it again. Writing cpu_mem[] directly is fine within bank 0, which is
53
// always cleared; outside it, use this or the byte survives into the next test.
54
void cpu_seed(uint32_t addr, uint8_t value);
55
56
// Zero memory and counters, point the reset vector at `start`, and reset the
57
// CPU into `mode`. Every scenario begins here, so one test cannot inherit
58
// state from the one before it.
59
void cpu_reset_to(cpu_mode_t mode, uint16_t start);
60
61
// Copy a block into memory, returning the address just past it so successive
62
// pieces can be laid down without recomputing offsets.
63
uint16_t cpu_load(uint16_t addr, const uint8_t *bytes, uint16_t len);
64
65
// Execute exactly `count` instructions.
66
void cpu_steps(int count);
67
68
// Execute one instruction and return what it cost. Timing assertions want this
69
// rather than cpu_last_cycles(), which covers a whole run: reading a per-
70
// instruction cost off a run that also included its setup is an easy mistake to
71
// make and a confusing one to debug.
72
uint32_t cpu_step(void);
73
74
// Run for `ticks` cycles through exec6502(), the loop the emulator itself uses.
75
// step6502() is the loop the debugger drives, and the two are separate pieces
76
// of code that are supposed to agree; this exists so a test can compare them.
77
void cpu_exec(uint32_t ticks);
78
79
// Cycles consumed by the most recent cpu_steps() call. Cycle counts are worth
80
// asserting on: they are what a debugger hook can perturb without changing any
81
// visible register.
82
uint32_t cpu_last_cycles(void);
83
84
// How often the CPU has signalled a halt (STP) or pulled a vector, since the
85
// last reset. Both are otherwise invisible to a test.
86
extern uint32_t cpu_stop_count;
87
extern uint32_t cpu_vector_pulls;
88
89
// Read a register at the width the CPU is currently using.
90
//
91
// The register names are less symmetric than they look: regs.a is the LOW BYTE
92
// of the accumulator, while regs.x and regs.y are the whole 16-bit registers
93
// (regs.xl and regs.yl are their low bytes). Checking regs.a in a 16-bit
94
// scenario would quietly drop the high byte and report a false pass, in exactly
95
// the mode this fixture exists to cover, so prefer these throughout.
96
uint16_t cpu_a(void);
97
uint16_t cpu_x(void);
98
uint16_t cpu_y(void);
99
100
// Set these to watch every bus access the CPU makes, so a test can compare the
101
// order and addresses against a reference trace. NULL by default, which costs
102
// one branch per access and keeps the other tests unaffected.
103
//
104
// The address is the full 24-bit one; on a 65C02 the bank is always zero.
105
extern void (*cpu_bus_read_hook)(uint32_t addr, uint8_t value);
106
extern void (*cpu_bus_write_hook)(uint32_t addr, uint8_t value);
107
108
// Whether the CPU is currently treating the accumulator, or the index
109
// registers, as 16 bits. Emulation mode forces both to 8 whatever the flags say.
110
bool memory_16bit_width(void);
111
bool index_16bit_width(void);
112
113
#endif // X16_TEST_CPU_FIXTURE_H