diff options
Diffstat (limited to 'gdb/z80-tdep.c')
-rw-r--r-- | gdb/z80-tdep.c | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c new file mode 100644 index 0000000..7b9a7e2 --- /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, 1, insn_ret }, //"ret" + { 0313, 0377, 2, insn_default }, //CB prefix + { 0315, 0377, 4, insn_call_nn }, //"call Mmn" + { 0323, 0367, 2, insn_default }, //"out (n),a", "in a,(n)" + { 0335, 0337, 1, insn_adl_ddfd }, //DD/FD prefix + { 0351, 0377, 1, insn_jp_rr }, //"jp (hl)" + { 0355, 0377, 1, insn_adl_ed }, //ED prefix + { 0371, 0377, 1, insn_ld_sp_rr }, //"ld sp,hl" + { 0000, 0000, 1, insn_default } //others +}; + +/* ED prefix opcodes table. + Note the instruction length does include the ED prefix (+ 1 byte) +*/ +static const struct insn_info +ez80_ed_insn_table[] = +{ + /* eZ80 only instructions */ + { 0002, 0366, 2, insn_default }, //"lea rr,ii+d" + { 0124, 0376, 2, insn_default }, //"lea ix,iy+d", "lea iy,ix+d" + { 0145, 0377, 2, insn_default }, //"pea ix+d" + { 0146, 0377, 2, insn_default }, //"pea iy+d" + { 0164, 0377, 2, insn_default }, //"tstio n" + /* Z180/eZ80 only instructions */ + { 0060, 0376, 1, insn_default }, //not an instruction + { 0000, 0306, 2, insn_default }, //"in0 r,(n)", "out0 (n),r" + { 0144, 0377, 2, insn_default }, //"tst a, n" + /* common instructions */ + { 0173, 0377, 3, insn_ld_sp_6nn9 }, //"ld sp,(nn)" + { 0103, 0307, 3, insn_default }, //"ld (nn),rr", "ld rr,(nn)" + { 0105, 0317, 1, insn_ret }, //"retn", "reti" + { 0000, 0000, 1, insn_default } +}; + +static const struct insn_info +ez80_adl_ed_insn_table[] = +{ + { 0002, 0366, 2, insn_default }, //"lea rr,ii+d" + { 0124, 0376, 2, insn_default }, //"lea ix,iy+d", "lea iy,ix+d" + { 0145, 0377, 2, insn_default }, //"pea ix+d" + { 0146, 0377, 2, insn_default }, //"pea iy+d" + { 0164, 0377, 2, insn_default }, //"tstio n" + { 0060, 0376, 1, insn_default }, //not an instruction + { 0000, 0306, 2, insn_default }, //"in0 r,(n)", "out0 (n),r" + { 0144, 0377, 2, insn_default }, //"tst a, n" + { 0173, 0377, 4, insn_ld_sp_6nn9 }, //"ld sp,(nn)" + { 0103, 0307, 4, insn_default }, //"ld (nn),rr", "ld rr,(nn)" + { 0105, 0317, 1, insn_ret }, //"retn", "reti" + { 0000, 0000, 1, insn_default } +}; + +/* table for FD and DD prefixed instructions */ +static const struct insn_info +ez80_ddfd_insn_table[] = +{ + /* ez80 only instructions */ + { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)" + { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)" + /* common instructions */ + { 0011, 0367, 2, insn_default }, //"add ii,rr" + { 0041, 0377, 3, insn_default }, //"ld ii,nn" + { 0042, 0367, 3, insn_default }, //"ld (nn),ii", "ld ii,(nn)" + { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii" + { 0044, 0366, 1, insn_default }, //"inc/dec iih/iil" + { 0046, 0367, 2, insn_default }, //"ld iih,n", "ld iil,n" + { 0064, 0376, 2, insn_default }, //"inc (ii+d)", "dec (ii+d)" + { 0066, 0377, 2, insn_default }, //"ld (ii+d),n" + { 0166, 0377, 0, insn_default }, //not an instruction + { 0160, 0370, 2, insn_default }, //"ld (ii+d),r" + { 0104, 0306, 1, insn_default }, //"ld r,iih", "ld r,iil" + { 0106, 0307, 2, insn_default }, //"ld r,(ii+d)" + { 0140, 0360, 1, insn_default }, //"ld iih,r", "ld iil,r" + { 0204, 0306, 1, insn_default }, //"alu_op a,iih", "alu_op a,iil" + { 0206, 0307, 2, insn_default }, //"alu_op a,(ii+d)" + { 0313, 0377, 3, insn_default }, //DD/FD CB dd oo instructions + { 0335, 0337, 0, insn_force_nop}, //double DD/FD prefix, exec DD/FD as NOP + { 0341, 0373, 1, insn_default }, //"pop ii", "push ii" + { 0343, 0377, 1, insn_default }, //"ex (sp),ii" + { 0351, 0377, 1, insn_jp_rr }, //"jp (ii)" + { 0371, 0377, 1, insn_ld_sp_rr}, //"ld sp,ii" + { 0000, 0000, 0, insn_default } //not an instruction, exec DD/FD as NOP +}; + +static const struct insn_info +ez80_adl_ddfd_insn_table[] = +{ + { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)" + { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)" + { 0011, 0367, 1, insn_default }, //"add ii,rr" + { 0041, 0377, 4, insn_default }, //"ld ii,nn" + { 0042, 0367, 4, insn_default }, //"ld (nn),ii", "ld ii,(nn)" + { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii" + { 0044, 0366, 1, insn_default }, //"inc/dec iih/iil" + { 0046, 0367, 2, insn_default }, //"ld iih,n", "ld iil,n" + { 0064, 0376, 2, insn_default }, //"inc (ii+d)", "dec (ii+d)" + { 0066, 0377, 3, insn_default }, //"ld (ii+d),n" + { 0166, 0377, 0, insn_default }, //not an instruction + { 0160, 0370, 2, insn_default }, //"ld (ii+d),r" + { 0104, 0306, 1, insn_default }, //"ld r,iih", "ld r,iil" + { 0106, 0307, 2, insn_default }, //"ld r,(ii+d)" + { 0140, 0360, 1, insn_default }, //"ld iih,r", "ld iil,r" + { 0204, 0306, 1, insn_default }, //"alu_op a,iih", "alu_op a,iil" + { 0206, 0307, 2, insn_default }, //"alu_op a,(ii+d)" + { 0313, 0377, 3, insn_default }, //DD/FD CB dd oo instructions + { 0335, 0337, 0, insn_force_nop}, //double DD/FD prefix, exec DD/FD as NOP + { 0341, 0373, 1, insn_default }, //"pop ii", "push ii" + { 0343, 0377, 1, insn_default }, //"ex (sp),ii" + { 0351, 0377, 1, insn_jp_rr }, //"jp (ii)" + { 0371, 0377, 1, insn_ld_sp_rr}, //"ld sp,ii" + { 0000, 0000, 0, insn_default } //not an instruction, exec DD/FD as NOP +}; + +/* Return pointer to instruction information structure corresponded to opcode + in buf. */ +static const struct insn_info * +z80_get_insn_info (struct gdbarch *gdbarch, const gdb_byte *buf, int *size) +{ + int code; + const struct insn_info *info; + unsigned long mach = gdbarch_bfd_arch_info (gdbarch)->mach; + *size = 0; + switch (mach) + { + case bfd_mach_ez80_z80: + info = &ez80_main_insn_table[4]; /* skip force_nops */ + break; + case bfd_mach_ez80_adl: + info = &ez80_adl_main_insn_table[4]; /* skip force_nops */ + break; + default: + info = &ez80_main_insn_table[8]; /* skip eZ80 prefices and force_nops */ + break; + } + do + { + for (; ((code = buf[*size]) & info->mask) != info->code; ++info) + ; + *size += info->size; + /* process instruction type */ + switch (info->type) + { + case insn_z80: + if (mach == bfd_mach_ez80_z80 || mach == bfd_mach_ez80_adl) + info = &ez80_main_insn_table[0]; + else + info = &ez80_main_insn_table[8]; + break; + case insn_adl: + info = &ez80_adl_main_insn_table[0]; + break; + /* These two (for GameBoy Z80 & Z80 Next CPUs) haven't been tested. + + case bfd_mach_gbz80: + info = &gbz80_main_insn_table[0]; + break; + case bfd_mach_z80n: + info = &z80n_main_insn_table[0]; + break; + */ + case insn_z80_ddfd: + if (mach == bfd_mach_ez80_z80 || mach == bfd_mach_ez80_adl) + info = &ez80_ddfd_insn_table[0]; + else + info = &ez80_ddfd_insn_table[2]; + break; + case insn_adl_ddfd: + info = &ez80_adl_ddfd_insn_table[0]; + break; + case insn_z80_ed: + info = &ez80_ed_insn_table[0]; + break; + case insn_adl_ed: + info = &ez80_adl_ed_insn_table[0]; + break; + case insn_force_nop: + return NULL; + default: + return info; + } + } + while (1); +} + +extern initialize_file_ftype _initialize_z80_tdep; + +void +_initialize_z80_tdep () +{ + register_gdbarch_init (bfd_arch_z80, z80_gdbarch_init); + initialize_tdesc_z80 (); +} |