HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Brainf*ck interpreter written in x86 assembly

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
interpreterwrittenx86assemblybrainf

Problem

I have written a program in x86 assembly (Intel syntax/MASM) that interprets brainfuck code that is fed to it via an interactive console and prints the final stack to stdout. Note that it does not include an implementation for the , command, but everything else has been implemented. The idea is that it presents the user with a prompt where they can enter their code; when the user hits Enter, it evaluates it and then dumps the resultant cell state to the console. While the cell state is maintained between prompt entries, the pointer position is not.

It looks like this:

$++++++++[>++++[>++>+++>+++>++>+>->>+[>.>---.+++++++..+++.>>.>+.>++.
Hello World!

0 0 72 100 87 33 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
--------------------------------------------------
%%CODEBLOCK_0%%gt;>[.>]
HdW!

0 0 72 100 87 33 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
--------------------------------------------------
$++++++++[>+++++++++.
A
0 65 72 100 87 33 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
--------------------------------------------------


This is my first major foray into assembly, so I'm mainly looking for general tips, such as:

  • Which registers I should be using in particular cases



  • When I should be using RAM and when I should be using registers



  • Ways that the code could be simplified



`.386
.model flat,stdcall
.stack 4096

include \masm32\include\masm32.inc
includelib \masm32\lib\masm32.lib

ExitProcess proto,dwExitCode:dword

.data
bfsrc BYTE 200 dup(0) ; buffer to store source code
bfcells BYTE 100 dup(0) ; 100-byte data array size for n

Solution

mov eax,0 ; eax is BF data pointer
mov ebx,0 ; ebx is BF source pointer
mov ecx,0 ; ecx is loop depth


If you used the EDI register for the BF data pointer and the ESI register for the BF source pointer, not only would this be a more natural choice, you could also dismiss the push eaxand pop eax around invoke StdOut, addr charBuf.

By further using EBX for the loop depth you can also eliminate the need for push ecxand pop ecx around the same invoke StdOut, addr charBuf.

Do note that clearing a register is better done trough an xor instruction:

xor  edi, edi ; EDI is BF data pointer
xor  esi, esi ; ESI is BF source pointer
xor  ebx, ebx ; EBX is loop depth


cmp BYTE PTR bfsrc[ebx], 0
 je done
 jmp processInstruction
done:


Here's a clear opportunity to optimize the code. Instead of conditionally jumping to done you can use the opposite conditional jump and fall through. This saves an instruction:

cmp BYTE PTR bfsrc[ebx], 0
jne processInstruction
done:


mov  eax, 0
printNext:
cmp  eax, 100
jge  reset
push eax
...
pop  eax
inc  eax
jmp  printNext


You've used a WHILE-loop here. A REPEAT-loop would have been more optimal. Also a better way to zero a register is by xor-ing it with itself. Furthermore by using the EDI register you eliminate the need for the push eax and pop eax:

xor edi, edi
printNext:
 ...
 inc edi
 cmp edi, 100
 jl printNext
reset:


mov dl, BYTE PTR bfcells[eax]
mov BYTE PTR charBuf[0], dl
; follow the character with null to terminate the string
mov BYTE PTR charBuf[1],0


Use the movzx here and shave off an instruction:

movzx edx, BYTE PTR bfcells[eax]
mov WORD PTR charBuf[0], dx ; follow the character with null to terminate the string

Code Snippets

mov eax,0 ; eax is BF data pointer
mov ebx,0 ; ebx is BF source pointer
mov ecx,0 ; ecx is loop depth
xor  edi, edi ; EDI is BF data pointer
xor  esi, esi ; ESI is BF source pointer
xor  ebx, ebx ; EBX is loop depth
cmp BYTE PTR bfsrc[ebx], 0
 je done
 jmp processInstruction
done:
cmp BYTE PTR bfsrc[ebx], 0
jne processInstruction
done:
mov  eax, 0
printNext:
cmp  eax, 100
jge  reset
push eax
...
pop  eax
inc  eax
jmp  printNext

Context

StackExchange Code Review Q#129518, answer score: 4

Revisions (0)

No revisions yet.