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