From 6740d7d98b433018c5056fccfc04229b5e678675 Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Sat, 5 Sep 2020 14:56:37 +1200 Subject: [PATCH] simx: MSP430X simulator by Bruce Burns --- AUTHORS | 3 + drivers/sim.c | 1295 ++++++++++++++++++++++++++++++++---------- drivers/sim.h | 1 + simio/simio.c | 16 + simio/simio_hwmult.c | 75 ++- ui/main.c | 5 +- util/dis.c | 31 +- util/dis.h | 1 + util/output_util.c | 3 + 9 files changed, 1116 insertions(+), 314 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1344978..58062f2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -87,3 +87,6 @@ Paolo Zebelloni : Jan Willeke : * GPIO JTAG driver. + +Bruce Burns + * CPUX support in simulation driver (simx) diff --git a/drivers/sim.c b/drivers/sim.c index 2985a38..636b166 100644 --- a/drivers/sim.c +++ b/drivers/sim.c @@ -1,5 +1,6 @@ /* MSPDebug - debugging tool for the eZ430 - * Copyright (C) 2009, 2010 Daniel Beer + * 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 @@ -28,31 +29,99 @@ #include "simio_cpu.h" #include "ctrlc.h" -#define MEM_SIZE 65536 -#define MEM_IO_END 0x200 +#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]; - uint16_t regs[DEVICE_NUM_REGS]; + uint32_t regs[DEVICE_NUM_REGS]; int running; - uint16_t current_insn; + uint32_t current_insn; int watchpoint_hit; + + int cpux; + + uint32_t addr_io_end; }; -#define MEM_GETB(dev, offset) ((dev)->memory[offset]) -#define MEM_SETB(dev, offset, value) ((dev)->memory[offset] = (value)) -#define MEM_GETW(dev, offset) \ - ((dev)->memory[offset] | \ - ((dev)->memory[(offset + 1) & 0xffff] << 8)) -#define MEM_SETW(dev, offset, value) \ - do { \ - (dev)->memory[offset & ~1] = (value) & 0xff; \ - (dev)->memory[offset | 1] = (value) >> 8; \ - } while (0); +#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) @@ -77,11 +146,12 @@ static void watchpoint_check(struct sim_device *dev, uint16_t addr, } static int fetch_operand(struct sim_device *dev, - int amode, int reg, int is_byte, - uint16_t *addr_ret, uint32_t *data_ret) + int amode, int reg, int opwidth, + uint32_t *addr_ret, uint32_t *data_ret, int ext, int ext_imm) { - uint16_t addr = 0; - uint32_t mask = is_byte ? 0xff : 0xffff; + uint32_t addr = 0; + uint32_t mask = (1 << opwidth) - 1; + int is_20bit_imm = 0; switch (amode) { case MSP430_AMODE_REGISTER: @@ -101,12 +171,23 @@ static int fetch_operand(struct sim_device *dev, return 0; } - addr = MEM_GETW(dev, dev->regs[MSP430_REG_PC]); + 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]; - dev->regs[MSP430_REG_PC] += 2; + if (!ext && (dev->regs[reg] & 0xF0000) == 0) + addr &= 0x0FFFF; + else + addr &= 0xFFFFF; + + add_to_pc(dev,2); break; case MSP430_AMODE_INDIRECT: @@ -125,6 +206,9 @@ static int fetch_operand(struct sim_device *dev, 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; @@ -136,62 +220,92 @@ static int fetch_operand(struct sim_device *dev, return 0; } addr = dev->regs[reg]; - dev->regs[reg] += (is_byte && reg != MSP430_REG_PC && - reg != MSP430_REG_SP) ? 1 : 2; + 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); - *data_ret = MEM_GETW(dev, addr) & mask; + if (addr < dev->addr_io_end) { - if (addr < MEM_IO_END) { - int ret; - - if (is_byte) { - uint8_t x = *data_ret; - - ret = simio_read_b(addr, &x); - *data_ret = x; + if (opwidth == 8) { + uint8_t byte; + ret = simio_read_b(addr, &byte); + *data_ret = byte; } else { - uint16_t x = *data_ret; + uint16_t lsw; - ret = simio_read(addr, &x); - *data_ret = x; + 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; + } } - return ret; + } 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 0; + return ret; } static int store_operand(struct sim_device *dev, - int amode, int reg, int is_byte, - uint16_t addr, uint16_t data) + int amode, int reg, int opwidth, + uint16_t addr, uint32_t data) { + if (amode == MSP430_AMODE_REGISTER) { - dev->regs[reg] = is_byte ? data & 0xFF : data; + uint32_t mask = ((1 << opwidth) - 1); + dev->regs[reg] = mask & data; return 0; } watchpoint_check(dev, addr, 1); - if (is_byte) - MEM_SETB(dev, addr, data); - else - MEM_SETW(dev, addr, data); + int ret = 0; - if (addr < MEM_IO_END) { - if (is_byte) + 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); - return simio_write(addr, data); + int ret = simio_write(addr, data); + + if (ret != 0 || opwidth != 20) return ret; + + return simio_write(addr + 2, data >> 16); } return 0; @@ -199,261 +313,470 @@ static int store_operand(struct sim_device *dev, #define ARITH_BITS (MSP430_SR_V | MSP430_SR_N | MSP430_SR_Z | MSP430_SR_C) -static int step_double(struct sim_device *dev, uint16_t ins) +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 is_byte = ins & 0x0040; int amode_src = (ins >> 4) & 0x3; int dreg = ins & 0x000f; uint32_t src_data; - uint16_t dst_addr = 0; + uint32_t dst_addr = 0; uint32_t dst_data; - uint32_t res_data; - uint32_t msb = is_byte ? 0x80 : 0x8000; - uint32_t mask = is_byte ? 0xff : 0xffff; + uint32_t res_data = 0; uint32_t shiftMask = 0x000f; uint32_t i = 0; int cycles; + int rept = 1; + uint16_t zc_sr_mask = ~0; - 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; + 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); - if (amode_dst == MSP430_AMODE_INDEXED) - cycles += 3; + 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 (fetch_operand(dev, amode_src, sreg, is_byte, NULL, &src_data) < 0) - return -1; - if (fetch_operand(dev, amode_dst, dreg, is_byte, &dst_addr, - opcode == MSP430_OP_MOV ? NULL : &dst_data) < 0) - return -1; + if (!dev->cpux) { /* original CPU timing */ - switch (opcode) { - case MSP430_OP_MOV: - res_data = src_data; - break; + 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; - case MSP430_OP_SUB: - case MSP430_OP_SUBC: - case MSP430_OP_CMP: - src_data = (~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] & - 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) & msb) && - (src_data ^ res_data) & msb) - dev->regs[MSP430_REG_SR] |= MSP430_SR_V; - break; - - case MSP430_OP_DADD: - res_data = 0; - if (dev->regs[MSP430_REG_SR] & MSP430_SR_C) - res_data++; - shiftMask = 0x000f; - for(i = 0; i < 4; ++i) - { - res_data += (src_data & shiftMask) + (dst_data & shiftMask); - if( (res_data & (0x1f << (i*4))) > (9 << (i*4))) - res_data += 6 << (i*4); - shiftMask = shiftMask << 4; + if (amode_dst == MSP430_AMODE_INDEXED) + cycles += 3; } - 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; - break; + } else { /* CPUX timing */ + cycles = 1; /* read opcode */ + if (ext) cycles += 1; /* read ext wd */ - case MSP430_OP_BIT: - case MSP430_OP_AND: - res_data = src_data & dst_data; + if (amode_src == MSP430_AMODE_INDEXED) + cycles += 1; /* read offset */ - 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; + 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; + } - 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: - printc_err("sim: invalid double-operand opcode: " - "0x%04x (PC = 0x%04x)\n", - opcode, dev->current_insn); + 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, is_byte, + 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) +static int step_single(struct sim_device *dev, uint16_t ins, uint16_t ext) { uint16_t opcode = ins & 0xff80; - int is_byte = ins & 0x0040; int amode = (ins >> 4) & 0x3; int reg = ins & 0x000f; - uint16_t msb = is_byte ? 0x80 : 0x8000; - uint32_t mask = is_byte ? 0xff : 0xffff; - uint16_t src_addr = 0; + 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; - if (fetch_operand(dev, amode, reg, is_byte, &src_addr, &src_data) < 0) - return -1; + int opwidth = determine_op_width(ins,ext); + if (opwidth == WIDTH_UNDEFINED) + return invalid_opcode(dev); - if (amode == MSP430_AMODE_INDEXED) - cycles = 4; - else if (amode == MSP430_AMODE_REGISTER) - cycles = 1; - else - cycles = 3; + uint32_t mask = (1 << opwidth) - 1; + uint32_t msb = 1 << (opwidth - 1); - 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] & MSP430_SR_C) - res_data |= msb; - } else { - res_data |= src_data & msb; - } + int ext_dst_bits = (ext >> 0) & 0xF; - 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); - break; - - case MSP430_OP_SXT: - res_data = src_data & 0xff; - dev->regs[MSP430_REG_SR] &= ~ARITH_BITS; - - if (src_data & 0x80) { - res_data |= 0xff00; - dev->regs[MSP430_REG_SR] |= MSP430_SR_N; - } - - dev->regs[MSP430_REG_SR] |= - (res_data & mask) ? MSP430_SR_C : MSP430_SR_Z; - break; - - case MSP430_OP_PUSH: - dev->regs[MSP430_REG_SP] -= 2; - MEM_SETW(dev, dev->regs[MSP430_REG_SP], src_data); - - if (amode == MSP430_AMODE_REGISTER) - cycles = 3; - else if (amode == MSP430_AMODE_INDIRECT || - (amode == MSP430_AMODE_INDIRECT_INC && - reg == MSP430_REG_PC)) - cycles = 4; + 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 - cycles = 5; - break; - - case MSP430_OP_CALL: - dev->regs[MSP430_REG_SP] -= 2; - MEM_SETW(dev, dev->regs[MSP430_REG_SP], - dev->regs[MSP430_REG_PC]); - dev->regs[MSP430_REG_PC] = src_data; - - if (amode == MSP430_AMODE_REGISTER || - amode == MSP430_AMODE_INDIRECT) - cycles = 4; - else - cycles = 5; - break; - - case MSP430_OP_RETI: - dev->regs[MSP430_REG_SR] = - MEM_GETW(dev, dev->regs[MSP430_REG_SP]); - 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; - cycles = 5; - break; - - default: - printc_err("sim: unknown single-operand opcode: 0x%04x " - "(PC = 0x%04x)\n", opcode, dev->current_insn); - return -1; + rept = ext_dst_bits + 1; + if (ext & 0x0100) + zc_sr_mask = ~MSP430_SR_C; } - if (opcode != MSP430_OP_PUSH && opcode != MSP430_OP_CALL && - opcode != MSP430_OP_RETI && - store_operand(dev, amode, reg, is_byte, src_addr, res_data) < 0) + 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; @@ -462,12 +785,9 @@ static int step_single(struct sim_device *dev, uint16_t ins) static int step_jump(struct sim_device *dev, uint16_t ins) { uint16_t opcode = ins & 0xfc00; - uint16_t pc_offset = (ins & 0x03ff) << 1; + int32_t pc_offset = (((ins + 0x200) & 0x03ff) - 0x200) << 1; uint16_t sr = dev->regs[MSP430_REG_SR]; - if (pc_offset & 0x0400) - pc_offset |= 0xff800; - switch (opcode) { case MSP430_OP_JNZ: sr = !(sr & MSP430_SR_Z); @@ -504,12 +824,334 @@ static int step_jump(struct sim_device *dev, uint16_t ins) break; } - if (sr) - dev->regs[MSP430_REG_PC] += pc_offset; + 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. */ @@ -518,18 +1160,58 @@ 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); - dev->regs[MSP430_REG_PC] += 2; + + ins = mem_getw(dev, dev->current_insn); + add_to_pc(dev,2); /* Handle different instruction types */ - if ((ins & 0xf000) >= 0x4000) - ret = step_double(dev, ins); - else if ((ins & 0xf000) >= 0x2000) - ret = step_jump(dev, ins); - else - ret = step_single(dev, ins); + 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) @@ -542,7 +1224,7 @@ 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_PC] = mem_getw(dev, 0xfffe); dev->regs[MSP430_REG_SR] = 0; simio_reset(); } @@ -559,21 +1241,23 @@ static int step_system(struct sim_device *dev) return 0; } else if (((status & MSP430_SR_GIE) && irq >= 0) || irq >= 14) { if (irq >= 16) { - printc_err("sim: invalid interrupt number: %d\n", irq); + printc_err("%s: invalid interrupt number: %d\n", SIMx, irq); return -1; } dev->regs[MSP430_REG_SP] -= 2; - MEM_SETW(dev, dev->regs[MSP430_REG_SP], - dev->regs[MSP430_REG_PC]); + if (mem_setw(dev, dev->regs[MSP430_REG_SP], + dev->regs[MSP430_REG_PC]) < 0) + return -1; dev->regs[MSP430_REG_SP] -= 2; - MEM_SETW(dev, dev->regs[MSP430_REG_SP], - dev->regs[MSP430_REG_SR]); + 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); + dev->regs[MSP430_REG_PC] = mem_getw(dev, 0xffe0 + irq * 2); simio_ack_interrupt(irq); count = 6; @@ -603,7 +1287,7 @@ static int sim_readmem(device_t dev_base, address_t addr, if (addr > MEM_SIZE || (addr + len) < addr || (addr + len) > MEM_SIZE) { - printc_err("sim: memory read out of range\n"); + printc_err("%s: memory read out of range\n",SIMx); return -1; } @@ -611,7 +1295,7 @@ static int sim_readmem(device_t dev_base, address_t addr, len = MEM_SIZE - addr; /* Read byte IO addresses */ - while (len && (addr < 0x100)) { + while (len && (addr < ADDR_BYTE_IO_END)) { simio_read_b(addr, mem); mem++; len--; @@ -619,7 +1303,7 @@ static int sim_readmem(device_t dev_base, address_t addr, } /* Read word IO addresses */ - while (len >= 2 && addr < 0x200) { + while (len >= 2 && addr < dev->addr_io_end) { uint16_t data = 0; simio_read(addr, &data); @@ -641,12 +1325,12 @@ static int sim_writemem(device_t dev_base, address_t addr, if (addr > MEM_SIZE || (addr + len) < addr || (addr + len) > MEM_SIZE) { - printc_err("sim: memory write out of range\n"); + printc_err("%s: memory write out of range\n",SIMx); return -1; } /* Write byte IO addresses */ - while (len && (addr < 0x100)) { + while (len && (addr < ADDR_BYTE_IO_END)) { simio_write_b(addr, *mem); mem++; len--; @@ -654,14 +1338,14 @@ static int sim_writemem(device_t dev_base, address_t addr, } /* Write word IO addresses */ - if (len == 1 && addr < 0x200) { - printc_err("sim: memory write on word IO, " - "at least 2 bytes data are necessary.\n"); - } else if (len % 2 != 0 && addr < 0x200) { - printc_err("sim: memory write on word IO, " - "the last byte is ignored.\n"); + 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 < 0x200) { + while (len >= 2 && addr < dev->addr_io_end) { simio_write(addr, ((uint16_t)mem[1] << 8) | mem[0]); mem += 2; len -= 2; @@ -713,7 +1397,7 @@ static int sim_ctl(device_t dev_base, device_ctl_t op) return 0; default: - printc_err("sim: unsupported operation\n"); + printc_err("%s: unsupported operation\n",SIMx); return -1; } @@ -809,13 +1493,24 @@ static device_t sim_open(const struct device_args *args) 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.", + .help = "Simulation mode (standard CPU)", .open = sim_open, .destroy = sim_destroy, .readmem = sim_readmem, @@ -827,3 +1522,19 @@ const struct device_class device_sim = { .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 +}; + diff --git a/drivers/sim.h b/drivers/sim.h index 8b5fdfe..c92da4c 100644 --- a/drivers/sim.h +++ b/drivers/sim.h @@ -23,5 +23,6 @@ /* Dummy/simulation implementation. */ extern const struct device_class device_sim; +extern const struct device_class device_simx; #endif diff --git a/simio/simio.c b/simio/simio.c index 6106206..25ca503 100644 --- a/simio/simio.c +++ b/simio/simio.c @@ -355,6 +355,12 @@ int simio_read(address_t addr, uint16_t *data) *data = ((uint16_t)sfr_data[addr]) | (((uint16_t)sfr_data[addr + 1]) << 8); return 0; + + } else if (addr >= 0x100 && addr < 0x110) { + /* most MSPs map SFR at 0x100 */ + *data = ((uint16_t)sfr_data[addr - 0x100]) | + (((uint16_t)sfr_data[addr - 0x100 + 1]) << 8); + return 0; } *data = 0; @@ -366,6 +372,11 @@ int simio_write_b(address_t addr, uint8_t data) if (addr < 16) { sfr_data[addr] = data; return 0; + + } else if (addr >= 0x100 && addr < 0x110) { + /* most MSPs map SFR at 0x100 */ + sfr_data[addr - 0x100] = data; + return 0; } return simio_write_b_device(addr, data); @@ -376,6 +387,11 @@ int simio_read_b(address_t addr, uint8_t *data) if (addr < 16) { *data = sfr_data[addr]; return 0; + + } else if (addr >= 0x100 && addr < 0x110) { + /* most MSPs map SFR at 0x100 */ + *data = sfr_data[addr - 0x100]; + return 0; } *data = 0; diff --git a/simio/simio_hwmult.c b/simio/simio_hwmult.c index 9f560c1..6f32efa 100644 --- a/simio/simio_hwmult.c +++ b/simio/simio_hwmult.c @@ -24,21 +24,23 @@ #include "output.h" #include "expr.h" -/* Multiplier register addresses - taken from mspgcc */ -#define MPY 0x0130 /* Multiply Unsigned/Operand 1 */ -#define MPYS 0x0132 /* Multiply Signed/Operand 1 */ -#define MAC 0x0134 /* Multiply Unsigned and Accumulate/Operand 1 */ -#define MACS 0x0136 /* Multiply Signed and Accumulate/Operand 1 */ -#define OP2 0x0138 /* Operand 2 */ -#define RESLO 0x013A /* Result Low Word */ -#define RESHI 0x013C /* Result High Word */ -#define SUMEXT 0x013E /* Sum Extend */ +/* Multiplier register offsets from base addr */ +#define MPY 0x0 /* Multiply Unsigned/Operand 1 */ +#define MPYS 0x2 /* Multiply Signed/Operand 1 */ +#define MAC 0x4 /* Multiply Unsigned and Accumulate/Operand 1 */ +#define MACS 0x6 /* Multiply Signed and Accumulate/Operand 1 */ +#define OP2 0x8 /* Operand 2 */ +#define RESLO 0xA /* Result Low Word */ +#define RESHI 0xC /* Result High Word */ +#define SUMEXT 0xE /* Sum Extend */ struct hwmult { struct simio_device base; int mode; + address_t base_addr; + uint16_t op1; uint16_t op2; uint32_t result; @@ -59,6 +61,8 @@ struct simio_device *hwmult_create(char **arg_text) memset(h, 0, sizeof(*h)); h->base.type = &simio_hwmult; + h->base_addr = 0x130; + return (struct simio_device *)h; } @@ -67,6 +71,44 @@ static void hwmult_destroy(struct simio_device *dev) free(dev); } +static int config_addr(address_t *addr, char **arg_text) +{ + char *text = get_arg(arg_text); + + if (!text) { + printc_err("hwmult: config: expected address\n"); + return -1; + } + + if (expr_eval(text, addr) < 0) { + printc_err("hwmult: can't parse address: %s\n", text); + return -1; + } + + return 0; +} + +static int hwmult_config(struct simio_device *dev, + const char *param, char **arg_text) +{ + struct hwmult *h = (struct hwmult *)dev; + + if (!strcasecmp(param, "base")) + return config_addr(&h->base_addr, arg_text); + + printc_err("hwmult: config: unknown parameter: %s\n", param); + return -1; +} + +static int hwmult_info(struct simio_device *dev) +{ + struct hwmult *h = (struct hwmult *)dev; + + printc("Base address: 0x%04x\n\n", h->base_addr); + + return 0; +} + static void do_multiply(struct hwmult *h) { uint32_t im; @@ -100,6 +142,10 @@ static int hwmult_write(struct simio_device *dev, address_t addr, uint16_t data) { struct hwmult *h = (struct hwmult *)dev; + if (addr < h->base_addr) return 1; + + addr -= h->base_addr; + switch (addr) { case RESHI: h->result = (h->result & 0xffff) | ((uint32_t)data << 16); @@ -130,6 +176,10 @@ static int hwmult_read(struct simio_device *dev, address_t addr, uint16_t *data) { struct hwmult *h = (struct hwmult *)dev; + if (addr < h->base_addr) return 1; + + addr -= h->base_addr; + switch (addr) { case MPY: case MPYS: @@ -161,9 +211,14 @@ static int hwmult_read(struct simio_device *dev, address_t addr, uint16_t *data) const struct simio_class simio_hwmult = { .name = "hwmult", .help = -"This module simulates the hardware multiplier.\n", +"This module simulates the hardware multiplier.\n" +"Config arguments are:\n" +" base
\n" +" Set the peripheral base address.\n", .create = hwmult_create, .destroy = hwmult_destroy, + .config = hwmult_config, + .info = hwmult_info, .write = hwmult_write, .read = hwmult_read }; diff --git a/ui/main.c b/ui/main.c index 17ab248..7a914ac 100644 --- a/ui/main.c +++ b/ui/main.c @@ -80,6 +80,7 @@ static const struct device_class *const driver_table[] = { &device_olimex_iso, &device_olimex_iso_mk2, &device_sim, + &device_simx, &device_uif, &device_bsl, &device_flash_bsl, @@ -550,11 +551,11 @@ int main(int argc, char **argv) goto fail_driver; } + simio_init(); + if (device_probe_id(device_default, args.devarg.forced_chip_id) < 0) printc_err("warning: device ID probe failed\n"); - simio_init(); - if (!(args.flags & OPT_NO_RC)) process_rc_file(args.alt_config); diff --git a/util/dis.c b/util/dis.c index faf88bf..6c34011 100644 --- a/util/dis.c +++ b/util/dis.c @@ -249,14 +249,24 @@ static int decode_14xx(const uint8_t *code, * valid single-operand instruction could not be found. */ static int decode_single(const uint8_t *code, address_t offset, - address_t size, struct msp430_instruction *insn) + address_t size, struct msp430_instruction *insn, + uint16_t ex_word) { uint16_t op = (code[1] << 8) | code[0]; int need_arg = 0; insn->itype = MSP430_ITYPE_SINGLE; insn->op = op & 0xff80; - insn->dsize = (op & 0x0040) ? MSP430_DSIZE_BYTE : MSP430_DSIZE_WORD; + + /* length encoding is based on AL bit (if ex_word present) and BW bit; + SWPB and SXT have non-standard encodings */ + insn->dsize = (insn->op != MSP430_OP_SWPB && insn->op != MSP430_OP_SXT) + ? ((!ex_word || (ex_word & 0x0040)) + ? ((op & 0x0040) ? MSP430_DSIZE_BYTE : MSP430_DSIZE_WORD) + : ((op & 0x0040) ? MSP430_DSIZE_AWORD : MSP430_DSIZE_UNKNOWN)) + : ((op & 0x0040) + ? MSP430_DSIZE_UNKNOWN + : (!ex_word || (ex_word & 0x0040)) ? MSP430_DSIZE_WORD : MSP430_DSIZE_AWORD); insn->dst_mode = (op >> 4) & 0x3; insn->dst_reg = op & 0xf; @@ -317,7 +327,9 @@ static int decode_double(const uint8_t *code, address_t offset, /* Decode and consume opcode */ insn->itype = MSP430_ITYPE_DOUBLE; insn->op = op & 0xf000; - insn->dsize = (op & 0x0040) ? MSP430_DSIZE_BYTE : MSP430_DSIZE_WORD; + insn->dsize = (!ex_word || (ex_word & 0x0040)) + ? ((op & 0x0040) ? MSP430_DSIZE_BYTE : MSP430_DSIZE_WORD) + : ((op & 0x0040) ? MSP430_DSIZE_AWORD : MSP430_DSIZE_UNKNOWN); insn->src_mode = (op >> 4) & 0x3; insn->src_reg = (op >> 8) & 0xf; @@ -785,7 +797,7 @@ int dis_decode(const uint8_t *code, address_t offset, address_t len, if ((op & 0xf000) >= 0x4000) ret = decode_double(code, offset, len, insn, ex_word); else if ((op & 0xf000) == 0x1000 && (op & 0xfc00) < 0x1280) - ret = decode_single(code, offset, len, insn); + ret = decode_single(code, offset, len, insn, ex_word); else return -1; @@ -796,16 +808,15 @@ int dis_decode(const uint8_t *code, address_t offset, address_t len, (insn->itype == MSP430_ITYPE_SINGLE || insn->src_mode == MSP430_AMODE_REGISTER)) { if ((ex_word >> 8) & 1) { - if (insn->op != MSP430_OP_RRCX) - return -1; - insn->op = MSP430_OP_RRUX; + if (insn->op == MSP430_OP_RRCX) + insn->op = MSP430_OP_RRUX; + else + insn->ignore_cy = 1; } insn->rep_register = (ex_word >> 7) & 1; insn->rep_index = ex_word & 0xf; } - if (!(ex_word & 0x40)) - insn->dsize |= 2; } else { if ((op & 0xf000) == 0x0000) ret = decode_00xx(code, len, insn); @@ -814,7 +825,7 @@ int dis_decode(const uint8_t *code, address_t offset, address_t len, else if ((op & 0xff00) == 0x1300) ret = decode_13xx(code, len, insn); else if ((op & 0xf000) == 0x1000) - ret = decode_single(code, offset, len, insn); + ret = decode_single(code, offset, len, insn, 0); else if ((op & 0xf000) >= 0x2000 && (op & 0xf000) < 0x4000) ret = decode_jump(code, offset, insn); else if ((op & 0xf000) >= 0x4000) diff --git a/util/dis.h b/util/dis.h index 174b35a..3e467fe 100644 --- a/util/dis.h +++ b/util/dis.h @@ -270,6 +270,7 @@ struct msp430_instruction { int rep_index; int rep_register; + int ignore_cy; }; /* Decode a single instruction. diff --git a/util/output_util.c b/util/output_util.c index e66b985..5a6b4d5 100644 --- a/util/output_util.c +++ b/util/output_util.c @@ -202,6 +202,9 @@ static int dis_format(const struct msp430_instruction *insn) else if (insn->rep_index) len += printc(" [repeat %d]", insn->rep_index + 1); + if (insn->ignore_cy) + len += printc(" [ignore carry]"); + return len; }