ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/C64_x.h
Revision: 1.6
Committed: 2004-01-10T18:10:56Z (20 years, 10 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.5: +22 -1 lines
Log Message:
more accurate timing under Linux

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * C64_x.h - Put the pieces together, X specific stuff
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "main.h"
22    
23    
24     static struct timeval tv_start;
25    
26     #ifndef HAVE_USLEEP
27     /*
28     * NAME:
29     * usleep -- This is the precision timer for Test Set
30     * Automation. It uses the select(2) system
31     * call to delay for the desired number of
32     * micro-seconds. This call returns ZERO
33     * (which is usually ignored) on successful
34     * completion, -1 otherwise.
35     *
36     * ALGORITHM:
37     * 1) We range check the passed in microseconds and log a
38     * warning message if appropriate. We then return without
39     * delay, flagging an error.
40     * 2) Load the Seconds and micro-seconds portion of the
41     * interval timer structure.
42     * 3) Call select(2) with no file descriptors set, just the
43     * timer, this results in either delaying the proper
44     * ammount of time or being interupted early by a signal.
45     *
46     * HISTORY:
47     * Added when the need for a subsecond timer was evident.
48     *
49     * AUTHOR:
50     * Michael J. Dyer Telephone: AT&T 414.647.4044
51     * General Electric Medical Systems GE DialComm 8 *767.4044
52     * P.O. Box 414 Mail Stop 12-27 Sect'y AT&T 414.647.4584
53     * Milwaukee, Wisconsin USA 53201 8 *767.4584
54     * internet: mike@sherlock.med.ge.com GEMS WIZARD e-mail: DYER
55     */
56    
57     #include <unistd.h>
58     #include <stdlib.h>
59     #include <stdio.h>
60     #include <errno.h>
61     #include <time.h>
62     #include <sys/time.h>
63     #include <sys/param.h>
64     #include <sys/types.h>
65    
66     int usleep(unsigned long int microSeconds)
67     {
68     unsigned int Seconds, uSec;
69     int nfds, readfds, writefds, exceptfds;
70     struct timeval Timer;
71    
72     nfds = readfds = writefds = exceptfds = 0;
73    
74     if( (microSeconds == (unsigned long) 0)
75     || microSeconds > (unsigned long) 4000000 )
76     {
77     errno = ERANGE; /* value out of range */
78     perror( "usleep time out of range ( 0 -> 4000000 ) " );
79     return -1;
80     }
81    
82     Seconds = microSeconds / (unsigned long) 1000000;
83     uSec = microSeconds % (unsigned long) 1000000;
84    
85     Timer.tv_sec = Seconds;
86     Timer.tv_usec = uSec;
87    
88     if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 )
89     {
90     perror( "usleep (select) failed" );
91     return -1;
92     }
93    
94     return 0;
95     }
96     #endif
97    
98 cebix 1.6 #ifdef __linux__
99     // select() timing is much more accurate under Linux
100     static void Delay_usec(unsigned long usec)
101     {
102     int was_error;
103     struct timeval tv;
104    
105     tv.tv_sec = 0;
106     tv.tv_usec = usec;
107     do {
108     errno = 0;
109     was_error = select(0, NULL, NULL, NULL, &tv);
110     } while (was_error && (errno == EINTR));
111     }
112     #else
113     static void Delay_usec(unsigned long usec)
114     {
115     usleep(usec);
116     }
117     #endif
118    
119 cebix 1.1
120     /*
121     * Constructor, system-dependent things
122     */
123    
124     void C64::c64_ctor1(void)
125     {
126     // Initialize joystick variables
127 cebix 1.5 joy_minx[0] = joy_miny[0] = 32767;
128     joy_maxx[0] = joy_maxy[0] = -32768;
129     joy_minx[1] = joy_miny[1] = 32767;
130     joy_maxx[1] = joy_maxy[1] = -32768;
131 cebix 1.1
132     // we need to create a potential GUI subprocess here, because we don't want
133     // it to inherit file-descriptors (such as for the audio-device and alike..)
134     #if defined(__svgalib__)
135     gui = 0;
136     #else
137     // try to start up Tk gui.
138 cebix 1.3 gui = new CmdPipe("wish", BINDIR "Frodo_GUI.tcl");
139 cebix 1.1 if (gui) {
140     if (gui->fail) {
141     delete gui; gui = 0;
142     }
143     }
144     // wait until the GUI process responds (if it does...)
145     if (gui) {
146     if (5 != gui->ewrite("ping\n",5)) {
147     delete gui; gui = 0;
148     } else {
149     char c;
150     fd_set set;
151     FD_ZERO(&set);
152     FD_SET(gui->get_read_fd(), &set);
153     struct timeval tv;
154     tv.tv_usec = 0;
155     tv.tv_sec = 5;
156     // Use the following commented line for HP-UX < 10.20
157     // if (select(FD_SETSIZE, (int *)&set, (int *)NULL, (int *)NULL, &tv) <= 0) {
158     if (select(FD_SETSIZE, &set, NULL, NULL, &tv) <= 0) {
159     delete gui; gui = 0;
160     } else {
161     if (1 != gui->eread(&c, 1)) {
162     delete gui; gui = 0;
163     } else {
164     if (c != 'o') {
165     delete gui; gui = 0;
166     }
167     }
168     }
169     }
170     }
171     #endif // __svgalib__
172     }
173    
174     void C64::c64_ctor2(void)
175     {
176     #ifndef __svgalib__
177     if (!gui) {
178     fprintf(stderr,"Alas, master, no preferences window will be available.\n"
179     "If you wish to see one, make sure the 'wish' interpreter\n"
180     "(Tk version >= 4.1) is installed in your path.\n"
181     "You can still use Frodo, though. Use F10 to quit, \n"
182     "F11 to cause an NMI and F12 to reset the C64.\n"
183     "You can change the preferences by editing ~/.frodorc\n");
184     }
185     #endif // SVGAlib
186    
187     gettimeofday(&tv_start, NULL);
188     }
189    
190    
191     /*
192     * Destructor, system-dependent things
193     */
194    
195     void C64::c64_dtor(void)
196     {
197     }
198    
199    
200     /*
201     * Start main emulation thread
202     */
203    
204     void C64::Run(void)
205     {
206     // Reset chips
207     TheCPU->Reset();
208     TheSID->Reset();
209     TheCIA1->Reset();
210     TheCIA2->Reset();
211     TheCPU1541->Reset();
212    
213     // Patch kernal IEC routines
214     orig_kernal_1d84 = Kernal[0x1d84];
215     orig_kernal_1d85 = Kernal[0x1d85];
216     PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc);
217    
218     quit_thyself = false;
219     thread_func();
220     }
221    
222    
223     /*
224     * Vertical blank: Poll keyboard and joysticks, update window
225     */
226    
227     void C64::VBlank(bool draw_frame)
228     {
229     // Poll keyboard
230     TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey);
231     if (TheDisplay->quit_requested)
232     quit_thyself = true;
233    
234     // Poll joysticks
235     TheCIA1->Joystick1 = poll_joystick(0);
236     TheCIA1->Joystick2 = poll_joystick(1);
237    
238     if (ThePrefs.JoystickSwap) {
239     uint8 tmp = TheCIA1->Joystick1;
240     TheCIA1->Joystick1 = TheCIA1->Joystick2;
241     TheCIA1->Joystick2 = tmp;
242     }
243    
244     // Joystick keyboard emulation
245     if (TheDisplay->NumLock())
246     TheCIA1->Joystick1 &= joykey;
247     else
248     TheCIA1->Joystick2 &= joykey;
249    
250     // Count TOD clocks
251     TheCIA1->CountTOD();
252     TheCIA2->CountTOD();
253    
254     // Update window if needed
255     if (draw_frame) {
256     TheDisplay->Update();
257    
258     // Calculate time between VBlanks, display speedometer
259     struct timeval tv;
260     gettimeofday(&tv, NULL);
261     if ((tv.tv_usec -= tv_start.tv_usec) < 0) {
262     tv.tv_usec += 1000000;
263     tv.tv_sec -= 1;
264     }
265     tv.tv_sec -= tv_start.tv_sec;
266     double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec;
267     speed_index = 20000 / (elapsed_time + 1) * ThePrefs.SkipFrames * 100;
268    
269     // Limit speed to 100% if desired
270     if ((speed_index > 100) && ThePrefs.LimitSpeed) {
271 cebix 1.6 Delay_usec((unsigned long)(ThePrefs.SkipFrames * 20000 - elapsed_time));
272 cebix 1.1 speed_index = 100;
273     }
274    
275     gettimeofday(&tv_start, NULL);
276    
277     TheDisplay->Speedometer((int)speed_index);
278     }
279     }
280    
281    
282     /*
283     * The emulation's main loop
284     */
285    
286     void C64::thread_func(void)
287     {
288     int linecnt = 0;
289    
290     #ifdef FRODO_SC
291     while (!quit_thyself) {
292    
293     // The order of calls is important here
294     if (TheVIC->EmulateCycle())
295     TheSID->EmulateLine();
296     TheCIA1->CheckIRQs();
297     TheCIA2->CheckIRQs();
298     TheCIA1->EmulateCycle();
299     TheCIA2->EmulateCycle();
300     TheCPU->EmulateCycle();
301    
302     if (ThePrefs.Emul1541Proc) {
303     TheCPU1541->CountVIATimers(1);
304     if (!TheCPU1541->Idle)
305     TheCPU1541->EmulateCycle();
306     }
307     CycleCounter++;
308     #else
309     while (!quit_thyself) {
310    
311     // The order of calls is important here
312     int cycles = TheVIC->EmulateLine();
313     TheSID->EmulateLine();
314     #if !PRECISE_CIA_CYCLES
315     TheCIA1->EmulateLine(ThePrefs.CIACycles);
316     TheCIA2->EmulateLine(ThePrefs.CIACycles);
317     #endif
318    
319     if (ThePrefs.Emul1541Proc) {
320     int cycles_1541 = ThePrefs.FloppyCycles;
321     TheCPU1541->CountVIATimers(cycles_1541);
322    
323     if (!TheCPU1541->Idle) {
324     // 1541 processor active, alternately execute
325     // 6502 and 6510 instructions until both have
326     // used up their cycles
327     while (cycles >= 0 || cycles_1541 >= 0)
328     if (cycles > cycles_1541)
329     cycles -= TheCPU->EmulateLine(1);
330     else
331     cycles_1541 -= TheCPU1541->EmulateLine(1);
332     } else
333     TheCPU->EmulateLine(cycles);
334     } else
335     // 1541 processor disabled, only emulate 6510
336     TheCPU->EmulateLine(cycles);
337     #endif
338     linecnt++;
339     #if !defined(__svgalib__)
340     if ((linecnt & 0xfff) == 0 && gui) {
341    
342     // check for command from GUI process
343     // fprintf(stderr,":");
344     while (gui->probe()) {
345     char c;
346     if (gui->eread(&c, 1) != 1) {
347     delete gui;
348     gui = 0;
349     fprintf(stderr,"Oops, GUI process died...\n");
350     } else {
351     // fprintf(stderr,"%c",c);
352     switch (c) {
353     case 'q':
354     quit_thyself = true;
355     break;
356     case 'r':
357     Reset();
358     break;
359     case 'p':{
360     Prefs *np = Frodo::reload_prefs();
361     NewPrefs(np);
362     ThePrefs = *np;
363     break;
364     }
365     default:
366     break;
367     }
368     }
369     }
370     }
371     #endif
372     }
373     #if !defined(__svgalib__)
374     if (gui) {
375     gui->ewrite("quit\n",5);
376     }
377     #endif
378     }