6502

minimal 6502 emulator written in C
Index Commits Files Refs README LICENSE
commit 7d1aca9563c3a1e587df714027db50c735c8d3f1
parent f5fb97645c1a7d2de0aef062f20a3f393fe5bfbc
Author: mjkloeckner <martin.cachari@gmail.com>
Date:   Sat, 23 Jul 2022 19:31:34 -0300

6502_functional_test.bin passed

All the logic of the cpu is fully implemented.

The code is full of printf's for debbuging purposes until a proper
system monitor is impemented.

The CPU now checks that an invalid instruction is not being executed, if
that is the case a msg gets printed and the CPU waits for user input;
the same occurs if the CPU catches a loop trap (like the ones that happen in the 6502_functional_test.bin)

In this commit ST_FLAG_MASK is no longer available since we could use only
the shift value

Now MEM_write returns the written value; this is useful in some function
that require the written value to set the CPU flags

CPU shifts Instructions gets modified since in previous commits this
weren't working; a lot of improvement can be done still since code gets
repeated sometimes

Diffstat:
M6502.c | 157++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
M6502.h | 34++++++++++++++--------------------
MMakefile | 3++-
Mmain.c | 27+++++++++++++--------------
4 files changed, 121 insertions(+), 100 deletions(-)
diff --git a/6502.c b/6502.c
@@ -3,7 +3,6 @@
 #include <stdio.h>
 
 /* included for debugging purposes */
-#include <unistd.h>
 #include <string.h>
 
 static CPU cpu;
@@ -12,28 +11,18 @@ static MEM mem; /* 8 bit word */
 static uint8_t cyc;
 static uint8_t opc;
 
-static uint16_t   tmp;
+static uint16_t  tmp;
 static uint16_t  addr_abs;
 static uint16_t  addr_rel;
 
 INS decode[0x100];
-
-static uint8_t ST_FLAG_MASK[] = {
-    (1 << 0),    /* Carry */
-    (1 << 1),    /* Zero  */
-    (1 << 2),    /* Disable interrupts  */
-    (1 << 3),    /* Decimal mode  */
-    (1 << 4),    /* Break */
-    (1 << 5),    /* Unused */
-    (1 << 6),    /* Overflow */
-    (1 << 7)     /* Negative */
-};
-
+static INS prev_ins;
 
 /* Cpu methods */
 void CPU_init(void) {
     cpu.ac = cpu.x = cpu.y = cpu.sp = cpu.st = 0;
     cyc = opc = tmp = 0;
+    prev_ins = (INS) {"???", &NUL, &IMP, 7};
 }
 
 void CPU_reset(void) {
@@ -41,7 +30,6 @@ void CPU_reset(void) {
     cpu.st = 0x00;
 
     /* little endian */
-    printf("pc: 0x%4X%4X\n", MEM_read(0xFFFD), MEM_read(0xFFFC));
     uint16_t lo = MEM_read(0xFFFC);
     uint16_t hi = MEM_read(0xFFFD);
 
@@ -60,15 +48,22 @@ void CPU_fetch(INS *ins) {
 uint8_t CPU_exec(INS ins) {
     /* simulate instructions cycles */
     do {
-        if(cyc == 0) {
-            cpu.pc++;
+        if (cyc == 0) {
+            if (!strcmp(ins.name, "???")) {
+                printf("EXEC: invalid instruction reached\n");
+                return 1;
+            } else if (!strcmp(ins.name, "BNE") && !(strcmp(prev_ins.name, "BNE"))) {
+                printf("EXEC: consecutive BNE reached\n");
+                return 1;
+            }
 
+            cpu.pc++;
             cyc = ins.cycles;
 
-            if(!strcmp(ins.name, "???")) return 1;
             uint8_t add_cyc_0 = ins.mode();
             uint8_t add_cyc_1 = ins.op();
 
+            prev_ins = ins;
             cyc += add_cyc_0 & add_cyc_1;
         }
     } while(--cyc);
@@ -128,11 +123,11 @@ void CPU_branch(void) {
 }
 
 void CPU_set_flag(ST_FLAG flag, uint8_t val) {
-    cpu.st = val ? cpu.st | ST_FLAG_MASK[flag] : cpu.st & ~(ST_FLAG_MASK[flag]);
+    cpu.st = val ? cpu.st | (1 << flag) : cpu.st & ~(1 << flag);
 }
 
 uint8_t CPU_get_flag(ST_FLAG flag) {
-    return ((cpu.st & ST_FLAG_MASK[flag]) >> flag);
+    return (cpu.st & (1 << flag)) >> flag;
 }
 
 void print_reg(uint8_t reg) {
@@ -151,7 +146,8 @@ void CPU_dump(void) {
     print_reg(cpu.st);
     printf("    y:   %02X (%3d)\n", cpu.y, cpu.y);
     printf("   sp:   %02X\n", cpu.sp);
-    printf("   pc: %04X -> %02X (%s)(%s)\n\n", cpu.pc, mem.ram[cpu.pc], aux.name, CPU_mode_name(aux.mode));
+    printf("   pc: %04X -> %02X (%s)(%s)\n", cpu.pc, mem.ram[cpu.pc], aux.name, CPU_mode_name(aux.mode));
+    printf("--------------------------------------------\n");
 }
 
 uint16_t CPU_get_pc(void) {
@@ -189,7 +185,7 @@ uint8_t MEM_read(uint16_t addr) {
 
 uint8_t MEM_write(uint16_t addr, uint8_t val) {
     mem.ram[addr] = val;
-    return 0;
+    return val;
 }
 
 void MEM_dump() {
@@ -240,6 +236,8 @@ uint8_t ABS(void) {
     uint16_t hi = MEM_read(cpu.pc++);
 
     addr_abs = (hi << 8) | lo;
+
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
@@ -252,6 +250,7 @@ uint8_t ABX(void) {
     printf("address fetched: %04X\nx register: %02X\nend address: %04X (%02X)\n", addr_abs, cpu.x, addr_abs + cpu.x, MEM_read(addr_abs + cpu.x));
 
     addr_abs += cpu.x;
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
 
     return ((addr_abs & 0x00FF) != (hi << 8)) ? 1 : 0;
 }
@@ -265,11 +264,13 @@ uint8_t ABY(void) {
     printf("address fetched: %04X\ny register: %02X\nend address: %04X (%02X)\n", addr_abs, cpu.y, addr_abs + cpu.y, MEM_read(addr_abs + cpu.y));
 
     addr_abs += cpu.y;
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return ((addr_abs & 0x00FF) != (hi << 8)) ? 1 : 0;
 }
 
 uint8_t IMM(void) {
     addr_abs = cpu.pc++;
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
@@ -290,6 +291,7 @@ uint8_t IND(void) {
      * and simulate page boundary hardware bug */
     addr_abs = (MEM_read((lo == 0xFF) ? (p & 0xFF00) : p + 1) << 8) | MEM_read(p);
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
@@ -302,6 +304,8 @@ uint8_t IZX(void) {
 
     addr_abs = ((hi << 8) & 0xFF00) | lo;
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
+
     return 0;
 }
 
@@ -315,13 +319,17 @@ uint8_t IZY(void) {
     addr_abs = ((hi << 8) & 0xFF00) | lo;
     addr_abs += cpu.y;
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
+
     return ((addr_abs & 0xFF00) != (hi << 8)) ? 1 : 0;
 }
 
 uint8_t REL(void) {
-    addr_rel = MEM_read(cpu.pc++);
+    addr_rel = (MEM_read(cpu.pc++) & 0x00FF);
+    printf("REL: offset: %02X (%d)\n", addr_rel, (int8_t)addr_rel);
 
-    if(addr_rel & (1 << 7))
+    /* signed byte */
+    if (addr_rel & (1 << 7))
         addr_rel |= 0xFF00;
 
     return 0;
@@ -329,20 +337,23 @@ uint8_t REL(void) {
 
 /* read only the offset of the zero page */
 uint8_t ZP0(void) {
-    addr_abs = MEM_read(cpu.pc++);
+    addr_abs = (MEM_read(cpu.pc++) & 0x00FF);
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
 uint8_t ZPX(void) {
     addr_abs = (MEM_read(cpu.pc++) + cpu.x) & 0x00FF;
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
 uint8_t ZPY(void) {
     addr_abs = (MEM_read(cpu.pc++) + cpu.y) & 0x00FF;
 
+    printf("Address: %04X -> (%02X)\n", addr_abs, MEM_read(addr_abs));
     return 0;
 }
 
@@ -355,7 +366,7 @@ uint8_t ADC(void) {
     CPU_set_flag(N, res & (1 << 7));
     CPU_set_flag(Z, (res & 0x00FF) == 0);
     CPU_set_flag(C, ((res & 0xFF00) >> 7));
-    CPU_set_flag(V, ((cpu.ac ^ res) & ~(res ^ data)) & (1 << 7));
+    CPU_set_flag(V, ((cpu.ac^res) & ~(cpu.ac^data)) & (1 << 7));
 
     cpu.ac = (uint8_t)(res & 0x00FF);
 
@@ -372,17 +383,20 @@ uint8_t AND(void) {
 }
 
 uint8_t ASL(void) {
-    tmp = (cpu.ac <<= 1);
+    if(decode[opc].mode == &IMP) {
+        tmp = cpu.ac << 1;
+        CPU_set_flag(C, (cpu.ac & (1 << 7)));
+        cpu.ac = (tmp & 0x00FF);
+    }
+    else {
+        tmp = MEM_read(addr_abs) << 1;
+        CPU_set_flag(C, MEM_read(addr_abs) & (1 << 7));
+        MEM_write(addr_abs, tmp & 0x00FF);
+    }
 
-    CPU_set_flag(C, (tmp & (1 << 8)) != 0);
     CPU_set_flag(Z, tmp == 0);
     CPU_set_flag(N, tmp & (1 << 7));
 
-    if(decode[opc].mode == &IMP)
-        cpu.ac = (tmp & 0x00FF);
-    else
-        MEM_write(addr_abs, tmp & 0x00FF);
-
     return 0;
 }
 
@@ -522,11 +536,10 @@ uint8_t CPY(void) {
 }
 
 uint8_t DEC(void) {
-    tmp = (MEM_read(addr_abs) - 1) & 0xFF;
-    MEM_write(addr_abs, tmp);
+    tmp = MEM_write(addr_abs, MEM_read(addr_abs) - 1) & 0x00FF;
 
     CPU_set_flag(Z, tmp == 0);
-    CPU_set_flag(N, tmp & 0xFF);
+    CPU_set_flag(N, tmp & (1 << 7));
 
     return 0;
 }
@@ -561,9 +574,7 @@ uint8_t EOR(void) {
 }
 
 uint8_t INC(void) {
-    tmp = MEM_read(addr_abs) + 1;
-
-    MEM_write(addr_abs, tmp);
+    tmp = MEM_write(addr_abs, MEM_read(addr_abs) + 1) & 0x00FF;
 
     CPU_set_flag(Z, tmp == 0);
     CPU_set_flag(N, tmp & (1 << 7));
@@ -633,17 +644,20 @@ uint8_t LDY(void) {
 }
 
 uint8_t LSR(void) {
-    tmp = (cpu.ac >>= 1);
+    if(decode[opc].mode == &IMP) {
+        tmp = (cpu.ac >> 1);
+        CPU_set_flag(C, cpu.ac & 0x01);
+        cpu.ac = (tmp & 0x00FF);
+    }
+    else {
+        tmp = (MEM_read(addr_abs) >> 1);
+        CPU_set_flag(C, MEM_read(addr_abs) & 0x01);
+        MEM_write(addr_abs, tmp & 0x00FF);
+    }
 
-    CPU_set_flag(C, (tmp & (1 << 8)) != 0);
     CPU_set_flag(Z, tmp == 0);
     CPU_set_flag(N, tmp & (1 << 7));
 
-    if(decode[opc].mode == &IMP)
-        cpu.ac = (tmp & 0x00FF);
-    else
-        MEM_write(addr_abs, tmp & 0x00FF);
-
     return 0;
 }
 
@@ -696,33 +710,46 @@ uint8_t PLP(void) {
 }
 
 uint8_t ROL(void) {
-    tmp = (cpu.ac <<= 1);
-    tmp = (tmp & 0xFF00) ? (tmp | 0x01) : (tmp & ~(0x01));
-
-    CPU_set_flag(Z, tmp == 0);
-    CPU_set_flag(N, tmp & (1 << 7));
-    CPU_set_flag(C, (tmp & (1 << 8)) != 0);
+    if(decode[opc].mode == &IMP) {
+        tmp = (cpu.ac << 1) & 0xFE;
+        tmp |= CPU_get_flag(C);
 
-    if(decode[opc].mode == &IMP)
+        CPU_set_flag(C, cpu.ac & (1 << 7));
         cpu.ac = (tmp & 0x00FF);
-    else
+    }
+    else {
+        tmp = (MEM_read(addr_abs) << 1) & 0xFE;
+        tmp |= CPU_get_flag(C);
+
+        CPU_set_flag(C, MEM_read(addr_abs) & (1 << 7));
         MEM_write(addr_abs, tmp & 0x00FF);
+    }
+
+    CPU_set_flag(Z, tmp == 0);
+    CPU_set_flag(N, tmp & (1 << 7));
 
     return 0;
 }
 
 uint8_t ROR(void) {
-    tmp = (cpu.ac <<= 1);
-    tmp = (tmp & 0xFF00) ? (tmp | 0x01) : (tmp & ~(0x01));
+    if(decode[opc].mode == &IMP) {
+        tmp = (cpu.ac >> 1) & 0x7F;
+        tmp |= (CPU_get_flag(C) << 7);
 
-    CPU_set_flag(Z, tmp == 0);
-    CPU_set_flag(N, tmp & (1 << 7));
-    CPU_set_flag(C, (tmp & (1 << 8)) != 0);
+        CPU_set_flag(C, cpu.ac & 0x01);
 
-    if(decode[opc].mode == &IMP)
         cpu.ac = (tmp & 0x00FF);
-    else
+    }
+    else {
+        tmp = (MEM_read(addr_abs) >> 1) & 0x7F;
+        tmp |= (CPU_get_flag(C) << 7);
+
+        CPU_set_flag(C, MEM_read(addr_abs) & 1);
         MEM_write(addr_abs, tmp & 0x00FF);
+    }
+
+    CPU_set_flag(Z, tmp == 0);
+    CPU_set_flag(N, tmp & (1 << 7));
 
     return 0;
 }
@@ -757,13 +784,13 @@ uint8_t RTS(void) {
 }
 
 uint8_t SBC(void) {
-    uint8_t data = MEM_read(addr_abs) ^ 0xFF;
+    uint16_t data = MEM_read(addr_abs) ^ 0xFF;
     uint16_t res = (cpu.ac + data + (uint16_t)CPU_get_flag(C));
 
-    CPU_set_flag(Z, cpu.ac == 0);
-    CPU_set_flag(N, cpu.ac & (1 << 7));
+    CPU_set_flag(N, res & (1 << 7));
+    CPU_set_flag(Z, (res & 0x00FF) == 0);
     CPU_set_flag(C, ((res & 0xFF00) >> 7));
-    CPU_set_flag(V, ((cpu.ac^data) & ~(cpu.ac^data)) & (1 << 7));
+    CPU_set_flag(V, ((cpu.ac^res) & ~(cpu.ac^data)) & (1 << 7));
 
     cpu.ac = (uint8_t)(res & 0x00FF);
 
diff --git a/6502.h b/6502.h
@@ -1,5 +1,5 @@
-#ifndef __6502_H__
-#define __6502_H__
+#ifndef _6502_H
+#define _6502_H
 
 #include <stdint.h>
 
@@ -16,7 +16,6 @@ typedef struct {
     uint16_t pc;
 } CPU;
 
-
 /* zero-page: fast shorter instructions
  * first-page: stack;
  * last-six bytes: used to store special addresses */
@@ -36,37 +35,32 @@ typedef enum {
     N     /* Negative */
 } ST_FLAG;
 
-void CPU_init(void);
-void CPU_reset(void);
-void CPU_fetch(INS *ins);
+char *CPU_mode_name(uint8_t (*mode)(void));
+uint16_t CPU_get_pc(void);
 uint8_t CPU_exec(INS ins);
-
+uint8_t CPU_get_flag(ST_FLAG flag);
+void CPU_branch(void);
+void CPU_dump(void);
+void CPU_fetch(INS *ins);
+void CPU_init(void);
 void CPU_irq(void);
 void CPU_nm_irq(void);
+void CPU_reset(void);
 void CPU_set_flag(ST_FLAG flag, uint8_t val);
-uint8_t CPU_get_flag(ST_FLAG flag);
-
-void CPU_dump(void);
-char *CPU_mode_name(uint8_t (*mode)(void));
-uint16_t CPU_get_pc(void);
-
-/* This should not be called from outside of file 6502.c */
-void CPU_branch(void);
-void print_reg(uint8_t);
-
-
 
 void MEM_init(void);
 void MEM_set_pc_start(uint16_t addr);
 uint8_t MEM_read(uint16_t addr);
-uint8_t MEM_write(uint16_t addr, uint8_t val);
-
+uint8_t MEM_write(uint16_t addr, uint8_t val); /* returns written value */
 void MEM_dump(void);
 void MEM_dump_page(uint16_t page);
 void MEM_dump_last_six(void);
 void MEM_load_from_file(char *fp);
 
 
+void print_reg(uint8_t);
+
+
 /*  Addresing modes  */
 uint8_t ABS(void);
 uint8_t ABX(void);
diff --git a/Makefile b/Makefile
@@ -1,7 +1,8 @@
 CC := gcc
 CLIBS :=
 CFLAGS := -Wall -Wshadow -pedantic -ansi -std=c99 -O3
-SRCS := $(wildcard *.c)
+# SRCS := $(wildcard *.c)
+SRCS := $(shell find ./ ! -name "shift.c" -name "*.c")
 OBJS := $(SRCS:.c=.o)
 
 TARGET := 6502
diff --git a/main.c b/main.c
@@ -4,54 +4,53 @@
 #include <stdlib.h>
 #include <stdbool.h>
 
-/* #define INPUT_FILE_PATH    "10times3.bin" */
 #define INPUT_FILE_PATH    "6502_functional_test.bin"
 
-/* TODO: Finish with CPU instructions implementation */
-/* TODO: add support for command line arguments */
-/* TODO: add proper system monitor (memory dump, cpu registers) */
+/* TODO: Add support for command line arguments */
+/* TODO: Add proper system monitor (memory dump, cpu registers) */
 
 static bool brk = false;
 
 void CPU_brk(uint16_t pc) {
     if(CPU_get_pc() == pc) {
+        printf("BRK: %2X reached\n", pc);
         brk = true;
     }
 }
 
-
 int main (void) {
     INS ins;
 
     /* Initialize memory to 0 */
     MEM_init();
 
-    /* set the first address that the pc should be set to */
-    MEM_set_pc_start(0x0400);
-
     /* Load program to memory */
     MEM_load_from_file(INPUT_FILE_PATH);
 
+    /* set the first address that the pc should be set to */
+    MEM_set_pc_start(0x0400);
+
     /* Initialize cpu registers to 0 */
     CPU_init();
 
     CPU_reset();
 
+    int c = 0;
     do {
+        if(c == 'c') brk = false;
+        CPU_brk(0x336d);
+
         /* Fetch an instruction and increment pc */
         CPU_fetch(&ins);
 
         CPU_dump();
-        MEM_dump_last_six();
+        /* MEM_dump_last_six(); */
         /* MEM_dump_page(0x0000); */
         /* MEM_dump_page(0x0100); */
 
         /* Execute instruction */
-        if(CPU_exec(ins) != 0) return 1;
-
-        CPU_brk(0x22D8);
-        printf("--------------------------------------------\n");
-    } while(brk ? (getchar() != 'q') : true);
+        if(CPU_exec(ins) != 0) brk = true;
+    } while(brk ? ((c = getchar()) != 'q') : true);
 
     return 0;
 }