dosgame1/system/dos/Timer.cpp

107 lines
2.2 KiB
C++

#include <go32.h>
#include "../Timer.h"
#include "../Pic.h"
#include "../../util/Asm.h"
#include "../../util/Log.h"
#define PIT_FREQ 1193181
#define PIT_MODE_CH0 0x00
#define PIT_MODE_CH1 0x40
#define PIT_MODE_CH2 0x80
#define PIT_MODE_ACCESS_LO 0x10
#define PIT_MODE_ACCESS_HI 0x20
#define PIT_MODE_ACCESS_LOHI 0x30
#define PIT_MODE_M0 0x00
#define PIT_MODE_M1 0x02
#define PIT_MODE_M2 0x04
#define PIT_MODE_M3 0x06
#define PIT_MODE_M4 0x08
#define PIT_MODE_M5 0x0a
#define PIT_MODE_BIN 0x00
#define PIT_MODE_BCD 0x01
#define PIT_REG_CH0 0x40
#define PIT_REG_CH1 0x41
#define PIT_REG_CH2 0x42
#define PIT_REG_MODE 0x43
void timerISR() {
Timer::instance().update();
}
Timer::Timer() {
_go32_dpmi_get_protected_mode_interrupt_vector(8, &_oldIsr);
_newIsr.pm_offset = reinterpret_cast<int>(timerISR);
_newIsr.pm_selector = _go32_my_cs();
_go32_dpmi_allocate_iret_wrapper(&_newIsr);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &_newIsr);
}
Timer::~Timer() {
setDivider(65535);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &_oldIsr);
_go32_dpmi_free_iret_wrapper(&_newIsr);
}
void Timer::update() {
_ticks++;
if (_callback) {
DefaultLog.log("Timer tick with callback\n");
_callback();
} else {
DefaultLog.log("Timer tick without callback\n");
}
_elapsed += _div;
if (_elapsed >= 65535) {
_elapsed -= 65535;
// TODO call old isr
// Documentation is kind of nonexistant so we will not call the handler for now
Pic::clearInterrupt();
} else {
Pic::clearInterrupt();
}
}
uint32_t Timer::getTicks() const {
return _ticks;
}
uint16_t Timer::getFrequency() const {
return _freq;
}
Timer &Timer::instance() {
static Timer inst;
return inst;
}
void Timer::setFrequency(uint16_t freq) {
_freq = freq;
setDivider(PIT_FREQ / freq);
}
void Timer::setDivider(uint16_t div) {
auto wasEnabled = interruptsEnabled();
disableInterrupts();
_div = div;
// Update PIT frequency
outb(PIT_REG_MODE, PIT_MODE_CH0|PIT_MODE_ACCESS_LOHI|PIT_MODE_M2|PIT_MODE_BIN);
outb(PIT_REG_CH0, _div & 0xff);
outb(PIT_REG_CH0, (_div >> 8) & 0xff);
if (wasEnabled) enableInterrupts();
}
Timer::Callback Timer::setCallback(Timer::Callback callback) {
auto oldCallback = _callback;
_callback = callback;
return oldCallback;
}