Last active
August 29, 2015 14:23
-
-
Save Measter/49a7e80de80c1c7d9758 to your computer and use it in GitHub Desktop.
Brainfuck Assembler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*----------------------------------------------------------- | |
* 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 | |
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
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?