From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id SMn0Nrjh72ACRQAAWB0awg (envelope-from ) for ; Thu, 15 Jul 2021 03:20:24 -0400 Received: by simark.ca (Postfix, from userid 112) id B45551E79C; Thu, 15 Jul 2021 03:20:24 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,MAILING_LIST_MULTI,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 58B3E1E79C for ; Thu, 15 Jul 2021 03:20:16 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 41AE43AA9410 for ; Thu, 15 Jul 2021 07:20:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 41AE43AA9410 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1626333615; bh=8VDMZVrwobJkvBRX+TqUQ7ufTUCEFxStGNKuO0zebf0=; h=References:In-Reply-To:Date:Subject:To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=uZb9C203c8wPm3AVTbpK43cvJMKC7MDbTAZ1+rErFGvsI3OBQLrBukeVahXxcVJtk v01zCBB9p719FDdk77LgvX8mx48UmOpSrCtTXzDL++O4AUZASWLU09ljC1NZ7IpsPO XQIiKVpCrmn24StClmVqmr5S8AyGoFWmATlKT4p8= Received: from mail-qt1-x82e.google.com (mail-qt1-x82e.google.com [IPv6:2607:f8b0:4864:20::82e]) by sourceware.org (Postfix) with ESMTPS id DEC0D3850425 for ; Thu, 15 Jul 2021 07:19:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DEC0D3850425 Received: by mail-qt1-x82e.google.com with SMTP id z25so3847534qto.12 for ; Thu, 15 Jul 2021 00:19:43 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=fZRSWEG3SluIX3lnPTJksg8bl2RSHH+Z0KnZXzJLkxA=; b=YmQlLWR9KoxCQdVjj1I11k1Rd0dmEcD68VyW9LeQl6fAMoqE2ZLGDdMYRj/4L6lF27 FRGfOMqU0c8gxtZLeUjO/LZxN2sQ/7zkIEc77jK7ZASKs3inyragVWIxZR2HeKDHHJ9Z +zlOTWTwfQFaJ72ulrLD7798OZIRZAnet2bgYu7ihnqqS7KrLFMtZn8k8Iy662nkrtPZ 3+O02VgRBHxPRobwDskhuWNlBlsQd3cnhqDYIjuUcyE2tFA75lhsX1VZ9ZiioKo91o23 u/IsFbISeGpDpGsR3T0wbJX6DmTvRRtMZ3CFzvxpQi5dqTMBoN2FoCZ2pgLs/oWk86um lTQw== X-Gm-Message-State: AOAM531IHy9QWFFdSbxKu9hGAng+fgLMq1D1IP5rzBU0h2uCXqDOy9CI WIZE4YI1WZxRfEl0LM2LFDu8iuosJB59RoHye9g= X-Google-Smtp-Source: ABdhPJx7jswAlVU37RQRfdC7vAbKF24QiCo8sJioFIwSPkjXwE9aV+1haxgKIZcyqza+5OR8L3i+WIh1nZ47iZVgyDE= X-Received: by 2002:ac8:13c4:: with SMTP id i4mr2789406qtj.136.1626333583287; Thu, 15 Jul 2021 00:19:43 -0700 (PDT) MIME-Version: 1.0 References: <5a120c4d-630a-08f5-fb81-d35237267adb@simark.ca> <20200925114042.14337-1-Sergey.Belyashov@gmail.com> <5ad11b8d-bc58-6a6f-7781-2805f7376736@polymtl.ca> <32cf79e9-95c1-7b15-9eab-9b921356287f@polymtl.ca> <9a541e64-a21e-67cf-3242-62c0f1992c25@polymtl.ca> In-Reply-To: <9a541e64-a21e-67cf-3242-62c0f1992c25@polymtl.ca> Date: Thu, 15 Jul 2021 10:19:30 +0300 Message-ID: Subject: Re: [PATCH v5] [gdb] Add basic Z80 CPU support To: Simon Marchi Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Belyashov via Gdb-patches Reply-To: Sergey Belyashov Cc: gdb-patches@sourceware.org Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org Sender: "Gdb-patches" Hi, Looks good for me =D1=87=D1=82, 15 =D0=B8=D1=8E=D0=BB. 2021 =D0=B3., 05:29 Simon Marchi : > 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 > 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=3Ddocs/z80/um0080&ex= tn=3D.pdf > Z180: > https://www.zilog.com/manage_directlink.php?filepath=3Ddocs/z180/ps0140&e= xtn=3D.pdf > eZ80: > http://www.zilog.com/force_download.php?filepath=3DYUhSMGNEb3ZMM2QzZHk1Nm= FXeHZaeTVqYjIwdlpHOWpjeTlWVFRBd056Y3VjR1Jt > > 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 =3D \ > 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 =3D 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=3D"xtensa-linux-tdep.o symfile-mem.o linux-tdep.o" > ;; > +z80*) > + # Target: Z80 > + gdb_target_obs=3D"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 =3D \ > s390x-tevx-linux64.xml \ > s390x-vx-linux64.xml \ > s390-gs-linux64.xml \ > - s390x-gs-linux64.xml > + s390x-gs-linux64.xml \ > + z80.xml > > TDESC_CFILES =3D $(patsubst %.xml,%.c,$(XMLTOC)) > GDB =3D 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 @@ > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > + > 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 =3D allocate_target_description (); > + set_tdesc_architecture (result.get (), bfd_scan_arch ("z80")); > + > + struct tdesc_feature *feature; > + > + feature =3D tdesc_create_feature (result.get (), "org.gnu.gdb.z80.cpu"= ); > + tdesc_type_with_fields *type_with_fields; > + type_with_fields =3D 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 =3D 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 @@ > + > + > + > + > + > + z80 > + > + > 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 . > */ > + > +/* 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 star= t. > + 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, gbz8= 0 > 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 writ= e > 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 consol= e > */ > +//#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 befor= e > return > + control to the program. It is useful when gdb-stub is placed in one o= f > 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 "\ > +\ > + \ > +\ > + \ > +\ > +" > +*/ > +#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) !=3D 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 > + > +#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 =3D DBG_SWBREAK_RST > +#else > +_break_handler =3D _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 =3D 'O'; > + for (; *str !=3D '\0'; ) > + { > + char c =3D high_hex (*str); > + csum +=3D c; > + putDebugChar (c); > + c =3D low_hex (*str++); > + csum +=3D 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) =3D (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 =3D (signed char)ex; > + store_pc_sp (pc_adj); > + > + DBG_ENTER > + > + /* after starting gdb_stub must always return stop reason */ > + *buffer =3D '?'; > + 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 <=3D 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 () !=3D '$'); > +retry: > + csum =3D 0; > + esc =3D 0; > + p =3D buffer; > + count =3D DBG_PACKET_SIZE; > + do > + { > + ch =3D getDebugChar (); > + switch (ch) > + { > + case '$': > + goto retry; > + case '#': > + goto finish; > + case '}': > + esc =3D 0x20; > + break; > + default: > + *p++ =3D ch ^ esc; > + esc =3D 0; > + --count; > + } > + csum +=3D ch; > + } > + while (count !=3D 0); > +finish: > + *p =3D '\0'; > + if (ch !=3D '#') /* packet is too large */ > + continue; > + ch =3D getDebugChar (); > + if (ch !=3D high_hex (csum)) > + continue; > + ch =3D getDebugChar (); > + if (ch !=3D low_hex (csum)) > + continue; > + break; > + } > + putDebugChar ('+'); > +} > + > +static void > +put_packet (const char *buffer) > +{ > + /* $#. */ > + for (;;) > + { > + putDebugChar ('$'); > + char checksum =3D put_packet_info (buffer); > + putDebugChar ('#'); > + putDebugChar (high_hex(checksum)); > + putDebugChar (low_hex(checksum)); > + for (;;) > + { > + char c =3D 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 =3D 0; > + for (;;) > + { > + ch =3D *src++; > + if (ch =3D=3D '\0') > + break; > + if (ch =3D=3D '}' || ch =3D=3D '*' || ch =3D=3D '#' || ch =3D=3D '= $') > + { > + /* escape special characters */ > + putDebugChar ('}'); > + checksum +=3D '}'; > + ch ^=3D 0x20; > + } > + putDebugChar (ch); > + checksum +=3D ch; > + } > + return checksum; > +} > + > +static void > +store_pc_sp (int pc_adj) FASTCALL > +{ > + byte *sp =3D get_reg_value (&state[R_SP]); > + byte *pc =3D get_reg_value (sp); > + pc +=3D 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++ =3D 'S'; > + sig =3D sigval; > + if (sig <=3D 0) > + sig =3D EX_SIGTRAP; > + p =3D byte2hex (p, (byte)sig); > + *p =3D '\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++ =3D 'T'; > + sig =3D sigval; > + if (sig <=3D 0) > + sig =3D EX_SIGTRAP; > + p =3D byte2hex (p, (byte)sig); > + p =3D format_reg_value(p, R_AF/REG_SIZE, &state[R_AF]); > + p =3D format_reg_value(p, R_SP/REG_SIZE, &state[R_SP]); > + p =3D 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 =3D 0; > + switch (sigval) > + { > +#ifdef DBG_SWBREAK_PROC > + case EX_SWBREAK: > + reason =3D "swbreak"; > + break; > +#endif > +#ifdef DBG_HWBREAK > + case EX_HWBREAK: > + reason =3D "hwbreak"; > + break; > +#endif > +#ifdef DBG_WWATCH > + case EX_WWATCH: > + reason =3D "watch"; > + addr =3D 1; > + break; > +#endif > +#ifdef DBG_RWATCH > + case EX_RWATCH: > + reason =3D "rwatch"; > + addr =3D 1; > + break; > +#endif > +#ifdef DBG_AWATCH > + case EX_AWATCH: > + reason =3D "awatch"; > + addr =3D 1; > + break; > +#endif > + default: > + goto finish; > + } > + while ((*p++ =3D *reason++)) > + ; > + --p; > + *p++ =3D ':'; > + if (addr !=3D 0) > + p =3D int2hex(p, addr); > + *p++ =3D ';'; > +finish: > +#endif /* DBG_HWBREAK, DBG_WWATCH, DBG_RWATCH, DBG_AWATCH */ > + *p++ =3D '\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) =3D=3D 0) > + { > + memcpy (buffer, "PacketSize=3D", 11); > + p =3D int2hex (&buffer[11], DBG_PACKET_SIZE); > +#ifndef DBG_MIN_SIZE > +#ifdef DBG_SWBREAK_PROC > + memcpy (p, ";swbreak+", 9); > + p +=3D 9; > +#endif > +#ifdef DBG_HWBREAK > + memcpy (p, ";hwbreak+", 9); > + p +=3D 9; > +#endif > +#endif /* DBG_MIN_SIZE */ > + > +#ifdef DBG_MEMORY_MAP > + memcpy (p, ";qXfer:memory-map:read+", 23); > + p +=3D 23; > +#endif > + *p =3D '\0'; > + return 0; > + } > +#ifdef DBG_MEMORY_MAP > + if (memcmp (buffer + 1, "Xfer:memory-map:read:", 21) =3D=3D 0) > + { > + p =3D strchr (buffer + 1 + 21, ':'); > + if (p =3D=3D NULL) > + return 1; > + ++p; > + unsigned offset =3D hex2int (&p); > + if (*p++ !=3D ',') > + return 2; > + unsigned length =3D hex2int (&p); > + if (length =3D=3D 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) =3D=3D 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 =3D '\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 =3D '\0'; > + return 0; > +} > + > +static signed char > +process_m (char *buffer) FASTCALL > +{/* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ > + char *p =3D &buffer[1]; > + byte *addr =3D (void*)hex2int(&p); > + if (*p++ !=3D ',') > + return 1; > + unsigned len =3D (unsigned)hex2int(&p); > + if (len =3D=3D 0) > + return 2; > + if (len > DBG_PACKET_SIZE/2) > + return 3; > + p =3D buffer; > +#ifdef DBG_MEMCPY > + do > + { > + byte tmp[16]; > + unsigned tlen =3D sizeof(tmp); > + if (tlen > len) > + tlen =3D len; > + if (!DBG_MEMCPY(tmp, addr, tlen)) > + return 4; > + p =3D mem2hex (p, tmp, tlen); > + addr +=3D tlen; > + len -=3D tlen; > + } > + while (len); > +#else > + p =3D 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 =3D &buffer[1]; > + byte *addr =3D (void*)hex2int(&p); > + if (*p !=3D ',') > + return 1; > + ++p; > + unsigned len =3D (unsigned)hex2int(&p); > + if (*p++ !=3D ':') > + return 2; > + if (len =3D=3D 0) > + goto end; > + if (len*2 + (p - buffer) > DBG_PACKET_SIZE) > + return 3; > +#ifdef DBG_MEMCPY > + do > + { > + byte tmp[16]; > + unsigned tlen =3D sizeof(tmp); > + if (tlen > len) > + tlen =3D len; > + p =3D hex2mem (tmp, p, tlen); > + if (!DBG_MEMCPY(addr, tmp, tlen)) > + return 4; > + addr +=3D tlen; > + len -=3D tlen; > + } > + while (len); > +#else > + hex2mem (addr, p, len); > +#endif > +end: > + /* OK response */ > + *buffer =3D '\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 =3D &buffer[1]; > + byte *addr =3D (void*)hex2int(&p); > + if (*p !=3D ',') > + return 1; > + ++p; > + unsigned len =3D (unsigned)hex2int(&p); > + if (*p++ !=3D ':') > + return 2; > + if (len =3D=3D 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 =3D '\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 =3D &buffer[1]; > + if (*p !=3D '\0') > + { > + void *addr =3D (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 =3D '\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) =3D=3D 0) > + { > + if (buffer[5] =3D=3D '?') > + { > + /* 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] =3D '\0'; > + if (buffer[5] =3D=3D ';' && (buffer[6] =3D=3D 'c' || buffer[6] =3D= =3D '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 =3D (*buffer =3D=3D 'Z'); > + const char *p =3D &buffer[3]; > + void *addr =3D (void*)hex2int(&p); > + if (*p !=3D ',') > + return 1; > + p++; > + int kind =3D hex2int(&p); > + *buffer =3D '\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 =3D do_process (buffer); > + char *p =3D buffer; > + char ret =3D 1; > + if (err =3D=3D -2) > + { > + ret =3D 0; > + err =3D 0; > + } > + if (err > 0) > + { > + *p++ =3D 'E'; > + p =3D byte2hex (p, err); > + *p =3D '\0'; > + } > + else if (err < 0) > + { > + *p =3D '\0'; > + } > + else if (*p =3D=3D '\0') > + memcpy(p, "OK", 3); > + return ret; > +} > + > +static char * > +byte2hex (char *p, byte v) > +{ > + *p++ =3D high_hex (v); > + *p++ =3D low_hex (v); > + return p; > +} > + > +static signed char > +hex2val (unsigned char hex) FASTCALL > +{ > + if (hex <=3D '9') > + return hex - '0'; > + hex &=3D 0xdf; /* make uppercase */ > + hex -=3D 'A' - 10; > + return (hex >=3D 10 && hex < 16) ? hex : -1; > +} > + > +static int > +hex2byte (const char *p) FASTCALL > +{ > + signed char h =3D hex2val (p[0]); > + signed char l =3D 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 =3D 0; > + for (;; (*buf)++) > + { > + signed char a =3D hex2val(**buf); > + if (a < 0) > + break; > + r <<=3D 4; > + r +=3D (byte)a; > + } > + return (int)r; > +} > + > +static char * > +int2hex (char *buf, int v) > +{ > + buf =3D 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 &=3D 0x0f; > + v +=3D '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 =3D buf; > + if (bytes !=3D 0) > + { > + do > + { > + d =3D byte2hex (d, *mem++); > + } > + while (--bytes); > + } > + *d =3D 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 !=3D 0) > + { > + do > + { > + *mem++ =3D hex2byte (buf); > + buf +=3D 2; > + } > + while (--bytes); > + } > + return buf; > +} > + > +#ifdef DBG_MEMORY_MAP > +static void > +read_memory_map (char *buffer, unsigned offset, unsigned length) > +{ > + const char *map =3D DBG_MEMORY_MAP; > + const unsigned map_sz =3D strlen(map); > + if (offset >=3D map_sz) > + { > + buffer[0] =3D 'l'; > + buffer[1] =3D '\0'; > + return; > + } > + if (offset + length > map_sz) > + length =3D map_sz - offset; > + buffer[0] =3D 'm'; > + memcpy (&buffer[1], &map[offset], length); > + buffer[1+length] =3D '\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 =3D p; > + unsigned char i; > + d =3D byte2hex(d, reg_num); > + *d++ =3D ':'; > + value +=3D REG_SIZE; > + i =3D REG_SIZE; > + do > + { > + d =3D byte2hex(d, *--value); > + } > + while (--i !=3D 0); > + *d++ =3D ';'; > + 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 . > */ > + > +#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_handle= r > + 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 functi= on > + 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: > + [] > + [] > + [ > + ] > + [] > + In simplest case is pointer to the call instruction > + (or call __call_hl). There are more difficult cases: interrupt handle= r > 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 =3D next_frame.prev_sp */ > + ULONGEST size; > + > + /* size of saved state (including frame pointer and return address), > + assume: prev_sp + size =3D 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[] =3D > +{ > + /* 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 >=3D 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 =3D 1; > + return Z80_BC_REGNUM | 0x100; > + case 0xd1: > + *size =3D 1; > + return Z80_DE_REGNUM | 0x100; > + case 0xe1: > + *size =3D 1; > + return Z80_HL_REGNUM | 0x100; > + case 0xf1: > + *size =3D 1; > + return Z80_AF_REGNUM | 0x100; > + case 0xdd: > + *size =3D 2; > + return (buf[1] =3D=3D 0xe1) ? (Z80_IX_REGNUM | 0x100) : 0; > + case 0xfd: > + *size =3D 2; > + return (buf[1] =3D=3D 0xe1) ? (Z80_IY_REGNUM | 0x100) : 0; > + } > + *size =3D 0; > + return 0; > +} > + > +static int > +z80_is_push_rr (const gdb_byte buf[], int *size) > +{ > + switch (buf[0]) > + { > + case 0xc5: > + *size =3D 1; > + return Z80_BC_REGNUM | 0x100; > + case 0xd5: > + *size =3D 1; > + return Z80_DE_REGNUM | 0x100; > + case 0xe5: > + *size =3D 1; > + return Z80_HL_REGNUM | 0x100; > + case 0xf5: > + *size =3D 1; > + return Z80_AF_REGNUM | 0x100; > + case 0xdd: > + *size =3D 2; > + return (buf[1] =3D=3D 0xe5) ? (Z80_IX_REGNUM | 0x100) : 0; > + case 0xfd: > + *size =3D 2; > + return (buf[1] =3D=3D 0xe5) ? (Z80_IY_REGNUM | 0x100) : 0; > + } > + *size =3D 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 =3D gdbarch_byte_order (gdbarch); > + int addr_len =3D gdbarch_tdep (gdbarch)->addr_length; > + gdb_byte prologue[32]; /* max prologue is 24 bytes: __interrupt with > local array */ > + int pos =3D 0; > + int len; > + int reg; > + CORE_ADDR value; > + > + len =3D pc_end - pc_beg; > + if (len > (int)sizeof (prologue)) > + len =3D sizeof (prologue); > + > + read_memory (pc_beg, prologue, len); > + > + /* stage0: check for series of POPs and then PUSHs */ > + if ((reg =3D z80_is_pop_rr(prologue, &pos))) > + { > + int i; > + int size =3D pos; > + gdb_byte regs[8]; /* Z80 have only 6 register pairs */ > + regs[0] =3D reg & 0xff; > + for (i =3D 1; i < 8 && (regs[i] =3D z80_is_pop_rr (&prologue[pos], > &size)); > + ++i, pos +=3D size); > + /* now we expect series of PUSHs in reverse order */ > + for (--i; i >=3D 0 && regs[i] =3D=3D z80_is_push_rr (&prologue[pos= ], > &size); > + --i, pos +=3D size); > + if (i =3D=3D -1 && pos > 0) > + info->prologue_type.load_args =3D 1; > + else > + pos =3D 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 =3D 1; > + pos +=3D 4; > + info->state_size +=3D 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 =3D 1; > + pos +=3D 6; > + info->state_size +=3D addr_len * 5; > + } > + > + /* stage2: check for FP saving scheme */ > + if (prologue[pos] =3D=3D 0xcd) /* call nn */ > + { > + struct bound_minimal_symbol msymbol; > + msymbol =3D lookup_minimal_symbol ("__sdcc_enter_ix", NULL, NULL); > + if (msymbol.minsym) > + { > + value =3D BMSYMBOL_VALUE_ADDRESS (msymbol); > + if (value =3D=3D extract_unsigned_integer (&prologue[pos+1], > addr_len, byte_order)) > + { > + pos +=3D 1 + addr_len; > + info->prologue_type.fp_sdcc =3D 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 +=3D 4 + addr_len + 4; > + info->prologue_type.fp_sdcc =3D 1; > + } > + else if (!memcmp (&prologue[pos], "\335\345", 2)) > + { /* push ix */ > + pos +=3D 2; > + info->prologue_type.fp_sdcc =3D 1; > + } > + > + /* stage3: check for local variables allocation */ > + switch (prologue[pos]) > + { > + case 0xf5: /* push af */ > + info->size =3D 0; > + while (prologue[pos] =3D=3D 0xf5) > + { > + info->size +=3D addr_len; > + pos++; > + } > + if (prologue[pos] =3D=3D 0x3b) /* dec sp */ > + { > + info->size++; > + pos++; > + } > + break; > + case 0x3b: /* dec sp */ > + info->size =3D 0; > + while (prologue[pos] =3D=3D 0x3b) > + { > + info->size++; > + pos++; > + } > + break; > + case 0x21: /*ld hl, -nn */ > + if (prologue[pos+addr_len] =3D=3D 0x39 && prologue[pos+addr_len] = >=3D > 0x80 && > + prologue[pos+addr_len+1] =3D=3D 0xf9) > + { /* add hl, sp; ld sp, hl */ > + info->size =3D -extract_signed_integer(&prologue[pos+1], > addr_len, byte_order); > + pos +=3D 1 + addr_len + 2; > + } > + break; > + case 0xfd: /* ld iy, -nn */ > + if (prologue[pos+1] =3D=3D 0x21 && prologue[pos+1+addr_len] >=3D = 0x80 && > + !memcmp (&prologue[pos+2+addr_len], "\375\071\375\371", 4)) > + { > + info->size =3D -extract_signed_integer(&prologue[pos+2], > addr_len, byte_order); > + pos +=3D 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] >=3D 0x80 && prologue[pos+3] =3D=3D 0xf9) > + { /* ld sp, hl */ > + info->size =3D -extract_signed_integer(&prologue[pos+2], = 1, > byte_order); > + pos +=3D 4; > + } > + break; > + case 0x55: /* lea iy, ix - n */ > + if (prologue[pos+2] >=3D 0x80 && prologue[pos+3] =3D=3D 0xfd = && > + prologue[pos+4] =3D=3D 0xf9) > + { /* ld sp, iy */ > + info->size =3D -extract_signed_integer(&prologue[pos+2], = 1, > byte_order); > + pos +=3D 5; > + } > + break; > + } > + break; > + } > + len =3D 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 +=3D 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 =3D skip_prologue_using_sal (gdbarch, func_addr); > + if (prologue_end !=3D 0) > + return std::max (pc, prologue_end); > + > + { > + struct z80_unwind_cache info =3D {0}; > + struct trad_frame_saved_reg saved_regs[Z80_NUM_REGS]; > + > + info.saved_regs =3D saved_regs; > + > + /* Need to run the prologue scanner to figure out if the function ha= s > a > + prologue. */ > + > + prologue_end =3D 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 !=3D 0) > + { > + struct symtab_and_line prologue_sal =3D find_pc_line (func_addr, 0= ); > + struct compunit_symtab *compunit =3D SYMTAB_COMPUNIT > (prologue_sal.symtab); > + const char *debug_format =3D COMPUNIT_DEBUGFORMAT (compunit); > + > + if (debug_format !=3D 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 =3D TYPE_LENGTH (valtype); > + > + if ((valtype->code () =3D=3D TYPE_CODE_STRUCT > + || valtype->code () =3D=3D TYPE_CODE_UNION > + || valtype->code () =3D=3D TYPE_CODE_ARRAY) > + && len > 4) > + return RETURN_VALUE_STRUCT_CONVENTION; > + > + if (writebuf !=3D NULL) > + { > + if (len > 2) > + { > + regcache->cooked_write_part (Z80_DE_REGNUM, 0, len - 2, > writebuf+2); > + len =3D 2; > + } > + regcache->cooked_write_part (Z80_HL_REGNUM, 0, len, writebuf); > + } > + > + if (readbuf !=3D NULL) > + { > + if (len > 2) > + { > + regcache->cooked_read_part (Z80_DE_REGNUM, 0, len - 2, > readbuf+2); > + len =3D 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 =3D get_frame_arch (this_frame); > + int addr_len =3D gdbarch_tdep (gdbarch)->addr_length; > + > + if (*this_prologue_cache) > + return (struct z80_unwind_cache *) *this_prologue_cache; > + > + info =3D FRAME_OBSTACK_ZALLOC (struct z80_unwind_cache); > + memset (info, 0, sizeof (*info)); > + info->saved_regs =3D trad_frame_alloc_saved_regs (this_frame); > + *this_prologue_cache =3D info; > + > + start_pc =3D get_frame_func (this_frame); > + current_pc =3D get_frame_pc (this_frame); > + if ((start_pc > 0) && (start_pc <=3D 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 =3D get_frame_register_unsigned (this_frame, Z80_IX_REGN= UM); > + info->prev_sp =3D this_base + info->size; > + } > + else > + { > + CORE_ADDR addr; > + CORE_ADDR sp; > + CORE_ADDR sp_mask =3D (1 << gdbarch_ptr_bit(gdbarch)) - 1; > + enum bfd_endian byte_order =3D gdbarch_byte_order (gdbarch); > + /* Assume that the FP is this frame's SP but with that pushed > + stack space added back. */ > + this_base =3D get_frame_register_unsigned (this_frame, Z80_SP_REGN= UM); > + sp =3D this_base + info->size; > + for (;; ++sp) > + { > + sp &=3D sp_mask; > + if (sp < this_base) > + { /* overflow, looks like end of stack */ > + sp =3D this_base + info->size; > + break; > + } > + /* find return address */ > + read_memory (sp, buf, addr_len); > + addr =3D extract_unsigned_integer(buf, addr_len, byte_order); > + read_memory (addr-addr_len-1, buf, addr_len+1); > + if (buf[0] =3D=3D 0xcd || (buf[0] & 0307) =3D=3D 0304) /* Is it= CALL */ > + { /* CALL nn or CALL cc,nn */ > + static const char *names[] =3D > + { > + "__sdcc_call_ix", "__sdcc_call_iy", "__sdcc_call_hl" > + }; > + addr =3D extract_unsigned_integer(buf+1, addr_len, byte_ord= er); > + if (addr =3D=3D start_pc) > + break; /* found */ > + for (i =3D sizeof(names)/sizeof(*names)-1; i >=3D 0; --i) > + { > + struct bound_minimal_symbol msymbol; > + msymbol =3D lookup_minimal_symbol (names[i], NULL, NULL= ); > + if (!msymbol.minsym) > + continue; > + if (addr =3D=3D BMSYMBOL_VALUE_ADDRESS (msymbol)) > + break; > + } > + if (i >=3D 0) > + break; > + continue; > + } > + else > + continue; /* it is not call_nn, call_cc_nn */ > + } > + info->prev_sp =3D sp; > + } > + > + /* Adjust all the saved registers so that they contain addresses and n= ot > + offsets. */ > + for (i =3D 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 =3D get_frame_func (this_frame); > + > + info =3D 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 =3D info->prev_sp; > + if (base =3D=3D 0) > + return; > + > + id =3D frame_id_build (base, func); > + *this_id =3D id; > +} > + > +static struct value * > +z80_frame_prev_register (struct frame_info *this_frame, > + void **this_prologue_cache, int regnum) > +{ > + struct z80_unwind_cache *info > + =3D z80_frame_unwind_cache (this_frame, this_prologue_cache); > + > + if (regnum =3D=3D 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 =3D get_frame_arch (this_frame); > + struct gdbarch_tdep *tdep =3D gdbarch_tdep (gdbarch); > + enum bfd_endian byte_order =3D gdbarch_byte_order (gdbarch); > + > + read_memory (info->saved_regs[Z80_PC_REGNUM].addr (), > + buf, tdep->addr_length); > + pc =3D 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 =3D -1; > + if (addr =3D=3D -1) > + { > + struct bound_minimal_symbol bh; > + bh =3D lookup_minimal_symbol ("_break_handler", NULL, NULL); > + if (bh.minsym) > + addr =3D 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 =3D 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 i= s > 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 *siz= e) > +{ > + static gdb_byte break_insn[8]; > + > + if ((kind & 070) =3D=3D kind) > + { > + break_insn[0] =3D kind | 0307; > + *size =3D 1; > + } > + else /* kind is non-RST address, use CALL instead, but it is dungerous > */ > + { > + gdb_byte *p =3D break_insn; > + *p++ =3D 0xcd; > + *p++ =3D (kind >> 0) & 0xff; > + *p++ =3D (kind >> 8) & 0xff; > + if (gdbarch_tdep (gdbarch)->addr_length > 2) > + *p++ =3D (kind >> 16) & 0xff; > + *size =3D 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 > +z80_software_single_step (struct regcache *regcache) > +{ > + static const int flag_mask[] =3D {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 ret (1); > + struct gdbarch *gdbarch =3D target_gdbarch (); > + > + regcache->cooked_read (Z80_PC_REGNUM, &addr); > + read_memory (addr, buf, sizeof(buf)); > + info =3D z80_get_insn_info (gdbarch, buf, &size); > + ret[0] =3D addr + size; > + if (info =3D=3D NULL) /* possible in case of double prefix */ > + { /* forced NOP, TODO: replace by NOP */ > + return ret; > + } > + opcode =3D 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) !=3D 0x100) > + return ret; > + break; > + case insn_jr_cc_d: > + opcode &=3D 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) !=3D 0) > + t =3D ~t; > + /* two higher bits of condition field defines flag, so use them on= ly > + 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 +=3D size; > + addr +=3D (signed char)buf[size-1]; > + break; > + case insn_jp_rr: > + if (size =3D=3D 1) > + opcode =3D Z80_HL_REGNUM; > + else > + opcode =3D (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 =3D buf[size-1] * 0x100 + buf[size-2]; > + if (info->size > 3) /* long instruction mode */ > + addr =3D addr * 0x100 + buf[size-3]; > + break; > + case insn_rst_n: > + addr =3D opcode & 070; > + break; > + case insn_ret: > + case insn_ret_cc: > + regcache->cooked_read (Z80_SP_REGNUM, &addr); > + read_memory (addr, buf, 3); > + addr =3D buf[1] * 0x100 + buf[0]; > + if (gdbarch_bfd_arch_info (gdbarch)->mach =3D=3D bfd_mach_ez80_adl= ) > + addr =3D addr * 0x100 + buf[2]; > + break; > + } > + ret[0] =3D addr; > + return ret; > +} > + > +/* Cached, dynamically allocated copies of the target data structures: *= / > +static unsigned (*cache_ovly_region_table)[3] =3D 0; > +static unsigned cache_novly_regions; > +static CORE_ADDR cache_ovly_region_table_base =3D 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 =3D 0; > + cache_ovly_region_table =3D NULL; > + cache_ovly_region_table_base =3D 0; > +} > + > +/* Read an array of ints of size SIZE from the target into a local buffe= r. > + 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 =3D (gdb_byte *) alloca (len * size); > + int i; > + > + read_memory (memaddr, buf, len * size); > + for (i =3D 0; i < len; i++) > + myaddr[i] =3D 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 =3D 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 =3D 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 =3D overlay_debugging= ; > + /* prevent infinite recurse */ > + overlay_debugging =3D ovly_off; > + > + gdbarch =3D ovly_region_table_msym.objfile->arch (); > + word_size =3D gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT; > + byte_order =3D gdbarch_byte_order (gdbarch); > + > + cache_novly_regions =3D read_memory_integer ( > + BMSYMBOL_VALUE_ADDRESS > (novly_regions_msym), > + 4, byte_order); > + cache_ovly_region_table > + =3D (unsigned int (*)[3]) xmalloc (cache_novly_regions * > + sizeof (*cache_ovly_region_table)= ); > + cache_ovly_region_table_base > + =3D 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 =3D save_ovly_dbg; > + return 1; /* SUCCESS */ > +} > + > +static int > +z80_overlay_update_1 (struct obj_section *osect) > +{ > + int i; > + asection *bsect =3D osect->the_bfd_section; > + unsigned lma; > + unsigned vma =3D bfd_section_vma (bsect); > + > + /* find region corresponding to the section VMA */ > + for (i =3D 0; i < cache_novly_regions; i++) > + if (cache_ovly_region_table[i][VMA] =3D=3D vma) > + break; > + if (i =3D=3D cache_novly_regions) > + return 0; /* no such region */ > + > + lma =3D cache_ovly_region_table[i][MAPPED_TO_LMA]; > + i =3D 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 =3D (lma =3D=3D bfd_section_lma > (osect->the_bfd_section)); > + i |=3D 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 !=3D 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 =3D osect->the_bfd_section; > + bfd_vma lma =3D bfd_section_lma (bsect); > + bfd_vma vma =3D bfd_section_vma (bsect); > + > + for (int i =3D 0; i < cache_novly_regions; ++i) > + if (cache_ovly_region_table[i][VMA] =3D=3D vma) > + osect->ovly_mapped =3D > + (cache_ovly_region_table[i][MAPPED_TO_LMA] =3D=3D 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 =3D 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 =3D 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 =3D 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 =3D > +{ > + "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 =3D info.bfd_arch_info->mach; > + const struct target_desc *tdesc =3D info.target_desc; > + > + if (!tdesc_has_registers (tdesc)) > + /* Pick a default target description. */ > + tdesc =3D tdesc_z80; > + > + /* Check any target description for validity. */ > + if (tdesc_has_registers (tdesc)) > + { > + const struct tdesc_feature *feature; > + int valid_p; > + > + feature =3D tdesc_find_feature (tdesc, "org.gnu.gdb.z80.cpu"); > + if (feature =3D=3D NULL) > + return NULL; > + > + tdesc_data =3D tdesc_data_alloc (); > + > + valid_p =3D 1; > + > + for (unsigned i =3D 0; i < Z80_NUM_REGS; i++) > + valid_p &=3D 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 =3D gdbarch_list_lookup_by_info (arches, &info); > + best_arch !=3D NULL; > + best_arch =3D gdbarch_list_lookup_by_info (best_arch->next, &info= )) > + { > + if (mach =3D=3D gdbarch_bfd_arch_info (best_arch->gdbarch)->mach) > + return best_arch->gdbarch; > + } > + > + /* None found, create a new architecture from the information > provided. */ > + tdep =3D XCNEW (struct gdbarch_tdep); > + gdbarch =3D gdbarch_alloc (&info, tdep); > + > + if (mach =3D=3D bfd_mach_ez80_adl) > + { > + tdep->addr_length =3D 3; > + set_gdbarch_max_insn_length (gdbarch, 6); > + } > + else > + { > + tdep->addr_length =3D 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 =3D arch_type (gdbarch, TYPE_CODE_VOID, TARGET_CHAR_BI= T, > + "void"); > + tdep->func_void_type =3D make_function_type (tdep->void_type, NULL); > + tdep->pc_type =3D 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 =3D=3D 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[] =3D > +{ /* 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[] =3D > +{ /* 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 instructio= n) > + { 0111, 0377, 1, insn_z80 }, //eZ80 mode prefix (short instructio= n) > + { 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,