マイペースなプログラミング日記

x86エミュレータやFPGA、WebGLにお熱なd-kamiがマイペースに書くブログ

Kernel/VM Advent Calendar 15日目: リアルモード Brainfuck

作った物

カーネル/VM Advent Calendarの俺の番が回ってきた。元々用意していたネタが準備不足により使えなくなってしまったのでリアルモードでBrainfuckしてみようとなった。リアルモードBrainfuckといってもただnasmで書いただけのような気もするが、気にしないで話を進めよう。

リアルモード BrainfuckとはCPUのリアルモードで動かすBrainfuckである。今回は自作(コピペ)ブートローダから直接呼び出すようにした。構成はブートローダ、メインプログラム、Brainfuckソースコードからなり、ブートローダとメインプログラムをバイナリにしたあと、できたバイナリとBrainfuckソースコードをcatで繋げたものとなっている。

とりあえず解説

ブートローダはINT 0x10でディスプレイを初期化して、INT 0x10でメッセージを表示後、INT 0x13でディスクを読み込んでいる。そしてディスクをある程度読み込んだらメインプログラムへJMP。メインプログラムではBrainfuckソースコードを読み込んで実行する。ソースコードの位置はアドレスを直接打ちこんだ。SIレジスタBrainfuckソースコードの位置、DIにBrainfuckで扱うメモリの位置が入っている。Brainfuckの実行に関してだが、ユーザからの入力を省いているため","が使えないのに注意。

使い方

以下のファイルがブートローダとメインプログラムになっている。nasmでバイナリにしたあと以下のようにcatで繋げる。hello.bfは自分で用意したbrainfuckソースコードである。Hello Worldの例を下に載せておく。

cat boot bfexe hello.bf boot > brainfuck

catした後は、qemuで実行する

qemu -fda brainfuck

これで実行できるはず。以下、ファイルとHello Worldの例

boot.asm 直
bfexe.asm 直

BrainfuckHello Worldの例

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.

最後にメインプログラムの部分だけ載せておく

[BITS 16]

s_start:
    MOV AX, CS
    MOV DS, AX
    MOV SI, 0x200

    CALL memset
    MOV DI, 0x8000

main:
    MOV AL, [SI]
    ADD SI, 1

    CMP AL, '>'
    JE incdi

    CMP AL, '<'
    JE decdi

    CMP AL, '+'
    JE incmem

    CMP AL, '-'
    JE decmem

    CMP AL, '.'
    JE dischar

    CMP AL, '['
    JE loopstart

    CMP AL, ']'
    JE loopend

    JMP bfend

incdi:
    ADD DI, 1
    JMP main

decdi:
    SUB DI, 1
    JMP main

incmem:
    MOV AL, [DI]
    ADD AL, 1
    MOV [DI], AL
    JMP main

decmem:
    MOV AL, [DI]
    SUB AL, 1
    MOV [DI], AL
    JMP main

dischar:
    MOV AL, [DI]
    MOV AH, 0x0E
    MOV BX, 0x15
    INT 0x10
    JMP main

loopstart:
    MOV AL, [DI]
    CMP AL, 0
    JNE main

    MOV BP, 1
toend:
    OR BP, 0
    JE main

    MOV AL, [SI]
    ADD SI, 1

    CMP AL, '['
    JE incnestfs
    CMP AL, ']'
    JE decnestfs

    JMP toend

loopend:
    MOV BP, 1
    SUB SI, 1

tostart:
    OR BP, 0
    JE main

    SUB SI, 1
    MOV AL, [SI]

    CMP AL, ']'
    JE incnestfe
    CMP AL, '['
    JE decnestfe

    JMP tostart

tomain:
    ADD SI, 1
    JMP main

incnestfs:
    ADD BP, 1
    JMP toend

decnestfs:
    SUB BP, 1
    JMP toend

incnestfe:
    ADD BP, 1
    JMP tostart

decnestfe:
    SUB BP, 1
    JMP tostart

memset:
    MOV DI, 0x8000
    MOV AX, 0
    MOV CX, 30000

    REP STOSB
    RET

bfend:
    JMP bfend

    TIMES 512 - ($ - $$) db 0