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