simulator

This commit is contained in:
Jesenko, Jakob 2025-12-07 21:46:10 +01:00
parent 6d722acba9
commit a9323bb7d9
10 changed files with 459 additions and 0 deletions

24
sim/README.md Normal file
View file

@ -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 <https://hexdocs.pm/sim>.
## Development
```sh
gleam run # Run the project
gleam test # Run the tests
```

21
sim/gleam.toml Normal file
View file

@ -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"

13
sim/manifest.toml Normal file
View file

@ -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" }

8
sim/src/sim.gleam Normal file
View file

@ -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
}

61
sim/src/sim/loader.gleam Normal file
View file

@ -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 <<position:24, _len:8, value:bits>> = record.content
echo position
echo bit_array.base16_encode(value)
memory.insert_at(memory, position, value)
}
_ -> memory
}
|> echo
})
}

223
sim/src/sim/machine.gleam Normal file
View file

@ -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 <<pc:24>> = 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 <<opcode:6, _:2>> = first_byte
opcode == inst.opcode
}),
Def(0, 0, "", fn(machine: Machine, code: BitArray) -> Machine { machine }),
)
}

61
sim/src/sim/memory.gleam Normal file
View file

@ -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(<<memory:bits, 0:1>>, 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) -> <<head:bits, value:bits, tail:bits>>
Error(Nil) -> <<head:bits, value:bits>>
}
}
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)
}

View file

@ -0,0 +1,6 @@
pub type Register =
BitArray
pub fn new() -> Register {
<<0:size(24)>>
}

View file

@ -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(),
)
}

13
sim/test/sim_test.gleam Normal file
View file

@ -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!"
}