最後にCPUID命令を実行して情報を取得する関数を作成します。
CPUIDはEAXレジスタに取得したい情報の種類を指定して呼び出すと、EAX、EBX、ECX、EDXに情報を格納してくれる命令です。
CPUID(Wikipedia)
引数に取得する情報の種類を取り、4つのレジスタの値を返す関数を作成してみます。
なお、自由に使えるレジスタが少ないっぽいので一旦スタックに積んだりしてます。
(defx86lapfunction cpuid ((operation arg_z))
;; 引数の数をチェック
(check-nargs 1)
;; スタックフレーム作成
(pushq (% rbp))
(movq (% rsp) (% rbp))
;; CPUIDで上書きされてしまうRBXを保存
(pushq (% rbx))
;; CPUIDで取得する情報の種類をRAX(EAX)に保存
(unbox-fixnum operation rax)
;; CPUID命令実行
(cpuid)
;; 呼び出し結果をlispのfixnumに変換
(box-fixnum rbx rbx)
;; 多値で返すためにスタックに積む(2つ目)
(pushq (% rbx))
;; 退避していたRBXの値を復元
(movq (@ 8 (% rsp)) (% rbx))
(box-fixnum rax rax)
;; 多値で返すためにスタックに積む(1つ目)
(movq (% rax) (@ 8 (% rsp)))
(box-fixnum rcx rcx)
;; 多値で返すためにスタックに積む(3つ目)
(pushq (% rcx))
(box-fixnum rdx rdx)
;; 多値で返すためにスタックに積む(4つ目)
(pushq (% rdx))
;; 戻り値の数を設定
(set-nargs 4)
;; 関数を終了して多値を返す
(jmp-subprim .SPnvalret))
(defun u32->str (n)
(map 'string
(lambda (byte) (code-char (ldb byte n)))
(list (byte 8 0)
(byte 8 8)
(byte 8 16)
(byte 8 24))))
;; 実行
(multiple-value-bind (_ ebx ecx edx) (cpuid 0)
(format nil "~A~A~A"
(u32->str ebx)
(u32->str edx)
(u32->str ecx)))
;; => "GenuineIntel"
※
Lisp Advent Calendar 2日目でタグの話題がありましたが、CCLはポインタにタグが付いてるタイプのLispです。
なので、Lisp側から整数の1を渡したつもりでも機械語としてはタグの分(ここでは3bit)ずれた値になってしまいます。
box/unboxはこのずれを補正するための処理で、実際には掛け算やシフトが行われています。