first round of patches
[drnoksnes] / platform / sdl.cpp
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/time.h>
4 #include <time.h>
5 #include <SDL.h>
6
7 #include "platform.h"
8 #include "snes9x.h"
9 #include "gfx.h"
10 #include "display.h"
11 #include "memmap.h"
12 #include "soundux.h"
13 #include "hacks.h"
14
15 #define kPollEveryNFrames       4               //Poll input only every this many frames
16
17 #define TRACE printf("trace: %s:%s\n", __FILE__, __func__);
18 #define DIE(format, ...) do { \
19                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
20                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
21                 abort(); \
22         } while (0);
23
24 void S9xMessage(int type, int number, const char * message)
25 {
26         printf("%s\n", message);
27 }
28
29 void S9xLoadSDD1Data()
30 {TRACE
31         Settings.SDD1Pack=FALSE;
32 }
33
34 static void S9xInit() 
35 {
36         if (!Memory.Init () || !S9xInitAPU())
37          DIE("Memory or APU failed");
38
39         if (!S9xInitSound ())
40                 DIE("Sound failed");
41         S9xSetSoundMute (TRUE);
42         
43         // TODO: PAL/NTSC something better than this
44         Settings.PAL = Settings.ForcePAL;
45         
46         Settings.FrameTime = Settings.PAL?Settings.FrameTimePAL:Settings.FrameTimeNTSC;
47         Memory.ROMFramesPerSecond = Settings.PAL?50:60;
48         
49         IPPU.RenderThisFrame = TRUE;
50 }
51
52 static void loadRom()
53 {
54         const char * file = S9xGetFilename(".smc");
55
56         printf("ROM: %s\n", file);
57
58         if (!Memory.LoadROM(file))
59                 DIE("Loading ROM failed");
60         
61         file = S9xGetFilename(".srm");
62         printf("SRAM: %s\n", file);
63         Memory.LoadSRAM(file); 
64 }
65
66 /* This comes nearly straight from snes9x */
67 static void frameSync() {
68         static struct timeval next1 = {0, 0};
69         struct timeval now;
70         
71         if (Settings.TurboMode)
72         {
73                 if(Settings.SkipFrames == AUTO_FRAMERATE || 
74                         ++IPPU.FrameSkip >= Settings.SkipFrames)
75                 {
76                         IPPU.FrameSkip = 0;
77                         IPPU.SkippedFrames = 0;
78                         IPPU.RenderThisFrame = TRUE;
79                 }
80                 else
81                 {
82                         ++IPPU.SkippedFrames;
83                         IPPU.RenderThisFrame = FALSE;
84                 }
85                 return;
86         }
87         
88         /* Normal mode */
89         
90         while (gettimeofday(&now, 0) < 0);
91         
92         /* If there is no known "next" frame, initialize it now */
93         if (next1.tv_sec == 0) { next1 = now; ++next1.tv_usec; }
94         
95     /* If we're on AUTO_FRAMERATE, we'll display frames always
96      * only if there's excess time.
97      * Otherwise we'll display the defined amount of frames.
98      */
99     unsigned limit = Settings.SkipFrames == AUTO_FRAMERATE
100                      ? (timercmp(&next1, &now, <) ? 10 : 1)
101                      : Settings.SkipFrames;
102                      
103     IPPU.RenderThisFrame = ++IPPU.SkippedFrames >= limit;
104     if(IPPU.RenderThisFrame)
105     {
106         IPPU.SkippedFrames = 0;
107     }
108     else
109     {
110         /* If we were behind the schedule, check how much it is */
111         if(timercmp(&next1, &now, <))
112         {
113             unsigned lag =
114                 (now.tv_sec - next1.tv_sec) * 1000000
115                + now.tv_usec - next1.tv_usec;
116             if(lag >= 500000)
117             {
118                 /* More than a half-second behind means probably
119                  * pause. The next line prevents the magic
120                  * fast-forward effect.
121                  */
122                 next1 = now;
123             }
124         }
125     }
126     
127     /* Delay until we're completed this frame */
128
129     /* Can't use setitimer because the sound code already could
130      * be using it. We don't actually need it either.
131      */
132
133     while(timercmp(&next1, &now, >))
134     {
135         /* If we're ahead of time, sleep a while */
136         unsigned timeleft =
137             (next1.tv_sec - now.tv_sec) * 1000000
138            + next1.tv_usec - now.tv_usec;
139
140         usleep(timeleft);
141
142         // XXX : CHECK_SOUND(); S9xProcessEvents(FALSE);
143
144         while (gettimeofday(&now, 0) < 0);
145         /* Continue with a while-loop because usleep()
146          * could be interrupted by a signal
147          */
148     }
149
150     /* Calculate the timestamp of the next frame. */
151     next1.tv_usec += Settings.FrameTime;
152     if (next1.tv_usec >= 1000000)
153     {
154         next1.tv_sec += next1.tv_usec / 1000000;
155         next1.tv_usec %= 1000000;
156     }
157 }
158
159 /** Wraps s9xProcessEvents, taking care of kPollEveryNFrames */
160 static inline void pollEvents() {
161         static int frames = 0;
162         
163         if (++frames > kPollEveryNFrames) {
164                 S9xProcessEvents(FALSE);
165                 frames = 0;
166         }
167 }
168
169 int main(int argc, const char ** argv) {        
170         // Initialise SDL
171         if (SDL_Init(0) < 0) 
172                 DIE("SDL_Init: %s", SDL_GetError());
173         
174         // Configure snes9x
175         S9xLoadConfig(argc, argv);
176         
177         // S9x initialization
178         S9xInitDisplay(argc, argv);
179         S9xInitAudioOutput();
180         S9xInitInputDevices();
181         S9xInit();
182         S9xReset();
183         
184         // Load rom and related files
185         loadRom();
186         
187         // Late initialization
188         sprintf(String, "DrNokSnes - %s", Memory.ROMName);
189         S9xSetTitle(String);
190         S9xHacksLoadFile(Config.hacksFile[0] ? Config.hacksFile : 0);
191         if (!S9xGraphicsInit())
192          DIE("S9xGraphicsInit failed");
193         S9xAudioOutputEnable(true);
194
195         do {
196                 frameSync();                    // May block, or set frameskip to true.
197                 S9xMainLoop();                  // Does CPU things, renders if needed.
198                 pollEvents();
199         } while (!Config.quitting);
200         
201         // Deinitialization
202         S9xAudioOutputEnable(false);
203         S9xDeinitAudioOutput();
204         S9xDeinitDisplay();
205         S9xGraphicsDeinit();
206
207         SDL_Quit();
208
209         return 0;
210 }
211
212 void S9xDoAction(unsigned char action)
213 {
214         if (action & kActionQuit) 
215                 Config.quitting = true;
216                 
217         if (action & kActionToggleFullscreen)
218                 S9xVideoToggleFullscreen();
219 }
220