mirror of
https://github.com/rcornwell/sims.git
synced 2026-04-30 13:33:14 +00:00
SEL32: Fix sel32_clk.c coding error in interval timer code. SEL32: Update to latest makecode.c utility and add makefile. SEL32: Update diag.ini file to show how to set boot regs and CSW values.
396 lines
17 KiB
C
396 lines
17 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);
|
|
t_stat rtc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
|
|
const char *rtc_desc(DEVICE *dptr);
|
|
|
|
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 */
|
|
/* default to 60 HZ RTC */
|
|
UNIT rtc_unit = { UDATA (&rtc_srv, UNIT_IDLE, 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, /* examine, deposit, reset */
|
|
NULL, NULL, NULL, /* boot, attach, detach */
|
|
NULL, 0, 0, NULL, /* dib, dev flags, debug flags, debug */
|
|
NULL, NULL, &rtc_help, /* ?, ?, help */
|
|
NULL, NULL, &rtc_desc, /* ?, ?, description */
|
|
};
|
|
|
|
/* 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 */
|
|
// time_t result = time(NULL);
|
|
// fprintf(stderr, "Clock int time %x\r\n", (uint32)result);
|
|
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_after (&rtc_unit, 1000000/rtc_tps);/* reactivate 16666 tics / sec */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Clock interrupt start/stop */
|
|
/* ss = 1 - starting clock */
|
|
/* ss = 0 - stopping clock */
|
|
/* level = interrupt level */
|
|
void rtc_setup(uint32 ss, uint32 level)
|
|
{
|
|
uint32 val = SPAD[level+0x80]; /* get SPAD value for interrupt vector */
|
|
uint32 addr = SPAD[0xf1] + (level<<2); /* vector address in SPAD */
|
|
|
|
rtc_lvl = level; /* save the interrupt level */
|
|
addr = M[addr>>2]; /* get the interrupt context block addr */
|
|
if (ss == 1) { /* starting? */
|
|
INTS[level] |= INTS_ENAB; /* make sure enabled */
|
|
SPAD[level+0x80] |= SINT_ENAB; /* in spad too */
|
|
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_unit(&rtc_unit, 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 current frequency setting */
|
|
if (rtc_tps < 100)
|
|
fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz");
|
|
else
|
|
fprintf (st, (rtc_tps == 100)? "100Hz": "120Hz");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* sho help rtc */
|
|
t_stat rtc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
|
|
{
|
|
fprintf(st, "SEL 32 IOP realtime clock at 0x7F06\r\n");
|
|
fprintf(st, "Use:\r\n");
|
|
fprintf(st, " sim> SET RTC [50][60][100][120]\r\n");
|
|
fprintf(st, "to set clock interrupt rate in HZ\r\n");
|
|
fprint_set_help(st, dptr);
|
|
fprint_show_help(st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* device description */
|
|
const char *rtc_desc(DEVICE *dptr)
|
|
{
|
|
return "SEL IOP realtime clock @ address 0x7F06";
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
/* Interval Timer support */
|
|
int32 itm_pie = 0; /* itm pulse enable */
|
|
int32 itm_cmd = 0; /* itm last user cmd */
|
|
int32 itm_cnt = 0; /* itm pulse count enable */
|
|
int32 itm_tick_size_x_100 = 3840; /* itm 26041 ticks/sec = 38.4 us per tic */
|
|
int32 itm_lvl = 0x5f; /* itm interrupt level */
|
|
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);
|
|
t_stat itm_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
|
|
const char *itm_desc(DEVICE *dptr);
|
|
|
|
/* Clock data structures
|
|
|
|
itm_dev Interval Timer ITM device descriptor
|
|
itm_unit Interval Timer ITM unit
|
|
itm_reg Interval Timer ITM register list
|
|
*/
|
|
|
|
UNIT itm_unit = { UDATA (&itm_srv, UNIT_IDLE, 0), 26042, UNIT_ADDR(0x7F04)};
|
|
|
|
REG itm_reg[] = {
|
|
{ FLDATA (PIE, itm_pie, 0) },
|
|
{ FLDATA (CNT, itm_cnt, 0) },
|
|
{ FLDATA (CMD, itm_cmd, 0) },
|
|
{ DRDATA (TICK_SIZE, itm_tick_size_x_100, 32), PV_LEFT + REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB itm_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 3840, NULL, "3840us",
|
|
&itm_set_freq, NULL, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 7680, NULL, "7680us",
|
|
&itm_set_freq, NULL, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "RESOLUTION", 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, /* examine, deposit, reset */
|
|
NULL, NULL, NULL, /* boot, attach, detach */
|
|
NULL, 0, 0, NULL, /* dib, ?, ?, debug */
|
|
NULL, NULL, &itm_help, /* ?, ?, help */
|
|
NULL, NULL, &itm_desc, /* ?, ?, description */
|
|
};
|
|
|
|
/* 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 expiration from simulator */
|
|
/* cause interrupt */
|
|
t_stat itm_srv (UNIT *uptr)
|
|
{
|
|
if (itm_pie) { /* interrupt enabled? */
|
|
time_t result = time(NULL);
|
|
fprintf(stderr, "Clock int time %x\r\n", (uint32)result);
|
|
INTS[itm_lvl] |= INTS_REQ; /* request the interrupt on zero value */
|
|
irq_pend = 1; /* make sure we scan for int */
|
|
if (itm_cmd == 0x3d) {
|
|
/* restart timer with value from user */
|
|
sim_activate_after_abs_d (&itm_unit, ((double)itm_cnt * itm_tick_size_x_100) / 100.0);
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* ITM read/load function called from CD command processing */
|
|
/* level = interrupt level */
|
|
/* cmd = 0x20 stop timer, do not transfer any value */
|
|
/* = 0x39 load and enable interval timer, no return value */
|
|
/* = 0x3d load and enable interval timer, countdown to zero, interrupt and reload */
|
|
/* = 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;
|
|
|
|
itm_cmd = cmd; /* save last cmd */
|
|
switch (cmd) {
|
|
case 0x20: /* stop timer */
|
|
// fprintf(stderr, "clk kill value %x (%d)\r\n", cnt, cnt);
|
|
sim_cancel (&itm_unit); /* cancel itc */
|
|
itm_cnt = 0; /* no count reset value */
|
|
return 0; /* does not matter, no value returned */
|
|
case 0x39: /* load timer with new value and start*/
|
|
if (cnt <= 0)
|
|
cnt = 26042; /* 0x65ba TRY 1,000,000/38.4 */
|
|
// fprintf(stderr, "clk 0x39 init value %x (%d)\r\n", cnt, cnt);
|
|
/* start timer with value from user */
|
|
sim_activate_after_abs_d (&itm_unit, ((double)cnt * itm_tick_size_x_100) / 100.0);
|
|
itm_cnt = 0; /* no count reset value */
|
|
return 0; /* does not matter, no value returned */
|
|
case 0x3d: /* load timer with new value and start*/
|
|
// fprintf(stderr, "clk 0x3d init value %x (%d)\r\n", cnt, cnt);
|
|
/* start timer with value from user, reload on zero time */
|
|
sim_activate_after_abs_d (&itm_unit, ((double)cnt * itm_tick_size_x_100) / 100.0);
|
|
itm_cnt = cnt; /* count reset value */
|
|
return 0; /* does not matter, no value returned */
|
|
case 0x60: /* read and stop timer */
|
|
/* get timer value and stop timer */
|
|
temp = (uint32)(100.0 * sim_activate_time_usecs (&itm_unit) / itm_tick_size_x_100);
|
|
// fprintf(stderr, "clk 0x60 temp value %x (%d)\r\n", temp, temp);
|
|
sim_cancel (&itm_unit);
|
|
return temp; /* return current count value */
|
|
case 0x79: /* read the current timer value */
|
|
/* get timer value, load new value and start timer */
|
|
temp = (uint32)(100.0 * sim_activate_time_usecs (&itm_unit) / itm_tick_size_x_100);
|
|
// fprintf(stderr, "clk 0x79 temp value %x (%d)\r\n", temp, temp);
|
|
// fprintf(stderr, "clk 0x79 init value %x (%d)\r\n", cnt, cnt);
|
|
/* start timer to fire after cnt ticks */
|
|
sim_activate_after_abs_d (&itm_unit, ((double)cnt * itm_tick_size_x_100) / 100.0);
|
|
itm_cnt = 0; /* no count reset value */
|
|
return temp; /* return current count value */
|
|
case 0x40: /* read the current timer value */
|
|
/* return current count value */
|
|
temp = (uint32)(100.0 * sim_activate_time_usecs (&itm_unit) / itm_tick_size_x_100);
|
|
// fprintf(stderr, "clk 0x40 temp value %x (%d)\r\n", temp, temp);
|
|
return temp;
|
|
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(uint32 ss, uint32 level)
|
|
{
|
|
itm_lvl = level; /* save the interrupt level */
|
|
if (ss == 1) { /* starting? */
|
|
INTS[level] |= INTS_ENAB; /* make sure enabled */
|
|
SPAD[level+0x80] |= SINT_ENAB; /* in spad too */
|
|
//DIAG INTS[level] |= INTS_REQ; /* request the interrupt */
|
|
sim_cancel (&itm_unit); /* not running yet */
|
|
} 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)
|
|
{
|
|
itm_pie = 0; /* disable pulse */
|
|
sim_cancel (&itm_unit); /* not running yet */
|
|
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 != 3840) && (val != 7680))
|
|
return SCPE_IERR; /* scope error */
|
|
itm_tick_size_x_100 = val; /* 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 current interval count setting */
|
|
fprintf (st, "%0.2fus", (itm_tick_size_x_100 / 100.0));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* sho help rtc */
|
|
t_stat itm_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr)
|
|
{
|
|
fprintf(st, "SEL 32 IOP interval timer at 0x7F04\r\n");
|
|
fprintf(st, "Use:\r\n");
|
|
fprintf(st, " sim> SET ITM [3840][7680]\r\n");
|
|
fprintf(st, "to set interval timer clock rate in us x 100\r\n");
|
|
fprint_set_help(st, dptr);
|
|
fprint_show_help(st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* device description */
|
|
const char *itm_desc(DEVICE *dptr)
|
|
{
|
|
return "SEL IOP interval timer @ address 0x7F04";
|
|
}
|
|
|
|
#endif
|
|
|