libfusefdc: better TR-DOS boot setting
[zymosis.git] / src / ZXEmuT / debugger.c
blobbee8a9adf3d7acb083ca562f00c9960adc5a1b89
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 //#define USE_URASM_DISASM
22 #include "../libzymosis/zymosis.h"
23 #ifdef USE_URASM_DISASM
24 # include "../liburasm/liburasm.h"
25 #else
26 # include "../libzymosis/zymosis_utils.h"
27 #endif
28 #include "libvideo/video.h"
29 #include "debugger.h"
30 #include "emucommon.h"
31 #include "emuvars.h"
32 #include "emuutils.h"
33 #include "emuexec.h"
34 #include "zxscrdraw.h"
35 #include "console.h"
36 #include "tapes.h"
39 ////////////////////////////////////////////////////////////////////////////////
40 int debuggerActive = 0;
41 int dbgAlpha = 220;
43 int dbgIntrHit = 0;
44 int dbgNMIHit = 0;
46 static int dbgAllowLabels = 0x03;
48 static uint8_t dbgBreakpoints[65536]; //FIXME: for 128
51 ////////////////////////////////////////////////////////////////////////////////
52 //static VOverlay *dbgOverlay = NULL;
53 static uint16_t dbgUnasmTop = 0;
54 static uint16_t dbgAddrSkip = 0;
55 //static uint16_t dbgCurAddr = 0;
56 static uint16_t dbgPageAddrs[32];
57 static int dbgPageY = 0;
58 static int dbgHidden = 0;
59 static int offsetLabels = 1;
60 static int dbgMemMode = 1;
63 typedef int (*KeyEventCB) (SDL_KeyboardEvent *key);
64 typedef void (*DrawOverlayCB) (void);
66 static KeyEventCB dbgKeyEventFn = NULL;
67 static DrawOverlayCB dbgDrawOverlayFn = NULL;
70 #define DBG_UNASM_HEIGHT (VID_TEXT_HEIGHT-5)
73 ////////////////////////////////////////////////////////////////////////////////
74 typedef struct DebugLabelT {
75 char *name; /* malloced */
76 int value; /* [0..65535] */
77 int asoffset;
78 struct DebugLabelT *nextBucket; /* in bucket */
79 struct DebugLabelT *nextName; /* list of all known labels */
80 } DebugLabel;
82 /*TODO: faster searching by name */
83 static DebugLabel *labelBuckets[65536];
84 static DebugLabel *labelHead = NULL;
85 static DebugLabel *labelTail = NULL;
87 static __attribute__((constructor)) void dbgInitLabelsCtor (void) {
88 for (unsigned f = 0; f < 65536; ++f) labelBuckets[f] = NULL;
92 void dbgClearLabels (void) {
93 for (unsigned f = 0; f < 65536; ++f) labelBuckets[f] = NULL;
94 while (labelHead) {
95 DebugLabel *l = labelHead;
96 labelHead = l->nextName;
97 free(l->name);
98 free(l);
100 labelHead = labelTail = NULL;
104 static DebugLabel *dbgFindLabelByNameIntr (const char *name, DebugLabel **prevp) {
105 if (prevp) *prevp = NULL;
106 if (!name || !name[0]) return NULL;
107 DebugLabel *prev = NULL;
108 for (DebugLabel *curr = labelHead; curr; prev = curr, curr = curr->nextName) {
109 if (strcasecmp(curr->name, name) == 0) {
110 if (prevp) *prevp = prev;
111 return curr;
114 return NULL;
118 static void dbgRemoveLabelIntr (DebugLabel *prev, DebugLabel *curr) {
119 if (!curr) return; // just in case
120 /* remove from linked list */
121 if (prev) prev->nextName = curr->nextName; else labelHead = curr->nextName;
122 if (!curr->nextName) labelTail = prev;
123 /* remove from bucket */
124 prev = NULL;
125 for (DebugLabel *cc = labelBuckets[curr->value]; cc; prev = cc, cc = cc->nextBucket) {
126 if (cc == curr) {
127 /* i found her! */
128 if (prev) prev->nextBucket = cc->nextBucket; else labelBuckets[curr->value] = cc->nextBucket;
129 break;
132 /* we can free it here */
133 free(curr->name);
134 free(curr);
138 int dbgFindLabelByName (const char *name) {
139 DebugLabel *lbl = dbgFindLabelByNameIntr(name, NULL);
140 return (lbl ? lbl->value : -1);
144 const char *dbgFindLabelByVal (int val, int asoffset) {
145 if (val < 0 || val > 65535) return NULL;
146 for (DebugLabel *lbl = labelBuckets[val]; lbl; lbl = lbl->nextBucket) {
147 if (lbl->value == val && lbl->asoffset == asoffset) return lbl->name;
149 return NULL;
153 void dbgAddLabel (int addr, const char *name, int asoffset) {
154 if (addr < 0 || addr > 65535 || !name || !name[0]) return;
155 DebugLabel *prev = NULL;
156 DebugLabel *lbl = dbgFindLabelByNameIntr(name, &prev);
157 if (lbl) {
158 if (lbl->value == addr) {
159 /* case */
160 lbl->asoffset = asoffset;
161 strcpy(lbl->name, name);
162 return;
164 /* remove it, so it will be readded */
165 dbgRemoveLabelIntr(prev, lbl);
166 /* remove label with the same value, if there is any */
168 lbl = dbgFindLabelByVal(addr);
169 if (lbl) {
170 lbl = dbgFindLabelByNameIntr(lbl->name, &prev);
171 dbgRemoveLabelIntr(prev, lbl);
175 /* it is guaranteed to be a new label */
176 lbl = malloc(sizeof(DebugLabel));
177 lbl->name = strdup(name);
178 lbl->value = addr;
179 lbl->asoffset = asoffset;
180 lbl->nextBucket = NULL;
181 lbl->nextName = NULL;
182 /* add to list */
183 if (labelTail) labelTail->nextName = lbl; else labelHead = lbl;
184 labelTail = lbl;
185 /* add to bucket */
186 prev = labelBuckets[addr];
187 if (prev) {
188 while (prev->nextBucket) prev = prev->nextBucket;
189 prev->nextBucket = lbl;
190 } else {
191 labelBuckets[addr] = lbl;
196 void dbgRemoveLabel (const char *name) {
197 DebugLabel *prev;
198 DebugLabel *lbl = dbgFindLabelByNameIntr(name, &prev);
199 if (lbl) dbgRemoveLabelIntr(prev, lbl);
203 /* !0: error */
204 int dbgSaveRefFile (const char *fname) {
205 if (!fname || !fname[0]) return -1;
206 FILE *fo = fopen(fname, "w");
207 if (!fo) return -2;
208 for (DebugLabel *curr = labelHead; curr; curr = curr->nextName) {
209 if (fprintf(fo, "#%04X %s\n", curr->value, curr->name) < 0) {
210 fclose(fo);
211 return -3;
214 return (fclose(fo) ? -4 : 0);
218 static __attribute__((always_inline)) inline int digitInBase (char ch, int base) {
219 if (!ch || base < 1) return -1;
220 if (ch >= '0' && ch <= '9') {
221 ch -= '0';
222 return (ch < base ? ch : -1);
224 if (base <= 10) return -1;
225 if (ch >= 'a' && ch <= 'z') ch -= 'a';
226 else if (ch >= 'A' && ch <= 'Z') ch -= 'A';
227 else return -1;
228 ch += 10;
229 return (ch < base ? ch : -1);
233 /* !0: error */
234 int dbgLoadRefFile (const char *fname) {
235 if (!fname || !fname[0]) return -1;
236 FILE *fl = fopen(fname, "r");
237 if (!fl) return -2;
238 dbgClearLabels();
239 char line[512];
240 int ignoreLine = 0;
241 while (fgets(line, (int)sizeof(line)-2, fl) != NULL) {
242 size_t slen = strlen(line);
243 if (slen == 0) { ignoreLine = 1; continue; }
244 const int eol = (line[slen-1] == '\n' || line[slen-1] == '\r');
245 if (ignoreLine) { ignoreLine = !eol; continue; }
246 ignoreLine = !eol;
248 /* remove comments */
249 for (;;) {
250 char *cmt = strchr(line, ';');
251 if (cmt) {
252 *cmt = 0;
253 slen = strlen(line);
254 continue;
256 cmt = strstr(line, "//");
257 if (cmt) {
258 *cmt = 0;
259 slen = strlen(line);
260 continue;
262 break;
265 /* remove trailing spaces */
266 while (slen > 0 && (unsigned)(line[slen-1]&0xff) <= 32) --slen;
267 if (slen == 0) continue;
268 line[slen] = 0;
270 /* remove leading spaces */
271 slen = 0;
272 while (line[slen] && (unsigned)(line[slen]&0xff) <= 32) ++slen;
273 if (!line[slen]) continue; /* empty line */
274 memmove(line, line+slen, strlen(line+slen)+1);
276 /* parse */
277 size_t pos;
278 int base;
279 if (line[0] == '$' || line[0] == '#' || (line[0] == '0' && (line[1] == 'x' || line[1] == 'X'))) {
280 /* hex */
281 pos = (line[0] == '0' ? 2 : 1);
282 base = 16;
283 } else if (digitInBase(line[0], 10)) {
284 pos = 0;
285 base = 10;
286 } else {
287 continue;
289 if (digitInBase(line[pos], base) < 0) continue;
290 int val = 0;
291 while (line[pos]) {
292 int d = digitInBase(line[pos], base);
293 if (d < 0) break;
294 val = val*base+d;
295 if (val > 65535) break;
296 ++pos;
298 if (val > 65535 || !line[pos] || (unsigned)(line[pos]&0xff) > 32) continue;
299 while (line[pos] && (unsigned)(line[pos]&0xff) <= 32) ++pos;
300 if (!line[pos]) continue; /* empty line */
301 memmove(line, line+pos, strlen(line+pos)+1);
303 pos = 1;
304 while (line[pos] && (unsigned)(line[pos]&0xff) > 32) ++pos;
305 const char occ = line[pos];
306 line[pos] = 0;
307 //fprintf(stderr, "NEW LABEL: #%04X <%s>\n", (unsigned)val, line);
308 // ignore labels starting with "__", or ending with "_"
309 // nope, add "X...__" labels as offsets (my convention)
310 const char *lbltype = "";
311 if (occ) {
312 lbltype = line+pos+1;
313 while (*lbltype && (unsigned)(*lbltype&0xff) <= 32) ++lbltype;
315 // ignore "equ" labels
316 if (strcmp(lbltype, "equ") == 0) continue;
317 #if 0
318 if (pos > 3 && line[0] != '_' && line[pos-1] == '_' && line[pos-2] == '_') {
319 dbgAddLabel(val, line, 1/*asoffset*/);
321 #endif
322 if (pos >= 2 && line[0] == '_' && line[1] == '_') continue;
323 if (pos == 1 && line[0] == '_') continue;
324 if (pos > 1 && line[pos-1] == '_') continue;
325 if (strcmp(lbltype, "stofs") == 0) {
326 dbgAddLabel(val, line, 1/*asoffset*/);
327 } else {
328 dbgAddLabel(val, line, 0/*asoffset*/);
331 fclose(fl);
332 return 0;
336 void dbgIteratorFree (void *it) {
337 /* nothing to do here */
341 static int xstrStartsWithCI (const char *s0, const char *s1) {
342 if (!s0 || !s1 || !s0[0] || !s1[0]) return 0;
343 while (*s1) {
344 char c0 = *s0++;
345 if (!c0) return 0;
346 if (c0 >= 'a' && c0 <= 'z') c0 = c0-'a'+'A';
347 char c1 = *s1++;
348 if (c1 >= 'a' && c1 <= 'z') c1 = c1-'a'+'A';
349 if (c0 != c1) return 0;
351 return 1;
355 /* NULL: no */
356 void *dbgFirstLabelByPrefix (const char *pfx) {
357 if (!pfx || !pfx[0]) return NULL;
358 for (DebugLabel *lbl = labelHead; lbl; lbl = lbl->nextName) {
359 if (xstrStartsWithCI(lbl->name, pfx)) return lbl;
361 return NULL;
364 /* NULL: no more */
365 void *dbgNextLabelByPrefix (void *it, const char *pfx) {
366 if (!it || !pfx || !pfx[0]) return NULL;
367 DebugLabel *lbl = (DebugLabel *)it;
368 for (lbl = lbl->nextName; lbl; lbl = lbl->nextName) {
369 if (xstrStartsWithCI(lbl->name, pfx)) return lbl;
371 return NULL;
375 const char *dbgIteratorLabelName (const void *it) {
376 if (!it) return NULL;
377 const DebugLabel *lbl = (const DebugLabel *)it;
378 return lbl->name;
382 #ifdef USE_URASM_DISASM
383 /* nothing */
384 #else
385 static const char *dbgZymGetLabel (uint16_t value, int type/*ZYM_DIS_ATYPE_XXX*/) {
386 if ((dbgAllowLabels&1) == 0) return NULL;
387 // it is safe to use static buffer here
388 static char namebuf[128];
389 if (type == ZYM_DIS_ATYPE_WORD || type == ZYM_DIS_ATYPE_PC_ADDR ||
390 type == ZYM_DIS_ATYPE_DATA_ADDR)
392 /* \x01: label start; \x02: label end */
393 const char *n = dbgFindLabelByVal(value, 0/*asoffset*/);
394 if (n) {
395 namebuf[0] = '\x02';
396 strncpy(namebuf+1, n, sizeof(namebuf)-2);
397 namebuf[sizeof(namebuf)-1] = 0;
398 namebuf[sizeof(namebuf)-2] = 0;
399 strcat(namebuf, "\x01");
400 return namebuf;
402 } else if (type == ZYM_DIS_ATYPE_OFFSET && offsetLabels) {
403 /* \x01: label start; \x02: label end */
404 int ofs = 0;
405 const char *n = dbgFindLabelByVal(value, 1/*asoffset*/);
406 if (!n) { ofs = 1; n = dbgFindLabelByVal(value+1, 1/*asoffset*/); }
407 if (!n) { ofs = 2; n = dbgFindLabelByVal(value+2, 1/*asoffset*/); }
408 if (!n) { ofs = -1; n = dbgFindLabelByVal(value-1, 1/*asoffset*/); }
409 if (!n) { ofs = -2; n = dbgFindLabelByVal(value-2, 1/*asoffset*/); }
410 //if (!n) { ofs = 3; n = dbgFindLabelByVal(value+3, 1/*asoffset*/); }
411 if (n) {
412 if (ofs) {
413 snprintf(namebuf, sizeof(namebuf), "\x02%s%d%s", (ofs < 0 ? "" : "+"), ofs, n);
414 } else {
415 snprintf(namebuf, sizeof(namebuf), "\x02%s", n);
418 namebuf[0] = '\x02';
419 strncpy(namebuf+1, n, sizeof(namebuf)-2);
421 namebuf[sizeof(namebuf)-1] = 0;
422 namebuf[sizeof(namebuf)-2] = 0;
423 strcat(namebuf, "\x01");
424 return namebuf;
427 return NULL;
429 #endif
432 ////////////////////////////////////////////////////////////////////////////////
433 static int dbgKeyEventMain (SDL_KeyboardEvent *key);
434 static int dbgKeyEventNewAddr (SDL_KeyboardEvent *key);
437 ////////////////////////////////////////////////////////////////////////////////
438 static struct {
439 uint16_t pc;
440 zym_regpair_t bc, de, hl, af, sp, ix, iy;
441 zym_regpair_t bcx, dex, hlx, afx;
442 uint8_t regI;
443 uint8_t regR;
444 int iff1, iff2;
445 uint8_t im;
446 } oldDState;
449 static void dbgSaveRegs (void) {
450 oldDState.pc = z80.pc;
451 oldDState.bc.w = z80.bc.w;
452 oldDState.de.w = z80.de.w;
453 oldDState.hl.w = z80.hl.w;
454 oldDState.af.w = z80.af.w;
455 oldDState.sp.w = z80.sp.w;
456 oldDState.ix.w = z80.ix.w;
457 oldDState.iy.w = z80.iy.w;
458 oldDState.bcx.w = z80.bcx.w;
459 oldDState.dex.w = z80.dex.w;
460 oldDState.hlx.w = z80.hlx.w;
461 oldDState.afx.w = z80.afx.w;
462 oldDState.regI = z80.regI;
463 oldDState.regR = z80.regR;
464 oldDState.iff1 = z80.iff1;
465 oldDState.iff2 = z80.iff2;
466 oldDState.im = z80.im;
468 dbgIntrHit = 0;
469 dbgNMIHit = 0;
473 void dbgInit (void) {
474 memset(dbgBreakpoints, 0, sizeof(dbgBreakpoints));
478 ////////////////////////////////////////////////////////////////////////////////
480 static void dbgDrawrClear (void) {
481 clearVO(dbgOverlay, 255); // transparent
485 static void dbgDrawRect (int x, int y, int w, int h, Uint8 bg, Uint8 fc) {
486 if (bg != 255) fillRectVO(dbgOverlay, x, y, w, h, bg);
487 if (fc != 255) drawFrameVO(dbgOverlay, x+1, y+1, w-2, h-2, fc);
492 ////////////////////////////////////////////////////////////////////////////////
493 static void dbgDrawStack (int x, int y) {
494 int addr = z80.sp.w;
495 #if 0
496 dbgDrawRect(x, y, 10*6+10, 4*8+6, 0, 7);
497 // skip frame
498 x += 3;
499 y += 3;
500 addr = (addr-4)&0xffff;
501 for (int f = 0; f < 8; ++f) {
502 Uint8 fc = (f < 2 ? 7: f > 2 ? 70 : 15);
503 char buf[8];
504 snprintf(buf, sizeof(buf), "#%02X", z80.mem_read(&z80, (addr+1)&0xffff, ZYM_MEMIO_OTHER));
505 snprintf(buf+3, sizeof(buf)-3, "%02X", z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER));
506 addr = (addr+2)&0xffff;
507 drawStr6VO(dbgOverlay, buf, x+(f < 4 ? 0 : 5*6+4), y+8*(f&0x03), fc, 255);
509 #else
510 x /= 6;
511 y /= 8;
512 addr = (addr-8)&0xffff;
513 vt_writechars(x, y+0, 5*3, ' ', 7);
514 vt_writechars(x, y+1, 5*3, ' ', 7);
515 vt_writechars(x, y+2, 5*3, ' ', 7);
516 vt_writechars(x, y+3, 5*3, ' ', 7);
517 for (int f = 0; f < 12; ++f) {
518 Uint8 fc = (f < 2+4 ? 7: f > 2+4 ? 5 : 15);
519 char buf[8];
520 snprintf(buf, sizeof(buf), "#%02X", z80.mem_read(&z80, (addr+1)&0xffff, ZYM_MEMIO_OTHER));
521 snprintf(buf+3, sizeof(buf)-3, "%02X", z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER));
522 addr = (addr+2)&0xffff;
523 vt_writestrz(x+(f < 4 ? 0 : f < 8 ? 6 : 12), y+(f&0x03), buf, fc);
525 #endif
529 static void dbgDrawMemBytes (int x, int y, uint16_t addr) {
530 char buf[8];
531 addr = (addr-8)&0xffff;
533 vt_writechars(x, y+0, 5*3, ' ', 7);
534 vt_writechars(x, y+1, 5*3, ' ', 7);
535 vt_writechars(x, y+2, 5*3, ' ', 7);
536 vt_writechars(x, y+3, 5*3, ' ', 7);
538 for (int dy = 0; dy < 4; ++dy) {
539 uint8_t bc = (dy == 1 ? 1 : 0);
540 snprintf(buf, sizeof(buf), "%04X", addr);
541 vt_writestrz(x, y+dy, buf, (0)|(5<<4));
542 for (int dx = 0; dx < 8; ++dx) {
543 uint8_t fc = (dx&1 ? 5 : 7);
544 snprintf(buf, sizeof(buf), "%02X", z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER));
545 addr = (addr+1)&0xffff;
546 vt_writestrz(x+4+dx*2, y+dy, buf, fc|(bc<<4));
552 static void dbgDrawMemBytesChars (int x, int y, uint16_t addr) {
553 char buf[8];
554 addr = (addr-4)&0xffff;
555 vt_writechars(x, y+0, 20, ' ', 0x07);
556 vt_writechars(x, y+1, 20, ' ', 0x17);
557 vt_writechars(x, y+2, 20, ' ', 0x07);
558 vt_writechars(x, y+3, 20, ' ', 0x07);
559 for (int dy = 0; dy < 4; ++dy) {
560 uint8_t bc = (dy == 1 ? 1 : 0);
561 snprintf(buf, sizeof(buf), "%04X", addr);
562 vt_writestrz(x, y+dy, buf, (0)|(5<<4));
563 for (int dx = 0; dx < 4; ++dx) {
564 uint8_t b = z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER);
565 uint8_t fc = (dx&1 ? 5 : 7);
566 snprintf(buf, sizeof(buf), "%02X", b);
567 addr = (addr+1)&0xffff;
568 vt_writestrz(x+5+dx*2, y+dy, buf, fc|(bc<<4));
569 vt_writechar(x+7+4*2+dx, y+dy, b, fc|(bc<<4));
575 static void dbgDrawMemBytes6 (int x, int y, uint16_t addr) {
576 char buf[8];
577 addr = (addr-6)&0xffff;
578 vt_writechars(x, y+0, 20, ' ', 0x07);
579 vt_writechars(x, y+1, 20, ' ', 0x17);
580 vt_writechars(x, y+2, 20, ' ', 0x07);
581 vt_writechars(x, y+3, 20, ' ', 0x07);
582 for (int dy = 0; dy < 4; ++dy) {
583 uint8_t bc = (dy == 1 ? 1 : 0);
584 snprintf(buf, sizeof(buf), "%04X", addr);
585 vt_writestrz(x, y+dy, buf, (0)|(5<<4));
586 for (int dx = 0; dx < 6; ++dx) {
587 uint8_t b = z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER);
588 uint8_t fc = (dx&1 ? 5 : 7);
589 snprintf(buf, sizeof(buf), "%02X", b);
590 addr = (addr+1)&0xffff;
591 vt_writestrz(x+5+dx*2, y+dy, buf, fc|(bc<<4));
597 static void dbgDrawHexW (int x, int y, uint16_t n, int hilight) {
598 Uint8 fc = (hilight > 0 ? 15 : 7);
599 Uint8 bc = (hilight < 0 ? 0 : 0);
600 char buf[8];
601 snprintf(buf, sizeof(buf), "%04X", n);
602 //drawStr6VO(dbgOverlay, buf, x, y, fc, bc);
603 vt_writestrz(x/6, y/8, buf, fc|(bc<<4));
607 static void dbgDrawHexB (int x, int y, uint16_t n, int hilight) {
608 Uint8 fc = (hilight ? 15 : 7);
609 char buf[8];
610 snprintf(buf, sizeof(buf), "%02X", n);
611 //drawStr6VO(dbgOverlay, buf, x, y, fc, 255);
612 vt_writestrz(x/6, y/8, buf, fc);
616 static void dbgDrawPortsFFD (void) {
617 #if 0
618 drawStr6VO(dbgOverlay, "7FFD:# ", dbgOverlay->w-8*6-3, 2*8+3, 70, 0);
619 dbgDrawHexB(dbgOverlay->w-2*6-3, 2*8+3, zxLastOut7ffd, 0);
620 drawStr6VO(dbgOverlay, "1FFD:# ", dbgOverlay->w-8*6-3, 3*8+3, 70, 0);
621 dbgDrawHexB(dbgOverlay->w-2*6-3, 3*8+3, zxLastOut1ffd, 0);
622 #else
623 char buf[32];
624 snprintf(buf, sizeof(buf), "7FFD:#%02X", zxLastOut7ffd);
625 vt_writestrz(VID_TEXT_WIDTH-8, 2, buf, 7);
626 snprintf(buf, sizeof(buf), "1FFD:#%02X", zxLastOut1ffd);
627 vt_writestrz(VID_TEXT_WIDTH-8, 3, buf, 7);
628 #endif
632 static void dbgDrawROMs (void) {
633 #if 0
634 drawStr6VO(dbgOverlay, "R ", dbgOverlay->w-3*6-3, 3, 70, 0);
635 dbgDrawHexB(dbgOverlay->w-2*6-3, 3, zxLastPagedROM, 0);
636 #else
637 char buf[16];
638 snprintf(buf, sizeof(buf), "R%02X", zxLastPagedROM);
639 vt_writestrz(VID_TEXT_WIDTH-3, 0, buf, 7);
640 #endif
644 static void dbgDrawPCPrevPC (void) {
645 char buf[32];
646 #if 0
647 int tx = dbgOverlay->w-8*6-3;
648 snprintf(buf, sizeof(buf), "%04X", z80.prev_pc);
649 drawStr6VO(dbgOverlay, buf, tx, 3, 70, 0);
650 snprintf(buf, sizeof(buf), "%04X=PC", z80.org_pc);
651 drawStr6VO(dbgOverlay, buf, tx, 11, 7, 0);
652 #else
653 snprintf(buf, sizeof(buf), "%04X", z80.prev_pc);
654 vt_writestrz(VID_TEXT_WIDTH-8, 0, buf, 5);
655 snprintf(buf, sizeof(buf), "%04X=PC", z80.org_pc);
656 vt_writestrz(VID_TEXT_WIDTH-8, 1, buf, 15);
657 #endif
661 static void dbgDrawPortsRegRPCs () {
662 #if 0
663 int x = dbgOverlay->w-8*6-5;
664 dbgDrawRect(x, 0, 8*6+5, 4*8+6, 0, 7);
665 #endif
666 dbgDrawPortsFFD();
667 dbgDrawROMs();
668 dbgDrawPCPrevPC();
672 static void dbgDrawRegs (int x, int y) {
673 const char *flgS[2] = { "sz.h.pnc", "SZ5H3PNC" };
674 char buf[32];
676 #if 0
677 dbgDrawRect(x, y, 32*6+6, 4*8+6, 0, 7);
678 // skip frame
679 x += 3;
680 y += 3;
682 if (dbgIntrHit) drawChar6VO(dbgOverlay, 'I', dbgOverlay->w-6, 0, 15, 0);
683 if (dbgNMIHit) drawChar6VO(dbgOverlay, 'N', dbgOverlay->w-6*2, 0, 14, 0);
684 if (zxTRDOSPagedIn) drawStr6VO(dbgOverlay, "TRD", dbgOverlay->w-6*3, 8, 15, 0);
686 drawStr6VO(dbgOverlay, "af: af' sp: ir:", x, y+8*0, 70, 255);
687 drawStr6VO(dbgOverlay, "bc: bc' pc: t:", x, y+8*1, 70, 255);
688 drawStr6VO(dbgOverlay, "de: de' ix: im ,i:", x, y+8*2, 70, 255);
689 drawStr6VO(dbgOverlay, "hl: hl' iy:", x, y+8*3, 70, 255);
691 dbgDrawHexW(x+3*6, y+8*0, z80.af.w, (z80.af.w != oldDState.af.w));
692 dbgDrawHexW(x+3*6, y+8*1, z80.bc.w, (z80.bc.w != oldDState.bc.w));
693 dbgDrawHexW(x+3*6, y+8*2, z80.de.w, (z80.de.w != oldDState.de.w));
694 dbgDrawHexW(x+3*6, y+8*3, z80.hl.w, (z80.hl.w != oldDState.hl.w));
696 dbgDrawHexW(x+11*6, y+8*0, z80.afx.w, (z80.afx.w != oldDState.afx.w));
697 dbgDrawHexW(x+11*6, y+8*1, z80.bcx.w, (z80.bcx.w != oldDState.bcx.w));
698 dbgDrawHexW(x+11*6, y+8*2, z80.dex.w, (z80.dex.w != oldDState.dex.w));
699 dbgDrawHexW(x+11*6, y+8*3, z80.hlx.w, (z80.hlx.w != oldDState.hlx.w));
701 dbgDrawHexW(x+19*6, y+8*0, z80.sp.w, (z80.sp.w != oldDState.sp.w));
702 dbgDrawHexW(x+19*6, y+8*1, z80.pc, (z80.pc != oldDState.pc));
703 dbgDrawHexW(x+19*6, y+8*2, z80.ix.w, (z80.ix.w != oldDState.ix.w));
704 dbgDrawHexW(x+19*6, y+8*3, z80.iy.w, (z80.iy.w != oldDState.iy.w));
706 dbgDrawHexB(x+28*6, y+8*0, z80.regI, (z80.regI != oldDState.regI));
707 dbgDrawHexB(x+30*6, y+8*0, z80.regR, (z80.regR != oldDState.regR));
709 snprintf(buf, sizeof(buf), "%5d", z80.tstates);
710 drawStr6VO(dbgOverlay, buf, x+27*6, y+8*1, 7, 0);
712 drawChar6VO(dbgOverlay, z80.im+'0', x+26*6, y+8*2, (z80.im != oldDState.im ? 15 : 7), 255);
713 drawChar6VO(dbgOverlay, z80.iff1+'0', x+30*6, y+8*2, (z80.iff1 != oldDState.iff1 ? 15 : 7), 255);
714 drawChar6VO(dbgOverlay, z80.iff2+'0', x+31*6, y+8*2, (z80.iff2 != oldDState.iff2 ? 15 : 7), 255);
716 for (int f = 0; f < 8; ++f) {
717 Uint8 c;
718 if (((z80.af.f>>(7-f))&0x01) != ((oldDState.af.f>>(7-f))&0x01)) c = 15;
719 else if ((z80.af.f>>(7-f))&0x01) c = 7;
720 else c = 70;
721 drawChar6VO(dbgOverlay, flgS[(z80.af.f>>(7-f))&0x01][f], x+(24+f)*6, y+8*3, c, 255);
723 #else
724 x /= 6;
725 y /= 8;
726 if (dbgIntrHit) vt_writechar(VID_TEXT_WIDTH-1, 0, 'I', 15);
727 if (dbgNMIHit) vt_writechar(VID_TEXT_WIDTH-2, 0, 'N', 14);
728 if (zxTRDOSPagedIn) vt_writestrz(VID_TEXT_WIDTH-3, 1, "TRD", 15);
730 vt_writestrz(x, y+0, "AF: AF' SP: IR: ", 5);
731 vt_writestrz(x, y+1, "BC: BC' PC: t: ", 5);
732 vt_writestrz(x, y+2, "DE: DE' IX: im ,IF", 5);
733 vt_writestrz(x, y+3, "HL: HL' IY: ", 5);
735 x *= 6;
736 y *= 8;
737 dbgDrawHexW(x+3*6, y+8*0, z80.af.w, (z80.af.w != oldDState.af.w));
738 dbgDrawHexW(x+3*6, y+8*1, z80.bc.w, (z80.bc.w != oldDState.bc.w));
739 dbgDrawHexW(x+3*6, y+8*2, z80.de.w, (z80.de.w != oldDState.de.w));
740 dbgDrawHexW(x+3*6, y+8*3, z80.hl.w, (z80.hl.w != oldDState.hl.w));
742 dbgDrawHexW(x+11*6, y+8*0, z80.afx.w, (z80.afx.w != oldDState.afx.w));
743 dbgDrawHexW(x+11*6, y+8*1, z80.bcx.w, (z80.bcx.w != oldDState.bcx.w));
744 dbgDrawHexW(x+11*6, y+8*2, z80.dex.w, (z80.dex.w != oldDState.dex.w));
745 dbgDrawHexW(x+11*6, y+8*3, z80.hlx.w, (z80.hlx.w != oldDState.hlx.w));
747 dbgDrawHexW(x+19*6, y+8*0, z80.sp.w, (z80.sp.w != oldDState.sp.w));
748 dbgDrawHexW(x+19*6, y+8*1, z80.pc, (z80.pc != oldDState.pc));
749 dbgDrawHexW(x+19*6, y+8*2, z80.ix.w, (z80.ix.w != oldDState.ix.w));
750 dbgDrawHexW(x+19*6, y+8*3, z80.iy.w, (z80.iy.w != oldDState.iy.w));
752 dbgDrawHexB(x+28*6, y+8*0, z80.regI, (z80.regI != oldDState.regI));
753 dbgDrawHexB(x+30*6, y+8*0, z80.regR, (z80.regR != oldDState.regR));
755 snprintf(buf, sizeof(buf), "%5d", z80.tstates);
756 vt_writestrz((x+27*6)/6, y/8+1, buf, 7);
758 x /= 6;
759 y /= 8;
760 vt_writechar(x+26, y+2, z80.im+'0', (z80.im != oldDState.im ? 15 : 7));
761 vt_writechar(x+30, y+2, z80.iff1+'0', (z80.iff1 != oldDState.iff1 ? 15 : 7));
762 vt_writechar(x+31, y+2, z80.iff2+'0', (z80.iff2 != oldDState.iff2 ? 15 : 7));
764 for (int f = 0; f < 8; ++f) {
765 Uint8 c;
766 if (((z80.af.f>>(7-f))&0x01) != ((oldDState.af.f>>(7-f))&0x01)) c = 15;
767 else if ((z80.af.f>>(7-f))&0x01) c = 7;
768 else c = 5;
769 vt_writechar(x+(24+f), y+3, flgS[(z80.af.f>>(7-f))&0x01][f], c);
771 #endif
775 ////////////////////////////////////////////////////////////////////////////////
776 static uint8_t dbgGetByte (uint16_t addr) {
777 return z80.mem_read(&z80, addr, ZYM_MEMIO_OTHER);
781 static inline int dbgOpLenAtAddr (uint16_t addr) {
782 #ifdef USE_URASM_DISASM
783 const int idx = urasm_disasm_opfind(addr);
784 return urasm_disasm_oplen(idx);
785 #else
786 zym_op_meminfo_t nfo;
787 zym_op_meminfo(&z80, &nfo, addr);
788 return nfo.inslen;
789 #endif
793 static void dbgUnasmUp (void) {
794 for (int f = 8; f > 0; --f) {
795 uint16_t a = ((int32_t)dbgUnasmTop-f)&0xffff;
796 const int inslen = dbgOpLenAtAddr(a);
797 if (((a+inslen)&0xffff) == dbgUnasmTop) { dbgUnasmTop = a; return; }
799 dbgUnasmTop = ((int32_t)dbgUnasmTop-1)&0xffff;
803 static void dbgUnasmDown (void) {
804 const int inslen = dbgOpLenAtAddr(dbgUnasmTop);
805 dbgUnasmTop = (dbgUnasmTop+inslen)&0xffff;
809 static void dbgBuildPageInfo (void) {
810 int a = dbgUnasmTop;
811 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) {
812 const int inslen = dbgOpLenAtAddr(a);
813 dbgPageAddrs[f] = a;
814 a = (a+inslen)&0xffff;
819 static void dbgCenterUnasm (uint16_t addr) {
820 dbgUnasmTop = addr;
821 for (int f = 0; f < DBG_UNASM_HEIGHT/2-1; ++f) dbgUnasmUp();
822 dbgPageY = 0;
823 dbgBuildPageInfo();
824 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) {
825 if (dbgPageAddrs[f] == addr) { dbgPageY = f; return; }
827 dbgUnasmTop = z80.pc;
828 dbgBuildPageInfo();
832 /* \x01: default color; \x02: label start; \x03: number start */
833 static void dbgDrawStr6VO (const char *s, int x, int y, Uint8 fc, Uint8 bg) {
834 if (!s || !s[0]) return;
835 //VOverlay *v = dbgOverlay;
836 int inLabel = 0;
837 while (*s) {
838 char ch = *s++;
839 if (ch >= 1 && ch <= 3) { inLabel = ch-1; continue; }
840 /*HACK!*/
841 //drawChar6VO(v, ch, x, y, (inLabel == 1 ? (bg < 7 ? 5+8 : 69) : (bg < 7 && inLabel == 2) ? 67 : ch == ',' ? (bg < 7 ? 69 : 5) : fc), bg);
842 uint8_t cc =
843 inLabel == 1 ? (bg >= 2 ? 5+8 : 5) :
844 (bg < 7 && inLabel == 2) ? 4+8 :
845 ch == ',' ? (bg < 2 ? 3 : 1) :
847 vt_writechar(x/6, y/8, ch, (cc&0x0f)|(bg<<4));
848 x += 6;
853 static int dbgStrLen (const char *s) {
854 if (!s) return 0;
855 int res = 0;
856 while (*s) {
857 char ch = *s++;
858 if (ch >= 1 && ch <= 3) continue;
859 ++res;
861 return res;
865 /* returns bytes in instruction */
866 static int dbgDrawUnasmLine (uint16_t addr, int x, int y, Uint8 fc, Uint8 bc) {
867 char buf[256];
868 char *t;
869 #ifdef USE_URASM_DISASM
870 const int inslen = dbgOpLenAtAddr(addr);
871 char disbuf[128];
872 urasm_disasm_opdisasm(disbuf, addr);
873 #else
874 zym_disop_t disop;
875 zym_disasm_init(&disop);
876 disop.flags = ZYM_DIS_FLAGS_DEFAULT;
877 zym_disasm_one(&z80, &disop, addr);
878 const int inslen = disop.inslen;
879 char *disbuf = disop.disbuf;
880 #endif
881 // clear line
882 //dbgDrawRect(x, y, 52*6+2, 8, bc, 255);
883 vt_writechars(x/6, y/8, VID_TEXT_WIDTH, ' ', fc|(bc<<4));
884 // draw address
885 snprintf(buf, sizeof(buf), "%04X", addr);
886 //drawStr6VO(dbgOverlay, buf, x, y, fc, bc);
887 vt_writestrz(x/6, y/8, buf, fc|(bc<<4));
889 int opx = x/6+5;
890 // draw opcode bytes
891 for (int f = 0; f < inslen; ++f) {
892 snprintf(buf, sizeof(buf), "%02X", dbgGetByte((addr+f)&0xffff));
893 //drawStr6VO(dbgOverlay, buf, x+(5+f*2)*6, y, (bc >= 7 ? fc : 68), bc);
894 vt_writestrz(opx, y/8, buf, (bc >= 7 ? fc : 4+(f&1))|(bc<<4));
895 opx += 2;
896 if (inslen <= 3) ++opx;
899 // draw opcode
900 if ((t = strchr(disbuf, '\t')) != NULL) *t++ = '\0';
901 dbgDrawStr6VO(disbuf, x+16*6, y, fc, bc);
902 // draw operands
903 if (t != NULL) {
904 dbgDrawStr6VO(t, x+21*6, y, fc, bc);
907 // draw address label
908 if (dbgAllowLabels&2) {
909 const char *lbl = dbgFindLabelByVal(addr, 0/*asoffset*/);
910 if (lbl) {
911 int lleft = VID_TEXT_WIDTH-(t ? 21+dbgStrLen(t) : dbgStrLen(disbuf));
912 char lbuf[128];
913 lbuf[0] = ';';
914 strncpy(lbuf+1, lbl, sizeof(lbuf)-1);
915 lbuf[sizeof(lbuf)-1] = 0;
916 int nlen = (int)strlen(lbuf);
917 if (nlen > lleft) { nlen = lleft; lbuf[lleft] = 0; }
918 //drawStr6VO(dbgOverlay, lbuf, x+(52-nlen)*6, y, 4, bc);
919 vt_writestrz(x/6+(VID_TEXT_WIDTH-nlen), y/8, lbuf, 4|(bc<<4));
922 return inslen;
926 static void dbgDrawUnasm (int x, int y) {
927 uint16_t addr = dbgUnasmTop;
929 //dbgDrawRect(x, y, 52*6+6+2, DBG_UNASM_HEIGHT*8+6, 0, 7);
931 // skip frame
932 //x += 3;
933 //y += 3;
935 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) {
936 Uint8 fc, bc;
937 int inslen;
939 if (addr == z80.pc) {
940 if (dbgBreakpoints[addr]&DBG_BP_EXEC) {
941 if (f == dbgPageY) {
942 fc = 2;
943 bc = 15;
944 } else {
945 fc = 2;
946 bc = 7;
948 } else {
949 fc = 0;
950 bc = (f == dbgPageY ? 15 : 7);
952 } else {
953 if (dbgBreakpoints[addr]&DBG_BP_EXEC) {
954 if (f == dbgPageY) {
955 fc = 15;
956 bc = 2;
957 } else {
958 fc = 7;
959 bc = 2;
961 } else {
962 if (f == dbgPageY) {
963 fc = 7;
964 bc = 1;
965 } else {
966 fc = 7;
967 bc = 0;
971 inslen = dbgDrawUnasmLine(addr, x, y+f*8, fc, bc);
972 addr = (addr+inslen)&0xffff;
977 // -2: on screen, but invisible (must move left to fix)
978 // -1: must go up
979 // 0..DBG_UNASM_HEIGHT-1: ok
980 // DBG_UNASM_HEIGHT
981 static int dbgIsPCOnScreen (void) {
982 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) {
983 if (dbgPageAddrs[f] == z80.pc) return f;
986 if (z80.pc < dbgUnasmTop) {
987 return -1;
988 } else {
989 int end = dbgUnasmTop;
991 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) {
992 const int inslen = dbgOpLenAtAddr(end&0xffff);
993 if (end == z80.pc) return f;
994 if (z80.pc > end && z80.pc < end+inslen) return -2; // invisible
995 end += inslen;
997 return DBG_UNASM_HEIGHT;
1002 static void dbgToPC (void) {
1003 dbgBuildPageInfo();
1004 for (;;) {
1005 int pcpos = dbgIsPCOnScreen();
1007 if (pcpos == -2) {
1008 dbgUnasmTop = ((int32_t)dbgUnasmTop+1)&0xffff;
1009 dbgBuildPageInfo();
1010 } else if (pcpos < 0 || pcpos >= DBG_UNASM_HEIGHT) {
1011 dbgCenterUnasm(z80.pc);
1012 } else {
1013 dbgPageY = pcpos;
1014 break;
1020 ////////////////////////////////////////////////////////////////////////////////
1021 // to view original screen
1022 int dbgIsHidden (void) {
1023 return dbgHidden;
1027 void dbgDraw (void) {
1028 vt_cls(0xb0, 0x01);
1030 //if (dbgOverlay == NULL) dbgOverlay = createVO(320, 240);
1031 //dbgDrawrClear();
1032 if (!dbgHidden) {
1033 dbgDrawRegs(0, 0);
1034 dbgDrawPortsRegRPCs();
1035 dbgDrawStack(32*6+6+6-6, 0);
1037 if (dbgMemMode == 0) dbgDrawMemBytes(51, 0, z80.pc);
1038 else if (dbgMemMode == 1) dbgDrawMemBytes6(51, 0, z80.pc);
1039 else dbgDrawMemBytesChars(51, 0, z80.pc);
1041 vt_writechars(0, 4, VID_TEXT_WIDTH, 0xc4, 5);
1042 for (int y = 0; y < 4; ++y) {
1043 vt_writechar(32, y, 0xb3, 5);
1044 vt_writechar(50, y, 0xb3, 5);
1045 vt_writechar(VID_TEXT_WIDTH-9, y, 0xb3, 5);
1047 vt_writechar(32, 4, 0xc1, 5);
1048 vt_writechar(50, 4, 0xc1, 5);
1049 vt_writechar(VID_TEXT_WIDTH-9, 4, 0xc1, 5);
1051 dbgDrawUnasm(0, 4*8+5+8);
1053 if (dbgDrawOverlayFn != NULL) dbgDrawOverlayFn();
1054 //blitVO(dbgOverlay, 0, 0, dbgAlpha);
1056 #if 0
1057 for (int y = 0; y < 16; ++y) {
1058 for (int x = 0; x < 16; ++x) {
1059 vt_writechar(x, y, y*16+x, 0x0f);
1062 for (int x = 0; x < 16; ++x) {
1063 vt_writechar(x, VID_TEXT_HEIGHT-1, '*', (x<<4)|(x < 8 ? 15 : 0));
1065 #endif
1066 //vt_cls(0, 0);
1070 ////////////////////////////////////////////////////////////////////////////////
1071 void dbgSetActive (int st) {
1072 st = !!st;
1073 if (st != debuggerActive) {
1074 if ((debuggerActive = st) != 0) {
1075 #ifdef USE_URASM_DISASM
1076 urasm_getbyte = dbgGetByte;
1077 #else
1078 zym_disasm_getlabel = dbgZymGetLabel;
1079 zym_disasm_mnemo_end = "\t";
1080 zym_disasm_num_start = "\x03";
1081 zym_disasm_num_end = "\x01";
1082 #endif
1083 dbgSaveRegs();
1084 dbgCenterUnasm(z80.pc);
1085 dbgKeyEventFn = dbgKeyEventMain;
1086 dbgHidden = 0;
1087 zxRealiseScreen(z80.tstates);
1088 } else {
1089 if (dbgBreakpoints[z80.pc]) {
1090 dbgAddrSkip = z80.pc;
1097 ////////////////////////////////////////////////////////////////////////////////
1098 int dbgKeyEvent (SDL_KeyboardEvent *key) {
1099 if (dbgKeyEventFn != NULL) return dbgKeyEventFn(key);
1100 return 0;
1104 ////////////////////////////////////////////////////////////////////////////////
1105 static int dbgNewAddrPos;
1106 static char dbgNewAddrBuf[8];
1107 static KeyEventCB dbgNewAddrPrevKeyEventFn;
1108 static DrawOverlayCB dbgNewAddrPrevDrawOverlayFn;
1111 void dbgSetUnasmAddr (uint16_t addr) {
1112 dbgUnasmTop = addr;
1113 dbgPageY = 0;
1114 dbgBuildPageInfo();
1118 static void dbgNewAddrDeinit (int setaddr) {
1119 dbgKeyEventFn = dbgNewAddrPrevKeyEventFn;
1120 dbgDrawOverlayFn = dbgNewAddrPrevDrawOverlayFn;
1121 if (setaddr) {
1122 dbgUnasmTop = 0;
1123 for (int f = 0; f < 4; ++f) dbgUnasmTop = (dbgUnasmTop*16)+dbgNewAddrBuf[f]-'0'-(dbgNewAddrBuf[f] > '9' ? 7 : 0);
1124 dbgPageY = 0;
1125 dbgBuildPageInfo();
1130 static void dbgDrawNewAddr (void) {
1131 #if 0
1132 drawStr6VO(dbgOverlay, dbgNewAddrBuf, 3, 4*8+5+3/*+dbgPageY*8*/, 0, 5+8);
1133 drawChar6VO(dbgOverlay, dbgNewAddrBuf[dbgNewAddrPos], 3+dbgNewAddrPos*6, 4*8+5+3/*+dbgPageY*8*/, 0, 6+8);
1134 #else
1135 vt_writestrz(0, 4+1, dbgNewAddrBuf, (5+8)<<4);
1136 vt_writechar(0+dbgNewAddrPos, 4+1, dbgNewAddrBuf[dbgNewAddrPos], (6+8)<<4);
1137 #endif
1141 static int dbgKeyEventNewAddr (SDL_KeyboardEvent *key) {
1142 if (key->type == SDL_KEYDOWN && (key->keysym.mod&KMOD_CTRL) == 0) {
1143 if (key->keysym.unicode >= 32 && key->keysym.unicode < 127) {
1144 char ch = toupper(key->keysym.unicode);
1145 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
1146 dbgNewAddrBuf[dbgNewAddrPos++] = ch;
1147 if (dbgNewAddrPos > 3) dbgNewAddrPos = 3;
1148 return 1;
1152 switch (key->keysym.sym) {
1153 case SDLK_ESCAPE:
1154 dbgNewAddrDeinit(0);
1155 return 1;
1156 case SDLK_RETURN:
1157 dbgNewAddrDeinit(1);
1158 return 1;
1159 case SDLK_LEFT: case SDLK_KP4: case SDLK_BACKSPACE:
1160 if (dbgNewAddrPos > 0) --dbgNewAddrPos;
1161 return 1;
1162 case SDLK_RIGHT: case SDLK_KP6:
1163 if (dbgNewAddrPos < 3) ++dbgNewAddrPos;
1164 return 1;
1165 case SDLK_HOME: case SDLK_KP7:
1166 dbgNewAddrPos = 0;
1167 return 1;
1168 case SDLK_END: case SDLK_KP1:
1169 dbgNewAddrPos = 3;
1170 return 1;
1171 case SDLK_p:
1172 if ((key->keysym.mod&KMOD_SHIFT) != 0) {
1173 snprintf(dbgNewAddrBuf, sizeof(dbgNewAddrBuf), "%04X", z80.pc);
1175 break;
1176 default: break;
1180 return 1;
1184 static void dbgNewAddrInit (void) {
1185 dbgNewAddrPrevKeyEventFn = dbgKeyEventFn;
1186 dbgNewAddrPrevDrawOverlayFn = dbgDrawOverlayFn;
1187 dbgKeyEventFn = dbgKeyEventNewAddr;
1188 dbgDrawOverlayFn = dbgDrawNewAddr;
1189 dbgNewAddrPos = 0;
1190 snprintf(dbgNewAddrBuf, sizeof(dbgNewAddrBuf), "%04X", dbgUnasmTop);
1194 ////////////////////////////////////////////////////////////////////////////////
1195 static int dbgKeyEventMain (SDL_KeyboardEvent *key) {
1196 if (key->type == SDL_KEYDOWN && (key->keysym.mod&KMOD_ALT) != 0) {
1197 switch (key->keysym.sym) {
1198 case SDLK_F5:
1199 dbgHidden = !dbgHidden;
1200 return 1;
1201 default: ;
1205 if (key->type == SDL_KEYDOWN && (key->keysym.mod&KMOD_CTRL) != 0) {
1206 int ots;
1208 switch (key->keysym.sym) {
1209 case SDLK_F5:
1210 ots = zxOldScreenTS;
1211 zxOldScreenTS = 0;
1212 zxRealiseScreen(machineInfo.tsperframe);
1213 zxOldScreenTS = ots;
1214 return 1;
1215 case SDLK_UP: case SDLK_KP8:
1216 if (dbgPageY < DBG_UNASM_HEIGHT-1) {
1217 dbgUnasmUp();
1218 dbgBuildPageInfo();
1219 ++dbgPageY;
1221 return 1;
1222 case SDLK_DOWN: case SDLK_KP2:
1223 if (dbgPageY > 0) {
1224 dbgUnasmDown();
1225 dbgBuildPageInfo();
1226 --dbgPageY;
1228 return 1;
1229 default: ;
1233 if (key->type == SDL_KEYDOWN && (key->keysym.mod&KMOD_CTRL) == 0) {
1234 if (dbgHidden) {
1235 switch (key->keysym.sym) {
1236 case SDLK_ESCAPE: case SDLK_RETURN: case SDLK_SPACE:
1237 dbgHidden = 0;
1238 return 1;
1239 default: ;
1241 return 1;
1244 switch (key->keysym.sym) {
1245 case SDLK_ESCAPE:
1246 dbgSetActive(0);
1247 return 1;
1248 case SDLK_UP: case SDLK_KP8:
1249 if (dbgPageY > 0) {
1250 --dbgPageY;
1251 } else {
1252 dbgUnasmUp();
1253 dbgBuildPageInfo();
1255 return 1;
1256 case SDLK_DOWN: case SDLK_KP2:
1257 if (++dbgPageY >= DBG_UNASM_HEIGHT) {
1258 dbgPageY = DBG_UNASM_HEIGHT-1;
1259 dbgUnasmDown();
1260 dbgBuildPageInfo();
1262 return 1;
1263 case SDLK_PAGEUP: case SDLK_KP9:
1264 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) dbgUnasmUp();
1265 dbgBuildPageInfo();
1266 return 1;
1267 case SDLK_PAGEDOWN: case SDLK_KP3:
1268 for (int f = 0; f < DBG_UNASM_HEIGHT; ++f) dbgUnasmDown();
1269 dbgBuildPageInfo();
1270 return 1;
1271 case SDLK_HOME: case SDLK_KP7:
1272 dbgPageY = 0;
1273 return 1;
1274 case SDLK_END: case SDLK_KP1:
1275 dbgPageY = DBG_UNASM_HEIGHT-1;
1276 return 1;
1277 case SDLK_LEFT: case SDLK_KP4:
1278 dbgUnasmTop = ((int32_t)dbgUnasmTop-1)&0xffff;
1279 dbgBuildPageInfo();
1280 return 1;
1281 case SDLK_RIGHT: case SDLK_KP6:
1282 dbgUnasmTop = (dbgUnasmTop+1)&0xffff;
1283 dbgBuildPageInfo();
1284 return 1;
1285 case SDLK_F7: // step
1286 dbgSaveRegs();
1287 dbgAddrSkip = z80.pc;
1288 z80Step();
1289 //fprintf(stderr, "%d %d\n", dbgIntrHit, dbgNMIHit);
1290 zxRealiseScreen(z80.tstates);
1291 dbgAddrSkip = 0;
1292 dbgToPC();
1293 return 1;
1294 case SDLK_F8: // to next
1295 dbgSaveRegs();
1296 dbgAddrSkip = z80.pc;
1298 int na = (dbgPageAddrs[dbgPageY]+dbgOpLenAtAddr(dbgPageAddrs[dbgPageY]))&0xffff;
1299 dbgBreakpoints[na] |= DBG_BP_EXECONE;
1301 dbgSetActive(0);
1302 return 1;
1303 case SDLK_SPACE:
1304 dbgBreakpoints[dbgPageAddrs[dbgPageY]] ^= DBG_BP_EXEC;
1305 return 1;
1306 case SDLK_g:
1307 dbgNewAddrInit();
1308 return 1;
1309 case SDLK_d: // difference between PC and current address
1310 cprintf("PC=#%04X; diff=%d\n", z80.pc, (int)dbgPageAddrs[dbgPageY]-(int)z80.pc);
1311 break;
1312 case SDLK_l:
1313 if ((key->keysym.mod&KMOD_SHIFT) == 0) {
1314 dbgAllowLabels ^= 0x01;
1315 } else {
1316 dbgAllowLabels ^= 0x02;
1318 break;
1319 case SDLK_m:
1320 dbgMemMode = (dbgMemMode+1)%3;
1321 break;
1322 case SDLK_x:
1323 offsetLabels = !offsetLabels;
1324 break;
1325 default: ;
1329 return (dbgHidden ? 1 : 0);
1333 ////////////////////////////////////////////////////////////////////////////////
1334 static int breakpointHit (uint8_t type) {
1335 char sbuf[128];
1336 snprintf(sbuf, sizeof(sbuf), "::breakpointhit %u", type);
1337 if (Jim_Eval(jim, sbuf) == JIM_OK) {
1338 Jim_Obj *res = Jim_GetResult(jim);
1339 long dostop = 0;
1340 /* process string results */
1341 const char *resstr = Jim_String(res);
1342 if (resstr && strcasecmp(resstr, "stop") == 0) dostop = 1;
1343 if (!dostop && Jim_GetLong(jim, res, &dostop) != JIM_OK) dostop = 0;
1344 if (!dostop) return 0;
1345 } else {
1346 Jim_MakeErrorMessage(jim);
1347 cprintf("\4=== JIM ERROR ===\n%s\n=================\n", Jim_GetString(Jim_GetResult(jim), NULL));
1349 dbgSetActive(1);
1350 return 1;
1354 int z80CheckBP (uint16_t addrport, uint8_t type) {
1355 if (type == DBG_BP_NONE) return 0;
1356 int res = 0;
1357 #if 0
1358 uint16_t addrport = z80.pc;
1359 Z80OpMemInfo nfo;
1360 Z80_GetOpMemInfo(&z80, &nfo, addrport);
1361 #endif
1362 if (debuggerActive >= 0) {
1363 if ((dbgBreakpoints[addrport]&type) != 0) {
1364 if (dbgAddrSkip == addrport && (type&(DBG_BP_EXEC|DBG_BP_EXECONE)) != 0) { dbgAddrSkip = 0; return 0; }
1365 type &= dbgBreakpoints[addrport];
1366 dbgBreakpoints[addrport] &= ~DBG_BP_EXECONE;
1367 res = breakpointHit(type);
1368 } else {
1369 /* check low port (0 means any) */
1370 type &= DBG_BP_PORTIN|DBG_BP_PORTOUT;
1371 if (type) {
1372 if ((dbgBreakpoints[addrport&0xff]&type) != 0 || (dbgBreakpoints[0]&type) != 0) {
1373 type &= dbgBreakpoints[addrport];
1374 res = breakpointHit(type);
1379 return res;
1383 const char *dbgGetBPTypeStr (uint8_t type) {
1384 static char buf[16];
1385 size_t pos = 0;
1386 if (type&DBG_BP_EXEC) buf[pos++] = 'E';
1387 if (type&DBG_BP_READ) buf[pos++] = 'R';
1388 if (type&DBG_BP_WRITE) buf[pos++] = 'W';
1389 if (type&DBG_BP_PORTIN) buf[pos++] = 'I';
1390 if (type&DBG_BP_PORTOUT) buf[pos++] = 'O';
1391 if (!pos) return "";
1392 buf[pos] = 0;
1393 return buf;
1397 uint8_t dbgGetBPType (uint16_t addr) {
1398 return dbgBreakpoints[addr];
1402 void dbgSetBPType (uint16_t addr, uint8_t type) {
1403 dbgBreakpoints[addr] = type;
1407 void dbgBPClear (void) {
1408 memset(dbgBreakpoints, 0, sizeof(dbgBreakpoints));
1412 int dbgBPSaveToFile (const char *fname) {
1413 if (!fname || !fname[0]) return -1;
1414 FILE *fo = fopen(fname, "w");
1415 if (!fo) return -1;
1416 for (unsigned f = 0; f < 65536; ++f) {
1417 const uint8_t bpt = dbgGetBPType(f&0xffffu);
1418 if (bpt == DBG_BP_NONE) continue;
1419 if (fprintf(fo, "%02X %02X\n", bpt, f) < 0) {
1420 fclose(fo);
1421 return -3;
1424 return (fclose(fo) == 0 ? 0 : -4);
1428 int dbgBPLoadFromFile (const char *fname) {
1429 if (!fname || !fname[0]) return -1;
1430 FILE *fl = fopen(fname, "r");
1431 if (!fl) return -1;
1432 dbgBPClear();
1433 char line[512];
1434 int ignoreLine = 0;
1435 while (fgets(line, (int)sizeof(line)-2, fl) != NULL) {
1436 size_t slen = strlen(line);
1437 if (slen == 0) { ignoreLine = 1; continue; }
1438 const int eol = (line[slen-1] == '\n' || line[slen-1] == '\r');
1439 if (ignoreLine) { ignoreLine = !eol; continue; }
1440 ignoreLine = !eol;
1441 char *cmt = strchr(line, ';');
1442 if (cmt) {
1443 *cmt = 0;
1444 slen = strlen(line);
1446 /* remove trailing spaces */
1447 while (slen > 0 && (unsigned)(line[slen-1]&0xff) <= 32) --slen;
1448 if (slen == 0) continue;
1449 line[slen] = 0;
1450 /* remove leading spaces */
1451 slen = 0;
1452 while (line[slen] && (unsigned)(line[slen]&0xff) <= 32) ++slen;
1453 if (!line[slen]) continue; /* empty line */
1454 memmove(line, line+slen, strlen(line+slen)+1);
1455 /* parse */
1456 if (digitInBase(line[0], 16) < 0) continue;
1457 size_t pos = 0;
1458 /* type */
1459 int type = 0;
1460 for (;;) {
1461 int d = digitInBase(line[pos], 16);
1462 if (d < 0) break;
1463 type = type*16+d;
1464 if (type > 255) break;
1465 ++pos;
1467 if (type == 0 || type > 255) continue;
1468 if (!line[pos] || (unsigned)(line[pos]&0xff) > 32) continue;
1469 /* address */
1470 while (line[pos] && (unsigned)(line[pos]&0xff) <= 32) ++pos;
1471 if (digitInBase(line[pos], 16) < 0) continue;
1472 int addr = 0;
1473 for (;;) {
1474 int d = digitInBase(line[pos], 16);
1475 if (d < 0) break;
1476 addr = addr*16+d;
1477 if (addr > 65535) break;
1478 ++pos;
1480 if (addr > 65535) continue;
1481 if (line[pos] && (unsigned)(line[pos]&0xff) > 32) continue;
1482 dbgSetBPType(addr&0xffffu, type&0xff);
1484 fclose(fl);
1485 return 0;