diff --git a/.idea/dsa2.iml b/.idea/dsa2.iml
index d44b7fe..3f76210 100644
--- a/.idea/dsa2.iml
+++ b/.idea/dsa2.iml
@@ -12,12 +12,14 @@
-
+
+
+
diff --git a/tooling/assembler/example.dsa b/doc/DSC/dscx.md
similarity index 100%
rename from tooling/assembler/example.dsa
rename to doc/DSC/dscx.md
diff --git a/doc/notes/functional language syntax.txt b/doc/notes/functional language syntax.txt
new file mode 100644
index 0000000..f310e91
--- /dev/null
+++ b/doc/notes/functional language syntax.txt
@@ -0,0 +1,47 @@
+# idk
+
+basic stuff.
+don't like haskell's reverse order of applying operations inside out, makes code hard to read.
+piping >>
+
+// this would be the syntax to filter a list of numbers to get the evens
+evens x List(Int) => x
+ |> filter_map (div 2 |> equals 0)
+
+// similarly you can use lambdas (same syntax as ordinary functions)
+evens x List(Int) => x
+ |> filter_map (x => div 2 |> equals 0)
+
+is_even x Int => x |> div 2 |> equals 0
+
+add_y_if_even_else_z x Int, y Int, z Int => x
+ |> is_even
+ |> then (add y)
+ |> else (add z)
+
+then Bool x, (-> T) val -> Option(T) =>
+ // language internal
+
+else Option(T) val (-> T) alternative -> T =>
+ // language internal
+
+type Result(T, E) => Ok(T) | Err(E)
+type Option(T) => Some(T) | None
+type Shape => Square(Int) | Rectangle(Int, Int) | Circle(Int)
+
+// we can pattern match
+area_doubled Shape s -> Int => s
+ |> case Square (s => multiply s s)
+ |> case Rectangle multiply // multiply takes two integer arguments and Rectangle provides this!
+ |> case Circle (r => multiply r r |> multiply PI)
+ |> case Circle (r => r |> multiply r |> multiply PI)
+ |> case Circle (r => r, r |> multiply |> multiply PI)
+ |> case Circle (r => r, r |> (multiply >> multiply PI))
+ |> multiply 2
+
+chained_computation Int s -> Result(Int, ErrType) => s
+ |> try fallible_computation_on_int
+ |> try fallible_computation_on_int
+
+case T item, T matched_variant, (-> R) -> Option(R) =>
+ // language internal
\ No newline at end of file
diff --git a/resources/bf_out.dsa b/resources/bf_out.dsa
new file mode 100644
index 0000000..dd56f3d
--- /dev/null
+++ b/resources/bf_out.dsa
@@ -0,0 +1,704 @@
+
+include print: "./print.dsa"
+
+resb program: 0x1000
+dw stack: 0x10000
+dw input: 0x30000
+
+_init:
+ ldw stack, bpr
+ mov bpr, spr
+main:
+ lwi program, rga
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_99
+loop_start_99:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_116
+loop_start_116:
+ nop
+ subi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jez acc, loop_end_122
+loop_start_122:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_122
+loop_end_122:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_129
+loop_start_129:
+ nop
+ addi acc, 1
+ jez acc, loop_end_131
+loop_start_131:
+ nop
+ subi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_131
+loop_end_131:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_129
+loop_end_129:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_116
+loop_end_116:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_150
+loop_start_150:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jnz acc, loop_start_150
+loop_end_150:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_157
+loop_start_157:
+ nop
+ subi acc, 1
+ jnz acc, loop_start_157
+loop_end_157:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_174
+loop_start_174:
+ nop
+ subi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jez acc, loop_end_178
+loop_start_178:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_178
+loop_end_178:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_185
+loop_start_185:
+ nop
+ addi acc, 1
+ jez acc, loop_end_187
+loop_start_187:
+ nop
+ subi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_187
+loop_end_187:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_185
+loop_end_185:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jnz acc, loop_start_174
+loop_end_174:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_205
+loop_start_205:
+ nop
+ subi acc, 1
+ jnz acc, loop_start_205
+loop_end_205:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_210
+loop_start_210:
+ nop
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ push acc
+ call print::print_byte
+ pop zero
+ jez acc, loop_end_260
+loop_start_260:
+ nop
+ subi acc, 1
+ jnz acc, loop_start_260
+loop_end_260:
+ nop
+ jnz acc, loop_start_210
+loop_end_210:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_265
+loop_start_265:
+ nop
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ push acc
+ call print::print_byte
+ pop zero
+ jez acc, loop_end_315
+loop_start_315:
+ nop
+ subi acc, 1
+ jnz acc, loop_start_315
+loop_end_315:
+ nop
+ jnz acc, loop_start_265
+loop_end_265:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ addi acc, 1
+ push acc
+ call print::print_byte
+ pop zero
+ jez acc, loop_end_371
+loop_start_371:
+ nop
+ subi acc, 1
+ jnz acc, loop_start_371
+loop_end_371:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ push acc
+ call print::print_byte
+ pop zero
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ push acc
+ call print::print_byte
+ pop zero
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_386
+loop_start_386:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jnz acc, loop_start_386
+loop_end_386:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_395
+loop_start_395:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jnz acc, loop_start_395
+loop_end_395:
+ nop
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ jez acc, loop_end_405
+loop_start_405:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ stw acc, rga
+ addi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jnz acc, loop_start_405
+loop_end_405:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ subi acc, 1
+ jnz acc, loop_start_99
+loop_end_99:
+ nop
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ stw acc, rga
+ subi rga, 4
+ ldw rga, acc
+ addi acc, 1
+ addi acc, 1
+ push acc
+ call print::print_byte
+ pop zero
+ push acc
+ call print::print_byte
+ pop zero
+ push acc
+ call print::print_byte
+ pop zero
+ hlt
diff --git a/resources/bf_out.dsb b/resources/bf_out.dsb
new file mode 100644
index 0000000..e5a022d
Binary files /dev/null and b/resources/bf_out.dsb differ
diff --git a/resources/brainf.bf b/resources/brainf.bf
new file mode 100644
index 0000000..4adf535
--- /dev/null
+++ b/resources/brainf.bf
@@ -0,0 +1,29 @@
+++++++++++++++++++++++++++++++++++++++++++++
+>++++++++++++++++++++++++++++++++
+>++++++++++++++++
+>
+>+
+<<
+[
+>>
+>
+>++++++++++
+<<
+[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]
+>[<+>-]
+>[-]
+>>
+>++++++++++
+<
+[->-[>+>>]>[+[-<+>]>+>>]<<<<<]
+>[-]
+>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]
+<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]
+<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-]
+<<<<<<<.>.
+>>[>>+<<-]
+>[>+<<+>-]
+>[<+>-]
+<<<-
+]
+<<++...
diff --git a/resources/dsc.dsa b/resources/dsc.dsa
new file mode 100644
index 0000000..15819ed
--- /dev/null
+++ b/resources/dsc.dsa
@@ -0,0 +1,135 @@
+// GENERATED BY DSC COMPILER
+// Generated at 2026-08-09 01:17:46
+
+// Imports
+include alloc: "./lib/memory/block_alloc.dsa"
+include print: "./print.dsa"
+
+// Globals & Reserved Memory
+
+// Entry Point
+dw stack: 0x010000
+db message: "Process Exited with code:"
+_init:
+ ldw stack, bpr, 0
+ mov bpr, spr
+ push zero
+ call main
+ call print::newline
+ lwi message, rg0
+ push rg0
+ call print::print
+ pop zero
+ call print::print_hex_word
+ pop zero
+ hlt
+
+// Return
+_ret:
+ mov bpr, spr
+ pop bpr
+ ret
+
+db str_4: "successful free of ptr"
+// fn main() -> u32
+main:
+ push bpr
+ mov spr, bpr
+ lli 32, rg0
+ lli 64, rg1
+ // push arg 1
+ push rg0
+ // push arg 0
+ push rg1
+ call alloc::init
+ pop rg2
+ pop zero
+ push rg2
+ // push arg 0
+ push rg2
+ call print::print_hex_word
+ pop zero
+ call print::newline
+ ldw spr, rg0, 0
+ stw rg0, spr, 0
+ // push arg 0
+ push rg0
+ call alloc::alloc
+ pop rg1
+ push rg1
+ // push arg 0
+ push rg1
+ call print::print_hex_word
+ pop zero
+ lli 200, rg0
+ ldw spr, rg1, 0
+ stw rg0, rg1, 0
+ stw rg1, spr, 0
+ call print::newline
+ ldw spr, rg0, 4
+ stw rg0, spr, 4
+ // push arg 0
+ push rg0
+ call alloc::alloc
+ pop rg2
+ push rg2
+ // push arg 0
+ push rg2
+ call print::print_hex_word
+ pop zero
+ call print::newline
+ ldw spr, rg0, 4
+ ldw rg0, rg2, 0
+ stw rg0, spr, 4
+ // push arg 0
+ ldw spr, rg2, 4
+ stw rg2, spr, 4
+ addi spr, 4, rg3
+ ldw spr, rg2, 8
+ stw rg2, spr, 8
+ // push arg 1
+ push rg3
+ // push arg 0
+ push rg2
+ call alloc::free
+ pop zero
+ pop zero
+ ldw spr, rg2, 8
+ stw rg2, spr, 8
+ // push arg 0
+ push rg2
+ call alloc::alloc
+ pop rg3
+ push rg3
+ call print::newline
+ ldw spr, rg2, 0
+ stw rg2, spr, 0
+ // push arg 0
+ push rg2
+ call print::print_hex_word
+ pop zero
+ call print::newline
+ ldw spr, rg2, 8
+ stw rg2, spr, 8
+ // push arg 0
+ push rg2
+ call print::print_hex_word
+ pop zero
+ ldw spr, rg2, 8
+ lli 0, rg4
+ ieq rg2, rg4, rg5
+ jez rg5, _else_2
+_then_1:
+ lwi str_4, rg4
+ stw rg2, spr, 8
+ // push arg 0
+ push rg4
+ call print::print
+ pop zero
+ jmp _end_3
+_else_2:
+ nop
+_end_3:
+ lli 0, rg4
+ stw rg4, bpr, 8
+ jmp _ret
\ No newline at end of file
diff --git a/resources/dsfl.dsb b/resources/dsfl.dsb
new file mode 100644
index 0000000..77dcf0b
Binary files /dev/null and b/resources/dsfl.dsb differ
diff --git a/resources/example.dsc b/resources/example.dsc
new file mode 100644
index 0000000..47ebd9d
--- /dev/null
+++ b/resources/example.dsc
@@ -0,0 +1,30 @@
+include print: "./lib/io/print.dsa";
+include arena: "./lib/memory/arena_alloc.dsa";
+
+fn main() -> u32 {
+
+ let x: u32 = 0;
+ let y: u32 = &x;
+
+ let alloc: u32 = arena::new(512);
+ let ptr1: u32 = arena::alloc(alloc, 32);
+ let ptr2: u32 = arena::alloc(alloc, 32);
+
+ print::print_hex_word(alloc);
+ print::print_newline();
+ print::print_hex_word(ptr1);
+ print::print_newline();
+ print::print_hex_word(ptr2);
+ print::print_newline();
+ print::print_num(*ptr2);
+ print::print_newline();
+ *ptr2 = 42;
+
+ print::print_hex_word(ptr2);
+ print::print_newline();
+ print::print_num(*ptr2);
+ print::print_newline();
+ print::println("end");
+
+ return 0;
+}
diff --git a/resources/hello_world.dsc b/resources/hello_world.dsc
new file mode 100644
index 0000000..2f82e59
--- /dev/null
+++ b/resources/hello_world.dsc
@@ -0,0 +1,5 @@
+include print: "./lib/io/print.dsa";
+
+fn main() {
+ print::print("Hello, world!");
+}
diff --git a/resources/lib/memory/block_alloc.dsa b/resources/lib/memory/block_alloc.dsa
new file mode 100644
index 0000000..bd1fe5b
--- /dev/null
+++ b/resources/lib/memory/block_alloc.dsa
@@ -0,0 +1,212 @@
+// block_alloc.dsa
+// Fixed-size block allocator
+//
+// Memory layout:
+// [base + 0]: free list head pointer (pointer to first free block, or 0 if none)
+// [base + 4]: block size
+// [base + 8]: total blocks
+// [base + 12]: base address of block pool
+// [base + 16+]: block pool (each block starts with a 4-byte next pointer)
+//
+// Usage:
+// include block_alloc "./lib/memory/block_alloc.dsa"
+//
+// For init:
+// push num_blocks (e.g., 32)
+// push block_size (e.g., 64 bytes)
+// call block_alloc::init
+// pop block_size
+// pop num_blocks
+// ; result in spr+8 (allocator handle)
+//
+// For alloc:
+// push allocator_handle
+// call block_alloc::alloc
+// pop allocator_handle
+// ; result in spr+8 (pointer to block, or 0 if out of memory)
+//
+// For free:
+// push block_pointer
+// push allocator_handle
+// call block_alloc::free
+// pop allocator_handle
+// pop block_pointer
+
+dw heap_start: 0x30000 // Start of our heap area
+
+// Initialize the allocator
+// Args: block_size, num_blocks
+// Returns: allocator handle (pointer to metadata)
+init:
+ push bpr
+ mov spr, bpr
+
+ ldw bpr, rg0, 8 // block_size
+ ldw bpr, rg1, 12 // num_blocks
+
+ // Allocate metadata (16 bytes) + pool space
+ ldw heap_start, rg2 // base address for this allocator
+ mov rg2, rg3 // save base in rg3
+
+ // Calculate total size needed: 16 + (block_size * num_blocks)
+ // We'll use a simple multiplication by repeated addition
+ mov rg0, rg4 // block_size to rg4
+ mov rg1, rg5 // num_blocks to rg5
+ lli 0, acc // accumulator for multiplication
+
+_multiply_loop:
+ jez rg5 _multiply_done
+ add acc, rg4, acc
+ subi rg5, 1
+ jmp _multiply_loop
+
+_multiply_done:
+ // acc now contains block_size * num_blocks
+ addi acc, 16 // add metadata size
+
+ // Update heap_start for next allocation
+ add rg2, acc, acc
+ stw acc, heap_start
+
+ // Now set up metadata at rg3 (base)
+ // [base + 0]: free list head (will point to first block)
+ // [base + 4]: block_size
+ // [base + 8]: total blocks
+ // [base + 12]: pool base address
+
+ addi rg3, 16, rg6 // rg6 = pool base
+
+ stw rg6, rg3 // store pool base as free list head initially
+ stw rg0, rg3, 4 // store block_size
+ stw rg1, rg3, 8 // store total blocks
+ stw rg6, rg3, 12 // store pool base address
+
+ // Now initialize the free list
+ // Each block's first 4 bytes point to the next block
+ // rg6 = current block pointer
+ // rg0 = block_size
+ // rg1 = num_blocks (counter)
+
+ subi rg1 1 // we'll count down from num_blocks-1
+
+_init_loop:
+ jez rg1, _init_loop_done
+
+ // Calculate next block address: current + block_size
+ add rg6, rg0, rg7 // rg7 = next block address
+
+ // Store next pointer at current block
+ stw rg7, rg6
+
+ // Move to next block
+ mov rg7, rg6
+ subi rg1, 1
+ jmp _init_loop
+
+_init_loop_done:
+ // Last block points to null (0)
+ lli 0, acc
+ stw acc, rg6
+
+ // Return allocator handle (base address - 16 to get back to metadata start)
+ stw rg3, bpr, 8
+
+ mov bpr, spr
+ pop bpr
+ ret
+
+
+// Allocate a block
+// Args: allocator_handle
+// Returns: pointer to block (or 0 if out of memory)
+alloc:
+ push bpr
+ mov spr, bpr
+
+ ldw bpr, rg0, 8 // allocator handle (metadata base)
+
+ // Load free list head
+ ldw rg0, rg1 // rg1 = free list head
+
+ // Check if free list is empty
+ jez, rg1 _alloc_fail
+
+ // Free list is not empty, pop the first block
+ // Load the next pointer from the block we're allocating
+ ldw rg1, rg2 // rg2 = next free block
+
+ // Update free list head to point to next block
+ stw rg2, rg0
+
+ // Return the allocated block (rg1)
+ stw rg1, bpr, 8
+ jmp _alloc_done
+
+_alloc_fail:
+ // No free blocks, return 0
+ lli 0, acc
+ stw acc, bpr, 8
+
+_alloc_done:
+ mov bpr, spr
+ pop bpr
+ ret
+
+
+// Free a block
+// Args: allocator_handle, block_pointer
+// Returns: nothing (but could return error code if block is invalid)
+free:
+ push bpr
+ mov spr, bpr
+
+ ldw bpr, rg0, 8 // allocator handle
+ ldw bpr, rg6, 12 // pointer to the block pointer to free
+ ldw rg6, rg1 // rg1 = block pointer to free
+
+ // Load current free list head
+ ldw rg0, rg2 // rg2 = current head
+
+ // Set the freed block's next pointer to current head
+ stw rg2, rg1
+
+ // Update free list head to point to freed block
+ stw rg1, rg0
+
+ // Update the freed block's previous pointer to NULL
+ lli 0, rg1
+ stw rg1, rg6
+
+ mov bpr, spr
+ pop bpr
+ ret
+
+
+// Debug function: get stats
+// Args: allocator_handle
+// Returns: nothing (but could populate a stats structure)
+get_stats:
+ push bpr
+ mov spr, bpr
+
+ ldw bpr, rg0, 8 // allocator handle
+
+ // Count free blocks by traversing the free list
+ ldw rg0, rg1 // rg1 = free list head
+ lli 0, rg2 // rg2 = counter
+
+count_loop:
+ jez, rg1 count_done
+
+ addi rg2, 1
+ ldw rg1, rg1 // move to next block
+ jmp count_loop
+
+count_done:
+ // rg2 now contains number of free blocks
+ // Could store this somewhere or return it
+ stw rg2, bpr, 8
+
+ mov bpr, spr
+ pop bpr
+ ret
diff --git a/resources/main.dsc b/resources/main.dsc
new file mode 100644
index 0000000..72cfd71
--- /dev/null
+++ b/resources/main.dsc
@@ -0,0 +1,37 @@
+include print: "./lib/io/print.dsa";
+include alloc: "./lib/memory/block_alloc.dsa";
+
+fn main() -> u32 {
+ let allocator: u32 = alloc::init(64, 32);
+
+ print::print_hex_word(allocator);
+
+ print::print_newline();
+
+ let ptr: u32 = alloc::alloc(allocator);
+ print::print_hex_word(ptr);
+ *ptr = 200;
+
+ print::print_newline();
+
+ let p2: u32 = alloc::alloc(allocator);
+ print::print_hex_word(p2);
+ print::print_newline();
+ print::print_num(*ptr);
+
+ alloc::free(allocator, &ptr);
+ let ptr3: u32 = alloc::alloc(allocator);
+
+ print::print_newline();
+
+ print::print_hex_word(ptr3);
+
+ print::print_newline();
+ print::print_hex_word(ptr);
+
+ if ptr == 0 {
+ print::print("successful free of ptr");
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/resources/print.dsa b/resources/print.dsa
index 24be7d8..5398fad 100644
--- a/resources/print.dsa
+++ b/resources/print.dsa
@@ -129,42 +129,67 @@ print_byte: func
// ------------------------------------------
// prints the value of arg[0] to the screen in hex.
-// print_hex_word:
-// push bpr
-// mov spr, bpr
+print_hex_word: func
+ push rg0
+ push rg1
+ push rg2
+ push rg3
+ push rg4
-// ldw current, rg1
+ ldw current, rg1
+ ldb bpr, rg0, 11
+ push rg0
+ call _print_hex_byte
+ addi spr, 4
-// ldb bpr, rg0, 8
-// push rg0
-// call _print_hex_byte
-// addi spr, 4
+ ldb bpr, rg0, 10
+ push rg0
+ call _print_hex_byte
+ addi spr, 4
-// ldb bpr, rg0, 9
-// push rg0
-// call _print_hex_byte
-// addi spr, 4
+ ldb bpr, rg0, 9
+ push rg0
+ call _print_hex_byte
+ addi spr, 4
-// ldb bpr, rg0, 10
-// push rg0
-// call _print_hex_byte
-// addi spr, 4
+ ldb bpr, rg0, 8
+ push rg0
+ call _print_hex_byte
+ addi spr, 4
-// ldb bpr, rg0, 11
-// push rg0
-// call _print_hex_byte
-// addi spr, 4
+ // save cursor
+ stw rg1, current
-// jmp _end
+ pop rg4
+ pop rg3
+ pop rg2
+ pop rg1
+ pop rg0
+ return
// ------------------------------------------
// prints the last byte of arg[0] to the screen in hex.
print_hex_byte: func
+ push rg0
+ push rg1
+ push rg2
+ push rg3
+ push rg4
+
ldw bpr, rg0, 8
ldw current, rg1
call _print_hex_byte
- jmp _end
+
+ // save cursor
+ stw rg1, current
+
+ pop rg4
+ pop rg3
+ pop rg2
+ pop rg1
+ pop rg0
+ return
// function body
_print_hex_byte:
@@ -183,14 +208,18 @@ _print_hex_byte:
// print a hex digit
_print_hex_nibble:
lli 10, rg3
+
ilt rg0, rg3, rg4
jnz rg4, _print_hex_nibble_number
+
+ // print a character 'A'..='F' by applying offset 0x37 ('A' - 10)
addi rg0, 0x37, rg0
stb rg0, rg1
addi rg1, 1
ret
// helper function.
+// prints a decimal digit by adding 0x30 ('0')
_print_hex_nibble_number:
addi rg0, 0x30, rg0
stb rg0, rg1
@@ -200,11 +229,21 @@ _print_hex_nibble_number:
// ------------------------------------------
// print whitespace
print_whitespace: func
+ push rg0
+ push rg1
+
ldw current, rg1
lli 0x20, rg0
stb rg0, rg1
addi rg1, 1
- jmp _end
+
+ // save cursor
+ stw rg1, current
+
+ pop rg1
+ pop rg0
+ return
+
// ------------------------------------------
// print newline
diff --git a/resources/program.dsa b/resources/program.dsa
new file mode 100644
index 0000000..d833342
--- /dev/null
+++ b/resources/program.dsa
@@ -0,0 +1,76 @@
+
+include print: "./print.dsa"
+dw stack: 0x10000
+
+_init:
+ ldw stack, bpr
+ mov bpr, spr
+main:
+ push zero
+ call program
+ call print::print_hex_word
+ pop zero
+ hlt
+
+program: func
+ push rg0
+ push rg1
+ lli 5, rg0
+ push rg0
+ lli 4, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ sub rg0, rg1, rg0
+ push rg0
+ lli 4, rg0
+ push rg0
+ lli 3, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ sub rg0, rg1, rg0
+ push rg0
+ lli 3, rg0
+ push rg0
+ lli 2, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ sub rg0, rg1, rg0
+ push rg0
+ lli 2, rg0
+ push rg0
+ lli 1, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ sub rg0, rg1, rg0
+ push rg0
+ lli 60, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ add rg0, rg1, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ add rg0, rg1, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ add rg0, rg1, rg0
+ push rg0
+ pop rg1
+ pop rg0
+ add rg0, rg1, rg0
+ push rg0
+
+ // return value in bpr+8
+ pop rg0
+ stw rg0, bpr, 8
+
+ // epilogue
+ pop rg1
+ pop rg0
+ return
diff --git a/resources/program.dsfl b/resources/program.dsfl
index 491727a..1cdec80 100644
--- a/resources/program.dsfl
+++ b/resources/program.dsfl
@@ -1 +1,13 @@
-(add 1 (or 1 (if true then 2 else 1)))
\ No newline at end of file
+(add
+ (sub 5 4)
+ (add
+ (sub 4 3)
+ (add
+ (sub 3 2)
+ (add
+ (sub 2 1)
+ 60
+ )
+ )
+ )
+)
\ No newline at end of file
diff --git a/resources/test.dsc b/resources/test.dsc
new file mode 100644
index 0000000..f13f9a4
--- /dev/null
+++ b/resources/test.dsc
@@ -0,0 +1,20 @@
+include print: "./lib/io/print.dsa";
+
+fn main() -> u32 {
+ let x: u32 = 30;
+ print::print_num(x);
+
+ 200 + 5;
+
+ let p: Point = Point {
+ x: 10,
+ y: 20,
+ test: [2, 3, 4]
+ };
+}
+
+struct Point {
+ x: u32,
+ y: u32,
+ test: [u32; 3],
+}
diff --git a/resources/testprint.dsa b/resources/testprint.dsa
new file mode 100644
index 0000000..5369ad6
--- /dev/null
+++ b/resources/testprint.dsa
@@ -0,0 +1,13 @@
+include print: "./print.dsa"
+
+dw stack: 0x10000
+
+_init:
+ lwi stack, bpr
+ mov bpr, spr
+
+ lwi 0x100abcaa, rg0
+ push rg0
+ call print::print_hex_word
+ pop zero
+ hlt
\ No newline at end of file
diff --git a/tooling/assembler/src/assembler/mod.rs b/tooling/assembler/src/assembler/mod.rs
index c9e0876..7a203f2 100644
--- a/tooling/assembler/src/assembler/mod.rs
+++ b/tooling/assembler/src/assembler/mod.rs
@@ -39,18 +39,18 @@ pub use self::{
#[derive(Debug)]
pub struct Assembler {
- src_path: PathBuf,
result_tx: mpsc::Sender, AssembleError>>,
result_rx: Option, AssembleError>>>,
is_running: bool,
+ filename: String,
}
impl Assembler {
#[must_use]
- pub fn new(src_path: impl Into) -> Self {
+ pub fn new(filename: impl Into) -> Self {
let (tx, rx) = mpsc::channel();
Self {
- src_path: src_path.into(),
+ filename: filename.into(),
result_tx: tx,
result_rx: Some(rx),
is_running: false,
@@ -59,21 +59,20 @@ impl Assembler {
}
impl TraceableProcess for Assembler {
+ type Input = PathBuf;
type Output = DseExecutable;
type Error = AssembleError;
- fn run(mut self: Box, ctx: ProcessAgent) -> Result {
- assemble(&self.src_path, &ctx)
+ fn label(&self) -> String {
+ format!("Assembling {}", self.filename)
}
- fn label(&self) -> String {
- format!(
- "Assembling {}",
- self.src_path
- .file_name()
- .expect("a valid path must have a file name")
- .to_string_lossy()
- )
+ fn run(
+ self: Box,
+ input: impl Into,
+ ctx: ProcessAgent,
+ ) -> Result {
+ assemble(&input.into(), &ctx)
}
}
diff --git a/tooling/assembler/src/main.rs b/tooling/assembler/src/main.rs
index f747621..f94c6ec 100644
--- a/tooling/assembler/src/main.rs
+++ b/tooling/assembler/src/main.rs
@@ -17,8 +17,11 @@ fn main() {
// Parse command line arguments
let args = Args::parse();
- let assembler = Assembler::new(PathBuf::from(args.input_path));
- let result = assembler.run_sync().expect("assembler failed.");
+ let assembler = Assembler::new(args.input_path.file_name().unwrap().to_string_lossy());
+ let result = assembler
+ .run_sync(&args.input_path)
+ .expect("assembler failed.");
+
let mut buffer = Cursor::new(Vec::new());
result.write(&mut buffer).unwrap();
diff --git a/tooling/bf_compiler/Cargo.toml b/tooling/bf_compiler/Cargo.toml
new file mode 100644
index 0000000..ee6e37a
--- /dev/null
+++ b/tooling/bf_compiler/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "bf_compiler"
+edition.workspace = true
+version.workspace = true
+authors.workspace = true
+
+[[bin]]
+name = "dsabf"
+path = "src/main.rs"
+
+[lib]
+name = "dsabf_compiler"
+path = "src/lib.rs"
+
+[dependencies]
+clap = { version = "4.6.0", features = ["derive"] }
+common = { path = "../../libraries/common" }
+dsa_formats = { path = "../../libraries/dsa_formats" }
+dsa_tasks = { path = "../../libraries/dsa_tasks" }
\ No newline at end of file
diff --git a/tooling/bf_compiler/src/lib.rs b/tooling/bf_compiler/src/lib.rs
new file mode 100644
index 0000000..7037b8f
--- /dev/null
+++ b/tooling/bf_compiler/src/lib.rs
@@ -0,0 +1,162 @@
+use dsa_tasks::{ProcessAgent, TraceableProcess};
+use std::fmt::{Display, Formatter};
+
+#[derive(Debug)]
+pub struct BfError(&'static str);
+impl std::error::Error for BfError {}
+impl Display for BfError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{self:?}")
+ }
+}
+
+#[derive(Debug)]
+pub struct BfCompiler {
+ filename: String,
+}
+
+impl BfCompiler {
+ pub fn new(filename: impl Into) -> Self {
+ Self {
+ filename: filename.into(),
+ }
+ }
+}
+
+impl TraceableProcess for BfCompiler {
+ type Input = String;
+ type Output = String;
+ type Error = BfError;
+
+ fn label(&self) -> String {
+ format!("Compiling {}", self.filename)
+ }
+
+ fn run(
+ self: Box,
+ input: impl Into,
+ ctx: ProcessAgent,
+ ) -> Result {
+ ctx.log(format!("Building {}", self.filename));
+
+ let asm_output = parse(&input.into(), &ctx);
+
+ ctx.log(format!("Successfully compiled {}", self.filename));
+ Ok(asm_output)
+ }
+}
+
+#[must_use]
+#[expect(clippy::too_many_lines)]
+pub fn parse(src: &str, ctx: &ProcessAgent) -> String {
+ let mut result = String::new();
+
+ ctx.log("Parsing Source");
+ let tokens = lex(src);
+
+ ctx.log("Building Header");
+
+ result.push_str(
+ "
+include print: \"./print.dsa\"
+
+resb program: 0x1000
+dw stack: 0x10000
+dw input: 0x30000
+
+_init:
+ ldw stack, bpr
+ mov bpr, spr
+main:
+ lwi program, rga\n",
+ );
+
+ ctx.log("Building Program");
+
+ // let _id = 0;
+ let mut idstack = Vec::::new();
+
+ for (id, tok) in tokens.iter().enumerate() {
+ match tok {
+ BfToken::Inc => result.push_str("\taddi acc, 1\n"),
+ BfToken::Dec => result.push_str("\tsubi acc, 1\n"),
+ BfToken::IncPtr => {
+ result.push_str("\tstw acc, rga\n");
+ result.push_str("\taddi rga, 4\n");
+ result.push_str("\tldw rga, acc\n");
+ }
+ BfToken::DecPtr => {
+ result.push_str("\tstw acc, rga\n");
+ result.push_str("\tsubi rga, 4\n");
+ result.push_str("\tldw rga, acc\n");
+ }
+ BfToken::Out => {
+ result.push_str("\tpush acc\n");
+ result.push_str("\tcall print::print_byte\n");
+ result.push_str("\tpop zero\n");
+ }
+ // Read a byte from input and store it at the current data pointer
+ // Assuming we have an input function mapped to a specific memory location or I/O port
+ BfToken::In => result.push_str("\tldw input acc\n"),
+ BfToken::Forward => {
+ // Start of loop [
+ let loop_start = format!("loop_start_{id}");
+ let loop_end = format!("loop_end_{id}");
+
+ // Push the current position for the matching ]
+ idstack.push(id as u32);
+
+ // Load current cell value and check if zero
+ result.push_str(&format!("\tjez acc, {loop_end}\n"));
+
+ result.push_str(&format!("{loop_start}:\n"));
+ result.push_str("\tnop\n");
+ }
+ BfToken::Back => {
+ // End of loop ]
+ if let Some(start_id) = idstack.pop() {
+ let loop_start = format!("loop_start_{start_id}");
+ let loop_end = format!("loop_end_{start_id}");
+
+ result.push_str(&format!("\tjnz acc, {loop_start}\n"));
+ result.push_str(&format!("{loop_end}:\n"));
+ result.push_str("\tnop\n");
+ } else {
+ // Unmatched closing bracket - could add error handling here
+ eprintln!("Warning: Unmatched ']' at position {id}");
+ }
+ }
+ }
+ }
+
+ result.push_str("\thlt\n");
+
+ result
+}
+
+enum BfToken {
+ Inc,
+ Dec,
+ IncPtr,
+ DecPtr,
+ Out,
+ In,
+ Forward,
+ Back,
+}
+
+fn lex(src: &str) -> Vec {
+ src.chars()
+ .filter_map(|c| match c {
+ '+' => Some(BfToken::Inc),
+ '-' => Some(BfToken::Dec),
+ '>' => Some(BfToken::IncPtr),
+ '<' => Some(BfToken::DecPtr),
+ '.' => Some(BfToken::Out),
+ ',' => Some(BfToken::In),
+ '[' => Some(BfToken::Forward),
+ ']' => Some(BfToken::Back),
+ _ => None,
+ })
+ .collect()
+}
diff --git a/tooling/bf_compiler/src/main.rs b/tooling/bf_compiler/src/main.rs
new file mode 100644
index 0000000..d76e181
--- /dev/null
+++ b/tooling/bf_compiler/src/main.rs
@@ -0,0 +1,36 @@
+use clap::Parser;
+use dsa_tasks::TraceableProcess;
+use dsabf_compiler::BfCompiler;
+use std::path::PathBuf;
+
+#[derive(Parser, Debug, Clone)]
+#[command(version, about, long_about = None)]
+struct Args {
+ #[arg(short = 'i')]
+ pub input_path: PathBuf,
+ #[arg(short = 'o')]
+ pub output_path: PathBuf,
+}
+
+fn main() {
+ // Parse command line arguments
+ let args = Args::parse();
+
+ let bf_src = std::fs::read_to_string(&args.input_path).unwrap();
+ let result = BfCompiler::new(
+ args.input_path
+ .file_stem()
+ .unwrap()
+ .to_string_lossy()
+ .to_string(),
+ )
+ .run_sync(bf_src)
+ .unwrap();
+
+ println!("Successfully compiled {}", args.input_path.display());
+ println!("Writing result to {}", args.output_path.display());
+
+ std::fs::write(&args.output_path, result).unwrap();
+
+ println!("Success");
+}
diff --git a/tooling/compiler/Cargo.toml b/tooling/compiler/Cargo.toml
deleted file mode 100644
index 1a32c86..0000000
--- a/tooling/compiler/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "compiler"
-edition.workspace = true
-version.workspace = true
-authors.workspace = true
-
-[dependencies]
diff --git a/tooling/compiler/src/lib.rs b/tooling/compiler/src/lib.rs
deleted file mode 100644
index b93cf3f..0000000
--- a/tooling/compiler/src/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-pub fn add(left: u64, right: u64) -> u64 {
- left + right
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
- }
-}
diff --git a/tooling/dscs_compiler/Cargo.toml b/tooling/dscs_compiler/Cargo.toml
new file mode 100644
index 0000000..b05daf3
--- /dev/null
+++ b/tooling/dscs_compiler/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "dscs_compiler"
+edition.workspace = true
+version.workspace = true
+authors.workspace = true
+
+[dependencies]
+clap = { version = "4.6.0", features = ["derive"] }
+common = { path = "../../libraries/common" }
+dsa_formats = { path = "../../libraries/dsa_formats" }
+dsa_tasks = { path = "../../libraries/dsa_tasks" }
+assembler = { path = "../assembler" }
+chrono = "0.4.45"
\ No newline at end of file
diff --git a/tooling/dscs_compiler/src/backend/codegen.rs b/tooling/dscs_compiler/src/backend/codegen.rs
new file mode 100644
index 0000000..0fd59c2
--- /dev/null
+++ b/tooling/dscs_compiler/src/backend/codegen.rs
@@ -0,0 +1,883 @@
+use super::instruction::{InsBlock as IB, Instruction as I, Label};
+use super::registers::{Register, RegisterAllocator};
+use crate::backend::instruction::Comparison::{
+ Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
+};
+use crate::model::{
+ AssignmentOperator, BinaryOperator, Call, CompilerError, ConstExpr, Declaration, Dependency,
+ Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable,
+};
+use chrono::{DateTime, Local};
+use dsa_tasks::ProcessAgent;
+use std::collections::HashMap;
+use std::sync::atomic::AtomicU32;
+use std::time::SystemTime;
+
+pub struct CodeGenerator {
+ ast: Program,
+ imports: HashMap,
+ globals: HashMap,
+ functions: Vec,
+ symbols: Vec,
+ allocator: RegisterAllocator,
+}
+
+impl CodeGenerator {
+ pub fn new(ast: Program) -> Self {
+ CodeGenerator {
+ ast,
+ imports: HashMap::new(),
+ globals: HashMap::new(),
+ functions: Vec::new(),
+ symbols: Vec::new(),
+ allocator: RegisterAllocator::new(),
+ }
+ }
+
+ pub fn include(&mut self, name: impl Into, path: impl Into) {
+ let name = name.into();
+ self.imports.insert(name.clone(), I::include(name, path));
+ }
+
+ fn is_global(&self, name: &str) -> bool {
+ // Check if this variable is in the globals list
+ self.globals.contains_key(name)
+ }
+
+ pub fn generate(&mut self, ctx: &ProcessAgent) -> Result {
+ // always include the print library for debugging!
+ self.include("print", "./lib/print.dsa");
+
+ for block in self.ast.clone().declarations {
+ match block {
+ Declaration::Variable {
+ var: Variable { name, .. },
+ ..
+ } => self.symbols.push(name),
+ Declaration::Function { name, .. } => self.symbols.push(name),
+ Declaration::Dependency(Dependency { name, .. }) => self.symbols.push(name),
+ Declaration::Struct { .. } => {} /* we can't do any code generation for
+ * a struct yet. we may need to later
+ * once these become class-like
+ * objects with implementations */
+ }
+ }
+
+ for block in self.ast.clone().declarations {
+ self.generate_block(block.clone())?;
+ }
+
+ let assembly = self.generate_layout()?;
+ Ok(assembly
+ .iter()
+ .map(|i| i.to_string())
+ .collect::>()
+ .join("\n"))
+ }
+
+ fn generate_layout(&mut self) -> Result {
+ let datetime: DateTime = SystemTime::now().into();
+
+ let mut block = IB::new();
+
+ block.extend(vec![
+ I::global_comment(format!(
+ "GENERATED BY DSC COMPILER
+ Generated at {}",
+ datetime.format("%Y-%m-%d %H:%M:%S")
+ )),
+ I::Newline,
+ I::global_comment("Imports"),
+ ]);
+
+ block.extend(self.imports.values().cloned().collect::>());
+
+ block.extend(vec![
+ I::Newline,
+ I::global_comment("Globals & Reserved Memory"),
+ ]);
+
+ block.extend(self.globals.values().cloned().collect::>());
+
+ block.extend(vec![
+ I::Newline,
+ I::global_comment("Entry Point"),
+ I::db_word("stack", 0x10000),
+ I::db_string("message", "Process Exited with code:"),
+ // init function for stack setup.
+ I::label("_init"),
+ I::ldw_label("stack", Register::Bpr),
+ I::mov(Register::Bpr, Register::Spr),
+ I::push(Register::Zero),
+ I::call("main"),
+ I::call("print::print_newline"),
+ I::lwi_label("message", Register::Rg0),
+ I::push(Register::Rg0),
+ I::call("print::print"),
+ I::pop(Register::Zero),
+ I::call("print::print_hex_word"),
+ I::pop(Register::Zero),
+ I::Hlt,
+ ]);
+
+ block.extend(vec![
+ I::Newline,
+ // default return block boilerplate
+ I::global_comment("Return"),
+ I::label("_ret"),
+ I::mov(Register::Bpr, Register::Spr),
+ I::pop(Register::Bpr),
+ I::Return,
+ ]);
+
+ for function in self.functions.iter() {
+ block.extend(function.iter().cloned());
+ }
+
+ Ok(block)
+ }
+
+ fn generate_global(&mut self, name: &str, init: Option) {
+ let init = init.unwrap_or(ConstExpr::Number(0));
+ match init {
+ ConstExpr::Number(value) => {
+ self.globals
+ .insert(name.to_string(), I::db_word(name, value as u32));
+ }
+ ConstExpr::String(str) => {
+ self.globals
+ .insert(name.to_string(), I::db_string(name, str));
+ }
+ }
+ }
+
+ fn generate_block(&mut self, block: Declaration) -> Result<(), CompilerError> {
+ match block {
+ Declaration::Variable { var, init, .. } => self.generate_global(&var.name, init),
+ Declaration::Function {
+ name,
+ params,
+ body,
+ return_type,
+ } => {
+ let func = self.generate_function(&name, ¶ms, &body, return_type);
+ self.functions.push(func);
+ }
+ Declaration::Dependency(Dependency { name, path }) => {
+ self.include(name, path);
+ }
+ Declaration::Struct { .. } => {} /* can't do any codegen for these yet,
+ * they're just types. */
+ };
+
+ Ok(())
+ }
+
+ // Example: Generate code for a function
+ fn generate_function(
+ &mut self,
+ name: &str,
+ params: &[Variable],
+ body: &[Statement],
+ return_type: TypeId,
+ ) -> IB {
+ let mut code = IB::new();
+
+ // Reset allocator for new function
+ self.allocator.reset();
+
+ let fmtparams = params
+ .iter()
+ .map(|p| format!("{}: {}", p.name, p.type_id))
+ .collect::>()
+ .join(", ");
+
+ code.extend(vec![
+ I::global_comment(format!("fn {name}({fmtparams}) -> {return_type}")),
+ I::label(name),
+ I::push(Register::Bpr),
+ I::mov(Register::Spr, Register::Bpr),
+ ]);
+
+ // Allocate parameters to registers or stack locations
+ for (i, param) in params.iter().enumerate() {
+ let offset = 8 + (i as i32 * 4); // Parameters start at bpr+8
+ // Track that this parameter is at a stack location
+ let (reg, load_code) = self.allocator.alloc_var(¶m.name).unwrap();
+ code.append(load_code);
+ code.push(I::ldw_reg_offset(Register::Bpr, reg, offset));
+ }
+
+ // Generate code for function body
+ for stmt in body {
+ let stmt_code = self.generate_statement(stmt, &mut code).unwrap();
+ code.append(stmt_code);
+ }
+
+ // automatically return at function end
+ if let Some(x) = code.iter().last()
+ && let I::Jmp { target: Label(val) } = x
+ && val == "_ret"
+ {
+ } else {
+ code.push(I::jmp("_ret"));
+ }
+
+ code.insert(0, I::Newline);
+
+ code
+ }
+
+ // Example: Generate code for a statement
+ fn generate_statement(
+ &mut self,
+ stmt: &Statement,
+ func_body: &mut IB,
+ ) -> Result {
+ let mut code = IB::new();
+
+ match stmt {
+ Statement::Declaration { var, value } => {
+ if let Some(expr) = value {
+ // Evaluate expression
+ let (result_reg, expr_code) =
+ self.generate_expression(expr, true, func_body)?;
+ code.append(expr_code);
+
+ // Store result in variable
+ let store_code = self.allocator.store_var(&var.name, &result_reg);
+ code.append(store_code);
+
+ // Free temporary register
+ self.allocator.free_temp(result_reg);
+ } else {
+ // Just declaring variable without initialization
+ self.allocator.alloc_var(&var.name)?;
+ }
+ }
+
+ Statement::Break => unimplemented!("need scope tracking first!"),
+ Statement::Continue => unimplemented!("need scope tracking first!"),
+ Statement::Defer(_func) => unimplemented!("we need scope tracking first!"),
+
+ Statement::PtrWrite { ptr, value } => {
+ let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?;
+ code.append(expr_code);
+
+ let (ptr_reg, ptr_code) = self.generate_expression(ptr, true, func_body)?;
+ code.append(ptr_code);
+
+ code.push(I::stw_reg(result_reg, ptr_reg));
+
+ self.allocator.free_temp(result_reg);
+ self.allocator.free_temp(ptr_reg);
+ }
+
+ Statement::Assign {
+ varname,
+ value,
+ operator,
+ } => {
+ // Evaluate expression
+ let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?;
+ code.append(expr_code);
+
+ if *operator == AssignmentOperator::Assign {
+ // Check if this is a global variable
+ if self.is_global(varname) {
+ // Store to global label
+ code.push(I::stw_label(result_reg, varname.clone()))
+ } else {
+ // Store result in local variable
+ let store_code = self.allocator.store_var(varname, &result_reg);
+ code.append(store_code);
+ }
+
+ // Free temporary register
+ self.allocator.free_temp(result_reg);
+
+ return Ok(code);
+ }
+
+ // for more complex assignment cases we need an intermediate register.
+ let (temp_reg, temp_code) = self.allocator.alloc_temp()?;
+ code.append(temp_code);
+
+ // fetch the value of the variable
+ let var_reg = if self.is_global(varname) {
+ let instruction = I::ldw_label(varname.clone(), temp_reg);
+ code.push(instruction);
+ temp_reg
+ } else {
+ let (rg, block) = self.allocator.load_var(varname)?;
+ code.append(block);
+ rg
+ };
+
+ let assign_code = match operator {
+ AssignmentOperator::Assign => {
+ unreachable!("assignment was already checked earlier.")
+ }
+ AssignmentOperator::AddAssign => I::add(var_reg, result_reg, temp_reg),
+ AssignmentOperator::SubAssign => I::sub(var_reg, result_reg, temp_reg),
+ AssignmentOperator::MulAssign => {
+ return Err(CompilerError::Unimplemented(
+ "TODO: implement multiplication for assignment".to_string(),
+ ));
+ }
+ AssignmentOperator::DivAssign => {
+ return Err(CompilerError::Unimplemented(
+ "TODO: write proper div function for DSA".to_string(),
+ ));
+ }
+ AssignmentOperator::ModAssign => {
+ return Err(CompilerError::Unimplemented(
+ "TODO: write proper mod function for DSA".to_string(),
+ ));
+ }
+ AssignmentOperator::AndAssign => I::and(var_reg, result_reg, temp_reg),
+ AssignmentOperator::OrAssign => I::or(var_reg, result_reg, temp_reg),
+ AssignmentOperator::XorAssign => I::xor(var_reg, result_reg, temp_reg),
+ AssignmentOperator::LeftShiftAssign => {
+ // this is only useful if we optimise out the register allocation
+ // inside value.
+ // if let Expression::Number { value, .. } = *value {
+ // I::shl(var_reg, value, temp_reg)
+ // }
+ I::shl(var_reg, result_reg, 0, temp_reg)
+ }
+ AssignmentOperator::RightShiftAssign => {
+ // this is only useful if we optimise out the register allocation
+ // if let Expression::Number { value, .. } = *value {
+ // I::shr(var_reg, value, temp_reg)
+ // }
+ I::shr(var_reg, result_reg, 0, temp_reg)
+ }
+ };
+ code.push(assign_code);
+
+ // Check if this is a global variable
+ if self.is_global(varname) {
+ // Store to global label
+ code.push(I::stw_label(temp_reg, varname.clone()))
+ } else {
+ // Store result in local variable
+ let store_code = self.allocator.store_var(varname, &temp_reg);
+ code.append(store_code);
+ }
+
+ self.allocator.free_temp(result_reg);
+ self.allocator.free_temp(temp_reg);
+ }
+
+ Statement::Return(expr) => {
+ if let Some(e) = expr {
+ let (result_reg, expr_code) = self.generate_expression(e, true, func_body)?;
+ code.append(expr_code);
+ code.push(I::stw_reg_offset(result_reg, Register::Bpr, 8));
+ code.push(I::jmp("_ret"));
+ self.allocator.free_temp(result_reg);
+ }
+ }
+
+ Statement::If {
+ condition,
+ then_stmt,
+ else_stmt,
+ } => {
+ // Generate condition
+ let (cond_reg, cond_code) = self.generate_expression(condition, true, func_body)?;
+ code.append(cond_code);
+
+ // Generate unique labels
+ let then_label = format!("_then_{}", self.get_unique_label());
+ let else_label = format!("_else_{}", self.get_unique_label());
+ let end_label = format!("_end_{}", self.get_unique_label());
+
+ // Jump to else if condition is false
+ code.push(I::jez(cond_reg, else_label.clone()));
+ self.allocator.free_temp(cond_reg);
+
+ // Then block
+ code.push(I::label(then_label));
+ for s in then_stmt {
+ code.append(self.generate_statement(s, func_body)?);
+ }
+
+ if then_stmt.is_empty() {
+ code.push(I::Nop);
+ }
+
+ code.push(I::jmp(end_label.clone()));
+
+ // Else block
+ code.push(I::label(else_label));
+ for s in else_stmt {
+ code.append(self.generate_statement(s, func_body)?);
+ }
+
+ if else_stmt.is_empty() {
+ code.push(I::Nop);
+ }
+
+ code.push(I::label(end_label));
+ }
+
+ Statement::While { condition, body } => {
+ let loop_start = format!("_while_start_{}", self.get_unique_label());
+ let loop_end = format!("_while_end_{}", self.get_unique_label());
+
+ code.push(I::label(&loop_start));
+
+ // Generate condition
+ let (cond_reg, cond_code) = self.generate_expression(condition, true, func_body)?;
+ code.append(cond_code);
+
+ code.push(I::jez(cond_reg, loop_end.clone()));
+ self.allocator.free_temp(cond_reg);
+
+ // Loop body
+ for s in body {
+ code.append(self.generate_statement(s, func_body)?);
+ }
+
+ code.push(I::jmp(loop_start));
+ code.push(I::label(loop_end));
+ }
+
+ Statement::Loop(body) => {
+ let loop_start = format!("_loop_start_{}", self.get_unique_label());
+ code.push(I::label(&loop_start));
+
+ for s in body {
+ code.append(self.generate_statement(s, func_body)?);
+ }
+
+ code.push(I::jmp(loop_start));
+ }
+
+ Statement::Expression { expr } => {
+ let (result_reg, expr_code) = self.generate_expression(expr, false, func_body)?;
+ code.append(expr_code);
+ self.allocator.free_temp(result_reg);
+ }
+
+ Statement::Block(statements) => {
+ for s in statements {
+ code.append(self.generate_statement(s, func_body)?);
+ }
+ }
+ }
+
+ Ok(code)
+ }
+
+ // Example: Generate code for an expression
+ // Returns (register containing result, assembly code)
+ fn generate_expression(
+ &mut self,
+ expr: &Expression,
+ use_result: bool,
+ func_body: &mut IB,
+ ) -> Result<(Register, IB), CompilerError> {
+ let mut code = IB::new();
+
+ match expr {
+ Expression::Empty => Ok((Register::Null, code)),
+
+ Expression::Number(n) => match n {
+ Number::Signed(value, _) => {
+ let (reg, alloc_code) = self.allocator.alloc_temp()?;
+ code.append(alloc_code);
+
+ // Load immediate value
+ code.push(I::lwi(*value as u32, reg));
+
+ Ok((reg, code))
+ }
+ Number::Unsigned(value, _) => {
+ let (reg, alloc_code) = self.allocator.alloc_temp()?;
+ code.append(alloc_code);
+
+ // Load immediate value
+ code.push(I::lwi(*value as u32, reg));
+
+ Ok((reg, code))
+ }
+ },
+
+ Expression::CharLiteral(value) => {
+ let (reg, alloc_code) = self.allocator.alloc_temp()?;
+ code.append(alloc_code);
+
+ // Load immediate value
+ code.push(I::comment(format!("char literal '{value}'")));
+ code.push(I::lwi(*value as u32, reg));
+
+ Ok((reg, code))
+ }
+
+ Expression::StringLiteral(value) => {
+ let (reg, alloc_code) = self.allocator.alloc_temp()?;
+ code.append(alloc_code);
+
+ // write string into memory
+ let uuid = self.get_unique_label();
+
+ func_body.insert(0, I::db_string(format!("str_{uuid}"), value));
+
+ // Load pointer to string
+ code.push(I::lwi_label(format!("str_{uuid}"), reg));
+
+ Ok((reg, code))
+ }
+
+ Expression::ArrayLiteral { elements, type_id } => todo!(),
+ Expression::StructLiteral {
+ name,
+ fields,
+ type_id,
+ } => todo!(),
+
+ Expression::Variable { name, .. } => {
+ if self.is_global(&name.name) {
+ // Allocate a temporary register for the global
+ let (reg, alloc_code) = self.allocator.alloc_temp()?;
+ code.append(alloc_code);
+
+ // Load from global label
+ code.push(I::ldw_label(name.name.clone(), reg));
+
+ Ok((reg, code))
+ } else {
+ // Local variable - use existing allocator logic
+ let (reg, load_code) = self.allocator.load_var(&name.name)?;
+ code.append(load_code);
+ Ok((reg, code))
+ }
+ }
+
+ Expression::Binary {
+ op, left, right, ..
+ } => {
+ // Evaluate left operand
+ let (left_reg, left_code) = self.generate_expression(left, true, func_body)?;
+ code.append(left_code);
+
+ // Evaluate right operand
+ let (right_reg, right_code) = self.generate_expression(right, true, func_body)?;
+ code.append(right_code);
+
+ // Allocate result register
+ let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
+ code.append(result_alloc);
+
+ // Generate operation
+ match op {
+ BinaryOperator::Add => {
+ code.push(I::add(left_reg, right_reg, result_reg));
+ }
+ BinaryOperator::Sub => {
+ code.push(I::sub(left_reg, right_reg, result_reg));
+ }
+ BinaryOperator::Mul => {
+ self.include("maths", "./lib/maths/core.dsa");
+ // Call multiply function
+ code.push(I::push(right_reg));
+ code.push(I::push(left_reg));
+ code.push(I::call("maths::multiply"));
+ code.push(I::pop(result_reg));
+ code.push(I::pop(Register::Zero));
+ }
+ BinaryOperator::Div => {
+ return Err(CompilerError::Unimplemented(
+ "TODO: write proper div function for DSA".to_string(),
+ ));
+ // self.include("maths", "./lib/maths/core.dsa");
+ // // Call divide function
+ // code.push(format!("\tpush {}", right_reg));
+ // code.push(format!("\tpush {}", left_reg));
+ // code.push("\tcall maths::divide".to_string());
+ // code.push(format!("\tpop {}", result_reg));
+ // code.push("\tpop zero".to_string());
+ }
+ BinaryOperator::Mod => {
+ return Err(CompilerError::Unimplemented(
+ "TODO: write proper mod function for DSA".to_string(),
+ ));
+ // self.include("maths", "./lib/maths/core.dsa");
+ // // Call modulo function
+ // code.push(format!("\tpush {}", right_reg));
+ // code.push(format!("\tpush {}", left_reg));
+ // code.push("\tcall maths::modulo".to_string());
+ // code.push(format!("\tpop {}", result_reg));
+ // code.push("\tpop zero".to_string());
+ }
+ BinaryOperator::BitwiseAnd => {
+ code.push(I::and(left_reg, right_reg, result_reg));
+ }
+ BinaryOperator::BitwiseOr => {
+ code.push(I::or(left_reg, right_reg, result_reg));
+ }
+ BinaryOperator::BitwiseXor => {
+ code.push(I::xor(left_reg, right_reg, result_reg));
+ }
+ BinaryOperator::LogicalAnd => {
+ return Err(CompilerError::Unimplemented(
+ "assembler/ISA does not yet support logical and!".to_string(),
+ ));
+ }
+ BinaryOperator::LogicalOr => {
+ return Err(CompilerError::Unimplemented(
+ "assembler/ISA does not yet support logical or!".to_string(),
+ ));
+ }
+ BinaryOperator::LeftShift => {
+ code.push(I::shl(left_reg, right_reg, 0, result_reg));
+ }
+ BinaryOperator::RightShift => {
+ code.push(I::shr(left_reg, right_reg, 0, result_reg));
+ }
+ // Comparison operators - return 1 (true) or 0 (false)
+ BinaryOperator::Equal => {
+ code.push(I::cmp(Equal, left_reg, right_reg, result_reg))
+ }
+ BinaryOperator::NotEqual => {
+ code.push(I::cmp(NotEqual, left_reg, right_reg, result_reg))
+ }
+ BinaryOperator::LessThan => {
+ code.push(I::cmp(Less, left_reg, right_reg, result_reg))
+ }
+ BinaryOperator::LessOrEqual => {
+ code.push(I::cmp(LessEqual, left_reg, right_reg, result_reg))
+ }
+ BinaryOperator::GreaterThan => {
+ code.push(I::cmp(Greater, left_reg, right_reg, result_reg))
+ }
+ BinaryOperator::GreaterOrEqual => {
+ code.push(I::cmp(GreaterEqual, left_reg, right_reg, result_reg))
+ } // _ => unimplemented!(),
+ }
+
+ // Free operand registers (allocator will protect variables)
+ self.allocator.free_temp(left_reg);
+ self.allocator.free_temp(right_reg);
+
+ Ok((result_reg, code))
+ }
+
+ Expression::UnaryPostfix { op, operand, .. } => {
+ let (operand_reg, operand_code) =
+ self.generate_expression(operand, true, func_body)?;
+ code.append(operand_code);
+
+ let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
+ code.append(result_alloc);
+
+ match op {
+ UnaryOperator::Increment => {
+ // postfix increment - return old value
+ code.push(I::mov(operand_reg, result_reg));
+ }
+ UnaryOperator::Decrement => {
+ // postfix decrement - return old value
+ code.push(I::mov(operand_reg, result_reg));
+ }
+ _ => {
+ return Err(CompilerError::Generic(format!("{op} is prefix only!")));
+ }
+ }
+
+ self.allocator.free_temp(operand_reg);
+ Ok((result_reg, code))
+ }
+
+ Expression::Unary { op, operand, .. } => {
+ let (operand_reg, operand_code) =
+ self.generate_expression(operand, true, func_body)?;
+ code.append(operand_code);
+
+ let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
+ code.append(result_alloc);
+
+ match op {
+ UnaryOperator::Minus => {
+ // Negate: result = 0 - operand
+ code.push(I::sub(Register::Zero, operand_reg, result_reg));
+ }
+ UnaryOperator::Plus => {
+ // Just move
+ code.push(I::mov(operand_reg, result_reg));
+ }
+ UnaryOperator::Dereference => {
+ code.push(I::ldw_reg(operand_reg, result_reg));
+ }
+ UnaryOperator::AddressOf => {
+ // ensure the referenced variable is on the stack and return its
+ // address.
+ let (offset, alloc_code) = self.allocator.free_register(&operand_reg)?;
+ code.push(alloc_code);
+ code.push(I::iadd_dest(
+ Register::Spr,
+ offset - self.allocator.get_stack_offset(),
+ result_reg,
+ ));
+ }
+ UnaryOperator::SizeOf => {
+ if let Ok(id) = operand.type_id() {
+ let size = id.size();
+ code.push(I::lwi(size as u32, result_reg));
+ }
+ }
+ UnaryOperator::Increment => {
+ // prefix increment
+ code.push(I::mov(operand_reg, result_reg));
+ code.push(I::iadd_dest(operand_reg, 1, result_reg));
+ }
+ UnaryOperator::Decrement => {
+ // prefix decrement
+ code.push(I::mov(operand_reg, result_reg));
+ code.push(I::iadd_dest(operand_reg, -1, result_reg));
+ }
+ UnaryOperator::BitwiseNot => {
+ code.push(I::not(operand_reg, result_reg));
+ }
+ UnaryOperator::LogicalNot => {
+ return Err(CompilerError::Unimplemented(
+ "Assembler/ISA does not yet support logical not".to_string(),
+ ));
+ }
+ _ => {
+ return Err(CompilerError::Generic(format!("{op} is postfix only!")));
+ }
+ }
+
+ self.allocator.free_temp(operand_reg);
+ Ok((result_reg, code))
+ }
+
+ Expression::Call {
+ func: Call { name, args },
+ ..
+ } => {
+ // first evaluate all the args we're going to need
+ let mut arg_regs = Vec::new();
+ for arg in args.iter().rev() {
+ let (arg_reg, arg_code) = self.generate_expression(arg, true, func_body)?;
+ code.append(arg_code);
+ arg_regs.push(arg_reg);
+ }
+
+ // Save caller-saved registers and track which ones we saved
+ let saved_regs = self.allocator.get_caller_saved_registers();
+ for reg in &saved_regs {
+ // spill variables to stack
+ code.push(self.allocator.free_register(reg).unwrap().1);
+ }
+
+ // Evaluate and push arguments in reverse order
+ for (i, arg_reg) in arg_regs.iter().enumerate() {
+ code.push(I::comment(format!("push arg {}", args.len() - 1 - i)));
+ code.push(I::push(*arg_reg));
+ }
+
+ if self.symbols.contains(&name.name) {
+ // Call local function
+ code.push(I::call(name.to_string()));
+ } else if let Some(ns) = name.namespace.clone()
+ && self.imports.contains_key(&ns)
+ {
+ code.push(I::call(name.to_string()));
+ } else {
+ return Err(CompilerError::Undefined(name.clone()));
+ }
+
+ let result_reg: Register;
+
+ if use_result {
+ let (temp_result_reg, result_alloc) = self.allocator.alloc_temp()?;
+ result_reg = temp_result_reg;
+
+ code.append(result_alloc);
+ code.push(I::pop(result_reg));
+
+ // Clean up arguments
+ if args.len() > 1 {
+ for _ in 0..(args.len() - 1) {
+ code.push(I::pop(Register::Zero));
+ }
+ }
+ } else {
+ result_reg = Register::Zero;
+
+ // Clean up arguments
+ if args.len() > 0 {
+ for _ in 0..(args.len()) {
+ code.push(I::pop(Register::Zero));
+ }
+ }
+ }
+
+ // Free argument registers
+ for reg in arg_regs {
+ self.allocator.free_temp(reg);
+ }
+
+ Ok((result_reg, code))
+ }
+
+ Expression::IndexAccess {
+ expr,
+ index,
+ type_id,
+ } => {
+ let (expr_reg, expr_alloc) = self.generate_expression(expr, true, func_body)?;
+ code.append(expr_alloc);
+
+ let (index_reg, index_alloc) = self.generate_expression(index, true, func_body)?;
+ code.append(index_alloc);
+
+ let (result_reg, result_alloc) = self.allocator.alloc_temp()?;
+ code.append(result_alloc);
+
+ // add the expr pointer to the index to get the final address.
+ code.push(I::add(expr_reg, index_reg, result_reg));
+ // load the value at the address.
+ code.push(I::ldw_reg(result_reg, result_reg));
+
+ self.allocator.free_temp(expr_reg);
+ self.allocator.free_temp(index_reg);
+
+ Ok((result_reg, code))
+ }
+ Expression::MemberAccess {
+ expr,
+ field_name,
+ type_id,
+ } => Err(CompilerError::Unimplemented(
+ "Structs are not yet implemented!".to_string(),
+ )),
+
+ Expression::TypeCast {
+ expr,
+ target_type,
+ type_id,
+ } => {
+ let (expr_reg, expr_code) = self.generate_expression(expr, true, func_body)?;
+
+ // not sure if we actually need to do anything here.
+ // for now we just return the previous expression.
+ Ok((expr_reg, expr_code))
+ }
+ }
+ }
+
+ // Helper for generating unique labels
+ fn get_unique_label(&mut self) -> String {
+ // You'd implement a counter here
+ static COUNTER: AtomicU32 = AtomicU32::new(0);
+
+ let val = COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
+ (val + 1).to_string()
+ }
+}
diff --git a/tooling/dscs_compiler/src/backend/instruction.rs b/tooling/dscs_compiler/src/backend/instruction.rs
new file mode 100644
index 0000000..b5407fc
--- /dev/null
+++ b/tooling/dscs_compiler/src/backend/instruction.rs
@@ -0,0 +1,858 @@
+use std::fmt;
+
+use crate::backend::registers::Register;
+
+pub struct InsBlock {
+ instructions: Vec,
+}
+
+impl InsBlock {
+ pub fn new() -> Self {
+ Self {
+ instructions: vec![],
+ }
+ }
+
+ pub fn insert(&mut self, index: usize, instr: Instruction) {
+ self.instructions.insert(index, instr);
+ }
+
+ pub fn push(&mut self, instr: Instruction) {
+ self.instructions.push(instr);
+ }
+
+ pub fn append(&mut self, mut other: Self) {
+ self.instructions.append(&mut other.instructions);
+ }
+
+ pub fn extend(&mut self, instrs: impl IntoIterator- ) {
+ self.instructions.extend(instrs);
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.instructions.is_empty()
+ }
+
+ pub fn len(&self) -> usize {
+ self.instructions.len()
+ }
+
+ pub fn iter(&self) -> impl Iterator
- {
+ self.instructions.iter()
+ }
+}
+
+impl From> for InsBlock {
+ fn from(instructions: Vec) -> Self {
+ Self { instructions }
+ }
+}
+
+impl From for InsBlock {
+ fn from(instr: Instruction) -> Self {
+ Self {
+ instructions: vec![instr],
+ }
+ }
+}
+
+pub enum Comparison {
+ Greater,
+ GreaterEqual,
+ Equal,
+ NotEqual,
+ Less,
+ LessEqual,
+}
+
+#[derive(Debug, Clone)]
+pub enum Instruction {
+ // Labels and comments
+ Label(Label),
+ Comment {
+ text: String,
+ top_level: bool,
+ },
+ Newline,
+
+ // Data Directives
+ Db {
+ label: String,
+ data: Vec,
+ },
+ Dh {
+ label: String,
+ data: Vec,
+ },
+ Dw {
+ label: String,
+ data: Vec,
+ },
+ DString {
+ // alias for db.
+ label: String,
+ data: String,
+ },
+
+ Resx {
+ label: String,
+ size: u32,
+ },
+
+ // Include
+ Include {
+ name: String,
+ path: String,
+ },
+
+ // Data movement
+ Mov {
+ src: Register,
+ dest: Register,
+ },
+ Movs {
+ src: Register,
+ dest: Register,
+ },
+
+ // Memory operations
+ Ldb {
+ src: MemOperand,
+ dest: Register,
+ },
+ Ldh {
+ src: MemOperand,
+ dest: Register,
+ },
+ Ldw {
+ src: MemOperand,
+ dest: Register,
+ },
+ Stb {
+ src: Register,
+ dest: MemOperand,
+ },
+ Sth {
+ src: Register,
+ dest: MemOperand,
+ },
+ Stw {
+ src: Register,
+ dest: MemOperand,
+ },
+
+ // Immediate loads
+ Lli {
+ imm: Imm,
+ dest: Register,
+ },
+ Lui {
+ imm: Imm,
+ dest: Register,
+ },
+ Lwi {
+ imm: Imm,
+ dest: Register,
+ },
+ LwiLabel {
+ label: String,
+ dest: Register,
+ },
+
+ // Arithmetic
+ Add {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Sub {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+
+ AddI {
+ src: Register,
+ imm: Imm,
+ dest: Option,
+ },
+ SubI {
+ src: Register,
+ imm: Imm,
+ dest: Option,
+ },
+
+ // Bitwise
+ And {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Or {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Xor {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Not {
+ src: Register,
+ dest: Register,
+ },
+ Nand {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Nor {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Xnor {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+
+ // Shifts
+ Shl {
+ src1: Register,
+ r_shamt: Register,
+ i_shamt: u16,
+ dest: Register,
+ },
+ Shr {
+ src1: Register,
+ r_shamt: Register,
+ i_shamt: u16,
+ dest: Register,
+ },
+
+ Ieq {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Ine {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Ilt {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Ile {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Igt {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+ Ige {
+ src1: Register,
+ src2: Register,
+ dest: Register,
+ },
+
+ // Jumps
+ Jmp {
+ target: Label,
+ },
+
+ Jnz {
+ src: Register,
+ target: Label,
+ },
+ Jez {
+ src: Register,
+ target: Label,
+ },
+
+ Jic {
+ target: Label,
+ },
+ Jnc {
+ target: Label,
+ },
+
+ // Stack
+ Push {
+ reg: Register,
+ },
+ Pop {
+ reg: Register,
+ },
+
+ // Function calls
+ Call {
+ target: String,
+ }, // namespace::function
+ Return,
+
+ // System
+ Hlt,
+ Nop,
+ Int {
+ code: u8,
+ },
+}
+
+pub enum DataDirective {
+ U8(Vec),
+ U16(Vec),
+ U32(Vec),
+ String(String),
+ Char(char),
+}
+
+impl fmt::Display for Instruction {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Label(l) => write!(f, "{}:", l),
+ Self::Newline => write!(f, ""), /* empty string as newlines are inserted */
+ // automatically.
+ Self::Comment { text, top_level } => write!(
+ f,
+ "{}",
+ text.lines()
+ .map(|line| format!(
+ "{}// {}",
+ if *top_level { "" } else { " " },
+ line.trim(),
+ ))
+ .collect::>()
+ .join("\n")
+ ),
+
+ Self::Include { name, path } => write!(f, "include {name}: \"{}\"", path),
+
+ Self::Db { label, data } => write!(
+ f,
+ "db {}: {}",
+ label,
+ data.iter()
+ .map(|&b| format!("{:#04X}", b))
+ .collect::>()
+ .join(", ")
+ ),
+ Self::Dh { label, data } => write!(
+ f,
+ "dh {}: {}",
+ label,
+ data.iter()
+ .map(|&b| format!("{:#06X}", b))
+ .collect::>()
+ .join(", ")
+ ),
+ Self::Dw { label, data } => write!(
+ f,
+ "dw {}: {}",
+ label,
+ data.iter()
+ .map(|&b| format!("{:#08X}", b))
+ .collect::>()
+ .join(", ")
+ ),
+ Self::DString { label, data } => write!(f, "db {}: \"{}\"", label, data),
+
+ Self::Resx { label, size } => write!(f, "resx {}: {}", label, size),
+
+ Self::Mov { src, dest } => write!(f, " mov {}, {}", src, dest),
+ Self::Movs { src, dest } => write!(f, " movs {}, {}", src, dest),
+
+ Self::Ldb { src: addr, dest } => {
+ let (reg, offset) = reg_and_offset(addr);
+ write!(f, " ldb {}, {}, {}", reg, dest, offset)
+ }
+ Self::Ldh { src: addr, dest } => {
+ let (reg, offset) = reg_and_offset(addr);
+ write!(f, " ldh {}, {}, {}", reg, dest, offset)
+ }
+ Self::Ldw { src, dest } => {
+ let (reg, offset) = reg_and_offset(src);
+ write!(f, " ldw {}, {}, {}", reg, dest, offset)
+ }
+ // Self::Ldbs { addr, dest } => {
+ // write!(f, " ldbs {}, {}", format_mem_operand(addr), dest)
+ // }
+ // Self::Ldhs { addr, dest } => {
+ // write!(f, " ldhs {}, {}", format_mem_operand(addr), dest)
+ // }
+ // Self::Ldws { addr, dest } => {
+ // write!(f, " ldws {}, {}", format_mem_operand(addr), dest)
+ // }
+ Self::Stb { src, dest: addr } => {
+ let (reg, offset) = reg_and_offset(addr);
+ write!(f, " stb {}, {}, {}", src, reg, offset)
+ }
+ Self::Sth { src, dest: addr } => {
+ let (reg, offset) = reg_and_offset(addr);
+ write!(f, " sth {}, {}, {}", src, reg, offset)
+ }
+ Self::Stw { src, dest: addr } => {
+ let (reg, offset) = reg_and_offset(addr);
+ write!(f, " stw {}, {}, {}", src, reg, offset)
+ }
+
+ Self::Lli { imm, dest } => write!(f, " lli {}, {}", imm, dest),
+ Self::Lui { imm, dest } => write!(f, " lui {}, {}", imm, dest),
+ Self::Lwi { imm, dest } => write!(f, " lwi {}, {}", imm, dest),
+ Self::LwiLabel { label, dest } => write!(f, " lwi {}, {}", label, dest),
+
+ // arithmetic
+ Self::Add { src1, src2, dest } => {
+ write!(f, " add {}, {}, {}", src1, src2, dest)
+ }
+ Self::Sub { src1, src2, dest } => {
+ write!(f, " sub {}, {}, {}", src1, src2, dest)
+ }
+ Self::And { src1, src2, dest } => {
+ write!(f, " and {}, {}, {}", src1, src2, dest)
+ }
+ Self::Or { src1, src2, dest } => {
+ write!(f, " or {}, {}, {}", src1, src2, dest)
+ }
+ Self::Nand { src1, src2, dest } => {
+ write!(f, " nand {}, {}, {}", src1, src2, dest)
+ }
+ Self::Xor { src1, src2, dest } => {
+ write!(f, " xor {}, {}, {}", src1, src2, dest)
+ }
+ Self::Nor { src1, src2, dest } => {
+ write!(f, " nor {}, {}, {}", src1, src2, dest)
+ }
+ Self::Not { src, dest } => {
+ write!(f, " not {} {}", src, dest)
+ }
+ Self::Xnor { src1, src2, dest } => {
+ write!(f, " xnor {}, {}, {}", src1, src2, dest)
+ }
+ Self::AddI { src, imm, dest } => {
+ if let Some(d) = dest {
+ write!(f, " addi {}, {}, {}", src, imm, d)
+ } else {
+ write!(f, " addi {}, {}", src, imm)
+ }
+ }
+ Self::SubI { src, imm, dest } => {
+ if let Some(d) = dest {
+ write!(f, " subi {}, {}, {}", src, imm, d)
+ } else {
+ write!(f, " subi {}, {}", src, imm)
+ }
+ }
+
+ // shift instructions
+ Self::Shl {
+ src1,
+ r_shamt,
+ i_shamt,
+ dest,
+ } => {
+ write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
+ }
+ Self::Shr {
+ src1,
+ r_shamt,
+ i_shamt,
+ dest,
+ } => {
+ write!(f, " shl {}, {}, {}, {}", src1, r_shamt, i_shamt, dest)
+ }
+
+ Self::Ieq { src1, src2, dest } => write!(f, " ieq {}, {}, {}", src1, src2, dest),
+ Self::Ine { src1, src2, dest } => write!(f, " ine {}, {}, {}", src1, src2, dest),
+ Self::Ilt { src1, src2, dest } => write!(f, " ilt {}, {}, {}", src1, src2, dest),
+ Self::Ile { src1, src2, dest } => write!(f, " ile {}, {}, {}", src1, src2, dest),
+ Self::Igt { src1, src2, dest } => write!(f, " igt {}, {}, {}", src1, src2, dest),
+ Self::Ige { src1, src2, dest } => write!(f, " ige {}, {}, {}", src1, src2, dest),
+
+ // jump instructions
+ Self::Jmp { target } => write!(f, " jmp {}", target),
+ Self::Jic { target } => write!(f, " jic {}", target),
+ Self::Jnc { target } => write!(f, " jnc {}", target),
+ Self::Jez { src, target } => write!(f, " jez {}, {}", src, target),
+ Self::Jnz { src, target } => write!(f, " jnz {}, {}", src, target),
+
+ // stack instructions
+ Self::Push { reg } => write!(f, " push {}", reg),
+ Self::Pop { reg } => write!(f, " pop {}", reg),
+
+ // call & return instructions
+ Self::Call { target } => write!(f, " call {}", target),
+ Self::Return => write!(f, " ret"),
+
+ // misc instructions
+ Self::Int { code } => write!(f, " int {}", code),
+ Self::Hlt => write!(f, " hlt"),
+ Self::Nop => write!(f, " nop"),
+ }
+ }
+}
+
+impl Instruction {
+ // data directives
+ pub fn db_string(label: impl Into, data: impl Into) -> Self {
+ Self::DString {
+ label: label.into(),
+ data: data.into(),
+ }
+ }
+
+ pub fn db_word(label: impl Into, data: u32) -> Self {
+ Self::Dw {
+ label: label.into(),
+ data: vec![data],
+ }
+ }
+
+ pub fn db_bytes(label: impl Into, data: &[u8]) -> Self {
+ Self::Db {
+ label: label.into(),
+ data: data.to_vec(),
+ }
+ }
+
+ // Movement
+ pub fn mov(src: R1, dest: R2) -> Self
+ where
+ R1: Into,
+ R2: Into,
+ {
+ Self::Mov {
+ src: src.into(),
+ dest: dest.into(),
+ }
+ }
+
+ // Memory loads
+ pub fn ldw_reg(base: R, dest: Register) -> Self
+ where
+ R: Into,
+ {
+ Self::Ldw {
+ src: MemOperand::RegIndirect(base.into()),
+ dest,
+ }
+ }
+
+ pub fn ldw_reg_offset(base: R, dest: Register, offset: i32) -> Self
+ where
+ R: Into,
+ {
+ Self::Ldw {
+ src: MemOperand::RegOffset(base.into(), offset),
+ dest,
+ }
+ }
+
+ pub fn ldw_label(label: impl Into