/* Target-dependent code for Moxie. Copyright (C) 2009-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 "frame.h" #include "frame-unwind.h" #include "frame-base.h" #include "symtab.h" #include "gdbtypes.h" #include "gdbcmd.h" #include "gdbcore.h" #include "value.h" #include "inferior.h" #include "symfile.h" #include "objfiles.h" #include "osabi.h" #include "language.h" #include "arch-utils.h" #include "regcache.h" #include "trad-frame.h" #include "dis-asm.h" #include "record.h" #include "record-full.h" #include "moxie-tdep.h" #include <algorithm> /* Use an invalid address value as 'not available' marker. */ enum { REG_UNAVAIL = (CORE_ADDR) -1 }; struct moxie_frame_cache { /* Base address. */ CORE_ADDR base; CORE_ADDR pc; LONGEST framesize; CORE_ADDR saved_regs[MOXIE_NUM_REGS]; CORE_ADDR saved_sp; }; /* Implement the "frame_align" gdbarch method. */ static CORE_ADDR moxie_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) { /* Align to the size of an instruction (so that they can safely be pushed onto the stack. */ return sp & ~1; } constexpr gdb_byte moxie_break_insn[] = { 0x35, 0x00 }; typedef BP_MANIPULATION (moxie_break_insn) moxie_breakpoint; /* Moxie register names. */ static const char * const moxie_register_names[] = { "$fp", "$sp", "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$pc", "$cc" }; /* Implement the "register_name" gdbarch method. */ static const char * moxie_register_name (struct gdbarch *gdbarch, int reg_nr) { if (reg_nr < 0) return NULL; if (reg_nr >= MOXIE_NUM_REGS) return NULL; return moxie_register_names[reg_nr]; } /* Implement the "register_type" gdbarch method. */ static struct type * moxie_register_type (struct gdbarch *gdbarch, int reg_nr) { if (reg_nr == MOXIE_PC_REGNUM) return builtin_type (gdbarch)->builtin_func_ptr; else if (reg_nr == MOXIE_SP_REGNUM || reg_nr == MOXIE_FP_REGNUM) return builtin_type (gdbarch)->builtin_data_ptr; else return builtin_type (gdbarch)->builtin_int32; } /* Write into appropriate registers a function return value of type TYPE, given in virtual format. */ static void moxie_store_return_value (struct type *type, struct regcache *regcache, const gdb_byte *valbuf) { struct gdbarch *gdbarch = regcache->arch (); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR regval; int len = TYPE_LENGTH (type); /* Things always get returned in RET1_REGNUM, RET2_REGNUM. */ regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order); regcache_cooked_write_unsigned (regcache, RET1_REGNUM, regval); if (len > 4) { regval = extract_unsigned_integer (valbuf + 4, len - 4, byte_order); regcache_cooked_write_unsigned (regcache, RET1_REGNUM + 1, regval); } } /* Decode the instructions within the given address range. Decide when we must have reached the end of the function prologue. If a frame_info pointer is provided, fill in its saved_regs etc. Returns the address of the first instruction after the prologue. */ static CORE_ADDR moxie_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr, struct moxie_frame_cache *cache, struct gdbarch *gdbarch) { enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); CORE_ADDR next_addr; ULONGEST inst, inst2; LONGEST offset; int regnum; /* Record where the jsra instruction saves the PC and FP. */ cache->saved_regs[MOXIE_PC_REGNUM] = -4; cache->saved_regs[MOXIE_FP_REGNUM] = 0; cache->framesize = 0; if (start_addr >= end_addr) return end_addr; for (next_addr = start_addr; next_addr < end_addr; ) { inst = read_memory_unsigned_integer (next_addr, 2, byte_order); /* Match "push $sp $rN" where N is between 0 and 13 inclusive. */ if (inst >= 0x0612 && inst <= 0x061f) { regnum = inst & 0x000f; cache->framesize += 4; cache->saved_regs[regnum] = cache->framesize; next_addr += 2; } else break; } inst = read_memory_unsigned_integer (next_addr, 2, byte_order); /* Optional stack allocation for args and local vars <= 4 byte. */ if (inst == 0x01e0) /* ldi.l $r12, X */ { offset = read_memory_integer (next_addr + 2, 4, byte_order); inst2 = read_memory_unsigned_integer (next_addr + 6, 2, byte_order); if (inst2 == 0x291e) /* sub.l $sp, $r12 */ { cache->framesize += offset; } return (next_addr + 8); } else if ((inst & 0xff00) == 0x9100) /* dec $sp, X */ { cache->framesize += (inst & 0x00ff); next_addr += 2; while (next_addr < end_addr) { inst = read_memory_unsigned_integer (next_addr, 2, byte_order); if ((inst & 0xff00) != 0x9100) /* no more dec $sp, X */ break; cache->framesize += (inst & 0x00ff); next_addr += 2; } } return next_addr; } /* Find the end of function prologue. */ static CORE_ADDR moxie_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) { CORE_ADDR func_addr = 0, func_end = 0; const char *func_name; /* See if we can determine the end of the prologue via the symbol table. If so, then return either PC, or the PC after the prologue, whichever is greater. */ if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) { CORE_ADDR post_prologue_pc = skip_prologue_using_sal (gdbarch, func_addr); if (post_prologue_pc != 0) return std::max (pc, post_prologue_pc); else { /* Can't determine prologue from the symbol table, need to examine instructions. */ struct symtab_and_line sal; struct symbol *sym; struct moxie_frame_cache cache; CORE_ADDR plg_end; memset (&cache, 0, sizeof cache); plg_end = moxie_analyze_prologue (func_addr, func_end, &cache, gdbarch); /* Found a function. */ sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL).symbol; /* Don't use line number debug info for assembly source files. */ if (sym && sym->language () != language_asm) { sal = find_pc_line (func_addr, 0); if (sal.end && sal.end < func_end) { /* Found a line number, use it as end of prologue. */ return sal.end; } } /* No useable line symbol. Use result of prologue parsing method. */ return plg_end; } } /* No function symbol -- just return the PC. */ return (CORE_ADDR) pc; } struct moxie_unwind_cache { /* The previous frame's inner most stack address. Used as this frame ID's stack_addr. */ CORE_ADDR prev_sp; /* The frame's base, optionally used by the high-level debug info. */ CORE_ADDR base; int size; /* How far the SP and r13 (FP) have been offset from the start of the stack frame (as defined by the previous frame's stack pointer). */ LONGEST sp_offset; LONGEST r13_offset; int uses_frame; /* Table indicating the location of each and every register. */ trad_frame_saved_reg *saved_regs; }; /* Read an unsigned integer from the inferior, and adjust endianness. */ static ULONGEST moxie_process_readu (CORE_ADDR addr, gdb_byte *buf, int length, enum bfd_endian byte_order) { if (target_read_memory (addr, buf, length)) { if (record_debug) printf_unfiltered (_("Process record: error reading memory at " "addr 0x%s len = %d.\n"), paddress (target_gdbarch (), addr), length); return -1; } return extract_unsigned_integer (buf, length, byte_order); } /* Helper macro to extract the signed 10-bit offset from a 16-bit branch instruction. */ #define INST2OFFSET(o) ((((signed short)((o & ((1<<10)-1))<<6))>>6)<<1) /* Insert a single step breakpoint. */ static std::vector<CORE_ADDR> moxie_software_single_step (struct regcache *regcache) { struct gdbarch *gdbarch = regcache->arch (); CORE_ADDR addr; gdb_byte buf[4]; uint16_t inst; uint32_t tmpu32; ULONGEST fp; enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); std::vector<CORE_ADDR> next_pcs; addr = regcache_read_pc (regcache); inst = (uint16_t) moxie_process_readu (addr, buf, 2, byte_order); /* Decode instruction. */ if (inst & (1 << 15)) { if (inst & (1 << 14)) { /* This is a Form 3 instruction. */ int opcode = (inst >> 10 & 0xf); switch (opcode) { case 0x00: /* beq */ case 0x01: /* bne */ case 0x02: /* blt */ case 0x03: /* bgt */ case 0x04: /* bltu */ case 0x05: /* bgtu */ case 0x06: /* bge */ case 0x07: /* ble */ case 0x08: /* bgeu */ case 0x09: /* bleu */ /* Insert breaks on both branches, because we can't currently tell which way things will go. */ next_pcs.push_back (addr + 2); next_pcs.push_back (addr + 2 + INST2OFFSET(inst)); break; default: { /* Do nothing. */ break; } } } else { /* This is a Form 2 instruction. They are all 16 bits. */ next_pcs.push_back (addr + 2); } } else { /* This is a Form 1 instruction. */ int opcode = inst >> 8; switch (opcode) { /* 16-bit instructions. */ case 0x00: /* bad */ case 0x02: /* mov (register-to-register) */ case 0x05: /* add.l */ case 0x06: /* push */ case 0x07: /* pop */ case 0x0a: /* ld.l (register indirect) */ case 0x0b: /* st.l */ case 0x0e: /* cmp */ case 0x0f: /* nop */ case 0x10: /* sex.b */ case 0x11: /* sex.s */ case 0x12: /* zex.b */ case 0x13: /* zex.s */ case 0x14: /* umul.x */ case 0x15: /* mul.x */ case 0x16: case 0x17: case 0x18: case 0x1c: /* ld.b (register indirect) */ case 0x1e: /* st.b */ case 0x21: /* ld.s (register indirect) */ case 0x23: /* st.s */ case 0x26: /* and */ case 0x27: /* lshr */ case 0x28: /* ashl */ case 0x29: /* sub.l */ case 0x2a: /* neg */ case 0x2b: /* or */ case 0x2c: /* not */ case 0x2d: /* ashr */ case 0x2e: /* xor */ case 0x2f: /* mul.l */ case 0x31: /* div.l */ case 0x32: /* udiv.l */ case 0x33: /* mod.l */ case 0x34: /* umod.l */ next_pcs.push_back (addr + 2); break; /* 32-bit instructions. */ case 0x0c: /* ldo.l */ case 0x0d: /* sto.l */ case 0x36: /* ldo.b */ case 0x37: /* sto.b */ case 0x38: /* ldo.s */ case 0x39: /* sto.s */ next_pcs.push_back (addr + 4); break; /* 48-bit instructions. */ case 0x01: /* ldi.l (immediate) */ case 0x08: /* lda.l */ case 0x09: /* sta.l */ case 0x1b: /* ldi.b (immediate) */ case 0x1d: /* lda.b */ case 0x1f: /* sta.b */ case 0x20: /* ldi.s (immediate) */ case 0x22: /* lda.s */ case 0x24: /* sta.s */ next_pcs.push_back (addr + 6); break; /* Control flow instructions. */ case 0x03: /* jsra */ case 0x1a: /* jmpa */ next_pcs.push_back (moxie_process_readu (addr + 2, buf, 4, byte_order)); break; case 0x04: /* ret */ regcache_cooked_read_unsigned (regcache, MOXIE_FP_REGNUM, &fp); next_pcs.push_back (moxie_process_readu (fp + 4, buf, 4, byte_order)); break; case 0x19: /* jsr */ case 0x25: /* jmp */ regcache->raw_read ((inst >> 4) & 0xf, (gdb_byte *) & tmpu32); next_pcs.push_back (tmpu32); break; case 0x30: /* swi */ case 0x35: /* brk */ /* Unsupported, for now. */ break; } } return next_pcs; } /* Given a return value in `regbuf' with a type `valtype', extract and copy its value into `valbuf'. */ static void moxie_extract_return_value (struct type *type, struct regcache *regcache, gdb_byte *dst) { struct gdbarch *gdbarch = regcache->arch (); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); int len = TYPE_LENGTH (type); ULONGEST tmp; /* By using store_unsigned_integer we avoid having to do anything special for small big-endian values. */ regcache_cooked_read_unsigned (regcache, RET1_REGNUM, &tmp); store_unsigned_integer (dst, (len > 4 ? len - 4 : len), byte_order, tmp); /* Ignore return values more than 8 bytes in size because the moxie returns anything more than 8 bytes in the stack. */ if (len > 4) { regcache_cooked_read_unsigned (regcache, RET1_REGNUM + 1, &tmp); store_unsigned_integer (dst + len - 4, 4, byte_order, tmp); } } /* Implement the "return_value" gdbarch method. */ static enum return_value_convention moxie_return_value (struct gdbarch *gdbarch, struct value *function, struct type *valtype, struct regcache *regcache, gdb_byte *readbuf, const gdb_byte *writebuf) { if (TYPE_LENGTH (valtype) > 8) return RETURN_VALUE_STRUCT_CONVENTION; else { if (readbuf != NULL) moxie_extract_return_value (valtype, regcache, readbuf); if (writebuf != NULL) moxie_store_return_value (valtype, regcache, writebuf); return RETURN_VALUE_REGISTER_CONVENTION; } } /* Allocate and initialize a moxie_frame_cache object. */ static struct moxie_frame_cache * moxie_alloc_frame_cache (void) { struct moxie_frame_cache *cache; int i; cache = FRAME_OBSTACK_ZALLOC (struct moxie_frame_cache); cache->base = 0; cache->saved_sp = 0; cache->pc = 0; cache->framesize = 0; for (i = 0; i < MOXIE_NUM_REGS; ++i) cache->saved_regs[i] = REG_UNAVAIL; return cache; } /* Populate a moxie_frame_cache object for this_frame. */ static struct moxie_frame_cache * moxie_frame_cache (struct frame_info *this_frame, void **this_cache) { struct moxie_frame_cache *cache; CORE_ADDR current_pc; int i; if (*this_cache) return (struct moxie_frame_cache *) *this_cache; cache = moxie_alloc_frame_cache (); *this_cache = cache; cache->base = get_frame_register_unsigned (this_frame, MOXIE_FP_REGNUM); if (cache->base == 0) return cache; cache->pc = get_frame_func (this_frame); current_pc = get_frame_pc (this_frame); if (cache->pc) { struct gdbarch *gdbarch = get_frame_arch (this_frame); moxie_analyze_prologue (cache->pc, current_pc, cache, gdbarch); } cache->saved_sp = cache->base - cache->framesize; for (i = 0; i < MOXIE_NUM_REGS; ++i) if (cache->saved_regs[i] != REG_UNAVAIL) cache->saved_regs[i] = cache->base - cache->saved_regs[i]; return cache; } /* 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 moxie_frame_this_id (struct frame_info *this_frame, void **this_prologue_cache, struct frame_id *this_id) { struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, this_prologue_cache); /* This marks the outermost frame. */ if (cache->base == 0) return; *this_id = frame_id_build (cache->saved_sp, cache->pc); } /* Get the value of register regnum in the previous stack frame. */ static struct value * moxie_frame_prev_register (struct frame_info *this_frame, void **this_prologue_cache, int regnum) { struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, this_prologue_cache); gdb_assert (regnum >= 0); if (regnum == MOXIE_SP_REGNUM && cache->saved_sp) return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp); if (regnum < MOXIE_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL) return frame_unwind_got_memory (this_frame, regnum, cache->saved_regs[regnum]); return frame_unwind_got_register (this_frame, regnum, regnum); } static const struct frame_unwind moxie_frame_unwind = { NORMAL_FRAME, default_frame_unwind_stop_reason, moxie_frame_this_id, moxie_frame_prev_register, NULL, default_frame_sniffer }; /* Return the base address of this_frame. */ static CORE_ADDR moxie_frame_base_address (struct frame_info *this_frame, void **this_cache) { struct moxie_frame_cache *cache = moxie_frame_cache (this_frame, this_cache); return cache->base; } static const struct frame_base moxie_frame_base = { &moxie_frame_unwind, moxie_frame_base_address, moxie_frame_base_address, moxie_frame_base_address }; /* Parse the current instruction and record the values of the registers and memory that will be changed in current instruction to "record_arch_list". Return -1 if something wrong. */ static int moxie_process_record (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) { gdb_byte buf[4]; uint16_t inst; uint32_t tmpu32; enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, "Process record: moxie_process_record " "addr = 0x%s\n", paddress (target_gdbarch (), addr)); inst = (uint16_t) moxie_process_readu (addr, buf, 2, byte_order); /* Decode instruction. */ if (inst & (1 << 15)) { if (inst & (1 << 14)) { /* This is a Form 3 instruction. */ int opcode = (inst >> 10 & 0xf); switch (opcode) { case 0x00: /* beq */ case 0x01: /* bne */ case 0x02: /* blt */ case 0x03: /* bgt */ case 0x04: /* bltu */ case 0x05: /* bgtu */ case 0x06: /* bge */ case 0x07: /* ble */ case 0x08: /* bgeu */ case 0x09: /* bleu */ /* Do nothing. */ break; default: { /* Do nothing. */ break; } } } else { /* This is a Form 2 instruction. */ int opcode = (inst >> 12 & 0x3); switch (opcode) { case 0x00: /* inc */ case 0x01: /* dec */ case 0x02: /* gsr */ { int reg = (inst >> 8) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x03: /* ssr */ { /* Do nothing until GDB learns about moxie's special registers. */ } break; default: /* Do nothing. */ break; } } } else { /* This is a Form 1 instruction. */ int opcode = inst >> 8; switch (opcode) { case 0x00: /* nop */ /* Do nothing. */ break; case 0x01: /* ldi.l (immediate) */ case 0x02: /* mov (register-to-register) */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x03: /* jsra */ { regcache->raw_read ( MOXIE_SP_REGNUM, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_reg (regcache, MOXIE_FP_REGNUM) || (record_full_arch_list_add_reg (regcache, MOXIE_SP_REGNUM)) || record_full_arch_list_add_mem (tmpu32 - 12, 12)) return -1; } break; case 0x04: /* ret */ { if (record_full_arch_list_add_reg (regcache, MOXIE_FP_REGNUM) || (record_full_arch_list_add_reg (regcache, MOXIE_SP_REGNUM))) return -1; } break; case 0x05: /* add.l */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x06: /* push */ { int reg = (inst >> 4) & 0xf; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_reg (regcache, reg) || record_full_arch_list_add_mem (tmpu32 - 4, 4)) return -1; } break; case 0x07: /* pop */ { int a = (inst >> 4) & 0xf; int b = inst & 0xf; if (record_full_arch_list_add_reg (regcache, a) || record_full_arch_list_add_reg (regcache, b)) return -1; } break; case 0x08: /* lda.l */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x09: /* sta.l */ { tmpu32 = (uint32_t) moxie_process_readu (addr+2, buf, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 4)) return -1; } break; case 0x0a: /* ld.l (register indirect) */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x0b: /* st.l */ { int reg = (inst >> 4) & 0xf; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 4)) return -1; } break; case 0x0c: /* ldo.l */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x0d: /* sto.l */ { int reg = (inst >> 4) & 0xf; uint32_t offset = (((int16_t) moxie_process_readu (addr+2, buf, 2, byte_order)) << 16 ) >> 16; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); tmpu32 += offset; if (record_full_arch_list_add_mem (tmpu32, 4)) return -1; } break; case 0x0e: /* cmp */ { if (record_full_arch_list_add_reg (regcache, MOXIE_CC_REGNUM)) return -1; } break; case 0x0f: /* nop */ { /* Do nothing. */ break; } case 0x10: /* sex.b */ case 0x11: /* sex.s */ case 0x12: /* zex.b */ case 0x13: /* zex.s */ case 0x14: /* umul.x */ case 0x15: /* mul.x */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x16: case 0x17: case 0x18: { /* Do nothing. */ break; } case 0x19: /* jsr */ { regcache->raw_read ( MOXIE_SP_REGNUM, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_reg (regcache, MOXIE_FP_REGNUM) || (record_full_arch_list_add_reg (regcache, MOXIE_SP_REGNUM)) || record_full_arch_list_add_mem (tmpu32 - 12, 12)) return -1; } break; case 0x1a: /* jmpa */ { /* Do nothing. */ } break; case 0x1b: /* ldi.b (immediate) */ case 0x1c: /* ld.b (register indirect) */ case 0x1d: /* lda.b */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x1e: /* st.b */ { int reg = (inst >> 4) & 0xf; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 1)) return -1; } break; case 0x1f: /* sta.b */ { tmpu32 = moxie_process_readu (addr+2, buf, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 1)) return -1; } break; case 0x20: /* ldi.s (immediate) */ case 0x21: /* ld.s (register indirect) */ case 0x22: /* lda.s */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x23: /* st.s */ { int reg = (inst >> 4) & 0xf; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 2)) return -1; } break; case 0x24: /* sta.s */ { tmpu32 = moxie_process_readu (addr+2, buf, 4, byte_order); if (record_full_arch_list_add_mem (tmpu32, 2)) return -1; } break; case 0x25: /* jmp */ { /* Do nothing. */ } break; case 0x26: /* and */ case 0x27: /* lshr */ case 0x28: /* ashl */ case 0x29: /* sub */ case 0x2a: /* neg */ case 0x2b: /* or */ case 0x2c: /* not */ case 0x2d: /* ashr */ case 0x2e: /* xor */ case 0x2f: /* mul */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x30: /* swi */ { /* We currently implement support for libgloss' system calls. */ int inum = moxie_process_readu (addr+2, buf, 4, byte_order); switch (inum) { case 0x1: /* SYS_exit */ { /* Do nothing. */ } break; case 0x2: /* SYS_open */ { if (record_full_arch_list_add_reg (regcache, RET1_REGNUM)) return -1; } break; case 0x4: /* SYS_read */ { uint32_t length, ptr; /* Read buffer pointer is in $r1. */ regcache->raw_read (3, (gdb_byte *) & ptr); ptr = extract_unsigned_integer ((gdb_byte *) & ptr, 4, byte_order); /* String length is at 0x12($fp). */ regcache->raw_read ( MOXIE_FP_REGNUM, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); length = moxie_process_readu (tmpu32+20, buf, 4, byte_order); if (record_full_arch_list_add_mem (ptr, length)) return -1; } break; case 0x5: /* SYS_write */ { if (record_full_arch_list_add_reg (regcache, RET1_REGNUM)) return -1; } break; default: break; } } break; case 0x31: /* div.l */ case 0x32: /* udiv.l */ case 0x33: /* mod.l */ case 0x34: /* umod.l */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x35: /* brk */ /* Do nothing. */ break; case 0x36: /* ldo.b */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x37: /* sto.b */ { int reg = (inst >> 4) & 0xf; uint32_t offset = (((int16_t) moxie_process_readu (addr+2, buf, 2, byte_order)) << 16 ) >> 16; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); tmpu32 += offset; if (record_full_arch_list_add_mem (tmpu32, 1)) return -1; } break; case 0x38: /* ldo.s */ { int reg = (inst >> 4) & 0xf; if (record_full_arch_list_add_reg (regcache, reg)) return -1; } break; case 0x39: /* sto.s */ { int reg = (inst >> 4) & 0xf; uint32_t offset = (((int16_t) moxie_process_readu (addr+2, buf, 2, byte_order)) << 16 ) >> 16; regcache->raw_read (reg, (gdb_byte *) & tmpu32); tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32, 4, byte_order); tmpu32 += offset; if (record_full_arch_list_add_mem (tmpu32, 2)) return -1; } break; default: /* Do nothing. */ break; } } if (record_full_arch_list_add_reg (regcache, MOXIE_PC_REGNUM)) return -1; if (record_full_arch_list_add_end ()) return -1; return 0; } /* Allocate and initialize the moxie gdbarch object. */ static struct gdbarch * moxie_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) { struct gdbarch *gdbarch; struct gdbarch_tdep *tdep; /* If there is already a candidate, use it. */ arches = gdbarch_list_lookup_by_info (arches, &info); if (arches != NULL) return arches->gdbarch; /* Allocate space for the new architecture. */ tdep = XCNEW (struct gdbarch_tdep); gdbarch = gdbarch_alloc (&info, tdep); set_gdbarch_wchar_bit (gdbarch, 32); set_gdbarch_wchar_signed (gdbarch, 0); set_gdbarch_num_regs (gdbarch, MOXIE_NUM_REGS); set_gdbarch_sp_regnum (gdbarch, MOXIE_SP_REGNUM); set_gdbarch_pc_regnum (gdbarch, MOXIE_PC_REGNUM); set_gdbarch_register_name (gdbarch, moxie_register_name); set_gdbarch_register_type (gdbarch, moxie_register_type); set_gdbarch_return_value (gdbarch, moxie_return_value); set_gdbarch_skip_prologue (gdbarch, moxie_skip_prologue); set_gdbarch_inner_than (gdbarch, core_addr_lessthan); set_gdbarch_breakpoint_kind_from_pc (gdbarch, moxie_breakpoint::kind_from_pc); set_gdbarch_sw_breakpoint_from_kind (gdbarch, moxie_breakpoint::bp_from_kind); set_gdbarch_frame_align (gdbarch, moxie_frame_align); frame_base_set_default (gdbarch, &moxie_frame_base); /* Hook in ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); /* Hook in the default unwinders. */ frame_unwind_append_unwinder (gdbarch, &moxie_frame_unwind); /* Single stepping. */ set_gdbarch_software_single_step (gdbarch, moxie_software_single_step); /* Support simple overlay manager. */ set_gdbarch_overlay_update (gdbarch, simple_overlay_update); /* Support reverse debugging. */ set_gdbarch_process_record (gdbarch, moxie_process_record); return gdbarch; } /* Register this machine's init routine. */ void _initialize_moxie_tdep (); void _initialize_moxie_tdep () { register_gdbarch_init (bfd_arch_moxie, moxie_gdbarch_init); }