These include simulators for the IBM 701, IBM 702, IBM 704, IBM 705, IBM 705/3, IBM 709, IBM 1410/IBM 7010, IBM 7070, IBM 7080, IBM 7090 and IBM7094. These basically were a collection of machines that shared a common set it peripherals, Each group had its own instruction set, hence different simulators. IBM 701 -> i701 IBM 702/705/705/3/7080 -> i7080 IBM 7070/7074 -> i7070 IBM 1410/7010 -> i7010 IBM 704 -> i704 IBM 704/709/7090/7094 -> i7090 The i7090 can be set to simulate a IBM 704 however you end up disabling almost everything, since the 704 did not have any channels. A build option exists that allows this one to be built without all the extra features. The i7090 simulator’s implementation of the IBM 7094 is a more complete implementation of the IBM 7094 which can run CTSS while the existing simh I7094 can’t.
292 lines
8.8 KiB
C
292 lines
8.8 KiB
C
/* i7090_chron.c: IBM 7090 Chrono clock on MT drive.
|
||
|
||
Copyright (c) 2005-2016, Richard Cornwell
|
||
|
||
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
|
||
RICHARD CORNWELL 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.
|
||
|
||
|
||
*/
|
||
|
||
#include "i7000_defs.h"
|
||
#include <time.h>
|
||
|
||
#ifdef NUM_DEVS_CHRON
|
||
#define BUFFSIZE (12)
|
||
|
||
#define UNIT_MT(x) UNIT_DISABLE | UNIT_ROABLE | \
|
||
UNIT_S_CHAN(x)
|
||
|
||
/* in u3 is device address */
|
||
/* in u4 is current buffer position */
|
||
/* in u5 */
|
||
#define MT_RDS 1
|
||
#define MT_RDSB 2
|
||
#define MT_SKIP 11 /* Do skip to end of record */
|
||
#define MT_CMDMSK 000017 /* Command being run */
|
||
#define MT_RDY 000020 /* Device is ready for command */
|
||
#define MT_IDLE 000040 /* Tape still in motion */
|
||
#define MT_EOR 000200 /* Hit end of record */
|
||
#define MT_ERR 000400 /* Device recieved error */
|
||
#define MT_BOT 001000 /* Unit at begining of tape */
|
||
#define MT_EOT 002000 /* Unit at end of tape */
|
||
|
||
uint32 chron_cmd(UNIT *, uint16, uint16);
|
||
t_stat chron_srv(UNIT *);
|
||
t_stat chron_reset(DEVICE *);
|
||
t_stat set_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat get_addr(FILE * st, UNIT *uptr, int32 v, CONST void *desc);
|
||
t_stat chron_help(FILE *st, DEVICE *dptr, UNIT *uptr,
|
||
int32 flags, const char *ctxt);
|
||
const char *chron_description (DEVICE *dptr);
|
||
|
||
/* One buffer per channel */
|
||
uint8 chron_buffer[BUFFSIZE];
|
||
|
||
UNIT chron_unit[] = {
|
||
/* Controller 1 */
|
||
{UDATA(&chron_srv, UNIT_MT(1) | UNIT_DIS, 0), 10}, /* 0 */
|
||
};
|
||
|
||
MTAB chron_mod[] = {
|
||
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "UNIT", "UNIT", &set_addr, &get_addr,
|
||
NULL, "Chronoclock unit number"},
|
||
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan,
|
||
NULL, "Chronoclock channel"},
|
||
{0}
|
||
};
|
||
|
||
DEVICE chron_dev = {
|
||
"CHRON", chron_unit, NULL, chron_mod,
|
||
NUM_DEVS_CHRON, 8, 15, 1, 8, 8,
|
||
NULL, NULL, &chron_reset, NULL, NULL, NULL,
|
||
&chron_dib, DEV_DISABLE, 0, NULL,
|
||
NULL, NULL, &chron_help, NULL, NULL, &chron_description
|
||
};
|
||
|
||
uint32 chron_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
int time = 30;
|
||
int unit = (dev & 017);
|
||
|
||
/* Make sure valid drive number */
|
||
if (unit != uptr->u3)
|
||
return SCPE_NODEV;
|
||
if (uptr->flags & UNIT_DIS)
|
||
return SCPE_NODEV;
|
||
|
||
/* Check if drive is ready to recieve a command */
|
||
if ((uptr->u5 & MT_RDY) == 0) {
|
||
/* Return indication if not ready and doing TRS */
|
||
if (cmd == IO_TRS) {
|
||
return SCPE_IOERR;
|
||
} else
|
||
return SCPE_BUSY;
|
||
}
|
||
uptr->u5 &= ~(MT_CMDMSK | MT_RDY);
|
||
switch (cmd) {
|
||
case IO_RDS:
|
||
if (dev & 020)
|
||
uptr->u5 |= MT_RDSB;
|
||
else
|
||
uptr->u5 |= MT_RDS;
|
||
time = 100;
|
||
chan_set_sel(chan, 0);
|
||
chan_clear_status(chan);
|
||
uptr->u6 = 0;
|
||
break;
|
||
case IO_WRS:
|
||
/* Can't write to it so return error */
|
||
return SCPE_IOERR;
|
||
|
||
case IO_BSR: /* Nop, just set us back at begining */
|
||
case IO_BSF: /* Nop, just set flag and leave */
|
||
chan_set(chan, CHS_BOT);
|
||
/* All nops, just return success */
|
||
case IO_WEF:
|
||
case IO_REW:
|
||
case IO_RUN:
|
||
case IO_SDL:
|
||
case IO_SDH:
|
||
case IO_TRS:
|
||
return SCPE_OK;
|
||
}
|
||
sim_cancel(uptr);
|
||
sim_activate(uptr, us_to_ticks(time));
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Chronolog clock */
|
||
|
||
/* Convert number (0-99) to BCD */
|
||
|
||
static void
|
||
bcd_2d(int n, uint8 * b2)
|
||
{
|
||
uint8 d1, d2;
|
||
|
||
d1 = n / 10;
|
||
d2 = n % 10;
|
||
*b2++ = d1;
|
||
*b2 = d2;
|
||
}
|
||
|
||
void
|
||
chron_read_buff(UNIT * uptr, int cmd)
|
||
{
|
||
time_t curtim;
|
||
struct tm *tptr;
|
||
int ms;
|
||
|
||
uptr->u6 = 0; /* Set to no data */
|
||
|
||
curtim = time(NULL); /* get time */
|
||
tptr = localtime(&curtim); /* decompose */
|
||
if (tptr == NULL)
|
||
return; /* error? */
|
||
|
||
ms = sim_os_msec() % 1000;
|
||
ms /= 100;
|
||
|
||
/* Convert and fill buffer */
|
||
bcd_2d(tptr->tm_mon + 1, &chron_buffer[0]);
|
||
bcd_2d(tptr->tm_mday, &chron_buffer[2]);
|
||
bcd_2d(tptr->tm_hour, &chron_buffer[4]);
|
||
bcd_2d(tptr->tm_min, &chron_buffer[6]);
|
||
bcd_2d(tptr->tm_sec, &chron_buffer[8]);
|
||
bcd_2d(ms, &chron_buffer[10]);
|
||
return;
|
||
}
|
||
|
||
t_stat chron_srv(UNIT * uptr)
|
||
{
|
||
int chan = UNIT_G_CHAN(uptr->flags);
|
||
int cmd = uptr->u5 & MT_CMDMSK;
|
||
|
||
/* Channel has disconnected, abort current read. */
|
||
if ((uptr->u5 & MT_RDY) == 0 && chan_stat(chan, DEV_DISCO)) {
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
if (cmd == MT_RDS || cmd == MT_RDSB) {
|
||
uptr->u6 = 0;
|
||
}
|
||
uptr->u5 |= MT_RDY;
|
||
chan_clear(chan, DEV_WEOR|DEV_SEL);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
switch (uptr->u5 & MT_CMDMSK) {
|
||
case 0: /* No command, stop tape */
|
||
uptr->u5 |= MT_RDY; /* Ready since command is done */
|
||
break;
|
||
|
||
case MT_SKIP: /* Record skip done, enable tape drive */
|
||
sim_activate(uptr, us_to_ticks(500));
|
||
break;
|
||
|
||
case MT_RDS:
|
||
case MT_RDSB:
|
||
if (uptr->u6 == 0)
|
||
chron_read_buff(uptr, cmd);
|
||
switch (chan_write_char(chan, &chron_buffer[uptr->u6],
|
||
(uptr->u6 == (BUFFSIZE-1)) ? DEV_REOR : 0)) {
|
||
case DATA_OK:
|
||
uptr->u6++;
|
||
sim_activate(uptr, us_to_ticks(100));
|
||
break;
|
||
|
||
case END_RECORD:
|
||
case TIME_ERROR:
|
||
uptr->u5 &= ~MT_CMDMSK;
|
||
uptr->u5 |= MT_SKIP;
|
||
sim_activate(uptr, us_to_ticks(100));
|
||
uptr->u6 = 0; /* Force read next record */
|
||
break;
|
||
}
|
||
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
chron_reset(DEVICE * dptr)
|
||
{
|
||
chron_unit[0].u5 = MT_RDY;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Sets the address of the chrono clock */
|
||
t_stat
|
||
set_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
int i;
|
||
|
||
if (cptr == NULL)
|
||
return SCPE_ARG;
|
||
if (uptr == NULL)
|
||
return SCPE_IERR;
|
||
if (uptr->flags & UNIT_ATT)
|
||
return SCPE_ALATT;
|
||
i = 0;
|
||
while (*cptr != '\0') {
|
||
if (*cptr < '0' || *cptr > '9')
|
||
return SCPE_ARG;
|
||
i = (i * 10) + (*cptr++) - '0';
|
||
}
|
||
if (i < 0 || i > 10)
|
||
return SCPE_ARG;
|
||
uptr->u3 = i;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
get_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc)
|
||
{
|
||
if (uptr == NULL)
|
||
return SCPE_IERR;
|
||
fprintf(st, "Unit=%d", uptr->u3);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
chron_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf (st, "Chronoclock\n\n");
|
||
fprintf (st, "The Chronoclock replaces one of your tape drives, and is\n");
|
||
fprintf (st, "for CTSS operation\n\n");
|
||
fprintf (st, " sim> SET %s ENABLE to enable chronoclock\n", dptr->name);
|
||
fprintf (st, " sim> SET %s UNIT=# sets unit to override [0-9]\n\n", dptr->name);
|
||
help_set_chan_type(st, dptr, "Chrono clock");
|
||
fprintf (st, "You must disable the corrosponding tape drive in order for\n");
|
||
fprintf (st, "the chronoclook to be seen. The chronoclock replaces one of\n");
|
||
fprintf (st, "your tape drives, and by reading the tape drive, it will\n");
|
||
fprintf (st, "return a short record with the current date and time, no year\n");
|
||
fprintf (st, "is returned\n");
|
||
|
||
fprint_set_help (st, dptr) ;
|
||
fprint_show_help (st, dptr) ;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *
|
||
chron_description (DEVICE *dptr)
|
||
{
|
||
return "Chronoclock";
|
||
}
|
||
|
||
|
||
#endif
|