From 033e23c72e2c07c7e02f20806e717f11a7130d81 Mon Sep 17 00:00:00 2001 From: duckey77 Date: Wed, 27 Jan 2016 15:43:47 -0700 Subject: [PATCH] Update to 1.4.2 Update VirtualC64 to 1.4.2 created autoload& run functionality first attempt at working save states --- C64/Archive.cpp | 19 +- C64/Archive.h | 53 +- C64/C64.cpp | 1384 ++- C64/C64.h | 569 +- C64/C64Memory.cpp | 74 +- C64/C64Memory.h | 188 +- C64/CIA.cpp | 206 +- C64/CIA.h | 564 +- C64/CPU.cpp | 7 +- C64/CPU.h | 664 +- C64/Cartridge.cpp | 20 +- C64/Cartridge.h | 47 +- C64/Container.cpp | 18 +- C64/Container.h | 107 +- C64/D64Archive.cpp | 120 +- C64/D64Archive.h | 60 +- C64/Datasette.cpp | 37 +- C64/Datasette.h | 140 +- C64/Disk525.cpp | 6 +- C64/Disk525.h | 71 +- C64/ExpansionPort.cpp | 12 +- C64/ExpansionPort.h | 116 +- C64/FileArchive.cpp | 14 +- C64/FileArchive.h | 27 +- C64/G64Archive.cpp | 12 +- C64/G64Archive.h | 26 +- C64/IEC.cpp | 8 +- C64/IEC.h | 32 +- C64/Instructions.cpp | 12 +- C64/Joystick.cpp | 56 +- C64/Joystick.h | 86 +- C64/Keyboard.cpp | 30 +- C64/Keyboard.h | 124 +- C64/Memory.cpp | 2 +- C64/Memory.h | 85 +- C64/Message.cpp | 77 +- C64/Message.h | 89 +- C64/NIBArchive.cpp | 26 +- C64/NIBArchive.h | 92 +- C64/OldSID.cpp | 5 +- C64/P00Archive.cpp | 29 +- C64/P00Archive.h | 27 +- C64/PRGArchive.cpp | 29 +- C64/PRGArchive.h | 27 +- C64/PixelEngine.cpp | 8 +- C64/PixelEngine.h | 437 +- C64/ReSID.cpp | 18 +- C64/ReSID.h | 4 +- C64/SIDWrapper.cpp | 8 +- C64/SIDWrapper.h | 111 +- C64/Snapshot.cpp | 5 +- C64/Snapshot.h | 78 +- C64/T64Archive.cpp | 54 +- C64/T64Archive.h | 51 +- C64/TAPArchive.cpp | 20 +- C64/TAPArchive.h | 23 +- C64/TOD.cpp | 4 +- C64/TOD.h | 137 +- C64/VC1541.cpp | 117 +- C64/VC1541.h | 263 +- C64/VC1541Memory.cpp | 12 +- C64/VC1541Memory.h | 63 +- C64/VC64Object.cpp | 108 + C64/VC64Object.h | 144 + C64/VIA6522.cpp | 38 +- C64/VIA6522.h | 168 +- C64/VIC.cpp | 58 +- C64/VIC.h | 719 +- C64/VIC_globals.h | 203 +- C64/VirtualComponent.cpp | 96 +- C64/VirtualComponent.h | 106 +- C64/_version.cc | 2 +- C64/basic.h | 146 +- VC64GameCore.h | 21 + VC64GameCore.mm | 380 +- VirtualC64.xcodeproj/project.pbxproj | 9 + temp | 14936 +++++++++++++++++++++++++ 77 files changed, 19627 insertions(+), 4317 deletions(-) mode change 100755 => 100644 C64/C64Memory.cpp mode change 100755 => 100644 C64/C64Memory.h mode change 100755 => 100644 C64/Cartridge.cpp mode change 100755 => 100644 C64/Cartridge.h mode change 100755 => 100644 C64/Container.cpp mode change 100755 => 100644 C64/Container.h mode change 100755 => 100644 C64/Datasette.cpp mode change 100755 => 100644 C64/Datasette.h mode change 100755 => 100644 C64/Disk525.cpp mode change 100755 => 100644 C64/Disk525.h mode change 100755 => 100644 C64/ExpansionPort.cpp mode change 100755 => 100644 C64/ExpansionPort.h mode change 100755 => 100644 C64/G64Archive.h mode change 100755 => 100644 C64/Joystick.cpp mode change 100755 => 100644 C64/Joystick.h mode change 100755 => 100644 C64/Message.cpp mode change 100755 => 100644 C64/Message.h mode change 100755 => 100644 C64/PixelEngine.cpp mode change 100755 => 100644 C64/PixelEngine.h mode change 100755 => 100644 C64/SIDWrapper.cpp mode change 100755 => 100644 C64/SIDWrapper.h mode change 100755 => 100644 C64/Snapshot.cpp mode change 100755 => 100644 C64/Snapshot.h mode change 100755 => 100644 C64/TAPArchive.cpp mode change 100755 => 100644 C64/TAPArchive.h mode change 100755 => 100644 C64/VC1541.cpp mode change 100755 => 100644 C64/VC1541.h mode change 100755 => 100644 C64/VC1541Memory.cpp mode change 100755 => 100644 C64/VC1541Memory.h create mode 100644 C64/VC64Object.cpp create mode 100644 C64/VC64Object.h mode change 100755 => 100644 C64/VIA6522.cpp mode change 100755 => 100644 C64/VIA6522.h mode change 100755 => 100644 C64/VIC_globals.h mode change 100755 => 100644 C64/_version.cc mode change 100755 => 100644 C64/basic.h create mode 100644 temp 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,55 +169,513 @@ 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(); } + +// +// Running the emulator +// + +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); + } +} + +void +C64::threadCleanup() +{ + p = NULL; + debug(1, "Execution thread cleanup\n"); +} + +bool +C64::isRunnable() { + return mem.basicRomIsLoaded() && mem.charRomIsLoaded() && mem.kernelRomIsLoaded() && floppy.mem.romIsLoaded(); +} + +bool +C64::isRunning() +{ + return p != NULL; +} + +void +C64::halt() +{ + 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::isHalted() +{ + return p == NULL; +} + +#if 0 +void +C64::restore() +{ + debug("RESTORE key\n"); + + // Hitting the restore key triggeres an NMI interrupt + cpu.setNMILineReset(); +} + +void +C64::runstopRestore() +{ + // 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(); +} + +// From Wolfgang Lorenz: Clock.txt +// +// o2 high phase | o2 low phase +// .-----. | .----------------. .------. .------. +// | | | | 1. Next Clock | | | | | +// .-> | CPU | ----> | 2. Fire Timers | --> | CIA1 | --> | CIA2 | -. +// | | | | | 3. VIC | | | | | | +// | '-----' | '----------------' '------' '------' | +// '---------------------------------------------------------------' + +#define EXECUTE(x) \ +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); +} + +void +C64::endOfRasterline() +{ + 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); + + // Execute the IEC bus + iec.execute(); + + // Count some sheep (zzzzzz) ... + if (!getWarp()) + synchronizeTiming(); + } +} + + +// +//! @functiongroup Managing the execution thread +// + void C64::setWarp(bool b) { @@ -261,17 +684,16 @@ C64::setWarp(bool b) 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. + // 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. if (warp) { // Quickly fade out SID - sid->rampDown(); + sid.rampDown(); } else { // Smoothly fade in SID - sid->rampUp(); + sid.rampUp(); restartTimer(); } @@ -297,618 +719,39 @@ C64::setWarpLoad(bool b) warpLoad = b; } -// ----------------------------------------------------------------------------------------------- -// Loading and saving -// ----------------------------------------------------------------------------------------------- - -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->setPAL(isPAL()); - snapshot->takeScreenshot((uint32_t *)vic->screenBuffer(), isPAL()); - - snapshot->alloc(stateSize()); - uint8_t *ptr = snapshot->getData(); - saveToBuffer(&ptr); -} - - -// ----------------------------------------------------------------------------------------------- -// Control -// ----------------------------------------------------------------------------------------------- - -void -C64::runstopRestore() -{ - // 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(); -} - -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; -} - -void -C64::halt() -{ - 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::isHalted() -{ - return p == NULL; -} - 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(); -} - -// From Wolfgang Lorenz: Clock.txt -// -// o2 high phase | o2 low phase -// .-----. | .----------------. .------. .------. -// | | | | 1. Next Clock | | | | | -// .-> | CPU | ----> | 2. Fire Timers | --> | CIA1 | --> | CIA2 | -. -// | | | | | 3. VIC | | | | | | -// | '-----' | '----------------' '------' '------' | -// '---------------------------------------------------------------' - -#define EXECUTE(x) \ - cia1->executeOneCycle(); \ - cia2->executeOneCycle(); \ - if (!cpu->executeOneCycle()) result = false; \ - if (!floppy->executeOneCycle()) result = false; \ - datasette.execute(); \ - cycles++; \ - rasterlineCycle++; - -void -C64::beginOfRasterline() -{ - // First cycle of rasterline - if (rasterline == 0) { - vic->beginFrame(); - } - vic->beginRasterline(rasterline); -} - -void -C64::endOfRasterline() -{ - 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(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(); - - // 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; -} - -bool -C64::loadRom(const char *filename) -{ - 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; -} - -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; -} - - -// ----------------------------------------------------------------------------------------------- -// Timing -// ----------------------------------------------------------------------------------------------- - -void -C64::restartTimer() +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(); + return expansionport.getCartridgeAttached(); } - - -// --------------------------------------------------------------------------------------------- -// Misc -// --------------------------------------------------------------------------------------------- - -void -C64::threadCleanup() -{ - p = NULL; - debug(1, "Execution thread cleanup\n"); -} - 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 +/*! + * @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 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; - - //! Reference to the connected virtual CPU. - CPU *cpu; - - //! Reference to the connected virtual video controller (VIC). - VIC *vic; - - //! Reference to the first connected virtual complex interface adapter (CIA 1). - CIA1 *cia1; - - //! Reference to the first connected virtual complex interface adapter (CIA 2). - CIA2 *cia2; - - //! Reference to the connected sound interface device (SID). - SIDWrapper *sid; - - //! Reference to the connected virtual keyboard. - Keyboard *keyboard; - - //! Reference to joystick in port 1 - Joystick *joystick1; + // ----------------------------------------------------------------------------------------------- + // Properties + // ----------------------------------------------------------------------------------------------- - //! Reference to joystick in port 2 - Joystick *joystick2; +public: + + // + // Sub components + // + + //! @brief The C64s virtual memory (ROM, RAM, and color RAM) + C64Memory mem; + + //! @brief The C64s virtual CPU + CPU cpu; + + //! @brief The C64s video controller chip + VIC vic; + + //! @brief The C64s first versatile interface adapter + CIA1 cia1; + + //! @brief The C64s second versatile interface adapter + CIA2 cia2; + + //! @brief The C64s sound chip + SIDWrapper sid; + + //! @brief The C64s virtual keyboard + Keyboard keyboard; + + //! @brief The C64s first virtual joystick (plugged into CONTROL PORT 1) + Joystick joystickA; - //! Reference to the virtual IEC bus - IEC *iec; + //! @brief The C64s second virtual joystick (plugged into CONTROL PORT 2) + Joystick joystickB; - //! Reference to the virtual expansion port (cartdrige slot) - ExpansionPort *expansionport; + //! @brief The C64s interface bus connecting the VC1541 drive + IEC iec; - //! Reference to the virtual VC1541 - VC1541 *floppy; + //! @brief The C64s virtual expansion port (cartdrige slot) + ExpansionPort expansionport; - //! Virtual tape drive (Datasette) + //! @brief A virtual VC1541 floppy drive + VC1541 floppy; + + //! @brief A virtual datasette Datasette datasette; private: - //! The execution thread - pthread_t p; + // + // Execution thread + // - //! System timer information (needed for running the execution thread at the desired speed) + //! @brief The emulators execution thread + pthread_t p; + + /*! @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; - //! 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]; - - //! ring buffer write pointer - unsigned backInTimeWritePtr; - - // ----------------------------------------------------------------------------------------------- - // 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,271 +251,308 @@ private: //! Indicates that we should run as fast as possible at least during disk operations bool warpLoad; + + + // + // Executed cycle, rasterline, and frame + // -public: + //! @brief Elapsed C64 clock cycles since power up + uint64_t cycle; - //! 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 + //! @brief Total number of frames drawn since power up uint64_t frame; - //! Current rasterline number + //! @brief Currently drawn rasterline uint16_t rasterline; -private: - - //! Target time in nanoseconds - /*! Used to synchronize emulation speed. */ - uint64_t nanoTargetTime; + /*! @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; + + + // + // Message queue + // + + /*! @brief Message queue. + * @details Used to communicate with the graphical user interface. + */ + MessageQueue queue; + + // ----------------------------------------------------------------------------------------------- // Methods // ----------------------------------------------------------------------------------------------- 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); } + //! @brief Sets the SID chip model + void setChipModel(chip_model value) { sid.setChipModel(value); } - - // ----------------------------------------------------------------------------------------------- - // Loading and saving - // ----------------------------------------------------------------------------------------------- - -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(); - //! Flush specified item from archive into memory and delete archive - /*! All archive types are flushable */ + + // + //! @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); + + + // + //! @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. +/*! + * @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 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 { -public: - //! 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) - 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; + // ----------------------------------------------------------------------------------------------- + // Properties + // ----------------------------------------------------------------------------------------------- - //! 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 +public: + + //! @brief Start address of the CIA I/O space (CIA 1 and CIA 2) + static const uint16_t CIA_START_ADDR = 0xDC00; + //! @brief End address of the CIA I/O space (CIA 1 and CIA 2) + + static const uint16_t CIA_END_ADDR = 0xDDFF; + + //! @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(); + + //! @brief Simulates a falling edge on the flag pin 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); - + + // // 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); - - //! Simulates a joystick movement - /*! \param nr r 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 Custom implementation of poke + void poke(uint16_t addr, uint8_t value); + + /*! @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); + + //! @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 +/*! + * @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 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" +#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 + * @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(); - //! 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 Sets the number of tracks stored in this image + void setNumberOfTracks(unsigned tracks); + + + /*! @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,22 +1,22 @@ -/* - * (C) 2006 - 2015 Dirk W. Hoffmann. All rights reserved. +/*! + * @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 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)); } @@ -39,43 +39,21 @@ Joystick::~Joystick() { } +void +Joystick::reset() +{ + VirtualComponent::reset(); + + button = false; + axisX = JOYSTICK_RELEASED; + axisY = JOYSTICK_RELEASED; +} + void Joystick::dumpState() { msg("Joystick port\n"); msg("-------------\n"); - msg("Button: %s AxisX: %d AxisY: %d\n", _buttonPressed ? "YES" : "NO", _axisX, _axisY); + msg("Button: %s AxisX: %d AxisY: %d\n", button ? "YES" : "NO", axisX, axisY); } -bool Joystick::GetButtonPressed() -{ - return _buttonPressed; -} - -JoystickAxisState Joystick::GetAxisX() -{ - return _axisX; -} - -JoystickAxisState Joystick::GetAxisY() -{ - return _axisY; -} - -void Joystick::SetButtonPressed(bool pressed) -{ - // fprintf(stderr,"%p %s", this, __PRETTY_FUNCTION__); - _buttonPressed = pressed; -} - -void Joystick::SetAxisX(JoystickAxisState state) -{ - // fprintf(stderr,"%p %s", this, __PRETTY_FUNCTION__); - _axisX = state; -} - -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,21 +1,23 @@ -/* - * 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. - * - * 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 +/*! + * @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. + * + * 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. +/*! + * @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 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. +/*! + * @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 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. +/*! + * @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 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. +/*! + * @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 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. +/*! + * @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 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/VC64Object.cpp b/C64/VC64Object.cpp new file mode 100644 index 0000000..f0b9906 --- /dev/null +++ b/C64/VC64Object.cpp @@ -0,0 +1,108 @@ +/* + * Author: 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. + * + * 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 + */ + +#include "VC64Object.h" + +VC64Object::VC64Object() +{ + debugLevel = defaultDebugLevel; + traceMode = false; + description = NULL; +} + +VC64Object::~VC64Object() +{ + if (logfile) + fclose(logfile); +} + +unsigned VC64Object::defaultDebugLevel = 2; +FILE *VC64Object::logfile = NULL; + +// --------------------------------------------------------------------------------------------- +// Printing messages +// --------------------------------------------------------------------------------------------- + +#define VC64OBJ_PARSE \ + char buf[256]; \ + va_list ap; \ + va_start(ap, fmt); \ + vsnprintf(buf, sizeof(buf), fmt, ap); \ + va_end(ap); + +void +VC64Object::msg(const char *fmt, ...) +{ + VC64OBJ_PARSE; + fprintf(logfile ? logfile : stderr, "%s", buf); +} + +void +VC64Object::msg(int level, const char *fmt, ...) +{ + if (level > debugLevel) + return; + + VC64OBJ_PARSE; + fprintf(logfile ? logfile : stderr, "%s", buf); +} + +void +VC64Object::debug(const char *fmt, ...) +{ + VC64OBJ_PARSE; + if (description) + fprintf(logfile ? logfile : stderr, "%s: %s", description, buf); + else + fprintf(logfile ? logfile : stderr, "%s", buf); +} + +void +VC64Object::debug(int level, const char *fmt, ...) +{ + if (level > debugLevel) + return; + + VC64OBJ_PARSE; + if (description) + fprintf(logfile ? logfile : stderr, "%s: %s", description, buf); + else + fprintf(logfile ? logfile : stderr, "%s", buf); +} + +void +VC64Object::warn(const char *fmt, ...) +{ + VC64OBJ_PARSE; + if (description) + fprintf(logfile ? logfile : stderr, "%s: WARNING: %s", description, buf); + else + fprintf(logfile ? logfile : stderr, "WARNING: %s", buf); +} + +void +VC64Object::panic(const char *fmt, ...) +{ + VC64OBJ_PARSE; + if (description) + fprintf(logfile ? logfile : stderr, "%s: PANIC: %s", description, buf); + else + fprintf(logfile ? logfile : stderr, "PANIC: %s", buf); + + assert(0); +} diff --git a/C64/VC64Object.h b/C64/VC64Object.h new file mode 100644 index 0000000..3bb5d7b --- /dev/null +++ b/C64/VC64Object.h @@ -0,0 +1,144 @@ +/*! + * @header VC64Object.h + * @author Dirk W. Hoffmann, www.dirkwhoffmann.de + * @copyright 2006 - 2015 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. + * + * 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 _VC64OBJECT_INC +#define _VC64OBJECT_INC + +#include "basic.h" + +/*! @brief Common functionality of all VirtualC64 objects. + * @details This class defines the base functionality of all objects such as + * printing debug messages. + */ +class VC64Object { + +private: + + /*! @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. + * @note logfile is a class member, i.e., it is shared among all objects + */ + static FILE *logfile; + + /*! @brief Default debug level + * @details On object creation, this value is used as debug level. + */ + static unsigned defaultDebugLevel; + + /*! @brief Debug level + * @details Debug messages are written either to console or a logfile. Set to 0 to omit messages. + */ + unsigned debugLevel; + + /*! @brief Indicates whether the component should print trace messages. + * @details In trace mode, all components are requested to dump debug information perodically. + * Only a few components will react to this flag. + */ + bool traceMode; + + /*! @brief Textual description of this object + * @details Most debug output methods preceed their output with this string. + * @note The default value is NULL. In that case, no prefix is printed. + */ + const char *description; + +public: + + //! Constructor + VC64Object(); + + //! Destructor + virtual ~VC64Object(); + + + // + //! @functiongroup Initializing the component + // + + /*! @brief Sets the logfile. + */ + inline static void setLogfile(FILE *file) { logfile = file; } + + /*! @brief Sets the default debug level. + */ + inline static void setDefaultDebugLevel(unsigned level) { defaultDebugLevel = level; } + + /*! @brief Changes the debug level for a specific object. + */ + inline void setDebugLevel(unsigned level) { debugLevel = level; } + + /*! @brief Returns the textual description. + */ + inline const char *getDescription() { return description ? description : ""; } + + /*! @brief Assigns a textual description. + */ + inline void setDescription(const char *desc) { description = desc; } + + + // + //! @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; } + + // + //! @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, ...); +}; + +#endif \ No newline at end of file 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; } - bool stepperActive1() { return (orb & 0x02) != 0; } - bool engineRunning() { return (orb & 0x04) != 0; } - bool redLEDshining() { return (orb & 0x08) != 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; } + + //! @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. +/*! + * @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 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,20 +1,23 @@ -/* - * 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. - * - * 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 +/*! + * @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. + * + * 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. +/*! + * @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 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 #import +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..af35192 100644 --- a/VC64GameCore.mm +++ b/VC64GameCore.mm @@ -41,6 +41,20 @@ 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; @@ -48,12 +62,15 @@ - (void)releaseKey:(char)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; iexecuteOneCycle(); - - 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; @@ -471,24 +495,25 @@ - (void)pressKey:(char)c { - c64->keyboard->pressKey(c); + c64->keyboard.pressKey(c); } - (void)releaseKey:(char)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 = ""; }; 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 3E3E75571C57D71600380EEB /* VC64Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VC64Object.cpp; sourceTree = ""; }; + 3E3E75581C57D71600380EEB /* VC64Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VC64Object.h; sourceTree = ""; }; 8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NIBArchive.cpp; sourceTree = ""; }; 8706C3D71C2FA1AC000C2893 /* NIBArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NIBArchive.h; sourceTree = ""; }; 8706C3D91C30C557000C2893 /* VIC_globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VIC_globals.h; sourceTree = ""; }; @@ -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)/../..\"", diff --git a/temp b/temp new file mode 100644 index 0000000..a617d40 --- /dev/null +++ b/temp @@ -0,0 +1,14936 @@ +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 + #import + ++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; iexecuteOneCycle(); +- +- 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 = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; ++ 3E3E75571C57D71600380EEB /* VC64Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VC64Object.cpp; sourceTree = ""; }; ++ 3E3E75581C57D71600380EEB /* VC64Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VC64Object.h; sourceTree = ""; }; + 8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NIBArchive.cpp; sourceTree = ""; }; + 8706C3D71C2FA1AC000C2893 /* NIBArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NIBArchive.h; sourceTree = ""; }; + 8706C3D91C30C557000C2893 /* VIC_globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VIC_globals.h; sourceTree = ""; }; +@@ -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)/../..\"",