// lib:
// print.dsa

// usage:
//
// include print "<relative path>"
//
// usage for print:
//      push (register containing address of string)
//      call print::print
//
// usage for reset:
//      call print::reset
//
// usage for clear:
//      call print::clear
//
// usage for print_byte:
//      push (register containing byte)
//      call print::print_byte
//
// usage for print_word:
//      push (register containing word)
//      call print::print_word
//
// usage for print_num:
//      push (register containing number to print in decimal)
//      call print::print_num
//

dw display: 0x20000
dw current: 0x20000

// ------------------------------------------
// prints the string at addr(arg[0]) to the screen.
print: func
    push rg0
    push rg1
    push rg2
    push rg3

    ldw bpr, rg0, 8
    ldw current, rg1

_print_loop:
    ldb rg0, rg2
    ieq rg2, zero, rg3
    jnz rg3, _print_end
    stb rg2, rg1

    addi rg0, 1
    addi rg1, 1

    jmp _print_loop

_print_end:
    stw rg1, current

    pop rg3
    pop rg2
    pop rg1
    pop rg0
    return

// ------------------------------------------
// println:
//     push bpr
//     mov spr, bpr

//     ldw bpr, rg0, 8
//     ldw current, rg1

// _println_loop:
//     ldb rg0, acc
//     ieq acc, zero, rg4
//     jnz rg4, _println_end
//     stb acc, rg1

//     addi rg0, 1
//     addi rg1, 1

//     jmp _println_loop

// _println_end:
//     call print_newline
//     jmp _end

// ------------------------------------------
// prints the value of arg[0] to the screen.
// print_word:
//     push bpr
//     mov spr, bpr

//     ldw bpr, rg0, 8
//     ldw current, rg1

//     addi rg1, 3

//     stb rg0, rg1
//     subi rg1, 1
//     shr rg0, 8
//     stb rg0, rg1
//     subi rg1, 1
//     shr rg0, 8
//     stb rg0, rg1
//     subi rg1, 1
//     shr rg0, 8
//     stb rg0, rg1

//     addi rg1, 4
//     jmp _end

// ------------------------------------------
// prints the last byte of arg[0] to the screen.
print_byte: func
    push rg0
    push rg1

    ldw bpr, rg0, 8
    ldw current, rg1

    stb rg0, rg1
    addi rg1, 1
    stw rg1, current

    pop rg1
    pop rg0
    return

// ------------------------------------------
// prints the value of arg[0] to the screen in hex.
// print_hex_word:
//     push bpr
//     mov spr, bpr

//     ldw current, rg1

//     ldb bpr, rg0, 8
//     push rg0
//     call _print_hex_byte
//     addi spr, 4

//     ldb bpr, rg0, 9
//     push rg0
//     call _print_hex_byte
//     addi spr, 4

//     ldb bpr, rg0, 10
//     push rg0
//     call _print_hex_byte
//     addi spr, 4

//     ldb bpr, rg0, 11
//     push rg0
//     call _print_hex_byte
//     addi spr, 4

//     jmp _end

// ------------------------------------------
// prints the last byte of arg[0] to the screen in hex.
print_hex_byte: func
    ldw bpr, rg0, 8
    ldw current, rg1

    call _print_hex_byte
    jmp _end

// function body
_print_hex_byte:
    lli 0xF, rg2
    push rg0

    shr rg0, 4
    and rg0, rg2, rg0
    call _print_hex_nibble
    pop rg0

    and rg0, rg2, rg0
    call _print_hex_nibble
    ret

// print a hex digit
_print_hex_nibble:
    lli 10, rg3
    ilt rg0, rg3, rg4
    jnz rg4, _print_hex_nibble_number
    addi rg0, 0x37, rg0
    stb rg0, rg1
    addi rg1, 1
    ret

// helper function.
_print_hex_nibble_number:
    addi rg0, 0x30, rg0
    stb rg0, rg1
    addi rg1, 1
    ret

// ------------------------------------------
// print whitespace
print_whitespace: func
    ldw current, rg1
    lli 0x20, rg0
    stb rg0, rg1
    addi rg1, 1
    jmp _end

// ------------------------------------------
// print newline
// print_newline:
//     push bpr
//     mov spr, bpr

//     ldw display, rg0
//     ldw current, rg1

//     sub rg1, rg0, rg0

//     lwi 80, rg2
//     push rg0
//     push rg1
//     push rg2
//     push rg0
//     push rg2
//     call maths::divmod
//     pop zero   // result
//     pop rg3    // remainder
//     pop rg0
//     pop rg1
//     pop rg2

//     sub rg1, rg3, rg2
//     addi rg2, 80, rg1

//     jmp _end

newline:
    push bpr
    mov spr, bpr

    ldw display, rg0
    ldw current, rg1

    sub rg1, rg0, rg2   // offset from base
    lli 80, rg3

_newline_mod:
    ilt rg2, rg3, rg4
    jnz rg4, _newline_done
    sub rg2, rg3, rg2
    jmp _newline_mod

_newline_done:
    // rg2 = offset % 80 (position within current line)
    sub rg1, rg2, rg1   // go back to line start
    addi rg1, 80, rg1   // advance to next line

    jmp _end

// ------------------------------------------
// prints arg[0] as a decimal number to the screen.
// print_num:
//     push bpr
//     mov spr, bpr

//     ldw bpr, rg0, 8
//     lli 0, rg5

//     ieq rg0, zero, rg4
//     jez rg4, _print_num_extract_digits

//     lli 0x30, rg6
//     push rg6
//     lli 1, rg5
//     jmp _print_num_output

// _print_num_extract_digits:
//     ieq rg0, zero, rg4
//     jnz rg4, _print_num_output

//     push rg0
//     lli 10, rg1
//     push rg1
//     call maths::divmod
//     pop rg0   // quotient
//     pop rg1   // remainder

//     addi rg1, 0x30, rg6
//     push rg6
//     inc rg5

//     jmp _print_num_extract_digits

// _print_num_output:
//     ldw current, rg1

// _print_num_output_loop:
//     ieq rg5, zero, rg4
//     jnz rg4, _print_num_done

//     pop rg6
//     stb rg6, rg1
//     addi rg1, 1
//     dec rg5

//     jmp _print_num_output_loop

// _print_num_done:
//     jmp _end

// ------------------------------------------
// resets the cursor position to 0x20000 (0,0)
reset: func
    ldw display, rg1
    jmp _end

// ------------------------------------------
// clears the screen
clear: func
    lli 500, rg0
    ldw display, rg1

_clear_loop:
    subi rg0, 1, rg0
    stw zero, rg1
    addi rg1, 4
    igt rg0, zero, rg4
    jnz rg4, _clear_loop
    jmp _end

// ------------------------------------------
// return — saves current, restores frame, returns
_end:
    stw rg1, current
    return
