zxemut: show libfdc messages in the console
[zymosis.git] / src / ZXEmuT / main.c
blob5345d24c87c6da0736805752d7f22df5a58e4d90
1 /* coded by Ketmar // Invisible Vector ( ketmar@ketmar.no-ip.org )
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
9 */
10 //#define DISABLE_Z80EX
11 //#define DISABLE_ZYMOSIS
12 #ifndef _BSD_SOURCE
13 # define _BSD_SOURCE
14 #endif
15 #ifndef _DEFAULT_SOURCE
16 # define _DEFAULT_SOURCE
17 #endif
19 #include <stdarg.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <fcntl.h>
28 #include <sys/socket.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32 #include <sys/select.h>
34 #include <SDL.h>
36 #include <libspectrum.h>
39 ////////////////////////////////////////////////////////////////////////////////
40 #include "libtinf/tinf.h"
41 #include "../libzymosis/zymosis.h"
42 #include "../libfdc/libfdc.h"
45 ////////////////////////////////////////////////////////////////////////////////
46 #include "emucommon.h"
47 #include "emuvars.h"
48 #include "emuutils.h"
49 #include "emuexec.h"
50 #include "libvideo/video.h"
51 #include "debugger.h"
52 #include "console.h"
53 #include "zxscrdraw.h"
54 #include "machines.h"
55 #include "snd_alsa.h"
56 #include "tapes.h"
57 #include "lssnap.h"
58 #include "zxkeyinfo.h"
60 #include "jimapi.h"
62 #include "keyled.h"
63 #include "diskled.h"
66 ////////////////////////////////////////////////////////////////////////////////
67 extern const unsigned char keyHelpScr[];
70 ////////////////////////////////////////////////////////////////////////////////
71 char binMyDir[8192];
74 static void initMyDir (void) {
75 if (readlink("/proc/self/exe", binMyDir, sizeof(binMyDir)-1) < 0) {
76 strcpy(binMyDir, ".");
77 } else {
78 char *p = (char *)strrchr(binMyDir, '/');
80 if (p == NULL) strcpy(binMyDir, "."); else *p = '\0';
85 ////////////////////////////////////////////////////////////////////////////////
86 const char *snapshotExtensions[] = {
87 ".z80",
88 ".sna",
89 ".snp",
90 ".sp",
91 ".szx",
92 ".slt",
93 ".zxs",
94 ".tap",
95 ".tzx",
96 ".spc",
97 ".sta",
98 ".ltp",
99 ".pzx",
100 ".trd",
101 ".scl",
102 ".fdi",
103 ".udi",
104 ".td0",
105 ".dsk",
106 ".dmb",
107 ".rzx",
108 ".zip",
109 NULL
113 ////////////////////////////////////////////////////////////////////////////////
114 typedef struct PortHandlers {
115 int machine; // ZX_MACHINE_MAX: any
116 uint16_t mask;
117 uint16_t value;
118 int (*portInCB) (zym_cpu_t *z80, uint16_t port, uint8_t *res);
119 int (*portOutCB) (zym_cpu_t *z80, uint16_t port, uint8_t value);
120 } PortHandlers;
123 #define MAX_PORTHANDLER (64)
124 static PortHandlers portHandlers[MAX_PORTHANDLER];
125 static int phCount = 0;
128 static void phAdd (const PortHandlers *ph) {
129 if (ph != NULL) {
130 while (ph->portInCB || ph->portOutCB) {
131 if (phCount >= MAX_PORTHANDLER) { fprintf(stderr, "FATAL: too many port handlers!\n"); abort(); }
132 portHandlers[phCount++] = *ph++;
138 ////////////////////////////////////////////////////////////////////////////////
139 #include "zymcb.c"
142 ////////////////////////////////////////////////////////////////////////////////
143 static int usock_stdin = 0;
144 static int usock_srv = -1;
145 static int usock_client = -1;
146 static char usock_command[1024];
147 static size_t usock_command_pos = 0;
150 static size_t unixsock_create_address (struct sockaddr_un *addr, const char *name) {
151 if (!addr || !name || !name[0] || strlen(name) > 100) return 0;
152 memset((void *)addr, 0, sizeof(*addr));
153 addr->sun_family = AF_UNIX;
154 addr->sun_path[0] = 0; /* abstract unix socket */
155 strcpy(addr->sun_path+1, name);
156 return strlen(addr->sun_path+1)+sizeof(addr->sun_family)+1u;
160 static void sock_make_non_blocking (int fd) {
161 if (fd >= 0) {
162 int flags = fcntl(fd, F_GETFL, 0);
163 flags |= O_NONBLOCK;
164 fcntl(fd, F_SETFL, flags);
169 static void unixsock_drop_client (void) {
170 if (usock_client >= 0) {
171 close(usock_client);
172 usock_client = -1;
174 usock_command_pos = 0;
178 static void unixsock_stop_server (void) {
179 unixsock_drop_client();
180 if (usock_srv >= 0) {
181 close(usock_srv);
182 usock_srv = -1;
187 static void unixsock_start_server (const char *name) {
188 unixsock_stop_server();
190 /* use stdin */
191 if (name && strcmp(name, "-") == 0) {
192 if (usock_stdin) {
193 cprintf("\4ERROR: stdin already closed!\n");
194 usock_srv = -1;
195 return;
197 usock_srv = -1;
198 usock_client = 0;
199 usock_stdin = 1;
200 sock_make_non_blocking(usock_srv);
201 return;
204 usock_srv = socket(AF_UNIX, SOCK_STREAM, 0);
205 if (usock_srv < 0) {
206 cprintf("\4ERROR: cannot create server socket!\n");
207 usock_srv = -1;
208 return;
210 sock_make_non_blocking(usock_srv);
212 struct sockaddr_un serv_addr;
213 size_t servlen = unixsock_create_address(&serv_addr, name);
214 if (bind(usock_srv, (struct sockaddr *)&serv_addr, servlen) < 0) {
215 cprintf("\4ERROR: cannot bind server socket!\n");
216 close(usock_srv);
217 usock_srv = -1;
218 return;
221 if (listen(usock_srv, 1) < 0) {
222 cprintf("\4ERROR: cannot listen on server socket!\n");
223 close(usock_srv);
224 usock_srv = -1;
225 return;
228 cprintf("\1created unix server socket '%s'\n", name);
232 static void unixsock_handle (void) {
233 if (usock_srv < 0 && usock_client < 0) return;
234 struct timeval tout;
235 fd_set rds;
236 tout.tv_sec = 0;
237 tout.tv_usec = 0;
238 /* can accept new client? */
239 if (usock_client < 0) {
240 /* yeah, check for new client */
241 FD_ZERO(&rds);
242 FD_SET(usock_srv, &rds);
243 int res = select(usock_srv+1, &rds, NULL, NULL, &tout);
244 if (res <= 0) return; /* nothing */
245 /* accept new client */
246 struct sockaddr_un cli_addr;
247 socklen_t clilen = (socklen_t)sizeof(cli_addr);
248 int newsockfd = accept(usock_srv, (struct sockaddr *)&cli_addr, &clilen);
249 if (newsockfd < 0) {
250 cprintf("\4ERROR: error accepting client connection!\n");
251 return;
253 usock_client = newsockfd;
254 cprintf("\1USOCK: accepted new client\n");
256 /* check if we can read from client socket */
257 for (int maxread = 4096; maxread > 0; --maxread) {
258 char ch;
259 FD_ZERO(&rds);
260 FD_SET(usock_client, &rds);
261 int res = select(usock_client+1, &rds, NULL, NULL, &tout);
262 if (res <= 0) break; /* nothing */
263 ssize_t rd = read(usock_client, &ch, 1);
264 if (rd == 0) {
265 cprintf("\2USOCK: client closed the connection\n");
266 unixsock_drop_client();
267 return;
269 if (rd < 0) {
270 cprintf("\4ERROR: error reading from client connection!\n");
271 unixsock_drop_client();
272 return;
274 if (usock_command_pos >= sizeof(usock_command)-2) {
275 cprintf("\4ERROR: too long command from client!\n");
276 unixsock_drop_client();
277 return;
279 if (ch == '\n') ch = 0;
280 usock_command[usock_command_pos++] = ch;
281 if (!ch) {
282 /* command complete */
283 usock_command_pos = 0;
284 char *cmd = strprintf("::usock_received %s", usock_command);
285 if (Jim_Eval(jim, cmd) == JIM_OK) {
286 free(cmd);
287 Jim_Obj *res = Jim_GetResult(jim);
288 const char *resstr = Jim_String(res);
289 if (resstr && strcasecmp(resstr, "close") == 0) {
290 cprintf("closing client connection (client request).\n");
291 unixsock_drop_client();
292 return;
294 if (resstr && strcasecmp(resstr, "done") == 0) {
295 continue;
297 } else {
298 free(cmd);
299 Jim_MakeErrorMessage(jim);
300 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
302 /* close client connection? */
304 if (strcmp(usock_command, "close") == 0) {
305 cprintf("closing client connection (client request).\n");
306 unixsock_drop_client();
307 return;
310 cprintf("\2USOCK COMMAND:<%s>\n", usock_command);
311 conExecute(usock_command, 0);
312 continue;
318 ////////////////////////////////////////////////////////////////////////////////
319 static inline void zxDoKeyUpDown (uint16_t portmask, int isdown) {
320 if (portmask != 0) {
321 if (isdown) {
322 zxKeyboardState[(portmask>>8)&0xff] &= ~(portmask&0xff);
323 } else {
324 zxKeyboardState[(portmask>>8)&0xff] |= (portmask&0xff);
330 ////////////////////////////////////////////////////////////////////////////////
331 static void emuInitMemory (void) {
332 zxMaxMemoryBank = 64;
333 for (int f = 0; f < zxMaxMemoryBank; ++f) {
334 if ((zxMemoryBanks[f] = calloc(1, 16384)) == NULL) {
335 fprintf(stderr, "FATAL: out of memory!\n");
336 abort();
340 zxMaxROMMemoryBank = 16;
341 for (int f = 0; f < zxMaxROMMemoryBank; ++f) {
342 if ((zxROMBanks[f] = calloc(1, 16384)) == NULL) {
343 fprintf(stderr, "FATAL: out of memory!\n");
344 abort();
350 static void emuInit (void) {
351 int lineNo = 0;
353 for (int t = 0; t < 3; ++t) {
354 for (int y = 0; y < 8; ++y) {
355 for (int z = 0; z < 8; ++z) {
356 zxScrLineOfs[lineNo++] = (t<<11)|(z<<8)|(y<<5);
361 memset(zxKeyBinds, 0, sizeof(zxKeyBinds));
362 memset(zxScreen, 0, sizeof(zxScreen));
363 memset(zxUlaSnow, 0, sizeof(zxUlaSnow));
365 zxScreenCurrent = 0;
366 zxWasUlaSnow = 0;
367 zxBorder = 0;
368 zxScreenBank = NULL;
369 zxFlashTimer = 0;
370 zxFlashState = 0;
371 zxOldScreenTS = 0;
372 emuFrameCount = 0;
373 emuFrameStartTime = 0;
374 emuLastFPSText[0] = '\0';
376 emuInitMemory();
377 emuAddPortHandlers();
379 soundAYReset();
381 if ((zxDiskIf = difCreate(DIF_BDI)) == NULL) { fprintf(stderr, "FATAL: can't create BetaDisk object!\n"); abort(); }
383 zym_init(&z80);
384 //zym_clear_callbacks(&z80);
385 z80.mem_read = z80MemRead;
386 z80.mem_write = z80MemWrite;
387 z80.mem_contention = z80Contention;
388 z80.port_read = z80PortIn;
389 z80.port_write = z80PortOut;
390 z80.port_contention = z80PortContention;
391 z80.trap_ed = z80EDTrap;
392 z80.evenM1 = machineInfo.evenM1;
396 ////////////////////////////////////////////////////////////////////////////////
397 // defattr < 0: use screen$ attr
398 static void paintZXScr (int x0, int y0, const void *ptr, int defattr) {
399 const uint8_t *buf = (const uint8_t *)ptr;
401 for (int y = 0; y < 192; ++y) {
402 int scrA = zxScrLineOfs[y], attrA = 6144+(y/8)*32;
404 for (int x = 0; x < 32; ++x) {
405 uint8_t bt = buf[scrA++];
406 uint8_t attr = (defattr >= 0 && defattr <= 255 ? defattr : buf[attrA++]);
407 uint8_t paper = (attr>>3)&0x0f, ink = (attr&0x07)+(paper&0x08);
409 if (attr&0x80 && zxFlashState) { uint8_t t = paper; paper = ink; ink = t; }
410 for (int f = 0; f < 8; ++f) {
411 putPixel(x0+x*8+f, y0+y, (bt&0x80 ? ink : paper));
412 bt = (bt&0x7f)<<1;
419 static void paintZXScrZ (int x0, int y0) {
420 static uint8_t scr[6912];
421 unsigned int outlen = 6912;
422 static int unpacked = 0;
424 if (!unpacked) {
425 if (tinf_zlib_uncompress(scr, &outlen, keyHelpScr, 2153) == TINF_OK && outlen == 6912) {
426 unpacked = 1;
427 } else {
428 return;
431 if (unpacked) {
432 clearScreen(0);
433 paintZXScr(x0, y0, scr, -1);
438 static void emuDrawFPS (void) {
439 if (emuFrameStartTime > 0 && emuFrameCount) {
440 int64_t tt = timerGetMS();
441 if (tt-emuFrameStartTime >= 1000) {
442 int fps = emuFrameCount/((tt-emuFrameStartTime)/1000.0)+0.5;
443 if (fps < 0) fps = 0;
444 //sprintf(emuLastFPSText, "%.15f %d %3d%%", ((double)emuFrameCount/((double)(tt-emuFrameStartTime)/1000)), fps, fps*100/50);
445 sprintf(emuLastFPSText, "%d %3d%%", fps, fps*100/50);
446 emuFrameStartTime = tt;
447 emuFrameCount = 0;
450 if (emuLastFPSText[0]) {
451 drawStr6Outline(emuLastFPSText, frameSfc->w-6*strlen(emuLastFPSText)-2, 2, 12, 0);
456 ////////////////////////////////////////////////////////////////////////////////
457 static void zxPaintOverlaysAndLeds (void) {
458 // key help
459 if (optKeyHelpVisible) paintZXScrZ(32-zxScreenOfs, 24);
461 // various leds
462 if (optDrawKeyLeds) keyledBlit(1, frameSfc->h-15);
463 if (optTapePlaying) emuTapeDrawCounter();
464 if (diskLastActivity > 0) diskBlit(frameSfc->w-26, frameSfc->h-26);
466 // "max speed" mark
467 if (optMaxSpeed) drawStr6Outline("max", frameSfc->w-3*6-2, frameSfc->h-10, 12, 0);
469 // "paused" or FPS
470 if (optPaused) {
471 //drawStr8Outline("paused", frameSfc->w-6*8-2, 2, 12, 0);
472 int w = drawStrZ("paused", 0, 0, 255, 255);
473 drawStrZ("paused", 320-w-1, 0, 66, 67);
474 } else if (optDrawFPS) {
475 emuDrawFPS();
478 // "late timings" mark
479 if (zxLateTimings) drawStr6Outline("LT", 2, 2, 12, 0);
481 // ui overlays
482 uiovlDraw();
484 // debugger window
485 if (debuggerActive) dbgDraw();
487 // console
488 if (conVisible) conDraw();
489 conDrawMessage();
491 // rzx progress
492 if (zxRZX) {
493 static char buf[1024];
494 Uint8 fg = ((zxBorder&0x07) < 6 ? 15 : 0);
495 if (zxRZXFrames > 0) {
496 int prc = 100*zxRZXCurFrame/zxRZXFrames;
497 int secs = (int)((zxRZXFrames-zxRZXCurFrame)/(50.0*optSpeed/100.0));
498 if (prc > 100) prc = 100;
499 if (secs < 0) secs = 0;
500 sprintf(buf, "RZX:%3d%% (%d:%02d left)", prc, secs/60, secs%60);
501 } else {
502 strcpy(buf, "RZX");
504 drawStr6(buf, 1, 1, fg, 255);
507 Jim_CollectIfNeeded(jim);
511 static void zxPaintZXScreen (void) {
512 for (int y = 0; y < 240; ++y) {
513 int lp = (y+24)*352+16+zxScreenOfs;
514 // the real Speccy screen is not 'centered', as aowen says; 10px seems to be the right value
515 if (optNoFlic) {
516 Uint8 *d = (((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
517 for (int x = 0; x < 320; ++x, ++lp, d += 4) {
518 //putPixelMix(x, y, zxScreen[zxScreenCurrent][lp], zxScreen[zxScreenCurrent^1][lp]);
519 Uint8 c0 = zxScreen[zxScreenCurrent][lp], c1 = zxScreen[zxScreenCurrent^1][lp];
520 d[vidRIdx] = (palRGB[c0][0]+palRGB[c1][0])/2;
521 d[vidGIdx] = (palRGB[c0][1]+palRGB[c1][1])/2;
522 d[vidBIdx] = (palRGB[c0][2]+palRGB[c1][2])/2;
524 } else {
525 Uint32 *dst = (Uint32 *)(((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
526 if (optSlowShade && optSpeed < 100) {
527 int shadeStart = zxScreenLineShadeStart(z80.tstates);
528 if (lp >= shadeStart) {
529 // the whole line is shaded
530 for (int x = 0; x < 320; ++x) {
531 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
532 //*dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
534 } else if (lp < shadeStart && lp+319 >= shadeStart) {
535 // line part is shaded
536 for (int x = 0; x < 320; ++x) {
537 if (lp < shadeStart) {
538 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
539 } else {
540 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
543 } else {
544 // not shaded at all
545 for (int x = 0; x < 320; ++x) {
546 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
547 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
550 } else {
551 for (int x = 0; x < 320; ++x) {
552 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
553 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
561 static void zxPaintScreen (void) {
562 zxPaintZXScreen();
563 zxPaintOverlaysAndLeds();
567 ////////////////////////////////////////////////////////////////////////////////
568 static int msCursorDrawn = 0;
569 static int msRealCursorVisible = 1;
572 void emuHideRealMouseCursor (void) {
573 if (msRealCursorVisible) {
574 SDL_ShowCursor(0);
575 msRealCursorVisible = 0;
580 void emuShowRealMouseCursor (void) {
581 if (!msRealCursorVisible) {
582 SDL_ShowCursor(1);
583 msRealCursorVisible = 1;
588 void emuRealizeRealMouseCursorState (void) {
589 if (optFullscreen) {
590 emuHideRealMouseCursor();
591 } else if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
592 // no grab
593 if (!msCursorDrawn) emuShowRealMouseCursor(); else emuHideRealMouseCursor();
594 } else {
595 // grab
596 emuHideRealMouseCursor();
601 void emuFullScreenChanged (void) {
602 emuRealizeRealMouseCursorState();
606 static void zxFrameCB (void) {
607 if (!debuggerActive && !optPaused) {
608 if (optMaxSpeed) {
609 // doing maxspeed
610 const int msscroff = (optMaxSpeedBadScreen || optMaxSpeedBadScreenTape);
611 if (msscroff) ++optNoScreenReal;
612 const int64_t stt = timerGetMS();
613 do {
614 while (!debuggerActive && !optPaused && !z80_was_frame_end) z80Step();
615 z80_was_frame_end = 0;
616 } while (!debuggerActive && !optPaused && timerGetMS()-stt < 20);
617 // redraw screen; prevent border flicking
618 if (msscroff) {
619 --optNoScreenReal;
620 zxOldScreenTS = 0;
621 zxRealiseScreen(machineInfo.tsperframe);
622 } else {
623 zxRealiseScreen(z80.tstates);
625 } else if (optSpeed != 100) {
626 // not 100% speed
627 int tickstodo = machineInfo.tsperframe*optSpeed/100+1;
628 const int64_t stt = timerGetMS();
629 if (emuPrevFCB == 0) {
630 emuPrevFCB = stt;
631 } else {
632 if (stt-emuPrevFCB < 20) SDL_Delay(20-(stt-emuPrevFCB));
633 emuPrevFCB = timerGetMS();
635 if (optSpeed < 100) {
636 // slower than the original
637 //fprintf(stderr, "speed=%d; tstates=%d; todo=%d; end=%d; frame=%d\n", optSpeed, z80.tstates, tickstodo, z80.tstates+tickstodo, machineInfo.tsperframe);
638 while (tickstodo > 0 && !debuggerActive && !optPaused) tickstodo -= z80Step();
639 z80_was_frame_end = 0;
640 } else {
641 // faster than the original
642 int nomore = 0;
643 // do incomplete frame
644 while (!z80_was_frame_end && !debuggerActive && !optPaused) tickstodo -= z80Step();
645 z80_was_frame_end = 0;
646 // execute full frames, checking for timeout
647 while (tickstodo >= machineInfo.tsperframe && !debuggerActive && !optPaused) {
648 // do one frame
649 while (!z80_was_frame_end && !debuggerActive && !optPaused) tickstodo -= z80Step();
650 z80_was_frame_end = 0;
651 if (timerGetMS()-stt >= 20) { nomore = 1; break; }
653 // do the rest
654 if (!nomore) {
655 //while (tickstodo > 0 && !debuggerActive && !optPaused && timerGetMS()-stt <= 20) tickstodo -= z80Step();
656 while (tickstodo > 0 && !debuggerActive && !optPaused) tickstodo -= z80Step();
659 z80_was_frame_end = 0;
660 } else {
661 // normal, do one frame till end
662 while (!debuggerActive && !optPaused && !z80_was_frame_end) z80Step();
663 if (z80_was_frame_end) {
664 z80_was_frame_end = 0;
665 if (!debuggerActive && !optPaused) {
666 // normal execution; draw frame, play sound
667 zxPaintScreen();
668 forcedShowFrame();
669 soundWrite();
670 return;
675 zxRealiseScreen(z80.tstates);
676 zxPaintScreen();
680 ////////////////////////////////////////////////////////////////////////////////
681 static void zxMouseSetButtons (int buttons) {
682 if (buttons&MS_BUTTON_WHEELUP) zxKMouseWheel = ((int)zxKMouseWheel-1)&0x0f;
683 if (buttons&MS_BUTTON_WHEELDOWN) zxKMouseWheel = ((int)zxKMouseWheel+1)&0x0f;
684 zxKMouseButtons = buttons&(MS_BUTTON_LEFT|MS_BUTTON_RIGHT|MS_BUTTON_MIDDLE);
688 static void zxMouseCB (int x, int y, int xrel, int yrel, int buttons) {
689 if (/*widgetsProcessMouse(x, y, 0, buttons) ||*/ msCursorDrawn) return;
690 //fprintf(stderr, "mouse: x=%d; y=%d; xrel=%d; yrel=%d; btn=#%02X\n", x, y, xrel, yrel, buttons);
691 if (!msCursorDrawn && vidWindowActivated && SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
692 #if 0
693 zxKMouseDXAccum += xrel;
694 zxKMouseDYAccum += yrel;
695 int speed = zxKMouseSpeed;
696 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
697 xrel = yrel = 0;
698 while (zxKMouseDXAccum >= speed) { xrel += 1; zxKMouseDXAccum -= speed; }
699 while (zxKMouseDXAccum <= -speed) { xrel -= 1; zxKMouseDXAccum += speed; }
700 while (zxKMouseDYAccum >= speed) { yrel += 1; zxKMouseDYAccum -= speed; }
701 while (zxKMouseDYAccum <= -speed) { yrel -= 1; zxKMouseDYAccum += speed; }
702 zxKMouseDX = (((int)zxKMouseDX+xrel)&0xff);
703 zxKMouseDY = (((int)zxKMouseDY-yrel)&0xff);
704 #else
705 //int lp = (y+24)*352+16+zxScreenOfs;
706 int dx = 0, dy = 0;
707 if (zxMouseWasMoved) {
708 dx = x-zxMouseLastX;
709 dy = y-zxMouseLastY;
710 } else {
711 zxMouseLastX = x;
712 zxMouseLastY = y;
713 zxMouseWasMoved = 1;
715 int speed = zxKMouseSpeed;
716 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
717 dx /= speed;
718 dy /= speed;
719 if (dx != 0) {
720 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
721 zxMouseLastX = x;
723 if (dy != 0) {
724 zxKMouseDY = (((int)zxKMouseDY-dy)&0xff);
725 zxMouseLastY = y;
727 #endif
728 zxMouseSetButtons(buttons);
729 } else {
730 zxMouseWasMoved = 0;
735 static void zxMouseButtonCB (int x, int y, int btn, int buttons) {
736 if (/*widgetsProcessMouse(x, y, btn, buttons) ||*/ msCursorDrawn) return;
737 if (!msCursorDrawn && vidWindowActivated) {
738 //fprintf(stderr, "buttons=0x%02x\n", buttons);
739 if (optKMouse && buttons == 0 && btn == (MS_BUTTON_LEFT|MS_BUTTON_DEPRESSED) &&
740 SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
742 SDL_WM_GrabInput(SDL_GRAB_ON);
743 emuRealizeRealMouseCursorState();
744 zxMouseWasMoved = 0;
745 return; // ignore this click
747 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
748 zxMouseSetButtons(buttons);
754 ////////////////////////////////////////////////////////////////////////////////
755 static void zxKeyCB (SDL_KeyboardEvent *key) {
756 if (conVisible && conKeyEvent(key)) return;
757 //if (msCursorDrawn) return;
758 if (debuggerActive && dbgKeyEvent(key)) return;
759 if (uiovlKey(key)) return;
760 if (key->type == SDL_KEYDOWN) {
761 SDLJimBinding *bind;
762 if ((bind = sdlFindKeyBind(sdlJimBindings, key->keysym.sym, key->keysym.mod)) != NULL && bind->action != NULL) {
763 Jim_Obj *eres = NULL/*, *dupl*/;
764 // duplicate 'action', 'cause Jim can return the same shared object
765 // if there is nothing to 'expand' in it; it's safe to omit
766 // duplication here, but future versions of Jim can check if the
767 // object is 'shared' here, so let's do it right
768 //!dupl = Jim_DuplicateObj(jim, bind->action);
769 //!Jim_IncrRefCount(dupl); // we need to do this after Jim_DuplicateObj()
770 if (Jim_SubstObj(jim, /*dupl*/bind->action, &eres, 0) == JIM_OK) {
771 Jim_IncrRefCount(eres);
772 if (Jim_EvalObjList(jim, eres) != JIM_OK) {
773 Jim_MakeErrorMessage(jim);
774 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
776 Jim_DecrRefCount(jim, eres);
778 //!Jim_DecrRefCount(jim, dupl);
779 return;
783 if (/*vidWindowActivated &&*/ msCursorDrawn) return;
785 if (zxKeyBinds[key->keysym.sym]) {
786 zxDoKeyUpDown(zxKeyBinds[key->keysym.sym]&0xffff, (key->type == SDL_KEYDOWN));
787 zxDoKeyUpDown((zxKeyBinds[key->keysym.sym]>>16)&0xffff, (key->type == SDL_KEYDOWN));
788 return;
793 ////////////////////////////////////////////////////////////////////////////////
794 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
795 #define DECODE_TUPLE(tuple,bytes) \
796 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
797 PUSH_BACK((char)((tuple >> 24)&0xff))
799 // returns ress length
800 static int ascii85Decode (char **ress, const char *srcs/*, int start, int length*/) {
801 static uint32_t pow85[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
802 const uint8_t *data = (const uint8_t *)srcs;
803 int len = (int)strlen(srcs);
804 uint32_t tuple = 0;
805 int count = 0, c = 0;
806 int dpos = 0;
807 int start = 0, length = len;
808 int tmp;
810 if (start < 0) start = 0; else { len -= start; data += start; }
811 if (length < 0 || len < length) length = len;
813 if (length > 0) {
814 int xlen = 4*((length+4)/5);
815 kstringReserve(ress, xlen);
819 *ress = (char *)calloc(1, len+1);
820 for (int f = length; f > 0; --f, ++data) {
821 c = *data;
822 if (c <= ' ') continue; // skip blanks
823 switch (c) {
824 case 'z': // zero tuple
825 if (count != 0) {
826 //fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
827 free(*ress);
828 *ress = NULL;
829 return -1;
831 PUSH_BACK('\0');
832 PUSH_BACK('\0');
833 PUSH_BACK('\0');
834 PUSH_BACK('\0');
835 break;
836 case '~': // '~>': end of sequence
837 if (f < 1 || data[1] != '>') { free(*ress); return -2; } // error
838 if (count > 0) { f = -1; break; }
839 default:
840 if (c < '!' || c > 'u') {
841 //fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
842 free(*ress);
843 return -3;
845 tuple += ((uint8_t)(c-'!'))*pow85[count++];
846 if (count == 5) {
847 DECODE_TUPLE(tuple, 4);
848 count = 0;
849 tuple = 0;
851 break;
854 // write last (possibly incomplete) tuple
855 if (count-- > 0) {
856 tuple += pow85[count];
857 DECODE_TUPLE(tuple, count);
859 return dpos;
862 #undef PUSH_BACK
863 #undef DECODE_TUPLE
866 static void decodeBA (char *str, int len) {
867 char pch = 42;
869 for (int f = 0; f < len; ++f, ++str) {
870 char ch = *str;
872 ch = (ch-f-1)^pch;
873 *str = ch;
874 pch = ch;
879 static void printEC (const char *txt) {
880 char *dest;
881 int len;
883 if ((len = ascii85Decode(&dest, txt)) >= 0) {
884 decodeBA(dest, len);
885 fprintf(stderr, "%s\n", dest);
886 free(dest);
891 static int isStr85Equ (const char *txt, const char *str) {
892 char *dest;
893 int len, res = 0;
895 if ((len = ascii85Decode(&dest, txt)) >= 0) {
896 res = (strcmp(dest, str) == 0);
897 free(dest);
899 return res;
903 static int checkEGG (const char *str) {
904 if (isStr85Equ("06:]JASq", str) || isStr85Equ("0/i", str)) {
905 printEC(
906 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
907 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
908 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
909 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
910 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
911 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
912 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
913 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
914 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
915 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
916 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
917 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
918 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
920 return 1;
922 if (isStr85Equ("04Jj?B)", str)) {
923 printEC(
924 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
925 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
926 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
927 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
929 return 1;
931 if (isStr85Equ("04o69A7Tr", str)) {
932 printEC(
933 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
934 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
935 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
937 return 1;
939 return 0;
943 static void addBoots (int simpleAutorun) {
944 for (int f = 0; f < 4; ++f) {
945 if ((snapWasDisk&(1<<f)) && !(snapWasCPCDisk&(1<<f))) addAutoBoot(f, 1, simpleAutorun);
947 snapWasDisk = 0;
948 snapWasCPCDisk = 0;
952 typedef enum {
953 ARUN_UNDEFINED,
954 ARUN_NONE,
955 ARUN_TRDOS,
956 ARUN_TRDOS48,
957 ARUN_TRDOS128,
958 ARUN_TRDOSPENT,
959 ARUN_TAP,
960 ARUN_TAP48,
961 ARUN_TAP128,
962 ARUN_TAPPENT,
963 ARUN_TAPP2A,
964 ARUN_TAPP3,
965 ARUN_P3DOS2A,
966 ARUN_P3DOS3,
967 } cli_arun_e;
970 static cli_arun_e cli_autorun = ARUN_UNDEFINED;
972 static void show_help (void) {
973 printf(
974 "options:\n"
975 " --48 use 48k model\n"
976 " --128 use 128k model\n"
977 " -A --no-autorun don't autorun file\n"
979 exit(0);
983 static char *strappend (char *s, const char *s1) {
984 if (!s) s = strdup("");
985 if (!s1 || !s1[0]) return s;
986 char *res = strprintf("%s%s", s, s1);
987 free(s);
988 return res;
992 enum {
993 CLI_MODEL_AUTO,
994 CLI_MODEL_48,
995 CLI_MODEL_128,
996 CLI_MODEL_PENT,
997 CLI_MODEL_PLUS2A,
998 CLI_MODEL_PLUS3,
1002 static void processOptions (int argc, char *argv[], int onlydbg) {
1003 int nomoreopts = 0, oldABoot = optAutoaddBoot;
1004 int do48 = CLI_MODEL_AUTO;
1005 optAutoaddBoot = 0;
1006 snapWasDisk = 0;
1007 snapWasCPCDisk = 0;
1008 snapWasTape = 0;
1009 for (int f = 1; f < argc; ++f) {
1010 if (checkEGG(argv[f])) exit(1);
1011 if (!nomoreopts) {
1012 if (strcmp(argv[f], "--") == 0) { nomoreopts = 1; continue; }
1013 if (strcmp(argv[f], "+") == 0) continue; // console command separator
1014 if (argv[f][0] == '+') {
1015 if (onlydbg) continue;
1016 optAutoaddBoot = oldABoot;
1017 if (strchr(argv[f], ' ') != NULL) {
1018 conExecute(argv[f]+1, 0);
1019 } else {
1020 // collect console command
1021 char *cmd = strdup(argv[f]+1);
1022 for (++f; f < argc; ++f) {
1023 if (argv[f][0] == '+') break;
1024 if (argv[f][0] == '-' && argv[f][1] == '-') break;
1025 cmd = strappend(cmd, " ");
1026 cmd = strappend(cmd, argv[f]);
1028 --f; // compensate 'for'
1029 conExecute(cmd, 0);
1030 free(cmd);
1032 if (oldABoot != optAutoaddBoot) {
1033 if (oldABoot) addBoots(0);
1034 oldABoot = optAutoaddBoot;
1035 snapWasDisk = 0;
1036 snapWasCPCDisk = 0;
1038 optAutoaddBoot = 0;
1039 continue;
1041 if (argv[f][0] == '-') {
1042 if (argv[f][1] == '-') {
1043 if (strcmp(argv[f], "--help") == 0) show_help();
1044 else if (strcmp(argv[f], "--48") == 0) do48 = CLI_MODEL_48;
1045 else if (strcmp(argv[f], "--128") == 0) do48 = CLI_MODEL_128;
1046 else if (strcmp(argv[f], "--pent") == 0 || strcmp(argv[f], "--pentagon") == 0) do48 = CLI_MODEL_PENT;
1047 else if (strcmp(argv[f], "--plus2a") == 0) do48 = CLI_MODEL_PLUS2A;
1048 else if (strcmp(argv[f], "--plus3") == 0) do48 = CLI_MODEL_PLUS3;
1049 else if (strcmp(argv[f], "--no-autorun") == 0) cli_autorun = ARUN_NONE;
1050 else if (strcmp(argv[f], "--opense") == 0) {
1051 if (!onlydbg) {
1052 if (!optOpenSE) { optOpenSE = 1; emuSetModel(zxModel, 1); }
1055 else if (strcmp(argv[f], "--usock") == 0 || strcmp(argv[f], "--usocket") == 0) {
1056 ++f;
1057 if (f >= argc) { fprintf(stderr, "option '%s' expects socket name!\n", argv[f-1]); exit(1); }
1058 if (!onlydbg) unixsock_start_server(argv[f]);
1060 else if (strcmp(argv[f], "--unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(1); }
1061 else if (strcmp(argv[f], "--no-unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(0); }
1062 else { fprintf(stderr, "unknown command line option: '%s'\n", argv[f]); exit(1); }
1063 continue;
1065 for (const char *a = argv[f]+1; *a; ++a) {
1066 switch (*a) {
1067 case 'h': show_help(); break;
1068 case 'A': cli_autorun = ARUN_NONE; break;
1069 case 'D':
1070 if (onlydbg) {
1071 fprintf(stderr, "console dump enabled\n");
1072 optConDump = 1;
1074 break;
1075 case 'q': /* do not dump consote text to stdout */
1076 optConDumpToStdout = 0;
1077 break;
1078 case 'S':
1079 if (onlydbg) {
1080 fprintf(stderr, "sound debug enabled\n");
1081 optSndSyncDebug = 1;
1083 break;
1084 default: fprintf(stderr, "unknown command line option: '%c'\n", *a); exit(1);
1087 continue; // jff
1090 if (onlydbg) continue;
1091 // snapshot name
1092 if (loadSnapshot(argv[f], SNAPLOAD_ANY) == 0) {
1093 cprintf("'%s' loaded\n", argv[f]);
1094 if (cli_autorun != ARUN_NONE) {
1095 if (snapWasDisk) cli_autorun = (snapWasCPCDisk ? ARUN_P3DOS2A : ARUN_TRDOS);
1096 else if (snapWasTape) cli_autorun = ARUN_TAP;
1097 else cli_autorun = ARUN_UNDEFINED;
1099 //cprintf("wasdisk=%d; wascpc=%d; ar=%d\n", snapWasDisk, snapWasCPCDisk, cli_autorun);
1100 } else {
1101 cprintf("failed to load '%s'\n", argv[f]);
1103 snapWasTape = 0;
1106 optAutoaddBoot = oldABoot;
1107 if (!onlydbg) {
1108 if (oldABoot && !snapWasCPCDisk) addBoots(1);
1109 if (cli_autorun > ARUN_NONE) {
1110 if (cli_autorun != ARUN_P3DOS2A && cli_autorun != ARUN_P3DOS3) {
1111 switch (do48) {
1112 case CLI_MODEL_AUTO:
1113 switch (cli_autorun) {
1114 case ARUN_TAP: cli_autorun = ARUN_TAP48; break;
1115 case ARUN_TRDOS: cli_autorun = ARUN_TRDOSPENT; break;
1116 default: break;
1118 break;
1119 case CLI_MODEL_48:
1120 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1121 break;
1122 case CLI_MODEL_128:
1123 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS128 : ARUN_TAP128);
1124 break;
1125 case CLI_MODEL_PENT:
1126 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOSPENT : ARUN_TAPPENT);
1127 break;
1128 case CLI_MODEL_PLUS2A:
1129 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS2A : ARUN_TAPP2A);
1130 break;
1131 case CLI_MODEL_PLUS3:
1132 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS3 : ARUN_TAPP3);
1133 break;
1134 default:
1135 cprintf("\1UNKNOWN CLI MODEL!\n");
1136 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1137 break;
1140 switch (cli_autorun) {
1141 case ARUN_TRDOS: conExecute("reset trdos", 0); break;
1142 case ARUN_TRDOS48: conExecute("reset 48k trdos", 0); break;
1143 case ARUN_TRDOS128: conExecute("reset 128k trdos", 0); break;
1144 case ARUN_TRDOSPENT: conExecute("reset pentagon 512 trdos", 0); break;
1145 case ARUN_TAP48: conExecute("reset 48k", 0); goto do_tape_autoload;
1146 case ARUN_TAP128: conExecute("reset 128k", 0); goto do_tape_autoload;
1147 case ARUN_TAPPENT: conExecute("reset pentagon 512", 0); goto do_tape_autoload;
1148 case ARUN_TAPP2A: conExecute("reset plus2a", 0); goto do_tape_autoload;
1149 case ARUN_TAPP3: conExecute("reset plus3", 0); goto do_tape_autoload;
1150 case ARUN_P3DOS2A: conExecute("reset plus2a", 0); break;
1151 case ARUN_P3DOS3: conExecute("reset plus3", 0); break;
1152 case ARUN_TAP:
1153 do_tape_autoload:
1154 conExecute("tape _autoload", 0);
1155 break;
1156 default: ;
1163 static void xMainLoop (void) {
1164 const int mcsInFrame = 20*1000;
1165 static int64_t mcsFrameEndWanted;
1166 int eres = 1;
1167 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1168 while (eres >= 0) {
1169 int64_t mcsCurFrameEnd;
1170 eres = processEvents(0);
1171 buildFrame();
1172 unixsock_handle();
1173 mcsCurFrameEnd = timerGetMicroSeconds();
1174 if (mcsCurFrameEnd > 0) {
1175 int mcsSleep = (mcsFrameEndWanted-mcsCurFrameEnd);
1176 //fprintf(stderr, "0: wait=%.15g\n", ((double)mcsSleep)/1000.0);
1177 if (mcsSleep > 0) {
1178 // less than 20 ms
1179 //fprintf(stderr, "SLEEP: %.15g\n", ((double)mcsSleep)/1000.0);
1180 usleep(mcsSleep);
1181 //mcsCurFrameEnd = timerGetMicroSeconds();
1182 //fprintf(stderr, "1:few=%d; cfe=%d; few-cfe=%.15g\n", (int)mcsFrameEndWanted, (int)mcsCurFrameEnd, ((double)(mcsInFrame-(mcsFrameEndWanted-mcsCurFrameEnd)))/1000);
1183 mcsFrameEndWanted += mcsInFrame;
1184 } else {
1185 fprintf(stderr, "DESYNC! (%d)\n", mcsSleep);
1186 //mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1187 mcsFrameEndWanted = mcsCurFrameEnd+mcsInFrame;
1189 } else {
1190 //FIXME
1191 // reinit timer
1192 timerReinit();
1193 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1199 ////////////////////////////////////////////////////////////////////////////////
1200 static void cprintLibFDC (int type, const char *msg) {
1201 switch (type) {
1202 case LIBFDC_MSG_DEBUG: cprintf("\3LIBFDC[debug]: %s\n", msg); break;
1203 case LIBFDC_MSG_WARNING: cprintf("\2LIBFDC[warn]: %s\n", msg); break;
1204 case LIBFDC_MSG_ERROR: cprintf("\4LIBFDC[error]: %s\n", msg); break;
1205 default: cprintf("\3LIBFDC[???]: %s\n", msg); break; // the thing that should not be
1210 ////////////////////////////////////////////////////////////////////////////////
1211 int main (int argc, char *argv[]) {
1212 Jim_SetAllowUnsafeExtensions(1);
1213 tinf_init();
1214 conInit();
1215 initMyDir();
1217 libfdcMessageCB = &cprintLibFDC;
1219 processOptions(argc, argv, 1);
1221 if (libspectrum_init() != LIBSPECTRUM_ERROR_NONE) {
1222 fprintf(stderr, "FATAL: can't init libspectrum!\n");
1223 return 1;
1225 cprintf("===================================\n");
1226 cprintf("using libspectrum v%s\n", libspectrum_version());
1228 switch (timerInit()) {
1229 case TIMER_ERROR: abort();
1230 case TIMER_HPET: break;
1231 default:
1232 cprintf(
1233 "\2WARNING: please, set your clock source to HPET!\n"
1234 "you can do this by issuing the following command:\n"
1235 "======\n"
1236 "\1sudo echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource\n"
1237 "======\n"
1238 "\2this is not a critical issue, but hpet clock will\n"
1239 "\2give you slightly better emulation.\n"
1241 break;
1244 if (!Jim_GetAllowUnsafeExtensions()) {
1245 cprintf("\2WARNING: Disabled unsafe Tcl extensions.\n");
1246 } else {
1247 cprintf("Unsafe Tcl extensions enabled ('--no-unsafe-tcl' to disable).\n");
1250 //zxwinInit();
1251 jimInit();
1253 emuInit();
1254 jimEvalFile("init/init.tcl", 0);
1256 jimEvalFile("init/roms.tcl", 0);
1257 emuSetModel(zxModel, 1);
1259 dbgInit();
1261 emuInitBindings();
1262 jimEvalFile("init/concmd.tcl", 0);
1264 sdlInit();
1265 initVideo();
1267 frameCB = zxFrameCB;
1268 keyCB = zxKeyCB;
1269 mouseCB = zxMouseCB;
1270 mouseButtonCB = zxMouseButtonCB;
1272 //jimEvalFile("init/widgets/init.tcl", 1);
1274 jimEvalFile("autoexec.tcl", 1);
1275 emuSetModel(zxModel, 1); // in case something vital was changed (like "opense on")
1277 processOptions(argc, argv, 0);
1279 jimEvalFile1("./.zxemutrc.tcl");
1280 jimEvalFile1("./.zxemut.tcl");
1281 jimEvalFile1("./zxemutrc.tcl");
1282 jimEvalFile1("./zxemut.tcl");
1284 #ifdef USE_SOUND
1285 if (sndSampleRate < 0 && initSound() != 0) {
1286 fprintf(stderr, "WARNING: can't initialize sound!\n");
1287 cprintf("\4WARNING: can't initialize sound!\n");
1288 sndSampleRate = 0;
1290 sndAllowUseToggle = 0;
1291 if (sndSampleRate <= 0) cprintf("NOSOUND mode");
1293 frameCB = zxFrameCB;
1294 keyCB = zxKeyCB;
1295 mouseCB = zxMouseCB;
1296 mouseButtonCB = zxMouseButtonCB;
1298 uiovlInit();
1300 if (sndSampleRate > 0) {
1301 while (processEvents(0) >= 0) {
1302 buildFrame();
1303 unixsock_handle();
1304 // optMaxSpeed implies sndUsing==0
1305 if (!optMaxSpeed && optSpeed == 100 && (debuggerActive || optPaused)) soundWrite();
1307 } else {
1308 xMainLoop();
1310 #else
1311 sndAllowUseToggle = 0;
1312 xMainLoop();
1313 #endif
1315 unixsock_stop_server();
1316 uiovlDeinit();
1317 //zxwinDeinit();
1318 difDestroy(zxDiskIf);
1319 deinitSound();
1320 jimDeinit();
1321 if (condumpfl != NULL) fclose(condumpfl);
1322 return 0;