// lib:
// print.dsa

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

include maths "../maths/core.dsa"

dw display: 0x20000
dw current: 0x20000

// ------------------------------------------
// prints the string at addr(arg[0]) to the screen. (no trailing whitespace unless explicitly provided)
print:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    ldw current, rg1

_print_loop:
	ldb rg0, acc
	cmp acc, zero
	jeq _end
	stb acc, rg1

	addi rg0, 1
	addi rg1, 1

	jmp _print_loop

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

    ldw bpr, rg0, 8
    ldw current, rg1

_println_loop:
	ldb rg0, acc
	cmp acc, zero
	jeq _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:
    // initialise
    push bpr
    mov spr, bpr

    // load byte into acc
    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:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    ldw current, rg1

    stb rg0, rg1
    addi rg1, 1
    jmp _end

// ------------------------------------------
// 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:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    ldw current, rg1

    call _print_hex_byte
    jmp _end

// function body
_print_hex_byte:
    // mask to get lower nibble
    lli 0xF, rg2
    // save rg0 state
    push rg0

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

    and rg0, rg2, rg0
    call _print_hex_nibble
    return

// print a hex digit
_print_hex_nibble:
    lli 10, rg3
    cmp rg0, rg3
    jlt _print_hex_nibble_number
    addi rg0, 0x37, rg0
    stb rg0, rg1
    addi rg1, 1
    return

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

// ------------------------------------------
// print whitespace
print_whitespace:
    push bpr
    mov spr, bpr

    ldw current, rg1
    lli 0x20, rg0
    stb rg0, rg1
    addi rg1, 1
    jmp _end

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

    // load variables into registers
    ldw display, rg0
    ldw current, rg1

    // get the offset from the display base
    sub rg1, rg0, rg0

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

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

    // _end saves the display state
    jmp _end

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

    ldw bpr, rg0, 8      // load number to print
    lli 0, rg5           // rg5 = digit counter

    // check if number is zero
    cmp rg0, zero
    jne _print_num_extract_digits

    // special case: print '0' for zero
    lli 0x30, rg6
    push rg6             // push digit to stack buffer
    lli 1, rg5           // we have 1 digit
    jmp _print_num_output

_print_num_extract_digits:
    // divide by 10 repeatedly to get digits
    cmp rg0, zero
    jeq _print_num_output

    // call divmod(rg0, 10)
    push rg0             // dividend
    lli 10, rg1
    push rg1             // divisor (10)
    call maths::divmod
    pop rg0              // quotient (continue dividing this)
    pop rg1              // remainder (the digit)

    // convert digit to ASCII and push to stack buffer
    addi rg1, 0x30, rg6  // convert to ASCII
    push rg6             // push digit to stack
    inc rg5              // increment digit counter

    jmp _print_num_extract_digits

_print_num_output:
    // now print digits (pop them off in reverse order)
    ldw current, rg1     // get display pointer

_print_num_output_loop:
    // check if we've printed all digits
    cmp rg5, zero
    jeq _print_num_done

    // pop digit and print it
    pop rg6
    stb rg6, rg1
    addi rg1, 1
    dec rg5

    jmp _print_num_output_loop

_print_num_done:
    jmp _end

// ------------------------------------------
// resets the cursor position on the screen to 0x20000. (0,0)
reset:
    push bpr
    mov spr, bpr
    ldw display, rg1
    jmp _end

// ------------------------------------------
// clears the screen
clear:
    push bpr
    mov spr, bpr
    // display size = 2000 bytes / 500 words
    lli 500 rg0
    ldw display, rg1

_clear_loop:
    dec rg0
    stw zero, rg1
    addi rg1, 4
    cmp rg0, zero
    jgt _clear_loop
    jmp _end

// ------------------------------------------
// return
_end:
    stw rg1, current

    mov bpr, spr
    pop bpr
    return
