// lib:
// print_serial.dsa

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

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

dw serial: 0x207D0      // 0x20000 + 2000

// ------------------------------------------
// prints the string at addr(arg[0]) to the serial port.
print:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    lwi 0x207D0, rg1

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

    addi rg0, 1
    jmp _print_loop

// ------------------------------------------
// prints the string at addr(arg[0]) followed by a newline to the serial port.
println:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    lwi 0x207D0, rg1

_println_loop:
    ldb rg0, acc
    cmp acc, zero
    jeq _println_end
    stb acc, rg1

    addi rg0, 1
    jmp _println_loop

_println_end:
    lli 0x0A, rg2       // newline character
    stb rg2, rg1
    jmp _end

// ------------------------------------------
// prints the word in arg[0] as 4 raw bytes to the serial port.
print_word:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    lwi 0x207D0, rg1

    stb rg0, rg1
    shr rg0, 8
    stb rg0, rg1
    shr rg0, 8
    stb rg0, rg1
    shr rg0, 8
    stb rg0, rg1
    jmp _end

// ------------------------------------------
// prints the last byte of arg[0] to the serial port.
print_byte:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    lwi 0x207D0, rg1

    stb rg0, rg1
    jmp _end

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

    lwi 0x207D0, 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 serial port in hex.
print_hex_byte:
    push bpr
    mov spr, bpr

    ldw bpr, rg0, 8
    lwi 0x207D0, 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
    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
    return

_print_hex_nibble_number:
    addi rg0, 0x30, rg0
    stb rg0, rg1
    return

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

    ldw bpr, rg0, 8
    lli 0, rg5

    cmp rg0, zero
    jne _print_num_extract_digits

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

_print_num_extract_digits:
    cmp rg0, zero
    jeq _print_num_output

    push rg0
    lli 10, rg1
    push rg1
    call maths::divmod
    pop rg0
    pop rg1

    addi rg1, 0x30, rg6
    push rg6
    inc rg5

    jmp _print_num_extract_digits

_print_num_output:
    lwi 0x207D0, rg1

_print_num_output_loop:
    cmp rg5, zero
    jeq _print_num_done

    pop rg6
    stb rg6, rg1
    dec rg5

    jmp _print_num_output_loop

_print_num_done:
    // fall through to _end

// ------------------------------------------
// return
_end:
    mov bpr, spr
    pop bpr
    return
