Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2881 lines
71 KiB

#include "insignia.h"
#include "host_def.h"
/*
* SoftPC version 2.0
*
* Title : Time Handler
*
* Description : Emulate the 8253 3-channel timer; invoke 'BIOS
* sytem timer interrupt code', cursor flash, repeat
* key processing etc.
*
* Author : Jerry Kramskoy
*
* Notes : There is only one real time timer per process, this
* module counts clock ticks and distributes calls
* to the appropriate functions as required.
*
* This module is host independent - see xxxx_timer.c
* where xxxx is a machine type for host dependent stuff.
*
* Mods: (r3.2) : (SCR 257). Code has been added to time_tick() to spot
* that video has been disabled for a period. If this is
* so, clear the screen. Refresh when video is enabled
* again.
*
* (r3.4) : Make use of the host time structures host_timeval,
* host_timezone, and host_tm, which are equivalent
* to the Unix BSD4.2 structures.
* Also convert references to gettimeofday() to
* host_getIdealTime().
*/
#ifdef SCCSID
static char SccsID[]="@(#)timer.c 1.41 05/31/95 Copyright Insignia Solutions Ltd.";
#endif
#ifdef SEGMENTATION
/*
* The following #include specifies the code segment into which this
* module will by placed by the MPW C compiler on the Mac II running
* MultiFinder.
*/
#include "SOFTPC_SUPPORT.seg"
#endif
/*
* O/S include files.
*/
#include <stdio.h>
#include TypesH
#include TimeH
/*
* SoftPC include files
*/
#include "xt.h"
#include CpuH
#include "sas.h"
#include "ios.h"
#include "ica.h"
#include "trace.h"
#include "bios.h"
#include "host.h"
#include "timer.h"
#include "timeval.h"
#include "idetect.h"
#include "debug.h"
#include "quick_ev.h"
#ifndef PROD
#include <stdlib.h>
#endif
#ifdef HUNTER
#include "hunter.h"
#endif
#ifdef NTVDM
#include "fla.h"
#include "nt_eoi.h"
#include "nt_reset.h"
#include "nt_pif.h"
#undef LOCAL
#define LOCAL
#endif
/* Imports */
/*
* ============================================================================
* Local static data and defines
* ============================================================================
*/
/* 'idealtime' gets initialised to be the host system's current
* time value (timer_init()), and thereafter gets incremented by
* the value 'idealInterval' every time that time_tick() gets called
* which gives the illusion of a 100% accurate signal delivery
*/
static struct host_timeval idealtime;
static unsigned long idealInterval;
#ifdef NTVDM
ULONG timer_delay_size= HOST_IDEAL_ALARM >> 1; // usecs
ULONG EoiPending=0;
ULONG EoiIntsPending=0;
ULONG EoiDelayInUse=0;
int ticks_blocked = 0;
#else /* NTVDM */
static unsigned long ticksPerIdealInterval;
static int ticks_blocked = 0;
#endif /* NTVDM */
#ifndef PROD
static char buf[80]; /* Used for tracing messages */
#endif
#ifdef HUNTER /* Only needed for HUNTER */
word timer_batch_count; /* Batch update when PC tick occurs */
#endif
int timer_int_enabled; /* Whether Bios timer ints are required */
/* control word format */
/* Values in D54 of control word - number of bytes to read/load into counter */
#define LATCH 0
#define RL_LSB 1
#define RL_MSB 2
#define RL_LMSB 3
/* Values in D321 of control word - the counter mode. */
#define INT_ON_TERMINALCOUNT 0
#define PROG_ONESHOT 1
#define RATE_GEN 2
#define SQUAREWAVE_GEN 3
#define SW_TRIG_STROBE 4
#define HW_TRIG_STROBE 5
/* NB. 6 = RATE_GEN, 7 = SQUAREWAVE_GEN */
/* Values in D0 of control word - whether prog wants to read/write binary or BCD to counter */
#define BINARY 0
#define BCD 1
#define INDEFINITE (ULONG)-1
#define STARTLO 0
#define STARTHI 1
#define NOREPEAT 0
#define REPEAT ~NOREPEAT
#define WRITE_SIGNAL 1
#define GATE_SIGNAL 2
#define UNLOCKED 0
#define LOCKED ~UNLOCKED
#define STATE_FUNCTION void
#define UNBLOCK_FUNCTION void
#define GATENABLED_FUNCTION void
#define UPDATECOUNTER_FUNCTION void
/*
* Timer read state
*/
#define UNREAD 0 /* Timer is in normal state */
#define READMSB 1 /* First byte of LMSB mode read, but not second yet */
#define READSTATUS 2 /* Status latched, will read it first */
/*
* These two figures give a timer frequency of 1.193 MHz (which is
* how fast the 8235 is clocked. This means that the timer will wrap round
* every 1/18.2th of a second... the same amount of time as the PC tick
* rate. This is not surprizing, as the PC tick rate is controlled by
* timer 0. Every time timer 0 wraps, the PC is interrupted by the timer.
*/
#define TIMER_CLOCK_NUMER 1000
#define TIMER_CLOCK_DENOM 1193
typedef half_word TSIGNAL;
/* the following structure defines an output waveform from a
* timer channel. The waveform consists of 'n' clocks at one
* logic level, and 'm' ticks at the other logic level.
* Which level starts the waveform is given by 'startLogicLevel'.
* e.g; the following waveform ...
*/
typedef struct {
long clocksAtLoLogicLevel;
long clocksAtHiLogicLevel;
long period;
long startLogicLevel;
long repeatWaveForm;
} WAVEFORM;
/* __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
* | | | | |
* |__ __ __| |__ __ __| |
*
* would be described by
* clocksAtLoLogicLevel = 3
* clocksAtHiLogicLevel = 6
* startLogicLevel = STARTHI
* repeatWaveForm = TRUE;
*
*
* The overall state of a counter is represented by the following
* structure. Its contents are described below.
*/
typedef enum trigCond_ {LEVEL, EDGE} trigCond;
typedef enum countload_ {AVAILABLE, USED} countload;
typedef struct {
int m;
int bcd;
int rl;
STATE_FUNCTION (*state) IPT2(int, signal, half_word, value);
STATE_FUNCTION (*statePriorWt) IPT2(int, signal, half_word, value);
STATE_FUNCTION (*stateOnGate) IPT2(int, signal, half_word, value);
UNBLOCK_FUNCTION (*actionOnWtComplete) IPT0();
UNBLOCK_FUNCTION (*actionOnGateEnabled) IPT0();
void (*getTime) IPT1(struct host_timeval *, t);
unsigned char outblsb;
unsigned char outbmsb;
unsigned long initialCount;
int readState;
int countlatched;
unsigned char latchvaluelsb;
unsigned char latchvaluemsb;
unsigned char latchstatus;
word Count;
countload newCount;
word tickadjust;
struct host_timeval activationTime;
int tc;
int freezeCounter;
#ifndef NTVDM
unsigned long lastTicks;
long microtick;
long timeFrig;
word saveCount;
int guessesPerHostTick; /* How often per host tick are we forced to guess? */
int guessesSoFar; /* How many times have we guessed so far? */
#endif
unsigned int delay;
trigCond trigger;
TSIGNAL gate;
TSIGNAL clk;
WAVEFORM out;
} COUNTER_UNIT;
/*
* When the counter is programmed, the 8253 receives a control word.
* (see pg 6-266 of manual). The counter being programmed is specified
* within this word. Provided 'rl' is non-zero, then this counter is
* being reprogrammed, and we remember the values of m (mode), rl
* (control of which bytes are involved in a read or load sequence)
* and bcd (whether the counter is in binary or bcd mode).
* Based on rl, the counter then must receive one or two bytes via
* outb's. Two states are used to accept 'outblsb' or
* 'outbmsb', or both. When the full byte complement has been received,
* 'initialCount' gets set to the value specified by 'outblsb' and 'outbmsb',
* taking account of BCD etc., along with 'Count'.
* 'Count' gets adjusted by the value of 'timeadjust'.
* 'timeadjust' is initialised to zero every time a new mode
* word (non-zero) is received. For certain modes of the counter,
* if they are sent a new count, without receiving a new mode, then
* this will cause the counter to start counting from this new count
* at some stage (based on mode and gate values). SInce we are not
* maintaining the counters continually (rather we prod them as a
* result of io or gate activity) then there is a good chance we
* will be late at resetting a counter for counting again. Hence
* 'timeadjust' is calculated for this lateness, and used as a
* correction factor.
* If a 'latch counter' command ('rl'=0 in command word) is issued, then
* the current counter value is latched into 'latchvaluelsb' and
* 'latchvaluemsb', and the flag 'countlatched' is set non-zero.
* If this flag is non-zero during a counter read, then these latched
* bytes are returned, and upon completion of the read sequence, the
* flag is cleared. If this flag is zero, then the current counter value
* is read, and returned. 'donate' is used to point at the appropriate
* byte to be delivered to the 'inb'.
* when a counter activates (i.e; count begins or continues after
* a gate signal change) a time stamp is taken, to enable a time delta
* to be calculated whenever the counter is read .. this is stored in
* 'activationTime'.
* A state function (state), representing the current state
* of the counter, gets called whenever inb,outb accesses occur, or when
* the ppi's signal TIM2GATESPK changes. reading/writing of the counters
* (as opposed to the control word register) always 'blocks' the current
* state,and puts the counter into a temporary state which handles reading or
* loading the counter. The blocked state is remembered in 'statePriorWt'.
* Once the counter has been loaded or read (as specified by its 'rl'
* parameter) then 'actionOnWtComplete' gets called. Typically this in turn
* reverts the counter back to the state it was in before it became
* blocked.
* If a counter is read, then the function 'updateCounter' gets called to
* determine what the current counter value is.
* If the counter's gate signal is disabling counting, and the counter
* has been fully programmed (and hence able to count), then the counter
* will be in the state 'awaitingGate'. When the appropriate gate signal
* appears (via a ppi call), the counter activates by calling the
* function 'actionOnGateEnabled'. This will take some sort of action, and then
* place the counter into the state 'stateOnGate'.
*
* sending a new count to a counter in modes 2 or 3 will not take
* effect until the end of the current period ... hence 'delay'
* is used as an indicator (for sound logic emulation only) of
* this. If the counter has say 10 clocks left to count down to
* the end of the period when it receives new waveform parameters,
* this information is passed onto the sound logic, with a 'delay'
* of 10. Otherwise 'delay' is not used.
*
* On some operating systems, the real time clock may well have to
* coarse a granularity. If the 8253 is read to quickly, there is a
* very good chance that the OS clock will still be reading the same.
* To cater for this, a frig factor 'microsecs' has been introduced.
* This gets incremented every time the above condition is detected,
* and used as part of the counter update calculations. Whenever
* the OS actually says something sensible, it gets cleared again.
*/
static COUNTER_UNIT timers[3], *pcu;
LOCAL STATE_FUNCTION uninit IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION awaitingGate IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION waitingFor1stWrite IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION waitingFor2ndWrite IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION Counting0 IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION Counting_4_5 IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION Counting1 IPT2(int, signal, half_word, value);
LOCAL STATE_FUNCTION Counting_2_3 IPT2(int, signal, half_word, value);
LOCAL UNBLOCK_FUNCTION resumeCounting_1_5 IPT0();
LOCAL GATENABLED_FUNCTION resumeCounting_2_3_4 IPT0();
LOCAL void resumeAwaitGate IPT0();
LOCAL UNBLOCK_FUNCTION CounterBufferLoaded IPT0();
LOCAL UNBLOCK_FUNCTION timererror IPT0();
LOCAL GATENABLED_FUNCTION runCount IPT0();
LOCAL void controlWordReg IPT1(half_word, cwd);
LOCAL void latchStatusValue IPT0();
LOCAL void readCounter IPT0();
LOCAL void timestamp IPT0();
LOCAL void outputWaveForm IPT5(unsigned int, delay, unsigned long, lowclocks,
unsigned long, hiclocks, int, lohi, int, repeat);
LOCAL void outputHigh IPT0();
LOCAL void outputLow IPT0();
LOCAL void setOutputAfterMode IPT0();
LOCAL void loadCounter IPT0();
LOCAL void updateCounter IPT0();
LOCAL void startCounting IPT0();
#ifdef NTVDM
unsigned long updateCount(void);
#else
LOCAL void updateCount IPT3(unsigned long, ticks, unsigned long *, wrap,
struct host_timeval *, now);
#endif
LOCAL unsigned short bin_to_bcd IPT1(unsigned long, val);
LOCAL word bcd_to_bin IPT1(word, val);
LOCAL void emu_8253 IPT3(io_addr, port, int, signal, half_word, value);
LOCAL void Timer_init IPT0();
LOCAL unsigned long timer_conv IPT1(word, count);
LOCAL void issueIREQ0 IPT1(unsigned int, n);
#ifndef NTVDM
LOCAL unsigned long guess IPT0();
LOCAL void throwaway IPT0();
#endif
#ifdef SYNCH_TIMERS
GLOBAL void IdealTimeInit IPT0();
#else
LOCAL void IdealTimeInit IPT0();
#endif
LOCAL void updateIdealTime IPT0();
LOCAL void getIdealTime IPT1(struct host_timeval *, t);
#ifndef NTVDM
LOCAL void getHostSysTime IPT1(struct host_timeval *, t);
LOCAL void checktimelock IPT0();
#endif
LOCAL void setTriggerCond IPT0();
LOCAL void WtComplete IPT0();
LOCAL void counter_init IPT1(COUNTER_UNIT *, p);
#ifndef NTVDM
LOCAL void setLastWrap IPT2(unsigned int, nclocks, struct host_timeval *, now);
LOCAL void timer_generate_int IPT1(long, n);
LOCAL void timer_multiple_ints IPT1(long, n);
#define MAX_BACK_SECS 15
LOCAL IU32 max_backlog = 0; /* max # of ints allowed to queue up */
IBOOL active_int_event = FALSE; /* current quick_event for timer queue */
IU32 more_timer_mult = 0; /* additions to timer int queue */
IU32 timer_multiple_delay = 0; /* us delay to next timer queue elem */
#endif
#if defined(NTVDM)
void timer_generate_int(void);
unsigned long clocksSinceCounterUpdate(struct host_timeval *pCuurTime,
struct host_timeval *pLastTime,
word *pCounter);
void ReinitIdealTime IPT1(struct host_timeval *, t);
void host_GetSysTime(struct host_timeval *time);
void InitPerfCounter(void);
/* holds real time values for counter zero */
struct host_timeval LastTimeCounterZero;
word RealTimeCountCounterZero;
#ifndef PROD
ULONG NtTicTesting = 0; /* tracing specific to NT port */
ULONG TicsGenerated;
ULONG TicsReceived;
#endif
/* for optimizing timer hardware interrupt generation */
word TimerInt08Seg = TIMER_INT_SEGMENT;
word TimerInt08Off = TIMER_INT_OFFSET;
word TimerInt1CSeg;
word TimerInt1COff;
#else
static int timelock; /* locks out time_tick if set */
static int needtick; /* causes time_tick() to be called if set */
/*
* Data for the hack to make sure that windows in standard mode doesn't get two timer
* ticks too close together.
*/
LOCAL BOOL hack_active=FALSE; /* This boolean indicates that we are spacing
timer interrupts out by discarding timer ticks.
It is set when we see a protected mode tick */
LOCAL BOOL too_soon_after_previous=FALSE; /* This boolean is set on when an interrupt is
generated... a quick event is requested to
clear it again after a "fixed" number of
instructions */
LOCAL BOOL ticks_lost_this_time=FALSE; /* This boolean is set if any interrupts were
required to be generated while too_soon_after_previous
was TRUE - if it's TRUE when too_soon_after_previous
is being set to FALSE, we generate an immediate
interrupt to get the best responsiveness */
LOCAL ULONG real_mode_ticks_in_a_row = 0; /* A count of the number of real mode ticks in a row...
this is used to disable the hack again when we have
left protected mode for a good while */
LOCAL ULONG instrs_per_tick = 37000; /* Nominal (as timed on the reference machine - a SPARC 1+) */
LOCAL ULONG adj_instrs_per_tick = 0; /* The estimated number of Intel instructions being emulated
each 20th of a second */
LOCAL ULONG n_rm_instrs_before_full_speed = 3000000;/* Nominal number of instructions to be emulated in real mode
before we're convinced that we're staying back in real mode */
LOCAL ULONG adj_n_real_mode_ticks_before_full_speed = 0;/* The value which real_mode_ticks_in_a_row must reach
before the hack is disabled */
#ifndef PROD
LOCAL ULONG ticks_ignored = 0; /* For information purposes only. */
#endif
#endif /* NTVDM */
#ifndef PROD /* Specific Timer change tracing that isn't timer_verbose */
GLOBAL int DoTimerChangeTracing = 0;
#endif
#if !defined(NTVDM)
#if defined(IRET_HOOKS) && defined(GISP_CPU)
extern IBOOL HostDelayTimerInt IPT1(int, numberInts);
extern IBOOL HostPendingTimerInt IPT0();
extern void HostResetTimerInts IPT0();
#endif /* IRET_HOOKS && GISP_CPU */
#endif
/*
* ============================================================================
* External variables
* ============================================================================
*/
boolean timer_video_enabled;
/*
* Table of function pointers to access TIMER routines
*/
void (*timer_inb_func) IPT2(io_addr, port, half_word *, value);
void (*timer_outb_func) IPT2(io_addr, port, half_word, value);
void (*timer_gate_func) IPT2(io_addr, port, half_word, value);
void (*timer_tick_func) IPT0();
/*
* ============================================================================
* External functions
* ============================================================================
*/
void
SWTMR_init_funcptrs IFN0()
{
/*
* initialize access functions for SW [emulated] TIMER
*/
timer_inb_func = SWTMR_inb;
timer_outb_func = SWTMR_outb;
timer_gate_func = SWTMR_gate;
timer_tick_func = SWTMR_time_tick;
#ifndef NTVDM
/*
* initialise stuff for PM timer hack
*/
too_soon_after_previous = FALSE;
if (adj_n_real_mode_ticks_before_full_speed == 0){
HOST_TIMER_DELAY_SIZE=25000;
adj_instrs_per_tick = host_speed (instrs_per_tick);
adj_n_real_mode_ticks_before_full_speed = n_rm_instrs_before_full_speed / adj_instrs_per_tick;
}
#endif
}
void
SWTMR_time_tick IFN0()
{
/*
* Give the idle detector a chance to see if we are really idle.
*/
#ifndef NTVDM
/* this is done in Nt's host heartbeat */
IDLE_tick();
#endif
/*
* Interrupt routine - called from time_strobe event/signal handler.
* That is called from host_timer_event
*/
#ifndef NTVDM
/* protect ourselves from shafting a timer-channel in midst-program
* ... the lock is controlled by timer_inb(),_outb() and _gate()
*/
if (timelock == LOCKED)
{
needtick = ~0;
return;
}
#endif
/* ideal time goes up by the number of timer clocks
* equivalent to the interval between calls to 'time_tick'
* (we assume these calls appear at perfectly spaced intervals ...
* thus ignoring fluctuations on the host system)
* The idea is to give an illusion of constant ticks here
*/
updateIdealTime();
pcu = &timers[0];
updateCounter();
/*
* Counter 2 is only used for sound, or for timing very short
* times. For timing short times (~100ms) we need accurate time
* all the time.
*/
pcu = &timers[2];
updateCounter();
}
/*
* The timer low I/O functions that support the Intel 8253 Counter chip.
*
* The Counters are used by the PC in the following ways:
*
* Counter 0 - Bios time of day function
* Counter 1 - Handler memory refresh
* Counter 2 - Drive Speaker Interface (plus input on PPI)
*
*/
/*
* These control the timer chip. they are the interface between the
* CPU and the timer chip.
*/
void SWTMR_gate IFN2(io_addr, port, half_word, value)
{
#ifdef NTVDM
host_ica_lock();
#else
timelock = LOCKED;
#endif
emu_8253 (port, GATE_SIGNAL, value);
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
{
sprintf(buf, "timer_gate() - sending %d to port 0x%x", value, port);
trace(buf, DUMP_REG);
}
#endif
#ifdef NTVDM
host_ica_unlock();
#else
checktimelock();
#endif
}
void SWTMR_inb IFN2(io_addr, port, half_word *, value)
{
#ifdef NTVDM
host_ica_lock();
#else
timelock = LOCKED;
#endif
pcu = &timers[port & 3];
if (!pcu->countlatched)
readCounter();
switch (pcu->readState)
{
case UNREAD:
switch (pcu->rl)
{
case RL_LSB:
*value = pcu->latchvaluelsb;
pcu->countlatched = 0; /* Unlatch the value read by inb() */
break;
case RL_LMSB:
*value = pcu->latchvaluelsb;
pcu->readState = READMSB; /* Read LSB, next in read MSB. */
break;
case RL_MSB:
*value = pcu->latchvaluemsb;
pcu->countlatched = 0; /* Unlatch the value read by inb() */
break;
}
break;
case READMSB:
*value = pcu->latchvaluemsb;
pcu->countlatched = 0; /* Unlatch the value read by inb() */
pcu->readState = UNREAD; /* Read MSB, back to unread state. */
break;
case READSTATUS:
*value = pcu->latchstatus;
pcu->readState = UNREAD;
break;
}
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
{
sprintf(buf, "timer_inb() - Returning %d(0x%x) for port 0x%x", *value, *value, port);
trace(buf, DUMP_REG);
}
#endif
#ifdef NTVDM
host_ica_unlock();
#else
checktimelock();
#endif
}
void SWTMR_outb IFN2(io_addr, port, half_word, value)
{
#if defined(NTVDM) || defined(GISP_SVGA)
if (port == 0x4f) /* dead port used by PS/2 XGA bios for DAC delays */
return;
#ifdef NTVDM
host_ica_lock();
#endif
#else
timelock = LOCKED;
#endif
port = port & TIMER_BIT_MASK;
if(port == 0x43)
controlWordReg(value);
else
emu_8253 (port, WRITE_SIGNAL, value);
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
{
sprintf(buf, "timer_outb() - Value %d to port 0x%x", value, port);
trace(buf, DUMP_REG);
}
#endif
#ifdef NTVDM
host_ica_unlock();
#else
checktimelock();
#endif
}
/* --------------------------------------------------------------------------- */
/* */
/* Return the current DOS tick value based on Timer 0 */
/* */
/* --------------------------------------------------------------------------- */
#ifndef NTVDM
GLOBAL ULONG get_DOS_ticks IFN0()
{
return( timers[0].Count );
}
#endif /* NTVDM */
#ifdef NTVDM
/*
* called by host to update the current ideal time
* after a block and resume.
*/
void ReinitIdealTime IFN1(struct host_timeval *, t)
{
/*
* Currently these are all time stamped by the same thing!
*/
LastTimeCounterZero =
timers[2].activationTime =
timers[1].activationTime =
timers[0].activationTime =
idealtime = *t;
/*
* Clear out extra pending interrupts
*/
if (EoiPending) {
EoiPending = 1;
}
EoiIntsPending = 0;
}
#endif
/* **************************************************************************** */
/* **************************************************************************** */
/* **************************************************************************** */
#ifndef NTVDM
/* check whether a time lock (set while application io is being serviced)
* has blocked out an alarm signal ... call time_tick() to catch up if so
* ... (time_tick() sets timelock = ~0, and just returns, if it sees the time
* lock set
*/
LOCAL void checktimelock IFN0()
{
timelock = UNLOCKED;
if (needtick)
{
needtick = 0;
time_tick();
}
}
#endif
/*
* emulate the 8253 chip.
*
* emu_8253(port,signal,value)
*
* port - port address being accessed
* (port & 3) gives A0,A1 lines
*
* signal - WRITE_SIGNAL (outb) or
* GATE_SIGNAL (ppi TIM2GATESPK change)
*
* value -
* for WRITE_SIGNAL, value = byte being written to chip
* for GATE_SIGNAL, value = GATE_SIGNAL_LOW or
* GATE_SIGNAL_RISE
*/
LOCAL void emu_8253 IFN3(io_addr, port, int, signal, half_word, value)
{
int A0A1;
/* get address lines A0 and A1 */
A0A1 = port & 3;
/* handle the access */
pcu = &timers[A0A1];
(pcu->state)(signal,value);
}
/* if a timer channel is unitialised, this is its associated
* state function ... simply ignore all accesses in this state
* A state transition from this state is only possible via
* the procedure controlReg()
*/
/* handle write access to the control word register.
* The documentation does not specify what happens if a mode
* setting control word is issued while a counter is active.
* In this model, we assume it resets the operation back to
* the start of a new counter programming sequence
*/
/*ADE*/
#define SELECT_0 0x00
#define SELECT_1 0x01
#define SELECT_2 0x02
#define READ_BACK 0x03
LOCAL void controlWordReg IFN1(half_word, cwd)
{
int rl,m,channel;
half_word select_bits;
/* decode control word */
channel = (cwd & 0xc0) >> 6;
if(channel == READ_BACK)
{
/* decode read back command */
select_bits = (cwd & 0xe) >> 1;
/* first look for counters to latch */
if (!(cwd & 0x20))
{
/* count bit low so latch selected counters */
if (select_bits & 0x01)
{
/* counter 0 */
pcu = &timers[0];
readCounter();
pcu->countlatched = 1;
}
if (select_bits & 0x02)
{
/* counter 1 */
pcu = &timers[1];
readCounter();
pcu->countlatched = 1;
}
if (select_bits & 0x04)
{
/* counter 2 */
pcu = &timers[2];
readCounter();
pcu->countlatched = 1;
}
}
/* now look for the status latch */
if (!(cwd & 0x10))
{
/* status bit low - status to be latched */
if (select_bits & 0x01)
{
/* counter 0 */
pcu = &timers[0];
latchStatusValue();
}
if (select_bits & 0x02)
{
/* counter 1 */
pcu = &timers[1];
latchStatusValue();
}
if (select_bits & 0x04)
{
/* counter 2 */
pcu = &timers[2];
latchStatusValue();
}
}
}
else
{
pcu = &timers[channel];
rl = (cwd & 0x30) >> 4;
/* are we simply latching the present count value, or are
* programming up a new mode ??
*/
if (rl == LATCH)
{ /* latching present count value */
readCounter();
pcu->countlatched = 1;
return;
}
else
{ /* new mode */
if (pcu == &timers[0])
timer_int_enabled = FALSE;
pcu->countlatched = 0;
pcu->tc = 0;
pcu->tickadjust = 0;
#ifndef NTVDM
pcu->microtick = 0;
pcu->saveCount = 0;
#endif
m = (cwd & 0xe) >> 1;
if(m > 5)m -= 4; /* Modes 6 and 7 don't exist - they are intepreted as modes 2 and 3 */
pcu->m = m;
setTriggerCond();
setOutputAfterMode();
pcu->bcd = cwd & 1;
pcu->rl = rl;
pcu->actionOnWtComplete = CounterBufferLoaded;
pcu->actionOnGateEnabled = CounterBufferLoaded;
pcu->statePriorWt = pcu->state;
pcu->state = waitingFor1stWrite;
}
}
}
/* latch status ready for reading */
LOCAL void latchStatusValue IFN0()
{
/*
* Status byte is of format :
*
* |OUT|Null Count|RW1|RW0|M2|M1|M0|BCD|
*
*/
/* NULL COUNT still only approximated. Who cares? */
pcu->latchstatus =
(pcu->out.startLogicLevel<<7)
| (pcu->newCount == AVAILABLE ? (1<<6) : 0)
| (pcu->rl<<4)
| (pcu->m<<1) | (pcu->bcd);
pcu->readState = READSTATUS;
}
/* set up flag establishing type of trigger condition for
* counter bassed on its mode
*/
LOCAL void setTriggerCond IFN0()
{
switch (pcu->m)
{
case RATE_GEN:
case SQUAREWAVE_GEN:
case SW_TRIG_STROBE:
case INT_ON_TERMINALCOUNT:
pcu->trigger = LEVEL;
return;
case PROG_ONESHOT:
case HW_TRIG_STROBE:
pcu->trigger = EDGE;
return;
}
}
/* transfer count buffer into counter */
LOCAL void loadCounter IFN0()
{
unsigned long modulo;
#ifndef NTVDM
IU32 maxback;
#endif
/* set counter */
/* get correct modulo to use for counter calculations */
modulo = (pcu->outbmsb << 8) | pcu->outblsb;
if (pcu->bcd == BCD)
{
if(modulo)
modulo = bcd_to_bin((word)modulo);
else
modulo = 10000L;
}
else
if(!modulo)modulo = 0x10000L;
/* Beware - Count and initialCount are different sizes, so don't merge the next two lines!! */
pcu->initialCount = modulo;
pcu->Count = (word)modulo;
/*
* not at terminal count anymore, so reflect this fact by resetting
* tc (which I think means "reached terminal count"
*/
pcu->tc = 0;
pcu->newCount = USED;
if(pcu == &timers[0])
{
/* Get rid of pending interrupts - these may no longer me appropriate - eg. in Sailing */
ica_hw_interrupt_cancel(ICA_MASTER,CPU_TIMER_INT);
#ifdef NTVDM
RealTimeCountCounterZero = pcu->Count;
#endif
#ifndef NTVDM
/* how many interrupts in MAX_BACK_SECS seconds? */
maxback = (1193180 * MAX_BACK_SECS) / modulo;
if (maxback > max_backlog)
{
#ifndef PROD
fprintf(trace_file, "setting max backlog to %d\n", maxback);
#endif
max_backlog = maxback;
}
#endif
}
#if defined(NTVDM) && !defined(PROD)
if (NtTicTesting) {
printf("Timer %d modulo=%lu %dHz\n",
pcu-timers, modulo, 1193180/modulo);
}
#endif /*NTVDM & !PROD*/
}
/* read counter into latch, ready for next read */
LOCAL void readCounter IFN0()
{
int countread;
updateCounter();
#ifdef NTVDM
/*
* Timer Zero is a special case, as it is maintaned
* by IdealInterval, and not RealTime. We must give
* real time granularity
*/
countread = pcu == &timers[0] ? RealTimeCountCounterZero
: pcu->Count;
if (pcu->bcd == BCD)
countread = bin_to_bcd(countread);
#else
if(pcu->bcd == BCD)
countread = bin_to_bcd(pcu->Count);
else
countread = pcu->Count;
#endif
pcu->latchvaluemsb = countread >> 8;
pcu->latchvaluelsb = countread & 0xff;
sure_note_trace1(TIMER_VERBOSE,"reading count %d",pcu->Count);
}
/* active counter (mode 0) lost its gate ... gate has now
* reappeared. Either continue 'active' counting (pre terminal count)
* or continue decrementing, but with OUT signal at high indefinitely.
*/
LOCAL GATENABLED_FUNCTION resumeCounting0onGate IFN0()
{
if (pcu->freezeCounter)
{
pcu->freezeCounter = 0;
timestamp();
}
if (pcu->newCount == AVAILABLE)
loadCounter();
if (!pcu->tc)
{
timestamp();
pcu->stateOnGate = Counting0;
runCount();
}
else
pcu->state = Counting0;
}
LOCAL GATENABLED_FUNCTION resumeCounting0 IFN0()
{
int doadjust = 0;
if (pcu->freezeCounter)
{
pcu->freezeCounter = 0;
timestamp();
}
if (pcu->newCount == AVAILABLE)
{
doadjust = 1;
loadCounter();
}
if (!pcu->tc)
{
pcu->stateOnGate = Counting0;
runCount();
}
else
{
pcu->state = Counting0;
if (doadjust)
pcu->Count -= pcu->tickadjust;
}
}
LOCAL GATENABLED_FUNCTION resumeCounting_2_3_4_onGate IFN0()
{
/* for modes 2 and 3, ought to wait until counter
* completes its current period, but we cant be as accurate
* as that
*/
if (pcu->newCount == AVAILABLE)
loadCounter();
if (pcu->m == RATE_GEN || pcu->m == SQUAREWAVE_GEN)
pcu->stateOnGate = Counting_2_3;
else
pcu->stateOnGate = Counting_4_5;
timestamp();
runCount();
}
LOCAL GATENABLED_FUNCTION resumeCounting_2_3_4 IFN0()
{
/* for modes 2 and 3, ought to wait until counter
* completes its current period, but we cant be as accurate
* as that
*/
if (pcu->newCount == AVAILABLE)
{
pcu->delay = pcu->Count;
loadCounter();
}
if (pcu->m == RATE_GEN || pcu->m == SQUAREWAVE_GEN)
pcu->stateOnGate = Counting_2_3;
else
pcu->stateOnGate = Counting_4_5;
runCount();
}
LOCAL GATENABLED_FUNCTION runCount IFN0()
{
unsigned long lowticks, hiticks;
unsigned long adjustedCount; /* For count = 0 and BCD */
adjustedCount = timer_conv(pcu->Count);
pcu->state = pcu->stateOnGate;
switch (pcu->m)
{
case INT_ON_TERMINALCOUNT:
outputWaveForm(pcu->delay,adjustedCount,
INDEFINITE,STARTLO,NOREPEAT);
return;
case PROG_ONESHOT:
loadCounter();
outputWaveForm(pcu->delay,adjustedCount,
INDEFINITE,STARTLO,NOREPEAT);
pcu->Count -= pcu->tickadjust;
return;
case RATE_GEN:
loadCounter();
outputWaveForm(pcu->delay,1,
adjustedCount-1,STARTHI,REPEAT);
pcu->Count -= pcu->tickadjust;
return;
case SQUAREWAVE_GEN:
loadCounter();
if (!(pcu->Count & 1))
lowticks = hiticks = adjustedCount >> 1;
else
{
lowticks = (adjustedCount - 1) >> 1;
hiticks = (adjustedCount + 1) >> 1;
}
outputWaveForm(pcu->delay,lowticks, hiticks,STARTHI,REPEAT);
pcu->Count -= pcu->tickadjust;
return;
case SW_TRIG_STROBE:
outputWaveForm(pcu->delay,1, adjustedCount,STARTHI,NOREPEAT);
return;
case HW_TRIG_STROBE:
loadCounter();
outputWaveForm(pcu->delay,1, adjustedCount,STARTHI,NOREPEAT);
return;
}
}
/* return to state waiting for gate signal */
LOCAL void resumeAwaitGate IFN0()
{
pcu->actionOnWtComplete = timererror;
pcu->state = awaitingGate;
awaitingGate(GATE_SIGNAL,pcu->gate);
}
/* ========================= OUTPUT SIGNAL UTILITIES ====================== */
/* ========================= OUTPUT SIGNAL UTILITIES ====================== */
/* ========================= OUTPUT SIGNAL UTILITIES ====================== */
/* set state of output signal after a mode command has
* been programmed (see pages 6-266 - 6-268 of Intel manual 231306-001)
*/
LOCAL void setOutputAfterMode IFN0()
{
switch (pcu->m)
{
case INT_ON_TERMINALCOUNT:
outputLow( /*INDEFINITE*/ );
return;
case PROG_ONESHOT:
case RATE_GEN:
case SQUAREWAVE_GEN:
case SW_TRIG_STROBE:
case HW_TRIG_STROBE:
outputHigh( /*INDEFINITE*/ );
return;
}
}
/* set output state low ... inform sound chip emulation if
* channel 2
*/
LOCAL void outputLow IFN0()
{
outputWaveForm(0,INDEFINITE,0,STARTLO,NOREPEAT);
}
/* set output state high ... inform sound chip emulation if
* channel 2
*/
LOCAL void outputHigh IFN0()
{
outputWaveForm(0,0,INDEFINITE,STARTHI,NOREPEAT);
}
/* when the wave form is deterministic, tell the sound emulation about it.
* delay - if <>0, don't start this waveform for
* this number of counter clocks.
* lowclocks - the #.counter clocks to stay low for
* hiclocks - the #.counter clocks to stay high for
* (either parameter may be INDEFINITE duration)
* lohi - 0 ==> start at low logic level
* - <>0 ==> start at high logic level
* repeat - 0 ==> don't
* <>0 ==> repeat.
*
* (n.b; 1 counter clock period = 1.19318 usecs)
*/
LOCAL void outputWaveForm IFN5(unsigned int, delay, unsigned long, lowclocks,
unsigned long, hiclocks, int, lohi, int, repeat)
{
#ifdef DOCUMENTATION
int ch;
#endif /* DOCUMENTATION */
pcu->out.startLogicLevel = lohi;
pcu->out.repeatWaveForm = repeat;
pcu->out.clocksAtLoLogicLevel = lowclocks;
pcu->out.clocksAtHiLogicLevel = hiclocks;
if (repeat == REPEAT)
pcu->out.period = lowclocks + hiclocks;
if (pcu == &timers[2])
{
host_timer2_waveform(delay,lowclocks,hiclocks,lohi,repeat);
}
pcu->delay = 0;
#ifdef DOCUMENTATION
if (pcu==&timers[0])
ch = 0;
if (pcu==&timers[1])
ch = 1;
if (pcu==&timers[2])
ch = 2;
sprintf(buf,"ch.%d waveform:delay %d lo %d hi %d lohi %d repeat %d\n",
ch,delay,lowclocks,hiclocks,lohi,repeat);
trace(buf,0);
#endif /* DOCUMENTATION */
}
/* time stamp the counter unit ... it is counting from this time
*/
LOCAL void timestamp IFN0()
{
#ifdef NTVDM
/* update counter zero time stamp */
if (pcu == &timers[0]) {
host_GetSysTime(&LastTimeCounterZero);
}
#else
/* Initialise lastTicks before referencing it in updateCount() */
/* Makes Norton SYSINFO version 5.0 work on fast (HP) machines */
pcu->lastTicks = 0 ;
#endif
/* Go and get the time since it was activated */
(*pcu->getTime)(&pcu->activationTime);
}
LOCAL UNBLOCK_FUNCTION timererror IFN0()
{
always_trace0("time error!!!!");
}
/* *************** COUNTER UPDATING FUNCTIONS FOR NON_IDLE COUNTERS **********/
/* *************** COUNTER UPDATING FUNCTIONS FOR NON_IDLE COUNTERS **********/
/* *************** COUNTER UPDATING FUNCTIONS FOR NON_IDLE COUNTERS **********/
/* ************************ STATE FUNCTIONS ********************************* */
/* ************************ STATE FUNCTIONS ********************************* */
/* ************************ STATE FUNCTIONS ********************************* */
/*
* STATE_FUNCTION uninit();
* STATE_FUNCTION awaitingGate();
* STATE_FUNCTION waitingFor1stWrite();
* STATE_FUNCTION waitingFor2ndWrite();
* STATE_FUNCTION Counting0();
* STATE_FUNCTION Counting_4_5();
* STATE_FUNCTION Counting1();
* STATE_FUNCTION Counting_2_3();
*/
LOCAL STATE_FUNCTION uninit IFN2(int, signal, half_word, value)
{
if (signal == GATE_SIGNAL)
pcu->gate = value;
}
LOCAL STATE_FUNCTION awaitingGate IFN2(int, signal, half_word, value)
{
switch (signal)
{
case GATE_SIGNAL:
pcu->gate = value;
if (value == GATE_SIGNAL_LOW)
return;
/* this is pathological ... should never have to
* wait for gate for channel 0
*/
if (pcu == &timers[0])
timer_int_enabled = TRUE;
(pcu->actionOnGateEnabled)();
return;
case WRITE_SIGNAL:
pcu->actionOnWtComplete = resumeAwaitGate;
pcu->statePriorWt = pcu->state;
waitingFor1stWrite(signal,value);
return;
}
}
/*
* Perform first of (probably) 2 writes.
* This is either called directly when some other state is
* written to, or set up as the current state when the timer mode is changed.
* If the timer is in 'read/write 2 bytes' mode, set the timer state
* to 'waiting for second byte'.
*/
LOCAL STATE_FUNCTION waitingFor1stWrite IFN2(int, signal, half_word, value)
{
switch (signal)
{
case GATE_SIGNAL:
/* remember gate signal state */
pcu->gate = value;
return;
case WRITE_SIGNAL:
switch (pcu->rl)
{
case RL_LSB:
pcu->outblsb = value;
/* Zero the most signifcant byte. */
pcu->outbmsb = 0;
pcu->newCount = AVAILABLE;
WtComplete();
return;
case RL_LMSB:
pcu->outblsb = value;
pcu->state = waitingFor2ndWrite;
return;
case RL_MSB:
pcu->outbmsb = value;
/* Zero the least signifcant byte. */
pcu->outblsb = 0;
pcu->newCount = AVAILABLE;
WtComplete();
return;
}
}
}
/*
* Write second byte to timer and unblock it.
*/
LOCAL STATE_FUNCTION waitingFor2ndWrite IFN2(int, signal, half_word, value)
{
switch (signal)
{
case GATE_SIGNAL:
/* remember gate signal state */
pcu->gate = value;
return;
case WRITE_SIGNAL:
pcu->newCount = AVAILABLE;
pcu->outbmsb = value;
WtComplete();
return;
}
}
/*
* the full complement of bytes has been read/loaded. During this
* phase, the gate signal might have been removed ... if so,
* change state to wait for an enabling gate signal. Otherwise
* take appropriate action to get back to previous state
*/
LOCAL void WtComplete IFN0()
{
if (pcu->gate == GATE_SIGNAL_LOW && pcu->trigger == LEVEL)
{
pcu->state = awaitingGate;
awaitingGate(GATE_SIGNAL, pcu->gate);
}
else
(pcu->actionOnWtComplete)();
}
/* active counter (Interrupt on Terminal Count)
* if the gate is lost, then
* set the output indefinitely high if at terminal count, or
* indefinitely low if still counting (i.e; extend current low
* level signal duration).
* if count reprogrammed during this time, this new count will be
* used on next trigger (gate).
* else
* if new count programmed, stop counter on receiving 1st byte.
* start new count on second byte. (done by resumeCounting0())
*/
LOCAL STATE_FUNCTION Counting0 IFN2(int, signal, half_word, value)
{
pcu->actionOnGateEnabled = resumeCounting0onGate;
pcu->actionOnWtComplete = resumeCounting0;
switch (signal)
{
case GATE_SIGNAL:
if (value == GATE_SIGNAL_HIGH)
return;
/* we're about to freeze timer channel ...
* get an up to date count. This might change the
* state of the counter.
*/
updateCounter();
pcu->gate = value;
if (pcu->tc)
outputHigh();
else
outputLow();
pcu->state = awaitingGate;
return;
case WRITE_SIGNAL:
pcu->freezeCounter = 1;
updateCounter();
if (pcu->tc)
outputHigh();
else
outputLow();
pcu->statePriorWt = pcu->state;
waitingFor1stWrite(signal,value);
return;
}
}
/* active counter (programmable One-Shot)
* if cvounter loses its gate, then simply wait for retrigger
* to start off count again.
*/
LOCAL STATE_FUNCTION Counting1 IFN2(int, signal, half_word, value)
{
pcu->actionOnGateEnabled = startCounting;
pcu->actionOnWtComplete = resumeCounting_1_5;
switch (signal)
{
case GATE_SIGNAL:
/* ignore transition to low on trigger.
* any rising edge retriggers the counter.
*/
if (value == GATE_SIGNAL_LOW)
return;
pcu->gate = GATE_SIGNAL_HIGH;
pcu->stateOnGate = Counting1;
timestamp();
runCount();
return;
case WRITE_SIGNAL:
pcu->statePriorWt = pcu->state;
waitingFor1stWrite(signal,value);
return;
}
}
LOCAL UNBLOCK_FUNCTION resumeCounting_1_5 IFN0()
{
/* if terminal count has been reached, wait for the next
* trigger ... any new count value programmed will be used
* then.
* Otherwise, even if new count is available, it still won't
* be used until next trigger
*/
if (pcu->gate == GATE_SIGNAL_RISE)
{
pcu->state = Counting1;
if (pcu->m == HW_TRIG_STROBE)
pcu->state = Counting_4_5;
return;
}
if (pcu->tc)
pcu->state = awaitingGate;
else
{
pcu->state = Counting1;
if (pcu->m == HW_TRIG_STROBE)
pcu->state = Counting_4_5;
}
}
LOCAL STATE_FUNCTION Counting_2_3 IFN2(int, signal, half_word, value)
{
pcu->actionOnGateEnabled = resumeCounting_2_3_4_onGate;
pcu->actionOnWtComplete = resumeCounting_2_3_4;
switch (signal)
{
case GATE_SIGNAL:
if (value == GATE_SIGNAL_HIGH)
return;
/* we're about to freeze timer channel ...
* get an up to date count. This might change the
* state of the counter.
*/
updateCounter();
pcu->gate = value;
outputHigh();
pcu->state = awaitingGate;
return;
case WRITE_SIGNAL:
pcu->statePriorWt = pcu->state;
waitingFor1stWrite(signal,value);
return;
}
}
LOCAL STATE_FUNCTION Counting_4_5 IFN2(int, signal, half_word, value)
{
pcu->actionOnGateEnabled = resumeCounting_2_3_4_onGate;
pcu->actionOnWtComplete = resumeCounting_2_3_4;
if (pcu->m == HW_TRIG_STROBE)
{
pcu->actionOnGateEnabled = resumeCounting_1_5;
pcu->actionOnWtComplete = resumeCounting_1_5;
}
switch (signal)
{
case GATE_SIGNAL:
if (value == GATE_SIGNAL_HIGH)
return;
/* we're about to freeze timer channel ...
* get an up to date count. This might change the
* state of the counter.
*/
updateCounter();
pcu->gate = value;
outputHigh();
pcu->state = awaitingGate;
return;
case WRITE_SIGNAL:
pcu->statePriorWt = pcu->state;
waitingFor1stWrite(signal,value);
return;
}
}
/* ****************** UNBLOCK FUNCTIONS ************************************* */
/* ****************** UNBLOCK FUNCTIONS ************************************* */
/* ****************** UNBLOCK FUNCTIONS ************************************* */
/* upon reaching this state, the timer's count register can be
* loaded (as per 'rl') ... and it can potentially start counting
* depending upon the state of its gate signal.
* If it can begin counting, then setup the output waveform that will
* appear at the timer channel's OUT signal.
* (If this channel is for sound, the waveform is exactly known)
*/
LOCAL UNBLOCK_FUNCTION CounterBufferLoaded IFN0()
{
unsigned long lowticks, hiticks, adjustedCount;
pcu->actionOnWtComplete = timererror;
loadCounter();
#ifdef DOCUMENTATION
/*
* Output state of timer if tracing.
* Currently dumpCounter has no effect, so just leave this in
* case anyone wants to implement it properly.
*/
if (io_verbose & TIMER_VERBOSE)
{
dumpCounter();
}
#endif /* DOCUMENTATION */
if (pcu->gate != GATE_SIGNAL_LOW)
{
if (pcu == &timers[0])
timer_int_enabled = TRUE;
timestamp();
adjustedCount = timer_conv(pcu->Count);
switch (pcu->m)
{
case INT_ON_TERMINALCOUNT:
outputWaveForm(pcu->delay,adjustedCount,
INDEFINITE,STARTLO,NOREPEAT);
pcu->Count -= pcu->tickadjust;
pcu->state = Counting0;
return;
case PROG_ONESHOT:
outputWaveForm(pcu->delay,adjustedCount,
INDEFINITE,STARTLO,NOREPEAT);
pcu->Count -= pcu->tickadjust;
pcu->state = Counting1;
return;
case RATE_GEN:
outputWaveForm(pcu->delay,1,
adjustedCount-1,STARTHI,REPEAT);
pcu->Count -= pcu->tickadjust;
pcu->state = Counting_2_3;
return;
case SQUAREWAVE_GEN:
if (!(pcu->Count & 1))
lowticks = hiticks = adjustedCount >> 1;
else
{
lowticks = (adjustedCount - 1) >> 1;
hiticks = (adjustedCount + 1) >> 1;
}
outputWaveForm(pcu->delay,lowticks,
hiticks,STARTHI,REPEAT);
pcu->Count -= pcu->tickadjust;
pcu->state = Counting_2_3;
return;
case SW_TRIG_STROBE:
case HW_TRIG_STROBE:
outputWaveForm(pcu->delay,1,
adjustedCount,STARTHI,NOREPEAT);
pcu->Count -= pcu->tickadjust;
pcu->state = Counting_4_5;
return;
}
}
else
if (pcu == &timers[0])
timer_int_enabled = FALSE;
pcu->state = awaitingGate;
pcu->actionOnGateEnabled = startCounting;
switch (pcu->m)
{
case INT_ON_TERMINALCOUNT:
pcu->stateOnGate = Counting0;
return;
case PROG_ONESHOT:
pcu->stateOnGate = Counting1;
return;
case RATE_GEN:
case SQUAREWAVE_GEN:
pcu->stateOnGate = Counting_2_3;
return;
case SW_TRIG_STROBE:
case HW_TRIG_STROBE:
pcu->stateOnGate = Counting_4_5;
return;
}
}
LOCAL void startCounting IFN0()
{
timestamp();
runCount();
}
#ifndef NTVDM
/* calculate the number of 8253 clocks elapsed since counter was last
* activated
*/
LOCAL unsigned long clocksSinceCounterActivated IFN1(struct host_timeval *, now)
{
struct host_timeval *first;
register unsigned long usec_val, nclocks;
register unsigned int secs;
first = &pcu->activationTime;
(*pcu->getTime)(now);
/* calculate #.usecs elapsed */
secs = (int)(now->tv_sec - first->tv_sec);
switch (secs)
{
case 0: usec_val = now->tv_usec - first->tv_usec;
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
if ( usec_val == 0 )
trace("clocksSinceCounterActivated() == 0 !", 0);
#endif
nclocks = (usec_val * TIMER_CLOCK_DENOM) / TIMER_CLOCK_NUMER;
break;
case 1: usec_val = 1000000L + now->tv_usec - first->tv_usec;
nclocks = (usec_val * TIMER_CLOCK_DENOM) / TIMER_CLOCK_NUMER;
break;
default:
nclocks = ((now->tv_usec - first->tv_usec) * TIMER_CLOCK_DENOM) / TIMER_CLOCK_NUMER;
nclocks += secs * (1000000L * TIMER_CLOCK_DENOM / TIMER_CLOCK_NUMER);
#ifndef PROD
if (io_verbose & TIMER_VERBOSE) {
sprintf(buf, "timer[%d]: %d seconds have passed!", pcu-timers, secs);
trace(buf, DUMP_NONE);
}
#endif
break;
}
return nclocks;
}
#endif
LOCAL void updateCounter IFN0()
{
#ifndef NTVDM
unsigned long nticks;
struct host_timeval now;
#endif /* NTVDM */
unsigned long wrap;
switch (pcu->m)
{
case INT_ON_TERMINALCOUNT:
case RATE_GEN:
case SQUAREWAVE_GEN:
if (pcu->gate == GATE_SIGNAL_LOW)
return;
#ifdef NTVDM
wrap = updateCount();
#else
nticks = clocksSinceCounterActivated(&now);
updateCount(nticks, &wrap,&now);
#endif
if (wrap)
pcu->tickadjust = pcu->Count;
if (pcu->m == INT_ON_TERMINALCOUNT && wrap)
pcu->tc = 1;
if (pcu == &timers[0] && wrap){
if (pcu->m != INT_ON_TERMINALCOUNT)
issueIREQ0((unsigned int)wrap);
else
issueIREQ0(1);
#ifdef HUNTER
timer_batch_count = wrap;
#endif
}
return;
case PROG_ONESHOT:
case SW_TRIG_STROBE:
case HW_TRIG_STROBE:
if (pcu->tc)
return;
#ifdef NTVDM
wrap = updateCount();
#else
nticks = clocksSinceCounterActivated(&now);
updateCount(nticks, &wrap,&now);
#endif
if (wrap)
{
pcu->Count = 0;
pcu->tc = 1;
#ifdef NTVDM
RealTimeCountCounterZero = 0;
#endif
}
#ifdef HUNTER
if (pcu == &timers[0]){
timer_batch_count = wrap;
}
#endif
return;
}
}
#ifndef NTVDM
#ifndef DELAYED_INTS
/*
* timer_no_longer_too_soon() - this is the function invoked by the quick event manager
* "HOST_TIMER_TOOLONG_DELAY" instructions after a hardware interrupt is generated. It
* clears the variable "too_soon_after_previous" to allow more interrupts to be
* generated and kicks off an immediate one if any have been suppressed this time.
*/
LOCAL void timer_no_longer_too_soon IFN1(long, dummy)
{
UNUSED(dummy);
too_soon_after_previous = FALSE;
if (ticks_lost_this_time){
/* At least one tick was suppressed... so send another one immediately */
timer_generate_int (1);
}
}
/*
* timer_generate_int() -The routine to generate a single timer hardware interrupt (and to
* schedule a quick event timer call on itself to have the remaining pending interrupts
* generated at a later time).
*
*/
LOCAL void timer_generate_int IFN1(long, n)
{
#if !(defined(GISP_CPU) || defined(CPU_40_STYLE))
if (getPE()){
/* Prot mode tick... */
#ifndef PROD
if (!hack_active){
SAVED BOOL first=TRUE;
always_trace0 ("PM timer Hack activated.");
if (first){
always_trace1 ("Min # instrs between interrupts = %d", HOST_TIMER_TOOLONG_DELAY);
always_trace1 (" Nominal instrs_per_tick = %d", instrs_per_tick);
always_trace1 (" adjusted instrs_per_tick = %d", adj_instrs_per_tick);
always_trace1 (" # rm instrs before full speed = %d", n_rm_instrs_before_full_speed);
always_trace1 (" rm ticks before full speed = %d", adj_n_real_mode_ticks_before_full_speed);
first = FALSE;
}
}
#endif
hack_active = TRUE;
real_mode_ticks_in_a_row = 0;
}else{
/* Real Mode Tick... */
if (hack_active){
real_mode_ticks_in_a_row++;
if (real_mode_ticks_in_a_row >= adj_n_real_mode_ticks_before_full_speed){
hack_active = FALSE;
always_trace0 ("PM timer Hack deactivated.");
}
}
}
#endif /* ! (GISP_CPU||CPU_40_STYLE) */
if (hack_active){
if (!too_soon_after_previous){
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, 1);
too_soon_after_previous = TRUE;
ticks_lost_this_time = FALSE;
add_q_event_i(timer_no_longer_too_soon,HOST_TIMER_TOOLONG_DELAY,0);
}else{
ticks_lost_this_time = TRUE;
#ifndef PROD
ticks_ignored++;
if (!(ticks_ignored & 0xFF)){
always_trace0 ("another 256 ticks lost!");
}
#endif
}
}else{
#ifndef GISP_CPU
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, 1);
}
}
#else /* GISP_CPU */
#if defined(IRET_HOOKS)
if (!HostDelayTimerInt(n))
{ /* no host need to delay this timer int, so generate one now. */
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, 1);
}
#else /* !IRET_HOOKS */
/* GISP_CPU doesn't use quick events so use ica_hw_interrupt(,,n). */
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, n);
#endif /* IRET_HOOKS */
}
}
#endif /* GISP_CPU */
#endif /* DELAYED_INTS */
#else /* NTVDM */
/*
* TimerGenerateMultipleInterrupts
*
*/
void TimerGenerateMultipleInterrupts(long n)
{
if (!EoiPending) {
EoiPending += n;
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, n);
}
else {
if (n > 5 && (dwWNTPifFlags & COMPAT_TIMERTIC)) {
n = 5;
}
if (EoiIntsPending/n < 19) { // less than a second behind ?
EoiIntsPending += n;
}
else {
EoiIntsPending++;
}
}
}
#ifndef MONITOR
/* On RISC ports, it is dangerous to call getIF from a non-CPU thread,
so we replace the call with a peek at the global 'EFLAGS' variable
until the CPU emulator getIF is made safe.
*/
#define getIF() (GLOBAL_EFLAGS & 0x200)
#endif /* !MONITOR */
/* timer_generate_int NTVDM
*
*/
void timer_generate_int (void)
{
word lo, hi, wrap;
/*
* For Nt port see if we really need to generate
* an int, checking if an app has hooked real-mode
* or protect-mode vectors.
*
* If we don't need to do it, then update the bios
* Data tic count directly.
*
* WARNING according to sfrost it is not safe to
* use sas, because of multithreading.
*/
hi = * (word *)(Start_of_M_area+0x1c*4+2);
lo = * (word *)(Start_of_M_area+0x1c*4);
wrap = (word) *(half_word *)(Start_of_M_area + ((ULONG)hi << 4) + lo);
if (!getIF() || ((hi != TimerInt1CSeg || lo != TimerInt1COff) && wrap != 0xcf) ||
*(word *)(Start_of_M_area+0x08*4+2) != TimerInt08Seg ||
*(word *)(Start_of_M_area+0x08*4) != TimerInt08Off )
{
TimerGenerateMultipleInterrupts(1);
}
else { /* update Bios Data Area directly */
++(*(double_word *)(Start_of_M_area + TIMER_LOW));
/* Wrap at 24 hours ? */
if (*(double_word *)(Start_of_M_area + TIMER_LOW) == 0x1800b0)
{
*(word *)(Start_of_M_area + TIMER_LOW) = 0;
*(word *)(Start_of_M_area + TIMER_HIGH) = 0;
*(half_word *)(Start_of_M_area+TIMER_OVFL)=1;
}
/* decr motor count */
--(*(half_word *)(Start_of_M_area + MOTOR_COUNT));
/* if motor count went to zero turn off the motor */
if (!*(half_word *)(Start_of_M_area + MOTOR_COUNT))
{
*(half_word *)(Start_of_M_area + MOTOR_STATUS) &= 0xF0;
fla_outb(DISKETTE_DOR_REG, 0x0C);
}
if (EoiDelayInUse && !(--EoiDelayInUse)) {
host_DelayHwInterrupt(CPU_TIMER_INT, 0, 0xFFFFFFFF);
}
}
}
/* TimerEoiHook
*
* EoiHook for the timer interrupt used to regulate the flow of
* timer interrupts to ensure that ints are not generated too
* close together. This routine is invoked by the ica EoiHook
* callbacks.
*
*/
void TimerEoiHook(int IrqLine, int CallCount)
{
if (EoiPending)
--EoiPending;
if (CallCount < 0) { // interrupts were canceled
EoiIntsPending = 0;
EoiPending = 0;
}
else if (CallCount) {
EoiDelayInUse = 100;
host_DelayHwInterrupt(CPU_TIMER_INT,
0,
timer_delay_size
);
}
else if (EoiIntsPending) {
EoiDelayInUse = 100;
if (host_DelayHwInterrupt(CPU_TIMER_INT,
EoiIntsPending,
timer_delay_size
))
{
EoiPending = EoiIntsPending;
}
EoiIntsPending = 0;
}
else {
if (EoiDelayInUse && !(--EoiDelayInUse)) {
host_DelayHwInterrupt(CPU_TIMER_INT, 0, 0xFFFFFFFF);
}
}
}
#endif /* NTVDM */
#ifndef NTVDM
/*
* Handle a cranked up timer where multiple interrupts are
* required per tick. Schedule 'n' ints over a tick. Period
* stored in quick event argument rather than in global.
*/
LOCAL void timer_multiple_ints IFN1(long, num_ints)
{
/* generate timer int */
timer_generate_int(1);
/* one less to do */
num_ints --;
/* any more arrived whilst we were q_ expiring? */
num_ints += more_timer_mult;
more_timer_mult = 0;
/* throw away ints that are going to take more than MAX_BACK_SECS
* to clear up. (!!!!)
*/
if (num_ints > max_backlog)
{
num_ints = max_backlog;
}
/* schedule next int (if required) */
if (num_ints == 0)
{
active_int_event = FALSE;
/* 1.193180 usecs per clock */
max_backlog = (1193180 * MAX_BACK_SECS) / timers[0].initialCount;
}
else /* more work to do */
{
/* set new quick_ev off - delay determined by timer wrap rate */
add_q_event_t(timer_multiple_ints, timer_multiple_delay, num_ints);
}
}
#endif
LOCAL void issueIREQ0 IFN1(unsigned int, n)
{
IU16 int_delay; /* delay before handling wrapped int */
#ifndef PROD
static pig_factor = 0;
static time_factor = 0;
#endif
#ifdef PIG
extern IBOOL ccpu_pig_enabled;
#endif
#ifndef PROD
if ( time_factor == 0 )
{
char *env;
env = host_getenv("TIMER_FACTOR");
if ( env )
time_factor = atoi(env);
if ( time_factor == 0 )
time_factor = 1;
#ifdef PIG
if ( pig_factor == 0 )
{
env = host_getenv("PIG_TIMER_FACTOR");
if ( env )
pig_factor = atoi(env);
if ( pig_factor == 0 )
pig_factor = 10;
}
#else
pig_factor = 1;
#endif
}
#endif
if (ticks_blocked == 0)
{
#ifndef PROD
#ifdef PIG
if ( ccpu_pig_enabled ) {
ticks_blocked = pig_factor-1;
} else
#endif
ticks_blocked = time_factor-1;
#endif
if (timer_int_enabled)
{
#ifdef DELAYED_INTS
ica_hw_interrupt_delay(ICA_MASTER,CPU_TIMER_INT, n,
HOST_TIMER_INT_DELAY);
#else /* !DELAYED_INTS */
#ifdef NTVDM
if (n > 0)
timer_delay_size= HOST_IDEAL_ALARM/(n+1);
if (n == 1) {
timer_generate_int();
}
else if (n > 1){
TimerGenerateMultipleInterrupts(n);
}
#else /* !NTVDM */
/* if we've got a quick event running, add to its workload */
if (active_int_event)
{
/* spread interrupts through system tick */
int_delay = SYSTEM_TICK_INTV / (n + 1);
if (int_delay < timer_multiple_delay)
timer_multiple_delay = int_delay;
more_timer_mult += n;
}
else
{
/* ensure multiple delay restarts at sensible speed */
timer_multiple_delay = SYSTEM_TICK_INTV >> 1;
if (n == 1)
{
timer_generate_int(1);
}
else
{
/* spread interrupts through system tick */
timer_generate_int(1);
timer_multiple_delay = SYSTEM_TICK_INTV / n;
active_int_event = TRUE;
add_q_event_t(timer_multiple_ints, timer_multiple_delay, n-1);
}
}
#endif /* !NTVDM */
#endif /* !DELAYED_INTS */
}
}
else if (ticks_blocked > 0)
{
ticks_blocked--;
}
}
#ifdef NTVDM
unsigned long clocksSinceCounterUpdate(struct host_timeval *pCurrTime,
struct host_timeval *pLastTime,
word *pCounter )
{
unsigned long clocks, wrap, usecs;
/* Calculate usecs elapsed and clocks elapsed since last update
*
* For NT port timer zero's IdealInterval is exact to a modulo
* of 65536, for efficiency and accuracy we stick with the exact
* number of clocks between IdealIntervals.
*/
if (pCounter == &timers[0].Count) { /* update quick way for IdealTime */
if (pCurrTime->tv_sec != pLastTime->tv_sec ||
pCurrTime->tv_usec != pLastTime->tv_usec )
{
*pLastTime = *pCurrTime;
return 65536/pcu->initialCount;
}
else {
usecs = clocks = 0;
}
}
else { /* calc diff in usecs and clocks elapsed */
usecs = (unsigned long)(pCurrTime->tv_sec - pLastTime->tv_sec);
if (!usecs) {
usecs = pCurrTime->tv_usec - pLastTime->tv_usec;
}
else if (usecs == 1) {
usecs = 1000000L - pLastTime->tv_usec + pCurrTime->tv_usec;
}
else {
usecs = pCurrTime->tv_usec - pLastTime->tv_usec +
(pCurrTime->tv_sec - pLastTime->tv_sec) * 1000000L;
}
/* ... clocks elapsed 1.193180 usecs per clock
*
* However, app time is not real time so round down
* a teency bit by truncating the "180"
*
* clocks = (usecs * 1193)/1000 + (usecs * 180)/1000000;
*/
clocks = (usecs * 1193)/1000;
}
/* how many times did counter wrap ? */
wrap = clocks/pcu->initialCount;
/* calc ticks from elapsed clocks */
clocks = clocks && pcu->initialCount ? clocks % pcu->initialCount : 0;
*pCounter = (word) (pcu->initialCount - clocks);
/* if the count wrapped reset Last Update time stamp */
if (wrap) {
*pLastTime = *pCurrTime;
if ((ULONG)pLastTime->tv_usec < usecs) {
pLastTime->tv_sec--;
pLastTime->tv_usec = 1000000L + pLastTime->tv_usec - usecs;
}
else {
pLastTime->tv_usec -= usecs;
}
}
return wrap;
}
unsigned long updateCount(void)
{
unsigned long wrap;
struct host_timeval curr;
/*
* For timer zero, update real time count, time stamp
*/
if (pcu == &timers[0]) {
host_GetSysTime(&curr);
clocksSinceCounterUpdate(&curr,
&LastTimeCounterZero,
&RealTimeCountCounterZero);
}
/*
* Update the pcu count, time stamps
*/
(*pcu->getTime)(&curr);
wrap = clocksSinceCounterUpdate(&curr,
&pcu->activationTime,
&pcu->Count);
return wrap;
}
#else
LOCAL void updateCount IFN3(unsigned long, ticks, unsigned long *, wrap,
struct host_timeval *, now)
{
unsigned long modulo = pcu->initialCount;
/*
* PCLABS version 4.2 uses counter 2 (the sound channel) to
* time around 45 ms on a 8MHz 286. On SoftPC we cannot
* guarantee to go that fast, and so we must wind the tick
* rate down to ensure that the counter does not wrap. How much
* we wind down the tick rate is host dependent. The object is
* to get the test finishing in less than
* host_timer_2_frig_factor/18 secs.
*
* host_timer_2_frig_factor is now redundant. 28/4/93 niall.
*/
if (pcu == &timers[2]) {
/*
* PMINFO uses ~counter, so one tick becomes 0.
* 2 ticks is just as good. Avoid guessing (frig_factor!).
*/
if ((ticks - pcu->lastTicks) == 0)
ticks = 2;
}
/* if the counter has been read too quickly after its last
* access, then the host may not show any visible change in
* host time ... in which case we just guess at a suitable
* number of elapsed ticks.
*/
if ((long)(ticks - pcu->lastTicks) <= 0){
ticks = guess();
}else{
throwaway();
pcu->lastTicks = ticks;
}
/* the counter holds some count down value ...
* if the number of 8253 clocks elapsed exceeds this amount
* then the counter must have wrapped
*/
if ( ticks < modulo ) {
*wrap = 0;
} else {
*wrap = 1;
ticks -= modulo;
if ( pcu->m == INT_ON_TERMINALCOUNT )
modulo = 0x10000L;
if ( ticks >= modulo ) {
*wrap += ticks/modulo;
ticks %= modulo;
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
if ( pcu->m == INT_ON_TERMINALCOUNT ) {
sprintf(buf, "%lx wraps for timer[%d]", *wrap, pcu-timers);
trace(buf, DUMP_NONE);
}
#endif
}
}
/* calculate new counter value */
pcu->Count = (word)(modulo-ticks);
/* calculate time at which last wrap point occurred, and
* use this to stamp the counter
*/
if (*wrap)
setLastWrap((unsigned int)(modulo-pcu->Count),now);
}
/* calculate time for last wrap around of counter, and use this
* to mark counter activation time.
*/
LOCAL void setLastWrap IFN2(unsigned int, nclocks, struct host_timeval *, now)
{
struct host_timeval *stamp;
unsigned long usecs;
stamp = &pcu->activationTime;
*stamp = *now;
usecs = ((unsigned long)nclocks * TIMER_CLOCK_NUMER) / TIMER_CLOCK_DENOM;
if (stamp->tv_usec < usecs)
{
stamp->tv_sec--;
stamp->tv_usec += 1000000L;
}
stamp->tv_usec -= usecs;
pcu->lastTicks = nclocks;
}
#endif /* NTVDM*/
#ifndef NTVDM
/*
* If the host timer gives the same result as last time it was called,
* we must give the illusion that time has passed.
* The algorithm uesd is to keep track of how often we have to guess
* between host timer ticks, and to assume that guesses should be evenly
* distributed between host ticks, ie. the time between two guesses should be:
* Guess ticks = hosttickinterval/nguesses
* If we find that the total guessed time is getting dangerously near to being the time
* between two host ticks, we start reducing the guess tick interval so as to avoid
* guessing a total time that is too large.
*
* From observation, applications use the timer in both coarse and fine 'modes'.
* The coarse mode is the hardware interrupt handler and then in between
* interrupts, the timer is polled to detect time passing (hence guess below).
* The fine mode polling does not commence until some portion of the tick time
* has elapsed - probably as the coarse int handler will consume some of the
* time. If guess() bases its timefrig over the whole tick, then given the
* above behaviour, a polling counter can reach tick end time before the int
* is delivered. This can fool applications (e.g. Win 3.1 VTD) into having
* time pass at the wrong rate (approx double for Win 3.1). By calculating
* the timefrig on most (7/8) of the tick period, this problem is avoided.
*/
LOCAL unsigned long guess IFN0()
{
if (!pcu->microtick)
{
pcu->saveCount = pcu->Count;
pcu->timeFrig = ((ticksPerIdealInterval * 7) >> 3) / pcu->guessesPerHostTick; /* guesses over 7/8 of tick */
#ifndef PROD
if (io_verbose & TIMER_VERBOSE) {
sprintf(buf, "guess init timer[%d]: timeFrig = %lx", pcu-timers, pcu->timeFrig);
trace(buf, DUMP_NONE);
}
#endif
}
if(pcu->guessesSoFar++ > pcu->guessesPerHostTick)
{
/*
* PC Program is reading the timer more often than in the last timer tick, so need to
* decay timeFrig to avoid too much 'time' passing between host ticks
*/
pcu->timeFrig = (pcu->timeFrig >> 1) + 1;
#ifndef PROD
if (io_verbose & TIMER_VERBOSE) {
sprintf(buf, "guess decay: timeFrig = %lx", pcu->timeFrig);
trace(buf, DUMP_NONE);
}
#endif
}
pcu->microtick += pcu->timeFrig;
return (pcu->microtick + pcu->lastTicks);
}
/*
* After a few (maybe none) guesses, the host timer has finally ticked.
* Try and work out a good frig factor for the next few guesses, based
* on the number of guesses we had to make.
*/
LOCAL void throwaway IFN0()
{
pcu->guessesPerHostTick = (pcu->guessesPerHostTick + pcu->guessesSoFar)>>1;
pcu->guessesSoFar = 2; /* Handy to count from 2! */
if (!pcu->microtick)
return;
#ifndef PROD
if (io_verbose & TIMER_VERBOSE)
{
sprintf(buf, "throwaway: guessesPerHostTick = %d", (int)pcu->guessesPerHostTick);
trace(buf, DUMP_NONE);
}
#endif
pcu->Count = pcu->saveCount;
pcu->microtick = 0;
}
#endif /* ndef NTVDM */
LOCAL unsigned short bin_to_bcd IFN1(unsigned long, val)
{
register unsigned short m, bcd, i;
m = (short)(val % 10000L);
bcd = 0;
for (i=0; i<4; i++)
{
bcd = bcd | ((m % 10) << (i << 2));
m /= 10;
}
return(bcd);
}
/*
* convert 4 decade bcd value to binary
*/
LOCAL word bcd_to_bin IFN1(word, val)
{
register word bin, i, mul;
bin = 0;
mul = 1;
for (i=0; i<4; i++)
{
bin += (val & 0xf) * mul;
mul *= 10;
val = val >> 4;
}
return (bin);
}
/*
* this routine returns the number of timer clocks equivalent
* to the input count, allowing for timer mode and down count.
*/
LOCAL unsigned long timer_conv IFN1(word, count)
{
if (!count)
{
if (pcu->bcd == BCD)
return 10000L;
else
return 0x10000L;
}
else
return (unsigned long)count;
}
/* this routine returns the current ideal time value ...
* this is a very coarse resolution time ... it only changes
* per call to time_tick(). It does however represent what the
* system time would be given 100% accurate time signal
* ... this routine gets used for time-stamping if time_tick()
* is active, otherwise time-stamping is done using
* getHostSysTime() ... see below.
*/
LOCAL void getIdealTime IFN1(struct host_timeval *, t)
{
t->tv_sec = idealtime.tv_sec;
t->tv_usec = idealtime.tv_usec;
}
/* update our ideal time by the period (in usecs) between timer signals
* from the host as though these were delivered 100% accurately
*/
LOCAL void updateIdealTime IFN0()
{
idealtime.tv_usec += idealInterval;
if (idealtime.tv_usec > 1000000L)
{
idealtime.tv_usec -=1000000L;
idealtime.tv_sec++;
}
}
#ifndef NTVDM
/* get current host system time ... used for time-stamping and
* querying during io from Intel application
*/
LOCAL void getHostSysTime IFN1(struct host_timeval *, t)
{
struct host_timezone dummy;
host_gettimeofday(t, &dummy);
/*
* check that we haven't gone back in time.
*/
if (t->tv_sec < idealtime.tv_sec ||
(t->tv_usec < idealtime.tv_usec && t->tv_sec == idealtime.tv_sec))
{
/*
* The real time has fallen behind the ideal time.
* This should never happen... If it does we must
* stay at the ideal time.
*/
#ifndef PROD
#ifndef PIG
sprintf(buf,"TIME WARP!!");
trace(buf,0);
#endif
#endif
*t = idealtime;
}
}
#endif /* !NTVDM */
/*
* Used to temporarily stop the timer interrupts. PCLABS bench29
* causes this to be called iff a 80287 is being used.
*/
void axe_ticks IFN1(int, ticks)
{
#ifndef PROD
/*
* No need to axe ticks if timers are disabled by toff2 (if
* ticks_blocked is negative)
*/
if (ticks_blocked >=0)
#endif /* PROD */
ticks_blocked = ticks;
}
/*
* Initialization code
*/
#ifdef SEGMENTATION
/*
* The following #include specifies the code segment into which this
* module will by placed by the MPW C compiler on the Mac II running
* MultiFinder.
*/
#include "SOFTPC_INIT.seg"
#endif
#ifdef SYNCH_TIMERS
GLOBAL void IdealTimeInit IFN0()
{
extern IU32 Q_timer_restart_val;
idealInterval = Q_timer_restart_val;
#else
LOCAL void IdealTimeInit IFN0()
{
idealInterval = HOST_IDEAL_ALARM;
#endif
#ifndef NTVDM
getHostSysTime(&idealtime);
ticksPerIdealInterval = (idealInterval * TIMER_CLOCK_DENOM) / TIMER_CLOCK_NUMER;
#endif
}
LOCAL void Timer_init IFN0()
{
int i;
for (i=0; i<3; i++)
counter_init(&timers[i]);
#ifdef NTVDM
timers[0].getTime = getIdealTime; /* Use the 'ideal' time for timer 0, ie. calls to time_tick */
timers[1].getTime = host_GetSysTime; /* We don't really expect anyone to use timer 1 */
timers[2].getTime = host_GetSysTime; /* Use real host time for timer 2 */
#else
timers[0].getTime = getIdealTime; /* Use the 'ideal' time for timer 0, ie. calls to time_tick */
timers[1].getTime = getHostSysTime; /* We don't really expect anyone to use timer 1 */
timers[2].getTime = getHostSysTime; /* Use real host time for timer 2 */
#endif
}
LOCAL void counter_init IFN1(COUNTER_UNIT *, p)
{
p->state = uninit;
p->initialCount = 0x10000L; /* Avoid dividing by zero in updateCount()! */
#ifndef NTVDM
p->guessesPerHostTick = p->guessesSoFar = 2; /* Handy to count from 2! */
p->timeFrig = ticksPerIdealInterval / p->guessesPerHostTick;
#endif
}
#if !defined (NTVDM)
#if defined(IRET_HOOKS) && defined(GISP_CPU)
/*(
*======================= TimerHookAgain() ============================
* TimerHookAgain
*
* Purpose
* This is the function that we tell the ica to call when a timer
* interrupt service routine IRETs.
*
* Input
* adapter_id The adapter id for the line. (Note the caller doesn't
* know what this is, he's just returning something
* we gave him earlier).
*
* Outputs
* return TRUE if there are more interrupts to service, FALSE otherwise.
*
* Description
* Check if we have a delayed interrupt, if so then generate the timer int
* and return TRUE, else return FALSE
)*/
GLOBAL IBOOL
TimerHookAgain IFN1(IUM32, adapter)
{ char scancode;
if (HostPendingTimerInt())
{ /* We have a host delayed interrupt, so generate a timer int. */
sure_note_trace0(TIMER_VERBOSE,"callback with delayed timer int.");
ica_hw_interrupt(ICA_MASTER,CPU_TIMER_INT, 1);
return(TRUE); /* more to do */
}
else
{
return(FALSE);
}
}
#endif /* IRET_HOOKS && GISP_CPU */
#endif /* !NTVDM */
void timer_init IFN0()
{
io_addr i;
/*
* Set up the IO chip select logic for this adaptor
*/
io_define_inb(TIMER_ADAPTOR, timer_inb_func);
io_define_outb(TIMER_ADAPTOR, timer_outb_func);
for(i = TIMER_PORT_START; i < TIMER_PORT_END; i++)
{
if( (i & 3) == 3 )
io_connect_port(i, TIMER_ADAPTOR, IO_WRITE); /* Control port - write only */
else
io_connect_port(i, TIMER_ADAPTOR, IO_READ_WRITE); /* Timer port - read/write */
}
IdealTimeInit();
Timer_init();
#ifndef NTVDM
timelock = UNLOCKED;
needtick = 0;
#else
RegisterEOIHook(CPU_TIMER_INT, TimerEoiHook);
#endif
/*
* Start up the host alarm system
*/
host_timer_init();
#if !defined(NTVDM)
#if defined(IRET_HOOKS) && defined(GISP_CPU)
/*
* Remove any existing hook call-back, and re-instate it afresh.
* TimerHookAgain is what gets called on a timer int iret.
*/
Ica_enable_hooking(CPU_TIMER_INT, NULL, ICA_MASTER);
Ica_enable_hooking(CPU_TIMER_INT, TimerHookAgain, ICA_MASTER);
/* Host routine to reset any internal data for IRET_HOOK delayed ints. */
HostResetTimerInts();
#endif /* IRET_HOOKS && GISP_CPU */
active_int_event = FALSE; /* clear any cranked timer state */
more_timer_mult = 0;
#if defined(CPU_40_STYLE)
ica_iret_hook_control(ICA_MASTER, CPU_TIMER_INT, TRUE);
#endif
#endif /* !NTVDM */
}
void timer_post IFN0()
{
/* enable gates on all timer channels */
timer_gate(TIMER0_REG,GATE_SIGNAL_RISE); /* start timer 1 going... */
timer_gate(TIMER1_REG,GATE_SIGNAL_RISE);
timer_gate(TIMER2_REG,GATE_SIGNAL_RISE);
timer_outb(TIMER_MODE_REG,0x36);
timer_outb(TIMER0_REG,0);
timer_outb(TIMER0_REG,0);
timer_outb(TIMER_MODE_REG,0x54);
timer_outb(TIMER1_REG,17);
timer_outb(TIMER_MODE_REG,0xb6);
timer_outb(TIMER2_REG,0x68);
timer_outb(TIMER2_REG,0x04);
}
#ifdef DOCUMENTATION
#ifndef PROD
/*
* Debugging code....
* This code has no effect. It is left here in case anyone wants to
* expand it in future.
*/
dumpCounter IFN0()
{
static char *modes[] =
{ "int on tc",
"prog one shot",
"rate gen",
"squarewave gen",
"sw trig strobe",
"hw trig strobe"
};
static char *as[] =
{ "binary",
"bcd"
};
char *p, *q;
p = modes[pcu->m];
q = as[pcu->bcd];
}
#endif /* nPROD */
#endif /* DOCUMENTATION */