130 lines
3.4 KiB
C

#include "pit.h"
#include "port.h"
#include "../cpu/isr.h"
enum {
PIT_CRYSTAL_HZ = 1193182,
CLOCK_PRECISION_HZ = 100,
PIT_PROGRAM_REG = PIT_CRYSTAL_HZ / CLOCK_PRECISION_HZ,
PIT_SELECT_CHANNEL0 = 0x0,
PIT_SELECT_CHANNEL1 = 0x1,
PIT_SELECT_CHANNEL2 = 0x2,
PIT_SELECT_CHANNEL_RB = 0x3,
PIT_ACCESS_MODE_LATCH_COUNT_VALUE_COMMAND = 0x0,
PIT_ACCESS_MODE_LOBYTE_ONLY = 0x1,
PIT_ACCESS_MODE_HIBYTE_ONLY = 0x2,
PIT_ACCESS_MODE_LOHIBYTE = 0x3,
PIT_MODE_INTERRUPT_ON_TERMINAL_COUNT = 0x0,
PIT_MODE_HW_ONESHOT = 0x1,
PIT_MODE_RATE_GENERATOR = 0x2,
PIT_MODES_SQUARE_WAVE_GENERATOR = 0x3,
PIT_MODES_SW_TRIGGERRED_STROBE = 0x4,
PIT_MODES_HW_TRIGGERRED_STROBE = 0x5,
PIT_COMMAND_PORT = 0x43,
PIT_CHANNEL0_DATA_PORT = 0x40,
PIT_CHANNEL2_DATA_PORT = 0x42,
PC_SPEAKER_PORT = 0x61,
};
// Wtf is that O_o
static timer_callback callbacks[100];
static int registered_callbacks = 0;
static volatile uint32_t uptime_ms = 0;
static volatile uint32_t speaker_frequency_hz = 0;
void add_timer_callback(timer_callback tc) {
callbacks[registered_callbacks++] = tc;
}
static void timer_interrupt_handler(registers_t *r) {
for (int i = 0; i < registered_callbacks; ++i) {
callbacks[i]();
}
}
struct pit_command_t {
unsigned char bcd : 1;
unsigned char operating_mode : 3;
unsigned char access_mode : 2;
unsigned char select_channel : 2;
} __attribute__((packed));
static void timer_int_callback(void);
void init_pit() {
struct pit_command_t cmd = {
.select_channel = PIT_SELECT_CHANNEL0,
.access_mode = PIT_ACCESS_MODE_LOHIBYTE,
.operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR,
.bcd = 0,
};
port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd));
port_byte_out(PIT_CHANNEL0_DATA_PORT, PIT_PROGRAM_REG & 0xff);
port_byte_out(PIT_CHANNEL0_DATA_PORT, (PIT_PROGRAM_REG & 0xff00) >> 8);
register_interrupt_handler(IRQ0, timer_interrupt_handler);
add_timer_callback(timer_int_callback);
}
static volatile int sleep_counter = 0;
static void timer_int_callback(void) {
uptime_ms += 1000 / CLOCK_PRECISION_HZ;
sleep_counter--;
}
uint32_t get_uptime_ms(void) {
return uptime_ms;
}
void set_beep_frequency_hz(uint32_t frequency_hz) {
if (frequency_hz == 0) {
disable_beep();
return;
}
uint32_t divisor = PIT_CRYSTAL_HZ / frequency_hz;
if (divisor == 0) {
divisor = 1;
}
if (divisor > 0xffff) {
divisor = 0xffff;
}
struct pit_command_t cmd = {
.select_channel = PIT_SELECT_CHANNEL2,
.access_mode = PIT_ACCESS_MODE_LOHIBYTE,
.operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR,
.bcd = 0,
};
port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd));
port_byte_out(PIT_CHANNEL2_DATA_PORT, divisor & 0xff);
port_byte_out(PIT_CHANNEL2_DATA_PORT, (divisor & 0xff00) >> 8);
uint8_t speaker = port_byte_in(PC_SPEAKER_PORT);
port_byte_out(PC_SPEAKER_PORT, speaker | 0x03);
speaker_frequency_hz = frequency_hz;
}
void disable_beep(void) {
uint8_t speaker = port_byte_in(PC_SPEAKER_PORT);
port_byte_out(PC_SPEAKER_PORT, speaker & 0xfc);
speaker_frequency_hz = 0;
}
uint32_t get_beep_frequency_hz(void) {
return speaker_frequency_hz;
}
void msleep(int ms) {
sleep_counter = ms / 10;
while (sleep_counter > 0) {
asm volatile("hlt");
}
}