171バイト1
すごい!半日かかりましたが、楽しかったです...
だから、ここにある。私はそれが仕様(セルポインタのラップアラウンド、入力上の文字のエコー、文字ごとの読み取り、入力文字のエコーなど)に準拠していると思います、そして実際に動作するようです(まあ、私は多くのプログラムを試しませんでした、しかし、言語のシンプルさを考えると、カバレッジはそれほど悪くはないと思います)。
制限事項
1つの重要なこと:Brainfuckプログラムに8つのBrainfuck命令以外の文字が含まれて[]
いる場合、またはバランスが悪い場合、mouhahahahaがクラッシュします!
また、brainfuckプログラムは512バイト(セクター)を超えることはできません。しかし、「実行可能なBrainfuckは2番目のディスクセクターにある」と言うので、これは適合しているようです。
最後の詳細:セルを明示的にゼロに初期化しませんでした。Qemuは私のためにそれをやっているようで、私はこれに頼っていますが、実際のコンピューターの実際のBIOSがそれを行うかどうかはわかりません(とにかく初期化はさらに数バイトかかります)。
コード
(テンプレートに基づいて、ところで、これをありがとう、私はそれなしでは試したことがないでしょう):
[BITS 16]
[ORG 0x7C00]
%define cellcount 30000 ; you can't actually increase this value much beyond this point...
; first sector:
boot:
; initialize segment registers
xor ax, ax
mov ss, ax
mov ds, ax
mov es, ax
jmp 0x0000:$+5
; initialize stack
mov sp, 0x7bfe
; load brainfuck code into 0x8000
; no error checking is used
mov ah, 2 ; read
mov al, 1 ; one sector
mov ch, 0 ; cylinder & 0xff
mov cl, 2 ; sector | ((cylinder >> 2) & 0xc0)
mov dh, 0 ; head
; dl is already the drive number
mov bx, 0x8000 ; read buffer (es:bx)
int 0x13 ; read sectors
; initialize SI (instruction pointer)
mov si, bx ; 0x8000
; initialize DI (data pointer)
mov bh, 0x82
mov di, bx ; 0x8200
decode:
lodsb ; fetch brainfuck instruction character
.theend:
test al, al ; endless loop on 0x00
jz .theend
and ax, 0x0013 ; otherwise, bit shuffling to get opcode id
shl ax, 4
shl al, 2
shr ax, 1
add ax, getchar ; and compute instruction implementation address
jmp ax
align 32, db 0
getchar:
xor ah, ah
int 0x16
cmp al, 13
jne .normal
mov al, 10 ; "enter" key translated to newline
.normal:
mov byte [di], al
push di
jmp echochar
align 32, db 0
decrementdata:
dec byte [di]
jmp decode
align 32, db 0
putchar:
push di
mov al, byte [di]
echochar:
mov ah, 0x0E
xor bx, bx
cmp al, 10 ; newline needs additional carriage return
jne .normal
mov al, 13
int 0x10
mov al, 10
.normal:
int 0x10
pop di
jmp decode
align 32, db 0
incrementdata:
inc byte [di]
jmp decode
align 32, db 0
decrementptr:
dec di
cmp di, 0x8200 ; pointer wraparound check (really, was that necessary?)
jge decode
add di, cellcount
jmp decode
align 32, db 0
jumpback:
pop si
jmp jumpforward
align 32, db 0
incrementptr:
inc di
cmp di, 0x8200+cellcount ; pointer wraparound check
jl decode
sub di, cellcount
jmp decode
align 32, db 0
jumpforward:
cmp byte [di], 0
jz .skip
push si
jmp decode
.skip:
xor bx, bx ; bx contains the count of [ ] imbrication
.loop:
lodsb
cmp al, '['
je .inc
cmp al, ']'
jne .loop
test bx, bx
jz decode
dec bx
jmp .loop
.inc:
inc bx
jmp .loop
; fill sector
times (0x1FE)-($-$$) db 0
; boot signature
db 0x55, 0xAA
; second sector contains the actual brainfuck program
; currently: "Hello world" followed by a stdin->stdout cat loop
db '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.,[.,]'
times 0x400-($-$$) db 0
使用されるトリック
OK、少しだまされました。あなたが言っているので、「ブートローダーであり、プログラムのサイズは、コンパイルされたコード内の非ゼロバイトでカウントされた」、Iは8つのbrainfuckオペコードの実装の間に「穴」を可能にすることによって、コード小さくします。このように、大きなテストシーケンスやジャンプテーブルなどは必要ありません。brainfuck命令を実行するために32を掛けたbrainfuck "opcode id"(0〜8)にジャンプするだけです(注意する価値があります)つまり、命令の実装は32バイトを超えることはできません)。
さらに、フェッチされたBrainfuckプログラムの文字からこの「オペコードID」を取得するには、少しシャッフルする必要があることに気付きました。実際、オペコード文字のビット0、1、4のみを考慮すると、8つの一意の組み合わせになります。
X XX
00101100 0x2C , Accept one byte of input, storing its value in the byte at the pointer.
00101101 0x2D - Decrement (decrease by one) the byte at the pointer.
00101110 0x2E . Output the value of the byte at the pointer.
00101011 0x2B + Increment (increase by one) the byte at the pointer.
00111100 0x3C < Decrement the pointer (to point to the next cell to the left).
01011101 0x5D ] Jump back after the corresp [ if data at pointer is nonzero.
00111110 0x3E > Increment the pointer (to point to the next cell to the right).
01011011 0x5B [ Jump forward after the corresp ] if data at pointer is zero.
そして、幸運なことに、実際に実装するのに32バイト以上を必要とするオペコードが1つありますが、それは最後のものです(ジャンプフォワード[
)。あとに余裕があるので、すべてが順調です。
その他のトリック:典型的なBrainfuckインタープリターがどのように機能するかわかりませんが、物事をより小さくするために]
、「[
ポインターのデータがゼロでない場合、対応するものの後にジャンプ」として実装しませんでした。代わりに、私は常に対応するに戻り[
、ここから、典型的な[
実装を再適用します(その後、]
必要に応じて、最終的には再び先に進みます)。このため、aを計算するたびに[
、内部命令を実行する前にスタックに現在の「brainfuck命令ポインター」を配置し、]
、命令ポインタをポップバックします。まるで関数の呼び出しであるかのように。したがって、理論的には多くの複合ループを作成することでスタックをオーバーフローさせることができますが、とにかくブレインファックコードの現在の512バイトの制限はありません。
1.コード自体の一部であるゼロバイトを含みますが、パディングの一部であるゼロバイトは含みません。
Input must be red
ほとんどのブートローダーは色をネイティブにサポートしていないと確信しています。