1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 #include <cstring>
19 
20 #ifdef DEBUGGER_SUPPORT
21   #include "Debugger.hxx"
22   #include "CartCDFWidget.hxx"
23   #include "CartCDFInfoWidget.hxx"
24 #endif
25 
26 #include "System.hxx"
27 #include "CartCDF.hxx"
28 #include "TIA.hxx"
29 #include "exception/FatalEmulationError.hxx"
30 
31 #define COMMSTREAM        0x20
32 #define JUMPSTREAM_BASE   0x21
33 
34 #define FAST_FETCH_ON ((myMode & 0x0F) == 0)
35 #define DIGITAL_AUDIO_ON ((myMode & 0xF0) == 0)
36 
37 #define getUInt32(_array, _address) ((_array)[(_address) + 0]        +  \
38                                     ((_array)[(_address) + 1] << 8)  +  \
39                                     ((_array)[(_address) + 2] << 16) +  \
40                                     ((_array)[(_address) + 3] << 24))
41 
42 namespace {
thumulatorConfiguration(CartridgeCDF::CDFSubtype subtype)43   Thumbulator::ConfigureFor thumulatorConfiguration(CartridgeCDF::CDFSubtype subtype)
44   {
45     switch (subtype) {
46       case CartridgeCDF::CDFSubtype::CDF0:
47         return Thumbulator::ConfigureFor::CDF;
48 
49       case CartridgeCDF::CDFSubtype::CDF1:
50         return Thumbulator::ConfigureFor::CDF1;
51 
52       case CartridgeCDF::CDFSubtype::CDFJ:
53         return Thumbulator::ConfigureFor::CDFJ;
54 
55       case CartridgeCDF::CDFSubtype::CDFJplus:
56         return Thumbulator::ConfigureFor::CDFJplus;
57 
58       default:
59         throw runtime_error("unreachable");
60     }
61   }
62 }
63 
64 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeCDF(const ByteBuffer & image,size_t size,const string & md5,const Settings & settings)65 CartridgeCDF::CartridgeCDF(const ByteBuffer& image, size_t size,
66                            const string& md5, const Settings& settings)
67   : CartridgeARM(md5, settings)
68 {
69   // Copy the ROM image into my buffer
70   mySize = std::min(size, 512_KB);
71   myImage = make_unique<uInt8[]>(mySize);
72   std::copy_n(image.get(), mySize, myImage.get());
73 
74   // Detect cart version
75   setupVersion();
76 
77   // The lowest 2K is not accessible to the debugger
78   createRomAccessArrays(isCDFJplus() ? mySize - 2_KB : 28_KB);
79 
80   // Pointer to the program ROM
81   // which starts after the 2K driver (and 2K C Code for CDF)
82   myProgramImage = myImage.get() + (isCDFJplus() ? 2_KB : 4_KB);
83 
84   // Pointer to CDF driver in RAM
85   myDriverImage = myRAM.data();
86 
87   // Pointer to the display RAM (starts after 2K driver)
88   myDisplayImage = myRAM.data() + 2_KB;
89 
90   // C addresses
91   uInt32 cBase, cStart, cStack;
92   if (isCDFJplus()) {
93     cBase = getUInt32(myImage.get(), 0x17F8) & 0xFFFFFFFE;    // C Base Address
94     cStart = cBase;                                           // C Start Address
95     cStack = getUInt32(myImage.get(), 0x17F4);                // C Stack
96   } else {
97     cBase = 0x800;          // C Base Address
98     cStart = 0x808;         // C Start Address (skip ARM header)
99     cStack = 0x40001FDC;    // C Stack
100   }
101 
102   // Create Thumbulator ARM emulator
103   bool devSettings = settings.getBool("dev.settings");
104   myThumbEmulator = make_unique<Thumbulator>(
105     reinterpret_cast<uInt16*>(myImage.get()),
106     reinterpret_cast<uInt16*>(myRAM.data()),
107     static_cast<uInt32>(mySize),
108     cBase, cStart, cStack,
109     devSettings ? settings.getBool("dev.thumb.trapfatal") : false,
110     devSettings ? static_cast<double>(
111       settings.getFloat("dev.thumb.cyclefactor")) : 1.0,
112     thumulatorConfiguration(myCDFSubtype),
113     this);
114 
115   setInitialState();
116 
117   myPlusROM = make_unique<PlusROM>(mySettings, *this);
118 
119   // Determine whether we have a PlusROM cart
120   myPlusROM->initialize(myImage, mySize);
121 }
122 
123 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
reset()124 void CartridgeCDF::reset()
125 {
126   initializeRAM(myRAM.data()+2_KB, myRAM.size()-2_KB);
127 
128   // CDF always starts in bank 6, CDFJ+ in bank 0
129   initializeStartBank(isCDFJplus() ? 0 : 6);
130 
131   myAudioCycles = myARMCycles = 0;
132   myFractionalClocks = 0.0;
133 
134   setInitialState();
135 
136   // Upon reset we switch to the startup bank
137   bank(startBank());
138 
139   CartridgeARM::reset();
140 }
141 
142 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setInitialState()143 void CartridgeCDF::setInitialState()
144 {
145   // Copy initial CDF driver to Harmony RAM
146   std::copy_n(myImage.get(), 2_KB, myDriverImage);
147 
148   myMusicWaveformSize.fill(27);
149 
150   // Assuming mode starts out with Fast Fetch off and 3-Voice music,
151   // need to confirm with Chris
152   myMode = 0xFF;
153 
154   myBankOffset = myLDAimmediateOperandAddress = myJMPoperandAddress = 0;
155   myFastJumpActive = myFastJumpStream = 0;
156 
157   CartridgeARM::setInitialState();
158 }
159 
160 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
install(System & system)161 void CartridgeCDF::install(System& system)
162 {
163   mySystem = &system;
164 
165   // Map all of the accesses to call peek and poke
166   System::PageAccess access(this, System::PageAccessType::READ);
167   for(uInt16 addr = 0x1000; addr < 0x1040; addr += System::PAGE_SIZE)
168     mySystem->setPageAccess(addr, access);
169 
170   // Install pages for the startup bank
171   bank(startBank());
172 }
173 
174 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
updateMusicModeDataFetchers()175 inline void CartridgeCDF::updateMusicModeDataFetchers()
176 {
177   // Calculate the number of cycles since the last update
178   uInt32 cycles = uInt32(mySystem->cycles() - myAudioCycles);
179   myAudioCycles = mySystem->cycles();
180 
181   // Calculate the number of CDF OSC clocks since the last update
182   double clocks = ((20000.0 * cycles) / myClockRate) + myFractionalClocks;
183   uInt32 wholeClocks = uInt32(clocks);
184   myFractionalClocks = clocks - double(wholeClocks);
185 
186   // Let's update counters and flags of the music mode data fetchers
187   if(wholeClocks > 0)
188     for(int x = 0; x <= 2; ++x)
189       myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks;
190 }
191 
192 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
callFunction(uInt8 value)193 inline void CartridgeCDF::callFunction(uInt8 value)
194 {
195   switch (value)
196   {
197     // Call user written ARM code (will most likely be C compiled for ARM)
198     case 254: // call with IRQ driven audio, no special handling needed at this
199               // time for Stella as ARM code "runs in zero 6507 cycles".
200     case 255: // call without IRQ driven audio
201       try {
202         uInt32 cycles = uInt32(mySystem->cycles() - myARMCycles);
203 
204         myARMCycles = mySystem->cycles();
205         myThumbEmulator->run(cycles, value == 254);
206         updateCycles(cycles);
207       }
208       catch(const runtime_error& e) {
209         if(!mySystem->autodetectMode())
210         {
211           FatalEmulationError::raise(e.what());
212         }
213       }
214       break;
215     default:
216       break;
217   }
218 }
219 
220 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
peek(uInt16 address)221 uInt8 CartridgeCDF::peek(uInt16 address)
222 {
223   // Is this a PlusROM?
224   if(myPlusROM->isValid())
225   {
226     uInt8 value = 0;
227     if(myPlusROM->peekHotspot(address, value))
228       return value;
229   }
230 
231   address &= 0x0FFF;
232   uInt8 peekvalue = myProgramImage[myBankOffset + address];
233 
234   // In debugger/bank-locked mode, we ignore all hotspots and in general
235   // anything that can change the internal state of the cart
236   if(hotspotsLocked())
237     return peekvalue;
238 
239   // implement JMP FASTJMP which fetches the destination address from stream 33
240   if (myFastJumpActive
241       && myJMPoperandAddress == address)
242   {
243     uInt32 pointer;
244     uInt8 value;
245 
246     --myFastJumpActive;
247     ++myJMPoperandAddress;
248 
249     pointer = getDatastreamPointer(myFastJumpStream);
250     if (isCDFJplus()) {
251       value = myDisplayImage[ pointer >> 16 ];
252       pointer += 0x00010000;  // always increment by 1
253     } else {
254       value = myDisplayImage[ pointer >> 20 ];
255       pointer += 0x00100000;  // always increment by 1
256     }
257 
258     setDatastreamPointer(myFastJumpStream, pointer);
259 
260     return value;
261   }
262 
263   // test for JMP FASTJUMP where FASTJUMP = $0000
264   if (FAST_FETCH_ON
265       && peekvalue == 0x4C
266       && (myProgramImage[myBankOffset + address+1] & myFastjumpStreamIndexMask) == 0
267       && myProgramImage[myBankOffset + address+2] == 0)
268   {
269     myFastJumpActive = 2; // return next two peeks from datastream 31
270     myJMPoperandAddress = address + 1;
271     myFastJumpStream = myProgramImage[myBankOffset + address+1] + JUMPSTREAM_BASE;
272     return peekvalue;
273   }
274 
275   myJMPoperandAddress = 0;
276 
277   // Do a FAST FETCH LDA# if:
278   //  1) in Fast Fetch mode
279   //  2) peeking the operand of an LDA # instruction
280   //  3) peek value is 0-34
281   if(FAST_FETCH_ON
282      && myLDAimmediateOperandAddress == address
283      && peekvalue <= myAmplitudeStream)
284   {
285     myLDAimmediateOperandAddress = 0;
286     if (peekvalue == myAmplitudeStream)
287     {
288       updateMusicModeDataFetchers();
289 
290       if DIGITAL_AUDIO_ON
291       {
292         // retrieve packed sample (max size is 2K, or 4K of unpacked data)
293 
294         uInt32 sampleaddress = getSample() + (myMusicCounters[0] >> (isCDFJplus() ? 13 : 21));
295 
296         // get sample value from ROM or RAM
297         if (sampleaddress < 0x00080000)
298           peekvalue = myImage[sampleaddress];
299         else if (sampleaddress >= 0x40000000 && sampleaddress < 0x40008000) // check for RAM
300           peekvalue = myRAM[sampleaddress - 0x40000000];
301         else
302           peekvalue = 0;
303 
304         // make sure current volume value is in the lower nybble
305         if ((myMusicCounters[0] & (1<<(isCDFJplus() ? 12 : 20))) == 0)
306           peekvalue >>= 4;
307         peekvalue &= 0x0f;
308       }
309       else
310       {
311         peekvalue = myDisplayImage[getWaveform(0) + (myMusicCounters[0] >> myMusicWaveformSize[0])]
312                   + myDisplayImage[getWaveform(1) + (myMusicCounters[1] >> myMusicWaveformSize[1])]
313                   + myDisplayImage[getWaveform(2) + (myMusicCounters[2] >> myMusicWaveformSize[2])];
314       }
315       return peekvalue;
316     }
317     else
318     {
319       return readFromDatastream(peekvalue);
320     }
321   }
322   myLDAimmediateOperandAddress = 0;
323 
324   // Switch banks if necessary
325   switch(address)
326   {
327     case 0x0FF4:
328       bank(isCDFJplus() ? 0 : 6);
329       break;
330 
331     case 0x0FF5:
332       bank(isCDFJplus() ? 1 : 0);
333       break;
334 
335     case 0x0FF6:
336       bank(isCDFJplus() ? 2 : 1);
337       break;
338 
339     case 0x0FF7:
340       bank(isCDFJplus() ? 3 : 2);
341       break;
342 
343     case 0x0FF8:
344       bank(isCDFJplus() ? 4 : 3);
345       break;
346 
347     case 0x0FF9:
348       bank(isCDFJplus() ? 5 : 4);
349       break;
350 
351     case 0x0FFA:
352       bank(isCDFJplus() ? 6 : 5);
353       break;
354 
355     case 0x0FFB:
356       bank(isCDFJplus() ? 0 : 6);
357       break;
358 
359     default:
360       break;
361   }
362 
363   if(FAST_FETCH_ON && peekvalue == 0xA9)
364     myLDAimmediateOperandAddress = address + 1;
365 
366   return peekvalue;
367 }
368 
369 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
370 bool CartridgeCDF::poke(uInt16 address, uInt8 value)
371 {
372   uInt32 pointer;
373 
374   // Is this a PlusROM?
375   if(myPlusROM->isValid() && myPlusROM->pokeHotspot(address, value))
376     return true;
377 
378   address &= 0x0FFF;
379   switch(address)
380   {
381     case 0x0FF0:   // DSWRITE
382       pointer = getDatastreamPointer(COMMSTREAM);
383       if (isCDFJplus()) {
384         myDisplayImage[ pointer >> 16 ] = value;
385         pointer += 0x00010000;  // always increment by 1 when writing
386       } else {
387         myDisplayImage[ pointer >> 20 ] = value;
388         pointer += 0x00100000;  // always increment by 1 when writing
389       }
390       setDatastreamPointer(COMMSTREAM, pointer);
391       break;
392 
393     case 0x0FF1:   // DSPTR
394       pointer = getDatastreamPointer(COMMSTREAM);
395       pointer <<= 8;
396       if (isCDFJplus()) {
397         pointer &= 0xff000000;
398         pointer |= (value << 16);
399       } else {
400         pointer &= 0xf0000000;
401         pointer |= (value << 20);
402       }
403       setDatastreamPointer(COMMSTREAM, pointer);
404       break;
405 
406     case 0x0FF2:   // SETMODE
407       myMode = value;
408       break;
409 
410     case 0x0FF3:   // CALLFN
411       callFunction(value);
412       break;
413 
414    case 0x00FF4:
415       bank(isCDFJplus() ? 0 : 6);
416       break;
417 
418     case 0x0FF5:
419       bank(isCDFJplus() ? 1 : 0);
420       break;
421 
422     case 0x0FF6:
423       bank(isCDFJplus() ? 2 : 1);
424       break;
425 
426     case 0x0FF7:
427       bank(isCDFJplus() ? 3 : 2);
428       break;
429 
430     case 0x0FF8:
431       bank(isCDFJplus() ? 4 : 3);
432       break;
433 
434     case 0x0FF9:
435       bank(isCDFJplus() ? 5 : 4);
436       break;
437 
438     case 0x0FFA:
439       bank(isCDFJplus() ? 6 : 5);
440       break;
441 
442     case 0x0FFB:
443       bank(isCDFJplus() ? 0 : 6);
444       break;
445 
446     default:
447       break;
448   }
449 
450   return false;
451 }
452 
453 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
454 bool CartridgeCDF::bank(uInt16 bank, uInt16)
455 {
456   if(hotspotsLocked()) return false;
457 
458   // Remember what bank we're in
459   myBankOffset = bank << 12;
460 
461   // Setup the page access methods for the current bank
462   System::PageAccess access(this, System::PageAccessType::READ);
463 
464   // Map Program ROM image into the system
465   for(uInt16 addr = 0x1040; addr < 0x2000; addr += System::PAGE_SIZE)
466   {
467     access.romAccessBase = &myRomAccessBase[myBankOffset + (addr & 0x0FFF)];
468     access.romPeekCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF)];
469     access.romPokeCounter = &myRomAccessCounter[myBankOffset + (addr & 0x0FFF) + 28_KB];  // TODO: Change for CDFJ+???
470     mySystem->setPageAccess(addr, access);
471   }
472   return myBankChanged = true;
473 }
474 
475 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
476 uInt16 CartridgeCDF::getBank(uInt16) const
477 {
478   return myBankOffset >> 12;
479 }
480 
481 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
482 uInt16 CartridgeCDF::romBankCount() const
483 {
484   return 7;
485 }
486 
487 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
488 bool CartridgeCDF::patch(uInt16 address, uInt8 value)
489 {
490   address &= 0x0FFF;
491 
492   // For now, we ignore attempts to patch the CDF address space
493   if(address >= 0x0040)
494   {
495     myProgramImage[myBankOffset + (address & 0x0FFF)] = value;
496     return myBankChanged = true;
497   }
498   else
499     return false;
500 }
501 
502 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
503 const ByteBuffer& CartridgeCDF::getImage(size_t& size) const
504 {
505   size = mySize;
506   return myImage;
507 }
508 
509 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
510 uInt32 CartridgeCDF::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
511 {
512   switch (function)
513   {
514     case 0:
515       // _SetNote - set the note/frequency
516       myMusicFrequencies[value1] = value2;
517       break;
518 
519       // _ResetWave - reset counter,
520       // used to make sure digital samples start from the beginning
521     case 1:
522       myMusicCounters[value1] = 0;
523       break;
524 
525       // _GetWavePtr - return the counter
526     case 2:
527       return myMusicCounters[value1];
528 
529       // _SetWaveSize - set size of waveform buffer
530     case 3:
531       myMusicWaveformSize[value1] = value2;
532       break;
533 
534     default:
535       break;
536   }
537 
538   return 0;
539 }
540 
541 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
542 uInt8 CartridgeCDF::internalRamGetValue(uInt16 addr) const
543 {
544   if(addr < internalRamSize())
545     return myRAM[addr];
546   else
547     return 0;
548 }
549 
550 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
551 bool CartridgeCDF::save(Serializer& out) const
552 {
553   try
554   {
555     // Indicates which bank is currently active
556     out.putShort(myBankOffset);
557 
558     // Indicates current mode
559     out.putByte(myMode);
560 
561     // State of FastJump
562     out.putByte(myFastJumpActive);
563 
564     // operand addresses
565     out.putShort(myLDAimmediateOperandAddress);
566     out.putShort(myJMPoperandAddress);
567 
568     // Harmony RAM
569     out.putByteArray(myRAM.data(), myRAM.size());
570 
571     // Audio info
572     out.putIntArray(myMusicCounters.data(), myMusicCounters.size());
573     out.putIntArray(myMusicFrequencies.data(), myMusicFrequencies.size());
574     out.putByteArray(myMusicWaveformSize.data(), myMusicWaveformSize.size());
575 
576     // Save cycles and clocks
577     out.putLong(myAudioCycles);
578     out.putDouble(myFractionalClocks);
579     out.putLong(myARMCycles);
580 
581     CartridgeARM::save(out);
582   }
583   catch(...)
584   {
585     cerr << "ERROR: CartridgeCDF::save" << endl;
586     return false;
587   }
588 
589   return true;
590 }
591 
592 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
593 bool CartridgeCDF::load(Serializer& in)
594 {
595   try
596   {
597     // Indicates which bank is currently active
598     myBankOffset = in.getShort();
599 
600     // Indicates current mode
601     myMode = in.getByte();
602 
603     // State of FastJump
604     myFastJumpActive = in.getByte();
605 
606     // Address of LDA # operand
607     myLDAimmediateOperandAddress = in.getShort();
608     myJMPoperandAddress = in.getShort();
609 
610     // Harmony RAM
611     in.getByteArray(myRAM.data(), myRAM.size());
612 
613     // Audio info
614     in.getIntArray(myMusicCounters.data(), myMusicCounters.size());
615     in.getIntArray(myMusicFrequencies.data(), myMusicFrequencies.size());
616     in.getByteArray(myMusicWaveformSize.data(), myMusicWaveformSize.size());
617 
618     // Get cycles and clocks
619     myAudioCycles = in.getLong();
620     myFractionalClocks = in.getDouble();
621     myARMCycles = in.getLong();
622 
623     CartridgeARM::load(in);
624   }
625   catch(...)
626   {
627     cerr << "ERROR: CartridgeCDF::load" << endl;
628     return false;
629   }
630 
631   // Now, go to the current bank
632   bank(myBankOffset >> 12);
633 
634   return true;
635 }
636 
637 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
638 uInt32 CartridgeCDF::getDatastreamPointer(uInt8 index) const
639 {
640   uInt16 address = myDatastreamBase + index * 4;
641 
642   return myRAM[address + 0]        +  // low byte
643         (myRAM[address + 1] << 8)  +
644         (myRAM[address + 2] << 16) +
645         (myRAM[address + 3] << 24) ;  // high byte
646 }
647 
648 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
649 void CartridgeCDF::setDatastreamPointer(uInt8 index, uInt32 value)
650 {
651   uInt16 address = myDatastreamBase + index * 4;
652 
653   myRAM[address + 0] = value & 0xff;          // low byte
654   myRAM[address + 1] = (value >> 8) & 0xff;
655   myRAM[address + 2] = (value >> 16) & 0xff;
656   myRAM[address + 3] = (value >> 24) & 0xff;  // high byte
657 }
658 
659 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
660 uInt32 CartridgeCDF::getDatastreamIncrement(uInt8 index) const
661 {
662   uInt16 address = myDatastreamIncrementBase + index * 4;
663 
664   return myRAM[address + 0]        +   // low byte
665         (myRAM[address + 1] << 8)  +
666         (myRAM[address + 2] << 16) +
667         (myRAM[address + 3] << 24) ;   // high byte
668 }
669 
670 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
671 uInt32 CartridgeCDF::getWaveform(uInt8 index) const
672 {
673   uInt16 address = myWaveformBase + index * 4;
674 
675   uInt32 result = myRAM[address + 0]        +  // low byte
676                  (myRAM[address + 1] << 8)  +
677                  (myRAM[address + 2] << 16) +
678                  (myRAM[address + 3] << 24);   // high byte
679 
680   result -= (0x40000000 + uInt32(2_KB));
681 
682   if (!isCDFJplus()) {
683     if (result >= 4096) {
684       result &= 4095;
685     }
686   }
687   return result;
688 }
689 
690 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
691 uInt32 CartridgeCDF::getSample()
692 {
693   uInt16 address = myWaveformBase;
694 
695   uInt32 result = myRAM[address + 0]        +  // low byte
696                  (myRAM[address + 1] << 8)  +
697                  (myRAM[address + 2] << 16) +
698                  (myRAM[address + 3] << 24);   // high byte
699 
700   return result;
701 }
702 
703 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
704 uInt32 CartridgeCDF::getWaveformSize(uInt8 index) const
705 {
706   return myMusicWaveformSize[index];
707 }
708 
709 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
710 uInt8 CartridgeCDF::readFromDatastream(uInt8 index)
711 {
712   // Pointers are stored as:
713   // PPPFF---
714   //
715   // Increments are stored as
716   // ----IIFF
717   //
718   // P = Pointer
719   // I = Increment
720   // F = Fractional
721 
722   uInt32 pointer = getDatastreamPointer(index);
723   uInt16 increment = getDatastreamIncrement(index);
724 
725   uInt8 value;
726   if (isCDFJplus()) {
727     value = myDisplayImage[ pointer >> 16 ];
728     pointer += (increment << 8);
729   } else {
730     value = myDisplayImage[ pointer >> 20 ];
731     pointer += (increment << 12);
732   }
733 
734   setDatastreamPointer(index, pointer);
735   return value;
736 }
737 
738 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
739 void CartridgeCDF::setupVersion()
740 {
741   // CDFJ+ detection
742   if (getUInt32(myImage.get(), 0x174) == 0x53554c50 &&    // Plus
743       getUInt32(myImage.get(), 0x178) == 0x4a464443 &&    // CDFJ
744       getUInt32(myImage.get(), 0x17C) == 0x00000001) {    // V1
745 
746     myCDFSubtype = CDFSubtype::CDFJplus;
747     myAmplitudeStream = 0x23;
748     myFastjumpStreamIndexMask = 0xfe;
749     myDatastreamBase = 0x0098;
750     myDatastreamIncrementBase = 0x0124;
751     myWaveformBase = 0x01b0;
752     return;
753   }
754 
755   uInt8 subversion = 0;
756   for(uInt32 i = 0; i < 2048; i += 4)
757   {
758     // CDF signature occurs 3 times in a row, i+3 (+7 or +11) is version
759     if (    myImage[i+0] == 0x43 && myImage[i + 4] == 0x43 && myImage[i + 8] == 0x43) // C
760       if (  myImage[i+1] == 0x44 && myImage[i + 5] == 0x44 && myImage[i + 9] == 0x44) // D
761         if (myImage[i+2] == 0x46 && myImage[i + 6] == 0x46 && myImage[i +10] == 0x46) // F
762         {
763           subversion = myImage[i+3];
764           break;
765         }
766   }
767 
768   switch (subversion) {
769 
770     case 0x4a:
771       myCDFSubtype = CDFSubtype::CDFJ;
772 
773       myAmplitudeStream = 0x23;
774       myFastjumpStreamIndexMask = 0xfe;
775       myDatastreamBase = 0x0098;
776       myDatastreamIncrementBase = 0x0124;
777       myWaveformBase = 0x01b0;
778 
779       break;
780 
781     case 0:
782       myCDFSubtype = CDFSubtype::CDF0;
783 
784       myAmplitudeStream = 0x22;
785       myFastjumpStreamIndexMask = 0xff;
786       myDatastreamBase = 0x06e0;
787       myDatastreamIncrementBase = 0x0768;
788       myWaveformBase = 0x07f0;
789 
790       break;
791 
792     default:
793       myCDFSubtype = CDFSubtype::CDF1;
794 
795       myAmplitudeStream = 0x22;
796       myFastjumpStreamIndexMask = 0xff;
797       myDatastreamBase = 0x00a0;
798       myDatastreamIncrementBase = 0x0128;
799       myWaveformBase = 0x01b0;
800   }
801 }
802 
803 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
804 string CartridgeCDF::name() const
805 {
806   switch(myCDFSubtype)
807   {
808     case CDFSubtype::CDF0:
809       return "CartridgeCDF0";
810     case CDFSubtype::CDF1:
811       return "CartridgeCDF1";
812     case CDFSubtype::CDFJ:
813       return "CartridgeCDFJ";
814     case CDFSubtype::CDFJplus:
815       return "CartridgeCDFJ+";
816     default:
817       return "Cart unknown";
818   }
819 }
820 
821 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
822 bool CartridgeCDF::isCDFJplus() const
823 {
824   return (myCDFSubtype == CDFSubtype::CDFJplus);
825 }
826 
827 uInt32 CartridgeCDF::ramSize() const
828 {
829   return uInt32(isCDFJplus() ? 32_KB : 8_KB);
830 }
831 
832 uInt32 CartridgeCDF::romSize() const
833 {
834   return uInt32(isCDFJplus() ? mySize : 32_KB);
835 }
836 
837 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
838 #ifdef DEBUGGER_SUPPORT
839   CartDebugWidget* CartridgeCDF::debugWidget(GuiObject* boss, const GUI::Font& lfont,
840                                const GUI::Font& nfont, int x, int y, int w, int h)
841   {
842     return new CartridgeCDFWidget(boss, lfont, nfont, x, y, w, h, *this);
843   }
844 
845   CartDebugWidget* CartridgeCDF::infoWidget(GuiObject* boss, const GUI::Font& lfont,
846                                              const GUI::Font& nfont, int x, int y, int w, int h)
847   {
848     return new CartridgeCDFInfoWidget(boss, lfont, nfont, x, y, w, h, *this);
849   }
850 #endif
851