zxemut: init absolute kmouse coords when "absolute" mode is turned on
[zymosis.git] / src / ZXEmuT / main.c
blobe638e9532fbd235a7a32b18b2746594b0eed9856
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 //fprintf(stderr, "!!!\n");
660 do {
661 z80Step(-1);
662 } while (!debuggerActive && !optPaused && timerGetMS()-stt < 20);
663 //fprintf(stderr, "tst=%d; xtime=%u\n", tst, (uint32_t)(timerGetMS()-stt));
664 z80_was_frame_end = 0;
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 z80Step(tickstodo);
687 } else {
688 // faster than the original
689 int nomore = 0;
690 // do incomplete frame
691 tickstodo -= z80Step(tickstodo);
692 z80_was_frame_end = 0;
693 // execute full frames, checking for timeout
694 while (tickstodo >= machineInfo.tsperframe && !debuggerActive && !optPaused) {
695 // do one frame
696 tickstodo -= z80Step(tickstodo);
697 z80_was_frame_end = 0;
698 if (timerGetMS()-stt >= 20) { nomore = 1; break; }
700 // do the rest
701 if (!nomore) {
702 while (tickstodo > 0 && !debuggerActive && !optPaused) {
703 tickstodo -= z80Step(tickstodo);
707 z80_was_frame_end = 0;
708 } else {
709 // normal, do one frame
710 z80Step(-1);
711 if (z80_was_frame_end) {
712 z80_was_frame_end = 0;
713 if (!debuggerActive && !optPaused) {
714 // normal execution; draw frame, play sound
715 zxPaintScreen();
716 forcedShowFrame();
717 soundWrite();
718 return;
723 zxRealiseScreen(z80.tstates);
724 zxPaintScreen();
728 ////////////////////////////////////////////////////////////////////////////////
729 static void zxMouseSetButtons (int buttons) {
730 if (buttons&MS_BUTTON_WHEELUP) zxKMouseWheel = ((int)zxKMouseWheel-1)&0x0f;
731 if (buttons&MS_BUTTON_WHEELDOWN) zxKMouseWheel = ((int)zxKMouseWheel+1)&0x0f;
732 zxKMouseButtons = buttons&(MS_BUTTON_LEFT|MS_BUTTON_RIGHT|MS_BUTTON_MIDDLE);
736 void emuSetKMouseAbsCoords (void) {
737 int x = msRealX, y = msRealY;
738 //fprintf(stderr, "MS: scale=%d, x=%d; y=%d\n", vidScale, x / vidScale, y / vidScale);
739 x /= vidScale;
740 y /= vidScale;
741 x -= 32 + zxScreenOfs;
742 y -= 24;
743 //fprintf(stderr, "MS: x=%d; y=%d\n", x, y);
744 kmouseAbsX = x; kmouseAbsY = y;
748 static void zxMouseCB (int x, int y, int xrel, int yrel, int buttons) {
749 if (/*widgetsProcessMouse(x, y, 0, buttons) ||*/ msCursorDrawn) return;
750 //fprintf(stderr, "mouse: x=%d; y=%d; xrel=%d; yrel=%d; btn=#%02X\n", x, y, xrel, yrel, buttons);
752 if (optKMouse && optKMouseAbsolute) {
753 emuSetKMouseAbsCoords();
754 return;
757 if (!msCursorDrawn && vidWindowActivated && SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
758 #if 0
759 zxKMouseDXAccum += xrel;
760 zxKMouseDYAccum += yrel;
761 int speed = zxKMouseSpeed;
762 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
763 xrel = yrel = 0;
764 while (zxKMouseDXAccum >= speed) { xrel += 1; zxKMouseDXAccum -= speed; }
765 while (zxKMouseDXAccum <= -speed) { xrel -= 1; zxKMouseDXAccum += speed; }
766 while (zxKMouseDYAccum >= speed) { yrel += 1; zxKMouseDYAccum -= speed; }
767 while (zxKMouseDYAccum <= -speed) { yrel -= 1; zxKMouseDYAccum += speed; }
768 zxKMouseDX = (((int)zxKMouseDX+xrel)&0xff);
769 zxKMouseDY = (((int)zxKMouseDY-yrel)&0xff);
770 #elif 0
771 //int lp = (y+24)*352+16+zxScreenOfs;
772 int dx = 0, dy = 0;
773 if (zxMouseWasMoved) {
774 dx = x-zxMouseLastX;
775 dy = y-zxMouseLastY;
776 } else {
777 zxMouseLastX = x;
778 zxMouseLastY = y;
779 zxMouseWasMoved = 1;
781 int speed = zxKMouseSpeed;
782 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
783 dx /= speed;
784 dy /= speed;
785 if (dx != 0) {
786 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
787 zxMouseLastX = x;
789 if (dy != 0) {
790 zxKMouseDY = (((int)zxKMouseDY-dy)&0xff);
791 zxMouseLastY = y;
793 #else
794 if (!zxMouseWasMoved) {
795 zxMouseLastX = zxMouseLastY = 0;
796 zxMouseFracX = zxMouseFracY = 0;
797 zxMouseWasMoved = 1;
798 } else {
799 int speed = zxKMouseSpeed;
800 if (speed < 1) speed = 1; else if (speed > 256) speed = 256;
801 zxMouseFracX += xrel;
802 zxMouseFracY -= yrel;
803 int dx = 0;
804 int dy = 0;
805 while (zxMouseFracX <= -speed) { --dx; zxMouseFracX += speed; }
806 while (zxMouseFracX >= speed) { ++dx; zxMouseFracX -= speed; }
807 while (zxMouseFracY <= -speed) { --dy; zxMouseFracY += speed; }
808 while (zxMouseFracY >= speed) { ++dy; zxMouseFracY -= speed; }
809 //if (abs(dx) >= 6) { if (dx < 0) dx -= (-dx)/2; else dx += dx/2; }
810 //if (abs(dy) >= 6) { if (dy < 0) dy -= (-dy)/2; else dy += dy/2; }
811 if (dx && zxMouseFracX <= 2) zxMouseFracX = 0;
812 if (dy && zxMouseFracY <= 2) zxMouseFracY = 0;
813 if (abs(dx) >= 6) dx *= 2;
814 if (abs(dy) >= 6) dy *= 2;
815 zxMouseLastX += dx;
816 zxMouseLastY += dy;
817 zxKMouseDX = (((int)zxKMouseDX+dx)&0xff);
818 zxKMouseDY = (((int)zxKMouseDY+dy)&0xff);
820 #endif
821 zxMouseSetButtons(buttons);
822 } else {
823 zxMouseWasMoved = 0;
828 static void zxMouseButtonCB (int x, int y, int btn, int buttons) {
829 if (/*widgetsProcessMouse(x, y, btn, buttons) ||*/ msCursorDrawn) return;
830 if (!msCursorDrawn && vidWindowActivated) {
831 //fprintf(stderr, "buttons=0x%02x\n", buttons);
832 if (optKMouse && optKMouseAbsolute) {
833 zxMouseSetButtons(buttons);
834 return;
836 if (optKMouse && buttons == 0 && btn == (MS_BUTTON_LEFT|MS_BUTTON_DEPRESSED) &&
837 SDL_WM_GrabInput(SDL_GRAB_QUERY) != SDL_GRAB_ON)
839 SDL_WM_GrabInput(SDL_GRAB_ON);
840 emuRealizeRealMouseCursorState();
841 zxMouseWasMoved = 0;
842 return; // ignore this click
844 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) {
845 zxMouseSetButtons(buttons);
851 ////////////////////////////////////////////////////////////////////////////////
852 static void zxKeyCB (SDL_KeyboardEvent *key) {
853 if (conVisible) {
854 if (conKeyEvent(key)) return;
855 // skip debugger check here, i want it to be transparent
856 if (conVisible || memviewActive || sprviewActive) return;
858 //if (msCursorDrawn) return;
859 if (debuggerActive && dbgKeyEvent(key)) return;
860 if (memviewActive && memviewKeyEvent(key)) return;
861 if (sprviewActive && sprviewKeyEvent(key)) return;
862 if (uiovlKey(key)) return;
864 // skip debugger check here, i want it to be transparent
865 if (conVisible || memviewActive || sprviewActive) return;
867 if (key->type == SDL_KEYDOWN) {
868 SDLJimBinding *bind;
869 if ((bind = sdlFindKeyBind(sdlJimBindings, key->keysym.sym, key->keysym.mod)) != NULL && bind->action != NULL) {
870 Jim_Obj *eres = NULL/*, *dupl*/;
871 // duplicate 'action', 'cause Jim can return the same shared object
872 // if there is nothing to 'expand' in it; it's safe to omit
873 // duplication here, but future versions of Jim can check if the
874 // object is 'shared' here, so let's do it right
875 //!dupl = Jim_DuplicateObj(jim, bind->action);
876 //!Jim_IncrRefCount(dupl); // we need to do this after Jim_DuplicateObj()
877 if (Jim_SubstObj(jim, /*dupl*/bind->action, &eres, 0) == JIM_OK) {
878 Jim_IncrRefCount(eres);
879 if (Jim_EvalObjList(jim, eres) != JIM_OK) {
880 Jim_MakeErrorMessage(jim);
881 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
883 Jim_DecrRefCount(jim, eres);
885 //!Jim_DecrRefCount(jim, dupl);
886 return;
890 if (/*vidWindowActivated &&*/ msCursorDrawn) return;
892 if (zxKeyBinds[key->keysym.sym]) {
893 zxDoKeyUpDown(zxKeyBinds[key->keysym.sym]&0xffff, (key->type == SDL_KEYDOWN));
894 zxDoKeyUpDown((zxKeyBinds[key->keysym.sym]>>16)&0xffff, (key->type == SDL_KEYDOWN));
895 return;
900 ////////////////////////////////////////////////////////////////////////////////
901 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
902 #define DECODE_TUPLE(tuple,bytes) \
903 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
904 PUSH_BACK((char)((tuple >> 24)&0xff))
906 // returns ress length
907 static int ascii85Decode (char **ress, const char *srcs/*, int start, int length*/) {
908 static uint32_t pow85[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
909 const uint8_t *data = (const uint8_t *)srcs;
910 int len = (int)strlen(srcs);
911 uint32_t tuple = 0;
912 int count = 0, c = 0;
913 int dpos = 0;
914 int start = 0, length = len;
915 int tmp;
917 if (start < 0) start = 0; else { len -= start; data += start; }
918 if (length < 0 || len < length) length = len;
920 if (length > 0) {
921 int xlen = 4*((length+4)/5);
922 kstringReserve(ress, xlen);
926 *ress = (char *)calloc(1, len+1);
927 for (int f = length; f > 0; --f, ++data) {
928 c = *data;
929 if (c <= ' ') continue; // skip blanks
930 switch (c) {
931 case 'z': // zero tuple
932 if (count != 0) {
933 //fprintf(stderr, "%s: z inside ascii85 5-tuple\n", file);
934 free(*ress);
935 *ress = NULL;
936 return -1;
938 PUSH_BACK('\0');
939 PUSH_BACK('\0');
940 PUSH_BACK('\0');
941 PUSH_BACK('\0');
942 break;
943 case '~': // '~>': end of sequence
944 if (f < 1 || data[1] != '>') { free(*ress); return -2; } // error
945 if (count > 0) { f = -1; break; }
946 default:
947 if (c < '!' || c > 'u') {
948 //fprintf(stderr, "%s: bad character in ascii85 region: %#o\n", file, c);
949 free(*ress);
950 return -3;
952 tuple += ((uint8_t)(c-'!'))*pow85[count++];
953 if (count == 5) {
954 DECODE_TUPLE(tuple, 4);
955 count = 0;
956 tuple = 0;
958 break;
961 // write last (possibly incomplete) tuple
962 if (count-- > 0) {
963 tuple += pow85[count];
964 DECODE_TUPLE(tuple, count);
966 return dpos;
969 #undef PUSH_BACK
970 #undef DECODE_TUPLE
973 static void decodeBA (char *str, int len) {
974 char pch = 42;
976 for (int f = 0; f < len; ++f, ++str) {
977 char ch = *str;
979 ch = (ch-f-1)^pch;
980 *str = ch;
981 pch = ch;
986 static void printEC (const char *txt) {
987 char *dest;
988 int len;
990 if ((len = ascii85Decode(&dest, txt)) >= 0) {
991 decodeBA(dest, len);
992 fprintf(stderr, "%s\n", dest);
993 free(dest);
998 static int isStr85Equ (const char *txt, const char *str) {
999 char *dest;
1000 int len, res = 0;
1002 if ((len = ascii85Decode(&dest, txt)) >= 0) {
1003 res = (strcmp(dest, str) == 0);
1004 free(dest);
1006 return res;
1010 static int checkEGG (const char *str) {
1011 if (isStr85Equ("06:]JASq", str) || isStr85Equ("0/i", str)) {
1012 printEC(
1013 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
1014 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
1015 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
1016 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
1017 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
1018 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
1019 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
1020 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
1021 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
1022 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
1023 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
1024 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
1025 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
1027 return 1;
1029 if (isStr85Equ("04Jj?B)", str)) {
1030 printEC(
1031 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
1032 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
1033 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
1034 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
1036 return 1;
1038 if (isStr85Equ("04o69A7Tr", str)) {
1039 printEC(
1040 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
1041 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
1042 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
1044 return 1;
1046 return 0;
1050 static void addBoots (int simpleAutorun) {
1051 for (int f = 0; f < 4; ++f) {
1052 if ((snapWasDisk&(1<<f)) && !(snapWasCPCDisk&(1<<f))) addAutoBoot(f, 1, simpleAutorun);
1054 snapWasDisk = 0;
1055 snapWasCPCDisk = 0;
1059 typedef enum {
1060 ARUN_UNDEFINED,
1061 ARUN_NONE,
1062 ARUN_TRDOS,
1063 ARUN_TRDOS48,
1064 ARUN_TRDOS128,
1065 ARUN_TRDOSPENT,
1066 ARUN_TAP,
1067 ARUN_TAP48,
1068 ARUN_TAP128,
1069 ARUN_TAPPENT,
1070 ARUN_TAPP2A,
1071 ARUN_TAPP3,
1072 ARUN_P3DOS2A,
1073 ARUN_P3DOS3,
1074 } cli_arun_e;
1077 static cli_arun_e cli_autorun = ARUN_UNDEFINED;
1079 static void show_help (void) {
1080 printf(
1081 "options:\n"
1082 " --48 use 48k model\n"
1083 " --128 use 128k model\n"
1084 " -A --no-autorun don't autorun file\n"
1086 exit(0);
1090 static char *strappend (char *s, const char *s1) {
1091 if (!s) s = strdup("");
1092 if (!s1 || !s1[0]) return s;
1093 char *res = strprintf("%s%s", s, s1);
1094 free(s);
1095 return res;
1099 enum {
1100 CLI_MODEL_AUTO,
1101 CLI_MODEL_48,
1102 CLI_MODEL_128,
1103 CLI_MODEL_PENT,
1104 CLI_MODEL_PLUS2A,
1105 CLI_MODEL_PLUS3,
1109 static void processOptions (int argc, char *argv[], int onlydbg) {
1110 int nomoreopts = 0, oldABoot = optAutoaddBoot;
1111 int do48 = CLI_MODEL_AUTO;
1112 optAutoaddBoot = 0;
1113 snapWasDisk = 0;
1114 snapWasCPCDisk = 0;
1115 snapWasTape = 0;
1116 for (int f = 1; f < argc; ++f) {
1117 if (checkEGG(argv[f])) exit(1);
1118 if (!nomoreopts) {
1119 if (strcmp(argv[f], "--") == 0) { nomoreopts = 1; continue; }
1120 if (strcmp(argv[f], "+") == 0) continue; // console command separator
1121 if (argv[f][0] == '+') {
1122 if (onlydbg) continue;
1123 optAutoaddBoot = oldABoot;
1124 if (strchr(argv[f], ' ') != NULL) {
1125 conExecute(argv[f]+1, 0);
1126 } else {
1127 // collect console command
1128 char *cmd = strdup(argv[f]+1);
1129 for (++f; f < argc; ++f) {
1130 if (argv[f][0] == '+') break;
1131 if (argv[f][0] == '-' && argv[f][1] == '-') break;
1132 cmd = strappend(cmd, " ");
1133 cmd = strappend(cmd, argv[f]);
1135 --f; // compensate 'for'
1136 conExecute(cmd, 0);
1137 free(cmd);
1139 if (oldABoot != optAutoaddBoot) {
1140 if (oldABoot) addBoots(0);
1141 oldABoot = optAutoaddBoot;
1142 snapWasDisk = 0;
1143 snapWasCPCDisk = 0;
1145 optAutoaddBoot = 0;
1146 continue;
1148 if (argv[f][0] == '-') {
1149 if (argv[f][1] == '-') {
1150 if (strcmp(argv[f], "--help") == 0) show_help();
1151 else if (strcmp(argv[f], "--48") == 0) do48 = CLI_MODEL_48;
1152 else if (strcmp(argv[f], "--128") == 0) do48 = CLI_MODEL_128;
1153 else if (strcmp(argv[f], "--pent") == 0 || strcmp(argv[f], "--pentagon") == 0) do48 = CLI_MODEL_PENT;
1154 else if (strcmp(argv[f], "--plus2a") == 0) do48 = CLI_MODEL_PLUS2A;
1155 else if (strcmp(argv[f], "--plus3") == 0) do48 = CLI_MODEL_PLUS3;
1156 else if (strcmp(argv[f], "--no-autorun") == 0) cli_autorun = ARUN_NONE;
1157 else if (strcmp(argv[f], "--opense") == 0) {
1158 if (!onlydbg) {
1159 if (!optOpenSE) { optOpenSE = 1; emuSetModel(zxModel, 1); }
1162 else if (strcmp(argv[f], "--usock") == 0 || strcmp(argv[f], "--usocket") == 0) {
1163 ++f;
1164 if (f >= argc) { fprintf(stderr, "option '%s' expects socket name!\n", argv[f-1]); exit(1); }
1165 if (!onlydbg) unixsock_start_server(argv[f]);
1167 else if (strcmp(argv[f], "--unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(1); }
1168 else if (strcmp(argv[f], "--no-unsafe-tcl") == 0) { if (onlydbg) Jim_SetAllowUnsafeExtensions(0); }
1169 else if (strcmp(argv[f], "--tcl-verbose-loading") == 0) { jim_verbose_loading = 1; }
1170 else if (strcmp(argv[f], "--tcl-silent-loading") == 0) { jim_verbose_loading = 0; }
1171 else { fprintf(stderr, "unknown command line option: '%s'\n", argv[f]); exit(1); }
1172 continue;
1174 for (const char *a = argv[f]+1; *a; ++a) {
1175 switch (*a) {
1176 case 'h': show_help(); break;
1177 case 'A': cli_autorun = ARUN_NONE; break;
1178 case 'D':
1179 if (onlydbg) {
1180 fprintf(stderr, "console dump enabled\n");
1181 optConDump = 1;
1183 break;
1184 case 'q': /* do not dump consote text to stdout */
1185 optConDumpToStdout = 0;
1186 break;
1187 case 'S':
1188 if (onlydbg) {
1189 fprintf(stderr, "sound debug enabled\n");
1190 optSndSyncDebug = 1;
1192 break;
1193 default: fprintf(stderr, "unknown command line option: '%c'\n", *a); exit(1);
1196 continue; // jff
1199 if (onlydbg) continue;
1200 // snapshot name
1201 if (loadSnapshot(argv[f], SNAPLOAD_ANY) == 0) {
1202 cprintf("'%s' loaded\n", argv[f]);
1203 if (cli_autorun != ARUN_NONE) {
1204 if (snapWasDisk) {
1205 cli_autorun = (snapWasCPCDisk ? ARUN_P3DOS2A : ARUN_TRDOS);
1206 if (do48 == CLI_MODEL_AUTO || do48 == CLI_MODEL_128) do48 = CLI_MODEL_PENT;
1207 } else if (snapWasTape) {
1208 cli_autorun = ARUN_TAP;
1209 } else {
1210 cli_autorun = ARUN_UNDEFINED;
1213 //cprintf("wasdisk=%d; wascpc=%d; ar=%d\n", snapWasDisk, snapWasCPCDisk, cli_autorun);
1214 } else {
1215 cprintf("failed to load '%s'\n", argv[f]);
1217 snapWasTape = 0;
1220 optAutoaddBoot = oldABoot;
1221 if (!onlydbg) {
1222 if (oldABoot && !snapWasCPCDisk) addBoots(1);
1223 if (cli_autorun > ARUN_NONE) {
1224 if (cli_autorun != ARUN_P3DOS2A && cli_autorun != ARUN_P3DOS3) {
1225 switch (do48) {
1226 case CLI_MODEL_AUTO:
1227 switch (cli_autorun) {
1228 case ARUN_TAP: cli_autorun = ARUN_TAP48; break;
1229 case ARUN_TRDOS: cli_autorun = ARUN_TRDOSPENT; break;
1230 default: break;
1232 break;
1233 case CLI_MODEL_48:
1234 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1235 break;
1236 case CLI_MODEL_128:
1237 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS128 : ARUN_TAP128);
1238 break;
1239 case CLI_MODEL_PENT:
1240 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOSPENT : ARUN_TAPPENT);
1241 break;
1242 case CLI_MODEL_PLUS2A:
1243 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS2A : ARUN_TAPP2A);
1244 break;
1245 case CLI_MODEL_PLUS3:
1246 cli_autorun = (cli_autorun == ARUN_P3DOS3 ? ARUN_P3DOS3 : ARUN_TAPP3);
1247 break;
1248 default:
1249 cprintf("\1UNKNOWN CLI MODEL!\n");
1250 cli_autorun = (cli_autorun == ARUN_TRDOS ? ARUN_TRDOS48 : ARUN_TAP48);
1251 break;
1254 switch (cli_autorun) {
1255 case ARUN_TRDOS: conExecute("reset trdos", 0); break;
1256 case ARUN_TRDOS48: conExecute("reset 48k trdos", 0); break;
1257 case ARUN_TRDOS128: conExecute("reset 128k trdos", 0); break;
1258 case ARUN_TRDOSPENT: conExecute("reset pentagon 512 trdos", 0); break;
1259 case ARUN_TAP48: conExecute("reset 48k", 0); goto do_tape_autoload;
1260 case ARUN_TAP128: conExecute("reset 128k", 0); goto do_tape_autoload;
1261 case ARUN_TAPPENT: conExecute("reset pentagon 512", 0); goto do_tape_autoload;
1262 case ARUN_TAPP2A: conExecute("reset plus2a", 0); goto do_tape_autoload;
1263 case ARUN_TAPP3: conExecute("reset plus3", 0); goto do_tape_autoload;
1264 case ARUN_P3DOS2A: conExecute("reset plus2a", 0); break;
1265 case ARUN_P3DOS3: conExecute("reset plus3", 0); break;
1266 case ARUN_TAP:
1267 do_tape_autoload:
1268 conExecute("tape _autoload", 0);
1269 break;
1270 default: ;
1277 static void xMainLoop (void) {
1278 const int mcsInFrame = 20*1000;
1279 static int64_t mcsFrameEndWanted;
1280 int eres = 1;
1281 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1282 while (eres >= 0) {
1283 int64_t mcsCurFrameEnd;
1284 eres = processEvents(0);
1285 buildFrame();
1286 unixsock_handle();
1287 mcsCurFrameEnd = timerGetMicroSeconds();
1288 if (optMaxSpeed && !debuggerActive && !optPaused) {
1289 mcsFrameEndWanted = mcsCurFrameEnd+mcsInFrame;
1290 continue;
1292 if (mcsCurFrameEnd > 0) {
1293 int mcsSleep = (mcsFrameEndWanted-mcsCurFrameEnd);
1294 //fprintf(stderr, "0: wait=%.15g\n", ((double)mcsSleep)/1000.0);
1295 if (mcsSleep > 0) {
1296 // less than 20 ms
1297 //fprintf(stderr, "SLEEP: %.15g\n", ((double)mcsSleep)/1000.0);
1298 usleep(mcsSleep);
1299 //mcsCurFrameEnd = timerGetMicroSeconds();
1300 //fprintf(stderr, "1:few=%d; cfe=%d; few-cfe=%.15g\n", (int)mcsFrameEndWanted, (int)mcsCurFrameEnd, ((double)(mcsInFrame-(mcsFrameEndWanted-mcsCurFrameEnd)))/1000);
1301 mcsFrameEndWanted += mcsInFrame;
1302 } else {
1303 fprintf(stderr, "DESYNC! (%d)\n", mcsSleep);
1304 //mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1305 mcsFrameEndWanted = mcsCurFrameEnd+mcsInFrame;
1307 } else {
1308 //FIXME
1309 // reinit timer
1310 timerReinit();
1311 mcsFrameEndWanted = timerGetMicroSeconds()+mcsInFrame;
1317 ////////////////////////////////////////////////////////////////////////////////
1318 static void cprintLibFDC (int type, const char *msg) {
1319 switch (type) {
1320 case LIBFDC_MSG_DEBUG: cprintf("\3LIBFDC[debug]: %s\n", msg); break;
1321 case LIBFDC_MSG_WARNING: cprintf("\2LIBFDC[warn]: %s\n", msg); break;
1322 case LIBFDC_MSG_ERROR: cprintf("\4LIBFDC[error]: %s\n", msg); break;
1323 default: cprintf("\3LIBFDC[???]: %s\n", msg); break; // the thing that should not be
1328 ////////////////////////////////////////////////////////////////////////////////
1329 int main (int argc, char *argv[]) {
1330 Jim_SetAllowUnsafeExtensions(1);
1331 tinf_init();
1332 conInit();
1333 initMyDir();
1335 libfdcMessageCB = &cprintLibFDC;
1337 processOptions(argc, argv, 1);
1339 if (libspectrum_init() != LIBSPECTRUM_ERROR_NONE) {
1340 fprintf(stderr, "FATAL: can't init libspectrum!\n");
1341 return 1;
1343 cprintf("===================================\n");
1344 cprintf("using libspectrum v%s\n", libspectrum_version());
1346 switch (timerInit()) {
1347 case TIMER_ERROR: abort();
1348 case TIMER_HPET: break;
1349 default:
1350 cprintf(
1351 "\2WARNING: please, set your clock source to HPET!\n"
1352 "you can do this by issuing the following command:\n"
1353 "======\n"
1354 "\1sudo echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource\n"
1355 "======\n"
1356 "\2this is not a critical issue, but hpet clock will\n"
1357 "\2give you slightly better emulation.\n"
1359 break;
1362 if (!Jim_GetAllowUnsafeExtensions()) {
1363 cprintf("\2WARNING: Disabled unsafe Tcl extensions.\n");
1364 } else {
1365 cprintf("Unsafe Tcl extensions enabled ('--no-unsafe-tcl' to disable).\n");
1368 //zxwinInit();
1369 jimInit();
1371 emuInit();
1372 jimEvalFile("init/init.tcl", 0);
1374 jimEvalFile("init/roms.tcl", 0);
1375 emuSetModel(zxModel, 1);
1377 dbgInit();
1378 memviewInit();
1379 sprviewInit();
1381 emuInitBindings();
1382 jimEvalFile("init/concmd.tcl", 0);
1384 sdlInit();
1385 initVideo();
1387 frameCB = zxFrameCB;
1388 keyCB = zxKeyCB;
1389 mouseCB = zxMouseCB;
1390 mouseButtonCB = zxMouseButtonCB;
1392 //jimEvalFile("init/widgets/init.tcl", 1);
1394 jimEvalFile("autoexec.tcl", 1);
1395 emuSetModel(zxModel, 1); // in case something vital was changed (like "opense on")
1397 processOptions(argc, argv, 0);
1399 jimEvalFile1("./.zxemutrc.tcl");
1400 jimEvalFile1("./.zxemut.tcl");
1401 jimEvalFile1("./zxemutrc.tcl");
1402 jimEvalFile1("./zxemut.tcl");
1404 #ifdef USE_SOUND
1405 if (sndSampleRate < 0 && initSound() != 0) {
1406 fprintf(stderr, "WARNING: can't initialize sound!\n");
1407 cprintf("\4WARNING: can't initialize sound!\n");
1408 sndSampleRate = 0;
1410 sndAllowUseToggle = 0;
1411 if (sndSampleRate <= 0) cprintf("NOSOUND mode");
1413 frameCB = zxFrameCB;
1414 keyCB = zxKeyCB;
1415 mouseCB = zxMouseCB;
1416 mouseButtonCB = zxMouseButtonCB;
1418 uiovlInit();
1420 if (sndSampleRate > 0) {
1421 while (processEvents(0) >= 0) {
1422 buildFrame();
1423 unixsock_handle();
1424 // optMaxSpeed implies sndUsing==0
1425 if (!optMaxSpeed && optSpeed == 100 && (debuggerActive || optPaused)) soundWrite();
1427 } else {
1428 xMainLoop();
1430 #else
1431 sndAllowUseToggle = 0;
1432 xMainLoop();
1433 #endif
1435 unixsock_stop_server();
1436 uiovlDeinit();
1437 //zxwinDeinit();
1438 beta_end();
1439 deinitSound();
1440 jimDeinit();
1441 if (condumpfl != NULL) fclose(condumpfl);
1442 return 0;