use core::arch::asm;
use core::mem::size_of;
use core::ptr::{self, null};
use pc_ints::*;
const IRQ_0_HANDLER: &[u8] = &[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x1E, 0x53, 0x50, 0x8C, 0xC8, 0x8E, 0xD8, 0xC6, 0x06, 0x0C, 0x00, 0x01, 0xBB, 0x00, 0x00, 0x83, 0x07, 0x01, 0x83, 0x57, 0x02, 0x00, 0x83, 0x57, 0x04, 0x00, 0x83, 0x57, 0x06, 0x00, 0xA1, 0x08, 0x00, 0x01, 0x06, 0x0A, 0x00, 0x73, 0x09, 0x58, 0x5B, 0x1F, 0x9D, 0xEA, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x20, 0xE6, 0x20, 0x58, 0x5B, 0x1F, 0x9D, 0xCF, ];
static mut TICKS: *const u64 = null();
pub unsafe fn init(frequency: u16) {
let low_level_ticks_per_tick = (0x1234DDu32 / frequency as u32).try_into().expect("frequency >= 19");
let irq_0_handler_segment = int_31h_ax_0100h_rm_alloc((IRQ_0_HANDLER.len().checked_add(15).unwrap() / 16).try_into().unwrap())
.expect("cannot allocate real-mode memory for timer");
let irq_0_handler_segment = irq_0_handler_segment.ax_segment;
assert!(size_of::<usize>() == size_of::<u32>());
let irq_0_handler_addr = ((irq_0_handler_segment as u32) << 4) as usize;
ptr::copy_nonoverlapping(IRQ_0_HANDLER.as_ptr(), irq_0_handler_addr as *mut u8, IRQ_0_HANDLER.len());
ptr::write_unaligned((irq_0_handler_addr + 0x0008) as *mut u16, low_level_ticks_per_tick);
asm! { "cli" }
let bios_handler = int_31h_ax_0200h_get_rm_int(8);
ptr::write_unaligned((irq_0_handler_addr + 0x003A) as *mut u16, bios_handler.dx_offset);
ptr::write_unaligned((irq_0_handler_addr + 0x003A + 2) as *mut u16, bios_handler.cx_segment);
int_31h_ax_0201h_set_rm_int(8, irq_0_handler_segment, 0x000D);
asm! {
"out 0x43, al",
in ("ax") 0x34u16
}
asm! {
"out 0x40, al",
in ("ax") (low_level_ticks_per_tick & 0xFF) as u8 as u16
}
asm! {
"out 0x40, al",
in ("ax") (low_level_ticks_per_tick >> 8) as u8 as u16
}
asm! { "sti", "nop" }
loop {
if ptr::read_volatile((irq_0_handler_addr + 0x000C) as *const u8) != 0 { break; }
}
TICKS = irq_0_handler_addr as *const u64;
}
pub unsafe fn ticks() -> u64 {
asm! { "cli" }
let ticks = *TICKS;
asm! { "sti", "nop" }
ticks
}
pub unsafe fn done() {
asm! { "cli" }
let irq_0_handler_addr = ptr::replace(ptr::addr_of_mut!(TICKS), null()) as usize;
let bios_handler_offset = ptr::read_unaligned((irq_0_handler_addr + 0x003A) as *mut u16);
let bios_handler_segment = ptr::read_unaligned((irq_0_handler_addr + 0x003A + 2) as *mut u16);
int_31h_ax_0201h_set_rm_int(8, bios_handler_segment, bios_handler_offset);
asm! {
"out 0x43, al",
in ("ax") 0x34u16
}
asm! {
"out 0x40, al",
in ("ax") 0u16,
}
asm! {
"out 0x40, al",
in ("ax") 0u16
}
asm! { "sti", "nop" }
}