+9
-10
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-22
@@ -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
|
||||
//
|
||||
|
||||
+669
-715
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
Executable → Regular
+21
-53
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Executable → Regular
+94
-94
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
+86
-120
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
+4
-3
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable → Regular
+11
-9
@@ -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;
|
||||
}
|
||||
|
||||
Executable → Regular
+25
-22
@@ -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
|
||||
|
||||
Executable → Regular
+11
-7
@@ -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;
|
||||
}
|
||||
|
||||
Executable → Regular
+61
-46
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
+53
-67
@@ -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++]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-24
@@ -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
|
||||
|
||||
Executable → Regular
+14
-23
@@ -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();
|
||||
|
||||
Executable → Regular
+87
-53
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
Executable → Regular
+4
-2
@@ -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++) {
|
||||
|
||||
Executable → Regular
+24
-47
@@ -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);
|
||||
|
||||
|
||||
|
||||
Executable → Regular
+6
-6
@@ -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);
|
||||
|
||||
Executable → Regular
+67
-49
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
+6
-8
@@ -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;
|
||||
|
||||
+15
-12
@@ -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
|
||||
|
||||
+5
-7
@@ -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
|
||||
|
||||
Executable → Regular
+15
-11
@@ -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
|
||||
//
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Executable → Regular
+17
-39
@@ -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;
|
||||
}
|
||||
|
||||
Executable → Regular
+42
-44
@@ -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; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
+28
-2
@@ -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
|
||||
|
||||
|
||||
+70
-54
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
|
||||
Memory::Memory()
|
||||
{
|
||||
name = "MEM";
|
||||
setDescription("MEM");
|
||||
}
|
||||
|
||||
Memory::~Memory()
|
||||
|
||||
+25
-60
@@ -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:
|
||||
|
||||
Executable → Regular
+36
-41
@@ -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);
|
||||
|
||||
Executable → Regular
+34
-55
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
+14
-12
@@ -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)) {
|
||||
|
||||
+47
-45
@@ -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);
|
||||
|
||||
+2
-3
@@ -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];
|
||||
|
||||
+9
-20
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -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
|
||||
//
|
||||
|
||||
+10
-19
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+15
-12
@@ -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
|
||||
//
|
||||
|
||||
Executable → Regular
+4
-4
@@ -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));
|
||||
|
||||
Executable → Regular
+239
-198
@@ -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
|
||||
+5
-13
@@ -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;
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
Executable → Regular
+3
-5
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Executable → Regular
+52
-59
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
Executable → Regular
+2
-3
@@ -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;
|
||||
|
||||
Executable → Regular
+42
-36
@@ -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; }
|
||||
|
||||
+17
-37
@@ -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
|
||||
+28
-23
@@ -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();
|
||||
};
|
||||
|
||||
Executable → Regular
+7
-13
@@ -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;
|
||||
|
||||
Executable → Regular
+13
-10
@@ -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
|
||||
//
|
||||
|
||||
+2
-2
@@ -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[] = {
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
Executable → Regular
+44
-73
@@ -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;
|
||||
}
|
||||
|
||||
Executable → Regular
+138
-125
@@ -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(); }
|
||||
};
|
||||
|
||||
|
||||
Executable → Regular
+6
-6
@@ -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
|
||||
|
||||
Executable → Regular
+33
-30
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Executable → Regular
+19
-19
@@ -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");
|
||||
|
||||
Executable → Regular
+96
-72
@@ -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); }
|
||||
|
||||
|
||||
+20
-38
@@ -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
|
||||
|
||||
Executable → Regular
+104
-99
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
+17
-89
@@ -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
|
||||
|
||||
Executable → Regular
+1
-1
@@ -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"
|
||||
|
||||
Executable → Regular
+77
-69
@@ -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
|
||||
|
||||
|
||||
@@ -28,6 +28,27 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <OpenEmuBase/OEGameCore.h>
|
||||
|
||||
const uint16_t MAC_F1 = 122;
|
||||
const uint16_t MAC_F2 = 120;
|
||||
const uint16_t MAC_F3 = 99;
|
||||
const uint16_t MAC_F4 = 118;
|
||||
const uint16_t MAC_F5 = 96;
|
||||
const uint16_t MAC_F6 = 97;
|
||||
const uint16_t MAC_F7 = 98;
|
||||
const uint16_t MAC_F8 = 100;
|
||||
const uint16_t MAC_APO = 39;
|
||||
const uint16_t MAC_DEL = 51;
|
||||
const uint16_t MAC_RET = 36;
|
||||
const uint16_t MAC_CL = 123;
|
||||
const uint16_t MAC_CR = 124;
|
||||
const uint16_t MAC_CU = 126;
|
||||
const uint16_t MAC_CD = 125;
|
||||
const uint16_t MAC_TAB = 48;
|
||||
const uint16_t MAC_SPC = 49;
|
||||
const uint16_t MAC_ESC = 53;
|
||||
const uint16_t MAC_HAT = 10;
|
||||
const uint16_t MAC_TILDE_US = 50;
|
||||
|
||||
@class OERingBuffer;
|
||||
|
||||
OE_EXPORTED_CLASS
|
||||
|
||||
+234
-146
@@ -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; i<cyclesToRun; ++i)
|
||||
c64->executeOneCycle();
|
||||
|
||||
int samples = c64->sid->getSampleRate() / (c64->isPAL() ? PAL_REFRESH_RATE : NTSC_REFRESH_RATE);
|
||||
|
||||
if(_didRUN)
|
||||
{
|
||||
|
||||
if(_didRUN){
|
||||
for(unsigned i = 0; i < samples; i++)
|
||||
{
|
||||
float bytes = c64->sid->readData();
|
||||
float bytes = c64->sid.readData();
|
||||
bytes = bytes * 32767.0;
|
||||
_soundBuffer[i] = (uint16_t)bytes;
|
||||
}
|
||||
|
||||
|
||||
[[self ringBufferAtIndex:0] write:_soundBuffer maxLength:samples * sizeof(uint16_t)];
|
||||
}else{
|
||||
|
||||
if (!isC64Ready){
|
||||
[self checkForReady]; //this is called every Execute frame C64 if not at ready prompt.
|
||||
}else{
|
||||
if(!isGameLoaded && !isGameLoading){ //If there is not game loaded, and we are not in the process of loading one, start the load procedure
|
||||
NSString *fileExtension = [[_fileToLoad pathExtension] lowercaseString];
|
||||
|
||||
[self _loadGame:fileExtension ];
|
||||
|
||||
}else{
|
||||
if(isGameLoaded && !_didRUN && !isStillTyping){
|
||||
if (!waitingForReady ){
|
||||
isAtReadyPrompt=false;
|
||||
waitingForReady=true;
|
||||
}
|
||||
if (!isAtReadyPrompt){
|
||||
[self checkForReady];
|
||||
}else{
|
||||
[self typeText:@"run \n" withDelay:50000];
|
||||
_didRUN=true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)resetEmulation
|
||||
{
|
||||
c64->reset();
|
||||
isC64Ready=false;
|
||||
isAtReadyPrompt=false;
|
||||
isGameLoaded=false;
|
||||
isGameLoading=false;
|
||||
waitingForReady=false;
|
||||
_didRUN = NO;
|
||||
}
|
||||
|
||||
- (void)stopEmulation
|
||||
{
|
||||
c64->halt();
|
||||
isC64Ready=false;
|
||||
isAtReadyPrompt=false;
|
||||
isGameLoaded=false;
|
||||
isGameLoading=false;
|
||||
waitingForReady=false;
|
||||
_didRUN = NO;
|
||||
|
||||
[super stopEmulation];
|
||||
@@ -198,18 +221,21 @@
|
||||
}
|
||||
|
||||
// Doesn't seem to work correctly, audio still goes out of sync
|
||||
//- (void)fastForward:(BOOL)flag
|
||||
//{
|
||||
// flag ? c64->setAlwaysWarp(true) : c64->setAlwaysWarp(false);
|
||||
//
|
||||
// [super fastForward:flag];
|
||||
//}
|
||||
// Use setWarp instead of Always warp
|
||||
-(void)fastForward:(BOOL)flag
|
||||
{
|
||||
flag ? c64->setWarp(true) : c64->setWarp(false);
|
||||
|
||||
[super fastForward:flag];
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Video
|
||||
|
||||
- (const void *)videoBuffer
|
||||
{
|
||||
return c64->vic->screenBuffer();
|
||||
return c64->vic.screenBuffer();
|
||||
}
|
||||
|
||||
- (OEIntSize)bufferSize
|
||||
@@ -246,7 +272,7 @@
|
||||
|
||||
- (double)audioSampleRate
|
||||
{
|
||||
return c64->sid->getSampleRate();
|
||||
return c64->sid.getSampleRate();
|
||||
}
|
||||
|
||||
- (NSUInteger)channelCount
|
||||
@@ -256,32 +282,28 @@
|
||||
|
||||
#pragma mark - Save States
|
||||
|
||||
- (BOOL)saveStateToFileAtPath:(NSString *)fileName
|
||||
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
|
||||
{
|
||||
// c64->suspend();
|
||||
//
|
||||
// Snapshot *saveState = new Snapshot;
|
||||
// c64->saveToSnapshot(saveState);
|
||||
// saveState->writeToFile([fileName UTF8String]);
|
||||
//
|
||||
// c64->resume();
|
||||
//
|
||||
// return YES;
|
||||
return NO;
|
||||
c64->suspend();
|
||||
|
||||
Snapshot *saveState = new Snapshot;
|
||||
c64->saveToSnapshot(saveState);
|
||||
block(saveState->writeToFile(fileName.fileSystemRepresentation),nil);
|
||||
|
||||
c64->resume();
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)loadStateFromFileAtPath:(NSString *)fileName
|
||||
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
|
||||
{
|
||||
// c64->suspend();
|
||||
//
|
||||
// Snapshot *saveState = new Snapshot;
|
||||
// saveState->readFromFile([fileName UTF8String]);
|
||||
// c64->loadFromSnapshot(saveState);
|
||||
//
|
||||
// c64->resume();
|
||||
//
|
||||
// return YES;
|
||||
return NO;
|
||||
c64->suspend();
|
||||
|
||||
Snapshot *saveState = new Snapshot;
|
||||
block(saveState->readFromFile(fileName.fileSystemRepresentation),nil);
|
||||
c64->loadFromSnapshot(saveState);
|
||||
|
||||
c64->resume();
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Input
|
||||
@@ -314,22 +336,24 @@
|
||||
- (int)translateKey:(char)key plainkey:(char)plainkey keycode:(short)keycode flags:(int)flags
|
||||
{
|
||||
switch (keycode) {
|
||||
case kHIDUsage_KeyboardF1: return Keyboard::C64KEY_F1;
|
||||
case kHIDUsage_KeyboardF2: return Keyboard::C64KEY_F2;
|
||||
case kHIDUsage_KeyboardF3: return Keyboard::C64KEY_F3;
|
||||
case kHIDUsage_KeyboardF4: return Keyboard::C64KEY_F4;
|
||||
case kHIDUsage_KeyboardF5: return Keyboard::C64KEY_F5;
|
||||
case kHIDUsage_KeyboardF6: return Keyboard::C64KEY_F6;
|
||||
case kHIDUsage_KeyboardF7: return Keyboard::C64KEY_F7;
|
||||
case kHIDUsage_KeyboardF8: return Keyboard::C64KEY_F8;
|
||||
case kHIDUsage_KeyboardDeleteOrBackspace: return (flags & NSShiftKeyMask) ? Keyboard::C64KEY_INS : Keyboard::C64KEY_DEL;
|
||||
case kHIDUsage_KeyboardReturnOrEnter: return Keyboard::C64KEY_RET;
|
||||
case kHIDUsage_KeyboardLeftArrow: return Keyboard::C64KEY_CL;
|
||||
case kHIDUsage_KeyboardRightArrow: return Keyboard::C64KEY_CR;
|
||||
case kHIDUsage_KeyboardUpArrow: return Keyboard::C64KEY_CU;
|
||||
case kHIDUsage_KeyboardDownArrow: return Keyboard::C64KEY_CD;
|
||||
//case MAC_HAT: return '^';
|
||||
//case MAC_TILDE_US: if (plainkey != '<' && plainkey != '>') return Keyboard::C64KEY_ARROW; else break;
|
||||
case MAC_F1: return Keyboard::C64KEY_F1;
|
||||
case MAC_F2: return Keyboard::C64KEY_F2;
|
||||
case MAC_F3: return Keyboard::C64KEY_F3;
|
||||
case MAC_F4: return Keyboard::C64KEY_F4;
|
||||
case MAC_F5: return Keyboard::C64KEY_F5;
|
||||
case MAC_F6: return Keyboard::C64KEY_F6;
|
||||
case MAC_F7: return Keyboard::C64KEY_F7;
|
||||
case MAC_F8: return Keyboard::C64KEY_F8;
|
||||
case MAC_DEL: return (flags & NSShiftKeyMask) ? Keyboard::C64KEY_INS : Keyboard::C64KEY_DEL;
|
||||
case MAC_RET: return Keyboard::C64KEY_RET;
|
||||
case MAC_CL: return Keyboard::C64KEY_CL;
|
||||
case MAC_CR: return Keyboard::C64KEY_CR;
|
||||
case MAC_CU: return Keyboard::C64KEY_CU;
|
||||
case MAC_CD: return Keyboard::C64KEY_CD;
|
||||
case MAC_ESC: return Keyboard::C64KEY_RUNSTOP;
|
||||
case MAC_TAB: return Keyboard::C64KEY_RESTORE;
|
||||
case MAC_HAT: return '^';
|
||||
case MAC_TILDE_US: if (plainkey != '<' && plainkey != '>') return Keyboard::C64KEY_ARROW; else break;
|
||||
}
|
||||
|
||||
if (flags & NSAlternateKeyMask) {
|
||||
@@ -344,7 +368,7 @@
|
||||
- (oneway void)keyDown:(unsigned short)keyHIDCode characters:(NSString *)characters charactersIgnoringModifiers:(NSString *)charactersIgnoringModifiers flags:(NSEventModifierFlags)modifierFlags
|
||||
{
|
||||
// Do not accept input before RUN
|
||||
if(!_didRUN)
|
||||
if(!isC64Ready)
|
||||
return;
|
||||
|
||||
unsigned char c = [characters UTF8String][0];
|
||||
@@ -374,13 +398,13 @@
|
||||
// Press key
|
||||
// NSLog(@"Storing key %c for keycode %ld",c64key, (long)keycode);
|
||||
_pressedKeys[(unsigned char)keycode] = c64key;
|
||||
c64->keyboard->pressKey(c64key);
|
||||
c64->keyboard.pressKey(c64key);
|
||||
}
|
||||
|
||||
- (oneway void)keyUp:(unsigned short)keyHIDCode characters:(NSString *)characters charactersIgnoringModifiers:(NSString *)charactersIgnoringModifiers flags:(NSEventModifierFlags)modifierFlags
|
||||
{
|
||||
// Do not accept input before RUN
|
||||
if(!_didRUN)
|
||||
if(!isC64Ready)
|
||||
return;
|
||||
|
||||
unsigned short keycode = keyHIDCode;
|
||||
@@ -394,40 +418,40 @@
|
||||
|
||||
// Release key
|
||||
// NSLog(@"Releasing stored key %c for keycode %ld",pressedKeys[keycode], (long)keycode);
|
||||
c64->keyboard->releaseKey(_pressedKeys[keycode]);
|
||||
c64->keyboard.releaseKey(_pressedKeys[keycode]);
|
||||
_pressedKeys[(unsigned char)keycode] = 0;
|
||||
}
|
||||
|
||||
- (oneway void)didPushC64Button:(OEC64Button)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
// Port 2 is used as the default for most programs and games due to technical reasons
|
||||
if(button == OEC64JoystickUp) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_Y_UP); }
|
||||
if(button == OEC64JoystickDown) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_Y_DOWN); }
|
||||
if(button == OEC64JoystickLeft) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_X_LEFT); }
|
||||
if(button == OEC64JoystickRight) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_X_RIGHT); }
|
||||
if(button == OEC64ButtonFire) { c64->joystick2->SetButtonPressed(true); }
|
||||
if(button == OEC64JoystickUp) { c64->joystickA.setAxisY(JOYSTICK_UP); }
|
||||
if(button == OEC64JoystickDown) { c64->joystickA.setAxisY(JOYSTICK_DOWN); }
|
||||
if(button == OEC64JoystickLeft) { c64->joystickA.setAxisX(JOYSTICK_LEFT); }
|
||||
if(button == OEC64JoystickRight) { c64->joystickA.setAxisX(JOYSTICK_RIGHT); }
|
||||
if(button == OEC64ButtonFire) { c64->joystickA.setButtonPressed(true); }
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseC64Button:(OEC64Button)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
if(button == OEC64JoystickUp) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_NONE); }
|
||||
if(button == OEC64JoystickDown) { c64->joystick2->SetAxisY(JOYSTICK_AXIS_NONE); }
|
||||
if(button == OEC64JoystickLeft) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_NONE); }
|
||||
if(button == OEC64JoystickRight) { c64->joystick2->SetAxisX(JOYSTICK_AXIS_NONE); }
|
||||
if(button == OEC64ButtonFire) { c64->joystick2->SetButtonPressed(false); }
|
||||
if(button == OEC64JoystickUp) { c64->joystickB.setAxisY(JOYSTICK_UP); }
|
||||
if(button == OEC64JoystickDown) { c64->joystickB.setAxisY(JOYSTICK_DOWN); }
|
||||
if(button == OEC64JoystickLeft) { c64->joystickB.setAxisX(JOYSTICK_LEFT); }
|
||||
if(button == OEC64JoystickRight) { c64->joystickB.setAxisX(JOYSTICK_RIGHT); }
|
||||
if(button == OEC64ButtonFire) { c64->joystickB.setButtonPressed(false); }
|
||||
}
|
||||
|
||||
#pragma mark - Misc & Helpers
|
||||
|
||||
- (BOOL)isC64ReadyToRUN
|
||||
{
|
||||
// HACK: Wait until enough cycles have passed to assume we're at the prompt
|
||||
// and ready to RUN whatever has been flashed ("flush") into memory
|
||||
if (c64->getCycles() >= 2803451 && !_didRUN)
|
||||
return YES;
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
//- (BOOL)isC64ReadyToRUN
|
||||
//{
|
||||
// // HACK: Wait until enough cycles have passed to assume we're at the prompt
|
||||
// // and ready to RUN whatever has been flashed ("flush") into memory
|
||||
// if (c64->getCycles() >= 2803451 && !_didRUN)
|
||||
// return YES;
|
||||
// else
|
||||
// return NO;
|
||||
//}
|
||||
|
||||
- (BOOL)loadBIOSRoms
|
||||
{
|
||||
@@ -435,7 +459,7 @@
|
||||
|
||||
// BASIC ROM
|
||||
NSString *basicROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"basic.901226-01.bin"];
|
||||
if(!c64->mem->isBasicRom([basicROM UTF8String]))
|
||||
if(!c64->mem.isBasicRom([basicROM UTF8String]))
|
||||
{
|
||||
NSLog(@"VirtualC64: %@ is not a valid Basic ROM!", basicROM);
|
||||
return NO;
|
||||
@@ -443,7 +467,7 @@
|
||||
|
||||
// "Kernal" ROM
|
||||
NSString *kernelROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"kernal.901227-03.bin"];
|
||||
if(!c64->mem->isKernelRom([kernelROM UTF8String]))
|
||||
if(!c64->mem.isKernelRom([kernelROM UTF8String]))
|
||||
{
|
||||
NSLog(@"VirtualC64: %@ is not a valid Kernal ROM!", kernelROM);
|
||||
return NO;
|
||||
@@ -451,7 +475,7 @@
|
||||
|
||||
// Char ROM
|
||||
NSString *charROM = [[self biosDirectoryPath] stringByAppendingPathComponent:@"characters.901225-01.bin"];
|
||||
if(!c64->mem->isCharRom([charROM UTF8String]))
|
||||
if(!c64->mem.isCharRom([charROM UTF8String]))
|
||||
{
|
||||
NSLog(@"VirtualC64: %@ is not a valid Char ROM!", charROM);
|
||||
return NO;
|
||||
@@ -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
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
3E3E75591C57D71600380EEB /* VC64Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3E3E75571C57D71600380EEB /* VC64Object.cpp */; };
|
||||
82EC40A30FD9EC5A0017FC19 /* VC64GameCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = B5008DAE0E8BFB3E005AECAF /* VC64GameCore.mm */; };
|
||||
8706C3D81C2FA1AC000C2893 /* NIBArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */; };
|
||||
8745D7621BFBD56C005E8699 /* _version.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8745D7611BFBD56C005E8699 /* _version.cc */; };
|
||||
@@ -129,6 +130,8 @@
|
||||
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
3E3E75571C57D71600380EEB /* VC64Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VC64Object.cpp; sourceTree = "<group>"; };
|
||||
3E3E75581C57D71600380EEB /* VC64Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VC64Object.h; sourceTree = "<group>"; };
|
||||
8706C3D61C2FA1AC000C2893 /* NIBArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NIBArchive.cpp; sourceTree = "<group>"; };
|
||||
8706C3D71C2FA1AC000C2893 /* NIBArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NIBArchive.h; sourceTree = "<group>"; };
|
||||
8706C3D91C30C557000C2893 /* VIC_globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VIC_globals.h; sourceTree = "<group>"; };
|
||||
@@ -323,6 +326,8 @@
|
||||
EB03DCB216FE194000CF0752 /* C64 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3E3E75571C57D71600380EEB /* VC64Object.cpp */,
|
||||
3E3E75581C57D71600380EEB /* VC64Object.h */,
|
||||
8745D7601BFBD56C005E8699 /* _spline.h */,
|
||||
8745D7611BFBD56C005E8699 /* _version.cc */,
|
||||
EB03DCB316FE194000CF0752 /* Archive.cpp */,
|
||||
@@ -563,6 +568,7 @@
|
||||
8745D7691BFBD59E005E8699 /* Datasette.cpp in Sources */,
|
||||
EB03DD2716FE194000CF0752 /* voice.cc in Sources */,
|
||||
8745D76B1BFBD59E005E8699 /* ExpansionPort.cpp in Sources */,
|
||||
3E3E75591C57D71600380EEB /* VC64Object.cpp in Sources */,
|
||||
EB03DD2816FE194000CF0752 /* wave.cc in Sources */,
|
||||
EB03DD2916FE194000CF0752 /* wave6581__ST.cc in Sources */,
|
||||
EB03DD2A16FE194000CF0752 /* wave6581_P_T.cc in Sources */,
|
||||
@@ -620,6 +626,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../..\"",
|
||||
@@ -634,6 +641,7 @@
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "\"$(USER_LIBRARY_DIR)/Application Support/OpenEmu/Cores\"";
|
||||
LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/fceumm\"";
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = VirtualC64;
|
||||
SKIP_INSTALL = YES;
|
||||
USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\"";
|
||||
@@ -646,6 +654,7 @@
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../..\"",
|
||||
|
||||
Reference in New Issue
Block a user