summary refs log tree commit diff
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2025-04-03 21:51:53 +0300
committerWlodekM <[email protected]>2025-04-03 21:51:53 +0300
commite2568226af4af2d3b27e9e1a775e7244c5ef7191 (patch)
treef6bd5d51753c549728a7160743e3b935a3a39602
parentc62e133afb34fc38445af7bbe61e88cf45e9a7e3 (diff)
more progress on trying to get wozmon running
-rw-r--r--65c02.ts16
-rw-r--r--eater.ts76
-rw-r--r--instructions/ASL.ts1
-rw-r--r--runtime.ts130
-rw-r--r--thing.s6
-rw-r--r--wozmon.s208
6 files changed, 391 insertions, 46 deletions
diff --git a/65c02.ts b/65c02.ts
index f1368a9..fb8f050 100644
--- a/65c02.ts
+++ b/65c02.ts
@@ -157,6 +157,22 @@ export default class The65c02 {
             this.programCounter.set(resetVector);
             return;
         }
+        if (this.io.interruptRequest.high && !this.IRQBDisable) {
+            console.log('interrupt!')
+            this.IRQBDisable = true;
+            this.push(this.programCounter.num() & 0x00FF)
+            this.push(this.programCounter.num() & 0xFF00)
+            this.push(this.status.num())
+            let interruptVector = 0;
+            this.io.address.set(0xFFFE);
+            this.read()
+            interruptVector |= this.io.data.num();
+            this.io.address.set(0xFFFF);
+            this.read()
+            interruptVector |= this.io.data.num() << 8;
+            this.programCounter.set(interruptVector);
+            return;
+        }
         this.io.address.set(this.programCounter.num());
         this.read();
         const instruction = this.io.data
diff --git a/eater.ts b/eater.ts
new file mode 100644
index 0000000..4440635
--- /dev/null
+++ b/eater.ts
@@ -0,0 +1,76 @@
+// deno-lint-ignore-file no-process-globals
+import The65c02 from "./65c02.ts";
+// eater.ts
+// a runtime meant to mimic ben eater's 65c02 computer
+
+if (!process.stdin.isRaw)
+    process.stdin.setRawMode(true)
+
+// the thing used for ram
+const ram = new Uint8Array(2**16);
+
+// initiate the emulator
+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)
+    if (this.io.address.num() == 0x5000
+        && ram[0x5001])
+        ram[0x5001] = 0;
+}, function (this: The65c02) {
+    if (this.io.address.num() == 0x5000) {
+        return Deno.stdout.write(new Uint8Array([this.io.data.num()]))
+    }
+    // write
+    ram[this.io.address.num()] = this.io.data.num()
+})
+
+const binStart = 0;
+
+await cpu.loadInstructions()
+
+const code = Deno.readFileSync('a.out')
+
+// mem address $0000
+ram[0xFFFC] = binStart & 0x00FF
+ram[0xFFFD] = (binStart & 0xFF00) >> 8
+
+// write code to ram before execution
+for (let offset = 0; offset < code.length; offset++) {
+    const byte = code[offset];
+    ram[binStart  + 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()
+
+
+let running = true;
+
+process.stdin.on('data', (data) => {
+    if (data[0] == 3)
+        return running = false;
+    // console.log(`uh`, data[0], data[0].toString(16))
+    ram[0x5000] = data[0];
+    ram[0x5001] = 0x08;
+    // cpu.io.interruptRequest.HI();
+})
+
+// repeat until the cpu requests an interrupt
+const clock = setInterval(() => {
+    if (!running) {
+        Deno.writeFileSync('ram.bin', ram)
+        clearInterval(clock)
+        
+        process.stdin.setRawMode(false);
+        Deno.exit(0)
+    }
+    if(cpu.io.interruptRequest.high && !cpu.IRQBDisable)
+        cpu.io.interruptRequest.LO();
+    cpu.cycle();
+// 1MHz i think
+}, 1)
diff --git a/instructions/ASL.ts b/instructions/ASL.ts
index 1f3fcbb..d65df3f 100644
--- a/instructions/ASL.ts
+++ b/instructions/ASL.ts
@@ -7,6 +7,7 @@ export default function (this: The65c02, mode: string) {
         this.negative = (mem & 128) != 0
         this.carry = (mem & 256) != 0
         this.regA.set(result & 0xFF);
+        this.programCounter.increment()
     } else {
         const addr = this.getAddr(mode)
         this.io.address.set(addr);
diff --git a/runtime.ts b/runtime.ts
index 8ecfe1b..1527c62 100644
--- a/runtime.ts
+++ b/runtime.ts
@@ -59,11 +59,11 @@ function inspect() {
         }).join('\n'));
     
     console.log('\nregisters:')
-    console.log(` A:      ${cpu.regA.bits.reverse().map(k=>+k).join('')} (0x${cpu.regA.num().toString(16)})`)
-    console.log(` X:      ${cpu.regX.bits.reverse().map(k=>+k).join('')} (0x${cpu.regX.num().toString(16)})`)
-    console.log(` Y:      ${cpu.regY.bits.reverse().map(k=>+k).join('')} (0x${cpu.regY.num().toString(16)})`)
-    console.log(` SP:     ${cpu.stackPointer.bits.reverse().map(k=>+k).join('')} (0x${cpu.stackPointer.num().toString(16)})`)
-    console.log(` PC:     ${cpu.programCounter.bits.reverse().map(k=>+k).join('')} (0x${cpu.programCounter.num().toString(16)})`)
+    console.log(` A:  ${cpu.regA.bits.reverse().map(k=>+k).join('')} (0x${cpu.regA.num().toString(16)})`)
+    console.log(` X:  ${cpu.regX.bits.reverse().map(k=>+k).join('')} (0x${cpu.regX.num().toString(16)})`)
+    console.log(` Y:  ${cpu.regY.bits.reverse().map(k=>+k).join('')} (0x${cpu.regY.num().toString(16)})`)
+    console.log(` SP: ${cpu.stackPointer.bits.reverse().map(k=>+k).join('')} (0x${cpu.stackPointer.num().toString(16)})`)
+    console.log(` PC: ${cpu.programCounter.bits.reverse().map(k=>+k).join('')} (0x${cpu.programCounter.num().toString(16)})`)
 }
 
 const debug = Deno.args.includes('-d')
@@ -77,6 +77,7 @@ if (debug) {
 }
 
 // repeat until the cpu requests an interrupt
+mainloop:
 while (!cpu.io.interruptRequest.high) {
     const instId = ram[cpu.programCounter.num()]
         .toString(16)
@@ -106,33 +107,39 @@ while (!cpu.io.interruptRequest.high) {
             skip--
             break dbg;
         }
-        const i = new Uint8Array(8);
-        Deno.stdout.write(Uint8Array.from(['.'.charCodeAt(0)]))
-        await Deno.stdin.read(i);
-        if (i[0] == 'b'.charCodeAt(0)) {
-            console.log('BREAK!!')
-            break;
-        } else if (i[0] == 'i'.charCodeAt(0)) {
-            inspect()
-        } else if (i[0] == 's'.charCodeAt(0)) {
-            console.log('stack:')
-            for (let i = 0; i < cpu.stackPointer.num(); i++) {
-                console.log(` ${i.toString(16).padStart(2, '0')} ${ram[0x01FF - i].toString(2).padStart(8, '0')} (0x${ram[0x01FF - i].toString(16).padStart(4, '0')} ${ram[0x01FF - i].toString().padStart(3)})`)
-            }
-        } else if (i[0] == 'k'.charCodeAt(0)) {
-            const num = +(new TextDecoder().decode(i.slice(1, 7)).replaceAll('\0', ''));
-            if (Number.isNaN(num)) {
-                console.log('NaN')
-                break dbg;
-            }
-            skip = num;
-            console.log(`skipping ${num} cycles`)
-        } else if (i[0] == 'r'.charCodeAt(0)) {
-            const num = i[2] ? parseInt(new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', ''), 16) : cpu.programCounter.num();
-            console.log(`set breakpoint on`, num.toString(16))
-            breakpoints.push(num)
-        } else if (i[0] == '?'.charCodeAt(0)) {
-            console.log(`b - break, exit
+        dbgl:
+        while (true) {
+            const i = new Uint8Array(16);
+            Deno.stdout.write(Uint8Array.from(['.'.charCodeAt(0)]))
+            await Deno.stdin.read(i);
+            if (i[0] == 'b'.charCodeAt(0)) {
+                console.log('BREAK!!')
+                break mainloop;
+            } else if (i[0] == 'i'.charCodeAt(0)) {
+                inspect()
+                continue;
+            } else if (i[0] == 's'.charCodeAt(0)) {
+                console.log('stack:')
+                for (let i = 0; i < cpu.stackPointer.num(); i++) {
+                    console.log(` ${i.toString(16).padStart(2, '0')} ${ram[0x01FF - i].toString(2).padStart(8, '0')} (0x${ram[0x01FF - i].toString(16).padStart(4, '0')} ${ram[0x01FF - i].toString().padStart(3)})`)
+                }
+                continue;
+            } else if (i[0] == 'k'.charCodeAt(0)) {
+                const num = +(new TextDecoder().decode(i.slice(1, 7)).replaceAll('\0', ''));
+                if (Number.isNaN(num)) {
+                    console.log('NaN')
+                    break dbg;
+                }
+                skip = num;
+                console.log(`skipping ${num} cycles`)
+                break dbgl;
+            } else if (i[0] == 'r'.charCodeAt(0)) {
+                const num = i[2] ? parseInt(new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', ''), 16) : cpu.programCounter.num();
+                console.log(`set breakpoint on`, num.toString(16))
+                breakpoints.push(num)
+                continue;
+            } else if (i[0] == '?'.charCodeAt(0)) {
+                console.log(`b - break, exit
 i - inspect
 s - inspect stack
 c - continue
@@ -140,17 +147,54 @@ k[NUM] - skip
 r[ADR] - breakpoint
 g[ADR] - goto, change PC
 I[INS] - breakpoint instruction`);
-        } else if (i[0] == 'g'.charCodeAt(0)) {
-            const num = i[2] ? parseInt(new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', ''), 16) : cpu.programCounter.num();
-            console.log(`PC set to`, num.toString(16))
-            cpu.programCounter.set(num)
-        } else if (i[0] == 'I'.charCodeAt(0)) {
-            const instr = new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', '')
-            console.log(`instruction breakpoint set on`, instr)
-            instBreakpoints.push(instr)
-        } else if (i[0] == 'c'.charCodeAt(0)) {
-            skip = -1
-            console.log(`continuing execution`)
+                continue;
+            } else if (i[0] == 'g'.charCodeAt(0)) {
+                const num = i[2] ? parseInt(new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', ''), 16) : cpu.programCounter.num();
+                console.log(`PC set to`, num.toString(16))
+                cpu.programCounter.set(num)
+                continue;
+            } else if (i[0] == 'I'.charCodeAt(0)) {
+                const instr = new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', '')
+                console.log(`instruction breakpoint set on`, instr)
+                instBreakpoints.push(instr)
+                continue;
+            } else if (i[0] == 'c'.charCodeAt(0)) {
+                skip = -1
+                console.log(`continuing execution`)
+            } else if (i[0] == ':'.charCodeAt(0)) {
+                const instr = new TextDecoder().decode(i.slice(1, 15)).replace('\n', '').replaceAll('\0', '')
+                const match = [...instr.matchAll(/^([a-fA-F0-9]{1,4})=([a-fA-F0-9]{1,2})$/g)];
+                if (!match || !match[0]) {
+                    console.log('not matched, uhoh')
+                    continue;
+                }
+                const [_, addr, data] = match[0]
+                console.log(`set $${addr} to 0x${data}`)
+                ram[parseInt(addr, 16)] = parseInt(data, 16)
+                continue;
+            } else if (i[0] == '\\'.charCodeAt(0)) {
+                const num = parseInt(new TextDecoder().decode(i.slice(1, 7)).replace('\n', '').replaceAll('\0', ''), 16);
+                if (Number.isNaN(num))
+                    continue;
+                console.log(`${num.toString(16).padStart(4, '0')}: ${ram[num].toString(16).padStart(2, '0')}`)
+                continue;
+            } else if (i[0] == '\''.charCodeAt(0)) {
+                if (i[1] == '\n'.charCodeAt(0)) {
+                    ram[0x5000] = 0;
+                    ram[0x5001] = 0;
+                    console.log('reset')
+                    continue;
+                }
+                const num = parseInt(new TextDecoder().decode(i.slice(1, 3)).replace('\n', '').replaceAll('\0', ''), 16);
+                if (Number.isNaN(num))
+                    continue;
+                ram[0x5000] = num;
+                ram[0x5001] = 0x08;
+                console.log(`set $5000 to 0x${num.toString(16).padStart(2, '0')} and $5001 to 08`)
+                console.log('skipping 8 cycles');
+                skip = 8;
+            }
+            break;
         }
     }
     cpu.cycle();
diff --git a/thing.s b/thing.s
index e5df832..115f596 100644
--- a/thing.s
+++ b/thing.s
@@ -11,8 +11,8 @@ _start:
 CHROUT:
                 pha
                 sta     ACIA_DATA
-                lda     #$FF
-.txdelay:       dec
-                bne    .txdelay
+;                 lda     #$FF
+; .txdelay:       dec
+;                 bne    .txdelay
                 pla
                 rts
\ No newline at end of file
diff --git a/wozmon.s b/wozmon.s
new file mode 100644
index 0000000..4fd7d10
--- /dev/null
+++ b/wozmon.s
@@ -0,0 +1,208 @@
+ACIA_DATA	= $5000
+ACIA_STATUS	= $5001
+ACIA_CMD	= $5002
+ACIA_CTRL	= $5003
+
+XAML		= $24                   ; Last "opened" location Low
+XAMH		= $25                   ; Last "opened" location High
+STL		    = $26                   ; Store address Low
+STH		    = $27                   ; Store address High
+L		    = $28                   ; Hex value parsing Low
+H		    = $29                   ; Hex value parsing High
+YSAV		= $2A                   ; Used to see if hex value is given
+MODE		= $2B                   ; $00=XAM, $7F=STOR, $AE=BLOCK XAM
+
+IN		= $0200			; Input buffer
+
+RESET:
+                CLD                     ; Clear decimal arithmetic mode.
+                CLI
+                LDA     #<CHRIN
+                STA     $FFFE
+                LDA     #>CHRIN
+                STA     $FFFF
+                LDA     #$1F            ; 8-N-1, 19200 bps
+                STA     ACIA_CTRL
+                LDY     #$8B            ; No parity, no echo, no interrupts.
+                STY     ACIA_CMD
+
+NOTCR:
+                CMP     #$08            ; Backspace key?
+                BEQ     BACKSPACE       ; Yes.
+                CMP     #$1B            ; ESC?
+                BEQ     ESCAPE          ; Yes.
+                INY                     ; Advance text index.
+                BPL     NEXTCHAR        ; Auto ESC if line longer than 127.
+
+ESCAPE:
+                LDA     #$5C            ; "\".
+                JSR     ECHO            ; Output it.
+
+GETLINE:
+                LDA     #$0D            ; Send CR
+                JSR     ECHO
+                LDA     #$0A            ; Send LF
+                JSR     ECHO
+
+                LDY     #$01            ; Initialize text index.
+BACKSPACE:      DEY                     ; Back up text index.
+                BMI     GETLINE         ; Beyond start of line, reinitialize.
+
+NEXTCHAR:
+                LDA     ACIA_STATUS     ; Check status.
+                AND     #$08            ; Key ready?
+                BEQ     NEXTCHAR        ; Loop until ready.
+                LDA     ACIA_DATA       ; Load character. B7 will be '0'.
+                STA     IN,Y            ; Add to text buffer.
+                JSR     ECHO            ; Display character.
+                CMP     #$0D            ; CR?
+                BNE     NOTCR           ; No.
+
+                LDY     #$FF            ; Reset text index.
+                LDA     #$00            ; For XAM mode.
+                TAX                     ; X=0.
+SETBLOCK:
+                ASL
+SETSTOR:
+                ASL                     ; Leaves $7B if setting STOR mode.
+SETMODE:
+                STA     MODE            ; $00 = XAM, $74 = STOR, $B8 = BLOK XAM.
+BLSKIP:
+                INY                     ; Advance text index.
+NEXTITEM:
+                LDA     IN,Y            ; Get character.
+                CMP     #$0D            ; CR?
+                BEQ     GETLINE         ; Yes, done this line.
+                CMP     #$2E            ; "."?
+                BCC     BLSKIP          ; Skip delimiter.
+                BEQ     SETBLOCK        ; Set BLOCK XAM mode.
+                CMP     #$3A            ; ":"?
+                BEQ     SETSTOR         ; Yes, set STOR mode.
+                CMP     #$52            ; "R"?
+                BEQ     RUNPROG         ; Yes, run user program.
+                STX     L               ; $00 -> L.
+                STX     H               ;    and H.
+                STY     YSAV            ; Save Y for comparison
+
+NEXTHEX:
+                LDA     IN,Y            ; Get character for hex test.
+                EOR     #$30            ; Map digits to $0-9.
+                CMP     #$0A            ; Digit?
+                BCC     DIG             ; Yes.
+                ADC     #$88            ; Map letter "A"-"F" to $FA-FF.
+                CMP     #$FA            ; Hex letter?
+                BCC     NOTHEX          ; No, character not hex.
+DIG:
+                ASL
+                ASL                     ; Hex digit to MSD of A.
+                ASL
+                ASL
+
+                LDX     #$04            ; Shift count.
+HEXSHIFT:
+                ASL                     ; Hex digit left, MSB to carry.
+                ROL     L               ; Rotate into LSD.
+                ROL     H               ; Rotate into MSD's.
+                DEX                     ; Done 4 shifts?
+                BNE     HEXSHIFT        ; No, loop.
+                INY                     ; Advance text index.
+                BNE     NEXTHEX         ; Always taken. Check next character for hex.
+
+NOTHEX:
+                CPY     YSAV            ; Check if L, H empty (no hex digits).
+                BEQ     ESCAPE          ; Yes, generate ESC sequence.
+
+                BIT     MODE            ; Test MODE byte.
+                BVC     NOTSTOR         ; B6=0 is STOR, 1 is XAM and BLOCK XAM.
+
+                LDA     L               ; LSD's of hex data.
+                STA     (STL,X)         ; Store current 'store index'.
+                INC     STL             ; Increment store index.
+                BNE     NEXTITEM        ; Get next item (no carry).
+                INC     STH             ; Add carry to 'store index' high order.
+TONEXTITEM:     JMP     NEXTITEM        ; Get next command item.
+
+RUNPROG:
+                JMP     (XAML)          ; Run at current XAM index.
+
+NOTSTOR:
+                BMI     XAMNEXT         ; B7 = 0 for XAM, 1 for BLOCK XAM.
+
+                LDX     #$02            ; Byte count.
+SETADR:         LDA     L-1,X           ; Copy hex data to
+                STA     STL-1,X         ;  'store index'.
+                STA     XAML-1,X        ; And to 'XAM index'.
+                DEX                     ; Next of 2 bytes.
+                BNE     SETADR          ; Loop unless X = 0.
+
+NXTPRNT:
+                BNE     PRDATA          ; NE means no address to print.
+                LDA     #$0D            ; CR.
+                JSR     ECHO            ; Output it.
+                LDA     #$0A            ; LF.
+                JSR     ECHO            ; Output it.
+                LDA     XAMH            ; 'Examine index' high-order byte.
+                JSR     PRBYTE          ; Output it in hex format.
+                LDA     XAML            ; Low-order 'examine index' byte.
+                JSR     PRBYTE          ; Output it in hex format.
+                LDA     #$3A            ; ":".
+                JSR     ECHO            ; Output it.
+
+PRDATA:
+                LDA     #$20            ; Blank.
+                JSR     ECHO            ; Output it.
+                LDA     (XAML,X)        ; Get data byte at 'examine index'.
+                JSR     PRBYTE          ; Output it in hex format.
+XAMNEXT:        STX     MODE            ; 0 -> MODE (XAM mode).
+                LDA     XAML
+                CMP     L               ; Compare 'examine index' to hex data.
+                LDA     XAMH
+                SBC     H
+                BCS     TONEXTITEM      ; Not less, so no more data to output.
+
+                INC     XAML
+                BNE     MOD8CHK         ; Increment 'examine index'.
+                INC     XAMH
+
+MOD8CHK:
+                LDA     XAML            ; Check low-order 'examine index' byte
+                AND     #$07            ; For MOD 8 = 0
+                BPL     NXTPRNT         ; Always taken.
+
+PRBYTE:
+                PHA                     ; Save A for LSD.
+                LSR
+                LSR
+                LSR                     ; MSD to LSD position.
+                LSR
+                JSR     PRHEX           ; Output hex digit.
+                PLA                     ; Restore A.
+
+PRHEX:
+                AND     #$0F            ; Mask LSD for hex print.
+                ORA     #$30            ; Add "0".
+                CMP     #$3A            ; Digit?
+                BCC     ECHO            ; Yes, output it.
+                ADC     #$06            ; Add offset for letter.
+
+ECHO:
+                STA     ACIA_DATA       ; Output character.
+                PHA                     ; Save A.
+;                 LDA     #$FF            ; Initialize delay loop.
+; TXDELAY:        DEC                     ; Decrement A.
+;                 BNE     TXDELAY         ; Until A gets to 0.
+                PLA                     ; Restore A.
+                RTS                     ; Return.
+
+CHRIN:
+                RTI
+
+CHROUT:
+                pha
+                sta     ACIA_DATA
+;                 lda     #$FF
+; .txdelay:       dec
+;                 bne    .txdelay
+                pla
+                rts
+