1 |
/* |
2 |
* C64_Acorn.h - Put the pieces together, RISC OS specific stuff |
3 |
* |
4 |
* Frodo (C) 1994-1997,2002-2005 Christian Bauer |
5 |
* |
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 "Prefs.h" |
22 |
#include "ROlib.h" |
23 |
#include "AcornGUI.h" |
24 |
|
25 |
|
26 |
void C64::LoadSystemConfig(const char *filename) |
27 |
{ |
28 |
FILE *fp; |
29 |
|
30 |
if ((fp = fopen(filename, "r")) != NULL) |
31 |
{ |
32 |
int i; |
33 |
Joy_Keys *jk; |
34 |
int args[10]; |
35 |
char line[256]; |
36 |
|
37 |
while (fgets(line, 255, fp) != 0) |
38 |
{ |
39 |
char *b = line; |
40 |
register char c; |
41 |
|
42 |
do {c = *b++;} while (c > 32); |
43 |
if (c == 32) // keyword mustn't contain spaces |
44 |
{ |
45 |
*(b-1) = '\0'; |
46 |
do {c = *b++;} while ((c >= 32) && (c != '=')); |
47 |
if (c == '=') // read in keyword's arguments |
48 |
{ |
49 |
int i=0; |
50 |
|
51 |
while ((*b != '\0') && (i < 10)) |
52 |
{ |
53 |
args[i++] = strtol(b, &b, 10); |
54 |
} |
55 |
if (strcmp(line, "PollAfter") == 0) {PollAfter = args[0];} |
56 |
else if (strcmp(line, "SpeedAfter") == 0) {SpeedAfter = args[0];} |
57 |
else if (strcmp(line, "PollSoundAfter") == 0) {PollSoundAfter = args[0];} |
58 |
else if (strcmp(line, "JoystickKeys1") == 0) |
59 |
{ |
60 |
jk = &(TheDisplay->JoystickKeys[0]); |
61 |
jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; |
62 |
jk->fire = args[4]; |
63 |
} |
64 |
else if (strcmp(line, "JoystickKeys2") == 0) |
65 |
{ |
66 |
jk = &(TheDisplay->JoystickKeys[1]); |
67 |
jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; |
68 |
jk->fire = args[4]; |
69 |
} |
70 |
else |
71 |
{ |
72 |
_kernel_oserror err; |
73 |
|
74 |
err.errnum = 0; |
75 |
sprintf(err.errmess,"Bad keyword <%s> in system configure file!",line); |
76 |
Wimp_ReportError(&err,1,TASKNAME); |
77 |
} |
78 |
} |
79 |
} |
80 |
} |
81 |
fclose(fp); |
82 |
} |
83 |
} |
84 |
|
85 |
|
86 |
void C64::SaveSystemConfig(const char *filename) |
87 |
{ |
88 |
FILE *fp; |
89 |
|
90 |
if ((fp = fopen(filename, "w")) != NULL) |
91 |
{ |
92 |
int i; |
93 |
Joy_Keys *jk; |
94 |
|
95 |
fprintf(fp,"PollAfter = %d\n", PollAfter); |
96 |
fprintf(fp,"SpeedAfter = %d\n", SpeedAfter); |
97 |
fprintf(fp,"PollSoundAfter = %d\n", PollSoundAfter); |
98 |
for (i=0; i<2; i++) |
99 |
{ |
100 |
jk = &(TheDisplay->JoystickKeys[i]); |
101 |
fprintf(fp,"JoystickKeys%d",i+1); |
102 |
fprintf(fp," = %d %d %d %d %d\n", jk->up, jk->down, jk->left, jk->right, jk->fire); |
103 |
} |
104 |
fclose(fp); |
105 |
} |
106 |
} |
107 |
|
108 |
|
109 |
void C64::ReadTimings(int *poll_after, int *speed_after, int *sound_after) |
110 |
{ |
111 |
*poll_after = PollAfter; *speed_after = SpeedAfter; *sound_after = PollSoundAfter; |
112 |
} |
113 |
|
114 |
|
115 |
void C64::WriteTimings(int poll_after, int speed_after, int sound_after) |
116 |
{ |
117 |
PollAfter = poll_after; SpeedAfter = speed_after; PollSoundAfter = sound_after; |
118 |
} |
119 |
|
120 |
|
121 |
void C64::RequestSnapshot(void) |
122 |
{ |
123 |
// Snapshots are only possible if the emulation progresses to the next vsync |
124 |
if (have_a_break) Resume(); |
125 |
make_a_snapshot = true; |
126 |
} |
127 |
|
128 |
|
129 |
void C64::c64_ctor1(void) |
130 |
{ |
131 |
TheWIMP = new WIMP(this); |
132 |
PollAfter = 20; // poll every 20 centiseconds |
133 |
SpeedAfter = 200; // update speedometer every 2 seconds |
134 |
PollSoundAfter = 50; // poll DigitalRenderer every 50 lines |
135 |
HostVolume = Sound_Volume(0); |
136 |
// Just a precaution |
137 |
if (HostVolume < 0) {HostVolume = 0;} |
138 |
if (HostVolume > MaximumVolume) {HostVolume = MaximumVolume;} |
139 |
Poll = false; |
140 |
make_a_snapshot = false; |
141 |
} |
142 |
|
143 |
|
144 |
void C64::c64_ctor2(void) |
145 |
{ |
146 |
LoadSystemConfig(DEFAULT_SYSCONF); |
147 |
// was started from multitasking so pretend ScrollLock OFF no matter what |
148 |
laststate = (ReadKeyboardStatus() & ~2); SingleTasking = false; |
149 |
lastptr = 1; |
150 |
} |
151 |
|
152 |
|
153 |
void C64::c64_dtor(void) |
154 |
{ |
155 |
delete TheWIMP; |
156 |
} |
157 |
|
158 |
|
159 |
void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) |
160 |
{ |
161 |
// Check if the Joystick module is loaded. If not then write an illegal value to |
162 |
// the joystick state. |
163 |
if (Joystick_Read(0) == -2) {joystate[0] = 0;} else {joystate[0] = 0xff;} |
164 |
if (Joystick_Read(1) == -2) {joystate[1] = 0;} else {joystate[1] = 0xff;} |
165 |
} |
166 |
|
167 |
|
168 |
uint8 C64::poll_joystick(int port) |
169 |
{ |
170 |
register int state; |
171 |
uint8 joy; |
172 |
|
173 |
if ((state = Joystick_Read(port)) != -2) // module present |
174 |
{ |
175 |
if (state == -1) {joy = joystate[port];} // use old value |
176 |
else |
177 |
{ |
178 |
joy = 0xff; |
179 |
if ((state & (JoyButton1 + JoyButton2)) != 0) {joy &= 0xef;} // fire |
180 |
if ((state & 0x80) == 0) // positive direction #1 |
181 |
{ |
182 |
if ((state & 0xff) >= JoyDir_Thresh) {joy &= 0xfe;} // up |
183 |
} |
184 |
else |
185 |
{ |
186 |
if ((256 - (state & 0xff)) >= JoyDir_Thresh) {joy &= 0xfd;} // down |
187 |
} |
188 |
if ((state & 0x8000) == 0) // positive direction #2 |
189 |
{ |
190 |
if ((state & 0xff00) >= JoyDir_Thresh<<8) {joy &= 0xf7;} // right |
191 |
} |
192 |
else |
193 |
{ |
194 |
if ((0x10000 - (state & 0xff00)) >= JoyDir_Thresh<<8) {joy &= 0xfb;} // left |
195 |
} |
196 |
} |
197 |
joystate[port] = joy; return(joy); |
198 |
} |
199 |
else |
200 |
{ |
201 |
joystate[port] = 0; return(0xff); |
202 |
} |
203 |
} |
204 |
|
205 |
|
206 |
void C64::VBlank(bool draw_frame) |
207 |
{ |
208 |
int Now, KeyState; |
209 |
bool InputFocus; |
210 |
|
211 |
// Poll keyboard if the window has the input focus. |
212 |
InputFocus = TheWIMP->EmuWindow->HaveInput(); |
213 |
if (InputFocus) |
214 |
{ |
215 |
TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey, &joykey2); |
216 |
} |
217 |
|
218 |
// Poll Joysticks |
219 |
TheCIA1->Joystick1 = (ThePrefs.Joystick1On) ? poll_joystick(0) : 0xff; |
220 |
TheCIA1->Joystick2 = (ThePrefs.Joystick2On) ? poll_joystick(1) : 0xff; |
221 |
|
222 |
// Swap joysticks? |
223 |
if (ThePrefs.JoystickSwap) |
224 |
{ |
225 |
register uint8 h; |
226 |
|
227 |
h = TheCIA1->Joystick1; TheCIA1->Joystick1 = TheCIA1->Joystick2; TheCIA1->Joystick2 = h; |
228 |
} |
229 |
|
230 |
// Read keyboard state directly since we'll also need ScrollLock later! |
231 |
KeyState = ReadKeyboardStatus(); |
232 |
if (InputFocus) |
233 |
{ |
234 |
// Keyboard emulates which joystick? (NumLock ==> Port 2, else Port 1) |
235 |
if ((KeyState & 4) == 0) |
236 |
{ |
237 |
TheCIA1->Joystick2 &= joykey; |
238 |
} |
239 |
else // joykey2 only mapped if numLOCK is off. |
240 |
{ |
241 |
TheCIA1->Joystick1 &= joykey; TheCIA1->Joystick2 &= joykey2; |
242 |
} |
243 |
} |
244 |
|
245 |
if (draw_frame) |
246 |
{ |
247 |
TheDisplay->Update(); |
248 |
} |
249 |
|
250 |
// Make snapshot? |
251 |
if (make_a_snapshot) |
252 |
{ |
253 |
SaveSnapshot((TheWIMP->SnapFile)+44); |
254 |
make_a_snapshot = false; |
255 |
} |
256 |
|
257 |
Now = OS_ReadMonotonicTime(); |
258 |
|
259 |
// Limit speed? (hahaha.... ah well...) |
260 |
if (ThePrefs.LimitSpeed) |
261 |
{ |
262 |
int Now; |
263 |
|
264 |
while ((Now - LastFrame) < 2) // 2cs per frame = 50fps (original speed) |
265 |
{ |
266 |
Now = OS_ReadMonotonicTime(); |
267 |
} |
268 |
LastFrame = Now; |
269 |
} |
270 |
FramesSince++; |
271 |
|
272 |
// Update speedometer (update, not force redraw!)? |
273 |
if ((Now - LastSpeed) >= SpeedAfter) |
274 |
{ |
275 |
char b[16]; |
276 |
|
277 |
if ((Now - LastSpeed) <= 0) {Now = LastSpeed+1;} |
278 |
// Speed: 100% equals 50fps (round result) |
279 |
sprintf(b,"%d%%\0",((400*FramesSince)/(Now - LastSpeed) + 1) >> 1); |
280 |
TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Speed,b); |
281 |
LastSpeed = Now; FramesSince = 0; |
282 |
} |
283 |
|
284 |
if (InputFocus) |
285 |
{ |
286 |
// Scroll lock state changed? |
287 |
if (((KeyState ^ laststate) & 2) != 0) |
288 |
{ |
289 |
// change to single tasking: turn off mouse, else restore previous pointer |
290 |
if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0); SingleTasking = true;} |
291 |
else {SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false;} |
292 |
} |
293 |
if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0);} |
294 |
else {SetMousePointer(lastptr); OS_FlushBuffer(9);} |
295 |
} |
296 |
|
297 |
// Poll? ScrollLock forces single tasking, i.e. overrides timings. |
298 |
if (!SingleTasking) |
299 |
{ |
300 |
if ((Now - LastPoll) >= PollAfter) |
301 |
{ |
302 |
Poll = true; |
303 |
} |
304 |
} |
305 |
laststate = KeyState; |
306 |
} |
307 |
|
308 |
|
309 |
void C64::Run(void) |
310 |
{ |
311 |
// Resetting chips |
312 |
TheCPU->Reset(); |
313 |
TheSID->Reset(); |
314 |
TheCIA1->Reset(); |
315 |
TheCIA2->Reset(); |
316 |
TheCPU1541->Reset(); |
317 |
|
318 |
// Patch kernel IEC routines (copied from C64_Amiga.i |
319 |
orig_kernal_1d84 = Kernal[0x1d84]; |
320 |
orig_kernal_1d85 = Kernal[0x1d85]; |
321 |
PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); |
322 |
|
323 |
// Start the emulation |
324 |
thread_running = true; quit_thyself = false; have_a_break = false; |
325 |
thread_func(); |
326 |
} |
327 |
|
328 |
|
329 |
void C64::Quit(void) |
330 |
{ |
331 |
if (thread_running) |
332 |
{ |
333 |
quit_thyself = true; thread_running = false; |
334 |
} |
335 |
} |
336 |
|
337 |
|
338 |
void C64::Pause(void) |
339 |
{ |
340 |
have_a_break = true; TheSID->PauseSound(); |
341 |
} |
342 |
|
343 |
|
344 |
void C64::Resume(void) |
345 |
{ |
346 |
have_a_break = false; TheSID->ResumeSound(); |
347 |
} |
348 |
|
349 |
|
350 |
void C64::thread_func(void) |
351 |
{ |
352 |
LastPoll = LastFrame = LastSpeed = OS_ReadMonotonicTime(); FramesSince = 0; |
353 |
|
354 |
while (!quit_thyself) |
355 |
{ |
356 |
#ifdef FRODO_SC |
357 |
if (TheVIC->EmulateCycle()) {TheSID->EmulateLine();} |
358 |
TheCIA1->EmulateCycle(); |
359 |
TheCIA2->EmulateCycle(); |
360 |
TheCPU->EmulateCycle(); |
361 |
|
362 |
if (ThePrefs.Emul1541Proc) |
363 |
{ |
364 |
TheCPU1541->CountVIATimers(1); |
365 |
if (!TheCPU1541->Idle) {TheCPU1541->EmulateCycle();} |
366 |
} |
367 |
CycleCounter++; |
368 |
|
369 |
#else |
370 |
// Emulate each device one rasterline. Order is important! |
371 |
int cycles = TheVIC->EmulateLine(); |
372 |
TheSID->EmulateLine(); |
373 |
#if !PRECISE_CIA_CYCLES |
374 |
TheCIA1->EmulateLine(ThePrefs.CIACycles); |
375 |
TheCIA2->EmulateLine(ThePrefs.CIACycles); |
376 |
#endif |
377 |
|
378 |
if (ThePrefs.Emul1541Proc) |
379 |
{ |
380 |
int cycles_1541 = ThePrefs.FloppyCycles; |
381 |
TheCPU1541->CountVIATimers(cycles_1541); |
382 |
if (!TheCPU1541->Idle) |
383 |
{ |
384 |
while ((cycles >= 0) || (cycles_1541 >= 0)) |
385 |
{ |
386 |
if (cycles > cycles_1541) {cycles -= TheCPU->EmulateLine(1);} |
387 |
else {cycles_1541 -= TheCPU1541->EmulateLine(1);} |
388 |
} |
389 |
} |
390 |
else {TheCPU->EmulateLine(cycles);} |
391 |
} |
392 |
else |
393 |
{ |
394 |
TheCPU->EmulateLine(cycles); |
395 |
} |
396 |
#endif |
397 |
|
398 |
// Single-tasking: busy-wait 'til unpause |
399 |
while (SingleTasking && have_a_break) |
400 |
{ |
401 |
int KeyState; |
402 |
|
403 |
TheDisplay->CheckForUnpause(true); // unpause? |
404 |
KeyState = ReadKeyboardStatus(); |
405 |
if ((KeyState & 2) == 0) // leave single tasking? |
406 |
{ |
407 |
SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false; |
408 |
} |
409 |
laststate = KeyState; |
410 |
} |
411 |
if (!SingleTasking) |
412 |
{ |
413 |
// The system-specific part of this function |
414 |
if (Poll || have_a_break) |
415 |
{ |
416 |
TheWIMP->Poll(have_a_break); |
417 |
LastPoll = LastFrame = OS_ReadMonotonicTime(); Poll = false; |
418 |
} |
419 |
} |
420 |
} |
421 |
} |