Bootloader golf: Brainf ***


20

Verilen Brainfuck programını yürüten bir bootloader oluşturun. Bu , bu yüzden en az bayt ile program kazanır. Bir önyükleyici olarak, programın boyutu derlenmiş kodda sıfır olmayan bayt olarak sayılır.

brainfuck

30000 8 bit taşan hücreler. İşaretçi sarılıyor.

İşlemlerle ilgili bazı notlar:

  • Giriş, yazdırılabilir tüm ASCII karakterleri doğru şekilde desteklenecek şekilde okunmalıdır. Diğer tuş vuruşları isteğe bağlı bir karakter ekleyebilir veya hiç bir şey yapamaz.
  • Okuma kullanıcı girdisi satır arabellek değil karakter arabellek olmalıdır.
  • Okunan kullanıcı girdisi eklenen karakteri yankılamalıdır.
  • Çıktı, kod sayfasını 437 veya yerleşik VGA bağdaştırıcılarının varsayılan kod sayfasını izlemelidir .

bootloader

Bu bir x86 önyükleyici. Bir önyükleyici geleneksel 55 AAsıra ile biter . Kodunuzun VirtualBox, Qemu veya diğer tanınmış x86 öykünücülerinde çalışması gerekir.

Disk

Yürütülebilir Brainfuck, önyükleyicinizden hemen sonra ikinci disk sektöründe bulunur; bu, genellikle MBR bölümünde, diskteki ilk sektördür. Ek kod (510 baytın üzerindeki herhangi bir kod) diğer disk sektörlerinde bulunabilir. Depolama aygıtınızın bir sabit sürücü veya bir disket olması gerekir.

STDIO

Tabii ki, bir önyükleyici işletim sisteminin IO özelliklerine erişemez. Bu nedenle, bunun yerine metin yazdırmak ve kullanıcı girişini okumak için BIOS işlevleri kullanılır.

şablon

Başlamak için, Nasm (intel sözdizimi) derlemesinde yazılmış basit bir şablon:

[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

Bunu derlemek oldukça kolaydır:

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

Ve koş. Örneğin, Qemu ile:

qemu-system-i386 -fda boot.raw

Ek bilgi: OsDev wiki , Wikipedia


21
Input must be redÇoğu bootloader'ın doğal olarak rengi desteklemediğinden eminim.
Monica'nın Davası

4
Bir disketin ne olduğunu genç geliştiricilere açıklamalıyız!
bobbel

1
@bobbel Bootloader ile başlayalım
Bálint

5
Bu unvanı rahatsız edici bulmuyorum, ama nasıl mümkün olabilir? Göre meta , o başlığında "brainfuck" koymak imkansız.
DJMcMayhem

30 binden fazla hücre kullanabilir miyiz?
CalculatorFeline

Yanıtlar:


13

171 bayt 1

Wooohoooo! Günün yarısını aldı, ama eğlenceliydi ...

İşte burada. Ben özelliklerine (hücre işaretçisi sarma, girişte karakter yankısı, char ile char okuma, giriş karakterleri yankısı, ...) uygun olduğunu düşünüyorum, ve aslında işe yarıyor gibi görünüyor (iyi, pek çok program denemedim , ancak dilin sadeliği göz önüne alındığında, kapsam o kadar da kötü değil bence).

Sınırlamalar

Önemli bir şey: Eğer beyin programınız 8 beyin komutundan başka bir karakter içeriyorsa veya []iyi dengelenmiş değilse , size çökecektir, mouhahahaha!

Ayrıca, beyin fırtınası programı 512 baytı (bir sektörü) aşamaz. Ancak, "yürütülebilir Brainfuck ikinci disk sektöründe yer alıyor" dediğiniz için bu uygun görünüyor .

Son detay: Hücreleri açıkça sıfıra başlatmadım. Qemu bunu benim için yapıyor gibi görünüyor ve buna güveniyorum, ancak gerçek bir bilgisayardaki gerçek bir BIOS'un bunu yapıp yapmayacağını bilmiyorum (başlatma zaten birkaç bayt daha fazla sürecek).

Kod

(şablonunuza dayanarak ve bu arada, bunun için teşekkürler, onsuz asla denemezdim):

[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

Kullanılan numaralar

Tamam, biraz kandırdım. "Önyükleyici olmak, programın boyutu derlenmiş kod sıfır olmayan bayt olarak sayılır" dediğinden beri , sekiz brainfuck opcodes uygulanması arasında "delik" izin vererek kodu daha küçük yaptım. Bu şekilde, testlerin büyük bir sırasına, bir atlama tablosuna veya herhangi bir şeye ihtiyacım yok: Sadece beyin lanet talimatını yürütmek için beyin çarpı "opcode id" (0'dan 8'e) çarparak 32 ile çarptım ( bu, talimatların uygulanmasının 32 bayttan fazla süremeyeceği anlamına gelir).

Ayrıca, bu "opcode id" getirilen brainfuck program karakteri almak için, ben sadece biraz karıştırılması gerektiğini fark ettim. Gerçekten de, sadece opcode karakterinin 0, 1 ve 4 bitlerini düşünürsek, 8 benzersiz kombinasyonla sonuçlanırız:

   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.

Ve şanslıyım, aslında 32 bayttan fazlasını gerektiren bir opcode var, ama sonuncusu (ileri atla [). Sonra daha fazla yer olduğundan, her şey yolunda.

Diğer hile: Tipik bir beyin fırtınası yorumlayıcısının nasıl çalıştığını bilmiyorum, ama işleri daha küçük yapmak için aslında " İşaretçideki veriler sıfır değilse karşılık gelenlerden sonra geri atla"] olarak uygulama yapmadım . Bunun yerine, her zaman karşılık gelenlere geri dönüyorum ve buradan, tipik uygulamayı yeniden uyguluyorum (daha sonra, gerekirse tekrardan sonra ileri gidiyor ). Bunun için, a'yı her şifrelediğimde , iç talimatları uygulamadan önce mevcut "beyin fuck talimatı işaretçisini" yığına koydum ve bir[[[][], Talimat göstergesini geri açtım. Sanki bir fonksiyon çağrısı gibiydi. Bu nedenle teorik olarak birçok bindirilmiş döngü oluşturarak yığını taşıyabilirsiniz, ancak zaten beyinfuck kodunun şu anki 512 bayt sınırlaması ile değil.


1. Kodun bir parçası olan ancak bazı dolguların parçası olmayan sıfır bayt dahil

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.