125 lines
3.4 KiB
C
125 lines
3.4 KiB
C
#include "pit.h"
|
|
#include "../cpu/isr.h"
|
|
#include "port.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");
|
|
}
|
|
}
|