1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Input and output devices

use core::fmt::{self, Write};

mod serial;
pub use serial::Serial;
use serial::COM1;

pub mod cga;
use cga::{Attribute, Color, Rect, Window};

mod keyboard;
pub use keyboard::Keyboard;

use crate::arch::cpu;
use crate::interrupts::guard::GUARD;
use crate::util::Spin;

/// Bounds of the user CGA window.
pub static mut KEYBOARD: Keyboard = Keyboard::new();

/// Access to the serial console.
/// The `serial!` macro might be more useful.
pub static SERIAL: Spin<Serial> = Spin::new(Serial::new(COM1));

/// The kernel output screen.
pub static KOUT: Spin<Window> = Spin::new(
    Window::new(Rect::new(0..40, 10..25))
        .with_hw_cursor()
        .with_style(Attribute::with(Color::Black, Color::Brown)),
);
/// The debug output screen.
pub static DBG: Spin<Window> = Spin::new(
    Window::new(Rect::new(40..80, 0..25)).with_style(Attribute::with(Color::Red, Color::Blue)),
);

/// Macro for printing to the cga screen.
///
/// ```
/// // print to the kout screen
/// print!("Hello World!");
/// // print to the dbg screen
/// print!(dbg: "Hello World!");
/// ```
macro_rules! print {
    (dbg: $($arg:tt)*) => ({
        use core::fmt::Write;
        $crate::arch::int::suppress(|| {
            let mut out = $crate::device::DBG.lock();
            let _ = write!(out, $($arg)*);
        })
    });
    ($($arg:tt)*) => ({
        use core::fmt::Write;
        $crate::arch::int::suppress(|| {
            let mut out = $crate::device::KOUT.lock();
            let _ = write!(out, $($arg)*);
        })
    });
}

/// Macro for printing to the cga screen.
///
/// ```
/// // print to the kout screen
/// print!("Hello World!");
/// // print to the dbg screen
/// print!(dbg: "Hello World!");
/// ```
macro_rules! println {
    (dbg: $($arg:tt)*) => ({
        use core::fmt::Write;
        $crate::arch::int::suppress(|| {
            let mut out = $crate::device::DBG.lock();
            let _ = writeln!(out, $($arg)*);
        })
    });
    ($($arg:tt)*) => ({
        use core::fmt::Write;
        $crate::arch::int::suppress(|| {
            let mut out = $crate::device::KOUT.lock();
            let _ = writeln!(out, $($arg)*);
        })
    });
}

/// Macro for printing to the serial console.
///
/// Locks the serial console and prints the message,
/// including core, thread, file and line.
///
/// ```
/// // lock console and print
/// serial!("Hello World!");
/// // if we want to print even if another one is printing
/// serial!(force: "Hello World!");
/// ```
macro_rules! serial {
    // if we want to ignore the lock
    (force: $($arg:tt)*) => ({
        #[allow(unused)]
        $crate::arch::int::suppress(|| {
            let serial = unsafe { $crate::device::SERIAL.raw() };
            $crate::device::_serial(serial, format_args!($($arg)*), core::file!(), core::line!());
        })
	});
    // lock the serial console and print
	($($arg:tt)*) => ({
        #[allow(unused)]
        $crate::arch::int::suppress(|| {
            let mut serial = $crate::device::SERIAL.lock();
            $crate::device::_serial(&mut serial, format_args!($($arg)*), core::file!(), core::line!());
        })
	});
}

pub fn _serial(serial: &mut Serial, args: fmt::Arguments, file: &str, line: u32) {
    let core = cpu::id();
    let tid = unsafe { GUARD.read().scheduler.active().unwrap_or(0) };
    let _ = write!(serial, "\x1b[0;90m[{core}@{tid} {file}:{line}]\x1b[0m ");
    let _ = serial.write_fmt(args);
    let _ = serial.write_char('\n');
}