| /* | |
| 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_debug(FILE *, UNIT *, int32, CONST void *); | |
| 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 *); | |
| /* | |
| 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, | |
| 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_10MSEC | |
| }; | |
| REG rtc_reg[] = { | |
| { HRDATA(FUNCTION, RTCdev.FUNCTION, 16) }, | |
| { HRDATA(COUNTER, RTCdev.COUNTER, 16) }, | |
| { HRDATA(HOLDING, RTCdev.HOLDREG, 16) }, | |
| { NULL } | |
| }; | |
| MTAB rtc_mod[] = { | |
| { MTAB_XTD|MTAB_VDV, 0, "10336-1", NULL, NULL, NULL }, | |
| { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "RATE", &rtc_set_rate, NULL, NULL, | |
| "val={1usec|10usec|100usec|1msec|10msec|100msec|1second}" }, | |
| { MTAB_XTD|MTAB_VDV, 0, "RATE", NULL, NULL, &rtc_show_rate, NULL, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL, NULL, &show_addr, NULL }, | |
| { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "EQUIPMENT", &set_equipment, NULL, NULL }, | |
| { MTAB_XTD|MTAB_VDV, 0, "DEBUG", NULL, NULL, &show_debug, NULL }, | |
| { 0 } | |
| }; | |
| DEBTAB rtc_deb[] = { | |
| { "TRACE", DBG_DTRACE }, | |
| { 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 | |
| }; | |
| 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; | |
| } |