summaryrefslogtreecommitdiff
path: root/6502
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
committerLLLL Colonq <llll@colonq>2026-07-09 23:51:55 -0400
commit2bdcaf319b1d74ffbaccf08a58336f804761beab (patch)
treec21de6df74ec79b5574ad2b89fcc275d10847802 /6502
Refactor into monorepo
Diffstat (limited to '6502')
-rw-r--r--6502/.gitignore1
-rw-r--r--6502/Makefile63
-rw-r--r--6502/include/lcq/6502.h37
-rw-r--r--6502/include/lcq/6502/ins.h99
-rw-r--r--6502/include/lcq/6502/types.h17
-rw-r--r--6502/packages.nix11
-rw-r--r--6502/src/6502.c247
-rw-r--r--6502/src/main.c6
8 files changed, 481 insertions, 0 deletions
diff --git a/6502/.gitignore b/6502/.gitignore
new file mode 100644
index 0000000..13a055e
--- /dev/null
+++ b/6502/.gitignore
@@ -0,0 +1 @@
+./6502 \ No newline at end of file
diff --git a/6502/Makefile b/6502/Makefile
new file mode 100644
index 0000000..c8119f3
--- /dev/null
+++ b/6502/Makefile
@@ -0,0 +1,63 @@
+CC ?= gcc
+AR ?= ar
+override CPPFLAGS += -MMD -MP
+override CFLAGS += -fPIC --std=c23 -g -Ideps/ -Isrc/ -Iinclude/ -Wall -Wextra -Wpedantic -Wconversion -Wformat-security -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wfloat-equal -Wundef -Wpointer-arith -Wbad-function-cast -Wlogical-op -Wmissing-braces -Wcast-align -Wstrict-overflow=5 -ftrapv
+override LDFLAGS += -g -static
+
+BUILD = build_$(CC)
+
+SRCS := src/main.c src/6502.c
+CHK_SOURCES ?= $(SRCS)
+OBJECTS := $(SRCS:src/%.c=$(BUILD)/%.o)
+EXE ?= 6502
+LIB := libcolonq-6502.a
+
+prefix ?= /usr/local
+exec_prefix ?= $(prefix)
+bindir ?= $(exec_prefix)/bin
+includedir ?= $(prefix)/include
+libdir ?= $(exec_prefix)/lib
+
+.PHONY: all clean install install-bin install-headers install-lib check-syntax
+
+all: $(EXE) $(LIB)
+
+$(EXE): $(BUILD)/main.o $(LIB)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+$(LIB): $(OBJECTS)
+ $(AR) rcs $@ $^
+
+$(BUILD):
+ mkdir $(BUILD)/
+
+$(BUILD)/%.o: src/%.c | $(BUILD)
+ $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+
+clean:
+ -rm $(EXE)
+ -rm $(LIB)
+ -rm -r $(BUILD)/
+
+TAGS: $(SRCS)
+ ctags --output-format=etags $^
+
+install: install-bin install-headers install-lib
+
+install-bin: $(EXE)
+ mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir)
+ install $(EXE) $(DESTDIR)$(bindir)/$(EXE)
+
+install-headers:
+ mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir)
+ cp -r include/* $(DESTDIR)$(includedir)
+
+install-lib: $(LIB)
+ mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir)
+ install $(LIB) $(DESTDIR)$(libdir)/$(LIB)
+
+check-syntax: TAGS
+ gcc $(CFLAGS) -fsyntax-only $(CHK_SOURCES)
+
+-include $(BUILD)/main.d
+-include $(OBJECTS:.o=.d)
diff --git a/6502/include/lcq/6502.h b/6502/include/lcq/6502.h
new file mode 100644
index 0000000..cb3b1fa
--- /dev/null
+++ b/6502/include/lcq/6502.h
@@ -0,0 +1,37 @@
+#ifndef LCOLONQ_6502_H
+#define LCOLONQ_6502_H
+
+#include <lcq/6502/types.h>
+
+typedef enum {
+ L6502_FLAG_CARRY = 1 << 1,
+ L6502_FLAG_ZERO = 1 << 2,
+ L6502_FLAG_INTERRUPT_DISABLE = 1 << 3,
+ L6502_FLAG_DECIMAL = 1 << 4,
+ L6502_FLAG_BREAK_COMMAND = 1 << 5,
+ L6502_FLAG_OVERFLOW = 1 << 6,
+ L6502_FLAG_NEGATIVE = 1 << 7,
+} l6502_flag;
+
+typedef struct {
+ u16 pc;
+ u8 sp, a, x, y, flags;
+} l6502_regs;
+
+typedef u8 (*l6502_mem_read_handler)(void *data, u16 addr);
+typedef void (*l6502_mem_write_handler)(void *data, u16 addr, u8 val);
+typedef struct {
+ l6502_mem_read_handler rh;
+ l6502_mem_write_handler wh;
+ void *data;
+} l6502_mem;
+void l6502_mem_basic_new(l6502_mem *m);
+
+typedef struct {
+ l6502_regs regs;
+ l6502_mem mem;
+} l6502_emu;
+
+void l6502_emulate(l6502_emu *e);
+
+#endif
diff --git a/6502/include/lcq/6502/ins.h b/6502/include/lcq/6502/ins.h
new file mode 100644
index 0000000..88eb3a8
--- /dev/null
+++ b/6502/include/lcq/6502/ins.h
@@ -0,0 +1,99 @@
+#ifndef LCOLONQ_6502_INS_H
+#define LCOLONQ_6502_INS_H
+
+typedef enum {
+ BRK = 0x00,
+ PHP = 0x08,
+ CLC = 0x18,
+ JSR = 0x20,
+ PLP = 0x28,
+ SEC = 0x38,
+ RTI = 0x40,
+ PHA = 0x48,
+ JMP_ABSOLUTE = 0x4c,
+ CLI = 0x58,
+ RTS = 0x60,
+ PLA = 0x68,
+ JMP_INDIRECT = 0x6c,
+ SEI = 0x78,
+ DEY = 0x88,
+ TXA = 0x8a,
+ TYA = 0x98,
+ TXS = 0x9a,
+ TAY = 0xa8,
+ TAX = 0xaa,
+ CLV = 0xb8,
+ TSX = 0xba,
+ INY = 0xc8,
+ DEX = 0xca,
+ CLD = 0xd8,
+ INX = 0xe8,
+ NOP = 0xea,
+ SED = 0xf8,
+} l6502_ins_misc;
+
+typedef enum {
+ G1 = 0b01,
+ G2 = 0b10,
+ G3 = 0b00,
+} l6502_ins_group;
+
+typedef enum {
+ G1_OPCODE_ORA = 0b000,
+ G1_OPCODE_AND = 0b001,
+ G1_OPCODE_EOR = 0b010,
+ G1_OPCODE_ADC = 0b011,
+ G1_OPCODE_STA = 0b100,
+ G1_OPCODE_LDA = 0b101,
+ G1_OPCODE_CMP = 0b110,
+ G1_OPCODE_SBC = 0b111,
+} l6502_ins_group_one_opcode;
+
+typedef enum {
+ G1_ADDRESSING_MODE_INDIRECT_ZEROPAGE_X = 0b000,
+ G1_ADDRESSING_MODE_ZEROPAGE = 0b001,
+ G1_ADDRESSING_MODE_IMMEDIATE = 0b010,
+ G1_ADDRESSING_MODE_ABSOLUTE = 0b011,
+ G1_ADDRESSING_MODE_INDIRECT_ZEROPAGE_Y = 0b100,
+ G1_ADDRESSING_MODE_ZEROPAGE_X = 0b101,
+ G1_ADDRESSING_MODE_ABSOLUTE_Y = 0b110,
+ G1_ADDRESSING_MODE_ABSOLUTE_X = 0b111,
+} l6502_ins_group_one_addressing_mode;
+
+typedef enum {
+ G2_OPCODE_ASL = 0b000,
+ G2_OPCODE_ROL = 0b001,
+ G2_OPCODE_LSR = 0b010,
+ G2_OPCODE_ROR = 0b011,
+ G2_OPCODE_STX = 0b100,
+ G2_OPCODE_LDX = 0b101,
+ G2_OPCODE_DEC = 0b110,
+ G2_OPCODE_INC = 0b111,
+} l6502_ins_group_two_opcode;
+
+typedef enum {
+ G2_ADDRESSING_MODE_IMMEDIATE = 0b000,
+ G2_ADDRESSING_MODE_ZEROPAGE = 0b001,
+ G2_ADDRESSING_MODE_ACCUMULATOR = 0b010,
+ G2_ADDRESSING_MODE_ABSOLUTE = 0b011,
+ G2_ADDRESSING_MODE_ZEROPAGE_X = 0b101,
+ G2_ADDRESSING_MODE_ABSOLUTE_X = 0b111,
+} l6502_ins_group_two_addressing_mode;
+
+typedef enum {
+ G3_OPCODE_BIT = 0b001,
+ G3_OPCODE_STY = 0b100,
+ G3_OPCODE_LDY = 0b101,
+ G3_OPCODE_CPY = 0b110,
+ G3_OPCODE_CPX = 0b111,
+} l6502_ins_group_three_opcode;
+
+typedef enum {
+ G3_ADDRESSING_MODE_IMMEDIATE = 0b000,
+ G3_ADDRESSING_MODE_ZEROPAGE = 0b001,
+ G3_ADDRESSING_MODE_ABSOLUTE = 0b011,
+ G3_ADDRESSING_MODE_ZEROPAGE_X = 0b101,
+ G3_ADDRESSING_MODE_ABSOLUTE_X = 0b111,
+} l6502_ins_group_three_addressing_mode;
+
+#endif
diff --git a/6502/include/lcq/6502/types.h b/6502/include/lcq/6502/types.h
new file mode 100644
index 0000000..44ce546
--- /dev/null
+++ b/6502/include/lcq/6502/types.h
@@ -0,0 +1,17 @@
+#ifndef LCOLONQ_6502_TYPES_H
+#define LCOLONQ_6502_TYPES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#endif
diff --git a/6502/packages.nix b/6502/packages.nix
new file mode 100644
index 0000000..6ce8162
--- /dev/null
+++ b/6502/packages.nix
@@ -0,0 +1,11 @@
+pkgs: lcq: {
+ native = pkgs.stdenv.mkDerivation {
+ pname = "libcolonq-6502";
+ version = "git";
+ src = ./.;
+ hardeningDisable = ["all"];
+ installPhase = ''
+ make prefix=$out install
+ '';
+ };
+}
diff --git a/6502/src/6502.c b/6502/src/6502.c
new file mode 100644
index 0000000..c9b802d
--- /dev/null
+++ b/6502/src/6502.c
@@ -0,0 +1,247 @@
+#include <lcq/6502.h>
+#include <lcq/6502/ins.h>
+
+#include <stdlib.h>
+
+static u8 basic_read_handler(void *data, u16 addr) {
+ return ((u8 *) data)[addr];
+}
+static void basic_write_handler(void *data, u16 addr, u8 val) {
+ ((u8 *) data)[addr] = val;
+}
+void l6502_mem_basic_new(l6502_mem *m) {
+ m->rh = basic_read_handler;
+ m->wh = basic_write_handler;
+ m->data = calloc(1 << 16, sizeof(u8));
+}
+
+static u8 l6502_flag_get(l6502_emu *e, l6502_flag f) { return (e->regs.flags & (u8) f) != 0; }
+static void l6502_flag_set(l6502_emu *e, l6502_flag f, u8 v) {
+ if (v) e->regs.flags |= (u8) f;
+ else e->regs.flags &= ~((u8) f);
+}
+static void l6502_flags_check_zn(l6502_emu *e, u8 v) {
+ l6502_flag_set(e, L6502_FLAG_ZERO, v == 0);
+ l6502_flag_set(e, L6502_FLAG_NEGATIVE, (v >> 7) & 0b1);
+}
+
+static u8 l6502_read(l6502_emu *e, u16 addr) { return e->mem.rh(e->mem.data, addr); }
+static void l6502_write(l6502_emu *e, u16 addr, u8 val) { e->mem.wh(e->mem.data, addr, val); }
+static u16 l6502_read_addr(l6502_emu *e, u16 addr) {
+ u8 alo = l6502_read(e, addr);
+ u8 ahi = l6502_read(e, addr + 1);
+ return ahi << 8 | alo;
+}
+
+static u8 l6502_pop(l6502_emu *e) { return l6502_read(e, 0x0100 + (u16) ++e->regs.sp); }
+static void l6502_push(l6502_emu *e, u8 val) { l6502_write(e, 0x0100 + (u16) e->regs.sp--, val); }
+static u16 l6502_pop_addr(l6502_emu *e) {
+ u8 ahi = l6502_pop(e);
+ u8 alo = l6502_pop(e);
+ return ahi << 8 | alo;
+}
+static void l6502_push_addr(l6502_emu *e, u16 addr) {
+ l6502_push(e, (u8) (addr & 0xff));
+ l6502_push(e, (u8) (addr >> 8));
+}
+
+static u16 l6502_addressing_mode_immediate(l6502_emu *e) {
+ return e->regs.pc++;
+}
+static u16 l6502_addressing_mode_absolute(l6502_emu *e) {
+ u16 addr = l6502_read_addr(e, e->regs.pc);
+ e->regs.pc += 2;
+ return addr;
+}
+static u16 l6502_addressing_mode_absolute_x(l6502_emu *e) {
+ u16 addr = l6502_read_addr(e, e->regs.pc) + (u16) e->regs.x;
+ e->regs.pc += 2;
+ return addr;
+}
+static u16 l6502_addressing_mode_absolute_y(l6502_emu *e) {
+ u16 addr = l6502_read_addr(e, e->regs.pc) + (u16) e->regs.y;
+ e->regs.pc += 2;
+ return addr;
+}
+static u16 l6502_addressing_mode_zeropage(l6502_emu *e) {
+ return (u16) l6502_read(e, e->regs.pc++);
+}
+static u16 l6502_addressing_mode_zeropage_x(l6502_emu *e) {
+ return (u16) (l6502_read(e, e->regs.pc++) + e->regs.x);
+}
+static u16 l6502_addressing_mode_indirect_zeropage_x(l6502_emu *e) {
+ return l6502_read_addr(e, l6502_read(e, e->regs.pc++) + e->regs.x);
+}
+static u16 l6502_addressing_mode_indirect_zeropage_y(l6502_emu *e) {
+ return l6502_read_addr(e, l6502_read(e, e->regs.pc++)) + (u16) e->regs.y;
+}
+
+void l6502_emulate(l6502_emu *e) {
+ u8 ins = l6502_read(e, e->regs.pc++);
+ u8 val = 0;
+ u16 res = 0;
+ bool set_flags = true;
+ bool set_result = true;
+ u8 *set_reg = NULL;
+ u16 addr = 0;
+ switch (ins) { /* identify some special-case instructions */
+ case BRK:
+ l6502_push_addr(e, e->regs.pc);
+ l6502_push(e, e->regs.flags);
+ e->regs.pc = l6502_read_addr(e, 0xfffe);
+ l6502_flag_set(e, L6502_FLAG_BREAK_COMMAND, true);
+ break;
+ case PHP: l6502_push(e, e->regs.flags); break;
+ case CLC: l6502_flag_set(e, L6502_FLAG_CARRY, false); break;
+ case JSR:
+ addr = l6502_read_addr(e, e->regs.pc);
+ l6502_push_addr(e, e->regs.pc + 1);
+ e->regs.pc = addr;
+ break;
+ case PLP: e->regs.flags = l6502_pop(e); break;
+ case SEC: l6502_flag_set(e, L6502_FLAG_CARRY, true); break;
+ case RTI:
+ e->regs.flags = l6502_pop(e);
+ e->regs.pc = l6502_pop_addr(e);
+ break;
+ case PHA: l6502_push(e, e->regs.a); break;
+ case JMP_ABSOLUTE:
+ addr = l6502_read_addr(e, e->regs.pc);
+ e->regs.pc = addr;
+ break;
+ case CLI: l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, false); break;
+ case RTS: e->regs.pc = l6502_pop_addr(e) + 1; break;
+ case PLA: e->regs.a = l6502_pop(e); break;
+ case JMP_INDIRECT:
+ addr = l6502_read_addr(e, l6502_read_addr(e, e->regs.pc));
+ e->regs.pc = addr;
+ break;
+ case SEI: l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, true); break;
+ case DEY: e->regs.y -= 1; l6502_flags_check_zn(e, e->regs.y); break;
+ case TXA: e->regs.a = e->regs.x; l6502_flags_check_zn(e, e->regs.a); break;
+ case TYA: e->regs.a = e->regs.y; l6502_flags_check_zn(e, e->regs.a); break;
+ case TXS: e->regs.sp = e->regs.x; break; /* note TXS does not set flags */
+ case TAY: e->regs.y = e->regs.a; l6502_flags_check_zn(e, e->regs.y); break;
+ case TAX: e->regs.x = e->regs.a; l6502_flags_check_zn(e, e->regs.x); break;
+ case CLV: l6502_flag_set(e, L6502_FLAG_OVERFLOW, false); break;
+ case TSX: e->regs.x = e->regs.sp; l6502_flags_check_zn(e, e->regs.x); break;
+ case INY: e->regs.y += 1; l6502_flags_check_zn(e, e->regs.y); break;
+ case DEX: e->regs.x -= 1; l6502_flags_check_zn(e, e->regs.x); break;
+ case CLD: l6502_flag_set(e, L6502_FLAG_DECIMAL, false); break;
+ case INX: e->regs.x += 1; l6502_flags_check_zn(e, e->regs.x); break;
+ case NOP: break;
+ case SED: l6502_flag_set(e, L6502_FLAG_DECIMAL, true); break;
+ }
+ if ((ins & 0b11111) == 0b10000) { /* identify conditional branches */
+ l6502_flag flags[4] = { L6502_FLAG_NEGATIVE, L6502_FLAG_OVERFLOW, L6502_FLAG_CARRY, L6502_FLAG_ZERO };
+ u8 off = l6502_read(e, e->regs.pc++);
+ if (l6502_flag_get(e, flags[(ins >> 6) & 0b11]) == ((ins >> 5) & 0b1)) {
+ e->regs.pc = (u16) ((i16) e->regs.pc + (i16) (i8) off);
+ }
+ return;
+ }
+ switch (ins & 0b11) { /* identify instructions in common forms */
+ case 0b01: { /* group one */
+ set_reg = &e->regs.a;
+ switch ((ins >> 2) & 0b111) {
+ case G1_ADDRESSING_MODE_INDIRECT_ZEROPAGE_X: addr = l6502_addressing_mode_indirect_zeropage_x(e); break;
+ case G1_ADDRESSING_MODE_ZEROPAGE: addr = l6502_addressing_mode_zeropage(e); break;
+ case G1_ADDRESSING_MODE_IMMEDIATE: addr = l6502_addressing_mode_immediate(e); break;
+ case G1_ADDRESSING_MODE_ABSOLUTE: addr = l6502_addressing_mode_absolute(e); break;
+ case G1_ADDRESSING_MODE_INDIRECT_ZEROPAGE_Y: addr = l6502_addressing_mode_indirect_zeropage_y(e); break;
+ case G1_ADDRESSING_MODE_ZEROPAGE_X: addr = l6502_addressing_mode_zeropage_x(e); break;
+ case G1_ADDRESSING_MODE_ABSOLUTE_Y: addr = l6502_addressing_mode_absolute_y(e); break;
+ case G1_ADDRESSING_MODE_ABSOLUTE_X: addr = l6502_addressing_mode_absolute_x(e); break;
+ }
+ val = l6502_read(e, addr);
+ switch ((ins >> 5) & 0b111) {
+ case G1_OPCODE_ORA: res = (u16) e->regs.a | (u16) val; break;
+ case G1_OPCODE_AND: res = (u16) e->regs.a & (u16) val; break;
+ case G1_OPCODE_EOR: res = (u16) e->regs.a ^ (u16) val; break;
+ case G1_OPCODE_ADC:
+ res = (u16) e->regs.a + (u16) val;
+ res += (u16) l6502_flag_get(e, L6502_FLAG_CARRY);
+ l6502_flag_set(e, L6502_FLAG_OVERFLOW, ((res >> 8) != 0) != ((res >> 7) & 0b1));
+ break;
+ case G1_OPCODE_STA: l6502_write(e, addr, e->regs.a); set_flags = set_result = false; break;
+ case G1_OPCODE_LDA: res = (u16) l6502_read(e, addr); break;
+ case G1_OPCODE_CMP:
+ set_result = false;
+ res = (u16) e->regs.a - (u16) val;
+ break;
+ case G1_OPCODE_SBC:
+ res = (u16) e->regs.a - (u16) val;
+ res -= (u16) (1 - l6502_flag_get(e, L6502_FLAG_CARRY));
+ l6502_flag_set(e, L6502_FLAG_OVERFLOW, ((res >> 8) != 0) != ((res >> 7) & 0b1));
+ break;
+ }
+ break;
+ }
+ case 0b10: { /* group two */
+ switch ((ins >> 2) & 0b111) {
+ case G2_ADDRESSING_MODE_IMMEDIATE: addr = l6502_addressing_mode_immediate(e); break;
+ case G2_ADDRESSING_MODE_ZEROPAGE: addr = l6502_addressing_mode_zeropage(e); break;
+ case G2_ADDRESSING_MODE_ACCUMULATOR: addr = (u16) e->regs.a; set_reg = &e->regs.a; break;
+ case G2_ADDRESSING_MODE_ABSOLUTE: addr = l6502_addressing_mode_absolute(e); break;
+ case G2_ADDRESSING_MODE_ZEROPAGE_X: addr = l6502_addressing_mode_zeropage_x(e); break;
+ case G2_ADDRESSING_MODE_ABSOLUTE_X: addr = l6502_addressing_mode_absolute_x(e); break;
+ }
+ val = set_reg ? (u8) addr : l6502_read(e, addr);
+ switch ((ins >> 5) & 0b111) {
+ case G2_OPCODE_ASL: res = ((u16) val) << 1; break;
+ case G2_OPCODE_ROL: res = ((u16) val) << 1 | (u16) l6502_flag_get(e, L6502_FLAG_CARRY); break;
+ case G2_OPCODE_LSR:
+ res = ((u16) val) >> 1
+ | (u16) (val & 0b1) << 8; /* place the previous low bit in carry */
+ break;
+ case G2_OPCODE_ROR:
+ res = ((u16) val) >> 1
+ | (u16) l6502_flag_get(e, L6502_FLAG_CARRY) << 7
+ | (u16) (val & 0b1) << 8; /* same as above */
+ break;
+ case G2_OPCODE_STX: l6502_write(e, addr, e->regs.x); set_flags = set_result = false; break;
+ case G2_OPCODE_LDX: res = (u16) l6502_read(e, addr); set_reg = &e->regs.x; break;
+ case G2_OPCODE_DEC: res = (u16) val - 1; break;
+ case G2_OPCODE_INC: res = (u16) val + 1; break;
+ }
+ break;
+ }
+ case 0b00: { /* group three */
+ switch ((ins >> 2) & 0b111) {
+ case G3_ADDRESSING_MODE_IMMEDIATE: addr = l6502_addressing_mode_immediate(e); break;
+ case G3_ADDRESSING_MODE_ZEROPAGE: addr = l6502_addressing_mode_zeropage(e); break;
+ case G3_ADDRESSING_MODE_ABSOLUTE: addr = l6502_addressing_mode_absolute(e); break;
+ case G3_ADDRESSING_MODE_ZEROPAGE_X: addr = l6502_addressing_mode_zeropage_x(e); break;
+ case G3_ADDRESSING_MODE_ABSOLUTE_X: addr = l6502_addressing_mode_absolute_x(e); break;
+ }
+ val = l6502_read(e, addr);
+ switch ((ins >> 5) & 0b111) {
+ case G3_OPCODE_BIT:
+ set_flags = set_result = false;
+ l6502_flag_set(e, L6502_FLAG_ZERO, (e->regs.a & val) == 0);
+ l6502_flag_set(e, L6502_FLAG_OVERFLOW, (val >> 6) & 0b1);
+ l6502_flag_set(e, L6502_FLAG_NEGATIVE, (val >> 7) & 0b1);
+ break;
+ case G3_OPCODE_STY: l6502_write(e, addr, e->regs.y); set_flags = set_result = false; break;
+ case G3_OPCODE_LDY: res = (u16) l6502_read(e, addr); set_reg = &e->regs.y; break;
+ case G3_OPCODE_CPY:
+ set_result = false;
+ res = (u16) e->regs.y - (u16) val;
+ break;
+ case G3_OPCODE_CPX:
+ set_result = false;
+ res = (u16) e->regs.x - (u16) val;
+ break;
+ }
+ }
+ }
+ if (set_flags) {
+ l6502_flag_set(e, L6502_FLAG_ZERO, res == 0);
+ l6502_flag_set(e, L6502_FLAG_NEGATIVE, (res >> 7) & 0b1);
+ l6502_flag_set(e, L6502_FLAG_CARRY, (res >> 8) != 0);
+ }
+ if (set_result) {
+ if (set_reg) *set_reg = (u8) res;
+ else l6502_write(e, addr, (u8) res);
+ }
+}
diff --git a/6502/src/main.c b/6502/src/main.c
new file mode 100644
index 0000000..fe54ce7
--- /dev/null
+++ b/6502/src/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ (void) argc; (void) argv;
+ puts("hi");
+}