Added RTC
Added I/O waits to PIC remapping Added fmt step to build When building will format all the code to the standard Fixed cascading interrupts Re-named to selectAnd*Register. Moved switching on registers into emun Removed build fmt step
This commit is contained in:
parent
96da426a3a
commit
7ab180f622
8 changed files with 1336 additions and 14 deletions
|
@ -7,6 +7,7 @@ const pic = @import("pic.zig");
|
||||||
const irq = @import("irq.zig");
|
const irq = @import("irq.zig");
|
||||||
const isr = @import("isr.zig");
|
const isr = @import("isr.zig");
|
||||||
const pit = @import("pit.zig");
|
const pit = @import("pit.zig");
|
||||||
|
const rtc = @import("rtc.zig");
|
||||||
const paging = @import("paging.zig");
|
const paging = @import("paging.zig");
|
||||||
const syscalls = @import("syscalls.zig");
|
const syscalls = @import("syscalls.zig");
|
||||||
const mem = @import("../../mem.zig");
|
const mem = @import("../../mem.zig");
|
||||||
|
@ -81,6 +82,13 @@ pub fn inb(port: u16) u8 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Force the CPU to wait for an I/O operation to compete. Use port 0x80 as this is unused.
|
||||||
|
///
|
||||||
|
pub fn ioWait() void {
|
||||||
|
outb(0x80, 0);
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Load the GDT and refreshing the code segment with the code segment offset of the kernel as we
|
/// Load the GDT and refreshing the code segment with the code segment offset of the kernel as we
|
||||||
/// are still in kernel land. Also loads the kernel data segment into all the other segment
|
/// are still in kernel land. Also loads the kernel data segment into all the other segment
|
||||||
|
@ -230,10 +238,11 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
|
||||||
isr.init();
|
isr.init();
|
||||||
irq.init();
|
irq.init();
|
||||||
|
|
||||||
pit.init();
|
|
||||||
|
|
||||||
paging.init(mb_info, mem_profile, allocator);
|
paging.init(mb_info, mem_profile, allocator);
|
||||||
|
|
||||||
|
pit.init();
|
||||||
|
rtc.init();
|
||||||
|
|
||||||
syscalls.init();
|
syscalls.init();
|
||||||
|
|
||||||
enableInterrupts();
|
enableInterrupts();
|
||||||
|
@ -246,6 +255,8 @@ test "" {
|
||||||
_ = @import("isr.zig");
|
_ = @import("isr.zig");
|
||||||
_ = @import("irq.zig");
|
_ = @import("irq.zig");
|
||||||
_ = @import("pit.zig");
|
_ = @import("pit.zig");
|
||||||
|
_ = @import("cmos.zig");
|
||||||
|
_ = @import("rtc.zig");
|
||||||
_ = @import("syscalls.zig");
|
_ = @import("syscalls.zig");
|
||||||
_ = @import("paging.zig");
|
_ = @import("paging.zig");
|
||||||
}
|
}
|
||||||
|
|
463
src/kernel/arch/x86/cmos.zig
Normal file
463
src/kernel/arch/x86/cmos.zig
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
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.outb(ADDRESS, reg | NMI_BIT);
|
||||||
|
} else {
|
||||||
|
arch.outb(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.outb(DATA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the selected register from the CMOS chip.
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// The value in the selected register.
|
||||||
|
///
|
||||||
|
inline fn readRegister() u8 {
|
||||||
|
return arch.inb(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("outb", .{ ADDRESS, STATUS_REGISTER_A });
|
||||||
|
|
||||||
|
const reg = STATUS_REGISTER_A;
|
||||||
|
|
||||||
|
selectRegister(reg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selectRegister no NMI" {
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_A | NMI_BIT });
|
||||||
|
|
||||||
|
const reg = STATUS_REGISTER_A;
|
||||||
|
|
||||||
|
selectRegister(reg, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "writeRegister" {
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("outb", .{ DATA, @as(u8, 0xAA) });
|
||||||
|
|
||||||
|
const data = @as(u8, 0xAA);
|
||||||
|
|
||||||
|
writeRegister(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readRegister" {
|
||||||
|
arch.initTest();
|
||||||
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ ADDRESS, STATUS_REGISTER_C });
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT });
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ 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("outb", .{ 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("outb", .{ ADDRESS, r });
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ ADDRESS, r });
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ ADDRESS, r | NMI_BIT });
|
||||||
|
arch.addTestParams("inb", .{ 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("outb", .{ 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("outb", .{ ADDRESS, r | NMI_BIT, DATA, @as(u8, 0x43) });
|
||||||
|
|
||||||
|
const data = @as(u8, 0x43);
|
||||||
|
writeStatusRegister(reg, data, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -436,23 +436,36 @@ pub fn init() void {
|
||||||
|
|
||||||
// Initiate
|
// Initiate
|
||||||
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
||||||
|
arch.ioWait();
|
||||||
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
|
||||||
|
arch.ioWait();
|
||||||
|
|
||||||
// Offsets
|
// Offsets
|
||||||
sendDataMaster(ICW2_MASTER_REMAP_OFFSET);
|
sendDataMaster(ICW2_MASTER_REMAP_OFFSET);
|
||||||
|
arch.ioWait();
|
||||||
sendDataSlave(ICW2_SLAVE_REMAP_OFFSET);
|
sendDataSlave(ICW2_SLAVE_REMAP_OFFSET);
|
||||||
|
arch.ioWait();
|
||||||
|
|
||||||
// IRQ lines
|
// IRQ lines
|
||||||
sendDataMaster(ICW3_MASTER_IRQ_MAP_FROM_SLAVE);
|
sendDataMaster(ICW3_MASTER_IRQ_MAP_FROM_SLAVE);
|
||||||
|
arch.ioWait();
|
||||||
sendDataSlave(ICW3_SLAVE_IRQ_MAP_TO_MASTER);
|
sendDataSlave(ICW3_SLAVE_IRQ_MAP_TO_MASTER);
|
||||||
|
arch.ioWait();
|
||||||
|
|
||||||
// 80x86 mode
|
// 80x86 mode
|
||||||
sendDataMaster(ICW4_80x86_MODE);
|
sendDataMaster(ICW4_80x86_MODE);
|
||||||
|
arch.ioWait();
|
||||||
sendDataSlave(ICW4_80x86_MODE);
|
sendDataSlave(ICW4_80x86_MODE);
|
||||||
|
arch.ioWait();
|
||||||
|
|
||||||
// Mask all interrupts
|
// Mask all interrupts
|
||||||
sendDataMaster(0xFF);
|
sendDataMaster(0xFF);
|
||||||
|
arch.ioWait();
|
||||||
sendDataSlave(0xFF);
|
sendDataSlave(0xFF);
|
||||||
|
arch.ioWait();
|
||||||
|
|
||||||
|
// Clear the IRQ for the slave
|
||||||
|
clearMask(IRQ_CASCADE_FOR_SLAVE);
|
||||||
|
|
||||||
log.logInfo("Done\n", .{});
|
log.logInfo("Done\n", .{});
|
||||||
|
|
||||||
|
@ -763,6 +776,8 @@ test "init" {
|
||||||
arch.initTest();
|
arch.initTest();
|
||||||
defer arch.freeTest();
|
defer arch.freeTest();
|
||||||
|
|
||||||
|
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
|
||||||
|
|
||||||
// Just a long list of OUT instructions setting up the PIC
|
// Just a long list of OUT instructions setting up the PIC
|
||||||
arch.addTestParams("outb", .{
|
arch.addTestParams("outb", .{
|
||||||
MASTER_COMMAND_REG,
|
MASTER_COMMAND_REG,
|
||||||
|
@ -785,8 +800,12 @@ test "init" {
|
||||||
@as(u8, 0xFF),
|
@as(u8, 0xFF),
|
||||||
SLAVE_DATA_REG,
|
SLAVE_DATA_REG,
|
||||||
@as(u8, 0xFF),
|
@as(u8, 0xFF),
|
||||||
|
MASTER_DATA_REG,
|
||||||
|
@as(u8, 0xFB),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +813,8 @@ test "init" {
|
||||||
/// Test that all the PIC masks are set so no interrupts can fire.
|
/// Test that all the PIC masks are set so no interrupts can fire.
|
||||||
///
|
///
|
||||||
fn rt_picAllMasked() void {
|
fn rt_picAllMasked() void {
|
||||||
if (readDataMaster() != 0xFF) {
|
// The master will have interrupt 2 clear because this is the link to the slave (third bit)
|
||||||
|
if (readDataMaster() != 0xFB) {
|
||||||
panic(@errorReturnTrace(), "Master masks are not set, found: {}\n", .{readDataMaster()});
|
panic(@errorReturnTrace(), "Master masks are not set, found: {}\n", .{readDataMaster()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
745
src/kernel/arch/x86/rtc.zig
Normal file
745
src/kernel/arch/x86/rtc.zig
Normal file
|
@ -0,0 +1,745 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const is_test = builtin.is_test;
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
const expectError = std.testing.expectError;
|
||||||
|
const build_options = @import("build_options");
|
||||||
|
const mock_path = build_options.arch_mock_path;
|
||||||
|
const arch = @import("arch.zig");
|
||||||
|
const log = @import("../../log.zig");
|
||||||
|
const pic = @import("pic.zig");
|
||||||
|
const pit = @import("pit.zig");
|
||||||
|
const irq = @import("irq.zig");
|
||||||
|
const cmos = if (is_test) @import(mock_path ++ "cmos_mock.zig") else @import("cmos.zig");
|
||||||
|
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
|
||||||
|
|
||||||
|
/// The Century register is unreliable. We need a APIC interface to infer if we have a century
|
||||||
|
/// register. So this is a current TODO.
|
||||||
|
const CURRENT_CENTURY: u32 = 2000;
|
||||||
|
|
||||||
|
/// TODO: To do with the unreliable century register. Once have APIC, can use this as a sanity check
|
||||||
|
/// if the the century register gives a wild answer then the other RTC values maybe wild. So then
|
||||||
|
/// could report that the CMOS chip is faulty or the battery is dyeing.
|
||||||
|
const CENTURY_REGISTER: bool = false;
|
||||||
|
|
||||||
|
/// A structure to hold all the date and time information in the RTC.
|
||||||
|
const DateTime = struct {
|
||||||
|
second: u32,
|
||||||
|
minute: u32,
|
||||||
|
hour: u32,
|
||||||
|
day: u32,
|
||||||
|
month: u32,
|
||||||
|
year: u32,
|
||||||
|
century: u32,
|
||||||
|
day_of_week: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The error set that can be returned from some RTC functions.
|
||||||
|
const RtcError = error{
|
||||||
|
/// If setting the rate for interrupts is less than 3 or greater than 15.
|
||||||
|
RateError,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The number of ticks that has passed when RTC was initially set up.
|
||||||
|
var ticks: u32 = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks if the CMOS chip isn't updating the RTC registers. Call this before reading any RTC
|
||||||
|
/// registers so don't get inconsistent values.
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the CMOS chip is buzzy and a update is in progress.
|
||||||
|
///
|
||||||
|
fn isBuzzy() bool {
|
||||||
|
return (cmos.readStatusRegister(cmos.StatusRegister.A, false) & 0x80) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Calculate the day of the week from the given day, month and year.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN date_time: DateTime - The DateTime structure that holds the current day, month and year.
|
||||||
|
///
|
||||||
|
/// Return: u8
|
||||||
|
/// A number that represents the day of the week. 1 = Sunday, 2 = Monday, ...
|
||||||
|
///
|
||||||
|
fn calcDayOfWeek(date_time: DateTime) u32 {
|
||||||
|
const t = [_]u8{ 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
|
||||||
|
const year = date_time.year - @boolToInt(date_time.month < 3);
|
||||||
|
const month = date_time.month;
|
||||||
|
const day = date_time.day;
|
||||||
|
|
||||||
|
return (year + (year / 4) - (year / 100) + (year / 400) + t[month - 1] + day) % 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if the RTC is in binary coded decimal mode. If the RTC ic counting in BCD, then the 3rd
|
||||||
|
/// bit in the status register B will be set.
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// When the RTC is counting in BCD.
|
||||||
|
///
|
||||||
|
fn isBcd() bool {
|
||||||
|
const reg_b = cmos.readStatusRegister(cmos.StatusRegister.B, false);
|
||||||
|
return reg_b & 0x04 != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if the RTC in 12 hour mode. If the RTC is in 12 hour mode, then the 2nd bit in the status
|
||||||
|
/// register B is set and the most significant bit on the hour field is set.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN date_time: DateTime - The DateTime structure containing at least the hour field set.
|
||||||
|
///
|
||||||
|
/// Return: bool
|
||||||
|
/// Whether the RTC is in 12 hour mode.
|
||||||
|
///
|
||||||
|
fn is12Hr(date_time: DateTime) bool {
|
||||||
|
const reg_b = cmos.readStatusRegister(cmos.StatusRegister.B, false);
|
||||||
|
return reg_b & 0x02 != 0 and date_time.hour & 0x80 != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert BCD to binary.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN bcd: u32 - The binary coded decimal value to convert
|
||||||
|
///
|
||||||
|
/// Return: u32
|
||||||
|
/// The converted BCD value.
|
||||||
|
///
|
||||||
|
fn bcdToBinary(bcd: u32) u32 {
|
||||||
|
return ((bcd & 0xF0) >> 1) + ((bcd & 0xF0) >> 3) + (bcd & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read the real time clock registers and return them in the DateTime structure. This will read
|
||||||
|
/// the seconds, minutes, hours, days, months, years, century and day of the week.
|
||||||
|
///
|
||||||
|
/// Return: DateTime
|
||||||
|
/// The data from the CMOS RTC registers.
|
||||||
|
///
|
||||||
|
fn readRtcRegisters() DateTime {
|
||||||
|
// Make sure there isn't a update in progress
|
||||||
|
while (isBuzzy()) {}
|
||||||
|
|
||||||
|
var date_time = DateTime{
|
||||||
|
.second = cmos.readRtcRegister(cmos.RtcRegister.SECOND),
|
||||||
|
.minute = cmos.readRtcRegister(cmos.RtcRegister.MINUTE),
|
||||||
|
.hour = cmos.readRtcRegister(cmos.RtcRegister.HOUR),
|
||||||
|
.day = cmos.readRtcRegister(cmos.RtcRegister.DAY),
|
||||||
|
.month = cmos.readRtcRegister(cmos.RtcRegister.MONTH),
|
||||||
|
.year = cmos.readRtcRegister(cmos.RtcRegister.YEAR),
|
||||||
|
.century = if (CENTURY_REGISTER) cmos.readRtcRegister(cmos.RtcRegister.CENTURY) else CURRENT_CENTURY,
|
||||||
|
// This will be filled in later
|
||||||
|
.day_of_week = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The day of the week register is also very unreliable, so is better to calculate it
|
||||||
|
date_time.day_of_week = calcDayOfWeek(date_time);
|
||||||
|
|
||||||
|
return date_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Read a stable time from the real time clock registers on the CMOS chip and return a BCD and
|
||||||
|
/// 12 hour converted date and time.
|
||||||
|
///
|
||||||
|
/// Return: DateTime
|
||||||
|
/// The data from the CMOS RTC registers with correct BCD conversions, 12 hour conversions and
|
||||||
|
/// the century added to the year.
|
||||||
|
///
|
||||||
|
fn readRtc() DateTime {
|
||||||
|
var date_time1 = readRtcRegisters();
|
||||||
|
var date_time2 = readRtcRegisters();
|
||||||
|
|
||||||
|
// Use the method: Read the registers twice and check if they are the same so to avoid
|
||||||
|
// inconsistent values due to RTC updates
|
||||||
|
|
||||||
|
var compare = false;
|
||||||
|
|
||||||
|
inline for (@typeInfo(DateTime).Struct.fields) |field| {
|
||||||
|
compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (compare) {
|
||||||
|
date_time1 = readRtcRegisters();
|
||||||
|
date_time2 = readRtcRegisters();
|
||||||
|
|
||||||
|
compare = false;
|
||||||
|
inline for (@typeInfo(DateTime).Struct.fields) |field| {
|
||||||
|
compare = compare or @field(date_time1, field.name) != @field(date_time2, field.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert BCD to binary if necessary
|
||||||
|
if (isBcd()) {
|
||||||
|
date_time1.second = bcdToBinary(date_time1.second);
|
||||||
|
date_time1.minute = bcdToBinary(date_time1.minute);
|
||||||
|
// Needs a special calculation because the upper bit is set
|
||||||
|
date_time1.hour = ((date_time1.hour & 0x0F) + (((date_time1.hour & 0x70) / 16) * 10)) | (date_time1.hour & 0x80);
|
||||||
|
date_time1.day = bcdToBinary(date_time1.day);
|
||||||
|
date_time1.month = bcdToBinary(date_time1.month);
|
||||||
|
date_time1.year = bcdToBinary(date_time1.year);
|
||||||
|
if (CENTURY_REGISTER) {
|
||||||
|
date_time1.century = bcdToBinary(date_time1.century);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to add on the century to the year
|
||||||
|
if (CENTURY_REGISTER) {
|
||||||
|
date_time1.year += date_time1.century * 100;
|
||||||
|
} else {
|
||||||
|
date_time1.year += CURRENT_CENTURY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to 24hr time
|
||||||
|
if (is12Hr(date_time1)) {
|
||||||
|
date_time1.hour = ((date_time1.hour & 0x7F) + 12) % 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
return date_time1;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The interrupt handler for the RTC.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN ctx: *arch.InterruptContext - Pointer to the interrupt context containing the contents
|
||||||
|
/// of the register at the time of the interrupt.
|
||||||
|
///
|
||||||
|
fn rtcHandler(ctx: *arch.InterruptContext) void {
|
||||||
|
ticks +%= 1;
|
||||||
|
|
||||||
|
// Need to read status register C
|
||||||
|
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the rate at which the interrupts will fire. Ranges from 0x3 to 0xF. Where the frequency
|
||||||
|
/// is determined by: frequency = 32768 >> (rate-1); This will assume the interrupts are disabled.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN rate: u4 - The rate value to set the frequency to.
|
||||||
|
///
|
||||||
|
/// Error: RtcError
|
||||||
|
/// RtcError.RateError - If the rate is less than 3.
|
||||||
|
///
|
||||||
|
fn setRate(rate: u8) RtcError!void {
|
||||||
|
if (rate < 3 or rate > 0xF) {
|
||||||
|
return RtcError.RateError;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to disable the NMI for this process
|
||||||
|
const status_a = cmos.readStatusRegister(cmos.StatusRegister.A, true);
|
||||||
|
cmos.writeStatusRegister(cmos.StatusRegister.A, (status_a & 0xF0) | rate, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Enable interrupts for the RTC. This will assume the interrupts have been disabled before hand.
|
||||||
|
///
|
||||||
|
fn enableInterrupts() void {
|
||||||
|
// Need to disable the NMI for this process
|
||||||
|
const status_b = cmos.readStatusRegister(cmos.StatusRegister.B, true);
|
||||||
|
|
||||||
|
// Set the 7th bit to enable interrupt
|
||||||
|
cmos.writeStatusRegister(cmos.StatusRegister.B, status_b | 0x40, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialise the RTC.
|
||||||
|
///
|
||||||
|
pub fn init() void {
|
||||||
|
log.logInfo("Init rtc\n", .{});
|
||||||
|
|
||||||
|
// Register the interrupt handler
|
||||||
|
irq.registerIrq(pic.IRQ_REAL_TIME_CLOCK, rtcHandler) catch |err| switch (err) {
|
||||||
|
error.IrqExists => {
|
||||||
|
panic(@errorReturnTrace(), "IRQ for RTC, IRQ number: {} exists", .{pic.IRQ_REAL_TIME_CLOCK});
|
||||||
|
},
|
||||||
|
error.InvalidIrq => {
|
||||||
|
panic(@errorReturnTrace(), "IRQ for RTC, IRQ number: {} is invalid", .{pic.IRQ_REAL_TIME_CLOCK});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Need to disable interrupts went setting up the RTC
|
||||||
|
arch.disableInterrupts();
|
||||||
|
|
||||||
|
// Set the interrupt rate to 512Hz
|
||||||
|
setRate(7) catch |err| switch (err) {
|
||||||
|
error.RateError => {
|
||||||
|
panic(@errorReturnTrace(), "Setting rate error", .{});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enable RTC interrupts
|
||||||
|
enableInterrupts();
|
||||||
|
|
||||||
|
// Read status register C to clear any interrupts that may have happened during set up
|
||||||
|
//const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
||||||
|
|
||||||
|
// Can now enable interrupts
|
||||||
|
arch.enableInterrupts();
|
||||||
|
|
||||||
|
const reg_c = cmos.readStatusRegister(cmos.StatusRegister.C, false);
|
||||||
|
|
||||||
|
log.logInfo("Done\n", .{});
|
||||||
|
|
||||||
|
if (build_options.rt_test) runtimeTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
test "isBuzzy not buzzy" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x60) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(!isBuzzy());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "isBuzzy buzzy" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x80) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(isBuzzy());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "calcDayOfWeek" {
|
||||||
|
var date_time = DateTime{
|
||||||
|
.second = 0,
|
||||||
|
.minute = 0,
|
||||||
|
.hour = 0,
|
||||||
|
.day = 10,
|
||||||
|
.month = 1,
|
||||||
|
.year = 2020,
|
||||||
|
.century = 0,
|
||||||
|
.day_of_week = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = calcDayOfWeek(date_time);
|
||||||
|
var expected = @as(u32, 5);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
|
||||||
|
date_time.day = 20;
|
||||||
|
date_time.month = 7;
|
||||||
|
date_time.year = 1940;
|
||||||
|
|
||||||
|
actual = calcDayOfWeek(date_time);
|
||||||
|
expected = @as(u32, 6);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
|
||||||
|
date_time.day = 9;
|
||||||
|
date_time.month = 11;
|
||||||
|
date_time.year = 2043;
|
||||||
|
|
||||||
|
actual = calcDayOfWeek(date_time);
|
||||||
|
expected = @as(u32, 1);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
|
||||||
|
date_time.day = 1;
|
||||||
|
date_time.month = 1;
|
||||||
|
date_time.year = 2000;
|
||||||
|
|
||||||
|
actual = calcDayOfWeek(date_time);
|
||||||
|
expected = @as(u32, 6);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "isBcd not BCD" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(!isBcd());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "isBcd BCD" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x04) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(isBcd());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "is12Hr not 12Hr" {
|
||||||
|
const date_time = DateTime{
|
||||||
|
.second = 0,
|
||||||
|
.minute = 0,
|
||||||
|
.hour = 0,
|
||||||
|
.day = 0,
|
||||||
|
.month = 0,
|
||||||
|
.year = 0,
|
||||||
|
.century = 0,
|
||||||
|
.day_of_week = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(!is12Hr(date_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "is12Hr 12Hr" {
|
||||||
|
const date_time = DateTime{
|
||||||
|
.second = 0,
|
||||||
|
.minute = 0,
|
||||||
|
.hour = 0x80,
|
||||||
|
.day = 0,
|
||||||
|
.month = 0,
|
||||||
|
.year = 0,
|
||||||
|
.century = 0,
|
||||||
|
.day_of_week = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x02) },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(is12Hr(date_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "bcdToBinary" {
|
||||||
|
var expected = @as(u32, 59);
|
||||||
|
var actual = bcdToBinary(0x59);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
|
||||||
|
expected = @as(u32, 48);
|
||||||
|
actual = bcdToBinary(0x48);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
|
||||||
|
expected = @as(u32, 1);
|
||||||
|
actual = bcdToBinary(0x01);
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readRtcRegisters" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
// Have 2 buzzy loops
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x80), cmos.StatusRegister.A, false, @as(u8, 0x80), cmos.StatusRegister.A, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// All the RTC registers without century as it isn't supported yet
|
||||||
|
cmos.addTestParams("readRtcRegister", .{
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 3),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
});
|
||||||
|
|
||||||
|
const expected = DateTime{
|
||||||
|
.second = 1,
|
||||||
|
.minute = 2,
|
||||||
|
.hour = 3,
|
||||||
|
.day = 10,
|
||||||
|
.month = 1,
|
||||||
|
.year = 20,
|
||||||
|
.century = 2000,
|
||||||
|
.day_of_week = 5,
|
||||||
|
};
|
||||||
|
const actual = readRtcRegisters();
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readRtc unstable read" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
// No buzzy loop
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reading the RTC registers twice, second time is one second ahead
|
||||||
|
cmos.addTestParams("readRtcRegister", .{
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 3),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 3),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Will try again, and now stable
|
||||||
|
// No buzzy loop
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
cmos.addTestParams("readRtcRegister", .{
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 3),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 3),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Not BCD
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Not 12hr
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expected = DateTime{
|
||||||
|
.second = 2,
|
||||||
|
.minute = 2,
|
||||||
|
.hour = 3,
|
||||||
|
.day = 10,
|
||||||
|
.month = 1,
|
||||||
|
.year = 2020,
|
||||||
|
.century = 2000,
|
||||||
|
.day_of_week = 5,
|
||||||
|
};
|
||||||
|
const actual = readRtc();
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readRtc is BCD" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
// No buzzy loop
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reading the RTC registers once
|
||||||
|
cmos.addTestParams("readRtcRegister", .{
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 0x30),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 0x59),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 0x11),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 0x10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 0x1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 0x20),
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 0x30),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 0x59),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 0x11),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 0x10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 0x1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 0x20),
|
||||||
|
});
|
||||||
|
|
||||||
|
// BCD
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x04) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Not 12hr
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expected = DateTime{
|
||||||
|
.second = 30,
|
||||||
|
.minute = 59,
|
||||||
|
.hour = 11,
|
||||||
|
.day = 10,
|
||||||
|
.month = 1,
|
||||||
|
.year = 2020,
|
||||||
|
.century = 2000,
|
||||||
|
.day_of_week = 5,
|
||||||
|
};
|
||||||
|
const actual = readRtc();
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "readRtc is 12 hours" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
// No buzzy loop
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.A, false, @as(u8, 0x00), cmos.StatusRegister.A, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reading the RTC registers once
|
||||||
|
cmos.addTestParams("readRtcRegister", .{
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 0x83),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
cmos.RtcRegister.SECOND, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.MINUTE, @as(u8, 2),
|
||||||
|
cmos.RtcRegister.HOUR, @as(u8, 0x83),
|
||||||
|
cmos.RtcRegister.DAY, @as(u8, 10),
|
||||||
|
cmos.RtcRegister.MONTH, @as(u8, 1),
|
||||||
|
cmos.RtcRegister.YEAR, @as(u8, 20),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Not BCD
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x00) },
|
||||||
|
);
|
||||||
|
|
||||||
|
// 12hr
|
||||||
|
cmos.addTestParams(
|
||||||
|
"readStatusRegister",
|
||||||
|
.{ cmos.StatusRegister.B, false, @as(u8, 0x02) },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expected = DateTime{
|
||||||
|
.second = 1,
|
||||||
|
.minute = 2,
|
||||||
|
.hour = 15,
|
||||||
|
.day = 10,
|
||||||
|
.month = 1,
|
||||||
|
.year = 2020,
|
||||||
|
.century = 2000,
|
||||||
|
.day_of_week = 5,
|
||||||
|
};
|
||||||
|
const actual = readRtc();
|
||||||
|
|
||||||
|
expectEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "setRate below 3" {
|
||||||
|
expectError(RtcError.RateError, setRate(0));
|
||||||
|
expectError(RtcError.RateError, setRate(1));
|
||||||
|
expectError(RtcError.RateError, setRate(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "setRate" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams("readStatusRegister", .{ cmos.StatusRegister.A, true, @as(u8, 0x10) });
|
||||||
|
cmos.addTestParams("writeStatusRegister", .{ cmos.StatusRegister.A, @as(u8, 0x17), true });
|
||||||
|
|
||||||
|
const rate = @as(u8, 7);
|
||||||
|
try setRate(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "enableInterrupts" {
|
||||||
|
cmos.initTest();
|
||||||
|
defer cmos.freeTest();
|
||||||
|
|
||||||
|
cmos.addTestParams("readStatusRegister", .{ cmos.StatusRegister.B, true, @as(u8, 0x20) });
|
||||||
|
cmos.addTestParams("writeStatusRegister", .{ cmos.StatusRegister.B, @as(u8, 0x60), true });
|
||||||
|
|
||||||
|
enableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check that the IRQ is registered correctly
|
||||||
|
///
|
||||||
|
fn rt_init() void {
|
||||||
|
var irq_exists = false;
|
||||||
|
irq.registerIrq(pic.IRQ_REAL_TIME_CLOCK, rtcHandler) catch |err| switch (err) {
|
||||||
|
error.IrqExists => {
|
||||||
|
// We should get this error
|
||||||
|
irq_exists = true;
|
||||||
|
},
|
||||||
|
error.InvalidIrq => {
|
||||||
|
panic(@errorReturnTrace(), "IRQ for RTC, IRQ number: {} is invalid", .{pic.IRQ_REAL_TIME_CLOCK});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!irq_exists) {
|
||||||
|
panic(@errorReturnTrace(), "IRQ for RTC doesn't exists\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the rate
|
||||||
|
const status_a = cmos.readStatusRegister(cmos.StatusRegister.A, false);
|
||||||
|
if (status_a & @as(u8, 0x0F) != 7) {
|
||||||
|
panic(@errorReturnTrace(), "Rate not set properly, got: {}\n", .{status_a & @as(u8, 0x0F)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if interrupts are enabled
|
||||||
|
const status_b = cmos.readStatusRegister(cmos.StatusRegister.B, true);
|
||||||
|
if (status_b & ~@as(u8, 0x40) == 0) {
|
||||||
|
panic(@errorReturnTrace(), "Interrupts not enabled\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("RTC: Tested init\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Check if the interrupt handler is called after a sleep so check that the RTC interrupts fire.
|
||||||
|
///
|
||||||
|
fn rt_interrupts() void {
|
||||||
|
const prev_ticks = ticks;
|
||||||
|
|
||||||
|
pit.waitTicks(100);
|
||||||
|
|
||||||
|
if (prev_ticks == ticks) {
|
||||||
|
panic(@errorReturnTrace(), "No interrupt happened\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
log.logInfo("RTC: Tested interrupts\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all the runtime tests.
|
||||||
|
///
|
||||||
|
fn runtimeTests() void {
|
||||||
|
rt_init();
|
||||||
|
rt_interrupts();
|
||||||
|
}
|
|
@ -10,12 +10,14 @@ def get_test_cases(TestCase):
|
||||||
TestCase("ISR tests", [r"ISR: Tested registered handlers", r"ISR: Tested opened IDT entries"]),
|
TestCase("ISR tests", [r"ISR: Tested registered handlers", r"ISR: Tested opened IDT entries"]),
|
||||||
TestCase("IRQ init", [r"Init irq", r"Done"]),
|
TestCase("IRQ init", [r"Init irq", r"Done"]),
|
||||||
TestCase("IRQ tests", [r"IRQ: Tested registered handlers", r"IRQ: Tested opened IDT entries"]),
|
TestCase("IRQ tests", [r"IRQ: Tested registered handlers", r"IRQ: Tested opened IDT entries"]),
|
||||||
|
TestCase("Paging init", [r"Init paging", r"Done"]),
|
||||||
|
TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]),
|
||||||
TestCase("PIT init", [r"Init pit"]),
|
TestCase("PIT init", [r"Init pit"]),
|
||||||
TestCase("PIT init", [r".+"], r"\[DEBUG\] "),
|
TestCase("PIT init", [r".+"], r"\[DEBUG\] "),
|
||||||
TestCase("PIT init", [r"Done"]),
|
TestCase("PIT init", [r"Done"]),
|
||||||
TestCase("PIT tests", [r"PIT: Tested init", r"PIT: Tested wait ticks", r"PIT: Tested wait ticks 2"]),
|
TestCase("PIT tests", [r"PIT: Tested init", r"PIT: Tested wait ticks", r"PIT: Tested wait ticks 2"]),
|
||||||
TestCase("Paging init", [r"Init paging", r"Done"]),
|
TestCase("RTC init", [r"Init rtc", r"Done"]),
|
||||||
TestCase("Paging tests", [r"Paging: Tested accessing unmapped memory", r"Paging: Tested accessing mapped memory"]),
|
TestCase("RTC tests", [r"RTC: Tested init", r"RTC: Tested interrupts"]),
|
||||||
TestCase("Syscalls init", [r"Init syscalls", r"Done"]),
|
TestCase("Syscalls init", [r"Init syscalls", r"Done"]),
|
||||||
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
|
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
|
||||||
]
|
]
|
||||||
|
|
|
@ -101,3 +101,5 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
|
||||||
pub fn mock_disableInterrupts() void {}
|
pub fn mock_disableInterrupts() void {}
|
||||||
|
|
||||||
pub fn mock_enableInterrupts() void {}
|
pub fn mock_enableInterrupts() void {}
|
||||||
|
|
||||||
|
pub fn mock_ioWait() void {}
|
||||||
|
|
34
test/mock/kernel/cmos_mock.zig
Normal file
34
test/mock/kernel/cmos_mock.zig
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const mock_framework = @import("mock_framework.zig");
|
||||||
|
pub const initTest = mock_framework.initTest;
|
||||||
|
pub const freeTest = mock_framework.freeTest;
|
||||||
|
pub const addTestParams = mock_framework.addTestParams;
|
||||||
|
pub const addConsumeFunction = mock_framework.addConsumeFunction;
|
||||||
|
pub const addRepeatFunction = mock_framework.addRepeatFunction;
|
||||||
|
|
||||||
|
pub const StatusRegister = enum {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RtcRegister = enum {
|
||||||
|
SECOND,
|
||||||
|
MINUTE,
|
||||||
|
HOUR,
|
||||||
|
DAY,
|
||||||
|
MONTH,
|
||||||
|
YEAR,
|
||||||
|
CENTURY,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn readRtcRegister(reg: RtcRegister) u8 {
|
||||||
|
return mock_framework.performAction("readRtcRegister", u8, .{reg});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn readStatusRegister(reg: StatusRegister, comptime disable_nmi: bool) u8 {
|
||||||
|
return mock_framework.performAction("readStatusRegister", u8, .{ reg, disable_nmi });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeStatusRegister(reg: StatusRegister, data: u8, comptime disable_nmi: bool) void {
|
||||||
|
return mock_framework.performAction("writeStatusRegister", void, .{ reg, data, disable_nmi });
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ const TailQueue = std.TailQueue;
|
||||||
const warn = std.debug.warn;
|
const warn = std.debug.warn;
|
||||||
const gdt = @import("gdt_mock.zig");
|
const gdt = @import("gdt_mock.zig");
|
||||||
const idt = @import("idt_mock.zig");
|
const idt = @import("idt_mock.zig");
|
||||||
|
const cmos = @import("cmos_mock.zig");
|
||||||
|
|
||||||
///
|
///
|
||||||
/// The enumeration of types that the mocking framework supports. These include basic types like u8
|
/// The enumeration of types that the mocking framework supports. These include basic types like u8
|
||||||
|
@ -18,6 +19,8 @@ const DataElementType = enum {
|
||||||
U8,
|
U8,
|
||||||
U16,
|
U16,
|
||||||
U32,
|
U32,
|
||||||
|
ECmosStatusRegister,
|
||||||
|
ECmosRtcRegister,
|
||||||
PTR_CONST_GdtPtr,
|
PTR_CONST_GdtPtr,
|
||||||
PTR_CONST_IdtPtr,
|
PTR_CONST_IdtPtr,
|
||||||
ERROR_IDTERROR_VOID,
|
ERROR_IDTERROR_VOID,
|
||||||
|
@ -34,6 +37,9 @@ const DataElementType = enum {
|
||||||
FN_IU8_IU8_OU16,
|
FN_IU8_IU8_OU16,
|
||||||
FN_IU16_IU8_OVOID,
|
FN_IU16_IU8_OVOID,
|
||||||
FN_IU16_IU16_OVOID,
|
FN_IU16_IU16_OVOID,
|
||||||
|
FN_IECmosStatusRegister_IBOOL_OU8,
|
||||||
|
FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
||||||
|
FN_IECmosRtcRegister_OU8,
|
||||||
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
FN_IU8_INFNOVOID_OERRORIDTERRORVOID,
|
||||||
FN_IPTRCONSTGDTPTR_OVOID,
|
FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
|
@ -44,6 +50,9 @@ const DataElementType = enum {
|
||||||
/// A tagged union of all the data elements that the mocking framework can work with. This can be
|
/// A tagged union of all the data elements that the mocking framework can work with. This can be
|
||||||
/// expanded to add new types. This is needed as need a list of data that all have different types,
|
/// expanded to add new types. This is needed as need a list of data that all have different types,
|
||||||
/// so this wraps the data into a union, (which is of one type) so can have a list of them.
|
/// so this wraps the data into a union, (which is of one type) so can have a list of them.
|
||||||
|
/// When https://github.com/ziglang/zig/issues/383 anf https://github.com/ziglang/zig/issues/2907
|
||||||
|
/// is done, can programitaclly create types for this. Can use a compile time block that loops
|
||||||
|
/// through the available basic types and create function types so don't have a long list.
|
||||||
///
|
///
|
||||||
const DataElement = union(DataElementType) {
|
const DataElement = union(DataElementType) {
|
||||||
BOOL: bool,
|
BOOL: bool,
|
||||||
|
@ -51,6 +60,8 @@ const DataElement = union(DataElementType) {
|
||||||
U8: u8,
|
U8: u8,
|
||||||
U16: u16,
|
U16: u16,
|
||||||
U32: u32,
|
U32: u32,
|
||||||
|
ECmosStatusRegister: cmos.StatusRegister,
|
||||||
|
ECmosRtcRegister: cmos.RtcRegister,
|
||||||
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
PTR_CONST_GdtPtr: *const gdt.GdtPtr,
|
||||||
PTR_CONST_IdtPtr: *const idt.IdtPtr,
|
PTR_CONST_IdtPtr: *const idt.IdtPtr,
|
||||||
ERROR_IDTERROR_VOID: idt.IdtError!void,
|
ERROR_IDTERROR_VOID: idt.IdtError!void,
|
||||||
|
@ -67,6 +78,9 @@ const DataElement = union(DataElementType) {
|
||||||
FN_IU8_IU8_OU16: fn (u8, u8) u16,
|
FN_IU8_IU8_OU16: fn (u8, u8) u16,
|
||||||
FN_IU16_IU8_OVOID: fn (u16, u8) void,
|
FN_IU16_IU8_OVOID: fn (u16, u8) void,
|
||||||
FN_IU16_IU16_OVOID: fn (u16, u16) void,
|
FN_IU16_IU16_OVOID: fn (u16, u16) void,
|
||||||
|
FN_IECmosStatusRegister_IBOOL_OU8: fn (cmos.StatusRegister, bool) u8,
|
||||||
|
FN_IECmosStatusRegister_IU8_IBOOL_OVOID: fn (cmos.StatusRegister, u8, bool) void,
|
||||||
|
FN_IECmosRtcRegister_OU8: fn (cmos.RtcRegister) u8,
|
||||||
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, extern fn () void) idt.IdtError!void,
|
FN_IU8_IEFNOVOID_OERRORIDTERRORVOID: fn (u8, extern fn () void) idt.IdtError!void,
|
||||||
FN_IU8_INFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.Naked) void) idt.IdtError!void,
|
FN_IU8_INFNOVOID_OERRORIDTERRORVOID: fn (u8, fn () callconv(.Naked) void) idt.IdtError!void,
|
||||||
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
|
FN_IPTRCONSTGDTPTR_OVOID: fn (*const gdt.GdtPtr) void,
|
||||||
|
@ -151,6 +165,8 @@ fn Mock() type {
|
||||||
u8 => DataElement{ .U8 = arg },
|
u8 => DataElement{ .U8 = arg },
|
||||||
u16 => DataElement{ .U16 = arg },
|
u16 => DataElement{ .U16 = arg },
|
||||||
u32 => DataElement{ .U32 = arg },
|
u32 => DataElement{ .U32 = arg },
|
||||||
|
cmos.StatusRegister => DataElement{ .ECmosStatusRegister = arg },
|
||||||
|
cmos.RtcRegister => DataElement{ .ECmosRtcRegister = arg },
|
||||||
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
*const gdt.GdtPtr => DataElement{ .PTR_CONST_GdtPtr = arg },
|
||||||
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
|
*const idt.IdtPtr => DataElement{ .PTR_CONST_IdtPtr = arg },
|
||||||
idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg },
|
idt.IdtError!void => DataElement{ .ERROR_IDTERROR_VOID = arg },
|
||||||
|
@ -167,6 +183,9 @@ fn Mock() type {
|
||||||
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
|
fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg },
|
||||||
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
|
fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg },
|
||||||
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg },
|
||||||
|
fn (cmos.StatusRegister, bool) u8 => DataElement{ .FN_IECmosStatusRegister_IBOOL_OU8 = arg },
|
||||||
|
fn (cmos.StatusRegister, u8, bool) void => DataElement{ .FN_IECmosStatusRegister_IU8_IBOOL_OVOID = arg },
|
||||||
|
fn (cmos.RtcRegister) u8 => DataElement{ .FN_IECmosRtcRegister_OU8 = arg },
|
||||||
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
fn (*const gdt.GdtPtr) void => DataElement{ .FN_IPTRCONSTGDTPTR_OVOID = arg },
|
||||||
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
fn (*const idt.IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg },
|
||||||
fn (u8, extern fn () void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg },
|
fn (u8, extern fn () void) idt.IdtError!void => DataElement{ .FN_IU8_IEFNOVOID_OERRORIDTERRORVOID = arg },
|
||||||
|
@ -191,6 +210,8 @@ fn Mock() type {
|
||||||
u8 => DataElementType.U8,
|
u8 => DataElementType.U8,
|
||||||
u16 => DataElementType.U16,
|
u16 => DataElementType.U16,
|
||||||
u32 => DataElementType.U32,
|
u32 => DataElementType.U32,
|
||||||
|
cmos.StatusRegister => DataElementType.ECmosStatusRegister,
|
||||||
|
cmos.RtcRegister => DataElementType.ECmosRtcRegister,
|
||||||
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
*const gdt.GdtPtr => DataElement.PTR_CONST_GdtPtr,
|
||||||
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
|
*const idt.IdtPtr => DataElement.PTR_CONST_IdtPtr,
|
||||||
idt.IdtError!void => DataElement.ERROR_IDTERROR_VOID,
|
idt.IdtError!void => DataElement.ERROR_IDTERROR_VOID,
|
||||||
|
@ -206,6 +227,9 @@ fn Mock() type {
|
||||||
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
|
fn (u8, u8) u16 => DataElementType.FN_IU8_IU8_OU16,
|
||||||
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
|
fn (u16, u8) void => DataElementType.FN_IU16_IU8_OVOID,
|
||||||
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => DataElementType.FN_IU16_IU16_OVOID,
|
||||||
|
fn (cmos.StatusRegister, bool) u8 => DataElementType.FN_IECmosStatusRegister_IBOOL_OU8,
|
||||||
|
fn (cmos.StatusRegister, u8, bool) void => DataElementType.FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
||||||
|
fn (cmos.RtcRegister) u8 => DataElementType.FN_IECmosRtcRegister_OU8,
|
||||||
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
|
fn (*const gdt.GdtPtr) void => DataElementType.FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
fn (*const idt.IdtPtr) void => DataElementType.FN_IPTRCONSTIDTPTR_OVOID,
|
fn (*const idt.IdtPtr) void => DataElementType.FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
fn (u8, extern fn () void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, extern fn () void) idt.IdtError!void => DataElementType.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
|
@ -232,6 +256,8 @@ fn Mock() type {
|
||||||
u8 => element.U8,
|
u8 => element.U8,
|
||||||
u16 => element.U16,
|
u16 => element.U16,
|
||||||
u32 => element.U32,
|
u32 => element.U32,
|
||||||
|
cmos.StatusRegister => element.ECmosStatusRegister,
|
||||||
|
cmos.RtcRegister => element.ECmosRtcRegister,
|
||||||
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
*const gdt.GdtPtr => element.PTR_CONST_GdtPtr,
|
||||||
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
|
*const idt.IdtPtr => element.PTR_CONST_IdtPtr,
|
||||||
idt.IdtError!void => element.ERROR_IDTERROR_VOID,
|
idt.IdtError!void => element.ERROR_IDTERROR_VOID,
|
||||||
|
@ -247,6 +273,9 @@ fn Mock() type {
|
||||||
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
|
fn (u8, u8) u16 => element.FN_IU8_IU8_OU16,
|
||||||
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
|
fn (u16, u8) void => element.FN_IU16_IU8_OVOID,
|
||||||
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
fn (u16, u16) void => element.FN_IU16_IU16_OVOID,
|
||||||
|
fn (cmos.StatusRegister, bool) u8 => element.FN_IECmosStatusRegister_IBOOL_OU8,
|
||||||
|
fn (cmos.StatusRegister, u8, bool) void => element.FN_IECmosStatusRegister_IU8_IBOOL_OVOID,
|
||||||
|
fn (cmos.RtcRegister) u8 => element.FN_IECmosRtcRegister_OU8,
|
||||||
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
|
fn (*const gdt.GdtPtr) void => element.FN_IPTRCONSTGDTPTR_OVOID,
|
||||||
fn (*const idt.IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID,
|
fn (*const idt.IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID,
|
||||||
fn (u8, extern fn () void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
fn (u8, extern fn () void) idt.IdtError!void => element.FN_IU8_IEFNOVOID_OERRORIDTERRORVOID,
|
||||||
|
@ -261,7 +290,7 @@ fn Mock() type {
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN RetType: type - The return type of the function.
|
/// IN RetType: type - The return type of the function.
|
||||||
/// IN params: arglist - The argument list for the function.
|
/// IN params: var - The argument list for the function.
|
||||||
///
|
///
|
||||||
/// Return: type
|
/// Return: type
|
||||||
/// A function type that represents the return type and its arguments.
|
/// A function type that represents the return type and its arguments.
|
||||||
|
@ -271,7 +300,7 @@ fn Mock() type {
|
||||||
0 => fn () RetType,
|
0 => fn () RetType,
|
||||||
1 => fn (@TypeOf(params[0])) RetType,
|
1 => fn (@TypeOf(params[0])) RetType,
|
||||||
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
||||||
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
|
else => @compileError("Couldn't generate function type for " ++ len ++ " parameters\n"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +406,7 @@ fn Mock() type {
|
||||||
/// perform a action.
|
/// perform a action.
|
||||||
/// IN fun_name: []const u8 - The function name to act on.
|
/// IN fun_name: []const u8 - The function name to act on.
|
||||||
/// IN RetType: type - The return type of the function being mocked.
|
/// IN RetType: type - The return type of the function being mocked.
|
||||||
/// IN params: arglist - The list of parameters of the mocked function.
|
/// IN params: var - The list of parameters of the mocked function.
|
||||||
///
|
///
|
||||||
/// Return: RetType
|
/// Return: RetType
|
||||||
/// The return value of the mocked function. This can be void.
|
/// The return value of the mocked function. This can be void.
|
||||||
|
@ -418,11 +447,18 @@ fn Mock() type {
|
||||||
// Waiting for this:
|
// Waiting for this:
|
||||||
// error: compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313
|
// error: compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313
|
||||||
// to be resolved
|
// to be resolved
|
||||||
|
|
||||||
|
comptime const param_len = [1]u8{switch (params.len) {
|
||||||
|
0...9 => params.len + @as(u8, '0'),
|
||||||
|
else => unreachable,
|
||||||
|
}};
|
||||||
|
|
||||||
const expected_function = switch (params.len) {
|
const expected_function = switch (params.len) {
|
||||||
0 => fn () RetType,
|
0 => fn () RetType,
|
||||||
1 => fn (@TypeOf(params[0])) RetType,
|
1 => fn (@TypeOf(params[0])) RetType,
|
||||||
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
||||||
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
|
3 => fn (@TypeOf(params[0]), @TypeOf(params[1]), @TypeOf(params[2])) RetType,
|
||||||
|
else => @compileError("Couldn't generate function type for " ++ param_len ++ " parameters\n"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the corresponding DataElementType
|
// Get the corresponding DataElementType
|
||||||
|
@ -438,11 +474,12 @@ fn Mock() type {
|
||||||
action_list.destroyNode(test_node, GlobalAllocator);
|
action_list.destroyNode(test_node, GlobalAllocator);
|
||||||
|
|
||||||
// The data element will contain the function to call
|
// The data element will contain the function to call
|
||||||
var r = switch (params.len) {
|
const r = switch (params.len) {
|
||||||
0 => actual_function(),
|
0 => actual_function(),
|
||||||
1 => actual_function(params[0]),
|
1 => actual_function(params[0]),
|
||||||
2 => actual_function(params[0], params[1]),
|
2 => actual_function(params[0], params[1]),
|
||||||
else => @compileError(params.len ++ " or more parameters not supported"),
|
3 => actual_function(params[0], params[1], params[2]),
|
||||||
|
else => @compileError(param_len ++ " or more parameters not supported"),
|
||||||
};
|
};
|
||||||
|
|
||||||
break :ret r;
|
break :ret r;
|
||||||
|
@ -451,11 +488,18 @@ fn Mock() type {
|
||||||
// Do the same for ActionType.ConsumeFunctionCall but instead of
|
// Do the same for ActionType.ConsumeFunctionCall but instead of
|
||||||
// popping the function, just peak
|
// popping the function, just peak
|
||||||
const test_element = action.data;
|
const test_element = action.data;
|
||||||
|
|
||||||
|
comptime const param_len = [1]u8{switch (params.len) {
|
||||||
|
0...9 => params.len + @as(u8, '0'),
|
||||||
|
else => unreachable,
|
||||||
|
}};
|
||||||
|
|
||||||
const expected_function = switch (params.len) {
|
const expected_function = switch (params.len) {
|
||||||
0 => fn () RetType,
|
0 => fn () RetType,
|
||||||
1 => fn (@TypeOf(params[0])) RetType,
|
1 => fn (@TypeOf(params[0])) RetType,
|
||||||
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
2 => fn (@TypeOf(params[0]), @TypeOf(params[1])) RetType,
|
||||||
else => @compileError("Couldn't generate function type for " ++ params.len ++ "parameters\n"),
|
3 => fn (@TypeOf(params[0]), @TypeOf(params[1]), @TypeOf(params[2])) RetType,
|
||||||
|
else => @compileError("Couldn't generate function type for " ++ param_len ++ " parameters\n"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the corresponding DataElementType
|
// Get the corresponding DataElementType
|
||||||
|
@ -472,7 +516,8 @@ fn Mock() type {
|
||||||
0 => actual_function(),
|
0 => actual_function(),
|
||||||
1 => actual_function(params[0]),
|
1 => actual_function(params[0]),
|
||||||
2 => actual_function(params[0], params[1]),
|
2 => actual_function(params[0], params[1]),
|
||||||
else => @compileError(params.len ++ " or more parameters not supported"),
|
3 => actual_function(params[0], params[1], params[2]),
|
||||||
|
else => @compileError(param_len ++ " or more parameters not supported"),
|
||||||
};
|
};
|
||||||
|
|
||||||
break :ret r;
|
break :ret r;
|
||||||
|
@ -634,7 +679,7 @@ pub fn addRepeatFunction(comptime fun_name: []const u8, function: var) void {
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// IN fun_name: []const u8 - The function name to act on.
|
/// IN fun_name: []const u8 - The function name to act on.
|
||||||
/// IN RetType: type - The return type of the function being mocked.
|
/// IN RetType: type - The return type of the function being mocked.
|
||||||
/// IN params: arglist - The list of parameters of the mocked function.
|
/// IN params: var - The list of parameters of the mocked function.
|
||||||
///
|
///
|
||||||
/// Return: RetType
|
/// Return: RetType
|
||||||
/// The return value of the mocked function. This can be void.
|
/// The return value of the mocked function. This can be void.
|
||||||
|
|
Loading…
Reference in a new issue