2010-03-18 17:34:53 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
2010-04-18 15:01:38 +00:00
|
|
|
// SSSS tt lll lll
|
|
|
|
// SS SS tt ll ll
|
|
|
|
// SS tttttt eeee ll ll aaaa
|
2010-03-18 17:34:53 +00:00
|
|
|
// 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
|
|
|
|
//
|
2010-04-10 21:37:23 +00:00
|
|
|
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
|
|
|
|
// and the Stella Team
|
2010-03-18 17:34:53 +00:00
|
|
|
//
|
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//
|
|
|
|
// $Id$
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include "System.hxx"
|
|
|
|
#include "CartDPCPlus.hxx"
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-08-19 21:48:28 +00:00
|
|
|
CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
|
|
|
|
const Settings& settings)
|
|
|
|
: Cartridge(settings),
|
|
|
|
myFastFetch(false),
|
2010-04-03 12:45:20 +00:00
|
|
|
myLDAimmediate(false),
|
2010-04-25 23:33:49 +00:00
|
|
|
myParameter(0),
|
2010-04-02 18:56:50 +00:00
|
|
|
mySystemCycles(0),
|
|
|
|
myFractionalClocks(0.0)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-05-02 22:52:59 +00:00
|
|
|
// Store image, making sure it's at least 29KB
|
|
|
|
uInt32 minsize = 4096 * 6 + 4096 + 1024 + 255;
|
|
|
|
mySize = BSPF_max(minsize, size);
|
|
|
|
myImage = new uInt8[mySize];
|
|
|
|
memcpy(myImage, image, size);
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-05-02 22:52:59 +00:00
|
|
|
// Pointer to the program ROM (24K @ 0 byte offset)
|
|
|
|
myProgramImage = myImage;
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-05-02 22:52:59 +00:00
|
|
|
// Pointer to the display ROM (4K @ 24K offset)
|
|
|
|
myDisplayImage = myProgramImage + 4096 * 6;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-05-02 22:52:59 +00:00
|
|
|
// Pointer to the Frequency ROM (1K @ 28K offset)
|
|
|
|
myFrequencyImage = myDisplayImage + 4096;
|
|
|
|
|
|
|
|
// If the image is larger than 29K, we assume any excess at the
|
|
|
|
// beginning is ARM code, and skip over it
|
|
|
|
if(size > 29 * 1024)
|
|
|
|
{
|
|
|
|
int offset = size - 29 * 1024;
|
|
|
|
myProgramImage += offset;
|
|
|
|
myDisplayImage += offset;
|
|
|
|
myFrequencyImage += offset;
|
|
|
|
}
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// Initialize the DPC data fetcher registers
|
|
|
|
for(uInt16 i = 0; i < 8; ++i)
|
2010-05-12 00:01:06 +00:00
|
|
|
myTops[i] = myBottoms[i] = myCounters[i] = myFractionalIncrements[i] = 0;
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-18 15:01:38 +00:00
|
|
|
// Set waveforms to first waveform entry
|
|
|
|
myMusicWaveforms[0] = myMusicWaveforms[1] = myMusicWaveforms[2] = 0;
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// Initialize the DPC's random number generator register (must be non-zero)
|
2010-03-28 03:13:10 +00:00
|
|
|
myRandomNumber = 0x2B435044; // "DPC+"
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-25 23:33:49 +00:00
|
|
|
// DPC+ always starts in bank 5
|
2010-03-28 03:13:10 +00:00
|
|
|
myStartBank = 5;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
CartridgeDPCPlus::~CartridgeDPCPlus()
|
|
|
|
{
|
2010-05-02 22:52:59 +00:00
|
|
|
delete[] myImage;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeDPCPlus::reset()
|
|
|
|
{
|
|
|
|
// Update cycles to the current system cycles
|
|
|
|
mySystemCycles = mySystem->cycles();
|
|
|
|
myFractionalClocks = 0.0;
|
|
|
|
|
|
|
|
// Upon reset we switch to the startup bank
|
|
|
|
bank(myStartBank);
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeDPCPlus::systemCyclesReset()
|
|
|
|
{
|
|
|
|
// Get the current system cycle
|
|
|
|
uInt32 cycles = mySystem->cycles();
|
|
|
|
|
|
|
|
// Adjust the cycle counter so that it reflects the new value
|
|
|
|
mySystemCycles -= cycles;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void CartridgeDPCPlus::install(System& system)
|
|
|
|
{
|
|
|
|
mySystem = &system;
|
|
|
|
uInt16 shift = mySystem->pageShift();
|
|
|
|
uInt16 mask = mySystem->pageMask();
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
// Make sure the system we're being installed in has a page size that'll work
|
|
|
|
assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0));
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-10-03 00:23:13 +00:00
|
|
|
System::PageAccess access(0, 0, myCodeAccessBase, this, System::PA_READ);
|
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
// Map all of the accesses to call peek and poke
|
|
|
|
for(uInt32 i = 0x1000; i < 0x2000; i += (1 << shift))
|
2010-03-18 17:34:53 +00:00
|
|
|
mySystem->setPageAccess(i >> shift, access);
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-28 03:13:10 +00:00
|
|
|
// Install pages for the startup bank
|
|
|
|
bank(myStartBank);
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void CartridgeDPCPlus::clockRandomNumberGenerator()
|
|
|
|
{
|
2010-03-28 03:13:10 +00:00
|
|
|
// Update random number generator (32-bit LFSR)
|
2010-04-02 19:32:06 +00:00
|
|
|
myRandomNumber = ((myRandomNumber & 1) ? 0xa260012b: 0x00) ^ ((myRandomNumber >> 1) & 0x7FFFFFFF);
|
2010-03-28 03:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
|
|
|
|
{
|
|
|
|
// Update random number generator (32-bit LFSR, reversed)
|
2010-04-02 18:56:50 +00:00
|
|
|
myRandomNumber = ((myRandomNumber & (1<<31)) ? 0x44c00257: 0x00) ^ (myRandomNumber << 1);
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void CartridgeDPCPlus::updateMusicModeDataFetchers()
|
|
|
|
{
|
|
|
|
// Calculate the number of cycles since the last update
|
|
|
|
Int32 cycles = mySystem->cycles() - mySystemCycles;
|
|
|
|
mySystemCycles = mySystem->cycles();
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
// Calculate the number of DPC OSC clocks since the last update
|
|
|
|
double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
|
|
|
|
Int32 wholeClocks = (Int32)clocks;
|
|
|
|
myFractionalClocks = clocks - (double)wholeClocks;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
if(wholeClocks <= 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
// Let's update counters and flags of the music mode data fetchers
|
2010-04-18 15:01:38 +00:00
|
|
|
for(int x = 0; x <= 2; ++x)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-18 15:01:38 +00:00
|
|
|
myMusicCounters[x] += myMusicFrequencies[x];
|
2010-04-04 13:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-04-25 23:33:49 +00:00
|
|
|
inline void CartridgeDPCPlus::callFunction(uInt8 value)
|
2010-04-04 13:15:35 +00:00
|
|
|
{
|
2010-04-25 23:33:49 +00:00
|
|
|
// myParameter
|
|
|
|
switch (value)
|
2010-04-04 13:15:35 +00:00
|
|
|
{
|
2010-04-25 23:33:49 +00:00
|
|
|
//case 0: template for future special functions
|
|
|
|
// CallSpecialFunctionO(myParameter);
|
|
|
|
// break;
|
|
|
|
case 255: // Call user writen ARM code (most likely be C compiled for ARM).
|
|
|
|
// ARM code not supported by Stella at this time.
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
// reserved
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt8 CartridgeDPCPlus::peek(uInt16 address)
|
|
|
|
{
|
|
|
|
address &= 0x0FFF;
|
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address];
|
2010-05-12 00:01:06 +00:00
|
|
|
uInt8 flag;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-02 18:56:50 +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;
|
|
|
|
|
2010-04-03 12:45:20 +00:00
|
|
|
// Check if we're in Fast Fetch mode and the prior byte was an A9 (LDA #value)
|
2010-04-02 18:56:50 +00:00
|
|
|
if(myFastFetch && myLDAimmediate)
|
|
|
|
{
|
|
|
|
if(peekvalue < 0x0028)
|
2010-04-18 15:01:38 +00:00
|
|
|
// if #value is a read-register then we want to use that as the address
|
2010-04-02 18:56:50 +00:00
|
|
|
address = peekvalue;
|
|
|
|
}
|
|
|
|
myLDAimmediate = false;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
if(address < 0x0028)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
|
|
|
uInt8 result = 0;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
// Get the index of the data fetcher that's being accessed
|
|
|
|
uInt32 index = address & 0x07;
|
|
|
|
uInt32 function = (address >> 3) & 0x07;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-05-12 00:01:06 +00:00
|
|
|
// Update flag for selected data fetcher
|
|
|
|
flag = (((myTops[index]-(myCounters[index] & 0x00ff)) & 0xFF) > ((myTops[index]-myBottoms[index]) & 0xFF)) ? 0xFF : 0;
|
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
switch(function)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
{
|
2010-03-28 03:13:10 +00:00
|
|
|
switch(index)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x00: // RANDOM0NEXT - advance and return byte 0 of random
|
2010-03-28 03:13:10 +00:00
|
|
|
clockRandomNumberGenerator();
|
|
|
|
result = myRandomNumber & 0xFF;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x01: // RANDOM0PRIOR - return to prior and return byte 0 of random
|
2010-03-28 03:13:10 +00:00
|
|
|
priorClockRandomNumberGenerator();
|
|
|
|
result = myRandomNumber & 0xFF;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x02: // RANDOM1
|
2010-03-28 03:13:10 +00:00
|
|
|
result = (myRandomNumber>>8) & 0xFF;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x03: // RANDOM2
|
2010-03-28 03:13:10 +00:00
|
|
|
result = (myRandomNumber>>16) & 0xFF;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x04: // RANDOM3
|
2010-03-28 03:13:10 +00:00
|
|
|
result = (myRandomNumber>>24) & 0xFF;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x05: // AMPLITUDE
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-03-28 03:13:10 +00:00
|
|
|
// Update the music data fetchers (counter & flag)
|
|
|
|
updateMusicModeDataFetchers();
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-05-02 22:52:59 +00:00
|
|
|
// using myProgramImage[] instead of myDisplayImage[] because waveforms
|
2010-04-18 15:01:38 +00:00
|
|
|
// could also be in the 1K Frequency table.
|
2010-05-02 22:52:59 +00:00
|
|
|
uInt32 i = myProgramImage[6*4096 + (myMusicWaveforms[0] << 5) + (myMusicCounters[0] >> 27)] +
|
|
|
|
myProgramImage[6*4096 + (myMusicWaveforms[1] << 5) + (myMusicCounters[1] >> 27)] +
|
|
|
|
myProgramImage[6*4096 + (myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)];
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
result = (uInt8)i;
|
2010-04-18 15:01:38 +00:00
|
|
|
break;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
2010-04-02 18:56:50 +00:00
|
|
|
|
|
|
|
case 0x06: // reserved
|
|
|
|
case 0x07: // reserved
|
|
|
|
break;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxDATA - display data read
|
2010-03-18 17:34:53 +00:00
|
|
|
case 0x01:
|
|
|
|
{
|
2010-04-02 18:56:50 +00:00
|
|
|
result = myDisplayImage[myCounters[index]];
|
2010-04-18 15:01:38 +00:00
|
|
|
myCounters[index] = (myCounters[index] + 0x1) & 0x0fff;
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxDATAW - display data read AND'd w/flag ("windowed")
|
2010-03-18 17:34:53 +00:00
|
|
|
case 0x02:
|
|
|
|
{
|
2010-05-12 00:01:06 +00:00
|
|
|
result = myDisplayImage[myCounters[index]] & flag;
|
2010-04-18 15:01:38 +00:00
|
|
|
myCounters[index] = (myCounters[index] + 0x1) & 0x0fff;
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
}
|
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxFRACDATA - display data read w/fractional increment
|
2010-03-28 03:13:10 +00:00
|
|
|
case 0x03:
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-02 18:56:50 +00:00
|
|
|
result = myDisplayImage[myFractionalCounters[index] >> 8];
|
2010-04-18 15:01:38 +00:00
|
|
|
myFractionalCounters[index] = (myFractionalCounters[index] + myFractionalIncrements[index]) & 0x0fffff;
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
}
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x04:
|
|
|
|
{
|
|
|
|
switch (index)
|
|
|
|
{
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x00: // DF0FLAG
|
|
|
|
case 0x01: // DF1FLAG
|
|
|
|
case 0x02: // DF2FLAG
|
|
|
|
case 0x03: // DF3FLAG
|
2010-04-02 18:56:50 +00:00
|
|
|
{
|
2010-05-12 00:01:06 +00:00
|
|
|
result = flag;
|
2010-04-02 18:56:50 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x04: // reserved
|
|
|
|
case 0x05: // reserved
|
|
|
|
case 0x06: // reserved
|
|
|
|
case 0x07: // reserved
|
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
break;
|
2010-04-02 18:56:50 +00:00
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Switch banks if necessary
|
|
|
|
switch(address)
|
|
|
|
{
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF6:
|
|
|
|
// Set the current bank to the first 4k bank
|
|
|
|
bank(0);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF7:
|
|
|
|
// Set the current bank to the second 4k bank
|
|
|
|
bank(1);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF8:
|
|
|
|
// Set the current bank to the third 4k bank
|
|
|
|
bank(2);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF9:
|
|
|
|
// Set the current bank to the fourth 4k bank
|
|
|
|
bank(3);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FFA:
|
|
|
|
// Set the current bank to the fifth 4k bank
|
|
|
|
bank(4);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FFB:
|
|
|
|
// Set the current bank to the last 4k bank
|
|
|
|
bank(5);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
default:
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
if(myFastFetch)
|
|
|
|
myLDAimmediate = (peekvalue == 0xA9);
|
|
|
|
|
|
|
|
return peekvalue;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-03-28 03:13:10 +00:00
|
|
|
bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
|
|
|
address &= 0x0FFF;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
|
|
|
if((address >= 0x0028) && (address < 0x0080))
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
|
|
|
// Get the index of the data fetcher that's being accessed
|
2010-04-18 15:01:38 +00:00
|
|
|
uInt32 index = address & 0x07;
|
2010-04-02 18:56:50 +00:00
|
|
|
uInt32 function = ((address - 0x28) >> 3) & 0x0f;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
switch(function)
|
|
|
|
{
|
2010-04-04 13:15:35 +00:00
|
|
|
//DFxFRACLOW - fractional data pointer low byte
|
2010-03-18 17:34:53 +00:00
|
|
|
case 0x00:
|
2010-04-02 18:56:50 +00:00
|
|
|
myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F0000) | ((uInt16)value << 8);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxFRACHI - fractional data pointer high byte
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x01:
|
|
|
|
myFractionalCounters[index] = (((uInt16)value & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
//DFxFRACINC - Fractional Increment amount
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x02:
|
2010-03-28 03:13:10 +00:00
|
|
|
myFractionalIncrements[index] = value;
|
2010-04-02 18:56:50 +00:00
|
|
|
myFractionalCounters[index] = myFractionalCounters[index] & 0x0FFF00;
|
2010-03-28 03:13:10 +00:00
|
|
|
break;
|
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxTOP - set top of window (for reads of DFxDATAW)
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x03:
|
2010-03-18 17:34:53 +00:00
|
|
|
myTops[index] = value;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxBOT - set bottom of window (for reads of DFxDATAW)
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x04:
|
2010-03-18 17:34:53 +00:00
|
|
|
myBottoms[index] = value;
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxLOW - data pointer low byte
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x05:
|
|
|
|
myCounters[index] = (myCounters[index] & 0x0F00) | value ;
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
// Control registers
|
|
|
|
case 0x06:
|
|
|
|
switch (index)
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x00: // FASTFETCH - turns on LDA #<DFxDATA mode of value is 0
|
2010-04-02 18:56:50 +00:00
|
|
|
myFastFetch = (value == 0);
|
|
|
|
break;
|
2010-04-04 13:15:35 +00:00
|
|
|
|
2010-04-25 23:33:49 +00:00
|
|
|
case 0x01: // PARAMETER - set parameter used by CALLFUNCTION (not all functions use the parameter)
|
|
|
|
myParameter = value;
|
2010-04-03 12:45:20 +00:00
|
|
|
break;
|
2010-04-04 13:15:35 +00:00
|
|
|
|
2010-04-25 23:33:49 +00:00
|
|
|
case 0x02: // CALLFUNCTION
|
|
|
|
callFunction(value);
|
2010-04-03 12:45:20 +00:00
|
|
|
break;
|
2010-04-04 13:15:35 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x03: // reserved
|
|
|
|
case 0x04: // reserved
|
|
|
|
break;
|
2010-04-04 13:15:35 +00:00
|
|
|
|
2010-04-18 15:01:38 +00:00
|
|
|
case 0x05: // WAVEFORM0
|
|
|
|
case 0x06: // WAVEFORM1
|
|
|
|
case 0x07: // WAVEFORM2
|
2010-04-25 23:33:49 +00:00
|
|
|
myMusicWaveforms[index - 5] = value & 0x7f;
|
2010-04-02 18:56:50 +00:00
|
|
|
break;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxPUSH - Push value into data bank
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x07:
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
2010-04-02 18:56:50 +00:00
|
|
|
myCounters[index] = (myCounters[index] - 0x1) & 0x0fff;
|
|
|
|
myDisplayImage[myCounters[index]] = value;
|
2010-03-28 03:13:10 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxHI - data pointer high byte
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x08:
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-02 18:56:50 +00:00
|
|
|
myCounters[index] = (((uInt16)value & 0x0F) << 8) | (myCounters[index] & 0x00ff);
|
2010-03-28 03:13:10 +00:00
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
}
|
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x09:
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
switch (index)
|
|
|
|
{
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x00: // RRESET - Random Number Generator Reset
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
myRandomNumber = 0x2B435044; // "DPC+"
|
|
|
|
break;
|
|
|
|
}
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x01: // RWRITE0 - update byte 0 of random number
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
myRandomNumber = (myRandomNumber & 0xFFFFFF00) | value;
|
|
|
|
break;
|
|
|
|
}
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x02: // RWRITE1 - update byte 1 of random number
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
myRandomNumber = (myRandomNumber & 0xFFFF00FF) | (value<<8);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x03: // RWRITE2 - update byte 2 of random number
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
myRandomNumber = (myRandomNumber & 0xFF00FFFF) | (value<<16);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x04: // RWRITE3 - update byte 3 of random number
|
2010-03-28 03:13:10 +00:00
|
|
|
{
|
|
|
|
myRandomNumber = (myRandomNumber & 0x00FFFFFF) | (value<<24);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-04 13:15:35 +00:00
|
|
|
case 0x05: // NOTE0
|
|
|
|
case 0x06: // NOTE1
|
|
|
|
case 0x07: // NOTE2
|
2010-04-18 15:01:38 +00:00
|
|
|
{
|
2010-04-03 12:45:20 +00:00
|
|
|
myMusicFrequencies[index-5] = myFrequencyImage[(value<<2)] +
|
2010-04-02 18:56:50 +00:00
|
|
|
(myFrequencyImage[(value<<2)+1]<<8) +
|
|
|
|
(myFrequencyImage[(value<<2)+2]<<16) +
|
|
|
|
(myFrequencyImage[(value<<2)+3]<<24);
|
2010-03-28 03:13:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// DFxWRITE - write into data bank
|
2010-04-02 18:56:50 +00:00
|
|
|
case 0x0a:
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-04-02 18:56:50 +00:00
|
|
|
myDisplayImage[myCounters[index]] = value;
|
|
|
|
myCounters[index] = (myCounters[index] + 0x1) & 0x0fff;
|
2010-03-18 17:34:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-28 03:13:10 +00:00
|
|
|
default:
|
2010-04-02 18:56:50 +00:00
|
|
|
{
|
2010-03-28 03:13:10 +00:00
|
|
|
break;
|
2010-04-02 18:56:50 +00:00
|
|
|
}
|
2010-04-18 15:01:38 +00:00
|
|
|
}
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Switch banks if necessary
|
|
|
|
switch(address)
|
|
|
|
{
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF6:
|
|
|
|
// Set the current bank to the first 4k bank
|
|
|
|
bank(0);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF7:
|
|
|
|
// Set the current bank to the second 4k bank
|
|
|
|
bank(1);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF8:
|
|
|
|
// Set the current bank to the third 4k bank
|
|
|
|
bank(2);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FF9:
|
|
|
|
// Set the current bank to the fourth 4k bank
|
|
|
|
bank(3);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FFA:
|
|
|
|
// Set the current bank to the fifth 4k bank
|
|
|
|
bank(4);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-22 17:24:08 +00:00
|
|
|
case 0x0FFB:
|
|
|
|
// Set the current bank to the last 4k bank
|
|
|
|
bank(5);
|
|
|
|
break;
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-03-28 03:13:10 +00:00
|
|
|
return false;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-08-16 16:41:24 +00:00
|
|
|
bool CartridgeDPCPlus::bank(uInt16 bank)
|
2010-04-18 15:01:38 +00:00
|
|
|
{
|
2010-08-16 16:41:24 +00:00
|
|
|
if(bankLocked()) return false;
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// Remember what bank we're in
|
|
|
|
myCurrentBank = bank;
|
|
|
|
|
2010-08-16 16:41:24 +00:00
|
|
|
return myBankChanged = true;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-04-02 22:09:31 +00:00
|
|
|
uInt16 CartridgeDPCPlus::bank() const
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
|
|
|
return myCurrentBank;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-04-02 22:09:31 +00:00
|
|
|
uInt16 CartridgeDPCPlus::bankCount() const
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value)
|
|
|
|
{
|
2010-03-30 20:36:13 +00:00
|
|
|
address &= 0x0FFF;
|
|
|
|
|
|
|
|
// For now, we ignore attempts to patch the DPC address space
|
|
|
|
if(address >= 0x0080)
|
|
|
|
{
|
|
|
|
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
|
|
|
|
return myBankChanged = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
2010-04-18 15:01:38 +00:00
|
|
|
}
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2010-04-02 22:09:31 +00:00
|
|
|
const uInt8* CartridgeDPCPlus::getImage(int& size) const
|
2010-03-18 17:34:53 +00:00
|
|
|
{
|
2010-05-02 22:52:59 +00:00
|
|
|
size = mySize;
|
|
|
|
return myImage;
|
2010-03-18 17:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeDPCPlus::save(Serializer& out) const
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
uInt32 i;
|
|
|
|
|
2010-05-10 00:50:26 +00:00
|
|
|
out.putString(name());
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// Indicates which bank is currently active
|
|
|
|
out.putInt(myCurrentBank);
|
|
|
|
|
|
|
|
// The top registers for the data fetchers
|
|
|
|
out.putInt(8);
|
|
|
|
for(i = 0; i < 8; ++i)
|
|
|
|
out.putByte((char)myTops[i]);
|
|
|
|
|
|
|
|
// The bottom registers for the data fetchers
|
|
|
|
out.putInt(8);
|
|
|
|
for(i = 0; i < 8; ++i)
|
|
|
|
out.putByte((char)myBottoms[i]);
|
|
|
|
|
|
|
|
// The counter registers for the data fetchers
|
|
|
|
out.putInt(8);
|
|
|
|
for(i = 0; i < 8; ++i)
|
|
|
|
out.putInt(myCounters[i]);
|
2010-04-18 15:01:38 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
// The counter registers for the fractional data fetchers
|
|
|
|
out.putInt(8);
|
|
|
|
for(i = 0; i < 8; ++i)
|
|
|
|
out.putInt(myFractionalCounters[i]);
|
|
|
|
|
2010-03-28 03:13:10 +00:00
|
|
|
// The fractional registers for the data fetchers
|
|
|
|
out.putInt(8);
|
|
|
|
for(i = 0; i < 8; ++i)
|
|
|
|
out.putByte((char)myFractionalIncrements[i]);
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
// The Fast Fetcher Enabled flag
|
|
|
|
out.putBool(myFastFetch);
|
|
|
|
out.putBool(myLDAimmediate);
|
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// Control Byte to update
|
2010-04-25 23:33:49 +00:00
|
|
|
out.putByte((char) myParameter);
|
2010-04-03 12:45:20 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// The music counters
|
2010-03-28 03:13:10 +00:00
|
|
|
out.putInt(3);
|
|
|
|
for(i = 0; i < 3; ++i)
|
2010-04-18 15:01:38 +00:00
|
|
|
out.putInt(myMusicCounters[i]);
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// The music frequencies
|
2010-03-28 03:13:10 +00:00
|
|
|
out.putInt(3);
|
|
|
|
for(i = 0; i < 3; ++i)
|
2010-04-03 12:45:20 +00:00
|
|
|
out.putInt(myMusicFrequencies[i]);
|
|
|
|
|
|
|
|
// The music waveforms
|
|
|
|
out.putInt(3);
|
|
|
|
for(i = 0; i < 3; ++i)
|
|
|
|
out.putInt(myMusicWaveforms[i]);
|
|
|
|
|
2010-03-18 17:34:53 +00:00
|
|
|
// The random number generator register
|
2010-04-02 19:32:06 +00:00
|
|
|
out.putInt(myRandomNumber);
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
out.putInt(mySystemCycles);
|
|
|
|
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
|
|
|
|
}
|
|
|
|
catch(const char* msg)
|
|
|
|
{
|
|
|
|
cerr << "ERROR: CartridgeDPCPlus::save" << endl << " " << msg << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool CartridgeDPCPlus::load(Serializer& in)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2010-05-10 00:50:26 +00:00
|
|
|
if(in.getString() != name())
|
2010-03-18 17:34:53 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
uInt32 i, limit;
|
|
|
|
|
|
|
|
// Indicates which bank is currently active
|
|
|
|
myCurrentBank = (uInt16) in.getInt();
|
|
|
|
|
|
|
|
// The top registers for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
|
|
|
myTops[i] = (uInt8) in.getByte();
|
|
|
|
|
|
|
|
// The bottom registers for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
|
|
|
myBottoms[i] = (uInt8) in.getByte();
|
|
|
|
|
|
|
|
// The counter registers for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
2010-04-02 18:56:50 +00:00
|
|
|
myCounters[i] = (uInt16) in.getInt();
|
|
|
|
|
2010-04-02 19:32:06 +00:00
|
|
|
// The counter registers for the fractional data fetchers
|
2010-04-02 18:56:50 +00:00
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
|
|
|
myFractionalCounters[i] = (uInt32) in.getInt();
|
2010-03-28 03:13:10 +00:00
|
|
|
|
|
|
|
// The fractional registers for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
|
|
|
myFractionalIncrements[i] = (uInt8) in.getByte();
|
2010-03-18 17:34:53 +00:00
|
|
|
|
2010-04-02 18:56:50 +00:00
|
|
|
// The Fast Fetcher Enabled flag
|
|
|
|
myFastFetch = in.getBool();
|
|
|
|
myLDAimmediate = in.getBool();
|
|
|
|
|
2010-04-04 13:15:35 +00:00
|
|
|
// Control Byte to update
|
2010-04-25 23:33:49 +00:00
|
|
|
myParameter = (uInt8) in.getByte();
|
2010-04-03 12:45:20 +00:00
|
|
|
|
2010-03-28 03:13:10 +00:00
|
|
|
// The music mode counters for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
2010-04-18 15:01:38 +00:00
|
|
|
myMusicCounters[i] = (uInt32) in.getInt();
|
2010-04-03 12:45:20 +00:00
|
|
|
|
2010-03-28 03:13:10 +00:00
|
|
|
// The music mode frequency addends for the data fetchers
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
2010-04-18 15:01:38 +00:00
|
|
|
myMusicFrequencies[i] = (uInt32) in.getInt();
|
2010-04-03 12:45:20 +00:00
|
|
|
|
|
|
|
// The music waveforms
|
|
|
|
limit = (uInt32) in.getInt();
|
|
|
|
for(i = 0; i < limit; ++i)
|
2010-04-18 15:01:38 +00:00
|
|
|
myMusicWaveforms[i] = (uInt16) in.getInt();
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// The random number generator register
|
2010-03-28 03:13:10 +00:00
|
|
|
myRandomNumber = (uInt32) in.getInt();
|
2010-03-18 17:34:53 +00:00
|
|
|
|
|
|
|
// Get system cycles and fractional clocks
|
|
|
|
mySystemCycles = in.getInt();
|
|
|
|
myFractionalClocks = (double)in.getInt() / 100000000.0;
|
|
|
|
}
|
|
|
|
catch(const char* msg)
|
|
|
|
{
|
|
|
|
cerr << "ERROR: CartridgeDPCPlus::load" << endl << " " << msg << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now, go to the current bank
|
|
|
|
bank(myCurrentBank);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|