int repeats = 1; /* msp430X can repeat some instructions in some cases */
boolean zeroCarry = false; /* msp430X can zero carry in repeats */
boolean word = (instruction & 0x40) == 0;
/* NOTE: there is a mode when wordx20 = true & word = true that is resereved */
AccessMode mode = wordx20 ? AccessMode.WORD20 : (word ? AccessMode.WORD : AccessMode.BYTE);
//if (mode == AccessMode.WORD20) System.out.println("WORD20 not really supported...");
// Destination vars
int dstRegister = 0;
int dstAddress = -1;
boolean dstRegMode = false;
int dst = -1;
boolean write = false;
boolean updateStatus = true;
// When is PC increased probably immediately (e.g. here)?
pc += 2;
writeRegister(PC, pc);
switch (op) {
case 0:
// MSP430X - additional instructions
op = instruction & 0xf0f0;
if (!MSP430XArch)
throw new EmulationException("Executing MSP430X instruction but MCU is not a MSP430X");
// System.out.println("Executing MSP430X instruction op:" + Utils.hex16(op) +
// " ins:" + Utils.hex16(instruction) + " PC = $" + getAddressAsString(pc - 2));
int src = 0;
/* data is either bit 19-16 or src register */
int srcData = (instruction & 0x0f00) >> 8;
int dstData = (instruction & 0x000f);
boolean rrword = true;
mode = AccessMode.WORD20;
switch(op) {
// 20 bit register write
case MOVA_IND:
/* Read from address in src register (20-bit?), move to destination register (=20 bit). */
writeRegister(dstData, currentSegment.read(readRegister(srcData), mode, AccessType.READ));
updateStatus = false;
cycles += 3;
break;
case MOVA_IND_AUTOINC:
if (profiler != null && instruction == 0x0110) {
profiler.profileReturn(cpuCycles);
}
writeRegister(PC, pc);
/* read from address in register */
src = readRegister(srcData);
// System.out.println("Reading $" + getAddressAsString(src) +
// " from register: " + srcData);
dst = currentSegment.read(src, mode, AccessType.READ);
// System.out.println("Reading from mem: $" + getAddressAsString(dst));
writeRegister(srcData, src + 4);
// System.out.println("*** Writing $" + getAddressAsString(dst) + " to reg: " + dstData);
writeRegister(dstData, dst);
updateStatus = false;
cycles += 3;
break;
case MOVA_ABS2REG:
src = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
writeRegister(PC, pc += 2);
dst = src + (srcData << 16);
//System.out.println(Utils.hex20(pc) + " MOVA &ABS Reading from $" + getAddressAsString(dst) + " to reg: " + dstData);
dst = currentSegment.read(dst, mode, AccessType.READ);
//System.out.println(" => $" + getAddressAsString(dst));
writeRegister(dstData, dst);
updateStatus = false;
cycles += 4;
break;
case MOVA_INDX2REG:
/* Read data from address in memory, indexed by source
* register, and place into destination register. */
int index = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
int indexModifier = readRegister(srcData);
index = convertTwoComplement16(index);
indexModifier = convertTwoComplement20(indexModifier);
writeRegister(dstData, currentSegment.read(indexModifier + index, mode, AccessType.READ));
writeRegister(PC, pc += 2);
updateStatus = false;
cycles += 4;
break;
case MOVA_REG2ABS:
dst = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
writeRegister(PC, pc += 2);
currentSegment.write(dst + (dstData << 16), readRegister(srcData), mode);
updateStatus = false;
cycles += 4;
break;
case MOVA_REG2INDX:
/* Read data from register, write to address in memory,
* indexed by source register. */
index = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
indexModifier = readRegister(dstData);
index = convertTwoComplement16(index);
indexModifier = convertTwoComplement20(indexModifier);
currentSegment.write(indexModifier + index, readRegister(srcData), mode);
writeRegister(PC, pc += 2);
updateStatus = false;
cycles += 4;
break;
case MOVA_IMM2REG:
src = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
writeRegister(PC, pc += 2);
dst = src + (srcData << 16);
// System.out.println("*** Writing $" + getAddressAsString(dst) + " to reg: " + dstData);
dst &= 0xfffff;
writeRegister(dstData, dst);
updateStatus = false;
cycles += 2;
break;
case ADDA_IMM:
// For all immediate instructions, the data low 16 bits of
// the data is stored in the following word (PC + 2) and
// the high 4 bits in the instruction word, which we have
// masked out as srcData.
int immData = currentSegment.read(pc, AccessMode.WORD, AccessType.READ) + (srcData << 16);
writeRegister(PC, pc += 2);
int dstArg = readRegister(dstData);
dst = dstArg + immData;
sr = StatusRegister.updateSR(readRegister(SR), immData, dstArg, dst);
writeRegister(SR, sr);
updateStatus = false;
dst &= 0xfffff;
writeRegister(dstData, dst);
cycles += 3;
break;
case CMPA_IMM: {
/* Status Bits N: Set if result is negative (src > dst), reset if positive (src ≤ dst)
Z: Set if result is zero (src = dst), reset otherwise (src ≠ dst)
C: Set if there is a carry from the MSB, reset otherwise
V: Set if the subtraction of a negative source operand from a positive destination
operand delivers a negative result, or if the subtraction of a positive source
operand from a negative destination operand delivers a positive result, reset
otherwise (no overflow) */
immData = currentSegment.read(pc, AccessMode.WORD, AccessType.READ) + (srcData << 16);
writeRegister(PC, pc += 2);
sr = readRegister(SR);
int destRegValue = readRegister(dstData);
sr &= ~(NEGATIVE | ZERO | CARRY | OVERFLOW);
if (destRegValue >= immData) {
sr |= CARRY;
}
if (destRegValue < immData) {
sr |= NEGATIVE;
}
if (destRegValue == immData) {
sr |= ZERO;
}
int cmpTmp = destRegValue - immData;
int b = 0x80000; // CMPA always use 20 bit data length
if (((destRegValue ^ cmpTmp) & b) == 0 &&
(((destRegValue ^ immData) & b) != 0)) {
sr |= OVERFLOW;
}
writeRegister(SR, sr);
updateStatus = false;
cycles += 3;
break;
}
case SUBA_IMM:
immData = currentSegment.read(pc, AccessMode.WORD, AccessType.READ) + (srcData << 16);
writeRegister(PC, pc += 2);
dst = readRegister(dstData) - immData;
writeRegister(dstData, dst);
cycles += 3;
break;
case MOVA_REG:
cycles += 1;
/* as = 0 since register mode */
writeRegister(dstData, readRegisterCG(srcData, 0));
updateStatus = false;
break;
case CMPA_REG: {
sr = readRegister(SR);
sr &= ~(NEGATIVE | ZERO | CARRY | OVERFLOW);
int destRegValue = readRegister(dstData);
int srcRegValue = readRegisterCG(srcData, 0);
if (destRegValue >= srcRegValue) {
sr |= CARRY;
}
if (destRegValue < srcRegValue) {
sr |= NEGATIVE;
}
if (destRegValue == srcRegValue) {
sr |= ZERO;
}
int cmpTmp = destRegValue - srcRegValue;
int b = 0x80000; // CMPA always use 20 bit data length
if (((destRegValue ^ cmpTmp) & b) == 0 &&
(((destRegValue ^ srcRegValue) & b) != 0)) {
sr |= OVERFLOW;
}
writeRegister(SR, sr);
updateStatus = false;
cycles += 1;
break;
}
case ADDA_REG:
// Assume AS = 2
dst = readRegister(dstData) + readRegisterCG(srcData, 2);
writeRegister(dstData, dst);
sr = StatusRegister.updateSR(readRegister(SR), readRegisterCG(srcData, 2), dstData, dst);
writeRegister(SR, sr);
updateStatus = false;
cycles += 1;
break;
case SUBA_REG:
// Assume AS = 2
dst = readRegister(dstData) - readRegisterCG(srcData, 2);
writeRegister(dstData, dst);
cycles += 1;
break;
case RRXX_ADDR:
rrword = false;
case RRXX_WORD:
int count = ((instruction >> 10) & 0x03) + 1;
dst = readRegister(dstData);
sr = readRegister(SR);
int nxtCarry = 0;
int carry = (sr & CARRY) > 0? 1: 0;
if (rrword) {
mode = AccessMode.WORD;
dst = dst & 0xffff;
}
cycles += 1 + count;
switch(instruction & RRMASK) {
/* if word zero anything above */
case RRCM:
/* if (rrword): Rotate right through carry the 16-bit CPU register content
if (!rrword): Rotate right through carry the 20-bit CPU register content */
/* Pull the (count) lowest bits from dst - those will
* be placed in the (count) high bits of dst after the
* instruction is complete. */
int dst_low = dst & ((1 << count) - 1);
/* Grab the bit that will be in the carry flag when instruction completes. */
nxtCarry = (dst & (1 << (count + 1))) > 0? CARRY: 0;
/* Rotate dst. */
dst = dst >> (count);
/* Rotate the high bits, insert into dst. */
if (rrword) {
dst |= (dst_low << (17 - count)) | (carry << (16 - count));
} else {
dst |= (dst_low << (21 - count)) | (carry << (20 - count));
}
break;
case RRAM:
// System.out.println("RRAM executing");
/* roll in MSB from above */
/* 1 11 111 1111 needs to get in if MSB is 1 */
if ((dst & (rrword ? 0x8000 : 0x80000)) > 0) {
/* add some 1 bits above MSB if MSB is 1 */
dst = dst | (rrword ? 0xf8000 : 0xf80000);
}
dst = dst >> (count - 1);
nxtCarry = (dst & 1) > 0 ? CARRY : 0;
dst = dst >> 1;
break;
case RLAM:
// System.out.println("RLAM executing at " + pc);
/* just roll in "zeroes" from left */
dst = dst << (count - 1);
nxtCarry = (dst & (rrword ? 0x8000 : 0x80000)) > 0 ? CARRY : 0;
dst = dst << 1;
break;
case RRUM:
//System.out.println("RRUM executing");
/* just roll in "zeroes" from right */
dst = dst >> (count - 1);
nxtCarry = (dst & 1) > 0 ? CARRY : 0;
dst = dst >> 1;
break;
}
/* clear overflow - set carry according to above OP */
writeRegister(SR, (sr & ~(CARRY | OVERFLOW)) | nxtCarry);
dst = dst & (rrword ? 0xffff : 0xfffff);
writeRegister(dstData, dst);
break;
default:
System.out.println("MSP430X instruction not yet supported: " +
Utils.hex16(instruction) +
" op " + Utils.hex16(op));
throw new EmulationException("Found unsupported MSP430X instruction " +
Utils.hex16(instruction) +
" op " + Utils.hex16(op));
}
break;
case 1:
{
// -------------------------------------------------------------------
// Single operand instructions
// -------------------------------------------------------------------
// Register
dstRegister = instruction & 0xf;
/* check if this is a MSP430X CALLA instruction */
if ((op = instruction & CALLA_MASK) > RETI) {
pc = readRegister(PC);
dst = -1; /* will be -1 if not a call! */
/* do not update status after these instructions!!! */
updateStatus = false;
switch(op) {
case CALLA_REG:
// The CALLA operations increase the SP before
// address resolution!
// store on stack - always move 2 steps before resolution
sp = readRegister(SP) - 2;
writeRegister(SP, sp);
dst = readRegister(dstRegister);
/*System.out.println("CALLA REG => " + Utils.hex20(dst));*/
cycles += 5;
break;
case CALLA_INDEX:
/* CALLA X(REG) => REG + X is the address*/
sp = readRegister(SP) - 2;
writeRegister(SP, sp);
// System.out.println("CALLA INDX: R" + dstRegister);
dst = readRegister(dstRegister);
/* what happens if wrapping here??? */
/* read the index which is from -15 bit - +15 bit. - so extend sign to 20-bit */
int index = currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
index = convertTwoComplement16(index);
// System.out.println("CALLA INDX: Reg = " + Utils.hex20(dst) + " INDX: " + index);
dst += index;
dst &= 0xfffff;
// System.out.println("CALLA INDX => " + Utils.hex20(dst));
dst = currentSegment.read(dst, AccessMode.WORD20, AccessType.READ);
// System.out.println("CALLA Read from INDX => " + Utils.hex20(dst));
cycles += 5;
pc += 2;
// System.exit(0);
break;
case CALLA_IMM:
sp = readRegister(SP) - 2;
writeRegister(SP, sp);
dst = (dstRegister << 16) | currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
pc += 2;
cycles += 5;
break;
case CALLA_IND:
sp = readRegister(SP) - 2;
writeRegister(SP, sp);
dstAddress = readRegister(dstRegister);
dst = currentSegment.read(dstAddress, AccessMode.WORD20, AccessType.READ);
cycles += 5;
break;
case CALLA_ABS:
sp = readRegister(SP) - 2;
writeRegister(SP, sp);
/* read the address of where the address to call is */
dst = (dstRegister << 16) | currentSegment.read(pc, AccessMode.WORD, AccessType.READ);
dst = currentSegment.read(dst, AccessMode.WORD20, AccessType.READ);
pc += 2;
cycles += 7;
break;
default:
AccessMode type = AccessMode.WORD;
int size = 2;
sp = readRegister(SP);
/* check for PUSHM... POPM... */
switch(op & 0x1f00) {
case PUSHM_A: