PDP-11による機械語入門 問3

PDP-11による機械語入門 - Qiita

問3-1

(defun dump-mov-n-rn (mem n)
  (format t "~4,'0x ~4,'0x mov $~x, r~x~%"
	(read16 mem *pc*) (read16 mem (+ *pc* 2)) (read16 mem (+ *pc* 2)) n)
  (incf *pc* 4))

(defun dump-mov-n-*rn (mem n)
   (format t "~4,'0x ~4,'0x mov $~x, (r~x)~%"
	   (read16 mem *pc*) (read16 mem (+ *pc* 2)) (read16 mem (+ *pc* 2)) n)
   (incf *pc* 4))

(defun dump-sys-exit (mem)
  (format t "~4,'0x sys 1 ; exit~%" (read16 mem *pc*))
  (incf *pc* 4))

(defun dump-sys-write (mem)
  (format t "~4,'0x sys 4 ; write~%" (read16 mem *pc*))
  (incf *pc* 2)
  (format t "~4,'0x: ~4,'0x ; arg~%" *pc* (read16 mem *pc*))
  (incf *pc* 2)
  (format t "~4,'0x: ~4,'0x ; arg~%" *pc* (read16 mem *pc*))
  (incf *pc* 2))

(defun reasem (mem)
  (let ((*pc* 0))
    (while (< *pc* (length mem))
	    (format t "~4,'0x: " *pc*)
	    (case (read16 mem *pc*)
	      (#x15c0 (dump-mov-n-rn mem 0))
	      (#x15c1 (dump-mov-n-rn mem 1))
	      (#x15c9 (dump-mov-n-*rn mem 1))

	      (#x8901 (dump-sys-exit mem))

	      (#x8904 (dump-sys-write mem))

	      (t (format t "~4,'0x ???~%" (read16 mem *pc*))
	         (incf *pc* 2))
	      )
	    )))




(defun run-d (path)
  (let* ((mem (load-memory path))
	 (tsize (read16 mem 2))
	 (dsize (read16 mem 4)))
    (reasem (subseq mem 16 (+ 16 tsize) ))))

問3-2

(defun write16 (mem r n)
  (setf (elt mem r) (logand n #b0000000011111111))
  (setf (elt mem (+ r 1)) (ash n -8)))

(defun mov-n-r0 (mem)
  (setf *r0* (read16 mem (+ *pc* 2)))
  (incf *pc* 4))

(defun mov-n-r1 (mem)
  (setf *r1* (read16 mem (+ *pc* 2)))
  (incf *pc* 4))

(defun mov-n-*r1 (mem)
  (write16 mem *r1* (read16 mem (+ *pc* 2)))
  (incf *pc* 4))

(defun sys-exit (mem)
  (setf *pc* -1))

(defun sys-write (mem)
  (let* ((s (read16 mem (+ *pc* 2)))
	 (len (read16 mem (+ *pc* 4)))
	 (str (map 'string #'character (subseq mem s (+ s len)))))
    (case *r0*
      (1 (format t str))
      )
    (incf *pc* 6)
    ))
(defun run1 (mem tsize)
  (let ((*pc* 0)
	(*r0* 0))
    (while (and (< *pc* tsize) (> *pc* -1))
	   (case (read16 mem *pc*)
	     (#x15c0 (mov-n-r0 mem))
	     (#x15c1 (mov-n-r1 mem))
	     (#x15c9 (mov-n-*r1 mem))
	     (#x8901 (sys-exit mem))
	     (#x8904 (sys-write mem))
	     (t (format t "~4,'0x: ~4,'0x ???~%" *pc* (read16 mem *pc*))
		(setf *pc* -1))
	     )
	   )))

(defun run (path)
  (let* ((mem (load-memory path))
	 (tsize (read16 mem 2))
	 (dsize (read16 mem 4)))
    (run1 (subseq mem 16) tsize)))

pdp11/pdp-11.lisp at master · mas454/pdp11 · GitHub

PDP-11による機械語入門 問2-2

PDP-11による機械語入門 - Qiita

問2-2

(defun mov-n-r0 (mem)
  (setf *r0* (read16 mem (+ *pc* 2)))
  (incf *pc* 4))

(defun sys-exit (mem)
  (setf *pc* -1))

(defun sys-write (mem)
  (let* ((s (read16 mem (+ *pc* 2)))
	 (len (read16 mem (+ *pc* 4)))
	 (str (map 'string #'character (subseq mem s (+ s len)))))
    (case *r0*
      (1 (format t str))
      )
    (incf *pc* 6)
    ))
(defun run1 (mem tsize)
  (let ((*pc* 0)
	(*r0* 0))
    (while (and (< *pc* tsize) (> *pc* -1))
	   (case (read16 mem *pc*)
	     (#x15c0 (mov-n-r0 mem))
	     (#x8901 (sys-exit mem))
	     (#x8904 (sys-write mem))
	     (t (format t "~4,'0x: ~4,'0x ???~%" *pc* (read16 mem *pc*))
		(setf *pc* -1))
	     )
	   )))

(defun run (path)
  (let* ((mem (load-memory path))
	 (tsize (read16 mem 2))
	 (dsize (read16 mem 4)))
    (run1 (subseq mem 16) tsize)))

pdp11/pdp-11.lisp at master · mas454/pdp11 · GitHub

PDP-11による機械語入門 問2-1

PDP-11による機械語入門 - Qiita

問2-1

(defun load-memory (path)
  (with-open-file (s path :element-type '(unsigned-byte 8))
    (let ((mem (make-array (file-length s) :element-type '(unsigned-byte 8))))
      (read-sequence mem s)
      mem)
    ))

(defun read16 (mem i)
  (logior (elt mem i) (ash (elt mem (+ i 1)) 8)))

(defun reasem (mem)
  (let ((i 0))
    (while (< i (length mem))
      (format t "~4,'0x: " i)
      (case (read16 mem i)
	(#x15c0 (format t "~4,'0x ~4,'0x mov $~x, r0~%"
			(read16 mem i) (read16 mem (+ i 2)) (read16 mem (+ i 2)))
	        (incf i 4))

	(#x8901 (format t "~4,'0x sys 1 ; exit~%" (read16 mem i))
	        (incf i 2))
	
        (#x8904 (format t "~4,'0x sys 4 ; write~%" (read16 mem i))
	        (incf i 2)
		(format t "~4,'0x: ~4,'0x ; arg~%" i (read16 mem i))
		(incf i 2)
		(format t "~4,'0x: ~4,'0x ; arg~%" i (read16 mem i))
		(incf i 2))

	(t (format t "~4,'0x ???~%" (read16 mem i))
	   (incf i 2))
	)
      )))

(defun run-d (path)
  (let* ((mem (load-memory path))
	 (tsize (read16 mem 2))
	 (dsize (read16 mem 4)))
    (reasem (subseq mem 16 (+ 16 tsize) ))))

GitHub - mas454/pdp11
今回はCommon Lispを使っています。

courseraのMachine Learningを修了

 courseraのMachine Learningを受講してたんですが、先日やっと修了できました。

 courseraのMachine Learningに関してはこのページが参考になります。
数学を避けてきた社会人プログラマが機械学習の勉強を始める際の最短経路 - Qiita
 動画には日本語字幕が付いていますが、試験やプログラミングの課題の部分は日本語訳がありません。
ただ、Google翻訳の精度がかなり上がっているので、今から始めるなら英語がわからなくてもGoogle翻訳使えば
なんとかなるかもしれません。

試験とプログラムの課題の合格のコツ
 試験は動画を一つ見る度に、resourcesにあるLecture Notesの動画に対応した部分を読んでノートに
まとめるなりすれば合格できます。

 プログラムの課題は行列がどう計算されるのかを考えれば解ける問題が多いです。

sumitでエラーが出る場合
 プログラムの課題を提出する時にsubmitでエラーが出る場合このページを参考にしてみてください。
Coursera Machine Learning の 課題提出, submit エラーについて - Qiita


 最後までやるには時間がかかりますが、機械学習の入門にはすごくいいと思います。

自作エミュレータで学ぶx86アーキテクチャ 3.10 条件分岐命令

https://github.com/mas454/x86emu

jc、jz、joなどの命令はフラグレジスタの値を元にジャンプするかどうかを
判断しています。

(define (make-jx is-flag)
  (lambda (emu)
    (if (is-flag emu)
      (eip-add emu (+ (get-sign-code8 emu 1) 2))
      (eip-add emu 2))))

make-jxは、jc、jzなどの命令に対応した関数を返します。

 例えばjzに対応した関数がほしい場合以下のようにします。

(define (is-zero? emu)
  (logbit? 6 (ref emu 'eflags)))

(make-jx is-zero)

 is-zero?はフラグレジスタのZFが1かどうかを返す関数で、
これをmake-jxに渡すとjzに対応した関数を返します。

自作エミュレータで学ぶx86アーキテクチャ 3.10 cmp

https://github.com/mas454/x86emu

 (define (cmp-r32-rm32 emu)
  (let ([modrm (make <modrm>)])
    (parse-modrm emu modrm)
    (let* ([r32 (get-r32 emu modrm)]
	   [rm32  (get-rm32 emu modrm)]
	   [result (+ r32 (num32->2complement rm32))])
      (update-eflags-sub emu r32 rm32 result))))

(define (cmp-rm32-imm8 emu modrm)
  (let ([rm32 (get-rm32 emu modrm)]
	[imm8  (get-code8 emu 0)])
    (eip-add emu 1)
    (update-eflags-sub emu rm32 imm8 (+ rm32 (num32->2complement imm8)))))

 cmp-r32-rm32とcmp-rm32-imm8は、cmp命令に対応した関数です。
cmpは比較のための命令で、内部では引き算を利用して比較を行っています。

元のコードでは、

uint64_t result = (uint64_t)r32 - (uint64_t)rm32;

のように減算の結果を符号なしの変数に入れているのですが、schemeだと無理なので
以下のように2の補数に変換してから加算で計算しています。

 [result (+ r32 (num32->2complement rm32))])

num32-2complementが2の補数に変換する関数です。

(define (num32->2complement num)
  (+ (logxor num (- (expt 2 32) 1) ) 1))

単純に(+ (lognot num) 1)で計算するとマイナスになってしまうので、
上のプログラムのように計算しています。

update-eflags-subはフラグレジスタに値をセットする関数で、
フラグレジスタの値で条件分岐を行います。

(define (update-eflags-sub emu v1 v2 result)
  (let* ([sign1 (ash v1 -31)]
	 [sign2 (ash v2 -31)]
	 [signr (logand (ash result -31) 1)])
    (set-carry emu  (zero? (ash result -32)))
    (set-zero emu (zero? (copy-bit 32 result #f)))
    (set-sign emu (not (zero? signr)))
    (set-overflow emu (and (not (= sign1 sign2)) (not (= sign1 signr))))))

2の補数を使った計算を行っているので、キャリーフラグを1にする条件が元のコードとは
逆になっています。