#lang racket/base ;; Transformer routines for the asm/dasm syntax. (provide asm/dasm-lambda-tx) (require "../op/static.rkt" ;; make-op-static (for-template "../op.rkt" ;; make-asm "operand.rkt" ;; asm+ racket/base)) ;; Parameter classes. ;; Assembly operands are single-character variables. This is to ;; facilitate instruction bit vector layout specification. In ;; addition, certain letters have special properties. ;; Determine field assembler in terms of parameter class. (define (paramclass->asm name) (case name ((R) #'asm+/pcr) ;; used for relative jumps (else #'asm+))) ;; assemble value ignoring overflow (define (paramclass->dasm name) (case name ((R) #'dasm/pcr) (else #'dasm/unsigned))) ;; SEXP FORM ;; (assembler-body '((118 7) (s 1) (k 8))) (define (assembler-body opcode-body) (syntax-case opcode-body () (((opcode . opcode-bits) . operands) (foldl (lambda (p/b inner) (syntax-case p/b () ((param bits) #`(#,(paramclass->asm (syntax-e #'param)) pc param bits #,inner)))) #'opcode (syntax->list #'operands))))) (define (asm-lambda-tx name formals template-list) #`(let ((#,name (lambda (address #,@formals) (let ((pc (+ address #,(length template-list)))) ;; address->pc translation (list #,@(map assembler-body template-list)))))) #,name)) ;; STRING FORM ;; (bitstring->list "0101 kkkk ffff ffff") (define (lex-bits str-stx) (define (char->stx char) (datum->syntax str-stx ;; preserve context ((lambda (string) (or (string->number string) (string->symbol string))) (string char)))) (define (valid-char? char) (not (equal? char #\space))) (map char->stx (filter valid-char? (string->list (syntax->datum str-stx))))) ;; Parse bit string (define (parse-bits stx) (syntax-case stx () ((bit ...) (let down ((stx (for/list ((el (syntax->list #'(bit ...)))) (list el 1)))) (syntax-case stx () (((k n) (l m) . rest) (let ((_n (syntax-e #'n)) (_m (syntax-e #'m)) (_k (syntax-e #'k)) (_l (syntax-e #'l))) (cond ;; Collect symbol's bits ((and (symbol? _k) (eq? _k _l)) (down #`((k #,(+ _n _m)) . rest))) ;; Collect number's bits ((and (number? _k) (number? _l)) (down #`((#,(+ (* 2 _k) _l) #,(+ _n _m)) . rest))) ;; Done, move to next. (else #`((k n) #,@(down #`((l m) . rest))))))) (other #'other)))))) ;; DISASSEMBLER ;; A disassembler is a "parser": it consumes a stream of numbers in ;; the form of a lazy list (delay (cons head tail)) | (delay '()) and ;; produces a (syntax/#f ll) value pair. ;; Compiling a disassembling works in two steps: ;; * compile an opcode matcher ;; * run the generic disassembler ;; Generates binding form for disassembler from instruction field ;; description. ;; ((disassemble-bind #'(((aaa 1) (bbb 2)) ((ccc 3) (ddd 4)))) #'123) ;; => ;; (lambda (temp20 temp21) ;; (let-values (((aaa bbb) (disassemble/values '(1 2) temp20)) ;; ((ccc ddd) (disassemble/values '(3 4) temp21))) ;; 123)) (define-syntax-rule (push! stack x) (set! stack (cons x stack))) (define-syntax-rule (lambda* formals . body) (lambda (a) (apply (lambda formals . body) a))) (define (generate-temp) (car (generate-temporaries #'(#f)))) (define (dasm-lambda-tx opcode-name formals body-stx) (define literals '()) (define variables (make-hash)) (define (fix-names! names bits) (for/list ((n (syntax->list names)) (b (syntax->list bits)) (i (in-naturals))) (let ((_n (syntax-e n))) (if (number? _n) (let ((__n (generate-temp))) (push! literals (list __n _n)) __n) (begin (hash-set! variables _n b) n))))) (let ((ws (generate-temporaries body-stx))) (syntax-case (list ws body-stx) () (((w ...) (((name bits) ...) ...)) #`(let ((#,opcode-name (lambda (assembler) ;; to bind to assembler instance later (lambda (address w ...) ;; address is passed explicitly: no asm pointers context dependency here. ;; (printf "dasm: ~a ~a ~a\n" '#,opcode-name '#,formals (list pc w ...)) (let ((pc (+ address #,(length ws)))) ;; address -> pc translation (let-values ;; Transpose it #,(for/list ((stx (syntax->list #'((w (name ...) (bits ...)) ...)))) (syntax-case stx () ((w ns bs) #`(#,(fix-names! #'ns #'bs) (disassemble/values 'bs w))))) (and #,@(map (lambda* (name value) #`(= #,name #,value)) literals) (list assembler ;; address -- Won't give address since all words ;; are converted to absolute addressing already. #,@(for/list ((f (syntax->list formals))) (let ((_f (syntax-e f))) #`(#,(paramclass->dasm _f) pc #,f #,(hash-ref variables _f)))))))))))) #,opcode-name))))) ;; Main entry point for asm/dasm spec -> function bodies + static info. (define (asm/dasm-lambda-tx stx) (syntax-case stx () ((_ name formals . body) ;; (printf "asm: ~a\n" (syntax-e #'name)) (let ((body-stx (syntax-case #'body () ((((param . bits) ...) ...) ;; sexp syntax (syntax->list #'body)) ((str ...) ;; string syntax (map (compose parse-bits lex-bits) (syntax->list #'(str ...))))))) (values (asm-lambda-tx #'name #'formals body-stx) (dasm-lambda-tx #'name #'formals body-stx)))))) ;; (define (proto->disassembler stx) ;; #`(lambda (ll) ;; (let (( ;; (syntax-case stx () ;; ((name formals) ;; #`(lambda (opcode) '(name))) ;; ((name formals ;; #`(lambda (opcode) ;; (match ;; (cadr ;; (chain ;; construct a chain of argument shifts ;; `(,opcode ()) ;; #,@(map ;; (match-lambda ;; ((param . bits) ;; #`(dasm-step '#,param #,bits))) ;; one shift tick ;; (reverse (cdr (car binary-words)))))) ;; (#,(map car ;; (cdr (car binary-words))) ;; (list '#,name #,@formals))))))