--- /dev/null
+/* 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 */
--- /dev/null
+/* Target-dependent code for the Z80.
+
+ Copyright (C) 1986-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/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "dis-asm.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "symfile.h"
+
+#include "z80-tdep.h"
+#include "features/z80.c"
+
+/* You need to define __gdb_break_handler symbol pointing to the breakpoint
+ handler. The value of the symbol will be used to determine the instruction
+ for software breakpoint. If __gdb_break_handler points to one of standard
+ RST addresses (0x00, 0x08, 0x10,... 0x38) then RST __gdb_break_handler
+ instruction will be used, else CALL __gdb_break_handler
+
+;breakpoint handler
+ .globl __gdb_break_handler
+ .org 8
+__gdb_break_handler:
+ jp _debug_swbreak
+
+*/
+
+/* Meaning of terms "previous" and "next":
+ previous frame - frame of callee, which is called by current function
+ current frame - frame of current function which has called callee
+ next frame - frame of caller, which has called current function
+*/
+
+struct gdbarch_tdep
+{
+ /* Number of bytes used for address:
+ 2 bytes for all Z80 family
+ 3 bytes for eZ80 CPUs operating in ADL mode */
+ int addr_length;
+
+ /* Type for void. */
+ struct type *void_type;
+ /* Type for a function returning void. */
+ struct type *func_void_type;
+ /* Type for a pointer to a function. Used for the type of PC. */
+ struct type *pc_type;
+};
+
+/* At any time stack frame contains following parts:
+ [<current PC>]
+ [<temporaries, y bytes>]
+ [<local variables, x bytes>
+ <next frame FP>]
+ [<saved state (critical or interrupt functions), 2 or 10 bytes>]
+ In simplest case <next PC> is pointer to the call instruction
+ (or call __call_hl). There are more difficult cases: interrupt handler or
+ push/ret and jp; but they are untrackable.
+*/
+
+struct z80_unwind_cache
+{
+ /* The previous frame's inner most stack address (SP after call executed),
+ it is current frame's frame_id. */
+ CORE_ADDR prev_sp;
+
+ /* Size of the frame, prev_sp + size = next_frame.prev_sp */
+ ULONGEST size;
+
+ /* size of saved state (including frame pointer and return address),
+ assume: prev_sp + size = IX + state_size */
+ ULONGEST state_size;
+
+ struct
+ {
+ int called:1; /* there is return address on stack */
+ int load_args:1; /* prologues loads args using POPs */
+ int fp_sdcc:1; /* prologue saves and adjusts frame pointer IX */
+ int interrupt:1; /* __interrupt handler */
+ int critical:1; /* __critical function */
+ } prologue_type;
+
+ /* Table indicating the location of each and every register. */
+ struct trad_frame_saved_reg *saved_regs;
+};
+
+enum instruction_type
+{
+ insn_default,
+ insn_z80,
+ insn_adl,
+ insn_z80_ed,
+ insn_adl_ed,
+ insn_z80_ddfd,
+ insn_adl_ddfd,
+ insn_djnz_d,
+ insn_jr_d,
+ insn_jr_cc_d,
+ insn_jp_nn,
+ insn_jp_rr,
+ insn_jp_cc_nn,
+ insn_call_nn,
+ insn_call_cc_nn,
+ insn_rst_n,
+ insn_ret,
+ insn_ret_cc,
+ insn_push_rr,
+ insn_pop_rr,
+ insn_dec_sp,
+ insn_inc_sp,
+ insn_ld_sp_nn,
+ insn_ld_sp_6nn9, /* ld sp, (nn) */
+ insn_ld_sp_rr,
+ insn_force_nop /* invalid opcode prefix */
+};
+
+struct insn_info
+{
+ gdb_byte code;
+ gdb_byte mask;
+ gdb_byte size; /* without prefix(es) */
+ enum instruction_type type;
+};
+
+/* Constants */
+
+static const struct insn_info *
+z80_get_insn_info (struct gdbarch *gdbarch, const gdb_byte *buf, int *size);
+
+static const char *z80_reg_names[] =
+{
+ /* 24 bit on eZ80, else 16 bit */
+ "af", "bc", "de", "hl",
+ "sp", "pc", "ix", "iy",
+ "af'", "bc'", "de'", "hl'",
+ "ir",
+ /* eZ80 only */
+ "sps"
+};
+
+/* Return the name of register REGNUM. */
+static const char *
+z80_register_name (struct gdbarch *gdbarch, int regnum)
+{
+ if (regnum >= 0 && regnum < ARRAY_SIZE (z80_reg_names))
+ return z80_reg_names[regnum];
+
+ return NULL;
+}
+
+/* Return the type of a register specified by the architecture. Only
+ the register cache should call this function directly; others should
+ use "register_type". */
+static struct type *
+z80_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+ return builtin_type (gdbarch)->builtin_data_ptr;
+}
+
+/* The next 2 functions check BUF for instruction. If it is pop/push rr, then
+ it returns register number OR'ed with 0x100 */
+static int
+z80_is_pop_rr (const gdb_byte buf[], int *size)
+{
+ switch (buf[0])
+ {
+ case 0xc1:
+ *size = 1;
+ return Z80_BC_REGNUM | 0x100;
+ case 0xd1:
+ *size = 1;
+ return Z80_DE_REGNUM | 0x100;
+ case 0xe1:
+ *size = 1;
+ return Z80_HL_REGNUM | 0x100;
+ case 0xf1:
+ *size = 1;
+ return Z80_AF_REGNUM | 0x100;
+ case 0xdd:
+ *size = 2;
+ return (buf[1] == 0xe1) ? (Z80_IX_REGNUM | 0x100) : 0;
+ case 0xfd:
+ *size = 2;
+ return (buf[1] == 0xe1) ? (Z80_IY_REGNUM | 0x100) : 0;
+ }
+ *size = 0;
+ return 0;
+}
+
+static int
+z80_is_push_rr (const gdb_byte buf[], int *size)
+{
+ switch (buf[0])
+ {
+ case 0xc5:
+ *size = 1;
+ return Z80_BC_REGNUM | 0x100;
+ case 0xd5:
+ *size = 1;
+ return Z80_DE_REGNUM | 0x100;
+ case 0xe5:
+ *size = 1;
+ return Z80_HL_REGNUM | 0x100;
+ case 0xf5:
+ *size = 1;
+ return Z80_AF_REGNUM | 0x100;
+ case 0xdd:
+ *size = 2;
+ return (buf[1] == 0xe5) ? (Z80_IX_REGNUM | 0x100) : 0;
+ case 0xfd:
+ *size = 2;
+ return (buf[1] == 0xe5) ? (Z80_IY_REGNUM | 0x100) : 0;
+ }
+ *size = 0;
+ return 0;
+}
+
+/* Function: z80_scan_prologue
+
+ This function decodes a function prologue to determine:
+ 1) the size of the stack frame
+ 2) which registers are saved on it
+ 3) the offsets of saved regs
+ This information is stored in the z80_unwind_cache structure.
+ Small SDCC functions may just load args using POP instructions in prologue:
+ pop af
+ pop de
+ pop hl
+ pop bc
+ push bc
+ push hl
+ push de
+ push af
+ SDCC function prologue may have up to 3 sections (all are optional):
+ 1) save state
+ a) __critical functions:
+ ld a,i
+ di
+ push af
+ b) __interrupt (both int and nmi) functions:
+ push af
+ push bc
+ push de
+ push hl
+ push iy
+ 2) save and adjust frame pointer
+ a) call to special function (size optimization)
+ call ___sdcc_enter_ix
+ b) inline (speed optimization)
+ push ix
+ ld ix, #0
+ add ix, sp
+ c) without FP, but saving it (IX is optimized out)
+ push ix
+ 3) allocate local variables
+ a) via series of PUSH AF and optional DEC SP (size optimization)
+ push af
+ ...
+ push af
+ dec sp ;optional, if allocated odd numbers of bytes
+ b) via SP decrements
+ dec sp
+ ...
+ dec sp
+ c) via addition (for large frames: 5+ for speed and 9+ for size opt.)
+ ld hl, #xxxx ;size of stack frame
+ add hl, sp
+ ld sp, hl
+ d) same, but using register IY (arrays or for __z88dk_fastcall functions)
+ ld iy, #xxxx ;size of stack frame
+ add iy, sp
+ ld sp, iy
+ e) same as c, but for eZ80
+ lea hl, ix - #nn
+ ld sp, hl
+ f) same as d, but for eZ80
+ lea iy, ix - #nn
+ ld sp, iy
+*/
+
+static int
+z80_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR pc_beg, CORE_ADDR pc_end,
+ struct z80_unwind_cache *info)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int addr_len = gdbarch_tdep (gdbarch)->addr_length;
+ gdb_byte prologue[32]; /* max prologue is 24 bytes: __interrupt with local array */
+ int pos = 0;
+ int len;
+ int reg;
+ CORE_ADDR value;
+
+ len = pc_end - pc_beg;
+ if (len > (int)sizeof (prologue))
+ len = sizeof (prologue);
+
+ read_memory (pc_beg, prologue, len);
+
+ /* stage0: check for series of POPs and then PUSHs */
+ if ((reg = z80_is_pop_rr(prologue, &pos)))
+ {
+ int i;
+ int size = pos;
+ gdb_byte regs[8]; /* Z80 have only 6 register pairs */
+ regs[0] = reg & 0xff;
+ for (i = 1; i < 8 && (regs[i] = z80_is_pop_rr (&prologue[pos], &size));
+ ++i, pos += size);
+ /* now we expect series of PUSHs in reverse order */
+ for (--i; i >= 0 && regs[i] == z80_is_push_rr (&prologue[pos], &size);
+ --i, pos += size);
+ if (i == -1 && pos > 0)
+ info->prologue_type.load_args = 1;
+ else
+ pos = 0;
+ }
+ /* stage1: check for __interrupt handlers and __critical functions */
+ else if (!memcmp (&prologue[pos], "\355\127\363\365", 4))
+ { /* ld a, i; di; push af */
+ info->prologue_type.critical = 1;
+ pos += 4;
+ info->state_size += addr_len;
+ }
+ else if (!memcmp (&prologue[pos], "\365\305\325\345\375\345", 6))
+ { /* push af; push bc; push de; push hl; push iy */
+ info->prologue_type.interrupt = 1;
+ pos += 6;
+ info->state_size += addr_len * 5;
+ }
+
+ /* stage2: check for FP saving scheme */
+ if (prologue[pos] == 0xcd) /* call nn */
+ {
+ struct bound_minimal_symbol msymbol;
+ msymbol = lookup_minimal_symbol ("__sdcc_enter_ix", NULL, NULL);
+ if (msymbol.minsym)
+ {
+ value = BMSYMBOL_VALUE_ADDRESS (msymbol);
+ if (value == extract_unsigned_integer (&prologue[pos+1], addr_len, byte_order))
+ {
+ pos += 1 + addr_len;
+ info->prologue_type.fp_sdcc = 1;
+ }
+ }
+ }
+ else if (!memcmp (&prologue[pos], "\335\345\335\041\000\000", 4+addr_len) &&
+ !memcmp (&prologue[pos+4+addr_len], "\335\071\335\371", 4))
+ { /* push ix; ld ix, #0; add ix, sp; ld sp, ix */
+ pos += 4 + addr_len + 4;
+ info->prologue_type.fp_sdcc = 1;
+ }
+ else if (!memcmp (&prologue[pos], "\335\345", 2))
+ { /* push ix */
+ pos += 2;
+ info->prologue_type.fp_sdcc = 1;
+ }
+
+ /* stage3: check for local variables allocation */
+ switch (prologue[pos])
+ {
+ case 0xf5: /* push af */
+ info->size = 0;
+ while (prologue[pos] == 0xf5)
+ {
+ info->size += addr_len;
+ pos++;
+ }
+ if (prologue[pos] == 0x3b) /* dec sp */
+ {
+ info->size++;
+ pos++;
+ }
+ break;
+ case 0x3b: /* dec sp */
+ info->size = 0;
+ while (prologue[pos] == 0x3b)
+ {
+ info->size++;
+ pos++;
+ }
+ break;
+ case 0x21: /*ld hl, -nn */
+ if (prologue[pos+addr_len] == 0x39 && prologue[pos+addr_len] >= 0x80 &&
+ prologue[pos+addr_len+1] == 0xf9)
+ { /* add hl, sp; ld sp, hl */
+ info->size = -extract_signed_integer(&prologue[pos+1], addr_len, byte_order);
+ pos += 1 + addr_len + 2;
+ }
+ break;
+ case 0xfd: /* ld iy, -nn */
+ if (prologue[pos+1] == 0x21 && prologue[pos+1+addr_len] >= 0x80 &&
+ !memcmp (&prologue[pos+2+addr_len], "\375\071\375\371", 4))
+ {
+ info->size = -extract_signed_integer(&prologue[pos+2], addr_len, byte_order);
+ pos += 2 + addr_len + 4;
+ }
+ break;
+ case 0xed: /* check for lea xx, ix - n */
+ switch (prologue[pos+1])
+ {
+ case 0x22: /* lea hl, ix - n */
+ if (prologue[pos+2] >= 0x80 && prologue[pos+3] == 0xf9)
+ { /* ld sp, hl */
+ info->size = -extract_signed_integer(&prologue[pos+2], 1, byte_order);
+ pos += 4;
+ }
+ break;
+ case 0x55: /* lea iy, ix - n */
+ if (prologue[pos+2] >= 0x80 && prologue[pos+3] == 0xfd &&
+ prologue[pos+4] == 0xf9)
+ { /* ld sp, iy */
+ info->size = -extract_signed_integer(&prologue[pos+2], 1, byte_order);
+ pos += 5;
+ }
+ break;
+ }
+ break;
+ }
+ len = 0;
+
+ if (info->prologue_type.interrupt)
+ {
+ info->saved_regs[Z80_AF_REGNUM].set_addr (len++);
+ info->saved_regs[Z80_BC_REGNUM].set_addr (len++);
+ info->saved_regs[Z80_DE_REGNUM].set_addr (len++);
+ info->saved_regs[Z80_HL_REGNUM].set_addr (len++);
+ info->saved_regs[Z80_IY_REGNUM].set_addr (len++);
+ }
+
+ if (info->prologue_type.critical)
+ len++; /* just skip IFF2 saved state */
+
+ if (info->prologue_type.fp_sdcc)
+ info->saved_regs[Z80_IX_REGNUM].set_addr (len++);
+
+ info->state_size += len * addr_len;
+
+ return pc_beg + pos;
+}
+
+static CORE_ADDR
+z80_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ CORE_ADDR func_addr, func_end;
+ CORE_ADDR prologue_end;
+
+ if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end))
+ return pc;
+
+ prologue_end = skip_prologue_using_sal (gdbarch, func_addr);
+ if (prologue_end != 0)
+ return std::max (pc, prologue_end);
+
+ {
+ struct z80_unwind_cache info = {0};
+ struct trad_frame_saved_reg saved_regs[Z80_NUM_REGS];
+
+ info.saved_regs = saved_regs;
+
+ /* Need to run the prologue scanner to figure out if the function has a
+ prologue. */
+
+ prologue_end = z80_scan_prologue (gdbarch, func_addr, func_end, &info);
+
+ if (info.prologue_type.fp_sdcc || info.prologue_type.interrupt ||
+ info.prologue_type.critical)
+ return std::max (pc, prologue_end);
+ }
+
+ if (prologue_end != 0)
+ {
+ struct symtab_and_line prologue_sal = find_pc_line (func_addr, 0);
+ struct compunit_symtab *compunit = SYMTAB_COMPUNIT (prologue_sal.symtab);
+ const char *debug_format = COMPUNIT_DEBUGFORMAT (compunit);
+
+ if (debug_format != NULL &&
+ !strncasecmp ("dwarf", debug_format, strlen("dwarf")))
+ return std::max (pc, prologue_end);
+ }
+
+ return pc;
+}
+
+/* Return the return-value convention that will be used by FUNCTION
+ to return a value of type VALTYPE. FUNCTION may be NULL in which
+ case the return convention is computed based only on VALTYPE.
+
+ If READBUF is not NULL, extract the return value and save it in this buffer.
+
+ If WRITEBUF is not NULL, it contains a return value which will be
+ stored into the appropriate register. This can be used when we want
+ to force the value returned by a function (see the "return" command
+ for instance). */
+static enum return_value_convention
+z80_return_value (struct gdbarch *gdbarch, struct value *function,
+ struct type *valtype, struct regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+ /* Byte are returned in L, word in HL, dword in DEHL. */
+ int len = TYPE_LENGTH (valtype);
+
+ if ((valtype->code () == TYPE_CODE_STRUCT
+ || valtype->code () == TYPE_CODE_UNION
+ || valtype->code () == TYPE_CODE_ARRAY)
+ && len > 4)
+ return RETURN_VALUE_STRUCT_CONVENTION;
+
+ if (writebuf != NULL)
+ {
+ if (len > 2)
+ {
+ regcache->cooked_write_part (Z80_DE_REGNUM, 0, len - 2, writebuf+2);
+ len = 2;
+ }
+ regcache->cooked_write_part (Z80_HL_REGNUM, 0, len, writebuf);
+ }
+
+ if (readbuf != NULL)
+ {
+ if (len > 2)
+ {
+ regcache->cooked_read_part (Z80_DE_REGNUM, 0, len - 2, readbuf+2);
+ len = 2;
+ }
+ regcache->cooked_read_part (Z80_HL_REGNUM, 0, len, readbuf);
+ }
+
+ return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+/* function unwinds current stack frame and returns next one */
+static struct z80_unwind_cache *
+z80_frame_unwind_cache (struct frame_info *this_frame,
+ void **this_prologue_cache)
+{
+ CORE_ADDR start_pc, current_pc;
+ ULONGEST this_base;
+ int i;
+ gdb_byte buf[sizeof(void*)];
+ struct z80_unwind_cache *info;
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ int addr_len = gdbarch_tdep (gdbarch)->addr_length;
+
+ if (*this_prologue_cache)
+ return (struct z80_unwind_cache *) *this_prologue_cache;
+
+ info = FRAME_OBSTACK_ZALLOC (struct z80_unwind_cache);
+ memset (info, 0, sizeof (*info));
+ info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+ *this_prologue_cache = info;
+
+ start_pc = get_frame_func (this_frame);
+ current_pc = get_frame_pc (this_frame);
+ if ((start_pc > 0) && (start_pc <= current_pc))
+ z80_scan_prologue (get_frame_arch (this_frame),
+ start_pc, current_pc, info);
+
+ if (info->prologue_type.fp_sdcc)
+ {
+ /* With SDCC standard prologue, IX points to the end of current frame
+ (where previous frame pointer and state are saved). */
+ this_base = get_frame_register_unsigned (this_frame, Z80_IX_REGNUM);
+ info->prev_sp = this_base + info->size;
+ }
+ else
+ {
+ CORE_ADDR addr;
+ CORE_ADDR sp;
+ CORE_ADDR sp_mask = (1 << gdbarch_ptr_bit(gdbarch)) - 1;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ /* Assume that the FP is this frame's SP but with that pushed
+ stack space added back. */
+ this_base = get_frame_register_unsigned (this_frame, Z80_SP_REGNUM);
+ sp = this_base + info->size;
+ for (;; ++sp)
+ {
+ sp &= sp_mask;
+ if (sp < this_base)
+ { /* overflow, looks like end of stack */
+ sp = this_base + info->size;
+ break;
+ }
+ /* find return address */
+ read_memory (sp, buf, addr_len);
+ addr = extract_unsigned_integer(buf, addr_len, byte_order);
+ read_memory (addr-addr_len-1, buf, addr_len+1);
+ if (buf[0] == 0xcd || (buf[0] & 0307) == 0304) /* Is it CALL */
+ { /* CALL nn or CALL cc,nn */
+ static const char *names[] =
+ {
+ "__sdcc_call_ix", "__sdcc_call_iy", "__sdcc_call_hl"
+ };
+ addr = extract_unsigned_integer(buf+1, addr_len, byte_order);
+ if (addr == start_pc)
+ break; /* found */
+ for (i = sizeof(names)/sizeof(*names)-1; i >= 0; --i)
+ {
+ struct bound_minimal_symbol msymbol;
+ msymbol = lookup_minimal_symbol (names[i], NULL, NULL);
+ if (!msymbol.minsym)
+ continue;
+ if (addr == BMSYMBOL_VALUE_ADDRESS (msymbol))
+ break;
+ }
+ if (i >= 0)
+ break;
+ continue;
+ }
+ else
+ continue; /* it is not call_nn, call_cc_nn */
+ }
+ info->prev_sp = sp;
+ }
+
+ /* Adjust all the saved registers so that they contain addresses and not
+ offsets. */
+ for (i = 0; i < gdbarch_num_regs (gdbarch) - 1; i++)
+ if (info->saved_regs[i].addr () > 0)
+ info->saved_regs[i].set_addr
+ (info->prev_sp - info->saved_regs[i].addr () * addr_len);
+
+ /* Except for the startup code, the return PC is always saved on
+ the stack and is at the base of the frame. */
+ info->saved_regs[Z80_PC_REGNUM].set_addr (info->prev_sp);
+
+ /* The previous frame's SP needed to be computed. Save the computed
+ value. */
+ info->saved_regs[Z80_SP_REGNUM].set_value (info->prev_sp + addr_len);
+ return info;
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+ frame. This will be used to create a new GDB frame struct. */
+static void
+z80_frame_this_id (struct frame_info *this_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct frame_id id;
+ struct z80_unwind_cache *info;
+ CORE_ADDR base;
+ CORE_ADDR func;
+
+ /* The FUNC is easy. */
+ func = get_frame_func (this_frame);
+
+ info = z80_frame_unwind_cache (this_frame, this_cache);
+ /* Hopefully the prologue analysis either correctly determined the
+ frame's base (which is the SP from the previous frame), or set
+ that base to "NULL". */
+ base = info->prev_sp;
+ if (base == 0)
+ return;
+
+ id = frame_id_build (base, func);
+ *this_id = id;
+}
+
+static struct value *
+z80_frame_prev_register (struct frame_info *this_frame,
+ void **this_prologue_cache, int regnum)
+{
+ struct z80_unwind_cache *info
+ = z80_frame_unwind_cache (this_frame, this_prologue_cache);
+
+ if (regnum == Z80_PC_REGNUM)
+ {
+ if (info->saved_regs[Z80_PC_REGNUM].is_addr ())
+ {
+ /* Reading the return PC from the PC register is slightly
+ abnormal. */
+ ULONGEST pc;
+ gdb_byte buf[3];
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ read_memory (info->saved_regs[Z80_PC_REGNUM].addr (),
+ buf, tdep->addr_length);
+ pc = extract_unsigned_integer (buf, tdep->addr_length, byte_order);
+ return frame_unwind_got_constant (this_frame, regnum, pc);
+ }
+
+ return frame_unwind_got_optimized (this_frame, regnum);
+ }
+
+ return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
+}
+
+/* Return the breakpoint kind for this target based on *PCPTR. */
+static int
+z80_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
+{
+ static int addr = -1;
+ if (addr == -1)
+ {
+ struct bound_minimal_symbol bh;
+ bh = lookup_minimal_symbol ("_break_handler", NULL, NULL);
+ if (bh.minsym)
+ addr = BMSYMBOL_VALUE_ADDRESS (bh);
+ else
+ {
+ warning(_("Unable to determine inferior's software breakpoint type: "
+ "couldn't find `_break_handler' function in inferior. Will "
+ "be used default software breakpoint instruction RST 0x08."));
+ addr = 0x0008;
+ }
+ }
+ return addr;
+}
+
+/* Return the software breakpoint from KIND. KIND is just address of breakpoint
+ handler. If address is on of standard RSTs, then RST n instruction is used
+ as breakpoint.
+ SIZE is set to the software breakpoint's length in memory. */
+static const gdb_byte *
+z80_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
+{
+ static gdb_byte break_insn[8];
+
+ if ((kind & 070) == kind)
+ {
+ break_insn[0] = kind | 0307;
+ *size = 1;
+ }
+ else /* kind is non-RST address, use CALL instead, but it is dungerous */
+ {
+ gdb_byte *p = break_insn;
+ *p++ = 0xcd;
+ *p++ = (kind >> 0) & 0xff;
+ *p++ = (kind >> 8) & 0xff;
+ if (gdbarch_tdep (gdbarch)->addr_length > 2)
+ *p++ = (kind >> 16) & 0xff;
+ *size = p - break_insn;
+ }
+ return break_insn;
+}
+
+/* Return a vector of addresses on which the software single step
+ breakpoints should be inserted. NULL means software single step is
+ not used.
+ Only one breakpoint address will be returned: conditional branches
+ will be always evaluated. */
+static std::vector<CORE_ADDR>
+z80_software_single_step (struct regcache *regcache)
+{
+ static const int flag_mask[] = {1 << 6, 1 << 0, 1 << 2, 1 << 7};
+ gdb_byte buf[8];
+ ULONGEST t;
+ ULONGEST addr;
+ int opcode;
+ int size;
+ const struct insn_info *info;
+ std::vector<CORE_ADDR> ret (1);
+ struct gdbarch *gdbarch = target_gdbarch ();
+
+ regcache->cooked_read (Z80_PC_REGNUM, &addr);
+ read_memory (addr, buf, sizeof(buf));
+ info = z80_get_insn_info (gdbarch, buf, &size);
+ ret[0] = addr + size;
+ if (info == NULL) /* possible in case of double prefix */
+ { /* forced NOP, TODO: replace by NOP */
+ return ret;
+ }
+ opcode = buf[size - info->size]; /* take opcode instead of prefix */
+ /* stage 1: check for conditions */
+ switch (info->type)
+ {
+ case insn_djnz_d:
+ regcache->cooked_read (Z80_BC_REGNUM, &t);
+ if ((t & 0xff00) != 0x100)
+ return ret;
+ break;
+ case insn_jr_cc_d:
+ opcode &= 030; /* JR NZ,d has cc equal to 040, but others 000 */
+ /* fall through */
+ case insn_jp_cc_nn:
+ case insn_call_cc_nn:
+ case insn_ret_cc:
+ regcache->cooked_read (Z80_AF_REGNUM, &t);
+ /* lower bit of condition inverts match, so invert flags if set */
+ if ((opcode & 010) != 0)
+ t = ~t;
+ /* two higher bits of condition field defines flag, so use them only
+ to check condition of "not execute" */
+ if (t & flag_mask[(opcode >> 4) & 3])
+ return ret;
+ break;
+ }
+ /* stage 2: compute address */
+ /* TODO: implement eZ80 MADL support */
+ switch (info->type)
+ {
+ default:
+ return ret;
+ case insn_djnz_d:
+ case insn_jr_d:
+ case insn_jr_cc_d:
+ addr += size;
+ addr += (signed char)buf[size-1];
+ break;
+ case insn_jp_rr:
+ if (size == 1)
+ opcode = Z80_HL_REGNUM;
+ else
+ opcode = (buf[size-2] & 0x20) ? Z80_IY_REGNUM : Z80_IX_REGNUM;
+ regcache->cooked_read (opcode, &addr);
+ break;
+ case insn_jp_nn:
+ case insn_jp_cc_nn:
+ case insn_call_nn:
+ case insn_call_cc_nn:
+ addr = buf[size-1] * 0x100 + buf[size-2];
+ if (info->size > 3) /* long instruction mode */
+ addr = addr * 0x100 + buf[size-3];
+ break;
+ case insn_rst_n:
+ addr = opcode & 070;
+ break;
+ case insn_ret:
+ case insn_ret_cc:
+ regcache->cooked_read (Z80_SP_REGNUM, &addr);
+ read_memory (addr, buf, 3);
+ addr = buf[1] * 0x100 + buf[0];
+ if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_ez80_adl)
+ addr = addr * 0x100 + buf[2];
+ break;
+ }
+ ret[0] = addr;
+ return ret;
+}
+
+/* Cached, dynamically allocated copies of the target data structures: */
+static unsigned (*cache_ovly_region_table)[3] = 0;
+static unsigned cache_novly_regions;
+static CORE_ADDR cache_ovly_region_table_base = 0;
+enum ovly_index
+ {
+ VMA, OSIZE, MAPPED_TO_LMA
+ };
+
+static void
+z80_free_overlay_region_table (void)
+{
+ if (cache_ovly_region_table)
+ xfree (cache_ovly_region_table);
+ cache_novly_regions = 0;
+ cache_ovly_region_table = NULL;
+ cache_ovly_region_table_base = 0;
+}
+
+/* Read an array of ints of size SIZE from the target into a local buffer.
+ Convert to host order. LEN is number of ints. */
+
+static void
+read_target_long_array (CORE_ADDR memaddr, unsigned int *myaddr,
+ int len, int size, enum bfd_endian byte_order)
+{
+ /* alloca is safe here, because regions array is very small. */
+ gdb_byte *buf = (gdb_byte *) alloca (len * size);
+ int i;
+
+ read_memory (memaddr, buf, len * size);
+ for (i = 0; i < len; i++)
+ myaddr[i] = extract_unsigned_integer (size * i + buf, size, byte_order);
+}
+
+static int
+z80_read_overlay_region_table ()
+{
+ struct bound_minimal_symbol novly_regions_msym;
+ struct bound_minimal_symbol ovly_region_table_msym;
+ struct gdbarch *gdbarch;
+ int word_size;
+ enum bfd_endian byte_order;
+
+ z80_free_overlay_region_table ();
+ novly_regions_msym = lookup_minimal_symbol ("_novly_regions", NULL, NULL);
+ if (! novly_regions_msym.minsym)
+ {
+ error (_("Error reading inferior's overlay table: "
+ "couldn't find `_novly_regions'\n"
+ "variable in inferior. Use `overlay manual' mode."));
+ return 0;
+ }
+
+ ovly_region_table_msym = lookup_bound_minimal_symbol ("_ovly_region_table");
+ if (! ovly_region_table_msym.minsym)
+ {
+ error (_("Error reading inferior's overlay table: couldn't find "
+ "`_ovly_region_table'\n"
+ "array in inferior. Use `overlay manual' mode."));
+ return 0;
+ }
+
+ const enum overlay_debugging_state save_ovly_dbg = overlay_debugging;
+ /* prevent infinite recurse */
+ overlay_debugging = ovly_off;
+
+ gdbarch = ovly_region_table_msym.objfile->arch ();
+ word_size = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT;
+ byte_order = gdbarch_byte_order (gdbarch);
+
+ cache_novly_regions = read_memory_integer (
+ BMSYMBOL_VALUE_ADDRESS (novly_regions_msym),
+ 4, byte_order);
+ cache_ovly_region_table
+ = (unsigned int (*)[3]) xmalloc (cache_novly_regions *
+ sizeof (*cache_ovly_region_table));
+ cache_ovly_region_table_base
+ = BMSYMBOL_VALUE_ADDRESS (ovly_region_table_msym);
+ read_target_long_array (cache_ovly_region_table_base,
+ (unsigned int *) cache_ovly_region_table,
+ cache_novly_regions * 3, word_size, byte_order);
+
+ overlay_debugging = save_ovly_dbg;
+ return 1; /* SUCCESS */
+}
+
+static int
+z80_overlay_update_1 (struct obj_section *osect)
+{
+ int i;
+ asection *bsect = osect->the_bfd_section;
+ unsigned lma;
+ unsigned vma = bfd_section_vma (bsect);
+
+ /* find region corresponding to the section VMA */
+ for (i = 0; i < cache_novly_regions; i++)
+ if (cache_ovly_region_table[i][VMA] == vma)
+ break;
+ if (i == cache_novly_regions)
+ return 0; /* no such region */
+
+ lma = cache_ovly_region_table[i][MAPPED_TO_LMA];
+ i = 0;
+
+ /* we have interest for sections with same VMA */
+ for (objfile *objfile : current_program_space->objfiles ())
+ ALL_OBJFILE_OSECTIONS (objfile, osect)
+ if (section_is_overlay (osect))
+ {
+ osect->ovly_mapped = (lma == bfd_section_lma (osect->the_bfd_section));
+ i |= osect->ovly_mapped; /* true, if at least one section is mapped */
+ }
+ return i;
+}
+
+/* Refresh overlay mapped state for section OSECT. */
+static void
+z80_overlay_update (struct obj_section *osect)
+{
+ /* Always need to read the entire table anew. */
+ if (!z80_read_overlay_region_table ())
+ return;
+
+ /* Were we given an osect to look up? NULL means do all of them. */
+ if (osect != nullptr && z80_overlay_update_1 (osect))
+ return;
+
+ /* Update all sections, even if only one was requested. */
+ for (objfile *objfile : current_program_space->objfiles ())
+ ALL_OBJFILE_OSECTIONS (objfile, osect)
+ {
+ if (!section_is_overlay (osect))
+ continue;
+
+ asection *bsect = osect->the_bfd_section;
+ bfd_vma lma = bfd_section_lma (bsect);
+ bfd_vma vma = bfd_section_vma (bsect);
+
+ for (int i = 0; i < cache_novly_regions; ++i)
+ if (cache_ovly_region_table[i][VMA] == vma)
+ osect->ovly_mapped =
+ (cache_ovly_region_table[i][MAPPED_TO_LMA] == lma);
+ }
+}
+
+/* Return non-zero if the instruction at ADDR is a call; zero otherwise. */
+static int
+z80_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ gdb_byte buf[8];
+ int size;
+ const struct insn_info *info;
+ read_memory (addr, buf, sizeof(buf));
+ info = z80_get_insn_info (gdbarch, buf, &size);
+ if (info)
+ switch (info->type)
+ {
+ case insn_call_nn:
+ case insn_call_cc_nn:
+ case insn_rst_n:
+ return 1;
+ }
+ return 0;
+}
+
+/* Return non-zero if the instruction at ADDR is a return; zero otherwise. */
+static int
+z80_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ gdb_byte buf[8];
+ int size;
+ const struct insn_info *info;
+ read_memory (addr, buf, sizeof(buf));
+ info = z80_get_insn_info (gdbarch, buf, &size);
+ if (info)
+ switch (info->type)
+ {
+ case insn_ret:
+ case insn_ret_cc:
+ return 1;
+ }
+ return 0;
+}
+
+/* Return non-zero if the instruction at ADDR is a jump; zero otherwise. */
+static int
+z80_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ gdb_byte buf[8];
+ int size;
+ const struct insn_info *info;
+ read_memory (addr, buf, sizeof(buf));
+ info = z80_get_insn_info (gdbarch, buf, &size);
+ if (info)
+ switch (info->type)
+ {
+ case insn_jp_nn:
+ case insn_jp_cc_nn:
+ case insn_jp_rr:
+ case insn_jr_d:
+ case insn_jr_cc_d:
+ case insn_djnz_d:
+ return 1;
+ }
+ return 0;
+}
+
+static const struct frame_unwind
+z80_frame_unwind =
+{
+ "z80",
+ NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
+ z80_frame_this_id,
+ z80_frame_prev_register,
+ NULL, /*unwind_data*/
+ default_frame_sniffer
+ /*dealloc_cache*/
+ /*prev_arch*/
+};
+
+/* Initialize the gdbarch struct for the Z80 arch */
+static struct gdbarch *
+z80_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch *gdbarch;
+ struct gdbarch_tdep *tdep;
+ struct gdbarch_list *best_arch;
+ tdesc_arch_data_up tdesc_data;
+ unsigned long mach = info.bfd_arch_info->mach;
+ const struct target_desc *tdesc = info.target_desc;
+
+ if (!tdesc_has_registers (tdesc))
+ /* Pick a default target description. */
+ tdesc = tdesc_z80;
+
+ /* Check any target description for validity. */
+ if (tdesc_has_registers (tdesc))
+ {
+ const struct tdesc_feature *feature;
+ int valid_p;
+
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.z80.cpu");
+ if (feature == NULL)
+ return NULL;
+
+ tdesc_data = tdesc_data_alloc ();
+
+ valid_p = 1;
+
+ for (unsigned i = 0; i < Z80_NUM_REGS; i++)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), i,
+ z80_reg_names[i]);
+
+ if (!valid_p)
+ return NULL;
+ }
+
+ /* If there is already a candidate, use it. */
+ for (best_arch = gdbarch_list_lookup_by_info (arches, &info);
+ best_arch != NULL;
+ best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
+ {
+ if (mach == gdbarch_bfd_arch_info (best_arch->gdbarch)->mach)
+ return best_arch->gdbarch;
+ }
+
+ /* None found, create a new architecture from the information provided. */
+ tdep = XCNEW (struct gdbarch_tdep);
+ gdbarch = gdbarch_alloc (&info, tdep);
+
+ if (mach == bfd_mach_ez80_adl)
+ {
+ tdep->addr_length = 3;
+ set_gdbarch_max_insn_length (gdbarch, 6);
+ }
+ else
+ {
+ tdep->addr_length = 2;
+ set_gdbarch_max_insn_length (gdbarch, 4);
+ }
+
+ /* Create a type for PC. We can't use builtin types here, as they may not
+ be defined. */
+ tdep->void_type = arch_type (gdbarch, TYPE_CODE_VOID, TARGET_CHAR_BIT,
+ "void");
+ tdep->func_void_type = make_function_type (tdep->void_type, NULL);
+ tdep->pc_type = arch_pointer_type (gdbarch,
+ tdep->addr_length * TARGET_CHAR_BIT,
+ NULL, tdep->func_void_type);
+
+ set_gdbarch_short_bit (gdbarch, TARGET_CHAR_BIT);
+ set_gdbarch_int_bit (gdbarch, 2 * TARGET_CHAR_BIT);
+ set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_ptr_bit (gdbarch, tdep->addr_length * TARGET_CHAR_BIT);
+ set_gdbarch_addr_bit (gdbarch, tdep->addr_length * TARGET_CHAR_BIT);
+
+ set_gdbarch_num_regs (gdbarch, (mach == bfd_mach_ez80_adl) ? EZ80_NUM_REGS
+ : Z80_NUM_REGS);
+ set_gdbarch_sp_regnum (gdbarch, Z80_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, Z80_PC_REGNUM);
+
+ set_gdbarch_register_name (gdbarch, z80_register_name);
+ set_gdbarch_register_type (gdbarch, z80_register_type);
+
+ /* TODO: get FP type from binary (extra flags required) */
+ set_gdbarch_float_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_double_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_long_double_bit (gdbarch, 4 * TARGET_CHAR_BIT);
+ set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
+ set_gdbarch_double_format (gdbarch, floatformats_ieee_single);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ieee_single);
+
+ set_gdbarch_return_value (gdbarch, z80_return_value);
+
+ set_gdbarch_skip_prologue (gdbarch, z80_skip_prologue);
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan); // falling stack
+
+ set_gdbarch_software_single_step (gdbarch, z80_software_single_step);
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch, z80_breakpoint_kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch, z80_sw_breakpoint_from_kind);
+ set_gdbarch_insn_is_call (gdbarch, z80_insn_is_call);
+ set_gdbarch_insn_is_jump (gdbarch, z80_insn_is_jump);
+ set_gdbarch_insn_is_ret (gdbarch, z80_insn_is_ret);
+
+ set_gdbarch_overlay_update (gdbarch, z80_overlay_update);
+
+ frame_unwind_append_unwinder (gdbarch, &z80_frame_unwind);
+ if (tdesc_data)
+ tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
+
+ return gdbarch;
+}
+
+/* Table to disassemble machine codes without prefix. */
+static const struct insn_info
+ez80_main_insn_table[] =
+{ /* table with double prefix check */
+ { 0100, 0377, 0, insn_force_nop}, //double prefix
+ { 0111, 0377, 0, insn_force_nop}, //double prefix
+ { 0122, 0377, 0, insn_force_nop}, //double prefix
+ { 0133, 0377, 0, insn_force_nop}, //double prefix
+ /* initial table for eZ80_z80 */
+ { 0100, 0377, 1, insn_z80 }, //eZ80 mode prefix
+ { 0111, 0377, 1, insn_z80 }, //eZ80 mode prefix
+ { 0122, 0377, 1, insn_adl }, //eZ80 mode prefix
+ { 0133, 0377, 1, insn_adl }, //eZ80 mode prefix
+ /* here common Z80/Z180/eZ80 opcodes */
+ { 0000, 0367, 1, insn_default }, //"nop", "ex af,af'"
+ { 0061, 0377, 3, insn_ld_sp_nn }, //"ld sp,nn"
+ { 0001, 0317, 3, insn_default }, //"ld rr,nn"
+ { 0002, 0347, 1, insn_default }, //"ld (rr),a", "ld a,(rr)"
+ { 0042, 0347, 3, insn_default }, //"ld (nn),hl/a", "ld hl/a,(nn)"
+ { 0063, 0377, 1, insn_inc_sp }, //"inc sp"
+ { 0073, 0377, 1, insn_dec_sp }, //"dec sp"
+ { 0003, 0303, 1, insn_default }, //"inc rr", "dec rr", ...
+ { 0004, 0307, 1, insn_default }, //"inc/dec r/(hl)"
+ { 0006, 0307, 2, insn_default }, //"ld r,n", "ld (hl),n"
+ { 0020, 0377, 2, insn_djnz_d }, //"djnz dis"
+ { 0030, 0377, 2, insn_jr_d }, //"jr dis"
+ { 0040, 0347, 2, insn_jr_cc_d }, //"jr cc,dis"
+ { 0100, 0300, 1, insn_default }, //"ld r,r", "halt"
+ { 0200, 0300, 1, insn_default }, //"alu_op a,r"
+ { 0300, 0307, 1, insn_ret_cc }, //"ret cc"
+ { 0301, 0317, 1, insn_pop_rr }, //"pop rr"
+ { 0302, 0307, 3, insn_jp_cc_nn }, //"jp cc,nn"
+ { 0303, 0377, 3, insn_jp_nn }, //"jp nn"
+ { 0304, 0307, 3, insn_call_cc_nn}, //"call cc,nn"
+ { 0305, 0317, 1, insn_push_rr }, //"push rr"
+ { 0306, 0307, 2, insn_default }, //"alu_op a,n"
+ { 0307, 0307, 1, insn_rst_n }, //"rst n"
+ { 0311, 0377, 1, insn_ret }, //"ret"
+ { 0313, 0377, 2, insn_default }, //CB prefix
+ { 0315, 0377, 3, insn_call_nn }, //"call nn"
+ { 0323, 0367, 2, insn_default }, //"out (n),a", "in a,(n)"
+ { 0335, 0337, 1, insn_z80_ddfd }, //DD/FD prefix
+ { 0351, 0377, 1, insn_jp_rr }, //"jp (hl)"
+ { 0355, 0377, 1, insn_z80_ed }, //ED prefix
+ { 0371, 0377, 1, insn_ld_sp_rr }, //"ld sp,hl"
+ { 0000, 0000, 1, insn_default } //others
+} ;
+
+static const struct insn_info
+ez80_adl_main_insn_table[] =
+{ /* table with double prefix check */
+ { 0100, 0377, 0, insn_force_nop}, //double prefix
+ { 0111, 0377, 0, insn_force_nop}, //double prefix
+ { 0122, 0377, 0, insn_force_nop}, //double prefix
+ { 0133, 0377, 0, insn_force_nop}, //double prefix
+ /* initial table for eZ80_adl */
+ { 0000, 0367, 1, insn_default }, //"nop", "ex af,af'"
+ { 0061, 0377, 4, insn_ld_sp_nn }, //"ld sp,Mmn"
+ { 0001, 0317, 4, insn_default }, //"ld rr,Mmn"
+ { 0002, 0347, 1, insn_default }, //"ld (rr),a", "ld a,(rr)"
+ { 0042, 0347, 4, insn_default }, //"ld (Mmn),hl/a", "ld hl/a,(Mmn)"
+ { 0063, 0377, 1, insn_inc_sp }, //"inc sp"
+ { 0073, 0377, 1, insn_dec_sp }, //"dec sp"
+ { 0003, 0303, 1, insn_default }, //"inc rr", "dec rr", ...
+ { 0004, 0307, 1, insn_default }, //"inc/dec r/(hl)"
+ { 0006, 0307, 2, insn_default }, //"ld r,n", "ld (hl),n"
+ { 0020, 0377, 2, insn_djnz_d }, //"djnz dis"
+ { 0030, 0377, 2, insn_jr_d }, //"jr dis"
+ { 0040, 0347, 2, insn_jr_cc_d }, //"jr cc,dis"
+ { 0100, 0377, 1, insn_z80 }, //eZ80 mode prefix (short instruction)
+ { 0111, 0377, 1, insn_z80 }, //eZ80 mode prefix (short instruction)
+ { 0122, 0377, 1, insn_adl }, //eZ80 mode prefix (long instruction)
+ { 0133, 0377, 1, insn_adl }, //eZ80 mode prefix (long instruction)
+ { 0100, 0300, 1, insn_default }, //"ld r,r", "halt"
+ { 0200, 0300, 1, insn_default }, //"alu_op a,r"
+ { 0300, 0307, 1, insn_ret_cc }, //"ret cc"
+ { 0301, 0317, 1, insn_pop_rr }, //"pop rr"
+ { 0302, 0307, 4, insn_jp_cc_nn }, //"jp cc,nn"
+ { 0303, 0377, 4, insn_jp_nn }, //"jp nn"
+ { 0304, 0307, 4, insn_call_cc_nn}, //"call cc,Mmn"
+ { 0305, 0317, 1, insn_push_rr }, //"push rr"
+ { 0306, 0307, 2, insn_default }, //"alu_op a,n"
+ { 0307, 0307, 1, insn_rst_n }, //"rst n"
+ { 0311, 0377, 1, insn_ret }, //"ret"
+ { 0313, 0377, 2, insn_default }, //CB prefix
+ { 0315, 0377, 4, insn_call_nn }, //"call Mmn"
+ { 0323, 0367, 2, insn_default }, //"out (n),a", "in a,(n)"
+ { 0335, 0337, 1, insn_adl_ddfd }, //DD/FD prefix
+ { 0351, 0377, 1, insn_jp_rr }, //"jp (hl)"
+ { 0355, 0377, 1, insn_adl_ed }, //ED prefix
+ { 0371, 0377, 1, insn_ld_sp_rr }, //"ld sp,hl"
+ { 0000, 0000, 1, insn_default } //others
+};
+
+/* ED prefix opcodes table.
+ Note the instruction length does include the ED prefix (+ 1 byte)
+*/
+static const struct insn_info
+ez80_ed_insn_table[] =
+{
+ /* eZ80 only instructions */
+ { 0002, 0366, 2, insn_default }, //"lea rr,ii+d"
+ { 0124, 0376, 2, insn_default }, //"lea ix,iy+d", "lea iy,ix+d"
+ { 0145, 0377, 2, insn_default }, //"pea ix+d"
+ { 0146, 0377, 2, insn_default }, //"pea iy+d"
+ { 0164, 0377, 2, insn_default }, //"tstio n"
+ /* Z180/eZ80 only instructions */
+ { 0060, 0376, 1, insn_default }, //not an instruction
+ { 0000, 0306, 2, insn_default }, //"in0 r,(n)", "out0 (n),r"
+ { 0144, 0377, 2, insn_default }, //"tst a, n"
+ /* common instructions */
+ { 0173, 0377, 3, insn_ld_sp_6nn9 }, //"ld sp,(nn)"
+ { 0103, 0307, 3, insn_default }, //"ld (nn),rr", "ld rr,(nn)"
+ { 0105, 0317, 1, insn_ret }, //"retn", "reti"
+ { 0000, 0000, 1, insn_default }
+};
+
+static const struct insn_info
+ez80_adl_ed_insn_table[] =
+{
+ { 0002, 0366, 2, insn_default }, //"lea rr,ii+d"
+ { 0124, 0376, 2, insn_default }, //"lea ix,iy+d", "lea iy,ix+d"
+ { 0145, 0377, 2, insn_default }, //"pea ix+d"
+ { 0146, 0377, 2, insn_default }, //"pea iy+d"
+ { 0164, 0377, 2, insn_default }, //"tstio n"
+ { 0060, 0376, 1, insn_default }, //not an instruction
+ { 0000, 0306, 2, insn_default }, //"in0 r,(n)", "out0 (n),r"
+ { 0144, 0377, 2, insn_default }, //"tst a, n"
+ { 0173, 0377, 4, insn_ld_sp_6nn9 }, //"ld sp,(nn)"
+ { 0103, 0307, 4, insn_default }, //"ld (nn),rr", "ld rr,(nn)"
+ { 0105, 0317, 1, insn_ret }, //"retn", "reti"
+ { 0000, 0000, 1, insn_default }
+};
+
+/* table for FD and DD prefixed instructions */
+static const struct insn_info
+ez80_ddfd_insn_table[] =
+{
+ /* ez80 only instructions */
+ { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)"
+ { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)"
+ /* common instructions */
+ { 0011, 0367, 2, insn_default }, //"add ii,rr"
+ { 0041, 0377, 3, insn_default }, //"ld ii,nn"
+ { 0042, 0367, 3, insn_default }, //"ld (nn),ii", "ld ii,(nn)"
+ { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii"
+ { 0044, 0366, 1, insn_default }, //"inc/dec iih/iil"
+ { 0046, 0367, 2, insn_default }, //"ld iih,n", "ld iil,n"
+ { 0064, 0376, 2, insn_default }, //"inc (ii+d)", "dec (ii+d)"
+ { 0066, 0377, 2, insn_default }, //"ld (ii+d),n"
+ { 0166, 0377, 0, insn_default }, //not an instruction
+ { 0160, 0370, 2, insn_default }, //"ld (ii+d),r"
+ { 0104, 0306, 1, insn_default }, //"ld r,iih", "ld r,iil"
+ { 0106, 0307, 2, insn_default }, //"ld r,(ii+d)"
+ { 0140, 0360, 1, insn_default }, //"ld iih,r", "ld iil,r"
+ { 0204, 0306, 1, insn_default }, //"alu_op a,iih", "alu_op a,iil"
+ { 0206, 0307, 2, insn_default }, //"alu_op a,(ii+d)"
+ { 0313, 0377, 3, insn_default }, //DD/FD CB dd oo instructions
+ { 0335, 0337, 0, insn_force_nop}, //double DD/FD prefix, exec DD/FD as NOP
+ { 0341, 0373, 1, insn_default }, //"pop ii", "push ii"
+ { 0343, 0377, 1, insn_default }, //"ex (sp),ii"
+ { 0351, 0377, 1, insn_jp_rr }, //"jp (ii)"
+ { 0371, 0377, 1, insn_ld_sp_rr}, //"ld sp,ii"
+ { 0000, 0000, 0, insn_default } //not an instruction, exec DD/FD as NOP
+};
+
+static const struct insn_info
+ez80_adl_ddfd_insn_table[] =
+{
+ { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)"
+ { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)"
+ { 0011, 0367, 1, insn_default }, //"add ii,rr"
+ { 0041, 0377, 4, insn_default }, //"ld ii,nn"
+ { 0042, 0367, 4, insn_default }, //"ld (nn),ii", "ld ii,(nn)"
+ { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii"
+ { 0044, 0366, 1, insn_default }, //"inc/dec iih/iil"
+ { 0046, 0367, 2, insn_default }, //"ld iih,n", "ld iil,n"
+ { 0064, 0376, 2, insn_default }, //"inc (ii+d)", "dec (ii+d)"
+ { 0066, 0377, 3, insn_default }, //"ld (ii+d),n"
+ { 0166, 0377, 0, insn_default }, //not an instruction
+ { 0160, 0370, 2, insn_default }, //"ld (ii+d),r"
+ { 0104, 0306, 1, insn_default }, //"ld r,iih", "ld r,iil"
+ { 0106, 0307, 2, insn_default }, //"ld r,(ii+d)"
+ { 0140, 0360, 1, insn_default }, //"ld iih,r", "ld iil,r"
+ { 0204, 0306, 1, insn_default }, //"alu_op a,iih", "alu_op a,iil"
+ { 0206, 0307, 2, insn_default }, //"alu_op a,(ii+d)"
+ { 0313, 0377, 3, insn_default }, //DD/FD CB dd oo instructions
+ { 0335, 0337, 0, insn_force_nop}, //double DD/FD prefix, exec DD/FD as NOP
+ { 0341, 0373, 1, insn_default }, //"pop ii", "push ii"
+ { 0343, 0377, 1, insn_default }, //"ex (sp),ii"
+ { 0351, 0377, 1, insn_jp_rr }, //"jp (ii)"
+ { 0371, 0377, 1, insn_ld_sp_rr}, //"ld sp,ii"
+ { 0000, 0000, 0, insn_default } //not an instruction, exec DD/FD as NOP
+};
+
+/* Return pointer to instruction information structure corresponded to opcode
+ in buf. */
+static const struct insn_info *
+z80_get_insn_info (struct gdbarch *gdbarch, const gdb_byte *buf, int *size)
+{
+ int code;
+ const struct insn_info *info;
+ unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ *size = 0;
+ switch (mach)
+ {
+ case bfd_mach_ez80_z80:
+ info = &ez80_main_insn_table[4]; /* skip force_nops */
+ break;
+ case bfd_mach_ez80_adl:
+ info = &ez80_adl_main_insn_table[4]; /* skip force_nops */
+ break;
+ default:
+ info = &ez80_main_insn_table[8]; /* skip eZ80 prefices and force_nops */
+ break;
+ }
+ do
+ {
+ for (; ((code = buf[*size]) & info->mask) != info->code; ++info)
+ ;
+ *size += info->size;
+ /* process instruction type */
+ switch (info->type)
+ {
+ case insn_z80:
+ if (mach == bfd_mach_ez80_z80 || mach == bfd_mach_ez80_adl)
+ info = &ez80_main_insn_table[0];
+ else
+ info = &ez80_main_insn_table[8];
+ break;
+ case insn_adl:
+ info = &ez80_adl_main_insn_table[0];
+ break;
+ /* These two (for GameBoy Z80 & Z80 Next CPUs) haven't been tested.
+
+ case bfd_mach_gbz80:
+ info = &gbz80_main_insn_table[0];
+ break;
+ case bfd_mach_z80n:
+ info = &z80n_main_insn_table[0];
+ break;
+ */
+ case insn_z80_ddfd:
+ if (mach == bfd_mach_ez80_z80 || mach == bfd_mach_ez80_adl)
+ info = &ez80_ddfd_insn_table[0];
+ else
+ info = &ez80_ddfd_insn_table[2];
+ break;
+ case insn_adl_ddfd:
+ info = &ez80_adl_ddfd_insn_table[0];
+ break;
+ case insn_z80_ed:
+ info = &ez80_ed_insn_table[0];
+ break;
+ case insn_adl_ed:
+ info = &ez80_adl_ed_insn_table[0];
+ break;
+ case insn_force_nop:
+ return NULL;
+ default:
+ return info;
+ }
+ }
+ while (1);
+}
+
+extern initialize_file_ftype _initialize_z80_tdep;
+
+void
+_initialize_z80_tdep ()
+{
+ register_gdbarch_init (bfd_arch_z80, z80_gdbarch_init);
+ initialize_tdesc_z80 ();
+}