Merge pull request #1 from duckey77/update-1.4.2

Update to 1.4.2
This commit is contained in:
duckey77
2016-01-27 15:46:32 -07:00
77 changed files with 19627 additions and 4317 deletions
+9 -10
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+302 -267
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+289 -275
View File
@@ -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
View File
@@ -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;
+400 -264
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -16,7 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "C64.h"
#include "D64Archive.h"
#include "T64Archive.h"
#include "PRGArchive.h"
#include "P00Archive.h"
#include "FileArchive.h"
typedef struct D64TrackInfo {
int numberOfSectors;
@@ -78,6 +82,7 @@ static const D64TrackInfo D64Map[] =
D64Archive::D64Archive()
{
setDescription("D64Archive");
memset(name, 0, sizeof(name));
memset(data, 0, sizeof(data));
memset(errors, 0, sizeof(errors));
@@ -116,16 +121,14 @@ D64Archive::isD64File(const char *filename)
D64Archive *
D64Archive::archiveFromD64File(const char *filename)
{
D64Archive *archive;
fprintf(stderr, "Loading D64 archive from D64 file...\n");
archive = new D64Archive();
D64Archive *archive = new D64Archive();
if (!archive->readFromFile(filename)) {
fprintf(stderr, "Failed to load archive\n");
delete archive;
archive = NULL;
return NULL;
}
archive->debug(1, "D64 archive created from file %s.\n", filename);
return archive;
}
@@ -158,18 +161,12 @@ D64Archive::archiveFromArbitraryFile(const char *filename)
D64Archive *
D64Archive::archiveFromD64Archive(D64Archive *otherArchive)
{
D64Archive *archive;
if (otherArchive == NULL)
return NULL;
fprintf(stderr, "Cloning D64 archive...\n");
D64Archive *archive = new D64Archive();
archive->debug(1, "Creating D64 archive by cloning other D64 archive ...\n");
if ((archive = new D64Archive()) == NULL) {
fprintf(stderr, "Failed to create D64 archive\n");
return NULL;
}
memcpy(archive->name, otherArchive->name, sizeof(archive->name));
memcpy(archive->data, otherArchive->data, sizeof(archive->data));
memcpy(archive->errors, otherArchive->errors, sizeof(archive->errors));
@@ -182,17 +179,11 @@ D64Archive::archiveFromD64Archive(D64Archive *otherArchive)
D64Archive *
D64Archive::archiveFromArchive(Archive *otherArchive)
{
D64Archive *archive;
if (otherArchive == NULL)
return NULL;
fprintf(stderr, "Creating D64 archive from an %s archive...\n", otherArchive->getTypeAsString());
if ((archive = new D64Archive()) == NULL) {
fprintf(stderr, "Failed to create D64 archive\n");
return NULL;
}
D64Archive *archive = new D64Archive();
archive->debug(1, "Creating D64 archive from a %s archive...\n", otherArchive->getTypeAsString());
// Copy file path
archive->setPath(otherArchive->getPath());
@@ -216,7 +207,7 @@ D64Archive::archiveFromArchive(Archive *otherArchive)
int byte;
unsigned num = 0;
fprintf(stderr, "Will write %d bytes\n", otherArchive->getSizeOfItem(i));
archive->debug(2, "Will write %d bytes\n", otherArchive->getSizeOfItem(i));
otherArchive->selectItem(i);
while ((byte = otherArchive->getByte()) != EOF) {
@@ -224,32 +215,29 @@ D64Archive::archiveFromArchive(Archive *otherArchive)
num++;
}
fprintf(stderr, "D64 item %d: %d bytes written\n", i, num);
archive->debug(2, "D64 item %d: %d bytes written\n", i, num);
// Item i has been written. Goto next free sector and proceed with the next item
(void)archive->nextTrackAndSector(track, sector, &track, &sector, true /* skip directory track */);
}
fprintf(stderr, "Archive created (item 0 has %d bytes)\n", archive->getSizeOfItem(0));
fprintf(stderr, "%s archive created (size of item 0 = %d).\n",
archive->debug(2, "Archive created (item 0 has %d bytes)\n", archive->getSizeOfItem(0));
archive->debug(2, "%s archive created (size of item 0 = %d).\n",
archive->getTypeAsString(), archive->getSizeOfItem(0));
return archive;
}
#if 0
D64Archive *
D64Archive::archiveFromDrive(VC1541 *drive)
{
D64Archive *archive;
int error;
fprintf(stderr, "Creating D64 archive from VC1541 drive...\n");
if ((archive = new D64Archive()) == NULL)
return NULL;
D64Archive *archive = new D64Archive();
archive->debug(1, "Creating D64 archive from VC1541 drive contents...\n");
// Perform test run
int error;
if (drive->disk.decodeDisk(NULL, &error) > D64_802_SECTORS_ECC || error) {
fprintf(stderr, "Cannot create archive (error code: %d)\n", error);
archive->warn("Cannot create archive (error code: %d)\n", error);
delete archive;
return NULL;
}
@@ -258,12 +246,12 @@ D64Archive::archiveFromDrive(VC1541 *drive)
archive->numTracks = 42;
drive->disk.decodeDisk(archive->data);
fprintf(stderr, "Archive has %d files\n", archive->getNumberOfItems());
fprintf(stderr, "Item %d has size: %d\n", 0, archive->getSizeOfItem(0));
archive->debug(2, "Archive has %d files\n", archive->getNumberOfItems());
archive->debug(2, "Item %d has size: %d\n", 0, archive->getSizeOfItem(0));
return archive;
}
#endif
//
// Virtual functions from Container class
@@ -284,45 +272,45 @@ D64Archive::readFromBuffer(const uint8_t *buffer, unsigned length)
{
case D64_683_SECTORS: // 35 tracks, no errors
fprintf(stderr, "D64 file contains 35 tracks, no EC bytes\n");
debug(2, "D64 file contains 35 tracks, no EC bytes\n");
numTracks = 35;
break;
case D64_683_SECTORS_ECC: // 35 tracks, 683 error bytes
fprintf(stderr, "D64 file contains 35 tracks, 683 EC bytes\n");
debug(2, "D64 file contains 35 tracks, 683 EC bytes\n");
numTracks = 35;
numberOfErrors = 683;
break;
case D64_768_SECTORS: // 40 tracks, no errors
fprintf(stderr, "D64 file contains 40 tracks, no EC bytes\n");
debug(2, "D64 file contains 40 tracks, no EC bytes\n");
numTracks = 40;
break;
case D64_768_SECTORS_ECC: // 40 tracks, 768 error bytes
fprintf(stderr, "D64 file contains 40 tracks, 768 EC bytes\n");
debug(2, "D64 file contains 40 tracks, 768 EC bytes\n");
numTracks = 40;
numberOfErrors = 768;
break;
case D64_802_SECTORS: // 42 tracks, no error bytes
fprintf(stderr, "D64 file contains 42 tracks, no EC bytes\n");
debug(2, "D64 file contains 42 tracks, no EC bytes\n");
numTracks = 42;
break;
case D64_802_SECTORS_ECC: // 42 tracks, 802 error bytes
fprintf(stderr, "D64 file contains 42 tracks, 802 EC bytes\n");
debug(2, "D64 file contains 42 tracks, 802 EC bytes\n");
numTracks = 42;
numberOfErrors = 802;
break;
default:
fprintf(stderr, "D64 has an unknown format\n");
warn("D64 has an unknown format\n");
return false;
}
@@ -395,7 +383,7 @@ D64Archive::getNumberOfItems()
return i;
#endif
unsigned offsets[MAX_FILES_ON_DISK];
unsigned offsets[144]; // a C64 disk contains at most 144 files
unsigned noOfFiles;
scanDirectory(offsets, &noOfFiles);
@@ -493,8 +481,6 @@ D64Archive::selectItem(int item)
{
fp = -1;
// fprintf(stderr, "selectItem:%d\n", item);
// check, if item exists
if (item >= getNumberOfItems())
return;
@@ -503,8 +489,6 @@ D64Archive::selectItem(int item)
if ((fp = findDirectoryEntry(item)) <= 0)
return;
// fprintf(stderr, "First data sector: %02X, %02X", data[fp+0x03], data[fp+0x04]);
// find first data sector
if ((fp = offset(data[fp+0x01], data[fp+0x02])) < 0)
return;
@@ -517,7 +501,6 @@ D64Archive::selectItem(int item)
fp += 2;
// We finally reached the first real data byte :-)
// fprintf(stderr, "Item selected (%d,%d)\n", data[fp+0x03], data[fp+0x04]);
}
int
@@ -610,6 +593,12 @@ D64Archive::numberOfTracks()
return numTracks;
}
void
D64Archive::setNumberOfTracks(unsigned tracks)
{
assert(numTracks == 35 || numTracks == 40 || numTracks == 42);
numTracks = tracks;
}
//
@@ -625,7 +614,7 @@ D64Archive::findSector(unsigned track, unsigned sector)
int
D64Archive::offset(int track, int sector)
{
assert(isTrackNumber(track));
assert(1 <= track && track <= 42);
assert(sector < D64Map[track].numberOfSectors);
return D64Map[track].offset + (sector * 256);
@@ -684,13 +673,12 @@ D64Archive::writeByteToSector(uint8_t byte, uint8_t *t, uint8_t *s)
uint8_t positionOfLastDataByte = data[pos + 1];
if (positionOfLastDataByte == 0xFF) {
// fprintf(stderr, "%d/%d is full. ", track, sector);
// No rool in this sector, proceed to next one
// No free slots in this sector, proceed to next one
if (!nextTrackAndSector(track, sector, &track, &sector, true /* skip directory track */)) {
// Sorry, disk is full
return false;
return false; // Sorry, disk is full
}
// fprintf(stderr, "Switching to %d/%d\n", track, sector);
// link previous sector with the new one
data[pos++] = track;
data[pos] = sector;
@@ -725,8 +713,6 @@ D64Archive::markSectorAsUsed(uint8_t track, uint8_t sector)
{
// For each track and sector, there exists a single bit in the BAM. 1 = used, 0 = unused
// fprintf(stderr,"Marking track %d and sector %d as used\n", track, sector);
// First byte of BAM
int bam = offset(18,0);
@@ -828,7 +814,7 @@ D64Archive::scanDirectory(unsigned *offsets, unsigned *noOfFiles, bool skipInvis
pos += 2; // Move to the beginning of the first directory entry
while (i < MAX_FILES_ON_DISK) {
while (i < 144 /* maximum number of files on disk */) {
// Only proceed if the directory entry is not a null entry
const char nullEntry[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
@@ -864,7 +850,7 @@ D64Archive::scanDirectory(unsigned *offsets, unsigned *noOfFiles, bool skipInvis
int
D64Archive::findDirectoryEntry(int item, bool skipInvisibleFiles)
{
unsigned offsets[MAX_FILES_ON_DISK];
unsigned offsets[144];
unsigned noOfFiles;
scanDirectory(offsets, &noOfFiles, skipInvisibleFiles);
@@ -878,8 +864,8 @@ D64Archive::writeDirectoryEntry(unsigned nr, const char *name, uint8_t startTrac
{
int pos;
if (nr >= MAX_FILES_ON_DISK) {
fprintf(stderr, "Cannot write directory entry. Number of files is limited to %d\n", MAX_FILES_ON_DISK);
if (nr >= 144) {
warn("Cannot write directory entry. Number of files is limited to 144\n");
return false;
}
@@ -941,9 +927,9 @@ D64Archive::dumpSector(int track, int sector)
{
int pos = offset(track, sector);
fprintf(stderr, "Sector %d/%d\n", track, sector);
msg("Sector %d/%d\n", track, sector);
for (int i = 0; i < 256; i++) {
fprintf(stderr, "%02X ", data[pos++]);
msg("%02X ", data[pos++]);
}
}
+36 -24
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+16 -16
View File
@@ -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
+4 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -20,7 +20,7 @@
Memory::Memory()
{
name = "MEM";
setDescription("MEM");
}
Memory::~Memory()
+25 -60
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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[] = {
+77 -60
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+108
View File
@@ -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);
}
+144
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+389 -330
View File
File diff suppressed because it is too large Load Diff
Executable → Regular
+104 -99
View File
@@ -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];
+4 -92
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+21
View File
@@ -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
View File
@@ -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
+9
View File
@@ -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)/../..\"",
+14936
View File
File diff suppressed because it is too large Load Diff