VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
Peddle.h
1// -----------------------------------------------------------------------------
2// This file is part of Peddle - A MOS 65xx CPU emulator
3//
4// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de
5// Published under the terms of the MIT License
6// -----------------------------------------------------------------------------
7
8#pragma once
9
10#include "PeddleConfig.h"
11#include "PeddleTypes.h"
12#include "PeddleDisassembler.h"
13#include "PeddleDebugger.h"
14#include "SubComponent.h"
15#include "PeddleUtils.h"
16#include "TimeDelayed.h"
17
18using namespace vc64;
19
20namespace vc64::peddle {
21
22class Peddle : public SubComponent {
23
24 friend class Debugger;
25 friend class Disassembler;
26 friend class Breakpoints;
27 friend class Watchpoints;
28
29 //
30 // Static lookup tables
31 //
32
33public:
34
35 // Table storing the first microinstruction for each opcode
36 static MicroInstruction actionFunc[256];
37
38 // Table storing a textual representation for each opcode
39 static const char *mnemonic[256];
40
41 // Table storing the adressing mode for each opcode
42 static AddressingMode addressingMode[256];
43
44
45 //
46 // Configuration
47 //
48
49protected:
50
51 // Instance counter (to distinguish different CPU instances)
52 isize id;
53
54 // Emulated CPU model
55 CPURevision cpuModel = MOS_6510;
56
57
58 //
59 // Sub components
60 //
61
62public:
63
64 Debugger debugger = Debugger(*this);
65 Disassembler disassembler = Disassembler(*this);
66
67
68 //
69 // Internal state
70 //
71
72public:
73
74 // Elapsed clock cycles since power up
75 i64 clock;
76
77protected:
78
79 // State flags
80 isize flags;
81
82 // The next microinstruction to be executed
83 MicroInstruction next;
84
85
86 //
87 // Registers
88 //
89
90public:
91
92 Registers reg;
93
94
95 //
96 // Ports
97 //
98
99protected:
100
101 // Ready line (RDY). If pulled low, the CPU freezes on the next read access
102 bool rdyLine;
103
104 // Cycle of the most recent rising edge of the RDY line
105 i64 rdyLineUp;
106
107 // Cycle of the most recent falling edge of the RDY line
108 i64 rdyLineDown;
109
110 /* Interrupt lines
111 *
112 * Usally both variables equal 0 which means that the two interrupt lines
113 * are high. When an external component requests an interrupt, the NMI or
114 * the IRQ line is pulled low. In that case, the corresponding variable is
115 * set to a positive value which indicates the interrupt source. The
116 * variables are used in form of bit fields since both interrupt lines are
117 * driven by multiple sources.
118 */
119 IntSource nmiLine;
120 IntSource irqLine;
121
122 /* Edge detector (NMI line)
123 * https://wiki.nesdev.com/w/index.php/CPU_interrupts
124 * "The NMI input is connected to an edge detector. This edge detector polls
125 * the status of the NMI line during φ2 of each CPU cycle (i.e., during the
126 * second half of each cycle) and raises an internal signal if the input
127 * goes from being high during one cycle to being low during the next. The
128 * internal signal goes high during φ1 of the cycle that follows the one
129 * where the edge is detected, and stays high until the NMI has been
130 * handled."
131 */
132 TimeDelayed <u8,1> edgeDetector = TimeDelayed <u8,1> (&clock);
133
134 /* Level detector of IRQ line.
135 * https://wiki.nesdev.com/w/index.php/CPU_interrupts
136 * "The IRQ input is connected to a level detector. If a low level is
137 * detected on the IRQ input during φ2 of a cycle, an internal signal is
138 * raised during φ1 the following cycle, remaining high for that cycle only
139 * (or put another way, remaining high as long as the IRQ input is low
140 * during the preceding cycle's φ2).
141 */
142 TimeDelayed <u8,1> levelDetector = TimeDelayed <u8,1> (&clock);
143
144 /* Result of the edge detector polling operation.
145 * https://wiki.nesdev.com/w/index.php/CPU_interrupts
146 * "The output from the edge detector and level detector are polled at
147 * certain points to detect pending interrupts. For most instructions, this
148 * polling happens during the final cycle of the instruction, before the
149 * opcode fetch for the next instruction. If the polling operation detects
150 * that an interrupt has been asserted, the next "instruction" executed
151 * is the interrupt sequence. Many references will claim that interrupts
152 * are polled during the last cycle of an instruction, but this is true
153 * only when talking about the output from the edge and level detectors."
154 */
155 bool doNmi;
156
157 /* Result of the level detector polling operation.
158 * https://wiki.nesdev.com/w/index.php/CPU_interrupts
159 * "If both an NMI and an IRQ are pending at the end of an instruction, the
160 * NMI will be handled and the pending status of the IRQ forgotten (though
161 * it's likely to be detected again during later polling)."
162 */
163 bool doIrq;
164
165
166 //
167 // Constructing
168 //
169
170public:
171
172 Peddle(C64 &ref);
173 virtual ~Peddle() = default;
174
175
176 //
177 // Initializing (PeddleInit_cpp)
178 //
179
180private:
181
182 // Registers the instruction set
183 void registerInstructions();
184 void registerLegalInstructions();
185 void registerIllegalInstructions();
186
187 // Registers a single instruction
188 void registerCallback(u8 opcode,
189 const char *mnemonic,
190 AddressingMode mode,
191 MicroInstruction mInstr);
192
193
194 //
195 // Configuring the CPU
196 //
197
198public:
199
200 // Selects the emulated CPU model
201 void setModel(CPURevision cpuModel);
202
203
204 //
205 // Querying CPU properties and the CPU state
206 //
207
208public:
209
210 // Informs whether the selected CPU model has a processor port
211 u16 hasProcessorPort() const;
212 template <CPURevision C> u16 hasProcessorPort() const;
213
214 // Returns the address bus mask for this CPU model
215 u16 addrMask() const;
216 template <CPURevision C> u16 addrMask() const;
217
218 // Returns true if the next cycle marks the beginning of an instruction
219 bool inFetchPhase() const { return next == fetch; }
220
221
222 //
223 // Examining instructions
224 //
225
226public:
227
228 // Returns the length of an instruction in bytes
229 isize getLengthOfInstruction(u8 opcode) const;
230 isize getLengthOfInstructionAt(u16 addr) const;
231 isize getLengthOfCurrentInstruction() const;
232
233 // Returns the address of the instruction following the current one
234 u16 getAddressOfNextInstruction() const;
235
236
237 //
238 // Running the CPU (PeddleExec_cpp)
239 //
240
241public:
242
243 // Performs a hard reset (power up)
244 void reset();
245 template <CPURevision C> void reset();
246
247 // Exexutes the CPU for a single cycle
248 void execute();
249 template <CPURevision C> void execute();
250
251 // Executes the CPU for the specified number of cycles
252 void execute(int count);
253 template <CPURevision C> void execute(int count);
254
255 // Executes the CPU for a single instruction
256 void executeInstruction();
257 template <CPURevision C> void executeInstruction();
258
259 // Executes the CPU for the specified number of instructions
260 void executeInstruction(int count);
261 template <CPURevision C> void executeInstruction(int count);
262
263 // Executes the CPU until the fetch phase is reached
264 void finishInstruction();
265 template <CPURevision C> void finishInstruction();
266
267protected:
268
269 // Called after the last microcycle has been completed
270 template <CPURevision C> void done();
271
272
273 //
274 // Handling interrupts
275 //
276
277public:
278
279 // Pulls down an interrupt line
280 void pullDownNmiLine(IntSource source);
281 void pullDownIrqLine(IntSource source);
282
283 // Releases an interrupt line
284 void releaseNmiLine(IntSource source);
285 void releaseIrqLine(IntSource source);
286
287 // Checks the status of an interrupt line
288 IntSource getNmiLine() const { return nmiLine; }
289 IntSource getIrqLine() const { return irqLine; }
290
291 // Sets the value on the RDY line
292 void setRDY(bool value);
293
294
295 //
296 // Accessing registers and flags
297 //
298
299public:
300
301 /* Returns the frozen program counter.
302 * Variable pc0 matches the value of the program counter when the CPU
303 * starts to execute an instruction. In contrast to the real program
304 * counter, the value isn't changed until the CPU starts to process the
305 * next instruction. In other words: This value always contains the start
306 * address of the currently executed command, even if some microcycles of
307 * the command have already been computed.
308 */
309 u16 getPC0() const { return reg.pc0; }
310
311 u16 getSP() const { return reg.sp; }
312
313 bool getN() const { return reg.sr.n; }
314 void setN(bool value) { reg.sr.n = value; }
315
316 bool getV() const { return reg.sr.v; }
317 void setV(bool value) { reg.sr.v = value; }
318
319 bool getB() const { return reg.sr.b; }
320 void setB(bool value) { reg.sr.b = value; }
321
322 bool getD() const { return reg.sr.d; }
323 void setD(bool value) { reg.sr.d = value; }
324
325 bool getI() const { return reg.sr.i; }
326 void setI(bool value) { reg.sr.i = value; }
327
328 bool getZ() const { return reg.sr.z; }
329 void setZ(bool value) { reg.sr.z = value; }
330
331 bool getC() const { return reg.sr.c; }
332 void setC(bool value) { reg.sr.c = value; }
333
334 u8 getP() const;
335 void setP(u8 p);
336
337private:
338
339 u8 getPWithClearedB() const;
340 void setPWithoutB(u8 p);
341
342
343 //
344 // Interfacing with the memory (high-level interface)
345 //
346
347private:
348
349 template <CPURevision C> u8 read(u16 addr);
350 template <CPURevision C> u8 readZeroPage(u8 addr);
351 template <CPURevision C> u8 readStack(u8 sp);
352
353 template <CPURevision C> void readIdle(u16 addr);
354 template <CPURevision C> void readZeroPageIdle(u8 addr);
355 template <CPURevision C> void readStackIdle(u8 sp);
356
357 template <CPURevision C> void write(u16 addr, u8 value);
358 template <CPURevision C> void writeZeroPage(u8 addr, u8 value);
359 template <CPURevision C> void writeStack(u8 sp, u8 value);
360
361 template <CPURevision C> u16 readDasm(u16 addr) const;
362 // template <CPURevision C> u16 readResetVector();
363
364
365 //
366 // Interfacing with the memory (low-level interface)
367 //
368
369protected:
370
371 virtual u8 read(u16 addr) { return 0; }
372 virtual void write(u16 addr, u8 val) { };
373 virtual u8 readDasm(u16 addr) const { return 0; }
374 virtual u16 readResetVector();
375
376
377 //
378 // Accessing the processor port
379 //
380
381public:
382
383 // Reads from the port register or the port direction register
384 virtual u8 readPort() const;
385 virtual u8 readPortDir() const { return reg.pport.direction; }
386
387 // Writes into the port register or the port direction register
388 virtual void writePort(u8 val) { reg.pport.data = val; }
389 virtual void writePortDir(u8 val) { reg.pport.direction = val; }
390
391protected:
392
393 // Data provider for the external port bits
394 virtual u8 externalPortBits() const { return 0; }
395
396
397 //
398 // Delegation methods
399 //
400
401protected:
402
403 // State delegates
404 virtual void cpuDidJam() { }
405
406 // Exception delegates
407 virtual void irqWillTrigger() { }
408 virtual void irqDidTrigger() { }
409 virtual void nmiWillTrigger() { }
410 virtual void nmiDidTrigger() { }
411
412 // Debugger delegates
413 virtual void breakpointReached(u16 addr) const { }
414 virtual void watchpointReached(u16 addr) const { }
415 virtual void instructionLogged() const { }
416 virtual void jumpedTo(u16 addr) const { }
417
418
419 //
420 // Operating the Arithmetical Logical Unit (ALU)
421 //
422
423private:
424
425 void adc(u8 op);
426 void adcBinary(u8 op);
427 void adcBcd(u8 op);
428 void sbc(u8 op);
429 void sbcBinary(u8 op);
430 void sbcBcd(u8 op);
431 void cmp(u8 op1, u8 op2);
432};
433
434}
VirtualC64 project namespace.
Definition CmdQueue.cpp:16