Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Sergey Belyashov via Gdb-patches <gdb-patches@sourceware.org>
To: Simon Marchi <simon.marchi@polymtl.ca>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH v5] [gdb] Add basic Z80 CPU support
Date: Thu, 15 Jul 2021 10:19:30 +0300	[thread overview]
Message-ID: <CAOe0RDxtuaKj000x0tA_s1WDnaoy_8dpyAmw6nJWtVE8J0QYZg@mail.gmail.com> (raw)
In-Reply-To: <9a541e64-a21e-67cf-3242-62c0f1992c25@polymtl.ca>

Hi,
Looks good for me

чт, 15 июл. 2021 г., 05:29 Simon Marchi <simon.marchi@polymtl.ca>:

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

  reply	other threads:[~2021-07-15  7:20 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-23  9:21 [PATCH] Add Zilog Z80 CPU (and derivatives) support Sergey Belyashov via Gdb-patches
2020-09-24  2:55 ` Simon Marchi
2020-09-24  2:57   ` Simon Marchi
2020-09-24  8:26     ` [PATCH] [gdb] Add Z80 CPU basic support sergey.belyashov--- via Gdb-patches
2020-09-24 14:08       ` Simon Marchi
2020-09-24 15:21         ` Sergey Belyashov via Gdb-patches
2020-09-24 15:42           ` Simon Marchi
2020-09-24 15:22         ` [PATCH v3] " Sergey Belyashov via Gdb-patches
2020-09-24 15:44           ` Simon Marchi
2020-09-25 11:40             ` [PATCH v4] [gdb] Add basic Z80 CPU support Sergey Belyashov via Gdb-patches
2020-10-06 10:17               ` Sergey Belyashov via Gdb-patches
2020-10-07  3:07                 ` Simon Marchi
2021-05-24 19:13               ` Joel Brobecker
2021-07-12 21:37               ` Simon Marchi via Gdb-patches
2021-07-13  5:42                 ` Sergey Belyashov via Gdb-patches
2021-07-13 13:01                   ` Simon Marchi via Gdb-patches
2021-07-13 13:28                     ` Sergey Belyashov via Gdb-patches
2021-07-15  2:23                       ` Simon Marchi via Gdb-patches
2021-07-15  2:29                         ` [PATCH v5] " Simon Marchi via Gdb-patches
2021-07-15  7:19                           ` Sergey Belyashov via Gdb-patches [this message]
2020-09-25 12:40             ` [PATCH v3] [gdb] Add Z80 CPU basic support Sergey Belyashov via Gdb-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAOe0RDxtuaKj000x0tA_s1WDnaoy_8dpyAmw6nJWtVE8J0QYZg@mail.gmail.com \
    --to=gdb-patches@sourceware.org \
    --cc=sergey.belyashov@gmail.com \
    --cc=simon.marchi@polymtl.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox