Add basic Z80 CPU support
authorSergey Belyashov <Sergey.Belyashov@gmail.com>
Fri, 25 Sep 2020 11:40:42 +0000 (14:40 +0300)
committerSimon Marchi <simon.marchi@polymtl.ca>
Sat, 17 Jul 2021 14:17:30 +0000 (10:17 -0400)
Supported ISAs:
- Z80 (all undocumented instructions)
- Z180
- eZ80 (Z80 mode only)

Datasheets:
Z80: https://www.zilog.com/manage_directlink.php?filepath=docs/z80/um0080&extn=.pdf
Z180: https://www.zilog.com/manage_directlink.php?filepath=docs/z180/ps0140&extn=.pdf
eZ80: http://www.zilog.com/force_download.php?filepath=YUhSMGNEb3ZMM2QzZHk1NmFXeHZaeTVqYjIwdlpHOWpjeTlWVFRBd056Y3VjR1Jt

To debug Z80 programs using GDB you must configure and embed
z80-stub.c to your program (SDCC compiler is required). Or
you may use some simulator with GDB support.

gdb/ChangeLog:

* Makefile.in (ALL_TARGET_OBS): Add z80-tdep.c.
* NEWS: Mention z80 support.
* configure.tgt: Handle z80*.
* features/Makefile (XMLTOC): Add z80.xml.
* features/z80-cpu.xml: New.
* features/z80.c: Generate.
* features/z80.xml: New.
* z80-tdep.c: New file.
* z80-tdep.h: New file.

gdb/stubs/ChangeLog:

* z80-stub.c: New file.

Change-Id: Id0b7a6e210c3f93c6853c5e3031b7bcee47d0db9

gdb/Makefile.in
gdb/NEWS
gdb/configure.tgt
gdb/features/Makefile
gdb/features/z80-cpu.xml [new file with mode: 0644]
gdb/features/z80.c [new file with mode: 0644]
gdb/features/z80.xml [new file with mode: 0644]
gdb/stubs/z80-stub.c [new file with mode: 0644]
gdb/z80-tdep.c [new file with mode: 0644]
gdb/z80-tdep.h [new file with mode: 0644]

index 2274b9b6a61a32ce641bd0d08305ca59118e2358..73a1bf83c858e97dee11063511ffd962e5fb174e 100644 (file)
@@ -853,7 +853,8 @@ ALL_TARGET_OBS = \
        xstormy16-tdep.o \
        xtensa-config.o \
        xtensa-linux-tdep.o \
-       xtensa-tdep.o
+       xtensa-tdep.o \
+       z80-tdep.o
 
 # The following native-target dependent variables are defined on
 # configure.nat.
index 44b8bbc88eed3bfb3194a300f62ab8a0b3a1d8c6..062249aea98e6a142a248340c7805ea3a1505f82 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -429,6 +429,7 @@ alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]
 
 GNU/Linux/RISC-V (gdbserver)   riscv*-*-linux*
 BPF                            bpf-unknown-none
+Z80                            z80-unknown-*
 
 * Python API
 
index 643973917fe0dd7149250147edb9bba1d2ea8679..97a5a57c37886c2261bb20ae16b9dc2dfc3f26e4 100644 (file)
@@ -761,6 +761,10 @@ xtensa*-*-*linux*)
        # Target: GNU/Linux Xtensa
        gdb_target_obs="xtensa-linux-tdep.o symfile-mem.o linux-tdep.o"
        ;;
+z80*)
+       # Target: Z80
+       gdb_target_obs="z80-tdep.o"
+       ;;
 
 esac
 
index 522ad58aab0fd93786140f9bb25d496bca2d0d88..ded8c3bb9da4d234032892e58708aab44f118c52 100644 (file)
@@ -170,7 +170,8 @@ XMLTOC = \
        s390x-tevx-linux64.xml \
        s390x-vx-linux64.xml \
        s390-gs-linux64.xml \
-       s390x-gs-linux64.xml
+       s390x-gs-linux64.xml \
+       z80.xml
 
 TDESC_CFILES = $(patsubst %.xml,%.c,$(XMLTOC))
 GDB = false
diff --git a/gdb/features/z80-cpu.xml b/gdb/features/z80-cpu.xml
new file mode 100644 (file)
index 0000000..98498b1
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2020 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.z80.cpu">
+  <flags id="af_flags" size="2">
+    <field name="C" start="0" end="0"/>
+    <field name="N" start="1" end="1"/>
+    <field name="P/V" start="2" end="2"/>
+    <field name="F3" start="3" end="3"/>
+    <field name="H" start="4" end="4"/>
+    <field name="F5" start="5" end="5"/>
+    <field name="Z" start="6" end="6"/>
+    <field name="S" start="7" end="7"/>
+  </flags>
+  <reg name="af" bitsize="16" type="af_flags"/>
+  <reg name="bc" bitsize="16" type="uint16"/>
+  <reg name="de" bitsize="16" type="data_ptr"/>
+  <reg name="hl" bitsize="16" type="data_ptr"/>
+  <reg name="sp" bitsize="16" type="data_ptr" />
+  <reg name="pc" bitsize="32" type="code_ptr" />
+  <reg name="ix" bitsize="16" type="data_ptr"/>
+  <reg name="iy" bitsize="16" type="data_ptr"/>
+  <reg name="af'" bitsize="16" type="af_flags"/>
+  <reg name="bc'" bitsize="16" type="uint16"/>
+  <reg name="de'" bitsize="16" type="data_ptr"/>
+  <reg name="hl'" bitsize="16" type="data_ptr"/>
+  <reg name="ir" bitsize="16" type="uint16"/>
+</feature>
diff --git a/gdb/features/z80.c b/gdb/features/z80.c
new file mode 100644 (file)
index 0000000..944b563
--- /dev/null
@@ -0,0 +1,44 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: z80.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_z80;
+static void
+initialize_tdesc_z80 (void)
+{
+  target_desc_up result = allocate_target_description ();
+  set_tdesc_architecture (result.get (), bfd_scan_arch ("z80"));
+
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result.get (), "org.gnu.gdb.z80.cpu");
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_flags (feature, "af_flags", 2);
+  tdesc_add_flag (type_with_fields, 0, "C");
+  tdesc_add_flag (type_with_fields, 1, "N");
+  tdesc_add_flag (type_with_fields, 2, "P/V");
+  tdesc_add_flag (type_with_fields, 3, "F3");
+  tdesc_add_flag (type_with_fields, 4, "H");
+  tdesc_add_flag (type_with_fields, 5, "F5");
+  tdesc_add_flag (type_with_fields, 6, "Z");
+  tdesc_add_flag (type_with_fields, 7, "S");
+
+  tdesc_create_reg (feature, "af", 0, 1, NULL, 16, "af_flags");
+  tdesc_create_reg (feature, "bc", 1, 1, NULL, 16, "uint16");
+  tdesc_create_reg (feature, "de", 2, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "hl", 3, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "sp", 4, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "pc", 5, 1, NULL, 32, "code_ptr");
+  tdesc_create_reg (feature, "ix", 6, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "iy", 7, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "af'", 8, 1, NULL, 16, "af_flags");
+  tdesc_create_reg (feature, "bc'", 9, 1, NULL, 16, "uint16");
+  tdesc_create_reg (feature, "de'", 10, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "hl'", 11, 1, NULL, 16, "data_ptr");
+  tdesc_create_reg (feature, "ir", 12, 1, NULL, 16, "uint16");
+
+  tdesc_z80 = result.release ();
+}
diff --git a/gdb/features/z80.xml b/gdb/features/z80.xml
new file mode 100644 (file)
index 0000000..238687a
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2020 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>z80</architecture>
+  <xi:include href="z80-cpu.xml"/>
+</target>
diff --git a/gdb/stubs/z80-stub.c b/gdb/stubs/z80-stub.c
new file mode 100644 (file)
index 0000000..0ec128f
--- /dev/null
@@ -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 */
diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c
new file mode 100644 (file)
index 0000000..7b9a7e2
--- /dev/null
@@ -0,0 +1,1461 @@
+/* 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 ();
+}
diff --git a/gdb/z80-tdep.h b/gdb/z80-tdep.h
new file mode 100644 (file)
index 0000000..4d73aeb
--- /dev/null
@@ -0,0 +1,52 @@
+/* Target-dependent code for the Z80.
+
+   Copyright (C) 2002-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/>.  */
+
+#ifndef Z80_TDEP_H
+#define Z80_TDEP_H
+
+/* Register pair constants
+   Order optimized for gdb-stub implementation
+   Most of register pairs are 16 bit length on Z80 and
+   24 bit on eZ80 in ADL or MADL modes */
+enum z80_regnum
+{
+  Z80_AF_REGNUM,
+  Z80_BC_REGNUM,
+  Z80_DE_REGNUM,
+  Z80_HL_REGNUM,
+  Z80_SP_REGNUM,       /* SPL on eZ80 CPU */
+  Z80_PC_REGNUM,
+  Z80_IX_REGNUM,
+  Z80_IY_REGNUM,
+  Z80_AFA_REGNUM,
+  Z80_BCA_REGNUM,
+  Z80_DEA_REGNUM,
+  Z80_HLA_REGNUM,
+  Z80_IR_REGNUM,
+/* eZ80 only registers */
+  Z80_SPS_REGNUM       /* SPS register of eZ80 CPU */
+};
+
+#define Z80_NUM_REGS   13
+#define Z80_REG_BYTES  (Z80_NUM_REGS*2)
+
+#define EZ80_NUM_REGS  (Z80_NUM_REGS + 1)
+#define EZ80_REG_BYTES (EZ80_NUM_REGS*3)
+
+#endif /* z80-tdep.h */