added event system a-la FUSE (because i'm going to steal FUSE FDC emulation code)
[zymosis.git] / src / ZXEmuT / main.c
blob15e9cbc0af7776ad2d094aae8a5d982e4b6c7d81
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2020 Ketmar Dark <ketmar@ketmar.no-ip.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
20 #ifndef _BSD_SOURCE
21 # define _BSD_SOURCE
22 #endif
23 #ifndef _DEFAULT_SOURCE
24 # define _DEFAULT_SOURCE
25 #endif
27 #include <stdarg.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <time.h>
33 #include <unistd.h>
34 #include <dirent.h>
35 #include <fcntl.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/un.h>
42 #include <sys/select.h>
44 #include <SDL.h>
46 #include <libspectrum.h>
49 ////////////////////////////////////////////////////////////////////////////////
50 #include "libtinf/tinf.h"
51 #include "../libzymosis/zymosis.h"
52 #include "../libfdc/libfdc.h"
55 ////////////////////////////////////////////////////////////////////////////////
56 #include "emucommon.h"
57 #include "emuvars.h"
58 #include "emuutils.h"
59 #include "emuexec.h"
60 #include "libvideo/video.h"
61 #include "debugger.h"
62 #include "console.h"
63 #include "zxscrdraw.h"
64 #include "machines.h"
65 #include "snd_alsa.h"
66 #include "tapes.h"
67 #include "lssnap.h"
68 #include "zxkeyinfo.h"
70 #include "jimapi.h"
72 #include "keyled.h"
73 #include "diskled.h"
76 ////////////////////////////////////////////////////////////////////////////////
77 extern const unsigned char keyHelpScr[];
80 ////////////////////////////////////////////////////////////////////////////////
81 char binMyDir[8192];
84 static void initMyDir (void) {
85 if (readlink("/proc/self/exe", binMyDir, sizeof(binMyDir)-1) < 0) {
86 strcpy(binMyDir, ".");
87 } else {
88 char *p = (char *)strrchr(binMyDir, '/');
90 if (p == NULL) strcpy(binMyDir, "."); else *p = '\0';
95 ////////////////////////////////////////////////////////////////////////////////
96 const char *snapshotExtensions[] = {
97 ".z80",
98 ".sna",
99 ".snp",
100 ".sp",
101 ".szx",
102 ".slt",
103 ".zxs",
104 ".tap",
105 ".tzx",
106 ".spc",
107 ".sta",
108 ".ltp",
109 ".pzx",
110 ".trd",
111 ".scl",
112 ".fdi",
113 ".udi",
114 ".td0",
115 ".dsk",
116 ".dmb",
117 ".rzx",
118 ".zip",
119 NULL
123 ////////////////////////////////////////////////////////////////////////////////
124 typedef struct PortHandlers {
125 int machine; // ZX_MACHINE_MAX: any
126 uint16_t mask;
127 uint16_t value;
128 int (*portInCB) (zym_cpu_t *z80, uint16_t port, uint8_t *res);
129 int (*portOutCB) (zym_cpu_t *z80, uint16_t port, uint8_t value);
130 } PortHandlers;
133 #define MAX_PORTHANDLER (64)
134 static PortHandlers portHandlers[MAX_PORTHANDLER];
135 static int phCount = 0;
138 static void phAdd (const PortHandlers *ph) {
139 if (ph != NULL) {
140 while (ph->portInCB || ph->portOutCB) {
141 if (phCount >= MAX_PORTHANDLER) { fprintf(stderr, "FATAL: too many port handlers!\n"); abort(); }
142 portHandlers[phCount++] = *ph++;
148 ////////////////////////////////////////////////////////////////////////////////
149 #include "zymcb.c"
152 ////////////////////////////////////////////////////////////////////////////////
153 static int usock_stdin = 0;
154 static int usock_srv = -1;
155 static int usock_client = -1;
156 static char usock_command[1024];
157 static size_t usock_command_pos = 0;
160 static size_t unixsock_create_address (struct sockaddr_un *addr, const char *name) {
161 if (!addr || !name || !name[0] || strlen(name) > 100) return 0;
162 memset((void *)addr, 0, sizeof(*addr));
163 addr->sun_family = AF_UNIX;
164 addr->sun_path[0] = 0; /* abstract unix socket */
165 strcpy(addr->sun_path+1, name);
166 return strlen(addr->sun_path+1)+sizeof(addr->sun_family)+1u;
170 static void sock_make_non_blocking (int fd) {
171 if (fd >= 0) {
172 int flags = fcntl(fd, F_GETFL, 0);
173 flags |= O_NONBLOCK;
174 fcntl(fd, F_SETFL, flags);
179 static void unixsock_drop_client (void) {
180 if (usock_client >= 0) {
181 close(usock_client);
182 usock_client = -1;
184 usock_command_pos = 0;
188 static void unixsock_stop_server (void) {
189 unixsock_drop_client();
190 if (usock_srv >= 0) {
191 close(usock_srv);
192 usock_srv = -1;
197 static void unixsock_start_server (const char *name) {
198 unixsock_stop_server();
200 /* use stdin */
201 if (name && strcmp(name, "-") == 0) {
202 if (usock_stdin) {
203 cprintf("\4ERROR: stdin already closed!\n");
204 usock_srv = -1;
205 return;
207 usock_srv = -1;
208 usock_client = 0;
209 usock_stdin = 1;
210 sock_make_non_blocking(usock_srv);
211 return;
214 usock_srv = socket(AF_UNIX, SOCK_STREAM, 0);
215 if (usock_srv < 0) {
216 cprintf("\4ERROR: cannot create server socket!\n");
217 usock_srv = -1;
218 return;
220 sock_make_non_blocking(usock_srv);
222 struct sockaddr_un serv_addr;
223 size_t servlen = unixsock_create_address(&serv_addr, name);
224 if (bind(usock_srv, (struct sockaddr *)&serv_addr, servlen) < 0) {
225 cprintf("\4ERROR: cannot bind server socket!\n");
226 close(usock_srv);
227 usock_srv = -1;
228 return;
231 if (listen(usock_srv, 1) < 0) {
232 cprintf("\4ERROR: cannot listen on server socket!\n");
233 close(usock_srv);
234 usock_srv = -1;
235 return;
238 cprintf("\1created unix server socket '%s'\n", name);
242 static void unixsock_handle (void) {
243 if (usock_srv < 0 && usock_client < 0) return;
244 struct timeval tout;
245 fd_set rds;
246 tout.tv_sec = 0;
247 tout.tv_usec = 0;
248 /* can accept new client? */
249 if (usock_client < 0) {
250 /* yeah, check for new client */
251 FD_ZERO(&rds);
252 FD_SET(usock_srv, &rds);
253 int res = select(usock_srv+1, &rds, NULL, NULL, &tout);
254 if (res <= 0) return; /* nothing */
255 /* accept new client */
256 struct sockaddr_un cli_addr;
257 socklen_t clilen = (socklen_t)sizeof(cli_addr);
258 int newsockfd = accept(usock_srv, (struct sockaddr *)&cli_addr, &clilen);
259 if (newsockfd < 0) {
260 cprintf("\4ERROR: error accepting client connection!\n");
261 return;
263 usock_client = newsockfd;
264 cprintf("\1USOCK: accepted new client\n");
266 /* check if we can read from client socket */
267 for (int maxread = 4096; maxread > 0; --maxread) {
268 char ch;
269 FD_ZERO(&rds);
270 FD_SET(usock_client, &rds);
271 int res = select(usock_client+1, &rds, NULL, NULL, &tout);
272 if (res <= 0) break; /* nothing */
273 ssize_t rd = read(usock_client, &ch, 1);
274 if (rd == 0) {
275 cprintf("\2USOCK: client closed the connection\n");
276 unixsock_drop_client();
277 return;
279 if (rd < 0) {
280 cprintf("\4ERROR: error reading from client connection!\n");
281 unixsock_drop_client();
282 return;
284 if (usock_command_pos >= sizeof(usock_command)-2) {
285 cprintf("\4ERROR: too long command from client!\n");
286 unixsock_drop_client();
287 return;
289 if (ch == '\n') ch = 0;
290 usock_command[usock_command_pos++] = ch;
291 if (!ch) {
292 /* command complete */
293 usock_command_pos = 0;
294 char *cmd = strprintf("::usock_received %s", usock_command);
295 if (Jim_Eval(jim, cmd) == JIM_OK) {
296 free(cmd);
297 Jim_Obj *res = Jim_GetResult(jim);
298 const char *resstr = Jim_String(res);
299 if (resstr && strcasecmp(resstr, "close") == 0) {
300 cprintf("closing client connection (client request).\n");
301 unixsock_drop_client();
302 return;
304 if (resstr && strcasecmp(resstr, "done") == 0) {
305 continue;
307 } else {
308 free(cmd);
309 Jim_MakeErrorMessage(jim);
310 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
312 /* close client connection? */
314 if (strcmp(usock_command, "close") == 0) {
315 cprintf("closing client connection (client request).\n");
316 unixsock_drop_client();
317 return;
320 cprintf("\2USOCK COMMAND:<%s>\n", usock_command);
321 conExecute(usock_command, 0);
322 continue;
328 ////////////////////////////////////////////////////////////////////////////////
329 static inline void zxDoKeyUpDown (uint16_t portmask, int isdown) {
330 if (portmask != 0) {
331 if (isdown) {
332 zxKeyboardState[(portmask>>8)&0xff] &= ~(portmask&0xff);
333 } else {
334 zxKeyboardState[(portmask>>8)&0xff] |= (portmask&0xff);
340 ////////////////////////////////////////////////////////////////////////////////
341 static void emuInitMemory (void) {
342 zxMaxMemoryBank = 64;
343 for (int f = 0; f < zxMaxMemoryBank; ++f) {
344 if ((zxMemoryBanks[f] = calloc(1, 16384)) == NULL) {
345 fprintf(stderr, "FATAL: out of memory!\n");
346 abort();
350 zxMaxROMMemoryBank = 16;
351 for (int f = 0; f < zxMaxROMMemoryBank; ++f) {
352 if ((zxROMBanks[f] = calloc(1, 16384)) == NULL) {
353 fprintf(stderr, "FATAL: out of memory!\n");
354 abort();
360 static void emuInit (void) {
361 int lineNo = 0;
363 for (int t = 0; t < 3; ++t) {
364 for (int y = 0; y < 8; ++y) {
365 for (int z = 0; z < 8; ++z) {
366 zxScrLineOfs[lineNo++] = (t<<11)|(z<<8)|(y<<5);
371 memset(zxKeyBinds, 0, sizeof(zxKeyBinds));
372 memset(zxScreen, 0, sizeof(zxScreen));
373 memset(zxUlaSnow, 0, sizeof(zxUlaSnow));
375 zxScreenCurrent = 0;
376 zxWasUlaSnow = 0;
377 zxBorder = 0;
378 zxScreenBank = NULL;
379 zxFlashTimer = 0;
380 zxFlashState = 0;
381 zxOldScreenTS = 0;
382 emuFrameCount = 0;
383 emuFrameStartTime = 0;
384 emuLastFPSText[0] = '\0';
386 emuInitMemory();
387 emuAddPortHandlers();
389 soundAYReset();
391 if ((zxDiskIf = difCreate(DIF_BDI)) == NULL) { fprintf(stderr, "FATAL: can't create BetaDisk object!\n"); abort(); }
393 zym_init(&z80);
394 //zym_clear_callbacks(&z80);
395 z80.mem_read = z80MemRead;
396 z80.mem_write = z80MemWrite;
397 z80.mem_contention = z80Contention;
398 z80.port_read = z80PortIn;
399 z80.port_write = z80PortOut;
400 z80.port_contention = z80PortContention;
401 z80.trap_ed = z80EDTrap;
402 z80.evenM1 = machineInfo.evenM1;
406 ////////////////////////////////////////////////////////////////////////////////
407 // defattr < 0: use screen$ attr
408 static void paintZXScr (int x0, int y0, const void *ptr, int defattr) {
409 const uint8_t *buf = (const uint8_t *)ptr;
411 for (int y = 0; y < 192; ++y) {
412 int scrA = zxScrLineOfs[y], attrA = 6144+(y/8)*32;
414 for (int x = 0; x < 32; ++x) {
415 uint8_t bt = buf[scrA++];
416 uint8_t attr = (defattr >= 0 && defattr <= 255 ? defattr : buf[attrA++]);
417 uint8_t paper = (attr>>3)&0x0f, ink = (attr&0x07)+(paper&0x08);
419 if (attr&0x80 && zxFlashState) { uint8_t t = paper; paper = ink; ink = t; }
420 for (int f = 0; f < 8; ++f) {
421 putPixel(x0+x*8+f, y0+y, (bt&0x80 ? ink : paper));
422 bt = (bt&0x7f)<<1;
429 static void paintZXScrZ (int x0, int y0) {
430 static uint8_t scr[6912];
431 unsigned int outlen = 6912;
432 static int unpacked = 0;
434 if (!unpacked) {
435 if (tinf_zlib_uncompress(scr, &outlen, keyHelpScr, 2153) == TINF_OK && outlen == 6912) {
436 unpacked = 1;
437 } else {
438 return;
441 if (unpacked) {
442 clearScreen(0);
443 paintZXScr(x0, y0, scr, -1);
448 static void emuDrawFPS (void) {
449 if (emuFrameStartTime > 0 && emuFrameCount) {
450 int64_t tt = timerGetMS();
451 if (tt-emuFrameStartTime >= 1000) {
452 int fps = emuFrameCount/((tt-emuFrameStartTime)/1000.0)+0.5;
453 if (fps < 0) fps = 0;
454 //sprintf(emuLastFPSText, "%.15f %d %3d%%", ((double)emuFrameCount/((double)(tt-emuFrameStartTime)/1000)), fps, fps*100/50);
455 sprintf(emuLastFPSText, "%d %3d%%", fps, fps*100/50);
456 emuFrameStartTime = tt;
457 emuFrameCount = 0;
460 if (emuLastFPSText[0]) {
461 drawStr6Outline(emuLastFPSText, frameSfc->w-6*strlen(emuLastFPSText)-2, 2, 12, 0);
466 ////////////////////////////////////////////////////////////////////////////////
467 static void zxPaintOverlaysAndLeds (void) {
468 // key help
469 if (optKeyHelpVisible) paintZXScrZ(32-zxScreenOfs, 24);
471 // various leds
472 if (optDrawKeyLeds) keyledBlit(1, frameSfc->h-15);
473 if (optTapePlaying) emuTapeDrawCounter();
474 if (diskLastActivity > 0) diskBlit(frameSfc->w-26, frameSfc->h-26);
476 // "max speed" mark
477 if (optMaxSpeed) drawStr6Outline("max", frameSfc->w-3*6-2, frameSfc->h-10, 12, 0);
479 // "paused" or FPS
480 if (optPaused) {
481 //drawStr8Outline("paused", frameSfc->w-6*8-2, 2, 12, 0);
482 int w = drawStrZ("paused", 0, 0, 255, 255);
483 drawStrZ("paused", 320-w-1, 0, 66, 67);
484 } else if (optDrawFPS) {
485 emuDrawFPS();
488 // "late timings" mark
489 if (zxLateTimings) drawStr6Outline("LT", 2, 2, 12, 0);
491 // ui overlays
492 uiovlDraw();
494 // debugger window
495 if (debuggerActive) dbgDraw();
497 // console
498 if (conVisible) {
499 vid_textsrc_height = CON_HEIGHT;
500 conDraw();
501 } else {
502 vid_textsrc_height = 0;
504 if (debuggerActive && !dbgIsHidden()) vid_textsrc_height = 0;
506 vid_textscr_active = ((debuggerActive && !dbgIsHidden()) || conVisible);
508 conDrawMessage();
510 // rzx progress
511 if (zxRZX) {
512 static char buf[1024];
513 Uint8 fg = ((zxBorder&0x07) < 6 ? 15 : 0);
514 if (zxRZXFrames > 0) {
515 int prc = 100*zxRZXCurFrame/zxRZXFrames;
516 int secs = (int)((zxRZXFrames-zxRZXCurFrame)/(50.0*optSpeed/100.0));
517 if (prc > 100) prc = 100;
518 if (secs < 0) secs = 0;
519 sprintf(buf, "RZX:%3d%% (%d:%02d left)", prc, secs/60, secs%60);
520 } else {
521 strcpy(buf, "RZX");
523 drawStr6(buf, 1, 1, fg, 255);
526 Jim_CollectIfNeeded(jim);
530 static void zxPaintZXScreen (void) {
531 for (int y = 0; y < 240; ++y) {
532 int lp = (y+24)*352+16+zxScreenOfs;
533 // the real Speccy screen is not 'centered', as aowen says; 10px seems to be the right value
534 if (optNoFlic > 0 || optNoFlicDetected) {
535 Uint8 *d = (((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
536 for (int x = 0; x < 320; ++x, ++lp, d += 4) {
537 //putPixelMix(x, y, zxScreen[zxScreenCurrent][lp], zxScreen[zxScreenCurrent^1][lp]);
538 Uint8 c0 = zxScreen[zxScreenCurrent][lp], c1 = zxScreen[zxScreenCurrent^1][lp];
539 d[vidRIdx] = (palRGB[c0][0]+palRGB[c1][0])/2;
540 d[vidGIdx] = (palRGB[c0][1]+palRGB[c1][1])/2;
541 d[vidBIdx] = (palRGB[c0][2]+palRGB[c1][2])/2;
543 } else {
544 Uint32 *dst = (Uint32 *)(((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
545 if (optSlowShade && optSpeed < 100) {
546 int shadeStart = zxScreenLineShadeStart(z80.tstates);
547 if (lp >= shadeStart) {
548 // the whole line is shaded
549 for (int x = 0; x < 320; ++x) {
550 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
551 //*dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
553 } else if (lp < shadeStart && lp+319 >= shadeStart) {
554 // line part is shaded
555 for (int x = 0; x < 320; ++x) {
556 if (lp < shadeStart) {
557 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
558 } else {
559 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
562 } else {
563 // not shaded at all
564 for (int x = 0; x < 320; ++x) {
565 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
566 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
569 } else {
570 for (int x = 0; x < 320; ++x) {
571 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
572 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
580 static void zxPaintScreen (void) {
581 zxPaintZXScreen();
582 zxPaintOverlaysAndLeds();
586 ////////////////////////////////////////////////////////////////////////////////
587 static int msCursorDrawn = 0;
588 static int msRealCursorVisible = 1;
589 static int msLastGrabState = 0;
592 void emuHideRealMouseCursor (void) {
593 if (msRealCursorVisible) {
594 SDL_ShowCursor(0);
595 msRealCursorVisible = 0;
600 void emuShowRealMouseCursor (void) {
601 if (!msRealCursorVisible) {
602 SDL_ShowCursor(1);
603 msRealCursorVisible = 1;
608 void emuRealizeRealMouseCursorState (void) {
609 if (optFullscreen) {
610 emuHideRealMouseCursor();
611 if (!msLastGrabState) {
612 msLastGrabState = 1;
613 zxMouseWasMoved = 0;
614 zxMouseLastX = zxMouseLastY = 0;
615 zxMouseFracX = zxMouseFracY = 0;
617 } else if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
618 // no grab
619 if (!msCursorDrawn) emuShowRealMouseCursor(); else emuHideRealMouseCursor();
620 msLastGrabState = 0;
621 } else {
622 // grab
623 emuHideRealMouseCursor();
624 if (!msLastGrabState) {
625 msLastGrabState = 1;
626 zxMouseWasMoved = 0;
627 zxMouseLastX = zxMouseLastY = 0;
628 zxMouseFracX = zxMouseFracY = 0;
634 void emuFullScreenChanged (void) {
635 emuRealizeRealMouseCursorState();
639 static void zxFrameCB (void) {
640 if (!debuggerActive && !optPaused) {
641 if (optMaxSpeed) {
642 // doing maxspeed
643 const int msscroff = (optMaxSpeedBadScreen || optMaxSpeedBadScreenTape);
644 if (msscroff) ++optNoScreenReal;
645 const int64_t stt = timerGetMS();
646 do {
647 while (!debuggerActive && !optPaused && !z80_was_frame_end) {
648 z80Step();
650 z80_was_frame_end = 0;
651 } while (!debuggerActive && !optPaused && timerGetMS()-stt < 20);
652 // redraw screen; prevent border flicking
653 if (msscroff) {
654 --optNoScreenReal;
655 zxOldScreenTS = 0;
656 zxRealiseScreen(machineInfo.tsperframe);
657 } else {
658 zxRealiseScreen(z80.tstates);
660 } else if (optSpeed != 100) {
661 // not 100% speed
662 int tickstodo = machineInfo.tsperframe*optSpeed/100+1;
663 const int64_t stt = timerGetMS();
664 if (emuPrevFCB == 0) {
665 emuPrevFCB = stt;
666 } else {
667 if (stt-emuPrevFCB < 20) SDL_Delay(20-(stt-emuPrevFCB));
668 emuPrevFCB = timerGetMS();
670 if (optSpeed < 100) {
671 // slower than the original
672 //fprintf(stderr, "speed=%d; tstates=%d; todo=%d; end=%d; frame=%d\n", optSpeed, z80.tstates, tickstodo, z80.tstates+tickstodo, machineInfo.tsperframe);
673 while (tickstodo > 0 && !debuggerActive && !optPaused) {
674 tickstodo -= z80Step();
676 z80_was_frame_end = 0;
677 } else {
678 // faster than the original
679 int nomore = 0;
680 // do incomplete frame
681 while (!z80_was_frame_end && !debuggerActive && !optPaused) {
682 tickstodo -= z80Step();
684 z80_was_frame_end = 0;
685 // execute full frames, checking for timeout
686 while (tickstodo >= machineInfo.tsperframe && !debuggerActive && !optPaused) {
687 // do one frame
688 while (!z80_was_frame_end && !debuggerActive && !optPaused) {
689 tickstodo -= z80Step();
691 z80_was_frame_end = 0;
692 if (timerGetMS()-stt >= 20) { nomore = 1; break; }
694 // do the rest
695 if (!nomore) {
696 //while (tickstodo > 0 && !debuggerActive && !optPaused && timerGetMS()-stt <= 20) tickstodo -= z80Step();
697 while (tickstodo > 0 && !debuggerActive && !optPaused) {
698 tickstodo -= z80Step();
702 z80_was_frame_end = 0;
703 } else {
704 // normal, do one frame till end
705 while (!debuggerActive && !optPaused && !z80_was_frame_end) {
706 z80Step();
708 if (z80_was_frame_end) {
709 z80_was_frame_end = 0;
710 if (!debuggerActive && !optPaused) {
711 // normal execution; draw frame, play sound
712 zxPaintScreen();
713 forcedShowFrame();
714 soundWrite();
715 return;
720 zxRealiseScreen(z80.tstates);
721 zxPaintScreen();
725 ////////////////////////////////////////////////////////////////////////////////
726 static void zxMouseSetButtons (int buttons) {
727 if (buttons&MS_BUTTON_WHEELUP) zxKMouseWheel = ((int)zxKMouseWheel-1)&0x0f;
728 if (buttons&MS_BUTTON_WHEELDOWN) zxKMouseWheel = ((int)zxKMouseWheel+1)&0x0f;
729 zxKMouseButtons = buttons&(MS_BUTTON_LEFT|MS_BUTTON_RIGHT|MS_BUTTON_MIDDLE);
733 static void zxMouseCB (int x, int y, int xrel, int yrel, int buttons) {
734 if (/*widgetsProcessMouse(x, y, 0, buttons) ||*/ msCursorDrawn) return;
735 //fprintf(stderr, "mouse: x=%d; y=%d; xrel=%d; yrel=%d; btn=#%02X\n", x, y, xrel, yrel, buttons);
736 if (!msCursorDrawn && vidWindowActivated && SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
737 #if 0
738 zxKMouseDXAccum += xrel;
739 zxKMouseDYAccum += yrel;
740 int speed = zxKMouseSpeed;
741 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
742 xrel = yrel = 0;
743 while (zxKMouseDXAccum >= speed) { xrel += 1; zxKMouseDXAccum -= speed; }
744 while (zxKMouseDXAccum <= -speed) { xrel -= 1; zxKMouseDXAccum += speed; }
745 while (zxKMouseDYAccum >= speed) { yrel += 1; zxKMouseDYAccum -= speed; }
746 while (zxKMouseDYAccum <= -speed) { yrel -= 1; zxKMouseDYAccum += speed; }
747 zxKMouseDX = (((int)zxKMouseDX+xrel)&0xff);
748 zxKMouseDY = (((int)zxKMouseDY-yrel)&0xff);
749 #elif 0
750 //int lp = (y+24)*352+16+zxScreenOfs;
751 int dx = 0, dy = 0;
752 if (zxMouseWasMoved) {
753 dx = x-zxMouseLastX;
754 dy = y-zxMouseLastY;
755 } else {
756 zxMouseLastX = x;
757 zxMouseLastY = y;
758 zxMouseWasMoved = 1;
760 int speed = zxKMouseSpeed;
761 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
762 dx /= speed;
763 dy /= speed;
764 if (dx != 0) {
765 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
766 zxMouseLastX = x;
768 if (dy != 0) {
769 zxKMouseDY = (((int)zxKMouseDY-dy)&0xff);
770 zxMouseLastY = y;
772 #else
773 if (!zxMouseWasMoved) {
774 zxMouseLastX = zxMouseLastY = 0;
775 zxMouseFracX = zxMouseFracY = 0;
776 zxMouseWasMoved = 1;
777 } else {
778 int speed = zxKMouseSpeed;
779 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
780 zxMouseFracX += xrel;
781 zxMouseFracY -= yrel;
782 int dx = 0;
783 int dy = 0;
784 while (zxMouseFracX <= -speed) { --dx; zxMouseFracX += speed; }
785 while (zxMouseFracX >= speed) { ++dx; zxMouseFracX -= speed; }
786 while (zxMouseFracY <= -speed) { --dy; zxMouseFracY += speed; }
787 while (zxMouseFracY >= speed) { ++dy; zxMouseFracY -= speed; }
788 //if (abs(dx) >= 6) { if (dx < 0) dx -= (-dx)/2; else dx += dx/2; }
789 //if (abs(dy) >= 6) { if (dy < 0) dy -= (-dy)/2; else dy += dy/2; }
790 if (dx && zxMouseFracX <= 2) zxMouseFracX = 0;
791 if (dy && zxMouseFracY <= 2) zxMouseFracY = 0;
792 if (abs(dx) >= 6) dx *= 2;
793 if (abs(dy) >= 6) dy *= 2;
794 zxMouseLastX += dx;
795 zxMouseLastY += dy;
796 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
797 zxKMouseDY = (((int)zxKMouseDY+dy)&0xff);
799 #endif
800 zxMouseSetButtons(buttons);
801 } else {
802 zxMouseWasMoved = 0;
807 static void zxMouseButtonCB (int x, int y, int btn, int buttons) {
808 if (/*widgetsProcessMouse(x, y, btn, buttons) ||*/ msCursorDrawn) return;
809 if (!msCursorDrawn && vidWindowActivated) {
810 //fprintf(stderr, "buttons=0x%02x\n", buttons);
811 if (optKMouse && buttons == 0 && btn == (MS_BUTTON_LEFT|MS_BUTTON_DEPRESSED) &&
812 SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
814 SDL_WM_GrabInput(SDL_GRAB_ON);
815 emuRealizeRealMouseCursorState();
816 zxMouseWasMoved = 0;
817 return; // ignore this click
819 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
820 zxMouseSetButtons(buttons);
826 ////////////////////////////////////////////////////////////////////////////////
827 static void zxKeyCB (SDL_KeyboardEvent *key) {
828 if (conVisible && conKeyEvent(key)) return;
829 //if (msCursorDrawn) return;
830 if (debuggerActive && dbgKeyEvent(key)) return;
831 if (uiovlKey(key)) return;
832 if (key->type == SDL_KEYDOWN) {
833 SDLJimBinding *bind;
834 if ((bind = sdlFindKeyBind(sdlJimBindings, key->keysym.sym, key->keysym.mod)) != NULL && bind->action != NULL) {
835 Jim_Obj *eres = NULL/*, *dupl*/;
836 // duplicate 'action', 'cause Jim can return the same shared object
837 // if there is nothing to 'expand' in it; it's safe to omit
838 // duplication here, but future versions of Jim can check if the
839 // object is 'shared' here, so let's do it right
840 //!dupl = Jim_DuplicateObj(jim, bind->action);
841 //!Jim_IncrRefCount(dupl); // we need to do this after Jim_DuplicateObj()
842 if (Jim_SubstObj(jim, /*dupl*/bind->action, &eres, 0) == JIM_OK) {
843 Jim_IncrRefCount(eres);
844 if (Jim_EvalObjList(jim, eres) != JIM_OK) {
845 Jim_MakeErrorMessage(jim);
846 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
848 Jim_DecrRefCount(jim, eres);
850 //!Jim_DecrRefCount(jim, dupl);
851 return;
855 if (/*vidWindowActivated &&*/ msCursorDrawn) return;
857 if (zxKeyBinds[key->keysym.sym]) {
858 zxDoKeyUpDown(zxKeyBinds[key->keysym.sym]&0xffff, (key->type == SDL_KEYDOWN));
859 zxDoKeyUpDown((zxKeyBinds[key->keysym.sym]>>16)&0xffff, (key->type == SDL_KEYDOWN));
860 return;
865 ////////////////////////////////////////////////////////////////////////////////
866 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
867 #define DECODE_TUPLE(tuple,bytes) \
868 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
869 PUSH_BACK((char)((tuple >> 24)&0xff))
871 // returns ress length
872 static int ascii85Decode (char **ress, const char *srcs/*, int start, int length*/) {
873 static uint32_t pow85[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
874 const uint8_t *data = (const uint8_t *)srcs;
875 int len = (int)strlen(srcs);
876 uint32_t tuple = 0;
877 int count = 0, c = 0;
878 int dpos = 0;
879 int start = 0, length = len;
880 int tmp;
882 if (start < 0) start = 0; else { len -= start; data += start; }
883 if (length < 0 || len < length) length = len;
885 if (length > 0) {
886 int xlen = 4*((length+4)/5);
887 kstringReserve(ress, xlen);
891 *ress = (char *)calloc(1, len+1);
892 for (int f = length; f > 0; --f, ++data) {
893 c = *data;
894 if (c <= ' ') continue; // skip blanks
895 switch (c) {
896 case 'z': // zero tuple
897 if (count != 0) {
898 //fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
899 free(*ress);
900 *ress = NULL;
901 return -1;
903 PUSH_BACK('\0');
904 PUSH_BACK('\0');
905 PUSH_BACK('\0');
906 PUSH_BACK('\0');
907 break;
908 case '~': // '~>': end of sequence
909 if (f < 1 || data[1] != '>') { free(*ress); return -2; } // error
910 if (count > 0) { f = -1; break; }
911 default:
912 if (c < '!' || c > 'u') {
913 //fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
914 free(*ress);
915 return -3;
917 tuple += ((uint8_t)(c-'!'))*pow85[count++];
918 if (count == 5) {
919 DECODE_TUPLE(tuple, 4);
920 count = 0;
921 tuple = 0;
923 break;
926 // write last (possibly incomplete) tuple
927 if (count-- > 0) {
928 tuple += pow85[count];
929 DECODE_TUPLE(tuple, count);
931 return dpos;
934 #undef PUSH_BACK
935 #undef DECODE_TUPLE
938 static void decodeBA (char *str, int len) {
939 char pch = 42;
941 for (int f = 0; f < len; ++f, ++str) {
942 char ch = *str;
944 ch = (ch-f-1)^pch;
945 *str = ch;
946 pch = ch;
951 static void printEC (const char *txt) {
952 char *dest;
953 int len;
955 if ((len = ascii85Decode(&dest, txt)) >= 0) {
956 decodeBA(dest, len);
957 fprintf(stderr, "%s\n", dest);
958 free(dest);
963 static int isStr85Equ (const char *txt, const char *str) {
964 char *dest;
965 int len, res = 0;
967 if ((len = ascii85Decode(&dest, txt)) >= 0) {
968 res = (strcmp(dest, str) == 0);
969 free(dest);
971 return res;
975 static int checkEGG (const char *str) {
976 if (isStr85Equ("06:]JASq", str) || isStr85Equ("0/i", str)) {
977 printEC(
978 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
979 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
980 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
981 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
982 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
983 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
984 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
985 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
986 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
987 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
988 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
989 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
990 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
992 return 1;
994 if (isStr85Equ("04Jj?B)", str)) {
995 printEC(
996 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
997 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
998 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
999 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
1001 return 1;
1003 if (isStr85Equ("04o69A7Tr", str)) {
1004 printEC(
1005 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
1006 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
1007 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
1009 return 1;
1011 return 0;
1015 static void addBoots (int simpleAutorun) {
1016 for (int f = 0; f < 4; ++f) {
1017 if ((snapWasDisk&(1<<f)) && !(snapWasCPCDisk&(1<<f))) addAutoBoot(f, 1, simpleAutorun);
1019 snapWasDisk = 0;
1020 snapWasCPCDisk = 0;
1024 typedef enum {
1025 ARUN_UNDEFINED,
1026 ARUN_NONE,
1027 ARUN_TRDOS,
1028 ARUN_TRDOS48,
1029 ARUN_TRDOS128,
1030 ARUN_TRDOSPENT,
1031 ARUN_TAP,
1032 ARUN_TAP48,
1033 ARUN_TAP128,
1034 ARUN_TAPPENT,
1035 ARUN_TAPP2A,
1036 ARUN_TAPP3,
1037 ARUN_P3DOS2A,
1038 ARUN_P3DOS3,
1039 } cli_arun_e;
1042 static cli_arun_e cli_autorun = ARUN_UNDEFINED;
1044 static void show_help (void) {
1045 printf(
1046 "options:\n"
1047 " --48 use 48k model\n"
1048 " --128 use 128k model\n"
1049 " -A --no-autorun don't autorun file\n"
1051 exit(0);
1055 static char *strappend (char *s, const char *s1) {
1056 if (!s) s = strdup("");
1057 if (!s1 || !s1[0]) return s;
1058 char *res = strprintf("%s%s", s, s1);
1059 free(s);
1060 return res;
1064 enum {
1065 CLI_MODEL_AUTO,
1066 CLI_MODEL_48,
1067 CLI_MODEL_128,
1068 CLI_MODEL_PENT,
1069 CLI_MODEL_PLUS2A,
1070 CLI_MODEL_PLUS3,
1074 static void processOptions (int argc, char *argv[], int onlydbg) {
1075 int nomoreopts = 0, oldABoot = optAutoaddBoot;
1076 int do48 = CLI_MODEL_AUTO;
1077 optAutoaddBoot = 0;
1078 snapWasDisk = 0;
1079 snapWasCPCDisk = 0;
1080 snapWasTape = 0;
1081 for (int f = 1; f < argc; ++f) {
1082 if (checkEGG(argv[f])) exit(1);
1083 if (!nomoreopts) {
1084 if (strcmp(argv[f], "--") == 0) { nomoreopts = 1; continue; }
1085 if (strcmp(argv[f], "+") == 0) continue; // console command separator
1086 if (argv[f][0] == '+') {
1087 if (onlydbg) continue;
1088 optAutoaddBoot = oldABoot;
1089 if (strchr(argv[f], ' ') != NULL) {
1090 conExecute(argv[f]+1, 0);
1091 } else {
1092 // collect console command
1093 char *cmd = strdup(argv[f]+1);
1094 for (++f; f < argc; ++f) {
1095 if (argv[f][0] == '+') break;
1096 if (argv[f][0] == '-' && argv[f][1] == '-') break;
1097 cmd = strappend(cmd, " ");
1098 cmd = strappend(cmd, argv[f]);
1100 --f; // compensate 'for'
1101 conExecute(cmd, 0);
1102 free(cmd);
1104 if (oldABoot != optAutoaddBoot) {
1105 if (oldABoot) addBoots(0);
1106 oldABoot = optAutoaddBoot;
1107 snapWasDisk = 0;
1108 snapWasCPCDisk = 0;
1110 optAutoaddBoot = 0;
1111 continue;
1113 if (argv[f][0] == '-') {
1114 if (argv[f][1] == '-') {
1115 if (strcmp(argv[f], "--help") == 0) show_help();
1116 else if (strcmp(argv[f], "--48") == 0) do48 = CLI_MODEL_48;
1117 else if (strcmp(argv[f], "--128") == 0) do48 = CLI_MODEL_128;
1118 else if (strcmp(argv[f], "--pent") == 0 || strcmp(argv[f], "--pentagon") == 0) do48 = CLI_MODEL_PENT;
1119 else if (strcmp(argv[f], "--plus2a") == 0) do48 = CLI_MODEL_PLUS2A;
1120 else if (strcmp(argv[f], "--plus3") == 0) do48 = CLI_MODEL_PLUS3;
1121 else if (strcmp(argv[f], "--no-autorun") == 0) cli_autorun = ARUN_NONE;
1122 else if (strcmp(argv[f], "--opense") == 0) {
1123 if (!onlydbg) {
1124 if (!optOpenSE) { optOpenSE = 1; emuSetModel(zxModel, 1); }
1127 else if (strcmp(argv[f], "--usock") == 0 || strcmp(argv[f], "--usocket") == 0) {
1128 ++f;
1129 if (f >= argc) { fprintf(stderr, "option '%s' expects socket name!\n", argv[f-1]); exit(1); }
1130 if (!onlydbg) unixsock_start_server(argv[f]);
1132 else if (strcmp(argv[f], "--unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(1); }
1133 else if (strcmp(argv[f], "--no-unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(0); }
1134 else { fprintf(stderr, "unknown command line option: '%s'\n", argv[f]); exit(1); }
1135 continue;
1137 for (const char *a = argv[f]+1; *a; ++a) {
1138 switch (*a) {
1139 case 'h': show_help(); break;
1140 case 'A': cli_autorun = ARUN_NONE; break;
1141 case 'D':
1142 if (onlydbg) {
1143 fprintf(stderr, "console dump enabled\n");
1144 optConDump = 1;
1146 break;
1147 case 'q': /* do not dump consote text to stdout */
1148 optConDumpToStdout = 0;
1149 break;
1150 case 'S':
1151 if (onlydbg) {
1152 fprintf(stderr, "sound debug enabled\n");
1153 optSndSyncDebug = 1;
1155 break;
1156 default: fprintf(stderr, "unknown command line option: '%c'\n", *a); exit(1);
1159 continue; // jff
1162 if (onlydbg) continue;
1163 // snapshot name
1164 if (loadSnapshot(argv[f], SNAPLOAD_ANY) == 0) {
1165 cprintf("'%s' loaded\n", argv[f]);
1166 if (cli_autorun != ARUN_NONE) {
1167 if (snapWasDisk) cli_autorun = (snapWasCPCDisk ? ARUN_P3DOS2A : ARUN_TRDOS);
1168 else if (snapWasTape) cli_autorun = ARUN_TAP;
1169 else cli_autorun = ARUN_UNDEFINED;
1171 //cprintf("wasdisk=%d; wascpc=%d; ar=%d\n", snapWasDisk, snapWasCPCDisk, cli_autorun);
1172 } else {
1173 cprintf("failed to load '%s'\n", argv[f]);
1175 snapWasTape = 0;
1178 optAutoaddBoot = oldABoot;
1179 if (!onlydbg) {
1180 if (oldABoot && !snapWasCPCDisk) addBoots(1);
1181 if (cli_autorun > ARUN_NONE) {
1182 if (cli_autorun != ARUN_P3DOS2A && cli_autorun != ARUN_P3DOS3) {
1183 switch (do48) {
1184 case CLI_MODEL_AUTO:
1185 switch (cli_autorun) {
1186 case ARUN_TAP: cli_autorun = ARUN_TAP48; break;
1187 case ARUN_TRDOS: cli_autorun = ARUN_TRDOSPENT; break;
1188 default: break;
1190 break;
1191 case CLI_MODEL_48:
1192 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1193 break;
1194 case CLI_MODEL_128:
1195 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS128 : ARUN_TAP128);
1196 break;
1197 case CLI_MODEL_PENT:
1198 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOSPENT : ARUN_TAPPENT);
1199 break;
1200 case CLI_MODEL_PLUS2A:
1201 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS2A : ARUN_TAPP2A);
1202 break;
1203 case CLI_MODEL_PLUS3:
1204 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS3 : ARUN_TAPP3);
1205 break;
1206 default:
1207 cprintf("\1UNKNOWN CLI MODEL!\n");
1208 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1209 break;
1212 switch (cli_autorun) {
1213 case ARUN_TRDOS: conExecute("reset trdos", 0); break;
1214 case ARUN_TRDOS48: conExecute("reset 48k trdos", 0); break;
1215 case ARUN_TRDOS128: conExecute("reset 128k trdos", 0); break;
1216 case ARUN_TRDOSPENT: conExecute("reset pentagon 512 trdos", 0); break;
1217 case ARUN_TAP48: conExecute("reset 48k", 0); goto do_tape_autoload;
1218 case ARUN_TAP128: conExecute("reset 128k", 0); goto do_tape_autoload;
1219 case ARUN_TAPPENT: conExecute("reset pentagon 512", 0); goto do_tape_autoload;
1220 case ARUN_TAPP2A: conExecute("reset plus2a", 0); goto do_tape_autoload;
1221 case ARUN_TAPP3: conExecute("reset plus3", 0); goto do_tape_autoload;
1222 case ARUN_P3DOS2A: conExecute("reset plus2a", 0); break;
1223 case ARUN_P3DOS3: conExecute("reset plus3", 0); break;
1224 case ARUN_TAP:
1225 do_tape_autoload:
1226 conExecute("tape _autoload", 0);
1227 break;
1228 default: ;
1235 static void xMainLoop (void) {
1236 const int mcsInFrame = 20*1000;
1237 static int64_t mcsFrameEndWanted;
1238 int eres = 1;
1239 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1240 while (eres >= 0) {
1241 int64_t mcsCurFrameEnd;
1242 eres = processEvents(0);
1243 buildFrame();
1244 unixsock_handle();
1245 mcsCurFrameEnd = timerGetMicroSeconds();
1246 if (mcsCurFrameEnd > 0) {
1247 int mcsSleep = (mcsFrameEndWanted-mcsCurFrameEnd);
1248 //fprintf(stderr, "0: wait=%.15g\n", ((double)mcsSleep)/1000.0);
1249 if (mcsSleep > 0) {
1250 // less than 20 ms
1251 //fprintf(stderr, "SLEEP: %.15g\n", ((double)mcsSleep)/1000.0);
1252 usleep(mcsSleep);
1253 //mcsCurFrameEnd = timerGetMicroSeconds();
1254 //fprintf(stderr, "1:few=%d; cfe=%d; few-cfe=%.15g\n", (int)mcsFrameEndWanted, (int)mcsCurFrameEnd, ((double)(mcsInFrame-(mcsFrameEndWanted-mcsCurFrameEnd)))/1000);
1255 mcsFrameEndWanted += mcsInFrame;
1256 } else {
1257 fprintf(stderr, "DESYNC! (%d)\n", mcsSleep);
1258 //mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1259 mcsFrameEndWanted = mcsCurFrameEnd+mcsInFrame;
1261 } else {
1262 //FIXME
1263 // reinit timer
1264 timerReinit();
1265 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1271 ////////////////////////////////////////////////////////////////////////////////
1272 static void cprintLibFDC (int type, const char *msg) {
1273 switch (type) {
1274 case LIBFDC_MSG_DEBUG: cprintf("\3LIBFDC[debug]: %s\n", msg); break;
1275 case LIBFDC_MSG_WARNING: cprintf("\2LIBFDC[warn]: %s\n", msg); break;
1276 case LIBFDC_MSG_ERROR: cprintf("\4LIBFDC[error]: %s\n", msg); break;
1277 default: cprintf("\3LIBFDC[???]: %s\n", msg); break; // the thing that should not be
1282 ////////////////////////////////////////////////////////////////////////////////
1283 int main (int argc, char *argv[]) {
1284 Jim_SetAllowUnsafeExtensions(1);
1285 tinf_init();
1286 conInit();
1287 initMyDir();
1289 libfdcMessageCB = &cprintLibFDC;
1291 processOptions(argc, argv, 1);
1293 if (libspectrum_init() != LIBSPECTRUM_ERROR_NONE) {
1294 fprintf(stderr, "FATAL: can't init libspectrum!\n");
1295 return 1;
1297 cprintf("===================================\n");
1298 cprintf("using libspectrum v%s\n", libspectrum_version());
1300 switch (timerInit()) {
1301 case TIMER_ERROR: abort();
1302 case TIMER_HPET: break;
1303 default:
1304 cprintf(
1305 "\2WARNING: please, set your clock source to HPET!\n"
1306 "you can do this by issuing the following command:\n"
1307 "======\n"
1308 "\1sudo echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource\n"
1309 "======\n"
1310 "\2this is not a critical issue, but hpet clock will\n"
1311 "\2give you slightly better emulation.\n"
1313 break;
1316 if (!Jim_GetAllowUnsafeExtensions()) {
1317 cprintf("\2WARNING: Disabled unsafe Tcl extensions.\n");
1318 } else {
1319 cprintf("Unsafe Tcl extensions enabled ('--no-unsafe-tcl' to disable).\n");
1322 //zxwinInit();
1323 jimInit();
1325 emuInit();
1326 jimEvalFile("init/init.tcl", 0);
1328 jimEvalFile("init/roms.tcl", 0);
1329 emuSetModel(zxModel, 1);
1331 dbgInit();
1333 emuInitBindings();
1334 jimEvalFile("init/concmd.tcl", 0);
1336 sdlInit();
1337 initVideo();
1339 frameCB = zxFrameCB;
1340 keyCB = zxKeyCB;
1341 mouseCB = zxMouseCB;
1342 mouseButtonCB = zxMouseButtonCB;
1344 //jimEvalFile("init/widgets/init.tcl", 1);
1346 jimEvalFile("autoexec.tcl", 1);
1347 emuSetModel(zxModel, 1); // in case something vital was changed (like "opense on")
1349 processOptions(argc, argv, 0);
1351 jimEvalFile1("./.zxemutrc.tcl");
1352 jimEvalFile1("./.zxemut.tcl");
1353 jimEvalFile1("./zxemutrc.tcl");
1354 jimEvalFile1("./zxemut.tcl");
1356 #ifdef USE_SOUND
1357 if (sndSampleRate < 0 && initSound() != 0) {
1358 fprintf(stderr, "WARNING: can't initialize sound!\n");
1359 cprintf("\4WARNING: can't initialize sound!\n");
1360 sndSampleRate = 0;
1362 sndAllowUseToggle = 0;
1363 if (sndSampleRate <= 0) cprintf("NOSOUND mode");
1365 frameCB = zxFrameCB;
1366 keyCB = zxKeyCB;
1367 mouseCB = zxMouseCB;
1368 mouseButtonCB = zxMouseButtonCB;
1370 uiovlInit();
1372 if (sndSampleRate > 0) {
1373 while (processEvents(0) >= 0) {
1374 buildFrame();
1375 unixsock_handle();
1376 // optMaxSpeed implies sndUsing==0
1377 if (!optMaxSpeed && optSpeed == 100 && (debuggerActive || optPaused)) soundWrite();
1379 } else {
1380 xMainLoop();
1382 #else
1383 sndAllowUseToggle = 0;
1384 xMainLoop();
1385 #endif
1387 unixsock_stop_server();
1388 uiovlDeinit();
1389 //zxwinDeinit();
1390 difDestroy(zxDiskIf);
1391 deinitSound();
1392 jimDeinit();
1393 if (condumpfl != NULL) fclose(condumpfl);
1394 return 0;