emu: it is now possible to activate the console from memview and sprview
[zymosis.git] / src / ZXEmuT / main.c
bloba36506a2e3305bef1c34360690947b85257f6cf1
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2022 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"
54 ////////////////////////////////////////////////////////////////////////////////
55 #include "emucommon.h"
56 #include "../libfusefdc/libfusefdc.h"
57 #include "emuvars.h"
58 #include "emuutils.h"
59 #include "emuexec.h"
60 #include "libvideo/video.h"
61 #include "debugger.h"
62 #include "memview.h"
63 #include "sprview.h"
64 #include "console.h"
65 #include "zxscrdraw.h"
66 #include "machines.h"
67 #include "snd_alsa.h"
68 #include "tapes.h"
69 #include "lssnap.h"
70 #include "zxkeyinfo.h"
72 #include "jimapi.h"
74 #include "keyled.h"
75 #include "diskled.h"
78 ////////////////////////////////////////////////////////////////////////////////
79 extern const unsigned char keyHelpScr[];
82 ////////////////////////////////////////////////////////////////////////////////
83 char binMyDir[8192];
86 static void initMyDir (void) {
87 if (readlink("/proc/self/exe", binMyDir, sizeof(binMyDir)-1) < 0) {
88 strcpy(binMyDir, ".");
89 } else {
90 char *p = (char *)strrchr(binMyDir, '/');
92 if (p == NULL) strcpy(binMyDir, "."); else *p = '\0';
97 ////////////////////////////////////////////////////////////////////////////////
98 const char *snapshotExtensions[] = {
99 ".z80",
100 ".sna",
101 ".snp",
102 ".sp",
103 ".szx",
104 ".slt",
105 ".zxs",
106 ".tap",
107 ".tzx",
108 ".spc",
109 ".sta",
110 ".ltp",
111 ".pzx",
112 ".trd",
113 ".scl",
114 ".fdi",
115 ".udi",
116 ".td0",
117 ".dsk",
118 ".dmb",
119 ".rzx",
120 ".zip",
121 NULL
125 ////////////////////////////////////////////////////////////////////////////////
126 typedef struct PortHandlers {
127 int machine; // ZX_MACHINE_MAX: any
128 uint16_t mask;
129 uint16_t value;
130 int (*portInCB) (zym_cpu_t *z80, uint16_t port, uint8_t *res);
131 int (*portOutCB) (zym_cpu_t *z80, uint16_t port, uint8_t value);
132 } PortHandlers;
135 #define MAX_PORTHANDLER (64)
136 static PortHandlers portHandlers[MAX_PORTHANDLER];
137 static int phCount = 0;
140 static void phAdd (const PortHandlers *ph) {
141 if (ph != NULL) {
142 while (ph->portInCB || ph->portOutCB) {
143 if (phCount >= MAX_PORTHANDLER) { fprintf(stderr, "FATAL: too many port handlers!\n"); abort(); }
144 portHandlers[phCount++] = *ph++;
150 ////////////////////////////////////////////////////////////////////////////////
151 #include "zymcb.c"
154 ////////////////////////////////////////////////////////////////////////////////
155 static int usock_stdin = 0;
156 static int usock_srv = -1;
157 static int usock_client = -1;
158 static char usock_command[1024];
159 static size_t usock_command_pos = 0;
162 static size_t unixsock_create_address (struct sockaddr_un *addr, const char *name) {
163 if (!addr || !name || !name[0] || strlen(name) > 100) return 0;
164 memset((void *)addr, 0, sizeof(*addr));
165 addr->sun_family = AF_UNIX;
166 addr->sun_path[0] = 0; /* abstract unix socket */
167 strcpy(addr->sun_path+1, name);
168 return strlen(addr->sun_path+1)+sizeof(addr->sun_family)+1u;
172 static void sock_make_non_blocking (int fd) {
173 if (fd >= 0) {
174 int flags = fcntl(fd, F_GETFL, 0);
175 flags |= O_NONBLOCK;
176 fcntl(fd, F_SETFL, flags);
181 static void unixsock_drop_client (void) {
182 if (usock_client >= 0) {
183 close(usock_client);
184 usock_client = -1;
186 usock_command_pos = 0;
190 static void unixsock_stop_server (void) {
191 unixsock_drop_client();
192 if (usock_srv >= 0) {
193 close(usock_srv);
194 usock_srv = -1;
199 static void unixsock_start_server (const char *name) {
200 unixsock_stop_server();
202 /* use stdin */
203 if (name && strcmp(name, "-") == 0) {
204 if (usock_stdin) {
205 cprintf("\4ERROR: stdin already closed!\n");
206 usock_srv = -1;
207 return;
209 usock_srv = -1;
210 usock_client = 0;
211 usock_stdin = 1;
212 sock_make_non_blocking(usock_srv);
213 return;
216 usock_srv = socket(AF_UNIX, SOCK_STREAM, 0);
217 if (usock_srv < 0) {
218 cprintf("\4ERROR: cannot create server socket!\n");
219 usock_srv = -1;
220 return;
222 sock_make_non_blocking(usock_srv);
224 struct sockaddr_un serv_addr;
225 size_t servlen = unixsock_create_address(&serv_addr, name);
226 if (bind(usock_srv, (struct sockaddr *)&serv_addr, servlen) < 0) {
227 cprintf("\4ERROR: cannot bind server socket!\n");
228 close(usock_srv);
229 usock_srv = -1;
230 return;
233 if (listen(usock_srv, 1) < 0) {
234 cprintf("\4ERROR: cannot listen on server socket!\n");
235 close(usock_srv);
236 usock_srv = -1;
237 return;
240 cprintf("\1created unix server socket '%s'\n", name);
244 static void unixsock_handle (void) {
245 if (usock_srv < 0 && usock_client < 0) return;
246 struct timeval tout;
247 fd_set rds;
248 tout.tv_sec = 0;
249 tout.tv_usec = 0;
250 /* can accept new client? */
251 if (usock_client < 0) {
252 /* yeah, check for new client */
253 FD_ZERO(&rds);
254 FD_SET(usock_srv, &rds);
255 int res = select(usock_srv+1, &rds, NULL, NULL, &tout);
256 if (res <= 0) return; /* nothing */
257 /* accept new client */
258 struct sockaddr_un cli_addr;
259 socklen_t clilen = (socklen_t)sizeof(cli_addr);
260 int newsockfd = accept(usock_srv, (struct sockaddr *)&cli_addr, &clilen);
261 if (newsockfd < 0) {
262 cprintf("\4ERROR: error accepting client connection!\n");
263 return;
265 usock_client = newsockfd;
266 cprintf("\1USOCK: accepted new client\n");
268 /* check if we can read from client socket */
269 for (int maxread = 4096; maxread > 0; --maxread) {
270 char ch;
271 FD_ZERO(&rds);
272 FD_SET(usock_client, &rds);
273 int res = select(usock_client+1, &rds, NULL, NULL, &tout);
274 if (res <= 0) break; /* nothing */
275 ssize_t rd = read(usock_client, &ch, 1);
276 if (rd == 0) {
277 cprintf("\2USOCK: client closed the connection\n");
278 unixsock_drop_client();
279 return;
281 if (rd < 0) {
282 cprintf("\4ERROR: error reading from client connection!\n");
283 unixsock_drop_client();
284 return;
286 if (usock_command_pos >= sizeof(usock_command)-2) {
287 cprintf("\4ERROR: too long command from client!\n");
288 unixsock_drop_client();
289 return;
291 if (ch == '\n') ch = 0;
292 usock_command[usock_command_pos++] = ch;
293 if (!ch) {
294 /* command complete */
295 usock_command_pos = 0;
296 char *cmd = strprintf("::usock_received %s", usock_command);
297 if (Jim_Eval(jim, cmd) == JIM_OK) {
298 free(cmd);
299 Jim_Obj *res = Jim_GetResult(jim);
300 const char *resstr = Jim_String(res);
301 if (resstr && strcasecmp(resstr, "close") == 0) {
302 cprintf("closing client connection (client request).\n");
303 unixsock_drop_client();
304 return;
306 if (resstr && strcasecmp(resstr, "done") == 0) {
307 continue;
309 } else {
310 free(cmd);
311 Jim_MakeErrorMessage(jim);
312 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
314 /* close client connection? */
316 if (strcmp(usock_command, "close") == 0) {
317 cprintf("closing client connection (client request).\n");
318 unixsock_drop_client();
319 return;
322 cprintf("\2USOCK COMMAND:<%s>\n", usock_command);
323 conExecute(usock_command, 0);
324 continue;
330 ////////////////////////////////////////////////////////////////////////////////
331 static inline void zxDoKeyUpDown (uint16_t portmask, int isdown) {
332 if (portmask != 0) {
333 if (isdown) {
334 zxKeyboardState[(portmask>>8)&0xff] &= ~(portmask&0xff);
335 } else {
336 zxKeyboardState[(portmask>>8)&0xff] |= (portmask&0xff);
342 ////////////////////////////////////////////////////////////////////////////////
343 static void emuInitMemory (void) {
344 zxMaxMemoryBank = 64;
345 for (int f = 0; f < zxMaxMemoryBank; ++f) {
346 if ((zxMemoryBanks[f] = calloc(1, 16384)) == NULL) {
347 fprintf(stderr, "FATAL: out of memory!\n");
348 abort();
352 zxMaxROMMemoryBank = 16;
353 for (int f = 0; f < zxMaxROMMemoryBank; ++f) {
354 if ((zxROMBanks[f] = calloc(1, 16384)) == NULL) {
355 fprintf(stderr, "FATAL: out of memory!\n");
356 abort();
362 static void emuInit (void) {
363 int lineNo = 0;
365 for (int t = 0; t < 3; ++t) {
366 for (int y = 0; y < 8; ++y) {
367 for (int z = 0; z < 8; ++z) {
368 zxScrLineOfs[lineNo++] = (t<<11)|(z<<8)|(y<<5);
373 memset(zxKeyBinds, 0, sizeof(zxKeyBinds));
374 memset(zxScreen, 0, sizeof(zxScreen));
375 memset(zxUlaSnow, 0, sizeof(zxUlaSnow));
377 zxScreenCurrent = 0;
378 zxWasUlaSnow = 0;
379 zxBorder = 0;
380 zxScreenBank = NULL;
381 zxFlashTimer = 0;
382 zxFlashState = 0;
383 zxOldScreenTS = 0;
384 emuFrameCount = 0;
385 emuFrameStartTime = 0;
386 emuLastFPSText[0] = '\0';
388 emuInitMemory();
389 emuAddPortHandlers();
391 soundAYReset();
393 emuInitDisks();
395 zym_init(&z80);
396 //zym_clear_callbacks(&z80);
397 z80.mem_read = z80MemRead;
398 z80.mem_write = z80MemWrite;
399 z80.mem_contention = z80Contention;
400 z80.port_read = z80PortIn;
401 z80.port_write = z80PortOut;
402 z80.port_contention = z80PortContention;
403 z80.trap_ed = z80EDTrap;
404 z80.evenM1 = machineInfo.evenM1;
408 ////////////////////////////////////////////////////////////////////////////////
409 // defattr < 0: use screen$ attr
410 static void paintZXScr (int x0, int y0, const void *ptr, int defattr) {
411 const uint8_t *buf = (const uint8_t *)ptr;
413 for (int y = 0; y < 192; ++y) {
414 int scrA = zxScrLineOfs[y], attrA = 6144+(y/8)*32;
416 for (int x = 0; x < 32; ++x) {
417 uint8_t bt = buf[scrA++];
418 uint8_t attr = (defattr >= 0 && defattr <= 255 ? defattr : buf[attrA++]);
419 uint8_t paper = (attr>>3)&0x0f, ink = (attr&0x07)+(paper&0x08);
421 if (attr&0x80 && zxFlashState) { uint8_t t = paper; paper = ink; ink = t; }
422 for (int f = 0; f < 8; ++f) {
423 putPixel(x0+x*8+f, y0+y, (bt&0x80 ? ink : paper));
424 bt = (bt&0x7f)<<1;
431 static void paintZXScrZ (int x0, int y0) {
432 static uint8_t scr[6912];
433 unsigned int outlen = 6912;
434 static int unpacked = 0;
436 if (!unpacked) {
437 if (tinf_zlib_uncompress(scr, &outlen, keyHelpScr, 2153) == TINF_OK && outlen == 6912) {
438 unpacked = 1;
439 } else {
440 return;
443 if (unpacked) {
444 clearScreen(0);
445 paintZXScr(x0, y0, scr, -1);
450 static void emuDrawFPS (void) {
451 if (emuFrameStartTime > 0 && emuFrameCount) {
452 int64_t tt = timerGetMS();
453 if (tt-emuFrameStartTime >= 1000) {
454 int fps = emuFrameCount/((tt-emuFrameStartTime)/1000.0)+0.5;
455 if (fps < 0) fps = 0;
456 //sprintf(emuLastFPSText, "%.15f %d %3d%%", ((double)emuFrameCount/((double)(tt-emuFrameStartTime)/1000)), fps, fps*100/50);
457 sprintf(emuLastFPSText, "%d %3d%%", fps, fps*100/50);
458 emuFrameStartTime = tt;
459 emuFrameCount = 0;
462 if (emuLastFPSText[0]) {
463 drawStr6Outline(emuLastFPSText, frameSfc->w-6*strlen(emuLastFPSText)-2, 2, 12, 0);
468 ////////////////////////////////////////////////////////////////////////////////
469 static void zxPaintOverlaysAndLeds (void) {
470 // key help
471 if (optKeyHelpVisible) paintZXScrZ(32-zxScreenOfs, 24);
473 // various leds
474 if (optDrawKeyLeds) keyledBlit(1, frameSfc->h-15);
475 if (optTapePlaying) emuTapeDrawCounter();
476 diskBlit(frameSfc->w-26, frameSfc->h-26);
478 // "max speed" mark
479 if (optMaxSpeed) drawStr6Outline("max", frameSfc->w-3*6-2, frameSfc->h-10, 12, 0);
481 // "paused" or FPS
482 if (optPaused) {
483 //drawStr8Outline("paused", frameSfc->w-6*8-2, 2, 12, 0);
484 int w = drawStrZ("paused", 0, 0, 255, 255);
485 drawStrZ("paused", 320-w-1, 0, 66, 67);
486 } else if (optDrawFPS) {
487 emuDrawFPS();
490 // "late timings" mark
491 if (zxLateTimings) drawStr6Outline("LT", 2, 2, 12, 0);
493 // ui overlays
494 uiovlDraw();
496 // debugger window
497 if (debuggerActive) dbgDraw();
499 // memory view window
500 if (memviewActive) memviewDraw();
502 // sprite view window
503 if (sprviewActive) sprviewDraw();
505 const int otherTextActive = (debuggerActive || memviewActive || sprviewActive);
507 // console
508 if (conVisible) {
509 if (!otherTextActive) {
510 vid_textsrc_height = CON_HEIGHT;
511 vt_cls(32, 0x07);
513 conDraw();
514 } else {
515 vid_textsrc_height = 0;
517 if (debuggerActive && !dbgIsHidden()) vid_textsrc_height = 0;
519 vid_textscr_active = ((debuggerActive && !dbgIsHidden()) || conVisible || memviewActive || sprviewActive);
521 conDrawMessage();
523 // rzx progress
524 if (zxRZX) {
525 static char buf[1024];
526 Uint8 fg = ((zxBorder&0x07) < 6 ? 15 : 0);
527 if (zxRZXFrames > 0) {
528 int prc = 100*zxRZXCurFrame/zxRZXFrames;
529 int secs = (int)((zxRZXFrames-zxRZXCurFrame)/(50.0*optSpeed/100.0));
530 if (prc > 100) prc = 100;
531 if (secs < 0) secs = 0;
532 sprintf(buf, "RZX:%3d%% (%d:%02d left)", prc, secs/60, secs%60);
533 } else {
534 strcpy(buf, "RZX");
536 drawStr6(buf, 1, 1, fg, 255);
539 Jim_CollectIfNeeded(jim);
543 static void zxPaintZXScreen (void) {
544 for (int y = 0; y < 240; ++y) {
545 int lp = (y+24)*352+16+zxScreenOfs;
546 // the real Speccy screen is not 'centered', as aowen says; 10px seems to be the right value
547 if (optNoFlic > 0 || optNoFlicDetected) {
548 Uint8 *d = (((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
549 for (int x = 0; x < 320; ++x, ++lp, d += 4) {
550 //putPixelMix(x, y, zxScreen[zxScreenCurrent][lp], zxScreen[zxScreenCurrent^1][lp]);
551 Uint8 c0 = zxScreen[zxScreenCurrent][lp], c1 = zxScreen[zxScreenCurrent^1][lp];
552 d[vidRIdx] = (palRGB[c0][0]+palRGB[c1][0])/2;
553 d[vidGIdx] = (palRGB[c0][1]+palRGB[c1][1])/2;
554 d[vidBIdx] = (palRGB[c0][2]+palRGB[c1][2])/2;
556 } else {
557 Uint32 *dst = (Uint32 *)(((Uint8 *)frameSfc->pixels)+(y*frameSfc->pitch));
558 if (optSlowShade && optSpeed < 100) {
559 int shadeStart = zxScreenLineShadeStart(z80.tstates);
560 if (lp >= shadeStart) {
561 // the whole line is shaded
562 for (int x = 0; x < 320; ++x) {
563 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
564 //*dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
566 } else if (lp < shadeStart && lp+319 >= shadeStart) {
567 // line part is shaded
568 for (int x = 0; x < 320; ++x) {
569 if (lp < shadeStart) {
570 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
571 } else {
572 putPixelMix(x, y, zxScreen[zxScreenCurrent][lp++], 0);
575 } else {
576 // not shaded at all
577 for (int x = 0; x < 320; ++x) {
578 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
579 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
582 } else {
583 for (int x = 0; x < 320; ++x) {
584 //putPixel(x, y, zxScreen[zxScreenCurrent][lp++]);
585 *dst++ = palette[zxScreen[zxScreenCurrent][lp++]];
593 static void zxPaintScreen (void) {
594 zxPaintZXScreen();
595 zxPaintOverlaysAndLeds();
599 ////////////////////////////////////////////////////////////////////////////////
600 static int msCursorDrawn = 0;
601 static int msRealCursorVisible = 1;
602 static int msLastGrabState = 0;
605 void emuHideRealMouseCursor (void) {
606 if (msRealCursorVisible) {
607 SDL_ShowCursor(0);
608 msRealCursorVisible = 0;
613 void emuShowRealMouseCursor (void) {
614 if (!msRealCursorVisible) {
615 SDL_ShowCursor(1);
616 msRealCursorVisible = 1;
621 void emuRealizeRealMouseCursorState (void) {
622 if (optFullscreen) {
623 emuHideRealMouseCursor();
624 if (!msLastGrabState) {
625 msLastGrabState = 1;
626 zxMouseWasMoved = 0;
627 zxMouseLastX = zxMouseLastY = 0;
628 zxMouseFracX = zxMouseFracY = 0;
630 } else if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
631 // no grab
632 if (!msCursorDrawn) emuShowRealMouseCursor(); else emuHideRealMouseCursor();
633 msLastGrabState = 0;
634 } else {
635 // grab
636 emuHideRealMouseCursor();
637 if (!msLastGrabState) {
638 msLastGrabState = 1;
639 zxMouseWasMoved = 0;
640 zxMouseLastX = zxMouseLastY = 0;
641 zxMouseFracX = zxMouseFracY = 0;
647 void emuFullScreenChanged (void) {
648 emuRealizeRealMouseCursorState();
652 static void zxFrameCB (void) {
653 if (!debuggerActive && !optPaused) {
654 if (optMaxSpeed) {
655 // doing maxspeed
656 const int msscroff = (optMaxSpeedBadScreen || optMaxSpeedBadScreenTape);
657 if (msscroff) ++optNoScreenReal;
658 const int64_t stt = timerGetMS();
659 do {
660 while (!debuggerActive && !optPaused && !z80_was_frame_end) {
661 z80Step();
663 z80_was_frame_end = 0;
664 } while (!debuggerActive && !optPaused && timerGetMS()-stt < 20);
665 // redraw screen; prevent border flicking
666 if (msscroff) {
667 --optNoScreenReal;
668 zxOldScreenTS = 0;
669 zxRealiseScreen(machineInfo.tsperframe);
670 } else {
671 zxRealiseScreen(z80.tstates);
673 } else if (optSpeed != 100) {
674 // not 100% speed
675 int tickstodo = machineInfo.tsperframe*optSpeed/100+1;
676 const int64_t stt = timerGetMS();
677 if (emuPrevFCB == 0) {
678 emuPrevFCB = stt;
679 } else {
680 if (stt-emuPrevFCB < 20) SDL_Delay(20-(stt-emuPrevFCB));
681 emuPrevFCB = timerGetMS();
683 if (optSpeed < 100) {
684 // slower than the original
685 //fprintf(stderr, "speed=%d; tstates=%d; todo=%d; end=%d; frame=%d\n", optSpeed, z80.tstates, tickstodo, z80.tstates+tickstodo, machineInfo.tsperframe);
686 while (tickstodo > 0 && !debuggerActive && !optPaused) {
687 tickstodo -= z80Step();
689 z80_was_frame_end = 0;
690 } else {
691 // faster than the original
692 int nomore = 0;
693 // do incomplete frame
694 while (!z80_was_frame_end && !debuggerActive && !optPaused) {
695 tickstodo -= z80Step();
697 z80_was_frame_end = 0;
698 // execute full frames, checking for timeout
699 while (tickstodo >= machineInfo.tsperframe && !debuggerActive && !optPaused) {
700 // do one frame
701 while (!z80_was_frame_end && !debuggerActive && !optPaused) {
702 tickstodo -= z80Step();
704 z80_was_frame_end = 0;
705 if (timerGetMS()-stt >= 20) { nomore = 1; break; }
707 // do the rest
708 if (!nomore) {
709 //while (tickstodo > 0 && !debuggerActive && !optPaused && timerGetMS()-stt <= 20) tickstodo -= z80Step();
710 while (tickstodo > 0 && !debuggerActive && !optPaused) {
711 tickstodo -= z80Step();
715 z80_was_frame_end = 0;
716 } else {
717 // normal, do one frame till end
718 while (!debuggerActive && !optPaused && !z80_was_frame_end) {
719 z80Step();
721 if (z80_was_frame_end) {
722 z80_was_frame_end = 0;
723 if (!debuggerActive && !optPaused) {
724 // normal execution; draw frame, play sound
725 zxPaintScreen();
726 forcedShowFrame();
727 soundWrite();
728 return;
733 zxRealiseScreen(z80.tstates);
734 zxPaintScreen();
738 ////////////////////////////////////////////////////////////////////////////////
739 static void zxMouseSetButtons (int buttons) {
740 if (buttons&MS_BUTTON_WHEELUP) zxKMouseWheel = ((int)zxKMouseWheel-1)&0x0f;
741 if (buttons&MS_BUTTON_WHEELDOWN) zxKMouseWheel = ((int)zxKMouseWheel+1)&0x0f;
742 zxKMouseButtons = buttons&(MS_BUTTON_LEFT|MS_BUTTON_RIGHT|MS_BUTTON_MIDDLE);
746 static void zxMouseCB (int x, int y, int xrel, int yrel, int buttons) {
747 if (/*widgetsProcessMouse(x, y, 0, buttons) ||*/ msCursorDrawn) return;
748 //fprintf(stderr, "mouse: x=%d; y=%d; xrel=%d; yrel=%d; btn=#%02X\n", x, y, xrel, yrel, buttons);
749 if (!msCursorDrawn && vidWindowActivated && SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
750 #if 0
751 zxKMouseDXAccum += xrel;
752 zxKMouseDYAccum += yrel;
753 int speed = zxKMouseSpeed;
754 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
755 xrel = yrel = 0;
756 while (zxKMouseDXAccum >= speed) { xrel += 1; zxKMouseDXAccum -= speed; }
757 while (zxKMouseDXAccum <= -speed) { xrel -= 1; zxKMouseDXAccum += speed; }
758 while (zxKMouseDYAccum >= speed) { yrel += 1; zxKMouseDYAccum -= speed; }
759 while (zxKMouseDYAccum <= -speed) { yrel -= 1; zxKMouseDYAccum += speed; }
760 zxKMouseDX = (((int)zxKMouseDX+xrel)&0xff);
761 zxKMouseDY = (((int)zxKMouseDY-yrel)&0xff);
762 #elif 0
763 //int lp = (y+24)*352+16+zxScreenOfs;
764 int dx = 0, dy = 0;
765 if (zxMouseWasMoved) {
766 dx = x-zxMouseLastX;
767 dy = y-zxMouseLastY;
768 } else {
769 zxMouseLastX = x;
770 zxMouseLastY = y;
771 zxMouseWasMoved = 1;
773 int speed = zxKMouseSpeed;
774 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
775 dx /= speed;
776 dy /= speed;
777 if (dx != 0) {
778 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
779 zxMouseLastX = x;
781 if (dy != 0) {
782 zxKMouseDY = (((int)zxKMouseDY-dy)&0xff);
783 zxMouseLastY = y;
785 #else
786 if (!zxMouseWasMoved) {
787 zxMouseLastX = zxMouseLastY = 0;
788 zxMouseFracX = zxMouseFracY = 0;
789 zxMouseWasMoved = 1;
790 } else {
791 int speed = zxKMouseSpeed;
792 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
793 zxMouseFracX += xrel;
794 zxMouseFracY -= yrel;
795 int dx = 0;
796 int dy = 0;
797 while (zxMouseFracX <= -speed) { --dx; zxMouseFracX += speed; }
798 while (zxMouseFracX >= speed) { ++dx; zxMouseFracX -= speed; }
799 while (zxMouseFracY <= -speed) { --dy; zxMouseFracY += speed; }
800 while (zxMouseFracY >= speed) { ++dy; zxMouseFracY -= speed; }
801 //if (abs(dx) >= 6) { if (dx < 0) dx -= (-dx)/2; else dx += dx/2; }
802 //if (abs(dy) >= 6) { if (dy < 0) dy -= (-dy)/2; else dy += dy/2; }
803 if (dx && zxMouseFracX <= 2) zxMouseFracX = 0;
804 if (dy && zxMouseFracY <= 2) zxMouseFracY = 0;
805 if (abs(dx) >= 6) dx *= 2;
806 if (abs(dy) >= 6) dy *= 2;
807 zxMouseLastX += dx;
808 zxMouseLastY += dy;
809 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
810 zxKMouseDY = (((int)zxKMouseDY+dy)&0xff);
812 #endif
813 zxMouseSetButtons(buttons);
814 } else {
815 zxMouseWasMoved = 0;
820 static void zxMouseButtonCB (int x, int y, int btn, int buttons) {
821 if (/*widgetsProcessMouse(x, y, btn, buttons) ||*/ msCursorDrawn) return;
822 if (!msCursorDrawn && vidWindowActivated) {
823 //fprintf(stderr, "buttons=0x%02x\n", buttons);
824 if (optKMouse && buttons == 0 && btn == (MS_BUTTON_LEFT|MS_BUTTON_DEPRESSED) &&
825 SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
827 SDL_WM_GrabInput(SDL_GRAB_ON);
828 emuRealizeRealMouseCursorState();
829 zxMouseWasMoved = 0;
830 return; // ignore this click
832 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
833 zxMouseSetButtons(buttons);
839 ////////////////////////////////////////////////////////////////////////////////
840 static void zxKeyCB (SDL_KeyboardEvent *key) {
841 if (conVisible) {
842 if (conKeyEvent(key)) return;
843 // skip debugger check here, i want it to be transparent
844 if (conVisible || memviewActive || sprviewActive) return;
846 //if (msCursorDrawn) return;
847 if (debuggerActive && dbgKeyEvent(key)) return;
848 if (memviewActive && memviewKeyEvent(key)) return;
849 if (sprviewActive && sprviewKeyEvent(key)) return;
850 if (uiovlKey(key)) return;
852 // skip debugger check here, i want it to be transparent
853 if (conVisible || memviewActive || sprviewActive) return;
855 if (key->type == SDL_KEYDOWN) {
856 SDLJimBinding *bind;
857 if ((bind = sdlFindKeyBind(sdlJimBindings, key->keysym.sym, key->keysym.mod)) != NULL && bind->action != NULL) {
858 Jim_Obj *eres = NULL/*, *dupl*/;
859 // duplicate 'action', 'cause Jim can return the same shared object
860 // if there is nothing to 'expand' in it; it's safe to omit
861 // duplication here, but future versions of Jim can check if the
862 // object is 'shared' here, so let's do it right
863 //!dupl = Jim_DuplicateObj(jim, bind->action);
864 //!Jim_IncrRefCount(dupl); // we need to do this after Jim_DuplicateObj()
865 if (Jim_SubstObj(jim, /*dupl*/bind->action, &eres, 0) == JIM_OK) {
866 Jim_IncrRefCount(eres);
867 if (Jim_EvalObjList(jim, eres) != JIM_OK) {
868 Jim_MakeErrorMessage(jim);
869 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
871 Jim_DecrRefCount(jim, eres);
873 //!Jim_DecrRefCount(jim, dupl);
874 return;
878 if (/*vidWindowActivated &&*/ msCursorDrawn) return;
880 if (zxKeyBinds[key->keysym.sym]) {
881 zxDoKeyUpDown(zxKeyBinds[key->keysym.sym]&0xffff, (key->type == SDL_KEYDOWN));
882 zxDoKeyUpDown((zxKeyBinds[key->keysym.sym]>>16)&0xffff, (key->type == SDL_KEYDOWN));
883 return;
888 ////////////////////////////////////////////////////////////////////////////////
889 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
890 #define DECODE_TUPLE(tuple,bytes) \
891 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
892 PUSH_BACK((char)((tuple >> 24)&0xff))
894 // returns ress length
895 static int ascii85Decode (char **ress, const char *srcs/*, int start, int length*/) {
896 static uint32_t pow85[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
897 const uint8_t *data = (const uint8_t *)srcs;
898 int len = (int)strlen(srcs);
899 uint32_t tuple = 0;
900 int count = 0, c = 0;
901 int dpos = 0;
902 int start = 0, length = len;
903 int tmp;
905 if (start < 0) start = 0; else { len -= start; data += start; }
906 if (length < 0 || len < length) length = len;
908 if (length > 0) {
909 int xlen = 4*((length+4)/5);
910 kstringReserve(ress, xlen);
914 *ress = (char *)calloc(1, len+1);
915 for (int f = length; f > 0; --f, ++data) {
916 c = *data;
917 if (c <= ' ') continue; // skip blanks
918 switch (c) {
919 case 'z': // zero tuple
920 if (count != 0) {
921 //fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
922 free(*ress);
923 *ress = NULL;
924 return -1;
926 PUSH_BACK('\0');
927 PUSH_BACK('\0');
928 PUSH_BACK('\0');
929 PUSH_BACK('\0');
930 break;
931 case '~': // '~>': end of sequence
932 if (f < 1 || data[1] != '>') { free(*ress); return -2; } // error
933 if (count > 0) { f = -1; break; }
934 default:
935 if (c < '!' || c > 'u') {
936 //fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
937 free(*ress);
938 return -3;
940 tuple += ((uint8_t)(c-'!'))*pow85[count++];
941 if (count == 5) {
942 DECODE_TUPLE(tuple, 4);
943 count = 0;
944 tuple = 0;
946 break;
949 // write last (possibly incomplete) tuple
950 if (count-- > 0) {
951 tuple += pow85[count];
952 DECODE_TUPLE(tuple, count);
954 return dpos;
957 #undef PUSH_BACK
958 #undef DECODE_TUPLE
961 static void decodeBA (char *str, int len) {
962 char pch = 42;
964 for (int f = 0; f < len; ++f, ++str) {
965 char ch = *str;
967 ch = (ch-f-1)^pch;
968 *str = ch;
969 pch = ch;
974 static void printEC (const char *txt) {
975 char *dest;
976 int len;
978 if ((len = ascii85Decode(&dest, txt)) >= 0) {
979 decodeBA(dest, len);
980 fprintf(stderr, "%s\n", dest);
981 free(dest);
986 static int isStr85Equ (const char *txt, const char *str) {
987 char *dest;
988 int len, res = 0;
990 if ((len = ascii85Decode(&dest, txt)) >= 0) {
991 res = (strcmp(dest, str) == 0);
992 free(dest);
994 return res;
998 static int checkEGG (const char *str) {
999 if (isStr85Equ("06:]JASq", str) || isStr85Equ("0/i", str)) {
1000 printEC(
1001 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
1002 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
1003 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
1004 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
1005 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
1006 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
1007 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
1008 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
1009 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
1010 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
1011 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
1012 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
1013 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
1015 return 1;
1017 if (isStr85Equ("04Jj?B)", str)) {
1018 printEC(
1019 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
1020 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
1021 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
1022 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
1024 return 1;
1026 if (isStr85Equ("04o69A7Tr", str)) {
1027 printEC(
1028 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
1029 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
1030 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
1032 return 1;
1034 return 0;
1038 static void addBoots (int simpleAutorun) {
1039 for (int f = 0; f < 4; ++f) {
1040 if ((snapWasDisk&(1<<f)) && !(snapWasCPCDisk&(1<<f))) addAutoBoot(f, 1, simpleAutorun);
1042 snapWasDisk = 0;
1043 snapWasCPCDisk = 0;
1047 typedef enum {
1048 ARUN_UNDEFINED,
1049 ARUN_NONE,
1050 ARUN_TRDOS,
1051 ARUN_TRDOS48,
1052 ARUN_TRDOS128,
1053 ARUN_TRDOSPENT,
1054 ARUN_TAP,
1055 ARUN_TAP48,
1056 ARUN_TAP128,
1057 ARUN_TAPPENT,
1058 ARUN_TAPP2A,
1059 ARUN_TAPP3,
1060 ARUN_P3DOS2A,
1061 ARUN_P3DOS3,
1062 } cli_arun_e;
1065 static cli_arun_e cli_autorun = ARUN_UNDEFINED;
1067 static void show_help (void) {
1068 printf(
1069 "options:\n"
1070 " --48 use 48k model\n"
1071 " --128 use 128k model\n"
1072 " -A --no-autorun don't autorun file\n"
1074 exit(0);
1078 static char *strappend (char *s, const char *s1) {
1079 if (!s) s = strdup("");
1080 if (!s1 || !s1[0]) return s;
1081 char *res = strprintf("%s%s", s, s1);
1082 free(s);
1083 return res;
1087 enum {
1088 CLI_MODEL_AUTO,
1089 CLI_MODEL_48,
1090 CLI_MODEL_128,
1091 CLI_MODEL_PENT,
1092 CLI_MODEL_PLUS2A,
1093 CLI_MODEL_PLUS3,
1097 static void processOptions (int argc, char *argv[], int onlydbg) {
1098 int nomoreopts = 0, oldABoot = optAutoaddBoot;
1099 int do48 = CLI_MODEL_AUTO;
1100 optAutoaddBoot = 0;
1101 snapWasDisk = 0;
1102 snapWasCPCDisk = 0;
1103 snapWasTape = 0;
1104 for (int f = 1; f < argc; ++f) {
1105 if (checkEGG(argv[f])) exit(1);
1106 if (!nomoreopts) {
1107 if (strcmp(argv[f], "--") == 0) { nomoreopts = 1; continue; }
1108 if (strcmp(argv[f], "+") == 0) continue; // console command separator
1109 if (argv[f][0] == '+') {
1110 if (onlydbg) continue;
1111 optAutoaddBoot = oldABoot;
1112 if (strchr(argv[f], ' ') != NULL) {
1113 conExecute(argv[f]+1, 0);
1114 } else {
1115 // collect console command
1116 char *cmd = strdup(argv[f]+1);
1117 for (++f; f < argc; ++f) {
1118 if (argv[f][0] == '+') break;
1119 if (argv[f][0] == '-' && argv[f][1] == '-') break;
1120 cmd = strappend(cmd, " ");
1121 cmd = strappend(cmd, argv[f]);
1123 --f; // compensate 'for'
1124 conExecute(cmd, 0);
1125 free(cmd);
1127 if (oldABoot != optAutoaddBoot) {
1128 if (oldABoot) addBoots(0);
1129 oldABoot = optAutoaddBoot;
1130 snapWasDisk = 0;
1131 snapWasCPCDisk = 0;
1133 optAutoaddBoot = 0;
1134 continue;
1136 if (argv[f][0] == '-') {
1137 if (argv[f][1] == '-') {
1138 if (strcmp(argv[f], "--help") == 0) show_help();
1139 else if (strcmp(argv[f], "--48") == 0) do48 = CLI_MODEL_48;
1140 else if (strcmp(argv[f], "--128") == 0) do48 = CLI_MODEL_128;
1141 else if (strcmp(argv[f], "--pent") == 0 || strcmp(argv[f], "--pentagon") == 0) do48 = CLI_MODEL_PENT;
1142 else if (strcmp(argv[f], "--plus2a") == 0) do48 = CLI_MODEL_PLUS2A;
1143 else if (strcmp(argv[f], "--plus3") == 0) do48 = CLI_MODEL_PLUS3;
1144 else if (strcmp(argv[f], "--no-autorun") == 0) cli_autorun = ARUN_NONE;
1145 else if (strcmp(argv[f], "--opense") == 0) {
1146 if (!onlydbg) {
1147 if (!optOpenSE) { optOpenSE = 1; emuSetModel(zxModel, 1); }
1150 else if (strcmp(argv[f], "--usock") == 0 || strcmp(argv[f], "--usocket") == 0) {
1151 ++f;
1152 if (f >= argc) { fprintf(stderr, "option '%s' expects socket name!\n", argv[f-1]); exit(1); }
1153 if (!onlydbg) unixsock_start_server(argv[f]);
1155 else if (strcmp(argv[f], "--unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(1); }
1156 else if (strcmp(argv[f], "--no-unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(0); }
1157 else { fprintf(stderr, "unknown command line option: '%s'\n", argv[f]); exit(1); }
1158 continue;
1160 for (const char *a = argv[f]+1; *a; ++a) {
1161 switch (*a) {
1162 case 'h': show_help(); break;
1163 case 'A': cli_autorun = ARUN_NONE; break;
1164 case 'D':
1165 if (onlydbg) {
1166 fprintf(stderr, "console dump enabled\n");
1167 optConDump = 1;
1169 break;
1170 case 'q': /* do not dump consote text to stdout */
1171 optConDumpToStdout = 0;
1172 break;
1173 case 'S':
1174 if (onlydbg) {
1175 fprintf(stderr, "sound debug enabled\n");
1176 optSndSyncDebug = 1;
1178 break;
1179 default: fprintf(stderr, "unknown command line option: '%c'\n", *a); exit(1);
1182 continue; // jff
1185 if (onlydbg) continue;
1186 // snapshot name
1187 if (loadSnapshot(argv[f], SNAPLOAD_ANY) == 0) {
1188 cprintf("'%s' loaded\n", argv[f]);
1189 if (cli_autorun != ARUN_NONE) {
1190 if (snapWasDisk) {
1191 cli_autorun = (snapWasCPCDisk ? ARUN_P3DOS2A : ARUN_TRDOS);
1192 if (do48 == CLI_MODEL_AUTO || do48 == CLI_MODEL_128) do48 = CLI_MODEL_PENT;
1193 } else if (snapWasTape) {
1194 cli_autorun = ARUN_TAP;
1195 } else {
1196 cli_autorun = ARUN_UNDEFINED;
1199 //cprintf("wasdisk=%d; wascpc=%d; ar=%d\n", snapWasDisk, snapWasCPCDisk, cli_autorun);
1200 } else {
1201 cprintf("failed to load '%s'\n", argv[f]);
1203 snapWasTape = 0;
1206 optAutoaddBoot = oldABoot;
1207 if (!onlydbg) {
1208 if (oldABoot && !snapWasCPCDisk) addBoots(1);
1209 if (cli_autorun > ARUN_NONE) {
1210 if (cli_autorun != ARUN_P3DOS2A && cli_autorun != ARUN_P3DOS3) {
1211 switch (do48) {
1212 case CLI_MODEL_AUTO:
1213 switch (cli_autorun) {
1214 case ARUN_TAP: cli_autorun = ARUN_TAP48; break;
1215 case ARUN_TRDOS: cli_autorun = ARUN_TRDOSPENT; break;
1216 default: break;
1218 break;
1219 case CLI_MODEL_48:
1220 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1221 break;
1222 case CLI_MODEL_128:
1223 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS128 : ARUN_TAP128);
1224 break;
1225 case CLI_MODEL_PENT:
1226 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOSPENT : ARUN_TAPPENT);
1227 break;
1228 case CLI_MODEL_PLUS2A:
1229 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS2A : ARUN_TAPP2A);
1230 break;
1231 case CLI_MODEL_PLUS3:
1232 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS3 : ARUN_TAPP3);
1233 break;
1234 default:
1235 cprintf("\1UNKNOWN CLI MODEL!\n");
1236 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1237 break;
1240 switch (cli_autorun) {
1241 case ARUN_TRDOS: conExecute("reset trdos", 0); break;
1242 case ARUN_TRDOS48: conExecute("reset 48k trdos", 0); break;
1243 case ARUN_TRDOS128: conExecute("reset 128k trdos", 0); break;
1244 case ARUN_TRDOSPENT: conExecute("reset pentagon 512 trdos", 0); break;
1245 case ARUN_TAP48: conExecute("reset 48k", 0); goto do_tape_autoload;
1246 case ARUN_TAP128: conExecute("reset 128k", 0); goto do_tape_autoload;
1247 case ARUN_TAPPENT: conExecute("reset pentagon 512", 0); goto do_tape_autoload;
1248 case ARUN_TAPP2A: conExecute("reset plus2a", 0); goto do_tape_autoload;
1249 case ARUN_TAPP3: conExecute("reset plus3", 0); goto do_tape_autoload;
1250 case ARUN_P3DOS2A: conExecute("reset plus2a", 0); break;
1251 case ARUN_P3DOS3: conExecute("reset plus3", 0); break;
1252 case ARUN_TAP:
1253 do_tape_autoload:
1254 conExecute("tape _autoload", 0);
1255 break;
1256 default: ;
1263 static void xMainLoop (void) {
1264 const int mcsInFrame = 20*1000;
1265 static int64_t mcsFrameEndWanted;
1266 int eres = 1;
1267 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1268 while (eres >= 0) {
1269 int64_t mcsCurFrameEnd;
1270 eres = processEvents(0);
1271 buildFrame();
1272 unixsock_handle();
1273 mcsCurFrameEnd = timerGetMicroSeconds();
1274 if (mcsCurFrameEnd > 0) {
1275 int mcsSleep = (mcsFrameEndWanted-mcsCurFrameEnd);
1276 //fprintf(stderr, "0: wait=%.15g\n", ((double)mcsSleep)/1000.0);
1277 if (mcsSleep > 0) {
1278 // less than 20 ms
1279 //fprintf(stderr, "SLEEP: %.15g\n", ((double)mcsSleep)/1000.0);
1280 usleep(mcsSleep);
1281 //mcsCurFrameEnd = timerGetMicroSeconds();
1282 //fprintf(stderr, "1:few=%d; cfe=%d; few-cfe=%.15g\n", (int)mcsFrameEndWanted, (int)mcsCurFrameEnd, ((double)(mcsInFrame-(mcsFrameEndWanted-mcsCurFrameEnd)))/1000);
1283 mcsFrameEndWanted += mcsInFrame;
1284 } else {
1285 fprintf(stderr, "DESYNC! (%d)\n", mcsSleep);
1286 //mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1287 mcsFrameEndWanted = mcsCurFrameEnd+mcsInFrame;
1289 } else {
1290 //FIXME
1291 // reinit timer
1292 timerReinit();
1293 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1299 ////////////////////////////////////////////////////////////////////////////////
1300 static void cprintLibFDC (int type, const char *msg) {
1301 switch (type) {
1302 case LIBFDC_MSG_DEBUG: cprintf("\3LIBFDC[debug]: %s\n", msg); break;
1303 case LIBFDC_MSG_WARNING: cprintf("\2LIBFDC[warn]: %s\n", msg); break;
1304 case LIBFDC_MSG_ERROR: cprintf("\4LIBFDC[error]: %s\n", msg); break;
1305 default: cprintf("\3LIBFDC[???]: %s\n", msg); break; // the thing that should not be
1310 ////////////////////////////////////////////////////////////////////////////////
1311 int main (int argc, char *argv[]) {
1312 Jim_SetAllowUnsafeExtensions(1);
1313 tinf_init();
1314 conInit();
1315 initMyDir();
1317 libfdcMessageCB = &cprintLibFDC;
1319 processOptions(argc, argv, 1);
1321 if (libspectrum_init() != LIBSPECTRUM_ERROR_NONE) {
1322 fprintf(stderr, "FATAL: can't init libspectrum!\n");
1323 return 1;
1325 cprintf("===================================\n");
1326 cprintf("using libspectrum v%s\n", libspectrum_version());
1328 switch (timerInit()) {
1329 case TIMER_ERROR: abort();
1330 case TIMER_HPET: break;
1331 default:
1332 cprintf(
1333 "\2WARNING: please, set your clock source to HPET!\n"
1334 "you can do this by issuing the following command:\n"
1335 "======\n"
1336 "\1sudo echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource\n"
1337 "======\n"
1338 "\2this is not a critical issue, but hpet clock will\n"
1339 "\2give you slightly better emulation.\n"
1341 break;
1344 if (!Jim_GetAllowUnsafeExtensions()) {
1345 cprintf("\2WARNING: Disabled unsafe Tcl extensions.\n");
1346 } else {
1347 cprintf("Unsafe Tcl extensions enabled ('--no-unsafe-tcl' to disable).\n");
1350 //zxwinInit();
1351 jimInit();
1353 emuInit();
1354 jimEvalFile("init/init.tcl", 0);
1356 jimEvalFile("init/roms.tcl", 0);
1357 emuSetModel(zxModel, 1);
1359 dbgInit();
1360 memviewInit();
1361 sprviewInit();
1363 emuInitBindings();
1364 jimEvalFile("init/concmd.tcl", 0);
1366 sdlInit();
1367 initVideo();
1369 frameCB = zxFrameCB;
1370 keyCB = zxKeyCB;
1371 mouseCB = zxMouseCB;
1372 mouseButtonCB = zxMouseButtonCB;
1374 //jimEvalFile("init/widgets/init.tcl", 1);
1376 jimEvalFile("autoexec.tcl", 1);
1377 emuSetModel(zxModel, 1); // in case something vital was changed (like "opense on")
1379 processOptions(argc, argv, 0);
1381 jimEvalFile1("./.zxemutrc.tcl");
1382 jimEvalFile1("./.zxemut.tcl");
1383 jimEvalFile1("./zxemutrc.tcl");
1384 jimEvalFile1("./zxemut.tcl");
1386 #ifdef USE_SOUND
1387 if (sndSampleRate < 0 && initSound() != 0) {
1388 fprintf(stderr, "WARNING: can't initialize sound!\n");
1389 cprintf("\4WARNING: can't initialize sound!\n");
1390 sndSampleRate = 0;
1392 sndAllowUseToggle = 0;
1393 if (sndSampleRate <= 0) cprintf("NOSOUND mode");
1395 frameCB = zxFrameCB;
1396 keyCB = zxKeyCB;
1397 mouseCB = zxMouseCB;
1398 mouseButtonCB = zxMouseButtonCB;
1400 uiovlInit();
1402 if (sndSampleRate > 0) {
1403 while (processEvents(0) >= 0) {
1404 buildFrame();
1405 unixsock_handle();
1406 // optMaxSpeed implies sndUsing==0
1407 if (!optMaxSpeed && optSpeed == 100 && (debuggerActive || optPaused)) soundWrite();
1409 } else {
1410 xMainLoop();
1412 #else
1413 sndAllowUseToggle = 0;
1414 xMainLoop();
1415 #endif
1417 unixsock_stop_server();
1418 uiovlDeinit();
1419 //zxwinDeinit();
1420 beta_end();
1421 deinitSound();
1422 jimDeinit();
1423 if (condumpfl != NULL) fclose(condumpfl);
1424 return 0;