Add run(), most instructions
diff --git a/README.md b/README.md
index 11e1de0..7fc2fec 100644
--- a/README.md
+++ b/README.md
@@ -7,4 +7,23 @@
The `instructions.h` header is generated from `6502.csv` and contains
definitions of every 6502 opcode, its mnemonic and addressing mode.
-It is built automatically by cmake.
\ No newline at end of file
+It is built automatically by cmake.
+
+
+```
+ ____________________________________
+/ On the subject of C program \
+| indentation: "In My Egotistical |
+| Opinion, most people's C programs |
+| should be indented |
+| six feet downward and covered with |
+| dirt." |
+| |
+\ -- Blair P. Houghton /
+ ------------------------------------
+ \ ^__^
+ \ (oo)\_______
+ (__)\ )\/\
+ ||----w |
+ || ||
+```
diff --git a/cpu.c b/cpu.c
index fe5cf6a..5a7c391 100644
--- a/cpu.c
+++ b/cpu.c
@@ -30,21 +30,69 @@
return cpu;
}
+uint16_t le_to_native(uint8_t a, uint8_t b)
+{
+#ifdef LITTLE_ENDIAN
+ return b << 8 | a;
+#else
+ return a << 8 | b;
+#endif
+}
+
+void native_to_le(uint16_t n, uint8_t *a, uint8_t *b)
+{
+#ifdef LITTLE_ENDIAN
+ *a = n >> 8;
+ *b = n & 0xFF;
+#else
+ *a = n & 0xFF;
+ *b = n >> 8;
+#endif
+}
+
void stack_push(cpu_t *cpu, uint8_t v)
{
cpu->mem[cpu->regs[SP]-- + 0x100] = v;
}
+void stack_pushle(cpu_t *cpu, uint16_t v)
+{
+ uint8_t a, b;
+ native_to_le(v, &a, &b);
+ // push in "reverse" order so that the address is stored as LE
+ stack_push(cpu, b);
+ stack_push(cpu, a);
+}
+
uint8_t stack_pop(cpu_t *cpu)
{
return cpu->mem[cpu->regs[SP]++ + 0x100];
}
+uint16_t stack_pople(cpu_t *cpu)
+{
+ uint8_t a = stack_pop(cpu);
+ uint8_t b = stack_pop(cpu);
+ return le_to_native(a, b);
+}
+
void free_cpu(cpu_t *cpu)
{
free(cpu->mem);
}
+// rotate right
+uint8_t ror(uint8_t a, uint8_t n)
+{
+ return (a >> n) | (a << (8 - n));
+}
+
+// rotate left
+uint8_t rol(uint8_t a, uint8_t n)
+{
+ return (a << n) | (a >> (8 - n));
+}
+
void stat_nz(cpu_t *cpu, int8_t v)
{
cpu->status.negative = v < 0;
@@ -63,7 +111,27 @@
cpu->status.carry = c < a || c < b;
}
-void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a)
+void cmp(cpu_t *cpu, uint8_t reg, uint8_t mem)
+{
+ cpu->status.negative = 0;
+ cpu->status.zero = 0;
+ cpu->status.carry = 0;
+ if (cpu->regs[reg] < mem)
+ {
+ cpu->status.negative = 1;
+ }
+ else if (cpu->regs[reg] == mem)
+ {
+ cpu->status.zero = 1;
+ cpu->status.carry = 1;
+ }
+ else
+ {
+ cpu->status.carry = 1;
+ }
+}
+
+void execute(cpu_t *cpu, const char *mnemonic, uint8_t op, arg_t a, uint8_t am)
{
// used to save space
#define REGS \
@@ -131,19 +199,205 @@
cpu->mem[a.ptr]--;
stat_nz(cpu, cpu->mem[a.ptr]);
break;
+
+ case DEX:
+ cpu->regs[X]--;
+ stat_nz(cpu, cpu->regs[X]);
+ break;
+
+ case DEY:
+ cpu->regs[Y]--;
+ stat_nz(cpu, cpu->regs[Y]);
+ break;
+
+ case ASL:
+ // This check must be done here unfortunately, it would be nice
+ // to do this while decoding operands but it would require
+ // a substantial change to the architecture of the emulator
+ if (am == AM_ACC)
+ {
+ cpu->status.carry = cpu->regs[A] >> 7;
+ cpu->regs[A] <<= 1;
+ stat_nz(cpu, cpu->regs[A]);
+ }
+ else
+ {
+ cpu->status.carry = cpu->mem[a.val] >> 7;
+ cpu->mem[a.ptr] <<= 1;
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ }
+ break;
+
+ case LSR:
+ if (am == AM_ACC)
+ {
+ cpu->status.carry = cpu->regs[A] & 1;
+ cpu->regs[A] >>= 1;
+ stat_nz(cpu, cpu->regs[A]);
+ }
+ else
+ {
+ cpu->status.carry = cpu->mem[a.val] & 7;
+ cpu->mem[a.ptr] >>= 1;
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ }
+ break;
+
+ case ROL:
+ if (am == AM_ACC)
+ {
+ cpu->status.carry = cpu->regs[A] >> 7;
+ cpu->regs[A] = rol(cpu->regs[A], 1);
+ stat_nz(cpu, cpu->regs[A]);
+ }
+ else
+ {
+ cpu->status.carry = cpu->mem[a.val] >> 7;
+ cpu->mem[a.ptr] = rol(a.val, 1);
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ }
+ break;
+
+ case ROR:
+ if (am == AM_ACC)
+ {
+ cpu->status.carry = cpu->regs[A] & 1;
+ cpu->regs[A] = ror(cpu->regs[A], 1);
+ stat_nz(cpu, cpu->regs[A]);
+ }
+ else
+ {
+ cpu->status.carry = cpu->mem[a.val] & 1;
+ cpu->mem[a.ptr] = ror(a.val, 1);
+ stat_nz(cpu, cpu->mem[a.ptr]);
+ }
+ break;
+
+ case AND:
+ cpu->regs[A] &= a.val;
+ stat_nz(cpu, cpu->regs[A]);
+ break;
+
+ case ORA:
+ cpu->regs[A] |= a.val;
+ stat_nz(cpu, cpu->regs[A]);
+ break;
+
+ case EOR:
+ cpu->regs[A] ^= a.val;
+ stat_nz(cpu, cpu->regs[A]);
+ break;
+
+ case CMP:
+ cmp(cpu, A, a.val);
+ break;
+
+ case CPX:
+ cmp(cpu, X, a.val);
+ break;
+
+ case CPY:
+ cmp(cpu, Y, a.val);
+ break;
+
+ // TODO: implement BIT here
+
+ #define BRANCHES \
+ B(BCC, carry == 0) \
+ B(BCS, carry == 1) \
+ B(BNE, zero == 0) \
+ B(BEQ, zero == 1) \
+ B(BPL, negative == 0) \
+ B(BMI, negative == 1) \
+ B(BVC, overflow == 0) \
+ B(BVS, overflow == 1)
+
+ #define B(i, c) \
+ case i: \
+ if (cpu->status . c) \
+ cpu->pc = a.ptr;\
+ break;
+
+ BRANCHES
+
+ #undef B
+ #undef BRANCHES
+
+ #define TRANSFERS \
+ T(A, X) \
+ T(X, A) \
+ T(A, Y) \
+ T(Y, A)
+
+ #define T(a, b) \
+ case T ## a ## b: \
+ cpu->regs[b] = cpu->regs[a]; \
+ stat_nz(cpu, cpu->regs[b]); \
+ break;
+
+ TRANSFERS
+
+ #undef T
+ #undef TRANSFERS
+
+ case TSX:
+ cpu->regs[X] = cpu->regs[SP];
+ stat_nz(cpu, cpu->regs[X]);
+ break;
+
+ case TXS:
+ cpu->regs[SP] = cpu->regs[X];
+ stat_nz(cpu, cpu->regs[X]);
+ break;
+
+ case PHA:
+ stack_push(cpu, cpu->regs[A]);
+ break;
+
+ case PLA:
+ cpu->regs[A] = stack_pop(cpu);
+ stat_nz(cpu, cpu->regs[A]);
+ break;
+
+ case PHP:
+ stack_push(cpu, *(uint8_t *)(&cpu->status));
+ break;
+
+ case PLP:
+ {
+ uint8_t s = stack_pop(cpu);
+ *(uint8_t *)(&cpu->status) = s;
+ }
+
+ case JMP:
+ cpu->pc = a.ptr;
+ break;
+
+ case JSR:
+ stack_pushle(cpu, cpu->pc);
+ break;
+
+ case RTS:
+ cpu->pc = stack_pople(cpu);
+ break;
+
+ // TODO: implement RTI
+ // TODO: implement flag instructions
+
+ case BRK:
+ // TODO: trigger an interrupt
+ cpu->running = false;
+ break;
+
+ case NOP:
+ break;
+
+ default:
+ die("Unsupported opcode: %x\n", op);
}
#undef REGS
}
-uint16_t le_to_native(uint8_t a, uint8_t b)
-{
-#ifdef LITTLE_ENDIAN
- return b << 8 | a;
-#else
- return a << 8 | b;
-#endif
-}
-
uint16_t fetch_le(cpu_t *cpu)
{
uint8_t a = cpu->mem[cpu->pc++];
@@ -255,7 +509,7 @@
{
#define INST(mn, am, op) \
case op: \
- execute(cpu, #mn, mn, fetch_addr(cpu, am, 0)); \
+ execute(cpu, #mn, mn, fetch_addr(cpu, am, 0), am); \
break;
INSTRUCTIONS
@@ -328,6 +582,14 @@
}
}
+void disas_num(cpu_t *cpu, uint16_t num)
+{
+ for (int i = 0; i < num; i++)
+ {
+ disas_step(cpu);
+ }
+}
+
void disas(cpu_t *cpu)
{
// Raw binary, no way to know what's code what isn't
@@ -336,3 +598,12 @@
disas_step(cpu);
}
}
+void run(cpu_t *cpu)
+{
+ while (cpu->running)
+ {
+ step(cpu);
+ }
+
+ printf("CPU Halted\n");
+}
diff --git a/cpu.h b/cpu.h
index 651f97f..888972c 100644
--- a/cpu.h
+++ b/cpu.h
@@ -138,4 +138,8 @@
void step(cpu_t *cpu);
void free_cpu(cpu_t *cpu);
void die(const char *message);
+// IMPORTANT: all disassembly functions mess with the PC
void disas(cpu_t *cpu);
+void disas_num(cpu_t *cpu, uint16_t num);
+void disas_step(cpu_t *cpu);
+void run(cpu_t *cpu);
diff --git a/main.c b/main.c
index 5ab918d..888168c 100644
--- a/main.c
+++ b/main.c
@@ -9,17 +9,19 @@
int main(int argc, char **argv)
{
- bool disflag = 0,
- runflag = 0,
- helpflag = 0,
- debugflag = 0,
- should_read = 0;
+ bool disflag = false,
+ runflag = false,
+ helpflag = false,
+ debugflag = false,
+ should_read = false;
+
+ int disasm_len = 0;
FILE *input = stdin;
char c;
- while ((c = getopt(argc, argv, "Ddrhi:")) != -1)
+ while ((c = getopt(argc, argv, "Ddrhi:n:")) != -1)
{
switch (c)
{
@@ -38,6 +40,9 @@
case 'i':
input = fopen(optarg, "r");
break;
+ case 'n':
+ disasm_len = atoi(optarg);
+ break;
case 'h':
case '?':
helpflag = 1;
@@ -53,6 +58,7 @@
" -r run input\n"
" -D debug input (open debug prompt)\n"
" -i <input> set input file, defaults to standard input\n"
+ " -n <number> number of instructions to disassemble, 0 for all\n"
" -h, -? show this help page\n");
return 0;
}
@@ -64,11 +70,20 @@
cpu = new_cpu();
fread(cpu.mem, 0xFFFF, 1, input);
}
+ else
+ {
+ puts("6502 toolchain by swissChili <swisschili.sh>");
+ printf("%s -h for help\n", argv[0]);
+ }
if (disflag)
{
disas(&cpu);
}
+ else if (runflag)
+ {
+ run(&cpu);
+ }
else if (debugflag)
{
debug(&cpu);
diff --git a/test.dat b/test.dat
index d5569e0..310bd9e 100644
--- a/test.dat
+++ b/test.dat
Binary files differ