13using namespace MaxOS::common;
14using namespace MaxOS::hardwarecommunication;
25 m_ticks_between_events(time_between_events)
29 s_active_clock =
this;
32 uint8_t status = read_hardware_clock(0xB);
35 m_24_hour_clock = status & 0x02;
36 m_binary = status & 0x04;
39Clock::~Clock() =
default;
45void Clock::handle_interrupt() {
49 m_ticks_until_next_event--;
52 if (m_ticks_until_next_event != 0)
60 m_ticks_until_next_event = m_ticks_between_events;
70uint8_t Clock::read_hardware_clock(uint8_t address) {
72 m_command_port.write(address);
73 return m_data_port.read();
82uint8_t Clock::binary_representation(uint8_t number)
const {
89 return ((number / 16) * 10) + (number & 0x0f);
108 uint64_t ticks_until_delay_is_over = m_ticks + rounded_milliseconds;
109 while (m_ticks < ticks_until_delay_is_over)
125 m_pit_ticks_per_ms = pit.ticks_per_ms();
130 Logger::DEBUG() <<
"Clock: Calibrated to " << ms_per_tick <<
"ms per kernel tick\n";
140 uint32_t lvt = 0x20 | (1 << 17);
141 local_apic->
write(0x320, lvt);
148 local_apic->
write(0x320, lvt);
159 while (read_hardware_clock(0xA) & 0x80)
164 time.year = binary_representation(read_hardware_clock(0x9)) + 2000;
165 time.month = binary_representation(read_hardware_clock(0x8));
166 time.day = binary_representation(read_hardware_clock(0x7));
167 time.hour = binary_representation(read_hardware_clock(0x4));
168 time.minute = binary_representation(read_hardware_clock(0x2));
169 time.second = binary_representation(read_hardware_clock(0x0));
172 if (!m_24_hour_clock && (time.hour & 0x80) != 0)
173 time.hour = ((time.hour & 0x7F) + 12) % 24;
183 return s_active_clock;
193 m_command_port(0x43),
194 m_local_apic(apic->local_apic()),
195 m_io_apic(apic->io_apic())
200PIT::~PIT() =
default;
205void PIT::handle_interrupt() {
214uint32_t PIT::ticks_per_ms() {
229 .
bcd_mode = (uint8_t) BCDMode::BINARY,
230 .operating_mode = (uint8_t) OperatingMode::RATE_GENERATOR,
231 .access_mode = (uint8_t) AccessMode::LOW_HIGH_BYTE,
232 .channel = (uint8_t) Channel::INTERRUPT
234 m_command_port.write(*(uint8_t *) &command);
237 uint16_t rate = 1193182 / 1000;
238 m_data_port.write(rate & 0xFF);
239 m_data_port.write(rate >> 8);
242 m_local_apic->
write(0x380, 0x00);
245 m_local_apic->
write(0x3E0, 0x0);
251 auto max = (uint32_t) -1;
252 m_local_apic->
write(0x380, max);
254 while (m_ticks < TICK_CALIBRATE_LENGTH)
257 uint32_t elapsed = max - (m_local_apic->
read(0x390));
260 Logger::DEBUG() <<
"Ticks per ms: " << (int) ticks_per_ms <<
"\n";
263 m_local_apic->
write(0x380, 0x00);
static LogRecord DEBUG()
Gets the active logger for debug logs.
static LogRecord INFO()
Gets the active logger for info logs.
Handles both the Local APIC (boot core) and IO APIC.
LocalAPIC * local_apic() const
Get the local apic.
Driver for the APIC clock.
common::time_t get_time()
Reads the current time from the APIC clock (in 24hr time)
uint64_t clock_accuracy
How accurate the clock is in microseconds (how often should an interrupt be raised)
Clock(AdvancedProgrammableInterruptController *apic, uint16_t time_between_events=10)
Constructor for the Clock class.
void setup_apic_clock(LocalAPIC *local_apic) const
Sets up the APIC clock to fire interrupts at the desired rate.
void delay(uint32_t milliseconds) const
Delays the program for a specified number of milliseconds (rounded up to the nearest degree of accura...
static Clock * active_clock()
Gets the currently active clock.
void calibrate(uint64_t ms_per_tick=1)
Configures the APIC clock to fire an interrupt at a specified interval in milliseconds.
void set_redirect(interrupt_redirect_t *redirect)
Redirect a system interrupt to a different IRQ.
void set_redirect_mask(uint8_t index, bool mask)
Enables/Disables an interrupt redirect by masking it.
Handles a certain interrupt number.
Handles the local APIC for the current core.
void write(uint32_t reg, uint32_t value) const
write a value to the apic register using the MSR or memory I/O depending on the local apic version
uint32_t read(uint32_t reg) const
read a value from the apic register using the MSR or memory I/O depending on the local apic version
Driver for the Programmable Interval Timer.
PIT(AdvancedProgrammableInterruptController *apic)
Constructor for the PIT class. Registers a handler for interrupt 0x22 and initialises ports.
Defines drivers for the Programmable Interval Timer (PIT) and APIC Clock.
constexpr uint16_t TICK_CALIBRATE_LENGTH
How many loops to spin for calibration.
Defines a Logger class for logging messages with different severity levels to multiple output streams...
Defines how an interrupt input is redirected to a specific processor via the I/O APIC.
uint8_t index
Where in the IO APIC redirection table this redirect should be placed.
uint8_t type
The type of redirect (should be 0 for normal interrupts)
The command byte to send to the PIT, specifies the channel, access mode, operating mode and BCD mode.
uint8_t bcd_mode
0 = binary, 1 = BCD