From 22955480b01bdc633f0be325ccec713d8dd5df6e Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Tue, 17 Nov 2009 12:52:10 +1300 Subject: [PATCH] Separated subsystems and tidied up code. --- .gitignore | 3 + Makefile | 32 +++ dis.c | 601 ++++++++++++++++++++++++++++++++++++++++++++++++++++ dis.h | 214 +++++++++++++++++++ fet.c | 605 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fet.h | 106 ++++++++++ main.c | 602 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rf2500.c | 210 +++++++++++++++++++ rf2500.h | 28 +++ 9 files changed, 2401 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 dis.c create mode 100644 dis.h create mode 100644 fet.c create mode 100644 fet.h create mode 100644 main.c create mode 100644 rf2500.c create mode 100644 rf2500.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f0e61c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.*.swp +*.o +mspdebug diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ec9db24 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +# MSPDebug - debugging tool for the eZ430 +# Copyright (C) 2009 Daniel Beer +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +CC = gcc + +all: mspdebug + +clean: + /bin/rm -f *.o + /bin/rm -f mspdebug + +.SUFFIXES: .c .o + +mspdebug: main.o fet.o rf2500.o dis.o + $(CC) $(CFLAGS) -o $@ $^ -lusb + +.c.o: + $(CC) $(CFLAGS) -O1 -Wall -o $@ -c $*.c diff --git a/dis.c b/dis.c new file mode 100644 index 0000000..ccbcfb2 --- /dev/null +++ b/dis.c @@ -0,0 +1,601 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "dis.h" + +/**********************************************************************/ +/* Disassembler + */ + +/* Decode a single-operand instruction. + * + * Returns the number of bytes consumed in decoding, or -1 if the a + * valid single-operand instruction could not be found. + */ +static int decode_single(u_int8_t *code, u_int16_t offset, u_int16_t size, + struct msp430_instruction *insn) +{ + u_int16_t op = (code[1] << 8) | code[0]; + int need_arg = 0; + + insn->op = op & 0xff80; + insn->is_byte_op = op & 0x0400; + + insn->dst_mode = (op >> 4) & 0x3; + insn->dst_reg = op & 0xf; + + switch (insn->dst_mode) { + case MSP430_AMODE_REGISTER: break; + + case MSP430_AMODE_INDEXED: + need_arg = 1; + if (insn->dst_reg == MSP430_REG_PC) { + insn->dst_addr = offset + 2; + insn->dst_mode = MSP430_AMODE_SYMBOLIC; + } else if (insn->dst_reg == MSP430_REG_SR) + insn->dst_mode = MSP430_AMODE_ABSOLUTE; + break; + + case MSP430_AMODE_INDIRECT: break; + + case MSP430_AMODE_INDIRECT_INC: + if (insn->dst_reg == MSP430_REG_PC) { + insn->dst_mode = MSP430_AMODE_IMMEDIATE; + need_arg = 1; + } + break; + + default: break; + } + + if (need_arg) { + if (size < 4) + return -1; + + insn->dst_addr += (code[3] << 8) | code[2]; + return 4; + } + + return 2; +} + +/* Decode a double-operand instruction. + * + * Returns the number of bytes consumed or -1 if a valid instruction + * could not be found. + */ +static int decode_double(u_int8_t *code, u_int16_t offset, u_int16_t size, + struct msp430_instruction *insn) +{ + u_int16_t op = (code[1] << 8) | code[0]; + int need_src = 0; + int need_dst = 0; + int ret = 2; + + insn->op = op & 0xf000; + insn->is_byte_op = op & 0x0040; + + insn->src_mode = (op >> 4) & 0x3; + insn->src_reg = (op >> 8) & 0xf; + + insn->dst_mode = (op >> 7) & 0x1; + insn->dst_reg = op & 0xf; + + switch (insn->dst_mode) { + case MSP430_AMODE_REGISTER: break; + case MSP430_AMODE_INDEXED: + need_dst = 1; + + if (insn->dst_reg == MSP430_REG_PC) { + insn->dst_mode = MSP430_AMODE_SYMBOLIC; + insn->dst_addr = offset + 2; + } else if (insn->dst_reg == MSP430_REG_SR) + insn->dst_mode = MSP430_AMODE_ABSOLUTE; + break; + + default: break; + } + + switch (insn->src_mode) { + case MSP430_AMODE_REGISTER: break; + case MSP430_AMODE_INDEXED: + need_src = 1; + + if (insn->src_reg == MSP430_REG_PC) { + insn->src_mode = MSP430_AMODE_SYMBOLIC; + insn->dst_addr = offset + 2; + } else if (insn->src_reg == MSP430_REG_SR) + insn->src_mode = MSP430_AMODE_ABSOLUTE; + else if (insn->src_reg == MSP430_REG_R3) + need_src = 0; + break; + + case MSP430_AMODE_INDIRECT: break; + + case MSP430_AMODE_INDIRECT_INC: + if (insn->src_reg == MSP430_REG_PC) { + insn->src_mode = MSP430_AMODE_IMMEDIATE; + need_src = 1; + } + break; + + default: break; + } + + offset += 2; + code += 2; + size -= 2; + + if (need_src) { + if (size < 2) + return -1; + + insn->src_addr += (code[1] << 8) | code[0]; + offset += 2; + code += 2; + size -= 2; + ret += 2; + } + + if (need_dst) { + if (size < 2) + return -1; + + insn->dst_addr += (code[1] << 8) | code[0]; + ret += 2; + } + + return ret; +} + +/* Decode a jump instruction. + * + * All jump instructions are one word in length, so this function + * always returns 2 (to indicate the consumption of 2 bytes). + */ +static int decode_jump(u_int8_t *code, u_int16_t offset, u_int16_t len, + struct msp430_instruction *insn) +{ + u_int16_t op = (code[1] << 8) | code[0]; + int tgtrel = op & 0x3ff; + + if (tgtrel & 0x200) + tgtrel -= 0x400; + + insn->op = op & 0xfc00; + insn->dst_addr = offset + 2 + tgtrel * 2; + insn->dst_mode = MSP430_AMODE_SYMBOLIC; + insn->dst_reg = MSP430_REG_PC; + + return 2; +} + +/* Take a decoded instruction and replace certain addressing modes of + * the constant generator registers with their corresponding immediate + * values. + */ +static void find_cgens(struct msp430_instruction *insn) +{ + if (insn->src_reg == MSP430_REG_SR) { + if (insn->src_mode == MSP430_AMODE_INDIRECT) { + insn->src_mode = MSP430_AMODE_IMMEDIATE; + insn->src_addr = 4; + } else if (insn->src_mode == MSP430_AMODE_INDIRECT_INC) { + insn->src_mode = MSP430_AMODE_IMMEDIATE; + insn->src_addr = 8; + } + } else if (insn->src_reg == MSP430_REG_R3) { + if (insn->src_mode == MSP430_AMODE_REGISTER) + insn->src_addr = 0; + else if (insn->src_mode == MSP430_AMODE_INDEXED) + insn->src_addr = 1; + else if (insn->src_mode == MSP430_AMODE_INDIRECT) + insn->src_addr = 2; + else if (insn->src_mode == MSP430_AMODE_INDIRECT_INC) + insn->src_addr = 0xffff; + + insn->src_mode = MSP430_AMODE_IMMEDIATE; + } +} + +/* Recognise special cases of real instructions and translate them to + * emulated instructions. + */ +static void find_emulated_ops(struct msp430_instruction *insn) +{ + switch (insn->op) { + case MSP430_OP_ADD: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE) { + if (insn->src_addr == 1) { + insn->op = MSP430_OP_INC; + insn->itype = MSP430_ITYPE_SINGLE; + } else if (insn->src_addr == 2) { + insn->op = MSP430_OP_INCD; + insn->itype = MSP430_ITYPE_SINGLE; + } + } else if (insn->dst_mode == insn->src_mode && + insn->dst_reg == insn->src_reg && + insn->dst_addr == insn->src_addr) { + insn->op = MSP430_OP_RLA; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + case MSP430_OP_ADDC: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + !insn->src_addr) { + insn->op = MSP430_OP_ADC; + insn->itype = MSP430_ITYPE_SINGLE; + } else if (insn->dst_mode == insn->src_mode && + insn->dst_reg == insn->src_reg && + insn->dst_addr == insn->src_addr) { + insn->op = MSP430_OP_RLC; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + case MSP430_OP_BIC: + if (insn->dst_mode == MSP430_AMODE_REGISTER && + insn->dst_reg == MSP430_REG_SR && + insn->src_mode == MSP430_AMODE_IMMEDIATE) { + if (insn->src_addr == 1) { + insn->op = MSP430_OP_CLRC; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 4) { + insn->op = MSP430_OP_CLRN; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 2) { + insn->op = MSP430_OP_CLRZ; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 8) { + insn->op = MSP430_OP_DINT; + insn->itype = MSP430_ITYPE_NOARG; + } + } + break; + + case MSP430_OP_BIS: + if (insn->dst_mode == MSP430_AMODE_REGISTER && + insn->dst_reg == MSP430_REG_SR && + insn->src_mode == MSP430_AMODE_IMMEDIATE) { + if (insn->src_addr == 1) { + insn->op = MSP430_OP_SETC; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 4) { + insn->op = MSP430_OP_SETN; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 2) { + insn->op = MSP430_OP_SETZ; + insn->itype = MSP430_ITYPE_NOARG; + } else if (insn->src_addr == 8) { + insn->op = MSP430_OP_EINT; + insn->itype = MSP430_ITYPE_NOARG; + } + } + break; + + case MSP430_OP_CMP: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + !insn->src_addr) { + insn->op = MSP430_OP_TST; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + case MSP430_OP_DADD: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + !insn->src_addr) { + insn->op = MSP430_OP_DADC; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + case MSP430_OP_MOV: + if (insn->src_mode == MSP430_AMODE_INDIRECT_INC && + insn->src_reg == MSP430_REG_SP) { + if (insn->dst_mode == MSP430_AMODE_REGISTER && + insn->dst_reg == MSP430_REG_PC) { + insn->op = MSP430_OP_RET; + insn->itype = MSP430_ITYPE_NOARG; + } else { + insn->op = MSP430_OP_POP; + insn->itype = MSP430_ITYPE_SINGLE; + } + } else if (insn->dst_mode == MSP430_AMODE_REGISTER && + insn->dst_reg == MSP430_REG_PC) { + insn->op = MSP430_OP_BR; + insn->itype = MSP430_ITYPE_SINGLE; + insn->dst_mode = insn->src_mode; + insn->dst_reg = insn->src_reg; + insn->dst_addr = insn->src_addr; + } else if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + !insn->src_addr) { + if (insn->dst_mode == MSP430_AMODE_REGISTER && + insn->dst_reg == MSP430_REG_R3) { + insn->op = MSP430_OP_NOP; + insn->itype = MSP430_ITYPE_NOARG; + } else { + insn->op = MSP430_OP_CLR; + insn->itype = MSP430_ITYPE_SINGLE; + } + } + break; + + case MSP430_OP_SUB: + if (insn->dst_mode == MSP430_AMODE_IMMEDIATE) { + if (insn->dst_addr == 1) { + insn->op = MSP430_OP_DEC; + insn->itype = MSP430_ITYPE_SINGLE; + } else if (insn->dst_addr == 2) { + insn->op = MSP430_OP_DECD; + insn->itype = MSP430_ITYPE_SINGLE; + } + } + break; + + case MSP430_OP_SUBC: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + !insn->src_addr) { + insn->op = MSP430_OP_SBC; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + case MSP430_OP_XOR: + if (insn->src_mode == MSP430_AMODE_IMMEDIATE && + insn->src_addr == 0xffff) { + insn->op = MSP430_OP_INV; + insn->itype = MSP430_ITYPE_SINGLE; + } + break; + + default: break; + } +} + +/* Decode a single instruction. + * + * Returns the number of bytes consumed, or -1 if an error occured. + * + * The caller needs to pass a pointer to the bytes to be decoded, the + * virtual offset of those bytes, and the maximum number available. If + * successful, the decoded instruction is written into the structure + * pointed to by insn. + */ +int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, + struct msp430_instruction *insn) +{ + u_int16_t op; + int ret; + + memset(insn, 0, sizeof(*insn)); + + if (len < 2) + return -1; + + insn->offset = offset; + op = (code[1] << 8) | code[0]; + + if ((op & 0xf000) == 0x1000) + insn->itype = MSP430_ITYPE_SINGLE; + else if ((op & 0xff00) >= 0x2000 && + (op & 0xff00) < 0x4000) + insn->itype = MSP430_ITYPE_JUMP; + else if ((op & 0xf000) >= 0x4000) + insn->itype = MSP430_ITYPE_DOUBLE; + else + return -1; + + switch (insn->itype) { + case MSP430_ITYPE_SINGLE: + ret = decode_single(code, offset, len, insn); + break; + + case MSP430_ITYPE_DOUBLE: + ret = decode_double(code, offset, len, insn); + break; + + case MSP430_ITYPE_JUMP: + ret = decode_jump(code, offset, len, insn); + break; + + default: break; + } + + find_cgens(insn); + find_emulated_ops(insn); + + insn->len = ret; + return ret; +} + +#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) + +/* Return the mnemonic for an operation, if possible. + * + * If the argument is not a valid operation, this function returns the + * string "???". + */ +static const char *msp_op_name(msp430_op_t op) +{ + static const struct { + msp430_op_t op; + const char *mnemonic; + } ops[] = { + /* Single operand */ + {MSP430_OP_RRC, "RRC"}, + {MSP430_OP_RRC, "SWPB"}, + {MSP430_OP_RRA, "RRA"}, + {MSP430_OP_SXT, "SXT"}, + {MSP430_OP_PUSH, "PUSH"}, + {MSP430_OP_CALL, "CALL"}, + {MSP430_OP_RETI, "RETI"}, + + /* Jump */ + {MSP430_OP_JNZ, "JNZ"}, + {MSP430_OP_JZ, "JZ"}, + {MSP430_OP_JNC, "JNC"}, + {MSP430_OP_JC, "JC"}, + {MSP430_OP_JN, "JN"}, + {MSP430_OP_JL, "JL"}, + {MSP430_OP_JGE, "JGE"}, + {MSP430_OP_JMP, "JMP"}, + + /* Double operand */ + {MSP430_OP_MOV, "MOV"}, + {MSP430_OP_ADD, "ADD"}, + {MSP430_OP_ADDC, "ADDC"}, + {MSP430_OP_SUBC, "SUBC"}, + {MSP430_OP_SUB, "SUB"}, + {MSP430_OP_CMP, "CMP"}, + {MSP430_OP_DADD, "DADD"}, + {MSP430_OP_BIT, "BIT"}, + {MSP430_OP_BIC, "BIC"}, + {MSP430_OP_BIS, "BIS"}, + {MSP430_OP_XOR, "XOR"}, + {MSP430_OP_AND, "AND"}, + + /* Emulated instructions */ + {MSP430_OP_ADC, "ADC"}, + {MSP430_OP_BR, "BR"}, + {MSP430_OP_CLR, "CLR"}, + {MSP430_OP_CLRC, "CLRC"}, + {MSP430_OP_CLRN, "CLRN"}, + {MSP430_OP_CLRZ, "CLRZ"}, + {MSP430_OP_DADC, "DADC"}, + {MSP430_OP_DEC, "DEC"}, + {MSP430_OP_DECD, "DECD"}, + {MSP430_OP_DINT, "DINT"}, + {MSP430_OP_EINT, "EINT"}, + {MSP430_OP_INC, "INC"}, + {MSP430_OP_INCD, "INCD"}, + {MSP430_OP_INV, "INV"}, + {MSP430_OP_NOP, "NOP"}, + {MSP430_OP_POP, "POP"}, + {MSP430_OP_RET, "RET"}, + {MSP430_OP_RLA, "RLA"}, + {MSP430_OP_RLC, "RLC"}, + {MSP430_OP_SBC, "SBC"}, + {MSP430_OP_SETC, "SETC"}, + {MSP430_OP_SETN, "SETN"}, + {MSP430_OP_SETZ, "SETZ"}, + {MSP430_OP_TST, "TST"} + }; + int i; + + for (i = 0; i < ARRAY_LEN(ops); i++) + if (op == ops[i].op) + return ops[i].mnemonic; + + return "???"; +} + +static const char *const msp430_reg_names[] = { + "PC", "SP", "SR", "R3", + "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", + "R12", "R13", "R14", "R15" +}; + +/* Given an operands addressing mode, value and associated register, + * print the canonical representation of it to stdout. + * + * Returns the number of characters printed. + */ +static int format_operand(char *buf, int max_len, + msp430_amode_t amode, u_int16_t addr, + msp430_reg_t reg) +{ + assert (reg >= 0 && reg < ARRAY_LEN(msp430_reg_names)); + + switch (amode) { + case MSP430_AMODE_REGISTER: + return snprintf(buf, max_len, "%s", msp430_reg_names[reg]); + + case MSP430_AMODE_INDEXED: + return snprintf(buf, max_len, "%d(%s)", (int16_t)addr, + msp430_reg_names[reg]); + + case MSP430_AMODE_SYMBOLIC: + return snprintf(buf, max_len, "0x%04x", addr); + + case MSP430_AMODE_ABSOLUTE: + return snprintf(buf, max_len, "&0x%04x", addr); + + case MSP430_AMODE_INDIRECT: + return snprintf(buf, max_len, "@%s", msp430_reg_names[reg]); + + case MSP430_AMODE_INDIRECT_INC: + return snprintf(buf, max_len, "@%s+", msp430_reg_names[reg]); + + case MSP430_AMODE_IMMEDIATE: + return snprintf(buf, max_len, "#%d", (int16_t)addr); + } + + return snprintf(buf, max_len, "???"); +} + +/* Write assembly language for the instruction to this buffer */ +int dis_format(char *buf, int max_len, + const struct msp430_instruction *insn) +{ + int count = 0; + + /* Opcode mnemonic */ + count = snprintf(buf, max_len, "%s", msp_op_name(insn->op)); + if (insn->is_byte_op) + count += snprintf(buf + count, max_len - count, ".B"); + while (count < 8 && count + 1 < max_len) + buf[count++] = ' '; + + /* Source operand */ + if (insn->itype == MSP430_ITYPE_DOUBLE) { + count += format_operand(buf + count, + max_len - count, + insn->src_mode, + insn->src_addr, + insn->src_reg); + + if (count + 1 < max_len) + buf[count++] = ','; + while (count < 20 && count + 1 < max_len) + buf[count++] = ' '; + } + + /* Destination operand */ + if (insn->itype != MSP430_ITYPE_NOARG) { + if ((insn->op == MSP430_OP_CALL || + insn->op == MSP430_OP_BR) && + insn->dst_mode == MSP430_AMODE_IMMEDIATE) + count += snprintf(buf + count, max_len - count, + "#0x%04x", insn->dst_addr); + else + count += format_operand(buf + count, + max_len - count, + insn->dst_mode, + insn->dst_addr, + insn->dst_reg); + } + + buf[count] = 0; + return count; +} diff --git a/dis.h b/dis.h new file mode 100644 index 0000000..9a33d27 --- /dev/null +++ b/dis.h @@ -0,0 +1,214 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DIS_H_ + +#include + +/* Addressing modes. + * + * Addressing modes are not determined solely by the address mode bits + * in an instruction. Rather, those bits specify one of four possible + * modes (REGISTER, INDEXED, INDIRECT and INDIRECT_INC). Using some of + * these modes in conjunction with special registers like PC or the + * constant generator registers results in extra modes. For example, the + * following code, written using INDIRECT_INC on PC: + * + * MOV @PC+, R5 + * .word 0x5729 + * + * can also be written as an instruction using IMMEDIATE addressing: + * + * MOV #0x5729, R5 + */ +typedef enum { + MSP430_AMODE_REGISTER = 0x0, + MSP430_AMODE_INDEXED = 0x1, + MSP430_AMODE_SYMBOLIC = 0x81, + MSP430_AMODE_ABSOLUTE = 0x82, + MSP430_AMODE_INDIRECT = 0x2, + MSP430_AMODE_INDIRECT_INC = 0x3, + MSP430_AMODE_IMMEDIATE = 0x83 +} msp430_amode_t; + +/* MSP430 registers. + * + * These are divided into: + * + * PC/R0: program counter + * SP/R1: stack pointer + * SR/R2: status register/constant generator 1 + * R3: constant generator 2 + * R4-R15: general purpose registers + */ +typedef enum { + MSP430_REG_PC = 0, + MSP430_REG_SP = 1, + MSP430_REG_SR = 2, + MSP430_REG_R3 = 3, + MSP430_REG_R4 = 4, + MSP430_REG_R5 = 5, + MSP430_REG_R6 = 6, + MSP430_REG_R7 = 7, + MSP430_REG_R8 = 8, + MSP430_REG_R9 = 9, + MSP430_REG_R10 = 10, + MSP430_REG_R11 = 11, + MSP430_REG_R12 = 12, + MSP430_REG_R13 = 13, + MSP430_REG_R14 = 14, + MSP430_REG_R15 = 15, +} msp430_reg_t; + +/* Status register bits. */ +#define MSP430_SR_V 0x0100 +#define MSP430_SR_SCG1 0x0080 +#define MSP430_SR_SCG0 0x0040 +#define MSP430_SR_OSCOFF 0x0020 +#define MSP430_SR_CPUOFF 0x0010 +#define MSP430_SR_GIE 0x0008 +#define MSP430_SR_N 0x0004 +#define MSP430_SR_Z 0x0002 +#define MSP430_SR_C 0x0001 + +/* MSP430 instruction formats. + * + * NOARG is not an actual instruction format recognised by the CPU. + * It is used only for emulated instructions. + */ +typedef enum { + MSP430_ITYPE_NOARG, + MSP430_ITYPE_JUMP, + MSP430_ITYPE_DOUBLE, + MSP430_ITYPE_SINGLE +} msp430_itype_t; + +/* MSP430 operations. + * + * Some of these are emulated instructions. Emulated instructions are + * alternate mnemonics for combinations of some real opcodes with + * common operand values. For example, the following real instruction: + * + * MOV #0, R8 + * + * can be written as the following emulated instruction: + * + * CLR R8 + */ +typedef enum { + /* Single operand */ + MSP430_OP_RRC = 0x1000, + MSP430_OP_SWPB = 0x1080, + MSP430_OP_RRA = 0x1100, + MSP430_OP_SXT = 0x1180, + MSP430_OP_PUSH = 0x1200, + MSP430_OP_CALL = 0x1280, + MSP430_OP_RETI = 0x1300, + + /* Jump */ + MSP430_OP_JNZ = 0x2000, + MSP430_OP_JZ = 0x2400, + MSP430_OP_JNC = 0x2800, + MSP430_OP_JC = 0x2C00, + MSP430_OP_JN = 0x3000, + MSP430_OP_JGE = 0x3400, + MSP430_OP_JL = 0x3800, + MSP430_OP_JMP = 0x3C00, + + /* Double operand */ + MSP430_OP_MOV = 0x4000, + MSP430_OP_ADD = 0x5000, + MSP430_OP_ADDC = 0x6000, + MSP430_OP_SUBC = 0x7000, + MSP430_OP_SUB = 0x8000, + MSP430_OP_CMP = 0x9000, + MSP430_OP_DADD = 0xA000, + MSP430_OP_BIT = 0xB000, + MSP430_OP_BIC = 0xC000, + MSP430_OP_BIS = 0xD000, + MSP430_OP_XOR = 0xE000, + MSP430_OP_AND = 0xF000, + + /* Emulated instructions */ + MSP430_OP_ADC = 0x10000, + MSP430_OP_BR = 0x10001, + MSP430_OP_CLR = 0x10002, + MSP430_OP_CLRC = 0x10003, + MSP430_OP_CLRN = 0x10004, + MSP430_OP_CLRZ = 0x10005, + MSP430_OP_DADC = 0x10006, + MSP430_OP_DEC = 0x10007, + MSP430_OP_DECD = 0x10008, + MSP430_OP_DINT = 0x10009, + MSP430_OP_EINT = 0x1000A, + MSP430_OP_INC = 0x1000B, + MSP430_OP_INCD = 0x1000C, + MSP430_OP_INV = 0x1000D, + MSP430_OP_NOP = 0x1000E, + MSP430_OP_POP = 0x1000F, + MSP430_OP_RET = 0x10010, + MSP430_OP_RLA = 0x10011, + MSP430_OP_RLC = 0x10012, + MSP430_OP_SBC = 0x10013, + MSP430_OP_SETC = 0x10014, + MSP430_OP_SETN = 0x10015, + MSP430_OP_SETZ = 0x10016, + MSP430_OP_TST = 0x10017 +} msp430_op_t; + +#define MSP430_OP_IS_JUMP(o) ((o) >= MSP430_OP_JNZ && (o) <= MSP430_OP_JMP) + +/* This represents a decoded instruction. All decoded addresses are + * absolute or register-indexed, depending on the addressing mode. + * + * For jump instructions, the target address is stored in dst_operand. + */ +struct msp430_instruction { + u_int16_t offset; + int len; + + msp430_op_t op; + msp430_itype_t itype; + int is_byte_op; + + msp430_amode_t src_mode; + u_int16_t src_addr; + msp430_reg_t src_reg; + + msp430_amode_t dst_mode; + u_int16_t dst_addr; + msp430_reg_t dst_reg; +}; + +/* Decode a single instruction. + * + * Returns the number of bytes consumed, or -1 if an error occured. + * + * The caller needs to pass a pointer to the bytes to be decoded, the + * virtual offset of those bytes, and the maximum number available. If + * successful, the decoded instruction is written into the structure + * pointed to by insn. + */ +int dis_decode(u_int8_t *code, u_int16_t offset, u_int16_t len, + struct msp430_instruction *insn); + +/* Write assembly language for the instruction to this buffer */ +int dis_format(char *buf, int max_len, + const struct msp430_instruction *insn); + +#endif diff --git a/fet.c b/fet.c new file mode 100644 index 0000000..a79ab4d --- /dev/null +++ b/fet.c @@ -0,0 +1,605 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "fet.h" + +static const struct fet_transport *fet_transport; + +/********************************************************************* + * Checksum calculation + */ + +static u_int16_t code_left[65536]; + +/* Initialise the code table. The code table is a function which takes + * us from one checksum position code to the next. + */ + +static void init_codes(void) +{ + int i; + + for (i = 0; i < 65536; i++) { + u_int16_t right = i << 1; + + if (i & 0x8000) + right ^= 0x0811; + + code_left[right] = i; + } +} + +/* Calculate the checksum over the given payload and return it. This checksum + * needs to be stored in little-endian format at the end of the payload. + */ + +static u_int16_t calc_checksum(const char *data, int len) +{ + int i; + u_int16_t cksum = 0xffff; + u_int16_t code = 0x8408; + + for (i = len * 8; i; i--) + cksum = code_left[cksum]; + + for (i = len - 1; i >= 0; i--) { + int j; + u_int8_t c = data[i]; + + for (j = 0; j < 8; j++) { + if (c & 0x80) + cksum ^= code; + code = code_left[code]; + c <<= 1; + } + } + + return cksum ^ 0xffff; +} + +/********************************************************************* + * FET packet transfer. This level of the interface deals in packets + * send to/from the device. + */ + +/* This is a type of data transfer which appears to be unique to + * the RF2500. Blocks of data are sent to an internal buffer. Each + * block is prefixed with a buffer offset and a payload length. + * + * No checksums are included. + */ +static int fet_send_data(const char *data, int len) +{ + int offset = 0; + + assert (fet_transport != NULL); + while (len) { + char pbuf[63]; + int plen = len > 59 ? 59 : len; + + pbuf[0] = 0x83; + pbuf[1] = offset & 0xff; + pbuf[2] = offset >> 8; + pbuf[3] = plen; + memcpy(pbuf + 4, data, plen); + if (fet_transport->send(pbuf, plen + 4) < 0) + return -1; + + data += plen; + len -= plen; + offset += plen; + } + + return 0; +} + +static char fet_buf[65538]; +static int fet_len; + +#define BUFFER_BYTE(b, x) ((int)((u_int8_t *)(b))[x]) +#define BUFFER_WORD(b, x) ((BUFFER_BYTE(b, x + 1) << 8) | BUFFER_BYTE(b, x)) + +static const char *fet_recv_packet(int *pktlen) +{ + int plen = BUFFER_WORD(fet_buf, 0); + + assert (fet_transport != NULL); + + /* If there's a packet still here from last time, get rid of it */ + if (fet_len >= plen + 2) { + memmove(fet_buf, fet_buf + plen + 2, fet_len - plen - 2); + fet_len -= plen + 2; + } + + /* Keep adding data to the buffer until we have a complete packet */ + for (;;) { + int len; + + plen = BUFFER_WORD(fet_buf, 0); + if (fet_len >= plen + 2) { + u_int16_t c = calc_checksum(fet_buf + 2, plen - 2); + u_int16_t r = BUFFER_WORD(fet_buf, plen); + + if (pktlen) + *pktlen = plen - 2; + + if (c != r) { + fprintf(stderr, "fet_fecv_packet: checksum " + "error (calc %04x, recv %04x)\n", + c, r); + return NULL; + } + + return fet_buf + 2; + } + + len = fet_transport->recv(fet_buf + fet_len, + sizeof(fet_buf) - fet_len); + if (len < 0) + return NULL; + fet_len += len; + } + + return NULL; +} + +static int fet_send_command(const char *data, int len) +{ + char datapkt[256]; + char buf[256]; + u_int16_t cksum = calc_checksum(data, len); + int i = 0; + int j; + + assert (len + 4 <= sizeof(buf)); + assert (len + 2 <= sizeof(datapkt)); + assert (fet_transport != NULL); + + memcpy(datapkt, data, len); + datapkt[len++] = cksum & 0xff; + datapkt[len++] = cksum >> 8; + + buf[i++] = 0x7e; + for (j = 0; j < len; j++) { + char c = datapkt[j]; + + if (c == 0x7e || c == 0x7d) { + buf[i++] = 0x7d; + c ^= 0x20; + } + + buf[i++] = c; + } + buf[i++] = 0x7e; + + assert (i < sizeof(buf)); + + return fet_transport->send(buf, i); +} + +static const char *fet_send_recv(const char *data, int len, int *recvlen) +{ + const char *buf; + + if (fet_send_command(data, len) < 0) + return NULL; + + buf = fet_recv_packet(recvlen); + if (!buf) + return NULL; + + if (data[0] != buf[0]) { + fprintf(stderr, "fet_send_recv: reply type mismatch\n"); + return NULL; + } + + return buf; +} + +/********************************************************************** + * MSP430 high-level control functions + */ + +int fet_open(const struct fet_transport *tr, int proto_flags, int vcc_mv) +{ + static char config[12] = { + 0x05, 0x02, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + static char vcc[8] = { + 0x06, 0x02, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00 + }; + + fet_transport = tr; + init_codes(); + + /* open */ + if (!fet_send_recv("\x01\x01", 2, NULL)) { + fprintf(stderr, "fet_startup: open failed\n"); + return -1; + } + + /* init */ + if (!fet_send_recv("\x27\x02\x01\x00\x04\x00\x00\x00\x00", 8, NULL)) { + fprintf(stderr, "fet_startup: init failed\n"); + return -1; + } + + /* configure: Spy-Bi-Wire or JTAG */ + config[8] = (proto_flags & FET_PROTO_SPYBIWIRE) ? 1 : 0; + if (!fet_send_recv(config, 12, NULL)) { + fprintf(stderr, "fet_startup: configure failed\n"); + return -1; + } + + /* I don't know what this is. It's RF2500-specific. It may have + * something to do with flash -- 0x1d is sent before an erase. + */ + if (!fet_send_recv("\x1e\x01", 2, NULL)) { + fprintf(stderr, "fet_startup: command 0x1e failed\n"); + return -1; + } + + /* set VCC */ + vcc[4] = vcc_mv & 0xff; + vcc[5] = vcc_mv >> 8; + if (!fet_send_recv(vcc, 8, NULL)) { + fprintf(stderr, "fet_startup: set VCC failed\n"); + return -1; + } + + /* I don't know what this is, but it appears to halt the MSP. Without + * it, memory reads return garbage. This is RF2500-specific. + */ + if (!fet_send_recv("\x28\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 12, NULL)) { + fprintf(stderr, "fet_startup: command 0x28 failed\n"); + return -1; + } + + /* Who knows what this is. Without it, register reads don't work. + * This is RF2500-specific. + */ + { + static char data[] = { + 0x00, 0x80, 0xff, 0xff, 0x00, 0x00, 0x00, 0x10, + 0xff, 0x10, 0x40, 0x00, 0x00, 0x02, 0xff, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x01, 0x00, 0xd7, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x07, 0x10, 0x0e, 0xc4, 0x09, 0x70, 0x17, + 0x58, 0x1b, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x33, 0x0f, 0x1f, 0x0f, + 0xff, 0xff, + }; + + if (fet_send_data(data, sizeof(data)) < 0 || + !fet_send_recv("\x29\x02\x04\x00\x00\x00\x00\x00" + "\x39\x00\x00\x00\x31\x00\x00\x00" + "\x4a\x00\x00\x00", 20, NULL)) { + fprintf(stderr, "fet_startup: command 0x29 failed\n"); + return -1; + } + } + + printf("FET initialized: %s (VCC = %d mV)\n", + (proto_flags & FET_PROTO_SPYBIWIRE) ? + "Spy-Bi-Wire" : "JTAG", vcc_mv); + return 0; +} + +int fet_reset(int flags) +{ + static char reset[] = { + 0x07, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + reset[4] = flags & FET_RESET_ALL; + if (flags & FET_RESET_HALT) { + reset[8] = 0; + reset[12] = 0; + } else { + reset[8] = 1; + reset[12] = 1; + } + + if (!fet_send_recv(reset, 16, NULL)) { + fprintf(stderr, "fet_reset: reset failed\n"); + return -1; + } + + return 0; +} + +int fet_close(void) +{ + if (!fet_send_recv("\x02\x02\x01\x00", 4, NULL)) { + fprintf(stderr, "fet_shutdown: close command failed\n"); + return -1; + } + + fet_transport->close(); + fet_transport = NULL; + + return 0; +} + +int fet_get_context(u_int16_t *regs) +{ + int len; + int i; + const char *buf; + + buf = fet_send_recv("\x08\x01", 2, &len); + if (len < 72) { + fprintf(stderr, "fet_get_context: short reply (%d bytes)\n", + len); + return -1; + } + + for (i = 0; i < FET_NUM_REGS; i++) + regs[i] = BUFFER_WORD(buf, i * 4 + 8); + + return 0; +} + +int fet_set_context(u_int16_t *regs) +{ + char buf[FET_NUM_REGS * 4]; + int i; + + memset(buf, 0, sizeof(buf)); + + for (i = 0; i < FET_NUM_REGS; i++) { + buf[i * 4] = regs[i] & 0xff; + buf[i * 4 + 1] = regs[i] >> 8; + } + + if (fet_send_data(buf, sizeof(buf)) < 0 || + !fet_send_recv("\x09\x02\x02\x00\xff\xff\x00\x00" + "\x40\x00\x00\x00", 12, NULL)) { + fprintf(stderr, "fet_set_context: context set failed\n"); + return -1; + } + + return 0; +} + +int fet_read_mem(u_int16_t addr, char *buffer, int count) +{ + while (count) { + int plen = count > 128 ? 128 : count; + const char *buf; + int len; + + static char readmem[] = { + 0x0d, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + readmem[4] = addr & 0xff; + readmem[5] = addr >> 8; + readmem[8] = plen; + + buf = fet_send_recv(readmem, 12, &len); + if (!buf) { + fprintf(stderr, "fet_read_mem: failed to read " + "from 0x%04x\n", addr); + return -1; + } + + if (len < plen + 8) { + fprintf(stderr, "fet_read_mem: short read " + "(%d bytes)\n", len); + return -1; + } + + memcpy(buffer, buf + 8, plen); + buffer += plen; + count -= plen; + addr += plen; + } + + return 0; +} + +int fet_write_mem(u_int16_t addr, char *buffer, int count) +{ + while (count) { + int plen = count > 128 ? 128 : count; + + static char writemem[] = { + 0x0e, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + writemem[4] = addr & 0xff; + writemem[5] = addr >> 8; + writemem[8] = plen; + + if (fet_send_data(buffer, plen) < 0 || + !fet_send_recv(writemem, 12, NULL)) { + fprintf(stderr, "fet_write_mem: failed to write " + "to 0x%04x\n", addr); + return -1; + } + + buffer += plen; + count -= plen; + addr += plen; + } + + return 0; +} + +int fet_erase(int type, u_int16_t addr) +{ + static char erase[] = { + 0x0c, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + switch (type) { + case FET_ERASE_MAIN: + erase[4] = 1; + erase[8] = 0xe0; + erase[9] = 0xff; + erase[12] = 2; + break; + + case FET_ERASE_ADDR: + erase[8] = addr & 0xff; + erase[9] = addr >> 8; + erase[12] = 2; + break; + + case FET_ERASE_INFO: + erase[9] = 0x10; + erase[13] = 1; + break; + + case FET_ERASE_ALL: + default: + erase[4] = 2; + erase[9] = 0x10; + erase[13] = 0x01; + break; + } + + if (!fet_send_recv("\x1d\x01", 2, NULL)) { + fprintf(stderr, "fet_erase: command 1d failed\n"); + return -1; + } + + if (!fet_send_recv("\x05\x02\x02\x00\x02\x00\x00\x00\x26\x00\x00\x00", + 12, NULL)) { + fprintf(stderr, "fet_erase: config (1) failed\n"); + return -1; + } + + if (!fet_send_recv("\x05\x02\x02\x00\x05\x00\x00\x00\x00\x00\x00\x00", + 12, NULL)) { + fprintf(stderr, "fet_erase: config (2) failed\n"); + return -1; + } + + if (!fet_send_recv(erase, 16, NULL)) { + fprintf(stderr, "fet_erase: erase command failed\n"); + return -1; + } + + return 0; +} + +int fet_poll(void) +{ + const char *reply; + int len; + + /* Without this delay, breakpoints can get lost. */ + if (usleep(500000) < 0) + return -1; + + reply = fet_send_recv("\x12\x02\x01\x00\x00\x00\x00\x00", 8, &len); + if (!reply) { + fprintf(stderr, "fet_poll: polling failed\n"); + return -1; + } + + return reply[6]; +} + +int fet_step(void) +{ + if (!fet_send_recv("\x11\x02\x02\x00\x02\x00\x00\x00\x00\x00\x00\x00", + 12, NULL)) { + fprintf(stderr, "fet_step: failed to single-step\n"); + return -1; + } + + return 0; +} + +int fet_run(void) +{ + if (!fet_send_recv("\x11\x02\x02\x00\x03\x00\x00\x00\x00\x00\x00\x00", + 12, NULL)) { + fprintf(stderr, "fet_run: run failed\n"); + return -1; + } + + return 0; +} + +int fet_stop(void) +{ + if (!fet_send_recv("\x12\x02\x01\x00\x01\x00\x00\x00", 8, NULL)) { + fprintf(stderr, "fet_stop: stop failed\n"); + return -1; + } + + return 0; +} + +int fet_break(int enable, u_int16_t addr) +{ + static char buf[] = { + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 + }; + + if (enable) { + buf[12] = addr & 0xff; + buf[13] = addr >> 8; + buf[30] = 0xff; + buf[31] = 0xff; + buf[36] = 2; + buf[52] = 3; + } else { + buf[12] = 0; + buf[13] = 0; + buf[30] = 0; + buf[31] = 0; + buf[36] = 0; + buf[52] = 1; + } + + if (fet_send_data(buf, sizeof(buf)) < 0 || + !fet_send_recv("\x2a\x02\x04\x00\x08\x00\x00\x00\xb0\x00\x00\x00" + "\x00\x00\x00\x00\x40\x00\x00\x00", 20, NULL)) { + fprintf(stderr, "fet_break: set breakpoint failed\n"); + return -1; + } + + return 0; +} + diff --git a/fet.h b/fet.h new file mode 100644 index 0000000..9c737ee --- /dev/null +++ b/fet.h @@ -0,0 +1,106 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FET_H_ +#define FET_H_ + +#include + +/* This structure is used to provide an interface to a lower-level + * transport. The transport mechanism is viewed as a stream by the FET + * controller, which handles packet encapsulation, checksums and other + * high-level functions. + */ +struct fet_transport { + int (*send)(const char *data, int len); + int (*recv)(char *data, int max_len); + void (*close)(void); +}; + +/* Start up the FET controller by specifying a transport, a voltage in + * millivolts and a set of protocol mode flags. + */ +#define FET_PROTO_SPYBIWIRE 0x01 + +int fet_open(const struct fet_transport *transport, + int proto_flags, int vcc_mv); + +/* Shut down the connection to the FET. This also closes the underlying + * transport. + */ +int fet_close(void); + +/* Issue a reset to the CPU. The CPU can be reset via any combination + * of three methods, and you can choose whether or not to leave the CPU + * halted after reset. + */ +#define FET_RESET_PUC 0x01 +#define FET_RESET_RST 0x02 +#define FET_RESET_VCC 0x04 +#define FET_RESET_ALL 0x07 +#define FET_RESET_HALT 0x10 + +int fet_reset(int flags); + +/* Retrieve and store register values. There are 16 16-bit registers in + * the MSP430 CPU. regs must always be a pointer to an array of 16 + * u_int16_t. + */ +#define FET_NUM_REGS 16 + +int fet_get_context(u_int16_t *regs); +int fet_set_context(u_int16_t *regs); + +/* Erase the CPU's internal flash. */ +#define FET_ERASE_ALL 0x01 +#define FET_ERASE_MAIN 0x02 +#define FET_ERASE_ADDR 0x03 +#define FET_ERASE_INFO 0x04 + +int fet_erase(int type, u_int16_t addr); + +/* Read and write memory. fet_write_mem can be used to reflash the + * device, but only after an erase. + */ +int fet_read_mem(u_int16_t addr, char *buffer, int count); +int fet_write_mem(u_int16_t addr, char *buffer, int count); + +/* Fetch the device status. If the device is currently running, then + * the FET_POLL_RUNNING flag will be set. FET_POLL_BREAKPOINT is set + * when the device hits the preset breakpoint, and then resets on the + * next call to fet_poll(). + */ +#define FET_POLL_RUNNING 0x01 +#define FET_POLL_BREAKPOINT 0x02 + +int fet_poll(void); + +/* CPU run/step/stop control. While the CPU is running, memory and + * registers are inaccessible (only fet_poll() or fet_stop()) will + * work. fet_step() is used to single-step the CPU. + */ +int fet_step(void); +int fet_run(void); +int fet_stop(void); + +/* Set or clear the breakpoint address. Only one breakpoint can be set + * at a time. + */ +int fet_break(int enable, u_int16_t addr); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..921c10d --- /dev/null +++ b/main.c @@ -0,0 +1,602 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "dis.h" +#include "fet.h" +#include "rf2500.h" + +static void hexdump(int addr, const char *data, int len) +{ + int offset = 0; + + while (offset < len) { + int i, j; + + /* Address label */ + printf(" %04x:", offset + addr); + + /* Hex portion */ + for (i = 0; i < 16 && offset + i < len; i++) + printf(" %02x", + ((const unsigned char *)data)[offset + i]); + for (j = i; j < 16; j++) + printf(" "); + + /* Printable characters */ + printf(" |"); + for (j = 0; j < i; j++) { + int c = ((const unsigned char *)data)[offset + j]; + + printf("%c", (c >= 32 && c <= 126) ? c : '.'); + } + for (; j < 16; j++) + printf(" "); + printf("|\n"); + + offset += i; + } +} + +/********************************************************************** + * Command-line interface + */ + +char *get_arg(char **text) +{ + char *start; + char *end; + + if (!text) + return NULL; + + start = *text; + while (*start && isspace(*start)) + start++; + + if (!*start) + return NULL; + + end = start; + while (*end && !isspace(*end)) + end++; + + if (*end) + while (*end && isspace(*end)) + *(end++) = 0; + + *text = end; + return start; +} + +#define REG_COLUMNS 4 +#define REG_ROWS ((FET_NUM_REGS + REG_COLUMNS - 1) / REG_COLUMNS) + +static void show_regs(u_int16_t *regs) +{ + int i; + + for (i = 0; i < REG_ROWS; i++) { + int j; + + printf(" "); + for (j = 0; j < REG_COLUMNS; j++) { + int k = j * REG_ROWS + i; + + if (k < FET_NUM_REGS) + printf("(r%02d: %04x) ", k, regs[k]); + } + printf("\n"); + } +} + +struct command { + const char *name; + int (*func)(char **arg); + const char *help; +}; + +static const struct command all_commands[]; + +static int cmd_help(char **arg); + +static int cmd_md(char **arg) +{ + char *off_text = get_arg(arg); + char *len_text = get_arg(arg); + unsigned int offset = 0; + unsigned int length = 0; + + if (!off_text) { + fprintf(stderr, "md: offset must be specified\n"); + return -1; + } + + sscanf(off_text, "%x", &offset); + if (len_text) + sscanf(len_text, "%x", &length); + else + length = 0x80; + if (offset >= 0x10000 || length > 0x10000 || + (offset + length) > 0x10000) { + fprintf(stderr, "md: memory out of range\n"); + return -1; + } + + while (length) { + char buf[128]; + int blen = length > sizeof(buf) ? sizeof(buf) : length; + + if (fet_read_mem(offset, buf, blen) < 0) + return -1; + hexdump(offset, buf, blen); + + offset += blen; + length -= blen; + } + + return 0; +} + +static void disassemble(u_int16_t offset, u_int8_t *data, int length) +{ + while (length) { + struct msp430_instruction insn; + int retval; + int count; + int i; + + retval = dis_decode(data, offset, length, &insn); + count = retval > 0 ? retval : 2; + if (count > length) + count = length; + printf(" %04x:", offset); + + for (i = 0; i < count; i++) + printf(" %02x", data[i]); + + while (i < 8) { + printf(" "); + i++; + } + + if (retval >= 0) { + char buf[32]; + + dis_format(buf, sizeof(buf), &insn); + printf("%s", buf); + } + + printf("\n"); + + offset += count; + length -= count; + data += count; + } +} + +static int cmd_dis(char **arg) +{ + char *off_text = get_arg(arg); + char *len_text = get_arg(arg); + unsigned int offset = 0; + unsigned int length = 0; + char buf[128]; + + if (!off_text) { + fprintf(stderr, "md: offset must be specified\n"); + return -1; + } + + sscanf(off_text, "%x", &offset); + if (len_text) + sscanf(len_text, "%x", &length); + else + length = 0x40; + if (offset >= 0x10000 || length > sizeof(buf) || + (offset + length) > 0x10000) { + fprintf(stderr, "dis: memory out of range\n"); + return -1; + } + + if (fet_read_mem(offset, buf, length) < 0) + return -1; + + disassemble(offset, (u_int8_t *)buf, length); + return 0; +} + +static int cmd_reset(char **arg) +{ + return fet_reset(FET_RESET_ALL | FET_RESET_HALT); +} + +static int cmd_regs(char **arg) +{ + u_int16_t regs[FET_NUM_REGS]; + char code[16]; + + if (fet_get_context(regs) < 0) + return -1; + show_regs(regs); + + /* Try to disassemble the instruction at PC */ + if (fet_read_mem(regs[0], code, sizeof(code)) < 0) + return 0; + + disassemble(regs[0], (u_int8_t *)code, sizeof(code)); + return 0; +} + +static int cmd_run(char **arg) +{ + char *bp_text = get_arg(arg); + + if (bp_text) { + unsigned int addr = 0; + + sscanf(bp_text, "%x", &addr); + fet_break(1, addr); + } else { + fet_break(0, 0); + } + + if (fet_run() < 0) + return -1; + + printf("Running. Press Ctrl+C to interrupt..."); + fflush(stdout); + + for (;;) { + int r = fet_poll(); + + if (r < 0 || !(r & FET_POLL_RUNNING)) + break; + } + + printf("\n"); + if (fet_stop() < 0) + return -1; + + return cmd_regs(NULL); +} + +static int cmd_set(char **arg) +{ + char *reg_text = get_arg(arg); + char *val_text = get_arg(arg); + int reg; + unsigned int value = 0; + u_int16_t regs[FET_NUM_REGS]; + + if (!(reg_text && val_text)) { + fprintf(stderr, "set: must specify a register and a value\n"); + return -1; + } + + while (*reg_text && !isdigit(*reg_text)) + reg_text++; + reg = atoi(reg_text); + sscanf(val_text, "%x", &value); + + if (reg < 0 || reg >= FET_NUM_REGS) { + fprintf(stderr, "set: register out of range: %d\n", reg); + return -1; + } + + if (fet_get_context(regs) < 0) + return -1; + regs[reg] = value; + if (fet_set_context(regs) < 0) + return -1; + + show_regs(regs); + return 0; +} + +static int cmd_step(char **arg) +{ + if (fet_step() < 0) + return -1; + if (fet_poll() < 0) + return -1; + + return cmd_regs(NULL); +} + +static int hexval(const char *text, int len) +{ + int value = 0; + + while (len && *text) { + value <<= 4; + + if (*text >= 'A' && *text <= 'F') + value += *text - 'A' + 10; + else if (*text >= 'a' && *text <= 'f') + value += *text - 'a' + 10; + else if (isdigit(*text)) + value += *text - '0'; + + text++; + len--; + } + + return value; +} + +static char prog_buf[128]; +static u_int16_t prog_addr; +static int prog_len; + +static int prog_flush(void) +{ + int wlen = prog_len; + + if (!prog_len) + return 0; + + /* Writing across this address seems to cause a hang */ + if (prog_addr < 0x999a && wlen + prog_addr > 0x999a) + wlen = 0x999a - prog_addr; + + printf("Writing %3d bytes to %04x...\n", wlen, prog_addr); + + if (fet_write_mem(prog_addr, prog_buf, wlen) < 0) + return -1; + + memmove(prog_buf, prog_buf + wlen, prog_len - wlen); + prog_len -= wlen; + prog_addr += wlen; + + return 0; +} + +static int prog_hex(int lno, const char *hex) +{ + int len = strlen(hex); + int count, address, type, cksum = 0; + int i; + + if (*hex != ':') + return 0; + + hex++; + len--; + + while (len && isspace(hex[len - 1])) + len--; + + if (len < 10) + return 0; + + count = hexval(hex, 2); + address = hexval(hex + 2, 4); + type = hexval(hex + 6, 2); + + if (type) + return 0; + + for (i = 0; i + 2 < len; i += 2) + cksum = (cksum + hexval(hex + i, 2)) + & 0xff; + cksum = ~(cksum - 1) & 0xff; + + if (count * 2 + 10 != len) { + fprintf(stderr, "warning: length mismatch at line %d\n", lno); + count = (len - 10) / 2; + } + + if (cksum != hexval(hex + len - 2, 2)) + fprintf(stderr, "warning: invalid checksum at line %d\n", lno); + + for (i = 0; i < count; i++) { + int offset; + + offset = address + i - prog_addr; + if (offset < 0 || offset >= sizeof(prog_buf)) + if (prog_flush() < 0) + return -1; + + if (!prog_len) + prog_addr = address + i; + + offset = address + i - prog_addr; + prog_buf[offset] = hexval(hex + 8 + i * 2, 2); + if (offset + 1 > prog_len) + prog_len = offset + 1; + } + + return 0; +} + +static int cmd_prog(char **arg) +{ + FILE *in = fopen(*arg, "r"); + char text[256]; + int lno = 1; + + if (!in) { + fprintf(stderr, "prog: %s: %s\n", *arg, strerror(errno)); + return -1; + } + + printf("Erasing...\n"); + if (fet_erase(FET_ERASE_ALL, 0) < 0) { + fclose(in); + return -1; + } + + if (fet_reset(FET_RESET_ALL | FET_RESET_HALT) < 0) + return -1; + + prog_len = 0; + while (fgets(text, sizeof(text), in)) + if (prog_hex(lno++, text) < 0) { + fclose(in); + return -1; + } + fclose(in); + + if (prog_flush() < 0) + return -1; + + return fet_reset(FET_RESET_ALL | FET_RESET_HALT); +} + +static const struct command all_commands[] = { + {"dis", cmd_dis, +"dis
\n" +" Disassemble a section of memory.\n"}, + {"help", cmd_help, +"help [command]\n" +" Without arguments, displays a list of commands. With a command name as\n" +" an argument, displays help for that command.\n"}, + {"md", cmd_md, +"md
\n" +" Read the specified number of bytes from memory at the given address,\n" +" and display a hexdump.\n"}, + {"prog", cmd_prog, +"prog \n" +" Erase the device and flash the data contained in an Intel HEX file.\n"}, + {"regs", cmd_regs, +"regs\n" +" Read and display the current register contents.\n"}, + {"reset", cmd_reset, +"reset\n" +" Reset (and halt) the CPU.\n"}, + {"run", cmd_run, +"run [breakpoint]\n" +" Run the CPU until either a specified breakpoint occurs or the command\n" +" is interrupted.\n"}, + {"set", cmd_set, +"set \n" +" Change the value of a CPU register.\n"}, + {"step", cmd_step, +"step\n" +" Single-step the CPU, and display the register state.\n"}, +}; + +#define NUM_COMMANDS (sizeof(all_commands) / sizeof(all_commands[0])) + +const struct command *find_command(const char *name) +{ + int i; + + for (i = 0; i < NUM_COMMANDS; i++) + if (!strcasecmp(name, all_commands[i].name)) + return &all_commands[i]; + + return NULL; +} + +static int cmd_help(char **arg) +{ + char *topic = get_arg(arg); + + if (topic) { + const struct command *cmd = find_command(topic); + + if (!cmd) { + fprintf(stderr, "help: unknown command: %s\n", topic); + return -1; + } + + fputs(cmd->help, stdout); + } else { + int i; + + printf("Available commands:"); + for (i = 0; i < NUM_COMMANDS; i++) + printf(" %s", all_commands[i].name); + printf("\n"); + printf("Type \"help \" for more information.\n"); + printf("Press Ctrl+D to quit.\n"); + } + + return 0; +} + +static void sigint_handler(int signum) +{ +} + +int main(void) +{ + const static struct sigaction siga = { + .sa_handler = sigint_handler, + .sa_flags = 0 + }; + + puts( +"MSPDebug version 0.1+ - debugging tool for the eZ430\n" +"Copyright (C) 2009 Daniel Beer \n" +"This is free software; see the source for copying conditions. There is NO\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + + if (rf2500_open() < 0) + return -1; + + sigaction(SIGINT, &siga, NULL); + cmd_help(NULL); + + for (;;) { + char buf[128]; + int len; + char *arg = buf; + char *cmd_text; + + printf("(mspdebug) "); + fflush(stdout); + if (!fgets(buf, sizeof(buf), stdin)) { + if (feof(stdin)) + break; + printf("\n"); + continue; + } + + len = strlen(buf); + while (len && isspace(buf[len - 1])) + len--; + buf[len] = 0; + + cmd_text = get_arg(&arg); + if (cmd_text) { + const struct command *cmd = find_command(cmd_text); + + if (cmd) + cmd->func(&arg); + else + fprintf(stderr, "unknown command: %s " + "(try \"help\")\n", + cmd_text); + } + } + + printf("\n"); + fet_run(); + fet_close(); + + return 0; +} diff --git a/rf2500.c b/rf2500.c new file mode 100644 index 0000000..8153d0c --- /dev/null +++ b/rf2500.c @@ -0,0 +1,210 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "fet.h" + +/********************************************************************* + * USB transport + * + * These functions handle the details of slicing data over USB + * transfers. The interface presented is a continuous byte stream with + * no slicing codes. + * + * Writes are unbuffered -- a single write translates to at least + * one transfer. + */ + +#define USB_FET_VENDOR 0x0451 +#define USB_FET_PRODUCT 0xf432 +#define USB_FET_INTERFACE_CLASS 3 + +#define USB_FET_IN_EP 0x81 +#define USB_FET_OUT_EP 0x01 + +static int usbtr_int_number; +static struct usb_dev_handle *usbtr_handle; + +static int usbtr_open_interface(struct usb_device *dev, int ino) +{ + printf("Trying to open interface %d on %s\n", ino, dev->filename); + + usbtr_int_number = ino; + + usbtr_handle = usb_open(dev); + if (!usbtr_handle) { + perror("usbtr_open_interface: can't open device"); + return -1; + } + + if (usb_detach_kernel_driver_np(usbtr_handle, usbtr_int_number) < 0) + perror("usbtr_open_interface: warning: can't " + "detach kernel driver"); + + if (usb_claim_interface(usbtr_handle, usbtr_int_number) < 0) { + perror("usbtr_open_interface: can't claim interface"); + usb_close(usbtr_handle); + return -1; + } + + return 0; +} + +static int usbtr_open_device(struct usb_device *dev) +{ + struct usb_config_descriptor *c = &dev->config[0]; + int i; + + for (i = 0; i < c->bNumInterfaces; i++) { + struct usb_interface *intf = &c->interface[i]; + struct usb_interface_descriptor *desc = &intf->altsetting[0]; + + if (desc->bInterfaceClass == USB_FET_INTERFACE_CLASS && + !usbtr_open_interface(dev, desc->bInterfaceNumber)) + return 0; + } + + return -1; +} + +static int usbtr_send(const char *data, int len) +{ + while (len) { + char pbuf[256]; + int plen = len > 255 ? 255 : len; + int txlen = plen + 1; + + memcpy(pbuf + 1, data, plen); + + /* This padding is needed to work around an apparent bug in + * the RF2500 FET. Without this, the device hangs. + */ + if (txlen > 32 && (txlen & 0x3f)) + while (txlen < 255 && (txlen & 0x3f)) + pbuf[txlen++] = 0xff; + else if (txlen > 16 && (txlen & 0xf)) + while (txlen < 255 && (txlen & 0xf) != 1) + pbuf[txlen++] = 0xff; + pbuf[0] = txlen - 1; + +#ifdef DEBUG_USBTR + puts("USB transfer out:"); + hexdump(0, pbuf, txlen); +#endif + if (usb_bulk_write(usbtr_handle, USB_FET_OUT_EP, + pbuf, txlen, 10000) < 0) { + perror("usbtr_send"); + return -1; + } + + data += plen; + len -= plen; + } + + return 0; +} + +static char usbtr_buf[64]; +static int usbtr_len; +static int usbtr_offset; + +static void usbtr_flush(void) +{ + char buf[64]; + + while (usb_bulk_read(usbtr_handle, USB_FET_IN_EP, + buf, sizeof(buf), 100) >= 0); +} + +static int usbtr_recv(char *databuf, int max_len) +{ + int rlen; + + if (usbtr_offset >= usbtr_len) { + if (usb_bulk_read(usbtr_handle, USB_FET_IN_EP, + usbtr_buf, sizeof(usbtr_buf), 10000) < 0) { + perror("usbtr_recv"); + return -1; + } + +#ifdef DEBUG_USBTR + puts("USB transfer in:"); + hexdump(0, usbtr_buf, 64); +#endif + + usbtr_len = usbtr_buf[1] + 2; + if (usbtr_len > sizeof(usbtr_buf)) + usbtr_len = sizeof(usbtr_buf); + usbtr_offset = 2; + } + + rlen = usbtr_len - usbtr_offset; + if (rlen > max_len) + rlen = max_len; + memcpy(databuf, usbtr_buf + usbtr_offset, rlen); + usbtr_offset += rlen; + + return rlen; +} + +static void usbtr_close(void) +{ + usb_release_interface(usbtr_handle, usbtr_int_number); + usb_close(usbtr_handle); +} + +static const struct fet_transport usbtr_transport = { + .send = usbtr_send, + .recv = usbtr_recv, + .close = usbtr_close +}; + +int rf2500_open(void) +{ + struct usb_bus *bus; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) { + struct usb_device *dev; + + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == USB_FET_VENDOR && + dev->descriptor.idProduct == USB_FET_PRODUCT && + !usbtr_open_device(dev)) { + usbtr_flush(); + + if (fet_open(&usbtr_transport, + FET_PROTO_SPYBIWIRE, 3000) < 0) { + usbtr_close(); + return -1; + } + + return 0; + } + } + } + + fprintf(stderr, "usbtr_open: no devices could be found\n"); + return -1; +} diff --git a/rf2500.h b/rf2500.h new file mode 100644 index 0000000..cdfb847 --- /dev/null +++ b/rf2500.h @@ -0,0 +1,28 @@ +/* MSPDebug - debugging tool for the eZ430 + * Copyright (C) 2009 Daniel Beer + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RF2500_H_ +#define RF2500_H_ + +/* Search the USB bus for the first eZ430-RF2500, and initialize it. If + * successful, 0 is returned and the fet_* functions are ready for use. + * If an error occurs, -1 is returned. + */ +int rf2500_open(void); + +#endif