/* MSPDebug - debugging tool for the eZ430 * Copyright (C) 2009, 2010, 2020 Daniel Beer * Copyright (C) 2020 Bruce G. Burns * * 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 "device.h" #include "dis.h" #include "util.h" #include "output.h" #include "sim.h" #include "simio_cpu.h" #include "ctrlc.h" #define MEM_SIZE (1<<17) #define ADDR_BYTE_IO_END 0x100 #define SIMx dev->base.type->name struct sim_device { struct device base; uint8_t memory[MEM_SIZE]; uint32_t regs[DEVICE_NUM_REGS]; int running; uint32_t current_insn; int watchpoint_hit; int cpux; uint32_t addr_io_end; }; #define WIDTH_UNDEFINED 0 static int mem_setb(struct sim_device *dev, uint32_t offset, uint8_t value); static int mem_setw(struct sim_device *dev, uint32_t offset, uint16_t value); static int mem_seta(struct sim_device *dev, uint32_t offset, uint32_t value); static uint16_t mem_getw(struct sim_device *dev, uint32_t offset); static uint32_t mem_geta(struct sim_device *dev, uint32_t offset); static void add_to_pc(struct sim_device *dev, int16_t offset); static int mem_setb(struct sim_device *dev, uint32_t offset, uint8_t value) { if (offset >= MEM_SIZE) { printc_err("%s: write to nonexistent addr 0x%05x at PC = 0x%05x\n", SIMx,offset,dev->current_insn); return -1; } uint8_t *mem = dev->memory; mem[offset] = value; return 0; } static int mem_setw(struct sim_device *dev, uint32_t offset, uint16_t value) { if (offset >= MEM_SIZE) { printc_err("%s: write to nonexistent addr 0x%05x at PC = 0x%05x\n", SIMx,offset,dev->current_insn); return -1; } uint8_t *mem = dev->memory; offset &= ~1; mem[offset + 0] = value; mem[offset + 1] = value >> 8; return 0; } static int mem_seta(struct sim_device *dev, uint32_t offset, uint32_t value) { if (mem_setw(dev,offset,value) < 0) return -1; return mem_setw(dev,offset+2,(value >> 16) & 0xF); } static uint16_t mem_getw(struct sim_device *dev, uint32_t offset) { offset &= ~1; if (offset >= MEM_SIZE) { printc_err("%s: read from nonexistent addr 0x%05x at PC = 0x%05x\n", SIMx,offset,dev->current_insn); return -1; } uint8_t *mem = dev->memory; return (mem[offset] | (mem[offset+1] << 8)); } static uint32_t mem_geta(struct sim_device *dev, uint32_t offset) { return mem_getw(dev,offset) | ((mem_getw(dev,offset+2) & 0xF) << 16); } static void add_to_pc(struct sim_device *dev, int16_t offset) { uint32_t pc = (dev->regs[MSP430_REG_PC] + offset) & 0xFFFFF; if (!dev->cpux) pc &= 0x0FFFF; dev->regs[MSP430_REG_PC] = pc; } static int invalid_opcode(struct sim_device *dev) { printc_err("%s: invalid opcode at PC = 0x%05x\n", SIMx, dev->current_insn); if (!dev->cpux) printc_err("perhaps you should use 'simx' instead of 'sim'?\n"); return -1; } static void watchpoint_check(struct sim_device *dev, uint16_t addr, int is_write) { int i; for (i = 0; i < DEVICE_MAX_BREAKPOINTS; i++) { const struct device_breakpoint *bp = &dev->base.breakpoints[i]; if ((bp->flags & DEVICE_BP_ENABLED) && (bp->addr == addr) && ((bp->type == DEVICE_BPTYPE_WATCH || (bp->type == DEVICE_BPTYPE_READ && !is_write) || (bp->type == DEVICE_BPTYPE_WRITE && is_write)))) { printc_dbg("Watchpoint %d triggered (0x%04x, %s)\n", i, addr, is_write ? "WRITE" : "READ"); dev->watchpoint_hit = 1; return; } } } static int fetch_operand(struct sim_device *dev, int amode, int reg, int opwidth, uint32_t *addr_ret, uint32_t *data_ret, int ext, int ext_imm) { uint32_t addr = 0; uint32_t mask = (1 << opwidth) - 1; int is_20bit_imm = 0; switch (amode) { case MSP430_AMODE_REGISTER: if (reg == MSP430_REG_R3) { if (data_ret) *data_ret = 0; return 0; } if (data_ret) *data_ret = dev->regs[reg] & mask; return 0; case MSP430_AMODE_INDEXED: if (reg == MSP430_REG_R3) { if (data_ret) *data_ret = 1; return 0; } addr = mem_getw(dev, dev->regs[MSP430_REG_PC]); if (ext) addr |= ext_imm << 16; else if (addr & 0x8000) addr |= 0xF0000; if (reg != MSP430_REG_SR) addr += dev->regs[reg]; if (!ext && (dev->regs[reg] & 0xF0000) == 0) addr &= 0x0FFFF; else addr &= 0xFFFFF; add_to_pc(dev,2); break; case MSP430_AMODE_INDIRECT: if (reg == MSP430_REG_SR) { if (data_ret) *data_ret = 4; return 0; } if (reg == MSP430_REG_R3) { if (data_ret) *data_ret = 2; return 0; } addr = dev->regs[reg]; break; case MSP430_AMODE_INDIRECT_INC: if (reg == MSP430_REG_PC && opwidth == 20) { is_20bit_imm = 1; } if (reg == MSP430_REG_SR) { if (data_ret) *data_ret = 8; return 0; } if (reg == MSP430_REG_R3) { if (data_ret) *data_ret = mask; return 0; } addr = dev->regs[reg]; dev->regs[reg] += (reg == MSP430_REG_PC) ? 2 : (opwidth == 20) ? 4 : (reg == MSP430_REG_SP) ? 2 : (opwidth == 16) ? 2 : 1; break; } if (addr_ret) *addr_ret = addr; int ret = 0; if (data_ret) { watchpoint_check(dev, addr, 0); if (addr < dev->addr_io_end) { if (opwidth == 8) { uint8_t byte; ret = simio_read_b(addr, &byte); *data_ret = byte; } else { uint16_t lsw; ret = simio_read(addr, &lsw); if (ret != 0) return ret; if (opwidth == 20) { uint16_t msw; ret = simio_read(addr+2, &msw); *data_ret = ((msw << 16) | lsw) & 0xFFFFF; } else { *data_ret = lsw; } } } else if (opwidth != 20 || is_20bit_imm) { uint16_t wd = mem_getw(dev, addr); if (opwidth == 8 && (addr & 1)) wd >>= 8; *data_ret = (wd | (ext_imm << 16)) & mask; } else { *data_ret = mem_geta(dev,addr) & mask; } } return ret; } static int store_operand(struct sim_device *dev, int amode, int reg, int opwidth, uint16_t addr, uint32_t data) { if (amode == MSP430_AMODE_REGISTER) { uint32_t mask = ((1 << opwidth) - 1); dev->regs[reg] = mask & data; return 0; } watchpoint_check(dev, addr, 1); int ret = 0; if (opwidth == 8) ret = mem_setb(dev, addr, data); else if (opwidth == 20) ret = mem_seta(dev, addr, data); else ret = mem_setw(dev, addr, data); if (ret != 0) return ret; if (addr < dev->addr_io_end) { if (opwidth == 8) return simio_write_b(addr, data); int ret = simio_write(addr, data); if (ret != 0 || opwidth != 20) return ret; return simio_write(addr + 2, data >> 16); } return 0; } #define ARITH_BITS (MSP430_SR_V | MSP430_SR_N | MSP430_SR_Z | MSP430_SR_C) static int determine_op_width(uint16_t ins, uint16_t ext) { uint16_t opcode = ins & 0xff80; /* handle inconsistent SXTX and SWPBX encoding */ if (ext && (opcode == MSP430_OP_SWPB || opcode == MSP430_OP_SXT)) return (ins & 0x40) ? WIDTH_UNDEFINED : (ext & 0x40) ? 16 : 20; else if (!ext || (ext & 0x0040)) return (ins & 0x0040) ? 8 : 16; else return (ins & 0x0040) ? 20 : WIDTH_UNDEFINED; } static int step_double(struct sim_device *dev, uint16_t ins, uint16_t ext) { uint16_t opcode = ins & 0xf000; int sreg = (ins >> 8) & 0xf; int amode_dst = (ins >> 7) & 1; int amode_src = (ins >> 4) & 0x3; int dreg = ins & 0x000f; uint32_t src_data; uint32_t dst_addr = 0; uint32_t dst_data; uint32_t res_data = 0; uint32_t shiftMask = 0x000f; uint32_t i = 0; int cycles; int rept = 1; uint16_t zc_sr_mask = ~0; int opwidth = determine_op_width(ins,ext); if (opwidth == WIDTH_UNDEFINED) { printc_err("%s: invalid op width encoding at PC = 0x%04x\n", SIMx,dev->current_insn); return -1; } uint32_t mask = (1 << opwidth) - 1; uint32_t msb = 1 << (opwidth - 1); int ext_src_bits = (ext >> 7) & 0xF; int ext_dst_bits = (ext >> 0) & 0xF; if (ext && amode_src == MSP430_AMODE_REGISTER && amode_dst == MSP430_AMODE_REGISTER) { /* certain ext features only supported on reg-reg ops */ if (ext & (1<<7)) rept = (dev->regs[ext_dst_bits] & 0xF) + 1; else rept = ext_dst_bits + 1; if (ext & 0x0100) zc_sr_mask = ~MSP430_SR_C; } if (!dev->cpux) { /* original CPU timing */ if (amode_dst == MSP430_AMODE_REGISTER && dreg == MSP430_REG_PC) { if (amode_src == MSP430_AMODE_REGISTER || amode_src == MSP430_AMODE_INDIRECT) cycles = 2; else cycles = 3; } else if (sreg == MSP430_REG_SR || sreg == MSP430_REG_R3) { if (amode_dst == MSP430_AMODE_REGISTER) cycles = 1; else cycles = 4; } else { if (amode_src == MSP430_AMODE_INDIRECT || amode_src == MSP430_AMODE_INDIRECT_INC) cycles = 2; else if (amode_src == MSP430_AMODE_INDEXED) cycles = 3; else cycles = 1; if (amode_dst == MSP430_AMODE_INDEXED) cycles += 3; } } else { /* CPUX timing */ cycles = 1; /* read opcode */ if (ext) cycles += 1; /* read ext wd */ if (amode_src == MSP430_AMODE_INDEXED) cycles += 1; /* read offset */ if (amode_src != MSP430_AMODE_REGISTER) { cycles += 1; /* read src value */ if (opwidth > 16 && (sreg != MSP430_REG_PC || amode_src != MSP430_AMODE_INDIRECT_INC)) cycles += 1; /* read src value high bits */ } if (amode_dst == MSP430_AMODE_INDEXED) { cycles += 1; /* read offset; */ if (opcode != MSP430_OP_MOV) { cycles += 1; /* read dst value */ if (opwidth > 16) cycles += 1; /* read dst value high bits */ } if (opcode != MSP430_OP_BIT && opcode != MSP430_OP_CMP) { cycles += 1; /* write dst value */ if (opwidth > 16) cycles += 1; /* write dst value high bits */ } } else if (dreg == MSP430_REG_PC) { if (opcode != MSP430_OP_MOV && opcode != MSP430_OP_ADD && opcode != MSP430_OP_SUB) cycles += 1; /* pipelining hit */ if (amode_src != MSP430_AMODE_INDIRECT_INC || sreg != MSP430_REG_PC) cycles += 1; /* pipelining hit */ } cycles += rept - 1; } if (fetch_operand(dev, amode_src, sreg, opwidth, NULL, &src_data, ext, ext_src_bits) < 0) return -1; if (fetch_operand(dev, amode_dst, dreg, opwidth, &dst_addr, opcode == MSP430_OP_MOV ? NULL : &dst_data, ext, ext_dst_bits) < 0) return -1; while (rept--) { uint32_t src_save = src_data; switch (opcode) { case MSP430_OP_MOV: res_data = src_data; break; case MSP430_OP_SUB: case MSP430_OP_SUBC: case MSP430_OP_CMP: src_data ^= mask; case MSP430_OP_ADD: case MSP430_OP_ADDC: if (opcode == MSP430_OP_ADDC || opcode == MSP430_OP_SUBC) res_data = (dev->regs[MSP430_REG_SR] & zc_sr_mask & MSP430_SR_C) ? 1 : 0; else if (opcode == MSP430_OP_SUB || opcode == MSP430_OP_CMP) res_data = 1; else res_data = 0; res_data += src_data; res_data += dst_data; dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; if (!(res_data & mask)) dev->regs[MSP430_REG_SR] |= MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; if (res_data & (msb << 1)) dev->regs[MSP430_REG_SR] |= MSP430_SR_C; if ((src_data ^ dst_data ^ res_data ^ (res_data>>1)) & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_V; break; case MSP430_OP_DADD: res_data = 0; if (dev->regs[MSP430_REG_SR] & zc_sr_mask & MSP430_SR_C) res_data++; shiftMask = 0x000f; for(i = 0; i < 5; ++i) { res_data += (src_data & shiftMask) + (dst_data & shiftMask); if( (res_data & (0x1f << (i*4))) > (9 << (i*4))) { res_data += 6 << (i*4); res_data |= (0x10 << (i*4)); res_data &= ~(0x20 << (i*4)); } shiftMask = shiftMask << 4; } dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; if (!(res_data & mask)) dev->regs[MSP430_REG_SR] |= MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; if (res_data & (msb << 1)) dev->regs[MSP430_REG_SR] |= MSP430_SR_C; /* V not specified for DADD, but FR5939 appears to match: */ const int S = opwidth - 4; if ( (!((src_data^dst_data)&msb) && ( ((8<= (20<regs[MSP430_REG_SR] |= MSP430_SR_V; break; case MSP430_OP_BIT: case MSP430_OP_AND: res_data = src_data & dst_data; dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; dev->regs[MSP430_REG_SR] |= (res_data & mask) ? MSP430_SR_C : MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; break; case MSP430_OP_BIC: res_data = dst_data & ~src_data; break; case MSP430_OP_BIS: res_data = dst_data | src_data; break; case MSP430_OP_XOR: res_data = dst_data ^ src_data; dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; dev->regs[MSP430_REG_SR] |= (res_data & mask) ? MSP430_SR_C : MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; if (src_data & dst_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_V; break; default: return invalid_opcode(dev); } /* no need to repeat ops that will yeild same result every time */ if (opcode == MSP430_OP_CMP || opcode == MSP430_OP_BIT || opcode == MSP430_OP_BIS || opcode == MSP430_OP_BIC || opcode == MSP430_OP_AND) break; src_data = src_save; dst_data = res_data & mask; if (dreg == sreg) src_data = res_data & mask; } if (opcode != MSP430_OP_CMP && opcode != MSP430_OP_BIT && store_operand(dev, amode_dst, dreg, opwidth, dst_addr, res_data) < 0) return -1; return cycles; } static int step_single(struct sim_device *dev, uint16_t ins, uint16_t ext) { uint16_t opcode = ins & 0xff80; int amode = (ins >> 4) & 0x3; int reg = ins & 0x000f; uint32_t src_addr = 0; uint32_t src_data; uint32_t res_data = 0; int cycles = 1; int rept = 1; uint16_t zc_sr_mask = ~0; int store_results = 1; int opwidth = determine_op_width(ins,ext); if (opwidth == WIDTH_UNDEFINED) return invalid_opcode(dev); uint32_t mask = (1 << opwidth) - 1; uint32_t msb = 1 << (opwidth - 1); int ext_dst_bits = (ext >> 0) & 0xF; if (ext && amode == MSP430_AMODE_REGISTER) { /* certain ext features only supported on reg ops */ if (ext & (1<<7)) rept = (dev->regs[ext_dst_bits] & 0xF) + 1; else rept = ext_dst_bits + 1; if (ext & 0x0100) zc_sr_mask = ~MSP430_SR_C; } if (!dev->cpux) { /* original CPU timing */ switch (opcode) { case MSP430_OP_PUSH: if (amode == MSP430_AMODE_REGISTER) cycles = 3; else if (amode == MSP430_AMODE_INDIRECT || (amode == MSP430_AMODE_INDIRECT_INC && reg == MSP430_REG_PC)) cycles = 4; else cycles = 5; break; case MSP430_OP_CALL: if (amode == MSP430_AMODE_REGISTER || amode == MSP430_AMODE_INDIRECT) cycles = 4; else cycles = 5; break; case MSP430_OP_RETI: cycles = 5; break; default: if (amode == MSP430_AMODE_INDEXED) cycles = 4; else if (amode == MSP430_AMODE_REGISTER) cycles = 1; else cycles = 3; break; } } else { /* CPUX timing */ cycles = 1; /* read opcode */ if (ext) cycles += 1; /* read ext wd */ if (amode == MSP430_AMODE_INDEXED) cycles += 1; /* read offset */ switch (opcode) { /* special-case opcodes */ case MSP430_OP_CALL: if (amode == MSP430_AMODE_INDEXED && reg == MSP430_REG_SR) cycles += 1; /* extra cycle for call &xxx */ /* fall through */ case MSP430_OP_PUSH: if (amode == MSP430_AMODE_REGISTER) cycles += 1; /* sp decr pipeline hit */ else { cycles += 1; /* read data */ if (opwidth > 16 && !(amode == MSP430_AMODE_INDIRECT_INC && reg == MSP430_REG_PC)) cycles += 1; /* read high wd, except if immediate */ } cycles += 1; /* write to stack */ if (opwidth > 16 || opcode == MSP430_OP_CALL) cycles += 1; /* write high bits to dest or stack */ /* to match observed MSP430FR5739 behavior requires the following additional fudge */ if (opwidth == 20 && amode == MSP430_AMODE_INDEXED) cycles += 1; /* reason unknown */ if (opwidth > 16) cycles += rept - 1; break; default: if (amode != MSP430_AMODE_REGISTER) { cycles += 2; /* read/write data */ if (opwidth > 16) cycles += 2; /* extra read/write cycles */ } break; } cycles += rept - 1; } if (fetch_operand(dev, amode, reg, opwidth, &src_addr, &src_data, ext, ext_dst_bits) < 0) return -1; while (rept--) { switch (opcode) { case MSP430_OP_RRC: case MSP430_OP_RRA: res_data = (src_data >> 1) & ~msb; if (opcode == MSP430_OP_RRC) { if (dev->regs[MSP430_REG_SR] & zc_sr_mask & MSP430_SR_C) res_data |= msb; } else { res_data |= src_data & msb; } dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; if (!(res_data & mask)) dev->regs[MSP430_REG_SR] |= MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; if (src_data & 1) dev->regs[MSP430_REG_SR] |= MSP430_SR_C; break; case MSP430_OP_SWPB: res_data = ((src_data & 0xff) << 8) | ((src_data >> 8) & 0xff); if (opwidth == 20) res_data |= src_data & 0xF0000; break; case MSP430_OP_SXT: dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; /* Although not documented by TI, the FR5739 extends from bit 15 rather than from bit 7 if the ZC bit of the extended opcode word is set. This is implemented here. */ uint32_t signbit = ((ext & 0x0100) ? 0x08000 : 0x00080); res_data = src_data & (signbit - 1); if (src_data & signbit) { res_data |= ((1<<20) - signbit); dev->regs[MSP430_REG_SR] |= MSP430_SR_N; } dev->regs[MSP430_REG_SR] |= res_data ? MSP430_SR_C : MSP430_SR_Z; if (amode == MSP430_AMODE_REGISTER && dev->cpux) opwidth = 20; /* store all bits for reg dst */ break; case MSP430_OP_PUSH: res_data = src_data; // in case of repeat dev->regs[MSP430_REG_SP] -= opwidth <= 16 ? 2 : 4; if (opwidth == 8) src_data |= mem_getw(dev, dev->regs[MSP430_REG_SP]) & 0xFF00; if (((opwidth <= 16) ? mem_setw(dev, dev->regs[MSP430_REG_SP], src_data) : mem_seta(dev, dev->regs[MSP430_REG_SP], src_data)) < 0) return -1; store_results = 0; break; case MSP430_OP_CALL: dev->regs[MSP430_REG_SP] -= 2; if (mem_setw(dev, dev->regs[MSP430_REG_SP], dev->regs[MSP430_REG_PC]) < 0) return -1; dev->regs[MSP430_REG_PC] = src_data & 0xFFFF; store_results = 0; break; case MSP430_OP_RETI: /* handled in step_reti_calla() for CPUX */ { dev->regs[MSP430_REG_SR] = mem_getw(dev, dev->regs[MSP430_REG_SP]) & 0x0FFF; dev->regs[MSP430_REG_SP] += 2; dev->regs[MSP430_REG_PC] = mem_getw(dev, dev->regs[MSP430_REG_SP]); dev->regs[MSP430_REG_SP] += 2; store_results = 0; } break; default: return invalid_opcode(dev); } src_data = res_data; } if (store_results && store_operand(dev, amode, reg, opwidth, src_addr, res_data) < 0) return -1; return cycles; } static int step_jump(struct sim_device *dev, uint16_t ins) { uint16_t opcode = ins & 0xfc00; int32_t pc_offset = (((ins + 0x200) & 0x03ff) - 0x200) << 1; uint16_t sr = dev->regs[MSP430_REG_SR]; switch (opcode) { case MSP430_OP_JNZ: sr = !(sr & MSP430_SR_Z); break; case MSP430_OP_JZ: sr &= MSP430_SR_Z; break; case MSP430_OP_JNC: sr = !(sr & MSP430_SR_C); break; case MSP430_OP_JC: sr &= MSP430_SR_C; break; case MSP430_OP_JN: sr &= MSP430_SR_N; break; case MSP430_OP_JGE: sr = ((sr & MSP430_SR_N) ? 1 : 0) == ((sr & MSP430_SR_V) ? 1 : 0); break; case MSP430_OP_JL: sr = ((sr & MSP430_SR_N) ? 1 : 0) != ((sr & MSP430_SR_V) ? 1 : 0); break; case MSP430_OP_JMP: sr = 1; break; } if (sr) { add_to_pc(dev,pc_offset); } return 2; } static int step_RxxM(struct sim_device *dev, uint16_t ins) { /* RxxM instruction */ // XXX TBD uint16_t dreg = ((ins >> 0) & 0xF); uint16_t rept = ((ins >> 10) & 0x3) + 1; int cycles = rept; int opwidth = (ins & 0x10) ? 16 : 20; uint32_t mask = (1 << opwidth) - 1; uint32_t msb = 1 << (opwidth - 1); uint32_t src_data = dev->regs[dreg] & mask; uint32_t res_data = 0; uint32_t cy = dev->regs[MSP430_REG_SR] & MSP430_SR_C; uint32_t oflo = 0; while (rept--) { switch (ins & 0x03e0) { case MSP430_OP_RRCM: res_data = (src_data >> 1); if (cy) res_data |= msb; cy = src_data & 1; break; case MSP430_OP_RRAM: res_data = (src_data >> 1) | (src_data & msb); cy = src_data & 1; break; case MSP430_OP_RRUM: res_data = (src_data >> 1); cy = src_data & 1; break; case MSP430_OP_RLAM: res_data = src_data << 1; cy = src_data & msb; oflo = (src_data ^ res_data) & msb; break; default: return invalid_opcode(dev); } src_data = res_data; /* for next iteration, if any */ } dev->regs[dreg] = res_data & mask; dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; if (cy) dev->regs[MSP430_REG_SR] |= MSP430_SR_C; if (!res_data) dev->regs[MSP430_REG_SR] |= MSP430_SR_Z; if (res_data & msb) dev->regs[MSP430_REG_SR] |= MSP430_SR_N; if (oflo) dev->regs[MSP430_REG_SR] |= MSP430_SR_V; /* TI docs say V flag is "undefined" for RLAM, but appears to be same as RLA of final repetition */ return cycles; } struct addr_inst_info_s { uint16_t op; int src_amode; int dst_amode; int cycles; int cycles_if_dst_pc; int words; }; const struct addr_inst_info_s addr_inst_lut[] = { {MSP430_OP_MOVA, MSP430_AMODE_INDIRECT, MSP430_AMODE_REGISTER, 3,5, 1, }, {MSP430_OP_MOVA, MSP430_AMODE_INDIRECT_INC,MSP430_AMODE_REGISTER, 3,5, 1, }, {MSP430_OP_MOVA, MSP430_AMODE_ABSOLUTE, MSP430_AMODE_REGISTER, 4,6, 2, }, {MSP430_OP_MOVA, MSP430_AMODE_INDEXED, MSP430_AMODE_REGISTER, 4,6, 2, }, {0,0,0,0,0,0, }, {0,0,0,0,0,0, }, {MSP430_OP_MOVA, MSP430_AMODE_REGISTER, MSP430_AMODE_ABSOLUTE, 4,4, 2, }, {MSP430_OP_MOVA, MSP430_AMODE_REGISTER, MSP430_AMODE_INDEXED, 4,4, 2, }, {MSP430_OP_MOVA, MSP430_AMODE_IMMEDIATE, MSP430_AMODE_REGISTER, 2,3, 2, }, {MSP430_OP_CMPA, MSP430_AMODE_IMMEDIATE, MSP430_AMODE_REGISTER, 2,3, 2, }, // note 1 {MSP430_OP_ADDA, MSP430_AMODE_IMMEDIATE, MSP430_AMODE_REGISTER, 2,3, 2, }, // note 1 {MSP430_OP_SUBA, MSP430_AMODE_IMMEDIATE, MSP430_AMODE_REGISTER, 2,3, 2, }, // note 1 {MSP430_OP_MOVA, MSP430_AMODE_REGISTER, MSP430_AMODE_REGISTER, 1,3, 1, }, {MSP430_OP_CMPA, MSP430_AMODE_REGISTER, MSP430_AMODE_REGISTER, 1,3, 1, }, {MSP430_OP_ADDA, MSP430_AMODE_REGISTER, MSP430_AMODE_REGISTER, 1,3, 1, }, {MSP430_OP_SUBA, MSP430_AMODE_REGISTER, MSP430_AMODE_REGISTER, 1,3, 1, }, }; /* note 1: the CPUX docs say 3 cycles for non-PC dest, but the FR5739 executes this * in two cycles, and so that value is used here. */ static int step_0xxx_addr(struct sim_device *dev, uint16_t ins) { /* MSP430_OP_MOVA, MSP430_OP_CMPA, MSP430_OP_ADDA, MSP430_OP_SUBA */ const struct addr_inst_info_s *info = &addr_inst_lut[(ins & 0x00F0) >> 4]; if (!info->words) return invalid_opcode(dev); int src = (ins & 0x0F00) >> 8; int dst = (ins & 0x000F) >> 0; const uint32_t mask = 0xFFFFF; const uint32_t msb = 0x80000; uint32_t dst_addr = 0; uint16_t word2 = 0; if (info->words > 1) { word2 = mem_getw(dev, dev->regs[MSP430_REG_PC]); add_to_pc(dev,2); } uint32_t src_data = 0; switch (info->src_amode) { case MSP430_AMODE_REGISTER: src_data = dev->regs[src]; break; case MSP430_AMODE_IMMEDIATE: src_data = (src << 16) | word2; break; case MSP430_AMODE_INDIRECT: src_data = mem_geta(dev,dev->regs[src]); break; case MSP430_AMODE_INDIRECT_INC: src_data = mem_geta(dev,dev->regs[src]); dev->regs[src] += 4; dev->regs[src] &= mask; break; case MSP430_AMODE_INDEXED: src_data = mem_geta(dev,(dev->regs[src] + (int16_t)word2) & mask); break; case MSP430_AMODE_ABSOLUTE: src_data = mem_geta(dev,(src << 16) | word2); break; } uint32_t dst_data = 0; switch (info->dst_amode) { case MSP430_AMODE_ABSOLUTE: dst_addr = (dst << 16) | word2; goto load_dst_data; case MSP430_AMODE_INDEXED: dst_addr = (dev->regs[dst] + (int16_t)word2) & mask; goto load_dst_data; load_dst_data: if (info->op != MSP430_OP_MOVA) dst_data = mem_geta(dev,dst_addr); break; case MSP430_AMODE_REGISTER: dst_data = dev->regs[dst]; break; } uint16_t status = dev->regs[MSP430_REG_SR]; uint32_t res_data = 0; switch (info->op) { case MSP430_OP_MOVA: res_data = src_data; break; case MSP430_OP_SUBA: case MSP430_OP_CMPA: src_data = ((~src_data)+1) & mask; case MSP430_OP_ADDA: res_data = src_data + dst_data; status &= ~ARITH_BITS; if (!(res_data & mask)) status |= MSP430_SR_Z; if (res_data & msb) status |= MSP430_SR_N; if (res_data & (msb << 1)) status |= MSP430_SR_C; if ((src_data ^ dst_data ^ res_data ^ (res_data>>1)) & msb) if (!((src_data ^ dst_data) & (src_data ^ res_data) & msb)) status |= MSP430_SR_V; res_data &= mask; break; } dev->regs[MSP430_REG_SR] = status; /* store result if appropriate */ if (info->op != MSP430_OP_CMPA) { switch (info->dst_amode) { case MSP430_AMODE_ABSOLUTE: case MSP430_AMODE_INDEXED: if (mem_seta(dev,dst_addr,res_data) < 0) return -1; break; case MSP430_AMODE_REGISTER: dev->regs[dst] = res_data; break; } } if (info->dst_amode == MSP430_AMODE_REGISTER && dst == MSP430_REG_PC) return info->cycles_if_dst_pc; return info->cycles; } static int step_pushm_popm(struct sim_device *dev, uint16_t ins) { /* PUSHM/POPM */ uint16_t opcode = ins & 0xfe00; int is_aword = ins & 0x0100; int reg = ins & 0x000f; int rept = ((ins >> 4) & 0xf) + 1; int cycles = 2 + (is_aword ? 2 : 1) * rept; switch (opcode) { case MSP430_OP_PUSHM: while (rept--) { dev->regs[MSP430_REG_SP] -= 2; if (mem_setw(dev, dev->regs[MSP430_REG_SP], dev->regs[reg--]) < 0) return -1; } break; case MSP430_OP_POPM: while (rept--) { dev->regs[reg++] = mem_getw(dev, dev->regs[MSP430_REG_SP]); dev->regs[MSP430_REG_SP] += 2; } break; default: return invalid_opcode(dev); }; return cycles; } static int step_reti_calla(struct sim_device *dev, uint16_t ins) { /* RETI, CALLA */ int amode; int reg = 0; int ext_imm = 0; uint32_t data; int cycles = 0; switch ((ins & 0x00C0)>>6) { case 0: /* RETI */ /* note: RETI handled in step_single() for basic CPU */ if (ins != MSP430_OP_RETI) return invalid_opcode(dev); uint16_t w1 = mem_getw(dev, dev->regs[MSP430_REG_SP]); dev->regs[MSP430_REG_SR] = w1 & 0x0FFF; dev->regs[MSP430_REG_SP] += 2; dev->regs[MSP430_REG_PC] = mem_getw(dev, dev->regs[MSP430_REG_SP]); dev->regs[MSP430_REG_PC] |= ((w1 & 0xF000) << 4); dev->regs[MSP430_REG_SP] += 2; cycles = 5; case 1: /* CALLA Rd, x(Rd), @Rd, @Rd+ */ amode = (ins & 0x30) >> 4; reg = (ins & 0xF); cycles = (amode & 2) ? 6 : 5; if (amode == 1 && reg == MSP430_REG_SP) cycles++; goto calla_common; case 2: /* CALLA &abs20, rel20, #imm20 */ if ((ins & 0x30) == 0x20) return invalid_opcode(dev); amode = ((ins & 0x30) >> 4) | 1; ext_imm = (ins & 0xF); cycles = (amode & 2) ? 5 : 7; goto calla_common; calla_common: if (fetch_operand(dev,amode,reg,20,NULL,&data,1,ext_imm) < 0) return -1; dev->regs[MSP430_REG_SP] -= 4; if (mem_setw(dev, dev->regs[MSP430_REG_SP] + 2, (dev->regs[MSP430_REG_PC] >> 16) & 0x0000F) < 0) return -1; if (mem_setw(dev, dev->regs[MSP430_REG_SP], dev->regs[MSP430_REG_PC]) < 0) return -1; dev->regs[MSP430_REG_PC] = data; break; case 3: /* RESERVED */ return invalid_opcode(dev); } return cycles; } /* Fetch and execute one instruction. Return the number of CPU cycles * it would have taken, or -1 if an error occurs. */ static int step_cpu(struct sim_device *dev) { uint16_t ins; int ret; const char *where = NULL; if (dev->regs[MSP430_REG_PC] < dev->addr_io_end) where = "in device space"; else if (dev->regs[MSP430_REG_PC] >= MEM_SIZE) where = "beyond end of memory"; if (where) { /* report bogus PC, provide previous location */ printc_err("%s: executing %s: PC = 0x%05x; " "previous PC value 0x%05x\n", SIMx,where,dev->regs[MSP430_REG_PC],dev->current_insn); return -1; } /* Fetch the instruction */ dev->current_insn = dev->regs[MSP430_REG_PC]; ins = mem_getw(dev, dev->current_insn); add_to_pc(dev,2); /* Handle different instruction types */ if ((ins & 0xf800) == 0x1800 && dev->cpux) { /* found extension word */ uint16_t ext = ins; ins = mem_getw(dev, dev->current_insn + 2); add_to_pc(dev,2); if ((ins & 0xf000) >= 0x4000) ret = step_double(dev, ins, ext); else if ((ins & 0xf000) == 0x1000 && (ins & 0xfc00) < 0x1280) ret = step_single(dev, ins, ext); else ret = invalid_opcode(dev); } else { if ((ins & 0xf0e0) == 0x0040 && dev->cpux) ret = step_RxxM(dev, ins); else if ((ins & 0xf000) == 0x0000 && dev->cpux) ret = step_0xxx_addr(dev, ins); else if ((ins & 0xfc00) == 0x1400 && dev->cpux) ret = step_pushm_popm(dev, ins); else if ((ins & 0xff00) == 0x1300 && dev->cpux) ret = step_reti_calla(dev, ins); else if ((ins & 0xf000) == 0x1000) ret = step_single(dev, ins, 0); else if ((ins & 0xe000) == 0x2000) ret = step_jump(dev, ins); else if ((ins & 0xf000) >= 0x4000) ret = step_double(dev, ins, 0); else ret = invalid_opcode(dev); } /* If things went wrong, restart at the current instruction */ if (ret < 0) dev->regs[MSP430_REG_PC] = dev->current_insn; return ret; } static void do_reset(struct sim_device *dev) { simio_step(dev->regs[MSP430_REG_SR], 4); memset(dev->regs, 0, sizeof(dev->regs)); dev->regs[MSP430_REG_PC] = mem_getw(dev, 0xfffe); dev->regs[MSP430_REG_SR] = 0; simio_reset(); } static int step_system(struct sim_device *dev) { int count = 1; int irq; uint16_t status = dev->regs[MSP430_REG_SR]; irq = simio_check_interrupt(); if (irq == 15) { do_reset(dev); return 0; } else if (((status & MSP430_SR_GIE) && irq >= 0) || irq >= 14) { if (irq >= 16) { printc_err("%s: invalid interrupt number: %d\n", SIMx, irq); return -1; } dev->regs[MSP430_REG_SP] -= 2; if (mem_setw(dev, dev->regs[MSP430_REG_SP], dev->regs[MSP430_REG_PC]) < 0) return -1; dev->regs[MSP430_REG_SP] -= 2; if (mem_setw(dev, dev->regs[MSP430_REG_SP], dev->regs[MSP430_REG_SR]) < 0) return -1; dev->regs[MSP430_REG_SR] &= ~(MSP430_SR_GIE | MSP430_SR_CPUOFF); dev->regs[MSP430_REG_PC] = mem_getw(dev, 0xffe0 + irq * 2); simio_ack_interrupt(irq); count = 6; } else if (!(status & MSP430_SR_CPUOFF)) { count = step_cpu(dev); if (count < 0) return -1; } simio_step(status, count); return 0; } /************************************************************************ * Device interface */ static void sim_destroy(device_t dev_base) { free(dev_base); } static int sim_readmem(device_t dev_base, address_t addr, uint8_t *mem, address_t len) { struct sim_device *dev = (struct sim_device *)dev_base; if (addr > MEM_SIZE || (addr + len) < addr || (addr + len) > MEM_SIZE) { printc_err("%s: memory read out of range\n",SIMx); return -1; } if (addr + len > MEM_SIZE) len = MEM_SIZE - addr; /* Read byte IO addresses */ while (len && (addr < ADDR_BYTE_IO_END)) { simio_read_b(addr, mem); mem++; len--; addr++; } /* Read word IO addresses */ while (len >= 2 && addr < dev->addr_io_end) { uint16_t data = 0; simio_read(addr, &data); mem[0] = data & 0xff; mem[1] = data >> 8; mem += 2; len -= 2; addr += 2; } memcpy(mem, dev->memory + addr, len); return 0; } static int sim_writemem(device_t dev_base, address_t addr, const uint8_t *mem, address_t len) { struct sim_device *dev = (struct sim_device *)dev_base; if (addr > MEM_SIZE || (addr + len) < addr || (addr + len) > MEM_SIZE) { printc_err("%s: memory write out of range\n",SIMx); return -1; } /* Write byte IO addresses */ while (len && (addr < ADDR_BYTE_IO_END)) { simio_write_b(addr, *mem); mem++; len--; addr++; } /* Write word IO addresses */ if (len == 1 && addr < dev->addr_io_end) { printc_err("%s: memory write on word IO, " "at least 2 bytes data are necessary.\n",SIMx); } else if (len % 2 != 0 && addr < dev->addr_io_end) { printc_err("%s: memory write on word IO, " "the last byte is ignored.\n",SIMx); } while (len >= 2 && addr < dev->addr_io_end) { simio_write(addr, ((uint16_t)mem[1] << 8) | mem[0]); mem += 2; len -= 2; addr += 2; } memcpy(dev->memory + addr, mem, len); return 0; } static int sim_getregs(device_t dev_base, address_t *regs) { struct sim_device *dev = (struct sim_device *)dev_base; int i; for (i = 0; i < DEVICE_NUM_REGS; i++) regs[i] = dev->regs[i]; return 0; } static int sim_setregs(device_t dev_base, const address_t *regs) { struct sim_device *dev = (struct sim_device *)dev_base; int i; for (i = 0; i < DEVICE_NUM_REGS; i++) dev->regs[i] = regs[i]; return 0; } static int sim_ctl(device_t dev_base, device_ctl_t op) { struct sim_device *dev = (struct sim_device *)dev_base; switch (op) { case DEVICE_CTL_RESET: do_reset(dev); return 0; case DEVICE_CTL_HALT: dev->running = 0; return 0; case DEVICE_CTL_STEP: return step_system(dev); case DEVICE_CTL_RUN: dev->running = 1; return 0; default: printc_err("%s: unsupported operation\n",SIMx); return -1; } return 0; } static int sim_erase(device_t dev_base, device_erase_type_t type, address_t addr) { struct sim_device *dev = (struct sim_device *)dev_base; switch (type) { case DEVICE_ERASE_MAIN: memset(dev->memory + 0x2000, 0xff, MEM_SIZE - 0x2000); break; case DEVICE_ERASE_ALL: memset(dev->memory, 0xff, MEM_SIZE); break; case DEVICE_ERASE_SEGMENT: addr &= ~0x3f; addr &= (MEM_SIZE - 1); memset(dev->memory + addr, 0xff, 64); break; } return 0; } static device_status_t sim_poll(device_t dev_base) { struct sim_device *dev = (struct sim_device *)dev_base; int count = 1000000; if (!dev->running) return DEVICE_STATUS_HALTED; dev->watchpoint_hit = 0; while (count > 0) { int i; for (i = 0; i < dev->base.max_breakpoints; i++) { struct device_breakpoint *bp = &dev->base.breakpoints[i]; if ((bp->flags & DEVICE_BP_ENABLED) && (bp->type == DEVICE_BPTYPE_BREAK) && dev->regs[MSP430_REG_PC] == bp->addr) { dev->running = 0; return DEVICE_STATUS_HALTED; } } if (step_system(dev) < 0) { dev->running = 0; return DEVICE_STATUS_ERROR; } if (dev->watchpoint_hit) { dev->running = 0; return DEVICE_STATUS_HALTED; } if (ctrlc_check()) return DEVICE_STATUS_INTR; count--; } return DEVICE_STATUS_RUNNING; } static device_t sim_open(const struct device_args *args) { struct sim_device *dev = malloc(sizeof(*dev)); (void)args; if (!dev) { pr_error("can't allocate memory for simulation"); return NULL; } memset(dev, 0, sizeof(*dev)); dev->base.type = &device_sim; dev->base.max_breakpoints = DEVICE_MAX_BREAKPOINTS; memset(dev->memory, 0xff, sizeof(dev->memory)); memset(dev->regs, 0xff, sizeof(dev->regs)); dev->running = 0; dev->current_insn = 0; dev->addr_io_end = 0x200; printc_dbg("Simulation started, 0x%x bytes of RAM\n", MEM_SIZE); return (device_t)dev; } static device_t simx_open(const struct device_args *args) { struct sim_device *dev = (struct sim_device *)sim_open(args); dev->base.type = &device_simx; dev->cpux = 1; dev->addr_io_end = 0x1000; return (device_t)dev; } const struct device_class device_sim = { .name = "sim", .help = "Simulation mode (standard CPU)", .open = sim_open, .destroy = sim_destroy, .readmem = sim_readmem, .writemem = sim_writemem, .erase = sim_erase, .getregs = sim_getregs, .setregs = sim_setregs, .ctl = sim_ctl, .poll = sim_poll, .getconfigfuses = NULL }; const struct device_class device_simx = { .name = "simx", .help = "CPUX Simulation mode", .open = simx_open, .destroy = sim_destroy, .readmem = sim_readmem, .writemem = sim_writemem, .erase = sim_erase, .getregs = sim_getregs, .setregs = sim_setregs, .ctl = sim_ctl, .poll = sim_poll, .getconfigfuses = NULL };