// a simple brainf##k interpreter, // because I already wrote a compiler lol. include print "./print.dsa" // "prints first 16 fibonacci numbers" db program: "++++++++++++++++++++++++++++++++++++++++++++ >++++++++++++++++++++++++++++++++ >++++++++++++++++ > >+ << [ >> > >++++++++++ << [->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<] >[<+>-] >[-] >> >++++++++++ < [->-[>+>>]>[+[-<+>]>+>>]<<<<<] >[-] >>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] <[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] <<<++++++++++++++++++++++++++++++++++++++++++++++++.[-] <<<<<<<.>. >>[>>+<<-] >[>+<<+>-] >[<+>-] <<<- ] <<++..." db error: "Invalid Instruction!" dh counter: 0xFF dw stack: 0x10000 dw input_addr: 0x30000 resb data: 1024 // set up a stack so we can call functions _init: ldw stack, bpr mov bpr, spr init: ldw counter, rg7 start: call bf_reset // load the start of the program into rg0 lwi program, rg0 lwi data, rg1 // rg0 is our instruction pointer // rg1 is our data pointer // rg2 is the value at the data pointer // rg3 stores the current instruction // rg4 is the expression nesting level. lli 43, rg8 // + = 43 increment lli 45, rg9 // - = 45 decrement lli 62, rga // > = 62 increment pointer lli 60, rgb // < = 60 decrement pointer lli 46, rgc // . = 46 output lli 44, rgd // , = 44 input lli 91, rge // [ = 91 loop start lli 93, rgf // ] = 93 loop end loop_start: // load the current instruction into rg3 ldb rg0, rg3 // switch on the instruction ieq rg3, rg8, rg4 jnz rg4, increment ieq rg3, rg9, rg4 jnz rg4, decrement ieq rg3, rga, rg4 jnz rg4, inc_ptr ieq rg3, rgb, rg4 jnz rg4, dec_ptr ieq rg3, rgc, rg4 jnz rg4, output ieq rg3, rgd, rg4 jnz rg4, input ieq rg3, rge, rg4 jnz rg4, expr_start ieq rg3, rgf, rg4 jnz rg4, expr_end ieq rg3, zero, rg4 jnz rg4, end // if we get here, we don't know what the instruction is lwi error, rg2 push rg0 push rg1 push rg2 call print::print pop zero pop rg1 pop rg0 db success: "Success!" end: call print::newline lwi success, rg0 push rg0 call print::print pop zero subi rg7, 1 jnz rg7, start hlt loop_end: addi rg0, 1, rg0 jmp loop_start // ------------------------------------------ // increment the current cell increment: addi rg2, 1, rg2 jmp loop_end // ------------------------------------------ // decrement the current cell decrement: subi rg2, 1, rg2 jmp loop_end // ------------------------------------------ // increment the pointer inc_ptr: stw rg2, rg1 addi rg1, 4 ldw rg1, rg2 jmp loop_end // ------------------------------------------ // decrement the pointer dec_ptr: stw rg2, rg1 subi rg1, 4 ldw rg1, rg2 jmp loop_end // ------------------------------------------ // print the byte in the current cell output: push rg0 push rg1 push rg2 call print::print_byte pop zero pop rg1 pop rg0 jmp loop_end // ------------------------------------------ // read a byte into the current cell input: ldw input_addr, rg2 jmp loop_end // ------------------------------------------ // handle an open bracket instruction expr_start: ieq rg2, zero, rg4 jez rg4, loop_end _traverse_right_start: // push a register that definitely has a nonzero value // when we pop this value from the stack // we know we've finished traversing. push rg8 _traverse_right: addi rg0, 1, rg0 ldb rg0, rg3 ieq rg3, rge, rg4 jnz rg4, open_right ieq rg3, rgf, rg4 jnz rg4, close_right ieq rg3, zero, rg4 jnz rg4, end jmp _traverse_right open_right: // push zero to the stack push zero jmp _traverse_right close_right: // check if we've reached the bottom of the stack pop rg4 ieq rg4, zero, rg5 jnz rg5, _traverse_right // go to next instruction after closing bracket addi rg0, 1, rg0 jmp loop_start // ------------------------------------------ // handle the close bracket instruction expr_end: ieq rg2, zero, rg4 jnz rg4, loop_end _traverse_left_start: push rg8 _traverse_left: subi rg0, 1, rg0 ldb rg0, rg3 ieq rg3, rge, rg4 jnz rg4, open_left ieq rg3, rgf, rg4 jnz rg4, close_left ieq rg3, zero, rg4 jnz rg4, end jmp _traverse_left open_left: // check if we've reached the bottom of the stack pop rg4 ieq rg4, zero, rg5 jnz rg5, _traverse_left // go to next instruction after open bracket addi rg0, 1, rg0 jmp loop_start close_left: // push zero to the stack push zero jmp _traverse_left // ------------------------------------------ // reset interpreter // clears screen, resets cursor and clears data buffer. bf_reset: func push rg0 push rg1 // clear screen and reset cursor call print::clear call print::reset // clear data buffer (resb data: 1024 = 256 words) lli 256, rg0 lwi data, rg1 _bf_reset_clear_data: subi rg0, 1, rg0 stw zero, rg1 addi rg1, 4, rg1 igt rg0, zero, rg4 jnz rg4, _bf_reset_clear_data pop rg1 pop rg0 // reload interpreter state lwi program, rg0 lwi data, rg1 lli 0, rg2 return