diff options
Diffstat (limited to 'gdb/stubs/z80-stub.c')
-rw-r--r-- | gdb/stubs/z80-stub.c | 1355 |
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 */ |