Update VirtualC64 to 1.4.2 created autoload& run functionality first attempt at working save states
14937 lines
521 KiB
Plaintext
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, §or, 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, §or, 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)/../..\"",
|