aboutsummaryrefslogtreecommitdiff
path: root/gdb/stubs/z80-stub.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/stubs/z80-stub.c')
-rw-r--r--gdb/stubs/z80-stub.c1355
1 files changed, 1355 insertions, 0 deletions
diff --git a/gdb/stubs/z80-stub.c b/gdb/stubs/z80-stub.c
new file mode 100644
index 0000000..0ec128f
--- /dev/null
+++ b/gdb/stubs/z80-stub.c
@@ -0,0 +1,1355 @@
+/* Debug stub for Z80.
+
+ Copyright (C) 2021 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+/* Usage:
+ 1. Copy this file to project directory
+ 2. Configure it commenting/uncommenting macros below or define DBG_CONFIGURED
+ and all required macros and then include this file to one of your C-source
+ files.
+ 3. Implement getDebugChar() and putDebugChar(), functions must not return
+ until data received or sent.
+ 4. Implement all optional functions used to toggle breakpoints/watchpoints,
+ if supported. Do not write fuctions to toggle software breakpoints if
+ you unsure (GDB will do itself).
+ 5. Implement serial port initialization routine called at program start.
+ 6. Add necessary debugger entry points to your program, for example:
+ .org 0x08 ;RST 8 handler
+ jp _debug_swbreak
+ ...
+ .org 0x66 ;NMI handler
+ jp _debug_nmi
+ ...
+ main_loop:
+ halt
+ call isDbgInterrupt
+ jr z,101$
+ ld hl, 2 ;EX_SIGINT
+ push hl
+ call _debug_exception
+ 101$:
+ ...
+ 7. Compile file using SDCC (supported ports are: z80, z180, z80n, gbz80 and
+ ez80_z80), do not use --peep-asm option. For example:
+ $ sdcc -mz80 --opt-code-size --max-allocs-per-node 50000 z80-stub.c
+*/
+/******************************************************************************\
+ Configuration
+\******************************************************************************/
+#ifndef DBG_CONFIGURED
+/* Uncomment this line, if stub size is critical for you */
+//#define DBG_MIN_SIZE
+
+/* Comment this line out if software breakpoints are unsupported.
+ If you have special function to toggle software breakpoints, then provide
+ here name of these function. Expected prototype:
+ int toggle_swbreak(int set, void *addr);
+ function must return 0 on success. */
+//#define DBG_SWBREAK toggle_swbreak
+#define DBG_SWBREAK
+
+/* Define if one of standard RST handlers is used as software
+ breakpoint entry point */
+//#define DBG_SWBREAK_RST 0x08
+
+/* if platform supports hardware breakpoints then define following macro
+ by name of function. Fuction must have next prototype:
+ int toggle_hwbreak(int set, void *addr);
+ function must return 0 on success. */
+//#define DBG_HWBREAK toggle_hwbreak
+
+/* if platform supports hardware watchpoints then define all or some of
+ following macros by names of functions. Fuctions prototypes:
+ int toggle_watch(int set, void *addr, size_t size); // memory write watch
+ int toggle_rwatch(int set, void *addr, size_t size); // memory read watch
+ int toggle_awatch(int set, void *addr, size_t size); // memory access watch
+ function must return 0 on success. */
+//#define DBG_WWATCH toggle_watch
+//#define DBG_RWATCH toggle_rwatch
+//#define DBG_AWATCH toggle_awatch
+
+/* Size of hardware breakpoint. Required to correct PC. */
+#define DBG_HWBREAK_SIZE 0
+
+/* Define following macro if you need custom memory read/write routine.
+ Function should return non-zero on success, and zero on failure
+ (for example, write to ROM area).
+ Useful with overlays (bank switching).
+ Do not forget to define:
+ _ovly_table - overlay table
+ _novlys - number of items in _ovly_table
+ or
+ _ovly_region_table - overlay regions table
+ _novly_regions - number of items in _ovly_region_table
+
+ _ovly_debug_prepare - function is called before overlay mapping
+ _ovly_debug_event - function is called after overlay mapping
+ */
+//#define DBG_MEMCPY memcpy
+
+/* define dedicated stack size if required */
+//#define DBG_STACK_SIZE 256
+
+/* max GDB packet size
+ should be much less that DBG_STACK_SIZE because it will be allocated on stack
+*/
+#define DBG_PACKET_SIZE 150
+
+/* Uncomment if required to use trampoline when resuming operation.
+ Useful with dedicated stack when stack pointer do not point to the stack or
+ stack is not writable */
+//#define DBG_USE_TRAMPOLINE
+
+/* Uncomment following macro to enable debug printing to debugger console */
+//#define DBG_PRINT
+
+#define DBG_NMI_EX EX_HWBREAK
+#define DBG_INT_EX EX_SIGINT
+
+/* Define following macro to statement, which will be exectuted after entering to
+ stub_main function. Statement should include semicolon. */
+//#define DBG_ENTER debug_enter();
+
+/* Define following macro to instruction(s), which will be execute before return
+ control to the program. It is useful when gdb-stub is placed in one of overlays.
+ This procedure must not change any register. On top of stack before invocation
+ will be return address of the program. */
+//#define DBG_RESUME jp _restore_bank
+
+/* Define following macro to the string containing memory map definition XML.
+ GDB will use it to select proper breakpoint type (HW or SW). */
+/*#define DBG_MEMORY_MAP "\
+<memory-map>\
+ <memory type=\"rom\" start=\"0x0000\" length=\"0x4000\"/>\
+<!-- <memory type=\"flash\" start=\"0x4000\" length=\"0x4000\">\
+ <property name=\"blocksize\">128</property>\
+ </memory> -->\
+ <memory type=\"ram\" start=\"0x8000\" length=\"0x8000\"/>\
+</memory-map>\
+"
+*/
+#endif /* DBG_CONFIGURED */
+/******************************************************************************\
+ Public Interface
+\******************************************************************************/
+
+/* Enter to debug mode from software or hardware breakpoint.
+ Assume address of next instruction after breakpoint call is on top of stack.
+ Do JP _debug_swbreak or JP _debug_hwbreak from RST handler, for example.
+ */
+void debug_swbreak (void);
+void debug_hwbreak (void);
+
+/* Jump to this function from NMI handler. Just replace RETN instruction by
+ JP _debug_nmi
+ Use if NMI detects request to enter to debug mode.
+ */
+void debug_nmi (void);
+
+/* Jump to this function from INT handler. Just replace EI+RETI instructions by
+ JP _debug_int
+ Use if INT detects request to enter to debug mode.
+ */
+void debug_int (void);
+
+#define EX_SWBREAK 0 /* sw breakpoint */
+#define EX_HWBREAK -1 /* hw breakpoint */
+#define EX_WWATCH -2 /* memory write watch */
+#define EX_RWATCH -3 /* memory read watch */
+#define EX_AWATCH -4 /* memory access watch */
+#define EX_SIGINT 2
+#define EX_SIGTRAP 5
+#define EX_SIGABRT 6
+#define EX_SIGBUS 10
+#define EX_SIGSEGV 11
+/* or any standard *nix signal value */
+
+/* Enter to debug mode (after receiving BREAK from GDB, for example)
+ * Assume:
+ * program PC in (SP+0)
+ * caught signal in (SP+2)
+ * program SP is SP+4
+ */
+void debug_exception (int ex);
+
+/* Prints to debugger console. */
+void debug_print(const char *str);
+/******************************************************************************\
+ Required functions
+\******************************************************************************/
+
+extern int getDebugChar (void);
+extern void putDebugChar (int ch);
+
+#ifdef DBG_SWBREAK
+#define DO_EXPAND(VAL) VAL ## 123456
+#define EXPAND(VAL) DO_EXPAND(VAL)
+
+#if EXPAND(DBG_SWBREAK) != 123456
+#define DBG_SWBREAK_PROC DBG_SWBREAK
+extern int DBG_SWBREAK(int set, void *addr);
+#endif
+
+#undef EXPAND
+#undef DO_EXPAND
+#endif /* DBG_SWBREAK */
+
+#ifdef DBG_HWBREAK
+extern int DBG_HWBREAK(int set, void *addr);
+#endif
+
+#ifdef DBG_MEMCPY
+extern void* DBG_MEMCPY (void *dest, const void *src, unsigned n);
+#endif
+
+#ifdef DBG_WWATCH
+extern int DBG_WWATCH(int set, void *addr, unsigned size);
+#endif
+
+#ifdef DBG_RWATCH
+extern int DBG_RWATCH(int set, void *addr, unsigned size);
+#endif
+
+#ifdef DBG_AWATCH
+extern int DBG_AWATCH(int set, void *addr, unsigned size);
+#endif
+
+/******************************************************************************\
+ IMPLEMENTATION
+\******************************************************************************/
+
+#include <string.h>
+
+#ifndef NULL
+# define NULL (void*)0
+#endif
+
+typedef unsigned char byte;
+typedef unsigned short word;
+
+/* CPU state */
+#ifdef __SDCC_ez80_adl
+# define REG_SIZE 3
+#else
+# define REG_SIZE 2
+#endif /* __SDCC_ez80_adl */
+
+#define R_AF (0*REG_SIZE)
+#define R_BC (1*REG_SIZE)
+#define R_DE (2*REG_SIZE)
+#define R_HL (3*REG_SIZE)
+#define R_SP (4*REG_SIZE)
+#define R_PC (5*REG_SIZE)
+
+#ifndef __SDCC_gbz80
+#define R_IX (6*REG_SIZE)
+#define R_IY (7*REG_SIZE)
+#define R_AF_ (8*REG_SIZE)
+#define R_BC_ (9*REG_SIZE)
+#define R_DE_ (10*REG_SIZE)
+#define R_HL_ (11*REG_SIZE)
+#define R_IR (12*REG_SIZE)
+
+#ifdef __SDCC_ez80_adl
+#define R_SPS (13*REG_SIZE)
+#define NUMREGBYTES (14*REG_SIZE)
+#else
+#define NUMREGBYTES (13*REG_SIZE)
+#endif /* __SDCC_ez80_adl */
+#else
+#define NUMREGBYTES (6*REG_SIZE)
+#define FASTCALL
+#endif /*__SDCC_gbz80 */
+static byte state[NUMREGBYTES];
+
+#if DBG_PACKET_SIZE < (NUMREGBYTES*2+5)
+#error "Too small DBG_PACKET_SIZE"
+#endif
+
+#ifndef FASTCALL
+#define FASTCALL __z88dk_fastcall
+#endif
+
+/* dedicated stack */
+#ifdef DBG_STACK_SIZE
+
+#define LOAD_SP ld sp, #_stack + DBG_STACK_SIZE
+
+static char stack[DBG_STACK_SIZE];
+
+#else
+
+#undef DBG_USE_TRAMPOLINE
+#define LOAD_SP
+
+#endif
+
+#ifndef DBG_ENTER
+#define DBG_ENTER
+#endif
+
+#ifndef DBG_RESUME
+#define DBG_RESUME ret
+#endif
+
+static signed char sigval;
+
+static void stub_main (int sigval, int pc_adj);
+static char high_hex (byte v) FASTCALL;
+static char low_hex (byte v) FASTCALL;
+static char put_packet_info (const char *buffer) FASTCALL;
+static void save_cpu_state (void);
+static void rest_cpu_state (void);
+
+/******************************************************************************/
+#ifdef DBG_SWBREAK
+#ifdef DBG_SWBREAK_RST
+#define DBG_SWBREAK_SIZE 1
+#else
+#define DBG_SWBREAK_SIZE 3
+#endif
+void
+debug_swbreak (void) __naked
+{
+ __asm
+ ld (#_state + R_SP), sp
+ LOAD_SP
+ call _save_cpu_state
+ ld hl, #-DBG_SWBREAK_SIZE
+ push hl
+ ld hl, #EX_SWBREAK
+ push hl
+ call _stub_main
+ .globl _break_handler
+#ifdef DBG_SWBREAK_RST
+_break_handler = DBG_SWBREAK_RST
+#else
+_break_handler = _debug_swbreak
+#endif
+ __endasm;
+}
+#endif /* DBG_SWBREAK */
+/******************************************************************************/
+#ifdef DBG_HWBREAK
+#ifndef DBG_HWBREAK_SIZE
+#define DBG_HWBREAK_SIZE 0
+#endif /* DBG_HWBREAK_SIZE */
+void
+debug_hwbreak (void) __naked
+{
+ __asm
+ ld (#_state + R_SP), sp
+ LOAD_SP
+ call _save_cpu_state
+ ld hl, #-DBG_HWBREAK_SIZE
+ push hl
+ ld hl, #EX_HWBREAK
+ push hl
+ call _stub_main
+ __endasm;
+}
+#endif /* DBG_HWBREAK_SET */
+/******************************************************************************/
+void
+debug_exception (int ex) __naked
+{
+ __asm
+ ld (#_state + R_SP), sp
+ LOAD_SP
+ call _save_cpu_state
+ ld hl, #0
+ push hl
+#ifdef __SDCC_gbz80
+ ld hl, #_state + R_SP
+ ld a, (hl+)
+ ld h, (hl)
+ ld l, a
+#else
+ ld hl, (#_state + R_SP)
+#endif
+ inc hl
+ inc hl
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ push de
+ call _stub_main
+ __endasm;
+ (void)ex;
+}
+/******************************************************************************/
+#ifndef __SDCC_gbz80
+void
+debug_nmi(void) __naked
+{
+ __asm
+ ld (#_state + R_SP), sp
+ LOAD_SP
+ call _save_cpu_state
+ ld hl, #0 ;pc_adj
+ push hl
+ ld hl, #DBG_NMI_EX
+ push hl
+ ld hl, #_stub_main
+ push hl
+ push hl
+ retn
+ __endasm;
+}
+#endif
+/******************************************************************************/
+void
+debug_int(void) __naked
+{
+ __asm
+ ld (#_state + R_SP), sp
+ LOAD_SP
+ call _save_cpu_state
+ ld hl, #0 ;pc_adj
+ push hl
+ ld hl, #DBG_INT_EX
+ push hl
+ ld hl, #_stub_main
+ push hl
+ push hl
+ ei
+ reti
+ __endasm;
+}
+/******************************************************************************/
+#ifdef DBG_PRINT
+void
+debug_print(const char *str)
+{
+ putDebugChar ('$');
+ putDebugChar ('O');
+ char csum = 'O';
+ for (; *str != '\0'; )
+ {
+ char c = high_hex (*str);
+ csum += c;
+ putDebugChar (c);
+ c = low_hex (*str++);
+ csum += c;
+ putDebugChar (c);
+ }
+ putDebugChar ('#');
+ putDebugChar (high_hex (csum));
+ putDebugChar (low_hex (csum));
+}
+#endif /* DBG_PRINT */
+/******************************************************************************/
+static void store_pc_sp (int pc_adj) FASTCALL;
+#define get_reg_value(mem) (*(void* const*)(mem))
+#define set_reg_value(mem,val) do { (*(void**)(mem) = (val)); } while (0)
+static char* byte2hex(char *buf, byte val);
+static int hex2int (const char **buf) FASTCALL;
+static char* int2hex (char *buf, int v);
+static void get_packet (char *buffer);
+static void put_packet (const char *buffer);
+static char process (char *buffer) FASTCALL;
+static void rest_cpu_state (void);
+
+static void
+stub_main (int ex, int pc_adj)
+{
+ char buffer[DBG_PACKET_SIZE+1];
+ sigval = (signed char)ex;
+ store_pc_sp (pc_adj);
+
+ DBG_ENTER
+
+ /* after starting gdb_stub must always return stop reason */
+ *buffer = '?';
+ for (; process (buffer);)
+ {
+ put_packet (buffer);
+ get_packet (buffer);
+ }
+ put_packet (buffer);
+ rest_cpu_state ();
+}
+
+static void
+get_packet (char *buffer)
+{
+ byte csum;
+ char ch;
+ char *p;
+ byte esc;
+#if DBG_PACKET_SIZE <= 256
+ byte count; /* it is OK to use up to 256 here */
+#else
+ unsigned count;
+#endif
+ for (;; putDebugChar ('-'))
+ {
+ /* wait for packet start character */
+ while (getDebugChar () != '$');
+retry:
+ csum = 0;
+ esc = 0;
+ p = buffer;
+ count = DBG_PACKET_SIZE;
+ do
+ {
+ ch = getDebugChar ();
+ switch (ch)
+ {
+ case '$':
+ goto retry;
+ case '#':
+ goto finish;
+ case '}':
+ esc = 0x20;
+ break;
+ default:
+ *p++ = ch ^ esc;
+ esc = 0;
+ --count;
+ }
+ csum += ch;
+ }
+ while (count != 0);
+finish:
+ *p = '\0';
+ if (ch != '#') /* packet is too large */
+ continue;
+ ch = getDebugChar ();
+ if (ch != high_hex (csum))
+ continue;
+ ch = getDebugChar ();
+ if (ch != low_hex (csum))
+ continue;
+ break;
+ }
+ putDebugChar ('+');
+}
+
+static void
+put_packet (const char *buffer)
+{
+ /* $<packet info>#<checksum>. */
+ for (;;)
+ {
+ putDebugChar ('$');
+ char checksum = put_packet_info (buffer);
+ putDebugChar ('#');
+ putDebugChar (high_hex(checksum));
+ putDebugChar (low_hex(checksum));
+ for (;;)
+ {
+ char c = getDebugChar ();
+ switch (c)
+ {
+ case '+': return;
+ case '-': break;
+ default:
+ putDebugChar (c);
+ continue;
+ }
+ break;
+ }
+ }
+}
+
+static char
+put_packet_info (const char *src) FASTCALL
+{
+ char ch;
+ char checksum = 0;
+ for (;;)
+ {
+ ch = *src++;
+ if (ch == '\0')
+ break;
+ if (ch == '}' || ch == '*' || ch == '#' || ch == '$')
+ {
+ /* escape special characters */
+ putDebugChar ('}');
+ checksum += '}';
+ ch ^= 0x20;
+ }
+ putDebugChar (ch);
+ checksum += ch;
+ }
+ return checksum;
+}
+
+static void
+store_pc_sp (int pc_adj) FASTCALL
+{
+ byte *sp = get_reg_value (&state[R_SP]);
+ byte *pc = get_reg_value (sp);
+ pc += pc_adj;
+ set_reg_value (&state[R_PC], pc);
+ set_reg_value (&state[R_SP], sp + REG_SIZE);
+}
+
+static char *mem2hex (char *buf, const byte *mem, unsigned bytes);
+static char *hex2mem (byte *mem, const char *buf, unsigned bytes);
+
+/* Command processors. Takes pointer to buffer (begins from command symbol),
+ modifies buffer, returns: -1 - empty response (ignore), 0 - success,
+ positive: error code. */
+
+#ifdef DBG_MIN_SIZE
+static signed char
+process_question (char *p) FASTCALL
+{
+ signed char sig;
+ *p++ = 'S';
+ sig = sigval;
+ if (sig <= 0)
+ sig = EX_SIGTRAP;
+ p = byte2hex (p, (byte)sig);
+ *p = '\0';
+ return 0;
+}
+#else /* DBG_MIN_SIZE */
+static char *format_reg_value (char *p, unsigned reg_num, const byte *value);
+
+static signed char
+process_question (char *p) FASTCALL
+{
+ signed char sig;
+ *p++ = 'T';
+ sig = sigval;
+ if (sig <= 0)
+ sig = EX_SIGTRAP;
+ p = byte2hex (p, (byte)sig);
+ p = format_reg_value(p, R_AF/REG_SIZE, &state[R_AF]);
+ p = format_reg_value(p, R_SP/REG_SIZE, &state[R_SP]);
+ p = format_reg_value(p, R_PC/REG_SIZE, &state[R_PC]);
+#if defined(DBG_SWBREAK_PROC) || defined(DBG_HWBREAK) || defined(DBG_WWATCH) || defined(DBG_RWATCH) || defined(DBG_AWATCH)
+ const char *reason;
+ unsigned addr = 0;
+ switch (sigval)
+ {
+#ifdef DBG_SWBREAK_PROC
+ case EX_SWBREAK:
+ reason = "swbreak";
+ break;
+#endif
+#ifdef DBG_HWBREAK
+ case EX_HWBREAK:
+ reason = "hwbreak";
+ break;
+#endif
+#ifdef DBG_WWATCH
+ case EX_WWATCH:
+ reason = "watch";
+ addr = 1;
+ break;
+#endif
+#ifdef DBG_RWATCH
+ case EX_RWATCH:
+ reason = "rwatch";
+ addr = 1;
+ break;
+#endif
+#ifdef DBG_AWATCH
+ case EX_AWATCH:
+ reason = "awatch";
+ addr = 1;
+ break;
+#endif
+ default:
+ goto finish;
+ }
+ while ((*p++ = *reason++))
+ ;
+ --p;
+ *p++ = ':';
+ if (addr != 0)
+ p = int2hex(p, addr);
+ *p++ = ';';
+finish:
+#endif /* DBG_HWBREAK, DBG_WWATCH, DBG_RWATCH, DBG_AWATCH */
+ *p++ = '\0';
+ return 0;
+}
+#endif /* DBG_MINSIZE */
+
+#define STRING2(x) #x
+#define STRING1(x) STRING2(x)
+#define STRING(x) STRING1(x)
+#ifdef DBG_MEMORY_MAP
+static void read_memory_map (char *buffer, unsigned offset, unsigned length);
+#endif
+
+static signed char
+process_q (char *buffer) FASTCALL
+{
+ char *p;
+ if (memcmp (buffer + 1, "Supported", 9) == 0)
+ {
+ memcpy (buffer, "PacketSize=", 11);
+ p = int2hex (&buffer[11], DBG_PACKET_SIZE);
+#ifndef DBG_MIN_SIZE
+#ifdef DBG_SWBREAK_PROC
+ memcpy (p, ";swbreak+", 9);
+ p += 9;
+#endif
+#ifdef DBG_HWBREAK
+ memcpy (p, ";hwbreak+", 9);
+ p += 9;
+#endif
+#endif /* DBG_MIN_SIZE */
+
+#ifdef DBG_MEMORY_MAP
+ memcpy (p, ";qXfer:memory-map:read+", 23);
+ p += 23;
+#endif
+ *p = '\0';
+ return 0;
+ }
+#ifdef DBG_MEMORY_MAP
+ if (memcmp (buffer + 1, "Xfer:memory-map:read:", 21) == 0)
+ {
+ p = strchr (buffer + 1 + 21, ':');
+ if (p == NULL)
+ return 1;
+ ++p;
+ unsigned offset = hex2int (&p);
+ if (*p++ != ',')
+ return 2;
+ unsigned length = hex2int (&p);
+ if (length == 0)
+ return 3;
+ if (length > DBG_PACKET_SIZE)
+ return 4;
+ read_memory_map (buffer, offset, length);
+ return 0;
+ }
+#endif
+#ifndef DBG_MIN_SIZE
+ if (memcmp (&buffer[1], "Attached", 9) == 0)
+ {
+ /* Just report that GDB attached to existing process
+ if it is not applicable for you, then send patches */
+ memcpy(buffer, "1", 2);
+ return 0;
+ }
+#endif /* DBG_MIN_SIZE */
+ *buffer = '\0';
+ return -1;
+}
+
+static signed char
+process_g (char *buffer) FASTCALL
+{
+ mem2hex (buffer, state, NUMREGBYTES);
+ return 0;
+}
+
+static signed char
+process_G (char *buffer) FASTCALL
+{
+ hex2mem (state, &buffer[1], NUMREGBYTES);
+ /* OK response */
+ *buffer = '\0';
+ return 0;
+}
+
+static signed char
+process_m (char *buffer) FASTCALL
+{/* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ char *p = &buffer[1];
+ byte *addr = (void*)hex2int(&p);
+ if (*p++ != ',')
+ return 1;
+ unsigned len = (unsigned)hex2int(&p);
+ if (len == 0)
+ return 2;
+ if (len > DBG_PACKET_SIZE/2)
+ return 3;
+ p = buffer;
+#ifdef DBG_MEMCPY
+ do
+ {
+ byte tmp[16];
+ unsigned tlen = sizeof(tmp);
+ if (tlen > len)
+ tlen = len;
+ if (!DBG_MEMCPY(tmp, addr, tlen))
+ return 4;
+ p = mem2hex (p, tmp, tlen);
+ addr += tlen;
+ len -= tlen;
+ }
+ while (len);
+#else
+ p = mem2hex (p, addr, len);
+#endif
+ return 0;
+}
+
+static signed char
+process_M (char *buffer) FASTCALL
+{/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+ char *p = &buffer[1];
+ byte *addr = (void*)hex2int(&p);
+ if (*p != ',')
+ return 1;
+ ++p;
+ unsigned len = (unsigned)hex2int(&p);
+ if (*p++ != ':')
+ return 2;
+ if (len == 0)
+ goto end;
+ if (len*2 + (p - buffer) > DBG_PACKET_SIZE)
+ return 3;
+#ifdef DBG_MEMCPY
+ do
+ {
+ byte tmp[16];
+ unsigned tlen = sizeof(tmp);
+ if (tlen > len)
+ tlen = len;
+ p = hex2mem (tmp, p, tlen);
+ if (!DBG_MEMCPY(addr, tmp, tlen))
+ return 4;
+ addr += tlen;
+ len -= tlen;
+ }
+ while (len);
+#else
+ hex2mem (addr, p, len);
+#endif
+end:
+ /* OK response */
+ *buffer = '\0';
+ return 0;
+}
+
+#ifndef DBG_MIN_SIZE
+static signed char
+process_X (char *buffer) FASTCALL
+{/* XAA..AA,LLLL: Write LLLL binary bytes at address AA.AA return OK */
+ char *p = &buffer[1];
+ byte *addr = (void*)hex2int(&p);
+ if (*p != ',')
+ return 1;
+ ++p;
+ unsigned len = (unsigned)hex2int(&p);
+ if (*p++ != ':')
+ return 2;
+ if (len == 0)
+ goto end;
+ if (len + (p - buffer) > DBG_PACKET_SIZE)
+ return 3;
+#ifdef DBG_MEMCPY
+ if (!DBG_MEMCPY(addr, p, len))
+ return 4;
+#else
+ memcpy (addr, p, len);
+#endif
+end:
+ /* OK response */
+ *buffer = '\0';
+ return 0;
+}
+#else /* DBG_MIN_SIZE */
+static signed char
+process_X (char *buffer) FASTCALL
+{
+ (void)buffer;
+ return -1;
+}
+#endif /* DBG_MIN_SIZE */
+
+static signed char
+process_c (char *buffer) FASTCALL
+{/* 'cAAAA' - Continue at address AAAA(optional) */
+ const char *p = &buffer[1];
+ if (*p != '\0')
+ {
+ void *addr = (void*)hex2int(&p);
+ set_reg_value (&state[R_PC], addr);
+ }
+ rest_cpu_state ();
+ return 0;
+}
+
+static signed char
+process_D (char *buffer) FASTCALL
+{/* 'D' - detach the program: continue execution */
+ *buffer = '\0';
+ return -2;
+}
+
+static signed char
+process_k (char *buffer) FASTCALL
+{/* 'k' - Kill the program */
+ set_reg_value (&state[R_PC], 0);
+ rest_cpu_state ();
+ (void)buffer;
+ return 0;
+}
+
+static signed char
+process_v (char *buffer) FASTCALL
+{
+#ifndef DBG_MIN_SIZE
+ if (memcmp (&buffer[1], "Cont", 4) == 0)
+ {
+ if (buffer[5] == '?')
+ {
+ /* result response will be "vCont;c;C"; C action must be
+ supported too, because GDB reguires at lease both of them */
+ memcpy (&buffer[5], ";c;C", 5);
+ return 0;
+ }
+ buffer[0] = '\0';
+ if (buffer[5] == ';' && (buffer[6] == 'c' || buffer[6] == 'C'))
+ return -2; /* resume execution */
+ return 1;
+ }
+#endif /* DBG_MIN_SIZE */
+ return -1;
+}
+
+static signed char
+process_zZ (char *buffer) FASTCALL
+{ /* insert/remove breakpoint */
+#if defined(DBG_SWBREAK_PROC) || defined(DBG_HWBREAK) || \
+ defined(DBG_WWATCH) || defined(DBG_RWATCH) || defined(DBG_AWATCH)
+ const byte set = (*buffer == 'Z');
+ const char *p = &buffer[3];
+ void *addr = (void*)hex2int(&p);
+ if (*p != ',')
+ return 1;
+ p++;
+ int kind = hex2int(&p);
+ *buffer = '\0';
+ switch (buffer[1])
+ {
+#ifdef DBG_SWBREAK_PROC
+ case '0': /* sw break */
+ return DBG_SWBREAK_PROC(set, addr);
+#endif
+#ifdef DBG_HWBREAK
+ case '1': /* hw break */
+ return DBG_HWBREAK(set, addr);
+#endif
+#ifdef DBG_WWATCH
+ case '2': /* write watch */
+ return DBG_WWATCH(set, addr, kind);
+#endif
+#ifdef DBG_RWATCH
+ case '3': /* read watch */
+ return DBG_RWATCH(set, addr, kind);
+#endif
+#ifdef DBG_AWATCH
+ case '4': /* access watch */
+ return DBG_AWATCH(set, addr, kind);
+#endif
+ default:; /* not supported */
+ }
+#endif
+ (void)buffer;
+ return -1;
+}
+
+static signed char
+do_process (char *buffer) FASTCALL
+{
+ switch (*buffer)
+ {
+ case '?': return process_question (buffer);
+ case 'G': return process_G (buffer);
+ case 'k': return process_k (buffer);
+ case 'M': return process_M (buffer);
+ case 'X': return process_X (buffer);
+ case 'Z': return process_zZ (buffer);
+ case 'c': return process_c (buffer);
+ case 'D': return process_D (buffer);
+ case 'g': return process_g (buffer);
+ case 'm': return process_m (buffer);
+ case 'q': return process_q (buffer);
+ case 'v': return process_v (buffer);
+ case 'z': return process_zZ (buffer);
+ default: return -1; /* empty response */
+ }
+}
+
+static char
+process (char *buffer) FASTCALL
+{
+ signed char err = do_process (buffer);
+ char *p = buffer;
+ char ret = 1;
+ if (err == -2)
+ {
+ ret = 0;
+ err = 0;
+ }
+ if (err > 0)
+ {
+ *p++ = 'E';
+ p = byte2hex (p, err);
+ *p = '\0';
+ }
+ else if (err < 0)
+ {
+ *p = '\0';
+ }
+ else if (*p == '\0')
+ memcpy(p, "OK", 3);
+ return ret;
+}
+
+static char *
+byte2hex (char *p, byte v)
+{
+ *p++ = high_hex (v);
+ *p++ = low_hex (v);
+ return p;
+}
+
+static signed char
+hex2val (unsigned char hex) FASTCALL
+{
+ if (hex <= '9')
+ return hex - '0';
+ hex &= 0xdf; /* make uppercase */
+ hex -= 'A' - 10;
+ return (hex >= 10 && hex < 16) ? hex : -1;
+}
+
+static int
+hex2byte (const char *p) FASTCALL
+{
+ signed char h = hex2val (p[0]);
+ signed char l = hex2val (p[1]);
+ if (h < 0 || l < 0)
+ return -1;
+ return (byte)((byte)h << 4) | (byte)l;
+}
+
+static int
+hex2int (const char **buf) FASTCALL
+{
+ word r = 0;
+ for (;; (*buf)++)
+ {
+ signed char a = hex2val(**buf);
+ if (a < 0)
+ break;
+ r <<= 4;
+ r += (byte)a;
+ }
+ return (int)r;
+}
+
+static char *
+int2hex (char *buf, int v)
+{
+ buf = byte2hex(buf, (word)v >> 8);
+ return byte2hex(buf, (byte)v);
+}
+
+static char
+high_hex (byte v) FASTCALL
+{
+ return low_hex(v >> 4);
+}
+
+static char
+low_hex (byte v) FASTCALL
+{
+/*
+ __asm
+ ld a, l
+ and a, #0x0f
+ add a, #0x90
+ daa
+ adc a, #0x40
+ daa
+ ld l, a
+ __endasm;
+ (void)v;
+*/
+ v &= 0x0f;
+ v += '0';
+ if (v < '9'+1)
+ return v;
+ return v + 'a' - '0' - 10;
+}
+
+/* convert the memory, pointed to by mem into hex, placing result in buf */
+/* return a pointer to the last char put in buf (null) */
+static char *
+mem2hex (char *buf, const byte *mem, unsigned bytes)
+{
+ char *d = buf;
+ if (bytes != 0)
+ {
+ do
+ {
+ d = byte2hex (d, *mem++);
+ }
+ while (--bytes);
+ }
+ *d = 0;
+ return d;
+}
+
+/* convert the hex array pointed to by buf into binary, to be placed in mem
+ return a pointer to the character after the last byte written */
+
+static const char *
+hex2mem (byte *mem, const char *buf, unsigned bytes)
+{
+ if (bytes != 0)
+ {
+ do
+ {
+ *mem++ = hex2byte (buf);
+ buf += 2;
+ }
+ while (--bytes);
+ }
+ return buf;
+}
+
+#ifdef DBG_MEMORY_MAP
+static void
+read_memory_map (char *buffer, unsigned offset, unsigned length)
+{
+ const char *map = DBG_MEMORY_MAP;
+ const unsigned map_sz = strlen(map);
+ if (offset >= map_sz)
+ {
+ buffer[0] = 'l';
+ buffer[1] = '\0';
+ return;
+ }
+ if (offset + length > map_sz)
+ length = map_sz - offset;
+ buffer[0] = 'm';
+ memcpy (&buffer[1], &map[offset], length);
+ buffer[1+length] = '\0';
+}
+#endif
+
+/* write string like " nn:0123" and return pointer after it */
+#ifndef DBG_MIN_SIZE
+static char *
+format_reg_value (char *p, unsigned reg_num, const byte *value)
+{
+ char *d = p;
+ unsigned char i;
+ d = byte2hex(d, reg_num);
+ *d++ = ':';
+ value += REG_SIZE;
+ i = REG_SIZE;
+ do
+ {
+ d = byte2hex(d, *--value);
+ }
+ while (--i != 0);
+ *d++ = ';';
+ return d;
+}
+#endif /* DBG_MIN_SIZE */
+
+#ifdef __SDCC_gbz80
+/* saves all state.except PC and SP */
+static void
+save_cpu_state() __naked
+{
+ __asm
+ push af
+ ld a, l
+ ld (#_state + R_HL + 0), a
+ ld a, h
+ ld (#_state + R_HL + 1), a
+ ld hl, #_state + R_HL - 1
+ ld (hl), d
+ dec hl
+ ld (hl), e
+ dec hl
+ ld (hl), b
+ dec hl
+ ld (hl), c
+ dec hl
+ pop bc
+ ld (hl), b
+ dec hl
+ ld (hl), c
+ ret
+ __endasm;
+}
+
+/* restore CPU state and continue execution */
+static void
+rest_cpu_state() __naked
+{
+ __asm
+;restore SP
+ ld a, (#_state + R_SP + 0)
+ ld l,a
+ ld a, (#_state + R_SP + 1)
+ ld h,a
+ ld sp, hl
+;push PC value as return address
+ ld a, (#_state + R_PC + 0)
+ ld l, a
+ ld a, (#_state + R_PC + 1)
+ ld h, a
+ push hl
+;restore registers
+ ld hl, #_state + R_AF
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ inc hl
+ push bc
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ inc hl
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+ pop af
+ ret
+ __endasm;
+}
+#else
+/* saves all state.except PC and SP */
+static void
+save_cpu_state() __naked
+{
+ __asm
+ ld (#_state + R_HL), hl
+ ld (#_state + R_DE), de
+ ld (#_state + R_BC), bc
+ push af
+ pop hl
+ ld (#_state + R_AF), hl
+ ld a, r ;R is increased by 7 or by 8 if called via RST
+ ld l, a
+ sub a, #7
+ xor a, l
+ and a, #0x7f
+ xor a, l
+#ifdef __SDCC_ez80_adl
+ ld hl, i
+ ex de, hl
+ ld hl, #_state + R_IR
+ ld (hl), a
+ inc hl
+ ld (hl), e
+ inc hl
+ ld (hl), d
+ ld a, MB
+ ld (#_state + R_AF+2), a
+#else
+ ld l, a
+ ld a, i
+ ld h, a
+ ld (#_state + R_IR), hl
+#endif /* __SDCC_ez80_adl */
+ ld (#_state + R_IX), ix
+ ld (#_state + R_IY), iy
+ ex af, af' ;'
+ exx
+ ld (#_state + R_HL_), hl
+ ld (#_state + R_DE_), de
+ ld (#_state + R_BC_), bc
+ push af
+ pop hl
+ ld (#_state + R_AF_), hl
+ ret
+ __endasm;
+}
+
+/* restore CPU state and continue execution */
+static void
+rest_cpu_state() __naked
+{
+ __asm
+#ifdef DBG_USE_TRAMPOLINE
+ ld sp, _stack + DBG_STACK_SIZE
+ ld hl, (#_state + R_PC)
+ push hl /* resume address */
+#ifdef __SDCC_ez80_adl
+ ld hl, 0xc30000 ; use 0xc34000 for jp.s
+#else
+ ld hl, 0xc300
+#endif
+ push hl /* JP opcode */
+#endif /* DBG_USE_TRAMPOLINE */
+ ld hl, (#_state + R_AF_)
+ push hl
+ pop af
+ ld bc, (#_state + R_BC_)
+ ld de, (#_state + R_DE_)
+ ld hl, (#_state + R_HL_)
+ exx
+ ex af, af' ;'
+ ld iy, (#_state + R_IY)
+ ld ix, (#_state + R_IX)
+#ifdef __SDCC_ez80_adl
+ ld a, (#_state + R_AF + 2)
+ ld MB, a
+ ld hl, (#_state + R_IR + 1) ;I register
+ ld i, hl
+ ld a, (#_state + R_IR + 0) ; R register
+ ld l, a
+#else
+ ld hl, (#_state + R_IR)
+ ld a, h
+ ld i, a
+ ld a, l
+#endif /* __SDCC_ez80_adl */
+ sub a, #10 ;number of M1 cycles after ld r,a
+ xor a, l
+ and a, #0x7f
+ xor a, l
+ ld r, a
+ ld de, (#_state + R_DE)
+ ld bc, (#_state + R_BC)
+ ld hl, (#_state + R_AF)
+ push hl
+ pop af
+ ld sp, (#_state + R_SP)
+#ifndef DBG_USE_TRAMPOLINE
+ ld hl, (#_state + R_PC)
+ push hl
+ ld hl, (#_state + R_HL)
+ DBG_RESUME
+#else
+ ld hl, (#_state + R_HL)
+#ifdef __SDCC_ez80_adl
+ jp #_stack + DBG_STACK_SIZE - 4
+#else
+ jp #_stack + DBG_STACK_SIZE - 3
+#endif
+#endif /* DBG_USE_TRAMPOLINE */
+ __endasm;
+}
+#endif /* __SDCC_gbz80 */