initial fce ultra 0.81 import
[fceu.git] / svga.c
CommitLineData
c62d2810 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
52FCEUS FSettings;
53
54static int howlong;
55static char errmsg[65];
56
57void 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
70void 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
81void 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
100void FCEUI_SetVidSystem(int a)
101{
102 FSettings.PAL=a?1:0;
103 FCEU_ResetVidSys();
104 FCEU_ResetPalette();
105}
106
107int 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
117void FCEUI_SetNetworkPlay(int type)
118{
119 FSettings.NetworkPlay=type;
120}
121#endif
122
123void FCEUI_SetGameGenie(int a)
124{
125 FSettings.GameGenie=a?1:0;
126}
127
128static void CalculatePalette(void);
129static void ChoosePalette(void);
130static void WritePalette(void);
131
132#ifndef NETWORK
133#define netplay 0
134#endif
135
136static uint8 StateShow=0;
137
138uint8 Exit=0;
139
140uint8 DIPS=0;
141uint8 vsdip=0;
142int coinon=0;
143
144uint8 pale=0;
145uint8 CommandQueue=0;
146
147static int controlselect=0;
148static int ntsccol=0;
149static int ntsctint=46+10;
150static int ntschue=72;
151static int controllength=0;
152
153pal *palo;
154static pal *palpoint[8]=
155 {
156 palette,
157 palettevscv,
158 palettevssmb,
159 palettevsmar,
160 palettevsgoon,
161 palettevsslalom,
162 palettevseb,
163 rp2c04001
164 };
165
166void FCEUI_SetSnapName(int a)
167{
168 FSettings.SnapName=a;
169}
170
171void FCEUI_SaveExtraDataUnderBase(int a)
172{
173 FSettings.SUnderBase=a;
174}
175
176void 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
194void FCEUI_SelectState(int w)
195{
196 if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
197 CommandQueue=42+w;
198}
199
200void FCEUI_SaveState(void)
201{
202 if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
203 CommandQueue=40;
204}
205
206void FCEUI_LoadState(void)
207{
208 if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF)
209 CommandQueue=41;
210}
211
212int32 FCEUI_GetDesiredFPS(void)
213{
214 if(PAL)
215 return(838977920); // ~50.007
216 else
217 return(1008307711); // ~60.1
218}
219
220static int dosnapsave=0;
221void FCEUI_SaveSnapshot(void)
222{
223 dosnapsave=1;
224}
225
226/* I like the sounds of breaking necks. */
227static 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
236void 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
306static uint8 lastd=0;
307void 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 if(!d) return; /* No deemphasis, so return. */
345
346 r=rtmul[d-1];
347 g=gtmul[d-1];
348 b=btmul[d-1];
349
350 for(x=0;x<0x40;x++)
351 {
352 uint32 m,n,o;
353
354 m=palo[x].r;
355 n=palo[x].g;
356 o=palo[x].b;
357 m=(m*r)>>15;
358 n=(n*g)>>15;
359 o=(o*b)>>15;
360 if(m>0xff) m=0xff;
361 if(n>0xff) n=0xff;
362 if(o>0xff) o=0xff;
363
364 FCEUD_SetPalette(x|0xC0,m,n,o);
365 }
366
367 lastd=d;
368}
369
370#define HUEVAL ((double)((double)ntschue/(double)2)+(double)300)
371#define TINTVAL ((double)((double)ntsctint/(double)128))
372
373static void CalculatePalette(void)
374{
375 int x,z;
376 int r,g,b;
377 double s,y,theta;
378 static uint8 cols[16]={0,24,21,18,15,12,9,6,3,0,33,30,27,0,0,0};
379 static uint8 br1[4]={6,9,12,12};
380 static double br2[4]={.29,.45,.73,.9};
381 static double br3[4]={0,.24,.47,.77};
382
383 for(x=0;x<=3;x++)
384 for(z=0;z<16;z++)
385 {
386 s=(double)TINTVAL;
387 y=(double)br2[x];
388 if(z==0) {s=0;y=((double)br1[x])/12;}
389
390 if(z>=13)
391 {
392 s=y=0;
393 if(z==13)
394 y=(double)br3[x];
395 }
396
397 theta=(double)M_PI*(double)(((double)cols[z]*10+HUEVAL)/(double)180);
398 r=(int)(((double)y+(double)s*(double)sin(theta))*(double)256);
399 g=(int)(((double)y-(double)((double)27/(double)53)*s*(double)sin(theta)+(double)((double)10/(double)53)*s*cos(theta))*(double)256);
400 b=(int)(((double)y-(double)s*(double)cos(theta))*(double)256);
401
402 // TODO: Fix RGB to compensate for phosphor changes(add to red??).
403
404 if(r>255) r=255;
405 if(g>255) g=255;
406 if(b>255) b=255;
407 if(r<0) r=0;
408 if(g<0) g=0;
409 if(b<0) b=0;
410
411 paletten[(x<<4)+z].r=r;
412 paletten[(x<<4)+z].g=g;
413 paletten[(x<<4)+z].b=b;
414 }
415 WritePalette();
416}
417
418#include "drawing.h"
419#ifdef FRAMESKIP
420void FCEU_PutImageDummy(void)
421{
422 if(FCEUGameInfo.type!=GIT_NSF)
423 {
424 if(controllength) controllength--;
425 }
426 if(StateShow) StateShow--; /* DrawState() */
427 if(howlong) howlong--; /* DrawMessage() */
428 #ifdef FPS
429 {
430 extern uint64 frcount;
431 frcount++;
432 }
433 #endif
434
435}
436#endif
437
438void FCEU_PutImage(void)
439{
440 if(FCEUGameInfo.type==GIT_NSF)
441 {
442 DrawNSF(XBuf);
443 /* Save snapshot after NSF screen is drawn. Why would we want to
444 do it before?
445 */
446 if(dosnapsave)
447 {
448 ReallySnap();
449 dosnapsave=0;
450 }
451 }
452 else
453 {
454 /* Save snapshot before overlay stuff is written. */
455 if(dosnapsave)
456 {
457 ReallySnap();
458 dosnapsave=0;
459 }
460 if(FCEUGameInfo.type==GIT_VSUNI && DIPS&2)
461 DrawDips();
462 if(StateShow) DrawState();
463 if(controllength) {controllength--;DrawBars();}
464 }
465 DrawMessage();
466 #ifdef FPS
467 {
468 extern uint64 frcount;
469 frcount++;
470 }
471 #endif
472 DrawInput(XBuf+8);
473}
474
475static int ipalette=0;
476
477void LoadGamePalette(void)
478{
479 uint8 ptmp[192];
480 FILE *fp;
481 ipalette=0;
482 if((fp=fopen(FCEU_MakeFName(FCEUMKF_PALETTE,0,0),"rb")))
483 {
484 int x;
485 fread(ptmp,1,192,fp);
486 fclose(fp);
487 for(x=0;x<64;x++)
488 {
489 palettei[x].r=ptmp[x+x+x];
490 palettei[x].g=ptmp[x+x+x+1];
491 palettei[x].b=ptmp[x+x+x+2];
492 }
493 ipalette=1;
494 }
495}
496
497void FCEU_ResetPalette(void)
498{
499 ChoosePalette();
500 WritePalette();
501}
502
503static void ChoosePalette(void)
504{
505 if(FCEUGameInfo.type==GIT_NSF)
506 palo=NSFPalette;
507 else if(ipalette)
508 palo=palettei;
509 else if(ntsccol && !PAL && FCEUGameInfo.type!=GIT_VSUNI)
510 {
511 palo=paletten;
512 CalculatePalette();
513 }
514 else
515 palo=palpoint[pale];
516}
517
518void WritePalette(void)
519{
520 int x;
521
522 for(x=0;x<6;x++)
523 FCEUD_SetPalette(x+128,unvpalette[x].r,unvpalette[x].g,unvpalette[x].b);
524 if(FCEUGameInfo.type==GIT_NSF)
525 {
526 for(x=0;x<39;x++)
527 FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b);
528 }
529 else
530 {
531 for(x=0;x<64;x++)
532 FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b);
533 SetNESDeemph(lastd,1);
534 }
535}
536
537void FlushCommandQueue(void)
538{
539 if(!netplay && CommandQueue) {DoCommand(CommandQueue);CommandQueue=0;}
540}
541
542void DoCommand(uint8 c)
543{
544 switch(c)
545 {
546 case 1:FDSControl(FDS_SELECT);break;
547 case 2:FDSControl(FDS_IDISK);break;
548 case 3:FDSControl(FDS_EJECT);break;
549
550 case 10:DIPS^=2;break;
551 case 11:vsdip^=1;DIPS|=2;break;
552 case 12:vsdip^=2;DIPS|=2;break;
553 case 13:vsdip^=4;DIPS|=2;break;
554 case 14:vsdip^=8;DIPS|=2;break;
555 case 15:vsdip^=0x10;DIPS|=2;break;
556 case 16:vsdip^=0x20;DIPS|=2;break;
557 case 17:vsdip^=0x40;DIPS|=2;break;
558 case 18:vsdip^=0x80;DIPS|=2;break;
559 case 19:coinon=6;break;
560 case 30:ResetNES();break;
561 case 31:PowerNES();break;
562 case 40:CheckStates();StateShow=0;SaveState();break;
563 case 41:CheckStates();StateShow=0;LoadState();break;
564 case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49:
565 case 50: case 51:StateShow=180;CurrentState=c-42;CheckStates();break;
566 }
567}