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.




	;;; 	CP/M Brainfuck compiler/interpreter, with a few optimizations
getch:	equ	1	; Read character from console
putch:	equ	2	; Print character to console
puts:	equ	9	; Print string to console
fopen:	equ	15	; Open file
fread:	equ	20	; Read from file
dmaoff:	equ	26	; Set DMA address
fcb:	equ	5Ch	; FCB for first command line argument
EOFCH:	equ	-1	; Value stored on the tape on EOF
	org	100h
	jmp	start
	;;;	Print the character on the tape, saving HL (tape location),
	;;;	and including CR/LF translation.
bfout:	push	h	; Keep tape location
	mov	a,m	; What are we printing?
	cpi	10	; Newline?
	jnz	outch	; If not, just print the character.
	mvi	e,13	; Otherwise, print a carriage return first.
	mvi	c,putch
	call	5
	pop	h	; Then get the tape back
	push	h
outch:	mov	e,m	; Print the character in A.
	mvi	c,putch
	call	5
	pop	h	; Restore tape location.
	ret
	;;;	Read a character and store it on the tape, including CR/LF
	;;;	translation; ^Z is EOF.
bfin:	push	h	; Keep tape location
	lda	bfeoff	; Have we seen EOF yet?
	ana	a
	jnz	bfeof	; If so, return EOF.
	mvi	c,getch	; Otherwise, read character
	call	5 
	cpi	26	; Was it EOF?
	jz	bfeof	; Then handle EOF.
	cpi	13	; Was it CR? (Pressing 'Enter' only gives CR.)
	jnz	bfin_s	; If not, just store the character.
	mvi	c,putch	; Otherwise, output a LF (only CR is echoed as well)
	mvi	e,10
	call	5
	mvi	a,10	; And then store a LF instead of the CR.
bfin_s:	pop	h	; Restore tape location
	mov	m,a	; Store the character
	ret
bfeof:	sta	bfeoff	; Set the EOF flag (A is nonzero here)
	pop	h	; Restore tape location
	mvi	m,EOFCH	; Store EOF return value.
	ret
bfeoff:	db	0	; EOF flag, EOF seen if nonzero.
	;;;	Print mismatched brackets error
brkerr:	lxi	d,ebrk
	;;;	Print error message under DE and quit
err:	mvi	c,puts	; Print string
	call	5
	rst	0	; Then quit
	;;;	Error messages.
efile:	db	'Cannot read file.$'
ebrk:	db	'Mismatched brackets.$'
	;;;	BF characters
bfchr:	db	'+-<>,.[]',26
	;;;	Main program
start:	lhld	6 	; Set stack pointer to highest available address
	sphl
	mvi	c,fopen	; Try to open the file given on the command line
	lxi	d,fcb
	call	5
	inr	a	; A=FF on error,
	lxi	d,efile	; so if we couldn't open the file, say so, and stop
	jz	err
	;;;	Read file into memory in its entirety
	lxi 	d,pgm	; Start of input
block:	mvi	c,dmaoff
	push	d	; Keep current address on stack 
	call	5	; Set DMA to location of current block
	mvi	c,fread	; Read 128-byte block to that address
	lxi	d,fcb
	call	5
	dcr	a	; A=1 = end of file
	jz	fdone
	inr	a	; Otherwise, A<>0 = error
	lxi	d,efile
	jnz	err
	pop	h	; Retrieve DMA address
	lxi	d,128	; Add 128 (advance to next block)
	dad	d
	xchg		; Put in DE
	jmp	block	; Go get next block.
fdone:	pop	h	; When done, find next address
	mvi	m,26	; Write EOF, so file always ends with EOF.
	;;;	Filter out all the non-BF characters
	lxi 	h,pgm	; Output pointer
	push	h 	; On stack
	lxi	b,pgm	; Input pointer	
filter:	ldax	b	; Get current character
	inx	b	; Look at next char next time
	lxi	h,bfchr	; Test against 9 brainfuck characters (8 + EOF)
	mvi	e,9
filchk:	cmp	m	; Is it a match?
	jz	filfnd	; Then we found it
	inx	h
	dcr	e
	jnz	filchk 
	jmp	filter	; Otherwise, try next character
filfnd:	pop	h	; Get pointer from stack
	mov	m,a	; Store current character
	inx	h	; Move pointer
	push	h	; Store pointer back on stack
	cpi	26	; Reached the end?
	jnz	filter	; If not, keep going.
	;;;	Move the program as high up into memory as possible.
	lxi	h,-1024	; Keep 1K stack space (allowing 512 levels of nested
	dad	sp	; loops)
	pop	d	; Source pointer in DE (destination in HL)
move:	ldax	d	; Copy backwards
	dcx	d
	mov	m,a
	dcx 	h
	ana	a	; Until zero is reached
	jnz	move
	inx	h	; Move pointer to byte after zero
	inx	h
	;;;	Compile the Brainfuck code into 8080 machine code
	lxi	b,0	; Push zero on stack (as boundary marker)
	push	b
	lxi 	d,pgm	; DE = start of binary area (HL at start of source)
compil:	mov	a,m	; Get source byte
	cpi	'+'	; Plus or minus - change the tape value 
	jz	tapval
	cpi	'-'
	jz	tapval
	cpi	'<'	; Left or right - move the tape
	jz	tapmov
	cpi	'>'
	jz 	tapmov
	cpi	'.'	; Input and output
	jz	chout
	cpi	','
	jz	chin
	cpi	'['	; Start of loop
	jz	loops	
	cpi	']'	; End of loop
	jz 	loope
	cpi	26	; EOF?
	jz	cdone
	inx	h	; Anything else is ignored
	jmp	compil
	;;;	Write code for '+' or '-' (change cell value)
tapval:	mvi	c,0	; C = change in value necessary
tapv_s:	mov	a,m	; Get current byte
	cpi	'+'	; If plus, 
	jz	tapinc	; Then we need to increment
	cpi	'-'	; If minus,
	jz	tapdec 	; Then we need to decrement
	;;;	The effect of the last instructions should be to
	;;;	change the cell at the tape head by C.
	;;;	If -3 <= B <= 3, INR M/DCR M are most efficient.
	;;;	Otherwise, MVI A,NN / ADD M / MOV M,A is most efficient.
	mov	a,c
	ana	a	; Zero?
	jz	compil	; Then we do nothing.
	cpi	4	; Larger than 3?
	jc	tapinr	; If not, 'INR M' * C
	cpi	-3	; Smaller than -3?
	jnc	tapdcr	; Then, 'DCR M' * -C
	xchg		; Otherwise, use an ADD instruction
	mvi	m,3Eh	; 'MVI A,'
	inx	h
	mov	m,c	; C (all math is mod 256)
	inx	h
	mvi	m,86h	; 'ADD M'
	inx	h
	mvi 	m,77h	; 'MOV M,A'
	inx	h
	xchg
	jmp	compil
tapinc:	inr	c	; '+' means one more
	inx	h	; Check next byte
	jmp	tapv_s
tapdec:	dcr	c	; '-' means one less
	inx	h	; Check next byte
	jmp	tapv_s
tapinr:	mvi	a,34h	; INR M (increment cell)
	jmp	wrbyte
tapdcr:	mvi	a,35h	; DCR M (decrement cell)
	jmp	wrnegc
	;;;	Write code for '<' or '>' (move tape head)
tapmov:	lxi	b,0	; BC = change in value necessary
tapm_s:	mov	a,m	; Get current byte
	cpi	'>'	; If right,
	jz	taprgt	; Then we need to move the tape right
	cpi	'<'	; If left,
	jz	taplft	; Then we need to move the tape left
	;;;	Move the tape by BC.
	;;;	If -4 <= BC <= 4, INX H/DCX H are most efficient.
	;;;	Otherwise, LXI B,NNNN / DAD B is most efficient.
	mov	a,b	; Is the displacement zero?
	ora	c
	jz	compil	; Then do nothing
	mov	a,b	; Otherwise, is the high byte 0?
	ana	a
	jnz	tbchi	; If not, it might be FF, but
	mov	a,c	; if so, is low byte <= 4?
	cpi	5
	jc	tapinx	; Then we need to write 'INX H' C times
	xra	a	; Otherwise, do it the long way
tbchi:	inr	a	; Is the high byte FF?
	jnz	tapwbc	; If not, we'll have to do it the long way
	mov	a,c	; But if so, is low byte >= -4?
	cpi	-4	
	jnc	tapdcx	; Then we can write 'DCX H' -C times
tapwbc:	xchg		; Otherwise, use a DAD instruction
	mvi	m,1h	; 'LXI B,'
	inx	h
	mov	m,c	; Low byte
	inx	h
	mov	m,b	; High byte
	inx	h
	mvi	m,9h	; 'DAD B'
	inx	h
	xchg
	jmp	compil
taprgt:	inx	b	; '>' is one to the right
	inx	h	; Check next byte
	jmp	tapm_s
taplft:	dcx	b	; '<' is one to the left
	inx	h	; Check next byte
	jmp	tapm_s
tapinx:	mvi	a,23h	; INX H (move tape right)
	jmp	wrbyte
tapdcx:	mvi	a,2Bh	; DCX H (move tape left)
	jmp	wrnegc
	;;;	Write the byte in A, -C times, to [DE++]
wrnegc: mov	b,a	; Keep A
	mov	a,c	; Negate C
	cma
	inr	a
	mov	c,a
	mov	a,b
	;;;	Write the byte in A, C times, to [DE++]
wrbyte:	stax	d
	inx	d
	dcr	c
	jnz	wrbyte
	jmp	compil
	;;;	Write code to print the current tape value
chout:	inx	h	; We know the cmd is '.', so skip it
	lxi	b,bfout	; Call the output routine
	jmp	wrcall
	;;;	Write code to read a character and store it on the tape
chin:	inx	h	; We know the cmd is ',', so skip it
	lxi	b,bfin
	;;;	Write code to CALL the routine with address BC
wrcall:	xchg
	mvi	m,0CDh	; CALL
	inx	h
	mov	m,c	; Low byte
	inx	h
	mov	m,b	; High byte
	inx	h
	xchg
	jmp	compil
	;;;	Write code to start a loop
loops:	inx	h	; We know the first cmd is '['
	mov	b,h	; Check for '-]'
	mov	c,l
	ldax	b
	cpi	'-'	
	jnz	loopsw	; If not '-', it's a real loop
	inx	b
	ldax 	b
	cpi	']'
	jz	lzero	; If ']', we just need to set the cell to 0
	;;;	Code for loop: MOV A,M / ANA A / JZ cmd-past-loop
loopsw:	xchg		; Destination pointer in HL
	mvi	m,7Eh	; MOV A,M
	inx	h
	mvi	m,0A7h	; ANA A
	inx	h
	mvi	m,0CAh	; JZ
	inx	h
	inx	h	; Advance past where the destination will go
	inx	h	; (End of loop will fill it in)
	push	h 	; Store the address to jump back to on the stack
	xchg
	jmp	compil
	;;;	Code to set a cell to zero in one go: MVI M,0
lzero:	inx	h	; Move past '-]'
	inx	h
	xchg		; Destination pointer in HL
	mvi	m,36h	; MVI M,
	inx	h
	mvi	m,0	; 0
	inx	h
	xchg
	jmp	compil
	;;;	Write code to end a loop: MOV A,M / ANA A / JNZ loop-start
loope:	inx	h	; We know the first cmd is ']'
	xchg		; Destination pointer in HL
	mvi	m,7Eh	; MOV A,M
	inx	h
	mvi	m,0A7h	; ANA A
	inx	h
	mvi	m,0C2h	; JNZ
	inx	h
	pop	b	; Get loop-start from the stack
	mov	a,b	; If it is 0, we've hit the sentinel, which means
	ora	c	; mismatched brackets
	jz	brkerr
	mov	m,c	; Store loop-start, low byte first,
	inx	h
	mov	m,b	; then high byte.
	inx	h
	dcx	b	; The two bytes before loop-start must be filled in
	mov	a,h	; with the address of the cmd past the loop, high
	stax	b	; byte last,
	dcx	b
	mov	a,l	; then low byte
	stax 	b
	xchg	
	jmp	compil
	;;;	Done: finish the code with a RST 0 to end the program
cdone:	xchg
	mvi	m,0C7h
	pop 	b 	; If the brackets are all matched, there should be
	mov	a,b	; a zero on the stack.
	ora	c
	jnz	brkerr
	;;;	Initialize the tape. The fastest way to fill up memory on the
	;;;	8080 is to push values to the stack, so we will fill it up
	;;;	with zeroes, and position the tape there.
	;;;	HL contains the top of the program.
	lxi	d,32	; The Brainfuck program doesn't use the stack, so
	dad	d	; reserving 16 levels for CP/M is more than enough.
	mov	a,l	; Complement the value (almost negation, but low bit
	cma		; doesn't really matter here)
	mov	l,a
	mov	a,h
	cma
	mov	h,a
	dad	sp	; Add the current stack pointer, giving bytes to fill
	ana	a	; Zero carry
	mov	a,h	; Divide value by two (we push words)
	rar
	mov	h,a
	mov	a,l
	rar
	mov	l,a
	lxi	d,0
ztape:	push	d	; Zero out the tape (on the stack)
	dcx	h
	mov	a,h
	ora	l
	jnz	ztape 
	dad	sp	; HL is now 0, add SP to get tape bottom
	;;;	The compiled program is stored after this point, so we just
	;;;	fall through into it.
	nop		; No-op (sentinel value)
pgm:	equ	$	; Compiled BF program stored here.