From a9323bb7d9c6e2ce604e3601355e2c27feb6fa7c Mon Sep 17 00:00:00 2001 From: jakobjesenko Date: Sun, 7 Dec 2025 21:46:10 +0100 Subject: [PATCH] simulator --- sim/README.md | 24 ++++ sim/gleam.toml | 21 ++++ sim/manifest.toml | 13 +++ sim/src/sim.gleam | 8 ++ sim/src/sim/loader.gleam | 61 ++++++++++ sim/src/sim/machine.gleam | 223 ++++++++++++++++++++++++++++++++++++ sim/src/sim/memory.gleam | 61 ++++++++++ sim/src/sim/register.gleam | 6 + sim/src/sim/registers.gleam | 29 +++++ sim/test/sim_test.gleam | 13 +++ 10 files changed, 459 insertions(+) create mode 100644 sim/README.md create mode 100644 sim/gleam.toml create mode 100644 sim/manifest.toml create mode 100644 sim/src/sim.gleam create mode 100644 sim/src/sim/loader.gleam create mode 100644 sim/src/sim/machine.gleam create mode 100644 sim/src/sim/memory.gleam create mode 100644 sim/src/sim/register.gleam create mode 100644 sim/src/sim/registers.gleam create mode 100644 sim/test/sim_test.gleam diff --git a/sim/README.md b/sim/README.md new file mode 100644 index 0000000..c8c8ab2 --- /dev/null +++ b/sim/README.md @@ -0,0 +1,24 @@ +# sim + +[![Package Version](https://img.shields.io/hexpm/v/sim)](https://hex.pm/packages/sim) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/sim/) + +```sh +gleam add sim@1 +``` +```gleam +import sim + +pub fn main() -> Nil { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +``` diff --git a/sim/gleam.toml b/sim/gleam.toml new file mode 100644 index 0000000..bfced93 --- /dev/null +++ b/sim/gleam.toml @@ -0,0 +1,21 @@ +name = "sim" +version = "1.0.0" + + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "", repo = "" } +# links = [{ title = "Website", href = "" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.44.0 and < 2.0.0" +file_streams = ">= 1.7.0 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/sim/manifest.toml b/sim/manifest.toml new file mode 100644 index 0000000..bcc481c --- /dev/null +++ b/sim/manifest.toml @@ -0,0 +1,13 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "file_streams", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "file_streams", source = "hex", outer_checksum = "62757932B5FAC14FC9D0EE69BC4694DC6EA9F0FE6E198F695711C4FCAC1F7A26" }, + { name = "gleam_stdlib", version = "0.67.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6CE3E4189A8B8EC2F73AB61A2FBDE49F159D6C9C61C49E3B3082E439F260D3D0" }, + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, +] + +[requirements] +file_streams = { version = ">= 1.7.0 and < 2.0.0" } +gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/sim/src/sim.gleam b/sim/src/sim.gleam new file mode 100644 index 0000000..657833b --- /dev/null +++ b/sim/src/sim.gleam @@ -0,0 +1,8 @@ +import sim/machine + +pub fn main() -> Nil { + let machine = machine.init("../arith.obj") + echo machine + machine.step(machine, machine.get_instructions()) + Nil +} diff --git a/sim/src/sim/loader.gleam b/sim/src/sim/loader.gleam new file mode 100644 index 0000000..151ca1c --- /dev/null +++ b/sim/src/sim/loader.gleam @@ -0,0 +1,61 @@ +import file_streams/file_stream +import gleam/bit_array +import gleam/list +import gleam/result +import gleam/string +import sim/memory + +type RecordType { + H + T + E +} + +fn record_from_string(str: String) -> RecordType { + case str { + "H" <> _ -> H + "T" <> _ -> T + "E" <> _ | _ -> E + } +} + +type Record { + Record(record_type: RecordType, content: BitArray) +} + +fn read_record(stream: file_stream.FileStream) -> Record { + let assert Ok(line) = file_stream.read_line(stream) + Record( + record_from_string(line), + result.unwrap( + bit_array.base16_decode(string.drop_end(string.drop_start(line, 1), 1)), + <<0>>, + ), + ) +} + +fn read_section(stream: file_stream.FileStream) -> List(Record) { + let record = read_record(stream) + case record.record_type { + E -> [record] + _ -> [record, ..read_section(stream)] + } +} + +pub fn load_obj(file: String) -> memory.Memory { + let assert Ok(stream) = file_stream.open_read(file) + let section = read_section(stream) + echo section + list.fold(section, <<0>>, fn(memory, record) -> memory.Memory { + case record.record_type { + T -> { + let assert <> = record.content + echo position + echo bit_array.base16_encode(value) + memory.insert_at(memory, position, value) + } + _ -> memory + } + |> echo + }) +} diff --git a/sim/src/sim/machine.gleam b/sim/src/sim/machine.gleam new file mode 100644 index 0000000..9c74ca5 --- /dev/null +++ b/sim/src/sim/machine.gleam @@ -0,0 +1,223 @@ +import gleam/list +import gleam/result +import sim/loader +import sim/memory +import sim/registers + +pub type Machine { + Machine(memory: memory.Memory, registers: registers.Registers) +} + +pub fn init(program: String) -> Machine { + Machine(loader.load_obj(program), registers.new()) +} + +fn fetch_instruction( + instructions: List(Def), + memory: memory.Memory, + ptr: Int, +) -> Instruction { + let char = memory.get_char_at(memory, ptr) + let def = get_def(instructions, char) + let inst = case def.size { + 1 -> Instruction(1, char, def.modifier) + 2 -> + Instruction( + 2, + << + char:bits, + memory.get_char_at(memory, ptr + 1):bits, + >>, + def.modifier, + ) + 3 | _ -> Instruction(3, memory.get_int_at(memory, ptr), def.modifier) + } + inst +} + +pub fn step(machine: Machine, instructions: List(Def)) -> Machine { + let assert <> = machine.registers.pc + + fetch_instruction(instructions, machine.memory, pc) + + machine +} + +pub type Instruction { + Instruction( + size: Int, + source: BitArray, + modifier: fn(Machine, BitArray) -> Machine, + ) +} + +pub type Def { + Def( + opcode: Int, + size: Int, + mnemonic: String, + modifier: fn(Machine, BitArray) -> Machine, + ) +} + +pub fn get_instructions() -> List(Def) { + [ + Def(6, 3, "ADD", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(22, 3, "ADDF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(36, 2, "ADDR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(16, 3, "AND", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(45, 2, "CLEAR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(10, 3, "COMP", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(34, 3, "COMPF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(40, 2, "COMPR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(9, 3, "DIV", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(25, 3, "DIVF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(39, 2, "DIVR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(49, 1, "FIX", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(48, 1, "FLOAT", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(61, 1, "HIO", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(15, 3, "J", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(12, 3, "JEQ", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(13, 3, "JGT", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(14, 3, "JLT", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(18, 3, "JSUB", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(0, 3, "LDA", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(26, 3, "LDB", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(20, 3, "LDCH", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(28, 3, "LDF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(2, 3, "LDL", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(27, 3, "LDS", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(29, 3, "LDT", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(1, 3, "LDX", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(52, 3, "LPS", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(8, 3, "MUL", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(24, 3, "MULF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(38, 2, "MULR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(50, 1, "NORM", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(17, 3, "OR", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(54, 3, "RD", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(43, 2, "RMO", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(19, 3, "RSUB", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(41, 2, "SHIFTL", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(42, 2, "SHIFTR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(60, 1, "SIO", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(59, 3, "SSK", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(3, 3, "STA", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(30, 3, "STB", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(21, 3, "STCH", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(32, 3, "STF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(53, 3, "STI", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(5, 3, "STL", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(31, 3, "STS", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(58, 3, "STSW", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(33, 3, "STT", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(4, 3, "STX", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(7, 3, "SUB", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(23, 3, "SUBF", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 2, "SUBR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 2, "SVC", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 3, "TD", fn(machine: Machine, code: BitArray) -> Machine { machine }), + Def(23, 1, "TIO", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 3, "TIX", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 2, "TIXR", fn(machine: Machine, code: BitArray) -> Machine { + machine + }), + Def(23, 3, "WD", fn(machine: Machine, code: BitArray) -> Machine { machine }), + ] +} + +pub fn get_def(instructions: List(Def), first_byte: BitArray) -> Def { + result.unwrap( + list.find(instructions, fn(inst) -> Bool { + let assert <> = first_byte + opcode == inst.opcode + }), + Def(0, 0, "", fn(machine: Machine, code: BitArray) -> Machine { machine }), + ) +} diff --git a/sim/src/sim/memory.gleam b/sim/src/sim/memory.gleam new file mode 100644 index 0000000..0e92d40 --- /dev/null +++ b/sim/src/sim/memory.gleam @@ -0,0 +1,61 @@ +import gleam/bit_array + +pub type Memory = + BitArray + +fn pad_to_length(memory: Memory, lenght: Int) -> Memory { + case bit_array.bit_size(memory) < lenght { + True -> { + pad_to_length(<>, lenght) + } + _ -> memory + } +} + +pub fn insert_at(memory: Memory, pos: Int, value: BitArray) -> Memory { + let memory = case pos >= bit_array.bit_size(memory) { + True -> { + pad_to_length(memory, pos) + } + False -> memory + } + let length = bit_array.bit_size(value) + let assert Ok(head) = bit_array.slice(memory, 0, pos) + case + bit_array.slice( + memory, + pos + length, + bit_array.bit_size(memory) - pos - length, + ) + { + Ok(tail) -> <> + Error(Nil) -> <> + } +} + +pub fn get_char_at(memory: Memory, pos: Int) -> BitArray { + let assert Ok(value) = bit_array.slice(memory, pos, 8) + value +} + +pub fn set_char_at(memory: Memory, pos: Int, value: BitArray) -> Memory { + insert_at(memory, pos, value) +} + +pub fn get_int_at(memory: Memory, pos: Int) -> BitArray { + let assert Ok(value) = bit_array.slice(memory, pos, 24) + value +} + +pub fn set_int_at(memory: Memory, pos: Int, value: BitArray) -> Memory { + insert_at(memory, pos, value) +} + +pub fn get_float_at(memory: Memory, pos: Int) -> BitArray { + let assert Ok(value) = bit_array.slice(memory, pos, 40) + value +} + +pub fn set_float_at(memory: Memory, pos: Int, value: BitArray) -> Memory { + insert_at(memory, pos, value) +} diff --git a/sim/src/sim/register.gleam b/sim/src/sim/register.gleam new file mode 100644 index 0000000..8df625e --- /dev/null +++ b/sim/src/sim/register.gleam @@ -0,0 +1,6 @@ +pub type Register = + BitArray + +pub fn new() -> Register { + <<0:size(24)>> +} diff --git a/sim/src/sim/registers.gleam b/sim/src/sim/registers.gleam new file mode 100644 index 0000000..015151f --- /dev/null +++ b/sim/src/sim/registers.gleam @@ -0,0 +1,29 @@ +import sim/register + +pub type Registers { + Registers( + a: register.Register, + x: register.Register, + l: register.Register, + b: register.Register, + s: register.Register, + t: register.Register, + f: register.Register, + pc: register.Register, + sw: register.Register, + ) +} + +pub fn new() -> Registers { + Registers( + register.new(), + register.new(), + register.new(), + register.new(), + register.new(), + register.new(), + register.new(), + register.new(), + register.new(), + ) +} diff --git a/sim/test/sim_test.gleam b/sim/test/sim_test.gleam new file mode 100644 index 0000000..fba3c88 --- /dev/null +++ b/sim/test/sim_test.gleam @@ -0,0 +1,13 @@ +import gleeunit + +pub fn main() -> Nil { + gleeunit.main() +} + +// gleeunit test functions end in `_test` +pub fn hello_world_test() { + let name = "Joe" + let greeting = "Hello, " <> name <> "!" + + assert greeting == "Hello, Joe!" +}