diff options
Diffstat (limited to 'gdb/avr-tdep.c')
-rw-r--r-- | gdb/avr-tdep.c | 1374 |
1 files changed, 1374 insertions, 0 deletions
diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c new file mode 100644 index 0000000..71c7796 --- /dev/null +++ b/gdb/avr-tdep.c @@ -0,0 +1,1374 @@ +/* Target-dependent code for Atmel AVR, for GDB. + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Contributed by Theodore A. Roth, troth@verinet.com */ + +/* Portions of this file were taken from the original gdb-4.18 patch developed + by Denis Chertykov, denisc@overta.ru */ + +#include "defs.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "inferior.h" +#include "symfile.h" +#include "arch-utils.h" +#include "regcache.h" + +/* AVR Background: + + (AVR micros are pure Harvard Architecture processors.) + + The AVR family of microcontrollers have three distinctly different memory + spaces: flash, sram and eeprom. The flash is 16 bits wide and is used for + the most part to store program instructions. The sram is 8 bits wide and is + used for the stack and the heap. Some devices lack sram and some can have + an additional external sram added on as a peripheral. + + The eeprom is 8 bits wide and is used to store data when the device is + powered down. Eeprom is not directly accessible, it can only be accessed + via io-registers using a special algorithm. Accessing eeprom via gdb's + remote serial protocol ('m' or 'M' packets) looks difficult to do and is + not included at this time. + + [The eeprom could be read manually via ``x/b <eaddr + AVR_EMEM_START>'' or + written using ``set {unsigned char}<eaddr + AVR_EMEM_START>''. For this to + work, the remote target must be able to handle eeprom accesses and perform + the address translation.] + + All three memory spaces have physical addresses beginning at 0x0. In + addition, the flash is addressed by gcc/binutils/gdb with respect to 8 bit + bytes instead of the 16 bit wide words used by the real device for the + Program Counter. + + In order for remote targets to work correctly, extra bits must be added to + addresses before they are send to the target or received from the target + via the remote serial protocol. The extra bits are the MSBs and are used to + decode which memory space the address is referring to. */ + +#undef XMALLOC +#define XMALLOC(TYPE) ((TYPE*) xmalloc (sizeof (TYPE))) + +#undef EXTRACT_INSN +#define EXTRACT_INSN(addr) extract_unsigned_integer(addr,2) + +/* Constants: prefixed with AVR_ to avoid name space clashes */ + +enum +{ + AVR_REG_W = 24, + AVR_REG_X = 26, + AVR_REG_Y = 28, + AVR_FP_REGNUM = 28, + AVR_REG_Z = 30, + + AVR_SREG_REGNUM = 32, + AVR_SP_REGNUM = 33, + AVR_PC_REGNUM = 34, + + AVR_NUM_REGS = 32 + 1 /*SREG*/ + 1 /*SP*/ + 1 /*PC*/, + AVR_NUM_REG_BYTES = 32 + 1 /*SREG*/ + 2 /*SP*/ + 4 /*PC*/, + + AVR_PC_REG_INDEX = 35, /* index into array of registers */ + + AVR_MAX_PROLOGUE_SIZE = 56, /* bytes */ + + /* Count of pushed registers. From r2 to r17 (inclusively), r28, r29 */ + AVR_MAX_PUSHES = 18, + + /* Number of the last pushed register. r17 for current avr-gcc */ + AVR_LAST_PUSHED_REGNUM = 17, + + /* FIXME: TRoth/2002-01-??: Can we shift all these memory masks left 8 + bits? Do these have to match the bfd vma values?. It sure would make + things easier in the future if they didn't need to match. + + Note: I chose these values so as to be consistent with bfd vma + addresses. + + TRoth/2002-04-08: There is already a conflict with very large programs + in the mega128. The mega128 has 128K instruction bytes (64K words), + thus the Most Significant Bit is 0x10000 which gets masked off my + AVR_MEM_MASK. + + The problem manifests itself when trying to set a breakpoint in a + function which resides in the upper half of the instruction space and + thus requires a 17-bit address. + + For now, I've just removed the EEPROM mask and changed AVR_MEM_MASK + from 0x00ff0000 to 0x00f00000. Eeprom is not accessible from gdb yet, + but could be for some remote targets by just adding the correct offset + to the address and letting the remote target handle the low-level + details of actually accessing the eeprom. */ + + AVR_IMEM_START = 0x00000000, /* INSN memory */ + AVR_SMEM_START = 0x00800000, /* SRAM memory */ +#if 1 + /* No eeprom mask defined */ + AVR_MEM_MASK = 0x00f00000, /* mask to determine memory space */ +#else + AVR_EMEM_START = 0x00810000, /* EEPROM memory */ + AVR_MEM_MASK = 0x00ff0000, /* mask to determine memory space */ +#endif +}; + +/* Any function with a frame looks like this + ....... <-SP POINTS HERE + LOCALS1 <-FP POINTS HERE + LOCALS0 + SAVED FP + SAVED R3 + SAVED R2 + RET PC + FIRST ARG + SECOND ARG */ + +struct frame_extra_info +{ + CORE_ADDR return_pc; + CORE_ADDR args_pointer; + int locals_size; + int framereg; + int framesize; + int is_main; +}; + +struct gdbarch_tdep +{ + /* FIXME: TRoth: is there anything to put here? */ + int foo; +}; + +/* Lookup the name of a register given it's number. */ + +static char * +avr_register_name (int regnum) +{ + static char *register_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "SREG", "SP", "PC" + }; + if (regnum < 0) + return NULL; + if (regnum >= (sizeof (register_names) / sizeof (*register_names))) + return NULL; + return register_names[regnum]; +} + +/* Index within `registers' of the first byte of the space for + register REGNUM. */ + +static int +avr_register_byte (int regnum) +{ + if (regnum < AVR_PC_REGNUM) + return regnum; + else + return AVR_PC_REG_INDEX; +} + +/* Number of bytes of storage in the actual machine representation for + register REGNUM. */ + +static int +avr_register_raw_size (int regnum) +{ + switch (regnum) + { + case AVR_PC_REGNUM: + return 4; + case AVR_SP_REGNUM: + case AVR_FP_REGNUM: + return 2; + default: + return 1; + } +} + +/* Number of bytes of storage in the program's representation + for register N. */ + +static int +avr_register_virtual_size (int regnum) +{ + return TYPE_LENGTH (REGISTER_VIRTUAL_TYPE (regnum)); +} + +/* Return the GDB type object for the "standard" data type + of data in register N. */ + +static struct type * +avr_register_virtual_type (int regnum) +{ + switch (regnum) + { + case AVR_PC_REGNUM: + return builtin_type_unsigned_long; + case AVR_SP_REGNUM: + return builtin_type_unsigned_short; + default: + return builtin_type_unsigned_char; + } +} + +/* Instruction address checks and convertions. */ + +static CORE_ADDR +avr_make_iaddr (CORE_ADDR x) +{ + return ((x) | AVR_IMEM_START); +} + +static int +avr_iaddr_p (CORE_ADDR x) +{ + return (((x) & AVR_MEM_MASK) == AVR_IMEM_START); +} + +/* FIXME: TRoth: Really need to use a larger mask for instructions. Some + devices are already up to 128KBytes of flash space. + + TRoth/2002-04-8: See comment above where AVR_IMEM_START is defined. */ + +static CORE_ADDR +avr_convert_iaddr_to_raw (CORE_ADDR x) +{ + return ((x) & 0xffffffff); +} + +/* SRAM address checks and convertions. */ + +static CORE_ADDR +avr_make_saddr (CORE_ADDR x) +{ + return ((x) | AVR_SMEM_START); +} + +static int +avr_saddr_p (CORE_ADDR x) +{ + return (((x) & AVR_MEM_MASK) == AVR_SMEM_START); +} + +static CORE_ADDR +avr_convert_saddr_to_raw (CORE_ADDR x) +{ + return ((x) & 0xffffffff); +} + +/* EEPROM address checks and convertions. I don't know if these will ever + actually be used, but I've added them just the same. TRoth */ + +/* TRoth/2002-04-08: Commented out for now to allow fix for problem with large + programs in the mega128. */ + +/* static CORE_ADDR */ +/* avr_make_eaddr (CORE_ADDR x) */ +/* { */ +/* return ((x) | AVR_EMEM_START); */ +/* } */ + +/* static int */ +/* avr_eaddr_p (CORE_ADDR x) */ +/* { */ +/* return (((x) & AVR_MEM_MASK) == AVR_EMEM_START); */ +/* } */ + +/* static CORE_ADDR */ +/* avr_convert_eaddr_to_raw (CORE_ADDR x) */ +/* { */ +/* return ((x) & 0xffffffff); */ +/* } */ + +/* Convert from address to pointer and vice-versa. */ + +static void +avr_address_to_pointer (struct type *type, void *buf, CORE_ADDR addr) +{ + /* Is it a code address? */ + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC + || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD) + { + store_unsigned_integer (buf, TYPE_LENGTH (type), + avr_convert_iaddr_to_raw (addr)); + } + else + { + /* Strip off any upper segment bits. */ + store_unsigned_integer (buf, TYPE_LENGTH (type), + avr_convert_saddr_to_raw (addr)); + } +} + +static CORE_ADDR +avr_pointer_to_address (struct type *type, void *buf) +{ + CORE_ADDR addr = extract_address (buf, TYPE_LENGTH (type)); + + if (TYPE_CODE_SPACE (TYPE_TARGET_TYPE (type))) + { + fprintf_unfiltered (gdb_stderr, "CODE_SPACE ---->> ptr->addr: 0x%lx\n", + addr); + fprintf_unfiltered (gdb_stderr, + "+++ If you see this, please send me an email <troth@verinet.com>\n"); + } + + /* Is it a code address? */ + if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_FUNC + || TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD + || TYPE_CODE_SPACE (TYPE_TARGET_TYPE (type))) + return avr_make_iaddr (addr); + else + return avr_make_saddr (addr); +} + +static CORE_ADDR +avr_read_pc (ptid_t ptid) +{ + ptid_t save_ptid; + CORE_ADDR pc; + CORE_ADDR retval; + + save_ptid = inferior_ptid; + inferior_ptid = ptid; + pc = (int) read_register (AVR_PC_REGNUM); + inferior_ptid = save_ptid; + retval = avr_make_iaddr (pc); + return retval; +} + +static void +avr_write_pc (CORE_ADDR val, ptid_t ptid) +{ + ptid_t save_ptid; + + save_ptid = inferior_ptid; + inferior_ptid = ptid; + write_register (AVR_PC_REGNUM, avr_convert_iaddr_to_raw (val)); + inferior_ptid = save_ptid; +} + +static CORE_ADDR +avr_read_sp (void) +{ + return (avr_make_saddr (read_register (AVR_SP_REGNUM))); +} + +static void +avr_write_sp (CORE_ADDR val) +{ + write_register (AVR_SP_REGNUM, avr_convert_saddr_to_raw (val)); +} + +static CORE_ADDR +avr_read_fp (void) +{ + return (avr_make_saddr (read_register (AVR_FP_REGNUM))); +} + +/* Translate a GDB virtual ADDR/LEN into a format the remote target + understands. Returns number of bytes that can be transfered + starting at TARG_ADDR. Return ZERO if no bytes can be transfered + (segmentation fault). + + TRoth/2002-04-08: Could this be used to check for dereferencing an invalid + pointer? */ + +static void +avr_remote_translate_xfer_address (CORE_ADDR memaddr, int nr_bytes, + CORE_ADDR *targ_addr, int *targ_len) +{ + long out_addr; + long out_len; + + /* FIXME: TRoth: Do nothing for now. Will need to examine memaddr at this + point and see if the high bit are set with the masks that we want. */ + + *targ_addr = memaddr; + *targ_len = nr_bytes; +} + +/* Function pointers obtained from the target are half of what gdb expects so + multiply by 2. */ + +static CORE_ADDR +avr_convert_from_func_ptr_addr (CORE_ADDR addr) +{ + return addr * 2; +} + +/* avr_scan_prologue is also used as the frame_init_saved_regs(). + + Put here the code to store, into fi->saved_regs, the addresses of + the saved registers of frame described by FRAME_INFO. This + includes special registers such as pc and fp saved in special ways + in the stack frame. sp is even more special: the address we return + for it IS the sp for the next frame. */ + +/* Function: avr_scan_prologue (helper function for avr_init_extra_frame_info) + This function decodes a AVR 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 "extra_info" field of the frame_info. + + A typical AVR function prologue might look like this: + push rXX + push r28 + push r29 + in r28,__SP_L__ + in r29,__SP_H__ + sbiw r28,<LOCALS_SIZE> + in __tmp_reg__,__SREG__ + cli + out __SP_L__,r28 + out __SREG__,__tmp_reg__ + out __SP_H__,r29 + + A `-mcall-prologues' prologue look like this: + ldi r26,<LOCALS_SIZE> + ldi r27,<LOCALS_SIZE>/265 + ldi r30,pm_lo8(.L_foo_body) + ldi r31,pm_hi8(.L_foo_body) + rjmp __prologue_saves__+RRR + .L_foo_body: */ + +static void +avr_scan_prologue (struct frame_info *fi) +{ + CORE_ADDR prologue_start; + CORE_ADDR prologue_end; + int i; + unsigned short insn; + int regno; + int scan_stage = 0; + char *name; + struct minimal_symbol *msymbol; + int prologue_len; + unsigned char prologue[AVR_MAX_PROLOGUE_SIZE]; + int vpc = 0; + + fi->extra_info->framereg = AVR_SP_REGNUM; + + if (find_pc_partial_function + (fi->pc, &name, &prologue_start, &prologue_end)) + { + struct symtab_and_line sal = find_pc_line (prologue_start, 0); + + if (sal.line == 0) /* no line info, use current PC */ + prologue_end = fi->pc; + else if (sal.end < prologue_end) /* next line begins after fn end */ + prologue_end = sal.end; /* (probably means no prologue) */ + } + else + /* We're in the boondocks: allow for */ + /* 19 pushes, an add, and "mv fp,sp" */ + prologue_end = prologue_start + AVR_MAX_PROLOGUE_SIZE; + + prologue_end = min (prologue_end, fi->pc); + + /* Search the prologue looking for instructions that set up the + frame pointer, adjust the stack pointer, and save registers. */ + + fi->extra_info->framesize = 0; + prologue_len = prologue_end - prologue_start; + read_memory (prologue_start, prologue, prologue_len); + + /* Scanning main()'s prologue + ldi r28,lo8(<RAM_ADDR> - <LOCALS_SIZE>) + ldi r29,hi8(<RAM_ADDR> - <LOCALS_SIZE>) + out __SP_H__,r29 + out __SP_L__,r28 */ + + if (name && strcmp ("main", name) == 0 && prologue_len == 8) + { + CORE_ADDR locals; + unsigned char img[] = { + 0xde, 0xbf, /* out __SP_H__,r29 */ + 0xcd, 0xbf /* out __SP_L__,r28 */ + }; + + fi->extra_info->framereg = AVR_FP_REGNUM; + insn = EXTRACT_INSN (&prologue[vpc]); + /* ldi r28,lo8(<RAM_ADDR> - <LOCALS_SIZE>) */ + if ((insn & 0xf0f0) == 0xe0c0) + { + locals = (insn & 0xf) | ((insn & 0x0f00) >> 4); + insn = EXTRACT_INSN (&prologue[vpc + 2]); + /* ldi r29,hi8(<RAM_ADDR> - <LOCALS_SIZE>) */ + if ((insn & 0xf0f0) == 0xe0d0) + { + locals |= ((insn & 0xf) | ((insn & 0x0f00) >> 4)) << 8; + if (memcmp (prologue + vpc + 4, img, sizeof (img)) == 0) + { + fi->frame = locals; + + /* TRoth: Does -1 mean we're in main? */ + fi->extra_info->is_main = 1; + return; + } + } + } + } + + /* Scanning `-mcall-prologues' prologue + FIXME: mega prologue have a 12 bytes long */ + + while (prologue_len <= 12) /* I'm use while to avoit many goto's */ + { + int loc_size; + int body_addr; + unsigned num_pushes; + + insn = EXTRACT_INSN (&prologue[vpc]); + /* ldi r26,<LOCALS_SIZE> */ + if ((insn & 0xf0f0) != 0xe0a0) + break; + loc_size = (insn & 0xf) | ((insn & 0x0f00) >> 4); + + insn = EXTRACT_INSN (&prologue[vpc + 2]); + /* ldi r27,<LOCALS_SIZE> / 256 */ + if ((insn & 0xf0f0) != 0xe0b0) + break; + loc_size |= ((insn & 0xf) | ((insn & 0x0f00) >> 4)) << 8; + + insn = EXTRACT_INSN (&prologue[vpc + 4]); + /* ldi r30,pm_lo8(.L_foo_body) */ + if ((insn & 0xf0f0) != 0xe0e0) + break; + body_addr = (insn & 0xf) | ((insn & 0x0f00) >> 4); + + insn = EXTRACT_INSN (&prologue[vpc + 6]); + /* ldi r31,pm_hi8(.L_foo_body) */ + if ((insn & 0xf0f0) != 0xe0f0) + break; + body_addr |= ((insn & 0xf) | ((insn & 0x0f00) >> 4)) << 8; + + if (body_addr != (prologue_start + 10) / 2) + break; + + msymbol = lookup_minimal_symbol ("__prologue_saves__", NULL, NULL); + if (!msymbol) + break; + + /* FIXME: prologue for mega have a JMP instead of RJMP */ + insn = EXTRACT_INSN (&prologue[vpc + 8]); + /* rjmp __prologue_saves__+RRR */ + if ((insn & 0xf000) != 0xc000) + break; + + /* Extract PC relative offset from RJMP */ + i = (insn & 0xfff) | (insn & 0x800 ? (-1 ^ 0xfff) : 0); + /* Convert offset to byte addressable mode */ + i *= 2; + /* Destination address */ + i += vpc + prologue_start + 10; + /* Resovle offset (in words) from __prologue_saves__ symbol. + Which is a pushes count in `-mcall-prologues' mode */ + num_pushes = AVR_MAX_PUSHES - (i - SYMBOL_VALUE_ADDRESS (msymbol)) / 2; + + if (num_pushes > AVR_MAX_PUSHES) + num_pushes = 0; + + if (num_pushes) + { + int from; + fi->saved_regs[AVR_FP_REGNUM + 1] = num_pushes; + if (num_pushes >= 2) + fi->saved_regs[AVR_FP_REGNUM] = num_pushes - 1; + i = 0; + for (from = AVR_LAST_PUSHED_REGNUM + 1 - (num_pushes - 2); + from <= AVR_LAST_PUSHED_REGNUM; ++from) + fi->saved_regs[from] = ++i; + } + fi->extra_info->locals_size = loc_size; + fi->extra_info->framesize = loc_size + num_pushes; + fi->extra_info->framereg = AVR_FP_REGNUM; + return; + } + + /* Scan interrupt or signal function */ + + if (prologue_len >= 12) + { + unsigned char img[] = { + 0x78, 0x94, /* sei */ + 0x1f, 0x92, /* push r1 */ + 0x0f, 0x92, /* push r0 */ + 0x0f, 0xb6, /* in r0,0x3f SREG */ + 0x0f, 0x92, /* push r0 */ + 0x11, 0x24 /* clr r1 */ + }; + if (memcmp (prologue, img, sizeof (img)) == 0) + { + vpc += sizeof (img); + fi->saved_regs[0] = 2; + fi->saved_regs[1] = 1; + fi->extra_info->framesize += 3; + } + else if (memcmp (img + 1, prologue, sizeof (img) - 1) == 0) + { + vpc += sizeof (img) - 1; + fi->saved_regs[0] = 2; + fi->saved_regs[1] = 1; + fi->extra_info->framesize += 3; + } + } + + /* First stage of the prologue scanning. + Scan pushes */ + + for (; vpc <= prologue_len; vpc += 2) + { + insn = EXTRACT_INSN (&prologue[vpc]); + if ((insn & 0xfe0f) == 0x920f) /* push rXX */ + { + /* Bits 4-9 contain a mask for registers R0-R32. */ + regno = (insn & 0x1f0) >> 4; + ++fi->extra_info->framesize; + fi->saved_regs[regno] = fi->extra_info->framesize; + scan_stage = 1; + } + else + break; + } + + /* Second stage of the prologue scanning. + Scan: + in r28,__SP_L__ + in r29,__SP_H__ */ + + if (scan_stage == 1 && vpc + 4 <= prologue_len) + { + unsigned char img[] = { + 0xcd, 0xb7, /* in r28,__SP_L__ */ + 0xde, 0xb7 /* in r29,__SP_H__ */ + }; + unsigned short insn1; + + if (memcmp (prologue + vpc, img, sizeof (img)) == 0) + { + vpc += 4; + fi->extra_info->framereg = AVR_FP_REGNUM; + scan_stage = 2; + } + } + + /* Third stage of the prologue scanning. (Really two stages) + Scan for: + sbiw r28,XX or subi r28,lo8(XX) + sbci r29,hi8(XX) + in __tmp_reg__,__SREG__ + cli + out __SP_L__,r28 + out __SREG__,__tmp_reg__ + out __SP_H__,r29 */ + + if (scan_stage == 2 && vpc + 12 <= prologue_len) + { + int locals_size = 0; + unsigned char img[] = { + 0x0f, 0xb6, /* in r0,0x3f */ + 0xf8, 0x94, /* cli */ + 0xcd, 0xbf, /* out 0x3d,r28 ; SPL */ + 0x0f, 0xbe, /* out 0x3f,r0 ; SREG */ + 0xde, 0xbf /* out 0x3e,r29 ; SPH */ + }; + unsigned char img_sig[] = { + 0xcd, 0xbf, /* out 0x3d,r28 ; SPL */ + 0xde, 0xbf /* out 0x3e,r29 ; SPH */ + }; + unsigned char img_int[] = { + 0xf8, 0x94, /* cli */ + 0xcd, 0xbf, /* out 0x3d,r28 ; SPL */ + 0x78, 0x94, /* sei */ + 0xde, 0xbf /* out 0x3e,r29 ; SPH */ + }; + + insn = EXTRACT_INSN (&prologue[vpc]); + vpc += 2; + if ((insn & 0xff30) == 0x9720) /* sbiw r28,XXX */ + locals_size = (insn & 0xf) | ((insn & 0xc0) >> 2); + else if ((insn & 0xf0f0) == 0x50c0) /* subi r28,lo8(XX) */ + { + locals_size = (insn & 0xf) | ((insn & 0xf00) >> 4); + insn = EXTRACT_INSN (&prologue[vpc]); + vpc += 2; + locals_size += ((insn & 0xf) | ((insn & 0xf00) >> 4) << 8); + } + else + return; + fi->extra_info->locals_size = locals_size; + fi->extra_info->framesize += locals_size; + } +} + +/* This function actually figures out the frame address for a given pc and + sp. This is tricky because we sometimes don't use an explicit + frame pointer, and the previous stack pointer isn't necessarily recorded + on the stack. The only reliable way to get this info is to + examine the prologue. */ + +static void +avr_init_extra_frame_info (int fromleaf, struct frame_info *fi) +{ + int reg; + + if (fi->next) + fi->pc = FRAME_SAVED_PC (fi->next); + + fi->extra_info = (struct frame_extra_info *) + frame_obstack_alloc (sizeof (struct frame_extra_info)); + frame_saved_regs_zalloc (fi); + + fi->extra_info->return_pc = 0; + fi->extra_info->args_pointer = 0; + fi->extra_info->locals_size = 0; + fi->extra_info->framereg = 0; + fi->extra_info->framesize = 0; + fi->extra_info->is_main = 0; + + avr_scan_prologue (fi); + + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + /* We need to setup fi->frame here because run_stack_dummy gets it wrong + by assuming it's always FP. */ + fi->frame = generic_read_register_dummy (fi->pc, fi->frame, fi->frame); + } + else if (!fi->next) /* this is the innermost frame? */ + fi->frame = read_register (fi->extra_info->framereg); + else if (fi->extra_info->is_main != 1) /* not the innermost frame, not `main' */ + /* If we have an next frame, the callee saved it. */ + { + struct frame_info *next_fi = fi->next; + if (fi->extra_info->framereg == AVR_SP_REGNUM) + fi->frame = + next_fi->frame + 2 /* ret addr */ + next_fi->extra_info->framesize; + /* FIXME: I don't analyse va_args functions */ + else + { + CORE_ADDR fp = 0; + CORE_ADDR fp1 = 0; + unsigned int fp_low, fp_high; + + /* Scan all frames */ + for (; next_fi; next_fi = next_fi->next) + { + /* look for saved AVR_FP_REGNUM */ + if (next_fi->saved_regs[AVR_FP_REGNUM] && !fp) + fp = next_fi->saved_regs[AVR_FP_REGNUM]; + /* look for saved AVR_FP_REGNUM + 1 */ + if (next_fi->saved_regs[AVR_FP_REGNUM + 1] && !fp1) + fp1 = next_fi->saved_regs[AVR_FP_REGNUM + 1]; + } + fp_low = (fp ? read_memory_unsigned_integer (avr_make_saddr (fp), 1) + : read_register (AVR_FP_REGNUM)) & 0xff; + fp_high = + (fp1 ? read_memory_unsigned_integer (avr_make_saddr (fp1), 1) : + read_register (AVR_FP_REGNUM + 1)) & 0xff; + fi->frame = fp_low | (fp_high << 8); + } + } + + /* TRoth: Do we want to do this if we are in main? I don't think we should + since return_pc makes no sense when we are in main. */ + + if ((fi->pc) && (fi->extra_info->is_main == 0)) /* We are not in CALL_DUMMY */ + { + CORE_ADDR addr; + int i; + + addr = fi->frame + fi->extra_info->framesize + 1; + + /* Return address in stack in different endianness */ + + fi->extra_info->return_pc = + read_memory_unsigned_integer (avr_make_saddr (addr), 1) << 8; + fi->extra_info->return_pc |= + read_memory_unsigned_integer (avr_make_saddr (addr + 1), 1); + + /* This return address in words, + must be converted to the bytes address */ + fi->extra_info->return_pc *= 2; + + /* Resolve a pushed registers addresses */ + for (i = 0; i < NUM_REGS; i++) + { + if (fi->saved_regs[i]) + fi->saved_regs[i] = addr - fi->saved_regs[i]; + } + } +} + +/* Restore the machine to the state it had before the current frame was + created. Usually used either by the "RETURN" command, or by + call_function_by_hand after the dummy_frame is finished. */ + +static void +avr_pop_frame (void) +{ + unsigned regnum; + CORE_ADDR saddr; + struct frame_info *frame = get_current_frame (); + + if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame)) + { + generic_pop_dummy_frame (); + } + else + { + /* TRoth: Why only loop over 8 registers? */ + + for (regnum = 0; regnum < 8; regnum++) + { + /* Don't forget AVR_SP_REGNUM in a frame_saved_regs struct is the + actual value we want, not the address of the value we want. */ + if (frame->saved_regs[regnum] && regnum != AVR_SP_REGNUM) + { + saddr = avr_make_saddr (frame->saved_regs[regnum]); + write_register (regnum, + read_memory_unsigned_integer (saddr, 1)); + } + else if (frame->saved_regs[regnum] && regnum == AVR_SP_REGNUM) + write_register (regnum, frame->frame + 2); + } + + /* Don't forget the update the PC too! */ + write_pc (frame->extra_info->return_pc); + } + flush_cached_frames (); +} + +/* Return the saved PC from this frame. */ + +static CORE_ADDR +avr_frame_saved_pc (struct frame_info *frame) +{ + if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame)) + return generic_read_register_dummy (frame->pc, frame->frame, + AVR_PC_REGNUM); + else + return frame->extra_info->return_pc; +} + +static CORE_ADDR +avr_saved_pc_after_call (struct frame_info *frame) +{ + unsigned char m1, m2; + unsigned int sp = read_register (AVR_SP_REGNUM); + m1 = read_memory_unsigned_integer (avr_make_saddr (sp + 1), 1); + m2 = read_memory_unsigned_integer (avr_make_saddr (sp + 2), 1); + return (m2 | (m1 << 8)) * 2; +} + +/* Figure out where in REGBUF the called function has left its return value. + Copy that into VALBUF. */ + +static void +avr_extract_return_value (struct type *type, char *regbuf, char *valbuf) +{ + int wordsize, len; + + wordsize = 2; + + len = TYPE_LENGTH (type); + + switch (len) + { + case 1: /* (char) */ + case 2: /* (short), (int) */ + memcpy (valbuf, regbuf + REGISTER_BYTE (24), 2); + break; + case 4: /* (long), (float) */ + memcpy (valbuf, regbuf + REGISTER_BYTE (22), 4); + break; + case 8: /* (double) (doesn't seem to happen, which is good, + because this almost certainly isn't right. */ + error ("I don't know how a double is returned."); + break; + } +} + +/* Returns the return address for a dummy. */ + +static CORE_ADDR +avr_call_dummy_address (void) +{ + return entry_point_address (); +} + +/* Place the appropriate value in the appropriate registers. + Primarily used by the RETURN command. */ + +static void +avr_store_return_value (struct type *type, char *valbuf) +{ + int wordsize, len, regval; + + wordsize = 2; + + len = TYPE_LENGTH (type); + switch (len) + { + case 1: /* char */ + case 2: /* short, int */ + regval = extract_address (valbuf, len); + write_register (0, regval); + break; + case 4: /* long, float */ + regval = extract_address (valbuf, len); + write_register (0, regval >> 16); + write_register (1, regval & 0xffff); + break; + case 8: /* presumeably double, but doesn't seem to happen */ + error ("I don't know how to return a double."); + break; + } +} + +/* Setup the return address for a dummy frame, as called by + call_function_by_hand. Only necessary when you are using an empty + CALL_DUMMY. */ + +static CORE_ADDR +avr_push_return_address (CORE_ADDR pc, CORE_ADDR sp) +{ + unsigned char buf[2]; + int wordsize = 2; + struct minimal_symbol *msymbol; + CORE_ADDR mon_brk; + + fprintf_unfiltered (gdb_stderr, "avr_push_return_address() was called\n"); + + buf[0] = 0; + buf[1] = 0; + sp -= wordsize; + write_memory (sp + 1, buf, 2); + +#if 0 + /* FIXME: TRoth/2002-02-18: This should probably be removed since it's a + left-over from Denis' original patch which used avr-mon for the target + instead of the generic remote target. */ + if ((strcmp (target_shortname, "avr-mon") == 0) + && (msymbol = lookup_minimal_symbol ("gdb_break", NULL, NULL))) + { + mon_brk = SYMBOL_VALUE_ADDRESS (msymbol); + store_unsigned_integer (buf, wordsize, mon_brk / 2); + sp -= wordsize; + write_memory (sp + 1, buf + 1, 1); + write_memory (sp + 2, buf, 1); + } +#endif + return sp; +} + +static CORE_ADDR +avr_skip_prologue (CORE_ADDR pc) +{ + CORE_ADDR func_addr, func_end; + struct symtab_and_line sal; + + /* See what the symbol table says */ + + if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) + { + sal = find_pc_line (func_addr, 0); + + if (sal.line != 0 && sal.end < func_end) + return sal.end; + } + +/* Either we didn't find the start of this function (nothing we can do), + or there's no line info, or the line after the prologue is after + the end of the function (there probably isn't a prologue). */ + + return pc; +} + +static CORE_ADDR +avr_frame_address (struct frame_info *fi) +{ + return avr_make_saddr (fi->frame); +} + +/* Given a GDB frame, determine the address of the calling function's frame. + This will be used to create a new GDB frame struct, and then + INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. + + For us, the frame address is its stack pointer value, so we look up + the function prologue to determine the caller's sp value, and return it. */ + +static CORE_ADDR +avr_frame_chain (struct frame_info *frame) +{ + if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame)) + { + /* initialize the return_pc now */ + frame->extra_info->return_pc = generic_read_register_dummy (frame->pc, + frame-> + frame, + AVR_PC_REGNUM); + return frame->frame; + } + return (frame->extra_info->is_main ? 0 + : frame->frame + frame->extra_info->framesize + 2 /* ret addr */ ); +} + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. + + We store structs through a pointer passed in the first Argument + register. */ + +static void +avr_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) +{ + write_register (0, addr); +} + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR (or an expression that can be used as one). */ + +static CORE_ADDR +avr_extract_struct_value_address (char *regbuf) +{ + return (extract_address ((regbuf) + REGISTER_BYTE (0), + REGISTER_RAW_SIZE (0)) | AVR_SMEM_START); +} + +/* Setup the function arguments for calling a function in the inferior. + + On the AVR architecture, there are 18 registers (R25 to R8) which are + dedicated for passing function arguments. Up to the first 18 arguments + (depending on size) may go into these registers. The rest go on the stack. + + Arguments that are larger than WORDSIZE bytes will be split between two or + more registers as available, but will NOT be split between a register and + the stack. + + An exceptional case exists for struct arguments (and possibly other + aggregates such as arrays) -- if the size is larger than WORDSIZE bytes but + not a multiple of WORDSIZE bytes. In this case the argument is never split + between the registers and the stack, but instead is copied in its entirety + onto the stack, AND also copied into as many registers as there is room + for. In other words, space in registers permitting, two copies of the same + argument are passed in. As far as I can tell, only the one on the stack is + used, although that may be a function of the level of compiler + optimization. I suspect this is a compiler bug. Arguments of these odd + sizes are left-justified within the word (as opposed to arguments smaller + than WORDSIZE bytes, which are right-justified). + + If the function is to return an aggregate type such as a struct, the caller + must allocate space into which the callee will copy the return value. In + this case, a pointer to the return value location is passed into the callee + in register R0, which displaces one of the other arguments passed in via + registers R0 to R2. */ + +static CORE_ADDR +avr_push_arguments (int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + int stack_alloc, stack_offset; + int wordsize; + int argreg; + int argnum; + struct type *type; + CORE_ADDR regval; + char *val; + char valbuf[4]; + int len; + + wordsize = 1; +#if 0 + /* Now make sure there's space on the stack */ + for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++) + stack_alloc += TYPE_LENGTH (VALUE_TYPE (args[argnum])); + sp -= stack_alloc; /* make room on stack for args */ + /* we may over-allocate a little here, but that won't hurt anything */ +#endif + argreg = 25; + if (struct_return) /* "struct return" pointer takes up one argreg */ + { + write_register (--argreg, struct_addr); + } + + /* Now load as many as possible of the first arguments into registers, and + push the rest onto the stack. There are 3N bytes in three registers + available. Loop thru args from first to last. */ + + for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++) + { + type = VALUE_TYPE (args[argnum]); + len = TYPE_LENGTH (type); + val = (char *) VALUE_CONTENTS (args[argnum]); + + /* NOTE WELL!!!!! This is not an "else if" clause!!! That's because + some *&^%$ things get passed on the stack AND in the registers! */ + while (len > 0) + { /* there's room in registers */ + len -= wordsize; + regval = extract_address (val + len, wordsize); + write_register (argreg--, regval); + } + } + return sp; +} + +/* Initialize the gdbarch structure for the AVR's. */ + +static struct gdbarch * +avr_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + /* FIXME: TRoth/2002-02-18: I have no idea if avr_call_dummy_words[] should + be bigger or not. Initial testing seems to show that `call my_func()` + works and backtrace from a breakpoint within the call looks correct. + Admittedly, I haven't tested with more than a very simple program. */ + static LONGEST avr_call_dummy_words[] = { 0 }; + + struct gdbarch *gdbarch; + struct gdbarch_tdep *tdep; + + /* Find a candidate among the list of pre-declared architectures. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != NULL) + return arches->gdbarch; + + /* None found, create a new architecture from the information provided. */ + tdep = XMALLOC (struct gdbarch_tdep); + gdbarch = gdbarch_alloc (&info, tdep); + + /* If we ever need to differentiate the device types, do it here. */ + switch (info.bfd_arch_info->mach) + { + case bfd_mach_avr1: + case bfd_mach_avr2: + case bfd_mach_avr3: + case bfd_mach_avr4: + case bfd_mach_avr5: + break; + } + + set_gdbarch_short_bit (gdbarch, 2 * TARGET_CHAR_BIT); + set_gdbarch_int_bit (gdbarch, 2 * TARGET_CHAR_BIT); + set_gdbarch_long_bit (gdbarch, 4 * TARGET_CHAR_BIT); + set_gdbarch_long_long_bit (gdbarch, 8 * TARGET_CHAR_BIT); + set_gdbarch_ptr_bit (gdbarch, 2 * TARGET_CHAR_BIT); + set_gdbarch_addr_bit (gdbarch, 32); + set_gdbarch_bfd_vma_bit (gdbarch, 32); /* FIXME: TRoth/2002-02-18: Is this needed? */ + + 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, &floatformat_ieee_single_little); + set_gdbarch_double_format (gdbarch, &floatformat_ieee_single_little); + set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_single_little); + + set_gdbarch_read_pc (gdbarch, avr_read_pc); + set_gdbarch_write_pc (gdbarch, avr_write_pc); + set_gdbarch_read_fp (gdbarch, avr_read_fp); + set_gdbarch_read_sp (gdbarch, avr_read_sp); + set_gdbarch_write_sp (gdbarch, avr_write_sp); + + set_gdbarch_num_regs (gdbarch, AVR_NUM_REGS); + + set_gdbarch_sp_regnum (gdbarch, AVR_SP_REGNUM); + set_gdbarch_fp_regnum (gdbarch, AVR_FP_REGNUM); + set_gdbarch_pc_regnum (gdbarch, AVR_PC_REGNUM); + + set_gdbarch_register_name (gdbarch, avr_register_name); + set_gdbarch_register_size (gdbarch, 1); + set_gdbarch_register_bytes (gdbarch, AVR_NUM_REG_BYTES); + set_gdbarch_register_byte (gdbarch, avr_register_byte); + set_gdbarch_register_raw_size (gdbarch, avr_register_raw_size); + set_gdbarch_max_register_raw_size (gdbarch, 4); + set_gdbarch_register_virtual_size (gdbarch, avr_register_virtual_size); + set_gdbarch_max_register_virtual_size (gdbarch, 4); + set_gdbarch_register_virtual_type (gdbarch, avr_register_virtual_type); + + /* We might need to define our own here or define FRAME_INIT_SAVED_REGS */ + set_gdbarch_get_saved_register (gdbarch, generic_get_saved_register); + + set_gdbarch_print_insn (gdbarch, print_insn_avr); + + set_gdbarch_use_generic_dummy_frames (gdbarch, 1); + set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT); + set_gdbarch_call_dummy_address (gdbarch, avr_call_dummy_address); + set_gdbarch_call_dummy_start_offset (gdbarch, 0); + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1); + set_gdbarch_call_dummy_breakpoint_offset (gdbarch, 0); + set_gdbarch_call_dummy_length (gdbarch, 0); + set_gdbarch_pc_in_call_dummy (gdbarch, generic_pc_in_call_dummy); + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, avr_call_dummy_words); + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + set_gdbarch_fix_call_dummy (gdbarch, generic_fix_call_dummy); + +/* set_gdbarch_believe_pcc_promotion (gdbarch, 1); // TRoth: should this be set? */ + + set_gdbarch_address_to_pointer (gdbarch, avr_address_to_pointer); + set_gdbarch_pointer_to_address (gdbarch, avr_pointer_to_address); + set_gdbarch_extract_return_value (gdbarch, avr_extract_return_value); + set_gdbarch_push_arguments (gdbarch, avr_push_arguments); + set_gdbarch_push_dummy_frame (gdbarch, generic_push_dummy_frame); +/* set_gdbarch_push_return_address (gdbarch, avr_push_return_address); */ + set_gdbarch_pop_frame (gdbarch, avr_pop_frame); + + set_gdbarch_store_return_value (gdbarch, avr_store_return_value); + + set_gdbarch_use_struct_convention (gdbarch, generic_use_struct_convention); + set_gdbarch_store_struct_return (gdbarch, avr_store_struct_return); + set_gdbarch_extract_struct_value_address (gdbarch, + avr_extract_struct_value_address); + + set_gdbarch_frame_init_saved_regs (gdbarch, avr_scan_prologue); + set_gdbarch_init_extra_frame_info (gdbarch, avr_init_extra_frame_info); + set_gdbarch_skip_prologue (gdbarch, avr_skip_prologue); +/* set_gdbarch_prologue_frameless_p (gdbarch, avr_prologue_frameless_p); */ + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + + set_gdbarch_decr_pc_after_break (gdbarch, 0); + + set_gdbarch_function_start_offset (gdbarch, 0); + set_gdbarch_remote_translate_xfer_address (gdbarch, + avr_remote_translate_xfer_address); + set_gdbarch_frame_args_skip (gdbarch, 0); + set_gdbarch_frameless_function_invocation (gdbarch, frameless_look_for_prologue); /* ??? */ + set_gdbarch_frame_chain (gdbarch, avr_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, generic_func_frame_chain_valid); + set_gdbarch_frame_saved_pc (gdbarch, avr_frame_saved_pc); + set_gdbarch_frame_args_address (gdbarch, avr_frame_address); + set_gdbarch_frame_locals_address (gdbarch, avr_frame_address); + set_gdbarch_saved_pc_after_call (gdbarch, avr_saved_pc_after_call); + set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); + + set_gdbarch_convert_from_func_ptr_addr (gdbarch, + avr_convert_from_func_ptr_addr); + + return gdbarch; +} + +/* Send a query request to the avr remote target asking for values of the io + registers. If args parameter is not NULL, then the user has requested info + on a specific io register [This still needs implemented and is ignored for + now]. The query string should be one of these forms: + + "Ravr.io_reg" -> reply is "NN" number of io registers + + "Ravr.io_reg:addr,len" where addr is first register and len is number of + registers to be read. The reply should be "<NAME>,VV;" for each io register + where, <NAME> is a string, and VV is the hex value of the register. + + All io registers are 8-bit. */ + +static void +avr_io_reg_read_command (char *args, int from_tty) +{ + int bufsiz = 0; + char buf[400]; + char query[400]; + char *p; + unsigned int nreg = 0; + unsigned int val; + int i, j, k, step; + +/* fprintf_unfiltered (gdb_stderr, "DEBUG: avr_io_reg_read_command (\"%s\", %d)\n", */ +/* args, from_tty); */ + + if (!current_target.to_query) + { + fprintf_unfiltered (gdb_stderr, + "ERR: info io_registers NOT supported by current target\n"); + return; + } + + /* Just get the maximum buffer size. */ + target_query ((int) 'R', 0, 0, &bufsiz); + if (bufsiz > sizeof (buf)) + bufsiz = sizeof (buf); + + /* Find out how many io registers the target has. */ + strcpy (query, "avr.io_reg"); + target_query ((int) 'R', query, buf, &bufsiz); + + if (strncmp (buf, "", bufsiz) == 0) + { + fprintf_unfiltered (gdb_stderr, + "info io_registers NOT supported by target\n"); + return; + } + + if (sscanf (buf, "%x", &nreg) != 1) + { + fprintf_unfiltered (gdb_stderr, + "Error fetching number of io registers\n"); + return; + } + + reinitialize_more_filter (); + + printf_unfiltered ("Target has %u io registers:\n\n", nreg); + + /* only fetch up to 8 registers at a time to keep the buffer small */ + step = 8; + + for (i = 0; i < nreg; i += step) + { + j = step - (nreg % step); /* how many registers this round? */ + + snprintf (query, sizeof (query) - 1, "avr.io_reg:%x,%x", i, j); + target_query ((int) 'R', query, buf, &bufsiz); + + p = buf; + for (k = i; k < (i + j); k++) + { + if (sscanf (p, "%[^,],%x;", query, &val) == 2) + { + printf_filtered ("[%02x] %-15s : %02x\n", k, query, val); + while ((*p != ';') && (*p != '\0')) + p++; + p++; /* skip over ';' */ + if (*p == '\0') + break; + } + } + } +} + +void +_initialize_avr_tdep (void) +{ + register_gdbarch_init (bfd_arch_avr, avr_gdbarch_init); + + /* Add a new command to allow the user to query the avr remote target for + the values of the io space registers in a saner way than just using + `x/NNNb ADDR`. */ + + /* FIXME: TRoth/2002-02-18: This should probably be changed to 'info avr + io_registers' to signify it is not available on other platforms. */ + + add_cmd ("io_registers", class_info, avr_io_reg_read_command, + "query remote avr target for io space register values", &infolist); +} |