828 lines
23 KiB
C
828 lines
23 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.
|
|
*
|
|
*
|
|
*
|
|
* Authors: 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/pit_fast.h>
|
|
#include <86box/ppi.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/sound.h>
|
|
#include <86box/snd_speaker.h>
|
|
#include <86box/video.h>
|
|
|
|
#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). */
|
|
|
|
#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
|
|
pitf_ctr_set_out(ctrf_t *ctr, int out, void *priv)
|
|
{
|
|
pitf_t *pit = (pitf_t *)priv;
|
|
|
|
if (ctr == NULL)
|
|
return;
|
|
|
|
if (ctr->out_func != NULL)
|
|
ctr->out_func(out, ctr->out, pit);
|
|
ctr->out = out;
|
|
}
|
|
|
|
static void
|
|
pitf_ctr_set_load_func(void *data, int counter_id, void (*func)(uint8_t new_m, int new_count))
|
|
{
|
|
if (data == NULL)
|
|
return;
|
|
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr = &pit->counters[counter_id];
|
|
|
|
ctr->load_func = func;
|
|
}
|
|
|
|
static uint16_t
|
|
pitf_ctr_get_count(void *data, int counter_id)
|
|
{
|
|
const pitf_t *pit = (pitf_t *) data;
|
|
const ctrf_t *ctr = &pit->counters[counter_id];
|
|
|
|
return (uint16_t) ctr->l;
|
|
}
|
|
|
|
void
|
|
pitf_ctr_set_out_func(void *data, int counter_id, void (*func)(int new_out, int old_out, void *priv))
|
|
{
|
|
if (data == NULL)
|
|
return;
|
|
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr = &pit->counters[counter_id];
|
|
|
|
ctr->out_func = func;
|
|
}
|
|
|
|
void
|
|
pitf_ctr_set_using_timer(void *data, int counter_id, int using_timer)
|
|
{
|
|
if (tsc > 0)
|
|
timer_process();
|
|
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr = &pit->counters[counter_id];
|
|
ctr->using_timer = using_timer;
|
|
}
|
|
|
|
static int
|
|
pitf_read_timer(ctrf_t *ctr)
|
|
{
|
|
if (ctr->using_timer && !(ctr->m == 3 && !ctr->gate) && timer_is_enabled(&ctr->timer)) {
|
|
int read = (int) ((timer_get_remaining_u64(&ctr->timer)) / ctr->pit_const);
|
|
if (ctr->m == 2)
|
|
read++;
|
|
if (read < 0)
|
|
read = 0;
|
|
if (read > 0x10000)
|
|
read = 0x10000;
|
|
if ((ctr->m == 3) && ctr->using_timer)
|
|
read <<= 1;
|
|
return read;
|
|
}
|
|
if (ctr->m == 2)
|
|
return ctr->count + 1;
|
|
return ctr->count;
|
|
}
|
|
|
|
/*Dump timer count back to pit->count[], and disable timer. This should be used
|
|
when stopping a PIT timer, to ensure the correct value can be read back.*/
|
|
static void
|
|
pitf_dump_and_disable_timer(ctrf_t *ctr)
|
|
{
|
|
if (ctr->using_timer && timer_is_enabled(&ctr->timer)) {
|
|
ctr->count = pitf_read_timer(ctr);
|
|
if (ctr->m == 2)
|
|
ctr->count--; /* Don't store the offset from pitf_read_timer */
|
|
timer_disable(&ctr->timer);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pitf_ctr_load(ctrf_t *ctr, void *priv)
|
|
{
|
|
pitf_t *pit = (pitf_t *)priv;
|
|
int l = ctr->l ? ctr->l : 0x10000;
|
|
|
|
ctr->newcount = 0;
|
|
ctr->disabled = 0;
|
|
|
|
switch (ctr->m) {
|
|
case 0: /*Interrupt on terminal count*/
|
|
ctr->count = l;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
ctr->thit = 0;
|
|
ctr->enabled = ctr->gate;
|
|
break;
|
|
case 1: /*Hardware retriggerable one-shot*/
|
|
ctr->enabled = 1;
|
|
break;
|
|
case 2: /*Rate generator*/
|
|
if (ctr->initial) {
|
|
ctr->count = l - 1;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) ((l - 1) * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->thit = 0;
|
|
}
|
|
ctr->enabled = ctr->gate;
|
|
break;
|
|
case 3: /*Square wave mode*/
|
|
if (ctr->initial) {
|
|
ctr->count = l;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (((l + 1) >> 1) * ctr->pit_const));
|
|
else
|
|
ctr->newcount = (l & 1);
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->thit = 0;
|
|
}
|
|
ctr->enabled = ctr->gate;
|
|
break;
|
|
case 4: /*Software triggered stobe*/
|
|
if (!ctr->thit && !ctr->initial)
|
|
ctr->newcount = 1;
|
|
else {
|
|
ctr->count = l;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
ctr->thit = 0;
|
|
}
|
|
ctr->enabled = ctr->gate;
|
|
break;
|
|
case 5: /*Hardware triggered stobe*/
|
|
ctr->enabled = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ctr->load_func != NULL)
|
|
ctr->load_func(ctr->m, l);
|
|
|
|
ctr->initial = 0;
|
|
ctr->running = ctr->enabled && ctr->using_timer && !ctr->disabled;
|
|
if (ctr->using_timer && !ctr->running)
|
|
pitf_dump_and_disable_timer(ctr);
|
|
}
|
|
|
|
static void
|
|
pitf_set_gate_no_timer(ctrf_t *ctr, int gate, void *priv)
|
|
{
|
|
pitf_t *pit = (pitf_t *)priv;
|
|
int l = ctr->l ? ctr->l : 0x10000;
|
|
|
|
if (ctr->disabled) {
|
|
ctr->gate = gate;
|
|
return;
|
|
}
|
|
|
|
switch (ctr->m) {
|
|
case 0: /*Interrupt on terminal count*/
|
|
case 4: /*Software triggered stobe*/
|
|
if (ctr->using_timer && !ctr->running)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
ctr->enabled = gate;
|
|
break;
|
|
case 1: /*Hardware retriggerable one-shot*/
|
|
case 5: /*Hardware triggered stobe*/
|
|
if (gate && !ctr->gate) {
|
|
ctr->count = l;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
ctr->thit = 0;
|
|
ctr->enabled = 1;
|
|
}
|
|
break;
|
|
case 2: /*Rate generator*/
|
|
if (gate && !ctr->gate) {
|
|
ctr->count = l - 1;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->thit = 0;
|
|
}
|
|
ctr->enabled = gate;
|
|
break;
|
|
case 3: /*Square wave mode*/
|
|
if (gate && !ctr->gate) {
|
|
ctr->count = l;
|
|
if (ctr->using_timer)
|
|
timer_set_delay_u64(&ctr->timer, (uint64_t) (((l + 1) >> 1) * ctr->pit_const));
|
|
else
|
|
ctr->newcount = (l & 1);
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->thit = 0;
|
|
}
|
|
ctr->enabled = gate;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
ctr->gate = gate;
|
|
ctr->running = ctr->enabled && ctr->using_timer && !ctr->disabled;
|
|
if (ctr->using_timer && !ctr->running)
|
|
pitf_dump_and_disable_timer(ctr);
|
|
}
|
|
|
|
void
|
|
pitf_ctr_set_gate(void *data, int counter_id, int gate)
|
|
{
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr = &pit->counters[counter_id];
|
|
|
|
if (ctr->disabled) {
|
|
ctr->gate = gate;
|
|
return;
|
|
}
|
|
|
|
pitf_set_gate_no_timer(ctr, gate, pit);
|
|
}
|
|
|
|
static void
|
|
pitf_over(ctrf_t *ctr, void *priv)
|
|
{
|
|
pitf_t *pit = (pitf_t *)priv;
|
|
int l = ctr->l ? ctr->l : 0x10000;
|
|
if (ctr->disabled) {
|
|
ctr->count += 0xffff;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (0xffff * ctr->pit_const));
|
|
return;
|
|
}
|
|
|
|
switch (ctr->m) {
|
|
case 0: /*Interrupt on terminal count*/
|
|
case 1: /*Hardware retriggerable one-shot*/
|
|
if (!ctr->thit)
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->thit = 1;
|
|
ctr->count += 0xffff;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (0xffff * ctr->pit_const));
|
|
break;
|
|
case 2: /*Rate generator*/
|
|
ctr->count += l;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
break;
|
|
case 3: /*Square wave mode*/
|
|
if (ctr->out) {
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
if (ctr->using_timer) {
|
|
ctr->count += (l >> 1);
|
|
timer_advance_u64(&ctr->timer, (uint64_t) ((l >> 1) * ctr->pit_const));
|
|
} else {
|
|
ctr->count += l;
|
|
ctr->newcount = (l & 1);
|
|
}
|
|
} else {
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
ctr->count += ((l + 1) >> 1);
|
|
if (ctr->using_timer) {
|
|
ctr->count += (l >> 1);
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (((l + 1) >> 1) * ctr->pit_const));
|
|
} else {
|
|
ctr->count += l;
|
|
ctr->newcount = (l & 1);
|
|
}
|
|
}
|
|
#if 0
|
|
if (!t)
|
|
pclog("pit_over: square wave mode c=%x %lli %f\n", pit.c[t], tsc, ctr->pit_const);
|
|
#endif
|
|
break;
|
|
case 4: /*Software triggered strove*/
|
|
if (!ctr->thit) {
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
}
|
|
if (ctr->newcount) {
|
|
ctr->newcount = 0;
|
|
ctr->count += l;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (l * ctr->pit_const));
|
|
} else {
|
|
ctr->thit = 1;
|
|
ctr->count += 0xffff;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (0xffff * ctr->pit_const));
|
|
}
|
|
break;
|
|
case 5: /*Hardware triggered strove*/
|
|
if (!ctr->thit) {
|
|
pitf_ctr_set_out(ctr, 0, pit);
|
|
pitf_ctr_set_out(ctr, 1, pit);
|
|
}
|
|
ctr->thit = 1;
|
|
ctr->count += 0xffff;
|
|
if (ctr->using_timer)
|
|
timer_advance_u64(&ctr->timer, (uint64_t) (0xffff * ctr->pit_const));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
ctr->running = ctr->enabled && ctr->using_timer && !ctr->disabled;
|
|
if (ctr->using_timer && !ctr->running)
|
|
pitf_dump_and_disable_timer(ctr);
|
|
}
|
|
|
|
static __inline void
|
|
pitf_ctr_latch_count(ctrf_t *ctr)
|
|
{
|
|
ctr->rl = pitf_read_timer(ctr);
|
|
#if 0
|
|
pclog("Timer latch %f %04X %04X\n",pit->c[0],pit->rl[0],pit->l[0]);
|
|
pit->ctrl |= 0x30;
|
|
#endif
|
|
ctr->rereadlatch = 0;
|
|
ctr->rm = 3;
|
|
ctr->latched = 1;
|
|
}
|
|
|
|
static __inline void
|
|
pitf_ctr_latch_status(ctrf_t *ctr)
|
|
{
|
|
ctr->read_status = (ctr->ctrl & 0x3f) | (ctr->out ? 0x80 : 0);
|
|
ctr->do_read_status = 1;
|
|
}
|
|
|
|
static void
|
|
pitf_write(uint16_t addr, uint8_t val, void *priv)
|
|
{
|
|
pitf_t *dev = (pitf_t *) priv;
|
|
int t = (addr & 3);
|
|
ctrf_t *ctr;
|
|
|
|
pit_log("[%04X:%08X] pit_write(%04X, %02X, %08X)\n", CS, cpu_state.pc, addr, val, priv);
|
|
|
|
cycles -= ISA_CYCLES(8);
|
|
|
|
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)
|
|
pitf_ctr_latch_count(&dev->counters[0]);
|
|
if (val & 4)
|
|
pitf_ctr_latch_count(&dev->counters[1]);
|
|
if (val & 8)
|
|
pitf_ctr_latch_count(&dev->counters[2]);
|
|
pit_log("PIT %i: Initiated readback command\n", t);
|
|
}
|
|
if (!(val & 0x10)) {
|
|
if (val & 2)
|
|
pitf_ctr_latch_status(&dev->counters[0]);
|
|
if (val & 4)
|
|
pitf_ctr_latch_status(&dev->counters[1]);
|
|
if (val & 8)
|
|
pitf_ctr_latch_status(&dev->counters[2]);
|
|
}
|
|
}
|
|
} else {
|
|
dev->ctrl = val;
|
|
ctr = &dev->counters[t];
|
|
|
|
if (!(dev->ctrl & 0x30)) {
|
|
pitf_ctr_latch_count(ctr);
|
|
dev->ctrl |= 0x30;
|
|
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;
|
|
if (!(ctr->rm)) {
|
|
ctr->rm = 3;
|
|
ctr->rl = pitf_read_timer(ctr);
|
|
}
|
|
ctr->rereadlatch = 1;
|
|
ctr->initial = 1;
|
|
if (!ctr->m)
|
|
pitf_ctr_set_out(ctr, 0, dev);
|
|
else
|
|
pitf_ctr_set_out(ctr, 1, dev);
|
|
ctr->disabled = 1;
|
|
|
|
pit_log("PIT %i: M = %i, RM/WM = %i, State = %i, Out = %i\n", t, ctr->m, ctr->rm, ctr->state, ctr->out);
|
|
}
|
|
ctr->thit = 0;
|
|
}
|
|
break;
|
|
|
|
case 0:
|
|
case 1:
|
|
case 2: /* the actual timers */
|
|
ctr = &dev->counters[t];
|
|
|
|
switch (ctr->wm) {
|
|
case 1:
|
|
ctr->l = val;
|
|
pitf_ctr_load(ctr, dev);
|
|
break;
|
|
case 2:
|
|
ctr->l = (val << 8);
|
|
pitf_ctr_load(ctr, dev);
|
|
break;
|
|
case 0:
|
|
ctr->l &= 0xFF;
|
|
ctr->l |= (val << 8);
|
|
pitf_ctr_load(ctr, dev);
|
|
ctr->wm = 3;
|
|
break;
|
|
case 3:
|
|
ctr->l &= 0xFF00;
|
|
ctr->l |= val;
|
|
ctr->wm = 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t
|
|
pitf_read_reg(void *priv, uint8_t reg)
|
|
{
|
|
pitf_t *dev = (pitf_t *) priv;
|
|
uint8_t ret = 0xff;
|
|
|
|
switch (reg) {
|
|
case 0x00:
|
|
case 0x02:
|
|
case 0x04:
|
|
ret = dev->counters[reg >> 1].l & 0xff;
|
|
break;
|
|
case 0x01:
|
|
case 0x03:
|
|
case 0x05:
|
|
ret = (dev->counters[reg >> 1].l >> 8) & 0xff;
|
|
break;
|
|
case 0x06:
|
|
ret = dev->ctrl;
|
|
break;
|
|
case 0x07:
|
|
/* The SiS 551x datasheet is unclear about how exactly
|
|
this register is structured.
|
|
Update: But the SiS 5571 datasheet is clear. */
|
|
ret = (dev->counters[0].rm & 0x80) ? 0x01 : 0x00;
|
|
ret |= (dev->counters[1].rm & 0x80) ? 0x02 : 0x00;
|
|
ret |= (dev->counters[2].rm & 0x80) ? 0x04 : 0x00;
|
|
ret |= (dev->counters[0].wm & 0x80) ? 0x08 : 0x00;
|
|
ret |= (dev->counters[1].wm & 0x80) ? 0x10 : 0x00;
|
|
ret |= (dev->counters[2].wm & 0x80) ? 0x20 : 0x00;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t
|
|
pitf_read(uint16_t addr, void *priv)
|
|
{
|
|
pitf_t *dev = (pitf_t *) priv;
|
|
uint8_t ret = 0xff;
|
|
int t = (addr & 3);
|
|
ctrf_t *ctr;
|
|
|
|
cycles -= ISA_CYCLES(8);
|
|
|
|
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;
|
|
}
|
|
|
|
if (ctr->rereadlatch && !ctr->latched) {
|
|
ctr->rereadlatch = 0;
|
|
ctr->rl = pitf_read_timer(ctr);
|
|
}
|
|
switch (ctr->rm) {
|
|
case 0:
|
|
ret = ctr->rl >> 8;
|
|
ctr->rm = 3;
|
|
ctr->latched = 0;
|
|
ctr->rereadlatch = 1;
|
|
break;
|
|
case 1:
|
|
ret = (ctr->rl) & 0xFF;
|
|
ctr->latched = 0;
|
|
ctr->rereadlatch = 1;
|
|
break;
|
|
case 2:
|
|
ret = (ctr->rl) >> 8;
|
|
ctr->latched = 0;
|
|
ctr->rereadlatch = 1;
|
|
break;
|
|
case 3:
|
|
ret = (ctr->rl) & 0xFF;
|
|
if (ctr->m & 0x80)
|
|
ctr->m &= 7;
|
|
else
|
|
ctr->rm = 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pit_log("[%04X:%08X] pit_read(%04X, %08X) = %02X\n", CS, cpu_state.pc, addr, priv, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
pitf_timer_over(void *priv)
|
|
{
|
|
ctrf_t *ctr = (ctrf_t *) priv;
|
|
pit_t *pit = (pit_t *)ctr->priv;
|
|
pitf_over(ctr, pit);
|
|
}
|
|
|
|
void
|
|
pitf_ctr_clock(void *data, int counter_id)
|
|
{
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr = &pit->counters[counter_id];
|
|
|
|
if (ctr->thit || !ctr->enabled)
|
|
return;
|
|
|
|
if (ctr->using_timer)
|
|
return;
|
|
|
|
if ((ctr->m == 3) && ctr->newcount) {
|
|
ctr->count -= ctr->out ? 1 : 3;
|
|
ctr->newcount = 0;
|
|
} else
|
|
ctr->count -= (ctr->m == 3) ? 2 : 1;
|
|
|
|
if (!ctr->count)
|
|
pitf_over(ctr, pit);
|
|
}
|
|
|
|
static void
|
|
ctr_reset(ctrf_t *ctr)
|
|
{
|
|
ctr->ctrl = 0;
|
|
ctr->m = 0;
|
|
ctr->gate = 0;
|
|
ctr->l = 0xffff;
|
|
ctr->thit = 1;
|
|
ctr->using_timer = 1;
|
|
}
|
|
|
|
static void
|
|
pitf_reset(pitf_t *dev)
|
|
{
|
|
memset(dev, 0, sizeof(pitf_t));
|
|
|
|
for (uint8_t i = 0; i < 3; i++)
|
|
ctr_reset(&dev->counters[i]);
|
|
|
|
/* Disable speaker gate. */
|
|
dev->counters[2].gate = 0;
|
|
}
|
|
|
|
void
|
|
pitf_set_pit_const(void *data, uint64_t pit_const)
|
|
{
|
|
pitf_t *pit = (pitf_t *) data;
|
|
ctrf_t *ctr;
|
|
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
ctr = &pit->counters[i];
|
|
ctr->pit_const = pit_const;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pitf_speed_changed(void *priv)
|
|
{
|
|
pitf_set_pit_const(priv, PITCONST);
|
|
}
|
|
|
|
static void
|
|
pitf_close(void *priv)
|
|
{
|
|
pitf_t *dev = (pitf_t *) priv;
|
|
|
|
if (dev == pit_devs[0].data)
|
|
pit_devs[0].data = NULL;
|
|
|
|
if (dev == pit_devs[1].data)
|
|
pit_devs[1].data = NULL;
|
|
|
|
if (dev != NULL)
|
|
free(dev);
|
|
}
|
|
|
|
void
|
|
pitf_handler(int set, uint16_t base, int size, void *priv)
|
|
{
|
|
io_handler(set, base, size, pitf_read, NULL, NULL, pitf_write, NULL, NULL, priv);
|
|
}
|
|
|
|
static void *
|
|
pitf_init(const device_t *info)
|
|
{
|
|
pitf_t *dev = (pitf_t *) malloc(sizeof(pitf_t));
|
|
|
|
pitf_reset(dev);
|
|
|
|
pitf_set_pit_const(dev, PITCONST);
|
|
|
|
dev->flags = info->local;
|
|
|
|
if (!(dev->flags & PIT_PS2) && !(dev->flags & PIT_CUSTOM_CLOCK)) {
|
|
for (int i = 0; i < 3; i++) {
|
|
ctrf_t *ctr = &dev->counters[i];
|
|
ctr->priv = dev;
|
|
timer_add(&ctr->timer, pitf_timer_over, (void *) ctr, 0);
|
|
}
|
|
}
|
|
|
|
if (!(dev->flags & PIT_EXT_IO)) {
|
|
io_sethandler((dev->flags & PIT_SECONDARY) ? 0x0048 : 0x0040, 0x0004,
|
|
pitf_read, NULL, NULL, pitf_write, NULL, NULL, dev);
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
const device_t i8253_fast_device = {
|
|
.name = "Intel 8253/8253-5 Programmable Interval Timer",
|
|
.internal_name = "i8253_fast",
|
|
.flags = DEVICE_ISA | DEVICE_PIT,
|
|
.local = PIT_8253,
|
|
.init = pitf_init,
|
|
.close = pitf_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = pitf_speed_changed,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t i8254_fast_device = {
|
|
.name = "Intel 8254 Programmable Interval Timer",
|
|
.internal_name = "i8254_fast",
|
|
.flags = DEVICE_ISA | DEVICE_PIT,
|
|
.local = PIT_8254,
|
|
.init = pitf_init,
|
|
.close = pitf_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = pitf_speed_changed,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t i8254_sec_fast_device = {
|
|
.name = "Intel 8254 Programmable Interval Timer (Secondary)",
|
|
.internal_name = "i8254_sec_fast",
|
|
.flags = DEVICE_ISA,
|
|
.local = PIT_8254 | PIT_SECONDARY,
|
|
.init = pitf_init,
|
|
.close = pitf_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = pitf_speed_changed,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t i8254_ext_io_fast_device = {
|
|
.name = "Intel 8254 Programmable Interval Timer (External I/O)",
|
|
.internal_name = "i8254_ext_io_fast",
|
|
.flags = DEVICE_ISA,
|
|
.local = PIT_8254 | PIT_EXT_IO,
|
|
.init = pitf_init,
|
|
.close = pitf_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const device_t i8254_ps2_fast_device = {
|
|
.name = "Intel 8254 Programmable Interval Timer (PS/2)",
|
|
.internal_name = "i8254_ps2_fast",
|
|
.flags = DEVICE_ISA,
|
|
.local = PIT_8254 | PIT_PS2 | PIT_EXT_IO,
|
|
.init = pitf_init,
|
|
.close = pitf_close,
|
|
.reset = NULL,
|
|
{ .available = NULL },
|
|
.speed_changed = pitf_speed_changed,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
const pit_intf_t pit_fast_intf = {
|
|
&pitf_read,
|
|
&pitf_write,
|
|
&pitf_ctr_get_count,
|
|
&pitf_ctr_set_gate,
|
|
&pitf_ctr_set_using_timer,
|
|
&pitf_ctr_set_out_func,
|
|
&pitf_ctr_set_load_func,
|
|
&pitf_ctr_clock,
|
|
&pitf_set_pit_const,
|
|
NULL,
|
|
};
|