diff --git a/dsa/emulator/src/backend/processor/processor.rs b/dsa/emulator/src/backend/processor/processor.rs index 5d4d3a6..77e20bc 100644 --- a/dsa/emulator/src/backend/processor/processor.rs +++ b/dsa/emulator/src/backend/processor/processor.rs @@ -272,9 +272,16 @@ impl Emulator { #[inline] fn interrupt(&mut self, int: Interrupt) { + println!("Interrupt while at address {}", self.reg(Register::Pcx)); + self.push(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] @@ -489,6 +496,11 @@ impl Emulator { Some(Opcode::IRet) => { self.pop(Register::Ret); *self.mut_reg(Register::Pcx) = self.reg(Register::Ret); + + println!( + "Returning from interrupt to address {}", + self.reg(Register::Pcx) + ); } None => {} } @@ -502,8 +514,12 @@ impl Emulator { #[inline] fn mem_write_byte(&mut self, addr: u32, val: u8) { - if addr == 0x40000 { - eprintln!("emulator wrote 0x{:08x} to uart", val); + if addr == 0x40000 || addr == 0x40001 || addr == 0x40002 || addr == 0x40003 { + 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); } diff --git a/dsa/emulator/src/frontend/components/serial.rs b/dsa/emulator/src/frontend/components/serial.rs index d20d190..7edd9d6 100644 --- a/dsa/emulator/src/frontend/components/serial.rs +++ b/dsa/emulator/src/frontend/components/serial.rs @@ -11,10 +11,10 @@ use crate::{ }; /// ## 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+0x01]: write byte - data payload for emulator to read -/// - [base+0x02]: read valid flag - set by emulator after writing a byte to serial -/// - [base+0x03]: read byte - data payload for kernel to read +/// - [base+0x00]: ser_out valid flag - set by kernel after writing a byte to serial +/// - [base+0x01]: ser_out byte - data payload for emulator to read +/// - [base+0x02]: ser_in valid flag - set by emulator after writing a byte to serial +/// - [base+0x03]: ser_in byte - data payload for kernel to read pub struct Serial { pub read_rx: Receiver, pub write_tx: Sender, @@ -33,20 +33,19 @@ impl Serial { rx: Receiver, ) { loop { - let word = mem.read_word(uart_base); - - // Kernel → emulator: valid flag in bits 31:24, data in bits 23:16 - if (word >> 24) as u8 == 0x01 { - let byte = (word >> 16) as u8; - let _ = tx.send(byte); - mem.write_word(uart_base, 0x00); // clear the whole word + let ser_out_valid = mem.read_byte(uart_base + 0); + // Kernel => emulator: valid flag in byte 0, data in byte 1 + if ser_out_valid == 0x01 { + let ser_out = mem.read_byte(uart_base + 1); + let _ = tx.send(ser_out); + mem.write_byte(uart_base, 0x00); // clear the whole word } - // Emulator → kernel: your existing rx path - if (word >> 8) as u8 == 0x00 { + // Emulator => kernel: valid flag in byte 2, data in byte 3 + let ser_in_valid = mem.read_byte(uart_base + 2); + if ser_in_valid == 0x00 { if let Ok(byte) = rx.try_recv() { state.interrupt_queue.send(Interrupt::SerialIn).unwrap(); - mem.write_byte(uart_base + 3, byte); mem.write_byte(uart_base + 2, 0x01); } diff --git a/dsa/emulator/src/lib.rs b/dsa/emulator/src/lib.rs index f8f2c41..ef240a7 100644 --- a/dsa/emulator/src/lib.rs +++ b/dsa/emulator/src/lib.rs @@ -1,5 +1,6 @@ #![feature(likely_unlikely)] #![feature(inherent_associated_types)] +#![feature(str_as_str)] pub mod args; pub mod backend; diff --git a/dsa_resources/animation.dsa b/dsa_resources/animation.dsa index 43ecc92..61eb01c 100644 --- a/dsa_resources/animation.dsa +++ b/dsa_resources/animation.dsa @@ -1,11 +1,20 @@ dw DISPLAY: 0x30000 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: // rg0 = x position // rg1 = y position // rg2 = x direction (1 = right, 0 = left) // rg3 = y direction (1 = down, 0 = up) +// rg5 = current colour _init: ldw stack, bpr @@ -16,17 +25,20 @@ start: lli 0, rg1 lli 1, rg2 lli 1, rg3 + lli 0, rg8 + stw rg8, COL_IDX + ldw COL0, rg5 frame_loop: // --- clear display --- ldw DISPLAY, rg4 - lwi 76800, rg5 + lwi 76800, rga clear_loop: stw zero, rg4 addi rg4, 4, rg4 - subi rg5, 1, rg5 - jnz rg5, clear_loop + subi rga, 1, rga + jnz rga, clear_loop // --- draw square --- mov rg1, rg6 @@ -36,7 +48,6 @@ draw_y_loop: add rg1, rg8, rg8 ilt rg6, rg8, rg9 jez rg9, draw_done - mov rg0, rg7 draw_x_loop: @@ -44,7 +55,6 @@ draw_x_loop: add rg0, rg8, rg8 ilt rg7, rg8, rg9 jez rg9, draw_x_done - shl rg6, 8, rg8 shl rg6, 6, rg9 add rg8, rg9, rg8 @@ -52,11 +62,7 @@ draw_x_loop: shl rg8, 2, rg8 ldw DISPLAY, rg9 add rg9, rg8, rg8 - - lli 0xFFFF, rg9 - lui 0xFFFF, rg9 - stw rg9, rg8 - + stw rg5, rg8 addi rg7, 1, rg7 jmp draw_x_loop @@ -65,24 +71,28 @@ draw_x_done: jmp draw_y_loop draw_done: - // --- check x walls before moving --- + // --- track bounces this frame --- + lli 0, rgb + lli 0, rgc + + // --- check x walls --- lli 1, rg8 ieq rg2, rg8, rg9 jez rg9, check_x_left_wall check_x_right_wall: - // moving right: if x >= 300 flip direction lli 300, rg8 ige rg0, rg8, rg9 jez rg9, move_x lli 0, rg2 + lli 1, rgb jmp move_x check_x_left_wall: - // moving left: if x == 0 flip direction ieq rg0, zero, rg9 jez rg9, move_x lli 1, rg2 + lli 1, rgb move_x: lli 1, rg8 @@ -94,40 +104,59 @@ do_x_left: subi rg0, 1, rg0 check_y_walls: - // --- check y walls before moving --- lli 1, rg8 ieq rg3, rg8, rg9 jez rg9, check_y_up_wall check_y_down_wall: - // moving down: if y >= 220 flip direction lli 220, rg8 ige rg1, rg8, rg9 jez rg9, move_y lli 0, rg3 + lli 1, rgc jmp move_y check_y_up_wall: - // moving up: if y == 0 flip direction ieq rg1, zero, rg9 jez rg9, move_y lli 1, rg3 + lli 1, rgc move_y: lli 1, rg8 ieq rg3, rg8, rg9 jez rg9, do_y_up addi rg1, 1, rg1 - jmp delay + jmp check_corner do_y_up: 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: lwi 2000000, rg8 delay_loop: subi rg8, 1, rg8 jnz rg8, delay_loop - jmp frame_loop - hlt + hlt \ No newline at end of file diff --git a/dsa_resources/idt.dsa b/dsa_resources/idt.dsa index 3fc482c..2e7641a 100644 --- a/dsa_resources/idt.dsa +++ b/dsa_resources/idt.dsa @@ -1,4 +1,5 @@ include print: "./print.dsa" +include serial: "./serial.dsa" dw idt: 0xFFFF0000 setup_base: func @@ -15,8 +16,8 @@ setup_base: func _loop_start: stw rg1, rg2 // store guard into current IDT entry subi rg2, 4, rg2 // decrement counter - ieq rg2, zero, rg3 // check if counter is zero - jnz rg3, _loop_start // repeat until all entries set + ieq rg2, idr, rg3 // check if we've reached the base address + jez rg3, _loop_start // repeat until all entries set // make sure we can handle an invalid interrupt lwi _handle_invalid_interrupt, rg0 @@ -31,16 +32,12 @@ _loop_start: setup_io: func push rg0 - lwi _serial_interrupt, rg0 - stw rg0, idr, 132 // 33*4, interrupt idx 32 + lwi serial::serial_isr, rg0 + stw rg0, idr, 128 // 32*4, interrupt idx 32 pop rg0 return -// does nothing, but wakes the kernel -_serial_interrupt: - nop - iret db _invalid_int: "FATAL: Invalid Interrupt!" _handle_invalid_interrupt: diff --git a/dsa_resources/serial.dsa b/dsa_resources/serial.dsa index 5dd00a5..1d34364 100644 --- a/dsa_resources/serial.dsa +++ b/dsa_resources/serial.dsa @@ -1,57 +1,74 @@ -dw uart: 0x40000 -// ------------------------------------------ -// 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 +// 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 + ldw UART, rg1 _serial_read_wait: - ldb rg1, rg0, 1 // base+0x01 = read valid flag + 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, 0 // base+0x00 = data byte - stb zero, rg1, 1 // clear read valid flag + 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 @@ -59,22 +76,22 @@ _serial_read_ready: return // _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: push rg0 push rg2 push rg3 push rg4 push rg5 - ldw uart, rg5 + ldw UART, rg5 _serial_wait: - ldb rg5, rg2, 3 // base+0x03 = write valid flag + 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, 2 // base+0x02 = data byte + stb rg0, rg5, 1 // base+0x01 = ser_out_data 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 rg4 pop rg3 @@ -114,3 +131,39 @@ print_byte: func // ------------------------------------------ _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 diff --git a/dsa_resources/serialtest.dsa b/dsa_resources/serialtest.dsa index c5083e9..496bde7 100644 --- a/dsa_resources/serialtest.dsa +++ b/dsa_resources/serialtest.dsa @@ -2,8 +2,6 @@ include serial: "./serial.dsa" include idt: "./idt.dsa" include print: "./print.dsa" -resb buff: 256 - dw stack: 0x1000 _init: ldw stack, bpr @@ -28,10 +26,8 @@ main: //lwi 32, rg1 //push rg0 //push rg1 - push zero - call serial::read_byte - pop rg0 - //pop zero + + lwi serial::SERIAL_BUFFER, rg0 push rg0 call print::print pop zero @@ -42,3 +38,4 @@ main: pop zero hlt + jmp main