Compare commits
2
Commits
f23796c4a9
...
49c471ece6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49c471ece6 | ||
|
|
38f6b95da8 |
+178
-136
@@ -1,5 +1,4 @@
|
|||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
import agent "vendor:agent"
|
|
||||||
|
|
||||||
struct Instruction
|
struct Instruction
|
||||||
full: u16
|
full: u16
|
||||||
@@ -24,9 +23,10 @@ struct Machine
|
|||||||
delay-timer: u8
|
delay-timer: u8
|
||||||
sound-timer: u8
|
sound-timer: u8
|
||||||
|
|
||||||
struct State
|
class State
|
||||||
screen-pixels: [2048 rl/Color]
|
screen-pixels: [2048 rl/Color]
|
||||||
screen-texture: rl/Texture2D
|
screen-texture: rl/Texture2D
|
||||||
|
audio-stream: rl/AudioStream
|
||||||
font: rl/Font
|
font: rl/Font
|
||||||
page: int
|
page: int
|
||||||
step-mode: bool
|
step-mode: bool
|
||||||
@@ -37,7 +37,7 @@ struct State
|
|||||||
skip-overflow-FX1E: bool
|
skip-overflow-FX1E: bool
|
||||||
inc-index-0xFX-55-65: bool
|
inc-index-0xFX-55-65: bool
|
||||||
|
|
||||||
const font: [80 u8] =
|
const font: [_ u8] =
|
||||||
[0xF0, 0x90, 0x90, 0x90, 0xF0, ;; 0
|
[0xF0, 0x90, 0x90, 0x90, 0xF0, ;; 0
|
||||||
0x20, 0x60, 0x20, 0x20, 0x70, ;; 1
|
0x20, 0x60, 0x20, 0x20, 0x70, ;; 1
|
||||||
0xF0, 0x10, 0xF0, 0x80, 0xF0, ;; 2
|
0xF0, 0x10, 0xF0, 0x80, 0xF0, ;; 2
|
||||||
@@ -55,7 +55,7 @@ const font: [80 u8] =
|
|||||||
0xF0, 0x80, 0xF0, 0x80, 0xF0, ;; E
|
0xF0, 0x80, 0xF0, 0x80, 0xF0, ;; E
|
||||||
0xF0, 0x80, 0xF0, 0x80, 0x80] ;; F
|
0xF0, 0x80, 0xF0, 0x80, 0x80] ;; F
|
||||||
|
|
||||||
const numbers: [16 [5 u8]] =
|
const numbers: [_ [_ u8]] =
|
||||||
[[0xF0, 0x90, 0x90, 0x90, 0xF0] ;; 0
|
[[0xF0, 0x90, 0x90, 0x90, 0xF0] ;; 0
|
||||||
[0x20, 0x60, 0x20, 0x20, 0x70] ;; 1
|
[0x20, 0x60, 0x20, 0x20, 0x70] ;; 1
|
||||||
[0xF0, 0x10, 0xF0, 0x80, 0xF0] ;; 2
|
[0xF0, 0x10, 0xF0, 0x80, 0xF0] ;; 2
|
||||||
@@ -73,19 +73,14 @@ const numbers: [16 [5 u8]] =
|
|||||||
[0xF0, 0x80, 0xF0, 0x80, 0xF0] ;; E
|
[0xF0, 0x80, 0xF0, 0x80, 0xF0] ;; E
|
||||||
[0xF0, 0x80, 0xF0, 0x80, 0x80]] ;; F
|
[0xF0, 0x80, 0xF0, 0x80, 0x80]] ;; F
|
||||||
|
|
||||||
const keypad-mapping: [16 u8] =
|
const keypad-mapping: [_ u8] =
|
||||||
[1, 2, 3, 0xC, 4, 5, 6, 0xD, 7, 8, 9, 0xE, 0xA, 0, 0xB, 0xF]
|
[1, 2, 3, 0xC, 4, 5, 6, 0xD, 7, 8, 9, 0xE, 0xA, 0, 0xB, 0xF]
|
||||||
|
|
||||||
const keypad-keymap: [16 rl/Key] =
|
const keypad-keymap: [_ rl/Key] =
|
||||||
[:key-one, :key-two, :key-three, :key-four,
|
[:key-one, :key-two, :key-three, :key-four,
|
||||||
:key-q, :key-w, :key-e, :key-r,
|
:key-q, :key-w, :key-e, :key-r,
|
||||||
:key-a, :key-s, :key-d, :key-f,
|
:key-a, :key-s, :key-d, :key-f,
|
||||||
:key-z, :key-x, :key-c, :key-v]
|
:key-z, :key-x, :key-c, :key-v]
|
||||||
|
|
||||||
const hex-numbers: str = "0123456789ABCDEF"
|
|
||||||
|
|
||||||
const reg-names: [16 str] = ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7",
|
|
||||||
"V8", "V9", "VA", "VB", "VC", "VD", "VE", "VF"]
|
|
||||||
|
|
||||||
;; let screen-width: int = 1300
|
;; let screen-width: int = 1300
|
||||||
;; let screen-height: int = 650
|
;; let screen-height: int = 650
|
||||||
@@ -96,18 +91,42 @@ let pixels-height: u8 = 32
|
|||||||
let ips: int = 700
|
let ips: int = 700
|
||||||
let cycles: f64 = 0.0
|
let cycles: f64 = 0.0
|
||||||
|
|
||||||
let cpu: Cpu = Cpu{}
|
once cpu: Cpu = Cpu{}
|
||||||
let machine: Machine = Machine{}
|
once machine: Machine = Machine{}
|
||||||
let state: State = State{}
|
once state: State = State{}
|
||||||
|
|
||||||
|
let max-samples = 512
|
||||||
|
max-samples-per-update = 4096
|
||||||
|
frequency: f64 = 440.0
|
||||||
|
audio-frequency: f64 = 440.0
|
||||||
|
sine-idx: f64 = 0.0
|
||||||
|
volume: f64
|
||||||
|
target-volume: f64
|
||||||
|
|
||||||
fn load-rom(path: str) -> ()
|
fn load-rom(path: str) -> ()
|
||||||
let rom = slurp(path)
|
let rom = slurp(path)
|
||||||
for i, b in rom
|
copy(machine.memory[0x200..], rom )
|
||||||
machine.memory[0x200 + i] = b
|
|
||||||
|
fn parse-instruction(pc: u16) -> Instruction
|
||||||
|
let byte1: u8 = machine.memory[pc]
|
||||||
|
let byte2: u8 = machine.memory[pc + 1]
|
||||||
|
let full = u16(byte1) << 8 | u16(byte2)
|
||||||
|
Instruction{.full full
|
||||||
|
.op (byte1 >> 4)
|
||||||
|
.x (byte1 & 0x0F)
|
||||||
|
.y (byte2 >> 4)
|
||||||
|
.n (byte2 & 0x0F)
|
||||||
|
.nn byte2
|
||||||
|
.nnn (full & 0x0FFF)}
|
||||||
|
|
||||||
fn reset() -> ()
|
fn reset() -> ()
|
||||||
cpu = Cpu{}
|
cpu = Cpu{}
|
||||||
machine = Machine{}
|
machine = Machine{}
|
||||||
|
;; load-rom("./IBM Logo.ch8")
|
||||||
|
;; load-rom("./vendor/chip8-test-suite/bin/1-chip8-logo.ch8")
|
||||||
|
;; load-rom("./vendor/chip8-test-suite/bin/2-ibm-logo.ch8")
|
||||||
|
;; load-rom("./vendor/chip8-test-suite/bin/3-corax+.ch8")
|
||||||
|
load-rom("./vendor/chip8-test-suite/bin/4-flags.ch8")
|
||||||
cpu.pc = 0x200
|
cpu.pc = 0x200
|
||||||
|
|
||||||
fn decode-instruction(inst: Instruction) -> str
|
fn decode-instruction(inst: Instruction) -> str
|
||||||
@@ -118,62 +137,49 @@ fn decode-instruction(inst: Instruction) -> str
|
|||||||
0xE0 -> "CLS"
|
0xE0 -> "CLS"
|
||||||
0xEE -> "RET"
|
0xEE -> "RET"
|
||||||
_ -> "UNKNOWN"
|
_ -> "UNKNOWN"
|
||||||
0x1 -> tformat("JP 0x{:03X}", inst.nnn)
|
0x1 -> format("JP 0x{:03X}", inst.nnn)
|
||||||
0x2 -> tformat("CALL 0x{:03X}", inst.nnn)
|
0x2 -> format("CALL 0x{:03X}", inst.nnn)
|
||||||
0x3 -> tformat("SE V{:X}, 0x{:02X}", inst.x, inst.nn)
|
0x3 -> format("SE V{:X}, 0x{:02X}", inst.x, inst.nn)
|
||||||
0x4 -> tformat("SNE V{:X}, 0x{:02X}", inst.x, inst.nn)
|
0x4 -> format("SNE V{:X}, 0x{:02X}", inst.x, inst.nn)
|
||||||
0x5 -> tformat("SE V{:X}, V{:X}", inst.x, inst.y)
|
0x5 -> format("SE V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x6 -> tformat("LD V{:X}, 0x{:02x}", inst.x, inst.nn)
|
0x6 -> format("LD V{:X}, 0x{:02x}", inst.x, inst.nn)
|
||||||
0x7 -> tformat("ADD V{:X}, 0x{:02x}", inst.x, inst.nn)
|
0x7 -> format("ADD V{:X}, 0x{:02x}", inst.x, inst.nn)
|
||||||
0x8 ->
|
0x8 ->
|
||||||
match inst.n
|
match inst.n
|
||||||
0x0 -> tformat("LD V{:X}, V{:X}", inst.x, inst.y)
|
0x0 -> format("LD V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x1 -> tformat("OR V{:X}, V{:X}", inst.x, inst.y)
|
0x1 -> format("OR V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x2 -> tformat("AND V{:X}, V{:X}", inst.x, inst.y)
|
0x2 -> format("AND V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x3 -> tformat("XOR V{:X}, V{:X}", inst.x, inst.y)
|
0x3 -> format("XOR V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x4 -> tformat("ADD V{:X}, V{:X}", inst.x, inst.y)
|
0x4 -> format("ADD V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x5 -> tformat("SUB V{:X}, V{:X}", inst.x, inst.y)
|
0x5 -> format("SUB V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x6 -> tformat("SHR V{:X}, V{:X}", inst.x, inst.y)
|
0x6 -> format("SHR V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0x7 -> tformat("SUBN V{:X}, V{:X}", inst.x, inst.y)
|
0x7 -> format("SUBN V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0xE -> tformat("SHL V{:X}, V{:X}", inst.x, inst.y)
|
0xE -> format("SHL V{:X}, V{:X}", inst.x, inst.y)
|
||||||
_ -> "UNKNOWN"
|
_ -> "UNKNOWN"
|
||||||
0x9 -> tformat("SNE V{:X}, V{:X}", inst.x, inst.y)
|
0x9 -> format("SNE V{:X}, V{:X}", inst.x, inst.y)
|
||||||
0xA -> tformat("LD I, 0x{:03X}", inst.nnn)
|
0xA -> format("LD I, 0x{:03X}", inst.nnn)
|
||||||
0xB -> tformat("JP V0, 0x{:03X}", inst.nnn)
|
0xB -> format("JP V0, 0x{:03X}", inst.nnn)
|
||||||
0xC -> tformat("RND V{:X}, 0x{:02x}", inst.x, inst.nn)
|
0xC -> format("RND V{:X}, 0x{:02x}", inst.x, inst.nn)
|
||||||
0xD -> tformat("DRW V{:X}, V{:X}, 0x{:X}", inst.x, inst.y, inst.n)
|
0xD -> format("DRW V{:X}, V{:X}, 0x{:X}", inst.x, inst.y, inst.n)
|
||||||
0xE ->
|
0xE ->
|
||||||
match inst.nn
|
match inst.nn
|
||||||
0x9E -> tformat("SKP V{:X}", inst.x)
|
0x9E -> format("SKP V{:X}", inst.x)
|
||||||
0xA1 -> tformat("SKNP V{:X}", inst.x)
|
0xA1 -> format("SKNP V{:X}", inst.x)
|
||||||
_ -> "UNKNOWN"
|
_ -> "UNKNOWN"
|
||||||
0xF ->
|
0xF ->
|
||||||
match inst.nn
|
match inst.nn
|
||||||
0x07 -> tformat("LD V{:X}, DT", inst.x)
|
0x07 -> format("LD V{:X}, DT", inst.x)
|
||||||
0x0A -> tformat("LD V{:X}, K", inst.x)
|
0x0A -> format("LD V{:X}, K", inst.x)
|
||||||
0x15 -> tformat("LD DT, V{:X}", inst.x)
|
0x15 -> format("LD DT, V{:X}", inst.x)
|
||||||
0x18 -> tformat("LD ST, V{:X}", inst.x)
|
0x18 -> format("LD ST, V{:X}", inst.x)
|
||||||
0x1E -> tformat("ADD I, V{:X}", inst.x)
|
0x1E -> format("ADD I, V{:X}", inst.x)
|
||||||
0x29 -> tformat("LD F, V{:X}", inst.x)
|
0x29 -> format("LD F, V{:X}", inst.x)
|
||||||
0x33 -> tformat("LD B, V{:X}", inst.x)
|
0x33 -> format("LD B, V{:X}", inst.x)
|
||||||
0x55 -> tformat("LD [I], V{:X}", inst.x)
|
0x55 -> format("LD [I], V{:X}", inst.x)
|
||||||
0x65 -> tformat("LD V{:X}, [I]", inst.x)
|
0x65 -> format("LD V{:X}, [I]", inst.x)
|
||||||
_ -> "UNKNOWN"
|
_ -> "UNKNOWN"
|
||||||
_ -> "UNKNOWN"
|
_ -> "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
fn parse-instruction(pc: u16) -> Instruction
|
|
||||||
let byte1: u8 = machine.memory[cpu.pc]
|
|
||||||
let byte2: u8 = machine.memory[cpu.pc + 1]
|
|
||||||
let full = u16(byte1) << 8 | u16(byte2)
|
|
||||||
Instruction{.full full
|
|
||||||
.op (byte1 >> 4)
|
|
||||||
.x (byte1 & 0x0F)
|
|
||||||
.y (byte2 >> 4)
|
|
||||||
.n (byte2 & 0x0F)
|
|
||||||
.nn byte2
|
|
||||||
.nnn (full & 0x0FFF)}
|
|
||||||
|
|
||||||
;; You should extract the execution from the other stuff here so you can manually
|
;; You should extract the execution from the other stuff here so you can manually
|
||||||
;; run an instruction when needed, all it needs is a parsed instruction
|
;; run an instruction when needed, all it needs is a parsed instruction
|
||||||
fn step() -> ()
|
fn step() -> ()
|
||||||
@@ -184,17 +190,15 @@ fn step() -> ()
|
|||||||
let inst = parse-instruction(cpu.pc)
|
let inst = parse-instruction(cpu.pc)
|
||||||
|
|
||||||
cpu.pc += 2
|
cpu.pc += 2
|
||||||
println("Executed: ", decode-instruction(inst), " || ",
|
|
||||||
"Next: ", decode-instruction(parse-instruction(cpu.pc + 1)))
|
|
||||||
match inst.op
|
match inst.op
|
||||||
0x0 ->
|
0x0 ->
|
||||||
match inst.nn
|
match inst.nn
|
||||||
;; TODO: Maybe this can be a draw command that clears the screen rather than special cased
|
;; TODO: Maybe this can be a draw command that clears the screen rather than special cased
|
||||||
0xE0 -> state.clear-screen = true
|
0xE0 -> state.clear-screen = true
|
||||||
0xEE ->
|
0xEE ->
|
||||||
cpu.pc = cpu.stack[cpu.sp]
|
|
||||||
cpu.sp -= 1
|
cpu.sp -= 1
|
||||||
_ -> println("Unrecognized 0x0 instruction")
|
cpu.pc = cpu.stack[cpu.sp]
|
||||||
|
_ -> print("Unrecognized 0x0 instruction")
|
||||||
0x1 -> cpu.pc = inst.nnn
|
0x1 -> cpu.pc = inst.nnn
|
||||||
0x2 -> ;; Call a subroutine
|
0x2 -> ;; Call a subroutine
|
||||||
cpu.stack[cpu.sp] = cpu.pc
|
cpu.stack[cpu.sp] = cpu.pc
|
||||||
@@ -204,7 +208,7 @@ fn step() -> ()
|
|||||||
0x4 -> if cpu.v[inst.x] != inst.nn then cpu.pc += 2
|
0x4 -> if cpu.v[inst.x] != inst.nn then cpu.pc += 2
|
||||||
0x5 -> if cpu.v[inst.x] == cpu.v[inst.y] then cpu.pc += 2
|
0x5 -> if cpu.v[inst.x] == cpu.v[inst.y] then cpu.pc += 2
|
||||||
0x6 -> cpu.v[inst.x] = inst.nn
|
0x6 -> cpu.v[inst.x] = inst.nn
|
||||||
0x7 -> cpu.v[inst.x] += inst.nn
|
0x7 -> cpu.v[inst.x] +%= inst.nn
|
||||||
0x8 ->
|
0x8 ->
|
||||||
match inst.n
|
match inst.n
|
||||||
0x0 -> cpu.v[inst.x] = cpu.v[inst.y]
|
0x0 -> cpu.v[inst.x] = cpu.v[inst.y]
|
||||||
@@ -216,24 +220,24 @@ fn step() -> ()
|
|||||||
cpu.v[inst.x] = u8(sum)
|
cpu.v[inst.x] = u8(sum)
|
||||||
cpu.v[0xF] = u8(sum >> 8)
|
cpu.v[0xF] = u8(sum >> 8)
|
||||||
0x5 ->
|
0x5 ->
|
||||||
let diff = u16(cpu.v[inst.x]) - u16(cpu.v[inst.y])
|
let diff = u16(cpu.v[inst.x]) -% u16(cpu.v[inst.y])
|
||||||
cpu.v[inst.x] = u8(diff)
|
cpu.v[inst.x] = u8(diff)
|
||||||
cpu.v[0xF] = u8(diff >> 8 & 0x1 ^ 1)
|
cpu.v[0xF] = u8(diff >> 8 & 0x1 ^ 1)
|
||||||
0x6 ->
|
0x6 ->
|
||||||
if state.shift-vx-swap
|
if state.shift-vx-swap
|
||||||
cpu.v[inst.x] = cpu.v[inst.y]
|
cpu.v[inst.x] = cpu.v[inst.y]
|
||||||
cpu.v[0xF] = cpu.v[inst.x] & 1
|
cpu.v[0xF] = cpu.v[inst.x] & 1
|
||||||
cpu.v[inst.x] = cpu.v[inst.x] >> 1
|
cpu.v[inst.x] >>= 1
|
||||||
0x7 ->
|
0x7 ->
|
||||||
let diff = u16(cpu.v[inst.y]) - u16(cpu.v[inst.x])
|
let diff = u16(cpu.v[inst.y]) -% u16(cpu.v[inst.x])
|
||||||
cpu.v[inst.x] = u8(diff)
|
cpu.v[inst.x] = u8(diff)
|
||||||
cpu.v[0xF] = u8(diff >> 8 & 0x1 ^ 1)
|
cpu.v[0xF] = u8(diff >> 8 & 0x1 ^ 1)
|
||||||
0xE ->
|
0xE ->
|
||||||
if state.shift-vx-swap
|
if state.shift-vx-swap
|
||||||
cpu.v[inst.x] = cpu.v[inst.y]
|
cpu.v[inst.x] = cpu.v[inst.y]
|
||||||
cpu.v[0xF] = (cpu.v[inst.x] & 0x80) >> 7
|
cpu.v[0xF] = (cpu.v[inst.x] & 0x80) >> 7
|
||||||
cpu.v[inst.x] = cpu.v[inst.x] << 1
|
cpu.v[inst.x] <<= 1
|
||||||
_ -> println("Unrecognized 0x8 instruction")
|
_ -> print("Unrecognized 0x8 instruction")
|
||||||
0x9 -> if cpu.v[inst.x] != cpu.v[inst.y] then cpu.pc += 2
|
0x9 -> if cpu.v[inst.x] != cpu.v[inst.y] then cpu.pc += 2
|
||||||
0xA -> cpu.index = inst.nnn
|
0xA -> cpu.index = inst.nnn
|
||||||
0xB -> cpu.pc = inst.nnn + if state.jump-xnn then cpu.v[inst.x] else cpu.v[0]
|
0xB -> cpu.pc = inst.nnn + if state.jump-xnn then cpu.v[inst.x] else cpu.v[0]
|
||||||
@@ -243,10 +247,10 @@ fn step() -> ()
|
|||||||
y = cpu.v[inst.y] % u8(pixels-height)
|
y = cpu.v[inst.y] % u8(pixels-height)
|
||||||
collision = false
|
collision = false
|
||||||
|
|
||||||
for row in 0..inst.n
|
loop row in 0..inst.n
|
||||||
let byte = machine.memory[cpu.index + row]
|
let byte = machine.memory[cpu.index + row]
|
||||||
let idx = i32(x) + (row + i32(y)) * i32(pixels-width)
|
let idx = i32(x) + (row + i32(y)) * i32(pixels-width)
|
||||||
for bit in reverse(u8(0)..8)
|
loop bit in reverse(0..8)
|
||||||
if byte >> bit & 0x1 == 1 and idx < length(machine.vbuffer)
|
if byte >> bit & 0x1 == 1 and idx < length(machine.vbuffer)
|
||||||
if machine.vbuffer[idx] then collision = true
|
if machine.vbuffer[idx] then collision = true
|
||||||
machine.vbuffer[idx] = true
|
machine.vbuffer[idx] = true
|
||||||
@@ -255,11 +259,10 @@ fn step() -> ()
|
|||||||
cpu.v[0xF] = if collision then 1 else 0
|
cpu.v[0xF] = if collision then 1 else 0
|
||||||
0xE ->
|
0xE ->
|
||||||
let pressed = machine.keypad[inst.x]
|
let pressed = machine.keypad[inst.x]
|
||||||
if (inst.nn == 0x9E and pressed) or (inst.nn == 0xA1 and not pressed)
|
match inst.nn
|
||||||
cpu.pc += 2
|
0x9E -> if pressed then cpu.pc += 2
|
||||||
;; TODO: When we have better pattern matching, do it with that so we can this catch error
|
0xA1 -> if not pressed then cpu.pc += 2
|
||||||
if inst.nn != 0x9E or inst.nn != 0xA1
|
_ -> print("Unrecognized 0xE instruction")
|
||||||
println("Unrecognized 0x8 instruction")
|
|
||||||
0xF ->
|
0xF ->
|
||||||
match inst.nn
|
match inst.nn
|
||||||
0x07 -> cpu.v[inst.x] = machine.delay-timer
|
0x07 -> cpu.v[inst.x] = machine.delay-timer
|
||||||
@@ -278,25 +281,25 @@ fn step() -> ()
|
|||||||
machine.memory[cpu.index + 1] = vx % 100 / 10
|
machine.memory[cpu.index + 1] = vx % 100 / 10
|
||||||
machine.memory[cpu.index + 2] = vx % 10
|
machine.memory[cpu.index + 2] = vx % 10
|
||||||
0x55 ->
|
0x55 ->
|
||||||
for i in 0..=inst.x
|
loop i in 0..=inst.x
|
||||||
machine.memory[cpu.index + i] = cpu.v[i]
|
machine.memory[cpu.index + i] = cpu.v[i]
|
||||||
if state.inc-index-0xFX-55-65
|
if state.inc-index-0xFX-55-65
|
||||||
cpu.index += inst.x
|
cpu.index += inst.x
|
||||||
0x65 ->
|
0x65 ->
|
||||||
for i in 0..=inst.x
|
loop i in 0..=inst.x
|
||||||
cpu.v[i] = machine.memory[cpu.index + i]
|
cpu.v[i] = machine.memory[cpu.index + i]
|
||||||
if state.inc-index-0xFX-55-65
|
if state.inc-index-0xFX-55-65
|
||||||
cpu.index += inst.x
|
cpu.index += inst.x
|
||||||
_ -> println("Unrecognized 0xF instruction")
|
_ -> print("Unrecognized 0xF instruction")
|
||||||
_ -> println("Instruction not handled: ", inst.op)
|
_ -> print("Instruction not handled: {:X}", inst.op)
|
||||||
|
|
||||||
fn run-update() -> ()
|
fn run-update() -> ()
|
||||||
for i, key in keypad-keymap
|
loop i, key in keypad-keymap
|
||||||
if rl/is-key-down(key) and not state.step-mode
|
if rl/is-key-down(key) and not state.step-mode
|
||||||
machine.keypad[keypad-mapping[i]] = true
|
machine.keypad[keypad-mapping[i]] = true
|
||||||
if state.pending-key as reg
|
if state.pending-key as reg
|
||||||
cpu.v[reg] = u8(i)
|
cpu.v[reg] = u8(i)
|
||||||
state.pending-key = None
|
state.pending-key = nil
|
||||||
|
|
||||||
if rl/is-key-pressed(:key-minus)
|
if rl/is-key-pressed(:key-minus)
|
||||||
state.page = max(0, state.page - 1)
|
state.page = max(0, state.page - 1)
|
||||||
@@ -305,41 +308,48 @@ fn run-update() -> ()
|
|||||||
|
|
||||||
if rl/is-key-pressed(:key-r)
|
if rl/is-key-pressed(:key-r)
|
||||||
reset()
|
reset()
|
||||||
load-rom("./IBM Logo.ch8")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if rl/is-key-pressed(:key-space)
|
||||||
|
state.step-mode = not state.step-mode
|
||||||
|
|
||||||
|
;; Generate sound
|
||||||
|
if machine.sound-timer > 0
|
||||||
|
target-volume = 1.0
|
||||||
|
machine.sound-timer -= 1
|
||||||
|
else
|
||||||
|
target-volume = 0.0
|
||||||
|
|
||||||
if state.pending-key?
|
if state.pending-key?
|
||||||
if machine.delay-timer > 0 then machine.delay-timer -= 1
|
if machine.delay-timer > 0 then machine.delay-timer -= 1
|
||||||
if machine.sound-timer > 0 then machine.sound-timer -= 1
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if state.step-mode
|
if state.step-mode
|
||||||
if rl/is-key-pressed(:key-n)
|
if rl/is-key-pressed(:key-n)
|
||||||
step()
|
step()
|
||||||
|
if rl/is-key-pressed-repeat(:key-n)
|
||||||
|
step()
|
||||||
else
|
else
|
||||||
cycles += f64(rl/get-frame-time()) * ips
|
cycles += f64(rl/get-frame-time()) * ips
|
||||||
|
|
||||||
while cycles >= 1.0
|
loop cycles >= 1.0
|
||||||
step()
|
step()
|
||||||
cycles -= 1.0
|
cycles -= 1.0
|
||||||
|
|
||||||
if state.clear-screen
|
if state.clear-screen
|
||||||
println("cleared screen")
|
|
||||||
state.clear-screen = false
|
state.clear-screen = false
|
||||||
for i,pixel in machine.vbuffer
|
fill(state.screen-pixels, rl/black)
|
||||||
state.screen-pixels[i] = rl/black
|
|
||||||
else
|
else
|
||||||
for i,pixel in machine.vbuffer
|
loop i,pixel in machine.vbuffer
|
||||||
state.screen-pixels[i] = if pixel then rl/white else rl/black
|
state.screen-pixels[i] = if pixel then rl/white else rl/black
|
||||||
|
|
||||||
;; for i,pixel in machine.vbuffer
|
;; loop i,pixel in machine.vbuffer
|
||||||
;; if i % 2 == 0 and i / pixels-width % 2 == 0 or i % 2 == 1 and i / pixels-width % 2 == 1
|
;; if i % 2 == 0 and i / pixels-width % 2 == 0 or i % 2 == 1 and i / pixels-width % 2 == 1
|
||||||
;; state.screen-pixels[i] = rl/white
|
;; state.screen-pixels[i] = rl/white
|
||||||
|
|
||||||
rl/update-texture(state.screen-texture, Ptr(const u8)(addr(state.screen-pixels)))
|
rl/update-texture(state.screen-texture, state.screen-pixels)
|
||||||
|
|
||||||
if machine.delay-timer > 0 then machine.delay-timer -= 1
|
if machine.delay-timer > 0 then machine.delay-timer -= 1
|
||||||
if machine.sound-timer > 0 then machine.sound-timer -= 1
|
|
||||||
|
|
||||||
fn draw-text-normal(text: str, x: $t, y: $t, color: rl/Color) -> () where is-numeric($t)
|
fn draw-text-normal(text: str, x: $t, y: $t, color: rl/Color) -> () where is-numeric($t)
|
||||||
rl/draw-text-ex(state.font, text, rl/Vector2(f32(x), f32(y)), 21, 0.0, color)
|
rl/draw-text-ex(state.font, text, rl/Vector2(f32(x), f32(y)), 21, 0.0, color)
|
||||||
@@ -349,27 +359,39 @@ fn run-drawing() -> ()
|
|||||||
|
|
||||||
draw-text-normal("Keypad", 10, 200, rl/white)
|
draw-text-normal("Keypad", 10, 200, rl/white)
|
||||||
let key-size = 25
|
let key-size = 25
|
||||||
for i, key in keypad-keymap
|
loop i, key in keypad-keymap
|
||||||
let x = 10 + (key-size * (i % 4))
|
let x = 10 + (key-size * (i % 4))
|
||||||
y = 230 + (key-size * (i / 4))
|
y = 230 + (key-size * (i / 4))
|
||||||
color = if machine.keypad[keypad-mapping[i]] then rl/green else rl/white
|
color = if machine.keypad[keypad-mapping[i]] then rl/green else rl/white
|
||||||
rl/draw-rectangle-lines(x, y, key-size, key-size, color)
|
rl/draw-rectangle-lines(x, y, key-size, key-size, color)
|
||||||
;; TODO: Maybe we don't need hex-numbers anymore?
|
draw-text-normal(format("{:X}", i), x + 4, y + 4, color)
|
||||||
draw-text-normal(hex-numbers[i..i + 1], x + 4, y + 4, color)
|
|
||||||
|
|
||||||
draw-text-normal("Actual", 150, 200, rl/white)
|
;; draw-text-normal("Actual", 150, 200, rl/white)
|
||||||
for i,key in machine.keypad
|
;; loop i,key in machine.keypad
|
||||||
let x = 150 + (key-size * (i % 4))
|
;; let x = 150 + (key-size * (i % 4))
|
||||||
y = 230 + (key-size * (i / 4))
|
;; y = 230 + (key-size * (i / 4))
|
||||||
color = if machine.keypad[i] then rl/green else rl/white
|
;; color = if machine.keypad[i] then rl/green else rl/white
|
||||||
rl/draw-rectangle-lines(x, y, key-size, key-size, color)
|
;; rl/draw-rectangle-lines(x, y, key-size, key-size, color)
|
||||||
draw-text-normal("0123456789ABCDEF"[i..i + 1], x + 4, y + 4, color)
|
;; draw-text-normal("0123456789ABCDEF"[i..i + 1], x + 4, y + 4, color)
|
||||||
|
|
||||||
|
;; Instructions
|
||||||
|
draw-text-normal("Instructions", 125, 200, rl/white)
|
||||||
|
rl/draw-rectangle-lines(125, 230, 150, 100, rl/white)
|
||||||
|
rl/draw-rectangle(128, 258, 140, 20, rl/darkgreen)
|
||||||
|
loop i in 0..4
|
||||||
|
if cpu.pc <= 0 and i == 0
|
||||||
|
continue
|
||||||
|
|
||||||
|
let inst = parse-instruction(cpu.pc + u8(i) * 2 - 2)
|
||||||
|
text = decode-instruction(inst)
|
||||||
|
if text != "UNKNOWN"
|
||||||
|
draw-text-normal(text, 130, 235 + i * 23, rl/white)
|
||||||
|
|
||||||
;; Registers
|
;; Registers
|
||||||
let reg-x = 290
|
let reg-x = 290
|
||||||
for i in 0..16
|
loop i in 0..16
|
||||||
;; draw-text-normal(str(i64->bytes(i64(cpu.v[i]))), 520, 60 + 26 * i, rl/white)
|
;; draw-text-normal(str(i64->bytes(i64(cpu.v[i]))), 520, 60 + 26 * i, rl/white)
|
||||||
let reg-val = tformat("V{:X}=0x{:02X}", i, cpu.v[i])
|
let reg-val = format("V{:X}=0x{:02X}", i, cpu.v[i])
|
||||||
draw-text-normal(reg-val, reg-x, 200 + 21 * i, rl/white)
|
draw-text-normal(reg-val, reg-x, 200 + 21 * i, rl/white)
|
||||||
|
|
||||||
;; Texture
|
;; Texture
|
||||||
@@ -379,26 +401,28 @@ fn run-drawing() -> ()
|
|||||||
rl/draw-texture-pro(state.screen-texture, src, dst, rl/Vector2(0.0, 0.0), 0.0, rl/white)
|
rl/draw-texture-pro(state.screen-texture, src, dst, rl/Vector2(0.0, 0.0), 0.0, rl/white)
|
||||||
rl/draw-rectangle-lines(i32(dst.x), i32(dst.y), i32(dst.width), i32(dst.height), rl/white)
|
rl/draw-rectangle-lines(i32(dst.x), i32(dst.y), i32(dst.width), i32(dst.height), rl/white)
|
||||||
|
|
||||||
for i in 0..16
|
;; Stack
|
||||||
|
loop i in 0..16
|
||||||
let row = i % 8
|
let row = i % 8
|
||||||
col = i / 8
|
col = i / 8
|
||||||
x = 10
|
x = 10
|
||||||
y = 20 * row
|
y = 20 * row
|
||||||
w = 80
|
w = 70
|
||||||
h = 20
|
h = 20
|
||||||
start-y = 360
|
start-y = 360
|
||||||
idx-txt = tformat(" {:X}|", i)
|
idx-txt = format("{:X}|", i)
|
||||||
rl/draw-rectangle-lines(x + col * w, start-y + y, w, h, rl/white)
|
rl/draw-rectangle-lines(x + col * w, start-y + y, w, h, rl/white)
|
||||||
draw-text-normal(idx-txt, x + col * w, start-y + y, rl/white)
|
draw-text-normal(idx-txt, x + 4 + col * w, start-y + y, rl/white)
|
||||||
if i < cpu.sp
|
if i < cpu.sp
|
||||||
let addr-txt = tformat("0x{:02X}", cpu.stack[i])
|
let addr-txt = format("0x{:02X}", cpu.stack[i])
|
||||||
draw-text-normal(addr-txt, x + 33 + col * w, start-y + y, rl/white)
|
draw-text-normal(addr-txt, x + 24 + col * w, start-y + y, rl/white)
|
||||||
|
|
||||||
let x = 195
|
;; Additional data
|
||||||
draw-text-normal(tformat(" i=0x{:02X}", cpu.index), x, 360, rl/white)
|
let x = 185
|
||||||
draw-text-normal(tformat("pc=0x{:02X}", cpu.pc), x, 380, rl/white)
|
draw-text-normal(format(" i=0x{:02X}", cpu.index), x, 360, rl/white)
|
||||||
draw-text-normal(tformat("dt={}", machine.delay-timer), x, 400, rl/white)
|
draw-text-normal(format("pc=0x{:02X}", cpu.pc), x, 380, rl/white)
|
||||||
draw-text-normal(tformat("st={}", machine.sound-timer), x, 420, rl/white)
|
draw-text-normal(format("dt={}", machine.delay-timer), x, 400, rl/white)
|
||||||
|
draw-text-normal(format("st={}", machine.sound-timer), x, 420, rl/white)
|
||||||
|
|
||||||
;; Hex dump
|
;; Hex dump
|
||||||
let hex-rows = 32
|
let hex-rows = 32
|
||||||
@@ -412,21 +436,21 @@ fn run-drawing() -> ()
|
|||||||
grid-h = hex-rows * cell-h + padding * 2
|
grid-h = hex-rows * cell-h + padding * 2
|
||||||
slots-per-page = hex-rows * hex-cols
|
slots-per-page = hex-rows * hex-cols
|
||||||
|
|
||||||
for i in 0..4096 / slots-per-page + 1
|
loop i in 0..4096 / slots-per-page + 1
|
||||||
let color = if state.page == i then rl/green else rl/white
|
let color = if state.page == i then rl/green else rl/white
|
||||||
tab-w = 80
|
tab-w = 80
|
||||||
start-num = i * slots-per-page
|
start-num = i * slots-per-page
|
||||||
end-num = min(4096, (i + 1) * slots-per-page)
|
end-num = min(4096, (i + 1) * slots-per-page)
|
||||||
tab-name = tformat("{:03X}-{:03X}", start-num, end-num - 1)
|
tab-name = format("{:03X}-{:03X}", start-num, end-num - 1)
|
||||||
rl/draw-rectangle(grid-x - 1 + i * tab-w + (i * 2), grid-y - 20, tab-w, 20, color)
|
rl/draw-rectangle(grid-x - 1 + i * tab-w + (i * 2), grid-y - 20, tab-w, 20, color)
|
||||||
draw-text-normal(tab-name, grid-x + 1 + i * tab-w + (i * 2), grid-y - 20, rl/black)
|
draw-text-normal(tab-name, grid-x + 1 + i * tab-w + (i * 2), grid-y - 20, rl/black)
|
||||||
|
|
||||||
rl/draw-rectangle-lines(grid-x, grid-y, grid-w, grid-h, rl/white)
|
rl/draw-rectangle-lines(grid-x, grid-y, grid-w, grid-h, rl/white)
|
||||||
for r in 0..hex-rows
|
loop r in 0..hex-rows
|
||||||
for c in 0..hex-cols
|
loop c in 0..hex-cols
|
||||||
let idx = c + r * hex-cols + hex-cols * hex-rows * state.page
|
let idx = c + r * hex-cols + hex-cols * hex-rows * state.page
|
||||||
if idx < length(machine.memory)
|
if idx < length(machine.memory)
|
||||||
let reg-val = tformat("{:02X}", machine.memory[idx])
|
let reg-val = format("{:02X}", machine.memory[idx])
|
||||||
x-padding = if c % 2 == 0 then 2 else -2
|
x-padding = if c % 2 == 0 then 2 else -2
|
||||||
x = grid-x + x-padding + c * cell-w
|
x = grid-x + x-padding + c * cell-w
|
||||||
y = grid-y + padding + r * cell-h
|
y = grid-y + padding + r * cell-h
|
||||||
@@ -436,12 +460,15 @@ fn run-drawing() -> ()
|
|||||||
rl/draw-rectangle(x, y + 2, 19, 15, rl/red)
|
rl/draw-rectangle(x, y + 2, 19, 15, rl/red)
|
||||||
draw-text-normal(reg-val, x, y, rl/white)
|
draw-text-normal(reg-val, x, y, rl/white)
|
||||||
|
|
||||||
comment:
|
fn audio-callback-fn(buffer: Ptr(u8), frames: u32) -> ()
|
||||||
cpu.stack[0] = 0xAA
|
audio-frequency = frequency + (audio-frequency - frequency) * 0.95
|
||||||
cpu.stack[1] = 0xAB
|
let incr = audio-frequency / 44100.0
|
||||||
cpu.stack[2] = 0xAC
|
d = slice-from(Ptr(i16)(buffer), i32(frames))
|
||||||
cpu.stack[3] = 0xAD
|
loop i in 0..frames
|
||||||
cpu.sp = 15
|
volume += (target-volume - volume) * 0.002
|
||||||
|
d[i] = i16(32000.0 * volume * sin(2 * pi * sine-idx))
|
||||||
|
sine-idx += incr
|
||||||
|
if sine-idx > 1.0 then sine-idx -= 1.0
|
||||||
|
|
||||||
fn main() -> i32
|
fn main() -> i32
|
||||||
rl/set-trace-log-level(:log-warning)
|
rl/set-trace-log-level(:log-warning)
|
||||||
@@ -449,6 +476,15 @@ fn main() -> i32
|
|||||||
defer rl/close-window()
|
defer rl/close-window()
|
||||||
rl/set-target-fps(60)
|
rl/set-target-fps(60)
|
||||||
|
|
||||||
|
rl/init-audio-device()
|
||||||
|
defer rl/close-audio-device()
|
||||||
|
rl/set-audio-stream-buffer-size-default(max-samples-per-update)
|
||||||
|
;; Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono)
|
||||||
|
state.audio-stream = rl/load-audio-stream(44100, 16, 1)
|
||||||
|
defer rl/unload-audio-stream(state.audio-stream)
|
||||||
|
rl/set-audio-stream-callback(state.audio-stream, audio-callback-fn)
|
||||||
|
rl/play-audio-stream(state.audio-stream)
|
||||||
|
|
||||||
let font = rl/load-font-ex("JetBrainsMono-Regular.otf", 21, [])
|
let font = rl/load-font-ex("JetBrainsMono-Regular.otf", 21, [])
|
||||||
defer rl/unload-font(state.font)
|
defer rl/unload-font(state.font)
|
||||||
state.font = font
|
state.font = font
|
||||||
@@ -460,7 +496,7 @@ fn main() -> i32
|
|||||||
rl/unload-image(img)
|
rl/unload-image(img)
|
||||||
rl/set-texture-filter(state.screen-texture, :filter-point)
|
rl/set-texture-filter(state.screen-texture, :filter-point)
|
||||||
|
|
||||||
until rl/window-should-close()
|
loop not rl/window-should-close()
|
||||||
restart-case
|
restart-case
|
||||||
agent/poll()
|
agent/poll()
|
||||||
run-update()
|
run-update()
|
||||||
@@ -473,9 +509,15 @@ fn main() -> i32
|
|||||||
|
|
||||||
comment:
|
comment:
|
||||||
reset()
|
reset()
|
||||||
load-rom("./IBM Logo.ch8")
|
loop i,pixel in machine.vbuffer
|
||||||
for i,pixel in machine.vbuffer
|
|
||||||
if pixel
|
if pixel
|
||||||
println("Row:", i / pixels-width, "Col:", i % pixels-width)
|
print("Row:", i / pixels-width, "Col:", i % pixels-width)
|
||||||
|
state.step-mode = false
|
||||||
|
state.step-mode = true
|
||||||
|
machine.sound-timer = 120
|
||||||
|
cpu.stack[0] = 0xAA
|
||||||
|
cpu.stack[1] = 0xAB
|
||||||
|
cpu.stack[2] = 0xAC
|
||||||
|
cpu.stack[3] = 0xAD
|
||||||
|
cpu.sp = 15
|
||||||
machine.vbuffer[8 + 12 * pixels-width + 1] = false
|
machine.vbuffer[8 + 12 * pixels-width + 1] = false
|
||||||
|
|
||||||
Reference in new issue
Block a user