summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--65c02.ts220
-rw-r--r--instructions.ts190
-rw-r--r--instructions/ADC.ts9
-rw-r--r--instructions/BRK.ts22
-rw-r--r--instructions/CLC.ts13
-rw-r--r--instructions/INC.ts71
-rw-r--r--instructions/INX.ts15
-rw-r--r--instructions/INY.ts15
-rw-r--r--instructions/LDA.ts9
-rw-r--r--opcode_matrix.json606
-rw-r--r--runtime.ts79
-rw-r--r--thing.s2
13 files changed, 1247 insertions, 5 deletions
diff --git a/.gitignore b/.gitignore
index c73d5a5..36c5889 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ code.o
 thefile
 riscv
 pc-thingg
+a.out
diff --git a/65c02.ts b/65c02.ts
index a7660b4..96207a3 100644
--- a/65c02.ts
+++ b/65c02.ts
@@ -1,8 +1,13 @@
-class BitField {
+import opcodeMatrix from "./opcode_matrix.json" with { type: "json" };
+
+export class BitField {
     bits: boolean[];
     flip(bit: number) {
         this.bits[bit] = !this.bits[bit];
     }
+    bit(bit: number) {
+        return this.bits[bit]
+    }
     setBit(bit: number, value: boolean) {
         this.bits[bit] = value;
     }
@@ -26,6 +31,217 @@ class BitField {
     }
 }
 
+export class Register<T=number> extends BitField {
+    get value(): number {
+        return this.num()
+    }
+    set value(value: number) {
+        this.set(value)
+    }
+    increment(): number {
+        this.set(this.num() + 1);
+        return this.num()
+    }
+    decrement(): number {
+        this.set(this.num() - 1);
+        return this.num()
+    }
+    constructor(bits: number) {
+        super(bits);
+    }
+}
+
+export class Pin {
+    high: boolean = false;
+    HI() {
+        this.high = true
+    }
+    LO() {
+        this.high = false
+    }
+    constructor(init = false) {
+        this.high = init
+    }
+}
+
+export class IO {
+    data:            BitField = new BitField(8);
+    address:         BitField = new BitField(16);
+    busEnable:            Pin = new Pin();
+    NMI:                  Pin = new Pin();
+    ready:                Pin = new Pin(true);
+    reset:                Pin = new Pin(true);
+    readWrite:            Pin = new Pin();
+    interruptRequest:     Pin = new Pin();
+}
+
+/**
+ * the shittiest 65c02 emulator ever
+ * 
+ * not clock cycle accurate because fuck you
+ */
 export default class The65c02 {
-    data: BitField = new BitField(8)
+    io:                    IO = new IO();
+    //SECTION - register
+    programCounter:  Register<16> = new Register(16);
+    regA:            Register<8>  = new Register(8);
+    regX:            Register<8>  = new Register(8);
+    regY:            Register<8>  = new Register(8);
+
+    stackPointer:    Register<8>  = new Register(8);
+    status:          Register<8>  = new Register(8);
+    //!SECTION
+    //SECTION - status reg bits
+    get carry(): boolean {return this.status.bit(0)}
+    set carry(value:boolean) {this.status.setBit(0, value)}
+    get zero(): boolean {return this.status.bit(1)}
+    set zero(value:boolean) {this.status.setBit(1, value)}
+    get IRQBDisable(): boolean {return this.status.bit(2)}
+    set IRQBDisable(value:boolean) {this.status.setBit(2, value)}
+    get decimalMode(): boolean {return this.status.bit(3)}
+    set decimalMode(value:boolean) {this.status.setBit(3, value)}
+    get BRK(): boolean {return this.status.bit(4)}
+    set BRK(value:boolean) {this.status.setBit(4, value)}
+    // ...1... //
+    get overflow(): boolean {return this.status.bit(6)}
+    set overflow(value:boolean) {this.status.setBit(6, value)}
+    get negative(): boolean {return this.status.bit(7)}
+    set negative(value:boolean) {this.status.setBit(7, value)}
+    //!SECTION
+    read: () => void;
+    write: () => void;
+    readPC(): BitField {
+        this.io.address = this.programCounter;
+        this.read()
+        return this.io.data;
+    }
+    flagZN(num: number) {
+        this.negative = (num & 0x80) != 0;
+        this.zero = num == 0;
+    }
+    flagZCN(num: number) {
+        this.carry = num > 0xFF
+        this.negative = (num & 0x80) != 0;
+        this.zero = num == 0;
+    }
+    instructions: Record<string, (mode: string) => void> = {};
+    cycle() {
+        if(!this.io.reset.high) {
+            // reset register thingies
+            this.IRQBDisable = true;
+            this.decimalMode = false;
+            this.BRK = true;
+            // get reset vector
+            let resetVector = 0;
+            this.io.address.set(0xFFFC);
+            this.read()
+            resetVector |= this.io.data.num();
+            this.io.address.set(0xFFFD);
+            this.read()
+            resetVector |= this.io.data.num() << 8;
+            // move PC to RV
+            this.programCounter.set(resetVector)
+        }
+        this.io.address.set(this.programCounter.num());
+        this.read();
+        const instruction = this.io.data
+            .num()
+            .toString(16)
+            .padStart(2, '0')
+            .toLowerCase();
+        const opm: Record<string, { mnemonic: string, mode: string }> = opcodeMatrix;
+        if (!opm[instruction])
+            throw `not found ${instruction}`
+        if (!this.instructions[opm[instruction].mnemonic])
+            throw `not implement, sowwy (${opm[instruction].mnemonic}.ts not found)`;
+        console.debug(opm[instruction].mnemonic, opm[instruction].mode)
+        this.instructions[opm[instruction].mnemonic].call(this, opm[instruction].mode);
+    }
+    //SECTION - utils
+    getZPAddr(): number {
+        this.programCounter.increment()
+        const zp = this.readPC().num()
+        return zp
+    }
+    getZPXAddr(): number {
+        this.programCounter.increment()
+        const zp = this.readPC().num()
+        return (this.regX.num() + zp) & 0xFF
+    }
+    getAbsoluteAddr(): number {
+        this.programCounter.increment()
+        const lo_abit = this.readPC().num()
+        this.programCounter.increment()
+        const hi_abit = this.readPC().num()
+        return (hi_abit << 8) | lo_abit
+    }
+    getAbsoluteXAddr(): number {
+        this.programCounter.increment()
+        const lo_abit = this.readPC().num()
+        this.programCounter.increment()
+        const hi_abit = this.readPC().num()
+        return this.regX.num() + ((hi_abit << 8) | lo_abit)
+    }
+    getAbsoluteYAddr(): number {
+        this.programCounter.increment()
+        const lo_abit = this.readPC().num()
+        this.programCounter.increment()
+        const hi_abit = this.readPC().num()
+        return this.regX.num() + ((hi_abit << 8) | lo_abit)
+    }
+    getIndirectXAddr(): number {
+        this.programCounter.increment()
+        const addr = this.readPC().num()
+        this.io.address.set((this.regX.num() + addr) & 0xFF)
+        const lo_abit = this.readPC().num()
+        this.io.address.set((this.regX.num() + addr + 1) & 0xFF)
+        const hi_abit = this.readPC().num()
+        return ((hi_abit << 8) | lo_abit)
+    }
+    getIndirectYAddr(): number {
+        this.programCounter.increment()
+        const addr = this.readPC().num()
+        this.io.address.set((addr) & 0xFF)
+        const lo_abit = this.readPC().num()
+        this.io.address.set((addr + 1) & 0xFF)
+        const hi_abit = this.readPC().num()
+        return ((hi_abit << 8) | lo_abit) + this.regY.num();
+    }
+    getAddr(mode: string): number {
+        switch (mode) {
+            case 'immediate':
+                this.programCounter.increment()
+                this.programCounter.increment()
+                return this.programCounter.num() - 1
+            case 'zero-page':
+                return this.getZPAddr()
+            case 'zero-page, X-indexed':
+                return this.getZPXAddr()
+            case 'absolute':
+                return this.getAbsoluteAddr()
+            case 'absolute, X-indexed':
+                return this.getAbsoluteXAddr()
+            case 'absolute, Y-indexed':
+                return this.getAbsoluteYAddr()
+            case 'indirect, Y-indexed':
+                return this.getIndirectYAddr()
+            case 'X-indexed, indirect':
+                return this.getIndirectXAddr()
+        
+            default:
+                throw 'unknown mode '+mode
+        }
+    }
+    //!SECTION
+    constructor (read: () => void, write: () => void) {
+        this.read = read;
+        this.write = write;
+    }
+    async loadInstructions() {
+        const dir = Deno.readDirSync('instructions')
+        for (const entry of dir) {
+            this.instructions[entry.name.replace('.ts', '')]
+                = (await import(`./instructions/${entry.name}`)).default
+        }
+    }
 }
\ No newline at end of file
diff --git a/instructions.ts b/instructions.ts
index 4d23d23..9f30620 100644
--- a/instructions.ts
+++ b/instructions.ts
@@ -1,3 +1,187 @@
-export const instructions: Record<string, number> = {
-    '': 0x01
-}
\ No newline at end of file
+export const instructions: Record<number, string> = {
+    1:  "ADC", // ADd memory to accumulator with Carry
+    2:  "AND", // "AND" memory with accumulator
+    3:  "ASL", // Arithmetic Shift one bit Left, memory or accumulator
+    // 4. BBR Branch on Bit Reset
+    // 5. BBS Branch of Bit Set
+    6:  "BCC", // Branch on Carry Clear (Pc=0)
+    7:  "BCS", // Branch on Carry Set (Pc=1)
+    8:  "BEQ", // Branch if EQual (Pz=1)
+    9:  "BIT", // BIt Test
+    10: "BMI", // Branch if result MInus (Pn=1)
+    11: "BNE", // Branch if Not Equal (Pz=0)
+    12: "BPL", // Branch if result PLus (Pn=0)
+    // 13. BRA // BRanch Always
+    14: "BRK", // BReaK instruction
+    15: "BVC", // Branch on oVerflow Clear (Pv=0)
+    16: "BVS", // Branch on oVerflow Set (Pv=1)
+    17: "CLC", // CLear Cary flag
+    18: "CLD", // CLear Decimal mode
+    19: "CLI", // CLear Interrupt disable bit
+    20: "CLV", // CLear oVerflow flag
+    21: "CMP", // CoMPare memory and accumulator
+    22: "CPX", // ComPare memory and X register
+    23: "CPY", // ComPare memory and Y register
+    24: "DEC", // DECrement memory or accumulate by one
+    25: "DEX", // DEcrement X by one
+    26: "DEY", // DEcrement Y by one
+    27: "EOR", // "Exclusive OR" memory with accumulate
+    28: "INC", // INCrement memory or accumulate by one
+    29: "INX", // INcrement X register by one
+    30: "INY", // INcrement Y register by one
+    31: "JMP", // JuMP to new location
+    32: "JSR", // Jump to new location Saving Return (Jump to SubRoutine)
+    33: "LDA", // LoaD Accumulator with memory
+    34: "LDX", // LoaD the X register with memory
+    35: "LDY", // LoaD the Y register with memory
+    36: "LSR", // Logical Shift one bit Right memory or accumulator
+    37: "NOP", // No OPeration
+    38: "ORA", // "OR" memory with Accumulator
+    39: "PHA", // PusH Accumulator on stack
+    40: "PHP", // PusH Processor status on stack
+    // 41. PHX PusH X register on stack
+    // 42. PHY PusH Y register on stack
+    43: "PLA", // PuLl Accumulator from stack
+    44: "PLP", // PuLl Processor status from stack
+    // 45. PLX PuLl X register from stack
+    // 46. PLY PuLl Y register from stack
+    // 47. RMB Reset Memory Bit
+    48: "ROL", // ROtate one bit Left memory or accumulator
+    49: "ROR", // ROtate one bit Right memory or accumulator
+    50: "RTI", // ReTurn from Interrupt
+    51: "RTS", // ReTurn from Subroutine
+    52: "SBC", // SuBtract memory from accumulator with borrow (Carry bit)
+    53: "SEC", // SEt Carry
+    54: "SED", // SEt Decimal mode
+    55: "SEI", // SEt Interrupt disable status
+    // 56. SMB Set Memory Bit
+    57: "STA", // STore Accumulator in memory
+    // 58. STP SToP mode
+    59: "STX", // STore the X register in memory
+    60: "STY", // STore the Y register in memory
+    // 61. STZ STore Zero in memory
+    62: "TAX", // Transfer the Accumulator to the X register
+    63: "TAY", // Transfer the Accumulator to the Y register
+    // 64. TRB Test and Reset memory Bit
+    // 65. TSB Test and Set memory Bit
+    66: "TSX", // Transfer the Stack pointer to the X register
+    67: "TXA", // Transfer the X register to the Accumulator
+    68: "TXS", // Transfer the X register to the Stack pointer register
+    69: "TYA", // Transfer Y register to the Accumulator
+    // 70. WAI WAit for Interrupt
+}
+
+export const instructionIds = {
+    /** ADd memory to accumulator with Carry */
+    "ADC": 1,
+    /** "AND" memory with accumulator */
+    "AND": 2,
+    /** Arithmetic Shift one bit Left, memory or accumulator */
+    "ASL": 3,
+    /** Branch on Carry Clear (Pc=0) */
+    "BCC": 6,
+    /** Branch on Carry Set (Pc=1) */
+    "BCS": 7,
+    /** Branch if EQual (Pz=1) */
+    "BEQ": 8,
+    /** BIt Test */
+    "BIT": 9,
+    /** Branch if result MInus (Pn=1) */
+    "BMI": 10,
+    /** Branch if Not Equal (Pz=0) */
+    "BNE": 11,
+    /** Branch if result PLus (Pn=0) */
+    "BPL": 12,
+    /** BReaK instruction */
+    "BRK": 14,
+    /** Branch on oVerflow Clear (Pv=0) */
+    "BVC": 15,
+    /** Branch on oVerflow Set (Pv=1) */
+    "BVS": 16,
+    /** CLear Cary flag */
+    "CLC": 17,
+    /** CLear Decimal mode */
+    "CLD": 18,
+    /** CLear Interrupt disable bit */
+    "CLI": 19,
+    /** CLear oVerflow flag */
+    "CLV": 20,
+    /** CoMPare memory and accumulator */
+    "CMP": 21,
+    /** ComPare memory and X register */
+    "CPX": 22,
+    /** ComPare memory and Y register */
+    "CPY": 23,
+    /** DECrement memory or accumulate by one */
+    "DEC": 24,
+    /** DEcrement X by one */
+    "DEX": 25,
+    /** DEcrement Y by one */
+    "DEY": 26,
+    /** "Exclusive OR" memory with accumulate */
+    "EOR": 27,
+    /** INCrement memory or accumulate by one */
+    "INC": 28,
+    /** INcrement X register by one */
+    "INX": 29,
+    /** INcrement Y register by one */
+    "INY": 30,
+    /** JuMP to new location */
+    "JMP": 31,
+    /** Jump to new location Saving Return (Jump to SubRoutine) */
+    "JSR": 32,
+    /** LoaD Accumulator with memory */
+    "LDA": 33,
+    /** LoaD the X register with memory */
+    "LDX": 34,
+    /** LoaD the Y register with memory */
+    "LDY": 35,
+    /** Logical Shift one bit Right memory or accumulator */
+    "LSR": 36,
+    /** No OPeration */
+    "NOP": 37,
+    /** "OR" memory with Accumulator */
+    "ORA": 38,
+    /** PusH Accumulator on stack */
+    "PHA": 39,
+    /** PusH Processor status on stack */
+    "PHP": 40,
+    /** PuLl Accumulator from stack */
+    "PLA": 43,
+    /** PuLl Processor status from stack */
+    "PLP": 44,
+    /** ROtate one bit Left memory or accumulator */
+    "ROL": 48,
+    /** ROtate one bit Right memory or accumulator */
+    "ROR": 49,
+    /** ReTurn from Interrupt */
+    "RTI": 50,
+    /** ReTurn from Subroutine */
+    "RTS": 51,
+    /** SuBtract memory from accumulator with borrow (Carry bit) */
+    "SBC": 52,
+    /** SEt Carry */
+    "SEC": 53,
+    /** SEt Decimal mode */
+    "SED": 54,
+    /** SEt Interrupt disable status */
+    "SEI": 55,
+    /** STore Accumulator in memory */
+    "STA": 57,
+    /** STore the X register in memory */
+    "STX": 59,
+    /** STore the Y register in memory */
+    "STY": 60,
+    /** Transfer the Accumulator to the X register */
+    "TAX": 62,
+    /** Transfer the Accumulator to the Y register */
+    "TAY": 63,
+    /** Transfer the Stack pointer to the X register */
+    "TSX": 66,
+    /** Transfer the X register to the Accumulator */
+    "TXA": 67,
+    /** Transfer the X register to the Stack pointer register */
+    "TXS": 68,
+    /** Transfer Y register to the Accumulator */
+    "TYA": 69,
+}
diff --git a/instructions/ADC.ts b/instructions/ADC.ts
new file mode 100644
index 0000000..c8163d0
--- /dev/null
+++ b/instructions/ADC.ts
@@ -0,0 +1,9 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    const addr = this.getAddr(mode);
+    this.io.address.set(addr);
+    this.read();
+    this.flagZN(this.io.data.num())
+    this.regA.set(this.io.data.num())
+}
\ No newline at end of file
diff --git a/instructions/BRK.ts b/instructions/BRK.ts
new file mode 100644
index 0000000..99e4072
--- /dev/null
+++ b/instructions/BRK.ts
@@ -0,0 +1,22 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    switch (mode) {
+        case 'implied':
+            this.io.interruptRequest.HI()
+            //TODO: push shit onto stack
+            this.io.data.set(this.programCounter.num() & 0x00FF)
+            this.io.address.set(0xFFFF)
+            this.write()
+            this.io.data.set(this.programCounter.num() & 0xFF00)
+            this.io.address.set(0xFFFE)
+            this.write()
+            this.BRK = true
+            this.programCounter.increment();
+            this.programCounter.increment();
+            break;
+    
+        default:
+            throw 'wha';
+    }
+}
\ No newline at end of file
diff --git a/instructions/CLC.ts b/instructions/CLC.ts
new file mode 100644
index 0000000..d14b2f4
--- /dev/null
+++ b/instructions/CLC.ts
@@ -0,0 +1,13 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    switch (mode) {
+        case 'implied':
+            this.carry = false;
+            this.programCounter.increment();
+            break;
+    
+        default:
+            throw 'wha';
+    }
+}
\ No newline at end of file
diff --git a/instructions/INC.ts b/instructions/INC.ts
new file mode 100644
index 0000000..971abd3
--- /dev/null
+++ b/instructions/INC.ts
@@ -0,0 +1,71 @@
+// deno-lint-ignore-file no-case-declarations
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    switch (mode) {
+        case 'zero-page':
+            // set PC to where the ZP address is
+            this.programCounter.increment()
+            const zpa = this.readPC()
+            this.io.address.set(zpa.num());
+            this.read();
+            let v = this.io.data.num();
+            v++
+            v &= 0xFF
+            this.io.data.set(v)
+            this.write()
+            this.flagZN(v)
+            this.programCounter.increment()
+            break;
+        
+        case 'zero-page, X-indexed':
+            // set PC to where the ZP address is
+            this.programCounter.increment()
+            const zpax = this.readPC()
+            this.io.address.set(zpax.num() + this.regX.num());
+            this.read();
+            let vzx = this.io.data.num();
+            vzx++
+            vzx &= 0xFF
+            this.io.data.set(vzx)
+            this.write()
+            this.flagZN(vzx)
+            this.programCounter.increment()
+            break;
+        
+        case 'absolute':
+            // skip over the opcode
+            this.programCounter.increment()
+            const lo_abit = this.readPC().num()
+            this.programCounter.increment()
+            const hi_abit = this.readPC().num()
+            this.io.address.set((hi_abit << 8) | lo_abit)
+            this.read()
+            let va = this.io.data.num();
+            va++
+            va &= 0xFF
+            this.io.data.set(va)
+            this.write()
+            this.programCounter.increment()
+            break;
+        
+        case 'absolute, X-indexed':
+            // skip over the opcode
+            this.programCounter.increment()
+            const lo_axbit = this.readPC().num()
+            this.programCounter.increment()
+            const hi_axbit = this.readPC().num()
+            this.io.address.set(((hi_axbit << 8) | lo_axbit) + this.regX.num())
+            this.read()
+            let vax = this.io.data.num();
+            vax++
+            vax &= 0xFF
+            this.io.data.set(vax)
+            this.write()
+            this.programCounter.increment()
+            break;
+    
+        default:
+            throw 'wha';
+    }
+}
\ No newline at end of file
diff --git a/instructions/INX.ts b/instructions/INX.ts
new file mode 100644
index 0000000..9c1cc1b
--- /dev/null
+++ b/instructions/INX.ts
@@ -0,0 +1,15 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    switch (mode) {
+        case 'implied':
+            this.regX.increment()
+            this.negative = this.regX.bit(7);
+            this.zero = this.regX.num() == 0;
+            this.programCounter.increment();
+            break;
+    
+        default:
+            throw 'wha';
+    }
+}
\ No newline at end of file
diff --git a/instructions/INY.ts b/instructions/INY.ts
new file mode 100644
index 0000000..566c6d4
--- /dev/null
+++ b/instructions/INY.ts
@@ -0,0 +1,15 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    switch (mode) {
+        case 'implied':
+            this.regY.increment()
+            this.negative = this.regY.bit(7);
+            this.zero = this.regY.num() == 0;
+            this.programCounter.increment();
+            break;
+    
+        default:
+            throw 'wha';
+    }
+}
\ No newline at end of file
diff --git a/instructions/LDA.ts b/instructions/LDA.ts
new file mode 100644
index 0000000..c8163d0
--- /dev/null
+++ b/instructions/LDA.ts
@@ -0,0 +1,9 @@
+import type The65c02 from "../65c02.ts";
+
+export default function (this: The65c02, mode: string) {
+    const addr = this.getAddr(mode);
+    this.io.address.set(addr);
+    this.read();
+    this.flagZN(this.io.data.num())
+    this.regA.set(this.io.data.num())
+}
\ No newline at end of file
diff --git a/opcode_matrix.json b/opcode_matrix.json
new file mode 100644
index 0000000..be27986
--- /dev/null
+++ b/opcode_matrix.json
@@ -0,0 +1,606 @@
+{
+    "10": {
+        "mnemonic": "BPL",
+        "mode": "relative"
+    },
+    "11": {
+        "mnemonic": "ORA",
+        "mode": "indirect, Y-indexed"
+    },
+    "15": {
+        "mnemonic": "ORA",
+        "mode": "zero-page, X-indexed"
+    },
+    "16": {
+        "mnemonic": "ASL",
+        "mode": "zero-page, X-indexed"
+    },
+    "18": {
+        "mnemonic": "CLC",
+        "mode": "implied"
+    },
+    "19": {
+        "mnemonic": "ORA",
+        "mode": "absolute, Y-indexed"
+    },
+    "20": {
+        "mnemonic": "JSR",
+        "mode": "absolute"
+    },
+    "21": {
+        "mnemonic": "AND",
+        "mode": "X-indexed, indirect"
+    },
+    "24": {
+        "mnemonic": "BIT",
+        "mode": "zero-page"
+    },
+    "25": {
+        "mnemonic": "AND",
+        "mode": "zero-page"
+    },
+    "26": {
+        "mnemonic": "ROL",
+        "mode": "zero-page"
+    },
+    "28": {
+        "mnemonic": "PLP",
+        "mode": "implied"
+    },
+    "29": {
+        "mnemonic": "AND",
+        "mode": "immediate"
+    },
+    "30": {
+        "mnemonic": "BMI",
+        "mode": "relative"
+    },
+    "31": {
+        "mnemonic": "AND",
+        "mode": "indirect, Y-indexed"
+    },
+    "35": {
+        "mnemonic": "AND",
+        "mode": "zero-page, X-indexed"
+    },
+    "36": {
+        "mnemonic": "ROL",
+        "mode": "zero-page, X-indexed"
+    },
+    "38": {
+        "mnemonic": "SEC",
+        "mode": "implied"
+    },
+    "39": {
+        "mnemonic": "AND",
+        "mode": "absolute, Y-indexed"
+    },
+    "40": {
+        "mnemonic": "RTI",
+        "mode": "implied"
+    },
+    "41": {
+        "mnemonic": "EOR",
+        "mode": "X-indexed, indirect"
+    },
+    "45": {
+        "mnemonic": "EOR",
+        "mode": "zero-page"
+    },
+    "46": {
+        "mnemonic": "LSR",
+        "mode": "zero-page"
+    },
+    "48": {
+        "mnemonic": "PHA",
+        "mode": "implied"
+    },
+    "49": {
+        "mnemonic": "EOR",
+        "mode": "immediate"
+    },
+    "50": {
+        "mnemonic": "BVC",
+        "mode": "relative"
+    },
+    "51": {
+        "mnemonic": "EOR",
+        "mode": "indirect, Y-indexed"
+    },
+    "55": {
+        "mnemonic": "EOR",
+        "mode": "zero-page, X-indexed"
+    },
+    "56": {
+        "mnemonic": "LSR",
+        "mode": "zero-page, X-indexed"
+    },
+    "58": {
+        "mnemonic": "CLI",
+        "mode": "implied"
+    },
+    "59": {
+        "mnemonic": "EOR",
+        "mode": "absolute, Y-indexed"
+    },
+    "60": {
+        "mnemonic": "RTS",
+        "mode": "implied"
+    },
+    "61": {
+        "mnemonic": "ADC",
+        "mode": "X-indexed, indirect"
+    },
+    "65": {
+        "mnemonic": "ADC",
+        "mode": "zero-page"
+    },
+    "66": {
+        "mnemonic": "ROR",
+        "mode": "zero-page"
+    },
+    "68": {
+        "mnemonic": "PLA",
+        "mode": "implied"
+    },
+    "69": {
+        "mnemonic": "ADC",
+        "mode": "immediate"
+    },
+    "70": {
+        "mnemonic": "BVS",
+        "mode": "relative"
+    },
+    "71": {
+        "mnemonic": "ADC",
+        "mode": "indirect, Y-indexed"
+    },
+    "75": {
+        "mnemonic": "ADC",
+        "mode": "zero-page, X-indexed"
+    },
+    "76": {
+        "mnemonic": "ROR",
+        "mode": "zero-page, X-indexed"
+    },
+    "78": {
+        "mnemonic": "SEI",
+        "mode": "implied"
+    },
+    "79": {
+        "mnemonic": "ADC",
+        "mode": "absolute, Y-indexed"
+    },
+    "81": {
+        "mnemonic": "STA",
+        "mode": "X-indexed, indirect"
+    },
+    "84": {
+        "mnemonic": "STY",
+        "mode": "zero-page"
+    },
+    "85": {
+        "mnemonic": "STA",
+        "mode": "zero-page"
+    },
+    "86": {
+        "mnemonic": "STX",
+        "mode": "zero-page"
+    },
+    "88": {
+        "mnemonic": "DEY",
+        "mode": "implied"
+    },
+    "90": {
+        "mnemonic": "BCC",
+        "mode": "relative"
+    },
+    "91": {
+        "mnemonic": "STA",
+        "mode": "indirect, Y-indexed"
+    },
+    "94": {
+        "mnemonic": "STY",
+        "mode": "zero-page, X-indexed"
+    },
+    "95": {
+        "mnemonic": "STA; InsItr",
+        "mode": "zero-page, X-indexed"
+    },
+    "96": {
+        "mnemonic": "STX",
+        "mode": "zero-page, Y-indexed"
+    },
+    "98": {
+        "mnemonic": "TYA",
+        "mode": "implied"
+    },
+    "99": {
+        "mnemonic": "STA",
+        "mode": "absolute, Y-indexed"
+    },
+    "00": {
+        "mnemonic": "BRK",
+        "mode": "implied"
+    },
+    "01": {
+        "mnemonic": "ORA",
+        "mode": "X-indexed, indirect"
+    },
+    "05": {
+        "mnemonic": "ORA",
+        "mode": "zero-page"
+    },
+    "06": {
+        "mnemonic": "ASL",
+        "mode": "zero-page"
+    },
+    "08": {
+        "mnemonic": "PHP",
+        "mode": "implied"
+    },
+    "09": {
+        "mnemonic": "ORA",
+        "mode": "immediate"
+    },
+    "0a": {
+        "mnemonic": "ASL",
+        "mode": "accumulator (implied)"
+    },
+    "0d": {
+        "mnemonic": "ORA",
+        "mode": "absolute"
+    },
+    "0e": {
+        "mnemonic": "ASL",
+        "mode": "absolute"
+    },
+    "1d": {
+        "mnemonic": "ORA",
+        "mode": "absolute, X-indexed"
+    },
+    "1e": {
+        "mnemonic": "ASL",
+        "mode": "absolute, X-indexed"
+    },
+    "2a": {
+        "mnemonic": "ROL",
+        "mode": "accumulator (implied)"
+    },
+    "2c": {
+        "mnemonic": "BIT",
+        "mode": "absolute"
+    },
+    "2d": {
+        "mnemonic": "AND",
+        "mode": "absolute"
+    },
+    "2e": {
+        "mnemonic": "ROL",
+        "mode": "absolute"
+    },
+    "3d": {
+        "mnemonic": "AND",
+        "mode": "absolute, X-indexed"
+    },
+    "3e": {
+        "mnemonic": "ROL",
+        "mode": "absolute, X-indexed"
+    },
+    "4a": {
+        "mnemonic": "LSR",
+        "mode": "accumulator (implied)"
+    },
+    "4c": {
+        "mnemonic": "JMP",
+        "mode": "absolute"
+    },
+    "4d": {
+        "mnemonic": "EOR",
+        "mode": "absolute"
+    },
+    "4e": {
+        "mnemonic": "LSR",
+        "mode": "absolute"
+    },
+    "5d": {
+        "mnemonic": "EOR",
+        "mode": "absolute, X-indexed"
+    },
+    "5e": {
+        "mnemonic": "LSR",
+        "mode": "absolute, X-indexed"
+    },
+    "6a": {
+        "mnemonic": "ROR",
+        "mode": "accumulator (implied)"
+    },
+    "6c": {
+        "mnemonic": "JMP",
+        "mode": "indirect"
+    },
+    "6d": {
+        "mnemonic": "ADC",
+        "mode": "absolute"
+    },
+    "6e": {
+        "mnemonic": "ROR",
+        "mode": "absolute"
+    },
+    "7d": {
+        "mnemonic": "ADC",
+        "mode": "absolute, X-indexed"
+    },
+    "7e": {
+        "mnemonic": "ROR",
+        "mode": "absolute, X-indexed"
+    },
+    "8a": {
+        "mnemonic": "TXA",
+        "mode": "implied"
+    },
+    "8c": {
+        "mnemonic": "STY",
+        "mode": "absolute"
+    },
+    "8d": {
+        "mnemonic": "STA",
+        "mode": "absolute"
+    },
+    "8e": {
+        "mnemonic": "STX",
+        "mode": "absolute"
+    },
+    "9a": {
+        "mnemonic": "TXS",
+        "mode": "implied"
+    },
+    "9d": {
+        "mnemonic": "STA",
+        "mode": "absolute, X-indexed"
+    },
+    "a0": {
+        "mnemonic": "LDY",
+        "mode": "immediate"
+    },
+    "a1": {
+        "mnemonic": "LDA",
+        "mode": "X-indexed, indirect"
+    },
+    "a2": {
+        "mnemonic": "LDX",
+        "mode": "immediate"
+    },
+    "a4": {
+        "mnemonic": "LDY",
+        "mode": "zero-page"
+    },
+    "a5": {
+        "mnemonic": "LDA",
+        "mode": "zero-page"
+    },
+    "a6": {
+        "mnemonic": "LDX",
+        "mode": "zero-page"
+    },
+    "a8": {
+        "mnemonic": "TAY",
+        "mode": "implied"
+    },
+    "a9": {
+        "mnemonic": "LDA",
+        "mode": "immediate"
+    },
+    "aa": {
+        "mnemonic": "TAX",
+        "mode": "implied"
+    },
+    "ac": {
+        "mnemonic": "LDY",
+        "mode": "absolute"
+    },
+    "ad": {
+        "mnemonic": "LDA",
+        "mode": "absolute"
+    },
+    "ae": {
+        "mnemonic": "LDX",
+        "mode": "absolute"
+    },
+    "b0": {
+        "mnemonic": "BCS",
+        "mode": "relative"
+    },
+    "b1": {
+        "mnemonic": "LDA",
+        "mode": "indirect, Y-indexed"
+    },
+    "b4": {
+        "mnemonic": "LDY",
+        "mode": "zero-page, X-indexed"
+    },
+    "b5": {
+        "mnemonic": "LDA",
+        "mode": "zero-page, X-indexed"
+    },
+    "b6": {
+        "mnemonic": "LDX",
+        "mode": "zero-page, Y-indexed"
+    },
+    "b8": {
+        "mnemonic": "CLV",
+        "mode": "implied"
+    },
+    "b9": {
+        "mnemonic": "LDA",
+        "mode": "absolute, Y-indexed"
+    },
+    "ba": {
+        "mnemonic": "TSX",
+        "mode": "implied"
+    },
+    "bc": {
+        "mnemonic": "LDY",
+        "mode": "absolute, X-indexed"
+    },
+    "bd": {
+        "mnemonic": "LDA",
+        "mode": "absolute, X-indexed"
+    },
+    "be": {
+        "mnemonic": "LDX",
+        "mode": "absolute, Y-indexed"
+    },
+    "c0": {
+        "mnemonic": "CPY",
+        "mode": "immediate"
+    },
+    "c1": {
+        "mnemonic": "CMP",
+        "mode": "X-indexed, indirect"
+    },
+    "c4": {
+        "mnemonic": "CPY",
+        "mode": "zero-page"
+    },
+    "c5": {
+        "mnemonic": "CMP",
+        "mode": "zero-page"
+    },
+    "c6": {
+        "mnemonic": "DEC",
+        "mode": "zero-page"
+    },
+    "c8": {
+        "mnemonic": "INY",
+        "mode": "implied"
+    },
+    "c9": {
+        "mnemonic": "CMP",
+        "mode": "immediate"
+    },
+    "ca": {
+        "mnemonic": "DEX",
+        "mode": "implied"
+    },
+    "cc": {
+        "mnemonic": "CPY",
+        "mode": "absolute"
+    },
+    "cd": {
+        "mnemonic": "CMP",
+        "mode": "absolute"
+    },
+    "ce": {
+        "mnemonic": "DEC",
+        "mode": "absolute"
+    },
+    "d0": {
+        "mnemonic": "BNE",
+        "mode": "relative"
+    },
+    "d1": {
+        "mnemonic": "CMP",
+        "mode": "indirect, Y-indexed"
+    },
+    "d5": {
+        "mnemonic": "CMP",
+        "mode": "zero-page, X-indexed"
+    },
+    "d6": {
+        "mnemonic": "DEC",
+        "mode": "zero-page, X-indexed"
+    },
+    "d8": {
+        "mnemonic": "CLD",
+        "mode": "implied"
+    },
+    "d9": {
+        "mnemonic": "CMP",
+        "mode": "absolute, Y-indexed"
+    },
+    "dd": {
+        "mnemonic": "CMP",
+        "mode": "absolute, X-indexed"
+    },
+    "de": {
+        "mnemonic": "DEC",
+        "mode": "absolute, X-indexed"
+    },
+    "e0": {
+        "mnemonic": "CPX",
+        "mode": "immediate"
+    },
+    "e1": {
+        "mnemonic": "SBC",
+        "mode": "X-indexed, indirect"
+    },
+    "e4": {
+        "mnemonic": "CPX",
+        "mode": "zero-page"
+    },
+    "e5": {
+        "mnemonic": "SBC",
+        "mode": "zero-page"
+    },
+    "e6": {
+        "mnemonic": "INC",
+        "mode": "zero-page"
+    },
+    "e8": {
+        "mnemonic": "INX",
+        "mode": "implied"
+    },
+    "e9": {
+        "mnemonic": "SBC",
+        "mode": "immediate"
+    },
+    "ea": {
+        "mnemonic": "NOP",
+        "mode": "implied"
+    },
+    "ec": {
+        "mnemonic": "CPX",
+        "mode": "absolute"
+    },
+    "ed": {
+        "mnemonic": "SBC",
+        "mode": "absolute"
+    },
+    "ee": {
+        "mnemonic": "INC",
+        "mode": "absolute"
+    },
+    "f0": {
+        "mnemonic": "BEQ",
+        "mode": "relative"
+    },
+    "f1": {
+        "mnemonic": "SBC",
+        "mode": "indirect, Y-indexed"
+    },
+    "f5": {
+        "mnemonic": "SBC",
+        "mode": "zero-page, X-indexed"
+    },
+    "f6": {
+        "mnemonic": "INC",
+        "mode": "zero-page, X-indexed"
+    },
+    "f8": {
+        "mnemonic": "SED",
+        "mode": "implied"
+    },
+    "f9": {
+        "mnemonic": "SBC",
+        "mode": "absolute, Y-indexed"
+    },
+    "fd": {
+        "mnemonic": "SBC",
+        "mode": "absolute, X-indexed"
+    },
+    "fe": {
+        "mnemonic": "INC",
+        "mode": "absolute, X-indexed"
+    }
+}
\ No newline at end of file
diff --git a/runtime.ts b/runtime.ts
new file mode 100644
index 0000000..4f6435d
--- /dev/null
+++ b/runtime.ts
@@ -0,0 +1,79 @@
+import The65c02, { BitField, Pin } from "./65c02.ts";
+// import { instructionIds } from "./instructions.ts";
+import opcodeMatrix from "./opcode_matrix.json" with { type: "json" };
+
+function instruction(name: string | TemplateStringsArray, mode: string = 'implied'): number {
+    const goog = (Object.entries(opcodeMatrix).find(([_, v]) =>
+        v.mnemonic == name && v.mode == mode) ?? [])[0];
+    if (!goog)
+        throw `unknown instruction (${name}-${mode})`
+    return parseInt(goog, 16)
+}
+
+// the thing used for ram
+const ram = new Uint8Array(2**16);
+
+const cpu = new The65c02(function (this: The65c02) {
+    // since this is controlled by the user it's easy
+    // to map things into address space
+    this.io.data.set(ram[this.io.address.num()] ?? 0)
+}, function (this: The65c02) {
+    // write
+    ram[this.io.address.num()] = this.io.data.num()
+})
+
+await cpu.loadInstructions()
+
+// mem address $0000
+ram[0xFFFC] = 0x00
+ram[0xFFFD] = 0x00
+
+//TODO: read code from file
+// const code = [
+//     // nop
+//     instruction`CLC`,
+//     // instruction`CLD`,
+//     instruction('INC', 'zero-page'),
+//     0x21,
+//     instruction`BRK`,
+// ]
+
+const code = Deno.readFileSync('a.out')
+
+// write code to ram before execution
+for (let offset = 0; offset < code.length; offset++) {
+    const byte = code[offset];
+    ram[offset] = byte;
+}
+
+// pull RESB low to reset the 65c02
+cpu.io.reset.LO()
+cpu.cycle()
+
+//the cpu reset, pull RESB high and start execution!
+cpu.io.reset.HI()
+
+while (!cpu.io.interruptRequest.high) {
+    cpu.cycle()
+}
+
+console.log('end of execution\n\nIO status:')
+console.log(Object.entries(cpu.io)
+    .map(([name, io]) => {
+        if (io instanceof Pin) {
+            return ` ${name}: ${' '.repeat(20 - name.length)}${io.high ? "HI" : "LO"}`
+        } else if (io instanceof BitField) {
+            return ` ${name}: ${' '.repeat(20 - name.length)}${io.bits.map(k=>+k).join('')} (0x${io.num().toString(16)})`
+        }
+    }).join('\n'));
+
+console.log('\nregisters:')
+console.log(` A: ${cpu.regA.bits.map(k=>+k).join('')} (0x${cpu.regA.num().toString(16)})`)
+console.log(` X: ${cpu.regX.bits.map(k=>+k).join('')} (0x${cpu.regX.num().toString(16)})`)
+console.log(` Y: ${cpu.regY.bits.map(k=>+k).join('')} (0x${cpu.regY.num().toString(16)})`)
+
+console.debug('\nRAM:')
+Deno.writeFileSync('ram.bin', ram)
+new Deno.Command('hexdump', {
+    args: ['-C', 'ram.bin']
+}).spawn()
diff --git a/thing.s b/thing.s
new file mode 100644
index 0000000..d2e3f9d
--- /dev/null
+++ b/thing.s
@@ -0,0 +1,2 @@
+  lda #$ff
+  brk
\ No newline at end of file