summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2023-04-23 09:42:21 +0800
committerIan C <ianc@noddybox.co.uk>2023-04-23 09:42:21 +0800
commitbb7b6e8532ef3282fdd15ec133deff919cd7e7c3 (patch)
treee576c7b35fabe8d903af8ee963ffa42dd2330619 /source
parent1d1e0acb5b4494f7c1b041e24e5de4f0614a150b (diff)
Initial work to add debug menu from 3dsspec
Diffstat (limited to 'source')
-rw-r--r--source/debug.c162
-rw-r--r--source/gui.c153
-rw-r--r--source/keyboard.c6
-rw-r--r--source/main.c467
4 files changed, 777 insertions, 11 deletions
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 <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 <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ Provides an interface for debug
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <3ds.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+
+#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;