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;
}