2017-03-24 02:25:33 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// SSSS tt lll lll
|
|
|
|
// SS SS tt ll ll
|
|
|
|
// SS tttttt eeee ll ll aaaa
|
|
|
|
// SSSS tt ee ee ll ll aa
|
|
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
|
|
// SS SS tt ee ll ll aa aa
|
|
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
|
|
//
|
2017-03-24 20:32:08 +00:00
|
|
|
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
|
2017-03-24 02:25:33 +00:00
|
|
|
// and the Stella Team
|
|
|
|
//
|
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
2017-03-24 20:32:08 +00:00
|
|
|
#include "Debugger.hxx"
|
2017-03-24 02:25:33 +00:00
|
|
|
#endif
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
#include "System.hxx"
|
|
|
|
#include "Thumbulator.hxx"
|
|
|
|
#include "CartCDF.hxx"
|
|
|
|
#include "TIA.hxx"
|
|
|
|
|
|
|
|
// Location of data within the RAM copy of the CDF Driver.
|
|
|
|
#define DSxPTR 0x06E0
|
2017-04-23 20:48:12 +00:00
|
|
|
#define DSxINC 0x0768
|
|
|
|
#define WAVEFORM 0x07F0
|
2017-03-24 02:25:33 +00:00
|
|
|
#define DSRAM 0x0800
|
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
#define WRITESTREAM 0x20
|
|
|
|
#define JUMPSTREAM 0x21
|
|
|
|
#define AMPLITUDE 0x22
|
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
#define FAST_FETCH_ON ((myMode & 0x0F) == 0)
|
2017-04-23 20:48:12 +00:00
|
|
|
#define DIGITAL_AUDIO_ON ((myMode & 0xF0) == 0)
|
2017-03-24 02:25:33 +00:00
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
CartridgeCDF::CartridgeCDF(const uInt8* image, uInt32 size,
|
|
|
|
const Settings& settings)
|
2017-03-24 20:32:08 +00:00
|
|
|
: Cartridge(settings),
|
2017-04-23 20:48:12 +00:00
|
|
|
myAudioCycles(0),
|
2017-03-24 20:32:08 +00:00
|
|
|
myARMCycles(0),
|
|
|
|
myFractionalClocks(0.0)
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
|
|
|
// Copy the ROM image into my buffer
|
|
|
|
memcpy(myImage, image, std::min(32768u, size));
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// even though the ROM is 32K, only 28K is accessible to the 6507
|
|
|
|
createCodeAccessBase(4096 * 7);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Pointer to the program ROM (28K @ 0 byte offset)
|
|
|
|
// which starts after the 2K CDF Driver and 2K C Code
|
|
|
|
myProgramImage = myImage + 4096;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Pointer to CDF driver in RAM
|
|
|
|
myBusDriverImage = myCDFRAM;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Pointer to the display RAM
|
|
|
|
myDisplayImage = myCDFRAM + DSRAM;
|
|
|
|
#ifdef THUMB_SUPPORT
|
|
|
|
// Create Thumbulator ARM emulator
|
2017-03-24 20:32:08 +00:00
|
|
|
myThumbEmulator = make_ptr<Thumbulator>((uInt16*)myImage, (uInt16*)myCDFRAM,
|
|
|
|
settings.getBool("thumb.trapfatal"), Thumbulator::ConfigureFor::CDF, this);
|
2017-03-24 02:25:33 +00:00
|
|
|
#endif
|
|
|
|
setInitialState();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeCDF::reset()
|
|
|
|
{
|
|
|
|
// Initialize RAM
|
|
|
|
if(mySettings.getBool("ramrandom"))
|
2017-03-24 20:32:08 +00:00
|
|
|
initializeRAM(myCDFRAM+2048, 8192-2048);
|
2017-03-24 02:25:33 +00:00
|
|
|
else
|
|
|
|
memset(myCDFRAM+2048, 0, 8192-2048);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Update cycles to the current system cycles
|
2017-04-23 20:48:12 +00:00
|
|
|
myAudioCycles = mySystem->cycles();
|
2017-03-24 02:25:33 +00:00
|
|
|
myARMCycles = mySystem->cycles();
|
|
|
|
myFractionalClocks = 0.0;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
setInitialState();
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Upon reset we switch to the startup bank
|
|
|
|
bank(myStartBank);
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeCDF::setInitialState()
|
|
|
|
{
|
|
|
|
// Copy initial CDF driver to Harmony RAM
|
|
|
|
memcpy(myBusDriverImage, myImage, 0x0800);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
for (int i=0; i < 3; ++i)
|
|
|
|
myMusicWaveformSize[i] = 27;
|
2017-04-23 20:48:12 +00:00
|
|
|
|
|
|
|
// CDF always starts in bank 6
|
|
|
|
myStartBank = 6;
|
|
|
|
|
|
|
|
// Assuming mode starts out with Fast Fetch off and 3-Voice music,
|
|
|
|
// need to confirm with Chris
|
|
|
|
myMode = 0xFF;
|
|
|
|
|
|
|
|
myFastJumpActive = 0;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeCDF::consoleChanged(ConsoleTiming timing)
|
|
|
|
{
|
|
|
|
#ifdef THUMB_SUPPORT
|
|
|
|
myThumbEmulator->setConsoleTiming(timing);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeCDF::systemCyclesReset()
|
|
|
|
{
|
|
|
|
// Adjust the cycle counter so that it reflects the new value
|
2017-04-23 20:48:12 +00:00
|
|
|
myAudioCycles -= mySystem->cycles();
|
2017-03-24 02:25:33 +00:00
|
|
|
myARMCycles -= mySystem->cycles();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeCDF::install(System& system)
|
|
|
|
{
|
|
|
|
mySystem = &system;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Map all of the accesses to call peek and poke
|
|
|
|
System::PageAccess access(this, System::PA_READ);
|
|
|
|
for(uInt32 i = 0x1000; i < 0x1040; i += (1 << System::PAGE_SHIFT))
|
|
|
|
mySystem->setPageAccess(i >> System::PAGE_SHIFT, access);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Install pages for the startup bank
|
2017-03-24 20:32:08 +00:00
|
|
|
bank(myStartBank);
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void CartridgeCDF::updateMusicModeDataFetchers()
|
|
|
|
{
|
|
|
|
// Calculate the number of cycles since the last update
|
2017-04-23 20:48:12 +00:00
|
|
|
Int32 cycles = mySystem->cycles() - myAudioCycles;
|
|
|
|
myAudioCycles = mySystem->cycles();
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Calculate the number of CDF OSC clocks since the last update
|
|
|
|
double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
|
|
|
|
Int32 wholeClocks = Int32(clocks);
|
|
|
|
myFractionalClocks = clocks - double(wholeClocks);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
if(wholeClocks <= 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Let's update counters and flags of the music mode data fetchers
|
|
|
|
for(int x = 0; x <= 2; ++x)
|
|
|
|
{
|
2017-04-25 21:27:39 +00:00
|
|
|
myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void CartridgeCDF::callFunction(uInt8 value)
|
|
|
|
{
|
|
|
|
switch (value)
|
|
|
|
{
|
|
|
|
#ifdef THUMB_SUPPORT
|
|
|
|
// Call user written ARM code (will most likely be C compiled for ARM)
|
|
|
|
case 254: // call with IRQ driven audio, no special handling needed at this
|
|
|
|
// time for Stella as ARM code "runs in zero 6507 cycles".
|
2017-03-24 20:32:08 +00:00
|
|
|
case 255: // call without IRQ driven audio
|
2017-03-24 02:25:33 +00:00
|
|
|
try {
|
|
|
|
Int32 cycles = mySystem->cycles() - myARMCycles;
|
|
|
|
myARMCycles = mySystem->cycles();
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
myThumbEmulator->run(cycles);
|
|
|
|
}
|
|
|
|
catch(const runtime_error& e) {
|
|
|
|
if(!mySystem->autodetectMode())
|
|
|
|
{
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
Debugger::debugger().startWithFatalError(e.what());
|
|
|
|
#else
|
|
|
|
cout << e.what() << endl;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt8 CartridgeCDF::peek(uInt16 address)
|
|
|
|
{
|
|
|
|
address &= 0x0FFF;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address];
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// In debugger/bank-locked mode, we ignore all hotspots and in general
|
|
|
|
// anything that can change the internal state of the cart
|
|
|
|
if(bankLocked())
|
|
|
|
return peekvalue;
|
2017-04-23 20:48:12 +00:00
|
|
|
|
|
|
|
// implement JMP FASTJMP which fetches the destination address from stream 33
|
|
|
|
if (myFastJumpActive)
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
uInt32 pointer;
|
|
|
|
uInt8 value;
|
|
|
|
|
|
|
|
myFastJumpActive--;
|
|
|
|
|
|
|
|
pointer = getDatastreamPointer(JUMPSTREAM);
|
|
|
|
value = myDisplayImage[ pointer >> 20 ];
|
|
|
|
pointer += 0x100000; // always increment by 1
|
|
|
|
setDatastreamPointer(JUMPSTREAM, pointer);
|
|
|
|
|
|
|
|
return value;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
// test for JMP FASTJUMP where FASTJUMP = $0000
|
|
|
|
if (FAST_FETCH_ON
|
|
|
|
&& peekvalue == 0x4C
|
|
|
|
&& myProgramImage[(myCurrentBank << 12) + address+1] == 0
|
|
|
|
&& myProgramImage[(myCurrentBank << 12) + address+2] == 0)
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
myFastJumpActive = 2; // return next two peeks from datastream 31
|
|
|
|
return peekvalue;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
// Check if we're in Fast Fetch mode and the prior byte was an A9 (LDA #value)
|
|
|
|
if(FAST_FETCH_ON
|
|
|
|
&& myLDAimmediateOperandAddress == address
|
|
|
|
&& peekvalue <= AMPLITUDE)
|
|
|
|
{
|
|
|
|
if (peekvalue == AMPLITUDE)
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
updateMusicModeDataFetchers();
|
|
|
|
|
|
|
|
if DIGITAL_AUDIO_ON
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
// retrieve packed sample (max size is 2K, or 4K of unpacked data)
|
|
|
|
address = getSample() + (myMusicCounters[0] >> 21);
|
|
|
|
peekvalue = myImage[address];
|
|
|
|
|
|
|
|
//
|
|
|
|
if ((myMusicCounters[0] & (1<<20)) == 0)
|
|
|
|
peekvalue >>= 4;
|
|
|
|
peekvalue &= 0x0f;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
2017-04-23 20:48:12 +00:00
|
|
|
else
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
peekvalue = myDisplayImage[getWaveform(0) + (myMusicCounters[0] >> myMusicWaveformSize[0])]
|
|
|
|
+ myDisplayImage[getWaveform(1) + (myMusicCounters[1] >> myMusicWaveformSize[1])]
|
|
|
|
+ myDisplayImage[getWaveform(2) + (myMusicCounters[2] >> myMusicWaveformSize[2])];
|
|
|
|
}
|
|
|
|
return peekvalue;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
2017-04-23 20:48:12 +00:00
|
|
|
else
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
return readFromDatastream(peekvalue);
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
2017-04-23 20:48:12 +00:00
|
|
|
}
|
|
|
|
myLDAimmediateOperandAddress = 0;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
// Switch banks if necessary
|
|
|
|
switch(address)
|
|
|
|
{
|
|
|
|
case 0xFF5:
|
|
|
|
// Set the current bank to the first 4k bank
|
|
|
|
bank(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF6:
|
|
|
|
// Set the current bank to the second 4k bank
|
|
|
|
bank(1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF7:
|
|
|
|
// Set the current bank to the third 4k bank
|
|
|
|
bank(2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF8:
|
|
|
|
// Set the current bank to the fourth 4k bank
|
|
|
|
bank(3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF9:
|
|
|
|
// Set the current bank to the fifth 4k bank
|
|
|
|
bank(4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FFA:
|
|
|
|
// Set the current bank to the sixth 4k bank
|
|
|
|
bank(5);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FFB:
|
|
|
|
// Set the current bank to the last 4k bank
|
|
|
|
bank(6);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
2017-04-23 20:48:12 +00:00
|
|
|
|
|
|
|
if(FAST_FETCH_ON && peekvalue == 0xA9)
|
|
|
|
myLDAimmediateOperandAddress = address + 1;
|
|
|
|
|
|
|
|
return peekvalue;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeCDF::poke(uInt16 address, uInt8 value)
|
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
uInt32 pointer;
|
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
address &= 0x0FFF;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
// Switch banks if necessary
|
|
|
|
switch(address)
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
2017-04-23 20:48:12 +00:00
|
|
|
case 0xFF0: // DSWRITE
|
|
|
|
pointer = getDatastreamPointer(WRITESTREAM);
|
|
|
|
myDisplayImage[ pointer >> 20 ] = value;
|
|
|
|
pointer += 0x100000; // always increment by 1 when writing
|
|
|
|
setDatastreamPointer(WRITESTREAM, pointer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xFF1: // DSPTR
|
|
|
|
pointer = getDatastreamPointer(WRITESTREAM);
|
|
|
|
pointer <<=8;
|
|
|
|
pointer &= 0xf0000000;
|
|
|
|
pointer |= (value << 20);
|
|
|
|
setDatastreamPointer(WRITESTREAM, pointer);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xFF2: // SETMODE
|
|
|
|
myMode = value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xFF3: // CALLFN
|
|
|
|
callFunction(value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xFF5:
|
|
|
|
// Set the current bank to the first 4k bank
|
|
|
|
bank(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF6:
|
|
|
|
// Set the current bank to the second 4k bank
|
|
|
|
bank(1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF7:
|
|
|
|
// Set the current bank to the third 4k bank
|
|
|
|
bank(2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF8:
|
|
|
|
// Set the current bank to the fourth 4k bank
|
|
|
|
bank(3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FF9:
|
|
|
|
// Set the current bank to the fifth 4k bank
|
|
|
|
bank(4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FFA:
|
|
|
|
// Set the current bank to the sixth 4k bank
|
|
|
|
bank(5);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0FFB:
|
|
|
|
// Set the current bank to the last 4k bank
|
|
|
|
bank(6);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeCDF::bank(uInt16 bank)
|
|
|
|
{
|
|
|
|
if(bankLocked()) return false;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Remember what bank we're in
|
|
|
|
myCurrentBank = bank;
|
|
|
|
uInt16 offset = myCurrentBank << 12;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Setup the page access methods for the current bank
|
|
|
|
System::PageAccess access(this, System::PA_READ);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Map Program ROM image into the system
|
|
|
|
for(uInt32 address = 0x1040; address < 0x2000;
|
|
|
|
address += (1 << System::PAGE_SHIFT))
|
|
|
|
{
|
|
|
|
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
|
|
|
|
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
|
|
|
|
}
|
|
|
|
return myBankChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt16 CartridgeCDF::getBank() const
|
|
|
|
{
|
|
|
|
return myCurrentBank;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt16 CartridgeCDF::bankCount() const
|
|
|
|
{
|
|
|
|
return 7;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeCDF::patch(uInt16 address, uInt8 value)
|
|
|
|
{
|
|
|
|
address &= 0x0FFF;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// For now, we ignore attempts to patch the CDF address space
|
|
|
|
if(address >= 0x0040)
|
|
|
|
{
|
|
|
|
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
|
|
|
|
return myBankChanged = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
const uInt8* CartridgeCDF::getImage(int& size) const
|
|
|
|
{
|
|
|
|
size = 32768;
|
|
|
|
return myImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
|
|
uInt32 CartridgeCDF::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
|
|
|
|
{
|
|
|
|
switch (function)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// _SetNote - set the note/frequency
|
|
|
|
myMusicFrequencies[value1] = value2;
|
|
|
|
break;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// _ResetWave - reset counter,
|
|
|
|
// used to make sure digital samples start from the beginning
|
|
|
|
case 1:
|
|
|
|
myMusicCounters[value1] = 0;
|
|
|
|
break;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// _GetWavePtr - return the counter
|
|
|
|
case 2:
|
|
|
|
return myMusicCounters[value1];
|
|
|
|
break;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// _SetWaveSize - set size of waveform buffer
|
|
|
|
case 3:
|
|
|
|
myMusicWaveformSize[value1] = value2;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeCDF::save(Serializer& out) const
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
out.putString(name());
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Indicates which bank is currently active
|
|
|
|
out.putShort(myCurrentBank);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-30 16:01:27 +00:00
|
|
|
// Indicates current mode
|
|
|
|
out.putByte(myMode);
|
|
|
|
|
|
|
|
// State of FastJump
|
|
|
|
out.putByte(myFastJumpActive);
|
|
|
|
|
|
|
|
// Address of LDA # operand
|
|
|
|
out.putShort(myLDAimmediateOperandAddress);
|
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Harmony RAM
|
|
|
|
out.putByteArray(myCDFRAM, 8192);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-30 16:01:27 +00:00
|
|
|
// Audio info
|
|
|
|
out.putIntArray(myMusicCounters, 3);
|
|
|
|
out.putIntArray(myMusicFrequencies, 3);
|
|
|
|
out.putByteArray(myMusicWaveformSize, 3);
|
|
|
|
|
|
|
|
// Save cycles and clocks
|
2017-04-23 20:48:12 +00:00
|
|
|
out.putInt(myAudioCycles);
|
2017-03-24 02:25:33 +00:00
|
|
|
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
|
|
|
|
out.putInt(myARMCycles);
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
cerr << "ERROR: CartridgeCDF::save" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeCDF::load(Serializer& in)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if(in.getString() != name())
|
|
|
|
return false;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Indicates which bank is currently active
|
|
|
|
myCurrentBank = in.getShort();
|
2017-04-30 16:01:27 +00:00
|
|
|
|
|
|
|
// Indicates current mode
|
|
|
|
myMode = in.getByte();
|
|
|
|
|
|
|
|
// State of FastJump
|
|
|
|
myFastJumpActive = in.getByte();
|
|
|
|
|
|
|
|
// Address of LDA # operand
|
|
|
|
myLDAimmediateOperandAddress = in.getShort();
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Harmony RAM
|
|
|
|
in.getByteArray(myCDFRAM, 8192);
|
2017-04-30 16:01:27 +00:00
|
|
|
|
|
|
|
// Audio info
|
|
|
|
in.getIntArray(myMusicCounters, 3);
|
|
|
|
in.getIntArray(myMusicFrequencies, 3);
|
|
|
|
in.getByteArray(myMusicWaveformSize, 3);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-30 16:01:27 +00:00
|
|
|
// Get cycles and clocks
|
2017-04-23 20:48:12 +00:00
|
|
|
myAudioCycles = (Int32)in.getInt();
|
2017-03-24 02:25:33 +00:00
|
|
|
myFractionalClocks = (double)in.getInt() / 100000000.0;
|
|
|
|
myARMCycles = (Int32)in.getInt();
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
cerr << "ERROR: CartridgeCDF::load" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
// Now, go to the current bank
|
|
|
|
bank(myCurrentBank);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 CartridgeCDF::getDatastreamPointer(uInt8 index) const
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
|
|
|
// index &= 0x0f;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return myCDFRAM[DSxPTR + index*4 + 0] + // low byte
|
2017-03-24 20:32:08 +00:00
|
|
|
(myCDFRAM[DSxPTR + index*4 + 1] << 8) +
|
|
|
|
(myCDFRAM[DSxPTR + index*4 + 2] << 16) +
|
|
|
|
(myCDFRAM[DSxPTR + index*4 + 3] << 24) ; // high byte
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2017-03-24 02:25:33 +00:00
|
|
|
void CartridgeCDF::setDatastreamPointer(uInt8 index, uInt32 value)
|
|
|
|
{
|
|
|
|
// index &= 0x1f;
|
|
|
|
myCDFRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte
|
|
|
|
myCDFRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff;
|
|
|
|
myCDFRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff;
|
|
|
|
myCDFRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 CartridgeCDF::getDatastreamIncrement(uInt8 index) const
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
|
|
|
return myCDFRAM[DSxINC + index*4 + 0] + // low byte
|
2017-03-24 20:32:08 +00:00
|
|
|
(myCDFRAM[DSxINC + index*4 + 1] << 8) +
|
|
|
|
(myCDFRAM[DSxINC + index*4 + 2] << 16) +
|
|
|
|
(myCDFRAM[DSxINC + index*4 + 3] << 24) ; // high byte
|
2017-03-24 02:25:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2017-03-24 02:25:33 +00:00
|
|
|
void CartridgeCDF::setDatastreamIncrement(uInt8 index, uInt32 value)
|
|
|
|
{
|
|
|
|
myCDFRAM[DSxINC + index*4 + 0] = value & 0xff; // low byte
|
|
|
|
myCDFRAM[DSxINC + index*4 + 1] = (value >> 8) & 0xff;
|
|
|
|
myCDFRAM[DSxINC + index*4 + 2] = (value >> 16) & 0xff;
|
|
|
|
myCDFRAM[DSxINC + index*4 + 3] = (value >> 24) & 0xff; // high byte
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 CartridgeCDF::getWaveform(uInt8 index) const
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
|
|
|
uInt32 result;
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
result = myCDFRAM[WAVEFORM + index*4 + 0] + // low byte
|
2017-03-24 20:32:08 +00:00
|
|
|
(myCDFRAM[WAVEFORM + index*4 + 1] << 8) +
|
|
|
|
(myCDFRAM[WAVEFORM + index*4 + 2] << 16) +
|
2017-04-23 20:48:12 +00:00
|
|
|
(myCDFRAM[WAVEFORM + index*4 + 3] << 24); // high byte
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
result -= (0x40000000 + DSRAM);
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
if (result >= 4096)
|
2017-04-23 20:48:12 +00:00
|
|
|
result &= 4095;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-04-23 20:48:12 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 CartridgeCDF::getSample()
|
|
|
|
{
|
|
|
|
uInt32 result;
|
|
|
|
|
|
|
|
result = myCDFRAM[WAVEFORM + 0] + // low byte
|
|
|
|
(myCDFRAM[WAVEFORM + 1] << 8) +
|
|
|
|
(myCDFRAM[WAVEFORM + 2] << 16) +
|
|
|
|
(myCDFRAM[WAVEFORM + 3] << 24); // high byte
|
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt32 CartridgeCDF::getWaveformSize(uInt8 index) const
|
2017-03-24 02:25:33 +00:00
|
|
|
{
|
|
|
|
return myMusicWaveformSize[index];
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:32:08 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2017-03-24 02:25:33 +00:00
|
|
|
uInt8 CartridgeCDF::readFromDatastream(uInt8 index)
|
|
|
|
{
|
|
|
|
// Pointers are stored as:
|
|
|
|
// PPPFF---
|
|
|
|
//
|
|
|
|
// Increments are stored as
|
|
|
|
// ----IIFF
|
|
|
|
//
|
|
|
|
// P = Pointer
|
|
|
|
// I = Increment
|
|
|
|
// F = Fractional
|
2017-03-24 20:32:08 +00:00
|
|
|
|
2017-03-24 02:25:33 +00:00
|
|
|
uInt32 pointer = getDatastreamPointer(index);
|
|
|
|
uInt16 increment = getDatastreamIncrement(index);
|
|
|
|
uInt8 value = myDisplayImage[ pointer >> 20 ];
|
|
|
|
pointer += (increment << 12);
|
|
|
|
setDatastreamPointer(index, pointer);
|
|
|
|
return value;
|
|
|
|
}
|