From 537b7f8693c9e83fe9f440c4857eb6ec2fb88c45 Mon Sep 17 00:00:00 2001 From: Fey Naomi Schrewe Date: Sun, 2 Nov 2025 20:56:12 +0100 Subject: [PATCH] feat: implement linking --- host/assembler.fnl | 49 +++++++++++++++++++++++++++----------- host/assembler/opcodes.fnl | 49 ++++++++++++++++++++++++++++---------- target/boot.lasm | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 target/boot.lasm diff --git a/host/assembler.fnl b/host/assembler.fnl index 45445f8..abd3ac4 100644 --- a/host/assembler.fnl +++ b/host/assembler.fnl @@ -2,10 +2,11 @@ {: r : move : branch - : branch-link} + : branch-link + : relocatable-instructions} (require :host.assembler.opcodes)) -(local {: any : slice : push : map} (require :deps.lume)) -(local {: word->byte} (require :host.util)) +(local {: any : slice : push : map :match find-pred} (require :deps.lume)) +(local {: word->byte : hex} (require :host.util)) (fn label [name] {:label name}) (fn label? [value] @@ -16,6 +17,12 @@ (:error (.. "Duplicate label " label)) (tset table :labels label index))) +(fn replace-label! + [form offset] + "Replace the label form with *offset* for later linking" + (each [i v (ipairs form)] (when (label? v) (tset form i offset))) + form) + (fn assemble [forms offset] (let [state {:labels {} :instructions [] :reverse-labels []}] (each [_ form (ipairs forms)] @@ -23,18 +30,32 @@ (insert-label state form (+ offset (* 4 (length (. state :instructions))))) (push (. state :instructions) (if (any (slice form 2) label?) - (do - (push (. state :reverse-labels) (+ 1 (length (. state :instructions)))) - form) + (let [{: type :offset addend} (. relocatable-instructions (. form 1))] + (push (. state :reverse-labels) {: type :label (. (find-pred form label?) :label) :offset (+ offset (* 4 (length (. state :instructions))))}) + ((. form 1) (table.unpack (replace-label! (slice form 2) addend)))) ((. form 1) (table.unpack (slice form 2))))))) state)) -; (fn link [{: labels : instructions : reverse-labels}] -; ()) +(fn link [{: labels : instructions : reverse-labels} base] + (each [_ {: type :label target : offset} (ipairs reverse-labels)] + (match type + :jump-24 (let [index (+ 1 (/ (- offset base) 4)) + instruction (. instructions index) + addend (lshift (band instruction 0xffffff) 2) + symbol (. labels target) + relocation (- (+ symbol addend) offset) + new-instruction + (bor + (band instruction (bnot 0xffffff)) + (band (rshift relocation 2) 0xffffff))] + (print (hex (band (rshift relocation 2) 0xffffff)) (hex instruction) (hex new-instruction)) + (tset instructions index new-instruction)))) + instructions) -(assemble - [[move (r 8) (r 2)] - :label-1 - [move (r 9) (r 3)] - [branch (label :label-1)]] - 0x8000) +(map (link (assemble + [[move (r 8) (r 2)] + :label-1 + [move (r 9) (r 3)] + [branch (label :label-1)]] + 0x8000) + 0x8000) hex) diff --git a/host/assembler/opcodes.fnl b/host/assembler/opcodes.fnl index d5fe095..7752702 100644 --- a/host/assembler/opcodes.fnl +++ b/host/assembler/opcodes.fnl @@ -1,4 +1,7 @@ (local util (require :host.util)) +(local {: push} (require :deps.lume)) + +(local relocatable-instructions {}) (macro construct-opcode [& forms] `(bor ,(unpack @@ -29,6 +32,10 @@ :sp 0xd n n)}) +(fn c [n] + "Create a coprocessor register for use in instructions" + {:coprocessor n}) + (fn register? [x] (and (= (type x) :table) (. x :register))) @@ -48,8 +55,17 @@ cond (. conditions (or cond :always))] {: cond : set-flags})) +(fn immediate-12bit [x] + "Calculate a 12 bit immediate value" + (let [rotation (calculate-rotation x)] + (when (= nil rotation) (error "Unencodable immediate value")) + (bor + (band (util.rotate-left x rotation) 0xff) + (lshift (/ rotation 2) 8)))) + (fn move [dest source ?options] - {:fnl/docstring "Set register *dest* to a value\n*source* can be a register or an immediate value smaller than 4096" + {:fnl/docstring + "Set register *dest* to a value\n*source* can be a register or an immediate value" :fnl/arglist [dest source & {: cond : flags}]} (let [{: cond : set-flags} (parse-conditions ?options) source-register (register? source) @@ -62,16 +78,15 @@ [set-flags 20] [dest 12] [source-register 0]) - (let [rotation (calculate-rotation source)] - (when (= nil rotation) (error "Unencodable immediate value")) - (construct-opcode - (cond 28) - (1 25) - (0xd 21) - (set-flags 20) - (dest 12) - ((/ rotation 2) 8) - ((band (util.rotate-left source rotation) 0xff) 0)))))) + (construct-opcode + (cond 28) + (1 25) + (0xd 21) + (set-flags 20) + (dest 12) + (immediate-12bit source))))) + +(fn subtract [offset ?options]) (fn branch [offset ?options] (let [{: cond} (parse-conditions ?options) @@ -81,6 +96,7 @@ (cond 28) (10 24) ((band (rshift offset 2) 0xffffff) 0)))) +(tset relocatable-instructions branch {:type :jump-24 :offset -8}) (fn branch-link [offset ?options] (let [{: cond} (parse-conditions ?options) @@ -90,8 +106,17 @@ (cond 28) (11 24) ((band (rshift offset 2) 0xffffff) 0)))) +(tset relocatable-instructions branch-link {:type :jump-24 :offset -8}) + +(fn branch-exchange [{: register} ?options] + (let [{: cond} (parse-conditions ?options)] + (construct-opcode + (cond 28) + (0x12fff1 4) + (register 0)))) {: r : move : branch - : branch-link} + : branch-link + : relocatable-instructions} diff --git a/target/boot.lasm b/target/boot.lasm new file mode 100644 index 0000000..d8459a0 --- /dev/null +++ b/target/boot.lasm @@ -0,0 +1,43 @@ +(config! {:address 0x8000}) +(const! mmio-base 0x3f000000) +(label! :boot) +(move sp :boot) + +; shut down cores 1-3 +(move-to-coprocessor {:processor 15 + :operations [0 5] + :source-register (r 5) + :dest-registers [(c 0) (c 0)]}) + +(and (r 5) (r 5) 3) +(cmp (r 5) 0) +(branch {:cond :not-equal} :halt) + +;; set r3 to gpio base +(move (r 3) :mmio-base) +(or (r 3) (r 3) 0x200000) + +;; set r4 to uart-base +(or (r 4) (r 4) 0x1000) + +;; disable uart 0 +(or (r 5) (r 4) 0x30) +(store (r 5) 0) + +;;; see https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +;;; pages 100 and following + +;; disable pull-up/pull down +(or (r 5) (r 3) 0x94) +(store (r 5) 0) +;; delay for 150 cycles +(move (r 0) 150) +(branch-link :delay) + +(label! end) +(branch :end) + +(label! delay) +(subtract {:flags true} (r 0) 1) +(branch {:cond :not-equal} #delay) +(branch-exchange lr)