From 53d190d66021ead8dbf8bb359523461b1addfa2c Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sat, 13 May 2023 09:28:12 +0200 Subject: [PATCH 01/26] basic disasm (no DD/FD prefix handling) --- src/disasm.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/disasm.h | 5 + 2 files changed, 374 insertions(+) create mode 100644 src/disasm.c create mode 100644 src/disasm.h diff --git a/src/disasm.c b/src/disasm.c new file mode 100644 index 0000000..b004b07 --- /dev/null +++ b/src/disasm.c @@ -0,0 +1,369 @@ +#include "disasm.h" + +#include +#include +#include +#include "memory.h" +#include "log.h" + +static const char *t_r[] = { + "b", "c", "d", "e", "h", "l", "(hl)", "a" +}; + +static const char *t_rp[] = { + "bc", "de", "hl", "sp" +}; + +static const char *t_rp2[] = { + "bc", "de", "hl", "af" +}; + +static const char *t_cc[] = { + "nz", "z", "nc", "c", "po", "pe", "p", "m" +}; + +static const char *t_alu[] = { + "add a,", "adc a,", "sub", "sbc a,", "and", "xor", "or", "cp" +}; + +static const char *t_sro[] = { + "rlc", "rrc", "rl", "rr", "sla", "sra", "sll", "srl" +}; + +static const char *t_blk[] = { + "ldi", "cpi", "ini", "outi", + "ldd", "cpd", "ind", "outd", + "ldir", "cpir", "inir", "otir", + "lddr", "cpdr", "indr", "otdr", +}; + +static const char *t_x1_z1_q[] = { + "ret", "exx", "jp hl", "ld sp, hl" +}; + +static const char *t_x3_z3[] = { + "ex (sp), hl", "ex de", "di", "ei" +}; + +static const char *t_x0_z7[] = { + "rlca", "rrca", "rla", "rra", "daa", "cpl", "scf", "ccf" +}; + +static const char *t_x0_z2[] = { + "ld (bc), a", "ld a, (bc)", + "ld (de), a", "ld a, (de)", + "ld (%s), hl", "ld hl, (%s)", + "ld (%s), a", "ld a, (%s)", +}; + +/* should potentially handle labels and shite + * NON REENTRANT */ +static char *get_paddr_str(uint8_t *p_addr) +{ + uint16_t a = p_addr[1] << 8 | p_addr[0]; + static char buf[128]; + snprintf(buf, sizeof(buf), "$%04x", a); + return buf; +} + +static char *get_addr_str(uint16_t addr) +{ + static char buf[128]; + snprintf(buf, sizeof(buf), "$%04x", addr); + return buf; +} + +static char *get_byte_str(uint8_t b) +{ + static char buf[128]; + snprintf(buf, sizeof(buf), "$%02x", b); + return buf; +} + +static char *prefix_cb(uint8_t *data, int *len) +{ + uint8_t op = *data; + + int x = op >> 6; + int y = (op >> 3) & 7; + int z = op & 7; + + char buf[128]; + size_t buflen = sizeof(buf); + + switch (x) + { + case 0: snprintf(buf, buflen, "%s %s", t_sro[y], t_r[z]); break; + case 1: snprintf(buf, buflen, "bit %d, %s", y, t_r[z]); break; + case 2: snprintf(buf, buflen, "res %d, %s", y, t_r[z]); break; + case 3: snprintf(buf, buflen, "set %d, %s", y, t_r[z]); break; + } + + *len = 2; + + size_t slen = strlen(buf) + 1; + char *str = malloc(slen); + if (str == NULL) { + return NULL; + } + memcpy(str, buf, slen-1); + str[slen-1] = 0; + + return str; +} + +static char *prefix_ed(uint8_t *data, int *len) +{ + uint8_t op = *data; + + int x = op >> 6; + int y = (op >> 3) & 7; + int z = op & 7; + int q = op & (1<<3); + int p = (op >> 4) & 3; + + char buf[128] = "UNHANDLED"; + size_t buflen = sizeof(buf); + + switch (x) + { + case 0: + case 3: + strncpy(buf, "nop*", buflen); break; + case 2: + if (z < 4 && y >= 4) + strncpy(buf, t_blk[(y-4)*4 + z], buflen); + else + strncpy(buf, "nop*", buflen); + break; + case 1: + switch (z) + { + case 0: { + char *s = (y != 6) ? "in %s, (c)" : "in (c)"; + snprintf(buf, buflen, s, t_r[z]); + break; + } + case 1: { + char *s = (y != 6) ? "out (c), %s" : "out (c), 0"; + snprintf(buf, buflen, s, t_r[z]); + break; + } + case 2: { + char *s = q ? "adc hl, %s" : "sbc hl, %s"; + snprintf(buf, buflen, s, t_rp[p]); + break; + } + case 3: { + if (q) { + snprintf(buf, buflen, "ld %s, (%s)", t_rp[p], get_paddr_str(data)); + } else { + snprintf(buf, buflen, "ld (%s), %s", get_paddr_str(data), t_rp[p]); + } + data += 2; + break; + } + case 4: strncpy(buf, (y == 0) ? "neg" : "neg*", buflen); break; + case 5: strncpy(buf, (y == 1) ? "reti" : "retn", buflen); break; + case 6: { + static const char *im[] = { "0", "0*", "1", "2", "0*", "0*", "1*", "2*" }; + snprintf(buf, buflen, "im %s", im[y]); + break; + } + case 7: { + static const char *s[] = { + "ld i, a", "ld r, a", + "ld a, i", "ld a, r", + "rrd", "rld", + "nop*", "nop*" + }; + strncpy(buf, s[y], buflen); + break; + } + } + } + + *len = 2; + + size_t slen = strlen(buf) + 1; + char *str = malloc(slen); + if (str == NULL) { + return NULL; + } + memcpy(str, buf, slen-1); + str[slen-1] = 0; + + return str; +} + +char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) +{ + uint8_t *dorg = data; + uint8_t op = *data; + data++; + + int x = op >> 6; + int y = (op >> 3) & 7; + int z = op & 7; + int q = op & (1<<3); + int p = (op >> 4) & 3; + + char buf[128] = "UNHANDLED"; + const size_t buflen = sizeof(buf); + + switch (x) + { + case 0: + switch (z) + { + case 0: + switch (y) + { + case 0: strncpy(buf, "nop", buflen); break; + case 1: strncpy(buf, "ex af", buflen); break; + case 2: + snprintf(buf, buflen, "djnz %s", get_addr_str(pc+2 + (int8_t)*data)); + data++; + break; + case 3: + snprintf(buf, buflen, "jr %s", get_addr_str(pc+2 + (int8_t)*data)); + data++; + break; + default: + snprintf(buf, buflen, "jr %s, %s", t_cc[y-4], get_addr_str(pc+2 + (int8_t)*data)); + data++; + break; + } + break; + case 1: + if (q) { + snprintf(buf, buflen, "add hl, %s", t_rp[p]); + } else { + snprintf(buf, buflen, "ld %s, %s", t_rp[p], get_paddr_str(data)); + data += 2; + } + break; + case 2: + if (y < 4) { + strncpy(buf, t_x0_z2[y], buflen); + } else { + snprintf(buf, buflen, t_x0_z2[y], get_paddr_str(data)); + data += 2; + } + break; + case 3: + if (q) { + snprintf(buf, buflen, "dec %s", t_rp[p]); + } else { + snprintf(buf, buflen, "inc %s", t_rp[p]); + } + break; + case 4: + snprintf(buf, buflen, "inc %s", t_r[y]); + break; + case 5: + snprintf(buf, buflen, "dec %s", t_r[y]); + break; + case 6: + snprintf(buf, buflen, "ld %s, %s", t_r[y], get_byte_str(*data)); + data++; + break; + case 7: + strncpy(buf, t_x0_z7[y], buflen); + break; + } + break; + case 1: + if (z == 6 && y == 6) + strncpy(buf, "halt", buflen); + else + snprintf(buf, buflen, "ld %s, %s", t_r[y], t_r[z]); + break; + case 2: + snprintf(buf, buflen, "%s %s", t_alu[y], t_r[z]); + break; + case 3: + switch (z) + { + case 0: + snprintf(buf, buflen, "ret %s", t_cc[y]); + break; + case 1: + if (q) + strncpy(buf, t_x1_z1_q[p], buflen); + else + snprintf(buf, buflen, "pop %s", t_rp2[p]); + break; + case 2: + snprintf(buf, buflen, "jp %s, %s", t_cc[y], get_paddr_str(data)); + data += 2; + break; + case 3: + switch (y) + { + case 0: + snprintf(buf, buflen, "jp %s", get_paddr_str(data)); + data += 2; + break; + case 1: + return prefix_cb(data, len); + break; + case 2: + snprintf(buf, buflen, "out (%s), a", get_byte_str(*data)); + data++; + break; + case 3: + snprintf(buf, buflen, "in a, (%s)", get_byte_str(*data)); + data++; + break; + default: + strncpy(buf, t_x3_z3[y-4], buflen); + } + break; + case 4: + snprintf(buf, buflen, "call %s, %s", t_cc[y], get_paddr_str(data)); + data += 2; + break; + case 5: + if (q) { + switch (p) + { + case 0: + snprintf(buf, buflen, "call %s", get_byte_str(*data)); + data += 2; + case 1: + // dd + break; + case 2: + return prefix_ed(data, len); + break; + case 3: + // fd + break; + } + } else { + snprintf(buf, buflen, "push %s", t_rp2[p]); + } + break; + case 6: + snprintf(buf, buflen, "%s %s", t_alu[y], get_byte_str(*data)); + data++; + break; + case 7: + snprintf(buf, buflen, "rst %s", get_byte_str(y*8)); + break; + } + } + + *len = data - dorg; + + size_t slen = strlen(buf) + 1; + char *str = malloc(slen); + if (str == NULL) { + return NULL; + } + memcpy(str, buf, slen-1); + str[slen-1] = 0; + + return str; +} diff --git a/src/disasm.h b/src/disasm.h new file mode 100644 index 0000000..6df3845 --- /dev/null +++ b/src/disasm.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc); From 307dcbff843c24760931280a46267b05e077ea3d Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sat, 13 May 2023 10:08:01 +0200 Subject: [PATCH 02/26] disasm: refactor & fix CB/ED prefix handling --- src/disasm.c | 116 ++++++++++++++++----------------------------------- 1 file changed, 37 insertions(+), 79 deletions(-) diff --git a/src/disasm.c b/src/disasm.c index b004b07..160a9fd 100644 --- a/src/disasm.c +++ b/src/disasm.c @@ -37,6 +37,10 @@ static const char *t_blk[] = { "lddr", "cpdr", "indr", "otdr", }; +static const char *t_im[] = { + "0", "0*", "1", "2", "0*", "0*", "1*", "2*" +}; + static const char *t_x1_z1_q[] = { "ret", "exx", "jp hl", "ld sp, hl" }; @@ -56,6 +60,13 @@ static const char *t_x0_z2[] = { "ld (%s), a", "ld a, (%s)", }; +static const char *ed_x1_z7[] = { + "ld i, a", "ld r, a", + "ld a, i", "ld a, r", + "rrd", "rld", + "nop*", "nop*" +}; + /* should potentially handle labels and shite * NON REENTRANT */ static char *get_paddr_str(uint8_t *p_addr) @@ -80,7 +91,7 @@ static char *get_byte_str(uint8_t b) return buf; } -static char *prefix_cb(uint8_t *data, int *len) +static int prefix_cb(uint8_t *data, char *buf, size_t buflen) { uint8_t op = *data; @@ -88,9 +99,6 @@ static char *prefix_cb(uint8_t *data, int *len) int y = (op >> 3) & 7; int z = op & 7; - char buf[128]; - size_t buflen = sizeof(buf); - switch (x) { case 0: snprintf(buf, buflen, "%s %s", t_sro[y], t_r[z]); break; @@ -99,22 +107,13 @@ static char *prefix_cb(uint8_t *data, int *len) case 3: snprintf(buf, buflen, "set %d, %s", y, t_r[z]); break; } - *len = 2; - - size_t slen = strlen(buf) + 1; - char *str = malloc(slen); - if (str == NULL) { - return NULL; - } - memcpy(str, buf, slen-1); - str[slen-1] = 0; - - return str; + return 1; } -static char *prefix_ed(uint8_t *data, int *len) +static int prefix_ed(uint8_t *data, char *buf, size_t buflen) { uint8_t op = *data; + data++; int x = op >> 6; int y = (op >> 3) & 7; @@ -122,78 +121,37 @@ static char *prefix_ed(uint8_t *data, int *len) int q = op & (1<<3); int p = (op >> 4) & 3; - char buf[128] = "UNHANDLED"; - size_t buflen = sizeof(buf); + const char *fmt = "UNHANDLED"; + const char *s1 = NULL; + const char *s2 = NULL; + + int len = 1; switch (x) { case 0: - case 3: - strncpy(buf, "nop*", buflen); break; - case 2: - if (z < 4 && y >= 4) - strncpy(buf, t_blk[(y-4)*4 + z], buflen); - else - strncpy(buf, "nop*", buflen); - break; + case 3: fmt = "nop*"; break; + case 2: fmt = (z<4 && y>=4) ? t_blk[(y-4)*4 + z] : "nop*"; break; case 1: switch (z) { - case 0: { - char *s = (y != 6) ? "in %s, (c)" : "in (c)"; - snprintf(buf, buflen, s, t_r[z]); - break; - } - case 1: { - char *s = (y != 6) ? "out (c), %s" : "out (c), 0"; - snprintf(buf, buflen, s, t_r[z]); - break; - } - case 2: { - char *s = q ? "adc hl, %s" : "sbc hl, %s"; - snprintf(buf, buflen, s, t_rp[p]); - break; - } - case 3: { - if (q) { - snprintf(buf, buflen, "ld %s, (%s)", t_rp[p], get_paddr_str(data)); - } else { - snprintf(buf, buflen, "ld (%s), %s", get_paddr_str(data), t_rp[p]); - } - data += 2; - break; - } - case 4: strncpy(buf, (y == 0) ? "neg" : "neg*", buflen); break; - case 5: strncpy(buf, (y == 1) ? "reti" : "retn", buflen); break; - case 6: { - static const char *im[] = { "0", "0*", "1", "2", "0*", "0*", "1*", "2*" }; - snprintf(buf, buflen, "im %s", im[y]); - break; - } - case 7: { - static const char *s[] = { - "ld i, a", "ld r, a", - "ld a, i", "ld a, r", - "rrd", "rld", - "nop*", "nop*" - }; - strncpy(buf, s[y], buflen); + case 0: fmt = (y != 6) ? "in %s, (c)" : "in (c)"; s1 = t_r[z]; break; + case 1: fmt = (y != 6) ? "out (c), %s" : "out (c), 0"; s1 = t_r[z]; break; + case 2: fmt = q ? "adc hl, %s" : "sbc hl, %s"; s1 = t_rp[p]; break; + case 3: + if (q) { fmt = "ld %s, (%s)"; s1 = t_rp[p]; s2 = get_paddr_str(data); } + else { fmt = "ld (%s), %s"; s1 = get_paddr_str(data); s2 = t_rp[p]; } + len += 2; break; - } + case 4: fmt = (y == 0) ? "neg" : "neg*"; break; + case 5: fmt = (y == 1) ? "reti" : "retn"; break; + case 6: fmt = "im %s"; s1 = t_im[y]; break; + case 7: fmt = ed_x1_z7[y]; break; } } - *len = 2; - - size_t slen = strlen(buf) + 1; - char *str = malloc(slen); - if (str == NULL) { - return NULL; - } - memcpy(str, buf, slen-1); - str[slen-1] = 0; - - return str; + snprintf(buf, buflen, fmt, s1, s2); + return len; } char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) @@ -306,7 +264,7 @@ char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) data += 2; break; case 1: - return prefix_cb(data, len); + data += prefix_cb(data, buf, buflen); break; case 2: snprintf(buf, buflen, "out (%s), a", get_byte_str(*data)); @@ -335,7 +293,7 @@ char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) // dd break; case 2: - return prefix_ed(data, len); + data += prefix_ed(data, buf, buflen); break; case 3: // fd From 53519b57b2e8067e596d0fbc09b0e0d874ccf73e Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sat, 13 May 2023 12:20:58 +0200 Subject: [PATCH 03/26] refactor op disasm to allow DD/FD prefix --- src/disasm.c | 253 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 97 deletions(-) diff --git a/src/disasm.c b/src/disasm.c index 160a9fd..3e82627 100644 --- a/src/disasm.c +++ b/src/disasm.c @@ -42,7 +42,7 @@ static const char *t_im[] = { }; static const char *t_x1_z1_q[] = { - "ret", "exx", "jp hl", "ld sp, hl" + "ret", "exx", "jp %s", "ld sp, %s" }; static const char *t_x3_z3[] = { @@ -56,7 +56,7 @@ static const char *t_x0_z7[] = { static const char *t_x0_z2[] = { "ld (bc), a", "ld a, (bc)", "ld (de), a", "ld a, (de)", - "ld (%s), hl", "ld hl, (%s)", + "ld (%s), %s", "ld %s, (%s)", "ld (%s), a", "ld a, (%s)", }; @@ -67,6 +67,47 @@ static const char *ed_x1_z7[] = { "nop*", "nop*" }; +static const char *tt_r(uint8_t **data, int i, int prefix) +{ + const char *iid[] = { "(ix%+hhd)", "(iy%+hhd)" }; + const char *ih[] = { "ixh", "iyh" }; + const char *il[] = { "ixl", "iyl" }; + + static char buf[128]; + if (prefix) { + if (i == 6) { + int8_t offset = **data; + *data += 1; + snprintf(buf, sizeof(buf), iid[prefix-1], offset); + return buf; + } else if (i == 4) { + return ih[prefix-1]; + } else if (i == 5) { + return il[prefix-1]; + } + } + + return t_r[i]; +} + +static const char *tt_rp(int i, int prefix) +{ + if (i == 2 && prefix) { + return prefix == 1 ? "ix" : "iy"; + } + + return t_rp[i]; +} + +static const char *tt_rp2(int i, int prefix) +{ + if (i == 2 && prefix) { + return prefix == 1 ? "ix" : "iy"; + } + + return t_rp2[i]; +} + /* should potentially handle labels and shite * NON REENTRANT */ static char *get_paddr_str(uint8_t *p_addr) @@ -110,6 +151,30 @@ static int prefix_cb(uint8_t *data, char *buf, size_t buflen) return 1; } +static int prefix_ddfd_cb(uint8_t *data, char *buf, size_t buflen, int prefix) +{ + uint8_t op = *data; + data++; + + int x = op >> 6; + int y = (op >> 3) & 7; + int z = op & 7; + + static const char *r[] = { + ", b", ", c", ", d", ", e", ", h", ", l", "", ", a" + }; + + switch (x) + { + case 0: snprintf(buf, buflen, "%s %s%s", t_sro[y], tt_r(&data, 6, prefix), r[z]); break; + case 1: snprintf(buf, buflen, "bit %d, %s", y, tt_r(&data, 6, prefix)); break; + case 2: snprintf(buf, buflen, "res %d, %s%s", y, tt_r(&data, 6, prefix), r[z]); break; + case 3: snprintf(buf, buflen, "set %d, %s%s", y, tt_r(&data, 6, prefix), r[z]); break; + } + + return 2; +} + static int prefix_ed(uint8_t *data, char *buf, size_t buflen) { uint8_t op = *data; @@ -154,7 +219,7 @@ static int prefix_ed(uint8_t *data, char *buf, size_t buflen) return len; } -char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) +static char *opcode(uint8_t *data, int *len, uint16_t pc, int prefix) { uint8_t *dorg = data; uint8_t op = *data; @@ -169,6 +234,11 @@ char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) char buf[128] = "UNHANDLED"; const size_t buflen = sizeof(buf); + const char *fmt = "UNHANDLED"; + const char *s1 = NULL; + const char *s2 = NULL; + int noprint = 0; + switch (x) { case 0: @@ -177,143 +247,127 @@ char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) case 0: switch (y) { - case 0: strncpy(buf, "nop", buflen); break; - case 1: strncpy(buf, "ex af", buflen); break; - case 2: - snprintf(buf, buflen, "djnz %s", get_addr_str(pc+2 + (int8_t)*data)); - data++; - break; - case 3: - snprintf(buf, buflen, "jr %s", get_addr_str(pc+2 + (int8_t)*data)); - data++; - break; + case 0: fmt = "nop"; break; + case 1: fmt = "ex af"; break; + case 2: fmt = "djnz %s"; s1 = get_addr_str(pc+2 + (int8_t)*data); data++; break; + case 3: fmt = "jr %s"; s1 = get_addr_str(pc+2 + (int8_t)*data); data++; break; default: - snprintf(buf, buflen, "jr %s, %s", t_cc[y-4], get_addr_str(pc+2 + (int8_t)*data)); + fmt = "jr %s, %s"; + s1 = t_cc[y-4]; s2 = get_addr_str(pc+2 + (int8_t)*data); data++; break; } break; case 1: - if (q) { - snprintf(buf, buflen, "add hl, %s", t_rp[p]); - } else { - snprintf(buf, buflen, "ld %s, %s", t_rp[p], get_paddr_str(data)); - data += 2; - } + if (q) { fmt = "add %s, %s"; s1 = tt_rp(2, prefix); s2 = tt_rp(p, prefix); } + else { fmt = "ld %s, %s", s1 = tt_rp(p, prefix); s2 = get_paddr_str(data); data += 2; } break; case 2: - if (y < 4) { - strncpy(buf, t_x0_z2[y], buflen); - } else { - snprintf(buf, buflen, t_x0_z2[y], get_paddr_str(data)); + fmt = t_x0_z2[y]; + if (y >= 4) { + if (y == 5) { + s1 = tt_rp(2, prefix); + s2 = get_paddr_str(data); + } else { + s1 = get_paddr_str(data); + s2 = tt_rp(2, prefix); + } data += 2; } break; - case 3: - if (q) { - snprintf(buf, buflen, "dec %s", t_rp[p]); + case 3: fmt = q ? "dec %s" : "inc %s"; s1 = tt_rp(p, prefix);break; + case 4: fmt = "inc %s"; s1 = tt_r(&data, y, prefix); break; + case 5: fmt = "dec %s"; s1 = tt_r(&data, y, prefix); break; + case 6: fmt = "ld %s, %s", s1 = tt_r(&data, y, prefix); s2 = get_byte_str(*data); data++; break; + case 7: fmt = t_x0_z7[y]; break; + } + break; + case 1: + if (op == 0x76) { + fmt = "halt"; + } else { + fmt = "ld %s, %s"; + if (y == 6) { + s1 = tt_r(&data, y, prefix); + s2 = t_r[z]; + } else if (z == 6) { + s1 = t_r[y]; + s2 = tt_r(&data, z, prefix); } else { - snprintf(buf, buflen, "inc %s", t_rp[p]); + s1 = tt_r(&data, y, prefix); + s2 = tt_r(&data, z, prefix); } - break; - case 4: - snprintf(buf, buflen, "inc %s", t_r[y]); - break; - case 5: - snprintf(buf, buflen, "dec %s", t_r[y]); - break; - case 6: - snprintf(buf, buflen, "ld %s, %s", t_r[y], get_byte_str(*data)); - data++; - break; - case 7: - strncpy(buf, t_x0_z7[y], buflen); - break; } break; - case 1: - if (z == 6 && y == 6) - strncpy(buf, "halt", buflen); - else - snprintf(buf, buflen, "ld %s, %s", t_r[y], t_r[z]); - break; - case 2: - snprintf(buf, buflen, "%s %s", t_alu[y], t_r[z]); - break; + case 2: fmt = "%s %s"; s1 = t_alu[y]; s2 = tt_r(&data, z, prefix); break; case 3: switch (z) { - case 0: - snprintf(buf, buflen, "ret %s", t_cc[y]); - break; + case 0: fmt = "ret %s"; s1 = t_cc[y]; break; case 1: - if (q) - strncpy(buf, t_x1_z1_q[p], buflen); - else - snprintf(buf, buflen, "pop %s", t_rp2[p]); - break; - case 2: - snprintf(buf, buflen, "jp %s, %s", t_cc[y], get_paddr_str(data)); - data += 2; + if (q) { fmt = t_x1_z1_q[p]; s1 = tt_rp(2, prefix); } + else { fmt = "pop %s"; s1 = tt_rp2(p, prefix); } break; + case 2: fmt = "jp %s, %s"; s1 = t_cc[y]; s2 = get_paddr_str(data); data += 2; break; case 3: switch (y) { - case 0: - snprintf(buf, buflen, "jp %s", get_paddr_str(data)); - data += 2; - break; + case 0: fmt = "jp %s"; s1 = get_paddr_str(data); data += 2; break; case 1: - data += prefix_cb(data, buf, buflen); - break; - case 2: - snprintf(buf, buflen, "out (%s), a", get_byte_str(*data)); - data++; - break; - case 3: - snprintf(buf, buflen, "in a, (%s)", get_byte_str(*data)); - data++; + if (prefix) { + data += prefix_ddfd_cb(data, buf, buflen,prefix); + } else { + data += prefix_cb(data, buf, buflen); + } + noprint = 1; break; + case 2: fmt = "out (%s), a"; s1 = get_byte_str(*data); data++; break; + case 3: fmt = "in a, (%s)"; s1 = get_byte_str(*data); data++; break; default: - strncpy(buf, t_x3_z3[y-4], buflen); + fmt = t_x3_z3[y-4]; } break; - case 4: - snprintf(buf, buflen, "call %s, %s", t_cc[y], get_paddr_str(data)); - data += 2; - break; + case 4: fmt = "call %s, %s"; s1 = t_cc[y]; s2 = get_paddr_str(data); data += 2; break; case 5: if (q) { switch (p) { - case 0: - snprintf(buf, buflen, "call %s", get_byte_str(*data)); - data += 2; - case 1: - // dd + case 0: fmt = "call %s"; s1 = get_byte_str(*data); data += 2; break; + case 1: // dd + if (prefix) { + fmt = "nop*"; + } else { + return opcode(data, len, pc, 1); + } break; case 2: - data += prefix_ed(data, buf, buflen); + if (prefix) { + fmt = "nop*"; + data--; + } else { + data += prefix_ed(data, buf, buflen); noprint = 1; + } break; - case 3: - // fd + case 3: // fd + if (prefix) { + fmt = "nop*"; + } else { + return opcode(data, len, pc, 2); + } break; } } else { - snprintf(buf, buflen, "push %s", t_rp2[p]); + fmt = "push %s"; s1 = tt_rp2(p, prefix); } break; - case 6: - snprintf(buf, buflen, "%s %s", t_alu[y], get_byte_str(*data)); - data++; - break; - case 7: - snprintf(buf, buflen, "rst %s", get_byte_str(y*8)); - break; + case 6: fmt = "%s %s"; s1 = t_alu[y]; s2 = get_byte_str(*data); data++; break; + case 7: fmt = "rst %s"; s1 = get_byte_str(y*8); break; } } - *len = data - dorg; + if (!noprint) snprintf(buf, buflen, fmt, s1, s2); + + *len = data - dorg + !(!prefix); size_t slen = strlen(buf) + 1; char *str = malloc(slen); @@ -325,3 +379,8 @@ char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) return str; } + +char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) +{ + return opcode(data, len, pc, 0); +} From da89726952fb5107fa1cabeb9a634b79bd2c04bf Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sat, 13 May 2023 12:21:52 +0200 Subject: [PATCH 04/26] remove redundant includes --- src/disasm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/disasm.c b/src/disasm.c index 3e82627..81b8e6e 100644 --- a/src/disasm.c +++ b/src/disasm.c @@ -3,8 +3,6 @@ #include #include #include -#include "memory.h" -#include "log.h" static const char *t_r[] = { "b", "c", "d", "e", "h", "l", "(hl)", "a" From c606f44ea0c2ea219819de1ff225ed6338bc7c40 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sat, 13 May 2023 12:28:19 +0200 Subject: [PATCH 05/26] fix broken DD/FD CB behavior --- src/disasm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/disasm.c b/src/disasm.c index 81b8e6e..bab1909 100644 --- a/src/disasm.c +++ b/src/disasm.c @@ -151,8 +151,8 @@ static int prefix_cb(uint8_t *data, char *buf, size_t buflen) static int prefix_ddfd_cb(uint8_t *data, char *buf, size_t buflen, int prefix) { + const char *iid = tt_r(&data, 6, prefix); uint8_t op = *data; - data++; int x = op >> 6; int y = (op >> 3) & 7; @@ -164,10 +164,10 @@ static int prefix_ddfd_cb(uint8_t *data, char *buf, size_t buflen, int prefix) switch (x) { - case 0: snprintf(buf, buflen, "%s %s%s", t_sro[y], tt_r(&data, 6, prefix), r[z]); break; - case 1: snprintf(buf, buflen, "bit %d, %s", y, tt_r(&data, 6, prefix)); break; - case 2: snprintf(buf, buflen, "res %d, %s%s", y, tt_r(&data, 6, prefix), r[z]); break; - case 3: snprintf(buf, buflen, "set %d, %s%s", y, tt_r(&data, 6, prefix), r[z]); break; + case 0: snprintf(buf, buflen, "%s %s%s", t_sro[y], iid, r[z]); break; + case 1: snprintf(buf, buflen, "bit %d, %s", y, iid); break; + case 2: snprintf(buf, buflen, "res %d, %s%s", y, iid, r[z]); break; + case 3: snprintf(buf, buflen, "set %d, %s%s", y, iid, r[z]); break; } return 2; From bdf337aef7a3a2a7adfa35786dab5b8f397cb3b4 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Sun, 14 May 2023 13:18:12 +0200 Subject: [PATCH 06/26] fix segfault on no palette folder --- src/main.c | 3 +-- src/palette.c | 13 +++++++++++++ src/palette.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index f58fa48..0e1aee7 100644 --- a/src/main.c +++ b/src/main.c @@ -117,8 +117,7 @@ int main(int argc, char *argv[]) config_set_int(&g_config, "window-scale", video_sdl_get_scale()); config_set_int(&g_config, "limit-fps", video_sdl_get_fps_limit()); - char **palette_list = palette_list_get(); - config_set_str(&g_config, "palette", palette_list[palette_get_index()]); + config_set_str(&g_config, "palette", palette_get_name()); config_save(); diff --git a/src/palette.c b/src/palette.c index 6f7b7e6..3fd841d 100644 --- a/src/palette.c +++ b/src/palette.c @@ -148,6 +148,10 @@ void palette_set_by_index(size_t index) void palette_set_by_name(const char *name) { + if (palette_list == NULL) { + return; + } + for (size_t i = 0; palette_list[i] != NULL; i++) { int result = strcmp(name, palette_list[i]); if (result == 0) { @@ -171,6 +175,15 @@ size_t palette_get_index() return palette_current; } +const char *palette_get_name() +{ + if (palette_list == NULL) { + return NULL; + } + + return palette_list[palette_current]; +} + bool palette_has_changed() { if (palette_changed) { diff --git a/src/palette.h b/src/palette.h index d4a1757..cc1ef86 100644 --- a/src/palette.h +++ b/src/palette.h @@ -27,4 +27,5 @@ void palette_set_by_index(size_t index); void palette_set_by_name(const char *name); void palette_set_default(); size_t palette_get_index(); +const char *palette_get_name(); bool palette_has_changed(); From 678ffd3d9ddd4a58b87db5f8341c127a96fbcfb5 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 00:14:42 +0200 Subject: [PATCH 07/26] disassemble_opcode -> disasm_opcode --- src/disasm.c | 2 +- src/disasm.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/disasm.c b/src/disasm.c index bab1909..f102873 100644 --- a/src/disasm.c +++ b/src/disasm.c @@ -378,7 +378,7 @@ static char *opcode(uint8_t *data, int *len, uint16_t pc, int prefix) return str; } -char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc) +char *disasm_opcode(uint8_t *data, int *len, uint16_t pc) { return opcode(data, len, pc, 0); } diff --git a/src/disasm.h b/src/disasm.h index 6df3845..a8b260a 100644 --- a/src/disasm.h +++ b/src/disasm.h @@ -2,4 +2,7 @@ #include -char *disassemble_opcode(uint8_t *data, int *len, uint16_t pc); +/* returns a disassembled instruction string or NULL on error. + * len gets set to instruction length in bytes. + * user needs to free the string after use */ +char *disasm_opcode(uint8_t *data, int *len, uint16_t pc); From 532eeba875272eb4da422a66d48dcaa1698b7345 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 00:16:23 +0200 Subject: [PATCH 08/26] preliminary debugger window --- .gitmodules | 3 + build.sh | 6 +- src/debugger.c | 298 +++++++++++++++++++++++++++++++++++++++++++ src/debugger.h | 3 + src/microui_render.c | 206 ++++++++++++++++++++++++++++++ src/microui_render.h | 12 ++ 6 files changed, 526 insertions(+), 2 deletions(-) create mode 100644 src/debugger.c create mode 100644 src/debugger.h create mode 100644 src/microui_render.c create mode 100644 src/microui_render.h diff --git a/.gitmodules b/.gitmodules index 71ea916..ad16518 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ayumi"] path = ayumi url = https://github.com/wermipls/ayumi +[submodule "microui"] + path = microui + url = https://github.com/wermipls/microui diff --git a/build.sh b/build.sh index 0ad12c1..d85bc4d 100755 --- a/build.sh +++ b/build.sh @@ -1,8 +1,10 @@ #!/bin/bash set -e -FILES="src/*.c ayumi/ayumi.c" -FLAGS="-std=gnu11 -Wall -Wextra -Wpedantic -lz -lm `sdl2-config --libs`" +FILES="src/*.c ayumi/ayumi.c microui/src/microui.c" +FLAGS="-std=gnu11 -Wall -Wextra -Wpedantic -lz -lm \ + `sdl2-config --libs` -I microui/src \ + `pkg-config --cflags --libs freetype2`" FLAGS_DEBUG="-Og -ggdb" FLAGS_RELEASE="-O3 -ffast-math -flto -fwhole-program" RELEASE_FILES="sleepdart* rom/* palettes/*" diff --git a/src/debugger.c b/src/debugger.c new file mode 100644 index 0000000..1eedd80 --- /dev/null +++ b/src/debugger.c @@ -0,0 +1,298 @@ +#include "debugger.h" + +#include "microui.h" +#include "microui_render.h" +#include "machine.h" +#include "z80.h" +#include "disasm.h" +#include "vector.h" +#include +#include + +static mu_Context context; +static Machine_t *machine; + +const char *icons[] = { + "", + "()", + "->", +}; + +struct Disasm +{ + int icon; + uint16_t pcval; + char *pc; + char *bytes; + char *instr; +}; + +struct Breakpoint +{ + int enabled; + uint16_t pc; +}; + +static struct Breakpoint breakpoints[256]; +static char registers[1024]; + +struct Disasm *disasm = NULL; + +int mu_button_ex_id(mu_Context *ctx, const char *label, int id_num, int icon, int opt) { + int res = 0; + mu_Id id = id_num ? mu_get_id(ctx, &id_num, sizeof(id_num)) + : mu_get_id(ctx, &icon, sizeof(icon)); + mu_Rect r = mu_layout_next(ctx); + mu_update_control(ctx, id, r, opt); + /* handle click */ + if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) { + res |= MU_RES_SUBMIT; + } + /* draw */ + mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, opt); + if (label) { mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, opt); } + if (icon) { mu_draw_icon(ctx, icon, r, ctx->style->colors[MU_COLOR_TEXT]); } + return res; +} + +static int is_breakpoint(uint16_t pc) +{ + for (int i = 0; i < 256; i++) { + if (breakpoints[i].enabled && pc == breakpoints[i].pc) { + return 1; + } + } + return 0; +} + +static void add_breakpoint(uint16_t pc) +{ + for (int i = 0; i < 256; i++) { + if (!breakpoints[i].enabled) { + breakpoints[i].enabled = 1; + breakpoints[i].pc = pc; + return; + } + } +} + +static void remove_breakpoint(uint16_t pc) +{ + for (int i = 0; i < 256; i++) { + if (breakpoints[i].enabled && pc == breakpoints[i].pc) { + breakpoints[i].enabled = 0; + } + } +} + +static void update_regs() +{ + struct Z80Regs *r = &machine->cpu.regs; + snprintf(registers, sizeof(registers), + "PC %04x SP %04x\n\n" + "AF %04x AF` %04x\n" + "BC %04x BC` %04x\n" + "DE %04x DE` %04x\n" + "HL %04x HL` %04x\n" + "IX %04x IY %04x\n", + r->pc, r->sp, + r->main.af, r->alt.af, + r->main.bc, r->alt.bc, + r->main.de, r->alt.de, + r->main.hl, r->alt.hl, + r->ix, r->iy); +} + +int dbg_continue = 0; + +static void disasm_window(mu_Context *ctx) { + if (mu_begin_window_ex(ctx, "Debugger", mu_rect(0, 0, 400, 600), MU_OPT_NOCLOSE)) { + int pc_changed = 0; + + mu_layout_row(ctx, 3, (int[]) { -200, -100, -1 }, 25); + if (mu_button(ctx, "Continue") || dbg_continue) { + dbg_continue = 1; + int timeout = 70000; // hack + uint16_t pc = machine->cpu.regs.pc; + do { + cpu_do_cycles(&machine->cpu); + if (is_breakpoint(machine->cpu.regs.pc)) { + dbg_continue = 0; + break; + } + } while (timeout--); + update_regs(); + pc_changed = 1; + }; + mu_button(ctx, "Step over"); + if (mu_button(ctx, "Step into")) { + uint16_t pc = machine->cpu.regs.pc; + cpu_do_cycles(&machine->cpu); + update_regs(); + pc_changed = (machine->cpu.regs.pc != pc); + }; + + mu_layout_row(ctx, 1, (int[]) { -1 }, -1); + mu_begin_panel(ctx, "disassembly"); + mu_Container *panel = mu_get_current_container(ctx); + int line_h = render_text_height(0) + 4; + int pci = 0; + for (size_t i = 0; i < vector_len(disasm); i++) { + struct Disasm *d = &disasm[i]; + int icon = d->icon; + if (d->pcval == machine->cpu.regs.pc) { + icon = 2; + pci = i; + } + mu_layout_row(ctx, 4, (int[]) { 16, 32, 100, -1 }, line_h); + if (mu_button_ex_id(ctx, icons[icon], 1+i, 0, MU_OPT_NOFRAME)) { + d->icon ^= 1; + if (d->icon) add_breakpoint(d->pcval); + else remove_breakpoint(d->pcval); + } + mu_label(ctx, d->pc); + mu_label(ctx, d->bytes); + mu_label(ctx, d->instr); + } + if (pc_changed) { + int curline_y = (line_h + ctx->style->spacing) * pci; + int offset = curline_y - panel->scroll.y; + if (offset < 100 || offset > (panel->body.h - 100)) { + panel->scroll.y = curline_y - panel->body.h / 2; + if (panel->scroll.y < 0) panel->scroll.y = 0; + } + } + mu_end_panel(ctx); + + mu_end_window(ctx); + } +} + +static void regs_window(mu_Context *ctx) +{ + if (mu_begin_window(ctx, "Registers", mu_rect(400, 0, 200, 200))) { + mu_layout_row(ctx, 1, (int[]) { -1 }, -1); + mu_text(ctx, registers); + mu_end_window(ctx); + } +} + +static void process_frame(mu_Context *ctx) { + mu_begin(ctx); + disasm_window(ctx); + regs_window(ctx); + mu_end(ctx); +} + +static int window_loop(mu_Context *ctx) +{ + SDL_Event e; + SDL_PumpEvents(); + while (SDL_PollEvent(&e)) { + switch (e.type) + { + case SDL_MOUSEMOTION: + mu_input_mousemove(ctx, e.motion.x, e.motion.y); + break; + case SDL_MOUSEBUTTONDOWN: + mu_input_mousedown(ctx, e.button.x, e.button.y, e.button.button); + break; + case SDL_MOUSEBUTTONUP: + mu_input_mouseup(ctx, e.button.x, e.button.y, e.button.button); + break; + case SDL_WINDOWEVENT: + switch (e.window.event) + { + case SDL_WINDOWEVENT_CLOSE: return 0; break; + } + } + } + + process_frame(ctx); + process_frame(ctx); + + render_clear(mu_color(0, 0, 0, 255)); + mu_Command *cmd = NULL; + while (mu_next_command(ctx, &cmd)) { + switch(cmd->type) + { + case MU_COMMAND_TEXT: render_text(cmd->text.font, cmd->text.str, cmd->text.pos, cmd->text.color); break; + case MU_COMMAND_RECT: render_draw_rect(cmd->rect.rect, cmd->rect.color); break; + } + } + render_present(); + + return 1; +} + + +void debugger_open(Machine_t *m) +{ + mu_init(&context); + context.text_height = render_text_height; + context.text_width = render_text_width; + + if (render_init()) { + return; + } + + disasm = vector_create(); + + memset(breakpoints, 0, sizeof(breakpoints)); + + char buf[1024]; + + machine = m; + + uint8_t *pmem = machine->memory.bus; + uint16_t pc = 0; + for (; pc < 0x4000;) { + struct Disasm d = { 0 }; + d.pcval = pc; + + int index = 0; + int len = 1; + char *op = disasm_opcode(pmem, &len, pc); + + snprintf( + buf, sizeof(buf), + "%04x", pc); + size_t size = strlen(buf)+1; + d.pc = malloc(size); + if (d.pc == NULL) { + free(op); + break; + } + memcpy(d.pc, buf, size-1); + d.pc[size-1] = 0; + + while (len--) { + index += snprintf( + buf+index, sizeof(buf)-index, + "%02x ", *pmem); + pmem++; + pc++; + } + + size = strlen(buf)+1; + d.bytes = malloc(size); + if (d.bytes == NULL) { + free(d.pc); + free(op); + break; + } + memcpy(d.bytes, buf, size-1); + d.bytes[size-1] = 0; + + d.instr = op; + vector_add(disasm, d); + } + + update_regs(); + + while (window_loop(&context)); + + render_deinit(); + vector_free(disasm); +} + diff --git a/src/debugger.h b/src/debugger.h new file mode 100644 index 0000000..238a6d3 --- /dev/null +++ b/src/debugger.h @@ -0,0 +1,3 @@ +#pragma once + +void debugger_open(); diff --git a/src/microui_render.c b/src/microui_render.c new file mode 100644 index 0000000..903ed43 --- /dev/null +++ b/src/microui_render.c @@ -0,0 +1,206 @@ +#include "microui.h" +#include "file.h" +#include +#include +#include FT_FREETYPE_H + +static FT_Library ft; +static FT_Face face; +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static int width = 600; +static int height = 600; + +struct Glyph +{ + SDL_Texture *tex; + int8_t adv_x; + int8_t adv_y; + int8_t offset_x; + int8_t offset_y; + int8_t w; + int8_t h; +}; + +static struct Glyph cache[256] = { 0 }; +static int glyph_height; + +static int cache_glyph(FT_Face f, char c) +{ + struct Glyph cached = { 0 }; + FT_GlyphSlot slot = f->glyph; + uint8_t cache_i = c; + + uint32_t idx = FT_Get_Char_Index(f, c); + int err = FT_Load_Glyph(f, idx, FT_LOAD_NO_BITMAP); + if (err) { + goto notex; + } + + err = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); + if (err) { + goto notex; + } + + FT_Bitmap *bitmap = &slot->bitmap; + SDL_Texture *tex = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_ARGB32, + SDL_TEXTUREACCESS_STREAMING, bitmap->width, bitmap->rows); + + void *buf; + int pitch; + + err = SDL_LockTexture(tex, NULL, &buf, &pitch); + if (err) { + goto notex; + } + + uint32_t *px = buf; + + for (int i = 0; i < bitmap->pitch*bitmap->rows; i++) { + px[i] = 0xFFFFFF00 | bitmap->buffer[i]; + } + + SDL_UnlockTexture(tex); + + SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); + + cached.tex = tex; + cached.w = bitmap->width; + cached.h = bitmap->rows; +notex: + cached.offset_x = slot->bitmap_left; + cached.offset_y = 0 - slot->bitmap_top; + cached.adv_x = slot->advance.x / 64; + cached.adv_y = slot->advance.y / 64; + + cache[cache_i] = cached; + + return 0; +} + +int render_text_width(mu_Font font, const char *text, int len) +{ + int x = 0; + + while (*text && len) { + uint8_t i = *text; + x += cache[i].adv_x; + + text++; + len--; + } + + return x; +} + +int render_text_height(mu_Font font) +{ + return glyph_height; +} + +void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color) +{ + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); + pos.y += face->size->metrics.ascender / 64; + + while (*text) { + struct Glyph *g = &cache[(uint8_t)*text]; + SDL_Rect r = { + .x = pos.x + g->offset_x, + .y = pos.y + g->offset_y, + .w = g->w, + .h = g->h, + }; + + SDL_RenderCopy(renderer, g->tex, NULL, &r); + + pos.x += g->adv_x; + pos.y += g->adv_y; + + text++; + } +} + +int render_init() +{ + if (renderer) return -1; + + int err = FT_Init_FreeType(&ft); + if (err) { + return -4; + } + + char buf[2048]; + file_path_append(buf, file_get_basedir(), "font.ttf", sizeof(buf)); + err = FT_New_Face(ft, buf, 0, &face); + if (err) { + return -5; + } + + err = FT_Set_Char_Size(face, 0, 12*64, 72, 72); + if (err) { + return -6; + } + + window = SDL_CreateWindow( + "sleepdart dbg", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + width, height, 0); + + if (!window) { + return -2; + } + + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + SDL_DestroyWindow(window); + return -3; + } + + for (int i = 0; i < 256; i++) { + cache_glyph(face, i); + } + + glyph_height = face->size->metrics.height / 64; + + return 0; +} + +void render_deinit() +{ + if (renderer) { + SDL_DestroyRenderer(renderer); + } + if (window) { + SDL_DestroyWindow(window); + } + + return; +} + +void render_draw_rect(mu_Rect rect, mu_Color c) +{ + SDL_Rect r = { + .x = rect.x, + .y = rect.y, + .w = rect.w, + .h = rect.h, + }; + + SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a); + SDL_RenderFillRect(renderer, &r); +} + +void render_clear(mu_Color c) +{ + SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a); + SDL_RenderClear(renderer); +} + +void render_present() +{ + SDL_RenderPresent(renderer); +} diff --git a/src/microui_render.h b/src/microui_render.h new file mode 100644 index 0000000..c43ca76 --- /dev/null +++ b/src/microui_render.h @@ -0,0 +1,12 @@ +#pragma once + +#include "microui.h" + +int render_text_width(mu_Font font, const char *text, int len); +int render_text_height(mu_Font font); +void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color); +int render_init(); +void render_deinit(); +void render_draw_rect(mu_Rect rect, mu_Color c); +void render_clear(mu_Color c); +void render_present(); From f140e05ae7875fe0eb028bc55bcb0ebc1f208a19 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 13:17:15 +0200 Subject: [PATCH 09/26] debugger improvements --- src/debugger.c | 172 +++++++++++++++++++++++++++++++------------ src/debugger.h | 7 +- src/input_sdl.c | 7 ++ src/machine.c | 4 + src/main.c | 7 ++ src/microui_render.c | 103 +++++++++++++++++++++++++- src/microui_render.h | 10 +++ 7 files changed, 260 insertions(+), 50 deletions(-) diff --git a/src/debugger.c b/src/debugger.c index 1eedd80..47b8eb3 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -12,15 +12,9 @@ static mu_Context context; static Machine_t *machine; -const char *icons[] = { - "", - "()", - "->", -}; - struct Disasm { - int icon; + int breakpoint; uint16_t pcval; char *pc; char *bytes; @@ -33,8 +27,18 @@ struct Breakpoint uint16_t pc; }; +enum Condition +{ + DBG_NONE, + DBG_STEPINTO, + DBG_STEPOVER, + DBG_BREAKPOINT, +}; + static struct Breakpoint breakpoints[256]; static char registers[1024]; +static enum Condition break_on = DBG_STEPINTO; +static bool debugger_enabled = false; struct Disasm *disasm = NULL; @@ -94,42 +98,33 @@ static void update_regs() "BC %04x BC` %04x\n" "DE %04x DE` %04x\n" "HL %04x HL` %04x\n" - "IX %04x IY %04x\n", + "IX %04x IY %04x\n\n" + "cycles: %05lld/%05u", r->pc, r->sp, r->main.af, r->alt.af, r->main.bc, r->alt.bc, r->main.de, r->alt.de, r->main.hl, r->alt.hl, - r->ix, r->iy); + r->ix, r->iy, + machine->cpu.cycles, machine->timing.t_frame); } -int dbg_continue = 0; - static void disasm_window(mu_Context *ctx) { if (mu_begin_window_ex(ctx, "Debugger", mu_rect(0, 0, 400, 600), MU_OPT_NOCLOSE)) { - int pc_changed = 0; - - mu_layout_row(ctx, 3, (int[]) { -200, -100, -1 }, 25); - if (mu_button(ctx, "Continue") || dbg_continue) { - dbg_continue = 1; - int timeout = 70000; // hack - uint16_t pc = machine->cpu.regs.pc; - do { - cpu_do_cycles(&machine->cpu); - if (is_breakpoint(machine->cpu.regs.pc)) { - dbg_continue = 0; - break; - } - } while (timeout--); - update_regs(); - pc_changed = 1; + static int pc = -1; + int pc_changed = (machine->cpu.regs.pc != pc); + pc = machine->cpu.regs.pc; + + mu_layout_row(ctx, 4, (int[]) { 70, -200, -100, -1 }, 25); + if (mu_button(ctx, "Pause")) { + break_on = DBG_STEPINTO; + }; + if (mu_button(ctx, "Continue")) { + break_on = DBG_BREAKPOINT; }; mu_button(ctx, "Step over"); if (mu_button(ctx, "Step into")) { - uint16_t pc = machine->cpu.regs.pc; - cpu_do_cycles(&machine->cpu); - update_regs(); - pc_changed = (machine->cpu.regs.pc != pc); + break_on = DBG_STEPINTO; }; mu_layout_row(ctx, 1, (int[]) { -1 }, -1); @@ -139,15 +134,15 @@ static void disasm_window(mu_Context *ctx) { int pci = 0; for (size_t i = 0; i < vector_len(disasm); i++) { struct Disasm *d = &disasm[i]; - int icon = d->icon; + int icon = d->breakpoint ? DBGICON_BREAKPOINT : 0; if (d->pcval == machine->cpu.regs.pc) { - icon = 2; + icon = DBGICON_CURRENT; pci = i; } - mu_layout_row(ctx, 4, (int[]) { 16, 32, 100, -1 }, line_h); - if (mu_button_ex_id(ctx, icons[icon], 1+i, 0, MU_OPT_NOFRAME)) { - d->icon ^= 1; - if (d->icon) add_breakpoint(d->pcval); + mu_layout_row(ctx, 4, (int[]) { line_h, 32, 100, -1 }, line_h); + if (mu_button_ex_id(ctx, NULL, 1+i, icon, MU_OPT_NOFRAME)) { + d->breakpoint ^= 1; + if (d->breakpoint) add_breakpoint(d->pcval); else remove_breakpoint(d->pcval); } mu_label(ctx, d->pc); @@ -170,9 +165,32 @@ static void disasm_window(mu_Context *ctx) { static void regs_window(mu_Context *ctx) { - if (mu_begin_window(ctx, "Registers", mu_rect(400, 0, 200, 200))) { - mu_layout_row(ctx, 1, (int[]) { -1 }, -1); + if (mu_begin_window(ctx, "Registers", mu_rect(400, 0, 200, 400))) { + mu_layout_row(ctx, 1, (int[]) { -1 }, 220); mu_text(ctx, registers); + + mu_layout_row(ctx, 8, (int[]) { 16, 16, 16, 16, 16, 16, 16, 16 }, 16); + static const char *flag_label[8] = { + "C", "N", "PV", "X", "H", "Y", "Z", "S" + }; + for (int i = 7; i >= 0; i--) { + mu_draw_control_text( + ctx, flag_label[i], mu_layout_next(ctx), + MU_COLOR_TEXT, MU_OPT_ALIGNCENTER); + } + int flags[8]; + int clicked = 0; + for (int i = 7; i >= 0; i--) { + flags[i] = machine->cpu.regs.main.f & (1<cpu.regs.main.f = f; + } mu_end_window(ctx); } } @@ -189,22 +207,32 @@ static int window_loop(mu_Context *ctx) SDL_Event e; SDL_PumpEvents(); while (SDL_PollEvent(&e)) { + int handled = 0; switch (e.type) { case SDL_MOUSEMOTION: + if (e.motion.windowID != render_get_window_id()) break; mu_input_mousemove(ctx, e.motion.x, e.motion.y); - break; + handled = 1; break; case SDL_MOUSEBUTTONDOWN: + if (e.button.windowID != render_get_window_id()) break; mu_input_mousedown(ctx, e.button.x, e.button.y, e.button.button); - break; + handled = 1; break; case SDL_MOUSEBUTTONUP: + if (e.button.windowID != render_get_window_id()) break; mu_input_mouseup(ctx, e.button.x, e.button.y, e.button.button); - break; + handled = 1; break; case SDL_WINDOWEVENT: - switch (e.window.event) - { - case SDL_WINDOWEVENT_CLOSE: return 0; break; - } + if (e.window.windowID != render_get_window_id()) break; + if (e.window.event == SDL_WINDOWEVENT_CLOSE) { + debugger_close(); + break_on = DBG_NONE; + return 0; + } + } + + if (!handled) { + SDL_PushEvent(&e); } } @@ -218,16 +246,57 @@ static int window_loop(mu_Context *ctx) { case MU_COMMAND_TEXT: render_text(cmd->text.font, cmd->text.str, cmd->text.pos, cmd->text.color); break; case MU_COMMAND_RECT: render_draw_rect(cmd->rect.rect, cmd->rect.color); break; + case MU_COMMAND_CLIP: render_clip_rect(cmd->rect.rect); break; + case MU_COMMAND_ICON: render_draw_icon(cmd->icon.id, cmd->icon.rect, cmd->icon.color); break; } } render_present(); + if (break_on != DBG_NONE) { + return 0; + } + return 1; } +void debugger_handle() +{ + if (!debugger_enabled) return; + + bool is_break = false; + + switch (break_on) + { + case DBG_BREAKPOINT: + if (is_breakpoint(machine->cpu.regs.pc)) { + is_break = true; + } + break; + case DBG_STEPOVER: + case DBG_STEPINTO: + is_break = true; + break; + } + + if (is_break) { + break_on = DBG_NONE; + update_regs(); + while (window_loop(&context)); + } +} + +void debugger_update_window() +{ + if (!debugger_enabled) return; + + update_regs(); + window_loop(&context); +} void debugger_open(Machine_t *m) { + if (debugger_enabled) return; + mu_init(&context); context.text_height = render_text_height; context.text_width = render_text_width; @@ -290,9 +359,18 @@ void debugger_open(Machine_t *m) update_regs(); - while (window_loop(&context)); + debugger_enabled = true; + + debugger_update_window(); +} + +void debugger_close() +{ + if (!debugger_enabled) return; render_deinit(); vector_free(disasm); + + debugger_enabled = false; } diff --git a/src/debugger.h b/src/debugger.h index 238a6d3..3b3cc67 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -1,3 +1,8 @@ #pragma once -void debugger_open(); +struct Machine; + +void debugger_handle(); +void debugger_update_window(); +void debugger_open(struct Machine *m); +void debugger_close(); diff --git a/src/input_sdl.c b/src/input_sdl.c index b396cad..0e65cc4 100644 --- a/src/input_sdl.c +++ b/src/input_sdl.c @@ -33,16 +33,23 @@ int input_sdl_update() SDL_Event e; while (SDL_PollEvent(&e)) { + int handled = 0; switch (e.type) { case SDL_QUIT: + handled = 1; quit = 1; break; case SDL_DROPFILE: + handled = 1; machine_open_file(e.drop.file); SDL_free(e.drop.file); break; } + + if (!handled) { + SDL_PushEvent(&e); + } } return quit; diff --git a/src/machine.c b/src/machine.c index c82e0ab..99ac11f 100644 --- a/src/machine.c +++ b/src/machine.c @@ -11,6 +11,7 @@ #include "audio_sdl.h" #include "dsp.h" #include "hotkeys.h" +#include "debugger.h" static Machine_t *m_cur = NULL; static bool file_open; @@ -198,6 +199,7 @@ int machine_do_cycles() machine_process_hooks(m_cur); machine_test_iterate(m_cur); + debugger_handle(); cpu_do_cycles(&m_cur->cpu); @@ -217,6 +219,8 @@ int machine_do_cycles() keyboard_macro_process(); + debugger_update_window(); + input_sdl_copy_old_state(); int quit = input_sdl_update(); if (quit) return -2; diff --git a/src/main.c b/src/main.c index 0e1aee7..37092c4 100644 --- a/src/main.c +++ b/src/main.c @@ -16,6 +16,8 @@ #include "sleepdart_info.h" #include "config.h" +#include "debugger.h" + int main(int argc, char *argv[]) { char *errsilent = getenv("SLEEPDART_ERRSILENT"); @@ -27,6 +29,7 @@ int main(int argc, char *argv[]) argparser_add_arg(parser, "file", 0, 0, true, "tape or snapshot file to be loaded"); argparser_add_arg(parser, "--scale", 's', ARG_INT, 0, "integer window scale"); argparser_add_arg(parser, "--fullscreen", 'f', ARG_STORE_TRUE, 0, "run in fullscreen mode"); + argparser_add_arg(parser, "--debugger", 'd', ARG_STORE_TRUE, 0, "open the debugger"); argparser_add_arg(parser, "--test", 0, ARG_STRING, 0, "perform an automated regression test"); dlog(LOG_INFO, @@ -108,6 +111,10 @@ int main(int argc, char *argv[]) machine_process_events(); + if (argparser_get(parser, "debugger")) { + debugger_open(&m); + } + for (;;) { int err = machine_do_cycles(); if (err) break; diff --git a/src/microui_render.c b/src/microui_render.c index 903ed43..d0ff770 100644 --- a/src/microui_render.c +++ b/src/microui_render.c @@ -1,6 +1,9 @@ +#include "microui_render.h" + #include "microui.h" #include "file.h" #include +#include #include #include FT_FREETYPE_H @@ -25,6 +28,16 @@ struct Glyph static struct Glyph cache[256] = { 0 }; static int glyph_height; +static SDL_Texture *icons_atlas; + +struct Icon +{ + SDL_Rect src; + mu_Color color; +}; + +static struct Icon icons[16]; + static int cache_glyph(FT_Face f, char c) { struct Glyph cached = { 0 }; @@ -80,8 +93,39 @@ static int cache_glyph(FT_Face f, char c) return 0; } +SDL_Texture *load_rgba32_zlib_texture(const char *path, int width, int height) +{ + int64_t fsize = file_get_size(path); + if (fsize <= 0) return NULL; + + uint8_t src[fsize]; + FILE *f = fopen(path, "rb"); + fread(src, 1, fsize, f); + fclose(f); + + SDL_Texture *tex = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, + width, height); + + if (!tex) return NULL; + + uint8_t *buf; + int pitch; + + SDL_LockTexture(tex, NULL, &buf, &pitch); + size_t len = pitch * height; + + uncompress(buf, &len, src, fsize); + SDL_UnlockTexture(tex); + SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); + + return tex; +} + int render_text_width(mu_Font font, const char *text, int len) { + if (!text) return 0; + int x = 0; while (*text && len) { @@ -102,8 +146,9 @@ int render_text_height(mu_Font font) void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color) { + if (!text) return 0; + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); - SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); pos.y += face->size->metrics.ascender / 64; while (*text) { @@ -115,6 +160,8 @@ void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color) .h = g->h, }; + SDL_SetTextureColorMod(g->tex, color.r, color.g, color.b); + SDL_SetTextureAlphaMod(g->tex, color.a); SDL_RenderCopy(renderer, g->tex, NULL, &r); pos.x += g->adv_x; @@ -134,7 +181,7 @@ int render_init() } char buf[2048]; - file_path_append(buf, file_get_basedir(), "font.ttf", sizeof(buf)); + file_path_append(buf, file_get_basedir(), "assets/font.ttf", sizeof(buf)); err = FT_New_Face(ft, buf, 0, &face); if (err) { return -5; @@ -154,6 +201,8 @@ int render_init() return -2; } + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + renderer = SDL_CreateRenderer(window, -1, 0); if (!renderer) { SDL_DestroyWindow(window); @@ -166,6 +215,17 @@ int render_init() glyph_height = face->size->metrics.height / 64; + file_path_append(buf, file_get_basedir(), "assets/icons.raw.zlib", sizeof(buf)); + icons_atlas = load_rgba32_zlib_texture(buf, 1024, 64); + + for (int i = 1; i < 1024/64; i++) { + icons[i].src = (SDL_Rect) { (i-1)*64, 0, 64, 64 }; + icons[i].color = mu_color(0, 0, 0, 0); + } + + icons[DBGICON_CURRENT].color = (mu_Color) { 255, 192, 50, 255 }; + icons[DBGICON_BREAKPOINT].color = (mu_Color) { 238, 49, 116, 255 }; + return 0; } @@ -194,6 +254,40 @@ void render_draw_rect(mu_Rect rect, mu_Color c) SDL_RenderFillRect(renderer, &r); } +void render_draw_icon(int id, mu_Rect rect, mu_Color c) +{ + SDL_Rect r = { + .x = rect.x, + .y = rect.y, + .w = rect.w, + .h = rect.h, + }; + + SDL_Rect *src = &icons[id].src; + mu_Color colors[2] = { + c, + icons[id].color, + }; + + c = colors[!(!colors[1].a)]; + + SDL_SetTextureColorMod(icons_atlas, c.r, c.g, c.b); + SDL_SetTextureAlphaMod(icons_atlas, c.a); + SDL_RenderCopy(renderer, icons_atlas, src, &r); +} + +void render_clip_rect(mu_Rect rect) +{ + SDL_Rect r = { + .x = rect.x, + .y = rect.y, + .w = rect.w, + .h = rect.h, + }; + + SDL_RenderSetClipRect(renderer, &r); +} + void render_clear(mu_Color c) { SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a); @@ -204,3 +298,8 @@ void render_present() { SDL_RenderPresent(renderer); } + +uint32_t render_get_window_id() +{ + return SDL_GetWindowID(window); +} diff --git a/src/microui_render.h b/src/microui_render.h index c43ca76..bc4bc48 100644 --- a/src/microui_render.h +++ b/src/microui_render.h @@ -1,12 +1,22 @@ #pragma once +#include #include "microui.h" +enum DebugIcon +{ + DBGICON_CURRENT = 6, + DBGICON_BREAKPOINT, +}; + int render_text_width(mu_Font font, const char *text, int len); int render_text_height(mu_Font font); void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color); int render_init(); void render_deinit(); void render_draw_rect(mu_Rect rect, mu_Color c); +void render_draw_icon(int id, mu_Rect rect, mu_Color c); +void render_clip_rect(mu_Rect rect); void render_clear(mu_Color c); void render_present(); +uint32_t render_get_window_id(); From dd73374e64244bc373417877433fcd45e27476d3 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 17:50:58 +0200 Subject: [PATCH 10/26] debugger improvements cont. * greatly improved input handling * improved error handling in microui_render.c * debugger now has dynamically updated disassembly (no full view yet) --- src/debugger.c | 195 ++++++++++++++++++++++++++----------------- src/debugger.h | 3 + src/input_sdl.c | 23 +++-- src/input_sdl.h | 2 + src/machine.c | 6 +- src/memory.c | 3 + src/microui_render.c | 16 +++- 7 files changed, 163 insertions(+), 85 deletions(-) diff --git a/src/debugger.c b/src/debugger.c index 47b8eb3..dfc5cee 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -6,6 +6,7 @@ #include "z80.h" #include "disasm.h" #include "vector.h" +#include "input_sdl.h" #include #include @@ -15,9 +16,11 @@ static Machine_t *machine; struct Disasm { int breakpoint; - uint16_t pcval; - char *pc; - char *bytes; + uint16_t pc; + int len; + int dirty; + char *pcstr; + char *bytestr; char *instr; }; @@ -89,6 +92,69 @@ static void remove_breakpoint(uint16_t pc) } } +static int disasm_update(struct Disasm *d, uint16_t pc) +{ + *d = (struct Disasm) { 0 }; + d->pc = pc; + + char buf[256]; + + int index = 0; + int len = 1; + char *op = disasm_opcode(&machine->memory.bus[pc], &len, pc); + d->len = len; + + snprintf( + buf, sizeof(buf), + "%04x", pc); + size_t size = strlen(buf)+1; + d->pcstr = malloc(size); + if (d->pcstr == NULL) { + free(op); + return -1; + } + memcpy(d->pcstr, buf, size-1); + d->pcstr[size-1] = 0; + + uint16_t i = pc; + while (len--) { + index += snprintf( + buf+index, sizeof(buf)-index, + "%02x ", machine->memory.bus[i]); + i++; + } + + size = strlen(buf)+1; + d->bytestr = malloc(size); + if (d->bytestr == NULL) { + free(d->pcstr); + free(op); + return -2; + } + memcpy(d->bytestr, buf, size-1); + d->bytestr[size-1] = 0; + + d->instr = op; + return 0; +} + +static void disasm_free(struct Disasm *d) +{ + if (d->bytestr) { free(d->bytestr); d->bytestr = NULL; } + if (d->instr) { free(d->instr); d->instr = NULL; } + if (d->pcstr) { free(d->pcstr); d->pcstr = NULL; } +} + +void debugger_mark_dirty(uint16_t addr) +{ + if (!debugger_enabled) return; + + disasm[addr--].dirty = true; + disasm[addr--].dirty = true; + disasm[addr--].dirty = true; + disasm[addr--].dirty = true; +} + static void update_regs() { struct Z80Regs *r = &machine->cpu.regs; @@ -116,7 +182,7 @@ static void disasm_window(mu_Context *ctx) { pc = machine->cpu.regs.pc; mu_layout_row(ctx, 4, (int[]) { 70, -200, -100, -1 }, 25); - if (mu_button(ctx, "Pause")) { + if (mu_button(ctx, "Pause") && break_on == DBG_BREAKPOINT) { break_on = DBG_STEPINTO; }; if (mu_button(ctx, "Continue")) { @@ -131,31 +197,38 @@ static void disasm_window(mu_Context *ctx) { mu_begin_panel(ctx, "disassembly"); mu_Container *panel = mu_get_current_container(ctx); int line_h = render_text_height(0) + 4; - int pci = 0; - for (size_t i = 0; i < vector_len(disasm); i++) { + int disasm_lines = 0; + int pclines = 0; + int i = machine->cpu.regs.pc - 32; + i = i < 0 ? 0 : i; + for (; i < vector_len(disasm) && disasm_lines < 64;) { struct Disasm *d = &disasm[i]; + if (d->dirty) { + disasm_free(d); + disasm_update(d, i); + } + mu_layout_row(ctx, 4, (int[]) { line_h, 32, 100, -1 }, line_h); int icon = d->breakpoint ? DBGICON_BREAKPOINT : 0; - if (d->pcval == machine->cpu.regs.pc) { + if (d->pc == machine->cpu.regs.pc) { icon = DBGICON_CURRENT; - pci = i; + pclines = disasm_lines; } - mu_layout_row(ctx, 4, (int[]) { line_h, 32, 100, -1 }, line_h); if (mu_button_ex_id(ctx, NULL, 1+i, icon, MU_OPT_NOFRAME)) { d->breakpoint ^= 1; - if (d->breakpoint) add_breakpoint(d->pcval); - else remove_breakpoint(d->pcval); + if (d->breakpoint) add_breakpoint(d->pc); + else remove_breakpoint(d->pc); } - mu_label(ctx, d->pc); - mu_label(ctx, d->bytes); + mu_label(ctx, d->pcstr); + mu_label(ctx, d->bytestr); mu_label(ctx, d->instr); + + disasm_lines++; + i += d->len; } if (pc_changed) { - int curline_y = (line_h + ctx->style->spacing) * pci; - int offset = curline_y - panel->scroll.y; - if (offset < 100 || offset > (panel->body.h - 100)) { - panel->scroll.y = curline_y - panel->body.h / 2; - if (panel->scroll.y < 0) panel->scroll.y = 0; - } + int curline_y = (line_h + ctx->style->spacing) * pclines; + panel->scroll.y = curline_y - panel->body.h / 2; + if (panel->scroll.y < 0) panel->scroll.y = 0; } mu_end_panel(ctx); @@ -202,26 +275,29 @@ static void process_frame(mu_Context *ctx) { mu_end(ctx); } -static int window_loop(mu_Context *ctx) +static int window_loop(mu_Context *ctx, int flush_events) { SDL_Event e; - SDL_PumpEvents(); - while (SDL_PollEvent(&e)) { - int handled = 0; + SDL_Event events[1024]; + if (flush_events) SDL_PumpEvents(); + int count = SDL_PeepEvents(events, 1024, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT); + + for (int i = 0; i < count; i++) { + e = events[i]; switch (e.type) { case SDL_MOUSEMOTION: if (e.motion.windowID != render_get_window_id()) break; mu_input_mousemove(ctx, e.motion.x, e.motion.y); - handled = 1; break; + break; case SDL_MOUSEBUTTONDOWN: if (e.button.windowID != render_get_window_id()) break; mu_input_mousedown(ctx, e.button.x, e.button.y, e.button.button); - handled = 1; break; + break; case SDL_MOUSEBUTTONUP: if (e.button.windowID != render_get_window_id()) break; mu_input_mouseup(ctx, e.button.x, e.button.y, e.button.button); - handled = 1; break; + break; case SDL_WINDOWEVENT: if (e.window.windowID != render_get_window_id()) break; if (e.window.event == SDL_WINDOWEVENT_CLOSE) { @@ -229,11 +305,17 @@ static int window_loop(mu_Context *ctx) break_on = DBG_NONE; return 0; } + break; + case SDL_MOUSEWHEEL: + if (e.wheel.windowID != render_get_window_id()) break; + mu_input_scroll(ctx, 0, e.wheel.y * -30); + break; } + } - if (!handled) { - SDL_PushEvent(&e); - } + if (flush_events) { + input_sdl_update(); + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); } process_frame(ctx); @@ -276,12 +358,14 @@ void debugger_handle() case DBG_STEPINTO: is_break = true; break; + default: + break; } if (is_break) { break_on = DBG_NONE; update_regs(); - while (window_loop(&context)); + while (window_loop(&context, true)); } } @@ -290,7 +374,7 @@ void debugger_update_window() if (!debugger_enabled) return; update_regs(); - window_loop(&context); + window_loop(&context, false); } void debugger_open(Machine_t *m) @@ -309,59 +393,20 @@ void debugger_open(Machine_t *m) memset(breakpoints, 0, sizeof(breakpoints)); - char buf[1024]; - machine = m; - uint8_t *pmem = machine->memory.bus; - uint16_t pc = 0; - for (; pc < 0x4000;) { + int pc = 0; + for (; pc < 0x10000;) { struct Disasm d = { 0 }; - d.pcval = pc; - - int index = 0; - int len = 1; - char *op = disasm_opcode(pmem, &len, pc); - - snprintf( - buf, sizeof(buf), - "%04x", pc); - size_t size = strlen(buf)+1; - d.pc = malloc(size); - if (d.pc == NULL) { - free(op); - break; - } - memcpy(d.pc, buf, size-1); - d.pc[size-1] = 0; - - while (len--) { - index += snprintf( - buf+index, sizeof(buf)-index, - "%02x ", *pmem); - pmem++; - pc++; - } - - size = strlen(buf)+1; - d.bytes = malloc(size); - if (d.bytes == NULL) { - free(d.pc); - free(op); - break; - } - memcpy(d.bytes, buf, size-1); - d.bytes[size-1] = 0; - - d.instr = op; + disasm_update(&d, pc); vector_add(disasm, d); + + pc++; } update_regs(); debugger_enabled = true; - - debugger_update_window(); } void debugger_close() diff --git a/src/debugger.h b/src/debugger.h index 3b3cc67..005581f 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -1,8 +1,11 @@ #pragma once +#include + struct Machine; void debugger_handle(); void debugger_update_window(); +void debugger_mark_dirty(uint16_t addr); void debugger_open(struct Machine *m); void debugger_close(); diff --git a/src/input_sdl.c b/src/input_sdl.c index 0e65cc4..f4bd6e8 100644 --- a/src/input_sdl.c +++ b/src/input_sdl.c @@ -32,29 +32,36 @@ int input_sdl_update() int quit = 0; SDL_Event e; - while (SDL_PollEvent(&e)) { - int handled = 0; + SDL_Event events[1024]; + int count = SDL_PeepEvents(events, 1024, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT); + + for (int i = 0; i < count; i++) { + e = events[i]; switch (e.type) { case SDL_QUIT: - handled = 1; quit = 1; break; case SDL_DROPFILE: - handled = 1; machine_open_file(e.drop.file); SDL_free(e.drop.file); break; } - - if (!handled) { - SDL_PushEvent(&e); - } } return quit; } +void input_sdl_pump_events() +{ + SDL_PumpEvents(); +} + +void input_sdl_flush_events() +{ + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); +} + void input_sdl_copy_old_state() { if (keyboard_state_old != NULL) { diff --git a/src/input_sdl.h b/src/input_sdl.h index baca715..ff1e3fb 100644 --- a/src/input_sdl.h +++ b/src/input_sdl.h @@ -5,6 +5,8 @@ void input_sdl_init(); void input_sdl_deinit(); int input_sdl_update(); +void input_sdl_pump_events(); +void input_sdl_flush_events(); void input_sdl_copy_old_state(); uint8_t input_sdl_get_key(uint16_t scancode); uint8_t input_sdl_get_key_pressed(uint16_t scancode); diff --git a/src/machine.c b/src/machine.c index 99ac11f..9dc473d 100644 --- a/src/machine.c +++ b/src/machine.c @@ -219,12 +219,16 @@ int machine_do_cycles() keyboard_macro_process(); + input_sdl_copy_old_state(); + input_sdl_pump_events(); + debugger_update_window(); - input_sdl_copy_old_state(); int quit = input_sdl_update(); if (quit) return -2; + input_sdl_flush_events(); + hotkeys_process(); machine_process_events(); return 0; diff --git a/src/memory.c b/src/memory.c index 2991b02..15040bc 100644 --- a/src/memory.c +++ b/src/memory.c @@ -6,6 +6,7 @@ #include "log.h" #include "ula.h" #include "machine.h" +#include "debugger.h" /* Initializes the DRAM to a pseudo-random state it would have on initial power-on. */ void memory_init(Memory_t *mem) @@ -59,6 +60,8 @@ uint8_t memory_write(struct Machine *ctx, uint16_t addr, uint8_t value) ctx->memory.bus[addr] = value; } + debugger_mark_dirty(addr); + return 0; } diff --git a/src/microui_render.c b/src/microui_render.c index d0ff770..67368c7 100644 --- a/src/microui_render.c +++ b/src/microui_render.c @@ -2,6 +2,7 @@ #include "microui.h" #include "file.h" +#include "log.h" #include #include #include @@ -177,18 +178,21 @@ int render_init() int err = FT_Init_FreeType(&ft); if (err) { - return -4; + dlog(LOG_ERR, "%s: failed to initialize FreeType", __func__); + return -4; } char buf[2048]; file_path_append(buf, file_get_basedir(), "assets/font.ttf", sizeof(buf)); err = FT_New_Face(ft, buf, 0, &face); if (err) { + dlog(LOG_ERR, "%s: failed to load face \"%s\"", __func__, buf); return -5; } err = FT_Set_Char_Size(face, 0, 12*64, 72, 72); if (err) { + dlog(LOG_ERR, "%s: failed to set character size", __func__); return -6; } @@ -198,6 +202,7 @@ int render_init() width, height, 0); if (!window) { + dlog(LOG_ERR, "%s: failed to create window", __func__); return -2; } @@ -206,6 +211,7 @@ int render_init() renderer = SDL_CreateRenderer(window, -1, 0); if (!renderer) { SDL_DestroyWindow(window); + dlog(LOG_ERR, "%s: failed to create renderer", __func__, buf); return -3; } @@ -217,6 +223,9 @@ int render_init() file_path_append(buf, file_get_basedir(), "assets/icons.raw.zlib", sizeof(buf)); icons_atlas = load_rgba32_zlib_texture(buf, 1024, 64); + if (!icons_atlas) { + dlog(LOG_WARN, "%s: failed to load \"%s\"", __func__, buf); + } for (int i = 1; i < 1024/64; i++) { icons[i].src = (SDL_Rect) { (i-1)*64, 0, 64, 64 }; @@ -271,6 +280,11 @@ void render_draw_icon(int id, mu_Rect rect, mu_Color c) c = colors[!(!colors[1].a)]; + if (!icons_atlas) { + render_draw_rect(rect, c); + return; + } + SDL_SetTextureColorMod(icons_atlas, c.r, c.g, c.b); SDL_SetTextureAlphaMod(icons_atlas, c.a); SDL_RenderCopy(renderer, icons_atlas, src, &r); From 52dbfbd37b769f4af2ff27347afc9f94b639e01a Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 18:46:24 +0200 Subject: [PATCH 11/26] fix some warnings & segfault on checkbox --- src/config_parser.c | 2 +- src/config_parser.h | 2 +- src/debugger.c | 7 ++++--- src/machine_test.c | 6 ++++-- src/microui_render.c | 9 +++++---- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/config_parser.c b/src/config_parser.c index fa7470e..a6da810 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -231,7 +231,7 @@ int config_get_float(CfgData_t *cfg, const char *key, float *dest) return 0; } -void config_set_str(CfgData_t *cfg, const char *key, char *value) +void config_set_str(CfgData_t *cfg, const char *key, const char *value) { if (value == NULL) { return; diff --git a/src/config_parser.h b/src/config_parser.h index 8989a06..801c614 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -37,6 +37,6 @@ int config_get_int(CfgData_t *cfg, const char *key, int *dest); * config value gets copied to dest */ int config_get_float(CfgData_t *cfg, const char *key, float *dest); -void config_set_str(CfgData_t *cfg, const char *key, char *value); +void config_set_str(CfgData_t *cfg, const char *key, const char *value); void config_set_int(CfgData_t *cfg, const char *key, int value); void config_set_float(CfgData_t *cfg, const char *key, float value); diff --git a/src/debugger.c b/src/debugger.c index dfc5cee..6a30270 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -199,8 +199,8 @@ static void disasm_window(mu_Context *ctx) { int line_h = render_text_height(0) + 4; int disasm_lines = 0; int pclines = 0; - int i = machine->cpu.regs.pc - 32; - i = i < 0 ? 0 : i; + size_t i = machine->cpu.regs.pc; + i = i > 32 ? i - 32 : 0; for (; i < vector_len(disasm) && disasm_lines < 64;) { struct Disasm *d = &disasm[i]; if (d->dirty) { @@ -255,7 +255,7 @@ static void regs_window(mu_Context *ctx) int clicked = 0; for (int i = 7; i >= 0; i--) { flags[i] = machine->cpu.regs.main.f & (1<cpu.regs.main.f = f; + update_regs(); } mu_end_window(ctx); } diff --git a/src/machine_test.c b/src/machine_test.c index 065ee05..0aafeed 100644 --- a/src/machine_test.c +++ b/src/machine_test.c @@ -376,7 +376,7 @@ static void finish_print() remove(tmp); } -static void test_finish(struct Machine *m) { +static void test_finish() { if (test.docflags) { finish_hash(test.docflags, "docflags"); } @@ -437,8 +437,10 @@ int machine_test_iterate(struct Machine *m) } if (test_condition(m)) { - test_finish(m); + test_finish(); } + + return 0; } void machine_test_close() diff --git a/src/microui_render.c b/src/microui_render.c index 67368c7..bdd7c04 100644 --- a/src/microui_render.c +++ b/src/microui_render.c @@ -71,8 +71,9 @@ static int cache_glyph(FT_Face f, char c) } uint32_t *px = buf; + int size = bitmap->pitch*bitmap->rows; - for (int i = 0; i < bitmap->pitch*bitmap->rows; i++) { + for (int i = 0; i < size; i++) { px[i] = 0xFFFFFF00 | bitmap->buffer[i]; } @@ -110,11 +111,11 @@ SDL_Texture *load_rgba32_zlib_texture(const char *path, int width, int height) if (!tex) return NULL; - uint8_t *buf; + void *buf; int pitch; SDL_LockTexture(tex, NULL, &buf, &pitch); - size_t len = pitch * height; + uLongf len = pitch * height; uncompress(buf, &len, src, fsize); SDL_UnlockTexture(tex); @@ -147,7 +148,7 @@ int render_text_height(mu_Font font) void render_text(mu_Font font, const char *text, mu_Vec2 pos, mu_Color color) { - if (!text) return 0; + if (!text) return; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); pos.y += face->size->metrics.ascender / 64; From 13537811be41b3414cd403af39e4d38e123482b6 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Mon, 15 May 2023 19:26:50 +0200 Subject: [PATCH 12/26] static build on windoze --- build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index d85bc4d..cc9b64d 100755 --- a/build.sh +++ b/build.sh @@ -20,7 +20,8 @@ done if [[ $PLATFORM == "WIN32" ]] || [[ $PLATFORM == "win32" ]]; then WIN32_FILES="src/win32/*.c src/win32/*.o" - WIN32_FLAGS="-DPLATFORM_WIN32 -static `sdl2-config --static-libs`" + WIN32_FLAGS="-DPLATFORM_WIN32 -static `sdl2-config --static-libs` \ + `pkg-config --libs --static freetype2` -lstdc++" FILES="$FILES $WIN32_FILES" FLAGS="$FLAGS $WIN32_FLAGS" From 62ce80a1de384da553eb4677bef3306da7a4062c Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 14:05:23 +0200 Subject: [PATCH 13/26] add a meson build file, wip --- meson.build | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 meson.build diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..c5c229e --- /dev/null +++ b/meson.build @@ -0,0 +1,77 @@ +project('sleepdart', 'c', default_options: ['buildtype=release']) + +release_args = ['-ffast-math', '-flto', '-fwhole-program'] +if get_option('buildtype') == 'release' + add_project_arguments(release_args, language : 'c') + add_project_link_arguments(release_args, language : 'c') +endif + +includes = include_directories('microui/src') + +extra_src = [] +extra_obj = [] +static_libs = false + +if target_machine.system() == 'windows' + windres = find_program('windres') + run_command(windres, 'src/win32/resource.rc', 'src/win32/resource.o') + extra_src = [ + 'src/win32/gui_windows.c', + ] + extra_obj = [ + 'src/win32/resource.o', + ] + add_project_arguments('-DPLATFORM_WIN32', language : 'c') + static_libs = true +endif + +deps = [ + dependency('zlib', static: static_libs), + dependency('sdl2', static: static_libs), + dependency('libxxhash', static: static_libs), + dependency('freetype2', static: static_libs), +] + +add_project_link_arguments('-lstdc++', language: 'c') + +executable( + 'sleepdart', + 'src/main.c', + 'src/argparser.c', + 'src/audio_sdl.c', + 'src/ay.c', + 'src/beeper.c', + 'src/config.c', + 'src/config_parser.c', + 'src/debugger.c', + 'src/disasm.c', + 'src/dsp.c', + 'src/file.c', + 'src/hotkeys.c', + 'src/input_sdl.c', + 'src/io.c', + 'src/keyboard.c', + 'src/keyboard_macro.c', + 'src/log.c', + 'src/machine.c', + 'src/machine_hooks.c', + 'src/machine_test.c', + 'src/memory.c', + 'src/microui_render.c', + 'src/palette.c', + 'src/parser_helpers.c', + 'src/szx_file.c', + 'src/szx_state.c', + 'src/tape.c', + 'src/ula.c', + 'src/video_sdl.c', + 'src/z80.c', + 'microui/src/microui.c', + 'ayumi/ayumi.c', + extra_src, + include_directories: includes, + dependencies: deps, + objects: extra_obj, + win_subsystem: 'windows', + link_args: '-lm', +) From cf7d22d351b6e4fcd5e14aab045691ae39e72b1e Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 14:08:33 +0200 Subject: [PATCH 14/26] fix possible crash in putc_zx --- src/machine_hooks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine_hooks.c b/src/machine_hooks.c index 838ff57..6f59796 100644 --- a/src/machine_hooks.c +++ b/src/machine_hooks.c @@ -17,6 +17,8 @@ static FILE *out_stream = NULL; static void putc_zx(uint8_t ch, FILE *f) { + if (!f) return; + char *s = NULL; switch (ch) { From 46fb2a81f7e79364bf2f04b7939547a96d4b1110 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 14:21:39 +0200 Subject: [PATCH 15/26] meson: add git describe, style adjustments --- meson.build | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index c5c229e..ede7ddb 100644 --- a/meson.build +++ b/meson.build @@ -2,8 +2,8 @@ project('sleepdart', 'c', default_options: ['buildtype=release']) release_args = ['-ffast-math', '-flto', '-fwhole-program'] if get_option('buildtype') == 'release' - add_project_arguments(release_args, language : 'c') - add_project_link_arguments(release_args, language : 'c') + add_project_arguments(release_args, language: 'c') + add_project_link_arguments(release_args, language: 'c') endif includes = include_directories('microui/src') @@ -21,10 +21,19 @@ if target_machine.system() == 'windows' extra_obj = [ 'src/win32/resource.o', ] - add_project_arguments('-DPLATFORM_WIN32', language : 'c') + add_project_arguments('-DPLATFORM_WIN32', language: 'c') static_libs = true endif +git = find_program('git', required: false) +if git.found() + gitargs = ['describe', '--tags', '--dirty', '--always'] + describe = run_command(git, gitargs, check: false).stdout().strip() + if describe != '' + add_project_arguments('-DGIT_DESCRIBE="' + describe + '"', language: 'c') + endif +endif + deps = [ dependency('zlib', static: static_libs), dependency('sdl2', static: static_libs), From 228feda45ac04fb5d58704dd251d2f741a0cbf08 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 15:58:40 +0200 Subject: [PATCH 16/26] copy executable to root after build --- meson.build | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ede7ddb..a95f7ef 100644 --- a/meson.build +++ b/meson.build @@ -43,7 +43,7 @@ deps = [ add_project_link_arguments('-lstdc++', language: 'c') -executable( +exe = executable( 'sleepdart', 'src/main.c', 'src/argparser.c', @@ -84,3 +84,13 @@ executable( win_subsystem: 'windows', link_args: '-lm', ) + +# a lil hacky... +dest = meson.project_source_root() +custom_target( + 'finalize', + depends: exe, + input: exe, + output: 'fake', + command: ['cp', '@INPUT@', dest], + build_by_default : true) From e19d2d1c65505abfb93fd375dd8abcb0b2ebb5bb Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 16:04:15 +0200 Subject: [PATCH 17/26] update workflow --- .github/workflows/build-test.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 986a3ed..23a749d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -14,9 +14,11 @@ jobs: with: submodules: recursive - name: set up packages - run: sudo apt install gcc libsdl2-dev libxxhash-dev + run: sudo apt install meson ninja-build gcc libsdl2-dev libxxhash-dev - name: build release - run: ./build.sh + run: | + meson setup build + meson compile -C build - name: upload build uses: actions/upload-artifact@v3 with: @@ -37,14 +39,16 @@ jobs: - uses: msys2/setup-msys2@v2 with: release: false - install: git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash + install: meson git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash - run: git config --global core.autocrlf input shell: bash - uses: actions/checkout@v3 with: submodules: recursive - name: build release - run: ./build.sh -p win32 + run: | + meson setup build + meson compile -C build - name: upload build uses: actions/upload-artifact@v3 with: From 715fd0e16a95fe5d0f7023daafe2499858c6b671 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 16:05:52 +0200 Subject: [PATCH 18/26] remove old build script --- build.sh | 58 -------------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index cc9b64d..0000000 --- a/build.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -set -e - -FILES="src/*.c ayumi/ayumi.c microui/src/microui.c" -FLAGS="-std=gnu11 -Wall -Wextra -Wpedantic -lz -lm \ - `sdl2-config --libs` -I microui/src \ - `pkg-config --cflags --libs freetype2`" -FLAGS_DEBUG="-Og -ggdb" -FLAGS_RELEASE="-O3 -ffast-math -flto -fwhole-program" -RELEASE_FILES="sleepdart* rom/* palettes/*" - -while getopts ':p:dr' opt; do - case $opt in - (p) PLATFORM=$OPTARG;; - (d) DEBUG=1;; - (r) RELEASEPKG=1;; - (:) :;; - esac -done - -if [[ $PLATFORM == "WIN32" ]] || [[ $PLATFORM == "win32" ]]; then - WIN32_FILES="src/win32/*.c src/win32/*.o" - WIN32_FLAGS="-DPLATFORM_WIN32 -static `sdl2-config --static-libs` \ - `pkg-config --libs --static freetype2` -lstdc++" - FILES="$FILES $WIN32_FILES" - FLAGS="$FLAGS $WIN32_FLAGS" - - windres src/win32/resource.rc src/win32/resource.o -fi - -if [[ $DEBUG == "1" ]]; then - FLAGS="$FLAGS $FLAGS_DEBUG" -else - FLAGS="$FLAGS $FLAGS_RELEASE" -fi - -set +e -DESCRIBE=`git describe --tags --dirty --always` -set -e -if [[ $? -eq 0 ]]; then - FLAGS="$FLAGS -DGIT_DESCRIBE=\"$DESCRIBE\"" -else - DESCRIBE=0 -fi - -echo "FILES: $FILES" -echo "FLAGS: $FLAGS" -gcc $FILES $FLAGS -o sleepdart - -if [[ $RELEASEPKG == "1" ]]; then - if [[ "$DESCRIBE" == "0" ]]; then - PKGNAME="sleepdart.zip" - else - PKGNAME="sleepdart-$DESCRIBE.zip" - fi - - zip "$PKGNAME" $RELEASE_FILES -fi From 57274c14b5ce0096916e9dfaa42060d391f58198 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 16:11:49 +0200 Subject: [PATCH 19/26] improve disasm update behavior --- src/debugger.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/debugger.c b/src/debugger.c index 6a30270..10daeaa 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -101,7 +101,13 @@ static int disasm_update(struct Disasm *d, uint16_t pc) int index = 0; int len = 1; - char *op = disasm_opcode(&machine->memory.bus[pc], &len, pc); + uint8_t opbuf[4]; + opbuf[0] = machine->memory.bus[(uint16_t)(pc+0)]; + opbuf[1] = machine->memory.bus[(uint16_t)(pc+1)]; + opbuf[2] = machine->memory.bus[(uint16_t)(pc+2)]; + opbuf[3] = machine->memory.bus[(uint16_t)(pc+3)]; + + char *op = disasm_opcode(opbuf, &len, pc); d->len = len; snprintf( From 7e09d8212d2d2ee8260aa18e512e5530155d59d9 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Tue, 16 May 2023 16:22:43 +0200 Subject: [PATCH 20/26] actually add microui module --- microui | 1 + 1 file changed, 1 insertion(+) create mode 160000 microui diff --git a/microui b/microui new file mode 160000 index 0000000..05d7b46 --- /dev/null +++ b/microui @@ -0,0 +1 @@ +Subproject commit 05d7b46c9cf650dd0c5fbc83a9bebf87c80d02a5 From e02d43ae3b7179457873e53c5deec1b154b76a6f Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Wed, 17 May 2023 21:06:13 +0200 Subject: [PATCH 21/26] some disasm improvements, infinite scroll --- src/debugger.c | 160 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 43 deletions(-) diff --git a/src/debugger.c b/src/debugger.c index 10daeaa..32c950c 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -9,6 +9,7 @@ #include "input_sdl.h" #include #include +#include "log.h" static mu_Context context; static Machine_t *machine; @@ -19,6 +20,7 @@ struct Disasm uint16_t pc; int len; int dirty; + int prev_offset; char *pcstr; char *bytestr; char *instr; @@ -94,7 +96,8 @@ static void remove_breakpoint(uint16_t pc) static int disasm_update(struct Disasm *d, uint16_t pc) { - *d = (struct Disasm) { 0 }; + d->prev_offset = 0; + d->dirty = 0; d->pc = pc; char buf[256]; @@ -151,6 +154,25 @@ static void disasm_free(struct Disasm *d) if (d->pcstr) { free(d->pcstr); d->pcstr = NULL; } } +static void update_dirty(uint16_t pc) +{ + int org_pc = pc; + int offset = 0; + do { + struct Disasm *d = &disasm[org_pc]; + offset = d->prev_offset; + disasm_free(d); + disasm_update(d, d->pc); + org_pc -= offset; + } while (offset && disasm[(uint16_t)org_pc].dirty); + + while (org_pc <= pc) { + int b = disasm[(uint16_t)org_pc].len; + org_pc += b; + disasm[(uint16_t)org_pc].prev_offset = b; + } +} + void debugger_mark_dirty(uint16_t addr) { if (!debugger_enabled) return; @@ -181,12 +203,94 @@ static void update_regs() machine->cpu.cycles, machine->timing.t_frame); } +static disasm_row(mu_Context *ctx, struct Disasm *d, int id, int line_h) +{ + if (d->dirty) { + update_dirty(d->pc); + } + mu_layout_row(ctx, 4, (int[]) { line_h, 32, 100, -1 }, line_h); + int icon = d->breakpoint ? DBGICON_BREAKPOINT : 0; + if (d->pc == machine->cpu.regs.pc) { + icon = DBGICON_CURRENT; + } + if (mu_button_ex_id(ctx, NULL, 1+id, icon, MU_OPT_NOFRAME)) { + d->breakpoint ^= 1; + if (d->breakpoint) add_breakpoint(d->pc); + else remove_breakpoint(d->pc); + } + mu_label(ctx, d->pcstr); + mu_label(ctx, d->bytestr); + mu_label(ctx, d->instr); +} + +static uint16_t find_prev_pc(uint16_t pc) +{ + if (disasm[pc].dirty) { + update_dirty(pc); + } + pc -= disasm[pc].prev_offset; + return pc; +} + +static disasm_panel(mu_Context *ctx) +{ + int line_h = (render_text_height(0) + 5) & ~1; + static int pc = -1; + static uint16_t abs_scroll_line = 0; + int pc_changed = (machine->cpu.regs.pc != pc); + pc = machine->cpu.regs.pc; + abs_scroll_line = pc_changed ? pc : abs_scroll_line; + + mu_begin_panel_ex(ctx, "disassembly", MU_OPT_NOSCROLL); + mu_Container *panel = mu_get_current_container(ctx); + + const int range = 64; + int up_offset = range / 2; + uint16_t line_start = abs_scroll_line; + do { + uint16_t old = line_start; + line_start = find_prev_pc(line_start); + if (line_start == old) { + break; + } + } while (--up_offset); + + for (int i = range; i--; ) { + disasm_row(ctx, &disasm[line_start], i, line_h); + line_start += disasm[line_start].len; + } + + if (mu_mouse_over(ctx, panel->body)) { ctx->scroll_target = panel; } + + int line_h_actual = line_h + ctx->style->spacing; + int target_center_scroll = ((range / 2 - up_offset) * line_h_actual) - (panel->body.h / 2); + if (pc_changed) panel->scroll.y = target_center_scroll; + + int offset = target_center_scroll - panel->scroll.y; + int offset_line = offset / line_h_actual; + if (offset_line < -5) { + do { + abs_scroll_line += disasm[abs_scroll_line].len; + offset_line++; + panel->scroll.y -= line_h_actual; + } while (offset_line < -5); + } else if (offset_line > 5) { + do { + uint16_t prev = find_prev_pc(abs_scroll_line); + if (prev == abs_scroll_line) { + break; + } + abs_scroll_line = prev; + offset_line--; + panel->scroll.y += line_h_actual; + } while (offset_line > 5); + } + + mu_end_panel(ctx); +} + static void disasm_window(mu_Context *ctx) { if (mu_begin_window_ex(ctx, "Debugger", mu_rect(0, 0, 400, 600), MU_OPT_NOCLOSE)) { - static int pc = -1; - int pc_changed = (machine->cpu.regs.pc != pc); - pc = machine->cpu.regs.pc; - mu_layout_row(ctx, 4, (int[]) { 70, -200, -100, -1 }, 25); if (mu_button(ctx, "Pause") && break_on == DBG_BREAKPOINT) { break_on = DBG_STEPINTO; @@ -198,45 +302,9 @@ static void disasm_window(mu_Context *ctx) { if (mu_button(ctx, "Step into")) { break_on = DBG_STEPINTO; }; - - mu_layout_row(ctx, 1, (int[]) { -1 }, -1); - mu_begin_panel(ctx, "disassembly"); - mu_Container *panel = mu_get_current_container(ctx); - int line_h = render_text_height(0) + 4; - int disasm_lines = 0; - int pclines = 0; - size_t i = machine->cpu.regs.pc; - i = i > 32 ? i - 32 : 0; - for (; i < vector_len(disasm) && disasm_lines < 64;) { - struct Disasm *d = &disasm[i]; - if (d->dirty) { - disasm_free(d); - disasm_update(d, i); - } - mu_layout_row(ctx, 4, (int[]) { line_h, 32, 100, -1 }, line_h); - int icon = d->breakpoint ? DBGICON_BREAKPOINT : 0; - if (d->pc == machine->cpu.regs.pc) { - icon = DBGICON_CURRENT; - pclines = disasm_lines; - } - if (mu_button_ex_id(ctx, NULL, 1+i, icon, MU_OPT_NOFRAME)) { - d->breakpoint ^= 1; - if (d->breakpoint) add_breakpoint(d->pc); - else remove_breakpoint(d->pc); - } - mu_label(ctx, d->pcstr); - mu_label(ctx, d->bytestr); - mu_label(ctx, d->instr); - disasm_lines++; - i += d->len; - } - if (pc_changed) { - int curline_y = (line_h + ctx->style->spacing) * pclines; - panel->scroll.y = curline_y - panel->body.h / 2; - if (panel->scroll.y < 0) panel->scroll.y = 0; - } - mu_end_panel(ctx); + mu_layout_row(ctx, 1, (int[]) { -1 }, -1); + disasm_panel(ctx); mu_end_window(ctx); } @@ -411,6 +479,12 @@ void debugger_open(Machine_t *m) pc++; } + for (pc = 0; pc < 0x10000;) { + int b = disasm[(uint16_t)(pc)].len; + pc += b; + disasm[(uint16_t)(pc)].prev_offset = b; + } + update_regs(); debugger_enabled = true; From 6858ea5644966622d22766cadc43f66d9c78eff0 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Wed, 17 May 2023 21:07:23 +0200 Subject: [PATCH 22/26] meson -> mingw-w64-x86_64-meson --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 23a749d..babcd7c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -39,7 +39,7 @@ jobs: - uses: msys2/setup-msys2@v2 with: release: false - install: meson git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash + install: mingw-w64-x86_64-meson git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash - run: git config --global core.autocrlf input shell: bash - uses: actions/checkout@v3 From 765f9519221b120d33e0391039afbd00844a9a44 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Wed, 17 May 2023 23:17:28 +0200 Subject: [PATCH 23/26] mingw-w64-x86_64-pkgconf --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index babcd7c..087e425 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -39,7 +39,7 @@ jobs: - uses: msys2/setup-msys2@v2 with: release: false - install: mingw-w64-x86_64-meson git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash + install: mingw-w64-x86_64-meson mingw-w64-x86_64-pkgconf git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash - run: git config --global core.autocrlf input shell: bash - uses: actions/checkout@v3 From b19219ef739cbe90dfe7524dc93b60e73526099e Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Wed, 17 May 2023 23:50:21 +0200 Subject: [PATCH 24/26] did i seriously forget half of the packages --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 087e425..de5370e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -39,7 +39,7 @@ jobs: - uses: msys2/setup-msys2@v2 with: release: false - install: mingw-w64-x86_64-meson mingw-w64-x86_64-pkgconf git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash + install: mingw-w64-x86_64-meson mingw-w64-x86_64-pkgconf git mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 zlib mingw-w64-x86_64-xxhash mingw-w64-x86_64-freetype - run: git config --global core.autocrlf input shell: bash - uses: actions/checkout@v3 From bef7e7ab7aaf2f448443d7328c9a026cf13ef4bf Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Thu, 18 May 2023 01:32:29 +0200 Subject: [PATCH 25/26] infinite scrollbar --- microui | 2 +- src/debugger.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/microui b/microui index 05d7b46..de1f356 160000 --- a/microui +++ b/microui @@ -1 +1 @@ -Subproject commit 05d7b46c9cf650dd0c5fbc83a9bebf87c80d02a5 +Subproject commit de1f3567ebbdb9b6635f1ccc981779c409d6f052 diff --git a/src/debugger.c b/src/debugger.c index 32c950c..7943b8b 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -9,6 +9,7 @@ #include "input_sdl.h" #include #include +#include #include "log.h" static mu_Context context; @@ -241,7 +242,7 @@ static disasm_panel(mu_Context *ctx) pc = machine->cpu.regs.pc; abs_scroll_line = pc_changed ? pc : abs_scroll_line; - mu_begin_panel_ex(ctx, "disassembly", MU_OPT_NOSCROLL); + mu_begin_panel_ex(ctx, "disassembly", MU_OPT_INFSCROLLY); mu_Container *panel = mu_get_current_container(ctx); const int range = 64; @@ -260,8 +261,6 @@ static disasm_panel(mu_Context *ctx) line_start += disasm[line_start].len; } - if (mu_mouse_over(ctx, panel->body)) { ctx->scroll_target = panel; } - int line_h_actual = line_h + ctx->style->spacing; int target_center_scroll = ((range / 2 - up_offset) * line_h_actual) - (panel->body.h / 2); if (pc_changed) panel->scroll.y = target_center_scroll; @@ -393,9 +392,15 @@ static int window_loop(mu_Context *ctx, int flush_events) SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); } + static uint64_t ticks_old = 0; + uint64_t ticks = SDL_GetTicks64(); + ctx->delta_ms = ticks - ticks_old; + process_frame(ctx); process_frame(ctx); + ticks_old = ticks; + render_clear(mu_color(0, 0, 0, 255)); mu_Command *cmd = NULL; while (mu_next_command(ctx, &cmd)) { From 795b6a62346a56a399a8948f9ca05c50f981d838 Mon Sep 17 00:00:00 2001 From: wermi <32251376+wermipls@users.noreply.github.com> Date: Thu, 18 May 2023 22:26:13 +0200 Subject: [PATCH 26/26] orz ... fix wraparound in update_dirty --- src/debugger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debugger.c b/src/debugger.c index 7943b8b..371e308 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -160,7 +160,7 @@ static void update_dirty(uint16_t pc) int org_pc = pc; int offset = 0; do { - struct Disasm *d = &disasm[org_pc]; + struct Disasm *d = &disasm[(uint16_t)org_pc]; offset = d->prev_offset; disasm_free(d); disasm_update(d, d->pc);