dosbox-staging/include/timer.h
2023-06-14 23:54:51 +10:00

150 lines
4.9 KiB
C++

/*
* Copyright (C) 2002-2021 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DOSBOX_TIMER_H
#define DOSBOX_TIMER_H
#include "bit_view.h"
#include <cassert>
#include <chrono>
#include <limits>
#include <thread>
/* underlying clock rate in HZ */
constexpr int PIT_TICK_RATE = 1193182;
// Short-hand unit conversions
constexpr auto PIT_TICK_RATE_KHZ = static_cast<double>(PIT_TICK_RATE) / 1000.0;
// The rate at which a repeating event occurs is the frequency, measured in Hertz.
// The inverse of frequency is the time between events, called 'period'.
// In this case, we want the period of every 1000 PIT tick events.
constexpr auto period_of_1k_pit_ticks = 1000.0 / static_cast<double>(PIT_TICK_RATE);
constexpr auto period_of_1k_pit_ticks_f = static_cast<float>(period_of_1k_pit_ticks);
/* PIT operating modes represented in 3 bits:
1 to 3 Operating mode :
0 0 0 = Mode 0 (interrupt on terminal count)
0 0 1 = Mode 1 (hardware re-triggerable one-shot)
0 1 0 = Mode 2 (rate generator)
0 1 1 = Mode 3 (square wave generator)
1 0 0 = Mode 4 (software triggered strobe)
1 0 1 = Mode 5 (hardware triggered strobe)
1 1 0 = Mode 2 (rate generator, same as 010b)
1 1 1 = Mode 3 (square wave generator, same as 011b)
Refs: http://www.osdever.net/bkerndev/Docs/pit.htm
https://wiki.osdev.org/Programmable_Interval_Timer#Operating_Modes
*/
enum class PitMode : uint8_t {
InterruptOnTerminalCount = 0b0'0'0,
OneShot = 0b0'0'1,
RateGenerator = 0b0'1'0,
SquareWave = 0b0'1'1,
SoftwareStrobe = 0b1'0'0,
HardwareStrobe = 0b1'0'1,
RateGeneratorAlias = 0b1'1'0,
SquareWaveAlias = 0b1'1'1,
Inactive,
};
/* PPI Port B Control Register
Bit System Description
~~~ ~~~~~~ ~~~~~~~~~~~
0 XT & PC Timer 2 gate to speaker output (read+write)
1 XT & PC Speaker data state (read+write)
4 XT & PC Toggles with each read
5 XT-only Toggles with each read
PC-only Mirrors timer 2 gate to speaker output
7 XT-only Clear keyboard buffer
*/
union PpiPortB {
uint8_t data = {0};
bit_view<0, 1> timer2_gating;
bit_view<1, 1> speaker_output;
bit_view<4, 1> read_toggle;
bit_view<5, 1> xt_read_toggle;
bit_view<5, 1> timer2_gating_alias;
bit_view<7, 1> xt_clear_keyboard;
// virtual view of the timer and speaker output fields
bit_view<0, 2> timer2_gating_and_speaker_out;
};
const char *pit_mode_to_string(const PitMode mode);
/* PC Speakers functions, tightly related to the timer functions */
void PCSPEAKER_SetCounter(const int count, const PitMode pit_mode);
void PCSPEAKER_SetType(const PpiPortB &port_b);
void PCSPEAKER_SetPITControl(const PitMode pit_mode);
typedef void (*TIMER_TickHandler)(void);
/* Register a function that gets called every time if 1 or more ticks pass */
void TIMER_AddTickHandler(TIMER_TickHandler handler);
void TIMER_DelTickHandler(TIMER_TickHandler handler);
/* This will add 1 milliscond to all timers */
void TIMER_AddTick(void);
extern const std::chrono::steady_clock::time_point system_start_time;
static inline int64_t GetTicks()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - system_start_time)
.count();
}
static inline int64_t GetTicksUs()
{
return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - system_start_time)
.count();
}
static inline int64_t GetTicksDiff(const int64_t new_ticks, const int64_t old_ticks)
{
assert(new_ticks >= old_ticks);
return new_ticks - old_ticks;
}
static inline int64_t GetTicksSince(const int64_t old_ticks)
{
const auto now = GetTicks();
return GetTicksDiff(now, old_ticks);
}
static inline int64_t GetTicksUsSince(const int64_t old_ticks)
{
const auto now = GetTicksUs();
return GetTicksDiff(now, old_ticks);
}
static inline void Delay(const int64_t milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
static inline void DelayUs(const int64_t microseconds)
{
std::this_thread::sleep_for(std::chrono::microseconds(microseconds));
}
#endif