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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
44 | CartridgeBUS::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
80 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
98 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
116 | void CartridgeBUS::consoleChanged(ConsoleTiming timing) |
117 | { |
118 | myThumbEmulator->setConsoleTiming(timing); |
119 | } |
120 | |
121 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
122 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
141 | inline 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
159 | inline 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
184 | uInt8 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
340 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
433 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
453 | uInt16 CartridgeBUS::getBank(uInt16) const |
454 | { |
455 | return myBankOffset >> 12; |
456 | } |
457 | |
458 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
459 | uInt16 CartridgeBUS::bankCount() const |
460 | { |
461 | return 7; |
462 | } |
463 | |
464 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
465 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
480 | const uInt8* CartridgeBUS::getImage(size_t& size) const |
481 | { |
482 | size = myImage.size(); |
483 | return myImage.data(); |
484 | } |
485 | |
486 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
487 | uInt8 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
514 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
543 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
584 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
628 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
639 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
649 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
659 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
669 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
699 | uInt32 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
712 | uInt32 CartridgeBUS::getWaveformSize(uInt8 index) const |
713 | { |
714 | return myMusicWaveformSize[index]; |
715 | } |
716 | |
717 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
718 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
728 | uInt8 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 | |