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