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