Execute Brain**** is an implementation of Brainf***.
Other implementations of Brainf***.
RCBF is a set of Brainf*** compilers and interpreters written for Rosetta Code in a variety of languages.

Below are links to each of the versions of RCBF.

An implementation need only properly implement the following instructions:



Command

Description


 > 
 Move the pointer to the right


 < 
 Move the pointer to the left


 + 
 Increment the memory cell under the pointer


 - 
 Decrement the memory cell under the pointer


 . 
 Output the character signified by the cell at the pointer


 , 
 Input a character and store it in the cell at the pointer


 [ 
 Jump past the matching ] if the cell under the pointer is 0


 ] 
 Jump back to the matching [ if the cell under the pointer is nonzero

Any cell size is allowed,   EOF   (End-O-File)   support is optional, as is whether you have bounded or unbounded memory.




	;;;	MS-DOS Brainf*** interpreter/compiler
	cpu	8086
putch:	equ	2h		; Print character
puts:	equ	9h		; Print string
open:	equ	3Dh		; Open file
read:	equ	3Fh		; Read from file
exit:	equ	4Ch		; Exit to DOS
flags:	equ	33h		; Set break flags
CMDLEN:	equ	80h		; Address of length of command line argument
CMDARG:	equ	81h		; Address of text of command line argument
BRK:	equ	1		; Break flag 
EOFCH:	equ	-1		; Written to the tape on EOF
section	.text
	org	100h
	;;;	See if there is enough memory
	mov	sp,stack.top	; Move stack inward to free up memory
	mov	ax,cs		; Get allocated memory size from DOS
	dec	ax		; (It is at location 3 in the MCB, which
	mov	es,ax		; is located one paragraph above CS.)
	mov	ax,[es:3]
	mov	bx,sp		; The amount of memory the program itself
	mov	cl,4		; needs is from CS:0 up to CS:SP in bytes,
	shr	bx,cl		; shifted right by 4 to give paragraphs;
	inc	bx		; making sure to round up.
	mov	bp,cs		; The paragraph right after this is used
	add	bp,bx 		; as the segment base for BF's memory.
	sub	ax,bx		; Free mem = allocated mem - program mem
	cmp	ax,128*1024/16	; We'll require at least 128k bytes
	jae	mem_ok		; (for two separate code and data segments)
	mov	dx,err.mem	; If we don't have enough,
	jmp	error		; give an error message. 
	;;;	Stop on Ctrl+C
mem_ok:	mov	ax,flags<<8|BRK
	mov	dl,1
	int	21h
	;;;	See if a command line argument was given
	mov	bl,[CMDLEN]	; Get length of argument
	test	bl,bl		; See if it's zero
	jnz	arg_ok
	mov	dx,err.usage	; Print usage string if no argument given
	jmp	error
arg_ok:	xor	bh,bh
	mov	[CMDARG+bx],bh	; Terminate the argument string with a zero
	mov	ax,open<<8	; Try to open the file for reading
	mov	dx,CMDARG+1	; Skip first item (always 1)
	int	21h
	jnc	fileok
	mov	dx,err.file	; Print file error if it fails
	jmp	error
fileok: mov	di,ax		; Keep file handle in DI
	xor	si,si		; Keep pointer in SI
	mov	ds,bp		; Start reading into the memory past our stack
block:	mov	ah,read		; Read from file
	mov	bx,di
	mov	cx,0FFFEh
	mov	dx,si		; To the place just beyond the last read
	int	21h
	jnc	.rdok
	mov	dx,err.file	; Read error
	jmp	error
.rdok:	test	ax,ax		; If zero bytes read, we're done
	jz	.done
	add	si,ax		; Move pointer past read
	jnc	block		; If there's still room, do another read
	mov	dx,err.mem	; If we overshot, then give memory error
	jmp	error
.done:	mov	[si],byte 0	; Zero-terminate the data
	;;;	Filter out all non-BF characters
	push	ds		; Set ES to DS 
	pop	es
	xor	si,si		; Source and destination pointer to beginning
	xor	di,di
filter:	lodsb			; Get byte from source
	xor	bx,bx		; See if byte is BF command
.test:	cmp	al,[cs:bx+bfchar]	; Test against current character
	je	.match		; If a match, we found it
	inc	bx		; If not, try next possible command
	cmp	bx,8
	jbe	.test
	jmp	filter		; If we didn't find it, ignore this character
.match:	stosb			; We found it, keep it
	test	al,al		; If zero, we found the end,
	jnz	filter		; Otherwise, do next character
	;;;	Compile the BF source into 8086 machine code
	add	bp,65536/16	; Set ES to point to the start of the second
	mov	es,bp		; 64k (4k paragraphs) that we allocated earlier
	xor	di,di		; Start at address zero,
	push	di		; Store a zero on the stack as boundary marker,
	mov	ax,stop		; At 0000, store a far pointer to the
	stosw			; cleanup routine,
	mov	ax,cs
	stosw
	mov	ax,bfout	; At 0004, store a far pointer to the
	stosw			; output routine,
	mov	ax,cs
	stosw
	mov	ax,bfin		; At 0008, store a far pointer to the
	stosw			; input routine,
	mov	ax,cs
	stosw			; Compiled BF code starts at 000C.
	xor	si,si		; Start at beginning of BF source code	
compil:	lodsb			; Get current command
.ch:	cmp	di,-16		; See if we still have 16 bytes free
	jb	.fch		; (Loop is 11 bytes, +5 for INT 21h/4Ch at end)
	mov	dx,err.mem	; If not, we're out of memory 
	jmp	error
.fch:	cmp	al,'+'		; + and - change the value of the current cell
	je	tapval
	cmp	al,'-'
	je	tapval
	cmp	al,'>'		; < and > move the tape
	je	tapmov
	cmp	al,'<'
	je	tapmov
	cmp	al,','		; I/O
	jne	.tsout		; Conditional jumps are limited to 128-byte
	jmp	chin		; displacement
.tsout:	cmp	al,'.'
	jne	.tsls
	jmp	chout
.tsls:	cmp	al,'['		; Loops 
	jne	.tsle
	jmp	loops
.tsle:	cmp	al,']'
	jne	.tsend
	jmp	loope
.tsend:	test	al,al 		; Reached zero?
	jnz 	compil		; If not, next command
	jmp	cdone		; If so, we're done
	;;;	Compile a string of +s and -s into an 8086 instruction
tapval:	xor	cl,cl		; Count up contiguous +s and -s modulo 256
.ch:	cmp	al,'+'		
	je	.inc
	cmp	al,'-'
	je	.dec
	test	cl,cl		; If zero,
	jz	compil.ch	; it's a no-op. 
	mov	bl,al		; Otherwise, keep next character
	cmp	cl,-1		; If -1, decrement cell
	mov	ax,0FFEh	; DEC BYTE [BX]
	je	.wword
	cmp	cl,1		; If 1, increment cell
	mov	ax,07FEh	; INC BYTE [BX]
	je	.wword
	mov	ax,0780h	; ADD BYTE [BX],
	stosw
	mov	al,cl		; change to cell
	stosb
	mov	al,bl		; Move next character back into AL
	jmp	compil.ch	; Compile next command
.inc:	inc	cl		; Increment cell
	lodsb	
	jmp	.ch
.dec:	dec	cl		; Decrement cell
	lodsb
	jmp	.ch
.wword:	stosw			; Write instruction word
	mov	al,bl		; Move next character back into AL
	jmp 	compil.ch	; Compile next command
	;;;	Compile a string of <s and >s into an 8086 instruction
tapmov:	xor	cx,cx		; Count up contiguous <s and >s modulo 65536
.ch:	cmp	al,'>'
	je	.right
	cmp	al,'<'
	je	.left
	test	cx,cx		; Is there any net movement at all?
	jnz	.move		; If so, generate a move instruction
	jmp	compil.ch	; But otherwise it's a no-op, ignore it
.move:	mov	bl,al		; Otherwise, keep next character
	cmp	cx,4		; If CX<4, a series of INC BX are best
	mov	al,43h		; INC BX
	jb	.wbyte
	neg	cx
	cmp	cx,4		; If -CX<4, a series of DEC BX are best
	mov	al,4Bh		; DEC BX
	jb	.wbyte
	neg	cx
	mov	ax,0C381h	; ADD BX,
	stosw
	mov	ax,cx		; tape movement
	stosw
	mov	al,bl		; Move next character back into AL
	jmp	compil.ch	; Compile next command
.left:	dec	cx		; Left: decrement pointer
	lodsb
	jmp	.ch
.right:	inc	cx		; Right: increment pointer
	lodsb
	jmp	.ch
.wbyte:	rep	stosb		; Write AL, CX times.
	mov	al,bl		; Move next character back into AL
	jmp	compil.ch	; Compile next command
	;;;	Compile BF input
chin:	mov	al,2Eh		; CS segment override
	stosb
	mov	ax,1EFFh	; CALL FAR PTR
	stosw
	mov	ax,8		; Pointer to input routine at address 8
	stosw
	jmp	compil		; Compile next command
	;;;	Compile BF output
chout:	mov	al,2Eh		; CS segment override
	stosb
	mov	ax,1EFFh	; CALL FAR PTR
	stosw
	mov	ax,4		; Pointer to output routine at address 4
	stosw
	jmp	compil
	;;;	Compile start of loop
loops:	cmp	word [si],5D2Dh	; Are the next two characters '-]'?
	je	.zero		; Then just set the cell to zero
	mov	ax,078Ah	; Otherwise, write out a real loop
	stosw			; ^- MOV AL,[BX]
	mov	ax,0C084h	; TEST AL,AL
	stosw
	mov	ax,0575h	; JNZ loop-body
	stosw
	mov	al,0B8h		; MOV AX,  (simulate absolute near jmp)
	stosb
	xor	ax,ax		; loop-end (we don't know it yet so 0)
	stosw
	mov	ax,0E0FFh	; JMP AX
	stosw
	push	di		; Store addr of loop body on stack 
	jmp	compil		; Compile next command
.zero:	mov	ax,07C6h	; MOV BYTE [BX],
	stosw
	xor	al,al		; 0
	stosb
	inc	si		; Move past -]
	inc	si
	jmp	compil		; Compile next command
	;;;	Compile end of loop
loope:	pop	bx		; Retrieve address of loop body from stack
	test	bx,bx 		; If it is zero, we've hit the top of stack
	jz	.ebrkt		; so the brackets aren't balanced.
	mov	ax,078Ah	; MOV AL,[BX]
	stosw
	mov	ax,0C084h	; TEST AL,AL
	stosw
	mov	ax,0574h	; JZ loop-end
	stosw
	mov	al,0B8h		; MOV AX, (simulate absolute near jmp)
	stosb
	mov	ax,bx		; loop-start
	stosw
	mov	ax,0E0FFh	; JMP AX
	stosw
	mov	[es:bx-4],di	; Store loop-end in matching loop start code
	jmp	compil
.ebrkt:	mov	dx,err.brk
	jmp	error
	;;;	Compilation is done. 
cdone:	mov	al,2Eh		; Code to jump to cleanup routine
	stosb			; ^- CS segment override
	mov	ax,2EFFh	; JMP FAR PTR 
	stosw
	pop	ax		; Should be zero if all loops closed 
	stosw
	test	ax,ax		; Were all loops closed?
	jz	.lp_ok
	mov	dx,err.brk	; If not, print error
	jmp	error
.lp_ok:	mov	[cs:cp],word 12	; Make far pointer to start of BF code
	mov	[cs:cp+2],bp	; (which starts at ES:0C = BP:0C)
	mov	ax,ds		; Set both DS and ES to BF tape segment
	mov	es,ax		; (also the initial source segment)
	xor	ax,ax		; Clear the tape (set all bytes to zero)
	mov	cx,32768	
	rep	stosw
	xor	bx,bx		; Tape begins at address 0
	xor	cx,cx		; No EOF and char buffer is empty
	jmp	far [cs:cp]	; Jump into the BF code
	;;;	BF program jumps here to stop the program
stop:	mov	ax,exit<<8|0	; Quit to DOS with return code 0
	int	21h
	;;;	Print error message in CS:DX and quit with errorlevel 2
error:	push	cs		; Set DS to CS
	pop	ds
	mov	ah,puts		; Print DS:DX
	int	21h
	mov	ax,exit<<8|2	; Quit to DOS
	int	21h
	;;;	Output subroutine called by the BF program (far call)
bfout:	mov	ah,putch	; Prepare to write character
	mov	dl,[bx] 	; Get character from tape
	cmp 	dl,10		; Is it LF?
	jne	.wr		; If not, just write it
	mov	dl,13		; Otherwise, print CR first,
	int	21h 
	mov	dl,10		; then LF.
.wr:	int	21h		; Write character
	retf
	;;;	Input subroutine called by the BF program (far call)
	;;;	Buffered input with CR/LF translation
	;;;	Note: this keeps state in registers!
	;;;	CL = chars left in buffer, CH = set if EOF seen,
	;;;	SI = buffer pointer, ES = BF data segment
bfin:	test	ch,ch		; EOF seen?
	jnz	.r_eof
	mov	ax,cs		; Set DS to our segment
	mov	ds,ax
.getch:	test	cl,cl		; Characters left in buffer?
	jnz	.retch		; If so, return next character
	mov	bp,bx		; Keep BF tape pointer
	mov	ah,read		; Read
	xor	bx,bx		; From STDIN
	mov	cx,255		; Max 255 characters
	mov	dx,ibuf		; Into the buffer
	int	21h
	mov	bx,bp		; Restore tape pointer
	jc	.ioerr		; If carry set, I/O error
	test	ax,ax		; If nothing returned, EOF
	jz	.s_eof
	mov	cx,ax		; Otherwise, set character count,
	mov	si,ibuf		; set buffer pointer back to start,
	jmp	.getch		; and return first character from buffer.
.s_eof:	inc	ch		; We've seen EOF now
.r_eof:	mov	al,EOFCH	; Return EOF
	jmp	.ret
.retch:	lodsb			; Get char from buffer
	dec	cl		; One fewer character left
	cmp	al,26		; ^Z = EOF when reading from keyboard
	je	.s_eof
	cmp	al,10		; If it is LF, ignore it and get another
	je	.getch
	cmp	al,13		; If it is CR, return LF instead
	jne	.ret
	mov	al,10
.ret:	mov	dx,es		; Set DS back to BF's data segment
	mov	ds,dx
	mov	[bx],al		; Put character on tape
	retf
.ioerr:	mov	dx,err.io	; Print I/O error and quit
	jmp	error
section	.data
bfchar:	db	'+-<>,.[]',0
err:	;;;	Error messages
.usage:	db	'BRAINFK PGM.B',13,10,10,9,'Run the BF program in PGM.B$'
.file:	db	'Cannot read file$'
.brk:	db	'Mismatched brackets$'
.mem:	db	'Out of memory$'
.io:	db	'I/O Error$'
section	.bss
cp:	resw	2		; Far pointer to start of BF code
ibuf:	resb	256		; 255 char input buffer
stack:	resw	512		; 512 words for the stack
.top:	equ	$