부트 로더 골프 : Brainf ***


20

주어진 Brainfuck 프로그램을 실행하는 부트 로더를 만듭니다. 이것은 이므로 바이트 수가 가장 적은 프로그램이 승리합니다. 부트 로더 인 프로그램의 크기는 컴파일 된 코드에서 0이 아닌 바이트로 계산됩니다.

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 Wiki , Wikipedia


21
Input must be red나는 대부분의 부트 로더가 기본적으로 색상을 지원하지 않는다고 확신합니다.
Fund Monica의 소송

4
플로피 디스크가 무엇인지 젊은 개발자들에게 설명해야합니다!
bobbel

1
@bobbel 부트 로더로 시작합시다
Bálint

5
이 제목이 공격적이라는 것을 알지 못하지만 어떻게 가능합니까? 메타 에 따르면 제목에 "brainfuck"을 넣는 것은 불가능합니다.
DJMcMayhem

30k 개 이상의 셀을 사용할 수 있습니까?
CalculatorFeline

답변:


13

171 바이트 1

우오오! 하루 반이 걸렸지 만 재미있었습니다 ...

여기 있습니다. 나는 그것이 스펙 (셀 포인터의 랩 어라운드, 입력시 문자 에코, 문자로 문자 읽기, 입력 문자의 에코 등)을 준수한다고 생각하며 실제로 작동하는 것 같습니다 (잘, 많은 프로그램을 시도하지 않았습니다) 그러나 언어의 단순성을 감안할 때 적용 범위는 그렇게 나쁘지 않습니다.)

한계

한 가지 중요한 점 : brainfuck 프로그램에 8 개의 brainfuck 명령 이외의 문자가 포함되어 있거나 []균형이 맞지 않으면 충돌이 발생합니다. mouhahahaha!

또한 brainfuck 프로그램은 512 바이트 (섹터)를 초과 할 수 없습니다. 그러나 "실행 가능한 Brainfuck은 두 번째 디스크 섹터에 있습니다" 라고 말하고 있기 때문에 이는 적합 해 보입니다 .

마지막 세부 사항 : 셀을 0으로 명시 적으로 초기화하지 않았습니다. 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

사용 된 트릭

좋아, 나는 약간 속였다. "부트 로더 이기 때문에 프로그램의 크기는 컴파일 된 코드에서 0이 아닌 바이트로 계산된다"고 말했기 때문에 , 나는 8 개의 brainfuck opcode의 구현 사이에 "구멍"을 허용함으로써 코드를 더 작게 만들었다. 이런 식으로, 나는 일련의 큰 테스트, 점프 테이블 또는 다른 것을 필요로하지 않습니다 : 나는 brainfuck 명령을 실행하기 위해 32로 곱한 brainfuck "opcode id"(0에서 8까지)로 점프합니다. 그것은 명령 구현이 32 바이트를 넘을 수 없다는 것을 의미합니다).

또한 brainfuck 프로그램 문자 에서이 "opcode id"를 가져 오기 위해 약간의 셔플 링이 필요하다는 것을 알았습니다. 실제로 opcode 문자의 비트 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 바이트 이상이 필요한 opcode가 하나 있지만 마지막 것입니다 (jump forward [). 더 많은 공간이 있으므로 모든 것이 정상입니다.

다른 트릭 : 나는 실제로 구현하지 않은, 전형적인 작품 통역 브레인 퍽하지만,이 상황이 훨씬 더 작은 만드는 방법을 알고하지 않는 ]것처럼 "해당 후 다시 이동 [포인터의 데이터가 0 인 경우" . 대신, 나는 항상 해당하는 것으로 돌아가서 [여기에서 일반적인 [구현을 다시 적용하십시오 ( ]필요한 경우 나중에 다시 구현 합니다). 이를 위해 [, 내가 a를 격려 할 때마다 내부 명령을 실행하기 전에 스택에 현재 "brainfuck 명령 포인터"를 넣습니다.]지시 포인터를 다시 띄웁니다. 마치 함수를 호출하는 것과 거의 같습니다. 따라서 이론적으로 많은 수의 함축 된 루프를 만들어 스택을 오버플로 할 수 있지만 현재 brainfuck 코드의 512 바이트 제한은 없습니다.


1. 코드 자체의 일부인 0 바이트를 포함하지만 일부 패딩의 일부는 아닌 0 바이트 포함

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.