]> code.bitgloo.com Git - clyne/lib430core.git/commitdiff
first draft; needs tests
authorClyne Sullivan <clyne@bitgloo.com>
Fri, 22 Oct 2021 11:50:50 +0000 (07:50 -0400)
committerClyne Sullivan <clyne@bitgloo.com>
Fri, 22 Oct 2021 11:50:50 +0000 (07:50 -0400)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
link.ld [new file with mode: 0644]
src/core.c [new file with mode: 0644]
src/core.h [new file with mode: 0644]
src/do_cycle_byte.c [new file with mode: 0644]
src/do_cycle_word.c [new file with mode: 0644]
tests/Makefile [new file with mode: 0644]
tests/core_test.c [new file with mode: 0644]
tests/test.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9eca6c8
--- /dev/null
@@ -0,0 +1,2 @@
+*.a
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d9c168c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+CROSS :=
+CC := $(CROSS)gcc
+CFLAGS := -Os -ggdb -g3 \
+                 -Wall -Wextra -pedantic -Werror
+
+CSRC := $(wildcard src/*.c)
+COBJ := $(patsubst %.c,%.o,$(CSRC))
+
+all: lib430core.a
+
+clean:
+       @echo "  CLEAN"
+       @rm -f lib430core.a $(COBJ)
+
+lib430core.a: $(COBJ)
+       @echo "  AR      " $@
+       @$(CROSS)ar rc $@ $(COBJ)
+       @$(CROSS)size -t $@
+
+.c.o:
+       @echo "  CC      " $<
+       @$(CC) $(CFLAGS) -c $< -o $@
+
diff --git a/link.ld b/link.ld
new file mode 100644 (file)
index 0000000..582fb5f
--- /dev/null
+++ b/link.ld
@@ -0,0 +1,15 @@
+SECTIONS {
+       . = 0x0000;
+       .text : {
+               * (.text);
+               . = ALIGN(8);
+               * (.rodata);
+       }
+       . = ALIGN(8);
+       .data : {
+               * (.data);
+               . = ALIGN(8);
+               * (.bss);
+       }
+}
+
diff --git a/src/core.c b/src/core.c
new file mode 100644 (file)
index 0000000..c29fe96
--- /dev/null
@@ -0,0 +1,258 @@
+#include "core.h"
+
+#include <stdint.h>
+
+#define MSP430_SR_C (1 << 0)
+#define MSP430_SR_Z (1 << 1)
+#define MSP430_SR_N (1 << 2)
+#define MSP430_SR_V (1 << 8)
+
+// r0 = pc
+// r1 = sp
+// r2 = sr
+
+static int msp430_do_cycle_jump(msp430_t *state, uint16_t opcode);
+int msp430_do_cycle_single_operand(msp430_t *state, uint16_t opcode);
+int msp430_do_cycle_dual_operand(msp430_t *state, uint16_t opcode);
+int msp430_do_cycle_single_operand_byte(msp430_t *state, uint16_t opcode);
+int msp430_do_cycle_dual_operand_byte(msp430_t *state, uint16_t opcode);
+
+uint16_t *msp430_do_cycle_get_single_operand(msp430_t *state, uint16_t opcode);
+uint16_t *msp430_do_cycle_get_source_operand(msp430_t *state, uint16_t opcode);
+uint16_t *msp430_do_cycle_get_dest_operand(msp430_t *state, uint16_t opcode);
+
+static inline int msp430_get_opcode_bw(uint16_t opcode);
+
+void msp430_init_state(msp430_t *state, void *mem)
+{
+       for (int i = 0; i < 16; ++i)
+               state->reg[i] = 0;
+       state->mem = mem;
+}
+
+int msp430_do_cycle(msp430_t *state)
+{
+       uint16_t pc = state->reg[0];
+       uint16_t opcode = *((uint16_t *)(state->mem + pc));
+
+       // Check for program end...
+       if (opcode == 0x4130 && state->reg[1] == 0)
+               return 1;
+
+       state->reg[0] = pc + 2;
+
+       int ret;
+       if ((opcode & 0xE000) == 0x2000) {
+               ret = msp430_do_cycle_jump(state, opcode);
+       } else {
+               int bw = msp430_get_opcode_bw(opcode);
+               if (bw == 0) {
+                       if ((opcode & 0xFC00) == 0x1000)
+                               ret = msp430_do_cycle_single_operand(state, opcode);
+                       else
+                               ret = msp430_do_cycle_dual_operand(state, opcode);
+               } else {
+                       if ((opcode & 0xFC00) == 0x1000)
+                               ret = msp430_do_cycle_single_operand_byte(state, opcode);
+                       else
+                               ret = msp430_do_cycle_dual_operand_byte(state, opcode);
+               }
+       }
+
+       return ret;
+}
+
+static void msp430_jump_to_offset(msp430_t *state, uint16_t opcode)
+{
+       uint16_t addr = opcode & 0x3FF;
+       if (addr & (1 << 9))
+               addr |= 0xFC00;
+       int16_t saddr = (int16_t)addr * 2;
+       state->reg[0] += saddr;
+}
+
+int msp430_do_cycle_jump(msp430_t *state, uint16_t opcode)
+{
+       // PC to become PC + ((opcode & 0x3FF) << 1)
+       switch ((opcode & 0x01C0) >> 10) {
+       case 0:
+               // JNE/JZ
+               if ((state->reg[2] & MSP430_SR_Z) == 0)
+                       msp430_jump_to_offset(state, opcode);
+               break;
+       case 1:
+               // JEQ/JZ
+               if ((state->reg[2] & MSP430_SR_Z) == MSP430_SR_Z)
+                       msp430_jump_to_offset(state, opcode);
+               break;
+       case 2:
+               // JNC/JLO
+               if ((state->reg[2] & MSP430_SR_C) == 0)
+                       msp430_jump_to_offset(state, opcode);
+               break;
+       case 3:
+               // JC/JHS
+               if ((state->reg[2] & MSP430_SR_C) == MSP430_SR_C)
+                       msp430_jump_to_offset(state, opcode);
+               break;
+       case 4:
+               // JN
+               if ((state->reg[2] & MSP430_SR_N) == MSP430_SR_N)
+                       msp430_jump_to_offset(state, opcode);
+               break;
+       case 5: {
+               // JGE
+               uint16_t flags = state->reg[2] & (MSP430_SR_N | MSP430_SR_V);
+               if (flags == 0 || flags == (MSP430_SR_N | MSP430_SR_V))
+                       msp430_jump_to_offset(state, opcode);
+               break; }
+       case 6: {
+               // JL
+               uint16_t flags = state->reg[2] & (MSP430_SR_N | MSP430_SR_V);
+               if (flags == MSP430_SR_N || flags == MSP430_SR_V)
+                       msp430_jump_to_offset(state, opcode);
+               break; }
+       case 7:
+               // JMP
+               msp430_jump_to_offset(state, opcode);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+uint16_t *msp430_do_cycle_get_operand(msp430_t *state, int AS, int BW, int SOURCE)
+{
+       static uint16_t constants[6] = {
+               0, 1, 2, -1, 4, 8
+       };
+
+       if (SOURCE == 0) {
+               if (AS == 1) {
+                       // Operand at PC + X
+                       // X = word after PC
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[0] + offset);
+                       state->reg[0] += 2;
+                       return ret;
+               } else if (AS == 3) {
+                       // Operand is X
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[0]);
+                       state->reg[0] += 2;
+                       return ret;
+               }
+       }
+
+       if (SOURCE == 2) {
+               switch (AS) {
+               case 0:
+                       // R2
+                       return &state->reg[2];
+                       break;
+               case 1: {
+                       // Operand at X
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + offset);
+                       state->reg[0] += 2;
+                       return ret;
+                       break; }
+               case 2:
+                       return &constants[4];
+                       break;
+               case 3:
+                       return &constants[5];
+                       break;
+               }
+       } else if (SOURCE == 3) {
+               // Constants
+               return &constants[AS];
+       } else {
+               switch (AS) {
+               case 0:
+                       // Register
+                       return &state->reg[SOURCE];
+                       break;
+               case 1: {
+                       // Operand at Rn + X
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[SOURCE] + offset);
+                       state->reg[0] += 2;
+                       return ret;
+                       break; }
+               case 2:
+                       // Operand at Rn
+                       return (uint16_t *)(state->mem + state->reg[SOURCE]);
+                       break;
+               case 3: {
+                       // Operand at Rn, increment Rn
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[SOURCE]);
+                       state->reg[SOURCE] += BW ? 1 : 2;
+                       return ret;
+                       break; }
+               }
+       }
+
+       return 0; // Failed...
+}
+
+inline int msp430_get_opcode_bw(uint16_t opcode)
+{
+       return opcode & (1 << 6);
+}
+
+uint16_t *msp430_do_cycle_get_single_operand(msp430_t *state, uint16_t opcode)
+{
+       return msp430_do_cycle_get_operand(state,
+               /* AS */     (opcode & 0x30) >> 4,
+               /* BW */     msp430_get_opcode_bw(opcode),
+               /* SOURCE */ (opcode & 0xF));
+}
+
+uint16_t *msp430_do_cycle_get_source_operand(msp430_t *state, uint16_t opcode)
+{
+       return msp430_do_cycle_get_operand(state,
+               /* AS */     (opcode & 0x30) >> 4,
+               /* BW */     msp430_get_opcode_bw(opcode),
+               /* SOURCE */ (opcode & 0xF00) >> 8);
+}
+uint16_t *msp430_do_cycle_get_dest_operand(msp430_t *state, uint16_t opcode)
+{
+       int AD = opcode & 0x80;
+       int DEST = opcode & 0xF;
+
+       if (AD == 0) {
+               // Byte operation? Clear MSB.
+               if (msp430_get_opcode_bw(opcode) == 0) {
+                       return &state->reg[DEST];
+               } else {
+                       uint16_t *ret = &state->reg[DEST];
+                       *ret &= 0xFF;
+                       return ret;
+               }
+       } else {
+               if (DEST == 0) {
+                       // dest at PC + X
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[0] + offset);
+                       state->reg[0] += 2;
+                       return ret;
+               } else if (DEST == 2) {
+                       // dest at X
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + offset);
+                       state->reg[0] += 2;
+                       return ret;
+               } else {
+                       // dest at Rn + X
+                       uint16_t offset = *((uint16_t *)(state->mem + state->reg[0]));
+                       uint16_t *ret = (uint16_t *)(state->mem + state->reg[DEST] + offset);
+                       state->reg[0] += 2;
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
diff --git a/src/core.h b/src/core.h
new file mode 100644 (file)
index 0000000..2a320a1
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef CORE_H
+#define CORE_H
+
+#include <stdint.h>
+
+typedef struct {
+       uint16_t reg[16];
+       uint8_t *mem;
+} msp430_t;
+
+void msp430_init_state(msp430_t *state, void *mem);
+int msp430_do_cycle(msp430_t *state);
+
+#endif // CORE_H
+
diff --git a/src/do_cycle_byte.c b/src/do_cycle_byte.c
new file mode 100644 (file)
index 0000000..247d3b4
--- /dev/null
@@ -0,0 +1,214 @@
+#include <stdint.h>
+
+#define MSP430_SR_C (1 << 0)
+#define MSP430_SR_Z (1 << 1)
+#define MSP430_SR_N (1 << 2)
+#define MSP430_SR_V (1 << 8)
+
+// r0 = pc
+// r1 = sp
+// r2 = sr
+typedef struct {
+       uint16_t reg[16];
+       uint8_t *mem;
+} msp430_t;
+
+extern uint16_t *msp430_do_cycle_get_single_operand(msp430_t *state, uint16_t opcode);
+extern uint16_t *msp430_do_cycle_get_source_operand(msp430_t *state, uint16_t opcode);
+extern uint16_t *msp430_do_cycle_get_dest_operand(msp430_t *state, uint16_t opcode);
+
+int msp430_do_cycle_single_operand_byte(msp430_t *state, uint16_t opcode)
+{
+       uint8_t *operand = (uint8_t *)msp430_do_cycle_get_single_operand(state, opcode);
+       if (operand == 0)
+               return -1;
+
+       switch ((opcode & 0x0380) >> 7) {
+       case 0: {
+               // RRC
+               uint8_t res = *operand;
+               uint16_t sr = 0;
+               if (res & 1)
+                       sr |= MSP430_SR_C;
+               res >>= 1;
+               if (state->reg[2] & MSP430_SR_C)
+                       res |= 0x80;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               *operand = res;
+               state->reg[2] = sr;
+               break; }
+       case 2: {
+               // RRA
+               uint8_t res = *operand;
+               uint16_t sr = 0;
+               if (res & 1)
+                       sr |= MSP430_SR_C;
+               res >>= 1;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               *operand = res;
+               state->reg[2] = sr;
+               break; }
+       case 4: {
+               // PUSH
+               uint16_t sp = state->reg[1] - 2;
+               *((uint16_t *)(state->mem + sp)) = *operand;
+               state->reg[1] = sp;
+               break; }
+       default:
+               return -1;
+               break;
+       }
+
+       return 0;
+}
+
+int msp430_do_cycle_dual_operand_byte(msp430_t *state, uint16_t opcode)
+{
+       uint8_t *src = (uint8_t *)msp430_do_cycle_get_source_operand(state, opcode);
+       uint8_t *dst = (uint8_t *)msp430_do_cycle_get_dest_operand(state, opcode);
+       if (src == 0 || dst == 0)
+               return -1;
+
+       switch ((opcode & 0xF000) >> 12) {
+       case 4:
+               // MOV
+               *dst = *src;
+               break;
+       case 5: {
+               // ADD
+               uint16_t res = *src + *dst;
+               uint16_t sr = 0;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFF00) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x80) ^ (*src & 0x80)) == 0 && (*src & 0x80) != (res & 0x80))
+                       sr |= MSP430_SR_V;
+               *dst = res & 0xFF;
+               state->reg[2] = sr;
+               break; }
+       case 6: {
+               // ADDC
+               uint16_t res = *src + *dst + (state->reg[2] & MSP430_SR_C);
+               uint16_t sr = 0;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFF00) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x80) ^ (*src & 0x80)) == 0 && (*src & 0x80) != (res & 0x80))
+                       sr |= MSP430_SR_V;
+               *dst = res & 0xFF;
+               state->reg[2] = sr;
+               break; }
+       case 7: {
+               // SUBC
+               uint16_t res = *dst + ~(*src) + (state->reg[2] & MSP430_SR_C);
+               uint16_t sr = 0;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFF00) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x80) ^ (*src & 0x80)) == 0 && (*src & 0x80) != (res & 0x80))
+                       sr |= MSP430_SR_V;
+               *dst = res & 0xFF;
+               state->reg[2] = sr;
+               break; }
+       case 8: {
+               // SUB
+               uint16_t res = *dst + ~(*src) + 1;
+               uint16_t sr = 0;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFF00) != 0) // TODO confirm
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x80) ^ (*src & 0x80)) == 0 && (*src & 0x80) != (res & 0x80))
+                       sr |= MSP430_SR_V;
+               *dst = res & 0xFF;
+               state->reg[2] = sr;
+               break; }
+       case 9: {
+               // CMP
+               uint16_t res = *dst + ~(*src) + 1;
+               uint16_t sr = 0;
+               if ((int8_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFF00) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x80) ^ (*src & 0x80)) == 0 && (*src & 0x80) != (res & 0x80))
+                       sr |= MSP430_SR_V;
+               state->reg[2] = sr;
+               break; }
+       case 10:
+               // DADD TODO
+               break;
+       case 11: {
+               // BIT
+               uint8_t res = *dst & *src;
+               uint16_t sr = 0;
+               if (res & 0x80)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               state->reg[2] = sr;
+               break; }
+       case 12:
+               // BIC
+               *dst &= ~(*src);
+               break;
+       case 13:
+               // BIS
+               *dst |= *src;
+               break;
+       case 14: {
+               // XOR
+               uint8_t res = *dst ^ *src;
+               uint16_t sr = 0;
+               if (res & 0x80)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               if ((*dst & 0x80) && (*src & 0x80))
+                       sr |= MSP430_SR_V;
+               *dst = res;
+               state->reg[2] = sr;
+               break; }
+       case 15: {
+               // AND
+               uint8_t res = *dst & *src;
+               uint16_t sr = 0;
+               if (res & 0x80)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               *dst = res;
+               state->reg[2] = sr;
+               break; }
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
diff --git a/src/do_cycle_word.c b/src/do_cycle_word.c
new file mode 100644 (file)
index 0000000..2599d13
--- /dev/null
@@ -0,0 +1,246 @@
+#include <stdint.h>
+
+#define MSP430_SR_C (1 << 0)
+#define MSP430_SR_Z (1 << 1)
+#define MSP430_SR_N (1 << 2)
+#define MSP430_SR_V (1 << 8)
+
+// r0 = pc
+// r1 = sp
+// r2 = sr
+typedef struct {
+       uint16_t reg[16];
+       uint8_t *mem;
+} msp430_t;
+
+extern uint16_t *msp430_do_cycle_get_single_operand(msp430_t *state, uint16_t opcode);
+extern uint16_t *msp430_do_cycle_get_source_operand(msp430_t *state, uint16_t opcode);
+extern uint16_t *msp430_do_cycle_get_dest_operand(msp430_t *state, uint16_t opcode);
+
+int msp430_do_cycle_single_operand(msp430_t *state, uint16_t opcode)
+{
+       uint16_t *operand = msp430_do_cycle_get_single_operand(state, opcode);
+       if (operand == 0)
+               return -1;
+
+       switch ((opcode & 0x0380) >> 7) {
+       case 0: {
+               // RRC
+               uint16_t res = *operand;
+               uint16_t sr = 0;
+               if (res & 1)
+                       sr |= MSP430_SR_C;
+               res >>= 1;
+               if (state->reg[2] & MSP430_SR_C)
+                       res |= 0x8000;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               *operand = res;
+               state->reg[2] = sr;
+               break; }
+       case 1: {
+               // SWPB
+               uint16_t swapped = ((*operand & 0xFF00) >> 8) | ((*operand & 0x00FF) << 8);
+               *operand = swapped;
+               break; }
+       case 2: {
+               // RRA
+               uint16_t res = *operand;
+               uint16_t sr = 0;
+               if (res & 1)
+                       sr |= MSP430_SR_C;
+               res >>= 1;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               *operand = res;
+               state->reg[2] = sr;
+               break; }
+       case 3: {
+               // SXT
+               uint16_t sr = 0;
+               uint16_t res = (*operand & 0x80) ? (*operand | 0xFF00) : *operand;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               *operand = res;
+               state->reg[2] = sr;
+               break; }
+       case 4: {
+               // PUSH
+               uint16_t sp = state->reg[1] - 2;
+               *((uint16_t *)(state->mem + sp)) = *operand;
+               state->reg[1] = sp;
+               break; }
+       case 5: {
+               // CALL
+               uint16_t sp = state->reg[1] - 2;
+               *((uint16_t *)(state->mem + sp)) = state->reg[0];
+               state->reg[0] = *operand;
+               state->reg[1] = sp;
+               break; }
+       case 6: {
+               // RETI
+               uint16_t sp = state->reg[1];
+               state->reg[2] = *((uint16_t *)(state->mem + sp));
+               state->reg[0] = *((uint16_t *)(state->mem + sp + 2));
+               state->reg[1] = sp + 4;
+               break; }
+       default:
+               return -1;
+               break;
+       }
+
+       return 0;
+}
+
+int msp430_do_cycle_dual_operand(msp430_t *state, uint16_t opcode)
+{
+       uint16_t *src = msp430_do_cycle_get_source_operand(state, opcode);
+       uint16_t *dst = msp430_do_cycle_get_dest_operand(state, opcode);
+       if (src == 0 || dst == 0)
+               return -1;
+
+       switch ((opcode & 0xF000) >> 12) {
+       case 4:
+               // MOV
+               *dst = *src;
+               break;
+       case 5: {
+               // ADD
+               uint32_t res = *src + *dst;
+               uint16_t sr = 0;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFFFF0000) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x8000) ^ (*src & 0x8000)) == 0 && (*src & 0x8000) != (res & 0x8000))
+                       sr |= MSP430_SR_V;
+               *dst = (uint16_t)res;
+               state->reg[2] = sr;
+               break; }
+       case 6: {
+               // ADDC
+               uint32_t res = *src + *dst + (state->reg[2] & MSP430_SR_C);
+               uint16_t sr = 0;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFFFF0000) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x8000) ^ (*src & 0x8000)) == 0 && (*src & 0x8000) != (res & 0x8000))
+                       sr |= MSP430_SR_V;
+               *dst = (uint16_t)res;
+               state->reg[2] = sr;
+               break; }
+       case 7: {
+               // SUBC
+               uint32_t res = *dst + ~(*src) + (state->reg[2] & MSP430_SR_C);
+               uint16_t sr = 0;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFFFF0000) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x8000) ^ (*src & 0x8000)) == 0 && (*src & 0x8000) != (res & 0x8000))
+                       sr |= MSP430_SR_V;
+               *dst = (uint16_t)res;
+               state->reg[2] = sr;
+               break; }
+       case 8: {
+               // SUB
+               uint32_t res = *dst + ~(*src) + 1;
+               uint16_t sr = 0;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFFFF0000) != 0) // TODO confirm
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x8000) ^ (*src & 0x8000)) == 0 && (*src & 0x8000) != (res & 0x8000))
+                       sr |= MSP430_SR_V;
+               *dst = (uint16_t)res;
+               state->reg[2] = sr;
+               break; }
+       case 9: {
+               // CMP
+               uint32_t res = *dst + ~(*src) + 1;
+               uint16_t sr = 0;
+               if ((int16_t)res < 0)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if ((res & 0xFFFF0000) != 0)
+                       sr |= MSP430_SR_C;
+               if (((*src & 0x8000) ^ (*src & 0x8000)) == 0 && (*src & 0x8000) != (res & 0x8000))
+                       sr |= MSP430_SR_V;
+               state->reg[2] = sr;
+               break; }
+       case 10:
+               // DADD TODO
+               break;
+       case 11: {
+               // BIT
+               uint16_t res = *dst & *src;
+               uint16_t sr = 0;
+               if (res & 0x8000)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               state->reg[2] = sr;
+               break; }
+       case 12:
+               // BIC
+               *dst &= ~(*src);
+               break;
+       case 13:
+               // BIS
+               *dst |= *src;
+               break;
+       case 14: {
+               // XOR
+               uint16_t res = *dst ^ *src;
+               uint16_t sr = 0;
+               if (res & 0x8000)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               if ((*dst & 0x8000) && (*src & 0x8000))
+                       sr |= MSP430_SR_V;
+               *dst = res;
+               state->reg[2] = sr;
+               break; }
+       case 15: {
+               // AND
+               uint16_t res = *dst & *src;
+               uint16_t sr = 0;
+               if (res & 0x8000)
+                       sr |= MSP430_SR_N;
+               if (res == 0)
+                       sr |= MSP430_SR_Z;
+               if (res != 0)
+                       sr |= MSP430_SR_C;
+               *dst = res;
+               state->reg[2] = sr;
+               break; }
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644 (file)
index 0000000..43d4134
--- /dev/null
@@ -0,0 +1,24 @@
+DEBUG := #-DDEBUG
+
+LIB430COREDIR = ..
+
+TEST_CSRC = test.c
+TEST_OUT = $(patsubst %.c,%.o,$(TEST_CSRC))
+
+all: $(LIB430COREDIR)/lib430core.a $(TEST_OUT)
+
+$(LIB430COREDIR)/lib430core.a:
+       @$(MAKE) -C ..
+
+.c.o:
+       @echo "Testing" $< "..."
+       @msp430-elf32-gcc -T../link.ld $< -o tmp.elf -nostdlib -lgcc -lc
+       @msp430-elf32-objcopy -Obinary tmp.elf tmp.bin
+       @echo "#define TESTBIN \\" > test.h
+       @od -t x1 tmp.bin | awk '{for (i=2; i<=NF; i++) printf "0x" $$i ", "}' >> test.h
+       @printf "\n#define TESTSIZE %u \n" $$(msp430-elf32-objdump tmp.elf -Dj .text | grep -E "^\s+[0-9a-fA-F]+:" | wc -l) >> test.h
+       @echo >> test.h
+       @gcc -Os -ggdb -g3 -Wall -W -pedantic $(DEBUG) -I../src -o tmp \
+               core_test.c -L$(LIB430COREDIR) -l430core
+       @./tmp > log
+       @rm log tmp.elf tmp.bin tmp test.h
diff --git a/tests/core_test.c b/tests/core_test.c
new file mode 100644 (file)
index 0000000..08e2650
--- /dev/null
@@ -0,0 +1,52 @@
+#include "core.h"
+#include "test.h"
+
+#include <stdio.h>
+
+//#define DEBUG
+
+#ifdef DEBUG
+static void dump_state(msp430_t *state)
+{
+       puts("MSP430 dump state:");
+       printf("R0/PC: 0x%04x R1/SP: 0x%04x R2/SR: 0x%04x R3:  0x%04x\n",
+               state->reg[0], state->reg[1], state->reg[2], state->reg[3]);
+       printf("R4:    0x%04x R5:    0x%04x R6:    0x%04x R7:  0x%04x\n",
+               state->reg[4], state->reg[5], state->reg[6], state->reg[7]);
+       printf("R8:    0x%04x R9:    0x%04x R10:   0x%04x R11: 0x%04x\n",
+               state->reg[8], state->reg[9], state->reg[10], state->reg[11]);
+       printf("R12:   0x%04x R13:   0x%04x R14:   0x%04x R15: 0x%04x\n\n",
+               state->reg[12], state->reg[13], state->reg[14], state->reg[15]);
+}
+#endif // DEBUG
+
+static uint8_t mem[0x10000] = {
+       TESTBIN
+};
+
+int main()
+{
+       msp430_t state;
+
+       msp430_init_state(&state, mem);
+
+#ifdef DEBUG
+       dump_state(&state);
+#endif // DEBUG
+       int r;
+       do {
+               r = msp430_do_cycle(&state);
+               if (r < 0) {
+                       printf("Failed to execute near PC=0x%04x!\n", state.reg[0]);
+                       return 1;
+                       //break;
+               }
+
+#ifdef DEBUG
+               dump_state(&state);
+#endif // DEBUG
+       } while (r != 1);
+
+       return 0;
+}
+
diff --git a/tests/test.c b/tests/test.c
new file mode 100644 (file)
index 0000000..8eb344a
--- /dev/null
@@ -0,0 +1,8 @@
+int main()
+{
+       volatile int *mem = (int *)0x200;
+       *mem = 24;
+       for (int i = 0; i < 8; ++i)
+               *mem += 2;
+       return *mem;
+}