diff --git a/host/assembler.fnl b/host/assembler.fnl new file mode 100644 index 0000000..ae638d8 --- /dev/null +++ b/host/assembler.fnl @@ -0,0 +1 @@ +(local opcodes (require :host.assembler.opcodes)) diff --git a/host/assembler/elf.fnl b/host/assembler/elf.fnl index 18b8c32..67f86a7 100644 --- a/host/assembler/elf.fnl +++ b/host/assembler/elf.fnl @@ -1,5 +1,3 @@ -(local {: print-string} (require :print)) - (local byte string.char) (fn word->byte [x] "convert a 32 bit word into a little endian byte string" @@ -70,7 +68,3 @@ :section-header-count 0 :string-section-index 0 :phead-count 0})) - -(with-open [f (io.open :test.elf :w)] - (f:write elf)) -(print-string elf) diff --git a/host/assembler/opcodes.fnl b/host/assembler/opcodes.fnl new file mode 100644 index 0000000..25403ce --- /dev/null +++ b/host/assembler/opcodes.fnl @@ -0,0 +1,92 @@ +(local util (require :host.util)) + +(local conditions {:equal 0x0 + :not-equal 0x1 + :carry 0x2 + :not-carry 0x3 + :negative 0x4 + :positive 0x5 + :overflow 0x6 + :no-overflow 0x7 + :higher-unsigned 0x8 + :lower-unsigned 0x9 + :greater-equal-signed 0xa + :less-signed 0xb + :greater-signed 0xc + :less-equal-signed 0xd + :always 0xe}) + +(fn r [n] + "Create a register for use in instructions" + {:register (case n + :pc 0xf + :lr 0xe + :sp 0xd + n n)}) + +(fn register? [x] + (and (= (type x) :table) (. x :register))) + + +(fn bit [flag] + "Convert a boolean to an integer for shifting" + (if flag 1 0)) + +(fn calculate-rotation [x] + "Construct a valid arm rotation of x" + (faccumulate [acc nil i 0 32 2 &until acc] + (let [rotated (util.rotate-left x i)] (if (<= rotated 0xff) i)))) + +(fn parse-conditions [?options] + (let [{:cond cond :flags set-flags} (or ?options {}) + set-flags (bit (or set-flags false)) + cond (. conditions (or cond :always))] + {: cond : set-flags})) + +(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/arglist [dest source & {: cond : flags}]} + (let [{: cond : set-flags} (parse-conditions ?options) + source-register (register? source) + dest (register? dest)] + (when (not dest) (error "dest must be a register")) + (if source-register + (bor + (lshift cond 28) + (lshift 0xd 21) + (lshift set-flags 20) + (lshift dest 12) + source-register) + (let [rotation (calculate-rotation source)] + (when (= nil rotation) (error "Unencodable immediate value")) + (bor + (lshift cond 28) + (lshift 1 25) + (lshift 0xd 21) + (lshift set-flags 20) + (lshift dest 12) + (lshift (/ rotation 2) 8) + (band (util.rotate-left source rotation) 0xff)))))) + +(fn branch [offset ?options] + (let [{: cond} (parse-conditions ?options) + max-immediate 0xffffff + offset (band offset util.word-max)] + (bor + (lshift cond 28) + (lshift 10 24) + (band (rshift offset 2) 0xffffff)))) + +(fn branch-link [offset ?options] + (let [{: cond} (parse-conditions ?options) + max-immediate 0xffffff + offset (band offset util.word-max)] + (bor + (lshift cond 28) + (lshift 11 24) + (band (rshift offset 2) 0xffffff)))) + +{: r + : move + : branch + : branch-link} diff --git a/host/assembler/print.fnl b/host/assembler/print.fnl index 93c9a29..6cd24fc 100644 --- a/host/assembler/print.fnl +++ b/host/assembler/print.fnl @@ -1,42 +1,44 @@ -(local {:map map} (require :lume)) +(local {:map map} (require :deps.lume)) -(local string->bytes (fn [s] - (table.pack (string.byte s 1 -1)))) +(fn string->bytes [s] + (table.pack (string.byte s 1 -1))) -(local bytes->string (fn [x] (string.char (table.unpack x)))) +(fn bytes->string [x] (string.char (table.unpack x))) -(local show-bytes (fn [byte-string] - "print a string as human readble bytes" - (let [formatted (map (string->bytes byte-string) - (λ [byte] - (if byte - (string.format :%02x byte) - "##")))] - (table.concat formatted " ")))) +(fn show-bytes [byte-string] + "print a string as human readble bytes" + (let [formatted (map (string->bytes byte-string) + (λ [byte] + (if byte + (string.format :%02x byte) + "##")))] + (table.concat formatted " "))) -(local show-ascii - (fn [byte-string] (-> byte-string - (string->bytes) - (map (lambda [byte] (if (< 31 byte 126) byte 46))) - (bytes->string)))) +(fn show-ascii [byte-string] + (-> byte-string + (string->bytes) + (map (lambda [byte] (if (< 31 byte 126) byte 46))) + (bytes->string))) -(local pad-string (fn [s count] - (string.format (.. :%- count :s) s))) +(fn pad-string [s count] + (string.format (.. :%- count :s) s)) -(local print-string - (fn [byte-string] - (let [row-len 8 +(fn print-string [byte-string] + (let [row-len 8 len (length byte-string) full-rows (math.floor (/ len row-len)) rest (math.modf len full-rows)] - (for [i 0 full-rows 1] - (let [row (byte-string:sub (+ 1 (* row-len i)) (+ row-len (* row-len i)))] - (when (< 0 (length row)) (print (.. - :| (pad-string (show-bytes row) (- (* 3 row-len) 1)) - :| - " " - :| - (pad-string (show-ascii row) row-len) - :|)))))))) + (for [i 0 full-rows 1] + (let [row (byte-string:sub + (+ 1 (* row-len i)) + (+ row-len (* row-len i)))] + (when (< 0 (length row)) + (print (.. + :| (pad-string (show-bytes row) (- (* 3 row-len) 1)) + :| + " " + :| + (pad-string (show-ascii row) row-len) + :|))))))) {: print-string} diff --git a/host/assembler/util.fnl b/host/assembler/util.fnl new file mode 100644 index 0000000..b078720 --- /dev/null +++ b/host/assembler/util.fnl @@ -0,0 +1,17 @@ +(local byte string.char) +(fn word->byte [x] + "convert a 32 bit word into a little endian byte string" + (string.char + (-> x (band 0xff)) + (-> x (rshift 8) (band 0xff)) + (-> x (rshift 16) (band 0xff)) + (-> x (rshift 24)))) +(fn half->byte [x] + "convert a 16 bit half-word into a little endian byte string" + (string.char + (-> x (band 0xff)) + (-> x (rshift 8) (band 0xff)))) + +{: byte + : word->byte + : half->byte} diff --git a/host/util.fnl b/host/util.fnl new file mode 100644 index 0000000..1d26ce8 --- /dev/null +++ b/host/util.fnl @@ -0,0 +1,34 @@ +(local byte string.char) +(fn word->byte [x] + "convert a 32 bit word into a little endian byte string" + (string.char + (-> x (band 0xff)) + (-> x (rshift 8) (band 0xff)) + (-> x (rshift 16) (band 0xff)) + (-> x (rshift 24)))) +(fn half->byte [x] + "convert a 16 bit half-word into a little endian byte string" + (string.char + (-> x (band 0xff)) + (-> x (rshift 8) (band 0xff)))) + +(local word-max 0xffffffff) + +(fn rotate-right [x n] + "Rotate *x* right as a 32 bit integer by *n* bits." + (bor (rshift x n) (band word-max (lshift x (- 32 n))))) +(fn rotate-left [x n] + "Rotate *x* right as a 32 bit integer by *n* bits." + (bor (band word-max (lshift x n)) (rshift x (- 32 n)))) + +(fn hex [x ?padding] + "Convert x to hexadecimal, optionally padding to ?padding characters" + (string.format (.. :% :0 (or ?padding 0) :x) x)) + +{: byte + : word->byte + : half->byte + : rotate-left + : rotate-right + : hex + : word-max}