You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
5.0 KiB

8 years ago
  1. //! A basic console driver, using the VGA text-mode buffer.
  2. use core::fmt;
  3. use core::ptr::Unique;
  4. use spin::Mutex;
  5. use volatile::Volatile;
  6. use x86::shared::io;
  7. pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
  8. column_pos: 0, row_pos: 0,
  9. style: CharStyle::new(Color::Cyan, Color::DarkGray),
  10. buffer: unsafe {Unique::new_unchecked(0xb8000 as *mut _)},
  11. });
  12. #[allow(dead_code)]
  13. #[derive(Clone, Copy, Debug)]
  14. #[repr(u8)]
  15. pub enum Color {
  16. Black = 0,
  17. Blue = 1,
  18. Green = 2,
  19. Cyan = 3,
  20. Red = 4,
  21. Magenta = 5,
  22. Brown = 6,
  23. LightGray = 7,
  24. DarkGray = 8,
  25. LightBlue = 9,
  26. LightGreen = 10,
  27. LightCyan = 11,
  28. LightRed = 12,
  29. Pink = 13,
  30. Yellow = 14,
  31. White = 15,
  32. }
  33. /// Foreground + background color pair for a VGA console cell.
  34. #[derive(Clone, Copy, Debug)]
  35. pub struct CharStyle(u8);
  36. impl CharStyle {
  37. pub const fn new(foreground: Color, background: Color) -> CharStyle {
  38. CharStyle((background as u8) << 4 | (foreground as u8))
  39. }
  40. }
  41. /// Character/style pair.
  42. #[derive(Clone, Copy, Debug)]
  43. #[repr(C)]
  44. struct VgaChar {
  45. ascii_char: u8,
  46. style: CharStyle,
  47. }
  48. const BUFFER_HEIGHT: usize = 25;
  49. const BUFFER_WIDTH: usize = 80;
  50. /// Shadows the actual VGA text-mode buffer at `0xb8000`.
  51. struct Buffer {
  52. chars: [[Volatile<VgaChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
  53. }
  54. /// Console-oriented abstraction layer used to write into the VGA text buffer.
  55. /// Maintains a cursor and current style.
  56. pub struct Writer {
  57. /// Current position of the 'write cursor'
  58. row_pos: usize, column_pos: usize,
  59. /// Current style we're writing with
  60. style: CharStyle,
  61. /// This is set up on initialization to shadow `0xb8000`, the VGA text-mode buffer.
  62. buffer: Unique<Buffer>,
  63. }
  64. #[allow(dead_code)]
  65. impl Writer {
  66. /// Write a single byte into the VGA buffer at the cursor location.
  67. /// Increments the cursor location and wraps to the next line if necessary.
  68. pub fn write_byte(&mut self, byte: u8) {
  69. match byte {
  70. b'\n' => self.new_line(),
  71. byte => {
  72. if self.column_pos >= BUFFER_WIDTH {self.new_line();}
  73. let row = self.row_pos;
  74. let col = self.column_pos;
  75. let style = self.style;
  76. self.buffer().chars[row][col].write(VgaChar {
  77. ascii_char: byte,
  78. style: style,
  79. });
  80. self.column_pos += 1;
  81. }
  82. }
  83. }
  84. /// Write a single byte at (row, col).
  85. pub fn write_byte_at(&mut self, byte: u8, row: usize, col: usize) {
  86. self.row_pos = row; self.column_pos = col;
  87. let style = self.style;
  88. self.buffer().chars[row][col].write(VgaChar {ascii_char: byte, style: style});
  89. }
  90. /// Clear the VGA buffer.
  91. pub fn clear_screen(&mut self) {
  92. let clear_style = VgaChar {ascii_char: b' ', style: self.style};
  93. for row in 0..BUFFER_HEIGHT {
  94. for col in 0..BUFFER_WIDTH {
  95. self.buffer().chars[row][col].write(clear_style);
  96. }
  97. }
  98. }
  99. pub fn set_style(&mut self, style: CharStyle) {
  100. self.style = style;
  101. }
  102. pub fn get_style(&self) -> CharStyle {
  103. self.style
  104. }
  105. /// Move the _console cursor_ (blinky bar) to (row, col).
  106. fn move_cursor(&mut self, row: usize, col: usize) {
  107. let pos: u16 = ((row * 80) + col) as u16;
  108. // Lovingly ripped off from wiki.osdev.org/Text_Mode_Cursor
  109. unsafe {
  110. io::outb(0x3d4, 0x0F);
  111. io::outb(0x3d5, (pos & 0xff) as u8);
  112. io::outb(0x3d4, 0x0e);
  113. io::outb(0x3d5, ((pos>>8) & 0xff) as u8);
  114. }
  115. }
  116. /// Move the internal cursor to a new line, scrolling if necessary.
  117. fn new_line(&mut self) {
  118. self.column_pos = 0;
  119. self.row_pos += 1;
  120. if self.row_pos >= BUFFER_HEIGHT {
  121. self.scroll();
  122. }
  123. }
  124. /// Clear a `row` of the VGA buffer.
  125. fn clear_row(&mut self, row: usize) {
  126. let clear_style = VgaChar {ascii_char: b' ', style: self.style};
  127. for col in 0..BUFFER_WIDTH {
  128. self.buffer().chars[row][col].write(clear_style);
  129. }
  130. }
  131. /// Scroll the buffer up by one row.
  132. fn scroll(&mut self) {
  133. for row in 0..(BUFFER_HEIGHT - 1) {
  134. for col in 0..BUFFER_WIDTH {
  135. let c = self.buffer().chars[row + 1][col].read();
  136. self.buffer().chars[row][col].write(c);
  137. }
  138. }
  139. self.clear_row(BUFFER_HEIGHT - 1);
  140. self.column_pos = 0;
  141. self.row_pos = BUFFER_HEIGHT - 1;
  142. }
  143. /// Get the underlying buffer.
  144. fn buffer(&mut self) -> &mut Buffer {
  145. unsafe { self.buffer.as_mut() }
  146. }
  147. }
  148. impl fmt::Write for Writer {
  149. fn write_str(&mut self, s: &str) -> fmt::Result {
  150. for byte in s.bytes() {
  151. self.write_byte(byte);
  152. }
  153. let row = self.row_pos;
  154. let col = self.column_pos;
  155. self.move_cursor(row, col);
  156. Ok(())
  157. }
  158. }