pub mod dma;
pub mod kbc;
pub mod pic;
pub mod pit;
pub mod rom;
pub mod rtc;
pub mod sysctl;
#[cfg(feature = "dev-pc-apic")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-apic")))]
pub mod apic;
#[cfg(feature = "dev-pc-apic")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-apic")))]
pub mod ioapic;
#[cfg(feature = "dev-pc-apic")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-apic")))]
pub mod imcr;
#[cfg(feature = "dev-pc-hpet")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-hpet")))]
pub mod hpet;
#[cfg(feature = "dev-pc-pci")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-pci")))]
pub mod pmc;
#[cfg(feature = "dev-pc-pci")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-pci")))]
pub mod vgapci;
#[cfg(feature = "dev-pc-video")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-video")))]
pub mod video;
#[cfg(feature = "dev-pc-floppy")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-floppy")))]
pub mod fdc;
#[cfg(feature = "dev-pc-ide")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-pc-ide")))]
pub mod ide;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::core::error::Result;
use crate::core::registry::Registry;
use crate::core::space::MemOps;
use crate::machine::realize::Bindings;
use crate::machine::validate::ClassSchema;
#[derive(Debug)]
pub struct PortPassthrough(Arc<dyn MemOps>);
impl PortPassthrough {
#[must_use]
pub fn new(ops: Arc<dyn MemOps>) -> PortPassthrough {
PortPassthrough(ops)
}
#[must_use]
pub fn ops(&self) -> &Arc<dyn MemOps> {
&self.0
}
}
pub fn register(reg: &mut Registry) -> Result<()> {
dma::register(reg)?;
kbc::register(reg)?;
pic::register(reg)?;
pit::register(reg)?;
rom::register(reg)?;
rtc::register(reg)?;
sysctl::register(reg)?;
#[cfg(feature = "dev-pc-apic")]
{
apic::register(reg)?;
ioapic::register(reg)?;
imcr::register(reg)?;
}
#[cfg(feature = "dev-pc-hpet")]
hpet::register(reg)?;
#[cfg(feature = "dev-pc-pci")]
pmc::register(reg)?;
#[cfg(feature = "dev-pc-pci")]
vgapci::register(reg)?;
#[cfg(feature = "dev-pc-video")]
video::register(reg)?;
#[cfg(feature = "dev-pc-floppy")]
fdc::register(reg)?;
#[cfg(feature = "dev-pc-ide")]
ide::register(reg)?;
Ok(())
}
pub fn bind(b: &mut Bindings) -> Result<()> {
dma::bind(b)?;
kbc::bind(b)?;
pic::bind(b)?;
pit::bind(b)?;
rom::bind(b)?;
rtc::bind(b)?;
sysctl::bind(b)?;
#[cfg(feature = "dev-pc-apic")]
{
apic::bind(b)?;
ioapic::bind(b)?;
imcr::bind(b)?;
}
#[cfg(feature = "dev-pc-hpet")]
hpet::bind(b)?;
#[cfg(feature = "dev-pc-pci")]
pmc::bind(b)?;
#[cfg(feature = "dev-pc-pci")]
vgapci::bind(b)?;
#[cfg(feature = "dev-pc-video")]
video::bind(b)?;
#[cfg(feature = "dev-pc-floppy")]
fdc::bind(b)?;
#[cfg(feature = "dev-pc-ide")]
ide::bind(b)?;
Ok(())
}
#[must_use]
pub fn schemas() -> Vec<ClassSchema> {
#[allow(unused_mut)]
let mut out = alloc::vec![
dma::schema(),
kbc::schema(),
pic::schema(),
pit::schema(),
rom::schema(),
rtc::schema(),
sysctl::schema(),
];
#[cfg(feature = "dev-pc-apic")]
{
out.push(apic::schema());
out.push(ioapic::schema());
out.push(imcr::schema());
}
#[cfg(feature = "dev-pc-hpet")]
out.push(hpet::schema());
#[cfg(feature = "dev-pc-pci")]
out.push(pmc::schema());
#[cfg(feature = "dev-pc-pci")]
out.push(vgapci::schema());
#[cfg(feature = "dev-pc-video")]
out.push(video::schema());
#[cfg(feature = "dev-pc-floppy")]
out.push(fdc::schema());
#[cfg(feature = "dev-pc-ide")]
out.push(ide::schema());
out
}
pub const PC_AT: &str = include_str!("../../../machines/pc-at.machine");
#[cfg(test)]
mod tests {
use super::*;
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide"
))]
use crate::machine::ClassTable;
use crate::machine::ResolveOptions;
use crate::machine::resolve_file;
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide"
))]
use crate::machine::validate::{ClassSchema, ValidateOptions, validate};
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide",
not(feature = "cpu-x86")
))]
use crate::machine::validate::{PortDir, PropSchema};
use alloc::string::ToString;
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide"
))]
fn x86_schema() -> ClassSchema {
#[cfg(feature = "cpu-x86")]
{
crate::cpu::x86::schemas()
.into_iter()
.find(|s| s.class == "cpu.x86")
.expect("the core publishes both of its class names")
}
#[cfg(not(feature = "cpu-x86"))]
{
ClassSchema::new("cpu.x86")
.prop(PropSchema::new("model", crate::core::props::ValueKind::Str))
.prop(PropSchema::new(
"engine",
crate::core::props::ValueKind::Str,
))
.prop(PropSchema::new(
"iospace",
crate::core::props::ValueKind::Str,
))
.port("intr", PortDir::In)
.port("nmi", PortDir::In)
.port("reset", PortDir::In)
.port("a20", PortDir::In)
}
}
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide"
))]
fn classes() -> ClassTable {
let mut table = ClassTable::new();
for schema in crate::machine::builtin::schemas() {
table.insert(schema);
}
for schema in schemas() {
table.insert(schema);
}
table.insert(x86_schema());
table
}
#[test]
fn the_board_parses_and_resolves() {
let resolved = match resolve_file("pc-at.machine", PC_AT, &ResolveOptions::new()) {
Ok(r) => r,
Err(e) => panic!("{e}"),
};
assert_eq!(resolved.name, "pc-at");
assert_eq!(resolved.oscillators.len(), 8);
let pit = resolved
.oscillators
.iter()
.find(|o| o.name == "pit")
.expect("the timer's crystal");
assert_eq!(pit.hz.denominator(), 11);
assert_eq!(pit.hz.numerator(), 13125000);
assert_eq!(resolved.spaces.len(), 2, "memory and I/O are separate");
}
#[cfg(all(
feature = "dev-pc-apic",
feature = "dev-pc-hpet",
feature = "dev-pc-video",
feature = "dev-pc-floppy",
feature = "dev-pc-ide"
))]
#[test]
fn the_board_validates_against_this_builds_classes() {
let resolved =
resolve_file("pc-at.machine", PC_AT, &ResolveOptions::new()).expect("it resolves");
if let Err(d) = validate(&resolved, &classes(), &ValidateOptions::new()) {
panic!("{}", d.message);
}
}
#[test]
fn the_board_names_exactly_the_media_slots_it_documents() {
let resolved =
resolve_file("pc-at.machine", PC_AT, &ResolveOptions::new()).expect("it resolves");
let mut slots: alloc::vec::Vec<alloc::string::String> = resolved
.objects
.iter()
.filter_map(|o| o.props.get("image"))
.filter_map(|v| v.as_str().map(ToString::to_string))
.collect();
slots.sort();
slots.dedup();
assert_eq!(slots, ["bios", "floppy", "hd0", "hd1", "vgabios"]);
}
}