Skip to content

Instantly share code, notes, and snippets.

@Measter
Last active August 29, 2015 14:23
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Measter/49a7e80de80c1c7d9758 to your computer and use it in GitHub Desktop.
Save Measter/49a7e80de80c1c7d9758 to your computer and use it in GitHub Desktop.
Brainfuck Assembler
*-----------------------------------------------------------
* Title : Brainfuck Assembler
* Written by : Stuart Haidon
* Date : 2015-06-16
* Versions:
* 1.0: Release
* 1.1: Changed JMP instruction in assembled program to RTS.
* 1.2: Now handles mis-matched brackets.
*-----------------------------------------------------------
ORG $1000
readlen EQU $100
*------------------------
* Subroutines
* Input
* D6 : Character to test.
* Output
* D7 : 0 if invalid.
is_valid:
move.l #1,d7
cmp.b #'>', d6
beq is_valid_exit
cmp.b #'<', d6
beq is_valid_exit
cmp.b #'-', d6
beq is_valid_exit
cmp.b #'+', d6
beq is_valid_exit
cmp.b #'.', d6
beq is_valid_exit
cmp.b #',', d6
beq is_valid_exit
cmp.b #'[', d6
beq is_valid_exit
cmp.b #']', d6
beq is_valid_exit
move.l #0,d7
is_valid_exit:
rts
* Zeroes the first 128kb of program memory. Memory pointer in A6.
zero_mem:
move.l #$8000, d0
zero_mem_start:
move.l #0,-4(a6,d0.l)
sub.l #4, d0
tst d0
bne zero_mem_start
rts
START: ; first instruction of program
*------------------------
* Put program code here
* Close existing handles. Just in case.
move.l #50, d0
trap #15
* Get filename.
move.l #0, a1
lea extensions, a2
lea fname_buffer, a3
move.l #58, d0 ; Open/save file dialog.
move.l #0, d1 ; Open file
trap #15
tst d1
beq end * User clicked cancel or closed the dialog. Exit.
* Start assembling the program.
* Open file
lea fname_buffer, a1
move.l #51, d0
trap #15 * D1 = File ID.
program_assemble:
lea program, a5 * A5 = Program output address.
lea read_buffer, a1 * A1 = Buffer address.
REPEAT
* Read 256 bytes into buffer.
move.l #53, d0 * Read file.
move.l #readlen, d2
trap #15 * D2 = Number of bytes read.
* Read and assemble brainfuck.
move.l #0, d3 * D3 = Buffer read offset pointer.
REPEAT
move.b (a1,d3.l), d6 * D6 = Read character.
jsr is_valid
tst d7
beq next_iter * Invalid character. Skip to next.
IF.b d6 <EQ> #'>' THEN.s
move.w #$528E, (a5)+ * add.l #1, A6
ENDI
IF.b d6 <EQ> #'<' THEN.s
move.w #$538E, (a5)+ * sub.l #1, A6
ENDI
IF.b d6 <EQ> #'+' THEN.s
move.w #$5216, (a5)+ * add.b #1, (A6)
ENDI
IF.b d6 <EQ> #'-' THEN.s
move.w #$5316, (a5)+ * sub.b #1, (A6)
ENDI
IF.b d6 <EQ> #'.' THEN.s
move.w #$7006, (a5)+ * move.l #6, d0
move.w #$1216, (a5)+ * move.b (a6), d1
move.w #$4E4F, (a5)+ * trap #15
ENDI
IF.b d6 <EQ> #',' THEN.s
move.w #$7005, (a5)+ * move.l #5, d0
move.w #$4E4F, (a5)+ * trap #15
move.w #$1C81, (a5)+ * move.b d1, (a6)
ENDI
IF.b d6 <EQ> #'[' THEN.s
move.w #$4A16, (a5)+ * tst (a6)
move.w #$6600, (a5)+ * bne
move.w #$0008, (a5)+ * Address for bne
move.w #$4ED5, (a5)+ * Jump to (a5) Is for handling mismatched bracket, and will be corrected to 4EF9 later.
*move.w #$4EF9, (a5)+ * jmp to following address.
* Push address to destination of current [.
move.l a5, -(a7)
move.w #$0000, (a5)+
move.w #$0000, (a5)+ * Address to matching ]
ENDI
IF.b d6 <EQ> #']' THEN.s
move.l a5,d5 * D5 = Address of current ] before writing the instruction.
* Write current instruction.
move.w #$4A16, (a5)+ * tst (a6)
move.w #$6700, (a5)+ * beq
move.w #$0008, (a5)+ * Address for beq
* Test for mis-matched bracket.
IF.l a7 <NE> #$1000000 THEN.s
move.l (a7)+, a3 * A3 = Address to matching [
move.w d5, 2(a3)
swap d5
move.w d5, 0(a3)
* Correct the jump opcode
move.w #$4EF9, -2(a3)
* Write jump opcode for current bracket.
move.w #$4EF9, (a5)+ * jmp
* Write matching return address.
move.l a3, d5
sub.l #8, d5
swap d5
move.w d5, (a5)+
swap d5
move.w d5, (a5)+
ELSE.s
* Write return error opcode.
move.w #$4ED5, (a5)+
ENDI
ENDI
next_iter:
add.w #1, d3
UNTIL.w d3 <EQ> d2 DO.l * Read untill reached the end of read data.
UNTIL.l d2 <NE> #readlen DO.l * Reached end of file.
* Exit program.
move.w #$4E75, (a5)+ * rts
* Close file.
move.l #56, d0
trap #15
* Zero memory, setup memory pointer, and jump to program.
lea program_memory, a6
* Write message.
move.l #13, d0
lea zero_mem_message, a1
trap #15
jsr zero_mem
* Clear screen.
move.l #11, d0
move.l #$ff00, d1
trap #15
* Set error pointer address.
lea program_error, a5
jsr program
* Program will jump here when finished.
prog_finish:
move.l #13, d0
lea finish_text, a1
trap #15 * Display string.
prog_finish_input_loop:
move.l #5, d0
trap #15
cmp.b #'y',d1
beq prog_finish_clear_restart
cmp.b #'n',d1
beq end
move.l #13,d0
lea finish_text_invalid, a1
trap #15
jmp prog_finish_input_loop
prog_finish_clear_restart:
move.l #11, d0
move.l #$ff00, d1
trap #15 * Clear screen.
jmp start
end:
SIMHALT ; halt simulator
program_error:
move.l #13,d0
lea mismatch_bracket, a1
trap #15
rts
*------------------------
* Put variables and constants here
zero_mem_message: dc.b 'Zeroing Memory',0
finish_text: dc.b $d,$a,'Program finished. Load another? (y or n)',0
finish_text_invalid: dc.b $d,$a,'Invalid Input',$d,$a,0
mismatch_bracket: dc.b $d,$a,'Encountered a program error. Exiting.',0
extensions: dc.b '*.b;*.bf',0
fname_buffer: dcb.b $100,0
read_buffer: dcb.b readlen,0
ORG $10000
program:
ORG $100000
program_memory:
END START ; last line of source
@timmattison
Copy link

I would update the first block comment to say "Assembler" instead of "Interpreter". It looks like it really is assembling the code and then running it as bare metal opcodes.

BTW, I haven't done any assembly in a long time and this is still pretty readable to me. Nice work! Do you think you could include a comment with the command-line you use to assemble this?

@Measter
Copy link
Author

Measter commented Jun 17, 2015

Thank you. I did originally intend to write an interpreter, hence the title in the file, but changed my mind when I realised I would have to search along the brainfuck code for the matching bracket. I figured it would be easier to assemble it to raw opcodes and just jump to it.

I used the Easy68k assembler/emulator to compile and run it, so it uses some emulator-specific functions, such as the open file dialog and file reading (Lines 66 to 96).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment