1051 lines
20 KiB
C
1051 lines
20 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Implementation of the Intel 8253/8254 Programmable Interval
|
|
* Timer.
|
|
*
|
|
*
|
|
*
|
|
* Author: Miran Grca, <mgrca8@gmail.com>
|
|
* Copyright 2019 Miran Grca.
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include "cpu.h"
|
|
#include <86box/device.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/cassette.h>
|
|
#include <86box/dma.h>
|
|
#include <86box/io.h>
|
|
#include <86box/nmi.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/pit.h>
|
|
#include <86box/ppi.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/sound.h>
|
|
#include <86box/snd_speaker.h>
|
|
#include <86box/video.h>
|
|
|
|
|
|
pit_t *pit, *pit2;
|
|
double cpuclock, PITCONSTD,
|
|
SYSCLK,
|
|
isa_timing,
|
|
bus_timing, pci_timing, agp_timing,
|
|
PCICLK, AGPCLK;
|
|
|
|
uint64_t PITCONST, ISACONST,
|
|
CGACONST,
|
|
MDACONST, HERCCONST,
|
|
VGACONST1, VGACONST2,
|
|
RTCCONST, ACPICONST;
|
|
|
|
int refresh_at_enable = 1,
|
|
io_delay = 5;
|
|
|
|
|
|
int64_t firsttime = 1;
|
|
|
|
|
|
#define PIT_PS2 16 /* The PIT is the PS/2's second PIT. */
|
|
#define PIT_EXT_IO 32 /* The PIT has externally specified port I/O. */
|
|
#define PIT_CUSTOM_CLOCK 64 /* The PIT uses custom clock inputs provided by another provider. */
|
|
#define PIT_SECONDARY 128 /* The PIT is secondary (ports 0048-004B). */
|
|
|
|
|
|
enum {
|
|
PIT_8253 = 0,
|
|
PIT_8254
|
|
};
|
|
|
|
|
|
#ifdef ENABLE_PIT_LOG
|
|
int pit_do_log = ENABLE_PIT_LOG;
|
|
|
|
|
|
static void
|
|
pit_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (pit_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
#define pit_log(fmt, ...)
|
|
#endif
|
|
|
|
|
|
static void
|
|
ctr_set_out(ctr_t *ctr, int out)
|
|
{
|
|
if (ctr == NULL)
|
|
return;
|
|
|
|
if (ctr->out_func != NULL)
|
|
ctr->out_func(out, ctr->out);
|
|
ctr->out = out;
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_decrease_count(ctr_t *ctr)
|
|
{
|
|
if (ctr->bcd) {
|
|
ctr->units--;
|
|
if (ctr->units == -1) {
|
|
ctr->units = -7;
|
|
ctr->tens--;
|
|
if (ctr->tens == -1) {
|
|
ctr->tens = -7;
|
|
ctr->hundreds--;
|
|
if (ctr->hundreds == -1) {
|
|
ctr->hundreds = -7;
|
|
ctr->thousands--;
|
|
if (ctr->thousands == -1) {
|
|
ctr->thousands = -7;
|
|
ctr->myriads--;
|
|
if (ctr->myriads == -1)
|
|
ctr->myriads = -7; /* 0 - 1 should wrap around to 9999. */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
ctr->count = (ctr->count - 1) & 0xffff;
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_load_count(ctr_t *ctr)
|
|
{
|
|
int l = ctr->l ? ctr->l : 0x10000;
|
|
|
|
ctr->count = l;
|
|
pit_log("ctr->count = %i\n", l);
|
|
ctr->null_count = 0;
|
|
ctr->newcount = !!(l & 1);
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_tick(ctr_t *ctr)
|
|
{
|
|
uint8_t state = ctr->state;
|
|
|
|
if (state == 1) {
|
|
/* This is true for all modes */
|
|
ctr_load_count(ctr);
|
|
ctr->state = 2;
|
|
return;
|
|
}
|
|
|
|
switch(ctr->m & 0x07) {
|
|
case 0:
|
|
/* Interrupt on terminal count */
|
|
switch (state) {
|
|
case 2:
|
|
if (ctr->gate && (ctr->count >= 1)) {
|
|
ctr_decrease_count(ctr);
|
|
if (ctr->count < 1) {
|
|
ctr->state = 3;
|
|
ctr_set_out(ctr, 1);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
ctr_decrease_count(ctr);
|
|
break;
|
|
}
|
|
break;
|
|
case 1:
|
|
/* Hardware retriggerable one-shot */
|
|
switch (state) {
|
|
case 1:
|
|
ctr_load_count(ctr);
|
|
ctr->state = 2;
|
|
ctr_set_out(ctr, 0);
|
|
break;
|
|
case 2:
|
|
if (ctr->count >= 1) {
|
|
ctr_decrease_count(ctr);
|
|
if (ctr->count < 1) {
|
|
ctr->state = 3;
|
|
ctr_set_out(ctr, 1);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
ctr_decrease_count(ctr);
|
|
break;
|
|
}
|
|
break;
|
|
case 2: case 6:
|
|
/* Rate generator */
|
|
switch (state) {
|
|
case 3:
|
|
ctr_load_count(ctr);
|
|
ctr->state = 2;
|
|
ctr_set_out(ctr, 1);
|
|
break;
|
|
case 2:
|
|
if (ctr->gate == 0)
|
|
break;
|
|
else if (ctr->count >= 2) {
|
|
ctr_decrease_count(ctr);
|
|
if (ctr->count < 2) {
|
|
ctr->state = 3;
|
|
ctr_set_out(ctr, 0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 3: case 7:
|
|
/* Square wave mode */
|
|
switch (state) {
|
|
case 2:
|
|
if (ctr->gate == 0)
|
|
break;
|
|
else if (ctr->count >= 0) {
|
|
if (ctr->bcd) {
|
|
ctr_decrease_count(ctr);
|
|
if (!ctr->newcount)
|
|
ctr_decrease_count(ctr);
|
|
} else
|
|
ctr->count -= (ctr->newcount ? 1 : 2);
|
|
if (ctr->count < 0) {
|
|
ctr_load_count(ctr);
|
|
ctr->state = 3;
|
|
ctr_set_out(ctr, 0);
|
|
} else if (ctr->newcount)
|
|
ctr->newcount = 0;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (ctr->gate == 0)
|
|
break;
|
|
else if (ctr->count >= 0) {
|
|
if (ctr->bcd) {
|
|
ctr_decrease_count(ctr);
|
|
ctr_decrease_count(ctr);
|
|
if (ctr->newcount)
|
|
ctr_decrease_count(ctr);
|
|
} else
|
|
ctr->count -= (ctr->newcount ? 3 : 2);
|
|
if (ctr->count < 0) {
|
|
ctr_load_count(ctr);
|
|
ctr->state = 2;
|
|
ctr_set_out(ctr, 1);
|
|
} else if (ctr->newcount)
|
|
ctr->newcount = 0;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 4: case 5:
|
|
/* Software triggered strobe */
|
|
/* Hardware triggered strobe */
|
|
if ((ctr->gate != 0) || (ctr->m != 4)) {
|
|
switch(state) {
|
|
case 0:
|
|
ctr_decrease_count(ctr);
|
|
break;
|
|
case 2:
|
|
if (ctr->count >= 1) {
|
|
ctr_decrease_count(ctr);
|
|
if (ctr->count < 1) {
|
|
ctr->state = 3;
|
|
ctr_set_out(ctr, 0);
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
ctr->state = 0;
|
|
ctr_set_out(ctr, 1);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_clock(ctr_t *ctr)
|
|
{
|
|
/* FIXME: Is this even needed? */
|
|
if ((ctr->state == 3) && (ctr->m != 2) && (ctr->m != 3))
|
|
return;
|
|
|
|
if (ctr->using_timer)
|
|
return;
|
|
|
|
ctr_tick(ctr);
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_set_state_1(ctr_t *ctr)
|
|
{
|
|
uint8_t mode = (ctr->m & 0x03);
|
|
|
|
if ((mode == 0) || ((mode > 1) && (ctr->state == 0)))
|
|
ctr->state = 1;
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_load(ctr_t *ctr)
|
|
{
|
|
if (ctr->l == 1) {
|
|
/* Count of 1 is illegal in modes 2 and 3. What happens here was
|
|
determined experimentally. */
|
|
if (ctr->m == 2)
|
|
ctr->l = 2;
|
|
else if (ctr->m == 3)
|
|
ctr->l = 0;
|
|
}
|
|
|
|
if (ctr->using_timer)
|
|
ctr->latch = 1;
|
|
else
|
|
ctr_set_state_1(ctr);
|
|
|
|
if (ctr->load_func != NULL)
|
|
ctr->load_func(ctr->m, ctr->l ? ctr->l : 0x10000);
|
|
|
|
pit_log("Counter loaded, state = %i, gate = %i\n", ctr->state, ctr->gate);
|
|
}
|
|
|
|
|
|
static __inline void
|
|
ctr_latch_status(ctr_t *ctr)
|
|
{
|
|
ctr->read_status = (ctr->ctrl & 0x3f) | (ctr->out ? 0x80 : 0) | (ctr->null_count ? 0x40 : 0);
|
|
ctr->do_read_status = 1;
|
|
}
|
|
|
|
|
|
static __inline void
|
|
ctr_latch_count(ctr_t *ctr)
|
|
{
|
|
int count = (ctr->latch || (ctr->state == 1)) ? ctr->l : ctr->count;
|
|
|
|
switch (ctr->rm & 0x03) {
|
|
case 0x00:
|
|
/* This should never happen. */
|
|
break;
|
|
case 0x01:
|
|
/* Latch bits 0-7 only. */
|
|
ctr->rl = ((count << 8) & 0xff00) | (count & 0xff);
|
|
ctr->latched = 1;
|
|
break;
|
|
case 0x02:
|
|
/* Latch bit 8-15 only. */
|
|
ctr->rl = (count & 0xff00) | ((count >> 8) & 0xff);
|
|
ctr->latched = 1;
|
|
break;
|
|
case 0x03:
|
|
/* Latch all 16 bits. */
|
|
ctr->rl = count;
|
|
ctr->latched = 2;
|
|
break;
|
|
}
|
|
|
|
pit_log("latched counter = %04X\n", ctr->rl & 0xffff);
|
|
}
|
|
|
|
|
|
uint16_t
|
|
pit_ctr_get_count(ctr_t *ctr)
|
|
{
|
|
return (uint16_t) ctr->l;
|
|
}
|
|
|
|
|
|
void
|
|
pit_ctr_set_load_func(ctr_t *ctr, void (*func)(uint8_t new_m, int new_count))
|
|
{
|
|
if (ctr == NULL)
|
|
return;
|
|
|
|
ctr->load_func = func;
|
|
}
|
|
|
|
|
|
void
|
|
pit_ctr_set_out_func(ctr_t *ctr, void (*func)(int new_out, int old_out))
|
|
{
|
|
if (ctr == NULL)
|
|
return;
|
|
|
|
ctr->out_func = func;
|
|
}
|
|
|
|
|
|
void
|
|
pit_ctr_set_gate(ctr_t *ctr, int gate)
|
|
{
|
|
int old = ctr->gate;
|
|
uint8_t mode = ctr->m & 3;
|
|
|
|
ctr->gate = gate;
|
|
|
|
switch (mode) {
|
|
case 1: case 2: case 3: case 5: case 6: case 7:
|
|
if (!old && gate) {
|
|
/* Here we handle the rising edges. */
|
|
if (mode & 1) {
|
|
if (mode != 1)
|
|
ctr_set_out(ctr, 1);
|
|
ctr->state = 1;
|
|
} else if (mode == 2)
|
|
ctr->state = 3;
|
|
} else if (old && !gate) {
|
|
/* Here we handle the lowering edges. */
|
|
if (mode & 2)
|
|
ctr_set_out(ctr, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static __inline void
|
|
pit_ctr_set_clock_common(ctr_t *ctr, int clock)
|
|
{
|
|
int old = ctr->clock;
|
|
|
|
ctr->clock = clock;
|
|
|
|
if (ctr->using_timer && ctr->latch) {
|
|
if (old && !ctr->clock) {
|
|
ctr_set_state_1(ctr);
|
|
ctr->latch = 0;
|
|
}
|
|
} else if (ctr->using_timer && !ctr->latch) {
|
|
if (ctr->state == 1) {
|
|
if (!old && ctr->clock)
|
|
ctr->s1_det = 1; /* Rising edge. */
|
|
else if (old && !ctr->clock) {
|
|
ctr->s1_det++; /* Falling edge. */
|
|
if (ctr->s1_det >= 2) {
|
|
ctr->s1_det = 0;
|
|
ctr_tick(ctr);
|
|
}
|
|
}
|
|
} else if (old && !ctr->clock)
|
|
ctr_tick(ctr);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
pit_ctr_set_clock(ctr_t *ctr, int clock)
|
|
{
|
|
pit_ctr_set_clock_common(ctr, clock);
|
|
}
|
|
|
|
|
|
void
|
|
pit_ctr_set_using_timer(ctr_t *ctr, int using_timer)
|
|
{
|
|
timer_process();
|
|
|
|
ctr->using_timer = using_timer;
|
|
}
|
|
|
|
|
|
static void
|
|
pit_timer_over(void *p)
|
|
{
|
|
pit_t *dev = (pit_t *) p;
|
|
int i;
|
|
|
|
dev->clock ^= 1;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
pit_ctr_set_clock_common(&dev->counters[i], dev->clock);
|
|
|
|
timer_advance_u64(&dev->callback_timer, PITCONST >> 1ULL);
|
|
}
|
|
|
|
|
|
static void
|
|
pit_write(uint16_t addr, uint8_t val, void *priv)
|
|
{
|
|
pit_t *dev = (pit_t *)priv;
|
|
int t = (addr & 3);
|
|
ctr_t *ctr;
|
|
|
|
pit_log("[%04X:%08X] pit_write(%04X, %02X, %08X)\n", CS, cpu_state.pc, addr, val, priv);
|
|
|
|
switch (addr & 3) {
|
|
case 3: /* control */
|
|
t = val >> 6;
|
|
|
|
if (t == 3) {
|
|
if (dev->flags & PIT_8254) {
|
|
/* This is 8254-only. */
|
|
if (!(val & 0x20)) {
|
|
if (val & 2)
|
|
ctr_latch_count(&dev->counters[0]);
|
|
if (val & 4)
|
|
ctr_latch_count(&dev->counters[1]);
|
|
if (val & 8)
|
|
ctr_latch_count(&dev->counters[2]);
|
|
pit_log("PIT %i: Initiated readback command\n", t);
|
|
}
|
|
if (!(val & 0x10)) {
|
|
if (val & 2)
|
|
ctr_latch_status(&dev->counters[0]);
|
|
if (val & 4)
|
|
ctr_latch_status(&dev->counters[1]);
|
|
if (val & 8)
|
|
ctr_latch_status(&dev->counters[2]);
|
|
}
|
|
}
|
|
} else {
|
|
dev->ctrl = val;
|
|
ctr = &dev->counters[t];
|
|
|
|
if (!(dev->ctrl & 0x30)) {
|
|
ctr_latch_count(ctr);
|
|
pit_log("PIT %i: Initiated latched read, %i bytes latched\n",
|
|
t, ctr->latched);
|
|
} else {
|
|
ctr->ctrl = val;
|
|
ctr->rm = ctr->wm = (ctr->ctrl >> 4) & 3;
|
|
ctr->m = (val >> 1) & 7;
|
|
if (ctr->m > 5)
|
|
ctr->m &= 3;
|
|
ctr->null_count = 1;
|
|
ctr->bcd = (ctr->ctrl & 0x01);
|
|
ctr_set_out(ctr, !!ctr->m);
|
|
ctr->state = 0;
|
|
if (ctr->latched) {
|
|
pit_log("PIT %i: Reload while counter is latched\n", t);
|
|
ctr->rl--;
|
|
}
|
|
|
|
pit_log("PIT %i: M = %i, RM/WM = %i, State = %i, Out = %i\n", t, ctr->m, ctr->rm, ctr->state, ctr->out);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
case 1:
|
|
case 2: /* the actual timers */
|
|
ctr = &dev->counters[t];
|
|
|
|
switch (ctr->wm) {
|
|
case 0:
|
|
/* This should never happen. */
|
|
break;
|
|
case 1:
|
|
ctr->l = val;
|
|
if (ctr->m == 0)
|
|
ctr_set_out(ctr, 0);
|
|
ctr_load(ctr);
|
|
break;
|
|
case 2:
|
|
ctr->l = (val << 8);
|
|
if (ctr->m == 0)
|
|
ctr_set_out(ctr, 0);
|
|
ctr_load(ctr);
|
|
break;
|
|
case 3: case 0x83:
|
|
if (ctr->wm & 0x80) {
|
|
ctr->l = (ctr->l & 0x00ff) | (val << 8);
|
|
pit_log("PIT %i: Written high byte %02X, latch now %04X\n", t, val, ctr->l);
|
|
ctr_load(ctr);
|
|
} else {
|
|
ctr->l = (ctr->l & 0xff00) | val;
|
|
pit_log("PIT %i: Written low byte %02X, latch now %04X\n", t, val, ctr->l);
|
|
if (ctr->m == 0) {
|
|
ctr->state = 0;
|
|
ctr_set_out(ctr, 0);
|
|
}
|
|
}
|
|
|
|
if (ctr->wm & 0x80)
|
|
ctr->wm &= ~0x80;
|
|
else
|
|
ctr->wm |= 0x80;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static uint8_t
|
|
pit_read(uint16_t addr, void *priv)
|
|
{
|
|
pit_t *dev = (pit_t *)priv;
|
|
uint8_t ret = 0xff;
|
|
int count, t = (addr & 3);
|
|
ctr_t *ctr;
|
|
|
|
switch (addr & 3) {
|
|
case 3: /* Control. */
|
|
/* This is 8254-only, 8253 returns 0x00. */
|
|
ret = (dev->flags & PIT_8254) ? dev->ctrl : 0x00;
|
|
break;
|
|
|
|
case 0:
|
|
case 1:
|
|
case 2: /* The actual timers. */
|
|
ctr = &dev->counters[t];
|
|
|
|
if (ctr->do_read_status) {
|
|
ctr->do_read_status = 0;
|
|
ret = ctr->read_status;
|
|
break;
|
|
}
|
|
|
|
count = (ctr->state == 1) ? ctr->l : ctr->count;
|
|
|
|
if (ctr->latched) {
|
|
ret = (ctr->rl) >> ((ctr->rm & 0x80) ? 8 : 0);
|
|
|
|
if (ctr->rm & 0x80)
|
|
ctr->rm &= ~0x80;
|
|
else
|
|
ctr->rm |= 0x80;
|
|
|
|
ctr->latched--;
|
|
} else switch (ctr->rm) {
|
|
case 0: case 0x80:
|
|
ret = 0x00;
|
|
break;
|
|
|
|
case 1:
|
|
ret = count & 0xff;
|
|
break;
|
|
|
|
case 2:
|
|
ret = count >> 8;
|
|
break;
|
|
|
|
case 3: case 0x83:
|
|
/* Yes, wm is correct here - this is to ensure correct readout while the
|
|
count is being written. */
|
|
if (ctr->wm & 0x80)
|
|
ret = ~(ctr->l & 0xff);
|
|
else
|
|
ret = count >> ((ctr->rm & 0x80) ? 8 : 0);
|
|
|
|
if (ctr->rm & 0x80)
|
|
ctr->rm &= ~0x80;
|
|
else
|
|
ctr->rm |= 0x80;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
pit_log("[%04X:%08X] pit_read(%04X, %08X) = %02X\n", CS, cpu_state.pc, addr, priv, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* FIXME: Should be moved to machine.c (default for most machine). */
|
|
void
|
|
pit_irq0_timer(int new_out, int old_out)
|
|
{
|
|
if (new_out && !old_out)
|
|
picint(1);
|
|
|
|
if (!new_out)
|
|
picintc(1);
|
|
}
|
|
|
|
|
|
void
|
|
pit_irq0_timer_pcjr(int new_out, int old_out)
|
|
{
|
|
if (new_out && !old_out) {
|
|
picint(1);
|
|
ctr_clock(&pit->counters[1]);
|
|
}
|
|
|
|
if (!new_out)
|
|
picintc(1);
|
|
}
|
|
|
|
|
|
void
|
|
pit_irq0_timer_ps2(int new_out, int old_out)
|
|
{
|
|
ctr_t *ctr = &pit2->counters[0];
|
|
|
|
if (new_out && !old_out) {
|
|
picint(1);
|
|
pit_ctr_set_gate(ctr, 1);
|
|
}
|
|
|
|
if (!new_out)
|
|
picintc(1);
|
|
|
|
if (!new_out && old_out)
|
|
ctr_clock(ctr);
|
|
}
|
|
|
|
|
|
void
|
|
pit_refresh_timer_xt(int new_out, int old_out)
|
|
{
|
|
if (new_out && !old_out)
|
|
dma_channel_read(0);
|
|
}
|
|
|
|
|
|
void
|
|
pit_refresh_timer_at(int new_out, int old_out)
|
|
{
|
|
if (refresh_at_enable && new_out && !old_out)
|
|
ppi.pb ^= 0x10;
|
|
}
|
|
|
|
|
|
void
|
|
pit_speaker_timer(int new_out, int old_out)
|
|
{
|
|
int l;
|
|
|
|
if (cassette != NULL)
|
|
pc_cas_set_out(cassette, new_out);
|
|
|
|
speaker_update();
|
|
|
|
l = pit->counters[2].l ? pit->counters[2].l : 0x10000;
|
|
if (l < 25)
|
|
speakon = 0;
|
|
else
|
|
speakon = new_out;
|
|
|
|
ppispeakon = new_out;
|
|
}
|
|
|
|
|
|
void
|
|
pit_nmi_timer_ps2(int new_out, int old_out)
|
|
{
|
|
nmi = new_out;
|
|
|
|
if (nmi)
|
|
nmi_auto_clear = 1;
|
|
}
|
|
|
|
|
|
static void
|
|
ctr_reset(ctr_t *ctr)
|
|
{
|
|
ctr->ctrl = 0;
|
|
ctr->m = 0;
|
|
ctr->gate = 0;
|
|
ctr->l = 0xffff;
|
|
ctr->using_timer = 1;
|
|
ctr->state = 0;
|
|
ctr->null_count = 1;
|
|
|
|
ctr->latch = 0;
|
|
|
|
ctr->s1_det = 0;
|
|
ctr->l_det = 0;
|
|
}
|
|
|
|
|
|
void
|
|
pit_reset(pit_t *dev)
|
|
{
|
|
int i;
|
|
|
|
memset(dev, 0, sizeof(pit_t));
|
|
|
|
dev->clock = 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
ctr_reset(&dev->counters[i]);
|
|
|
|
/* Disable speaker gate. */
|
|
dev->counters[2].gate = 0;
|
|
}
|
|
|
|
|
|
void
|
|
pit_handler(int set, uint16_t base, int size, void *priv)
|
|
{
|
|
io_handler(set, base, size, pit_read, NULL, NULL, pit_write, NULL, NULL, priv);
|
|
}
|
|
|
|
|
|
static void
|
|
pit_close(void *priv)
|
|
{
|
|
pit_t *dev = (pit_t *) priv;
|
|
|
|
if (dev == pit)
|
|
pit = NULL;
|
|
|
|
if (dev == pit2)
|
|
pit2 = NULL;
|
|
|
|
if (dev != NULL)
|
|
free(dev);
|
|
}
|
|
|
|
|
|
static void *
|
|
pit_init(const device_t *info)
|
|
{
|
|
pit_t *dev = (pit_t *) malloc(sizeof(pit_t));
|
|
pit_reset(dev);
|
|
|
|
if (!(dev->flags & PIT_PS2) && !(dev->flags & PIT_CUSTOM_CLOCK)) {
|
|
timer_add(&dev->callback_timer, pit_timer_over, (void *) dev, 0);
|
|
timer_set_delay_u64(&dev->callback_timer, PITCONST >> 1ULL);
|
|
}
|
|
|
|
dev->flags = info->local;
|
|
|
|
if (!(dev->flags & PIT_EXT_IO)) {
|
|
io_sethandler((dev->flags & PIT_SECONDARY) ? 0x0048 : 0x0040, 0x0004,
|
|
pit_read, NULL, NULL, pit_write, NULL, NULL, dev);
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
|
|
const device_t i8253_device =
|
|
{
|
|
"Intel 8253/8253-5 Programmable Interval Timer",
|
|
DEVICE_ISA,
|
|
PIT_8253,
|
|
pit_init, pit_close, NULL,
|
|
{ NULL }, NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
const device_t i8254_device =
|
|
{
|
|
"Intel 8254 Programmable Interval Timer",
|
|
DEVICE_ISA,
|
|
PIT_8254,
|
|
pit_init, pit_close, NULL,
|
|
{ NULL }, NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
const device_t i8254_sec_device =
|
|
{
|
|
"Intel 8254 Programmable Interval Timer (Secondary)",
|
|
DEVICE_ISA,
|
|
PIT_8254 | PIT_SECONDARY,
|
|
pit_init, pit_close, NULL,
|
|
{ NULL }, NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
const device_t i8254_ext_io_device =
|
|
{
|
|
"Intel 8254 Programmable Interval Timer (External I/O)",
|
|
DEVICE_ISA,
|
|
PIT_8254 | PIT_EXT_IO,
|
|
pit_init, pit_close, NULL,
|
|
{ NULL }, NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
const device_t i8254_ps2_device =
|
|
{
|
|
"Intel 8254 Programmable Interval Timer (PS/2)",
|
|
DEVICE_ISA,
|
|
PIT_8254 | PIT_PS2 | PIT_EXT_IO,
|
|
pit_init, pit_close, NULL,
|
|
{ NULL }, NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
pit_t *
|
|
pit_common_init(int type, void (*out0)(int new_out, int old_out), void (*out1)(int new_out, int old_out))
|
|
{
|
|
int i;
|
|
|
|
switch (type) {
|
|
case PIT_8253:
|
|
default:
|
|
pit = device_add(&i8253_device);
|
|
break;
|
|
case PIT_8254:
|
|
pit = device_add(&i8254_device);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
pit->counters[i].gate = 1;
|
|
pit->counters[i].using_timer = 1;
|
|
}
|
|
|
|
pit_ctr_set_out_func(&pit->counters[0], out0);
|
|
pit_ctr_set_out_func(&pit->counters[1], out1);
|
|
pit_ctr_set_out_func(&pit->counters[2], pit_speaker_timer);
|
|
pit_ctr_set_load_func(&pit->counters[2], speaker_set_count);
|
|
pit->counters[2].gate = 0;
|
|
|
|
return pit;
|
|
}
|
|
|
|
|
|
pit_t *
|
|
pit_ps2_init(void)
|
|
{
|
|
pit2 = device_add(&i8254_ps2_device);
|
|
|
|
pit_handler(1, 0x0044, 0x0001, pit2);
|
|
pit_handler(1, 0x0047, 0x0001, pit2);
|
|
|
|
pit2->counters[0].gate = 0;
|
|
pit2->counters[0].using_timer = pit2->counters[1].using_timer = pit2->counters[2].using_timer = 0;
|
|
|
|
pit_ctr_set_out_func(&pit->counters[0], pit_irq0_timer_ps2);
|
|
pit_ctr_set_out_func(&pit2->counters[0], pit_nmi_timer_ps2);
|
|
|
|
return pit2;
|
|
}
|
|
|
|
|
|
void
|
|
pit_set_clock(int clock)
|
|
{
|
|
/* Set default CPU/crystal clock and xt_cpu_multi. */
|
|
if (cpu_s->cpu_type >= CPU_286) {
|
|
int remainder = (clock % 100000000);
|
|
if (remainder == 66666666)
|
|
cpuclock = (double) (clock - remainder) + (200000000.0 / 3.0);
|
|
else if (remainder == 33333333)
|
|
cpuclock = (double) (clock - remainder) + (100000000.0 / 3.0);
|
|
else
|
|
cpuclock = (double) clock;
|
|
|
|
PITCONSTD = (cpuclock / 1193182.0);
|
|
PITCONST = (uint64_t) (PITCONSTD * (double)(1ull << 32));
|
|
CGACONST = (uint64_t) ((cpuclock / (19687503.0/11.0)) * (double)(1ull << 32));
|
|
ISACONST = (uint64_t) ((cpuclock / (double)cpu_isa_speed) * (double)(1ull << 32));
|
|
xt_cpu_multi = 1ULL;
|
|
} else {
|
|
cpuclock = 14318184.0;
|
|
PITCONSTD = 12.0;
|
|
PITCONST = (12ULL << 32ULL);
|
|
CGACONST = (8ULL << 32ULL);
|
|
xt_cpu_multi = 3ULL;
|
|
|
|
switch (cpu_s->rspeed) {
|
|
case 7159092:
|
|
if (cpu_s->cpu_flags & CPU_ALTERNATE_XTAL) {
|
|
cpuclock = 28636368.0;
|
|
xt_cpu_multi = 4ULL;
|
|
} else
|
|
xt_cpu_multi = 2ULL;
|
|
break;
|
|
|
|
case 8000000:
|
|
cpuclock = 24000000.0;
|
|
break;
|
|
case 9545456:
|
|
cpuclock = 28636368.0;
|
|
break;
|
|
case 10000000:
|
|
cpuclock = 30000000.0;
|
|
break;
|
|
case 12000000:
|
|
cpuclock = 36000000.0;
|
|
break;
|
|
case 16000000:
|
|
cpuclock = 48000000.0;
|
|
break;
|
|
|
|
default:
|
|
if (cpu_s->cpu_flags & CPU_ALTERNATE_XTAL) {
|
|
cpuclock = 28636368.0;
|
|
xt_cpu_multi = 6ULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (cpuclock == 28636368.0) {
|
|
PITCONSTD = 24.0;
|
|
PITCONST = (24ULL << 32LL);
|
|
CGACONST = (16ULL << 32LL);
|
|
} else if (cpuclock != 14318184.0) {
|
|
PITCONSTD = (cpuclock / 1193182.0);
|
|
PITCONST = (uint64_t) (PITCONSTD * (double)(1ull << 32));
|
|
CGACONST = (uint64_t) (((cpuclock/(19687503.0/11.0)) * (double)(1ull << 32)));
|
|
}
|
|
|
|
ISACONST = (1ULL << 32ULL);
|
|
}
|
|
xt_cpu_multi <<= 32ULL;
|
|
|
|
/* Delay for empty I/O ports. */
|
|
io_delay = (int) round(((double) cpu_s->rspeed) / 3000000.0);
|
|
|
|
MDACONST = (uint64_t) (cpuclock / 2032125.0 * (double)(1ull << 32));
|
|
HERCCONST = MDACONST;
|
|
VGACONST1 = (uint64_t) (cpuclock / 25175000.0 * (double)(1ull << 32));
|
|
VGACONST2 = (uint64_t) (cpuclock / 28322000.0 * (double)(1ull << 32));
|
|
RTCCONST = (uint64_t) (cpuclock / 32768.0 * (double)(1ull << 32));
|
|
ACPICONST = (uint64_t) (cpuclock / 3579545.0 * (double)(1ull << 32));
|
|
|
|
TIMER_USEC = (uint64_t)((cpuclock / 1000000.0) * (double)(1ull << 32));
|
|
|
|
isa_timing = (cpuclock / (double)cpu_isa_speed);
|
|
if (cpu_64bitbus)
|
|
bus_timing = (cpuclock / ((double)cpu_busspeed / 2));
|
|
else
|
|
bus_timing = (cpuclock / (double)cpu_busspeed);
|
|
pci_timing = (cpuclock / (double)cpu_pci_speed);
|
|
agp_timing = (cpuclock / (double)cpu_agp_speed);
|
|
|
|
/* PCICLK in us for use with timer_on_auto(). */
|
|
PCICLK = pci_timing / (cpuclock / 1000000.0);
|
|
AGPCLK = agp_timing / (cpuclock / 1000000.0);
|
|
|
|
if (cpu_busspeed >= 30000000)
|
|
SYSCLK = bus_timing * 4.0;
|
|
else
|
|
SYSCLK = bus_timing * 3.0;
|
|
|
|
video_update_timing();
|
|
|
|
device_speed_changed();
|
|
}
|