abc712233b
Closes #244 Move PCI to arch Plus spelling Added new out and in functions Added new out and in to mocking Return pci devices as a list Improved comment Removed mask for the return Removed type for OUT Added new types
463 lines
13 KiB
Zig
463 lines
13 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const is_test = builtin.is_test;
|
|
const expectEqual = std.testing.expectEqual;
|
|
const build_options = @import("build_options");
|
|
const mock_path = build_options.arch_mock_path;
|
|
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
|
|
|
|
/// The current year to be used for calculating the 4 digit year, as the CMOS return the last two
|
|
/// digits of the year.
|
|
const CURRENT_CENTURY: u8 = 2000;
|
|
|
|
/// The port address for the CMOS command register.
|
|
const ADDRESS: u16 = 0x70;
|
|
|
|
/// The port address for the CMOS data register.
|
|
const DATA: u16 = 0x71;
|
|
|
|
/// The register location for returning the seconds, (0 - 59).
|
|
const REGISTER_SECOND: u8 = 0x00;
|
|
|
|
/// The register location for returning the minute, (0 - 59).
|
|
const REGISTER_MINUTE: u8 = 0x02;
|
|
|
|
/// The register location for returning the hours, (0 - 23 or 0 - 12 depending if a 12hr or 24hr
|
|
/// clock).
|
|
const REGISTER_HOUR: u8 = 0x04;
|
|
|
|
/// The register location for returning the weekday, (0 - 6). Very unreliable, so will calculate
|
|
/// the day of the week instead.
|
|
const REGISTER_WEEKDAY: u8 = 0x06;
|
|
|
|
/// The register location for returning the day, (0 - 31).
|
|
const REGISTER_DAY: u8 = 0x07;
|
|
|
|
/// The register location for returning the month, (0 - 11).
|
|
const REGISTER_MONTH: u8 = 0x08;
|
|
|
|
/// The register location for returning the year, (0 - 99).
|
|
const REGISTER_YEAR: u8 = 0x09;
|
|
|
|
/// The register location for returning the century.
|
|
const REGISTER_CENTURY: u8 = 0x32;
|
|
|
|
/// The register location for return the status A register.
|
|
const STATUS_REGISTER_A: u8 = 0x0A;
|
|
|
|
/// The register location for return the status B register.
|
|
const STATUS_REGISTER_B: u8 = 0x0B;
|
|
|
|
/// The register location for return the status C register.
|
|
const STATUS_REGISTER_C: u8 = 0x0C;
|
|
|
|
/// The non-mockable interrupts are on the 8th bit of port 0x70 which is the register select port
|
|
/// for the CMOS. This will need to be disabled when selecting CMOS registers.
|
|
const NMI_BIT: u8 = 0x80;
|
|
|
|
/// The enum for selecting the status register to read from
|
|
pub const StatusRegister = enum {
|
|
/// Status register A
|
|
A,
|
|
|
|
/// Status register B
|
|
B,
|
|
|
|
/// Status register C
|
|
C,
|
|
|
|
///
|
|
/// Get the register index for the status registers
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: StatusRegister - The enum that represents one of the 3 status registers.
|
|
///
|
|
/// Return: u8
|
|
/// The register index for one of the 3 status registers.
|
|
///
|
|
pub fn getRegister(reg: StatusRegister) u8 {
|
|
return switch (reg) {
|
|
.A => STATUS_REGISTER_A,
|
|
.B => STATUS_REGISTER_B,
|
|
.C => STATUS_REGISTER_C,
|
|
};
|
|
}
|
|
};
|
|
|
|
/// The enum for selecting the real time clock registers.
|
|
pub const RtcRegister = enum {
|
|
/// The seconds register
|
|
SECOND,
|
|
|
|
/// The minutes register
|
|
MINUTE,
|
|
|
|
/// The hours register
|
|
HOUR,
|
|
|
|
/// The days register
|
|
DAY,
|
|
|
|
/// The months register
|
|
MONTH,
|
|
|
|
/// The year register
|
|
YEAR,
|
|
|
|
/// The century register
|
|
CENTURY,
|
|
|
|
///
|
|
/// Get the register index for the RTC registers
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: RtcRegister - The enum that represents one of the RTC registers.
|
|
///
|
|
/// Return: u8
|
|
/// The register index for one of the RTC registers.
|
|
///
|
|
pub fn getRegister(reg: RtcRegister) u8 {
|
|
return switch (reg) {
|
|
.SECOND => REGISTER_SECOND,
|
|
.MINUTE => REGISTER_MINUTE,
|
|
.HOUR => REGISTER_HOUR,
|
|
.DAY => REGISTER_DAY,
|
|
.MONTH => REGISTER_MONTH,
|
|
.YEAR => REGISTER_YEAR,
|
|
.CENTURY => REGISTER_CENTURY,
|
|
};
|
|
}
|
|
};
|
|
|
|
///
|
|
/// Tell the CMOS chip to select the given register ready for read or writing to. This also
|
|
/// disables the NMI when disable_nmi is true.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: u8 - The register index to select in the CMOS chip.
|
|
/// IN comptime disable_nmi: bool - Whether to disable NMI when selecting a register.
|
|
///
|
|
inline fn selectRegister(reg: u8, comptime disable_nmi: bool) void {
|
|
if (disable_nmi) {
|
|
arch.out(ADDRESS, reg | NMI_BIT);
|
|
} else {
|
|
arch.out(ADDRESS, reg);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Write to the selected register to the CMOS chip.
|
|
///
|
|
/// Arguments:
|
|
/// IN data: u8 - The data to write to the selected register.
|
|
///
|
|
inline fn writeRegister(data: u8) void {
|
|
arch.out(DATA, data);
|
|
}
|
|
|
|
///
|
|
/// Read the selected register from the CMOS chip.
|
|
///
|
|
/// Return: u8
|
|
/// The value in the selected register.
|
|
///
|
|
inline fn readRegister() u8 {
|
|
return arch.in(u8, DATA);
|
|
}
|
|
|
|
///
|
|
/// Select then read a register from the CMOS chip. This include a I/O wait to ensure the CMOS chip
|
|
/// has time to select the register.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: u8 - The register index to select in the CMOS chip.
|
|
/// IN comptime disable_nmi: bool - Whether to disable NMI when selecting a register.
|
|
///
|
|
/// Return: u8
|
|
/// The value in the selected register.
|
|
///
|
|
inline fn selectAndReadRegister(reg: u8, comptime disable_nmi: bool) u8 {
|
|
selectRegister(reg, disable_nmi);
|
|
arch.ioWait();
|
|
return readRegister();
|
|
}
|
|
|
|
///
|
|
/// Select then write to a register to the CMOS chip. This include a I/O wait to ensure the CMOS chip
|
|
/// has time to select the register.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: u8 - The register index to select in the CMOS chip.
|
|
/// IN data: u8 - The data to write to the selected register.
|
|
/// IN comptime disable_nmi: bool - Whether to disable NMI when selecting a register.
|
|
///
|
|
inline fn selectAndWriteRegister(reg: u8, data: u8, comptime disable_nmi: bool) void {
|
|
selectRegister(reg, disable_nmi);
|
|
arch.ioWait();
|
|
writeRegister(data);
|
|
}
|
|
|
|
///
|
|
/// Read a register that corresponds to a real time clock register.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: RtcRegister - A RTC register to select in the CMOS chip.
|
|
///
|
|
/// Return: u8
|
|
/// The value in the selected register.
|
|
///
|
|
pub fn readRtcRegister(reg: RtcRegister) u8 {
|
|
return selectAndReadRegister(reg.getRegister(), false);
|
|
}
|
|
|
|
///
|
|
/// Read a status register in the CMOS chip.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: StatusRegister - The status register to select.
|
|
/// IN comptime disable_nmi: bool - Whether to disable NMI when selecting a register.
|
|
///
|
|
/// Return: u8
|
|
/// The value in the selected register.
|
|
///
|
|
pub fn readStatusRegister(reg: StatusRegister, comptime disable_nmi: bool) u8 {
|
|
return selectAndReadRegister(reg.getRegister(), disable_nmi);
|
|
}
|
|
|
|
///
|
|
/// Write to a status register in the CMOS chip.
|
|
///
|
|
/// Arguments:
|
|
/// IN reg: StatusRegister - The status register to select.
|
|
/// IN data: u8 - The data to write to the selected register.
|
|
/// IN comptime disable_nmi: bool - Whether to disable NMI when selecting a register.
|
|
///
|
|
pub fn writeStatusRegister(reg: StatusRegister, data: u8, comptime disable_nmi: bool) void {
|
|
selectAndWriteRegister(reg.getRegister(), data, disable_nmi);
|
|
}
|
|
|
|
test "selectRegister" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_A });
|
|
|
|
const reg = STATUS_REGISTER_A;
|
|
|
|
selectRegister(reg, false);
|
|
}
|
|
|
|
test "selectRegister no NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_A | NMI_BIT });
|
|
|
|
const reg = STATUS_REGISTER_A;
|
|
|
|
selectRegister(reg, true);
|
|
}
|
|
|
|
test "writeRegister" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ DATA, @as(u8, 0xAA) });
|
|
|
|
const data = @as(u8, 0xAA);
|
|
|
|
writeRegister(data);
|
|
}
|
|
|
|
test "readRegister" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x55) });
|
|
|
|
const expected = @as(u8, 0x55);
|
|
const actual = readRegister();
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
|
|
test "selectAndReadRegister NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C });
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x44) });
|
|
arch.addConsumeFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const reg = STATUS_REGISTER_C;
|
|
|
|
const expected = @as(u8, 0x44);
|
|
const actual = selectAndReadRegister(reg, false);
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
|
|
test "selectAndReadRegister no NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT });
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x44) });
|
|
arch.addConsumeFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const reg = STATUS_REGISTER_C;
|
|
|
|
const expected = @as(u8, 0x44);
|
|
const actual = selectAndReadRegister(reg, true);
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
|
|
test "selectAndWriteRegister NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C, DATA, @as(u8, 0x88) });
|
|
arch.addConsumeFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const reg = STATUS_REGISTER_C;
|
|
const data = @as(u8, 0x88);
|
|
|
|
selectAndWriteRegister(reg, data, false);
|
|
}
|
|
|
|
test "selectAndWriteRegister no NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT, DATA, @as(u8, 0x88) });
|
|
arch.addConsumeFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const reg = STATUS_REGISTER_C;
|
|
const data = @as(u8, 0x88);
|
|
|
|
selectAndWriteRegister(reg, data, true);
|
|
}
|
|
|
|
test "readRtcRegister" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const rtc_regs = [_]RtcRegister{ RtcRegister.SECOND, RtcRegister.MINUTE, RtcRegister.HOUR, RtcRegister.DAY, RtcRegister.MONTH, RtcRegister.YEAR, RtcRegister.CENTURY };
|
|
|
|
for (rtc_regs) |reg| {
|
|
const r = switch (reg) {
|
|
.SECOND => REGISTER_SECOND,
|
|
.MINUTE => REGISTER_MINUTE,
|
|
.HOUR => REGISTER_HOUR,
|
|
.DAY => REGISTER_DAY,
|
|
.MONTH => REGISTER_MONTH,
|
|
.YEAR => REGISTER_YEAR,
|
|
.CENTURY => REGISTER_CENTURY,
|
|
};
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, r });
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x44) });
|
|
|
|
const expected = @as(u8, 0x44);
|
|
const actual = readRtcRegister(reg);
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
}
|
|
|
|
test "readStatusRegister NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const status_regs = [_]StatusRegister{ StatusRegister.A, StatusRegister.B, StatusRegister.C };
|
|
|
|
for (status_regs) |reg| {
|
|
const r = switch (reg) {
|
|
.A => STATUS_REGISTER_A,
|
|
.B => STATUS_REGISTER_B,
|
|
.C => STATUS_REGISTER_C,
|
|
};
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, r });
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x78) });
|
|
|
|
const expected = @as(u8, 0x78);
|
|
const actual = readStatusRegister(reg, false);
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
}
|
|
|
|
test "readStatusRegister no NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const status_regs = [_]StatusRegister{ StatusRegister.A, StatusRegister.B, StatusRegister.C };
|
|
|
|
for (status_regs) |reg| {
|
|
const r = switch (reg) {
|
|
.A => STATUS_REGISTER_A,
|
|
.B => STATUS_REGISTER_B,
|
|
.C => STATUS_REGISTER_C,
|
|
};
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, r | NMI_BIT });
|
|
arch.addTestParams("in", .{ DATA, @as(u8, 0x78) });
|
|
|
|
const expected = @as(u8, 0x78);
|
|
const actual = readStatusRegister(reg, true);
|
|
|
|
expectEqual(expected, actual);
|
|
}
|
|
}
|
|
|
|
test "writeStatusRegister NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const status_regs = [_]StatusRegister{ StatusRegister.A, StatusRegister.B, StatusRegister.C };
|
|
|
|
for (status_regs) |reg| {
|
|
const r = switch (reg) {
|
|
.A => STATUS_REGISTER_A,
|
|
.B => STATUS_REGISTER_B,
|
|
.C => STATUS_REGISTER_C,
|
|
};
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, r, DATA, @as(u8, 0x43) });
|
|
|
|
const data = @as(u8, 0x43);
|
|
writeStatusRegister(reg, data, false);
|
|
}
|
|
}
|
|
|
|
test "writeStatusRegister no NMI" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
|
|
|
const status_regs = [_]StatusRegister{ StatusRegister.A, StatusRegister.B, StatusRegister.C };
|
|
|
|
for (status_regs) |reg| {
|
|
const r = switch (reg) {
|
|
.A => STATUS_REGISTER_A,
|
|
.B => STATUS_REGISTER_B,
|
|
.C => STATUS_REGISTER_C,
|
|
};
|
|
|
|
arch.addTestParams("out", .{ ADDRESS, r | NMI_BIT, DATA, @as(u8, 0x43) });
|
|
|
|
const data = @as(u8, 0x43);
|
|
writeStatusRegister(reg, data, true);
|
|
}
|
|
}
|