Ich weiß, dass es "da draußen" genügend Brainfuck-Interpreter gibt. Also warum einen eigenen schreiben? Nun, es ist eine gewisse Herausforderung und es macht einfach Spaß. Also fangen wir an.

Die Sprache Brainfuck

Die Programmiersprache Brainfuck zählt zu den esoterischen Programmiersprachen. Mit ihren 8 Befehlen ist sie turing-vollständig. Ein Brainfuck-Programm weißt eine Trennung zwischen Daten und Programm auf. Ein Programm besteht aus einer Liste von Befehlen, die sequentiell abgearbeitet wird. Dazu gibt es ein unendlichen Band an Speicherzellen. Im Gegensatz zur klassischen Turing-Maschine ist dieses Band nach einer Seite begrenzt.

Der Interpreter

Der Interpreter nutzt einen objektorientierten Ansatz. Es gibt Klassen für den Interpreter und für die Ein- und Ausgabe. In einer While-Schleife wird das Bf-Programm solange durchlaufen, bis es eventuell terminiert. Das Programm wird aus einer Datei gelesen und geparst. Das Parsing findet so statt, dass alle gelesenen Zeichen, die keine Bf-Befehle sind, ignoriert werden. Das daraus gewonnene Programm wird dann zeichenweise abgearbeitet. Wenn durch die Abarbeitung das "Ende" des Speicherbandes erreicht wird, wird es entsprechend erweitert. Dies kann natürlich schnell dazu führen, dass der Speicher knapp wird (z.B. durch "+[>+]").

Die Assembler-Version

Im Rahmen des Uni-Kurses soll eine Implementierung in Assembler geschrieben werden. Dazu hier meine Implementierung:

section .text
  extern printc
  extern readc
  extern allocmem
  global bfintr
  
bfintr:
;Parameter in eigene Register verschieben
  
  mov r8, rdi  ; r8 BF-Programm
  mov r9, rsi  ; r9 BF-Speicher
  mov r10, rdx ; r10 BF-Speicher-Größe
  mov r11, 0   ; r11 Programmposition
  mov r12, 0   ; r12 Speicherposition
  mov r13, 0   ; r13 Anzahl an Klammern
  
  mov rax, 0
  
bfintr_while:
  mov r14, [r8 + r11]
  and r14, 0xFF
  cmp r14, 0
  je bfintr_end
  
  cmp r14, '>'
  je bfintr_greater
  
  cmp r14, '<'
  je bfintr_smaller
  
  cmp r14, '+'
  je bfintr_plus
  
  cmp r14, '-'
  je bfintr_minus
  
  cmp r14, '.'
  je bfintr_point
  
  cmp r14, ','
  je bfintr_comma
  
  cmp r14, '['
  je bfintr_opening
  
  cmp r14, ']'
  je bfintr_closing
  
  jmp bfintr_while_end
  
bfintr_while_end:
  inc r11
  jmp bfintr_while

bfintr_end:
  mov rax, r11
  mov rsi, r9
  ret

bfintr_greater:
  inc r12
  cmp r12, r10
  mov rax, 1
  je bfintr_greater_alloc
  jmp bfintr_while_end
 
bfintr_greater_alloc:
  push r8
  push r10
  push r11
  push r12
  push r13
  mov rdi, r9
  mov rsi, r10
  call allocmem
  mov r9, rax
  pop r13
  pop r12
  pop r11
  pop r10
  pop r8
  add r10, 1024
  jmp bfintr_while_end
  
bfintr_smaller:
  cmp r12, 0
  mov rax, 2
  je bfintr_end
  dec r12
  jmp bfintr_while_end
  
bfintr_plus:
  mov r15, [r9 + r12]
  inc r15
  mov [r9 + r12], r15
  jmp bfintr_while_end
  
bfintr_minus:
  mov r15, [r9 + r12]
  dec r15
  mov [r9 + r12], r15
  jmp bfintr_while_end
  
bfintr_point:
  mov rax, 3
  push r8
  push r9
  push r10
  push r11
  push r12
  push r13
  mov rdi, [r9 + r12]
  call printc
  pop r13
  pop r12
  pop r11
  pop r10
  pop r9
  pop r8
  jmp bfintr_while_end
  
bfintr_comma:
  mov rax, 4
  push r8
  push r9
  push r10
  push r11
  push r12
  push r13
  call readc
  pop r13
  pop r12
  pop r11
  pop r10
  pop r9
  pop r8
  mov [r9 + r12], al
  
  jmp bfintr_while_end
  
bfintr_opening:
  mov r15, [r9 + r12]
  and r15, 0xFF
  cmp r15, 0
  je bfintr_skip

  push r11
  inc r13  ; Anzahl an Klammern inkrementieren
  
  jmp bfintr_while_end
bfintr_closing:
  pop r11
  dec r11
  dec r13
  jmp bfintr_while_end
  
bfintr_skip:
  push r13
  mov r13, 0
bfintr_skip_while:
  inc r11
  mov r14, [r8 + r11]
  and r14, 0xFF
  
  cmp r14, '['
  je bfintr_skip_opening
  
  cmp r14, ']'
  je bfintr_skip_closing
  jmp bfintr_skip_while
  
bfintr_skip_end:
  pop r13
  ;inc r11
  jmp bfintr_while_end
  
bfintr_skip_opening:
  inc r13
  jmp bfintr_skip_while
  
bfintr_skip_closing:
  cmp r13, 0
  je bfintr_skip_end
  dec r13
  jmp bfintr_skip_while