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 바이트 포함
Input must be red
나는 대부분의 부트 로더가 기본적으로 색상을 지원하지 않는다고 확신합니다.