1 | //============================================================================ |
2 | // |
3 | // MM MM 6666 555555 0000 2222 |
4 | // MMMM MMMM 66 66 55 00 00 22 22 |
5 | // MM MMM MM 66 55 00 00 22 |
6 | // MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" |
7 | // MM MM 66 66 55 00 00 22 |
8 | // MM MM 66 66 55 55 00 00 22 |
9 | // MM MM 6666 5555 0000 222222 |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #ifndef M6502_HXX |
19 | #define M6502_HXX |
20 | |
21 | #include <functional> |
22 | |
23 | class Settings; |
24 | class System; |
25 | class DispatchResult; |
26 | |
27 | #ifdef DEBUGGER_SUPPORT |
28 | class Debugger; |
29 | class CpuDebug; |
30 | |
31 | #include "Expression.hxx" |
32 | #include "TrapArray.hxx" |
33 | #include "BreakpointMap.hxx" |
34 | #endif |
35 | |
36 | #include "bspf.hxx" |
37 | #include "Serializable.hxx" |
38 | |
39 | /** |
40 | The 6502 is an 8-bit microprocessor that has a 64K addressing space. |
41 | This class provides a high compatibility 6502 microprocessor emulator. |
42 | |
43 | The memory accesses and cycle counts it generates are valid at the |
44 | sub-instruction level and "false" reads are generated (such as the ones |
45 | produced by the Indirect,X addressing when it crosses a page boundary). |
46 | This provides provides better compatibility for hardware that has side |
47 | effects and for games which are very time sensitive. |
48 | |
49 | @author Bradford W. Mott |
50 | */ |
51 | class M6502 : public Serializable |
52 | { |
53 | // The 6502 and Cart debugger classes are friends who need special access |
54 | friend class CartDebug; |
55 | friend class CpuDebug; |
56 | |
57 | public: |
58 | |
59 | using onHaltCallback = std::function<void()>; |
60 | |
61 | public: |
62 | /** |
63 | Create a new 6502 microprocessor. |
64 | */ |
65 | explicit M6502(const Settings& settings); |
66 | virtual ~M6502() = default; |
67 | |
68 | public: |
69 | /** |
70 | Install the processor in the specified system. Invoked by the |
71 | system when the processor is attached to it. |
72 | |
73 | @param system The system the processor should install itself in |
74 | */ |
75 | void install(System& system); |
76 | |
77 | /** |
78 | Reset the processor to its power-on state. This method should not |
79 | be invoked until the entire 6502 system is constructed and installed |
80 | since it involves reading the reset vector from memory. |
81 | */ |
82 | void reset(); |
83 | |
84 | /** |
85 | Request a maskable interrupt |
86 | */ |
87 | void irq() { myExecutionStatus |= MaskableInterruptBit; } |
88 | |
89 | /** |
90 | Request a non-maskable interrupt |
91 | */ |
92 | void nmi() { myExecutionStatus |= NonmaskableInterruptBit; } |
93 | |
94 | /** |
95 | Set the callback for handling a halt condition |
96 | */ |
97 | void setOnHaltCallback(onHaltCallback callback) { myOnHaltCallback = callback; } |
98 | |
99 | /** |
100 | RDY pulled low --- halt on next read. |
101 | */ |
102 | void requestHalt(); |
103 | |
104 | /** |
105 | Pull RDY high again before the callback was triggered. |
106 | */ |
107 | void clearHaltRequest() { myHaltRequested = false; } |
108 | |
109 | /** |
110 | Execute instructions until the specified number of instructions |
111 | is executed, someone stops execution, or an error occurs. Answers |
112 | true iff execution stops normally. |
113 | |
114 | @param cycles Indicates the number of cycles to execute. Not that the actual |
115 | granularity of the CPU is instructions, so this is only accurate up to |
116 | a couple of cycles |
117 | @param result A DispatchResult object that will transport the result |
118 | */ |
119 | void execute(uInt64 cycles, DispatchResult& result); |
120 | |
121 | bool execute(uInt64 cycles); |
122 | |
123 | /** |
124 | Tell the processor to stop executing instructions. Invoking this |
125 | method while the processor is executing instructions will stop |
126 | execution as soon as possible. |
127 | */ |
128 | void stop() { myExecutionStatus |= StopExecutionBit; } |
129 | |
130 | /** |
131 | Answer true iff a fatal error has occured from which the processor |
132 | cannot recover (i.e. illegal instruction, etc.) |
133 | |
134 | @return true iff a fatal error has occured |
135 | */ |
136 | bool fatalError() const { return myExecutionStatus & FatalErrorBit; } |
137 | |
138 | /** |
139 | Get the 16-bit value of the Program Counter register. |
140 | |
141 | @return The program counter register |
142 | */ |
143 | // uInt16 getPC() const { return PC; } |
144 | |
145 | /** |
146 | Check the type of the last peek(). |
147 | |
148 | @return true, if the last peek() was a ghost read. |
149 | */ |
150 | bool lastWasGhostPeek() const { return myFlags == 0; } // DISASM_NONE |
151 | |
152 | /** |
153 | Return the last address that was part of a read/peek. |
154 | |
155 | @return The address of the last read |
156 | */ |
157 | uInt16 lastReadBaseAddress() const { return myLastPeekBaseAddress; } |
158 | |
159 | /** |
160 | Return the last address that was part of a write/poke. |
161 | |
162 | @return The address of the last write |
163 | */ |
164 | uInt16 lastWriteBaseAddress() const { return myLastPokeBaseAddress; } |
165 | |
166 | /** |
167 | Return the source of the address that was used for a write/poke. |
168 | Note that this isn't the same as the address that is poked, but |
169 | is instead the address of the *data* that is poked (if any). |
170 | |
171 | @return The address of the data used in the last poke, else 0 |
172 | */ |
173 | uInt16 lastDataAddressForPoke() const { return myDataAddressForPoke; } |
174 | |
175 | /** |
176 | Return the last data address used as part of a peek operation for |
177 | the S/A/X/Y registers. Note that if an address wasn't used (as in |
178 | immediate mode), then the address is -1. |
179 | |
180 | @return The address of the data used in the last peek, else -1 |
181 | */ |
182 | Int32 lastSrcAddressS() const { return myLastSrcAddressS; } |
183 | Int32 lastSrcAddressA() const { return myLastSrcAddressA; } |
184 | Int32 lastSrcAddressX() const { return myLastSrcAddressX; } |
185 | Int32 lastSrcAddressY() const { return myLastSrcAddressY; } |
186 | |
187 | /** |
188 | Get the number of memory accesses to distinct memory locations |
189 | |
190 | @return The number of memory accesses to distinct memory locations |
191 | */ |
192 | uInt32 distinctAccesses() const { return myNumberOfDistinctAccesses; } |
193 | |
194 | /** |
195 | Saves the current state of this device to the given Serializer. |
196 | |
197 | @param out The serializer device to save to. |
198 | @return The result of the save. True on success, false on failure. |
199 | */ |
200 | bool save(Serializer& out) const override; |
201 | |
202 | /** |
203 | Loads the current state of this device from the given Serializer. |
204 | |
205 | @param in The Serializer device to load from. |
206 | @return The result of the load. True on success, false on failure. |
207 | */ |
208 | bool load(Serializer& in) override; |
209 | |
210 | #ifdef DEBUGGER_SUPPORT |
211 | public: |
212 | // Attach the specified debugger. |
213 | void attach(Debugger& debugger); |
214 | |
215 | TrapArray& readTraps() { return myReadTraps; } |
216 | TrapArray& writeTraps() { return myWriteTraps; } |
217 | |
218 | BreakpointMap& breakPoints() { return myBreakPoints; } |
219 | |
220 | // methods for 'breakif' handling |
221 | uInt32 addCondBreak(Expression* e, const string& name, bool oneShot = false); |
222 | bool delCondBreak(uInt32 idx); |
223 | void clearCondBreaks(); |
224 | const StringList& getCondBreakNames() const; |
225 | |
226 | // methods for 'savestateif' handling |
227 | uInt32 addCondSaveState(Expression* e, const string& name); |
228 | bool delCondSaveState(uInt32 idx); |
229 | void clearCondSaveStates(); |
230 | const StringList& getCondSaveStateNames() const; |
231 | |
232 | // methods for 'trapif' handling |
233 | uInt32 addCondTrap(Expression* e, const string& name); |
234 | bool delCondTrap(uInt32 brk); |
235 | void clearCondTraps(); |
236 | const StringList& getCondTrapNames() const; |
237 | |
238 | void setGhostReadsTrap(bool enable) { myGhostReadsTrap = enable; } |
239 | void setReadFromWritePortBreak(bool enable) { myReadFromWritePortBreak = enable; } |
240 | void setWriteToReadPortBreak(bool enable) { myWriteToReadPortBreak = enable; } |
241 | #endif // DEBUGGER_SUPPORT |
242 | |
243 | private: |
244 | /** |
245 | Get the byte at the specified address and update the cycle count. |
246 | Addresses marked as code are hints to the debugger/disassembler to |
247 | conclusively determine code sections, even if the disassembler cannot |
248 | find them itself. |
249 | |
250 | @param address The address from which the value should be loaded |
251 | @param flags Indicates that this address has the given flags |
252 | for type of access (CODE, DATA, GFX, etc) |
253 | |
254 | @return The byte at the specified address |
255 | */ |
256 | uInt8 peek(uInt16 address, uInt8 flags); |
257 | |
258 | /** |
259 | Change the byte at the specified address to the given value and |
260 | update the cycle count. |
261 | |
262 | @param address The address where the value should be stored |
263 | @param value The value to be stored at the address |
264 | */ |
265 | void poke(uInt16 address, uInt8 value, uInt8 flags = 0); |
266 | |
267 | /** |
268 | Get the 8-bit value of the Processor Status register. |
269 | |
270 | @return The processor status register |
271 | */ |
272 | uInt8 PS() const { |
273 | uInt8 ps = 0x20; |
274 | |
275 | if(N) ps |= 0x80; |
276 | if(V) ps |= 0x40; |
277 | if(B) ps |= 0x10; |
278 | if(D) ps |= 0x08; |
279 | if(I) ps |= 0x04; |
280 | if(!notZ) ps |= 0x02; |
281 | if(C) ps |= 0x01; |
282 | |
283 | return ps; |
284 | } |
285 | |
286 | /** |
287 | Change the Processor Status register to correspond to the given value. |
288 | |
289 | @param ps The value to set the processor status register to |
290 | */ |
291 | void PS(uInt8 ps) { |
292 | N = ps & 0x80; |
293 | V = ps & 0x40; |
294 | B = true; // B = ps & 0x10; The 6507's B flag always true |
295 | D = ps & 0x08; |
296 | I = ps & 0x04; |
297 | notZ = !(ps & 0x02); |
298 | C = ps & 0x01; |
299 | } |
300 | |
301 | /** |
302 | Called after an interrupt has be requested using irq() or nmi() |
303 | */ |
304 | void interruptHandler(); |
305 | |
306 | /** |
307 | Check whether halt was requested (RDY low) and notify |
308 | */ |
309 | void handleHalt(); |
310 | |
311 | /** |
312 | This is the actual dispatch function that does the grunt work. M6502::execute |
313 | wraps it and makes sure that any pending halt is processed before returning. |
314 | */ |
315 | void _execute(uInt64 cycles, DispatchResult& result); |
316 | |
317 | #ifdef DEBUGGER_SUPPORT |
318 | /** |
319 | Check whether we are required to update hardware (TIA + RIOT) in lockstep |
320 | with the CPU and update the flag accordingly. |
321 | */ |
322 | void updateStepStateByInstruction(); |
323 | #endif // DEBUGGER_SUPPORT |
324 | |
325 | private: |
326 | /** |
327 | Bit fields used to indicate that certain conditions need to be |
328 | handled such as stopping execution, fatal errors, maskable interrupts |
329 | and non-maskable interrupts (in myExecutionStatus) |
330 | */ |
331 | static constexpr uInt8 |
332 | StopExecutionBit = 0x01, |
333 | FatalErrorBit = 0x02, |
334 | MaskableInterruptBit = 0x04, |
335 | NonmaskableInterruptBit = 0x08 |
336 | ; |
337 | uInt8 myExecutionStatus; |
338 | |
339 | /// Pointer to the system the processor is installed in or the null pointer |
340 | System* mySystem; |
341 | |
342 | /// Reference to the settings |
343 | const Settings& mySettings; |
344 | |
345 | uInt8 A; // Accumulator |
346 | uInt8 X; // X index register |
347 | uInt8 Y; // Y index register |
348 | uInt8 SP; // Stack Pointer |
349 | uInt8 IR; // Instruction register |
350 | uInt16 PC; // Program Counter |
351 | |
352 | bool N; // N flag for processor status register |
353 | bool V; // V flag for processor status register |
354 | bool B; // B flag for processor status register |
355 | bool D; // D flag for processor status register |
356 | bool I; // I flag for processor status register |
357 | bool notZ; // Z flag complement for processor status register |
358 | bool C; // C flag for processor status register |
359 | |
360 | uInt8 icycles; // cycles of last instruction |
361 | |
362 | /// Indicates the numer of distinct memory accesses |
363 | uInt32 myNumberOfDistinctAccesses; |
364 | |
365 | /// Indicates the last address which was accessed |
366 | uInt16 myLastAddress; |
367 | |
368 | /// Last cycle that triggered a breakpoint |
369 | uInt64 myLastBreakCycle; |
370 | |
371 | /// Indicates the last address which was accessed specifically |
372 | /// by a peek or poke command |
373 | uInt16 myLastPeekAddress, myLastPokeAddress; |
374 | /// Indicates the last base (= non-mirrored) address which was |
375 | /// accessed specifically by a peek or poke command |
376 | uInt16 myLastPeekBaseAddress, myLastPokeBaseAddress; |
377 | // Indicates the type of the last access |
378 | uInt8 myFlags; |
379 | |
380 | /// Indicates the last address used to access data by a peek command |
381 | /// for the CPU registers (S/A/X/Y) |
382 | Int32 myLastSrcAddressS, myLastSrcAddressA, |
383 | myLastSrcAddressX, myLastSrcAddressY; |
384 | |
385 | /// Indicates the data address used by the last command that performed |
386 | /// a poke (currently, the last address used by STx) |
387 | /// If an address wasn't used (ie, as in immediate mode), the address |
388 | /// is set to zero |
389 | uInt16 myDataAddressForPoke; |
390 | |
391 | /// Indicates the number of system cycles per processor cycle |
392 | static constexpr uInt32 SYSTEM_CYCLES_PER_CPU = 1; |
393 | |
394 | /// Called when the processor enters halt state |
395 | onHaltCallback myOnHaltCallback; |
396 | |
397 | /// Indicates whether RDY was pulled low |
398 | bool myHaltRequested; |
399 | |
400 | #ifdef DEBUGGER_SUPPORT |
401 | Int32 evalCondBreaks() { |
402 | for(Int32 i = Int32(myCondBreaks.size()) - 1; i >= 0; --i) |
403 | if(myCondBreaks[i]->evaluate()) |
404 | return i; |
405 | |
406 | return -1; // no break hit |
407 | } |
408 | |
409 | Int32 evalCondSaveStates() |
410 | { |
411 | for(Int32 i = Int32(myCondSaveStates.size()) - 1; i >= 0; --i) |
412 | if(myCondSaveStates[i]->evaluate()) |
413 | return i; |
414 | |
415 | return -1; // no save state point hit |
416 | } |
417 | |
418 | Int32 evalCondTraps() |
419 | { |
420 | for(Int32 i = Int32(myTrapConds.size()) - 1; i >= 0; --i) |
421 | if(myTrapConds[i]->evaluate()) |
422 | return i; |
423 | |
424 | return -1; // no trapif hit |
425 | } |
426 | |
427 | /// Pointer to the debugger for this processor or the null pointer |
428 | Debugger* myDebugger; |
429 | |
430 | // Addresses for which the specified action should occur |
431 | TrapArray myReadTraps, myWriteTraps; |
432 | |
433 | // Did we just now hit a trap? |
434 | bool myJustHitReadTrapFlag; |
435 | bool myJustHitWriteTrapFlag; |
436 | struct HitTrapInfo { |
437 | string message; |
438 | int address; |
439 | }; |
440 | HitTrapInfo myHitTrapInfo; |
441 | |
442 | BreakpointMap myBreakPoints; |
443 | vector<unique_ptr<Expression>> myCondBreaks; |
444 | StringList myCondBreakNames; |
445 | vector<unique_ptr<Expression>> myCondSaveStates; |
446 | StringList myCondSaveStateNames; |
447 | vector<unique_ptr<Expression>> myTrapConds; |
448 | StringList myTrapCondNames; |
449 | #endif // DEBUGGER_SUPPORT |
450 | |
451 | bool myGhostReadsTrap; // trap on ghost reads |
452 | bool myReadFromWritePortBreak; // trap on reads from write ports |
453 | bool myWriteToReadPortBreak; // trap on writes to read ports |
454 | bool myStepStateByInstruction; |
455 | |
456 | private: |
457 | // Following constructors and assignment operators not supported |
458 | M6502() = delete; |
459 | M6502(const M6502&) = delete; |
460 | M6502(M6502&&) = delete; |
461 | M6502& operator=(const M6502&) = delete; |
462 | M6502& operator=(M6502&&) = delete; |
463 | }; |
464 | |
465 | #endif |
466 | |