171 ไบต์1
Wooohoooo! ใช้เวลาครึ่งวัน แต่มันสนุก ...
ดังนั้นนี่คือ ฉันคิดว่ามันสอดคล้องกับสเปค (โดยประมาณของตัวชี้เซลล์, เสียงก้องของตัวอักษรในอินพุต, การอ่านถ่านโดยถ่าน, เสียงก้องของอินพุตตัวอักษร, ... ) และดูเหมือนว่าจะใช้งานได้จริง (ดีฉันไม่ได้ลองหลายโปรแกรม แต่ด้วยความเรียบง่ายของภาษาความคุ้มครองก็ไม่ได้แย่ขนาดนั้นฉันคิดว่า)
ข้อ จำกัด
สิ่งหนึ่งที่สำคัญ: ถ้าโปรแกรม brainfuck ของคุณมีตัวอักษรอื่นนอกเหนือจาก 8 คำสั่ง brainfuck หรือถ้าโปรแกรม[]
ไม่สมดุลกันมันก็จะผิดพลาดกับคุณ
นอกจากนี้โปรแกรม 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
เทคนิคที่ใช้
ตกลงฉันโกงนิดหน่อย เนื่องจากคุณบอกว่า"การเป็น bootloader ขนาดของโปรแกรมจะนับเป็นไบต์ที่ไม่เป็นศูนย์ในโค้ดที่คอมไพล์"ฉันทำให้โค้ดมีขนาดเล็กลงโดยอนุญาตให้ "รู" ระหว่างการใช้งาน opcodes brainfuck แปดตัว ด้วยวิธีนี้ฉันไม่ต้องการลำดับการทดสอบขนาดใหญ่ตารางการกระโดดหรืออะไรก็ตาม: ฉันแค่ข้ามไปที่ "รหัส opcode" (จาก 0 ถึง 8) brainfuck คูณด้วย 32 เพื่อดำเนินการคำสั่ง brainfuck (ควรสังเกตว่า มันหมายถึงการใช้งานคำแนะนำนั้นไม่สามารถใช้เกิน 32 ไบต์)
ยิ่งไปกว่านั้นเพื่อให้ได้ "รหัส opcode" นี้จากการนำโปรแกรมตัวอักษร brainfuck มาใช้ฉันก็สังเกตเห็นว่าจำเป็นต้องมีการสับบิตเล็กน้อย แน่นอนถ้าเราพิจารณาบิต 0, 1 และ 4 ของอักขระ opcode เราจะได้ชุดที่ไม่ซ้ำกัน 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 ไบต์ที่จะใช้ แต่มันเป็นคนสุดท้าย (กระโดดไปข้างหน้า[
) เนื่องจากมีห้องเพิ่มเติมหลังจากนั้นทุกอย่างดี
เคล็ดลับอื่น ๆ : ผมไม่ทราบวิธีการที่ brainfuck ทั่วไปล่ามงาน แต่จะทำให้สิ่งที่มีขนาดเล็กมากผมไม่ได้จริงใช้]
เป็น"กระโดดกลับมาหลังจากที่สอดคล้องกัน[
หากข้อมูลที่ชี้ไม่ใช่ศูนย์" แต่ฉันมักจะกลับไปที่สอดคล้องกัน[
และจากที่นี่อีกครั้งใช้ทั่วไป[
การดำเนินงาน (ซึ่งในที่สุดจะไปข้างหน้าหลังจากที่]
อีกครั้งถ้าจำเป็น) สำหรับสิ่งนี้ทุกครั้งที่ฉันเข้ารหัส[
ฉันใส่ "ตัวชี้คำสั่ง brainfuck" ปัจจุบันลงบนสแต็กก่อนเรียกใช้คำแนะนำด้านในและเมื่อฉันพบ]
ฉันกลับมาที่ตัวชี้คำสั่ง ค่อนข้างมากราวกับว่ามันเป็นการเรียกฟังก์ชั่น ในทางทฤษฎีแล้วคุณสามารถล้นสแต็กได้โดยสร้างลูปที่มีการวนรอบจำนวนมาก แต่ไม่ได้มีข้อ จำกัด 512 ไบต์ในปัจจุบันของรหัส brainfuck อยู่ดี
1. รวมศูนย์ไบต์ที่เป็นส่วนหนึ่งของรหัส แต่ไม่รวมถึงส่วนที่เป็นส่วนหนึ่งของการแพ็ด
Input must be red
ฉันค่อนข้างมั่นใจว่า bootloaders ส่วนใหญ่ไม่สนับสนุนสี