From 5243ef874d184564fe28e6bf8fdcc732fe56126c Mon Sep 17 00:00:00 2001 From: acheronfail Date: Tue, 16 Oct 2018 07:57:36 +1000 Subject: [PATCH] add gifs for keyboard and fix source to compile --- .../posts/08-hardware-interrupts/index.md | 16 ++++---- .../qemu-printing-numbers.gif | Bin 0 -> 5033 bytes .../qemu-printing-scancodes.gif | Bin 0 -> 4686 bytes src/interrupts.rs | 1 + src/lib.rs | 6 +++ src/main.rs | 36 ++++++++++++++++-- 6 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 blog/content/second-edition/posts/08-hardware-interrupts/qemu-printing-numbers.gif create mode 100644 blog/content/second-edition/posts/08-hardware-interrupts/qemu-printing-scancodes.gif diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/index.md b/blog/content/second-edition/posts/08-hardware-interrupts/index.md index 08d4f1a7..8387de66 100644 --- a/blog/content/second-edition/posts/08-hardware-interrupts/index.md +++ b/blog/content/second-edition/posts/08-hardware-interrupts/index.md @@ -330,13 +330,13 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( _stack_frame: &mut ExceptionStackFrame) { print!("k"); - unsafe { PICS.lock().notify_end_of_interrupt(KEYBOARD_INTERRUPT_ID) } + unsafe { PICS.lock().notify_end_of_interrupt(interrupts::KEYBOARD_INTERRUPT_ID) } } ``` As we see from the graphic [above](#the-8259-pic), the keyboard uses line 1 of the primary PIC. This means that it arrives at the CPU as interrupt 33 (1 + offset 32). We again create a `KEYBOARD_INTERRUPT_ID` constant to keep things organized. In the interrupt handler, we print a `k` and send the end of interrupt signal to the interrupt controller. -We now see that a `k` appears whenever we press or release a key. The keyboard controller generates continuous interrupts if the key is hold down, so we see a series of `k`s on the screen. +We now see that a `k` appears on the screen when we press a key. However, this only works for the first key we press, even if we continue to press keys no more `k`s appear on the screen. This is because the keyboard won't send another interrupt if we haven't read the pressed from its data port. ### Reading the Scancodes @@ -355,7 +355,7 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( let port = Port::new(0x60); let scancode: u8 = unsafe { port.read() }; print!("{}", scancode); - unsafe { PICS.lock().notify_end_of_interrupt(KEYBOARD_INTERRUPT_ID) } + unsafe { PICS.lock().notify_end_of_interrupt(interrupts::KEYBOARD_INTERRUPT_ID) } } ``` @@ -364,12 +364,12 @@ We use the [`Port`] type of the `x86_64` crate to read a byte from the keyboard' [`Port`]: https://docs.rs/x86_64/0.2.11/x86_64/instructions/port/struct.Port.html [_scancode_]: https://en.wikipedia.org/wiki/Scancode -TODO image/gif +![QEMU printing scancodes to the screen when keys are pressed](qemu-printing-scancodes.gif) The above image shows me slowly typing "123". We see that adjacent keys have adjacent scancodes and that pressing a key causes a different scancode than releasing it. But how do we translate the scancodes to the actual key actions exactly? ### Interpreting the Scancodes -There are three different standards for the mapping between scancodes and keys, the so-called _scancode sets_. All three sets go back to the keyboards of early IBM computers: the [IBM XT], the [IBM 3270 PC], and the [IBM AT]. Later computers fortunatly did not continue the trend of defining new scancodes, but rather emulated the existing scancode sets and extending them. Today most keyboards can be configured to emulate any of the three sets. +There are three different standards for the mapping between scancodes and keys, the so-called _scancode sets_. All three sets go back to the keyboards of early IBM computers: the [IBM XT], the [IBM 3270 PC], and the [IBM AT]. Later computers fortunately did not continue the trend of defining new scancodes, but rather emulated the existing scancode sets and extending them. Today most keyboards can be configured to emulate any of the three sets. [IBM XT]: https://en.wikipedia.org/wiki/IBM_Personal_Computer_XT [IBM 3270 PC]: https://en.wikipedia.org/wiki/IBM_3270_PC @@ -409,13 +409,13 @@ extern "x86-interrupt" fn keyboard_interrupt_handler( if let Some(key) = key { print!("{}", key); } - unsafe { PICS.lock().notify_end_of_interrupt(KEYBOARD_INTERRUPT_ID) } + unsafe { PICS.lock().notify_end_of_interrupt(interrupts::KEYBOARD_INTERRUPT_ID) } } ``` The above code just translates the numbers 0-9 and ignores all other keys. Now we can write numbers: -TODO image +![QEMU printing numbers to the screen](qemu-printing-numbers.gif) Translating the other keys could work in the same way, probably with an enum for control keys such as escape or backspace. Such a translation function would be a good candidate for a small external crate, but I couldn't find one that works with scancode set 1. In case you'd like to write such a crate and need mentoring, just let us know, we're happy to help! @@ -423,7 +423,7 @@ Translating the other keys could work in the same way, probably with an enum for In this post we learned how to enable and handle external interrupts. We learned about the 8259 PIC and its primary/secondary layout, the remapping of the interrupt numbers, and the "end of interrupt" signal. We saw that the hardware timer and the keyboard controller are active by default and start to send interrupts as soon as we enable them in the CPU. We learned about the `hlt` instruction, which halts the CPU until the next interrupt, and about the scancode sets of PS/2 keyboards. -Now we are able to interact with our kernel and have some fundamential building blocks for creating a small shell or simple games. +Now we are able to interact with our kernel and have some fundamental building blocks for creating a small shell or simple games. ## What's next? diff --git a/blog/content/second-edition/posts/08-hardware-interrupts/qemu-printing-numbers.gif b/blog/content/second-edition/posts/08-hardware-interrupts/qemu-printing-numbers.gif new file mode 100644 index 0000000000000000000000000000000000000000..3985bd14fb27b73121b0b99ee52605750f610c2b GIT binary patch literal 5033 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 Q;PjV-Aq-ZXjUsFf0B_~zd;kCd literal 0 HcmV?d00001 diff --git a/src/interrupts.rs b/src/interrupts.rs index 810219df..3c3332d3 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -8,3 +8,4 @@ pub static PICS: spin::Mutex = spin::Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) }); pub const TIMER_INTERRUPT_ID: u8 = PIC_1_OFFSET; +pub const KEYBOARD_INTERRUPT_ID: u8 = PIC_1_OFFSET + 1; diff --git a/src/lib.rs b/src/lib.rs index 351a79ca..505bc4d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,3 +25,9 @@ pub unsafe fn exit_qemu() { let mut port = Port::::new(0xf4); port.write(0); } + +pub fn hlt_loop() -> ! { + loop { + x86_64::instructions::hlt(); + } +} diff --git a/src/main.rs b/src/main.rs index 501b14bf..f4f22d86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,8 +10,8 @@ extern crate x86_64; #[macro_use] extern crate lazy_static; -use core::panic::PanicInfo; use blog_os::interrupts::{self, PICS}; +use core::panic::PanicInfo; /// This function is the entry point, since the linker looks for a function /// named `_start` by default. @@ -26,7 +26,7 @@ pub extern "C" fn _start() -> ! { x86_64::instructions::interrupts::enable(); println!("It did not crash!"); - loop {} + blog_os::hlt_loop(); } /// This function is called on panic. @@ -35,7 +35,7 @@ pub extern "C" fn _start() -> ! { #[no_mangle] pub fn panic(info: &PanicInfo) -> ! { println!("{}", info); - loop {} + blog_os::hlt_loop(); } use x86_64::structures::idt::{ExceptionStackFrame, InterruptDescriptorTable}; @@ -52,6 +52,8 @@ lazy_static! { let timer_interrupt_id = usize::from(interrupts::TIMER_INTERRUPT_ID); idt[timer_interrupt_id].set_handler_fn(timer_interrupt_handler); + let keyboard_interrupt_id = usize::from(interrupts::KEYBOARD_INTERRUPT_ID); + idt[keyboard_interrupt_id].set_handler_fn(keyboard_interrupt_handler); idt }; @@ -80,3 +82,31 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut ExceptionSt .notify_end_of_interrupt(interrupts::TIMER_INTERRUPT_ID) } } + +extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut ExceptionStackFrame) { + use x86_64::instructions::port::Port; + + let port = Port::new(0x60); + let scancode: u8 = unsafe { port.read() }; + + let key = match scancode { + 0x02 => Some('1'), + 0x03 => Some('2'), + 0x04 => Some('3'), + 0x05 => Some('4'), + 0x06 => Some('5'), + 0x07 => Some('6'), + 0x08 => Some('7'), + 0x09 => Some('8'), + 0x0a => Some('9'), + 0x0b => Some('0'), + _ => None, + }; + if let Some(key) = key { + print!("{}", key); + } + unsafe { + PICS.lock() + .notify_end_of_interrupt(interrupts::KEYBOARD_INTERRUPT_ID) + } +}