1 |
cebix |
1.1 |
/* |
2 |
|
|
* main_beos.cpp - Startup code for BeOS |
3 |
|
|
* |
4 |
|
|
* Basilisk II (C) 1997-1999 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 <AppKit.h> |
22 |
|
|
#include <InterfaceKit.h> |
23 |
|
|
#include <KernelKit.h> |
24 |
|
|
#include <StorageKit.h> |
25 |
|
|
|
26 |
|
|
#include <unistd.h> |
27 |
|
|
#include <fcntl.h> |
28 |
|
|
#include <signal.h> |
29 |
|
|
#include <stdio.h> |
30 |
|
|
#include <stdlib.h> |
31 |
|
|
#include <time.h> |
32 |
|
|
|
33 |
|
|
#include "sysdeps.h" |
34 |
|
|
#include "cpu_emulation.h" |
35 |
|
|
#include "xpram.h" |
36 |
|
|
#include "timer.h" |
37 |
|
|
#include "sony.h" |
38 |
|
|
#include "disk.h" |
39 |
|
|
#include "cdrom.h" |
40 |
|
|
#include "scsi.h" |
41 |
|
|
#include "audio.h" |
42 |
|
|
#include "video.h" |
43 |
|
|
#include "serial.h" |
44 |
|
|
#include "ether.h" |
45 |
|
|
#include "clip.h" |
46 |
|
|
#include "rom_patches.h" |
47 |
|
|
#include "prefs.h" |
48 |
|
|
#include "prefs_editor.h" |
49 |
|
|
#include "sys.h" |
50 |
|
|
#include "user_strings.h" |
51 |
|
|
#include "version.h" |
52 |
|
|
#include "main.h" |
53 |
|
|
|
54 |
|
|
#include "sheep_driver.h" |
55 |
|
|
|
56 |
|
|
#define DEBUG 0 |
57 |
|
|
#include "debug.h" |
58 |
|
|
|
59 |
|
|
|
60 |
|
|
// Constants |
61 |
|
|
const char APP_SIGNATURE[] = "application/x-vnd.cebix-BasiliskII"; |
62 |
|
|
const char ROM_FILE_NAME[] = "ROM"; |
63 |
|
|
const char RAM_AREA_NAME[] = "Macintosh RAM"; |
64 |
|
|
const char ROM_AREA_NAME[] = "Macintosh ROM"; |
65 |
|
|
const uint32 MSG_START = 'strt'; // Emulator start message |
66 |
|
|
const uint32 ROM_AREA_SIZE = 0x500000; // Enough to hold PowerMac ROM (for powerrom_cpu) |
67 |
|
|
|
68 |
|
|
// Prototypes |
69 |
|
|
#if __POWERPC__ |
70 |
|
|
static void sigsegv_handler(vregs *r); |
71 |
|
|
#endif |
72 |
|
|
|
73 |
|
|
|
74 |
|
|
// Application object |
75 |
|
|
class BasiliskII : public BApplication { |
76 |
|
|
public: |
77 |
|
|
BasiliskII() : BApplication(APP_SIGNATURE) |
78 |
|
|
{ |
79 |
|
|
// Find application directory and cwd to it |
80 |
|
|
app_info the_info; |
81 |
|
|
GetAppInfo(&the_info); |
82 |
|
|
BEntry the_file(&the_info.ref); |
83 |
|
|
BEntry the_dir; |
84 |
|
|
the_file.GetParent(&the_dir); |
85 |
|
|
BPath the_path; |
86 |
|
|
the_dir.GetPath(&the_path); |
87 |
|
|
chdir(the_path.Path()); |
88 |
|
|
|
89 |
|
|
// Initialize other variables |
90 |
|
|
rom_area = ram_area = -1; |
91 |
|
|
xpram_thread = tick_thread = -1; |
92 |
|
|
xpram_thread_active = true; |
93 |
|
|
tick_thread_active = true; |
94 |
|
|
AllowQuitting = true; |
95 |
|
|
} |
96 |
|
|
virtual void ReadyToRun(void); |
97 |
|
|
virtual void MessageReceived(BMessage *msg); |
98 |
|
|
void StartEmulator(void); |
99 |
|
|
virtual bool QuitRequested(void); |
100 |
|
|
virtual void Quit(void); |
101 |
|
|
|
102 |
|
|
thread_id xpram_thread; // XPRAM watchdog |
103 |
|
|
thread_id tick_thread; // 60Hz thread |
104 |
|
|
|
105 |
|
|
volatile bool xpram_thread_active; // Flag for quitting the XPRAM thread |
106 |
|
|
volatile bool tick_thread_active; // Flag for quitting the 60Hz thread |
107 |
|
|
|
108 |
|
|
bool AllowQuitting; // Flag: Alt-Q quitting allowed |
109 |
|
|
|
110 |
|
|
private: |
111 |
|
|
static status_t emul_func(void *arg); |
112 |
|
|
static status_t tick_func(void *arg); |
113 |
|
|
static status_t xpram_func(void *arg); |
114 |
|
|
static void sigsegv_invoc(int sig, void *arg, vregs *r); |
115 |
|
|
|
116 |
|
|
void init_rom(void); |
117 |
|
|
void load_rom(void); |
118 |
|
|
|
119 |
|
|
area_id rom_area; // ROM area ID |
120 |
|
|
area_id ram_area; // RAM area ID |
121 |
|
|
|
122 |
|
|
struct sigaction sigsegv_action; // Data access exception signal (of emulator thread) |
123 |
|
|
|
124 |
|
|
// Exceptions |
125 |
|
|
class area_error {}; |
126 |
|
|
class file_open_error {}; |
127 |
|
|
class file_read_error {}; |
128 |
|
|
class rom_size_error {}; |
129 |
|
|
}; |
130 |
|
|
|
131 |
|
|
static BasiliskII *the_app; |
132 |
|
|
|
133 |
|
|
|
134 |
|
|
// CPU and FPU type, addressing mode |
135 |
|
|
int CPUType; |
136 |
|
|
bool CPUIs68060; |
137 |
|
|
int FPUType; |
138 |
|
|
bool TwentyFourBitAddressing; |
139 |
|
|
|
140 |
|
|
|
141 |
|
|
// Global variables for PowerROM CPU |
142 |
|
|
thread_id emul_thread = -1; // Emulator thread |
143 |
|
|
|
144 |
|
|
#if __POWERPC__ |
145 |
|
|
int sheep_fd = -1; // fd of sheep driver |
146 |
|
|
#endif |
147 |
|
|
|
148 |
|
|
|
149 |
|
|
/* |
150 |
|
|
* Create application object and start it |
151 |
|
|
*/ |
152 |
|
|
|
153 |
|
|
int main(int argc, char **argv) |
154 |
|
|
{ |
155 |
|
|
the_app = new BasiliskII(); |
156 |
|
|
the_app->Run(); |
157 |
|
|
delete the_app; |
158 |
|
|
return 0; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
|
162 |
|
|
/* |
163 |
|
|
* Run application |
164 |
|
|
*/ |
165 |
|
|
|
166 |
|
|
void BasiliskII::ReadyToRun(void) |
167 |
|
|
{ |
168 |
|
|
// Initialize variables |
169 |
|
|
RAMBaseHost = NULL; |
170 |
|
|
ROMBaseHost = NULL; |
171 |
|
|
srand(real_time_clock()); |
172 |
|
|
tzset(); |
173 |
|
|
|
174 |
|
|
// Print some info |
175 |
|
|
printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR); |
176 |
|
|
printf(" %s\n", GetString(STR_ABOUT_TEXT2)); |
177 |
|
|
|
178 |
|
|
// Delete old areas |
179 |
|
|
area_id old_ram_area = find_area(RAM_AREA_NAME); |
180 |
|
|
if (old_ram_area > 0) |
181 |
|
|
delete_area(old_ram_area); |
182 |
|
|
area_id old_rom_area = find_area(ROM_AREA_NAME); |
183 |
|
|
if (old_rom_area > 0) |
184 |
|
|
delete_area(old_rom_area); |
185 |
|
|
|
186 |
|
|
// Read preferences |
187 |
|
|
PrefsInit(); |
188 |
|
|
|
189 |
|
|
// Init system routines |
190 |
|
|
SysInit(); |
191 |
|
|
|
192 |
|
|
// Show preferences editor (or start emulator directly) |
193 |
|
|
if (!PrefsFindBool("nogui")) |
194 |
|
|
PrefsEditor(); |
195 |
|
|
else |
196 |
|
|
PostMessage(MSG_START); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
|
200 |
|
|
/* |
201 |
|
|
* Message received |
202 |
|
|
*/ |
203 |
|
|
|
204 |
|
|
void BasiliskII::MessageReceived(BMessage *msg) |
205 |
|
|
{ |
206 |
|
|
switch (msg->what) { |
207 |
|
|
case MSG_START: |
208 |
|
|
StartEmulator(); |
209 |
|
|
break; |
210 |
|
|
default: |
211 |
|
|
BApplication::MessageReceived(msg); |
212 |
|
|
} |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
|
216 |
|
|
/* |
217 |
|
|
* Start emulator |
218 |
|
|
*/ |
219 |
|
|
|
220 |
|
|
void BasiliskII::StartEmulator(void) |
221 |
|
|
{ |
222 |
|
|
char str[256]; |
223 |
|
|
|
224 |
|
|
#if REAL_ADDRESSING |
225 |
|
|
// Open memory mess driver and remap low memory |
226 |
|
|
sheep_fd = open("/dev/sheep", 0); |
227 |
|
|
if (sheep_fd < 0) { |
228 |
|
|
sprintf(str, GetString(STR_NO_SHEEP_DRIVER_ERR), strerror(sheep_fd), sheep_fd); |
229 |
|
|
ErrorAlert(str); |
230 |
|
|
PostMessage(B_QUIT_REQUESTED); |
231 |
|
|
return; |
232 |
|
|
} |
233 |
|
|
status_t res = ioctl(sheep_fd, SHEEP_UP); |
234 |
|
|
if (res < 0) { |
235 |
|
|
sprintf(str, GetString(STR_SHEEP_UP_ERR), strerror(res), res); |
236 |
|
|
ErrorAlert(str); |
237 |
|
|
PostMessage(B_QUIT_REQUESTED); |
238 |
|
|
return; |
239 |
|
|
} |
240 |
|
|
#endif |
241 |
|
|
|
242 |
|
|
// Create area for Mac RAM |
243 |
|
|
RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary |
244 |
|
|
if (RAMSize < 1024*1024) { |
245 |
|
|
WarningAlert(GetString(STR_SMALL_RAM_WARN)); |
246 |
|
|
RAMSize = 1024*1024; |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
RAMBaseHost = (uint8 *)0x10000000; |
250 |
|
|
ram_area = create_area(RAM_AREA_NAME, (void **)&RAMBaseHost, B_BASE_ADDRESS, RAMSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); |
251 |
|
|
if (ram_area < 0) { |
252 |
|
|
ErrorAlert(GetString(STR_NO_RAM_AREA_ERR)); |
253 |
|
|
PostMessage(B_QUIT_REQUESTED); |
254 |
|
|
return; |
255 |
|
|
} |
256 |
|
|
D(bug("RAM area %ld at %p\n", ram_area, RAMBaseHost)); |
257 |
|
|
|
258 |
|
|
// Create area and load Mac ROM |
259 |
|
|
try { |
260 |
|
|
init_rom(); |
261 |
|
|
} catch (area_error) { |
262 |
|
|
ErrorAlert(GetString(STR_NO_ROM_AREA_ERR)); |
263 |
|
|
PostMessage(B_QUIT_REQUESTED); |
264 |
|
|
return; |
265 |
|
|
} catch (file_open_error) { |
266 |
|
|
ErrorAlert(GetString(STR_NO_ROM_FILE_ERR)); |
267 |
|
|
PostMessage(B_QUIT_REQUESTED); |
268 |
|
|
return; |
269 |
|
|
} catch (file_read_error) { |
270 |
|
|
ErrorAlert(GetString(STR_ROM_FILE_READ_ERR)); |
271 |
|
|
PostMessage(B_QUIT_REQUESTED); |
272 |
|
|
return; |
273 |
|
|
} catch (rom_size_error) { |
274 |
|
|
ErrorAlert(GetString(STR_ROM_SIZE_ERR)); |
275 |
|
|
PostMessage(B_QUIT_REQUESTED); |
276 |
|
|
return; |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
// Check ROM version |
280 |
|
|
if (!CheckROM()) { |
281 |
|
|
ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR)); |
282 |
|
|
PostMessage(B_QUIT_REQUESTED); |
283 |
|
|
return; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
// Set CPU and FPU type (UAE emulation) |
287 |
|
|
switch (ROMVersion) { |
288 |
|
|
case ROM_VERSION_64K: |
289 |
|
|
case ROM_VERSION_PLUS: |
290 |
|
|
case ROM_VERSION_CLASSIC: |
291 |
|
|
CPUType = 0; |
292 |
|
|
FPUType = 0; |
293 |
|
|
TwentyFourBitAddressing = true; |
294 |
|
|
break; |
295 |
|
|
case ROM_VERSION_II: |
296 |
|
|
CPUType = 2; |
297 |
|
|
FPUType = PrefsFindBool("fpu") ? 1 : 0; |
298 |
|
|
TwentyFourBitAddressing = true; |
299 |
|
|
break; |
300 |
|
|
case ROM_VERSION_32: |
301 |
|
|
CPUType = 3; |
302 |
|
|
FPUType = PrefsFindBool("fpu") ? 1 : 0; |
303 |
|
|
TwentyFourBitAddressing = false; |
304 |
|
|
break; |
305 |
|
|
} |
306 |
|
|
CPUIs68060 = false; |
307 |
|
|
|
308 |
|
|
// Load XPRAM |
309 |
|
|
XPRAMInit(); |
310 |
|
|
|
311 |
|
|
// Set boot volume |
312 |
|
|
int16 i16 = PrefsFindInt16("bootdrive"); |
313 |
|
|
XPRAM[0x78] = i16 >> 8; |
314 |
|
|
XPRAM[0x79] = i16 & 0xff; |
315 |
|
|
i16 = PrefsFindInt16("bootdriver"); |
316 |
|
|
XPRAM[0x7a] = i16 >> 8; |
317 |
|
|
XPRAM[0x7b] = i16 & 0xff; |
318 |
|
|
|
319 |
|
|
// Start XPRAM watchdog thread |
320 |
|
|
xpram_thread = spawn_thread(xpram_func, "XPRAM Watchdog", B_LOW_PRIORITY, this); |
321 |
|
|
resume_thread(xpram_thread); |
322 |
|
|
|
323 |
|
|
// Init drivers |
324 |
|
|
SonyInit(); |
325 |
|
|
DiskInit(); |
326 |
|
|
CDROMInit(); |
327 |
|
|
SCSIInit(); |
328 |
|
|
|
329 |
|
|
// Init network |
330 |
|
|
EtherInit(); |
331 |
|
|
|
332 |
|
|
// Init serial ports |
333 |
|
|
SerialInit(); |
334 |
|
|
|
335 |
|
|
// Init Time Manager |
336 |
|
|
TimerInit(); |
337 |
|
|
|
338 |
|
|
// Init clipboard |
339 |
|
|
ClipInit(); |
340 |
|
|
|
341 |
|
|
// Init audio |
342 |
|
|
AudioInit(); |
343 |
|
|
|
344 |
|
|
// Init video |
345 |
|
|
if (!VideoInit(ROMVersion == ROM_VERSION_64K || ROMVersion == ROM_VERSION_PLUS || ROMVersion == ROM_VERSION_CLASSIC)) { |
346 |
|
|
PostMessage(B_QUIT_REQUESTED); |
347 |
|
|
return; |
348 |
|
|
} |
349 |
|
|
|
350 |
|
|
// Init 680x0 emulation (this also activates the memory system which is needed for PatchROM()) |
351 |
|
|
if (!Init680x0()) { |
352 |
|
|
PostMessage(B_QUIT_REQUESTED); |
353 |
|
|
return; |
354 |
|
|
} |
355 |
|
|
|
356 |
|
|
// Install ROM patches |
357 |
|
|
if (!PatchROM()) { |
358 |
|
|
ErrorAlert(GetString(STR_UNSUPPORTED_ROM_TYPE_ERR)); |
359 |
|
|
PostMessage(B_QUIT_REQUESTED); |
360 |
|
|
return; |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
// Write protect ROM |
364 |
|
|
set_area_protection(rom_area, B_READ_AREA); |
365 |
|
|
|
366 |
|
|
// Disallow quitting with Alt-Q from now on |
367 |
|
|
AllowQuitting = false; |
368 |
|
|
|
369 |
|
|
// Start 60Hz interrupt |
370 |
|
|
tick_thread = spawn_thread(tick_func, "60Hz", B_REAL_TIME_PRIORITY, this); |
371 |
|
|
resume_thread(tick_thread); |
372 |
|
|
|
373 |
|
|
// Start emulator thread |
374 |
|
|
emul_thread = spawn_thread(emul_func, "MacOS", B_NORMAL_PRIORITY, this); |
375 |
|
|
resume_thread(emul_thread); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
|
379 |
|
|
/* |
380 |
|
|
* Quit emulator |
381 |
|
|
*/ |
382 |
|
|
|
383 |
|
|
void QuitEmulator(void) |
384 |
|
|
{ |
385 |
|
|
the_app->AllowQuitting = true; |
386 |
|
|
be_app->PostMessage(B_QUIT_REQUESTED); |
387 |
|
|
exit_thread(0); |
388 |
|
|
} |
389 |
|
|
|
390 |
|
|
bool BasiliskII::QuitRequested(void) |
391 |
|
|
{ |
392 |
|
|
if (AllowQuitting) |
393 |
|
|
return BApplication::QuitRequested(); |
394 |
|
|
else |
395 |
|
|
return false; |
396 |
|
|
} |
397 |
|
|
|
398 |
|
|
void BasiliskII::Quit(void) |
399 |
|
|
{ |
400 |
|
|
status_t l; |
401 |
|
|
|
402 |
|
|
// Stop 60Hz interrupt |
403 |
|
|
if (tick_thread > 0) { |
404 |
|
|
tick_thread_active = false; |
405 |
|
|
wait_for_thread(tick_thread, &l); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
// Wait for emulator thread to finish |
409 |
|
|
if (emul_thread > 0) |
410 |
|
|
wait_for_thread(emul_thread, &l); |
411 |
|
|
|
412 |
|
|
// Exit 680x0 emulation |
413 |
|
|
Exit680x0(); |
414 |
|
|
|
415 |
|
|
// Stop XPRAM watchdog thread |
416 |
|
|
if (xpram_thread > 0) { |
417 |
|
|
xpram_thread_active = false; |
418 |
|
|
suspend_thread(xpram_thread); // Wake thread up from snooze() |
419 |
|
|
snooze(1000); |
420 |
|
|
resume_thread(xpram_thread); |
421 |
|
|
wait_for_thread(xpram_thread, &l); |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
// Save XPRAM |
425 |
|
|
XPRAMExit(); |
426 |
|
|
|
427 |
|
|
// Exit video |
428 |
|
|
VideoExit(); |
429 |
|
|
|
430 |
|
|
// Exit audio |
431 |
|
|
AudioExit(); |
432 |
|
|
|
433 |
|
|
// Exit clipboard |
434 |
|
|
ClipExit(); |
435 |
|
|
|
436 |
|
|
// Exit Time Manager |
437 |
|
|
TimerExit(); |
438 |
|
|
|
439 |
|
|
// Exit serial ports |
440 |
|
|
SerialExit(); |
441 |
|
|
|
442 |
|
|
// Exit network |
443 |
|
|
EtherExit(); |
444 |
|
|
|
445 |
|
|
// Exit drivers |
446 |
|
|
SCSIExit(); |
447 |
|
|
CDROMExit(); |
448 |
|
|
DiskExit(); |
449 |
|
|
SonyExit(); |
450 |
|
|
|
451 |
|
|
// Delete ROM area |
452 |
|
|
if (rom_area >= 0) |
453 |
|
|
delete_area(rom_area); |
454 |
|
|
|
455 |
|
|
// Delete RAM area |
456 |
|
|
if (ram_area >= 0) |
457 |
|
|
delete_area(ram_area); |
458 |
|
|
|
459 |
|
|
#if REAL_ADDRESSING |
460 |
|
|
// Unmap low memory and close memory mess driver |
461 |
|
|
if (sheep_fd >= 0) { |
462 |
|
|
ioctl(sheep_fd, SHEEP_DOWN); |
463 |
|
|
close(sheep_fd); |
464 |
|
|
} |
465 |
|
|
#endif |
466 |
|
|
|
467 |
|
|
// Exit system routines |
468 |
|
|
SysExit(); |
469 |
|
|
|
470 |
|
|
// Exit preferences |
471 |
|
|
PrefsExit(); |
472 |
|
|
|
473 |
|
|
BApplication::Quit(); |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
|
477 |
|
|
/* |
478 |
|
|
* Create area for ROM (sets rom_area) and load ROM file |
479 |
|
|
* |
480 |
|
|
* area_error : Cannot create area |
481 |
|
|
* file_open_error: Cannot open ROM file |
482 |
|
|
* file_read_error: Cannot read ROM file |
483 |
|
|
*/ |
484 |
|
|
|
485 |
|
|
void BasiliskII::init_rom(void) |
486 |
|
|
{ |
487 |
|
|
// Create area for ROM |
488 |
|
|
ROMBaseHost = (uint8 *)0x40800000; |
489 |
|
|
rom_area = create_area(ROM_AREA_NAME, (void **)&ROMBaseHost, B_BASE_ADDRESS, ROM_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); |
490 |
|
|
if (rom_area < 0) |
491 |
|
|
throw area_error(); |
492 |
|
|
D(bug("ROM area %ld at %p\n", rom_area, ROMBaseHost)); |
493 |
|
|
|
494 |
|
|
// Load ROM |
495 |
|
|
load_rom(); |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
|
499 |
|
|
/* |
500 |
|
|
* Load ROM file |
501 |
|
|
* |
502 |
|
|
* file_open_error: Cannot open ROM file |
503 |
|
|
* file_read_error: Cannot read ROM file |
504 |
|
|
*/ |
505 |
|
|
|
506 |
|
|
void BasiliskII::load_rom(void) |
507 |
|
|
{ |
508 |
|
|
// Get rom file path from preferences |
509 |
|
|
const char *rom_path = PrefsFindString("rom"); |
510 |
|
|
|
511 |
|
|
// Try to open ROM file |
512 |
|
|
BFile file(rom_path ? rom_path : ROM_FILE_NAME, B_READ_ONLY); |
513 |
|
|
if (file.InitCheck() != B_NO_ERROR) |
514 |
|
|
throw file_open_error(); |
515 |
|
|
|
516 |
|
|
printf(GetString(STR_READING_ROM_FILE)); |
517 |
|
|
|
518 |
|
|
// Is the ROM size correct? |
519 |
|
|
off_t rom_size = 0; |
520 |
|
|
file.GetSize(&rom_size); |
521 |
|
|
if (rom_size != 64*1024 && rom_size != 128*1024 && rom_size != 256*1024 && rom_size != 512*1024 && rom_size != 1024*1024) |
522 |
|
|
throw rom_size_error(); |
523 |
|
|
|
524 |
|
|
uint8 *rom = new uint8[rom_size]; // Reading directly into the area doesn't work |
525 |
|
|
ssize_t actual = file.Read((void *)rom, rom_size); |
526 |
|
|
if (actual == rom_size) |
527 |
|
|
memcpy(ROMBaseHost, rom, rom_size); |
528 |
|
|
delete[] rom; |
529 |
|
|
if (actual != rom_size) |
530 |
|
|
throw file_read_error(); |
531 |
|
|
ROMSize = rom_size; |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
|
535 |
|
|
/* |
536 |
|
|
* Emulator thread function |
537 |
|
|
*/ |
538 |
|
|
|
539 |
|
|
status_t BasiliskII::emul_func(void *arg) |
540 |
|
|
{ |
541 |
|
|
BasiliskII *obj = (BasiliskII *)arg; |
542 |
|
|
|
543 |
|
|
#if __POWERPC__ |
544 |
|
|
// Install data access signal handler |
545 |
|
|
sigemptyset(&obj->sigsegv_action.sa_mask); |
546 |
|
|
obj->sigsegv_action.sa_handler = (__signal_func_ptr)(obj->sigsegv_invoc); |
547 |
|
|
obj->sigsegv_action.sa_flags = 0; |
548 |
|
|
obj->sigsegv_action.sa_userdata = arg; |
549 |
|
|
sigaction(SIGSEGV, &obj->sigsegv_action, NULL); |
550 |
|
|
#endif |
551 |
|
|
|
552 |
|
|
// Exceptions will send signals |
553 |
|
|
disable_debugger(true); |
554 |
|
|
|
555 |
|
|
// Start 68k and jump to ROM boot routine |
556 |
|
|
Start680x0(); |
557 |
|
|
|
558 |
|
|
// Quit program |
559 |
|
|
obj->AllowQuitting = true; |
560 |
|
|
be_app->PostMessage(B_QUIT_REQUESTED); |
561 |
|
|
return 0; |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
|
565 |
|
|
/* |
566 |
|
|
* Code was patched, flush caches if neccessary (i.e. when using a real 680x0 |
567 |
|
|
* or a dynamically recompiling emulator) |
568 |
|
|
*/ |
569 |
|
|
|
570 |
|
|
void FlushCodeCache(void *start, uint32 size) |
571 |
|
|
{ |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
|
575 |
|
|
/* |
576 |
|
|
* Interrupt flags (must be handled atomically!) |
577 |
|
|
*/ |
578 |
|
|
|
579 |
|
|
uint32 InterruptFlags = 0; |
580 |
|
|
|
581 |
|
|
void SetInterruptFlag(uint32 flag) |
582 |
|
|
{ |
583 |
|
|
atomic_or((int32 *)&InterruptFlags, flag); |
584 |
|
|
} |
585 |
|
|
|
586 |
|
|
void ClearInterruptFlag(uint32 flag) |
587 |
|
|
{ |
588 |
|
|
atomic_and((int32 *)&InterruptFlags, ~flag); |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
|
592 |
|
|
/* |
593 |
|
|
* 60Hz thread (really 60.15Hz) |
594 |
|
|
*/ |
595 |
|
|
|
596 |
|
|
status_t BasiliskII::tick_func(void *arg) |
597 |
|
|
{ |
598 |
|
|
BasiliskII *obj = (BasiliskII *)arg; |
599 |
|
|
int tick_counter = 0; |
600 |
|
|
bigtime_t current = system_time(); |
601 |
|
|
|
602 |
|
|
while (obj->tick_thread_active) { |
603 |
|
|
|
604 |
|
|
// Wait |
605 |
|
|
current += 16625; |
606 |
|
|
snooze_until(current, B_SYSTEM_TIMEBASE); |
607 |
|
|
|
608 |
|
|
// Pseudo Mac 1Hz interrupt, update local time |
609 |
|
|
if (++tick_counter > 60) { |
610 |
|
|
tick_counter = 0; |
611 |
|
|
WriteMacInt32(0x20c, TimerDateTime()); |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
// Trigger 60Hz interrupt |
615 |
|
|
SetInterruptFlag(INTFLAG_60HZ); |
616 |
|
|
TriggerInterrupt(); |
617 |
|
|
} |
618 |
|
|
return 0; |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
|
622 |
|
|
/* |
623 |
|
|
* XPRAM watchdog thread (saves XPRAM every minute) |
624 |
|
|
*/ |
625 |
|
|
|
626 |
|
|
status_t BasiliskII::xpram_func(void *arg) |
627 |
|
|
{ |
628 |
|
|
uint8 last_xpram[256]; |
629 |
|
|
memcpy(last_xpram, XPRAM, 256); |
630 |
|
|
|
631 |
|
|
while (((BasiliskII *)arg)->xpram_thread_active) { |
632 |
|
|
snooze(60*1000000); |
633 |
|
|
if (memcmp(last_xpram, XPRAM, 256)) { |
634 |
|
|
memcpy(last_xpram, XPRAM, 256); |
635 |
|
|
SaveXPRAM(); |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
return 0; |
639 |
|
|
} |
640 |
|
|
|
641 |
|
|
|
642 |
|
|
/* |
643 |
|
|
* Display error alert |
644 |
|
|
*/ |
645 |
|
|
|
646 |
|
|
void ErrorAlert(const char *text) |
647 |
|
|
{ |
648 |
|
|
if (PrefsFindBool("nogui")) { |
649 |
|
|
printf(GetString(STR_SHELL_ERROR_PREFIX), text); |
650 |
|
|
return; |
651 |
|
|
} |
652 |
|
|
VideoQuitFullScreen(); |
653 |
|
|
char str[256]; |
654 |
|
|
sprintf(str, GetString(STR_GUI_ERROR_PREFIX), text); |
655 |
|
|
BAlert *alert = new BAlert(GetString(STR_ERROR_ALERT_TITLE), str, GetString(STR_QUIT_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); |
656 |
|
|
alert->Go(); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
|
660 |
|
|
/* |
661 |
|
|
* Display warning alert |
662 |
|
|
*/ |
663 |
|
|
|
664 |
|
|
void WarningAlert(const char *text) |
665 |
|
|
{ |
666 |
|
|
if (PrefsFindBool("nogui")) { |
667 |
|
|
printf(GetString(STR_SHELL_WARNING_PREFIX), text); |
668 |
|
|
return; |
669 |
|
|
} |
670 |
|
|
char str[256]; |
671 |
|
|
sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text); |
672 |
|
|
BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, GetString(STR_OK_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT); |
673 |
|
|
alert->Go(); |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
|
677 |
|
|
/* |
678 |
|
|
* Display choice alert |
679 |
|
|
*/ |
680 |
|
|
|
681 |
|
|
bool ChoiceAlert(const char *text, const char *pos, const char *neg) |
682 |
|
|
{ |
683 |
|
|
char str[256]; |
684 |
|
|
sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text); |
685 |
|
|
BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, pos, neg, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT); |
686 |
|
|
return alert->Go() == 0; |
687 |
|
|
} |
688 |
|
|
|
689 |
|
|
|
690 |
|
|
/* |
691 |
|
|
* SEGV handler |
692 |
|
|
*/ |
693 |
|
|
|
694 |
|
|
#if __POWERPC__ |
695 |
|
|
static uint32 segv_r[32]; |
696 |
|
|
|
697 |
|
|
asm void BasiliskII::sigsegv_invoc(register int sig, register void *arg, register vregs *r) |
698 |
|
|
{ |
699 |
|
|
mflr r0 |
700 |
|
|
stw r0,8(r1) |
701 |
|
|
stwu r1,-56(r1) |
702 |
|
|
|
703 |
|
|
lwz r3,segv_r(r2) |
704 |
|
|
stmw r13,13*4(r3) |
705 |
|
|
|
706 |
|
|
mr r3,r5 |
707 |
|
|
bl sigsegv_handler |
708 |
|
|
|
709 |
|
|
lwz r3,segv_r(r2) |
710 |
|
|
lmw r13,13*4(r3) |
711 |
|
|
|
712 |
|
|
lwz r0,56+8(r1) |
713 |
|
|
mtlr r0 |
714 |
|
|
addi r1,r1,56 |
715 |
|
|
blr |
716 |
|
|
} |
717 |
|
|
|
718 |
|
|
static void sigsegv_handler(vregs *r) |
719 |
|
|
{ |
720 |
|
|
// Fetch volatile registers |
721 |
|
|
segv_r[0] = r->r0; |
722 |
|
|
segv_r[1] = r->r1; |
723 |
|
|
segv_r[2] = r->r2; |
724 |
|
|
segv_r[3] = r->r3; |
725 |
|
|
segv_r[4] = r->r4; |
726 |
|
|
segv_r[5] = r->r5; |
727 |
|
|
segv_r[6] = r->r6; |
728 |
|
|
segv_r[7] = r->r7; |
729 |
|
|
segv_r[8] = r->r8; |
730 |
|
|
segv_r[9] = r->r9; |
731 |
|
|
segv_r[10] = r->r10; |
732 |
|
|
segv_r[11] = r->r11; |
733 |
|
|
segv_r[12] = r->r12; |
734 |
|
|
|
735 |
|
|
// Get opcode and divide into fields |
736 |
|
|
uint32 opcode = *(uint32 *)r->pc; |
737 |
|
|
uint32 primop = opcode >> 26; |
738 |
|
|
uint32 exop = (opcode >> 1) & 0x3ff; |
739 |
|
|
uint32 ra = (opcode >> 16) & 0x1f; |
740 |
|
|
uint32 rb = (opcode >> 11) & 0x1f; |
741 |
|
|
uint32 rd = (opcode >> 21) & 0x1f; |
742 |
|
|
uint32 imm = opcode & 0xffff; |
743 |
|
|
|
744 |
|
|
// Analyze opcode |
745 |
|
|
enum { |
746 |
|
|
TYPE_UNKNOWN, |
747 |
|
|
TYPE_LOAD, |
748 |
|
|
TYPE_STORE |
749 |
|
|
} transfer_type = TYPE_UNKNOWN; |
750 |
|
|
enum { |
751 |
|
|
SIZE_UNKNOWN, |
752 |
|
|
SIZE_BYTE, |
753 |
|
|
SIZE_HALFWORD, |
754 |
|
|
SIZE_WORD |
755 |
|
|
} transfer_size = SIZE_UNKNOWN; |
756 |
|
|
enum { |
757 |
|
|
MODE_UNKNOWN, |
758 |
|
|
MODE_NORM, |
759 |
|
|
MODE_U, |
760 |
|
|
MODE_X, |
761 |
|
|
MODE_UX |
762 |
|
|
} addr_mode = MODE_UNKNOWN; |
763 |
|
|
switch (primop) { |
764 |
|
|
case 31: |
765 |
|
|
switch (exop) { |
766 |
|
|
case 23: // lwzx |
767 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; |
768 |
|
|
case 55: // lwzux |
769 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; |
770 |
|
|
case 87: // lbzx |
771 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; |
772 |
|
|
case 119: // lbzux |
773 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; |
774 |
|
|
case 151: // stwx |
775 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; |
776 |
|
|
case 183: // stwux |
777 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; |
778 |
|
|
case 215: // stbx |
779 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; |
780 |
|
|
case 247: // stbux |
781 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; |
782 |
|
|
case 279: // lhzx |
783 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
784 |
|
|
case 311: // lhzux |
785 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
786 |
|
|
case 343: // lhax |
787 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
788 |
|
|
case 375: // lhaux |
789 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
790 |
|
|
case 407: // sthx |
791 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; |
792 |
|
|
case 439: // sthux |
793 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; |
794 |
|
|
} |
795 |
|
|
break; |
796 |
|
|
|
797 |
|
|
case 32: // lwz |
798 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; |
799 |
|
|
case 33: // lwzu |
800 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; |
801 |
|
|
case 34: // lbz |
802 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; |
803 |
|
|
case 35: // lbzu |
804 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; |
805 |
|
|
case 36: // stw |
806 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; |
807 |
|
|
case 37: // stwu |
808 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; |
809 |
|
|
case 38: // stb |
810 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; |
811 |
|
|
case 39: // stbu |
812 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; |
813 |
|
|
case 40: // lhz |
814 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
815 |
|
|
case 41: // lhzu |
816 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
817 |
|
|
case 42: // lha |
818 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
819 |
|
|
case 43: // lhau |
820 |
|
|
transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
821 |
|
|
case 44: // sth |
822 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; |
823 |
|
|
case 45: // sthu |
824 |
|
|
transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; |
825 |
|
|
} |
826 |
|
|
|
827 |
|
|
// Calculate effective address |
828 |
|
|
uint32 addr = 0; |
829 |
|
|
switch (addr_mode) { |
830 |
|
|
case MODE_X: |
831 |
|
|
case MODE_UX: |
832 |
|
|
if (ra == 0) |
833 |
|
|
addr = segv_r[rb]; |
834 |
|
|
else |
835 |
|
|
addr = segv_r[ra] + segv_r[rb]; |
836 |
|
|
break; |
837 |
|
|
case MODE_NORM: |
838 |
|
|
case MODE_U: |
839 |
|
|
if (ra == 0) |
840 |
|
|
addr = (int32)(int16)imm; |
841 |
|
|
else |
842 |
|
|
addr = segv_r[ra] + (int32)(int16)imm; |
843 |
|
|
break; |
844 |
|
|
} |
845 |
|
|
|
846 |
|
|
// Ignore ROM writes |
847 |
|
|
if (transfer_type == TYPE_STORE && addr >= (uint32)ROMBaseHost && addr < (uint32)ROMBaseHost + ROMSize) { |
848 |
|
|
// D(bug("WARNING: %s write access to ROM at %p, 68k pc %p\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->pc)); |
849 |
|
|
if (addr_mode == MODE_U || addr_mode == MODE_UX) |
850 |
|
|
segv_r[ra] = addr; |
851 |
|
|
r->pc += 4; |
852 |
|
|
goto rti; |
853 |
|
|
} |
854 |
|
|
|
855 |
|
|
// For all other errors, jump into debugger |
856 |
|
|
char str[256]; |
857 |
|
|
sprintf(str, "SIGSEGV\n" |
858 |
|
|
" pc %08lx lr %08lx ctr %08lx msr %08lx\n" |
859 |
|
|
" xer %08lx cr %08lx fpscr %08lx\n" |
860 |
|
|
" r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n" |
861 |
|
|
" r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n" |
862 |
|
|
" r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n" |
863 |
|
|
" r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n" |
864 |
|
|
" r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n" |
865 |
|
|
" r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n" |
866 |
|
|
" r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n" |
867 |
|
|
" r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n", |
868 |
|
|
r->pc, r->lr, r->ctr, r->msr, |
869 |
|
|
r->xer, r->cr, r->fpscr, |
870 |
|
|
r->r0, r->r1, r->r2, r->r3, |
871 |
|
|
r->r4, r->r5, r->r6, r->r7, |
872 |
|
|
r->r8, r->r9, r->r10, r->r11, |
873 |
|
|
r->r12, segv_r[13], segv_r[14], segv_r[15], |
874 |
|
|
segv_r[16], segv_r[17], segv_r[18], segv_r[19], |
875 |
|
|
segv_r[20], segv_r[21], segv_r[22], segv_r[23], |
876 |
|
|
segv_r[24], segv_r[25], segv_r[26], segv_r[27], |
877 |
|
|
segv_r[28], segv_r[29], segv_r[30], segv_r[31]); |
878 |
|
|
disable_debugger(false); |
879 |
|
|
debugger(str); |
880 |
|
|
QuitEmulator(); |
881 |
|
|
return; |
882 |
|
|
|
883 |
|
|
rti: |
884 |
|
|
// Restore volatile registers |
885 |
|
|
r->r0 = segv_r[0]; |
886 |
|
|
r->r1 = segv_r[1]; |
887 |
|
|
r->r2 = segv_r[2]; |
888 |
|
|
r->r3 = segv_r[3]; |
889 |
|
|
r->r4 = segv_r[4]; |
890 |
|
|
r->r5 = segv_r[5]; |
891 |
|
|
r->r6 = segv_r[6]; |
892 |
|
|
r->r7 = segv_r[7]; |
893 |
|
|
r->r8 = segv_r[8]; |
894 |
|
|
r->r9 = segv_r[9]; |
895 |
|
|
r->r10 = segv_r[10]; |
896 |
|
|
r->r11 = segv_r[11]; |
897 |
|
|
r->r12 = segv_r[12]; |
898 |
|
|
} |
899 |
|
|
#endif |