commit
13b10c055b
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vga"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
authors = ["Ryan Kennedy <rkennedy9064@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Support for vga specific functions, data structures, and registers."
|
||||
|
@ -19,6 +19,12 @@ repository = "https://github.com/rust-osdev/vga"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
conquer-once = { version = "0.2.0", default-features = false }
|
||||
font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"] }
|
||||
spinning_top = { version = "0.1.0", features = ["nightly"] }
|
||||
x86_64 = "0.9.6"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2.11"
|
||||
default-features = false
|
||||
|
|
21
Changelog.md
21
Changelog.md
|
@ -1,3 +1,23 @@
|
|||
# 0.2.0
|
||||
|
||||
## Breaking
|
||||
|
||||
- Registers moved `vga::registers`.
|
||||
- `Plane` converted to `ReadPlane` and `PlaneMask`.
|
||||
- Register read/write ability removed from `Vga`.
|
||||
- Public access added to `Vga` fields.
|
||||
- `TextWriter::get_width` and `TextWriter::get_height` moved to a `Screen` trait.
|
||||
- `Color16Bit` renamed to `Color16`.
|
||||
|
||||
## Other
|
||||
|
||||
- Added a new `Screen` trait for dealing with the size of a screen.
|
||||
- Added a `GraphicsWriter` trait for dealing with vga graphics.
|
||||
- Added `Graphics640x480x16::clear_screen`.
|
||||
- Added `Graphics640x480x16::draw_line`.
|
||||
- Added `Graphics640x480x16::draw_character`.
|
||||
- Added `vga::drawing::Point` for drawing lines.
|
||||
|
||||
# 0.1.2
|
||||
|
||||
## Breaking
|
||||
|
@ -5,6 +25,7 @@
|
|||
- `ScreenCharacter::new` now takes a `TextModeColor` instead of 2 `Color16Bit`.
|
||||
|
||||
## Other
|
||||
|
||||
- Added `ScreenCharacter::get_character`.
|
||||
- Added `ScreenCharacter::get_color`.
|
||||
- Added `TextWriter::read_character`.
|
||||
|
|
24
README.md
24
README.md
|
@ -9,16 +9,34 @@ this crate to work properly.
|
|||
|
||||
**Note: This crate is currently experimental and subject to change since it's in active development.**
|
||||
|
||||
## Usage
|
||||
## Text Mode
|
||||
```rust
|
||||
use vga::colors::{Color16Bit, TextModeColor};
|
||||
use vga::colors::{Color16, TextModeColor};
|
||||
use vga::writers::{ScreenCharacter, TextWriter, Text80x25};
|
||||
|
||||
let text_mode = Text80x25::new();
|
||||
let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
let color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
let screen_character = ScreenCharacter::new(b'T', color);
|
||||
|
||||
text_mode.set_mode();
|
||||
text_mode.clear_screen();
|
||||
text_mode.write_character(0, 0, screen_character);
|
||||
```
|
||||
|
||||
## Graphics Mode
|
||||
```rust
|
||||
use vga::colors::Color16;
|
||||
use vga::writers::{Graphics640x480x16, GraphicsWriter};
|
||||
|
||||
let mode = Graphics640x480x16::new();
|
||||
mode.set_mode();
|
||||
mode.clear_screen(Color16::Black);
|
||||
mode.draw_line((80, 60), (80, 420), Color16::White);
|
||||
mode.draw_line((80, 60), (540, 60), Color16::White);
|
||||
mode.draw_line((80, 420), (540, 420), Color16::White);
|
||||
mode.draw_line((540, 420), (540, 60), Color16::White);
|
||||
mode.draw_line((80, 90), (540, 90), Color16::White);
|
||||
for (offset, character) in "Hello World!".chars().enumerate() {
|
||||
mode.draw_character(270 + offset * 8, 72, character, Color16::White)
|
||||
}
|
||||
```
|
||||
|
|
|
@ -6,7 +6,7 @@ pub const PALETTE_SIZE: usize = 768;
|
|||
/// Represents a 16 bit color used for vga display.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Color16Bit {
|
||||
pub enum Color16 {
|
||||
/// Represents the color `Black (0x0)`.
|
||||
Black = 0x0,
|
||||
/// Represents the color `Blue (0x1)`.
|
||||
|
@ -41,6 +41,12 @@ pub enum Color16Bit {
|
|||
White = 0xF,
|
||||
}
|
||||
|
||||
impl From<Color16> for u8 {
|
||||
fn from(value: Color16) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a color for vga text modes.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
|
@ -49,17 +55,17 @@ pub struct TextModeColor(u8);
|
|||
impl TextModeColor {
|
||||
/// Returns a new `TextModeColor` given the specified `foreground`
|
||||
/// and `background` color.
|
||||
pub const fn new(foreground: Color16Bit, background: Color16Bit) -> TextModeColor {
|
||||
pub const fn new(foreground: Color16, background: Color16) -> TextModeColor {
|
||||
TextModeColor((background as u8) << 4 | (foreground as u8))
|
||||
}
|
||||
|
||||
/// Sets the background color given the specified `background`;
|
||||
pub fn set_background(&mut self, background: Color16Bit) {
|
||||
pub fn set_background(&mut self, background: Color16) {
|
||||
self.0 = (background as u8) << 4 | (self.0 & 0x0F);
|
||||
}
|
||||
|
||||
/// Sets the foreground color given the specified `foreground`.
|
||||
pub fn set_foreground(&mut self, foreground: Color16Bit) {
|
||||
pub fn set_foreground(&mut self, foreground: Color16) {
|
||||
self.0 = foreground as u8;
|
||||
}
|
||||
}
|
||||
|
@ -122,15 +128,15 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_set_foreground() {
|
||||
let mut color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
color.set_foreground(Color16Bit::Red);
|
||||
assert_eq!(color.0 & 0x0F, Color16Bit::Red as u8);
|
||||
let mut color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
color.set_foreground(Color16::Red);
|
||||
assert_eq!(color.0 & 0x0F, Color16::Red as u8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_background() {
|
||||
let mut color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
color.set_background(Color16Bit::DarkGrey);
|
||||
assert_eq!(color.0 >> 4, Color16Bit::DarkGrey as u8);
|
||||
let mut color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
color.set_background(Color16::DarkGrey);
|
||||
assert_eq!(color.0 >> 4, Color16::DarkGrey as u8);
|
||||
}
|
||||
}
|
||||
|
|
57
src/drawing/bresenham.rs
Normal file
57
src/drawing/bresenham.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use super::{Octant, Point, SignedNum};
|
||||
|
||||
pub(crate) struct Bresenham<T> {
|
||||
point: Point<T>,
|
||||
end_x: T,
|
||||
delta_x: T,
|
||||
delta_y: T,
|
||||
error: T,
|
||||
octant: Octant,
|
||||
}
|
||||
|
||||
impl<T: SignedNum> Bresenham<T> {
|
||||
#[inline]
|
||||
pub fn new(start: Point<T>, end: Point<T>) -> Self {
|
||||
let octant = Octant::new(start, end);
|
||||
let start = octant.to(start);
|
||||
let end = octant.to(end);
|
||||
|
||||
let delta_x = end.0 - start.0;
|
||||
let delta_y = end.1 - start.1;
|
||||
|
||||
Self {
|
||||
delta_x,
|
||||
delta_y,
|
||||
octant,
|
||||
point: start,
|
||||
end_x: end.0,
|
||||
error: delta_y - delta_x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for Bresenham<T>
|
||||
where
|
||||
T: SignedNum,
|
||||
{
|
||||
type Item = Point<T>;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.point.0 <= self.end_x {
|
||||
let point = self.octant.from(self.point);
|
||||
|
||||
if self.error >= T::zero() {
|
||||
self.point.1 += T::one();
|
||||
self.error -= self.delta_x;
|
||||
}
|
||||
|
||||
self.point.0 += T::one();
|
||||
self.error += self.delta_y;
|
||||
|
||||
Some(point)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
20
src/drawing/mod.rs
Normal file
20
src/drawing/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
//! Common functionality for drawing in vga graphics mode.
|
||||
//! Original implementation here https://github.com/expenses/line_drawing.
|
||||
use num_traits::{NumAssignOps, NumCast, Signed};
|
||||
|
||||
mod bresenham;
|
||||
mod octant;
|
||||
|
||||
pub(crate) use bresenham::Bresenham;
|
||||
use octant::Octant;
|
||||
|
||||
/// A point in 2D space.
|
||||
pub type Point<T> = (T, T);
|
||||
|
||||
pub(crate) trait SignedNum: Signed + Ord + Copy + NumCast + NumAssignOps {
|
||||
fn cast<T: NumCast>(value: T) -> Self {
|
||||
NumCast::from(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Signed + Ord + Copy + NumCast + NumAssignOps> SignedNum for T {}
|
75
src/drawing/octant.rs
Normal file
75
src/drawing/octant.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use super::Point;
|
||||
use core::ops::{Neg, Sub};
|
||||
use num_traits::Zero;
|
||||
|
||||
/// A simple octant struct for transforming line points.
|
||||
pub struct Octant {
|
||||
value: u8,
|
||||
}
|
||||
|
||||
impl Octant {
|
||||
#[inline]
|
||||
/// Get the relevant octant from a start and end point.
|
||||
pub fn new<T>(start: Point<T>, end: Point<T>) -> Self
|
||||
where
|
||||
T: Sub<Output = T> + Neg<Output = T> + PartialOrd + Zero,
|
||||
{
|
||||
let mut value = 0;
|
||||
let mut dx = end.0 - start.0;
|
||||
let mut dy = end.1 - start.1;
|
||||
|
||||
if dy < T::zero() {
|
||||
dx = -dx;
|
||||
dy = -dy;
|
||||
value += 4;
|
||||
}
|
||||
|
||||
if dx < T::zero() {
|
||||
let tmp = dx;
|
||||
dx = dy;
|
||||
dy = -tmp;
|
||||
value += 2;
|
||||
}
|
||||
|
||||
if dx < dy {
|
||||
value += 1;
|
||||
}
|
||||
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// Convert a point to its position in the octant.
|
||||
#[inline]
|
||||
pub fn to<T>(&self, point: Point<T>) -> Point<T>
|
||||
where
|
||||
T: Neg<Output = T>,
|
||||
{
|
||||
match self.value {
|
||||
0 => (point.0, point.1),
|
||||
1 => (point.1, point.0),
|
||||
2 => (point.1, -point.0),
|
||||
3 => (-point.0, point.1),
|
||||
4 => (-point.0, -point.1),
|
||||
5 => (-point.1, -point.0),
|
||||
6 => (-point.1, point.0),
|
||||
7 => (point.0, -point.1),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a point from its position in the octant.
|
||||
#[inline]
|
||||
pub fn from<T: Neg<Output = T>>(&self, point: Point<T>) -> Point<T> {
|
||||
match self.value {
|
||||
0 => (point.0, point.1),
|
||||
1 => (point.1, point.0),
|
||||
2 => (-point.1, point.0),
|
||||
3 => (-point.0, point.1),
|
||||
4 => (-point.0, -point.1),
|
||||
5 => (-point.1, -point.0),
|
||||
6 => (point.1, -point.0),
|
||||
7 => (point.0, -point.1),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
pub mod colors;
|
||||
pub mod configurations;
|
||||
pub mod drawing;
|
||||
pub mod fonts;
|
||||
pub mod registers;
|
||||
pub mod vga;
|
||||
|
|
510
src/registers.rs
510
src/registers.rs
|
@ -1,510 +0,0 @@
|
|||
//! Common registers used in vga programming.
|
||||
|
||||
use crate::colors::PALETTE_SIZE;
|
||||
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
|
||||
|
||||
const ST00_READ_ADDRESS: u16 = 0x3C2;
|
||||
const ST01_READ_CGA_ADDRESS: u16 = 0x3DA;
|
||||
const ST01_READ_MDA_ADDRESS: u16 = 0x3BA;
|
||||
const FCR_READ_ADDRESS: u16 = 0x3CA;
|
||||
const FCR_CGA_WRITE_ADDRESS: u16 = 0x3DA;
|
||||
const FCR_MDA_WRITE_ADDRESS: u16 = 0x3BA;
|
||||
const MSR_READ_ADDRESS: u16 = 0x3CC;
|
||||
const MSR_WRITE_ADDRESS: u16 = 0x3C2;
|
||||
|
||||
const SRX_INDEX_ADDRESS: u16 = 0x3C4;
|
||||
const SRX_DATA_ADDRESS: u16 = 0x3C5;
|
||||
|
||||
const GRX_INDEX_ADDRESS: u16 = 0x3CE;
|
||||
const GRX_DATA_ADDRESS: u16 = 0x3CF;
|
||||
|
||||
const ARX_INDEX_ADDRESS: u16 = 0x3C0;
|
||||
const ARX_DATA_ADDRESS: u16 = 0x3C1;
|
||||
|
||||
const CRX_INDEX_CGA_ADDRESS: u16 = 0x3D4;
|
||||
const CRX_INDEX_MDA_ADDRESS: u16 = 0x3B4;
|
||||
const CRX_DATA_CGA_ADDRESS: u16 = 0x3D5;
|
||||
const CRX_DATA_MDA_ADDRESS: u16 = 0x3B5;
|
||||
|
||||
const COLOR_PALETTE_DATA_ADDRESS: u16 = 0x3C9;
|
||||
const COLOR_PALETTE_INDEX_READ_ADDRESS: u16 = 0x3C7;
|
||||
const COLOR_PALETTE_INDEX_WRITE_ADDRESSS: u16 = 0x3C8;
|
||||
|
||||
/// Represents a vga emulation mode.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum EmulationMode {
|
||||
/// Represents a monochrome emulation mode.
|
||||
Mda = 0x0,
|
||||
/// Respresents a color emulation mode.
|
||||
Cga = 0x1,
|
||||
}
|
||||
|
||||
impl From<u8> for EmulationMode {
|
||||
fn from(value: u8) -> EmulationMode {
|
||||
match value {
|
||||
0x0 => EmulationMode::Mda,
|
||||
0x1 => EmulationMode::Cga,
|
||||
_ => panic!("{} is an invalid emulation mode", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the seqeuncer registers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum SequencerIndex {
|
||||
/// Represents the `Sequencer Reset` register index.
|
||||
SequencerReset = 0x0,
|
||||
/// Represents the `Clocking Mode` register index.
|
||||
ClockingMode = 0x1,
|
||||
/// Represents the Plane/Map mask register index.
|
||||
PlaneMask = 0x2,
|
||||
/// Represents the `Character Font` register index.
|
||||
CharacterFont = 0x3,
|
||||
/// Represents the `Memory Mode` register index.
|
||||
MemoryMode = 0x4,
|
||||
/// Represents the `Horizontal Character Counter Reset` register index.
|
||||
CounterReset = 0x7,
|
||||
}
|
||||
|
||||
impl From<SequencerIndex> for u8 {
|
||||
fn from(value: SequencerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the graphics controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum GraphicsControllerIndex {
|
||||
/// Represents the `Set/Reset` register index.
|
||||
SetReset = 0x0,
|
||||
/// Represents the `Enable Set/Reset` register index.
|
||||
EnableSetReset = 0x1,
|
||||
/// Represents the `Color Compare` register index.
|
||||
ColorCompare = 0x2,
|
||||
/// Represents the `Data Rotate` register index.
|
||||
DataRotate = 0x3,
|
||||
/// Represents the `Read Plane Select` register index.
|
||||
ReadPlaneSelect = 0x4,
|
||||
/// Represents the `Graphics Mode` register index.
|
||||
GraphicsMode = 0x5,
|
||||
/// Represents the `Miscellaneous` register index.
|
||||
Miscellaneous = 0x6,
|
||||
/// Represents the `Color Don't Care` register index.
|
||||
ColorDontCare = 0x7,
|
||||
/// Represents the `Bit Mask` register index.
|
||||
BitMask = 0x8,
|
||||
/// Represents the `Address Mapping` register index.
|
||||
AddressMapping = 0x10,
|
||||
/// Represents the `Page Selector` register index.
|
||||
PageSelector = 0x11,
|
||||
/// Represents the `Software Flags` register index.
|
||||
SoftwareFlags = 0x18,
|
||||
}
|
||||
|
||||
impl From<GraphicsControllerIndex> for u8 {
|
||||
fn from(value: GraphicsControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the attribute controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum AttributeControllerIndex {
|
||||
/// Represents the `Palette 0` register index.
|
||||
PaletteRegister0 = 0x00,
|
||||
/// Represents the `Palette 1` register index.
|
||||
PaletteRegister1 = 0x01,
|
||||
/// Represents the `Palette 2` register index.
|
||||
PaletteRegister2 = 0x02,
|
||||
/// Represents the `Palette 3` register index.
|
||||
PaletteRegister3 = 0x03,
|
||||
/// Represents the `Palette 4` register index.
|
||||
PaletteRegister4 = 0x04,
|
||||
/// Represents the `Palette 5` register index.
|
||||
PaletteRegister5 = 0x05,
|
||||
/// Represents the `Palette 6` register index.
|
||||
PaletteRegister6 = 0x06,
|
||||
/// Represents the `Palette 7` register index.
|
||||
PaletteRegister7 = 0x07,
|
||||
/// Represents the `Palette 8` register index.
|
||||
PaletteRegister8 = 0x08,
|
||||
/// Represents the `Palette 9` register index.
|
||||
PaletteRegister9 = 0x09,
|
||||
/// Represents the `Palette A` register index.
|
||||
PaletteRegisterA = 0x0A,
|
||||
/// Represents the `Palette B` register index.
|
||||
PaletteRegisterB = 0x0B,
|
||||
/// Represents the `Palette C` register index.
|
||||
PaletteRegisterC = 0x0C,
|
||||
/// Represents the `Palette D` register index.
|
||||
PaletteRegisterD = 0x0D,
|
||||
/// Represents the `Palette E` register index.
|
||||
PaletteRegisterE = 0x0E,
|
||||
/// Represents the `Palette F` register index.
|
||||
PaletteRegisterF = 0x0F,
|
||||
/// Represents the `Mode Control` register index.
|
||||
ModeControl = 0x10,
|
||||
/// Represents the `Overscan Color` register index.
|
||||
OverscanColor = 0x11,
|
||||
/// Represents the `Memory Plane Enable` register index.
|
||||
MemoryPlaneEnable = 0x12,
|
||||
/// Represents the `Horizontal Pixel Panning` register index.
|
||||
HorizontalPixelPanning = 0x13,
|
||||
/// Represents the `Color Select` register index.
|
||||
ColorSelect = 0x14,
|
||||
}
|
||||
|
||||
impl From<AttributeControllerIndex> for u8 {
|
||||
fn from(value: AttributeControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the crtc controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum CrtcControllerIndex {
|
||||
/// Represents the `Horizontal Total` register index.
|
||||
HorizontalTotal = 0x00,
|
||||
/// Represents the `Horizontal Display Enable End` register index.
|
||||
HorizontalDisplayEnableEnd = 0x01,
|
||||
/// Represents the `Horizontal Blanking Start` register index.
|
||||
HorizontalBlankingStart = 0x02,
|
||||
/// Represents the `Horizontal Blanking End` register index.
|
||||
HorizontalBlankingEnd = 0x03,
|
||||
/// Represents the `Horizontal Sync Start` register index.
|
||||
HorizontalSyncStart = 0x04,
|
||||
/// Represents the `Horizontal Sync End` register index.
|
||||
HorizontalSyncEnd = 0x05,
|
||||
/// Represents the `Vertical Total` register index.
|
||||
VeritcalTotal = 0x06,
|
||||
/// Represents the `Overflow` register index.
|
||||
Overflow = 0x07,
|
||||
/// Represents the `Preset Row Scan` register index.
|
||||
PresetRowScan = 0x08,
|
||||
/// Represents the `Maximum Scan Line` register index.
|
||||
MaximumScanLine = 0x09,
|
||||
/// Represents the `Text Cursor Start` register index.
|
||||
TextCursorStart = 0x0A,
|
||||
/// Represents the `Text Cursor End` register index.
|
||||
TextCursorEnd = 0x0B,
|
||||
/// Represents the `Start Address High` register index.
|
||||
StartAddressHigh = 0x0C,
|
||||
/// Represents the `Start Address Low` register index.
|
||||
StartAddressLow = 0x0D,
|
||||
/// Represents the `Text Cursor Location High` register index.
|
||||
TextCursorLocationHigh = 0x0E,
|
||||
/// Represents the `Text Cursor Location Low` register index.
|
||||
TextCursorLocationLow = 0x0F,
|
||||
/// Represents the `Vertical Sync Start` register index.
|
||||
VerticalSyncStart = 0x10,
|
||||
/// Represents the `Vertical Sync End` register index.
|
||||
VerticalSyncEnd = 0x11,
|
||||
/// Represents the `Vertical Display Enable End` register index
|
||||
VerticalDisplayEnableEnd = 0x12,
|
||||
/// Represents the `Offset` register index.
|
||||
Offset = 0x13,
|
||||
/// Represents the `Underline Location` register index.
|
||||
UnderlineLocation = 0x14,
|
||||
/// Represents the `Vertical Blanking Start` register index.
|
||||
VerticalBlankingStart = 0x15,
|
||||
/// Represents the `Vertical Blanking End` register index.
|
||||
VerticalBlankingEnd = 0x16,
|
||||
/// Represents the `Mode Control` register index.
|
||||
ModeControl = 0x17,
|
||||
/// Represents the `Line Compare` register index.
|
||||
LineCompare = 0x18,
|
||||
/// Represents the `Memory Read Latch Data` register index.
|
||||
MemoryReadLatchData = 0x22,
|
||||
/// Represents the `Toggle State Of Attribute Controller` register index.
|
||||
ToggleStateOfAttributeController = 0x24,
|
||||
}
|
||||
|
||||
impl From<CrtcControllerIndex> for u8 {
|
||||
fn from(value: CrtcControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct GeneralRegisters {
|
||||
st00_read: PortReadOnly<u8>,
|
||||
st01_read_cga: PortReadOnly<u8>,
|
||||
st01_read_mda: PortReadOnly<u8>,
|
||||
fcr_read: PortReadOnly<u8>,
|
||||
fcr_write_cga: PortWriteOnly<u8>,
|
||||
fcr_write_mda: PortWriteOnly<u8>,
|
||||
msr_read: PortReadOnly<u8>,
|
||||
msr_write: PortWriteOnly<u8>,
|
||||
}
|
||||
|
||||
impl GeneralRegisters {
|
||||
pub fn new() -> GeneralRegisters {
|
||||
GeneralRegisters {
|
||||
st00_read: PortReadOnly::new(ST00_READ_ADDRESS),
|
||||
st01_read_cga: PortReadOnly::new(ST01_READ_CGA_ADDRESS),
|
||||
st01_read_mda: PortReadOnly::new(ST01_READ_MDA_ADDRESS),
|
||||
fcr_read: PortReadOnly::new(FCR_READ_ADDRESS),
|
||||
fcr_write_cga: PortWriteOnly::new(FCR_CGA_WRITE_ADDRESS),
|
||||
fcr_write_mda: PortWriteOnly::new(FCR_MDA_WRITE_ADDRESS),
|
||||
msr_read: PortReadOnly::new(MSR_READ_ADDRESS),
|
||||
msr_write: PortWriteOnly::new(MSR_WRITE_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_msr(&mut self) -> u8 {
|
||||
unsafe { self.msr_read.read() }
|
||||
}
|
||||
|
||||
pub fn write_msr(&mut self, value: u8) {
|
||||
unsafe {
|
||||
self.msr_write.write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SequencerRegisters {
|
||||
srx_index: Port<u8>,
|
||||
srx_data: Port<u8>,
|
||||
}
|
||||
|
||||
impl SequencerRegisters {
|
||||
pub fn new() -> SequencerRegisters {
|
||||
SequencerRegisters {
|
||||
srx_index: Port::new(SRX_INDEX_ADDRESS),
|
||||
srx_data: Port::new(SRX_DATA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, index: SequencerIndex) -> u8 {
|
||||
self.set_index(index);
|
||||
unsafe { self.srx_data.read() }
|
||||
}
|
||||
|
||||
pub fn write(&mut self, index: SequencerIndex, value: u8) {
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.srx_data.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: SequencerIndex) {
|
||||
unsafe {
|
||||
self.srx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct GraphicsControllerRegisters {
|
||||
grx_index: Port<u8>,
|
||||
grx_data: Port<u8>,
|
||||
}
|
||||
|
||||
impl GraphicsControllerRegisters {
|
||||
pub fn new() -> GraphicsControllerRegisters {
|
||||
GraphicsControllerRegisters {
|
||||
grx_index: Port::new(GRX_INDEX_ADDRESS),
|
||||
grx_data: Port::new(GRX_DATA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, index: GraphicsControllerIndex) -> u8 {
|
||||
self.set_index(index);
|
||||
unsafe { self.grx_data.read() }
|
||||
}
|
||||
|
||||
pub fn write(&mut self, index: GraphicsControllerIndex, value: u8) {
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.grx_data.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: GraphicsControllerIndex) {
|
||||
unsafe {
|
||||
self.grx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AttributeControllerRegisters {
|
||||
arx_index: Port<u8>,
|
||||
arx_data: Port<u8>,
|
||||
st01_read_cga: Port<u8>,
|
||||
st01_read_mda: Port<u8>,
|
||||
}
|
||||
|
||||
impl AttributeControllerRegisters {
|
||||
pub fn new() -> AttributeControllerRegisters {
|
||||
AttributeControllerRegisters {
|
||||
arx_index: Port::new(ARX_INDEX_ADDRESS),
|
||||
arx_data: Port::new(ARX_DATA_ADDRESS),
|
||||
st01_read_cga: Port::new(ST01_READ_CGA_ADDRESS),
|
||||
st01_read_mda: Port::new(ST01_READ_MDA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 {
|
||||
self.toggle_index(emulation_mode);
|
||||
self.set_index(index);
|
||||
unsafe { self.arx_data.read() }
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
&mut self,
|
||||
emulation_mode: EmulationMode,
|
||||
index: AttributeControllerIndex,
|
||||
value: u8,
|
||||
) {
|
||||
self.toggle_index(emulation_mode);
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.arx_index.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: AttributeControllerIndex) {
|
||||
unsafe {
|
||||
self.arx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_index(&mut self, emulation_mode: EmulationMode) {
|
||||
let st01_read = match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.st01_read_cga,
|
||||
EmulationMode::Mda => &mut self.st01_read_mda,
|
||||
};
|
||||
unsafe {
|
||||
st01_read.read();
|
||||
}
|
||||
}
|
||||
|
||||
/// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit.
|
||||
/// Clearing this bit will cause the VGA display data to become all 00 index values. For the default
|
||||
/// palette, this will cause a black screen. The video timing signals continue. Another control bit will
|
||||
/// turn video off and stop the data fetches.
|
||||
///
|
||||
/// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU.
|
||||
///
|
||||
/// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU.
|
||||
pub fn blank_screen(&mut self, emulation_mode: EmulationMode) {
|
||||
self.toggle_index(emulation_mode);
|
||||
let arx_index_value = unsafe { self.arx_index.read() };
|
||||
unsafe {
|
||||
self.arx_index.write(arx_index_value & 0xDF);
|
||||
}
|
||||
}
|
||||
|
||||
/// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit.
|
||||
/// Clearing this bit will cause the VGA display data to become all 00 index values. For the default
|
||||
/// palette, this will cause a black screen. The video timing signals continue. Another control bit will
|
||||
/// turn video off and stop the data fetches.
|
||||
///
|
||||
/// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU.
|
||||
///
|
||||
/// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU.
|
||||
pub fn unblank_screen(&mut self, emulation_mode: EmulationMode) {
|
||||
self.toggle_index(emulation_mode);
|
||||
let arx_index_value = unsafe { self.arx_index.read() };
|
||||
unsafe {
|
||||
self.arx_index.write(arx_index_value | 0x20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CrtcControllerRegisters {
|
||||
crx_index_cga: Port<u8>,
|
||||
crx_index_mda: Port<u8>,
|
||||
crx_data_cga: Port<u8>,
|
||||
crx_data_mda: Port<u8>,
|
||||
}
|
||||
|
||||
impl CrtcControllerRegisters {
|
||||
pub fn new() -> CrtcControllerRegisters {
|
||||
CrtcControllerRegisters {
|
||||
crx_index_cga: Port::new(CRX_INDEX_CGA_ADDRESS),
|
||||
crx_index_mda: Port::new(CRX_INDEX_MDA_ADDRESS),
|
||||
crx_data_cga: Port::new(CRX_DATA_CGA_ADDRESS),
|
||||
crx_data_mda: Port::new(CRX_DATA_MDA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) -> u8 {
|
||||
self.set_index(emulation_mode, index);
|
||||
unsafe { self.get_data_port(emulation_mode).read() }
|
||||
}
|
||||
|
||||
pub fn write(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex, value: u8) {
|
||||
self.set_index(emulation_mode, index);
|
||||
unsafe {
|
||||
self.get_data_port(emulation_mode).write(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) {
|
||||
unsafe {
|
||||
self.get_index_port(emulation_mode).write(u8::from(index));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_port(&mut self, emulation_mode: EmulationMode) -> &mut Port<u8> {
|
||||
match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.crx_data_cga,
|
||||
EmulationMode::Mda => &mut self.crx_data_mda,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index_port(&mut self, emulation_mode: EmulationMode) -> &mut Port<u8> {
|
||||
match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.crx_index_cga,
|
||||
EmulationMode::Mda => &mut self.crx_index_mda,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ColorPaletteRegisters {
|
||||
data_port: Port<u8>,
|
||||
index_read_port: Port<u8>,
|
||||
index_write_port: Port<u8>,
|
||||
}
|
||||
|
||||
impl ColorPaletteRegisters {
|
||||
pub fn new() -> ColorPaletteRegisters {
|
||||
ColorPaletteRegisters {
|
||||
data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS),
|
||||
index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS),
|
||||
index_write_port: Port::new(COLOR_PALETTE_INDEX_WRITE_ADDRESSS),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) {
|
||||
unsafe {
|
||||
self.index_write_port.write(0);
|
||||
}
|
||||
for i in palette.iter() {
|
||||
unsafe {
|
||||
self.data_port.write(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) {
|
||||
unsafe {
|
||||
self.index_read_port.write(0);
|
||||
}
|
||||
for byte in palette.iter_mut().take(PALETTE_SIZE) {
|
||||
unsafe {
|
||||
*byte = self.data_port.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
150
src/registers/attribute_controller.rs
Normal file
150
src/registers/attribute_controller.rs
Normal file
|
@ -0,0 +1,150 @@
|
|||
use super::{
|
||||
EmulationMode, ARX_DATA_ADDRESS, ARX_INDEX_ADDRESS, ST01_READ_CGA_ADDRESS,
|
||||
ST01_READ_MDA_ADDRESS,
|
||||
};
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
/// Represents an index for the attribute controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum AttributeControllerIndex {
|
||||
/// Represents the `Palette 0` register index.
|
||||
PaletteRegister0 = 0x00,
|
||||
/// Represents the `Palette 1` register index.
|
||||
PaletteRegister1 = 0x01,
|
||||
/// Represents the `Palette 2` register index.
|
||||
PaletteRegister2 = 0x02,
|
||||
/// Represents the `Palette 3` register index.
|
||||
PaletteRegister3 = 0x03,
|
||||
/// Represents the `Palette 4` register index.
|
||||
PaletteRegister4 = 0x04,
|
||||
/// Represents the `Palette 5` register index.
|
||||
PaletteRegister5 = 0x05,
|
||||
/// Represents the `Palette 6` register index.
|
||||
PaletteRegister6 = 0x06,
|
||||
/// Represents the `Palette 7` register index.
|
||||
PaletteRegister7 = 0x07,
|
||||
/// Represents the `Palette 8` register index.
|
||||
PaletteRegister8 = 0x08,
|
||||
/// Represents the `Palette 9` register index.
|
||||
PaletteRegister9 = 0x09,
|
||||
/// Represents the `Palette A` register index.
|
||||
PaletteRegisterA = 0x0A,
|
||||
/// Represents the `Palette B` register index.
|
||||
PaletteRegisterB = 0x0B,
|
||||
/// Represents the `Palette C` register index.
|
||||
PaletteRegisterC = 0x0C,
|
||||
/// Represents the `Palette D` register index.
|
||||
PaletteRegisterD = 0x0D,
|
||||
/// Represents the `Palette E` register index.
|
||||
PaletteRegisterE = 0x0E,
|
||||
/// Represents the `Palette F` register index.
|
||||
PaletteRegisterF = 0x0F,
|
||||
/// Represents the `Mode Control` register index.
|
||||
ModeControl = 0x10,
|
||||
/// Represents the `Overscan Color` register index.
|
||||
OverscanColor = 0x11,
|
||||
/// Represents the `Memory Plane Enable` register index.
|
||||
MemoryPlaneEnable = 0x12,
|
||||
/// Represents the `Horizontal Pixel Panning` register index.
|
||||
HorizontalPixelPanning = 0x13,
|
||||
/// Represents the `Color Select` register index.
|
||||
ColorSelect = 0x14,
|
||||
}
|
||||
|
||||
impl From<AttributeControllerIndex> for u8 {
|
||||
fn from(value: AttributeControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the attribute controller registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct AttributeControllerRegisters {
|
||||
arx_index: Port<u8>,
|
||||
arx_data: Port<u8>,
|
||||
st01_read_cga: Port<u8>,
|
||||
st01_read_mda: Port<u8>,
|
||||
}
|
||||
|
||||
impl AttributeControllerRegisters {
|
||||
pub(crate) fn new() -> AttributeControllerRegisters {
|
||||
AttributeControllerRegisters {
|
||||
arx_index: Port::new(ARX_INDEX_ADDRESS),
|
||||
arx_data: Port::new(ARX_DATA_ADDRESS),
|
||||
st01_read_cga: Port::new(ST01_READ_CGA_ADDRESS),
|
||||
st01_read_mda: Port::new(ST01_READ_MDA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current value of the attribute controller, as specified
|
||||
/// by `emulation_mode` and `index`.
|
||||
pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 {
|
||||
self.toggle_index(emulation_mode);
|
||||
self.set_index(index);
|
||||
unsafe { self.arx_data.read() }
|
||||
}
|
||||
|
||||
/// Writes the `value` to the attribute controller, as specified
|
||||
/// `emulation_mode` and `index`.
|
||||
pub fn write(
|
||||
&mut self,
|
||||
emulation_mode: EmulationMode,
|
||||
index: AttributeControllerIndex,
|
||||
value: u8,
|
||||
) {
|
||||
self.toggle_index(emulation_mode);
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.arx_index.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit.
|
||||
/// Clearing this bit will cause the VGA display data to become all 00 index values. For the default
|
||||
/// palette, this will cause a black screen. The video timing signals continue. Another control bit will
|
||||
/// turn video off and stop the data fetches.
|
||||
///
|
||||
/// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU.
|
||||
///
|
||||
/// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU.
|
||||
pub fn blank_screen(&mut self, emulation_mode: EmulationMode) {
|
||||
self.toggle_index(emulation_mode);
|
||||
let arx_index_value = unsafe { self.arx_index.read() };
|
||||
unsafe {
|
||||
self.arx_index.write(arx_index_value & 0xDF);
|
||||
}
|
||||
}
|
||||
|
||||
/// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit.
|
||||
/// Clearing this bit will cause the VGA display data to become all 00 index values. For the default
|
||||
/// palette, this will cause a black screen. The video timing signals continue. Another control bit will
|
||||
/// turn video off and stop the data fetches.
|
||||
///
|
||||
/// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU.
|
||||
///
|
||||
/// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU.
|
||||
pub fn unblank_screen(&mut self, emulation_mode: EmulationMode) {
|
||||
self.toggle_index(emulation_mode);
|
||||
let arx_index_value = unsafe { self.arx_index.read() };
|
||||
unsafe {
|
||||
self.arx_index.write(arx_index_value | 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: AttributeControllerIndex) {
|
||||
unsafe {
|
||||
self.arx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_index(&mut self, emulation_mode: EmulationMode) {
|
||||
let st01_read = match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.st01_read_cga,
|
||||
EmulationMode::Mda => &mut self.st01_read_mda,
|
||||
};
|
||||
unsafe {
|
||||
st01_read.read();
|
||||
}
|
||||
}
|
||||
}
|
49
src/registers/color_palette.rs
Normal file
49
src/registers/color_palette.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use super::{
|
||||
COLOR_PALETTE_DATA_ADDRESS, COLOR_PALETTE_INDEX_READ_ADDRESS,
|
||||
COLOR_PALETTE_INDEX_WRITE_ADDRESSS, PALETTE_SIZE,
|
||||
};
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
/// Represents the color palette registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct ColorPaletteRegisters {
|
||||
data_port: Port<u8>,
|
||||
index_read_port: Port<u8>,
|
||||
index_write_port: Port<u8>,
|
||||
}
|
||||
|
||||
impl ColorPaletteRegisters {
|
||||
pub(crate) fn new() -> ColorPaletteRegisters {
|
||||
ColorPaletteRegisters {
|
||||
data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS),
|
||||
index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS),
|
||||
index_write_port: Port::new(COLOR_PALETTE_INDEX_WRITE_ADDRESSS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a 256 color palette, as specified by `palette`, with every 3
|
||||
/// bytes representing a color.
|
||||
pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) {
|
||||
unsafe {
|
||||
self.index_write_port.write(0);
|
||||
}
|
||||
for i in palette.iter() {
|
||||
unsafe {
|
||||
self.data_port.write(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current 256 color palette into `palette`, with every 3
|
||||
/// bytes representing a color.
|
||||
pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) {
|
||||
unsafe {
|
||||
self.index_read_port.write(0);
|
||||
}
|
||||
for byte in palette.iter_mut().take(PALETTE_SIZE) {
|
||||
unsafe {
|
||||
*byte = self.data_port.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
127
src/registers/crtc_controller.rs
Normal file
127
src/registers/crtc_controller.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use super::{
|
||||
EmulationMode, CRX_DATA_CGA_ADDRESS, CRX_DATA_MDA_ADDRESS, CRX_INDEX_CGA_ADDRESS,
|
||||
CRX_INDEX_MDA_ADDRESS,
|
||||
};
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
/// Represents an index for the crtc controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum CrtcControllerIndex {
|
||||
/// Represents the `Horizontal Total` register index.
|
||||
HorizontalTotal = 0x00,
|
||||
/// Represents the `Horizontal Display Enable End` register index.
|
||||
HorizontalDisplayEnableEnd = 0x01,
|
||||
/// Represents the `Horizontal Blanking Start` register index.
|
||||
HorizontalBlankingStart = 0x02,
|
||||
/// Represents the `Horizontal Blanking End` register index.
|
||||
HorizontalBlankingEnd = 0x03,
|
||||
/// Represents the `Horizontal Sync Start` register index.
|
||||
HorizontalSyncStart = 0x04,
|
||||
/// Represents the `Horizontal Sync End` register index.
|
||||
HorizontalSyncEnd = 0x05,
|
||||
/// Represents the `Vertical Total` register index.
|
||||
VeritcalTotal = 0x06,
|
||||
/// Represents the `Overflow` register index.
|
||||
Overflow = 0x07,
|
||||
/// Represents the `Preset Row Scan` register index.
|
||||
PresetRowScan = 0x08,
|
||||
/// Represents the `Maximum Scan Line` register index.
|
||||
MaximumScanLine = 0x09,
|
||||
/// Represents the `Text Cursor Start` register index.
|
||||
TextCursorStart = 0x0A,
|
||||
/// Represents the `Text Cursor End` register index.
|
||||
TextCursorEnd = 0x0B,
|
||||
/// Represents the `Start Address High` register index.
|
||||
StartAddressHigh = 0x0C,
|
||||
/// Represents the `Start Address Low` register index.
|
||||
StartAddressLow = 0x0D,
|
||||
/// Represents the `Text Cursor Location High` register index.
|
||||
TextCursorLocationHigh = 0x0E,
|
||||
/// Represents the `Text Cursor Location Low` register index.
|
||||
TextCursorLocationLow = 0x0F,
|
||||
/// Represents the `Vertical Sync Start` register index.
|
||||
VerticalSyncStart = 0x10,
|
||||
/// Represents the `Vertical Sync End` register index.
|
||||
VerticalSyncEnd = 0x11,
|
||||
/// Represents the `Vertical Display Enable End` register index
|
||||
VerticalDisplayEnableEnd = 0x12,
|
||||
/// Represents the `Offset` register index.
|
||||
Offset = 0x13,
|
||||
/// Represents the `Underline Location` register index.
|
||||
UnderlineLocation = 0x14,
|
||||
/// Represents the `Vertical Blanking Start` register index.
|
||||
VerticalBlankingStart = 0x15,
|
||||
/// Represents the `Vertical Blanking End` register index.
|
||||
VerticalBlankingEnd = 0x16,
|
||||
/// Represents the `Mode Control` register index.
|
||||
ModeControl = 0x17,
|
||||
/// Represents the `Line Compare` register index.
|
||||
LineCompare = 0x18,
|
||||
/// Represents the `Memory Read Latch Data` register index.
|
||||
MemoryReadLatchData = 0x22,
|
||||
/// Represents the `Toggle State Of Attribute Controller` register index.
|
||||
ToggleStateOfAttributeController = 0x24,
|
||||
}
|
||||
|
||||
impl From<CrtcControllerIndex> for u8 {
|
||||
fn from(value: CrtcControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the crtc controller registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct CrtcControllerRegisters {
|
||||
crx_index_cga: Port<u8>,
|
||||
crx_index_mda: Port<u8>,
|
||||
crx_data_cga: Port<u8>,
|
||||
crx_data_mda: Port<u8>,
|
||||
}
|
||||
|
||||
impl CrtcControllerRegisters {
|
||||
pub(crate) fn new() -> CrtcControllerRegisters {
|
||||
CrtcControllerRegisters {
|
||||
crx_index_cga: Port::new(CRX_INDEX_CGA_ADDRESS),
|
||||
crx_index_mda: Port::new(CRX_INDEX_MDA_ADDRESS),
|
||||
crx_data_cga: Port::new(CRX_DATA_CGA_ADDRESS),
|
||||
crx_data_mda: Port::new(CRX_DATA_MDA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current value from the crtc controller, as specified
|
||||
/// by `emulation_mode` and `index`.
|
||||
pub fn read(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) -> u8 {
|
||||
self.set_index(emulation_mode, index);
|
||||
unsafe { self.get_data_port(emulation_mode).read() }
|
||||
}
|
||||
|
||||
/// Writes the `value` to the crtc_controller, as specified
|
||||
/// by `emulation_mode` and `index`.
|
||||
pub fn write(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex, value: u8) {
|
||||
self.set_index(emulation_mode, index);
|
||||
unsafe {
|
||||
self.get_data_port(emulation_mode).write(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_index(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) {
|
||||
unsafe {
|
||||
self.get_index_port(emulation_mode).write(u8::from(index));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_port(&mut self, emulation_mode: EmulationMode) -> &mut Port<u8> {
|
||||
match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.crx_data_cga,
|
||||
EmulationMode::Mda => &mut self.crx_data_mda,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index_port(&mut self, emulation_mode: EmulationMode) -> &mut Port<u8> {
|
||||
match emulation_mode {
|
||||
EmulationMode::Cga => &mut self.crx_index_cga,
|
||||
EmulationMode::Mda => &mut self.crx_index_mda,
|
||||
}
|
||||
}
|
||||
}
|
45
src/registers/general.rs
Normal file
45
src/registers/general.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use super::{
|
||||
FCR_CGA_WRITE_ADDRESS, FCR_MDA_WRITE_ADDRESS, FCR_READ_ADDRESS, MSR_READ_ADDRESS,
|
||||
MSR_WRITE_ADDRESS, ST00_READ_ADDRESS, ST01_READ_CGA_ADDRESS, ST01_READ_MDA_ADDRESS,
|
||||
};
|
||||
use x86_64::instructions::port::{PortReadOnly, PortWriteOnly};
|
||||
|
||||
/// Represents the general registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct GeneralRegisters {
|
||||
st00_read: PortReadOnly<u8>,
|
||||
st01_read_cga: PortReadOnly<u8>,
|
||||
st01_read_mda: PortReadOnly<u8>,
|
||||
fcr_read: PortReadOnly<u8>,
|
||||
fcr_write_cga: PortWriteOnly<u8>,
|
||||
fcr_write_mda: PortWriteOnly<u8>,
|
||||
msr_read: PortReadOnly<u8>,
|
||||
msr_write: PortWriteOnly<u8>,
|
||||
}
|
||||
|
||||
impl GeneralRegisters {
|
||||
pub(crate) fn new() -> GeneralRegisters {
|
||||
GeneralRegisters {
|
||||
st00_read: PortReadOnly::new(ST00_READ_ADDRESS),
|
||||
st01_read_cga: PortReadOnly::new(ST01_READ_CGA_ADDRESS),
|
||||
st01_read_mda: PortReadOnly::new(ST01_READ_MDA_ADDRESS),
|
||||
fcr_read: PortReadOnly::new(FCR_READ_ADDRESS),
|
||||
fcr_write_cga: PortWriteOnly::new(FCR_CGA_WRITE_ADDRESS),
|
||||
fcr_write_mda: PortWriteOnly::new(FCR_MDA_WRITE_ADDRESS),
|
||||
msr_read: PortReadOnly::new(MSR_READ_ADDRESS),
|
||||
msr_write: PortWriteOnly::new(MSR_WRITE_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current value from the miscellaneous output register.
|
||||
pub fn read_msr(&mut self) -> u8 {
|
||||
unsafe { self.msr_read.read() }
|
||||
}
|
||||
|
||||
/// Writes the `value` to the miscellaneous output register.
|
||||
pub fn write_msr(&mut self, value: u8) {
|
||||
unsafe {
|
||||
self.msr_write.write(value);
|
||||
}
|
||||
}
|
||||
}
|
210
src/registers/graphics_controller.rs
Normal file
210
src/registers/graphics_controller.rs
Normal file
|
@ -0,0 +1,210 @@
|
|||
use super::{Color16, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS};
|
||||
use core::convert::TryFrom;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
/// Represents a plane for the `GraphicsControllerIndex::ReadPlaneSelect` register.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum ReadPlane {
|
||||
/// Represents `Plane 0 (0x0)`.
|
||||
Plane0 = 0x0,
|
||||
/// Represents `Plane 1 (0x1)`.
|
||||
Plane1 = 0x1,
|
||||
/// Represents `Plane 2 (0x2)`.
|
||||
Plane2 = 0x2,
|
||||
/// Represents `Plane 3 (0x3)`.
|
||||
Plane3 = 0x3,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ReadPlane {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(ReadPlane::Plane0),
|
||||
1 => Ok(ReadPlane::Plane1),
|
||||
2 => Ok(ReadPlane::Plane2),
|
||||
3 => Ok(ReadPlane::Plane3),
|
||||
_ => Err("ReadPlane only accepts values between 0-3!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReadPlane> for u8 {
|
||||
fn from(value: ReadPlane) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the graphics controller registers.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum GraphicsControllerIndex {
|
||||
/// Represents the `Set/Reset` register index.
|
||||
SetReset = 0x0,
|
||||
/// Represents the `Enable Set/Reset` register index.
|
||||
EnableSetReset = 0x1,
|
||||
/// Represents the `Color Compare` register index.
|
||||
ColorCompare = 0x2,
|
||||
/// Represents the `Data Rotate` register index.
|
||||
DataRotate = 0x3,
|
||||
/// Represents the `Read Plane Select` register index.
|
||||
ReadPlaneSelect = 0x4,
|
||||
/// Represents the `Graphics Mode` register index.
|
||||
GraphicsMode = 0x5,
|
||||
/// Represents the `Miscellaneous` register index.
|
||||
Miscellaneous = 0x6,
|
||||
/// Represents the `Color Don't Care` register index.
|
||||
ColorDontCare = 0x7,
|
||||
/// Represents the `Bit Mask` register index.
|
||||
BitMask = 0x8,
|
||||
/// Represents the `Address Mapping` register index.
|
||||
AddressMapping = 0x10,
|
||||
/// Represents the `Page Selector` register index.
|
||||
PageSelector = 0x11,
|
||||
/// Represents the `Software Flags` register index.
|
||||
SoftwareFlags = 0x18,
|
||||
}
|
||||
|
||||
impl From<GraphicsControllerIndex> for u8 {
|
||||
fn from(value: GraphicsControllerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a write mode for vga hardware.
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum WriteMode {
|
||||
/// Represents `WriteMode` 0.
|
||||
///
|
||||
/// During a CPU write to the frame buffer, the
|
||||
/// addressed byte in each of the 4 memory planes is written with the
|
||||
/// CPU write data after it has been rotated by the number of counts
|
||||
/// specified in the `GraphicsControllerIndex::DataRotate` register. If, however, the bit(s)
|
||||
/// in the `GraphicsControllerIndex::EnableSetReset` register corresponding to one or
|
||||
/// more of the memory planes is set to 1, then those memory planes
|
||||
/// will be written to with the data stored in the corresponding bits in
|
||||
/// the `GraphicsControllerIndex::SetReset` register.
|
||||
Mode0 = 0x0,
|
||||
/// Represents `WriteMode` 1.
|
||||
///
|
||||
/// During a CPU write to the frame buffer, the
|
||||
/// addressed byte in each of the 4 memory planes is written to with
|
||||
/// the data stored in the memory read latches. (The memory read
|
||||
/// latches stores an unaltered copy of the data last read from any
|
||||
/// location in the frame buffer.)
|
||||
Mode1 = 0x1,
|
||||
/// Represents `WriteMode` 2.
|
||||
///
|
||||
/// During a CPU write to the frame buffer, the least
|
||||
/// significant 4 data bits of the CPU write data is treated as the color
|
||||
/// value for the pixels in the addressed byte in all 4 memory planes.
|
||||
/// The 8 bits of the `GraphicsControllerIndex::BitMask` register are used to selectively
|
||||
/// enable or disable the ability to write to the corresponding bit in
|
||||
/// each of the 4 memory planes that correspond to a given pixel. A
|
||||
/// setting of 0 in a bit in the Bit Mask Register at a given bit position
|
||||
/// causes the bits in the corresponding bit positions in the addressed
|
||||
/// byte in all 4 memory planes to be written with value of their
|
||||
/// counterparts in the memory read latches. A setting of 1 in a Bit
|
||||
/// Mask Register at a given bit position causes the bits in the
|
||||
/// corresponding bit positions in the addressed byte in all 4 memory
|
||||
/// planes to be written with the 4 bits taken from the CPU write data
|
||||
/// to thereby cause the pixel corresponding to these bits to be set to
|
||||
/// the color value.
|
||||
Mode2 = 0x2,
|
||||
/// Represents `WriteMode` 3.
|
||||
///
|
||||
/// During a CPU write to the frame buffer, the CPU
|
||||
/// write data is logically ANDed with the contents of the `GraphicsControllerIndex::BitMask`
|
||||
/// register. The result of this ANDing is treated as the bit
|
||||
/// mask used in writing the contents of the `GraphicsControllerIndex::SetReset` register
|
||||
/// are written to addressed byte in all 4 memory planes.
|
||||
Mode3 = 0x3,
|
||||
}
|
||||
|
||||
impl From<WriteMode> for u8 {
|
||||
fn from(value: WriteMode) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the graphics controller registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct GraphicsControllerRegisters {
|
||||
grx_index: Port<u8>,
|
||||
grx_data: Port<u8>,
|
||||
}
|
||||
|
||||
impl GraphicsControllerRegisters {
|
||||
pub(crate) fn new() -> GraphicsControllerRegisters {
|
||||
GraphicsControllerRegisters {
|
||||
grx_index: Port::new(GRX_INDEX_ADDRESS),
|
||||
grx_data: Port::new(GRX_DATA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current value from the graphics controller, as specified
|
||||
/// by `index`.
|
||||
pub fn read(&mut self, index: GraphicsControllerIndex) -> u8 {
|
||||
self.set_index(index);
|
||||
unsafe { self.grx_data.read() }
|
||||
}
|
||||
|
||||
/// Writes the `value` to the graphics controller, as specified
|
||||
/// by `index.
|
||||
pub fn write(&mut self, index: GraphicsControllerIndex, value: u8) {
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.grx_data.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the read plane of the graphics controller, as specified by `read_plane`.
|
||||
pub fn write_read_plane(&mut self, read_plane: ReadPlane) {
|
||||
let read_plane = u8::from(read_plane) & 0x3;
|
||||
self.write(GraphicsControllerIndex::ReadPlaneSelect, read_plane);
|
||||
}
|
||||
|
||||
/// Sets the value to use for `GraphicsControllerIndex::SetReset`,
|
||||
/// as spcified by `color`.
|
||||
pub fn write_set_reset(&mut self, color: Color16) {
|
||||
let original_value = self.read(GraphicsControllerIndex::SetReset) & 0xF0;
|
||||
self.write(
|
||||
GraphicsControllerIndex::SetReset,
|
||||
original_value | u8::from(color),
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets which bits are effected by `GraphicsControllerIndex::SetReset`,
|
||||
/// as specified by `bit_mask`.
|
||||
pub fn write_enable_set_reset(&mut self, bit_mask: u8) {
|
||||
let original_value = self.read(GraphicsControllerIndex::EnableSetReset) & 0xF0;
|
||||
self.write(
|
||||
GraphicsControllerIndex::EnableSetReset,
|
||||
original_value | bit_mask,
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets which mode the vga writes in, as specified by `write_mode`.
|
||||
pub fn set_write_mode(&mut self, write_mode: WriteMode) {
|
||||
let original_value = self.read(GraphicsControllerIndex::GraphicsMode) & 0xFC;
|
||||
self.write(
|
||||
GraphicsControllerIndex::GraphicsMode,
|
||||
original_value | u8::from(write_mode),
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets which bits are effected by certain operations, as specified
|
||||
/// by `bit_mask`.
|
||||
pub fn set_bit_mask(&mut self, bit_mask: u8) {
|
||||
self.write(GraphicsControllerIndex::BitMask, bit_mask);
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: GraphicsControllerIndex) {
|
||||
unsafe {
|
||||
self.grx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
}
|
64
src/registers/mod.rs
Normal file
64
src/registers/mod.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
//! Common registers used in vga programming.
|
||||
|
||||
mod attribute_controller;
|
||||
mod color_palette;
|
||||
mod crtc_controller;
|
||||
mod general;
|
||||
mod graphics_controller;
|
||||
mod sequencer;
|
||||
|
||||
use crate::colors::{Color16, PALETTE_SIZE};
|
||||
|
||||
pub use attribute_controller::{AttributeControllerIndex, AttributeControllerRegisters};
|
||||
pub use color_palette::ColorPaletteRegisters;
|
||||
pub use crtc_controller::{CrtcControllerIndex, CrtcControllerRegisters};
|
||||
pub use general::GeneralRegisters;
|
||||
pub use graphics_controller::{GraphicsControllerIndex, GraphicsControllerRegisters, WriteMode};
|
||||
pub use sequencer::{PlaneMask, SequencerIndex, SequencerRegisters};
|
||||
|
||||
const ST00_READ_ADDRESS: u16 = 0x3C2;
|
||||
const ST01_READ_CGA_ADDRESS: u16 = 0x3DA;
|
||||
const ST01_READ_MDA_ADDRESS: u16 = 0x3BA;
|
||||
const FCR_READ_ADDRESS: u16 = 0x3CA;
|
||||
const FCR_CGA_WRITE_ADDRESS: u16 = 0x3DA;
|
||||
const FCR_MDA_WRITE_ADDRESS: u16 = 0x3BA;
|
||||
const MSR_READ_ADDRESS: u16 = 0x3CC;
|
||||
const MSR_WRITE_ADDRESS: u16 = 0x3C2;
|
||||
|
||||
const SRX_INDEX_ADDRESS: u16 = 0x3C4;
|
||||
const SRX_DATA_ADDRESS: u16 = 0x3C5;
|
||||
|
||||
const GRX_INDEX_ADDRESS: u16 = 0x3CE;
|
||||
const GRX_DATA_ADDRESS: u16 = 0x3CF;
|
||||
|
||||
const ARX_INDEX_ADDRESS: u16 = 0x3C0;
|
||||
const ARX_DATA_ADDRESS: u16 = 0x3C1;
|
||||
|
||||
const CRX_INDEX_CGA_ADDRESS: u16 = 0x3D4;
|
||||
const CRX_INDEX_MDA_ADDRESS: u16 = 0x3B4;
|
||||
const CRX_DATA_CGA_ADDRESS: u16 = 0x3D5;
|
||||
const CRX_DATA_MDA_ADDRESS: u16 = 0x3B5;
|
||||
|
||||
const COLOR_PALETTE_DATA_ADDRESS: u16 = 0x3C9;
|
||||
const COLOR_PALETTE_INDEX_READ_ADDRESS: u16 = 0x3C7;
|
||||
const COLOR_PALETTE_INDEX_WRITE_ADDRESSS: u16 = 0x3C8;
|
||||
|
||||
/// Represents a vga emulation mode.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum EmulationMode {
|
||||
/// Represents a monochrome emulation mode.
|
||||
Mda = 0x0,
|
||||
/// Respresents a color emulation mode.
|
||||
Cga = 0x1,
|
||||
}
|
||||
|
||||
impl From<u8> for EmulationMode {
|
||||
fn from(value: u8) -> EmulationMode {
|
||||
match value {
|
||||
0x0 => EmulationMode::Mda,
|
||||
0x1 => EmulationMode::Cga,
|
||||
_ => panic!("{} is an invalid emulation mode", value),
|
||||
}
|
||||
}
|
||||
}
|
111
src/registers/sequencer.rs
Normal file
111
src/registers/sequencer.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use super::{SRX_DATA_ADDRESS, SRX_INDEX_ADDRESS};
|
||||
use bitflags::bitflags;
|
||||
use core::convert::TryFrom;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
bitflags! {
|
||||
/// Represents the plane masks of the `SequencerIndex::PlaneMask` register.
|
||||
pub struct PlaneMask: u8 {
|
||||
/// Represents none of the plane masks of vga memory.
|
||||
const NONE = 0b0000_0000;
|
||||
/// Represents `Plane0` of vga memory.
|
||||
const PLANE0 = 0b0000_0001;
|
||||
/// Represents `Plane1` of vga memory.
|
||||
const PLANE1 = 0b0000_0010;
|
||||
/// Represents `Plane2` of vga memory.
|
||||
const PLANE2 = 0b0000_0100;
|
||||
/// Represents `Plane3` of vga memory.
|
||||
const PLANE3 = 0b0000_1000;
|
||||
/// Represents all of the plane masks of vga memory.
|
||||
const ALL_PLANES = Self::PLANE0.bits() | Self::PLANE1.bits() | Self::PLANE2.bits() | Self::PLANE3.bits();
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PlaneMask {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(PlaneMask::PLANE0),
|
||||
1 => Ok(PlaneMask::PLANE1),
|
||||
2 => Ok(PlaneMask::PLANE2),
|
||||
3 => Ok(PlaneMask::PLANE3),
|
||||
_ => Err("PlaneMask only accepts values between 0-3!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlaneMask> for u8 {
|
||||
fn from(value: PlaneMask) -> u8 {
|
||||
value.bits()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an index for the seqeuncer registers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum SequencerIndex {
|
||||
/// Represents the `Sequencer Reset` register index.
|
||||
SequencerReset = 0x0,
|
||||
/// Represents the `Clocking Mode` register index.
|
||||
ClockingMode = 0x1,
|
||||
/// Represents the `Plane/Map` mask register index.
|
||||
PlaneMask = 0x2,
|
||||
/// Represents the `Character Font` register index.
|
||||
CharacterFont = 0x3,
|
||||
/// Represents the `Memory Mode` register index.
|
||||
MemoryMode = 0x4,
|
||||
/// Represents the `Horizontal Character Counter Reset` register index.
|
||||
CounterReset = 0x7,
|
||||
}
|
||||
|
||||
impl From<SequencerIndex> for u8 {
|
||||
fn from(value: SequencerIndex) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the sequencer registers on vga hardware.
|
||||
#[derive(Debug)]
|
||||
pub struct SequencerRegisters {
|
||||
srx_index: Port<u8>,
|
||||
srx_data: Port<u8>,
|
||||
}
|
||||
|
||||
impl SequencerRegisters {
|
||||
pub(crate) fn new() -> SequencerRegisters {
|
||||
SequencerRegisters {
|
||||
srx_index: Port::new(SRX_INDEX_ADDRESS),
|
||||
srx_data: Port::new(SRX_DATA_ADDRESS),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the current value from the sequencer, as specified by `index`.
|
||||
pub fn read(&mut self, index: SequencerIndex) -> u8 {
|
||||
self.set_index(index);
|
||||
unsafe { self.srx_data.read() }
|
||||
}
|
||||
|
||||
/// Writes the `value` to the sequencer, as specified by `index`.
|
||||
pub fn write(&mut self, index: SequencerIndex, value: u8) {
|
||||
self.set_index(index);
|
||||
unsafe {
|
||||
self.srx_data.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the plane mask of the sequencer controller, as specified by `plane_mask`.
|
||||
pub fn set_plane_mask(&mut self, plane_mask: PlaneMask) {
|
||||
let original_value = self.read(SequencerIndex::PlaneMask);
|
||||
self.write(
|
||||
SequencerIndex::PlaneMask,
|
||||
original_value | u8::from(plane_mask),
|
||||
);
|
||||
}
|
||||
|
||||
fn set_index(&mut self, index: SequencerIndex) {
|
||||
unsafe {
|
||||
self.srx_index.write(u8::from(index));
|
||||
}
|
||||
}
|
||||
}
|
123
src/vga.rs
123
src/vga.rs
|
@ -1,16 +1,15 @@
|
|||
//! Provides access to the vga graphics card.
|
||||
|
||||
use super::{
|
||||
colors::PALETTE_SIZE,
|
||||
configurations::{
|
||||
VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION,
|
||||
MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION,
|
||||
},
|
||||
fonts::VgaFont,
|
||||
registers::{
|
||||
AttributeControllerIndex, AttributeControllerRegisters, ColorPaletteRegisters,
|
||||
CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, GeneralRegisters,
|
||||
GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, SequencerRegisters,
|
||||
AttributeControllerRegisters, ColorPaletteRegisters, CrtcControllerIndex,
|
||||
CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex,
|
||||
GraphicsControllerRegisters, PlaneMask, SequencerIndex, SequencerRegisters,
|
||||
},
|
||||
};
|
||||
use conquer_once::spin::Lazy;
|
||||
|
@ -49,27 +48,6 @@ impl From<FrameBuffer> for u32 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a plane for reading and writing vga data.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum Plane {
|
||||
/// Represents `Plane 0 (0x0)`.
|
||||
Plane0 = 0x0,
|
||||
/// Represents `Plane 1 (0x1)`.
|
||||
Plane1 = 0x1,
|
||||
/// Represents `Plane 2 (0x2)`.
|
||||
Plane2 = 0x2,
|
||||
/// Represents `Plane 3 (0x3)`.
|
||||
Plane3 = 0x3,
|
||||
}
|
||||
|
||||
impl From<Plane> for u8 {
|
||||
fn from(value: Plane) -> u8 {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specified vga video mode.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VideoMode {
|
||||
|
@ -86,12 +64,18 @@ pub enum VideoMode {
|
|||
/// Represents a vga graphics card with it's common registers,
|
||||
/// as well as the most recent video mode.
|
||||
pub struct Vga {
|
||||
general_registers: GeneralRegisters,
|
||||
sequencer_registers: SequencerRegisters,
|
||||
graphics_controller_registers: GraphicsControllerRegisters,
|
||||
attribute_controller_registers: AttributeControllerRegisters,
|
||||
crtc_controller_registers: CrtcControllerRegisters,
|
||||
color_palette_registers: ColorPaletteRegisters,
|
||||
/// Represents the general registers on vga hardware.
|
||||
pub general_registers: GeneralRegisters,
|
||||
/// Represents the sequencer registers on vga hardware.
|
||||
pub sequencer_registers: SequencerRegisters,
|
||||
/// Represents the graphics controller registers on vga hardware.
|
||||
pub graphics_controller_registers: GraphicsControllerRegisters,
|
||||
/// Represents the attribute controller registers on vga hardware.
|
||||
pub attribute_controller_registers: AttributeControllerRegisters,
|
||||
/// Represents the crtc controller registers on vga hardware.
|
||||
pub crtc_controller_registers: CrtcControllerRegisters,
|
||||
/// Represents the color palette registers on vga hardware.
|
||||
pub color_palette_registers: ColorPaletteRegisters,
|
||||
most_recent_video_mode: Option<VideoMode>,
|
||||
}
|
||||
|
||||
|
@ -134,74 +118,11 @@ impl Vga {
|
|||
self.most_recent_video_mode
|
||||
}
|
||||
|
||||
/// Returns the current value of the miscellaneous output register.
|
||||
pub fn read_msr(&mut self) -> u8 {
|
||||
self.general_registers.read_msr()
|
||||
}
|
||||
|
||||
/// Returns the current value of the sequencer register, as determined by `index`.
|
||||
pub fn read_sequencer(&mut self, index: SequencerIndex) -> u8 {
|
||||
self.sequencer_registers.read(index)
|
||||
}
|
||||
|
||||
/// Returns the current value of the graphics controller register, as determined by `index`.
|
||||
pub fn read_graphics_controller(&mut self, index: GraphicsControllerIndex) -> u8 {
|
||||
self.graphics_controller_registers.read(index)
|
||||
}
|
||||
|
||||
/// Returns the current value of the attribute controller register, as determined by `emulation_mode`
|
||||
/// and `index`.
|
||||
pub fn read_attribute_controller(
|
||||
&mut self,
|
||||
emulation_mode: EmulationMode,
|
||||
index: AttributeControllerIndex,
|
||||
) -> u8 {
|
||||
self.attribute_controller_registers
|
||||
.read(emulation_mode, index)
|
||||
}
|
||||
|
||||
/// Returns the current value of the crtc controller, as determined by `emulation_mode`
|
||||
/// and `index`.
|
||||
pub fn read_crtc_controller(
|
||||
&mut self,
|
||||
emulation_mode: EmulationMode,
|
||||
index: CrtcControllerIndex,
|
||||
) -> u8 {
|
||||
self.crtc_controller_registers.read(emulation_mode, index)
|
||||
}
|
||||
|
||||
/// Writes `value` to the crtc controller, as determined by `index`.
|
||||
pub fn write_crtc_controller(
|
||||
&mut self,
|
||||
emulation_mode: EmulationMode,
|
||||
index: CrtcControllerIndex,
|
||||
value: u8,
|
||||
) {
|
||||
self.crtc_controller_registers
|
||||
.write(emulation_mode, index, value);
|
||||
}
|
||||
|
||||
/// Returns the current `EmulationMode` as determined by the miscellaneous output register.
|
||||
pub fn get_emulation_mode(&mut self) -> EmulationMode {
|
||||
EmulationMode::from(self.general_registers.read_msr() & 0x1)
|
||||
}
|
||||
|
||||
/// Loads a new palette into the vga, as specified by `palette`.
|
||||
///
|
||||
/// Each palette must be `PALETTE_SIZE` bytes long, with every 3
|
||||
/// bytes representing one color `(R, G, B)`.
|
||||
pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) {
|
||||
self.color_palette_registers.load_palette(palette);
|
||||
}
|
||||
|
||||
/// Reads the current vga palette into `palette`.
|
||||
///
|
||||
/// Each palette must be `PALETTE_SIZE` bytes long, with every 3
|
||||
/// bytes representing one color `(R, G, B)`.
|
||||
pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) {
|
||||
self.color_palette_registers.read_palette(palette);
|
||||
}
|
||||
|
||||
/// Loads a vga text mode font as specified by `vga_font`.
|
||||
pub fn load_font(&mut self, vga_font: &VgaFont) {
|
||||
// Save registers
|
||||
|
@ -226,7 +147,7 @@ impl Vga {
|
|||
);
|
||||
|
||||
// Write font to plane
|
||||
self.set_plane(Plane::Plane2);
|
||||
self.sequencer_registers.set_plane_mask(PlaneMask::PLANE2);
|
||||
|
||||
let frame_buffer = u32::from(self.get_frame_buffer()) as *mut u8;
|
||||
|
||||
|
@ -286,18 +207,6 @@ impl Vga {
|
|||
)
|
||||
}
|
||||
|
||||
/// Turns on the given `Plane` in the vga graphics card.
|
||||
pub fn set_plane(&mut self, plane: Plane) {
|
||||
let mut plane = u8::from(plane);
|
||||
|
||||
plane &= 0x3;
|
||||
|
||||
self.graphics_controller_registers
|
||||
.write(GraphicsControllerIndex::ReadPlaneSelect, plane);
|
||||
self.sequencer_registers
|
||||
.write(SequencerIndex::PlaneMask, 0x1 << plane);
|
||||
}
|
||||
|
||||
fn set_registers(&mut self, configuration: &VgaConfiguration) {
|
||||
let emulation_mode = self.get_emulation_mode();
|
||||
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use super::GraphicsWriter;
|
||||
use crate::{
|
||||
colors::{Color16Bit, DEFAULT_PALETTE},
|
||||
vga::{Plane, Vga, VideoMode, VGA},
|
||||
colors::{Color16, DEFAULT_PALETTE},
|
||||
drawing::{Bresenham, Point},
|
||||
registers::{PlaneMask, WriteMode},
|
||||
vga::{Vga, VideoMode, VGA},
|
||||
};
|
||||
use font8x8::UnicodeFonts;
|
||||
use spinning_top::SpinlockGuard;
|
||||
|
||||
const WIDTH: usize = 640;
|
||||
const HEIGHT: usize = 480;
|
||||
|
||||
static PLANES: &[Plane] = &[Plane::Plane0, Plane::Plane1, Plane::Plane2, Plane::Plane3];
|
||||
const ALL_PLANES_SCREEN_SIZE: usize = (WIDTH * HEIGHT) / 8;
|
||||
const WIDTH_IN_BYTES: usize = WIDTH / 8;
|
||||
|
||||
/// A basic interface for interacting with vga graphics mode 640x480x16
|
||||
///
|
||||
|
@ -16,69 +20,97 @@ static PLANES: &[Plane] = &[Plane::Plane0, Plane::Plane1, Plane::Plane2, Plane::
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use vga::writers::Graphics640x480x16;
|
||||
///
|
||||
/// let graphics_mode = Graphics640x480x16::new();
|
||||
///
|
||||
/// graphics_mode.set_mode();
|
||||
/// graphics_mode.clear_screen();
|
||||
/// use vga::colors::Color16;
|
||||
/// use vga::writers::{Graphics640x480x16, GraphicsWriter};
|
||||
|
||||
/// let mode = Graphics640x480x16::new();
|
||||
/// mode.set_mode();
|
||||
/// mode.clear_screen(Color16::Black);
|
||||
/// mode.draw_line((80, 60), (80, 420), Color16::White);
|
||||
/// mode.draw_line((80, 60), (540, 60), Color16::White);
|
||||
/// mode.draw_line((80, 420), (540, 420), Color16::White);
|
||||
/// mode.draw_line((540, 420), (540, 60), Color16::White);
|
||||
/// mode.draw_line((80, 90), (540, 90), Color16::White);
|
||||
/// for (offset, character) in "Hello World!".chars().enumerate() {
|
||||
/// mode.draw_character(270 + offset * 8, 72, character, Color16::White)
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct Graphics640x480x16;
|
||||
|
||||
impl GraphicsWriter<Color16> for Graphics640x480x16 {
|
||||
fn clear_screen(&self, color: Color16) {
|
||||
self.set_write_mode_2();
|
||||
let (_vga, frame_buffer) = self.get_frame_buffer();
|
||||
for offset in 0..ALL_PLANES_SCREEN_SIZE {
|
||||
unsafe {
|
||||
frame_buffer.add(offset).write_volatile(u8::from(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_line(&self, start: Point<isize>, end: Point<isize>, color: Color16) {
|
||||
self.set_write_mode_0(color);
|
||||
for (x, y) in Bresenham::new(start, end) {
|
||||
self._set_pixel(x as usize, y as usize, color);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_character(&self, x: usize, y: usize, character: char, color: Color16) {
|
||||
self.set_write_mode_2();
|
||||
let character = match font8x8::BASIC_FONTS.get(character) {
|
||||
Some(character) => character,
|
||||
// Default to a filled block if the character isn't found
|
||||
None => font8x8::unicode::BLOCK_UNICODE[8].byte_array(),
|
||||
};
|
||||
|
||||
for (row, byte) in character.iter().enumerate() {
|
||||
for bit in 0..8 {
|
||||
match *byte & 1 << bit {
|
||||
0 => (),
|
||||
_ => self._set_pixel(x + bit, y + row, color),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_pixel(&self, x: usize, y: usize, color: Color16) {
|
||||
self.set_write_mode_2();
|
||||
self._set_pixel(x, y, color);
|
||||
}
|
||||
|
||||
fn set_mode(&self) {
|
||||
let mut vga = VGA.lock();
|
||||
vga.set_video_mode(VideoMode::Mode640x480x16);
|
||||
|
||||
// Some bios mess up the palette when switching modes,
|
||||
// so explicitly set it.
|
||||
vga.color_palette_registers.load_palette(&DEFAULT_PALETTE);
|
||||
}
|
||||
}
|
||||
|
||||
impl Graphics640x480x16 {
|
||||
/// Creates a new `Graphics640x480x16`.
|
||||
pub fn new() -> Graphics640x480x16 {
|
||||
Graphics640x480x16 {}
|
||||
}
|
||||
|
||||
/// Clears the screen by setting all pixels to `Color16Bit::Black`.
|
||||
pub fn clear_screen(&self) {
|
||||
// TODO: Clear the screen by using 4-plane mode instead of slow `set_pixel`.
|
||||
for x in 0..WIDTH {
|
||||
for y in 0..HEIGHT {
|
||||
self.set_pixel(x, y, Color16Bit::Yellow);
|
||||
}
|
||||
}
|
||||
fn set_write_mode_0(&self, color: Color16) {
|
||||
let (mut vga, _frame_buffer) = self.get_frame_buffer();
|
||||
vga.graphics_controller_registers.write_set_reset(color);
|
||||
vga.graphics_controller_registers
|
||||
.write_enable_set_reset(0xF);
|
||||
vga.graphics_controller_registers
|
||||
.set_write_mode(WriteMode::Mode0);
|
||||
}
|
||||
|
||||
/// Sets the given pixel at `(x, y)` to the given `color`.
|
||||
///
|
||||
/// Panics if `x >= 640` or `y >= 480`.
|
||||
pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) {
|
||||
assert!(x < WIDTH, "x >= {}", WIDTH);
|
||||
assert!(y < HEIGHT, "y >= {}", HEIGHT);
|
||||
let (mut vga, frame_buffer) = self.get_frame_buffer();
|
||||
let offset = x / 8 + (WIDTH / 8) * y;
|
||||
|
||||
// Store the current value for masking.
|
||||
let x = x & 7;
|
||||
let mask = 0x80 >> (x & 7);
|
||||
let mut plane_mask = 0x01;
|
||||
|
||||
for plane in PLANES {
|
||||
vga.set_plane(*plane);
|
||||
let current_value = unsafe { frame_buffer.add(offset).read_volatile() };
|
||||
let new_value = if plane_mask & color as u8 != 0 {
|
||||
current_value | mask
|
||||
} else {
|
||||
current_value & !mask
|
||||
};
|
||||
unsafe {
|
||||
frame_buffer.add(offset).write_volatile(new_value);
|
||||
}
|
||||
plane_mask <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the graphics device to `VideoMode::Mode640x480x16`.
|
||||
pub fn set_mode(&self) {
|
||||
let mut vga = VGA.lock();
|
||||
vga.set_video_mode(VideoMode::Mode640x480x16);
|
||||
|
||||
// Some bios mess up the palette when switching modes,
|
||||
// so explicitly set it.
|
||||
vga.load_palette(&DEFAULT_PALETTE);
|
||||
fn set_write_mode_2(&self) {
|
||||
let (mut vga, _frame_buffer) = self.get_frame_buffer();
|
||||
vga.graphics_controller_registers
|
||||
.set_write_mode(WriteMode::Mode2);
|
||||
vga.graphics_controller_registers.set_bit_mask(0xFF);
|
||||
vga.sequencer_registers
|
||||
.set_plane_mask(PlaneMask::ALL_PLANES);
|
||||
}
|
||||
|
||||
/// Returns the start of the `FrameBuffer` as `*mut u8` as
|
||||
|
@ -89,4 +121,16 @@ impl Graphics640x480x16 {
|
|||
let frame_buffer = vga.get_frame_buffer();
|
||||
(vga, u32::from(frame_buffer) as *mut u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _set_pixel(&self, x: usize, y: usize, color: Color16) {
|
||||
let (mut vga, frame_buffer) = self.get_frame_buffer();
|
||||
let offset = x / 8 + y * WIDTH_IN_BYTES;
|
||||
let pixel_mask = 0x80 >> (x & 0x07);
|
||||
vga.graphics_controller_registers.set_bit_mask(pixel_mask);
|
||||
unsafe {
|
||||
frame_buffer.add(offset).read_volatile();
|
||||
frame_buffer.add(offset).write_volatile(u8::from(color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ mod text_40x50;
|
|||
mod text_80x25;
|
||||
|
||||
use super::{
|
||||
colors::{Color16Bit, TextModeColor},
|
||||
colors::{Color16, TextModeColor},
|
||||
drawing::Point,
|
||||
registers::CrtcControllerIndex,
|
||||
vga::{Vga, VGA},
|
||||
};
|
||||
|
@ -43,17 +44,21 @@ impl ScreenCharacter {
|
|||
|
||||
static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter {
|
||||
character: b' ',
|
||||
color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black),
|
||||
color: TextModeColor::new(Color16::Yellow, Color16::Black),
|
||||
};
|
||||
|
||||
/// A helper trait used to interact with various vga text modes.
|
||||
pub trait TextWriter {
|
||||
/// Returns the width of the `TextWriter`.
|
||||
/// A helper trait used to interact with various vga screens.
|
||||
pub trait Screen {
|
||||
/// Returns the width of the `Screen`.
|
||||
fn get_width(&self) -> usize;
|
||||
|
||||
/// Returns the height of the `TextWriter`.
|
||||
/// Returns the height of the `Screen`.
|
||||
fn get_height(&self) -> usize;
|
||||
/// Returns the size of the `Screen`.
|
||||
fn get_size(&self) -> usize;
|
||||
}
|
||||
|
||||
/// A helper trait used to interact with various vga text modes.
|
||||
pub trait TextWriter: Screen {
|
||||
/// Sets the graphics device to a video mode as determined by
|
||||
/// the `TextWriter` implementation.
|
||||
fn set_mode(&self);
|
||||
|
@ -68,8 +73,8 @@ pub trait TextWriter {
|
|||
}
|
||||
|
||||
/// Clears the screen by setting all cells to `b' '` with
|
||||
/// a background color of `Color16Bit::Black` and a foreground
|
||||
/// color of `Color16Bit::Yellow`.
|
||||
/// a background color of `Color16::Black` and a foreground
|
||||
/// color of `Color16::Yellow`.
|
||||
fn clear_screen(&self) {
|
||||
let (_vga, frame_buffer) = self.get_frame_buffer();
|
||||
let screen_size = self.get_width() * self.get_height();
|
||||
|
@ -84,9 +89,10 @@ pub trait TextWriter {
|
|||
fn disable_cursor(&self) {
|
||||
let (mut vga, _frame_buffer) = self.get_frame_buffer();
|
||||
let emulation_mode = vga.get_emulation_mode();
|
||||
let cursor_start =
|
||||
vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart);
|
||||
vga.write_crtc_controller(
|
||||
let cursor_start = vga
|
||||
.crtc_controller_registers
|
||||
.read(emulation_mode, CrtcControllerIndex::TextCursorStart);
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorStart,
|
||||
cursor_start | 0x20,
|
||||
|
@ -97,9 +103,10 @@ pub trait TextWriter {
|
|||
fn enable_cursor(&self) {
|
||||
let (mut vga, _frame_buffer) = self.get_frame_buffer();
|
||||
let emulation_mode = vga.get_emulation_mode();
|
||||
let cursor_start =
|
||||
vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart);
|
||||
vga.write_crtc_controller(
|
||||
let cursor_start = vga
|
||||
.crtc_controller_registers
|
||||
.read(emulation_mode, CrtcControllerIndex::TextCursorStart);
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorStart,
|
||||
cursor_start & 0xDF,
|
||||
|
@ -122,16 +129,20 @@ pub trait TextWriter {
|
|||
fn set_cursor(&self, scan_line_start: u8, scan_line_end: u8) {
|
||||
let (mut vga, _frame_buffer) = self.get_frame_buffer();
|
||||
let emulation_mode = vga.get_emulation_mode();
|
||||
let cursor_start =
|
||||
vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart) & 0xC0;
|
||||
let cursor_end =
|
||||
vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorEnd) & 0xE0;
|
||||
vga.write_crtc_controller(
|
||||
let cursor_start = vga
|
||||
.crtc_controller_registers
|
||||
.read(emulation_mode, CrtcControllerIndex::TextCursorStart)
|
||||
& 0xC0;
|
||||
let cursor_end = vga
|
||||
.crtc_controller_registers
|
||||
.read(emulation_mode, CrtcControllerIndex::TextCursorEnd)
|
||||
& 0xE0;
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorStart,
|
||||
cursor_start | scan_line_start,
|
||||
);
|
||||
vga.write_crtc_controller(
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorEnd,
|
||||
cursor_end | scan_line_end,
|
||||
|
@ -146,12 +157,12 @@ pub trait TextWriter {
|
|||
let emulation_mode = vga.get_emulation_mode();
|
||||
let cursor_start = offset & 0xFF;
|
||||
let cursor_end = (offset >> 8) & 0xFF;
|
||||
vga.write_crtc_controller(
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorLocationLow,
|
||||
cursor_start as u8,
|
||||
);
|
||||
vga.write_crtc_controller(
|
||||
vga.crtc_controller_registers.write(
|
||||
emulation_mode,
|
||||
CrtcControllerIndex::TextCursorLocationHigh,
|
||||
cursor_end as u8,
|
||||
|
@ -167,3 +178,22 @@ pub trait TextWriter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper trait used to interact with various vga graphics modes.
|
||||
pub trait GraphicsWriter<Color> {
|
||||
/// Clears the screen by setting all pixels to the specified `color`.
|
||||
fn clear_screen(&self, color: Color);
|
||||
/// /// Draws a line from `start` to `end` with the specified `color`.
|
||||
fn draw_line(&self, start: Point<isize>, end: Point<isize>, color: Color);
|
||||
/// Draws a character at the given `(x, y)` coordinant to the specified `color`.
|
||||
fn draw_character(&self, x: usize, y: usize, character: char, color: Color);
|
||||
/// Sets the given pixel at `(x, y)` to the given `color`.
|
||||
///
|
||||
/// **Note:** This method is provided for convenience, but has terrible
|
||||
/// performance since it needs to ensure the correct `WriteMode` per pixel
|
||||
/// drawn. If you need to draw more then one pixel, consider using a method
|
||||
/// such as `draw_line`.
|
||||
fn set_pixel(&self, x: usize, y: usize, color: Color);
|
||||
/// Sets the graphics device to a `VideoMode`.
|
||||
fn set_mode(&self);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::TextWriter;
|
||||
use super::{Screen, TextWriter};
|
||||
use crate::{
|
||||
colors::DEFAULT_PALETTE,
|
||||
fonts::TEXT_8X16_FONT,
|
||||
|
@ -15,11 +15,11 @@ const HEIGHT: usize = 25;
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use vga::colors::{Color16Bit, TextModeColor};
|
||||
/// use vga::colors::{Color16, TextModeColor};
|
||||
/// use vga::writers::{ScreenCharacter, TextWriter, Text40x25};
|
||||
///
|
||||
/// let text_mode = Text40x25::new();
|
||||
/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
/// let color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
/// let screen_character = ScreenCharacter::new(b'T', color);
|
||||
///
|
||||
/// text_mode.set_mode();
|
||||
|
@ -29,7 +29,7 @@ const HEIGHT: usize = 25;
|
|||
#[derive(Default)]
|
||||
pub struct Text40x25;
|
||||
|
||||
impl TextWriter for Text40x25 {
|
||||
impl Screen for Text40x25 {
|
||||
fn get_width(&self) -> usize {
|
||||
WIDTH
|
||||
}
|
||||
|
@ -38,6 +38,12 @@ impl TextWriter for Text40x25 {
|
|||
HEIGHT
|
||||
}
|
||||
|
||||
fn get_size(&self) -> usize {
|
||||
WIDTH * HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
impl TextWriter for Text40x25 {
|
||||
/// Sets the graphics device to `VideoMode::Mode40x25`.
|
||||
fn set_mode(&self) {
|
||||
let mut vga = VGA.lock();
|
||||
|
@ -45,7 +51,7 @@ impl TextWriter for Text40x25 {
|
|||
|
||||
// Some bios mess up the palette when switching modes,
|
||||
// so explicitly set it.
|
||||
vga.load_palette(&DEFAULT_PALETTE);
|
||||
vga.color_palette_registers.load_palette(&DEFAULT_PALETTE);
|
||||
vga.load_font(&TEXT_8X16_FONT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::TextWriter;
|
||||
use super::{Screen, TextWriter};
|
||||
use crate::{
|
||||
colors::DEFAULT_PALETTE,
|
||||
fonts::TEXT_8X8_FONT,
|
||||
|
@ -15,11 +15,11 @@ const HEIGHT: usize = 50;
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use vga::colors::{Color16Bit, TextModeColor};
|
||||
/// use vga::colors::{Color16, TextModeColor};
|
||||
/// use vga::writers::{ScreenCharacter, TextWriter, Text40x50};
|
||||
///
|
||||
/// let text_mode = Text40x50::new();
|
||||
/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
/// let color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
/// let screen_character = ScreenCharacter::new(b'T', color);
|
||||
///
|
||||
/// text_mode.set_mode();
|
||||
|
@ -29,7 +29,7 @@ const HEIGHT: usize = 50;
|
|||
#[derive(Default)]
|
||||
pub struct Text40x50;
|
||||
|
||||
impl TextWriter for Text40x50 {
|
||||
impl Screen for Text40x50 {
|
||||
fn get_width(&self) -> usize {
|
||||
WIDTH
|
||||
}
|
||||
|
@ -38,6 +38,12 @@ impl TextWriter for Text40x50 {
|
|||
HEIGHT
|
||||
}
|
||||
|
||||
fn get_size(&self) -> usize {
|
||||
WIDTH * HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
impl TextWriter for Text40x50 {
|
||||
/// Sets the graphics device to `VideoMode::Mode40x50`.
|
||||
fn set_mode(&self) {
|
||||
let mut vga = VGA.lock();
|
||||
|
@ -45,7 +51,7 @@ impl TextWriter for Text40x50 {
|
|||
|
||||
// Some bios mess up the palette when switching modes,
|
||||
// so explicitly set it.
|
||||
vga.load_palette(&DEFAULT_PALETTE);
|
||||
vga.color_palette_registers.load_palette(&DEFAULT_PALETTE);
|
||||
vga.load_font(&TEXT_8X8_FONT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::TextWriter;
|
||||
use super::{Screen, TextWriter};
|
||||
use crate::{
|
||||
colors::DEFAULT_PALETTE,
|
||||
fonts::TEXT_8X16_FONT,
|
||||
|
@ -15,11 +15,11 @@ const HEIGHT: usize = 25;
|
|||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use vga::colors::{Color16Bit, TextModeColor};
|
||||
/// use vga::colors::{Color16, TextModeColor};
|
||||
/// use vga::writers::{ScreenCharacter, TextWriter, Text80x25};
|
||||
///
|
||||
/// let text_mode = Text80x25::new();
|
||||
/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black);
|
||||
/// let color = TextModeColor::new(Color16::Yellow, Color16::Black);
|
||||
/// let screen_character = ScreenCharacter::new(b'T', color);
|
||||
///
|
||||
/// text_mode.set_mode();
|
||||
|
@ -29,7 +29,7 @@ const HEIGHT: usize = 25;
|
|||
#[derive(Default)]
|
||||
pub struct Text80x25;
|
||||
|
||||
impl TextWriter for Text80x25 {
|
||||
impl Screen for Text80x25 {
|
||||
fn get_width(&self) -> usize {
|
||||
WIDTH
|
||||
}
|
||||
|
@ -38,13 +38,19 @@ impl TextWriter for Text80x25 {
|
|||
HEIGHT
|
||||
}
|
||||
|
||||
fn get_size(&self) -> usize {
|
||||
WIDTH * HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
impl TextWriter for Text80x25 {
|
||||
fn set_mode(&self) {
|
||||
let mut vga = VGA.lock();
|
||||
vga.set_video_mode(VideoMode::Mode80x25);
|
||||
|
||||
// Some bios mess up the palette when switching modes,
|
||||
// so explicitly set it.
|
||||
vga.load_palette(&DEFAULT_PALETTE);
|
||||
vga.color_palette_registers.load_palette(&DEFAULT_PALETTE);
|
||||
vga.load_font(&TEXT_8X16_FONT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,8 +83,8 @@ fn load_palette() {
|
|||
|
||||
let mut palette = [0u8; PALETTE_SIZE];
|
||||
let mut vga = VGA.lock();
|
||||
vga.load_palette(&DEFAULT_PALETTE);
|
||||
vga.read_palette(&mut palette);
|
||||
vga.color_palette_registers.load_palette(&DEFAULT_PALETTE);
|
||||
vga.color_palette_registers.read_palette(&mut palette);
|
||||
|
||||
for i in 0..PALETTE_SIZE {
|
||||
assert_eq!(palette[i], DEFAULT_PALETTE[i]);
|
||||
|
@ -95,23 +95,30 @@ fn load_palette() {
|
|||
|
||||
fn check_registers(vga: &mut Vga, configuration: &VgaConfiguration) {
|
||||
let emulation_mode = vga.get_emulation_mode();
|
||||
assert_eq!(vga.read_msr(), configuration.miscellaneous_output);
|
||||
assert_eq!(
|
||||
vga.general_registers.read_msr(),
|
||||
configuration.miscellaneous_output
|
||||
);
|
||||
|
||||
for (index, value) in configuration.sequencer_registers {
|
||||
assert_eq!(vga.read_sequencer(*index), *value);
|
||||
assert_eq!(vga.sequencer_registers.read(*index), *value);
|
||||
}
|
||||
|
||||
for (index, value) in configuration.crtc_controller_registers {
|
||||
assert_eq!(vga.read_crtc_controller(emulation_mode, *index), *value);
|
||||
assert_eq!(
|
||||
vga.crtc_controller_registers.read(emulation_mode, *index),
|
||||
*value
|
||||
);
|
||||
}
|
||||
|
||||
for (index, value) in configuration.graphics_controller_registers {
|
||||
assert_eq!(vga.read_graphics_controller(*index), *value);
|
||||
assert_eq!(vga.graphics_controller_registers.read(*index), *value);
|
||||
}
|
||||
|
||||
for (index, value) in configuration.attribute_controller_registers {
|
||||
assert_eq!(
|
||||
vga.read_attribute_controller(emulation_mode, *index),
|
||||
vga.attribute_controller_registers
|
||||
.read(emulation_mode, *index),
|
||||
*value
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue