アセンブラ完成

コンピュータシステムの理論と実装8章のシンボルも処理できるアセンブラ
完成しました。
前のプログラムから追加した部分の説明を書いておきます。

プログラムを読み込んだら、最初にラベルの処理をします。

(define (assem-label1 lis n result)
  (cond ((null? lis) (reverse result))
	((char=? (caar lis) #\( )
	 (let ((label (list->string (parse-label (car lis)))))
	   (add-entry! label n)
	   (assem-label1 (cdr lis) n result)))
	(else
	 (assem-label1 (cdr lis) (+ n 1) (cons (car lis) result)))))

;ラベルを処理
(define (assem-label lis)
  (let ((lis2 (remove-come&spa-all (map string->list lis))))
    (assem-label1 lis2 0 '())))

例えば

(aaa)
D=1
(bbb)
A=1

のようなプログラムだと、シンボルテーブルに

((aaa . 0)
 (bbb . 1))

のように登録されます。

次に、A命令でシンボルの処理をする部分を追加しました。

;数字の文字列を数値に変えてからnumber->stringで2進数の文字列を返す
(define (code-@1 nimonics) 
  (number->string 
   (string->number 
    (list->string nimonics)) 2))

(define (code-@2 nimonics)
  (cond ((char-numeric? (car nimonics))
	 (code-@1 nimonics))
	(else                                ;シンボルを解決する
	 (let ((sym (list->string nimonics))) 
	   (if (contains sym)
	       (number->string (get-address sym) 2)
	       (number->string (make-var sym) 2)
	       )
	   ))))

特に、この部分でシンボルの処理をしています。

(if (contains sym)
    (number->string (get-address sym) 2)
    (number->string (make-var sym) 2)
 )

シンボルがテーブルに登録されている場合は、get-addressでシンボルに割り当てられている
アドレスにシンボルを置き換える。

登録されていない場合は、make-varでシンボルにアドレスを割り当てる。

(define (make-var sym) 
  (add-entry! sym var-n)
  (set! var-n (+ var-n 1))
  (- var-n 1))



以下が、全体のプログラムです。

(define symbol-table '())

(define (init-symbol-table)
  (add-entry! "SP" 0)
  (add-entry! "LCL" 1)
  (add-entry! "ARG" 2)
  (add-entry! "THIS" 3)
  (add-entry! "THAT" 4)
  (add-entry! "R0" 0)
  (add-entry! "R1" 1)
  (add-entry! "R2" 2)
  (add-entry! "R3" 3)
  (add-entry! "R4" 4)
  (add-entry! "R5" 5)
  (add-entry! "R6" 6)
  (add-entry! "R7" 7)
  (add-entry! "R8" 8)
  (add-entry! "R9" 9)
  (add-entry! "R10" 10)
  (add-entry! "R11" 11)
  (add-entry! "R12" 12)
  (add-entry! "R13" 13)
  (add-entry! "R14" 14)
  (add-entry! "R15" 15)
  (add-entry! "SCREEN" 16384)
  (add-entry! "KBD" 24576))


(define (add-entry! key val) 
  (set! symbol-table (assoc-set! symbol-table key val)))

(define (contains key) (assoc key symbol-table))

(define (get-address symbol)
  (cdr (assoc symbol symbol-table)))

(define var-n 16)

(define (init)
  (init-symbol-table)
  (set! var-n 16))

;変数にメモリを割り当てる
(define (make-var sym) 
  (add-entry! sym var-n)
  (set! var-n (+ var-n 1))
  (- var-n 1))

;inにアセンブルするプログラムのファイル名
;outにアセンブルした結果を出力するファイル名
;例:(assembler "./test.asm" "./test.hack")
(define (assembler in out)
  (init)
  (out-hack out 
	    (assem 
	     (assem-label (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-label1 lis n result)
  (cond ((null? lis) (reverse result))
	((char=? (caar lis) #\( )
	 (let ((label (list->string (parse-label (car lis)))))
	   (add-entry! label n)
	   (assem-label1 (cdr lis) n result)))
	(else
	 (assem-label1 (cdr lis) (+ n 1) (cons (car lis) result)))))

;ラベルを処理
(define (assem-label lis)
  (let ((lis2 (remove-come&spa-all (map string->list lis))))
    (assem-label1 lis2 0 '())))
 
;;アセンブルする
(define (assem lis)
  (map (lambda (s) (assem1  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) (or (char=? s #\space) (char=? s #\tab))) 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 '()))

;コメントとスペース除去する
(define (remove-come&spa lis)
  (remove-space (remove-coment lis)))

;プログラム全体からコメントとスペースと空行を除去する
(define (remove-come&spa-all lis)
  (remove null? (map remove-come&spa 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))

(define (parse-label str) 
  (remove (lambda (s) (char=? s #\) )) (cdr str)))

;数字の文字列を数値に変えてからnumber->stringで2進数の文字列を返す
(define (code-@1 nimonics) 
  (number->string 
   (string->number 
    (list->string nimonics)) 2))

(define (code-@2 nimonics)
  (cond ((char-numeric? (car nimonics))
	 (code-@1 nimonics))
	(else                                ;シンボルを解決する
	 (let ((sym (list->string nimonics))) 
	   (if (contains sym)
	       (number->string (get-address sym) 2)
	       (number->string (make-var sym) 2)
	       )
	   ))))

(define (code-@ nimonics)
  (let ((s (code-@2 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)))))