| /* 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 (); |
| } |