Bootloader高尔夫:Brainf ***


20

创建一个执行给定Brainfuck程序的引导程序。这是,因此具有最少字节数的程序将获胜。作为引导加载程序,程序的大小在已编译的代码中以非零字节计。

脑干

30000个8位溢出单元。指针结束。

有关操作的一些注意事项:

  • 必须以正确支持所有可打印ASCII字符的方式读取输入。其他击键可能会插入一个任意字符,或者什么也不做。
  • 读取用户输入必须使用字符缓冲,而不是行缓冲。
  • 读取用户输入必须回显插入的字符。
  • 输出必须遵循代码页437或内置的VGA适配器默认代码页。

引导程序

这是一个x86引导程序。引导加载程序以传统55 AA顺序结束。您的代码必须在VirtualBox,Qemu或其他知名的x86模拟器上运行。

磁碟

可执行的Brainfuck位于第二个磁盘扇区,紧随您的引导加载程序之后,该引导加载程序通常位于MBR部分中的磁盘的第一个扇区中。附加代码(超过510字节的任何代码)可以位于其他磁盘扇区。您的存储设备必须是硬盘驱动器或软盘。

STDIO

当然,引导加载程序无法访问操作系统的IO功能。因此,BIOS功能代替了打印文本和读取用户输入的功能。

模板

首先,这是一个用Nasm(intel语法)程序集编写的简单模板:

[BITS 16]
[ORG 0x7c00]

; first sector:

boot:
    ; initialize segment registers
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax

    ; 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


; fill sector
times (0x200 - 2)-($-$$) db 0

; boot signature
db 0x55, 0xaa

; second sector:

db 'brainfuck code here'

times 0x400-($-$$) db 0

编译起来很容易:

nasm file.asm -f bin -o boot.raw

并运行它。例如,使用Qemu:

qemu-system-i386 -fda boot.raw

附加信息:OsDev WikiWikipedia


21
Input must be red我敢肯定,大多数引导程序本身都不支持颜色。
基金莫妮卡的诉讼

4
应该向年轻的开发人员解释什么是软盘!
bobbel '16

1
@bobbel让我们先从引导程序
巴林特

5
不是说我觉得这个标题令人反感,而是怎么可能呢?根据meta的说法,不可能在标题中加上“ brainfuck”。
DJMcMayhem

我们可以使用超过30k的电池吗?
CalculatorFeline

Answers:


13

171字节1

oo!花了半天时间,但是很有趣。

所以,就在这里。我认为它符合规范(单元格指针的环绕,输入字符回显,按字符读取字符,输入字符回显...),而且似乎确实有效(嗯,我没有尝试很多程序,但考虑到该语言的简单性,我认为覆盖范围还不错。

局限性

重要的一件事:如果您的Brainfuck程序包含8个Brainfuck指令以外的其他字符,或者如果[]均衡性不佳,它将对您造成崩溃,mouhahahaha!

另外,brainfuck程序不能超过512字节(一个扇区)。但是,这似乎是合规的,因为您说“可执行的Brainfuck位于第二个磁盘扇区”

最后一个细节:我没有明确将单元格初始化为零。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

使用技巧

好吧,我有点作弊。既然您说过“作为引导加载程序,程序的大小将在已编译的代码中以非零字节计”,所以我通过允许八个“笨蛋”操作码的实现之间存在“漏洞”来使代码更小。这样,我不需要进行大量的测试,跳转表或其他任何操作:我只需跳转到Brainfuck的“操作码ID”(从0到8)乘以32即可执行Brainfuck指令(值得注意的是,这意味着指令的执行不能超过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个以上的字节来实现,但这是最后一个(向前跳转[)。随着之后有更多的空间,一切都很好。

其他技巧:我不知道典型的“脑操”解释器是如何工作的,但是,为了使事情变得更小,我实际上并没有实现 ][如果指针处的数据为非零,则在相应的位置之后跳回”。取而代之的是,我总是回到相应的[,然后从这里重新应用典型的[实现(然后,最终,]如果需要,可以再次进行)。为此,每次访问a时[,我都会在执行内部指令之前以及当遇到a时将当前的“ brainfuck指令指针”放到堆栈上],我弹出指令指针。几乎就像是对函数的调用一样。因此,从理论上讲,您可以通过执行许多合并的循环来使堆栈溢出,但是无论如何,当前脑筋代码的限制不是512字节。


1.包括代码本身的零字节,但不包括部分填充的零字节

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.