1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #define __MAIN_DECLARATIONS__
7 #include "sapGlobals.h"
8
9 #ifdef _MSC_EXTENSIONS
10 #pragma pack(push)
11 #pragma pack(1)
12 #endif
13 typedef struct {
14 int numOfSongs;
15 int defSong; // zero based index (0....numOfSongs-1)
16 char *commentBuffer;
17 int currentTimeIn50Sec;
18 } sapMUSICstrc;
19 #ifdef _MSC_EXTENSIONS
20 #pragma pack(pop)
21 #endif
22
23 sapMUSICstrc *sapLoadMusicFile( char *fname );
24 void sapPlaySong( int numOfSong );
25 void sapRenderBuffer( short int *buffer, int number_of_samples );
26
27 namespace _SAP_internals_ {
28
29 void playerCallSubroutine( WORD address );
30 void playerProcessOneFrame( void );
31
32 volatile int prSbp;
33 volatile int musicAddress,playerAddress,playerInit,playerType,defSong,fastPlay;
34 volatile BOOL fileLoadStatus=FALSE;
35
36 char inputBuffer[65536];
37 char commentBuffer[65536];
38
39 sapMUSICstrc currentMusic;
40
41 BYTE emulEmptyLine[]={
42 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
43 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
46 };
47
48 }
49
50 using namespace _SAP_internals_;
51
sapLoadMusicFile(char * fname)52 sapMUSICstrc *sapLoadMusicFile( char *fname )
53 {
54 BOOL formatKnown,binaryFile; int i;
55 int numOfSongs;
56
57 fileLoadStatus = FALSE;
58 if( fname==NULL )
59 return NULL;
60
61 cpuInit( );
62 pokeyInit( );
63 pokeyReset( );
64 prSbp = 0;
65 sndBufPtr = 0;
66 memset( sndBuf, 0, sizeof(sndBuf) );
67 commentBuffer[0] = 0;
68 memset( atariMem, 0, sizeof(atariMem) );
69 atariMem[0x0000] = 0xFF; // CMC bug.
70 atariMem[0xFEFF] = 0xFF; // CMC bug.
71 // atariMem[0xFFFE] = 0xFF; // CMC bug.
72 // atariMem[0xFFFF] = 0xFF; // CMC bug.
73
74 isStereo = false;
75 sampleStep = 1;
76 formatKnown = FALSE; binaryFile = FALSE;
77 musicAddress = playerAddress = playerType = numOfSongs = defSong = fastPlay = -1;
78 FILE *inpf;
79 inpf = fopen( fname, "rb" );
80 if( inpf!=NULL )
81 {
82 BYTE blkHead[4]; WORD blkBegin,blkEnd; int blk;
83
84 blk = 0; i = 0;
85 while( 1 )
86 {
87 int val; val = fgetc( inpf );
88 if( formatKnown==FALSE )
89 {
90 if( (val==EOF) || (i>=4) )
91 {
92 // error
93 break;
94 }
95 if( (val==0x0A) || (val==0x0D) )
96 {
97 inputBuffer[i] = 0;
98 if( strcmp( inputBuffer, "SAP" )!=0 )
99 {
100 // error
101 break;
102 }
103 // headerID is OK
104 i = 0;
105 formatKnown = TRUE;
106 continue;;
107 }
108 inputBuffer[i++] = (char)val;
109 }
110 else if( binaryFile==FALSE )
111 {
112 if( val==EOF )
113 {
114 // error
115 break;
116 }
117 if( val==0xFF )
118 {
119 if( i!=0 )
120 {
121 // error. There wasn't EOL in this line
122 break;
123 }
124 // so this is the end of header
125 binaryFile = TRUE;
126 blkHead[blk++] = (BYTE)val;
127 continue;
128 }
129 if( (val==0x0A) || (val==0x0D) )
130 {
131 if( i==0 )
132 continue;
133 inputBuffer[i++] = 0;
134 char *codes[]={ "PLAYER", "MUSIC", "INIT", "TYPE", "SONGS", "DEFSONG", "FASTPLAY", "STEREO", "TIME", NULL };
135 int j,k,a;
136 for( j=0;; j++ )
137 {
138 if( codes[j]==NULL )
139 break;
140 k = 0;
141 while( 1 )
142 {
143 if( k>=i )
144 {
145 // not this code
146 i = 0;
147 break;
148 }
149 if( (codes[j][k]==0) && ((inputBuffer[k]==' ') || (inputBuffer[k]==0)) )
150 {
151 // found
152 switch( j )
153 {
154 case 0: // player address
155 a = strtol( &inputBuffer[k], NULL, 16 );
156 if( (a==0) || (a>0xFFFF) )
157 {
158 // error
159 i = 0;
160 goto cont;
161 }
162 playerAddress = a;
163 i = 0;
164 goto cont;
165 break;
166 case 1: // music address
167 a = strtol( &inputBuffer[k], NULL, 16 );
168 if( (a==0) || (a>0xFFFF) )
169 {
170 // error
171 i = 0;
172 goto cont;
173 }
174 musicAddress = a;
175 i = 0;
176 goto cont;
177 break;
178 case 2: // binary player init
179 a = strtol( &inputBuffer[k], NULL, 16 );
180 if( (a==0) || (a>0xFFFF) )
181 {
182 // error
183 i = 0;
184 goto cont;
185 }
186 playerInit = a;
187 i = 0;
188 goto cont;
189 break;
190 case 3: // player type
191 while( inputBuffer[k]!=0 )
192 {
193 if( (inputBuffer[k]=='B') || (inputBuffer[k]=='b') )
194 {
195 playerType = 'b';
196 break;
197 }
198 if( (inputBuffer[k]=='C') || (inputBuffer[k]=='c') )
199 {
200 playerType = 'c';
201 break;
202 }
203 if( (inputBuffer[k]=='D') || (inputBuffer[k]=='d') )
204 {
205 playerType = 'd';
206 break;
207 }
208 if( (inputBuffer[k]=='S') || (inputBuffer[k]=='s') )
209 {
210 playerType = 's';
211 break;
212 }
213 if( !isspace(inputBuffer[k]) )
214 {
215 i = 0;
216 goto cont;
217 }
218 k++;
219 }
220 i = 0;
221 goto cont;
222 break;
223 case 4: // num of songs
224 a = strtol( &inputBuffer[k], NULL, 10 );
225 if( (a==0) || (a>0xFFFF) )
226 {
227 // error
228 i = 0;
229 goto cont;
230 }
231 numOfSongs = a;
232 sprintf( &commentBuffer[ strlen(commentBuffer) ], "Number of songs = %d\n", numOfSongs );
233 i = 0;
234 goto cont;
235 break;
236 case 5: // default songs
237 a = strtol( &inputBuffer[k], NULL, 10 );
238 if( (a==0) || (a>0xFFFF) )
239 {
240 // error
241 i = 0;
242 goto cont;
243 }
244 defSong = a;
245 i = 0;
246 goto cont;
247 break;
248 case 6: // fastPlay
249 a = strtol( &inputBuffer[k], NULL, 10 );
250 if( (a==0) || (a>0xFFFF) )
251 {
252 // error
253 i = 0;
254 goto cont;
255 }
256 fastPlay = a;
257 i = 0;
258 goto cont;
259 break;
260 case 7: // Stereo
261 isStereo = true;
262 sampleStep = 2;
263 sprintf( &commentBuffer[ strlen(commentBuffer) ], "In Stereo!\n" );
264 i = 0;
265 goto cont;
266 break;
267 case 8: // Time
268 {
269 int min,sec;
270 min = sec = -1;
271 sscanf( &inputBuffer[k], "%d:%d", &min, &sec );
272 if( min==-1 || sec==-1 )
273 {
274 i = 0;
275 goto cont;
276 }
277 sprintf( &commentBuffer[ strlen(commentBuffer) ], "Time in sec %d\n", min*60+sec );
278 i = 0;
279 goto cont;
280 break;
281 }
282 }
283 i = 0;
284 goto cont;
285 }
286 if( codes[j][k]==0 )
287 break; // not this code
288 if( codes[j][k]!=inputBuffer[k] )
289 break; // not this code
290 k++;
291 }
292 }
293 sprintf( &commentBuffer[ strlen(commentBuffer) ], "%s\n", inputBuffer );
294 // not found
295 i = 0;
296 continue;
297 }
298 inputBuffer[i++] = (char)val;
299 }
300 else if( binaryFile==TRUE )
301 {
302 if( val==EOF )
303 {
304 if( blk==0 )
305 {
306 fileLoadStatus = TRUE;
307 break; // OK
308 }
309 // error
310 break;
311 }
312 if( blk<4 )
313 {
314 blkHead[blk++] = (BYTE)val;
315 if( blk==2 )
316 {
317 if( (blkHead[0]&blkHead[1])==255 )
318 blk = 0;
319 }
320 if( blk==4 )
321 {
322 blkBegin = ((WORD)blkHead[0]) + ((WORD)blkHead[1])*256;
323 blkEnd = ((WORD)blkHead[2]) + ((WORD)blkHead[3])*256;
324 }
325 }
326 else
327 {
328 atariMem[blkBegin] = (BYTE)val;
329 if( blkBegin==blkEnd )
330 {
331 blk = 0;
332 }
333 blkBegin++;
334 }
335 }
336 cont:;
337
338 }
339 fclose( inpf );
340
341 }
342 if( playerType==-1 )
343 return NULL;
344 fileLoadStatus = TRUE;
345 currentMusic.defSong = defSong;
346 currentMusic.numOfSongs = numOfSongs;
347 currentMusic.commentBuffer = &commentBuffer[0];
348 currentMusic.currentTimeIn50Sec = 0;
349
350 sapPlaySong( defSong );
351 return ¤tMusic;
352 }
353
354
sapPlaySong(int numOfSong)355 void sapPlaySong( int numOfSong )
356 {
357 if( fileLoadStatus==FALSE )
358 return;
359
360 if( numOfSong==-1 )
361 numOfSong = 0;
362 numOfSong &= 0xFF;
363 numOfSong = numOfSong % currentMusic.numOfSongs;
364
365 sndBufPtr = prSbp = 0;
366 switch( playerType )
367 {
368 case 'c':
369 if( (playerAddress==-1) || (musicAddress==-1) )
370 {
371 fileLoadStatus = FALSE;
372 break;
373 }
374 cpuReg_S = 0xFF;
375 cpuReg_A = 0x70;
376 cpuReg_X = (musicAddress&0xFF);
377 cpuReg_Y = (musicAddress>>8)&0xFF;
378 playerCallSubroutine( playerAddress+3 );
379 cpuReg_S = 0xFF;
380 cpuReg_A = 0x00;
381 cpuReg_X = numOfSong;
382 playerCallSubroutine( playerAddress+3 );
383 break;
384 case 'b':
385 case 'm':
386 if( (playerInit==-1) || (playerAddress==-1) )
387 {
388 fileLoadStatus = FALSE;
389 break;
390 }
391 cpuReg_S = 0xFF;
392 cpuReg_A = numOfSong;
393 playerCallSubroutine( playerInit );
394 break;
395 case 'd':
396 if( (playerInit==-1) || (playerAddress==-1) )
397 {
398 fileLoadStatus = FALSE;
399 break;
400 }
401 cpuReg_S = 0xFF;
402 cpuReg_PC = 0xFFFF;
403 cpuReg_PC--;
404 atariMem[0x100 + cpuReg_S] = (cpuReg_PC>>8)&0xFF; cpuReg_S--;
405 atariMem[0x100 + cpuReg_S] = cpuReg_PC&0xFF; cpuReg_S--;
406 cpuReg_PC = playerInit;
407 cpuReg_A = numOfSong;
408 cpuReg_X = 0;
409 cpuReg_Y = 0;
410 cpuSetFlags( 0x20 );
411 break;
412 case 's':
413 if( (playerInit==-1) || (playerAddress==-1) )
414 {
415 fileLoadStatus = FALSE;
416 break;
417 }
418 cpuReg_S = 0xFF;
419 cpuReg_PC = playerInit;
420 cpuReg_A = 0;
421 cpuReg_X = 0;
422 cpuReg_Y = 0;
423 cpuSetFlags( 0x20 );
424 break;
425 }
426 }
427
428
playerCallSubroutine(WORD address)429 void _SAP_internals_::playerCallSubroutine( WORD address )
430 {
431 int i,k;
432
433 cpuReg_PC = 0xFFFF;
434 cpuReg_PC--;
435 atariMem[0x100 + cpuReg_S] = (cpuReg_PC>>8)&0xFF; cpuReg_S--;
436 atariMem[0x100 + cpuReg_S] = cpuReg_PC&0xFF; cpuReg_S--;
437 cpuReg_PC = address;
438 k = 0;
439 bool holded;
440 while(k<1000000)
441 {
442 BYTE opcode = atariMem[ cpuReg_PC ];
443 i = opcodes_0x00_0xFF[opcode](holded);
444 k+=i;
445 if( i>10 )
446 return;
447 if( cpuReg_PC==0xFFFF )
448 break;
449 }
450 k++;
451 }
452
453 extern bool *generateIRQ0;
454 int numsPerFrame;
455
playerProcessOneFrame(void)456 void _SAP_internals_::playerProcessOneFrame( void )
457 {
458 int ilp;
459 BYTE oldFlags,oldA,oldX,oldY,oldS;
460 WORD oldPC;
461 BOOL notRestored;
462 bool holded;
463 int cycleInLine,lineInFrame;
464
465 numsPerFrame = 0;
466
467 if( playerType=='d' )
468 {
469 oldFlags = cpuGetFlags();
470 oldA = cpuReg_A;
471 oldX = cpuReg_X;
472 oldY = cpuReg_Y;
473 oldPC = cpuReg_PC;
474 oldS = cpuReg_S;
475 notRestored = TRUE;
476 }
477 if( playerType=='s' )
478 {
479
480 }
481
482 if( playerType!='s' )
483 {
484 cpuReg_S = 0x8F;
485 cpuReg_PC = 0xFFFF;
486 cpuReg_PC--;
487 atariMem[0x100 + cpuReg_S] = (cpuReg_PC>>8)&0xFF; cpuReg_S--;
488 atariMem[0x100 + cpuReg_S] = cpuReg_PC&0xFF; cpuReg_S--;
489 }
490
491 switch( playerType )
492 {
493 case 'c':
494 cpuReg_PC = playerAddress+6;
495 break;
496 case 'b':
497 case 'd':
498 case 'm':
499 cpuReg_PC = playerAddress;
500 break;
501 }
502 cycleInLine = 0;
503 if( fastPlay!=-1 )
504 lineInFrame = 0;
505 else if( playerType=='s' )
506 lineInFrame = 0;
507 else
508 lineInFrame = 248;
509 holded = false;
510 pokeyUpdateSoundCounters();
511
512 while(1)
513 {
514 if( cpuReg_PC!=0xFFFF )
515 {
516 BYTE opcode = atariMem[ cpuReg_PC ];
517 ilp = opcodes_0x00_0xFF[opcode](holded);
518 if( ilp>10 )
519 return; // not supported instruction has been executed
520 if( (playerType=='b') || (playerType=='c') )
521 ilp = 1;
522 }
523 else
524 {
525 if( playerType=='d' )
526 {
527 if( notRestored==TRUE )
528 {
529 notRestored = FALSE;
530 cpuReg_PC = oldPC;
531 cpuReg_A = oldA;
532 cpuReg_X = oldX;
533 cpuReg_Y = oldY;
534 cpuSetFlags( oldFlags );
535 cpuReg_S = oldS;
536 }
537 }
538 else
539 {
540 do {
541 pokeyUpdateSound(114);
542 lineInFrame++;
543 currentMusic.currentTimeIn50Sec++;
544 if( fastPlay!=-1 )
545 {
546 if( lineInFrame==fastPlay )
547 {
548 pokeyUpdateSoundCounters();
549 goto bre;
550 }
551 }
552 else
553 {
554 if( lineInFrame==248 )
555 {
556 pokeyUpdateSoundCounters();
557 goto bre;
558 }
559 if( lineInFrame==312 )
560 {
561 lineInFrame=0;
562 pokeyUpdateSoundCounters();
563 }
564 }
565 } while(1);
566 }
567 }
568 again:
569 for(;ilp>0;ilp--)
570 {
571 if( cycleInLine>=103-9 )
572 {
573 if( cycleInLine==103-9 )
574 {
575 if( holded )
576 {
577 ilp = 0;
578 holded = false;
579 }
580 }
581 if( cycleInLine==113-9 )
582 {
583 pokeyUpdateSound(114);
584 cycleInLine = -1;
585 lineInFrame++;
586 currentMusic.currentTimeIn50Sec++;
587 if( fastPlay!=-1 )
588 {
589 if( lineInFrame==fastPlay )
590 {
591 pokeyUpdateSoundCounters();
592 goto bre;
593 }
594 }
595 else if( playerType=='s' )
596 {
597 if( lineInFrame==78 )
598 {
599 pokeyUpdateSoundCounters();
600 atariMem[0x45]--;
601 if( atariMem[0x45]==0 )
602 atariMem[0xB07B]++;
603 goto bre;
604 }
605 }
606 else
607 {
608 if( lineInFrame==248 )
609 {
610 pokeyUpdateSoundCounters();
611 goto bre;
612 }
613 if( lineInFrame==312 )
614 {
615 lineInFrame=0;
616 pokeyUpdateSoundCounters();
617 }
618 }
619 ANTIC_VCOUNT_D40B = (BYTE)(lineInFrame / 2);
620 }
621 }
622 cycleInLine++;
623 }
624 if( holded )
625 {
626 ilp=114-9;
627 goto again;
628 }
629 if( generateIRQ0[0] )
630 {
631 generateIRQ0[0] = false;
632 pokeyGenerateIRQ(1);
633 numsPerFrame++;
634 }
635 }
636 bre:;
637 }
638
639
sapRenderBuffer(short int * buffer,int number_of_samples)640 void sapRenderBuffer( short int *buffer, int number_of_samples )
641 {
642 int i;
643
644 if( fileLoadStatus==FALSE )
645 return;
646
647 i = 0;
648 number_of_samples *= sampleStep;
649 while( i<number_of_samples )
650 {
651 if( prSbp==sndBufPtr )
652 playerProcessOneFrame();
653 for( ;prSbp!=sndBufPtr; prSbp=(prSbp+1)&16383 )
654 {
655 if( isStereo )
656 buffer[i] = sndBuf[prSbp&16383];
657 else
658 {
659 buffer[i*2+0] = sndBuf[prSbp&16383];
660 buffer[i*2+1] = sndBuf[prSbp&16383];
661 }
662 if( i>=number_of_samples )
663 break;
664 i++;
665 }
666 }
667 }
668
669
670
671 /*
672 {
673 FILE *outf;
674 outf = fopen( "c:\\asd.txt", "at" );
675 fprintf( outf, "%02X %02X %02X %02X %02X %02X %02X %02X - %02X\n", logWrites[0],
676 logWrites[1], logWrites[2], logWrites[3], logWrites[4],
677 logWrites[5], logWrites[6], logWrites[7], logWrites[8] );
678 fclose( outf );
679 }*/
680