Various updates to the 'CTY' scheme, all by SpiceWare:

- music support
- autodetection support
- creation of 60K ROMs

For the latter, I still need to see how we will distribute those ROMs, and get them added to the database.
This commit is contained in:
Stephen Anthony 2018-09-16 22:49:33 -02:30
parent 8f03a48f11
commit 7982f7cb9b
6 changed files with 267 additions and 1877 deletions

View File

@ -104,6 +104,10 @@
* Added recently released 'Arkyology' prototype ROM to the database. * Added recently released 'Arkyology' prototype ROM to the database.
* Added premliminary support for 'CTY' bankswitching scheme and recently
released 'Chetiry' ROMs. Special thanks to SpiceWare for adding music
support to this scheme.
* For UNIX systems: in the ROM launcher, when using symlinks use the * For UNIX systems: in the ROM launcher, when using symlinks use the
symlink pathname instead of the underlying filesystem pathname. symlink pathname instead of the underlying filesystem pathname.

View File

@ -3587,7 +3587,7 @@ Ms Pac-Man (Stella extended codes):
<tr><td>BUS </td><td>Experimental</td><td>.BUS </td></tr> <tr><td>BUS </td><td>Experimental</td><td>.BUS </td></tr>
<tr><td>CDF </td><td>Chris, Darrell, Fred</td><td>.CDF </td></tr> <tr><td>CDF </td><td>Chris, Darrell, Fred</td><td>.CDF </td></tr>
<tr><td>CM &#185;</td><td>Spectravideo CompuMate </td><td>.CM </td></tr> <tr><td>CM &#185;</td><td>Spectravideo CompuMate </td><td>.CM </td></tr>
<tr><td>CTY &#185;&#178;</td><td>CDW - Chetiry </td><td>.CTY </td></tr> <tr><td>CTY &#178;</td><td>CDW - Chetiry </td><td>.CTY </td></tr>
<tr><td>CV </td><td>Commavid extra ram </td><td>.CV </td></tr> <tr><td>CV </td><td>Commavid extra ram </td><td>.CV </td></tr>
<tr><td>CV+ </td><td>Extended Commavid extra ram</td><td>.CVP </td></tr> <tr><td>CV+ </td><td>Extended Commavid extra ram</td><td>.CVP </td></tr>
<tr><td>DASH </td><td>Boulder Dash 2 </td><td>.DAS </td></tr> <tr><td>DASH </td><td>Boulder Dash 2 </td><td>.DAS </td></tr>

View File

@ -18,7 +18,6 @@
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Serializer.hxx" #include "Serializer.hxx"
#include "System.hxx" #include "System.hxx"
#include "CartCTYTunes.hxx"
#include "CartCTY.hxx" #include "CartCTY.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -27,7 +26,7 @@ CartridgeCTY::CartridgeCTY(const BytePtr& image, uInt32 size,
: Cartridge(osystem.settings()), : Cartridge(osystem.settings()),
myOSystem(osystem), myOSystem(osystem),
myOperationType(0), myOperationType(0),
myCounter(0), myTunePosition(0),
myLDAimmediate(false), myLDAimmediate(false),
myRandomNumber(0x2B435044), myRandomNumber(0x2B435044),
myRamAccessTimeout(0), myRamAccessTimeout(0),
@ -39,16 +38,24 @@ CartridgeCTY::CartridgeCTY(const BytePtr& image, uInt32 size,
memcpy(myImage, image.get(), std::min(32768u, size)); memcpy(myImage, image.get(), std::min(32768u, size));
createCodeAccessBase(32768); createCodeAccessBase(32768);
// Default to no tune data in case user is utilizing an old ROM
memset(myTuneData, 0, 28*1024);
// Extract tune data if it exists
if (size > 32768u)
memcpy(myTuneData, image.get() + 32768u, size - 32768u);
// Point to the first tune // Point to the first tune
myFrequencyImage = CartCTYTunes; myFrequencyImage = myTuneData;
for(uInt8 i = 0; i < 3; i++)
myMusicCounters[i] = myMusicFrequencies[i] = 0;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::reset() void CartridgeCTY::reset()
{ {
initializeRAM(myRAM, 64); initializeRAM(myRAM, 64);
// Remember startup bank (not bank 0, since that's ARM code)
initializeStartBank(1); initializeStartBank(1);
myRAM[0] = myRAM[1] = myRAM[2] = myRAM[3] = 0xFF; myRAM[0] = myRAM[1] = myRAM[2] = myRAM[3] = 0xFF;
@ -94,15 +101,23 @@ uInt8 CartridgeCTY::peek(uInt16 address)
// Update the music data fetchers (counter & flag) // Update the music data fetchers (counter & flag)
updateMusicModeDataFetchers(); updateMusicModeDataFetchers();
#if 0 uInt8 i = 0;
// using myDisplayImage[] instead of myProgramImage[] because waveforms
// can be modified during runtime. /*
uInt32 i = myDisplayImage[(myMusicWaveforms[0] << 5) + (myMusicCounters[0] >> 27)] + in the ARM driver registers 8-10 are the music counters 0-2
myDisplayImage[(myMusicWaveforms[1] << 5) + (myMusicCounters[1] >> 27)] + lsr r2, r8, #31
myDisplayImage[(myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)]; add r2, r2, r9, lsr #31
return = (uInt8)i; add r2, r2, r10, lsr #31
#endif lsl r2, r2, #2
return 0xF2; // FIXME - return frequency value here */
i = myMusicCounters[0] >> 31;
i = i + (myMusicCounters[1] >> 31);
i = i + (myMusicCounters[2] >> 31);
i <<= 2;
return i;
} }
else else
myLDAimmediate = false; myLDAimmediate = false;
@ -132,9 +147,9 @@ uInt8 CartridgeCTY::peek(uInt16 address)
((myRandomNumber >> 11) | (myRandomNumber << 21)); ((myRandomNumber >> 11) | (myRandomNumber << 21));
return myRandomNumber & 0xFF; return myRandomNumber & 0xFF;
case 0x02: // Get Tune position (low byte) case 0x02: // Get Tune position (low byte)
return myCounter & 0xFF; return myTunePosition & 0xFF;
case 0x03: // Get Tune position (high byte) case 0x03: // Get Tune position (high byte)
return (myCounter >> 8) & 0xFF; return (myTunePosition >> 8) & 0xFF;
default: default:
return myRAM[address]; return myRAM[address];
} }
@ -175,7 +190,7 @@ bool CartridgeCTY::poke(uInt16 address, uInt8 value)
//cerr << "POKE: address=" << HEX4 << address << ", value=" << HEX2 << value << endl; //cerr << "POKE: address=" << HEX4 << address << ", value=" << HEX2 << value << endl;
if(address < 0x0040) // Write port is at $1000 - $103F (64 bytes) if(address < 0x0040) // Write port is at $1000 - $103F (64 bytes)
{ {
switch(address) // FIXME for functionality switch(address)
{ {
case 0x00: // Operation type for $1FF4 case 0x00: // Operation type for $1FF4
myOperationType = value; myOperationType = value;
@ -184,10 +199,16 @@ bool CartridgeCTY::poke(uInt16 address, uInt8 value)
myRandomNumber = 0x2B435044; myRandomNumber = 0x2B435044;
break; break;
case 0x02: // Reset fetcher to beginning of tune case 0x02: // Reset fetcher to beginning of tune
myCounter = 0; myTunePosition = 0;
myMusicCounters[0] = 0;
myMusicCounters[1] = 0;
myMusicCounters[2] = 0;
myMusicFrequencies[0] = 0;
myMusicFrequencies[1] = 0;
myMusicFrequencies[2] = 0;
break; break;
case 0x03: // Advance fetcher to next tune position case 0x03: // Advance fetcher to next tune position
myCounter = (myCounter + 3) & 0x0fff; updateTune();
break; break;
default: default:
myRAM[address] = value; myRAM[address] = value;
@ -283,11 +304,14 @@ bool CartridgeCTY::save(Serializer& out) const
out.putByteArray(myRAM, 64); out.putByteArray(myRAM, 64);
out.putByte(myOperationType); out.putByte(myOperationType);
out.putShort(myCounter); out.putShort(myTunePosition);
out.putBool(myLDAimmediate); out.putBool(myLDAimmediate);
out.putInt(myRandomNumber); out.putInt(myRandomNumber);
out.putLong(myAudioCycles); out.putLong(myAudioCycles);
out.putDouble(myFractionalClocks); out.putDouble(myFractionalClocks);
out.putIntArray(myMusicCounters, 3);
out.putIntArray(myMusicFrequencies, 3);
out.putLong(myFrequencyImage - myTuneData);
} }
catch(...) catch(...)
@ -309,11 +333,14 @@ bool CartridgeCTY::load(Serializer& in)
in.getByteArray(myRAM, 64); in.getByteArray(myRAM, 64);
myOperationType = in.getByte(); myOperationType = in.getByte();
myCounter = in.getShort(); myTunePosition = in.getShort();
myLDAimmediate = in.getBool(); myLDAimmediate = in.getBool();
myRandomNumber = in.getInt(); myRandomNumber = in.getInt();
myAudioCycles = in.getLong(); myAudioCycles = in.getLong();
myFractionalClocks = in.getDouble(); myFractionalClocks = in.getDouble();
in.getIntArray(myMusicCounters, 3);
in.getIntArray(myMusicFrequencies, 3);
myFrequencyImage = myTuneData + in.getLong();
} }
catch(...) catch(...)
{ {
@ -415,10 +442,62 @@ void CartridgeCTY::loadTune(uInt8 index)
// Each tune is offset by 4096 bytes // Each tune is offset by 4096 bytes
// Instead of copying non-modifiable data around (as would happen on the // Instead of copying non-modifiable data around (as would happen on the
// Harmony), we simply point to the appropriate tune // Harmony), we simply point to the appropriate tune
myFrequencyImage = CartCTYTunes + (index << 12); myFrequencyImage = myTuneData + (index << 12);
// Reset to beginning of tune // Reset to beginning of tune
myCounter = 0; myTunePosition = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCTY::updateTune()
{
//UpdateTune:
// /* Float data bus */
// strb r8, [r0, #+0x01]
//
// /* Increment song position */
// add r7, r7, #1 r7 = songPosition
//
// /* Read song data (0 = continue) */
// msr cpsr_c, #MODE_FIQ|I_BIT|F_BIT
// ldrb r2, [r14], #1 r14 = myTunePosition, r2 = note
// cmp r2, #0
// ldrne r11, [r6, +r2, lsl #2] r6 +r2 = ourFrequencyTable[note]. Why lsl #2?
// ldrb r2, [r14], #1 r11 = myMusicFrequency[0]
// cmp r2, #0
// ldrne r12, [r6, +r2, lsl #2] r12 = myMusicFrequency[1]
// ldrb r2, [r14], #1
// cmp r2, #1
// ldrcs r13, [r6, +r2, lsl #2] r13 = myMusicFrequency[2]
//
// /* Reset tune */
// mvneq r7, #0
// moveq r14, r4 r4 = start of tune data
// msr cpsr_c, #MODE_SYS|I_BIT|F_BIT
//
// /* Wait until address changes */
//WaitAddrChangeA:
// ldrh r2, [r0, #+0x16]
// cmp r1, r2
// beq WaitAddrChangeA
// b NewAddress
myTunePosition += 1;
uInt16 songPosition = (myTunePosition - 1) *3;
uInt8 note = myFrequencyImage[songPosition + 0];
if (note)
myMusicFrequencies[0] = ourFrequencyTable[note];
note = myFrequencyImage[songPosition + 1];
if (note)
myMusicFrequencies[1] = ourFrequencyTable[note];
note = myFrequencyImage[songPosition + 2];
if (note == 1)
myTunePosition = 0;
else
myMusicFrequencies[2] = ourFrequencyTable[note];
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -511,5 +590,139 @@ inline void CartridgeCTY::updateMusicModeDataFetchers()
// Let's update counters and flags of the music mode data fetchers // Let's update counters and flags of the music mode data fetchers
if(wholeClocks > 0) if(wholeClocks > 0)
for(int x = 0; x <= 2; ++x) for(int x = 0; x <= 2; ++x)
; //myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks; myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt32 CartridgeCTY::ourFrequencyTable[63] =
{
// this should really be referenced from within the ROM, but its part of
// the Harmony/Melody CTY Driver, which does not appear to be in the ROM.
0, // CONT 0 Continue Note
0, // REPEAT 1 Repeat Song
0, // REST 2 Note Rest
/*
3511350 // C0
3720300 // C0s
3941491 // D0
4175781 // D0s
4424031 // E0
4687313 // F0
4965841 // F0s
5261120 // G0
5496699 // G0s
5905580 // A1
6256694 // A1s
6628853 // B1
7022916 // C1
7440601 // C1s
7882983 // D1
8351778 // D1s
8848277 // E1
9374625 // F1
9931897 // F1s
10522455 // G1
11148232 // G1s
*/
11811160, // A2
12513387, // A2s
13257490, // B2
14045832, // C2
14881203, // C2s
15765966, // D2
16703557, // D2s
17696768, // E2
18749035, // F2
19864009, // F2s
21045125, // G2
22296464, // G2s
23622320, // A3
25026989, // A3s
26515195, // B3
28091878, // C3
29762191, // C3s
31531932, // D3
33406900, // D3s
35393537, // E3
37498071, // F3
39727803, // F3s
42090250, // G3
44592927, // G3s
47244640, // A4
50053978, // A4s
53030391, // B4
56183756, // C4 (Middle C)
59524596, // C4s
63064079, // D4
66814014, // D4s
70787074, // E4
74996142, // F4
79455606, // F4s
84180285, // G4
89186069, // G4s
94489281, // A5
100107957, // A5s
106060567, // B5
112367297, // C5
119048977, // C5s
126128157, // D5
133628029, // D5s
141573933, // E5
149992288, // F5
158911428, // F5s
168360785, // G5
178371925, // G5s
188978561, // A6
200215913, // A6s
212121348, // B6
224734593, // C6
238098169, // C6s
252256099, // D6
267256058, // D6s
283147866, // E6
299984783, // F6
317822855, // F6s
336721571, // G6
356744064 // G6s
/*
377957122 // A7
400431612 // A7s
424242481 // B7
449469401 // C7
476196124 // C7s
504512198 // D7
534512116 // D7s
566295948 // E7
599969565 // F7
635645496 // F7s
673443141 // G7
713488128 // G7s
755914244 // A8
800863224 // A8s
848484963 // B8
898938588 // C8
952392248 // C8s
1009024398 // D8
1069024232 // D8s
1132591895 // E8
1199939130 // F8
1271290992 // F8s
1346886282 // G8
1426976255 // G8s
1511828488 // A9
1601726449 // A9s
1696969925 // B9
1797877176 // C9
1904784495 // C9s
2018048796 // D9
2138048463 // D9s
*/
};

View File

@ -83,8 +83,6 @@ class System;
The tune table functionality is also based on Harmony EEPROM, where The tune table functionality is also based on Harmony EEPROM, where
7 4K tunes are stored (28K total). The 'index' for operation 1 can 7 4K tunes are stored (28K total). The 'index' for operation 1 can
therefore be in the range 0 - 6, indicating which tune to load. therefore be in the range 0 - 6, indicating which tune to load.
For this implementation, the 28K tune data is in the 'CartCTYTunes'
header file.
DPC+: DPC+:
The music functionality is quite similar to the DPC+ scheme. The music functionality is quite similar to the DPC+ scheme.
@ -256,6 +254,8 @@ class CartridgeCTY : public Cartridge
*/ */
void updateMusicModeDataFetchers(); void updateMusicModeDataFetchers();
void updateTune();
private: private:
// OSsytem currently in use // OSsytem currently in use
const OSystem& myOSystem; const OSystem& myOSystem;
@ -263,6 +263,9 @@ class CartridgeCTY : public Cartridge
// The 32K ROM image of the cartridge // The 32K ROM image of the cartridge
uInt8 myImage[32768]; uInt8 myImage[32768];
// The 28K ROM image of the music
uInt8 myTuneData[28*1024];
// The 64 bytes of RAM accessible at $1000 - $1080 // The 64 bytes of RAM accessible at $1000 - $1080
uInt8 myRAM[64]; uInt8 myRAM[64];
@ -270,11 +273,17 @@ class CartridgeCTY : public Cartridge
uInt8 myOperationType; uInt8 myOperationType;
// Pointer to the 28K frequency table (points to the start of one // Pointer to the 28K frequency table (points to the start of one
// of seven 4K tunes in CartCTYTunes) // of seven 4K tunes in myTuneData)
const uInt8* myFrequencyImage; const uInt8* myFrequencyImage;
// The counter register for the data fetcher // The counter register for the data fetcher
uInt16 myCounter; uInt16 myTunePosition;
// The music mode counters
uInt32 myMusicCounters[3];
// The music frequency
uInt32 myMusicFrequencies[3];
// Flags that last byte peeked was A9 (LDA #) // Flags that last byte peeked was A9 (LDA #)
bool myLDAimmediate; bool myLDAimmediate;
@ -301,6 +310,8 @@ class CartridgeCTY : public Cartridge
// Indicates the offset into the ROM image (aligns to current bank) // Indicates the offset into the ROM image (aligns to current bank)
uInt16 myBankOffset; uInt16 myBankOffset;
static const uInt32 ourFrequencyTable[63];
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
CartridgeCTY() = delete; CartridgeCTY() = delete;

File diff suppressed because it is too large Load Diff

View File

@ -445,13 +445,18 @@ Bankswitch::Type CartDetector::autodetectType(const BytePtr& image, uInt32 size)
type = Bankswitch::Type::_CDF; type = Bankswitch::Type::_CDF;
else if(isProbablyDPCplus(image, size)) else if(isProbablyDPCplus(image, size))
type = Bankswitch::Type::_DPCP; type = Bankswitch::Type::_DPCP;
else if(isProbablyCTY(image, size))
type = Bankswitch::Type::_CTY;
else if(isProbablyFA2(image, size)) else if(isProbablyFA2(image, size))
type = Bankswitch::Type::_FA2; type = Bankswitch::Type::_FA2;
else else
type = Bankswitch::Type::_F4; type = Bankswitch::Type::_F4;
} }
else if(size == 60*1024) // 60K
{
if(isProbablyCTY(image, size))
type = Bankswitch::Type::_CTY;
else
type = Bankswitch::Type::_F4;
}
else if(size == 64*1024) // 64K else if(size == 64*1024) // 64K
{ {
if(isProbably3E(image, size)) if(isProbably3E(image, size))
@ -662,9 +667,10 @@ bool CartDetector::isProbably4A50(const BytePtr& image, uInt32 size)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartDetector::isProbablyCTY(const BytePtr&, uInt32) bool CartDetector::isProbablyCTY(const BytePtr& image, uInt32 size)
{ {
return false; // TODO - add autodetection uInt8 signature[] = { 'L', 'E', 'N', 'I', 'N' };
return searchForBytes(image.get(), size, signature, 5, 1);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -