Login
7 branches 0 tags
Ben (Win10) Fixed some DOS issues 6f98529 3 years ago 478 Commits
nujel / stdlib / compiler / assembler.nuj
; Contains the bytecode assembler, so we don't have to write opcodes directly

[defun val->bytecode-op [v]
       [def i [val->index v]]
       [list [int->bytecode-op [logand [>> i 16] #xFF]]
             [int->bytecode-op [logand [>> i  8] #xFF]]
             [int->bytecode-op [logand     i     #xFF]]]]

[defun sym->bytecode-op [v]
       [def i [sym->index v]]
       [list [int->bytecode-op [logand [>> i 16] #xFF]]
             [int->bytecode-op [logand [>> i  8] #xFF]]
             [int->bytecode-op [logand     i     #xFF]]]]

[defun int-fit-in-byte? [a]
       [and [<= a 127] [>= a -128]]]

[defun $nop []
       "- | Do nothing"
       '[#$0]]

[defun $ret []
       "a - | Return top of value stack"
       '[#$1]]

[defun $push/int/byte [a]
       "- a | Return top of value stack"
       [when-not [int-fit-in-byte? a]
                 [throw [list :invalid-bc-op "$push/int/byte can only push a signed 8-bit value" a [current-lambda]]]]
       [list #$2 [int->bytecode-op a]]]

[defun $push/int [a]
       "- a | Return top of value stack"
       [if [int-fit-in-byte? a]
           [$push/int/byte a]
           [$push/lval a]]]

[defun $add/int []
       "a b - c | Adds the two topmost values and pushes the result"
       '[#$3]]

[defun $debug/print-stack []
       "- | Print out the stack for the current closure"
       '[#$4]]

[defun $push/lval [v]
       "- v | Pushes v onto the stack"
       [list #$5 [val->bytecode-op v]]]

[defun $make-list [item-count]
       "items ... - list | Makes a list of item-count items from the stack and pushes the resulting list"
       [list #$6 [int->bytecode-op item-count]]]

[defun $eval [a]
       "form - | Evaluates the form from the top of the stack"
       '[#$7]]

[defun $apply [arg-count fun]
       "arguments ... - result | Read arg-count arguments from the stack, apply the to fun and push the result on the stack"
       [case arg-count 2
             [2 [case fun
                      [add/int [$add/int]]
                      [< [$<]]
                      [<= [$<=]]
                      [== [$==]]
                      [>= [$>=]]
                      [> [$>]]
                      [otherwise [list #$8
                                       [int->bytecode-op arg-count]
                                       [val->bytecode-op fun]]]]]
             [otherwise [list #$8
                              [int->bytecode-op arg-count]
                              [val->bytecode-op fun]]]]]

[defun $apply/dynamic [arg-count fun]
       [list #$1A [int->bytecode-op arg-count]]]

[defun $call [target]
       " - | Call a bytecode subroutine"
       [list #$17 [list :relocate target 8 0 [int->bytecode-op 0]]
                  [list :relocate target 0 1 [int->bytecode-op 0]]]]

[defun $try [target]
       " - | Try something, jumping to target if an exception occurs"
       [list #$18 [list :relocate target 8 0 [int->bytecode-op 0]]
                  [list :relocate target 0 1 [int->bytecode-op 0]]]]

[defun $throw []
       " - | Return to the closest exception handler"
       [list #$19]]

[defun $jmp [target]
       [list #$9 [list :relocate target 8 0 [int->bytecode-op 0]]
                 [list :relocate target 0 1 [int->bytecode-op 0]]]]

[defun $jt [target]
       [list #$A [list :relocate target 8 0 [int->bytecode-op 0]]
                 [list :relocate target 0 1 [int->bytecode-op 0]]]]

[defun $jf [target]
       [list #$B [list :relocate target 8 0 [int->bytecode-op 0]]
                 [list :relocate target 0 1 [int->bytecode-op 0]]]]

[defun $dup []
       '[#$C]]

[defun $drop []
       '[#$D]]

[defun $def [v]
       [list #$E [sym->bytecode-op v]]]

[defun $set [v]
       [list #$F [sym->bytecode-op v]]]

[defun $get [v]
       [list #$10 [sym->bytecode-op v]]]

[defun $lambda [name args docs body]
       [list #$11
             [val->bytecode-op name]
             [val->bytecode-op args]
             [val->bytecode-op docs]
             [val->bytecode-op body]]]

[defun $macro [name args docs body]
       [list #$12
             [val->bytecode-op name]
             [val->bytecode-op args]
             [val->bytecode-op docs]
             [val->bytecode-op body]]]

[defun $closure/push []
       '[#$13]]

[defun $closure/enter []
       '[#$14]]

[defun $let []
       '[#$15]]

[defun $closure/pop []
       '[#$16]]

[defun $roots/push []
       '[#$1B]]

[defun $roots/pop []
       '[#$1C]]

[defun $roots/peek []
       '[#$1D]]

[defun $< []
       '[#$1E]]

[defun $<= []
       '[#$1F]]

[defun $== []
       '[#$20]]

[defun $>= []
       '[#$21]]

[defun $> []
       '[#$22]]

[defun assemble/build-sym-map [code sym-map pos]
       [while code
              [case [type-of [car code]]
                    [:bytecode-op [tree/set! sym-map :last-op [++ pos]]]
                    [[:symbol :keyword] [and [== [car code] :label]
                                             [tree/set! sym-map [cadr code] pos]]]
                    [:pair        [set! pos [assemble/build-sym-map [car code] sym-map pos]]]]
              [cdr! code]]
       pos]

[defun assemble/relocate-op [code sym-map pos out]
       [def target [tree/get sym-map [cadr code]]]
       [def off [- [+ target [cadddr code]] pos]]
       [array/set! out [++ pos] [int->bytecode-op [logand [>> off [caddr code]] #xFF]]]
       pos]

[defun assemble/emit-relocated-ops [code sym-map pos out]
       [if [== [car code] :relocate]
           [set! pos [assemble/relocate-op code sym-map pos out]]
           [for-in [op code]
                   [case [type-of op]
                         [:bytecode-op [array/set! out [++ pos] op]]
                         [:pair        [set! pos [assemble/emit-relocated-ops op sym-map pos out]]]]]]
       pos]

[def assemble/verbose #f]
[defun assemble* [code]
       "Assemble all arguments into a single :bytecode-array"
       [def sym-map @[]]

       [and assemble/verbose [println [cat [ansi-blue "Input:\n" [str/write code]]]]]
       [assemble/build-sym-map code sym-map 0]
       [and assemble/verbose [println [cat [ansi-yellow "Symbol Map:\n" [str/write sym-map]]]]]
       [def out [array/allocate [tree/get sym-map :last-op]]]
       [assemble/emit-relocated-ops code sym-map -1 out]
       [and assemble/verbose [println [cat [ansi-green "Output:\n" [str/write out]]]]]

       [arr->bytecode-arr out]]

[defun assemble l
       "Assemble all arguments into a single :bytecode-array"
       [assemble* l]]

[defmacro asmrun ops
          "Assemble and evaluate all bytecode arguments"
          `[bytecode-eval [assemble ~@ops]]]