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 &currentMusic;
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