// Serial port address in MMIO
dw UART: 0x40000

// Reserve space for our read buffer
resb SERIAL_BUFFER: 1025 // final byte is a null terminator
dw SER_READ_PTR: 0
dw SER_WRITE_PTR: 0

reset: func
    push rg0

    lwi SERIAL_BUFFER, rg0
    stw rg0, SER_READ_PTR, 0
	stw rg0, SER_WRITE_PTR, 0

	pop rg0
    return

isr:
	push rg0
	push rg1
	push rg2

    ldw UART, rg0
    ldb rg0, rg1, 2 // load ser_in_valid flag

    // no serial input
    jez rg1, _isr_end

    // we should have a valid byte to read at offset 3
    ldb rg0, rg1, 3 // data byte
    stb zero, rg0, 2 // unset ser_in_valid

	// write data to ptr and increment
    ldw SER_WRITE_PTR, rg2
	stb rg1, rg2
	addi rg2, 1

	// load buff+1024
    lwi SERIAL_BUFFER, rg0
	addi rg0, 1024, rg1

	// if ptr != buff+1024, nowrap.
	ine rg1, rg2, rg1
	jnz rg1, _isr_nowrap

	lwi SERIAL_BUFFER, rg2 // set the pointer

_isr_nowrap:
    stw rg2, SER_WRITE_PTR

_isr_end:
	pop rg2
	pop rg1
	pop rg0
    iret

// serial_readline: waits until a null byte is received
// returns nothing, data is in SERIAL_BUFFER starting at SER_READ_PTR
readline: func
    push rg0
    push rg1
_readline_wait:
    // check if write ptr == read ptr (buffer empty)
    ldw SER_WRITE_PTR, rg0
    ldw SER_READ_PTR, rg1
    ieq rg0, rg1, rg0
    jnz rg0, _readline_halt  // nothing written yet, halt

    // check if the byte just before write ptr is null
    ldw SER_WRITE_PTR, rg0
    // handle wrap: if write ptr == SERIAL_BUFFER, check last byte of buffer
    lwi SERIAL_BUFFER, rg1
    ieq rg0, rg1, rg1
    jnz rg1, _readline_check_wrap

    // normal case: read byte at write_ptr - 1
    subi rg0, 1, rg0
    ldb rg0, rg1, 0
    jez rg1, _readline_done
    jmp _readline_halt

_readline_check_wrap:
    // write ptr is at start of buffer, last byte is at SERIAL_BUFFER + 1023
    lwi SERIAL_BUFFER, rg0
    addi rg0, 1023, rg0
    ldb rg0, rg1, 0
    jez rg1, _readline_done

_readline_halt:
    hlt
    jmp _readline_wait

_readline_done:
    pop rg1
    pop rg0
    return

// _serial_send: trashes rg2, rg3, rg4, rg5
// byte to send in rg0
_serial_send:
    ldw UART, rg5
_serial_wait:
    ldb rg5, rg2, 0
    lli 0x01, rg3
    ieq rg2, rg3, rg4
    jnz rg4, _serial_wait
    stb rg0, rg5, 1
    stb rg3, rg5, 0         // rg3 is still 0x01 from above
    ret

print: func
    push rg0
    push rg1
    ldw bpr, rg0, 8
_serial_print_loop:
    ldb rg0, rg1
    jez rg1, _serial_print_end
    push rg0
    mov rg1, rg0
    call _serial_send
    pop rg0
    addi rg0, 1, rg0
    jmp _serial_print_loop
_serial_print_end:
    pop rg1
    pop rg0
    return

print_byte: func
    push rg0
    ldw bpr, rg0, 8
    call _serial_send
    pop rg0
    return
