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 <cstdlib>
19
20 #include "Event.hxx"
21 #include "KidVid.hxx"
22
23 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KidVid(Jack jack,const Event & event,const System & system,const string & romMd5)24 KidVid::KidVid(Jack jack, const Event& event, const System& system,
25 const string& romMd5)
26 : Controller(jack, event, system, Controller::Type::KidVid),
27 myEnabled{myJack == Jack::Right}
28 {
29 // Right now, there are only two games that use the KidVid
30 if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6")
31 myGame = KVBBEARS; // Berenstain Bears
32 else if(romMd5 == "a204cd4fb1944c86e800120706512a64")
33 myGame = KVSMURFS; // Smurfs Save the Day
34 else
35 myEnabled = false;
36 }
37
38 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
~KidVid()39 KidVid::~KidVid()
40 {
41 closeSampleFile();
42 }
43
44 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
update()45 void KidVid::update()
46 {
47 if(!myEnabled)
48 return;
49
50 if(myEvent.get(Event::ConsoleReset))
51 {
52 myTape = 0; // rewind Kid Vid tape
53 closeSampleFile();
54 }
55 if(myEvent.get(Event::RightKeyboard1))
56 {
57 myTape = 2;
58 myIdx = myGame == KVBBEARS ? KVBLOCKBITS : 0;
59 myBlockIdx = KVBLOCKBITS;
60 myBlock = 0;
61 openSampleFile();
62 //cerr << "myTape = " << myTape << endl;
63 }
64 else if(myEvent.get(Event::RightKeyboard2))
65 {
66 myTape = 3;
67 myIdx = myGame == KVBBEARS ? KVBLOCKBITS : 0;
68 myBlockIdx = KVBLOCKBITS;
69 myBlock = 0;
70 openSampleFile();
71 //cerr << "myTape = " << myTape << endl;
72 }
73 else if(myEvent.get(Event::RightKeyboard3))
74 {
75 if(myGame == KVBBEARS) /* Berenstain Bears ? */
76 {
77 myTape = 4;
78 myIdx = KVBLOCKBITS;
79 //cerr << "myTape = " << myTape << endl;
80 }
81 else /* no, Smurf Save The Day */
82 {
83 myTape = 1;
84 myIdx = 0;
85 //cerr << "myTape = " << myTape << endl;
86 }
87 myBlockIdx = KVBLOCKBITS;
88 myBlock = 0;
89 openSampleFile();
90 }
91
92 // Convert separate pin states into a 'register'
93 uInt8 IOPortA = 0b11110000;
94 if(getPin(DigitalPin::One)) IOPortA |= 0b0001;
95 if(getPin(DigitalPin::Two)) IOPortA |= 0b0010;
96 if(getPin(DigitalPin::Three)) IOPortA |= 0b0100;
97 if(getPin(DigitalPin::Four)) IOPortA |= 0b1000;
98
99 // Is the tape running?
100 if((myTape != 0) && ((IOPortA & 0b0001) == 0b0001) && !myTapeBusy)
101 {
102 IOPortA = (IOPortA & 0b11110111) | (((ourKVData[myIdx >> 3] << (myIdx & 0x07)) & 0x80) >> 4);
103
104 // increase to next bit
105 ++myIdx;
106 --myBlockIdx;
107
108 // increase to next block (byte)
109 if(myBlockIdx == 0)
110 {
111 if(myBlock == 0)
112 myIdx = ((myTape * 6) + 12 - KVBLOCKS) * 8; //KVData00-KVData=12
113 else
114 {
115 if(myGame == KVSMURFS)
116 {
117 if(myBlock >= ourKVBlocks[myTape - 1])
118 myIdx = 42 * 8; //KVData80-KVData=42
119 else
120 {
121 myIdx = 36 * 8;//KVPause-KVData=36
122 setNextSong();
123 }
124 }
125 else
126 {
127 if(myBlock >= ourKVBlocks[myTape + 2 - 1])
128 myIdx = 42 * 8; //KVData80-KVData=42
129 else
130 {
131 myIdx = 36 * 8;//KVPause-KVData=36
132 setNextSong();
133 }
134 }
135 }
136 ++myBlock;
137 myBlockIdx = KVBLOCKBITS;
138 }
139 }
140
141 // Now convert the register back into separate boolean values
142 setPin(DigitalPin::One, IOPortA & 0b0001);
143 setPin(DigitalPin::Two, IOPortA & 0b0010);
144 setPin(DigitalPin::Three, IOPortA & 0b0100);
145 setPin(DigitalPin::Four, IOPortA & 0b1000);
146 }
147
148 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
openSampleFile()149 void KidVid::openSampleFile()
150 {
151 #if 0
152 static const char* const kvNameTable[6] = {
153 "kvs3.wav", "kvs1.wav", "kvs2.wav", "kvb3.wav", "kvb1.wav", "kvb2.wav"
154 };
155 static uInt32 StartSong[6] = {
156 44+38, 0, 44, 44+38+42+62+80, 44+38+42, 44+38+42+62
157 };
158
159 if(!myEnabled)
160 return;
161
162 if(!myFileOpened)
163 {
164 int i = myGame == KVSMURFS ? 0 : 3;
165 i += myTape - 1;
166 if(myTape == 4) i -= 3;
167
168 mySampleFile = fopen(kvNameTable[i], "rb");
169 if(mySampleFile != nullptr)
170 {
171 cerr << "opened file: " << kvNameTable[i] << endl;
172 mySharedSampleFile = fopen("kvshared.wav", "rb");
173 if(mySharedSampleFile == nullptr)
174 {
175 fclose(mySampleFile);
176 myFileOpened = false;
177 }
178 else
179 {
180 cerr << "opened file: " << "kvshared.wav" << endl;
181 // fseek(mySampleFile, 45, SEEK_SET);
182 myFileOpened = true;
183 }
184 }
185 else
186 myFileOpened = false;
187
188 mySongCounter = 0;
189 myTapeBusy = false;
190 myFilePointer = StartSong[i];
191 }
192 #endif
193 }
194
195 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
closeSampleFile()196 void KidVid::closeSampleFile()
197 {
198 #if 0
199 if(myFileOpened)
200 {
201 fclose(mySampleFile);
202 fclose(mySharedSampleFile);
203 myFileOpened = false;
204 }
205 #endif
206 }
207
208 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setNextSong()209 void KidVid::setNextSong()
210 {
211 if(myFileOpened)
212 {
213 myBeep = (ourSongPositions[myFilePointer] & 0x80) ? false : true;
214
215 uInt8 temp = ourSongPositions[myFilePointer] & 0x7f;
216 mySharedData = (temp < 10);
217 mySongCounter = ourSongStart[temp+1] - ourSongStart[temp];
218
219 #if 0
220 if(mySharedData)
221 ; // fseek(mySharedSampleFile, ourSongStart[temp], SEEK_SET);
222 else
223 ; // fseek(mySampleFile, ourSongStart[temp], SEEK_SET);
224 #endif
225
226 ++myFilePointer;
227 myTapeBusy = true;
228 }
229 else
230 {
231 myBeep = true;
232 myTapeBusy = true;
233 mySongCounter = 80*262; /* delay needed for Harmony without tape */
234 }
235 }
236
237 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getNextSampleByte()238 void KidVid::getNextSampleByte()
239 {
240 // static int oddeven = 0;
241 if(mySongCounter == 0)
242 mySampleByte = 0x80;
243 #if 0
244 else
245 {
246 oddeven = oddeven^1;
247 if(oddeven & 1)
248 {
249 mySongCounter--;
250 myTapeBusy = (mySongCounter > 262*48) || !myBeep;
251
252 if(myFileOpened)
253 {
254 if(mySharedData)
255 mySampleByte = getc(mySharedSampleFile);
256 else
257 mySampleByte = getc(mySampleFile);
258 }
259 else
260 mySampleByte = 0x80;
261
262 if(!myBeep && (mySongCounter == 0))
263 setNextSong();
264 }
265 }
266 #endif
267 }
268
269 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
270 const std::array<uInt8, KidVid::KVBLOCKS> KidVid::ourKVBlocks = {
271 2+40, 2+21, 2+35, /* Smurfs tapes 3, 1, 2 */
272 42+60, 42+78, 42+60 /* BBears tapes 1, 2, 3 (40 extra blocks for intro) */
273 };
274
275 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
276 const std::array<uInt8, KidVid::KVBLOCKBITS> KidVid::ourKVData = {
277 /* KVData44 */
278 0x7b, // 0111 1011b ; (1)0
279 0x1e, // 0001 1110b ; 1
280 0xc6, // 1100 0110b ; 00
281 0x31, // 0011 0001b ; 01
282 0xec, // 1110 1100b ; 0
283 0x60, // 0110 0000b ; 0+
284
285 /* KVData48 */
286 0x7b, // 0111 1011b ; (1)0
287 0x1e, // 0001 1110b ; 1
288 0xc6, // 1100 0110b ; 00
289 0x3d, // 0011 1101b ; 10
290 0x8c, // 1000 1100b ; 0
291 0x60, // 0110 0000b ; 0+
292
293 /* KVData00 */
294 0xf6, // 1111 0110b
295 0x31, // 0011 0001b
296 0x8c, // 1000 1100b
297 0x63, // 0110 0011b
298 0x18, // 0001 1000b
299 0xc0, // 1100 0000b
300
301 /* KVData01 */
302 0xf6, // 1111 0110b
303 0x31, // 0011 0001b
304 0x8c, // 1000 1100b
305 0x63, // 0110 0011b
306 0x18, // 0001 1000b
307 0xf0, // 1111 0000b
308
309 /* KVData02 */
310 0xf6, // 1111 0110b
311 0x31, // 0011 0001b
312 0x8c, // 1000 1100b
313 0x63, // 0110 0011b
314 0x1e, // 0001 1110b
315 0xc0, // 1100 0000b
316
317 /* KVData03 */
318 0xf6, // 1111 0110b
319 0x31, // 0011 0001b
320 0x8c, // 1000 1100b
321 0x63, // 0110 0011b
322 0x1e, // 0001 1110b
323 0xf0, // 1111 0000b
324
325 /* KVPause */
326 0x3f, // 0011 1111b
327 0xf0, // 1111 0000b
328 0x00, // 0000 0000b
329 0x00, // 0000 0000b
330 0x00, // 0000 0000b
331 0x00, // 0000 0000b
332
333 /* KVData80 */
334 0xf7, // 1111 0111b ; marks end of tape (green/yellow screen)
335 0xb1, // 1011 0001b
336 0x8c, // 1000 1100b
337 0x63, // 0110 0011b
338 0x18, // 0001 1000b
339 0xc0 // 1100 0000b
340 };
341
342 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
343 const std::array<uInt8, KidVid::SONG_POS_SIZE> KidVid::ourSongPositions = {
344 /* kvs1 44 */
345 11, 12+0x80, 13+0x80, 14, 15+0x80, 16, 8+0x80, 17, 18+0x80, 19, 20+0x80,
346 21, 8+0x80, 22, 15+0x80, 23, 18+0x80, 14, 20+0x80, 16, 18+0x80,
347 17, 15+0x80, 19, 8+0x80, 21, 20+0x80, 22, 18+0x80, 23, 15+0x80,
348 14, 20+0x80, 16, 8+0x80, 22, 15+0x80, 23, 18+0x80, 14, 20+0x80,
349 16, 8+0x80, 9,
350
351 /* kvs2 38 */
352 25+0x80, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
353 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
354 30+0x80, 9,
355
356 /* kvs3 42 */
357 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 34, 42, 36, 43, 40, 39, 38, 37,
358 34, 43, 36, 39, 40, 37, 38, 43, 34, 37, 36, 43, 40, 39, 38, 37, 34, 43,
359 36, 39, 40, 37, 38+0x80, 9,
360
361 /* kvb1 62 */
362 0, 1, 45, 2, 3, 46, 4, 5, 47, 6, 7, 48, 4, 3, 49, 2, 1, 50, 6, 7, 51,
363 4, 5, 52, 6, 1, 53, 2, 7, 54, 6, 5, 45, 2, 1, 46, 4, 3, 47, 2, 5, 48,
364 4, 7, 49, 6, 1, 50, 2, 5, 51, 6, 3, 52, 4, 7, 53, 2, 1, 54, 6+0x80, 9,
365
366 /* kvb2 80 */
367 0, 1, 56, 4, 3, 57, 2, 5, 58, 6, 7, 59, 2, 3, 60, 4, 1, 61, 6, 7, 62,
368 2, 5, 63, 6, 1, 64, 4, 7, 65, 6, 5, 66, 4, 1, 67, 2, 3, 68, 6, 5, 69,
369 2, 7, 70, 4, 1, 71, 2, 5, 72, 4, 3, 73, 6, 7, 74, 2, 1, 75, 6, 3, 76,
370 4, 5, 77, 6, 7, 78, 2, 3, 79, 4, 1, 80, 2, 7, 81, 4+0x80, 9,
371
372 /* kvb3 62 */
373 0, 1, 83, 2, 3, 84, 4, 5, 85, 6, 7, 86, 4, 3, 87, 2, 1, 88, 6, 7, 89,
374 2, 5, 90, 6, 1, 91, 4, 7, 92, 6, 5, 93, 4, 1, 94, 2, 3, 95, 6, 5, 96,
375 2, 7, 97, 4, 1, 98, 6, 5, 99, 4, 3, 100, 2, 7, 101, 4, 1, 102, 2+0x80, 9
376 };
377
378 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
379 const std::array<uInt32, KidVid::SONG_START_SIZE> KidVid::ourSongStart = {
380 /* kvshared */
381 44, /* Welcome + intro Berenstain Bears */
382 980829, /* boulders in the road */
383 1178398, /* standing ovations */
384 1430063, /* brother bear */
385 1691136, /* good work */
386 1841665, /* crossing a bridge */
387 2100386, /* not bad (applause) */
388 2283843, /* ourgame */
389 2629588, /* start the parade */
390 2824805, /* rewind */
391 3059116,
392
393 /* kvs1 */
394 44, /* Harmony into 1 */
395 164685, /* falling notes (into 2) */
396 395182, /* instructions */
397 750335, /* high notes are high */
398 962016, /* my hat's off to you */
399 1204273, /* 1 2 3 do re mi */
400 1538258, /* Harmony */
401 1801683, /* concratulations (all of the Smurfs voted) */
402 2086276, /* line or space */
403 2399093, /* hooray */
404 2589606, /* hear yeeh */
405 2801287, /* over the river */
406 3111752, /* musical deduction */
407 3436329,
408
409 /* kvs2 */
410 44, /* Handy intro + instructions */
411 778557, /* place in shape */
412 1100782, /* sailor mate + whistle */
413 // 1281887,
414 1293648, /* attention */
415 1493569, /* colours */
416 1801682, /* congratulations (Handy and friends voted) */
417 2086275,
418
419 /* kvs3 */
420 44, /* Greedy and Clumsy intro + instructions */
421 686829, /* red */
422 893806, /* don't count your chicken */
423 1143119, /* yellow */
424 1385376, /* thank you */
425 1578241, /* mixin' and matchin' */
426 1942802, /* fun / colour shake */
427 2168595, /* colours can be usefull */
428 2493172, /* hip hip horay */
429 2662517, /* green */
430 3022374, /* purple */
431 3229351, /* white */
432 3720920,
433
434 /* kvb1 */
435 44, /* 3 */
436 592749, /* 5 */
437 936142, /* 2 */
438 1465343, /* 4 */
439 1787568, /* 1 */
440 2145073, /* 7 */
441 2568434, /* 9 */
442 2822451, /* 8 */
443 3045892, /* 6 */
444 3709157, /* 0 */
445 4219542,
446
447 /* kvb2 */
448 44, /* A */
449 303453, /* B */
450 703294, /* C */
451 1150175, /* D */
452 1514736, /* E */
453 2208577, /* F */
454 2511986, /* G */
455 2864787, /* H */
456 3306964, /* I */
457 3864389, /* J */
458 4148982, /* K */
459 4499431, /* L */
460 4824008, /* M */
461 5162697, /* N */
462 5581354, /* O */
463 5844779, /* P */
464 6162300, /* Q */
465 6590365, /* R */
466 6839678, /* S */
467 7225407, /* T */
468 7552336, /* U */
469 7867505, /* V */
470 8316738, /* W */
471 8608387, /* X */
472 8940020, /* Y */
473 9274005, /* Z */
474 9593878,
475
476 /* kvb3 */
477 44, /* cat */
478 341085, /* one */
479 653902, /* red */
480 1018463, /* two */
481 1265424, /* dog */
482 1669969, /* six */
483 1919282, /* hat */
484 2227395, /* ten */
485 2535508, /* mom */
486 3057653, /* dad */
487 3375174, /* ball */
488 3704455, /* fish */
489 4092536, /* nine */
490 4487673, /* bear */
491 5026282, /* four */
492 5416715, /* bird */
493 5670732, /* tree */
494 6225805, /* rock */
495 6736190, /* book */
496 7110159, /* road */
497 7676992
498 };
499