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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Input and output devices

#![allow(unused_imports)]

/// Macro for printing to the debug screen.
///
/// ```
/// debug!("Hello World!");
/// ```
#[allow(unused)]
macro_rules! debug {
    ($($arg:tt)*) => ({
        $crate::device::Output::Debug.write(format_args!($($arg)*));
    });
}

/// Macro for printing to the kernel screen.
///
/// ```
/// print!("Hello World!");
/// ```
#[allow(unused)]
macro_rules! print {
    ($($arg:tt)*) => ({
        $crate::device::Output::Print.write(format_args!($($arg)*));
    });
}

/// Macro for printing to the kernel screen.
///
/// ```
/// println!("Hello World!");
/// ```
#[allow(unused)]
macro_rules! println {
    ($($arg:tt)*) => ({
        $crate::device::Output::Println.write(format_args!($($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!");
/// ```
#[allow(unused)]
macro_rules! serial {
    // if we want to ignore any locks
    (force: $($arg:tt)*) => ({
        $crate::device::Output::Serial {
            force: true,
            file: core::file!(),
            line: core::line!(),
        }.write(format_args!($($arg)*))
	});
    // lock the serial console and print
	($($arg:tt)*) => ({
        $crate::device::Output::Serial {
            force: false,
            file: core::file!(),
            line: core::line!(),
        }.write(format_args!($($arg)*));
	});
}

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

mod serial;
pub use serial::Serial;

pub mod cga;
use cga::{Attribute, Color, Rect, Window};
#[cfg(feature = "graphics")]
pub mod vga;

mod keyboard;
pub use keyboard::{KeyBuffer, PS2Controller};

use crate::arch::{cpu, int};
use crate::interrupts::guard::GUARD;
use crate::util::Spin;

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

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

/// The serial console.
pub static mut SERIAL: Serial = Serial::new(serial::COM1);

/// Output devices.
#[allow(unused)]
pub enum Output {
    /// Debug output.
    Debug,
    /// Kernel output without newline.
    Print,
    /// Kernel output with newline.
    Println,
    /// Serial output with debug info.
    Serial {
        /// Ignore any locks and print anyway!
        force: bool,
        file: &'static str,
        line: u32,
    },
}

impl Output {
    /// Write to the device.
    pub fn write(self, args: fmt::Arguments) {
        let _ = int::suppress(|| match self {
            Self::Debug => writeln!(unsafe { &mut *addr_of_mut!(DBG) }, "{args}"),
            Self::Print => write!(unsafe { &mut *addr_of_mut!(KOUT) }, "{args}"),
            Self::Println => writeln!(unsafe { &mut *addr_of_mut!(KOUT) }, "{args}"),
            Self::Serial {
                force: _,
                file,
                line,
            } => {
                let c = cpu::id();
                let t = 0; // TODO: A4 - Get active thread id
                unsafe { writeln!(SERIAL, "\x1b[0;90m[{c}:{t} {file}:{line}]\x1b[0m {args}") }
            }
        });
    }
}