// a simple brainf##k interpreter, // because I already wrote a compiler lol. include print "./print.dsa" // "print hello world" db program: "++++++++++++++++++++++++++++++++++++++++++++ >++++++++++++++++++++++++++++++++ >++++++++++++++++ > >+ << [ >> > >++++++++++ << [->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<] >[<+>-] >[-] >> >++++++++++ < [->-[>+>>]>[+[-<+>]>+>>]<<<<<] >[-] >>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] <[++++++++++++++++++++++++++++++++++++++++++++++++.[-]] <<<++++++++++++++++++++++++++++++++++++++++++++++++.[-] <<<<<<<.>. >>[>>+<<-] >[>+<<+>-] >[<+>-] <<<- ] <<++..." db error: "Invalid Instruction!" dw stack: 0x10000 dw input: 0x30000 resb data: 1024 // set up a stack so we can call functions _init_stack: ldw stack, bpr mov bpr, spr start: // 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 end: 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, 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