Add start of emulator, debugger
diff --git a/cpu.c b/cpu.c
index af9ad27..fe5cf6a 100644
--- a/cpu.c
+++ b/cpu.c
@@ -7,15 +7,18 @@
#include <string.h>
#define die(m, ...) \
- printf("\033[31mError: " m "\033[0m\n", ##__VA_ARGS__); \
+ printf("\033[31m" m "\033[0m\n", ##__VA_ARGS__); \
exit(1);
+#define warn(m, ...) \
+ printf("\033[33m" m "\033[0m\n", ##__VA_ARGS__);
+
cpu_t new_cpu()
{
cpu_t cpu = { 0 };
- cpu.regs[SR] = UNUSED; // unused flag always set
cpu.regs[SP] = 0xFD; // stack at is 0x100 + SP
cpu.pc = 0; // arbitrary program counter start
+ cpu.running = true;
cpu.mem = malloc(0xFFFF);
memset(cpu.mem, 0, 0xFFFF);
@@ -27,24 +30,117 @@
return cpu;
}
+void stack_push(cpu_t *cpu, uint8_t v)
+{
+ cpu->mem[cpu->regs[SP]-- + 0x100] = v;
+}
+
+uint8_t stack_pop(cpu_t *cpu)
+{
+ return cpu->mem[cpu->regs[SP]++ + 0x100];
+}
+
void free_cpu(cpu_t *cpu)
{
free(cpu->mem);
}
-void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t addr)
+void stat_nz(cpu_t *cpu, int8_t v)
{
+ cpu->status.negative = v < 0;
+ cpu->status.zero = v == 0;
+}
+
+// Used to check for overflow, is c unique?
+bool last_unique(bool a, bool b, bool c)
+{
+ return a == b && a != c;
+}
+
+void stat_cv(cpu_t *cpu, uint8_t a, uint8_t b, uint8_t c)
+{
+ cpu->status.overflow = last_unique(a >> 7, b >> 7, c >> 7);
+ cpu->status.carry = c < a || c < b;
+}
+
+void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a)
+{
+ // used to save space
+ #define REGS \
+ R(X) R(A) R(Y)
+
switch (op) {
-
+ // Load and store instructions:
+ #define R(reg) \
+ case LD##reg: \
+ cpu->regs[reg] = a.val; \
+ stat_nz(cpu, a.val); \
+ break;
+
+ REGS
+
+ #undef R
+
+ #define R(reg) \
+ case ST##reg: \
+ cpu->mem[a.ptr] = cpu->regs[reg]; \
+ break; \
+
+ REGS
+
+ #undef R
+
+ // Arithmetic instructions:
+ // NOTE: binary coded decimals are NOT SUPPORTED because I don't want
+ // to implement them.
+ case ADC:
+ {
+ uint8_t sum = cpu->regs[A] + a.val + cpu->status.carry;
+ // signed overflow
+ stat_cv(cpu, cpu->regs[A], a.val + cpu->status.carry, sum);
+ stat_nz(cpu, sum);
+ cpu->regs[A] = sum;
+ break;
+ }
+
+ case SBC:
+ {
+ uint8_t diff = cpu->regs[A] - a.val - !cpu->status.carry;
+ stat_cv(cpu, cpu->regs[A], a.val - !cpu->status.carry, diff);
+ stat_nz(cpu, diff);
+ cpu->regs[A] = diff;
+ break;
+ }
+
+ case INC:
+ cpu->mem[a.ptr]++;
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ break;
+
+ case INX:
+ cpu->regs[X]++;
+ stat_nz(cpu, cpu->regs[X]);
+ break;
+
+ case INY:
+ cpu->regs[Y]++;
+ stat_nz(cpu, cpu->regs[Y]);
+ break;
+
+ case DEC:
+ cpu->mem[a.ptr]--;
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ break;
}
+ #undef REGS
}
uint16_t le_to_native(uint8_t a, uint8_t b)
{
#ifdef LITTLE_ENDIAN
- return a << 8 | b;
-#else
return b << 8 | a;
+#else
+ return a << 8 | b;
#endif
}
@@ -91,15 +187,21 @@
case AM_REL:
{
- // PC should point to the opcode
- // braces needed to avoid c stupidity
- uint16_t pc = cpu->pc - 1;
- return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + pc);
+ // Aparently, PC should will point to the NEXT opcode
+ // I can't find any documentation on this unfortunately, but
+ // I have discovered this through testing the output of other
+ // assemblers.
+ uint16_t pc = cpu->pc + 1;
+ return arg_ptr(cpu, f, (int8_t)cpu->mem[cpu->pc++] + pc);
}
case AM_IND:
{
uint16_t addr = fetch_le(cpu);
+
+ if (f & FETCH_NO_INDIRECTION)
+ return arg_imm(addr);
+
uint8_t low = cpu->mem[addr],
high = cpu->mem[addr + 1];
@@ -121,6 +223,10 @@
case AM_ZIX:
{
uint8_t zp = cpu->mem[cpu->pc++];
+
+ if (f & FETCH_NO_INDIRECTION)
+ return arg_imm(zp);
+
uint16_t addr = zp + cpu->regs[X];
uint16_t indirect = le_to_native(cpu->mem[addr], cpu->mem[addr + 1]);
return arg_ptr(cpu, f, indirect);
@@ -129,6 +235,10 @@
case AM_ZIY:
{
uint8_t zp = cpu->mem[cpu->pc++];
+
+ if (f & FETCH_NO_INDIRECTION)
+ return arg_imm(zp);
+
uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[zp + 1]);
return arg_ptr(cpu, f, base + cpu->regs[Y]);
}
@@ -199,7 +309,7 @@
void disas_step(cpu_t *cpu)
{
- printf("%x", cpu->pc);
+ printf("$%x", cpu->pc);
uint8_t op = cpu->mem[cpu->pc++];
switch (op)
{
@@ -214,12 +324,13 @@
#undef INST
default:
- die("Undefined opcode %x", op);
+ warn("\tUndefined opcode %x", op);
}
}
void disas(cpu_t *cpu)
{
+ // Raw binary, no way to know what's code what isn't
while (cpu->pc < 0xFFFF)
{
disas_step(cpu);