first debug version of ncpu
[fceu.git] / fds.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "types.h"
26#include "x6502.h"
27#include "version.h"
28#include "fce.h"
29#include "fds.h"
30#include "svga.h"
31#include "sound.h"
32#include "general.h"
33#include "state.h"
34#include "file.h"
35#include "memory.h"
36#include "cart.h"
37
38/* TODO: Add code to put a delay in between the time a disk is inserted
39 and the when it can be successfully read/written to. This should
40 prevent writes to wrong places OR add code to prevent disk ejects
41 when the virtual motor is on(mmm...virtual motor).
42*/
43
44static DECLFR(FDSRead4030);
45static DECLFR(FDSRead4031);
46static DECLFR(FDSRead4032);
47static DECLFR(FDSRead4033);
48static DECLFW(FDSWrite4020);
49static DECLFW(FDSWrite4021);
50static DECLFW(FDSWrite4022);
51static DECLFW(FDSWrite4023);
52static DECLFW(FDSWrite4024);
53static DECLFW(FDSWrite4025);
54static DECLFW(FDSWaveWrite);
55static DECLFR(FDSWaveRead);
56
57static DECLFR(FDSSRead);
58static DECLFW(FDSSWrite);
59static DECLFR(FDSBIOSRead);
60static DECLFR(FDSRAMRead);
61static DECLFW(FDSRAMWrite);
62static void FDSInit(void);
63static void FDSReset(void);
64static void FP_FASTAPASS(1) FDSFix(int a);
65
66#define FDSRAM GameMemBlock
67#define mapbyte1 (GameMemBlock+32768)
68#define mapbyte2 (GameMemBlock+32768+8)
69#define mapbyte3 (GameMemBlock+32768+16)
70#define mapbyte4 (GameMemBlock+32768+24) // 8 bytes
71#define CHRRAM (GameMemBlock+32768+32+64)
72
73#define IRQLatch (*(int32*)(CHRRAM+8192))
74#define IRQCount (*(int32*)(CHRRAM+8192+4))
75#define IRQa (*(CHRRAM+8192+8))
76
77static void FDSClose(void);
78static uint8 header[16];
79#define writeskip mapbyte2[0]
80
81static char FDSSaveName[2048];
82
83uint8 FDSBIOS[8192];
84uint8 *diskdata[4]={0,0,0,0};
85
86#define SideWrite mapbyte2[1]
87#define DiskPtr (*(uint32*)(mapbyte2+4))
88#define dsr0 mapbyte2[2]
89#define dsr1 mapbyte2[3]
90
91#define DC_INC 1
92
93#define DiskSeekIRQ (*(int32*)(mapbyte3+4))
94#define SelectDisk mapbyte3[0]
95#define InDisk mapbyte3[1]
96
97static void FDSReset(void)
98{
99 memset(mapbyte1,0,8);
100 memset(mapbyte2,0,8);
101 memset(mapbyte3+4,0,4);
102 memset(mapbyte4,0,8);
103}
104
105void FDSGI(int h)
106{
107 switch(h)
108 {
109 case GI_CLOSE: FDSClose();break;
110 case GI_POWER: FDSReset();FDSInit();break;
111 }
112}
113
114static void FDSStateRestore(int version)
115{
116 setmirror(((mapbyte1[5]&8)>>3)^1);
117}
118
119void FDSSound();
120void FDSSoundReset(void);
121void FDSSoundStateAdd(void);
122static void RenderSound(void);
123
124static void FDSInit(void)
125{
126 dsr0=0;
127 dsr1=0x41;
128 setmirror(1);
129 if(InDisk!=255)
130 dsr1&=0xFE;
131
132 setprg8r(0,0xe000,0); // BIOS
133 setprg32r(1,0x6000,0); // 32KB RAM
134 setchr8(0); // 8KB CHR RAM
135
136 MapIRQHook=FDSFix;
137 GameStateRestore=FDSStateRestore;
138
139 SetReadHandler(0x4030,0x4030,FDSRead4030);
140 SetReadHandler(0x4031,0x4031,FDSRead4031);
141 SetReadHandler(0x4032,0x4032,FDSRead4032);
142 SetReadHandler(0x4033,0x4033,FDSRead4033);
143
144 SetWriteHandler(0x4020,0x4020,FDSWrite4020);
145 SetWriteHandler(0x4021,0x4021,FDSWrite4021);
146 SetWriteHandler(0x4022,0x4022,FDSWrite4022);
147 SetWriteHandler(0x4023,0x4023,FDSWrite4023);
148 SetWriteHandler(0x4024,0x4024,FDSWrite4024);
149 SetWriteHandler(0x4025,0x4025,FDSWrite4025);
150
151 SetWriteHandler(0x6000,0xdfff,FDSRAMWrite);
152 SetReadHandler(0x6000,0xdfff,FDSRAMRead);
153 SetReadHandler(0xE000,0xFFFF,FDSBIOSRead);
154
155 IRQCount=IRQLatch=IRQa=0;
156
157 FDSSoundReset();
158}
159
160void FDSControl(int what)
161{
162 switch(what)
163 {
164 case FDS_IDISK:dsr1&=0xFE;
165 if(InDisk==255)
166 {
167 FCEU_DispMessage("Disk %d Side %s Inserted",
168 SelectDisk>>1,(SelectDisk&1)?"B":"A");
169 InDisk=SelectDisk;
170 }
171 else
172 FCEU_DispMessage("Jamming disks is a BAD IDEA");
173 break;
174 case FDS_EJECT:
175 if(InDisk!=255)
176 FCEU_DispMessage("Disk Ejected");
177 else
178 FCEU_DispMessage("Cannot Eject Air");
179 dsr1|=1;
180 InDisk=255;
181 break;
182 case FDS_SELECT:
183 if(InDisk!=255)
184 {
185 FCEU_DispMessage("Eject disk before selecting.");
186 break;
187 }
188 SelectDisk=((SelectDisk+1)%header[4])&3;
189 FCEU_DispMessage("Disk %d Side %s Selected",
190 SelectDisk>>1,(SelectDisk&1)?"B":"A");
191 break;
192 }
193}
194
195static void FP_FASTAPASS(1) FDSFix(int a)
196{
197 if(IRQa)
198 {
199 IRQCount-=a;
200 if(IRQCount<=0)
201 {
202 IRQa=0;
203 dsr0|=1;
204 dsr0&=~2;
205 IRQCount=0xFFFF;
206 X6502_IRQBegin(FCEU_IQEXT);
207 }
208 }
209 if(DiskSeekIRQ>0)
210 {
211 DiskSeekIRQ-=a;
212 if(DiskSeekIRQ<=0)
213 {
214 if(mapbyte1[5]&0x80)
215 {
216 dsr0&=~1;
217 dsr0|=2;
218 TriggerIRQ();
219 }
220 }
221 }
222}
223
224void DiskControl(int which)
225{
226if(mapbyte1[5]&1)
227 {
228 switch(which)
229 {
230 case DC_INC:
231 if(DiskPtr<64999) DiskPtr++;
232 //DiskSeekIRQ=160+100;
233 //DiskSeekIRQ=140;
234 //DiskSeekIRQ=160;
235 DiskSeekIRQ=150;
236 break;
237 }
238 }
239}
240static DECLFR(FDSRead4030)
241{
242 X6502_IRQEnd(FCEU_IQEXT);
243 return dsr0;
244}
245
246static DECLFR(FDSRead4031)
247{
248 static uint8 z=0;
249 if(InDisk!=255)
250 {
251 z=diskdata[InDisk][DiskPtr];
252 DiskControl(DC_INC);
253 }
254 return z;
255}
256
257static DECLFR(FDSRead4032)
258{
259 return dsr1;
260}
261
262static DECLFR(FDSRead4033)
263{
264 return 0x80; // battery
265}
266
267static DECLFW(FDSRAMWrite)
268{
269 (FDSRAM-0x6000)[A]=V;
270}
271
272static DECLFR(FDSBIOSRead)
273{
274 return (FDSBIOS-0xE000)[A];
275}
276
277static DECLFR(FDSRAMRead)
278{
279 return (FDSRAM-0x6000)[A];
280}
281
282/* Begin FDS sound */
283
284#define FDSClock (1789772.7272727272727272/8)
285
286typedef struct {
287 int64 cycles; // Cycles per PCM sample
288 int64 count; // Cycle counter
289 int64 envcount; // Envelope cycle counter
290 uint32 b19shiftreg60;
291 uint32 b24adder66;
292 uint32 b24latch68;
293 uint32 b17latch76;
294 int32 clockcount; // Counter to divide frequency by 8.
295 uint8 b8shiftreg88; // Modulation register.
296 uint8 amplitude[2]; // Current amplitudes.
297 uint8 mwave[0x20]; // Modulation waveform
298 uint8 cwave[0x40]; // Game-defined waveform(carrier)
299 uint8 SPSG[0xB];
300} FDSSOUND;
301
302static FDSSOUND fdso;
303
304#define SPSG fdso.SPSG
305#define b19shiftreg60 fdso.b19shiftreg60
306#define b24adder66 fdso.b24adder66
307#define b24latch68 fdso.b24latch68
308#define b17latch76 fdso.b17latch76
309#define b8shiftreg88 fdso.b8shiftreg88
310#define clockcount fdso.clockcount
311#define amplitude fdso.amplitude
312
313void FDSSoundStateAdd(void)
314{
315 AddExState(fdso.cwave,64,0,"WAVE");
316 AddExState(fdso.mwave,32,0,"MWAV");
317 AddExState(amplitude,2,0,"AMPL");
318 AddExState(SPSG,0xB,0,"SPSG");
319
320 AddExState(&b8shiftreg88,1,0,"B88");
321
322 AddExState(&clockcount, 4, 1, "CLOC");
323 AddExState(&b19shiftreg60,4,1,"B60");
324 AddExState(&b24adder66,4,1,"B66");
325 AddExState(&b24latch68,4,1,"B68");
326 AddExState(&b17latch76,4,1,"B76");
327
328}
329
330static DECLFR(FDSSRead)
331{
332 switch(A&0xF)
333 {
334 case 0x0:return(amplitude[0]|(X.DB&0xC0));
335 case 0x2:return(amplitude[1]|(X.DB&0xC0));
336 }
337 return(X.DB);
338}
339
340static DECLFW(FDSSWrite)
341{
342 if(FSettings.SndRate)
343 RenderSound();
344 A-=0x4080;
345 switch(A)
346 {
347 case 0x0:
348 case 0x4:
349 if(!(V&0x80))
350 {
351 // if(V&0x40) amplitude[(A&0xF)>>2]=0;
352 // else amplitude[(A&0xF)>>2]=0x3F;
353 }
354 else
355 amplitude[(A&0xF)>>2]=V&0x3F;
356 break;
357 case 0x7: b17latch76=0;SPSG[0x5]=0;break;
358 case 0x8:
359 //printf("%d:$%02x\n",SPSG[0x5],V);
360 fdso.mwave[SPSG[0x5]&0x1F]=V&0x7;
361 SPSG[0x5]=(SPSG[0x5]+1)&0x1F;
362 break;
363 }
364 //if(A>=0x7 && A!=0x8 && A<=0xF)
365 //if(A==0xA || A==0x9) printf("$%04x:$%02x\n",A,V);
366 SPSG[A]=V;
367}
368
369// $4080 - Fundamental wave amplitude data register 92
370// $4082 - Fundamental wave frequency data register 58
371// $4083 - Same as $4082($4083 is the upper 4 bits).
372
373// $4084 - Modulation amplitude data register 78
374// $4086 - Modulation frequency data register 72
375// $4087 - Same as $4086($4087 is the upper 4 bits)
376
377
378static void DoEnv()
379{
380 int x;
381
382 for(x=0;x<2;x++)
383 if(!(SPSG[x<<2]&0x80) && !(SPSG[0x9+x]&0x80))
384 {
385 static int counto[2]={0,0};
386
387 if(counto[x]<=0)
388 {
389 if(SPSG[x<<2]&0x40)
390 {
391 if(amplitude[x]<0x3F)
392 amplitude[x]++;
393 }
394 else
395 {
396 if(amplitude[x]>0)
397 amplitude[x]--;
398 }
399 counto[x]=(SPSG[x<<2]&0x3F);
400 }
401 else
402 counto[x]--;
403 }
404}
405
406static DECLFR(FDSWaveRead)
407{
408 return(fdso.cwave[A&0x3f]|(X.DB&0xC0));
409}
410
411static DECLFW(FDSWaveWrite)
412{
413 if(SPSG[0x9]&0x80)
414 fdso.cwave[A&0x3f]=V&0x3F;
415}
416
417static INLINE void ClockRise(void)
418{
419 if(!clockcount)
420 {
421 b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8));
422 b17latch76=(SPSG[0x6]|((SPSG[0x07]&0x3)<<8))+b17latch76;
423
424 if(!(SPSG[0x7]&0x80))
425 {
426 b8shiftreg88=(amplitude[1]*((fdso.mwave[(b17latch76>>11)&0x1F]&7)));
427 //b8shiftreg88=((fdso.mwave[(b17latch76>>11)&0x1F]&7))|(amplitude[1]<<3);
428 }
429 else
430 { b8shiftreg88=0;}
431 }
432 else
433 {
434 b19shiftreg60<<=1;
435 b8shiftreg88>>=1;
436 }
437 b24adder66=(b24latch68+b19shiftreg60)&0xFFFFFF;
438}
439
440static INLINE void ClockFall(void)
441{
442// if(!(SPSG[0x7]&0x80))
443 {
444 if(!(b8shiftreg88&1))
445 b24latch68=b24adder66;
446 }
447 clockcount=(clockcount+1)&7;
448}
449
450static INLINE int32 FDSDoSound(void)
451{
452 fdso.count+=fdso.cycles;
453 if(fdso.count>=((int64)1<<40))
454 {
455 dogk:
456 fdso.count-=(int64)1<<40;
457 ClockRise();
458 ClockFall();
459 }
460 if(fdso.count>=32768) goto dogk;
461
462 fdso.envcount-=fdso.cycles;
463 if(fdso.envcount<=0)
464 {
465 // Fix this?
466 fdso.envcount+=((int64)1<<40)*FDSClock/1024;
467 DoEnv();
468 }
469
470 // Might need to emulate applying the amplitude to the waveform a bit better...
471 return (fdso.cwave[b24latch68>>18]*amplitude[0]);
472}
473
474static int32 FBC=0;
475
476static void RenderSound(void)
477{
478 int32 end, start;
479 int32 x;
480
481 start=FBC;
482 end=(timestamp<<16)/soundtsinc;
483 if(end<=start)
484 return;
485 FBC=end;
486
487 if(!(SPSG[0x9]&0x80))
488 for(x=start;x<end;x++)
489 {
490 uint32 t=FDSDoSound();
491 Wave[x>>4]+=t>>3;
492 }
493}
494
495void FDSSound(int c)
496{
497 RenderSound();
498 FBC=c;
499}
500
501static void FDS_ESI(void)
502{
503 if(FSettings.SndRate)
504 {
505 fdso.cycles=((int64)1<<40)*FDSClock;
506 fdso.cycles/=FSettings.SndRate OVERSAMPLE;
507 }
508// fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate OVERSAMPLE);
509 SetReadHandler(0x4040,0x407f,FDSWaveRead);
510 SetWriteHandler(0x4040,0x407f,FDSWaveWrite);
511 SetWriteHandler(0x4080,0x408A,FDSSWrite);
512 SetReadHandler(0x4090,0x4092,FDSSRead);
513}
514
515void FDSSoundReset(void)
516{
517 memset(&fdso,0,sizeof(fdso));
518 FDS_ESI();
519 GameExpSound.Fill=FDSSound;
520 GameExpSound.RChange=FDS_ESI;
521}
522
523
524static DECLFW(FDSWrite4020)
525{
526 X6502_IRQEnd(FCEU_IQEXT);
527 IRQLatch&=0xFF00;
528 IRQLatch|=V;
529 mapbyte1[0]=V;
530}
531static DECLFW(FDSWrite4021)
532{
533 X6502_IRQEnd(FCEU_IQEXT);
534 IRQLatch&=0xFF;
535 IRQLatch|=V<<8;
536 mapbyte1[1]=V;
537}
538static DECLFW(FDSWrite4022)
539{
540 X6502_IRQEnd(FCEU_IQEXT);
541 IRQCount=IRQLatch;
542 IRQa=V&2;
543 mapbyte1[2]=V;
544}
545static DECLFW(FDSWrite4023)
546{
547 mapbyte1[3]=V;
548}
549static DECLFW(FDSWrite4024)
550{
551 if(InDisk!=255 && !(mapbyte1[5]&0x4) && mapbyte1[3]&0x1)
552 {
553 if(DiskPtr>=0 && DiskPtr<65000)
554 {
555 if(writeskip) writeskip--;
556 else if(DiskPtr>=2)
557 {
558 SideWrite|=1<<InDisk;
559 diskdata[InDisk][DiskPtr-2]=V;
560 }
561 }
562 }
563}
564static DECLFW(FDSWrite4025)
565{
566 if(InDisk!=255)
567 {
568 if(!(V&0x40))
569 {
570 if(mapbyte1[5]&0x40 && !(V&0x10))
571 {
572 DiskSeekIRQ=200;
573 DiskPtr-=2;
574 }
575 if(DiskPtr<0) DiskPtr=0;
576 }
577 if(!(V&0x4)) writeskip=2;
578 if(V&2) {DiskPtr=0;DiskSeekIRQ=200;}
579 if(V&0x40) DiskSeekIRQ=200;
580 }
581 mapbyte1[5]=V;
582 setmirror(((V>>3)&1)^1);
583}
584static void FreeFDSMemory(void)
585{
586 int x;
587
588 for(x=0;x<header[4];x++)
589 if(diskdata[x])
590 {
591 free(diskdata[x]);
592 diskdata[x]=0;
593 }
594}
595
596int FDSLoad(char *name, int fp)
597{
598 FILE *zp;
599 int x;
600
601 FCEU_fseek(fp,0,SEEK_SET);
602 FCEU_fread(header,16,1,fp);
603
604 if(memcmp(header,"FDS\x1a",4))
605 {
606 if(!(memcmp(header+1,"*NINTENDO-HVC*",14)))
607 {
608 long t;
609 t=FCEU_fgetsize(fp);
610 if(t<65500)
611 t=65500;
612 header[4]=t/65500;
613 header[0]=0;
614 FCEU_fseek(fp,0,SEEK_SET);
615 }
616 else
617 return 0;
618 }
619
620 if(header[4]>4) header[4]=4;
621 if(!header[4]) header[4]|=1;
622 for(x=0;x<header[4];x++)
623 {
624 diskdata[x]=FCEU_malloc(65500);
625 if(!diskdata[x])
626 {
627 int zol;
628 for(zol=0;zol<x;zol++)
629 free(diskdata[zol]);
630 return 0;
631 }
632 FCEU_fread(diskdata[x],1,65500,fp);
633 }
634
635 if(!(zp=fopen(FCEU_MakeFName(FCEUMKF_FDSROM,0,0),"rb")))
636 {
637 FCEU_PrintError("FDS BIOS ROM image missing!");
638 FreeFDSMemory();
639 return 0;
640 }
641
642 if(fread(FDSBIOS,1,8192,zp)!=8192)
643 {
644 fclose(zp);
645 FreeFDSMemory();
646 FCEU_PrintError("Error reading FDS BIOS ROM image");
647 return 0;
648 }
649
650 fclose(zp);
651
652 FCEUGameInfo.type=GIT_FDS;
653 strcpy(FDSSaveName,name);
654 GameInterface=FDSGI;
655
656 SelectDisk=0;
657 InDisk=255;
658
659 ResetExState();
660 FDSSoundStateAdd();
661
662 for(x=0;x<header[4];x++)
663 {
664 char temp[5];
665 sprintf(temp,"DDT%d",x);
666 AddExState(diskdata[x],65500,0,temp);
667 }
668
669 AddExState(FDSRAM,32768,0,"FDSR");
670 AddExState(mapbyte1,32,0,"MPBY");
671 AddExState(CHRRAM,8192,0,"CHRR");
672 AddExState(&IRQCount, 4, 1, "IRQC");
673 AddExState(&IRQLatch, 4, 1, "IQL1");
674 AddExState(&IRQa, 1, 0, "IRQA");
675
676
677 ResetCartMapping();
678
679 SetupCartCHRMapping(0,CHRRAM,8192,1);
680 SetupCartMirroring(0,0,0);
681
682 return 1;
683}
684
685void FDSClose(void)
686{
687 int fp;
688 int x;
689
690 fp=FCEU_fopen(FDSSaveName,"wb");
691 if(!fp) return;
692
693 if(header[0]) // Does it have a 16-byte FWNES-style header?
694 if(FCEU_fwrite(header,1,16,fp)!=16) // Write it out. Should be nicer than fseek()'ing if the file is compressed. Hopefully...
695 goto fdswerr;
696
697 for(x=0;x<header[4];x++)
698 {
699 if(FCEU_fwrite(diskdata[x],1,65500,fp)!=65500)
700 {
701 fdswerr:
702 FCEU_PrintError("Error writing FDS image \"%s\"!",FDSSaveName);
703 FCEU_fclose(fp);
704 return;
705 }
706 }
707 FreeFDSMemory();
708 FCEU_fclose(fp);
709}