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