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,
next prev parent 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