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