continuing with serial driver in dsa.

This commit is contained in:
2026-03-16 23:18:53 +00:00
parent 6061e1701e
commit ffdd392003
7 changed files with 186 additions and 94 deletions
@@ -272,9 +272,16 @@ impl Emulator {
#[inline] #[inline]
fn interrupt(&mut self, int: Interrupt) { fn interrupt(&mut self, int: Interrupt) {
println!("Interrupt while at address {}", self.reg(Register::Pcx));
self.push(Register::Pcx); self.push(Register::Pcx);
*self.mut_reg(Register::Pcx) = *self.mut_reg(Register::Pcx) =
self.mem_read_word(self.reg(Register::Idr) + int.code() as u32 * 4) self.mem_read_word(self.reg(Register::Idr) + int.code() as u32 * 4);
println!(
"Jumping to interrupt at address {}",
self.reg(Register::Pcx)
);
} }
#[inline] #[inline]
@@ -489,6 +496,11 @@ impl Emulator {
Some(Opcode::IRet) => { Some(Opcode::IRet) => {
self.pop(Register::Ret); self.pop(Register::Ret);
*self.mut_reg(Register::Pcx) = self.reg(Register::Ret); *self.mut_reg(Register::Pcx) = self.reg(Register::Ret);
println!(
"Returning from interrupt to address {}",
self.reg(Register::Pcx)
);
} }
None => {} None => {}
} }
@@ -502,8 +514,12 @@ impl Emulator {
#[inline] #[inline]
fn mem_write_byte(&mut self, addr: u32, val: u8) { fn mem_write_byte(&mut self, addr: u32, val: u8) {
if addr == 0x40000 { if addr == 0x40000 || addr == 0x40001 || addr == 0x40002 || addr == 0x40003 {
eprintln!("emulator wrote 0x{:08x} to uart", val); eprintln!("emulator wrote 0x{:08x} to uart + {}", val, addr - 0x40000);
if addr == 0x40001 || addr == 0x40003 {
eprintln!("writing char {} to uart + {}", val as char, addr - 0x40000);
}
} }
self.mainstore.write_byte(addr, val); self.mainstore.write_byte(addr, val);
} }
+13 -14
View File
@@ -11,10 +11,10 @@ use crate::{
}; };
/// ## SERIAL LAYOUT: (from the perspective of code inside the emulator) /// ## SERIAL LAYOUT: (from the perspective of code inside the emulator)
/// - [base+0x00]: write valid flag - set by kernel after writing a byte to serial /// - [base+0x00]: ser_out valid flag - set by kernel after writing a byte to serial
/// - [base+0x01]: write byte - data payload for emulator to read /// - [base+0x01]: ser_out byte - data payload for emulator to read
/// - [base+0x02]: read valid flag - set by emulator after writing a byte to serial /// - [base+0x02]: ser_in valid flag - set by emulator after writing a byte to serial
/// - [base+0x03]: read byte - data payload for kernel to read /// - [base+0x03]: ser_in byte - data payload for kernel to read
pub struct Serial { pub struct Serial {
pub read_rx: Receiver<u8>, pub read_rx: Receiver<u8>,
pub write_tx: Sender<u8>, pub write_tx: Sender<u8>,
@@ -33,20 +33,19 @@ impl Serial {
rx: Receiver<u8>, rx: Receiver<u8>,
) { ) {
loop { loop {
let word = mem.read_word(uart_base); let ser_out_valid = mem.read_byte(uart_base + 0);
// Kernel => emulator: valid flag in byte 0, data in byte 1
// Kernel → emulator: valid flag in bits 31:24, data in bits 23:16 if ser_out_valid == 0x01 {
if (word >> 24) as u8 == 0x01 { let ser_out = mem.read_byte(uart_base + 1);
let byte = (word >> 16) as u8; let _ = tx.send(ser_out);
let _ = tx.send(byte); mem.write_byte(uart_base, 0x00); // clear the whole word
mem.write_word(uart_base, 0x00); // clear the whole word
} }
// Emulator kernel: your existing rx path // Emulator => kernel: valid flag in byte 2, data in byte 3
if (word >> 8) as u8 == 0x00 { let ser_in_valid = mem.read_byte(uart_base + 2);
if ser_in_valid == 0x00 {
if let Ok(byte) = rx.try_recv() { if let Ok(byte) = rx.try_recv() {
state.interrupt_queue.send(Interrupt::SerialIn).unwrap(); state.interrupt_queue.send(Interrupt::SerialIn).unwrap();
mem.write_byte(uart_base + 3, byte); mem.write_byte(uart_base + 3, byte);
mem.write_byte(uart_base + 2, 0x01); mem.write_byte(uart_base + 2, 0x01);
} }
+1
View File
@@ -1,5 +1,6 @@
#![feature(likely_unlikely)] #![feature(likely_unlikely)]
#![feature(inherent_associated_types)] #![feature(inherent_associated_types)]
#![feature(str_as_str)]
pub mod args; pub mod args;
pub mod backend; pub mod backend;
+48 -19
View File
@@ -1,11 +1,20 @@
dw DISPLAY: 0x30000 dw DISPLAY: 0x30000
dw stack: 0x10000 dw stack: 0x10000
// colour table — AABBGGRR format
dw COL0: 0xFFFFFFFF // white
dw COL1: 0xFF0000FF // red
dw COL2: 0xFF00FF00 // green
dw COL3: 0xFFFF0000 // blue
dw COL4: 0xFF00FFFF // yellow
dw COL_IDX: 0
// registers: // registers:
// rg0 = x position // rg0 = x position
// rg1 = y position // rg1 = y position
// rg2 = x direction (1 = right, 0 = left) // rg2 = x direction (1 = right, 0 = left)
// rg3 = y direction (1 = down, 0 = up) // rg3 = y direction (1 = down, 0 = up)
// rg5 = current colour
_init: _init:
ldw stack, bpr ldw stack, bpr
@@ -16,17 +25,20 @@ start:
lli 0, rg1 lli 0, rg1
lli 1, rg2 lli 1, rg2
lli 1, rg3 lli 1, rg3
lli 0, rg8
stw rg8, COL_IDX
ldw COL0, rg5
frame_loop: frame_loop:
// --- clear display --- // --- clear display ---
ldw DISPLAY, rg4 ldw DISPLAY, rg4
lwi 76800, rg5 lwi 76800, rga
clear_loop: clear_loop:
stw zero, rg4 stw zero, rg4
addi rg4, 4, rg4 addi rg4, 4, rg4
subi rg5, 1, rg5 subi rga, 1, rga
jnz rg5, clear_loop jnz rga, clear_loop
// --- draw square --- // --- draw square ---
mov rg1, rg6 mov rg1, rg6
@@ -36,7 +48,6 @@ draw_y_loop:
add rg1, rg8, rg8 add rg1, rg8, rg8
ilt rg6, rg8, rg9 ilt rg6, rg8, rg9
jez rg9, draw_done jez rg9, draw_done
mov rg0, rg7 mov rg0, rg7
draw_x_loop: draw_x_loop:
@@ -44,7 +55,6 @@ draw_x_loop:
add rg0, rg8, rg8 add rg0, rg8, rg8
ilt rg7, rg8, rg9 ilt rg7, rg8, rg9
jez rg9, draw_x_done jez rg9, draw_x_done
shl rg6, 8, rg8 shl rg6, 8, rg8
shl rg6, 6, rg9 shl rg6, 6, rg9
add rg8, rg9, rg8 add rg8, rg9, rg8
@@ -52,11 +62,7 @@ draw_x_loop:
shl rg8, 2, rg8 shl rg8, 2, rg8
ldw DISPLAY, rg9 ldw DISPLAY, rg9
add rg9, rg8, rg8 add rg9, rg8, rg8
stw rg5, rg8
lli 0xFFFF, rg9
lui 0xFFFF, rg9
stw rg9, rg8
addi rg7, 1, rg7 addi rg7, 1, rg7
jmp draw_x_loop jmp draw_x_loop
@@ -65,24 +71,28 @@ draw_x_done:
jmp draw_y_loop jmp draw_y_loop
draw_done: draw_done:
// --- check x walls before moving --- // --- track bounces this frame ---
lli 0, rgb
lli 0, rgc
// --- check x walls ---
lli 1, rg8 lli 1, rg8
ieq rg2, rg8, rg9 ieq rg2, rg8, rg9
jez rg9, check_x_left_wall jez rg9, check_x_left_wall
check_x_right_wall: check_x_right_wall:
// moving right: if x >= 300 flip direction
lli 300, rg8 lli 300, rg8
ige rg0, rg8, rg9 ige rg0, rg8, rg9
jez rg9, move_x jez rg9, move_x
lli 0, rg2 lli 0, rg2
lli 1, rgb
jmp move_x jmp move_x
check_x_left_wall: check_x_left_wall:
// moving left: if x == 0 flip direction
ieq rg0, zero, rg9 ieq rg0, zero, rg9
jez rg9, move_x jez rg9, move_x
lli 1, rg2 lli 1, rg2
lli 1, rgb
move_x: move_x:
lli 1, rg8 lli 1, rg8
@@ -94,40 +104,59 @@ do_x_left:
subi rg0, 1, rg0 subi rg0, 1, rg0
check_y_walls: check_y_walls:
// --- check y walls before moving ---
lli 1, rg8 lli 1, rg8
ieq rg3, rg8, rg9 ieq rg3, rg8, rg9
jez rg9, check_y_up_wall jez rg9, check_y_up_wall
check_y_down_wall: check_y_down_wall:
// moving down: if y >= 220 flip direction
lli 220, rg8 lli 220, rg8
ige rg1, rg8, rg9 ige rg1, rg8, rg9
jez rg9, move_y jez rg9, move_y
lli 0, rg3 lli 0, rg3
lli 1, rgc
jmp move_y jmp move_y
check_y_up_wall: check_y_up_wall:
// moving up: if y == 0 flip direction
ieq rg1, zero, rg9 ieq rg1, zero, rg9
jez rg9, move_y jez rg9, move_y
lli 1, rg3 lli 1, rg3
lli 1, rgc
move_y: move_y:
lli 1, rg8 lli 1, rg8
ieq rg3, rg8, rg9 ieq rg3, rg8, rg9
jez rg9, do_y_up jez rg9, do_y_up
addi rg1, 1, rg1 addi rg1, 1, rg1
jmp delay jmp check_corner
do_y_up: do_y_up:
subi rg1, 1, rg1 subi rg1, 1, rg1
check_corner:
and rgb, rgc, rg8
jez rg8, delay
// advance colour index
ldw COL_IDX, rg8
addi rg8, 1, rg8
lli 5, rg9
ige rg8, rg9, rg9
jez rg9, store_col_idx
lli 0, rg8
store_col_idx:
stw rg8, COL_IDX
// load colour from table: address = COL0 + idx * 4
shl rg8, 2, rg8
lwi COL0, rg9
add rg9, rg8, rg8
ldw rg8, rg5
delay: delay:
lwi 2000000, rg8 lwi 2000000, rg8
delay_loop: delay_loop:
subi rg8, 1, rg8 subi rg8, 1, rg8
jnz rg8, delay_loop jnz rg8, delay_loop
jmp frame_loop jmp frame_loop
hlt hlt
+5 -8
View File
@@ -1,4 +1,5 @@
include print: "./print.dsa" include print: "./print.dsa"
include serial: "./serial.dsa"
dw idt: 0xFFFF0000 dw idt: 0xFFFF0000
setup_base: func setup_base: func
@@ -15,8 +16,8 @@ setup_base: func
_loop_start: _loop_start:
stw rg1, rg2 // store guard into current IDT entry stw rg1, rg2 // store guard into current IDT entry
subi rg2, 4, rg2 // decrement counter subi rg2, 4, rg2 // decrement counter
ieq rg2, zero, rg3 // check if counter is zero ieq rg2, idr, rg3 // check if we've reached the base address
jnz rg3, _loop_start // repeat until all entries set jez rg3, _loop_start // repeat until all entries set
// make sure we can handle an invalid interrupt // make sure we can handle an invalid interrupt
lwi _handle_invalid_interrupt, rg0 lwi _handle_invalid_interrupt, rg0
@@ -31,16 +32,12 @@ _loop_start:
setup_io: func setup_io: func
push rg0 push rg0
lwi _serial_interrupt, rg0 lwi serial::serial_isr, rg0
stw rg0, idr, 132 // 33*4, interrupt idx 32 stw rg0, idr, 128 // 32*4, interrupt idx 32
pop rg0 pop rg0
return return
// does nothing, but wakes the kernel
_serial_interrupt:
nop
iret
db _invalid_int: "FATAL: Invalid Interrupt!" db _invalid_int: "FATAL: Invalid Interrupt!"
_handle_invalid_interrupt: _handle_invalid_interrupt:
+97 -44
View File
@@ -1,57 +1,74 @@
dw uart: 0x40000 // Serial port address in MMIO
// ------------------------------------------ dw UART: 0x40000
// reads up to rg1 bytes into buffer at rg2
// args: buf_size @ bpr+8, buf_addr @ bpr+12 // Reserve space for our read buffer
// returns: 0 in bpr+8 on success, 1 on nullptr error resb SERIAL_BUFFER: 1024
buf_read: func dw SER_READ_PTR: 0
push rg0 dw SER_WRITE_PTR: 0
push rg1
push rg2 serial_init: func
push rg3 lwi SERIAL_BUFFER, rg0
ldw bpr, rg1, 8 // buf size stw rg0, SER_READ_PTR, 0
ldw bpr, rg2, 12 // buf addr stw rg0, SER_WRITE_PTR, 0
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 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 // read_byte: emulator → kernel
// reads data from base+0x00, valid flag at base+0x01 // reads data from base+0x00, valid flag at base+0x01
read_byte: func read_byte: func
push rg0 push rg0
push rg1 push rg1
push rg2 push rg2
ldw uart, rg1 ldw UART, rg1
_serial_read_wait: _serial_read_wait:
ldb rg1, rg0, 1 // base+0x01 = read valid flag ldb rg1, rg0, 2 // base+0x02 = read valid flag
lli 0x01, rg2 lli 0x01, rg2
ieq rg0, rg2, rg2 ieq rg0, rg2, rg2
jnz rg2, _serial_read_ready jnz rg2, _serial_read_ready
hlt hlt
jmp _serial_read_wait jmp _serial_read_wait
_serial_read_ready: _serial_read_ready:
ldb rg1, rg0, 0 // base+0x00 = data byte ldb rg1, rg0, 3 // base+0x03 = data byte
stb zero, rg1, 1 // clear read valid flag stb zero, rg1, 2 // clear read valid flag
stb rg0, bpr, 8 // return byte stb rg0, bpr, 8 // return byte
pop rg2 pop rg2
pop rg1 pop rg1
@@ -59,22 +76,22 @@ _serial_read_ready:
return return
// _serial_send: kernel → emulator // _serial_send: kernel → emulator
// writes data to base+0x02, valid flag to base+0x03 // writes data to base+0x01, valid flag to base+0x00
_serial_send: _serial_send:
push rg0 push rg0
push rg2 push rg2
push rg3 push rg3
push rg4 push rg4
push rg5 push rg5
ldw uart, rg5 ldw UART, rg5
_serial_wait: _serial_wait:
ldb rg5, rg2, 3 // base+0x03 = write valid flag ldb rg5, rg2, 0 // base+0x00 = ser_out_valid
lli 0x01, rg3 lli 0x01, rg3
ieq rg2, rg3, rg4 ieq rg2, rg3, rg4
jnz rg4, _serial_wait // wait while emulator hasn't consumed yet jnz rg4, _serial_wait // wait while emulator hasn't consumed yet
stb rg0, rg5, 2 // base+0x02 = data byte stb rg0, rg5, 1 // base+0x01 = ser_out_data
lli 0x01, rg2 lli 0x01, rg2
stb rg2, rg5, 3 // base+0x03 = set valid flag stb rg2, rg5, 0 // base+0x00 = ser_out_valid = 1
pop rg5 pop rg5
pop rg4 pop rg4
pop rg3 pop rg3
@@ -114,3 +131,39 @@ print_byte: func
// ------------------------------------------ // ------------------------------------------
_serial_end: _serial_end:
return 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
+3 -6
View File
@@ -2,8 +2,6 @@ include serial: "./serial.dsa"
include idt: "./idt.dsa" include idt: "./idt.dsa"
include print: "./print.dsa" include print: "./print.dsa"
resb buff: 256
dw stack: 0x1000 dw stack: 0x1000
_init: _init:
ldw stack, bpr ldw stack, bpr
@@ -28,10 +26,8 @@ main:
//lwi 32, rg1 //lwi 32, rg1
//push rg0 //push rg0
//push rg1 //push rg1
push zero
call serial::read_byte lwi serial::SERIAL_BUFFER, rg0
pop rg0
//pop zero
push rg0 push rg0
call print::print call print::print
pop zero pop zero
@@ -42,3 +38,4 @@ main:
pop zero pop zero
hlt hlt
jmp main