implemented hardware interrupts

added ps2 keyboard support, and added functionality to print pressed
keys to screen

right now the pic8259 keeps sending timer interrupts, but im just
handling them with a function that just prints a period to the vga
buffer. need to implement stuff for multiple threads
This commit is contained in:
2026-06-02 13:44:04 +03:00
parent 218c26e4db
commit f67facaddc
7 changed files with 126 additions and 15 deletions
Generated
+17
View File
@@ -29,6 +29,21 @@ dependencies = [
"spin 0.9.8",
]
[[package]]
name = "pc-keyboard"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed089a1fbffe3337a1a345501c981f1eb1e47e69de5a40e852433e12953c3174"
[[package]]
name = "pic8259"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb844b5b01db1e0b17938685738f113bfc903846f18932b378bc0eabfa40e194"
dependencies = [
"x86_64",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -41,6 +56,8 @@ version = "0.1.0"
dependencies = [
"bootloader",
"lazy_static",
"pc-keyboard",
"pic8259",
"spin 0.5.2",
"uart_16550",
"volatile 0.2.7",
+2
View File
@@ -26,6 +26,8 @@ volatile = "0.2.6"
spin = "0.5.2"
x86_64 = "0.14.2"
uart_16550 = "0.6.0"
pic8259 = "0.10.1"
pc-keyboard = "0.7.0"
[dependencies.lazy_static]
version = "1.0"
+65 -1
View File
@@ -1,7 +1,15 @@
use lazy_static::lazy_static;
use pic8259::ChainedPics;
use spin;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use crate::{gdt, println};
use crate::{gdt, print, println};
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: spin::Mutex<ChainedPics> =
spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
@@ -12,6 +20,8 @@ lazy_static! {
.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(kyb_interrupt_handler);
idt
};
}
@@ -29,6 +39,60 @@ extern "x86-interrupt" fn double_fault_handler(
) -> ! {
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn timer_interrupt_handler(_stack_hanfler: InterruptStackFrame) {
print!(".");
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
}
}
extern "x86-interrupt" fn kyb_interrupt_handler(_stack_frame: InterruptStackFrame) {
use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts};
use spin::Mutex;
use x86_64::instructions::port::Port;
static KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(Keyboard::new(
ScancodeSet1::new(),
layouts::Us104Key,
HandleControl::Ignore,
));
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => print!("{}", character),
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
unsafe {
PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard,
}
impl InterruptIndex {
fn as_u8(self) -> u8 {
self as u8
}
fn as_usize(self) -> usize {
usize::from(self.as_u8())
}
}
#[test_case]
fn test_breakpoint_exception() {
+13 -2
View File
@@ -15,6 +15,17 @@ pub mod vga_buffer;
pub fn init() {
interrupts::init_idt();
gdt::init();
unsafe {
interrupts::PICS.lock().initialize();
}
x86_64::instructions::interrupts::enable();
}
// cpu halter for efficiency
pub fn hlt_loop() -> ! {
loop {
x86_64::instructions::hlt();
}
}
// shit to make functions and methods testable
@@ -47,7 +58,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! {
serial_println!("[failed]\n");
serial_println!("Error: {}\n", info);
exit_qemu(QemuExitCode::Failed);
loop {}
hlt_loop();
}
// this this is here cuz lib.rs built separately, so need test for lib functions as well
@@ -57,7 +68,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! {
pub extern "C" fn _start() -> ! {
init();
test_main();
loop {}
hlt_loop();
}
#[cfg(test)]
+2 -2
View File
@@ -18,7 +18,7 @@ pub extern "C" fn _start() -> ! {
test_main();
println!("it didnt fucking crash!");
loop {}
rusty_os::hlt_loop();
}
// called on panic -> only when run, not in test
@@ -26,7 +26,7 @@ pub extern "C" fn _start() -> ! {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("{}", info);
loop {}
rusty_os::hlt_loop();
}
// panic handler for tests
+8 -4
View File
@@ -11,10 +11,14 @@ lazy_static! {
#[doc(hidden)]
pub fn _print(args: ::core::fmt::Arguments) {
use core::fmt::Write;
SERIAL1
.lock()
.write_fmt(args)
.expect("Printing to resial failed!");
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
SERIAL1
.lock()
.write_fmt(args)
.expect("Printing to resial failed!");
});
}
// macro that prints to host thru serial interface
+19 -6
View File
@@ -147,7 +147,13 @@ macro_rules! println {
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
WRITER.lock().write_fmt(args).unwrap();
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
// prevents deadlock by making sure the main thread is not interrupted by
// anything requiring to print shit
WRITER.lock().write_fmt(args).unwrap();
});
}
// VGA Buffer tests:
@@ -169,10 +175,17 @@ fn test_println_many() {
// check printed shits appear on screen
#[test_case]
fn test_println_output() {
use core::fmt::Write;
use x86_64::instructions::interrupts;
let s = "Some test string that fits on a single line";
println!("{}", s);
for (i, c) in s.chars().enumerate() {
let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writeln!(writer, "\n{}", s).expect("writeln failed");
for (i, c) in s.chars().enumerate() {
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
}