- Add 1752 Drum support. Allow shared subroutines across interrupt levels. - Document and add sample scripts for customizing MSOS5.
372 lines
11 KiB
C
372 lines
11 KiB
C
/*
|
|
|
|
Copyright (c) 2015-2016, John Forecast
|
|
|
|
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
|
|
JOHN FORECAST 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.
|
|
|
|
Except as contained in this notice, the name of John Forecast shall not
|
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from John Forecast.
|
|
|
|
*/
|
|
|
|
/* cdc1700_rtc.c: 10336-1 Real-time clock support
|
|
* Simh devices: rtc
|
|
*/
|
|
#include "cdc1700_defs.h"
|
|
|
|
#define HOLDREG iod_writeR[0] /* Holding register */
|
|
#define COUNTER iod_readR[1] /* Counter */
|
|
|
|
extern char INTprefix[];
|
|
|
|
extern t_stat checkReset(DEVICE *, uint8);
|
|
|
|
extern t_stat show_addr(FILE *, UNIT *, int32, CONST void *);
|
|
|
|
extern t_stat set_equipment(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern void RaiseExternalInterrupt(DEVICE *);
|
|
extern void rebuildPending(void);
|
|
|
|
extern uint16 Areg;
|
|
|
|
extern t_bool IOFWinitialized;
|
|
|
|
t_stat rtc_show_rate(FILE *, UNIT *, int32, CONST void *);
|
|
t_stat rtc_set_rate(UNIT *, int32, CONST char *, void *);
|
|
|
|
void RTCstate(char *, DEVICE *, IO_DEVICE *);
|
|
uint16 RTCraised(DEVICE *);
|
|
enum IOstatus RTCin(IO_DEVICE *, uint8);
|
|
enum IOstatus RTCout(IO_DEVICE *, uint8);
|
|
|
|
t_stat rtc_svc(UNIT *);
|
|
t_stat rtc_reset(DEVICE *);
|
|
|
|
t_stat rtc_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
|
|
/*
|
|
10336-1 Real-Time Clock
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
(Bits 01-00)
|
|
|
|
00 Load Register
|
|
01 Director Function Read Counter
|
|
|
|
Operations:
|
|
|
|
Director Function 1
|
|
|
|
15 14 7 6 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | X | X | X | X | X | X | | | X | X | X | X | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | |
|
|
| | | | | Clr Controller
|
|
| | | | Ack. Interrupt
|
|
| | | Stop Clock
|
|
| | Start Clock
|
|
| Disable Interrupt
|
|
Enable Interrupt
|
|
|
|
The counter and register values are unsigned 16-bit values.
|
|
*/
|
|
|
|
IO_DEVICE RTCdev = IODEV(NULL, "10336-1", 10336, 13, 0xFF, 0,
|
|
NULL, RTCin, RTCout, NULL, NULL,
|
|
NULL, NULL, RTCraised, NULL, NULL, NULL,
|
|
0x7F, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1,
|
|
MASK_REGISTER0, 0,
|
|
AQ_ONLY, 0, NULL);
|
|
|
|
/*
|
|
* Define usage for "private" IO_DEVICE data areas.
|
|
*/
|
|
#define iod_RTCstate iod_private
|
|
#define iod_RTCraised iod_private4
|
|
|
|
/*
|
|
* Current state of the device.
|
|
*/
|
|
#define IODP_RTCIDLE 0x0000
|
|
#define IODP_RTCRUNNING 0x0001
|
|
#define IODP_RTCINTR 0x0002
|
|
|
|
/*
|
|
* The RTC operates at a user determined frequency (via a jumper plug).
|
|
* Basic time periods are:
|
|
*
|
|
* 1 uSec, 10 uSec, 100 uSec, 1 mSec, 10 mSec, 100 mSec and 1 second.
|
|
*
|
|
* We use CPU instruction execution as a proxy for generating these
|
|
* frequencies. If we assume an average execution time of 1.25 uSec (1784-2
|
|
* processor), each time period will be represented by the following
|
|
* instruction counts:
|
|
*
|
|
* 1, 8, 80, 800, 8000, 80000, 800000
|
|
*/
|
|
#define RTC_1USEC 1
|
|
#define RTC_10USEC 8
|
|
#define RTC_100USEC 80
|
|
#define RTC_1MSEC 800
|
|
#define RTC_10MSEC 8000
|
|
#define RTC_100MSEC 80000
|
|
#define RTC_1SEC 800000
|
|
|
|
struct RTCtimebase {
|
|
const char *name;
|
|
const char *rate;
|
|
int32 icount;
|
|
} timeBase[] = {
|
|
{ "1USEC", "1 uSec", RTC_1USEC },
|
|
{ "10USEC", "10 uSec", RTC_10USEC },
|
|
{ "100USEC", "100 uSec", RTC_100USEC },
|
|
{ "1MSEC", "1 mSec", RTC_1MSEC },
|
|
{ "10MSEC", "10 mSec", RTC_10MSEC },
|
|
{ "100MSEC", "100 mSec", RTC_100MSEC },
|
|
{ "1SEC", "1 Seccond", RTC_1SEC },
|
|
{ NULL }
|
|
};
|
|
|
|
/* RTC data structures
|
|
|
|
rtc_dev RTC device descriptor
|
|
rtc_unit RTC unit descriptor
|
|
rtc_reg RTC register list
|
|
rtc_mod RTC modifiers list
|
|
*/
|
|
|
|
UNIT rtc_unit = {
|
|
UDATA(&rtc_svc, 0, 0), RTC_10USEC
|
|
};
|
|
|
|
REG rtc_reg[] = {
|
|
{ HRDATAD(FUNCTION, RTCdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(COUNTER, RTCdev.COUNTER, 16, "Counter register") },
|
|
{ HRDATAD(HOLDING, RTCdev.HOLDREG, 16, "Hold register") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB rtc_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "10336-1 Real Time Clock" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", "EQUIPMENT=hexAddress",
|
|
&set_equipment, &show_addr, NULL, "Display equipment address" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={1usec|10usec|100usec|1msec|10msec|100msec|1second}",
|
|
&rtc_set_rate, &rtc_show_rate, NULL, "Show timer tick interval" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB rtc_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE rtc_dev = {
|
|
"RTC", &rtc_unit, rtc_reg, rtc_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &rtc_reset,
|
|
NULL, NULL, NULL,
|
|
&RTCdev,
|
|
DEV_DEBUG | DEV_DISABLE, 0, rtc_deb,
|
|
NULL, NULL, &rtc_help, NULL, NULL, NULL
|
|
};
|
|
|
|
t_stat rtc_show_rate(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
struct RTCtimebase *tb = timeBase;
|
|
|
|
while (tb->name != NULL) {
|
|
if (tb->icount == rtc_unit.wait) {
|
|
fprintf(st, "Timebase rate: %s", tb->rate);
|
|
return SCPE_OK;
|
|
}
|
|
tb++;
|
|
}
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
t_stat rtc_set_rate(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (cptr) {
|
|
struct RTCtimebase *tb = timeBase;
|
|
|
|
while (tb->name != NULL) {
|
|
if (!strcmp(cptr, tb->name)) {
|
|
rtc_unit.wait = tb->icount;
|
|
return SCPE_OK;
|
|
}
|
|
tb++;
|
|
}
|
|
}
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
/*
|
|
* Determine if the clock interrupt is asserted, returning the appropriate
|
|
* interrupt bit or 0.
|
|
*/
|
|
uint16 RTCraised(DEVICE *dptr)
|
|
{
|
|
IO_DEVICE *iod = (IO_DEVICE *)dptr->ctxt;
|
|
|
|
return iod->iod_RTCraised ? iod->iod_interrupt : 0;
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat rtc_svc(UNIT *uptr)
|
|
{
|
|
if (RTCdev.iod_RTCstate != IODP_RTCIDLE) {
|
|
if ((RTCdev.iod_RTCstate & IODP_RTCRUNNING) != 0) {
|
|
RTCdev.COUNTER++;
|
|
|
|
if ((RTCdev.iod_RTCstate & IODP_RTCINTR) != 0) {
|
|
if (RTCdev.COUNTER == RTCdev.HOLDREG) {
|
|
RTCdev.COUNTER = 0;
|
|
RTCdev.iod_RTCraised = TRUE;
|
|
RaiseExternalInterrupt(&rtc_dev);
|
|
}
|
|
}
|
|
sim_activate(&rtc_unit, rtc_unit.wait);
|
|
}
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat rtc_reset(DEVICE * dptr)
|
|
{
|
|
t_stat r;
|
|
|
|
if (IOFWinitialized)
|
|
if ((dptr->flags & DEV_DIS) == 0)
|
|
if ((r = checkReset(dptr, RTCdev.iod_equip)) != SCPE_OK)
|
|
return r;
|
|
|
|
RTCdev.iod_RTCstate = IODP_RTCIDLE;
|
|
RTCdev.iod_RTCraised = FALSE;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus RTCin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The framework only passes IN operations for the data register.
|
|
*/
|
|
return IO_REJECT;
|
|
}
|
|
|
|
enum IOstatus RTCout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
switch (reg) {
|
|
case 0x00:
|
|
RTCdev.HOLDREG = Areg;
|
|
break;
|
|
|
|
case 0x01:
|
|
/*
|
|
* Check for illegal bit combinations
|
|
*/
|
|
if (((Areg & (IO_10336_ENA | IO_10336_DIS)) ==
|
|
(IO_10336_ENA | IO_10336_DIS)) ||
|
|
((Areg & (IO_10336_START | IO_10336_STOP)) ==
|
|
(IO_10336_START | IO_10336_STOP)))
|
|
return IO_REJECT;
|
|
|
|
if ((Areg & IO_DIR_CCONT) != 0) {
|
|
sim_cancel(&rtc_unit);
|
|
|
|
RTCdev.iod_RTCstate = IODP_RTCIDLE;
|
|
RTCdev.iod_RTCraised = FALSE;
|
|
rebuildPending();
|
|
|
|
RTCdev.HOLDREG = 0;
|
|
RTCdev.COUNTER = 0;
|
|
}
|
|
|
|
if ((Areg & IO_10336_STOP) != 0) {
|
|
RTCdev.iod_RTCstate &= ~IODP_RTCRUNNING;
|
|
sim_cancel(&rtc_unit);
|
|
}
|
|
|
|
if ((Areg & IO_10336_START) != 0) {
|
|
RTCdev.COUNTER = 0;
|
|
RTCdev.iod_RTCstate |= IODP_RTCRUNNING;
|
|
sim_activate(&rtc_unit, rtc_unit.wait);
|
|
}
|
|
|
|
if ((Areg & IO_10336_ACK) != 0) {
|
|
RTCdev.iod_RTCraised = FALSE;
|
|
rebuildPending();
|
|
}
|
|
|
|
if ((Areg & IO_10336_DIS) != 0) {
|
|
RTCdev.iod_RTCstate &= ~IODP_RTCINTR;
|
|
RTCdev.iod_RTCraised = FALSE;
|
|
rebuildPending();
|
|
}
|
|
|
|
if ((Areg & IO_10336_ENA) != 0)
|
|
RTCdev.iod_RTCstate |= IODP_RTCINTR;
|
|
break;
|
|
}
|
|
return IO_REPLY;
|
|
}
|
|
|
|
t_stat rtc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/****************************************************************************/
|
|
" The %D device 10336-1 Real Time Clock.\n"
|
|
"1 Hardware Description\n"
|
|
" The 10336-1 is a Real Time Clock which can generate periodic interrupts\n"
|
|
" or measure elapsed time. The timer resolution is set via jumpers on the\n"
|
|
" physical hardware. For the simulator, the default resolution is\n"
|
|
" 10 uSec and can be changed by:\n\n"
|
|
"+sim> SET %D RATE=1usec\n"
|
|
"+sim> SET %D RATE=10usec\n"
|
|
"+sim> SET %D RATE=100usec\n"
|
|
"+sim> SET %D RATE=1msec\n"
|
|
"+sim> SET %D RATE=10msec\n"
|
|
"+sim> SET %D RATE=100msec\n"
|
|
"+sim> SET %D RATE=1second\n\n"
|
|
"2 Equipment Address\n"
|
|
" The %D device is set to equipment address 13. This address may be\n"
|
|
" changed by:\n\n"
|
|
"+sim> SET %D EQUIPMENT=hexValue\n\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET commands\n"
|
|
"2 $Set commands\n";
|
|
|
|
return scp_help(st, dptr, uptr, flag, helpString, cptr);
|
|
}
|