1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-05-01 22:07:31 +00:00
Files
rcornwell.sims/SEL32/sel32_clk.c
2019-07-17 21:39:18 -04:00

367 lines
16 KiB
C

/* sel32_clk.c: SEL 32 Class F IOP processor RTOM functions.
Copyright (c) 2018, James C. Bevier
Portions provided by Richard Cornwell and other SIMH contributers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JAMES C. BEVIER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This module support the real-time clock and the interval timer.
These are CD/TD class 3 devices. The RTC can be programmed to
50/100 HZ or 60/120 HZ rates and creates an interrupt at the
requested rate. The interval timer is a 32 bit register that is
loaded with a value to be down counted. An interrupt is generated
when the count reaches zero, The clock continues down counting
until read/reset by the programmer. The rate can be external or
38.4 microseconds per count.
*/
#include "sel32_defs.h"
#ifdef NUM_DEVS_RTOM
extern t_stat set_dev_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
extern t_stat show_dev_addr(FILE *st, UNIT * uptr, int32 v, CONST void *desc);
extern void chan_end(uint16 chan, uint8 flags);
extern int chan_read_byte(uint16 chan, uint8 *data);
extern int chan_write_byte(uint16 chan, uint8 *data);
extern void set_devattn(uint16 addr, uint8 flags);
extern void post_extirq(void);
void rtc_setup (uint32 ss, uint32 level);
t_stat rtc_srv (UNIT *uptr);
t_stat rtc_reset (DEVICE *dptr);
t_stat rtc_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
extern int irq_pend; /* go scan for pending int or I/O */
extern uint32 INTS[]; /* interrupt control flags */
extern uint32 SPAD[]; /* computer SPAD */
extern uint32 M[]; /* system memory */
int32 rtc_pie = 0; /* rtc pulse ie */
int32 rtc_tps = 60; /* rtc ticks/sec */
int32 rtc_lvl = 0x18; /* rtc interrupt level */
/* Clock data structures
rtc_dev RTC device descriptor
rtc_unit RTC unit
rtc_reg RTC register list
*/
/* clock is attached all the time */
/* defailt to 60 HZ RTC */
UNIT rtc_unit = { UDATA (&rtc_srv, UNIT_ATT, 0), 16666, UNIT_ADDR(0x7F06)};
REG rtc_reg[] = {
{ FLDATA (PIE, rtc_pie, 0) },
{ DRDATA (TIME, rtc_unit.wait, 32), REG_NZ + PV_LEFT },
{ DRDATA (TPS, rtc_tps, 8), PV_LEFT + REG_HRO },
{ NULL }
};
MTAB rtc_mod[] = {
{ MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ",
&rtc_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ",
&rtc_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 100, NULL, "100HZ",
&rtc_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 120, NULL, "120HZ",
&rtc_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL,
NULL, &rtc_show_freq, NULL },
{ 0 }
};
DEVICE rtc_dev = {
"RTC", &rtc_unit, rtc_reg, rtc_mod,
1, 8, 8, 1, 8, 8,
NULL, NULL, &rtc_reset,
NULL, NULL, NULL
};
/* The real time clock runs continuously; therefore, it only has
a unit service routine and a reset routine. The service routine
sets an interrupt that invokes the clock counter.
*/
/* service clock signal from simulator */
t_stat rtc_srv (UNIT *uptr)
{
if (rtc_pie) { /* set pulse intr */
INTS[rtc_lvl] |= INTS_REQ; /* request the interrupt */
irq_pend = 1; /* make sure we scan for int */
}
// rtc_unit.wait = sim_rtcn_calb (rtc_tps, TMR_RTC); /* calibrate */
// sim_activate (&rtc_unit, rtc_unit.wait); /* reactivate */
sim_activate (&rtc_unit, 16667); /* reactivate */
return SCPE_OK;
}
/* Clock interrupt start/stop */
/* ss = 1 - starting clock */
/* ss = 0 - stopping clock */
/* level = interrupt level */
void rtc_setup(uint ss, uint32 level)
{
uint32 val = SPAD[level+0x80]; /* get SPAD value for interrupt vector */
rtc_lvl = level; /* save the interrupt level */
uint32 addr = SPAD[0xf1] + (level<<2); /* vector address in SPAD */
addr = M[addr>>2]; /* get the interrupt context block addr */
//fprintf(stderr, "rtc_setup called ss %x level %x SPAD %x icba %x\r\n", ss, level, val, addr);
if (ss == 1) { /* starting? */
INTS[level] |= INTS_ENAB; /* make sure enabled */
SPAD[level+0x80] |= SINT_ENAB; /* in spad too */
INTS[level] |= INTS_REQ; /* request the interrupt */
sim_activate(&rtc_unit, 20); /* start us off */
} else {
INTS[level] &= ~INTS_ENAB; /* make sure disabled */
SPAD[level+0x80] &= ~SINT_ENAB; /* in spad too */
}
rtc_pie = ss; /* set new state */
}
/* Clock reset */
t_stat rtc_reset(DEVICE *dptr)
{
rtc_pie = 0; /* disable pulse */
rtc_unit.wait = sim_rtcn_init (rtc_unit.wait, TMR_RTC); /* initialize clock calibration */
sim_activate (&rtc_unit, rtc_unit.wait); /* activate unit */
return SCPE_OK;
}
/* Set frequency */
t_stat rtc_set_freq(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (cptr) /* if chars, bad */
return SCPE_ARG; /* ARG error */
if ((val != 50) && (val != 60) && (val != 100) && (val != 120))
return SCPE_IERR; /* scope error */
rtc_tps = val; /* set the new frequency */
return SCPE_OK; /* we done */
}
/* Show frequency */
t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
/* print the cirrent frequency setting */
if (rtc_tps < 100)
fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz");
else
fprintf (st, (rtc_tps == 100)? "100Hz": "120Hz");
return SCPE_OK;
}
/************************************************************************/
/* Interval Timer support */
int32 itm_pie = 0; /* itm pulse enable */
//int32 itm_tps = 38; /* itm 26041 ticks/sec = 38.4 us per tic */
///int32 itm_tps = 48; /* itm 26041 ticks/sec = 38.4 us per tic */
int32 itm_tps = 64; /* itm 26041 ticks/sec = 38.4 us per tic */
int32 itm_lvl = 0x5f; /* itm interrupt level */
int32 itm_cnt = 26041; /* value that we are downcounting */
int32 itm_run = 0; /* set when timer running */
t_stat itm_srv (UNIT *uptr);
t_stat itm_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat itm_reset (DEVICE *dptr);
t_stat itm_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
#define TMR_ITM 2
/* Clock data structures
itm_dev Interval Timer ITM device descriptor
itm_unit Interval Timer ITM unit
itm_reg Interval Timer ITM register list
*/
/* clock is attached all the time */
/* defailt to 60 HZ RTC */
//UNIT itm_unit = { UDATA (&itm_srv, UNIT_ATT, 0), 38, UNIT_ADDR(0x7F04)};
//UNIT itm_unit = { UDATA (&itm_srv, UNIT_ATT, 0), 48, UNIT_ADDR(0x7F04)};
//UNIT itm_unit = { UDATA (&itm_srv, UNIT_ATT, 0), 26042, UNIT_ADDR(0x7F04)};
UNIT itm_unit = { UDATA (&itm_srv, UNIT_ATT, 0), 26042, UNIT_ADDR(0x7F04)};
REG itm_reg[] = {
{ FLDATA (PIE, itm_pie, 0) },
{ DRDATA (TIME, itm_unit.wait, 32), REG_NZ + PV_LEFT },
{ DRDATA (TPS, itm_tps, 32), PV_LEFT + REG_HRO },
{ NULL }
};
MTAB itm_mod[] = {
{ MTAB_XTD|MTAB_VDV, 384, NULL, "38.4us",
&itm_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 768, NULL, "76.86us",
&itm_set_freq, NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL,
NULL, &itm_show_freq, NULL },
{ 0 }
};
DEVICE itm_dev = {
"ITM", &itm_unit, itm_reg, itm_mod,
1, 8, 8, 1, 8, 8,
NULL, NULL, &itm_reset,
NULL, NULL, NULL
};
/* The interval timer downcounts the value it is loaded with and
runs continuously; therefore, it has a read/write routine,
a unit service routine and a reset routine. The service routine
sets an interrupt that invokes the clock counter.
*/
/* service clock signal from simulator */
/* for 38.4 us/tic we get 26041 ticks per second */
/* downcount the loaded value until zero and then cause interrupt */
t_stat itm_srv (UNIT *uptr)
{
// uint32 val = SPAD[itm_lvl+0x80]; /* get SPAD value for interrupt vector */
// uint32 addr = SPAD[0xf1] + (itm_lvl<<2); /* vector address in SPAD */
// addr = M[addr>>2]; /* get the interrupt context block addr */
//fprintf(stderr, "itm_srv level %x itm_pie %x wait %x spad %x icba %x\r\n",
// itm_lvl, itm_pie, itm_unit.wait, val, addr);
/* count down about 48 instructions per tick ~38.4 us */
/* we will be called once for each instructon */
itm_unit.wait -= 1; /* subtract 1 from wait count */
if (itm_unit.wait > 0)
return SCPE_OK; /* not time yet */
itm_unit.wait = itm_tps; /* reset wait count */
if (itm_run) { /* see if timer running */
itm_cnt--; /* down count by one */
if ((itm_cnt == 0) && itm_pie) { /* see if reached 0 yet */
// if (itm_cnt == 0) { /* see if reached 0 yet */
//fprintf(stderr, "itm_srv REQ itm_pie %x wait %x itm_cnt %x\r\n", itm_pie, itm_unit.wait, itm_cnt);
INTS[itm_lvl] |= INTS_REQ; /* request the interrupt on zero value */
irq_pend = 1; /* make sure we scan for int */
}
}
#if 0
itm_unit.wait = sim_rtcn_calb (itm_tps, TMR_ITM); /* calibrate */
sim_activate (&itm_unit, itm_unit.wait); /* reactivate */
#endif
return SCPE_OK;
}
/* ITM read/load function called from CD command processing */
/* level = interrupt level */
/* cmd = 0x39 load and enable interval timer, no return value */
/* = 0x40 read timer value */
/* = 0x60 read timer value and stop timer */
/* = 0x79 read/reload and start timer */
/* cnt = value to write to timer */
/* ret = return value read from timer */
int32 itm_rdwr(uint32 cmd, int32 cnt, uint32 level)
{
uint32 temp;
// uint32 val = SPAD[level+0x80]; /* get SPAD value for interrupt vector */
// itm_lvl = level; /* save the interrupt level */
// uint32 addr = SPAD[0xf1] + (level<<2); /* vector address in SPAD */
// addr = M[addr>>2]; /* get the interrupt context block addr */
//fprintf(stderr, "itm_rdwr called ss %x level %x SPAD %x icba %x\r\n", ss, level, val, addr);
//fprintf(stderr, "itm_rdwr called cmd %x count %x (%d) level %x return cnt %x (%d)\r\n",
// cmd, cnt, cnt, level, itm_cnt, itm_cnt);
switch (cmd) {
case 0x39: /* load timer with new value and start*/
if (cnt < 0)
cnt = 26042; /* TRY ??*/
itm_cnt = cnt; /* load timer with value from user to down count */
itm_run = 1; /* start timer */
return 0; /* does not matter, no value returned */
case 0x60: /* read and stop timer */
temp = itm_cnt; /* get timer value and stop timer */
itm_run = 0; /* stop timer */
// itm_cnt = 0; /* reset with timer value from user to down count */
return temp; /* return current count value */
case 0x79: /* read the current timer value */
temp = itm_cnt; /* get timer value, load new value and start timer */
itm_cnt = cnt; /* load timer with value from user to down count */
itm_run = 1; /* start timer */
return temp; /* return current count value */
case 0x40: /* read the current timer value */
return itm_cnt; /* return current count value */
break;
}
return 0; /* does not matter, no value returned */
}
/* Clock interrupt start/stop */
/* ss = 1 - clock interrupt enabled */
/* ss = 0 - clock interrupt disabled */
/* level = interrupt level */
void itm_setup(uint ss, uint32 level)
{
itm_lvl = level; /* save the interrupt level */
// fprintf(stderr, "itm_setup called ss %x level %x\r\n", ss, level);
if (ss == 1) { /* starting? */
INTS[level] |= INTS_ENAB; /* make sure enabled */
SPAD[level+0x80] |= SINT_ENAB; /* in spad too */
INTS[level] |= INTS_REQ; /* request the interrupt */
itm_cnt = 26042; /* start with 1 sec */
itm_run = 0; /* not running yet */
/// sim_activate(&itm_unit, 48); /* start us off */
} else {
INTS[level] &= ~INTS_ENAB; /* make sure disabled */
SPAD[level+0x80] &= ~SINT_ENAB; /* in spad too */
}
itm_pie = ss; /* set new state */
}
/* Clock reset */
t_stat itm_reset (DEVICE *dptr)
{
// int intlev = 0x5f; /* interrupt level for itm */
//fprintf(stderr, "itm_reset called\r\n");
itm_pie = 0; /* disable pulse */
itm_cnt = 26042; /* start with 1 sec */
itm_run = 0; /* not running yet */
#if 0
rtc_unit.wait = sim_rtcn_init (itm_unit.wait, TMR_ITM); /* initialize clock calibration */
sim_activate (&itm_unit, itm_unit.wait); /* activate unit */
#endif
return SCPE_OK;
}
/* Set frequency */
t_stat itm_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
if (cptr) /* if chars, bad */
return SCPE_ARG; /* ARG error */
if ((val != 384) && (val != 768))
return SCPE_IERR; /* scope error */
itm_tps = val/10; /* set the new frequency */
return SCPE_OK; /* we done */
}
/* Show frequency */
t_stat itm_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
/* print the cirrent frequency setting */
fprintf (st, (itm_tps == 38)? "38.4us": "76.8us");
return SCPE_OK;
}
#endif