// 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
