VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
C64.h
1// -----------------------------------------------------------------------------
2// This file is part of VirtualC64
3//
4// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de
5// This FILE is dual-licensed. You are free to choose between:
6//
7// - The GNU General Public License v3 (or any later version)
8// - The Mozilla Public License v2
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0
11// -----------------------------------------------------------------------------
12
13#pragma once
14
15#include "C64Types.h"
16#include "MsgQueue.h"
17#include "Thread.h"
18
19// Sub components
20#include "Keyboard.h"
21#include "C64Memory.h"
22#include "Debugger.h"
23#include "DriveMemory.h"
24#include "FlashRom.h"
25#include "VICII.h"
26#include "SIDBridge.h"
27#include "TOD.h"
28#include "CIA.h"
29#include "CPU.h"
30#include "Recorder.h"
31#include "RegressionTester.h"
32#include "RetroShell.h"
33
34// Ports
35#include "AudioPort.h"
36#include "VideoPort.h"
37#include "ControlPort.h"
38#include "PowerPort.h"
39#include "ExpansionPort.h"
40#include "SerialPort.h"
41
42// Peripherals
43#include "Drive.h"
44#include "ParCable.h"
45#include "Datasette.h"
46#include "Mouse.h"
47#include "Monitor.h"
48
49// Cartridges
50#include "Cartridge.h"
51#include "CustomCartridges.h"
52
53// Media files
54#include "Snapshot.h"
55#include "T64File.h"
56#include "D64File.h"
57#include "G64File.h"
58#include "PRGFile.h"
59#include "Folder.h"
60#include "P00File.h"
61#include "RomFile.h"
62#include "TAPFile.h"
63#include "CRTFile.h"
64#include "FileSystem.h"
65
66namespace vc64 {
67
68//
69// Macros and constants
70//
71
72// Checks the category of an event slot
73static constexpr bool isPrimarySlot(isize s) { return s <= SLOT_SEC; }
74static constexpr bool isSecondarySlot(isize s) { return s > SLOT_SEC && s <= SLOT_TER; }
75static constexpr bool isTertiarySlot(isize s) { return s > SLOT_TER; }
76
77// Time stamp used for messages that never trigger
78static constexpr Cycle NEVER = INT64_MAX;
79
80// Inspection interval in seconds (interval between INS_xxx events)
81static constexpr double inspectionInterval = 0.1;
82
83
84/* A complete virtual C64. This class is the most prominent one of all. To run
85 * the emulator, it is sufficient to create a single object of this type. All
86 * subcomponents are created automatically. The public API gives you control
87 * over the emulator's behaviour such as running and pausing the emulation.
88 * Please note that most subcomponents have their own public API. E.g., to
89 * query information from VICII, you need to invoke a method on c64.vicii.
90 */
91class C64 final : public CoreComponent, public Inspectable<C64Info> {
92
93 friend class Emulator;
94
95 Descriptions descriptions = {
96 {
97 .name = "C64",
98 .description = "Commodore 64"
99 },
100 {
101 .name = "C64[run-ahead]",
102 .description = "Commodore 64"
103 }
104 };
105
106 ConfigOptions options = {
107
115 };
116
117private:
118
119 // The current configuration
120 C64Config config = {};
121
122 // Result of the latest inspection
123 mutable EventSlotInfo slotInfo[SLOT_COUNT];
124
125
126 //
127 // Sub components
128 //
129
130public:
131
132 // Core components
133 C64Memory mem = C64Memory(*this);
134 CPU cpu = CPU(MOS_6510, *this);
135 CIA1 cia1 = CIA1(*this);
136 CIA2 cia2 = CIA2(*this);
137 VICII vic = VICII(*this);
138 SIDBridge sidBridge = SIDBridge(*this);
139
140 // Logic board
141 AudioPort audioPort = AudioPort(*this);
142 VideoPort videoPort = VideoPort(*this);
143 PowerPort supply = PowerPort(*this);
144 ControlPort port1 = ControlPort(*this, PORT_1);
145 ControlPort port2 = ControlPort(*this, PORT_2);
146 ExpansionPort expansionport = ExpansionPort(*this);
147 SerialPort iec = SerialPort(*this);
148
149 // Peripherals
150 Keyboard keyboard = Keyboard(*this);
151 Drive drive8 = Drive(*this, 0);
152 Drive drive9 = Drive(*this, 1);
153 ParCable parCable = ParCable(*this);
154 Datasette datasette = Datasette(*this);
155 Monitor monitor = Monitor(*this);
156
157 // Gateway to the GUI
158 MsgQueue msgQueue = MsgQueue();
159
160 // Misc
161 RetroShell retroShell = RetroShell(*this);
162 Debugger debugger = Debugger(*this);
163 RegressionTester regressionTester = RegressionTester(*this);
164 Recorder recorder = Recorder(*this);
165
166
167 //
168 // Event scheduler
169 //
170
171public:
172
173 // Trigger cycle
174 Cycle trigger[SLOT_COUNT] = { };
175
176 // The event identifier
177 EventID eventid[SLOT_COUNT] = { };
178
179 // An optional data value
180 i64 data[SLOT_COUNT] = { };
181
182 // Next trigger cycle
183 Cycle nextTrigger = NEVER;
184
185
186 //
187 // Emulator thread
188 //
189
190private:
191
192 /* Run loop flags. This variable is checked at the end of each runloop
193 * iteration. Most of the time, the variable is 0 which causes the runloop
194 * to repeat. A value greater than 0 means that one or more runloop control
195 * flags are set. These flags are flags processed and the loop either
196 * repeats or terminates depending on the provided flags.
197 */
198 RunLoopFlags flags = 0;
199
200
201 //
202 // Storage
203 //
204
205private:
206
207 Snapshot *autoSnapshot = nullptr;
208 Snapshot *userSnapshot = nullptr;
209
210 typedef struct { Cycle trigger; i64 payload; } Alarm;
211 std::vector<Alarm> alarms;
212
213
214 //
215 // State
216 //
217
218public:
219
220 // The total number of frames drawn since power up
221 u64 frame = 0;
222
223 // The currently drawn scanline (first scanline = 0)
224 u16 scanline = 0;
225
226 // The currently executed scanline cycle (first cylce = 1)
227 u8 rasterCycle = 1;
228
229private:
230
231 /* Indicates whether C64 is running in ultimax mode. Ultimax mode can be
232 * enabled by external cartridges by pulling game line low and keeping
233 * exrom line high. In ultimax mode, most of the C64's RAM and ROM is
234 * invisible.
235 */
236 bool ultimax = false;
237
238 /* Indicates if headless mode is activated. If yes, the pixel drawing code
239 * is skipped. Headless mode is used to accelerate warp mode and to speed
240 * up the computation of some frames in run-ahead mode.
241 */
242 bool headless = false;
243
244 /* Indicates whether the state has been altered by an external event.
245 * This flag is used to determine whether the run-ahead instance needs to
246 * be recreated.
247 */
248 bool isDirty = false;
249
250 // Duration of a CPU cycle in 1/10 nano seconds
251 i64 durationOfOneCycle;
252
253 // Target address for step mode
254 std::optional<u16> stepTo = { };
255
256
257 //
258 // Static methods
259 //
260
261public:
262
263 // Returns a version string for this release
264 static string version();
265
266 // Returns a build number string for this release
267 static string build();
268
269 // Returns a textual description for an event
270 static const char *eventName(EventSlot slot, EventID id);
271
272
273 //
274 // Methods
275 //
276
277public:
278
279 C64(class Emulator& ref, isize id);
280 ~C64();
281
282private:
283
284 void initialize();
285
286
287 //
288 // Static methods
289 //
290
291public:
292
293 // Converts a time span to an (approximate) cycle count
294 static Cycle usec(isize delay) { return Cycle(delay * 1LL); }
295 static Cycle msec(isize delay) { return Cycle(delay * 1000LL); }
296 static Cycle sec(double delay) { return Cycle(delay * 1000000LL); }
297
298
299 //
300 // Methods from CoreComponent
301 //
302
303public:
304
305 C64& operator= (const C64& other) {
306
307 CLONE(mem)
308 CLONE(cpu)
309 CLONE(cia1)
310 CLONE(cia2)
311 CLONE(vic)
312 CLONE(sidBridge)
313 CLONE(audioPort)
314 CLONE(videoPort)
315 CLONE(supply)
316 CLONE(port1)
317 CLONE(port2)
318 CLONE(expansionport)
319 CLONE(iec)
320 CLONE(keyboard)
321 CLONE(drive8)
322 CLONE(drive9)
323 CLONE(parCable)
324 CLONE(datasette)
325 CLONE(retroShell)
326 CLONE(regressionTester)
327 CLONE(recorder)
328
329 CLONE_ARRAY(trigger)
330 CLONE_ARRAY(eventid)
331 CLONE_ARRAY(data)
332 CLONE(nextTrigger)
333 CLONE(frame)
334 CLONE(scanline)
335 CLONE(rasterCycle)
336 CLONE(ultimax)
337
338 CLONE(durationOfOneCycle)
339
340 return *this;
341 }
342
343
344 //
345 // Methods from Serializable
346 //
347
348public:
349
350 template <class T>
351 void serialize(T& worker)
352 {
353 worker
354
355 << mem
356 << cpu
357 << cia1
358 << cia2
359 << vic
360 << sidBridge
361 << audioPort
362 << videoPort
363 << supply
364 << port1
365 << port2
366 << expansionport
367 << iec
368 << keyboard
369 << drive8
370 << drive9
371 << parCable
372 << datasette
373 << monitor
374 << retroShell
375 << regressionTester
376 << recorder
377
378 << trigger
379 << eventid
380 << data
381 << nextTrigger
382 << frame
383 << scanline
384 << rasterCycle
385 << ultimax;
386
387 if (isResetter(worker)) return;
388
389 worker
390
391 << durationOfOneCycle
392
393 << config.warpBoot
394 << config.warpMode
395 << config.vsync
396 << config.speedAdjust
397 << config.snapshots
398 << config.snapshotDelay
399 << config.runAhead;
400 }
401
402 void operator << (SerResetter &worker) override;
403 void operator << (SerChecker &worker) override { serialize(worker); }
404 void operator << (SerCounter &worker) override { serialize(worker); }
405 void operator << (SerReader &worker) override { serialize(worker); }
406 void operator << (SerWriter &worker) override { serialize(worker); }
407
408
409 //
410 // Methods from CoreComponent
411 //
412
413public:
414
415 const Descriptions &getDescriptions() const override { return descriptions; }
416 void prefix() const override;
417
418private:
419
420 void _dump(Category category, std::ostream& os) const override;
421 void _reset(bool hard) override;
422
423
424 //
425 // Methods from Inspectable
426 //
427
428public:
429
430 virtual void record() const override;
431 void cacheInfo(C64Info &result) const override;
432
433 EventSlotInfo getSlotInfo(isize nr) const;
434
435private:
436
437 void inspectSlot(EventSlot nr) const;
438
439
440 //
441 // Methods from Configurable
442 //
443
444public:
445
446 const ConfigOptions &getOptions() const override { return options; }
447 i64 getOption(Option opt) const override;
448 void checkOption(Option opt, i64 value) override;
449 void setOption(Option opt, i64 value) override;
450
451
452 //
453 // Configuring
454 //
455
456public:
457
458 const C64Config &getConfig() const { return config; }
459
460 // Updates the clock frequency and all variables derived from it
461 void updateClockFrequency();
462
463 // Indicates that the run-ahead instance needs an update
464 void markAsDirty() { isDirty = true; }
465
466 // Enables or disables headless mode
467 bool getHeadless() const { return headless; }
468 void setHeadless(bool value) { headless = value; }
469
470 // Exports the current configuration to a script file
471 void exportConfig(const fs::path &path) const;
472 void exportConfig(std::ostream& stream) const;
473
474
475 //
476 // Analyzing
477 //
478
479public:
480
481 InspectionTarget getInspectionTarget() const;
482
483private:
484
485 void setInspectionTarget(InspectionTarget target);
486 void removeInspectionTarget() { setInspectionTarget(INSPECTION_NONE); }
487
488
489 //
490 //
491 //
492
493public:
494
495 isize size();
496 isize load(const u8 *buffer);
497 isize save(u8 *buffer);
498
499
500 //
501 // Executing
502 //
503
504private:
505
506 void execute();
507 void execute(bool headless);
508 void executeHeadless() { execute(true); }
509 template <bool enable8, bool enable9> void execute();
510 template <bool enable8, bool enable9> alwaysinline void executeCycle();
511 template <bool enable8, bool enable9> void finishInstruction();
512 void processFlags();
513
514
515 // Experimental (for runahead)
516 void fastForward(isize frames);
517
518
519 //
520 // Controlling
521 //
522
523private:
524
525 void _isReady() const throws override;
526 void _powerOn() override;
527 void _powerOff() override;
528 void _run() override;
529 void _pause() override;
530 void _halt() override;
531 void _warpOn() override;
532 void _warpOff() override;
533 void _trackOn() override;
534 void _trackOff() override;
535
536
537 //
538 // Running the emulator
539 //
540
541public:
542
543 bool getUltimax() const { return ultimax; }
544 void setUltimax(bool b) { ultimax = b; }
545
546 /* Sets or clears a flag for controlling the run loop. The functions are
547 * thread-safe and can be called safely from outside the emulator thread.
548 */
549 void setFlag(u32 flags);
550 void clearFlag(u32 flags);
551
552 // Convenience wrappers
553 void signalBreakpoint() { setFlag(RL::BREAKPOINT); }
554 void signalWatchpoint() { setFlag(RL::WATCHPOINT); }
555 void signalJammed() { setFlag(RL::CPU_JAM); }
556 void signalStop() { setFlag(RL::STOP); }
557
558 // Executes a single clock cycle.
559 void executeOneCycle();
560
561private:
562
563 // Invoked after executing the last cycle of a scanline
564 void endScanline();
565
566 // Invoked after executing the last scanline of a frame
567 void endFrame();
568
569
570 //
571 // Managing commands and events
572 //
573
574public:
575
576 // Processes a command from the command queue
577 void process(const Cmd &cmd);
578
579 // Processes all pending events
580 void processEvents(Cycle cycle);
581
582 // Returns true iff the specified slot contains any event
583 template<EventSlot s> bool hasEvent() const { return this->eventid[s] != (EventID)0; }
584
585 // Returns true iff the specified slot contains a specific event
586 template<EventSlot s> bool hasEvent(EventID id) const { return this->eventid[s] == id; }
587
588 // Returns true iff the specified slot contains a pending event
589 template<EventSlot s> bool isPending() const { return this->trigger[s] != NEVER; }
590
591 // Returns true iff the specified slot contains a due event
592 template<EventSlot s> bool isDue(Cycle cycle) const { return cycle >= this->trigger[s]; }
593
594 // Schedules an event in certain ways
595 template<EventSlot s> void scheduleAbs(Cycle cycle, EventID id)
596 {
597 this->trigger[s] = cycle;
598 this->eventid[s] = id;
599
600 if (cycle < nextTrigger) nextTrigger = cycle;
601
602 if constexpr (isTertiarySlot(s)) {
603 if (cycle < trigger[SLOT_TER]) trigger[SLOT_TER] = cycle;
604 }
605 if constexpr (isSecondarySlot(s) || isTertiarySlot(s)) {
606 if (cycle < trigger[SLOT_SEC]) trigger[SLOT_SEC] = cycle;
607 }
608 }
609
610 template<EventSlot s> void scheduleAbs(Cycle cycle, EventID id, i64 data)
611 {
612 scheduleAbs<s>(cycle, id);
613 this->data[s] = data;
614 }
615
616 template<EventSlot s> void rescheduleAbs(Cycle cycle)
617 {
618 trigger[s] = cycle;
619 if (cycle < nextTrigger) nextTrigger = cycle;
620
621 if constexpr (isTertiarySlot(s)) {
622 if (cycle < trigger[SLOT_TER]) trigger[SLOT_TER] = cycle;
623 }
624 if constexpr (isSecondarySlot(s) || isTertiarySlot(s)) {
625 if (cycle < trigger[SLOT_SEC]) trigger[SLOT_SEC] = cycle;
626 }
627 }
628
629 template<EventSlot s> void scheduleImm(EventID id)
630 {
631 scheduleAbs<s>(cpu.clock, id);
632 }
633
634 template<EventSlot s> void scheduleImm(EventID id, i64 data)
635 {
636 scheduleAbs<s>(cpu.clock, id);
637 this->data[s] = data;
638 }
639
640 template<EventSlot s> void scheduleRel(Cycle cycle, EventID id) {
641 scheduleAbs<s>(cpu.clock + cycle, id);
642 }
643
644 template<EventSlot s> void scheduleRel(Cycle cycle, EventID id, i64 data) {
645 scheduleAbs<s>(cpu.clock + cycle, id, data);
646 }
647
648 template<EventSlot s> void rescheduleRel(Cycle cycle) {
649 rescheduleAbs<s>(cpu.clock + cycle);
650 }
651
652 template<EventSlot s> void scheduleInc(Cycle cycle, EventID id)
653 {
654 scheduleAbs<s>(trigger[s] + cycle, id);
655 }
656
657 template<EventSlot s> void scheduleInc(Cycle cycle, EventID id, i64 data)
658 {
659 scheduleAbs<s>(trigger[s] + cycle, id);
660 this->data[s] = data;
661 }
662
663 template<EventSlot s> void rescheduleInc(Cycle cycle)
664 {
665 rescheduleAbs<s>(trigger[s] + cycle);
666 }
667
668 template<EventSlot s> void cancel()
669 {
670 eventid[s] = (EventID)0;
671 data[s] = 0;
672 trigger[s] = NEVER;
673 }
674
675private:
676
677 // Services an inspection event
678 void processINSEvent(EventID id);
679
680
681 //
682 // Handling snapshots
683 //
684
685public:
686
687 // Takes a snapshot
688 MediaFile *takeSnapshot();
689
690 // Loads the current state from a snapshot file
691 void loadSnapshot(const MediaFile &snapshot) throws;
692
693private:
694
695 // Services a snapshot event
696 void processSNPEvent(EventID id);
697
698 // Schedules the next snapshot event
699 void scheduleNextSNPEvent();
700
701
702 //
703 // Handling Roms
704 //
705
706public:
707
708 // Queries ROM information
709 static RomTraits getRomTraits(u64 fnv);
710 RomTraits getRomTraits(RomType type) const;
711
712 // Computes a Rom checksum
713 u32 romCRC32(RomType type) const;
714 u64 romFNV64(RomType type) const;
715
716 // Checks if a certain Rom is present
717 bool hasRom(RomType type) const;
718 bool hasMega65Rom(RomType type) const;
719
720private:
721
722 // Returns a revision string if a Mega65 Rom is installed
723 const char *mega65BasicRev() const;
724 const char *mega65KernalRev() const;
725
726public:
727
728 // Installs a Rom
729 void loadRom(const fs::path &path) throws;
730 void loadRom(const MediaFile &file);
731
732 // Erases an installed Rom
733 void deleteRom(RomType type);
734
735 // Saves a Rom to disk
736 void saveRom(RomType rom, const fs::path &path) throws;
737
738
739 //
740 // Flashing files
741 //
742
743 // Flashes a single file into memory
744 void flash(const MediaFile &file) throws;
745 void flash(const MediaFile &file, isize item) throws;
746 void flash(const FileSystem &fs, isize item) throws;
747
748
749 //
750 // Handling alarms
751 //
752
753public:
754
755 /* Alarms are scheduled notifications set by the client (GUI). Once the
756 * trigger cycle of an alarm has been reached, the emulator sends a
757 * MSG_ALARM to the client.
758 */
759 void setAlarmAbs(Cycle trigger, i64 payload);
760 void setAlarmRel(Cycle trigger, i64 payload);
761
762 // Services an alarm event
763 void processAlarmEvent();
764
765private:
766
767 // Schedules the next alarm event
768 void scheduleNextAlarm();
769
770
771 //
772 // Miscellaneous
773 //
774
775public:
776
777 // Gets or sets an internal debug variable (only available in debug builds)
778 static bool getDebugVariable(DebugFlag flag);
779 static void setDebugVariable(DebugFlag flag, bool val);
780
781 // Translates the current clock cycle into pseudo-random number
782 u32 random();
783
784 // Translates x into a pseudo-random number
785 u32 random(u32 seed);
786};
787
788}
Inspection interface.
Definition Inspectable.h:32
VirtualC64 project namespace.
Definition CmdQueue.cpp:16
DEBUG_FLAG
Definition EmulatorTypes.h:27
OPT
Configuration option.
Definition OptionTypes.h:26
@ OPT_EMU_SNAPSHOTS
Take a snapshots once in a while.
Definition OptionTypes.h:38
@ OPT_EMU_SPEED_ADJUST
Speed adjustment in percent.
Definition OptionTypes.h:37
@ OPT_EMU_WARP_MODE
Warp activation mode.
Definition OptionTypes.h:35
@ OPT_EMU_RUN_AHEAD
Number of run-ahead frames.
Definition OptionTypes.h:40
@ OPT_EMU_WARP_BOOT
Warp-boot time in seconds.
Definition OptionTypes.h:34
@ OPT_EMU_VSYNC
Adapt the frame rate to the VSYNC signal.
Definition OptionTypes.h:36
@ OPT_EMU_SNAPSHOT_DELAY
Delay between two snapshots in seconds.
Definition OptionTypes.h:39