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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
|
#pragma once
#include "nes/sys/types/cycle-count.hh"
#include "nes/sys/types/address.hh"
#include "nes/sys/types/snapshot.hh"
#include "nes/common/status.hh"
#include "nes/common/types.hh"
namespace nes::sys
{
class ppu;
class controller;
class cartridge;
class cpu
{
static constexpr auto ram_size = u32{ 0x800 };
static constexpr auto stack_offset = address{ 0x100 };
cycle_count current_cycles_;
ppu& ppu_;
cartridge& cartridge_;
controller& controller_1_;
controller& controller_2_;
u8 ram_[ram_size]{};
bool nmi_pending_{ false };
// Registers
struct
{
u16 pc{ 0 };
u8 sp{ 0xFD };
u8 a{ 0 };
u8 x{ 0 };
u8 y{ 0 };
struct
{
auto get_c() const -> bool { return value & 0b00000001; } // carry
auto get_z() const -> bool { return value & 0b00000010; } // zero
auto get_i() const -> bool { return value & 0b00000100; } // interrupt inhibit
auto get_d() const -> bool { return value & 0b00001000; } // decimal
auto get_b() const -> bool { return value & 0b00010000; } // break
auto get_v() const -> bool { return value & 0b01000000; } // overflow
auto get_n() const -> bool { return value & 0b10000000; } // negative
auto set_c(bool const v) -> void { value = (value & ~0b00000001) | (v ? 0b00000001 : 0); }
auto set_z(bool const v) -> void { value = (value & ~0b00000010) | (v ? 0b00000010 : 0); }
auto set_i(bool const v) -> void { value = (value & ~0b00000100) | (v ? 0b00000100 : 0); }
auto set_d(bool const v) -> void { value = (value & ~0b00001000) | (v ? 0b00001000 : 0); }
auto set_b(bool const v) -> void { value = (value & ~0b00010000) | (v ? 0b00010000 : 0); }
auto set_v(bool const v) -> void { value = (value & ~0b01000000) | (v ? 0b01000000 : 0); }
auto set_n(bool const v) -> void { value = (value & ~0b10000000) | (v ? 0b10000000 : 0); }
u8 value{ 0b00100100 };
} p{};
} registers_{};
enum class addressing_mode
{
accumulator, // A
immediate, // #v
zero_page, // d
absolute, // a
relative, // *+d
indirect, // (a)
zero_page_indexed_x, // d,x
zero_page_indexed_y, // d,y
absolute_indexed_x, // a,x
absolute_indexed_y, // a,y
indexed_indirect, // (d,x)
indirect_indexed, // (d),y
};
enum class force_page_crossing
{
no,
yes,
};
template<addressing_mode>
class operand
{
cpu& cpu_;
address address_;
cycle_count cycles_;
public:
explicit operand(cpu& cpu, address const address, cycle_count const cycles)
: cpu_{ cpu }
, address_{ address }
, cycles_{ cycles }
{
}
auto read() -> u8 { return cpu_.read8(address_); }
auto write(u8 const value) -> void { cpu_.write8(address_, value); }
auto get_address() -> address { return address_; }
auto get_cycles() -> cycle_count { return cycles_; }
};
template<>
class operand<addressing_mode::accumulator>
{
cpu& cpu_;
public:
explicit operand(cpu& cpu)
: cpu_{ cpu }
{
}
auto read() -> u8 { return cpu_.registers_.a; }
auto write(u8 const value) -> void { cpu_.registers_.a = value; }
auto get_cycles() -> cycle_count { return cycle_count::from_cpu(2); }
};
template<>
class operand<addressing_mode::immediate>
{
u8 value_{ 0 };
public:
explicit operand(u8 const value)
: value_{ value }
{
}
auto read() -> u8 { return value_; }
auto get_cycles() -> cycle_count { return cycle_count::from_cpu(2); }
};
public:
explicit cpu(ppu&, cartridge&, controller& controller_1, controller& controller_2);
cpu(cpu const&) = delete;
cpu(cpu&&) = delete;
auto operator=(cpu const&) -> cpu& = delete;
auto operator=(cpu&&) -> cpu& = delete;
auto get_cycles() const -> cycle_count { return current_cycles_; }
#ifdef NES_ENABLE_SNAPSHOTS
auto build_snapshot(snapshot&) -> void;
#endif
auto stall_cycles(cycle_count) -> void;
auto trigger_nmi() -> void;
auto step() -> status;
auto is_nmi_pending() const -> bool { return nmi_pending_; } // XXX: Debugging
// Memory access
auto read8(address) -> u8;
auto read16(address) -> u16;
auto write8(address, u8) -> void;
auto write16(address, u16) -> void;
private:
// Instructions
#define DEFINE_SIMPLE_INSTRUCTION(name) \
auto run_##name() -> status;
#define DEFINE_OPERAND_INSTRUCTION(name) \
template<addressing_mode Mode> auto run_##name() -> status;
DEFINE_OPERAND_INSTRUCTION(brk)
DEFINE_OPERAND_INSTRUCTION(ora)
DEFINE_SIMPLE_INSTRUCTION(stp)
DEFINE_OPERAND_INSTRUCTION(slo)
DEFINE_OPERAND_INSTRUCTION(nop)
DEFINE_SIMPLE_INSTRUCTION(nop)
DEFINE_SIMPLE_INSTRUCTION(php)
DEFINE_OPERAND_INSTRUCTION(asl)
DEFINE_OPERAND_INSTRUCTION(anc)
DEFINE_OPERAND_INSTRUCTION(bpl)
DEFINE_SIMPLE_INSTRUCTION(clc)
DEFINE_OPERAND_INSTRUCTION(jsr)
DEFINE_OPERAND_INSTRUCTION(and)
DEFINE_OPERAND_INSTRUCTION(rla)
DEFINE_OPERAND_INSTRUCTION(bit)
DEFINE_OPERAND_INSTRUCTION(rol)
DEFINE_SIMPLE_INSTRUCTION(plp)
DEFINE_OPERAND_INSTRUCTION(bmi)
DEFINE_SIMPLE_INSTRUCTION(sec)
DEFINE_SIMPLE_INSTRUCTION(rti)
DEFINE_OPERAND_INSTRUCTION(eor)
DEFINE_OPERAND_INSTRUCTION(sre)
DEFINE_OPERAND_INSTRUCTION(lsr)
DEFINE_OPERAND_INSTRUCTION(alr)
DEFINE_SIMPLE_INSTRUCTION(pha)
DEFINE_OPERAND_INSTRUCTION(jmp)
DEFINE_OPERAND_INSTRUCTION(bvc)
DEFINE_SIMPLE_INSTRUCTION(cli)
DEFINE_SIMPLE_INSTRUCTION(rts)
DEFINE_OPERAND_INSTRUCTION(adc)
DEFINE_OPERAND_INSTRUCTION(rra)
DEFINE_OPERAND_INSTRUCTION(ror)
DEFINE_OPERAND_INSTRUCTION(arr)
DEFINE_SIMPLE_INSTRUCTION(pla)
DEFINE_OPERAND_INSTRUCTION(bvs)
DEFINE_SIMPLE_INSTRUCTION(sei)
DEFINE_OPERAND_INSTRUCTION(sta)
DEFINE_OPERAND_INSTRUCTION(ahx)
DEFINE_OPERAND_INSTRUCTION(sax)
DEFINE_OPERAND_INSTRUCTION(sty)
DEFINE_OPERAND_INSTRUCTION(stx)
DEFINE_SIMPLE_INSTRUCTION(dey)
DEFINE_SIMPLE_INSTRUCTION(txa)
DEFINE_OPERAND_INSTRUCTION(xaa)
DEFINE_OPERAND_INSTRUCTION(bcc)
DEFINE_SIMPLE_INSTRUCTION(tya)
DEFINE_SIMPLE_INSTRUCTION(txs)
DEFINE_OPERAND_INSTRUCTION(tas)
DEFINE_OPERAND_INSTRUCTION(shy)
DEFINE_OPERAND_INSTRUCTION(shx)
DEFINE_OPERAND_INSTRUCTION(ldy)
DEFINE_OPERAND_INSTRUCTION(lda)
DEFINE_OPERAND_INSTRUCTION(ldx)
DEFINE_OPERAND_INSTRUCTION(lax)
DEFINE_SIMPLE_INSTRUCTION(tay)
DEFINE_SIMPLE_INSTRUCTION(tax)
DEFINE_OPERAND_INSTRUCTION(bcs)
DEFINE_SIMPLE_INSTRUCTION(clv)
DEFINE_SIMPLE_INSTRUCTION(tsx)
DEFINE_OPERAND_INSTRUCTION(las)
DEFINE_OPERAND_INSTRUCTION(cpy)
DEFINE_OPERAND_INSTRUCTION(axs)
DEFINE_OPERAND_INSTRUCTION(cmp)
DEFINE_OPERAND_INSTRUCTION(dcp)
DEFINE_OPERAND_INSTRUCTION(dec)
DEFINE_SIMPLE_INSTRUCTION(iny)
DEFINE_SIMPLE_INSTRUCTION(dex)
DEFINE_OPERAND_INSTRUCTION(bne)
DEFINE_SIMPLE_INSTRUCTION(cld)
DEFINE_OPERAND_INSTRUCTION(cpx)
DEFINE_OPERAND_INSTRUCTION(sbc)
DEFINE_OPERAND_INSTRUCTION(isc)
DEFINE_OPERAND_INSTRUCTION(inc)
DEFINE_SIMPLE_INSTRUCTION(inx)
DEFINE_OPERAND_INSTRUCTION(beq)
DEFINE_SIMPLE_INSTRUCTION(sed)
#undef DEFINE_SIMPLE_INSTRUCTION
#undef DEFINE_OPERAND_INSTRUCTION
// Helpers
auto advance_pc8() -> u8;
auto advance_pc16() -> u16;
auto push_stack8(u8) -> void;
auto push_stack16(u16) -> void;
auto pop_stack8() -> u8;
auto pop_stack16() -> u16;
auto update_zn(u8 value) -> void;
template<addressing_mode Mode>
auto branch(bool condition) -> void;
auto execute_interrupt(address) -> void;
auto eval_ror(u8 arg) -> u8;
auto eval_rol(u8 arg) -> u8;
auto eval_asl(u8 arg) -> u8;
auto eval_lsr(u8 arg) -> u8;
auto eval_adc(u8 arg) -> void;
auto eval_and(u8 arg) -> void;
auto eval_ora(u8 arg) -> void;
auto eval_eor(u8 arg) -> void;
auto eval_cmp(u8 a, u8 b) -> void;
auto eval_plp() -> void;
auto eval_php() -> void;
// Operands
#define DEFINE_CYCLE_COUNT(name) \
template<addressing_mode Mode> static auto name##_cycle_count() -> cycle_count;
#define DEFINE_CYCLE_COUNT_INSTANCE(name, mode, value) \
template<> auto name##_cycle_count<addressing_mode::mode>() -> cycle_count { return cycle_count::from_cpu(value); }
DEFINE_CYCLE_COUNT(jump)
DEFINE_CYCLE_COUNT_INSTANCE(jump, absolute, 3)
DEFINE_CYCLE_COUNT_INSTANCE(jump, indirect, 5)
DEFINE_CYCLE_COUNT(shift)
DEFINE_CYCLE_COUNT_INSTANCE(shift, accumulator, 2)
DEFINE_CYCLE_COUNT_INSTANCE(shift, zero_page, 5)
DEFINE_CYCLE_COUNT_INSTANCE(shift, zero_page_indexed_x, 6)
DEFINE_CYCLE_COUNT_INSTANCE(shift, absolute, 6)
DEFINE_CYCLE_COUNT_INSTANCE(shift, absolute_indexed_x, 7)
DEFINE_CYCLE_COUNT(inc_dec)
DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, zero_page, 5)
DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, zero_page_indexed_x, 6)
DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, absolute, 6)
DEFINE_CYCLE_COUNT_INSTANCE(inc_dec, absolute_indexed_x, 7)
#undef DEFINE_CYCLE_COUNT
#undef DEFINE_CYCLE_COUNT_INSTANCE
template<addressing_mode Mode>
auto fetch_operand(force_page_crossing = force_page_crossing::no) -> operand<Mode>;
template<>
auto fetch_operand<addressing_mode::accumulator>(force_page_crossing) -> operand<addressing_mode::accumulator>;
template<>
auto fetch_operand<addressing_mode::immediate>(force_page_crossing) -> operand<addressing_mode::immediate>;
};
} // namespace nes::sys
|