-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpu.cpp
134 lines (114 loc) · 3.27 KB
/
cpu.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include "cpu.h"
cpu::cpu() {
std::fill(write_handlers, write_handlers + CPU_ADDRESS_SIZE, nullptr);
std::fill(read_handlers, read_handlers + CPU_ADDRESS_SIZE, nullptr);
}
void cpu::step() {
if (cycle_stall_) {
cycle_stall_--;
return;
}
current_instruction_ = next8();
fetched_current_addr_ = false;
printf("%04X %02X %s\t\t\tA:%02X X:%02X Y:%02X P:%02X SP:%02X\n",
PC - 1, current_instruction_, opcode_names[current_instruction_], A, X, Y, P, SP);
auto handler = opcode_handlers[current_instruction_];
if (handler == nullptr) {
printf("Invalid opcode %02X\n", current_instruction_);
throw std::logic_error("invalid opcode");
}
auto insn_cycles = opcode_defs[current_instruction_].cycles;
cycle_stall_ += insn_cycles - 1;
cycle += insn_cycles;
(this->*handler)();
}
void cpu::reset() {
A = 0;
X = 0;
Y = 0;
SP = 0xFF;
PC = read16(0xFFFC);
printf("Reset PC to %04X\n", PC);
P = 0x14;
cycle = 0;
}
uint8_t cpu::operand() {
if (opcode_defs[current_instruction_].address_mode() == Direct)
return A;
current_addr_ = operand_address();
orig_rmw_value_ = read8(current_addr_);
return orig_rmw_value_;
}
void cpu::operand(uint8_t val) {
auto def = opcode_defs[current_instruction_];
if (def.address_mode() == Direct)
A = val;
else {
current_addr_ = operand_address();
// Fake another write for RMW opcodes
if (def.is_rmw())
write8(current_addr_, orig_rmw_value_);
write8(current_addr_, val);
}
}
uint8_t cpu::next8() {
return read8(PC++);
}
uint16_t cpu::next16() {
return next8() | (next8() << 8);
}
int8_t cpu::nexts8() {
return next8();
}
void cpu::push8(uint8_t val) {
write8(0x100 + SP, val);
SP--;
}
uint8_t cpu::pop8() {
SP++;
return read8(0x100 + SP);
}
void cpu::push16(uint16_t val) {
push8(val >> 8);
push8(val & 0xFF);
}
uint16_t cpu::pop16() {
return pop8() | (pop8() << 8);
}
uint16_t cpu::operand_address() {
if (fetched_current_addr_)
return current_addr_;
fetched_current_addr_ = true;
auto def = opcode_defs[current_instruction_];
switch (def.address_mode()) {
case Immediate:
return PC++;
case ZeroPage:
return next8();
case Absolute:
return next16();
case ZeroPageX:
return (next8() + X) & 0xFF;
case ZeroPageY:
return (next8() + Y) & 0xFF;
case AbsoluteX: {
uint16_t addr = next16();
if (def.has_extra_page_boundary_cycle() && (addr & 0xFF00) != ((addr + X) & 0xFF00)) cycle += 1;
return addr + X;
}
case AbsoluteY: {
uint16_t addr = next16();
if (def.has_extra_page_boundary_cycle() && (addr & 0xFF00) != ((addr + Y) & 0xFF00)) cycle += 1;
return addr + Y;
}
case IndirectX:
return read16(next8() + X);
case IndirectY: {
uint16_t addr = read16(next8());
if (def.has_extra_page_boundary_cycle() && (addr & 0xFF00) != ((addr + Y) & 0xFF00)) cycle += 1;
return addr + Y;
}
default:
throw std::logic_error("invalid address mode");
}
}