From bb7b6e8532ef3282fdd15ec133deff919cd7e7c3 Mon Sep 17 00:00:00 2001 From: Ian C Date: Sun, 23 Apr 2023 09:42:21 +0800 Subject: Initial work to add debug menu from 3dsspec --- include/debug.h | 53 ++++++ include/gui.h | 1 + include/keyboard.h | 4 + source/debug.c | 162 +++++++++++++++++++ source/gui.c | 153 ++++++++++++++++++ source/keyboard.c | 6 + source/main.c | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 835 insertions(+), 11 deletions(-) create mode 100644 include/debug.h create mode 100644 source/debug.c diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..a6fce71 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,53 @@ +/* + + 3ds81 - Nintendo 3DS ZX81 emulator + + Copyright (C) 2021 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see + + ------------------------------------------------------------------------- + + Provides an interface for debug + +*/ + +#ifndef DSSPEC_DEBUG_H +#define DSSPEC_DEBUG_H + +/* This variable is TRUE if debug is enabled. Also use this for setting the + mode. +*/ +extern int debug_enabled; + +/* Use this macro to use debug. It saves a function call if debug is disabled. +*/ +#define SPEC_DEBUG(...) \ +do \ +{ \ + if (debug_enabled) DEBUG_Output(__VA_ARGS__); \ +} while(0) + +/* The host and port to use for the debug +*/ +void DEBUG_SetAddress(const char *host, const char *port); + +/* The debug interface. Takes a printf style format and parameters. +*/ +void DEBUG_Output(const char *format, ...); + +#endif + + +/* END OF FILE */ diff --git a/include/gui.h b/include/gui.h index 3dd3fe2..c5eeaed 100644 --- a/include/gui.h +++ b/include/gui.h @@ -28,5 +28,6 @@ int GUI_FileSelect(char pwd[], char selected_file[], const char *filter); int GUI_InputName(const char *prompt, const char *ext, char name[], int maxlen); +int GUI_Input(const char *prompt, char text[], int maxlen); #endif /* DS81_GUI_H */ diff --git a/include/keyboard.h b/include/keyboard.h index f0671f0..e0d247c 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -127,6 +127,10 @@ void SK_DefinePad(SoftKey pad, SoftKey key); */ const char *SK_KeyName(SoftKey pad); +/* Returns TRUE if the passed key is currently pressed +*/ +int SK_KeyPressed(SoftKey key); + /* Allows the keyboard to save/restore its state from a stream */ void SK_SaveSnapshot(FILE *fp); diff --git a/source/debug.c b/source/debug.c new file mode 100644 index 0000000..c9c36a1 --- /dev/null +++ b/source/debug.c @@ -0,0 +1,162 @@ +/* + + 3ds81 - Nintendo 3DS ZX81 emulator + + Copyright (C) 2021 Ian Cowburn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see + + ------------------------------------------------------------------------- + + Provides an interface for debug + +*/ +#include +#include +#include +#include +#include +#include <3ds.h> + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "gui.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* ---------------------------------------- GLOBAL EXTERNALS +*/ +int debug_enabled = FALSE; + +/* ---------------------------------------- PRIVATE DAYA +*/ +static char debug_host[256]; +static char debug_port[32]; +static int sock = -1; + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void Error(const char *format, ...) +{ + static char buff[4096]; + va_list va; + + va_start(va, format); + vsnprintf(buff, 4096, format, va); + buff[4095] = 0; + va_end(va); + + GUI_Alert(FALSE, buff); +} + +static void OpenDebugConnection(void) +{ + if (sock == -1) + { + struct addrinfo *addr, *addr_p; + struct addrinfo hints = {0}; + + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(debug_host, debug_port, &hints, &addr) == 0) + { + for(addr_p = addr; addr_p && sock == -1; addr_p = addr_p->ai_next) + { + sock = socket(addr_p->ai_family, + addr_p->ai_socktype, + addr_p->ai_protocol); + + if (sock != -1) + { + if (connect(sock, addr_p->ai_addr, addr_p->ai_addrlen) + == -1) + { + Error("Failed to connect socket"); + close(sock); + sock = -1; + } + } + else + { + Error("Failed to create socket(%d,%d,%d)\n%d %s", + addr_p->ai_family, + addr_p->ai_socktype, + addr_p->ai_protocol, + errno, + strerror(errno)); + } + } + } + else + { + Error("Failed to getaddrinfo(%s,%s)", debug_host, debug_port); + } + } +} + +/* ---------------------------------------- EXPORTED INTERFACES +*/ +void DEBUG_SetAddress(const char *host, const char *port) +{ + strncpy(debug_host, host, (sizeof debug_host) - 1); + strncpy(debug_port, port, (sizeof debug_port) - 1); + + debug_host[(sizeof debug_host) - 1] = 0; + debug_port[(sizeof debug_port) - 1] = 0; + + if (sock != -1) + { + close(sock); + sock = -1; + } +} + + +void DEBUG_Output(const char *format, ...) +{ + static char buff[4096]; + + if (debug_enabled) + { + va_list va; + + OpenDebugConnection(); + + va_start(va, format); + vsnprintf(buff, 4096, format, va); + buff[4095] = 0; + va_end(va); + + if (sock != -1) + { + send(sock, buff, strlen(buff), 0); + } + } +} + + +/* END OF FILE */ diff --git a/source/gui.c b/source/gui.c index eee82f4..bbea23c 100644 --- a/source/gui.c +++ b/source/gui.c @@ -917,3 +917,156 @@ int GUI_InputName(const char *prompt, const char *ext, char name[], int maxlen) return accept; } + +int GUI_Input(const char *prompt, char text[], int maxlen) +{ + struct + { + SoftKey key; + int ascii; + int shift; + } keymap[] = + { + {SK_1, '1', '!'}, + {SK_2, '2', '@'}, + {SK_3, '3', '#'}, + {SK_4, '4', '$'}, + {SK_5, '5', '%'}, + {SK_6, '6', '&'}, + {SK_7, '7', '\''}, + {SK_8, '8', '('}, + {SK_9, '9', ')'}, + {SK_0, '0', '_'}, + {SK_A, 'A', '~'}, + {SK_B, 'B', '*'}, + {SK_C, 'C', '?'}, + {SK_D, 'D', '\\'}, + {SK_E, 'E', 0}, + {SK_F, 'F', '{'}, + {SK_G, 'G', '}'}, + {SK_H, 'H', '^'}, + {SK_I, 'I', 0}, + {SK_J, 'J', '-'}, + {SK_K, 'K', '+'}, + {SK_L, 'L', '='}, + {SK_M, 'M', '.'}, + {SK_N, 'N', ','}, + {SK_O, 'O', ';'}, + {SK_P, 'P', '"'}, + {SK_Q, 'Q', 0}, + {SK_R, 'R', '<'}, + {SK_S, 'S', '|'}, + {SK_T, 'T', '>'}, + {SK_U, 'U', ']'}, + {SK_V, 'V', '/'}, + {SK_W, 'W', 0}, + {SK_X, 'X', 0}, + {SK_Y, 'Y', '['}, + {SK_Z, 'Z', ':'}, + {0, 0, 0} + }; + + SoftKeyEvent ev; + int done = FALSE; + int accept = FALSE; + int shifted; + Framebuffer upper; + + text[0] = 0; + shifted = SK_KeyPressed(SK_SHIFT); + + while(!done) + { + FB_StartFrame(&upper, NULL); + + FB_Clear(&upper, COL_WHITE); + SK_DisplayKeyboard(); + + FB_printf(&upper, 0, 16, COL_BLACK, COL_TRANSPARENT, "%s", prompt); + FB_printf(&upper, 0, 8, COL_BLACK, COL_TRANSPARENT, "\"%s\001L\001\"", + text); + + FB_Print(&upper, "PRESS ENTER TO ACCEPT", 0, 32, + COL_BLACK, COL_TRANSPARENT); + FB_Print(&upper, "PRESS SPACE/BREAK TO CANCEL", 0, 40, + COL_BLACK, COL_TRANSPARENT); + + FB_EndFrame(); + + while(SK_GetBareEvent(&ev)) + { + if (ev.pressed) + { + switch(ev.key) + { + case SK_SHIFT: + shifted = TRUE; + break; + default: + break; + } + } + + if (!ev.pressed) + { + size_t l; + int f; + int ascii; + + l = strlen(text); + + switch(ev.key) + { + case SK_SHIFT: + shifted = FALSE; + break; + + case SK_SPACE: + done = TRUE; + accept = FALSE; + break; + + case SK_NEWLINE: + done = TRUE; + accept = TRUE; + break; + + default: + if (l < maxlen) + { + f = 0; + ascii = 0; + + while(!ascii && keymap[f].ascii) + { + if (ev.key == keymap[f].key) + { + if (shifted) + { + ascii = keymap[f].shift; + } + else + { + ascii = keymap[f].ascii; + } + } + + f++; + } + + if (ascii) + { + text[l++] = ascii; + text[l] = 0; + } + } + break; + } + } + } + } + + return accept; +} + + diff --git a/source/keyboard.c b/source/keyboard.c index a08b72d..b598a7c 100644 --- a/source/keyboard.c +++ b/source/keyboard.c @@ -372,6 +372,12 @@ const char *SK_KeyName(SoftKey k) } +int SK_KeyPressed(SoftKey key) +{ + return key_state[key].state; +} + + void SK_SaveSnapshot(FILE *fp) { int f; diff --git a/source/main.c b/source/main.c index 8ea8e00..ef2633e 100644 --- a/source/main.c +++ b/source/main.c @@ -32,6 +32,7 @@ #include "tapes.h" #include "config.h" #include "snapshot.h" +#include "debug.h" #ifndef DS81_VERSION #define DS81_VERSION "DEV " __TIME__ "/" __DATE__ @@ -45,17 +46,9 @@ #define FALSE 0 #endif -#define DEBUG(x) \ - do \ - { \ - printf(x); \ - gfxFlushBuffers(); \ - gfxSwapBuffers(); \ - } while(0); \ - do \ - { \ - hidScanInput(); \ - } while(!(hidKeysDown() & KEY_TOUCH)) \ +/* Macro for PRINT AT in the upper framebuffer +*/ +#define AT(x,y) (x)*8,upper.height-1-(y)*8 /* ---------------------------------------- STATIC DATA */ @@ -70,6 +63,7 @@ static const char *main_menu[]= "Save Joypad/Key State", "Load Joypad/Key State", "Help", + "Debug", "Exit 3DS81", "Cancel", NULL @@ -86,9 +80,30 @@ typedef enum MenuSaveMappings, MenuLoadMappings, MenuHelp, + MenuDebug, MenuExit } MenuOpt; +static const char *debug_menu[]= + { + "Enable Debug Output", + "Disable Debug Output", + "Disassembler", + "Memory Viewer", + "CPU Info", + "Cancel", + NULL + }; + +typedef enum +{ + DebugMenuEnableOutput, + DebugMenuDisableOutput, + DebugMenuDisassembler, + DebugMenuMemoryView, + DebugMenuCPUInfo, +} DebugMenuOpt; + /* ---------------------------------------- DISPLAY FUNCS */ @@ -314,6 +329,432 @@ static void MapJoypad(void) } +/* ---------------------------------------- DEBUG FUNCTIONS +*/ +static void DebugDisassembler(Z80 *cpu) +{ + static Z80Word addr = 0; + int done = FALSE; + Z80Word curr_addr = 0; + Framebuffer upper; + Framebuffer lower; + SoftKeyEvent ev; + int f; + + while(!done && aptMainLoop()) + { + FB_StartFrame(&upper, &lower); + + SK_DisplayKeyboard(); + + curr_addr = addr; + + FB_Clear(&upper, COL_WHITE); + + for(f=upper.height - 1; f > 0; f -= 8) + { + FB_printf(&upper, 0, f, COL_RED, COL_TRANSPARENT, + "%4.4x:", (unsigned)curr_addr); + FB_printf(&upper, 6*8, f, COL_BLACK, COL_TRANSPARENT, + "%s", Z80Disassemble(cpu, &curr_addr)); + } + + FB_EndFrame(); + + while(SK_GetBareEvent(&ev)) + { + if (ev.pressed) + { + switch(ev.key) + { + case SK_PAD_UP: + addr--; + break; + + case SK_PAD_DOWN: + addr++; + break; + + case SK_PAD_R: + addr = curr_addr; + break; + + case SK_PAD_L: + addr -= 20; + break; + + case SK_PAD_START: + done = TRUE; + break; + + case SK_PAD_B: + { + char addrstr[32] = {0}; + + if (GUI_Input("Enter address", addrstr, 31)) + { + addr = (Z80Word)strtoul(addrstr, NULL, 0); + } + + break; + } + + default: + break; + } + } + } + } +} + +static void DebugMemory(Z80 *cpu) +{ + static Z80Word addr = 0; + int done = FALSE; + Z80Word curr_addr = 0; + Framebuffer upper; + Framebuffer lower; + SoftKeyEvent ev; + int f; + int r; + int rows; + + while(!done && aptMainLoop()) + { + FB_StartFrame(&upper, &lower); + + SK_DisplayKeyboard(); + + curr_addr = addr; + + FB_Clear(&upper, COL_WHITE); + + rows = 0; + + for(f=upper.height - 1; f > 0; f -= 8) + { + FB_printf(&upper, 0, f, COL_RED, COL_TRANSPARENT, + "%4.4x:", (unsigned)curr_addr); + + for(r=0; r < 8; r++) + { + char c = '.'; + unsigned b; + + b = ZX81ReadDisassem(cpu, curr_addr); + + FB_printf(&upper, 6*8 + r*24, f, COL_BLACK, COL_TRANSPARENT, + "%2.2x", b); + + if (b >= 32 && b < 128) + { + c = b; + } + + FB_printf(&upper, 31*8 + r*8, f, COL_BLUE, COL_TRANSPARENT, + "%c", c); + + curr_addr++; + } + + rows++; + } + + FB_EndFrame(); + + while(SK_GetBareEvent(&ev)) + { + if (ev.pressed) + { + switch(ev.key) + { + case SK_PAD_UP: + addr--; + break; + + case SK_PAD_DOWN: + addr++; + break; + + case SK_PAD_R: + addr += rows * 8; + break; + + case SK_PAD_L: + addr -= rows * 8; + break; + + case SK_PAD_START: + done = TRUE; + break; + + case SK_PAD_B: + { + char addrstr[32] = {0}; + + if (GUI_Input("Enter address", addrstr, 31)) + { + addr = (Z80Word)strtoul(addrstr, NULL, 0); + } + + break; + } + + default: + break; + } + } + } + } +} + +static void DebugCPU(Z80 *cpu) +{ + static enum + { + AddrPC, + AddrSP, + AddrHL, + AddrBC, + AddrDE, + AddrIX, + AddrIY, + NumAddr + } addr_type = AddrPC; + + const char *addr_text[]= + { + "PC", "SP", "HL", "BC", "DE", "IX", "IY" + }; + + int done = FALSE; + Framebuffer upper; + Framebuffer lower; + SoftKeyEvent ev; + Z80Word addr; + Z80Word base; + int f; + + while(!done && aptMainLoop()) + { + FB_StartFrame(&upper, &lower); + + SK_DisplayKeyboard(); + + FB_Clear(&upper, COL_WHITE); + + addr = cpu->PC; + + FB_printf(&upper, AT(0,0), COL_BLACK, COL_TRANSPARENT, + "PC: %4.4x %s", (unsigned)cpu->PC, + Z80Disassemble(cpu, &addr)); + + FB_printf(&upper, AT(0,2), COL_BLACK, COL_TRANSPARENT, + "AF: %4.4x", (unsigned)cpu->AF.w); + + FB_printf(&upper, AT(0,3), COL_BLACK, COL_TRANSPARENT, + "BC: %4.4x", (unsigned)cpu->BC.w); + + FB_printf(&upper, AT(0,4), COL_BLACK, COL_TRANSPARENT, + "DE: %4.4x", (unsigned)cpu->DE.w); + + FB_printf(&upper, AT(0,5), COL_BLACK, COL_TRANSPARENT, + "HL: %4.4x", (unsigned)cpu->HL.w); + + FB_printf(&upper, AT(0,6), COL_BLACK, COL_TRANSPARENT, + "IX: %4.4x", (unsigned)cpu->IX.w); + + FB_printf(&upper, AT(0,7), COL_BLACK, COL_TRANSPARENT, + "IY: %4.4x", (unsigned)cpu->IX.w); + + FB_printf(&upper, AT(9,2), COL_BLACK, COL_TRANSPARENT, + "AF': %4.4x", (unsigned)cpu->AF_); + + FB_printf(&upper, AT(9,3), COL_BLACK, COL_TRANSPARENT, + "BC': %4.4x", (unsigned)cpu->BC_); + + FB_printf(&upper, AT(9,4), COL_BLACK, COL_TRANSPARENT, + "DE': %4.4x", (unsigned)cpu->DE_); + + FB_printf(&upper, AT(9,5), COL_BLACK, COL_TRANSPARENT, + "HL': %4.4x", (unsigned)cpu->HL_); + + switch(addr_type) + { + case AddrPC: + addr = cpu->PC; + break; + case AddrSP: + addr = cpu->SP; + break; + case AddrHL: + addr = cpu->HL.w; + break; + case AddrBC: + addr = cpu->BC.w; + break; + case AddrDE: + addr = cpu->DE.w; + break; + case AddrIX: + addr = cpu->IX.w; + break; + case AddrIY: + addr = cpu->IY.w; + break; + default: + break; + } + + base = addr; + addr -= 5; + + for(f=0; f < 11; f++) + { + if (addr == base) + { + FB_printf(&upper, AT(0,9+f), COL_BLACK, COL_LIGHTGREY, + "%s %4.4x: %2.2x", + addr_text[addr_type], + (unsigned)addr, + (unsigned)ZX81ReadDisassem(cpu, addr)); + } + else + { + FB_printf(&upper, AT(3,9+f), COL_BLACK, COL_TRANSPARENT, + "%4.4x: %2.2x", + (unsigned)addr, + (unsigned)ZX81ReadDisassem(cpu, addr)); + } + + addr++; + } + + FB_printf(&upper, AT(0,21), COL_BLACK, COL_TRANSPARENT, + "IFF1: %2.2x IFF2: %2.2x IM: %2.2x I:%2.2x R:%2.2x", + (unsigned)cpu->IFF1, + (unsigned)cpu->IFF2, + (unsigned)cpu->IM, + (unsigned)cpu->I, + (unsigned)cpu->R); + + FB_printf(&upper, AT(0,23), COL_BLACK, COL_TRANSPARENT, + "Flags: SZ5H3PNC"); + + FB_printf(&upper, AT(7,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Sign ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(8,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Zero ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(9,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Hidden5 ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(10,24), COL_BLACK, + cpu->AF.b.lo & eZ80_HalfCarry ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(11,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Hidden3 ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(12,24), COL_BLACK, + cpu->AF.b.lo & eZ80_PV ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(13,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Neg ? COL_GREEN : COL_RED, " "); + + FB_printf(&upper, AT(14,24), COL_BLACK, + cpu->AF.b.lo & eZ80_Carry ? COL_GREEN : COL_RED, " "); + + FB_EndFrame(); + + if (SK_KeyPressed(SK_PAD_X)) + { + Z80SingleStep(cpu); + } + + while(SK_GetBareEvent(&ev)) + { + if (ev.pressed) + { + switch(ev.key) + { + case SK_PAD_L: + if (addr_type == AddrPC) + { + addr_type = AddrIY; + } + else + { + addr_type--; + } + break; + + case SK_PAD_R: + addr_type = (addr_type + 1) % NumAddr; + break; + + case SK_PAD_START: + done = TRUE; + break; + + case SK_PAD_B: + Z80SingleStep(cpu); + break; + + default: + break; + } + } + } + } +} + +static void DebugMenu(Z80 *cpu) +{ + switch(GUI_Menu(debug_menu)) + { + case DebugMenuEnableOutput: + { + char host[32] = {0}; + char port[32] = {0}; + + debug_enabled = TRUE; + + GUI_Input("Enter host", host, 31); + GUI_Input("Enter port", port, 31); + + if (!host[0]) + { + strcpy(host, "192.168.0.77"); + } + + if (!port[0]) + { + strcpy(port, "12345"); + } + + DEBUG_SetAddress(host, port); + break; + } + + case DebugMenuDisableOutput: + debug_enabled = FALSE; + break; + + case DebugMenuDisassembler: + DebugDisassembler(cpu); + break; + + case DebugMenuMemoryView: + DebugMemory(cpu); + break; + + case DebugMenuCPUInfo: + DebugCPU(cpu); + break; + } +} + /* ---------------------------------------- MAIN */ int main(int argc, char *argv[]) @@ -417,6 +858,10 @@ int main(int argc, char *argv[]) ShowHelp(); break; + case MenuDebug: + DebugMenu(z80); + break; + case MenuExit: quit = TRUE; break; -- cgit v1.2.3