BrainfuckインタプリタをAssemblyで書き直してみた。


先日の池袋バイナリ勉強会に行ったときの記事を覚えてますでしょうか?

バイナリ勉強会#1

ここにあるようにC++でBrainfuckのインタプリタを書いたのですが、このコードをコンパイルした実行ファイルの大きさがゆうに100kBいってしまっていて、もっと軽くならないものかと思ってたんですよね・・・

 

そこで、今回はBrainfuckのインタプリタをAssembly言語で最初から書き直してみました。

それではどうぞ!


;Brainfuck.asm
 bits 16
 org 100h

;メモリ初期化
 mov cx,799h
set: mov bx,cx
 mov byte [mem+bx],0
 loop set

;コード変換
 mov ah,09h
 mov dx,msg0
 int 21h
 mov ah,01h
 int 21h
 sub al,30h
 mov byte [cvt],al

;Inputメッセージ
 mov ah,09h
 mov dx,crlf
 int 21h
 mov dx,msg1
 int 21h
 mov dx,crlf
 int 21h

;Brainfuckコード入力
 mov ah,01h
input: int 21h
 cmp al,0dh
 jz domsg
 inc word [len]
 mov bx,[len]
 mov byte [cmd+bx],al
 jmp input

;コード実行
domsg: mov ah,09h
 mov dx,crlf
 int 21h
 mov dx,msg2
 int 21h
 mov dx,crlf
 int 21h

main: inc word [cps]
 mov bx,[cps]

cmp byte [cmd+bx],'-'
 jz decr

cmp byte [cmd+bx],'>'
 jz next

cmp byte [cmd+bx],'<'
 jz back

cmp byte [cmd+bx],','
 jz getc

cmp byte [cmd+bx],'.'
 jz putc

;変換分岐
 cmp byte [cvt],1
 jz cnvrt

;変換不必要
 cmp byte [cmd+bx],'+'
 jz incr

cmp byte [cmd+bx],'['
 jz lin

cmp byte [cmd+bx],']'
 jz lout

;変換必要
cnvrt: cmp byte [cmd+bx],':'
 jz incr

cmp byte [cmd+bx],']'
 jz lin

cmp byte [cmd+bx],'\'
 jz lout

done: mov bx,[mps]
 cmp byte [mem+bx],0
 js err
 cmp word [mps],0
 js err
 mov dx,[len]
 cmp [cps],dx
 js main
 mov ax,4c00h
 int 21h

;エラー処理
err: mov ah,09h
 mov dx,msg3
 int 21h
 mov ax,4c00h
 int 21h

;+
incr: mov bx,[mps]
 inc byte [mem+bx]
 jmp done

;-
decr: mov bx,[mps]
 dec byte [mem+bx]
 jmp done

;>
next: inc word [mps]
 jmp done

;<
back: dec word [mps]
 jmp done

;.
putc: mov bx,[mps]
 mov dl,[mem+bx]
 mov ah,02h
 int 21h
 jmp done

;,
getc: mov ah,08h
 int 21h
 mov bx,[mps]
 mov byte [mem+bx],al
 jmp done

;[
lin: mov bx,[mps]
 cmp byte [mem+bx],0
 jz skip
 mov bx,[cps]
 push bx
 jmp done

skip: inc word [cps]
 mov bx,[cps]
 ;変換分岐
 cmp byte [cvt],1
 jz scnvrt
 ;変換不要
 cmp byte [cmd+bx],'['
 jz li
 cmp byte [cmd+bx],']'
 jz ld
 ;変換必要
scnvrt: cmp byte [cmd+bx],']'
 jz li
 cmp byte [cmd+bx],'\'
 jz ld
 jmp skip

li: inc byte [lev]
 jmp skip

ld: cmp byte [lev],0
 jz done
 dec byte [lev]
 jmp skip

;]
lout: pop bx
 mov word [cps],bx
 dec word [cps]
 jmp done

msg0 db "Do you need convert code? (Y:1,N:other)>>$"
msg1 db "Input Brainfuck code!$"
msg2 db "Output$"
msg3 db "Error!!$"
crlf db 0dh,0ah,"$"
cvt db 0
cmd resb 800h
cps dw 0
mem resb 800h
mps dw 0
len dw 0
lev db 0

ソースコードは多少長く長くなりましたが、実行ファイルの大きさはなんと4.5kB!びっくりするほど小さくなりました~

あ、でもここでは命令用・実行用メモリをそれぞれ0x800個、つまり十進数で2048個取っているのですが、Brainfuckの仕様通りに実行用メモリの数を30000個取るようにすると30kBほどに膨れ上がってしまうんです。その代わり、やはり演算は相変わらずめちゃくちゃ早いですw

ダウンロードは先日と同様こちらからどうぞ。Brainfuckインタプリタ

 

また来週くらいに同様の勉強会があるようなので、続きをやりに行こうと思います!!

OSって奥深いねぇ


昨日から、やっと自作OSの開発に本格的に取り組み始めました!
と言っても、教科書の順を追ってコーディングしてくだけなんですけどねw

川合秀実さんの「OS自作入門」、これ結構分かりやすいですよねぇ~
川合さんは、セキュリティー&プログラミングキャンプ2010のOS自作組で講師をしていらしたようです。
こんなことなら、ネットワークセキュリティ組よか(ry
なんでもありません。元々自分はネットワークの畑でやってこうとしてた者ですから、全くもって問題ないです。
ていうか、もし違う組だったら今知り合ってる人たちとは知り合えなかったのかなぁ・・・?

ま、いいや

今日はそこまで進んではいません。

まあ、まずは皆さんおなじみの「Hello,World」ですよね。
ちょっと小さくて見にくいかもしれません。
まあ、そこらへんは勘弁してください。
これはブートローダだけのとても軽いプログラムです。
因みにアセンブリで書かれています。
次はディスクの読み込みですね。OSたるもの、ちゃんとシステムを読まなきゃブートローダもなにも無いですもんねw
画面には何も表示されませんが、奥でごにょごにょやってるBIOS君やブートローダ君、いつもありがとうございます!

で、次は早速OS本体の製作です。
方針としてはどうやら、はじめのCPU君とメモリ君とのやり取りはアセンブリで行い、その後のOSの機能はC言語で記述していくようです。
でも、OSの中でも直接I/Oやメモリ等々と渡り歩かなきゃいけない時があり 、そのときはまたアセンブリに頼るようです~

早速作ったのは・・・
なんだかしましまですね。
これはVRAMに色の値を書き込んでやっただけなんですが、、、
よくよくみるとこのVRAM結構すごい。
VRAMは全部で64KBあるらしいですが、そのメモリひとつひとつが全ての画素ひとつひとつに割り当てられてるんですってね!
当たり前っちゃ当たり前なんですけど、物を知らない俺にとってはちょっと感動物でした。

続いては四角、四角、刺客もとい四角。
これは単純にx座標方向に連続して同じ値を代入し、それをy座標方向にも同じだけ繰り返す、という作業で正方形ができます。

まあ、そこで本日の〆はこんな感じ。
なんだかWindows95とかってこんな感じでしたっけ?
いきなりやってることが飛んだように見えますがやってることは至って単純。
一旦バックを緑で埋めて、その後下に灰色のタスクバーを生成。後はメニューボタンとかが入りそうなところに影つけてボタンに見せるだけ。
ようは、これボタンじゃないんですよねww

しばらくはこんな感じで本の受け売りが続くかもしれませんが、暖かく見守っていてください。
では~

結構時間かかるのよな


 私の生活スタイルって結構特殊なんですよねぇ

 ついこの前までは夜は1時半頃に寝て、朝は5時に起きるという極めて不健康な感じだった訳ですけども、最近になってようやく変えることができました。
それは、夜は10時に寝て朝は2時に起きるというものです。
そんな朝早くおきてどうするの、と聞かれれば、それは勿論コーディングですw

 でも時々目覚まし時計で起きれなく、気がついたら5時で「その日はコーディング無し!」になることもあるんですよ(泣)
ま、それが昨日なんですけどねww

 んで、今日も引き続きアセンブリ言語っすよ。
ところで、なんで今頃アセンブリ言語なんてやってるのかというと、一言で言えば「自作OS」!!
夢なんです。それも高校のうちでっていう
そんなわけで今も頑張っているわけですが、よくよく考えると今やってるやつってDOS上のシステムコール使ってるわけで、自作OSに関係ないじゃん?

・・・・・・
・・・

え、
まあ、細かいことは気にせずに、練習っていうことですね。

 本日の学習範囲は演算です。
いやはや、やっぱり高水準言語とは違って面白いね
加算のADDや減算のSUB、これらはディスティネーションオペランドに元の値を入れてソースオペランドで加えたり引いたりする値を指定する。これは感覚で分かります。
でも、ここで面白いのが乗算のMULと除算のDIV。両者ともオペランドはひとつしかなく、ALレジスタやAXレジスタに入っている値を元の値とします。
 MULはオペランドに指定された値をAL、もしくはAXに入っている値と掛け合わせてAXに保存。
 DIVは指定された値でAXに入っている値を割り、商はAL、余りはAHに保存されます。

使い方を逡巡してしまいました。でもすぐ分かったけどね♪
これはシステムコール関係ないし、良かったねww

この本まだまだ終わりそうにないなぁ・・・
いつになったらOS自作始めれるんだろ??

雪降ったのに。。。


昨晩はかなり雪が降りましたね~
うちのプリウス君もフロントガラスが結構斜めってるから効率的に雪を上に積もらせてましたよw

で、今日は学校行事「クロスカントリーレース」がある筈なんです。
多摩川沿いを9kmほど走ります。
んでこの雪ですよ。無くなると高をくくってました。
そこに連絡網「あるそうですよ~
・・・・・。

よし、今日はアセンブリで入出力をやってみよう。
あ、そういえば昨日言いませんでしたが、この中に出てくる単独の数字は十進法で表されたものです。ほかの2進数は○○b、16進法は0x○○とか、○○hとかで表記します。
あと、自分の開発環境はWindowsなのでシステムコールはDOS(Windows)用です。
あしからず

DOSのINT 21hの機能

キーボードからの文字列入力:AHレジスタに0Ahをセット(DX=バッファの先頭アドレス)
ファイル作成:3Chをセット
(DX=ファイル名の先頭アドレス、CX=作成モード、CF=結果、AX=ファイルハンドル)
ファイルオープン:3Dhをセット
(DX=ファイル名の先頭アドレス、AL=モード、CF=結果、AX=ファイルハンドル)
ファイルクローズ:3Ehをセット
(BX=ファイルハンドラ、CF=成功か否か)
ファイルからの読み取り:3Fhをセット
(DX=保存先の先頭アドレス、CX=読み取るバイト数、BX=ファイルハンドラ、CF=結果、AX=読み取ったバイト数)
ファイルへの書き込み:40hをセット
(DX=聞き込み元先頭アドレス、CX=書き込むバイト数、BX=ファイルハンドラ、CF=結果、AX=書き込んだバイト数)

多いね、ちと覚えるの大変だわ~
で、今日はこんなの作りました。
まあ見てもらえば分かるけど、ただ単純に「srcfile.txt」の中身を任意の名前のファイルにコピーするだけw

;bcopy.asm
bits 16
org 100h
mov dx,input
mov ah,09h 
int 21h 
mov ah,0ah
mov dx,sfile
mov byte [sfile],80h
int 21h
mov bh,0
mov bl,byte [sfile+1]
mov [sfile+bx+2],byte 30h
mov dx,crlf
mov ah,09h
int 21h
mov dx,output
mov ah,09h
int 21h
mov ah,0ah
mov dx,dfile
mov byte [dfile],80h
int 21h
mov bh,0
mov bl,byte [dfile+1]
mov [dfile+bx+2],byte 30h

mov dx,sfile+2
mov al,0  ;読み出しモード 
mov ah,3dh  ;ファイルオープン
int 21h jc endquit mov [ifh],ax

mov dx,dfile+2
mov cx,0  ;通常ファイル
mov ah,3ch  ;ファイル作成
int 21h
jc endquit
mov [ofh],ax

mov dx,crlf
mov ah,09h
int 21h

readb:
mov bx,[ifh] ;入力ファイルハンドル
mov dx,buf
mov cx,1
mov ah,3fh  ;読み込み
int 21h

test cx,ax
jz endquit
jc endquit

mov dl,[buf]
call putc

mov bx,[ofh] ;出力ファイルハンドル
mov dx,buf
mov cx,1
mov ah,40h  ;書き込み
int 21h
jmp readb

endquit:
mov ah,3eh
mov bx,[ifh]
int 21h
mov bx,[ofh]
int 21h

mov ax,4c00h
int 21h

putc:
push ax
mov ah,02h
int 21h
pop ax ret

input db "Put the name of the copied file&gt;$"
output db "Put the name of the make file&gt;$"
crlf db 0dh,0ah,"$"ifh dw 0ofh dw 0sfile resb 80hdfile resb 80hbuf resb 2

本に書いてあったコードを見ながら手を加えた程度ですので、そんな大したものではないです。
でもま、こうやって技術は重ねてくものだよね

忘れとる(汗


本格的にAndroid開発始めてから早一ヶ月、全くもってアセンブリやるの忘れてた!!
てなわけで、今日は一ヶ月半ぶりにアセンブリ言語やってみましたよ。

ほうら、忘れてる忘れてる~
一文字出力はAHレジスタに2を移動してシステムコールだとか、そこらへんのことは覚えているのだが・・・
x86ファミリーのレジスタとかJccのニモニックとかの数多すぎだろ、覚えられんわ です。

まだまだ時間はある(?)ので、じっくりと覚えていくことにします。

あ、そうだ、今日ここよくわかんなかったんで、誰か分かる人がいたら解説ほしいw
@simiyu113でもおk

;putchar.asm
 bits 16
 org 100h


 mov dl,41h ;'A'
 push dx
 call putchar
 pop dx

 mov dl,42h ;'B'
 push dx
 call putchar
 pop dx

 mov ax,4c00h
 int 21h

putchar:
 push bp
 mov bp,sp
 sub sp,40h
 mov bx,[bp+4]

 mov dl,bl
 mov ah,2
 int 21h

 mov sp,bp
 pop bp
 ret

・・・サブルーチンのputcharはBPレジスタとかSPレジスタとか一体何をしているの??

 

まあ、そのほかは楽勝