Files
VirtualC64-Core/temp
T
duckey77 033e23c72e Update to 1.4.2
Update VirtualC64 to 1.4.2
created autoload& run functionality
first attempt at working save states
2016-01-27 15:43:47 -07:00

14937 lines
521 KiB
Plaintext

diff --git a/C64/Archive.cpp b/C64/Archive.cpp
index 3782fec..9f2ca91 100755
--- a/C64/Archive.cpp
+++ b/C64/Archive.cpp
@@ -16,11 +16,10 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "Archive.h"
Archive::Archive()
{
- // writeProtection = false;
}
Archive::~Archive()
@@ -88,22 +87,22 @@ Archive::getSizeOfItem(int n)
void
Archive::dumpDirectory()
{
- fprintf(stderr, "Archive: %s\n", getName());
- fprintf(stderr, "-------\n");
- fprintf(stderr, " Path: %s\n", getPath());
- fprintf(stderr, " Items: %d\n", getNumberOfItems());
+ msg("Archive: %s\n", getName());
+ msg("-------\n");
+ msg(" Path: %s\n", getPath());
+ msg(" Items: %d\n", getNumberOfItems());
for (unsigned i = 0; i < getNumberOfItems(); i++) {
- fprintf(stderr, " Item %2d: %s (%d bytes, load address: %d)\n",
+ msg(" Item %2d: %s (%d bytes, load address: %d)\n",
i, getNameOfItem(i), getSizeOfItem(i), getDestinationAddrOfItem(i));
- fprintf(stderr, " ");
+ msg(" ");
selectItem(i);
for (unsigned j = 0; j < 8; j++) {
int byte = getByte();
if (byte != -1)
- fprintf(stderr, "%02X ", byte);
+ msg("%02X ", byte);
}
- fprintf(stderr, "\n");
+ msg("\n");
}
}
diff --git a/C64/Archive.h b/C64/Archive.h
index 646feed..91efdef 100755
--- a/C64/Archive.h
+++ b/C64/Archive.h
@@ -1,6 +1,9 @@
+/*!
+ * @header Archive.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
/*
- * Author: Dirk W. Hoffmann, www.dirkwhoffmann.de
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -15,18 +18,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
-// For a detailed description of the various file formats, see
-// http://www.infinite-loop.at/Power20/Documentation/Power20-LiesMich/AE-Dateiformate.html
-
+/*
+ * For a detailed description of the various file formats, see
+ * http://www.infinite-loop.at/Power20/Documentation/Power20-LiesMich/AE-Dateiformate.html
+ */
#ifndef _ARCHIVE_INC
#define _ARCHIVE_INC
#include "Container.h"
-/*! @class Archive
- @brief Base class for all loadable objects with multiple files included. */
+/*! @class Archive
+ @brief Base class for all loadable objects with multiple files included. */
class Archive : public Container {
@@ -36,57 +39,63 @@ public:
//! Creating and destructing containers
//
- //! Standard constructor.
+ //! @brief Standard constructor
Archive();
- //! Standard destructor.
+ //! @brief Standard destructor
virtual ~Archive();
+
//
//! Accessing archive attributes
//
- //! Returns the number of items in this archive.
+ //! @brief Returns the number of items in this archive.
virtual int getNumberOfItems() = 0;
+
//
//! Accessing item attributes
//
- /*! Searches the directory for a specific item.
- @param filename The item name may contain the wildcard characters '?' and '*'.
- @return The number of the item (starting at 0) or -1, if no matching item was found. */
+ /*! @brief Searches the directory for a specific item.
+ * @param filename The item name may contain the wildcard characters '?' and '*'.
+ * @return The number of the item (starting at 0) or -1, if no matching item was found.
+ */
int getItemWithName(char *filename);
//! @brief Returns the name of an item (NULL, if the item does not exists)
virtual const char *getNameOfItem(int n) = 0;
- //! @brief Returns the type of an item as a string (e.g., "PRG" or "DEL")
+ //! @brief Returns the type of an item as a string (e.g., "PRG" or "DEL")
virtual const char *getTypeOfItem(int n) = 0;
- //! @brief Returns the size of an item in bytes
+ //! @brief Returns the size of an item in bytes
virtual int getSizeOfItem(int n);
- //! @brief Returns the size of an item in bits
+ //! @brief Returns the size of an item in bits
virtual int getSizeOfItemInBits(int n) { return 8 * getSizeOfItem(n); }
- //! @brief Returns the size of an item in blocks
+ //! @brief Returns the size of an item in blocks
virtual int getSizeOfItemInBlocks(int n) { return (getSizeOfItem(n) + 253) / 254; }
- /*! @brief Returns the proposed memory location of an item.
- @discussion When a file is flashed into memory, the raw data is copied to this location. */
+ /*! @brief Returns the proposed memory location of an item.
+ * @details When a file is flashed into memory, the raw data is copied to this location.
+ */
virtual uint16_t getDestinationAddrOfItem(int n) = 0;
+
//
//! @functiongroup Reading an item
//
- //! @brief Selects an item to read from
+ //! @brief Selects an item to read from
virtual void selectItem(int n) = 0;
- //! @brief Reads the next byte from the currently selected item
+ //! @brief Reads the next byte from the currently selected item
virtual int getByte() = 0;
+
//
//! @functiongroup Debugging
//
diff --git a/C64/C64.cpp b/C64/C64.cpp
index fc15681..da03f57 100755
--- a/C64/C64.cpp
+++ b/C64/C64.cpp
@@ -21,11 +21,10 @@
int debugcnt = 0;
int debugirq = 0;
-// --------------------------------------------------------------------------------
+//
// Execution thread
-// --------------------------------------------------------------------------------
+//
-// Exit function of the main execution loop
void
threadCleanup(void* thisC64)
{
@@ -38,8 +37,6 @@ threadCleanup(void* thisC64)
c64->putMessage(MSG_HALT);
}
-
-// Main execution loop
void
*runThread(void *thisC64) {
@@ -55,19 +52,16 @@ void
pthread_cleanup_push(threadCleanup, thisC64);
// Prepare to run...
- c64->cpu->clearErrorState();
- c64->floppy->cpu->clearErrorState();
+ c64->cpu.clearErrorState();
+ c64->floppy.cpu.clearErrorState();
c64->restartTimer();
while (1) {
if (!c64->executeOneLine())
break;
- if (c64->getRasterline() == 0 && c64->getFrame() % 8 == 0) {
-
- // Check if thread was requested to terminate
- pthread_testcancel();
- }
+ if (c64->getRasterline() == 0 && c64->getFrame() % 8 == 0)
+ pthread_testcancel(); // Check if thread was requested to terminate
}
pthread_cleanup_pop(1);
@@ -75,49 +69,35 @@ void
}
-// --------------------------------------------------------------------------------
+//
// Class methods
-// --------------------------------------------------------------------------------
+//
C64::C64()
-{
- name = "C64";
-
- debug(1, "Creating virtual C64\n");
+{
+ setDescription("C64");
+ debug(1, "Creating virtual C64 at address %p\n", this);
p = NULL;
warp = false;
alwaysWarp = false;
warpLoad = false;
- // Create components
- cpu = new CPU();
- mem = new C64Memory();
- vic = new VIC();
- sid = new SIDWrapper();
- cia1 = new CIA1();
- cia2 = new CIA2();
- iec = new IEC();
- expansionport = new ExpansionPort();
- floppy = new VC1541();
- keyboard = new Keyboard();
- joystick1 = new Joystick();
- joystick2 = new Joystick();
-
// Register sub components
VirtualComponent *subcomponents[] = {
- cpu,
- mem,
- vic,
- sid,
- cia1, cia2,
- iec,
- expansionport,
- floppy,
+ &cpu,
+ &mem,
+ &vic,
+ &sid,
+ &cia1, &cia2,
+ &iec,
+ &expansionport,
+ &floppy,
&datasette,
- keyboard,
- joystick1, joystick2,
+ &keyboard,
+ &joystickA,
+ &joystickB,
NULL };
registerSubComponents(subcomponents, sizeof(subcomponents));
@@ -126,11 +106,10 @@ C64::C64()
// Register snapshot items
SnapshotItem items[] = {
- { &warpLoad, sizeof(warpLoad), KEEP_ON_RESET },
-
- { &alwaysWarp, sizeof(alwaysWarp), CLEAR_ON_RESET },
{ &warp, sizeof(warp), CLEAR_ON_RESET },
- { &cycles, sizeof(cycles), CLEAR_ON_RESET },
+ { &alwaysWarp, sizeof(alwaysWarp), CLEAR_ON_RESET },
+ { &warpLoad, sizeof(warpLoad), KEEP_ON_RESET },
+ { &cycle, sizeof(cycle), CLEAR_ON_RESET },
{ &frame, sizeof(frame), CLEAR_ON_RESET },
{ &rasterline, sizeof(rasterline), CLEAR_ON_RESET },
{ &rasterlineCycle, sizeof(rasterlineCycle), CLEAR_ON_RESET },
@@ -151,37 +130,23 @@ C64::C64()
backInTimeWritePtr = 0;
}
-// Construction and destruction
C64::~C64()
{
- // Halt emulator
- halt();
-
- // Release all components
- delete joystick2;
- delete joystick1;
- delete floppy;
- delete expansionport;
- delete iec;
- delete keyboard;
- delete cia2;
- delete cia1;
- delete sid;
- delete vic;
- delete cpu;
- delete mem;
+ debug(1, "Destroying virtual C64 at address %p\n", this);
- debug(1, "Cleaned up virtual C64\n", this);
+ halt();
}
-void C64::reset()
+void
+C64::reset()
{
+ debug(1, "Resetting virtual C64 at address %p\n", this);
+
suspend();
VirtualComponent::reset();
-
- cpu->mem = mem;
- cpu->setPC(0xFCE2);
+ cpu.mem = &mem;
+ cpu.setPC(0xFCE2);
rasterlineCycle = 1;
nanoTargetTime = 0UL;
@@ -192,7 +157,7 @@ void C64::reset()
void C64::ping()
{
- debug (1, "Pinging virtual C64\n");
+ debug (1, "Pinging virtual C64 at address %p\n", this);
VirtualComponent::ping();
putMessage(MSG_WARP, warp);
@@ -204,211 +169,147 @@ C64::dumpState() {
msg("C64:\n");
msg("----\n\n");
msg(" Machine type : %s\n", isPAL() ? "PAL" : "NTSC");
- msg(" Frames per second : %d\n", vic->getFramesPerSecond());
- msg(" Rasterlines per frame : %d\n", vic->getRasterlinesPerFrame());
- msg(" Cycles per rasterline : %d\n", vic->getCyclesPerRasterline());
- msg(" Current cycle : %llu\n", cycles);
+ msg(" Frames per second : %d\n", vic.getFramesPerSecond());
+ msg(" Rasterlines per frame : %d\n", vic.getRasterlinesPerFrame());
+ msg(" Cycles per rasterline : %d\n", vic.getCyclesPerRasterline());
+ msg(" Current cycle : %llu\n", cycle);
msg(" Current frame : %d\n", frame);
msg(" Current rasterline : %d\n", rasterline);
msg("Current rasterline cycle : %d\n", rasterlineCycle);
msg("\n");
}
-Message *C64::getMessage()
-{
- return queue.getMessage();
-}
-
-void C64::putMessage(int id, int i, void *p, const char *c)
-{
- queue.putMessage(id, i, p, c);
-}
-
-// -----------------------------------------------------------------------------------------------
-// Configure
-// -----------------------------------------------------------------------------------------------
+//
+// Configuring the emulator
+//
void
C64::setPAL()
{
- suspend();
-
- vic->setChipModel(MOS6569_PAL);
- sid->setPAL();
+ debug(2, "C64::setPAL\n");
- debug(2, "Switching VIC chip model to MOS6569 (PAL)\n");
+ suspend();
+ vic.setChipModel(MOS6569_PAL);
+ sid.setPAL();
resume();
}
void
C64::setNTSC()
{
- suspend();
-
- vic->setChipModel(MOS6567_NTSC);
- sid->setNTSC();
+ debug(2, "C64::setNTSC\n");
- debug(2, "Switching VIC chip model to MOS6567 (NTSC)\n");
+ suspend();
+ vic.setChipModel(MOS6567_NTSC);
+ sid.setNTSC();
resume();
}
-void
-C64::setWarp(bool b)
-{
- if (warp == b)
- return;
-
- warp = b;
- // Warping has the unavoidable drawback that audio playback gets out of sync.
- // Therefore, we silence SID during warp mode and smoothly bring back sound when
- // warping ends.
-
- if (warp) {
- // Quickly fade out SID
- sid->rampDown();
-
- } else {
- // Smoothly fade in SID
- sid->rampUp();
- restartTimer();
- }
-
- putMessage(MSG_WARP, b);
-}
+//
+// Running the emulator
+//
void
-C64::setAlwaysWarp(bool b)
-{
- if (alwaysWarp == b)
- return;
+C64::run() {
- if (alwaysWarp != b) {
- alwaysWarp = b;
- setWarp(b);
- putMessage(MSG_ALWAYS_WARP, b);
+ if (isHalted()) {
+
+ // Check for ROM images
+ if (getMissingRoms()) {
+ putMessage(MSG_ROM_MISSING, getMissingRoms());
+ return;
+ }
+
+ // Power on sub components
+ sid.run();
+
+ // Start execution thread
+ pthread_create(&p, NULL, runThread, (void *)this);
}
}
void
-C64::setWarpLoad(bool b)
+C64::threadCleanup()
{
- warpLoad = b;
+ p = NULL;
+ debug(1, "Execution thread cleanup\n");
}
-// -----------------------------------------------------------------------------------------------
-// Loading and saving
-// -----------------------------------------------------------------------------------------------
-
-void C64::loadFromSnapshot(Snapshot *snapshot)
-{
- if (snapshot == NULL)
- return;
-
- uint8_t *ptr = snapshot->getData();
- loadFromBuffer(&ptr);
- ping();
+bool
+C64::isRunnable() {
+ return mem.basicRomIsLoaded() && mem.charRomIsLoaded() && mem.kernelRomIsLoaded() && floppy.mem.romIsLoaded();
}
-void
-C64::saveToSnapshot(Snapshot *snapshot)
+bool
+C64::isRunning()
{
- if (snapshot == NULL)
- return;
-
- snapshot->setTimestamp(time(NULL));
- // snapshot->setPAL(isPAL());
- snapshot->takeScreenshot((uint32_t *)vic->screenBuffer(), isPAL());
-
- snapshot->alloc(stateSize());
- uint8_t *ptr = snapshot->getData();
- saveToBuffer(&ptr);
+ return p != NULL;
}
-
-// -----------------------------------------------------------------------------------------------
-// Control
-// -----------------------------------------------------------------------------------------------
-
-void
-C64::runstopRestore()
+void
+C64::halt()
{
- // Note: The restore key is directly connected to the NMI line of the CPU
- // Thus, the runstop/restore key combination triggers an interrupts that causes a soft reset
- keyboard->pressRunstopKey();
- cpu->setNMILineReset();
- // Hold runstop key down for a while...
- sleepMicrosec((uint64_t)100000);
- keyboard->releaseRunstopKey();
+ if (isRunning()) {
+
+ // Cancel execution thread
+ pthread_cancel(p);
+ // Wait until thread terminates
+ pthread_join(p, NULL);
+ // Finish the current command (to reach a clean state)
+ step();
+ // Shut down sub components
+ sid.halt();
+ }
}
bool
-C64::isRunnable() {
- return mem->basicRomIsLoaded() && mem->charRomIsLoaded() && mem->kernelRomIsLoaded() && floppy->mem->romIsLoaded();
-}
-
-void
-C64::run() {
-
- if (isHalted()) {
-
- // Check for ROM images
- if (getMissingRoms()) {
- putMessage(MSG_ROM_MISSING, getMissingRoms());
- return;
- }
-
- // Power on sub components
- sid->run();
-
- // Start execution thread
- pthread_create(&p, NULL, runThread, (void *)this);
- }
-}
-
-bool
-C64::isRunning() {
- return p != NULL;
+C64::isHalted()
+{
+ return p == NULL;
}
-void
-C64::halt()
+#if 0
+void
+C64::restore()
{
- if (isRunning()) {
-
- // Cancel execution thread
- pthread_cancel(p);
- // Wait until thread terminates
- pthread_join(p, NULL);
- // Finish the current command (to reach a clean state)
- step();
- // Shut down sub components
- sid->halt();
- }
+ debug("RESTORE key\n");
+
+ // Hitting the restore key triggeres an NMI interrupt
+ cpu.setNMILineReset();
}
-bool
-C64::isHalted()
+void
+C64::runstopRestore()
{
- return p == NULL;
+ // Press runstop
+ keyboard.pressRunstopKey();
+
+ // Press restore
+ restore();
+
+ // Hold down runstop key for a while...
+ sleepMicrosec((uint64_t)100000);
+ keyboard.releaseRunstopKey();
}
+#endif
void
-C64::step()
-{
- // Clear error states
- cpu->clearErrorState();
- floppy->cpu->clearErrorState();
-
- // Execute next command
- do {
- executeOneCycle();
- } while (!cpu->atBeginningOfNewCommand());
-
- // We are now at cycle 0 of the next command
- // Execute one more cycle (and stop in cycle 1)
- executeOneCycle();
+C64::step()
+{
+ // Clear error states
+ cpu.clearErrorState();
+ floppy.cpu.clearErrorState();
+
+ // Execute next command
+ do {
+ executeOneCycle();
+ } while (!cpu.atBeginningOfNewCommand());
+
+ // We are now at cycle 0 of the next command
+ // Execute one more cycle (and stop in cycle 1)
+ executeOneCycle();
}
// From Wolfgang Lorenz: Clock.txt
@@ -422,493 +323,435 @@ C64::step()
// '---------------------------------------------------------------'
#define EXECUTE(x) \
- cia1->executeOneCycle(); \
- cia2->executeOneCycle(); \
- if (!cpu->executeOneCycle()) result = false; \
- if (!floppy->executeOneCycle()) result = false; \
- datasette.execute(); \
- cycles++; \
- rasterlineCycle++;
+cia1.executeOneCycle(); \
+cia2.executeOneCycle(); \
+if (!cpu.executeOneCycle()) result = false; \
+if (!floppy.executeOneCycle()) result = false; \
+datasette.execute(); \
+cycle++; \
+rasterlineCycle++;
+
+inline bool
+C64::executeOneCycle()
+{
+ bool result = true; // Don't break execution
+
+ switch(rasterlineCycle) {
+ case 1:
+ beginOfRasterline();
+ vic.cycle1();
+ EXECUTE(1);
+ break;
+ case 2:
+ vic.cycle2();
+ EXECUTE(2);
+ break;
+ case 3:
+ vic.cycle3();
+ EXECUTE(3);
+ break;
+ case 4:
+ vic.cycle4();
+ EXECUTE(4);
+ break;
+ case 5:
+ vic.cycle5();
+ EXECUTE(5);
+ break;
+ case 6:
+ vic.cycle6();
+ EXECUTE(6);
+ break;
+ case 7:
+ vic.cycle7();
+ EXECUTE(7);
+ break;
+ case 8:
+ vic.cycle8();
+ EXECUTE(8);
+ break;
+ case 9:
+ vic.cycle9();
+ EXECUTE(9);
+ break;
+ case 10:
+ vic.cycle10();
+ EXECUTE(10);
+ break;
+ case 11:
+ vic.cycle11();
+ EXECUTE(11);
+ break;
+ case 12:
+ vic.cycle12();
+ EXECUTE(12);
+ break;
+ case 13:
+ vic.cycle13();
+ EXECUTE(13);
+ break;
+ case 14:
+ vic.cycle14();
+ EXECUTE(14);
+ break;
+ case 15:
+ vic.cycle15();
+ EXECUTE(15);
+ break;
+ case 16:
+ vic.cycle16();
+ EXECUTE(16);
+ break;
+ case 17:
+ vic.cycle17();
+ EXECUTE(17);
+ break;
+ case 18:
+ vic.cycle18();
+ EXECUTE(18);
+ break;
+ case 19:
+ vic.cycle19to54();
+ EXECUTE(19);
+ break;
+ case 20:
+ vic.cycle19to54();
+ EXECUTE(20);
+ break;
+ case 21:
+ vic.cycle19to54();
+ EXECUTE(21);
+ break;
+ case 22:
+ vic.cycle19to54();
+ EXECUTE(22);
+ break;
+ case 23:
+ vic.cycle19to54();
+ EXECUTE(23);
+ break;
+ case 24:
+ vic.cycle19to54();
+ EXECUTE(24);
+ break;
+ case 25:
+ vic.cycle19to54();
+ EXECUTE(25);
+ break;
+ case 26:
+ vic.cycle19to54();
+ EXECUTE(26);
+ break;
+ case 27:
+ vic.cycle19to54();
+ EXECUTE(27);
+ break;
+ case 28:
+ vic.cycle19to54();
+ EXECUTE(28);
+ break;
+ case 29:
+ vic.cycle19to54();
+ EXECUTE(29);
+ break;
+ case 30:
+ vic.cycle19to54();
+ EXECUTE(30);
+ break;
+ case 31:
+ vic.cycle19to54();
+ EXECUTE(31);
+ break;
+ case 32:
+ vic.cycle19to54();
+ EXECUTE(32);
+ break;
+ case 33:
+ vic.cycle19to54();
+ EXECUTE(33);
+ break;
+ case 34:
+ vic.cycle19to54();
+ EXECUTE(34);
+ break;
+ case 35:
+ vic.cycle19to54();
+ EXECUTE(35);
+ break;
+ case 36:
+ vic.cycle19to54();
+ EXECUTE(36);
+ break;
+ case 37:
+ vic.cycle19to54();
+ EXECUTE(37);
+ break;
+ case 38:
+ vic.cycle19to54();
+ EXECUTE(38);
+ break;
+ case 39:
+ vic.cycle19to54();
+ EXECUTE(39);
+ break;
+ case 40:
+ vic.cycle19to54();
+ EXECUTE(40);
+ break;
+ case 41:
+ vic.cycle19to54();
+ EXECUTE(41);
+ break;
+ case 42:
+ vic.cycle19to54();
+ EXECUTE(42);
+ break;
+ case 43:
+ vic.cycle19to54();
+ EXECUTE(43);
+ break;
+ case 44:
+ vic.cycle19to54();
+ EXECUTE(44);
+ break;
+ case 45:
+ vic.cycle19to54();
+ EXECUTE(45);
+ break;
+ case 46:
+ vic.cycle19to54();
+ EXECUTE(46);
+ break;
+ case 47:
+ vic.cycle19to54();
+ EXECUTE(47);
+ break;
+ case 48:
+ vic.cycle19to54();
+ EXECUTE(48);
+ break;
+ case 49:
+ vic.cycle19to54();
+ EXECUTE(49);
+ break;
+ case 50:
+ vic.cycle19to54();
+ EXECUTE(50);
+ break;
+ case 51:
+ vic.cycle19to54();
+ EXECUTE(51);
+ break;
+ case 52:
+ vic.cycle19to54();
+ EXECUTE(52);
+ break;
+ case 53:
+ vic.cycle19to54();
+ EXECUTE(53);
+ break;
+ case 54:
+ vic.cycle19to54();
+ EXECUTE(54);
+ break;
+ case 55:
+ vic.cycle55();
+ EXECUTE(55);
+ break;
+ case 56:
+ vic.cycle56();
+ EXECUTE(56);
+ break;
+ case 57:
+ vic.cycle57();
+ EXECUTE(57);
+ break;
+ case 58:
+ vic.cycle58();
+ EXECUTE(58);
+ break;
+ case 59:
+ vic.cycle59();
+ EXECUTE(59);
+ break;
+ case 60:
+ vic.cycle60();
+ EXECUTE(60);
+ break;
+ case 61:
+ vic.cycle61();
+ EXECUTE(61);
+ break;
+ case 62:
+ vic.cycle62();
+ EXECUTE(62);
+ break;
+ case 63:
+ vic.cycle63();
+ EXECUTE(63);
+ if (vic.getCyclesPerRasterline() == 63) {
+ // last cycle for PAL machines
+ endOfRasterline();
+ }
+ break;
+ case 64:
+ vic.cycle64();
+ EXECUTE(64);
+ break;
+ case 65:
+ vic.cycle65();
+ EXECUTE(65);
+ endOfRasterline();
+ break;
+
+ default:
+ // can't reach
+ assert(false);
+ return false;
+ }
+
+ return result;
+}
+
+inline bool
+C64::executeOneLine()
+{
+ uint8_t lastCycle = vic.getCyclesPerRasterline();
+ for (unsigned i = rasterlineCycle; i <= lastCycle; i++) {
+ if (!executeOneCycle())
+ return false;
+ }
+ return true;
+}
void
C64::beginOfRasterline()
{
- // First cycle of rasterline
- if (rasterline == 0) {
- vic->beginFrame();
- }
- vic->beginRasterline(rasterline);
+ // First cycle of rasterline
+ if (rasterline == 0) {
+ vic.beginFrame();
+ }
+ vic.beginRasterline(rasterline);
}
void
C64::endOfRasterline()
{
- vic->endRasterline();
- rasterlineCycle = 1;
- rasterline++;
-
- if (rasterline >= vic->getRasterlinesPerFrame()) {
+ vic.endRasterline();
+ rasterlineCycle = 1;
+ rasterline++;
+
+ if (rasterline >= vic.getRasterlinesPerFrame()) {
+
+ // Last rasterline of frame
+ rasterline = 0;
+ vic.endFrame();
+ frame++;
+
+ // Increment time of day clocks every tenth of a second
+ if (frame % (vic.getFramesPerSecond() / 10) == 0) {
+ cia1.incrementTOD();
+ cia2.incrementTOD();
+ }
+
+ // Take a snapshot once in a while
+ if (frame % (vic.getFramesPerSecond() * 4) == 0) {
+ takeSnapshot();
+ }
+
+ // Execute remaining SID cycles
+ sid.executeUntil(cycle);
- // Last rasterline of frame
- rasterline = 0;
- vic->endFrame();
- frame++;
-
- // Increment time of day clocks every tenth of a second
- if (frame % (vic->getFramesPerSecond() / 10) == 0) {
- cia1->incrementTOD();
- cia2->incrementTOD();
- }
-
- // Take a snapshot once in a while
- if (frame % (vic->getFramesPerSecond() * 4) == 0) {
- takeSnapshot();
- }
-
- // Execute remaining SID cycles
- sid->executeUntil(cycles);
- /*
- int diff = sid->resid->writePtr - sid->resid->readPtr;
- debug(2,"SID readCnt: %8d writeCnt: %8d readPtr: %8d writePtr: %8d diff: %8d volume:%d target:%d\n",
- sid->resid->readDataCnt, sid->resid->writeDataCnt,
- sid->resid->readPtr, sid->resid->writePtr,
- (diff > 0) ? diff : 44100 + diff,sid->resid->volume,sid->resid->targetVolume);
- sid->resid->readDataCnt = sid->resid->writeDataCnt = 0;
- */
-
// Execute the IEC bus
- iec->execute();
-
+ iec.execute();
+
// Count some sheep (zzzzzz) ...
if (!getWarp())
synchronizeTiming();
}
}
-inline bool
-C64::executeOneCycle()
-{
- bool result = true; // Don't break execution
-
- switch(rasterlineCycle) {
- case 1:
- beginOfRasterline();
- vic->cycle1();
- EXECUTE(1);
- break;
- case 2:
- vic->cycle2();
- EXECUTE(2);
- break;
- case 3:
- vic->cycle3();
- EXECUTE(3);
- break;
- case 4:
- vic->cycle4();
- EXECUTE(4);
- break;
- case 5:
- vic->cycle5();
- EXECUTE(5);
- break;
- case 6:
- vic->cycle6();
- EXECUTE(6);
- break;
- case 7:
- vic->cycle7();
- EXECUTE(7);
- break;
- case 8:
- vic->cycle8();
- EXECUTE(8);
- break;
- case 9:
- vic->cycle9();
- EXECUTE(9);
- break;
- case 10:
- vic->cycle10();
- EXECUTE(10);
- break;
- case 11:
- vic->cycle11();
- EXECUTE(11);
- break;
- case 12:
- vic->cycle12();
- EXECUTE(12);
- break;
- case 13:
- vic->cycle13();
- EXECUTE(13);
- break;
- case 14:
- vic->cycle14();
- EXECUTE(14);
- break;
- case 15:
- vic->cycle15();
- EXECUTE(15);
- break;
- case 16:
- vic->cycle16();
- EXECUTE(16);
- break;
- case 17:
- vic->cycle17();
- EXECUTE(17);
- break;
- case 18:
- vic->cycle18();
- EXECUTE(18);
- break;
- case 19:
- vic->cycle19to54();
- EXECUTE(19);
- break;
- case 20:
- vic->cycle19to54();
- EXECUTE(20);
- break;
- case 21:
- vic->cycle19to54();
- EXECUTE(21);
- break;
- case 22:
- vic->cycle19to54();
- EXECUTE(22);
- break;
- case 23:
- vic->cycle19to54();
- EXECUTE(23);
- break;
- case 24:
- vic->cycle19to54();
- EXECUTE(24);
- break;
- case 25:
- vic->cycle19to54();
- EXECUTE(25);
- break;
- case 26:
- vic->cycle19to54();
- EXECUTE(26);
- break;
- case 27:
- vic->cycle19to54();
- EXECUTE(27);
- break;
- case 28:
- vic->cycle19to54();
- EXECUTE(28);
- break;
- case 29:
- vic->cycle19to54();
- EXECUTE(29);
- break;
- case 30:
- vic->cycle19to54();
- EXECUTE(30);
- break;
- case 31:
- vic->cycle19to54();
- EXECUTE(31);
- break;
- case 32:
- vic->cycle19to54();
- EXECUTE(32);
- break;
- case 33:
- vic->cycle19to54();
- EXECUTE(33);
- break;
- case 34:
- vic->cycle19to54();
- EXECUTE(34);
- break;
- case 35:
- vic->cycle19to54();
- EXECUTE(35);
- break;
- case 36:
- vic->cycle19to54();
- EXECUTE(36);
- break;
- case 37:
- vic->cycle19to54();
- EXECUTE(37);
- break;
- case 38:
- vic->cycle19to54();
- EXECUTE(38);
- break;
- case 39:
- vic->cycle19to54();
- EXECUTE(39);
- break;
- case 40:
- vic->cycle19to54();
- EXECUTE(40);
- break;
- case 41:
- vic->cycle19to54();
- EXECUTE(41);
- break;
- case 42:
- vic->cycle19to54();
- EXECUTE(42);
- break;
- case 43:
- vic->cycle19to54();
- EXECUTE(43);
- break;
- case 44:
- vic->cycle19to54();
- EXECUTE(44);
- break;
- case 45:
- vic->cycle19to54();
- EXECUTE(45);
- break;
- case 46:
- vic->cycle19to54();
- EXECUTE(46);
- break;
- case 47:
- vic->cycle19to54();
- EXECUTE(47);
- break;
- case 48:
- vic->cycle19to54();
- EXECUTE(48);
- break;
- case 49:
- vic->cycle19to54();
- EXECUTE(49);
- break;
- case 50:
- vic->cycle19to54();
- EXECUTE(50);
- break;
- case 51:
- vic->cycle19to54();
- EXECUTE(51);
- break;
- case 52:
- vic->cycle19to54();
- EXECUTE(52);
- break;
- case 53:
- vic->cycle19to54();
- EXECUTE(53);
- break;
- case 54:
- vic->cycle19to54();
- EXECUTE(54);
- break;
- case 55:
- vic->cycle55();
- EXECUTE(55);
- break;
- case 56:
- vic->cycle56();
- EXECUTE(56);
- break;
- case 57:
- vic->cycle57();
- EXECUTE(57);
- break;
- case 58:
- vic->cycle58();
- EXECUTE(58);
- break;
- case 59:
- vic->cycle59();
- EXECUTE(59);
- break;
- case 60:
- vic->cycle60();
- EXECUTE(60);
- break;
- case 61:
- vic->cycle61();
- EXECUTE(61);
- break;
- case 62:
- vic->cycle62();
- EXECUTE(62);
- break;
- case 63:
- vic->cycle63();
- EXECUTE(63);
- if (vic->getCyclesPerRasterline() == 63) {
- // last cycle for PAL machines
- endOfRasterline();
- }
- break;
- case 64:
- vic->cycle64();
- EXECUTE(64);
- break;
- case 65:
- vic->cycle65();
- EXECUTE(65);
- endOfRasterline();
- break;
-
- default:
- // can't reach
- assert(false);
- return false;
- }
-
- return result;
-}
-
-// Execute until the end of the rasterline
-inline bool
-C64::executeOneLine()
-{
- uint8_t lastCycle = vic->getCyclesPerRasterline();
- for (int i = rasterlineCycle; i <= lastCycle; i++) {
- if (!executeOneCycle())
- return false;
- }
- return true;
-}
-
-
-// -----------------------------------------------------------------------------------------------
-// ROM and snapshot handling
-// -----------------------------------------------------------------------------------------------
-uint8_t
-C64::getMissingRoms() {
-
- uint8_t missingRoms = 0;
-
- if (!mem->basicRomIsLoaded()) missingRoms |= BASIC_ROM;
- if (!mem->charRomIsLoaded()) missingRoms |= CHAR_ROM;
- if (!mem->kernelRomIsLoaded()) missingRoms |= KERNEL_ROM;
- if (!floppy->mem->romIsLoaded()) missingRoms |= VC1541_ROM;
- return missingRoms;
-}
+//
+//! @functiongroup Managing the execution thread
+//
-bool
-C64::loadRom(const char *filename)
+void
+C64::setWarp(bool b)
{
- bool result = false;
-
- suspend();
-
- bool wasRunnable = isRunnable();
-
- if (C64Memory::isBasicRom(filename)) {
- result = mem->loadBasicRom(filename);
- if (result) putMessage(MSG_ROM_LOADED, BASIC_ROM);
- }
-
- if (C64Memory::isCharRom(filename)) {
- result = mem->loadCharRom(filename);
- if (result) putMessage(MSG_ROM_LOADED, CHAR_ROM);
- }
-
- if (C64Memory::isKernelRom(filename)) {
- result = mem->loadKernelRom(filename);
- if (result) putMessage(MSG_ROM_LOADED, KERNEL_ROM);
- }
-
- if (VC1541Memory::is1541Rom(filename)) {
- result = floppy->mem->loadRom(filename);
- if (result) putMessage(MSG_ROM_LOADED, VC1541_ROM);
- }
-
- bool isNowRunnable = isRunnable();
-
- if (!wasRunnable && isNowRunnable) {
- // Last missing ROM was loaded
- putMessage(MSG_ROM_COMPLETE);
- }
-
- resume();
- return result;
-}
+ if (warp == b)
+ return;
+
+ warp = b;
-void
-C64::takeSnapshot()
-{
- debug(3, "Taking snapshop %d (%p)\n", backInTimeWritePtr, backInTimeHistory[backInTimeWritePtr]);
+ // Warping has the unavoidable drawback that audio playback gets out of sync. To cope with this issue,
+ // we silence SID during warp mode and smoothly bring back sound when warping ends.
- saveToSnapshot(backInTimeHistory[backInTimeWritePtr]);
- backInTimeWritePtr = (backInTimeWritePtr + 1) % BACK_IN_TIME_BUFFER_SIZE;
+ if (warp) {
+ // Quickly fade out SID
+ sid.rampDown();
+
+ } else {
+ // Smoothly fade in SID
+ sid.rampUp();
+ restartTimer();
+ }
+
+ putMessage(MSG_WARP, b);
}
-unsigned
-C64::numHistoricSnapshots()
+void
+C64::setAlwaysWarp(bool b)
{
- for (int i = BACK_IN_TIME_BUFFER_SIZE - 1; i >= 0; i--) {
- if (!backInTimeHistory[i]->isEmpty())
- return i + 1;
- }
- return 0;
+ if (alwaysWarp == b)
+ return;
+
+ if (alwaysWarp != b) {
+ alwaysWarp = b;
+ setWarp(b);
+ putMessage(MSG_ALWAYS_WARP, b);
+ }
}
-Snapshot *
-C64::getHistoricSnapshot(int nr)
+void
+C64::setWarpLoad(bool b)
{
- if (nr >= BACK_IN_TIME_BUFFER_SIZE)
- return NULL;
-
- int pos = (BACK_IN_TIME_BUFFER_SIZE + backInTimeWritePtr - 1 - nr) % BACK_IN_TIME_BUFFER_SIZE;
- Snapshot *snapshot = backInTimeHistory[pos];
- assert(snapshot != NULL);
-
- if (snapshot->isEmpty())
- return NULL;
-
- return snapshot;
+ warpLoad = b;
}
-
-// -----------------------------------------------------------------------------------------------
-// Timing
-// -----------------------------------------------------------------------------------------------
-
-void
-C64::restartTimer()
+void
+C64::restartTimer()
{
uint64_t kernelNow = mach_absolute_time();
uint64_t nanoNow = abs_to_nanos(kernelNow);
- nanoTargetTime = nanoNow + vic->getFrameDelay();
+ nanoTargetTime = nanoNow + vic.getFrameDelay();
}
-void
+void
C64::synchronizeTiming()
{
const uint64_t earlyWakeup = 1500000; /* 1.5 milliseconds */
-
+
// Convert usec into kernel unit
int64_t kernelTargetTime = nanos_to_abs(nanoTargetTime);
-
+
// Check how long we're supposed to sleep
int64_t timediff = kernelTargetTime - (int64_t)mach_absolute_time();
if (timediff > 200000000 /* 0.2 sec */) {
-
+
// The emulator seems to be out of sync, so we better reset the synchronization timer
debug(2, "Emulator lost synchronization (%lld). Restarting synchronization timer.\n", timediff);
restartTimer();
// return;
}
-
+
// Sleep and update target timer
// debug(2, "%p Sleeping for %lld\n", this, kernelTargetTime - mach_absolute_time());
int64_t jitter = sleepUntil(kernelTargetTime, earlyWakeup);
- nanoTargetTime += vic->getFrameDelay();
-
+ nanoTargetTime += vic.getFrameDelay();
+
// debug(2, "Jitter = %d", jitter);
if (jitter > 1000000000 /* 1 sec */) {
@@ -920,9 +763,136 @@ C64::synchronizeTiming()
}
}
-// ---------------------------------------------------------------------------------------------
-// Archives (disks, tapes, etc.)
-// ---------------------------------------------------------------------------------------------
+
+//
+//! @functiongroup Loading ROM images
+//
+
+uint8_t
+C64::getMissingRoms() {
+
+ uint8_t missingRoms = 0;
+
+ if (!mem.basicRomIsLoaded()) missingRoms |= BASIC_ROM;
+ if (!mem.charRomIsLoaded()) missingRoms |= CHAR_ROM;
+ if (!mem.kernelRomIsLoaded()) missingRoms |= KERNEL_ROM;
+ if (!floppy.mem.romIsLoaded()) missingRoms |= VC1541_ROM;
+ return missingRoms;
+}
+
+bool
+C64::loadRom(const char *filename)
+{
+ bool result = false;
+
+ debug(1, "Trying to load ROM image %s\n", filename);
+
+ suspend();
+ bool wasRunnable = isRunnable();
+
+ if (C64Memory::isBasicRom(filename)) {
+ result = mem.loadBasicRom(filename);
+ if (result) putMessage(MSG_ROM_LOADED, BASIC_ROM);
+ }
+
+ if (C64Memory::isCharRom(filename)) {
+ result = mem.loadCharRom(filename);
+ if (result) putMessage(MSG_ROM_LOADED, CHAR_ROM);
+ }
+
+ if (C64Memory::isKernelRom(filename)) {
+ result = mem.loadKernelRom(filename);
+ if (result) putMessage(MSG_ROM_LOADED, KERNEL_ROM);
+ }
+
+ if (VC1541Memory::is1541Rom(filename)) {
+ result = floppy.mem.loadRom(filename);
+ if (result) putMessage(MSG_ROM_LOADED, VC1541_ROM);
+ }
+
+ bool isNowRunnable = isRunnable();
+
+ if (!wasRunnable && isNowRunnable) { // Good news! All ROMs are in place
+ putMessage(MSG_ROM_COMPLETE);
+ }
+ resume();
+
+ return result;
+}
+
+
+//
+//! @functiongroup Loading and saving snapshots
+//
+
+void C64::loadFromSnapshot(Snapshot *snapshot)
+{
+ if (snapshot == NULL)
+ return;
+
+ uint8_t *ptr = snapshot->getData();
+ loadFromBuffer(&ptr);
+ ping();
+}
+
+void
+C64::saveToSnapshot(Snapshot *snapshot)
+{
+ if (snapshot == NULL)
+ return;
+
+ snapshot->setTimestamp(time(NULL));
+ snapshot->takeScreenshot((uint32_t *)vic.screenBuffer(), isPAL());
+
+ snapshot->alloc(stateSize());
+ uint8_t *ptr = snapshot->getData();
+ saveToBuffer(&ptr);
+}
+
+
+
+
+
+
+void
+C64::takeSnapshot()
+{
+ debug(3, "Taking snapshop %d (%p)\n", backInTimeWritePtr, backInTimeHistory[backInTimeWritePtr]);
+
+ saveToSnapshot(backInTimeHistory[backInTimeWritePtr]);
+ backInTimeWritePtr = (backInTimeWritePtr + 1) % BACK_IN_TIME_BUFFER_SIZE;
+}
+
+unsigned
+C64::numHistoricSnapshots()
+{
+ for (int i = BACK_IN_TIME_BUFFER_SIZE - 1; i >= 0; i--) {
+ if (!backInTimeHistory[i]->isEmpty())
+ return i + 1;
+ }
+ return 0;
+}
+
+Snapshot *
+C64::getHistoricSnapshot(int nr)
+{
+ if (nr >= BACK_IN_TIME_BUFFER_SIZE)
+ return NULL;
+
+ int pos = (BACK_IN_TIME_BUFFER_SIZE + backInTimeWritePtr - 1 - nr) % BACK_IN_TIME_BUFFER_SIZE;
+ Snapshot *snapshot = backInTimeHistory[pos];
+ assert(snapshot != NULL);
+
+ if (snapshot->isEmpty())
+ return NULL;
+
+ return snapshot;
+}
+
+
+//
+//! @functiongroup Handling archives, tapes, and cartridges
+//
bool
C64::flushArchive(Archive *a, int item)
@@ -939,7 +909,7 @@ C64::flushArchive(Archive *a, int item)
data = a->getByte();
if (data < 0) break;
- mem->pokeRam(addr, (uint8_t)data);
+ mem.pokeRam(addr, (uint8_t)data);
if (addr == 0xFFFF) break;
addr++;
@@ -953,7 +923,7 @@ C64::mountArchive(Archive *a)
if (a == NULL)
return false;
- floppy->insertDisk(a);
+ floppy.insertDisk(a);
return true;
}
@@ -963,45 +933,29 @@ C64::insertTape(TAPArchive *a)
if (a == NULL)
return false;
+ debug(1, "Inserting TAP archive into datasette\n");
+
suspend();
- debug("Inserting tape %p\n", a);
datasette.insertTape(a);
resume();
return true;
}
-// ---------------------------------------------------------------------------------------------
-// Cartridges
-// ---------------------------------------------------------------------------------------------
-
bool
C64::attachCartridge(Cartridge *c)
{
- return expansionport->attachCartridge(c);
+ return expansionport.attachCartridge(c);
}
void
C64::detachCartridge()
{
- expansionport->detachCartridge();
+ expansionport.detachCartridge();
}
bool
C64::isCartridgeAttached()
{
- return expansionport->getCartridgeAttached();
-}
-
-
-// ---------------------------------------------------------------------------------------------
-// Misc
-// ---------------------------------------------------------------------------------------------
-
-void
-C64::threadCleanup()
-{
- p = NULL;
- debug(1, "Execution thread cleanup\n");
+ return expansionport.getCartridgeAttached();
}
-
diff --git a/C64/C64.h b/C64/C64.h
index 33b0d03..6c026fe 100755
--- a/C64/C64.h
+++ b/C64/C64.h
@@ -1,40 +1,38 @@
-/* Written by Dirk W. Hoffmann, 2006 - 2015
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header C64.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// VERSION 1.4.1:
-// ENHANCEMENTS:
+
+// VERSION 1.4.2:
//
-// Applied some speed optimizations to the new VIC code. At maximum speed, the
-// emulator is up to 10% faster now.
-// In the debug menu, option "Hide sprites" was broken. This has been fixed.
+// The ESC key on the Mac keyboard is now mapped to the C64s runstop key and the TAB key to the restore key.
//
// TODO:
-// Add subtext "xx Tracks", "Type 0 tape" to Media Dialog
// Use better text descriptions in Mount dialog for G64 and NIB files
// Cartridge dialog
//
// CLEANUP:
-// Remove MyOpenGLView class
+// 1. Remove MyOpenGLView class
//
// SPEEDUP:
//
-// 1. Make CIA1 and CIA2 dynamic objects
-// Inline execution functions as much as possible
-// 2. Add routine to quickly get the disk name from GCR data
+// 1. Add routine to quickly get the disk name from GCR data
// Right now, the hardware dialog takes some time to open
//
// ENHANCEMENTS (BRAIN STORMING):
@@ -47,16 +45,11 @@
#define _C64_INC
#define NDEBUG // RELEASE
-#define DEBUG_LEVEL 2 // RELEASE
-
-// Snapshot version number of this release
-#define V_MAJOR 1
-#define V_MINOR 4
-#define V_SUBMINOR 1
-#include "basic.h"
-#include "VirtualComponent.h"
+// General
#include "Message.h"
+
+// Loading and saving
#include "Snapshot.h"
#include "T64Archive.h"
#include "D64Archive.h"
@@ -66,6 +59,8 @@
#include "PRGArchive.h"
#include "P00Archive.h"
#include "FileArchive.h"
+
+// Sub components
#include "IEC.h"
#include "Keyboard.h"
#include "Joystick.h"
@@ -78,19 +73,22 @@
#include "TOD.h"
#include "CIA.h"
#include "CPU.h"
+
+// Peripherals
#include "VC1541.h"
#include "Datasette.h"
#include "Cartridge.h"
#include "ExpansionPort.h"
-//! A complete virtual C64
-/*! The class puts all components together to a working virtual computer.
+//! @class A complete virtual C64
+
+/*
------------------------ ------------------------
| | | |
-->| C64Proxy |<-->| C64 |
- | | Obj-C / C++ bridge | | (c++ world) |
+ | | Obj-C / C++ bridge | | (C++ world) |
| ------------------------ ------------------------
| |
| ------------------------ |
@@ -176,74 +174,74 @@
class C64 : public VirtualComponent {
-public:
-
- //! Message queue. Used to communicate with the graphical user interface.
- MessageQueue queue;
-
- //! Reference to the connected virtual memory.
- C64Memory *mem;
+ // -----------------------------------------------------------------------------------------------
+ // Properties
+ // -----------------------------------------------------------------------------------------------
+
+public:
+
+ //
+ // Sub components
+ //
+
+ //! @brief The C64s virtual memory (ROM, RAM, and color RAM)
+ C64Memory mem;
- //! Reference to the connected virtual CPU.
- CPU *cpu;
+ //! @brief The C64s virtual CPU
+ CPU cpu;
- //! Reference to the connected virtual video controller (VIC).
- VIC *vic;
+ //! @brief The C64s video controller chip
+ VIC vic;
- //! Reference to the first connected virtual complex interface adapter (CIA 1).
- CIA1 *cia1;
+ //! @brief The C64s first versatile interface adapter
+ CIA1 cia1;
- //! Reference to the first connected virtual complex interface adapter (CIA 2).
- CIA2 *cia2;
+ //! @brief The C64s second versatile interface adapter
+ CIA2 cia2;
- //! Reference to the connected sound interface device (SID).
- SIDWrapper *sid;
+ //! @brief The C64s sound chip
+ SIDWrapper sid;
- //! Reference to the connected virtual keyboard.
- Keyboard *keyboard;
+ //! @brief The C64s virtual keyboard
+ Keyboard keyboard;
- //! Reference to joystick in port 1
- Joystick *joystick1;
+ //! @brief The C64s first virtual joystick (plugged into CONTROL PORT 1)
+ Joystick joystickA;
- //! Reference to joystick in port 2
- Joystick *joystick2;
+ //! @brief The C64s second virtual joystick (plugged into CONTROL PORT 2)
+ Joystick joystickB;
- //! Reference to the virtual IEC bus
- IEC *iec;
+ //! @brief The C64s interface bus connecting the VC1541 drive
+ IEC iec;
- //! Reference to the virtual expansion port (cartdrige slot)
- ExpansionPort *expansionport;
+ //! @brief The C64s virtual expansion port (cartdrige slot)
+ ExpansionPort expansionport;
- //! Reference to the virtual VC1541
- VC1541 *floppy;
+ //! @brief A virtual VC1541 floppy drive
+ VC1541 floppy;
- //! Virtual tape drive (Datasette)
+ //! @brief A virtual datasette
Datasette datasette;
private:
- //! The execution thread
- pthread_t p;
-
- //! System timer information (needed for running the execution thread at the desired speed)
- mach_timebase_info_data_t timebase;
+ //
+ // Execution thread
+ //
- //! Converts kernel time to nanoseconds
- uint64_t abs_to_nanos(uint64_t abs) { return abs * timebase.numer / timebase.denom; }
-
- //! Converts nanoseconds to kernel time
- uint64_t nanos_to_abs(uint64_t nanos) { return nanos * timebase.denom / timebase.numer; }
-
- //! Snapshot history ring buffer (for cheatbox)
- Snapshot *backInTimeHistory[BACK_IN_TIME_BUFFER_SIZE];
+ //! @brief The emulators execution thread
+ pthread_t p;
- //! ring buffer write pointer
- unsigned backInTimeWritePtr;
+ /*! @brief System timer information
+ * @details Used to put the emulation thread to sleep for the proper amount of time
+ */
+ mach_timebase_info_data_t timebase;
- // -----------------------------------------------------------------------------------------------
- // Properties
- // -----------------------------------------------------------------------------------------------
+ /*! @brief Wake-up time of the synchronization timer in nanoseconds
+ * @details This value is recomputed each time the emulator thread is put to sleep
+ */
+ uint64_t nanoTargetTime;
//! Indicates if c64 is currently running at maximum speed (with timing synchronization disabled)
bool warp;
@@ -253,29 +251,47 @@ private:
//! Indicates that we should run as fast as possible at least during disk operations
bool warpLoad;
-
-public:
- //! Current clock cycle since power up
- uint64_t cycles;
-
- //! Current clock cycle relative to the current rasterline
- /*! Range: 1 ... 63 on PAL machines
- 1 ... 65 on NTSC machines */
- int rasterlineCycle;
- //! Current frame number since power up
+ //
+ // Executed cycle, rasterline, and frame
+ //
+
+ //! @brief Elapsed C64 clock cycles since power up
+ uint64_t cycle;
+
+ //! @brief Total number of frames drawn since power up
uint64_t frame;
- //! Current rasterline number
+ //! @brief Currently drawn rasterline
uint16_t rasterline;
-private:
+ /*! @brief Currently executed clock cycle relative to the current rasterline
+ * @details Range: 1 ... 63 on PAL machines, 1 ... 65 on NTSC machines
+ */
+ uint8_t rasterlineCycle;
+
+
+ //
+ // Time travel ring buffer
+ //
+
+ //! @brief Ring buffer for storing the time travel snapshot images
+ Snapshot *backInTimeHistory[BACK_IN_TIME_BUFFER_SIZE];
+
+ //! @brief Write pointer of the time travel ring buffer
+ unsigned backInTimeWritePtr;
- //! Target time in nanoseconds
- /*! Used to synchronize emulation speed. */
- uint64_t nanoTargetTime;
+ //
+ // Message queue
+ //
+
+ /*! @brief Message queue.
+ * @details Used to communicate with the graphical user interface.
+ */
+ MessageQueue queue;
+
// -----------------------------------------------------------------------------------------------
// Methods
@@ -283,241 +299,260 @@ private:
public:
- //! Constructor
+ //! @brief Constructor
C64();
- //! Destructor
+ //! @brief Destructor
~C64();
- //! Reset the virtual C64 and all of its sub components.
- /*! A reset is performed by simulating a hard reset on a real C64. */
+ //! @brief Resets the virtual C64 and all of its sub components.
void reset();
- //! Dump current configuration into message queue
+ //! @brief Dumps current configuration into message queue
void ping();
- //! Dump current state into logfile
+ //! @brief Prints debugging information
void dumpState();
- // -----------------------------------------------------------------------------------------------
- // Configure hardware
- // -----------------------------------------------------------------------------------------------
+ //
+ //! @functiongroup Configuring the emulator
+ //
-public:
-
- //! Returns true for PAL machines
- inline bool isPAL() { return vic->isPAL(); }
+ //! @brief Returns true if the emulator is currently running in PAL mode
+ inline bool isPAL() { return vic.isPAL(); }
- //! Set PAL mode
- // DEPRECATED. PAL/NTSC IS DETERMINED BY VIC CHIP MODEL
+ /*! @brief Puts the emulator in PAL mode
+ * @details This method plugs in a PAL VIC chip and reconfigures SID with the proper timing information
+ */
void setPAL();
- //! Returns true for NTSC machines
- // DEPRECATED. PAL/NTSC IS DETERMINED BY VIC CHIP MODEL
- inline bool isNTSC() { return !vic->isPAL(); }
+ //! @brief Returns true if the emulator is currently running in NTSC mode
+ inline bool isNTSC() { return !vic.isPAL(); }
- //! Set NTSC mode
+ /*! @brief Puts the emulator in PAL mode
+ * @details This method plugs in a PAL VIC chip and reconfigures SID with the proper timing information
+ */
void setNTSC();
- //! Returns true iff audio filters are enabled.
- bool getAudioFilter() { return sid->getAudioFilter(); }
+ //! @brief Returns true iff audio filters are enabled.
+ bool getAudioFilter() { return sid.getAudioFilter(); }
- //! Enable or disable filters of SID.
- void setAudioFilter(bool value) { sid->setAudioFilter(value); }
+ //! @brief Enables or disables SID audio filters.
+ void setAudioFilter(bool value) { sid.setAudioFilter(value); }
- //! Returns true if reSID library is used
- bool getReSID() { return sid->getReSID(); }
+ //! @brief Returns true if reSID library is used
+ bool getReSID() { return sid.getReSID(); }
- //! Turn reSID library on or off
- void setReSID(bool value) { sid->setReSID(value); }
+ //! @brief Turns reSID library on or off
+ void setReSID(bool value) { sid.setReSID(value); }
- //! Get sampling method
- inline sampling_method getSamplingMethod() { return sid->getSamplingMethod(); }
+ //! @brief Gets the sampling method
+ inline sampling_method getSamplingMethod() { return sid.getSamplingMethod(); }
- //! Set sampling method
- void setSamplingMethod(sampling_method value) { sid->setSamplingMethod(value); }
+ //! @brief Sets the sampling method
+ void setSamplingMethod(sampling_method value) { sid.setSamplingMethod(value); }
- //! Get chip model
- inline chip_model getChipModel() { return sid->getChipModel(); }
+ //! @brief Gets the SID chip model
+ inline chip_model getChipModel() { return sid.getChipModel(); }
- //! Set chip model
- void setChipModel(chip_model value) { sid->setChipModel(value); }
-
-
- // -----------------------------------------------------------------------------------------------
- // Loading and saving
- // -----------------------------------------------------------------------------------------------
+ //! @brief Sets the SID chip model
+ void setChipModel(chip_model value) { sid.setChipModel(value); }
-public:
-
- //! Load state from snapshot container
- void loadFromSnapshot(Snapshot *snapshot);
-
- //! Save state to snapshot container
- void saveToSnapshot(Snapshot *snapshot);
-
-
- // -----------------------------------------------------------------------------------------------
- // Control
- // -----------------------------------------------------------------------------------------------
-
-public:
-
- //! Perform a soft reset
- /*! On a real C64, a soft reset is triggered by hitting Runstop and Restore */
- void runstopRestore();
-
- //! Returns true iff the virtual C64 is able to run (i.e., all ROMs are loaded)
- bool isRunnable();
-
- //! Power on virtual C64
- /*! The execution thread is launched and the virtual computer enters the "running" state */
+
+ //
+ //! @functiongroup Running the emulator
+ //
+
+ //! @brief Launches the emulator
+ /*! @details The execution thread is launched and the virtual computer enters the "running" state
+ */
void run();
- // Returns true iff the virtual C64 is in the "running" state */
+ /*! @brief The tread exit function.
+ * @details This method is invoked automatically when the execution thread terminates.
+ */
+ void threadCleanup();
+
+ //! @brief Returns true iff the virtual C64 is able to run (i.e., all ROMs are loaded)
+ bool isRunnable();
+
+ //! @brief Returns true iff the virtual C64 is in the "running" state
bool isRunning();
- //! Freeze the emulator
- /*! The execution thread is terminated and the virtual computers enters the "halted" state */
+ /*! @brief Freezes the emulator
+ * @details The execution thread is terminated and the virtual computers enters the "halted" state
+ */
void halt();
- //! Returns true iff the virtual C64 is in the "halted" state */
+ //! @brief Returns true iff the virtual C64 is in the "halted" state
bool isHalted();
- //! Execute one command
+ /*! @brief Perform a manually triggered NMI interrupt
+ * @details On a real C64, an NMI interrupt is triggered by hitting the restore key
+ */
+ // void restore();
+
+ /*! @brief Perform a soft reset
+ * @details On a real C64, a soft reset is triggered by hitting Runstop and Restore
+ */
+ // void runstopRestore();
+
+ /*! @brief Executes one CPU instruction
+ * @details This method implements the "step" action of the debugger
+ */
void step();
- //! Execute virtual C64 for one cycle
- inline bool executeOneCycle();
-
- //! Execute until the end of the rasterline
+ //! @brief Executes until the end of the rasterline
bool executeOneLine();
-
- //! Get notification message from message queue
- Message *getMessage();
-
- //! Feed notification message into message queue
- void putMessage(int id, int i = 0, void *p = NULL, const char *c = NULL);
-
+ //! @brief Executes virtual C64 for one cycle
+ inline bool executeOneCycle();
+
private:
- //! Actions to be performed at the beginning of each rasterline
+
+
+ //! @brief Invoked before executing the first cycle of rasterline
void beginOfRasterline();
- //! Actions to be performed at the end of each rasterline
+ //! @brief Invoked after executing the last cycle of rasterline
void endOfRasterline();
-
-
- // -----------------------------------------------------------------------------------------------
- // ROM and snapshot handling
- // -----------------------------------------------------------------------------------------------
-
-public:
- //! Missing ROMs are indicated by a 1 in the returned bitmap */
- uint8_t getMissingRoms();
-
- //! Load ROM image into memory
- bool loadRom(const char *filename);
-
- //! Take a snapshot and store it in ringbuffer
- void takeSnapshot();
-
- //! Returns the number of previously taken snapshots
- /*! Returns a number between 0 and BACK_IN_TIME_BUFFER_SIZE */
- unsigned numHistoricSnapshots();
-
- //! Get snapshot from history buffer
- /*! The latest snapshot has number 0. Return NULL, if requested snapshot does not exist */
- Snapshot *getHistoricSnapshot(int nr);
-
-
- // -----------------------------------------------------------------------------------------------
- // Timing
- // -----------------------------------------------------------------------------------------------
-
+
+ //
+ //! @functiongroup Managing the execution thread
+ //
+
+ //! @brief Converts kernel time to nanoseconds
+ uint64_t abs_to_nanos(uint64_t abs) { return abs * timebase.numer / timebase.denom; }
+
+ //! @brief Converts nanoseconds to kernel time
+ uint64_t nanos_to_abs(uint64_t nanos) { return nanos * timebase.denom / timebase.numer; }
+
public:
- //! Returns true iff cpu runs at maximum speed (timing sychronization is disabled)
+ //! @brief Returns true iff cpu runs at maximum speed (timing sychronization is disabled).
inline bool getWarp() { return warp; }
- //! Enable or disable timing synchronization
+ //! @brief Enables or disables timing synchronization.
void setWarp(bool b);
- //! Returns true iff cpu should always run at maximun speed
+ //! @brief Returns true iff cpu should always run at maximun speed.
inline bool getAlwaysWarp() { return alwaysWarp; }
- //! Setter for alwaysWarp
+ //! @brief Setter for alwaysWarp.
void setAlwaysWarp(bool b);
- //! Returns true iff warp mode is activated during disk operations
+ //! @brief Returns true iff warp mode is activated during disk operations.
inline bool getWarpLoad() { return warpLoad; }
- //! Setter for warpLoad
+ //! @brief Setter for warpLoad.
void setWarpLoad(bool b);
- //! Initialize timer (sets variable target_time)
- void restartTimer();
-
- //! Wait until target_time has been reached and then updates target_time.
- void synchronizeTiming();
-
+ /*! @brief Restarts the synchronization timer
+ * @details The function is invoked at launch time to initialize the timer and reinvoked
+ * when the synchronization timer gets out of sync.
+ */
+ void restartTimer();
- // ---------------------------------------------------------------------------------------------
- // Archives (disks, tapes, etc.)
- // ---------------------------------------------------------------------------------------------
-
-public:
+ //! @brief Waits until target_time has been reached and then updates target_time.
+ void synchronizeTiming();
+
+
+ //
+ //! @functiongroup Accessing cycle, rasterline, and frame information
+ //
+
+ //! @brief Returns the number of CPU cycles elapsed so far.
+ inline uint64_t getCycles() { return cycle; }
+
+ //! @brief Returns the number of the currently drawn frame.
+ inline uint64_t getFrame() { return frame; }
+
+ //! @brief Returns the number of the currently drawn rasterline.
+ inline uint16_t getRasterline() { return rasterline; }
+
+ //! @brief Returns the currently executed rasterline clock cycle
+ inline uint8_t getRasterlineCycle() { return rasterlineCycle; }
+
+
+ //
+ //! @functiongroup Loading ROM images
+ //
+
+ /*! @brief Provides information about missing ROM images.
+ * @details Each missing ROM is indicated by a 1 in the returned bitmap.
+ */
+ uint8_t getMissingRoms();
+
+ //! @brief Loads ROM image into memory
+ bool loadRom(const char *filename);
- //! Flush specified item from archive into memory and delete archive
- /*! All archive types are flushable */
+
+ //
+ //! @functiongroup Loading and saving snapshots
+ //
+
+ //! @brief Loads the current state from a snapshot container
+ void loadFromSnapshot(Snapshot *snapshot);
+
+ //! @brief Saves the current state to a snapshot container
+ void saveToSnapshot(Snapshot *snapshot);
+
+ //! @brief Takes a snapshot and stores it into the time travel ringbuffer
+ void takeSnapshot();
+
+ /*! @brief Returns the number of previously taken snapshots
+ * @result Value between 0 and BACK_IN_TIME_BUFFER_SIZE
+ */
+ unsigned numHistoricSnapshots();
+
+ /*! @brief Reads a snapshopt from the time travel ringbuffer
+ * @details The latest snapshot is indexed 0.
+ * @result A reference to a snapshot, if present. NULL, otherwise.
+ */
+ Snapshot *getHistoricSnapshot(int nr);
+
+
+ //
+ //! @functiongroup Handling archives, tapes, and cartridges
+ //
+
+ /*! @brief Flush specified item from archive into memory and delete archive.
+ * @details All archive types are flushable.
+ */
bool flushArchive(Archive *a, int item);
- //! @brief Inserts an archive as a virtual floppy disk
- /*! @discussion Only D64 and G64 archives are supported */
+ /*! @brief Inserts an archive as a virtual floppy disk.
+ * @details Only D64 and G64 archives are supported.
+ */
bool mountArchive(Archive *a);
- //! @brief Inserts a TAP archive as a virtual datasette tape
- /*! @discussion Only TAP archives can be used as tape */
+ /*! @brief Inserts a TAP archive as a virtual datasette tape.
+ * @details Only TAP archives can be used as tape.
+ */
bool insertTape(TAPArchive *a);
-
- // ---------------------------------------------------------------------------------------------
- // Cartridges
- // ---------------------------------------------------------------------------------------------
-
- //! Attach cartridge
+ //! @brief Attaches a cartridge to the expansion port.
bool attachCartridge(Cartridge *c);
- // Detach cartridge
+ //! @brief Detaches a cartridge from the expansion port.
void detachCartridge();
- //! Returns true iff a cartridge is attached
+ //! @brief Returns true iff a cartridge is attached.
bool isCartridgeAttached();
-
- // ---------------------------------------------------------------------------------------------
- // Getter and setter
- // ---------------------------------------------------------------------------------------------
-
- //! Returns the number of CPU cycles elapsed so far
- inline uint64_t getCycles() { return cycles; }
-
- //! Returns the number of the currently drawn frame
- inline uint64_t getFrame() { return frame; }
-
- //! Returns the number of the currently drawn rasterline
- inline uint16_t getRasterline() { return rasterline; }
+ //
+ //! @functiongroup Accessing the message queue
+ //
- // ---------------------------------------------------------------------------------------------
- // Misc
- // ---------------------------------------------------------------------------------------------
-
- //! The tread exit function.
- /*! Automatically invoked by the execution thread on termination */
- void threadCleanup();
+ //! @brief Gets a notification message from message queue
+ Message *getMessage() { return queue.getMessage(); }
+
+ //! @brief Feeds a notification message into message queue
+ void putMessage(int id, int i = 0, void *p = NULL, const char *c = NULL) { queue.putMessage(id, i, p, c); }
+
};
#endif
diff --git a/C64/C64Memory.cpp b/C64/C64Memory.cpp
old mode 100755
new mode 100644
index 381303f..708427a
--- a/C64/C64Memory.cpp
+++ b/C64/C64Memory.cpp
@@ -24,8 +24,9 @@
C64Memory::C64Memory()
{
- name ="C64 memory";
- debug (2, " Creating main memory at address %p...\n", this);
+ setDescription("C64 memory");
+
+ debug (3, " Creating main memory at address %p...\n", this);
charRomFile = NULL;
kernelRomFile = NULL;
@@ -48,7 +49,7 @@ C64Memory::C64Memory()
C64Memory::~C64Memory()
{
- debug(2, " Releasing main memory at address %p...\n", this);
+ debug(3, " Releasing main memory at address %p...\n", this);
}
void C64Memory::reset()
@@ -56,11 +57,8 @@ void C64Memory::reset()
VirtualComponent::reset();
// Establish bindings
- vic = c64->vic;
- sid = c64->sid;
- cia1 = c64->cia1;
- cia2 = c64->cia2;
- cpu = c64->cpu;
+ // sid = &c64->sid;
+ cpu = &c64->cpu;
// Initialize RAM (powerup pattern similar to Frodo and VICE)
for (unsigned i = 0; i < sizeof(ram); i++)
@@ -223,23 +221,11 @@ bool C64Memory::isValidAddr(uint16_t addr, MemoryType type)
}
}
-uint8_t
-C64Memory::peekRam(uint16_t addr)
-{
- return ram[addr];
-}
-
-uint8_t
-C64Memory::peekRom(uint16_t addr)
-{
- return rom[addr];
-}
-
void
C64Memory::updatePeekPokeLookupTables()
{
- uint8_t EXROM = c64->expansionport->getExromLine() ? 0x10 : 0x00;
- uint8_t GAME = c64->expansionport->getGameLine() ? 0x08 : 0x00;
+ uint8_t EXROM = c64->expansionport.getExromLine() ? 0x10 : 0x00;
+ uint8_t GAME = c64->expansionport.getGameLine() ? 0x08 : 0x00;
uint8_t index = (cpu->getPortLines() & 0x07) | EXROM | GAME;
@@ -299,33 +285,33 @@ uint8_t C64Memory::peekIO(uint16_t addr)
if (addr <= 0xD3FF) {
// Note: Only the lower 6 bits are used for adressing the VIC I/O space
// Therefore, the VIC I/O memory repeats every 64 bytes
- return vic->peek(addr & 0x003F);
+ return c64->vic.peek(addr & 0x003F);
}
// 0xD400 - 0xD7FF (SID)
if (addr <= 0xD7FF) {
// Note: Only the lower 5 bits are used for adressing the SID I/O space
// Therefore, the SID I/O memory repeats every 32 bytes
- return sid->peek(addr & 0x001F);
+ return c64->sid.peek(addr & 0x001F);
}
// 0xD800 - 0xDBFF (Color RAM)
if (addr <= 0xDBFF) {
- return (colorRam[addr - 0xD800] & 0x0F) | (vic->getDataBus() & 0xF0);
+ return (colorRam[addr - 0xD800] & 0x0F) | (c64->vic.getDataBus() & 0xF0);
}
// 0xDC00 - 0xDCFF (CIA 1)
if (addr <= 0xDCFF) {
// Note: Only the lower 4 bits are used for adressing the CIA I/O space
// Therefore, the CIA I/O memory repeats every 16 bytes
- return cia1->peek(addr & 0x000F);
+ return c64->cia1.peek(addr & 0x000F);
}
// 0xDD00 - 0xDDFF (CIA 2)
if (addr <= 0xDDFF) {
// Note: Only the lower 4 bits are used for adressing the CIA I/O space
// Therefore, the CIA I/O memory repeats every 16 bytes
- return cia2->peek(addr & 0x000F);
+ return c64->cia2.peek(addr & 0x000F);
}
// 0xDE00 - 0xDEFF (I/O area 1)
@@ -339,7 +325,7 @@ uint8_t C64Memory::peekIO(uint16_t addr)
Lesen von offenen Adressen liefert nämlich auf vielen C64 das zuletzt vom
VIC gelesene Byte zurück!)" [C.B.] */
- return vic->getDataBus();
+ return c64->vic.getDataBus();
}
assert(false);
@@ -364,8 +350,8 @@ uint8_t C64Memory::peek(uint16_t addr)
case M_CRTLO:
case M_CRTHI:
- if (c64->expansionport->romIsBlendedIn(addr))
- return c64->expansionport->peek(addr);
+ if (c64->expansionport.romIsBlendedIn(addr))
+ return c64->expansionport.peek(addr);
else
return ram[addr];
@@ -401,31 +387,13 @@ uint8_t C64Memory::peek(uint16_t addr)
// Poke
// --------------------------------------------------------------------------------
-void C64Memory::pokeRam(uint16_t addr, uint8_t value)
-{
- ram[addr] = value;
-}
-
-void C64Memory::pokeRom(uint16_t addr, uint8_t value)
-{
-#if 0
- if (cartridge != NULL && cartridge->isRomAddr(addr)) {
- cartridge->poke(addr, value);
- } else {
- rom[addr] = value;
- }
-#endif
-
- rom[addr] = value;
-}
-
void C64Memory::pokeIO(uint16_t addr, uint8_t value)
{
// 0xD000 - 0xD3FF (VIC)
if (addr < 0xD400) {
// Note: Only the lower 6 bits are used for adressing the VIC I/O space
// Therefore, the VIC I/O memory repeats every 64 bytes
- vic->poke(addr & 0x003F, value);
+ c64->vic.poke(addr & 0x003F, value);
return;
}
@@ -433,7 +401,7 @@ void C64Memory::pokeIO(uint16_t addr, uint8_t value)
if (addr < 0xD800) {
// Note: Only the lower 5 bits are used for adressing the SID I/O space
// Therefore, the SID I/O memory repeats every 32 bytes
- sid->poke(addr & 0x001F, value);
+ c64->sid.poke(addr & 0x001F, value);
return;
}
@@ -449,7 +417,7 @@ void C64Memory::pokeIO(uint16_t addr, uint8_t value)
if (addr < 0xDD00) {
// Note: Only the lower 4 bits are used for adressing the CIA I/O space
// Therefore, the VIC I/O memory repeats every 16 bytes
- cia1->poke(addr & 0x000F, value);
+ c64->cia1.poke(addr & 0x000F, value);
return;
}
@@ -457,7 +425,7 @@ void C64Memory::pokeIO(uint16_t addr, uint8_t value)
if (addr < 0xDE00) {
// Note: Only the lower 4 bits are used for adressing the CIA I/O space
// Therefore, the VIC I/O memory repeats every 16 bytes
- cia2->poke(addr & 0x000F, value);
+ c64->cia2.poke(addr & 0x000F, value);
return;
}
@@ -466,7 +434,7 @@ void C64Memory::pokeIO(uint16_t addr, uint8_t value)
if (addr < 0xE000) {
// Some registers in this area trigger a bank switch in the
// attached cartridge module. So we pass the value there...
- c64->expansionport->poke(addr, value);
+ c64->expansionport.poke(addr, value);
return;
}
diff --git a/C64/C64Memory.h b/C64/C64Memory.h
old mode 100755
new mode 100644
index 52ce9a8..680b4f7
--- a/C64/C64Memory.h
+++ b/C64/C64Memory.h
@@ -1,7 +1,9 @@
-/*
- * (C) 2008 - 2015 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
+/*!
+ * @header C64Memory.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2008 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -24,31 +26,29 @@
// Forward declarations
class VIC;
class SIDWrapper;
-class CIA1;
-class CIA2;
-//! This class represents the RAM and ROM of a virtual C64
-/*! Note that the RAM, ROM, and the I/O space are superposed and therefore share the same locations in memory.
- The contents of memory location 0x0001 determines which kind of memory is currently visible. */
+/*! @brief This class represents the RAM and ROM of a virtual C64
+ * @details Note that the RAM, ROM, and the I/O space are superposed and therefore share the same locations
+ * in memory. The contents of memory location 0x0001 determines which memory is currently visible.
+ */
class C64Memory : public Memory {
- // Memory source identifiers used inside the peek/poke lookup tables
+ //! @brief Memory source identifiers used inside the peek and poke lookup tables
enum MemorySource
{
M_RAM = 1,
M_ROM,
+ M_CHAR = M_ROM,
+ M_KERNEL = M_ROM,
+ M_BASIC = M_ROM,
M_IO,
M_CRTLO,
M_CRTHI,
M_PP,
M_NONE
};
-
-#define M_CHAR M_ROM
-#define M_KERNEL M_ROM
-#define M_BASIC M_ROM
-
- //! C64 bank mapping
+
+ //! @brief C64 bank mapping
//
// If x = (EXROM, GAME, CHAREN, HIRAM, LORAM), then
// BankMap[x][0] = mapping for range $1000 - $7FFF
@@ -97,169 +97,169 @@ class C64Memory : public Memory {
};
public:
-
- //! Reference to the connected VIC chip
- VIC *vic;
-
- //! Reference to the connected SID chip
- SIDWrapper *sid;
-
- //! References to CIA 1
- CIA1 *cia1;
-
- //! References to CIA 2
- CIA2 *cia2;
-
- //! Virtual RAM
+
+ //! @brief The C64s Random Access Memory
uint8_t ram[65536];
- //! Virtual color RAM
- /*! The color RAM is located in the I/O space, starting at D800 and ending at DBFF
- Only the lower four bits are accessible, the upper four bits are open and can show any value */
+ /*! @brief The C64s color RAM
+ * @details The color RAM is located in the I/O space, starting at $D800 and ending at $DBFF
+ * Only the lower four bits are accessible, the upper four bits are open and can show any value.
+ */
uint8_t colorRam[1024];
- //! Virtual ROM
- /*! Only specific memory cells are valid ROM locations. In total, the C64 has three ROMs that
- are located at different addresses in the ROM space. Note, that the ROMs do not span over
- the whole 64k range. Therefore, only some address are valid ROM addresses.
- \see isRomAddr */
- uint8_t rom[65536];
+ //! @brief The C64s Read Only Memory
+ /*! @details Only specific memory cells are valid ROM locations. In total, the C64 has three ROMs that
+ * are located at different addresses in the ROM space. Note, that the ROMs do not span over
+ * the whole 64k range. Therefore, only some addresses are valid ROM addresses.
+ */
+ uint8_t rom[65536];
public:
- //! Check integrity of Basic ROM image
- /*! Returns true, iff the specified file contains a valid BASIC ROM image.
- File integrity is checked via the checkFileHeader function.
- \param filename Name of the file being loaded
- \see C64:loadRom
+ /*! @brief Checks the integrity of a Basic ROM image.
+ * @details Returns true, iff the specified file contains a valid Basic ROM image.
+ * File integrity is checked via the checkFileHeader function.
*/
static bool isBasicRom(const char *filename);
- //! Check integrity of Kernel ROM image
- /*! Returns true, iff the specified file contains a valid Kernel ROM image.
- File integrity is checked via the checkFileHeader function.
- @param filename Name of the file being loaded
+ /*! @brief Checks the integrity of a Kernal ROM image file.
+ * @details Returns true, iff the specified file contains a valid Kernel ROM image.
+ * File integrity is checked via the checkFileHeader function.
*/
static bool isKernelRom(const char *filename);
- //! Check integrity of Character ROM image
- /*! Returns true, iff the specified file contains a valid Character ROM image.
- File integrity is checked via the checkFileHeader function.
- @param filename Name of the file being loaded
+ /*! @brief Checks the integrity of a Character ROM image file.
+ * @details Returns true, iff the specified file contains a valid Character ROM image.
+ * File integrity is checked via the checkFileHeader function.
*/
static bool isCharRom(const char *filename);
- //! Check integrity of ROM image
- /*! Returns true, iff the specified file is one of the three possible ROM images.
- @param filename Name of the file being loaded
+ /*! @brief Checks the integrity of a ROM image file
+ * @details Returns true, iff the specified file is one of the three possible ROM images.
*/
static bool isRom(const char *filename);
- //! Returns true, iff the provided address is in the Basic ROM address range
+ /*! @brief Returns true, iff the provided address is in the Basic ROM address range.
+ */
static inline bool isBasicRomAddr(uint16_t addr) { return (0xA000 <= addr && addr <= 0xBFFF); }
- //! Returns true, iff the provided address is in the Character ROM address range
+ /*! @brief Returns true, iff the provided address is in the Character ROM address range.
+ */
static inline bool isCharRomAddr(uint16_t addr) { return (0xD000 <= addr && addr <= 0xDFFF); }
- //! Returns true, iff the provided address is in the Kernel ROM address range
+ /*! @brief Returns true, iff the provided address is in the Kernel ROM address range.
+ */
static inline bool isKernelRomAddr(uint16_t addr) { return (0xE000 <= addr); }
- //! Returns true, iff the provided address is in the possible cartridge address ranges
+ /*! @brief Returns true, iff the provided address is in the possible cartridge address ranges.
+ */
static inline bool isCartridgeRomAddr(uint16_t addr)
{ return (0x8000 <= addr && addr <= 0x9FFF)||(0xA000 <= addr && addr <= 0xBFFF)||(0xE000 <= addr && addr <= 0xFFFE); }
- //! Returns true, iff the provided address is in one of the three ROM address ranges
+ /*! @brief Returns true, iff the provided address is in one of the three ROM address ranges.
+ */
static inline bool isRomAddr(uint16_t addr)
{ return isCharRomAddr(addr) || isKernelRomAddr(addr) || isBasicRomAddr(addr) || isCartridgeRomAddr(addr); }
private:
- //! File name of the Character ROM image.
- /*! The file name is set by the loadRom routine. It is saved for further reference, so the ROM can be reloaded any tim e. */
+ /*! @brief File name of the Character ROM image.
+ * @details The file name is set in loadRom(). It is saved for further reference, so the ROM can be reloaded
+ * any time.
+ */
char *charRomFile;
- //! File name of the Kernel ROM image.
- /*! The file name is set by the loadRom routine. It is saved for further reference, so the ROM can be reloaded any tim e. */
+ /*! @brief File name of the Kernal ROM image.
+ * @details The file name is set in loadRom(). It is saved for further reference, so the ROM can be reloaded
+ * any time.
+ */
char *kernelRomFile;
- //! File name of the Basic ROM image.
- /*! The file name is set by the loadRom routine. It is saved for further reference, so the ROM can be reloaded any tim e. */
- char *basicRomFile;
+ /*! @brief File name of the Basic ROM image.
+ * @details The file name is set in loadRom(). It is saved for further reference, so the ROM can be reloaded
+ * any time.
+ */
+ char *basicRomFile;
public:
- //! Constructor
+ //! @brief Constructor
C64Memory();
- //! Destructor
+ //! @brief Destructor
~C64Memory();
- //! Restore initial state
+ //! @brief Restores the initial state
void reset();
- //! Dump current state into logfile
+ //! @brief Prints debug information
void dumpState();
- //! Load basic ROM image into memory
+ //! @brief Loads a basic ROM image into memory
bool loadBasicRom(const char *filename);
- //! Load character ROM image into memory
+ //! @brief Loads a character ROM image into memory
bool loadCharRom(const char *filename);
- //! Load kernel ROM image into memory
+ //! @brief Loads a kernel ROM image into memory
bool loadKernelRom(const char *filename);
- //! Returns true, iff the Basic ROM is alrady loaded
+ //! @brief Returns true, iff the Basic ROM is alrady loaded
bool basicRomIsLoaded() { return basicRomFile != NULL; }
- //! Returns true, iff the Kernel ROM is alrady loaded
+ //! @brief Returns true, iff the Kernel ROM is alrady loaded
bool kernelRomIsLoaded() { return kernelRomFile != NULL; }
- //! Returns true, iff the Character ROM is alrady loaded
+ //! @brief Returns true, iff the Character ROM is alrady loaded
bool charRomIsLoaded() { return charRomFile != NULL; }
private:
- //! Lookup table for peek function
+ //! @brief Lookup table for peek()
MemorySource peekSrc[16];
- //! Lookup table for poke function
+ //! @brief Lookup table for poke()
MemorySource pokeTarget[16];
public:
- //! Updates peekSrc and pokeTarget
- /*! Values depend on three processor port bits and the cartridge exrom and game lines */
+ /*! @brief Updates the peek and poke lookup tables.
+ * @details The lookup values depend on three processor port bits and the cartridge exrom and game lines
+ */
void updatePeekPokeLookupTables();
- //! Returns true iff the provided address is a valid address of the specified type */
+ //! @brief Returns true iff the provided address is a valid address of the specified type
bool isValidAddr(uint16_t addr, MemoryType type);
- //! Read value from RAM
- uint8_t peekRam(uint16_t addr);
+ //! @brief Reads a byte from RAM.
+ inline uint8_t peekRam(uint16_t addr) { return ram[addr]; }
- //! Read value from ROM
- uint8_t peekRom(uint16_t addr);
+ //! @brief Reads a byte from ROM.
+ inline uint8_t peekRom(uint16_t addr) { return rom[addr]; }
- //! Read value from IO space
+ //! @brief Reads a byte from I/O space.
uint8_t peekIO(uint16_t addr);
- //! Read value from the currently visible source (RAM, ROM, I/O etc.)
+ /*! @brief Reads a byte from memory.
+ * @details The memory source (RAM, ROM, or I/O space) is read from the poke lookup table.
+ */
uint8_t peek(uint16_t addr);
- //! Write value to RAM
- void pokeRam(uint16_t addr, uint8_t value);
+ //! @brief Write a byte into RAM.
+ void pokeRam(uint16_t addr, uint8_t value) { ram[addr] = value; }
- //! Write value to ROM
- void pokeRom(uint16_t addr, uint8_t value);
+ //! @brief Write a byte into ROM.
+ void pokeRom(uint16_t addr, uint8_t value) { rom[addr] = value; }
- //! Write value to I/O space
+ //! @brief Write a byte into I/O space.
void pokeIO(uint16_t addr, uint8_t value);
- //! Write value to the currently visible source (RAM, I/O etc.)
+ /*! @brief Writes a byte into memory.
+ * @details The memory target (RAM, ROM, or I/O space) is read from the poke lookup table.
+ */
void poke(uint16_t addr, uint8_t value);
};
diff --git a/C64/CIA.cpp b/C64/CIA.cpp
index 2fa2def..7da8839 100755
--- a/C64/CIA.cpp
+++ b/C64/CIA.cpp
@@ -20,7 +20,7 @@
CIA::CIA()
{
- name = "CIA";
+ setDescription("CIA");
// Register sub components
VirtualComponent *subcomponents[] = { &tod, NULL };
@@ -65,10 +65,6 @@ CIA::reset()
{
VirtualComponent::reset();
- // Establish bindings
- cpu = c64->cpu;
- vic = c64->vic;
-
clearInterruptLine();
PA = 0xff;
@@ -80,17 +76,6 @@ CIA::reset()
latchB = 0xFFFF;
}
-#if 0
-void
-CIA::setFlagPin(uint8_t value)
-{
- if (value) // Note: FLAG pin is inverted
- ICR &= ~0x10;
- else
- ICR |= 0x10;
-}
-#endif
-
void
CIA::triggerRisingEdgeOnFlagPin()
{
@@ -116,63 +101,63 @@ CIA::peek(uint16_t addr)
uint8_t result;
switch(addr) {
- case CIA_DATA_DIRECTION_A:
+ case 0x02: // CIA_DATA_DIRECTION_A
result = DDRA;
break;
- case CIA_DATA_DIRECTION_B:
+ case 0x03: // CIA_DATA_DIRECTION_B
result = DDRB;
break;
- case CIA_TIMER_A_LOW:
+ case 0x04: // CIA_TIMER_A_LOW
result = getCounterALo();
break;
- case CIA_TIMER_A_HIGH:
+ case 0x05: // CIA_TIMER_A_HIGH
result = getCounterAHi();
break;
- case CIA_TIMER_B_LOW:
+ case 0x06: // CIA_TIMER_B_LOW
result = getCounterBLo();
break;
- case CIA_TIMER_B_HIGH:
+ case 0x07: // CIA_TIMER_B_HIGH
result = getCounterBHi();
break;
- case CIA_TIME_OF_DAY_SEC_FRAC:
+ case 0x08: // CIA_TIME_OF_DAY_SEC_FRAC
result = tod.getTodTenth();
tod.defreeze();
break;
- case CIA_TIME_OF_DAY_SECONDS:
+ case 0x09: // CIA_TIME_OF_DAY_SECONDS
result = tod.getTodSeconds();
break;
- case CIA_TIME_OF_DAY_MINUTES:
+ case 0x0A: // CIA_TIME_OF_DAY_MINUTES
result = tod.getTodMinutes();
break;
- case CIA_TIME_OF_DAY_HOURS:
+ case 0x0B: // CIA_TIME_OF_DAY_HOURS
tod.freeze();
result = tod.getTodHours();
break;
- case CIA_SERIAL_IO_BUFFER:
+ case 0x0C: // CIA_SERIAL_IO_BUFFER
result = 0x00;
break;
- case CIA_INTERRUPT_CONTROL:
+ case 0x0D: // CIA_INTERRUPT_CONTROL
result = ICR;
@@ -194,12 +179,12 @@ CIA::peek(uint16_t addr)
break;
- case CIA_CONTROL_REG_A:
+ case 0x0E: // CIA_CONTROL_REG_A
result = (uint8_t)(CRA & ~0x10); // Bit 4 is always 0 when read
break;
- case CIA_CONTROL_REG_B:
+ case 0x0F: // CIA_CONTROL_REG_B
result = (uint8_t)(CRB & ~0x10); // Bit 4 is always 0 when read
break;
@@ -217,7 +202,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
{
switch(addr) {
- case CIA_TIMER_A_LOW:
+ case 0x04: // CIA_TIMER_A_LOW
setLatchALo(value);
@@ -227,7 +212,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_TIMER_A_HIGH:
+ case 0x05: // CIA_TIMER_A_HIGH
setLatchAHi(value);
@@ -242,7 +227,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_TIMER_B_LOW:
+ case 0x06: // CIA_TIMER_B_LOW
setLatchBLo(value);
@@ -252,7 +237,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_TIMER_B_HIGH:
+ case 0x07: // CIA_TIMER_B_HIGH
setLatchBHi(value);
// load counter if timer is stopped
@@ -266,7 +251,8 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_TIME_OF_DAY_SEC_FRAC:
+ case 0x08: // CIA_TIME_OF_DAY_SEC_FRAC
+
if (CRB & 0x80) {
tod.setAlarmTenth(value);
} else {
@@ -275,21 +261,23 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_TIME_OF_DAY_SECONDS:
+ case 0x09: // CIA_TIME_OF_DAY_SECONDS
+
if (CRB & 0x80)
tod.setAlarmSeconds(value);
else
tod.setTodSeconds(value);
return;
- case CIA_TIME_OF_DAY_MINUTES:
+ case 0x0A: // CIA_TIME_OF_DAY_MINUTES
+
if (CRB & 0x80)
tod.setAlarmMinutes(value);
else
tod.setTodMinutes(value);
return;
- case CIA_TIME_OF_DAY_HOURS:
+ case 0x0B: // CIA_TIME_OF_DAY_HOURS
if (CRB & 0x80) {
tod.setAlarmHours(value);
@@ -303,13 +291,14 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_SERIAL_IO_BUFFER:
+ case 0x0C: // CIA_SERIAL_IO_BUFFER
+
// Serial I/O communication is not (yet) implemented
//triggerInterrupt(0x08);
// debug("poke CIA_SERIAL_IO_BUFFER: %0x2X\n", value);
return;
- case CIA_INTERRUPT_CONTROL:
+ case 0x0D: // CIA_INTERRUPT_CONTROL
//if ((value & 0x84) == 0x84)
// debug("SETTING TIME OF DAY ALARM (%02X)\n", value);
@@ -329,7 +318,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
}
return;
- case CIA_CONTROL_REG_A:
+ case 0x0E: // CIA_CONTROL_REG_A
{
//
// Adapted from PC64Win by Wolfgang Lorenz
@@ -386,7 +375,7 @@ void CIA::poke(uint16_t addr, uint8_t value)
return;
}
- case CIA_CONTROL_REG_B:
+ case 0x0F: // CIA_CONTROL_REG_B
{
//
// Adapted from PC64Win by Wolfgang Lorenz
@@ -505,8 +494,6 @@ void CIA::dumpTrace()
void CIA::dumpState()
{
- // assert(0);
-
msg(" Counter A : %02X\n", getCounterA());
msg(" Latch A : %02X\n", getLatchA());
msg(" Data port A : %02X\n", getDataPortA());
@@ -767,22 +754,18 @@ void CIA::executeOneCycle()
CIA1::CIA1()
{
- name = "CIA1";
- debug(2, " Creating CIA1 at address %p...\n", this);
+ setDescription("CIA1");
+ debug(3, " Creating CIA1 at address %p...\n", this);
}
CIA1::~CIA1()
{
- this->c64 = c64;
- debug(2, " Releasing CIA1\n");
+ debug(3, " Releasing CIA1\n");
}
void
CIA1::reset()
{
- keyboard = c64->keyboard;
- joy[0] = c64->joystick1;
- joy[1] = c64->joystick2;
joystick[0] = 0xff;
joystick[1] = 0xff;
CIA::reset();
@@ -799,27 +782,21 @@ CIA1::dumpState()
void
CIA1::raiseInterruptLine()
{
- cpu->setIRQLineCIA();
+ c64->cpu.setIRQLineCIA();
}
void
CIA1::clearInterruptLine()
{
- cpu->clearIRQLineCIA();
-}
-
-uint8_t
-CIA1::getInterruptLine()
-{
- return cpu->getIRQLineCIA();
+ c64->cpu.clearIRQLineCIA();
}
void
CIA1::pollJoystick(Joystick *joy, int joyDevNo)
{
- JoystickAxisState leftRightState = joy->GetAxisX();
- JoystickAxisState upDownState = joy->GetAxisY();
- bool buttonState = joy->GetButtonPressed();
+ JoystickDirection leftRightState = joy->getAxisX();
+ JoystickDirection upDownState = joy->getAxisY();
+ bool buttonState = joy->getButton();
assert (joy != NULL);
@@ -827,10 +804,10 @@ CIA1::pollJoystick(Joystick *joy, int joyDevNo)
// set the down bit: 2, 2 and clear up bit: 2, 1
// Remember: clearJoystickBits(x, y) means pressed
// setJoystickBits( x, y ) means released
- if(upDownState == JOYSTICK_AXIS_Y_UP) {
+ if(upDownState == JOYSTICK_UP) {
clearJoystickBits(joyDevNo, 1);
setJoystickBits(joyDevNo, 2);
- } else if(upDownState == JOYSTICK_AXIS_Y_DOWN) {
+ } else if(upDownState == JOYSTICK_DOWN) {
clearJoystickBits(joyDevNo, 2);
setJoystickBits(joyDevNo, 1);
} else {
@@ -839,10 +816,10 @@ CIA1::pollJoystick(Joystick *joy, int joyDevNo)
}
// left/right
- if(leftRightState == JOYSTICK_AXIS_X_LEFT) {
+ if(leftRightState == JOYSTICK_LEFT) {
clearJoystickBits(joyDevNo, 4);
setJoystickBits(joyDevNo, 8);
- } else if(leftRightState == JOYSTICK_AXIS_X_RIGHT) {
+ } else if(leftRightState == JOYSTICK_RIGHT) {
clearJoystickBits(joyDevNo, 8);
setJoystickBits(joyDevNo, 4);
} else {
@@ -866,9 +843,9 @@ CIA1::peek(uint16_t addr)
assert(addr <= CIA1_END_ADDR - CIA1_START_ADDR);
switch(addr) {
- case CIA_DATA_PORT_A:
+ case 0x00: // CIA_DATA_PORT_A
- pollJoystick(joy[1], 2);
+ pollJoystick(&c64->joystickB, 2);
// We change only those bits that are configured as outputs, all input bits are 1
result = PA; // iomem[addr] | ~iomem[CIA_DATA_DIRECTION_A];
@@ -880,12 +857,12 @@ CIA1::peek(uint16_t addr)
result &= joystick[1];
break;
- case CIA_DATA_PORT_B:
+ case 0x01: // CIA_DATA_PORT_B
{
- uint8_t bitmask = CIA1::peek(CIA_DATA_PORT_A);
- uint8_t keyboardBits = keyboard->getRowValues(bitmask);
+ uint8_t bitmask = CIA1::peek(0x00 /* CIA_DATA_PORT_A */);
+ uint8_t keyboardBits = c64->keyboard.getRowValues(bitmask);
- pollJoystick(joy[0], 1);
+ pollJoystick(&c64->joystickA, 1);
result = PB;
@@ -902,7 +879,6 @@ CIA1::peek(uint16_t addr)
break;
}
- // log("PEEKING %04X: %02X\n", 0xDC00 + addr, result);
return result;
}
@@ -913,24 +889,16 @@ CIA1::poke(uint16_t addr, uint8_t value)
assert(addr <= CIA1_END_ADDR - CIA1_START_ADDR);
- // log("Poking %02X to %04X\n", value, 0xDC00 + addr);
-
// The following registers need special handling
switch(addr) {
- case CIA_DATA_PORT_A:
+ case 0x00: // CIA_DATA_PORT_A
PALatch = value;
PA = PALatch | ~DDRA;
return;
- case CIA_DATA_DIRECTION_A:
-
- DDRA = value;
- PA = PALatch | ~DDRA;
- return;
-
- case CIA_DATA_PORT_B:
+ case 0x01: // CIA_DATA_PORT_B
PBold = PB;
@@ -938,11 +906,17 @@ CIA1::poke(uint16_t addr, uint8_t value)
PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode);
if ((PBold & 0x10) != (PB & 0x10)) { // edge on lightpen bit?
- vic->triggerLightPenInterrupt();
+ c64->vic.triggerLightPenInterrupt();
}
return;
- case CIA_DATA_DIRECTION_B:
+ case 0x02: // CIA_DATA_DIRECTION_A
+
+ DDRA = value;
+ PA = PALatch | ~DDRA;
+ return;
+
+ case 0x03: // CIA_DATA_DIRECTION_B
PBold = PB;
@@ -950,7 +924,7 @@ CIA1::poke(uint16_t addr, uint8_t value)
PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode);
if ((PBold & 0x10) != (PB & 0x10)) { // edge on lightpen bit?
- vic->triggerLightPenInterrupt();
+ c64->vic.triggerLightPenInterrupt();
}
return;
@@ -987,19 +961,17 @@ CIA1::clearJoystickBits(int nr, uint8_t mask)
CIA2::CIA2()
{
- name = "CIA2";
- debug(2, " Creating CIA2 at address %p...\n", this);
+ setDescription("CIA2");
+ debug(3, " Creating CIA2 at address %p...\n", this);
}
CIA2::~CIA2()
{
- debug(2, " Releasing CIA2...\n");
+ debug(3, " Releasing CIA2...\n");
}
void CIA2::reset()
{
- this->c64 = c64;
- iec = c64->iec;
CIA::reset();
}
@@ -1014,19 +986,13 @@ CIA2::dumpState()
void
CIA2::raiseInterruptLine()
{
- cpu->setNMILineCIA();
+ c64->cpu.setNMILineCIA();
}
void
CIA2::clearInterruptLine()
{
- cpu->clearNMILineCIA();
-}
-
-uint8_t
-CIA2::getInterruptLine()
-{
- return cpu->getNMILineCIA();
+ c64->cpu.clearNMILineCIA();
}
uint8_t
@@ -1037,14 +1003,14 @@ CIA2::peek(uint16_t addr)
assert(addr <= CIA_END_ADDR - CIA_START_ADDR);
switch(addr) {
- case CIA_DATA_PORT_A:
+ case 0x00: // CIA_DATA_PORT_A
result = PA;
// The two upper bits are connected to the clock line and the data line
result &= 0x3F;
- result |= (iec->getClockLine() ? 0x40 : 0x00);
- result |= (iec->getDataLine() ? 0x80 : 0x00);
+ result |= (c64->iec.getClockLine() ? 0x40 : 0x00);
+ result |= (c64->iec.getDataLine() ? 0x80 : 0x00);
// The external port lines can pull down any bit, even if it configured as output.
// Note that bits 0 and 1 are not connected to the bus and determine the memory bank seen by the VIC chip
@@ -1052,7 +1018,7 @@ CIA2::peek(uint16_t addr)
return result;
- case CIA_DATA_PORT_B:
+ case 0x01: // CIA_DATA_PORT_B
result = PB;
return result;
@@ -1068,38 +1034,38 @@ CIA2::poke(uint16_t addr, uint8_t value)
assert(addr <= CIA2_END_ADDR - CIA2_START_ADDR);
switch(addr) {
- case CIA_DATA_PORT_A:
+ case 0x00: // CIA_DATA_PORT_A
PALatch = value;
PA = PALatch | ~DDRA;
// Bits 0 and 1 determine the memory bank seen the VIC
- vic->setMemoryBankAddr((~PA & 0x03) << 14);
+ c64->vic.setMemoryBankAddr((~PA & 0x03) << 14);
// Bits 3 to 5 of PA are connected to the IEC bus
- iec->updateCiaPins(PALatch, DDRA);
+ c64->iec.updateCiaPins(PALatch, DDRA);
return;
- case CIA_DATA_DIRECTION_A:
-
- DDRA = value;
- PA = PALatch | ~DDRA;
-
- // Bits 0 and 1 determine the memory bank seen the VIC
- vic->setMemoryBankAddr((~PA & 0x03) << 14);
-
- // Bits 3 to 5 of PA are connected to the IEC bus
- iec->updateCiaPins(PALatch, DDRA);
- return;
-
- case CIA_DATA_PORT_B:
+ case 0x01: // CIA_DATA_PORT_B
PBLatch = value;
PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode);
// oldPB = PB;
return;
- case CIA_DATA_DIRECTION_B:
+ case 0x02: // CIA_DATA_DIRECTION_A
+
+ DDRA = value;
+ PA = PALatch | ~DDRA;
+
+ // Bits 0 and 1 determine the memory bank seen the VIC
+ c64->vic.setMemoryBankAddr((~PA & 0x03) << 14);
+
+ // Bits 3 to 5 of PA are connected to the IEC bus
+ c64->iec.updateCiaPins(PALatch, DDRA);
+ return;
+
+ case 0x03: // CIA_DATA_DIRECTION_B
DDRB = value;
PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode);
diff --git a/C64/CIA.h b/C64/CIA.h
index 00c0f20..bfb733c 100755
--- a/C64/CIA.h
+++ b/C64/CIA.h
@@ -1,22 +1,23 @@
-/*
- * (C) 2006 - 2009 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header CIA.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// Last review: 25.7.06
#ifndef _CIA_INC
#define _CIA_INC
@@ -24,7 +25,6 @@
#include "TOD.h"
// Forward declarations
-class CPU;
class VIC;
class IEC;
class Keyboard;
@@ -56,89 +56,39 @@ class Joystick;
#define DelayMask ~(0x00400000 | CountA0 | CountB0 | LoadA0 | LoadB0 | PB6Low0 | PB7Low0 | Interrupt0 | OneShotA0 | OneShotB0)
-//! Virtual complex interface adapter (CIA)
-/*! The original C64 consists of two CIA chips (CIA 1 and CIA 2). Each CIA chip features two programmable
- hardware timers and a real-time clock. Furthermore, the CIA chips manage the communication with connected
- peripheral devices such as joysticks, printer or the keyboard.
-
- The \a CIA class implements the common functionality of both CIA chips.
-*/
+/*! @brief Virtual complex interface adapter (CIA)
+ * @details The original C64 consists of two CIA chips (CIA 1 and CIA 2). Each CIA chip features two programmable
+ * hardware timers and a real-time clock. Furthermore, the CIA chips manage the communication with connected
+ * peripheral devices such as joysticks, printer or the keyboard. The CIA class implements the common
+ * functionality of both CIA chips.
+ */
class CIA : public VirtualComponent {
+ // -----------------------------------------------------------------------------------------------
+ // Properties
+ // -----------------------------------------------------------------------------------------------
+
public:
- //! Start address of the CIA I/O space (CIA 1 and CIA 2)
+
+ //! @brief Start address of the CIA I/O space (CIA 1 and CIA 2)
static const uint16_t CIA_START_ADDR = 0xDC00;
- //! End address of the CIA I/O space (CIA 1 and CIA 2)
+ //! @brief End address of the CIA I/O space (CIA 1 and CIA 2)
+
static const uint16_t CIA_END_ADDR = 0xDDFF;
-
- //! Address offset of the CIA data port register A
- static const uint16_t CIA_DATA_PORT_A = 0x00;
- //! Address offset of the CIA data port register B
- static const uint16_t CIA_DATA_PORT_B = 0x01;
- //! Address offset of the CIA data direction register A
- static const uint16_t CIA_DATA_DIRECTION_A = 0x02;
- //! Address offset of the CIA data direction register B
- static const uint16_t CIA_DATA_DIRECTION_B = 0x03;
-
- //! Address offset to the low-byte of timer A
- /*! The register contains the low-Byte of the current value of timer A. */
- static const uint16_t CIA_TIMER_A_LOW = 0x04;
- //! Address offset to the high-byte of timer A
- /*! The register contains the high-Byte of the current value of timer A. */
- static const uint16_t CIA_TIMER_A_HIGH = 0x05;
- //! Address offset to the low-byte of timer B
- /*! The register contains the low-Byte of the current value of timer B. */
- static const uint16_t CIA_TIMER_B_LOW = 0x06;
- //! Address offset to the high-byte of timer B
- /*! The register contains the HIGH-Byte of the current value of timer B. */
- static const uint16_t CIA_TIMER_B_HIGH = 0x07;
-
- //! Address offset of the time of day register
- /*! The register contains the "10th of a second" portion of the current time. */
- static const uint16_t CIA_TIME_OF_DAY_SEC_FRAC = 0x08;
- //! Address offset of the time of day register
- /*! The register contains the "seconds" portion of the current time. */
- static const uint16_t CIA_TIME_OF_DAY_SECONDS = 0x09;
- //! Address offset of the time of day register
- /*! The register contains the "minutes" portion of the current time. */
- static const uint16_t CIA_TIME_OF_DAY_MINUTES = 0x0A;
- //! Address offset of the time of day register
- /*! The register contains the "hours" portion of the current time. Bit 7 serves as an AM/PM flag. */
- static const uint16_t CIA_TIME_OF_DAY_HOURS = 0x0B;
-
- //! Address offset of the Synchronous Serial I/O Data Buffer
- static const uint16_t CIA_SERIAL_IO_BUFFER = 0x0C;
-
- //! Address offset of the CIA Interrupt Control Register
- static const uint16_t CIA_INTERRUPT_CONTROL = 0x0D;
- //! Address offset of the CIA Control Register A
- static const uint16_t CIA_CONTROL_REG_A = 0x0E;
- //! Address offset of the CIA Control Register B
- static const uint16_t CIA_CONTROL_REG_B = 0x0F;
-
- //! Reference to the connected video interface controller (VIC).
- /*! The CIA chip needs to know about the VIC chip, because
- 1. the video memory bank selection is handled by the CIA chip (register 0xDD00).
- 2. lightpen interrupts can be simulated by writing into a CIA register
- */
- VIC *vic;
-
- //! Reference to the connected CPU.
- CPU *cpu;
-
- //! Timer A counter
+
+ //! @brief Timer A counter
uint16_t counterA;
- //! Timer A latch
+ //! @brief Timer A latch
uint16_t latchA;
- //! Timer B counter
+ //! @brief Timer B counter
uint16_t counterB;
- //! Timer B latch
+ //! @brief Timer B latch
uint16_t latchB;
- //! Time of day clock
+ //! @brief Time of day clock
TOD tod;
public:
@@ -147,425 +97,489 @@ public:
// Adapted from PC64Win by Wolfgang Lorenz
//
- // control
- uint32_t delay; // performs delay by shifting left at each clock
- uint32_t feed; // new bits to feed into dwDelay
- uint8_t CRA; // control register A
- uint8_t CRB; // control register B
- uint8_t ICR; // interrupt control register
- uint8_t IMR; // interrupt mask register
- uint8_t PB67TimerMode; // bit mask for PB outputs: 0 = port register, 1 = timer
- uint8_t PB67TimerOut; // PB outputs bits 6 and 7 in timer mode
- uint8_t PB67Toggle; // PB outputs bits 6 and 7 in toggle mode
+ //
+ // Control
+ //
+
+ //! @brief Performs delay by shifting left at each clock
+ uint32_t delay;
+
+ //! @brief New bits to feed into dwDelay
+ uint32_t feed;
+
+ //! @brief Control register A
+ uint8_t CRA;
+
+ //! @brief Control register V
+ uint8_t CRB;
+
+ //! @brief Interrupt control register
+ uint8_t ICR;
+
+ //! @brief Interrupt mask register
+ uint8_t IMR;
+
+ //! @brief Bit mask for PB outputs: 0 = port register, 1 = timer
+ uint8_t PB67TimerMode;
+
+ //! @brief PB outputs bits 6 and 7 in timer mode
+ uint8_t PB67TimerOut;
+
+ //! @brief PB outputs bits 6 and 7 in toggle mode
+ uint8_t PB67Toggle;
- // ports
- uint8_t PALatch; // buffered output values
- uint8_t PBLatch;
- uint8_t DDRA; // directions: 0 = input, 1 = output
- uint8_t DDRB;
+ //
+ // Ports
+ //
+
+ //! @brief Bbuffered output value of port A
+ uint8_t PALatch;
+
+ //! @brief Bbuffered output value of port B
+ uint8_t PBLatch;
+
+ //! @brief Data directon register for port A (0 = input, 1 = output)
+ uint8_t DDRA;
+
+ //! @brief Data directon register for port B (0 = input, 1 = output)
+ uint8_t DDRB;
- // interfaces
+ //
+ // Interfaces
+ //
+
uint8_t PA;
uint8_t PB;
- bool CNT; // serial clock or input timer clock or timer gate
+
+ //! @brief Serial clock or input timer clock or timer gate
+ bool CNT;
bool INT;
-
- bool readICR; // Indicated if ICR register is currently read
- //! Activates the interrupt line
- /*! The function is abstract and will be implemented differently by the CIA 1 and CIA 2 class.
- Whereas the CIA 1 activates the IRQ line, the CIA 2 activates clears the NMI line.
- */
+ //! @brief Indicates if ICR register is currently read
+ bool readICR;
+
+ /*! @brief Activates the interrupt line
+ * @details The function is abstract and will be implemented differently by the CIA 1 and CIA 2 class.
+ * Whereas the CIA 1 activates the IRQ line, the CIA 2 activates clears the NMI line.
+ */
virtual void raiseInterruptLine() = 0;
- //! Clears the interrupt line
- /*! The function is abstract and will be implemented differently by the CIA 1 and CIA 2 class.
- Whereas the CIA 1 clears the IRQ line, the CIA 2 chip clears the NMI line.
- */
+ /*! @brief Clears the interrupt line
+ * @details The function is abstract and will be implemented differently by the CIA 1 and CIA 2 class.
+ * Whereas the CIA 1 clears the IRQ line, the CIA 2 chip clears the NMI line.
+ */
virtual void clearInterruptLine() = 0;
-
- //! Get current value of the interrupt line
- /*! The function is abstract and will be implemented differently by the CIA 1 and CIA 2 class.
- Whereas the CIA 1 polls the IRQ line, the CIA 2 chip polls the NMI line.
- */
- virtual uint8_t getInterruptLine() = 0;
+
+ // -----------------------------------------------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------------------------------------------
+
public:
- //! Returns true if the \a addr is located in the I/O range of one of the two CIA chips
- static inline bool isCiaAddr(uint16_t addr)
- { return (CIA_START_ADDR <= addr && addr <= CIA_END_ADDR); }
+ //! @brief Returns true if addr is located in the I/O range of one of the two CIA chips
+ static inline bool isCiaAddr(uint16_t addr) { return (CIA_START_ADDR <= addr && addr <= CIA_END_ADDR); }
- //! Constructor
+ //! @brief Constructor
CIA();
- //! Destructor
+ //! @brief Destructor
~CIA();
- //! Bring the CIA back to its initial state
+ //! @brief Bring the CIA back to its initial state
void reset();
- //! Dump internal state
+ //! @brief Dump internal state
void dumpState();
- //! Dump trace line
+ //! @brief Dump trace line
void dumpTrace();
- //! Returns the value of data port A
- inline uint8_t getDataPortA() { return peek(CIA_DATA_PORT_A); }
+
+ //
+ //! @functiongroup Accessing device properties
+ //
+
+ //! @brief Returns the value of data port A
+ inline uint8_t getDataPortA() { return peek(0x00); }
- //! Sets the current value of data port A
- inline void setDataPortA(uint8_t value) { poke(CIA_DATA_PORT_A, value); }
+ //! @brief Sets the current value of data port A
+ inline void setDataPortA(uint8_t value) { poke(0x00, value); }
- //! Returns the value of the data port A direction register
+ //! @brief Returns the value of the data port A direction register
inline uint8_t getDataPortDirectionA() { return DDRA; }
- //! Sets the current value of the data port A direction register
+ //! @brief Sets the current value of the data port A direction register
inline void setDataPortDirectionA(uint8_t value) { DDRA = value; }
- //! Returns the value of data port B
+ //! @brief Returns the value of data port B
inline uint8_t getDataPortB() { return PB; }
- //! Sets the current value of data port B
- inline void setDataPortB(uint8_t value) { poke(CIA_DATA_PORT_B,value); }
+ //! @brief Sets the current value of data port B
+ inline void setDataPortB(uint8_t value) { poke(0x01, value); }
- //! Returns the value of the data port B direction register
+ //! @brief Returns the value of the data port B direction register
inline uint8_t getDataPortDirectionB() { return DDRB; }
- //! Sets the current value of the data port B direction register
+ //! @brief Sets the current value of the data port B direction register
inline void setDataPortDirectionB(uint8_t value) { DDRB = value; }
- //! Sets the current value of the FLAG pin
- // void setFlagPin(uint8_t value);
+ //! @brief Simulates a rising edge on the flag pin
void triggerRisingEdgeOnFlagPin();
- void triggerFallingEdgeOnFlagPin();
-
- //! Special peek function for the I/O memory range
- /*! The peek function only handles those registers that are treated similarily by the CIA 1 and CIA 2 chip */
- virtual uint8_t peek(uint16_t addr);
-
- //! Special poke function for the I/O memory range
- /*! The poke function only handles those registers that are treated similarily by the CIA 1 and CIA 2 chip */
- virtual void poke(uint16_t addr, uint8_t value);
+ //! @brief Simulates a falling edge on the flag pin
+ void triggerFallingEdgeOnFlagPin();
+
+ //
// Interrupt control
-
- //! Returns true, if timer can trigger interrupts
+ //
+
+ //! @brief Returns true, if timer can trigger interrupts
inline bool isInterruptEnabledA() { return IMR & 0x01; }
- //! Set or delete interrupt enable flag
+ //! @brief Sets or deletes interrupt enable flag
inline void setInterruptEnabledA(bool b) { if (b) IMR |= 0x01; else IMR &= (0xff-0x01); }
- //! Toggle interrupt enable flag of timer A
+ //! @brief Toggles interrupt enable flag of timer A
inline void toggleInterruptEnableFlagA() { setInterruptEnabledA(!isInterruptEnabledA()); }
- //! Returns true, if timer A has reached zero
+ //! @brief Returns true, if timer A has reached zero
inline bool isSignalPendingA() { return ICR & 0x01; }
- //! Set or delete signal pending flag
+ //! @brief Sets or delete signal pending flag
inline void setSignalPendingA(bool b) { if (b) ICR |= 0x01; else ICR &= (0xff-0x01); }
- //! Toggle signal pending flag of timer A
+ //! @brief Toggles signal pending flag of timer A
inline void togglePendingSignalFlagA() { setSignalPendingA(!isSignalPendingA()); }
- //! Returns true, if timer B can trigger interrupts
+ //! @brief Returns true, if timer B can trigger interrupts
inline bool isInterruptEnabledB() { return IMR & 0x02; }
- //! Set or delete interrupt enable flag
+ //! @brief Sets or deletes interrupt enable flag
inline void setInterruptEnabledB(bool b) { if (b) IMR |= 0x02; else IMR &= (0xff-0x02); }
- //! Toggle interrupt enable flag of timer B
+ //! @brief Toggles interrupt enable flag of timer B
inline void toggleInterruptEnableFlagB() { setInterruptEnabledB(!isInterruptEnabledB()); }
- //! Returns true, if timer B has reached zero
+ //! @brief Returns true, if timer B has reached zero
inline bool isSignalPendingB() { return ICR & 0x02; }
- //! Set or delete signal pending flag
+ //! @brief Sets or delete signal pending flag
inline void setSignalPendingB(bool b) { if (b) ICR |= 0x02; else ICR &= (0xff-0x02); }
- //! Toggle signal pending flag of timer B
+ //! @brief Toggles signal pending flag of timer B
inline void togglePendingSignalFlagB() { setSignalPendingB(!isSignalPendingB()); }
- //! Returns true, if the "time of day" interrupt alarm is enabled
+ //! @brief Returns true, if the "time of day" interrupt alarm is enabled
inline bool isInterruptEnabledTOD() { return ICR & 0x04; }
- //! Enable or disable "time of day" interrupts
+ //! @brief Enables or disable "time of day" interrupts
inline void setInterruptEnabledTOD(bool b) { if (b) ICR |= 0x04; else ICR &= (0xff-0x04); }
- //! Returns true, if a negative edge on the FLAG pin triggers an interrupt
+ //! @brief Returns true, if a negative edge on the FLAG pin triggers an interrupt
inline bool isInterruptEnabledFlg() { return ICR & 0x10; }
- //! Enable or disable interrupts on negative edges of the FLAG pin
+ //! @brief Enables or disable interrupts on negative edges of the FLAG pin
inline void setInterruptEnabledFlg(bool b) { if (b) ICR |= 0x10; else ICR &= (0xff-0x10); }
//
// Timer A
//
- //! Return latch value
+ //! @brief Returns latch value.
inline uint16_t getLatchA() { return latchA; }
- //! Set latch value
+ //! @brief Sets latch value.
inline void setLatchA(uint16_t value) { latchA = value; }
- //! Get low byte of latch
+ //! @brief Returns low byte of latch.
inline uint8_t getLatchALo() { return (uint8_t)(latchA & 0xFF); }
- //! Set low byte of latch
+ //! @brief Sets low byte of latch.
inline void setLatchALo(uint8_t value) { latchA = (latchA & 0xFF00) | value; }
- //! Get high byte of latch
+ //! @brief Returns high byte of latch.
inline uint8_t getLatchAHi() { return (uint8_t)(latchA >> 8); }
- //! Set high byte of latch
+ //! @brief Sets high byte of latch.
inline void setLatchAHi(uint8_t value) { latchA = (value << 8) | (latchA & 0xFF); }
- //! Return current timer value
+ //! @brief Returns current timer value.
inline uint16_t getCounterA() { return counterA; }
- //! Set current timer value
+ //! @brief Sets current timer value.
inline void setCounterA(uint16_t value) { counterA = value; }
- //! Get low byte of current timer value
+ //! @brief Returns low byte of current timer value.
inline uint8_t getCounterALo() { return (uint8_t)(counterA & 0xFF); }
- //! Set low byte of current timer value
+ //! @brief Sets low byte of current timer value.
inline void setCounterALo(uint8_t value) { counterA = (counterA & 0xFF00) | value; }
- //! Get high byte of current timer value
+ //! @brief Returns high byte of current timer value.
inline uint8_t getCounterAHi() { return (uint8_t)(counterA >> 8); }
- //! Set high byte of current timer value
+ //! @brief Sets high byte of current timer value.
inline void setCounterAHi(uint8_t value) { counterA = (value << 8) | (counterA & 0xFF); }
- //! Load latched value into timer
- /*! As a side effect, CountA2 is cleared. This causes the timer to wait for one cycle before it continous to count */
+ /*! @brief Load latched value into timer.
+ * @details As a side effect, CountA2 is cleared. This causes the timer to wait for one cycle before
+ * it continous to count.
+ */
inline void reloadTimerA() { counterA = latchA; delay &= ~CountA2; }
- //! Returns true, if timer is running, 0 if stopped
+ //! @brief Returns true, if timer is running, 0 if stopped.
inline bool isStartedA() { return CRA & 0x01; }
- //! Start or stop timer
+ //! @brief Starts or stops timer.
inline void setStartedA(bool b) { if (b) CRA |= 0x01; else CRA &= 0xFE; }
- //! Toggle start flag
+ //! @brief Toggles start flag.
inline void toggleStartFlagA() { setStartedA(!isStartedA()); }
- //! Returns true, if the force load strobe is 1
+ //! @brief Returns true, if the force load strobe is 1.
inline bool forceLoadStrobeA() { return CRA & 0x10; }
- //! Returns true, if an underflow will be indicated in bit #6 in Port B register
+ //! @brief Returns true, if an underflow will be indicated in bit #6 in Port B register.
inline bool willIndicateUnderflowA() { return CRA & 0x02; }
- //! Returns true, if an underflow will be indicated as a single pulse
+ //! @brief Returns true, if an underflow will be indicated as a single pulse.
inline bool willIndicateUnderflowAsPulseA() { return !(CRA & 0x04); }
- //! Enable or disable underflow indication
+ //! @brief Enables or disables underflow indication.
inline void setIndicateUnderflowA(bool b) { if (b) CRA |= 0x02; else CRA &= (0xFF-0x02); }
- //! Toggle underflow indication flag
+ //! @brief Toggles underflow indication flag.
inline void toggleUnderflowFlagA() { setIndicateUnderflowA(!willIndicateUnderflowA()); }
- //! Returns true, if timer is in one shot mode
+ //! @brief Returns true, if timer is in one shot mode.
inline bool isOneShotA() { return CRA & 0x08; }
- //! Enable or disable one-shot-mode
+ //! @brief Enables or disables one-shot-mode.
inline void setOneShotA(bool b) { if (b) CRA |= 0x08; else CRA &= (0xff-0x08); }
- //! Toggle one shot flag
+ //! @brief Toggle one shot flag.
inline void toggleOneShotFlagA() { setOneShotA(!isOneShotA()); }
- //! Returns true, if timer counts clock ticks
+ //! @brief Returns true, if timer counts clock ticks.
inline bool isCountingClockTicksA() { return (CRA & 0x20) == 0x00; }
- //! Return value of timer control register
+ //! @brief Returns value of timer control register.
inline bool getControlRegA() { return CRA; }
- //! Set value of timer control register
+ //! @brief Sets value of timer control register.
inline void setControlRegA(uint8_t value) { CRA = value; }
//
// Timer B
//
- //! Return latch value
+ //! @brief Returns latch value.
inline uint16_t getLatchB() { return latchB; }
- //! Set latch value
+ //! @brief Sets latch value.
inline void setLatchB(uint16_t value) { latchB = value; }
- //! Get low byte of latch
+ //! @brief Returns low byte of latch.
inline uint8_t getLatchBLo() { return (uint8_t)(latchB & 0xFF); }
- //! Set low byte of latch
+ //! @brief Set low byte of latch.
inline void setLatchBLo(uint8_t value) { latchB = (latchB & 0xFF00) | value; }
- //! Get high byte of latch
+ //! @brief Returns high byte of latch.
inline uint8_t getLatchBHi() { return (uint8_t)(latchB >> 8); }
- //! Set high byte of latch
+ //! @brief Set high byte of latch.
inline void setLatchBHi(uint8_t value) { latchB = (value << 8) | (latchB & 0xFF); }
- //! Return current timer value
+ //! @brief Returns current timer value.
inline uint16_t getCounterB() { return counterB; }
- //! Set current timer value
+ //! @brief Set current timer value.
inline void setCounterB(uint16_t value) { counterB = value; }
- //! Get low byte of current timer value
+ //! @brief Returns low byte of current timer value.
inline uint8_t getCounterBLo() { return (uint8_t)(counterB & 0xFF); }
- //! Set low byte of current timer value
+ //! @brief Set low byte of current timer value.
inline void setCounterBLo(uint8_t value) { counterB = (counterB & 0xFF00) | value; }
- //! Get high byte of current timer value
+ //! @brief Returns high byte of current timer value.
inline uint8_t getCounterBHi() { return (uint8_t)(counterB >> 8); }
- //! Set high byte of current timer value
+ //! @brief Set high byte of current timer value.
inline void setCounterBHi(uint8_t value) { counterB = (value << 8) | (counterB & 0xFF); }
- //! Load latched value into timer
- /*! As a side effect, CountB2 is cleared. This causes the timer to wait for one cycle before it continous to count */
+ /*! @brief Loads latched value into timer.
+ * @details As a side effect, CountB2 is cleared. This causes the timer to wait for one cycle before
+ * it continous to count.
+ */
inline void reloadTimerB() { counterB = latchB; delay &= ~CountB2; }
- //! Returns true, if timer is running, 0 if stopped
+ //! @brief Returns true, if timer is running, 0 if stopped.
inline bool isStartedB() { return CRB & 0x01; }
- //! Start or stop timer
+ //! @brief Starts or stop timer.
inline void setStartedB(bool b) { if (b) CRB |= 0x01; else CRB &= 0xFE; }
- //! Toggle start flag
+ //! @brief Toggles start flag.
inline void toggleStartFlagB() { setStartedB(!isStartedB()); }
- //! Returns true, if the force load strobe is 1
+ //! @brief Returns true, if the force load strobe is 1.
inline bool forceLoadStrobeB() { return CRB & 0x10; }
- //! Returns true, if an underflow will be indicated in bit #7 in Port B register
+ //! @brief Returns true, if an underflow will be indicated in bit #7 in Port B register.
inline bool willIndicateUnderflowB() { return CRB & 0x02; }
- //! Returns true, if an underflow will be indicated as a single pulse
+ //! @brief Returns true, if an underflow will be indicated as a single pulse.
inline bool willIndicateUnderflowAsPulseB() { return !(CRB & 0x04); }
- //! Enable or disable underflow indication
+ //! @brief Enables or disables underflow indication.
inline void setIndicateUnderflowB(bool b) { if (b) CRB |= 0x02; else CRB &= (0xFF-0x02); }
- //! Toggle underflow indication flag
+ //! @brief Toggles underflow indication flag.
inline void toggleUnderflowFlagB() { setIndicateUnderflowB(!willIndicateUnderflowB()); }
- //! Returns true, if timer is in one shot mode
+ //! @brief Returns true, if timer is in one shot mode.
inline bool isOneShotB() { return CRB & 0x08; }
- //! Enable or disable one-shot-mode
+ //! @brief Enables or disable one-shot-mode.
inline void setOneShotB(bool b) { if (b) CRB |= 0x08; else CRB &= (0xff-0x08); }
- //! Toggle one shot flag
+ //! @brief Toggles one shot flag.
inline void toggleOneShotFlagB() { setOneShotB(!isOneShotB()); }
- //! Returns true, if timer counts clock ticks
+ //! @brief Returns true, if timer counts clock ticks.
inline bool isCountingClockTicksB() { return (CRB & 0x20) == 0x00; }
- //! Return value of timer control register
+ //! @brief Returns value of timer control register.
inline bool getControlRegB() { return CRB; }
- //! Set value of timer control register
+ //! @brief Sets value of timer control register.
inline void setControlRegB(uint8_t value) { CRB = value; }
- // -----------------------------------------------------------------------------------------------
- // General
- // -----------------------------------------------------------------------------------------------
-
- //! Pass control to the CIA chip
- /*! The CIA will be executed for one clock cycle
- The functions decreases all running counters and triggers an CPU interrput if necessary.
- */
+ //
+ //! @functiongroup Communicating via the I/O address space
+ //
+
+ /*! @brief Special peek function for the I/O memory range
+ * @details The peek function only handles those registers that are treated similarily by the CIA 1 and CIA 2 chip
+ */
+ virtual uint8_t peek(uint16_t addr);
+
+ /*! @brief Special poke function for the I/O memory range
+ * @details The poke function only handles those registers that are treated similarily by the CIA 1 and CIA 2 chip
+ */
+ virtual void poke(uint16_t addr, uint8_t value);
+
+
+ //
+ //! @functiongroup Running the device
+ //
+
+ //! @brief Executes the CIA for one cycle
void executeOneCycle();
- //! Increment the TOD clock by one tenth of a second
- /*! Issues an interrupt if the alarm time is reached.
- The function is supposed to be invoked whenever a frame is finished (during VBlank)
- */
+ //! Increments the TOD clock by one tenth of a second
void incrementTOD();
};
-//! The first virtual complex interface adapter (CIA 1)
-/*! The CIA 1 chips differs from the CIA 2 chip in several smaller aspects. For example, the CIA 1 interrupts the
- CPU via the IRQ line (maskable interrupts). Furthermore, the keyboard is connected to the the C64 via the CIA 1 chip.
-*/
+/*! @class The first virtual complex interface adapter (CIA 1)
+ * @details The CIA 1 chips differs from the CIA 2 chip in several smaller aspects. For example,
+ * the CIA 1 interrupts the CPU via the IRQ line (maskable interrupts). Furthermore,
+ * the keyboard is connected to the the C64 via the CIA 1 chip.
+ */
class CIA1 : public CIA {
public:
- //! Start address of the CIA 1 I/O space
+ //! @brief Start address of the CIA 1 I/O space
static const uint16_t CIA1_START_ADDR = 0xDC00;
- //! End address of the CIA 1 I/O space
+
+ //! @brief End address of the CIA 1 I/O space
static const uint16_t CIA1_END_ADDR = 0xDCFF;
- //! Joystick bits
+ //! @brief Joystick bits
uint8_t joystick[2];
private:
- //! Reference to the virtual keyboard
- Keyboard *keyboard;
- Joystick *joy[2];
+ //! @brief Polls current state of joystick
+ void pollJoystick(Joystick *joy, int joyDevNo);
- void pollJoystick( Joystick *joy, int joyDevNo );
+ /*! @brief Raises the interrupt line
+ * @details Note that CIA 1 is connected to the IRQ line
+ */ void raiseInterruptLine();
+
+ /*! @brief Clears the interrupt line
+ * @details Note that CIA 1 is connected to the IRQ line
+ */ void clearInterruptLine();
- void raiseInterruptLine();
- void clearInterruptLine();
- uint8_t getInterruptLine();
public:
- //! Constructor
+ //! @brief Constructor
CIA1();
- //! Destructor
+ //! @brief Destructor
~CIA1();
- //! Bring the CIA back to its initial state
+ //! @brief Restores the initial state
void reset();
- //! Returns true if the \a addr is located in the I/O range of the CIA 1 chip
+ //! @brief Returns true if addr is located in the I/O range of the CIA 1 chip
static inline bool isCia1Addr(uint16_t addr)
{ return (CIA1_START_ADDR <= addr && addr <= CIA1_END_ADDR); }
+ //! @brief Custom implementation of peek
uint8_t peek(uint16_t addr);
- void poke(uint16_t addr, uint8_t value);
+
+ //! @brief Custom implementation of poke
+ void poke(uint16_t addr, uint8_t value);
- //! Simulates a joystick movement
- /*! \param nr r number (1 or 2)
- \param value bit pattern of joystick movement */
+ /*! @brief Simulates a joystick movement
+ * @param nr joystick number (1 or 2)
+ * @param value bit pattern of joystick movement
+ */
void setJoystickBits(int nr, uint8_t mask);
- void clearJoystickBits(int nr, uint8_t mask);
+ //! @brief Clears all joystick bits
+ void clearJoystickBits(int nr, uint8_t mask);
+
+ //! @brief Prints debug information
void dumpState();
};
-//! The second virtual complex interface adapter (CIA 2)
-/*! The CIA 2 chips differs from the CIA 1 chip in several smaller aspects. For example, the CIA 2 interrupts the
- CPU via the NMI line (non maskable interrupts). Furthermore, the CIA 2 controlls the memory bank seen by the video controller.
- Therefore, CIA 2 needs to know about the VIC chip, in contrast to CIA 1.
-*/
+/*! @brief The second virtual complex interface adapter (CIA 2)
+ * @details The CIA 2 chips differs from the CIA 1 chip in several smaller aspects. For example,
+ * the CIA 2 interrupts the CPU via the NMI line (non maskable interrupts). Furthermore,
+ * the CIA 2 controlls the memory bank seen by the video controller. Therefore, CIA 2
+ * needs to know about the VIC chip, in contrast to CIA 1.
+ */
class CIA2 : public CIA {
public:
- //! Start address of the CIA 2 I/O space
+ //! @brief Start address of the CIA 2 I/O space
static const uint16_t CIA2_START_ADDR = 0xDD00;
- //! End address of the CIA 1 2/O space
+
+ //! @brief End address of the CIA 1 2/O space
static const uint16_t CIA2_END_ADDR = 0xDDFF;
private:
-
- //! Reference to the connected IEC bus
- IEC *iec;
-
- void raiseInterruptLine();
- void clearInterruptLine();
- uint8_t getInterruptLine();
+
+ /*! @brief Raises the interrupt line
+ * @details Note that CIA 2 is connected to the NMI line
+ */
+ void raiseInterruptLine();
+
+ /*! @brief Clears the interrupt line
+ * @details Note that CIA 2 is connected to the NMI line
+ */
+ void clearInterruptLine();
+
public:
diff --git a/C64/CPU.cpp b/C64/CPU.cpp
index f957389..58c99dd 100755
--- a/C64/CPU.cpp
+++ b/C64/CPU.cpp
@@ -20,8 +20,8 @@
CPU::CPU()
{
- name = "CPU";
- debug(2, " Creating CPU at address %p...\n", this);
+ setDescription("CPU");
+ debug(3, " Creating CPU at address %p...\n", this);
// Chip model
chipModel = MOS6510;
@@ -83,7 +83,7 @@ CPU::CPU()
CPU::~CPU()
{
- debug(2, " Releasing CPU...\n");
+ debug(3, " Releasing CPU...\n");
}
void
@@ -91,6 +91,7 @@ CPU::reset()
{
VirtualComponent::reset();
+ B = 1;
external_port_bits = 0x1F;
rdyLine = true;
next = &CPU::fetch;
diff --git a/C64/CPU.h b/C64/CPU.h
index 16149a6..3029b75 100755
--- a/C64/CPU.h
+++ b/C64/CPU.h
@@ -1,41 +1,40 @@
-/*
- * Author: Dirk W. Hoffmann, 2006 - 2015
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header CPU.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
#ifndef _CPU_INC
#define _CPU_INC
#include "Memory.h"
-class C64;
-class IEC;
-
-//! The virtual 6510 processor
+/*! @class The virtual 6510 processor
+ */
class CPU : public VirtualComponent {
public:
- //! Processor models
+ //! @brief Processor models
enum ChipModel {
MOS6510 = 0,
MOS6502 = 1
};
- //! Addressing modes
+ //! @brief Addressing modes
enum AddressingMode {
ADDR_IMPLIED,
ADDR_ACCUMULATOR,
@@ -53,11 +52,12 @@ public:
ADDR_INDIRECT
};
- //! Error states of the virtual CPU
- /*! CPU_OK indicates normal operation. When a (soft or hard) breakpoint is reached, the CPU enters
- the CPU_BREAKPOINT_REACHED state. CPU_ILLEGAL_INSTRUCTION is entered when an opcode is not understood
- by the CPU. Once the CPU enters a different state than CPU_OK, the execution thread is terminated.
- */
+ /*! @brief Error states of the virtual CPU
+ * @details CPU_OK indicates normal operation. When a (soft or hard) breakpoint is reached,
+ * the CPU enters the CPU_BREAKPOINT_REACHED state. CPU_ILLEGAL_INSTRUCTION is
+ * entered when an opcode is not understood by the CPU. Once the CPU enters a
+ * different state than CPU_OK, the execution thread is terminated.
+ */
enum ErrorState {
OK = 0,
SOFT_BREAKPOINT_REACHED,
@@ -65,43 +65,50 @@ public:
ILLEGAL_INSTRUCTION
};
- //! Breakpoint type
- /*! Each memory call is marked with a breakpoint tag. Originally, each cell is tagged with NO_BREAKPOINT
- which has no effect. CPU execution will stop if the memory cell is tagged with one of the following breakpoint types:
-
- HARD_BREAKPOINT: execution is halted
- SOFT_BREAKPOINT: execution is halted and the tag is deleted
- */
+ /*! @brief Breakpoint type
+ * @details Each memory call is marked with a breakpoint tag. Originally, each cell is
+ * tagged with NO_BREAKPOINT which has no effect. CPU execution will stop if the
+ * memory cell is tagged with one of the following breakpoint types:
+ *
+ * HARD_BREAKPOINT: execution is halted
+ * SOFT_BREAKPOINT: execution is halted and the tag is deleted
+ */
enum Breakpoint {
NO_BREAKPOINT = 0x00,
HARD_BREAKPOINT = 0x01,
SOFT_BREAKPOINT = 0x02
};
- //! Clock frequency of the original C64 (NTSC version) in Hz
+ //! @brief Clock frequency of the original C64 (NTSC version) in Hz
static const uint32_t CLOCK_FREQUENCY_NTSC = 1022727;
- //! Clock frequency of the original C64 (PAL version) in Hz
+ //! @brief Clock frequency of the original C64 (PAL version) in Hz
static const uint32_t CLOCK_FREQUENCY_PAL = 985249;
- //! Bit position of the Negative flag
+ //! @brief Bit position of the Negative flag
static const uint8_t N_FLAG = 0x80;
- //! Bit position of the Overflow flag
+
+ //! @brief Bit position of the Overflow flag
static const uint8_t V_FLAG = 0x40;
- //! Bit position of the Break flag
+
+ //! @brief Bit position of the Break flag
static const uint8_t B_FLAG = 0x10;
- //! Bit position of the Decimal flag
+
+ //! @brief Bit position of the Decimal flag
static const uint8_t D_FLAG = 0x08;
- //! Bit position of the Interrupt flag
+
+ //! @brief Bit position of the Interrupt flag
static const uint8_t I_FLAG = 0x04;
- //! Bit position of the Zero flag
+
+ //! @brief Bit position of the Zero flag
static const uint8_t Z_FLAG = 0x02;
- //! Bit position of the Carry flag
+
+ //! @brief Bit position of the Carry flag
static const uint8_t C_FLAG = 0x01;
public:
- //! Reference to the connected virtual memory
+ //! @brief Reference to the connected virtual memory
Memory *mem;
/*! @brief Selected chip model
@@ -112,414 +119,543 @@ public:
private:
- // Accumulator register
+ // @brief Accumulator
uint8_t A;
- // X register
+
+ // @brief X register
uint8_t X;
- // Y register
- uint8_t Y;
- //! Program counter
+
+ // @brief Y register
+ uint8_t Y;
+
+ //! @brief Program counter
uint16_t PC;
- //! Memory location of the currently executed command
+
+ //! @brief Memory location of the currently executed command
uint16_t PC_at_cycle_0;
- // Stack pointer
+
+ // @brief Stack pointer
uint8_t SP;
- //! Negative flag
- /*! The negative flag is set when the most significant bit (sign bit) equals 1. */
+
+ /*! @brief Negative flag
+ * @details The negative flag is set when the most significant bit (sign bit) equals 1.
+ */
uint8_t N;
- //! Overflow flag
- /*! The overflow flag is set iff an arithmetic operation causes a \a signed overflow. */
+
+ /*! @brief Overflow flag
+ * @details The overflow flag is set iff an arithmetic operation causes a \a signed overflow.
+ */
uint8_t V;
- //! Break flag
- /*! Is set to signal external interrupt. */
+
+ /*! @brief Break flag
+ * @details Is set to signal external interrupt.
+ */
uint8_t B;
- //! Decimal flag
- /*! If set, the CPU operates in BCD mode. (BCD mode is not supported yet). */
+
+ /*! @brief Decimal flag
+ * @details If set, the CPU operates in BCD mode. (BCD mode is not supported yet).
+ */
uint8_t D;
- //! Interrupt flag
- /*! If set, all interrupts are blocked. (No interrupt request will be answered). */
+
+ /*! @brief Interrupt flag
+ * @details If set, all interrupts are blocked. (No interrupt request will be answered).
+ */
uint8_t I;
- //! Zero flag
- /*! The zero flag is set iff the result of an arithmetic operation is zero. */
+
+ /*! @brief Zero flag
+ * @details The zero flag is set iff the result of an arithmetic operation is zero.
+ */
uint8_t Z;
- //! Carry flag
- /*! The carry flag is set iff an arithmetic operation causes an \a unsigned overflow. */
+
+ /*! @brief Carry flag
+ * @details The carry flag is set iff an arithmetic operation causes an \a unsigned overflow.
+ */
uint8_t C;
- // Opcode of the currently executed command
+ //! @brief Opcode of the currently executed command
uint8_t opcode;
- // Internal address register (low byte)
+
+ //! @brief Internal address register (low byte)
uint8_t addr_lo;
- // Internal address register (high byte)
+
+ //! @brief Internal address register (high byte)
uint8_t addr_hi;
- // Pointer for indirect addressing modes
+
+ //! @brief Pointer for indirect addressing modes
uint8_t ptr;
- // Temporary storage for program counter (low byte)
+
+ //! @brief Temporary storage for program counter (low byte)
uint8_t pc_lo;
- // Temporary storage for program counter (high byte)
+
+ //! @brief Temporary storage for program counter (high byte)
uint8_t pc_hi;
- // Address overflow indicater
- /* Used to indicate whether the page boundary has been crossed */
+
+ /*! @brief Address overflow indicater
+ * @details Used to indicate whether the page boundary has been crossed
+ */
bool overflow;
- // Internal data register
+
+ //! @brief Internal data register
uint8_t data;
- //! Processor port register
- uint8_t port;
- //! Processor port data direction register
+ //! @brief Processor port register
+ uint8_t port;
+
+ //! @brief Processor port data direction register
uint8_t port_direction;
- //! Experimental
+
+ //! @brief Experimental
uint8_t external_port_bits;
- //! RDY line (ready line)
- /*! If this line is LOW, the CPU freezes on the next read access.
- RDY is pulled down by VIC to perform longer lasting read operations.
- */
+ /*! @brief RDY line (ready line)
+ * @details If this line is LOW, the CPU freezes on the next read access.
+ * RDY is pulled down by VIC to perform longer lasting read operations.
+ */
bool rdyLine;
- //! IRQ line (maskable interrupts)
- /*! The CPU checks the IRQ line before the next instruction is executed.
- If the Interrupt flag is cleared and at least one bit is set, the CPU performs an interrupt.
- The IRQ line of the real CPU is driven by multiple sources (CIA, VIC). Each source is represented by a separate bit.
- \see CPU::I CPU::I_FLAG
- */
+ /*! @brief IRQ line (maskable interrupts)
+ * @details The CPU checks the IRQ line before the next instruction is executed.
+ * If the Interrupt flag is cleared and at least one bit is set, the CPU performs
+ * an interrupt. The IRQ line of the real CPU is driven by multiple sources
+ * (CIA, VIC). Each source is represented by a separate bit.
+ * @see CPU::I CPU::I_FLAG
+ */
uint8_t irqLine;
- //! NMI line (non maskable interrupts)
- /*! The CPU checks the IRQ line before the next instruction is executed.
- If at least one bit is set, the CPU performs an interrupt, regardless of the value of the I flag.
- The IRQ line of the real CPU is driven by multiple sources (CIA, VIC). Each source is represented by a separate bit.
- */
+ /*! @brief NMI line (non maskable interrupts)
+ * @details The CPU checks the IRQ line before the next instruction is executed.
+ * If at least one bit is set, the CPU performs an interrupt, regardless of the
+ * value of the I flag. The IRQ line of the real CPU is driven by multiple sources
+ * (CIA, VIC). Each source is represented by a separate bit.
+ */
uint8_t nmiLine;
- //! Indicates the occurance of an interrupt triggering edge on the NMI line
- /*! The variable is set to 1, when the value of variable nmiLine is changed from 0 to another value. The variable is
- used to determine when an NMI interrupt needs to be triggered. */
+ /*! @brief Indicates the occurance of an interrupt triggering edge on the NMI line
+ * @details The variable is set to 1, when the value of variable nmiLine is changed from 0 to
+ * another value. The variable is used to determine when an NMI interrupt needs
+ * to be triggered.
+ */
bool nmiEdge;
- //! Indicates if the CPU has to check for pending interrupts in its fetch phase
- /*! This variable has beed introduced for speedup. At all times, it is equivalent to "(irqLine || nmiEdge)" */
+ /*! @brief Indicates if the CPU has to check for pending interrupts in its fetch phase
+ * @details This variable has beed introduced for speedup. At all times, it is equivalent
+ * to "(irqLine || nmiEdge)".
+ */
bool interruptsPending;
- //! This variable is set when a negative edge occurs on the irq line and stores the next cycle in which an IRQ can occur.
- /*! The value is needed to determine the exact time to trigger the interrupt */
+ /*! @brief Indicates when the next IRQ can occurr.
+ * @details This variable is set when a negative edge occurs on the irq line and stores the
+ * next cycle in which an IRQ can occur. The value is needed to determine the exact
+ * time to trigger the interrupt.
+ */
uint64_t nextPossibleIrqCycle;
- //! This variable is set when a negative edge occurs on the nmi line and stores the next cycle in which an NMI can occur.
- /*! The value is needed to determine the exact time to trigger the interrupt */
+ /*! @brief Indicates when the next NMI can occurr.
+ * @details This variable is set when a negative edge occurs on the nmi line and stores the
+ * next cycle in which an NMI can occur. The value is needed to determine the exact
+ * time to trigger the interrupt.
+ */
uint64_t nextPossibleNmiCycle;
- //! Current error state
+ //! @brief Current error state
ErrorState errorState;
- //! Next function to be executed
- /*! Each function performs the actions of a single cycle */
+ /*! @brief Next function to be executed
+ * @details Each function performs the actions of a single cycle
+ */
void (CPU::*next)(void);
- //! Callback function array pointing to the execution function of each instruction.
+ //! @brief Callback function array pointing to the execution function of each instruction
void (CPU::*actionFunc[256])(void);
- //! Breakpoint tag for each memory cell
- /*! \see Breakpoint */
+ //! @brief Breakpoint tag for each memory cell
uint8_t breakpoint[65536];
- //! Records all subroutine calls
- /*! Whenever a JSR instruction is executed, the address of the instruction is recorded in the callstack.
- */
+ /*! @brief Records all subroutine calls
+ * @details Whenever a JSR instruction is executed, the address of the instruction is recorded
+ * in the callstack.
+ */
uint16_t callStack[256];
- //! Location of the next free cell of the callstack
+ //! @brief Location of the next free cell of the callstack.
uint8_t callStackPointer;
- //! Value of the I flag before it got changed with the SEI command
+ //! @brief Value of the I flag before it got changed with the SEI command.
uint8_t oldI;
- //! Returns true iff IRQs are blocked
- /*! IRQs are blocked by setting the I flag to 1. The I flag is set with the SEI command and cleared with the CLI command.
- Note that the timing is important here! When an interrupt occures while SEI or CLI is executed, the previous value of I
- determines whether an interrupt is triggered or not. To handle timing correctly, the previous value of I is stored in
- variable oldI whenever SEI or CLI is executed. */
- bool IRQsAreBlocked();
-
#include "Instructions.h"
public:
- // Constructor
+ //! @brief Constructor
CPU();
- // Destructor
+ //! @brief Destructor
~CPU();
- // Brings CPU back to its initial state
+ //! @brief Restores the initial state.
void reset();
- //! Size of internal state
+ //! @brief Returns the size of the internal state.
uint32_t stateSize();
- //! Load state
+ //! @brief Reads the internal state from a buffer.
void loadFromBuffer(uint8_t **buffer);
- //! Save state
+ //! @brief Writes the internal state into a buffer.
void saveToBuffer(uint8_t **buffer);
- //! Dump internal state to console
+ //! @brief Prints debugging information.
void dumpState();
- // Returns true iff this object is the C64 CPU (for debugging, only)
- bool isC64CPU() { return strcmp(name, "CPU") == 0; /* VC1541 CPU is calles "1541CPU" */ }
-
- //! Get value of processor port
+ //! @brief Returns true iff this object is the C64 CPU (for debugging, only).
+ bool isC64CPU() { return strcmp(getDescription(), "CPU") == 0; /* VC1541 CPU is calles "1541CPU" */ }
+
+
+ //
+ //! @functiongroup Managing the processor port
+ //
+
+ //! @brief Returns the value of processor port.
inline uint8_t getPort() { return port; }
- //! Set value of processor port register
+
+ //! @brief Sets the value of the processor port register.
void setPort(uint8_t value);
- //! Get value of processor port
+
+ //! @brief Returns the value of processor port register.
inline uint8_t getPortDirection() { return port_direction; }
- //! Experimental
+
+ //! @brief Experimental.
inline uint8_t getExternalPortBits() { return external_port_bits; }
- //! Set value of processor port data direction register
+
+ //! @brief Sets the value of the processor port data direction register.
void setPortDirection(uint8_t value);
- //! Get physical values of port lines
+
+ //! @brief Returns the physical value of the port lines.
uint8_t getPortLines() { return (port | ~port_direction); }
- //! Returns current value of the accumulator register
+
+ //
+ //! @functiongroup Handling registers and flags
+ //
+
+ //! @brief Returns the contents of the accumulator.
inline uint8_t getA() { return A; };
- //! Returns current value of the X register
+
+ //! @brief Returns current value of the X register.
inline uint8_t getX() { return X; };
- //! Returns current value of the Y register
+
+ //! @brief Returns current value of the Y register.
inline uint8_t getY() { return Y; };
- //! Returns current value of the program counter
+
+ //! @brief Returns current value of the program counter.
inline uint16_t getPC() { return PC; };
- //! Returns "freezed" program counter
+
+ //! @brief Returns "freezed" program counter.
inline uint16_t getPC_at_cycle_0() { return PC_at_cycle_0; };
- //! Returns current value of the program counter
+
+ //! @brief Returns current value of the program counter.
inline uint8_t getSP() { return SP; };
- //! Returns current value of the memory cell addressed by the program counter
+ //! @brief Returns current value of the memory cell addressed by the program counter.
inline uint8_t peekPC() { return mem->peek(PC); }
- //! Returns 1, if Negative flag is set, 0 otherwise
+ //! @brief Returns 1, if Negative flag is set, 0 otherwise.
inline uint8_t getN() { return (N ? N_FLAG : 0); }
- //! Returns 1, if Overflow flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Overflow flag is set, 0 otherwise.
inline uint8_t getV() { return (V ? V_FLAG : 0); }
- //! Returns 1, if Break flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Break flag is set, 0 otherwise.
inline uint8_t getB() { return (B ? B_FLAG : 0); }
- //! Returns 1, if Decimal flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Decimal flag is set, 0 otherwise.
inline uint8_t getD() { return (D ? D_FLAG : 0); }
- //! Returns 1, if Interrupt flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Interrupt flag is set, 0 otherwise.
inline uint8_t getI() { return (I ? I_FLAG : 0); }
- //! Returns 1, if Zero flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Zero flag is set, 0 otherwise.
inline uint8_t getZ() { return (Z ? Z_FLAG : 0); }
- //! Returns 1, if Carry flag is set, 0 otherwise
+
+ //! @brief Returns 1, if Carry flag is set, 0 otherwise.
inline uint8_t getC() { return (C ? C_FLAG : 0); }
- //! Returns the status register
- /*! Each bit in the status register corresponds to the value of a single flag, except bit 5 which is always set. */
+
+ /*! @brief Returns the contents of the status register
+ * @details Each bit in the status register corresponds to the value of a single flag,
+ * except bit 5 which is always set.
+ */
inline uint8_t getP() { return getN() | getV() | 32 | getB() | getD() | getI() | getZ() | getC(); }
- //! Returns the status register without the B flag
- /*! The bit position of the B flag is always 0. This function is needed for proper interrupt handling. When an IRQ
- or NMI is triggered internally, the status register is pushed on the stack with the B-flag cleared. */
+
+ /*! @brief Returns the status register without the B flag
+ * @details The bit position of the B flag is always 0. This function is needed for proper
+ * interrupt handling. When an IRQ or NMI is triggered internally, the status
+ * register is pushed on the stack with the B-flag cleared.
+ */
inline uint8_t getPWithClearedB() { return getN() | getV() | 32 | getD() | getI() | getZ() | getC(); }
- //! Return current opcode
+ //! @brief Returns current opcode.
inline uint8_t getOpcode() { return opcode; }
- //! Write value to the accumulator register. Flags remain untouched.
+ //! @brief Writes value to the accumulator register. Flags remain untouched.
inline void setA(uint8_t a) { A = a; }
- //! Write value to the the X register. Flags remain untouched.
+
+ //! @brief Writes value to the the X register. Flags remain untouched.
inline void setX(uint8_t x) { X = x; }
- //! Write value to the the Y register. Flags remain untouched.
+
+ //! @brief Writes value to the the Y register. Flags remain untouched.
inline void setY(uint8_t y) { Y = y; }
- //! Write value to the the program counter.
+
+ //! @brief Writes value to the the program counter.
inline void setPC(uint16_t pc) { PC = pc; }
- //! Write value to the freezend program counter.
+
+ //! @brief Writes value to the freezend program counter.
inline void setPC_at_cycle_0(uint16_t pc) { PC_at_cycle_0 = PC = pc; next = &CPU::fetch;}
- //! Change low byte of the program counter only
+
+ //! @brief Changes low byte of the program counter only.
inline void setPCL(uint8_t lo) { PC = (PC & 0xff00) | lo; }
- //! Change high byte of the program counter only
+
+ //! @brief Changes high byte of the program counter only.
inline void setPCH(uint8_t hi) { PC = (PC & 0x00ff) | ((uint16_t)hi << 8); }
- //! Increment the program counter by the specified amount.
- /*! If no argument is provided, the program counter is incremented by one. */
+
+ /*! @brief Increments the program counter by the specified amount.
+ * @details If no argument is provided, the program counter is incremented by one.
+ */
inline void incPC(uint8_t offset = 1) { PC += offset; }
- //! Increment low byte of program counter (hi byte remains unchanged)
+
+ //! @brief Increments low byte of program counter (hi byte remains unchanged).
inline void incPCL(uint8_t offset = 1) { setPCL(LO_BYTE(PC) + offset); }
- //! Increment high byte of program counter (lo byte remains unchanged)
+
+ //! @brief Increments high byte of program counter (lo byte remains unchanged).
inline void incPCH(uint8_t offset = 1) { setPCH(HI_BYTE(PC) + offset); }
- //! Write value to the stack pointer
+ //! @brief Writes value to the stack pointer.
inline void setSP(uint8_t sp) { SP = sp; }
- //! 0: Negative-flag is cleared, any other value: flag is set
+ //! @brief 0: Negative-flag is cleared, any other value: flag is set.
inline void setN(uint8_t n) { N = n; }
- //! 0: Overflow-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Overflow-flag is cleared, any other value: flag is set.
inline void setV(uint8_t v) { V = v; }
- //! 0: Break-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Break-flag is cleared, any other value: flag is set.
inline void setB(uint8_t b) { B = b; }
- //! 0: Decimal-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Decimal-flag is cleared, any other value: flag is set.
inline void setD(uint8_t d) { D = d; }
- //! 0: Interrupt-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Interrupt-flag is cleared, any other value: flag is set.
inline void setI(uint8_t i) { I = i; }
- //! 0: Zero-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Zero-flag is cleared, any other value: flag is set.
inline void setZ(uint8_t z) { Z = z; }
- //! 0: Carry-flag is cleared, any other value: flag is set
+
+ //! @brief 0: Carry-flag is cleared, any other value: flag is set.
inline void setC(uint8_t c) { C = c; }
- //! Write value to the status register. The value of bit 5 is ignored. */
+
+ //! @brief Write value to the status register. The value of bit 5 is ignored.
inline void setP(uint8_t p)
{ setN(p & N_FLAG); setV(p & V_FLAG); setB(p & B_FLAG); setD(p & D_FLAG); setI(p & I_FLAG); setZ(p & Z_FLAG); setC(p & C_FLAG); }
inline void setPWithoutB(uint8_t p)
{ setN(p & N_FLAG); setV(p & V_FLAG); setD(p & D_FLAG); setI(p & I_FLAG); setZ(p & Z_FLAG); setC(p & C_FLAG); }
- //! Load value into the accumulator. The Z- and N-flag may change. */
+ //! @brief Loads the accumulator. The Z- and N-flag may change.
inline void loadA(uint8_t a) { A = a; N = a & 128; Z = (a == 0); }
- //! Load value into the X register. The Z- and N-flag may change. */
+
+ //! @brief Loads the X register. The Z- and N-flag may change.
inline void loadX(uint8_t x) { X = x; N = x & 128; Z = (x == 0); }
- //! Load value into the Y register. The Z- and N-flag may change. */
+
+ //! @brief Loads the Y register. The Z- and N-flag may change.
inline void loadY(uint8_t y) { Y = y; N = y & 128; Z = (y == 0); }
- //! Load value into the stack register. The Z- and N-flag may change. */
+
+ //! @brief Loads the stack register. The Z- and N-flag may change.
inline void loadSP(uint8_t s) { SP = s; N = s & 128; Z = (s == 0); }
- //! Load value into memory. The Z- and N-flag may change. */
+
+ //! @brief Loads a value into memory. The Z- and N-flag may change.
inline void loadM(uint16_t addr, uint8_t s) { mem->poke(addr, s); N = s & 128; Z = (s == 0); }
- //! Set bit of IRQ line
+
+ //
+ //! @functiongroup Handling interrupts
+ //
+
+ /*! @brief Returns true iff IRQs are blocked
+ * @details IRQs are blocked by setting the I flag to 1. The I flag is set with the SEI command
+ * and cleared with the CLI command. Note that the timing is important here! When an
+ * interrupt occures while SEI or CLI is executed, the previous value of I determines
+ * whether an interrupt is triggered or not. To handle timing correctly, the previous
+ * value of I is stored in variable oldI whenever SEI or CLI is executed.
+ */
+ bool IRQsAreBlocked();
+
+ //! @brief Sets a bit of the IRQ line.
void setIRQLine(uint8_t bit);
- //! Clear bit of IRQ line
- inline void clearIRQLine(uint8_t bit) { irqLine &= (~bit); interruptsPending = irqLine || nmiEdge; }
+ //! @brief Clears a bit of the IRQ line.
+ inline void clearIRQLine(uint8_t bit) { irqLine &= ~bit; interruptsPending = irqLine || nmiEdge; }
- //! Get bit of IRQ line
+ //! @brief Returns bit of IRQ line.
inline uint8_t getIRQLine(uint8_t bit) { return irqLine & bit; }
- //! Check if IRQ line has been activated for at least 2 cycles
+ //! @brief Checks if IRQ line has been activated for at least 2 cycles.
bool IRQLineRaisedLongEnough();
- //! Set bit of NMI line
+ //! @brief Sets bit of NMI line.
void setNMILine(uint8_t bit);
- //! Indicate a negative edge on the NMI line
+ //! @brief Indicates a negative edge on the NMI line.
void setNMIEdge();
- //! Remove negative edge indicator for the NMI line
+ //! @brief Removes negative edge indicator for the NMI line.
void clearNMIEdge();
- //! Clear bit of NMI line
- inline void clearNMILine(uint8_t bit) { nmiLine &= (0xff - bit); }
+ //! @brief Clears bit of NMI line.
+ inline void clearNMILine(uint8_t bit) { nmiLine &= ~bit; }
- //! Get bit of IRQ line
- inline uint8_t getNMILine(uint8_t bit) { return nmiLine & bit; }
-
- //! Check if NMI line has been activated for at least 2 cycles
+ //! @brief Checks if NMI line has been activated for at least 2 cycles.
bool NMILineRaisedLongEnough();
- //! Get CIA bit of IRQ line
- inline uint8_t getIRQLineCIA() { return getIRQLine(0x01); }
- //! Set CIA bit of IRQ line
+ //! @brief Sets CIA bit of IRQ line.
inline void setIRQLineCIA() { setIRQLine(0x01); }
- //! Set VIC bit of IRQ line
+
+ //! @brief Sets VIC bit of IRQ line.
inline void setIRQLineVIC() { setIRQLine(0x02); }
- //! Set VIA bit of IRQ line (1541 drive)
+
+ //! @brief Sets VIA bit of IRQ line (1541 drive).
inline void setIRQLineVIA() { setIRQLine(0x10); }
- //! Set VIA 1 bit of IRQ line (1541 drive)
- // inline void setIRQLineVIA1() { setIRQLine(0x10); }
- //! Set VIA 2 bit of IRQ line (1541 drive)
- // inline void setIRQLineVIA2() { setIRQLine(0x20); }
- //! Set ATN bit of IRQ line (1541 drive)
+
+ //! @brief Sets ATN bit of IRQ line (1541 drive).
inline void setIRQLineATN() { setIRQLine(0x40); }
- //! Clear CIA bit of IRQ line
+
+ //! @brief Clears CIA bit of IRQ line.
inline void clearIRQLineCIA() { clearIRQLine(0x01); }
- //! Clear VIC bit of IRQ line
- inline void clearIRQLineVIC() { clearIRQLine(0x02); }
- //! Clear VIA 1 bit of IRQ line (1541 drive)
+
+ //! @brief Clears VIC bit of IRQ line.
+ inline void clearIRQLineVIC() { clearIRQLine(0x02); }
+
+ //! @brief Clears VIA 1 bit of IRQ line (1541 drive).
inline void clearIRQLineVIA() { clearIRQLine(0x10); }
- //! Clear ATN bit of IRQ line (1541 drive)
- inline void clearIRQLineATN() { clearIRQLine(0x40); } // DEPRECATED
-
- //! Get CIA bit of NMI line
- inline uint8_t getNMILineCIA() { return getNMILine(0x01); }
- //! Set CIA bit of NMI line
- inline void setNMILineCIA() { setNMILine(0x01); }
- //! Clear CIA bit of NMI line
+
+ //! @brief Clears ATN bit of IRQ line (1541 drive).
+ inline void clearIRQLineATN() { clearIRQLine(0x40); }
+
+ //! @brief Sets CIA bit of NMI line.
+ inline void setNMILineCIA() { setNMILine(0x01); }
+
+ //! @brief Clears CIA bit of NMI line.
inline void clearNMILineCIA() { clearNMILine(0x01); }
- //! Set Reset bit of NMI line
- inline void setNMILineReset() { setNMILine(0x08); }
- //! Clear Reset bit of NMI line
+
+ //! @brief Sets reset bit of NMI line.
+ inline void setNMILineReset() { setNMILine(0x08); }
+
+ //! @brief Clears reset bit of NMI line.
inline void clearNMILineReset() { clearNMILine(0x08); }
- //! Get RDY line
- inline bool getRDY() { return rdyLine; }
- //! Set RDY line
+
+ //! @brief Sets the RDY line.
inline void setRDY(bool value) { rdyLine = value; }
- //! Returns the three letter mnemonic for a given opcode
+
+ //
+ //! @functiongroup Examining the currently executed instruction
+ //
+
+ //! @brief Returns the three letter mnemonic for a given opcode.
const char *getMnemonic(uint8_t opcode);
- //! Returns the three letter mnemonic of the next instruction to execute
+
+ //! @brief Returns the three letter mnemonic of the next instruction to execute.
const char *getMnemonic() { return getMnemonic(mem->peek(PC)); }
- //! Returns the adressing mode for a given opcode
+
+ //! @brief Returns the adressing mode for a given opcode.
AddressingMode getAddressingMode(uint8_t opcode);
- //! Returns the adressing mode of the next instruction to execute
- AddressingMode getAddressingMode() { return getAddressingMode(mem->peek(PC)); }
- //! Returns the length in bytes of the instruction with the specified opcode
- /*! Possible values: 1 to 3 */
+
+ //! @brief Returns the adressing mode of the next instruction to execute.
+ AddressingMode getAddressingMode() { return getAddressingMode(mem->peek(PC)); }
+
+ /*! @brief Returns the length in bytes of the instruction with the specified opcode.
+ * @result Integer value between 1 and 3.
+ */
int getLengthOfInstruction(uint8_t opcode);
- //! Returns the length in bytes of the instruction with the specified address
- /*! Possible values: 1 to 3 */
+
+ /*! @brief Returns the length in bytes of the instruction with the specified address.
+ * @result Integer value between 1 and 3.
+ */
inline int getLengthOfInstructionAtAddress(uint16_t addr) { return getLengthOfInstruction(mem->peek(addr)); }
- //! Returns the length in bytes of the next instruction to execute
- /*! Possible values: 1 to 3 */
+
+ /*! @brief Returns the length in bytes of the next instruction to execute.
+ * @result Integer value between 1 and 3.
+ */
inline int getLengthOfCurrentInstruction() { return getLengthOfInstructionAtAddress(PC_at_cycle_0); }
- //! Returns the address of the instruction following the current instruction
- /*! Possible values: 1 to 3 */
- inline uint16_t getAddressOfNextInstruction() { return PC_at_cycle_0 + getLengthOfCurrentInstruction(); }
- //! Disassemble current instruction
+
+ /*! @brief Returns the address of the instruction following the current instruction.
+ * @result Integer value between 1 and 3.
+ */
+ inline uint16_t getAddressOfNextInstruction() { return PC_at_cycle_0 + getLengthOfCurrentInstruction(); }
+
+ //! @brief Disassembles the current instruction.
char *disassemble();
- // char *disassemble(uint64_t state);
- //! Returns true, iff the next cycle is the first cycle of a command
+ //! @brief Returns true, iff the next cycle is the first cycle of a command.
inline bool atBeginningOfNewCommand() { return next == &CPU::fetch; }
- //! Execute CPU for one cycle
- /*! This is the normal operation mode. Interrupt requests are handled. */
+
+ //
+ //! @functiongroup Executing the device
+ //
+
+ /*! @brief Executes the device for one cycle.
+ * @details This is the normal operation mode. Interrupt requests are handled.
+ */
inline bool executeOneCycle() { (*this.*next)(); return errorState == CPU::OK; }
- //! Returns the current error state
+ //! @brief Returns the current error state.
inline ErrorState getErrorState() { return errorState; }
- //! Sets the current error state
+ //! @brief Sets the error state.
void setErrorState(ErrorState state);
- //! Sets the error state back to normal
+ //! @brief Sets the error state back to normal.
void clearErrorState() { setErrorState(OK); }
- //! Return breakpoint tag for the specified address
+
+ //
+ //! @functiongroup Handling breakpoints
+ //
+
+ //! @brief Returns breakpoint tag for the specified address.
inline uint8_t getBreakpointTag(uint16_t addr) { return breakpoint[addr]; }
- //! Returns the breakpoint tag for the specified address
+ //! @brief Returns the breakpoint tag for the specified address.
uint8_t getBreakpoint(uint16_t addr) { return breakpoint[addr]; }
- //! Set a breakpoint tag at the specified address
+ //! @brief Sets a breakpoint tag at the specified address.
void setBreakpoint(uint16_t addr, uint8_t tag) { breakpoint[addr] = tag; }
- //! Sets a hard breakpoint at the specified address
- void setHardBreakpoint(uint16_t addr) {
- debug(1, "Setting hard breakpoint at address %d (%4X)\n", addr, addr);
- breakpoint[addr] |= HARD_BREAKPOINT;
- }
+ //! @brief Sets a hard breakpoint at the specified address.
+ void setHardBreakpoint(uint16_t addr) { breakpoint[addr] |= HARD_BREAKPOINT; }
- //! Deletes a hard breakpoint at the specified address
- void deleteHardBreakpoint(uint16_t addr) {
- debug(1, "Deleting hard breakpoint at address %d (%4X)\n", addr, addr);
- breakpoint[addr] &= (255-HARD_BREAKPOINT);
- }
+ //! @brief Deletes a hard breakpoint at the specified address.
+ void deleteHardBreakpoint(uint16_t addr) { breakpoint[addr] &= (255-HARD_BREAKPOINT); }
- //! Sets or deletes a hard breakpoint at the specified address
+ //! @brief Sets or deletes a hard breakpoint at the specified address.
void toggleHardBreakpoint(uint16_t addr) { breakpoint[addr] ^= HARD_BREAKPOINT; }
- //! Sets a soft breakpoint at the specified address
+
+ //! @brief Sets a soft breakpoint at the specified address.
void setSoftBreakpoint(uint16_t addr) { breakpoint[addr] |= SOFT_BREAKPOINT; }
- //! Deletes a soft breakpoint at the specified address
+
+ //! @brief Deletes a soft breakpoint at the specified address.
void deleteSoftBreakpoint(uint16_t addr) { breakpoint[addr] &= (255-SOFT_BREAKPOINT); }
- //! Sets or deletes a hard breakpoint at the specified address
+
+ //! @brief Sets or deletes a hard breakpoint at the specified address.
void toggleSoftBreakpoint(uint16_t addr) { breakpoint[addr] ^= SOFT_BREAKPOINT; }
- //! Read entry from callstack
+
+ //
+ //! @functiongroup Querying the callstack
+ //
+
+ //! @brief Reads entry from callstack.
int getTopOfCallStack() { return (callStackPointer > 0) ? callStack[callStackPointer-1] : -1; }
-
- // void dumpHistory();
+
};
+
#endif
diff --git a/C64/Cartridge.cpp b/C64/Cartridge.cpp
old mode 100755
new mode 100644
index 5416666..3b5f59d
--- a/C64/Cartridge.cpp
+++ b/C64/Cartridge.cpp
@@ -1,5 +1,7 @@
/*
- * (C) 2009 A. Carl Douglas. All rights reserved.
+ * Original implementation by A. Carl Douglas, 2009
+ * Modified and maintained by Dirk W. Hoffmann
+ * All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -86,7 +88,7 @@ Cartridge::readFromBuffer(const uint8_t *buffer, unsigned length)
// Scan cartridge header
if (memcmp("C64 CARTRIDGE ", data, 16) != 0) {
- fprintf(stderr, "Bad cartridge signature. Expected 'C64 CARTRIDGE ', got ...\n");
+ warn("Bad cartridge signature. Expected 'C64 CARTRIDGE ', got ...\n");
printReadable(&data[0], 16);
return false;
}
@@ -97,18 +99,18 @@ Cartridge::readFromBuffer(const uint8_t *buffer, unsigned length)
// Minimum header size is 0x40. Some cartridges show a value of 0x20 which is wrong.
if (headerSize < 0x40) headerSize = 0x40;
- fprintf(stderr, "Cartridge: %s\n", getCartridgeName());
- fprintf(stderr, " Header: %08X bytes long (normally 0x40)\n", headerSize);
- fprintf(stderr, " Type: %d\n", getCartridgeType());
- fprintf(stderr, " Game: %d\n", getGameLine());
- fprintf(stderr, " Exrom: %d\n", getExromLine());
+ msg("Cartridge: %s\n", getCartridgeName());
+ msg(" Header: %08X bytes long (normally 0x40)\n", headerSize);
+ msg(" Type: %d\n", getCartridgeType());
+ msg(" Game: %d\n", getGameLine());
+ msg(" Exrom: %d\n", getExromLine());
// Load chip packets
uint8_t *ptr = &data[headerSize];
for (numberOfChips = 0; ptr < data + length; numberOfChips++) {
if (memcmp("CHIP", ptr, 4) != 0) {
- fprintf(stderr, "Unexpected data in cartridge, expected 'CHIP'\n");
+ warn("Unexpected data in cartridge, expected 'CHIP'\n");
printReadable(ptr, 4);
return false;
}
@@ -120,6 +122,6 @@ Cartridge::readFromBuffer(const uint8_t *buffer, unsigned length)
ptr += getChipSize(numberOfChips);
}
- fprintf(stderr, "CRT container imported successfully (%d chips)\n", numberOfChips);
+ debug("CRT container imported successfully (%d chips)\n", numberOfChips);
return true;
}
diff --git a/C64/Cartridge.h b/C64/Cartridge.h
old mode 100755
new mode 100644
index 8832f5d..a7195b5
--- a/C64/Cartridge.h
+++ b/C64/Cartridge.h
@@ -1,6 +1,9 @@
+/*!
+ * @header ExpansionPort.h
+ * @author Written by Dirk Hoffmann based on the original code by A. Carl Douglas.
+ * @copyright All rights reserved.
+ */
/*
- * Written by Dirk Hoffmann based on the original code by A. Carl Douglas.
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -17,16 +20,16 @@
*/
/*
- For general info about C64 cartridges,
- see: http://www.c64-wiki.com/index.php/Cartridge
-
- For information about bank switching,
- see: http://www.c64-wiki.com/index.php/Bankswitching
-
- For details about the .CRT format,
- see: http://ist.uwaterloo.ca/~schepers/formats/CRT.TXT
-
- As well read the Commodore 64 Programmers Reference Guide pages 260-267.
+ * For general info about C64 cartridges,
+ * see: http://www.c64-wiki.com/index.php/Cartridge
+ *
+ * For information about bank switching,
+ * see: http://www.c64-wiki.com/index.php/Bankswitching
+ *
+ * For details about the .CRT format,
+ * see: http://ist.uwaterloo.ca/~schepers/formats/CRT.TXT
+ *
+ * As well read the Commodore 64 Programmers Reference Guide pages 260-267.
*/
#ifndef _CARTRIDGE_H
@@ -34,37 +37,37 @@
#include "Container.h"
-/*! @class Cartridge
- * @brief The Cartridge class declares the programmatic interface for a file of the CRT format type.
+/*! @class Cartridge
+ * @brief The Cartridge class declares the programmatic interface for a file of the CRT format type.
*/
class Cartridge : public Container {
private:
- //! Raw data of CRT container file
+ //! @brief Raw data of CRT container file
uint8_t *data;
- //! Number of chips contained in cartridge file
+ //! @brief Number of chips contained in cartridge file
unsigned int numberOfChips;
- //! Indicates where each chip section starts
+ //! @brief Indicates where each chip section starts
uint8_t *chips[64];
public:
- //! Constructor
+ //! @brief Constructor
Cartridge();
- //! Destructor
+ //! @brief Destructor
~Cartridge();
- //! Free allocated memory
+ //! @brief Frees the allocated memory.
void dealloc();
- //! Type of container
+ //! @brief Returns the container type
ContainerType getType() { return CRT_CONTAINER; }
- //! Type of container in plain text
+ //! @brief Type of container in plain text
const char *getTypeAsString() { return "CRT"; }
//! Returns true of filename points to a valid file of that type
diff --git a/C64/Container.cpp b/C64/Container.cpp
old mode 100755
new mode 100644
index 054fe1c..da1b688
--- a/C64/Container.cpp
+++ b/C64/Container.cpp
@@ -109,12 +109,12 @@ Container::readFromFile(const char *filename)
setPath(filename);
setName(ChangeExtension(ExtractFilename(getPath()), "").c_str());
- fprintf(stderr, "Container %s (%s) read successfully from file %s\n", name, getName(), path);
+ debug(1, "Container %s (%s) read successfully from file %s\n", name, getName(), path);
success = true;
exit:
- if (file)
+ if (file)
fclose(file);
if (buffer)
free(buffer);
@@ -134,11 +134,15 @@ Container::writeToFile(const char *filename)
bool success = false;
uint8_t *data = NULL;
FILE *file;
- unsigned filesize = writeToBuffer(NULL);
+ unsigned filesize;
- assert (filename != NULL);
-
+ // Determine file size
+ filesize = writeToBuffer(NULL);
+ if (filesize == 0)
+ return false;
+
// Open file
+ assert (filename != NULL);
if (!(file = fopen(filename, "w"))) {
goto exit;
}
@@ -163,9 +167,9 @@ Container::writeToFile(const char *filename)
exit:
if (file)
- fclose(file);
+ fclose(file);
if (data)
- free(data);
+ free(data);
return success;
}
diff --git a/C64/Container.h b/C64/Container.h
old mode 100755
new mode 100644
index ed4ed80..6025674
--- a/C64/Container.h
+++ b/C64/Container.h
@@ -1,6 +1,9 @@
+/*!
+ * @header Container.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
/*
- * Author: Dirk W. Hoffmann, www.dirkwhoffmann.de
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -19,20 +22,21 @@
#ifndef _CONTAINER_INC
#define _CONTAINER_INC
-#include "basic.h"
-
-/*! @enum ContainerType
- @brief The type of a container
- @constant CRT_CONTAINER A cartridge that can be plugged into the expansion port.
- @constant V64_CONTAINER A snapshot file (contains a frozen C64 state).
- @constant D64_CONTAINER A floppy disk image with multiply files.
- @constant T64_CONTAINER A tape archive with multiple files.
- @constant PRG_CONTAINER A program archive containing a single file.
- @constant P00_CONTAINER A program archive containing a single file.
- @constant G64_CONTAINER A collection of bit-streams resembling a floppy disk.
- @constant NIB_CONTAINER A collection of bit-streams resembling a floppy disk.
- @constant TAP_CONTAINER A bit-stream resembling a datasette tape.
- @constant FILE_CONTAINER An arbitrary file that is interpreted as raw data. */
+#include "VC64Object.h"
+
+/*! @enum ContainerType
+ * @brief The type of a container
+ * @constant CRT_CONTAINER A cartridge that can be plugged into the expansion port.
+ * @constant V64_CONTAINER A snapshot file (contains a frozen C64 state).
+ * @constant D64_CONTAINER A floppy disk image with multiply files.
+ * @constant T64_CONTAINER A tape archive with multiple files.
+ * @constant PRG_CONTAINER A program archive containing a single file.
+ * @constant P00_CONTAINER A program archive containing a single file.
+ * @constant G64_CONTAINER A collection of bit-streams resembling a floppy disk.
+ * @constant NIB_CONTAINER A collection of bit-streams resembling a floppy disk.
+ * @constant TAP_CONTAINER A bit-stream resembling a datasette tape.
+ * @constant FILE_CONTAINER An arbitrary file that is interpreted as raw data.
+ */
enum ContainerType {
CRT_CONTAINER = 1,
V64_CONTAINER,
@@ -47,20 +51,23 @@ enum ContainerType {
};
-/*! @class Container
- @brief Base class for all loadable objects.
- @discussion The class provides basic functionality for reading and writing files. */
-class Container {
+/*! @class Container
+ * @brief Base class for all loadable objects.
+ * @details The class provides basic functionality for reading and writing files.
+ */
+class Container : public VC64Object {
private:
-
- //! @brief The physical name (full path name) of the archive.
+
+ //! @brief The physical name (full path name) of the archive.
char *path;
protected:
- /*! @brief The logical name of the archive.
- @discussion Some archives store a logical name in their header section. If they don't, the logical name is the raw filename (path and extension stripped off). */
+ /*! @brief The logical name of the archive.
+ * @details Some archives store a logical name in their header section. If they don't store a special name,
+ * the logical name is the raw filename (path and extension stripped off).
+ */
char name[256];
@@ -70,13 +77,13 @@ protected:
public:
- //! @brief Standard constructor.
+ //! @brief Constructor
Container();
- //! @brief Standard destructor.
+ //! @brief Destructor
virtual ~Container();
- //! @brief Convert file name extension into numerical identifier
+ //! @brief Convert file name extension into numerical identifier
static ContainerType typeOf(const char *extension);
private:
@@ -90,23 +97,24 @@ private:
public:
- //! @brief Returns the physical name.
+ //! @brief Returns the physical name.
const char *getPath() { return path ? path : ""; }
- //! @brief Sets the physical name.
+ //! @brief Sets the physical name.
void setPath(const char *path);
- //! @brief Returns the logical name.
+ //! @brief Returns the logical name.
virtual const char *getName() { return name; }
- //! @brief Sets the logical name.
+ //! @brief Sets the logical name.
void setName(const char *name);
- //! @brief Returns the type of this container.
+ //! @brief Returns the type of this container.
virtual ContainerType getType() = 0;
- /*! @brief Returns the type of this container object as plain text, e.g., "T64" or "D64".
- @deprecated Use getType instead. */
+ /*! @brief Returns the type of this container object as plain text, e.g., "T64" or "D64".
+ * @deprecated Use getType instead.
+ */
virtual const char *getTypeAsString() = 0;
@@ -114,27 +122,34 @@ public:
//! @functiongroup Serializing a container
//
- //! @brief Returns true iff the specified file is a file of this container type.
+ //! @brief Returns true iff the specified file is a file of this container type.
virtual bool fileIsValid(const char *filename) = 0;
- /*! @brief Read container contents from a memory buffer.
- @param buffer The address of a binary representation in memory.
- @param length The size of the binary representation. */
+ /*! @brief Read container contents from a memory buffer.
+ * @param buffer The address of a binary representation in memory.
+ * @param length The size of the binary representation.
+ */
virtual bool readFromBuffer(const uint8_t *buffer, unsigned length) = 0;
- /*! @brief Read container contents from a file.
- @param filename The name of a file containing a binary representation.
- @discussion This function requires no custom implementation. It first reads in the file contents in memory and invokes readFromBuffer afterwards. */
+ /*! @brief Read container contents from a file.
+ * @details This function requires no custom implementation. It first reads in the file contents
+ * in memory and invokes readFromBuffer afterwards.
+ * @param filename The name of a file containing a binary representation.
+ */
bool readFromFile(const char *filename);
- /*! @brief Write container contents into a memory buffer.
- @param buffer The address of the buffer in memory.
- @discussion If a NULL pointer is passed in, a test run is performed. Test runs are performed to determine the size of the container on disk. */
+ /*! @brief Write container contents into a memory buffer.
+ * @details If a NULL pointer is passed in, a test run is performed. Test runs are performed to
+ * determine the size of the container on disk.
+ * @param buffer The address of the buffer in memory.
+ */
virtual unsigned writeToBuffer(uint8_t *buffer);
- /*! @brief Write container contents to a file.
- @param filename The name of a file to be written.
- @discussion This function requires no custom implementation. t first invokes writeToBuffer and writes the data to disk afterwards. */
+ /*! @brief Write container contents to a file.
+ * @details This function requires no custom implementation. t first invokes writeToBuffer and
+ * writes the data to disk afterwards.
+ * @param filename The name of a file to be written.
+ */
bool writeToFile(const char *filename);
};
diff --git a/C64/D64Archive.cpp b/C64/D64Archive.cpp
index 19cb26e..6f8db12 100755
--- a/C64/D64Archive.cpp
+++ b/C64/D64Archive.cpp
@@ -16,7 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "D64Archive.h"
+#include "T64Archive.h"
+#include "PRGArchive.h"
+#include "P00Archive.h"
+#include "FileArchive.h"
typedef struct D64TrackInfo {
int numberOfSectors;
@@ -78,6 +82,7 @@ static const D64TrackInfo D64Map[] =
D64Archive::D64Archive()
{
+ setDescription("D64Archive");
memset(name, 0, sizeof(name));
memset(data, 0, sizeof(data));
memset(errors, 0, sizeof(errors));
@@ -116,16 +121,14 @@ D64Archive::isD64File(const char *filename)
D64Archive *
D64Archive::archiveFromD64File(const char *filename)
{
- D64Archive *archive;
-
- fprintf(stderr, "Loading D64 archive from D64 file...\n");
- archive = new D64Archive();
+ D64Archive *archive = new D64Archive();
+
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
delete archive;
- archive = NULL;
+ return NULL;
}
+ archive->debug(1, "D64 archive created from file %s.\n", filename);
return archive;
}
@@ -158,18 +161,12 @@ D64Archive::archiveFromArbitraryFile(const char *filename)
D64Archive *
D64Archive::archiveFromD64Archive(D64Archive *otherArchive)
{
- D64Archive *archive;
-
if (otherArchive == NULL)
return NULL;
- fprintf(stderr, "Cloning D64 archive...\n");
+ D64Archive *archive = new D64Archive();
+ archive->debug(1, "Creating D64 archive by cloning other D64 archive ...\n");
- if ((archive = new D64Archive()) == NULL) {
- fprintf(stderr, "Failed to create D64 archive\n");
- return NULL;
- }
-
memcpy(archive->name, otherArchive->name, sizeof(archive->name));
memcpy(archive->data, otherArchive->data, sizeof(archive->data));
memcpy(archive->errors, otherArchive->errors, sizeof(archive->errors));
@@ -182,17 +179,11 @@ D64Archive::archiveFromD64Archive(D64Archive *otherArchive)
D64Archive *
D64Archive::archiveFromArchive(Archive *otherArchive)
{
- D64Archive *archive;
-
if (otherArchive == NULL)
return NULL;
-
- fprintf(stderr, "Creating D64 archive from an %s archive...\n", otherArchive->getTypeAsString());
-
- if ((archive = new D64Archive()) == NULL) {
- fprintf(stderr, "Failed to create D64 archive\n");
- return NULL;
- }
+
+ D64Archive *archive = new D64Archive();
+ archive->debug(1, "Creating D64 archive from a %s archive...\n", otherArchive->getTypeAsString());
// Copy file path
archive->setPath(otherArchive->getPath());
@@ -216,7 +207,7 @@ D64Archive::archiveFromArchive(Archive *otherArchive)
int byte;
unsigned num = 0;
- fprintf(stderr, "Will write %d bytes\n", otherArchive->getSizeOfItem(i));
+ archive->debug(2, "Will write %d bytes\n", otherArchive->getSizeOfItem(i));
otherArchive->selectItem(i);
while ((byte = otherArchive->getByte()) != EOF) {
@@ -224,32 +215,29 @@ D64Archive::archiveFromArchive(Archive *otherArchive)
num++;
}
- fprintf(stderr, "D64 item %d: %d bytes written\n", i, num);
+ archive->debug(2, "D64 item %d: %d bytes written\n", i, num);
// Item i has been written. Goto next free sector and proceed with the next item
(void)archive->nextTrackAndSector(track, sector, &track, &sector, true /* skip directory track */);
}
- fprintf(stderr, "Archive created (item 0 has %d bytes)\n", archive->getSizeOfItem(0));
- fprintf(stderr, "%s archive created (size of item 0 = %d).\n",
+ archive->debug(2, "Archive created (item 0 has %d bytes)\n", archive->getSizeOfItem(0));
+ archive->debug(2, "%s archive created (size of item 0 = %d).\n",
archive->getTypeAsString(), archive->getSizeOfItem(0));
return archive;
}
+#if 0
D64Archive *
D64Archive::archiveFromDrive(VC1541 *drive)
{
- D64Archive *archive;
- int error;
-
- fprintf(stderr, "Creating D64 archive from VC1541 drive...\n");
-
- if ((archive = new D64Archive()) == NULL)
- return NULL;
-
+ D64Archive *archive = new D64Archive();
+ archive->debug(1, "Creating D64 archive from VC1541 drive contents...\n");
+
// Perform test run
+ int error;
if (drive->disk.decodeDisk(NULL, &error) > D64_802_SECTORS_ECC || error) {
- fprintf(stderr, "Cannot create archive (error code: %d)\n", error);
+ archive->warn("Cannot create archive (error code: %d)\n", error);
delete archive;
return NULL;
}
@@ -258,12 +246,12 @@ D64Archive::archiveFromDrive(VC1541 *drive)
archive->numTracks = 42;
drive->disk.decodeDisk(archive->data);
- fprintf(stderr, "Archive has %d files\n", archive->getNumberOfItems());
- fprintf(stderr, "Item %d has size: %d\n", 0, archive->getSizeOfItem(0));
+ archive->debug(2, "Archive has %d files\n", archive->getNumberOfItems());
+ archive->debug(2, "Item %d has size: %d\n", 0, archive->getSizeOfItem(0));
return archive;
}
-
+#endif
//
// Virtual functions from Container class
@@ -284,45 +272,45 @@ D64Archive::readFromBuffer(const uint8_t *buffer, unsigned length)
{
case D64_683_SECTORS: // 35 tracks, no errors
- fprintf(stderr, "D64 file contains 35 tracks, no EC bytes\n");
+ debug(2, "D64 file contains 35 tracks, no EC bytes\n");
numTracks = 35;
break;
case D64_683_SECTORS_ECC: // 35 tracks, 683 error bytes
- fprintf(stderr, "D64 file contains 35 tracks, 683 EC bytes\n");
+ debug(2, "D64 file contains 35 tracks, 683 EC bytes\n");
numTracks = 35;
numberOfErrors = 683;
break;
case D64_768_SECTORS: // 40 tracks, no errors
- fprintf(stderr, "D64 file contains 40 tracks, no EC bytes\n");
+ debug(2, "D64 file contains 40 tracks, no EC bytes\n");
numTracks = 40;
break;
case D64_768_SECTORS_ECC: // 40 tracks, 768 error bytes
- fprintf(stderr, "D64 file contains 40 tracks, 768 EC bytes\n");
+ debug(2, "D64 file contains 40 tracks, 768 EC bytes\n");
numTracks = 40;
numberOfErrors = 768;
break;
case D64_802_SECTORS: // 42 tracks, no error bytes
- fprintf(stderr, "D64 file contains 42 tracks, no EC bytes\n");
+ debug(2, "D64 file contains 42 tracks, no EC bytes\n");
numTracks = 42;
break;
case D64_802_SECTORS_ECC: // 42 tracks, 802 error bytes
- fprintf(stderr, "D64 file contains 42 tracks, 802 EC bytes\n");
+ debug(2, "D64 file contains 42 tracks, 802 EC bytes\n");
numTracks = 42;
numberOfErrors = 802;
break;
default:
- fprintf(stderr, "D64 has an unknown format\n");
+ warn("D64 has an unknown format\n");
return false;
}
@@ -395,7 +383,7 @@ D64Archive::getNumberOfItems()
return i;
#endif
- unsigned offsets[MAX_FILES_ON_DISK];
+ unsigned offsets[144]; // a C64 disk contains at most 144 files
unsigned noOfFiles;
scanDirectory(offsets, &noOfFiles);
@@ -493,8 +481,6 @@ D64Archive::selectItem(int item)
{
fp = -1;
- // fprintf(stderr, "selectItem:%d\n", item);
-
// check, if item exists
if (item >= getNumberOfItems())
return;
@@ -503,8 +489,6 @@ D64Archive::selectItem(int item)
if ((fp = findDirectoryEntry(item)) <= 0)
return;
- // fprintf(stderr, "First data sector: %02X, %02X", data[fp+0x03], data[fp+0x04]);
-
// find first data sector
if ((fp = offset(data[fp+0x01], data[fp+0x02])) < 0)
return;
@@ -517,7 +501,6 @@ D64Archive::selectItem(int item)
fp += 2;
// We finally reached the first real data byte :-)
- // fprintf(stderr, "Item selected (%d,%d)\n", data[fp+0x03], data[fp+0x04]);
}
int
@@ -610,6 +593,12 @@ D64Archive::numberOfTracks()
return numTracks;
}
+void
+D64Archive::setNumberOfTracks(unsigned tracks)
+{
+ assert(numTracks == 35 || numTracks == 40 || numTracks == 42);
+ numTracks = tracks;
+}
//
@@ -625,7 +614,7 @@ D64Archive::findSector(unsigned track, unsigned sector)
int
D64Archive::offset(int track, int sector)
{
- assert(isTrackNumber(track));
+ assert(1 <= track && track <= 42);
assert(sector < D64Map[track].numberOfSectors);
return D64Map[track].offset + (sector * 256);
@@ -684,13 +673,12 @@ D64Archive::writeByteToSector(uint8_t byte, uint8_t *t, uint8_t *s)
uint8_t positionOfLastDataByte = data[pos + 1];
if (positionOfLastDataByte == 0xFF) {
- // fprintf(stderr, "%d/%d is full. ", track, sector);
- // No rool in this sector, proceed to next one
+
+ // No free slots in this sector, proceed to next one
if (!nextTrackAndSector(track, sector, &track, &sector, true /* skip directory track */)) {
- // Sorry, disk is full
- return false;
+ return false; // Sorry, disk is full
}
- // fprintf(stderr, "Switching to %d/%d\n", track, sector);
+
// link previous sector with the new one
data[pos++] = track;
data[pos] = sector;
@@ -725,8 +713,6 @@ D64Archive::markSectorAsUsed(uint8_t track, uint8_t sector)
{
// For each track and sector, there exists a single bit in the BAM. 1 = used, 0 = unused
- // fprintf(stderr,"Marking track %d and sector %d as used\n", track, sector);
-
// First byte of BAM
int bam = offset(18,0);
@@ -828,7 +814,7 @@ D64Archive::scanDirectory(unsigned *offsets, unsigned *noOfFiles, bool skipInvis
pos += 2; // Move to the beginning of the first directory entry
- while (i < MAX_FILES_ON_DISK) {
+ while (i < 144 /* maximum number of files on disk */) {
// Only proceed if the directory entry is not a null entry
const char nullEntry[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
@@ -864,7 +850,7 @@ D64Archive::scanDirectory(unsigned *offsets, unsigned *noOfFiles, bool skipInvis
int
D64Archive::findDirectoryEntry(int item, bool skipInvisibleFiles)
{
- unsigned offsets[MAX_FILES_ON_DISK];
+ unsigned offsets[144];
unsigned noOfFiles;
scanDirectory(offsets, &noOfFiles, skipInvisibleFiles);
@@ -878,8 +864,8 @@ D64Archive::writeDirectoryEntry(unsigned nr, const char *name, uint8_t startTrac
{
int pos;
- if (nr >= MAX_FILES_ON_DISK) {
- fprintf(stderr, "Cannot write directory entry. Number of files is limited to %d\n", MAX_FILES_ON_DISK);
+ if (nr >= 144) {
+ warn("Cannot write directory entry. Number of files is limited to 144\n");
return false;
}
@@ -941,9 +927,9 @@ D64Archive::dumpSector(int track, int sector)
{
int pos = offset(track, sector);
- fprintf(stderr, "Sector %d/%d\n", track, sector);
+ msg("Sector %d/%d\n", track, sector);
for (int i = 0; i < 256; i++) {
- fprintf(stderr, "%02X ", data[pos++]);
+ msg("%02X ", data[pos++]);
}
}
diff --git a/C64/D64Archive.h b/C64/D64Archive.h
index 154f739..7e1c71b 100755
--- a/C64/D64Archive.h
+++ b/C64/D64Archive.h
@@ -24,9 +24,6 @@
#include "Archive.h"
-// Forward declarations
-class VC1541;
-
// D64 files come in six different sizes
#define D64_683_SECTORS 174848
#define D64_683_SECTORS_ECC 175531
@@ -99,8 +96,10 @@ public:
/*! @brief Creates a D64 archive from a VC1541 drive.
* @param drive A VC1541 drive with a disk inserted.
* @result A D64 archive containing the same files as the currently inserted disk;
- * NULL if no disk is inserted. */
- static D64Archive *archiveFromDrive(VC1541 *drive);
+ * NULL if no disk is inserted.
+ @ @deprecated
+ */
+ // static D64Archive *archiveFromDrive(VC1541 *drive);
//
@@ -135,30 +134,40 @@ public:
//
//! @functiongroup Accessing archive attributes
//
+
+ //! @brief Returns a pointer to the raw archive data
+ uint8_t *getData() { return data; }
+
+ //! @brief Returns the number of tracks stored in this image
+ unsigned numberOfTracks();
+
+ //! @brief Sets the number of tracks stored in this image
+ void setNumberOfTracks(unsigned tracks);
+
- //! Returns true iff item is a visible directory entry
- /*! Some files, e.g., deleted ones, are still present on the directory sector, but
- don't show up when loading the directory via LOAD "$",8.
- If the extension parameter is provides, an extension string is returned (e.g. "PRG").
- Invisible files will be returned with extension "" */
+ /*! @brief Returns true iff item is a visible file
+ * @details Whether a file is visible or not is determined by the type character, a special byte
+ * stored inside the directory. The type character also determines how the file is displayed
+ * when the directory is loaded via LOAD "$",8. E.g., standard program files are listes as PRG.
+ * @param typeChar The type character of a file.
+ * @param extension If this parameter is provided, an extension string is returned (e.g. "PRG").
+ * Invisible files will return "" as extension string.
+ */
bool itemIsVisible(uint8_t typeChar, const char **extension = NULL);
- //! @brief Returns the logical name of the archive in PET format
+ //! @brief Returns the logical name of the archive in PET format
const char *getNameAsPETString();
- //! @brief Returns the name of an item in PET format
+ //! @brief Returns the name of an item in PET format
const char *getNameOfItemAsPETString(int n);
- //! @brief Class function that returns the total number of sectors in a specific track
+ //! @brief Class function that returns the total number of sectors in a specific track
static unsigned numberOfSectors(unsigned trackNr);
- //! @brief Returns the number of tracks stored in this image
- unsigned numberOfTracks();
-
- //! @brief Returns the low byte of the disk ID
+ //! @brief Returns the low byte of the disk ID
uint8_t diskIdLow() { return data[offset(18, 0) + 0xA2]; }
- //! @brief Returns the high byte of the disk ID
+ //! @brief Returns the high byte of the disk ID
uint8_t diskIdHi() { return data[offset(18, 0) + 0xA3]; }
@@ -246,15 +255,18 @@ private:
/*! Example usage: firstTrackOfFile(findDirectoryEntry(42)) */
inline uint8_t firstTrackOfFile(unsigned dirEntry) { return data[dirEntry + 1]; }
- //! Returns the sector number of the first file block
- /*! Example usage: firstSectorOfFile(findDirectoryEntry(42)) */
+ //! @brief Returns the sector number of the first file block
+ /*! @details Example usage: firstSectorOfFile(findDirectoryEntry(42))
+ */
inline uint8_t firstSectorOfFile(unsigned dirEntry) { return data[dirEntry + 2]; }
- //! @brief Returns true iff offset points to the last byte of a file
+ /*! @brief Returns true iff offset points to the last byte of a file
+ */
bool isEndOfFile(int offset) { return nextTrack(offset) == 0x00 && nextSector(offset) == offset % 256; }
- /*! @brief Writes a directory item
- @discussion This function is used to convert other archive formats into the D64 format. */
+ /*! @brief Writes a directory item
+ * @details This function is used to convert other archive formats into the D64 format.
+ */
bool writeDirectoryEntry(unsigned nr, const char *name, uint8_t startTrack, uint8_t startSector, unsigned filesize);
@@ -264,7 +276,7 @@ private:
private:
- //! @brief Dumps the contents of a sector to stderr
+ //! @brief Dumps the contents of a sector to stderr
void dumpSector(int track, int sector);
};
#endif
diff --git a/C64/Datasette.cpp b/C64/Datasette.cpp
old mode 100755
new mode 100644
index 611fce0..a897097
--- a/C64/Datasette.cpp
+++ b/C64/Datasette.cpp
@@ -1,7 +1,10 @@
-/*
- * Written 2015 by Dirk W. Hoffmann
- *
- * This program is free software; you can redistribute it and/or modify
+/*!
+ * @header Datasette.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2015 - 2016 Dirk W. Hoffmann
+ * @brief Declares Datasette class
+ */
+/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -20,8 +23,8 @@
Datasette::Datasette()
{
- name = "Datasette";
- debug(2, "Creating virtual datasette at address %p\n", this);
+ setDescription("Datasette");
+ debug(3, "Creating virtual datasette at address %p\n", this);
// Register snapshot items
SnapshotItem items[] = {
@@ -53,7 +56,7 @@ Datasette::Datasette()
Datasette::~Datasette()
{
- debug(2, "Releasing Datasette...\n");
+ debug(3, "Releasing Datasette...\n");
if (data)
delete data;
@@ -69,7 +72,7 @@ Datasette::reset()
void
Datasette::ping()
{
- debug(2, "Pinging Datasette...\n");
+ debug(3, "Pinging Datasette...\n");
c64->putMessage(MSG_VC1530_TAPE, hasTape() ? 1 : 0);
// c64->putMessage(MSG_VC1530_MOTOR, motor ? 1 : 0);
// c64->putMessage(MSG_VC1530_PLAY, playKey ? 1 : 0);
@@ -119,11 +122,6 @@ Datasette::dumpState()
#if 0
msg("Datasette\n");
msg("---------\n\n");
- msg(" Bit ready timer : %d\n", bitReadyTimer);
- msg(" Head position : Track %d, Bit offset %d\n", halftrack, bitoffset);
- msg(" SYNC : %d\n", sync);
- msg(" Read mode : %s\n", readMode() ? "YES" : "NO");
- msg("\n");
#endif
}
@@ -232,17 +230,12 @@ Datasette::pressPlay()
return;
debug("Datasette::pressPlay\n");
+ playKey = true;
- // nextFallingEdge = 0.5 * PAL_CYCLES_PER_SECOND; /* kick off in 0.5 seconds */
- // nextRisingEdge = -1;
-
// Schedule first pulse
uint64_t length = pulseLength();
nextRisingEdge = length / 2;
nextFallingEdge = length;
-
- playKey = true;
- // c64->putMessage(MSG_VC1530_PLAY, true);
}
void
@@ -251,7 +244,6 @@ Datasette::pressStop()
debug("Datasette::pressStop\n");
setMotor(false);
playKey = false;
- // c64->putMessage(MSG_VC1530_PLAY, false);
}
void
@@ -261,7 +253,6 @@ Datasette::setMotor(bool value)
return;
motor = value;
- // c64->putMessage(MSG_VC1530_MOTOR, motor);
}
void
@@ -291,13 +282,13 @@ Datasette::_execute()
void
Datasette::_executeRising()
{
- c64->cia1->triggerRisingEdgeOnFlagPin();
+ c64->cia1.triggerRisingEdgeOnFlagPin();
}
void
Datasette::_executeFalling()
{
- c64->cia1->triggerFallingEdgeOnFlagPin();
+ c64->cia1.triggerFallingEdgeOnFlagPin();
// Schedule next pulse
advanceHead();
diff --git a/C64/Datasette.h b/C64/Datasette.h
old mode 100755
new mode 100644
index 3e20208..60df31c
--- a/C64/Datasette.h
+++ b/C64/Datasette.h
@@ -1,8 +1,7 @@
/*!
* @header Datasette.h
* @author Dirk W. Hoffmann, www.dirkwhoffmann.de
- * @copyright 2015 Dirk W. Hoffmann
- * @brief Declares Datasette class
+ * @copyright 2015 - 2016 Dirk W. Hoffmann
*/
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,39 +22,40 @@
#define _DATASETTE_INC
// Forward declarations
-class C64;
+class TAPArchive;
-/*!
- * @brief Virtual tape recorder (datasette)
+/*!
+ * @brief Virtual tape recorder (datasette)
*/
class Datasette : public VirtualComponent {
public:
- //! Constructor
+ //! @brief Constructor
Datasette();
- //! Destructor
+ //! @brief Destructor
~Datasette();
- //! Reset VC1541 drive
+ //! @brief Resets the VC1541 drive
void reset();
- //! Dump current configuration into message queue
+ //! @brief Dumps current configuration into message queue
void ping();
- //! Size of internal state
+ //! @brief Return the size of the internal state
uint32_t stateSize();
- //! Load state
+ //! @brief Restores the current state from a buffer
void loadFromBuffer(uint8_t **buffer);
- //! Save state
+ //! @brief Saves the current state into a buffer
void saveToBuffer(uint8_t **buffer);
- //! Dump current state into logfile
+ //! @brief Dumps the current state
void dumpState();
+
// ---------------------------------------------------------------------------------------------
// Atrributes
// ---------------------------------------------------------------------------------------------
@@ -66,50 +66,61 @@ private:
//! @functiongroup Tape
//
- //! @brief Data buffer (contains the raw data of the TAP archive)
- /*! @discussion Pointer is NULL if no data is present */
+ /*! @brief Data buffer (contains the raw data of the TAP archive)
+ * @details Pointer is NULL if no data is present
+ */
uint8_t *data;
- //! @brief Size of the attached data buffer
- /*! @discussion Equals 0 iff no tape is inserted. */
+ /*! @brief Size of the attached data buffer
+ * @details Equals 0 iff no tape is inserted.
+ */
uint32_t size;
- //! @brief Data format (TAP type)
- /*! @discussion In TAP format 0, data byte 0 signals a long puls without stating its length precisely.
- * In TAP format 1, each 0 is followed by three bytes stating the precise length in
- * LO_LO_HI_00 format. */
+ /*! @brief Data format (TAP type)
+ * @details In TAP format 0, data byte 0 signals a long puls without stating its length precisely.
+ * In TAP format 1, each 0 is followed by three bytes stating the precise length in
+ * LO_LO_HI_00 format.
+ */
uint8_t type;
- //! @brief Tape length in cycles
- /*! @discussion The value is computed in insertTape by examining all pulses in the data buffer */
+ /*! @brief Tape length in cycles
+ * @details The value is computed in insertTape by examining all pulses in the data buffer
+ */
uint64_t durationInCycles;
//
//! @functiongroup Datasette
//
- /*! @brief Read/Write head
- * @discussion Value must be between 0 and size. head == size indicates EOT (end of tape) */
+ /*! @brief Read/Write head
+ * @details Value must be between 0 and size. head == size indicates EOT (end of tape)
+ */
uint32_t head;
- /*! @brief Read/Write head
- * @discussion Head position, measured in cycles */
+ /*! @brief Read/Write head
+ * @details Head position, measured in cycles
+ */
uint64_t headInCycles;
- /*! @brief Read/Write head
- * @discussion Head position, measured in seconds */
+ /*! @brief Read/Write head
+ * @details Head position, measured in seconds
+ */
uint32_t headInSeconds;
- /*! @brief Next scheduled rising edge on data line */
+ /*! @brief Next scheduled rising edge on data line
+ */
int64_t nextRisingEdge;
- /*! @brief Next scheduled falling edge on data line */
+ /*! @brief Next scheduled falling edge on data line
+ */
int64_t nextFallingEdge;
- /*! @brief Indicates whether the play key is pressed */
+ /*! @brief Indicates whether the play key is pressed
+ */
bool playKey;
- /*! @brief Indicates whether the motor is on */
+ /*! @brief Indicates whether the motor is on
+ */
bool motor;
// ---------------------------------------------------------------------------------------------
@@ -122,75 +133,98 @@ public:
//! @functiongroup Handling virtual tapes
//
- /*! @brief Returns true if a tape is inserted */
+ /*! @brief Returns true if a tape is inserted
+ */
inline bool hasTape() { return size != 0; }
- /*! @brief Inserts a TAP archive as a virtual tape */
+ /*! @brief Inserts a TAP archive as a virtual tape
+ */
void insertTape(TAPArchive *a);
- /*! @brief Ejects the virtual tape
- * @discussion Does nothing, if no tape is present. */
+ /*! @brief Ejects the virtual tape
+ * @details Does nothing, if no tape is present.
+ */
void ejectTape();
- /*! @brief Returns the tape length in cycles */
+ /*! @brief Returns type of tape (TAP format, 0 or 1).
+ */
+ inline uint8_t getType() { return type; }
+
+ /*! @brief Returns the tape length in cycles
+ */
uint64_t getDurationInCycles() { return durationInCycles; }
- /*! @brief Returns the tape length in seconds */
+ /*! @brief Returns the tape length in seconds
+ */
uint32_t getDurationInSeconds() { return durationInCycles / PAL_CYCLES_PER_SECOND; }
//
//! @functiongroup Handling the read/write head
//
- /*! @brief Put head at the beginning of the tape */
+ /*! @brief Puts the read/write head at the beginning of the tape
+ */
void rewind() { head = headInSeconds = headInCycles = 0; }
- /*! @brief Advances the read/write head for one pulse
- * @discussion This methods updates head, headInCycles, and headInSeconds */
+ /*! @brief Advances the read/write head for one pulse
+ * @details This methods updates head, headInCycles, and headInSeconds
+ */
void advanceHead(bool silent = false);
- /*! @brief Get current head position in different units */
+ /*! @brief Gets the current head position in different units
+ */
uint32_t getHead() { return head; }
uint32_t getHeadInCycles() { return headInCycles; }
uint32_t getHeadInSeconds() { return headInSeconds; }
- /*! @brief Set current head position in cycles */
+ /*! @brief Sets the current head position in cycles
+ */
void setHeadInCycles(uint64_t value);
- /*! @brief Pulse length at current head position */
+ /*! @brief Returns the pulse length at the current head position
+ */
int pulseLength(int *skip);
int pulseLength() { return pulseLength(NULL); }
+
//
//! @functiongroup Running the device
//
- /*! @brief Returns true if the play key is pressed */
+ /*! @brief Returns true if the play key is pressed
+ */
bool getPlayKey() { return playKey; }
- /*! @brief Press play on tape */
+ /*! @brief Press play on tape
+ */
void pressPlay();
- /*! @brief Press stop key */
+ /*! @brief Press stop key
+ */
void pressStop();
- /*! @brief Returns true if the datasette motor is switched on */
+ /*! @brief Returns true if the datasette motor is switched on
+ */
bool getMotor() { return motor; }
- /*! @brief Switches motor on or off */
+ /*! @brief Switches motor on or off
+ */
void setMotor(bool value);
- /*! @brief Executes the virtual datasette
+ /*! @brief Executes the virtual datasette
*/
inline void execute() { if (playKey && motor) _execute(); }
private:
+ //! @brief Internal execution function
void _execute();
- void _executeFirst();
+
+ //! @brief Simulates the falling edge of a pulse
void _executeFalling();
+
+ //! @brief Simulates the rising edge of a pulse
void _executeRising();
- void _executeLast();
};
diff --git a/C64/Disk525.cpp b/C64/Disk525.cpp
old mode 100755
new mode 100644
index 2b47363..e4fc778
--- a/C64/Disk525.cpp
+++ b/C64/Disk525.cpp
@@ -24,7 +24,7 @@
Disk525::Disk525()
{
- name = "Disk525";
+ setDescription("Disk525");
// Register snapshot items
SnapshotItem items[] = {
@@ -431,8 +431,10 @@ Disk525::decodeDisk(uint8_t *dest, int *error)
unsigned r, w, copies, noOfOneBits, bitsOnTrack = 0, numBytes = 0;
int startOfFirstSyncMark = -1;
- if (error) *error = 0; // We assume the best
+ memset(tmpbuf1, 0, sizeof(tmpbuf1));
+ memset(tmpbuf2, 0, sizeof(tmpbuf2));
+ if (error) *error = 0; // We assume the best
// For each full track ...
for (Track t = 1; t <= numTracks; t++) {
diff --git a/C64/Disk525.h b/C64/Disk525.h
old mode 100755
new mode 100644
index 7c4e822..085ba83
--- a/C64/Disk525.h
+++ b/C64/Disk525.h
@@ -1,8 +1,7 @@
/*!
* @header Disk525.h
* @author Dirk W. Hoffmann, www.dirkwhoffmann.de
- * @copyright 2015 Dirk W. Hoffmann
- * @brief Declares Disk525 class
+ * @copyright 2015 - 2016 Dirk W. Hoffmann
*/
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,6 +23,7 @@
#include "VirtualComponent.h"
+// Forward declarations
class C64;
class D64Archive;
class G64Archive;
@@ -43,32 +43,32 @@ class NIBArchive;
* -----------------------------------------------------------------------------
*/
-/*! @brief Data type for addressing half and full tracks on disk
- * @details The VC1541 drive head can move between position 1 and 85. The odd numbers between
- * 1 and 70 mark the 35 tracks that are used by VC1541 DOS. This means that DOS moves
- * the drive head always two positions up or down. If programmed manually, the head can
- * also be position on half tracks and on tracks beyond 35.
- * @see Track
+/*! @brief Data type for addressing half and full tracks on disk
+ * @details The VC1541 drive head can move between position 1 and 85. The odd numbers between
+ * 1 and 70 mark the 35 tracks that are used by VC1541 DOS. This means that DOS moves
+ * the drive head always two positions up or down. If programmed manually, the head can
+ * also be position on half tracks and on tracks beyond 35.
+ * @see Track
*/
typedef unsigned Halftrack;
-/*! @brief Data type for addressing full tracks on disk
- * @see Halftrack
+/*! @brief Data type for addressing full tracks on disk
+ * @see Halftrack
*/
typedef unsigned Track;
-/*! @brief Checks if a given number is a valid halftrack number
+/*! @brief Checks if a given number is a valid halftrack number
*/
inline bool isHalftrackNumber(unsigned nr) { return 1 <= nr && nr <= 84; }
-/*! @brief Checks if a given number is a valid track number
+/*! @brief Checks if a given number is a valid track number
*/
inline bool isTrackNumber(unsigned nr) { return 1 <= nr && nr <= 42; }
-/*! @brief Maximum number of files that can be stored on a single disk
- * @details VC1541 DOS stores the directors on track 18 which contains 19 sectors. Sector 0 is
- * reserved for the BAM. Each of the remaining sectors can hold up to 8 directory entries,
- * summing um to a total of 144 items.
+/*! @brief Maximum number of files that can be stored on a single disk
+ * @details VC1541 DOS stores the directors on track 18 which contains 19 sectors. Sector 0 is
+ * reserved for the BAM. Each of the remaining sectors can hold up to 8 directory entries,
+ * summing um to a total of 144 items.
*/
const unsigned MAX_FILES_ON_DISK = 144;
@@ -77,7 +77,7 @@ const unsigned MAX_FILES_ON_DISK = 144;
// Disk525
// -----------------------------------------------------------------------------------------------
-/*! @brief A virtual 5,25" floppy disk
+/*! @brief A virtual 5,25" floppy disk
*/
class Disk525 : public VirtualComponent {
@@ -86,14 +86,14 @@ public:
Disk525();
~Disk525();
- //! @brief Dump debug information
+ //! @brief Dump debug information
void dumpState();
private:
- /*! @brief GCR encoding table
- @details Maps 4 data bits to 5 GCR bits
+ /*! @brief GCR encoding table
+ @details Maps 4 data bits to 5 GCR bits
*/
const uint16_t gcr[16] = {
0x0a, 0x0b, 0x12, 0x13,
@@ -102,8 +102,8 @@ private:
0x0d, 0x1d, 0x1e, 0x15
};
- /*! @brief Inverse GCR encoding table
- @details Maps 5 data bits to 4 GCR bits. Initialized in constructor
+ /*! @brief Inverse GCR encoding table
+ @details Maps 5 data bits to 4 GCR bits. Initialized in constructor
*/
uint8_t invgcr[32];
@@ -231,10 +231,6 @@ public:
return result;
}
- /*
- inline uint8_t readByteFromHalftrack(Halftrack ht, unsigned offset) {
- assert(isHalftrackNumber(ht)); return readByte(data.halftrack[ht], offset); }
- */
//
//! @functiongroup Writing data to disk
@@ -244,15 +240,13 @@ public:
* @param data Pointer to the first data byte of a track
* @param offset Number of bit to set to 1 (first bit has offset 0)
*/
- inline void setBit(uint8_t *data, unsigned offset) {
- data[offset / 8] |= (0x80 >> (offset % 8)); } // modified = true; }
+ inline void setBit(uint8_t *data, unsigned offset) { data[offset / 8] |= (0x80 >> (offset % 8)); }
/*! @brief Sets a single bit on disk to 0
* @param data Pointer to the first data byte of a track
* @param offset Number of bit to clear (first bit has offset 0)
*/
- inline void clearBit(uint8_t *data, unsigned offset) {
- data[offset / 8] &= ~(0x80 >> (offset % 8)); } // modified = true; }
+ inline void clearBit(uint8_t *data, unsigned offset) { data[offset / 8] &= ~(0x80 >> (offset % 8)); }
/*! @brief Writes a single bit to disk
* @param data Pointer to the first data byte of a track
@@ -290,10 +284,6 @@ public:
writeBitToHalftrack(ht, offset + i, byte & mask);
}
- /*
- inline void writeByteToHalftrack(Halftrack ht, unsigned offset, uint8_t byte) {
- assert(isHalftrackNumber(ht)); writeByte(data.halftrack[ht], offset, byte); }
- */
//
//! @functiongroup Erasing disk data
@@ -363,26 +353,13 @@ private:
void writeSyncBits(uint8_t *dest, unsigned offset, unsigned length) {
for (unsigned i = 0; i < length; i++) writeBit(dest, offset + i, 1); }
- /*! @brief Write five SYNC bytes
- * @deprecated Implement encodeSyncBits instead
- */
- // void encodeSync(uint8_t *dest) { writeSyncBits(dest, 0, 5 * 8); }
- // { for (unsigned i = 0; i < 5; i++) dest[i] = 0xFF; }
-
/*! @brief Write interblock gap
*/
void writeGap(uint8_t *dest, unsigned offset, unsigned length) {
for (unsigned i = 0; i < length; i++) writeByte(dest, offset + i * 8, 0x55); }
-
- /*! @brief Write interblock gap
- * @deprecated Use writeGap instead
- */
- // void encodeGap(uint8_t *dest, unsigned size) { writeGap(dest, 0, size); }
- // { for (unsigned i = 0; i < size; i++) dest[i] = 0x55; }
/*! @brief Translates four data bytes into five GCR encodes bytes
*/
- // void encodeGcr(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t *dest);
void encodeGcr(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t *dest, unsigned offset);
diff --git a/C64/ExpansionPort.cpp b/C64/ExpansionPort.cpp
old mode 100755
new mode 100644
index ea638c8..64f8106
--- a/C64/ExpansionPort.cpp
+++ b/C64/ExpansionPort.cpp
@@ -20,8 +20,8 @@
ExpansionPort::ExpansionPort()
{
- name = "Expansion port";
- debug(2, " Creating expansion port at address %p...\n", this);
+ setDescription("Expansion port");
+ debug(3, " Creating expansion port at address %p...\n", this);
// We reset the cartridge here, as reset() keeps the cartridge intact.
resetCartridge();
@@ -29,7 +29,7 @@ ExpansionPort::ExpansionPort()
ExpansionPort::~ExpansionPort()
{
- debug(2, " Releasing expansion port...\n");
+ debug(3, " Releasing expansion port...\n");
detachCartridge();
}
@@ -236,14 +236,14 @@ void
ExpansionPort::setGameLine(bool value)
{
gameLine = value;
- c64->mem->updatePeekPokeLookupTables();
+ c64->mem.updatePeekPokeLookupTables();
}
void
ExpansionPort::setExromLine(bool value)
{
exromLine = value;
- c64->mem->updatePeekPokeLookupTables();
+ c64->mem.updatePeekPokeLookupTables();
}
void
@@ -311,7 +311,7 @@ ExpansionPort::attachCartridge(Cartridge *c)
// Blend in chip 0
switchBank(0);
- c64->mem->updatePeekPokeLookupTables();
+ c64->mem.updatePeekPokeLookupTables();
// dumpState();
c64->putMessage(MSG_CARTRIDGE, 1);
diff --git a/C64/ExpansionPort.h b/C64/ExpansionPort.h
old mode 100755
new mode 100644
index beac164..91f0994
--- a/C64/ExpansionPort.h
+++ b/C64/ExpansionPort.h
@@ -1,7 +1,9 @@
-/*
- * Written by Dirk Hoffmann based on the original code by A. Carl Douglas.
- *
- * This program is free software; you can redistribute it and/or modify
+/*!
+ * @header ExpansionPort.h
+ * @author Written by Dirk Hoffmann based on the original code by A. Carl Douglas.
+ * @copyright All rights reserved.
+ */
+/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -17,21 +19,20 @@
*/
/*
- See: http://www.c64-wiki.com/index.php/Cartridge
-
- "The cartridge system implemented in the C64 provides an easy way to
- hook 8 or 16 kilobytes of ROM into the computer's address space:
- This allows for applications and games up to 16 K, or BASIC expansions
- up to 8 K in size and appearing to the CPU along with the built-in
- BASIC ROM. In theory, such a cartridge need only contain the
- ROM circuit without any extra support electronics."
-
- Also: http://www.c64-wiki.com/index.php/Bankswitching
-
- As well read the Commodore 64 Programmers Reference Guide pages 260-267.
+ * For more information: http://www.c64-wiki.com/index.php/Cartridge
+ *
+ * "The cartridge system implemented in the C64 provides an easy way to
+ * hook 8 or 16 kilobytes of ROM into the computer's address space:
+ * This allows for applications and games up to 16 K, or BASIC expansions
+ * up to 8 K in size and appearing to the CPU along with the built-in
+ * BASIC ROM. In theory, such a cartridge need only contain the
+ * ROM circuit without any extra support electronics."
+ *
+ * Also: http://www.c64-wiki.com/index.php/Bankswitching
+ *
+ * As well read the Commodore 64 Programmers Reference Guide pages 260-267.
*/
-
#ifndef _EXPANSIONPORT_H
#define _EXPANSIONPORT_H
@@ -41,7 +42,7 @@ class ExpansionPort : public VirtualComponent {
public:
- //! Cartridge types
+ //! @brief Cartridge types
enum CartridgeType {
CRT_NORMAL = 0,
CRT_ACTION_REPLAY = 1,
@@ -76,100 +77,117 @@ public:
private:
- //! Type of attached cartridge (CRT_NONE if no cartridge is plugged in)
+ /*! @brief Type of the attached cartridge
+ * @details Value is CRT_NONE if no cartridge is plugged in.
+ */
uint8_t type;
- //! Game line of cartridge (HIGH if no cartridge is attached)
+ /*! @brief Game line of the attached cartridge
+ * @details Line is HIGH if no cartridge is plugged in.
+ */
bool gameLine;
- //! Exrom line of cartridge (HIGH if no cartridge is attached)
+ /*! @brief Exrom line of the attached cartridge
+ * @details Line is HIGH if no cartridge is plugged in.
+ */
bool exromLine;
- //! A cartridge can contain up to 64 chips that contain ROM data
+ /*! @brief ROM chips contained in the attached cartridge
+ * @details A cartridge can contain up to 64 chips
+ */
uint8_t *chip[64];
- //! Load address of chip
+ //! @brief Array containing the load addresses of all chips
uint16_t chipStartAddress[64];
- //! Chip size in Number of bytes in chip
+ //! @brief Array containing the chip sizes of all chips
uint16_t chipSize[64];
- //! Virtual cartridge ROM (32 kb starting at $8000)
- uint8_t rom[32768];
+ //! @brief Virtual cartridge ROM (32 kb starting at $8000)
+ uint8_t rom[0x8000];
- //! Indicates if ROM is blended in (0x01) or or out (0x00)
- /*! Each array item represents a 4k block above $8000 */
+ /*! @brief Indicates whether ROM is blended in (0x01) or or out (0x00)
+ * @details Each array item represents a 4k block above $8000
+ */
uint8_t blendedIn[16];
public:
- //! Constructor
+ //! @brief Constructor
ExpansionPort();
- //! Destructor
+ //! @brief Destructor
~ExpansionPort();
- //! Reset expansion port
+ //! @brief Resets the expansion port
void resetPort();
- //! Reset cartridge
+ //! @brief Resets the attached cartridge
void resetCartridge();
- //! Revert to initial state but keep cartridge data in place
+ //! @brief Reverts expansion port to its initial state, but keeps cartridge data in place
void softreset();
- //! Dump current configuration into message queue
+ //! @brief Dumps the current configuration into the message queue
void ping();
- //! Size of internal state
+ //! @brief Returns the size of the internal state
uint32_t stateSize();
- //! Load state
+ //! @brief Loads the current state from a buffer
void loadFromBuffer(uint8_t **buffer);
- //! Save state
+ //! @brief Save the current state into a buffer
void saveToBuffer(uint8_t **buffer);
- //! Dump internal state to console
+ //! @brief Prints debugging information
void dumpState();
- //! Returns true if cartride ROM is blended in at the specified location
+ //! @brief Returns true if cartride ROM is blended in at the specified location
bool romIsBlendedIn(uint16_t addr) { return blendedIn[addr >> 12]; }
- //! Peek fallthrough
+ //! @brief Peek fallthrough
uint8_t peek(uint16_t addr) { return rom[addr & 0x7FFF]; }
- //! Poke fallthrough
+ //! @brief Poke fallthrough
void poke(uint16_t addr, uint8_t value);
- //! Getter and setter
+ //! @brief Returns the cartridge type
CartridgeType getCartridgeType() { return (CartridgeType)type; }
- //! Count the number of chips
+ /*! @brief Counts the number of chips
+ * @return Value between 0 and 64
+ */
unsigned numberOfChips();
- //! Sums up the sizes of all chips in bytes
+ //! @brief Sums up the sizes of all chips in bytes
unsigned numberOfBytes();
+ //! @brief Returns the state of the game line
bool getGameLine() { return gameLine; }
+
+ //! @brief Sets the state of the game line
void setGameLine(bool value);
+ //! @brief Returns the state of the exrom line
bool getExromLine() { return exromLine; }
+
+ //! @brief Sets the state of the exrom line
void setExromLine(bool value);
- //! Make chip contents visible
+ //! @brief Blends in a cartridge chip into the ROM address space
void switchBank(unsigned nr);
- //! Returns true if a cartridge is attached to the expansion port
+ //! @brief Returns true if a cartridge is attached to the expansion port
inline bool getCartridgeAttached() { return type != CRT_NONE; }
- //! Attach a single cartridge chip
+ //! @brief Attaches a single cartridge chip
void attachChip(unsigned nr, Cartridge *c);
- //! Attach a cartridge to the expansion port (cartridges can contain multiple chips)
+ //! @brief Attaches a cartridge to the expansion port
bool attachCartridge(Cartridge *c);
- //! Remove a cartridge from the expansion port
+ //! @brief Removes a cartridge from the expansion port
void detachCartridge();
};
diff --git a/C64/FileArchive.cpp b/C64/FileArchive.cpp
index 39b5ff5..8a036eb 100755
--- a/C64/FileArchive.cpp
+++ b/C64/FileArchive.cpp
@@ -16,10 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "FileArchive.h"
FileArchive::FileArchive()
{
+ setDescription("FileArchive");
data = NULL;
dealloc();
}
@@ -46,16 +47,14 @@ FileArchive::isAcceptableFile(const char *filename)
FileArchive *
FileArchive::archiveFromRawFiledata(const char *filename)
{
- FileArchive *archive;
-
- fprintf(stderr, "Loading file archive from file...\n");
- archive = new FileArchive();
+ FileArchive *archive = new FileArchive();
+
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
delete archive;
- archive = NULL;
+ return NULL;
}
+ archive->debug(1, "File archive created from file %s.\n", filename);
return archive;
}
@@ -81,7 +80,6 @@ FileArchive::readFromBuffer(const uint8_t *buffer, unsigned length)
return false;
memcpy(data, buffer, length);
- fprintf(stderr, "length = %d %x\n", length, length);
size = length;
return true;
diff --git a/C64/FileArchive.h b/C64/FileArchive.h
index f4bcbb5..5039440 100755
--- a/C64/FileArchive.h
+++ b/C64/FileArchive.h
@@ -21,18 +21,21 @@
#include "Archive.h"
-/*! @class FileArchive
- * @brief The FileArchive class declares the programmatic interface for a file that does not match any of the standard formats.
- @discussion If a file does not match any of the standard formats, each byte is interpreted as raw data and is loaded at the standard memory location.
+/*! @class FileArchive
+ * @brief The FileArchive class declares the programmatic interface for a file that does not match any
+ * of the standard formats.
+ * @details If a file does not match any of the standard formats, each byte is interpreted as raw data and
+ * is loaded at the standard memory location.
*/
class FileArchive : public Archive {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive
uint8_t *data;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ @details An offset into the data array
+ */
int fp;
//! @brief File size
@@ -40,31 +43,31 @@ private:
public:
- //! @brief Standard constructor.
+ //! @brief Standard constructor
FileArchive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~FileArchive();
- //! @brief Returns true if filename points to a loadable file
+ //! @brief Returns true if filename points to a loadable file
static bool isAcceptableFile(const char *filename);
- //! @brief Creates an archive from a loadable file.
+ //! @brief Creates an archive from a loadable file.
static FileArchive *archiveFromRawFiledata(const char *filename);
+
//
// Virtual functions from Container class
//
void dealloc();
- // const char *getName();
ContainerType getType() { return FILE_CONTAINER; }
const char *getTypeAsString() { return "FILE"; }
bool fileIsValid(const char *filename);
bool readFromBuffer(const uint8_t *buffer, unsigned length);
- // unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
diff --git a/C64/G64Archive.cpp b/C64/G64Archive.cpp
index 85981f2..f4e49c1 100755
--- a/C64/G64Archive.cpp
+++ b/C64/G64Archive.cpp
@@ -20,6 +20,7 @@
G64Archive::G64Archive()
{
+ setDescription("G64Archive");
data = NULL;
dealloc();
}
@@ -52,16 +53,14 @@ G64Archive::isG64File(const char *filename)
G64Archive *
G64Archive::archiveFromG64File(const char *filename)
{
- G64Archive *archive;
-
- fprintf(stderr, "Loading G64 archive from G64 file...\n");
- archive = new G64Archive();
+ G64Archive *archive = new G64Archive();
+
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
delete archive;
- archive = NULL;
+ return NULL;
}
+ archive->debug(1, "G64 archive created from file %s.\n", filename);
return archive;
}
@@ -159,7 +158,6 @@ G64Archive::selectItem(int n)
fp = getStartOfItem(n);
fp += 2; // skip length information
fp_eof = fp + getSizeOfItem(n);
- fprintf(stderr, "fp = %d, f_eof = %d\n", fp, fp_eof);
}
int
diff --git a/C64/G64Archive.h b/C64/G64Archive.h
old mode 100755
new mode 100644
index 0c1b3d1..7c11485
--- a/C64/G64Archive.h
+++ b/C64/G64Archive.h
@@ -21,25 +21,27 @@
#include "Archive.h"
-/*! @class G64Archive
- * @brief The G64Archive class declares the programmatic interface for a file in G64 format.
+/*! @class G64Archive
+ * @brief The G64Archive class declares the programmatic interface for a file in G64 format.
*/
class G64Archive : public Archive {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive.
uint8_t *data;
//! @brief Size of G64 file
int size;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ * @details An offset into the data array.
+ */
int fp;
- /*! @brief End of file position
- @discussion This value equals the last valid offset plus 1 */
+ /*! @brief End of file position
+ * @details This value equals the last valid offset plus 1
+ */
int fp_eof;
public:
@@ -48,18 +50,19 @@ public:
//! @functiongroup Creating and destructing G64 archives
//
- //! @brief Standard constructor.
+ //! @brief Standard constructor
G64Archive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~G64Archive();
- //! @brief Returns true iff the specified file is a G64 file
+ //! @brief Returns true iff the specified file is a G64 file
static bool isG64File(const char *filename);
- /*! @brief Creates a G64 archive from a G64 file located on disk. */
+ //! @brief Creates a G64 archive from a G64 file located on disk.
static G64Archive *archiveFromG64File(const char *filename);
+
//
// Virtual functions from Container class
//
@@ -74,6 +77,7 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
//
diff --git a/C64/IEC.cpp b/C64/IEC.cpp
index 1cd2ce4..cc27d0e 100755
--- a/C64/IEC.cpp
+++ b/C64/IEC.cpp
@@ -20,8 +20,8 @@
IEC::IEC()
{
- name = "IEC";
- debug(2, " Creating IEC bus at address %p...\n", this);
+ setDescription("IEC");
+ debug(3, " Creating IEC bus at address %p...\n", this);
// Register snapshot items
SnapshotItem items[] = {
@@ -53,7 +53,7 @@ IEC::IEC()
IEC::~IEC()
{
- debug(2, " Releasing IEC bus...\n");
+ debug(3, " Releasing IEC bus...\n");
}
void
@@ -62,7 +62,7 @@ IEC::reset()
VirtualComponent::reset();
// Establish bindings
- drive = c64->floppy;
+ drive = &c64->floppy;
driveConnected = 1;
atnLine = 1;
diff --git a/C64/IEC.h b/C64/IEC.h
index 43081cf..520e4c9 100755
--- a/C64/IEC.h
+++ b/C64/IEC.h
@@ -1,23 +1,23 @@
-/*
- * (C) 2006 - 2015 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header IEC.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// Last review:
-
#ifndef _IEC_INC
#define _IEC_INC
diff --git a/C64/Instructions.cpp b/C64/Instructions.cpp
index d896319..15c322c 100755
--- a/C64/Instructions.cpp
+++ b/C64/Instructions.cpp
@@ -26,8 +26,6 @@ extern unsigned dirkcnt;
void
CPU::fetch() {
- bool doNMI = false, doIRQ = false;
-
PC_at_cycle_0 = PC;
// Check interrupt lines
@@ -39,14 +37,12 @@ CPU::fetch() {
debug(1, "NMI (source = %02X)\n", nmiLine);
nmiEdge = false;
next = &CPU::nmi_2;
- doNMI = true;
return;
} else if (irqLine && !IRQsAreBlocked() && IRQLineRaisedLongEnough()) {
if (tracingEnabled())
debug(1, "IRQ (source = %02X)\n", irqLine);
next = &CPU::irq_2;
- doIRQ = true;
return;
}
}
@@ -1501,10 +1497,10 @@ void CPU::BVC_relative()
{
READ_IMMEDIATE;
- if (chipModel == MOS6502 /* Drive CPU */ && !c64->floppy->getBitAccuracy()) {
+ if (chipModel == MOS6502 /* Drive CPU */ && !c64->floppy.getBitAccuracy()) {
// Special handling for the VC1541 CPU. Taken from Frodo
- if (!((c64->floppy->via2.io[12] & 0x0E) == 0x0E || getV())) {
+ if (!((c64->floppy.via2.io[12] & 0x0E) == 0x0E || getV())) {
next = &CPU::BVC_relative_2;
} else {
DONE;
@@ -1550,10 +1546,10 @@ void CPU::BVS_relative()
{
READ_IMMEDIATE;
- if (chipModel == MOS6502 /* Drive CPU */ && !c64->floppy->getBitAccuracy()) {
+ if (chipModel == MOS6502 /* Drive CPU */ && !c64->floppy.getBitAccuracy()) {
// Special handling for the VC1541 CPU. Taken from Frodo
- if ((c64->floppy->via2.io[12] & 0x0E) == 0x0E || getV()) {
+ if ((c64->floppy.via2.io[12] & 0x0E) == 0x0E || getV()) {
next = &CPU::BVS_relative_2;
} else {
DONE;
diff --git a/C64/Joystick.cpp b/C64/Joystick.cpp
old mode 100755
new mode 100644
index 07df957..08dfdb9
--- a/C64/Joystick.cpp
+++ b/C64/Joystick.cpp
@@ -21,16 +21,16 @@
Joystick::Joystick() {
- name = "Joystick";
- debug(2, " Creating joystick at address %p...\n", this);
+ setDescription("Joystick");
+ debug(3, " Creating joystick at address %p...\n", this);
// Register snapshot items
SnapshotItem items[] = {
- { &_buttonPressed, sizeof(_buttonPressed), CLEAR_ON_RESET },
- { &_axisX, sizeof(_axisX), CLEAR_ON_RESET },
- { &_axisY, sizeof(_axisY), CLEAR_ON_RESET },
- { NULL, 0, 0 }};
+ { &button, sizeof(button), 0 },
+ { &axisX, sizeof(axisX), 0 },
+ { &axisY, sizeof(axisY), 0 },
+ { NULL, 0, 0 }};
registerSnapshotItems(items, sizeof(items));
}
@@ -40,42 +40,20 @@ Joystick::~Joystick()
}
void
-Joystick::dumpState()
-{
- msg("Joystick port\n");
- msg("-------------\n");
- msg("Button: %s AxisX: %d AxisY: %d\n", _buttonPressed ? "YES" : "NO", _axisX, _axisY);
-}
-
-bool Joystick::GetButtonPressed()
-{
- return _buttonPressed;
-}
-
-JoystickAxisState Joystick::GetAxisX()
-{
- return _axisX;
-}
-
-JoystickAxisState Joystick::GetAxisY()
+Joystick::reset()
{
- return _axisY;
-}
+ VirtualComponent::reset();
-void Joystick::SetButtonPressed(bool pressed)
-{
- // fprintf(stderr,"%p %s", this, __PRETTY_FUNCTION__);
- _buttonPressed = pressed;
+ button = false;
+ axisX = JOYSTICK_RELEASED;
+ axisY = JOYSTICK_RELEASED;
}
-void Joystick::SetAxisX(JoystickAxisState state)
+void
+Joystick::dumpState()
{
- // fprintf(stderr,"%p %s", this, __PRETTY_FUNCTION__);
- _axisX = state;
+ msg("Joystick port\n");
+ msg("-------------\n");
+ msg("Button: %s AxisX: %d AxisY: %d\n", button ? "YES" : "NO", axisX, axisY);
}
-void Joystick::SetAxisY(JoystickAxisState state)
-{
- // fprintf(stderr,"%p %s", this, __PRETTY_FUNCTION__);
- _axisY = state;
-}
diff --git a/C64/Joystick.h b/C64/Joystick.h
old mode 100755
new mode 100644
index 27d9643..1274d9e
--- a/C64/Joystick.h
+++ b/C64/Joystick.h
@@ -1,22 +1,24 @@
-/*
- * Authors: Benjamin Klein
- * Dirk Hoffmann
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header Joystick.h
+ * @author Original code by Benjamin Klein, rewritten by Dirk W. Hoffmann
+ * @copyright All rights reserved.
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef JOYSTICK_H
#define JOYSTICK_H
@@ -26,49 +28,45 @@ enum JoystickDirection
JOYSTICK_DOWN,
JOYSTICK_LEFT,
JOYSTICK_RIGHT,
- JOYSTICK_FIRE
-};
-
-// DEPRECATED
-enum JoystickAxisState
-{
- JOYSTICK_AXIS_NONE = 0,
-
- JOYSTICK_AXIS_X_LEFT = -1,
- JOYSTICK_AXIS_X_RIGHT = 1,
- JOYSTICK_AXIS_X_NONE = 0,
-
- JOYSTICK_AXIS_Y_UP = -1,
- JOYSTICK_AXIS_Y_DOWN = 1,
- JOYSTICK_AXIS_Y_NONE = 0
+ JOYSTICK_FIRE,
+
+ JOYSTICK_RELEASED
};
class Joystick : public VirtualComponent {
private:
- bool _buttonPressed;
- JoystickAxisState _axisX;
- JoystickAxisState _axisY;
+ //! @brief True, if button is pressed
+ bool button;
+
+ //! @brief Horizontal joystick position
+ JoystickDirection axisX;
+
+ //! @brief Vertical joystick position
+ JoystickDirection axisY;
public:
- //! Constructor
+ //! @brief Constructor
Joystick();
- //! Destructor
+ //! @brief Destructor
~Joystick();
- //! Dump internal state to console
+ //! @brief Restores the initial state
+ void reset();
+
+ //! @brief Prints debugging information
void dumpState();
- bool GetButtonPressed();
- JoystickAxisState GetAxisX();
- JoystickAxisState GetAxisY();
+ inline bool getButton() { return button; }
+ inline JoystickDirection getAxisX() { return axisX; }
+ inline JoystickDirection getAxisY() { return axisY; }
- void SetButtonPressed(bool pressed);
- void SetAxisX(JoystickAxisState state);
- void SetAxisY(JoystickAxisState state);
+ inline void setButtonPressed(bool pressed) { button = pressed; }
+ inline void setAxisX(JoystickDirection state) { axisX = state; }
+ inline void setAxisY(JoystickDirection state) { axisY = state; }
};
diff --git a/C64/Keyboard.cpp b/C64/Keyboard.cpp
index 63062f0..761f1c0 100755
--- a/C64/Keyboard.cpp
+++ b/C64/Keyboard.cpp
@@ -22,8 +22,8 @@
Keyboard::Keyboard()
{
- name = "Keyboard";
- debug(2, "Creating keyboard at address %p...\n", this);
+ setDescription("Keyboard");
+ debug(3, "Creating keyboard at address %p...\n", this);
for (int i = 0; i < 256; i++) {
rowcolmap[i] = 0xFFFF;
@@ -101,6 +101,7 @@ Keyboard::Keyboard()
rowcolmap[C64KEY_CR] = 0x0002;
rowcolmap[C64KEY_CU] = 0x0007 | SHIFT_FLAG;
rowcolmap[C64KEY_CD] = 0x0007;
+ rowcolmap[C64KEY_RUNSTOP] = 0x0707;
// Register snapshot items
SnapshotItem items[] = {
@@ -156,12 +157,21 @@ uint8_t Keyboard::getRowValues(uint8_t columnMask)
void Keyboard::pressKey(uint8_t row, uint8_t col)
{
+ // printf("(%d %d)\n",row,col);
if (row < 8 && col < 8)
kbMatrix[row] &= 255 - (1 << col);
}
void Keyboard::pressKey(int c)
{
+ // debug("Pressing (%ld)\n", (long)c);
+
+ // Check for restore key
+ if (c == C64KEY_RESTORE) {
+ pressRestoreKey();
+ return;
+ }
+
// Check for commodore key flag
if (c & C64KEY_COMMODORE) {
pressCommodoreKey();
@@ -184,6 +194,11 @@ void Keyboard::pressKey(int c)
pressKey(row, col);
}
+void Keyboard::pressRestoreKey()
+{
+ c64->cpu.setNMILineReset();
+}
+
void Keyboard::releaseKey(uint8_t row, uint8_t col)
{
if (row < 8 && col < 8) {
@@ -193,6 +208,12 @@ void Keyboard::releaseKey(uint8_t row, uint8_t col)
void Keyboard::releaseKey(int c)
{
+ // Check for restore key
+ if (c == C64KEY_RESTORE) {
+ releaseRestoreKey();
+ return;
+ }
+
// Check for commodore key flag
if (c & C64KEY_COMMODORE) {
releaseCommodoreKey();
@@ -215,5 +236,10 @@ void Keyboard::releaseKey(int c)
releaseKey(row, col);
}
+void Keyboard::releaseRestoreKey()
+{
+ c64->cpu.clearNMILineReset();
+}
+
#undef SHIFT_FLAG
diff --git a/C64/Keyboard.h b/C64/Keyboard.h
index 4569c9f..071fe33 100755
--- a/C64/Keyboard.h
+++ b/C64/Keyboard.h
@@ -1,19 +1,21 @@
-/*
- * (C) 2006 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header Keyboard.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _KEYBOARD_INC
@@ -21,25 +23,26 @@
#include "VirtualComponent.h"
-//! The virtual keyboard of a C64
-/*! This class manages the keyboard matrix of the virtual C64.
- Keyboard management works as follows: When the GUI recognizes a pressed or release key,
- it calls one of the functions in this class to tell the virtual keyboard about the event.
- The called functions does nothing more than clearing or setting a bit in the keyboard matrix.
- Communication with the virtual computer is managed solely by the CIA chip. When a special
- CIA register is peeked, method \a getRowValues is called which finally brings the contents
- of the keyboard matrix into the virtual C64.
-*/
+/*! @class The virtual keyboard of a C64
+ * @details This class manages the keyboard matrix of the virtual C64.
+ * Keyboard management works as follows: When the GUI recognizes a pressed or
+ * release key, it calls one of the functions in this class to tell the virtual
+ * keyboard about the event. The called functions does nothing more than
+ * clearing or setting a bit in the keyboard matrix. Communication with the virtual
+ * computer is managed solely by the CIA chip. When a special CIA register is peeked,
+ * method getRowValues is called which finally brings the contents of the keyboard
+ * matrix into the virtual C64.
+ */
class Keyboard : public VirtualComponent {
- //! The C64 keyboard matrix
- /*! The C64 maintains a 8x8 matrix. Each key corresponds to a specific bit in the matrix and
- is uniquely determined by a row and a column value.
- */
+ /*! @brief The C64 keyboard matrix
+ * @details The C64 maintains a 8x8 matrix. Each key corresponds to a specific bit in the
+ * matrix and is uniquely determined by a row and a column value.
+ */
uint8_t kbMatrix[8];
public:
- //! Special keys
+ //! @brief Special keys
enum C64Key {
C64KEY_F1 = 0x80,
C64KEY_F2,
@@ -57,85 +60,98 @@ public:
C64KEY_CU,
C64KEY_CD,
C64KEY_ARROW,
+ C64KEY_RUNSTOP,
+ C64KEY_RESTORE,
C64KEY_COMMODORE = 0x0100 // flag that is combinable with all other keys
};
- //! Constructor
+ //! @brief Constructor
Keyboard();
- //! Destructor
+ //! @brief Destructor
~Keyboard();
- //! Reset
+ //! @brief Restores the initial state.
void reset();
- //! Dump internal state to console
+ //! @brief Prints debug information.
void dumpState();
- //! Inform keyboard about a pressed key (by keycode)
+ /*! @brief Presses a key.
+ * @details The key is identified by its native row and column index.
+ */
void pressKey(uint8_t row, uint8_t col);
- //! Inform keyboard about a pressed key (by character)
+ //! @brief Presses a key.
void pressKey(int c);
- //! Inform keyboard that the Shift key has been pressen
+ //! @brief Presses the shift hey.
void pressShiftKey() { pressKey(1,7); }
- //! Inform keyboard that the Commodore key has been pressen
+ //! @brief Presses the commodore key.
void pressCommodoreKey() { pressKey(7,5); }
- //! Inform keyboard that the Runstop key has been pressen
+ //! @brief Presses the runstop key.
void pressRunstopKey() { pressKey(7,7); }
- //! Inform keyboard that the Shift+Runstop key has been pressen
+ //! @brief Presses shift and runstop simultaniously.
void pressShiftRunstopKey() { pressShiftKey(); pressKey(7,7); }
- //! Inform keyboard that the Clear key has been pressen
+ //! @brief Presses the restore key.
+ void pressRestoreKey();
+
+ //! @brief Presses the clear key.
void pressClearKey() { pressShiftKey(); pressKey(6,3); }
- //! Inform keyboard that the Home key has been pressen
+ //! @brief Presses the home key.
void pressHomeKey() { pressKey(6,3); }
- //! Inform keyboard that the Insert key has been pressen
+ //! @brief Presses the insert key.
void pressInsertKey() { pressShiftKey(); pressKey(0,0); }
- //! Inform keyboard about a released key (by keycode)
+ /*! @brief Releases a pressed key.
+ * @details The key is identified by its native row and column index.
+ */
void releaseKey(uint8_t row, uint8_t col);
- //! Inform keyboard about a pressed key (by character)
+ //! @brief Releases a pressed key.
void releaseKey(int c);
- //! Inform keyboard that the Shift key has been released
+ //! @brief Releases the shift key.
void releaseShiftKey() { releaseKey(1,7); }
- //! Inform keyboard that the Commodore key has been released
+ //! @brief Releases the commodore key.
void releaseCommodoreKey() { releaseKey(7,5); }
- //! Inform keyboard that the Runstop key has been released
+ //! @brief Releases the runstop key.
void releaseRunstopKey() { releaseKey(7,7); }
- //! Inform keyboard that the Shift+Runstop key has been released
+ //! @brief Releases shift and runstop simultaniously.
void releaseShiftRunstopKey() { releaseKey(7,7); releaseShiftKey(); }
- //! Inform keyboard that the Clear key has been released
+ //! @brief Releases the restore key.
+ void releaseRestoreKey();
+
+ //! @brief Releases the clear key.
void releaseClearKey() { releaseKey(6,3); releaseShiftKey(); }
- //! Inform keyboard that the Home key has been released
+ //! @brief Releases the home key.
void releaseHomeKey() { releaseKey(6,3); }
- //! Inform keyboard that the Insert key has been released
+ //! @brief Releases the insert key.
void releaseInsertKey() { releaseKey(0,0); releaseShiftKey(); }
- //! Clear keyboard matrix
+ //! @brief Clears the keyboard matrix.
void releaseAll() { for (int i=0; i<8; i++) kbMatrix[i] = 0xff; }
- //! Read the keyboard matrix
- /*! /param columnMask determines the column bits to be read. */
+ /*! @brief Reads the keyboard matrix.
+ * @param columnMask the column bits to be read.
+ */
uint8_t getRowValues(uint8_t columnMask);
private:
- //! Mapping from characters to the C64 row/column format
+ //! @brief Mapping from ASCII characters to the C64 row/column format
uint16_t rowcolmap[256];
};
diff --git a/C64/Memory.cpp b/C64/Memory.cpp
index c3fb48e..dd1bedb 100755
--- a/C64/Memory.cpp
+++ b/C64/Memory.cpp
@@ -20,7 +20,7 @@
Memory::Memory()
{
- name = "MEM";
+ setDescription("MEM");
}
Memory::~Memory()
diff --git a/C64/Memory.h b/C64/Memory.h
index 3da955d..2ce9a8b 100755
--- a/C64/Memory.h
+++ b/C64/Memory.h
@@ -1,7 +1,9 @@
-/*
- * (C) 2006 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
+/*!
+ * @header Datasette.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -16,8 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// Last review: 25.7.06
-
#ifndef _MEMORY_INC
#define _MEMORY_INC
@@ -35,73 +35,53 @@ public:
// Constant definitions
// --------------------------------------------------------------------------------
- //! Memory type
- /*! This datatype defines a constant value for the different kinds of memory. */
+ /*! @brief Memory type
+ * @details This datatype defines a constant value for the different kinds of memory.
+ */
enum MemoryType {
MEM_RAM,
MEM_ROM,
MEM_IO
};
- //! Returns true, if the specified address is a valid memory address
- /*! Note, that the ROM and IO memory only partially cover the address space. */
+ //! @brief Returns true, if the specified address is a valid memory address.
virtual bool isValidAddr(uint16_t addr, MemoryType type) = 0;
- //! Reference to the connected virtual CPU
+ //! @brief Reference to the connected virtual CPU
CPU *cpu;
public:
- //! Constructor
+ //! @brief Constructor
Memory();
- //! Destructor
+ //! @brief Destructor
~Memory();
+
// --------------------------------------------------------------------------------
// Peek
// --------------------------------------------------------------------------------
protected:
- //! Read a BYTE from RAM.
- /*! The BYTE is always read from RAM, regardless of the value of the processor port register.
- \param addr Memory address
- \return RAM contents at address addr
- \see peek
- */
+ //! @brief Reads a byte from RAM.
virtual uint8_t peekRam(uint16_t addr) = 0;
- //! Read a BYTE from ROM.
- /*! The BYTE is always read from ROM, regardless of the value of the processor port register.
- \param addr Memory address
- \return ROM contents at address addr
- \see peek
- */
+ //! @brief Reads a byte from ROM.
virtual uint8_t peekRom(uint16_t addr) = 0;
- //! Read a BYTE from I/O space.
- /*! The BYTE is always read from I/O space, regardless of the value of the processor port register.
- \param addr Memory address
- \return I/O register contents at address addr
- \see peek
- */
+ //! @brief Reads a byte from I/O space.
virtual uint8_t peekIO(uint16_t addr) = 0;
public:
- //! Read a BYTE from the specified memory source.
- /*! Depending in the specified memory type, either \a peekRam, \a peekRom, or \a peekIO is called. */
+ //! @brief Reads a byte from the specified memory source.
uint8_t peekFrom(uint16_t addr, MemoryType source);
- //! Read a BYTE from memory.
- /*! This functions implements the native memory peek of the original C64 including all side effects.
- The functions automatically determines the correct memory source by the value of the processor
- port register. Afterwards, the value is read either from RAM, ROM, or the I/O address space and
- returned.
- \param addr Memory address
- \return RAM, ROM, or I/O memory contents at address addr
- */
+ /*! @brief Reads a byte from memory.
+ * @details This function implements the native memory peek of the original C64 including all side effects.
+ */
virtual uint8_t peek(uint16_t addr) = 0;
//! Wrapper around peek
@@ -138,28 +118,13 @@ public:
protected:
- //! Write a BYTE to RAM.
- /*! The BYTE is always written to RAM, regardless of the value of the processor port register.
- \param addr Memory address
- \param value Value to write
- \see poke
- */
- virtual void pokeRam(uint16_t addr, uint8_t value) = 0;
+ //! @brief Writes a byte into RAM
+ virtual void pokeRam(uint16_t addr, uint8_t value) = 0;
- //! Write a BYTE to ROM.
- /*! The BYTE is always written to ROM, regardless of the value of the processor port register.
- \param addr Memory address
- \param value Value to write
- \see poke
- */
+ //! @brief Writes a byte into ROM
virtual void pokeRom(uint16_t addr, uint8_t value) = 0;
- //! Write a BYTE to I/O space.
- /*! The BYTE is always written to I/O space, regardless of the value of the processor port register.
- \param addr Memory address
- \param value Value to write
- \see poke
- */
+ //! @brief Writes a byte into I/O space
virtual void pokeIO(uint16_t addr, uint8_t value) = 0;
public:
diff --git a/C64/Message.cpp b/C64/Message.cpp
old mode 100755
new mode 100644
index 6977d3e..e36b1cd
--- a/C64/Message.cpp
+++ b/C64/Message.cpp
@@ -11,6 +11,7 @@
MessageQueue::MessageQueue()
{
+ setDescription("MessageQueue");
r = w = 0;
pthread_mutex_init(&lock, NULL);
}
@@ -20,66 +21,68 @@ MessageQueue::~MessageQueue()
pthread_mutex_destroy(&lock);
}
-void MessageQueue::printMessage(Message *msg)
+void
+MessageQueue::printMessage(Message *msg)
{
switch (msg->id) {
case MSG_ROM_LOADED:
- fprintf(stderr, "MSG_ROM_LOADED");
+ debug(2, "MSG_ROM_LOADED");
break;
case MSG_ROM_MISSING:
- fprintf(stderr, "MSG_ROM_MISSING");
+ debug(2, "MSG_ROM_MISSING");
break;
case MSG_RUN:
- fprintf(stderr, "MSG_RUN");
+ debug(2, "MSG_RUN");
break;
case MSG_HALT:
- fprintf(stderr, "MSG_HALT");
+ debug(2, "MSG_HALT");
break;
case MSG_CPU:
- fprintf(stderr, "MSG_CPU");
+ debug(2, "MSG_CPU");
break;
case MSG_WARP:
- fprintf(stderr, "MSG_WARP");
+ debug(2, "MSG_WARP");
break;
case MSG_LOG:
- fprintf(stderr, "MSG_LOG");
+ debug(2, "MSG_LOG");
break;
case MSG_VC1541_ATTACHED:
- fprintf(stderr, "MSG_VC1541_ATTACHED");
+ debug(2, "MSG_VC1541_ATTACHED");
break;
case MSG_VC1541_DISK:
- fprintf(stderr, "MSG_VC_1541_DISK");
+ debug(2, "MSG_VC_1541_DISK");
break;
case MSG_VC1541_LED:
- fprintf(stderr, "MSG_VC1541_LED");
+ debug(2, "MSG_VC1541_LED");
break;
case MSG_VC1541_DATA:
- fprintf(stderr, "MSG_VC1541_DATA");
+ debug(2, "MSG_VC1541_DATA");
break;
case MSG_VC1541_MOTOR:
- fprintf(stderr, "MSG_VC1541_MOTOR");
+ debug(2, "MSG_VC1541_MOTOR");
break;
case MSG_CARTRIDGE:
- fprintf(stderr, "MSG_CARTRIDGE");
+ debug(2, "MSG_CARTRIDGE");
break;
case MSG_JOYSTICK_ATTACHED:
- fprintf(stderr, "MSG_JOYSTICK_ATTACHED");
+ debug(2, "MSG_JOYSTICK_ATTACHED");
break;
case MSG_JOYSTICK_REMOVED:
- fprintf(stderr, "MSG_JOYSTICK_REMOVED");
+ debug(2, "MSG_JOYSTICK_REMOVED");
break;
case MSG_PAL:
- fprintf(stderr, "MSG_PAL");
+ debug(2, "MSG_PAL");
break;
case MSG_NTSC:
- fprintf(stderr, "MSG_NTSC");
+ debug(2, "MSG_NTSC");
break;
default:
assert(0);
}
}
-Message *MessageQueue::getMessage()
+Message *
+MessageQueue::getMessage()
{
Message *result;
@@ -87,46 +90,38 @@ Message *MessageQueue::getMessage()
// Get data
if (r == w) {
- // Queue is empty!
- // fprintf(stderr, "MessageQueue::read: Queue is empty!\n");
- result = NULL;
+ result = NULL; // Queue is empty!
} else {
- // fprintf(stderr, "getMessage: "); printMessage(&queue[r]); fprintf(stderr,"\n");
- result = (r == w) ? NULL : &queue[r];
+ result = (r == w) ? NULL : &queue[r];
}
// Move read pointer to next location
- if (result) r = (r + 1) % QUEUE_SIZE;
+ if (result) r = (r + 1) % queue_size;
pthread_mutex_unlock(&lock);
return result;
}
-void MessageQueue::putMessage(int id, int i, void *p, const char *c)
+void
+MessageQueue::putMessage(int id, int i, void *p, const char *c)
{
- // If queue gets filled up, we don't accept any periodic messages any more...
- //if (queueGetsFilledUp() && id == MSG_DRAW) {
- // return;
- //}
-
- pthread_mutex_lock(&lock);
+ pthread_mutex_lock(&lock);
// Write data
queue[w].id = id;
queue[w].i = i;
- queue[w].p = p;
- if (c != NULL) strncpy(queue[w].c, c, 128);
-
- // fprintf(stderr, "putMessage: "); printMessage(&queue[w]); fprintf(stderr,"\n");
-
+ queue[w].p = p;
+
+ if (c != NULL)
+ strncpy(queue[w].c, c, 128);
+
// Move write pointer to next location
- w = (w + 1) % QUEUE_SIZE;
+ w = (w + 1) % queue_size;
if (w == r) {
- fprintf(stderr, "putMessag: Queue overflow!!! Message is lost!!!\n");
- // assert(0);
- r = (r + 1) % QUEUE_SIZE;
+ warn("Queue overflow!!! Message is lost!!!\n");
+ r = (r + 1) % queue_size;
}
pthread_mutex_unlock(&lock);
diff --git a/C64/Message.h b/C64/Message.h
old mode 100755
new mode 100644
index 8422680..f33c771
--- a/C64/Message.h
+++ b/C64/Message.h
@@ -1,28 +1,27 @@
-/*
- * (C) 2010 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header Message.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2010 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MESSAGE_INC
#define _MESSAGE_INC
-#include "basic.h"
-
-// Queue size
-#define QUEUE_SIZE 64
+#include "VC64Object.h"
// Message types
enum {
@@ -54,24 +53,6 @@ enum {
MSG_NTSC
};
-/*
-#define MSG_ROM_LOADED 1
-#define MSG_ROM_MISSING 2
-#define MSG_ROM_COMPLETE 3
-#define MSG_RUN 4
-#define MSG_HALT 5
-#define MSG_CPU 6
-#define MSG_WARP 7
-#define MSG_LOG 8
-#define MSG_VC1541_ATTACHED 9
-#define MSG_VC1541_DISK 10
-#define MSG_VC1541_LED 11
-#define MSG_VC1541_DATA 12
-#define MSG_VC1541_MOTOR 13
-#define MSG_CARTRIDGE 14
-#define MSG_JOYSTICK_ATTACHED 15
-#define MSG_JOYSTICK_REMOVED 16
-*/
typedef struct {
int id; // Message ID
@@ -80,43 +61,41 @@ typedef struct {
void *p; // Pointer value
} Message;
-class MessageQueue {
+class MessageQueue : public VC64Object {
private:
- Message queue[QUEUE_SIZE];
+
+ //! @brief Maximum number of queued messages
+ const static unsigned queue_size = 64;
+
+ //! @brief Message queue ring buffer
+ Message queue[queue_size];
- // Read pointer
+ //! @brief The ring buffers read pointer
int r;
- // Write pointer
+ //! @brief The ring buffers read pointer
int w;
- // Mutex for protecting the message queue
+ //! @brief Mutex for streamlining parallel read and write accesses
pthread_mutex_t lock;
- // Print message contents to stderr
+ //! @brief Prints a textual description of the message for debugging
void printMessage(Message *msg);
public:
- //! Constructor
+ //! @brief Constructor
MessageQueue();
- //! Destructor
+ //! @brief Destructor
~MessageQueue();
- // Returns number of messages in queue
- int numMessagesInQueue() { return (w >= r) ? w - r : (w + QUEUE_SIZE) - r; };
-
- // Returns true, iff message queue is full
- bool queueIsFull() { return numMessagesInQueue() == QUEUE_SIZE - 1; }
-
- // Returns true, iff message queue gets filled up
- bool queueGetsFilledUp() { return numMessagesInQueue() > (QUEUE_SIZE / 2); }
-
- // Get next message from queue
+ /*! @brief Returns the next pending message
+ * @return Returns NULL, if the queue is empty
+ */
Message *getMessage();
- // Put message into queue
+ //! @brief Adds new message to queue
void putMessage(int id, int i = 0, void *p = NULL, const char *c = NULL);
};
diff --git a/C64/NIBArchive.cpp b/C64/NIBArchive.cpp
index de6f215..a52b81a 100755
--- a/C64/NIBArchive.cpp
+++ b/C64/NIBArchive.cpp
@@ -16,12 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "Disk525.h"
#include "NIBArchive.h"
-#include "stdio.h"
NIBArchive::NIBArchive()
{
+ setDescription("NIBArchive");
data = NULL;
dealloc();
@@ -62,16 +61,19 @@ NIBArchive::isNIBFile(const char *filename)
NIBArchive *
NIBArchive::archiveFromNIBFile(const char *filename)
{
- NIBArchive *archive;
-
- fprintf(stderr, "Loading NIB archive from NIB file...\n");
- archive = new NIBArchive();
- if (!archive->readFromFile(filename) || !archive->scan()) {
- fprintf(stderr, "Failed to load archive\n");
+ NIBArchive *archive = new NIBArchive();
+
+ if (!archive->readFromFile(filename)) {
delete archive;
- archive = NULL;
+ return NULL;
}
-
+
+ if (!archive->scan()) {
+ delete archive;
+ return NULL;
+ }
+
+ archive->debug(1, "NIB archive created from file %s.\n", filename);
return archive;
}
@@ -88,7 +90,7 @@ NIBArchive::scan()
// Does item no 'item' exist in NIB file?
if (data[i] < 2 || data[i] > 83)
continue;
- Halftrack ht = data[i] + 1;
+ unsigned ht = data[i] + 1;
// Convert byte stream into a bit stream
unsigned j, startOfTrack = 0x100 + item * 0x2000;
@@ -120,7 +122,7 @@ NIBArchive::scan()
}
bool
-NIBArchive::scanTrack(Halftrack ht, uint8_t *bits, int *start, int *end, int *gap)
+NIBArchive::scanTrack(unsigned ht, uint8_t *bits, int *start, int *end, int *gap)
{
// Find loop
if (!scanForLoop(bits, sizeof(bits), start, end)) {
diff --git a/C64/NIBArchive.h b/C64/NIBArchive.h
index a73f3a7..237a991 100755
--- a/C64/NIBArchive.h
+++ b/C64/NIBArchive.h
@@ -29,26 +29,27 @@
*/
class NIBArchive : public Archive {
-private:
+private:
- /*! @brief Raw data of this archive */
+ //! @brief Raw data of this archive
uint8_t *data;
- /*! @brief Size of NIB file */
+ //! @brief Size of NIB file
int size;
- /*! @brief Decoded track data */
+ //! @brief Decoded track data
uint8_t halftrack[85][8 * MAX_TRACK_LENGTH];
- /*! @brief Decoded track length in bits
- * @discussion Equals 0 if halftrack is not contained in archive */
+ /*! @brief Decoded track length in bits
+ * @details Equals 0 if halftrack is not contained in archive */
int length[85];
- /*! @brief Selected halftrack to read from. */
+ //! @brief Selected halftrack to read from.
int selectedtrack;
- /*! @brief File pointer
- @discussion An offset into the halftrack array. */
+ /*! @brief File pointer
+ * @details An offset into the halftrack array.
+ */
int fp;
public:
@@ -57,55 +58,56 @@ public:
//! @functiongroup Creating and destructing NIB archives
//
- //! @brief Standard constructor.
+ //! @brief Standard constructor
NIBArchive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~NIBArchive();
- //! @brief Returns true iff the specified file is a NIB file
+ //! @brief Returns true iff the specified file is a NIB file.
static bool isNIBFile(const char *filename);
- //! @brief Creates a NIB archive from a NIB file located on disk.
+ //! @brief Creates a NIB archive from a NIB file located on disk.
static NIBArchive *archiveFromNIBFile(const char *filename);
- /*! @brief Creates a NIB archive from a virtual 5,25 floppy disk. */
- // static NIBArchive *archiveFromDisk(Disk525 *disk);
-
- /*! @brief Scans all tracks in archive
- * @returns true, if the scan was successful, false, if archive data is corrupt
- * @seealso scanTrack */
+ /*! @brief Scans all tracks in archive
+ * @return true, if the scan was successful, false, if archive data is corrupt
+ * @seealso scanTrack
+ */
bool scan();
- /*! @brief Scans a single track in archive
- * @discussion For eack track, the number of bits is determined and stored in array numBits.
- * Furthermore, the total number of tracks is stored in variable numTracks.
- * @param ht Halftrack number
- * @param bits The raw bit stream
- * @param start Offset the the first bit of the loop
- * @param end Offset the last bit belonging to the loop + 1
- * @param gap Offset to the gap position
- * @returns true, if the scan was successful, false, if archive data is corrupt */
+ /*! @brief Scans a single track in archive
+ * @details For eack track, the number of bits is determined and stored in array numBits.
+ * Furthermore, the total number of tracks is stored in variable numTracks.
+ * @param ht Halftrack number
+ * @param bits The raw bit stream
+ * @param start Offset the the first bit of the loop
+ * @param end Offset the last bit belonging to the loop + 1
+ * @param gap Offset to the gap position
+ * @return true, if the scan was successful, false, if archive data is corrupt
+ */
bool scanTrack(unsigned ht, uint8_t *bits, int *start, int *end, int *gap);
- /*! @brief Looks for a loop in the provided bit stream
- * @discussion A NIB file consists of 0x2000 bytes a nibbled data. As the nibbler cannot determine
- * when the drive head has completed a full rotation, the bit stream data overlaps.
- * This method searches for the overlap. If the repeating code sequence has been found,
- * the start and the end position are stored in startBit and endBit, respectively.
- * @param bits The raw bit stream.
- * @param length Length of the provided bit stream
- * @param start Offset the the first bit of the loop
- * @param end Offset the last bit belonging to the loop + 1
- * @return true if the repetition has been found. */
+ /*! @brief Looks for a loop in the provided bit stream
+ * @details A NIB file consists of 0x2000 bytes a nibbled data. As the nibbler cannot determine
+ * when the drive head has completed a full rotation, the bit stream data overlaps.
+ * This method searches for the overlap. If the repeating code sequence has been found,
+ * the start and the end position are stored in startBit and endBit, respectively.
+ * @param bits The raw bit stream.
+ * @param length Length of the provided bit stream
+ * @param start Offset the the first bit of the loop
+ * @param end Offset the last bit belonging to the loop + 1
+ * @return true if the repetition has been found.
+ */
bool scanForLoop(uint8_t *bits, int length, int *start, int *end);
- /*! @brief Looks for the longest area between two SYNC marks
- * @discussion The computed offset is used to properly align the tracks next to each other.
- * @param bits The raw bit stream as stored in the NIB file.
- * @param length Length of the provided bit stream
- * @param gap Offset to the gap position
- * @return true if a gap has been found, false otherwise. */
+ /*! @brief Looks for the longest area between two SYNC marks
+ * @details The computed offset is used to properly align the tracks next to each other.
+ * @param bits The raw bit stream as stored in the NIB file.
+ * @param length Length of the provided bit stream
+ * @param gap Offset to the gap position
+ * @return true if a gap has been found, false otherwise.
+ */
bool scanForGap(uint8_t *bits, int length, int *gap);
@@ -123,12 +125,12 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
//
int getNumberOfItems();
- // int getStartOfItem(int n);
int getSizeOfItem(int n);
const char *getNameOfItem(int n);
diff --git a/C64/OldSID.cpp b/C64/OldSID.cpp
index 771e69a..8c6d3c8 100755
--- a/C64/OldSID.cpp
+++ b/C64/OldSID.cpp
@@ -49,9 +49,8 @@ const float OldSID::volumeLevelTable[16] = { 0.0f, 0.07f, 0.13f, 0.20f, 0.27f, 0
OldSID::OldSID()
{
- name = "SID";
-
- debug(2, " Creating SID at address %p...\n", this);
+ setDescription("SID");
+ debug(3, " Creating SID at address %p...\n", this);
// link voices together
voice[0].mod_by = &voice[2];
diff --git a/C64/P00Archive.cpp b/C64/P00Archive.cpp
index 25d9b0e..61c9c74 100755
--- a/C64/P00Archive.cpp
+++ b/C64/P00Archive.cpp
@@ -16,10 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "P00Archive.h"
P00Archive::P00Archive()
{
+ setDescription("P00Archive");
data = NULL;
dealloc();
}
@@ -48,40 +49,30 @@ P00Archive::isP00File(const char *filename)
P00Archive *
P00Archive::archiveFromP00File(const char *filename)
{
- P00Archive *archive;
-
- fprintf(stderr, "Loading P00 archive from P00 file...\n");
- archive = new P00Archive();
+ P00Archive *archive = new P00Archive();
+
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
delete archive;
- archive = NULL;
+ return NULL;
}
- fprintf(stderr, "%s archive created with %d bytes (size of item 0 = %d).\n",
- archive->getTypeAsString(), archive->size, archive->getSizeOfItem(0));
+ archive->debug(1, "P00 archive created from file %s.\n", filename);
return archive;
}
P00Archive *
P00Archive::archiveFromArchive(Archive *otherArchive)
{
- P00Archive *archive;
-
if (otherArchive == NULL || otherArchive->getNumberOfItems() == 0)
return NULL;
- fprintf(stderr, "Creating P00 archive from %s archive...\n", otherArchive->getTypeAsString());
-
- if ((archive = new P00Archive()) == NULL) {
- fprintf(stderr, "Failed to create archive\n");
- return NULL;
- }
+ P00Archive *archive = new P00Archive();
+ archive->debug(1, "Creating P00 archive from %s archive...\n", otherArchive->getTypeAsString());
// Determine container size and allocate memory
archive->size = 8 + 17 + 1 + 2 + otherArchive->getSizeOfItem(0);
if ((archive->data = (uint8_t *)malloc(archive->size)) == NULL) {
- fprintf(stderr, "Failed to allocate %d bytes of memory\n", archive->size);
+ archive->warn("Failed to allocate %d bytes of memory\n", archive->size);
delete archive;
return NULL;
}
@@ -110,8 +101,6 @@ P00Archive::archiveFromArchive(Archive *otherArchive)
*ptr++ = (uint8_t)byte;
}
- fprintf(stderr, "%s archive created with %d bytes (size of item 0 = %d).\n",
- archive->getTypeAsString(), archive->size, archive->getSizeOfItem(0));
return archive;
}
diff --git a/C64/P00Archive.h b/C64/P00Archive.h
index a8be52f..e33c7d9 100755
--- a/C64/P00Archive.h
+++ b/C64/P00Archive.h
@@ -21,39 +21,41 @@
#include "Archive.h"
-/*! @class D64Archive
- * @brief The D64Archive class declares the programmatic interface for a file in P00 format.
+/*! @class D64Archive
+ * @brief The D64Archive class declares the programmatic interface for a file in P00 format.
*/
class P00Archive : public Archive {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive.
uint8_t *data;
- //! @brief File size
+ //! @brief File size
int size;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ * @details An offset into the data array.
+ */
int fp;
public:
- //! @brief Standard constructor.
+ //! @brief Standard constructor.
P00Archive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor.
~P00Archive();
- //! @brief Returns true iff the specified file is a P00 file
+ //! @brief Returns true iff the specified file is a P00 file
static bool isP00File(const char *filename);
- //! @brief Creates a P00 archive from a P00 file.
+ //! @brief Creates a P00 archive from a P00 file.
static P00Archive *archiveFromP00File(const char *filename);
- /*! @brief Creates a P00 archive from another archive.
- @result A P00 archive that contains the first directory item of the other archive. */
+ /*! @brief Creates a P00 archive from another archive.
+ * @result A P00 archive that contains the first directory item of the other archive.
+ */
static P00Archive *archiveFromArchive(Archive *otherArchive);
@@ -71,6 +73,7 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
//
diff --git a/C64/PRGArchive.cpp b/C64/PRGArchive.cpp
index da45ff9..4b80cae 100755
--- a/C64/PRGArchive.cpp
+++ b/C64/PRGArchive.cpp
@@ -16,10 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "PRGArchive.h"
PRGArchive::PRGArchive()
{
+ setDescription("PRGArchive");
data = NULL;
dealloc();
}
@@ -46,38 +47,30 @@ PRGArchive::isPRGFile(const char *filename)
PRGArchive *
PRGArchive::archiveFromPRGFile(const char *filename)
{
- PRGArchive *archive;
-
- fprintf(stderr, "Loading PRG archive from PRG file...\n");
- archive = new PRGArchive();
- if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
+ PRGArchive *archive = new PRGArchive();
+
+ if (!archive->readFromFile(filename)) {
delete archive;
- archive = NULL;
+ return NULL;
}
+ archive->debug(1, "PRG archive created from file %s.\n", filename);
return archive;
}
PRGArchive *
PRGArchive::archiveFromArchive(Archive *otherArchive)
{
- PRGArchive *archive;
-
if (otherArchive == NULL || otherArchive->getNumberOfItems() == 0)
return NULL;
- fprintf(stderr, "Creating PRG archive from %s archive...\n", otherArchive->getTypeAsString());
-
- if ((archive = new PRGArchive()) == NULL) {
- fprintf(stderr, "Failed to create archive\n");
- return NULL;
- }
+ PRGArchive *archive = new PRGArchive();
+ archive->debug(1, "Creating PRG archive from %s archive...\n", otherArchive->getTypeAsString());
// Determine container size and allocate memory
archive->size = 2 + otherArchive->getSizeOfItem(0);
if ((archive->data = (uint8_t *)malloc(archive->size)) == NULL) {
- fprintf(stderr, "Failed to allocate %d bytes of memory\n", archive->size);
+ archive->warn("Failed to allocate %d bytes of memory\n", archive->size);
delete archive;
return NULL;
}
@@ -94,8 +87,6 @@ PRGArchive::archiveFromArchive(Archive *otherArchive)
*ptr++ = (uint8_t)byte;
}
- fprintf(stderr, "%s archive created with %d bytes (size of item 0 = %d).\n",
- archive->getTypeAsString(), archive->size, archive->getSizeOfItem(0));
return archive;
}
diff --git a/C64/PRGArchive.h b/C64/PRGArchive.h
index b56b886..9c7367b 100755
--- a/C64/PRGArchive.h
+++ b/C64/PRGArchive.h
@@ -21,21 +21,22 @@
#include "Archive.h"
-/*! @class D64Archive
- * @brief The D64Archive class declares the programmatic interface for a file in PRG format.
+/*! @class D64Archive
+ * @brief The D64Archive class declares the programmatic interface for a file in PRG format.
*/
class PRGArchive : public Archive {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive.
uint8_t *data;
- //! @brief File size
+ //! @brief File size
int size;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ * @details An offset into the data array.
+ */
int fp;
public:
@@ -44,20 +45,21 @@ public:
//! @functiongroup Creating and destructing PRG archives
//
- //! @brief Standard constructor.
+ //! @brief Standard constructor
PRGArchive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~PRGArchive();
- //! @brief Returns true iff the specified file is a PRG file
+ //! @brief Returns true iff the specified file is a PRG file.
static bool isPRGFile(const char *filename);
- //! @brief Creates a PRG archive from a PRG file.
+ //! @brief Creates a PRG archive from a PRG file.
static PRGArchive *archiveFromPRGFile(const char *filename);
- /*! @brief Creates a PRG archive from another archive.
- @result A PRG archive that contains the first directory item of the other archive. */
+ /*! @brief Creates a PRG archive from another archive.
+ * @result A PRG archive that contains the first directory item of the other archive.
+ */
static PRGArchive *archiveFromArchive(Archive *otherArchive);
@@ -74,6 +76,7 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
//
diff --git a/C64/PixelEngine.cpp b/C64/PixelEngine.cpp
old mode 100755
new mode 100644
index 5dc5b13..2139c78
--- a/C64/PixelEngine.cpp
+++ b/C64/PixelEngine.cpp
@@ -27,9 +27,9 @@ extern unsigned dirkcnt;
PixelEngine::PixelEngine() // C64 *c64)
{
- name = "PixelEngine";
+ setDescription("PixelEngine");
- debug(2, " Creating PixelEngine at address %p...\n", this);
+ debug(3, " Creating PixelEngine at address %p...\n", this);
currentScreenBuffer = screenBuffer1[0];
pixelBuffer = currentScreenBuffer;
@@ -64,7 +64,7 @@ PixelEngine::PixelEngine() // C64 *c64)
PixelEngine::~PixelEngine()
{
- debug(2, " Releasing PixelEngine...\n");
+ debug(3, " Releasing PixelEngine...\n");
}
void
@@ -73,7 +73,7 @@ PixelEngine::reset()
VirtualComponent::reset();
// Establish bindings
- vic = c64->vic;
+ vic = &c64->vic;
memset(&sr, 0, sizeof(sr));
memset(&sprite_sr, 0, sizeof(sprite_sr));
diff --git a/C64/PixelEngine.h b/C64/PixelEngine.h
old mode 100755
new mode 100644
index a7cd204..49bf253
--- a/C64/PixelEngine.h
+++ b/C64/PixelEngine.h
@@ -1,19 +1,21 @@
-/*
- * (C) 2015 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header PixelEngine.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2015 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PIXELENGINGE_INC
@@ -24,7 +26,6 @@
// Forward declarations
class VIC;
-class C64;
// Depth of different drawing layers
#define BORDER_LAYER_DEPTH 0x10 /* in front of everything */
@@ -46,31 +47,33 @@ enum DisplayMode {
INVALID_MULTICOLOR_BITMAP = 0x70
};
-//! PixelEngine
-/*! This component is part of the virtual VICII chip and encapulates all functionality that is related to the
- synthesis of pixels. Its main entry point are prepareForCycle() and draw() which are called in every
- VIC cycle inside the viewable range.
-*/
+//! @class PixelEngine
+/*! @details This component is part of the virtual VICII chip and encapulates all functionality
+ * that is related to the synthesis of pixels. Its main entry point are prepareForCycle()
+ * and draw() which are called in every VIC cycle inside the viewable range.
+ */
class PixelEngine : public VirtualComponent {
friend class VIC;
public:
- //! Reference to the connected video interface controller (VIC)
+ //! @brief Reference to the connected video interface controller (VIC)
VIC *vic;
- //! Constructor
+ //! @brief Constructor
PixelEngine();
- //! Destructor
+ //! @brief Destructor
~PixelEngine();
- //! Restore initial state
+ //! @brief Restores the initial state
void reset();
- //! Initialize both screenBuffers
- /*! This function is used for debugging. It write some recognizable pattern into both buffers */
+ //! @brief Initializes both screenBuffers
+ /*! @details This function is needed for debugging, only. It write some recognizable pattern
+ * into both buffers
+ */
void resetScreenBuffers();
@@ -78,7 +81,7 @@ public:
// Constant definitions
// -----------------------------------------------------------------------------------------------
- //! VIC colors
+ //! @brief VIC colors
enum Color {
BLACK = 0x00,
WHITE = 0x01,
@@ -105,7 +108,7 @@ public:
private:
- //! All sixteen C64 colors in RGBA format
+ //! @brief All sixteen C64 colors in RGBA format
uint32_t colors[16] = {
LO_LO_HI_HI(0x10, 0x10, 0x10, 0xFF),
LO_LO_HI_HI(0xff, 0xff, 0xff, 0xFF),
@@ -125,46 +128,59 @@ private:
LO_LO_HI_HI(0xc0, 0xc0, 0xc0, 0xFF)
};
- //! First screen buffer
- /*! The VIC chip writes it output into this buffer. The contents of the array is later copied into to
- texture RAM of your graphic card by the drawRect method in the GPU related code. */
+ /*! @brief First screen buffer
+ * @details The VIC chip writes it output into this buffer. The contents of the array is
+ * later copied into to texture RAM of your graphic card by the drawRect method
+ * in the GPU related code.
+ */
int screenBuffer1[PAL_RASTERLINES][NTSC_PIXELS];
- //! Second screen buffer
- /*! The VIC chip uses double buffering. Once a frame is drawn, the VIC chip writes the next frame to the
- second buffer. */
+ /*! @brief Second screen buffer
+ * @details The VIC chip uses double buffering. Once a frame is drawn, the VIC chip writes
+ * the next frame to the second buffer.
+ */
int screenBuffer2[PAL_RASTERLINES][NTSC_PIXELS];
- //! Target screen buffer for all rendering methods
- /*! The variable points either to screenBuffer1 or screenBuffer2 */
+ /*! @brief Target screen buffer for all rendering methods
+ * @details The variable points either to screenBuffer1 or screenBuffer2
+ */
int *currentScreenBuffer;
- //! Pointer to the beginning of the current rasterline
- /*! This pointer is used by all rendering methods to write pixels. It always points to the beginning of a
- rasterline, either in screenBuffer1 or screenBuffer2. It is reset at the beginning of each frame and
- incremented at the beginning of each rasterline. */
+ /*! @brief Pointer to the beginning of the current rasterline
+ * @details This pointer is used by all rendering methods to write pixels. It always points
+ * to the beginning of a rasterline, either in screenBuffer1 or screenBuffer2.
+ * It is reset at the beginning of each frame and incremented at the beginning of
+ * each rasterline.
+ */
int *pixelBuffer;
- //! Z buffer
- /*! Virtual VICII uses depth buffering to determine pixel priority. In the various render routines, a pixel is
- only written to the screen buffer, if it is closer to the view point. The depth of the closest pixel is kept
- in the z buffer. The lower the value of the z buffer, the closer it is to the viewer. */
+ /*! @brief Z buffer
+ * @details Virtual VICII uses depth buffering to determine pixel priority. In the various
+ * render routines, a pixel is only written to the screen buffer, if it is closer
+ * to the view point. The depth of the closest pixel is kept in the z buffer.
+ * The lower the value of the z buffer, the closer it is to the viewer.
+ */
int zBuffer[8];
- //! Indicates the source of a drawn pixel
- /*! Whenever a foreground pixel or sprite pixel is drawn, a distinct bit in the pixelSource array is set.
- * The information is utilized to detect sprite-sprite and sprite-background collisions. */
+ /*! @brief Indicates the source of a drawn pixel
+ * @details Whenever a foreground pixel or sprite pixel is drawn, a distinct bit in the
+ * pixelSource array is set. The information is utilized to detect sprite-sprite
+ * and sprite-background collisions.
+ */
int pixelSource[8];
- //! Offset into pixelBuffer
- /*! Variable points to the first pixel of the currently drawn 8 pixel chunk */
+ /*! @brief Offset into pixelBuffer
+ * @details Variable points to the first pixel of the currently drawn 8 pixel chunk
+ */
short bufferoffset;
public:
- //! Get screen buffer that is currently stable
- /*! This method is called by the GPU code at the beginning of each frame. */
- inline void *screenBuffer() { return (currentScreenBuffer == screenBuffer1[0]) ? screenBuffer2[0] : screenBuffer1[0]; }
+ /*! @brief Get screen buffer that is currently stable
+ * @details This method is called by the GPU code at the beginning of each frame.
+ */
+ inline void *screenBuffer() {
+ return (currentScreenBuffer == screenBuffer1[0]) ? screenBuffer2[0] : screenBuffer1[0]; }
// -----------------------------------------------------------------------------------------------
@@ -173,9 +189,10 @@ public:
private:
- //! Indicates wether we are in a visible display column or not
- /*! The visible columns comprise canvas columns and border columns. The first visible column is
- * drawn in cycle 14 (first left border column) and the last in cycle ?? (fourth right border column).
+ /*! @brief Indicates wether we are in a visible display column or not
+ * @details The visible columns comprise canvas columns and border columns. The first visible
+ * column is drawn in cycle 14 (first left border column) and the last in cycle ??
+ * (fourth right border column).
*/
bool visibleColumn;
@@ -186,16 +203,16 @@ private:
public:
- //! Prepare for new frame
+ //! @brief Prepares for a new frame
void beginFrame();
- //! Prepare for new rasterline
+ //! @brief Prepares for a new rasterline
void beginRasterline();
- //! Finish up rasterline
+ //! @brief Finishes up a rasterline
void endRasterline();
- //! Finish up frame
+ //! @brief Finishes up a frame
void endFrame();
@@ -203,74 +220,91 @@ public:
// VIC state latching
// -----------------------------------------------------------------------------------------------
- //! Register pipe
+ //! @brief Register pipe
PixelEnginePipe pipe;
- //! Border color pipe
+ //! @brief Border color pipe
BorderColorPipe bpipe;
- //! Canvas color pipe
+ //! @brief Canvas color pipe
CanvasColorPipe cpipe;
- //! Sprite color pipe
+ //! @brief Sprite color pipe
SpriteColorPipe spipe;
- //! Latched VIC state
- /*! To draw pixels right, it is important to gather the necessary information at the right time.
- Some VIC and memory registers need to be looked up one cycle before drawing, others need
- to be looked up at the same cycle or even in the middle of drawing an 8 pixel chunk. To make
- this process transparent, all gatheres information is stored in this structure. */
+ /*! @brief Latched VIC state
+ * @details To draw pixels right, it is important to gather the necessary information at the right time.
+ * Some VIC and memory registers need to be looked up one cycle before drawing, others need
+ * to be looked up at the same cycle or even in the middle of drawing an 8 pixel chunk. To make
+ * this process transparent, all gatheres information is stored in this structure.
+ */
- // TODO: Introduce PixelEngineColorPipe
+ // TODO:
+ // Rename dc to spriteOnOffPipe
+ // Rename spriteOnOffPipe to spriteOnOff1
+ // Rename spriteOnOff to spriteOnOff2
struct {
uint8_t spriteOnOffPipe;
uint8_t spriteOnOff;
} dc;
- //! Current display mode
- /*! The display mode is determined by three bits (one in register 0xD016 and two in register 0xD011).
- * These bits don't show up simultaniously. They are latched in method drawCanvas() after
- * after certain pixels have been draw. */
+ /*! @brief Current display mode
+ * @details The display mode is determined by three bits (one in register 0xD016 and two in register 0xD011).
+ * These bits don't show up simultanously. They are latched in method drawCanvas() after
+ * after certain pixels have been draw.
+ */
uint8_t displayMode;
- //! @brief Latches the sprite enable bits
- /*! @discussion This method is called in drawSprites() */
+ /*! @brief Latches the sprite enable bits
+ * @details This method is called in drawSprites()
+ */
void updateSpriteOnOff();
+
// -----------------------------------------------------------------------------------------------
// Shift register logic for canvas pixels (handled in drawCanvasPixel)
// -----------------------------------------------------------------------------------------------
- //! Shift register
- /*! To synthesize pixels, VICII uses a 8 bit shift register */
+ //! @brief Main shift register
+ /*! @details An eight bit shift register used to synthesize the canvas pixels.
+ */
struct {
- //! Shift register data
+ //! @brief Shift register data
uint8_t data;
- //! Indicates whether the shift register can load data
- /*! If true, the register is loaded when the current x scroll offset matches the current pixel number. */
+ /*! @brief Indicates whether the shift register can load data
+ * @details If true, the register is loaded when the current x scroll offset matches the
+ * current pixel number.
+ */
bool canLoad;
- //! Multi-color synchronization flipflop
- /*! Whenever the shift register is loaded, the synchronization flipflop is also set.
- It is toggled with each pixel and used to synchronize the synthesis of multi-color pixels. */
+ /*! @brief Multi-color synchronization flipflop
+ * @details Whenever the shift register is loaded, the synchronization flipflop is also set.
+ * It is toggled with each pixel and used to synchronize the synthesis of
+ * multi-color pixels. */
bool mc_flop;
- //! Latched character info
- /*! Whenever the shift register is loaded, the current character value (which was once read during
- a gAccess) is latched. This value is used until the shift register loads again. */
+ /*! @brief Latched character info
+ * @details Whenever the shift register is loaded, the current character value (which was
+ * once read during a gAccess) is latched. This value is used until the shift
+ * register loads again.
+ */
uint8_t latchedCharacter;
- //! Latched color info
- /*! Whenever the shift register is loaded, the current color value (which was once read during
- a gAccess) is latched. This value is used until the shift register loads again. */
+ /*! @brief Latched color info
+ * @details Whenever the shift register is loaded, the current color value (which was
+ * once read during a gAccess) is latched. This value is used until the shift
+ * register loads again.
+ */
uint8_t latchedColor;
- //! Color bits
- /*! Every second pixel (as synchronized with mc_flop), the multi-color bits are remembered. */
+ /*! @brief Color bits
+ * @details Every second pixel (as synchronized with mc_flop), the multi-color bits are
+ * remembered.
+ */
uint8_t colorbits;
} sr;
@@ -280,61 +314,52 @@ public:
// Shift register logic for sprite pixels (handled in drawSpritePixel)
// -----------------------------------------------------------------------------------------------
- //! @brief Sprite shift registers
- /*! @discussion The VIC chip has a 24 bit (3 byte) shift register for each sprite. It stores the sprite
- * for one rasterline. If a sprite is a display candidate in the current rasterline, its
- * shift register is activated when the raster X coordinate matches the sprites X coordinate.
- * The comparison is done in method drawSprite().
- * Once a shift register is activated, it remains activated until the beginning of the next
- * rasterline. However, after an activated shift register has dumped out its 24 pixels, it
- * can't draw anything else than transparent pixels (which is the same as not to draw anything).
- * An exception is during DMA cycles. When a shift register is activated during such a cycle,
- * it freezes a short period of time in which it repeats the previous drawn pixel.
+ /*! @brief Sprite shift registers
+ * @details The VIC chip has a 24 bit (3 byte) shift register for each sprite. It stores the sprite
+ * for one rasterline. If a sprite is a display candidate in the current rasterline, its
+ * shift register is activated when the raster X coordinate matches the sprites X coordinate.
+ * The comparison is done in method drawSprite().
+ * Once a shift register is activated, it remains activated until the beginning of the next
+ * rasterline. However, after an activated shift register has dumped out its 24 pixels, it
+ * can't draw anything else than transparent pixels (which is the same as not to draw anything).
+ * An exception is during DMA cycles. When a shift register is activated during such a cycle,
+ * it freezes a short period of time in which it repeats the previous drawn pixel.
*/
struct {
- //! Shift register data (24 bit)
+ //! @brief Shift register data (24 bit)
uint32_t data;
- //! The shift register data is read in three chunks
+ //! @brief The shift register data is read in three chunks
uint8_t chunk1, chunk2, chunk3;
- //! Remaining bits to be pumped out
- /*! At the beginning of each rasterline, this value is initialized with -1 and set to
- 26 when the horizontal trigger condition is met (sprite X trigger coord reaches xCounter).
- When all bits are drawn, this value reaches 0. */
+ /*! @brief Remaining bits to be pumped out
+ * @details At the beginning of each rasterline, this value is initialized with -1 and set to
+ * 26 when the horizontal trigger condition is met (sprite X trigger coord reaches xCounter).
+ * When all bits are drawn, this value reaches 0.
+ */
int remaining_bits;
- //! Multi-color synchronization flipflop
- /*! Whenever the shift register is loaded, the synchronization flipflop is also set.
- It is toggled with each pixel and used to synchronize the synthesis of multi-color pixels. */
+ /*! @brief Multi-color synchronization flipflop
+ * @details Whenever the shift register is loaded, the synchronization flipflop is also set.
+ * It is toggled with each pixel and used to synchronize the synthesis of multi-color pixels.
+ */
bool mc_flop;
- //! xExpansion synchronization flipflop
- /*! */
+ //! @brief x expansion synchronization flipflop
bool exp_flop;
- //! @brief Remembers if the currently processed pixel is a multi-color or single-color pixel
- /*! @deprecated This has been made a local variable (unsure if this is right?!) */
- // bool mcol;
-
- //! @brief Color bits of the currently processed pixel
- /*! @discussion In single-color mode, these bits are updats every cycle
- * In multi-color mode, these bits are updats every second cycle (synchronized with mc_flop) */
+ /*! @brief Color bits of the currently processed pixel
+ * @details In single-color mode, these bits are updats every cycle
+ * In multi-color mode, these bits are updats every second cycle (synchronized with mc_flop)
+ */
uint8_t col_bits;
- //! @brief Multi-color bits of the currently processed pixel
- /*! @discussion The multi-color bits are updats every second cycle (synchronized with mc_flop)
- * @deprecated */
- // uint8_t mcol_bits;
-
- //! @brief Single-color bit of the currently processed pixel
- /*! @discussion The single-color bit is updated every cycle
- * @deprecated */
- // uint8_t scol_bit;
-
} sprite_sr[8];
+ /*! @brief Loads the sprite shift register.
+ * @details The shift register is loaded with the three data bytes fetched in the previous sAccesses.
+ */
inline void loadShiftRegister(unsigned nr) {
sprite_sr[nr].data = (sprite_sr[nr].chunk1 << 16) | (sprite_sr[nr].chunk2 << 8) | sprite_sr[nr].chunk3;
}
@@ -345,73 +370,82 @@ public:
public:
- //! Synthesize 8 pixels according the the current drawing context.
- /*! This is the main entry point and is invoked in each VIC drawing cycle, except cycle 17 and
- cycle 55 which are handles seperately for speedup purposes.
- To get the correct output, preparePixelEngineForCycle() must be called one cycle before. */
+ /*! @brief Synthesize 8 pixels according the the current drawing context.
+ * @details This is the main entry point and is invoked in each VIC drawing cycle, except cycle 17 and
+ * cycle 55 which are handles seperately for speedup purposes.
+ * To get the correct output, preparePixelEngineForCycle() must be called one cycle before.
+ */
void draw();
- //! Special draw routine for cycle 14
- // void draw14();
-
- //! Special draw routine for cycle 17
+ //! @brief Special draw routine for cycle 17
void draw17();
- //! Special draw routine for cycle 55
+ //! @brief Special draw routine for cycle 55
void draw55();
- //! Draw routine for cycles outside the visible screen region.
- /*! The sprite sequencer needs to be run outside the visible area, although no
- pixels will be drawn (drawing is omitted by having visibleColumn set to false */
+ /*! @brief Draw routine for cycles outside the visible screen region.
+ * @details The sprite sequencer needs to be run outside the visible area, although no
+ * pixels will be drawn (drawing is omitted by having visibleColumn set to false
+ */
void drawOutsideBorder();
private:
- //! Draws 8 border pixels
- /*! Invoked inside draw() */
+ /*! @brief Draws 8 border pixels
+ * @details Invoked inside draw()
+ */
void drawBorder();
- //! Draws 8 border pixels
- /*! Invoked inside draw17() */
+ /*! @brief Draws 8 border pixels
+ * @details Invoked inside draw17()
+ */
void drawBorder17();
- //! Draws 8 border pixels
- /*! Invoked inside draw55() */
+ /*! @brief Draws 8 border pixels
+ * @details Invoked inside draw55()
+ */
void drawBorder55();
- //! Draws 8 canvas pixels
- /*! Invoked inside draw() */
+ /*! @brief Draws 8 canvas pixels
+ * @details Invoked inside draw()
+ */
void drawCanvas();
- //! Draws a single canvas pixel
- /*! pixelnr is the pixel number and must be in the range 0 to 7 */
+ /*! @brief Draws a single canvas pixel
+ * @param pixelnr is the pixel number and must be in the range 0 to 7
+ */
void drawCanvasPixel(uint8_t pixelnr);
- //! Draws 8 sprite pixels
- /*! Invoked inside draw() */
+ /*! @brief Draws 8 sprite pixels
+ * @details Invoked inside draw()
+ */
void drawSprites();
- //! @brief Draws a single sprite pixel for all sprites
- /*! @param pixelnr Pixel number (0 to 7)
- * @param freeze If the i-th bit is set to 1, the i-th shift register will freeze temporarily
- * @param halt If the i-th bit is set to 1, the i-th shift register will be deactivated
- * @param load If the i-th bit is set to 1, the i-th shift register will grab new data bits */
+ /*! @brief Draws a single sprite pixel for all sprites
+ * @param pixelnr Pixel number (0 to 7)
+ * @param freeze If the i-th bit is set to 1, the i-th shift register will freeze temporarily
+ * @param halt If the i-th bit is set to 1, the i-th shift register will be deactivated
+ * @param load If the i-th bit is set to 1, the i-th shift register will grab new data bits
+ */
void drawSpritePixel(unsigned pixelnr, uint8_t freeze, uint8_t halt, uint8_t load);
- //! @brief Draws a single sprite pixel for a single sprite
- /*! @param spritenr Sprite number (0 to 7)
- * @param pixelnr Pixel number (0 to 7)
- * @param freeze If set to true, the sprites shift register will freeze temporarily
- * @param halt If set to true, the sprites shift shift register will be deactivated
- * @param load If set to true, the sprites shift shift register will grab new data bits */
+ /*! @brief Draws a single sprite pixel for a single sprite
+ * @param spritenr Sprite number (0 to 7)
+ * @param pixelnr Pixel number (0 to 7)
+ * @param freeze If set to true, the sprites shift register will freeze temporarily
+ * @param halt If set to true, the sprites shift shift register will be deactivated
+ * @param load If set to true, the sprites shift shift register will grab new data bits
+ */
void drawSpritePixel(unsigned spritenr, unsigned pixelnr, bool freeze, bool halt, bool load);
- //! Draws all sprites into the pixelbuffer
- /*! A sprite is only drawn if it's enabled and if sprite drawing is not switched off for debugging */
+ /*! @brief Draws all sprites into the pixelbuffer
+ * @details A sprite is only drawn if it's enabled and if sprite drawing is not switched off for debugging
+ */
void drawAllSprites();
- //! Draw single sprite into pixel buffer
- /*! Helper function for drawSprites */
+ /*! @brief Draw single sprite into pixel buffer
+ * @details Helper function for drawSprites
+ */
void drawSprite(uint8_t nr);
@@ -421,42 +455,48 @@ private:
private:
- //! This is where loadColors() stores all retrieved colors
- /*! [0] : color for '0' pixels in single color mode or '00' pixels in multicolor mode
- [1] : color for '1' pixels in single color mode or '01' pixels in multicolor mode
- [2] : color for '10' pixels in multicolor mode
- [3] : color for '11' pixels in multicolor mode */
+ /*! @brief This is where loadColors() stores all retrieved colors
+ * @details [0] : color for '0' pixels in single color mode or '00' pixels in multicolor mode
+ * [1] : color for '1' pixels in single color mode or '01' pixels in multicolor mode
+ * [2] : color for '10' pixels in multicolor mode
+ * [3] : color for '11' pixels in multicolor mode
+ */
int col_rgba[4];
- //! loadColors() also determines if we are in single-color or multi-color mode
+ //! @brief loadColors() also determines if we are in single-color or multi-color mode
bool multicol;
public:
- // Determine pixel colors accordig to the provided display mode
+ //! @brief Determines pixel colors accordig to the provided display mode
void loadColors(DisplayMode mode, uint8_t characterSpace, uint8_t colorSpace);
- //! Draw single canvas pixel in single-color mode
- /*! 1s are drawn with setForegroundPixel, 0s are drawn with setBackgroundPixel.
- Uses the drawing colors that are setup by loadColors(). */
+ /*! @brief Draws single canvas pixel in single-color mode
+ * @details 1s are drawn with setForegroundPixel, 0s are drawn with setBackgroundPixel.
+ * Uses the drawing colors that are setup by loadColors().
+ */
void setSingleColorPixel(unsigned pixelnr, uint8_t bit);
- //! Draw single canvas pixel in multi-color mode
- /*! The left of the two color bits determines whether setForegroundPixel or setBackgroundPixel is used.
- Uses the drawing colors that are setup by loadColors(). */
+ /*! @brief Draws single canvas pixel in multi-color mode
+ * @details The left of the two color bits determines whether setForegroundPixel or setBackgroundPixel is used.
+ * Uses the drawing colors that are setup by loadColors().
+ */
void setMultiColorPixel(unsigned pixelnr, uint8_t two_bits);
- //! Draw single sprite pixel in single-color mode
- /*! Uses the drawing colors that are setup by updateSpriteColors */
+ /*! @brief Draws single sprite pixel in single-color mode
+ * @details Uses the drawing colors that are setup by updateSpriteColors
+ */
void setSingleColorSpritePixel(unsigned spritenr, unsigned pixelnr, uint8_t bit);
- //! Draw single sprite pixel in multi-color mode
- /*! Uses the drawing colors that are setup by updateSpriteColors */
+ /*! @brief Draws single sprite pixel in multi-color mode
+ * @details Uses the drawing colors that are setup by updateSpriteColors
+ */
void setMultiColorSpritePixel(unsigned spritenr, unsigned pixelnr, uint8_t two_bits);
- //! Draw a single sprite pixel
- /*! This function is invoked by setSingleColorPixel() and setMultiColorPixel().
- It takes care of collison and invokes setSpritePixel(4) to actually render the pixel. */
+ /*! @brief Draws a single sprite pixel
+ * @details This function is invoked by setSingleColorPixel() and setMultiColorPixel().
+ * It takes care of collison and invokes setSpritePixel(4) to actually render the pixel.
+ */
void setSpritePixel(unsigned pixelnr, int color, int nr);
@@ -466,31 +506,32 @@ public:
public:
- //! Draw a single frame pixel
+ //! @brief Draw a single frame pixel
void setFramePixel(unsigned pixelnr, int rgba);
- //! Draw a single foreground pixel
+ //! @brief Draw a single foreground pixel
void setForegroundPixel(unsigned pixelnr, int rgba);
- //! Draw a single background pixel
+ //! @brief Draw a single background pixel
void setBackgroundPixel(unsigned pixelnr, int rgba);
- //! Draw eight background pixels in a row
+ //! @brief Draw eight background pixels in a row
inline void setEightBackgroundPixels(int rgba) {
for (unsigned i = 0; i < 8; i++) setBackgroundPixel(i, rgba); }
- //! Draw a single sprite pixel
+ //! @brief Draw a single sprite pixel
void setSpritePixel(unsigned pixelnr, int rgba, int depth, int source);
- //! Extend border to the left and right to look nice.
- /*! This functions replicates the color of the leftmost and rightmost pixel */
+ /*! @brief Extend border to the left and right to look nice.
+ * @details This functions replicates the color of the leftmost and rightmost pixel
+ */
void expandBorders();
- //! Draw a horizontal colored line into the screen buffer
- /*! This method is utilized for debugging purposes, only. */
+ /*! @brief Draw a horizontal colored line into the screen buffer
+ * @details This method is utilized for debugging purposes, only.
+ */
void markLine(uint8_t color, unsigned start = 0, unsigned end = NTSC_PIXELS);
};
-
#endif
\ No newline at end of file
diff --git a/C64/ReSID.cpp b/C64/ReSID.cpp
index cda9d89..da60596 100755
--- a/C64/ReSID.cpp
+++ b/C64/ReSID.cpp
@@ -20,9 +20,8 @@
ReSID::ReSID()
{
- name = "ReSID";
-
- debug(2, " Creating ReSID at address %p...\n", this);
+ setDescription("ReSID");
+ debug(3, " Creating ReSID at address %p...\n", this);
sid = new SID();
@@ -106,16 +105,9 @@ ReSID::reset()
void
ReSID::setChipModel(chip_model model)
{
- switch (model) {
- case MOS6581:
- debug(2, "Plugging in MOS6581\n");
- break;
- case MOS8580:
- debug(2, "Plugging in MOS8580\n");
- break;
- default:
- warn("Unknown chip model. Using MOS8580\n");
- model = MOS8580;
+ if (model != MOS6581 && model != MOS8580) {
+ warn("Unknown chip model (%d). Using MOS8580\n", model);
+ model = MOS8580;
}
chipModel = model;
diff --git a/C64/ReSID.h b/C64/ReSID.h
index 89eeae1..445c72b 100755
--- a/C64/ReSID.h
+++ b/C64/ReSID.h
@@ -204,11 +204,11 @@ public:
/*! @brief Sets the current volume
*/
- void setVolume(int32_t v) { volume = v; }
+ void setVolume(int32_t vol) { volume = vol; }
/*! @brief Sets the target volume
*/
- void setTargetVolume(int32_t targetVolume) { targetVolume = volume; }
+ // void setTargetVolume(int32_t vol) { targetVolume = vol; }
/*! @brief Triggers volume ramp up phase
* @details Configures volume and targetVolume to simulate a smooth audio fade in
diff --git a/C64/SIDWrapper.cpp b/C64/SIDWrapper.cpp
old mode 100755
new mode 100644
index 8cf10ad..31c1e56
--- a/C64/SIDWrapper.cpp
+++ b/C64/SIDWrapper.cpp
@@ -20,7 +20,7 @@
SIDWrapper::SIDWrapper()
{
- name = "SIDWrapper";
+ setDescription("SIDWrapper");
oldsid = new OldSID();
resid = new ReSID();
@@ -73,16 +73,14 @@ SIDWrapper::dumpState()
void
SIDWrapper::setPAL()
{
- debug(2, "Switching SID to PAL frequency\n");
- // setClockFrequency(CPU::CLOCK_FREQUENCY_PAL);
+ debug(2, "SIDWrapper::setPAL\n");
setClockFrequency(PAL_CYCLES_PER_FRAME * PAL_REFRESH_RATE);
}
void
SIDWrapper::setNTSC()
{
- debug(2, "Switching SID to NTSC frequency\n");
- // setClockFrequency(CPU::CLOCK_FREQUENCY_NTSC);
+ debug(2, "SIDWrapper::setNTSC\n");
setClockFrequency(NTSC_CYCLES_PER_FRAME * NTSC_REFRESH_RATE);
}
diff --git a/C64/SIDWrapper.h b/C64/SIDWrapper.h
old mode 100755
new mode 100644
index ef20142..a671ea9
--- a/C64/SIDWrapper.h
+++ b/C64/SIDWrapper.h
@@ -1,19 +1,21 @@
-/*
- * (C) 2011 Dirk W. Hoffmann, All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header SidWrapper.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2011 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SIDWRAPPER_INC
@@ -27,40 +29,40 @@ class SIDWrapper : public VirtualComponent {
public:
- //! Start address of the SID I/O space.
+ //! @brief Start address of the SID I/O space.
static const uint16_t SID_START_ADDR = 0xD400;
- //! End address of the SID I/O space.
+ //! @brief End address of the SID I/O space.
static const uint16_t SID_END_ADDR = 0xD7FF;
private:
- //! Old SID implementation
+ //! @brief Old SID implementation
OldSID *oldsid;
public:
- //! Implementation based on the ReSID library
+ //! @brief Implementation based on the ReSID library
ReSID *resid;
private:
- //! SID selector
+ //! @brief SID selector
bool useReSID;
- //! Remembers latest written value
+ //! @brief Remembers latest written value
uint8_t latchedDataBus;
public:
- //! Returns true if the \a addr is located in the I/O range of the SID chip.
+ //! @brief Returns true if the addr is located in the I/O range of the SID chip.
static inline bool isSidAddr(uint16_t addr)
{ return (SID_START_ADDR <= addr && addr <= SID_END_ADDR); }
- //! Constructor.
+ //! @brief Constructor
SIDWrapper();
- //! Destructor.
+ //! @brief Destructor
~SIDWrapper();
- //! Dump internal state to console
+ //! @brief Prints debug information
void dumpState();
@@ -68,56 +70,50 @@ public:
// Configuring
// -----------------------------------------------------------------------------------------------
- //! Configure the SID chip for being used in PAL machines
+ //! @brief Configures the SID chip for being used in PAL machines.
void setPAL();
- //! Configure the SID chip for being used in NTSC machines
+ //! @brief Configures the SID chip for being used in NTSC machines.
void setNTSC();
- //! Returns true, iff ReSID libray shall be used.
+ //! @brief Returns true, whether ReSID or the old implementation should be used.
inline bool getReSID() { return useReSID; }
- //! Enable or disable ReSID library.
+ //! @brief Enables or disables the ReSID library.
void setReSID(bool enable);
- //! Get chip model
+ //! @brief Returns the simulated chip model.
inline chip_model getChipModel() { return resid->getChipModel(); }
- //! Set chip model (ReSID only)
+ //! @brief Sets chip model (ReSID only)
void setChipModel(chip_model value);
- //! Returns true iff audio filters are enabled.
- // inline bool getAudioFilter() { return resid->getAudioFilter(); }
+ //! @brief Returns true iff audio filters are enabled.
inline bool getAudioFilter() { return resid->getExternalAudioFilter(); }
- //! Enable or disable filters of SID.
+ //! @brief Enables or disables filters of SID.
void setAudioFilter(bool enable);
- //! Get sampling method
+ //! @brief Returns the sampling method.
inline sampling_method getSamplingMethod() { return resid->getSamplingMethod(); }
- //! Set sampling method (ReSID only)
+ //! @brief Sets the sampling method (ReSID only).
void setSamplingMethod(sampling_method value);
- //! Return samplerate.
+ //! @brief Returns the sample rate.
inline uint32_t getSampleRate() { return resid->getSampleRate(); }
- //! Sets samplerate of SID and it's 3 voices.
+ //! @brief Sets the samplerate of SID and it's 3 voices.
void setSampleRate(uint32_t sr);
- //! Get clock frequency
+ //! @brief Returns the clock frequency.
inline uint32_t getClockFrequency();
- //! Set clock frequency
+ //! @brief Sets the clock frequency.
void setClockFrequency(uint32_t frequency);
- /*! @brief Sets the current volume
- */
+ //! @brief Sets the current volume
void setVolume(int32_t v) { resid->setVolume(v); }
-
- /*! @brief Sets the target volume
- */
- void setTargetVolume(int32_t volume) { resid->setTargetVolume(volume); }
/*! @brief Triggers volume ramp up phase
* @details Configures volume and targetVolume to simulate a smooth audio fade in
@@ -130,8 +126,7 @@ public:
*/
void rampDown() { resid->rampDown(); }
- /*! @brief Clears ringbuffer
- */
+ //! @brief Clears the audio sample ringbuffer
void clearRingbuffer() { resid->clearRingbuffer(); }
@@ -141,27 +136,25 @@ public:
private:
- //! Current clock cycle since power up
+ //! @brief Current clock cycle since power up
uint64_t cycles;
public:
- /*!
- @abstract Executes SID until a certain cycle is reached
- @param cycle The target cycle
+ /*! @brief Executes SID until a certain cycle is reached
+ * @param cycle The target cycle
*/
void executeUntil(uint64_t targetCycle);
- /*!
- @abstract Executes SID for a certain number of cycles
- @param cycles Number of cycles to execute
+ /*! @brief Executes SID for a certain number of cycles
+ * @param cycles Number of cycles to execute
*/
void execute(uint64_t numCycles);
- //! Notifies the SID chip that the emulator has started
+ //! @brief Notifies the SID chip that the emulator has started
void run();
- //! Notifies the SID chip that the emulator has started
+ //! @brief Notifies the SID chip that the emulator has started
void halt();
@@ -169,13 +162,13 @@ public:
// Getter and setter
// -----------------------------------------------------------------------------------------------
- //! Special peek function for the I/O memory range.
+ //! @brief Special peek function for the I/O memory range.
uint8_t peek(uint16_t addr);
- //! Special poke function for the I/O memory range.
+ //! @brief Special poke function for the I/O memory range.
void poke(uint16_t addr, uint8_t value);
- //! Get next sample from \a ringBuffer.
+ //! @brief Reads next audio sample from the ringbuffer
float readData();
};
diff --git a/C64/Snapshot.cpp b/C64/Snapshot.cpp
old mode 100755
new mode 100644
index a318048..51ab324
--- a/C64/Snapshot.cpp
+++ b/C64/Snapshot.cpp
@@ -16,7 +16,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "Snapshot.h"
Snapshot::Snapshot()
{
@@ -166,8 +166,7 @@ Snapshot::writeToBuffer(uint8_t *buffer)
void
Snapshot::takeScreenshot(uint32_t *buf, bool pal)
{
- unsigned x_start = (pal ? 23 : 20);
- unsigned y_start = (pal ? 25 : 0);
+ unsigned x_start, y_start;
if (pal) {
x_start = PAL_LEFT_BORDER_WIDTH - 36;
diff --git a/C64/Snapshot.h b/C64/Snapshot.h
old mode 100755
new mode 100644
index 0d50041..3a68bc7
--- a/C64/Snapshot.h
+++ b/C64/Snapshot.h
@@ -1,5 +1,5 @@
/*
- * (C) 2009 Dirk W. Hoffmann. All rights reserved.
+ * (C) 2009 - 2015 Dirk W. Hoffmann. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,10 +23,17 @@
#include "Container.h"
#include "VIC_globals.h"
+// Snapshot version number of this release
+#define V_MAJOR 1
+#define V_MINOR 4
+#define V_SUBMINOR 2
+
+// Forward declarations
class C64;
-/*! @class Snapshot
- * @brief The Snapshot class declares the programmatic interface for a file that contains an emulator snapshot (a frozen internal state).
+/*! @class Snapshot
+ * @brief The Snapshot class declares the programmatic interface for a file that contains an emulator snapshot
+ * (a frozen internal state).
*/
class Snapshot : public Container {
@@ -34,95 +41,94 @@ private:
struct {
- //! Magic bytes ('V','C','6','4')
+ //! @brief Magic bytes ('V','C','6','4')
char magic[4];
- //! Version number (V major.minor.subminor)
+ //! @brief Version number (V major.minor.subminor)
uint8_t major;
uint8_t minor;
uint8_t subminor;
-
- //! Is this a snapshot of a PAL machine or an NTSC machine?
- // uint8_t isPAL;
- // Screenshot
+ //! @brief Screenshot
struct {
- //! Image width and height
+ //! @brief Image width and height
uint16_t width, height;
- //! Screen buffer data
+ //! @brief Screen buffer data
uint32_t screen[PAL_RASTERLINES * NTSC_PIXELS];
} screenshot;
- // Size of internal state
+ //! @brief Size of internal state
uint32_t size;
} header;
- // Internal state data
+ //! @brief Internal state data
uint8_t *state;
- //! Date and time of snapshot creation
+ //! @brief Date and time of snapshot creation
time_t timestamp;
public:
- //! Constructor
+ //! @brief Constructor
Snapshot();
- //! Destructor
+ //! @brief Destructor
~Snapshot();
- //! Free allocated memory
+ //! @brief Frees the allocated memory
void dealloc();
- //! Allocate memory for storing internal state
+ //! @brief Allocates memory for storing internal state
bool alloc(unsigned size);
- //! Returns true if file header matches
+ //! @brief Returns true if file header matches
static bool isSnapshot(const char *filename);
- //! Returns true if 'fileIsValid' and version number match
+ //! @brief Returns true if 'fileIsValid' and version number match
static bool isSnapshot(const char *filename, int major, int minor, int subminor);
- //! Factory methods
+ /*! @brief Factory method
+ * @details Creates a snapshot with data from a file
+ */
static Snapshot *snapshotFromFile(const char *filename);
- static Snapshot *snapshotFromBuffer(const uint8_t *buffer, unsigned size);
+
+ /*! @brief Factory method
+ * @details Creates a snapshot with data from a buffer
+ */
+ static Snapshot *snapshotFromBuffer(const uint8_t *buffer, unsigned size);
- //! Virtual functions from Container class
+ //
+ // Virtual functions from Container class
+ //
+
bool fileIsValid(const char *filename);
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
unsigned sizeOnDisk() { return getHeaderSize() + getDataSize(); }
-
ContainerType getType();
const char *getTypeAsString();
- //! Returns size of header
+ //! @brief Returns size of header
uint32_t getHeaderSize() { return sizeof(header); }
- //! Returns pointer to header data
+ //! @brief Returns pointer to header data
uint8_t *getHeader() { return (uint8_t *)&header; }
- //! Returns size of core data
+ //! @brief Returns size of core data
uint32_t getDataSize() { return header.size; }
- //! Returns pointer to core data
+ //! @brief Returns pointer to core data
uint8_t *getData() { return state; }
- //! Return timestamp
+ //! @brief Returns the timestamp
time_t getTimestamp() { return timestamp; }
- //! Set timestamp
+ //! @brief Sets the timestamp
void setTimestamp(time_t value) { timestamp = value; }
-
- //! Return PAL/NTSC flag
- // bool isPAL() { return (bool)header.isPAL; }
-
- //! Set PAL/NTSC flag
- // void setPAL(bool value) { header.isPAL = (uint8_t)value; }
//! Returns true, if snapshot does not contain data yet
bool isEmpty() { return timestamp == 0; }
diff --git a/C64/T64Archive.cpp b/C64/T64Archive.cpp
index ca80127..0688844 100755
--- a/C64/T64Archive.cpp
+++ b/C64/T64Archive.cpp
@@ -20,6 +20,7 @@
T64Archive::T64Archive()
{
+ setDescription("T64Archive");
data = NULL;
dealloc();
}
@@ -53,39 +54,30 @@ T64Archive::isT64File(const char *filename)
T64Archive *
T64Archive::archiveFromT64File(const char *filename)
{
- T64Archive *archive;
-
- fprintf(stderr, "Loading T64 archive from T64 file...\n");
- archive = new T64Archive();
+ T64Archive *archive = new T64Archive();
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
- delete archive;
- archive = NULL;
+ delete archive;
+ return NULL;
}
if (!archive->repair()) {
delete archive;
- archive = NULL;
+ return NULL;
}
+ archive->debug(1, "T64 archive created from file %s.\n", filename);
return archive;
}
T64Archive *
T64Archive::archiveFromArchive(Archive *otherArchive)
{
- T64Archive *archive;
-
if (otherArchive == NULL)
return NULL;
- fprintf(stderr, "Creating T64 archive from %s archive...\n", otherArchive->getTypeAsString());
-
- if ((archive = new T64Archive()) == NULL) {
- fprintf(stderr, "Failed to create archive\n");
- return NULL;
- }
+ T64Archive *archive = new T64Archive();
+ archive->debug(1, "Creating T64 archive from %s archive...\n", otherArchive->getTypeAsString());
// Determine container size and allocate memory
unsigned currentFiles = otherArchive->getNumberOfItems();
@@ -96,7 +88,7 @@ T64Archive::archiveFromArchive(Archive *otherArchive)
archive->size += otherArchive->getSizeOfItem(i);
if ((archive->data = (uint8_t *)malloc(archive->size)) == NULL) {
- fprintf(stderr, "Failed to allocate %d bytes of memory\n", archive->size);
+ archive->warn("Failed to allocate %d bytes of memory\n", archive->size);
delete archive;
return NULL;
}
@@ -355,7 +347,9 @@ T64Archive::getByte()
if (fp == fp_eof || fp == size)
fp = -1;
- // fprintf(stderr, "%02X ", result);
+ if (tracingEnabled())
+ msg("%02X%c", result, fp == -1 ? '\n' : ' ');
+
return result;
}
@@ -393,8 +387,8 @@ T64Archive::repair()
uint16_t noOfItemsStatedInHeader = getNumberOfItems();
if (noOfItems != noOfItemsStatedInHeader) {
- fprintf(stderr, "Repairing corrupted T64 archive: Changing number of items from %d to %d.\n",
- noOfItemsStatedInHeader, noOfItems);
+ debug(1, "Repairing corrupted T64 archive: Changing number of items from %d to %d.\n",
+ noOfItemsStatedInHeader, noOfItems);
data[0x24] = LO_BYTE(noOfItems);
data[0x25] = HI_BYTE(noOfItems);
@@ -413,7 +407,7 @@ T64Archive::repair()
uint16_t startAddrInContainer = LO_LO_HI_HI(data[n], data[n+1], data[n+2], data[n+3]);
if (startAddrInContainer >= size) {
- fprintf(stderr, "T64 archive is corrupt (offset mismatch). Sorry, can't repair.\n");
+ warn("T64 archive is corrupt (offset mismatch). Sorry, can't repair.\n");
return false;
}
@@ -434,8 +428,8 @@ T64Archive::repair()
// Let's assume that the rest of the file data belongs to this file ...
uint16_t fixedEndAddrInMemory = startAddrInMemory + (size - startAddrInContainer);
- fprintf(stderr, "Repairing corrupted T64 archive: Changing end address of item %d from %04X to %04X.\n",
- i, endAddrInMemory, fixedEndAddrInMemory);
+ debug(1, "Repairing corrupted T64 archive: Changing end address of item %d from %04X to %04X.\n",
+ i, endAddrInMemory, fixedEndAddrInMemory);
data[n] = LO_BYTE(fixedEndAddrInMemory);
data[n+1] = HI_BYTE(fixedEndAddrInMemory);
@@ -444,17 +438,3 @@ T64Archive::repair()
return 1; // Archive repaired successfully
}
-
-
-#if 0
-void
-T64Archive::dumpDirectory()
-{
- Archive::dumpDirectory();
-
- for (unsigned i = 0; i < getNumberOfItems(); i++) {
- fprintf(stderr, " Item %2d: %s (%d bytes, load address: %d)\n",
- i, getNameOfItem(i), getSizeOfItem(i), getDestinationAddrOfItem(i));
- }
-}
-#endif
\ No newline at end of file
diff --git a/C64/T64Archive.h b/C64/T64Archive.h
index 3feb9bd..90eb5a8 100755
--- a/C64/T64Archive.h
+++ b/C64/T64Archive.h
@@ -21,25 +21,27 @@
#include "Archive.h"
-/*! @class D64Archive
- * @brief The D64Archive class declares the programmatic interface for a file in T64 format.
+/*! @class D64Archive
+ * @brief The D64Archive class declares the programmatic interface for a file in T64 format.
*/
class T64Archive : public Archive {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive.
uint8_t *data;
- //! @brief File size
+ //! @brief File size
int size;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ @details An offset into the data array.
+ */
int fp;
- /*! @brief End of file position
- @discussion Maximum value for fp. Do we really need this? */
+ /*! @brief End of file position
+ * @details Maximum value for fp. Do we really need this?
+ */
int fp_eof;
public:
@@ -48,20 +50,21 @@ public:
//! @functiongroup Creating and destructing T64 archives
//
- //! @brief Standard constructor.
+ //! @brief Standard constructor
T64Archive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~T64Archive();
- //! @brief Returns true iff the specified file is a T64 file
+ //! @brief Returns true iff the specified file is a T64 file
static bool isT64File(const char *filename);
- //! @brief Creates a T64 archive from a T64 file located on disk.
+ //! @brief Creates a T64 archive from a T64 file located on disk.
static T64Archive *archiveFromT64File(const char *filename);
- /*! @brief Creates a T64 archive from another archive.
- @result A T64 archive that contains the first directory item of the other archive. */
+ /*! @brief Creates a T64 archive from another archive.
+ * @result A T64 archive that contains the first directory item of the other archive.
+ */
static T64Archive *archiveFromArchive(Archive *otherArchive);
@@ -79,6 +82,7 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Virtual functions from Archive class
//
@@ -91,6 +95,7 @@ public:
void selectItem(int n);
int getByte();
+
//
// Custom methods
//
@@ -98,15 +103,15 @@ public:
//! @brief Check if the file header contains information at the specific location
bool directoryItemIsPresent(int n);
- //! @brief Check archive consistency and repair inconsistent information
- /*! @discussion This method can eliminate the following inconsistencies:
- * number of files: some archives state falsely in their header that zero
- * files are present. This value will be fixed.
- * end loading address: Archives that are created with CONVC64 often contain
- * a value of 0xC3C6, which is wrong (e.g., paradrd.t64). This value will be
- * changed such that getByte() will read until the end of the physical file.
- * @result true, if archive was consistent or could be repaired. false, if an inconsistency
- * has been detected that could not be repaired.
+ /*! @brief Check archive consistency and repair inconsistent information
+ * @details This method can eliminate the following inconsistencies:
+ * number of files: some archives state falsely in their header that zero
+ * files are present. This value will be fixed.
+ * end loading address: Archives that are created with CONVC64 often contain
+ * a value of 0xC3C6, which is wrong (e.g., paradrd.t64). This value will be
+ * changed such that getByte() will read until the end of the physical file.
+ * @result true, if archive was consistent or could be repaired. false, if an inconsistency
+ * has been detected that could not be repaired.
*/
bool repair();
};
diff --git a/C64/TAPArchive.cpp b/C64/TAPArchive.cpp
old mode 100755
new mode 100644
index d879f8d..9b057fc
--- a/C64/TAPArchive.cpp
+++ b/C64/TAPArchive.cpp
@@ -16,10 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "C64.h"
+#include "TAPArchive.h"
TAPArchive::TAPArchive()
{
+ setDescription("TAPArchive");
data = NULL;
dealloc();
}
@@ -51,16 +52,14 @@ TAPArchive::isTAPFile(const char *filename)
TAPArchive *
TAPArchive::archiveFromTAPFile(const char *filename)
{
- TAPArchive *archive;
+ TAPArchive *archive = new TAPArchive();
- fprintf(stderr, "Loading TAP archive from TAP file...\n");
- archive = new TAPArchive();
if (!archive->readFromFile(filename)) {
- fprintf(stderr, "Failed to load archive\n");
delete archive;
- archive = NULL;
+ return NULL;
}
-
+
+ archive->debug(1, "TAP archive created from file %s.\n", filename);
return archive;
}
@@ -100,14 +99,9 @@ TAPArchive::readFromBuffer(const uint8_t *buffer, unsigned length)
memcpy(data, buffer, length);
size = length;
- for (unsigned i = 16; i > 0; i--) {
- fprintf(stderr, "%02X ", data[size - i]);
- }
- fprintf(stderr, "\n");
-
int l = LO_LO_HI_HI(data[0x10], data[0x11], data[0x12], data[0x13]);
if (l + 0x14 /* Header */ != size) {
- fprintf(stderr, "Size mismatch! Archive should have %d data bytes, found %d\n", l, size - 0x14);
+ warn("Size mismatch! Archive should have %d data bytes, found %d\n", l, size - 0x14);
}
return true;
diff --git a/C64/TAPArchive.h b/C64/TAPArchive.h
old mode 100755
new mode 100644
index f4f0a21..192b6ad
--- a/C64/TAPArchive.h
+++ b/C64/TAPArchive.h
@@ -21,37 +21,39 @@
#include "Archive.h"
-/*! @class TAPArchive
- * @brief The TAPArchive class declares the programmatic interface for a file in TAP format.
+/*! @class TAPArchive
+ * @brief The TAPArchive class declares the programmatic interface for a file in TAP format.
*/
class TAPArchive : public Container {
private:
- //! @brief The raw data of this archive.
+ //! @brief The raw data of this archive.
uint8_t *data;
- //! @brief File size
+ //! @brief File size
int size;
- /*! @brief File pointer
- @discussion An offset into the data array. */
+ /*! @brief File pointer
+ * @details An offset into the data array.
+ */
int fp;
public:
- //! @brief Standard constructor.
+ //! @brief Standard constructor
TAPArchive();
- //! @brief Standard destructor.
+ //! @brief Standard destructor
~TAPArchive();
- //! @brief Returns true iff the specified file is a TAP file
+ //! @brief Returns true iff the specified file is a TAP file
static bool isTAPFile(const char *filename);
- //! @brief Creates a TAP archive from a TAP file.
+ //! @brief Creates a TAP archive from a TAP file.
static TAPArchive *archiveFromTAPFile(const char *filename);
+
//
// Virtual functions from Container class
//
@@ -66,6 +68,7 @@ public:
bool readFromBuffer(const uint8_t *buffer, unsigned length);
unsigned writeToBuffer(uint8_t *buffer);
+
//
// Accessing the archive
//
diff --git a/C64/TOD.cpp b/C64/TOD.cpp
index 8a33a65..2bcaa3f 100755
--- a/C64/TOD.cpp
+++ b/C64/TOD.cpp
@@ -20,8 +20,8 @@
TOD::TOD()
{
- name = "TOD";
- debug(2, " Creating TOD at address %p...\n", this);
+ setDescription("TOD");
+ debug(3, " Creating TOD at address %p...\n", this);
// Register snapshot items
SnapshotItem items[] = {
diff --git a/C64/TOD.h b/C64/TOD.h
index 9feec08..73b02cc 100755
--- a/C64/TOD.h
+++ b/C64/TOD.h
@@ -1,19 +1,21 @@
-/*
- * (C) 2007 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header TOD.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2007 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TOD_INC
@@ -21,10 +23,11 @@
#include "VirtualComponent.h"
-//! Time of day clock (TOD)
-/*! Each CIA chip contains a time of day clock, counting hours, minutes, seconds and tenth of a second.
-Every TOD clock features an alarm mechanism. When the alarm time is reached, an interrupt is initiated.
-*/
+/*! @brief Time of day clock (TOD)
+ * @details Each CIA chip contains a time of day clock, counting hours, minutes,
+ * seconds and tenth of a second. Every TOD clock features an alarm mechanism.
+ * When the alarm time is reached, an interrupt is initiated.
+ */
class TOD : public VirtualComponent {
private:
@@ -42,100 +45,114 @@ private:
TimeOfDay alarm;
TimeOfDay latch;
- //! True, if the TOD registers are frozen
- /*! The CIA chip freezes the registers when the hours-part is read and reactivates them, when the 1/10th part is read.
- Although the values stay constant, the internal clock continues to run.
- Purpose: If you start reading with the hours-part, the clock won't change until you have read the whole time.
- */
+ /*! @brief If set to true, the TOD registers are frozen
+ * @details The CIA chip freezes the registers when the hours-part is read and reactivates them,
+ * when the 1/10th part is read. Although the values stay constant, the internal clock
+ * continues to run. Purpose: If you start reading with the hours-part, the clock won't
+ * change until you have read the whole time.
+ */
bool frozen;
- //! True, if the TOD clock is stopped
- /*! The CIA chip stops the TOD clock, when the hours-part is written and restarts it, when the 1/10th part is written.
- Purpose: The clock will only start running when the time is completely set.
- */
+ /*! @brief If set to true, the TOD clock is stopped
+ * @details The CIA chip stops the TOD clock, when the hours-part is written and restarts it,
+ * when the 1/10th part is written. Purpose: The clock will only start running when the
+ * time is completely set.
+ */
bool stopped;
public:
- //! Constructor
+ //! @brief Constructor
TOD();
- //! Destructor
+ //! @brief Destructor
~TOD();
- //! Bring the CIA back to its initial state
+ //! @brief Restores the initial state.
void reset();
- //! Size of internal state
+ //! @brief Returns the size of the internal state.
uint32_t stateSize();
- //! Dump internal state to console
+ //! @brief Prints debug information.
void dumpState();
+ //! @brief Freezes the time of day clock.
inline void freeze() { latch.value = tod.value; frozen = true; }
- inline void defreeze() { frozen = false; }
- inline void stop() { stopped = true; }
- inline void cont() { stopped = false; }
+
+ //! @brief Defreezes the time of day clock.
+ inline void defreeze() { frozen = false; }
+
+ //! @brief Stops the time of day clock.
+ inline void stop() { stopped = true; }
+
+ //! @brief Starts the time of day clock.
+ inline void cont() { stopped = false; }
- //! Returns the hours digits of the time of day clock
- /*! Note: The TOD clock freezes on a read or write access */
+ /*! @brief Returns the hours digits of the time of day clock.
+ * @note The TOD clock freezes when a read or write access occurrs.
+ */
uint8_t getTodHours() { return frozen ? latch.time.hours & 0x9F : tod.time.hours & 0x9F; }
- //! Sets the hours digits of the time of day clock
- /*! Note: The TOD clock freezes on a read or write access */
+ /*! @brief Sets the hours digits of the time of day clock.
+ * @note The TOD clock freezes when a read or write access occurrs.
+ */
void setTodHours(uint8_t value) { tod.time.hours = value & 0x9F; }
- //! Returns the minutes digits of the time of day clock
+ //! @brief Returns the minutes digits of the time of day clock.
inline uint8_t getTodMinutes() { return frozen ? latch.time.minutes & 0x7F : tod.time.minutes & 0x7F; }
- //! Sets the minutes digits of the time of day clock
+ //! @brief Sets the minutes digits of the time of day clock.
inline void setTodMinutes(uint8_t value) { tod.time.minutes = value & 0x7F; }
- //! Returns the seconds digits of the time of day clock
+ //! @brief Returns the seconds digits of the time of day clock.
inline uint8_t getTodSeconds() { return frozen ? latch.time.seconds & 0x7F : tod.time.seconds & 0x7F; }
- //! Sets the seconds digits of the time of day clock
+ //! @brief Sets the seconds digits of the time of day clock.
inline void setTodSeconds(uint8_t value) { tod.time.seconds = value & 0x7F; }
- //! Returns the tenth-of-a-second digits of the time of day clock
- /*! Note: The TOD clock unfreezes on a read or write access */
+ /*! @brief Returns the tenth-of-a-second digits of the time of day clock.
+ * @note The TOD clock unfreezes on a read or write access.
+ */
uint8_t getTodTenth() { return frozen ? latch.time.tenth & 0x0F : tod.time.tenth & 0x0F; }
- //! Sets the tenth-of-a-second digits of the time of day clock
- /*! Note: The TOD clock unfreezes on a read or write access */
+ /*! @brief Sets the tenth-of-a-second digits of the time of day clock.
+ * @note The TOD clock unfreezes on a read or write access.
+ */
void setTodTenth(uint8_t value) { tod.time.tenth = value & 0x0F; }
- //! Returns the hours digits of the alarm time
+ //! @brief Returns the hours digits of the alarm time.
inline uint8_t getAlarmHours() { return alarm.time.hours & 0x9F; }
- //! Sets the hours digits of the alarm time
+ //! @brief Sets the hours digits of the alarm time.
inline void setAlarmHours(uint8_t value) { alarm.time.hours = value & 0x9F; }
- //! Returns the minutes digits of the alarm time
+ //! @brief Returns the minutes digits of the alarm time.
inline uint8_t getAlarmMinutes() { return alarm.time.minutes & 0x7F; }
- //! Sets the minutes digits of the alarm time
+ //! @brief Sets the minutes digits of the alarm time.
inline void setAlarmMinutes(uint8_t value) { alarm.time.minutes = value & 0x7F; }
- //! Returns the seconds digits of the alarm time
+ //! @brief Returns the seconds digits of the alarm time.
inline uint8_t getAlarmSeconds() { return alarm.time.seconds & 0x7F; }
- //! Sets the seconds digits of the alarm time
+ //! @brief Sets the seconds digits of the alarm time.
inline void setAlarmSeconds(uint8_t value) { alarm.time.seconds = value & 0x7F; }
- //! Returns the tenth-of-a-second digits of the alarm time
+ //! @brief Returns the tenth-of-a-second digits of the alarm time.
inline uint8_t getAlarmTenth() { return alarm.time.tenth & 0x0F; }
- //! Sets the tenth-of-a-second digits of the time of day clock
+ //! @brief Sets the tenth-of-a-second digits of the time of day clock.
inline void setAlarmTenth(uint8_t value) { alarm.time.tenth = value & 0x0F; }
- //! Returns true, iff the TOD clock is currently frozen
+ //! @brief Returns true, iff the TOD clock is currently frozen.
inline bool isFrozen() { return frozen; }
- //! Increment the TOD clock by one tenth of a second
- /*! The function increments the TOD clock. It returns true iff the currently set alarm time is reached.
- The function is supposed to be invoked whenever a frame is finished (during VBlank)
- */
+ /*! @brief Increments the TOD clock by one tenth of a second.
+ * @details The function increments the TOD clock. It returns true iff the currently set
+ * alarm time is reached. The function is supposed to be invoked whenever a
+ * frame is finished (during VBlank).
+ */
bool increment();
};
diff --git a/C64/VC1541.cpp b/C64/VC1541.cpp
old mode 100755
new mode 100644
index 537e8ea..f003493
--- a/C64/VC1541.cpp
+++ b/C64/VC1541.cpp
@@ -20,17 +20,15 @@
VC1541::VC1541()
{
- name = "1541";
- debug(2, "Creating virtual VC1541 at address %p\n", this);
+ setDescription("1541");
+ debug(3, "Creating virtual VC1541 at address %p\n", this);
- // Create sub components
- mem = new VC1541Memory();
- cpu = new CPU();
- cpu->setName("1541CPU");
- cpu->chipModel = CPU::MOS6502;
+ // Configure CPU
+ cpu.setDescription("1541CPU");
+ cpu.chipModel = CPU::MOS6502;
// Register sub components
- VirtualComponent *subcomponents[] = { mem, cpu, &via1, &via2, &disk, NULL };
+ VirtualComponent *subcomponents[] = { &mem, &cpu, &via1, &via2, &disk, NULL };
registerSubComponents(subcomponents, sizeof(subcomponents));
// Register snapshot items
@@ -65,10 +63,7 @@ VC1541::VC1541()
VC1541::~VC1541()
{
- debug(2, "Releasing VC1541...\n");
-
- delete cpu;
- delete mem;
+ debug(3, "Releasing VC1541...\n");
}
void
@@ -77,17 +72,17 @@ VC1541::reset()
VirtualComponent::reset();
// Establish bindings
- iec = c64->iec;
+ iec = &c64->iec;
- cpu->mem = mem;
- cpu->setPC(0xEAA0);
+ cpu.mem = &mem;
+ cpu.setPC(0xEAA0);
halftrack = 41;
}
void
VC1541::resetDisk()
{
- debug (2, "Resetting disk in VC1541...\n");
+ debug (3, "Resetting disk in VC1541...\n");
// Disk properties
disk.clearDisk();
@@ -98,64 +93,19 @@ VC1541::resetDisk()
void
VC1541::ping()
{
- debug(2, "Pinging VC1541...\n");
+ debug(3, "Pinging VC1541...\n");
c64->putMessage(MSG_VC1541_LED, redLED ? 1 : 0);
c64->putMessage(MSG_VC1541_MOTOR, rotating ? 1 : 0);
c64->putMessage(MSG_VC1541_DISK, diskInserted ? 1 : 0);
- cpu->ping();
- mem->ping();
+ // TODO: Replace manual pinging of sub components by a call to super::ping()
+ cpu.ping();
+ mem.ping();
via1.ping();
via2.ping();
}
-#if 0
-uint32_t
-VC1541::stateSize()
-{
- uint32_t result = VirtualComponent::stateSize();
-
- result += disk.stateSize();
- result += cpu->stateSize();
- result += via1.stateSize();
- result += via2.stateSize();
- result += mem->stateSize();
-
- return result;
-}
-
-void
-VC1541::loadFromBuffer(uint8_t **buffer)
-{
- uint8_t *old = *buffer;
-
- VirtualComponent::loadFromBuffer(buffer);
- disk.loadFromBuffer(buffer);
- cpu->loadFromBuffer(buffer);
- via1.loadFromBuffer(buffer);
- via2.loadFromBuffer(buffer);
- mem->loadFromBuffer(buffer);
-
- assert(*buffer - old == stateSize());
-}
-
-void
-VC1541::saveToBuffer(uint8_t **buffer)
-{
- uint8_t *old = *buffer;
-
- VirtualComponent::saveToBuffer(buffer);
- disk.saveToBuffer(buffer);
- cpu->saveToBuffer(buffer);
- via1.saveToBuffer(buffer);
- via2.saveToBuffer(buffer);
- mem->saveToBuffer(buffer);
-
- assert(*buffer - old == stateSize());
-}
-#endif
-
void
VC1541::dumpState()
{
@@ -241,7 +191,7 @@ VC1541::byteReady(uint8_t byte)
inline void
VC1541::byteReady()
{
- if (via2.overflowEnabled()) cpu->setV(1);
+ if (via2.overflowEnabled()) cpu.setV(1);
}
@@ -250,7 +200,7 @@ VC1541::simulateAtnInterrupt()
{
if (via1.atnInterruptsEnabled()) {
via1.indicateAtnInterrupt();
- cpu->setIRQLineATN();
+ cpu.setIRQLineATN();
// debug("CPU is interrupted by ATN line.\n");
} else {
// debug("Sorry, want to interrupt, but CPU does not accept ATN line interrupts\n");
@@ -420,20 +370,41 @@ VC1541::ejectDisk()
c64->putMessage(MSG_VC1541_DISK_SOUND, 0);
}
+D64Archive *
+VC1541::convertToD64()
+{
+ D64Archive *archive = new D64Archive();
+ debug(1, "Creating D64 archive from currently inserted diskette ...\n");
+
+ // Perform test run
+ int error;
+ if (disk.decodeDisk(NULL, &error) > D64_802_SECTORS_ECC || error) {
+ archive->warn("Cannot create archive (error code: %d)\n", error);
+ delete archive;
+ return NULL;
+ }
+
+ // Decode diskette
+ archive->setNumberOfTracks(42);
+ disk.decodeDisk(archive->getData());
+
+ archive->debug(2, "Archive has %d files\n", archive->getNumberOfItems());
+ archive->debug(2, "Item %d has size: %d\n", 0, archive->getSizeOfItem(0));
+
+ return archive;
+}
+
bool
VC1541::exportToD64(const char *filename)
{
- D64Archive *archive;
-
assert(filename != NULL);
+
+ D64Archive *archive = convertToD64();
- // Create archive
- if ((archive = D64Archive::archiveFromDrive(this)) == NULL)
+ if (archive == NULL)
return false;
- // Write archive to disk
archive->writeToFile(filename);
-
delete archive;
return true;
}
diff --git a/C64/VC1541.h b/C64/VC1541.h
old mode 100755
new mode 100644
index 56abc9b..94ddd4d
--- a/C64/VC1541.h
+++ b/C64/VC1541.h
@@ -24,69 +24,71 @@
#include "VIA6522.h"
#include "Disk525.h"
+#include "D64Archive.h"
// Forward declarations
class IEC;
class C64;
/*!
- * @brief Virtual VC1541 drive
- * @details Bit-accurate emulation of a VC1541
+ * @brief Virtual VC1541 drive
+ * @details Bit-accurate emulation of a VC1541
*/
class VC1541 : public VirtualComponent {
public:
- //! Reference to the virtual IEC bus
+ //! @brief Reference to the virtual IEC bus
IEC *iec;
- //! Reference to the virtual 6502 CPU
- CPU *cpu;
+ //! @brief CPU of the virtual drive (6502)
+ CPU cpu;
- //! Reference to the virtual drive memory
- VC1541Memory *mem;
+ //! @brief Memory of the virtual drive
+ VC1541Memory mem;
- //! VIA6522 connecting the drive CPU with the IEC bus
+ //! @brief VIA6522 connecting the drive CPU with the IEC bus
VIA1 via1;
- //! VIA6522 connecting the drive CPU with the drives read/write head
+ //! @brief VIA6522 connecting the drive CPU with the drives read/write head
VIA2 via2;
- //! Disk in this drive (single sided 5,25" floppy disk)
+ //! @brief Disk in this drive (single sided 5,25" floppy disk)
Disk525 disk;
- //! Constructor
+ //! @brief Constructor
VC1541();
- //! Destructor
+ //! @brief Destructor
~VC1541();
- //! Reset VC1541 drive
+ //! @brief Resets the VC1541 drive.
void reset();
- //! Reset disk properties
- /*! Resets all disk related properties. reset() keeps the disk alive. */
+ /*! @brief Resets disk properties
+ * @details Resets all disk related properties. reset() keeps the disk alive.
+ */
void resetDisk();
- //! Dump current configuration into message queue
+ //! @brief Dump current configuration into message queue
void ping();
- //! Dump current state into logfile
+ //! @brief Dump current state into logfile
void dumpState();
private:
- //! The stored numbers indicate how many clock cycles are needed for reading or writing a single bit
- /*! Background: The VC1541 drive is clocked by 16 Mhz. The base frequency is divided by N where N ranges
- from 13 (zone 0) to 16 (zone 4). On the logic board, this is done with a 4-bit counter of type 74SL193
- whose reset value bits are connected to the two "zone" bits (PB5 and PB6) coming from via 2.
- A second 74SL193 divides the signal by 4. The result serves as the clock signal for all units operating
- on bit level (i.e., the two shift registers that transfer bits from and to the head).
- It follows that a single bit is ready after 3,25 CPU cycles in zone 0 and 4 CPU cycles in zone 4.
- The resulting signal is fed into a third counter (of type 74LS191). It divides the signal by 8 and its
- output is fed into a three input NAND-gate computing the important BYTE-READY signal. */
-
+ /*! @brief The stored numbers indicate how many clock cycles are needed for reading or writing a single bit
+ * @details The VC1541 drive is clocked by 16 Mhz. The base frequency is divided by N where N ranges
+ * from 13 (zone 0) to 16 (zone 4). On the logic board, this is done with a 4-bit counter of type 74SL193
+ * whose reset value bits are connected to the two "zone" bits (PB5 and PB6) coming from via 2.
+ * A second 74SL193 divides the signal by 4. The result serves as the clock signal for all units operating
+ * on bit level (i.e., the two shift registers that transfer bits from and to the head).
+ * It follows that a single bit is ready after 3,25 CPU cycles in zone 0 and 4 CPU cycles in zone 4.
+ * The resulting signal is fed into a third counter (of type 74LS191). It divides the signal by 8 and its
+ * output is fed into a three input NAND-gate computing the important BYTE-READY signal.
+ */
const uint16_t cyclesPerBit[4] = {
13 * 4, // Zone 0: One bit each (16 * 3.25) base clock cycles (= 3.25 CPU cycles)
14 * 4, // Zone 1: One bit each (16 * 3.5) base clock cycles (= 3.5 CPU cycles)
@@ -105,71 +107,76 @@ public:
//! @functiongroup Configuring the device
//
- /*! @brief Returns true if sound messages are sent to the GUI
- */
+ //! @brief Returns true if sound messages are sent to the GUI.
inline bool soundMessagesEnabled() { return sendSoundMessages; }
- /*! @brief Enables or disables sending of sound messages
- */
+ //! @brief Enables or disables sending of sound messages.
inline void setSendSoundMessages(bool b) { sendSoundMessages = b; }
- /*! @brief Returns true if drive is emulated bit accurately
- */
+ //! @brief Returns true if drive is emulated bit accurately.
inline bool getBitAccuracy() { return bitAccuracy; }
- /*! @brief Enables or disables bit accurate drive emulation
- */
+ //! @brief Enables or disables bit accurate drive emulation.
void setBitAccuracy(bool b);
+
//
//! @functiongroup Accessing drive properties
//
+ //! @brief Returns true iff the red drive LED is shining.
inline bool getRedLED() { return redLED; };
+
+ //! @brief Turns red drive LED on or off.
void setRedLED(bool b);
-
+
+ //! @brief Returns true iff the drive engine is on.
inline bool isRotating() { return rotating; };
+
+ //! @brief Turns the drive engine on or off.
void setRotating(bool b);
+
//
//! @functiongroup Handling virtual disks
//
- /*! @brief Returns true if a disk is inserted
- */
+ //! @brief Returns true if a disk is inserted.
inline bool hasDisk() { return diskInserted; }
- /*! @brief Inserts an archive as a virtual disk
- * @discussion Before inserting, the archive data is converted to VC1541s GCR-encoded track/sector format.
+ /*! @brief Inserts an archive as a virtual disk.
+ * @details Before inserting, the archive data is converted to VC1541s GCR-encoded track/sector format.
*/
void insertDisk(Archive *a);
- // void insertDisk(D64Archive *a);
-
- /*! @brief Exports the currently inserted disk to D64 file
- */
- bool exportToD64(const char *filename);
-
- /*! @brief Returns true if a disk is partially inserted
- */
+ //! @brief Returns true if a disk is partially inserted.
inline bool isDiskPartiallyInserted() { return diskPartiallyInserted; }
- /*! @brief Sets if a disk is partially inserted
- */
+ //! @brief Sets if a disk is partially inserted.
inline void setDiskPartiallyInserted(bool b) { diskPartiallyInserted = b; }
- /*! @brief Returns the current status of the write protection light barrier
- * @discussion If the light barrier is blocked, the drive head is unable to change data bits
+ /*! @brief Returns the current status of the write protection light barrier
+ * @details If the light barrier is blocked, the drive head is unable to change data bits
*/
inline bool getLightBarrier() { return isDiskPartiallyInserted() || disk.isWriteProtected(); }
- /*! @brief Ejects the virtual disk
- * @discussion Does nothing, if no disk is present. Beware that this function causes a considerable time delay,
- * because it is necessary to block the write protection light barrier for a while. Otherwise,
- * VC1541 DOS would not recognize the ejection.
+ /*! @brief Ejects the virtual disk
+ * @details Does nothing, if no disk is present. Beware that this function causes a considerable time delay,
+ * because it is necessary to block the write protection light barrier for a while. Otherwise,
+ * VC1541 DOS would not recognize the ejection.
*/
void ejectDisk();
+ /*! @brief Converts the currently inserted disk into a D64 archive.
+ * @result A D64 archive containing the same files as the currently inserted disk;
+ * NULL if no disk is inserted.
+ */
+ D64Archive *convertToD64();
+
+ //! @brief Exports the currently inserted disk to D64 file.
+ bool exportToD64(const char *filename);
+
+
//
//! @functiongroup Running the device
//
@@ -182,7 +189,7 @@ public:
via1.execute();
via2.execute();
- uint8_t result = cpu->executeOneCycle();
+ uint8_t result = cpu.executeOneCycle();
// Only proceed if drive is active
if (!rotating)
@@ -207,13 +214,13 @@ public:
private:
- /*! @brief Helper method for executeOneCycle
- * @discussion Method is executed whenever a single bit is ready
+ /*! @brief Helper method for executeOneCycle
+ * @details Method is executed whenever a single bit is ready
*/
void executeBitReady();
- /*! @brief Helper method for executeBitReady
- * @discussion Method is executed whenever a single byte is ready
+ /*! @brief Helper method for executeBitReady
+ * @details Method is executed whenever a single byte is ready
*/
void executeByteReady();
@@ -224,28 +231,31 @@ private:
private:
- //! Indicates whether disk is rotating or not
+ //! @brief Indicates whether disk is rotating or not
bool rotating;
- //! Indicates whether red LED is on or off
+ //! @brief Indicates whether red LED is on or off
bool redLED;
- /*! @brief Indicates whether a disk is inserted
- * @note A fully inserted disk blocks the write protection barrier if it is write protected */
+ /*! @brief Indicates whether a disk is inserted
+ * @note A fully inserted disk blocks the write protection barrier if it is write protected
+ */
bool diskInserted;
- /*! @brief Indicates whether a disk is inserted only partially
- * @note A partially inserted disk blocks always blocks the write protection barrier */
+ /*! @brief Indicates whether a disk is inserted only partially
+ * @note A partially inserted disk blocks always blocks the write protection barrier
+ */
bool diskPartiallyInserted;
- /*! @brief Indicates whether VC1541 is simulated on the bit level
- * @discussion Bit level simulation is the standard emulation mode. If it is disabled, the
- * emulator uses a fast load mechanism to make disk data available whenever the
- * VC1541 DOS waits for it. Right now, this is an experimental feature. Note, that
- * writing to disk is only works when bit level emulation is enabled. */
+ /*! @brief Indicates whether VC1541 is simulated on the bit level
+ * @details Bit level simulation is the standard emulation mode. If it is disabled, the
+ * emulator uses a fast load mechanism to make disk data available whenever the
+ * VC1541 DOS waits for it. Right now, this is an experimental feature. Note, that
+ * writing to disk is only works when bit level emulation is enabled.
+ */
bool bitAccuracy;
- //! Indicates whether the VC1541 shall provide sound notification messages to the GUI
+ //! @brief Indicates whether the VC1541 shall provide sound notification messages to the GUI
bool sendSoundMessages;
@@ -255,111 +265,114 @@ private:
private:
- //! The next bit will be ready after this number of cycles
+ //! @brief The next bit will be ready after this number of cycles.
int16_t bitReadyTimer;
- /*!
- @brief Serial load signal
- @abstract The VC1541 logic board contains a 4-bit-counter of type 72LS191 which is advanced whenever
- a bit is ready. By reaching 7, the counter signals that a byte is ready. In that case,
- the write shift register is loaded with new data and the byte ready signal, which is connected
- to CA1 of VIA2, changes state. In read mode, this state change will feed the input latch of VIA2
- with the current contents of the read shift register.
+ /*! @brief Serial load signal
+ * @details The VC1541 logic board contains a 4-bit-counter of type 72LS191 which is advanced whenever
+ * a bit is ready. By reaching 7, the counter signals that a byte is ready. In that case,
+ * the write shift register is loaded with new data and the byte ready signal, which is connected
+ * to CA1 of VIA2, changes state. In read mode, this state change will feed the input latch of VIA2
+ * with the current contents of the read shift register.
*/
uint8_t byteReadyCounter;
- //! Halftrack position of the read/write head
+ //! @brief Halftrack position of the read/write head
Halftrack halftrack;
- //! Bit position of the read/write head inside the current track
+ //! @brief Bit position of the read/write head inside the current track
uint16_t bitoffset;
- /*! @brief Current disk zone
- * @discussion Each track belongs to one of four zones. Whenever the drive moves the r/w head,
- * it computed the new number and writes into PB5 and PB6 of via2. These bits are
- * hard-wired to a 74LS193 counter on the logic board that breaks down the 16 Mhz base
- * frequency. This mechanism is used to slow down the read/write process on inner tracks.
+ /*! @brief Current disk zone
+ * @details Each track belongs to one of four zones. Whenever the drive moves the r/w head,
+ * it computed the new number and writes into PB5 and PB6 of via2. These bits are
+ * hard-wired to a 74LS193 counter on the logic board that breaks down the 16 Mhz base
+ * frequency. This mechanism is used to slow down the read/write process on inner tracks.
*/
uint8_t zone;
- /*! @brief The 74LS164 serial to parallel shift register
- * @discussion In read mode, this register is fed by the drive head with data.
+ /*! @brief The 74LS164 serial to parallel shift register
+ * @details In read mode, this register is fed by the drive head with data.
*/
uint16_t read_shiftreg;
- /*! @brief The 74LS165 parallel to serial shift register
- * @discussion In write mode, this register feeds the drive head with data.
+ /*! @brief The 74LS165 parallel to serial shift register
+ * @details In write mode, this register feeds the drive head with data.
*/
uint8_t write_shiftreg;
- /*! @brief Current value of the SYNC signal
- @discussion This signal plays an important role for timing synchronization. It becomes true when the
- beginning of a SYNC is detected. On the logic board, the SYNC signal is computed by a NAND gate
- that combines the 10 previously read bits rom the input shift register and CB2 of VIA2 (the
- r/w mode pin). Connecting CB2 to the NAND gates ensures that SYNC can only be true in read mode.
- When SYNC becomes false (meaning that a 0 was pushed into the shift register), the byteReadyCounter
- is reset.
+ /*! @brief Current value of the SYNC signal
+ * @details This signal plays an important role for timing synchronization. It becomes true when the
+ * beginning of a SYNC is detected. On the logic board, the SYNC signal is computed by a NAND gate
+ * that combines the 10 previously read bits rom the input shift register and CB2 of VIA2 (the
+ * r/w mode pin). Connecting CB2 to the NAND gates ensures that SYNC can only be true in read mode.
+ * When SYNC becomes false (meaning that a 0 was pushed into the shift register), the byteReadyCounter
+ * is reset.
*/
bool sync;
public:
- //! @brief Returns true iff drive is currently in read mode
+ //! @brief Returns true iff drive is currently in read mode
bool readMode() { return (via2.io[0x0C] & 0x20); }
- //! @brief Returns true iff drive is currently in write mode
+ //! @brief Returns true iff drive is currently in write mode
bool writeMode() { return !(via2.io[0x0C] & 0x20); }
- //! @brief Moves head one halftrack up
+ //! @brief Moves head one halftrack up
void moveHeadUp();
- //! @brief Moves head one halftrack down
+ //! @brief Moves head one halftrack down
void moveHeadDown();
- //! @brief Returns the current value of the sync signal
+ //! @brief Returns the current value of the sync signal
inline bool getSync() { return sync; }
- //! @brief Returns the current track zone (0 to 3)
+ //! @brief Returns the current track zone (0 to 3)
inline bool getZone() { return zone; }
- //! @brief Sets the current track zone
- /*! @param z drive zone (0 to 3) */
+ /*! @brief Sets the current track zone
+ * @param z drive zone (0 to 3)
+ */
void setZone(uint8_t z);
- //! Triggers an ATN interrupt
- /*! This function is called by the IEC bus when the ATN signal raises. */
+ /*! @brief Triggers an ATN interrupt
+ * @details This function is called by the IEC bus when the ATN signal raises.
+ */
void simulateAtnInterrupt();
private:
- /// @abstract Reads a single bit from the disk head
- /// @result 0 or 1
+ /*! @brief Reads a single bit from the disk head
+ * @result 0 or 1
+ */
inline uint8_t readBitFromHead() { return disk.readBitFromHalftrack(halftrack, bitoffset); }
- /// @abstract Reads a single byte from the disk head
- /// @result 0 ... 255
+ /*! @brief Reads a single byte from the disk head
+ * @result 0 ... 255
+ */
inline uint8_t readByteFromHead() { return disk.readByteFromHalftrack(halftrack, bitoffset); }
- /*! @brief Writes a single bit to the disk head */
+ //! @brief Writes a single bit to the disk head
inline void writeBitToHead(uint8_t bit) { disk.writeBitToHalftrack(halftrack, bitoffset, bit); }
- /*! @brief Writes a single byte to the disk head */
+ //! @brief Writes a single byte to the disk head
inline void writeByteToHead(uint8_t byte) { disk.writeByteToHalftrack(halftrack, bitoffset, byte); }
- /*! @brief Advances drive head position by one bit */
+ //! @brief Advances drive head position by one bit
inline void rotateDisk() { if (++bitoffset >= disk.length.halftrack[halftrack]) bitoffset = 0; }
- /*! @brief Moves drive head position back by one bit */
+ //! @brief Moves drive head position back by one bit
inline void rotateBack() { bitoffset = (bitoffset > 0) ? (bitoffset - 1) : (disk.length.halftrack[halftrack] - 1); }
- /*! @brief Advances drive head position by eight bits */
+ //! @brief Advances drive head position by eight bits
inline void rotateDiskByOneByte() { for (unsigned i = 0; i < 8; i++) rotateDisk(); }
- /*! @brief Moves drive head position back by eight bits */
+ //! @brief Moves drive head position back by eight bits
inline void rotateBackByOneByte() { for (unsigned i = 0; i < 8; i++) rotateBack(); }
- /*! @brief Align drive head to the beginning of a byte */
+ //! @brief Align drive head to the beginning of a byte
inline void alignHead() { bitoffset &= 0xFFF8; byteReadyCounter = 0; }
//! @brief Signals the CPU that a byte has been processed
@@ -370,17 +383,17 @@ private:
public:
- /*! @brief Performs read access of the fast loader
- * @abstract This method is used to latch in a byte from disk when bit accurate emulation is disabled
+ /*! @brief Performs read access of the fast loader
+ * @details This method is used to latch in a byte from disk when bit accurate emulation is disabled
*/
void fastLoaderRead();
- /*! @brief Fast loader sync detection
- * @abstract Returns true when the drive head is currently inside a SYNC mark
+ /*! @brief Fast loader sync detection
+ * @details Returns true when the drive head is currently inside a SYNC mark
*/
bool getFastLoaderSync();
- /*! @brief Skip sync mark (for d */
+ //! @brief Skip sync mark
inline void fastLoaderSkipSyncMark() { while (readByteFromHead() == 0xFF) rotateDiskByOneByte(); }
};
diff --git a/C64/VC1541Memory.cpp b/C64/VC1541Memory.cpp
old mode 100755
new mode 100644
index d5dd0b6..eec6ec2
--- a/C64/VC1541Memory.cpp
+++ b/C64/VC1541Memory.cpp
@@ -20,8 +20,8 @@
VC1541Memory::VC1541Memory()
{
- name = "1541MEM";
- debug(2, " Creating VC1541 memory at %p...\n", this);
+ setDescription("1541MEM");
+ debug(3, " Creating VC1541 memory at %p...\n", this);
// Register snapshot items
SnapshotItem items[] = {
@@ -37,7 +37,7 @@ VC1541Memory::VC1541Memory()
VC1541Memory::~VC1541Memory()
{
- debug(2, " Releasing VC1541 memory at %p...\n", this);
+ debug(3, " Releasing VC1541 memory at %p...\n", this);
}
void
@@ -46,9 +46,9 @@ VC1541Memory::reset()
VirtualComponent::reset();
// Establish bindings
- cpu = c64->cpu;
- iec = c64->iec;
- floppy = c64->floppy;
+ cpu = &c64->cpu;
+ iec = &c64->iec;
+ floppy = &c64->floppy;
}
bool
diff --git a/C64/VC1541Memory.h b/C64/VC1541Memory.h
old mode 100755
new mode 100644
index 5c3e5d2..3728d3f
--- a/C64/VC1541Memory.h
+++ b/C64/VC1541Memory.h
@@ -1,7 +1,9 @@
-/*
- * (C) 2008 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
+/*!
+ * @header VC151Memory.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2008 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -23,59 +25,60 @@
class VC1541;
-//! This class represents the RAM and ROM of a virtual VC1541 floopy disk drive
+/*! @brief This class represents the RAM and ROM of a virtual VC1541 floopy disk drive.
+ */
class VC1541Memory : public Memory {
-private:
- //! Reference to the connected IEC bus
+private:
+
+ //! @brief Reference to the connected IEC bus
IEC *iec;
- //! Reference to the connected disk drive
+ //! @brief Reference to the connected disk drive
VC1541 *floppy;
public:
- //! Virtual memory storage
+ //! @brief The VC1541s memory space
uint8_t mem[65536];
- //! File name of the ROM image.
- /*! The file name is set by the loadRom routine. It is saved for further reference, so the ROM can be reloaded any time. */
+ /*! @brief File name of the VC1541 ROM image.
+ * @details The file name is set in loadRom(). It is saved for further reference, so the ROM can be reloaded
+ * any time.
+ */
char *romFile;
- //! Check integrity of ROM image
- /*! Returns true, iff the specified file contains a valid VC1541 ROM image.
- File integrity is checked via the checkFileHeader function.
- \param filename Name of the file being loaded
- \see C64:loadRom
- */
+ /*! @brief Checks the integrity of a VC1541 ROM image.
+ * @details Returns true, iff the specified file contains a valid VC1541 ROM image.
+ * File integrity is checked via the checkFileHeader function.
+ */
static bool is1541Rom(const char *filename);
public:
- //! Constructor
+ //! @brief Constructor
VC1541Memory();
- //! Destructor
+ //! @brief Destructor
~VC1541Memory();
- //! Restore initial state
+ //! @brief Restores the initial state.
void reset();
- //! Dump current state into logfile
+ //! @brief Prints debugging information
void dumpState();
- //! Load a ROM image into memory.
- /*! The function automatically determines the type of the specified file. In case of a valid ROM image, it
- is loaded into the ROM space at the proper location.
- \param filename Filename of the ROM Image
- \return Returns true, if the file could be loaded successfully. In case of an error (file not found,
- the file is no proper ROM image, ...) the function returns false.
- */
+ /*! @brief Loads a ROM image into memory.
+ * @details The function automatically determines the type of the specified file. In case of a valid ROM image,
+ * it is loaded into the ROM space at the proper location.
+ * @return Returns true, if the file could be loaded successfully. In case of an error (file not found,
+ * the file is no proper ROM image, ...) the function returns false.
+ */
bool loadRom(const char *filename);
- //! Returns true, iff the ROM image is alrady loaded
+ //! @brief Returns true, iff the ROM image is alrady loaded
bool romIsLoaded() { return romFile != NULL; }
- // Virtual fuctions that need to be implemented...
+ // Virtual fuctions from Memory class
bool isValidAddr(uint16_t addr, MemoryType type);
uint8_t peekRam(uint16_t addr);
uint8_t peekRom(uint16_t addr);
diff --git a/C64/VIA6522.cpp b/C64/VIA6522.cpp
old mode 100755
new mode 100644
index 58042fd..9ba2e9a
--- a/C64/VIA6522.cpp
+++ b/C64/VIA6522.cpp
@@ -26,7 +26,7 @@
VIA6522::VIA6522()
{
- name = "VIA";
+ setDescription("VIA");
// Register snapshot items
SnapshotItem items[] = {
@@ -58,7 +58,7 @@ void VIA6522::reset()
VirtualComponent::reset();
// Establish bindings
- floppy = c64->floppy;
+ floppy = &c64->floppy;
}
void
@@ -149,10 +149,10 @@ VIA6522::executeTimer2()
bool
VIA6522::IRQ() {
if (io[0xD] /* IFR */ & io[0xE] /* IER */) {
- floppy->cpu->setIRQLineVIA();
+ floppy->cpu.setIRQLineVIA();
return true;
} else {
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return false;
}
}
@@ -185,7 +185,7 @@ VIA6522::peek(uint16_t addr)
// IS RESET (BIT 6 IN INTERRUPT FLAG REGISTER)" [F. K.]
clearInterruptFlag_T1();
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return LO_BYTE(t1);
case 0x5: // T1 high-order counter
@@ -211,7 +211,7 @@ VIA6522::peek(uint16_t addr)
// "8 BITS FROM T2 LOW-ORDER COUNTER TRANSFERRED TO MPU. T2 INTERRUPT FLAG IS RESET" [F. K.]
clearInterruptFlag_T2();
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return LO_BYTE(t2);
case 0x9: // T2 high-order counter COUNTER TRANSFERRED TO MPU" [F. K.]
@@ -299,7 +299,7 @@ void VIA6522::poke(uint16_t addr, uint8_t value)
t1 = HI_LO(t1_latch_hi, t1_latch_lo);
clearInterruptFlag_T1();
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return;
case 0x6: // T1 low-order latct
@@ -324,7 +324,7 @@ void VIA6522::poke(uint16_t addr, uint8_t value)
t2_latch_lo = value;
clearInterruptFlag_T2();
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return;
case 0x9: // T2 high-order counter
@@ -334,7 +334,7 @@ void VIA6522::poke(uint16_t addr, uint8_t value)
t2 = HI_LO(value, t2_latch_lo);
clearInterruptFlag_T2();
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return;
case 0xA: // Shift register
@@ -427,7 +427,7 @@ uint8_t VIA1::peek(uint16_t addr)
clearInterruptFlag_CA2();
// Clean this up ...
- floppy->cpu->clearIRQLineATN();
+ floppy->cpu.clearIRQLineATN();
return ora;
default:
@@ -550,7 +550,7 @@ uint8_t VIA2::peek(uint16_t addr)
}
case 0x4:
- floppy->cpu->clearIRQLineVIA();
+ floppy->cpu.clearIRQLineVIA();
return VIA6522::peek(addr);
default:
@@ -650,24 +650,24 @@ void VIA2::poke(uint16_t addr, uint8_t value)
VIA1::VIA1()
{
- name = "VIA1";
- debug(2, " Creating VIA1 at address %p...\n", this);
+ setDescription("VIA1");
+ debug(3, " Creating VIA1 at address %p...\n", this);
}
VIA1::~VIA1()
{
- debug(2, " Releasing VIA1...\n");
+ debug(3, " Releasing VIA1...\n");
}
VIA2::VIA2()
{
- name = "VIA2";
- debug(2, " Creating VIA2 at address %p...\n", this);
+ setDescription("VIA2");
+ debug(3, " Creating VIA2 at address %p...\n", this);
}
VIA2::~VIA2()
{
- debug(2, " Releasing VIA2...\n");
+ debug(3, " Releasing VIA2...\n");
}
void VIA2::debug0xC() {
@@ -684,8 +684,8 @@ void VIA2::debug0xC() {
case 3: debug(2," INDEPENDENT INTERRUPT INPUT POSITIVE EDGE\n"); break;
case 4: debug(2," HANDSHAKE OUTPUT\n"); break;
case 5: debug(2," PULSE OUTPUT\n"); break;
- case 6: debug(2," LOW OUTPUT %04X\n", floppy->cpu->getPC_at_cycle_0()); break;
- case 7: debug(2," HIGH OUTPUT %04X\n", floppy->cpu->getPC_at_cycle_0()); break;
+ case 6: debug(2," LOW OUTPUT %04X\n", floppy->cpu.getPC_at_cycle_0()); break;
+ case 7: debug(2," HIGH OUTPUT %04X\n", floppy->cpu.getPC_at_cycle_0()); break;
}
debug(2,"CB1:\n");
diff --git a/C64/VIA6522.h b/C64/VIA6522.h
old mode 100755
new mode 100644
index 1f99350..d2a1a80
--- a/C64/VIA6522.h
+++ b/C64/VIA6522.h
@@ -28,35 +28,34 @@
class VC1541;
-/*! @brief Virtual VIA6522 controller
- @details The VC1541 drive contains two VIAs on its logic board.
+/*! @brief Virtual VIA6522 controller
+ @details The VC1541 drive contains two VIAs on its logic board.
*/
class VIA6522 : public VirtualComponent {
public:
- //! @brief Reference to the connected disk drive.
+ //! @brief Reference to the connected disk drive.
VC1541 *floppy;
public:
- //! @brief Peripheral ports
- /*! @details
- * "The R6522 VIA has two 8-bit bidirectional I/O ports (Port A and Port B)
- * and each port has two associated control lines.
+ /*! @brief Peripheral ports
+ * @details "The R6522 VIA has two 8-bit bidirectional I/O ports (Port A and Port B)
+ * and each port has two associated control lines.
*
- * Each 8-bit peripheral port has a Data Direction Register (DDRA, DDRB) for
- * specifying whether the peripheral pins are to act as inputs or outputs. A 0
- * in a bit of the Data Direction Register causes the corresponding peripheral
- * pin to act as an input. A 1 causes the pin to act as an output.
+ * Each 8-bit peripheral port has a Data Direction Register (DDRA, DDRB) for
+ * specifying whether the peripheral pins are to act as inputs or outputs. A 0
+ * in a bit of the Data Direction Register causes the corresponding peripheral
+ * pin to act as an input. A 1 causes the pin to act as an output.
*
- * Each peripheral pin is also controlled by a bit in the Output Register
- * (ORA, ORB) and the Input Register (IRA, IRB). When the pin is programmed as
- * an output, the voltage on the pin is controlled by the corresponding bit of
- * the Output Register. A 1 in the Output Register causes the output to go
- * high, and a 0 causes the output to go low. Data may be written into Output
- * Register bits corresponding to pins which are programmed as inputs. In this
- * case, however, the output signal is unaffected." [F. K.]
+ * Each peripheral pin is also controlled by a bit in the Output Register
+ * (ORA, ORB) and the Input Register (IRA, IRB). When the pin is programmed as
+ * an output, the voltage on the pin is controlled by the corresponding bit of
+ * the Output Register. A 1 in the Output Register causes the output to go
+ * high, and a 0 causes the output to go low. Data may be written into Output
+ * Register bits corresponding to pins which are programmed as inputs. In this
+ * case, however, the output signal is unaffected." [F. K.]
*/
uint8_t ddra, ddrb;
uint8_t ira, irb;
@@ -65,83 +64,91 @@ public:
// protected:
public:
- //! VIA I/O Memory
- /*! Whenever a value is poked to the VIA address space, it is stored here. */
+ /*! @brief VIA I/O Memory
+ * @details Whenever a value is poked to the VIA address space, it is stored here.
+ */
uint8_t io[16];
- //! VIA timer 1
- /*! Interval Timer T1 consists of two 8-bit latches and a 16-bit
- counter. The latches store data which is to be loaded into the
- counter. After loading, the counter decrements at 02 clock rate. Upon
- reaching zero, an interrupt flag is set, and IRQ goes low if the T1
- interrupt is enabled. Timer 1 then disables any further interrupts or
- automatically transfers the contents of the latches into the counter and
- continues to decrement. In addition, the timer may be programmed to invert
- the output signal on a peripheral pin (PB7) each time it "times-out". Each
- of these modes is discussed separately below.
- */
-
+ /*! @brief VIA timer 1
+ * @details "Interval Timer T1 consists of two 8-bit latches and a 16-bit
+ * counter. The latches store data which is to be loaded into the
+ * counter. After loading, the counter decrements at 02 clock rate. Upon
+ * reaching zero, an interrupt flag is set, and IRQ goes low if the T1
+ * interrupt is enabled. Timer 1 then disables any further interrupts or
+ * automatically transfers the contents of the latches into the counter and
+ * continues to decrement. In addition, the timer may be programmed to invert
+ * the output signal on a peripheral pin (PB7) each time it "times-out". Each
+ * of these modes is discussed separately below." [F. K.]
+ */
uint16_t t1;
uint8_t t1_latch_lo, t1_latch_hi;
- //! VIA timer 2
- /*! Timer 2 operates as an interval timer (in the "one-shot" mode only), or as
- a counter for counting negative pulses on the PB6 peripheral pin. A single
- control bit in the Auxiliary Control Register selects between these two
- modes. This timer is comprised of a "write-only" low-order latch (T2L-L), a
- "read-only" low-order counter (T2C-L) and a read/write high order counter
- (T2C-H). The counter registers act as a 16-bit counter which decrements at
- 02 rate.
- */
+ /*! @brief VIA timer 2
+ * @details "Timer 2 operates as an interval timer (in the "one-shot" mode only), or as
+ * a counter for counting negative pulses on the PB6 peripheral pin. A single
+ * control bit in the Auxiliary Control Register selects between these two
+ * modes. This timer is comprised of a "write-only" low-order latch (T2L-L), a
+ * "read-only" low-order counter (T2C-L) and a read/write high order counter
+ * (T2C-H). The counter registers act as a 16-bit counter which decrements at
+ * 02 rate." [F. K.]
+ */
uint16_t t2;
uint8_t t2_latch_lo;
- //! Indicates that timer 1 or timer 2 has reached zero
+ //! @brief Indicates whether timer 1 has reached zero.
bool t1_underflow;
+
+ //! @brief Indicates whether timer 2 has reached zero.
bool t2_underflow;
+
public:
- //! Constructor
+ //! @brief Constructor
VIA6522();
- //! Destructor
+ //! @brief Destructor
~VIA6522();
- //! Bring the VIA back to its initial state
+ //! @brief Brings the VIA back to its initial state.
void reset();
- //! Dump debug information
+ //! @brief Dumps debug information.
void dumpState();
- //! Execute virtual VIA for one cycle
+ //! @brief Executes the virtual VIA for one cycle.
inline void execute() {
if (t1 || t1_underflow) executeTimer1();
if (t2 || t2_underflow) executeTimer2();
}
- //! Execution function for timer 1
+ //! @brief Executes timer 1 for one cycle.
void executeTimer1();
- //! Execution function for timer 2
+ //! @brief Executes timer 2 for one cycle.
void executeTimer2();
- //! Special peek function for the I/O memory range
- /*! The peek function only handles those registers that are treated similarily by both VIA chips */
+ /*! @brief Special peek function for the I/O memory range
+ * @details The peek function only handles those registers that are treated similarily by both VIA chips
+ */
virtual uint8_t peek(uint16_t addr);
- //! Special poke function for the I/O memory range
- /*! The poke function only handles those registers that are treated similarily by both VIA chips */
+ /*! @brief Special poke function for the I/O memory range
+ * @details The poke function only handles those registers that are treated similarily by both VIA chips
+ */
virtual void poke(uint16_t addr, uint8_t value);
+
// -----------------------------------------------------------------------------------------------
// Internal Configuration
// -----------------------------------------------------------------------------------------------
- //! Returns true iff timer 1 is in free-run mode (continous interrupts)
+ //! @brief Returns true iff timer 1 is in free-run mode (continous interrupts)
bool freeRunMode1() { return (io[0x0B] & 0x40) != 0; }
- //! Check if input latching is enabled
+ //! @brief Checks if input latching is enabled
bool inputLatchingEnabledA() { return (GET_BIT(io[0x0B],0)); }
+
+ //! @brief Checks if input latching is enabled
bool inputLatchingEnabledB() { return (GET_BIT(io[0x0B],1)); }
@@ -164,9 +171,10 @@ public:
// Interrupt handling
// -----------------------------------------------------------------------------------------------
- // Returns the value of the IRQ pin
- // This method updates the IRQ pin of the connected CPU as a side effect and is therefore
- // invoked on every change in register IFR or register IER.
+ /*! @brief Returns the value of the IRQ pin
+ * @details This method updates the IRQ pin of the connected CPU as a side effect and is therefore
+ * invoked on every change in register IFR or register IER.
+ */
bool IRQ();
//
@@ -229,25 +237,32 @@ class VIA1 : public VIA6522 {
public:
- //! Constructor
+ //! @brief Constructor
VIA1();
- //! Destructor
+ //! @brief Destructor
~VIA1();
- //! Execution function for timer 1
+ //! @brief Executes timer 1 for one cycle
void executeTimer1();
- //! Execution function for timer 2
+ //! @brief Executes timer 2 for one cycle
void executeTimer2();
+ //! @brief Peeks a value from VIAs I/O space
uint8_t peek(uint16_t addr);
- void poke(uint16_t addr, uint8_t value);
+
+ //! @brief Pokes a value into VIAs I/O space
+ void poke(uint16_t addr, uint8_t value);
-
+ //! @brief Returns true iff a change of the atn line can trigger interrups
inline bool atnInterruptsEnabled() { return io[0x0E] & 0x02; }
+
+ //! @brief Indicates that an ATN interrupt has occured.
inline void indicateAtnInterrupt() { io[0x0D] |= 0x02; }
- inline void clearAtnIndicator() { io[0x0D] &= (0xFF-0x02); }
+
+ //! @brief Clears the ATN interrupt indication bit.
+ inline void clearAtnIndicator() { io[0x0D] &= (0xFF-0x02); }
};
//! The second versatile interface adapter (VIA2)
@@ -255,26 +270,35 @@ class VIA2 : public VIA6522 {
public:
- //! Constructor
+ //! @brief Constructor
VIA2();
- //! Destructor
+ //! @brief Destructor
~VIA2();
- //! Execution function for timer 1
+ //! @brief Executes timer 1 for one cycle
void executeTimer1();
- //! Execution function for timer 2
+ //! @brief Executes timer 2 for one cycle
void executeTimer2();
+ //! @brief Peeks a value from VIAs I/O space
uint8_t peek(uint16_t addr);
+
+ //! @brief Pokes a value into VIAs I/O space
void poke(uint16_t addr, uint8_t value);
+ //! @brief Returns bit 0 of output register B
bool stepperActive0() { return (orb & 0x01) != 0; }
+
+ //! @brief Returns bit 1 of output register B
bool stepperActive1() { return (orb & 0x02) != 0; }
+
+ //! @brief Returns bit 2 of output register B
bool engineRunning() { return (orb & 0x04) != 0; }
- bool redLEDshining() { return (orb & 0x08) != 0; }
+ //! @brief Returns bit 3 of output register B
+ bool redLEDshining() { return (orb & 0x08) != 0; }
bool overflowEnabled() { return (io[0x0C] & 0x02); }
diff --git a/C64/VIC.cpp b/C64/VIC.cpp
index 115037d..6d95de1 100755
--- a/C64/VIC.cpp
+++ b/C64/VIC.cpp
@@ -26,8 +26,8 @@
VIC::VIC()
{
- name = "VIC";
- debug(2, " Creating VIC at address %p...\n", this);
+ setDescription("VIC");
+ debug(3, " Creating VIC at address %p...\n", this);
// Start with all debug options disabled
markIRQLines = false;
@@ -103,17 +103,11 @@ void
VIC::reset()
{
VirtualComponent::reset();
-
- // Establish bindungs
- cpu = c64->cpu;
- mem = c64->mem;
// Internal state
yCounter = PAL_HEIGHT;
bp.borderColor = PixelEngine::LTBLUE; // Let the border color look correct right from the beginning
- // iomem[0x20] = PixelEngine::LTBLUE; // Let the border color look correct right from the beginning
cp.backgroundColor[0] = PixelEngine::BLUE; // Let the background color look correct right from the beginning
- // iomem[0x21] = PixelEngine::BLUE; // Let the background color look correct right from the beginning
setScreenMemoryAddr(0x400); // Remove startup graphics glitches by setting the initial value early
p.registerCTRL1 = 0x10; // Make screen visible from the beginning
expansionFF = 0xFF;
@@ -226,10 +220,10 @@ uint8_t VIC::memAccess(uint16_t addr)
// Accessing range 0x1000 - 0x1FFF or 0x9000 - 0x9FFF
// Character ROM is blended in here
assert ((0xC000 + addr) >= 0xD000 && (0xC000 + addr) <= 0xDFFF);
- dataBus = mem->rom[0xC000 + addr];
+ dataBus = c64->mem.rom[0xC000 + addr];
} else {
- dataBus = mem->ram[addrBus];
+ dataBus = c64->mem.ram[addrBus];
}
return dataBus;
@@ -239,7 +233,7 @@ uint8_t VIC::memIdleAccess()
{
// return memAccess(0x3FFF);
addrBus = bankAddr + 0x3FFF;
- return mem->ram[addrBus];
+ return c64->mem.ram[addrBus];
}
inline void VIC::cAccess()
@@ -255,7 +249,7 @@ inline void VIC::cAccess()
uint16_t addr = (VM13VM12VM11VM10() << 6) | registerVC;
characterSpace[registerVMLI] = memAccess(addr);
- colorSpace[registerVMLI] = mem->colorRam[registerVC] & 0x0F;
+ colorSpace[registerVMLI] = c64->mem.colorRam[registerVC] & 0x0F;
}
// VIC has no access, yet
@@ -278,7 +272,7 @@ inline void VIC::cAccess()
Erst danach werden wieder regul�re Videomatrixdaten gelesen." [C.B.] */
characterSpace[registerVMLI] = 0xFF;
- colorSpace[registerVMLI] = c64->mem->ram[cpu->getPC()] & 0x0F;
+ colorSpace[registerVMLI] = c64->mem.ram[c64->cpu.getPC()] & 0x0F;
}
}
@@ -345,33 +339,27 @@ inline void VIC::pAccess(unsigned sprite)
}
-// TODO: Change return type to void
-inline bool VIC::sFirstAccess(unsigned sprite)
+inline void VIC::sFirstAccess(unsigned sprite)
{
assert(sprite < 8);
uint8_t data = 0x00; // TODO: VICE is doing this: vicii.last_bus_phi2;
- bool memAccessed = false;
isFirstDMAcycle = (1 << sprite);
if (spriteDmaOnOff & (1 << sprite)) {
- if (BApulledDownForAtLeastThreeCycles()) {
+ if (BApulledDownForAtLeastThreeCycles())
data = memAccess(spritePtr[sprite] | mc[sprite]);
- memAccessed = true;
- }
mc[sprite]++;
mc[sprite] &= 0x3F; // 6 bit overflow
}
pixelEngine.sprite_sr[sprite].chunk1 = data;
- return memAccessed;
}
-// TODO: Change return type to void
-inline bool VIC::sSecondAccess(unsigned sprite)
+inline void VIC::sSecondAccess(unsigned sprite)
{
assert(sprite < 8);
@@ -398,31 +386,24 @@ inline bool VIC::sSecondAccess(unsigned sprite)
memIdleAccess();
pixelEngine.sprite_sr[sprite].chunk2 = data;
- return memAccessed;
}
-// TODO: Change return type to void
-inline bool VIC::sThirdAccess(unsigned sprite)
+inline void VIC::sThirdAccess(unsigned sprite)
{
assert(sprite < 8);
uint8_t data = 0x00; // TODO: VICE is doing this: vicii.last_bus_phi2;
- bool memAccessed = false;
if (spriteDmaOnOff & (1 << sprite)) {
- if (BApulledDownForAtLeastThreeCycles()) {
+ if (BApulledDownForAtLeastThreeCycles())
data = memAccess(spritePtr[sprite] | mc[sprite]);
- memAccessed = true;
- }
mc[sprite]++;
mc[sprite] &= 0x3F; // 6 bit overflow
}
pixelEngine.sprite_sr[sprite].chunk3 = data;
-
- return memAccessed;
}
@@ -671,7 +652,7 @@ VIC::poke(uint16_t addr, uint8_t value)
case 0x19: // IRQ flags
// A bit is cleared when a "1" is written
iomem[addr] &= (~value & 0x0f);
- cpu->clearIRQLineVIC();
+ c64->cpu.clearIRQLineVIC();
if (iomem[addr] & iomem[0x1a])
iomem[addr] |= 0x80;
return;
@@ -710,10 +691,10 @@ VIC::poke(uint16_t addr, uint8_t value)
iomem[addr] = value & 0x0f;
if (iomem[addr] & iomem[0x19]) {
iomem[0x19] |= 0x80; // set uppermost bit (is directly connected to the IRQ line)
- cpu->setIRQLineVIC();
+ c64->cpu.setIRQLineVIC();
} else {
iomem[0x19] &= 0x7f; // clear uppermost bit
- cpu->clearIRQLineVIC();
+ c64->cpu.clearIRQLineVIC();
}
return;
@@ -771,7 +752,7 @@ VIC::setBAlow(uint8_t value)
BAwentLowAtCycle = c64->getCycles();
BAlow = value;
- cpu->setRDY(value == 0);
+ c64->cpu.setRDY(value == 0);
}
inline bool
@@ -787,7 +768,7 @@ VIC::triggerIRQ(uint8_t source)
if (iomem[0x1A] & source) {
// Interrupt is enabled
iomem[0x19] |= 128;
- cpu->setIRQLineVIC();
+ c64->cpu.setIRQLineVIC();
// debug("Interrupting at rasterline %x %d\n", yCounter, yCounter);
}
}
@@ -1018,12 +999,13 @@ VIC::endRasterline()
pixelEngine.endRasterline();
}
-bool
+inline bool
VIC::yCounterOverflow()
{
// PAL machines reset yCounter in cycle 2 in the first physical rasterline
// NTSC machines reset yCounter in cycle 2 in the middle of the lower border area
- return (c64->isPAL() && c64->getRasterline() == 0) || (!c64->isPAL() && c64->getRasterline() == 238);
+ // return (c64->isPAL() && c64->getRasterline() == 0) || (!c64->isPAL() && c64->getRasterline() == 238);
+ return c64->getRasterline() == (c64->isPAL() ? 0 : 238);
}
void
diff --git a/C64/VIC.h b/C64/VIC.h
index 47674f6..bb97e7f 100755
--- a/C64/VIC.h
+++ b/C64/VIC.h
@@ -1,21 +1,22 @@
-/*
- * (C) 2006 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header VIC.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-
#ifndef _VIC_INC
#define _VIC_INC
@@ -28,28 +29,24 @@ class C64Memory;
class PixelEngine;
-/*! @brief Virtual Video Controller (VICII)
- * @discussion VICII is the video controller chip of the Commodore 64.
- * VICII occupied the memory mapped I/O space from address 0xD000 to 0xD02E. */
+/*! @brief Virtual Video Controller (VICII)
+ * @details VICII is the video controller chip of the Commodore 64.
+ * It occupies the memory mapped I/O space from address 0xD000 to 0xD02E.
+ */
class VIC : public VirtualComponent {
friend class PixelEngine;
private:
- // Reference to the attached pixel engine (PE). The PE encapsulates all drawing related
- // routines in a seperate class.
+ /*! @brief Reference to the attached pixel engine (PE).
+ * @details The PE encapsulates all drawing related routines in a seperate class.
+ */
PixelEngine pixelEngine;
- //! Reference to the connected CPU
- CPU *cpu;
-
- //! Reference to the connected virtual memory
- C64Memory *mem;
-
public:
- //! Dump current configuration into message queue
+ //! @brief Dump current configuration into message queue
void ping();
@@ -57,99 +54,108 @@ public:
// Internal state
// -----------------------------------------------------------------------------------------------
- //! @brief Main pixel engine pipe
+ //! @brief Main pixel engine pipe
PixelEnginePipe p;
- //! @brief Color pipes
+ //! @brief Color pipes
BorderColorPipe bp;
CanvasColorPipe cp;
SpriteColorPipe sp;
- //! Selected chip model (determines whether video mode is PAL or NTSC)
+ //! @brief Selected chip model (determines whether video mode is PAL or NTSC)
VICChipModel chipModel;
- //! Indicates whether the currently drawn rasterline belongs to VBLANK area
+ //! @brief Indicates whether the currently drawn rasterline belongs to VBLANK area
bool vblank;
- //! Internal VIC register, 10 bit video counter
+ //! @brief Internal VIC register, 10 bit video counter
uint16_t registerVC;
- //! Internal VIC-II register, 10 bit video counter base
+ //! @brief Internal VIC-II register, 10 bit video counter base
uint16_t registerVCBASE;
- //! Internal VIC-II register, 3 bit row counter
+ //! @brief Internal VIC-II register, 3 bit row counter
uint8_t registerRC;
- //! Internal VIC-II register, 6 bit video matrix line index
+ //! @brief Internal VIC-II register, 6 bit video matrix line index
uint8_t registerVMLI;
- //! Rasterline counter
- /*! The rasterline counter is is usually incremented in cycle 1. The only exception is the
- overflow condition which is handled in cycle 2 */
+ /*! @brief Rasterline counter
+ * @details The rasterline counter is is usually incremented in cycle 1. The only exception is the
+ * overflow condition which is handled in cycle 2
+ */
uint32_t yCounter;
- //! Vertical frame flipflop set condition
- /*! Indicates whether the vertical frame ff needs to be set in current rasterline */
+ /*! @brief Vertical frame flipflop set condition
+ * @details Indicates whether the vertical frame ff needs to be set in current rasterline
+ */
bool verticalFrameFFsetCond;
- //! Vertical frame flipflop clear condition
- /*! Indicates whether the vertical frame ff needs to be cleared in current rasterline */
+ /*! @brief Vertical frame flipflop clear condition
+ * @details Indicates whether the vertical frame ff needs to be cleared in current rasterline
+ */
bool verticalFrameFFclearCond;
- //! DRAM refresh counter
- /*! "In jeder Rasterzeile führt der VIC fünf Lesezugriffe zum Refresh des
- dynamischen RAM durch. Es wird ein 8-Bit Refreshzähler (REF) zur Erzeugung
- von 256 DRAM-Zeilenadressen benutzt." [C.B.] */
+ /*! @brief DRAM refresh counter
+ * @details "In jeder Rasterzeile führt der VIC fünf Lesezugriffe zum Refresh des
+ * dynamischen RAM durch. Es wird ein 8-Bit Refreshzähler (REF) zur Erzeugung
+ * von 256 DRAM-Zeilenadressen benutzt." [C.B.]
+ */
uint8_t refreshCounter;
- //! Address bus
- /*! Whenever VIC performs a memory read, the generated memory address is stored here */
+ /*! @brief Address bus
+ * @details Whenever VIC performs a memory read, the generated memory address is stored here
+ */
uint16_t addrBus;
- //! Data bus
- /*! Whenever VIC performs a memory read, the result is stored here */
+ /*! @brief Data bus
+ * @details Whenever VIC performs a memory read, the result is stored here
+ */
uint8_t dataBus;
- //! Display mode in latest gAccess
+ //! @brief Display mode in latest gAccess
uint8_t gAccessDisplayMode;
- //! Foreground color fetched in latest gAccess
+ //! @brief Foreground color fetched in latest gAccess
uint8_t gAccessfgColor;
- //! Background color fetched in latest gAccess
+ //! @brief Background color fetched in latest gAccess
uint8_t gAccessbgColor;
- //! Indicates that we are curretly processing a DMA line (bad line)
+ //! @brief Indicates that we are curretly processing a DMA line (bad line)
bool badLineCondition;
- //! Determines, if DMA lines (bad lines) can occurr within the current frame.
- /*! Bad lines can only occur, if the DEN bit was set during an arbitary cycle in rasterline 30
- The DEN bit is located in register 0x11 (CONTROL REGISTER 1) */
+ /*! @brief Determines, if DMA lines (bad lines) can occurr within the current frame.
+ * @details Bad lines can only occur, if the DEN bit was set during an arbitary cycle in rasterline 30
+ * The DEN bit is located in register 0x11 (CONTROL REGISTER 1)
+ */
bool DENwasSetInRasterline30;
- //! Display State
- /*! The VIC is either in idle or display state */
+ /*! @brief Display State
+ * @details The VIC is either in idle or display state
+ */
bool displayState;
- //! BA line
- /* Remember: Each CPU cycle is split into two phases
- First phase (LOW): VIC gets access to the bus
- Second phase (HIGH): CPU gets access to the bus
- In rare cases, VIC needs access in the HIGH phase, too. To block the CPU, the BA line is pulled down.
- Note: The BA line can be pulled down by multiple sources (wired AND). */
+ /*! @brief The BA line
+ * @details Remember: Each CPU cycle is split into two phases:
+ * First phase (LOW): VIC gets access to the bus
+ * Second phase (HIGH): CPU gets access to the bus
+ * In rare cases, VIC needs access in the HIGH phase, too. To block the CPU, the BA line
+ * is pulled down.
+ * @note The BA line can be pulled down by multiple sources (wired AND).
+ */
uint16_t BAlow;
- //! Remember at which cycle BA line has been pulled down
+ //! @brief Remember at which cycle BA line has been pulled down
uint64_t BAwentLowAtCycle;
-
- //! Increase x counter by 8
+ //! @brief Increases the X counter by 8
inline void countX() { p.xCounter += 8; }
- //! Returns true if yCounter needs to be reset to 0 in this rasterline
+ //! @brief Returns true if yCounter needs to be reset to 0 in this rasterline
bool yCounterOverflow();
- //! cAccesses can only be performed is BA line is down for more than 2 cycles
+ //! @brief cAccesses can only be performed is BA line is down for more than 2 cycles
bool BApulledDownForAtLeastThreeCycles();
/* "Der VIC benutzt zwei Flipflops, um den Rahmen um das Anzeigefenster
@@ -174,26 +180,34 @@ public:
vertikale Rahmenflipflop gelöscht, wird das Haupt-Flipflop gelöscht." [C.B.]
*/
- //! Takes care of the vertical frame flipflop value.
- /*! Invoked in each VIC II cycle */
+ /*! @brief Takes care of the vertical frame flipflop value.
+ * @details Invoked in each VIC II cycle
+ */
void checkVerticalFrameFF();
- //! Check frame fliplops at left border
+ //! @brief Checks frame fliplops at left border
void checkFrameFlipflopsLeft(uint16_t comparisonValue);
- //! Check frame fliplops at right border
+ //! @brief Checks frame fliplops at right border
void checkFrameFlipflopsRight(uint16_t comparisonValue);
- //! Comparison values for frame flipflops
+ //! @brief Returns in which cycle the frame flipflop is checked for the left border
inline uint16_t leftComparisonValue() { return isCSEL() ? 24 : 31; }
+
+ //! @brief Returns in which cycle the frame flipflop is checked for the right border
inline uint16_t rightComparisonValue() { return isCSEL() ? 344 : 335; }
+
+ //! @brief Returns in which cycle the frame flipflop is checked for the upper border
inline uint16_t upperComparisonValue() { return isRSEL() ? 51 : 55; }
+
+ //! @brief Returns in which cycle the frame flipflop is checked for the lower border
inline uint16_t lowerComparisonValue() { return isRSEL() ? 251 : 247; }
- //! Clear main frame flipflop
- /* "Das vertikale Rahmenflipflop dient zur Unterstützung bei der Darstellung
- des oberen/unteren Rahmens. Ist es gesetzt, kann das Haupt-Rahmenflipflop
- nicht gelöscht werden." [C.B.] */
+ /*! @brief Clear main frame flipflop
+ * @details "Das vertikale Rahmenflipflop dient zur Unterstützung bei der Darstellung
+ * des oberen/unteren Rahmens. Ist es gesetzt, kann das Haupt-Rahmenflipflop
+ * nicht gelöscht werden." [C.B.]
+ */
inline void clearMainFrameFF() { if (!p.verticalFrameFF && !verticalFrameFFsetCond) p.mainFrameFF = false; }
@@ -203,34 +217,39 @@ public:
public:
- //! I/O Memory
- /*! If a value is poked to the VIC address space, it is stored here. */
+ /*! @brief I/O Memory
+ * @details This array is used to store most of the register values that are poked into the
+ * VIC address space. Note that this does not hold for all register values. Some of them
+ * are directly stored inside the state pipe for speedup purposes.
+ */
uint8_t iomem[64];
private:
- //! Start address of the currently selected memory bank
- /*! There are four banks in total since the VIC chip can only "see" 16 KB of memory at one time
- Two bank select bits in the CIA I/O space determine which quarter of the memory we're actually seeing
-
- \verbatim
- +-------+------+-------+----------+-------------------------------------+
- | VALUE | BITS | BANK | STARTING | VIC-II CHIP RANGE |
- | OF A | | | LOCATION | |
- +-------+------+-------+----------+-------------------------------------+
- | 0 | 00 | 3 | 49152 | ($C000-$FFFF) |
- | 1 | 01 | 2 | 32768 | ($8000-$BFFF) |
- | 2 | 10 | 1 | 16384 | ($4000-$7FFF) |
- | 3 | 11 | 0 | 0 | ($0000-$3FFF) (DEFAULT VALUE) |
- +-------+------+-------+----------+-------------------------------------+
- \endverbatim
- */
+ /*! @brief Start address of the currently selected memory bank
+ * @details There are four banks in total since the VIC chip can only "see" 16 KB of memory
+ * at one time. Two bank select bits in the CIA I/O space determine which quarter of
+ * the memory we're actually seeing
+ */
+ /*
+ * +-------+------+-------+----------+-------------------------------------+
+ * | VALUE | BITS | BANK | STARTING | VIC-II CHIP RANGE |
+ * | OF A | | | LOCATION | |
+ * +-------+------+-------+----------+-------------------------------------+
+ * | 0 | 00 | 3 | 49152 | ($C000-$FFFF) |
+ * | 1 | 01 | 2 | 32768 | ($8000-$BFFF) |
+ * | 2 | 10 | 1 | 16384 | ($4000-$7FFF) |
+ * | 3 | 11 | 0 | 0 | ($0000-$3FFF) (DEFAULT VALUE) |
+ * +-------+------+-------+----------+-------------------------------------+
+ */
uint16_t bankAddr;
- //! General memory access via address and data bus
+ //! @brief Simulates a memory access via the address and data bus.
uint8_t memAccess(uint16_t addr);
- //! Idle memory access at address 0x3fff
+ /*! @brief Simulates an idle memory access.
+ * @details An idle memory access is an access of memory location 0x3fff.
+ */
uint8_t memIdleAccess();
@@ -238,17 +257,21 @@ private:
// Character access (cAccess)
// -----------------------------------------------------------------------------------------------
- //! During a cAccess, VIC accesses the video matrix
+ /*! @brief Performs a character access (cAccess)
+ * @details During a cAccess, VIC accesses the video matrix
+ */
void cAccess();
- //! cAcess character storage
- /*! Every 8th rasterline, the VIC chips performs a DMA access and fills this array with
- character information */
+ /*! @brief cAcess character storage
+ * @details Every 8th rasterline, the VIC chips performs a DMA access and fills this array with
+ * character information
+ */
uint8_t characterSpace[40];
- //! cAcess color storage
- /*! Every 8th rasterline, the VIC chips performs a DMA access and fills the array with t
- color information */
+ /*! @brief cAcess color storage
+ * @details Every 8th rasterline, the VIC chips performs a DMA access and fills the array with
+ * color information
+ */
uint8_t colorSpace[40];
@@ -256,9 +279,11 @@ private:
// Graphics access (gAccess)
// -----------------------------------------------------------------------------------------------
- //! During a 'g access', VIC reads graphics data (character or bitmap patterns)
- /*! The result of the gAccess is stored in variables prefixed with 'g_', i.e.,
- * g_data, g_character, g_color, g_mode */
+ /*! @brief Performs a graphics access (gAccess)
+ * @details During a gAccess, VIC reads graphics data (character or bitmap patterns)
+ * The result of the gAccess is stored in variables prefixed with 'g_', i.e.,
+ * g_data, g_character, g_color, g_mode
+ */
void gAccess();
@@ -266,29 +291,33 @@ private:
// Sprite accesses (pAccess and sAccess)
// -----------------------------------------------------------------------------------------------
- //! Sprite pointer access
+ //! @brief Performs a sprite pointer access (sAccess)
void pAccess(unsigned sprite);
- //! @brief First sprite data access
- /*! @result true iff sprite data was fetched (a memory access has occurred) */
- bool sFirstAccess(unsigned sprite);
+ /*! @brief First sprite data access
+ * @result true iff sprite data was fetched (a memory access has occurred)
+ */
+ void sFirstAccess(unsigned sprite);
- //! @brief Second sprite data access
- /*! @result Returns true iff sprite data was fetched (a memory access has occurred) */
- bool sSecondAccess(unsigned sprite);
+ /*! @brief Second sprite data access
+ * @result Returns true iff sprite data was fetched (a memory access has occurred)
+ */
+ void sSecondAccess(unsigned sprite);
- //! @brief Third sprite data access
- /*! @result Returns true iff sprite data was fetched (a memory access has occurred) */
- bool sThirdAccess(unsigned sprite);
+ /*! @brief Third sprite data access
+ * @result Returns true iff sprite data was fetched (a memory access has occurred)
+ */
+ void sThirdAccess(unsigned sprite);
- //! @brief Finalizes the sprite data access
- /*! @discussion This method is invoked one cycle after the second and third sprite DMA */
+ /*! @brief Finalizes the sprite data access
+ * @details This method is invoked one cycle after the second and third sprite DMA
+ */
void sFinalize(unsigned sprite);
- //! @brief Bit i is set to 1 iff sprite i performs its first DMA in the current cycle
+ //! @brief Bit i is set to 1 iff sprite i performs its first DMA in the current cycle
uint8_t isFirstDMAcycle;
- //! @brief Bit i is set to 1 iff sprite i performs its second and third DMA in the current cycle
+ //! @brief Bit i is set to 1 iff sprite i performs its second and third DMA in the current cycle
uint8_t isSecondDMAcycle;
@@ -296,10 +325,10 @@ private:
// Memory refresh accesses (rAccess)
// -----------------------------------------------------------------------------------------------
- //! Performs a DRAM refresh
+ //! @brief Performs a DRAM refresh
inline void rAccess() { (void)memAccess(0x3F00 | refreshCounter--); }
- //! Performs a DRAM idle access
+ //! @brief Performs a DRAM idle access
inline void rIdleAccess() { (void)memIdleAccess(); }
@@ -307,32 +336,41 @@ private:
// Sprites
// -----------------------------------------------------------------------------------------------
- //! MC register
- /*! MOB data counter (6 bit counter). One register for each sprite */
+ /*! @brief MOB data counter.
+ * @details A 6 bit counter, one for each sprite.
+ */
uint8_t mc[8];
- //! MCBASE register
- /*! MOB data counter (6 bit counter). One register for each sprite */
+ /*! @brief MCBASE register.
+ * @details A 6 bit counter, one register for each sprite.
+ */
uint8_t mcbase[8];
- //! Sprite pointer
- /*! Determines where the sprite data comes from */
+ /*! @brief Sprite pointer fetched during a pAccess.
+ * @details Determines where the sprite data comes from.
+ */
uint16_t spritePtr[8];
- //! Sprite on off
- /*! Determines if a sprite needs to be drawn in the current rasterline. Each bit represents a single sprite. */
+ /*! @brief Sprite on off register
+ * @details Determines if a sprite needs to be drawn in the current rasterline.
+ * Each bit represents a single sprite.
+ */
uint8_t spriteOnOff;
- //! Sprite DMA on off
- /*! Determines if sprite dma access is enabled or disabled. Each bit represents a single sprite. */
+ /*! @brief Sprite DMA on off register
+ * @details Determines if sprite dma access is enabled or disabled.
+ * Each bit represents a single sprite.
+ */
uint8_t spriteDmaOnOff;
- //! Expansion flipflop
- /*! Used to handle Y sprite stretching. One bit for each sprite */
+ /*! @brief Expansion flipflop
+ * @details Used to handle Y sprite stretching, one bit for each sprite
+ */
uint8_t expansionFF;
- //! Remembers which bits the CPU has cleared in the expansion Y register (D017)
- /*! This value is set in pokeIO and cycle 15 and read in cycle 16 */
+ /*! @brief Remembers which bits the CPU has cleared in the expansion Y register (D017)
+ * @details This value is set in pokeIO and cycle 15 and read in cycle 16
+ */
uint8_t cleared_bits_in_d017;
@@ -340,10 +378,11 @@ private:
// Lightpen
// -----------------------------------------------------------------------------------------------
- //! Lightpen triggered?
- /*! This variable ndicates whether a lightpen interrupt has occurred within the current frame.
- The variable is needed, because a lightpen interrupt can only occur once in a frame. It is set to false
- at the beginning of each frame. */
+ /*! @brief Indicates whether the lightpen has triggered
+ * @details This variable ndicates whether a lightpen interrupt has occurred within the current
+ * frame. The variable is needed, because a lightpen interrupt can only occur once in
+ * a frame. It is set to false at the beginning of each frame.
+ */
bool lightpenIRQhasOccured;
@@ -351,34 +390,39 @@ private:
// Debugging
// -----------------------------------------------------------------------------------------------
- //! Determines whether sprites are drawn or not
- /*! During normal emulation, the value is always true. For debugging purposes, the value can be set to false.
- In this case, sprites are no longer drawn.
+ /*! @brief Determines whether sprites are drawn or not
+ * @details During normal emulation, the value is always true. For debugging purposes, the value
+ * can be set to false. In this case, sprites are no longer drawn.
*/
bool drawSprites;
- //! Enable sprite-sprite collision
- /*! If set to true, the virtual VIC chips checks for sprite-sprite collision as the original C64 does.
- For debugging purposes and cheating, collision detection can be disabled by setting the variabel to false.
- Collision detection can be enabled or disabled for each sprite seperately. Each bit is dedicated to a single sprite.
- */
+ /*! @brief Enable sprite-sprite collision
+ * @details If set to true, the virtual VIC chips checks for sprite-sprite collision as the
+ * original C64 does. For debugging purposes and cheating, collision detection can be
+ * disabled by setting the variabel to false. Collision detection can be enabled or
+ * disabled for each sprite seperately. Each bit is dedicated to a single sprite.
+ */
uint8_t spriteSpriteCollisionEnabled;
- //! Enable sprite-background collision
- /*! If set to true, the virtual VIC chips checks for sprite-background collision as the original C64 does.
- For debugging purposes and cheating, collision detection can be disabled by setting the variabel to false.
- Collision detection can be enabled or disabled for each sprite seperately. Each bit is dedicated to a single sprite.
- */
+ /*! @brief Enable sprite-background collision
+ * @details If set to true, the virtual VIC chips checks for sprite-background collision as the
+ * original C64 does. For debugging purposes and cheating, collision detection can be
+ * disabled by setting the variabel to false. Collision detection can be enabled or
+ * disabled for each sprite seperately. Each bit is dedicated to a single sprite.
+ */
uint8_t spriteBackgroundCollisionEnabled;
- //! Determines whether IRQ lines will be made visible.
- /*! Each rasterline that will potentially trigger a raster IRQ is highlighted. This feature is useful for
- debugging purposes as it visualizes how the screen is divided into multiple parts. */
+ /*! @brief Determines whether IRQ lines will be made visible.
+ * @details Each rasterline that will potentially trigger a raster IRQ is highlighted. This feature
+ * is useful for debugging purposes as it visualizes how the screen is divided into
+ * multiple parts.
+ */
bool markIRQLines;
- //! Determines whether DMA lines will be made visible.
- /*! Each rasterline in which the vic will read additional data from the memory and stun the CPU is made visible.
- Note that partial DMA lines may not appear. */
+ /*! @brief Determines whether DMA lines will be made visible.
+ * @details Each rasterline in which the vic will read additional data from the memory and
+ * stun the CPU is made visible. Note that partial DMA lines may not appear.
+ */
bool markDMALines;
@@ -388,19 +432,19 @@ private:
public:
- //! Constructor
+ //! @brief Constructor
VIC();
- //! Destructor
+ //! @brief Destructor
~VIC();
- //! Get screen buffer that is currently stable
+ //! @brief Returns the screen buffer that is currently stable.
inline void *screenBuffer() { return pixelEngine.screenBuffer(); }
- //! Reset the VIC chip to its initial state
+ //! @brief Restores the initial state.
void reset();
- //! Dump internal state to console
+ //! @brief Prints debugging information.
void dumpState();
@@ -410,38 +454,38 @@ public:
public:
- //! Returns true iff virtual vic is running in PAL mode
+ //! @brief Returns true iff a PAL chip is plugged in.
inline bool isPAL() { return chipModel == MOS6569_PAL; }
- //! Returns true iff virtual vic is running in NTSC mode
+ //! @brief Returns true iff a NTSC chip is plugged in.
inline bool isNTSC() { return chipModel == MOS6567_NTSC; }
- //! Get chip model
+ //! @brief Returns the currently plugged in chip model.
inline VICChipModel getChipModel() { return chipModel; }
- //! Set chip model
+ //! @brief Sets the chip model.
void setChipModel(VICChipModel model);
- //! Get color
+ //! @brief Returns one of the sixteen C64 colors in RGBA format.
inline uint32_t getColor(unsigned nr) { assert(nr < 16); return pixelEngine.colors[nr]; }
- //! Get color
+ //! @brief Sets one of the sixteen C64 colors in RGBA format.
inline void setColor(unsigned nr, int rgba) { assert(nr < 16); pixelEngine.colors[nr] = rgba; }
- // Returns the number of frames per second
+ // @brief Returns the number of frames per second.
inline unsigned getFramesPerSecond() { return isPAL() ? (unsigned)PAL_REFRESH_RATE : (unsigned)NTSC_REFRESH_RATE; }
- //! Returns the number of rasterlines per frame
+ //! @brief Returns the number of rasterlines per frame.
inline int getRasterlinesPerFrame() { return isPAL() ? PAL_HEIGHT : NTSC_HEIGHT; }
- //! Returns the number of CPU cycles performed per rasterline
+ //! @brief Returns the number of CPU cycles performed per rasterline.
inline int getCyclesPerRasterline() { return isPAL() ? PAL_CYCLES_PER_RASTERLINE : NTSC_CYCLES_PER_RASTERLINE; }
- //! Returns the number of CPU cycles performed per frame
+ //! @brief Returns the number of CPU cycles performed per frame.
inline int getCyclesPerFrame() {
return isPAL() ? (PAL_HEIGHT * PAL_CYCLES_PER_RASTERLINE) : (NTSC_HEIGHT * NTSC_CYCLES_PER_RASTERLINE); }
- //! Returns the time interval between two frames in nanoseconds
+ //! @brief Returns the time interval between two frames in nanoseconds.
inline uint64_t getFrameDelay() { return (uint64_t)(1000000000.0 / (isPAL() ? PAL_REFRESH_RATE : NTSC_REFRESH_RATE)); }
@@ -451,54 +495,48 @@ public:
public:
- //! Returns true if the specified address lies in the VIC I/O range
+ //! @brief Returns true if the specified address lies in the VIC I/O range
static inline bool isVicAddr(uint16_t addr) { return (VIC_START_ADDR <= addr && addr <= VIC_END_ADDR); }
- //! Get current scanline
+ //! @brief Returns the current scanline
inline uint16_t getScanline() { return yCounter; }
- //! Set rasterline
+ //! @brief Sets the rasterline
inline void setScanline(uint16_t line) { yCounter = line; }
- //! Get memory bank start address
+ //! @brief Returns the memory bank start address
uint16_t getMemoryBankAddr();
- //! Set memory bank start address
+ //! @brief Sets the memory bank start address
void setMemoryBankAddr(uint16_t addr);
- //! Get screen memory address
- /*! This function is not needed internally and only invoked by the GUI debug panel */
+ /*! @brief Returns the screen memory address
+ * @note This function is not needed internally and only invoked by the GUI debug panel
+ */
uint16_t getScreenMemoryAddr();
- //! Set screen memory address
- /*! This function is not needed internally and only invoked by the GUI debug panel */
+ /*! @brief Sets the screen memory address
+ * @note This function is not needed internally and only invoked by the GUI debug panel
+ */
void setScreenMemoryAddr(uint16_t addr);
- //! Get character memory start address
- /*! This function is not needed internally and only invoked by the GUI debug panel */
+ /*! @brief Returns the character memory address
+ * @note This function is not needed internally and only invoked by the GUI debug panel
+ */
uint16_t getCharacterMemoryAddr();
- //! Set character memory start address
- /*! This function is not needed internally and only invoked by the GUI debug panel */
+ /*! @brief Sets the character memory address
+ * @note This function is not needed internally and only invoked by the GUI debug panel
+ */
void setCharacterMemoryAddr(uint16_t addr);
- //! Peek fallthrough
- /*! The fallthrough mechanism works as follows:
- If the memory is asked to peek a value, it first checks whether the RAM, ROM, or I/O space is visible.
- If an address in the I/O space is specified, the memory is unable to handle the request itself and
- passes it to the corresponding I/O chip.
- */
+ //! @brief Peek fallthrough
uint8_t peek(uint16_t addr);
- //! Poke fallthrough
- /*! The fallthrough mechanism works as follows:
- If the memory is asked to poke a value, it first checks whether the RAM, ROM, or I/O space is visible.
- If an address in the I/O space is specified, the memory is unable to handle the request itself and
- passes it to the corresponding I/O chip.
- */
+ //! @brief Poke fallthrough
void poke(uint16_t addr, uint8_t value);
- //! Return last value on VIC data bus
+ //! @brief Returns the last value on VIC data bus
inline uint8_t getDataBus() { return dataBus; }
@@ -508,88 +546,92 @@ public:
public:
- //! Current value of DEN bit (Display Enabled)
+ //! @brief Returns the current value of the DEN bit (Display Enabled).
inline bool DENbit() { return GET_BIT(p.registerCTRL1, 4); }
- //! DEN bit in previous cycle (Display Enabled)
+ //! @brief Returns the value of the DEN bit in the previous cycle.
// inline bool DENbitInPreviousCycle() { return GET_BIT(pixelEngine.pipe.registerCTRL1, 4); }
- //! Current value of BMM bit (Bit Map Mode)
+ //! @brief Returns the current value of the BMM bit (Bit Map Mode).
inline bool BMMbit() { return GET_BIT(p.registerCTRL1, 5); }
- //! BMM bit in previous cycle (Bit Map Mode)
+ //! @brief Returns the value of the BMM bit in the previous cycle.
inline bool BMMbitInPreviousCycle() { return GET_BIT(pixelEngine.pipe.registerCTRL1, 5); }
- //! Current value of ECM bit (Extended Character Mode)
+ //! @brief Returns the current value of the ECM bit (Extended Character Mode).
inline bool ECMbit() { return GET_BIT(p.registerCTRL1, 6); }
- //! ECM bit in previous cycle (Extended Character Mode)
+ //! @brief Returns the value of the ECM bit in the previous cycle.
inline bool ECMbitInPreviousCycle() { return GET_BIT(pixelEngine.pipe.registerCTRL1, 6); }
- //! Returns masked CB13 bit (controls memory access)
+ //! @brief Returns the masked CB13 bit (controls memory access).
inline uint8_t CB13() { return iomem[0x18] & 0x08; }
- //! Returns masked CB13/CB12/CB11 bits (controls memory access)
+ //! @brief Returns the masked CB13/CB12/CB11 bits (controls memory access).
inline uint8_t CB13CB12CB11() { return iomem[0x18] & 0x0E; }
- //! Returns masked VM13/VM12/VM11/VM10 bits (controls memory access)
+ //! @brief Returns the masked VM13/VM12/VM11/VM10 bits (controls memory access).
inline uint8_t VM13VM12VM11VM10() { return iomem[0x18] & 0xF0; }
- //! Returns the state of the CSEL bit
+ //! @brief Returns the state of the CSEL bit.
inline bool isCSEL() { return GET_BIT(p.registerCTRL2, 3); }
- //! Returns the state of the RSEL bit
+ //! @brief Returns the state of the RSEL bit.
inline bool isRSEL() { return GET_BIT(p.registerCTRL1, 3); }
- //! Returns the currently set display mode
- /*! The display mode is determined by bits 5 and 6 of control register 1 and bit 4 of control register 2. */
+ /*! @brief Returns the current display mode.
+ * @details The display mode is determined by bits 5 and 6 of control register 1 and
+ * bit 4 of control register 2.
+ */
inline DisplayMode getDisplayMode()
{ return (DisplayMode)((p.registerCTRL1 & 0x60) | (p.registerCTRL2 & 0x10)); }
- //! Set display mode
+ //! @brief Sets the display mode.
inline void setDisplayMode(DisplayMode m) {
p.registerCTRL1 = (p.registerCTRL1 & ~0x60) | (m & 0x60);
p.registerCTRL2 = (p.registerCTRL2 & ~0x10) | (m & 0x10); }
- //! Get the current screen geometry
+ //! @brief Returns the current screen geometry.
ScreenGeometry getScreenGeometry(void);
- //! Set the screen geometry
+ //! @brief Sets the current screen geometry.
void setScreenGeometry(ScreenGeometry mode);
- //! Returns the number of rows to be drawn (24 or 25)
+ //! @brief Returns the number of rows to be drawn (24 or 25).
inline int numberOfRows() { return GET_BIT(p.registerCTRL1, 3) ? 25 : 24; }
- //! Set the number of rows to be drawn (24 or 25)
+ //! @brief Sets the number of rows to be drawn (24 or 25).
inline void setNumberOfRows(int rs) { assert(rs == 24 || rs == 25); WRITE_BIT(p.registerCTRL1, 3, rs == 25); }
- //! Return the number of columns to be drawn (38 or 40)
+ //! @brief Returns the number of columns to be drawn (38 or 40).
inline int numberOfColumns() { return GET_BIT(p.registerCTRL2, 3) ? 40 : 38; }
- //! Set the number of columns to be drawn (38 or 40)
+ //! @brief Sets the number of columns to be drawn (38 or 40).
inline void setNumberOfColumns(int cs) { assert(cs == 38 || cs == 40); WRITE_BIT(p.registerCTRL2, 3, cs == 40); }
- //! Returns the vertical raster scroll offset (0 to 7)
- /*! The vertical raster offset is usally used by games for smoothly scrolling the screen */
+ /*! @brief Returns the vertical raster scroll offset (0 to 7).
+ * @details The vertical raster offset is usally used by games for smoothly scrolling the screen.
+ */
inline uint8_t getVerticalRasterScroll() { return p.registerCTRL1 & 0x07; }
- //! Set vertical raster scroll offset (0 to 7)
+ //! @brief Sets the vertical raster scroll offset (0 to 7).
inline void setVerticalRasterScroll(uint8_t offset) { p.registerCTRL1 = (p.registerCTRL1 & 0xF8) | (offset & 0x07); }
- //! Returns the horizontal raster scroll offset (0 to 7)
- /*! The vertical raster offset is usally used by games for smoothly scrolling the screen */
+ /*! @brief Returns the horizontal raster scroll offset (0 to 7).
+ * @details The vertical raster offset is usally used by games for smoothly scrolling the screen.
+ */
inline uint8_t getHorizontalRasterScroll() { return p.registerCTRL2 & 0x07; }
- //! Set horizontan raster scroll offset (0 to 7)
+ //! @brief Sets the horizontan raster scroll offset (0 to 7).
inline void setHorizontalRasterScroll(uint8_t offset) { p.registerCTRL2 = (p.registerCTRL2 & 0xF8) | (offset & 0x07); }
- //! Return border color
+ //! @brief Returns the border color.
inline uint8_t getBorderColor() { return bp.borderColor; }
- //! Returns background color
+ //! @brief Returns the background color.
inline uint8_t getBackgroundColor() { return cp.backgroundColor[0]; }
- //! Returns extra background color (for multicolor modes)
+ //! Returns extra background color (for multicolor modes).
inline uint8_t getExtraBackgroundColor(int offset) { return cp.backgroundColor[offset]; }
@@ -599,16 +641,18 @@ public:
private:
- //! Set to true in cycle 1, cycle 63 and cycle 65 iff yCounter equals contents of D012
- /*! Variable is needed to determine if a rasterline should be issued in cycle 1 or 2 */
+ /*! @brief Set to true in cycle 1, cycle 63 and cycle 65 iff yCounter equals contents of D012
+ * @details Variable is needed to determine if a rasterline should be issued in cycle 1 or 2
+ */
bool yCounterEqualsIrqRasterline;
- /*! Update bad line condition
- "Ein Bad-Line-Zustand liegt in einem beliebigen Taktzyklus vor, wenn an der
- negativen Flanke von ¯0 zu Beginn des
- [1] Zyklus RASTER >= $30 und RASTER <= $f7 und
- [2] die unteren drei Bits von RASTER mit YSCROLL übereinstimmen
- [3] und in einem beliebigen Zyklus von Rasterzeile $30 das DEN-Bit gesetzt war." [C.B.] */
+ /*! @brief Update bad line condition
+ * @details "Ein Bad-Line-Zustand liegt in einem beliebigen Taktzyklus vor, wenn an der
+ * negativen Flanke von ¯0 zu Beginn des
+ * [1] Zyklus RASTER >= $30 und RASTER <= $f7 und
+ * [2] die unteren drei Bits von RASTER mit YSCROLL übereinstimmen
+ * [3] und in einem beliebigen Zyklus von Rasterzeile $30 das DEN-Bit gesetzt war." [C.B.]
+ */
inline void updateBadLineCondition() {
badLineCondition =
yCounter >= 0x30 && yCounter <= 0xf7 /* [1] */ &&
@@ -616,40 +660,44 @@ private:
DENwasSetInRasterline30 /* [3] */;
}
- //! Update display state
- /*! Invoked at the end of each VIC cycle */
+ /*! @brief Update display state
+ * @details Invoked at the end of each VIC cycle
+ */
inline void updateDisplayState() { if (badLineCondition) displayState = true; }
- //! Set BA line
+ //! @brief Set BA line
void setBAlow(uint8_t value);
- //! Trigger a VIC interrupt
- /*! VIC interrupts can be triggered from multiple sources. Each one is associated with a specific bit */
+ /*! @brief Trigger a VIC interrupt
+ * @details VIC interrupts can be triggered from multiple sources. Each one is associated with a specific bit
+ */
void triggerIRQ(uint8_t source);
public:
- //! @brief Returns next interrupt rasterline
- /*! @discussion In line 0, the interrupt is triggered in cycle 2. In all other lines, it is triggered in cycle 1 */
+ /*! @brief Returns next interrupt rasterline
+ * @details In line 0, the interrupt is triggered in cycle 2. In all other lines, it is triggered in cycle 1
+ */
inline uint16_t rasterInterruptLine() { return ((p.registerCTRL1 & 0x80) << 1) | iomem[0x12]; }
- //! Set interrupt rasterline
+ //! @brief Set interrupt rasterline
inline void setRasterInterruptLine(uint16_t line) {
iomem[0x12] = line & 0xFF; if (line > 0xFF) p.registerCTRL1 |= 0x80; else p.registerCTRL1 &= 0x7F; }
- //! Returns true, iff rasterline interrupts are enabled
+ //! @brief Returns true, iff rasterline interrupts are enabled
inline bool rasterInterruptEnabled() { return GET_BIT(iomem[0x1A], 1); }
- //! Enable or disable rasterline interrupts
+ //! @brief Enable or disable rasterline interrupts
inline void setRasterInterruptEnable(bool b) { WRITE_BIT(iomem[0x1A], 1, b); }
- //! Enable or disable rasterline interrupts
+ //! @brief Enable or disable rasterline interrupts
inline void toggleRasterInterruptFlag() { TOGGLE_BIT(iomem[0x1A], 1); }
- //! Simulate a light pen event
- /*! Although we do not support hardware lightpens, we need to take care of it because lightpen interrupts
- can be triggered by software. It is used by some games to determine the current X position within
- the current rasterline. */
+ /*! @brief Simulate a light pen event
+ * @details Although we do not support hardware lightpens, we need to take care of it because lightpen
+ * interrupts can be triggered by software. It is used by some games to determine the current
+ * X position within the current rasterline.
+ */
void triggerLightPenInterrupt();
@@ -659,49 +707,54 @@ public:
private:
- //! Turn off sprite dma if conditions are met
- /*! In cycle 16, the mcbase pointer is advanced three bytes for all dma enabled sprites. Advancing
- three bytes means that mcbase will then point to the next sprite line. When mcbase reached 63,
- all 21 sprite lines have been drawn and sprite dma is switched off.
- The whole operation is skipped when the y expansion flipflop is 0. This never happens for
- normal sprites (there is no skipping then), but happens every other cycle for vertically expanded
- sprites. Thus, mcbase advances for those sprites at half speed which actually causes the expansion. */
+ /*! @brief Turns off sprite dma if conditions are met.
+ * @details In cycle 16, the mcbase pointer is advanced three bytes for all dma enabled sprites.
+ * Advancing three bytes means that mcbase will then point to the next sprite line.
+ * When mcbase reached 63, all 21 sprite lines have been drawn and sprite dma is
+ * switched off. The whole operation is skipped when the y expansion flipflop is 0.
+ * This never happens for normal sprites (there is no skipping then), but happens every
+ * other cycle for vertically expanded sprites. Thus, mcbase advances for those sprites
+ * at half speed which actually causes the expansion.
+ */
void turnSpriteDmaOff();
- //! Turn on sprite dma accesses if drawing conditions are met
- /*! Sprite dma is turned on either in cycle 55 or cycle 56. Dma is turned on iff it's currently turned
- off and the sprite y positon equals the lower 8 bits of yCounter. */
+ /*! @brief Turns on sprite dma accesses if drawing conditions are met.
+ * @details Sprite dma is turned on either in cycle 55 or cycle 56. Dma is turned on iff it's
+ * currently turned off and the sprite y positon equals the lower 8 bits of yCounter.
+ */
void turnSpriteDmaOn();
- //! Toggle expansion flipflop for vertically stretched sprites
- /*! In cycle 56, register D017 is read and the flipflop gets inverted for all sprites with vertical
- stretching enabled. When the flipflop goes down, advanceMCBase() will have no effect in the
- next rasterline. This causes each sprite line to be drawn twice. */
+ /*! @brief Toggles expansion flipflop for vertically stretched sprites.
+ * @details In cycle 56, register D017 is read and the flipflop gets inverted for all sprites
+ * with vertical stretching enabled. When the flipflop goes down, advanceMCBase() will
+ * have no effect in the next rasterline. This causes each sprite line to be drawn twice.
+ */
void toggleExpansionFlipflop();
- //! Get sprite depth
- /*! The value is written to the z buffer to resolve overlapping pixels */
+ /*! @brief Gets depth of a sprite.
+ * @details The value is written to the z buffer to resolve overlapping pixels.
+ */
inline uint8_t spriteDepth(uint8_t nr) {
return spriteIsDrawnInBackground(nr) ? (SPRITE_LAYER_BG_DEPTH | nr) : (SPRITE_LAYER_FG_DEPTH | nr); }
public:
- //! Returns color code of multicolor sprites (extra color 1)
+ //! @brief Returns color code of multicolor sprites (extra color 1).
inline uint8_t spriteExtraColor1() { return sp.spriteExtraColor1; }
- //! Returns color code of multicolor sprites (extra color 2)
+ //! @brief Returns color code of multicolor sprites (extra color 2).
inline uint8_t spriteExtraColor2() { return sp.spriteExtraColor2; }
- //! Get sprite color
+ //! @brief Returns the color of a sprite.
inline uint8_t spriteColor(uint8_t nr) { assert(nr < 8); return sp.spriteColor[nr]; }
- //! Set sprite color
+ //! @brief Sets the color of a sprite.
inline void setSpriteColor(uint8_t nr, uint8_t color) { assert(nr < 8); sp.spriteColor[nr] = color; }
- //! Get X coordinate of sprite
+ //! @brief Returns the X coordinate of a sprite.
inline uint16_t getSpriteX(uint8_t nr) { assert(nr < 8); return p.spriteX[nr]; }
- //! Set X coordinate of sprite
+ //! @brief Set the X coordinate of a sprite.
inline void setSpriteX(uint8_t nr, uint16_t x) {
if (x < 512) {
p.spriteX[nr] = x;
@@ -710,82 +763,82 @@ public:
}
}
- //! Get Y coordinate of sprite
+ //! @brief Returns the Y coordinate of a sprite.
inline uint8_t getSpriteY(uint8_t nr) { assert(nr < 8); return iomem[1+2*nr]; }
- //! Set Y coordinate of sprite
+ //! @brief Sets the Y coordinate of sprite.
inline void setSpriteY(uint8_t nr, uint8_t y) { iomem[1+2*nr] = y; }
- //! Compare Y coordinate of all sprites with 8 bit value
+ //! @brief Compares the Y coordinates of all sprites with an eight bit value.
inline uint8_t compareSpriteY(uint8_t y) { return
((iomem[1] == y) << 0) | ((iomem[3] == y) << 1) | ((iomem[5] == y) << 2) | ((iomem[7] == y) << 3) |
((iomem[9] == y) << 4) | ((iomem[11] == y) << 5) | ((iomem[13] == y) << 6) | ((iomem[15] == y) << 7);
}
- //! Returns true, if sprite is enabled (drawn on the screen)
+ //! @brief Returns true, if sprite is enabled (drawn on the screen).
inline bool spriteIsEnabled(uint8_t nr) { return GET_BIT(iomem[0x15], nr); }
- //! Enable or disable sprite
+ //! @brief Enables or disables a sprite.
inline void setSpriteEnabled(uint8_t nr, bool b) { WRITE_BIT(iomem[0x15], nr, b); }
- //! Enable or disable sprite
+ //! @brief Enables or disables a sprite.
inline void toggleSpriteEnabled(uint8_t nr) { TOGGLE_BIT(iomem[0x15], nr); }
- //! Returns true, iff an interrupt will be triggered when a sprite/background collision occurs
+ //! @brief Returns true, iff an interrupt will be triggered when a sprite/background collision occurs.
inline bool spriteBackgroundInterruptEnabled() { return GET_BIT(iomem[0x1A], 1); }
- //! Returns true, iff an interrupt will be triggered when a sprite/sprite collision occurs
+ //! @brief Returns true, iff an interrupt will be triggered when a sprite/sprite collision occurs.
inline bool spriteSpriteInterruptEnabled() { return GET_BIT(iomem[0x1A], 2); }
- //! Returns true, iff a rasterline interrupt has occurred
+ //! @brief Returns true, iff a rasterline interrupt has occurred.
inline bool rasterInterruptOccurred() { return GET_BIT(iomem[0x19], 0); }
- //! Returns true, iff a sprite/background interrupt has occurred
+ //! @brief Returns true, iff a sprite/background interrupt has occurred.
inline bool spriteBackgroundInterruptOccurred() { return GET_BIT(iomem[0x19], 1); }
- //! Returns true, iff a sprite/sprite interrupt has occurred
+ //! @brief Returns true, iff a sprite/sprite interrupt has occurred.
inline bool spriteSpriteInterruptOccurred() { return GET_BIT(iomem[0x19], 1); }
- //! Returns true, iff sprites are drawn behind the scenary
+ //! @brief Returns true, iff sprites are drawn behind the scenary.
inline bool spriteIsDrawnInBackground(unsigned nr) { assert(nr < 8); return GET_BIT(iomem[0x1B], nr); }
- //! Determine whether a sprite is drawn before or behind the scenary
+ //! @brief Determines whether a sprite is drawn before or behind the scenary.
inline void setSpriteInBackground(unsigned nr, bool b) { assert(nr < 8); WRITE_BIT(iomem[0x1B], nr, b); }
- //! Determine whether a sprite is drawn before or behind the scenary
+ //! @brief Determines whether a sprite is drawn before or behind the scenary.
inline void spriteToggleBackgroundPriorityFlag(unsigned nr) { assert(nr < 8); TOGGLE_BIT(iomem[0x1B], nr); }
- //! Returns true, iff sprite is a multicolor sprite
+ //! @brief Returns true, iff sprite is a multicolor sprite.
inline bool spriteIsMulticolor(unsigned nr) { assert(nr < 8); return GET_BIT(iomem[0x1C], nr); }
- //! Set single color or multi color mode for sprite
+ //! @brief Sets single color or multi color mode for sprite.
inline void setSpriteMulticolor(unsigned nr, bool b) { assert(nr < 8); WRITE_BIT(iomem[0x1C], nr, b); }
- //! Switch between single color or multi color mode
+ //! @brief Switches between single color or multi color mode.
inline void toggleMulticolorFlag(unsigned nr) { assert(nr < 8); TOGGLE_BIT(iomem[0x1C], nr); }
- //! Returns true, if the sprite is vertically stretched
+ //! @brief Returns true, iff the sprite is vertically stretched.
inline bool spriteHeightIsDoubled(unsigned nr) { assert(nr < 8); return GET_BIT(iomem[0x17], nr); }
- //! Stretch or shrink sprite vertically
+ //! @brief Stretches or shrinks a sprite vertically.
inline void setSpriteStretchY(unsigned nr, bool b) { assert(nr < 8); WRITE_BIT(iomem[0x17], nr, b); }
- //! Stretch or shrink sprite vertically
+ //! @brief Stretches or shrinks a sprite vertically.
inline void spriteToggleStretchYFlag(unsigned nr) { assert(nr < 8); TOGGLE_BIT(iomem[0x17], nr); }
- //! Returns true, if the sprite is horizontally stretched
+ //! @brief Returns true, iff the sprite is horizontally stretched.
inline bool spriteWidthIsDoubled(unsigned nr) { assert(nr < 8); return GET_BIT(p.spriteXexpand, nr); }
- //! Stretch or shrink sprite horizontally
+ //! @brief Stretches or shrinks sprite horizontally.
inline void setSpriteStretchX(unsigned nr, bool b) { assert(nr < 8); WRITE_BIT(p.spriteXexpand, nr, b); }
- //! Stretch or shrink sprite horizontally
+ //! @brief Stretches or shrinks sprite horizontally.
inline void spriteToggleStretchXFlag(unsigned nr) { assert(nr < 8); TOGGLE_BIT(p.spriteXexpand, nr); }
- //! Returns true, iff sprite collides with another sprite
+ //! @brief Returns true, iff sprite collides with another sprite.
inline bool spriteCollidesWithSprite(unsigned nr) { assert(nr < 8); return GET_BIT(iomem[0x1E], nr); }
- //! Returns true, iff sprite collides with background
+ //! @brief Returns true, iff sprite collides with background.
inline bool spriteCollidesWithBackground(unsigned nr) { assert(nr < 8); return GET_BIT(iomem[0x1F], nr); }
@@ -795,27 +848,30 @@ public:
public:
- //! Prepare for new frame
- /*! This function is called prior to cycle 1 of rasterline 0 */
+ /*! @brief Prepares VIC for drawing a new frame.
+ * @details This function is called prior to cycle 1 of rasterline 0.
+ */
void beginFrame();
- //! Prepare for new rasterline
- /*! This function is called prior to cycle 1 at the beginning of each rasterline */
+ /*! @brief Prepares VIC for drawing a new rasterline.
+ * @details This function is called prior to cycle 1 at the beginning of each rasterline.
+ */
void beginRasterline(uint16_t rasterline);
- //! Finish rasterline
- /*! This function is called after the last cycle of each rasterline. */
+ /*! @brief Finishes up a rasterline.
+ * @details This function is called after the last cycle of each rasterline.
+ */
void endRasterline();
- //! Finish frame
- /*! This function is called after the last cycle of the last rasterline */
+ /*! @brief Finishes up a frame.
+ * @details This function is called after the last cycle of the last rasterline .
+ */
void endFrame();
- //! Push portions of the VIC state into the pixel engine
- /*! Pushs everything that needs to be recorded one cycle prior to drawing */
+ //! @brief Pushes portions of the VIC state into the pixel engine.
inline void preparePixelEngine() { pixelEngine.pipe = p; };
- //! VIC execution functions
+ //! @brief Executes a specific rasterline cycle
void cycle1(); void cycle2(); void cycle3(); void cycle4();
void cycle5(); void cycle6(); void cycle7(); void cycle8();
void cycle9(); void cycle10(); void cycle11(); void cycle12();
@@ -828,53 +884,56 @@ public:
void cycle59(); void cycle60(); void cycle61(); void cycle62();
void cycle63(); void cycle64(); void cycle65();
- //! Debug entry point for each rasterline cycle
-
private:
+
+ /*! @brief Implements a debug entry point for each rasterline cycle.
+ * @details As this function is invoked in each cycle, it should be empty in the relase version.
+ */
void debug_cycle(unsigned cycle);
+
// -----------------------------------------------------------------------------------------------
// Debugging
// -----------------------------------------------------------------------------------------------
-
public:
- //! Return true iff IRQ lines are colorized
+ //! @brief Returns true iff IRQ lines are colorized
bool showIrqLines() { return markIRQLines; }
- //! Show or hide IRQ lines
+ //! @brief Shows or hides IRQ lines
void setShowIrqLines(bool show) { markIRQLines = show; }
- //! Return true iff DMA lines are colorized
+ //! @brief Returns true iff DMA lines are colorized
bool showDmaLines() { return markDMALines; }
- //! Show or hide DMA lines
+ //! @brief Shows or hides DMA lines
void setShowDmaLines(bool show) { markDMALines = show; }
- //! Return true iff sprites are hidden
+ //! @brief Returns true iff sprites are hidden
bool hideSprites() { return !drawSprites; }
- //! Hide or show sprites
+ //! @brief Hides or shows sprites
void setHideSprites(bool hide) { drawSprites = !hide; }
- //! Return true iff sprite-sprite collision detection is enabled
+ //! @brief Returns true iff sprite-sprite collision detection is enabled
bool getSpriteSpriteCollisionFlag() { return spriteSpriteCollisionEnabled; }
- //! Enable or disable sprite-sprite collision detection
+ //! @brief Enables or disable sprite-sprite collision detection
void setSpriteSpriteCollisionFlag(bool b) { spriteSpriteCollisionEnabled = b; };
- //! Enable or disable sprite-sprite collision detection
+ //! @brief Enables or disable sprite-sprite collision detection
void toggleSpriteSpriteCollisionFlag() { spriteSpriteCollisionEnabled = !spriteSpriteCollisionEnabled; }
- //! Return true iff sprite-background collision detection is enabled
+ //! @brief Returns true iff sprite-background collision detection is enabled
bool getSpriteBackgroundCollisionFlag() { return spriteBackgroundCollisionEnabled; }
- //! Enable or disable sprite-background collision detection
+ //! @brief Enables or disable sprite-background collision detection
void setSpriteBackgroundCollisionFlag(bool b) { spriteBackgroundCollisionEnabled = b; }
- //! Enable or disable sprite-background collision detection
- void toggleSpriteBackgroundCollisionFlag() { spriteBackgroundCollisionEnabled = !spriteBackgroundCollisionEnabled; }
+ //! @brief Enables or disable sprite-background collision detection
+ void toggleSpriteBackgroundCollisionFlag() {
+ spriteBackgroundCollisionEnabled = !spriteBackgroundCollisionEnabled; }
};
#endif
diff --git a/C64/VIC_globals.h b/C64/VIC_globals.h
old mode 100755
new mode 100644
index fec1f0f..91a53ff
--- a/C64/VIC_globals.h
+++ b/C64/VIC_globals.h
@@ -1,21 +1,24 @@
-/*
- * Written 2015 by Dirk W. Hoffmann
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header VIC_globals.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2015 - 2016 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _VIC_CONSTANTS_INC
#define _VIC_CONSTANTS_INC
@@ -32,13 +35,13 @@
#define SPR6 0x40
#define SPR7 0x80
-//! @brief Supported VIC II chip models
+//! @brief Supported VIC II chip models
enum VICChipModel {
MOS6567_NTSC = 0,
MOS6569_PAL = 1
};
-//! @brief Screen geometries
+//! @brief Screen geometries
enum ScreenGeometry {
COL_40_ROW_25 = 0x01,
COL_38_ROW_25 = 0x02,
@@ -46,10 +49,10 @@ enum ScreenGeometry {
COL_38_ROW_24 = 0x04
};
-//! @brief Start address of the VIC I/O space
+//! @brief Start address of the VIC I/O space
static const uint16_t VIC_START_ADDR = 0xD000;
-//! @brief End address of the VIC I/O space
+//! @brief End address of the VIC I/O space
static const uint16_t VIC_END_ADDR = 0xD3FF;
@@ -57,73 +60,73 @@ static const uint16_t VIC_END_ADDR = 0xD3FF;
// NTSC constants
// -----------------------------------------------------------------------------------------------
-//! Frames per second
+//! @brief Frames per second in NTSC mode
static const double NTSC_REFRESH_RATE = 59.826;
-//! CPU cycles per rasterline
+//! @brief CPU cycles per rasterline in NTSC mode
static const uint16_t NTSC_CYCLES_PER_RASTERLINE = 65;
-//! CPU cycles per frame
+//! @brief CPU cycles per frame in NTSC mode
static const unsigned NTSC_CYCLES_PER_FRAME = 17095;
-//! CPU cycles per second
+//! @brief CPU cycles per second in NTSC mode
static const unsigned NTSC_CYCLES_PER_SECOND = NTSC_REFRESH_RATE * NTSC_CYCLES_PER_FRAME;
-//! Pixel aspect ratio
+//! @brief Pixel aspect ratio in NTSC mode
static const uint16_t NTSC_PIXEL_ASPECT_RATIO = 0.75;
// Horizontal screen parameters
-//! Width of left VBLANK area
+//! @brief Width of left VBLANK area in NTSC mode
static const uint16_t NTSC_LEFT_VBLANK = 77;
-//! Width of left border
-static const uint16_t NTSC_LEFT_BORDER_WIDTH = 55; // was 49
+//! @brief Width of left border in NTSC mode
+static const uint16_t NTSC_LEFT_BORDER_WIDTH = 55;
-//! Width of canvas area
+//! @brief Width of canvas area in NTSC mode
static const uint16_t NTSC_CANVAS_WIDTH = 320;
-//! Width of right border
-static const uint16_t NTSC_RIGHT_BORDER_WIDTH = 53; // was 49
+//! @brief Width of right border in NTSC mode
+static const uint16_t NTSC_RIGHT_BORDER_WIDTH = 53;
-//! Width of right VBLANK area
+//! @brief Width of right VBLANK area in NTSC mode
static const uint16_t NTSC_RIGHT_VBLANK = 15;
-//! Total width of a rasterline (including VBLANK)
+//! @brief Total width of a rasterline (including VBLANK) in NTSC mode
static const uint16_t NTSC_WIDTH = 520; // 77 + 55 + 320 + 53 + 15
-//! Number of drawn pixels per rasterline
+//! @brief Number of drawn pixels per rasterline in NTSC mode
static const uint16_t NTSC_PIXELS = 428; // 55 + 320 + 53
-//! Number of viewable pixels per rasterline
+//! @brief Number of viewable pixels per rasterline in NTSC mode
static const uint16_t NTSC_VISIBLE_PIXELS = 418;
// Vertical screen parameters
-//! Number of VBLANK lines at top
+//! @brief Number of VBLANK lines at top in NTSC mode
static const uint16_t NTSC_UPPER_VBLANK = 16;
-//! Heigt of upper boder
-static const uint16_t NTSC_UPPER_BORDER_HEIGHT = 10; // Was 23
+//! @brief Heigt of upper boder in NTSC mode
+static const uint16_t NTSC_UPPER_BORDER_HEIGHT = 10;
-//! Height of canvas area
+//! @brief Height of canvas area in NTSC mode
static const uint16_t NTSC_CANVAS_HEIGHT = 200;
-//! Lower border height
-static const uint16_t NTSC_LOWER_BORDER_HEIGHT = 25; // Was 12
+//! @brief Lower border height in NTSC mode
+static const uint16_t NTSC_LOWER_BORDER_HEIGHT = 25;
-//! Number of VBLANK lines at bottom
+//! @brief Number of VBLANK lines at bottom in NTSC mode
static const uint16_t NTSC_LOWER_VBLANK = 12;
-//! Total height of a frame (including VBLANK)
+//! @brief Total height of a frame (including VBLANK) in NTSC mode
static const uint16_t NTSC_HEIGHT = 263; // 16 + 10 + 200 + 25 + 12
-//! Number of drawn rasterlines per frame
+//! @brief Number of drawn rasterlines per frame in NTSC mode
static const uint16_t NTSC_RASTERLINES = 235; // 10 + 200 + 25
-//! Number of viewable rasterlines per frame
+//! @brief Number of viewable rasterlines per frame in NTSC mode
static const uint16_t NTSC_VISIBLE_RASTERLINES = 235;
@@ -131,132 +134,134 @@ static const uint16_t NTSC_VISIBLE_RASTERLINES = 235;
// PAL constants
// -----------------------------------------------------------------------------------------------
-//! Frames per second
+//! @brief Frames per second in PAL mode
static const double PAL_REFRESH_RATE = 50.125;
-//! CPU cycles per rasterline
+//! @brief CPU cycles per rasterline in PAL mode
static const uint16_t PAL_CYCLES_PER_RASTERLINE = 63;
-//! CPU cycles per frame
+//! @brief CPU cycles per frame in PAL mode
static const unsigned PAL_CYCLES_PER_FRAME = 19656;
-//! CPU cycles per second
+//! @brief CPU cycles per second in PAL mode
static const unsigned PAL_CYCLES_PER_SECOND = PAL_REFRESH_RATE * PAL_CYCLES_PER_FRAME;
-//! Pixel aspect ratio
+//! @brief Pixel aspect ratio in PAL mode
static const uint16_t PAL_PIXEL_ASPECT_RATIO = 0.9365;
// Horizontal screen parameters
-//! Width of left VBLANK area
+//! @brief Width of left VBLANK area in PAL mode
static const uint16_t PAL_LEFT_VBLANK = 76;
-//! Width of left border
-static const uint16_t PAL_LEFT_BORDER_WIDTH = 48; // was 46; // Should be 48
+//! @brief Width of left border in PAL mode
+static const uint16_t PAL_LEFT_BORDER_WIDTH = 48;
-//! Width of canvas area
+//! @brief Width of canvas area in PAL mode
static const uint16_t PAL_CANVAS_WIDTH = 320;
-//! Width of right border
-static const uint16_t PAL_RIGHT_BORDER_WIDTH = 37; // was 36; // Should be 37
+//! @brief Width of right border in PAL mode
+static const uint16_t PAL_RIGHT_BORDER_WIDTH = 37;
-//! Width of right VBLANK area
+//! @brief Width of right VBLANK area in PAL mode
static const uint16_t PAL_RIGHT_VBLANK = 23;
-//! Total width of a rasterline (including VBLANK)
+//! @brief Total width of a rasterline (including VBLANK) in PAL mode
static const uint16_t PAL_WIDTH = 504; // 76 + 48 + 320 + 37 + 23
-//! Number of drawn pixels per rasterline
+//! @brief Number of drawn pixels per rasterline in PAL mode
static const uint16_t PAL_PIXELS = 405; // 48 + 320 + 37
-//! Number of viewable pixels per rasterline
-static const uint16_t PAL_VISIBLE_PIXELS = 403; // was 402
+//! @brief Number of viewable pixels per rasterline in PAL mode
+static const uint16_t PAL_VISIBLE_PIXELS = 403;
// Vertical screen parameters
-//! Number of VBLANK lines at top
+//! @brief Number of VBLANK lines at top in PAL mode
static const uint16_t PAL_UPPER_VBLANK = 16;
-//! Heigt of upper boder
-static const uint16_t PAL_UPPER_BORDER_HEIGHT = 35; // was 43
+//! @brief Heigt of upper boder in PAL mode
+static const uint16_t PAL_UPPER_BORDER_HEIGHT = 35;
-//! Height of canvas area
+//! @brief Height of canvas area in PAL mode
static const uint16_t PAL_CANVAS_HEIGHT = 200;
-//! Lower border height
+//! @brief Lower border height in PAL mode
static const uint16_t PAL_LOWER_BORDER_HEIGHT = 49;
-//! Number of VBLANK lines at bottom
+//! @brief Number of VBLANK lines at bottom in PAL mode
static const uint16_t PAL_LOWER_VBLANK = 12;
-//! Total height of a frame (including VBLANK)
+//! @brief Total height of a frame (including VBLANK) in PAL mode
static const uint16_t PAL_HEIGHT = 312; // 16 + 35 + 200 + 49 + 12
-//! Number of drawn rasterlines per frame
+//! @brief Number of drawn rasterlines per frame in PAL mode
static const uint16_t PAL_RASTERLINES = 284; // 35 + 200 + 49
-//! Number of viewable rasterlines per frame
+//! @brief Number of viewable rasterlines per frame in PAL mode
static const uint16_t PAL_VISIBLE_RASTERLINES = 284; // was 292
// -----------------------------------------------------------------------------------------------
// VIC state pipes
-//
-// Each pipe comprises a certain portion of the VICs internal state. I.e., they comprise those
-// state variables that are accessed by the pixel engine and need to be delayed by a certain
-// amount to get the timing right. Most state variables need to be delayed by one cycle.
-// An exception are the color registers that usually exhibit a value change somewhere in the middle
-// of an pixel chunk. To implement the delay, both VIC and PixelEngine hold a pipe variable of their
-// own, and the contents of the VICs variable is copied over the contents of the PixelEngines
-// variable at the right time. Putting the state variables in seperate structures allows the compiler
-// to optize the copy process.
// -----------------------------------------------------------------------------------------------
-//! @brief A certain portion of VICs internal state
-/*! @discussion This structure comprises all state variables that need to be delayed one cycle to get
- * the timing right. */
+/*! @brief A certain portion of VICs internal state
+ * @details This structure comprises all state variables that need to be delayed one cycle to get
+ * the timing right.
+ * @note A general note about state pipes:
+ * Each pipe comprises a certain portion of the VICs internal state. I.e., they comprise those
+ * state variables that are accessed by the pixel engine and need to be delayed by a certain
+ * amount to get the timing right. Most state variables need to be delayed by one cycle.
+ * An exception are the color registers that usually exhibit a value change somewhere in the middle
+ * of an pixel chunk. To implement the delay, both VIC and PixelEngine hold a pipe variable of their
+ * own, and the contents of the VICs variable is copied over the contents of the PixelEngines
+ * variable at the right time. Putting the state variables in seperate structures allows the compiler
+ * to optize the copy process.
+ */
typedef struct {
- //! @brief Internal x counter of the sequencer (sprite coordinate system)
+ //! @brief Internal x counter of the sequencer (sprite coordinate system)
uint16_t xCounter;
- //! @brief Sprite X coordinates
- /*! @discussion The X coordinate is a 9 bit value. For each sprite, the lower 8 bits are stored in a
- * seperate IO register, while the uppermost bits are packed in a single register (0xD010).
- * The sprites X coordinate is updated whenever one the corresponding IO register changes
- * its value. */
+ /*! @brief Sprite X coordinates
+ * @details The X coordinate is a 9 bit value. For each sprite, the lower 8 bits are stored in a
+ * seperate IO register, while the uppermost bits are packed in a single register (0xD010).
+ * The sprites X coordinate is updated whenever one the corresponding IO register changes
+ * its value.
+ */
uint16_t spriteX[8];
- //! @brief Sprite X expansion bits
+ //! @brief Sprite X expansion bits
uint8_t spriteXexpand;
- //! @brief Internal VIC-II register D011, control register 1
+ //! @brief Internal VIC-II register D011, control register 1
uint8_t registerCTRL1;
- //! @brief Internal VIC-II register D016, control register 2
+ //! @brief Internal VIC-II register D016, control register 2
uint8_t registerCTRL2;
- //! @brief Data value grabbed in gAccess()
+ //! @brief Data value grabbed in gAccess()
uint8_t g_data;
- //! @brief Character value grabbed in gAccess()
+ //! @brief Character value grabbed in gAccess()
uint8_t g_character;
- //! @brief Color value grabbed in gAccess()
+ //! @brief Color value grabbed in gAccess()
uint8_t g_color;
- //! @brief Main frame flipflop
+ //! @brief Main frame flipflop
uint8_t mainFrameFF;
- //! @brief Vertical frame Flipflop
+ //! @brief Vertical frame Flipflop
uint8_t verticalFrameFF;
} PixelEnginePipe;
-//! @brief Color for drawing border pixels
+//! @brief Color for drawing border pixels
typedef struct {
uint8_t borderColor;
@@ -264,14 +269,14 @@ typedef struct {
} BorderColorPipe;
-//! @brief Colors for drawing canvas pixels
+//! @brief Colors for drawing canvas pixels
typedef struct {
uint8_t backgroundColor[4];
} CanvasColorPipe;
-//! @brief Colors for drawing sprites
+//! @brief Colors for drawing sprites
typedef struct {
uint8_t spriteColor[8];
diff --git a/C64/VirtualComponent.cpp b/C64/VirtualComponent.cpp
index 2727a72..0b19339 100755
--- a/C64/VirtualComponent.cpp
+++ b/C64/VirtualComponent.cpp
@@ -19,13 +19,9 @@
#include "C64.h"
VirtualComponent::VirtualComponent()
-{
- name = "Unnamed component";
- debugLevel = DEBUG_LEVEL;
+{
running = false;
suspendCounter = 0;
- traceMode = false;
- logfile = NULL;
snapshotItems = NULL;
subComponents = NULL;
snapshotSize = 0;
@@ -33,16 +29,13 @@ VirtualComponent::VirtualComponent()
VirtualComponent::~VirtualComponent()
{
- // debug(2, "Terminated\n");
+ debug(3, "Terminated\n");
if (subComponents)
delete [] subComponents;
if (snapshotItems)
delete [] snapshotItems;
-
- if (logfile)
- fclose(logfile);
}
void
@@ -70,7 +63,7 @@ VirtualComponent::reset()
setTraceMode(false);
- debug(2, "Resetting...\n");
+ debug(3, "Resetting...\n");
}
@@ -136,83 +129,6 @@ VirtualComponent::dumpState()
{
}
-// ---------------------------------------------------------------------------------------------
-// Printing messages
-// ---------------------------------------------------------------------------------------------
-
-void
-VirtualComponent::msg(const char *fmt, ...)
-{
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s", buf);
-}
-
-void
-VirtualComponent::msg(int level, const char *fmt, ...)
-{
- if (level > debugLevel)
- return;
-
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s", buf);
-}
-
-void
-VirtualComponent::debug(const char *fmt, ...)
-{
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s: %s", name, buf);
-}
-
-void
-VirtualComponent::debug(int level, const char *fmt, ...)
-{
- if (level > debugLevel)
- return;
-
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s: %s", name, buf);
-}
-
-void
-VirtualComponent::warn(const char *fmt, ...)
-{
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s: WARNING: %s", name, buf);
-}
-
-void
-VirtualComponent::panic(const char *fmt, ...)
-{
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- fprintf(logfile ? logfile : stderr, "%s: PANIC: %s", name, buf);
-
- assert(0);
-}
// ---------------------------------------------------------------------------------------------
// Snapshots
@@ -243,15 +159,11 @@ VirtualComponent::registerSubComponents(VirtualComponent **components, unsigned
unsigned numItems = length / sizeof(VirtualComponent *);
- debug(2, "Registering %d components\n", numItems);
+ debug(3, "Registering %d components\n", numItems);
// Allocate new array on heap and copy array data
subComponents = new VirtualComponent*[numItems];
std::copy(components, components + numItems, &subComponents[0]);
-
- // for (unsigned i = 0; subComponents[i] != NULL; i++)
- // printf("%s ", subComponents[i]->name);
-
}
uint32_t
diff --git a/C64/VirtualComponent.h b/C64/VirtualComponent.h
index 7137636..681249d 100755
--- a/C64/VirtualComponent.h
+++ b/C64/VirtualComponent.h
@@ -2,27 +2,26 @@
* @header VirtualComponent.h
* @author Dirk W. Hoffmann, www.dirkwhoffmann.de
* @copyright 2006 - 2015 Dirk W. Hoffmann
- * @brief Declares Disk525 class
*/
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VIRTUAL_COMPONENT_INC
#define _VIRTUAL_COMPONENT_INC
-#include "basic.h"
+#include "VC64Object.h"
// Forward declarations
class C64;
@@ -33,7 +32,7 @@ class C64;
* The class comprises functions for resetting, suspending and resuming the component,
* as well as functions for loading and saving state (snapshots).
*/
-class VirtualComponent {
+class VirtualComponent : public VC64Object {
public:
@@ -41,18 +40,6 @@ public:
* @details This reference is setup in the reset method and provides easy access to all
* other components of the same virtual C64. */
C64 *c64;
-
- //! @brief Debug level
- /*! @details Debug messages are written either to console or a logfile. Set to 0 to omit messages.
- * @see logfile
- */
- unsigned debugLevel;
-
-protected:
-
- /*! @brief Name of this component.
- */
- const char *name;
private:
@@ -63,12 +50,6 @@ private:
* the 'halted' state.
*/
bool running;
-
- /*! @brief Indicates whether the component should print trace messages.
- * @details In trace mode, all components are requested to dump debug informatik perodically.
- * Only a few components will react to this flag.
- */
- bool traceMode;
/*! @brief The original state before the first call of suspend().
* @see suspend
@@ -81,25 +62,17 @@ private:
int suspendCounter;
public:
- /*! @brief Log file.
- * @details By default, this variable is NULL and all debug and trace messages are sent to
- * stdout or stderr. Assign a file handle, if you wish to send debug output to a file.
- */
- FILE *logfile;
-
- //! Constructor
+
+ //! @brief Constructor
VirtualComponent();
- //! Destructor
+ //! @brief Destructor
virtual ~VirtualComponent();
+
//
//! @functiongroup Initializing the component
//
-
- /*! @brief Assign name.
- */
- inline void setName(const char *componentName) { name = componentName; }
/*! @brief Assign top-level C64 object.
* @details The provided reference is propagated automatically to all sub components.
@@ -123,14 +96,6 @@ public:
//! @functiongroup Debugging the component
//
- /*! @brief Returns true iff trace mode is enabled.
- */
- inline bool tracingEnabled() { return traceMode; }
-
- /*! @brief Enables or disables trace mode.
- */
- inline void setTraceMode(bool b) { traceMode = b; }
-
//! @brief Print info about the internal state.
/*! @details This functions is intended for debugging purposes only. Any derived component should
* override this method and print out some useful debugging information.
@@ -181,48 +146,11 @@ public:
*/
void resume();
-
- //
- //! @functiongroup Printing messages to console
- //
-
- /*! @brief Prints message to console or a log file.
- */
- void msg(const char *fmt, ...);
-
- /*! @brief Prints message to console or a log file if debug level is high enough.
- */
- void msg(int level, const char *fmt, ...);
-
-
- /*! @brief Prints debug message to console or a log file
- * @details Debug messages are prefixed by a custom string naming the component.
- */
- void debug(const char *fmt, ...);
-
- /*! @brief Prints debug message to console or a log file if debug level is high enogh.
- * @details Debug messages are prefixed by a custom string naming the component.
- */
- void debug(int level, const char *fmt, ...);
-
- /*! @brief Prints warning message to console or a log file.
- * @details Warning messages are prefixed by a custom string naming the component.
- * Warning messages are printed when something unexpected is encountered.
- */
- void warn(const char *fmt, ...);
-
- /*! @brief Prints a panic message to console or a log file.
- * @details Panic messages are prefixed by a custom string naming the component.
- * Panic messages indicate that a code bug is encountered.
- */
- void panic(const char *fmt, ...);
-
//
//! @functiongroup Registering snapshot items and sub components
//
-
protected:
/*! @brief Type and behavior of a snapshot item
diff --git a/C64/_version.cc b/C64/_version.cc
old mode 100755
new mode 100644
index 3b61afd..321b603
--- a/C64/_version.cc
+++ b/C64/_version.cc
@@ -17,5 +17,5 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ---------------------------------------------------------------------------
-#define __VERSION_CC__
+#define __VERSION2_CC__
#include "siddefs.h"
diff --git a/C64/basic.h b/C64/basic.h
old mode 100755
new mode 100644
index 6a7ddd9..d4ac422
--- a/C64/basic.h
+++ b/C64/basic.h
@@ -1,19 +1,21 @@
-/*
- * (C) 2006 Dirk W. Hoffmann. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+/*!
+ * @header basic.h
+ * @author Dirk W. Hoffmann, www.dirkwhoffmann.de
+ * @copyright 2006 Dirk W. Hoffmann
+ */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BASIC_INC
@@ -92,92 +94,100 @@ void printReadable(const void *data, int length);
//
-/*! @brief Converts a PET character to a unicocde character.
- * @discussion This function uses the PET upper case character set.
- * @result Returns 0x00 if no unicode counterpart exists. */
+/*! @brief Converts a PET character to a unicocde character.
+ * @details This function uses the PET upper case character set.
+ * @result Returns 0x00 if no unicode counterpart exists.
+ */
uint16_t pet2unicode(uint8_t petchar);
-/*! @brief Converts a PET character to an ASCII character.
- * @discussion This function uses the PET upper case character set.
- * @result Returns '.' if no ASCII counterpart exists .
- * @deprecated Use pet2ascii instead. */
+/*! @brief Converts a PET character to an ASCII character.
+ * @details This function uses the PET upper case character set.
+ * @result Returns '.' if no ASCII counterpart exists.
+ * @deprecated Use pet2ascii instead.
+ */
char toASCII(char c);
-/*! @brief Converts a PET character to an ASCII character.
- * @discussion This function uses the PET upper case character set.
- * @result Returns '.' if no ASCII counterpart exists. */
+/*! @brief Converts a PET character to an ASCII character.
+ * @details This function uses the PET upper case character set.
+ * @result Returns '.' if no ASCII counterpart exists.
+ */
uint8_t pet2ascii(uint8_t petchar);
-/*! @brief Converts an PET string into a ASCII string. */
+/*! @brief Converts an PET string into a ASCII string.
+ */
void pet2ascii(char *petstring);
-/*! @brief Converts an ASCII character to a PET character.
- * @discussion This function translates into the unshifted PET character set. I.e., lower case characters are converted to uppercase characters.
- * @result Returns ' ' if the ASCII character is not covered. */
+/*! @brief Converts an ASCII character to a PET character.
+ * @details This function translates into the unshifted PET character set.
+ * I.e., lower case characters are converted to uppercase characters.
+ * @result Returns ' ' if the ASCII character is not covered.
+ */
uint8_t ascii2pet(uint8_t asciichar);
-/*! @brief Converts an ASCII string into a PET string. */
+//! @brief Converts an ASCII string into a PET string.
void ascii2pet(char *asciistring);
-//! Write ASCII representation of 8 bit value to a string
+//! @brief Writes the ASCII representation of 8 bit value to a string.
void binary8_to_string(uint8_t value, char *s);
-//! Write ASCII representation of 32 bit value to a string
+//! @brief Writes the ASCII representation of 32 bit value to a string.
void binary32_to_string(uint32_t value, char *s);
-//! Convert a BCD number to a binary value
+//! @brief Converts a BCD number to a binary value.
inline uint8_t BCDToBinary(uint8_t value) { return (10 * (value >> 4)) + (value & 0x0F); }
-//! Convert a binary value to a BCD number
+//! @brief Converts a binary value to a BCD number.
inline uint8_t BinaryToBCD(uint8_t value) { return ((value / 10) << 4) + (value % 10); }
-//! Increment BCD number by one
-inline uint8_t incBCD(uint8_t value) { return ((value & 0x0F) == 0x09) ? (value & 0xF0) + 0x10 : (value & 0xF0) + ((value + 0x01) & 0x0F); }
+//! @brief Increments a BCD number by one.
+inline uint8_t incBCD(uint8_t value) {
+ return ((value & 0x0F) == 0x09) ? (value & 0xF0) + 0x10 : (value & 0xF0) + ((value + 0x01) & 0x0F); }
//
//! Handling file and path names
//
-//! Extract directory from path
+//! @brief Extracts directory from a path.
inline std::string ExtractDirectory( const std::string& path )
{
return path.substr(0, path.find_last_of( '/' ) + 1);
}
-//! Extract filename from path
+//! @brief Extracts filename from a path.
inline std::string ExtractFilename( const std::string& path )
{
- return path.substr( path.find_last_of( '/' ) +1 );
+ return path.substr(path.find_last_of( '/' ) + 1);
}
-//! Change extension
+//! @brief Changes the file extension.
inline std::string ChangeExtension( const std::string& path, const std::string& ext )
{
std::string filename = ExtractFilename(path);
- return ExtractDirectory(path) + filename.substr(0, filename.find_last_of( '.' )) + ext;
+ return ExtractDirectory(path) + filename.substr(0, filename.find_last_of('.')) + ext;
}
-//! @brief Check file suffix
-/*! @discussion The function is used for determining the type of a file. */
+/*! @brief Check file suffix
+ * @details The function is used for determining the type of a file.
+ */
bool checkFileSuffix(const char *filename, const char *suffix);
-//! @brief Returns the size of a file in bytes
+//! @brief Returns the size of a file in bytes
int getSizeOfFile(const char *filename);
-//! @brief Check file size
-/*! @discussion The function is used for validating the size of a file.
- @param filename Path and name of the file to investigate
- @param min Expected minimum size (-1 if no lower bound exists)
- @param max Expected maximum size (-1 if no upper bound exists) */
+/*! @brief Checks the size of a file
+ * @details The function is used for validating the size of a file.
+ * @param filename Path and name of the file to investigate
+ * @param min Expected minimum size (-1 if no lower bound exists)
+ * @param max Expected maximum size (-1 if no upper bound exists)
+ */
bool checkFileSize(const char *filename, int min, int max);
-//! Check magic bytes of a file.
-/*! The function is used for determining the type of a file.
- \param filename Path and name of the file to investigate
- \param header Expected byte sequence, terminated by EOF
- \return Returns true iff magic bytes match
- \see Memory::isBasicRom Memory::isKernelRom Memory::isCharRom
+/*! @brief Checks the magic bytes of a file.
+ * @details The function is used for determining the type of a file.
+ * @param filename Path and name of the file to investigate.
+ * @param header Expected byte sequence, terminated by EOF.
+ * @return Returns true iff magic bytes match.
*/
bool
checkFileHeader(const char *filename, int *header);
@@ -186,33 +196,33 @@ checkFileHeader(const char *filename, int *header);
//! @functiongroup Managing time
//
-//! Application launch time in seconds
-/*! The value is read by function \a msec for computing the elapsed number of microseconds. */
+/*! @brief Application launch time in seconds
+ * @details The value is read by function msec for computing the elapsed number of microseconds.
+ */
extern long tv_base;
-//! Return the number of elapsed microseconds since program launch
+//! @brief Return the number of elapsed microseconds since program launch.
uint64_t usec();
-//! Reads the real-time clock (1/10th seconds)
+//! @brief Reads the real-time clock (1/10th seconds).
uint8_t localTimeSecFrac();
-//! Reads the real-time clock (seconds)
+//! @brief Reads the real-time clock (seconds).
uint8_t localTimeSec();
-//! Reads the real-time clock (minutes)
+//! @brief Reads the real-time clock (minutes).
uint8_t localTimeMin();
-//! Reads the real-time clock (hours)
+//! @brief Reads the real-time clock (hours).
uint8_t localTimeHour();
-//! Sleep for some microseconds
-// DEPRECATED. USE SleepUntil INSTEAD
+//! @brief Put the current thread to sleep for a certain amount of time.
void sleepMicrosec(uint64_t usec);
-/*! @brief Sleeps until kernel timer reaches kernelTargetTime
- * @param kernelEarlyWakeup To increase timing precision, the function wakes up the thread earlier
- * by this amount and waits actively in a delay loop until the deadline is reached.
- * @result Overshoot time (jitter), measured in kernel time. Smaller values are better, 0 is best.
+/*! @brief Sleeps until kernel timer reaches kernelTargetTime
+ * @param kernelEarlyWakeup To increase timing precision, the function wakes up the thread earlier
+ * by this amount and waits actively in a delay loop until the deadline is reached.
+ * @result Overshoot time (jitter), measured in kernel time. Smaller values are better, 0 is best.
*/
int64_t sleepUntil(uint64_t kernelTargetTime, uint64_t kernelEarlyWakeup = 0);
@@ -222,7 +232,5 @@ int64_t sleepUntil(uint64_t kernelTargetTime, uint64_t kernelEarlyWakeup = 0);
//! @functiongroup Debugging
//
-#define RANGE(val,min,max) ((val) >= (min) && (val) <= (max))
-
#endif
diff --git a/VC64GameCore.h b/VC64GameCore.h
index 01dbda3..12fc109 100644
--- a/VC64GameCore.h
+++ b/VC64GameCore.h
@@ -28,6 +28,27 @@
#import <Cocoa/Cocoa.h>
#import <OpenEmuBase/OEGameCore.h>
+const uint16_t MAC_F1 = 122;
+const uint16_t MAC_F2 = 120;
+const uint16_t MAC_F3 = 99;
+const uint16_t MAC_F4 = 118;
+const uint16_t MAC_F5 = 96;
+const uint16_t MAC_F6 = 97;
+const uint16_t MAC_F7 = 98;
+const uint16_t MAC_F8 = 100;
+const uint16_t MAC_APO = 39;
+const uint16_t MAC_DEL = 51;
+const uint16_t MAC_RET = 36;
+const uint16_t MAC_CL = 123;
+const uint16_t MAC_CR = 124;
+const uint16_t MAC_CU = 126;
+const uint16_t MAC_CD = 125;
+const uint16_t MAC_TAB = 48;
+const uint16_t MAC_SPC = 49;
+const uint16_t MAC_ESC = 53;
+const uint16_t MAC_HAT = 10;
+const uint16_t MAC_TILDE_US = 50;
+
@class OERingBuffer;
OE_EXPORTED_CLASS
diff --git a/VC64GameCore.mm b/VC64GameCore.mm
index 7082797..ed996cd 100644
--- a/VC64GameCore.mm
+++ b/VC64GameCore.mm
@@ -41,19 +41,36 @@
uint16_t *_soundBuffer;
uint32_t _pressedKeys[256];
BOOL _didRUN;
+
+ // Used to tell the system that the C64 has finished loading and is ready for interaction
+ BOOL isC64Ready;
+
+ // Used to control if we are still waiting for the prompt, or if we are finally there
+ BOOL isAtReadyPrompt;
+ BOOL waitingForReady;
+
+ // Used to make sure we don't send more text if it is in the process of auto typing already
+ BOOL isStillTyping;
+
+ //Controls weather we have loaded the game or still in the process of doing so
+ BOOL isGameLoading;
+ BOOL isGameLoaded;
}
- (int)translateKey:(char)key plainkey:(char)plainkey keycode:(short)keycode flags:(int)flags;
-- (void)pressKey:(char)c;
-- (void)releaseKey:(char)c;
+- (void)pressKey:(int)c;
+- (void)releaseKey:(int)c;
- (void)typeText:(NSString *)text;
- (void)typeText:(NSString *)text withDelay:(int)delay;
-- (BOOL)isC64ReadyToRUN;
+- (void)checkForReady;
+
- (BOOL)loadBIOSRoms;
@end
@implementation VC64GameCore
+
+
- (id)init
{
if((self = [super init]))
@@ -62,6 +79,16 @@
_soundBuffer = (uint16_t *)malloc(SOUNDBUFFERSIZE * sizeof(uint16_t));
memset(_soundBuffer, 0, SOUNDBUFFERSIZE * sizeof(uint16_t));
+
+ isC64Ready = false;
+
+ isAtReadyPrompt=false;
+ waitingForReady=false;
+
+ isStillTyping = false;
+
+ isGameLoading=false;
+ isGameLoaded=false;
// Keyboard initialization
for (int i = 0; i < 256; i++) {
@@ -87,14 +114,14 @@
// System
// TODO: Determine region
c64->setNTSC();
-
+
if(![self loadBIOSRoms])
return NO;
// Peripherals
- c64->setWarpLoad(false); // Leave disabled otherwise audio can get slightly out of sync
- c64->floppy->setSendSoundMessages(false);
- c64->floppy->setBitAccuracy(true); // Disable to put drive in a faster, but less compatible read-only mode
+ c64->setWarpLoad(true); // Leave disabled otherwise audio can get slightly out of sync
+ c64->floppy.setSendSoundMessages(true);
+ c64->floppy.setBitAccuracy(false); // Disable to put drive in a faster, but less compatible read-only mode
// Audio
c64->setReSID(true);
@@ -108,85 +135,81 @@
- (void)setupEmulation
{
// Power on sub components
- c64->sid->run();
-
- c64->cpu->clearErrorState();
- c64->floppy->cpu->clearErrorState();
+ c64->sid.run();
+ c64->cpu.clearErrorState();
+ c64->floppy.cpu.clearErrorState();
c64->restartTimer();
+
+
}
- (void)executeFrame
{
- // Lazy/Late Init, we need to send RUN when the system is ready
- if([self isC64ReadyToRUN])
- {
- NSString *fileExtension = [[_fileToLoad pathExtension] lowercaseString];
-
- if([fileExtension isEqualToString:@"d64"] ||
- [fileExtension isEqualToString:@"p00"] ||
- [fileExtension isEqualToString:@"prg"] ||
- [fileExtension isEqualToString:@"t64"])
- {
- if(c64->mountArchive(D64Archive::archiveFromArbitraryFile([_fileToLoad UTF8String])) &&
- c64->flushArchive(D64Archive::archiveFromArbitraryFile([_fileToLoad UTF8String]), 0))
- {
- [self typeText:@"RUN\n" withDelay:500000];
- //[self typeText:@"LOAD \"*\",8,1\n" withDelay:500000];
- //[self typeText:@"LOAD \"$\",8\n" withDelay:500000];
- //[self typeText:@"LIST\n" withDelay:500000];
- }
- }
- else if([fileExtension isEqualToString:@"tap"])
- {
- if(c64->insertTape(TAPArchive::archiveFromTAPFile([_fileToLoad UTF8String])))
- {
- [self typeText:@"LOAD\n" withDelay:500000];
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- usleep(400000);
- c64->datasette.pressPlay();
- });
- }
- }
- else if([fileExtension isEqualToString:@"crt"])
- {
- if(c64->attachCartridge(Cartridge::cartridgeFromFile([_fileToLoad UTF8String])))
- c64->reset();
- }
-
- _didRUN = YES;
- }
-
// Run the game loop ourselves
- int cyclesToRun = c64->vic->getCyclesPerFrame();
-
+ int cyclesToRun = c64->vic.getCyclesPerFrame();
+ int samples = c64->sid.getSampleRate() / (c64->isPAL() ? PAL_REFRESH_RATE : NTSC_REFRESH_RATE);
for(int i=0; i<cyclesToRun; ++i)
c64->executeOneCycle();
-
- int samples = c64->sid->getSampleRate() / (c64->isPAL() ? PAL_REFRESH_RATE : NTSC_REFRESH_RATE);
-
- if(_didRUN)
- {
+
+ if(_didRUN){
for(unsigned i = 0; i < samples; i++)
{
- float bytes = c64->sid->readData();
+ float bytes = c64->sid.readData();
bytes = bytes * 32767.0;
_soundBuffer[i] = (uint16_t)bytes;
}
-
+
[[self ringBufferAtIndex:0] write:_soundBuffer maxLength:samples * sizeof(uint16_t)];
+ }else{
+
+ if (!isC64Ready){
+ [self checkForReady]; //this is called every Execute frame C64 if not at ready prompt.
+ }else{
+ if(!isGameLoaded && !isGameLoading){ //If there is not game loaded, and we are not in the process of loading one, start the load procedure
+ NSString *fileExtension = [[_fileToLoad pathExtension] lowercaseString];
+
+ [self _loadGame:fileExtension ];
+
+ }else{
+ if(isGameLoaded && !_didRUN && !isStillTyping){
+ if (!waitingForReady ){
+ isAtReadyPrompt=false;
+ waitingForReady=true;
+ }
+ if (!isAtReadyPrompt){
+ [self checkForReady];
+ }else{
+ [self typeText:@"run \n" withDelay:50000];
+ _didRUN=true;
+
+ }
+ }
+ }
}
}
+}
+
+
- (void)resetEmulation
{
c64->reset();
+ isC64Ready=false;
+ isAtReadyPrompt=false;
+ isGameLoaded=false;
+ isGameLoading=false;
+ waitingForReady=false;
_didRUN = NO;
}
- (void)stopEmulation
{
c64->halt();
+ isC64Ready=false;
+ isAtReadyPrompt=false;
+ isGameLoaded=false;
+ isGameLoading=false;
+ waitingForReady=false;
_didRUN = NO;
[super stopEmulation];
@@ -198,18 +221,21 @@
}
// Doesn't seem to work correctly, audio still goes out of sync
-//- (void)fastForward:(BOOL)flag
-//{
-// flag ? c64->setAlwaysWarp(true) : c64->setAlwaysWarp(false);
-//
-// [super fastForward:flag];
-//}
+// Use setWarp instead of Always warp
+-(void)fastForward:(BOOL)flag
+{
+ flag ? c64->setWarp(true) : c64->setWarp(false);
+
+ [super fastForward:flag];
+
+
+}
#pragma mark - Video
- (const void *)videoBuffer
{
- return c64->vic->screenBuffer();
+ return c64->vic.screenBuffer();
}
- (OEIntSize)bufferSize
@@ -246,7 +272,7 @@
- (double)audioSampleRate
{
- return c64->sid->getSampleRate();
+ return c64->sid.getSampleRate();
}
- (NSUInteger)channelCount
@@ -256,32 +282,28 @@
#pragma mark - Save States
-- (BOOL)saveStateToFileAtPath:(NSString *)fileName
+- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
-// c64->suspend();
-//
-// Snapshot *saveState = new Snapshot;
-// c64->saveToSnapshot(saveState);
-// saveState->writeToFile([fileName UTF8String]);
-//
-// c64->resume();
-//
-// return YES;
- return NO;
+ c64->suspend();
+
+ Snapshot *saveState = new Snapshot;
+ c64->saveToSnapshot(saveState);
+ block(saveState->writeToFile(fileName.fileSystemRepresentation),nil);
+
+ c64->resume();
+
}
-- (BOOL)loadStateFromFileAtPath:(NSString *)fileName
+- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
{
-// c64->suspend();
-//
-// Snapshot *saveState = new Snapshot;
-// saveState->readFromFile([fileName UTF8String]);
-// c64->loadFromSnapshot(saveState);
-//
-// c64->resume();
-//
-// return YES;
- return NO;
+ c64->suspend();
+
+ Snapshot *saveState = new Snapshot;
+ block(saveState->readFromFile(fileName.fileSystemRepresentation),nil);
+ c64->loadFromSnapshot(saveState);
+
+ c64->resume();
+
}
#pragma mark - Input
@@ -314,22 +336,24 @@
- (int)translateKey:(char)key plainkey:(char)plainkey keycode:(short)keycode flags:(int)flags
{
switch (keycode) {
- case kHIDUsage_KeyboardF1: return Keyboard::C64KEY_F1;
- case kHIDUsage_KeyboardF2: return Keyboard::C64KEY_F2;
- case kHIDUsage_KeyboardF3: return Keyboard::C64KEY_F3;
- case kHIDUsage_KeyboardF4: return Keyboard::C64KEY_F4;
- case kHIDUsage_KeyboardF5: return Keyboard::C64KEY_F5;
- case kHIDUsage_KeyboardF6: return Keyboard::C64KEY_F6;
- case kHIDUsage_KeyboardF7: return Keyboard::C64KEY_F7;
- case kHIDUsage_KeyboardF8: return Keyboard::C64KEY_F8;
- case kHIDUsage_KeyboardDeleteOrBackspace: return (flags & NSShiftKeyMask) ? Keyboard::C64KEY_INS : Keyboard::C64KEY_DEL;
- case kHIDUsage_KeyboardReturnOrEnter: return Keyboard::C64KEY_RET;
- case kHIDUsage_KeyboardLeftArrow: return Keyboard::C64KEY_CL;
- case kHIDUsage_KeyboardRightArrow: return Keyboard::C64KEY_CR;
- case kHIDUsage_KeyboardUpArrow: return Keyboard::C64KEY_CU;
- case kHIDUsage_KeyboardDownArrow: return Keyboard::C64KEY_CD;
- //case MAC_HAT: return '^';
- //case MAC_TILDE_US: if (plainkey != '<' && plainkey != '>') return Keyboard::C64KEY_ARROW; else break;
+ case MAC_F1: return Keyboard::C64KEY_F1;
+ case MAC_F2: return Keyboard::C64KEY_F2;
+ case MAC_F3: return Keyboard::C64KEY_F3;
+ case MAC_F4: return Keyboard::C64KEY_F4;
+ case MAC_F5: return Keyboard::C64KEY_F5;
+ case MAC_F6: return Keyboard::C64KEY_F6;
+ case MAC_F7: return Keyboard::C64KEY_F7;
+ case MAC_F8: return Keyboard::C64KEY_F8;
+ case MAC_DEL: return (flags & NSShiftKeyMask) ? Keyboard::C64KEY_INS : Keyboard::C64KEY_DEL;
+ case MAC_RET: return Keyboard::C64KEY_RET;
+ case MAC_CL: return Keyboard::C64KEY_CL;
+ case MAC_CR: return Keyboard::C64KEY_CR;
+ case MAC_CU: return Keyboard::C64KEY_CU;
+ case MAC_CD: return Keyboard::C64KEY_CD;
+ case MAC_ESC: return Keyboard::C64KEY_RUNSTOP;
+ case MAC_TAB: return Keyboard::C64KEY_RESTORE;
+ case MAC_HAT: return '^';
+ case MAC_TILDE_US: if (plainkey != '<' && plainkey != '>') return Keyboard::C64KEY_ARROW; else break;
}
if (flags & NSAlternateKeyMask) {
@@ -344,7 +368,7 @@
- (oneway void)keyDown:(unsigned short)keyHIDCode characters:(NSString *)characters charactersIgnoringModifiers:(NSString *)charactersIgnoringModifiers flags:(NSEventModifierFlags)modifierFlags
{
// Do not accept input before RUN
- if(!_didRUN)
+ if(!isC64Ready)
return;
unsigned char c = [characters UTF8String][0];
@@ -374,13 +398,13 @@
// Press key
// NSLog(@"Storing key %c for keycode %ld",c64key, (long)keycode);
_pressedKeys[(unsigned char)keycode] = c64key;
- c64->keyboard->pressKey(c64key);
+ c64->keyboard.pressKey(c64key);
}
- (oneway void)keyUp:(unsigned short)keyHIDCode characters:(NSString *)characters charactersIgnoringModifiers:(NSString *)charactersIgnoringModifiers flags:(NSEventModifierFlags)modifierFlags
{
// Do not accept input before RUN
- if(!_didRUN)
+ if(!isC64Ready)
return;
unsigned short keycode = keyHIDCode;
@@ -394,40 +418,40 @@
// Release key
// NSLog(@"Releasing stored key %c for keycode %ld",pressedKeys[keycode], (long)keycode);
- c64->keyboard->releaseKey(_pressedKeys[keycode]);
+ c64->keyboard.releaseKey(_pressedKeys[keycode]);
_pressedKeys[(unsigned char)keycode] = 0;
}
- (oneway void)didPushC64Button:(OEC64Button)button forPlayer:(NSUInteger)player;
{
// Port 2 is used as the default for most programs and games due to technical reasons
- if(button == OEC64JoystickUp) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_Y_UP); }
- if(button == OEC64JoystickDown) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_Y_DOWN); }
- if(button == OEC64JoystickLeft) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_X_LEFT); }
- if(button == OEC64JoystickRight) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_X_RIGHT); }
- if(button == OEC64ButtonFire) { c64->joystick2->SetButtonPressed(true); }
+ if(button == OEC64JoystickUp) { c64->joystickA.setAxisY(JOYSTICK_UP); }
+ if(button == OEC64JoystickDown) { c64->joystickA.setAxisY(JOYSTICK_DOWN); }
+ if(button == OEC64JoystickLeft) { c64->joystickA.setAxisX(JOYSTICK_LEFT); }
+ if(button == OEC64JoystickRight) { c64->joystickA.setAxisX(JOYSTICK_RIGHT); }
+ if(button == OEC64ButtonFire) { c64->joystickA.setButtonPressed(true); }
}
- (oneway void)didReleaseC64Button:(OEC64Button)button forPlayer:(NSUInteger)player;
{
- if(button == OEC64JoystickUp) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_NONE); }
- if(button == OEC64JoystickDown) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_NONE); }
- if(button == OEC64JoystickLeft) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_NONE); }
- if(button == OEC64JoystickRight) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_NONE); }
- if(button == OEC64ButtonFire) { c64->joystick2->SetButtonPressed(false); }
+ if(button == OEC64JoystickUp) { c64->joystickB.setAxisY(JOYSTICK_UP); }
+ if(button == OEC64JoystickDown) { c64->joystickB.setAxisY(JOYSTICK_DOWN); }
+ if(button == OEC64JoystickLeft) { c64->joystickB.setAxisX(JOYSTICK_LEFT); }
+ if(button == OEC64JoystickRight) { c64->joystickB.setAxisX(JOYSTICK_RIGHT); }
+ if(button == OEC64ButtonFire) { c64->joystickB.setButtonPressed(false); }
}
#pragma mark - Misc & Helpers
-- (BOOL)isC64ReadyToRUN
-{
- // HACK: Wait until enough cycles have passed to assume we're at the prompt
- // and ready to RUN whatever has been flashed ("flush") into memory
- if (c64->getCycles() >= 2803451 && !_didRUN)
- return YES;
- else
- return NO;
-}
+//- (BOOL)isC64ReadyToRUN
+//{
+// // HACK: Wait until enough cycles have passed to assume we're at the prompt
+// // and ready to RUN whatever has been flashed ("flush") into memory
+// if (c64->getCycles() >= 2803451 && !_didRUN)
+// return YES;
+// else
+// return NO;
+//}
- (BOOL)loadBIOSRoms
{
@@ -435,7 +459,7 @@
// BASIC ROM
NSString *basicROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"basic.901226-01.bin"];
- if(!c64->mem->isBasicRom([basicROM UTF8String]))
+ if(!c64->mem.isBasicRom([basicROM UTF8String]))
{
NSLog(@"VirtualC64: %@ is not a valid Basic ROM!", basicROM);
return NO;
@@ -443,7 +467,7 @@
// "Kernal" ROM
NSString *kernelROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"kernal.901227-03.bin"];
- if(!c64->mem->isKernelRom([kernelROM UTF8String]))
+ if(!c64->mem.isKernelRom([kernelROM UTF8String]))
{
NSLog(@"VirtualC64: %@ is not a valid Kernal ROM!", kernelROM);
return NO;
@@ -451,7 +475,7 @@
// Char ROM
NSString *charROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"characters.901225-01.bin"];
- if(!c64->mem->isCharRom([charROM UTF8String]))
+ if(!c64->mem.isCharRom([charROM UTF8String]))
{
NSLog(@"VirtualC64: %@ is not a valid Char ROM!", charROM);
return NO;
@@ -469,26 +493,27 @@
return YES;
}
-- (void)pressKey:(char)c
+- (void)pressKey:(int)c
{
- c64->keyboard->pressKey(c);
+ c64->keyboard.pressKey(c);
}
-- (void)releaseKey:(char)c
+- (void)releaseKey:(int)c
{
- c64->keyboard->releaseKey(c);
+ c64->keyboard.releaseKey(c);
}
- (void)typeText:(NSString *)text
{
- [self typeText:text withDelay:0];
+ [self _typeText:text withDelay:0];
}
- (void)typeText:(NSString *)text withDelay:(int)delay
{
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [self _typeText:text withDelay:delay];
- });
+ while (isStillTyping)
+ usleep(50);
+
+ dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self _typeText:text withDelay:delay]; });
}
- (void)_typeText:(NSString *)text withDelay:(int)delay
@@ -496,26 +521,28 @@
const unsigned MAXCHARS = 256;
const unsigned KEYDELAY = 27500;
unsigned i;
-
- fprintf(stderr,"Typing: ");
-
+
+ isStillTyping= true;
+
+ fprintf(stderr, "Typing: ");
+
usleep(delay);
for (i = 0; i < [text length] && i < MAXCHARS; i++) {
-
+
unichar uc = [text characterAtIndex:i];
char c = (char)uc;
-
+
if (isupper(c))
c = tolower(c);
-
- fprintf(stderr,"%c",c);
-
+
+ fprintf(stderr, "%c",c);
+
usleep(KEYDELAY);
[self pressKey:c];
usleep(KEYDELAY);
[self releaseKey:c];
}
-
+
if (i != [text length]) {
// Abbreviate text by three dots
for (i = 0; i < 3; i++) {
@@ -525,8 +552,69 @@
usleep(KEYDELAY);
}
}
-
+
+ isStillTyping=false;
+
fprintf(stderr,"\n");
}
+- (void) _loadGame:(NSString *)fileExtension{
+ isGameLoading=true;
+
+ if([fileExtension isEqualToString:@"d64"] ||
+ [fileExtension isEqualToString:@"p00"] ||
+ [fileExtension isEqualToString:@"prg"] ||
+ [fileExtension isEqualToString:@"t64"]){
+
+ if(c64->mountArchive(D64Archive::archiveFromArbitraryFile([_fileToLoad UTF8String])) &&
+ c64->flushArchive(D64Archive::archiveFromArbitraryFile([_fileToLoad UTF8String]), 0)){
+
+ [self typeText:@"load \"*\",8,1\n" withDelay:5000 ];
+
+ }else if([fileExtension isEqualToString:@"tap"]){
+ if(c64->insertTape(TAPArchive::archiveFromTAPFile([_fileToLoad UTF8String]))){
+
+ [self typeText:@"LOAD\n" withDelay:5000];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{usleep(400000);c64->datasette.pressPlay();});
+ }}
+
+ }else if([fileExtension isEqualToString:@"crt"]) {
+ if(c64->attachCartridge(Cartridge::cartridgeFromFile([_fileToLoad UTF8String]))){
+ isGameLoaded=true;
+ _didRUN=true;
+ c64->reset();
+ }
+ }
+ isGameLoading=false;
+ isGameLoaded=true;
+
+}
+
+- (void) checkForReady{
+ int pnt = (c64->mem.peek(0x00d1)| (c64->mem.peek(0x00d2) <<8)); //Get Current Cursor position
+ int pntr = c64->mem.peek(0x00d3); // Current column on the line
+ int lnmx = c64->mem.peek(0x00d5)+1; // Get the line lenght
+ int blnsw = c64->mem.peek(0x00cc); // is the curson blinking? 0 is yes, 1 in no
+ int addrStrt = pnt - lnmx; // set the start position in Ram to start looking at the previous line
+ char *s = "READY."; // We are looking for READY.
+ bool charsFound = false;
+
+ for (int i = 0; s[i] != '\0'; i++) {
+ if (c64->mem.peek(addrStrt + i) == (s[i] % 64)) {
+ charsFound = true;
+ }else{
+ charsFound=false;
+ }
+ }
+
+ if (charsFound){
+ isAtReadyPrompt=true;
+ isC64Ready = true;
+ NSLog(@"Screen address: %d,%d, %d, %d", pnt,pntr, blnsw, lnmx);
+ }
+}
+
+
+
@end
diff --git a/VirtualC64.xcodeproj/project.pbxproj b/VirtualC64.xcodeproj/project.pbxproj
index b626251..a5fecbc 100644
--- a/VirtualC64.xcodeproj/project.pbxproj
+++ b/VirtualC64.xcodeproj/project.pbxproj
@@ -35,6 +35,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 3E3E75591C57D71600380EEB /* VC64Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3E3E75571C57D71600380EEB /* VC64Object.cpp */; };
82EC40A30FD9EC5A0017FC19 /* VC64GameCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = B5008DAE0E8BFB3E005AECAF /* VC64GameCore.mm */; };
8706C3D81C2FA1AC000C2893 /* NIBArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */; };
8745D7621BFBD56C005E8699 /* _version.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8745D7611BFBD56C005E8699 /* _version.cc */; };
@@ -129,6 +130,8 @@
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 3E3E75571C57D71600380EEB /* VC64Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VC64Object.cpp; sourceTree = "<group>"; };
+ 3E3E75581C57D71600380EEB /* VC64Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VC64Object.h; sourceTree = "<group>"; };
8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NIBArchive.cpp; sourceTree = "<group>"; };
8706C3D71C2FA1AC000C2893 /* NIBArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NIBArchive.h; sourceTree = "<group>"; };
8706C3D91C30C557000C2893 /* VIC_globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VIC_globals.h; sourceTree = "<group>"; };
@@ -323,6 +326,8 @@
EB03DCB216FE194000CF0752 /* C64 */ = {
isa = PBXGroup;
children = (
+ 3E3E75571C57D71600380EEB /* VC64Object.cpp */,
+ 3E3E75581C57D71600380EEB /* VC64Object.h */,
8745D7601BFBD56C005E8699 /* _spline.h */,
8745D7611BFBD56C005E8699 /* _version.cc */,
EB03DCB316FE194000CF0752 /* Archive.cpp */,
@@ -563,6 +568,7 @@
8745D7691BFBD59E005E8699 /* Datasette.cpp in Sources */,
EB03DD2716FE194000CF0752 /* voice.cc in Sources */,
8745D76B1BFBD59E005E8699 /* ExpansionPort.cpp in Sources */,
+ 3E3E75591C57D71600380EEB /* VC64Object.cpp in Sources */,
EB03DD2816FE194000CF0752 /* wave.cc in Sources */,
EB03DD2916FE194000CF0752 /* wave6581__ST.cc in Sources */,
EB03DD2A16FE194000CF0752 /* wave6581_P_T.cc in Sources */,
@@ -620,6 +626,7 @@
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../..\"",
@@ -634,6 +641,7 @@
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "\"$(USER_LIBRARY_DIR)/Application Support/OpenEmu/Cores\"";
LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/fceumm\"";
+ ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = VirtualC64;
SKIP_INSTALL = YES;
USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\"";
@@ -646,6 +654,7 @@
buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../..\"",