diff --git a/host/assembler.fnl b/host/assembler.fnl index ae638d8..6cc2dde 100644 --- a/host/assembler.fnl +++ b/host/assembler.fnl @@ -1 +1,44 @@ -(local opcodes (require :host.assembler.opcodes)) +(local + {: r + : move + : branch + : branch-link} + (require :host.assembler.opcodes)) +(local {: any : slice : push} (require :deps.lume)) + +(fn label [name] {:label name}) +(fn label? [value] + (if (and (= (type value) :table) (. value :label)) true false)) + +(fn insert-label [table label index] + (if (. table :label) + (:error (.. "Duplicate label " label)) + (tset table :labels label index))) + +(fn assemble [forms offset] + (let [state {:labels {} :instructions [] :reverse-labels []}] + (each [_ form (ipairs forms)] + (if (= (type form) :string) + (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) + ((. form 1) (table.unpack (slice form 2))))))) + state)) + +(fn link [{: labels : instructions : reverse-labels}] + ()) + +(macro x [...] + `[,(unpack (icollect [_ form (ipairs ...)] '[,(. form 1)]))]) + +(macrodebug (x (move (r 9) (r 8)))) + +(assemble + [[move (r 8) (r 2)] + :label-1 + [move (r 9) (r 3)] + [branch (label :label-1)]] + 0x8000) diff --git a/host/assembler/opcodes.fnl b/host/assembler/opcodes.fnl index d187c2c..c33912e 100644 --- a/host/assembler/opcodes.fnl +++ b/host/assembler/opcodes.fnl @@ -1,95 +1,95 @@ (local util (require :host.util)) -(macro construct-code [& forms] - `(bor - ,(unpack - (icollect [i n (ipairs forms)] `(lshift ,(. n 1) ,(. n 2)))))) +(macro construct-opcode [& forms] + `(bor ,(unpack + (icollect [i n (ipairs forms)] + `(lshift ,(. n 1) ,(. n 2)))))) (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}) + :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)}) + "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))) + (and (= (type x) :table) (. x :register))) (fn bit [flag] - "Convert a boolean to an integer for shifting" - (if flag 1 0)) + "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)))) + "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})) + (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)))))) + {: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)))) + (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)))) + (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 diff --git a/test/assembler/opcodes.fnl b/test/assembler/opcodes.fnl new file mode 100644 index 0000000..841f1ea --- /dev/null +++ b/test/assembler/opcodes.fnl @@ -0,0 +1,2 @@ +(local t (require :deps.faith)) +(local opcodes (require :host.assembler.opcodes))