コンピュータシステムの理論と実装:シンボルフリー版のアセンブラ完成
シンボルフリー版のアセンブラ完成しました。
簡単にプログラムの説明を書いておきます。
A=1 M=D
と書かれたファイルがあるとする。
このファイルをread-asmで読み込むと
("A=1" "M=1")
のようなリストが返されます。
その次に1個ずつ要素を、string->list関数で文字のリストにして
assem1関数に渡す。
例えば、
(assem1 '(#\A #\= #\1))
を実行すると、まずremove-comentとremove-spaceに渡されて、コメントとスペースが
除去される(この例ではコメントもスペースもないですが。)
その次にA命令かC命令かを判断して、parse-@関数かparse関数に渡される。
今回の例ではC命令なのでparse関数に渡されます。
;parse関数の実行結果 ((#\A) (#\1) (#\n #\u #\l #\l))
parse関数でdest部、comp部、jump部に分けられます(今回の例ではjump部がないのでnullになっています。)
次にこの結果がcode関数に渡されます。
;code関数の実行結果 ("0111111" "100" "000")
parse関数で分けた部分ごとに、code-dest関数、code-comp関数、code-jump関数に
それぞれ渡され対応したバイナリコードが返されます。
Gaucheで動作確認をしています。
;inにアセンブルするプログラムのファイル名 ;outにアセンブルした結果を出力するファイル名 ;例:(assembler "./test.asm" "./test.hack") (define (assembler in out) (out-hack out (assem (read-asm in)))) ;プログラムをリストに読み込む (define (read-asm file) (call-with-input-file file (lambda (i) (let loop ((c (read-line i)) (lis '())) (if (eof-object? c) (reverse lis) (loop (read-line i) (cons c lis))))))) ;ファイルに出力する (define (out-hack file lis) (call-with-output-file file (lambda (i) (for-each (lambda (s) (if (not (string=? s "")) (begin (display s i) (newline i)))) lis)))) ;;アセンブルする (define (assem lis) (map (lambda (s) (assem1 (string->list s))) lis)) ;;1行をアセンブルする (define (assem1 str) (let ((str2 (remove-space (remove-coment str)))) (if (null? str2) "" (if (char=? (car str2) #\@) (apply string-append (list "0" (code-@ (parse-@ str2)))) (apply string-append (cons "111" (code (parse str2)))))))) ;スペースを除去する (define (remove-space lis) (remove (lambda (s) (char=? s #\space)) lis)) ;コメントを除去する (define (remove-coment1 lis result) (cond ((null? lis) (reverse result)) ((char=? (car lis) #\/) (reverse result)) (else (remove-coment1 (cdr lis) (cons (car lis) result))))) (define (remove-coment lis) (remove-coment1 lis '())) ;comp,dist,jumpに分ける (define (parse str) (let* ((dest (parse-dest str)) (comp (parse-comp (cadr dest))) (jump (parse-jump (cadr comp)))) (list (car dest) (car comp) jump))) ;dest,comp,jumpのそれぞれに対応したバイナリコードを返す (define (code nimonics);parse関数の結果を渡す (let ((dest (code-dest (list-ref nimonics 0))) (comp (code-comp (list-ref nimonics 1))) (jump (code-jump (list-ref nimonics 2)))) (list comp dest jump))) (define (parse-@ str) (cdr str)) ;文字列を数値に変えてからnumber->stringで2進数の文字列を返す (define (code-@1 nimonics) (number->string (string->number (list->string nimonics)) 2)) (define (code-@ nimonics) (let ((s (code-@1 nimonics))) (string-append (list->string (make-list (- 15 (string-length s)) #\0));桁が足りない場合0を付加する s) ) ) (define (parse-dest1 str lis) (if (char=? #\= (car str)) (list (reverse lis) (cdr str)) (parse-dest1 (cdr str) (cons (car str) lis)))) (define (parse-dest str) (if (member #\= str) (parse-dest1 str '()) (list (string->list "null") str))) (define (parse-comp1 str lis) (if (or (null? str) (char=? #\; (car str))) (list (reverse lis) str) (parse-comp1 (cdr str) (cons (car str) lis)))) (define (parse-comp str) (parse-comp1 str '())) (define (parse-jump1 str lis) (if (null? str) (reverse lis) (parse-jump1 (cdr str) (cons (car str) lis)))) (define (parse-jump str) (if (or (null? str) (null? (cdr str))) (string->list "null") (parse-jump1 (cdr str) '()))) (define (code-comp nimonic) (let1 nimostr (list->string nimonic) (cond ((string=? nimostr "D+1") "0011111") ((string=? nimostr "0") "0101010") ((string=? nimostr "1") "0111111") ((string=? nimostr "-1") "0111010") ((string=? nimostr "D") "0001100") ((string=? nimostr "A") "0110000") ((string=? nimostr "M") "1110000") ((string=? nimostr "!D") "0001101") ((string=? nimostr "!A") "0110001") ((string=? nimostr "!M") "1110001") ((string=? nimostr "-D") "0001111") ((string=? nimostr "-A") "0110011") ((string=? nimostr "-M") "1110011") ((string=? nimostr "A+1") "0110111") ((string=? nimostr "M+1") "1110111") ((string=? nimostr "D-1") "0001110") ((string=? nimostr "A-1") "0110010") ((string=? nimostr "M-1") "1110010") ((string=? nimostr "D+A") "0000010") ((string=? nimostr "D+M") "1000010") ((string=? nimostr "D-A") "0010011") ((string=? nimostr "D-M") "1010011") ((string=? nimostr "A-D") "0000111") ((string=? nimostr "M-D") "1000111") ((string=? nimostr "D&A") "0000000") ((string=? nimostr "D&M") "1000000") ((string=? nimostr "D|A") "0010101") ((string=? nimostr "D|M") "1010101") (else (error nimostr)) ))) (define (code-dest nimonic) (let1 nimostr (list->string nimonic) (cond ((string=? nimostr "null") "000") ((string=? nimostr "M") "001") ((string=? nimostr "D") "010") ((string=? nimostr "MD") "011") ((string=? nimostr "A") "100") ((string=? nimostr "AM") "101") ((string=? nimostr "AD") "110") ((string=? nimostr "AMD") "111") (else (error nimostr))))) (define (code-jump nimonic) (let1 nimostr (list->string nimonic) (cond ((string=? nimostr "null") "000") ((string=? nimostr "JGT") "001") ((string=? nimostr "JEQ") "010") ((string=? nimostr "JGE") "011") ((string=? nimostr "JLT") "100") ((string=? nimostr "JNE") "101") ((string=? nimostr "JLE") "110") ((string=? nimostr "JMP") "111") (else (error nimostr)))))