New upstream version 2.0-0.9
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
geos_one
2025-08-14 09:28:49 +02:00
parent c338ff82fb
commit 17bb5d7efa
634 changed files with 19105 additions and 52303 deletions

View File

@@ -9,7 +9,7 @@
* REMARK
* This is the timer emulation for DOSEMU. It emulates the Programmable
* Interval Timer (PIT), and also handles IRQ0 interrupt events.
* A lot of animation and video game software are dependent on this module
* A lot of animation and video game software are dependant on this module
* for high frequency timer interrupts (IRQ0).
*
* This code will actually generate 18.2 DOS interrupts/second (the code
@@ -33,58 +33,73 @@
#include "emu.h"
#include "port.h"
#include "timers.h"
#include "iodev.h"
#include "int.h"
#include "pic.h"
#include "emudpmi.h"
#include "vtmr.h"
#include "evtimer.h"
#include "timers.h"
#undef DEBUG_PIT
#undef ONE_MINUTE_TEST
/*******************************************************************
* Programmable Interrupt Timer (PIT) chip *
*******************************************************************/
pit_latch_struct pit[PIT_TIMERS]; /* values of 3 PIT counters */
typedef struct {
Bit16u read_state;
Bit16u write_state;
Bit8u mode, outpin;
Bit32u read_latch;
Bit16u write_latch;
Bit32s cntr;
hitimer_u time;
uint32_t q_ticks;
void *evtmr;
int tmr_skip;
} pit_latch_struct;
static pit_latch_struct pit[PIT_TIMERS]; /* values of 3 PIT counters */
hitimer_t pic_sys_time; /* system time set by pic_watch */
static int irq0_cnt;
#define NEVER -1
static hitimer_t pic_itime[33] = /* time to trigger next interrupt */
{NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER,
NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER,
NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER,
NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER,
NEVER};
static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
{
return /*(__int128_t)*/a * b / c;
}
#define TICKS_TO_NS(t) muldiv64(t, NANOSECONDS_PER_SECOND, PIT_TICK_RATE)
#define NS_TO_TICKS(n) muldiv64(n, PIT_TICK_RATE, NANOSECONDS_PER_SECOND)
static u_long timer_div; /* used by timer int code */
static u_long ticks_accum; /* For timer_tick function, 100usec ticks */
static Bit8u port61 = 0x0c;
int is_cli;
/*
* DANG_BEGIN_FUNCTION initialize_timers
*
* description:
* ensure the 0x40 port timer is initially set correctly
*
* DANG_END_FUNCTION
*/
void initialize_timers(void)
{
hitimer_t cur_time;
cur_time = GETtickTIME(0);
pit[0].mode = 3;
pit[0].outpin = 0;
pit[0].cntr = 0x10000;
pit[0].time.td = cur_time;
pit[0].read_latch = -1;
pit[0].write_latch = 0;
pit[0].read_state = 3;
pit[0].write_state = 3;
pit[1].mode = 2;
pit[1].outpin = 0;
pit[1].cntr = 18;
pit[1].time.td = cur_time;
pit[1].read_latch = -1;
pit[1].write_latch = 18;
pit[1].read_state = 3;
pit[1].write_state = 3;
pit[2].mode = 0;
pit[2].outpin = 0;
pit[2].cntr = -1;
pit[2].time.td = cur_time;
pit[2].read_latch = -1;
pit[2].write_latch = 0;
pit[2].read_state = 3;
pit[2].write_state = 3;
ticks_accum = 0;
timer_div = (pit[0].cntr * 10000) / PIT_TICK_RATE;
timer_tick(); /* a starting tick! */
port61 = 0x0c;
}
/*
* DANG_BEGIN_FUNCTION timer_tick
*
@@ -97,7 +112,30 @@ int is_cli;
*/
void timer_tick(void)
{
pic_sys_time = GETtickTIME(0);
#ifdef ONE_MINUTE_TEST
static int dbug_count = 0;
#endif
hitimer_u tp;
u_long time_curr;
static u_long time_old = 0; /* Preserve value for next call */
/* Get system time in ticks */
tp.td = GETtickTIME(0);
#ifdef ONE_MINUTE_TEST
if (++dbug_count > 6000) leavedos(0);
#endif
/* compute the number of 100ticks since we started */
time_curr = (tp.td - pit[0].time.td) / 100;
/* Reset old timer value to 0 if time_curr wrapped around back to 0 */
if (time_curr < time_old) time_old = 0;
/* Compute number of 100ticks ticks since the last time this function ran */
ticks_accum += (time_curr - time_old);
/* Save old value of the timer */
time_old = time_curr;
if (config.cli_timeout && is_cli) {
if (isset_IF()) {
@@ -109,6 +147,9 @@ void timer_tick(void)
}
}
dpmi_timer();
/* test for stuck interrupts, trigger any scheduled interrupts */
pic_watch(&tp);
}
@@ -148,7 +189,7 @@ void do_sound(Bit16u period)
*
* But if you set it too low, then sounds can be cut off - clarence
*/
static const unsigned sound_duration = 30000; /* in milliseconds */
static const unsigned sound_duration = 30000; /* in miliseconds */
switch (port61 & 3) {
case 3: /* speaker on & speaker control through timer channel 2 */
if ((pit[2].mode == 2) || (pit[2].mode == 3)) { /* is this test needed? */
@@ -168,11 +209,11 @@ void do_sound(Bit16u period)
}
}
static int _pit_latch(int latch, uint64_t cur)
static void pit_latch(int latch)
{
int ret = 0;
hitimer_u cur_time;
long ticks=0;
u_long ticks=0;
pit_latch_struct *p = &pit[latch];
/* check for special 'read latch status' mode */
@@ -191,10 +232,10 @@ static int _pit_latch(int latch, uint64_t cur)
if (p->cntr == -1)
p->read_latch |= 0x40;
p->mode &= ~0x80;
return ret; /* let bit 7 on */
return; /* let bit 7 on */
}
cur_time.td = cur;
cur_time.td = GETtickTIME(0);
if ((p->mode & 2)==0) {
/* non-periodical modes 0,1,4,5 - used mainly by games
@@ -210,7 +251,7 @@ static int _pit_latch(int latch, uint64_t cur)
/* should have been initialized to the value in write_latch */
if (p->cntr != -1) {
ticks = NS_TO_TICKS(cur_time.td - p->time.td);
ticks = (cur_time.td - p->time.td);
if (ticks > p->cntr) { /* time has elapsed */
if ((p->mode&0x40)==0)
@@ -231,28 +272,24 @@ static int _pit_latch(int latch, uint64_t cur)
/* mode 6 -- ??? */
/* mode 3 -- square-wave generator, countdown by 2 */
/* mode 7 -- ??? */
pic_sys_time = cur_time.td; /* full counter */
/* NEVER is a special invalid value... skip it if found */
pic_sys_time += (pic_sys_time == NEVER);
if (latch == 0) {
#if 0
/* when current time is greater than irq time, call pic_request
which will then point pic_itime to next interrupt */
if (((p->mode&0x40)==0) && (pic_sys_time > pic_itime[PIC_IRQ0])) {
if (pic_request(PIC_IRQ0)==PIC_REQ_OK)
{ r_printf("PIT: pit_latch, pic_request IRQ0 mode 2/3\n"); }
}
#endif
/* while current time is less than next irq time, ticks decrease;
* ticks can go out of bounds or negative when the interrupt
* is lost or pending */
if (cur > pic_itime[latch]) {
ticks = 1;
ret++; // underflow seen
} else {
ticks = NS_TO_TICKS(pic_itime[latch] - cur) + 1;
if (ticks > p->cntr)
ticks = 0; // should not be here
}
ticks = (pic_itime[PIC_IRQ0] - pic_sys_time) % p->cntr;
} else {
ticks = p->cntr - (cur % p->cntr);
ticks = p->cntr - (pic_sys_time % p->cntr);
}
if ((p->mode & 3)==3) {
@@ -276,57 +313,20 @@ static int _pit_latch(int latch, uint64_t cur)
i_printf("PIT%d: ticks=%lx latch=%x pin=%d\n",latch,ticks,
p->read_latch,(p->outpin!=0));
#endif
return ret;
}
static int do_pit_latch(int latch)
{
int ret;
uint64_t cur_time;
evtimer_block(pit[latch].evtmr);
cur_time = evtimer_gettime(pit[latch].evtmr);
/* if timer is lagging we run it by hands */
if (cur_time > pic_itime[latch] &&
__sync_bool_compare_and_swap(&pit[latch].q_ticks, 0, 1)) {
/* timer thread blocked, we can increment vars w/o sync/atomics */
pit[latch].tmr_skip++;
if (!latch)
vtmr_raise(VTMR_PIT);
else
pit[latch].q_ticks--;
pit[latch].time.td = pic_itime[latch];
pic_itime[latch] += TICKS_TO_NS(pit[latch].cntr);
}
ret = _pit_latch(latch, cur_time);
vtmr_sync(VTMR_PIT);
evtimer_unblock(pit[latch].evtmr);
return ret;
}
static int pit_latch_hndl(void)
{
return do_pit_latch(0);
}
static void pit_latch(int latch)
{
if (latch == 0)
vtmr_latch(VTMR_PIT);
else
do_pit_latch(latch);
}
/* This is called also by port 0x61 - some programs can use timer #2
* as a GP timer and read bit 5 of port 0x61 (e.g. Matrox BIOS)
*/
Bit8u pit_inp(ioport_t port, void *arg)
Bit8u pit_inp(ioport_t port)
{
int ret = 0;
port -= 0x40;
if ((port == 2) && (config.speaker == SPKR_NATIVE))
return std_port_inb(0x42);
if ((port == 2) && (config.speaker == SPKR_NATIVE)) {
error ("pit_inp() - how could we come here if we defined PORT_FAST?\n");
return safe_port_in_byte(0x42);
}
else if (port == 1)
i_printf("PIT: someone is reading the CMOS refresh time?!?");
@@ -360,14 +360,15 @@ Bit8u pit_inp(ioport_t port, void *arg)
return ret;
}
void pit_outp(ioport_t port, Bit8u val, void *arg)
void pit_outp(ioport_t port, Bit8u val)
{
port -= 0x40;
if (port == 1)
i_printf("PORT: someone is writing the CMOS refresh time?!?");
else if (port == 2 && config.speaker == SPKR_NATIVE) {
std_port_outb(0x42, val);
error ("pit_outp() - how could we come here if we defined PORT_FAST?\n");
safe_port_out_byte(0x42, val);
return;
}
@@ -402,28 +403,36 @@ void pit_outp(ioport_t port, Bit8u val, void *arg)
if (pit[port].write_state != 0) {
if (pit[port].write_latch == 0)
pit[port].cntr = 0xffff;
pit[port].cntr = 0x10000;
else
pit[port].cntr = pit[port].write_latch;
if (!port)
evtimer_set_rel(pit[port].evtmr, TICKS_TO_NS(pit[port].cntr), 1);
else
evtimer_stop(pit[port].evtmr);
h_printf("PIT: timer %i set to %i ticks\n", port, pit[port].cntr);
pit[port].time.td = 0;
pic_itime[port] = TICKS_TO_NS(pit[port].cntr);
if (port == 2 && (port61 & 3) == 3)
do_sound(pit[port].cntr);
pit[port].time.td = GETtickTIME(0);
if (port == 0) {
ticks_accum = 0;
timer_div = (pit[0].cntr * 10000) / PIT_TICK_RATE;
if (timer_div == 0)
timer_div = 1;
#if 1
i_printf("timer_interrupt_rate count %i, requested %.3g Hz, granted %.3g Hz\n",
pit[0].cntr, PIT_TICK_RATE/(double)pit[0].cntr, 10000.0/timer_div);
#endif
}
#if 0
else if (port == 2 && config.speaker == SPKR_EMULATED) {
do_sound(pit[2].write_latch & 0xffff);
}
#endif
}
}
Bit8u pit_control_inp(ioport_t port, void *arg)
Bit8u pit_control_inp(ioport_t port)
{
return 0;
}
void pit_control_outp(ioport_t port, Bit8u val, void *arg)
void pit_control_outp(ioport_t port, Bit8u val)
{
int latch = (val >> 6) & 0x03;
@@ -454,7 +463,7 @@ void pit_control_outp(ioport_t port, Bit8u val, void *arg)
switch (latch) {
case 2:
if (config.speaker == SPKR_NATIVE) {
std_port_outb(0x43, val);
safe_port_out_byte(0x43, val);
break;
}
/* nobreak; */
@@ -470,7 +479,7 @@ void pit_control_outp(ioport_t port, Bit8u val, void *arg)
/* set the time base for the counter - safety code for programs
* which use a non-periodical mode without reloading the counter
*/
pit[latch].time.td = evtimer_gettime(pit[latch].evtmr);
pit[latch].time.td = GETtickTIME(0);
}
}
#ifdef DEBUG_PIT
@@ -494,47 +503,32 @@ void pit_control_outp(ioport_t port, Bit8u val, void *arg)
}
}
static void timer_activate(int ticks, void *arg)
/* DANG_BEGIN_FUNCTION timer_int_engine
*
* This is experimental TIMER-IRQ CHAIN code!
* This is a function to determine whether it is time to invoke a
* new timer irq 0 event. Normally it is 18 times a second, but
* many video games set it to 100 times per second or more. Since
* the kernel cannot keep an accurate timer interrupt, the job of this
* routine is to perform a chained timer irq 0 right after the previous
* timer irq 0. This routine should, ideally, be called right after
* the end of a timer irq, if possible.
*
* This would speed up high frequency timer interrupts if this code
* can be converted into an assembly macro equivalent!
*
* PLEASE NOTE
*
* This code has been replaced by interrupt scheduling code in pic.
* The result is that we simply call pic_sched and run the dos interrupt.
* If the new code causes no problems, I'll revise this section permanently.
*
* DANG_END_FUNCTION
*/
static int timer_int_engine(int ilevel)
{
int pit_num = (uintptr_t)arg;
uint32_t q;
if (pit[pit_num].tmr_skip) {
pit[pit_num].tmr_skip--;
return;
}
if (!ticks) {
error("0 ticks on PIT\n");
return;
}
q = __sync_fetch_and_add(&pit[pit_num].q_ticks, ticks);
h_printf("PIT: timer %i expired, %i\n", pit_num, q);
if (pit_num) {
pit[pit_num].time.td = evtimer_gettime(pit[pit_num].evtmr);
return;
}
if (!q) {
vtmr_raise(VTMR_PIT);
pit[0].time.td = pic_itime[0];
pic_itime[0] += TICKS_TO_NS(pit[0].cntr);
}
}
static int timer_irq_ack(int masked)
{
uint32_t q = __sync_sub_and_fetch(&pit[0].q_ticks, 1);
int ret = 0;
h_printf("PIT: timer 0 acknowledged, %i\n", q);
if (q) {
pit[0].time.td = pic_itime[0];
pic_itime[0] += TICKS_TO_NS(pit[0].cntr);
ret = 1;
}
if (!masked)
irq0_cnt++;
return ret;
pic_sched(PIC_IRQ0,pit[0].cntr);
return 1;
}
/* reads/writes to the speaker control port (0x61)
@@ -562,7 +556,7 @@ static int timer_irq_ack(int masked)
Bit8u spkr_io_read(ioport_t port) {
if (port==0x61) {
if (config.speaker == SPKR_NATIVE)
return std_port_inb(0x61);
return port_safe_inb(0x61);
else {
/* keep the connection between port 0x61 and PIT timer#2 */
pit_latch(2);
@@ -578,7 +572,7 @@ void spkr_io_write(ioport_t port, Bit8u value) {
if (port==0x61) {
switch (config.speaker) {
case SPKR_NATIVE:
std_port_outb(0x61, value & 0x03);
port_safe_outb(0x61, value & 0x03);
break;
case SPKR_EMULATED:
@@ -609,11 +603,14 @@ void pit_init(void)
io_device.handler_name = "8254 Timer0";
io_device.start_addr = 0x0040;
io_device.end_addr = 0x0040;
io_device.irq = 0;
io_device.fd = -1;
port_register_handler(io_device, 0);
io_device.handler_name = "8254 Timer1";
io_device.start_addr = 0x0041;
io_device.end_addr = 0x0041;
io_device.irq = EMU_NO_IRQ;
port_register_handler(io_device, 0);
io_device.handler_name = "8254 Timer2";
@@ -636,82 +633,54 @@ void pit_init(void)
port_register_handler(io_device, 0);
#endif
vtmr_register(VTMR_PIT, timer_irq_ack);
vtmr_register_latch(VTMR_PIT, pit_latch_hndl);
vtmr_set_tweaked(VTMR_PIT, config.timer_tweaks, 0);
pit[0].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)0);
pit[1].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)1);
pit[2].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)2);
}
void pit_done(void)
{
evtimer_delete(pit[0].evtmr);
evtimer_delete(pit[1].evtmr);
evtimer_delete(pit[2].evtmr);
pic_seti(PIC_IRQ0, timer_int_engine, 0, NULL); /* do_irq0 in pic.c */
pic_request(PIC_IRQ0); /* start timer */
}
void pit_reset(void)
{
hitimer_t cur_time;
cur_time = GETtickTIME(0);
pit[0].mode = 3;
pit[0].outpin = 0;
pit[0].cntr = 0xffff;
pit[0].time.td = 0;
pit[0].cntr = 0x10000;
pit[0].time.td = cur_time;
pit[0].read_latch = 0xffffffff;
pit[0].write_latch = 0;
pit[0].read_state = 3;
pit[0].write_state = 3;
pit[0].q_ticks = 0;
evtimer_stop(pit[0].evtmr);
pit[1].mode = 2;
pit[1].outpin = 0;
pit[1].cntr = 18;
pit[1].time.td = 0;
pit[1].time.td = cur_time;
pit[1].read_latch = 0xffffffff;
pit[1].write_latch = 18;
pit[1].read_state = 3;
pit[1].write_state = 3;
pit[1].q_ticks = 0;
evtimer_stop(pit[1].evtmr);
pit[2].mode = 0;
pit[2].outpin = 0;
pit[2].cntr = 0xffff;
pit[2].time.td = 0;
pit[2].cntr = 0x10000;
pit[2].time.td = cur_time;
pit[2].read_latch = 0xffffffff;
pit[2].write_latch = 0;
pit[2].read_state = 3;
pit[2].write_state = 3;
pit[2].q_ticks = 0;
evtimer_stop(pit[2].evtmr);
pit[3].mode = 0;
pit[3].outpin = 0;
pit[3].cntr = 0xffff;
pit[3].time.td = 0;
pit[3].cntr = 0x10000;
pit[3].time.td = cur_time;
pit[3].read_latch = 0xffffffff;
pit[3].write_latch = 0;
pit[3].read_state = 3;
pit[3].write_state = 3;
#if 0
timer_handle = timer_create(pit_timer_func, NULL, pit_timer_usecs(0x10000));
#endif
port61 = 0x0c;
pic_sys_time = GETtickTIME(0);
}
void pit_late_init(void)
{
evtimer_set_rel(pit[0].evtmr, TICKS_TO_NS(pit[0].cntr), 1);
pit[0].time.td = 0;
pic_itime[0] = TICKS_TO_NS(pit[0].cntr);
}
#define TIMER0_FLOOD_THRESHOLD 50
int CAN_SLEEP(void)
{
return (!(pic_get_isr() || (REG(eflags) & VIP) || signal_pending() ||
(pit[0].q_ticks > TIMER0_FLOOD_THRESHOLD) || in_leavedos));
}