Archive for 9月, 2009
cl-openglで画像出た
日曜日, 9月 27th, 2009何も分からないままにcl-openglのサンプルを適当にいじったら画像が表示できた。
テクスチャとして貼付けてるので、簡単に回転できた。

それにしても、asdfやrequireというのがよくわからない。
cl-openglを使ったソースを探したら、みんな
(require :cl-glut)
とかやってるけど、これをするとcl-openglが見つからないと怒られる。
(asdf:oos ‘asdf:load-op :cl-glut)
これで代用できるみたいだけど面倒だ。
画像を読み込むために、cl-pngというのを入れたけど、これも同じ。それから、
(asdf:oos ‘asdf:load-op :png)
という風に、cl-pngではなくpngと指定しないといけない。ややこしい。
(defclass hello-window (glut:window)
((image :initarg :image :initform nil :accessor image)
(texture :accessor texture))
(:default-initargs :pos-x 100 :pos-y 100 :width 800 :height 600
:mode '(:single :rgb) :title "YUNOHA"))
(defmethod glut:display-window :before ((w hello-window))
(gl:clear-color 0 0 0 0)
(gl:matrix-mode :projection)
(gl:load-identity)
(gl:ortho -4 4 -3 3 -1 1)
(gl:pixel-store :unpack-alignment 1)
(setf (texture w) (car (gl:gen-textures 1)))
(gl:bind-texture :texture-2d (texture w))
(gl:tex-parameter :texture-2d :texture-min-filter :nearest)
(gl:tex-parameter :texture-2d :texture-mag-filter :nearest)
(gl:tex-image-2d :texture-2d 0 :rgb 800 600 0 :rgb :unsigned-byte (image w)))
(defmethod glut:display ((w hello-window))
(gl:clear :color-buffer-bit)
(gl:rotate 4 0 0 1)
(gl:raster-pos 0 0)
(gl:enable :texture-2d)
(gl:with-primitive :quads
(gl:tex-coord 0 0)
(gl:vertex -3.75 -2.75 0)
(gl:tex-coord 1 0)
(gl:vertex 3.75 -2.75 0)
(gl:tex-coord 1 1)
(gl:vertex 3.75 2.75 0)
(gl:tex-coord 0 1)
(gl:vertex -3.75 2.75 0))
(gl:disable :texture-2d)
(gl:flush))
(defmethod glut:idle ((widow hello-window))
(sleep (/ 1.0 30.0))
(glut:post-redisplay))
(defmethod glut:keyboard ((window hello-window) key x y)
(declare (ignore x y))
(case key
(#¥Esc (glut:destroy-current-window))))
(defun run ()
(glut:display-window (make-instance 'hello-window :image (png->raw "kawanishi_lico01.png"))))
(defun png->raw (input-pathname)
(let* ((old (with-open-file (input input-pathname
:element-type '(unsigned-byte 8))
(png:decode input)))
(new (make-array (* (png:image-width old) (png:image-height old) 3)
:element-type '(unsigned-byte 8))))
(dotimes (i (png:image-height old) new)
(dotimes (j (png:image-width old))
(dotimes (k 3)
(setf (aref new (+ (* i (png:image-width old) 3) (* j 3) k))
(aref old (- (png:image-height old) i 1) j k)))))))
cl-opengl試した
土曜日, 9月 26th, 2009いつかCLでゲームを作ろうと思い、その下準備としてcl-openglを入れました。
OSはMac OS X v10.5、処理系はSBCL 1.0.29。
以下はその時のメモ。
1)darcsをインストール。今回はMacPortsを利用。
2)cl-openglを取ってくる
% darcs get http://www.common-lisp.net/project/cl-opengl/darcs/cl-opengl/
3)cl-openglを適切な場所に置く
% mv ./cl-opengl /opt/local/lib/sbcl/site/ % cd /opt/local/lib/sbcl/site-system/ % ln -s ../cl-opengl/*.asd .
4)cffiをインストール
% sbcl * (require 'asdf-install) * (asdf-install:install :cffi)
5)cl-openglをコンパイル
* (asdf:oos 'asdf:load-op :cl-opengl)
6)テスト
* (asdf:oos 'asdf:load-op :cl-glut-examples) * (cl-glut-examples:gears)

ゲームは「いつか」作ります。
バイナリストリームを対話環境でテストする
土曜日, 9月 26th, 2009Java逆アセンブラを作っていたときの話。
バイナリファイルを読み込んで色々するプログラムを書くので、
「バイナリストリームからNバイト読み込んで…」
といった関数を沢山書いたんですが、これのテストがやりにくいです。
ファイルを実際に開く以外に、バイナリストリームを作る方法を知らないので、
ちょっとしたテストをする場合にも、実際にファイルを作らないといけない。
これでは非常に面倒です。
文字ストリームを扱う関数をテストする場合には、
with-input-from-stream, with-output-to-stream
を使えば、対話環境で楽に試せるので、
これのバイナリストリーム版を作ってみました。
Gray Streamを使っています。
SBCLでテスト済み。
(defclass binary-array-input-stream (fundamental-binary-input-stream)
((array :initarg :array :type (array t (*)))
(index :initarg :start :type fixnum)
(end :initarg :end :type fixnum)))
(defun make-binary-array-input-stream (array &optional (start 0) end)
(make-instance 'binary-array-input-stream
:array array :start start :end (or end (length array))))
(defmethod stream-read-byte ((stream binary-array-input-stream))
(with-slots (index end array) stream
(if (>= index end)
:eof
(prog1 (aref array index)
(incf index)))))
(defmacro with-input-from-binary-array((var array)
&body body)
`(let ((,var (make-binary-array-input-stream ,array)))
(multiple-value-prog1
(unwind-protect
(progn ,@body)
(close ,var)))))
配列の型は本当は (array (unsigned-byte 8) (*)) の方がいいんでしょうが、
そうすると、 #(1 2 3) の記法が使えなくなって面倒なので指定を緩くしました。
ここで、次のような関数を作ったとします。
(defun byte-list->number (byte-list &optional little-endian-p)
(reduce #'(lambda (high low) (+ (ash high 8) low))
(if little-endian-p
(reverse byte-list)
byte-list)))
(defun read-byte-list (n stream)
(let (byte-list)
(dotimes (_ n (nreverse byte-list))
(push (read-byte stream) byte-list))))
これらの関数を対話環境でテストしたければ、次のようにします。
(with-input-from-binary-array (s #(1 44))
(let ((byte-list (read-byte-list 2 s)))
(format t "big endian: ~A~%" (byte-list->number byte-list))
(format t "little endian: ~A~%" (byte-list->number byte-list t))))
big endianは300、little endianは11265となり、ちゃんと動きます。
Javaのアセンブラ/逆アセンブラをLispで作った
水曜日, 9月 23rd, 2009Javaのアセンブラと逆アセンブラをCommon Lispで作りました。
一部対応してない命令がありますが、大体動作します。
アセンブリはもちろんS式で記述します。読み込むときはreadするだけ。
オペランドのない命令はアトム、オペランド付きの命令はリストとなっています。
とりあえずhello world。
;; ljTest.lja
(class "ljTest" "java/lang/Object" (public super)
method
("<init>" "()V" (public)
aload_0
(invokespecial "java/lang/Object" "" "()V")
return)
method
("main" "([Ljava/lang/String;)V" (public static)
(meta max-stack 2)
(getstatic "java/lang/System" "out" "Ljava/io/PrintStream;")
(ldc "hello world")
(invokevirtual "java/io/PrintStream" "println" "(Ljava/lang/String;)V")
return))
hello worldだけなのに長いです。さすがJava。
<init>というのはコンストラクタです。
% clisp > (load "ljasm.lisp") > (assemble-file "ljTest.java") > (exit) % java ljTest hello world
こんな感じに動作します。
次は逆アセンブル。
// le.java
public class le{
public static void main(String args[]) {
try {
for (int i=5; i>=0; i--) {
System.out.println(100 / i);
}
} catch (ArithmeticException e) {
System.out.println("Nice exception...");
}
}
}
ループと例外が使われています。
% javac le.java % clisp > (load "ljasm.lisp") > (disassemble-file "le.class") > (exit)
これで次のようなファイルが作られます。
(整形は私が手作業でやりました)
(CLASS "le" "java/lang/Object" (PUBLIC SUPER)
INTERFACE NIL
METHOD
("" "()V" (PUBLIC)
(META MAX-STACK 1)
(META MAX-LOCAL 1)
ALOAD_0
(INVOKESPECIAL "java/lang/Object" "" "()V")
RETURN)
METHOD
("main" "([Ljava/lang/String;)V" (PUBLIC STATIC)
(META MAX-STACK 3)
(META MAX-LOCAL 2)
:L0
ICONST_5 ISTORE_1
:L2
ILOAD_1 (IFLT :L22)
(GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;")
(BIPUSH 100) ILOAD_1 IDIV
(INVOKEVIRTUAL "java/io/PrintStream" "println" "(I)V")
(IINC 1 255) (GOTO :L2)
:L22
(GOTO :L34)
:L25
ASTORE_1 ;store exception object
(GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;")
(LDC "Nice exception...")
(INVOKEVIRTUAL "java/io/PrintStream" "println" "(Ljava/lang/String;)V")
:L34
RETURN
(META EXCEPTION :L0 :L22 :L25 "java/lang/ArithmeticException"))
キーワードはラベルと見なされます。
逆アセンブリするときはラベル名は自動生成されます。
例外の指定は非常に地味です。 (META EXCEPTION …) の
最初の:L0と:L22は例外を捕まえる範囲、:L25は例外ハンドラです。
という訳で、それっぽく動いています。
めでたしめでたし。
マンガで分かるLisp [Section 2.3]
火曜日, 9月 22nd, 2009マンガで分かるLisp [その7]
月曜日, 9月 14th, 2009先にその5を読み直した方がいいかも。

白クダーはミャーと鳴く。
マンガで分かるLisp [Section 2.2]
月曜日, 9月 7th, 2009setqの正しい発音は「せっときゅー」とHyperSpecに書いてある。

それでも私は「せっとく」と発音したい。


