├── .gitignore ├── README.md ├── Cargo.toml ├── src ├── color.rs └── lib.rs └── tests └── vga.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # test-driven vga driver 2 | 3 | this repo is proof of concept for writing a vga driver tdd-style. 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vga" 3 | version = "0.1.0" 4 | authors = ["Steve Klabnik "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub enum Color { 3 | Black = 0x0, 4 | Blue = 0x1, 5 | Green = 0x2, 6 | Cyan = 0x3, 7 | Red = 0x4, 8 | Magenta = 0x5, 9 | Brown = 0x6, 10 | Gray = 0x7, 11 | DarkGray = 0x8, 12 | BrightBlue = 0x9, 13 | BrightGreen = 0xA, 14 | BrightCyan = 0xB, 15 | BrightRed = 0xC, 16 | BrightMagenta = 0xD, 17 | Yellow = 0xE, 18 | White = 0xF, 19 | } 20 | 21 | pub fn colorcode(foreground: Color, background: Color) -> u8 { 22 | ((background as u8) << 4) + (foreground as u8) 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use color::Color; 28 | use color; 29 | 30 | #[test] 31 | fn colorcode() { 32 | assert_eq!(color::colorcode(Color::Blue, Color::BrightMagenta), 0xD1); 33 | assert_eq!(color::colorcode(Color::Yellow, Color::Red), 0x4E); 34 | assert_eq!(color::colorcode(Color::DarkGray, Color::White), 0xF8); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /tests/vga.rs: -------------------------------------------------------------------------------- 1 | extern crate vga; 2 | extern crate core; 3 | 4 | use core::fmt::Write; 5 | use vga::Vga; 6 | 7 | #[test] 8 | fn create() { 9 | let mut mock_memory = vec![0u8; 25 * 80 * 2]; 10 | 11 | Vga::new(&mut mock_memory); 12 | } 13 | 14 | fn check_write(_: T) { } 15 | 16 | #[test] 17 | fn write() { 18 | let mut mock_memory = vec![0u8; 25 * 80 * 2]; 19 | let vga = Vga::new(&mut mock_memory); 20 | check_write(vga); 21 | } 22 | 23 | #[test] 24 | fn flush() { 25 | let mut mock_memory = vec![0u8; 25 * 80 * 2]; 26 | 27 | { 28 | let mut vga = Vga::new(&mut mock_memory); 29 | 30 | vga.write_str("hello").unwrap(); 31 | 32 | vga.flush(); 33 | } 34 | 35 | assert_eq!(mock_memory[0], 'h' as u8); 36 | assert_eq!(mock_memory[1], 0x02); 37 | assert_eq!(mock_memory[2], 'e' as u8); 38 | assert_eq!(mock_memory[3], 0x02); 39 | assert_eq!(mock_memory[4], 'l' as u8); 40 | assert_eq!(mock_memory[5], 0x02); 41 | assert_eq!(mock_memory[6], 'l' as u8); 42 | assert_eq!(mock_memory[7], 0x02); 43 | assert_eq!(mock_memory[8], 'o' as u8); 44 | assert_eq!(mock_memory[9], 0x02); 45 | } 46 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::fmt; 4 | use core::fmt::Write; 5 | 6 | mod color; 7 | use color::Color; 8 | 9 | const ROWS: usize = 25; 10 | const COLS: usize = 80; 11 | const COL_BYTES: usize = COLS * 2; 12 | 13 | pub struct Vga> { 14 | slice: T, 15 | buffer: [u8; ROWS * COL_BYTES], 16 | position: usize, 17 | } 18 | 19 | impl> Vga { 20 | pub fn new(mut slice: T) -> Vga { 21 | // we must have enough bytes of backing storage to make this work. 22 | assert_eq!(slice.as_mut().len(), ROWS * COL_BYTES); 23 | 24 | Vga { 25 | slice: slice, 26 | buffer: [0; ROWS * COL_BYTES], 27 | position: 0, 28 | } 29 | } 30 | 31 | pub fn flush(&mut self) { 32 | self.slice.as_mut().clone_from_slice(&self.buffer); 33 | } 34 | 35 | fn write_byte(&mut self, byte: u8) { 36 | let i = self.position; 37 | 38 | if byte == '\n' as u8 { 39 | let current_line = self.position / (COL_BYTES); 40 | self.position = (current_line + 1) * COL_BYTES; 41 | } else { 42 | self.buffer[i] = byte; 43 | self.buffer[i + 1] = color::colorcode(Color::Green, Color::Black); 44 | 45 | self.position += 2; 46 | } 47 | 48 | if self.position >= self.buffer.len() { 49 | self.scroll(); 50 | } 51 | } 52 | 53 | fn scroll(&mut self) { 54 | for row in 1..ROWS { 55 | for cb in 0..COL_BYTES { 56 | let prev_position = ((row - 1) * COL_BYTES) + cb; 57 | let current_position = (row * COL_BYTES) + cb; 58 | self.buffer[prev_position] = self.buffer[current_position]; 59 | } 60 | } 61 | 62 | for cb in 0..COL_BYTES/2 { 63 | self.buffer[((ROWS - 1) * COL_BYTES) + (cb * 2)] = ' ' as u8; 64 | } 65 | 66 | self.position = (ROWS - 1) * COL_BYTES; 67 | } 68 | } 69 | 70 | impl> Write for Vga { 71 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 72 | for b in s.bytes() { 73 | self.write_byte(b); 74 | } 75 | 76 | Ok(()) 77 | } 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use Vga; 83 | use core::fmt::Write; 84 | 85 | use ROWS; 86 | use COL_BYTES; 87 | 88 | #[test] 89 | fn write_a_letter() { 90 | let mut mock_memory = [0u8; ROWS * COL_BYTES]; 91 | 92 | let mut vga = Vga::new(&mut mock_memory[..]); 93 | 94 | vga.write_str("a").unwrap(); 95 | 96 | assert_eq!(vga.buffer[0], 'a' as u8); 97 | assert_eq!(vga.buffer[1], 0x02); 98 | } 99 | 100 | #[test] 101 | fn write_a_word() { 102 | let mut mock_memory = [0u8; ROWS * COL_BYTES]; 103 | let mut vga = Vga::new(&mut mock_memory[..]); 104 | 105 | let word = "word"; 106 | vga.write_str(word).unwrap(); 107 | 108 | assert_eq!(vga.buffer[0], 'w' as u8); 109 | assert_eq!(vga.buffer[1], 0x02); 110 | assert_eq!(vga.buffer[2], 'o' as u8); 111 | assert_eq!(vga.buffer[3], 0x02); 112 | assert_eq!(vga.buffer[4], 'r' as u8); 113 | assert_eq!(vga.buffer[5], 0x02); 114 | assert_eq!(vga.buffer[6], 'd' as u8); 115 | assert_eq!(vga.buffer[7], 0x02); 116 | } 117 | 118 | #[test] 119 | fn write_multiple_words() { 120 | let mut mock_memory = [0u8; ROWS * COL_BYTES]; 121 | let mut vga = Vga::new(&mut mock_memory[..]); 122 | 123 | vga.write_str("hello ").unwrap(); 124 | vga.write_str("world").unwrap(); 125 | 126 | assert_eq!(vga.buffer[0], 'h' as u8); 127 | assert_eq!(vga.buffer[1], 0x02); 128 | assert_eq!(vga.buffer[2], 'e' as u8); 129 | assert_eq!(vga.buffer[3], 0x02); 130 | assert_eq!(vga.buffer[4], 'l' as u8); 131 | assert_eq!(vga.buffer[5], 0x02); 132 | assert_eq!(vga.buffer[6], 'l' as u8); 133 | assert_eq!(vga.buffer[7], 0x02); 134 | assert_eq!(vga.buffer[8], 'o' as u8); 135 | assert_eq!(vga.buffer[9], 0x02); 136 | assert_eq!(vga.buffer[10], ' ' as u8); 137 | assert_eq!(vga.buffer[11], 0x02); 138 | assert_eq!(vga.buffer[12], 'w' as u8); 139 | assert_eq!(vga.buffer[13], 0x02); 140 | assert_eq!(vga.buffer[14], 'o' as u8); 141 | assert_eq!(vga.buffer[15], 0x02); 142 | assert_eq!(vga.buffer[16], 'r' as u8); 143 | assert_eq!(vga.buffer[17], 0x02); 144 | assert_eq!(vga.buffer[18], 'l' as u8); 145 | assert_eq!(vga.buffer[19], 0x02); 146 | assert_eq!(vga.buffer[20], 'd' as u8); 147 | assert_eq!(vga.buffer[21], 0x02); 148 | } 149 | 150 | #[test] 151 | fn write_newline() { 152 | let mut mock_memory = [0u8; ROWS * COL_BYTES]; 153 | let mut vga = Vga::new(&mut mock_memory[..]); 154 | 155 | vga.write_str("hello\nworld\n!").unwrap(); 156 | 157 | assert_eq!(vga.buffer[0], 'h' as u8); 158 | assert_eq!(vga.buffer[1], 0x02); 159 | assert_eq!(vga.buffer[2], 'e' as u8); 160 | assert_eq!(vga.buffer[3], 0x02); 161 | assert_eq!(vga.buffer[4], 'l' as u8); 162 | assert_eq!(vga.buffer[5], 0x02); 163 | assert_eq!(vga.buffer[6], 'l' as u8); 164 | assert_eq!(vga.buffer[7], 0x02); 165 | assert_eq!(vga.buffer[8], 'o' as u8); 166 | assert_eq!(vga.buffer[9], 0x02); 167 | assert_eq!(vga.buffer[160], 'w' as u8); 168 | assert_eq!(vga.buffer[161], 0x02); 169 | assert_eq!(vga.buffer[162], 'o' as u8); 170 | assert_eq!(vga.buffer[163], 0x02); 171 | assert_eq!(vga.buffer[164], 'r' as u8); 172 | assert_eq!(vga.buffer[165], 0x02); 173 | assert_eq!(vga.buffer[166], 'l' as u8); 174 | assert_eq!(vga.buffer[167], 0x02); 175 | assert_eq!(vga.buffer[168], 'd' as u8); 176 | assert_eq!(vga.buffer[169], 0x02); 177 | assert_eq!(vga.buffer[320], '!' as u8); 178 | assert_eq!(vga.buffer[321], 0x02); 179 | } 180 | 181 | #[test] 182 | fn write_scroll() { 183 | let mut mock_memory = [0u8; ROWS * COL_BYTES]; 184 | let mut vga = Vga::new(&mut mock_memory[..]); 185 | 186 | for b in "abcdefghijklmnopqrstuvwxyz".bytes() { 187 | vga.write_byte(b); 188 | vga.write_byte('\n' as u8); 189 | } 190 | 191 | assert_eq!(vga.buffer[0], 'c' as u8); 192 | for cb in 0..COL_BYTES/2 { 193 | assert_eq!(vga.buffer[(ROWS - 1) * COL_BYTES + (cb * 2)], ' ' as u8); 194 | } 195 | } 196 | 197 | } 198 | --------------------------------------------------------------------------------