// Serial port address in MMIO dw UART: 0x40000 // Reserve space for our read buffer resb SERIAL_BUFFER: 1024 dw SER_READ_PTR: 0 dw SER_WRITE_PTR: 0 serial_init: func lwi SERIAL_BUFFER, rg0 stw rg0, SER_READ_PTR, 0 stw rg0, SER_WRITE_PTR, 0 return serial_isr: push rg0 push rg1 push rg2 ldw UART, rg0 ldb rg0, rg1, 2 // load ser_in_valid flag // no serial input jez rg1, _serial_isr_end // we should have a valid byte to read at offset 3 ldb rg0, rg1, 3 // data byte // 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, _serial_isr_nowrap lwi SERIAL_BUFFER, rg2 // set the pointer _serial_isr_nowrap: stw rg2, SER_WRITE_PTR _serial_isr_end: pop rg0 pop rg1 pop rg2 iret // read_byte: emulator → kernel // reads data from base+0x00, valid flag at base+0x01 read_byte: func push rg0 push rg1 push rg2 ldw UART, rg1 _serial_read_wait: ldb rg1, rg0, 2 // base+0x02 = read valid flag lli 0x01, rg2 ieq rg0, rg2, rg2 jnz rg2, _serial_read_ready hlt jmp _serial_read_wait _serial_read_ready: ldb rg1, rg0, 3 // base+0x03 = data byte stb zero, rg1, 2 // clear read valid flag stb rg0, bpr, 8 // return byte pop rg2 pop rg1 pop rg0 return // _serial_send: kernel → emulator // writes data to base+0x01, valid flag to base+0x00 _serial_send: push rg0 push rg2 push rg3 push rg4 push rg5 ldw UART, rg5 _serial_wait: ldb rg5, rg2, 0 // base+0x00 = ser_out_valid lli 0x01, rg3 ieq rg2, rg3, rg4 jnz rg4, _serial_wait // wait while emulator hasn't consumed yet stb rg0, rg5, 1 // base+0x01 = ser_out_data lli 0x01, rg2 stb rg2, rg5, 0 // base+0x00 = ser_out_valid = 1 pop rg5 pop rg4 pop rg3 pop rg2 pop rg0 ret // ------------------------------------------ // prints the null-terminated string at addr(arg[0]) print: func push rg0 push rg1 push rg2 ldw bpr, rg0, 8 // load string address from arg _serial_print_loop: ldb rg0, rg1 // load byte ieq rg1, zero, rg2 // null terminator? jnz rg2, _serial_print_end push rg0 // save string pointer mov rg1, rg0 call _serial_send pop rg0 addi rg0, 1, rg0 jmp _serial_print_loop _serial_print_end: pop rg2 pop rg1 pop rg0 jmp _serial_end // ------------------------------------------ // prints the last byte of arg[0] print_byte: func push rg0 ldw bpr, rg0, 8 call _serial_send pop rg0 jmp _serial_end // ------------------------------------------ _serial_end: return // ------------------------------------------ // reads up to rg1 bytes into buffer at rg2 // args: buf_size @ bpr+8, buf_addr @ bpr+12 // returns: 0 in bpr+8 on success, 1 on nullptr error buf_read: func push rg0 push rg1 push rg2 push rg3 ldw bpr, rg1, 8 // buf size ldw bpr, rg2, 12 // buf addr jez rg2, _buf_read_err // return err if nullptr _buf_read_loop: jez rg1, _buf_read_end call read_byte ldw bpr, rg3, 8 // read return value from frame stb rg3, rg2 // store into buffer addi rg2, 1, rg2 subi rg1, 1, rg1 jmp _buf_read_loop _buf_read_end: stw zero, bpr, 8 // return 0 (success) pop rg3 pop rg2 pop rg1 pop rg0 return _buf_read_err: lli 1, rg0 stw rg0, bpr, 8 // return 1 (error) pop rg3 pop rg2 pop rg1 pop rg0 return