// lib:
// serial.dsa
//
// usage:
//   include serial "<relative path>"
//
//   push (register containing address of string)
//   call serial::print
//
//   push (register containing byte)
//   call serial::print_byte

dw uart: 0x40000

// ------------------------------------------
// transmit a single byte via sentinel protocol
// expects: byte in rg0, trashes rg2, rg3, rg4, rg5
_serial_send:
    shl rg0, 16, rg2        // shift byte to bits [23:16]
    lli 0x01, rg0
    shl rg0, 24, rg0        // valid flag in bits [31:24]
    or rg0, rg2, rg2        // rg2 = 0x01_XX_0000

    ldw uart, rg5           // rg5 = 0x40000 (uart address, held for store)

_serial_wait:
    ldw rg5, rg0            // poll mem[0x40000]
    shr rg0, 24, rg0
    lli 0x01, rg3
    ieq rg0, rg3, rg4
    jnz rg4, _serial_wait   // loop while valid == 1

    stw rg2, rg5            // mem[0x40000] = sentinel word
    ret

// ------------------------------------------
// prints the null-terminated string at addr(arg[0])
print: func
    ldw bpr, rg0, 8         // load string address from arg

_serial_print_loop:
    ldb rg0, rg2            // load byte
    ieq rg2, zero, rg4      // null terminator?
    jnz rg4, _serial_end

    push rg0                // save string pointer
    mov rg2, rg0
    call _serial_send
    pop rg0

    addi rg0, 1
    jmp _serial_print_loop

// ------------------------------------------
// prints the last byte of arg[0]
print_byte: func
    ldw bpr, rg0, 8
    call _serial_send
    jmp _serial_end

// ------------------------------------------
_serial_end:
    return
