b632336d736e6584d69ffbf7b5a5f3f80832775a
[fceu.git] / svga.c
1 /* FCE Ultra - NES/Famicom Emulator
2  *
3  * Copyright notice for this file:
4  *  Copyright (C) 1998 BERO
5  *  Copyright (C) 2002 Ben Parnell
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 /*                      SVGA High Level Routines
23                           FCE / FCE Ultra
24 */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <string.h>
29
30 #include <stdarg.h>
31
32
33 #ifndef M_PI
34 #define M_PI 3.14159265358979323846
35 #endif
36
37 #include "types.h"
38 #include "svga.h"
39 #include "fce.h"
40 #include "general.h"
41 #include "video.h"
42 #include "sound.h"
43 #include "version.h"
44 #include "nsf.h"
45 #include "palette.h"
46 #include "fds.h"
47 #include "netplay.h"
48 #include "state.h"
49 #include "cart.h"
50 #include "input.h"
51
52 FCEUS FSettings;
53
54 static int howlong;
55 static char errmsg[65];
56
57 void FCEU_PrintError(char *format, ...)
58 {
59  char temp[2048];
60
61  va_list ap;
62
63  va_start(ap,format);
64  vsprintf(temp,format,ap);
65  FCEUD_PrintError(temp);
66
67  va_end(ap);
68 }
69
70 void FCEU_DispMessage(char *format, ...)
71 {
72  va_list ap;
73
74  va_start(ap,format);
75  vsprintf(errmsg,format,ap);
76  va_end(ap);
77
78  howlong=180;
79 }
80
81 void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall)
82 {
83  FSettings.UsrFirstSLine[0]=ntscf;
84  FSettings.UsrLastSLine[0]=ntscl;
85  FSettings.UsrFirstSLine[1]=palf;
86  FSettings.UsrLastSLine[1]=pall;
87  if(PAL)
88  {
89   FSettings.FirstSLine=FSettings.UsrFirstSLine[1];
90   FSettings.LastSLine=FSettings.UsrLastSLine[1];
91  }
92  else
93  {
94   FSettings.FirstSLine=FSettings.UsrFirstSLine[0];
95   FSettings.LastSLine=FSettings.UsrLastSLine[0];
96  }
97
98 }
99
100 void FCEUI_SetVidSystem(int a)
101 {
102  FSettings.PAL=a?1:0;
103  FCEU_ResetVidSys();
104  FCEU_ResetPalette();
105 }
106
107 int FCEUI_GetCurrentVidSystem(int *slstart, int *slend)
108 {
109  if(slstart)
110   *slstart=FSettings.FirstSLine;
111  if(slend)
112   *slend=FSettings.LastSLine;
113  return(PAL);
114 }
115
116 #ifdef NETWORK
117 void FCEUI_SetNetworkPlay(int type)
118 {
119  FSettings.NetworkPlay=type;
120 }
121 #endif
122
123 void FCEUI_SetGameGenie(int a)
124 {
125  FSettings.GameGenie=a?1:0;
126 }
127
128 static void CalculatePalette(void);
129 static void ChoosePalette(void);
130 static void WritePalette(void);
131
132 #ifndef NETWORK
133 #define netplay 0
134 #endif
135
136 static uint8 StateShow=0;
137
138 uint8 Exit=0;
139
140 uint8 DIPS=0;
141 uint8 vsdip=0;
142 int coinon=0;
143
144 uint8 pale=0;
145 uint8 CommandQueue=0;
146
147 static int controlselect=0;
148 static int ntsccol=0;
149 static int ntsctint=46+10;
150 static int ntschue=72;
151 static int controllength=0;
152
153 pal *palo;
154 static pal *palpoint[8]=
155      {
156      palette,
157      palettevscv,
158      palettevssmb,
159      palettevsmar,
160      palettevsgoon,
161      palettevsslalom,
162      palettevseb,
163      rp2c04001
164      };
165
166 void FCEUI_SetSnapName(int a)
167 {
168  FSettings.SnapName=a;
169 }
170
171 void FCEUI_SaveExtraDataUnderBase(int a)
172 {
173  FSettings.SUnderBase=a;
174 }
175
176 void FCEUI_SetPaletteArray(uint8 *pal)
177 {
178  if(!pal)
179   palpoint[0]=palette;
180  else
181  {
182   int x;
183   palpoint[0]=palettec;
184   for(x=0;x<64;x++)
185   {
186    palpoint[0][x].r=*((uint8 *)pal+x+x+x);
187    palpoint[0][x].g=*((uint8 *)pal+x+x+x+1);
188    palpoint[0][x].b=*((uint8 *)pal+x+x+x+2);
189   }
190  }
191  FCEU_ResetPalette();
192 }
193
194 void FCEUI_SelectState(int w)
195 {
196  if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
197   CommandQueue=42+w;
198 }
199
200 void FCEUI_SaveState(void)
201 {
202  if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
203   CommandQueue=40;
204 }
205
206 void FCEUI_LoadState(void)
207 {
208  if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
209   CommandQueue=41;
210 }
211
212 int32 FCEUI_GetDesiredFPS(void)
213 {
214   if(PAL)
215    return(838977920); // ~50.007
216   else
217    return(1008307711);  // ~60.1
218 }
219
220 static int dosnapsave=0;
221 void FCEUI_SaveSnapshot(void)
222 {
223  dosnapsave=1;
224 }
225
226 /* I like the sounds of breaking necks. */
227 static void ReallySnap(void)
228 {
229  int x=SaveSnapshot();
230  if(!x)
231   FCEU_DispMessage("Error saving screen snapshot.");
232  else
233   FCEU_DispMessage("Screen snapshot %d saved.",x-1);
234 }
235
236 void DriverInterface(int w, void *d)
237 {
238  switch(w)
239  {
240   case DES_NTSCCOL:ntsccol=*(int *)d;FCEU_ResetPalette();break;
241   case DES_RESET:if(netplay!=2) CommandQueue=30;break;
242   case DES_POWER:if(netplay!=2) CommandQueue=31;break;
243   case DES_GETNTSCTINT:*(int*)d=ntsctint;break;
244   case DES_GETNTSCHUE:*(int*)d=ntschue;break;
245   case DES_SETNTSCTINT:ntsctint=*(int*)d;if(ntsccol)FCEU_ResetPalette();break;
246   case DES_SETNTSCHUE:ntschue=*(int*)d;if(ntsccol)FCEU_ResetPalette();break;
247
248   case DES_FDSINSERT:if(netplay!=2) CommandQueue=2;break;
249   case DES_FDSEJECT:if(netplay!=2) CommandQueue=3;break;
250   case DES_FDSSELECT:if(netplay!=2) CommandQueue=1;break;
251
252   case DES_NSFINC:NSFControl(1);break;
253   case DES_NSFDEC:NSFControl(2);break;
254   case DES_NSFRES:NSFControl(0);break;
255
256   case DES_VSUNIDIPSET:CommandQueue=10+(int)d;break;
257   case DES_VSUNITOGGLEDIPVIEW:CommandQueue=10;break;
258   case DES_VSUNICOIN:CommandQueue=19;break;
259   case DES_NTSCSELHUE:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=1;controllength=360;}break;
260   case DES_NTSCSELTINT:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=2;controllength=360;}break;
261
262   case DES_NTSCDEC:
263                   if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF)
264                   {
265                    char which;
266                    if(controlselect)
267                    {
268                     if(controllength)
269                     {
270                      which=controlselect==1?ntschue:ntsctint;
271                      which--;
272                      if(which<0) which=0;
273                          if(controlselect==1)
274                           ntschue=which;
275                          else ntsctint=which;
276                      CalculatePalette();
277                     }
278                    controllength=360;
279                     }
280                    }
281                   break;
282   case DES_NTSCINC:
283                    if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF)
284                      if(controlselect)
285                      {
286                       if(controllength)
287                       {
288                        switch(controlselect)
289                        {
290                         case 1:ntschue++;
291                                if(ntschue>128) ntschue=128;
292                                CalculatePalette();
293                                break;
294                         case 2:ntsctint++;
295                                if(ntsctint>128) ntsctint=128;
296                                CalculatePalette();
297                                break;
298                        }
299                       }
300                       controllength=360;
301                      }
302                     break;
303   }
304 }
305
306 static uint8 lastd=0;
307 void SetNESDeemph(uint8 d, int force)
308 {
309  static uint16 rtmul[7]={32768*1.239,32768*.794,32768*1.019,32768*.905,32768*1.023,32768*.741,32768*.75};
310  static uint16 gtmul[7]={32768*.915,32768*1.086,32768*.98,32768*1.026,32768*.908,32768*.987,32768*.75};
311  static uint16 btmul[7]={32768*.743,32768*.882,32768*.653,32768*1.277,32768*.979,32768*.101,32768*.75};
312  uint32 r,g,b;
313  int x;
314
315  /* If it's not forced(only forced when the palette changes),
316     don't waste cpu time if the same deemphasis bits are set as the last call.
317  */
318  if(!force)
319  {
320   if(d==lastd)
321    return;
322  }
323  else   /* Only set this when palette has changed. */
324  {
325   r=rtmul[6];
326   g=rtmul[6];
327   b=rtmul[6];
328
329   for(x=0;x<0x40;x++)
330   {
331    uint32 m,n,o;
332    m=palo[x].r;
333    n=palo[x].g;
334    o=palo[x].b;
335    m=(m*r)>>15;
336    n=(n*g)>>15;
337    o=(o*b)>>15;
338    if(m>0xff) m=0xff;
339    if(n>0xff) n=0xff;
340    if(o>0xff) o=0xff;
341    FCEUD_SetPalette(x|0x40,m,n,o);
342
343
344   }
345  }
346  if(!d) return; /* No deemphasis, so return. */
347
348   r=rtmul[d-1];
349   g=gtmul[d-1];
350   b=btmul[d-1];
351
352     for(x=0;x<0x40;x++)
353     {
354      uint32 m,n,o;
355
356      m=palo[x].r;
357      n=palo[x].g;
358      o=palo[x].b;
359      m=(m*r)>>15;
360      n=(n*g)>>15;
361      o=(o*b)>>15;
362      if(m>0xff) m=0xff;
363      if(n>0xff) n=0xff;
364      if(o>0xff) o=0xff;
365
366      FCEUD_SetPalette(x|0xC0,m,n,o);
367
368     }
369
370  lastd=d;
371 }
372
373 #define HUEVAL  ((double)((double)ntschue/(double)2)+(double)300)
374 #define TINTVAL ((double)((double)ntsctint/(double)128))
375
376 static void CalculatePalette(void)
377 {
378  int x,z;
379  int r,g,b;
380  double s,y,theta;
381  static uint8 cols[16]={0,24,21,18,15,12,9,6,3,0,33,30,27,0,0,0};
382  static uint8 br1[4]={6,9,12,12};
383  static double br2[4]={.29,.45,.73,.9};
384  static double br3[4]={0,.24,.47,.77};
385
386  for(x=0;x<=3;x++)
387   for(z=0;z<16;z++)
388   {
389    s=(double)TINTVAL;
390    y=(double)br2[x];
391    if(z==0)  {s=0;y=((double)br1[x])/12;}
392
393    if(z>=13)
394    {
395     s=y=0;
396     if(z==13)
397      y=(double)br3[x];
398    }
399
400    theta=(double)M_PI*(double)(((double)cols[z]*10+HUEVAL)/(double)180);
401    r=(int)(((double)y+(double)s*(double)sin(theta))*(double)256);
402    g=(int)(((double)y-(double)((double)27/(double)53)*s*(double)sin(theta)+(double)((double)10/(double)53)*s*cos(theta))*(double)256);
403    b=(int)(((double)y-(double)s*(double)cos(theta))*(double)256);
404
405    // TODO:  Fix RGB to compensate for phosphor changes(add to red??).
406
407    if(r>255) r=255;
408    if(g>255) g=255;
409    if(b>255) b=255;
410    if(r<0) r=0;
411    if(g<0) g=0;
412    if(b<0) b=0;
413
414    paletten[(x<<4)+z].r=r;
415    paletten[(x<<4)+z].g=g;
416    paletten[(x<<4)+z].b=b;
417   }
418  WritePalette();
419 }
420
421 #include "drawing.h"
422 #ifdef FRAMESKIP
423 void FCEU_PutImageDummy(void)
424 {
425  if(FCEUGameInfo.type!=GIT_NSF)
426  {
427   if(controllength) controllength--;
428  }
429  if(StateShow) StateShow--; /* DrawState() */
430  if(howlong) howlong--; /* DrawMessage() */
431  #ifdef FPS
432  {
433   extern uint64 frcount;
434   frcount++;
435  }
436  #endif
437
438 }
439 #endif
440
441 void FCEU_PutImage(void)
442 {
443         if(FCEUGameInfo.type==GIT_NSF)
444         {
445          DrawNSF(XBuf);
446          /* Save snapshot after NSF screen is drawn.  Why would we want to
447             do it before?
448          */
449          if(dosnapsave)
450          {
451           ReallySnap();
452           dosnapsave=0;
453          }
454         }
455         else
456         {
457          /* Save snapshot before overlay stuff is written. */
458          if(dosnapsave)
459          {
460           ReallySnap();
461           dosnapsave=0;
462          }
463          if(FCEUGameInfo.type==GIT_VSUNI && DIPS&2)
464           DrawDips();
465          if(StateShow) DrawState();
466          if(controllength) {controllength--;DrawBars();}
467         }
468         DrawMessage();
469         #ifdef FPS
470         {
471         extern uint64 frcount;
472         frcount++;
473         }
474         #endif
475         DrawInput(XBuf+8);
476 }
477
478 static int ipalette=0;
479
480 void LoadGamePalette(void)
481 {
482   uint8 ptmp[192];
483   FILE *fp;
484   ipalette=0;
485   if((fp=fopen(FCEU_MakeFName(FCEUMKF_PALETTE,0,0),"rb")))
486   {
487    int x;
488    fread(ptmp,1,192,fp);
489    fclose(fp);
490    for(x=0;x<64;x++)
491    {
492     palettei[x].r=ptmp[x+x+x];
493     palettei[x].g=ptmp[x+x+x+1];
494     palettei[x].b=ptmp[x+x+x+2];
495    }
496    ipalette=1;
497   }
498 }
499
500 void FCEU_ResetPalette(void)
501 {
502    ChoosePalette();
503    WritePalette();
504 }
505
506 static void ChoosePalette(void)
507 {
508     if(FCEUGameInfo.type==GIT_NSF)
509      palo=NSFPalette;
510     else if(ipalette)
511      palo=palettei;
512     else if(ntsccol && !PAL && FCEUGameInfo.type!=GIT_VSUNI)
513      {
514       palo=paletten;
515       CalculatePalette();
516      }
517     else
518      palo=palpoint[pale];
519 }
520
521 void WritePalette(void)
522 {
523     int x;
524
525     for(x=0;x<6;x++)
526      FCEUD_SetPalette(x+128,unvpalette[x].r,unvpalette[x].g,unvpalette[x].b);
527     if(FCEUGameInfo.type==GIT_NSF)
528     {
529      for(x=0;x<39;x++)
530       FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b);
531     }
532     else
533     {
534      for(x=0;x<64;x++)
535       FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b);
536      SetNESDeemph(lastd,1);
537     }
538 }
539
540 void FlushCommandQueue(void)
541 {
542   if(!netplay && CommandQueue) {DoCommand(CommandQueue);CommandQueue=0;}
543 }
544
545 void DoCommand(uint8 c)
546 {
547  switch(c)
548  {
549   case 1:FDSControl(FDS_SELECT);break;
550   case 2:FDSControl(FDS_IDISK);break;
551   case 3:FDSControl(FDS_EJECT);break;
552
553   case 10:DIPS^=2;break;
554   case 11:vsdip^=1;DIPS|=2;break;
555   case 12:vsdip^=2;DIPS|=2;break;
556   case 13:vsdip^=4;DIPS|=2;break;
557   case 14:vsdip^=8;DIPS|=2;break;
558   case 15:vsdip^=0x10;DIPS|=2;break;
559   case 16:vsdip^=0x20;DIPS|=2;break;
560   case 17:vsdip^=0x40;DIPS|=2;break;
561   case 18:vsdip^=0x80;DIPS|=2;break;
562   case 19:coinon=6;break;
563   case 30:ResetNES();break;
564   case 31:PowerNES();break;
565   case 40:CheckStates();StateShow=0;SaveState();break;
566   case 41:CheckStates();StateShow=0;LoadState();break;
567   case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49:
568   case 50: case 51:StateShow=180;CurrentState=c-42;CheckStates();break;
569  }
570 }