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:
Generated
+17
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user