/*
 * 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;
	if ((ctr->m & 0x07) == 0x01)
		ctr_set_out(ctr, 0);
	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 = {
    .name = "Intel 8253/8253-5 Programmable Interval Timer",
    .internal_name = "i8253",
    .flags = DEVICE_ISA,
    .local = PIT_8253,
    .init = pit_init,
    .close = pit_close,
    .reset = NULL,
    { .available = NULL },
    .speed_changed = NULL,
    .force_redraw = NULL,
    .config = NULL
};

const device_t i8254_device = {
    .name = "Intel 8254 Programmable Interval Timer",
    .internal_name = "i8254",
    .flags = DEVICE_ISA,
    .local = PIT_8254,
    .init = pit_init,
    .close = pit_close,
    .reset = NULL,
    { .available = NULL },
    .speed_changed = NULL,
    .force_redraw = NULL,
    .config = NULL
};

const device_t i8254_sec_device = {
    .name = "Intel 8254 Programmable Interval Timer (Secondary)",
    .internal_name = "i8254_sec",
    .flags = DEVICE_ISA,
    .local = PIT_8254 | PIT_SECONDARY,
    .init = pit_init,
    .close = pit_close,
    .reset = NULL,
    { .available = NULL },
    .speed_changed = NULL,
    .force_redraw = NULL,
    .config = NULL
};

const device_t i8254_ext_io_device = {
    .name = "Intel 8254 Programmable Interval Timer (External I/O)",
    .internal_name = "i8254_ext_io",
    .flags = DEVICE_ISA,
    .local = PIT_8254 | PIT_EXT_IO,
    .init = pit_init,
    .close = pit_close,
    .reset = NULL,
    { .available = NULL },
    .speed_changed = NULL,
    .force_redraw = NULL,
    .config = NULL
};

const device_t i8254_ps2_device = {
    .name = "Intel 8254 Programmable Interval Timer (PS/2)",
    .internal_name = "i8254_ps2",
    .flags = DEVICE_ISA,
    .local = PIT_8254 | PIT_PS2 | PIT_EXT_IO,
    .init = pit_init,
    .close = pit_close,
    .reset = NULL,
    { .available = NULL },
    .speed_changed = NULL,
    .force_redraw = NULL,
    .config = 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();
}
