lspci, console refactor

This commit is contained in:
Jack Halford 2019-01-20 22:11:14 +01:00
parent 85afa2b437
commit c9ad4e9e58
10 changed files with 250 additions and 327 deletions

View file

@ -126,17 +126,9 @@ impl Iterator for ACPISDTIter {
} }
} }
fn is_init() -> Result<(), &'static str> {
if unsafe { ACPI.valid } {
Ok(())
} else {
Err("ACPI is not initialized")
}
}
/// Initalized the ACPI module /// Initalized the ACPI module
pub fn init() -> Result<(), &'static str> { pub fn init() -> Result<(), &'static str> {
if let Ok(()) = is_init() { if unsafe{ACPI.valid} {
return Ok(()); return Ok(());
} }
unsafe { ACPI.init() } unsafe { ACPI.init() }
@ -144,7 +136,7 @@ pub fn init() -> Result<(), &'static str> {
/// Load the ACPI module, addr given is a ptr to RSDP /// Load the ACPI module, addr given is a ptr to RSDP
pub fn load(rsdp_addr: u32) -> Result<(), &'static str> { pub fn load(rsdp_addr: u32) -> Result<(), &'static str> {
if let Ok(()) = is_init() { if unsafe{ACPI.valid} {
return Ok(()); return Ok(());
} }
unsafe { ACPI.load(rsdp_addr) } unsafe { ACPI.load(rsdp_addr) }
@ -152,32 +144,30 @@ pub fn load(rsdp_addr: u32) -> Result<(), &'static str> {
/// Proceed to ACPI shutdown /// Proceed to ACPI shutdown
/// This function doesn't work with Virtual Box yet /// This function doesn't work with Virtual Box yet
pub fn shutdown() -> Result<(), &'static str> { pub fn shutdown() {
is_init()?; if unsafe{ACPI.valid} { return }
dsdt::shutdown(fadt::get_controlblock()?) dsdt::shutdown(fadt::get_controlblock().unwrap());
} }
/// Proceed to ACPI reboot /// Proceed to ACPI reboot
/// This function need ACPI in v2 /// This function need ACPI in v2
pub fn reboot() -> Result<(), &'static str> { pub fn reboot() {
is_init()?; if unsafe {!ACPI.valid} {
println!("ACPI not initialized");
}
if unsafe { ACPI.v2 } { if unsafe { ACPI.v2 } {
fadt::reboot() fadt::reboot().unwrap()
} else { } else {
Err("ACPI reboot only available in ACPI v2+") println!("ACPI reboot only available in ACPI v2+");
} }
} }
/// Display state of ACPI /// Display state of ACPI
pub fn info() -> Result<(), &'static str> { pub fn info() {
is_init()?; if unsafe { !ACPI.valid } { println!("ACPI not initialized"); return }
println!( match fadt::is_enable() {
"ACPI STATE:\n {}", Ok(True) => println!("ACPI is disabled"),
if fadt::is_enable()? { Ok(False) => println!("ACPI is disabled"),
"ENABLED" Err(msg) => println!("error while checking ACPI")
} else {
"DISABLED"
} }
);
Ok(())
} }

View file

@ -23,5 +23,5 @@ pub unsafe fn init(active_table: &mut ActivePageTable) {
#[alloc_error_handler] #[alloc_error_handler]
fn foo(_: core::alloc::Layout) -> ! { fn foo(_: core::alloc::Layout) -> ! {
panic!(); panic!("alloc_error_handler");
} }

View file

@ -1,10 +1,8 @@
extern crate raw_cpuid; extern crate raw_cpuid;
use core::fmt::Result;
use self::raw_cpuid::CpuId; use self::raw_cpuid::CpuId;
pub fn cpu_info() -> Result { pub fn cpu_info() {
let cpuid = CpuId::new(); let cpuid = CpuId::new();
if let Some(info) = cpuid.get_vendor_info() { if let Some(info) = cpuid.get_vendor_info() {
@ -285,6 +283,4 @@ pub fn cpu_info() -> Result {
}; };
println!(""); println!("");
} }
Ok(())
} }

View file

@ -17,10 +17,6 @@ pub unsafe extern "C" fn x86_rust_start(multiboot_info_addr: usize) {
// parse multiboot2 info // parse multiboot2 info
let boot_info = multiboot2::load(multiboot_info_addr); let boot_info = multiboot2::load(multiboot_info_addr);
// println!("{:?}", boot_info);
// flush!();
// asm!("hlt");
// ACPI must be intialized BEFORE paging is active // ACPI must be intialized BEFORE paging is active
if let Some(rsdp) = boot_info.rsdp_v2_tag() { if let Some(rsdp) = boot_info.rsdp_v2_tag() {
acpi::load(rsdp).expect("ACPI failed"); acpi::load(rsdp).expect("ACPI failed");
@ -116,3 +112,30 @@ pub unsafe fn usermode(ip: u32, sp: u32) -> ! {
unreachable!(); unreachable!();
} }
/// Dump control registers
pub fn regs() {
use x86::registers::control::*;
use x86::instructions::tables::tr;
use x86::instructions::segmentation::*;
use x86::registers::flags::*;
use x86::structures::gdt;
println!("cr0 = {:?}", Cr0::read());
println!("cr3 = {:?}", Cr3::read());
println!("cr4 = {:?}", Cr4::read());
println!("flags= {:?}", flags());
println!("tr = {:?}", tr());
println!("ss = {:?}", ss());
println!("cs = {:?}", cs());
println!("ds = {:?}", ds());
println!("es = {:?}", es());
println!("fs = {:?}", fs());
println!("gs = {:?}", gs());
unsafe {
println!(
"tss = {:#?}",
gdt::Descriptor(::arch::x86::gdt::GDT.table[tr().index() as usize])
);
}
flush!();
}

View file

@ -1,14 +1,49 @@
extern crate core; extern crate core;
// extern crate multiboot2; // extern crate multiboot2;
use acpi;
use time;
use keyboard::PS2;
use core::char;
use vga::*; use vga::*;
use alloc::collections::BTreeMap;
pub static mut CONSOLE: Console = self::Console::new(); pub static mut CONSOLE: Console = self::Console::new();
lazy_static! {
static ref COMMANDS: BTreeMap<&'static str, fn()> = {
let mut commands = BTreeMap::new();
commands.insert("help", self::help as fn());
// ACPI
commands.insert("acpi", ::acpi::info as fn());
commands.insert("reboot", ::acpi::reboot as fn());
commands.insert("shutdown", ::acpi::shutdown as fn());
// time
commands.insert("uptime", ::time::uptime as fn());
// cpu
use arch::x86;
commands.insert("cpu", x86::devices::cpu::cpu_info as fn());
commands.insert("regs", x86::regs as fn());
commands.insert("int3", ::x86::instructions::interrupts::int3 as fn());
//memory
commands.insert("stack", ::memory::print_stack as fn());
commands.insert("page_fault", ::memory::page_fault as fn());
commands.insert("overflow", ::memory::overflow as fn());
commands.insert("overflow", ::memory::overflow as fn());
//pci
commands.insert("lspci", ::pci::lspci as fn());
commands
};
}
fn help() {
println!("The following commands are available:");
for (key, val) in COMMANDS.iter() {
println!("{}", key);
}
}
pub struct Console { pub struct Console {
command: [u8; 10], command: [u8; 10],
command_len: usize, command_len: usize,
@ -54,11 +89,7 @@ impl Console {
} }
b'\n' => { b'\n' => {
unsafe { VGA.write_byte(b'\n'); } unsafe { VGA.write_byte(b'\n'); }
if let Err(msg) = self.exec() { self.exec();
set_color!(Red);
println!("{}", msg);
set_color!();
}
self.command_len = 0; self.command_len = 0;
self.prompt(); self.prompt();
} }
@ -73,256 +104,27 @@ impl Console {
self.command_len += 1; self.command_len += 1;
} }
} }
flush!(); unsafe { VGA.flush(); }
} }
fn get_command(&self) -> Result<fn(), &'static str> {
fn get_command(&self) -> Result<&str, &'static str> {
match core::str::from_utf8(&self.command) { match core::str::from_utf8(&self.command) {
Ok(y) => Ok(&y[..self.command_len]), Ok(y) => {
if let Some(command) = COMMANDS.get(&y[..self.command_len]) {
Ok(*command)
} else {
Err("Command not found, try help")
}
},
Err(_) => Err("Command is not utf8"), Err(_) => Err("Command is not utf8"),
} }
} }
pub fn exec(&self) -> core::result::Result<(), &'static str> { pub fn exec(&self) {
let command = self.get_command(); let command = self.get_command();
if let Err(msg) = command { match command {
return Err(msg)
}
match command.unwrap() {
"help" | "h" => self::help(),
// multiboot
// "memory" => self::mb2_memory(),
// "multiboot" => self::mb2_info(),
// "sections" => self::mb2_sections(),
// ACPI
"acpi" => self::acpi_info(),
"reboot" => self::reboot(),
"shutdown" | "halt" | "q" => self::shutdown(),
// x86 specific
"stack" => self::print_stack(),
"regs" => self::regs(),
"cpu" => self::cpu(),
"int3" => self::int3(),
"overflow" => self::overflow(),
"page_fault" => self::page_fault(),
// time
"uptime" => self::uptime(),
_ => Err("Command unknown. (try help)"),
}
}
}
fn help() -> Result<(), &'static str> {
println!("help | h => print this help");
// println!("memory => Print memory areas");
// println!("multiboot => Print multiboot information");
// println!("sections => Print elf sections");
println!("reboot => reboot");
println!("shutdown | halt | q => acpi shutdown");
println!("acpi => acpi state");
println!("stack => print kernel stack in a fancy way");
println!("regs => print controle register");
println!("cpu => print cpu information");
println!("overflow => triggers a stack overflow");
println!("page_fault => triggers a page fault on 0xdead");
flush!();
Ok(())
}
fn uptime() -> Result<(), &'static str> {
let mut offset = time::OFFSET.lock();
fprintln!("{}s", offset.0 + offset.1 / 1_000_000);
flush!();
Ok(())
}
use x86::instructions::halt;
/// Reboot the kernel
///
/// If reboot failed, will loop on a halt cmd
///
fn reboot() -> ! {
match acpi::reboot() {
Err(msg) => println!("{}", msg), Err(msg) => println!("{}", msg),
_ => println!("Unable to perform ACPI reboot."), Ok(func) => (func)(),
} }
flush!();
unsafe { PS2.ps2_8042_reset() }; // TODO unsafe
println!("Unable to perform 8042 reboot. Kernel will be halted");
flush!();
halt();
}
/// Shutdown the kernel
///
/// If shutdown is performed but failed, will loop on a halt cmd
/// If shutdown cannot be called, return a Err(&str)
///
fn shutdown() -> ! {
match acpi::shutdown() {
Err(msg) => println!("{}", msg),
_ => println!("Unable to perform ACPI shutdown. Kernel will be halted"),
} }
flush!();
halt();
}
fn hexdump(start: usize, end: usize) {
let mut address = 0;
let data = unsafe { core::slice::from_raw_parts_mut(start as *mut u8, end - start) };
while address <= data.len() {
let next_end = core::cmp::min(address + 16, data.len());
print_line(&data[address..next_end], address + start);
address = address + 16;
}
println!("");
}
fn is_control(c: char) -> bool {
!(c >= ' ' && c <= '~')
}
fn print_line(line: &[u8], address: usize) {
print!("\n{:#08x}: ", address);
for byte in line {
print!("{:02x} ", *byte);
}
let length: usize = 16 - line.len();
for _ in 0..length {
print!(" ");
}
print!("|");
for byte in line {
match is_control(*byte as char) {
true => print!("."),
false => print!("{}", *byte as char),
};
}
print!("|");
}
/// Print the kernel stack
pub fn print_stack() -> Result<(), &'static str> {
let esp: usize;
let ebp: usize;
unsafe { asm!("" : "={esp}"(esp), "={ebp}"(ebp):::) };
println!("esp = {:#x}", esp);
println!("ebp = {:#x}", ebp);
println!("size = {:#X} bytes", ebp - esp);
hexdump(esp, ebp);
flush!();
Ok(())
}
// fn mb2_memory() -> Result <(), &'static str> {
// let boot_info = ::multiboot2::boot_info();
// let memory_map_tag = boot_info.memory_map_tag()
// .expect("Memory map tag required");
// println!("memory areas:");
// for area in memory_map_tag.memory_areas() {
// println!(" start: 0x{:x}, length: 0x{:x}",
// area.start_address(), area.size());
// }
// Ok(())
// }
// fn mb2_sections() -> Result <(), &'static str> {
// let boot_info = ::multiboot2::boot_info();
// let elf_sections_tag = boot_info.elf_sections_tag()
// .expect("Elf-sections tag required");
// println!("kernel sections:");
// for section in elf_sections_tag.sections() {
// println!(" {: <10} {:#x}, size: {:#x}, flags: {:#X}",
// section.name(), section.start_address(), section.size(), section.flags());
// }
// Ok(())
// }
// fn mb2_info() -> Result <(), &'static str> {
// let boot_info = context::boot_info();
// let command_line_tag = boot_info.command_line_tag()
// .expect("Elf-sections tag required");
// let bootloader_tag = boot_info.boot_loader_name_tag()
// .expect("Elf-sections tag required");
// println!("bootloader: {}", bootloader_tag.name());
// if command_line_tag.command_line().len() != 0 {
// println!("command line: {}", command_line_tag.command_line());
// }
// Ok(())
// }
pub fn acpi_info() -> Result<(), &'static str> {
acpi::info()?;
Ok(())
}
/// Dump control registers
pub fn regs() -> Result<(), &'static str> {
use x86::registers::control::*;
use x86::instructions::tables::tr;
use x86::instructions::segmentation::*;
use x86::registers::flags::*;
use x86::structures::gdt;
println!("cr0 = {:?}", Cr0::read());
println!("cr3 = {:?}", Cr3::read());
println!("cr4 = {:?}", Cr4::read());
println!("flags= {:?}", flags());
println!("tr = {:?}", tr());
println!("ss = {:?}", ss());
println!("cs = {:?}", cs());
println!("ds = {:?}", ds());
println!("es = {:?}", es());
println!("fs = {:?}", fs());
println!("gs = {:?}", gs());
unsafe {
println!(
"tss = {:#?}",
gdt::Descriptor(::arch::x86::gdt::GDT.table[tr().index() as usize])
);
}
flush!();
Ok(())
}
/// Dump cpu info, should add power management info
pub fn cpu() -> Result<(), &'static str> {
use arch::x86::devices::cpu;
cpu::cpu_info().expect("cpu info not available");
flush!();
Ok(())
}
pub fn int3() -> Result<(), &'static str> {
use x86;
x86::instructions::interrupts::int3();
Ok(())
}
#[allow(unconditional_recursion)]
pub fn overflow() -> Result<(), &'static str> {
fn stack_overflow() {
stack_overflow();
}
stack_overflow();
Ok(())
}
pub fn page_fault() -> Result<(), &'static str> {
unsafe {
*(0xdead as *mut u32) = 42;
};
Ok(())
} }

View file

@ -51,7 +51,9 @@ pub fn kmain() -> ! {
// unsafe VGA // unsafe VGA
unsafe { console::CONSOLE.init(); } unsafe { console::CONSOLE.init(); }
pci::lspci(); if let Ok(slot) = pci::get_ide1() {
println!("found IDE at slot {}", slot);
}
// scheduler WIP // scheduler WIP
// scheduling::schedule(); // scheduling::schedule();
unreachable!(); unreachable!();

View file

@ -101,3 +101,63 @@ pub fn init_noncore() {
} }
} }
} }
/// for console
#[allow(unconditional_recursion)]
pub fn overflow() {
overflow();
}
/// for console
pub fn page_fault() {
unsafe {
*(0xdead as *mut u32) = 42;
};
}
/// Print the kernel stack
pub fn print_stack() {
fn hexdump(start: usize, end: usize) {
let mut address = 0;
let data = unsafe { core::slice::from_raw_parts_mut(start as *mut u8, end - start) };
while address <= data.len() {
let next_end = core::cmp::min(address + 16, data.len());
print_line(&data[address..next_end], address + start);
address = address + 16;
}
println!("");
}
fn is_control(c: char) -> bool {
!(c >= ' ' && c <= '~')
}
fn print_line(line: &[u8], address: usize) {
print!("\n{:#08x}: ", address);
for byte in line {
print!("{:02x} ", *byte);
}
let length: usize = 16 - line.len();
for _ in 0..length {
print!(" ");
}
print!("|");
for byte in line {
match is_control(*byte as char) {
true => print!("."),
false => print!("{}", *byte as char),
};
}
print!("|");
}
let esp: usize;
let ebp: usize;
unsafe { asm!("" : "={esp}"(esp), "={ebp}"(ebp):::) };
println!("esp = {:#x}", esp);
println!("ebp = {:#x}", ebp);
println!("size = {:#X} bytes", ebp - esp);
hexdump(esp, ebp);
flush!();
}

View file

@ -1,22 +1,79 @@
// https://wiki.osdev.org/PCI // https://wiki.osdev.org/PCI
use x86::devices::io::{Pio}; use x86::devices::io::{Pio, Io};
pub static mut PCI_CONFIG_ADDRESS: Pio<u8> = Pio::new(0xCF8); pub static mut PCI_CONFIG_ADDRESS: Pio<u32> = Pio::new(0xCF8);
pub static mut PCI_CONFIG_DATA: Pio<u8> = Pio::new(0xCFC); pub static mut PCI_CONFIG_DATA: Pio<u32> = Pio::new(0xCFC);
pub fn get_ide1() -> Result<u32, ()> {
let bus = 0;
for slot in 0..31 {
let vendor = pci_config_read_word(bus, slot, 0, 0);
if vendor == 0xffff {
continue;
}
let class = pci_config_read_byte(bus, slot, 0, 11);
let subclass = pci_config_read_byte(bus, slot, 0, 10);
if class == 0x01 && subclass == 0x01 {
return Ok(slot);
}
}
Err(())
}
pub fn lspci() { pub fn lspci() {
pci_config_read_word(1, 1, 1, 2); let bus = 0;
for slot in 0..31 {
let vendor = pci_config_read_word(bus, slot, 0, 0);
if vendor == 0xffff {
continue;
}
let header_type = pci_config_read_byte(bus, slot, 0, 14) ;
if header_type & 0x80 != 0 {
// device has multiple functions
for function in 0..0x7 {
let vendor = pci_config_read_word(bus, slot, function, 0);
if vendor != 0xffff {
pci_display(bus, slot, function);
}
}
} else {
pci_display(bus, slot, 0);
}
}
} }
pub fn pci_config_read_word(bus: u32, slot: u32, func: u32, offset: u32) { pub fn pci_display(bus: u32, slot: u32, function: u32) {
// let address: u64 = (bus as u32) << 16 let vendor = pci_config_read_word(bus, slot, function, 0);
// | (slot as u32) << 11 let device = pci_config_read_word(bus, slot, function, 2);
// | (func as u32) << 8 let class = pci_config_read_byte(bus, slot, function, 11);
// | (offset as u32) & 0xfc let subclass = pci_config_read_byte(bus, slot, function, 10);
// | 0x80000000; println!("{}:{}.{} {:#x},{:#x}: {:#x} {:#x}",
let address: u64 = 0x80000000; bus, slot, function, class, subclass, vendor, device);
println!("{} {} {} {}", bus, slot, func, offset); }
println!("{:#x}", address);
println!("{:#b}", address); pub fn pci_access(bus: u32, slot: u32, func: u32, offset: u32) {
let address = (bus as u32) << 16
| (slot as u32) << 11
| (func as u32) << 8
| (offset as u32) & 0xfc
| 0x80000000;
unsafe { PCI_CONFIG_ADDRESS.write(address); }
}
pub fn pci_config_read_doubleword(bus: u32, slot: u32, func: u32, offset: u32) -> u32 {
pci_access(bus, slot, func, offset);
unsafe { PCI_CONFIG_DATA.read() }
}
pub fn pci_config_read_word(bus: u32, slot: u32, func: u32, offset: u32) -> u16 {
pci_access(bus, slot, func, offset);
unsafe { (PCI_CONFIG_DATA.read() >> ((offset & 2) * 8)) as u16 }
}
pub fn pci_config_read_byte(bus: u32, slot: u32, func: u32, offset: u32) -> u8 {
pci_access(bus, slot, func, offset);
unsafe { (PCI_CONFIG_DATA.read() >> ((offset & 3) * 8)) as u8 }
} }

View file

@ -15,3 +15,8 @@ pub fn realtime() -> (u32, u32) {
let sum = start.1 + offset.1; let sum = start.1 + offset.1;
(start.0 + offset.0 + sum / 1_000_000, sum % 1_000_000) (start.0 + offset.0 + sum / 1_000_000, sum % 1_000_000)
} }
pub fn uptime() {
let mut offset = self::OFFSET.lock();
println!("{}s", offset.0 + offset.1 / 1_000_000);
}

View file

@ -4,8 +4,6 @@ pub mod cursor;
pub use self::color::{Color, ColorCode}; pub use self::color::{Color, ColorCode};
use self::cursor::CURSOR; use self::cursor::CURSOR;
use console;
pub static mut VGA: Writer = self::Writer::new(); pub static mut VGA: Writer = self::Writer::new();
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -16,6 +14,7 @@ struct ScreenChar {
} }
// print wrapper macro around vga // print wrapper macro around vga
#[allow(unused_macros)]
macro_rules! print { macro_rules! print {
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
$crate::vga::print(format_args!($($arg)*)); $crate::vga::print(format_args!($($arg)*));
@ -23,6 +22,7 @@ macro_rules! print {
} }
// flushed print // flushed print
#[allow(unused_macros)]
macro_rules! fprint { macro_rules! fprint {
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
print!($($arg)*); print!($($arg)*);
@ -31,12 +31,15 @@ macro_rules! fprint {
} }
// print with a line feed // print with a line feed
#[allow(unused_macros)]
macro_rules! println { macro_rules! println {
() => ({print!("\n")});
($fmt:expr) => (print!(concat!($fmt, "\n"))); ($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
} }
// flushed println // flushed println
#[allow(unused_macros)]
macro_rules! fprintln { macro_rules! fprintln {
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
println!($($arg)*); println!($($arg)*);
@ -73,8 +76,6 @@ pub struct Writer {
pub buffer_pos: usize, pub buffer_pos: usize,
pub color_code: ColorCode, pub color_code: ColorCode,
buffer: [u8; BUFFER_ROWS * BUFFER_COLS], buffer: [u8; BUFFER_ROWS * BUFFER_COLS],
command: [u8; 10],
command_len: usize,
} }
impl Writer { impl Writer {
@ -83,46 +84,36 @@ impl Writer {
buffer_pos: 0, buffer_pos: 0,
color_code: ColorCode::new(Color::White, Color::Black), color_code: ColorCode::new(Color::White, Color::Black),
buffer: [0; BUFFER_ROWS * BUFFER_COLS], buffer: [0; BUFFER_ROWS * BUFFER_COLS],
command: [b'\0'; 10],
command_len: 0,
} }
} }
pub fn get_command(&self) -> Result<&str, &'static str> {
match core::str::from_utf8(&self.command) {
Ok(y) => Ok(&y[..self.command_len]),
Err(_) => Err("Command is not utf8 char"),
}
}
pub fn erase_byte(&mut self) { pub fn erase_byte(&mut self) {
self.buffer_pos -= 2; self.buffer_pos -= 2;
let i = self.buffer_pos; let i = self.buffer_pos;
self.buffer[i] = b' '; self.buffer[i] = b' ';
self.buffer[i + 1] = self.color_code.0; self.buffer[i + 1] = self.color_code.0;
self.flush(); self.flush();
// flush!();
} }
pub fn write_byte(&mut self, byte: u8) { pub fn write_byte(&mut self, byte: u8) {
let i = self.buffer_pos;
match byte { match byte {
b'\n' => { b'\n' => {
let current_line = self.buffer_pos / (BUFFER_COLS); let current_line = self.buffer_pos / (BUFFER_COLS);
self.buffer_pos = (current_line + 1) * BUFFER_COLS; self.buffer_pos = (current_line + 1) * BUFFER_COLS;
} }
byte => { byte => {
self.buffer[i] = byte; self.buffer[self.buffer_pos] = byte;
self.buffer[i + 1] = self.color_code.0; self.buffer[self.buffer_pos + 1] = self.color_code.0;
self.buffer_pos += 2; self.buffer_pos += 2;
} }
} }
if self.buffer_pos >= self.buffer.len() { if self.buffer_pos >= self.buffer.len() {
self.scroll(); self.scroll();
self.flush();
} }
// flushing here is correct but slow
// self.flush();
} }
pub fn write_str(&mut self, s: &str) { pub fn write_str(&mut self, s: &str) {
@ -132,9 +123,7 @@ impl Writer {
} }
fn flush_cursor(&self) { fn flush_cursor(&self) {
unsafe { unsafe { CURSOR.flush(self.buffer_pos / 2); }
CURSOR.flush(self.buffer_pos / 2);
}
} }
pub fn flush(&mut self) { pub fn flush(&mut self) {
@ -157,7 +146,6 @@ impl Writer {
self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col + 1)] = self.buffer[((BUFFER_ROWS - 1) * BUFFER_COLS) + (col + 1)] =
ColorCode::new(Color::White, Color::Black).0; ColorCode::new(Color::White, Color::Black).0;
} }
self.buffer_pos = (BUFFER_ROWS - 1) * BUFFER_COLS; self.buffer_pos = (BUFFER_ROWS - 1) * BUFFER_COLS;
} }
} }