From b5ea9ec38e0e915fa332ae542ce549f5d97a48d5 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Thu, 28 Dec 2017 05:05:25 -0800 Subject: [PATCH] I7000: Initial release of a set of simulators for IBM 7000 series mainframes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- I7000/README.md | 54 + I7000/i7000_cdp.c | 363 +++ I7000/i7000_cdr.c | 408 +++ I7000/i7000_chan.c | 511 +++ I7000/i7000_chron.c | 292 ++ I7000/i7000_com.c | 1317 ++++++++ I7000/i7000_con.c | 279 ++ I7000/i7000_defs.h | 642 ++++ I7000/i7000_dsk.c | 1887 ++++++++++++ I7000/i7000_ht.c | 992 ++++++ I7000/i7000_lpr.c | 529 ++++ I7000/i7000_mt.c | 1398 +++++++++ I7000/i7010_chan.c | 727 +++++ I7000/i7010_cpu.c | 3957 ++++++++++++++++++++++++ I7000/i7010_defs.h | 133 + I7000/i7010_sys.c | 1250 ++++++++ I7000/i701_chan.c | 416 +++ I7000/i701_cpu.c | 961 ++++++ I7000/i701_sys.c | 477 +++ I7000/i7070_chan.c | 1597 ++++++++++ I7000/i7070_cpu.c | 3001 ++++++++++++++++++ I7000/i7070_defs.h | 241 ++ I7000/i7070_sys.c | 1100 +++++++ I7000/i7080_chan.c | 1250 ++++++++ I7000/i7080_cpu.c | 3426 +++++++++++++++++++++ I7000/i7080_defs.h | 103 + I7000/i7080_drum.c | 195 ++ I7000/i7080_sys.c | 729 +++++ I7000/i7090_cdp.c | 302 ++ I7000/i7090_cdr.c | 339 ++ I7000/i7090_chan.c | 1710 +++++++++++ I7000/i7090_cpu.c | 4435 +++++++++++++++++++++++++++ I7000/i7090_defs.h | 437 +++ I7000/i7090_drum.c | 287 ++ I7000/i7090_hdrum.c | 253 ++ I7000/i7090_lpr.c | 696 +++++ I7000/i7090_sys.c | 1056 +++++++ README.md | 4 +- Visual Studio Projects/I701.vcproj | 351 +++ Visual Studio Projects/I7010.vcproj | 367 +++ Visual Studio Projects/I704.vcproj | 351 +++ Visual Studio Projects/I7070.vcproj | 367 +++ Visual Studio Projects/I7080.vcproj | 371 +++ Visual Studio Projects/I7090.vcproj | 371 +++ Visual Studio Projects/Simh.sln | 51 + doc/i7010_doc.doc | Bin 0 -> 193024 bytes doc/i701_doc.doc | Bin 0 -> 146432 bytes doc/i7070_doc.doc | Bin 0 -> 190976 bytes doc/i7080_doc.doc | Bin 0 -> 200704 bytes doc/i7090_doc.doc | Bin 0 -> 229376 bytes makefile | 83 +- 51 files changed, 40064 insertions(+), 2 deletions(-) create mode 100644 I7000/README.md create mode 100644 I7000/i7000_cdp.c create mode 100644 I7000/i7000_cdr.c create mode 100644 I7000/i7000_chan.c create mode 100644 I7000/i7000_chron.c create mode 100644 I7000/i7000_com.c create mode 100644 I7000/i7000_con.c create mode 100644 I7000/i7000_defs.h create mode 100644 I7000/i7000_dsk.c create mode 100644 I7000/i7000_ht.c create mode 100644 I7000/i7000_lpr.c create mode 100644 I7000/i7000_mt.c create mode 100644 I7000/i7010_chan.c create mode 100644 I7000/i7010_cpu.c create mode 100644 I7000/i7010_defs.h create mode 100644 I7000/i7010_sys.c create mode 100644 I7000/i701_chan.c create mode 100644 I7000/i701_cpu.c create mode 100644 I7000/i701_sys.c create mode 100644 I7000/i7070_chan.c create mode 100644 I7000/i7070_cpu.c create mode 100644 I7000/i7070_defs.h create mode 100644 I7000/i7070_sys.c create mode 100644 I7000/i7080_chan.c create mode 100644 I7000/i7080_cpu.c create mode 100644 I7000/i7080_defs.h create mode 100644 I7000/i7080_drum.c create mode 100644 I7000/i7080_sys.c create mode 100644 I7000/i7090_cdp.c create mode 100644 I7000/i7090_cdr.c create mode 100644 I7000/i7090_chan.c create mode 100644 I7000/i7090_cpu.c create mode 100644 I7000/i7090_defs.h create mode 100644 I7000/i7090_drum.c create mode 100644 I7000/i7090_hdrum.c create mode 100644 I7000/i7090_lpr.c create mode 100644 I7000/i7090_sys.c create mode 100644 Visual Studio Projects/I701.vcproj create mode 100644 Visual Studio Projects/I7010.vcproj create mode 100644 Visual Studio Projects/I704.vcproj create mode 100644 Visual Studio Projects/I7070.vcproj create mode 100644 Visual Studio Projects/I7080.vcproj create mode 100644 Visual Studio Projects/I7090.vcproj create mode 100644 doc/i7010_doc.doc create mode 100644 doc/i701_doc.doc create mode 100644 doc/i7070_doc.doc create mode 100644 doc/i7080_doc.doc create mode 100644 doc/i7090_doc.doc diff --git a/I7000/README.md b/I7000/README.md new file mode 100644 index 00000000..16a901ff --- /dev/null +++ b/I7000/README.md @@ -0,0 +1,54 @@ +Latest status for I7000 Cpus: + +## i701 + + * Largely untested. + +## i704 + * SAP works. + * Fort2 unfinished. + +## i7010 + * PR155 works. + * PR108 works. + * Most Diags appear to pass without serious error. + * Protection mode has some errors left. + * Protection mode does not handle setting H or L to 0. + +## i7070 + * Will load Diags, need to remember how to run them to run + * tests on machine. + +## i7080 + * Sort of working. + * RWW, ECB untested. + * TLx instructions implimented, untested, see 8SE + * Will boot from card. + * Tape system appear to be working. + + * 8CU10B errors: + 410, 412, 413, 414, 418, 419, 420-427 error becuase + storage is not parity checked. + 440 divide not producing correct sign on error. + +## i7090 + * Working with exceptions. + + * Known bugs: + + * DFDP/DFMP Sometimes off by +/-1 or 2 in least signifigant part of result. + * EAD +n + -n should be -0 is +0 + * Not all channel skips working for 9P01C. + * HTx Not sure what problems are, does not quite work. + * DKx Sometimes fails diagnostics with missing inhibit of interrupt. + + * CTSS works. + + * IBSYS works. + + * Stand alone assembler works. + + * Lisp 1.5 works. + + * Signifigence mode Not tested, Test Code Needed. + diff --git a/I7000/i7000_cdp.c b/I7000/i7000_cdp.c new file mode 100644 index 00000000..e471ccea --- /dev/null +++ b/I7000/i7000_cdp.c @@ -0,0 +1,363 @@ +/* i7000_cdp.c: IBM 7000 Card Punch + + 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. + + This is the standard card punch. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as BCD characters. + +*/ + +#include "i7000_defs.h" +#include "sim_card.h" +#include "sim_defs.h" +#ifdef NUM_DEVS_CDP + +#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_026 + + +/* Flags for punch and reader. */ +#define ATTENA (1 << (UNIT_V_UF+7)) +#define ATTENB (1 << (UNIT_V_UF+14)) + + +/* std devices. data structures + + cdp_dev Card Punch device descriptor + cdp_unit Card Punch unit descriptor + cdp_reg Card Punch register list + cdp_mod Card Punch modifiers list +*/ + +uint32 cdp_cmd(UNIT *, uint16, uint16); +void cdp_ini(UNIT *, t_bool); +t_stat cdp_srv(UNIT *); +t_stat cdp_reset(DEVICE *); +t_stat cdp_attach(UNIT *, CONST char *); +t_stat cdp_detach(UNIT *); +t_stat cdp_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *cdp_description(DEVICE *dptr); +t_stat stk_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *stk_description(DEVICE *dptr); + +UNIT cdp_unit[] = { + {UDATA(cdp_srv, UNIT_S_CHAN(CHAN_CHUREC) | UNIT_CDP, 0), 600}, /* A */ +#if NUM_DEVS_CDP > 1 + {UDATA(cdp_srv, UNIT_S_CHAN(CHAN_CHUREC+1) | UNIT_CDP, 0), 600}, /* B */ +#endif +}; + +MTAB cdp_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL, "Set card format"}, +#ifdef I7070 + {ATTENA|ATTENB,0, NULL, "NOATTEN", NULL, NULL, NULL, "No attention signal"}, + {ATTENA|ATTENB,ATTENA, "ATTENA", "ATTENA", NULL, NULL, NULL, "Signal Attention A"}, + {ATTENA|ATTENB,ATTENB, "ATTENB", "ATTENB", NULL, NULL, NULL, "Signal Attention B"}, +#endif +#ifdef I7010 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL, "Set device channel"}, +#endif + {0} +}; + +DEVICE cdp_dev = { + "CDP", cdp_unit, NULL, cdp_mod, + NUM_DEVS_CDP, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, + &cdp_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, + NULL, NULL, &cdp_help, NULL, NULL, &cdp_description +}; + +#ifdef STACK_DEV +UNIT stack_unit[] = { + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + }; + +DEVICE stack_dev = { + "STKR", stack_unit, NULL, NULL, + NUM_DEVS_CDP * 10, 10, 31, 1, 8, 7, + NULL, NULL, NULL, NULL, &sim_card_attach, &sim_card_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, + NULL, NULL, &stk_help, NULL, NULL, &stk_description + }; +#endif + + + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + + +uint32 cdp_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdp_unit); + int stk = dev & 017; + + /* Are we currently tranfering? */ + if (uptr->u5 & URCSTA_WRITE) + return SCPE_BUSY; + + if (stk == 10) + stk = 0; + if ((uptr->flags & UNIT_ATT) == 0) { +#ifdef STACK_DEV + if ((stack_unit[stk * u].flags & UNIT_ATT) == 0) +#endif + return SCPE_IOERR; + } + + switch(cmd) { + /* Test ready */ + case IO_TRS: + sim_debug(DEBUG_CMD, &cdp_dev, "%d: Cmd TRS\n", u); + return SCPE_OK; + + /* Suppress punch */ + case IO_RUN: + uptr->u5 &= ~URCSTA_FULL; + sim_debug(DEBUG_CMD, &cdp_dev, "%d: Cmd RUN\n", u); + return SCPE_OK; + + /* Retieve data from CPU */ + case IO_WRS: +#ifdef STACK_DEV + uptr->u5 &= ~0xF0000; + uptr->u5 |= stk << 16; +#endif + sim_debug(DEBUG_CMD, &cdp_dev, "%d: Cmd WRS\n", u); + chan_set_sel(chan, 1); + uptr->u5 |= URCSTA_WRITE; + uptr->u4 = 0; + if ((uptr->u5 & URCSTA_BUSY) == 0) + sim_activate(uptr, 50); + return SCPE_OK; + } + chan_set_attn(chan); + return SCPE_IOERR; +} + +/* Handle transfer of data for card punch */ +t_stat +cdp_srv(UNIT *uptr) { + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdp_unit); + /* Waiting for disconnect */ + if (uptr->u5 & URCSTA_WDISCO) { + if (chan_stat(chan, DEV_DISCO)) { + chan_clear(chan, DEV_SEL|DEV_WEOR); + uptr->u5 &= ~ URCSTA_WDISCO; + } else { + /* No disco yet, try again in a bit */ + sim_activate(uptr, 50); + return SCPE_OK; + } + /* If still busy, schedule another wait */ + if (uptr->u5 & URCSTA_BUSY) + sim_activate(uptr, uptr->wait); + } + + if (uptr->u5 & URCSTA_BUSY) { + /* Done waiting, punch card */ + if (uptr->u5 & URCSTA_FULL) { +#ifdef STACK_DEV + switch(sim_punch_card(uptr, + &stack_unit[(u * 10) + ((uptr->u5 >> 16) & 0xf)])) { +#else + switch(sim_punch_card(uptr, NULL)) { +#endif + case SCPE_EOF: + case SCPE_UNATT: + chan_set_eof(chan); + break; + /* If we get here, something is wrong */ + case SCPE_IOERR: + chan_set_error(chan); + break; + case SCPE_OK: + break; + } + uptr->u5 &= ~URCSTA_FULL; + } + uptr->u5 &= ~URCSTA_BUSY; +#ifdef I7070 + switch(uptr->flags & (ATTENA|ATTENB)) { + case ATTENA: chan_set_attn_a(chan); break; + case ATTENB: chan_set_attn_b(chan); break; + } +#endif +#ifdef I7010 + chan_set_attn_urec(chan, cdp_dib.addr); +#endif + } + + /* Copy next column over */ + if (uptr->u5 & URCSTA_WRITE && uptr->u4 < 80) { + struct _card_data *data; + uint8 ch = 0; + + data = (struct _card_data *)uptr->up7; + + switch(chan_read_char(chan, &ch, 0)) { + case TIME_ERROR: + case END_RECORD: + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY|URCSTA_FULL; + uptr->u5 &= ~URCSTA_WRITE; + break; + case DATA_OK: + if (ch == 0) + ch = 020; + else if (ch == 020) + ch = 0; + sim_debug(DEBUG_DATA, &cdp_dev, "%d: Char < %02o\n", u, ch); + data->image[uptr->u4++] = sim_bcd_to_hol(ch); + if (uptr->u4 == 80) { + chan_set(chan, DEV_REOR); + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY|URCSTA_FULL; + uptr->u5 &= ~URCSTA_WRITE; + } + break; + } + sim_activate(uptr, 10); + } + return SCPE_OK; +} + + +void +cdp_ini(UNIT *uptr, t_bool f) { +} + +t_stat +cdp_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + return SCPE_OK; +} + +t_stat +cdp_detach(UNIT * uptr) +{ + if (uptr->u5 & URCSTA_FULL) +#ifdef STACK_DEV + sim_punch_card(uptr, &stack_unit[ + ((uptr - cdp_unit) * 10) + ((uptr->u5 >> 16) & 0xf)]); +#else + sim_punch_card(uptr, NULL); +#endif + return sim_card_detach(uptr); +} + +#ifdef STACK_DEV +t_stat +stk_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", stk_description(dptr)); + fprintf (st, "Allows stack control functions to direct cards to specific "); + fprintf (st, "bins based on stacker\nselection. Attach cards here if you "); + fprintf (st, "wish this specific stacker select to recieve\nthis group of"); + fprintf (st, " cards. If nothing is attached cards will be punched on the"); + fprintf (st, " default\npunch\n\n"); + sim_card_attach_help(st, dptr, uptr, flag, cptr); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +stk_description(DEVICE *dptr) +{ + return "Card stacking device"; +} +#endif + +t_stat +cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", cdp_description(dptr)); +#ifdef STACK_DEV + fprintf (st, "If the punch device is not attached and instead the %s ", stack_dev.name); + fprintf (st, "device is attached,\nthe cards will be sent out to the "); + fprintf (st, "given stacker based on the flag set by\nthe processor.\n\n"); +#endif +#ifdef I7070 + fprintf (st, "Unit record devices can be configured to interrupt the CPU on\n"); + fprintf (st, "one of two priority channels A or B, to set this\n\n"); + fprintf (st, " sim> SET %s ATTENA to set device to raise Atten A\n\n", dptr->name); +#endif +#ifdef I7010 + help_set_chan_type(st, dptr, "Card punches"); +#endif + sim_card_attach_help(st, dptr, uptr, flag, cptr); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cdp_description(DEVICE *dptr) +{ +#ifdef I7010 + return "1402 Card Punch"; +#endif +#ifdef I7070 + return "7550 Card Punch"; +#endif +#ifdef I7080 + return "721 Card Punch"; +#endif +} + +#endif + diff --git a/I7000/i7000_cdr.c b/I7000/i7000_cdr.c new file mode 100644 index 00000000..17063a95 --- /dev/null +++ b/I7000/i7000_cdr.c @@ -0,0 +1,408 @@ +/* i7000_cdr.c: IBM 7000 Card reader. + + 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. + + This is the standard card reader. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as BCD characters. + +*/ + +#include "i7000_defs.h" +#include "sim_card.h" +#include "sim_defs.h" +#ifdef NUM_DEVS_CDR + +#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | \ + UNIT_ROABLE | MODE_026 + +/* Flags for punch and reader. */ +#define ATTENA (1 << (UNIT_V_UF+7)) +#define ATTENB (1 << (UNIT_V_UF+14)) + + +/* std devices. data structures + + cdr_dev Card Reader device descriptor + cdr_unit Card Reader unit descriptor + cdr_reg Card Reader register list + cdr_mod Card Reader modifiers list +*/ + +uint32 cdr_cmd(UNIT *, uint16, uint16); +t_stat cdr_boot(int32, DEVICE *); +t_stat cdr_srv(UNIT *); +t_stat cdr_reset(DEVICE *); +t_stat cdr_attach(UNIT *, CONST char *); +t_stat cdr_detach(UNIT *); +extern t_stat chan_boot(int32, DEVICE *); +#ifdef I7070 +t_stat cdr_setload(UNIT *, int32, CONST char *, void *); +t_stat cdr_getload(FILE *, UNIT *, int32, CONST void *); +#endif +t_stat cdr_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *cdr_description(DEVICE *dptr); + +UNIT cdr_unit[] = { + {UDATA(cdr_srv, UNIT_S_CHAN(CHAN_CHUREC) | UNIT_CDR, 0), 300}, /* A */ +#if NUM_DEVS_CDR > 1 + {UDATA(cdr_srv, UNIT_S_CHAN(CHAN_CHUREC+1) | UNIT_CDR, 0), 300}, /* B */ +#endif +}; + +MTAB cdr_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL, "Set card format"}, +#ifdef I7070 + {ATTENA|ATTENB, 0, NULL, "NOATTEN", NULL, NULL, NULL, "No attention signal"}, + {ATTENA|ATTENB, ATTENA, "ATTENA", "ATTENA", NULL, NULL, NULL, "Signal Attention A"}, + {ATTENA|ATTENB, ATTENB, "ATTENB", "ATTENB", NULL, NULL, NULL, "Signal Attention B"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "LCOL", "LCOL", &cdr_setload, + &cdr_getload, NULL, "Load card column indicator"}, +#endif +#ifdef I7010 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL, "Set device channel"}, +#endif + {0} +}; + +DEVICE cdr_dev = { + "CDR", cdr_unit, NULL, cdr_mod, + NUM_DEVS_CDR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, &cdr_boot, &cdr_attach, &sim_card_detach, + &cdr_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, + NULL, NULL, &cdr_help, NULL, NULL, &cdr_description +}; + + +/* + * Device entry points for card reader. + */ +uint32 cdr_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdr_unit); + int stk = dev & 017; + + /* Are we currently tranfering? */ + if (uptr->u5 & URCSTA_READ) + return SCPE_BUSY; + + /* Test ready */ + if (cmd == IO_TRS && uptr->flags & UNIT_ATT) { + sim_debug(DEBUG_CMD, &cdr_dev, "%d: Test Rdy\n", u); + return SCPE_OK; + } + + if (stk == 10) + stk = 0; + +#ifdef STACK_DEV + uptr->u5 &= ~0xF0000; + uptr->u5 |= stk << 16; +#endif + if (uptr->u5 & (URCSTA_EOF|URCSTA_ERR)) + return SCPE_IOERR; + + /* Process commands */ + switch(cmd) { + case IO_RDS: + sim_debug(DEBUG_CMD, &cdr_dev, "%d: Cmd RDS %02o\n", u, dev & 077); +#ifdef I7010 + if (stk!= 9) +#endif + uptr->u5 &= ~(URCSTA_CARD|URCSTA_ERR); + break; + case IO_CTL: + sim_debug(DEBUG_CMD, &cdr_dev, "%d: Cmd CTL %02o\n", u, dev & 077); +#ifdef I7010 + uptr->u5 |= URCSTA_NOXFER; +#endif + break; + default: + chan_set_attn(chan); + return SCPE_IOERR; + } + + /* If at eof, just return EOF */ + if (uptr->u5 & URCSTA_EOF) { + chan_set_eof(chan); + chan_set_attn(chan); + return SCPE_OK; + } + + uptr->u5 |= URCSTA_READ; + uptr->u4 = 0; + + if ((uptr->u5 & URCSTA_NOXFER) == 0) + chan_set_sel(chan, 0); + /* Wake it up if not busy */ + if ((uptr->u5 & URCSTA_BUSY) == 0) + sim_activate(uptr, 50); + return SCPE_OK; +} + +/* Handle transfer of data for card reader */ +t_stat +cdr_srv(UNIT *uptr) { + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdr_unit); + struct _card_data *data; + + data = (struct _card_data *)uptr->up7; + + /* Waiting for disconnect */ + if (uptr->u5 & URCSTA_WDISCO) { + if (chan_stat(chan, DEV_DISCO)) { + chan_clear(chan, DEV_SEL|DEV_WEOR); + uptr->u5 &= ~ URCSTA_WDISCO; + } else { + /* No disco yet, try again in a bit */ + sim_activate(uptr, 50); + return SCPE_OK; + } + /* If still busy, schedule another wait */ + if (uptr->u5 & URCSTA_BUSY) + sim_activate(uptr, uptr->wait); + } + + if (uptr->u5 & URCSTA_BUSY) { + uptr->u5 &= ~URCSTA_BUSY; +#ifdef I7070 + switch(uptr->flags & (ATTENA|ATTENB)) { + case ATTENA: chan_set_attn_a(chan); break; + case ATTENB: chan_set_attn_b(chan); break; + } +#endif + } + + /* Check if new card requested. */ + if (uptr->u4 == 0 && uptr->u5 & URCSTA_READ && + (uptr->u5 & URCSTA_CARD) == 0) { + switch(sim_read_card(uptr)) { + case SCPE_EOF: + sim_debug(DEBUG_DETAIL, &cdr_dev, "%d: EOF\n", u); + /* Fall through */ + + case SCPE_UNATT: + chan_set_eof(chan); + chan_set_attn(chan); + chan_clear(chan, DEV_SEL); + uptr->u5 |= URCSTA_EOF; + uptr->u5 &= ~(URCSTA_BUSY|URCSTA_READ); + return SCPE_OK; + case SCPE_IOERR: + sim_debug(DEBUG_DETAIL, &cdr_dev, "%d: ERF\n", u); + uptr->u5 |= URCSTA_ERR; + uptr->u5 &= ~(URCSTA_BUSY|URCSTA_READ); + chan_set_attn(chan); + chan_clear(chan, DEV_SEL); + return SCPE_OK; + case SCPE_OK: + uptr->u5 |= URCSTA_CARD; +#ifdef I7010 + chan_set_attn_urec(chan, cdr_dib.addr); +#endif + break; + } +#ifdef I7070 + /* Check if load card. */ + if (uptr->capac && (data->image[uptr->capac-1] & 0x800)) { + uptr->u5 |= URCSTA_LOAD; + chan_set_load_mode(chan); + } else { + uptr->u5 &= ~URCSTA_LOAD; + } +#endif + } + + if (uptr->u5 & URCSTA_NOXFER) { + uptr->u5 &= ~(URCSTA_NOXFER|URCSTA_READ); + return SCPE_OK; + } + + /* Copy next column over */ + if (uptr->u5 & URCSTA_READ && uptr->u4 < 80) { + uint8 ch = 0; + +#ifdef I7080 + /* Detect RSU */ + if (data->image[uptr->u4] == 0x924) { + uptr->u5 &= ~URCSTA_READ; + uptr->u5 |= URCSTA_WDISCO; + chan_set(chan, DEV_REOR); + sim_activate(uptr, 10); + return SCPE_OK; + } +#endif + + ch = sim_hol_to_bcd(data->image[uptr->u4]); + + /* Handle invalid punch */ + if (ch == 0x7f) { +#ifdef I7080 + uptr->u5 &= ~(URCSTA_READ|URCSTA_BUSY); + sim_debug(DEBUG_DETAIL, &cdr_dev, "%d: bad punch %d\n", u, + uptr->u4); + chan_set_attn(chan); + chan_set_error(chan); + chan_clear(chan, DEV_SEL); +#else + uptr->u5 |= URCSTA_ERR; + ch = 017; +#endif + } + +#ifdef I7070 + /* During load, only sign on every 10 columns */ + if (uptr->u5 & URCSTA_LOAD && (uptr->u4 % 10) != 9) + ch &= 0xf; +#endif + + switch(chan_write_char(chan, &ch, (uptr->u4 == 79)? DEV_REOR: 0)) { + case TIME_ERROR: + case END_RECORD: + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY; + uptr->u5 &= ~URCSTA_READ; + break; + case DATA_OK: + uptr->u4++; + break; + } + sim_debug(DEBUG_DATA, &cdr_dev, "%d: Char > %02o\n", u, ch); + sim_activate(uptr, 10); + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +cdr_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + t_stat r; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + /* Read in one record */ + r = cdr_cmd(uptr, IO_RDS, cdr_dib.addr); + if (r != SCPE_OK) + return r; + r = chan_boot(unit_num, dptr); + return r; +} + +t_stat +cdr_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 &= URCSTA_BUSY|URCSTA_WDISCO; + uptr->u4 = 0; + uptr->u6 = 0; +#ifdef I7010 + chan_set_attn_urec(UNIT_G_CHAN(uptr->flags), cdr_dib.addr); +#endif + return SCPE_OK; +} +#ifdef I7070 +t_stat +cdr_setload(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = 0; + while(*cptr != '\0') { + if (*cptr < '0' || *cptr > '9') + return SCPE_ARG; + i = (i * 10) + (*cptr++) - '0'; + } + if (i > 80) + return SCPE_ARG; + uptr->capac = i; + return SCPE_OK; +} + +t_stat +cdr_getload(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "loadcolumn=%d", uptr->capac); + return SCPE_OK; +} +#endif + +t_stat +cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", cdr_description(dptr)); +#if NUM_DEVS_CDR > 1 + fprintf (st, "The system supports up to two card readers.\n"); +#else + fprintf (st, "The system supports one card reader.\n"); +#endif +#ifdef I7070 + fprintf (st, "Unit record devices can be configured to interrupt the CPU on\n"); + fprintf (st, "one of two priority channels A or B, to set this\n\n"); + fprintf (st, " sim> SET %s ATTENA To set device to raise Atten A\n\n", dptr->name); + fprintf (st, "The 7500 Card reader supported a load mode, this was\n"); + fprintf (st, "selected by use of a 12 punch in a given column. When this\n"); + fprintf (st, "was seen the card was read into 8 words. Normal read is\n"); + fprintf (st, "text only\n\n"); + fprintf (st, " sim> SET %s LCOL=72 Sets column to select load mode\n\n", dptr->name); +#endif +#if NUM_DEVS_CDR > 1 +#ifdef I7010 + help_set_chan_type(st, dptr, "Card reader"); +#endif +#endif + sim_card_attach_help(st, dptr, uptr, flag, cptr); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cdr_description(DEVICE *dptr) +{ +#ifdef I7010 + return "1402 Card Reader"; +#endif +#ifdef I7070 + return "7500 Card Reader"; +#endif +#ifdef I7080 + return "711 Card Reader"; +#endif +} + +#endif + diff --git a/I7000/i7000_chan.c b/I7000/i7000_chan.c new file mode 100644 index 00000000..9436af1f --- /dev/null +++ b/I7000/i7000_chan.c @@ -0,0 +1,511 @@ +/* i7000_chan.c: IBM 7000 Channel simulator + + 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. + + channel + + Common routines for handling channel functions. + +*/ + +#include "i7000_defs.h" + +extern DEVICE *sim_devices[]; + + + +int num_devs[NUM_CHAN]; + + +t_stat +chan_set_devs(DEVICE * dptr) +{ + int i; + + for(i = 0; i < NUM_CHAN; i++) { + num_devs[i] = 0; + } + /* Build channel array */ + for (i = 0; sim_devices[i] != NULL; i++) { + UNIT *uptr = sim_devices[i]->units; + DIB *dibp = (DIB *) sim_devices[i]->ctxt; + int ctype; + int num; + + /* If no DIB, not channel device */ + if (dibp == NULL) + continue; + /* Skip channel devices */ + if (sim_devices[i] == &chan_dev) + continue; + /* Skip disabled devices */ + if (sim_devices[i]->flags & DEV_DIS) + continue; + ctype = dibp->ctype; + if (dibp->upc > 1) { + int chan = UNIT_G_CHAN(uptr->flags); + int type = CHAN_G_TYPE(chan_unit[chan].flags); + if (((1 << type) & ctype) == 0) { + if ((chan_unit[chan].flags & CHAN_SET) || + ((chan_unit[chan].flags & CHAN_AUTO) + && num_devs[chan] != 0)) { + for (num = sim_devices[i]->numunits; num > 0; num--) + (uptr++)->flags |= UNIT_DIS; + goto nextdev; + } + } + /* Set channel to highest type */ + if ((chan_unit[chan].flags & CHAN_SET) == 0) { + /* Set type to highest found */ + for(type = 7; type >=0; type--) + if (ctype & (1 << type)) + break; + chan_unit[chan].flags &= ~(CHAN_MODEL); + chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; + } + num_devs[chan] += sim_devices[i]->numunits; + if (dibp->ini != NULL) { + for (num = sim_devices[i]->numunits; num > 0; num--) { + uptr->flags &= ~UNIT_CHAN; + uptr->flags |= UNIT_S_CHAN(chan); + dibp->ini(uptr++, 1); + } + } + goto nextdev; + } + + for (num = sim_devices[i]->numunits; num > 0; num--) { + int chan = UNIT_G_CHAN(uptr->flags); + int type = CHAN_G_TYPE(chan_unit[chan].flags); + + if ((uptr->flags & UNIT_DIS) == 0) { + if (((1 << type) & ctype) == 0) { + if ((chan_unit[chan].flags & CHAN_SET) || + ((chan_unit[chan].flags & CHAN_AUTO) + && num_devs[chan] != 0)) { + uptr->flags |= UNIT_DIS; + goto next; + } + } + /* Set channel to highest type */ + if ((chan_unit[chan].flags & CHAN_SET) == 0) { + /* Set type to highest found */ + for(type = 7; type >=0; type--) + if (ctype & (1 << type)) + break; + chan_unit[chan].flags &= ~(CHAN_MODEL); + chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; + } + num_devs[chan]++; + if (dibp->ini != NULL) + dibp->ini(uptr, 1); + } + next: + uptr++; + } + nextdev: + ; + } + return SCPE_OK; +} + +/* Print help for "SET dev CHAN" based on allowed types */ +void help_set_chan_type(FILE *st, DEVICE *dptr, const char *name) +{ +#if NUM_CHAN > 1 + DIB *dibp = (DIB *) dptr->ctxt; + int ctype = dibp->ctype; + int i; + int m; + + fprintf (st, "Devices can be moved to any channel via the command\n\n"); + fprintf (st, " sim> SET %s CHAN=x where x is", dptr->name); + if (ctype & 3) { + if (ctype == 1 || ctype == 2) + fprintf(st, " only"); + fprintf (st, " %s", chname[0]); + if ((ctype & ~3) != 0) + fprintf(st, " or"); + } + if ((ctype & ~3) != 0) + fprintf(st, " %s to %s", chname[1], chname[NUM_CHAN-1]); + fprintf (st, "\n\n%s can be attached to ", name); + m = 1; + for(i = 0; ctype != 0; i++) { + if (ctype & m) { + fprintf(st, "%s", chan_type_name[i]); + ctype &= ~m; + if (ctype != 0) + fprintf(st, ", or "); + } + m <<= 1; + } + fprintf(st, " channel\n"); +#endif +} + + +/* Sets the device onto a given channel */ +t_stat +set_chan(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int newch; + int chan; + int num; + int type; + int ctype; + int compat; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + chan = UNIT_G_CHAN(uptr->flags); + dibp = (DIB *) dptr->ctxt; + + if (dibp == NULL) + return SCPE_IERR; + for(newch = 0; newch < NUM_CHAN; newch++) + if (strcmp(cptr, chname[newch]) == 0) + break; + if (newch == NUM_CHAN) + return SCPE_ARG; + if (newch == chan) + return SCPE_OK; + + ctype = dibp->ctype; + compat = ctype; + + /* Update the number of devices on this channel */ + num_devs[newch] = 0; + for (num = 0; sim_devices[num] != NULL; num++) { + UNIT *u = sim_devices[num]->units; + DIB *dibp = (DIB *) sim_devices[num]->ctxt; + int units = sim_devices[num]->numunits; + + /* If no DIB, not channel device */ + if (dibp == NULL) + continue; + /* Skip channel devices */ + if (sim_devices[num] == &chan_dev) + continue; + /* Skip disabled devices */ + if (sim_devices[num]->flags & DEV_DIS) + continue; + if (dibp->upc > 1) { + if ((u->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(u->flags) == chan) { + num_devs[newch] += units; + compat &= dibp->ctype; + } + } else { + int i; + for (i = 0; i < units; i++) { + if ((u->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(u->flags) == chan) { + num_devs[newch]++; + compat &= dibp->ctype; + } + u++; + } + } + } + + /* If nothing left on channel, drop set bit */ + if (num_devs[newch] == 0 && chan_unit[newch].flags & CHAN_AUTO) { + chan_unit[newch].flags &= ~CHAN_SET; + compat = ctype; + } + + /* Check if same type or everyone can handle new type */ + type = CHAN_G_TYPE(chan_unit[newch].flags); + if (((1 << type) & ctype) == 0) { + /* If set or no common types */ + if (chan_unit[newch].flags & CHAN_SET && compat == 0) + return SCPE_IERR; + if ((chan_unit[newch].flags & CHAN_AUTO) && + (compat == 0 && num_devs[newch] != 0)) + return SCPE_IERR; + else { + /* Set type to highest compatable type */ + for(type = 7; type >=0; type--) + if (compat >> type) + break; + chan_unit[newch].flags &= ~(CHAN_MODEL); + chan_unit[newch].flags |= CHAN_S_TYPE(type)|CHAN_SET; + } + } + + /* Set channel to highest type */ + if ((chan_unit[chan].flags & CHAN_SET) == 0) { + /* Set type to highest found */ + for(type = 7; type >=0; type--) + if (ctype >> type) + break; + chan_unit[chan].flags &= ~(CHAN_MODEL); + chan_unit[chan].flags |= CHAN_S_TYPE(type)|CHAN_SET; + } + + /* Detach unit from orignal channel */ + if (dibp->upc > 1) + num_devs[chan] -= dptr->numunits; + else + num_devs[chan]--; + if (num_devs[chan] == 0 && (chan_unit[chan].flags & CHAN_AUTO)) + chan_unit[chan].flags &= ~CHAN_SET; + + /* Hook up to new channel */ + if (dibp->upc > 1) { + uint32 unit; + for (unit = 0; unit < dptr->numunits; unit++) { + /* Set the new channel */ + dptr->units[unit].flags &= ~UNIT_CHAN; + dptr->units[unit].flags |= UNIT_S_CHAN(newch); + } + num_devs[newch] += dptr->numunits; + } else { + /* Set the new channel */ + uptr->flags &= ~UNIT_CHAN; + uptr->flags |= UNIT_S_CHAN(newch); + num_devs[newch]++; + } + return SCPE_OK; +} + +/* Print devices on channel */ +t_stat +print_chan(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + int chan = uptr - chan_unit; + int i; + + /* Check all devices */ + fprintf(st, "units="); + for (i = 0; sim_devices[i] != NULL; i++) { + UNIT *u = sim_devices[i]->units; + DIB *dibp = (DIB *) sim_devices[i]->ctxt; + uint32 num; + + /* If no DIB, not channel device */ + if (dibp == NULL) + continue; + /* Skip channel devices */ + if (sim_devices[i] == &chan_dev) + continue; + /* Skip disabled devices */ + if (sim_devices[i]->flags & DEV_DIS) + continue; + if (dibp->upc > 1) { + if ((u->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(u->flags) == chan) + fprintf(st, "%s, ", sim_devices[i]->name); + } else { + for (num = 0; num < sim_devices[i]->numunits; num++) { + if ((u->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(u->flags) == chan) + fprintf(st, "%s%d, ", sim_devices[i]->name, num); + u++; + } + } + } + return SCPE_OK; +} + +t_stat +get_chan(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int chan; + + if (uptr == NULL) + return SCPE_IERR; + chan = UNIT_G_CHAN(uptr->flags); + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + fprintf(st, "Chan=%s", chname[chan]); + return SCPE_OK; +} + +t_stat +chan9_set_select(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int newsel; + DEVICE *dptr; + DIB *dibp; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + if (*cptr == '\0' || cptr[1] != '\0') + return SCPE_ARG; + if (*cptr == '0') + newsel = 0; + else if (*cptr == '1') + newsel = 1; + else + return SCPE_ARG; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + + if (dibp == NULL) + return SCPE_IERR; + + /* Change to new selection. */ + if (dibp->upc > 1) { + uint32 unit; + for (unit = 0; unit < dptr->numunits; unit++) { + if (newsel) + dptr->units[unit].flags |= UNIT_SELECT; + else + dptr->units[unit].flags &= ~UNIT_SELECT; + } + } else { + if (newsel) + uptr->flags |= UNIT_SELECT; + else + uptr->flags &= ~UNIT_SELECT; + } + return SCPE_OK; +} + +t_stat +chan9_get_select(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + if (uptr->flags & UNIT_SELECT) + fputs("Select=1", st); + else + fputs("Select=0", st); + return SCPE_OK; +} + +/* Check channel for error */ +int chan_error(int chan) +{ + return chan_flags[chan] & CHS_ATTN; +} + +/* Check channel for flag, clear it if it was set */ +int chan_stat(int chan, uint32 flag) +{ + if (chan_flags[chan] & flag) { + chan_flags[chan] &= ~flag; + return 1; + } + return 0; +} + +/* Check channel for flag */ +int chan_test(int chan, uint32 flag) +{ + if (chan_flags[chan] & flag) + return 1; + return 0; +} + +/* Check channel is selected */ +int chan_select(int chan) +{ + return chan_flags[chan] & DEV_SEL; +} + +/* Check channel is active */ +int chan_active(int chan) +{ + return (chan_flags[chan] & + (DEV_DISCO |DEV_SEL | STA_ACTIVE | STA_WAIT | STA_TWAIT)) != 0; +} + +void +chan_set_attn(int chan) +{ + chan_flags[chan] |= CHS_ATTN; +} + +void +chan_set_eof(int chan) +{ + chan_flags[chan] |= CHS_EOF; +} + +void +chan_set_error(int chan) +{ + chan_flags[chan] |= CHS_ERR; +} + +void +chan_set_sel(int chan, int need) +{ + chan_flags[chan] &= + ~(DEV_WEOR | DEV_REOR | DEV_FULL | DEV_WRITE | DEV_DISCO); + chan_flags[chan] |= DEV_SEL; + if (need) + chan_flags[chan] |= DEV_WRITE; +} + +void +chan_clear_status(int chan) +{ + chan_flags[chan] &= + ~(CHS_ATTN | CHS_EOT | CHS_BOT | DEV_REOR | DEV_WEOR); +} + +void +chan_set(int chan, uint32 flag) +{ + chan_flags[chan] |= flag; +} + +void +chan_clear(int chan, uint32 flag) +{ + chan_flags[chan] &= ~flag; +} + +void +chan9_clear_error(int chan, int sel) { + chan_flags[chan] &= ~(SNS_UEND | (SNS_ATTN1 >> sel)); +} + +void +chan9_set_attn(int chan, int sel) +{ + uint16 mask = SNS_ATTN1 >> sel; + + chan9_set_error(chan, mask); +} + diff --git a/I7000/i7000_chron.c b/I7000/i7000_chron.c new file mode 100644 index 00000000..417f74d2 --- /dev/null +++ b/I7000/i7000_chron.c @@ -0,0 +1,292 @@ +/* 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 + +#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 diff --git a/I7000/i7000_com.c b/I7000/i7000_com.c new file mode 100644 index 00000000..6dbda788 --- /dev/null +++ b/I7000/i7000_com.c @@ -0,0 +1,1317 @@ +/* i7090_com.c: IBM 7094 7750 communications interface simulator + + Copyright (c) 2010-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. + + Except as contained in this notice, the name of Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell + + com 7750 controller + coml 7750 lines + + This module implements an abstract simulator for the IBM 7750 communications + computer as used by the CTSS system. The 7750 supports up to 112 lines; + the simulator supports 33. The 7750 can handle both high-speed lines, in + 6b and 12b mode, and normal terminals, in 12b mode only; the simulator + supports only terminals. The 7750 can handle many different kinds of + terminals; the simulator supports only a limited subset. + + Input is asynchronous. The 7750 sets ATN1 to signal availability of input. + When the 7094 issues a CTLRN, the 7750 gathers available input characters + into a message. The message has a 12b sequence number, followed by 12b line + number/character pairs, followed by end-of-medium (03777). Input characters + can either be control characters (bit 02000 set) or data characters. Data + characters are 1's complemented and are 8b wide: 7 data bits and 1 parity + bit (which may be 0). + + Output is synchronous. When the 7094 issues a CTLWN, the 7750 interprets + the channel output as a message. The message has a 12b line number, followed + by a 12b character count, followed by characters, followed by end-of-medium. + If bit 02000 of the line number is set, the characters are 12b wide. If + bit 01000 is set, the message is a control message. 12b characters consist + of 7 data bits, 1 parity bit, and 1 start bit. Data characters are 1's + complemented. Data character 03777 is special and causes the 7750 to + repeat the previous bit for the number of bit times specified in the next + character. This is used to generate delays for positioning characters. + + The 7750 supports flow control for output. To help the 7094 account for + usage of 7750 buffer memory, the 7750 sends 'character output completion' + messages for every 'n' characters output on a line, where n <= 31. + + Note that the simulator console is mapped in as line n+1. + + Sense word based on 7074 Principles of Operation. + + 1 A Reserved. + 3 4 Program Check Summary byte + 4 2 Exceptional Condtion Summary byte + 5 1 Data Check Summary byte + 7 A Reserved + 9 4 Message Length Check Program Check + 10 2 Channel Hold Program Check + 11 1 Channel Queue Full Program Check + 13 A Reserved + 15 4 Reserved + 16 2 Reserved + 17 1 Interface Timeout Data Check + 19 A Reserved + 21 4 Data Message Ready Exceptional Condition + 22 2 Input space available Exceptional Condition + 23 1 Service Message Ready Exceptional Condition + +*/ + +#include "i7000_defs.h" +#include "sim_timer.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#ifdef NUM_DEVS_COM +#define COM_MLINES 32 /* mux lines */ +#define COM_TLINES (COM_MLINES) +#define COM_BUFSIZ 120 /* max chan transfer */ +#define COM_PKTSIZ 16384 /* character buffer */ + +#define UNIT_V_2741 (UNIT_V_UF + 0) /* 2741 - ni */ +#define UNIT_V_K35 (UNIT_V_UF + 1) /* KSR-35 */ +#define UNIT_2741 (1 << UNIT_V_2741) +#define UNIT_K35 (1 << UNIT_V_K35) + +#define TMR_COM 2 + +#define CONN u3 /* line is connected */ +#define NEEDID u4 /* need to send ID */ +#define ECHO u5 /* echoing output */ + +#define COM_INIT_POLL 8000 /* polling interval */ +#define COMC_WAIT 2 /* channel delay time */ +#define COML_WAIT 500 /* char delay time */ +#define COM_LBASE 4 /* start of lines */ + +/* Input threads */ + +#define COM_PLU 0 /* multiplexor poll */ +#define COM_CIU 1 /* console input */ +#define COM_CHU 2 /* console output */ + +/* Communications input */ + +#define COMI_LCOMP 002000 /* line complete */ +#define COMI_DIALUP 002001 /* dialup */ +#define COMI_ENDID 002002 /* end ID */ +#define COMI_INTR 002003 /* interrupt */ +#define COMI_QUIT 002004 /* quit */ +#define COMI_HANGUP 002005 /* hangup */ +#define COMI_EOM 013777 /* end of medium */ +#define COMI_COMP(x) ((uint16) (03000 + ((x) & COMI_CMAX))) +#define COMI_K35 6 /* KSR-35 ID */ +#define COMI_K37 7 /* KSR-37 ID */ +#define COMI_2741 8 /* 2741 ID */ +#define COMI_CMAX 31 /* max chars returned */ +#define COMI_PARITY 00200 /* parity bit */ +#define COMI_BMAX 50 /* buffer max, words */ +#define COMI_12BMAX ((3 * COMI_BMAX) - 1) /* last 12b char */ + +/* Communications output - characters */ + +#define COMO_LIN12B 02000 /* line is 12b */ +#define COMO_LINCTL 01000 /* control msg */ +#define COMO_GETLN(x) ((x) & 0777) +#define COMO_CTLRST 07777 /* control reset */ +#define COMO_BITRPT 03777 /* bit repeat */ +#define COMO_EOM12B 07777 /* end of medium */ +#define COMO_EOM6B 077 /* end of medium */ +#define COMO_BMAX 94 /* buffer max, words */ +#define COMO_12BMAX ((3 * COMO_BMAX) - 1) + +/* Report variables */ + +#define COMR_FQ 1 /* free queue */ +#define COMR_IQ 2 /* input queue */ +#define COMR_OQ 4 /* output queue */ + +/* Sense word flags */ +#define EXPT_SRVRDY 0x1001 /* Service message available */ +#define EXPT_INAVAIL 0x1002 /* Input available */ +#define EXPT_DATRDY 0x1004 /* Data ready. */ +#define DATA_TIMEOUT 0x2010 /* Timeout */ +#define PROG_FULL 0x4100 /* No more space to send message */ +#define PROG_HOLD 0x4200 /* Channel hold */ +#define PROG_MSGLEN 0x4400 /* Invalid message length */ + +/* Input and output ring buffers */ +uint16 in_buff[256]; +int in_head; +int in_tail; +int in_count; /* Number of entries in queue */ +int in_delay = 5000; + + +typedef struct +{ + uint16 link; + uint16 data; +} OLIST; + +uint32 com_posti = 0; /* Posted a IRQ */ +uint32 com_active = 0; /* Channel active */ +uint32 com_ocnt = 0; /* Number of characters to output */ +uint32 com_oln = 0; /* Output line number */ +uint32 com_o12b = 0; /* Outputing 12 bit */ +uint32 com_enab = 0; /* 7750 enabled */ +uint32 com_msgn = 0; /* next input msg num */ +uint32 com_sta = 0; /* 7750 state */ +uint32 com_quit = 3; /* quit code */ +uint32 com_intr = 4; /* interrupt code */ +uint32 com_tps = 50; /* polls/second */ +uint8 com_out_inesc[COM_TLINES]; +uint16 com_out_head[COM_TLINES]; +uint16 com_out_tail[COM_TLINES]; +uint16 com_comp_cnt[COM_TLINES]; +int com_line; /* Current line */ +uint16 com_free; /* free list */ +OLIST com_buf[10240]; +TMLN com_ldsc[COM_TLINES]; /* line descriptors */ +TMXR com_desc = { COM_TLINES, 0, 0, com_ldsc }; /* mux descriptor */ +uint32 com_sense = 0; /* Sense word */ +uint16 com_data; +uint8 com_dflg = 0; + + +/* 2741 convertion table */ +static const uint8 com_2741_out[256] = { +/* Upper case */ +/* 0 1 2 3 4 5 6 7 */ + ' ', '-', '2', '+', '*', 'Q', 'Y', 'H', /* 000 */ + ':', 'M', 'U', 'D', '_', '_', '_', '_', /* 010 */ + '@', 'K', 'S', 'B', ')', '_', '_', '_', /* 020 */ + '\'', 'O', 'W', 'F','\n','\b', ' ', '_', /* 030 */ + '=', 'J', '?', 'A', '(', 'R', 'Z', 'I', /* 040 */ + '%', 'N', 'V', 'E', '_','\n','\r', '\t', /* 050 */ + ';', 'L', 'T', 'C', '#', '$', ',', '.', /* 060 */ + '"', 'P', 'X', 'G', '_','\t', '<', '\0', /* 070 */ + ' ', '-', '@', '&', '8', 'q', 'y', 'h', /* 100 */ + '4', 'm', 'u', 'd', '_', '_', '_', '_', /* 110 */ + '2', 'k', 's', 'b', '0', '_', '_', '_', /* 120 */ + '6', 'o', 'w', 'f', '_','\b', ' ', '_', /* 130 */ + '1', 'j', '/', 'a', '9', 'r', 'z', 'i', /* 140 */ + '5', 'n', 'v', 'e','\n','\n','\r', '\t', /* 150 */ + '3', 'l', 't', 'c', '_', '!', ',', '.', /* 160 */ + '7', 'p', 'x', 'g', '_','\t', '_','\0', /* 170 */ +}; + +/* 76 43 177 15 */ +/* 76 23 177 16 */ +static const uint8 com_2741_in[128] = { + /* Control */ + 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*0-37*/ + /*Control*/ + 0135, 0057, 0155, 0000, 0000, 0155, 0000, 0000, + /*Control*/ + 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, + /*Control*/ + 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, + /* sp ! " # $ % & ' */ + 0100, 0365, 0070, 0264, 0165, 0150, 0303, 0130, /* 40 - 77 */ + /* ( ) * + , - . / */ + 0144, 0124, 0004, 0203, 0166, 0001, 0067, 0342, + /* 0 1 2 3 4 5 6 7 */ + 0324, 0240, 0220, 0360, 0210, 0350, 0330, 0270, + /* 8 9 : ; < = > ? */ + 0204, 0344, 0010, 0160, 0000, 0040, 0000, 0142, + /* @ A B C D E F G */ + 0202, 0043, 0023, 0163, 0013, 0153, 0133, 0073, /* 100 - 137 */ + /* H I J K L M N O */ + 0007, 0147, 0141, 0121, 0061, 0111, 0051, 0031, + /* P Q R S T U V W */ + 0171, 0105, 0045, 0122, 0062, 0112, 0052, 0032, + /* X Y Z [ \ ] ^ _ */ + 0172, 0106, 0046, 0000, 0000, 0000, 0000, 0000, + /* ` a b c d e f g */ + 0000, 0243, 0223, 0363, 0213, 0353, 0333, 0273, /* 140 - 177 */ + /* h i j k l m n o */ + 0207, 0347, 0341, 0321, 0261, 0311, 0251, 0231, + /* p q r s t u v w */ + 0371, 0305, 0245, 0322, 0262, 0312, 0252, 0232, + /* x y z { | } ~ del*/ + 0372, 0306, 0246, 0000, 0000, 0000, 0000, 0177 +}; + + + +uint32 com_cmd(UNIT * uptr, uint16 cmd, uint16 dev); +t_stat com_svc(UNIT * uptr); +t_stat comi_svc(UNIT * uptr); +t_stat como_svc(UNIT * uptr); +t_stat comti_svc(UNIT * uptr); +t_stat comto_svc(UNIT * uptr); +t_stat com_reset(DEVICE * dptr); +t_stat com_attach(UNIT * uptr, CONST char *cptr); +t_stat com_detach(UNIT * uptr); +t_stat com_summ(FILE * st, UNIT * uptr, int32 val, CONST void *desc); +t_stat com_show(FILE * st, UNIT * uptr, int32 val, CONST void *desc); +void com_reset_ln(uint32 i); +t_stat com_queue_in(uint32 ln, uint16 ch); +uint32 com_queue_out(uint32 ln, uint16 * c1); +t_stat com_send_id(uint32 ln); +t_stat com_send_ccmp(uint32 ln); +void com_skip_outc(uint32 ln); +t_bool com_get(int ln, uint16 *ch); +t_bool com_put(int ln, uint16 ch); +void com_post_eom(); +t_bool com_inp_msg(uint32 ln, uint16 msg); +const char *com_description(DEVICE *dptr); +t_stat com_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *coml_description(DEVICE *dptr); +t_stat coml_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + + +/* COM data structures + + com_dev COM device descriptor + com_unit COM unit descriptor + com_reg COM register list + com_mod COM modifiers list +*/ +#ifdef I7010 +#define COM_CHAN 4 +#else +#define COM_CHAN 5 +#endif + +UNIT com_unit[] = { + {UDATA(&comi_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_ATTABLE, 0), COM_INIT_POLL}, + {UDATA(&comti_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_DIS, 0), KBD_POLL_WAIT}, + {UDATA(&com_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_DIS, 0), COMC_WAIT}, +}; + +REG com_reg[] = { + {FLDATA(ENABLE, com_enab, 0)}, + {ORDATA(STATE, com_sta, 6)}, + {ORDATA(MSGNUM, com_msgn, 12)}, + {NULL} +}; + +MTAB com_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", + &set_chan, &get_chan, NULL, "Set channel"}, +#ifndef I7010 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "SELECT", "SELECT", + &chan9_set_select, &chan9_get_select, NULL, "Set selection channel"}, +#endif + {UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &com_summ}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &com_show, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &com_show, NULL}, + {0} +}; + +DEVICE com_dev = { + "COM", com_unit, com_reg, com_mod, + 3, 10, 31, 1, 16, 8, + &tmxr_ex, &tmxr_dep, &com_reset, + NULL, &com_attach, &com_detach, + &com_dib, DEV_DISABLE| DEV_DEBUG|DEV_NET, 0, dev_debug, + NULL, NULL, &com_help, NULL, NULL, &com_description +}; + +/* COMLL data structures + + coml_dev COML device descriptor + coml_unit COML unit descriptor + coml_reg COML register list + coml_mod COML modifiers list +*/ + +UNIT coml_unit[] = { + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */ + {UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */ +}; + +MTAB coml_mod[] = { + {UNIT_K35 + UNIT_2741, 0, "KSR-37", "KSR-37", NULL, NULL, NULL, + "Standard KSR"}, + {UNIT_K35 + UNIT_2741, UNIT_K35, "KSR-35", "KSR-35", NULL, NULL, NULL, + "Upper case only KSR"}, + {UNIT_K35 + UNIT_2741, UNIT_2741, "2741", "2741", NULL, NULL, NULL, + "IBM 2741 terminal"}, + {MTAB_XTD | MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &com_desc, "Disconnect line"}, + {MTAB_XTD | MTAB_VUN | MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &com_desc}, + {MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &com_desc}, + {0} +}; + +REG coml_reg[] = { + {URDATA(TIME, coml_unit[0].wait, 16, 24, 0, + COM_TLINES, REG_NZ + PV_LEFT)}, + {NULL} +}; + +DEVICE coml_dev = { + "COML", coml_unit, coml_reg, coml_mod, + COM_TLINES, 10, 31, 1, 16, 8, + NULL, NULL, &com_reset, NULL, NULL, NULL, + NULL, DEV_DISABLE, 0, NULL, NULL, + NULL, &coml_help, NULL, NULL, &coml_description +}; + +/* COM: channel select */ +uint32 com_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + /* Activate the com device */ + sim_activate(&com_unit[COM_CHU], 10); +#if 0 /* Commented out until I can detect hangup signal */ + if (!sim_is_active(&com_unit[COM_CIU])) /* console */ + sim_activate(&com_unit[COM_CIU], com_unit[COM_CIU].wait); + if (!sim_is_active(&com_unit[COM_PLU])) { + if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */ + int32 t = + sim_rtcn_init(com_unit[COM_PLU].wait, TMR_COM); + sim_activate(&com_unit[COM_PLU], t); + } + } +#endif + com_sta = 1; + com_dflg = 0; + com_active = 1; + return SCPE_OK; +} + +/* Unit service - channel program */ +t_stat com_svc(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + uint8 ch; + + if (sel != chan_test(chan, CTL_SEL)) + return SCPE_OK; + + /* Handle disconnect */ + if (com_sta != 0 && chan_stat(chan, DEV_DISCO)) { + chan_clear(chan, DEV_WEOR|DEV_SEL); + com_sta = 0; + com_active = 0; + return SCPE_OK; + } + + if (chan_test(chan, CTL_SNS)) { + int eor = (com_sta == 4)?DEV_REOR:0; + + ch = (com_sense >> ((4 - com_sta) * 4)) & 0xf; + if (ch & 010) /* Move A bit over one */ + ch ^= 030; + sim_debug(DEBUG_SNS, &com_dev, "sense unit=%02x\n", ch); + switch(chan_write_char(chan, &ch, eor)) { + case TIME_ERROR: + case END_RECORD: + com_sta = -1; + com_sense = 0; + break; + case DATA_OK: + com_sta++; + } + sim_activate(uptr, 50); + return SCPE_OK; + } + + /* Start a command, only do read/write */ + if (chan_test(chan, CTL_CNTL)) { + chan_clear(chan, DEV_FULL); + chan_set(chan, DEV_REOR|DEV_SEL); + sim_activate(uptr, 50); + return SCPE_OK; + } + + /* Send down next buffer word */ + if (chan_test(chan, CTL_READ)) { + /* Send low order character if one */ + if (com_dflg) { + ch = com_data & 0377; + sim_debug(DEBUG_DATA, &com_dev, "sent=%02o\n", ch); + switch (chan_write_char(chan, &ch, (com_sta == 3)?DEV_REOR:0)) { + case DATA_OK: + case END_RECORD: + com_dflg = 0; + break; + case TIME_ERROR: + com_sense |= DATA_TIMEOUT; + } + sim_activate(uptr, 50); + return SCPE_OK; + } + + switch (com_sta) { + case 1: + com_data = com_msgn; /* 1st char is msg num */ + com_msgn = (com_msgn + 1) & 03777; /* incr msg num */ + com_sta++; + com_posti = 0; + chan9_clear_error(chan, sel); + break; + case 2: + /* Check if queue empty. */ + if (in_head == in_tail) { + com_data = COMI_EOM; + com_sta++; + } else { + /* Grab next entry. */ + in_head++; + /* Wrap around end of ring */ + if (in_head >= (sizeof(in_buff)/sizeof(uint16))) + in_head = 0; + com_data = in_buff[in_head]; + /* Check if end of current transfer */ + if (com_data == COMI_EOM) + com_sta++; + in_count--; + } + break; + case 3: + chan_set(chan, DEV_REOR|CTL_END); + sim_activate(uptr, 50); + com_posti = 0; + com_sta++; + return SCPE_OK; /* q empty, done */ + } + sim_debug(DEBUG_DATA, &com_dev, "send data=%04o\n", com_data); + ch = (com_data >> 6) & 077; + com_data &= 077; + switch (chan_write_char(chan, &ch, 0)) { + case DATA_OK: + case END_RECORD: + com_dflg = 1; + break; + case TIME_ERROR: + com_sense |= DATA_TIMEOUT; + } + sim_activate(uptr, 50); + return SCPE_OK; + } + + if (chan_test(chan, CTL_WRITE)) { + uint32 ln; + + /* Read in two characters */ + if (com_dflg == 0) { + switch(chan_read_char(chan, &ch, 0)) { + case DATA_OK: + com_dflg = 1; + com_data = (ch & 077) << 6; + break; + case END_RECORD: + case TIME_ERROR: + com_sense |= DATA_TIMEOUT; + } + sim_activate(uptr, 50); + return SCPE_OK; + } else { + switch(chan_read_char(chan, &ch, 0)) { + case DATA_OK: + com_dflg = 0; + com_data |= (ch & 077); + break; + case END_RECORD: + case TIME_ERROR: + com_sense |= DATA_TIMEOUT; + sim_activate(uptr, 50); + return SCPE_OK; + } + } + sim_debug(DEBUG_DATA, &com_dev, "recieved=%04o\n", com_data); + switch (com_sta) { + case 1: + com_oln = com_data; + if (com_data == 07777) { /* turn on? */ + sim_debug(DEBUG_DETAIL, &com_dev, "enable\n"); + com_enab = 1; /* enable 7750 */ + in_delay = 200; + com_msgn = 0; /* init message # */ + com_sta = 4; + chan_set(chan, DEV_REOR|CTL_END); + } else if (com_data & COMO_LINCTL) { /* control message? */ + ln = COMO_GETLN(com_data); /* line number */ + sim_debug(DEBUG_DETAIL, &com_dev, "line %d\n", ln); + if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ + return STOP_INVLIN; + if (ln > COM_LBASE) /* valid line? */ + com_reset_ln(ln - COM_LBASE); + com_sta = 4; + chan_set(chan, DEV_REOR|CTL_END); + } else /* data message */ + com_sta++; + break; + case 2: + com_ocnt = (com_data & 07777) + 1; /* char count plus EOM */ + if (com_oln & COMO_LIN12B) { + com_ocnt = com_ocnt << 1; /* 12b double */ + com_o12b = 1; + } else + com_o12b = 0; + com_oln = COMO_GETLN(com_oln); /* line number */ + sim_debug(DEBUG_DETAIL, &com_dev, "output line %d\n", com_oln); + com_sta++; /* next state */ + break; + case 3: /* other words */ + ln = com_oln; /* effective line */ + /* unpack chars */ + if (com_o12b) { + com_ocnt -= 2; + if (com_data == COMO_EOM12B) { + com_sta++; + if (com_ocnt != 0) { + chan9_set_error(chan, SNS_UEND); + com_sense |= PROG_MSGLEN; + } + chan_set(chan, DEV_REOR|CTL_END); /* end, last state */ + break; /* EOM? */ + } + } else { + com_ocnt--; + if (((com_data >> 6) & 077) == COMO_EOM6B) { + com_sta++; + if (com_ocnt != 0) { + sim_debug(DEBUG_EXP, &com_dev, "messge length error %d\n", com_ocnt); + chan9_set_error(chan, SNS_UEND); + com_sense |= PROG_MSGLEN; + } + chan_set(chan, DEV_REOR|CTL_END); /* end, last state */ + break; /* EOM? */ + } + sim_debug(DEBUG_DETAIL, &com_dev, "queing %o %d\n", (com_data >> 6) & 077, com_ocnt); + if (com_put(ln, (com_data >> 6) & 077)) { + sim_debug(DEBUG_EXP, &com_dev, "Insert error\n"); + chan9_set_error(chan, SNS_UEND); + com_sense |= PROG_FULL; + } + com_ocnt--; + com_data &= 077; + if (com_data == COMO_EOM6B) { + com_sta++; + if (com_ocnt != 0) { + sim_debug(DEBUG_EXP, &com_dev, "messge length error %d\n", com_ocnt); + chan9_set_error(chan, SNS_UEND); + com_sense |= PROG_MSGLEN; + } + chan_set(chan, DEV_REOR|CTL_END); /* end, last state */ + break; /* EOM? */ + } + } + sim_debug(DEBUG_DETAIL, &com_dev, "queing %o %d\n", com_data, com_ocnt); + if (com_put(ln, com_data)) { + sim_debug(DEBUG_EXP, &com_dev, "Insert error\n"); + chan9_set_error(chan, SNS_UEND); + com_sense |= PROG_FULL; + } + break; + } + sim_activate(uptr, 50); + } + return SCPE_OK; +} + +/* Unit service - console receive - always running, even if device is not */ + +t_stat +comti_svc(UNIT * uptr) +{ + int32 c; + t_stat r; + + sim_activate(uptr, uptr->wait); /* continue poll */ + c = sim_poll_kbd(); /* get character */ + if (c && (c < SCPE_KFLAG)) + return c; /* error? */ + if (((com_unit[COM_PLU].flags & UNIT_ATT) == 0) || /* not att, not enab, */ + !com_enab || (c & SCPE_BREAK)) + return SCPE_OK; /* break? done */ + c = c & 0177; + if (c) { + r = com_queue_in(0, c); + if (r != SCPE_OK) + return r; + sim_putchar(c); + if (c == '\r') + sim_putchar('\n'); + } + return SCPE_OK; +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections */ + +t_stat +comi_svc(UNIT * uptr) +{ + int32 c, ln, t; + t_stat r; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_OK; /* attached? */ + if (in_delay-- <= 0) { /* Check for any inputs to send over. */ + in_delay = 50; /* Time to wait for polling again. */ + if (!com_active && in_count > 0) + com_post_eom(); + } + t = sim_rtcn_calb(com_tps, TMR_COM); /* calibrate */ + sim_activate(uptr, t); /* continue poll */ + ln = tmxr_poll_conn(&com_desc); /* look for connect */ + if (ln >= 0) { /* got one? */ + com_ldsc[ln].rcve = 1; /* rcv enabled */ + coml_unit[ln].CONN = 1; /* flag connected */ + coml_unit[ln].NEEDID = 1; /* need ID */ + coml_unit[ln].ECHO = 1; /* echoing output */ + } + if (!com_enab) + return SCPE_OK; /* not enabled? exit */ + tmxr_poll_rx(&com_desc); /* poll for input */ + for (ln = 0; ln < COM_TLINES; ln++) { /* loop thru mux */ + if (com_ldsc[ln].conn) { /* connected? */ + if (coml_unit[ln].NEEDID) + com_send_id(ln); + c = tmxr_getc_ln(&com_ldsc[ln]); /* get char */ + if (c) { /* any char? */ + c = c & 0177; /* mask to 7b */ + r = com_queue_in(ln, c); + if (r != SCPE_OK) + return r; /* queue char, err? */ + if (coml_unit[ln].ECHO && com_ldsc[ln].xmte) { /* output enabled? */ + if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ + if (islower(c)) + c = toupper(c); /* convert LC to UC */ + } + tmxr_putc_ln(&com_ldsc[ln], c); /* echo char */ + if (c == '\r') /* add LF after CR */ + tmxr_putc_ln(&com_ldsc[ln], '\n'); + } /* end if enabled */ + } /* end if char */ + } /* end if conn */ + else if (coml_unit[ln].CONN) { /* not conn, was conn? */ + coml_unit[ln].CONN = 0; /* clear connected */ + coml_unit[ln].NEEDID = 0; /* clear need id */ + if (com_inp_msg(ln, COMI_HANGUP)) /* hangup message */ + return STOP_NOIFREE; + } + } /* end for */ + tmxr_poll_tx(&com_desc); /* poll xmt */ + return SCPE_OK; +} + +/* Unit service - console transmit */ + +t_stat +comto_svc(UNIT * uptr) +{ + uint16 c, c1; + + if (com_out_head[0] == 0) + return com_send_ccmp(0); /* Send out a completion code */ + c = com_queue_out(0, &c1); /* get character, cvt */ + if (c) + sim_putchar(c); /* printable? output */ + if (c1) + sim_putchar(c1); /* second char? output */ + sim_activate(uptr, uptr->wait); /* next char */ + if (com_comp_cnt[0] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp(0); /* generate msg */ + return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat +como_svc(UNIT * uptr) +{ + uint16 c, c1; + int32 ln = uptr - coml_unit; /* line # */ + + if (com_out_head[ln] == 0) /* no more characters? */ + return com_send_ccmp(ln); /* free any remaining */ + if (com_ldsc[ln].conn) { /* connected? */ + if (com_ldsc[ln].xmte) { /* output enabled? */ + c = com_queue_out(ln, &c1); /* get character, cvt */ + if (c) + tmxr_putc_ln(&com_ldsc[ln], c); /* printable? output */ + if (c1) + tmxr_putc_ln(&com_ldsc[ln], c1); /* print second */ + } /* end if */ + tmxr_poll_tx(&com_desc); /* poll xmt */ + sim_activate(uptr, uptr->wait); /* next char */ + if (com_comp_cnt[ln] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp(ln); /* generate msg */ + } /* end if conn */ + return SCPE_OK; +} + +/* Send ID sequence on input */ + +t_stat +com_send_id(uint32 ln) +{ + com_inp_msg(ln, COMI_DIALUP); /* input message: */ + if (coml_unit[ln].flags & UNIT_2741) /* dialup, ID, endID */ + com_inp_msg(ln, COMI_2741); + else if (coml_unit[ln].flags & UNIT_K35) + com_inp_msg(ln, COMI_K35); + else + com_inp_msg(ln, COMI_K37); + com_inp_msg(ln, 0); + com_inp_msg(ln, 0); + com_inp_msg(ln, 0); + com_queue_in(ln, 'T'); + com_queue_in(ln, '0' + ((ln+1) / 10)); + com_queue_in(ln, '0' + ((ln+1) % 10)); + if (com_inp_msg(ln, COMI_ENDID)) /* make sure there */ + return STOP_NOIFREE; /* was room for msg */ + coml_unit[ln].NEEDID = 0; + com_sense |= EXPT_SRVRDY; + return SCPE_OK; +} +/* Translate and queue input character */ + +t_stat +com_queue_in(uint32 ln, uint16 c) +{ + uint16 out; + uint16 parity; + + if (c == com_intr) + out = COMI_INTR; + else if (c == com_quit) + out = COMI_QUIT; + else { + if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ + if (islower(c)) + c = toupper(c); /* convert LC to UC */ + } + if ((coml_unit[ln].flags & UNIT_K35) == 0) { /* KSR-37 or 2741 */ + if (c == '\r') + c = '\n'; + } + if (coml_unit[ln].flags & UNIT_2741) { + c = com_2741_in[c]; + if (c & 0200) { /* Check if lower case */ + if ((com_out_inesc[ln] & 2) == 0) { /* In upper case? */ + uint8 c2; + c2 = com_2741_out[c & 077]; /* Check if no need to change */ + if (c2 != com_2741_out[(c&077)|0100]) { + com_inp_msg(ln, 0034); /* Switch case */ + com_out_inesc[ln] &= 1; + } + } + } else { + if (com_out_inesc[ln] & 2) { /* In lower case? */ + uint8 c2; + c2 = com_2741_out[c & 077]; /* Check if no need to change */ + if (c2 != com_2741_out[(c&077)|0100]) { + com_inp_msg(ln, 0037); /* Switch case */ + com_out_inesc[ln] |= 2; + } + } + } + c &= 0177; + } + + out = (~c) & 0177; /* 1's complement */ + parity = out ^ (out >> 4); + parity = parity ^ (parity >> 2); + parity = parity ^ (parity >> 1); + if (parity & 1) + out |= COMI_PARITY; /* add even parity */ + } + if (com_inp_msg(ln, out)) + return STOP_NOIFREE; /* input message */ + com_sense |= EXPT_DATRDY; + return SCPE_OK; +} + +/* Retrieve and translate output character */ + +uint32 +com_queue_out(uint32 ln, uint16 * c1) +{ + uint16 c, raw; + + *c1 = 0; /* assume non-printing */ + if (com_get(ln, &raw)) /* Try to get character */ + return 0; + if (raw == COMO_BITRPT) { /* insert delay? */ + com_skip_outc(ln); + return 0; + } + c = (~raw >> 1) & 0177; /* remove start, parity */ + if (coml_unit[ln].flags & UNIT_2741) { + uint8 c2; + c2 = c & 077; + if (com_out_inesc[ln] & 4) { + com_out_inesc[ln] &= 3; + switch (c) { + case '\043': /* Red */ + tmxr_putc_ln(&com_ldsc[ln], '\033'); + tmxr_putc_ln(&com_ldsc[ln], '['); + tmxr_putc_ln(&com_ldsc[ln], '3'); + tmxr_putc_ln(&com_ldsc[ln], '1'); + tmxr_putc_ln(&com_ldsc[ln], 'm'); + return 0; + case '\023': /* Black */ + tmxr_putc_ln(&com_ldsc[ln], '\033'); + tmxr_putc_ln(&com_ldsc[ln], '['); + tmxr_putc_ln(&com_ldsc[ln], '0'); + tmxr_putc_ln(&com_ldsc[ln], 'm'); + return 0; + } + *c1 = c; + return '\033'; + } + switch (c2) { + case '\034': com_out_inesc[ln] &= 2; return 0; /* UC */ + case '\037': com_out_inesc[ln] |= 1; return 0; /* LC */ + case '\076': com_out_inesc[ln] |= 4; return 0; /* Esc */ + case '\016': coml_unit[ln].ECHO = FALSE; return 0; /* Poff */ + case '\015': coml_unit[ln].ECHO = TRUE; return 0; /* Pon */ + } + c2 = com_2741_out[(com_out_inesc[ln]&1)? (0100|c2): c2]; + sim_debug(DEBUG_DETAIL, &com_dev, "printing %d %04o '%c' %o\n", + ln, c, (c2>= ' ')?c2: 0, com_out_inesc[ln]&1); + if (c2 == '\n') + *c1 = '\r'; + return c2; + } + if (com_out_inesc[ln]) { + com_out_inesc[ln] = 0; + switch (c) { + case '3': /* Red */ + tmxr_putc_ln(&com_ldsc[ln], '\033'); + tmxr_putc_ln(&com_ldsc[ln], '['); + tmxr_putc_ln(&com_ldsc[ln], '3'); + tmxr_putc_ln(&com_ldsc[ln], '1'); + tmxr_putc_ln(&com_ldsc[ln], 'm'); + return 0; + case '4': /* Black */ + tmxr_putc_ln(&com_ldsc[ln], '\033'); + tmxr_putc_ln(&com_ldsc[ln], '['); + tmxr_putc_ln(&com_ldsc[ln], '0'); + tmxr_putc_ln(&com_ldsc[ln], 'm'); + return 0; + case ':': /* Poff */ + coml_unit[ln].ECHO = FALSE; + return 0; + case ';': /* Pon */ + coml_unit[ln].ECHO = TRUE; + return 0; + } + *c1 = c; + return '\033'; + } + sim_debug(DEBUG_DETAIL, &com_dev, "printing %d %04o '%c'\n", ln, c, (c >= ' ')?c:0); + if (c >= 040) { /* printable? */ + if (c == 0177) + return 0; /* DEL? ignore */ + if ((coml_unit[ln].flags & UNIT_K35) && islower(c)) /* KSR-35 LC? */ + c = toupper(c); /* cvt to UC */ + return c; + } + switch (c) { + case '\033': /* Escape character */ + com_out_inesc[ln] = 1; + return 0; + + case '\t': + case '\f': + case '\b': + case '\a': /* valid ctrls */ + return c; + + case '\r': /* carriage return? */ + if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */ + *c1 = '\n'; /* lf after cr */ + return c; + + case '\n': /* line feed? */ + if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + *c1 = '\n'; /* lf after cr */ + return '\r'; + } + return c; /* print lf */ + +#if 0 + case 022: /* DC2 */ + if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + com_skip_outc(ln); /* skip next */ + return '\n'; /* print lf */ + } + break; + + case 024: /* DC4 */ + if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */ + com_skip_outc(ln); /* skip next */ + break; +#endif + } + + return 0; /* ignore others */ +} + +/* Generate completion message, if needed */ + +t_stat +com_send_ccmp(uint32 ln) +{ + uint32 t; + + t = com_comp_cnt[ln]; + if (t != 0) { /* chars not returned? */ + if (t > COMI_CMAX) + t = COMI_CMAX; /* limit to max */ + com_comp_cnt[ln] -= t; /* keep count */ + if (com_inp_msg(ln, COMI_COMP(t))) /* gen completion msg */ + return STOP_NOIFREE; + } + return SCPE_OK; +} + +/* Skip next char in output queue */ + +void +com_skip_outc(uint32 ln) +{ + uint16 tmp; + if (com_get(ln, &tmp)) + com_comp_cnt[ln]++; /* count it */ + return; +} + +/* List routines - remove from head and free */ + +t_bool +com_get(int ln, uint16 *ch) +{ + uint16 ent; + + ent = com_out_head[ln]; + if (ent == 0) /* Check if anything to send. */ + return TRUE; + *ch = com_buf[ent].data; + com_comp_cnt[ln]++; + com_out_head[ln] = com_buf[ent].link; /* Get next char */ + com_buf[ent].link = com_free; /* Put back on free list */ + com_free = ent; + if (com_out_head[ln] == 0) { /* done with queue? */ + com_out_tail[ln] = 0; + } + return FALSE; +} + +/* Put a character onto output queue for a line */ +t_bool +com_put(int ln, uint16 ch) +{ + uint16 ent; + + ln -= COM_LBASE; + ent = com_free; /* Get a character spot */ + if (ent == 0) + return TRUE; /* No room */ + com_free = com_buf[ent].link; /* Next free character */ + com_buf[ent].data = ch; + com_buf[ent].link = 0; + if (com_out_tail[ln] == 0) { /* Idle line */ + com_out_head[ln] = ent; + } else { /* Active line */ + com_buf[com_out_tail[ln]].link = ent; + } + com_out_tail[ln] = ent; + /* Activate line if not already running */ + if (!sim_is_active(&coml_unit[ln])) + sim_activate(&coml_unit[ln], coml_unit[ln].wait); + return FALSE; +} + +/* Put EOM on input queue and post interupt to wake up CPU */ +void +com_post_eom() +{ + int ent; + int chan = UNIT_G_CHAN(com_unit[0].flags); + int sel = (com_unit[0].flags & UNIT_SELECT) ? 1 : 0; + /* See if we can insert a EOM message */ + if (in_buff[in_tail] != COMI_EOM) { + sim_debug(DEBUG_EXP, &com_dev, "inserting eom %d %d %d\n", + in_head, in_tail, in_count); + ent = in_tail + 1; + if (ent >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */ + ent = 0; + if (ent != in_head) { /* If next element would be head, queue is full */ + /* If we can't put one on, handler will do it for us */ + in_buff[ent] = COMI_EOM; + in_tail = ent; + in_count++; + } + } + chan9_set_attn(chan, sel); + com_posti = 1; +} + +/* Insert line and message into input queue */ + +t_bool +com_inp_msg(uint32 ln, uint16 msg) +{ + int ent1, ent2; + + sim_debug(DEBUG_EXP, &com_dev, "inserting %d %04o %d %d %d\n", ln, msg, + in_head, in_tail, in_count); + ent1 = in_tail + 1; + if (ent1 >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */ + ent1 = 0; + if (ent1 == in_head) /* If next element would be head, queue is full */ + return TRUE; + ent2 = ent1 + 1; + if (ent2 >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */ + ent2 = 0; + if (ent2 == in_head) /* If next element would be head, queue is full */ + return TRUE; + /* Ok we have room to put this message in. */ + ln += COM_LBASE; + in_buff[ent1] = 02000 + ln; + in_buff[ent2] = msg; + in_count += 2; + in_tail = ent2; + /* Check if over limit */ + if (!com_active && in_count > 150) { + com_post_eom(); + } + return FALSE; +} + +/* Reset routine */ + +t_stat +com_reset(DEVICE * dptr) +{ + uint32 i; + + if (dptr->flags & DEV_DIS) { /* disabled? */ + com_dev.flags = com_dev.flags | DEV_DIS; /* disable lines */ + coml_dev.flags = coml_dev.flags | DEV_DIS; + } else { + com_dev.flags = com_dev.flags & ~DEV_DIS; /* enable lines */ + coml_dev.flags = coml_dev.flags & ~DEV_DIS; + } +#if 0 /* Commented out until I can detect hangup signal */ + sim_activate(&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */ + sim_cancel(&com_unit[COM_CHU]); +#endif + sim_cancel(&com_unit[COM_PLU]); + if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */ + int32 t = + + sim_rtcn_init(com_unit[COM_PLU].wait, TMR_COM); + sim_activate(&com_unit[COM_PLU], t); + } + com_enab = 0; + com_msgn = 0; + com_sta = 0; + com_sense = 0; + /* Put dummy message on Queue before first login */ + in_head = 0; + in_tail = 0; + in_count = 0; + for (i = 0; i < COM_TLINES; i++) { + com_out_tail[i] = 0; + com_out_head[i] = 0; + com_reset_ln(i); + } + com_free = sizeof(com_buf)/(sizeof(OLIST)); + for (i = 1; i < com_free; i++) { + com_buf[i].link = i + 1; + com_buf[i].data = 0; + } + com_buf[com_free - 1].link = 0; /* end of free list */ + com_free = 1; + + return SCPE_OK; +} + +/* Attach master unit */ + +t_stat +com_attach(UNIT * uptr, CONST char *cptr) +{ + t_stat r; + + r = tmxr_attach(&com_desc, uptr, cptr); /* attach */ + if (r != SCPE_OK) + return r; /* error */ + sim_rtcn_init(uptr->wait, TMR_COM); + sim_activate(uptr, 100); /* quick poll */ + return SCPE_OK; +} + +/* Detach master unit */ + +t_stat +com_detach(UNIT * uptr) +{ + uint32 i; + t_stat r; + + r = tmxr_detach(&com_desc, uptr); /* detach */ + for (i = 0; i < COM_MLINES; i++) + com_ldsc[i].rcve = 0; /* disable rcv */ + sim_cancel(uptr); /* stop poll */ + return r; +} + +/* Show summary processor */ + +t_stat +com_summ(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + uint32 i, t; + + t = 0; + for (i = 0; i < COM_TLINES; i++) + t = t + (com_ldsc[i].conn != 0); + if (t == 1) + fprintf(st, "1 connection"); + else + fprintf(st, "%d connections", t); + return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat +com_show(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 i, cc; + + for (cc = 0; (cc < COM_MLINES) && com_ldsc[cc].conn; cc++) ; + if (cc) { + for (i = 0; i < COM_MLINES; i++) { + if (com_ldsc[i].conn) { + if (val) + tmxr_fconns(st, &com_ldsc[i], i); + else + tmxr_fstats(st, &com_ldsc[i], i); + } + } + } else + fprintf(st, "all disconnected\n"); + return SCPE_OK; +} + +/* Reset an individual line */ + +void +com_reset_ln(uint32 ln) +{ + uint16 ch; + + while (!com_get(ln, &ch)) ; + com_comp_cnt[ln] = 0; + com_out_inesc[ln] = 0; + sim_cancel(&coml_unit[ln]); + if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0)) + coml_unit[ln].CONN = 0; + return; +} +const char * +coml_description(DEVICE *dptr) +{ + return "IBM 7750 terminal"; +} + +t_stat +coml_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "Each COM line can be set to a given type of terminal\n\n"); +fprintf (st, " sim> SET COMLn KSR-37 Standard connection\n"); +fprintf (st, " sim> SET COMLn KSR-35 Allows only upper case\n"); +fprintf (st, " sim> SET COMLn 2741 Set to look like a 2741\n"); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +return SCPE_OK; +} + +const char * +com_description(DEVICE *dptr) +{ + return "IBM 7750 terminal controller"; +} + +t_stat +com_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "IBM 7750 terminal controller, this is required for CTSS to run.\n"); +fprintf (st, "The ATTACH command specifies the port to be used:\n\n"); +tmxr_attach_help (st, dptr, uptr, flag, cptr); +help_set_chan_type(st, dptr, "IBM 7750"); +#ifndef I7010 +fprintf (st, "Each device on the channel can be at either select 0 or 1, \n"); +fprintf (st, "this is set with the\n\n"); +fprintf (st, " sim> SET COM SELECT=n\n\n"); +#endif +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +return SCPE_OK; +} + +#endif + + diff --git a/I7000/i7000_con.c b/I7000/i7000_con.c new file mode 100644 index 00000000..75b717ba --- /dev/null +++ b/I7000/i7000_con.c @@ -0,0 +1,279 @@ +/* i7000_con.c: IBM 7000 Inquiry Console. + + 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. + + This is the standard inquiry or console interface. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as BCD characters. + +*/ + +#include "i7000_defs.h" +#include "sim_card.h" +#include "sim_defs.h" + +#ifdef NUM_DEVS_CON + + +/* std devices. data structures + + cdr_dev Card Reader device descriptor + cdr_unit Card Reader unit descriptor + cdr_reg Card Reader register list + cdr_mod Card Reader modifiers list +*/ + +/* Device status information stored in u5 */ + +struct _con_data +{ + uint8 ibuff[145]; /* Input line buffer */ + uint8 inptr; +} +con_data[NUM_DEVS_CON]; + +uint32 con_cmd(UNIT *, uint16, uint16); +void con_ini(UNIT *, t_bool); +t_stat con_srv(UNIT *); +t_stat con_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *con_description(DEVICE *dptr); + +extern char ascii_to_six[128]; + +UNIT con_unit[] = { + {UDATA(con_srv, UNIT_S_CHAN(CHAN_CHUREC), 0), 0}, /* A */ +}; + +DEVICE con_dev = { + "INQ", con_unit, NULL, NULL, + NUM_DEVS_CON, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, NULL, NULL, + &con_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &con_help, NULL, NULL, &con_description +}; + + + +/* + *Console printer routines. + */ +void +con_ini(UNIT *uptr, t_bool f) { + int u = (uptr - con_unit); + con_data[u].inptr = 0; + uptr->u5 = 0; + sim_activate(uptr, 1000); +} + +uint32 +con_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - con_unit); + + /* Are we currently tranfering? */ + if (uptr->u5 & (URCSTA_READ|URCSTA_WRITE|URCSTA_BUSY)) + return SCPE_BUSY; + + switch (cmd) { + /* Test ready */ + case IO_TRS: + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd TRS\n", u); + return SCPE_OK; + + /* Get record from CPU */ + case IO_WRS: + sim_putchar('R'); + sim_putchar(' '); + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd WRS\n", u); + chan_set_sel(chan, 1); + uptr->u5 |= URCSTA_WRITE; + uptr->u3 = 0; + return SCPE_OK; + + /* Send record to CPU */ + case IO_RDS: + if (uptr->u5 & URCSTA_INPUT) + return SCPE_BUSY; + if (con_data[u].inptr == 0) { + /* Activate input so we can get response */ + uptr->u5 |= URCSTA_INPUT; + sim_putchar('I'); + sim_putchar(' '); + } + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd RDS\n", u); + chan_set_sel(chan, 0); + uptr->u5 |= URCSTA_READ; + uptr->u3 = 0; + return SCPE_OK; + } + chan_set_attn(chan); + return SCPE_IOERR; +} + +/* Handle transfer of data for printer */ +t_stat +con_srv(UNIT *uptr) { + int chan = UNIT_G_CHAN(uptr->flags); + uint8 ch; + int u = (uptr - con_unit); + t_stat r; + + /* Waiting for disconnect */ + if (uptr->u5 & URCSTA_WDISCO) { + if (chan_stat(chan, DEV_DISCO)) { + sim_debug(DEBUG_DETAIL, &con_dev, " Disco\n"); + chan_clear(chan, DEV_SEL|DEV_WEOR); + uptr->u5 &= ~ URCSTA_WDISCO; + sim_activate(uptr, 25); + return SCPE_OK; + } else { + /* No disco yet, try again in a bit */ + sim_activate(uptr, 50); + return SCPE_OK; + } + } + + uptr->u5 &= ~URCSTA_BUSY; /* Clear busy */ + + /* Copy next column over */ + if (uptr->u5 & URCSTA_WRITE) { + switch(chan_read_char(chan, &ch, 0)) { + case TIME_ERROR: + case END_RECORD: + sim_putchar('\r'); + sim_putchar('\n'); + sim_debug(DEBUG_EXP, &con_dev, "\n\r"); + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY; + uptr->u5 &= ~URCSTA_WRITE; + break; + case DATA_OK: + ch &= 077; + sim_debug(DEBUG_EXP, &con_dev, "%c", sim_six_to_ascii[ch]); + sim_putchar(sim_six_to_ascii[ch]); + break; + } + sim_activate(uptr, 100); + return SCPE_OK; + } + + /* Copy next column over */ + if ((uptr->u5 & URCSTA_INPUT) == 0 && uptr->u5 & URCSTA_READ) { + sim_debug(DEBUG_DATA, &con_dev, "%d: Char > %02o %x\n", u, + con_data[u].ibuff[uptr->u3], chan_flags[chan]); + switch(chan_write_char(chan, &con_data[u].ibuff[uptr->u3], + ((uptr->u3+1) == con_data[u].inptr)? DEV_REOR: 0)) { + case TIME_ERROR: + case END_RECORD: + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY; + uptr->u5 &= ~URCSTA_READ; + sim_debug(DEBUG_EXP, &con_dev, "EOR"); + chan_clear_attn_inq(chan); + con_data[u].inptr = 0; + break; + case DATA_OK: + uptr->u3++; + break; + } + sim_activate(uptr, 10); + return SCPE_OK; + } + + r = sim_poll_kbd(); + if (r & SCPE_KFLAG) { + ch = r & 0377; + if (uptr->u5 & URCSTA_INPUT) { + /* Handle end of buffer */ + switch (ch) { + case '\r': + case '\n': + uptr->u5 &= ~URCSTA_INPUT; + sim_putchar('\r'); + sim_putchar('\n'); + chan_set_attn_inq(chan); + break; + case 033: + uptr->u5 &= ~URCSTA_INPUT; + con_data[u].inptr = 0; + break; + case '\b': + if (con_data[u].inptr != 0) { + con_data[u].inptr--; + sim_putchar(ch); + } + break; + default: + if (con_data[u].inptr < sizeof(con_data[u].ibuff)) { + ch = sim_ascii_to_six[0177&ch]; + if (ch == 0xff) { + sim_putchar('\007'); + break; + } + sim_putchar(sim_six_to_ascii[ch]); + con_data[u].ibuff[con_data[u].inptr++] = ch; + } + break; + } + } else { + if (ch == 033) { + if (con_data[u].inptr != 0) { + chan_clear_attn_inq(chan); + } else { +#ifdef I7070 + chan_set_attn_inq(chan); +#endif + sim_putchar('I'); + sim_putchar(' '); + uptr->u5 |= URCSTA_INPUT; + } + con_data[u].inptr = 0; + } + } + } + sim_activate(uptr, 500); + return SCPE_OK; +} + +t_stat +con_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "Supervisory Printer\n\n"); + fprintf (st, "This is the interface from the operator to the system. The printer\n"); + fprintf (st, "operated in a half duplex mode. To request the system to accept input\n"); + fprintf (st, "press the key and wait until the system responds with a line with\n"); + fprintf (st, "I as the first character. When you have finished typing your line, press\n"); + fprintf (st, "return or enter key. Backspace will delete the last character.\n"); + fprintf (st, "All responses from the system are prefixed with a R and blank as the\n"); + fprintf (st, "first character\n"); + return SCPE_OK; +} + +const char * +con_description(DEVICE *dptr) +{ + return "Supervisory Printer"; +} + +#endif + diff --git a/I7000/i7000_defs.h b/I7000/i7000_defs.h new file mode 100644 index 00000000..78bd2851 --- /dev/null +++ b/I7000/i7000_defs.h @@ -0,0 +1,642 @@ +/* i7000_defs.h: IBM 70xx simulator definitions + + 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. + + Generic channel interface for all processors in IBM 700 and 7000 line. +*/ + +#ifndef _I7000_H_ +#define _I7000_H_ + +#include "sim_defs.h" /* simulator defns */ + +/* Definitions for each supported CPU */ + +#ifdef I701 +#define NUM_CHAN 1 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_DR 1 +#define NUM_DEVS_MT 0 +#define MT_CHANNEL_ZERO +#define NUM_UNITS_MT 5 +#define NUM_UNITS_DR 16 +#define MAXMEMSIZE 2048 +#define CHARSPERWORD 6 +extern t_uint64 M[]; +#endif +#ifdef I7010 /* Includes 1410 and 7010 */ +#define NUM_CHAN 5 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CDP 1 +#define STACK_DEV 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_DSK 5 +#define NUM_DEVS_COM 1 +#define NUM_DEVS_MT 3 +#define CHAN_CHUREC 1 +#define NUM_UNITS_MT 10 /* A, B */ +#define MAXMEMSIZE (100000) +#define CHARSPERWORD 1 +extern uint8 M[]; +#endif +#ifdef I7030 +/* Not yet */ +#endif +#ifdef I7070 /* Includes 7070, 7074 */ +#define NUM_CHAN 9 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_MT 3 +#define NUM_DEVS_DSK 10 +#define NUM_DEVS_HT 0 +#define NUM_DEVS_COM 1 +#define NUM_UNITS_HT 10 +#define NUM_UNITS_MT 10 /* A, B */ +#define NUM_DEVS_CHRON 1 +#define MAXMEMSIZE (30000) +#define CHARSPERWORD 5 +extern t_uint64 M[]; +#endif +#ifdef I7080 /* Includes 702, 705-i/ii, 705-iii, 7080 */ +#define NUM_CHAN 11 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_MT 4 +#define NUM_DEVS_CHRON 1 +#define NUM_DEVS_DR 1 +#define NUM_DEVS_DSK 5 +#define NUM_DEVS_HT 0 +#define NUM_DEVS_COM 1 +#define NUM_UNITS_MT 10 +#define MT_CHANNEL_ZERO +#define NUM_UNITS_HT 10 +#define MAXMEMSIZE (160000) +#define CHARSPERWORD 1 +extern uint8 M[]; +#endif +#ifdef I704 /* Special build for 704 only */ +#define NUM_CHAN 1 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_DR 1 +#define NUM_DEVS_MT 0 +#define NUM_UNITS_MT 10 +#define MT_CHANNEL_ZERO +#define NUM_UNITS_DR 16 +#define MAXMEMSIZE (32*1024) +#define CHARSPERWORD 6 +extern t_uint64 M[]; +#endif +#ifdef I7040 /* Includes 7040, 7044 */ +/* Not yet */ +#define NUM_CHAN 8 +#define NUM_DEVS_CDP 2 +#define NUM_DEVS_CDR 2 +#define NUM_DEVS_LPR 2 +#define NUM_DEVS_MT 4 +#define NUM_DEVS_CHRON 1 +#define NUM_DEVS_DSK 10 +#define NUM_DEVS_COM 1 +#define NUM_DEVS_HD 1 +#define NUM_DEVS_HT 0 +#define MT_CHANNEL_ZERO +#define NUM_UNITS_HT 10 +#define NUM_UNITS_MT 10 /* A, B */ +#define NUM_UNITS_HD 8 +#define MAXMEMSIZE (32*1024) +#define CHARSPERWORD 6 +extern t_uint64 M[]; +#endif +#ifdef I7090 /* Includes 704, 709, 7090, 7094 */ +#define NUM_CHAN 9 +#define NUM_DEVS_CDP 4 +#define NUM_DEVS_CDR 4 +#define NUM_DEVS_LPR 4 +#define NUM_DEVS_MT 3 +#define NUM_DEVS_CHRON 1 +#define NUM_DEVS_DR 1 +#define NUM_DEVS_DSK 10 +#define NUM_DEVS_COM 1 +#define NUM_DEVS_HD 1 +#define NUM_DEVS_HT 0 +#define MT_CHANNEL_ZERO +#define NUM_UNITS_HT 10 +#define NUM_UNITS_MT 10 /* A, B */ +#define NUM_UNITS_DR 16 +#define NUM_UNITS_HD 8 +#define MAXMEMSIZE (64*1024) +#define CHARSPERWORD 6 +/*#define EXTRA_SL */ /* Remove comments to allow 4 extra sense lights */ +/*#define EXTRA_SW */ /* Remove comments to allow 6 extra switchs */ +extern t_uint64 M[]; +#endif + +/* Simulation stop codes. */ +#define STOP_IONRDY 1 /* I/O dev not ready */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_UUO 4 /* invalid opcode */ +#define STOP_INDLIM 5 /* indirect limit */ +#define STOP_XECLIM 6 /* XEC limit */ +#define STOP_IOCHECK 7 /* IOCHECK */ +#define STOP_MMTRP 8 /* mm in trap */ +#define STOP_INVLIN 9 /* 7750 invalid line number */ +#define STOP_INVMSG 10 /* 7750 invalid message */ +#define STOP_NOOFREE 11 /* 7750 No free output buffers */ +#define STOP_NOIFREE 12 /* 7750 No free input buffers */ +#define STOP_FIELD 13 /* Field overflow */ +#define STOP_ACOFL 13 /* AC Overflow - 7080 */ +#define STOP_SIGN 14 /* Sign change */ +#define STOP_DIV 15 /* Divide error */ +#define STOP_INDEX 16 /* 7070 Alpha index */ +#define STOP_NOWM 17 /* Stop if no word mark found */ +#define STOP_INVADDR 18 /* Stop on invalid address */ +#define STOP_INVLEN 19 /* Invalid length instruction */ +#define STOP_RECCHK 19 /* Record check - 7080 */ +#define STOP_PROG 20 /* Program fault */ +#define STOP_PROT 21 /* Protection fault */ + +/* Memory */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEMMASK (MEMSIZE - 1) /* Memory bits */ + +/* Globally visible flags */ +#define CHAN_PIO (0) /* Polled mode I/O */ +#define CHAN_UREC (1) /* Unit record devices */ +#define CHAN_7010 (1) /* Channel type for 7010 */ +#define CHAN_7604 (2) /* 7070 tape controller */ +#define CHAN_7607 (2) /* Generic 7090 channel */ +#define CHAN_7621 (2) /* 7080 tape controller */ +#define CHAN_7904 (3) /* 7040 generic channel */ +#define CHAN_7907 (3) /* Disk/Hyper/7750 channel for 7070 */ +#define CHAN_7908 (3) /* Disk/Hyper/7750 channel for 7080 */ +#define CHAN_7909 (3) /* Disk/Hyper/7750 channel for 7090 */ +#define CHAN_7289 (4) /* Special CTSS device for 7090 */ +#define CHAN_754 (4) /* 705 tape controller */ + +#define CH_TYP_PIO 01 /* Can be on PIO channel */ +#define CH_TYP_UREC 02 /* Can be on Unit record channel */ +#define CH_TYP_76XX 04 /* Can be on 76xx Channel */ +#define CH_TYP_79XX 010 /* Can be on a 79xx channel */ +#define CH_TYP_SPEC 020 /* Special channel */ +#define CH_TYP_754 020 /* 705 tape controller */ + +/* Device information block */ +struct dib { + uint8 ctype; /* Type of channel */ + uint8 upc; /* Units per channel */ + uint16 addr; /* Unit address */ + uint16 mask; /* Channel mask type */ + uint32 (*cmd)(UNIT *up, uint16 cmd, uint16 dev);/* Issue command. */ + void (*ini)(UNIT *up, t_bool f); +}; + +typedef struct dib DIB; + + +/* Debuging controls */ +#define DEBUG_CHAN 0x0000001 /* Show channel fetchs */ +#define DEBUG_TRAP 0x0000002 /* Show CPU Traps */ +#define DEBUG_CMD 0x0000004 /* Show device commands */ +#define DEBUG_DATA 0x0000008 /* Show data transfers */ +#define DEBUG_DETAIL 0x0000020 /* Show details */ +#define DEBUG_EXP 0x0000040 /* Show error conditions */ +#define DEBUG_SNS 0x0000080 /* Shows sense data for 7909 devs */ +#define DEBUG_CTSS 0x0000100 /* Shows CTSS specail instructions */ +#define DEBUG_PRIO 0x0000100 /* Debug Priority mode on 7010 */ +#define DEBUG_PROT 0x0000200 /* Protection traps */ + +extern DEBTAB dev_debug[]; +extern DEBTAB crd_debug[]; + +/* Channels */ +#define CHAN_CHPIO 0 /* Puesdo access for 704 */ +#ifndef CHAN_CHUREC +#define CHAN_CHUREC 0 /* Unit record devices */ +#endif +#define CHAN_A 1 +#define CHAN_B 2 +#define CHAN_C 3 +#define CHAN_D 4 +#define CHAN_E 5 +#define CHAN_F 6 +#define CHAN_G 7 +#define CHAN_H 8 + +#define UNIT_V_SELECT (UNIT_V_UF + 9) /* 9 */ +#define UNIT_SELECT (1 << UNIT_V_SELECT) +#define UNIT_V_CHAN (UNIT_V_SELECT + 1) /* 10 */ +#define UNIT_CHAN (017 << UNIT_V_CHAN) /* 10-14 */ +#define UNIT_S_CHAN(x) (UNIT_CHAN & ((x) << UNIT_V_CHAN)) +#define UNIT_G_CHAN(x) ((UNIT_CHAN & (x)) >> UNIT_V_CHAN) +#define UNIT_V_LOCAL (UNIT_V_UF + 0) /* 0 */ +#define DEV_BUF_NUM(x) (((x) & 07) << DEV_V_UF) +#define GET_DEV_BUF(x) (((x) >> DEV_V_UF) & 07) +#define UNIT_V_MODE (UNIT_V_LOCAL + 1) /* 1 */ + +/* Specific to channel devices */ +#define UNIT_V_MODEL (UNIT_V_UF + 0) +#define CHAN_MODEL (07 << UNIT_V_MODEL) +#define CHAN_S_TYPE(x) (CHAN_MODEL & ((x) << UNIT_V_MODEL)) +#define CHAN_G_TYPE(x) ((CHAN_MODEL & (x)) >> UNIT_V_MODEL) +#define UNIT_V_AUTO (UNIT_V_UF + 4) +#define CHAN_AUTO (1 << UNIT_V_AUTO) +#define UNIT_V_SET (UNIT_V_UF + 5) +#define CHAN_SET (1 << UNIT_V_SET) + +extern t_value assembly[NUM_CHAN]; /* Assembly register */ +/* I/O routine functions */ +/* Channel half of controls */ +/* Channel status */ +extern uint32 chan_flags[NUM_CHAN]; /* Channel flags */ +extern const char *chname[11]; /* Channel names */ +extern int num_devs[NUM_CHAN]; /* Number devices per channel*/ +extern uint8 lpr_chan9[NUM_CHAN]; +#ifdef I7010 +extern uint8 lpr_chan12[NUM_CHAN]; +#endif + +/* Sense information for 7909 channels */ +#define SNS_IOCHECK 0x00000400 /* IO Check */ +#define SNS_SEQCHECK 0x00000200 /* Sequence check */ +#define SNS_UEND 0x00000100 /* Unusual end */ +#define SNS_ATTN1 0x00000080 /* Attention 1 */ +#define SNS_ATTN2 0x00000040 /* Attention 2 */ +#define SNS_ADCHECK 0x00000020 /* Adaptor check */ +#define CTL_PREAD 0x00000010 /* Prepare to read */ +#define CTL_PWRITE 0x00000008 /* Prepare to write */ +#define CTL_READ 0x00000004 /* Read Status */ +#define CTL_WRITE 0x00000002 /* Write Status */ +#define SNS_IRQ 0x00000001 /* IRQ */ +#define SNS_MASK 0x000007fe /* Mask of sense codes */ +#define SNS_IRQS 0x000007e0 +#define SNS_IMSK 0x00000620 /* Non maskable irqs */ +#define CTL_END 0x00000800 /* Transfer is done */ +#define CTL_INHB 0x00001000 /* Interupts inhibited */ +#define CTL_SEL 0x00002000 /* Device select */ +#define CTL_SNS 0x00004000 /* Sense transfer */ +#define CTL_CNTL 0x00008000 /* Control transfer */ +/* Channel status infomation */ +#define STA_PEND 0x00010000 /* Pending LCH instruct */ +#define STA_ACTIVE 0x00020000 /* Channel active */ +#define STA_WAIT 0x00040000 /* Channel waiting for EOR */ +#define STA_START 0x00080000 /* Channel was started, but not reset */ +#define STA_TWAIT 0x00100000 /* Channel waiting on IORT */ +/* Device error controls */ +#define CHS_EOT 0x00200000 /* Channel at EOT */ +#define CHS_BOT 0x00400000 /* Channel at BOT */ +#define CHS_EOF 0x00800000 /* Channel at EOF */ +#define CHS_ERR 0x01000000 /* Channel has Error */ +#define CHS_ATTN 0x02000000 /* Channel attention*/ +/* Device half of controls */ +#define DEV_SEL 0x04000000 /* Channel selected */ +#define DEV_WRITE 0x08000000 /* Device is writing to memory */ +#define DEV_FULL 0x10000000 /* Buffer full */ +#define DEV_REOR 0x20000000 /* Device at End of Record */ +#define DEV_DISCO 0x40000000 /* Channel is done with device */ +#define DEV_WEOR 0x80000000 /* Channel wants EOR written */ + +/* Device status information stored in u5 */ +#define URCSTA_EOF 0001 /* Hit end of file */ +#define URCSTA_ERR 0002 /* Error reading record */ +#define URCSTA_CARD 0004 /* Unit has card in buffer */ +#define URCSTA_FULL 0004 /* Unit has full buffer */ +#define URCSTA_BUSY 0010 /* Device is busy */ +#define URCSTA_WDISCO 0020 /* Device is wait for disconnect */ +#define URCSTA_READ 0040 /* Device is reading channel */ +#define URCSTA_WRITE 0100 /* Device is reading channel */ +#define URCSTA_INPUT 0200 /* Console fill buffer from keyboard */ +#define URCSTA_ON 0200 /* 7090 Unit is on */ +#define URCSTA_IDLE 0400 /* 7090 Unit is idle */ +#define URCSTA_WMKS 0400 /* Printer print WM as 1 */ +#define URCSTA_SKIPAFT 01000 /* Skip to line after printing next line */ +#define URCSTA_NOXFER 01000 /* Don't set up to transfer after feed */ +#define URCSTA_LOAD 01000 /* Load flag for 7070 card reader */ +#define URCSTA_CMD 01000 /* 7090 Command recieved */ + +/* Boot from given device */ +t_stat chan_boot(int32 unit_num, DEVICE *dptr); + +/* Sets the device onto a given channel */ +t_stat chan_set_devs(DEVICE *dptr); +t_stat set_chan(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat set_cchan(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat print_chan(FILE *st, UNIT *uptr, int32 v, CONST void *desc); +t_stat get_chan(FILE *st, UNIT *uptr, int32 v, CONST void *desc); +t_stat chan9_set_select(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat chan9_get_select(FILE *st, UNIT *uptr, int32 v, CONST void *desc); + +/* Check channel for error */ +int chan_error(int chan); + +/* Check channel for flags, clear flags if set */ +int chan_stat(int chan, uint32 flag); + +/* Check channel for flags set */ +int chan_test(int chan, uint32 flag); + +/* Check channel is active */ +int chan_active(int chan); + +/* Check channel is selected */ +int chan_select(int chan); + +/* Channel data handling char at a time */ +int chan_write_char(int chan, uint8 *data, int flags); +int chan_read_char(int chan, uint8 *data, int flags); + +/* Flag end of file on channel */ +void chan_set_eof(int chan); + +/* Flag error on channel */ +void chan_set_error(int chan); + +/* Flag attention on channel */ +void chan_set_attn(int chan); + +/* Start a selection command on channel */ +void chan_set_sel(int chan, int need); +void chan_clear_status(int chan); + +/* Set or clear a flag */ +void chan_set(int chan, uint32 flag); +void chan_clear(int chan, uint32 flag); + +/* Channel 7909 special error posting */ +void chan9_clear_error(int chan, int sel); +void chan9_set_attn(int chan, int sel); +void chan9_set_error(int chan, uint32 mask); + +void chan_proc(); + +#ifdef I7010 +/* Sets the device that will interrupt on the channel. */ +t_stat set_urec(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat get_urec(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +/* Execute the next channel instruction. */ +void chan_set_attn_urec(int chan, uint16 addr); +void chan_set_attn_inq(int chan); +void chan_clear_attn_inq(int chan); +#endif + +#ifdef I7070 +void chan_set_attn_a(int chan); +void chan_set_attn_b(int chan); +void chan_set_attn_inq(int chan); +void chan_clear_attn_inq(int chan); +void chan_set_load_mode(int chan); +#endif + +#ifdef I7080 +void chan_set_attn_inq(int chan); +void chan_clear_attn_inq(int chan); +#endif + +/* Convert micro seconds to click ticks */ +#define us_to_ticks(us) (((us) * 10) / cycle_time) + +/* Returns from chan_read/chan_write */ +#define DATA_OK 0 /* Data transfered ok */ +#define TIME_ERROR 1 /* Channel did not transfer last operation */ +#define END_RECORD 2 /* End of record */ + +/* Returns from device commands */ +#define SCPE_BUSY (1) /* Device is active */ +#define SCPE_NODEV (2) /* No device exists */ + +/* I/O Command codes */ +#define IO_RDS 1 /* Read record */ +#define IO_BSR 2 /* Backspace one record */ +#define IO_BSF 3 /* Backspace one file */ +#define IO_WRS 4 /* Write one record */ +#define IO_WEF 5 /* Write eof */ +#define IO_REW 6 /* Rewind */ +#define IO_DRS 7 /* Set unit offline */ +#define IO_SDL 8 /* Set density low */ +#define IO_SDH 9 /* Set density high */ +#define IO_RUN 10 /* Rewind and unload unit */ +#define IO_TRS 11 /* Check it unit ready */ +#define IO_CTL 12 /* Io control device specific */ +#define IO_RDB 13 /* Read backwards */ +#define IO_SKR 14 /* Skip record forward */ +#define IO_ERG 15 /* Erase next records from tape */ + +/* Global device definitions */ +#ifdef CPANEL +extern DEVICE cp_dev; +#endif + +#ifdef NUM_DEVS_TP +extern DIB tp_dib; +extern uint32 tp_cmd(UNIT *, uint16, uint16); +extern DEVICE tpa_dev; +#endif + +#ifdef NUM_DEVS_CDR +extern DIB cdr_dib; +extern DEVICE cdr_dev; +extern uint32 cdr_cmd(UNIT *, uint16, uint16); +#endif + +#ifdef NUM_DEVS_CDP +extern DIB cdp_dib; +extern DEVICE cdp_dev; +extern uint32 cdp_cmd(UNIT *, uint16, uint16); +extern void cdp_ini(UNIT *, t_bool); +#endif + +#ifdef STACK_DEV +extern DEVICE stack_dev; +#endif + +#ifdef NUM_DEVS_LPR +extern DIB lpr_dib; +extern DEVICE lpr_dev; +extern uint32 lpr_cmd(UNIT *, uint16, uint16); +extern void lpr_ini(UNIT *, t_bool); +#endif + +#ifdef NUM_DEVS_CON +extern DIB con_dib; +extern DEVICE con_dev; +extern uint32 con_cmd(UNIT *, uint16, uint16); +extern void con_ini(UNIT *, t_bool); +#endif + +#ifdef NUM_DEVS_CHRON +extern DIB chron_dib; +extern DEVICE chron_dev; +extern uint32 chron_cmd(UNIT *, uint16, uint16); +#endif + +#ifdef NUM_DEVS_COM +extern uint32 com_cmd(UNIT *, uint16, uint16); +extern DIB com_dib; +extern DEVICE com_dev; +extern DEVICE coml_dev; +#endif + +#ifdef NUM_DEVS_DR +extern uint32 drm_cmd(UNIT *, uint16, uint16); +extern void drm_ini(UNIT *, t_bool); +extern DIB drm_dib; +extern DEVICE drm_dev; +#endif + +#ifdef NUM_DEVS_DSK +extern uint32 dsk_cmd(UNIT *, uint16, uint16); +extern void dsk_ini(UNIT *, t_bool); +extern DIB dsk_dib; +extern DEVICE dsk_dev; +#endif + +#ifdef NUM_DEVS_HD +extern uint32 hsdrm_cmd(UNIT *, uint16, uint16); +extern void hsdrm_ini(UNIT *, t_bool); +extern DIB hsdrm_dib; +extern DEVICE hsdrm_dev; +#endif + +#ifdef NUM_DEVS_HT +extern DIB ht_dib; +extern uint32 ht_cmd(UNIT *, uint16, uint16); +extern DEVICE hta_dev; +#if NUM_DEVS_HT > 1 +extern DEVICE htb_dev; +#endif +#endif + +#if (NUM_DEVS_MT > 0) || defined(MT_CHANNEL_ZERO) +extern DIB mt_dib; +extern uint32 mt_cmd(UNIT *, uint16, uint16); +extern void mt_ini(UNIT *, t_bool); +#ifdef MT_CHANNEL_ZERO +extern DEVICE mtz_dev; +#endif +#if NUM_DEVS_MT > 0 +extern DEVICE mta_dev; +#if NUM_DEVS_MT > 1 +extern DEVICE mtb_dev; +#if NUM_DEVS_MT > 2 +extern DEVICE mtc_dev; +#if NUM_DEVS_MT > 3 +extern DEVICE mtd_dev; +#if NUM_DEVS_MT > 4 +extern DEVICE mte_dev; +#if NUM_DEVS_MT > 5 +extern DEVICE mtf_dev; +#endif /* 5 */ +#endif /* 4 */ +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ +#endif /* NUM_DEVS_MT */ + +/* Character codes */ +#define CHR_ABLANK 000 +#define CHR_MARK CHR_ABLANK +#define CHR_1 001 +#define CHR_2 002 +#define CHR_3 003 +#define CHR_4 004 +#define CHR_5 005 +#define CHR_6 006 +#define CHR_7 007 +#define CHR_8 010 +#define CHR_9 011 +#define CHR_0 012 +#define CHR_EQ 013 +#define CHR_QUOT 014 /* Also @ */ +#define CHR_COL 015 +#define CHR_GT 016 +#define CHR_TRM 017 +#define CHR_BLANK 020 +#define CHR_SLSH 021 +#define CHR_S 022 +#define CHR_T 023 +#define CHR_U 024 +#define CHR_V 025 +#define CHR_W 026 +#define CHR_X 027 +#define CHR_Y 030 +#define CHR_Z 031 +#define CHR_RM 032 +#define CHR_COM 033 +#define CHR_RPARN 034 /* Also % */ +#define CHR_WM 035 +#define CHR_BSLSH 036 +#define CHR_UND 037 +#define CHR_MINUS 040 +#define CHR_J 041 +#define CHR_K 042 +#define CHR_L 043 +#define CHR_M 044 +#define CHR_N 045 +#define CHR_O 046 +#define CHR_P 047 +#define CHR_Q 050 +#define CHR_R 051 +#define CHR_EXPL 052 +#define CHR_DOL 053 +#define CHR_STAR 054 +#define CHR_LBRK 055 +#define CHR_SEMI 056 +#define CHR_CART 057 +#define CHR_PLUS 060 +#define CHR_A 061 +#define CHR_B 062 +#define CHR_C 063 +#define CHR_D 064 +#define CHR_E 065 +#define CHR_F 066 +#define CHR_G 067 +#define CHR_H 070 +#define CHR_I 071 +#define CHR_QUEST 072 +#define CHR_DOT 073 +#define CHR_LPARN 074 /* Also Square */ +#define CHR_RBRAK 075 +#define CHR_LESS 076 +#define CHR_GM 077 + +/* Generic devices common to all */ +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE chan_dev; +extern UNIT chan_unit[NUM_CHAN]; +extern REG cpu_reg[]; +extern int cycle_time; + +extern const char mem_to_ascii[64]; + +extern const char *cpu_description(DEVICE *dptr); +extern const char *chan_type_name[]; +extern void help_set_chan_type(FILE *st, DEVICE *dptr, const char *name); + +#endif /* _I7000_H_ */ diff --git a/I7000/i7000_dsk.c b/I7000/i7000_dsk.c new file mode 100644 index 00000000..e51c3c19 --- /dev/null +++ b/I7000/i7000_dsk.c @@ -0,0 +1,1887 @@ +/* i7090_disk.c: IBM 7090 Disk + + 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 CORNRWELL 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. + + Support for 1301/1302/2302 disks and 7238 drums + + Disks are represented in files as follows: + + Since these drives supported variable format for each cylinder the + format is represented as one track per cylinder as follows: + + 0 data + 1 header + 2 Home Address + 3 end of track + + These codes are packed 4 per byte and used to control read/write of + data. + + After one format track per cylinder there is one record of bytes per + track data for each track. First bytes are home address 2, followed + by record address, and record data to cover number in format. All data + is stored with the top 2 bits as zero. + + Limitiation of this are that the address field for each record can not + be more then 16 bytes. + +*/ + +#include "i7000_defs.h" + +#ifdef NUM_DEVS_DSK +#define UNIT_DSK UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX +#define FORMAT_OK (1 << (UNIT_V_LOCAL+0)) +#define HA2_OK (1 << (UNIT_V_LOCAL+1)) +#define CTSS_BOOT (1 << (UNIT_V_MODE)) + +/* Device status information stored in u5 */ +#define DSKSTA_CMD 0x0000100 /* Unit has recieved a cmd */ +#define DSKSTA_DATA 0x0000200 /* Unit has finished cmd */ +#define DSKSTA_WRITE 0x0000400 /* Last command was a write */ +#define DSKSTA_CHECK 0x0000800 /* Doing a write check */ +#define DSKSTA_CMSK 0x00000ff /* Command mask */ +#define DSKSTA_ARGMSK 0x0fff000 /* Command argument */ +#define DSKSTA_ARGSHFT 12 +#define DSKSTA_SCAN 0x1000000 /* Scanning for header */ +#define DSKSTA_SKIP 0x2000000 /* Skiping record */ +#define DSKSTA_XFER 0x4000000 /* Tranfser current record */ +#define DSKSTA_DIRTY 0x8000000 /* Buffer needs to be written */ + +#define FMT_DATA 0 /* Data */ +#define FMT_HDR 1 /* Header */ +#define FMT_HA2 2 /* Home address 2 */ +#define FMT_END 3 /* End of track */ + +/* Disk commands */ +#define DNOP 0x00 /* Nop */ +#define DREL 0x04 /* Release */ +#define DEBM 0x08 /* Eight Bit mode */ +#define DSBM 0x09 /* Six bit mode */ +#define DSEK 0x80 /* Seek */ +#define DVSR 0x82 /* Prepare to Verify single record */ +#define DWRF 0x83 /* Prepare to Format */ +#define DVTN 0x84 /* Prepare to Verify track no addr */ +#define DVCY 0x85 /* Prepare to Verify Cyl */ +#define DWRC 0x86 /* Prepare to Write Check */ +#define DSAI 0x87 /* Set Access Inoperative */ +#define DVTA 0x88 /* Prepare to Verify track addr */ +#define DVHA 0x89 /* Prepare to Verify home addr */ + +/* Disk sense codes */ /*01234 */ +#define STAT_SIXBIT 0x00004 /* Disk in 6bit more. */ +#define EXPT_FILECHK 0x10010 /* File control check error */ +#define EXPT_DSKCHK 0x10020 /* Disk storage error */ +#define STAT_NOTRDY 0x10040 /* Disk no ready */ +#define STAT_OFFLINE 0x10080 /* Disk offline */ +#define DATA_PARITY 0x20100 /* Data parity error */ +#define DATA_CHECK 0x20200 /* Compare error */ +#define DATA_RESPONSE 0x20400 /* Response check */ +#define PROG_INVADDR 0x40800 /* Invalid seek address */ +#define PROG_NOREC 0x41000 /* No record found */ +#define PROG_FMTCHK 0x42000 /* Format check */ +#define PROG_INVCODE 0x44000 /* Invalid code */ +#define PROG_INVSEQ 0x48000 /* Invalid sequence */ + +#define MAXTRACK 6020 /* Max size per track */ + +uint32 dsk_cmd(UNIT *, uint16, uint16); +t_stat dsk_srv(UNIT *); +t_stat dsk_boot(int32, DEVICE *); +void dsk_ini(UNIT *, t_bool); +t_stat dsk_reset(DEVICE *); +t_stat dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat dsk_get_module(FILE * st, UNIT * uptr, int32 v, + CONST void *desc); +t_stat dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat dsk_get_type(FILE * st, UNIT * uptr, int32 v, + CONST void *desc); +t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *dsk_description (DEVICE *dptr); + +int disk_rblock(UNIT * uptr, int track); +int disk_wblock(UNIT * uptr); +void disk_posterr(UNIT * uptr, uint32 error); +void disk_cmderr(UNIT * uptr, uint32 error); +int disk_cmd(UNIT * uptr); +int disk_write(UNIT * uptr, uint8 data, int chan, + int eor); +int disk_read(UNIT * uptr, uint8 * data, int chan); +int disk_format(UNIT * uptr, FILE * f, int cyl, + UNIT * base); +int bcd_to_track(uint32 addr); + +/* Data buffer for track */ +uint8 dbuffer[NUM_DEVS_DSK * 4][MAXTRACK]; + +/* Format buffer for cylinder */ +uint8 fbuffer[NUM_DEVS_DSK * 4][MAXTRACK / 4]; + +/* Currently loaded format record */ +uint16 fmt_cyl[NUM_DEVS_DSK * 4]; + +/* Currently read in track in buffer */ +uint16 dtrack[NUM_DEVS_DSK * 4]; + +/* Arm position */ +uint16 arm_cyl[NUM_DEVS_DSK * 4]; +uint32 sense[NUM_CHAN * 2]; +uint32 sense_unit[NUM_CHAN * 2]; +uint8 cmd_buffer[NUM_CHAN]; /* Command buffer per channel */ +uint8 cmd_mod[NUM_CHAN]; /* Command module per channel */ +uint32 cmd_option[NUM_CHAN]; /* Command option per channel */ +uint16 cmd_count[NUM_CHAN]; /* Number of chars recieved */ + +#ifdef I7010 +extern uint8 chan_seek_done[NUM_CHAN]; /* Seek finished flag */ +extern uint8 chan_io_status[NUM_CHAN]; /* Channel status flags */ +#endif + +/* Macro to help build the disk size table */ +#define DISK_DEF(name, cyl, cylpertrk, acc, charpertrk, overhd, mod, dr) \ + { name, cyl, cylpertrk, ((charpertrk/128) + 1) * 128, \ + acc, (((charpertrk/128) + 1) * 128)/4, \ + acc * cyl * ((((charpertrk/128) + 1) * 128)/4), \ + overhd, mod, dr } + +struct disk_t +{ + const char *name; /* Type Name */ + int cyl; /* Number of cylinders */ + int track; /* Number of tracks/cylinder */ + unsigned int bpt; /* Max bytes per track */ + int arms; /* Number of access arms */ + int fbpt; /* Number of format bytes per track */ + int fmtsz; /* Format size */ + int overhd; /* Number of characters overhead on HA/RA */ + int mods; /* Number of modules */ + int datarate; /* us per chars */ +} +disk_type[] = +{ + DISK_DEF("1301", 254, 40, 1, 2880, 4, 1, 15), + DISK_DEF("1301-2", 254, 40, 1, 2880, 4, 2, 15), + DISK_DEF("1302", 254, 40, 2, 5940, 7, 1, 10), + DISK_DEF("1302-2", 254, 40, 2, 5940, 7, 2, 10), + DISK_DEF("2302", 254, 40, 2, 5940, 7, 2, 10), + DISK_DEF("7238", 1, 404, 1, 3270, 4, 1, 10), + DISK_DEF("7238-2", 1, 404, 1, 3270, 4, 2, 10), { + NULL, 0} +}; + +int unit_bit[] = { + /*0 1 2 3 4 5 6 7 8 9 A B C D E F */ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 30, 30, 30, 30, 30, 30, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 30, 30, 30, 30, 30, 30 +}; + +#define DSKSTA_BSY 0x02 /* Controller busy. */ +#define DSKSTA_EIGHT 0x04 /* Controller in 8 bit mode */ + +#ifdef I7090 +#define CH1 4 +#define CH2 6 +#endif +#ifdef I7070 +#define CH1 5 +#define CH2 6 +#endif +#ifdef I7080 +#define CH1 5 +#define CH2 6 +#endif +#ifndef CH1 +#define CH1 1 +#endif +#ifndef CH2 +#define CH2 2 +#endif + +UNIT dsk_unit[] = { + {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x000, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x102, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x204, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x306, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH1) | UNIT_DSK, 0), 0, 0x408, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x500, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x602, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x704, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x806, 0}, + {UDATA(&dsk_srv, UNIT_S_CHAN(CH2) | UNIT_DSK, 0), 0, 0x908, 0}, +/* Second set for arm two of each unit */ + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, +/* Third set for arm one of second module */ + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, +/* Fourth set for arm two of second module */ + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, + {UDATA(&dsk_srv, UNIT_DIS, 0), 0, 0xff, 0}, +}; + +MTAB dsk_mod[] = { + {FORMAT_OK, 0, 0, "NOFORMAT", NULL, NULL, NULL, "Format not allowed"}, + {FORMAT_OK, FORMAT_OK, "FORMAT", "FORMAT", NULL, NULL, NULL, + "Format allowed"}, + {HA2_OK, 0, 0, "NOHA2", NULL, NULL, NULL, "No writing of Home Address"}, + {HA2_OK, HA2_OK, "HA2", "HA2", NULL, NULL, NULL, + "Allow writing of Home Address"}, +#ifdef I7090 + {CTSS_BOOT, 0, 0, "IBSYS", NULL, NULL, NULL, "IBSYS Boot Card"}, + {CTSS_BOOT, CTSS_BOOT, "CTSS", "CTSS", NULL, NULL, NULL, "CTSS Boot Card"}, +#endif + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &dsk_set_type, &dsk_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "MODULE", "MODULE", + &dsk_set_module, &dsk_get_module, NULL, "Module number"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", + &set_chan, &get_chan, NULL, "Channel number"}, +#ifndef I7010 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "SELECT", "SELECT", + &chan9_set_select, &chan9_get_select, NULL, "Unit select"}, +#endif + {0} +}; + +DEVICE dsk_dev = { + "DK", dsk_unit, NULL /* Registers */ , dsk_mod, + NUM_DEVS_DSK, 8, 15, 1, 8, 8, + NULL, NULL, &dsk_reset, &dsk_boot, NULL, NULL, + &dsk_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dsk_help, NULL, NULL, &dsk_description +}; + +uint32 dsk_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan; + int u = (uptr->u3 >> 8) & 0xf; + UNIT *base = &dsk_unit[u]; + int sel; + + chan = UNIT_G_CHAN(dsk_unit[u].flags); + sel = (base->flags & UNIT_SELECT) ? 1 : 0; +#ifdef I7010 + if (cmd & 0x100) + sense[(chan * 2) + sel] |= STAT_SIXBIT; + else + sense[(chan * 2) + sel] &= ~STAT_SIXBIT; + cmd_buffer[chan] = cmd & 0xff; + cmd_count[chan] = 2; + sim_debug(DEBUG_CHAN, &dsk_dev, "unit %d = cmd=%02x\n\r", + dev, cmd & 0xff); +#else + cmd_buffer[chan] = 0; + cmd_count[chan] = 0; + sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d cmd\n\r", dev); +#endif + cmd_option[chan] = 0; + cmd_mod[chan] = 0; + chan_clear(chan, DEV_SEL); + + /* If device is not active start it working */ + if (!sim_is_active(uptr)) + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; +} + +t_stat dsk_srv(UNIT * uptr) +{ + int chan; + int sel; + int schan; + int dev = uptr->u3 & 0xff; + int u = (uptr->u3 >> 8) & 0xf; + struct disk_t *dsk = &disk_type[uptr->u4]; + UNIT *base = &dsk_unit[u]; + uint8 ch = 0; + int eor = 0; + + chan = UNIT_G_CHAN(base->flags); + sel = (base->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + /* Make sure channel is talking to us */ + if (sel != chan_test(chan, CTL_SEL)) + goto seeking; + /* Channel has disconnected, abort current operation. */ + if (chan_test(chan, DEV_DISCO)) { + int i; + + /* Scan all units and stop them if they are on this channel */ + for (i = 0; i < NUM_DEVS_DSK; i++) { + int xchan = UNIT_G_CHAN(dsk_unit[i].flags); + int j; + + if (xchan != chan) + continue; + for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK) { + disk_wblock(&dsk_unit[j]); /* Flush block */ + if (dsk_unit[j].u5 & DSKSTA_CMD) { + dsk_unit[j].u5 &= + ~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN | DSKSTA_DATA); + sim_cancel(&dsk_unit[j]); + } + } + } + chan_clear(chan, (DEV_DISCO | DEV_WEOR | DEV_SEL)); + sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d disconnecting\n\r", dev); + if (uptr->wait > 0) + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + + /* Call channel proccess to make sure all date is available. */ + chan_proc(); + /* Handle sending sense data */ + if (chan_test(chan, CTL_SNS)) { + chan9_clear_error(chan, sel); + switch(cmd_count[chan]) { + case 0: + sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d chan sense=%05x\n", dev, + sense[schan]); + /* fall through */ + case 1: case 2: case 3: case 4: + ch = (sense[schan] >> (4 * (4 - cmd_count[chan]))) & 0xF; + break; + case 9: + sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d unit sense=%08x\n", dev, + sense_unit[schan]); + eor = DEV_REOR; + /* fall through */ + case 5: case 6: case 7: case 8: + ch = (sense_unit[schan] >> (4 * (9 - cmd_count[chan]))) & 0xF; + break; + } + if (ch & 010) + ch ^= 030; + sim_debug(DEBUG_SNS, &dsk_dev, "unit=%d sense %d %02o\n\r", dev, + cmd_count[chan], ch); + cmd_count[chan]++; + switch(chan_write_char(chan, &ch, eor)) { + case DATA_OK: + if (eor == 0) + break; + case TIME_ERROR: + case END_RECORD: + sense[chan] &= STAT_SIXBIT; + sense_unit[chan] = 0; + chan_set(chan, CTL_END); + chan_clear(chan, DEV_SEL); + sim_debug(DEBUG_CHAN, &dsk_dev, "unit=%d sense eor\n\r", dev); + break; + } + sim_activate(uptr, us_to_ticks(20)); + return SCPE_OK; + } + + if (chan_test(chan, CTL_CNTL) && disk_cmd(uptr)) { + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + + /* Handle writing of data */ + if (chan_test(chan, CTL_WRITE) && uptr->u5 & DSKSTA_CMD) { + /* Channel asking for end of record? */ + if (chan_stat(chan, DEV_WEOR)) { + sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan); + while (disk_write(uptr, 0x40, chan, 1) == 0) ; + disk_wblock(uptr); /* Flush block */ + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + chan_set(chan, CTL_END|DEV_REOR); + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + uptr->u5 |= DSKSTA_WRITE; /* Flag as write */ + switch(chan_read_char(chan, &ch, 0)) { + case TIME_ERROR: + disk_posterr(uptr, DATA_RESPONSE); + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + case END_RECORD: + /* If last word, fill with zeros until we get eor */ + sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r",chan); + uptr->u5 |= DSKSTA_DATA; + while (disk_write(uptr, 0x40, chan, 1) == 0) ; + disk_wblock(uptr); /* Flush block */ + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + chan_set(chan, CTL_END); + sim_activate(uptr, us_to_ticks(100)); + break; + case DATA_OK: + eor = disk_write(uptr, ch, chan, 0); + uptr->u5 |= DSKSTA_DATA; + if (eor == 1) { + /* Handle end of record */ + sim_debug(DEBUG_CHAN, &dsk_dev, + "Disk chan %d end of track\n\r", chan); + if ((uptr->u5 & DSKSTA_CMSK) == DVSR && + (uptr->u5 & DSKSTA_XFER) == 0) + disk_posterr(uptr, PROG_NOREC); + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + chan_set(chan, DEV_REOR|CTL_END); + } + sim_activate(uptr, us_to_ticks(dsk->datarate)); + return SCPE_OK; + } + } + + /* Handle reading of data */ + if (chan_test(chan, CTL_READ) && uptr->u5 & DSKSTA_CMD) { + /* Channel asking for end of record? */ + if (chan_stat(chan, DEV_WEOR)) { + sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d -> weor\n\r", chan); + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + chan_set(chan, CTL_END|DEV_REOR); + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + eor = disk_read(uptr, &ch, chan); + /* Check if we got error during read */ + if (eor == -1) { + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + switch(chan_write_char(chan, &ch, (eor)?DEV_REOR:0)) { + case TIME_ERROR: + /* Flag as timming error */ + disk_posterr(uptr, DATA_RESPONSE); + break; + case END_RECORD: + /* Fill the buffer until end of record */ + sim_debug(DEBUG_CHAN, &dsk_dev, "Disk chan %d eor\n\r", chan); + if ((uptr->u5 & DSKSTA_CMSK) == DVSR && + (uptr->u5 & DSKSTA_XFER) == 0) + disk_posterr(uptr, PROG_NOREC); + uptr->u5 &= ~(DSKSTA_SCAN | DSKSTA_XFER); + chan_set(chan, CTL_END); + uptr->u5 |= DSKSTA_DATA; + break; + case DATA_OK: + uptr->u5 |= DSKSTA_DATA; + break; + } + sim_activate(uptr, us_to_ticks(dsk->datarate)); + return SCPE_OK; + } + + /* Handle read/write without a command */ + if (chan_test(chan, CTL_WRITE|CTL_READ) && + (uptr->u3 & 0xff) == cmd_mod[chan] && + (uptr->u5 & (DSKSTA_DATA|DSKSTA_CMD)) == 0) + disk_posterr(uptr, PROG_INVSEQ); + + seeking: + if (uptr->wait || uptr->u5 & DSKSTA_CMD) + sim_activate(uptr, us_to_ticks(100)); + + /* Handle seeking */ + if (uptr->wait > 0) { + uptr->wait--; + if (uptr->wait == 0) { + sim_debug(DEBUG_EXP, &dsk_dev, "Seek done dev=%d\n", dev); + sense_unit[schan] |= 1 << unit_bit[dev]; +#ifdef I7010 + chan_seek_done[chan] = 1; +#else + chan9_set_attn(chan, sel); +#endif + } + } + + return SCPE_OK; +} + +/* Post a error on a given unit. */ +void +disk_posterr(UNIT * uptr, uint32 error) +{ + int chan; + int schan; + int sel; + int u = (uptr->u3 >> 8) & 0xf; + + uptr = &dsk_unit[u]; + uptr->u5 &= ~(DSKSTA_CMD | DSKSTA_XFER | DSKSTA_SCAN); + chan = UNIT_G_CHAN(uptr->flags); + sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + sense[schan] |= error; + if (error != 0) + chan9_set_error(chan, SNS_UEND); + chan_set(chan, DEV_REOR|CTL_END); + sim_debug(DEBUG_DETAIL, &dsk_dev, "post err dev=%d err=%08x\n", u,error); +#ifdef I7010 + if ((error & STAT_OFFLINE) == STAT_OFFLINE) + chan_io_status[chan] |= 01; + + if ((error & STAT_NOTRDY) == STAT_NOTRDY) + chan_io_status[chan] |= 02; + + if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK| + DATA_RESPONSE|PROG_INVADDR) & 0xFFFF) + chan_io_status[chan] |= 04; + + if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff) + chan_io_status[chan] |= 010; +#endif +} + +/* Post error for command that could not be completed */ +void +disk_cmderr(UNIT * uptr, uint32 error) +{ + int chan; + int schan; + int sel; + int u = (uptr->u3 >> 8) & 0xf; + + uptr = &dsk_unit[u]; + uptr->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE); + chan = UNIT_G_CHAN(uptr->flags); + sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + sense[schan] |= error; + if (error != 0) + chan9_set_error(chan, SNS_UEND); + chan_set(chan, DEV_REOR|CTL_END); + sim_debug(DEBUG_DETAIL, &dsk_dev, "cmd err dev=%d err=%08x\n", u,error); +#ifdef I7010 + if ((error & STAT_OFFLINE) == STAT_OFFLINE) + chan_io_status[chan] |= 01; + + if ((error & STAT_NOTRDY) == STAT_NOTRDY) + chan_io_status[chan] |= 02; + + if (error & (EXPT_FILECHK|EXPT_DSKCHK|DATA_PARITY|DATA_CHECK| + DATA_RESPONSE|PROG_INVADDR) & 0xFFFF) + chan_io_status[chan] |= 04; + + if (error & (PROG_NOREC|PROG_FMTCHK|PROG_INVCODE|PROG_INVSEQ) & 0xffff) + chan_io_status[chan] |= 010; +#endif +} + +/* Process command */ +int +disk_cmd(UNIT * uptr) +{ + uint8 ch; + UNIT *base; + UNIT *up; + int chan; + int schan; + int sel; + int i; + int u; + int t; + int trk; + int cyl; + + /* Get information on unit */ + u = uptr - dsk_unit; + base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; + chan = UNIT_G_CHAN(base->flags); + sel = (base->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + + /* Check if we have a command yet */ + switch(chan_read_char(chan, &ch, 0)) { + case TIME_ERROR: + disk_cmderr(uptr, DATA_RESPONSE); + case END_RECORD: + return 0; + case DATA_OK: + sim_debug(DEBUG_DATA, &dsk_dev, "unit=%d data=%02o\n", u, ch); + break; + } + + /* Place in command buffer */ + switch(cmd_count[chan]) { + case 0: + case 1: + if (ch != 012) + cmd_buffer[chan] |= (ch & 0xf) << (4 * (1 - cmd_count[chan])); + break; + case 2: + case 3: + if (ch != 012) + cmd_mod[chan] |= (ch & 0xf) << (4 * (3 - cmd_count[chan])); + break; + case 4: + case 5: + case 6: + case 7: + if (ch != 012) + cmd_option[chan] |= (ch & 0xf) << (16 + (4 * (7 - cmd_count[chan]))); + break; + case 8: + case 9: + cmd_option[chan] |= (ch & 0x3f) << (6 * (9 - cmd_count[chan])); + break; + } + + /* Need at least two chars to determine command */ + if (++cmd_count[chan] == 1) + return 1; + + /* Check if we have enough digits */ + switch(cmd_buffer[chan]) { + case DNOP: /* Nop */ + case DREL: /* Release */ + case DEBM: /* Eight Bit mode */ + case DSBM: /* Six bit mode */ + break; /* Yes */ + + case DSAI: /* Set Access Inoperative */ + if (cmd_count[chan] <= 3) + return 1; /* Need more */ + break; + case DSEK: /* Seek */ + case DWRF: /* Prepare to Format */ + case DVHA: /* Prepare to Verify home addr */ + if (cmd_count[chan] <= 7) + return 1; /* Need more */ + break; + case DVTA: /* Prepare to Verify track addr */ + case DVTN: /* Prepare to Verify track no addr */ + case DVCY: /* Prepare to Verify Cyl */ + case DVSR: /* Prepare to Verify single record */ + case DWRC: /* Prepare to Write Check */ + if (cmd_count[chan] < 10) + return 1; + break; + } + + /* Flag last item recieved */ + chan_set(chan, DEV_REOR); + chan9_clear_error(chan, sel); + + sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d cmd=%02x %02x %04x %04o ",u, + cmd_buffer[chan], cmd_mod[chan], cmd_option[chan] >> 16, + cmd_option[chan] & 07777); + /* 40 36 32 2824 2016 6 0 */ + /* 01 2 3 4 5 6 7 8 9 */ + /* op | ac mod | t t | t t | HA2 | HA2 */ + up = NULL; + + sense[schan] &= STAT_SIXBIT; + switch (cmd_buffer[chan]) { + case DNOP: /* Nop */ + case DREL: /* Release */ + /* Should not happen, but if read or write, cpy will be + * expected so turn on command flag to catch disconnect */ + sim_debug(DEBUG_CMD, &dsk_dev, "nop\n"); + goto clear_drive; + + case DEBM: /* Eight Bit mode */ + /* Should not happen, but if read or write, cpy will be + * expected so turn on command flag to catch disconnect */ + sim_debug(DEBUG_CMD, &dsk_dev, "eight bit mode\n"); + sense[schan] &= ~STAT_SIXBIT; + goto clear_drive; + + case DSBM: /* Six bit mode */ + /* Should not happen, but if read or write, cpy will be + * expected so turn on command flag to catch disconnect */ + sim_debug(DEBUG_CMD, &dsk_dev, "six bit mode\n"); + sense[schan] |= STAT_SIXBIT; +clear_drive: + /* Scan all units and clear command */ + for (i = 0; i < NUM_DEVS_DSK; i++) { + int xchan = UNIT_G_CHAN(dsk_unit[i].flags); + int j; + + if (xchan != chan) + continue; + for (j = 3 * NUM_DEVS_DSK + i; j >= 0; j -= NUM_DEVS_DSK) + dsk_unit[j].u5 &= ~ DSKSTA_CMSK; + } + sim_activate(uptr, us_to_ticks(100)); + return 1; + + case DWRC: /* Prepare to Write Check */ + sim_debug(DEBUG_CMD, &dsk_dev, "write check\n"); + case DVSR: /* Prepare to Verify single record */ + case DWRF: /* Prepare to Format */ + case DVTN: /* Prepare to Verify track no addr */ + case DVCY: /* Prepare to Verify Cyl */ + case DVTA: /* Prepare to Verify track addr */ + case DVHA: /* Prepare to Verify home addr */ + case DSAI: /* Set Access Inoperative */ + case DSEK: /* Seek */ + break; /* Go find unit to operate on */ + default: + sim_debug(DEBUG_CMD, &dsk_dev, " Unknown Command\n\r"); + /* Flag as invalid command */ + disk_cmderr(uptr, PROG_INVCODE); + return 1; + } + + /* Find actual owner of this command */ + for (i = 0; i < NUM_DEVS_DSK; i++) { + + if ((dsk_unit[i].flags & (UNIT_SELECT | UNIT_CHAN)) != + (base->flags & (UNIT_SELECT | UNIT_CHAN))) + continue; + + /* Adjust for unit */ + if ((0xff & dsk_unit[i].u3) == cmd_mod[chan]) + up = &dsk_unit[i]; + else if ((0xff & dsk_unit[i + NUM_DEVS_DSK].u3) == cmd_mod[chan]) + up = &dsk_unit[i + NUM_DEVS_DSK]; + else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 2)].u3) == cmd_mod[chan]) + up = &dsk_unit[i + (NUM_DEVS_DSK * 2)]; + else if ((0xff & dsk_unit[i + (NUM_DEVS_DSK * 3)].u3) == cmd_mod[chan]) + up = &dsk_unit[i + (NUM_DEVS_DSK * 3)]; + else + continue; + + /* Check if unit is busy */ + if (cmd_buffer[chan] != DWRC && (up->u5 & DSKSTA_CMD || up->wait > 0)) { + sim_debug(DEBUG_CMD, &dsk_dev, "unit=%d busy\n", u); + if (up->wait > 5) + up->wait = 5; + disk_cmderr(uptr, STAT_NOTRDY); + return 1; + } else { + if (cmd_buffer[chan] == DWRC) { + if (up->u5 & DSKSTA_CMSK) { + up->u5 |= DSKSTA_CHECK; + up->u5 &= ~DSKSTA_DATA; + cmd_buffer[chan] = up->u5 & DSKSTA_CMSK; + } else { + disk_cmderr(up, PROG_INVSEQ); + return 0; + } + } else { + up->u5 &= ~(DSKSTA_CMSK|DSKSTA_CHECK|DSKSTA_WRITE|DSKSTA_DATA); + up->u5 |= cmd_buffer[chan]; + } + break; + } + } + + if (up == NULL) { + sim_debug(DEBUG_CMD, &dsk_dev, "invalid unit\n"); + /* Flag as invalid command */ + disk_cmderr(uptr, STAT_OFFLINE); + return 1; + } + + /* Adjust to new unit */ + u = up - dsk_unit; + base = &dsk_unit[(up->u3 >> 8) & 0xf]; + chan = UNIT_G_CHAN(base->flags); + sel = (base->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + /* Clear unit attention */ + sense_unit[schan] &= ~(1 << unit_bit[up->u3 & 0xff]); + + /* Check if there is a unit here */ + if ((base->flags & UNIT_ATT) == 0) { + disk_cmderr(uptr, STAT_OFFLINE); + return 1; + } + + /* Find out track and cylinder of this operation */ + trk = bcd_to_track(cmd_option[chan]); + + if (chan_test(chan, CTL_PWRITE)) + sim_debug(DEBUG_CMD, &dsk_dev, "write "); + if (chan_test(chan, CTL_PREAD)) + sim_debug(DEBUG_CMD, &dsk_dev, "read "); + /* Do command */ + switch (cmd_buffer[chan]) { + case DSAI: /* Set Access Inoperative */ + detach_unit(base); + disk_cmderr(up, 0); + return 1; + + case DSEK: /* Seek */ + cyl = trk / disk_type[base->u4].track; + /* Check how far we have to move */ + t = cyl - arm_cyl[u]; + if (t < 0) + t = -t; + sim_debug(DEBUG_CMD, &dsk_dev, + "DSEK unit=%d %d cylinders to %d trk=%d\n", u, t, cyl, trk); + up->u5 &= ~(DSKSTA_CMSK | DSKSTA_CMD | DSKSTA_CHECK | DSKSTA_WRITE); + arm_cyl[u] = cyl; /* And cylinder */ + if (cyl > disk_type[base->u4].cyl) { + disk_cmderr(up, PROG_INVADDR); + return 1; + } + /* Read in track buffer */ + disk_rblock(up, trk); + + /* Set up for seek wait */ + up->wait = 0; + /* From documentation, it looks like seeks were a fixed time + * based on movement between cylinder groups */ + if (t == 0) /* At cylinder, give attention */ + up->wait = 2; /* Electronic select time */ + else if (t > 50) + up->wait = (1800); + else if (t > 10) + up->wait = (1200); + else + up->wait = (300); + break; + + case DWRF: /* Prepare to Format */ + /* Verify ok to format */ + if ((base->flags & FORMAT_OK) == 0) { + disk_cmderr(uptr, PROG_FMTCHK); + return 1; + } + + cyl = trk / disk_type[base->u4].track; + + /* Make sure positioned to correct track */ + if (arm_cyl[u] != cyl) { + disk_cmderr(uptr, PROG_INVSEQ); + return 1; + } + + /* Make sure head on valid cylinder */ + if (cyl > disk_type[base->u4].cyl) { + disk_cmderr(up, PROG_INVADDR); + return 1; + } + + fmt_cyl[u] = cyl; + sim_debug(DEBUG_CMD, &dsk_dev, "FMT unit=%d\n", u); + up->u5 |= DSKSTA_SCAN | DSKSTA_CMD | DSKSTA_WRITE; /* Flag as write */ + up->u6 = 0; + chan_set(chan, DEV_SEL); + break; + + case DVHA: /* Prepare to Verify home addr */ + /* Verify HA2 ok to write */ + if ((base->flags & HA2_OK) == 0) { + disk_cmderr(up, PROG_FMTCHK); + return 1; + } + /* fall through */ + case DVTA: /* Prepare to Verify track addr */ + case DVTN: /* Prepare to Verify track no addr */ + case DVCY: /* Prepare to Verify Cyl */ + switch (cmd_buffer[chan]) { + case DVTA: + sim_debug(DEBUG_CMD, &dsk_dev, "DVTA unit=%d ", u); + break; + case DVTN: + sim_debug(DEBUG_CMD, &dsk_dev, "DVTN unit=%d ", u); + break; + case DVCY: + sim_debug(DEBUG_CMD, &dsk_dev, "DVCY unit=%d ", u); + break; + case DVHA: + sim_debug(DEBUG_CMD, &dsk_dev, "DVHA unit=%d ", u); + break; + } + + sim_debug(DEBUG_CMD, &dsk_dev, "trk=%d\n\r", trk); + + /* Make sure head on valid cylinder */ + if ((trk / disk_type[base->u4].track) > disk_type[base->u4].cyl) { + disk_cmderr(up, PROG_INVADDR); + return 1; + } + + /* Make sure we have correct format for this cylinder in buffer */ + disk_rblock(up, trk); + /* Start actual operations */ + up->u5 |= DSKSTA_SCAN | DSKSTA_CMD; + up->u6 = 0; + chan_set(chan, DEV_SEL); + break; + + case DVSR: /* Prepare to Verify single record */ + /* Start actual operations */ + up->u5 |= DSKSTA_SCAN | DSKSTA_CMD; + up->u6 = 0; + chan_set(chan, DEV_SEL); + break; + } + sim_activate(up, us_to_ticks(50)); + return 0; +} + +/* Print a format pattern */ +void +print_format(UNIT * uptr) +{ + struct disk_t *dsk = &disk_type[uptr->u4]; + int i, j; + int u = uptr - dsk_unit; + int flag; + int lflag = -1; + + sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name); + + i = 0; + j = 0; + do { + flag = fbuffer[u][i / 4]; + flag >>= (i & 03) * 2; + flag &= 03; + if (lflag != flag) { + if (j != 0) { + switch(lflag) { + case FMT_DATA: + sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j); + break; + case FMT_HDR: + sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j); + break; + case FMT_HA2: + sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j); + break; + } + } + j = 1; + lflag = flag; + } else { + j++; + } + i++; + } while (flag != FMT_END); + + sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", i); +} + + +int +disk_rblock(UNIT * uptr, int trk) +{ + int u = uptr - dsk_unit; + struct disk_t *dsk = &disk_type[uptr->u4]; + UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; + FILE *f = base->fileref; + int offset = 0; + int fbase = 0; + + offset = dsk->cyl * dsk->track * dsk->bpt; + fbase = dsk->fmtsz; + offset *= u / (NUM_DEVS_DSK); + fbase *= u / (NUM_DEVS_DSK); + offset += dsk->fmtsz * dsk->mods * dsk->arms; + + if (uptr->u5 & DSKSTA_DIRTY) { + disk_wblock(uptr); + } + + if (arm_cyl[u] != fmt_cyl[u]) { + (void)sim_fseek(f, fbase + arm_cyl[u] * dsk->fbpt, SEEK_SET); + (void)sim_fread(fbuffer[u], 1, dsk->fbpt, f); + fmt_cyl[u] = arm_cyl[u]; + print_format(uptr); + } + /* Read in actualy track data */ + if (dtrack[u] != trk) { + sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Read track %d\n", u, + trk); + (void)sim_fseek(f, offset + trk * dsk->bpt, SEEK_SET); + if (sim_fread(dbuffer[u], 1, dsk->bpt, f) != dsk->bpt) + memset(dbuffer[u], 0, dsk->bpt); + dtrack[u] = trk; + } + return 1; +} + +int +disk_wblock(UNIT * uptr) +{ + int u = uptr - dsk_unit; + struct disk_t *dsk = &disk_type[uptr->u4]; + UNIT *base = &dsk_unit[(uptr->u3 >> 8) & 0xf]; + FILE *f = base->fileref; + int offset = 0; + + offset = dsk->cyl * dsk->track * dsk->bpt; + offset *= u / (NUM_DEVS_DSK); + offset += dsk->fmtsz * dsk->mods * dsk->arms; + + /* Check if new format data */ + if ((uptr->u5 & DSKSTA_CMSK) == DWRF) { + if (uptr->u5 & (DSKSTA_CHECK|DSKSTA_DIRTY)) { + switch (disk_format(uptr, f, arm_cyl[u], base)) { + case 2: + if (uptr->u5 & DSKSTA_CHECK) { + disk_posterr(uptr, PROG_FMTCHK|EXPT_DSKCHK); + break; + } + case 1: + disk_posterr(uptr, PROG_FMTCHK); + break; + case 0: + break; + } + uptr->u5 &= ~(DSKSTA_CHECK); + } + return 1; + } + + if (uptr->u5 & DSKSTA_CHECK) { + uptr->u5 &= ~(DSKSTA_CHECK); + if ((uptr->u5 & DSKSTA_DIRTY) == 0) + return 1; + } else if ((uptr->u5 & DSKSTA_DIRTY) == 0) + return 1; + + sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d Write track %d\n", + u, dtrack[u]); + /* Write in actualy track data */ + (void)sim_fseek(f, offset + dtrack[u] * dsk->bpt, SEEK_SET); + (void)sim_fwrite(dbuffer[u], 1, dsk->bpt, f); + uptr->u5 &= ~DSKSTA_DIRTY; + return 1; +} + +/* Convert a format pattern into a format track */ +int +disk_format(UNIT * uptr, FILE * f, int cyl, UNIT * base) +{ + uint8 tbuffer[MAXTRACK]; + struct disk_t *dsk = &disk_type[uptr->u4]; + int i, j; + int out = 0; + int u = uptr - dsk_unit; + uint8 ch; + int offset; + + f = base->fileref; + + offset = dsk->fmtsz; + offset *= u / (NUM_DEVS_DSK); + + uptr->u5 &= ~(DSKSTA_DIRTY); + sim_debug(DEBUG_DETAIL, &dsk_dev, "unit=%d (%s) format: ", u, dsk->name); + /* Scan over new format */ + /* Convert format specification in place in dbuffer, then + * pack it into fbuffer and write to file */ + + /* Skip initial gap */ + for (i = 0; i < MAXTRACK && dbuffer[u][i] == 04; i++) ; + if (i == MAXTRACK) + return 2; /* Failed if we hit end */ + /* HA1 Gap */ + for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ; + if ((i - j) > 12) + return 1; /* HA1 too big */ + + if (dbuffer[u][i++] != 04) + return 2; /* Not gap */ + for (j = i; i < MAXTRACK && dbuffer[u][i] == 03; i++) ; + if (i == MAXTRACK) + return 2; /* Failed if we hit end */ + + if (dbuffer[u][i++] != 04) + return 2; /* Not gap */ + + /* Size up HA2 gap */ + for (j = i; + i < MAXTRACK && (dbuffer[u][i] == 03 || dbuffer[u][i] == 01); + i++) ; + j = i - j; + if (j < 6) + return 2; + + j -= dsk->overhd; /* Remove overhead */ + sim_debug(DEBUG_DETAIL, &dsk_dev, "HA2(%d) ", j); + for (; j > 0; j--) + tbuffer[out++] = FMT_HA2; + /* Now grab records */ + while (i < MAXTRACK) { + ch = dbuffer[u][i++]; + if (ch == 0x40) + break; /* End of record */ + if (ch != 04 && ch != 02) + return 2; /* Not a gap */ + + for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; + ch = dbuffer[u][i]; /* Should be RA */ + /* Gap not long enough or eor */ + if (ch == 0x40 || (i - j) < 11) + break; + + /* Size up RA gap */ + if (ch != 01 && ch != 03) + return 1; /* Not header */ + for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; + j = i - j; + if (j < 10) + return 2; + j -= dsk->overhd; /* Remove overhead */ + sim_debug(DEBUG_DETAIL, &dsk_dev, "RA(%d) ", j); + for (; j > 0; j--) + tbuffer[out++] = FMT_HDR; + ch = dbuffer[u][i++]; + if (ch == 0x40) + break; /* End of record */ + if (ch != 04 && ch != 02) + return 1; /* End of RA Field */ + ch = dbuffer[u][i]; + if (ch != 01 && ch != 03) + return 1; /* Not gap */ + for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; + if ((i - j) < 10) + return 2; /* Gap not large enough */ + ch = dbuffer[u][i++]; + if (ch != 04 && ch != 02) + return 2; /* End of RA Gap */ + ch = dbuffer[u][i]; /* Should be DA */ + if (ch == 0x40) + break; /* End of record */ + if (ch != 01 && ch != 03) + return 1; /* Not Record data */ + for (j = i; i < MAXTRACK && dbuffer[u][i] == ch; i++) ; + j = i - j; + if (j < 10) + return 2; + j -= dsk->overhd; /* Remove overhead */ + sim_debug(DEBUG_DETAIL, &dsk_dev, "DA(%d) ", j); + for (; j > 0; j--) + tbuffer[out++] = FMT_DATA; + } + sim_debug(DEBUG_DETAIL, &dsk_dev, "total=%d\n", out); + /* If checking, don't update */ + if (uptr->u5 & DSKSTA_CHECK) + return 0; + + /* Put four END chars to end of buffer */ + for (j = 4; j > 0; j--) + tbuffer[out++] = FMT_END; + + /* Now grab every four characters and place them in next format location */ + for (j = i = 0; j < out; i++) { + uint8 temp; + + temp = (tbuffer[j++] & 03); + temp |= (tbuffer[j++] & 03) << 2; + temp |= (tbuffer[j++] & 03) << 4; + temp |= (tbuffer[j++] & 03) << 6; + fbuffer[u][i] = temp; + if (i > dsk->fbpt) + break; + } + fbuffer[u][dsk->fbpt-1] = (FMT_END<<6)|(FMT_END<<4)|(FMT_END<<2)|FMT_END; + + /* Now write the buffer to the file */ + (void)sim_fseek(f, offset + cyl * dsk->fbpt, SEEK_SET); + (void)sim_fwrite(fbuffer[u], 1, dsk->fbpt, f); + + /* Make sure we did not pass size of track */ + if (out > (int)dsk->bpt) + return 1; /* Too big for track */ + return 0; +} + +/* Handle writing of one character to disk */ +int +disk_write(UNIT * uptr, uint8 data, int chan, int eor) +{ + int u = uptr - dsk_unit; + UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; + int skip = 1; + int flag = -1; + uint8 cmd; + int schan; + + schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0); + cmd = (uptr->u5) & DSKSTA_CMSK; + if (uptr->u6 == 0 && cmd != DWRF) { + int cyl = dtrack[u] / disk_type[base->u4].track; + + if (arm_cyl[u] != cyl) { + sim_debug(DEBUG_CMD, &dsk_dev, "cyl not equal %d %d %d\n\r", + u, arm_cyl[u], cyl); + disk_posterr(uptr, PROG_INVADDR); + return -1; + } + + /* Verify that the home address matches */ + if (cmd != DVHA && cmd != DVSR) { + uint16 t = cmd_option[chan] & 01717; + uint16 ha; + ha = (077 & dbuffer[u][0]) << 6; + ha |= 077 & dbuffer[u][1]; + /* Mask out bits we ignore */ + ha &= 01717; + /* Convert BCD 0 to Binary 0 */ + if ((ha & 01700) == 01200) + ha &= 077; + if ((ha & 017) == 012) + ha &= 07700; + if ((t & 01700) == 01200) + t &= 077; + if ((t & 017) == 012) + t &= 07700; + + sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha); + if (ha != t) { + disk_posterr(uptr, PROG_NOREC); + return -1; + } + } else { + sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n"); + } + } + + while (skip) { + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + switch (uptr->u5 & DSKSTA_CMSK) { + case DWRF: /* Format */ + if ((uint32)uptr->u6 > disk_type[uptr->u4].bpt) { + return 1; + } + if (uptr->u5 & DSKSTA_CHECK) { + if ((dbuffer[u][uptr->u6++] & 077) != (data & 077)) { + disk_posterr(uptr, DATA_CHECK); + return -1; + } + } else { + dbuffer[u][uptr->u6++] = data; + uptr->u5 |= DSKSTA_DIRTY; + } + return 0; + case DVTN: /* Verify track no addr */ + if (flag == FMT_END) { + return 1; + } + if (flag == FMT_DATA) + skip = 0; + else + uptr->u6++; + break; + case DVTA: /* Verify track addr */ + if (flag == FMT_END) { + return 1; + } + if (flag != FMT_HA2) + skip = 0; + else + uptr->u6++; + break; + case DVHA: /* Verify home addr */ + if (flag == FMT_END) { + return 1; + } + skip = 0; + break; + case DVCY: /* Verify Cyl */ + if (flag == FMT_END) { + /* Move to next track */ + int trk = dtrack[u]; + int cyl = trk / disk_type[uptr->u4].track; + + uptr->u6 = 0; + if (eor) + return 1; + + if ((++trk) / disk_type[uptr->u4].track != cyl) + return 1; + disk_rblock(uptr, trk); + } else if (flag != FMT_DATA) + uptr->u6++; + else + skip = 0; + break; + case DVSR: /* Verify single record */ + if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) { + skip = 0; + } else if (uptr->u5 & DSKSTA_XFER) { + /* Not in a Data record, and we were transfering, all done */ + uptr->u5 &= ~DSKSTA_XFER; + uptr->u6 = 0; + return 1; + } else if (flag == FMT_END) { + /* If we hit the end, then no record found */ + disk_posterr(uptr, PROG_NOREC); + return -1; + } else if (flag == FMT_HDR) { + uint8 ch; + uint32 match = 0; + int i; + + for (i = 0; i < 4 && flag == FMT_HDR; i++) { + ch = dbuffer[u][uptr->u6++]; + match <<= 4; + if (ch != 012) + match |= ch & 0xf; + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + } + /* Short header, skip it, but should not have been made */ + if (flag != FMT_HDR) + break; + match <<= 16; + /* Grab last two Characters */ + while (flag == FMT_HDR) { + ch = dbuffer[u][uptr->u6++]; + match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) | + (ch & 077); + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + } + if (flag != FMT_DATA) + break; + /* Compare values. */ + if (match != cmd_option[chan]) + break; + /* Got a match, exit loop */ + uptr->u5 &= ~DSKSTA_SCAN; + uptr->u5 |= DSKSTA_XFER; + skip = 0; + } else { + uptr->u6++; + } + break; + } + } + data &= (sense[schan] & STAT_SIXBIT)?077:0277; + if (uptr->u5 & DSKSTA_CHECK) { + if (dbuffer[u][uptr->u6++] != data) { + fprintf(stderr, "Mismatch %d %03o != %03o\n\r", + uptr->u6-1, dbuffer[u][uptr->u6-1], data); + disk_posterr(uptr, DATA_CHECK); + } + } else { + dbuffer[u][uptr->u6++] = data; + uptr->u5 |= DSKSTA_DIRTY; + } + return 0; +} + +/* Handle reading of one character to disk */ +int +disk_read(UNIT * uptr, uint8 * data, int chan) +{ + int u = uptr - dsk_unit; + UNIT *base = (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; + int skip = 1; + int flag; + uint8 cmd; + int schan; + + schan = (chan * 2) + ((base->flags & UNIT_SELECT) ? 1 : 0); + cmd = (uptr->u5) & DSKSTA_CMSK; + if (uptr->u6 == 0) { + int cyl = dtrack[u] / disk_type[base->u4].track; + if (arm_cyl[u] != cyl) { + disk_posterr(uptr, PROG_INVADDR); + return -1; + } + + /* Verify that the home address matches */ + if (cmd != DVHA && cmd != DVSR) { + uint16 t = cmd_option[chan] & 01717; + uint16 ha; + ha = (077 & dbuffer[u][0]) << 6; + ha |= 077 & dbuffer[u][1]; + /* Mask out bits we ignore */ + ha &= 01717; + /* Convert BCD 0 to Binary 0 */ + if ((ha & 07700) == 01200) + ha &= 077; + if ((ha & 077) == 012) + ha &= 07700; + if ((t & 07700) == 01200) + t &= 077; + if ((t & 077) == 012) + t &= 07700; + + sim_debug(DEBUG_CMD, &dsk_dev, "HA %04o(c) %04o(d)\n", t, ha); + if (ha != t) { + disk_posterr(uptr, PROG_NOREC); + return -1; + } + } else { + sim_debug(DEBUG_CMD, &dsk_dev, "HA ignored\n"); + } + } + + while (skip) { + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + switch (cmd) { + case DWRF: /* Format */ + disk_posterr(uptr, PROG_FMTCHK); + return 1; + case DVTN: /* Verify track no addr */ + if (flag == FMT_END) { + uptr->u6 = 0; + return 1; + } + if (flag == FMT_DATA) + skip = 0; + else + uptr->u6++; + break; + case DVTA: /* Verify track addr */ + if (flag == FMT_END) { + uptr->u6 = 0; + return 1; + } + if (flag != FMT_HA2) + skip = 0; + else + uptr->u6++; + break; + case DVHA: /* Prepare to Verify home addr */ + if (flag == FMT_END) { + uptr->u6 = 0; + return 1; + } + skip = 0; + break; + case DVCY: /* Verify Cyl */ + if (flag == FMT_END) { + /* Move to next track */ + int trk = dtrack[u]; + UNIT *base = + (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; + int cyl = trk / disk_type[base->u4].track; + + uptr->u6 = 0; + if ((++trk) / disk_type[base->u4].track != cyl) + return 1; + disk_rblock(uptr, trk); + } else if (flag != FMT_DATA) + uptr->u6++; + else + skip = 0; + break; + case DVSR: /* Verify single record */ + if (flag == FMT_DATA && uptr->u5 & DSKSTA_XFER) { + skip = 0; + } else if (uptr->u5 & DSKSTA_XFER) { + /* Not in a Data record, and we were transfering, all done */ + uptr->u5 &= ~DSKSTA_XFER; + uptr->u6 = 0; + return 1; + } else if (flag == FMT_END) { + /* If we hit the end, then no record found */ + disk_posterr(uptr, PROG_NOREC); + return -1; + } else if (flag == FMT_HDR) { + uint8 ch; + uint32 match = 0; + int i; + + for (i = 0; i < 4 && flag == FMT_HDR; i++) { + ch = dbuffer[u][uptr->u6++]; + match <<= 4; + if (ch != 012) + match |= ch & 0xf; + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + } + /* Short header, skip it, but should not have been made */ + if (flag != FMT_HDR) + break; + match <<= 16; + /* Grab last two Characters */ + while (flag == FMT_HDR) { + ch = dbuffer[u][uptr->u6++]; + match = (match & 0xFFFF0000) | ((match & 0x3f) << 6) | + (ch & 077); + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + } + if (flag != FMT_DATA) + break; + /* Compare values. */ + if (match != cmd_option[chan]) + break; + /* Got a match, exit loop */ + uptr->u5 &= ~DSKSTA_SCAN; + uptr->u5 |= DSKSTA_XFER; + skip = 0; + } else { + uptr->u6++; + } + break; + } + } + *data = dbuffer[u][uptr->u6++] & ((sense[schan] & STAT_SIXBIT)?077:0277); + /* Check if character is last in record */ + flag = fbuffer[u][uptr->u6 / 4]; + flag >>= (uptr->u6 & 03) * 2; + flag &= 03; + switch (cmd) { + case DVTN: /* Verify track no addr */ + case DVTA: /* Verify track addr */ + case DVHA: /* Prepare to Verify home addr */ + if (flag == FMT_END) { + sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); + return 1; + } + break; + case DVCY: /* Verify Cyl */ + if (flag == FMT_END) { + /* Move to next track */ + UNIT *base = + (u > NUM_DEVS_DSK) ? &uptr[-NUM_DEVS_DSK] : uptr; + + if (((dtrack[u]+1) / disk_type[base->u4].track) != + (dtrack[u] / disk_type[base->u4].track)) { + sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); + return 1; + } + } + break; + case DVSR: /* Verify single record */ + if (flag != FMT_DATA) { + sim_debug(DEBUG_DATA, &dsk_dev, "eor\n"); + return 1; + } + } + return 0; +} + +/* Convert BCD track address to binary address */ +int +bcd_to_track(uint32 addr) +{ + int trk = 0; + int i; + + for (i = 28; i >= 16; i -= 4) { + trk = (trk * 10) + ((addr >> i) & 0xf); + } + return trk; +} + +/* Boot disk. Build boot card loader in memory and transfer to it */ +t_stat +dsk_boot(int unit_num, DEVICE * dptr) +{ +#ifdef I7090 + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags) - 1; + int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + int dev = uptr->u3 & 0xff; + int msk = (chan / 2) | ((chan & 1) << 11); + extern uint16 IC; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + if (dev == 0) + dev = 012; + if (uptr->flags & CTSS_BOOT) { + /* Build CTSS boot program in memory */ + /* Read first cylinder into B-Core */ + M[0] = 0377777000100LL; /* IORT BOTTOM,,-1 */ + M[1] = 0006000000001LL; /* TCOA * */ + M[2] = 0007400400100LL; /* START TSX ENTER,4 */ + M[0100] = 0076000000350LL; /* ENTER RICU */ + M[0100] |= (chan + 1) << 9; + M[0101] = 0054000000120LL; /* RSCU READ */ + M[0101] |= ((t_uint64) (msk)) << 24; + M[0102] = 0006000000102LL; /* TCOU * */ + M[0102] |= ((t_uint64) (chan)) << 24; + M[0103] = 0476100000042LL; /* SEB */ + M[0104] = 0450000000000LL; /* CAL 0 */ + M[0105] = 0036100477777LL; /* ACL 32767,4 */ + M[0106] = 0200001400105LL; /* TIX *-1,4,1 */ + M[0107] = 0476100000041LL; /* SEA */ + M[0110] = 0032200000131LL; /* ERA CHKSUM */ + M[0111] = 0450100000046LL; /* ORA ULOC */ + M[0112] = 0010000000132LL; /* TZE EXIT */ + M[0113] = 0000000000002LL; /* HTR START */ + M[0114] = 0101212001212LL; + M[0114] |= ((t_uint64) (dev)) << 12; + M[0115] = 0121212121212LL; + M[0116] = 0100512001212LL; + M[0116] |= ((t_uint64) (dev)) << 12; + M[0117] = 0121267671212LL; + M[0120] = 0700000000004LL; /* READ SMS 4 */ + M[0120] |= sel; + M[0121] = 0200000000114LL; /* CTL SEEK */ + M[0122] = 0500000200122LL; /* TCM *,,0 */ + M[0123] = 0200000200116LL; /* CTLR CYLOP */ + M[0124] = 0400007000125LL; /* CPYP *+1,,N */ + IC = 02; + } else { + /* Build IBSYS Boot program in memory */ + M[0] = 0000025000101LL; /* IOCD RSCQ,,21 */ + M[1] = 0006000000001LL; /* TCOA * */ + M[2] = 0002000000101LL; /* TRA RSCQ */ + + M[0101] = 0054000000115LL; /* RSCQ RSCC SMSQ Mod */ + M[0101] |= ((t_uint64) (msk)) << 24; + M[0102] = 0064400000000LL; /* SCDQ SCDC 0 Mod */ + M[0102] |= ((t_uint64) (msk)) << 24; + M[0103] = 0044100000000LL; /* LDI 0 */ + M[0104] = 0405400007100LL; /* LFT 7100 */ + M[0105] = 0002000000110LL; /* TRA *+3 */ + M[0106] = 0006000000102LL; /* TCOQ TCOC SCDQ Mod */ + M[0106] |= ((t_uint64) (chan)) << 24; + M[0107] = 0002000000003LL; /* TRA 3 Enter IBSYS */ + M[0110] = 0076000000350LL; /* RICQ RICC ** Mod */ + M[0110] |= (chan + 1) << 9; + M[0111] = 0500512001212LL; /*LDVCY DVCY Mod */ + M[0111] |= ((t_uint64) (dev)) << 12; + M[0112] = 0121222440000LL; /* * */ + M[0113] = 0501212001212LL; /*LDSEK DSEEK Mod */ + M[0113] |= ((t_uint64) (dev)) << 12; + M[0114] = 0121200000000LL; /* * */ + M[0115] = 0700000000016LL; /* SMSQ SMS 14 */ + M[0115] |= sel; + M[0116] = 0200000000113LL; /* CTL LDSEK */ + M[0117] = 0500000200117LL; /* TCM *,,, */ + M[0120] = 0200000200111LL; /* CTLR LDVCY */ + M[0121] = 0400001000122LL; /* CPYP *+1,,1 */ + M[0122] = 0000000000122LL; /* WTR * */ + M[0123] = 0100000000121LL; /* TCH *-2 */ + M[0124] = 0500000000000LL; /* CPYD 0,,0 */ + M[0125] = 0340000000125LL; /* TWT * */ + IC = 0101; + } + + return SCPE_OK; +#else + return SCPE_NOFNC; +#endif +} + +void +dsk_ini(UNIT * uptr, t_bool f) +{ + uptr->u5 = 0; +} + +t_stat +dsk_reset(DEVICE * dptr) +{ + int i; + int t; + + for (i = 0; i < NUM_CHAN; i++) { + sense[i * 2] = sense[i * 2 + 1] = STAT_SIXBIT; + sense_unit[i * 2] = sense_unit[i * 2 + 1] = 0; + } + for (i = 0; i < NUM_DEVS_DSK; i++) { + dtrack[i] = 077777; + fmt_cyl[i] = 077777; + arm_cyl[i] = 0; + dtrack[i + NUM_DEVS_DSK] = 077777; + fmt_cyl[i + NUM_DEVS_DSK] = 077777; + arm_cyl[i + NUM_DEVS_DSK] = 0; + dtrack[i + (NUM_DEVS_DSK * 2)] = 077777; + fmt_cyl[i + (NUM_DEVS_DSK * 2)] = 077777; + arm_cyl[i + (NUM_DEVS_DSK * 2)] = 0; + dtrack[i + (NUM_DEVS_DSK * 3)] = 077777; + fmt_cyl[i + (NUM_DEVS_DSK * 3)] = 077777; + arm_cyl[i + (NUM_DEVS_DSK * 3)] = 0; + t = dptr->units[i].u4; + /* Fill in max capacity */ + dptr->units[i + NUM_DEVS_DSK].u3 = 0xff; + dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = 0xff; + dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = 0xff; + dptr->units[i].capac = disk_type[t].mods * disk_type[t].bpt * + disk_type[t].arms * disk_type[t].track * disk_type[t].cyl; + if (disk_type[t].arms > 1) + dptr->units[i + NUM_DEVS_DSK].u3 = + 0x10 | dptr->units[i].u3 | (i << 8); + if (disk_type[t].mods > 1) { + dptr->units[i + (NUM_DEVS_DSK * 2)].u3 = (i<<8) | (dptr->units[i].u3 + 1); + if (disk_type[t].arms > 1) + dptr->units[i + (NUM_DEVS_DSK * 3)].u3 = (i<<8) | 0x10 | + (dptr->units[i].u3 + 1); + } + } + return SCPE_OK; +} + +/* Disk option setting commands */ + +t_stat +dsk_set_type(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int i, u; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + for (i = 0; disk_type[i].name != 0; i++) { + if (strcmp(disk_type[i].name, cptr) == 0) { + uptr->u4 = i; + uptr[NUM_DEVS_DSK].u4 = i; + uptr[NUM_DEVS_DSK].u3 = 0xff; + uptr[NUM_DEVS_DSK * 2].u4 = i; + uptr[NUM_DEVS_DSK * 2].u3 = 0xff; + uptr[NUM_DEVS_DSK * 3].u4 = i; + uptr[NUM_DEVS_DSK * 3].u3 = 0xff; + uptr->capac = disk_type[i].mods * disk_type[i].bpt * + disk_type[i].arms * disk_type[i].track * disk_type[i].cyl; + /* Adjust other disks in case changed number arms/modules */ + u = uptr->u3 & 0xf0f; + if (disk_type[uptr->u4].arms > 1) + uptr[NUM_DEVS_DSK].u3 = u | 0x10; + if (disk_type[uptr->u4].mods > 1) { + uptr[NUM_DEVS_DSK * 2].u3 = u + 1; + if (disk_type[uptr->u4].arms > 1) + uptr[NUM_DEVS_DSK * 3].u3 = (u + 1) | 0x10; + } + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat +dsk_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs(disk_type[uptr->u4].name, st); + return SCPE_OK; +} + +t_stat +dsk_set_module(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int u; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + if (*cptr == '\0' || cptr[1] != '\0') + return SCPE_ARG; + if (*cptr < '0' || *cptr > '8') + return SCPE_ARG; + if (*cptr & 1) + return SCPE_ARG; + u = uptr->u3 & 0xf00; + uptr->u3 = u | (*cptr - '0'); + uptr[NUM_DEVS_DSK].u3 = 0xff; + uptr[NUM_DEVS_DSK * 2].u3 = 0xff; + uptr[NUM_DEVS_DSK * 3].u3 = 0xff; + if (disk_type[uptr->u4].arms > 1) + uptr[NUM_DEVS_DSK].u3 = u | 0x10 | (*cptr - '0'); + if (disk_type[uptr->u4].mods > 1) { + uptr[NUM_DEVS_DSK * 2].u3 = u | ((*cptr - '0') + 1); + if (disk_type[uptr->u4].arms > 1) + uptr[NUM_DEVS_DSK * 3].u3 = u | 0x10 | ((*cptr - '0') + 1); + } + return SCPE_OK; +} + +t_stat +dsk_get_module(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "Module=%d", uptr->u3 & 0xff); + return SCPE_OK; +} + +t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr) +{ + int i; +fprintf (st, "IBM 7631 Disk File Controller\n\n"); +fprintf (st, "The IBM 7631 Disk File Controller supports several types of "); +fprintf (st, "disk drives and\ndrums. The drive must be formatted for use "); +fprintf (st, "of the system. This is handled by\nutilities provided by the "); +fprintf (st, "operating system. This will write a special format\ntrack.\n\n"); +fprintf (st, "Use:\n\n"); +fprintf (st, " sim> SET DKn TYPE=type\n"); +fprintf (st, "Type can be: "); +for (i = 0; disk_type[i].name != 0; i++) { + fprintf(st, "%s", disk_type[i].name); + if (disk_type[i+1].name != 0) + fprintf(st, ", "); +} +fprintf (st, ".\nEach drive has the following storage capacity:\n\n"); +for (i = 0; disk_type[i].name != 0; i++) { + int32 size = disk_type[i].mods * disk_type[i].bpt * + disk_type[i].arms * disk_type[i].track * disk_type[i].cyl; + char sm = 'K'; + size /= 1024; + if (size > 5000) { + size /= 1024; + sm = 'M'; + } + fprintf(st, " %-8s %4d%cB %d modules\n", disk_type[i].name, size, sm, + disk_type[i].mods); +} +fprintf (st, "\nTo enable formating the format switch must be set "); +fprintf (st, "to enable, and the Home\nAddress 2 write must be enabled.\n"); +fprintf (st, "To do this:\n\n"); +fprintf (st, " sim> SET DKn FORMAT HA2\n\n"); +fprintf (st, "To prevent accidental formating of the drive use:\n\n"); +fprintf (st, " sim> SET DKn NOFORMAT NOHA2\n\n"); +help_set_chan_type(st, dptr, "IBM 7631 Disk File"); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +return SCPE_OK; +} + +const char *dsk_description (DEVICE *dptr) +{ +return "IBM 7631 disk file controller"; +} + +#endif diff --git a/I7000/i7000_ht.c b/I7000/i7000_ht.c new file mode 100644 index 00000000..69743ed0 --- /dev/null +++ b/I7000/i7000_ht.c @@ -0,0 +1,992 @@ +/* i7090_ht.c: ibm 7090 hypertape + + 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. + + Support for 7640 hypertape + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. + + Hypertape orders appear to be of the following formats. Since there + is no documentation on them, I can going with what is shown in the + IBSYS sources. + + BCD translated: + CTLW/CTLR 06u01 where u is unit numder + CTLW/CTLR 07u01 Backwords reading, where u is unit numder + CTL 06uoo01 Where u is unit number, and oo is order code + 3x or 42 +*/ + +#include "i7000_defs.h" +#include "sim_tape.h" + +#ifdef NUM_DEVS_HT +#define BUFFSIZE (MAXMEMSIZE * CHARSPERWORD) + +#define UNIT_HT(x) UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE|UNIT_S_CHAN(x)| \ + UNIT_SELECT + +#if defined(HTSIZE) +#undef HTSIZE +#endif +#define HTSIZE 31731000 + +/* in u3 is device address */ +/* in u4 is current buffer position */ +/* in u5 */ +#define HT_CMDMSK 00000077 /* Command being run */ +#define HT_NOTRDY 00000100 /* Devices is running a command */ +#define HT_IDLE 00000200 /* Tape still in motion */ +#define HT_MARK 00000400 /* Hit tape mark */ +#define HT_EOR 00001000 /* Hit end of record */ +#define HT_ERR 00002000 /* Device recieved error */ +#define HT_BOT 00004000 /* Unit at begining of tape */ +#define HT_EOT 00010000 /* Unit at end of tape */ +#define HT_ATTN 00020000 /* Unit requests attntion */ +#define HT_MOVE 00040000 /* Unit is moving to new record */ +#define HT_WRITE 00100000 /* Were we writing */ +#define HT_SNS 00200000 /* We were doing sense */ +#define HT_CMD 00400000 /* We are fetching a command */ +#define HT_PEND 01000000 /* Hold channel while command runs */ + +/* Hypertape commands */ +#define HNOP 0x00 /* Nop */ +#define HEOS 0x01 /* End of sequence */ +#define HRLF 0x02 /* Reserved Light Off */ +#define HRLN 0x03 /* Reserved Light On */ +#define HCLF 0x04 /* Check light off? Not documented by might + be valid command */ +#define HCLN 0x05 /* Check light on */ +#define HSEL 0x06 /* Select */ +#define HSBR 0x07 /* Select for backwards reading */ +#define HCCR 0x28 /* Change cartridge and rewind */ +#define HRWD 0x30 /* Rewind */ +#define HRUN 0x31 /* Rewind and unload */ +#define HERG 0x32 /* Erase long gap */ +#define HWTM 0x33 /* Write tape mark */ +#define HBSR 0x34 /* Backspace */ +#define HBSF 0x35 /* Backspace file */ +#define HSKR 0x36 /* Space */ +#define HSKF 0x37 /* Space file */ +#define HCHC 0x38 /* Change Cartridge */ +#define HUNL 0x39 /* Unload Cartridge */ +#define HFPN 0x42 /* File Protect On */ + +/* Hypertape sense word 1 codes */ + /* 01234567 */ +#define SEL_MASK 0x0F000000 /* Selected unit mask */ +#define STAT_NOTRDY 0x80800000 /* Drive not ready */ +#define PROG_NOTLOAD 0x40400000 /* *Drive not loaded */ +#define PROG_FILEPROT 0x40200000 /* Drive write protected */ +#define PROG_INVCODE 0x40080000 /* Invalid code */ +#define PROG_BUSY 0x40040000 /* Drive Busy */ +#define PROG_BOT 0x40020000 /* Drive at BOT BSR/BSF requested */ +#define PROG_EOT 0x40010000 /* Drive at EOT forward motion requested. */ +#define DATA_CHECK 0x20008000 /* *Error corrected */ +#define DATA_PARITY 0x20004000 /* *Parity error */ +#define DATA_CODECHK 0x20002000 /* *Code check */ +#define DATA_ENVCHK 0x20001000 /* *Envelop error */ +#define DATA_RESPONSE 0x20000800 /* Response check */ +#define DATA_EXECSKEW 0x20000400 /* *Excessive skew check */ +#define DATA_TRACKSKEW 0x20000200 /* *Track skew check */ +#define EXP_MARK 0x10000080 /* Drive read a mark */ +#define EXP_EWA 0x10000040 /* *Drive near EOT */ +#define EXP_NODATA 0x10000020 /* *No data transfered */ +#define READ_BSY 0x00000008 /* *Controller reading */ +#define WRITE_BSY 0x00000004 /* *Controller writing */ +#define BACK_MODE 0x00000002 /* *Backwards mode */ + +uint32 ht_cmd(UNIT *, uint16, uint16); +t_stat ht_srv(UNIT *); +t_stat htc_srv(UNIT *); +void ht_tape_cmd(DEVICE *, UNIT *); +t_stat ht_error(UNIT *, int, t_stat); +t_stat ht_boot(int32, DEVICE *); +t_stat ht_reset(DEVICE *); +t_stat ht_attach(UNIT *, CONST char *); +t_stat ht_detach(UNIT *); +t_stat ht_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *ht_description (DEVICE *dptr); +void ht_tape_posterr(UNIT * uptr, uint32 error); + + + +/* One buffer per channel */ +uint8 ht_unit[NUM_CHAN * 2]; /* Currently selected unit */ +uint8 ht_buffer[NUM_DEVS_HT+1][BUFFSIZE]; +int ht_cmdbuffer[NUM_CHAN]; /* Buffer holding command ids */ +int ht_cmdcount[NUM_CHAN]; /* Count of command digits recieved */ +uint32 ht_sense[NUM_CHAN * 2]; /* Sense data for unit */ + +UNIT hta_unit[] = { +/* Controller 1 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 0 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 1 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 2 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 3 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 4 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 5 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 6 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 7 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 8 */ + {UDATA(&ht_srv, UNIT_HT(5), HTSIZE)}, /* 9 */ + {UDATA(&htc_srv, UNIT_S_CHAN(5)|UNIT_DISABLE|UNIT_DIS, 0)}, /* Controller */ +#if NUM_DEVS_HT > 1 +/* Controller 2 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 0 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 1 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 2 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 3 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 4 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 5 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 6 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 7 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 8 */ + {UDATA(&ht_srv, UNIT_HT(8), HTSIZE)}, /* 9 */ + {UDATA(&htc_srv, UNIT_S_CHAN(8)|UNIT_DISABLE|UNIT_DIS, 0)}, /* Controller */ +#endif +}; + +MTAB ht_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL, + "Write ring in place"}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, NULL, + "no Write ring in place"}, + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, + "Set/Display tape format (SIMH, E11, TPC, P7B)" }, + {MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH", + NULL, &sim_tape_show_capac, NULL, + "Set unit n capacity to arg MB (0 = unlimited)" }, + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan, + NULL, "Set Channel for device"}, +#ifndef I7010 /* Not sure 7010 ever supported hypertapes */ + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "SELECT", "SELECT", + &chan9_set_select, &chan9_get_select, NULL, + "Set unit number"}, +#endif + {0} +}; + +DEVICE hta_dev = { + "HTA", hta_unit, NULL, ht_mod, + NUM_UNITS_HT + 1, 8, 15, 1, 8, 8, + NULL, NULL, &ht_reset, &ht_boot, &ht_attach, &ht_detach, + &ht_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &ht_help, NULL, NULL, &ht_description +}; + +#if NUM_DEVS_HT > 1 +DEVICE htb_dev = { + "HTB", &hta_unit[NUM_UNITS_HT + 1], NULL, ht_mod, + NUM_UNITS_HT + 1, 8, 15, 1, 8, 8, + NULL, NULL, &ht_reset, &ht_boot, &ht_attach, &ht_detach, + &ht_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &ht_help, NULL, NULL, &ht_description +}; +#endif + + +uint32 ht_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + int chan = UNIT_G_CHAN(dptr->units[0].flags); + UNIT *u = &dptr->units[NUM_UNITS_HT]; + + /* Activate the device to start doing something */ + ht_cmdbuffer[chan] = 0; + ht_cmdcount[chan] = 0; + sim_activate(u, 10); + return SCPE_OK; +} + +t_stat htc_srv(UNIT * uptr) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + int chan = UNIT_G_CHAN(dptr->units[0].flags); + int sel; + int schan; + + sel = (dptr->units[0].flags & UNIT_SELECT) ? 1 : 0; + if (sel != chan_test(chan, CTL_SEL)) + return SCPE_OK; + schan = (chan * 2) + sel; + /* Drive is busy */ + if (uptr->u5 & HT_NOTRDY) { + sim_debug(DEBUG_EXP, dptr, "Controller busy\n"); + return SCPE_OK; + } + + /* Handle sense on unit */ + if (chan_test(chan, CTL_SNS)) { + uint8 ch = 0; + int eor = 0; + int i; + UNIT *up; + switch(ht_cmdcount[chan]) { + case 0: ch = (ht_sense[schan] >> 24) & 0xF; + uptr->u5 |= HT_SNS; + chan9_clear_error(chan, sel); + sim_debug(DEBUG_SNS, dptr, "Sense %08x\n", ht_sense[schan]); + break; + case 1: ch = ht_unit[schan]; + break; + case 2: case 3: case 4: case 5: case 6: case 7: + ch = (ht_sense[schan] >> (4 * (7 - ht_cmdcount[chan]))) & 0xF; + break; + case 10: + eor = DEV_REOR; + /* Fall through */ + case 9: + case 8: + i = 4 * (ht_cmdcount[chan] - 8); + up = &dptr->units[i]; + ch = 0; + for (i = 3; i >= 0; i--, up++) { + if (up->u5 & HT_ATTN) + ch |= 1 << i; + } + break; + } + + /* Fix out of align bit */ + if (ch & 010) + ch ^= 030; + + sim_debug(DEBUG_DATA, dptr, "sense %d %02o ", ht_cmdcount[chan], ch); + ht_cmdcount[chan]++; + switch(chan_write_char(chan, &ch, eor)) { + case TIME_ERROR: + case END_RECORD: + ht_sense[schan] = 0; + /* Fall through */ + case DATA_OK: + uptr->u5 |= HT_SNS; /* So we catch disconnect */ + if (eor) { + ht_sense[schan] = 0; + for (up = dptr->units, i = NUM_UNITS_HT; i >= 0; i--, up++) + up->u5 &= ~HT_ATTN; + } + break; + } + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + + /* If control, go collect it */ + if (chan_test(chan, CTL_CNTL)) { + uptr->u5 |= HT_CMD; + ht_tape_cmd(dptr, uptr); + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + + /* Channel has disconnected, abort current operation. */ + if (uptr->u5 & (HT_SNS|HT_CMD) && chan_stat(chan, DEV_DISCO)) { + uptr->u5 &= ~(HT_SNS|HT_CMD); + chan_clear(chan, DEV_WEOR|DEV_SEL); + sim_debug(DEBUG_CHAN, dptr, "control disconnecting\n"); + } + return SCPE_OK; +} + +t_stat ht_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + UNIT *ctlr = &dptr->units[NUM_UNITS_HT]; + int sel; + int schan; + t_stat r; + t_mtrlnt reclen; + + sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + + /* Handle seeking */ + if (uptr->wait > 0) { + uptr->wait--; + if (uptr->wait == 0) { + if (uptr->u5 & HT_PEND) { + chan_set(chan, DEV_REOR|CTL_END); + ctlr->u5 &= ~(HT_NOTRDY); + sim_activate(ctlr, us_to_ticks(50)); /* Schedule control to disco */ + } else { + uptr->u5 |= HT_ATTN; + chan9_set_attn(chan, sel); + } + uptr->u5 &= ~(HT_PEND | HT_NOTRDY | HT_CMDMSK); + sim_debug(DEBUG_DETAIL, dptr, "%d Seek done\n", unit); + } else + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; + } + + if (sel != chan_test(chan, CTL_SEL)) + return SCPE_OK; + + /* Channel has disconnected, abort current operation. */ + if ((uptr->u5 & HT_CMDMSK) == HSEL && chan_stat(chan, DEV_DISCO)) { + if (uptr->u5 & HT_WRITE) { + sim_debug(DEBUG_CMD, dptr, + "Write flush Block %d chars %d words\n", uptr->u6, + uptr->u6 / 6); + r = sim_tape_wrrecf(uptr, &ht_buffer[GET_DEV_BUF(dptr->flags)][0], + uptr->u6); + uptr->u5 &= ~HT_WRITE; + if (r != MTSE_OK) { + ht_error(uptr, schan, r); + chan9_set_attn(chan, sel); + } + uptr->u6 = 0; + } + uptr->u5 &= ~(HT_NOTRDY | HT_CMDMSK); + ctlr->u5 &= ~(HT_NOTRDY); + chan_clear(chan, DEV_WEOR|DEV_SEL); + sim_debug(DEBUG_CHAN, dptr, "disconnecting\n"); + return SCPE_OK; + } + + /* Handle writing of data */ + if (chan_test(chan, CTL_WRITE) && (uptr->u5 & HT_CMDMSK) == HSEL) { + uint8 ch; + + if (uptr->u6 == 0 && sim_tape_wrp(uptr)) { + ctlr->u5 &= ~(HT_NOTRDY); + ht_tape_posterr(uptr, PROG_FILEPROT); + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + + switch(chan_read_char(chan, &ch, 0)) { + case TIME_ERROR: + ht_tape_posterr(uptr, DATA_RESPONSE); + break; + case DATA_OK: + uptr->u5 |= HT_WRITE|HT_NOTRDY; + ctlr->u5 |= HT_NOTRDY; + ht_buffer[GET_DEV_BUF(dptr->flags)][uptr->u6++] = ch; + sim_debug(DEBUG_DATA, dptr, " write %d \n", ch); + if (uptr->u6 < BUFFSIZE) + break; + /* Overran tape buffer, give error */ + ht_tape_posterr(uptr, DATA_TRACKSKEW); + case END_RECORD: + if (uptr->u6 != 0) { + sim_debug(DEBUG_CMD, dptr, + " Write Block %d chars %d words\n", uptr->u6, + uptr->u6 / 6); + r = sim_tape_wrrecf(uptr, + &ht_buffer[GET_DEV_BUF(dptr->flags)][0], + uptr->u6); + uptr->u5 &= ~HT_WRITE; + uptr->u6 = 0; + if (r != MTSE_OK) { + ht_error(uptr, schan, r); + chan9_set_error(chan, SNS_UEND); + } + } + chan_set(chan, DEV_REOR|CTL_END); + } + sim_activate(uptr, us_to_ticks(20)); + return SCPE_OK; + } + + /* Handle reading of data */ + if (chan_test(chan, CTL_READ) && (uptr->u5 & HT_CMDMSK) == (HSEL)) { + uint8 ch; + + if (uptr->u6 == 0) { + if (ht_sense[schan] & BACK_MODE) + r = sim_tape_rdrecr(uptr, + &ht_buffer[GET_DEV_BUF(dptr->flags)][0], + &reclen, BUFFSIZE); + else + r = sim_tape_rdrecf(uptr, + &ht_buffer[GET_DEV_BUF(dptr->flags)][0], + &reclen, BUFFSIZE); + if (r == MTSE_TMK) + sim_debug(DEBUG_CMD, dptr, "Read Mark\n"); + else + sim_debug(DEBUG_CMD, dptr, "Read %d bytes\n", reclen); + /* Handle EOM special */ + if (r == MTSE_EOM && (uptr->u5 & HT_EOT) == 0) { + uptr->u5 |= HT_EOT; + ht_sense[schan] |= EXP_NODATA; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + ctlr->u5 &= ~HT_NOTRDY; + sim_activate(uptr, us_to_ticks(20)); + return SCPE_OK; + } else + /* Not read ok, return error */ + if (r != MTSE_OK) { + ht_error(uptr, schan, r); + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + ctlr->u5 &= ~HT_NOTRDY; + uptr->wait = 0; + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + uptr->hwmark = reclen; + uptr->u5 |= HT_NOTRDY; + ctlr->u5 |= HT_NOTRDY; + } + + if (uptr->u6 > (int32)uptr->hwmark) { + chan_set(chan, DEV_REOR|CTL_END); + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; + } + ch = ht_buffer[GET_DEV_BUF(dptr->flags)][uptr->u6++]; + sim_debug(DEBUG_DATA, dptr, "data %02o\n", ch); + switch(chan_write_char(chan, &ch, + (uptr->u6 > (int32)uptr->hwmark)?DEV_REOR:0)) { + case TIME_ERROR: + /* Nop flag as timming error */ + ht_tape_posterr(uptr, DATA_RESPONSE); + break; + case END_RECORD: + sim_debug(DEBUG_DATA, dptr, "eor\n"); + chan_set(chan, DEV_REOR|CTL_END); + case DATA_OK: + break; + } + sim_activate(uptr, us_to_ticks(20)); + return SCPE_OK; + } + + /* If we have a command, keep scheduling us. */ + if ((uptr->u5 & HT_CMDMSK) == (HSEL)) + sim_activate(uptr, us_to_ticks(50)); + return SCPE_OK; +} + +/* Post a error on a given unit. */ +void +ht_tape_posterr(UNIT * uptr, uint32 error) +{ + int chan; + int schan; + int sel; + + chan = UNIT_G_CHAN(uptr->flags); + sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + schan = (chan * 2) + sel; + uptr->u5 |= HT_ATTN; + ht_sense[schan] = error; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_attn(chan, sel); + if (error != 0) + chan9_set_error(chan, SNS_UEND); +} + +/* Convert error codes to sense codes */ +t_stat ht_error(UNIT * uptr, int schan, t_stat r) +{ + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_TMK: /* tape mark */ + uptr->u5 |= HT_MARK; + ht_sense[schan] |= EXP_MARK; + break; + + case MTSE_WRP: /* write protected */ + uptr->u5 |= HT_ATTN; + ht_sense[schan] |= PROG_FILEPROT; + break; + + case MTSE_UNATT: /* unattached */ + uptr->u5 |= HT_ATTN; + ht_sense[schan] = PROG_NOTLOAD; + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_FMT: /* invalid format */ + case MTSE_RECE: /* error in record */ + uptr->u5 |= HT_ERR; + ht_sense[schan] |= DATA_CODECHK; + break; + case MTSE_BOT: /* beginning of tape */ + uptr->u5 |= HT_BOT; + ht_sense[schan] |= PROG_BOT; + break; + case MTSE_EOM: /* end of medium */ + uptr->u5 |= HT_EOT; + ht_sense[schan] |= PROG_EOT; + break; + default: /* Anything else if error */ + ht_sense[schan] = PROG_INVCODE; + break; + } + return SCPE_OK; +} + +/* Process command */ +void +ht_tape_cmd(DEVICE * dptr, UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + int schan = (chan * 2) + sel; + UNIT *up; + uint8 c; + int i; + int t; + int cmd; + int unit; + t_stat r; + t_mtrlnt reclen; + + /* Get information on unit */ + + /* Check if we have a command yet */ + switch(chan_read_char(chan, &c, 0)) { + case END_RECORD: + case TIME_ERROR: + return; + case DATA_OK: + break; + } + + c &= 017; + if (c == 012) + c = 0; + ht_cmdbuffer[chan] <<= 4; + ht_cmdbuffer[chan] |= c; + ht_cmdcount[chan]++; + /* If did not find end of sequence, request more */ + if ((ht_cmdbuffer[chan] & 0xff) != HEOS) { + if (ht_cmdcount[chan] >= 8) { + /* command error */ + ht_cmdcount[chan] = 0; + ht_sense[schan] = PROG_INVCODE; + ht_unit[schan] = 0; + chan_set(chan, DEV_REOR|SNS_UEND); + uptr->u5 &= ~HT_CMD; + } + return; + } + + sim_debug(DEBUG_DETAIL, dptr, " cmd = %08x %d nybbles ", + ht_cmdbuffer[chan], ht_cmdcount[chan]); + + /* See if we have a whole command string yet */ + uptr->u5 &= ~HT_CMD; + cmd = 0xff; + unit = NUM_UNITS_HT + 1; + for (i = ht_cmdcount[chan] - 2; i >= 2; i -= 2) { + t = (ht_cmdbuffer[chan] >> (i * 4)) & 0xff; + switch (t) { + case HSEL: /* Select */ + case HSBR: /* Select for backwards reading */ + i--; + unit = (ht_cmdbuffer[chan] >> (i * 4)) & 0xf; + ht_sense[schan] = 0; /* Clear sense codes */ + cmd = t; + break; + case HEOS: /* End of sequence */ + break; + case HRLF: /* Reserved Light Off */ + case HRLN: /* Reserved Light On */ + case HCLF: /* Check light off */ + case HCLN: /* Check light on */ + case HNOP: /* Nop */ + case HCCR: /* Change cartridge and rewind */ + case HRWD: /* Rewind */ + case HRUN: /* Rewind and unload */ + case HERG: /* Erase long gap */ + case HWTM: /* Write tape mark */ + case HBSR: /* Backspace */ + case HBSF: /* Backspace file */ + case HSKR: /* Space */ + case HSKF: /* Space file */ + case HCHC: /* Change Cartridge */ + case HUNL: /* Unload Cartridge */ + case HFPN: /* File Protect On */ + if (cmd != HSEL) + cmd = 0xff; + else + cmd = t; + break; + default: + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Invalid command %x\n", + cmd); + ht_sense[schan] = PROG_INVCODE; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + return; + } + } + + /* Ok got a full command */ + ht_cmdcount[chan] = 0; + + /* Make sure we got a unit and command */ + if (unit <= NUM_UNITS_HT) + ht_unit[schan] = unit; + else { + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, + "Invalid unit %d cmd=%x\n", unit, cmd); + ht_sense[schan] = STAT_NOTRDY; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + return; + } + + if (cmd == 0xff) { + /* command error */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Invalid command %x\n", + cmd); + ht_sense[schan] = PROG_INVCODE; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + return; + } + + /* Find real device this command is for */ + up = &dptr->units[unit]; + if ((up->flags & UNIT_ATT) == 0) { + /* Not attached! */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Not ready %d cmd=%x\n", + unit, cmd); + ht_sense[schan] = STAT_NOTRDY; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + return; + } + + if (up->u5 & HT_NOTRDY || up->wait > 0) { + /* Unit busy */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Busy unit %d cmd=%x\n", unit, + cmd); + ht_sense[schan] = PROG_BUSY; + chan_set(chan, DEV_REOR|CTL_END); + chan9_set_error(chan, SNS_UEND); + return; + } + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "Execute unit %d cmd=%x ", + unit, cmd); + + /* Ok, unit is ready and not in motion, set up to run command */ + up->u5 &= ~(HT_PEND | HT_MARK | HT_ERR | HT_CMDMSK); + up->wait = 0; + up->u5 |= cmd; + ht_sense[schan] &= ~BACK_MODE; + r = MTSE_OK; + switch (cmd) { + case HSBR: /* Select for backwards reading */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "HSBR\n"); + up->hwmark = -1; + up->u6 = 0; + ht_sense[schan] |= BACK_MODE; + up->u5 &= ~(HT_CMDMSK); + up->u5 |= HSEL; + chan_set(chan, DEV_REOR|DEV_SEL); + break; + + case HSEL: /* Select */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "HSEL\n"); + up->hwmark = -1; + up->u6 = 0; + chan_set(chan, DEV_REOR|DEV_SEL); + break; + + case HRLF: /* Reserved Light Off */ + case HRLN: /* Reserved Light On */ + case HCLF: /* Check light off */ + case HCLN: /* Check light on */ + case HFPN: /* File Protect On (Nop for now ) */ + case HEOS: /* End of sequence */ + case HNOP: /* Nop */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "NOP\n"); + up->u5 &= ~(HT_NOTRDY | HT_CMDMSK); + chan_set(chan, DEV_REOR|CTL_END); + return; + + case HRWD: /* Rewind */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "REW\n"); + if (up->u5 & HT_BOT) { + r = MTSE_OK; + up->wait = 1; + } else { + r = sim_tape_rewind(up); + up->u5 &= ~HT_EOT; + up->wait = 500; + } + up->u5 |= HT_BOT|HT_NOTRDY; + chan_set(chan, DEV_REOR|CTL_END); + break; + + case HERG: /* Erase long gap */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "ERG\n"); + if (sim_tape_wrp(up)) { + r = MTSE_WRP; + } else { + up->wait = 10; + up->u5 |= HT_PEND|HT_NOTRDY; + uptr->u5 |= HT_NOTRDY; + up->u5 &= ~HT_BOT; + } + break; + + case HWTM: /* Write tape mark */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "WTM\n"); + if (sim_tape_wrp(up)) { + r = MTSE_WRP; + } else { + r = sim_tape_wrtmk(up); + up->wait = 5; + up->u5 |= HT_PEND|HT_NOTRDY; + up->u5 &= ~(HT_BOT|HT_EOT); + uptr->u5 |= HT_NOTRDY; + } + break; + + case HBSR: /* Backspace */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "BSR\n"); + if (sim_tape_bot(up)) { + r = MTSE_BOT; + break; + } + r = sim_tape_sprecr(up, &reclen); + up->wait = reclen / 100; + up->wait += 2; + up->u5 |= HT_PEND|HT_NOTRDY; + up->u5 &= ~(HT_BOT|HT_EOT); + uptr->u5 |= HT_NOTRDY; + if (r == MTSE_TMK) { + r = MTSE_OK; + up->u5 |= HT_MARK; + } + if (sim_tape_bot(up)) + up->u5 |= HT_BOT; + else + up->u5 &= ~HT_BOT; + break; + + case HBSF: /* Backspace file */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "BSF\n"); + if (sim_tape_bot(up)) { + r = MTSE_BOT; + break; + } + while ((r = sim_tape_sprecr(up, &reclen)) == MTSE_OK) { + up->wait += reclen; + } + up->wait /= 100; + up->wait += 2; + up->u5 |= HT_PEND|HT_NOTRDY; + up->u5 &= ~(HT_BOT|HT_EOT); + uptr->u5 |= HT_NOTRDY; + if (r == MTSE_TMK) { + r = MTSE_OK; + up->u5 |= HT_MARK; + } + if (sim_tape_bot(up)) + up->u5 |= HT_BOT; + else + up->u5 &= ~HT_BOT; + break; + + case HSKR: /* Space */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "SKR\n"); + r = sim_tape_sprecf(up, &reclen); + up->u5 |= HT_PEND|HT_NOTRDY; + uptr->u5 |= HT_NOTRDY; + if (r == MTSE_TMK) { + r = MTSE_OK; + up->u5 |= HT_MARK; + } + up->wait = reclen / 100; + up->wait += 2; + up->u5 &= ~HT_BOT; + break; + + case HSKF: /* Space file */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "SKF\n"); + while ((r = sim_tape_sprecf(up, &reclen)) == MTSE_OK) { + up->wait += reclen; + } + up->wait /= 100; + up->wait += 2; + up->u5 |= HT_PEND|HT_NOTRDY; + uptr->u5 |= HT_NOTRDY; + if (r == MTSE_TMK) { + r = MTSE_OK; + up->u5 |= HT_MARK; + } + up->u5 &= ~HT_BOT; + break; + + case HCCR: /* Change cartridge and rewind */ + case HRUN: /* Rewind and unload */ + case HCHC: /* Change Cartridge */ + case HUNL: /* Unload Cartridge */ + sim_debug((DEBUG_DETAIL | DEBUG_CMD), dptr, "RUN\n"); + r = sim_tape_detach(up); + chan_set(chan, DEV_REOR|CTL_END); + up->u5 |= HT_NOTRDY; + up->wait = 100; + break; + } + + if (r != MTSE_OK) { + ht_error(up, schan, r); + chan9_set_error(chan, SNS_UEND); + chan9_set_attn(chan, sel); + chan_set(chan, DEV_REOR|CTL_END); + up->u5 &= ~(HT_NOTRDY | HT_CMDMSK); + uptr->u5 &= ~HT_NOTRDY; + up->wait = 0; + } else if (up->u5 & HT_CMDMSK) { + sim_activate(up, us_to_ticks(1000)); + } else { + chan9_set_attn(chan, sel); + } + + return; +} + +/* Boot Hypertape. Build boot card loader in memory and transfer to it */ +t_stat +ht_boot(int unit_num, DEVICE * dptr) +{ +#ifdef I7090 + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags) - 1; + int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0; + int dev = uptr->u3; + int msk = (chan / 2) | ((chan & 1) << 11); + extern uint16 IC; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + if (dev == 0) + dev = 012; + /* Build Boot program in memory */ + M[0] = 0000025000101LL; /* IOCD RSCQ,,21 */ + M[1] = 0006000000001LL; /* TCOA * */ + M[2] = 0002000000101LL; /* TRA RSCQ */ + + M[0101] = 0054000000113LL; /* RSCQ RSCC SMSQ Mod */ + M[0101] |= ((t_uint64) (msk)) << 24; + M[0102] = 0064500000000LL; /* SCDQ SCDC 0 Mod */ + M[0102] |= ((t_uint64) (msk)) << 24; + M[0103] = 0044100000000LL; /* LDI 0 */ + M[0104] = 0405400001700LL; /* LFT 1700 */ + M[0105] = 0002000000122LL; /* TRA HYP7 */ + M[0106] = 0006000000102LL; /* TCOQ TCOC SCDQ Mod */ + M[0106] |= ((t_uint64) (chan)) << 24; + M[0107] = 0002000000003LL; /* TRA 3 Enter IBSYS */ + M[0110] = 0120600120112LL; + M[0110] |= ((t_uint64) (dev)) << 18; + M[0111] = 0120600030412LL; /*LDVCY DVCY Mod */ + M[0111] |= ((t_uint64) (dev)) << 18; + M[0112] = 0010000000000LL; /* * */ + M[0113] = 0700000000012LL; /* HYP6 SMS 10 */ + M[0113] |= sel; + M[0114] = 0200000200110LL; /* CTLR *-4 */ + M[0115] = 0400001000116LL; /* CPYP *+1,,1 */ + M[0116] = 0000000000116LL; /* WTR * */ + M[0117] = 0100000000115LL; /* TCH *-2 */ + M[0120] = 0700000400113LL; /* SMS* HYP6 */ + M[0121] = 0200000000111LL; /* CTL HYP6-2 */ + M[0122] = 0076000000350LL; /* HYP7 RICC ** */ + M[0122] |= ((t_uint64) (chan)) << 9; + M[0123] = 0054000000120LL; /* RSCC *-3 Mod */ + M[0123] |= ((t_uint64) (msk)) << 24; + M[0124] = 0500000000000LL; /* CPYD 0,,0 */ + M[0125] = 0340000000125LL; /* TWT * */ + IC = 0101; + return SCPE_OK; +#else + return SCPE_NOFNC; +#endif +} + +t_stat +ht_reset(DEVICE * dptr) +{ + int i; + + for (i = 0; i < NUM_CHAN; i++) { + ht_cmdbuffer[i] = ht_cmdcount[i] = 0; + ht_sense[i] = 0; + } + return SCPE_OK; +} + + +t_stat +ht_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = HT_BOT /*|HT_ATTN */ ; + return SCPE_OK; +} + +t_stat +ht_detach(UNIT * uptr) +{ + uptr->u5 = 0; + if (uptr->flags & UNIT_DIS) return SCPE_OK; + return sim_tape_detach(uptr); +} + +t_stat +ht_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "IBM 7340 Hypertape unit\n\n"); + help_set_chan_type(st, dptr, "IBM 7340 Hypertape"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +ht_description(DEVICE *dptr) +{ + return "IBM 7340 Hypertape unit"; +} + +#endif diff --git a/I7000/i7000_lpr.c b/I7000/i7000_lpr.c new file mode 100644 index 00000000..de7453ff --- /dev/null +++ b/I7000/i7000_lpr.c @@ -0,0 +1,529 @@ +/* i7000_lpr.c: IBM 7000 Line Printer. + + 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. + + This is the standard line printer. + + These units each buffer one record in local memory and signal + ready when the buffer is full or empty. The channel must be + ready to recieve/transmit data when they are activated since + they will transfer their block during chan_cmd. All data is + transmitted as BCD characters. + +*/ + +#include "i7000_defs.h" +#include "sim_card.h" +#include "sim_defs.h" +#ifdef NUM_DEVS_LPR + +#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE + + +/* Flags for line printer. */ +#define ECHO (1 << (UNIT_V_UF+0)) +#ifdef I7070 +#define ATTENA (1 << (UNIT_V_UF+1)) +#define ATTENB (1 << (UNIT_V_UF+2)) +#endif +#ifdef I7080 +#define DOUBLE (1 << (UNIT_V_UF+1)) +#define PROGRAM (1 << (UNIT_V_UF+2)) +#endif + + +/* std devices. data structures + + lpr_dev Line Printer device descriptor + lpr_unit Line Printer unit descriptor + lpr_reg Line Printer register list + lpr_mod Line Printer modifiers list +*/ + + +struct _lpr_data +{ + uint8 lbuff[145]; /* Output line buffer */ +} +lpr_data[NUM_DEVS_LPR]; + +uint32 lpr_cmd(UNIT *, uint16, uint16); +void lpr_ini(UNIT *, t_bool); +t_stat lpr_srv(UNIT *); +t_stat lpr_reset(DEVICE *); +t_stat lpr_attach(UNIT *, CONST char *); +t_stat lpr_detach(UNIT *); +t_stat lpr_setlpp(UNIT *, int32, CONST char *, void *); +t_stat lpr_getlpp(FILE *, UNIT *, int32, CONST void *); +t_stat lpr_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *lpr_description(DEVICE *dptr); + +UNIT lpr_unit[] = { + {UDATA(lpr_srv, UNIT_S_CHAN(CHAN_CHUREC) | UNIT_LPR, 55), 300}, /* A */ +#if NUM_DEVS_LPR > 1 + {UDATA(lpr_srv, UNIT_S_CHAN(CHAN_CHUREC+1) | UNIT_LPR, 55), 300}, /* B */ +#endif +}; + +MTAB lpr_mod[] = { + {ECHO, 0, NULL, "NOECHO", NULL, NULL, NULL, "Don't echo to console"}, + {ECHO, ECHO, "ECHO", "ECHO", NULL, NULL, NULL, "Echo to console"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"}, +#ifdef I7080 + {DOUBLE|PROGRAM, 0, "SINGLE", "SINGLE", NULL, NULL, NULL, "Single space output"}, + {DOUBLE|PROGRAM, DOUBLE, "DOUBLE", "DOUBLE", NULL, NULL, NULL, "Double space output"}, + {DOUBLE|PROGRAM, PROGRAM, "PROGRAM", "PROGRAM", NULL, NULL, NULL, "Programatic spacing"}, +#endif +#ifdef I7070 + {ATTENA|ATTENB, 0, NULL, "NOATTEN", NULL, NULL, NULL, "No attention signal"}, + {ATTENA|ATTENB, ATTENA, "ATTENA", "ATTENA", NULL, NULL, NULL, "Signal Attention A"}, + {ATTENA|ATTENB, ATTENB, "ATTENB", "ATTENB", NULL, NULL, NULL, "Signal Attention B"}, +#endif +#ifdef I7010 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL, "Set device channel"}, +#endif + {0} +}; + +DEVICE lpr_dev = { + "LP", lpr_unit, NULL, lpr_mod, + NUM_DEVS_LPR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, + &lpr_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lpr_help, NULL, NULL, &lpr_description +}; + + + +/* + * Line printer routines + */ + +t_stat +lpr_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = 0; + while(*cptr != '\0') { + if (*cptr < '0' || *cptr > '9') + return SCPE_ARG; + i = (i * 10) + (*cptr++) - '0'; + } + if (i < 20 || i > 100) + return SCPE_ARG; + uptr->capac = i; + uptr->u4 = 0; + return SCPE_OK; +} + +t_stat +lpr_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "linesperpage=%d", uptr->capac); + return SCPE_OK; +} + +t_stat +print_line(UNIT * uptr, int chan, int unit) +{ +/* Convert word record into column image */ +/* Check output type, if auto or text, try and convert record to bcd first */ +/* If failed and text report error and dump what we have */ +/* Else if binary or not convertable, dump as image */ + + char out[150]; /* Temp conversion buffer */ + int i; + + if ((uptr->flags & (UNIT_ATT | ECHO)) == 0) + return SCPE_UNATT; /* attached? */ + + /* Try to convert to text */ + memset(out, 0, sizeof(out)); + +#ifdef I7080 + if (uptr->flags & PROGRAM) { + switch(lpr_data[unit].lbuff[0] & 077) { + case 060: /* suppress space */ + uptr->u5 |= URCSTA_SKIPAFT | (0 << 12); + break; + case 020: /* single space */ + break; + case 012: /* double space */ + uptr->u5 |= URCSTA_SKIPAFT | (1 << 12); + break; + default: + /* Skip channel */ + i = 0; + switch(lpr_data[unit].lbuff[0] & 017) { + case 3: i = 5 - (uptr->u4 % 5); break; + case 2: i = 8 - (uptr->u4 % 8); break; + case 1: + case 9: if (uptr->u4 == 1) + break; + i = uptr->capac - uptr->u4 + 1; break; + } + if (i == 0) + break; + uptr->u5 |= URCSTA_SKIPAFT | (i << 12); + } + /* Scan each column */ + for (i = 0; i < 143; i++) { + int bcd = lpr_data[unit].lbuff[i+1] & 077; + + out[i] = sim_six_to_ascii[bcd]; + } + } else { + if (uptr->flags & DOUBLE) + uptr->u5 |= URCSTA_SKIPAFT | (1 << 12); +#endif + /* Scan each column */ + for (i = 0; i < 144; i++) { + int bcd = lpr_data[unit].lbuff[i] & 077; + + out[i] = sim_six_to_ascii[bcd]; + } + +#ifdef I7080 + } +#endif + + /* Trim trailing spaces */ + for (--i; i > 0 && out[i] == ' '; i--) ; + out[++i] = '\r'; + out[++i] = '\n'; + out[++i] = '\0'; + sim_debug(DEBUG_DETAIL, &lpr_dev, "WRS unit=%d [%s]\n", unit, &out[0]); + + /* Print out buffer */ + if (uptr->flags & UNIT_ATT) + sim_fwrite(&out, 1, i, uptr->fileref); + if (uptr->flags & ECHO) { + int j = 0; + + while (j <= i) + sim_putchar(out[j++]); + } + uptr->u4++; + if (uptr->u4 > (int32)uptr->capac) { + uptr->u4 = 1; + } + + if (uptr->u5 & URCSTA_SKIPAFT) { + i = (uptr->u5 >> 12) & 0x7f; + if (i == 0) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) + sim_putchar('\r'); + } else { + for (; i > 1; i--) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) + sim_putchar('\r'); + sim_putchar('\n'); + uptr->u4++; + if (uptr->u4 > (int32)uptr->capac) { + uptr->u4 = 1; + } + } + } + uptr->u5 &= ~(URCSTA_SKIPAFT|(0x7f << 12)); + } + + if (uptr->u4 == 1) + lpr_chan9[chan] = 1; +#ifdef I7010 + if (uptr->u4 == uptr->capac) + lpr_chan12[chan] = 1; +#endif + + return SCPE_OK; +} + + +uint32 lpr_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - lpr_unit); +#ifdef I7010 + int i; +#endif + + /* Are we currently tranfering? */ + if (uptr->u5 & URCSTA_WRITE) + return SCPE_BUSY; + + switch(cmd) { + /* Test ready */ + case IO_TRS: + if (uptr->flags & UNIT_ATT) + return SCPE_OK; + break; + + /* Suppress punch */ + case IO_RUN: + sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd RUN\n", u); + uptr->u5 &= ~URCSTA_FULL; + return SCPE_OK; + + /* Get record from CPU */ + case IO_WRS: + sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd WRS\n", u); + lpr_chan9[chan] = 0; +#ifdef I7010 + lpr_chan12[chan] = 0; + switch (dev & 017) { + case 01: + uptr->u5 |= URCSTA_WMKS; + break; + case 012: + uptr->u5 &= ~URCSTA_WMKS; + break; + default: + return SCPE_IOERR; + } +#endif + chan_set_sel(chan, 1); + uptr->u5 |= URCSTA_WRITE; + uptr->u3 = 0; + if ((uptr->u5 & URCSTA_BUSY) == 0) + sim_activate(uptr, 50); + return SCPE_OK; + + case IO_CTL: + sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd CTL %02o\n", u, dev & 077); +#ifdef I7010 + /* 1-0 immediate skip to channel */ + /* 00xxxx skip to channel immediate */ + /* 11xxxx skip to channel after */ + /* 1000xx space before */ + /* 0100xx space after */ + switch(dev & 060) { + case 020: /* Space after */ + uptr->u5 |= URCSTA_SKIPAFT | ((dev & 03) << 12); + break; + case 040: /* Space before */ + for (i = dev & 03; i > 1; i--) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + } + } + break; + case 0: /* Skip channel immediate */ + case 060: /* Skip channel after */ + i = 0; + switch(dev & 017) { + case 3: i = 5 - (uptr->u4 % 5); break; + case 2: i = 8 - (uptr->u4 % 8); break; + case 1: + case 9: if (uptr->u4 == 1) + break; + i = uptr->capac - uptr->u4 + 1; break; + case 12: i = (uptr->capac/2) - uptr->u4; break; + } + if (i == 0) + break; + if (dev & 060) { + uptr->u5 |= URCSTA_SKIPAFT | (i << 12); + break; + } + for (; i > 0; i--) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + } + } + break; + } + if (uptr->u4 == uptr->capac) + lpr_chan12[chan] = 1; +#endif + if (uptr->u4 == 1) + lpr_chan9[chan] = 1; + return SCPE_OK; + } + chan_set_attn(chan); + return SCPE_IOERR; +} + +/* Handle transfer of data for printer */ +t_stat +lpr_srv(UNIT *uptr) { + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - lpr_unit); + /* Waiting for disconnect */ + if (uptr->u5 & URCSTA_WDISCO) { + if (chan_stat(chan, DEV_DISCO)) { + chan_clear(chan, DEV_SEL|DEV_WEOR); + uptr->u5 &= ~ URCSTA_WDISCO; + } else { + /* No disco yet, try again in a bit */ + sim_activate(uptr, 50); + return SCPE_OK; + } + /* If still busy, schedule another wait */ + if (uptr->u5 & URCSTA_BUSY) + sim_activate(uptr, uptr->wait); + } + + if (uptr->u5 & URCSTA_BUSY) { + /* Done waiting, print line */ + if (uptr->u5 & URCSTA_FULL) { + uptr->u5 &= ~URCSTA_FULL; + switch(print_line(uptr, chan, u)) { + case SCPE_EOF: + case SCPE_UNATT: + chan_set_eof(chan); + break; + /* If we get here, something is wrong */ + case SCPE_IOERR: + chan_set_error(chan); + break; + case SCPE_OK: + break; + } + } + memset(&lpr_data[u].lbuff[0], 0, 144); + uptr->u5 &= ~URCSTA_BUSY; +#ifdef I7070 + switch(uptr->flags & (ATTENA|ATTENB)) { + case ATTENA: chan_set_attn_a(chan); break; + case ATTENB: chan_set_attn_b(chan); break; + } +#endif +#ifdef I7010 + chan_set_attn_urec(chan, lpr_dib.addr); +#endif + } + + /* Copy next column over */ + if (uptr->u5 & URCSTA_WRITE && uptr->u3 < 144) { + switch(chan_read_char(chan, &lpr_data[u].lbuff[uptr->u3], + (uptr->u3 == 143)?DEV_REOR: 0)) { + case TIME_ERROR: + case END_RECORD: + uptr->u5 |= URCSTA_WDISCO|URCSTA_BUSY|URCSTA_FULL; + uptr->u5 &= ~URCSTA_WRITE; + break; + case DATA_OK: + sim_debug(DEBUG_DATA, &lpr_dev, "%d: Char < %02o\n", u, + lpr_data[u].lbuff[uptr->u3]); +#ifdef I7010 + if (uptr->u5 & URCSTA_WMKS) { + if (lpr_data[u].lbuff[uptr->u3] & 0200) + lpr_data[u].lbuff[uptr->u3] = 1; + else + lpr_data[u].lbuff[uptr->u3] = 012; + } +#endif + uptr->u3++; + break; + } + sim_activate(uptr, 10); + } + return SCPE_OK; +} + +void +lpr_ini(UNIT *uptr, t_bool f) { +} + +t_stat +lpr_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + uptr->u4 = 0; + return SCPE_OK; +} + +t_stat +lpr_detach(UNIT * uptr) +{ + if (uptr->u5 & URCSTA_FULL) + print_line(uptr, UNIT_G_CHAN(uptr->flags), uptr - lpr_unit); + return detach_unit(uptr); +} + +t_stat +lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + + fprintf (st, "%s\n\n", lpr_description(dptr)); + fprintf (st, "The line printer output can be echoed to the console to check"); + fprintf (st, "the \nprogress of jobs being run. This can be done with the\n"); + fprintf (st, " sim> SET %s ECHO set echo to console\n\n", dptr->name); + fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n"); + fprintf (st, " sim> SET %s LINESPERPAGE=n\n\n", dptr->name); + fprintf (st, "The default is 59 lines per page.\n\n"); +#ifdef I7080 + fprintf (st, "The 716 printer can operate in one of three spacing modes\n"); + fprintf (st, " sim> SET %s SINGLE for single spacing\n", dptr->name); + fprintf (st, " sim> SET %s DOUBLE for double spacing\n", dptr->name); + fprintf (st, " sim> SET %s PROGRAM for program control of spacing\n\n", dptr->name); +#endif +#ifdef I7070 + fprintf (st, "Unit record devices can be configured to interrupt the CPU on\n"); + fprintf (st, "one of two priority channels A or B, to set this\n\n"); + fprintf (st, " sim> SET %s ATTENA to set device to raise Atten A\n", dptr->name); + fprintf (st, " sim> SET %s ATTENB to set device to raise Atten B\n\n", dptr->name); +#endif +#ifdef I7010 + help_set_chan_type(st, dptr, "Line printer"); +#endif + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +lpr_description(DEVICE *dptr) +{ +#ifdef I7010 + return "1403 Line Printer"; +#endif +#ifdef I7070 + return "7400 Line Printer"; +#endif +#ifdef I7080 + return "716 Line Printer"; +#endif +} + +#endif + + diff --git a/I7000/i7000_mt.c b/I7000/i7000_mt.c new file mode 100644 index 00000000..a9f4244a --- /dev/null +++ b/I7000/i7000_mt.c @@ -0,0 +1,1398 @@ +/* i7090_mt.c: IBM 7090 Magnetic tape controller + + 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. + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "i7000_defs.h" +#include "sim_tape.h" + +#ifndef NUM_DEVS_MT +#define NUM_DEVS_MT 0 +#endif + +#if (NUM_DEVS_MT > 0) || defined(MT_CHANNEL_ZERO) + +#define BUFFSIZE (MAXMEMSIZE * CHARSPERWORD) +#define UNIT_MT(x) UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | \ + UNIT_S_CHAN(x) +#define MTUF_LDN (1 << MTUF_V_UF) +#define MTUF_ONLINE (1 << UNIT_V_UF_31) + + +/* in u3 is current frame of tape */ +/* in u5 holds the commands */ +#define MT_RDS 1 +#define MT_RDSB 2 +#define MT_WRS 3 +#define MT_WRSB 4 +#define MT_WEF 5 +#define MT_BSR 6 +#define MT_BSF 7 +#define MT_REW 8 +#define MT_SDN 9 +#define MT_RUN 10 +#define MT_SKIP 11 /* Do skip to end of record */ +#define MT_WRITE 12 /* Actual transfer operation */ +#define MT_SKR 13 +#define MT_ERG 14 +#define MT_RDB 15 +#define MT_LREW 16 /* Low speed rewind */ +#define MT_HREW 17 /* High speed rewind */ +#define MT_CMDMSK 000037 /* Command being run */ +#define MT_RDY 000040 /* Device is ready for command */ +#define MT_IDLE 000100 /* Tape still in motion */ +#define MT_MARK 000200 /* Hit tape mark */ +#define MT_EOT 000400 /* At End Of Tape */ +#define MT_RM 001000 /* Hit a record mark character */ +#define MT_EOR 002000 /* Set EOR on next record */ +#define MT_UNLOAD 004000 /* Unload when rewind done */ +#define MT_EGAP 010000 /* Write extended gap on next write */ + +/* u6 holds the current buffer position */ + +/* Flags for mt_chan */ +#define MTC_SEL 0020 /* Controller executing read/write */ +#define MTC_BSY 0040 /* Controller is busy - executing cmd */ +#define MTC_UNIT 0017 /* device Channel is on */ + +/* Timing for tape */ +#define IPS 75 /* Inches per second 75 or 112 */ +#define HS_IPS 500 /* High speed rewind Inches per second */ + +#define LD 200 +#define HD 555 + +#define LT_GAP_LEN ((3 * LD)/ 4) /* Gap length for low density */ +#define HT_GAP_LEN ((3 * HD)/ 4) /* Gap length for high density */ +#define LT (1000000/(LD * IPS)) /* Time per char low density */ +#define HT (1000000/(HD * IPS)) /* Time per char high density */ +#define LT_GAP_TIM (LT_GAP_LEN * LT) /* Time per char low density */ +#define HT_GAP_TIM (HT_GAP_LEN * HT) /* Time per char high density */ + +/* Normal frame time */ +#define T1 ((uptr->flags & MTUF_LDN) ?LT:HT) +#define T1_us us_to_ticks(T1) +/* Gap time */ +#define T2 ((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM) +#define T2_us us_to_ticks(T2) +/* Start time */ +#define T3 (((uptr->flags & MTUF_LDN) ?LT_GAP_TIM:HT_GAP_TIM) + 500) +#define T3_us us_to_ticks(T3) +#define GAP_LEN ((uptr->flags & MTUF_LDN) ?LT_GAP_LEN:HT_GAP_LEN) + +/* Definitions */ +uint32 mt_cmd(UNIT *, uint16, uint16); +t_stat mt_srv(UNIT *); +t_stat mt_boot(int32, DEVICE *); +void mt_ini(UNIT *, t_bool); +t_stat mt_reset(DEVICE *); +t_stat mt_attach(UNIT *, CONST char *); +t_stat mt_detach(UNIT *); +t_stat mt_rew(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *mt_description (DEVICE *dptr); +extern t_stat chan_boot(int32, DEVICE *); +#ifdef I7010 +extern uint8 chan_io_status[NUM_CHAN]; /* Channel status */ +#endif + +#ifdef MT_CHANNEL_ZERO +#define NUM_DEVS (NUM_DEVS_MT + 1) +#else +#define NUM_DEVS (NUM_DEVS_MT) +#endif + +/* Channel level activity */ +uint8 mt_chan[NUM_DEVS]; + +/* One buffer per channel */ +uint8 mt_buffer[NUM_DEVS][BUFFSIZE]; + +UNIT mta_unit[] = { +/* Controller 1 */ +#if (NUM_DEVS_MT > 0) + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(1), 0), 0}, /* 9 */ +#if (NUM_DEVS_MT > 1) +/* Controller 2 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(2), 0), 0}, /* 9 */ +#if (NUM_DEVS_MT > 2) +/* Controller 3 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(3), 0), 0}, /* 9 */ +#if (NUM_DEVS_MT > 3) +/* Controller 4 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(4), 0), 0}, /* 9 */ +#if (NUM_DEVS_MT > 4) +/* Controller 5 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(5), 0), 0}, /* 9 */ +#if (NUM_DEVS_MT > 5) +/* Controller 6 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(6), 0), 0}, /* 9 */ +#endif +#endif +#endif +#endif +#endif +#endif +#ifdef MT_CHANNEL_ZERO +/* Controller 7 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 7 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT(0), 0), 0}, /* 9 */ +#endif +}; + +MTAB mt_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, NULL, + "Write ring in place"}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, NULL, + "No write ring in place"}, + {MTUF_LDN, 0, "high density", "HIGH", &mt_tape_density, NULL, NULL, + "556 BPI"}, + {MTUF_LDN, MTUF_LDN, "low density", "LOW", &mt_tape_density, NULL, NULL, + "200 BPI"}, +#ifdef I7090 + {MTUF_ONLINE, 0, "offline", "OFFLINE", NULL, NULL, NULL, + "Tape offline"}, + {MTUF_ONLINE, MTUF_ONLINE, "online", "ONLINE", NULL, NULL, NULL, + "Tape Online"}, +#endif + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, + "Set/Display tape format (SIMH, E11, TPC, P7B)"}, + {MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH", + &sim_tape_set_capac, &sim_tape_show_capac, NULL, + "Set unit n capacity to arg MB (0 = unlimited)" }, + {MTAB_XTD | MTAB_VUN, 0, NULL, "REWIND", + &mt_rew, NULL, NULL, "Rewind tape" + }, +#ifdef I7090 + {MTAB_XTD | MTAB_VDV | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan, + NULL, "Device Channel"}, +#endif + {0} +}; + +#ifdef MT_CHANNEL_ZERO +DEVICE mtz_dev = { + "MT", &mta_unit[NUM_DEVS_MT * 10], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(NUM_DEVS_MT) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; +#endif + +#if (NUM_DEVS_MT > 0) +DEVICE mta_dev = { + "MTA", mta_unit, NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +#if (NUM_DEVS_MT > 1) +DEVICE mtb_dev = { + "MTB", &mta_unit[10], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +#if (NUM_DEVS_MT > 2) +DEVICE mtc_dev = { + "MTC", &mta_unit[20], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(2) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +#if (NUM_DEVS_MT > 3) +DEVICE mtd_dev = { + "MTD", &mta_unit[30], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 36, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(3) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +#if (NUM_DEVS_MT > 4) +DEVICE mte_dev = { + "MTE", &mta_unit[40], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(4) | DEV_DIS | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +#if (NUM_DEVS_MT > 5) +DEVICE mtf_dev = { + "MTF", &mta_unit[50], NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_BUF_NUM(5) | DEV_DIS | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; +#endif +#endif +#endif +#endif +#endif +#endif + + +uint8 parity_table[64] = { + /* 0 1 2 3 4 5 6 7 */ + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000 +}; + +/* Rewind tape drive */ +t_stat +mt_rew(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + /* If drive is offline or not attached return not ready */ + if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) == 0) + return SCPE_NOATT; + /* Check if drive is ready to recieve a command */ + if ((uptr->u5 & MT_RDY) == 0) + return STOP_IOCHECK; + return sim_tape_rewind(uptr); +} + +/* Start off a mag tape command */ +uint32 mt_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + DEVICE *dptr = find_dev_from_unit(uptr); + int time = us_to_ticks(100); + int unit = dev & 017; + + unit -= mt_dib.addr & 017; /* Adjust to origin zero */ + if (unit == 10) + unit = 0; + /* Make sure valid drive number */ + if (unit > NUM_UNITS_MT || unit < 0) + return SCPE_NODEV; + uptr += unit; + /* If unit disabled return error */ + if (uptr->flags & UNIT_DIS) { + /* + fprintf(stderr, "Attempt to access disconnected unit %s%d\n", + dptr->name, unit); */ + return SCPE_NODEV; + } + + /* Check status of the drive */ + + /* Can't do nothing if controller is busy */ + if (mt_chan[chan] & MTC_BSY) { + return SCPE_BUSY; + } + /* If drive is offline or not attached return not ready */ + if ((uptr->flags & (UNIT_ATT | MTUF_ONLINE)) != + (UNIT_ATT | MTUF_ONLINE)) { + fprintf(stderr, "Attempt to access offline unit %s%d\n\r", + dptr->name, unit); + return SCPE_IOERR; + } + /* 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); + time = us_to_ticks(12000); + if ((uptr->u5 & MT_IDLE) == 0) + time = us_to_ticks(15000); + switch (cmd) { + case IO_RDS: + if (sim_tape_bot(uptr)) + time = us_to_ticks(21000); + if (mt_chan[chan] & MTC_SEL) { + uptr->u5 |= MT_RDY; + return SCPE_BUSY; + } + +#ifdef I701 + uptr->u5 |= MT_RDSB; +#else + if (dev & 020) + uptr->u5 |= MT_RDSB; + else + uptr->u5 |= MT_RDS; +#endif + chan_set_sel(chan, 0); + chan_clear_status(chan); + mt_chan[chan] = MTC_BSY | MTC_SEL | unit; + uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP); + uptr->u6 = -1; + uptr->hwmark = -1; +#if I7010 | I7080 + chan_set(chan, STA_TWAIT); +#endif + sim_debug(DEBUG_CMD, dptr, "RDS %s unit=%d %d\n", + ((uptr->u5 & MT_CMDMSK) == MT_RDS) ? "BCD" : "Binary", + unit, dev); + break; + + case IO_WRS: + if (sim_tape_bot(uptr)) + time = us_to_ticks(40000); + if (mt_chan[chan] & MTC_SEL) { + uptr->u5 |= MT_RDY; + return SCPE_BUSY; + } + if (sim_tape_wrp(uptr)) { + sim_debug(DEBUG_EXP, dptr, + "WRS %d attempted on locked tape\n", unit); + uptr->u5 |= MT_RDY; + return SCPE_IOERR; + } +#ifdef I701 + uptr->u5 |= MT_WRSB; +#else + if (dev & 020) + uptr->u5 |= MT_WRSB; + else + uptr->u5 |= MT_WRS; +#endif + time += T2_us; + uptr->u6 = 0; + uptr->hwmark = 0; + chan_set_sel(chan, 1); + chan_clear_status(chan); + mt_chan[chan] = MTC_BSY | MTC_SEL | unit; + uptr->u5 &= ~(MT_MARK | MT_EOT); +#if I7010 | I7080 + chan_set(chan, STA_TWAIT); +#endif + sim_debug(DEBUG_CMD, dptr, "WRS %s unit=%d %d\n", + ((uptr->u5 & MT_CMDMSK) == MT_WRS) ? "BCD" : "Binary", + unit, dev); + break; + + case IO_RDB: + if (mt_chan[chan] & MTC_SEL) { + uptr->u5 |= MT_RDY; + return SCPE_BUSY; + } + + uptr->u5 |= MT_RDB; + chan_set_sel(chan, 0); + chan_clear_status(chan); + mt_chan[chan] = MTC_BSY | MTC_SEL | unit; + uptr->u5 &= ~(MT_RM|MT_EOR|MT_EGAP); + uptr->u6 = -1; + uptr->hwmark = -1; +#if I7010 | I7080 + chan_set(chan, STA_TWAIT); +#endif + sim_debug(DEBUG_CMD, dptr, "RDB unit=%d %d\n", unit, dev); + break; + + case IO_WEF: + if (sim_tape_bot(uptr)) + time = us_to_ticks(40000); + uptr->u5 &= ~(MT_EOT|MT_MARK); + if (sim_tape_wrp(uptr)) { + sim_debug(DEBUG_EXP, dptr, + "WRS %d attempted on locked tape\n", unit); + uptr->u5 |= MT_RDY; + return SCPE_IOERR; + } + uptr->u5 |= MT_WEF; +#if I7010 + chan_set_sel(chan, 1); + chan_clear_status(chan); + mt_chan[chan] = MTC_BSY | MTC_SEL | unit; + chan_set(chan, STA_TWAIT); +#else + mt_chan[chan] = MTC_BSY; +#endif + sim_debug(DEBUG_CMD, dptr, "WEF unit=%d\n", unit); + break; + + case IO_BSR: + uptr->u5 &= ~(MT_MARK); + /* Check if at load point, quick return if so */ + if (sim_tape_bot(uptr)) { + sim_debug(DEBUG_CMD, dptr, "BSR unit=%d at BOT\n", unit); + uptr->u5 |= MT_RDY; + uptr->u3 = 0; + chan_set(chan, CHS_BOT); + return SCPE_OK; + } + uptr->u5 |= MT_BSR; + mt_chan[chan] = MTC_BSY; + sim_debug(DEBUG_CMD, dptr, "BSR unit=%d\n", unit); + break; + + case IO_BSF: + uptr->u5 &= ~(MT_MARK); + /* Check if at load point, quick return if so */ + if (sim_tape_bot(uptr)) { + sim_debug(DEBUG_CMD, dptr, "BSF unit=%d at BOT\n", unit); + uptr->u5 |= MT_RDY; + uptr->u3 = 0; + chan_set(chan, CHS_BOT); + return SCPE_OK; + } + uptr->u5 |= MT_BSF; + mt_chan[chan] = MTC_BSY; + sim_debug(DEBUG_CMD, dptr, "BSF unit=%d\n", unit); + break; + + case IO_SKR: + if (sim_tape_bot(uptr)) + time = us_to_ticks(21000); + uptr->u5 &= ~(MT_MARK|MT_EGAP); + uptr->u5 |= MT_SKR; + mt_chan[chan] = MTC_BSY; + sim_debug(DEBUG_CMD, dptr, "SKR unit=%d\n", unit); + break; + + case IO_ERG: + sim_debug(DEBUG_CMD, dptr, "ERG unit=%d\n", unit); +#ifdef I7080 + uptr->u5 &= ~(MT_MARK); + uptr->u5 |= MT_ERG; + mt_chan[chan] = MTC_BSY; + chan_set(chan, STA_TWAIT); + break; +#else + uptr->u5 |= MT_EGAP|MT_RDY; /* Command is quick */ + return SCPE_OK; +#endif + + case IO_REW: + uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP); + /* Check if at load point, quick return if so */ + if (sim_tape_bot(uptr)) { + sim_debug(DEBUG_CMD, dptr, "REW unit=%d at BOT\n", unit); + uptr->u5 |= MT_RDY; + uptr->u3 = 0; + return SCPE_OK; + } + time = 1000; + uptr->u5 |= MT_REW; + mt_chan[chan] = MTC_BSY; + sim_debug(DEBUG_CMD, dptr, "REW unit=%d\n", unit); + sim_cancel(uptr); + sim_activate(uptr, time); + return SCPE_OK; + + case IO_RUN: + uptr->u5 &= ~(MT_EOT|MT_MARK|MT_EGAP); + chan_clear_status(chan); + uptr->u5 |= MT_RUN; + mt_chan[chan] = MTC_BSY; + time = 1000; + sim_debug(DEBUG_CMD, dptr, "RUN unit=%d\n", unit); + sim_cancel(uptr); + sim_activate(uptr, time); + return SCPE_OK; + + case IO_SDL: + uptr->u5 |= MT_RDY; /* Command is quick */ + uptr->flags |= MTUF_LDN; + sim_debug(DEBUG_CMD, dptr, "SDN unit=%d low\n", unit); + return SCPE_OK; + + case IO_SDH: + uptr->u5 |= MT_RDY; /* Command is quick */ + uptr->flags &= ~MTUF_LDN; + sim_debug(DEBUG_CMD, dptr, "SDN unit=%d high\n", unit); + return SCPE_OK; + + case IO_DRS: + uptr->flags &= ~MTUF_ONLINE; + uptr->u5 |= MT_RDY; /* Command is quick */ + sim_debug(DEBUG_CMD, dptr, "DRS unit=%d\n", unit); + return SCPE_OK; + + case IO_TRS: + uptr->u5 |= MT_RDY; /* Get here we are ready */ + sim_debug(DEBUG_CMD, dptr, "TRS unit=%d\n", unit); + return SCPE_OK; + } + sim_cancel(uptr); + sim_activate(uptr, time); + return SCPE_OK; +} + + +#if I7090 | I704 | I701 +/* Read a word from tape, used during boot read */ +int +mt_read_buff(UNIT * uptr, int cmd, DEVICE * dptr, t_uint64 *word) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int bufnum = GET_DEV_BUF(dptr->flags); + int i; + uint8 ch; + int mode = 0; + int mark = 1; + int parity = 0; + + uptr->u5 &= ~MT_MARK; + if (cmd == MT_RDS) + mode = 0100; + + *word = 0; + for(i = CHARSPERWORD-1; i >= 0 && uptr->u6 < (int32)uptr->hwmark; i--) { + ch = mt_buffer[bufnum][uptr->u6++]; + /* Do BCD translation */ + if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { + parity = 1; + } + ch &= 077; + /* Not needed on decimal machines */ + if (mode) { + /* Map BCD to internal format */ + ch ^= (ch & 020) << 1; + if (ch == 012) + ch = 0; + if (ch == 017 && mark) { + chan_set_error(chan); /* Force CRC error. */ + ch = 0; + mark = 0; + uptr->u6++; /* Skip next character */ + i--; + } + } + if (i >= 0) + *word |= ((t_uint64) ch) << (6 * i); + } + + if (parity) { + chan_set_error(chan); /* Force redundency error */ + return 0; + } + return 1; +} +#endif + +/* Map simH errors into machine errors */ +t_stat mt_error(UNIT * uptr, int chan, t_stat r, DEVICE * dptr) +{ + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_TMK: /* tape mark */ + sim_debug(DEBUG_EXP, dptr, "MARK "); + chan_set_eof(chan); + break; + + case MTSE_WRP: /* write protected */ + case MTSE_UNATT: /* unattached */ + sim_debug(DEBUG_EXP, dptr, "ATTENTION %d ", r); + chan_set_attn(chan); + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + case MTSE_RECE: /* error in record */ + chan_set_error(chan); /* Force redundency error */ + chan_set_attn(chan); /* Set error */ + sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r); + break; + case MTSE_BOT: /* beginning of tape */ + chan_set(chan, CHS_BOT); /* Set flag */ + sim_debug(DEBUG_EXP, dptr, "BOT "); + break; + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_EOM: /* end of medium */ + uptr->u5 |= MT_EOT; + sim_debug(DEBUG_EXP, dptr, "EOT "); +#ifdef I7010 + chan_set_attn(chan); /* Set error */ +#endif + break; + } + return SCPE_OK; +} + +/* Handle processing of tape requests. */ +t_stat mt_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units) & MTC_UNIT; + int cmd = uptr->u5 & MT_CMDMSK; + int bufnum = GET_DEV_BUF(dptr->flags); + t_mtrlnt reclen; + t_stat r = SCPE_ARG; /* Force error if not set */ + uint8 ch; + int mode = 0; +#ifdef I7010 + extern uint8 astmode; +#endif + + /* Call channel proccess to make sure data is ready */ + chan_proc(); + + /* Channel has disconnected, abort current read. */ + if ((mt_chan[chan] & 037) == (MTC_SEL | unit) && + chan_stat(chan, DEV_DISCO)) { + uptr->u5 &= ~MT_CMDMSK; + reclen = uptr->hwmark; + if (cmd == MT_WRS || cmd == MT_WRSB) { + if (uptr->u6 > 0) { + uptr->u3 += GAP_LEN; + sim_debug(DEBUG_DETAIL, dptr, + "Write flush unit=%d %s Block %d chars\n", + unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen); + r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); + mt_error(uptr, chan, r, dptr); /* Record errors */ + } + } else if (cmd == MT_RDS || cmd == MT_RDSB) { + sim_debug(DEBUG_DETAIL, dptr, + "Read flush unit=%d %s Block %d chars\n", + unit, (cmd == MT_RDS) ? "BCD" : "Binary", reclen); + /* Keep moving until end of block */ + if (uptr->u6 < (int32)uptr->hwmark ) { + reclen -= uptr->u6; + uptr->u3 += reclen; + uptr->u5 |= MT_SKIP|MT_IDLE; + uptr->u6 = 0; + uptr->hwmark = 0; + chan_clear(chan, DEV_DISCO | DEV_WEOR); + sim_activate(uptr, reclen * T1_us); + return SCPE_OK; + } else { +#ifndef I7010 + if (uptr->u5 & MT_MARK) { + /* We hit tapemark, Back up so next read hits it */ + /* Or write starts just before it */ + /* This is due to SIMH returning mark after read */ + (void) sim_tape_sprecr(uptr, &reclen); + uptr->u5 &= ~MT_MARK; + uptr->u3 -= GAP_LEN + reclen; + } +#endif + } + } + /* Allow time for tape to be restarted, before stop */ + sim_activate(uptr, us_to_ticks(500)); + uptr->u6 = 0; + uptr->hwmark = 0; + sim_debug(DEBUG_CHAN, dptr, "Disconnect unit=%d\n", unit); + uptr->u5 |= MT_IDLE|MT_RDY; + mt_chan[chan] = 0; + chan_clear(chan, DEV_DISCO | DEV_WEOR | DEV_SEL); +#if I7010 | I7080 + chan_clear(chan, STA_TWAIT); +#endif + return SCPE_OK; + } + + uptr->u5 &= ~MT_IDLE; + switch (cmd) { + case 0: /* No command, stop tape */ + uptr->u5 |= MT_RDY; /* Ready since command is done */ + sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit); + return SCPE_OK; + + case MT_SKIP: /* Record skip done, enable tape drive */ + uptr->u5 &= ~MT_CMDMSK; + uptr->u5 |= MT_RDY | MT_IDLE; +#if I7090 | I704 | I701 + chan_clear(chan, DEV_SEL); +#else + chan_clear(chan, DEV_SEL|STA_TWAIT); +#endif + mt_chan[chan] = 0; + sim_debug(DEBUG_DETAIL, dptr, "Skip unit=%d\n", unit); + /* Allow time for tape to be restarted, before stop */ + sim_activate(uptr, us_to_ticks(500)); + return SCPE_OK; + + case MT_RDS: + mode = 0100; + /* Fall through */ + + case MT_RDSB: + /* Post EOR */ + if (uptr->u5 & MT_EOR) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post EOR\n", unit); + chan_set(chan, DEV_REOR); + uptr->u5 &= ~ MT_EOR; + sim_activate(uptr, T1_us); + return SCPE_OK; + } + + /* If tape mark pending, return it */ + if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit); + uptr->u5 &= ~(MT_CMDMSK|MT_MARK); +#ifdef I7010 + if (astmode) { + ch = mode?017:054; + chan_write_char(chan, &ch, DEV_REOR); + if (mode) { + chan_clear(chan, STA_TWAIT); + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + } +#endif + chan_set_attn(chan); + sim_activate(uptr, us_to_ticks(100)); + return mt_error(uptr, chan, MTSE_TMK, dptr); + } + /* If at end of record, fill buffer */ + if (uptr->u6 == uptr->hwmark) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit); + uptr->u3 += GAP_LEN; + if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen, + BUFFSIZE)) != MTSE_OK) { + if (r == MTSE_TMK && uptr->u6 != -1) { + sim_debug(DEBUG_DETAIL, dptr, "pend TM\n"); + uptr->u5 |= MT_MARK; + r = MTSE_OK; + } else { + sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r); + uptr->u5 &= ~MT_CMDMSK; +#ifdef I7010 + /* Translate TM characters for 7010 */ + if (r == MTSE_TMK && astmode) { + sim_debug(DEBUG_DETAIL, dptr, "Read TM "); + ch = mode?017:054; + chan_write_char(chan, &ch, 0); + chan_set_attn(chan); + chan_set(chan, DEV_REOR); + chan_clear(chan, STA_TWAIT); + if (mode) { + sim_activate(uptr, T1_us); + return SCPE_OK; + } + chan_set_error(chan); + } +#else + chan_set_attn(chan); +#endif + } + sim_activate(uptr, T1_us); + return mt_error(uptr, chan, r, dptr); + } + uptr->u6 = 0; + uptr->hwmark = reclen; + chan_clear(chan, CHS_EOF|CHS_ERR); + sim_debug(DEBUG_DETAIL, dptr, "%s Block %d chars\n", + (cmd == MT_RDS) ? "BCD" : "Binary", reclen); +#ifdef I7010 + if (mode && mt_buffer[bufnum][0] == 017) + chan_set_eof(chan); +#endif + + } + + ch = mt_buffer[bufnum][uptr->u6++]; + uptr->u3++; + /* Do BCD translation */ + if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { +#ifdef I7010 + if (astmode) + ch = 054; +#else + chan_set_attn(chan); +#endif + chan_set_error(chan); + } +#if I7090 | I704 | I701 + /* Not needed on decimal machines */ + if (mode) { + /* Map BCD to internal format */ + ch ^= (ch & 020) << 1; + if (ch == 012) + ch = 0; + if (ch == 017) { + chan_set_error(chan); /* Force CRC error. */ + if ((uptr->u5 & MT_RM) == 0) { + ch = 0; + uptr->u5 |= MT_RM; + mt_buffer[bufnum][uptr->u6] = 0; + } + } + } +#endif +#ifdef I7010 + if (mode) { + if (ch == 0120) + ch = 0; + } +#endif + ch &= 077; + + /* Convert one word. */ + switch (chan_write_char(chan, &ch, 0)) { + case END_RECORD: + sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit); + /* If not read whole record, skip till end */ + uptr->u5 |= MT_EOR; + if (uptr->u6 < (int32)uptr->hwmark) { + sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); + uptr->u3 += (uptr->hwmark - uptr->u6); + uptr->u6 = uptr->hwmark; /* Force read next record */ + } + sim_activate(uptr, T1_us); + break; + + case DATA_OK: + sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n", + unit, uptr->u6, ch); + if (uptr->u6 >= (int32)uptr->hwmark) /* In IRG */ + uptr->u5 |= MT_EOR; + sim_activate(uptr, T1_us); + break; + + case TIME_ERROR: + sim_debug(DEBUG_DATA, dptr, "Read unit=%d timeout\n", unit); + uptr->u3 += (uptr->hwmark - uptr->u6); + uptr->u5 &= ~MT_CMDMSK; + uptr->u5 |= MT_SKIP; + sim_activate(uptr, ((uptr->hwmark - uptr->u6) * T1_us) + T2_us); + uptr->u6 = uptr->hwmark; /* Force read next record */ + break; + } + return SCPE_OK; + + + /* Check mode */ + case MT_WRS: + mode = 0100; + /* fall through */ + case MT_WRSB: + if (uptr->u5 & MT_EGAP) { + sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit); + uptr->u5 &= ~MT_EGAP; + r = sim_tape_wrgap(uptr, 35); + sim_activate(uptr, 10*T3_us); + return SCPE_OK; + } + + switch (chan_read_char(chan, &ch, + (uptr->u6 > BUFFSIZE) ? DEV_WEOR : 0)) { + case TIME_ERROR: +#if I7090 | I701 | I704 + /* If no data was written, simulate a write gap */ + if (uptr->u6 == 0) { + r = sim_tape_wrgap(uptr, 35); + if (r != MTSE_OK) { + mt_error(uptr, chan, r, dptr); /* Record errors */ + return SCPE_OK; + } + } +#endif + chan_set_attn(chan); + /* fall through */ + + case END_RECORD: + if (uptr->u6 > 0) { /* Only if data in record */ + reclen = uptr->hwmark; + sim_debug(DEBUG_DETAIL, dptr, + "Write unit=%d %s Block %d chars\n", + unit, (cmd == MT_WRS) ? "BCD" : "Binary", reclen); + r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); + uptr->u3 += GAP_LEN; + uptr->u6 = 0; + uptr->hwmark = 0; + mt_error(uptr, chan, r, dptr); /* Record errors */ + } + sim_activate(uptr, T2_us); + return SCPE_OK; + case DATA_OK: + /* Copy data to buffer */ + ch &= 077; +#if I7090 | I701 | I704 + /* Not needed on decimal machines */ + if (mode) { + /* Do BCD translation */ + ch ^= (ch & 020) << 1; + if (ch == 0) + ch = 012; + } +#endif + ch |= mode ^ parity_table[ch] ^ 0100; + mt_buffer[bufnum][uptr->u6++] = ch; + uptr->u3++; + sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n", + unit, uptr->u6, ch); + uptr->hwmark = uptr->u6; + break; + } + sim_activate(uptr, T1_us); + return SCPE_OK; + + case MT_RDB: + /* If tape mark pending, return it */ + if (chan_test(chan, DEV_FULL) == 0 && uptr->u5 & MT_MARK) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d post ", unit); + uptr->u5 &= ~(MT_CMDMSK|MT_MARK); + mt_chan[chan] &= MTC_BSY; + chan_clear(chan, DEV_SEL); + sim_activate(uptr, us_to_ticks(100)); + return mt_error(uptr, chan, MTSE_TMK, dptr); + } + /* If at end of record, fill buffer */ + if (uptr->u6 == uptr->hwmark) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit); + if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen, + BUFFSIZE)) != MTSE_OK) { + uptr->u3 -= GAP_LEN; + sim_activate(uptr, T2_us); + if (r == MTSE_TMK && uptr->u6 != -1) { + sim_debug(DEBUG_DETAIL, dptr, "pend TM\n"); + uptr->u5 |= MT_MARK; + r = MTSE_OK; + } else { + sim_debug(DEBUG_DETAIL, dptr, "error=%d\n", r); + uptr->u5 &= ~MT_CMDMSK; + chan_set_attn(chan); + chan_clear(chan, DEV_SEL); + mt_chan[chan] &= MTC_BSY; + } + return mt_error(uptr, chan, r, dptr); + } + uptr->u6 = 0; + uptr->hwmark = reclen; + chan_clear(chan, CHS_EOF|CHS_ERR); + sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen); + } + + ch = mt_buffer[bufnum][uptr->u6++]; + uptr->u3--; + /* Do BCD translation */ + if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { + chan_set_error(chan); + chan_set_attn(chan); + } + ch &= 077; + + /* Convert one word. */ + switch (chan_write_char(chan, &ch, + (uptr->u6 >= (int32)uptr->hwmark) ? DEV_REOR : 0)) { + case END_RECORD: + sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n", unit); + if (uptr->u6 >= (int32)uptr->hwmark) { + uptr->u5 &= ~MT_CMDMSK; + uptr->u5 |= MT_SKIP; + uptr->u3 -= (uptr->hwmark-uptr->u6); + sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); + chan_set(chan, DEV_REOR); + uptr->u6 = uptr->hwmark; /* Force read next record */ + break; + } + /* fall through */ + + case DATA_OK: + sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n", + unit, uptr->u6, ch); + if (uptr->u6 >= (int32)uptr->hwmark) { /* In IRG */ + uptr->u3 -= (uptr->hwmark-uptr->u6); + sim_activate(uptr, T2_us); + } else + sim_activate(uptr, T1_us); + break; + + case TIME_ERROR: + uptr->u5 &= ~MT_CMDMSK; + uptr->u5 |= MT_SKIP; + uptr->u3 -= (uptr->hwmark-uptr->u6); + sim_activate(uptr, (uptr->hwmark-uptr->u6) * T1_us); + uptr->u6 = uptr->hwmark; /* Force read next record */ + break; + } + return SCPE_OK; + + case MT_WEF: + if (uptr->u5 & MT_EGAP) { + sim_debug(DEBUG_DETAIL, dptr, "Write extended Gap unit=%d\n", unit); + uptr->u5 &= ~MT_EGAP; + r = sim_tape_wrgap(uptr, 35); + sim_activate(uptr, 10*T3_us); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit); + uptr->u5 &= ~(MT_CMDMSK|MT_MARK); + uptr->u5 |= (MT_RDY); + r = sim_tape_wrtmk(uptr); + uptr->u3 += GAP_LEN; + mt_chan[chan] &= ~MTC_BSY; + sim_activate(uptr, T2_us); +#if I7010 | I7080 + chan_set(chan, DEV_REOR); +#endif + break; + + case MT_BSR: + sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit); + /* Clear tape mark, command, idle since we will need to change dir */ + uptr->u5 &= ~(MT_CMDMSK | MT_EOT | MT_RDY); + r = sim_tape_sprecr(uptr, &reclen); + if (r != MTSE_BOT) + uptr->u3 -= GAP_LEN; + mt_chan[chan] &= ~MTC_BSY; + if (r == MTSE_TMK) { +#ifdef I7080 + chan_set_eof(chan); +#else + /* We don't set EOF on BSR */ +#endif + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, T2_us); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen); + uptr->u3 -= reclen; + sim_activate(uptr, T2_us + (reclen * T1_us)); + return SCPE_OK; + + case MT_BSF: + uptr->u5 &= ~(MT_IDLE | MT_RDY | MT_EOT); + r = sim_tape_sprecr(uptr, &reclen); + if (r != MTSE_BOT) + uptr->u3 -= GAP_LEN; + /* If we hit mark or end of tape */ + if (r == MTSE_TMK || r == MTSE_BOT) { + sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d\n", unit); + uptr->u5 &= ~MT_CMDMSK; + mt_chan[chan] &= ~MTC_BSY; + sim_activate(uptr, T2_us); + } else { + uptr->u3 -= reclen; + sim_activate(uptr, T2_us + (reclen * T1_us)); + } + return SCPE_OK; + + case MT_SKR: + sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit); + /* Clear tape mark, command, idle since we will need to change dir */ + uptr->u5 &= ~(MT_CMDMSK | MT_EOT); + uptr->u5 |= (MT_RDY | MT_IDLE); + r = sim_tape_sprecf(uptr, &reclen); + uptr->u3 += GAP_LEN; + mt_chan[chan] &= ~MTC_BSY; +#if I7010 | I7080 + chan_clear(chan, STA_TWAIT); +#endif +#ifdef I7010 + chan_set(chan, STA_PEND); +#else + /* We are like read that transfers nothing */ + chan_set(chan, DEV_REOR); +#endif + /* We don't set EOF on SKR */ + if (r == MTSE_TMK) { + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, T1_us); + return SCPE_OK; +#ifdef I7010 + } else if (r == MTSE_EOM) { + chan_set(chan, STA_PEND); +#endif + } + sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen); + uptr->u3 += reclen; + sim_activate(uptr, (reclen * T1_us)); + break; + + case MT_ERG: + sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d\n", unit); + uptr->u5 &= ~(MT_CMDMSK|MT_MARK); + uptr->u5 |= (MT_RDY | MT_IDLE); +#if I7010 | I7080 + chan_clear(chan, STA_TWAIT); +#endif + r = sim_tape_wrgap(uptr, 35); + uptr->u3 += GAP_LEN; + mt_chan[chan] &= ~MTC_BSY; + sim_activate(uptr, 10*T3_us); + break; + + case MT_REW: + sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d %d %d\n", unit, uptr->u3, + uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200); + uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY); + if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { + uptr->u5 |= MT_HREW; + sim_activate(uptr, us_to_ticks(5000000)); + } else { + uptr->u5 |= MT_LREW; + sim_activate(uptr, 300); + } + mt_chan[chan] &= ~MTC_BSY; + break; + + case MT_RUN: + sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit); + uptr->u5 &= ~(MT_CMDMSK | MT_IDLE | MT_RDY); + uptr->u5 |= MT_UNLOAD; + if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { + uptr->u5 |= MT_HREW; + sim_activate(uptr, us_to_ticks(5000000)); + } else { + uptr->u5 |= MT_LREW; + sim_activate(uptr, 300); + } + mt_chan[chan] &= ~MTC_BSY; + return SCPE_OK; + + case MT_HREW: + sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d HS\n", unit); + if ((uptr->u3 / ((uptr->flags & MTUF_LDN) ? 200 : 555) / 1200) > 2) { + uptr->u3 -= (uptr->flags & MTUF_LDN) ? 1666 :4625; + sim_activate(uptr, us_to_ticks(16000)); + } else { + uptr->u5 &= ~(MT_CMDMSK); + uptr->u5 |= MT_LREW; + sim_activate(uptr, us_to_ticks(5000000)); + } + return SCPE_OK; + + case MT_LREW: + sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d LS\n", unit); + if (uptr->u3 > 0) { + uptr->u3 -= (uptr->flags & MTUF_LDN) ? 373 :1036; + sim_activate(uptr, us_to_ticks(16000)); + return SCPE_OK; + } else { + if(uptr->u5 & MT_UNLOAD) + r = sim_tape_detach(uptr); + else + r = sim_tape_rewind(uptr); + uptr->u5 &= ~(MT_CMDMSK|MT_UNLOAD); + uptr->u5 |= MT_RDY; + uptr->u3 = 0; + } + break; + } + return mt_error(uptr, chan, r, dptr); +} + +/* Boot from given device */ +t_stat +mt_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + uint16 dev = unit_num + 020 + mt_dib.addr; +#if I7090 | I704 | I701 + t_mtrlnt reclen; + t_stat r; +#endif + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + /* Start a read. */ + if (mt_cmd(dptr->units, IO_RDS, dev) != SCPE_OK) + return STOP_IONRDY; + +#if I7090 | I704 | I701 + r = sim_tape_rdrecf(uptr, &mt_buffer[GET_DEV_BUF(dptr->flags)][0], &reclen, + BUFFSIZE); + if (r != SCPE_OK) + return r; + uptr->u6 = 0; + uptr->hwmark = reclen; + + /* Copy first three records. */ + mt_read_buff(uptr, MT_RDSB, dptr, &M[0]); + mt_read_buff(uptr, MT_RDSB, dptr, &M[1]); + if (UNIT_G_CHAN(uptr->flags) != 0) + mt_read_buff(uptr, MT_RDSB, dptr, &M[2]); + /* Make sure channel is set to start reading rest. */ +#endif + return chan_boot(unit_num, dptr); +} + +void +mt_ini(UNIT * uptr, t_bool f) +{ + int chan = UNIT_G_CHAN(uptr->flags); + + if (uptr->flags & UNIT_ATT) + uptr->u5 = MT_RDY; + else + uptr->u5 = 0; + uptr->u3 = 0; + mt_chan[chan] = 0; +} + +t_stat +mt_reset(DEVICE * dptr) +{ + UNIT *uptr = dptr->units; + uint32 i; + for (i = 0; i < dptr->numunits; i++) { + sim_tape_set_dens (uptr, + ((uptr->flags & MTUF_LDN) ? MT_DENS_200 : MT_DENS_556), + NULL, NULL); + uptr++; + } + return SCPE_OK; +} + +t_stat +mt_tape_density(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + return SCPE_OK; +} + +t_stat +mt_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u3 = 0; + uptr->u5 |= MT_RDY; + uptr->flags |= MTUF_ONLINE; + uptr->dynflags = MT_200_VALID | MT_556_VALID | + (((uptr->flags & MTUF_LDN) ? MT_556_VALID : MT_200_VALID) < UNIT_V_DF_TAPE); + return SCPE_OK; +} + +t_stat +mt_detach(UNIT * uptr) +{ + uptr->u3 = 0; + uptr->u5 = 0; + uptr->flags &= ~MTUF_ONLINE; + return sim_tape_detach(uptr); +} + +t_stat +mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", mt_description(dptr)); + fprintf (st, "The magnetic tape controller assumes that all tapes are 7 track\n"); + fprintf (st, "with valid parity. Tapes are assumed to be 555.5 characters per\n"); + fprintf (st, "inch. To simulate a standard 2400foot tape, do:\n\n"); + fprintf (st, " sim> SET %s LENGTH 15\n\n", dptr->name); + fprintf (st, "The mag tape drives support the BOOT command\n\n"); + help_set_chan_type(st, dptr, "Mag tape"); + sim_tape_attach_help (st, dptr, uptr, flag, cptr); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +mt_description(DEVICE *dptr) +{ + return "IBM 729 Magnetic tape unit"; +} + +#endif diff --git a/I7000/i7010_chan.c b/I7000/i7010_chan.c new file mode 100644 index 00000000..763c8546 --- /dev/null +++ b/I7000/i7010_chan.c @@ -0,0 +1,727 @@ +/* i7010_chan.c: IBM 7010 Channel simulator + + 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. + + channel + + The system state for the IBM 7010 channel is: + There is only one type of channel on 7010, and it will talk to + All type devices. + + Common registers to all but PIO channels. + ADDR<0:16> Address of next command. + CMD<0:6> Channel command. + ASM<0:32> Assembled data from devices. + + Simulation registers to handle device handshake. + STATUS<0:16> Simulated register for basic channel status. + SENSE<0:16> Additional flags for 7907 channels. +*/ + +#include "i7010_defs.h" + +extern UNIT cpu_unit; +extern uint8 chan_seek_done[NUM_CHAN]; /* Channel seek finished */ + +#define CHAN_DEF UNIT_DISABLE|CHAN_SET + +t_stat set_urec(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat get_urec(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +t_stat chan_reset(DEVICE * dptr); +t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *chan_description (DEVICE *dptr); + + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +uint32 caddr[NUM_CHAN]; /* Channel memory address */ +uint8 bcnt[NUM_CHAN]; /* Channel character count */ +uint8 cmd[NUM_CHAN]; /* Current command */ +uint16 irqdev[NUM_CHAN]; /* Device to generate interupts + for channel */ +uint32 chunit[NUM_CHAN]; /* Channel unit */ +uint32 assembly[NUM_CHAN]; /* Assembly register */ +uint32 chan_flags[NUM_CHAN]; /* Unit status */ +extern uint8 chan_io_status[NUM_CHAN]; +extern uint8 inquiry; +extern uint8 urec_irq[NUM_CHAN]; + +#define CHAN_LOAD 0001 /* Channel in load mode */ +#define CHAN_NOREC 0002 /* Don't stop at record */ +#define CHAN_WM 0004 /* Sent word mark char */ +#define CHAN_6BIT 0010 /* Send 6-8 bit command */ +#define CHAN_DSK_SEEK 0020 /* Seek Command */ +#define CHAN_DSK_DATA 0040 /* Command needs data */ +#define CHAN_DSK_RD 0100 /* Command is read command */ +#define CHAN_OVLP 0200 /* Channel ran overlaped */ + +const char *chan_type_name[] = { + "Polled", "Unit Record", "7010", "7010", "7010"}; + + +/* Map commands to channel commands */ +/* Commands are reversed to be way they are sent out */ +uint8 disk_cmdmap[16] = { 0xff, 0x82, 0x84, 0x86, 0x00, 0x89, 0x88, 0x83, + 0x87, 0x04, 0x80, 0xff, 0x85, 0xff, 0xff, 0xff}; + +UNIT chan_unit[] = { + {UDATA(NULL, CHAN_SET|UNIT_DIS, 0)}, /* Place holder channel */ + {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(1),0)}, + {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(2),0)}, + {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(3),0)}, + {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_7010)|UNIT_S_CHAN(4),0)}, +}; + +REG chan_reg[] = { + {BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT}, + {NULL} +}; + +MTAB chan_mod[] = { + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7010), "7010", NULL, NULL,NULL,NULL}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "UREC", "UREC", &set_urec, &get_urec, + NULL}, + {MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL}, + {0} +}; + +/* Simulator debug controls */ +DEBTAB chn_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {"CH1", 0x0100 << 1}, + {"CH2", 0x0100 << 2}, + {"CH3", 0x0100 << 3}, + {"CH4", 0x0100 << 4}, + {0, 0} +}; + +DEVICE chan_dev = { + "CH", chan_unit, chan_reg, chan_mod, + NUM_CHAN, 10, 18, 1, 8, 8, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, chn_debug, + NULL, NULL, &chan_help, NULL, NULL, &chan_description +}; + +struct urec_t { + uint16 addr; + const char *name; +} urec_devs[] = { + {0100, "CR"}, + {0200, "LP"}, + {0400, "CP"}, + {0000, "NONE"}, + {0000, NULL} +}; + + +/* Sets the device that will interrupt on the channel. */ +t_stat +set_urec(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int chan; + int i; + + if (cptr == NULL) + return SCPE_IERR; + if (uptr == NULL) + return SCPE_IERR; + + chan = UNIT_G_CHAN(uptr->flags); + for(i = 0; urec_devs[i].name != NULL; i++) + if (strcmp(cptr, urec_devs[i].name) == 0) + break; + if (urec_devs[i].name == NULL) + return SCPE_ARG; + + irqdev[chan] = urec_devs[i].addr; + return SCPE_OK; +} + +t_stat +get_urec(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + int chan; + int i; + + if (uptr == NULL) + return SCPE_IERR; + chan = UNIT_G_CHAN(uptr->flags); + if (irqdev[chan] == 0) { + fprintf(st, "UREC=NONE"); + return SCPE_OK; + } + for(i = 0; urec_devs[i].name != NULL; i++) { + if (urec_devs[i].addr == irqdev[chan]) { + fprintf(st, "UREC=%s", urec_devs[i].name); + return SCPE_OK; + } + } + fprintf(st, "UREC=%o", irqdev[chan]); + return SCPE_OK; +} + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + chan_flags[i] = 0; + chunit[i] = 0; + caddr[i] = 0; + cmd[i] = 0; + bcnt[i] = 0; + } + return chan_set_devs(dptr); +} + +/* Channel selector characters */ +uint8 chan_char[NUM_CHAN] = {0, CHR_RPARN, CHR_LPARN, CHR_QUEST, CHR_EXPL}; + +/* Boot from given device */ +t_stat +chan_boot(int32 unit_num, DEVICE * dptr) +{ + /* Set IAR = 1 (done by reset), channel to read one + record to location 1 */ + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags); + extern int chwait; + + chwait = chan; /* Force wait for channel */ + /* Set up channel to load into location 1 */ + caddr[chan] = 1; + assembly[chan] = 0; + cmd[chan] = CHAN_NOREC|CHAN_LOAD; + chunit[chan] = unit_num; + chan_flags[chan] |= STA_ACTIVE; + return SCPE_OK; +} + +t_stat +chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) { + DEVICE **dptr; + DIB *dibp; + uint32 j; + UNIT *uptr; + + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int r; + + dibp = (DIB *) (*dptr)->ctxt; + /* If no DIB, not channel device */ + if (dibp == 0) + continue; + uptr = (*dptr)->units; + /* If this is a 7907 device, check it */ + if (dibp->ctype & CH_TYP_79XX) { + for (j = 0; j < (*dptr)->numunits; j++, uptr++) { + if (UNIT_G_CHAN(uptr->flags) == chan && + (UNIT_SELECT & uptr->flags) == 0 && + (dibp->addr & dibp->mask) == (dev & dibp->mask)) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + } else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) { + if (dibp->upc == 1) { + for (j = 0; j < (*dptr)->numunits; j++) { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + uptr++; + } + } else { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + } + } + return SCPE_NODEV; +} + +/* Execute the next channel instruction. */ +void +chan_proc() +{ + int chan; + int cmask; + + /* Scan channels looking for work */ + for (chan = 0; chan < NUM_CHAN; chan++) { + + /* Skip if channel is disabled */ + if (chan_unit[chan].flags & UNIT_DIS) + continue; + + cmask = 0x0100 << chan; + /* If channel is disconnecting, do nothing */ + if (chan_flags[chan] & DEV_DISCO) + continue; + + if (chan_flags[chan] & CHS_EOF) { + chan_io_status[chan] |= IO_CHS_COND; + chan_flags[chan] &= ~CHS_EOF; + } + + if (chan_flags[chan] & CHS_ERR) { + chan_io_status[chan] |= IO_CHS_CHECK; + chan_flags[chan] &= ~CHS_ERR; + } + + if (cmd[chan] & CHAN_DSK_DATA) { + if (chan_flags[chan] & DEV_REOR) { + /* Find end of command */ + while(MEM_ADDR_OK(caddr[chan]) && M[caddr[chan]] != (WM|077)) { + + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CHAN, &chan_dev, "%02o,", M[caddr[chan]]); + caddr[chan]++; + } + caddr[chan]++; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CHAN, &chan_dev, "chan %d fin\n", chan); + /* Configure channel for data transfer */ + cmd[chan] &= ~CHAN_DSK_DATA; + chan_flags[chan] |= (chan_flags[chan]& + (CTL_PREAD|CTL_PWRITE))>>2; + chan_flags[chan] &= ~(DEV_REOR|CTL_PREAD|CTL_PWRITE|CTL_CNTL); + /* If no select, all done */ + if ((chan_flags[chan] & DEV_SEL) == 0) + chan_flags[chan] &= ~(CTL_READ|CTL_WRITE); + /* Set direction if reading */ + if (chan_flags[chan] & CTL_READ) + chan_flags[chan] |= DEV_WRITE; + /* Check if we should finish now */ + if ((chan_flags[chan] & (CTL_READ|CTL_WRITE)) == 0 + || chan_flags[chan] & (SNS_UEND|CTL_END)) { + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_WEOR|DEV_DISCO; + if (cmd[chan] & CHAN_DSK_SEEK) + chan_flags[chan] &= ~(CTL_END); + else + chan_flags[chan] &= ~(STA_ACTIVE|SNS_UEND|CTL_END); + chan_io_status[chan] |= IO_CHS_DONE; + } + continue; + } + } + + if (cmd[chan] & CHAN_DSK_SEEK) { + if (chan_seek_done[chan] || chan_flags[chan] & SNS_UEND) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CHAN, &chan_dev, "chan %d seek done\n", chan); + chan_flags[chan] &= ~(STA_ACTIVE|SNS_UEND); + cmd[chan] &= ~CHAN_DSK_SEEK; + } + continue; + } + + if ((chan_flags[chan] & (CTL_READ|CTL_WRITE)) && + (chan_flags[chan] & (CTL_END|SNS_UEND))) { + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_WEOR|DEV_DISCO; + chan_flags[chan] &= ~(STA_ACTIVE|SNS_UEND|CTL_END|CTL_READ + |CTL_WRITE); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CHAN, &chan_dev, "chan %d end\n", chan); + cmd[chan] &= ~CHAN_DSK_SEEK; + chan_io_status[chan] |= IO_CHS_DONE; + } + + /* If device put up EOR, terminate transfer. */ + if (chan_flags[chan] & DEV_REOR) { + if (chan_flags[chan] & DEV_WRITE) { + if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == (CHAN_WM|CHAN_LOAD)) + M[caddr[chan]++] = 035; + caddr[chan]++; + } else { + if ((cmd[chan] & CHAN_NOREC) == 0 && + (chan_flags[chan] & STA_WAIT) == 0) { + if (MEM_ADDR_OK(caddr[chan])) { + if (M[caddr[chan]++] != (WM|077)) { + if (MEM_ADDR_OK(caddr[chan])) { + chan_io_status[chan] |= IO_CHS_WRL; + if (!MEM_ADDR_OK(caddr[chan]+1)) { + caddr[chan]++; + } + } + } + } else { + chan_io_status[chan] |= IO_CHS_WRL; + } + } + if ((cmd[chan] & CHAN_NOREC) && MEM_ADDR_OK(caddr[chan])) { + chan_io_status[chan] |= IO_CHS_WRL; + if (!MEM_ADDR_OK(caddr[chan]+1)) { + chan_io_status[chan] &= ~IO_CHS_WRL; + } + caddr[chan]++; + } + } + chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT|DEV_WRITE|DEV_REOR); + chan_io_status[chan] |= IO_CHS_DONE; + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR %d %o\n", chan, + caddr[chan], chan_io_status[chan]); + continue; + } + + if (((chan_flags[chan] & (DEV_SEL|STA_ACTIVE)) == STA_ACTIVE) && + (chan_flags[chan] & (CTL_CNTL|CTL_PREAD|CTL_PWRITE|CTL_READ| + CTL_WRITE|CTL_SNS)) == 0) { + chan_flags[chan] &= ~STA_ACTIVE; + } + + /* If device requested attention, abort current command */ + if (chan_flags[chan] & CHS_ATTN) { + chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT); + chan_io_status[chan] |= IO_CHS_DONE|IO_CHS_COND; + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn %o\n", + chan, chan_io_status[chan]); + continue; + } + } +} + +void chan_set_attn_urec(int chan, uint16 addr) { + if (irqdev[chan] == addr) + urec_irq[chan] = 1; +} + +void chan_set_attn_inq(int chan) { + inquiry = 1; +} + +void chan_clear_attn_inq(int chan) { + inquiry = 0; +} + + +/* Issue a command to a channel */ +int +chan_cmd(uint16 dev, uint16 dcmd, uint32 addr) +{ + uint32 chan; + t_stat r; + + /* Find device on given channel and give it the command */ + chan = (dev >> 12) & 0x7; + /* If no channel device, quick exit */ + if (chan_unit[chan].flags & UNIT_DIS) + return SCPE_IOERR; + /* Unit is busy doing something, wait */ + if (chan_flags[chan] & (DEV_SEL|DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE)) + return SCPE_BUSY; + /* Ok, try and find the unit */ + caddr[chan] = addr; + assembly[chan] = 0; + cmd[chan] = 0; + if (dcmd & 0100) /* Mod $ or X */ + cmd[chan] |= CHAN_NOREC; + if (dcmd & 0200) /* Opcode L */ + cmd[chan] |= CHAN_LOAD; + else + cmd[chan] |= CHAN_WM; /* Force first char to have word mark set */ + dcmd = (dcmd >> 8) & 0x7f; + chunit[chan] = dev; + chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE|SNS_UEND|CTL_WRITE + |CTL_SNS|STA_PEND); + /* Handle disk device special */ + if ((dsk_dib.mask & dev) == (dsk_dib.addr & dsk_dib.mask)) { + uint16 dsk_cmd = 0; + dsk_cmd = disk_cmdmap[dev&017]; + /* Set up channel if command ok */ + if (dsk_cmd == 0xFF || dev & 060) { + /* Set io error and abort */ + return SCPE_IOERR; + } + if (cmd[chan] & CHAN_LOAD) { + cmd[chan] &= ~CHAN_LOAD; + dsk_cmd = 0x100; + } else { + cmd[chan] |= CHAN_6BIT; + } + /* Try to start drive */ + r = chan_issue_cmd(chan, dsk_cmd, dev); + if (r != SCPE_OK) + return r; + chan_flags[chan] |= CTL_CNTL; + if (dcmd == IO_RDS) + chan_flags[chan] |= CTL_PREAD; + if (dcmd == IO_WRS) + chan_flags[chan] |= CTL_PWRITE; + if (dcmd == IO_TRS) + chan_flags[chan] |= CTL_SNS; + cmd[chan] |= CHAN_DSK_DATA; + if ((dsk_cmd & 0xff) == 0x80 && cmd[chan] & CHAN_OVLP) { + cmd[chan] |= CHAN_DSK_SEEK; + chan_seek_done[chan] = 0; + } + chan_flags[chan] &= ~DEV_REOR; /* Clear in case still set */ + chan_flags[chan] |= STA_ACTIVE; + return r; + } + if ((com_dib.mask & dev) == (com_dib.addr & com_dib.mask)) { + switch(dcmd) { + case IO_RDS: chan_flags[chan] |= CTL_READ; break; + case IO_WRS: chan_flags[chan] |= CTL_WRITE; break; + case IO_TRS: chan_flags[chan] |= CTL_SNS; break; + case IO_CTL: chan_flags[chan] |= CTL_CNTL; break; + } + if ((dev & 077) != 1) + cmd[chan] |= CHAN_6BIT; + r = chan_issue_cmd(chan, dcmd, dev); + if (r == SCPE_OK) + chan_flags[chan] |= STA_ACTIVE; + return r; + } + + r = chan_issue_cmd(chan, dcmd, dev); + /* Activate channel if select raised */ + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= STA_ACTIVE; + } + return r; +} + +/* + * Write a word to the assembly register. + */ +int +chan_write(int chan, t_uint64 * data, int flags) +{ + /* Not implimented on this machine */ + return TIME_ERROR; +} + +/* + * Read next word from assembly register. + */ +int +chan_read(int chan, t_uint64 * data, int flags) +{ + /* Not implimented on this machine */ + return TIME_ERROR; +} + +/* + * Write a char to the assembly register. + */ +int +chan_write_char(int chan, uint8 * data, int flags) +{ + uint8 ch = *data; + + sim_debug(DEBUG_DATA, &chan_dev, "chan %d char %o %d %o %o\n", chan, + *data, caddr[chan], chan_io_status[chan], flags); + + if (chan_flags[chan] & STA_WAIT) { + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d setWR %d %o\n", chan, + caddr[chan], chan_io_status[chan]); + chan_io_status[chan] |= IO_CHS_WRL; + return END_RECORD; + } + + /* Check if end of data */ + if ((chan_flags[chan] & STA_WAIT) == 0 && (cmd[chan] & CHAN_NOREC) == 0 && + M[caddr[chan]] == (WM|077)) { + chan_flags[chan] |= STA_WAIT; /* Saw group mark */ + chan_io_status[chan] |= IO_CHS_WRL; + caddr[chan]++; + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d GEor %d %o\n", chan, + caddr[chan], chan_io_status[chan]); + return END_RECORD; + } + + /* If over size of memory, terminate */ + if (!MEM_ADDR_OK(caddr[chan])) { + chan_flags[chan] |= DEV_REOR; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_io_status[chan] |= IO_CHS_DONE; + caddr[chan]++; + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d past mem %d %o\n", chan, + caddr[chan], chan_io_status[chan]); + chan_flags[chan] &= ~(DEV_WRITE|STA_ACTIVE); + return DATA_OK; + } + + /* If we are in load mode and see word mark, save it */ + if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == CHAN_LOAD && ch == 035) + cmd[chan] |= CHAN_WM; + else { + if (cmd[chan] & CHAN_6BIT) + ch &= 077; + if (cmd[chan] & CHAN_WM && ch != 035) + ch |= WM; + cmd[chan] &= ~CHAN_WM; + if ((cmd[chan] & CHAN_LOAD) == 0) + ch |= M[caddr[chan]] & WM; + if ((chan_flags[chan] & DEV_REOR) == 0) + M[caddr[chan]] = ch; + caddr[chan]++; + } + + + /* If device gave us an end, terminate transfer */ + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_REOR; + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d Eor %d %o %x\n", chan, + caddr[chan], chan_io_status[chan], chan_flags[chan]); + return END_RECORD; + } + + + return DATA_OK; +} + +/* + * Read next char from assembly register. + */ +int +chan_read_char(int chan, uint8 * data, int flags) +{ + + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR); + chan_io_status[chan] |= IO_CHS_DONE; + return END_RECORD; + } + + /* Check if he write out last data */ + if ((chan_flags[chan] & STA_ACTIVE) == 0) + return TIME_ERROR; + + /* Send rest of command */ + if (cmd[chan] & CHAN_DSK_DATA) { + *data = M[caddr[chan]]; + if (*data == (WM|077)) + return END_RECORD; + *data &= 077; + caddr[chan]++; + return DATA_OK; + } + + /* If we had a previous word mark send it */ + if ((cmd[chan] & (CHAN_LOAD|CHAN_WM)) == (CHAN_LOAD|CHAN_WM)) { + *data = assembly[chan]; + cmd[chan] &= ~CHAN_WM; + } else { + if (!MEM_ADDR_OK(caddr[chan]+1)) { + chan_flags[chan] &= ~STA_ACTIVE; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + caddr[chan]++; + return END_RECORD; + } + assembly[chan] = M[caddr[chan]++]; + /* Handle end of record */ + if ((cmd[chan] & CHAN_NOREC) == 0 && assembly[chan] == (WM|077)) { + chan_flags[chan] &= ~STA_ACTIVE; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_io_status[chan] |= IO_CHS_DONE; + return END_RECORD; + } + if (cmd[chan] & CHAN_LOAD && + (assembly[chan] & WM || assembly[chan] == 035)) { + cmd[chan] |= CHAN_WM; + assembly[chan] &= 077; + *data = 035; + return DATA_OK; + } + if (cmd[chan] & CHAN_6BIT) + *data &= 077; + *data = assembly[chan]; + } + + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE|STA_ACTIVE); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_io_status[chan] |= IO_CHS_DONE; + chan_flags[chan] |= DEV_REOR; + return END_RECORD; + } else + chan_flags[chan] |= DEV_WRITE; + return DATA_OK; +} + + +void +chan9_set_error(int chan, uint32 mask) +{ + if (chan_flags[chan] & mask) + return; + chan_flags[chan] |= mask; +} + +t_stat +chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", chan_description(dptr)); + fprintf (st, "The 7010 supports up to 4 channels. Channel models include\n\n"); + fprintf (st, " Channel * is for unit record devices.\n"); + fprintf (st, " Channels 1-4 are 7010 multiplexor channel\n\n"); + fprintf (st, "Channels are fixed on the 7010.\n\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +chan_description(DEVICE *dptr) +{ + return "IBM 7010 channel controller"; +} + diff --git a/I7000/i7010_cpu.c b/I7000/i7010_cpu.c new file mode 100644 index 00000000..ee4e5c08 --- /dev/null +++ b/I7000/i7010_cpu.c @@ -0,0 +1,3957 @@ +/* i7010_cpu.c: IBM 7010 CPU simulator + + Copyright (c) 2006, 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. + + cpu 7010 central processor + + The IBM 1410 and 7010 were designed as enhancements to the IBM 1401, + these were somewhat source compatable, but not binary compatable. + The 1410 was introduced on September, 12 1960 and the 7010 in 1962. + The 1410 was withdrawn on March 30, 1970. The 7010 featured + 4 I/O channels where the 1410 had 2. Also the 7010 could access 100,000 + characters of memory as opposed to the 80,000 for the 1410. The 7010 also + featured optional decimal floating point instructions. Memory was + divided into feilds seperated by a special flag called a word mark. + Instructions end at the first character with the word mark set. They + consist of a operation code, followed by 1 or 2 5-digit addresses, and + an optional instruction modifier. If the 10's and 100's digit have zone + bits set the address is modified by the contents of the five characters + at locations 25-100. Each register is 5 characters long and word marks + are ignored. The 1410 and 7010 could also be optionaly equiped with + priority mode to allow for device complete interupts. + + The 7010 or 1410 cpu has no registers. All operations on done from + memory. + + i7010_defs.h add device definitions + i7010_sys.c add sim_devices table entry +*/ + +#include "i7010_defs.h" +#include "sim_card.h" +#include + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) +#define UNIT_MSIZE (017 << UNIT_V_MSIZE) +#define UNIT_V_CPUMODEL (UNIT_V_UF + 5) +#define UNIT_MODEL (0x3 << UNIT_V_CPUMODEL) +#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_CPUMODEL) & 0x3) +#define MODEL(x) (x << UNIT_V_CPUMODEL) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define OPTION_PRIO (1 << (UNIT_V_UF + 13)) +#define OPTION_FLOAT (1 << (UNIT_V_UF + 14)) +#define OPTION_PROT (1 << (UNIT_V_UF_31)) + +#define TMR_RTC 100 + +#define HIST_XCT 1 /* instruction */ +#define HIST_INT 2 /* interrupt cycle */ +#define HIST_TRP 3 /* trap cycle */ +#define HIST_MIN 64 +#define HIST_MAX 65536 +#define HIST_NOEA 0x40000000 +#define HIST_PC 0x100000 +#define HIST_MSK 0x0FFFFF +#define HIST_1401 0x200000 /* 1401 instruction */ + +struct InstHistory +{ + uint32 ic; + uint8 inst[15]; + uint32 astart; + uint32 bstart; + uint32 aend; + uint32 bend; + uint8 dlen; + uint8 bdata[50]; +}; + +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); +int do_addint(int val); +t_stat do_addsub(int mode); +t_stat do_mult(); +t_stat do_divide(); + +/* Interval timer option */ +t_stat rtc_srv(UNIT * uptr); +t_stat rtc_reset(DEVICE * dptr); +int32 rtc_tps = 200; + + +/* General registers */ +uint8 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 IAR; /* program counter */ +int32 AAR; /* A Address Register */ +int32 BAR; /* B Address Register */ +int32 CAR; /* C Address Register */ +int32 DAR; /* D Address Register */ +uint8 SW; /* Switch register */ +uint32 XR; /* IO Address register */ +uint8 cind; /* Compare indicators */ +uint8 zind; /* Zero balence */ +uint8 oind; /* Overflow indicator */ +uint8 dind; /* Divide Over indicator */ +uint8 tind; /* Tape indicator */ +uint8 op_mod; /* Opcode modifier */ +uint8 euind; /* Exp underflow indicator */ +uint8 eoind; /* Exp overflow indicator */ +uint8 fault; /* Access fault */ +uint8 pri_enb = 1; /* Priority mode flags */ +uint8 inquiry = 0; /* Inquiry IRQ pending */ +uint8 urec_irq[NUM_CHAN]; /* Unit record IRQ pending */ +uint8 astmode = 1; /* Astrisk mode */ +uint8 chan_io_status[NUM_CHAN]; /* Channel status */ +uint8 chan_seek_done[NUM_CHAN]; /* Channel seek finished */ +uint8 chan_irq_enb[NUM_CHAN]; /* IRQ type opcode */ +uint8 lpr_chan9[NUM_CHAN]; /* Line printer at channel 9 */ +uint8 lpr_chan12[NUM_CHAN]; /* Line printer at channel 12 */ +extern uint32 caddr[NUM_CHAN]; /* Channel addresses */ +int low_addr = -1; /* Low protection address */ +int high_addr = -1; /* High protection address */ +int reloc = 0; /* Dislocate address flag */ +uint8 prot_fault = 0; /* Protection fault indicators. */ +uint8 prot_enb = 0; /* Protection enables */ +uint8 relo_flags = 0; /* Relocation flags */ +uint8 timer_irq = 0; /* Interval timer interrupt */ +uint8 timer_enable = 0; /* Interval timer enable */ +int timer_interval = 0; /* Interval timer interval */ +int chwait = 0; /* Wait for channel to finish */ +int io_flags = 0; /* Io flags for 1401 */ +int cycle_time = 28; /* Cycle time in 100ns */ +uint8 time_digs[] = {0, 2, 3, 5, 7, 8}; + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ +extern UNIT chan_unit[]; + +/* Simulator debug controls */ +DEBTAB cpu_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"PRI", DEBUG_PRIO}, + {0, 0} +}; + + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + { UDATA(rtc_srv, MODEL(2)|MEMAMOUNT(9)|OPTION_PRIO|OPTION_FLOAT, + MAXMEMSIZE), 10000 }; + +REG cpu_reg[] = { + {DRDATAD(IAR, IAR, 18, "Instruction Address Register"), REG_FIT}, + {DRDATAD(A, AAR, 18, "A Address register"), REG_FIT}, + {DRDATAD(B, BAR, 18, "B Address register"), REG_FIT}, + {DRDATAD(C, CAR, 18, "C Address register"), REG_FIT}, + {DRDATAD(D, DAR, 18, "D Address register"), REG_FIT}, + {DRDATAD(E, caddr[0], 18, "Channel 0 address"), REG_FIT}, + {DRDATAD(F, caddr[1], 18, "Channel 1 address"), REG_FIT}, + {DRDATAD(G, caddr[2], 18, "Channel 2 address"), REG_FIT}, + {DRDATAD(H, caddr[3], 18, "Channel 3 address"), REG_FIT}, + {FLDATAD(ASTRISK, astmode, 1, "Asterix Mode"), REG_FIT}, + {BRDATAD(SW, &SW, 2, 7, 1, "Sense Switch register"), REG_FIT}, + {FLDATAD(SW1, SW, 0, "Sense Switch 0"), REG_FIT}, + {FLDATAD(SW2, SW, 1, "Sense Switch 1"), REG_FIT}, + {FLDATAD(SW3, SW, 2, "Sense Switch 2"), REG_FIT}, + {FLDATAD(SW4, SW, 3, "Sense Switch 3"), REG_FIT}, + {FLDATAD(SW5, SW, 4, "Sense Switch 4"), REG_FIT}, + {FLDATAD(SW6, SW, 5, "Sense Switch 5"), REG_FIT}, + {FLDATAD(SW7, SW, 6, "Sense Switch 6"), REG_FIT}, + {NULL} +}; + +MTAB cpu_mod[] = { + {UNIT_MODEL, MODEL(1), "1401", "1401", NULL, NULL, NULL, "Emulate a 1401"}, + {UNIT_MODEL, MODEL(2), "7010", "7010", NULL, NULL, NULL, "Emulate a 7010"}, + {UNIT_MSIZE, MEMAMOUNT(0), "10K", "10K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(1), "20K", "20K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(2), "30K", "30K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(3), "40K", "40K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(4), "50K", "50K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(5), "60K", "60K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(6), "70K", "70K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(7), "80K", "80K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(8), "90K", "90K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(9), "100K", "100K", &cpu_set_size}, + {OPTION_PRIO, 0, NULL, "NOPRIORITY", NULL, NULL, NULL, + "No Priority Mode"}, + {OPTION_PRIO, OPTION_PRIO, "PRIORITY", "PRIORITY", NULL, NULL, NULL, + "Priority Mode"}, + {OPTION_FLOAT, 0, NULL, "NOFLOAT", NULL, NULL,NULL, + "No Floating Point"}, + {OPTION_FLOAT, OPTION_FLOAT, "FLOAT", "FLOAT", NULL, NULL, NULL, + "Floating point"}, + {OPTION_PROT, 0, NULL, "NOPROT", NULL, NULL,NULL, + "No memory protection"}, + {OPTION_PROT, OPTION_PROT, "PROT", "PROT", NULL, NULL, NULL, + "Memory Protection"}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 18, 1, 8, 8, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, cpu_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description +}; + + + /*0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F */ +uint8 bcd_bin[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7}; +uint8 bin_bcd[20] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +uint32 dscale[4][16] = { + {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 0,30,0,0,0,0}, + {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 0,0,0,0,0,0}, + {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 0,0,0,0,0,0}, + {0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, + 0,0,0,0,0,0} +}; + + +#define NORELA 0x2 +#define NORELB 0x4 + +uint8 digit_addone[16] = { + 0,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x01,0x0b,0x0c,0x0d,0x0e, + 0x0f}; + +uint8 cmp_order[0100] = { + /* b 1 2 3 4 5 6 7 */ + 0, 55, 56, 57, 58, 59, 60, 61, + /* 8 9 0 # @ : > tm */ + 62, 63, 54, 20, 21, 22, 23, 24, + /*cent / S T U V W X */ + 19, 13, 46, 47, 48, 49, 50, 51, + /* Y Z rm , % = ' " */ + 52, 53, 45, 14, 15, 16, 17, 18, + /* - J K L M N O P */ + 12, 36, 37, 38, 39, 40, 41, 42, + /* Q R ! $ * ) ; del */ + 43, 44, 35, 7, 8, 9, 10, 11, + /* & A B C D E F G */ + 6, 26, 27, 28, 29, 30, 31, 32, + /* H I ? . sq ( < gm */ + 33, 34, 25, 1, 2, 3, 4, 5 +}; + + +#define O_A 001 /* Can take A */ +#define O_B 002 /* Can take B */ +#define O_AB (O_A|O_B) /* Can take both A & B */ +#define O_M 004 /* Can take modifier */ +#define O_X 010 /* Special Operand */ +#define O_C 020 /* Load C register on frist argument */ +#define O_D 0100 /* Load D register on second argument */ +#define O_DBL 0200 /* When chained A same as B */ +#define O_ABCD (O_A|O_B|O_C|O_D) + +uint8 op_args[64] = { + /* 00 01 02 03 04 05 06 07 */ + /* CC2 SSF2 */ + 0, 0, O_M, 0, O_M, 0, 0, 0, /* 00 */ + /* FP M */ + 0, 0, 0, O_A|O_M, O_AB, 0, 0, 0, /* 10 */ + /* CS S T UC BWE BBE IO2 */ + 0,O_AB|O_DBL,O_AB|O_DBL,O_AB|O_M,O_X|O_M,O_AB|O_M,O_AB|O_M,O_A|O_M|O_DBL,/* 20 */ + /* PRI MSZ SWM D */ + O_A|O_M,O_AB,0,O_AB|O_DBL, O_AB, 0, 0, 0, /* 30 */ + /* B SSF1 RDW RD NOP */ + 0,O_A|O_M|O_DBL,O_M,O_X|O_B|O_M,O_X|O_B|O_M, 0, 0, 0,/* 40 */ + /* IO1 ZS STS */ + 0, O_A|O_M|O_DBL,O_AB|O_DBL,O_A|O_M, 0, 0, 0, 0, /* 50 */ + /* A BCE C MOV E CC1 SAR */ + 0, O_AB|O_DBL,O_AB|O_M,O_AB,O_AB|O_M,O_AB, O_M,O_C|O_M,/* 60 */ + /* 0 ZA H CWM */ + 0, 0, O_AB|O_DBL,O_A,O_AB|O_DBL, 0, 0, 0, /* 70 */ +}; + + +uint8 op_1401[64] = { + /* 00 01 02 03 04 05 06 07 */ + /* b 1 2 3 4 5 6 7 */ + /* RCD PRT PUN */ + 0, O_A, O_A|O_M, O_A, O_A, O_A, O_A|O_M, O_A|O_M, /* 00 */ + /* 8 9 0 # @ : > tm */ + /* M */ + 0, 0, 0, O_AB|O_DBL, O_AB, 0, 0, 0, /* 10 */ + /*cent / S T U V W X */ + /* CS S BWZ BBE */ + 0,O_AB|O_DBL,O_AB|O_DBL,0,O_X|O_M,O_AB|O_M,O_AB|O_M,0,/* 20 */ + /* Y Z rm , % = ' " */ + /* MZ MCS SWM MA */ + O_AB, O_AB,0,O_AB|O_DBL, O_AB, O_AB, 0, 0, /* 30 */ + /* - J K L M N O P */ + /* RDW MLCWA MLC NOP MRCM */ + 0, 0,O_M|O_A,O_AB, O_AB,O_AB,0, O_AB,/* 40 */ + /* Q R ! $ * ) ; del */ + /* SAR ZS */ + O_C, 0, O_AB|O_DBL,0, 0, 0, 0, 0, /* 50 */ + /* & A B C D E F G */ + /* A B C MLNS */ + 0, O_AB|O_DBL,O_AB|O_M,O_AB,O_AB,O_AB, O_M|O_A,0,/* 60 */ + /* H I ? . sq ( < gm */ + /* SBR ZA H CWM */ + O_C|O_B,0, O_AB|O_DBL,O_A,O_AB|O_DBL, 0, 0, 0, /* 70 */ +}; + +uint8 FetchP(uint32 MA) { + uint32 MAR = MA & AMASK; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return 0; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return 0; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return 0; + } + return M[MAR]; +} + + +uint8 ReadP(uint32 MA) { + uint32 MAR = MA & AMASK; + + if (fault) + return 0; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return 0; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return 0; + } + if (((low_addr >= 0) && (MAR < (uint32)low_addr)) || + ((high_addr > 0) && (MAR > (uint32)high_addr))) { + fault = STOP_PROT; + return 0; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return 0; + } + return M[MAR]; +} + +void WriteP(uint32 MA, uint8 v) { + uint32 MAR = MA & AMASK; + + if (fault) + return; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return; + } + if (((low_addr >= 0) && (MAR < (uint32)low_addr)) || + ((high_addr > 0) && (MAR > (uint32)high_addr))) { + fault = STOP_PROT; + return; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return; + } + M[MAR] = v; +} + +void ReplaceMask(uint32 MA, uint8 v, uint8 mask) { + uint32 MAR = MA & AMASK; + + if (fault) + return; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return; + } + if (((low_addr >= 0) && (MAR < (uint32)low_addr)) || + ((high_addr > 0) && (MAR > (uint32)high_addr))) { + fault = STOP_PROT; + return; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return; + } + M[MAR] &= ~mask; + M[MAR] |= v; +} + + +void SetBit(uint32 MA, uint8 v) { + uint32 MAR = MA & AMASK; + + if (fault) + return; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return; + } + if (((low_addr >= 0) && (MAR < (uint32)low_addr)) || + ((high_addr > 0) && (MAR > (uint32)high_addr))) { + fault = STOP_PROT; + return; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return; + } + M[MAR] |= v; +} + +void ClrBit(uint32 MA, uint8 v) { + uint32 MAR = MA & AMASK; + + if (fault) + return; + + if (reloc && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr > 0) { + MAR += low_addr; + if (MAR >= 100000) + MAR -= 100000; + } + if (prot_enb && (high_addr > 0) && (MAR > (uint32)high_addr)) { + fault = STOP_PROT; + return; + } + } else if (prot_enb && (MA & BBIT) == 0 && MAR > 100) { + if (low_addr < 0 && high_addr == 0) { + fault = STOP_PROT; + return; + } + if (((low_addr >= 0) && (MAR < (uint32)low_addr)) || + ((high_addr > 0) && (MAR > (uint32)high_addr))) { + fault = STOP_PROT; + return; + } + } + if (MAR >= MEMSIZE) { + fault = STOP_INVADDR; + return; + } + M[MAR] &= ~v; +} + +#define UpReg(reg) reg++; if ((reg & AMASK) == MEMSIZE) { \ + reason = STOP_INVADDR; break; } + +#define DownReg(reg) if ((reg & AMASK) == 0) { \ + reason = STOP_INVADDR; break; } else { reg--; } + +#define ValidAddr(reg) if ((reg & AMASK)== 0 || !MEM_ADDR_OK(reg)) { \ + reason = STOP_INVADDR; break; \ + } + +#define ZeroAddr(reg) if ((reg & AMASK)== 0) { \ + reason = STOP_INVADDR; break; \ + } + +t_stat +sim_instr(void) +{ + t_stat reason; + uint16 t; + int temp; + int32 STAR; + uint8 op, op_info; + int state; + uint8 ix; + uint8 br; + uint8 ar; + int sign, qsign; + uint8 ch; + int cy; + int i; + int jump; /* Do transfer to AAR after op */ + int instr_count = 0;/* Number of instructions to execute */ + + if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); + } + + reason = 0; + fault = 0; + if (cpu_unit.flags & OPTION_PROT) + sim_activate(&cpu_unit, sim_rtcn_calb(cpu_unit.wait, TMR_RTC)); + + while (reason == 0) { /* loop until halted */ + + chan_proc(); + if (chwait != 0) { + if (chan_active(chwait & 07)) { + sim_interval = 0; + } else { + if ((chwait & 040) == 0) { + BAR = caddr[chwait & 07]; + if (hst_lnt) /* History enabled? */ + hst[hst_p].bend = BAR; + } + chan_io_status[chwait & 07] &= ~0100; + chwait = 0; + } + } + + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + break; /* process */ + } + } + + if (chwait == 0 && sim_brk_summ && sim_brk_test(IAR, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + + if (chwait == 0) { + uint8 bbit = 0; + if (hst_lnt) { /* History enabled? */ + hst_p = (hst_p+1); /* Next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = IAR | HIST_PC; + if (CPU_MODEL == 1) + hst[hst_p].ic |= HIST_1401; + } + op = FetchP(IAR++); + /* Check if over the top */ + if (fault) + goto check_prot; + if (hst_lnt) /* History enabled? */ + hst[hst_p].inst[0] = op; + sim_interval -= 2; + if ((op & WM) == 0) { + reason = STOP_NOWM; + goto check_prot; + } + op &= 077; + op_info = (CPU_MODEL != 1)? op_args[op]: op_1401[op]; + state = 1; + i = 1; + temp = IAR + 5; /* Save for intertupt routine */ + while(((br = FetchP(IAR)) & WM) == 0 && op_info != 0 && fault == 0) { + IAR++; + sim_interval -= 2; + if (hst_lnt) /* History enabled? */ + hst[hst_p].inst[i++] = br; + br &= 077; + if (CPU_MODEL != 1) { + switch(state) { + case 1: /* could be operand or address */ + ar = br; + state = 2; + if (ar & 040) + bbit = 1; + else + bbit = 0; + break; + case 2: /* Has to be address, check if goes to C or AB */ + state = 3; + if (op_info & O_X) { + XR = (ar << 12) | (br << 6); + } else if (op_info & (O_C|O_A)) { + STAR = dscale[3][bcd_bin[ar & 0xf]]; + STAR += dscale[2][bcd_bin[br & 0xf]]; + if ((ar & 020) || (br & 060)) + reason = STOP_INVADDR; + } + break; + case 3: /* Has to be address, check if goes to C or AB */ + state = 4; + if (op_info & O_X) { + XR |= br; + state = 6; + } else if (op_info & (O_C|O_A)) { /* hundreds */ + ix = (br & 0x30) >> 2; + STAR += dscale[1][bcd_bin[br & 0xf]]; + } + break; + case 4: /* Has to be address, check if goes to C or AB */ + state = 5; + if (op_info & (O_C|O_A)) { /* tens */ + ix |= (br & 0x30) >> 4; + STAR += dscale[0][bcd_bin[br & 0xf]]; + } + break; + case 5: /* Has to be address, check if goes to C or AB */ + state = 6; + if (op_info & (O_C|O_A) && br & 060) { + reason = STOP_INVADDR; + break; + } + if (op_info & (O_C|O_A)) /* units */ + STAR += bcd_bin[br & 0xf]; + if ((op_info & O_A) && (ix != 0)) { + int j, a, s; + /* do indexing */ + ix = (ix * 5) + 24; + s = ((ReadP(ix) & 060) == 040)?1: 0; + a = bcd_bin[ReadP(ix--) & 0xf]; + for(j = 0; j < 4; j++) + a += dscale[j][bcd_bin[ReadP(ix--) & 0xf]]; + STAR += (s)?(99999 - a):a; + STAR += s; + STAR %= 100000; + sim_interval -= 10; + } + if (bbit) + STAR |= BBIT; + bbit = 0; + if (op_info & O_C) + CAR = STAR; + if (op_info & O_A) { + AAR = STAR; + if (op_info & O_DBL) { + if (op_info & O_D) + DAR = AAR; + BAR = AAR; + } + } + temp = IAR; /* Save for intertupt routine */ + break; + case 6: /* Could be either B address or operand. */ + state = 7; + ar = br; + if (ar & 040) + bbit = 1; + else + bbit = 0; + break; + case 7: /* Has to be B address */ + state = 8; + /* ten thousand, thousand */ + if (op_info & (O_B|O_D)) { + STAR = dscale[3][bcd_bin[ar & 0xf]]; + STAR += dscale[2][bcd_bin[br & 0xf]]; + if ((ar & 020) || (br & 060)) + reason = STOP_INVADDR; + } + if ((op_info & O_M) == 0) + op_mod = 0; + break; + case 8: /* Has to be B address */ + state = 9; + /* hundreds */ + if (op_info & (O_B|O_D)) { + STAR += dscale[1][bcd_bin[br & 0xf]]; + ix = (br & 0x30) >> 2; + } + break; + case 9: /* Has to be B address */ + state = 10; + /* tens */ + if (op_info & (O_B|O_D)) { + STAR += dscale[0][bcd_bin[br & 0xf]]; + ix |= (br & 0x30) >> 4; + } + break; + case 10: /* Units digit of B address */ + state = 11; + if (op_info & (O_B|O_D)) { + if (br & 060) { + reason = STOP_INVADDR; + break; + } + STAR += bcd_bin[br & 0xf]; + } + if (op_info & O_B && ix != 0) { + int j, a, s; + /* do indexing */ + ix = (ix * 5) + 24; + s = ((ReadP(ix) & 060) == 040)?1: 0; + a = bcd_bin[ReadP(ix--) & 0xf]; + for(j = 0; j < 4; j++) + a += dscale[j][bcd_bin[ReadP(ix--) & 0xf]]; + STAR += (s)?(99999 - a):a; + STAR += s; + STAR %= 100000; + sim_interval -= 10; + } + if (bbit) + STAR |= BBIT; + bbit = 0; + if (op_info & O_D) + DAR = STAR; + if (op_info & O_B) + BAR = STAR; + break; + case 11: /* Has to be modifier */ + state = 12; + ar = br; + break; + case 12: /* Too long */ + reason = STOP_NOWM; + state = 13; + break; + } + } else { /* Handle 1401 emulation mode */ + switch(state) { + case 1: /* could be operand or address */ + /* BA */ + /* 00 0-999 */ + /* 01 1000-1999 */ + /* 10 2000-2999 */ + /* 11 3000-3999 */ + ar = br; + if (op_info & O_X || + ((op == CHR_M || op == CHR_L) && br == CHR_RPARN)) { + XR = br << 12; + op_info |= O_X; + } + state = 2; + break; + case 2:/* Has to be address, check if goes to C or AB */ + /* BA */ + /* 00 - none */ + /* 01 - 1 87,88,89, 90,91 */ + /* 10 - 2 92,93,94, 95,96 */ + /* 11 - 3 97,98,99 */ + state = 3; + if (op_info & O_X) + XR |= br << 6; + if (op_info & (O_C|O_A)) { + STAR = dscale[1][bcd_bin[ar & 0xf]]; + STAR += dscale[0][bcd_bin[br & 0xf]]; + STAR += ((ar & 0x30) >> 4) * 1000; + ix = (br & 0x30) >> 4; + } + break; + case 3:/* Has to be address, check if goes to C or AB */ + /* BA */ + /* 00 0-3999 */ + /* 01 4000-7999 */ + /* 10 8000-11999 */ + /* 11 12000-15999 */ + state = 4; + if (op_info & O_X) + XR |= br; + if (op_info & (O_C|O_A)) { /* hundreds */ + STAR += bcd_bin[br & 0xf]; + STAR += ((br & 0x30) >> 4) * 4000; + if (ix != 0) { + int a; + /* do indexing */ + ix = (ix * 5) + 82; + a = dscale[1][bcd_bin[M[ix] & 0xf]]; + a += dscale[0][bcd_bin[M[ix+1] & 0xf]]; + a += bcd_bin[M[ix+2] & 0xf]; + a += dscale[2][((M[ix] & 060) >> 4)]; + a += ((M[ix+2] & 060) >> 4) * 4000; + STAR += a; + STAR %= 16000; + sim_interval -= 3; + } + } + if (op_info & O_C) + CAR = STAR; + if (op_info & O_A) { + AAR = STAR; + if (op_info & O_DBL) { + if (op_info & O_D) + DAR = AAR; + BAR = AAR; + } + } + break; + case 4: /* Could be either B address or operand. */ + state = 5; + ar = br; + break; + case 5: /* Has to be B address */ + state = 6; + /* ten thousand, thousand */ + if (op_info & (O_B|O_D)) { + STAR = dscale[1][bcd_bin[ar & 0xf]]; + STAR += dscale[0][bcd_bin[br & 0xf]]; + STAR += ((ar & 0x30) >> 4) * 1000; + ix = (br & 0x30) >> 4; + } + if ((op_info & O_M) == 0) + op_mod = 0; + break; + case 6: /* Has to be B address */ + state = 7; + /* hundreds */ + if (op_info & (O_B|O_D)) { + STAR += bcd_bin[br & 0xf]; + STAR += ((br & 0x30) >> 4) * 4000; + if (ix != 0) { + int a; + /* do indexing */ + ix = (ix * 5) + 82; + a = dscale[1][bcd_bin[M[ix] & 0xf]]; + a += dscale[0][bcd_bin[M[ix+1] & 0xf]]; + a += bcd_bin[M[ix+2] & 0xf]; + a += dscale[2][((M[ix] & 060) >> 4)]; + a += ((M[ix+2] & 060) >> 4) * 4000; + STAR += a; + STAR %= 16000; + sim_interval -= 3; + } + } + if (op_info & O_D) + DAR = STAR; + if (op_info & O_B) + BAR = STAR; + break; + case 7: /* Has to be modifier */ + state = 8; + ar = br; + break; + case 8: /* Too long */ + if (op != OP_NOP && op != CHR_B) + reason = STOP_NOWM; + state = 9; + break; + } + /* Some instructions don't have to have word marks */ + if (op == OP_SWM && state == 7) + break; + if (op == CHR_B && state == 5 && ar == CHR_ABLANK) + break; + if (op == CHR_B && state == 9) + break; + } + if (reason != 0) + goto check_prot; + } + + if (hst_lnt) /* History enabled? */ + hst[hst_p].inst[i++] = WM; /* Term hist ins */ + + jump = 0; + if (CPU_MODEL == 1) { + + if (hst_lnt) { /* History enabled? */ + hst[hst_p].astart = AAR; + hst[hst_p].bstart = BAR; + hst[hst_p].inst[state] = WM; + } + + /* Translate instruction from 1401 to 1410 */ + switch(op) { + case CHR_B: /* Fix branch to correct kind */ + switch(state) { + case 8: op_mod = ar; /* B ddd iii c */ + case 7: /* B ddd iii */ + case 1: /* B */ + op = OP_BCE; + break; + case 4: ar = CHR_ABLANK; /* B ddd */ + case 2: /* B c ?? */ + default: /* B ddd c */ + case 5: op = OP_B; op_mod = ar; break; + } + break; + case CHR_U: + case CHR_W: + case CHR_V: if (state == 8 || state == 2 || state == 5) + op_mod = ar; + break; + case CHR_K: + case CHR_F: + temp = (op == CHR_K)?010100:010200; + if (state == 2 || state == 5) + op_mod = ar; + temp |= op_mod; + while((t = chan_cmd(temp, (IO_CTL<<8), 0)) + == SCPE_BUSY); + if (t != SCPE_OK) { + t = (temp >> 6) & 07; + io_flags &= ~t; + } + if (state == 4 || state == 5) + jump = 1; + op = OP_NOP; + break; + + /* Translate Move into 1410 move */ + case CHR_M: if (op_info & O_X) { + chan_io_status[1] = 0; + op_mod = ar; + } else { + op = OP_MOV; + op_mod = CHR_C; + } + break; + case CHR_L: if (op_info & O_X) { + chan_io_status[1] = 0; + op_mod = ar; + } else { + op = OP_MOV; + op_mod = CHR_X; + } + break; + case CHR_D: op = OP_MOV; op_mod = CHR_1; break; + case CHR_P: op = OP_MOV; op_mod = CHR_DOT; break; + case CHR_Y: op = OP_MOV; op_mod = CHR_2; break; + + /* Handle 1401 I/O opcodes */ + case CHR_1: /* Reader */ + case CHR_2: /* Print */ + case CHR_3: /* Print and read */ + case CHR_4: /* Punch */ + case CHR_5: /* Read and Punch */ + case CHR_6: /* Print and Punch */ + case CHR_7: /* Print,Read and Punch */ + op_mod = op; + op = OP_NOP; + while(op_mod != 0 || chwait != 0) { + while (chan_active(1) && reason == 0) { + sim_interval = 0; + reason = sim_process_event(); + chan_proc(); + } + if (chwait != 0) { + BAR = caddr[1]; + if (hst_lnt) /* History enabled? */ + hst[hst_p].bend = BAR; + chwait = 0; + } + + /* Stop if something wrong */ + if (reason != 0) + break; + /* Convert to channel instruction */ + if (op_mod & 02) { /* Print line */ + temp = 010200; + if ((state == 2 || state == 5) + && ar == CHR_LPARN) + temp |= 1; + else + temp |= 012; + t = (IO_WRS << 8); + BAR = 201; + } else if (op_mod & 01) { /* Reader */ + temp = 010100; + t = (IO_RDS << 8); + BAR = 1; + } else if (op_mod & 04) { /* Punch */ + temp = 010400; + t = (IO_WRS << 8); + BAR = 101; + } else + break; + + /* Try to start command */ + switch (chan_cmd(temp, t, BAR)) { + case SCPE_OK: + t = (temp >> 6) & 07; + io_flags &= ~t; + op_mod &= ~t; + chwait = 01; + if (chan_stat(1, CHS_EOF)) + io_flags |= (t << 3) | t; + break; + case SCPE_BUSY: + sim_interval = 0; + reason = sim_process_event(); + chan_proc(); + break; + case SCPE_NODEV: + case SCPE_IOERR: + chan_io_status[1] = 01; + io_flags |= (temp >> 6) & 07; + op_mod = 0; /* Abort */ + break; + } + }; + /* Handle branching */ + if (state == 4 || state == 5) + jump = 1; + break; + + case CHR_8: /* Start read feed */ + case CHR_9: /* Start punch feed */ + /* Not supportable by sim */ + op = OP_NOP; + break; + + case CHR_EQ: /* Handle modify address here */ + op = OP_NOP; + DAR = BAR; /* Save for later */ + ar = ReadP(AAR); + br = ReadP(BAR); + sim_interval -= 2; + ix = (ar & 060) + (br & 060); /* Add zone */ + ar = bcd_bin[br & 017] + bcd_bin[ar & 017]; + cy = ar > 9; + WriteP(BAR, (br & WM) | (ix & 060) | bin_bcd[ar]); + DownReg(AAR); + DownReg(BAR); + ar = ReadP(AAR); + br = ReadP(BAR); + sim_interval -= 2; + ar = bcd_bin[br & 017] + bcd_bin[ar & 017] + cy; + cy = ar > 9; + WriteP(BAR, (br & (WM | 060)) | bin_bcd[ar]); + DownReg(AAR); + DownReg(BAR); + ar = ReadP(AAR); + br = ReadP(BAR); + sim_interval -= 2; + ix = (ar & 060) + (br & 060); /* Add zone */ + ar = bcd_bin[br & 017] + bcd_bin[ar & 017] + cy; + if (ar > 9) + ix += 020; + WriteP(BAR, (br & WM) | (ix & 060) | bin_bcd[ar]); + DownReg(AAR); + if (ix & 0100) { /* Carry out of low zone */ + BAR = DAR; /* Restore */ + br = ReadP(BAR); + ix = (br & 060) + 020; /* Add zone */ + WriteP(BAR, (br & (WM|017)) | (ix & 060)); + sim_interval--; + } + DownReg(BAR); + break; + case CHR_Q: /* Handle SAR here */ + BAR = AAR; /* Copy AAR to BAR */ + case CHR_H: /* Handle SBR here */ + op = OP_NOP;/* done and at WM, so skip rest */ + if (state > 2) + AAR = CAR; + temp = BAR % 1000; /* Compute base address */ + i = (BAR - temp) / 1000; + ch = temp % 10; + temp /= 10; + ch = bin_bcd[ch] | ((i & 014) << 2); + ReplaceMask(AAR, ch, 077); + sim_interval--; + DownReg(AAR); + ch = temp % 10; + temp /= 10; + ch = bin_bcd[ch]; + ReplaceMask(AAR, ch, 077); + sim_interval--; + DownReg(AAR); + ch = temp; + ch = bin_bcd[ch] | ((i & 03) << 4); + ReplaceMask(AAR, ch, 077); + sim_interval--; + DownReg(AAR); + break; + default: + /* no change needed */ + break; + } + } else { + if (fault) + goto check_prot; + + /* Check instruction length */ + switch(op) { + case OP_S: + case OP_A: + case OP_ZS: + case OP_ZA: + case OP_M: + case OP_D: + case OP_C: + case OP_CS: + case OP_SWM: + case OP_CWM: + case OP_MSZ: + case OP_E: + /* Valid forms */ + /* Op */ + /* Op AAAAA */ + /* Op AAAAA BBBBB */ + if (state != 1 && state != 6 && state != 11) + reason = STOP_INVLEN; + break; + case OP_BCE: + case OP_BBE: + case OP_BWE: + case OP_MOV: + case OP_T: + /* Valid forms */ + /* Op */ + /* Op mod */ + /* Op AAAAA */ + /* Op AAAAA mod */ + /* Op AAAAA BBBBB */ + /* Op AAAAA BBBBB mod */ + /* Check for modifier */ + if (state == 2 || state == 7 || state == 12) { + op_mod = ar; + break; + } + + /* Make sure len correct */ + if (state != 1 && state != 6 && state != 11) + reason = STOP_INVLEN; + break; + case OP_IO1: + case OP_IO2: + case OP_IO3: + case OP_IO4: + /* Not in protected mode */ + if (prot_enb || reloc) { + reason = STOP_PROG; + break; + } + case OP_STS: + /* Not in protected mode */ + if (prot_enb) { + reason = STOP_PROG; + break; + } + + case OP_PRI: + case OP_B: + case OP_SAR: + case OP_FP: + /* Valid forms */ + /* Op */ + /* Op mod */ + /* Op AAAAA */ + /* Op AAAAA mod */ + /* Check for modifier */ + if (state == 2 || state == 7) { + op_mod = ar; + break; + } + + /* Make sure len correct */ + if (state != 1 && state != 6) + reason = STOP_INVLEN; + break; + case OP_H: + /* Not in protected mode */ + if (prot_enb || reloc) { + reason = STOP_PROG; + break; + } + + /* Valid forms */ + /* Op */ + /* Op AAAAA */ + if (state != 1 && state != 6) + reason = STOP_INVLEN; + break; + case OP_UC: + /* Not in protected mode */ + if (prot_enb || reloc) { + reason = STOP_PROG; + break; + } + + /* Valid forms */ + /* Op xxx mod */ + /* Check for modifier */ + if (state == 7) { + op_mod = ar; + break; + } + + /* Make sure len correct */ + if (state != 7) + reason = STOP_INVLEN; + break; + case OP_CC1: + case OP_CC2: + case OP_SSF1: + case OP_SSF2: + /* Not in protected mode */ + if (prot_enb || reloc) { + reason = STOP_PROG; + break; + } + + /* Valid forms */ + /* Op mod */ + /* Check for modifier */ + if (state == 2) { + op_mod = ar; + break; + } + + /* Make sure len correct */ + reason = STOP_INVLEN; + break; + + case OP_RD: + case OP_RDW: + /* Not in protected mode */ + if (prot_enb || reloc) { + reason = STOP_PROG; + break; + } + + /* Valid forms */ + /* Op xxx mod */ + /* Op xxx BBBBB mod */ + /* Check for modifier */ + if (state == 7 || state == 12 ) { + op_mod = ar; + break; + } + + reason = STOP_INVLEN; + break; + case OP_NOP: + break; + } + + if (hst_lnt) { /* History enabled? */ + hst[hst_p].astart = AAR; + hst[hst_p].bstart = BAR; + if (op_info & O_M && + (state == 1 || state == 6 || state == 11)) { + hst[hst_p].inst[state] = op_mod; + hst[hst_p].inst[state+1] = WM; + } + } + + /* Handle fault */ + if (reason != 0) { + goto check_prot; + } + + /* Check to see if we should interupt */ + if (cpu_unit.flags & OPTION_PRIO && (pri_enb || timer_enable)) { + int irq = inquiry; + int ok_irq = 0; + for(i = 1; i < NUM_CHAN; i++ ) { + if ((chan_io_status[i] & 0300) == 0300 && + chan_irq_enb[i]) + irq = 1; + if (chan_test(i, SNS_ATTN1)) + irq = 1; + if (urec_irq[i]) + irq = 1; + } + + + if (irq || (timer_enable && timer_irq == 1)) { + /* Check if we can interupt this opcode */ + switch(op) { + case OP_S: + case OP_A: + case OP_ZS: + case OP_ZA: + case OP_M: + case OP_D: + case OP_SWM: + case OP_CWM: + case OP_MOV: + case OP_MSZ: + case OP_E: + case OP_C: + case OP_CS: + if (state > 10) + ok_irq = 1; + break; + case OP_T: + case OP_BCE: + case OP_BBE: + case OP_BWE: + if (state > 11) + ok_irq = 1; + break; + case OP_IO1: + case OP_IO2: + case OP_IO3: + case OP_IO4: + if (op_mod != 0) + break; + case OP_B: + if (state > 6) + ok_irq = 1; + break; + case OP_SAR: + case OP_H: + case OP_NOP: + case OP_RD: + case OP_RDW: + case OP_CC1: + case OP_CC2: + case OP_SSF1: + case OP_SSF2: + case OP_UC: + case OP_PRI: + case OP_STS: + case OP_FP: + break; + } + + if (ok_irq) { + sim_debug(DEBUG_PRIO, &cpu_dev, "Irq IAR=%d\n",IAR); + prot_enb = reloc = 0; + if (pri_enb && irq) { + IAR = temp; + AAR = 101; + op = OP_PRI; + op_mod = CHR_X; /* X */ + if (hst_lnt) { /* History enabled? */ + hst[hst_p].inst[0] = op; + hst[hst_p].inst[1] = op_mod; + hst[hst_p].inst[2] = WM; + } + } else if (timer_enable && timer_irq == 1) { + IAR = temp; + AAR = 301; + timer_irq = 2; + op = OP_PRI; + op_mod = CHR_X; /* X */ + if (hst_lnt) { /* History enabled? */ + hst[hst_p].inst[0] = op; + hst[hst_p].inst[1] = op_mod; + hst[hst_p].inst[2] = WM; + } + } + } + } + } + } + + + /* Execute instructions */ + switch(op) { + case OP_S: + /* Check if over the top */ + ValidAddr(AAR); + ValidAddr(BAR); + reason = do_addsub(1); + break; + + case OP_A: + /* Check if over the top */ + ValidAddr(AAR); + ValidAddr(BAR); + reason = do_addsub(0); + break; + + case OP_M: + /* Check if over the top */ + ValidAddr(AAR); + ValidAddr(BAR); + reason = do_mult(); + break; + + case OP_D: + /* Check if over the top */ + ValidAddr(AAR); + ValidAddr(BAR); + reason = do_divide(); + break; + + case OP_ZS: + /* Check if over the top */ + ar = ReadP(AAR); + DownReg(AAR); + if ((ar & 060) == 040) + ar |= 060; + else { + ar &= 017|WM; + ar |= 040; + } + goto zadd; + + case OP_ZA: + /* Check if over the top */ + ar = ReadP(AAR); + DownReg(AAR); + if ((ar & 060) != 040) + ar |= 060; + else { + ar &= 017|WM; + ar |= 040; + } + zadd: + zind = 1; + /* Copy digits until A or B word mark */ + br = ReadP(BAR) & WM; + STAR = BAR; + DownReg(BAR); + sim_interval -= 4; + while (1) { + WriteP(STAR, br | bin_bcd[bcd_bin[ar & 0xf]] | (ar & 060)); + if (bcd_bin[ar & 0xf] != 0) /* Update zero flag */ + zind = 0; + if (br & WM) + break; + sim_interval -= 4; + if (ar & WM) + ar = 10|WM; + else { + ar = ReadP(AAR) & (WM|017); + DownReg(AAR); + } + br = ReadP(BAR) & WM; + STAR = BAR; + DownReg(BAR); + } + break; + + case OP_SAR: + if ((CAR & AMASK) < 5 || !MEM_ADDR_OK(CAR)) { + reason = STOP_INVADDR; + break; + } + switch(op_mod) { + case CHR_A: temp = AAR; + if (reloc && low_addr >= 0 && temp & BBIT) { + if (temp < low_addr) + temp += 100000 - low_addr; + else + temp -= low_addr; + } + break; /* A */ + case CHR_B: temp = BAR; + if (reloc && low_addr >= 0 && temp & BBIT) { + if (temp < low_addr) + temp += 100000 - low_addr; + else + temp -= low_addr; + } + break; /* B */ + case CHR_E: temp = caddr[1]; break; /* E */ + case CHR_F: temp = caddr[2]; break; /* F */ + case CHR_G: temp = caddr[3]; break; /* G */ + case CHR_H: temp = caddr[4]; break; /* H */ + case CHR_T: /* T */ + { + time_t curtim; + struct tm *tptr; + + temp = 99999; + curtim = time(NULL); /* get time */ + tptr = localtime(&curtim); /* decompose */ + if (tptr != NULL && tptr->tm_sec != 59) { + /* Convert minutes to 100th hour */ + temp = time_digs[tptr->tm_min % 6]; + temp += 10 * (tptr->tm_min / 6); + temp += 100 * tptr->tm_hour; + } + } + break; + default: temp = 0; break; + } + temp &= AMASK; + for(i = 0; i<= 4; i++) { + sim_interval --; + ch = temp % 10; + temp /= 10; + if (ch == 0) + ch = 10; + ReplaceMask(CAR, ch, 017); + DownReg(CAR); + } + break; + + case OP_SWM: + SetBit(AAR, WM); + DownReg(AAR); + SetBit(BAR, WM); + DownReg(BAR); + sim_interval -= 4; + break; + + case OP_CWM: + ClrBit(AAR, WM); + DownReg(AAR); + ClrBit(BAR, WM); + DownReg(BAR); + sim_interval -= 4; + break; + + case OP_CS: + /* Clear memory until BAR equal xxx99 */ + do { + WriteP(BAR, 0); + sim_interval -= 2; + if ((BAR & AMASK) == 0) { + if (CPU_MODEL == 1) + BAR = 15999; + else + BAR = MAXMEMSIZE-1; + break; + } + BAR--; + } while (((BAR & AMASK) % 100) != 99); + /* If two address, do branch */ + if (state > 6) + jump = 1; + break; + + case OP_H: + if (state > 2) + jump = 1; + reason = STOP_HALT; + break; + + /* Treat invalid op as a NOP */ + default: + reason = STOP_UUO; + /* Fall through */ + + case OP_NOP: + /* Skip until next word mark */ + while((FetchP(IAR) & WM) == 0 && fault == 0) { + sim_interval -= 2; + UpReg(IAR); + } + break; + + case OP_MOV: + + /* Set terminate to false */ + sign = 1; + while(sign) { + sim_interval -= 4; + ar = ReadP(AAR); + STAR = BAR; + br = ReadP(BAR); + + /* Adjust addresses. */ + if (op_mod & 010) { + UpReg(AAR); + UpReg(BAR); + } else { + DownReg(AAR); + DownReg(BAR); + } + + switch(op_mod & 070) { + case 020: /* A, No B or 8 bit */ + if (ar & WM) /* 1st WM - A-field */ + sign = 0; + break; + case 040: /* B, no 8 or A bit */ + if (br & WM) /* 1st WM - B-field */ + sign = 0; + break; + case 010: /* No A or B, 8 bit */ + case 060: /* B and A bit, no 8 bit */ + if (ar & WM || br & WM) /* 1st WM - A or B-field */ + sign = 0; + break; + case 030: /* A & 8 bit, No B */ + if ((ar & 077) == CHR_RM) /* 1st RM - A-field */ + sign = 0; + break; + case 050: /* B and 8, no A bit */ + if ((ar & 0277) == (CHR_GM|WM)) /* 1st GM,WM - A-field*/ + sign = 0; + break; + case 070: /* B and A and 8 bit */ + /* 1st RM or GM,WM - A-field */ + if ((ar & 077) == CHR_RM || (ar & 0277) == (CHR_GM|WM)) + sign = 0; + break; + case 000: /* No A or B or 8 bit */ + sign = 0; /* After one position */ + break; + } + /* Copy bits */ + if (op_mod & 001) { + br &= ~0xf; + br |= ar & 0xf; + } + if (op_mod & 002) { + br &= ~0x30; + br |= ar & 0x30; + } + if (op_mod & 004) { + br &= ~WM; + br |= ar & WM; + } + /* Restore value */ + WriteP(STAR, br); + } + break; + + case OP_MSZ: + ar = ReadP(AAR); /* First character, no zone, force WM */ + WriteP(BAR, (ar & 017) |WM); + DownReg(AAR); + DownReg(BAR); + t = 1; /* Suppress zeros. */ + sim_interval -= 4; + while ((ar & WM) == 0) { /* Copy record */ + ar = ReadP(AAR); + WriteP(BAR, ar & 077); + sim_interval -= 4; + DownReg(AAR); + DownReg(BAR); + } + /* Scan backward from end to Word Mark suppressing zeros */ + UpReg(BAR); + br = ReadP(BAR); /* Forward one */ + sim_interval -= 2; + while(1) { + ch = br & 077; + if (ch > 0 && ch < 10) + t = 0; + else if (ch == 0 || ch == 10 || ch == CHR_COM) + ch = (t)?0:ch; /* B blank, zero, comma */ + else if (ch != CHR_MINUS && ch != CHR_DOT) + t = 1; /* B - or . */ + WriteP(BAR, ch); + UpReg(BAR); + if (br & WM) + break; + br = ReadP(BAR); /* Forward one */ + } + break; + + case OP_C: + cind = 2; /* Set equal */ + do { + /* scan digits until A or B word mark */ + ar = ReadP(AAR); + br = ReadP(BAR); + sim_interval -= 4; + sign = cmp_order[br & 077] - cmp_order[ar & 077]; + if (sign > 0) + cind = 4; + else if (sign < 0) + cind = 1; + DownReg(AAR); + DownReg(BAR); + } while ((br & WM) == 0 && (ar & WM) == 0); + if ((br & WM) == 0 && (ar & WM)) + cind = 4; + break; + + case OP_T: + /* Check opcode */ + if ((op_mod & 070) != 0) { + reason = STOP_UUO; + break; + } + cind = 2; + qsign = 1; /* Set unit/body */ + CAR = AAR; + ar = ReadP(AAR); + DownReg(AAR); + while(1) { + /* Scan digits until A or B word mark */ + sim_interval -= 4; + ZeroAddr(AAR); + br = ReadP(BAR); + DownReg(BAR); + if (qsign) { + sign = cmp_order[br & 077] - cmp_order[ar & 077]; + if (sign > 0) + cind = 4; + else if (sign < 0) + cind = 1; + } + /* Hit end of search argument */ + if (ar & WM) { + if (cind & op_mod) /* Check if match */ + break; + if (br & WM) { + AAR = CAR; + ar = ReadP(AAR); + DownReg(AAR); + qsign = 1; /* Set unit/body */ + cind = 2; + } else + qsign = 0; + } else if (br & WM) { /* Found end of table */ + cind = 4; + break; + } else { + ar = ReadP(AAR); + DownReg(AAR); + } + } + break; + + case OP_E: + cy = 0x10; /* latchs */ + /* 1 Supress zero latch */ + /* 2 Decimal latch */ + /* 4 * Fill latch */ + /* 8 $ Fill latch */ + /* 0x10 Unit latch */ + /* 0x20 Body latch */ + /* 0x40 Ext latch */ + ar = ReadP(AAR); + DownReg(AAR); + sim_interval -= 2; + sign = (ar & 060) == 040; + ch = ar & 017; + /* First scan cycle */ + while (1) { + br = ReadP(STAR = BAR); + DownReg(BAR); + sim_interval -= 2; + if (cy & 0x40) + ch = br & 077; + switch (br & 077) { + case CHR_MINUS: /* - */ + case CHR_C: /* C */ + case CHR_R: /* R */ + if (sign || cy & 0x20) /* - or body */ + WriteP(STAR, br & 077); + else + WriteP(STAR, 0); + break; + case CHR_COM: /* , */ + if (cy & 0x40) + WriteP(STAR, 0); + else + WriteP(STAR, br & 077); + break; + case CHR_PLUS: /* & */ + WriteP(STAR, 0); + break; + case CHR_DOL: /* $ */ + case CHR_STAR: /* * */ + if ((cy & 0x20) == 0) { /* not body, skip */ + WriteP(STAR, br & 077); + break; + } + if ((cy & 0xd) == 1) { /* Set fill flag */ + cy |= ((br & 077) == CHR_DOL)?0x8:0x4; + } + case CHR_0: /* 0 */ + /* Supression off */ + if ((br & 077) == CHR_0 && (cy & 1) == 0) { + ch |= WM; + cy |= 1; /* Set on */ + } + case CHR_ABLANK: /* blank */ + WriteP(STAR, ch); + if ((br & WM) == 0) { + if (ar & WM) { + cy &= ~0x70; /* Set Ext */ + cy |= 0x40; + } else { + ar = ReadP(AAR); /* Set Body */ + DownReg(AAR); + ch = ar & 077; + cy &= ~0x70; + cy |= 0x20; + } + } + break; + default: + WriteP(STAR, br & 077); /* Clear word mark */ + break; + } + if (br & WM) + break; + } + /* A */ + /* If suppression off and first char not zero stop */ + if ((cy & 0x1) == 0 && (ReadP(BAR) & 077) != CHR_0) + break; + UpReg(BAR); + /* Do second scan */ + while (1) { + br = ReadP(STAR = BAR); + UpReg(BAR); + sim_interval -= 2; + ch = br & 077; + switch (ch) { + case 1: case 2: case 3: case 4: case 5: + case 6: case 7: case 8: case 9: + cy &= ~1; /* Turn off suppression latch */ + break; + case CHR_COM: /* , */ + if ((cy & 3) == 2) { /* Decimal suppress */ + ch = (cy & 0x4)?CHR_STAR:0; /* * or blank */ + } + case CHR_0: /* 0 */ + case CHR_ABLANK: /* blank */ + if ((cy & 3) == 1) { /* Supress, no dec */ + ch = (cy & 0x4)?CHR_STAR:0; /* * or blank */ + } + break; + case CHR_DOT: /* . */ + if (cy & 1) + cy |= 2; /* Set dec */ + case CHR_MINUS: /* - */ + break; + default: + if ((cy & 0x3) == 0) + cy |= 1; /* Set 0 if not dec */ + break; + } + WriteP(STAR, ch); /* Store char back */ + if (br & WM) + break; + } + /* Dec not set & $ fill or zero and $ fill not set */ + if ((cy & 0xA) == 0 || (cy & 0xB) == 2 || + ((cy & 0xB) == 3 && ch == CHR_MINUS)) + break; + DownReg(BAR); + /* Third scan pass */ + while(1) { + ch = ReadP(STAR = BAR) & 077; + DownReg(BAR); + sim_interval -= 2; + if (ch == 0) { + if (cy & 0x4) { + WriteP(STAR, CHR_STAR); /* * */ + } else if (cy & 0x8) { + WriteP(STAR, CHR_DOL); /* $ */ + break; /* Stop after $ */ + } + } else if (ch == CHR_0) { /* 0 */ + if (cy & 1) { + WriteP(STAR, (cy & 04)?CHR_STAR:0); /* * */ + } + } else if (ch == CHR_DOT) { /* . */ + if (cy & 1) { + WriteP(STAR, (cy & 04)?CHR_STAR:0); /* * */ + break; + } + if ((cy & 0xA) == 0xA) /* Both . and $ set */ + break; + } + } + break; + + case OP_B: + switch(op_mod) { + case CHR_ABLANK: /* 1401 same */ + jump = 1; + break; + case CHR_Z: /* Z Arith overflow */ /* 1401 same */ + jump = oind; + oind = 0; + break; + case CHR_S: /* S Equal */ /* 1401 same */ + jump = (cind == 2); + break; + case CHR_U: /* U High */ /* 1401 same */ + jump = (cind == 4); + break; + case CHR_T: /* T Low */ /* 1401 same */ + jump = (cind == 1); + break; + case CHR_SLSH: /* / High or Low */ /* 1401 same */ + jump = (cind != 2); + break; + case CHR_W: /* W Divide overflow */ + jump = dind; + dind = 0; + break; + case CHR_V: /* V Zero Balence */ + jump = zind; + break; + case CHR_X: /* X floating point */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) + reason = STOP_UUO; + jump = euind; + euind = 0; + break; + case CHR_Y: /* Y floating point */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) + reason = STOP_UUO; + jump = eoind; + eoind = 0; + break; + case CHR_K: /* K Tape indicator */ + /* 1401 end of real */ + if (CPU_MODEL == 1) { + jump = chan_stat(1, CHS_EOF|CHS_EOT); + } else if (tind) { + jump = 1; + tind = 0; + } else { + for(i = 1; i <= NUM_CHAN && jump == 0; i++) + jump = chan_stat(i, STA_PEND); + if (jump) + sim_debug(DEBUG_CMD, &cpu_dev, "Tape Ind\n"); + } + break; + case CHR_Q: /* Q Inq req ch 1 */ + jump = inquiry; + break; + case CHR_STAR: /* * Inq req ch 2 */ + break; + case CHR_1: /* 1 Overlap in Proc Ch 1 */ + jump = ((chan_io_status[1] & 0300) == 0200) && + chan_active(1); + break; + case CHR_2: /* 2 Overlap in Proc Ch 2 */ + jump = ((chan_io_status[2] & 0300) == 0200) && + chan_active(2); + break; + case CHR_4: /* 4 Channel 3 */ + jump = ((chan_io_status[3] & 0300) == 0200) && + chan_active(3); + break; + case CHR_RPARN: /* ) Channel 4 */ + jump = ((chan_io_status[4] & 0300) == 0200) && + chan_active(4); + break; + case CHR_9: /* 9 Carriage 9 CH1 */ /* 1401 same */ + jump = lpr_chan9[1]; + break; + case CHR_EXPL: /* ! Carriage 9 CH2 */ + /* 1401 punch error */ + jump = lpr_chan9[2]; + break; + case CHR_R: /* R Carriage Busy CH1 */ /* 1401 same */ + /* Try to start command */ + switch (chan_cmd(010200, IO_TRS << 8, 0)) { + case SCPE_BUSY: + jump = 1; + break; + case SCPE_OK: + case SCPE_NODEV: + case SCPE_IOERR: + break; + } + break; + case CHR_L: /* L Carriage Busy CH2 */ + /* 1401 tape error */ + if (CPU_MODEL == 1) { + jump = chan_stat(1, CHS_ERR); + break; + } + /* Try to start command */ + switch (chan_cmd(020200, IO_TRS << 8, 0)) { + case SCPE_BUSY: + jump = 1; + break; + case SCPE_OK: + case SCPE_NODEV: + case SCPE_IOERR: + break; + } + break; + case CHR_QUOT: /* @ Cariage Overflow 12 CH 1 */ + /* 1401 same */ + jump = lpr_chan12[1]; + break; + case CHR_LPARN: /* sq Cariage Overflow 12 CH 2 */ + /* 1401 process check switch off */ + jump = lpr_chan12[2]; + break; + case CHR_A: /* 1401 sense switch A */ + if (CPU_MODEL == 1) { + jump = (SW & 0x01) | (io_flags & 010); + io_flags &= ~010; + } else { + reason = STOP_UUO; + } + break; + case CHR_B: /* 1401 sense switch B */ + if (CPU_MODEL == 1) { + jump = SW & 0x02; + } else { + reason = STOP_UUO; + } + break; + case CHR_C: /* 1401 sense switch C */ + if (CPU_MODEL == 1) { + jump = SW & 0x04; + } else { + reason = STOP_UUO; + } + break; + case CHR_D: /* 1401 sense switch D */ + if (CPU_MODEL == 1) { + jump = SW & 0x08; + } else { + reason = STOP_UUO; + } + break; + case CHR_E: /* 1401 sense switch E */ + if (CPU_MODEL == 1) { + jump = SW & 0x10; + } else { + reason = STOP_UUO; + } + break; + case CHR_F: /* 1401 sense switch F */ + if (CPU_MODEL == 1) { + jump = SW & 0x20; + } else { + reason = STOP_UUO; + } + break; + case CHR_G: /* 1401 sense switch G */ + if (CPU_MODEL == 1) { + jump = SW & 0x40; + } else { + reason = STOP_UUO; + } + break; + case CHR_QUEST: /* 1401 reader error */ + if (CPU_MODEL == 1) { + jump = io_flags & 1; + io_flags &= ~01; + } else { + reason = STOP_UUO; + } + break; + case CHR_RM: /* 1401 print error */ + if (CPU_MODEL == 1) { + jump = io_flags & 2; + io_flags &= ~02; + } else { + reason = STOP_UUO; + } + break; + case CHR_I: /* 1401 punch error */ + if (CPU_MODEL == 1) { + jump = io_flags & 4; + io_flags &= ~04; + } else { + reason = STOP_UUO; + } + break; + } + break; + + case OP_BCE: + sim_interval -= 2; + cind = 2; /* Set equal */ + sign = cmp_order[ReadP(BAR) & ~WM] - cmp_order[op_mod]; + if (sign > 0) + cind = 4; + else if (sign < 0) + cind = 1; + if (cind == 2) + jump = 1; + DownReg(BAR); + break; + + case OP_BBE: + sim_interval -= 2; + if (ReadP(BAR) & op_mod) + jump = 1; + DownReg(BAR); + break; + + case OP_BWE: + sim_interval -= 2; + br = ReadP(BAR); + if (((op_mod & 01) && (br & WM)) || + ((op_mod & 02) && (br & 060) == (op_mod & 060))) + jump = 1; + DownReg(BAR); + break; + + case OP_RD: + case OP_RDW: + /* Decode operands */ + /* X1 digit 1 == channel 034 % non-overlap */ + /* 2 == channel 074 sq non-overlap */ + /* 3 == channel 072 ? non-overlap */ + /* 4 == channel 052 ! non-overlap */ + /* 1 == channel 014 @ overlap */ + /* 2 == channel 054 * overlap */ + /* X2 digit device */ + /* 1 == Reader 001 */ + /* 2 == Printer 002 */ + /* 4 == Punch 004 */ + /* U == Tape BCD 024 */ + /* B == Tape Binary 062 */ + /* T == Console 023 */ + /* F == Disk 066 */ + /* K == Com 042 */ + /* X3 digit device option or unit number */ + /* op_mod R == Read 051 */ + /* $ == Read 053 ignore word/group */ + /* W == Write 026 */ + /* X == Write 027 ignore word/group */ + /* Q == Nop 050 input */ + /* V == Nop 025 output */ + switch ((XR >> 12) & 077) { + case CHR_RPARN: ch = 011; break; /* %/( 1 - non-overlap */ + case CHR_LPARN: ch = 012; break; /* sq 2 - non-overlap */ + case CHR_QUEST: ch = 013; break; /* ? 3 - non-overlap */ + case CHR_EXPL: ch = 014; break; /* ! 4 - non-overlap */ + case CHR_QUOT: ch = 001; break; /* @/' 1 - overlap */ + case CHR_STAR: ch = 002; break; /* * 2 - overlap */ + case CHR_DOL: ch = 003; break; /* $ 3 - overlap */ + case CHR_EQ: ch = 004; break; /* = 4 - overlap */ + default: ch = 0; + reason = STOP_IOCHECK; + break; + } + + temp = ch << 12; + if ((XR & 07700) == 06200) { + if ((XR & 017) != 10) + temp |= XR & 017; + temp |= 02420; + } else if ((XR & 07700) == 02400) { + if ((XR & 017) != 10) + temp |= XR & 017; + temp |= 02400; + } else { + temp |= XR & 07777; + } + + + switch(op_mod) { + case CHR_R: t = (IO_RDS << 8); break; /* R */ + case CHR_DOL: t = (IO_RDS << 8) | 0100; break; /* $ */ + case CHR_W: t = (IO_WRS << 8); break; /* W */ + case CHR_X: t = (IO_WRS << 8) | 0100; break; /* X */ + case CHR_Q: t = (IO_TRS << 8); ch &= 07; break; /* Q */ + case CHR_V: t = (IO_TRS << 8) | 0100; ch &= 07; break; /* Y */ + case CHR_S: t = (IO_TRS << 8); break; /* S sense */ + case CHR_C: t = (IO_CTL << 8); break; /* C control */ + default: t = 0; + reason = STOP_UUO; + break; + } + if (reason != 0) + break; + + while (chan_active(ch & 07) && reason == 0) { + sim_interval = 0; + reason = sim_process_event(); + chan_proc(); + } + if (reason != 0) + break; + + if (op == OP_RDW) + t |= 0200; + if ((ch & 010) == 0) + t &= ~0100; /* Can't be overlaped */ + + /* Try to start command */ + switch (chan_cmd(temp, t, BAR & AMASK)) { + case SCPE_OK: + if (ch & 010) { + chan_io_status[ch & 07] = 0; + chwait = ch & 07; + chan_irq_enb[ch & 7] = 0; + } else { + chan_io_status[ch & 07] = IO_CHS_OVER; + chan_irq_enb[ch & 7] = 1; + } + sim_debug(DEBUG_CMD, &cpu_dev, + "%d %c on %o %o %s %c\n", IAR, sim_six_to_ascii[op], + ch & 07, temp, + (ch & 010)?"":"overlap", + sim_six_to_ascii[op_mod]); + break; + case SCPE_BUSY: + sim_debug(DEBUG_CMD, &cpu_dev, + "%d %c Busy on %o %s %c %o\n", IAR, + sim_six_to_ascii[op], ch & 07, + (ch & 010)?"": "overlap", + sim_six_to_ascii[op_mod], chan_io_status[ch & 07]); + chan_io_status[ch & 07] = IO_CHS_BUSY; + break; + case SCPE_NODEV: + case SCPE_IOERR: + chan_io_status[ch & 07] = IO_CHS_NORDY; + break; + } + if (CPU_MODEL == 1) + chan_io_status[ch & 07] &= 0177; + break; + + case OP_CC1: + t = (IO_CTL << 8); + temp = 010200 | op_mod; + ch = 1; + chan_io: + switch (chan_cmd(temp, t, 0)) { + case SCPE_OK: + chan_io_status[ch & 07] = 0000; + if (ch & 010) + chwait = (ch & 07) | 040; + chan_irq_enb[ch & 7] = 0; + break; + case SCPE_BUSY: + chan_io_status[ch & 07] = IO_CHS_BUSY; + break; + case SCPE_NODEV: + case SCPE_IOERR: + chan_io_status[ch & 07] = IO_CHS_NORDY; + break; + } + break; + + case OP_CC2: + t = (IO_CTL << 8); + temp = 020200 | op_mod; + ch = 2; + goto chan_io; + + case OP_SSF1: + t = (IO_CTL << 8); + temp = 010100 | op_mod; + ch = 1; + goto chan_io; + + case OP_SSF2: + t = (IO_CTL << 8); + temp = 020100 | op_mod; + ch = 2; + goto chan_io; + + case OP_UC: + switch ((XR >> 12) & 077) { + case CHR_RPARN: ch = 011; break; /* %/) 1 - non-overlap */ + case CHR_LPARN: ch = 012; break; /* sq 2 - non-overlap */ + case CHR_QUEST: ch = 013; break; /* ? 3 - non-overlap */ + case CHR_EXPL: ch = 014; break; /* ! 4 - non-overlap */ + case CHR_QUOT: ch = 001; break; /* @/' 1 - overlap */ + case CHR_STAR: ch = 002; break; /* * 2 - overlap */ + case CHR_DOL: ch = 003; break; /* $ 3 - overlap */ + case CHR_EQ: ch = 004; break; /* = 4 - overlap */ + default: ch = 0; + reason = STOP_IOCHECK; + break; + } + temp = ch << 12; + if ((XR & 07700) != 02400 && (XR & 07700) != 06200) { + reason = STOP_UUO; + break; + } + if ((XR & 017) != 10) + temp |= XR & 017; + temp |= 02400; + t = 0; + switch(op_mod) { + case CHR_B: t = (IO_BSR << 8); ch |= 010; break; + case CHR_A: t = (IO_SKR << 8); ch |= 010; break; + case CHR_R: t = (IO_REW << 8); ch |= 010; break; + case CHR_GT: t = (IO_RUN << 8); ch |= 010; break; + case CHR_E: t = (IO_ERG << 8); ch |= 010; break; + case CHR_M: t = (IO_WEF << 8); break; + default: t = 0; reason = STOP_UUO; break; + } + + while (chan_active(ch & 07) && reason == 0) { + sim_interval = 0; + reason = sim_process_event(); + chan_proc(); + } + if (reason != 0) + break; + /* For nop, set command done */ + if (t == 0) { + chan_io_status[ch & 07] = 0000; + break; + } + /* Issue command */ + switch (chan_cmd(temp, t, 0)) { + case SCPE_OK: + chan_io_status[ch & 07] = 0000; + if (ch & 010) { + chwait = (ch & 07) | 040; + } else if (op_mod == CHR_M) { + chan_io_status[ch & 07] = IO_CHS_OVER; + } + chan_irq_enb[ch & 7] = 0; + sim_debug(DEBUG_CMD, &cpu_dev, + "%d UC on %o %o %s %c %o\n", IAR, ch & 07, temp, + (ch & 010)?"": "overlap", + sim_six_to_ascii[op_mod], chan_io_status[ch & 07]); + + break; + case SCPE_BUSY: + chan_io_status[ch & 07] = IO_CHS_BUSY; + break; + case SCPE_NODEV: + case SCPE_IOERR: + chan_io_status[ch & 07] = IO_CHS_NORDY; + break; + } + if (CPU_MODEL == 1) + chan_io_status[ch & 07] &= 0177; + sim_interval -= 100; + break; + + case OP_IO1: + /* Wait for channel to finish before continuing */ + ch = 1; + checkchan: + chan_proc(); + if (chan_io_status[ch] & op_mod) { + jump = 1; + } + chan_io_status[ch] &= 077; + sim_debug(DEBUG_CMD, &cpu_dev, "Check chan %d %o %x\n", ch, + chan_io_status[ch], chan_flags[ch]); + break; + + case OP_IO2: + ch = 2; + goto checkchan; + + case OP_IO3: + ch = 3; + goto checkchan; + + case OP_IO4: + ch = 4; + goto checkchan; + + case OP_FP: + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + /* Check if over the top */ + ValidAddr(AAR); + /* AAR pointes to exponent of FP */ + /* BAR point to FP register locations 280 - 299 */ + BAR = 299; + if (hst_lnt) /* History enabled? */ + hst[hst_p].bstart = BAR; + switch(op_mod) { + case CHR_R: /* R - Floating Reset Add */ + /* Copy exponent to accumulator */ + zind = 1; + ar = ReadP(AAR); + DownReg(AAR); + if ((ar & 060) != 040) + ar |= 060; + else { + ar |= 040; + ar &= 057; + } + WriteP(BAR--, bin_bcd[bcd_bin[ar & 0xf]] | (ar & 060)); + sim_interval -= 4; + ar = ReadP(AAR); + DownReg(AAR); + WriteP(BAR--, bin_bcd[bcd_bin[ar & 0xf]] | (ar & (WM|060))); + /* Prepare to copy rest. */ + br = ReadP(STAR = BAR--) & WM; + sim_interval -= 4; + ar = ReadP(AAR); + DownReg(AAR); + while (1) { + WriteP(STAR, ar); + if ((ar & 0xf) != 10) /* Update zero flag */ + zind = 0; + if (ar & WM) /* Done yet? */ + break; + if (BAR == 279) /* Stop at lower limit */ + break; + sim_interval -= 4; + ar = ReadP(AAR); + DownReg(AAR); + br = ReadP(STAR = BAR--) & WM; + } + SetBit(STAR, WM); + break; + + case CHR_L: /* L - Floating store */ + /* Copy two digit exponent */ + br = ReadP(BAR--); + if ((br & 060) != 040) + br |= 060; + else { + br &= 017|WM; + br |= 040; + } + WriteP(AAR, bin_bcd[bcd_bin[br & 0xf]] | (br & 060)); + DownReg(AAR); + br = ReadP(BAR--); + WriteP(AAR, bin_bcd[bcd_bin[br & 0xf]] | (br & (WM|060))); + DownReg(AAR); + sim_interval -= 4; + /* Copy digits until A or B word mark */ + zind = 1; + ar = ReadP(STAR = AAR) & WM; + DownReg(AAR); + br = ReadP(BAR--); + while (1) { + WriteP(STAR, br); + if ((br & 0xf) != 10) /* Update zero flag */ + zind = 0; + if (br & WM || ar & WM || BAR == 279) + break; + sim_interval -= 4; + ar = ReadP(STAR = AAR) & WM; + DownReg(AAR); + br = ReadP(BAR--); + } + SetBit(STAR, WM); + break; + + case CHR_S: /* S - Floating sub */ + case CHR_A: /* A - Floating add */ + zind = 1; + /* Compute A exponent */ + ar = ReadP(AAR); + DownReg(AAR); + qsign = (ar & 060) == 040; + cy = bcd_bin[ar & 0xf]; + ar = ReadP(AAR); + DownReg(AAR); + cy += 10 * bcd_bin[ar & 0xf]; + if (qsign) + cy = -cy; + /* Compute B exponent */ + br = ReadP(BAR--); + sign = (br & 060) == 040; + temp = bcd_bin[br & 0xf]; + br = ReadP(BAR--); + temp += 10 * bcd_bin[br & 0xf]; + if (sign) + temp = -temp; + sim_interval -= 10; + temp -= cy; + ar = ReadP(AAR); + DownReg(AAR); + sign = (ar & 060) == 040; + if (temp == 0) /* Same go add */ + goto fadd; + if (temp > 17) { /* Normalize */ + /* Move BAR to just below WM */ + do { + br = ReadP(BAR--); + } while((br & WM) == 0); + goto fnorm; + } + if (temp < -17) { /* Copy A to ACC */ + fcopy: + BAR = 299; /* Copy exponent */ + if (cy < 0) + cy = -cy; + WriteP(BAR--, bin_bcd[cy % 10] | ((qsign)? 040: 060)); + WriteP(BAR--, bin_bcd[cy / 10] | WM); + br = ReadP(STAR = BAR--) & WM; + /* Flip sign if doing subtract */ + if (op_mod == CHR_S) { + ar &= WM|017; + ar |= sign?060:040; + } + while (1) { + WriteP(STAR, ar); + if ((ar & 0xf) != 10) /* Update zero flag */ + zind = 0; + if (br & WM || ar & WM) + break; + if (BAR == 280) + SetBit(BAR, WM); + sim_interval -= 4; + ar = ReadP(AAR); + DownReg(AAR); + br = ReadP(STAR = BAR--) & WM; + } + SetBit(STAR, WM); + goto fnorm; + } + + if (temp > 0) { /* Shift A */ + while (temp-- > 0 && (ar & WM) == 0) { + sim_interval -= 2; + ar = ReadP(AAR); + DownReg(AAR); + } + if (ar & WM && temp != 0) { + /* Move BAR to just below WM */ + while((ReadP(BAR--) & WM) == 0); + goto fnorm; + } + } else { /* Shift B */ + ix = br = ReadP(BAR--); + while (temp++ < 0) { + if (br & WM || BAR == 279) + break; + sim_interval -= 2; + br = ReadP(BAR--); + } + if (br & WM && temp < 0) { + /* Copy exponent to ACC first */ + BAR = 299; + if (cy < 0) + cy = -cy; + WriteP(BAR--, bin_bcd[cy % 10] | ((qsign)? 040: 060)); + WriteP(BAR--, bin_bcd[cy / 10] | WM); + sim_interval -= 4; + goto fcopy; + } + DAR = 297; + /* Copy B up */ + while(1) { + WriteP(DAR--, (br & 017) | (ix & 060)); + ix = 0; + if (br & WM || BAR == 279) + break; + br = ReadP(BAR--); + } + /* Zero fill new locations */ + while(DAR != BAR) + ReplaceMask(DAR--, 10, 077); + /* Copy A exponent */ + BAR = 299; + if (cy < 0) + cy = -cy; + WriteP(BAR--, bin_bcd[cy % 10] | ((qsign)? 040: 060)); + WriteP(BAR--, bin_bcd[cy / 10] | WM); + } + fadd: + if (op_mod == CHR_S) + sign ^= 1; /* Change sign for subtract oper */ + zind = 1; + DAR = BAR; + sim_interval -= 2; + if ((ReadP(297) & 060) == 040) + sign ^= 1; + cy = sign; + br = ReadP(STAR = BAR--); + + ix = 0; + /* Add until word mark on A or B */ + while(1) { + ix |= ar & WM; + ch = bcd_bin[ar & 0xf]; + ch = bcd_bin[br& 0xf] + ((sign)? (9 - ch):ch) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + if (ch != CHR_0) /* Clear zero */ + zind = 0; + WriteP(STAR, (br & 0360) | ch); + if (br & WM || BAR == 279) + break; + if (ix) + ar = CHR_0; + else { + ar = ReadP(AAR); + DownReg(AAR); + sim_interval -= 2; + } + br = ReadP(STAR = BAR--); + sim_interval -= 4; + } + + /* If cy and qsign, tens-compliment result and flip sign */ + if (sign && cy == 0) { + STAR = BAR = DAR; + br = ReadP(BAR--); + sim_interval -= 2; + if ((br & 060) == 040) + br |= 060; + else { + br &= ~020; /* Switch B sign */ + br |= 040; + } + zind = 1; + cy = 1; + /* Compliment until B word mark */ + while(1) { + ch = (9 - bcd_bin[br& 0xf]) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + if (ch != CHR_0) /* Clear zero */ + zind = 0; + WriteP(STAR, (br & 0360) | ch); + if (br & WM) + break; + sim_interval -= 2; + br = ReadP(STAR = BAR--); + } + } + + /* If carry fix exponent and shift result */ + if ((sign == 0 && cy) || ix == 0) { + BAR = 299; + eoind = do_addint(1); + /* Now shift mantissa right one */ + br = ReadP(STAR = BAR--); + ar = ReadP(BAR); + while ((br & WM) == 0) { + WriteP(STAR, (ar & 017) | (br & 060)); + if (BAR == 279) + break; + sim_interval -= 4; + br = ReadP(STAR = BAR--); + ar = ReadP(BAR); + } + WriteP(STAR, WM|1); /* New high order 1 + WM */ + zind = 0; + } + fnorm: + temp = 0; + DAR = BAR; + br = ReadP(++BAR) & 077; + zind = 1; + while ((br & WM) == 0) { + if ((br & 017) != 10) { + zind = 0; + break; + } + temp++; + br = ReadP(++BAR); + } + if (br & WM) { /* Zero result, set exponent to zero */ + SetBit(BAR-1, 060); /* Force plus */ + WriteP(BAR++, WM | 9); + WriteP(BAR, 040 | 9); + break; + } + if (temp > 0) { /* Need to shift it temp places */ + ar = ReadP(++DAR); + while (1) { + WriteP(DAR, (ar & WM) | (br & 017)); + ar = ReadP(++DAR); + br = ReadP(++BAR); + if (br & WM) + break; + } + while(DAR != BAR) { + ReplaceMask(DAR++, 10, 017); + } + /* Adjust exponent */ + BAR = 299; + if (do_addint(-temp)) { + undfacc: + euind = 1; + zerofacc: + zind = 1; + /* Move zero to accumulator */ + BAR=299; + WriteP(BAR--, 040 | 9); + WriteP(BAR--, WM | 9); + br = ReadP(BAR) | 060; + while(1) { + WriteP(BAR--, (br & (WM|060)) | 10); + if (br & WM) + break; + br = ReadP(BAR) & WM; + } + } + } + break; + + case CHR_M: /* M - Floating mul */ + temp = oind; + oind = 0; + reason = do_addsub(0); + ch = oind; + oind = temp; + if (reason != SCPE_OK) + break; + if (ch) { + zind = 0; + if ((ReadP(299) & 060) == 040) + goto undfacc; + eoind = 1; + break; + } + CAR = AAR; + DAR = 279; + /* Scan for A word mark */ + qsign = 1; + ar = ReadP(AAR); + DownReg(AAR); + while (1) { + if ((ar & 017) != 10) + qsign = 0; + ClrBit(DAR--, WM); /* Clear word marks */ + if (ar & WM || AAR == 0) + break; + ar = ReadP(AAR); + DownReg(AAR); + sim_interval -= 4; + }; + + ClrBit(DAR--, WM); /* Extra zero */ + if (qsign) + goto zerofacc; + + /* Scan for B word mark */ + zind = 1; + br = ReadP(BAR--); + while (1) { + if ((br & 017) != 10) + zind = 0; + WriteP(DAR--, br); + if (br & WM || BAR == 279) + break; + br = ReadP(BAR--); + sim_interval -= 2; + }; + + /* If B zero, scan to A word mark and set B zero */ + if (zind || qsign) + goto zerofacc; + + temp = BAR; /* Save for later */ + BAR = 279; + AAR = CAR; + reason = do_mult(); + if (reason != SCPE_OK) + break; + + /* Count number of leading zeros */ + ix = 0; + BAR++; /* Skip first zero */ + while (BAR != 280) { + br = ReadP(++BAR); + if ((br & 017) != 10) + break; + ix++; + } + if (ix != 0) { + DAR = BAR; + BAR = 299; + if(do_addint(-ix)) + goto undfacc; + BAR = DAR; + } + /* Find end of result */ + CAR = 297; + ar = ReadP(CAR--); + while((ar & WM) == 0) + ar = ReadP(CAR--); + br = (ReadP(BAR) & 017) | WM; + /* Copy result */ + while(CAR != 297 && BAR != 279) { + WriteP(++CAR, br); + br = ReadP(++BAR) & 017; + } + while(CAR != 297) /* Zero fill rest */ + WriteP(++CAR, 10); + SetBit(297, ReadP(279) & 060); /* Copy sign */ + break; + + case CHR_D: /* D - Floating div */ + temp = oind; + oind = 0; + reason = do_addsub(1); + BAR = 299; + ch = oind; + sign = do_addint(1); /* Add 1 to exp */ + oind = temp; + if (reason != SCPE_OK) + break; + + CAR = AAR; + br = ReadP(BAR--); + ar = ReadP(AAR); + DownReg(AAR); + /* Scan for B word mark */ + qsign = 1; + zind = 1; + while (1) { + if ((ar & 017) != 10) + qsign = 0; + if ((br & 017) != 10) + zind = 0; + if (br & WM || BAR == 279) + break; + if (ar & WM || AAR == 0) + break; + br = ReadP(BAR--); + ar = ReadP(AAR); + DownReg(AAR); + sim_interval -= 4; + }; + + /* Are fractions same size? */ + if ((br & WM) && (ar & WM) == 0) + goto zerofacc; + + /* Is A zero? */ + if (qsign) { + if (ch || sign) { + eoind = 1; + } + dind = 1; + break; + } + + /* Copy B to work area and fill zeros for A size */ + DAR = 279; + br = ReadP(297); + /* Set sign */ + WriteP(DAR--, (br & 060) | 10); + sim_interval -= 2; + + /* Zero remainder area */ + for(i = 297 - BAR; i > 1; i--) { + WriteP(DAR--, 10); + sim_interval -= 2; + } + + /* Save unit position */ + temp = DAR; + + /* Copy accumulator to work */ + BAR = 297; + br = ReadP(BAR--); + sim_interval -= 2; + while(1) { + WriteP(DAR--, br & 017); + if (br & WM) + break; + br = ReadP(BAR--); + sim_interval -= 2; + } + + /* Two extra zeros */ + WriteP(DAR--, 10); + WriteP(DAR--, 10); + + /* Set up for divide */ + BAR = temp; + temp = DAR; + + /* Check for error conditions */ + if (zind) { + if (ch) + goto undfacc; + goto zerofacc; + } + + if (sign) { + eoind = 1; + break; + } + + if (ch) + goto undfacc; + + AAR = CAR; + /* Do actual divide */ + reason = do_divide(); + if (reason != 0) + break; + + /* Scan backward for word mark */ + qsign = ReadP(BAR+1); + sim_interval -= 2; + + /* Count number of leading zeros */ + ix = 0; + DAR = BAR+2; + CAR = temp+1; /* restore address */ + while (CAR != 280) { + br = ReadP(CAR); + sim_interval -= 2; + if ((br & 017) != 10) + break; + CAR++; + ix++; + } + + /* Adjust exponent if any leading zeros */ + if (ix != 0) { + BAR = 299; + if (do_addint(-ix)) + goto undfacc; + } + /* Find end of result */ + BAR = 297; + ar = ReadP(BAR--); + while((ar & WM) == 0) + ar = ReadP(BAR--); + temp = BAR; + br = (br & 017) | WM; + /* Copy result */ + while(BAR != 297 && CAR != DAR) { + WriteP(++BAR, br); + br = ReadP(++CAR) & 017; + sim_interval -= 4; + } + while(BAR != 297) + WriteP(++BAR, 10); + SetBit(297, qsign & 060); + BAR = temp; + break; + } + break; + + case OP_STS: /* Store CPU Status */ + /* Check if over the top */ + ValidAddr(AAR); + BAR = AAR; + ch = 0; + switch(op_mod) { + /* Restore channel status */ + case CHR_1: /* 1 */ + ch = 1; + break; + case CHR_2: /* 2 */ + ch = 2; + break; + case CHR_3: /* 3 */ + ch = 3; + break; + case CHR_4: /* 4 */ + ch = 4; + break; + /* Store channel status */ + case CHR_E: /* E - 1 */ + ch = 011; + break; + case CHR_F: /* F - 2 */ + ch = 012; + break; + case CHR_G: /* G - 3 */ + ch = 013; + break; + case CHR_H: /* H - 4 */ + ch = 014; + break; + /* Store CPU Status */ + case CHR_S: /* S store */ + br = 0; + ch = 0; + switch (cind) { + case 2: br |= 1; break; + case 4: br |= 2; break; + case 1: br |= 4; break; + } + if (zind) + br |= 8; + if (oind) + br |= 16; + if (dind) + br |= 32; + WriteP(BAR, br); + DownReg(BAR); + break; + /* Restore CPU Status */ + case CHR_R: /* R restore */ + br = ReadP(BAR); + DownReg(BAR); + ch = 0; + oind = (br & 32)?1:0; + dind = (br & 16)?1:0; + zind = (br & 8)?1:0; + cind = (br & 1)?2:0; + cind = (br & 2)?4:cind; + cind = (br & 4)?1:cind; + break; + /* Protected mode store */ + case CHR_P: /* P */ + if (cpu_unit.flags & OPTION_PROT) { + if (prot_enb /*|| reloc != 0*/) { /* Abort */ + reason = STOP_PROG; + sim_debug(DEBUG_DETAIL, &cpu_dev, "High set in prot mode\n"); + } else { + temp = bcd_bin[ReadP(BAR) & 017]; + DownReg(BAR); + temp += 10 * bcd_bin[ReadP(BAR) & 017]; + DownReg(BAR); + high_addr = 1000 * temp; + sim_debug(DEBUG_DETAIL, &cpu_dev, "High set to %d\n", high_addr); + } + } + break; + case CHR_QUEST: /* ? - 3*/ + if (cpu_unit.flags & OPTION_PROT) { + if (prot_enb || reloc != 0) { /* Abort */ + reason = STOP_PROG; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Low set in prot mode\n"); + } else { + temp = bcd_bin[ReadP(BAR) & 017]; + DownReg(BAR); + temp += 10 * bcd_bin[ReadP(BAR) & 017]; + DownReg(BAR); + low_addr = 1000 * temp; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Low set to %d\n", low_addr); + } + } + break; + default: + reason = STOP_UUO; + break; + } + if (ch) { + /* Wait for channel idle before operate */ + while (chan_active(ch & 07) && reason == 0) { + sim_interval = 0; + reason = sim_process_event(); + chan_proc(); + } + /* Do load or store channel */ + if (ch & 010) + WriteP(BAR, chan_io_status[ch & 07] & 0277); + else + chan_io_status[ch] = ReadP(BAR) & 077; + DownReg(BAR); + } + break; + + /* Priority mode operations */ + case OP_PRI: + jump = 0; + switch(op_mod) { + case CHR_U: /* U branch if ch 1 i-o unit priority */ + jump = urec_irq[1]; + urec_irq[1] = 0; + break; + case CHR_F: /* F branch if ch 2 i-o unit priority */ + jump = urec_irq[2]; + urec_irq[2] = 0; + break; + case CHR_1: /* 1 branch if ch 1 overlap priority */ + if (chan_irq_enb[1]) + jump = (chan_io_status[1] & 0300) == 0300; + break; + case CHR_2: /* 2 branch if ch 2 overlap priority */ + if (chan_irq_enb[2]) + jump = (chan_io_status[2] & 0300) == 0300; + break; + case CHR_3: /* 3 branch if ch 3 overlap priority */ + if (chan_irq_enb[3]) + jump = (chan_io_status[3] & 0300) == 0300; + break; + case CHR_4: /* 4 branch if ch 4 overlap priority */ + if (chan_irq_enb[4]) + jump = (chan_io_status[4] & 0300) == 0300; + break; + case CHR_Q: /* Q branch if inquiry ch 1 */ + jump = inquiry; + break; + case CHR_LBRK: /* * branch if inquiry ch 2 */ + break; + case CHR_N: /* N branch if outquiry ch 1 */ + break; + case CHR_TRM: /* rm branch if outquiry ch 2 */ + break; + case CHR_S: /* S branch if seek priority ch 1 */ + jump = chan_seek_done[1]; + chan_seek_done[1] = 0; + break; + case CHR_T: /* T branch if seek priority ch 2 */ + jump = chan_seek_done[2]; + chan_seek_done[2] = 0; + break; + case CHR_Y: /* Y branch if seek priority ch 3 */ + jump = chan_seek_done[3]; + chan_seek_done[3] = 0; + break; + case CHR_RPARN: /* ) branch if seek priority ch 4 */ + jump = chan_seek_done[4]; + chan_seek_done[4] = 0; + break; + case CHR_X: /* X branch and exit */ + pri_enb = 0; + sim_debug(DEBUG_PRIO, &cpu_dev, "dis irq\n"); + jump = 1; + break; + case CHR_E: /* E branch and enter */ + pri_enb = 1; + sim_debug(DEBUG_PRIO, &cpu_dev, "enb irq\n"); + jump = 1; + break; + case CHR_A: /* A branch if ch1 attention */ + jump = chan_stat(1, SNS_ATTN1); + break; + case CHR_B: /* B branch if ch2 attention */ + jump = chan_stat(2, SNS_ATTN1); + break; + case CHR_C: /* C branch if ch3 attention */ + jump = chan_stat(3, SNS_ATTN1); + break; + case CHR_D: /* D branch if ch4 attention */ + jump = chan_stat(4, SNS_ATTN1); + break; + + /* Protection mode operations */ + case CHR_QUEST: /* ? Enable protection mode */ + if (cpu_unit.flags & OPTION_PROT) { + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Prot enter %d\n", AAR & AMASK); + /* If in protect mode, abort */ + if (prot_enb) { + reason = STOP_PROG; + } else { + /* Else enter protected mode */ + prot_enb = 1; + prot_fault = 0; + jump = 1; + } + } + break; + + case CHR_9: /* 9 Leave Prot mode */ + if (cpu_unit.flags & OPTION_PROT) { + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Leave Protect mode %d %d %d\n", + AAR & AMASK, prot_enb, reloc); + /* If in protect mode, abort */ + if ((prot_enb /*|| reloc*/) /*&& (AAR & BBIT) == 0*/) { + reason = STOP_PROG; + } else { + /* Test protect mode */ + if (reloc && (AAR & BBIT) == 0) { + reason = STOP_PROG; + } else { + jump = 1; + prot_enb = 0; + reloc = 0; + high_addr = -1; + low_addr = -1; + } + } + } + break; + + case CHR_P: /* P Check Protection faults */ + if (cpu_unit.flags & OPTION_PROT) { + /* If in protect mode, abort */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Check protect fault %d %d\n", + AAR, prot_fault&1); + if (prot_enb) { + reason = STOP_PROG; + } else { + jump = prot_fault & 1; + prot_fault &= 2; /* Clear fault */ + } + } + break; + + case CHR_H: /* H Test for Prog faults */ + if (cpu_unit.flags & OPTION_PROT) { + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Check prog fault %d %d\n", + AAR, prot_fault&2); + /* If in protect mode, abort */ + if (prot_enb) { + reason = STOP_PROG; + } else { + jump = prot_fault & 2; + prot_fault &= 1; /* Clear fault */ + } + } + break; + + case CHR_SLSH: /* Enable relocation - mode */ + if (cpu_unit.flags & OPTION_PROT) { + /* If in protect mode, abort */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Enable relocation %d\n", + AAR & AMASK); + if (prot_enb) { + reason = STOP_PROG; + } else { + reloc = 1; + prot_fault = 0; + BAR = IAR; + IAR = AAR; + if ((IAR & BBIT) == 0 && low_addr >= 0) { + if (IAR < low_addr) + IAR += 100000 - low_addr; + else + IAR -= low_addr; + } + /* Fix BAR for correct return address */ + if ((BAR & BBIT) == 0 && low_addr >= 0) { + if (BAR < low_addr) + BAR += 100000 - low_addr; + else + BAR -= low_addr; + } + AAR = BAR; + } + } + break; + + case CHR_DOL: /* Enable relocation + prot mode */ + if (cpu_unit.flags & OPTION_PROT) { + /* If in protect mode, abort */ + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Enable relocation + prot %d\n", + AAR & AMASK); + if (prot_enb) { + reason = STOP_PROG; + } else { + prot_enb = 1; + reloc = 1; + prot_fault = 0; + BAR = IAR; + IAR = AAR; + if ((IAR & BBIT) == 0 && low_addr >= 0) { + if (IAR < low_addr) + IAR += 100000 - low_addr; + else + IAR -= low_addr; + } + /* Fix BAR for correct return address */ + if ((BAR & BBIT) == 0 && low_addr >= 0) { + if (BAR < low_addr) + BAR += 100000 - low_addr; + else + BAR -= low_addr; + } + AAR = BAR; + } + } + break; + + case CHR_I: /* I ???? */ + if (cpu_unit.flags & OPTION_PROT) { + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Prot opcode %02o %d\n", op_mod, AAR); + } + break; + + case CHR_GM: /* | timer release? */ + if (cpu_unit.flags & OPTION_PROT) { + jump = timer_irq; + timer_irq &= 1; + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Timer release %d\n", jump); + } + break; + case CHR_QUOT: /* ' Turn on 20ms timer */ + if (cpu_unit.flags & OPTION_PROT) { + timer_enable = 1; + timer_interval = 10; + timer_irq = 0; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Timer start\n"); + } + jump = 1; + break; + case CHR_DOT: /* . Turn off 20ms timer */ + jump = 1; + if (cpu_unit.flags & OPTION_PROT) { + timer_enable = 0; + timer_irq = 0; + sim_debug(DEBUG_DETAIL, &cpu_dev, "Timer stop\n"); + } + break; + } + break; + } + + /* Do a jump to new location. */ + if (jump) { + BAR = IAR; /* Save current for posterity */ + IAR = AAR & AMASK; + } + if (hst_lnt) { /* History enabled? */ + int len, start; + hst[hst_p].aend = AAR; + hst[hst_p].bend = BAR; + len = hst[hst_p].bend - hst[hst_p].bstart; + if (len < 0) { + len = -len; + start = hst[hst_p].bend + 1; + if (len > 50) { + start = hst[hst_p].bstart - 50; + len = 50; + } + } else { + if (len > 50) + len = 50; + start = hst[hst_p].bstart; + } + if (jump) { + len = 0; + start = hst[hst_p].bstart; + } + for(i = 0; i < len; i++) + hst[hst_p].bdata[i] = ReadP(start+i); + hst[hst_p].dlen = len; + } + } + + /* Handle protection faults */ +check_prot: + if (fault) { + reason = fault; + fault = 0; + } + + if (reason != 0 && cpu_unit.flags & OPTION_PROT && (prot_enb || reloc != 0)) { + switch(reason) { + case STOP_NOWM: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d No WM AAR=%d BAR=%d\n", IAR, AAR, BAR); + prot_fault |= 2; + reason = 0; + break; + case STOP_INVADDR: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d Inv Addr AAR=%d BAR=%d\n", IAR, AAR, BAR); + prot_fault |= 2; + reason = 0; + break; + case STOP_UUO: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d Inv Op AAR=%d BAR=%d\n", IAR, AAR, BAR); + prot_fault |= 2; + reason = 0; + break; + case STOP_INVLEN: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d Invlen Op AAR=%d BAR=%d\n", IAR, AAR, BAR); + prot_fault |= 2; + reason = 0; + break; + case STOP_IOCHECK: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d I/O Check AAR=%d BAR=%d\n", IAR, AAR, BAR); + prot_fault |= 2; + reason = 0; + break; + case STOP_PROG: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d Prog check AAR=%d BAR=%d low=%d high=%d\n", + IAR, AAR, BAR, low_addr, high_addr); + prot_fault |= 2; + reason = 0; + break; + case STOP_PROT: + sim_debug(DEBUG_DETAIL, &cpu_dev, + "IAR = %d Prot check AAR=%d BAR=%d low=%d high=%d\n", + IAR, AAR, BAR, low_addr, high_addr); + prot_fault |= 1; + reason = 0; + break; + default: /* Anything else halt sim */ + break; + } + /* If faults, B 8, otherwise stop sim */ + if (prot_fault && reason == 0) { + prot_enb = 0; + high_addr = -1; + low_addr = -1; + reloc = 0; + BAR = IAR; /* Save current for posterity */ + IAR = AAR = 8; + } + } + if (instr_count != 0 && --instr_count == 0) + return SCPE_STEP; + } /* end while */ + +/* Simulation halted */ + return reason; +} + +#define UpAddr(reg) reg++; if ((reg & AMASK) == MEMSIZE) { \ + return STOP_INVADDR; } +#define DownAddr(reg) if ((reg & AMASK) == 0) { \ + return STOP_INVADDR; } else { reg--; } + + +/* Add constant, two digits only, used by FP code */ +int do_addint(int val) { + uint8 br; + int sign; + uint8 ch; + int cy; + + br = ReadP(BAR); + sign = (br & 060) == 040; + if (val < 0) { + sign = !sign; + val = -val; + } + cy = sign; + ch = val % 10; + ch = bcd_bin[br& 0xf] + (sign?(9-ch):ch) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + WriteP(BAR--, (br & 060) | ch); + br = ReadP(BAR); + ch = val / 10; + ch = bcd_bin[br& 0xf] + (sign?(9-ch):ch) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + WriteP(BAR--, WM | (br & 060) | ch); + sim_interval -= 2; + if (sign && cy == 0) { + BAR += 2; /* Back up */ + br = ReadP(BAR); + sim_interval -= 2; + if ((br & 060) == 040) + br |= 060; + else { + br &= ~020; /* Switch B sign */ + br |= 040; + } + cy = 1; + /* Compliment until B word mark */ + ch = (9 - bcd_bin[br& 0xf]) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + WriteP(BAR--, (br & 0360) | ch); + sim_interval -= 2; + br = ReadP(BAR); + ch = (9 - bcd_bin[br& 0xf]) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + WriteP(BAR--, (br & 0360) | ch); + } + if (sign == 0 && cy) + return 1; + return 0; +} + +t_stat do_addsub(int mode) { + uint8 br; + uint8 ar; + int sign; + uint8 ch; + int cy; + uint32 STAR; + + DAR = BAR; + ar = ReadP(AAR); + br = ReadP(STAR = BAR); + sim_interval -= 2; + DownAddr(AAR); + DownAddr(BAR); + if (mode) /* Subtraction */ + sign = (ar & 060) != 040; + else /* Addition */ + sign = (ar & 060) == 040; + zind = 1; + if ((br & 060) == 040) + sign ^= 1; + cy = sign; + + if (CPU_MODEL == 1 && sign) + br |= ((br & 060) != 040)?060:0; + /* Add until word mark on A or B */ + while(1) { + ch = bcd_bin[ar & 0xf]; + ch = bcd_bin[br & 0xf] + ((sign)? (9 - ch):ch) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + if (ch != CHR_0) /* Clear zero */ + zind = 0; + WriteP(STAR, (br & 0360) | ch); + if (br & WM) { + if (CPU_MODEL == 1 && !sign && cy) + WriteP(STAR, + WM | ch |(060&(br + 020))); + break; + } + if (ar & WM) + ar = WM|CHR_0; + else { + sim_interval--; + ar = ReadP(AAR); + DownAddr(AAR); + } + sim_interval--; + br = ReadP(STAR = BAR); + DownAddr(BAR); + if (CPU_MODEL == 1) { + if ((br & WM) == 0 || sign) + br &= WM | 0xf; + } + } + + /* If cy and qsign, tens-compliment result and flip sign */ + if (sign && cy == 0) { + STAR = BAR = DAR; + br = ReadP(BAR); + DownAddr(BAR); + sim_interval--; + if ((br & 060) == 040) + br |= 060; + else { + br &= ~020; /* Switch B sign */ + br |= 040; + } + zind = 1; + cy = 1; + /* Compliment until B word mark */ + while(1) { + ch = (9 - bcd_bin[br& 0xf]) + cy; + cy = ch > 9; /* Update carry */ + ch = bin_bcd[ch]; + if (ch != CHR_0) /* Clear zero */ + zind = 0; + WriteP(STAR, (br & 0360) | ch); + if (br & WM) + break; + br = ReadP(STAR = BAR); + DownAddr(BAR); + sim_interval--; + if (CPU_MODEL == 1) + br &= WM|0xf; + } + } + + /* If carry set overflow */ + if (sign == 0 && cy) + oind = 1; + return SCPE_OK; +} + +t_stat +do_mult() +{ + uint8 br; + uint8 ar; + int sign; + uint8 ch; + int cy; + + CAR = AAR; + DAR = BAR; + ar = ReadP(AAR); + DownAddr(AAR); + zind = 1; + sign = ((ar & 060) == 040); + /* Scan A for word mark setting B digits to zero */ + while (1) { + WriteP(BAR, 10); + sim_interval -= 4; + DownAddr(BAR); + if (ar & WM) + break; + ar = ReadP(AAR); + DownAddr(AAR); + }; + + /* Skip last digit of product */ + WriteP(BAR, 10); + DownAddr(BAR); + sim_interval -= 2; + /* Check signs of B and A. */ + br = ReadP(BAR); + /* Compute sign */ + sign ^= ((br & 060) == 040); + sign = (sign)?040:060; + /* Do multiple loop until B word mark */ + while(1) { + /* Interloop, multiply one digit */ + ch = bcd_bin[br & 0xf]; + while (ch != 0) { + WriteP(BAR, bin_bcd[ch - 1] | (br & WM)); + BAR = DAR; + br = ReadP(BAR); + cy = 0; + AAR = CAR; + ar = ReadP(AAR); + DownAddr(AAR); + while(1) { + ch = bcd_bin[br & 0xf]; + ch = bcd_bin[ar & 0xf] + ch + cy; + if (ch != 0) /* Clear zero */ + zind = 0; + cy = ch > 9; /* Update carry */ + WriteP(BAR, bin_bcd[ch] | (br & WM)); + DownAddr(BAR); + br = ReadP(BAR); + if (ar & WM) + break; + ar = ReadP(AAR); + DownAddr(AAR); + sim_interval -= 4; + } + /* Add carry to next digit */ + ch = bcd_bin[br & 0xf] + cy; + if (ch != 0) /* Clear zero */ + zind = 0; + sim_interval -= 2; + WriteP(BAR, bin_bcd[ch] | (br & WM)); + DownAddr(BAR); + br = ReadP(BAR); + ch = bcd_bin[br & 0xf]; + } + WriteP(BAR, CHR_0 | (br & WM)); + DownAddr(BAR); + SetBit(DAR, sign); + DownAddr(DAR); + sign = 0; /* Only on first digit */ + if (br & WM) + break; + br = ReadP(BAR); + } + return SCPE_OK; +} + +t_stat +do_divide() +{ + uint16 t; + int temp; + uint8 br; + uint8 ar; + int sign, qsign; + uint8 ch; + int cy; + + qsign = 9; /* Set compliment and carry in */ + cy = 1; + temp = 0; /* MDL latch */ + sign = 0; + CAR = AAR; + DAR = BAR; + while (1) { + AAR = CAR; + BAR = DAR; + ar = ReadP(AAR); + DownAddr(AAR); + br = ReadP(BAR); + if (qsign == 0 && br & 040) { + sign = ((ar & 060) == 040); /* Compute sign */ + sign ^= ((br & 060) == 040); + sign = (sign)?040:060; + temp = 1; /* Set last cycle */ + } + while (1) { + sim_interval -= 4; + t = bcd_bin[ar& 0xf]; + ch = ((qsign)?(9-t):t) + bcd_bin[br & 0xf] + cy; + cy = ch > 9; /* Update carry */ + ReplaceMask(BAR, bin_bcd[ch], 017); + DownAddr(BAR); + br = ReadP(BAR); + sim_interval -= 2; + if (ar & WM) { + ch = qsign + bcd_bin[br & 0xf] + cy; + cy = ch > 9; /* Update carry */ + ReplaceMask(BAR, bin_bcd[ch], 017); + DownAddr(BAR); + br = ReadP(BAR); + sim_interval -= 2; + break; + } else { + ar = ReadP(AAR); + DownAddr(AAR); + } + } + if (qsign == 9) { + if (cy) { + ch = bcd_bin[br & 0xf] + cy; + ReplaceMask(BAR, bin_bcd[ch], 017); + DownAddr(BAR); + if (ch > 9) { + if (CPU_MODEL == 1) + oind = 1; + else + dind = 1; + break; + } + } else { + qsign = 0; + } + } else { + if (temp) { + ch = 9 + bcd_bin[br & 0xf] + cy; + WriteP(BAR, bin_bcd[ch] | sign | (br & WM)); + DownAddr(BAR); + break; + } + qsign = 9; + cy = 1; + UpAddr(DAR); /* Back up one digit */ + } + } + return SCPE_OK; +} + + +/* Interval timer routines */ +t_stat +rtc_srv(UNIT * uptr) +{ + int32 t; + + t = sim_rtcn_calb (rtc_tps, TMR_RTC); + sim_activate_after(uptr, 1000000/rtc_tps); + + if (timer_enable) { + if (--timer_interval == 0) { + timer_irq |= 1; + timer_interval = 10; + } + } + return SCPE_OK; +} + +/* Reset routine */ +t_stat +cpu_reset(DEVICE * dptr) +{ + IAR = 1; + AAR = 0; + BAR = 0; + sim_brk_types = sim_brk_dflt = SWMASK('E'); + pri_enb = 0; + timer_enable = 0; + cind = 2; + zind = oind = dind = euind = eoind = 0; + if (cpu_unit.flags & OPTION_PROT) + sim_rtcn_init_unit (&cpu_unit, 10000, TMR_RTC); + return SCPE_OK; +} + +/* Memory examine */ +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = M[addr] & (077 | WM); + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + M[addr] = val & (077 | WM); + return SCPE_OK; +} + +t_stat +cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + uint8 mc = 0; + int32 i; + int32 v; + + v = val >> UNIT_V_MSIZE; + v++; + v *= 10000; + if ((v < 0) || (v > MAXMEMSIZE)) + return SCPE_ARG; + for (i = v-1; i < MAXMEMSIZE; i++) + mc |= M[i]; + if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + cpu_unit.capac = v; + cpu_unit.flags &= ~UNIT_MSIZE; + cpu_unit.flags |= val; + for (i = MEMSIZE; i < MAXMEMSIZE; i++) + M[i] = 0; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].ic = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, i, di, lnt, pc; + char *cptr = (char *) desc; + t_stat r; + t_value sim_eval[50]; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "IC A B Aend Bend \n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ic & HIST_PC) { /* instruction? */ + pc = h->ic & HIST_MSK; + fprintf(st, "%05d ", pc); + fprintf(st, "%05d ", h->astart & AMASK); + fprintf(st, "%05d ", h->bstart & AMASK); + fprintf(st, "%05d%c", h->aend & AMASK, (h->aend & BBIT)?'+':' '); + fprintf(st, "%05d%c|", h->bend & AMASK, (h->bend & BBIT)?'+':' '); + for(i = 0; i < h->dlen; i++) + fputc(mem_to_ascii[h->bdata[i]&077], st); + fputc('|', st); + fputc(' ', st); + for(i = 0; i< 15; i++) + sim_eval[i] = h->inst[i]; + (void)fprint_sym(st, pc, sim_eval, &cpu_unit, SWMASK((h->ic & HIST_1401)?'N':'M')); + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + + +const char * +cpu_description (DEVICE *dptr) +{ + return "IBM 7010 CPU"; +} + +t_stat +cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "The CPU can be set to a IBM 1401 or IBM 1410/7010\n"); + fprintf (st, "The type of CPU can be set by one of the following commands\n\n"); + fprintf (st, " sim> set CPU 1401 sets IBM 1401 emulation\n"); + fprintf (st, " sim> set CPU 7010 sets IBM 1410/7010 emulation\n\n"); + fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); + fprintf (st, " -c examine/deposit characters, 6 per word\n"); + fprintf (st, " -l examine/deposit half words\n"); + fprintf (st, " -m examine/deposit IBM 7010 instructions\n\n"); + fprintf (st, "The memory of the CPU can be set in 10K incrememts from 10K to 100K with the\n\n"); + fprintf (st, " sim> SET CPU xK\n\n"); + fprintf (st, "For the IBM 7010 the following options can be enabled\n\n"); + fprintf (st, " sim> SET CPU PRIORITY enables Priority Interupts\n"); + fprintf (st, " sim> SET CPU NOPRIORITY disables Priority Interupts\n\n"); + fprintf (st, " sim> SET CPU FLOAT enables Floating Point\n"); + fprintf (st, " sim> SET CPU NOFLOAT disables Floating Point\n\n"); + fprintf (st, " sim> SET CPU PROT enables memory protection feature\n"); + fprintf (st, " sim> SET CPU NOPROT disables memory protection feature\n\n"); + fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n"); + fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); + fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); + fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); + fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); + fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + + return SCPE_OK; +} + diff --git a/I7000/i7010_defs.h b/I7000/i7010_defs.h new file mode 100644 index 00000000..231935c9 --- /dev/null +++ b/I7000/i7010_defs.h @@ -0,0 +1,133 @@ +/* i7010_defs.h: IBM 7010 simulator definitions + + Copyright (c) 2006-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 "sim_defs.h" /* simulator defns */ + +#include "i7000_defs.h" + +/* Memory */ +#define AMASK 0x1ffff +#define BBIT 0x80000000 +#define MEM_ADDR_OK(x) ((uint32)(x & AMASK) < MEMSIZE) +extern uint8 M[MAXMEMSIZE]; +#define WM 0200 /* Word mark in memory */ + +/* Issue a command to a channel */ +int chan_cmd(uint16 dev, uint16 cmd, uint32 addr); + +/* Opcodes */ /* I A B */ +#define OP_A CHR_A /* Aab */ /* NSI ALW BLB */ +#define OP_S CHR_S /* Sab */ /* NSI ALW BLB */ +#define OP_ZA CHR_QUEST /* ?ab */ /* NSI ALW BLB */ +#define OP_ZS CHR_EXPL /* !ab */ /* NSI ALW BLB */ +#define OP_M CHR_QUOT /* @ab */ /* NSI ALA BLB */ +#define OP_D CHR_RPARN /* %ab */ /* NSI ALA 10-quotient */ +#define OP_SAR CHR_G /* Gcd */ /* NSI * * C */ + /* A B E F T*/ /* 061, 062, 065, 066 */ +#define OP_SWM CHR_COM /* ,ab */ /* NSI A-1 B-1 */ +#define OP_CWM CHR_LPARN /* sqab*/ /* NSI A-1 B-1 */ +#define OP_CS CHR_SLSH /* /ib */ /* NSI/B B bbb00-1/NSIB */ +#define OP_H CHR_DOT /* .i */ /* NSI/B BI NSIB */ +#define OP_NOP CHR_N /* Nxxx^ */ /* NSI * * */ +#define OP_MOV CHR_D /* Dabd */ /* NSI */ + /* B A 8 4 2 1 */ + /* 1pos r-l scan */ + /* awm wm Z N */ + /* bwm */ + /* a|bwm */ + /* a|bwm l-r */ + /* arm */ + /* agm|awm */ + /* arm|agm|arm */ +#define OP_MSZ CHR_Z /* Zab */ /* NSI ALA B+1 */ +#define OP_C CHR_C /* Cab */ /* NSI ALW BLW */ +#define OP_T CHR_T /* Tabd */ /* NSI ALW last addr */ + /* 1 <, 2 ==, 3 <=, 4 >, 5 <>, 6 =>, 7 any, b end */ +#define OP_E CHR_E /* Eab */ /* NSI ALA ? */ +#define OP_B CHR_J /* Jid */ /* NSIB BI NSIB */ + /* blank jump */ + /* Z Arith overflow */ + /* 9 Carriage 9 CH1 */ + /* ! Carriage 9 CH2 */ + /* R Carriage Busy CH1 */ + /* L Carriage Busy CH2 */ + /* @ Cariage Overflow 12 CH 1 */ + /* sq Cariage Overflow 12 CH 2 */ + /* S Equal */ + /* U High */ + /* T Low */ + /* / High or Low */ + /* W Divide overflow */ + /* Q Inq req ch 1 */ + /* * Inq req ch 2 */ + /* 1 Overlap in Proc Ch 1 */ + /* 2 Overlap in Proc Ch 2 */ + /* 3 Overlap in Proc Ch 1 */ + /* 4 Overlap in Proc Ch 2 */ + /* K Tape indicator */ + /* V Zero Balence */ + /* Y Branch Exp over */ + /* X Branch Exp under */ + /* M Branch Bin Card 1 */ + /* ( Branch Bin Card 2 */ +#define OP_IO1 CHR_R /* Rid */ /* NSIB BI NSIB */ +#define OP_IO2 CHR_X /* Xid */ /* NSIB BI NSIB */ +#define OP_IO3 CHR_3 /* 3id */ /* NSIB BI NSIB */ +#define OP_IO4 CHR_1 /* 1id */ /* NSIB BI NSIB */ + /* B-Wrong len, A-No Trans, 8-Condition, + 4-Data Check, 2-Busy, 1- not Ready */ +#define OP_BCE CHR_B /* Bibd */ /* NSIB BI B-1/NSIB */ +#define OP_BBE CHR_W /* Wibd */ /* NSIB BI B-1/NSIB */ +#define OP_BWE CHR_V /* Vibd */ /* NSIB BI B-1/NSIB */ + +#define OP_RD CHR_M /* Mxbd */ +#define OP_RDW CHR_L /* Lxbd */ +#define OP_CC1 CHR_F /* Fd */ +#define OP_CC2 CHR_2 /* 2d */ +#define OP_SSF1 CHR_K /* Kd */ +#define OP_SSF2 CHR_4 /* 4d */ +#define OP_UC CHR_U /* Uxd */ +#define OP_PRI CHR_Y /* Yd */ /* E- enter, X leave */ + /* 21 - enable prot? */ + /* 11 - disable prot? */ +#define OP_STS CHR_DOL /* $ad */ /* S- store, R- restore */ + /* E - stchan1, G stchan2 */ + /* 1 - rschan1, 4 rschan4*/ + /* 47 - set low? */ + /* 72 - set high? */ +#define OP_FP CHR_EQ /* =ad */ /* R - Floating Reset Add */ + /* L - Floating store */ + /* A - Floating add */ + /* S - Floating sub */ + /* M - Floating mul */ + /* D - Floating div */ + +/* Flags for chan_io_status. */ +#define IO_CHS_NORDY 0001 /* Unit not Ready */ +#define IO_CHS_BUSY 0002 /* Unit or channel Busy */ +#define IO_CHS_CHECK 0004 /* Data check */ +#define IO_CHS_COND 0010 /* Condition */ +#define IO_CHS_NOTR 0020 /* No transfer */ +#define IO_CHS_WRL 0040 /* Wrong length */ +#define IO_CHS_DONE 0100 /* Device done */ +#define IO_CHS_OVER 0200 /* Channel busy on overlap processing */ diff --git a/I7000/i7010_sys.c b/I7000/i7010_sys.c new file mode 100644 index 00000000..f8995582 --- /dev/null +++ b/I7000/i7010_sys.c @@ -0,0 +1,1250 @@ +/* i7010_sys.c: IBM 7010 Simulator system interface. + + 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 "i7010_defs.h" +#include "sim_card.h" +#include + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 7010"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 50; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef STACK_DEV + &stack_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#ifdef NUM_DEVS_CON + &con_dev, +#endif +#if NUM_DEVS_MT > 0 + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#if NUM_DEVS_MT > 2 + &mtc_dev, +#endif +#endif +#endif +#ifdef NUM_DEVS_HD + &hsdrm_dev, +#endif +#ifdef NUM_DEVS_DR + &drm_dev, +#endif +#ifdef NUM_DEVS_DSK + &dsk_dev, +#endif +#ifdef NUM_DEVS_COM + &coml_dev, + &com_dev, +#endif +#ifdef NUM_DEVS_CHRON + &chron_dev, +#endif + NULL +}; + +/* Device addressing words */ +#ifdef NUM_DEVS_CDP +DIB cdp_dib = { CH_TYP_UREC, 1, 00400, 07700, &cdp_cmd, &cdp_ini }; +#endif +#ifdef NUM_DEVS_CDR +DIB cdr_dib = { CH_TYP_UREC, 1, 00100, 07700, &cdr_cmd, NULL }; +#endif +#ifdef NUM_DEVS_LPR +DIB lpr_dib = { CH_TYP_UREC, 1, 00200, 07700, &lpr_cmd, &lpr_ini }; +#endif +#ifdef NUM_DEVS_CON +DIB con_dib = { CH_TYP_UREC, 1, 02300, 07700, &con_cmd, &con_ini }; +#endif +#ifdef NUM_DEVS_MT +DIB mt_dib = { CH_TYP_UREC, NUM_UNITS_MT, 02400, 07700, &mt_cmd, &mt_ini }; +#endif +#ifdef NUM_DEVS_CHRON +DIB chron_dib = { CH_TYP_UREC, 1, 02400, 07700, &chron_cmd, NULL }; +#endif +#ifdef NUM_DEVS_DSK +DIB dsk_dib = { CH_TYP_79XX|CH_TYP_UREC, 0, 06600, 07700, &dsk_cmd, &dsk_ini }; +#endif +#ifdef NUM_DEVS_COM +DIB com_dib = { CH_TYP_79XX|CH_TYP_UREC, 0, 04200, 07700, &com_cmd, NULL }; +#endif + + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Error1", /* Ind limit */ /* Not on 7010 */ + "Error2", /* XEC limit */ /* Not on 7010 */ + "I/O Check opcode", + "Error3", /* MM in trap */ /* Not on 7010 */ + "7750 invalid line number", + "7750 invalid message", + "7750 No free output buffers", + "7750 No free input buffers", + "Error4", /* Field overflow */ /* Not on 7010 */ + "Error5", /* Sign change */ /* Not on 7010 */ + "Divide error", + "Error6", /* Alpha index */ /* Not on 7010 */ + "No word mark", + "Invalid Address", + "Invalid Lenght Instruction", + "Program Check", + "Protect Check", + 0, +}; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {0, 0} +}; + +DEBTAB crd_debug[] = { + {"CHAN", DEBUG_CHAN}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"CARD", DEBUG_CARD}, + {0, 0} +}; + + +/* Character conversion tables */ +const char ascii_to_six[128] = { + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /*sp ! " # $ % & ' */ + 000, 052, -1, 032, 053, 017, 060, 014, /* 40 - 77 */ + /* ( ) * + , - . / */ + 034, 074, 054, 060, 033, 040, 073, 021, + /* 0 1 2 3 4 5 6 7 */ + 012, 001, 002, 003, 004, 005, 006, 007, + /* 8 9 : ; < = > ? */ + 010, 011, 015, 056, 076, 013, 016, 032, + /* @ A B C D E F G */ + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + /* H I J K L M N O */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* P Q R S T U V W */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* X Y Z [ \ ] ^ _ */ + 027, 030, 031, 075, 036, 055, 057, 020, + /* ` a b c d e f g */ + 035, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + /* h i j k l m n o */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* p q r s t u v w */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* x y z { | } ~ del*/ + 027, 030, 031, 057, 077, 017, -1, -1 +}; + + +const char mem_to_ascii[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', 's', + 'b', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'x', ',', '(', '`', '\\', '_', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '|', + /*Sq*/ /*RM*/ +}; + + +/* Load a card image file into memory. */ + +t_stat +sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + return SCPE_NOFNC; +} + +/* Symbol tables */ +typedef struct _opcode +{ + uint16 opbase; + const char *name; + uint8 type; +} +t_opcode; + +#define TYPE_0 0 /* no operands, no options */ +#define TYPE_1 1 /* One operand, no options */ +#define TYPE_2 2 /* Two operands, no options */ +#define TYPE_T 4 /* Tape opcode, option */ +#define TYPE_B 5 /* Branch, one operand, option */ +#define TYPE_BE 6 /* Branch, three operands */ +#define TYPE_BZ 7 /* Branch, three operands */ +#define TYPE_CC 8 /* Carrage control */ +#define TYPE_IO 9 /* IO select, address, option */ +#define TYPE_Y 10 /* one operand, option */ +#define TYPE_M 11 /* Move type, two ops, mod */ +#define TYPE_BS 12 /* One operand, print mod */ + +#define MOD(x) (x<<6) +t_opcode ops_1401[] = { + {CHR_A, "A", TYPE_2}, + {OP_B|MOD(CHR_9), "BC9", TYPE_B}, + {OP_B|MOD(CHR_QUOT),"BCV", TYPE_B}, + {OP_B|MOD(CHR_Z), "BAV", TYPE_B}, + {OP_B|MOD(CHR_S), "BE", TYPE_B}, + {OP_B|MOD(CHR_T), "BH", TYPE_B}, + {OP_B|MOD(CHR_U), "BL", TYPE_B}, + {OP_B|MOD(CHR_SLSH),"BU", TYPE_B}, + {OP_B|MOD(CHR_A), "BLC", TYPE_B}, + {OP_B|MOD(CHR_B), "BSS", TYPE_BS}, + {OP_B|MOD(CHR_C), "BSS", TYPE_BS}, + {OP_B|MOD(CHR_D), "BSS", TYPE_BS}, + {OP_B|MOD(CHR_E), "BSS", TYPE_BS}, + {OP_B|MOD(CHR_F), "BSS", TYPE_BS}, + {OP_B|MOD(CHR_K), "BEF", TYPE_B}, + {OP_B|MOD(CHR_L), "BER", TYPE_B}, + {OP_B|MOD(CHR_P), "BPB", TYPE_B}, + {OP_B|MOD(CHR_N), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_I), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_RM), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_V), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_W), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_X), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_Z), "BIN", TYPE_BS}, + {OP_B|MOD(CHR_STAR),"BIN", TYPE_BS}, + {OP_B|MOD(CHR_QUEST),"BIN", TYPE_BS}, + {OP_B|MOD(CHR_RPARN),"BIN", TYPE_BS}, + {OP_B|MOD(CHR_9), "BPCB", TYPE_B}, + {OP_B, "B", TYPE_BS}, + {OP_BCE, "BCE", TYPE_BE}, + {OP_BBE, "BBE", TYPE_BE}, + {OP_BWE, "BWZ", TYPE_BE}, + {OP_CC1, "CC", TYPE_CC}, + {OP_CS, "CS", TYPE_2}, + {OP_CWM, "CW", TYPE_2}, + {OP_C, "C", TYPE_2}, + {OP_D, "D", TYPE_2}, + {OP_M, "M", TYPE_2}, + {OP_H, "H", TYPE_1}, + {CHR_M, "MLC", TYPE_IO}, + {CHR_P, "MRCM", TYPE_2}, + {CHR_Z, "MCS", TYPE_2}, + {CHR_Y, "MLZS", TYPE_2}, + {CHR_E, "MCE", TYPE_2}, + {CHR_D, "MLNS", TYPE_2}, + {CHR_L, "MLCWA", TYPE_IO}, + {CHR_Q, "SAR", TYPE_2}, + {CHR_H, "SBR", TYPE_2}, + {CHR_1, "R", TYPE_1}, + {CHR_2|07400, "WM", TYPE_Y}, + {CHR_2, "W", TYPE_1}, + {CHR_3, "WR", TYPE_1}, + {CHR_4, "P", TYPE_1}, + {CHR_5, "RP", TYPE_1}, + {CHR_6, "WP", TYPE_1}, + {CHR_7, "WRP", TYPE_1}, + {CHR_EQ, "MA", TYPE_2}, + {OP_NOP, "NOP", TYPE_0}, + {OP_SWM, "SW", TYPE_2}, + {OP_UC|06100, "SKF", TYPE_T}, + {OP_UC|06200, "BSP", TYPE_T}, + {OP_UC|06500, "SKP", TYPE_T}, + {OP_UC|05100, "RWD", TYPE_T}, + {OP_UC|02400, "RUN", TYPE_T}, + {OP_UC|04400, "WTM", TYPE_T}, + {OP_UC, "UC", TYPE_IO}, + {OP_S, "S", TYPE_2}, + {OP_SSF1, "SSF1", TYPE_CC}, + {OP_SSF2, "SSF2", TYPE_CC}, + {OP_ZA, "ZA", TYPE_2}, + {OP_ZS, "ZS", TYPE_2}, + {0, NULL, TYPE_BE}, +}; + +/* Opcodes */ +t_opcode base_ops[] = { + {OP_IO1|MOD(077), "BA1", TYPE_B}, + {OP_IO1|MOD(001), "BNR1", TYPE_B}, + {OP_IO1|MOD(002), "BCB1", TYPE_B}, + {OP_IO1|MOD(004), "BER1", TYPE_B}, + {OP_IO1|MOD(010), "BEF1", TYPE_B}, + {OP_IO1|MOD(020), "BNT1", TYPE_B}, + {OP_IO1|MOD(040), "BWL1", TYPE_B}, + {OP_IO1|MOD(000), "BEX1", TYPE_B}, + {OP_IO1|MOD(000), "BEX1", TYPE_BE}, + {OP_IO2|MOD(077), "BA2", TYPE_B}, + {OP_IO2|MOD(001), "BNR2", TYPE_B}, + {OP_IO2|MOD(002), "BCB2", TYPE_B}, + {OP_IO2|MOD(004), "BER2", TYPE_B}, + {OP_IO2|MOD(010), "BEF2", TYPE_B}, + {OP_IO2|MOD(020), "BNT2", TYPE_B}, + {OP_IO2|MOD(040), "BWL2", TYPE_B}, + {OP_IO2|MOD(000), "BEX2", TYPE_B}, + {OP_IO2|MOD(000), "BEX2", TYPE_BE}, + {OP_IO3|MOD(077), "BA3", TYPE_B}, + {OP_IO3|MOD(001), "BNR3", TYPE_B}, + {OP_IO3|MOD(002), "BCB3", TYPE_B}, + {OP_IO3|MOD(004), "BER3", TYPE_B}, + {OP_IO3|MOD(010), "BEF3", TYPE_B}, + {OP_IO3|MOD(020), "BNT3", TYPE_B}, + {OP_IO3|MOD(040), "BWL3", TYPE_B}, + {OP_IO3|MOD(000), "BEX3", TYPE_B}, + {OP_IO3|MOD(000), "BEX3", TYPE_BE}, + {OP_IO4|MOD(077), "BA4", TYPE_B}, + {OP_IO4|MOD(001), "BNR4", TYPE_B}, + {OP_IO4|MOD(002), "BCB4", TYPE_B}, + {OP_IO4|MOD(004), "BER4", TYPE_B}, + {OP_IO4|MOD(010), "BEF4", TYPE_B}, + {OP_IO4|MOD(020), "BNT4", TYPE_B}, + {OP_IO4|MOD(040), "BWL4", TYPE_B}, + {OP_IO4|MOD(000), "BEX4", TYPE_B}, + {OP_IO4|MOD(000), "BEX4", TYPE_BE}, + {OP_A, "A", TYPE_2}, + {OP_BBE, "BBE", TYPE_BE}, + {OP_BCE, "BCE", TYPE_BE}, + {OP_B|04100, "BPCB", TYPE_B}, + {OP_B|04300, "BPCB2", TYPE_B}, + {OP_B|01000, "BC9", TYPE_B}, + {OP_B|05200, "BC92", TYPE_B}, + {OP_B|03200, "BCV", TYPE_B}, + {OP_B|07400, "BCV2", TYPE_B}, + {OP_B|03100, "BAV", TYPE_B}, + {OP_B|02200, "BE", TYPE_B}, + {OP_B|02400, "BH", TYPE_B}, + {OP_B|02300, "BL", TYPE_B}, + {OP_B|02100, "BU", TYPE_B}, + {OP_B|02600, "BDV", TYPE_B}, + {OP_B|05000, "BNQ", TYPE_B}, + {OP_B|05400, "BNQ2", TYPE_B}, + {OP_B|00100, "BOL1", TYPE_B}, + {OP_B|00200, "BOL2", TYPE_B}, + {OP_B|00300, "BOL3", TYPE_B}, + {OP_B|00400, "BOL4", TYPE_B}, + {OP_B|04200, "BTI", TYPE_B}, + {OP_B|02500, "BZ", TYPE_B}, + {OP_B|02700, "BXO", TYPE_B}, + {OP_B|03000, "BXU", TYPE_B}, + {OP_BWE|00100, "BW", TYPE_BZ}, + {OP_BWE|00300, "BWZ", TYPE_BZ}, + {OP_BWE|00200, "BZN", TYPE_BZ}, + {OP_BWE|00000, "BWE", TYPE_Y}, + {OP_B, "B", TYPE_B}, + {OP_B, "JIO", TYPE_Y}, + {OP_CC1, "CC1", TYPE_CC}, + {OP_CC2, "CC2", TYPE_CC}, + {OP_CS, "CS", TYPE_2}, + {OP_CWM, "CW", TYPE_2}, + {OP_C, "C", TYPE_2}, + {OP_D, "D", TYPE_2}, + {OP_H, "H", TYPE_1}, + {OP_T|00200, "LE", TYPE_B}, + {OP_T|00600, "LEH", TYPE_B}, + {OP_T|00400, "LH", TYPE_B}, + {OP_T|00100, "LL", TYPE_B}, + {OP_T|00300, "LLE", TYPE_B}, + {OP_T|00500, "LLH", TYPE_B}, + {OP_T|00700, "LA", TYPE_B}, + {OP_T|00000, "L", TYPE_B}, + {OP_MSZ, "MCS", TYPE_2}, + {OP_E, "MCE", TYPE_2}, + {OP_M, "M", TYPE_2}, + {OP_MOV|00100, "MLNS", TYPE_M}, + {OP_MOV|00200, "MLZS", TYPE_M}, + {OP_MOV|00300, "MLCS", TYPE_M}, + {OP_MOV|00400, "MLWS", TYPE_M}, + {OP_MOV|00500, "MLNWS", TYPE_M}, + {OP_MOV|00600, "MLZWS", TYPE_M}, + {OP_MOV|00700, "MLCWS", TYPE_M}, + {OP_MOV|01000, "SCNR", TYPE_M}, + {OP_MOV|01100, "MRN", TYPE_M}, + {OP_MOV|01200, "MRZ", TYPE_M}, + {OP_MOV|01300, "MRC", TYPE_M}, + {OP_MOV|01400, "MRW", TYPE_M}, + {OP_MOV|01500, "MRNW", TYPE_M}, + {OP_MOV|01600, "MRZW", TYPE_M}, + {OP_MOV|01700, "MRCW", TYPE_M}, + {OP_MOV|02000, "SCNLA", TYPE_M}, + {OP_MOV|02100, "MLNA", TYPE_M}, + {OP_MOV|02200, "MLZA", TYPE_M}, + {OP_MOV|02300, "MLCA", TYPE_M}, + {OP_MOV|02400, "MLWA", TYPE_M}, + {OP_MOV|02500, "MLNWA", TYPE_M}, + {OP_MOV|02600, "MLZWA", TYPE_M}, + {OP_MOV|02700, "MLCWA", TYPE_M}, + {OP_MOV|03000, "SCNRR", TYPE_M}, + {OP_MOV|03100, "MRNR", TYPE_M}, + {OP_MOV|03200, "MRZR", TYPE_M}, + {OP_MOV|03300, "MRCR", TYPE_M}, + {OP_MOV|03400, "MRWR", TYPE_M}, + {OP_MOV|03500, "MRNWR", TYPE_M}, + {OP_MOV|03600, "MRZWR", TYPE_M}, + {OP_MOV|03700, "MRCWR", TYPE_M}, + {OP_MOV|04000, "SCNLB", TYPE_M}, + {OP_MOV|04100, "MLNB", TYPE_M}, + {OP_MOV|04200, "MLZB", TYPE_M}, + {OP_MOV|04300, "MLCB", TYPE_M}, + {OP_MOV|04400, "MLWB", TYPE_M}, + {OP_MOV|04500, "MLNWB", TYPE_M}, + {OP_MOV|04600, "MLZWB", TYPE_M}, + {OP_MOV|04700, "MLCWB", TYPE_M}, + {OP_MOV|05000, "SCNRG", TYPE_M}, + {OP_MOV|05100, "MRNG", TYPE_M}, + {OP_MOV|05200, "MRZG", TYPE_M}, + {OP_MOV|05300, "MRCG", TYPE_M}, + {OP_MOV|05400, "MRWG", TYPE_M}, + {OP_MOV|05500, "MRNWG", TYPE_M}, + {OP_MOV|05600, "MRZWG", TYPE_M}, + {OP_MOV|05700, "MRCWG", TYPE_M}, + {OP_MOV|06000, "SCNL", TYPE_M}, + {OP_MOV|06100, "MLN", TYPE_M}, + {OP_MOV|06200, "MLZ", TYPE_M}, + {OP_MOV|06300, "MLC", TYPE_M}, + {OP_MOV|06400, "MLW", TYPE_M}, + {OP_MOV|06500, "MLNW", TYPE_M}, + {OP_MOV|06600, "MLZW", TYPE_M}, + {OP_MOV|06700, "MLCW", TYPE_M}, + {OP_MOV|07000, "SCNRM", TYPE_M}, + {OP_MOV|07100, "MRNM", TYPE_M}, + {OP_MOV|07200, "MRZM", TYPE_M}, + {OP_MOV|07300, "MRCM", TYPE_M}, + {OP_MOV|07400, "MRWM", TYPE_M}, + {OP_MOV|07500, "MRNWM", TYPE_M}, + {OP_MOV|07600, "MRZWM", TYPE_M}, + {OP_MOV|07700, "MRCWM", TYPE_M}, + {OP_MOV|00000, "SCNLS", TYPE_M}, + {OP_NOP, "NOP", TYPE_0}, + {OP_SWM, "SW", TYPE_2}, + {OP_UC|06100, "SKF", TYPE_T}, + {OP_UC|06200, "BSP", TYPE_T}, + {OP_UC|06500, "SKP", TYPE_T}, + {OP_UC|05100, "RWD", TYPE_T}, + {OP_UC|02400, "RUN", TYPE_T}, + {OP_UC|04400, "WTM", TYPE_T}, + {OP_SAR|06100, "SAR", TYPE_1}, + {OP_SAR|06200, "SBR", TYPE_1}, + {OP_SAR|06500, "SER", TYPE_1}, + {OP_SAR|06600, "SFR", TYPE_1}, + {OP_SAR|06700, "SGR", TYPE_1}, + {OP_SAR|07000, "SHR", TYPE_1}, + {OP_SAR|02300, "STC", TYPE_1}, + {OP_S, "S", TYPE_2}, + {OP_SSF1, "SSF1", TYPE_CC}, + {OP_SSF2, "SSF2", TYPE_CC}, + {OP_ZA, "ZA", TYPE_2}, + {OP_ZS, "ZS", TYPE_2}, + {OP_RD|00000, "MU", TYPE_IO}, + {OP_RDW|00000, "LU", TYPE_IO}, + {OP_STS|00000, "STATS", TYPE_Y}, + {OP_FP|05100, "FRA", TYPE_B}, + {OP_FP|04300, "FST", TYPE_B}, + {OP_FP|06100, "FA", TYPE_B}, + {OP_FP|02200, "FS", TYPE_B}, + {OP_FP|04400, "FM", TYPE_B}, + {OP_FP|06400, "FD", TYPE_B}, + {OP_FP|00000, "FP", TYPE_Y}, + {OP_PRI|02400, "BUPR1", TYPE_B}, + {OP_PRI|06600, "BUPR2", TYPE_B}, + {OP_PRI|00100, "BOPR1", TYPE_B}, + {OP_PRI|00200, "BOPR2", TYPE_B}, + {OP_PRI|00300, "BOPR3", TYPE_B}, + {OP_PRI|00400, "BOPR4", TYPE_B}, + {OP_PRI|05000, "BIPR1", TYPE_B}, + {OP_PRI|05500, "BIPR2", TYPE_B}, + {OP_PRI|04500, "BQPR1", TYPE_B}, + {OP_PRI|03200, "BQPR2", TYPE_B}, + {OP_PRI|02200, "BSPR1", TYPE_B}, + {OP_PRI|02300, "BSPR2", TYPE_B}, + {OP_PRI|03000, "BSPR3", TYPE_B}, + {OP_PRI|03400, "BSPR4", TYPE_B}, + {OP_PRI|02700, "BXPA", TYPE_B}, + {OP_PRI|06500, "BEPA", TYPE_B}, + {OP_PRI|06100, "BXPR1", TYPE_B}, + {OP_PRI|06200, "BXPR2", TYPE_B}, + {OP_PRI|06300, "BXPR3", TYPE_B}, + {OP_PRI|06400, "BXPR4", TYPE_B}, + {OP_PRI|00000, "BPI", TYPE_Y}, + {0, NULL, TYPE_BE}, +}; + +const char *chname[] = { + "*", "1", "2", "3", "4" +}; + + +/* Print out a address plus index */ +t_stat fprint_addr (FILE *of, uint32 addr) { + int i; + int reg; + + reg = ((addr >> 10) & 03) | ((addr >> 14) & 014); + addr &= 07777171777; /* Mask register bits */ + for(i = 24; i>=0; i -= 6) + fputc(mem_to_ascii[(addr >> i) & 077], of); + if (reg != 0) + fprintf(of, "+X%d", reg); + return SCPE_OK; +} + +/* Print out a 1401 address plus index */ +t_stat fprint_addr_1401 (FILE *of, uint32 addr) { + int reg; + int v = 0; + + if ((addr & 0170000) != 0120000) + v += ((addr >> 12) & 017) * 100; + if ((addr & 01700) != 01200) + v += ((addr >> 6) & 017) * 10; + if ((addr & 017) != 012) + v += addr & 017; + v += ((addr & 0600000) >> 16) * 1000; + v += ((addr & 060) >> 4) * 4000; + reg = (addr >> 10) & 03; + fprintf(of, "%d", v); + if (reg != 0) + fprintf(of, "+X%d", reg); + return SCPE_OK; +} + + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ +int32 i, t; +uint32 a, b; +uint8 op, mod, flags; + +if (sw & SWMASK ('C')) { /* character? */ + t = val[0]; + fprintf (of, (t & WM)? "`%c<%02o> ": " %c<%02o> ", mem_to_ascii[t & 077], + t & 077); + return SCPE_OK; + } +if ((uptr != NULL) && (uptr != &cpu_unit)) return SCPE_ARG; /* CPU? */ +if (sw & SWMASK ('D')) { /* dump? */ + for (i = 0; i < 50; i++) fprintf (of, "%c", mem_to_ascii[val[i]&077]) ; + fprintf (of, "\n\t"); + for (i = 0; i < 50; i++) fprintf (of, (val[i]&WM)? "1": " ") ; + return -(i - 1); + } +if (sw & SWMASK ('S')) { /* string? */ + i = 0; + do { + t = val[i++]; + fprintf (of, (t & WM)? "`%c": "%c", mem_to_ascii[t & 077]); + } while ((i < 50) && ((val[i] & WM) == 0)); + return -(i - 1); + } +if (sw & SWMASK ('N')) { /* 1401 machine code? */ + uint16 temp; + t_opcode *tab; + + mod = 0; + flags = 0; + a = 0; + b = 0; + i = 0; + op = val[i++] & 077; + /* 1 234 567 8 */ + /* 0 123 456 7 */ + /* o a b m */ + /* 0 123 */ + /* 0 123 4 */ + /* 0 123 456 */ + /* 0 1 */ + if ((val[i] & WM) == 0) { + /* Grab next value if one */ + /* Grab 3 address digits */ + do { + a = (a << 6) | (val[i++] & 077); + } while((val[i] & WM) == 0 && i < 4); + } + /* If more then grab B address and or modifier */ + if ((val[i] & WM) == 0) { + do { + b = (b << 6) | (val[i++] & 077); + } while((val[i] & WM) == 0 && i < 7); + } + + /* Grab modifier */ + if ((val[i] & WM) == 0) { + mod = val[i++] & 077; + flags |= 010; + } + + /* Determine A, B and modifier values */ + if (i == 2) { /* Only mod */ + mod = a; + flags |= 010; + } else if (i == 4) { /* Only A */ + flags |= 002; + } else if (i == 5) { /* A + mod */ + mod = b; + flags |= 012; + } else if (i > 6) { /* A + B */ + flags |= 006; + } + + /* Modify 2 op branch to BCE or B */ + if (op == CHR_B) { + switch(i) { + case 1: + case 7: + case 8: op = CHR_B; break; + default: op = CHR_J; break; + } + } + + temp = (mod << 6) | op; + for(tab = ops_1401; tab->name != NULL; tab++) { + if (temp == tab->opbase) + break; + if ((tab->type == TYPE_BE || tab->type == TYPE_CC) && + (temp & 077) == tab->opbase) + break; + if (tab->type == TYPE_BZ && (temp & 0377) == tab->opbase) + break; + if ((temp & 077) == tab->opbase) + break; + } + + if (tab->type == TYPE_IO && (a & 0770000) == 0340000) { + fprintf(of, "%cU\t", mem_to_ascii[op]); + flags &= 075; + flags |= 020; + } else if (tab->name == NULL) + fprintf(of, "%c<%02o>\t", mem_to_ascii[op], op); + else + fprintf(of, "%s\t", tab->name); + + switch(tab->type) { + case TYPE_0: /* no operands, no options */ + case TYPE_1: /* One operand, no options */ + case TYPE_2: /* Two operands, no options */ + case TYPE_B: /* Branch, one operand, option */ + case TYPE_M: + if (flags & 02) + fprint_addr_1401(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + if (flags & 010) { + fputc(',', of); + fputc(mem_to_ascii[mod], of); + } + break; + case TYPE_BS: /* Branch, one operand, option */ + if (flags & 02) + fprint_addr_1401(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + if (flags &010) { + fputc(',', of); + fputc(mem_to_ascii[mod], of); + } + break; + case TYPE_IO: /* Tape opcode or move */ + if (flags & 020) + for (t = 18; t >= 0; t-=6) + fprintf (of, "%c", mem_to_ascii[(a>>t)&077]) ; + else if (flags & 02) + fprint_addr_1401(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + if (flags & 010) + fprintf (of, ",%c", mem_to_ascii[mod]); + break; + case TYPE_T: /* Tape opcode, option */ + if (flags & 02) + for (t = 18; t >= 0; t-=6) + fprintf (of, "%c", mem_to_ascii[(a>>t)&077]) ; + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + break; + + case TYPE_BZ: /* Branch, three operands */ + if (flags & 02) + fprint_addr_1401(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + if (flags & 010 && mod & 060) { + fputc(',', of); + if (mod & 020) + fputc('A', of); + if (mod & 040) + fputc('B', of); + } + break; + + case TYPE_BE: /* Branch, three operands */ + case TYPE_Y: /* Store special */ + default: + if (flags & 02) + fprint_addr_1401(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr_1401(of, b); + } + if (flags &010) { + fputc(',', of); + fputc(mem_to_ascii[mod], of); + } + break; + case TYPE_CC: + if (flags & 02) { + fprint_addr_1401(of, a); + if (flags & 010) + fputc(',', of); + } + if (flags &010) + fputc(mem_to_ascii[mod], of); + break; + } + return -(i - 1); +} +if (sw & SWMASK ('M')) { /* machine code? */ + uint16 temp; + t_opcode *tab; + + mod = 0; + flags = 0; + a = 0; + b = 0; + i = 0; + op = val[i++] & 077; + if ((val[i] & WM) == 0) { + /* Grab next value if one */ + if (op == OP_RD || op == OP_RDW || op == OP_UC) { + /* Three digit IO address */ + do { + a = (a << 6) | (val[i++] & 077); + } while((val[i] & WM) == 0 && i < 4); + flags = 1; + } else { + /* Grab 5 address digits */ + do { + a = (a << 6) | (val[i++] & 077); + } while((val[i] & WM) == 0 && i < 6); + } + } + /* If more then grab B address and or modifier */ + if ((val[i] & WM) == 0) { + int j = 0; + do { + b = (b << 6) | (val[i++] & 077); + } while((val[i] & WM) == 0 && ++j < 5); + } + + /* Determine A, B and modifier values */ + if (i == 2) { + mod = a; + flags |= 010; + } else if ((flags == 1 && i == 5) || (flags == 0 && i == 7)) { + mod = b; + flags |= 012; + } else if ((flags == 1 && i == 4) || (flags == 0 && i == 6)) { + flags |= 002; + } else { + flags |= 006; + if ((val[i] & WM) == 0) { + mod = val[i++] & 077; + flags |= 010; + } + } + + temp = (mod << 6) | op; + for(tab = base_ops; tab->name != NULL; tab++) { + if (temp == tab->opbase) + break; + if ((tab->type == TYPE_BE || tab->type == TYPE_CC) && + (temp & 077) == tab->opbase) + break; + if (tab->type == TYPE_BZ && (temp & 0377) == tab->opbase) + break; + if ((temp & 077) == tab->opbase) + break; + } + if (tab->name == NULL) + fprintf(of, "%c<%02o>\t", mem_to_ascii[op], op); + else + fprintf(of, "%s\t", tab->name); + + switch(tab->type) { + case TYPE_0: /* no operands, no options */ + case TYPE_1: /* One operand, no options */ + case TYPE_2: /* Two operands, no options */ + case TYPE_B: /* Branch, one operand, option */ + case TYPE_M: + if (flags & 02) + fprint_addr(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr(of, b); + } + break; + case TYPE_IO: /* Tape opcode, option */ + case TYPE_T: /* Tape opcode, option */ + if (flags & 010) + fprintf (of, "%c", mem_to_ascii[mod]); + if (flags & 02) + for (t = 18; t >= 0; t-=6) + fprintf (of, "%c", mem_to_ascii[(a>>t)&077]) ; + if (flags & 04) { + fputc(',', of); + fprint_addr(of, b); + } + break; + + case TYPE_BZ: /* Branch, three operands */ + if (flags & 02) + fprint_addr(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr(of, b); + } + if (flags & 010 && mod & 060) { + fputc(',', of); + if (mod & 020) + fputc('A', of); + if (mod & 040) + fputc('B', of); + } + break; + + case TYPE_BE: /* Branch, three operands */ + case TYPE_Y: /* Store special */ + default: + if (flags & 02) + fprint_addr(of, a); + if (flags & 04) { + fputc(',', of); + fprint_addr(of, b); + } + if (flags &010) { + fputc(',', of); + fputc(mem_to_ascii[mod], of); + } + break; + case TYPE_CC: + if (flags &010) + fputc(mem_to_ascii[mod], of); + break; + } + return -(i - 1); +} +t = val[0]; +fprintf (of, (t & WM)? "~%02o ": " %02o ", t & 077); +return 0; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + t_value d; + char buffer[100]; + int wm_seen; + + while (isspace(*cptr)) + cptr++; + d = 0; + if (sw & SWMASK('C')) { + i = 0; + wm_seen = 0; + while (*cptr != '\0') { + if (*cptr == '~' && wm_seen == 0) + wm_seen = WM; + else { + d = ascii_to_six[0177 & *cptr] | wm_seen; + val[i++] = d; + wm_seen = 0; + } + cptr++; + } + if (i == 0 || wm_seen) + return SCPE_ARG; + return -(i - 1); + } else if (sw & SWMASK('M')) { + t_opcode *op; + int j; + int32 addr; + + i = 0; + /* Grab opcode */ + cptr = get_glyph(cptr, buffer, 0); + if ((op = find_opcode(buffer, base_ops)) == 0) + return STOP_UUO; + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + val[i++] = WM | (op->opbase & 077); + switch(op->type) { + case TYPE_0: /* no operands, no options */ + if (*cptr != '\0') + return SCPE_ARG; + return -(i - 1); + + case TYPE_CC: + if (*cptr == '\0') + val[i++] = 10; + else + val[i++] = ascii_to_six[(int)*cptr++]; + return -(i - 1); + + case TYPE_IO: /* Tape opcode, option */ + case TYPE_T: /* Tape opcode, option */ + if (*cptr == '\0') + return SCPE_ARG; + val[i++] = ascii_to_six[(int)*cptr++]; + if (*cptr == '\0') + return SCPE_ARG; + val[i++] = ascii_to_six[(int)*cptr++]; + if (*cptr == '\0') + return SCPE_ARG; + val[i++] = ascii_to_six[(int)*cptr++]; + /* Go grab address */ + if (op->type == TYPE_T) { + val[i++] = (op->opbase >> 6) & 077; + return -(i-1); + } + break; + + case TYPE_1: /* One operand, no options */ + case TYPE_2: /* Two operands, no options */ + case TYPE_BE: /* Branch, three operands */ + case TYPE_Y: /* Store special */ + if (*cptr == '\0') + return -(i - 1); + break; + + case TYPE_B: /* Branch, one operand, option */ + case TYPE_BZ: /* Branch, three operands */ + case TYPE_M: /* Move opcode */ + default: + if (*cptr == '\0') { + val[i++] = (op->opbase >> 6) & 077; + return -(i-1); + } + break; + } + + /* Pick up at least one address & possible index */ + addr = 0; + while(*cptr != '\0') { + if (*cptr >= '0' && *cptr <= '9') + addr = (addr * 10) + (*cptr++ - '0'); + else if (*cptr == '+' || *cptr == ',') + break; + } + + /* Convert to BCD */ + for(j = 4; j >= 0;j--) { + buffer[j] = addr % 10; + if (buffer[j] == 0) + buffer[j] = 10; + addr /= 10; + } + + /* Merge in index bits if any */ + if (*cptr == '+') { + int n = 0; + cptr++; + if (*cptr != 'X' && *cptr != 'x') + return SCPE_ARG; + cptr++; + if (!(*cptr >= '0' && *cptr <= '9')) + return SCPE_ARG; + n = *cptr++ - '0'; + if (*cptr >= '0' && *cptr <= '9') + n = (n * 10) + (*cptr++ - '0'); + if (n > 16) + return SCPE_ARG; + buffer[3] |= (n & 3) << 4; + buffer[2] |= (n & 014) << 2; + } + + /* Copy over address */ + for(j = 0; j <= 4; j++) + val[i++] = buffer[j]; + + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + switch(op->type) { + case TYPE_IO: /* Tape opcode, option */ + case TYPE_T: /* Tape opcode, option */ + if (*cptr == ',') { + val[i++] = ascii_to_six[(int)*++cptr]; + while(isspace(*++cptr)); + } + if (*cptr == '\0') + return -(i - 1); + return SCPE_ARG; + + default: + case TYPE_1: /* One operand, no options */ + if (*cptr != '\0') + return SCPE_ARG; + return -(i - 1); + + case TYPE_2: /* Two operands, no options */ + case TYPE_BE: /* Branch, three operands */ + if (*cptr == '\0') + return -(i - 1); + break; + + case TYPE_B: /* Branch, one operand, option */ + val[i++] = (op->opbase >> 6) & 077; + return -(i-1); + + case TYPE_Y: /* Store special */ + if (*cptr == ',') { + val[i++] = ascii_to_six[(int)*++cptr]; + while(isspace(*++cptr)); + } + if (*cptr == '\0') + return -(i - 1); + return SCPE_ARG; + + case TYPE_BZ: /* Branch, three operands */ + case TYPE_M: /* Move opcode */ + if (*cptr == '\0') { + val[i++] = (op->opbase >> 6) & 077; + return -(i-1); + } + break; + } + + if (*cptr != ',') + return SCPE_ARG; + cptr++; + + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + + /* Pick up at least one address & possible index */ + addr = 0; + while(*cptr != '\0') { + if (*cptr >= '0' && *cptr <= '9') + addr = (addr * 10) + (*cptr++ - '0'); + else if (*cptr == '+' || *cptr == ',') + break; + } + + /* Convert to BCD */ + for(j = 4; j >= 0;j--) { + buffer[j] = addr % 10; + if (buffer[j] == 0) + buffer[j] = 10; + addr /= 10; + } + + /* Merge in index bits if any */ + if (*cptr == '+') { + int n = 0; + cptr++; + if (*cptr != 'X' && *cptr != 'x') + return SCPE_ARG; + cptr++; + if (!(*cptr >= '0' && *cptr <= '9')) + return SCPE_ARG; + n = *cptr++ - '0'; + if (*cptr >= '0' && *cptr <= '9') + n = (n * 10) + (*cptr++ - '0'); + if (n > 16) + return SCPE_ARG; + buffer[3] |= (n & 3) << 4; + buffer[2] |= (n & 014) << 2; + } + + /* Copy over address */ + for(j = 0; j <= 4; j++) + val[i++] = buffer[j]; + + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + switch(op->type) { + case TYPE_M: /* Move opcode */ + val[i++] = (op->opbase >> 6) & 077; + /* fall through */ + + default: + case TYPE_IO: /* Tape opcode, option */ + case TYPE_T: /* Tape opcode, option */ + case TYPE_1: /* One operand, no options */ + case TYPE_B: /* Branch, one operand, option */ + case TYPE_Y: /* Store special */ + case TYPE_2: /* Two operands, no options */ + if (*cptr == '\0') + return -(i - 1); + break; + + case TYPE_BE: /* Branch, three operands */ + if (*cptr == ',') { + val[i++] = ascii_to_six[(int)*++cptr]; + while(isspace(*++cptr)); + } + if (*cptr == '\0') + return -(i - 1); + break; + + case TYPE_BZ: /* Branch, three operands */ + if (*cptr == '\0') { + val[i++] = (op->opbase >> 6) & 077; + return -(i-1); + } else if (*cptr == ',') { + d = (op->opbase >> 6) & 077; + cptr++; + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + while(*cptr != '\0') { + if (*cptr == 'A' || *cptr == 'a') + d |= 020; + else if (*cptr == 'B' || *cptr == 'b') + d |= 040; + else + return SCPE_ARG; + cptr++; + } + val[i++] = d; + return -(i-1); + } + } + return SCPE_ARG; + } else { + int sign = 0; + i = 0; + wm_seen = 1; + while (*cptr != '\0') { + sign = 0; + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + if (*cptr == '+') { + cptr++; + sign = 1; + } else if (*cptr == '-') { + cptr++; + sign = -1; + } + if (!(*cptr >= '0' && *cptr <= '9')) + return SCPE_ARG; + while(*cptr >= '0' && *cptr <= '9') { + d = *cptr++ - '0'; + if (d == 0) + d = 10; + if (wm_seen) { + d |= WM; + wm_seen = 0; + } + val[i++] = d; + } + if (*cptr == ',') + cptr++; + if (sign != 0) + val[i-1] |= (sign < 0)?040:060; /* Set sign last digit */ + } + if (i == 0) + return SCPE_ARG; + return -(i - 1); + } + return SCPE_OK; +} diff --git a/I7000/i701_chan.c b/I7000/i701_chan.c new file mode 100644 index 00000000..9eafb9fc --- /dev/null +++ b/I7000/i701_chan.c @@ -0,0 +1,416 @@ +/* i701_chan.c: IBM 701 Channel simulator + + Copyright (c) 2005, 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. + + channel + + There is no channel on the 701, this module just provides basic support + for polled mode devices. + + Simulated register for the channel is: + STATUS<0:16> Simulated register for basic channel status. +*/ + +#include "i7090_defs.h" + +extern uint8 iocheck; +extern UNIT cpu_unit; +extern uint16 IC; +extern t_uint64 MQ; + +t_stat chan_reset(DEVICE * dptr); +void chan_fetch(int chan); +t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *chan_description (DEVICE *dptr); +uint32 dly_cmd(UNIT *, uint16, uint16); + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +t_uint64 assembly[NUM_CHAN]; /* Assembly register */ +uint32 chan_flags[NUM_CHAN]; /* Unit status */ +uint8 bcnt[NUM_CHAN]; /* Character count */ + +const char *chan_type_name[] = { + "Polled", "", "", "", ""}; + + +/* Delay device for IOD instruction */ +DIB dly_dib = + { CH_TYP_PIO, 1, 2052, 07777, &dly_cmd, NULL }; + + +UNIT chan_unit[] = { + /* Puesdo channel for 701 devices */ + {UDATA(NULL, UNIT_DISABLE | CHAN_SET | + CHAN_S_TYPE(CHAN_PIO)|UNIT_S_CHAN(0), 0)}, +}; + +REG chan_reg[] = { + {BRDATAD(ASM, assembly, 8, 36, NUM_CHAN, "Channel Assembly Register"), + REG_RO|REG_FIT}, + {BRDATAD(FLAGS, chan_flags, 2, 32, NUM_CHAN, "Channel flags"), + REG_RO|REG_FIT}, + {NULL} +}; + +MTAB chan_mod[] = { + {0} +}; + +DEVICE chan_dev = { + "CH", chan_unit, chan_reg, chan_mod, + NUM_CHAN, 8, 15, 1, 8, 36, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + &dly_dib, DEV_DEBUG, 0, NULL, + NULL, NULL, &chan_help, NULL, NULL, &chan_description +}; + + +/* Nothing special to do, just return true if cmd is write and we got here */ +uint32 dly_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + if (cmd == IO_WRS) + return SCPE_OK; + return SCPE_NODEV; +} + + + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + if (chan_unit[i].flags & CHAN_AUTO) + chan_unit[i].flags &= ~CHAN_SET; + else + chan_unit[i].flags |= CHAN_SET; + chan_flags[i] = 0; + } + return chan_set_devs(dptr); +} + +/* Boot from given device */ +t_stat +chan_boot(int32 unit_num, DEVICE * dptr) +{ + /* Tell device to do a read, 3 records */ + /* Set channel address = 0, wc = 3, location = 0, CMD=0 */ + /* Set M[1] = TCO? 1, IC = 1 */ + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags); + + IC = 0; + chan_flags[chan] |= STA_ACTIVE; + chan_flags[chan] &= ~STA_PEND; + return SCPE_OK; +} + + +/* Execute the next channel instruction. */ +void +chan_proc() +{ + if (chan_flags[0] & CHS_ATTN) { + chan_flags[0] &= ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); + if (chan_flags[0] & DEV_SEL) + chan_flags[0] |= (DEV_DISCO); + } +} + + + +/* Issue a command to a channel */ +int +chan_cmd(uint16 dev, uint16 dcmd) +{ + UNIT *uptr; + uint32 chan; + DEVICE **dptr; + DIB *dibp; + int j; + + /* Find device on given channel and give it the command */ + chan = (dev >> 9) & 017; + if (chan >= NUM_CHAN) + return SCPE_IOERR; + /* If no channel device, quick exit */ + if (chan_unit[chan].flags & UNIT_DIS) + return SCPE_IOERR; + /* On 704 device new command aborts current operation */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO && + (chan_flags[chan] & (DEV_SEL | DEV_FULL | DEV_DISCO)) == DEV_SEL) { + chan_flags[chan] |= DEV_DISCO | DEV_WEOR; + return SCPE_BUSY; + } + /* Unit is busy doing something, wait */ + if (chan_flags[chan] & (DEV_SEL | DEV_DISCO | STA_TWAIT | STA_WAIT)) + return SCPE_BUSY; + /* Ok, try and find the unit */ + dev &= 07777; + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int r; + + dibp = (DIB *) (*dptr)->ctxt; + /* If no DIB, not channel device */ + if (dibp == NULL || dibp->ctype == CHAN_7909 || + (dibp->addr & dibp->mask) != (dev & dibp->mask)) + continue; + uptr = (*dptr)->units; + if (dibp->upc == 1) { + int num = (*dptr)->numunits; + + for (j = 0; j < num; j++) { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) { + bcnt[chan] = 6; + return r; + } + } + uptr++; + } + } else { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) { + bcnt[chan] = 6; + return r; + } + } + } + } + return SCPE_NODEV; +} + +/* + * Write a word to the assembly register. + */ +int +chan_write(int chan, t_uint64 * data, int flags) +{ + + /* Check if last data still not taken */ + if (chan_flags[chan] & DEV_FULL) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR|STA_WAIT); + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; /* We had error */ + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + chan_flags[chan] |= DEV_DISCO; + return TIME_ERROR; + } else { + if (chan == 0) + MQ = *data; + assembly[chan] = *data; + bcnt[chan] = 6; + chan_flags[chan] |= DEV_FULL; + chan_flags[chan] &= ~DEV_WRITE; + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_REOR; + } + } + + /* If Writing end of record, abort */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR); + return END_RECORD; + } + + return DATA_OK; +} + +/* + * Read next word from assembly register. + */ +int +chan_read(int chan, t_uint64 * data, int flags) +{ + + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* Check if he write out last data */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_WRITE; + chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT); + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + chan_flags[chan] |= DEV_DISCO; + return TIME_ERROR; + } else { + *data = assembly[chan]; + bcnt[chan] = 6; + chan_flags[chan] &= ~DEV_FULL; + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE); + chan_flags[chan] |= DEV_REOR; + } else + chan_flags[chan] |= DEV_WRITE; + } + return DATA_OK; +} + +/* + * Write a char to the assembly register. + */ +int +chan_write_char(int chan, uint8 * data, int flags) +{ + + /* Check if last data still not taken */ + if (chan_flags[chan] & DEV_FULL) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR|STA_WAIT); + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; /* We had error */ + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + chan_flags[chan] |= DEV_DISCO; + return TIME_ERROR; + } else { + int cnt = --bcnt[chan]; + t_uint64 wd = (chan == 0)? MQ:assembly[chan]; + wd &= 0007777777777LL; + wd <<= 6; + wd |= (*data) & 077; + if (chan == 0) + MQ = wd; + else + assembly[chan] = wd; + + if (cnt == 0) { + chan_flags[chan] |= DEV_FULL; + chan_flags[chan] &= ~DEV_WRITE; + } + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_FULL|DEV_REOR; + chan_flags[chan] &= ~DEV_WRITE; + } + } + + /* If Writing end of record, abort */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR); + return END_RECORD; + } + + return DATA_OK; +} + +/* + * Read next char from assembly register. + */ +int +chan_read_char(int chan, uint8 * data, int flags) +{ + + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* Check if he write out last data */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_WRITE; + chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT); + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + chan_flags[chan] |= DEV_DISCO; + return TIME_ERROR; + } else { + int cnt = --bcnt[chan]; + t_uint64 wd = assembly[chan]; + *data = 077 & (wd >> 30); + wd <<= 6; + wd |= 077 & (wd >> 36); + wd &= 0777777777777LL; + if (chan == 0) + MQ = wd; + assembly[chan] = wd; + if (cnt == 0) { + chan_flags[chan] &= ~DEV_FULL; + bcnt[chan] = 6; + } + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL); + chan_flags[chan] |= DEV_REOR; + } else + chan_flags[chan] |= DEV_WRITE; + } + return DATA_OK; +} + +void +chan9_set_error(int chan, uint32 mask) +{ +} + +t_stat +chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { + fprintf(st, "IBM 701 Channel\n\n"); + fprintf(st, "Psuedo device to display IBM 701 I/O. The IBM 701 used polled"); + fprintf(st, " I/O,\nThe assembly register and the flags can be displayed\n"); + fprintf(st, "There are no options for the this device\n"); + return SCPE_OK; +} + +const char *chan_description (DEVICE *dptr) { + return "IBM 701 Psuedo Channel"; +} + diff --git a/I7000/i701_cpu.c b/I7000/i701_cpu.c new file mode 100644 index 00000000..ac5bd392 --- /dev/null +++ b/I7000/i701_cpu.c @@ -0,0 +1,961 @@ +/* i701_cpu.c: IBM 701 CPU simulator + + 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. + + cpu 701 central processor + + The IBM 701 also know as "Defense Calculator" was introduced by IBM + on April 7, 1953. This computer was start of IBM 700 and 7000 line. + Memory was 2048 36 bit words. Each instruction could be signed plus + or minus, plus would access memory as 18 bit words, minus as 36 bit + words. There was a expansion option to add another 2048 words of + memory, but I can't find documentation on how it worked. Memory cycle + time was 12 microseconds. The 701 was withdrawn from the market + October 1, 1954 replaced by 704 and 702. A total of 19 machines were + installed. + + The system state for the IBM 701 is: + + AC AC register + MQ MQ register + IC<0:15> program counter + SSW<0:5> sense switches + SLT<0:3> sense lights + ACOVF AC overflow + DVC divide check + IOC I/O check + + The 701 had one instruction format: memory reference, + + 00000 000011111111 + S 12345 678901234567 + +-+-----+------------+ + | |opcod| address | memory reference + +-+-----+------------+ + + This routine is the instruction decode routine for the 701. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until a stop condition occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + illegal instruction + illegal I/O operation for device + illegal I/O operation for channel + breakpoint encountered + nested XEC's exceeding limit + divide check + I/O error in I/O simulator + + 3. Arithmetic. The 701 uses signed magnitude arithmetic for + integer and floating point calculations, and 2's complement + arithmetic for indexing calculations. + + 4. Adding I/O devices. These modules must be modified: + + i7090_defs.h add device definitions + i7090_chan.c add channel subsystem + i701_sys.c add sim_devices table entry +*/ + +#include "i7090_defs.h" + +#define HIST_XCT 1 /* instruction */ +#define HIST_INT 2 /* interrupt cycle */ +#define HIST_TRP 3 /* trap cycle */ +#define HIST_MIN 64 +#define HIST_MAX 65536 +#define HIST_NOEA 0x40000000 +#define HIST_PC 0x10000 + +struct InstHistory +{ + t_int64 ac; + t_int64 mq; + t_int64 op; + t_int64 sr; + uint32 ic; + uint16 ea; +}; + +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + + +t_uint64 M[MAXMEMSIZE] = { 0 }; /* memory */ +t_uint64 AC, MQ; /* registers */ +uint16 IC; /* program counter */ +uint8 SL; /* Sense lights */ +uint8 SW = 0; /* Sense switch */ +uint8 dcheck; /* Divide check */ +uint8 acoflag; /* AC Overflow */ +uint8 ihold = 0; /* Hold interrupts */ +uint16 iotraps; /* IO trap flags */ +t_uint64 ioflags; /* Trap enable flags */ +uint8 iocheck; +uint8 iowait; /* Waiting on io */ +uint8 dualcore; /* Set to true if dual core in + use */ +uint16 dev_pulse[NUM_CHAN]; /* SPRA device pulses */ +int cycle_time = 120; /* Cycle time of 12us */ + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ +extern uint32 drum_addr; +uint32 hsdrm_addr; +extern UNIT chan_unit[]; + +#undef AMASK /* Change definition of AMASK here */ +#define AMASK 00000000007777L + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + { UDATA(NULL, UNIT_BINK, MAXMEMSIZE / 2) }; + +REG cpu_reg[] = { + {ORDATAD(IC, IC, 15, "Instruction counter"), REG_FIT}, + {ORDATAD(AC, AC, 38, "Accumulator"), REG_FIT}, + {ORDATAD(MQ, MQ, 36, "Multiplier quotent"), REG_FIT}, + {ORDATAD(SL, SL, 4, "Lights"), REG_FIT}, + {ORDATAD(SW, SW, 6, "Switch register"), REG_FIT}, + {FLDATAD(SW1, SW, 0, "Switch 0"), REG_FIT}, + {FLDATAD(SW2, SW, 1, "Switch 1"), REG_FIT}, + {FLDATAD(SW3, SW, 2, "Switch 2"), REG_FIT}, + {FLDATAD(SW4, SW, 3, "Switch 3"), REG_FIT}, + {FLDATAD(SW5, SW, 4, "Switch 4"), REG_FIT}, + {FLDATAD(SW6, SW, 5, "Switch 5"), REG_FIT}, + {ORDATAD(ACOVF, acoflag, 1, "Overflow flag"), REG_FIT}, + {ORDATAD(IOC, iocheck, 1, "I/O Check flag"), REG_FIT}, + {ORDATAD(DVC, dcheck, 1, "Divide Check"), REG_FIT}, + {NULL} +}; + +MTAB cpu_mod[] = { + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, 0, 0, NULL, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description +}; + +/* Simulate instructions */ +t_stat +sim_instr(void) +{ + t_stat reason; + t_uint64 temp = 0; + t_uint64 ibr; + t_uint64 SR; + uint16 opcode; + uint16 MA; + uint8 f; + int shiftcnt; + int stopnext = 0; + int instr_count = 0; /* Number of instructions to execute */ + + if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); + } + + reason = 0; + + iowait = 0; + while (reason == 0) { /* loop until halted */ + +/* If doing fast I/O don't sit in idle loop */ + if (iowait == 0 && stopnext) + return SCPE_STEP; + + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + if (reason == SCPE_STEP && iowait) + stopnext = 1; + else + break; /* process */ + } + } + + if (iowait == 0 && sim_brk_summ && sim_brk_test(IC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + +/* Split out current instruction */ + if (iowait) { + /* If we are awaiting I/O complete, don't fetch. */ + sim_interval -= 6; + iowait = 0; + } else { + MA = IC >> 1; + sim_interval -= 24; /* count down */ + SR = ReadP(MA); + temp = SR; + if ((IC & 1) == 0) + temp >>= 18; + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = IC | HIST_PC; + hst[hst_p].ea = 0; + hst[hst_p].op = temp & RMASK; + hst[hst_p].ac = AC; + hst[hst_p].mq = MQ; + hst[hst_p].sr = 0; + } + IC = (IC + 1) & AMASK; + } + ihold = 0; + opcode = ((uint16)(temp >> 12L)) & 077; + MA = (uint16)(temp & AMASK); + ibr = SR = ReadP(MA>>1); + if ((opcode & 040) == 0) { + if (MA & 1) + SR <<= 18; + SR &= LMASK; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].sr = SR; + hst[hst_p].ea = MA; + } + + switch (opcode & 037) { + case 19: /* RND */ + if (MQ & ONEBIT) + AC++; + break; + case 30: /* SENSE */ + switch (MA) { + case 64: /* SLN */ + SL = 0; + break; + case 65: /* */ + SL |= 1; + break; + case 66: + SL |= 2; + break; + case 67: + SL |= 4; + break; + case 68: + SL |= 8; + break; + case 69: + if ((SW & 1) == 0) + IC++; + break; + case 70: + if ((SW & 2) == 0) + IC++; + break; + case 71: + if ((SW & 4) == 0) + IC++; + break; + case 72: + if ((SW & 8) == 0) + IC++; + break; + case 73: + if ((SW & 16) == 0) + IC++; + break; + case 74: + if ((SW & 32) == 0) + IC++; + break; + case 1024: + case 1025: + MA -= 1024; + dev_pulse[0] |= 1 << MA; + break; + case 522: + if (dev_pulse[0] & PRINT_I) + IC++; + dev_pulse[0] &= ~PRINT_I; + break; + case 512: + case 513: + case 514: + case 515: + case 516: + case 517: + case 518: + case 519: + case 520: + case 521: + MA = (MA - 512) + 5; + dev_pulse[0] |= 1 << MA; + break; + } + break; + case 0: /* STOP */ + /* Stop at HTR instruction if trapped */ + IC--; + halt: + reason = STOP_HALT; + /* Clear off any pending events before we halt */ + do { + + f = chan_active(0); + chan_proc(); + for (shiftcnt = 1; shiftcnt < NUM_CHAN; shiftcnt++) { + f |= chan_active(shiftcnt); + } + sim_interval = 0; + sim_process_event(); + } while (f); + if (reason != 0) + IC = MA; + break; + case 8: /* NO OP */ + break; + case 1: /* TR */ + IC = MA; + break; + case 4: /* TR 0 */ + f = (AC & AMMASK) == 0; + branch: + if (f) { + IC = MA; + } + break; + case 2: /* TR OV */ + f = acoflag; + acoflag = 0; + goto branch; + case 3: /* TR + */ + f = ((AC & AMSIGN) == 0); + goto branch; + case 10: /* R ADD */ + AC = ((SR & MSIGN) << 2) | (SR & PMASK); + sim_interval -= 6; + break; + case 6: /* R SUB */ + AC = (((SR & MSIGN) ^ MSIGN) << 2) | (SR & PMASK); + sim_interval -= 6; + break; + case 15: /* LOAD MQ */ + MQ = SR; + sim_interval -= 6; + break; + case 14: /* STORE MQ */ + SR = MQ; + goto store; + case 12: /* STORE */ + SR = AC & PMASK; + if (AC & AMSIGN) + SR |= MSIGN; +store: + if ((opcode & 040) == 0) { + SR &= LMASK; + if (MA & 1) { + ibr &= LMASK; + SR >>= 18; + } else { + ibr &= RMASK; + } + SR |= ibr; + } + WriteP(MA>>1, SR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].sr = SR; + } + sim_interval -= 6; + break; +/* Logic operations */ + case 13: /* EXTR or STORE A */ + if ((opcode & 040) == 0) { + SR &= ~(AMASK << 18); + SR |= AC & (AMASK << 18); + } else { + t_uint64 t = AC & PMASK; + if (AC & AMSIGN) + t |= MSIGN; + SR &= t; + } + goto store; + case 7: /* SUB AB */ + SR |= MSIGN; + goto iadd; + case 11: /* ADD AB */ + SR &= PMASK; + goto iadd; + case 5: /* SUB */ + SR ^= MSIGN; + /* Fall through */ + case 9: /* ADD */ + iadd: + f = 0; + /* Make AC Positive */ + if (AC & AMSIGN) { + f = 2; + AC &= AMMASK; + } + if (AC & APSIGN) + f |= 8; + /* Check signes of SR & AC */ + if (((SR & MSIGN) && ((f & 2) == 0)) || + (((SR & MSIGN) == 0) && ((f & 2) != 0))) { + AC ^= AMMASK; /* One's compliment */ + f |= 1; + } + AC = AC + (SR & PMASK); + /* Check carry from Q */ + if (f & 1) { /* Check if signs were not same */ + if (AC & AMSIGN) { + f ^= 2; + AC++; + if (((AC & APSIGN) != 0) != ((f & 8) != 0)) + acoflag = 1; + } else { + AC ^= AMMASK; /* One's compliment */ + } + } else { + if (((AC & APSIGN) != 0) != ((f & 8) != 0)) + acoflag = 1; + } + /* Restore sign to AC */ + AC &= AMMASK; + if (f & 2) + AC |= AMSIGN; + sim_interval -= 6; + break; + case 16: /* MPY */ + case 17: /* MPY R */ + shiftcnt = 043; + sim_interval -= 34 * 6; + f = 0; + /* Save sign */ + if (MQ & MSIGN) + f |= 1; + if (SR & MSIGN) + f |= 2; + SR &= PMASK; + MQ &= PMASK; + AC = 0; /* Clear AC */ + if (SR == 0) { + MQ = 0; + } else { + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= ONEBIT; + AC >>= 1; + } + } + if ((opcode & 037) == 17 && MQ & ONEBIT) + AC++; + if (f & 2) + f ^= 1; + if (f & 1) { + MQ |= MSIGN; + AC |= AMSIGN; + } + break; + case 18: /* DIV */ + shiftcnt = 043; + sim_interval -= 34 * 6; + /* Save sign */ + if (SR & MSIGN) { + SR &= PMASK; + f = 1; + } else + f = 0; + + if (AC & AMSIGN) + f |= 2; + + /* Check if SR less then AC */ + if (((SR - (AC & AMMASK)) & AMSIGN) || + (SR == (AC & AMMASK))) { + dcheck = 1; + MQ &= PMASK; + if (f == 1 || f == 2) + MQ |= MSIGN; + goto halt; + } + /* Clear signs */ + MQ &= PMASK; + AC &= AMMASK; + /* Do divide operation */ + do { + AC <<= 1; + AC &= AMMASK; + MQ <<= 1; + if (MQ & MSIGN) { + MQ ^= MSIGN; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + switch (f) { + case 0: + break; + case 3: + AC |= AMSIGN; + break; + case 2: + AC |= AMSIGN; + /* FALL THRU */ + case 1: + MQ |= MSIGN; + break; + } + break; + +/* Shift */ + case 20: /* L LEFT */ + shiftcnt = MA & 0377; + sim_interval -= (shiftcnt >> 6) * 6; + /* Save sign */ + if (MQ & MSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AQMASK; + while (shiftcnt-- > 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & MSIGN) + AC |= 1; + if (AC & APSIGN) + acoflag = 1; + } + /* Restore sign when done */ + AC &= AMMASK; + MQ &= PMASK; + if (f) { + AC |= AMSIGN; + MQ |= MSIGN; + } + break; + case 21: /* L RIGHT */ + shiftcnt = MA & 0377; + sim_interval -= (shiftcnt >> 6) * 6; + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + MQ &= PMASK; + while (shiftcnt-- > 0) { + if (AC & 1) + MQ |= MSIGN;; + MQ >>= 1; + AC >>= 1; + } + /* Restore sign when done */ + AC &= AMMASK; + if (f) { + AC |= AMSIGN; + MQ |= MSIGN; + } + break; + case 22: /* A LEFT */ + shiftcnt = MA & 0377; + sim_interval -= (shiftcnt >> 6) * 6; + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AQMASK; + while (shiftcnt-- > 0) { + AC <<= 1; + if (AC & APSIGN) + acoflag = 1; + } + /* Restore sign and overflow when done */ + AC &= AMMASK; + if (f) + AC |= AMSIGN; + break; + case 23: /* A RIGHT */ + shiftcnt = MA & 0377; + sim_interval -= (shiftcnt >> 6) * 6; + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + AC >>= shiftcnt; + /* Restore sign when done */ + if (f) + AC |= AMSIGN; + break; + +/* 704 Input output Instructions */ + case 29: /* SET DR */ + if (chan_test(0, DEV_SEL)) { + drum_addr = (uint32)MA; + chan_clear(0, DEV_FULL); /* Incase something got + read while waiting */ + } else + iocheck = 1; + break; + case 31: /* COPY */ + /* If no channel, set Iocheck and treat as nop */ + if (chan_unit[0].flags & UNIT_DIS) { + iocheck = 1; + break; + } + + /* If device disconnecting, just wait */ + if (chan_test(0, DEV_DISCO)) { + iowait = 1; + break; + } + + /* Instruct is NOP first time */ + /* Incomplete last word leaves result in MQ */ + if (chan_select(0)) { + extern uint8 bcnt[NUM_CHAN]; + chan_set(0, STA_ACTIVE); + switch (chan_flags[0] & (DEV_WRITE | DEV_FULL)) { + case DEV_WRITE | DEV_FULL: + case 0: + /* On EOR skip 1, on EOF skip two */ + if (chan_test(0, CHS_EOF|CHS_EOT|DEV_REOR)) + chan_set(0, DEV_DISCO); + iowait = 1; + break; + case DEV_WRITE: + MQ = assembly[0] = SR; + bcnt[0] = 6; + chan_set(0, DEV_FULL); + break; + case DEV_FULL: + SR = MQ; + WriteP(MA, MQ); + bcnt[0] = 6; + chan_clear(0, DEV_FULL); + break; + } + } else { + if (chan_stat(0, CHS_EOF|CHS_EOT)) { + IC++; + /* On EOR skip two */ + } else if (chan_stat(0, DEV_REOR)) { + IC += 2; + /* Advance 1 on Error and set iocheck */ + } else if (chan_stat(0, CHS_ERR)) { + iocheck = 1; + IC++; + } + chan_clear(0, STA_ACTIVE|DEV_REOR|CHS_ERR); + break; + } + break; + +/* Input/Output Instuctions */ + case 24: /* Read select */ + opcode = IO_RDS; + MQ = 0; + goto iostart; + case 26: /* Write select */ + opcode = IO_WRS; + goto iostart; + case 27: /* Write EOF */ + opcode = IO_WEF; + goto iostart; + case 25: /* Read Backwards */ + opcode = IO_RDB; + MQ = 0; + goto iostart; + case 28: /* Rewind */ + opcode = IO_REW; + iostart: + switch (chan_cmd(MA, opcode)) { + case SCPE_BUSY: + iowait = 1; /* Channel is active, hold */ + case SCPE_OK: + ihold = 1; /* Hold interupts for one */ + break; + case SCPE_IOERR: + iocheck = 1; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + } + break; + + default: + reason = STOP_UUO; + break; + } + + chan_proc(); /* process any pending channel events */ + if (instr_count != 0 && --instr_count == 0) + return SCPE_STEP; + } /* end while */ + +/* Simulation halted */ + + return reason; +} + + +/* Reset routine */ + +t_stat +cpu_reset(DEVICE * dptr) +{ + AC = 0; + MQ = 0; + dualcore = 0; + iotraps = 0; + ioflags = 0; + dcheck = acoflag = iocheck = 0; + sim_brk_types = sim_brk_dflt = SWMASK('E'); + return SCPE_OK; +} + +/* Memory examine */ + +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= (MEMSIZE * 2)) + return SCPE_NXM; + if (vptr == NULL) + return SCPE_OK; + + *vptr = M[(addr & 07777) >> 1]; + if ((addr & 0400000) == 0) { + if ( addr & 1) + *vptr <<= 18; + else + *vptr &= LMASK; + } + *vptr &= 0777777777777L; + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + t_addr a = (addr >> 1) & 03777; + + if (addr >= (MEMSIZE * 2)) + return SCPE_NXM; + if ((addr & 0400000) == 0) { + if (addr & 1) { + M[a] &= LMASK; + M[a] |= (val >> 18) & RMASK; + } else { + M[a] &= RMASK; + M[a] |= val & LMASK; + } + } else + M[a] = val & 0777777777777L; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].ic = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + t_stat r; + t_value sim_eval; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "IC AC MQ EA SR\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ic & HIST_PC) { /* instruction? */ + fprintf(st, "%06lo ", h->ic & AMASK); + switch (h->ac & (AMSIGN | AQSIGN | APSIGN)) { + case AMSIGN | AQSIGN | APSIGN: + fprintf(st, "-QP"); + break; + case AMSIGN | AQSIGN: + fprintf(st, " -Q"); + break; + case AMSIGN | APSIGN: + fprintf(st, " -P"); + break; + case AMSIGN: + fprintf(st, " -"); + break; + case AQSIGN | APSIGN: + fprintf(st, " QP"); + break; + case AQSIGN: + fprintf(st, " Q"); + break; + case APSIGN: + fprintf(st, " P"); + break; + case 0: + fprintf(st, " "); + break; + } + fprint_val(st, h->ac & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + if (h->mq & MSIGN) + fputc('-', st); + else + fputc(' ', st); + fprint_val(st, h->mq & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->ea, 8, 12, PV_RZRO); + fputc(' ', st); + if (h->sr & MSIGN) + fputc('-', st); + else + fputc(' ', st); + fprint_val(st, h->sr & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + sim_eval = h->op; + if ( + (fprint_sym + (st, h->ic & AMASK, &sim_eval, &cpu_unit, + SWMASK('M'))) > 0) fprintf(st, "(undefined) %012llo", + h->op); + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ + return "IBM 701 CPU"; +} + +t_stat +cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "The CPU behaves as a IBM 701\n"); + fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); + fprintf (st, " -c examine/deposit characters, 6 per word\n"); + fprintf (st, " -l examine/deposit half words\n"); + fprintf (st, " -m examine/deposit IBM 701 instructions\n\n"); + fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n"); + fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); + fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); + fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); + fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); + fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + +return SCPE_OK; +} + diff --git a/I7000/i701_sys.c b/I7000/i701_sys.c new file mode 100644 index 00000000..3d4acc0f --- /dev/null +++ b/I7000/i701_sys.c @@ -0,0 +1,477 @@ +/* i701_sys.c: IBM 701 Simulator system interface. + + 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 "i7090_defs.h" +#include "sim_card.h" +#include + +t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 701"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#ifdef MT_CHANNEL_ZERO + &mtz_dev, +#endif +#ifdef NUM_DEVS_DR + &drm_dev, +#endif + NULL +}; + +#ifdef NUM_DEVS_CDR +DIB cdp_dib = { CH_TYP_PIO, 1, 02000, 07777, &cdp_cmd, &cdp_ini }; +#endif +#ifdef NUM_DEVS_CDP +DIB cdr_dib = { CH_TYP_PIO, 1, 04000, 07777, &cdr_cmd, NULL }; +#endif +#ifdef NUM_DEVS_DR +DIB drm_dib = { CH_TYP_PIO, 1, 0200, 07774, &drm_cmd, &drm_ini }; +#endif +#ifdef NUM_DEVS_LPR +DIB lpr_dib = { CH_TYP_PIO, 1, 01000, 07777, &lpr_cmd, &lpr_ini }; +#endif +#ifdef MT_CHANNEL_ZERO +DIB mt_dib = { CH_TYP_PIO, NUM_UNITS_MT, 0400, 07770, &mt_cmd, &mt_ini }; +#endif + + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Nested indirects exceed limit", + "Nested XEC's exceed limit", + "I/O Check opcode", + "Memory management trap during trap", + "7750 invalid line number", + "7750 invalid message", + "7750 No free output buffers", + "7750 No free input buffers", "Error?", "Error2", 0 +}; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CHANNEL", DEBUG_CHAN, "Debug Channel use"}, + {"TRAP", DEBUG_TRAP, "Show CPU Traps"}, + {"CMD", DEBUG_CMD, "Show device commands"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show detailed device information"}, + {"EXP", DEBUG_EXP, "Show device exceptions"}, + {"SENSE", DEBUG_SNS, "Show sense data on 7909 channel"}, + {0, 0} +}; + +DEBTAB crd_debug[] = { + {"CHAN", DEBUG_CHAN}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"CARD", DEBUG_CARD}, + {0, 0} +}; + +/* Load a card image file into memory. */ + +t_stat +sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + t_uint64 wd; + t_uint64 mask; + int addr = 0; + int dlen = 0; + char *p; + char buf[160]; + + if (match_ext(fnam, "crd")) { + int firstcard = 1; + uint16 cbuf[80]; + t_uint64 lbuff[24]; + int i; + + while (sim_fread(cbuf, 2, 80, fileref) == 80) { + + /* Bit flip into read buffer */ + for (i = 0; i < 24; i++) { + int bit = 1 << (i / 2); + int b = 36 * (i & 1); + int col; + + mask = 1; + wd = 0; + for (col = 35; col >= 0; mask <<= 1) { + if (cbuf[col-- + b] & bit) + wd |= mask; + } + lbuff[i] = wd; + } + i = 2; + if (firstcard) { + addr = 0; + dlen = 3 + (int)((lbuff[0] >> 18) & 077777); + firstcard = 0; + i = 0; + } else if (dlen == 0) { + addr = (int)(lbuff[0] & 077777); + dlen = (int)(lbuff[0] >> 18) & 077777; + } + for (; i < 24 && dlen > 0; i++) { + M[addr++] = lbuff[i]; + dlen--; + } + } + } else if (match_ext(fnam, "oct")) { + while (fgets(&buf[0], 160, fileref) != 0) { + for(p = &buf[0]; *p == ' ' || *p == '\t'; p++); + /* Grab address */ + for(addr = 0; *p >= '0' && *p <= '7'; p++) + addr = (addr << 3) + *p - '0'; + while(*p != '\n' && *p != '\0') { + for(; *p == ' ' || *p == '\t'; p++); + for(wd = 0; *p >= '0' && *p <= '7'; p++) + wd = (wd << 3) + *p - '0'; + if (addr < MAXMEMSIZE) + M[addr++] = wd; + } + } + } else if (match_ext(fnam, "txt")) { + while (fgets(&buf[0], 160, fileref) != 0) { + for(p = &buf[0]; *p == ' ' || *p == '\t'; p++); + /* Grab address */ + for(addr = 0; *p >= '0' && *p <= '7'; p++) + addr = (addr << 3) + *p - '0'; + while(*p == ' ' || *p == '\t') p++; + if(sim_strncasecmp(p, "BCD", 3) == 0) { + p += 3; + parse_sym(++p, addr, &cpu_unit, &M[addr], SWMASK('C')); + } else if (sim_strncasecmp(p, "OCT", 3) == 0) { + p += 3; + for(; *p == ' ' || *p == '\t'; p++); + parse_sym(p, addr, &cpu_unit, &M[addr], 0); + } else { + parse_sym(p, addr, &cpu_unit, &M[addr], SWMASK('M')); + } + } + } else + return SCPE_ARG; + return SCPE_OK; +} + +/* Symbol tables */ +typedef struct _opcode +{ + uint16 opbase; + CONST char *name; +} +t_opcode; + +/* Opcodes */ +t_opcode base_ops[] = { + {0, "STOP"}, + {1, "TR"}, + {2, "TRO"}, + {3, "TRP"}, + {4, "TRZ"}, + {5, "SUB"}, + {6, "R SUB"}, + {7, "SUB AB"}, + {8, "NO OP"}, + {9, "ADD"}, + {10, "R ADD"}, + {11, "ADD AB"}, + {12, "STORE"}, + {13, "STORE A"}, + {14, "STORE MQ"}, + {15, "LOAD MQ"}, + {16, "MPY"}, + {17, "MPY R"}, + {18, "DIV"}, + {19, "ROUND"}, + {20, "L LEFT"}, + {21, "L RIGHT"}, + {22, "A LEFT"}, + {23, "A RIGHT"}, + {24, "READ"}, + {25, "READ B"}, + {26, "WRITE"}, + {27, "WRITE EF"}, + {28, "REWIND"}, + {29, "SET DR"}, + {30, "SENSE"}, + {31, "COPY"}, + {13 + 040, "EXTR"}, + {0, NULL} +}; + +const char *chname[] = { "*" }; + + + +/* Parse address + + Inputs: + *dptr = pointer to device. + *cptr = pointer to string. + **tptr = pointer to final scaned character. + Outputs: + address with sign. +*/ + +t_addr +parse_addr(DEVICE *dptr, const char *cptr, const char **tptr) { + t_addr v; + int s = 0; + *tptr = cptr; + if (dptr != &cpu_dev) + return 0; + v = 0; + if (*cptr == '-') { + cptr++; + s = 1; + } + while(*cptr >= '0' && *cptr <= '7') { + v <<= 3; + v += *cptr++ - '0'; + } + if (v > 4096) + return 0; + if (s) { + if ((cptr - 1) != *tptr) + *tptr = cptr; + v |= 0400000; + } else { + if (cptr != *tptr) + *tptr = cptr; + } + return v; +} + + + +void sys_init(void) { + sim_vm_parse_addr = &parse_addr; +} + +void (*sim_vm_init) (void) = &sys_init; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat +fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw) +{ + t_uint64 inst = *val; + +/* Print value in octal first */ + fputc(' ', of); + fprint_val(of, inst, 8, 36, PV_RZRO); + + if (sw & SWMASK('M')) { + int op = (int)(inst >> (12+18)); + int i; + fputs(" rt ", of); + if (op != (040 + 13)) + op &= 037; + for(i = 0; base_ops[i].name != NULL; i++) { + if (base_ops[i].opbase == op) { + fputs(base_ops[i].name, of); + break; + } + } + fputc(' ', of); + if ((inst >> 18) & 0400000L) + fputc('-', of); + fprint_val(of, (inst >> 18) & 0000000007777L, 8, 12, PV_RZRO); + op = (int)(inst >> 12); + fputs(" lt ", of); + if (op != (040 + 13)) + op &= 037; + for(i = 0; base_ops[i].name != NULL; i++) { + if (base_ops[i].opbase == op) { + fputs(base_ops[i].name, of); + break; + } + } + fputc(' ', of); + if (inst & 0400000L) + fputc('-', of); + else + fputc(' ', of); + fprint_val(of, inst & 0000000007777L, 8, 12, PV_RZRO); + } + + if (sw & SWMASK('C')) { + int i; + + fputs(" '", of); + for (i = 5; i >= 0; i--) { + int ch; + + ch = (int)(inst >> (6 * i)) & 077; + fputc(sim_six_to_ascii[ch], of); + } + fputc('\'', of); + } + return SCPE_OK; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + int f; + t_value d; + t_addr tag; + int sign; + char opcode[100]; + const char *arg; + + while (isspace(*cptr)) + cptr++; + d = 0; + if (sw & SWMASK('M')) { + t_opcode *op; + + do { + i = 0; + sign = 0; + f = 0; + if (*cptr == ',') { + d <<= 18; + cptr++; + } + /* Skip blanks */ + while (isspace(*cptr)) + cptr++; + /* Grab opcode */ + cptr = get_glyph(cptr, opcode, ','); + + if ((op = find_opcode(opcode, base_ops)) != 0) { + d |= (t_uint64) op->opbase << 12; + } else { + return STOP_UUO; + } + + cptr = get_glyph(cptr, opcode, ','); + tag = parse_addr(&cpu_dev, opcode, &arg); + if (*arg != opcode[0]) + d += (t_value)tag; + } while (*cptr == ','); + if (*cptr != '\0') + return STOP_UUO; + *val = d; + return SCPE_OK; + } else if (sw & SWMASK('C')) { + i = 0; + while (*cptr != '\0' && i < 6) { + d <<= 6; + if (sim_ascii_to_six[0177 & *cptr] != -1) + d |= sim_ascii_to_six[0177 & *cptr]; + cptr++; + i++; + } + while (i < 6) { + d <<= 6; + d |= 060; + i++; + } + } else { + if (*cptr == '-') { + sign = 1; + cptr++; + } else { + sign = 0; + if (*cptr == '+') + cptr++; + } + while (*cptr >= '0' && *cptr <= '7') { + d <<= 3; + d |= *cptr++ - '0'; + } + if (sign) + d |= 00400000000000L; + } + *val = d; + return SCPE_OK; +} diff --git a/I7000/i7070_chan.c b/I7000/i7070_chan.c new file mode 100644 index 00000000..007facc5 --- /dev/null +++ b/I7000/i7070_chan.c @@ -0,0 +1,1597 @@ +/* i7070_chan.c: IBM 7070 Channel simulator + + 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. + + channel + + The system state for the IBM 7070 channel is: + There are 4 types of channel: + PIO: Basic polled mode transfer. Channel only manages + status and disconnect of devices. + 7604: Basic channel. + 7907: Enhanced channel for disk, hypertape and com controlers. + + Common registers to all but PIO channels. + ADDR<0:16> Location to read or write next word from. + CMD<0:6> Channel command. + LIMIT<0:16> Transfer limit + ASM<0:44> Assembled data from devices. + LOCATION<0:16> Address of next command. + + Simulation registers to handle device handshake. + STATUS<0:16> Simulated register for basic channel status. + SENSE<0:16> Additional flags for 7907 channels. +*/ + +#include "i7070_defs.h" + +extern UNIT cpu_unit; + +#define CHAN_DEF UNIT_DISABLE|CHAN_SET + +t_stat chan_reset(DEVICE * dptr); +void chan_fetch(int chan); +t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *chan_description (DEVICE *dptr); + + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +uint32 location[NUM_CHAN]; /* Location of RDW instruction*/ +uint32 caddr[NUM_CHAN]; /* Channel memory address */ +uint8 bcnt[NUM_CHAN]; /* Channel character count */ +uint8 cmd[NUM_CHAN]; /* Current command */ +uint8 op[NUM_CHAN]; /* Operators for 7907 channel */ +uint32 limit[NUM_CHAN]; /* Word count */ +t_uint64 assembly[NUM_CHAN]; /* Assembly register */ +uint32 chan_flags[NUM_CHAN]; /* Unit status */ +uint32 chan_info[NUM_CHAN]; /* Private channel info */ +uint8 chan_irq[NUM_CHAN]; /* Channel has a irq pending */ +extern uint16 pri_latchs[10]; + +#define CHAN_OUTDEV 0x010000 /* Type out device */ +#define CHAN_PRIO 0x008000 /* Channel has priority pending */ +#define CHAN_TWE 0x004000 /* Channel format error */ +#define CHAN_SEOR 0x002000 /* Channel saw a eor */ +#define CHAN_NORDW 0x020000 /* No RDW for this command */ +#define CHAN_SEOS 0x040000 /* Channel saw a end of segment */ +#define CHAN_SCLR 0x080000 /* Short record */ +#define CHAN_FIRST 0x100000 /* First tranfered word */ +#define CHAN_START 0x200000 /* Channel has just started */ +#define CHAN_OCTAL 0x400000 /* Octal conversion */ + +const char *chan_type_name[] = { + "Polled", "Unit Record", "7604", "7907", ""}; + + +UNIT chan_unit[] = { + /* Puesdo channel for PIO devices */ + {UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_UREC)|UNIT_S_CHAN(CHAN_CHUREC),0)}, + /* Normal channels */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_A)|CHAN_S_TYPE(CHAN_7604),0)},/* A */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_B)|CHAN_S_TYPE(CHAN_7604),0)},/* B */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_C)|CHAN_S_TYPE(CHAN_7604),0)},/* C */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_D)|CHAN_S_TYPE(CHAN_7604),0)},/* D */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_E)|CHAN_S_TYPE(CHAN_7907),0)},/* E */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_F)|CHAN_S_TYPE(CHAN_7907),0)},/* F */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_G)|CHAN_S_TYPE(CHAN_7907),0)},/* G */ + {UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_H)|CHAN_S_TYPE(CHAN_7907),0)} /* H */ +}; + +REG chan_reg[] = { + {BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(LIMIT, limit, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(ASM, assembly, 16, 44, NUM_CHAN), REG_VMIO|REG_RO|REG_FIT}, + {BRDATA(LOCATION, location, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT}, + {NULL} +}; + +MTAB chan_mod[] = { + {CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", NULL, NULL,NULL,NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7604), "7604", NULL, NULL, NULL, NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7907), "7907", NULL, NULL, NULL, NULL}, + {MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL, "Show units on channel"}, + {0} +}; + +/* Simulator debug controls */ +DEBTAB chn_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {"CH0", 0x0100 << 0}, + {"CH1", 0x0100 << 1}, + {"CH2", 0x0100 << 2}, + {"CH3", 0x0100 << 3}, + {"CH4", 0x0100 << 4}, + {"CHA", 0x0100 << 5}, + {"CHB", 0x0100 << 6}, + {"CHC", 0x0100 << 7}, + {"CHD", 0x0100 << 8}, + {0, 0} +}; + +DEVICE chan_dev = { + "CH", chan_unit, chan_reg, chan_mod, + NUM_CHAN, 10, 18, 1, 10, 44, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, chn_debug, + NULL, NULL, &chan_help, NULL, NULL, &chan_description +}; + +#define DELTA_CHAR 057 +#define SM_CHAR 037 +#define SM_MEM 0x39 +#define RM_CHAR 0x80 + +/* Translation tables */ +uint8 bcd_mem[64] = { + /* ? 1 2 3 4 5 6 7 */ +/* 00 */ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + /* 8 9 0 =/# !/@ ? ? tm */ +/* 10 */ 0x98, 0x99, 0x90, 0x45, 0x46, 0x47, 0x48, 0x49, + /* sp / S T U V W X */ +/* 20 */ 0x60, 0x31, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /* Y Z rm , %/( ? ? sm */ +/* 30 */ 0x88, 0x89, 0x80, 0x35, 0x36, 0x37, 0x38, 0x39, + /* - J K L M N O P */ +/* 40 */ 0x30, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + /* Q R -0 $ * ? ? del */ +/* 50 */ 0x78, 0x79, 0x70, 0x25, 0x26, 0x27, 0x28, 0xFF, + /*+/& A B C D E F G */ +/* 60 */ 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* H I +0 . sq ? ? gm */ +/* 70 */ 0x68, 0x69, 0x60, 0x15, 0x16, 0x17, 0x18, 0x19 +}; + +uint8 mem_bcd[256] = { + /* sp */ +/* 00 */ 020, 000, 000, 000, 000, 000, 000, 000, + /* */ +/* 08 */ 000, 000, 000, 000, 000, 000, 000, 000, + /* sq ? */ +/* 10 */ 000, 000, 000, 000, 000, 073, 074, 075, + /* ? ? */ +/* 18 */ 076, 077, 000, 000, 000, 000, 000, 000, + /*+/- $ * ? */ +/* 20 */ 060, 000, 000, 000, 000, 053, 054, 055, + /* ? +/- */ +/* 28 */ 056, 060, 000, 000, 000, 000, 000, 000, + /* - / , %/( ? */ +/* 30 */ 040, 021, 000, 000, 000, 033, 034, 035, + /* ? sm */ +/* 38 */ 036, 037, 000, 000, 000, 000, 000, 000, + /* =/# !/@ ? */ +/* 40 */ 000, 000, 000, 000, 000, 013, 014, 015, + /* ? tm */ +/* 48 */ 016, 017, 000, 000, 000, 000, 000, 000, + /* */ +/* 50 */ 000, 000, 000, 000, 000, 000, 000, 000, + /* */ +/* 58 */ 000, 000, 000, 000, 000, 000, 000, 000, + /* +0 A B C D E F G */ +/* 60 */ 072, 061, 062, 063, 064, 065, 066, 067, + /* H I */ +/* 68 */ 070, 071, 000, 000, 000, 000, 000, 000, + /* -0 J K L M N O P */ +/* 70 */ 052, 041, 042, 043, 044, 045, 046, 047, + /* Q R */ +/* 78 */ 050, 051, 000, 000, 000, 000, 000, 000, + /* rm S T U V W X */ +/* 80 */ 032, 000, 022, 023, 024, 025, 026, 027, + /* Y Z */ +/* 88 */ 030, 031, 000, 000, 000, 000, 000, 000, + /* 0 1 2 3 4 5 6 7 */ +/* 90 */ 012, 001, 002, 003, 004, 005, 006, 007, + /* 8 9 */ +/* 98 */ 010, 011, 000, 000, 000, 000, 000, 000 +}; + + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + chan_flags[i] = 0; + chan_info[i] = 0; + caddr[i] = 0; + cmd[i] = 0; + bcnt[i] = 10; + chan_irq[i] = 0; + limit[i] = 0; + location[i] = 0; + } + return chan_set_devs(dptr); +} + +/* Boot from given device */ +t_stat +chan_boot(int32 unit_num, DEVICE * dptr) +{ + return SCPE_NOFNC; /* Not implimented until I know how boot work */ +} + +t_stat +chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) { + DEVICE **dptr; + DIB *dibp; + uint32 j; + UNIT *uptr; + + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int r; + + dibp = (DIB *) (*dptr)->ctxt; + /* If no DIB, not channel device */ + if (dibp == 0) + continue; + uptr = (*dptr)->units; + /* If this is a 79XX device, check it */ + if (dibp->ctype & CH_TYP_79XX) { + for (j = 0; j < (*dptr)->numunits; j++, uptr++) { + if ((uptr->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(uptr->flags) == chan && + (dev == ((UNIT_SELECT & uptr->flags) != 0))) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + } else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) { + if (dibp->upc == 1) { + for (j = 0; j < (*dptr)->numunits; j++) { + if ((uptr->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + uptr++; + } + } else { + if ((uptr->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + } + } + return SCPE_NODEV; +} + +/* Execute the next channel instruction. */ +void +chan_proc() +{ + int chan; + int cmask; + + /* Scan channels looking for work */ + for (chan = 0; chan < NUM_CHAN; chan++) { + /* Skip if channel is disabled */ + if (chan_unit[chan].flags & UNIT_DIS) + continue; + + cmask = 0x0100 << chan; + switch (CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_UREC: + case CHAN_7604: + /* If channel is disconnecting, do nothing */ + if (chan_flags[chan] & DEV_DISCO) + continue; + + /* If device requested attention, abort current command */ + if (chan_flags[chan] & CHS_ATTN) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn\n", + chan); + chan_flags[chan] &= + ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + continue; + } + + /* If no select, stop channel */ + if ((chan_flags[chan] & DEV_SEL) == 0 + && (chan_flags[chan] & STA_TWAIT)) { + t_uint64 temp; + int adr; +chan_trap: + if (chan != 0) { + adr = 100 + (chan * 10) + (chan_info[chan] & 0xf); + temp = 2; + if (chan_info[chan] & CHAN_TWE) + temp = 0; + else if (chan_flags[chan] & CHS_ERR) + temp = 1; + else if (chan_flags[chan] & CHS_EOF) + temp = 5; + else if (chan_info[chan] & CHAN_SEOS) + temp = 6; + else if (chan_info[chan] & CHAN_SCLR) + temp = 7; + else if ((chan_info[chan] & CHAN_NORDW) == 0) { + if ((chan_info[chan] & CHAN_SEOR) == 0 && + caddr[chan] > limit[chan]) + temp = 4; + else if (caddr[chan] < limit[chan]) + temp = 3; + } + chan_flags[chan] &= ~(CHS_ERR|CHS_EOF); + temp <<= 32; + if (chan_info[chan] & CHAN_NORDW) { + temp |= M[adr] & 0xFFFFFFFFLL; + } else { + upd_idx(&temp, caddr[chan]); + bin_dec(&temp, location[chan], 0, 4); + } + temp |= PSIGN; + /* Copy over flag */ + temp |= M[adr] & 0xF000000000LL; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, + "chan %d Trap: %012llx prio=%d\n\r", chan, + temp, (chan_info[chan]&CHAN_PRIO)?1:0); + M[adr] = temp; + if ((chan_info[chan] & CHAN_PRIO) || + ((temp >> 32) & 0xf) != 2) + pri_latchs[chan] |= 1 << (chan_info[chan] & 0xF); + chan_info[chan] &= ~CHAN_PRIO; + } else if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap %04x\n", + chan, chan_info[chan]); + chan_flags[chan] &= + ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT); + continue; + } + + /* No activity, nothing happening here folks, move along */ + if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) { + /* This could be a no data command, if pending priorty + request, ask device if it is ready */ + if ((cmd[chan] & CHN_SEGMENT) == 0 && + chan_info[chan] & CHAN_PRIO && + chan_issue_cmd(chan, IO_TRS, chan_info[chan]&0xf) + == SCPE_OK) + goto chan_trap; + continue; + } + + /* If first time through here, load up channel control */ + if (chan_flags[chan] & STA_ACTIVE && chan_info[chan] & CHAN_START) + chan_fetch(chan); + + /* Process reading of a segment command */ + if ((cmd[chan] & (CHN_SEGMENT|CHN_RM_FND)) == + (CHN_SEGMENT|CHN_RM_FND)) { + /* Two backspaces and a read */ + switch (cmd[chan] & (CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS)) { + case CHN_RM_FND: + if (chan_issue_cmd(chan, IO_BSR, + chan_info[chan]&0xf) == SCPE_OK) { + cmd[chan] |= CHN_COMPRESS; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "segment %d bsr 2\n\r", chan); + } + break; + case CHN_RM_FND|CHN_COMPRESS: + if (chan_issue_cmd(chan, IO_BSR, + chan_info[chan]&0xf) == SCPE_OK) { + cmd[chan] |= CHN_NUM_MODE; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "segment %d bsr 2\n\r", chan); + if (chan_flags[chan] & CHS_BOT) { + chan_flags[chan] &= ~(STA_ACTIVE); + goto chan_trap; + } + } + break; + case CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS: + chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST); + if ( chan_issue_cmd(chan, IO_RDS, + chan_info[chan]&0xf) == SCPE_OK) { + cmd[chan] &= ~(CHN_NUM_MODE|CHN_COMPRESS|CHN_RM_FND); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "segment %d read\n\r", chan); + chan_flags[chan] &= ~(STA_WAIT|DEV_REOR); + } + break; + } + /* Device ready, decide command to issue */ + if (cmd[chan] & CHN_RECORD) { + if (chan_flags[chan] & CHS_BOT) { + chan_flags[chan] |= STA_TWAIT; + } + /* Two backspaces and a read */ + } else if (chan_flags[chan] & CHS_EOT) { + chan_flags[chan] |= STA_TWAIT; + } + continue; + } + + /* None disabled, active channel is if transfering */ + switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) { + /* Device has given us a dataword */ + case DEV_FULL: + /* Process reading of a segment command */ + if (cmd[chan] & CHN_SEGMENT) { + /* Check if hit end of record */ +#if 0 /* Check segment operation correct before removing */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chk segment %d %d data = %012llx", + chan, bcnt[chan], assembly[chan]); + + if ((chan_flags[chan] & (STA_WAIT|DEV_REOR)) + == (STA_WAIT|DEV_REOR)) { + chan_flags[chan] &= ~(STA_WAIT|DEV_REOR|DEV_FULL); + chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST); + continue; + } + + if (bcnt[chan] >= 6 && chan_flags[chan] & DEV_REOR) { + if (chan_info[chan] & CHAN_SEOS) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, " found\n"); + /* Correct error */ + chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR); + /* What we were looking for? */ + if (caddr[chan] >= limit[chan]) { + chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF); + chan_flags[chan] |= STA_TWAIT; + chan_info[chan] |= CHAN_SEOR; + } else + caddr[chan]++; + } + chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST|CHAN_TWE); + chan_flags[chan] &= ~(DEV_FULL|DEV_REOR|CHS_ERR); + } else + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, " search\n"); + /* How about regular record */ + chan_flags[chan] &= ~DEV_FULL; + /* Wait for next record */ + chan_flags[chan] |= STA_WAIT|DEV_DISCO; + if (cmd[chan] & CHN_RECORD) + cmd[chan] |= CHN_RM_FND; + else + cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE; + assembly[chan] = 0; + bcnt[chan] = 10; +#endif + continue; + } + + /* If we are not waiting EOR save it in memory */ + if ((chan_flags[chan] & STA_WAIT) == 0) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data < %011llx\n", + chan, assembly[chan]); + if (caddr[chan] < MEMSIZE) + M[caddr[chan]] = assembly[chan]; + + if (bcnt[chan] != 0) + chan_info[chan] |= CHAN_SCLR; + else + chan_info[chan] &= ~CHAN_SCLR; + /* Check if last transfer before final */ + if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) { + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT; + } else + caddr[chan]++; + + /* Update channel status word */ + if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) { + int adr = 100 + (chan * 10) + (chan_info[chan]&0xf); + upd_idx(&M[adr], caddr[chan]); + bin_dec(&M[adr], location[chan], 0, 4); + } + + /* Check for record mark */ + if ((cmd[chan] & CHN_RECORD) && + (assembly[chan] & SMASK) == ASIGN && + (assembly[chan] & 0xFF) == RM_CHAR) { + if (cmd[chan] & CHN_LAST) { + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT; + } else + chan_fetch(chan); + } + bcnt[chan] = 10; + assembly[chan] = 0; + } + chan_info[chan] |= CHAN_FIRST; /* Saved one char */ + chan_flags[chan] &= ~DEV_FULL; + + /* Device does not need a word and has not given us one */ + case 0: + if (chan_flags[chan] & DEV_REOR) { + /* Check EOR at end of segment */ + if (cmd[chan] & CHN_SEGMENT) { + if ((chan_info[chan] & CHAN_FIRST) == 0 && + bcnt[chan] == 8 && + assembly[chan] == (ASIGN|(((t_uint64)SM_MEM) << 32))){ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chk segment %d %d data = %012llx found\n\r", + chan, bcnt[chan], assembly[chan]); + + /* What we were looking for? */ + if (caddr[chan] >= limit[chan]) { + chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF); + chan_flags[chan] |= STA_TWAIT; + } else + caddr[chan]++; + } else + /* Check if hit end of record */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chk segment %d %d data = %012llx search\n\r", + chan, bcnt[chan], assembly[chan]); + /* Correct error */ + chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR); + + assembly[chan] = 0; + bcnt[chan] = 10; + chan_flags[chan] &= ~(DEV_REOR|DEV_FULL); + /* Wait for next record */ + chan_flags[chan] |= STA_WAIT|DEV_DISCO; + if (cmd[chan] & CHN_RECORD) + cmd[chan] |= CHN_RM_FND; + else + cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE; + continue; + } + /* Device idle, expecting data from it */ + /* Check if got EOR */ + chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE|STA_WAIT); + chan_flags[chan] |= STA_TWAIT|DEV_DISCO; + chan_info[chan] |= CHAN_SEOR; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR< %o\n", + chan, cmd[chan]); + continue; + } + if (caddr[chan] > limit[chan] + && (chan_flags[chan] & STA_WAIT) == 0) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, + "chan %d < WC0 %o\n", chan, cmd[chan]); + if (cmd[chan] & CHN_LAST) { + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_DISCO; + chan_info[chan] &= ~CHAN_SEOR; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, + "chan %d < DISCO\n", chan); + } else + chan_fetch(chan); + } + break; + + /* Device has word, but has not taken it yet */ + case DEV_WRITE | DEV_FULL: + continue; /* Do nothing if no data xfer pending */ + + /* Device needs a word of data */ + case DEV_WRITE: /* Device needs data word */ + /* If we are waiting on EOR, do nothing */ + if (chan_flags[chan] & STA_WAIT) + continue; + + /* Special for write segment mark command */ + if (cmd[chan] & CHN_SEGMENT) { + /* Send one char */ + assembly[chan] = SM_MEM; + bcnt[chan] = 2; + caddr[chan] = limit[chan]+1; + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_FULL|DEV_WEOR; + cmd[chan] = CHN_ALPHA|CHN_SEGMENT; + chan_info[chan] |= CHAN_NORDW; + continue; + } + /* Give device new word if we have one */ + if (caddr[chan] <= limit[chan]) { + /* Check if got EOR */ + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= ~(STA_WAIT | DEV_REOR + | STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, + "chan %d EOR> %o\n", chan, cmd[chan] & 070); + continue; + } + + if (caddr[chan] < MEMSIZE) + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > %011llx\n", chan, + assembly[chan]); + bcnt[chan] = 10; + chan_flags[chan] |= DEV_FULL; + + /* Check if last transfer before final */ + if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) { + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT; + } else + caddr[chan]++; + + /* Update channel status word */ + if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) { + int adr = 100 + (chan * 10) + (chan_info[chan]&0xf); + upd_idx(&M[adr], caddr[chan]); + bin_dec(&M[adr], location[chan], 0, 4); + } + + /* Check for record mark */ + if ((cmd[chan] & CHN_RECORD) && + (assembly[chan] & SMASK) == ASIGN && + (assembly[chan] & 0xFF) == RM_CHAR) { + if (cmd[chan] & CHN_LAST) { + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT|STA_WAIT; + } else + chan_fetch(chan); + } + + continue; /* Don't start next command until data taken */ + } + + /* Wait for device to recognize EOR */ + if (chan_flags[chan] & DEV_WEOR) + continue; + + /* Get here if passed limit */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d > WC0 %o stat=%x\n", + chan, cmd[chan] & 070, chan_flags[chan]); + + if (cmd[chan] & CHN_LAST) { + chan_flags[chan] |= DEV_DISCO | DEV_WEOR | STA_TWAIT; + chan_flags[chan] &= ~(STA_START | STA_ACTIVE); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, + "chan %d > DISCO\n", chan); + } else + chan_fetch(chan); + } + break; + case CHAN_7907: + /* If channel is disconnecting, just hold on */ + if (chan_flags[chan] & DEV_DISCO) + continue; + + /* If no select, stop channel */ + if ((chan_flags[chan] & DEV_SEL) == 0 + && (chan_flags[chan] & STA_TWAIT)) { + t_uint64 temp; + + temp = 2; + if (chan_info[chan] & CHAN_TWE) + temp = 1; + else if (chan_flags[chan] & SNS_UEND) + temp = 5; + else if ((chan_info[chan] & CHAN_SEOR) == 0 && op[chan] == 1) + temp = 4; + else if (caddr[chan] <= limit[chan] && + (op[chan] == 1 || op[chan] == 3)) + temp = 3; + temp <<= 36; + chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2); + temp |= (chan_irq[chan])?MSIGN:PSIGN; + chan_flags[chan] &= ~(SNS_UEND|CTL_CNTL|CTL_SNS|CTL_READ| + CTL_WRITE|CTL_PREAD|CTL_PWRITE); + upd_idx(&temp, caddr[chan]); + bin_dec(&temp, location[chan], 0, 4); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap: %012llx\n", + chan, temp); + M[(chan - 4) + 300] = temp; + if ((chan_info[chan] & CHAN_PRIO) || ((temp >> 36) & 0xf) != 2) + pri_latchs[8] |= 1<<(4-chan); + chan_flags[chan] &= + ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT); + chan_info[chan] &= ~CHAN_PRIO; + continue; + } + + /* Check if device raised attention */ + if ((chan_flags[chan] & (STA_ACTIVE|DEV_SEL|STA_TWAIT)) == 0 && + (chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2))) { + t_uint64 temp; + + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Attn Trap\n", + chan); + temp = 2; + if (chan_flags[chan] & SNS_UEND) + temp = 5; + temp <<= 36; + temp |= MSIGN; + chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2); + chan_flags[chan] &= ~(SNS_ATTN1|SNS_ATTN2|SNS_UEND); + upd_idx(&temp, caddr[chan]); + bin_dec(&temp, location[chan], 0, 4); + M[(chan - 4) + 300] = temp; + pri_latchs[9] |= 1 << (4 - chan); + continue; + } + + /* Nothing more to do if not active. */ + if (chan_flags[chan] & STA_ACTIVE) { + t_uint64 temp; + + /* Execute the next command */ + switch (op[chan]) { + case 6: /* Tranfer in channel */ + /* I am not sure if this is correct, but it passes + diagnostics */ + location[chan] = limit[chan]; + break; + case 0: /* Write status */ + temp = PSIGN|(2LL << 36); + upd_idx(&temp, caddr[chan]); + bin_dec(&temp, location[chan], 0, 4); + M[caddr[chan]] = temp; + break; + case 1: /* Read */ + /* Check if in other mode */ + if (chan_flags[chan] & (CTL_CNTL)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d read busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(CTL_CNTL|DEV_REOR|DEV_WRITE); + } else + continue; + } + /* Check if last command not finished */ + if (chan_flags[chan] & (CTL_SNS|CTL_WRITE)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d read busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= ~(CTL_SNS|CTL_WRITE); + } + continue; + } + + /* If not already in read mode, set read mode */ + if ((chan_flags[chan] & CTL_READ) == 0) { + chan_flags[chan] |= CTL_READ; + chan_flags[chan] &= ~(DEV_FULL|DEV_WRITE|DEV_REOR); + chan_info[chan] &= ~CHAN_SEOR; + bcnt[chan] = 10; + assembly[chan] = 0; + continue; + } + + /* Has device given us a word */ + if (chan_flags[chan] & DEV_FULL) { + /* Check if record mark */ + if ((cmd[chan] & CHN_RECORD) && + (assembly[chan] & SMASK) == ASIGN && + (assembly[chan] & 0xFF) == RM_CHAR) { + break; + } + + + /* Check if ready to transfer something */ + if (caddr[chan] <= limit[chan]) { + M[caddr[chan]] = assembly[chan]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > %012llx\n", + chan, assembly[chan]); + caddr[chan]++; + bcnt[chan] = 10; + assembly[chan] = 0; + chan_flags[chan] &= ~DEV_FULL; + /* Check if last word transfered */ + if (caddr[chan] > limit[chan]) + break; + } + + /* Check if we still have a select signal */ + if ((chan_flags[chan] & DEV_SEL) == 0) { + chan_info[chan] |= CHAN_TWE; + chan_flags[chan] &= + ~(CTL_WRITE|CTL_END|STA_ACTIVE); + break; + } + + /* Device gave us a EOR, get next word */ + if (chan_flags[chan] & DEV_REOR) { + chan_info[chan] |= CHAN_SEOR; + chan_flags[chan] &= ~DEV_REOR; + break; + } + continue; + } + + /* Abort if we get control end */ + if (chan_flags[chan] & CTL_END) { + /* Disconnect channel if select still active */ + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= (DEV_DISCO); + } + if (chan_flags[chan] & DEV_REOR) + chan_info[chan] |= CHAN_SEOR; + chan_flags[chan] &= + ~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END); + break; + } + continue; + + case 3: /* Write */ + /* Check if in other mode */ + if (chan_flags[chan] & (CTL_CNTL)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d write busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(CTL_CNTL|DEV_REOR|DEV_WRITE); + } else + continue; + } + /* Check if last command not finished */ + if (chan_flags[chan] & (CTL_SNS|CTL_READ)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d write busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= ~(CTL_READ|CTL_SNS); + } + continue; + } + + /* If not in write, flag as write */ + if ((chan_flags[chan] & CTL_WRITE) == 0) { + chan_flags[chan] |= CTL_WRITE|DEV_WRITE; + } + + /* Command finished?*/ + if (chan_flags[chan] & CTL_END) { + /* Disconnect channel if select still active */ + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= (DEV_DISCO); + } + chan_flags[chan] &= + ~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END); + break; + } + + /* Check if we still have a select signal */ + if ((chan_flags[chan] & DEV_SEL) == 0 && + caddr[chan] < limit[chan]) { + chan_info[chan] |= CHAN_TWE; + chan_flags[chan] &= ~(CTL_WRITE|CTL_END|STA_ACTIVE); + break; + } + + /* Check if device needs data */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + /* Got EOR? */ + if (chan_flags[chan] & DEV_REOR) { + if (caddr[chan] > limit[chan]) { + chan_info[chan] |= CHAN_SEOR; + } + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= ~DEV_REOR; + break; + } + + + /* Check if ready to transfer something */ + if (caddr[chan] <= limit[chan]) { + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > %012llx\n", + chan, assembly[chan]); + caddr[chan]++; + bcnt[chan] = 10; + chan_flags[chan] |= DEV_FULL; + /* Check if record mark */ + if ((cmd[chan] & CHN_RECORD) && + (assembly[chan] & SMASK) == ASIGN && + (assembly[chan] & 0xFF) == RM_CHAR) { + chan_flags[chan] |= DEV_WEOR; + break; + } + continue; + } + chan_info[chan] |= CHAN_SEOR; + break; + } + continue; + + case 5: /* Sense */ + /* Check if in other mode */ + if (chan_flags[chan] & (CTL_CNTL)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d sense busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(CTL_CNTL|DEV_REOR|DEV_WRITE); + } else + continue; + } + if (chan_flags[chan] & CTL_SNS) { + /* Check if we still have a select signal */ + if ((chan_flags[chan] & DEV_SEL) == 0) { + chan_info[chan] |= CHAN_TWE; + chan_flags[chan] &= ~(CTL_SNS|STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT; + break; + } + + /* If device ended, quit transfer */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + if (chan_flags[chan] & CTL_END) { + /* Disconnect channel if select still active */ + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= (DEV_DISCO); + } + if (chan_flags[chan] & DEV_REOR) { + chan_info[chan] |= CHAN_SEOR; + chan_flags[chan] &= ~DEV_REOR; + } + chan_flags[chan] &= ~(CTL_SNS); + break; + } + + /* Check if last word transfered */ + if (caddr[chan] > limit[chan]) { + if (chan_flags[chan] & SNS_UEND) { + chan_flags[chan] |= + (DEV_DISCO | DEV_WEOR); + chan_flags[chan] &= ~(DEV_SEL); + } else { + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= ~DEV_REOR; + chan_info[chan] |= CHAN_SEOR; + } + } + chan_flags[chan] &= ~(CTL_SNS); + break; + } + } else { + /* Device has given us a dataword */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data < %012llx\n", + chan, assembly[chan]); + M[caddr[chan]] = assembly[chan]; + assembly[chan] = 0; + bcnt[chan] = 10; + chan_flags[chan] &= ~DEV_FULL; + if (caddr[chan] >= limit[chan]) + break; + caddr[chan]++; + } + /* Handle EOR on sense */ + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= ~(CTL_SNS|DEV_REOR); + chan_info[chan] |= CHAN_SEOR; + break; + } + continue; + } + + /* Check if in other mode */ + if (chan_flags[chan] & (CTL_CNTL|CTL_READ|CTL_WRITE)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d sense busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT; + } + chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE); + continue; + } + + /* Start sense command */ + chan_flags[chan] |= CTL_SNS; + chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL); + switch(chan_issue_cmd(chan,0,chan_test(chan, CTL_SEL))){ + case SCPE_IOERR: + case SCPE_NODEV: + chan_info[chan] |= CHAN_TWE; + chan_flags[chan] &= ~STA_ACTIVE; + case SCPE_BUSY: /* Device not ready yet, wait */ + chan_flags[chan] &= ~(CTL_SNS); + continue; + case SCPE_OK: + /* Device will be waiting for command */ + break; + } + /* Get channel ready to transfer */ + chan_flags[chan] &= ~DEV_WRITE; + chan_flags[chan] |= DEV_SEL; + continue; + + case 4: /* Transfer command */ + if (chan_flags[chan] & CTL_CNTL) + goto xfer; + if (chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d control busy %04x\n", + chan, chan_flags[chan]); + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT; + } + chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE); + continue; + } + chan_flags[chan] |= CTL_CNTL; + /* Get channel ready to transfer */ + chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL); + + switch(chan_issue_cmd(chan,0,chan_stat(chan,CTL_SEL))){ + case SCPE_IOERR: + case SCPE_NODEV: + chan_info[chan] |= CHAN_TWE; + chan_flags[chan] &= + ~(CTL_SNS|CTL_CNTL|STA_ACTIVE); + continue; + case SCPE_BUSY: + /* Device not ready yet, wait */ + continue; + case SCPE_OK: + /* Device will be waiting for command */ + break; + } + + chan_flags[chan] |= DEV_WRITE; + xfer: + /* Check if comand tranfer done */ + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(DEV_WRITE|DEV_REOR|DEV_FULL|CTL_CNTL); + chan_info[chan] |= CHAN_SEOR; + break; + } + + /* Wait for device to grab the command */ + if (chan_flags[chan] & DEV_FULL) + continue; + + /* Check if device ready for next command word */ + if ((chan_flags[chan] & (DEV_WRITE | DEV_FULL)) == + DEV_WRITE) { + if (caddr[chan] <= limit[chan]) { + assembly[chan] = M[caddr[chan]]; + chan_flags[chan] |= DEV_FULL; + bcnt[chan] = 10; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CMD, &chan_dev, + "chan %d cmd > %012llx\n", + chan, assembly[chan]); + if (caddr[chan] < limit[chan]) + caddr[chan]++; + continue; + } + } + break; + + case 2: /* Read Backwards */ + /* Unknown commands at moment */ + case 7: + case 8: + case 9: + chan_info[chan] |= CHAN_TWE; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= + ~(STA_ACTIVE|CTL_WRITE|CTL_READ|CTL_CNTL|CTL_SNS); + break; + + } + + /* If last all done */ + if (cmd[chan] & CHN_LAST || chan_flags[chan] & (SNS_UEND) + || chan_info[chan] & CHAN_TWE) { + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT; + } else + chan_fetch(chan); + } + continue; + } + } +} + +void +chan_fetch(int chan) +{ + uint32 loc = location[chan]; + t_uint64 temp; + + sim_interval--; + chan_info[chan] &= ~CHAN_START; + if (loc < MEMSIZE) + temp = M[loc]; + else { + cmd[chan] |= CHN_LAST; + return; + } + location[chan] = (loc + 1); + if ((temp & SMASK) == MSIGN) + cmd[chan] |= CHN_LAST; + get_rdw(temp, &caddr[chan], &limit[chan]); + op[chan] = (temp >> 36) & 0xf; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CHAN, &chan_dev, + "chan %d fetch adr=%05d op=%d cmd=%03o caddr=%05d limit=%05d\n", + chan, loc, op[chan], cmd[chan], caddr[chan], limit[chan]); +} + +void chan_set_attn_a(int chan) { + pri_latchs[0] |= 0x002; +} + +void chan_set_attn_b(int chan) { + pri_latchs[0] |= 0x004; +} + +void chan_set_attn_inq(int chan) { + if (chan == CHAN_UREC) + pri_latchs[0] |= 0x080; + else + pri_latchs[0] |= 0x100; +} + +void chan_clear_attn_inq(int chan) { + if (chan == CHAN_UREC) + pri_latchs[0] &= ~0x080; + else + pri_latchs[0] &= ~0x100; +} + + +/* Issue a command to a channel */ +int +chan_cmd(uint16 dev, uint16 dcmd, uint16 addr) +{ + uint32 chan; + int prio; + t_stat r; + + /* Find device on given channel and give it the command */ + chan = (dev >> 8) & 0xf; + /* If no channel device, quick exit */ + if (chan_unit[chan].flags & UNIT_DIS) + return SCPE_IOERR; + /* Unit is busy doing something, wait */ + if (chan_flags[chan] & (DEV_SEL|DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE)) + return SCPE_BUSY; + /* Ok, try and find the unit */ + prio = (dev & 0x1000)? 1: 0; + dev &= 0xff; + location[chan] = addr; + cmd[chan] = dcmd & 0xff; + dcmd >>= 8; + chan_info[chan] = (dev & 0xf) | (chan << 4); + chan_info[chan] |= CHAN_START; + /* Special check for console */ + if (chan == 0 && dev == 0) + chan_info[chan] |= CHAN_OUTDEV; + + /* Check for octal translation */ + if (chan == 1 && dev & 020) + chan_info[chan] |= CHAN_OCTAL; + + /* Enable priority */ + if (prio) + chan_info[chan] |= CHAN_PRIO; + assembly[chan] = 0; + bcnt[chan] = 10; + + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7907) { + chan_flags[chan] |= STA_ACTIVE; + if (dev & 1) + chan_flags[chan] |= CTL_SEL; + else + chan_flags[chan] &= ~CTL_SEL; + chan_fetch(chan); + return SCPE_OK; + } + r = chan_issue_cmd(chan, dcmd, dev); + if (r != SCPE_OK) { + /* No device, kill active */ + chan_flags[chan] &= ~(STA_ACTIVE); + } else { + extern uint32 IC; + /* If transfering data, activate channel */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= STA_ACTIVE; + + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CMD, &chan_dev, + "chan %d cmd=%o IC=%05d addr=%05d\n\r", chan, dcmd, IC, addr); + } + return r; +} + +/* + * Write a word to the assembly register. + */ +int +chan_write(int chan, t_uint64 * data, int flags) +{ + return TIME_ERROR; +} + +/* + * Read next word from assembly register. + */ +int +chan_read(int chan, t_uint64 * data, int flags) +{ + return TIME_ERROR; +} + +/* + * Write a char to the assembly register. + */ +int +chan_write_char(int chan, uint8 * data, int flags) +{ + uint8 ch = *data; + /* Check if last data still not taken */ + if (chan_flags[chan] & DEV_FULL) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] &= ~DEV_WEOR; + chan_flags[chan] |= DEV_REOR; + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; /* We had error */ + } + if (chan == 0) { + chan_flags[chan] |= DEV_DISCO; + } + return TIME_ERROR; + } + +#if 0 /* Check segment working before removing */ + /* Check for segment mark */ + if (flags & DEV_REOR && (chan_info[chan] & CHAN_FIRST) == 0) { + if (ch == SM_CHAR) + chan_info[chan] |= CHAN_SEOS; + assembly[chan] |= ASIGN|(((t_uint64)bcd_mem[ch]) << 32); + if (cmd[chan] & CHN_ALPHA) + chan_flags[chan] |= DEV_FULL|DEV_REOR; + else + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~DEV_WRITE; + chan_proc(); + return END_RECORD; + } + + /* Clear first char in record flag */ + chan_info[chan] |= CHAN_FIRST; +#endif + + if (ch == DELTA_CHAR && (cmd[chan] & CHN_ALPHA) == 0) { + if (bcnt[chan] == 10) + cmd[chan] ^= CHN_NUM_MODE; + else + chan_info[chan] |= CHAN_TWE; + } else { + if (chan_flags[chan] & CTL_SNS) { + /* Set sign based on attention signal */ + if (bcnt[chan] == 10) { + if (chan_irq[chan] & (SNS_ATTN1 >> (chan_info[chan] & 1))) + assembly[chan] = PSIGN; + else + assembly[chan] = MSIGN; + chan_irq[chan] &= ~(SNS_ATTN1 >> (chan_info[chan] & 1)); + } + /* Store character */ + ch &= 0x17; + if (ch & 0x04) + ch ^= 0x24; /* Bit move */ + ch |= 0x44; + bcnt[chan]-=2; + assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan]); + } else if (cmd[chan] & CHN_NUM_MODE) { + ch &= 0xf; + if (ch == 0 || ch > 10) + chan_info[chan] |= CHAN_TWE; + else if (ch == 10) + ch = 0; + bcnt[chan]--; + assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan])|PSIGN; + /* Check for sign digit */ + switch(*data & 060) { + case 0: /* Normal digit */ + case 020: /* error */ + if (bcnt[chan] == 0) + chan_info[chan] |= CHAN_TWE; + break; + case 040: + if (bcnt[chan] > 5) + chan_info[chan] |= CHAN_TWE; + assembly[chan] &= DMASK; + /* Put number in right location */ + while(bcnt[chan] != 0) { + bcnt[chan]--; + assembly[chan] >>= 4; + } + assembly[chan] |= MSIGN; + break; + case 060: + if (bcnt[chan] > 5) + chan_info[chan] |= CHAN_TWE; + assembly[chan] &= DMASK; + /* Put number in right location */ + while(bcnt[chan] != 0) { + bcnt[chan]--; + assembly[chan] >>= 4; + } + assembly[chan] |= PSIGN; + break; + } + } else { + if (chan_info[chan] & CHAN_OCTAL) + ch = ((ch & 070) << 1) | (ch & 07); + else + ch = bcd_mem[ch]; + if (ch == 0xFF) { + chan_info[chan] |= CHAN_TWE; + ch = 0; + } + bcnt[chan] -= 2; + assembly[chan] |= ((t_uint64)ch) << (8 * (bcnt[chan] / 2)); + assembly[chan] |= (chan_info[chan] & CHAN_OCTAL)?PSIGN:ASIGN; + } + } + + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_FULL|DEV_REOR; + chan_flags[chan] &= ~DEV_WRITE; + if (bcnt[chan] != 0 && ((cmd[chan] & (CHN_NUM_MODE)) == 0 || + (cmd[chan] & (CHN_ALPHA)) != 0)) + chan_info[chan] |= CHAN_SCLR; + chan_info[chan] |= CHAN_SEOR; + chan_proc(); + return END_RECORD; + } else if (bcnt[chan] == 0) { + chan_flags[chan] |= DEV_FULL; + chan_flags[chan] &= ~DEV_WRITE; + chan_proc(); + } + + /* If Writing end of record, abort */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR); + return END_RECORD; + } + + return DATA_OK; +} + +/* + * Read next char from assembly register. + */ +int +chan_read_char(int chan, uint8 * data, int flags) +{ + uint8 ch; + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/); + return END_RECORD; + } + + chan_proc(); + + /* Check if he write out last data */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT | DEV_WRITE); + chan_flags[chan] |= DEV_REOR|DEV_DISCO; + return END_RECORD; + } + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + } + if (chan == 0) { + chan_flags[chan] |= DEV_DISCO; + } + return TIME_ERROR; + } + + /* Send control words differently */ + if (chan_flags[chan] & CTL_CNTL) { + if ((assembly[chan] & SMASK) == ASIGN) { + bcnt[chan] -= 2; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; + *data = mem_bcd[ch]; + } else { + bcnt[chan]--; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; + if (ch == 0) + ch = 10; + *data = ch; + } + goto done; + } + + /* Handle console type out */ + if (chan_info[chan] & CHAN_OUTDEV) { + if (bcnt[chan] == 10 && (cmd[chan] & CHN_NUM_MODE) == 0) { + switch(assembly[chan] & SMASK) { + case ASIGN: break; + case PSIGN: *data = 060; + cmd[chan] |= CHN_NUM_MODE; + return SCPE_OK; + case MSIGN: *data = 040; + cmd[chan] |= CHN_NUM_MODE; + return SCPE_OK; + } + } + if (cmd[chan] & CHN_NUM_MODE) { + bcnt[chan]--; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; + if (ch == 0) + ch = 10; + if (bcnt[chan] == 0) + cmd[chan] &= ~CHN_NUM_MODE; + *data = ch; + } else { + bcnt[chan] -= 2; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; + *data = mem_bcd[ch]; + } + goto done; + } + + /* Check for mode change */ + if (bcnt[chan] == 10 && (cmd[chan] & (CHN_ALPHA)) == 0) { + if (((assembly[chan] & SMASK) == ASIGN && + (cmd[chan] & CHN_NUM_MODE) == CHN_NUM_MODE) + ||((assembly[chan] & SMASK) != ASIGN && + (cmd[chan] & CHN_NUM_MODE) == CHN_ALPHA_MODE)) { + *data = DELTA_CHAR; + cmd[chan] ^= CHN_NUM_MODE; + return DATA_OK; + } + /* Handle zero compression here */ + if ((cmd[chan] & (CHN_NUM_MODE|CHN_COMPRESS)) == + (CHN_NUM_MODE|CHN_COMPRESS)) { + while((assembly[chan] >> (4 * bcnt[chan]) & 0xf) == 0 && + bcnt[chan] < 5) + bcnt[chan]--; + } + } + + /* If in number mode, dump as number */ + if (cmd[chan] & CHN_NUM_MODE) { + bcnt[chan]--; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; + if (ch == 0) + ch = 10; + if (bcnt[chan] == 0) + ch |= ((assembly[chan] & SMASK) == MSIGN)? 040: 060; + *data = ch; + } else { + bcnt[chan] -= 2; + ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; + *data = mem_bcd[ch]; + } + +done: + if (bcnt[chan] == 0) { + chan_flags[chan] &= ~DEV_FULL; + bcnt[chan] = 10; + } + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL); + chan_flags[chan] |= DEV_REOR; + chan_proc(); + } else + chan_flags[chan] |= DEV_WRITE; + return DATA_OK; +} + +void +chan_set_load_mode(int chan) +{ + cmd[chan] &= ~CHN_ALPHA; + cmd[chan] |= CHN_NUM_MODE; +} + +void +chan9_set_error(int chan, uint32 mask) +{ + if (chan_flags[chan] & mask) + return; + chan_flags[chan] |= mask; +} + +t_stat +chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", chan_description(dptr)); + fprintf (st, "The 7070 supports up to 8 channels. Channel models include\n\n"); + fprintf (st, " 7604 standard multiplexor channel\n"); + fprintf (st, " 7907 advanced capabilities channel\n\n"); + fprintf (st, "Channels are fixed on the 7070.\n\n"); + fprintf (st, "Channel * is for unit record devices.\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +chan_description(DEVICE *dptr) +{ + return "IBM 7070 channel controller"; +} + + diff --git a/I7000/i7070_cpu.c b/I7000/i7070_cpu.c new file mode 100644 index 00000000..654de0c9 --- /dev/null +++ b/I7000/i7070_cpu.c @@ -0,0 +1,3001 @@ +/* i7070_cpu.c: IBM 7070 CPU simulator + + 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. + + cpu IBM 7070 central processor + + The IBM 7070 was introduced in June 1960, as a replacement to the IBM 650. + It had core memory up to 10,000 10 digit words. + The 7072 was introduced November 1962 and the 7074 on November 1961. + The 7074 is a faster version of the 7070 with the addition of memory up + to 40,000 10 digit words. The first 100 memory locations can be used as + index registers. Most memory reference instructions allow for a field + of digits to be selected to operate on and not modify the rest. + + The 7070 is a decimal machine with each word consisting of 10 digits + plus a sign. The sign can be plus, minus or alpha. Alpha data is stored + 5 characters to a word (2 digits per character). + + The system state for the IBM 7070 is: + + AC1<0:10> AC1 register + AC2<0:10> AC2 register + AC3<0:10> AC3 register + IC<0:5> program counter + + The 7070 had one basic instuction format. + + 01 23 45 6789 + and 01 are opcode. Alpha is not allowed. + 23 specify an index register from memory location 01 to 99. + or if extended addressing is enabled 10-99. 01-09 specify + high order digit of address. + 45 encode either a field, or operands depending on instruction. + 6789 are address in memory. If index is specified they are + added to fields [1]2345 of memory addressed by field 23. + + Accumulators may be accessed 9991/2/3 or 99991/2/3. + + Signs are stored as 9 for plus. + 6 for minus. + 3 for alpha. + + Options supported are Timer, Extended addressing and Floating point. +*/ + +#include "i7070_defs.h" +#include "sim_timer.h" + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) +#define UNIT_MSIZE (7 << UNIT_V_MSIZE) +#define UNIT_V_CPUMODEL (UNIT_V_UF + 4) +#define UNIT_MODEL (0x01 << UNIT_V_CPUMODEL) +#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_CPUMODEL) & 0x01) +#define MODEL(x) (x << UNIT_V_CPUMODEL) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define OPTION_FLOAT (1 << (UNIT_V_CPUMODEL + 1)) +#define OPTION_TIMER (1 << (UNIT_V_CPUMODEL + 2)) +#define OPTION_EXTEND (1 << (UNIT_V_CPUMODEL + 3)) + +#define TMR_RTC 1 + +#define HIST_NOEA 0x10000000 +#define HIST_NOAFT 0x20000000 +#define HIST_NOBEF 0x40000000 +#define HIST_PC 0x10000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +struct InstHistory +{ + t_int64 op; + uint32 ic; + uint32 ea; + t_int64 before; + t_int64 after; +}; + +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + +void mem_init(void); + +/* Interval timer option */ +t_stat rtc_srv(UNIT * uptr); +t_stat rtc_reset(DEVICE * dptr); + +t_uint64 M[MAXMEMSIZE] = { PSIGN }; /* memory */ +t_uint64 AC[4]; /* registers */ +t_uint64 inds; /* Error indicators */ +t_uint64 diaglatch; /* Diagnostic latches */ +uint16 timer; /* Timer register */ +uint32 IC; /* program counter */ +uint16 timer_clock; /* Timer clock */ +uint8 SW = 0; /* Sense switch */ +uint8 emode; /* Extended address mode */ +uint16 pri_latchs[10]; /* Priority latchs */ +uint32 pri_mask = 0xFFFFFF; /* Priority masks */ +uint8 pri_enb = 1; /* Enable priority procs */ +uint8 lpr_chan9[NUM_CHAN]; /* Line printer on channel 9 */ +int cycle_time = 20; /* Cycle time of 12us */ + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ +void (*sim_vm_init) (void) = &mem_init; + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + { UDATA(&rtc_srv, OPTION_FLOAT|MEMAMOUNT(1)|MODEL(0x0), 10000), 10 }; + +REG cpu_reg[] = { + {DRDATA(IC, IC, 20), REG_FIT}, + {HRDATA(AC1, AC[1], 44), REG_VMIO|REG_FIT}, + {HRDATA(AC2, AC[2], 44), REG_VMIO|REG_FIT}, + {HRDATA(AC3, AC[3], 44), REG_VMIO|REG_FIT}, + {HRDATA(IND, inds, 44), REG_VMIO|REG_FIT}, + {ORDATA(SW, SW, 4), REG_FIT}, + {FLDATA(SW1, SW, 0), REG_FIT}, + {FLDATA(SW2, SW, 1), REG_FIT}, + {FLDATA(SW3, SW, 2), REG_FIT}, + {FLDATA(SW4, SW, 3), REG_FIT}, + {NULL} +}; + +MTAB cpu_mod[] = { + {UNIT_MODEL, MODEL(0x0), "7070", "7070", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(0x1), "7074", "7074", NULL, NULL, NULL}, + {UNIT_MSIZE, MEMAMOUNT(0), "5K", "5K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(1), "10K", "10K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(2), "15K", "15K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(3), "20K", "20K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(4), "25K", "25K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(5), "30K", "30K", &cpu_set_size}, + {OPTION_FLOAT, 0, NULL, "NOFLOAT", NULL, NULL, NULL}, + {OPTION_FLOAT, OPTION_FLOAT, "FLOAT", "FLOAT", NULL, NULL, NULL}, + {OPTION_EXTEND, 0, NULL, "NOEXTEND", NULL, NULL, NULL}, + {OPTION_EXTEND, OPTION_EXTEND, "EXTEND", "EXTEND", NULL, NULL, NULL}, + {OPTION_TIMER, 0, NULL, "NOCLOCK", NULL, NULL, NULL}, + {OPTION_TIMER, OPTION_TIMER, "CLOCK", "CLOCK", NULL, NULL, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 18, 1, 16, 44, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description +}; + +uint32 dscale[4][16] = { + {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 0,0,0,0,0,0}, + {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 0,0,0,0,0,0}, + {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 0,0,0,0,0,0}, + {0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, + 0,0,0,0,0,0} +}; + +t_uint64 fdmask[11] = { + 0x0000000000LL, + 0xF000000000LL, 0xFF00000000LL, 0xFFF0000000LL, 0xFFFF000000LL, + 0xFFFFF00000LL, 0xFFFFFF0000LL, 0xFFFFFFF000LL, 0xFFFFFFFF00LL, + 0xFFFFFFFFF0LL, 0xFFFFFFFFFFLL +}; + +t_uint64 rdmask[11] = { + 0xFFFFFFFFFFLL, 0x0FFFFFFFFFLL, 0x00FFFFFFFFLL, 0x000FFFFFFFLL, + 0x0000FFFFFFLL, 0x00000FFFFFLL, 0x000000FFFFLL, 0x0000000FFFLL, + 0x00000000FFLL, 0x000000000FLL, 0x0LL +}; + +t_uint64 ldmask[11] = { + 0x0LL, 0xFLL, 0xFFLL, 0xFFFLL, 0xFFFFLL, 0xFFFFFLL, 0xFFFFFFLL, 0xFFFFFFFLL, + 0xFFFFFFFFLL, 0xFFFFFFFFFLL, 0xFFFFFFFFFFLL +}; + +t_uint64 dmask[11] = { + 0x0LL, 0xFLL, 0xF0LL, 0xF00LL, 0xF000LL, 0xF0000LL, + 0xF00000LL, 0xF000000LL, 0xF0000000LL, 0xF00000000LL, 0xF000000000LL +}; + +#define gdigit(w, d) (((w) >> ((d) * 4)) & 0xF) +#define sdigit(d, v) ((((t_uint64)v) & 0xFLL) << ((d) * 4)) +#define mdigit(d) (0xFLL << ((d) * 4)) + +t_uint64 ReadP(uint32 addr) { + sim_interval -= (CPU_MODEL == 0x0)? 2: 1; + if (emode) { + if (addr > MAXMEMSIZE) { + switch(addr) { + case 99991: return AC[1]; + case 99992: return AC[2]; + case 99993: return AC[3]; + default: return 0LL; + } + } + } else { + if (addr >= 9990) { + switch(addr) { + case 9991: return AC[1]; + case 9992: return AC[2]; + case 9993: return AC[3]; + default: return 0LL; + } + } + } + if (addr < MEMSIZE && addr < MAXMEMSIZE) + return M[addr]; + return 0LL; +} + +void WriteP(uint32 addr, t_uint64 value) { + sim_interval -= (CPU_MODEL == 0x0)? 2: 1; + if (emode) { + if (addr > MAXMEMSIZE) { + switch(addr) { + case 99991: AC[1] = value; return; + case 99992: AC[2] = value; return; + case 99993: AC[3] = value; return; + } + } + } else { + if (addr >= 9990) { + switch(addr) { + case 9991: AC[1] = value; return; + case 9992: AC[2] = value; return; + case 9993: AC[3] = value; return; + default: return; + } + } + } + if (addr < MEMSIZE && addr < MAXMEMSIZE) + M[addr] = value; +} + +t_stat +sim_instr(void) +{ + t_stat reason; + t_uint64 temp; + t_uint64 MBR; + uint16 opcode = 0; + uint32 MA = 0; + uint32 utmp; /* Unsigned temp */ + int tmp; /* Signed temp */ + uint8 f = 0; + uint8 stopnext; + uint8 IX = 0; + uint8 f1 = 0; + uint8 f2 = 0; + uint8 op2 = 0; + int iowait = 0; /* Wait for IO to start */ + int chwait = 0; /* Wait for channel to be inactive */ + int sign; + int instr_count = 0; /* Number of instructions to execute */ + + if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); + } + + reason = 0; + + iowait = 0; + stopnext = 0; + while (reason == 0) { /* loop until halted */ + +/* If doing fast I/O don't sit in idle loop */ + if (iowait == 0 && chwait == 0 && stopnext) + return SCPE_STEP; + + if (chwait != 0 && chan_active(chwait)) + sim_interval = 0; + else + chwait = 0; + + if (iowait) + sim_interval = 0; + + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + if (reason == SCPE_STEP && iowait) + stopnext = 1; + else + break; /* process */ + } + } + + /* Only check for break points during actual fetch */ + if (iowait == 0 && chwait == 0 + && sim_brk_summ && sim_brk_test(IC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + + /* Don't do interupt if waiting on IO or channel */ + if (pri_enb && iowait == 0 && chwait == 0) { + /* Check if we have to process one */ + if ((tmp = scan_irq()) != 0) { + /* Save instruction counter */ + if (CPU_MODEL == 0x1) /* On 7074 location 97 modified */ + MBR = M[97]; + else + MBR = 0; /* On 7070/2 location cleared */ + upd_idx(&MBR, IC); + MBR &= DMASK; + MBR |= PSIGN; + M[97] = MBR; + /* Save indicators */ + M[100] = inds; + inds = PSIGN; + pri_enb = 0; + IC = tmp; + sim_debug(DEBUG_TRAP, &cpu_dev, "IRQ= %d %d\n\r", IC, tmp); + } + } + + /* Main instruction fetch/decode loop */ + if (chwait == 0) { + /* Split out current instruction */ + sim_interval -= 24; /* count down */ + /* If waiting for IO don't bump IC or create history */ + if (iowait) + /* Don't do a fetch if waiting on I/O to be ready */ + iowait = 0; + else { + MBR = ReadP(IC); + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = (IC) | HIST_PC; + hst[hst_p].op = MBR; + hst[hst_p].after = 0; + } + IC++; + MA = MBR & 0xf; MBR >>= 4; + MA += dscale[0][MBR & 0xf]; MBR >>= 4; + MA += dscale[1][MBR & 0xf]; MBR >>= 4; + MA += dscale[2][MBR & 0xf]; MBR >>= 4; + f2 = MBR & 0xf; MBR >>= 4; + f1 = MBR & 0xf; MBR >>= 4; + IX = MBR & 0xf; MBR >>= 4; + IX += dscale[0][MBR & 0xf]; MBR >>= 4; + opcode = MBR & 0xff; + op2 = (opcode >> 4) & 0xf; + if ((MBR & (SMASK >> 32)) == (MSIGN >> 32)) + opcode |= 0x100; + /* Check if extended addressing mode */ + if (emode && IX < 10) { + MA += dscale[3][IX]; + IX = 0; + } + /* Handle indexing */ + if (IX > 0) { + sim_interval -= (CPU_MODEL == 0x0)? 10: 1; + MBR = M[IX]; + utmp = dec_bin_idx(MBR); + if ((MBR & SMASK) == MSIGN) { /* Change sign */ + if (MA < utmp) { + if (emode) + MA = 100000 - MA - utmp; + else + MA = 10000 - MA - utmp; + } else + MA -= utmp; + } else if ((MBR & SMASK) == PSIGN) { + MA += utmp; + if (emode) { + if (MA > 100000) + MA -= 100000; + } else { + if (MA > 10000) + MA -= 10000; + } + } else { + reason = STOP_INDEX; + break; + } + } + IX = f2 + dscale[0][f1]; + /* Fetch data */ + MBR = ReadP(MA); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = MA; + hst[hst_p].before = MBR; + } + } + + switch(opcode) { + /* Zero add absolute */ + case OP_ZAA: + MBR &= DMASK; + MBR |= PSIGN; + goto set_ac; + /* Zero sub absolute */ + case OP_ZSA: + MBR &= DMASK; + MBR |= MSIGN; + goto set_ac; + /* Load AC negitive from memory */ + case OP_ZS1: case OP_ZS2: case OP_ZS3: + if ((MBR & SMASK) != ASIGN) + MBR ^= SMASK; + /* Zero add, load AC from memory */ + case OP_ZA1: case OP_ZA2: case OP_ZA3: + set_ac: + MBR = (MBR & SMASK)|((rdmask[f1] & MBR) >> ((9 - f2) * 4)); + + AC[op2] = MBR; + sim_interval -= (CPU_MODEL == 0x0)? (f2 - f1)/3: 1; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[op2]; + } + break; + /* AC - memory */ + case OP_S1: case OP_S2: case OP_S3: + sign = (MBR & SMASK) != MSIGN; + if ((MBR & SMASK) == ASIGN) + sign |= 8; + goto add; + /* AC - |memory| */ + case OP_SA: + /* AC + |memory| */ + sign = 1; + goto add; + case OP_AA: + sign = 0; + goto add; + /* AC + memory */ + case OP_A1: case OP_A2: case OP_A3: + /* Get field. */ + sign = (MBR & SMASK) == MSIGN; + if ((MBR & SMASK) == ASIGN) + sign |= 8; + add: + if ((AC[op2] & SMASK) == ASIGN) + sign |= 8; + MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + sim_interval -= (CPU_MODEL == 0x0)? 4*(f2 - f1)/3: 1; + if ((AC[op2] & SMASK) == MSIGN) + sign ^= 3; + AC[op2] &= DMASK; + if (sign & 1) { + int cy; + cy = dec_add(&AC[op2], NINES - MBR); + cy |= dec_add(&AC[op2], 1LL); + if (cy == 0) { + AC[op2] = NINES - AC[op2]; + dec_add(&AC[op2], 1LL); + sim_interval -= (CPU_MODEL == 0x0)? + 12*(f2 - f1)/3: 1; + sign ^= 3; + } + } else { + if(dec_add(&AC[op2], MBR)) + inds |= 1LL << (4 * (3 - op2)); /* Set overflow */ + } + AC[op2] &= DMASK; + if (sign & 8) + AC[op2] |= ASIGN; + else if (sign & 2) + AC[op2] |= MSIGN; + else + AC[op2] |= PSIGN; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[op2]; + } + break; + /* |memory| + AC */ + case OP_AAS1: case OP_AAS2: case OP_AAS3: + sign = ((MBR & SMASK) == MSIGN)?2:0; + goto addstore; + /* mem = AC - |memory| */ + case OP_SS1: case OP_SS2: case OP_SS3: + sign = ((MBR & SMASK) != MSIGN)?1:2; + goto addstore; + /* memory + AC */ + case OP_AS1: case OP_AS2: case OP_AS3: + /* Get field. */ + sign = ((MBR & SMASK) == MSIGN)?3:0; + addstore: + if ((MBR & SMASK) == ASIGN) + sign |= 4; + switch (AC[op2] & SMASK) { + case ASIGN: sign |= 8; break; + case MSIGN: sign ^= 1; break; + } + temp = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + sim_interval -= (CPU_MODEL == 0x0)? 4*(f2 - f1)/3: 1; + if (sign & 1) { + int cy; + cy = dec_add(&temp, NINES - (AC[op2] & DMASK)); + cy |= dec_add(&temp, 1LL); + if (cy == 0) { + temp = NINES - temp; + dec_add(&temp, 1LL); + sim_interval -= (CPU_MODEL == 0x0)? + 12*(f2 - f1)/3: 1; + sign ^= 3; + } + } else { + if(dec_add(&temp, DMASK&AC[op2])) + inds |= 1LL << (4 * (3 - op2)); /* Set overflow */ + } + + /* Put results back */ + utmp = (MBR & SMASK) >> 40;/* Original sign for compare */ + MBR &= DMASK; + MBR &= ~(rdmask[f1] & fdmask[f2+1]); /* Clear digits. */ + /* Check overflow */ + if (temp & ~ldmask[f2-f1+1]) { + if (inds & 0x0F00000000LL) { + inds &= 0xFF0FFFFFFFFLL; /* Set field */ + inds |= 0x00900000000LL; + } else { + reason = STOP_FIELD; + } + } + temp &= ldmask[f2-f1+1]; + /* Compute final sign */ + if ((opcode & 0x10f) == (OP_AAS1 & 0x10f)) { + sign = utmp; + } else if (sign & 0xc) { + sign = ASIGN >> 40; + } else if (sign & 2) { + sign = MSIGN >> 40; + } else { + sign = PSIGN >> 40; + } + /* Check for sign change, and other data in word */ + if (MBR != 0 && ((sign != utmp && f1 != 0 && f2 != 9) || + (sign == (ASIGN >> 40) && utmp != (ASIGN >> 40)))) { + if (inds & 0xF000000000LL) { + inds &= 0xF0FFFFFFFFFLL; /* Set field */ + inds |= 0x09000000000LL; + } else { + reason = STOP_SIGN; + } + } + /* Restore results and sign */ + MBR |= DMASK & temp << ((9 - f2) * 4); + MBR |= ((t_uint64)sign) << 40; + WriteP(MA, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + + /* AC :: memory */ + case OP_C1: case OP_C2: case OP_C3: + temp = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + temp |= MBR & SMASK; /* Copy sign */ + inds &= 0xFFFFF000FFFLL; + switch(dec_cmp(temp, AC[op2])) { + case -1: inds |= 0x0000001000LL; break; + case 1: inds |= 0x0000100000LL; break; + case 0: inds |= 0x0000010000LL; break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[op2]; + } + break; + + /* Clear Memory, store */ + case OP_ZST1: case OP_ZST2: case OP_ZST3: + MBR = SMASK & AC[op2]; /* Same sign as AC */ + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF; + } + /* Store digit */ + case OP_STD1: case OP_STD2: case OP_STD3: + /* Store AC */ + case OP_ST1: case OP_ST2: case OP_ST3: + /* Check for sign change, and other data in word */ + if ((opcode & 0x10f) == (OP_ST1 & 0x10f)) { + if ((AC[op2] & SMASK) != (MBR & SMASK) && + (MBR & DMASK) != 0) { + if (inds & 0xF000000000LL) { + inds &= 0xF0FFFFFFFFFLL; /* Set field */ + inds |= 0x09000000000LL; + } else { + reason = STOP_SIGN; + break; + } + } + /* Restore set */ + MBR &= DMASK; + MBR |= SMASK & AC[op2]; + } + MBR &= ~(rdmask[f1] & fdmask[f2+1]); /* Clear digits. */ + temp = AC[op2] & DMASK; + /* Check overflow */ + if (temp & ~ldmask[f2-f1+1]) { + if (inds & 0x0F00000000LL) { + inds &= 0xFF0FFFFFFFFLL; /* Set field */ + inds |= 0x00900000000LL; + } else { + reason = STOP_FIELD; + break; + } + } + temp &= ldmask[f2-f1+1]; + /* Restore results */ + MBR |= DMASK & (temp << ((9 - f2) * 4)); + WriteP(MA, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + + /* Branch AC zero */ + case OP_BZ1: case OP_BZ2: case OP_BZ3: + if ((AC[op2] & DMASK) == 0) + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Branch AC overflow */ + case OP_BV1: case OP_BV2: case OP_BV3: + if ((inds >> (4 * (3 - op2))) & 0x1) { + IC = MA; + inds &= ~(0xFLL << (4 * (3 - op2)));/* clear overflow */ + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Branch AC minus */ + case OP_BM1: case OP_BM2: case OP_BM3: + if ((AC[op2] & SMASK) == MSIGN) + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Multiply */ + case OP_M: + /* Multiplicand in AC[3] */ + AC[1] = AC[2] = 0; + sign = (MBR & SMASK) >> 40; + MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + sign = (((AC[3] & SMASK) >> 40) != sign) ? 6 : 9; + /* Multiply MBR * AC[3] result to AC[1],AC[2] */ + for(tmp = 36; tmp >= 0; tmp-=4) { + int digit = (AC[3] >> tmp) & 0xf; + AC[1] <<= 4; + AC[1] &= DMASK; + AC[1] |= (AC[2] >> 36) & 0xf; + AC[2] <<= 4; + AC[2] &= DMASK; + if (digit != 0) { + sim_interval -= (CPU_MODEL == 0x0)? + 12*digit: digit; + /* Form product */ + mul_step(&AC[2], MBR, digit); + digit = (AC[2] >> 40) & 0xff; + if (digit != 0) + dec_add_noov(&AC[1], digit); + AC[2] &= DMASK; + } else + sim_interval -= (CPU_MODEL == 0x0)? 2: 0; + } + /* Set sign */ + AC[1] |= ((t_uint64)sign) << 40; + AC[2] |= ((t_uint64)sign) << 40; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Divide */ + case OP_D: + /* dividend AC[1],AC[2] */ + /* divisor in MBR */ + sign = (MBR & SMASK) >> 40; + AC[3] = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + if (AC[3] == 0) { + AC[3] |= ((t_uint64)sign) << 40; + reason = STOP_DIV; + break; + } + utmp = (AC[1] & SMASK) >> 40; + /* Compute sign */ + if (utmp != 3 && utmp != sign) + utmp ^= 0xf; + /* If any are alpha, result is alpha */ + if (sign == 3 || utmp == 3 || utmp == 0xc) + utmp = 3; + /* Divide AC1,AC2 by AC3 */ + AC[1] &= DMASK; + AC[2] &= DMASK; + dec_comp(&AC[3]); + for(tmp = 10; tmp != 0; --tmp) + div_step(AC[3]); + dec_comp(&AC[3]); + /* Fix signs */ + AC[1] |= ((t_uint64)utmp) << 40; + AC[2] |= ((t_uint64)sign) << 40; + AC[3] &= DMASK; + AC[3] |= ((t_uint64)sign) << 40; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Shift control */ + case OP_SC: + op2 = (MA / 1000) % 10; + if (op2 == 0 || op2 > 3) + break; + utmp = MA % 100; + if (utmp > 10) + break; + temp = AC[op2] & DMASK; + switch ((MA / 100) % 10) { + case 0: + temp >>= utmp * 4; + break; + case 1: + if (utmp != 0) { + temp >>= (utmp - 1) * 4; + f1 = temp & 0xF; + temp >>= 4; + if (f1 > 5) + dec_add(&temp, 1LL); + } + break; + case 2: + temp <<= utmp * 4; + break; + case 3: + utmp = 0; + if (temp != 0) { + while((temp & dmask[10]) == 0) { + utmp++; + temp <<= 4; + } + } + if (IX) { + MBR = ReadP(IX); + MBR &= ~IMASK; + MBR &= DMASK; + MBR |= PSIGN; + if (utmp > 10) /* BCD adjust */ + utmp += 6; + MBR |= ((t_uint64)utmp) << 16; + WriteP(IX, MBR); + } + break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = AC[op2]; + } + AC[op2] &= SMASK; + AC[op2] |= DMASK & temp; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[op2]; + } + break; + /* Coupled shift control */ + case OP_CSC: + utmp = MA % 100; /* Number of shifts */ + if (utmp > 20) + break; + op2 = (MA / 100) % 10; /* Operand */ + f2 += f1 * 10; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = AC[1]; + } + switch (op2) { + default: + case 0: /* Shift right coupled */ + sign = (AC[1] >> 40) & 0xf; + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + f1 = AC[1] & 0xf; + AC[1] >>= 4; + AC[2] |= ((t_uint64)f1) << 40; + AC[2] >>= 4; + } + break; + case 1: /* Shift right coupled and round */ + sign = (AC[1] >> 40) & 0xf; + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + f1 = AC[1] & 0xf; + AC[1] >>= 4; + AC[2] |= ((t_uint64)f1) << 40; + f1 = AC[2] & 0xf; + AC[2] >>= 4; + } + if (f1 > 5) + if (dec_add(&AC[2], 1LL)) + if (dec_add(&AC[1], 1LL)) + inds |= 1LL << 8; /* Set overflow */ + break; + case 2: /* Shift left coupled */ + sign = (AC[2] >> 40) & 0xf; + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + AC[1] <<= 4; + AC[1] &= DMASK; + AC[1] |= (AC[2] >> 36) & 0xf; + AC[2] <<= 4; + AC[2] &= DMASK; + } + break; + case 3: /* Shift left and count coupled */ + sign = (AC[2] >> 40) & 0xf; + AC[1] &= DMASK; + AC[2] &= DMASK; + utmp = 0; + if (AC[1] != 0 || AC[2] != 0) { + while((AC[1] & dmask[10]) == 0) { + AC[1] <<= 4; + AC[1] &= DMASK; + AC[1] |= (AC[2] >> 36) & 0xf; + AC[2] <<= 4; + AC[2] &= DMASK; + utmp++; + } + } + if (IX) { + MBR = ReadP(IX); + MBR &= ~IMASK; + MBR &= DMASK; + if (utmp > 10) /* BCD adjust */ + utmp += 6; + MBR |= ((t_uint64)utmp) << 16; + WriteP(IX, MBR); + } + break; + case 4: /* Shift right from point ac1 */ + sign = (AC[1] >> 40) & 0xf; + tmp = (MA / 1000) % 10; /* Split point */ + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + f1 = AC[1] & 0xf; + AC[1] = (AC[1] & fdmask[tmp]) | + ((AC[1] & rdmask[tmp]) >> 4); + AC[2] |= ((t_uint64)f1) << 40; + AC[2] >>= 4; + } + break; + case 5: /* Shift left from point ac1 */ + sign = (AC[2] >> 40) & 0xf; + tmp = (MA / 1000) % 10; /* Split point */ + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + AC[1] = (AC[1] & rdmask[tmp]) | + ((AC[1] & fdmask[tmp]) << 4); + } + break; + case 6: /* Shift right from point ac2 */ + sign = (AC[2] >> 40) & 0xf; + tmp = (MA / 1000) % 10; /* Split point */ + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + AC[2] = (AC[2] & fdmask[tmp]) | + ((AC[2] & rdmask[tmp]) >> 4); + } + break; + case 7: /* Shift left from point ac2 */ + sign = (AC[2] >> 40) & 0xf; + tmp = (MA / 1000) % 10; /* Split point */ + AC[1] &= DMASK; + AC[2] &= DMASK; + for(;utmp != 0; utmp--) { + AC[1] <<= 4; + AC[1] &= DMASK; + AC[1] |= ((AC[2] & fdmask[tmp]) >> 36) & 0xf; + AC[2] = (AC[2] & rdmask[tmp]) | + ((AC[2] & fdmask[tmp]) << 4); + } + break; + } + AC[1] |= ((t_uint64)sign) << 40; + AC[2] |= ((t_uint64)sign) << 40; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + /* AC1 : |Memory | */ + case OP_CA: + MBR = (rdmask[f1] & MBR) >> ((9 - f2) * 4); + inds &= 0xFFFFF000FFFLL; + switch(dec_cmp(MBR & DMASK, AC[1]&DMASK)) { + case -1: inds |= 0x0000001000LL; break; + case 1: inds |= 0x0000100000LL; break; + case 0: inds |= 0x0000010000LL; break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + /* Compare digit */ + case OP_CD: + inds &= 0xFFFFF000FFFLL; + MBR >>= ((9 - f2) * 4); + MBR &= 0xF; + if (MBR > f1) + inds |= 0x0000100000LL; + else if (MBR < f1) + inds |= 0x0000001000LL; + else + inds |= 0x0000010000LL; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + /* Branch load index */ + case OP_BLX: + upd_idx(&M[IX], IC); + /* Branch */ + case OP_B: + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Branch low */ + case OP_BL: + if (inds & 0x0000001000LL) + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Branch high */ + case OP_BH: + if (inds & 0x0000100000LL) + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Branch equal */ + case OP_BE: + if (inds & 0x0000010000LL) + IC = MA; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Extended memory on 7074 */ + case OP_EXMEM: + if (CPU_MODEL == 0x1 && cpu_unit.flags & OPTION_EXTEND) { + switch(f1) { + case 0: + if (emode) + IC = MA; + break; + case 1: + emode = 0; + break; + case 2: + emode = 1; + break; + } + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + case OP_HB: + IC = MA; + /* fall through */ + case OP_HP: + reason = STOP_HALT; + /* fall through */ + case OP_NOP: + /* fall through */ + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + /* Floating point option */ + /* Floating point: sEEMMMMMMM EE+50*/ + case OP_FD: /* AC2 <- 0, AC1,AC2 <- AC1/M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + AC[2] = 0; + case OP_FDD: /* AC1,2 <- AC1/M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + + /* Extract signs */ + sign = (MBR & SMASK) == MSIGN; + if ((AC[1] & SMASK) == MSIGN) + sign ^= 3; + + /* Extract exponents */ + f1 = (((AC[1] >> 36) & 0xf) * 10) + + ((AC[1] >> 32) & 0xf); + tmp = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf); + tmp = 51 + (f1 - tmp); + /* Clear exponents */ + MBR &= FMASK; + if (MBR == 0) { + reason = STOP_DIV; + break; + } + AC[1] &= FMASK; + AC[2] &= FMASK; + AC[1] = NINES - AC[1]; /* One's compliment AC[1]&AC[2] */ + AC[2] = FNINES - AC[2]; + dec_add(&AC[2], 1LL); + if (AC[2] & EMASK) { /* Propagate carry */ + dec_add(&AC[1], AC[2] >> 32); + AC[2] &= FMASK; + } + /* Check if overflow on first try */ + temp = AC[1]; + if (dec_add(&temp, MBR)) { + /* Yes, shift right before starting */ + AC[1] <<= 4; + AC[1] &= DMASK; + AC[2] <<= 4; + AC[1] |= (AC[2] >> 32) & 0xf; + AC[2] &= FMASK; + tmp--; + } else + f1++; + utmp = 8; + do { + int cnt = 0; + /* Repeated subtract until overflow */ + while(1) { + temp = AC[1]; + if (dec_add(&temp, MBR)) + break; + cnt++; + AC[1] = temp; /* Restore AC if not less */ + if (cnt > 9) { /* Catch divide check */ + reason = STOP_DIV; + goto done; + } + } + /* Shift right coupled */ + AC[1] <<= 4; + AC[1] &= DMASK; + AC[2] <<= 4; + AC[1] |= (AC[2] >> 32) & 0xf; + AC[2] &= FMASK; + AC[2] |= cnt; /* Put in count */ + } while (--utmp != 0); + + /* Restore remainder to correct value */ + dec_comp(&AC[1]); + + /* Now exchange AC1 & AC2 to get results in right place */ + temp = AC[1]; + AC[1] = AC[2]; + AC[2] = temp; + + /* Check overflow */ + if (tmp > 99) { + inds |= 0x0001000000LL; /* Set overflow */ + tmp = 0; + } + /* Save exponents */ + bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */ + if (f1 < 8) + AC[2] = 0; + else { + f1 -= 8; + AC[2] >>= 4; + if ((AC[2] & EMASK) != 0) { + if (f1-- != 0) + AC[2] >>= 4; + else + AC[2] = f1 = 0; + } + bin_dec(&AC[2], f1, 8, 2); /* Restore exponent */ + } + + /* Fix signs */ + if (sign & 1) { + AC[1] |= MSIGN; + } else { + AC[1] |= PSIGN; + } + + if (sign & 3) { + AC[2] |= MSIGN; + } else { + AC[2] |= PSIGN; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Floating point multiply */ + case OP_FM: /* AC1,2 <- AC1 * M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + + /* Extract signs */ + sign = (MBR & SMASK) == MSIGN; + sign ^= (AC[1] & SMASK) == MSIGN; + + /* Extract exponents */ + utmp = (((AC[1] >> 36) & 0xf) * 10) + + ((AC[1] >> 32) & 0xf); + f1 = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf); + utmp += f1; + utmp -= 50; + /* Clear exponents */ + MBR &= FMASK; + temp = AC[1]; + AC[1] = 0; + AC[2] = 0; + /* Multiplicand in AC[3] */ + /* Multiply MBR * AC[1] result to AC[1],AC[2] */ + for(tmp = 28; tmp >= 0; tmp-=4) { + int digit = (temp >> tmp) & 0xf; + AC[1] <<= 4; + AC[1] &= DMASK; + AC[1] |= (AC[2] >> 28) & 0xf; + AC[2] <<= 4; + AC[2] &= FMASK; + if (digit != 0) { + sim_interval -= (CPU_MODEL == 0x0)? 12*digit:digit; + /* Form product */ + mul_step(&AC[2], MBR, digit); + digit = (AC[2] >> 32) & 0xff; + if (digit != 0) + dec_add(&AC[1], digit); + AC[2] &= FMASK; + } else + sim_interval -= (CPU_MODEL == 0x0)? 2: 0; + } + /* Check if needs to be normalized */ + if ((AC[1] & NMASK) == 0) { + AC[1] <<= 4; + AC[1] |= (AC[2] >> 28) & 0xf; + AC[2] <<= 4; + AC[2] &= FMASK; + utmp--; + } + /* Check overflow */ + if (utmp > 99) { + inds |= 0x0001000000LL; /* Set overflow */ + utmp = 0; + } + /* Save exponents */ + bin_dec(&AC[1], utmp, 8, 2); /* Restore exponent */ + if (utmp < 8) + AC[2] = 0; + else + bin_dec(&AC[2], utmp-8, 8, 2); /* Restore exponent */ + /* Set signs */ + if (sign) { + AC[1] |= MSIGN; + AC[2] |= MSIGN; + } else { + AC[1] |= PSIGN; + AC[2] |= PSIGN; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Floating point round */ + case OP_FR: /* AC1 <- +.5, AC2 <- 0 */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = AC[1]; + } + if (((AC[2] >> 28) & 0xf) > 5) { + temp = AC[1] & SMASK; + AC[1] &= DMASK; + if (dec_add(&AC[1], 1LL)) { + inds |= 0x0001000000LL; + AC[1] = 0; + } + AC[1] |= temp; /* Restore sign */ + } + AC[2] = PSIGN; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Floating subtract absolute */ + case OP_FSA: /* AC2 <- 0, AC1,2 <- AC1,2-|M|, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + sign = 1; + AC[2] = 0x65000000000LL; + goto float_add; + /* Floating subtract */ + case OP_FS: /* AC2 <- 0, AC1,2 <- AC1,2 - M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + sign = (MBR & SMASK) != MSIGN; + AC[2] = 0x65000000000LL; + goto float_add; + /* Floating add absolute */ + case OP_FAA: /* AC2 <- 0, AC1,2 <- AC1,2+|M|, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + sign = 0; + goto float_add; + /* Floating add */ + case OP_FA: /* AC2 <- 0, AC1,2 <- AC1,2+M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + AC[2] = 0x65000000000LL; + /* Floating add double */ + case OP_FAD: /* AC1,2 <- AC1,2 + M, norm */ + /* Floating add double, suppress norm */ + case OP_FADS: /* AC1,2 <- AC1,2 + M */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((AC[1] & SMASK) == ASIGN || (MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + goto done; + } + sign = (MBR & SMASK) == MSIGN; + float_add: + /* Extract sign */ + if ((AC[1] & SMASK) == MSIGN) + sign ^= 3; + + /* Compare exponents */ + utmp = (((AC[1] >> 36) & 0xf) * 10) + + ((AC[1] >> 32) & 0xf); + f1 = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf); + tmp = ((signed int)utmp) - ((signed int)f1); + /* Clear exponents and sign */ + MBR &= FMASK; + AC[1] &= FMASK; + AC[2] &= FMASK; + temp = 0; + if (tmp > 0) { /* AC Bigger */ + /* Shift MBR */ + if (tmp > 16) + goto float_norm; + while(tmp > 0) { + temp |= ((t_uint64)(MBR & 0xf)) << 32; + MBR >>= 4; + temp >>= 4; + tmp--; + } + } else if (tmp < 0) { /* AC Smaller */ + utmp = f1; + /* Shift AC */ + if (tmp > -16) { + while(tmp < 0) { + AC[2] |= ((t_uint64)(AC[1] & 0xf)) << 32; + AC[1] >>= 4; + AC[2] >>= 4; + tmp++; + } + } else { + AC[1] = MBR; + AC[2] = 0; + goto float_norm; + } + } + /* Do actual addition now */ + if (sign & 1) { + /* Do compliment add */ + dec_add(&AC[2], FNINES - temp); + dec_add(&AC[2], 1LL); + dec_add(&AC[1], FNINES - MBR); + /* Carry between halfs */ + if (AC[2] & EMASK) { /* Carry out */ + dec_add(&AC[1], (AC[2] >> 32) & 0xff); + AC[2] &= FMASK; + } + + if ((AC[1] & EMASK) == 0) { + AC[2] = FNINES - (AC[2] & FMASK); + AC[1] = FNINES - (AC[1] & FMASK); + dec_add(&AC[2], 1LL); + if (AC[2] & EMASK) { /* Carry out */ + dec_add(&AC[1], (AC[2] >> 32) & 0xff); + AC[2] &= FMASK; + } + sim_interval -= (CPU_MODEL == 0x0)? + 12*(f2 - f1)/3: 1; + sign ^= 3; + } + AC[1] &= FMASK; + } else { + /* Add low then high */ + dec_add(&AC[2], temp); + dec_add(&AC[1], MBR); + if (AC[2] & EMASK) /* Carry out */ + dec_add(&AC[1], (AC[2] >> 32) & 0xf); + } + if (AC[1] & EMASK) { + AC[2] |= ((t_uint64)(AC[1] & 0xf)) << 32; + AC[1] >>= 4; + AC[2] >>= 4; + utmp++; + } + float_norm: + /* Normalize result */ + tmp = utmp; + if (opcode != OP_FADS && AC[1] != 0 && AC[2] != 0) { + while((AC[1] & NMASK) == 0) { + AC[1] <<= 4; + AC[1] |= (AC[2] >> 28) & 0xf; + AC[2] <<= 4; + AC[2] &= FMASK; + tmp--; + } + } + /* Set exponent if error */ + if (AC[1] == 0 && AC[2] == 0) + tmp = 50; + if (tmp < 0) { + inds |= 0x0010000000LL; /* Set underflow */ + tmp = 0; + } + if (tmp > 99) { + inds |= 0x0001000000LL; /* Set overflow */ + tmp = 0; + } + /* Restore exponents */ + bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */ + if (tmp < 8) + AC[2] = 0; + else + bin_dec(&AC[2], tmp-8, 8, 2); /* Restore exponent */ + /* Set signs */ + if (sign & 2) { + AC[1] |= MSIGN; + AC[2] |= MSIGN; + } else { + AC[1] |= PSIGN; + AC[2] |= PSIGN; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + /* Floating zero and add */ + case OP_FZA: /* AC2 <- 0, AC1 <- M, norm */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + /* Check sign */ + if ((MBR & SMASK) == ASIGN) { + reason = STOP_SIGN; + break; + } + AC[2] = 0; + /* Extract exponent */ + tmp = (((MBR >> 36) & 0xf) * 10) + ((MBR >> 32) & 0xf); + AC[1] = MBR & FMASK; + if (AC[1] != 0) { + while((AC[1] & NMASK) == 0) { + tmp--; + AC[1] <<= 4; + } + } else + tmp = 50; + if (tmp < 0) { + inds |= 0x0010000000LL; /* Set underflow */ + tmp = 0; + } + bin_dec(&AC[1], tmp, 8, 2); /* Restore exponent */ + AC[1] |= MBR & SMASK; + AC[2] |= MBR & SMASK; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = AC[1]; + } + break; + + case OP_FBU: /* Branch floating overflow */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((inds & 0x000F000000LL) != 0) + IC = MA; + inds &= 0xFFFF0FFFFFFLL; /* Clear flag */ + break; + + case OP_FBV: /* Branch floating underflow */ + if ((cpu_unit.flags & OPTION_FLOAT) == 0) { + reason = STOP_UUO; + break; + } + if ((inds & 0x00F0000000LL) != 0) + IC = MA; + inds &= 0xFFF0FFFFFFFLL; /* Clear flag */ + break; + + /* Load with interchange */ + case OP_XLIN: + MBR = (MBR & (SMASK|OMASK)) | ((MBR >> 16) & AMASK) | + ((MBR << 16) & IMASK); + /* Load index */ + case OP_XL: + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + /* Unload index */ + case OP_XU: + MBR = ReadP(IX); + WriteP(MA, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Index zero subtract */ + case OP_XZS: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + upd_idx(&MBR, MA); + MBR &= DMASK; + MBR |= MSIGN; + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Index zero add */ + case OP_XZA: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + upd_idx(&MBR, MA); + MBR &= DMASK; + MBR |= PSIGN; + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Index subtract */ + case OP_XS: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + temp = dec_bin_idx(MBR); + sign = ((MBR & SMASK)>> 40); + MBR &= DMASK; + switch(sign) { + case 0x6: /* + - tc b add */ + temp += MA; + break; + default: + case 0x3: /* + a add res a */ + case 0x9: /* + + add */ + temp = ~temp + MA + 1; + if (temp & 0x8000) { + temp = ~temp + 1; + if (sign == 0x9) sign = 0x6; + } + break; + } + MBR |= ((t_uint64)sign) << 40; + upd_idx(&MBR, (uint32)temp); + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Index add */ + case OP_XA: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + temp = 0; + upd_idx(&temp, MA); + sign = ((MBR & SMASK)>> 40); + MBR &= DMASK; + switch(sign) { + default: + case 0x3: /* + a add res a */ + case 0x9: /* + + add */ + dec_add(&temp, MBR & ((emode)?IMASK2:IMASK)); + break; + case 0x6: /* + - tc b add */ + if (temp == 0) + break; + dec_comp(&temp); + dec_add(&temp, MBR & ((emode)?IMASK2:IMASK)); + if (temp & ((emode)?XMASK2:XMASK)) { + dec_comp(&temp); + sign = 0x9; + } + break; + } + MBR |= ((t_uint64)sign) << 40; + MBR &= (emode)?~IMASK2:~IMASK; + MBR |= temp; + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Index set non-indexing */ + case OP_XSN: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + bin_dec(&MBR, MA, 0, 4); + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + /* Branch if index word index = 0 */ + case OP_BXN: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + hst[hst_p].ic |= HIST_NOAFT; + } + if ((MBR & IMASK) != 0) + IC = MA; + break; + /* Branch decrement index */ + case OP_BDX: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + temp = MBR & IMASK; + dec_add(&temp, (emode)?0x999990000LL:0x99990000LL); + MBR &= (emode)?~IMASK2:~IMASK; + MBR |= temp & (emode)?IMASK2:IMASK; + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + goto checkix; + /* Branch increment index */ + case OP_BIX: + temp = MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + dec_add(&temp, 0x10000LL); + MBR &= (emode)?~IMASK2:~IMASK; + MBR |= temp & ((emode)?IMASK2:IMASK); + WriteP(IX, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + goto checkix; + /* Branch compared index */ + case OP_BCX: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + hst[hst_p].ic |= HIST_NOAFT; + } + checkix: + temp = (MBR & IMASK) >> 16; + MBR &= AMASK; + dec_comp(&temp); + if(dec_add(&temp, MBR)) + IC = MA; + break; + /* Branch if index word index minus */ + case OP_BXM: + MBR = ReadP(IX); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + hst[hst_p].ic |= HIST_NOAFT; + } + if ((MBR & SMASK) == MSIGN) + IC = MA; + break; + /* Field over flow control */ + case OP_BFLD: + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + switch (f2) { + case 0: + if ((inds & 0x0F00000000LL) == 0x0900000000LL) { + IC = MA; + inds ^= 0x0F00000000LL; + } + break; + case 1: + if ((inds & 0x0F00000000LL) != 0x0500000000LL) { + inds &= 0xFF0FFFFFFFFLL; + inds |= 0x00500000000LL; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOEA; + } + break; + case 2: + if ((inds & 0x0F00000000LL) == 0x0900000000LL) + reason = STOP_SIGN; + else + inds &= 0xFF0FFFFFFFFLL; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOEA; + } + break; + } + break; + /* Sign change control */ + case OP_CS: + switch (f2) { + case 0: + inds &= 0xFFFFF000FFFLL; + utmp = ((MBR >> 40) & 0xf); + if (utmp > f1) + inds |= 0x00000100000LL; + else if (utmp < f1) + inds |= 0x00000001000LL; + else + inds |= 0x00000010000LL; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + case 1: + MBR &= DMASK; + MBR |= ((t_uint64)f1) << 40; + WriteP(MA, MBR); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + break; + case 2: + if ((inds & 0xF000000000LL) == 0) { + inds |= 0x5000000000LL; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOEA|HIST_NOBEF|HIST_NOAFT; + } + break; + case 3: + if ((inds & 0xF000000000LL) == 0x9000000000LL) + reason = STOP_SIGN; + else + inds &= 0xF0FFFFFFFFFLL; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOEA|HIST_NOBEF|HIST_NOAFT; + } + break; + case 4: + if ((inds & 0xF000000000LL) == 0x9000000000LL) { + IC = MA; + inds ^= 0xF000000000LL; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + } + break; + + /* Record scatter */ + case OP_RS: + /* Generate source address */ + temp = M[IX]; + utmp = dec_bin_idx(temp); + do { + uint32 dst, limit; + MBR = ReadP(MA++); /* Grab next RDW */ + get_rdw(MBR, &dst, &limit); + while(dst <= limit) { + WriteP(dst++, ReadP(utmp++)); + if (utmp > MEMSIZE) + utmp = 0; + } + } while ((MBR & SMASK) != MSIGN); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + /* Record gather */ + case OP_RG: + /* Generate destination address */ + temp = M[IX]; + utmp = dec_bin_idx(temp); + do { + uint32 src, limit; + MBR = ReadP(MA++); /* Grab next RDW */ + get_rdw(MBR, &src, &limit); + while(src <= limit) { + WriteP(utmp++, ReadP(src++)); + if (utmp > MEMSIZE) + utmp = 0; + } + } while ((MBR & SMASK) != MSIGN); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + /* Edit alpha to numeric */ + case OP_ENB: + case OP_ENS: + case OP_ENA: + /* Generate source address */ + temp = M[IX]; + utmp = dec_bin_idx(temp); + do { + uint32 dst, limit; + MBR = ReadP(MA++); /* Grab next RDW */ + get_rdw(MBR, &dst, &limit); + while(dst <= limit) { + t_uint64 buffer; + f1 = (opcode == OP_ENB)? 0: 1; + temp = ReadP(utmp++); + if (utmp > MEMSIZE) + utmp = 0; + buffer = 0x9090909090LL|ASIGN; + for(tmp = 9; tmp > 4; tmp--) { + if (f1 == 0) { + if ((temp & dmask[tmp+1]) == 0) + buffer &= ~(0xFFLL << ((tmp-4)*8)); + else + f1 = 1; + } + buffer |= (temp & dmask[tmp+1]) << ((tmp-4)*8); + } + WriteP(dst++, buffer); + if (opcode == OP_ENS) { + switch(temp & SMASK) { + case ASIGN: + buffer = 0x9090909090LL|ASIGN; + break; + case PSIGN: + buffer = 0x9090909060LL|ASIGN; + break; + case MSIGN: + buffer = 0x9090909070LL|ASIGN; + break; + } + } else + buffer = 0x9090909090LL|ASIGN; + for(; tmp >= 0; tmp--) { + if (f1 == 0) { + if ((temp & dmask[tmp+1]) == 0) + buffer &= ~(0xFFLL << (tmp * 8)); + else + f1 = 1; + } + buffer |= (temp & dmask[tmp+1]) << (tmp*8); + } + WriteP(dst++, buffer); + } + } while ((MBR & SMASK) != MSIGN); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + case OP_EAN: + /* Generate destination address */ + temp = M[IX]; + utmp = dec_bin_idx(temp); + do { + uint32 src, limit; + MBR = ReadP(MA++); /* Grab next RDW */ + get_rdw(MBR, &src, &limit); + while(src <= limit) { + t_uint64 buffer = 0; + temp = ReadP(src++); + for(tmp = 8, f1 = 16; tmp >= 0; tmp-= 2) { + buffer |= (temp & dmask[tmp+1]) << f1; + f1 += 4; + } + temp = ReadP(src++); + for(tmp = 8, f1 = 16; tmp >= 0; tmp-=2) { + buffer |= (temp & dmask[tmp+1]) >> f1; + f1 -= 4; + } + if ((temp & 0xF0LL) == 0x70LL) + buffer |= MSIGN; + else + buffer |= PSIGN; + WriteP(utmp++, buffer); + if (utmp > MEMSIZE) + utmp = 0; + } + } while ((MBR & SMASK) != MSIGN); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + case OP_LL: + case OP_LE: + case OP_LEH: + /* Generate increment */ + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + temp = M[98]; + utmp = dec_bin_idx(temp); + do { + uint32 src, limit; + MBR = ReadP(MA++); /* Grab next RDW */ + get_rdw(MBR, &src, &limit); + while(src <= limit) { + temp = ReadP(src); + temp = (rdmask[f1] & temp) >> ((9 - f2) * 4); + switch(dec_cmp(temp, AC[3])) { + case -1: + if (opcode == OP_LL) { + f = 1; + AC[3] = temp; + bin_dec(&M[98], src, 4,(emode)?5:4); + M[98] &= DMASK; + M[98] |= PSIGN; + } + break; + case 1: + if (opcode != OP_LEH) + break; + case 0: + if (opcode != OP_LL) { + f = 1; + bin_dec(&M[98], src, 4,(emode)?5:4); + M[98] &= DMASK; + M[98] |= PSIGN; + goto found; + } + break; + } + src += utmp; + } + } while ((MBR & SMASK) != MSIGN); + found: + if (f) + IC++; + break; + /* Electronic switch */ + case OP_BSW21: + case OP_BSW22: + case OP_BSW23: + /* Read flag */ + opcode -= OP_BSW21; + opcode += 101; + MBR = ReadP(opcode); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].before = MBR; + } + /* Compute mask */ + f2 = 4 * (9 - f2); + temp = 0xFLL << f2; + switch(f1) { + case 0: /* Switch test */ + case 3: /* test and turn on */ + case 4: /* test and turn off */ + if (MBR & temp) + IC = MA; + } + if (f1 != 0) + MBR &= ~(temp); + if (f1 & 1) /* Set flag */ + MBR |= 0x1LL << f2; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + WriteP(opcode, MBR); + break; + + /* Priority control */ + case OP_PC: + utmp = 0; + temp = 0xF; + /* Convert digits into bits */ + for(f1 = 0; f1 < 10; f1++) { + if ((MBR & temp) != 0) + utmp |= 1; + temp <<= 4; + utmp <<= 1; + } + utmp >>= 1; + if (f2 == 1) { + utmp <<= 10; + pri_mask &= 0x3FF; + pri_mask |= utmp; + } else if (f2 == 0) { + pri_mask &= 0xFFC00; + pri_mask |= utmp; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + break; + + /* Test priority latches */ + case OP_PRTST: + if (f1 == 0 && f2 == 0) { + for(tmp = 0; tmp < 10; tmp++) { + if (pri_latchs[tmp] != 0) { + IC = MA; + break; + } + } + } else { + if ((pri_latchs[f1] >> f2) & 1) + IC = MA; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOBEF|HIST_NOAFT; + } + break; + + /* Set priority latches */ + case OP_PRION: + switch(f1) { + case 0: + case 8: + case 9: + case 1: + case 2: + case 3: + case 4: + pri_latchs[f1] |= 1 << f2; + break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOEA; + } + break; + + /* Clear priority latches */ + case OP_PRIOF: + pri_latchs[f1] &= ~(1 << f2); + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF|HIST_NOEA; + } + break; + + case OP_PR: + if (pri_enb == 1) /* Not in priority mode, nop */ + break; + if ((tmp = scan_irq()) != 0) { + if (MA != 97) { + /* Save instruction counter */ + MBR = ReadP(97); + upd_idx(&MBR, MA); + MBR &= DMASK; + MBR |= PSIGN; + WriteP(97, MBR); + pri_enb = 0; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].after = MBR; + } + } else if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + inds = PSIGN; + IC = tmp; + } else { + if (MA == 97) + IC = dec_bin_idx(MBR); + else + IC = MA; + inds = ReadP(100); + pri_enb = 1; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT; + } + } + break; + /* Check switch */ + case OP_BSWITCH: + if (f1 == 0 || f1 > 4) { + reason = STOP_UUO; + break; + } + switch(f2) { + case 0: + if ((SW >> (f1 - 1)) & 1) + IC = MA; + break; + case 1: + case 2: + if (chan_active(((f2 - 1) * 4) + f1)) + IC = MA; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + break; + + /* Inquiry station */ + case OP_INQ: + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + IX = 0; + if (f1 != 1) { /* Only support group one */ + reason = STOP_IOCHECK; + goto done; + } + switch (f2) { + case 0: utmp = (IO_RDS << 8)|CHN_ALPHA; break; /* QR */ + case 1: utmp = (IO_WRS << 8)|CHN_ALPHA; break; /* QW */ + default: + reason = STOP_UUO; + goto done; + } + + /* Start off command */ + switch (chan_cmd(4, utmp, MA)) { + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_IOERR: + reason = STOP_IOCHECK; + break; + case SCPE_OK: + while(chan_active(0)) { + sim_interval = 0; + reason = sim_process_event(); + if (reason != SCPE_OK) + break; + chan_proc(); + } + break; + } + break; + /* Unit record I/O */ + case OP_UREC: + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + switch (f2) { + case 0: utmp = (IO_TRS << 8); break; /* US */ + case 1: utmp = (IO_RDS << 8)|CHN_ALPHA; break;/* UR */ + case 4: /* TYP */ + case 2: /* UW */ + case 3: /* UWIV */ + utmp = (IO_WRS << 8)|CHN_ALPHA; + break; + case 9: + if (cpu_unit.flags & OPTION_TIMER) { + if (f1 == 0) + timer = 0; + else if (f1 == 1) + WriteP(MA, PSIGN|timer); + goto done; + } + /* fall through */ + default: + reason = STOP_UUO; + goto done; + } + + /* Start off command */ + switch (chan_cmd(f1, utmp, MA)) { + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_IOERR: + reason = STOP_IOCHECK; + break; + case SCPE_OK: + while(chan_active(0)) { + sim_interval = 0; + reason = sim_process_event(); + if (reason != SCPE_OK) + break; + chan_proc(); + } + switch (f2) { + case 0: /* US */ + chan_stat(0, CHS_ERR); + break; + case 1: /* UR */ + if (chan_stat(0, CHS_ERR)) + break; + IC++; + if (chan_stat(0, CHS_EOF)) + break; + IC++; + break; + case 4: /* TYP */ + if (chan_stat(0, CHS_ERR)) + break; + IC++; + break; + case 2: /* UW */ + if (chan_stat(0, CHS_ERR)) + break; + IC++; + if (lpr_chan9[0]) + break; + IC++; + break; + case 3: /* UWIV */ + chan_stat(0, CHS_ERR); + break; + } + } + done: + break; + /* Tape channel channels */ + case OP_TAP1: + case OP_TAP2: + case OP_TAP3: + case OP_TAP4: + case OP_TAPP1: + case OP_TAPP2: + case OP_TAPP3: + case OP_TAPP4: + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + /* If pending IRQ, wait for it to be processed */ + if ((pri_latchs[opcode & 0xf] >> f1) & 1) { + iowait = 1; + break; + } + /* If the channel is busy no need to continue */ + if (chan_active(opcode & 0xf)) { + iowait = 1; + break; + } + /* Set up channel */ + utmp = ((opcode & 0xf) << 8) + f1; + if ((opcode & 0x100) == 0) /* Set priority signal */ + utmp |= 0x1000; + + tmp = 0; + switch (f2) { + case 0: + switch(MA % 10) { + case 0: tmp = (IO_TRS << 8); + utmp &= 0xfff; + break; /* TSEL */ + case 1: tmp = (IO_WEF << 8); break; /* TM */ + case 2: tmp = (IO_REW << 8); + utmp &= 0xfff; + break; /* TRW */ + case 3: tmp = (IO_RUN << 8); + utmp &= 0xfff; + break; /* TRU */ + case 4: tmp = (IO_BSR << 8); + utmp &= 0xfff; + break; /* TRB */ + case 5: tmp = (IO_WRS << 8)|CHN_SEGMENT|CHN_ALPHA; + break; /* TSM */ + case 6: tmp = (IO_ERG << 8); + utmp &= 0xfff; + break; /* TSK */ + case 7: chan_stat(opcode & 0xf, CHS_EOF); + goto done; /* TEF */ + case 8: tmp = (IO_SDL << 8); + utmp &= 0xfff; + break; /* TSDL */ + case 9: tmp = (IO_SDH << 8); + utmp &= 0xfff; + break; /* TSDH */ + } + break; + case 1: tmp = (IO_RDS << 8); break; /* TR */ + case 2: tmp = (IO_RDS << 8)|CHN_RECORD; + break; /* TRR */ + case 3: tmp = (IO_WRS << 8); break; /* TW */ + case 4: tmp = (IO_WRS << 8)|CHN_RECORD; + break; /* TWR */ + case 5: tmp = (IO_WRS << 8)|CHN_COMPRESS; + break; /* TWZ */ + case 6: tmp = (IO_WRS << 8)|CHN_COMPRESS|CHN_RECORD; + break; /* TWC */ + case 7: tmp = (IO_RDS << 8)|CHN_SEGMENT|CHN_ALPHA; + break; /* TSF */ + case 8: tmp = (IO_RDS << 8)|CHN_SEGMENT|CHN_RECORD|CHN_ALPHA; + break; /* TSB */ + case 9: tmp = (IO_RDS << 8)|CHN_ALPHA; + break; /* TRA */ + } + MBR = ((utmp & 0x1000) ? PSIGN:MSIGN) | 0x8000000000LL | + (((t_uint64)f2)<<32); + upd_idx(&MBR, IC); + bin_dec(&MBR, MA, 0, 4); + f = (utmp >> 8) & 0xf; + WriteP(150 + (f * 10) + (utmp & 0xF), MBR); + /* Start off command */ + switch(chan_cmd(utmp, tmp, MA)) { + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_IOERR: + reason = STOP_IOCHECK; + break; + case SCPE_OK: + /* Not priority transfer, wait for channel done */ + if ((utmp & 0x1000) == 0) + chwait = f; + } + break; + + /* Binary tape read channel 1 */ + case OP_TRN: + case OP_TRNP: + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + /* If pending IRQ, wait for it to be processed */ + if ((pri_latchs[1] >> f1) & 1) { + iowait = 1; + break; + } + /* If the channel is busy no need to continue */ + if (chan_active(1)) { + iowait = 1; + break; + } + /* Set up channel */ + utmp = (1 << 8) + f1 + 020; /* Binary mode */ + if ((opcode & 0x100) == 0) /* Set priority signal */ + utmp |= 0x1000; + + switch (f2) { + case 1: tmp = (IO_RDS << 8)|CHN_ALPHA; break; + default: /* Anything else is error */ + reason = STOP_UUO; + goto done; + } + MBR = ((utmp & 0x1000) ? PSIGN:MSIGN) | 0x8000000000LL | + (((t_uint64)f2)<<32); + upd_idx(&MBR, IC); + bin_dec(&MBR, MA, 0, 4); + f = (utmp >> 8) & 0xf; + WriteP(150 + (f * 10) + (utmp & 0xF), MBR); + /* Start off command */ + switch(chan_cmd(utmp, tmp, MA)) { + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_IOERR: + reason = STOP_IOCHECK; + break; + case SCPE_OK: + /* Not priority transfer, wait for channel done */ + if ((utmp & 0x1000) == 0) + chwait = f; + } + break; + + case OP_CHNP1: + case OP_CHNP2: + case OP_CHNP3: + case OP_CHNP4: + case OP_CHN1: + case OP_CHN2: + case OP_CHN3: + case OP_CHN4: + /* If the channel is busy no need to continue */ + if (chan_active(opcode & 0xf)) { + iowait = 1; + break; + } + utmp = ((opcode & 0xf) << 8) + ((f1 & 3) - 1) + 0x200; + if ((opcode & 0x100) == 0) /* Set priority signal */ + utmp |= 0x1000; + /* Build initial status word */ + MBR = ((t_uint64)opcode & 0xFF) << 32; + MBR |= (opcode & 0x100)?MSIGN:PSIGN; + upd_idx(&MBR, IC); + bin_dec(&MBR, MA, 0, 4); + tmp = 0xff; + switch (f2) { + case 1: tmp = CHN_COMPRESS; break; /* DCP */ + case 2: tmp = 0; break; /* DCUA */ + case 3: tmp = CHN_RECORD; break; /* DCUR */ + case 4: tmp = CHN_RECORD|CHN_COMPRESS;break;/*DCPR*/ + case 6: tmp = CHN_NUM_MODE; break; /* DCU */ + } + if (tmp == 0xff) + break; + tmp |= IO_RDS << 8; /* Activate channel */ + /* Start off command */ + switch(chan_cmd(utmp, tmp, MA)) { + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_IOERR: + reason = STOP_IOCHECK; + break; + case SCPE_OK: + WriteP(350 + ((utmp >> 8) & 0xf) - 4, MBR); + break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ic |= HIST_NOAFT|HIST_NOBEF; + } + break; + + /* Diagnostics instructions, not fully implimented */ + /* All of these errors can not occur on the simulation */ + case OP_DIAGT: + if (IX == 99) { + IX = 63; + } else if (IX == 98) { + IX = 63; + diaglatch |= 1LL << 63; + IC = MA; + break; + } else if (IX > 60) { + break; + } + if (diaglatch & (1LL << IX)) + IC = MA; + diaglatch &= ~(1LL << IX); + break; + case OP_DIAGR: + if (IX > 60) + break; + if (IX == 0) + diaglatch &= 1LL << 63; + else + diaglatch &= ~(1LL << IX); + break; + case OP_DIAGC: + case OP_DIAGS: + break; + } + + + } + chan_proc(); /* process any pending channel events */ + if (instr_count != 0 && --instr_count == 0) + return SCPE_STEP; + } /* end while */ + +/* Simulation halted */ + + return reason; +} + +/* Decimal arithmetic routines */ +/* Add a to b result in a */ +int dec_add(t_uint64 *a, t_uint64 b) { + t_uint64 t1,t2,t3; + t1 = *a ^ b; + t2 = *a + b; + t3 = t2 + 0x6666666666LL; + t2 = ((t2 < *a) || (t3 < t2)); + t2 = ((t1 ^ t3) >> 3) | (t2 << 37); + t2 = 0x2222222222LL & ~t2; + t1 = t3 - (3 * t2); + if ((t1 & (~DMASK)) != 0) { + t1 &= DMASK; + *a = t1; + return 1; + } + *a = t1; + return 0; +} + +/* Decimal arithmetic routines */ +/* Add a to b result in a */ +/* Don't detect overflow, and use 2 more guard digits */ +void dec_add_noov(t_uint64 *a, t_uint64 b) { + t_uint64 t1,t2,t3; + t1 = *a ^ b; + t2 = *a + b; + t3 = t2 + 0x666666666666LL; + t2 = ((t2 < *a) || (t3 < t2)); + t2 = ((t1 ^ t3) >> 3) | (t2 << 45); + t2 = 0x222222222222LL & ~t2; + t1 = t3 - (3 * t2); + *a = t1; +} + + +/* tens compliment a */ +void dec_comp(t_uint64 *a) { + *a = 0x9999999999LL - *a; + dec_add(a, 1LL); +} + +/* Compare to words, includeing sign */ +int dec_cmp(t_uint64 a, t_uint64 b) { + t_uint64 t1,t2,t3; + + a = 0x99999999999LL - a; + t1 = a ^ b; + t2 = a + b; + t3 = t2 + 0x66666666666LL; + t2 = ((t2 < a) || (t3 < t2)); + t2 = ((t1 ^ t3) >> 3) | (t2 << 41); + t2 = 0x22222222222LL & ~t2; + t1 = t3 - (3 * t2); + if (t1 == 0x99999999999LL) { + return 0; + } + if ((t1 & ~(SMASK|DMASK)) != 0) { + return 1; + } + return -1; +} + +/* Do a multiply step */ +void mul_step(t_uint64 *a, t_uint64 b, int c) { + t_uint64 prod; + int i; + + for(i = 0; i < 40; i+=4) { + /* Multiply each digit */ + prod = ((b >> i) & 0xf) * c; + /* Convert to decimal */ + prod = ((prod / 10) << 4) + (prod % 10); + if (prod != 0) { + prod <<= i; /* Move into place */ + dec_add_noov(a, prod); + } + } +} + +void div_step(t_uint64 b) { + t_uint64 t1,t2,t3; + + AC[1] &= DMASK; + AC[1] <<= 4; + AC[1] |= (AC[2] >> 36) & 0xf; + AC[2] <<= 4; + AC[2] &= DMASK; +/* Repeated subtract until overflow */ + while((AC[2] & 0xF) != 0x9) { + t1 = AC[1] ^ b; + t2 = AC[1] + b; + t3 = t2 + 0x66666666666LL; + t2 = ((t2 < AC[1]) || (t3 < t2)); + t2 = ((t1 ^ t3) >> 3) | (t2 << 41); + t2 = 0x22222222222LL & ~t2; + t1 = t3 - (3 * t2); + if ((t1 & ~DMASK) == 0) + return; + AC[1] = t1 & DMASK; + AC[2] += 1LL; + } +} + +/* Convert a binary number to BCD */ +void bin_dec(t_uint64 *a, uint32 b, int s, int l) { + s *= 4; + l *= 4; + l += s; + while (s < l) { + *a &= ~(0xFLL << s); + *a |= ((t_uint64)(b % 10)) << s; + b /= 10; + s += 4; + } +} + +/* Convert index to binary */ +uint32 dec_bin_idx(t_uint64 a) { + uint32 v = (a >> 16) & 0xf; + v += dscale[0][(a >> 20) & 0xf]; + v += dscale[1][(a >> 24) & 0xf]; + v += dscale[2][(a >> 28) & 0xf]; + if (emode) + a += dscale[3][(a >> 32) & 0xf]; + return v; +} + +uint32 dec_bin_lim(t_uint64 a, uint32 b) { + uint32 v = a & 0xf; + v += dscale[0][(a >> 4) & 0xf]; + v += dscale[1][(a >> 8) & 0xf]; + v += dscale[2][(a >> 12) & 0xf]; + if (emode) { + if (v < b) + v += dscale[3][((a >> 32) & 0xf)+1]; + } + return v; +} + +/* Extract information from a RDW */ +int get_rdw(t_uint64 a, uint32 *base, uint32 *limit) { + *base = dec_bin_idx(a); + *limit = dec_bin_lim(a, *base); + return (a >> 40); +} + +void upd_idx(t_uint64 *a, uint32 b) { + bin_dec(a, b, 4, (emode)?5:4); +} + +/* Scan for interupt */ +int scan_irq() { + int irq = 0; + int tmp; + + for(tmp = 0; tmp < 20 && irq == 0; tmp++) { + if ((pri_mask & (1 << tmp)) == 0) { + switch(tmp) { + case 9: /* Unit A */ + if (pri_latchs[0] & 0x002) { + pri_latchs[0] &= ~0x002; + irq = 104; + } + break; + case 8: /* Unit B */ + if (pri_latchs[0] & 0x004) { + pri_latchs[0] &= ~0x004; + irq = 105; + } + break; + case 7: /* Tape Chan 1 */ + if (pri_latchs[1]) { + int i; + for(i = 0; i < 10; i++) { + if (pri_latchs[1] & (1 << i)) { + pri_latchs[1] &= ~(1 << i); + irq = 150 + ((M[110+i] >> 36) & 0xf); + /* Save final record address */ + upd_idx(&M[99], 110+i); + M[99] &= DMASK; + M[99] |= PSIGN; + break; + } + } + } + break; + case 6: /* Tape Chan 2 */ + if (pri_latchs[2]) { + int i; + for(i = 0; i < 10; i++) { + if (pri_latchs[2] & (1 << i)) { + pri_latchs[2] &= ~(1 << i); + irq = 150 + ((M[120+i] >> 36) & 0xf); + /* Save final record address */ + upd_idx(&M[99], 120+i); + M[99] &= DMASK; + M[99] |= PSIGN; + break; + } + } + } + break; + case 5: /* 7300 disk seek */ + break; /* Not implimented */ + case 4: /* 7300 disk read and write */ + break; /* Not implimented */ + case 3: /* Inquiry group 1 */ + if (pri_latchs[0] & 0x080) { + pri_latchs[0] &= ~0x080; + irq = 106; + } + break; + case 2: /* Inquiry group 2 */ + if (pri_latchs[0] & 0x100) { + pri_latchs[0] &= ~0x100; + irq = 107; + } + break; + case 1: /* Tape Chan 3 */ + if (pri_latchs[3]) { + int i; + for(i = 0; i < 10; i++) { + if (pri_latchs[3] & (1 << i)) { + pri_latchs[3] &= ~(1 << i); + irq = 150 + ((M[130+i] >> 36) & 0xf); + /* Save final record address */ + upd_idx(&M[99], 130+i); + M[99] &= DMASK; + M[99] |= PSIGN; + break; + } + } + } + break; + case 0: /* Tape Chan 4 */ + if (pri_latchs[4]) { + int i; + for(i = 0; i < 10; i++) { + if (pri_latchs[4] & (1 << i)) { + pri_latchs[4] &= ~(1 << i); + irq = 150 + ((M[140+i] >> 36) & 0xf); + /* Save final record address */ + upd_idx(&M[99], 140+i); + M[99] &= DMASK; + M[99] |= PSIGN; + break; + } + } + } + break; + case 10: /* Channel 1 */ + if (pri_latchs[8] & 0x002) { + pri_latchs[8] &= ~0x002; + irq = 311; + upd_idx(&M[99], 301); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 11: /* Channel 1 Attn */ + if (pri_latchs[9] & 0x002) { + pri_latchs[9] &= ~0x002; + irq = 321; + upd_idx(&M[99], 301); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 12: /* Channel 2 */ + if (pri_latchs[8] & 0x004) { + pri_latchs[8] &= ~0x004; + irq = 312; + upd_idx(&M[99], 302); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 13: /* Channel 2 Attn */ + if (pri_latchs[9] & 0x004) { + pri_latchs[9] &= ~0x004; + irq = 322; + upd_idx(&M[99], 302); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 14: /* Channel 3 */ + if (pri_latchs[8] & 0x008) { + pri_latchs[8] &= ~0x008; + irq = 313; + upd_idx(&M[99], 303); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 15: /* Channel 3 Attn */ + if (pri_latchs[9] & 0x008) { + pri_latchs[9] &= ~0x008; + irq = 323; + upd_idx(&M[99], 303); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 16: /* Channel 4 */ + if (pri_latchs[8] & 0x010) { + pri_latchs[8] &= ~0x010; + irq = 314; + upd_idx(&M[99], 304); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + case 17: /* Channel 4 Attn */ + if (pri_latchs[9] & 0x010) { + pri_latchs[9] &= ~0x010; + irq = 324; + upd_idx(&M[99], 304); + M[99] &= DMASK; + M[99] |= PSIGN; + } + break; + } + } + } + return irq; +} + + +/* Initialize memory to all plus zero */ +void +mem_init() { + int i; + /* Force memory to have all valid signs */ + for(i = 0; i < MAXMEMSIZE; i++) + M[i] = PSIGN; +} + + +/* Reset routine */ +t_stat +cpu_reset(DEVICE * dptr) +{ + + AC[1] = PSIGN; + AC[2] = PSIGN; + AC[3] = PSIGN; + inds = PSIGN; + pri_enb = 1; + sim_brk_types = sim_brk_dflt = SWMASK('E'); + if (cpu_unit.flags & OPTION_TIMER) { + sim_rtcn_init_unit (&cpu_unit, cpu_unit.wait, TMR_RTC); + sim_activate(&cpu_unit, cpu_unit.wait); + } + return SCPE_OK; +} + +/* Interval timer routines */ +t_stat +rtc_srv(UNIT * uptr) +{ + if (cpu_unit.flags & OPTION_TIMER) { + timer_clock++; + if (timer_clock == 300) { + t_uint64 t = (t_uint64) timer; + dec_add(&t, 1); + timer = t & 0xfff; + timer_clock = 0; + } + sim_activate(&cpu_unit, sim_rtcn_calb(uptr->wait, TMR_RTC)); + } + return SCPE_OK; +} + +t_stat +rtc_reset(DEVICE * dptr) +{ + if (cpu_unit.flags & OPTION_TIMER) { + sim_activate(&cpu_unit, cpu_unit.wait); + } + return SCPE_OK; +} + +/* Memory examine */ + +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr > MEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = M[addr]; + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr > MEMSIZE) + return SCPE_NXM; + M[addr] = val; + return SCPE_OK; +} + +t_stat +cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + t_uint64 mc = 0; + uint32 i; + int32 v; + + v = val >> UNIT_V_MSIZE; + v = (v + 1) * 5000; + if ((v <= 0) || (v > MAXMEMSIZE)) + return SCPE_ARG; + for (i = v-1; i < MEMSIZE; i++) { + if (M[i] != PSIGN) { + mc = 1; + break; + } + } + if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + cpu_unit.flags &= ~UNIT_MSIZE; + cpu_unit.flags |= val; + MEMSIZE = v; + for (i = MEMSIZE; i < MAXMEMSIZE; i++) + M[i] = PSIGN; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].ic = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + t_stat r; + t_value sim_eval; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "IC EA BEFORE AFTER INST\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ic & HIST_PC) { /* instruction? */ + fprintf(st, "%05d ", h->ic & 0xffff); + if (h->ic & HIST_NOEA) + fputs(" ", st); + else + fprintf(st, " %05d ", h->ea); + if (h->ic & HIST_NOBEF) + fputs(" ", st); + else { + switch (h->before & SMASK) { + case PSIGN: fputc('+', st); break; + case MSIGN: fputc('-', st); break; + case ASIGN: fputc('@', st); break; + default: fputc('#', st); break; + } + fprint_val(st, h->before & DMASK, 16, 40, PV_RZRO); + } + fputc(' ', st); + if (h->ic & HIST_NOAFT) + fputs(" ", st); + else { + switch (h->after & SMASK) { + case PSIGN: fputc('+', st); break; + case MSIGN: fputc('-', st); break; + case ASIGN: fputc('@', st); break; + default: fputc('#', st); break; + } + fprint_val(st, h->after & DMASK, 16, 40, PV_RZRO); + } + fputc(' ', st); + sim_eval = h->op; + if ( + (fprint_sym(st, h->ic & AMASK, &sim_eval, &cpu_unit, + SWMASK('M'))) > 0) fputs("(undefined)", st); + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + +t_stat +cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { + fprintf (st, "The CPU can be set to a IBM 7070 or IBM 7074\n"); + fprintf (st, "The type of CPU can be set by one of the following commands\n\n"); + fprintf (st, " sim> set CPU 7070 sets IBM 7070 emulation\n"); + fprintf (st, " sim> set CPU 7074 sets IBM 7074 emulation\n\n"); + fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); + fprintf (st, " -c examine/deposit characters, 5 per word\n"); + fprintf (st, " -m examine/deposit IBM 7070 instructions\n\n"); + fprintf (st, "The memory of the CPU can be set in 5K incrememts from 5K to 30K with the\n\n"); + fprintf (st, " sim> SET CPU xK\n\n"); + fprintf (st, "For the IBM 7070 the following options can be enabled\n\n"); + fprintf (st, " sim> SET CPU FLOAT enables Floating Point\n"); + fprintf (st, " sim> SET CPU NOFLOAT disables Floating Point\n\n"); + fprintf (st, " sim> SET CPU EXTEND enables extended memory\n"); + fprintf (st, " sim> SET CPU NOEXTEND disables extended memory\n\n"); + fprintf (st, " sim> SET CPU CLOCK enables timer clock\n"); + fprintf (st, " sim> SET CPU NOCLOCK disables timer clock\n\n"); + fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n"); + fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); + fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); + fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); + fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); + fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) { + return "IBM 7070 CPU"; +} + diff --git a/I7000/i7070_defs.h b/I7000/i7070_defs.h new file mode 100644 index 00000000..45dba797 --- /dev/null +++ b/I7000/i7070_defs.h @@ -0,0 +1,241 @@ +/* i7070_defs.h: IBM 7070 simulator definitions + + Copyright (c) 2006-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 "sim_defs.h" /* simulator defns */ + +#include "i7000_defs.h" +/* Simulator stop codes */ + +#define STOP_IONRDY 1 /* I/O dev not ready */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_UUO 4 /* invalid opcode */ +#define STOP_INDLIM 5 /* indirect limit */ +#define STOP_XECLIM 6 /* XEC limit */ +#define STOP_IOCHECK 7 /* IOCHECK */ +#define STOP_MMTRP 8 /* mm in trap */ +#define STOP_INVLIN 9 /* 7750 invalid line number */ +#define STOP_INVMSG 10 /* 7750 invalid message */ +#define STOP_NOOFREE 11 /* 7750 No free output buffers */ +#define STOP_NOIFREE 12 /* 7750 No free input buffers */ + +/* Trap codes */ + + +/* Conditional error returns */ + +/* Memory */ + +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) +extern t_uint64 M[MAXMEMSIZE]; + +/* Arithmetic */ + +/* Instruction format */ + +/* Globally visible flags */ +#define DMASK 0x0FFFFFFFFFFLL +#define IMASK 0x000FFFF0000LL +#define IMASK2 0x00FFFFF0000LL +#define XMASK 0x00F00000000LL +#define XMASK2 0x0F000000000LL +#define AMASK 0x0000000FFFFLL +#define OMASK 0x0FF00000000LL +#define SMASK 0xF0000000000LL +#define PSIGN 0x90000000000LL +#define MSIGN 0x60000000000LL +#define ASIGN 0x30000000000LL +#define NINES 0x09999999999LL /* All nines */ +#define FNINES 0x00099999999LL /* Floating point all nines */ +#define EMASK 0x0FF00000000LL /* Floating point exponent */ +#define FMASK 0x000FFFFFFFFLL /* Floating point mantisa mask */ +#define NMASK 0x000F0000000LL /* Floating point normalize mask */ + +/* 7604 channel commands */ +#define CHN_ALPHA_MODE 000 /* Recieve alpha words */ +#define CHN_NUM_MODE 001 /* Recieve numeric words */ +#define CHN_WRITE 002 /* Channel direction */ +#define CHN_LAST 004 /* Last transfer */ +#define CHN_MODE 0170 /* Mode of channel */ +#define CHN_NORMAL 000 /* Normal mode */ +#define CHN_COMPRESS 010 /* Compress leading zeros */ +#define CHN_RECORD 020 /* Dectect record marks */ +#define CHN_SEGMENT 040 /* Search for next segment */ +#define CHN_ALPHA 0100 /* Alpha read only */ +#define CHN_RM_FND 0200 /* Record mark read */ + +/* 7070 channel specific functions */ + +/* Issue a command to a channel */ +int chan_cmd(uint16 dev, uint16 cmd, uint16 addr); + +/* Decimal helper functions */ +int dec_add(t_uint64 *a, t_uint64 b); +void dec_add_noov(t_uint64 *a, t_uint64 b); +void dec_comp(t_uint64 *a); +int dec_cmp(t_uint64 a, t_uint64 b); +void mul_step(t_uint64 *a, t_uint64 b, int c); +void div_step(t_uint64 b); +void bin_dec(t_uint64 *a, uint32 b, int s, int l); +uint32 dec_bin_idx(t_uint64 a); +uint32 dec_bin_lim(t_uint64 a, uint32 b); +int get_rdw(t_uint64 a, uint32 *base, uint32 *limit); +void upd_idx(t_uint64 *a, uint32 b); +int scan_irq(); + +/* Opcodes */ +#define OP_HB 0x000 +#define OP_B 0x001 +#define OP_BLX 0x002 +#define OP_CD 0x003 +#define OP_EXMEM 0x004 +#define OP_DIAGC 0x008 +#define OP_DIAGT 0x009 +#define OP_BZ1 0x010 +#define OP_BV1 0x011 +#define OP_ST1 0x012 +#define OP_ZA1 0x013 +#define OP_A1 0x014 +#define OP_C1 0x015 +#define OP_ZAA 0x016 +#define OP_AA 0x017 +#define OP_AS1 0x018 +#define OP_AAS1 0x019 +#define OP_BZ2 0x020 +#define OP_BV2 0x021 +#define OP_ST2 0x022 +#define OP_ZA2 0x023 +#define OP_A2 0x024 +#define OP_C2 0x025 +#define OP_AS2 0x028 +#define OP_AAS2 0x029 +#define OP_BZ3 0x030 +#define OP_BV3 0x031 +#define OP_ST3 0x032 +#define OP_ZA3 0x033 +#define OP_A3 0x034 +#define OP_C3 0x035 +#define OP_AS3 0x038 +#define OP_AAS3 0x039 +#define OP_BL 0x040 +#define OP_BFLD 0x041 +#define OP_BXN 0x044 +#define OP_XL 0x045 +#define OP_XZA 0x046 +#define OP_XA 0x047 +#define OP_XSN 0x048 +#define OP_BIX 0x049 +#define OP_SC 0x050 +#define OP_BSWITCH 0x051 +#define OP_M 0x053 +#define OP_INQ 0x054 +#define OP_PC 0x055 +#define OP_ENA 0x056 +#define OP_ENB 0x057 +#define OP_PRTST 0x060 +#define OP_BSW21 0x061 +#define OP_BSW22 0x062 +#define OP_BSW23 0x063 +#define OP_PR 0x064 +#define OP_RS 0x065 +#define OP_LL 0x066 +#define OP_LE 0x067 +#define OP_LEH 0x068 +#define OP_UREC 0x069 +#define OP_FBV 0x070 +#define OP_FR 0x071 +#define OP_FM 0x073 +#define OP_FA 0x074 +#define OP_FZA 0x075 +#define OP_FAD 0x076 +#define OP_FAA 0x077 +#define OP_TAPP1 0x081 +#define OP_TAPP2 0x082 +#define OP_TAPP3 0x083 +#define OP_TAPP4 0x084 +#define OP_TRNP 0x088 +#define OP_CHNP1 0x093 +#define OP_CHNP2 0x094 +#define OP_CHNP3 0x096 +#define OP_CHNP4 0x097 +#define OP_HP 0x100 +#define OP_NOP 0x101 +#define OP_CS 0x103 +#define OP_DIAGS 0x108 +#define OP_DIAGR 0x109 +#define OP_BM1 0x110 +#define OP_ZST1 0x111 +#define OP_STD1 0x112 +#define OP_ZS1 0x113 +#define OP_S1 0x114 +#define OP_CA 0x115 +#define OP_ZSA 0x116 +#define OP_SA 0x117 +#define OP_SS1 0x118 +#define OP_BM2 0x120 +#define OP_ZST2 0x121 +#define OP_STD2 0x122 +#define OP_ZS2 0x123 +#define OP_S2 0x124 +#define OP_SS2 0x128 +#define OP_BM3 0x130 +#define OP_ZST3 0x131 +#define OP_STD3 0x132 +#define OP_ZS3 0x133 +#define OP_S3 0x134 +#define OP_SS3 0x138 +#define OP_BH 0x140 +#define OP_BE 0x141 +#define OP_BCX 0x143 +#define OP_BXM 0x144 +#define OP_XU 0x145 +#define OP_XZS 0x146 +#define OP_XS 0x147 +#define OP_XLIN 0x148 +#define OP_BDX 0x149 +#define OP_CSC 0x150 +#define OP_D 0x153 +#define OP_ENS 0x156 +#define OP_EAN 0x157 +#define OP_PRION 0x161 +#define OP_PRIOF 0x162 +#define OP_RG 0x165 +#define OP_FBU 0x170 +#define OP_FD 0x173 +#define OP_FS 0x174 +#define OP_FDD 0x175 +#define OP_FADS 0x176 +#define OP_FSA 0x177 +#define OP_TAP1 0x181 +#define OP_TAP2 0x182 +#define OP_TAP3 0x183 +#define OP_TAP4 0x184 +#define OP_TRN 0x188 +#define OP_CHN1 0x193 +#define OP_CHN2 0x194 +#define OP_CHN3 0x196 +#define OP_CHN4 0x197 + + + + diff --git a/I7000/i7070_sys.c b/I7000/i7070_sys.c new file mode 100644 index 00000000..1908dfa1 --- /dev/null +++ b/I7000/i7070_sys.c @@ -0,0 +1,1100 @@ +/* i7070_sys.c: IBM 7070 Simulator system interface. + + Copyright (c) 2006-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 "i7070_defs.h" +#include "sim_card.h" +#include + +t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 7070"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#ifdef NUM_DEVS_CON + &con_dev, +#endif +#if NUM_DEVS_MT > 0 + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#if NUM_DEVS_MT > 2 + &mtc_dev, +#if NUM_DEVS_MT > 3 + &mtd_dev, +#endif +#endif +#endif +#endif +#if NUM_DEVS_HT > 0 + &hta_dev, +#if NUM_DEVS_HT > 1 + &htb_dev, +#endif +#endif +#ifdef NUM_DEVS_DSK + &dsk_dev, +#endif +#ifdef NUM_DEVS_COM + &coml_dev, + &com_dev, +#endif +#ifdef NUM_DEVS_CHRON + &chron_dev, +#endif + NULL +}; +/* Device addressing words */ +#ifdef NUM_DEVS_CDR +DIB cdr_dib = { CH_TYP_UREC, 1, 01, 0xF, &cdr_cmd, NULL }; +#endif +#ifdef NUM_DEVS_CDP +DIB cdp_dib = { CH_TYP_UREC, 1, 02, 0xF, &cdp_cmd, &cdp_ini }; +#endif +#ifdef NUM_DEVS_LPR +DIB lpr_dib = { CH_TYP_UREC, 1, 03, 0xF, &lpr_cmd, &lpr_ini }; +#endif +#ifdef NUM_DEVS_CON +DIB con_dib = { CH_TYP_UREC, 1, 00, 0xF, &con_cmd, &con_ini }; +#endif +DIB mt_dib = { CH_TYP_76XX, NUM_UNITS_MT, 0000, 0000, &mt_cmd, &mt_ini }; +#ifdef NUM_DEVS_CHRON +DIB chron_dib = { CH_TYP_76XX, 1, 0000, 0000, &chron_cmd, NULL }; +#endif +#ifdef NUM_DEVS_DSK +DIB dsk_dib = { CH_TYP_79XX, 0, 0, 0, &dsk_cmd, &dsk_ini }; +#endif +#ifdef NUM_DEVS_HT +DIB ht_dib = { CH_TYP_79XX, NUM_UNITS_HT, 0, 0, &ht_cmd, NULL }; +#endif +#ifdef NUM_DEVS_COM +DIB com_dib = { CH_TYP_79XX, 0, 0, 0, &com_cmd, NULL }; +#endif + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "", + "", + "I/O Check error", + "", + "7750 invalid line number", + "7750 invalid message", + "7750 No free output buffers", + "7750 No free input buffers", + "Field overflow", + "Sign change", + "Divide error", + "Alpha index word", + "Error?", "Error2", 0 +}; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {0, 0} +}; + +DEBTAB crd_debug[] = { + {"CHAN", DEBUG_CHAN}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"CARD", DEBUG_CARD}, + {0, 0} +}; + +const char mem_to_ascii[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', 's', + 'b', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'x', ',', '(', '~', '\\', '_', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '|', + /*Sq*/ /*RM*/ +}; + +char mem_ascii[256] = { +/* 00 */ ' ', '~', '~', '~', '~', '~', '~', '~', + /* */ +/* 08 */ '~', '~', '~', '~', '~', '~', '~', '~', + /* . sq ? */ +/* 10 */ '~', '~', '~', '~', '~', '.', '[', '?', + /* \ # */ +/* 18 */ '\\', '#', '|', '~', '~', '~', '~', '~', + /*+/- $ * ? */ +/* 20 */ '+', '~', '~', '~', '~', '$', '*', '?', + /* ? +/- */ +/* 28 */ '?', '-', '~', '~', '~', '~', '~', '~', + /* - / , %/( ? */ +/* 30 */ '-', '/', '~', '~', '~', ',', '%', '?', + /* ? sm */ +/* 38 */ '?', 's', '~', '~', '~', '~', '~', '~', + /* =/# !/@ ? */ +/* 40 */ '~', '~', '~', '~', '~', '=', '!', '?', + /* ? tm */ +/* 48 */ '?', 't', '~', '~', '~', '~', '~', '~', + /* */ +/* 50 */ '~', '~', '~', '~', '~', '~', '~', '~', + /* */ +/* 58 */ '~', '~', '~', '~', '~', '~', '~', '~', + /* +0 A B C D E F G */ +/* 60 */ '^', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + /* H I */ +/* 68 */ 'H', 'I', '~', '~', '~', '~', '~', '~', + /* -0 J K L M N O P */ +/* 70 */ '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + /* Q R */ +/* 78 */ 'Q', 'R', '~', '~', '~', '~', '~', '~', + /* rm S T U V W X */ +/* 80 */ 'r', '~', 'S', 'T', 'U', 'V', 'W', 'X', + /* Y Z */ +/* 88 */ 'Y', 'Z', '~', '~', '~', '~', '~', '~', + /* 0 1 2 3 4 5 6 7 */ +/* 90 */ '0', '1', '2', '3', '4', '5', '6', '7', + /* 8 9 */ +/* 98 */ '8', '9', '~', '~', '~', '~', '~', '~' +}; + + +/* Load a card image file into memory. */ + +t_stat +sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + /* Currently not implimented until I know format of load files */ + return SCPE_NOFNC; +} + +/* Symbol tables */ +typedef struct _opcode +{ + uint16 opbase; + const char *name; + uint8 type; +} +t_opcode; + +#define TYPE_A 0 /* Basic oo ii ff aaaa */ +#define TYPE_S 1 /* Shift control */ +#define TYPE_B 2 /* Basic oo ii xx aaaa */ +#define TYPE_C 3 /* Alternate switch */ +#define TYPE_D 4 /* Electronic switch */ +#define TYPE_E 5 /* Extended memory */ +#define TYPE_F 6 /* Branch type 2 */ +#define TYPE_Z 8 /* Sign control */ +#define TYPE_I 9 /* Basic oo ii II aaaa */ +#define TYPE_X 10 /* No extra values */ +#define TYPE_T 11 /* Tape control */ +#define TYPE_U 12 /* unit record control */ +#define TYPE_V 13 /* Channel IO */ +#define TYPE_G 14 /* Diag SW */ +#define TYPE_P1 15 /* Branch priority */ +#define TYPE_P2 16 /* Set Priority latch */ +#define TYPE_P3 17 /* Clear Priority latch */ +#define TYPE_IQ 18 /* Inquiry instruction */ +#define TYPE_TB 19 /* Binary Tape operations */ + +/* Opcodes */ +t_opcode base_ops[] = { + {OP_HB, "HB", TYPE_B}, + {OP_B, "B", TYPE_B}, + {OP_BLX, "BLX", TYPE_I}, + {OP_CD, "CD", TYPE_A}, + {OP_EXMEM, "EXMEM", TYPE_E}, + {OP_DIAGC, "DIAGC", TYPE_I}, + {OP_DIAGT, "DIAGT", TYPE_I}, + {OP_DIAGR, "DIAGR", TYPE_I}, + {OP_DIAGS, "DIAGS", TYPE_I}, + {OP_BZ1, "BZ1", TYPE_B}, + {OP_BV1, "BV1", TYPE_B}, + {OP_ST1, "ST1", TYPE_A}, + {OP_ZA1, "ZA1", TYPE_A}, + {OP_A1, "A1", TYPE_A}, + {OP_C1, "C1", TYPE_A}, + {OP_ZAA, "ZAA", TYPE_A}, + {OP_AA, "AA", TYPE_A}, + {OP_AAS1, "AAS1", TYPE_A}, + {OP_AS1, "AS1", TYPE_A}, + {OP_BZ2, "BZ2", TYPE_B}, + {OP_BV2, "BV2", TYPE_B}, + {OP_ST2, "ST2", TYPE_A}, + {OP_ZA2, "ZA2", TYPE_A}, + {OP_A2, "A2", TYPE_A}, + {OP_C2, "C2", TYPE_A}, + {OP_AS2, "AS2", TYPE_A}, + {OP_AAS2, "AAS2", TYPE_A}, + {OP_BZ3, "BZ3", TYPE_B}, + {OP_BV3, "BV3", TYPE_B}, + {OP_ST3, "ST3", TYPE_A}, + {OP_ZA3, "ZA3", TYPE_A}, + {OP_A3, "A3", TYPE_A}, + {OP_C3, "C3", TYPE_A}, + {OP_AS3, "AS3", TYPE_A}, + {OP_AAS3, "AAS3", TYPE_A}, + {OP_BL, "BL", TYPE_B}, + {OP_BFLD, "BFLD", TYPE_F}, + {OP_BXN, "BXN", TYPE_I}, + {OP_XL , "XL ", TYPE_I}, + {OP_XZA, "XZA", TYPE_I}, + {OP_XA, "XA", TYPE_I}, + {OP_XSN, "XSN", TYPE_I}, + {OP_BIX, "BIX", TYPE_I}, + {OP_SC, "SC", TYPE_S}, + {OP_INQ, "INQ", TYPE_IQ}, + {OP_BSWITCH, "BSWITCH", TYPE_C}, + {OP_M, "M", TYPE_A}, + {OP_PC, "PC", TYPE_I}, + {OP_ENA, "ENA", TYPE_I}, + {OP_ENB, "ENB", TYPE_I}, + {OP_PRTST, "PRTST", TYPE_P1}, + {OP_BSW21, "BES", TYPE_D}, + {OP_BSW22, "BES", TYPE_D}, + {OP_BSW23, "BES", TYPE_D}, + {OP_PR, "PR", TYPE_B}, + {OP_RS, "RS", TYPE_I}, + {OP_LL, "LL", TYPE_A}, + {OP_LE, "LE", TYPE_A}, + {OP_LEH, "LEH", TYPE_A}, + {OP_UREC, "UREC", TYPE_U}, + {OP_FBV, "FBV", TYPE_B}, + {OP_FR, "FR", TYPE_X}, + {OP_FM, "FM", TYPE_B}, + {OP_FA, "FA", TYPE_B}, + {OP_FZA, "FZA", TYPE_B}, + {OP_FAD, "FAD", TYPE_B}, + {OP_FAA, "FAA", TYPE_B}, + {OP_HP, "HP", TYPE_X}, + {OP_NOP, "NOP", TYPE_X}, + {OP_CS, "CS", TYPE_Z}, + {OP_BM1, "BM1", TYPE_B}, + {OP_ZST1, "ZST1", TYPE_A}, + {OP_STD1, "STD1", TYPE_A}, + {OP_ZS1, "ZS1", TYPE_A}, + {OP_S1, "S1", TYPE_A}, + {OP_CA, "CA", TYPE_A}, + {OP_ZSA, "ZSA", TYPE_A}, + {OP_SA, "SA", TYPE_A}, + {OP_SS1, "SS1", TYPE_A}, + {OP_BM2, "BM2", TYPE_B}, + {OP_ZST2, "ZST2", TYPE_A}, + {OP_STD2, "STD2", TYPE_A}, + {OP_ZS2, "ZS2", TYPE_A}, + {OP_S2, "S2", TYPE_A}, + {OP_SS2, "SS2", TYPE_A}, + {OP_BM3, "BM3", TYPE_B}, + {OP_ZST3, "ZST3", TYPE_A}, + {OP_STD3, "STD3", TYPE_A}, + {OP_ZS3, "ZS3", TYPE_A}, + {OP_S3, "S3", TYPE_A}, + {OP_SS3, "SS3", TYPE_A}, + {OP_BH, "BH", TYPE_B}, + {OP_BE, "BE", TYPE_B}, + {OP_BCX, "BCX", TYPE_I}, + {OP_BXM, "BXM", TYPE_I}, + {OP_XU, "XU", TYPE_I}, + {OP_XZS, "XZS", TYPE_I}, + {OP_XS, "XS", TYPE_I}, + {OP_XLIN, "XLIN", TYPE_I}, + {OP_BDX, "BDX", TYPE_I}, + {OP_CSC, "CSC", TYPE_S}, + {OP_D, "D", TYPE_A}, + {OP_ENS, "ENS", TYPE_I}, + {OP_EAN, "EAN", TYPE_I}, + {OP_PRION, "DCAN", TYPE_P2}, + {OP_PRIOF, "DCAF", TYPE_P3}, + {OP_RG, "RG", TYPE_I}, + {OP_FBU, "FBU", TYPE_B}, + {OP_FD, "FD", TYPE_B}, + {OP_FS, "FS", TYPE_B}, + {OP_FDD, "FDD", TYPE_B}, + {OP_FADS, "FADS", TYPE_B}, + {OP_FSA, "FSA", TYPE_B}, + {OP_TRN, "TRN", TYPE_TB}, + {OP_TRNP, "PTRN", TYPE_TB}, + {OP_TAP1, "", TYPE_T}, + {OP_TAP2, "", TYPE_T}, + {OP_TAP3, "", TYPE_T}, + {OP_TAP4, "", TYPE_T}, + {OP_TAPP1, "P", TYPE_T}, + {OP_TAPP2, "P", TYPE_T}, + {OP_TAPP3, "P", TYPE_T}, + {OP_TAPP4, "P", TYPE_T}, + {OP_CHN1, "", TYPE_V}, + {OP_CHN2, "", TYPE_V}, + {OP_CHN3, "", TYPE_V}, + {OP_CHN4, "", TYPE_V}, + {OP_CHNP1, "P", TYPE_V}, + {OP_CHNP2, "P", TYPE_V}, + {OP_CHNP3, "P", TYPE_V}, + {OP_CHNP4, "P", TYPE_V}, + {0, NULL, 0}, +}; + +t_opcode sub_ops[] = { + {0, "SR", TYPE_S}, + {1, "SRR", TYPE_S}, + {2, "SL", TYPE_S}, + {3, "SLC", TYPE_S}, + {4, "SRS", TYPE_S}, + {5, "SLS", TYPE_S}, + {6, "SRS", TYPE_S}, + {7, "SLS", TYPE_S}, + {0, "BAS", TYPE_C}, + {1, "BCB", TYPE_C}, + {2, "BDCB", TYPE_C}, + {0, "BES", TYPE_D}, + {1, "ESN", TYPE_D}, + {2, "ESF", TYPE_D}, + {3, "BSN", TYPE_D}, + {4, "BSF", TYPE_D}, + {0x00, "BAL", TYPE_P1}, + {0x01, "BUL", TYPE_P1}, + {0x02, "BUL", TYPE_P1}, + {0x03, "BQL", TYPE_P1}, + {0x04, "BQL", TYPE_P1}, + {0x10, "BTL", TYPE_P1}, + {0x20, "BTL", TYPE_P1}, + {0x30, "BTL", TYPE_P1}, + {0x40, "BTL", TYPE_P1}, + {0x80, "BDCL", TYPE_P1}, + {0x90, "BDCA", TYPE_P1}, + {0x01, "ULN", TYPE_P2}, + {0x02, "ULN", TYPE_P2}, + {0x03, "QLN", TYPE_P2}, + {0x04, "QLN", TYPE_P2}, + {0x10, "TLN", TYPE_P2}, + {0x20, "TLN", TYPE_P2}, + {0x30, "TLN", TYPE_P2}, + {0x40, "TLN", TYPE_P2}, + {0x80, "BDLN", TYPE_P2}, + {0x90, "BDAN", TYPE_P2}, + {0x01, "ULF", TYPE_P3}, + {0x02, "ULF", TYPE_P3}, + {0x03, "QLF", TYPE_P3}, + {0x04, "QLF", TYPE_P3}, + {0x10, "TLF", TYPE_P3}, + {0x20, "TLF", TYPE_P3}, + {0x30, "TLF", TYPE_P3}, + {0x40, "TLF", TYPE_P3}, + {0x80, "BDLF", TYPE_P3}, + {0x90, "BDAF", TYPE_P3}, + {0, "BASS", TYPE_E}, + {1, "ASSN", TYPE_E}, + {2, "ASSF", TYPE_E}, + {0, "BFV", TYPE_F}, + {1, "SMFV", TYPE_F}, + {2, "HMFV", TYPE_F}, + {0x30, "CSA", TYPE_Z}, + {0x60, "CSM", TYPE_Z}, + {0x90, "CSP", TYPE_Z}, + {0x31, "MSA", TYPE_Z}, + {0x61, "MSM", TYPE_Z}, + {0x91, "MSP", TYPE_Z}, + {2, "SMSC", TYPE_Z}, + {3, "HMSC", TYPE_Z}, + {4, "BSC", TYPE_Z}, + {0x10, "TR", TYPE_T}, + {0x20, "TRR", TYPE_T}, + {0x30, "TW", TYPE_T}, + {0x40, "TWR", TYPE_T}, + {0x50, "TWZ", TYPE_T}, + {0x60, "TWC", TYPE_T}, + {0x70, "TSF", TYPE_T}, + {0x80, "TSB", TYPE_T}, + {0x90, "TRA", TYPE_T}, + {0x00, "TSEL", TYPE_T}, + {0x01, "TM", TYPE_T}, + {0x02, "TRW", TYPE_T}, + {0x03, "TRU", TYPE_T}, + {0x04, "TRB", TYPE_T}, + {0x05, "TSM", TYPE_T}, + {0x06, "TSK", TYPE_T}, + {0x07, "TEF", TYPE_T}, + {0x08, "TSLD", TYPE_T}, + {0x09, "TSHD", TYPE_T}, + {0, "US", TYPE_U}, + {1, "UR", TYPE_U}, + {2, "UW", TYPE_U}, + {3, "UWIV", TYPE_U}, + {4, "TYP", TYPE_U}, + {1, "DCP", TYPE_V}, + {2, "DCUA", TYPE_V}, + {3, "DCUR", TYPE_V}, + {4, "DCPR", TYPE_V}, + {6, "DCU", TYPE_V}, + {0, "QR", TYPE_IQ}, + {1, "QW", TYPE_IQ}, + {0, NULL, 0}, +}; + + +const char *chname[11] = { + "*", "1", "2", "3", "4", "A", "B", "C", "D" +}; + +/* Print out an instruction */ +void +print_opcode(FILE * of, t_value val, t_opcode * tab) +{ + uint32 MA; + uint8 f1; + uint8 f2; + uint8 IX; + uint16 op; + int type; + int t; + + MA = AMASK & val; + f1 = (val >> 16) & 0xf; + f2 = (val >> 20) & 0xf; + IX = (val >> 24) & 0xff; + op = (val >> 32) & 0xff; + if ((val & SMASK) == MSIGN) + op |= 0x100; + + while (tab->name != NULL) { + if (tab->opbase == op) { + switch (type = tab->type) { + case TYPE_X: + fputs(tab->name, of); + return; + + case TYPE_A: + fputs(tab->name, of); + fputc(' ', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + if (f1 != 0 || f2 != 9) { + fprintf(of, "(%d,%d)", f2, f1); + } + return; + case TYPE_S: + f1 = (MA >> 8) & 0xf; + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + if (op == OP_SC) + fputc('0' + ((MA >> 12) & 0xf), of); + fputc(' ', of); + fprint_val(of, MA & 0xff, 16, 8, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_B: + fputs(tab->name, of); + fputc(' ', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_C: + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fprintf(of, " %d,", f2); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_D: /* Electronic switch */ + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f2) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fputc(' ', of); + fputc('0' + (op & 0xf), of); + fputc('0' + f1, of); + fputc(',', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_E: /* Extended memory */ + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + if (f1 == 0) { + fputc(' ', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + } + return; + case TYPE_F: /* Branch type 2 */ + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f2) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + if (f2 == 0) { + fputc(' ', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + } + return; + case TYPE_Z: /* Sign control */ + f1 |= f2 << 4; + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fputc(' ', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_TB: /* Binary Tape operation. */ + fprintf(of, "%s %d,", tab->name, f2); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_I: /* Indexed operand */ + fprintf(of, "%s %d%d,", tab->name, f2, f1); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_T: /* Tape Control */ + f1 <<= 4; + if (f1 == 0) + f1 += MA & 0xf; + fputs(tab->name, of); + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fputc(' ', of); + fputc('0' + (op & 0xf), of); + fputc('0' + f2, of); + fputc(',', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_U: /* Unit Record Control */ + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f2) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fputc(' ', of); + fputc('0' + f1, of); + fputc(',', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_V: /* Channel Control */ + fputs(tab->name, of); + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f1) + break; + } + if (tab->name == NULL) + break; + fputs(tab->name, of); + fputc(' ', of); + fputc('0' + (op & 0xf), of); + fputc(',', of); + fputc('0' + f2, of); + fputc(',', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_P1: /* Priority Control */ + case TYPE_P2: + case TYPE_P3: + if (f2 == 0) + t = f1; + else + t = f2 << 4; + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == t) + break; + } + if (tab->name == NULL) { + /* Not found, decode */ + for(tab = base_ops; tab->name != NULL; tab++) { + if (tab->opbase == op) + break; + } + } + fputs(tab->name, of); + fputc(' ', of); + switch(f2) { + case 4: + case 3: + case 2: fputc('0' + f2 - 1, of); + case 1: + case 8: + case 9: + case 0: fputc('0' + f1, of); + break; + } + fputc(',', of); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + case TYPE_IQ: + for(tab = sub_ops; tab->name != NULL; tab++) { + if (tab->type == type && tab->opbase == f2) + break; + } + if (tab->name == NULL) + break; + fprintf(of, "%s %d,", tab->name, f2); + fprint_val(of, MA, 16, 16, PV_RZRO); + if (IX != 0) { + fputs("+X", of); + fprint_val(of, IX, 16, 8, 0); + } + return; + default: + return; + } + } + tab++; + } + fprintf(of, " %d Unknown opcode", op); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat +fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw) +{ + t_uint64 inst = *val; + +/* Print value in decimal first */ + fputc(' ', of); + switch (inst & SMASK) { + case PSIGN: fputc('+', of); break; + case MSIGN: fputc('-', of); break; + case ASIGN: fputc('@', of); break; + default: fputc('#', of); break; + } + fprint_val(of, inst & DMASK, 16, 40, PV_RZRO); + + if (sw & SWMASK('M')) { + fputs(" ", of); + print_opcode(of, inst, base_ops); + } + if (sw & SWMASK('C')) { + int i; + + fputs(" '", of); + for (i = 4; i >= 0; i--) { + int ch; + + ch = (int)(inst >> (8 * i)) & 0xff; + fputc(mem_ascii[ch], of); + } + fputc('\'', of); + } + return SCPE_OK; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + int idx; + t_value a, opr, d; + int sign; + char opcode[100]; + + while (isspace(*cptr)) + cptr++; + d = 0; + if (sw & SWMASK('M')) { + t_opcode *op, *op2; + + i = 0; + sign = 0; + /* Grab opcode */ + cptr = get_glyph(cptr, opcode, 0); + + op2 = 0; + if((op = find_opcode(opcode, base_ops)) == 0) { + if ((op2 = find_opcode(opcode, sub_ops)) != 0) { + for(op = base_ops; (op->type != op2->type) && + (op->name != NULL); op++); + if (op->name == NULL) + return STOP_UUO; + } else if (opcode[0] == 'P' && + (op2 = find_opcode(&opcode[1], sub_ops)) != 0) { + for(op = base_ops; (op->type != op2->type) && + (op->name != NULL) && op->name[0] == 'P'; op++); + if (op->name == NULL) + return STOP_UUO; + } + } + if (op == 0) + return STOP_UUO; + + d = (((t_uint64)op->opbase) << 32) & DMASK; + d |= (op->opbase & 0x100)? MSIGN:PSIGN; + if (op->type == TYPE_X) { + *val = d; + return SCPE_OK; + } + if (op2 != 0 && op2->opbase != 0 && op->type == TYPE_E) { + d |= ((t_uint64)op2->opbase) << 24; + *val = d; + return SCPE_OK; + } + if (op2 != 0 && op2->opbase != 0 && op->type == TYPE_F) { + d |= ((t_uint64)op2->opbase) << 28; + *val = d; + return SCPE_OK; + } + a = 0; + idx = 0; + opr = 0; + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Collect first argument if there is one */ + while (*cptr >= '0' && *cptr <= '9') + opr = (opr << 4) + (*cptr++ - '0'); + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Type A opc addr+X#(n,m) */ + /* Type B opc addr+X# */ + /* Type S opcA addr+X# f1 = op */ + /* Type C opc addr+X#,f2 f1 = op */ + /* Type D opc opf2,addr+X */ + /* Type E opc *addr+X# f1 = op */ + /* Type F opc *addr+X# f2 = op*/ + /* Type Z opc addr+X# f1 = op */ + /* Type I opc f2f1,addr+X# */ + /* Type T opc opf2,addr+X# f1 = op */ + /* Type U opc f1,addr+X# f2 = op */ + /* Type V opc opf1,addr+X# f2 = op */ + /* End of opcode, give to address */ + if (*cptr == '\0' || *cptr == '(') { + a = opr; + opr = 0; + } + + /* If plus, then must be address and follow by index */ + if (*cptr == '+') { + a = opr; + opr = 0; + cptr++; + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr != 'x' && *cptr != 'X') + return STOP_UUO; + cptr++; + while (*cptr >= '0' && *cptr <= '9') + idx = (idx << 4) + (*cptr++ - '0'); + if (idx >= 0x100) + return STOP_UUO; + } + + /* Comma, first was operand, now get address */ + if (*cptr == ',') { + a = 0; + cptr++; + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Collect second argument if there is one */ + while (*cptr >= '0' && *cptr <= '9') + a = (a << 4) + (*cptr++ - '0'); + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr == '+') { + cptr++; + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr != 'x' && *cptr != 'X') + return STOP_UUO; + cptr++; + while (*cptr >= '0' && *cptr <= '9') + idx = (idx << 4) + (*cptr++ - '0'); + if (idx >= 0x100) + return STOP_UUO; + } + } + + /* If we get a (, then grab field spec */ + if (*cptr == '(') { + if (op->type != TYPE_A) + return STOP_UUO; + cptr++; + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Collect second argument if there is one */ + if (*cptr >= '0' && *cptr <= '9') + opr = *cptr++ - '0'; + else + return STOP_UUO; + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr == ',') { + /* Skip blanks */ + cptr++; + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Collect second argument if there is one */ + if (*cptr >= '0' && *cptr <= '9') { + opr <<= 4; + opr |= *cptr++ - '0'; + } else + return STOP_UUO; + } else if (*cptr == ')') + opr |= opr << 4; + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr++ != ')') + return STOP_UUO; + } else if (op->type == TYPE_A) + opr = 0x09; + + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + if (*cptr != '\0') + return STOP_UUO; + d |= ((t_uint64)idx) << 24; + d |= a; + switch(op->type) { + case TYPE_P1: + case TYPE_P2: + case TYPE_P3: + if (op2 == NULL) + d |= ((t_uint64)opr) << 16; + else + d |= ((t_uint64)(opr + op2->opbase)) << 16; + break; + case TYPE_A: + d |= ((t_uint64)opr) << 16; + break; + case TYPE_E: + case TYPE_F: + case TYPE_B: + break; + case TYPE_S: + case TYPE_D: + case TYPE_V: + d += ((t_uint64)opr &0xF0) << 28; + d |= ((t_uint64)opr &0x0F) << 28; + break; + case TYPE_Z: + d |= ((t_uint64)op2->opbase) << 16; + break; + case TYPE_TB: + opr <<= 4; + opr |= 1; + /* Fall through */ + case TYPE_I: + d |= ((t_uint64)opr) << 16; + break; + case TYPE_T: + if (op2->opbase & 0xf0) + d |= ((t_uint64)op2->opbase &0xF0) << 12; + else + d |= op2->opbase; + d |= ((t_uint64)opr & 0xF) << 16; + d += ((t_uint64)opr &0xF0) << 28; + break; + case TYPE_U: + case TYPE_C: + d |= ((t_uint64)opr) << 20; + d |= ((t_uint64)op2->opbase) << 16; + /* Fall through */ + case TYPE_IQ: + d |= ((t_uint64)opr) << 20; + break; + } + } else if (sw & SWMASK('C')) { + extern uint8 bcd_mem[64]; + i = 0; + while (*cptr != '\0' && i < 5) { + d <<= 8; + if (sim_ascii_to_six[0177 & *cptr] != -1) + d |= bcd_mem[(int)sim_ascii_to_six[0177 & *cptr]]; + cptr++; + i++; + } + d <<= 8 * (5 - i); + d |= ASIGN; + } else { + switch(*cptr) { + case '-': sign = -1; cptr++; break; + case '@': sign = 1; cptr++; break; + case '+': cptr++; + /* Fall through */ + default: + sign = 0; + } + while (*cptr >= '0' && *cptr <= '9') { + d <<= 4; + d |= *cptr++ - '0'; + } + d &= DMASK; + switch (sign) { + case 1: d |= ASIGN; break; + case 0: d |= PSIGN; break; + case -1: d |= MSIGN; break; + } + } + *val = d; + return SCPE_OK; +} diff --git a/I7000/i7080_chan.c b/I7000/i7080_chan.c new file mode 100644 index 00000000..71504542 --- /dev/null +++ b/I7000/i7080_chan.c @@ -0,0 +1,1250 @@ +/* i7080_chan.c: IBM 7080 Channel simulator + + 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. + + channel + + The channel state for the IBM 705 channel is: + 705: Polled mode transfer, unit record devices. + Each chan_cmd will transfer one record. + 7621: Basic data channel for 729 tapes. + 7908: Channel to talk to disk, hypertape and data com. + + The 705 has 4 7621 channels. + The status for these are kept in bank 2. Form as follow: + Word 3 digits 7-6: 0 + Word 3 digit 5: chan control digit. + Word 3 digits 4-0: data buffer A. + Word 2 digits 7-6: 0 + Word 2 digit 5: chan control digit. + Word 2 digits 4-0: data buffer B. + Word 1 digits 7-4: 0 + Word 1 digits 3-0: Data Memory Address SMAC. + Word 0 digits 7-4: Channel Program Status. + Word 0 digits 3-0: Record Count/Program location. + + The 705 has 2 7908 channels. + The status for these are kept in bank 4. Form as follow: + Word 3 digits 7-0: 0 + Word 2 digits 7-0: 0 + Word 1 digits 7-4: 0 + Word 1 digits 3-0: Data Memory Address SMAC. + Word 0 digits 7-4: Channel Program Status. + Word 0 digits 3-0: Program location. + +*/ + +#include "i7080_defs.h" + +extern uint16 iotraps; +extern uint8 iocheck; +extern UNIT cpu_unit; +extern uint16 IC; +extern uint8 AC[5 * 512]; +extern int chwait; +extern uint16 selreg; +extern uint16 selreg2; +extern uint16 flags; +extern uint32 MAC2; +extern uint16 irqflags; +extern uint8 ioflags[5000/8]; + +#define UNIT_V_MOD (UNIT_V_UF + 4) +#define UNIT_V_HS (UNIT_V_MOD + 1) +#define CHAN_MOD (1 << UNIT_V_MOD) +#define CHAN_HS (1 << UNIT_V_HS) + + + +t_stat set_chan_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat chan_reset(DEVICE * dptr); +t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *chan_description (DEVICE *dptr); + + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +uint32 caddr[NUM_CHAN]; /* Channel memory address */ +uint8 bcnt[NUM_CHAN]; /* Channel character count */ +uint16 cmd[NUM_CHAN]; /* Current command */ +uint16 irqdev[NUM_CHAN]; /* Device to generate interupts + for channel */ +uint32 assembly[NUM_CHAN]; /* Assembly register */ +uint32 chan_flags[NUM_CHAN]; /* Unit status */ +extern uint8 inquiry; + + +#define READ_WRD 1 +#define WRITE_WRD 2 + +const char *chan_type_name[] = { + "Polled", "Unit Record", "7621", "7908", "754"}; + + +UNIT chan_unit[] = { + {UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_UREC), 0)}, + /* Tape devices */ + {UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 0}, /* 20 */ + {UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 1}, /* 21 */ + {UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 2}, /* 22 */ + {UDATA(NULL, CHAN_MOD|CHAN_SET|CHAN_S_TYPE(CHAN_7621), 0), 0, 3}, /* 23 */ + /* 7080 High speed data channels */ + {UDATA(NULL, CHAN_HS | CHAN_SET | CHAN_S_TYPE(CHAN_7908), 0), 0, 0}, /* 40 */ + {UDATA(NULL, CHAN_HS | CHAN_SET | CHAN_S_TYPE(CHAN_7908), 0), 0, 1}, /* 41 */ + {UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 4}, /* 44 */ + {UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 5}, /* 45 */ + {UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 6}, /* 46 */ + {UDATA(NULL, CHAN_SET | CHAN_S_TYPE(CHAN_7621), 0), 0, 7}, /* 47 */ +}; + +REG chan_reg[] = { + {BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO}, + {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO}, + {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO}, + {NULL} +}; + +MTAB chan_mod[] = { + {CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", "UREC", &set_chan_type, + NULL,NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_754), "754", "754", &set_chan_type, + NULL,NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7621), "7621", "7621", &set_chan_type, + NULL,NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7908), "7908", NULL, NULL,NULL,NULL}, + {CHAN_HS, CHAN_HS, "HS", "HS", NULL,NULL,NULL}, + {MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL}, + {0} +}; + + +/* Simulator debug controls */ +DEBTAB chn_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {"CH0", 0x0100 << 0}, + {"CH20", 0x0100 << 1}, + {"CH21", 0x0100 << 2}, + {"CH22", 0x0100 << 3}, + {"CH23", 0x0100 << 4}, + {"CH40", 0x0100 << 5}, + {"CH41", 0x0100 << 6}, + {"CH44", 0x0100 << 7}, + {"CH45", 0x0100 << 8}, + {"CH46", 0x0100 << 9}, + {"CH47", 0x0100 << 10}, + {0, 0} +}; + +DEVICE chan_dev = { + "CH", chan_unit, chan_reg, chan_mod, + NUM_CHAN, 8, 15, 1, 8, 36, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, chn_debug, + NULL, NULL, &chan_help, NULL, NULL, &chan_description +}; + + +t_stat +set_chan_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) { + if ((uptr->flags & CHAN_MOD) == 0) + return SCPE_ARG; + uptr->flags &= ~CHAN_MODEL; + uptr->flags |= val; + return SCPE_OK; +} + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + chan_flags[i] = 0; + caddr[i] = 0; + cmd[i] = 0; + bcnt[i] = 0; + } + return chan_set_devs(dptr); +} + +/* Map device to channel */ +int +chan_mapdev(uint16 dev) { + + switch((dev >> 8) & 0xff) { + case 0x02: return 1 + ((dev >> 4) & 0xf); /* Map tapes to 20-23 */ + case 0x20: + if (CHAN_G_TYPE(chan_unit[1].flags) == CHAN_754) + return -1; + return 1; /* Channel 20 */ + case 0x21: + if (CHAN_G_TYPE(chan_unit[2].flags) == CHAN_754) + return -1; + return 2; /* Channel 21 */ + case 0x22: + if (CHAN_G_TYPE(chan_unit[3].flags) == CHAN_754) + return -1; + return 3; /* Channel 22 */ + case 0x23: + if (CHAN_G_TYPE(chan_unit[4].flags) == CHAN_754) + return -1; + return 4; /* Channel 23 */ + case 0x40: return 5; /* HS Channel 40 */ + case 0x41: return 6; /* HS Channel 41 */ + case 0x44: return 7; /* Channel 44 */ + case 0x45: return 8; /* Channel 45 */ + case 0x46: return 9; /* Channel 46 */ + case 0x47: return 10; /* Channel 47 */ + default: + if (dev > 0x2000) /* Invalid if over 2000 and not selected */ + return -1; + return 0; + } +} + +/* Boot from given device */ +t_stat +chan_boot(int32 unit_num, DEVICE * dptr) +{ + /* Set IAR = 1 (done by reset), channel to read one + record to location 1 */ + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags); + extern t_stat cpu_reset(DEVICE *); + + cpu_reset(&cpu_dev); + selreg = ((DIB *) dptr->ctxt)->addr + unit_num; + chwait = chan + 1; /* Force wait for channel */ + chan_flags[chan] |= STA_ACTIVE; + chan_flags[chan] &= ~STA_PEND; + cmd[chan] = 0; + caddr[chan] = 0; + return SCPE_OK; +} + +t_stat +chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) { + DEVICE **dptr; + DIB *dibp; + unsigned int j; + UNIT *uptr; + + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int r; + + dibp = (DIB *) (*dptr)->ctxt; + /* If no DIB, not channel device */ + if (dibp == 0) + continue; + uptr = (*dptr)->units; + /* If this is a 7907 device, check it */ + if (dibp->ctype & CH_TYP_79XX) { + for (j = 0; j < (*dptr)->numunits; j++, uptr++) { + if (UNIT_G_CHAN(uptr->flags) == chan && + (UNIT_SELECT & uptr->flags) == 0 && + (dibp->addr & dibp->mask) == (dev & dibp->mask)) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + /* Handle 7621 DS units */ + } else if (dibp->ctype & CH_TYP_76XX && + (UNIT_G_CHAN(uptr->flags) == chan)) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + /* Handle 754 and unit record devices */ + } else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) { + if (dibp->upc == 1) { + for (j = 0; j < (*dptr)->numunits; j++) { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + uptr++; + } + } else { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) + return r; + } + } + } + } + return SCPE_NODEV; +} + +/* Decrement the record count for a given channel, return 1 when + no more records to send */ +int chan_decr_reccnt(int chan) { + int unit; + + unit = 512 + chan_unit[chan].u3 * 32; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt %02o %02o %02o\n", + chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]); + if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10) + return 1; + if (AC[unit + 1] != 10) { + AC[unit + 1]--; + if (AC[unit + 1] == 0) + AC[unit + 1] = 10; + } else { + AC[unit + 1] = 9; + if (AC[unit + 2] != 10) { + AC[unit + 2]--; + if (AC[unit + 2] == 0) + AC[unit + 2] = 10; + } else { + AC[unit + 2] = 9; + if (AC[unit + 3] != 10) { + AC[unit + 3]--; + if (AC[unit + 3] == 0) + AC[unit + 3] = 10; + } + } + } + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt- %02o %02o %02o\n", + chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]); + if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10) + return 1; + return 0; +} + +/* Return true if record count is zero */ +int chan_zero_reccnt(int chan) { + int unit; + + unit = 512 + chan_unit[chan].u3 * 32; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d reccnt %02o %02o %02o\n", + chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]); + if (AC[unit + 1] == 10 && AC[unit + 2] == 10 && AC[unit + 3] == 10) + return 1; + return 0; +} + +/* Return next channel data address, advance address by 5 if channel */ +uint32 chan_next_addr(int chan) { + int unit = 0; + uint32 addr = 0; + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + return ++caddr[chan]; + + case CHAN_7621: + unit = 8 + 512 + chan_unit[chan].u3 * 32; + break; + case CHAN_7908: + unit = 8 + 1024 + chan_unit[chan].u3 * 32; + break; + } + addr = load_addr(unit); + store_addr(addr + 5, unit); + return addr; +} + +/* Execute the next channel instruction. */ +void +chan_proc() +{ + int chan; + int cmask; + int unit; + uint32 addr; + + /* Scan channels looking for work */ + for (chan = 0; chan < NUM_CHAN; chan++) { + /* Skip if channel is disabled */ + if (chan_unit[chan].flags & UNIT_DIS) + continue; + + /* If channel is disconnecting, do nothing */ + if (chan_flags[chan] & DEV_DISCO) + continue; + cmask = 0x0100 << chan; + + /* Check if RWW pending */ + if (chan_flags[chan] & STA_PEND) { + /* If pending issue read command */ + chan_flags[chan] &= ~STA_PEND; + if (selreg2 & 0x8000) { + int chan2; + /* Find device on given channel and give it the command */ + chan2 = chan_mapdev(selreg2 & 0x7fff); + if (chan2 < 0 || chan2 >= NUM_CHAN) + continue; + /* If no channel device, quick exit */ + if (chan_unit[chan2].flags & UNIT_DIS || + CHAN_G_TYPE(chan_unit[chan2].flags) != CHAN_754) { + flags |= 0x440; /* Set I/O Check */ + selreg2 = 0; + continue; + } + + /* Channel is busy doing something, wait */ + if (chan_flags[chan2] & (DEV_SEL| DEV_DISCO|STA_TWAIT|STA_WAIT| + STA_ACTIVE)) { + chan_flags[chan] |= STA_PEND; + /* Try again */ + continue; + } + + /* Issue another command */ + switch(chan_issue_cmd(chan2, IO_RDS, selreg2 & 0x7fff)) { + case SCPE_BUSY: /* Try again */ + chan_flags[chan] |= STA_PEND; + break; + case SCPE_NODEV: + case SCPE_IOERR: + /* Something wrong, stop */ + flags |= 0x440; /* Set I/O Check */ + selreg2 = 0; + break; + case SCPE_OK: + chan_flags[chan2] |= STA_ACTIVE; + selreg2 &= 0x7fff; + chwait = chan2+1; /* Change wait channel */ + break; + } + } else { + /* No pending, just store last address in MAC2 */ + MAC2 = caddr[chan]; + selreg2 = 0; + } + continue; + } + + /* If channel not active, don't process anything */ + if ((chan_flags[chan] & STA_ACTIVE) == 0) + continue; + + if ((chan_flags[chan] & (CTL_READ|CTL_WRITE)) && + (chan_flags[chan] & (CTL_END|SNS_UEND))) { + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_WEOR|DEV_DISCO; + chan_flags[chan] &= ~(SNS_UEND|CTL_END|CTL_READ|CTL_WRITE); + } + + /* If device requested attention, abort current command */ + if (chan_flags[chan] & CHS_ATTN) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn %d\n", + chan, irqdev[chan]); + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_UREC: + case CHAN_754: + if (selreg2 != 0) + chan_flags[chan] |= STA_PEND; + if (chan_flags[chan] & CHS_ERR) + flags |= 0x40; /* Set I/O Check */ + break; + case CHAN_7621: + case CHAN_7908: + irqflags |= 1 << chan; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d IRQ %x\n", + chan, irqdev[chan]); + break; + } + chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT|DEV_WRITE); + cmd[chan] &= ~CHAN_RECCNT; + unit = irqdev[chan]; + if (chan_flags[chan] & CHS_EOF) + ioflags[unit/8] |= (1 << (unit & 07)); + flags |= 0x400; /* Set Any flag */ + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + continue; + } + + /* If channel action all done, finish operation */ + if (((chan_flags[chan] & (DEV_SEL|STA_ACTIVE|STA_WAIT)) == STA_ACTIVE) + && (chan_flags[chan] & (CTL_CNTL|CTL_PREAD|CTL_PWRITE|CTL_READ| + CTL_WRITE|CTL_SNS)) == 0) { + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_UREC: + case CHAN_754: + if (selreg2 != 0) + chan_flags[chan] |= STA_PEND; + break; + case CHAN_7621: + case CHAN_7908: + irqflags |= 1 << chan; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d IRQ %x\n", + chan, irqdev[chan]); + break; + } + chan_flags[chan] &= ~(STA_ACTIVE|DEV_WRITE); + if (chan_flags[chan] & CHS_EOF) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOF %x\n", + chan, irqdev[chan]); + unit = irqdev[chan]; + ioflags[unit/8] |= (1 << (unit & 07)); + chan_flags[chan] &= ~CHS_EOF; + chan_flags[chan] |= CHS_ERR; + flags |= 0x400; /* Set Any flag */ + } + continue; + } + + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_UREC: + case CHAN_754: + /* If device put up EOR, terminate transfer. */ + if (chan_flags[chan] & (DEV_REOR|DEV_WEOR)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR\n", + chan); + if (selreg2 != 0) + chan_flags[chan] |= STA_PEND; + chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT|DEV_WRITE|DEV_REOR); + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + continue; + } + break; + + case CHAN_7621: + /* Waiting on unit ready, or command */ + if (chan_flags[chan] & STA_WAIT) { + if ((chan_flags[chan] & STA_TWAIT) == 0) { + /* Device ready, see if command under record count */ + if (cmd[chan] & CHAN_CMD) { + /* Done if EOF set */ + if (chan_flags[chan] & CHS_EOF) { + cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD); + chan_flags[chan] &= ~(STA_WAIT); + continue; /* On to next channel */ + } + + /* Issue another command */ + switch(chan_issue_cmd(chan, 0xff & (cmd[chan] >> 9), + irqdev[chan])) { + case SCPE_BUSY: + continue; + case SCPE_OK: + /* If started, drop record count */ + if (chan_decr_reccnt(chan)){ + cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD); + chan_flags[chan] &= ~(STA_WAIT); + continue; /* On to next channel */ + } + continue; + case SCPE_NODEV: + case SCPE_IOERR: + /* Something wrong, stop */ + cmd[chan] &= ~(CHAN_RECCNT|CHAN_CMD); + break; + } + continue; /* On to next channel */ + } + chan_flags[chan] &= ~(STA_WAIT); + } + continue; /* On to next channel */ + } + + /* If device put up EOR, terminate transfer. */ + if (chan_flags[chan] & DEV_REOR) { + int ch; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR\n", + chan); + /* If reading, check if partial word read */ + if ((chan_flags[chan] & DEV_WRITE) == 0) { + unit = 512 + chan_unit[chan].u3 * 32; + unit += (cmd[chan] & CHAN_BFLAG)? 16: 24; + ch = AC[unit + 5]; + if (ch != 10) { + /* Yes, fill with group marks and mark as full */ + while(ch < 5) + AC[unit+ch++] = CHR_GM; + cmd[chan] |= (cmd[chan] & CHAN_BFLAG)? + CHAN_BFULL: CHAN_AFULL; + } + } + if (cmd[chan] & CHAN_RECCNT) { + if (!chan_decr_reccnt(chan)) { + chan_flags[chan] &= ~DEV_REOR; + continue; + } + cmd[chan] &= ~CHAN_RECCNT; + } + chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR); + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + } + + /* Channel gave us a Write EOR, terminate it needed. */ + if (chan_flags[chan] & DEV_WEOR) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d WEOR\n", + chan); + if (cmd[chan] & CHAN_RECCNT) { + if (!chan_decr_reccnt(chan)) { + chan_flags[chan] &= ~DEV_WEOR; + cmd[chan] &= ~CHAN_END; + continue; + } + cmd[chan] &= ~CHAN_RECCNT; + } + chan_flags[chan] &= ~(DEV_WEOR|DEV_REOR); + /* Disconnect if selected */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + } + + if ((chan_flags[chan] & DEV_WRITE) && + (cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) != + (CHAN_AFULL|CHAN_BFULL)) { + char ch; + unit = 512 + chan_unit[chan].u3 * 32; + if (cmd[chan] & CHAN_END) + break; + addr = chan_next_addr(chan); + if ((cmd[chan] & CHAN_AFULL) == 0) { + unit += 24; + cmd[chan] |= CHAN_AFULL; + ch = 'a'; + } else if ((cmd[chan] & CHAN_BFULL) == 0) { + unit += 16; + cmd[chan] |= CHAN_BFULL; + ch = 'b'; + } else { + break; + } + AC[unit] = M[addr++]; + AC[unit+1] = M[addr++]; + AC[unit+2] = M[addr++]; + AC[unit+3] = M[addr++]; + AC[unit+4] = M[addr++]; + AC[unit+5] = 10; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d (%d) > %c %02o%02o%02o%02o%02o\n", + chan, addr-5, ch, + AC[unit], AC[unit+1], AC[unit+2], AC[unit+3], + AC[unit+4]); + + if (cmd[chan] & CHAN_NOREC && (addr % 20000) == 0) + cmd[chan] |= CHAN_END; + /* Wrap around if over end of memory, set error */ + if (addr > EMEMSIZE) { + chan_flags[chan] |= CHS_ERR; + /* addr -= EMEMSIZE; */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "write wrap %d\n", chan); + } + break; + } + while ((chan_flags[chan] & DEV_WRITE) == 0&& + (cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) != 0) { + char ch; + unit = 512 + chan_unit[chan].u3 * 32; + addr = chan_next_addr(chan); + if (cmd[chan] & CHAN_AFULL) { + unit += 24; + cmd[chan] &= ~CHAN_AFULL; + ch = 'a'; + } else if (cmd[chan] & CHAN_BFULL) { + unit += 16; + cmd[chan] &= ~CHAN_BFULL; + ch = 'b'; + } else { + break; + } + if ((cmd[chan]& CHAN_SKIP) == 0) { + M[addr++] = AC[unit]; + M[addr++] = AC[unit+1]; + M[addr++] = AC[unit+2]; + M[addr++] = AC[unit+3]; + M[addr++] = AC[unit+4]; + if (addr > EMEMSIZE) { + cmd[chan] |= CHAN_SKIP; + chan_flags[chan] |= CHS_ERR; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_EXP, &chan_dev, "read wrap %d\n", chan); + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d (%d) < %c %02o%02o%02o%02o%02o\n", + chan, addr-5, ch, + AC[unit], AC[unit+1], AC[unit+2], AC[unit+3], + AC[unit+4]); + } + AC[unit+5] = 10; + } + break; + case CHAN_7908: + switch(chan_flags[chan] & (DEV_WRITE|DEV_FULL)) { + case 0: + case DEV_WRITE|DEV_FULL: + continue; + case DEV_WRITE: + if (cmd[chan] & CHAN_END) + break; + unit = 1024 + chan_unit[chan].u3 * 32; + addr = chan_next_addr(chan); + assembly[chan] = M[addr++] & 077; + assembly[chan] |= (M[addr++] & 077) << 6; + assembly[chan] |= (M[addr++] & 077) << 12; + assembly[chan] |= (M[addr++] & 077) << 18; + assembly[chan] |= (M[addr++] & 077) << 24; + if (cmd[chan] & CHAN_NOREC && (addr % 20000) == 19999) + cmd[chan] |= CHAN_END; + bcnt[chan] = 0; + chan_flags[chan] |= DEV_FULL; + break; + case DEV_FULL: + unit = 1024 + chan_unit[chan].u3 * 32; + addr = chan_next_addr(chan); + if ((cmd[chan]& CHAN_SKIP) == 0) { + M[addr++] = assembly[chan] & 077; + M[addr++] = (assembly[chan] >> 6) & 077; + M[addr++] = (assembly[chan] >> 12) & 077; + M[addr++] = (assembly[chan] >> 18) & 077; + M[addr++] = (assembly[chan] >> 24) & 077; + } + if (addr > EMEMSIZE) { + cmd[chan] |= CHAN_SKIP; + chan_flags[chan] |= CHS_ATTN; + } + bcnt[chan] = 0; + chan_flags[chan] &= ~DEV_FULL; + break; + } + break; + } + } +} + +void chan_set_attn_inq(int chan) { +/* inquiry = 1; */ +} + +void chan_clear_attn_inq(int chan) { +/* inquiry = 0; */ +} + + +/* Issue a command to a channel */ +int +chan_cmd(uint16 dev, uint16 dcmd, uint32 addr) +{ + int chan; + int unit; + t_stat r; + int op; + + /* Find device on given channel and give it the command */ + chan = chan_mapdev(dev); + if (chan < 0 || chan >= NUM_CHAN) + return SCPE_IOERR; + /* If no channel device, quick exit */ + if (chan_unit[chan].flags & UNIT_DIS) + return SCPE_IOERR; + /* Unit is busy doing something, wait */ + if (chan_flags[chan] & (DEV_SEL| DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE)) + return SCPE_BUSY; + + /* Ok, try and find the unit */ + caddr[chan] = addr; + assembly[chan] = 0; + op = dcmd >> 8; + cmd[chan] &= CHAN_RECCNT; /* Clear everything but record count flag */ + cmd[chan] |= dcmd & CHAN_ZERO; + if (op == IO_RDS && (dcmd & 0xf) != 0) { + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + cmd[chan] |= CHAN_SKIP; + break; + case CHAN_7621: + switch(dcmd & 0xf) { + case 1: cmd[chan] |= CHAN_SKIP; break; + case 2: + unit = 8+512 + chan_unit[chan].u3 * 32; + M[addr++] = AC[unit++]; + M[addr++] = AC[unit++]; + M[addr++] = AC[unit++]; + M[addr++] = AC[unit++]; + cmd[chan] &= ~CHAN_RECCNT; + return SCPE_OK; + default: + break; + } + break; + case CHAN_7908: + switch(dcmd & 0xf) { + case 1: cmd[chan] |= CHAN_SKIP; + case 0: chan_flags[chan] |= CTL_READ; break; + case 3: chan_flags[chan] |= CTL_SNS; break; /* Do Sense */ + case 4: /* Do control then read */ + chan_flags[chan] |= CTL_CNTL|CTL_PREAD; + break; + } + break; + } + } + if (op == IO_WRS && (dcmd & 0xf) != 0) { + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + cmd[chan] |= CHAN_NOREC; + break; + case CHAN_7621: + dcmd &= ~ CHAN_ZERO; + switch(dcmd & 0xf) { + case 1: cmd[chan] |= CHAN_NOREC; break; + case 2: + unit = 512 + chan_unit[chan].u3 * 32; + addr /= 10; + AC[unit+1] = addr % 10; + if (AC[unit+1] == 0) + AC[unit+1] = 10; + addr /= 10; + AC[unit+2] = addr % 10; + addr /= 10; + if (AC[unit+2] == 0) + AC[unit+2] = 10; + AC[unit+3] = addr % 10; + if (AC[unit+3] == 0) + AC[unit+3] = 10; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d set reccnt %02o %02o %02o\n", + chan, AC[unit + 3], AC[unit + 2], AC[unit + 1]); + cmd[chan] |= CHAN_RECCNT; + return SCPE_OK; + default: + break; + } + break; + case CHAN_7908: + switch(dcmd & 0xf) { + case 1: cmd[chan] |= CHAN_NOREC; + case 0: chan_flags[chan] |= CTL_WRITE; break; + case 3: /* Do control */ + chan_flags[chan] |= CTL_CNTL; break; + case 4: /* Do control then write */ + chan_flags[chan] |= CTL_CNTL|CTL_PWRITE; break; + } + break; + } + } + /* Handle initial record count of zero for special ops */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7621 && + cmd[chan] & CHAN_RECCNT) { + switch(op) { + case IO_WEF: + case IO_ERG: + case IO_BSR: + if (chan_zero_reccnt(chan)) { + /* Just check if unit ready */ + r = chan_issue_cmd(chan, OP_TRS, dev); + if (r == SCPE_OK) + cmd[chan] &= ~CHAN_RECCNT; + return r; + } + break; + default: /* Don't worry about it, not effected by RC */ + break; + } + } + + chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE|SNS_UEND|CTL_WRITE| + CTL_SNS|CHS_ATTN); + + r = chan_issue_cmd(chan, op, dev); + if (r == SCPE_OK) + chan_flags[chan] &= ~(CHS_EOF|CHS_ERR|CHS_ATTN); + /* Activate channel if select raised */ + if (r == SCPE_OK && chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= STA_ACTIVE; + irqdev[chan] = dev; + irqflags &= ~(1 << chan); + ioflags[dev/8] &= ~(1 << (dev & 07)); + /* Set starting address. */ + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + if (op == IO_RDS) { + if (selreg2 & 0x8000) + caddr[chan] = MAC2; + selreg2 &= 0x7fff; + } + chwait = chan+1; + break; + case CHAN_7621: + unit = 512 + chan_unit[chan].u3 * 32; + AC[unit+16+5] = 10; /* Set digit next to 0 */ + AC[unit+24+5] = 10; + store_addr(caddr[chan], 8 + unit); + if (cmd[chan] & CHAN_RECCNT && chan_zero_reccnt(chan)) { + cmd[chan] &= ~CHAN_RECCNT; + } + break; + case CHAN_7908: + store_addr(caddr[chan], 8+1024 + chan_unit[chan].u3 * 32); + break; + } + } + if (r == SCPE_OK && CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7621) { + switch(op) { + case IO_WEF: + case IO_ERG: + case IO_BSR: + if (cmd[chan] & CHAN_RECCNT && chan_zero_reccnt(chan)) { + cmd[chan] &= ~CHAN_RECCNT; + } + if (cmd[chan] & CHAN_RECCNT) { + chan_decr_reccnt(chan); + cmd[chan] &= CHAN_RECCNT; + cmd[chan] |= (op << 9) | CHAN_CMD; + } + /* Fall through */ + + case IO_SKR: + case IO_BSF: + case IO_REW: + case IO_RUN: + chan_flags[chan] |= STA_ACTIVE|STA_WAIT; + irqdev[chan] = dev; + irqflags &= ~(1 << chan); + ioflags[dev/8] &= ~(1 << (dev & 07)); + default: + break; + } + } + return r; +} + +/* + * Process the CHR 3 13 command and abort all channel activity + */ +void +chan_chr_13() +{ + int chan; + + /* Scan channels looking for work */ + for (chan = 0; chan < NUM_CHAN; chan++) { + /* Skip if channel is disabled */ + if (chan_unit[chan].flags & UNIT_DIS) + continue; + + /* If channel is disconnecting, do nothing */ + if (chan_flags[chan] & DEV_DISCO) + continue; + + /* If channel not active, don't process anything */ + if ((chan_flags[chan] & STA_ACTIVE) == 0) + continue; + + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_WEOR|DEV_DISCO; + + chan_flags[chan] &= ~(CHS_ATTN|STA_ACTIVE|STA_WAIT); + } + + irqflags = 0; +} + + +/* + * Write a word to the assembly register. + */ +int +chan_write(int chan, t_uint64 * data, int flags) +{ + return TIME_ERROR; +} + +/* + * Read next word from assembly register. + */ +int +chan_read(int chan, t_uint64 * data, int flags) +{ + return TIME_ERROR; +} + +/* + * Write a char to the assembly register. + */ +int +chan_write_char(int chan, uint8 * data, int flags) +{ + uint8 ch = *data; + int unit; + uint16 msk; + + /* Based on channel type get next character */ + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + if (*data == 0) + *data = 020; + if (caddr[chan] > EMEMSIZE) { + cmd[chan] |= CHAN_SKIP; + chan_flags[chan] |= CHS_ATTN; + } + if ((cmd[chan] & CHAN_SKIP) == 0) + M[caddr[chan]] = *data; + caddr[chan]++; + break; + case CHAN_7621: + if (*data == 0) + *data = 020; + /* If no data, then return timing error */ + if ((cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) == (CHAN_AFULL|CHAN_BFULL)) + return TIME_ERROR; + unit = 512 + chan_unit[chan].u3 * 32; + if ((cmd[chan] & CHAN_BFLAG) == 0 && (cmd[chan] & CHAN_AFULL) == 0) { + unit += 24; + msk = CHAN_AFULL; + } else if ((cmd[chan] & CHAN_BFLAG) && (cmd[chan] & CHAN_BFULL) == 0) { + unit += 16; + msk = CHAN_BFULL; + } else { /* We are off sync, or something */ + /* We have data, so switch CHAN_BFLAG and try other buffer */ + cmd[chan] ^= CHAN_BFLAG; + unit += (cmd[chan] & CHAN_BFLAG)? 16: 24; + msk = (cmd[chan] & CHAN_BFLAG)? CHAN_BFULL: CHAN_AFULL; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, + "switching buffer %d\n", chan); + } + ch = AC[5 + unit]; + if (ch == 10) + ch = 0; + AC[unit + ch] = *data & 077; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DATA, &chan_dev, + "%d < %02o (%d)\n", chan, *data, ch); + AC[5 + unit] = ch + 1; + if (ch == 4) { + cmd[chan] |= msk; + cmd[chan] ^= CHAN_BFLAG; + } + break; + case CHAN_7908: + if (bcnt[chan] > 4) + return TIME_ERROR; + if (chan_flags[chan] & CTL_SNS) { + *data &= 027; + *data |= 040; + } else { + if (*data == 0) + *data = 020; + } + assembly[chan] |= *data << (6 * bcnt[chan]); + bcnt[chan]++; + if (bcnt[chan] == 5) + chan_flags[chan] |= DEV_FULL; + } + + + /* If device gave us an end, terminate transfer */ + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_REOR|DEV_FULL; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + return END_RECORD; + /* If over size of memory, terminate */ + } else if (!MEM_ADDR_OK(caddr[chan])) { + chan_flags[chan] |= DEV_REOR|DEV_FULL; + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + return END_RECORD; + } + + return DATA_OK; +} + +/* + * Read next char from assembly register. + */ +int +chan_read_char(int chan, uint8 * data, int flags) +{ + uint8 ch; + int unit; + uint16 msk; + + /* Check if he write out last data */ + if ((chan_flags[chan] & STA_ACTIVE) == 0) + return TIME_ERROR; + + /* Based on channel type get next character */ + switch(CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_754: + case CHAN_UREC: + *data = M[caddr[chan]]; + if (*data == CHR_BLANK) + *data = CHR_ABLANK; + if (cmd[chan] & CHAN_ZERO && *data != CHR_GM) + M[caddr[chan]] = CHR_BLANK; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DATA, &chan_dev, + "%d > %02o (%d)\n", chan, *data, *data); + caddr[chan]++; + if ((cmd[chan] & CHAN_NOREC && (caddr[chan] % 19999) == 0)) { + chan_flags[chan] |= DEV_WEOR; + return END_RECORD; + } + break; + case CHAN_7621: + /* If no data, then return timing error */ + if ((cmd[chan] & (CHAN_AFULL|CHAN_BFULL)) == 0) { + if (cmd[chan] & CHAN_END) { + chan_flags[chan] |= DEV_WEOR; + return END_RECORD; + } + return TIME_ERROR; + } + unit = 512 + chan_unit[chan].u3 * 32; + if ((cmd[chan] & CHAN_BFLAG) == 0 && (cmd[chan] & CHAN_AFULL)) { + unit += 24; + msk = ~CHAN_AFULL; + } else if ((cmd[chan] & CHAN_BFLAG) && (cmd[chan] & CHAN_BFULL)) { + unit += 16; + msk = ~CHAN_BFULL; + } else { /* We are off sync, or something */ + /* We have data, so switch CHAN_BFLAG and try other buffer */ + cmd[chan] ^= CHAN_BFLAG; + unit += (cmd[chan] & CHAN_BFLAG)? 16: 24; + msk = (cmd[chan] & CHAN_BFLAG)? ~CHAN_BFULL: ~CHAN_AFULL; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DETAIL, &chan_dev, + "switching buffer %d\n", chan); + } + ch = AC[5 + unit]; + if (ch == 10) + ch = 0; + *data = AC[unit + ch] & 077; + if (*data == CHR_BLANK) + *data = CHR_ABLANK; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_DATA, &chan_dev, + "%d > %02o (%d)\n", chan, *data, ch); + AC[5 + unit] = ch + 1; + if (ch == 4) { + cmd[chan] &= msk; + cmd[chan] ^= CHAN_BFLAG; + } + break; + case CHAN_7908: + if (bcnt[chan] > 4) + return TIME_ERROR; + *data = assembly[chan] >> (6 * bcnt[chan]); + if (*data == CHR_BLANK) + *data = CHR_ABLANK; + if (chan_flags[chan] & CTL_CNTL && *data == CHR_GM) { + chan_flags[chan] |= (chan_flags[chan] & (CTL_PREAD|CTL_PWRITE)) << 2; + chan_flags[chan] &= ~(CTL_CNTL|CTL_PREAD|CTL_PWRITE); + if (chan_flags[chan] & CTL_READ) { + chan_next_addr(chan); + chan_next_addr(chan); + } + bcnt[chan] = 0; + return END_RECORD; + } + + bcnt[chan]++; + if (bcnt[chan] == 5) + chan_flags[chan] &= ~DEV_FULL; + } + + /* Check if we hit group mark */ + if ((cmd[chan] & CHAN_NOREC) == 0 && *data == CHR_GM) { + chan_flags[chan] |= DEV_WEOR; + return END_RECORD; + } + + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE/*|STA_ACTIVE*/); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] |= DEV_REOR; + return TIME_ERROR; + } else + chan_flags[chan] |= DEV_WRITE; + return DATA_OK; +} + + +void +chan9_set_error(int chan, uint32 mask) +{ + if (chan_flags[chan] & mask) + return; + chan_flags[chan] |= mask; +} + +t_stat +chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "%s\n", chan_description(dptr)); + fprintf(st, "The 7080 supports up to 10 channels. Channel 0 is for unit\n"); + fprintf(st, "record devices. Channels 1 through 4 are for tape drives.\n\n"); + fprintf (st, " 7261 tapes on Data Synchronizer\n"); + fprintf (st, " 754 Standard 705 tape drives\n\n"); + fprintf (st, "Channels are fixed on the 7080.\n\n"); + fprintf (st, "Channel * is a puesdo channel for unit record devices.\n"); + + fprintf(st, "\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +chan_description(DEVICE *dptr) +{ + return "IBM 7080 channel controller"; +} + + diff --git a/I7000/i7080_cpu.c b/I7000/i7080_cpu.c new file mode 100644 index 00000000..7d7f802d --- /dev/null +++ b/I7000/i7080_cpu.c @@ -0,0 +1,3426 @@ +/* i7080_cpu.c: IBM 7080 CPU simulator + + Copyright (c) 2006-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. + + cpu 705 central processor + + The system state for the IBM 705 is: + + IC<0:15> program counter + SW<0:6> sense switches + AC<0:6>[0:512] AC + + The 705 has one instruction format. + + Char + 1 2 3 4 5 + opc addh add add addl + + + This routine is the instruction decode routine for the 705. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until a stop condition occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + illegal instruction + illegal I/O operation for device + breakpoint encountered + divide check + I/O error in I/O simulator + + 2. Arithmetic. The 705 uses decimal arithmetic. + + 4. Adding I/O devices. These modules must be modified: + + i705_defs.h add device definitions + i705_sys.c add sim_devices table entry +*/ + +#include "i7080_defs.h" +#include "sim_card.h" +#include + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) +#define UNIT_MSIZE (017 << UNIT_V_MSIZE) +#define UNIT_V_CPUMODEL (UNIT_V_UF + 4) +#define UNIT_MODEL (0x3 << UNIT_V_CPUMODEL) +#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_CPUMODEL) & 0x3) +#define MODEL(x) (x << UNIT_V_CPUMODEL) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define UNIT_EMU (UNIT_V_CPUMODEL + 2) +#define EMULATE3 (1 << UNIT_EMU) +#define EMULATE2 (2 << UNIT_EMU) +#define UNIT_V_NONSTOP (UNIT_EMU + 2) +#define NONSTOP (1 << UNIT_V_NONSTOP) + +#define CPU_702 0x0 +#define CPU_705 0x1 +#define CPU_7053 0x2 +#define CPU_7080 0x3 + +#define HIST_XCT 1 /* instruction */ +#define HIST_INT 2 /* interrupt cycle */ +#define HIST_TRP 3 /* trap cycle */ +#define HIST_MIN 64 +#define HIST_MAX 65536 +#define HIST_NOEA 0x40000000 +#define HIST_PC 0x80000 + +struct InstHistory +{ + uint32 ic; + uint32 ea; + uint32 inst; + uint8 reg; + uint8 op; + uint16 flags; + uint8 store[256]; +}; + +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + +uint32 read_addr(uint8 *reg, uint8 *zone); +void write_addr(uint32 addr, uint8 reg, uint8 zone); +uint32 load_addr(int loc); +void store_addr(uint32 addr, int loc); +void store_cpu(uint32 addr, int full); +void load_cpu(uint32 addr, int full); +uint16 get_acstart(uint8 reg); +t_stat do_addsub(int mode, int reg, int smt, uint16 fmsk); +t_stat do_mult(int reg, uint16 fmsk); +t_stat do_divide(int reg, uint16 fmsk); +t_stat do_compare(int reg, int tluop); +void mem_init(void); + + +uint16 bstarts[16] = { + /* 1 2 3 4 5 6 7 */ + 0, 512, 528, 544, 560, 576, 592, 608, + 624, 640, 656, 672, 688, 704, 720, 736, +}; + +uint8 bcd_bin[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, + 1, 2, 3, 4, 5}; +uint8 bin_bcd[21] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; +uint32 dig2[11] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90,0 }; +uint32 dig3[11] = { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900,0 }; +uint32 dig4[11] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,0}; +uint32 dig_zone[16] = {0, 10000, 20000, 30000, + 80000, 90000, 100000, 110000, + 40000, 50000, 60000, 70000, + 120000, 130000, 140000, 150000 + }; +uint8 zone_dig[16] = {0x0, 0x4, 0x8, 0xc, + 0x2, 0x6, 0xa, 0xe, + 0x1, 0x5, 0x9, 0xd, + 0x3, 0x7, 0xb, 0xf + }; + +/* Flip BA bits of low order zone for LDA */ +uint8 lda_flip[16] = {0x0, 0x1, 0x2, 0x3, + 0x8, 0x9, 0xa, 0xb, + 0x4, 0x5, 0x6, 0x7, + 0xc, 0xd, 0xe, 0xf + }; + + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ +uint8 comp_bcd[16] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 6, 5, 4, 3, 2 }; + +#define I 0x80 + +uint8 digit_addone[16] = { + 0,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x01,0x0b,0x0c,0x0d,0x0e, + 0x0f}; + +uint8 cmp_order[0100] = { + 077, 42, 43, 44, 45, 46, 47, 48, 49, 50, 41, 10, 11, 077, 077, 077, + 0, 7, 33, 34, 35, 36, 37, 38, 39, 40, 32, 8, 9, 077, 077, 077, + 4, 23, 24, 25, 26, 27, 28, 29, 30, 31, 22, 5, 6, 077, 077, 077, + 1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 12, 2, 3, 077, 077, 077 +}; + +/* Flags */ +#define ASIGN 0x0001 /* ACC Minus */ +#define BSIGN 0x0002 /* ASU Minus */ +#define AZERO 0x0004 /* ACC A Zero */ +#define BZERO 0x0008 /* ASU Zero */ +#define INSTFLAG 0x0010 /* Instruction error */ +#define MCHCHK 0x0020 /* Machine check */ +#define IOCHK 0x0040 /* I/O Check */ +#define RECCHK 0x0080 /* Record check */ +#define ACOFLAG 0x0100 /* AC Overflow flag */ +#define SGNFLAG 0x0200 /* Sign mismatch */ +#define ANYFLAG 0x0400 /* Anyflag set */ +#define EIGHTMODE 0x0800 /* 7080 mode */ +#define HIGHFLAG 0x1000 /* High comparison */ +#define LOWFLAG 0x2000 /* Low comparison */ +#define CMPFLAG 0x3000 /* Comparison flags */ + +/* If stop_flags is set to 1 (Automatic Mode) the sim stops if the flag is + set. If the stop_flags is set to 0 (Program mode) the sim continues */ + +#define SIGN (ASIGN|BSIGN) +#define ZERO (AZERO|BZERO) +#define IRQFLAGS (INSTFLAG|MCHCHK|IOCHK|RECCHK|ACOFLAG|SGNFLAG) + +uint8 M[MAXMEMSIZE] = { 0 }; /* memory */ +uint32 EMEMSIZE; /* Physical memory size */ +uint8 AC[6*256]; /* store registers */ +uint16 flags; /* Flags */ +uint16 spc; /* Reg start point */ +uint16 spcb; /* Reg start point b */ +uint32 IC; /* program counter */ +uint8 SL; /* Sense lights */ +uint32 MA; /* Memory address */ +uint32 MAC; /* Memory address */ +uint32 MAC2; /* Second memory address */ +uint8 SW = 0; /* Sense switch */ +uint8 indflag; /* Indirect flag */ +uint8 intmode; /* Interupt mode */ +uint8 intprog; /* Interupt program */ +uint16 stop_flags = 0; /* Stop on error */ +uint16 selreg; /* Last select address */ +uint16 selreg2; /* RWW select address */ +int chwait; /* Channel wait register */ +uint8 ioflags[5000/8] = {0}; /* IO Error flags */ +uint16 irqflags; /* IRQ Flags */ +uint8 lpr_chan9[NUM_CHAN]; /* Line printer Channel 9 flag */ +uint8 bkcmp = 0; /* Backwords compare */ +uint8 cpu_type; /* Current CPU type */ +int cycle_time = 45; /* Cycle time is 4.5us */ + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ +extern uint32 drum_addr; +extern UNIT chan_unit[]; +void (*sim_vm_init) (void) = &mem_init; + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + { UDATA(NULL, MODEL(CPU_7053) | MEMAMOUNT(3) | NONSTOP, MAXMEMSIZE) }; + +REG cpu_reg[] = { + {DRDATAD(IC, IC, 32, "Instruction register")}, + {"A", &AC, 8, 8, 0, 256, "A Register", NULL, REG_VMIO|REG_CIRC, 0, }, + {"ASU1", &AC[256], 8, 8, 256, 16, "ASU1 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU2", &AC[256], 8, 8, 256, 16, "ASU2 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU3", &AC[256], 8, 8, 256, 16, "ASU3 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU4", &AC[256], 8, 8, 256, 16, "ASU4 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU5", &AC[256], 8, 8, 256, 16, "ASU5 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU6", &AC[256], 8, 8, 256, 16, "ASU6 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU7", &AC[256], 8, 8, 256, 16, "ASU7 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU8", &AC[256], 8, 8, 256, 16, "ASU8 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU9", &AC[256], 8, 8, 256, 16, "ASU9 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU10", &AC[256], 8, 8, 256, 16, "ASU10 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU11", &AC[256], 8, 8, 256, 16, "ASU11 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU12", &AC[256], 8, 8, 256, 16, "ASU12 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU13", &AC[256], 8, 8, 256, 16, "ASU13 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU14", &AC[256], 8, 8, 256, 16, "ASU14 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {"ASU15", &AC[256], 8, 8, 256, 32, "ASU15 Register", NULL, REG_VMIO|REG_CIRC, 0}, + {BRDATA(SW, &SW, 2, 6, 1), REG_FIT}, + {FLDATA(SW911, SW, 0), REG_FIT}, + {FLDATA(SW912, SW, 1), REG_FIT}, + {FLDATA(SW913, SW, 2), REG_FIT}, + {FLDATA(SW914, SW, 3), REG_FIT}, + {FLDATA(SW915, SW, 4), REG_FIT}, + {FLDATA(SW916, SW, 5), REG_FIT}, + {GRDATA(STOP, stop_flags, 2, 6, 4), REG_FIT}, + {FLDATA(STOP0, stop_flags, 4), REG_FIT}, + {FLDATA(STOP1, stop_flags, 5), REG_FIT}, + {FLDATA(STOP2, stop_flags, 6), REG_FIT}, + {FLDATA(STOP3, stop_flags, 7), REG_FIT}, + {FLDATA(STOP4, stop_flags, 8), REG_FIT}, + {FLDATA(STOP5, stop_flags, 9), REG_FIT}, + {NULL} +}; + +MTAB cpu_mod[] = { + {UNIT_MODEL, MODEL(CPU_702), "702", "702", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(CPU_705), "705", "705", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(CPU_7053), "7053", "7053", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(CPU_7080), "7080", "7080", NULL, NULL, NULL}, + {UNIT_MSIZE, MEMAMOUNT(0), "10K", "10K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(1), "20K", "20K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(3), "40K", "40K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(7), "80K", "80K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(11), "120K", "120K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(15), "160K", "160K", &cpu_set_size}, + {EMULATE2, 0, NULL, "NOEMU40K", NULL, NULL, NULL}, + {EMULATE2, EMULATE2, "EMU40K", "EMU40K", NULL, NULL, NULL}, + {EMULATE3, 0, "EMU705", "EMU705", NULL, NULL, NULL}, + {EMULATE3, EMULATE3, "EMU7053", "EMU7053", NULL, NULL, NULL}, + {NONSTOP, 0, "PROGRAM", "PROGRAM", NULL, NULL, NULL}, + {NONSTOP, NONSTOP, "NONSTOP", "NONSTOP", NULL, NULL, NULL}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 18, 1, 8, 8, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description +}; + + +/* Quick ways to wrap addresses */ +uint16 next_addr[6 * 256]; /* Next storage location */ +uint16 prev_addr[6 * 256]; /* Previous storage location */ +uint16 next_half[6 * 256]; /* Forward half loop locations */ + +/*#define ReadP(addr) M[(addr) % EMEMSIZE] */ +#define WriteP(addr, data) M[(addr) % EMEMSIZE] = data + +#define Next(reg) if (reg == 0) reg = EMEMSIZE; reg-- +#define Prev5(reg) reg += 5; if (reg > EMEMSIZE) reg -= EMEMSIZE +#define Prev10(reg) reg += 10; if (reg > EMEMSIZE) reg -= EMEMSIZE +#define Prev(reg) reg++; if (reg == EMEMSIZE) reg = 0 + +/* Read 1 character from memory, checking for reducancy error. */ +uint8 ReadP(uint32 addr, uint16 flag) { + uint8 value; + value = M[(addr) % EMEMSIZE]; + if (value & 0100) { + if (flag == 0) + return value; + flags |= flag|ANYFLAG; + } else if (value == 0) { + flags |= flag|ANYFLAG; + } + return value & 077; +} + +/* Read 5 characters from memory starting at addr */ +uint32 Read5(uint32 addr, uint16 flag) { + uint32 value; + + value = ReadP(addr-4, flag) << (4 * 6); + value |= ReadP(addr-3, flag) << (3 * 6); + value |= ReadP(addr-2, flag) << (2 * 6); + value |= ReadP(addr-1, flag) << (1 * 6); + value |= ReadP(addr, flag); + return value; +} + +/* Write 5 characters from memory starting at addr */ +void Write5(uint32 addr, uint32 value) { + WriteP(addr-4, 077 & (value >> (4 * 6))); + WriteP(addr-3, 077 & (value >> (3 * 6))); + WriteP(addr-2, 077 & (value >> (2 * 6))); + WriteP(addr-1, 077 & (value >> (1 * 6))); + WriteP(addr , 077 & (value)); +} + + +t_stat +sim_instr(void) +{ + t_stat reason; + int opcode; + uint8 reg; + uint16 fmsk; + uint8 zone; + uint8 sign; + uint8 zero; + uint8 at; + uint8 carry; + uint8 t; + uint8 cr1, cr2; + int temp; + uint32 addr; + uint8 iowait = 0; + int instr_count = 0; /* Number of instructions to execute */ + + if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); + } + + + cpu_type = CPU_MODEL; + /* Adjust max memory and flags based on emulation mode */ + EMEMSIZE = MEMSIZE; + switch (cpu_type) { + case CPU_7080: + if ((flags & EIGHTMODE) == 0) { + cpu_type = (cpu_unit.flags & EMULATE3)?CPU_7053:CPU_705; + EMEMSIZE = MEMSIZE; + if (cpu_unit.flags & EMULATE2 && EMEMSIZE > 40000) + EMEMSIZE = 40000; + if (cpu_type == CPU_705 && (cpu_unit.flags & EMULATE2) == 0 + && EMEMSIZE > 20000) + EMEMSIZE = 20000; + if (EMEMSIZE > 80000) + EMEMSIZE = 80000; + } + break; + case CPU_7053: + if (EMEMSIZE > 80000) + EMEMSIZE = 80000; + if (cpu_unit.flags & EMULATE2 && EMEMSIZE > 40000) + EMEMSIZE = 40000; + break; + case CPU_705: + if (cpu_unit.flags & EMULATE2 && EMEMSIZE > 40000) + EMEMSIZE = 40000; + else if (EMEMSIZE > 20000) + EMEMSIZE = 20000; + break; + case CPU_702: + EMEMSIZE = 10000; + break; + } + reason = 0; + + while (reason == 0) { /* loop until halted */ + + chan_proc(); + if (chwait != 0) { + if (chan_active(chwait - 1)) { + sim_interval = 0; + } else { + chwait = 0; + } + } + +stop_cpu: + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) + break; /* process */ + } + + if (sim_brk_summ && sim_brk_test(IC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + + /* Make sure instruction is on 4 or 9 boundary */ + if (((IC + 1) % 5) != 0) { + flags |= INSTFLAG|ANYFLAG; + } + + /* Check stop conditions */ + if ((cpu_unit.flags & NONSTOP) && (intprog == 0) && intmode != 0 && + selreg2 == 0 && (IRQFLAGS & flags)) { + /* Process as interrupt */ + Next(IC); /* Back up to start of instruction */ + Next(IC); + Next(IC); + Next(IC); + Next(IC); + store_cpu(0x3E0, 1); + load_cpu(0x2A0, 0); + intprog = 1; + spc = 0x200; + } else if (((cpu_unit.flags & NONSTOP) == 0 || intprog == 0) && + (stop_flags & flags)) { + /* Issue sim halt */ + if (stop_flags & flags & INSTFLAG) { + reason = STOP_UUO; + flags &= ~ (INSTFLAG|ANYFLAG); + break; + } + if (stop_flags & flags & MCHCHK) { + reason = STOP_MMTRP; + flags &= ~(MCHCHK|ANYFLAG); + break; + } + if (stop_flags & flags & IOCHK) { + reason = STOP_IOCHECK; + flags &= ~(IOCHK|ANYFLAG); + break; + } + if (stop_flags & flags & RECCHK) { + reason = STOP_RECCHK; + flags &= ~(RECCHK|ANYFLAG); + break; + } + if (stop_flags & flags & ACOFLAG) { + reason = STOP_ACOFL; + flags &= ~(ACOFLAG|ANYFLAG); + break; + } + if (stop_flags & flags & SGNFLAG) { + reason = STOP_SIGN; + flags &= ~(SGNFLAG|ANYFLAG); + break; + } + } else if (cpu_unit.flags & NONSTOP && intprog && (IRQFLAGS & flags)) { + /* Issue sim halt */ + if (flags & INSTFLAG) { + reason = STOP_UUO; + flags &= ~ (INSTFLAG|ANYFLAG); + break; + } + if (flags & MCHCHK) { + reason = STOP_MMTRP; + flags &= ~(MCHCHK|ANYFLAG); + break; + } + if (flags & IOCHK) { + reason = STOP_IOCHECK; + flags &= ~(IOCHK|ANYFLAG); + break; + } + if (flags & RECCHK) { + reason = STOP_RECCHK; + flags &= ~(RECCHK|ANYFLAG); + break; + } + if (flags & ACOFLAG) { + reason = STOP_ACOFL; + flags &= ~(ACOFLAG|ANYFLAG); + break; + } + if (flags & SGNFLAG) { + reason = STOP_SIGN; + flags &= ~(SGNFLAG|ANYFLAG); + break; + } + } + + + /* If we are waiting on I/O, don't fetch */ + if (!chwait) { + if (!iowait) { + if (indflag == 0 && bkcmp == 0 && intprog == 0 && + intmode != 0 && irqflags != 0) { + /* Process as interrupt */ + store_cpu(0x3E0, 1); + addr = 0x200; + temp = 2; /* Start channel 20 */ + while((temp & irqflags) == 0) { + temp <<= 1; + addr += 32; + if (temp == 0x20) /* Channel 40 */ + addr = 0x400; + } + sim_debug(DEBUG_TRAP, &cpu_dev, "Trap on channel %x\n", addr); + irqflags &= ~temp; + load_cpu(addr, 0); + intprog = 1; + spc = 0x200; + sim_debug(DEBUG_TRAP, &cpu_dev, "Trap to addr %d\n", IC); + } + /* Make sure IC is on correct boundry */ + if ((IC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + sim_interval--; + goto stop_cpu; + } + /* Split out current instruction */ + MA = IC; + MAC = read_addr(®, &zone); + opcode = ReadP(MA, INSTFLAG); /* Finaly read opcode */ + MA = MAC; + IC += 5; + switch (CPU_MODEL) { + case CPU_7080: + temp = 160000; + if ((flags & EIGHTMODE) == 0) { + temp = 80000; + if (cpu_unit.flags & EMULATE2) + temp = 40000; + else if (cpu_type == CPU_705) + temp = 20000; + } + break; + case CPU_7053: + temp = 80000; + if (cpu_unit.flags & EMULATE2) + temp = 40000; + break; + case CPU_705: + temp = 20000; + if (cpu_unit.flags & EMULATE2) + temp = 40000; + break; + case CPU_702: + temp = 10000; + break; + } + while (IC >= (uint32)temp) + IC -= temp; + /* Resolve full address and register based on cpu mode */ + switch (cpu_type) { + case CPU_705: /* 705 */ + case CPU_702: /* 702 */ + break; + case CPU_7080: /* 7080 */ + if (indflag) { + indflag = 0; + if ((MA % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + goto stop_cpu; + } + MAC = read_addr(&t, &zone); + MA = MAC; + } + break; + case CPU_7053: /* 705-iii */ + if (zone & 04) { /* Check indirect */ + if ((MA % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + goto stop_cpu; + } + MAC = read_addr(&t, &zone); + MA = MAC; + } + break; + } + + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = (IC - 5) | HIST_PC; + hst[hst_p].op = opcode; + hst[hst_p].ea = MAC; + hst[hst_p].reg = reg; + hst[hst_p].inst = Read5(IC-5, 0); +#if 0 + addr = get_acstart(reg); + for (t = 0; t < 32; t++) { + hst[hst_p].store[t] = AC[addr]; + addr = next_addr[addr]; + if (hst[hst_p].store[t] == 0) + break; + } +#endif + } + } + + fmsk = (reg)?(BSIGN|BZERO):(ASIGN|AZERO); + iowait = 0; + sim_interval -= 5; /* count down */ + switch (opcode) { + case OP_TR: /* TR */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + /* 7080, reg = 1, TSL */ + if (cpu_type >= CPU_7053 && reg == 1) { + /* MAC2 <- IC+5 */ + MA = MAC2+4; + write_addr(IC, 0, 0); + sim_interval -= 4; /* count down */ + } + IC = MAC; + break; + + case OP_HLT: /* STOP */ + if ((cpu_unit.flags & NONSTOP) && (intprog == 0) + && intmode != 0) { + /* Process as interrupt */ + store_cpu(0x3E0, 1); + load_cpu(0x2A0, 0); + intprog = 1; + spc = 0x200; + } else + reason = STOP_HALT; + break; + + case OP_TRH: /* TR HI */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if (flags & HIGHFLAG) { + IC = MAC; + } + break; + + case OP_TRE: /* TR EQ */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if ((flags & CMPFLAG) == 0) { + IC = MAC; + } + break; + + case OP_TRP: /* TR + */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if ((flags & SIGN & fmsk) == 0) { + IC = MAC; + } + break; + + case OP_TRZ: /* TR 0 */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if (flags & ZERO & fmsk) { + IC = MAC; + } + break; + + case OP_TRS: /* TR SIG */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + temp = selreg & 0xff; + t = 0; + if (cpu_type >= CPU_7053 && reg != 0) { + switch (reg) { + case 1: /* TRR */ + switch (chan_cmd(selreg, IO_TRS << 8, 0)) { + case SCPE_OK: + t = 1; + break; + case SCPE_BUSY: + case SCPE_NODEV: + case SCPE_IOERR: + break; + } + break; + case 2: /* TTC */ + temp = chan_mapdev(selreg); + if (temp > 0 && chan_test(temp, CHS_ERR)) + t = 1; + break; + case 3: /* TSA */ + temp = chan_mapdev(selreg); + addr = (selreg & 0xf) +((selreg >> 8) & 0xff0); + if (temp > 0 && chan_active(temp)) { + chwait = temp + 1; + IC -= 5; + } else if (temp > 0 && chan_test(temp, CHS_ERR)) + t = 1; + else if (ioflags[selreg/8] & 1<<(selreg & 07)) + t = 1; + else if (ioflags[addr/8] & 1<<(addr & 07)) + t = 1; + break; + case 10:/* TIC */ /* Instruction error */ + case 11:/* TMC */ /* Machine check */ + case 12:/* TRC */ /* I/O Check */ + case 13:/* TEC */ /* Record check */ + case 14:/* TOC */ /* AC Overflow flag */ + case 15:/* TSC */ /* Sign mismatch */ + temp = 1 << (reg - 6); + if (flags & temp) + t = 1; + flags &= ~temp; + break; + default: + break; + } + } else { + switch((selreg >> 8) & 0xff) { + case 20: /* Tape DS */ + case 21: + case 22: + case 23: + if (ioflags[selreg/8] & 1<<(selreg & 07)) + t = 1; + /* Handle tapes at either location */ + temp = (selreg & 0xf) +((selreg >> 8) & 0xff0); + if (ioflags[temp/8] & 1<<(temp & 07)) + t = 1; + break; + case 2: /* Tape EOF */ + if (ioflags[selreg/8] & 1<<(selreg & 07)) + t = 1; + /* Handle tapes at either location */ + temp = (selreg & 0xf) +((selreg << 8) & 0xff0); + if (temp < 2400) { + if (ioflags[temp/8] & 1<<(temp & 07)) + t = 1; + } + break; + case 1: /* Card Reader */ + if (ioflags[selreg/8] & 1<<(selreg & 07)) + t = 1; + break; + case 9: /* Special signals */ + switch(temp) { + case 0: /* Instruction error */ + case 1: /* Machine check */ + case 2: /* I/O Check */ + case 3: /* Record check */ + case 4: /* AC Overflow flag */ + case 5: /* Sign mismatch */ + temp = 1 << (temp + 4); + if (flags & temp) + t = 1; + flags &= ~temp; + break; + case 0x11: case 0x12: case 0x13: case 0x14: + case 0x15: case 0x16: case 0x17: case 0x18: + case 0x19: + if(SW & (1 << ((temp & 0xf) - 1))) + t = 1; + break; + } + break; + case 4: /* Printer */ + /* Check channel 12 end of page */ + /* Devices never signals */ + case 3: /* Card punch */ + case 5: /* Typewriter */ + /* Invalid digits */ + case 0: /* Nothing */ + case 6: /* ???? */ + case 7: /* ???? */ + case 8: /* ???? */ + default: /* Drum */ + break; + } + } + if (t) { + IC = MAC; + } + break; + + case OP_TRA: /* TRA */ + t = 0; + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + switch (cpu_type) { + case CPU_7080: /* 7080 */ + case CPU_7053: /* 705-iii */ + if (reg > 0 && reg < 7) { + /* Test sense switch */ + if (SW & (1<<(reg - 1))) + t = 1; + break; + } else if (reg == 7) { + /* Transfer if Non-stop */ + if (cpu_unit.flags & NONSTOP) + t = 1; + break; + } else if (reg > 7) { + /* Nop */ + break; + } + case CPU_705: /* 705 */ + case CPU_702: /* 702 */ + if (flags & ANYFLAG) + t = 1; + flags &= ~ANYFLAG; + break; + } + if (t) { + IC = MAC; + } + break; + + case OP_TZB: /* TZB */ + /* transfer bit zero addr = MAC2 */ + if (CPU_MODEL < CPU_7053) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + t = ReadP(MAC2, MCHCHK); + /* Undocumented, but diags seem to indicate this */ +/* if (t == CHR_RM) t = 0; */ + switch(reg) { + case 7: /* C */ + /* Develop parity */ + t = sim_parity_table[t & 077]; + t ^= M[MAC % EMEMSIZE] & 0100; /* C654 */ + if (t == 0) + IC = MA; + break; + + case 1: /* 1 */ + case 2: /* 2 */ + case 3: /* 4 */ + case 4: /* 8 */ + case 5: /* A */ + case 6: /* B */ + if ((t & (1<<(reg-1))) == 0) + IC = MA; + break; + case 0: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + break; + } + sim_interval --; /* count down */ + break; + + case OP_NOP: /* NOP */ + break; + + case OP_CMP: /* CMP */ + do_compare(reg, 0); + break; + + case OP_UNL: /* UNL */ + addr = get_acstart(reg); + cr2 = AC[addr]; + while(cr2 != 0) { + WriteP(MA, cr2); + Next(MA); + addr = next_addr[addr]; + cr2 = AC[addr]; + sim_interval--; /* count down */ + } + break; + + case OP_LOD: /* LOD */ + addr = get_acstart(reg); + flags |= ZERO & fmsk; + /* Clear sign */ + flags &= ~(SIGN & fmsk); + while(AC[addr] != 0) { + cr1 = ReadP(MA, MCHCHK); + AC[addr] = cr1; + if ((cr1 & 0xf) != 10) + flags &= ~(ZERO & fmsk); + Next(MA); + addr = next_addr[addr]; + sim_interval--; /* count down */ + } + break; + + case OP_ST: /* ST */ + addr = get_acstart(reg); + sim_interval--; /* count down */ + at = 1; /* Use to indicate first cycle */ + while ((cr2 = AC[addr]) != 0) { + if (at) { + cr2 &= 0xf; + if (flags & fmsk & SIGN) + cr2 |= 040; /* Minus */ + else + cr2 |= 060; /* Plus */ + at = 0; + } else { + if ((cr2 & 0xf) == 0) { + cr2 &= 060; + cr2 |= 012; + } + if ((cr2 & 060) == 040 || (cr2 & 060) == 020) + cr2 |= 0100; + } + WriteP(MA, cr2); + Next(MA); + addr = next_addr[addr]; + sim_interval--; /* count down */ + } + /* Adjust next character */ + cr1 = ReadP(MA, MCHCHK); + if (at == 0 && cr1 == 10) + cr1 = 0; + if ((cr1 & 060) == 0) + cr1 |= 060; + WriteP(MA, cr1); + sim_interval--; /* count down */ + break; + case OP_SGN: /* SGN */ + cr1 = ReadP(MA, MCHCHK); + /* Adjust memory to zero zone or blank */ + if (cr1 & 017) { + WriteP(MA, cr1 & 017); + } else { + WriteP(MA, 020); + } + sim_interval--; /* count down */ + /* Make AC either + or - */ + flags &= ~fmsk; + cr1 &= 060; + if (cr1 == 040) + flags |= SIGN & fmsk; + else + cr1 |= 060; + /* One char in AC */ + addr = get_acstart(reg); + AC[addr] = cr1; + addr = next_addr[addr]; + AC[addr] = 0; + break; + + case OP_NTR: /* NORM TR */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + at = 1; + zero = 0; + /* Space to end storage */ + while(AC[addr] != 0) { + addr = next_addr[addr]; + if (at) { + zero = 1; + at = 0; + } else + zero = 0; + sim_interval--; /* count down */ + } + /* Zero or one digit, exit */ + if (at || zero) + break; + /* Back up one */ + addr = prev_addr[addr]; + if (AC[addr] == 10) { + AC[addr] = 0; + IC = MA; + sim_interval --; /* count down */ + } + break; + + case OP_SET: /* SET */ + addr = get_acstart(reg); + flags |= (fmsk & ZERO); /* Might be zero */ + at = 0; /* No smt yet */ + /* Scan for mark */ + while (MAC != 0) { + if (at) + AC[addr] = 10; /* Zero fill */ + else if (AC[addr] == 0) { + at = 1; /* Indicate that we found smt */ + AC[addr] = 10; + } else if (AC[addr] != 10) + flags &= ~(ZERO & fmsk); /* No zero, adjust flag */ + MAC--; + addr = next_addr[addr]; + sim_interval --; /* count down */ + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != 0) + break; + chan_proc(); + } + } + /* Insert a mark at new end */ + AC[addr] = 0; + /* Clear sign if zero */ + flags &= ~(((flags & fmsk) >> 2) & SIGN); + break; + + case OP_SHR: /* SHR */ + if (cpu_type != CPU_702 && reg != 0) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + while (MA != 0) { + MA--; + addr = next_addr[addr]; + sim_interval --; /* count down */ + } + if (cpu_type == CPU_702 && reg != 0) { + spcb = addr; + } else { + if (cpu_type == CPU_702) + spc = addr; + else if (reg == 0) + spc = (spc & 0x700) | (addr & 0xff); + } + flags |= (fmsk & ZERO); /* Might be zero */ + /* Check if zero */ + while (AC[addr] != 0) { + if (AC[addr] != 10) { + flags &= ~(ZERO & fmsk); + break; + } + addr = next_addr[addr]; + sim_interval --; /* count down */ + } + /* Clear sign if zero */ + flags &= ~(((flags & fmsk) >> 2) & SIGN); + break; + + case OP_LEN: /* LEN */ + if (cpu_type != CPU_702 && reg != 0) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + addr = prev_addr[addr]; + while(MA != 0) { + AC[addr] = 10; + addr = prev_addr[addr]; + MA--; + sim_interval --; /* count down */ + } + AC[addr] = 0; + addr = next_addr[addr]; /* Back up one */ + if (cpu_type == CPU_702 && reg != 0) + spcb = addr; + else { + if (cpu_type == CPU_702) + spc = addr; + else if (reg == 0) + spc = (spc & 0x700) | (addr & 0xff); + } + break; + + case OP_RND: /* RND */ + if (cpu_type != CPU_702 && reg != 0) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + flags |= (fmsk & ZERO); /* Might be zero */ + if (MA != 0) { + int smt = 0; + /* Adjust Address */ + while (MA != 0) { + MA--; + addr = next_addr[addr]; + sim_interval --; /* count down */ + } + /* Adjust start pointer */ + if (cpu_type == CPU_702 && reg != 0) { + spcb = addr; + } else { + if (cpu_type == CPU_702) + spc = addr; + else if (reg == 0) + spc = (spc & 0x700) | (addr & 0xff); + } + addr = prev_addr[addr]; /* Back up one */ + /* Process while valid digit in memory */ + t = 5; + do { + uint8 cr1; + if (AC[addr] == 0) { + smt = 1; + cr1 = t; + t = 0; + } else { + cr1 = bcd_bin[AC[addr]&0xf] + t; + } + if (t != 5 && cr1 != 0) + flags &= ~(ZERO & fmsk); + t = cr1 >= 10; + AC[addr] = (AC[addr] & 060) | bin_bcd[cr1]; + addr = next_addr[addr]; + sim_interval --; /* count down */ + } while (t != 0); /* Loop while carry */ + /* If we overflowed, set flag */ + if (smt) { + flags |= ACOFLAG|ANYFLAG; + AC[addr] = 0; /* Write storage mark */ + } + } + + /* Check if zero */ + while (AC[addr] != 0) { + if (AC[addr] != 10) { + flags &= ~(ZERO & fmsk); + break; + } + addr = next_addr[addr]; + sim_interval --; /* count down */ + } + /* Clear sign if zero */ + flags &= ~(((flags & fmsk) >> 2) & SIGN); + break; + + case OP_SPR: /* ST PR */ + addr = get_acstart(reg); + sign = ((reg)?(flags >> 1): flags) & ASIGN; + WriteP(MA, (sign)?040:020); + sim_interval --; /* count down */ + while(AC[addr] != 0) { + Next(MA); + cr1 = ReadP(MA, MCHCHK); + if (cr1 != CHR_COM && cr1 != CHR_DOT) { + cr2 = AC[addr]; + WriteP(MA, cr2); + addr = next_addr[addr]; + } + sim_interval --; /* count down */ + } + while (1) { + cr1 = ReadP(MA, MCHCHK); + sim_interval --; /* count down */ + if (cr1 == CHR_COM || cr1 == 10) { + WriteP(MA, 020); + } else + break; + Prev(MA); + } + break; + + case OP_ADM: /* ADM */ + /* Cycle 1 */ + addr = get_acstart(reg); + zero = 1; + cr1 = ReadP(MA, MCHCHK); + cr2 = AC[addr]; + sim_interval --; /* count down */ + /* Set sign to sign of Ac */ + sign = (flags & fmsk & SIGN)?1:0; + carry = 0; + /* Check sign if not valid then treat as 0 */ + if (cr1 & 040) { + int smt = 1; + int met = 1; + int msign; + + /* Numeric */ + /* Check sign */ + msign = (cr1 & 020)? 0: 1; /* + - */ + /* Compliment if signs differ */ + t = (msign != sign)? 1: 0; /* -+,+- --,++ */ + carry = t; + if (cr2 == 0) { /* Check for storage mark */ + smt = 0; + cr2 = 10; + } + cr1 &= 0xf; + temp = (t)? comp_bcd[cr2 & 0xf]:bcd_bin[cr2 & 0xf]; + temp = bcd_bin[cr1 & 0xf] + temp + carry; + carry = temp >= 10; + WriteP(MA, (msign? 040:060) | bin_bcd[temp]); + Next(MA); + addr = next_addr[addr]; + do { + if (smt) { + cr2 = AC[addr]; + if (cr2 == 0) + smt = 0; + } else + cr2 = 10; + cr1 = ReadP(MA, MCHCHK); + if (cr1 < 1 || cr1 > 10) { + met = 0; + } else { + temp = (t)? comp_bcd[cr2 & 0xf]:bcd_bin[cr2 & 0xf]; + temp = bcd_bin[cr1 & 0xf] + temp + carry; + carry = temp >= 10; + WriteP(MA, bin_bcd[temp]); + sim_interval --; /* count down */ + addr = next_addr[addr]; + Next(MA); + cr1 = ReadP(MA, MCHCHK); + } + } while (met); + + /* Recompliment */ + if (t && carry == 0) { + MA = MAC; + cr1 = ReadP(MA, MCHCHK); + sim_interval --; /* count down */ + cr1 ^= 020; /* Compliment sign */ + temp = comp_bcd[cr1 & 0xf] + 1; + carry = temp >= 10; + WriteP(MA, (cr1 & 060) | bin_bcd[temp]); + Next(MA); + while(1) { + cr1 = ReadP(MA, MCHCHK); + if (cr1 < 1 || cr1 > 10) + break; + temp = comp_bcd[cr1 & 0xf] + carry; + carry = temp >= 10; + WriteP(MA, bin_bcd[temp]); + sim_interval --; /* count down */ + Next(MA); + } + } + } else { + int zcarry = 0; + + /* Non-numeric */ + while (cr2 != 0) { + temp = bcd_bin[(cr2 & 0xf)] + bcd_bin[(cr1 & 0xf)] + carry; + carry = temp >= 10; + if (temp > 10) + temp -= 10; + t = (cr2 & 0x30) + (cr1 & 0x30) + zcarry; /* Zone add */ + zcarry = (t & 0x40)?0x10:0; + addr = next_addr[addr]; + cr2 = AC[addr]; + if (cr2 == 0 && carry) + t += 0x10; + temp = (temp & 0xf) | (t & 0x30); + if (temp == 0) + temp = 10; + WriteP(MA, temp); + Next(MA); + cr1 = ReadP(MA, MCHCHK); + sim_interval --; /* count down */ + } + } + break; + + case OP_SUB: /* SUB */ + do_addsub(1, reg, 0, fmsk); + break; + + case OP_ADD: /* ADD */ + do_addsub(0, reg, 0, fmsk); + break; + + case OP_RSU: /* R SUB */ + do_addsub(1, reg, 1, fmsk); + break; + + case OP_RAD: /* R ADD */ + do_addsub(0, reg, 1, fmsk); + break; + + case OP_MPY: /* MPY */ + do_mult(reg, fmsk); + break; + + case OP_DIV: /* DIV */ + do_divide(reg, fmsk); + break; + + + case OP_RCV: /* RCV 705 only */ + if (cpu_type == CPU_702) + flags |= INSTFLAG|ANYFLAG; + else + MAC2 = MAC; + break; + + case OP_TMT: /* TMT 705 only */ + if (cpu_type == CPU_702) { + flags |= INSTFLAG|ANYFLAG; + break; + } + if (reg == 0) { + /* Copy in blocks of 5 characters */ + if ((MAC2 % 5) != 4 || (MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + do { + addr = Read5(MAC, MCHCHK); + Write5(MAC2, addr); + Prev5(MAC2); + Prev5(MAC); + sim_interval -= 10; /* count down */ + } while ((addr & 077) != CHR_RM); + } else { + /* One char at a time */ + addr = get_acstart(reg); + while(AC[addr] != 0) { + cr1 = ReadP(MAC, MCHCHK); + WriteP(MAC2, cr1); + Prev(MAC); + Prev(MAC2); + addr = next_addr[addr]; + sim_interval -= 2; /* count down */ + } + } + break; + + case OP_SEL: /* SEL */ + /* Convert device to hex number */ + selreg = MAC % 10; + MAC /= 10; + selreg |= (MAC % 10) << 4; + MAC /= 10; + selreg |= (MAC % 10) << 8; + MAC /= 10; + selreg |= (MAC % 10) << 12; + MAC /= 10; + break; + + case OP_CTL: /* CTL */ + temp = 0; + if (reg > 1) { + /* Process ASU modes non-zero */ + switch (reg) { + /* 7080 */ + case 12: /* ECB */ + /* Enable backwards compare */ + if (CPU_MODEL == CPU_7080 && cpu_type > CPU_705) + bkcmp = 1; + else + flags |= INSTFLAG|ANYFLAG; + break; + + case 13: /* CHR */ + /* Clear io error flags */ + chan_chr_13(); + memset(ioflags, 0, sizeof(ioflags)); + flags &= ~(IRQFLAGS); + break; + + case 14: /* EEM */ + /* Enter 80 mode */ + if (CPU_MODEL == CPU_7080) { + flags |= EIGHTMODE; + EMEMSIZE = MEMSIZE; + cpu_type = CPU_7080; + } else + flags |= INSTFLAG|ANYFLAG; + break; + + case 15: /* LEM */ + /* Leave 80 mode */ + if (CPU_MODEL == CPU_7080) { + flags &= ~EIGHTMODE; + cpu_type = (cpu_unit.flags & EMULATE3)? + CPU_7053:CPU_705; + EMEMSIZE = MEMSIZE; + if (cpu_unit.flags & EMULATE2 && + EMEMSIZE > 40000) + EMEMSIZE = 40000; + if (cpu_type == CPU_705 && + (cpu_unit.flags & EMULATE2) == 0 && + EMEMSIZE > 20000) + EMEMSIZE = 20000; + if (EMEMSIZE > 80000) + EMEMSIZE = 80000; + } else + flags |= INSTFLAG|ANYFLAG; + break; + } + break; + } + + switch (MAC) { + case 0: /* IOF */ + ioflags[selreg/8] &= ~(1<<(selreg&07)); + if ((selreg & 0xff00) == 0x200) { + /* Handle tapes at either location */ + temp = (selreg & 0xf) + + ((selreg & 0xff0) << 8); + if (temp < 0x2400) + ioflags[temp/8] &= ~(1<<(temp & 07)); + } + if ((selreg & 0xf000) == 0x2000) { + temp = (selreg & 0xf) + + ((selreg >> 8) & 0xff0); + ioflags[temp/8] &= ~(1<<(temp & 07)); + } + temp = 0; + break; + + case 1: /* WTM */ + temp = IO_WEF << 8; + break; + case 2: /* REW */ + if (cpu_type > CPU_705 && reg == 1) + temp = IO_RUN << 8; + else + temp = IO_REW << 8; + break; + + case 3: /* ION */ + ioflags[selreg/8] |= 1<<(selreg&07); + if ((selreg & 0xff00) == 0x200) { + /* Handle tapes at either location */ + temp = (selreg & 0xf) + + ((selreg & 0xff0) << 8); + if (temp < 0x2400) + ioflags[temp/8] |= + 1<<(temp & 07); + } + if ((selreg & 0xf000) == 0x2000) { + temp = (selreg & 0xf) + + ((selreg >> 8) & 0xff0); + ioflags[temp/8] |= 1<<(temp & 07); + } + temp = 0; + break; + + case 4: /* BSR */ + if (cpu_type >= CPU_7053 && reg == 1) + temp = IO_BSF << 8; + else + temp = IO_BSR << 8; + break; + case 5: + case 9: /* SKP */ + temp = IO_ERG << 8; + break; + case 37: /* SDL */ + temp = IO_SDL << 8; + break; + case 38: /* SDH */ + temp = IO_SDH << 8; + break; + default: + flags |= ANYFLAG|INSTFLAG; + break; + } + if (temp != 0) { + switch (chan_cmd(selreg, temp, 0)) { + case SCPE_OK: + break; + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + case SCPE_IOERR: + flags |= ANYFLAG|INSTFLAG; + break; + } + } + break; + + case OP_RD: /* READ */ + temp = (IO_RDS << 8) | reg; + switch (chan_cmd(selreg, temp, MAC)) { + case SCPE_OK: + break; + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + case SCPE_IOERR: + flags |= ANYFLAG|INSTFLAG; + break; + } + break; + + case OP_WR: /* WRITE */ + temp = (IO_WRS << 8) | reg; + switch (chan_cmd(selreg, temp, MAC)) { + case SCPE_OK: + break; + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + case SCPE_IOERR: + flags |= ANYFLAG|INSTFLAG; + break; + } + break; + + case OP_WRE: /* WR ER */ + temp = (IO_WRS << 8) | reg | CHAN_ZERO; + switch (chan_cmd(selreg, temp, MAC)){ + case SCPE_OK: + break; + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + case SCPE_IOERR: + flags |= ANYFLAG|INSTFLAG; + break; + } + break; + + case OP_RWW: /* RWW 705 only */ + MAC2 = MAC; + selreg2 = selreg | 0x8000; + break; + + /* 7080 opcodes */ + case OP_CTL2: + if (cpu_type != CPU_7080) { + flags |= ANYFLAG|INSTFLAG; + break; + } + switch(reg) { + case 0: /* SPC */ + /* Set starting point */ + /* Selects on char of 8 char words */ + temp = (MA % 10) & 7; /* Units digit */ + MA /= 10; + t = MA % 10; /* Tens digit */ + temp += (t&3) << 3; /* One of words */ + MA /= 10; + t = MA % 10; /* Hundreds */ + temp += (t&7) << 5; /* One of four word sets */ + MA /= 10; /* Thousands */ + t = MA % 10; + temp += (t&7) << 8; /* Bank */ + spc = temp; + break; + + case 2: /* LFC */ + /* load four chars */ + addr = spc; + do { + t = ReadP(MA, MCHCHK); + if (t == CHR_LESS) + t = 0; + AC[addr] = t; + addr = next_addr[addr]; + Next(MA); + sim_interval --; /* count down */ + } while((MA % 5) != 0); + break; + + case 3: /* UFC */ + /* unload four chars */ + addr = spc; + do { + t = AC[addr]; + addr = next_addr[addr]; + if (t == 0) + t = CHR_LESS; + WriteP(MA, t); + Next(MA); + sim_interval --; /* count down */ + } while((MA % 5) != 0); + break; + + case 4: /* LSB */ + /* Load storage bank */ + addr = spc & 0x700; + temp = 256; + while(temp-- > 0) { + t = ReadP(MA, MCHCHK); + if (t == CHR_LESS) + t = 0; + AC[addr] = t; + addr = next_addr[addr]; + Next(MA); + sim_interval --; /* count down */ + } + break; + + case 5: /* USB */ + /* Unload storage bank */ + addr = spc & 0x700; + temp = 256; + while(temp-- > 0) { + t = AC[addr]; + addr = next_addr[addr]; + if (t == 0) + t = CHR_LESS; + WriteP(MA, t); + Next(MA); + sim_interval --; /* count down */ + } + break; + + case 6: /* EIM */ + /* Enter interrupt mode */ + intmode = 1; + break; + + case 7: /* LIM */ + /* Leave interrupt mode */ + intmode = 0; + break; + + case 8: /* TCT */ + /* Ten char transmit */ + /* Copy in blocks of 10 characters */ + if ((MAC2 % 10) != 9 || (MAC % 10) != 9) { + flags |= INSTFLAG|ANYFLAG; + break; + } + do { + addr = Read5(MAC-5, MCHCHK); + Write5(MAC2-5, addr); + addr = Read5(MAC, MCHCHK); + Write5(MAC2, addr); + Prev10(MAC); + Prev10(MAC2); + sim_interval -= 20; /* count down */ + } while ((addr & 077) != CHR_RM); + break; + case 10: /* EIA */ + /* Enable indirect address */ + indflag = 1; + break; + + case 11: /* CNO */ + /* Nop */ + break; + + case 12: /* TLU */ + /* Table lookup equal. */ + /* Walk backward in memory until equal, or GM */ + do { + do_compare(0, 1); + if ((flags & CMPFLAG) == 0) + break; + while ((cr1 = ReadP(MA, MCHCHK)) != CHR_RM || + cr1 != CHR_GM) + { Next(MA); } + } while(cr1 != CHR_GM); + MAC2 = MA; + break; + case 13: /* TLU */ + /* Table lookup equal or hi */ + /* Walk backward in memory until equal, or GM */ + do { + do_compare(0, 1); + if ((flags & LOWFLAG) == 0) + break; + while ((cr1 = ReadP(MA, MCHCHK)) != CHR_RM || + cr1 != CHR_GM) { + Next(MA); + } + } while(cr1 != CHR_GM); + MAC2 = MA; + break; + case 14: /* TIP */ + /* Transfer to interrupt program */ + if ((MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + store_cpu(0x3E0, 1); + intprog = 1; + spc = 0x200; + IC = MA; + flags &= ~IRQFLAGS; + break; + case 15: /* LIP */ + /* Leave interrupt program */ + if (MA != 9) { + /* Selects on char of 8 char words */ + temp = (MA % 10) & 7; /* Units digit */ + MA /= 10; + t = MA % 10; /* Tens digit */ + temp += (t&3) << 3; /* One of words */ + MA /= 10; + t = MA % 10; /* Hundreds */ + temp += (t&7) << 5; /* One of four word sets */ + MA /= 10; /* Thousands */ + t = MA % 10; + temp += (t&7) << 8; /* Bank */ + store_cpu(temp, 0); + } + /* Fully load new context */ + load_cpu(0x3E0, 1); + intprog = 0; + break; + } + break; + + case OP_CTL3: + if (cpu_type != CPU_7080) { + flags |= ANYFLAG|INSTFLAG; + break; + } + addr = get_acstart(reg); + switch(reg) { + case 8: /* TCR */ + /* Ten char recieve */ + /* Copy in blocks of 10 characters */ + if ((MAC2 % 10) != 9 || (MAC % 10) != 9) { + flags |= INSTFLAG|ANYFLAG; + break; + } + do { + addr = Read5(MAC2-5, MCHCHK); + Write5(MAC-5, addr); + addr = Read5(MAC2, MCHCHK); + Write5(MAC, addr); + Prev10(MAC); + Prev10(MAC2); + sim_interval -= 2; /* count down */ + } while ((addr & 077) != CHR_RM); + break; + break; + case 14: /* SMT */ + write_addr(MAC2, 0, 0); + WriteP(MA, 10); /* Finish with zero */ + store_addr(MAC2, addr); + sim_interval -= 10; + break; + } + break; + + case OP_AAM: /* AAM */ + /* Add address in store to memory */ + if (CPU_MODEL < CPU_7053 || (MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + t = ReadP(MA, MCHCHK); /* Read low order digit */ + sim_interval --; /* count down */ + if (AC[addr] != 0) { + temp = AC[addr]; + addr = next_addr[addr]; + } else + temp = 10; + temp = bcd_bin[temp & 0xf] + bcd_bin[t & 0xf]; + carry = temp > 9; + if (carry) + temp -= 10; + t = (t & 060) | temp; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK); /* Read tens order digit */ + sim_interval --; /* count down */ + if (AC[addr] != 0) { + temp = AC[addr]; + addr = next_addr[addr]; + } else + temp = 10; + at = (t & 060) + (temp & 060); + temp = bcd_bin[temp & 0xf] + bcd_bin[t & 0xf] + carry; + carry = temp > 9; + if (carry) + temp -= 10; + t = (at & 060) | temp; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK); /* Read hundreds order digit */ + sim_interval --; /* count down */ + if (AC[addr] != 0) { + temp = AC[addr]; + addr = next_addr[addr]; + } else + temp = 10; + at = ((at & 0100) >> 2) + (t & 060) + (temp & 060); + temp = bcd_bin[temp & 0xf] + bcd_bin[t & 0xf] + carry; + carry = temp > 9; + if (carry) + temp -= 10; + t = (at & 060) | temp; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK); /* Read thousands order digit */ + sim_interval --; /* count down */ + if (AC[addr] != 0) { + temp = AC[addr]; + addr = next_addr[addr]; + } else + temp = 10; + temp = bcd_bin[temp & 0xf] + bcd_bin[t & 0xf] + carry; + carry = (temp > 9)?0x10:0; + if (carry) + temp -= 10; + t = (t & 060) | temp; + temp = 0; + /* Decode digits 5 and 6 */ + if (AC[addr] != 0) { + temp = bcd_bin[AC[addr] & 0xf]; + addr = next_addr[addr]; + if (AC[addr] != 0 && CPU_MODEL == CPU_7080 && + flags & EIGHTMODE) + temp += (1 & bcd_bin[(AC[addr] & 0xf)]) * 10; + temp &= 0xf; + } + /* Add zone bits for top digit */ + t += ((temp & 3) << 4) + carry; + carry = (t & 0100) != 0; + t &= 077; + if ((t & 0xf) == 10) + t &= 060; + if (t == 0) + t = 10; + WriteP(MA, t); + /* Merge high order bits into units if needed */ + switch (CPU_MODEL) { + case CPU_7080: /* 7080 */ + if (flags & EIGHTMODE) { + t = ReadP(MAC, MCHCHK); + temp = (temp >> 2) + carry; + if (t & 040) + temp++; + if (t & 020) + temp += 2; + t = (t & 0xf) | ((temp & 0x1) << 5) | + ((temp & 0x2) << 3); + if ((t & 0xf) == 10) + t &= 060; + if (t == 0) + t = 10; + WriteP(MAC, t); + sim_interval --; /* count down */ + break; + } else if ((cpu_unit.flags & EMULATE3) == 0) + break; + case CPU_7053: /* 705-iii */ + if ((cpu_unit.flags & EMULATE2) == 0) { + t = ReadP(MAC, MCHCHK); + temp = ((temp >> 2) & 1) + carry; + if (t & 040) + temp++; + t = (t & 0x1f) | ((temp & 0x1) << 5); + if ((t & 0xf) == 10) + t &= 060; + if (t == 0) + t = 10; + WriteP(MAC, t); + sim_interval --; /* count down */ + } + break; + case CPU_705: /* 705 */ + case CPU_702: /* 702 */ + break; + } + break; + + case OP_LDA: /* LDA */ + /* Load address */ + if (CPU_MODEL < CPU_7053 || (MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + flags |= ZERO & fmsk; + fmsk = ~(ZERO | fmsk); + addr = get_acstart(reg); + t = ReadP(MA, MCHCHK); /* Read low order digit */ + temp = (t & 060) >> 2; + t &= 0xf; + if (t == 0) + t = 10; + else if (t > 10) + flags |= INSTFLAG|ANYFLAG; + else if (t != 10) + flags &= fmsk; /* Clear zero */ + AC[addr] = t; + addr = next_addr[addr]; + Next(MA); + t = ReadP(MA, MCHCHK); /* next digit */ + t &= 0xf; + if (t == 0) + t = 10; + else if (t > 10) + flags |= INSTFLAG|ANYFLAG; + else if (t != 10) + flags &= fmsk; /* Clear zero */ + AC[addr] = t; + addr = next_addr[addr]; + Next(MA); + t = ReadP(MA, MCHCHK); /* Read third digit */ + t &= 0xf; + if (t == 0) + t = 10; + else if (t > 10) + flags |= INSTFLAG|ANYFLAG; + else if (t != 10) + flags &= fmsk; /* Clear zero */ + AC[addr] = t; + addr = next_addr[addr]; + Next(MA); + t = ReadP(MA, MCHCHK); /* Save High order address */ + temp |= (t & 060) >> 4; + t &= 0xf; + if (t == 0) + t = 10; + else if (t > 10) + flags |= INSTFLAG|ANYFLAG; + else if (t != 10) + flags &= fmsk; /* Clear zero */ + AC[addr] = t; + addr = next_addr[addr]; + temp = lda_flip[temp]; + switch (CPU_MODEL) { + case CPU_702: /* 702 */ + break; + case CPU_7080: /* 7080 */ + if (flags & EIGHTMODE) { + if (temp > 10) { + AC[addr] = bin_bcd[temp - 10]; + addr = next_addr[addr]; + AC[addr] = 1; + } else { + AC[addr] = bin_bcd[temp]; + addr = next_addr[addr]; + AC[addr] = 10; + } + break; + } else if ((cpu_unit.flags & EMULATE3) == 0) + temp &= 03; + case CPU_7053: /* 705-iii */ + temp &= 07; + AC[addr] = bin_bcd[temp]; + if (AC[addr] != 10) + zero = 0; + break; + case CPU_705: /* 705 */ + temp &= 03; + AC[addr] = bin_bcd[temp]; + if (AC[addr] != 10) + zero = 0; + break; + } + if (temp != 0) + flags &= fmsk; /* Clear zero */ + addr = next_addr[addr]; + AC[addr] = 0; + sim_interval -= 5; /* count down */ + break; + + case OP_ULA: /* ULA */ + /* Unload address */ + if (CPU_MODEL < CPU_7053 || (MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + addr = get_acstart(reg); + t = ReadP(MA, MCHCHK) & 0360; /* Read unitsr digit */ + if (AC[addr] == 0) { + t |= 10; + } else { + t |= AC[addr] & 0xf; + addr = next_addr[addr]; + } + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK) & 0360; /* next digit */ + if (AC[addr] == 0) { + t |= 10; + } else { + t |= AC[addr] & 0xf; + addr = next_addr[addr]; + } + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK) & 0360; /* Read third digit */ + if (AC[addr] == 0) { + t |= 10; + } else { + t |= AC[addr] & 0xf; + addr = next_addr[addr]; + } + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MA, t); + Next(MA); + t = ReadP(MA, MCHCHK) & 0360; /* Save High order address */ + if (AC[addr] == 0) { + t |= 10; + } else { + t |= AC[addr] & 0xf; + addr = next_addr[addr]; + } + temp = 0; + /* Decode digits 5 and 6 */ + if (AC[addr] != 0) { + temp = bcd_bin[AC[addr] & 0xf]; + addr = next_addr[addr]; + if (AC[addr] != 0 && cpu_type == CPU_7080) + temp += (1 & bcd_bin[(AC[addr] & 0xf)]) * 10; + } + /* Add zone bits for top digit */ + temp = zone_dig[temp & 0xf]; + t &= 0xf; + t |= (temp & 0xc) << 2; + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MA, t); + /* Merge high order bits into units if needed */ + switch (cpu_type) { + case CPU_7080: /* 7080 */ + t = ReadP(MAC, MCHCHK) & 0xf; + t |= (temp & 0x3) << 4; + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MAC, t); + break; + case CPU_7053: /* 705-iii */ + /* Check if 80K machine */ + if ((cpu_unit.flags & EMULATE2) == 0) { + t = ReadP(MAC, MCHCHK) & 0x1f; + t |= (temp & 0x2) << 4; + if ((t & 0xf) == 10) + t &= 0360; + if (t == 0) + t = 10; + WriteP(MAC, t); + } + break; + case CPU_705: /* 705 */ + case CPU_702: /* 702 , Illegal on this machine */ + break; + } + sim_interval -= 5; /* count down */ + break; + + case OP_SND: /* SND */ + /* Only on 705/3 and above */ + /* Addresses must be on 5 char boundry */ + if (CPU_MODEL < CPU_7053 || (MAC2 % 5) != 4 || (MAC % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + selreg2 = 0; + break; + } + /* If RWW pending, SND does a memory check until end of block */ + if (selreg2 != 0) { + selreg2 = 0; + while((MAC % 200000) != 19999) { + (void)Read5(MAC, MCHCHK); + Prev5(MAC); + sim_interval -= 5; /* count down */ + } + break; + } + addr = get_acstart(reg); + while(AC[addr] != 0) { + uint32 v; + v = Read5(MAC, MCHCHK); + Write5(MAC2, v); + Prev5(MAC2); + Prev5(MAC); + addr = next_addr[addr]; + sim_interval -= 5; /* count down */ + } + break; + + case OP_BLM: /* BLM */ + /* Blank memory */ /* ASU 0 5 char, ASU 1 1 char */ + if (CPU_MODEL < CPU_7053) { + flags |= INSTFLAG|ANYFLAG; + break; + } + /* Blank in blocks of 5 characters */ + if (reg == 0) { + if ((MAC2 % 5) != 4) { + flags |= INSTFLAG|ANYFLAG; + break; + } + while(MAC > 0) { + Write5(MAC2, CHR_BLANK << (4 * 6)| + CHR_BLANK << (3 * 6)|CHR_BLANK << (2 * 6)| + CHR_BLANK << (1 * 6)|CHR_BLANK); + Prev5(MAC2); + MAC--; + sim_interval -= 5; /* count down */ + } + } else if (reg == 1) { + while(MAC > 0) { + WriteP(MAC2, CHR_BLANK); + Prev(MAC2); + MAC--; + sim_interval --; /* count down */ + } + } else { + flags |= INSTFLAG|ANYFLAG; + } + break; + + case OP_SBZ: /* SBZ|A|R|N */ + /* reg 1-6: bit# <- 0 */ + /* reg 7: bitA ^= 1 */ + /* reg 8: bit error ^= 1 */ + /* reg 9-14: bit# <- 1 */ + if (CPU_MODEL < CPU_7053) { + flags |= INSTFLAG|ANYFLAG; + break; + } + t = ReadP(MA, 0); + if (t & 0100) + flags |= MCHCHK|ANYFLAG; + sim_interval --; /* count down */ + switch(reg) { + case 0: /* Nop */ + break; + case 1: /* 1 */ + case 2: /* 2 */ + case 3: /* 4 */ + case 4: /* 8 */ + case 5: /* A */ + case 6: /* B */ + t &= ~(1<<(reg-1)); + break; + case 7: /* Reverse A */ + t ^= 020; + break; + case 8: /* Reverse C */ + t = M[MA % EMEMSIZE] ^ 0100; + break; + case 9: /* 1 */ + case 10: /* 2 */ + case 11: /* 4 */ + case 12: /* 8 */ + case 13: /* A */ + case 14: /* B */ + t |= 1<<(reg-9); + break; + } + WriteP(MA, t); + break; + + default: + flags |= ANYFLAG|INSTFLAG; + break; + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].flags = flags; + addr = get_acstart(reg); + for (t = 0; t < 254; t++) { + hst[hst_p].store[t] = AC[addr]; + addr = next_addr[addr]; + if (hst[hst_p].store[t] == 0) + break; + } + } + } + if (instr_count != 0 && --instr_count == 0) + return SCPE_STEP; + } /* end while */ + +/* Simulation halted */ + return reason; +} + + +/* Read and convert address of instruction */ +uint32 read_addr(uint8 *reg, uint8 *zone) { + uint8 t; + uint32 addr; + + t = ReadP(MA, INSTFLAG); /* Read low order digit */ + *zone = (t & 060) >> 2; + addr = bcd_bin[t & 0xf]; + if ((t & 0xf) > 10) /* Check valid numeric */ + flags |= INSTFLAG|ANYFLAG; + MA--; + t = ReadP(MA, INSTFLAG); /* next digit */ + *reg = (t & 060) >> 4; + if ((t & 0xf) > 10) + flags |= INSTFLAG|ANYFLAG; + addr += dig2[t & 0xf]; + MA--; + t = ReadP(MA, INSTFLAG); /* Read third digit */ + *reg |= (t & 060) >> 2; + if ((t & 0xf) > 10) + flags |= INSTFLAG|ANYFLAG; + addr += dig3[t & 0xf]; + MA--; + t = ReadP(MA, INSTFLAG); /* Save High order address */ + *zone |= (t & 060) >> 4; + if ((t & 0xf) > 10) + flags |= INSTFLAG|ANYFLAG; + addr += dig4[t & 0xf]; + MA--; + switch (cpu_type) { + case CPU_7080: /* 7080 */ + addr += dig_zone[*zone]; + *zone = 0; + break; + case CPU_7053: /* 705-iii */ + addr += dig_zone[*zone & 013]; + *zone &= 04; + break; + case CPU_705: /* 705 */ + /* Can't have any value */ + addr += dig_zone[*zone & 03]; + *zone &= 014; + break; + case CPU_702: /* 702 */ + if (*zone == 02) { /* B bit in highest digit select AC B */ + *reg = 1; + } else if (*zone != 0) + flags |= INSTFLAG|ANYFLAG; + *zone = 0; + break; + } + return addr; +} + +/* Write converted address of instruction */ +void write_addr(uint32 addr, uint8 reg, uint8 zone) { + uint8 value[4]; + int i; + + if ((MA % 5) != 0) { + flags |= INSTFLAG|ANYFLAG; + return; + } + /* Convert address into BCD first */ + for(i = 0; i < 4; i++) { + value[i] = bin_bcd[addr % 10]; + addr /= 10; + } + + addr = zone_dig[addr & 0xf]; + /* Decode extra addresses and ASU setting */ + switch (cpu_type) { + case CPU_7080: /* 7080 */ + value[0] |= (addr & 03) << 4; + value[3] |= (addr & 0xc) << 2; + break; + case CPU_7053: /* 705-iii */ + /* If 80k emulation */ + if ((cpu_unit.flags & EMULATE2) == 0) + value[0] |= (addr & 02) << 4; + value[3] |= (addr & 0xc) << 2; + break; + case CPU_705: /* 705 */ + /* If doing 40k machine */ + if ((cpu_unit.flags & EMULATE2)) + value[3] |= (addr & 0xc) << 2; + else /* 20k */ + value[3] |= (addr & 0x8) << 2; + break; + case CPU_702: /* 702 */ + if (reg == 1) + value[3] |= 040; /* Set minus */ + reg = 0; + break; + } + value[2] |= (reg & 03) << 4; + value[1] |= (reg & 014) << 2; + + /* Or in zone values */ + value[0] |= (zone & 014) << 2; + value[3] |= (zone & 03) << 4; + + /* Write it out to memory backwards */ + for(i = 0; i< 4; i++) { + MA--; + if ((value[i] & 0xf) == 10) + value[i] &= 0360; + if (value[i] == 0) + value[i] = 10; + WriteP(MA, value[i]); + } +} + +/* Store converted address in storage */ +void store_addr(uint32 addr, int loc) { + uint8 value[4]; + int i; + + /* Convert address into BCD first */ + value[0] = bin_bcd[addr % 10]; + addr /= 10; + value[1] = bin_bcd[addr % 10]; + addr /= 10; + value[2] = bin_bcd[addr % 10]; + addr /= 10; + value[3] = bin_bcd[addr % 10]; + addr /= 10; + addr = zone_dig[addr & 0xf]; + switch (cpu_type) { + case CPU_7080: /* 7080 */ + value[0] |= (addr & 03) << 4; + value[3] |= (addr & 0xc) << 2; + break; + case CPU_7053: /* 705-iii */ + /* If 80k emulation */ + if ((cpu_unit.flags & EMULATE2) == 0) + value[0] |= (addr & 02) << 4; + value[3] |= (addr & 0xc) << 2; + break; + case CPU_705: /* 705 */ + /* If doing 40k machine */ + if ((cpu_unit.flags & EMULATE2)) + value[3] |= (addr & 0xc) << 2; + else /* 20k */ + value[3] |= (addr & 0x8) << 2; + break; + case CPU_702: /* 702 */ + break; + } + + /* Write it out to storage backwards */ + for(i = 0; i< 4; i++) { + if ((value[i] & 0xf) == 10) + value[i] &= 0360; + if (value[i] == 0) + value[i] = 10; + AC[loc] = value[i]; + loc = next_addr[loc]; + } +} + + +/* Read address from storage */ +uint32 load_addr(int loc) { + uint8 t; + uint8 zone; + uint32 addr; + + t = AC[loc]; /* First digit */ + loc = next_addr[loc]; + zone = (t & 060) >> 2; + addr = bcd_bin[t & 0xf]; + t = AC[loc]; /* Second digit */ + loc = next_addr[loc]; + addr += dig2[bcd_bin[t & 0xf]]; + t = AC[loc]; /* Read third digit */ + loc = next_addr[loc]; + addr += dig3[bcd_bin[t & 0xf]]; + t = AC[loc]; /* Save High order address */ + loc = next_addr[loc]; + zone |= (t & 060) >> 4; + addr += dig4[bcd_bin[t & 0xf]]; + switch (cpu_type) { + case CPU_7080: /* 7080 */ + break; + case CPU_7053: /* 705-iii */ + /* If doing 40k */ + if (cpu_unit.flags & EMULATE2) + zone &= 3; /* 40k */ + else + zone &= 013; /* 80k */ + break; + case CPU_705: /* 705 */ + if (cpu_unit.flags & EMULATE2) + zone &= 3; /* 40K */ + else + zone &= 1; /* 20k */ + break; + case CPU_702: /* 702 */ + zone = 0; /* 10k Memory */ + break; + } + addr += dig_zone[zone]; + return addr; +} + +/* Store converted hex address in storage */ +void store_hex(uint32 addr, int loc) { + /* Convert address into BCD first */ + AC[loc] = bin_bcd[addr & 0xf]; + loc = next_addr[loc]; + AC[loc] = bin_bcd[(addr >> 4) & 0xf]; + loc = next_addr[loc]; + AC[loc] = bin_bcd[(addr >> 8) & 0xf]; + loc = next_addr[loc]; + AC[loc] = bin_bcd[(addr >> 12) & 0xf]; +} + +/* Read hex address from storage */ +uint32 load_hex(int loc) { + uint8 t; + uint32 addr; + + t = AC[loc]; /* First digit */ + addr = bcd_bin[t & 0xf]; + loc = next_addr[loc]; + t = AC[loc]; /* Second digit */ + addr += bcd_bin[t & 0xf] << 4; + loc = next_addr[loc]; + t = AC[loc]; /* Read third digit */ + addr += bcd_bin[t & 0xf] << 8; + loc = next_addr[loc]; + t = AC[loc]; /* Save High order address */ + addr += bcd_bin[t & 0xf] << 12; + return addr; +} + + +/* Compute starting point in Storage for accumulator */ +uint16 get_acstart(uint8 reg) { + if (reg == 0) + return spc; + if (cpu_type == CPU_702) { + return spcb; + } else { + uint16 addr; + addr = (spc & 0x700) | 0x100 | ((reg - 1) << 4); + if (addr > 0x4ff) + addr &= 0x4ff; + return addr; + } +} + +/* Store CPU state in CASU 15 */ +void store_cpu(uint32 addr, int full) { + uint8 t; + + store_addr(IC, addr); + addr = next_addr[addr]; + addr = next_addr[addr]; + addr = next_addr[addr]; + addr = next_addr[addr]; + /* Save status characters */ + t = flags & 0xf; + AC[addr] = 040 | ((t + 8) & 027); + addr = next_addr[addr]; + t = (flags >> 4) & 0xf; + AC[addr] = 040 | ((t + 8) & 027); + addr = next_addr[addr]; + t = (flags >> 8) & 0xf; + AC[addr] = 040 | ((t + 8) & 027); + addr = next_addr[addr]; + t = (flags >> 12) & 0x3; + AC[addr] = 040 | t; + if (full) { + addr = next_addr[addr]; + AC[addr] = bin_bcd[spc & 7]; + addr = next_addr[addr]; + AC[addr] = bin_bcd[(spc >> 3) & 3]; + addr = next_addr[addr]; + AC[addr] = bin_bcd[(spc >> 5) & 7]; + addr = next_addr[addr]; + AC[addr] = bin_bcd[(spc >> 8) & 7]; + addr = next_addr[addr]; + for(; addr < 0x3F8; addr++) + AC[addr] = 10; + for(; addr < 0x400; addr++) + AC[addr] = 0; + store_addr(MAC2, 0x3F0); + store_hex(selreg, 0x3F8); + } +} + +/* Load CPU State from storage */ +void load_cpu(uint32 addr, int full) { + uint8 t; + + IC = load_addr(addr); + addr = next_addr[addr]; + addr = next_addr[addr]; + addr = next_addr[addr]; + addr = next_addr[addr]; + flags = 0; + t = AC[addr++]; + flags |= (t & 0x7) | ((t >> 1) & 0x8); + t = AC[addr++]; + flags |= ((t & 0x7) | ((t >> 1) & 0x8)) << 4; + t = AC[addr++]; + flags |= ((t & 0x7) | ((t >> 1) & 0x8)) << 8; + t = AC[addr++]; + flags |= (t & 0x3) << 12; + /* Adjust Max memory if mode changed */ + EMEMSIZE = MEMSIZE; + if (flags & EIGHTMODE) { + cpu_type = CPU_7080; + } else { + cpu_type = (cpu_unit.flags & EMULATE3)? CPU_7053:CPU_705; + EMEMSIZE = MEMSIZE; + if (cpu_unit.flags & EMULATE2 && EMEMSIZE > 40000) + EMEMSIZE = 40000; + if (cpu_type == CPU_705 && (cpu_unit.flags & EMULATE2) == 0 + && EMEMSIZE > 20000) + EMEMSIZE = 20000; + if (EMEMSIZE > 80000) + EMEMSIZE = 80000; + } + if (full) { + spc = bcd_bin[AC[addr++]] & 07; /* Units digit */ + /* One of words */ + spc += (bcd_bin[AC[addr++]] & 3) << 3; /* Tens digit */ + /* One of four word sets */ + spc += (bcd_bin[AC[addr++]] & 7) << 5; /* Hundreds */ + /* Bank */ + spc += (bcd_bin[AC[addr++]] & 7) << 8; /* Thousands */ + addr += 4; + MAC2 = load_addr(addr); + addr += 8; + selreg = load_hex(addr); + } +} + +/* Do add or subtract instruction. + mode == 1 for subtract + mode == 0 for addition. + Register is ASU or zero for A. + smt == 0 if ADD/SUB + smt == 1 if RSU/RAD + fmsk is the flags mask to set or clear */ +t_stat do_addsub(int mode, int reg, int smt, uint16 fmsk) { + uint8 cr1, cr2; + int sign; + int msign; + int carry; + uint32 addr; + int met = 1; + int addsub; + + addr = get_acstart(reg); + cr1 = ReadP(MA, MCHCHK); + Next(MA); + sim_interval --; /* count down */ + /* Check sign if not valid then treat as 0 */ + msign = 0; + switch(cr1 & 060) { + case 000: + case 020: + flags |= SGNFLAG|ANYFLAG; + case 060: + break; + case 040: + msign = 1; + break; + } + /* Fix cr1 to decimal */ + cr1 &= 0xf; + + /* Set sign to sign of Ac */ + sign = (flags & fmsk & SIGN)?1:0; + + /* Set Zero and clear Sign */ + flags |= fmsk & ZERO; + flags &= ~(fmsk & SIGN); + + /* Decide mode of operation */ + addsub = 0; + if (smt) { + sign = (mode)?(!msign):msign; /* Fix sign */ + cr2 = 0; /* After end, force zero */ + } else { + if(mode) { /* Decide mode based on signs */ + if (sign == msign) + addsub = 1; + } else { + if (sign != msign) + addsub = 1; + } + cr2 = AC[addr]; + if (cr2 == 0) /* Check for storage mark */ + smt = 0; /* Done storage */ + } + + smt = !smt; + carry = addsub; + + /* Process while valid digit in memory */ + while(smt || met) { + cr2 &= 0xf; + cr1 = bcd_bin[cr1&0xf] + ((addsub)? comp_bcd[cr2]: bcd_bin[cr2]) + + carry; + carry = cr1 >= 10; + AC[addr] = bin_bcd[cr1]; + /* Update zero flag */ + if (cr1 != 0 && cr1 != 10) + flags &= ~(fmsk & ZERO); + addr = next_addr[addr]; + if (met) { + cr1 = ReadP(MA, MCHCHK); + if (cr1 == 0 || cr1 > 10) { + met = 0; /* End of memory */ + cr1 = 0; /* zero */ + } + Next(MA); + } else { + cr1 = 0; /* Force to zero */ + } + /* Grab storage value */ + if (smt) { + cr2 = AC[addr]; + if (cr2 == 0) /* Check for storage mark */ + smt = 0; /* Done storage */ + } else { + cr2 = 0; /* After end, force zero */ + } + sim_interval --; /* count down */ + } + AC[addr] = 0; /* Force storage mark */ + + /* Handle last digit */ + if (carry) { + if (addsub) { + sign = !sign; + } else { + /* Overflow, extend by one digit */ + AC[addr] = 1; + addr = next_addr[addr]; + AC[addr] = 0; /* Storage mark */ + flags |= ACOFLAG|ANYFLAG; + flags &= ~(fmsk & ZERO); + } + } else { + if (addsub) { + /* Recomplement storage */ + addr = get_acstart(reg); + carry = 1; + flags |= fmsk & ZERO; + while ( AC[addr] != 0) { + cr2 = AC[addr]; + cr2 = comp_bcd[cr2] + carry; + carry = cr2 >= 10; /* Update carry */ + AC[addr] = bin_bcd[cr2]; + /* Update zero flag */ + if (cr2 != 0 && cr2 != 10) + flags &= ~(fmsk & ZERO); + addr = next_addr[addr]; + sim_interval --; /* count down */ + } + } + } + + /* Update sign and zero */ + flags |= (fmsk & SIGN) & (sign | (sign << 1)); + flags &= ~(((flags & ZERO) >> 2) & fmsk); + return SCPE_OK; +} + +/* Multiply memory to AC */ +t_stat +do_mult(int reg, uint16 fmsk) +{ + uint8 t; + uint8 at; + uint8 cr1, cr2; + uint16 addr; + uint16 prod; + int mult; + int msign = 0; + + /* Type I cycle */ + addr = get_acstart(reg); + mult = AC[addr]; + AC[addr] &= 0xf; + if (AC[addr] == 0) /* If initial storage mark, replace */ + AC[addr] = 10; /* With zero */ + prod = next_half[addr]; + flags |= fmsk & ZERO; + t = 1; + at = 0; + /* Check for mark */ + while (mult != 0) { + /* Type II */ + /* Check signs of B and A. */ + cr1 = ReadP(MA, MCHCHK); + sim_interval --; /* count down */ + Next(MA); + /* Compute sign */ + if (t) { + switch(cr1 & 060) { + case 000: + case 020: + flags |= SGNFLAG|ANYFLAG; + case 060: + break; + case 040: + msign = fmsk & SIGN; + break; + } + t = 0; + cr1 = bin_bcd[cr1 & 0xf]; + } + mult = bcd_bin[mult & 0xf]; + /* Type III */ + cr2 = 0; + while(cr1 >= 1 && cr1 <= 10 ) { + cr2 += mult * bcd_bin[cr1]; + if (at) + cr2 += bcd_bin[AC[prod]]; + AC[prod] = bin_bcd[cr2 % 10]; + if (AC[prod] != 10) + flags &= ~(fmsk & ZERO); + cr2 /= 10; + prod = next_addr[prod]; + cr1 = ReadP(MA, MCHCHK); + Next(MA); + sim_interval --; /* count down */ + } + if (cr2 != 0) + flags &= ~(fmsk & ZERO); + AC[prod] = bin_bcd[cr2]; + prod = next_addr[prod]; + AC[prod] = 0; /* Set storage mark */ + /* Type IV */ + at = 1; /* Parcial product exists */ + addr = next_addr[addr]; + prod = next_half[addr]; /* Were to put results */ + mult = AC[addr]; /* Grab next digit */ + AC[addr] &= 0xf; /* Clear zone */ + MA = MAC; /* Back to start of field */ + t = 1; /* Set to handle sign */ + } + + /* Type V */ + /* Update position */ + addr = get_acstart(reg); + addr = next_half[addr]; /* Adjust pointer */ + + if (CPU_MODEL == CPU_702 && reg != 0) { + spcb = addr; + } else { + if (CPU_MODEL == CPU_702) + spc = addr; + else if (reg == 0) + spc = (spc & 0x700) | (addr & 0xff); + } + + /* Update sign and zero */ + flags ^= msign; + flags &= ~(((flags & ZERO) >> 2) & fmsk); + return SCPE_OK; +} + +t_stat +do_divide(int reg, uint16 fmsk) +{ + int cr1; + int cr2; + int tsac; + int tspc; + int at; + int smt; + int msign; + int remtrig; + int carry; + int dzt; + + /* Step I, put storage mark before start of AC */ + at = 0; + tspc = get_acstart(reg); + AC[prev_addr[tspc]] = 0; + smt = 1; + carry = 0; + msign = 0; + + /* Step II, step address until we find storage mark */ +step2: + while(AC[tspc] != 0) { + AC[tspc] &= 0xf; /* Make all numeric */ + tspc = next_addr[tspc]; + sim_interval --; /* count down */ + } + + tsac = next_half[tspc]; + tspc = prev_addr[tspc]; + /* Step III, step second address 128/256 locations. */ + dzt = 1; + if (at) { + tspc = next_half[tspc]; + goto done; + } + AC[tsac] = 0; + at = 1; + smt = 0; + tsac = tspc; + sim_interval --; /* count down */ + + /* Step IV, back up first address while advancing MA */ + do { + sim_interval --; /* count down */ + cr1 = ReadP(MA, MCHCHK); + if (AC[tsac] == 0) { /* Short */ + tsac = next_addr[tsac]; + tspc = tsac; + goto done; + } + if (at) { + switch(cr1 & 060) { + case 000: + case 020: + flags |= SGNFLAG|ANYFLAG; + /* Fall through */ + case 060: + msign = 0; + break; + case 040: + msign = (fmsk & SIGN); + break; + } + at = 0; + } else if (cr1 == 0 || cr1 > 10) { /* Next sign digit */ + at = 1; + MA = MAC; + tspc = tsac; + goto step5; + } + tsac = prev_addr[tsac]; + Next(MA); + } while(1); /* Next sign digit */ + + /* Type V, perform first subtract */ +step5: + remtrig = 0; + MA = MAC; + while (1) { + /* Step V, subtract Memory from storage */ + cr1 = ReadP(MA, MCHCHK); + cr2 = AC[tsac]; + sim_interval --; /* count down */ + if (cr2 == 0) { + tspc = next_addr[tspc]; + goto step9; + } else if (at) { + carry = 1; + cr1 &= 017; + at = 0; + } else if (cr1 == 0 || cr1 > 10) { + cr1 = comp_bcd[cr2] + carry; + carry = cr1 >= 10; + AC[tsac] = bin_bcd[cr1]; + MA = MAC; + tsac = next_half[tsac]; + at = 1; + goto step6; + } + Next(MA); + cr1 = comp_bcd[cr2] + bcd_bin[cr1] + carry; + carry = cr1 >= 10; + AC[tsac] = bin_bcd[cr1]; + if (AC[tsac] != 10) + remtrig = 1; + tsac = next_addr[tsac]; + } + +step6: + cr2 = AC[tsac]; + cr1 = 1; + if (carry) { + smt = 0; + if (remtrig) { + if (at) { + AC[tsac] = 10; + } else { + at = 1; + } + tsac = tspc; + goto step8; + } else { + int t; + if (at) + cr2 = 0; + else + cr2 = bin_bcd[cr2]; + t = cr2 + 1; + AC[tsac] = bin_bcd[t]; + tsac = tspc; + if (t >= 10) { + flags |= ACOFLAG|ANYFLAG; + at = 1; + goto step2; + } + dzt = 0; + at = 0; + goto step9; + } + } else { + int t; + if (at) + cr2 = 0; + else + cr2 = bcd_bin[cr2]; + t = cr2 + 1; + AC[tsac] = bin_bcd[t]; + tsac = tspc; + remtrig = 0; + at = 1; + if (t >= 10) { + flags |= ACOFLAG|ANYFLAG; + goto step2; + } + dzt = 0; + } + smt = 0; + while(!smt) { + cr1 = ReadP(MA, MCHCHK); + Next(MA); + sim_interval --; /* count down */ + cr2 = AC[tsac]; + if (cr2 == 0) { + smt = 1; /* Check usage here */ + break; /* goto step6; */ + } + if (at) { + cr1 &= 017; + at = 0; + } else if (cr1 == 0 || cr1 > 10) { + cr2 = bcd_bin[cr2] + carry; + carry = cr2 >= 10; + AC[tsac] = bin_bcd[cr2]; + if (AC[tsac] != 10) + remtrig = 1; + MA = MAC; + tsac = next_half[tsac]; + break; /* goto step6; */ + } + cr2 = bcd_bin[cr2] + bcd_bin[cr1] + carry; + carry = cr2 >= 10; + AC[tsac] = bin_bcd[cr2]; + if (AC[tsac] != 10) + remtrig = 1; + tsac = next_addr[tsac]; + } + goto step6; + +step8: + smt = 0; + while(!smt) { + cr1 = ReadP(MA, MCHCHK); + Next(MA); + sim_interval --; /* count down */ + cr2 = AC[tsac]; + if (cr2 == 0) + smt = 1; + if (at) { + at = 0; + cr1 &= 017; + carry = 1; + } else { + if (cr1 == 0 || cr1 > 10) { + cr2 = comp_bcd[cr2] + carry; + carry = cr2 >= 10; + AC[tsac] = bin_bcd[cr2]; + MA = MAC; + tsac = tspc; + goto step9; + } + } + cr2 = comp_bcd[cr2] + bcd_bin[cr1] + carry; + carry = cr2 >= 10; + AC[tsac] = bin_bcd[cr2]; + tsac = next_addr[tsac]; + }; + + /* Step 9 */ +step9: + if (at) { + tspc = next_half[tspc]; + Next(MA); + goto step10; + } else { + tsac = prev_addr[tsac]; + tspc = prev_addr[tspc]; + remtrig = 0; + at = 1; + goto step5; + } + + /* Step X */ +step10: + do { + cr1 = ReadP(MA, MCHCHK); + Next(MA); + sim_interval --; /* count down */ + tspc = next_addr[tspc]; + } while (cr1 > 0 && cr1 <= 10); +done: + if (CPU_MODEL == CPU_702) + spc = tspc; + else + spc = (spc & 0x700) | (tspc & 0xff); + + if (dzt) + flags |= (fmsk & ZERO); + else + flags &= ~(fmsk & ZERO); + + /* Update sign and zero */ + flags ^= msign; + flags &= ~(((flags & ZERO) >> 2) & fmsk); + return SCPE_OK; +} + +t_stat +do_compare(int reg, int tluop) { + int addr; + uint8 cr1; + uint8 cr2; + + + addr = get_acstart(reg); + flags &= ~CMPFLAG; + while(AC[addr] != 0) { + int sup8; + cmpnext: + cr2 = AC[addr]; + if (cr2 == 0) + break; + cr1 = ReadP(MA, MCHCHK); + sim_interval--; /* count down */ + /* Stop table opcode on GM or RM */ + if (tluop && (cr1 == CHR_GM || cr1 == CHR_RM)) { + bkcmp = 0; + return SCPE_OK; + } + if ((cr1 & 0xf) > 10) + sup8 = 007; + else + sup8 = 017; + if(bkcmp) { + Prev(MA); + } else { + Next(MA); + } + addr = next_addr[addr]; + if (cr1 == CHR_BLANK) { + if (cr2 != CHR_BLANK) { + flags &= ~CMPFLAG; + flags |= HIGHFLAG; + } + goto cmpnext; + } + if (cr2 == CHR_BLANK) { + flags &= ~CMPFLAG; + flags |= LOWFLAG; + } else { + int t1 = cr1 & 017; + int t2 = cr2 & 017; + if ((t1 == 11) || (t1 == 12)) { /* CR1 Special? */ + if ((t2 != 11) && (t2 != 12)) { /* CR2 not special */ + flags &= ~CMPFLAG; + flags |= HIGHFLAG; + goto cmpnext; + } + } else if ((t2 == 11) || (t2 == 12)) {/* CR2 special */ + if ((t1 != 11) && (t1 != 12)) { /* CR1 not special */ + flags &= ~CMPFLAG; + flags |= LOWFLAG; + goto cmpnext; + } + } + if ((cr1 & 060) != (cr2 & 060)) { /* Check zones */ + flags &= ~CMPFLAG; + t1 = (cr1 & 060) + (060 ^ (cr2 & 060)); + flags |= (t1 & 0100) ? HIGHFLAG:LOWFLAG; + } else { /* Zones same */ + if ((cr1 == 040) || (cr1 == 060)) { + if ((cr2 != 040) && (cr2 != 060)) { + flags &= ~CMPFLAG; + flags |= LOWFLAG; + goto cmpnext; + } + } else if ((cr2 == 040) || (cr2 == 060)) { + flags &= ~CMPFLAG; + flags |= HIGHFLAG; + goto cmpnext; + } + /* Compare actual digits */ + t1 = bcd_bin[t1 & sup8] + comp_bcd[t2] + 1; + if (t1 != 10) { + flags &= ~CMPFLAG; + flags |= (t1 <= 10)?HIGHFLAG:LOWFLAG; + } + } + } + } + bkcmp = 0; + return SCPE_OK; +} + + +/* Initialize memory to all blank */ +void +mem_init() { + int i; + /* Force memory to be blanks on load */ + for(i = 0; i < (MAXMEMSIZE-1); i++) + M[i] = CHR_BLANK; + MEMSIZE = (((cpu_unit.flags & UNIT_MSIZE) >> UNIT_V_MSIZE) + 1) * 10000; + EMEMSIZE = MEMSIZE; +} + + +/* Reset routine */ +t_stat +cpu_reset(DEVICE * dptr) +{ + int i; + int n,p,h; + + /* Set next and previous address arrays based on CPU type */ + if (CPU_MODEL == CPU_702) { + for(i = 0; i < 512; i++) { + n = (i + 1) & 0777; /* A */ + p = (i - 1) & 0777; + h = (i + 256) & 0777; + next_addr[i] = n; /* A */ + prev_addr[i] = p; + next_half[i] = h; + next_addr[i+512] = 512 + n; /* B */ + prev_addr[i+512] = 512 + p; + next_half[i+512] = 512 + h; + } + cpu_reg[1].depth = 512; + cpu_reg[2].offset = 512; + cpu_reg[2].depth = 512; + cpu_reg[2].loc = &AC[512]; + } else { + for(i = 0; i < 256; i++) { + n = next_addr[i] = (i + 1) & 0377; /* A */ + p = prev_addr[i] = (i - 1) & 0377; + h = next_half[i] = (i + 128) & 0377; + next_addr[i+256] = 256 + n; /* Bank 1 */ + prev_addr[i+256] = 256 + p; + next_half[i+256] = 256 + h; + next_addr[i+512] = 512 + n; /* Bank 2 */ + prev_addr[i+512] = 512 + p; + next_half[i+512] = 512 + h; + next_addr[i+768] = 768 + n; /* Bank 3 */ + prev_addr[i+768] = 768 + p; + next_half[i+768] = 768 + h; + next_addr[i+1024] = 1024 + n; /* Bank 4 */ + prev_addr[i+1024] = 1024 + p; + next_half[i+1024] = 1024 + h; + next_addr[i+1280] = 1280 + n; /* Bank 5 */ + prev_addr[i+1280] = 1280 + p; + next_half[i+1280] = 1280 + h; + } + cpu_reg[1].depth = 256; + cpu_reg[2].offset = 256; + for(i = 0; i < 15; i++) { + cpu_reg[i+2].loc = &AC[256 + 16*i]; + cpu_reg[i+2].depth = 256; + } + } + + /* Clear io error flags */ + memset(ioflags, 0, sizeof(ioflags)); + /* Clear accumulators to storage mark */ + memset(AC, 0, sizeof(AC)); + flags = 0; + intmode = 0; + intprog = 0; + irqflags = 0; + selreg = 0; + selreg2 = 0; + IC = 4; + sim_brk_types = sim_brk_dflt = SWMASK('E'); + return SCPE_OK; +} + +/* Memory examine */ + +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = M[addr] & 077; + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + M[addr] = val & 077; + return SCPE_OK; +} + +t_stat +cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + t_uint64 mc = 0; + uint32 size; + uint32 i; + + size = val >> UNIT_V_MSIZE; + size++; + size *= 10000; + if (size > MAXMEMSIZE) + return SCPE_ARG; + for (i = size-1; i < MEMSIZE; i++) { + if (M[i] != CHR_BLANK) { + mc = 1; + break; + } + } + if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + cpu_unit.flags &= ~UNIT_MSIZE; + cpu_unit.flags |= val; + EMEMSIZE = MEMSIZE = size; + for (i = MEMSIZE - 1; i < (MAXMEMSIZE-1); i++) + M[i] = CHR_BLANK; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].ic = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + int len; + t_stat r; + t_value sim_eval[50]; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "IC OP MA REG\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ic & HIST_PC) { /* instruction? */ + fprintf(st, "%06d %c %06d %02d ", h->ic & 0x3ffff, + mem_to_ascii[h->op], h->ea, h->reg); + sim_eval[0] = (h->inst >> (4 * 6)) & 077; + sim_eval[1] = (h->inst >> (3 * 6)) & 077; + sim_eval[2] = (h->inst >> (2 * 6)) & 077; + sim_eval[3] = (h->inst >> (1 * 6)) & 077; + sim_eval[4] = h->inst & 077; + (void)fprint_sym (st, h->ic, sim_eval, &cpu_unit, SWMASK('M')); + for(len = 0; len < 256 && (h->store[len] & 077) != 0; len++); + fprintf(st, "\t%-2d %c%c %c%c %c@", len, + (h->flags & AZERO)?'Z':' ', (h->flags & ASIGN)?'-':'+', + (h->flags & BZERO)?'Z':' ', (h->flags & BSIGN)?'-':'+', + (h->flags & LOWFLAG)? 'l' : + ((h->flags & HIGHFLAG) ? 'h' : 'e')); + + for(len--; len >= 0; len--) + fputc(mem_to_ascii[h->store[len] & 077], st); + fputc('@', st); + if (h->flags & 0x7f0) { + int i; + fputc(' ', st); + for (i = 0; i < 7; i++) { + if (h->flags & (0x10 << i)) + fputc('0' + i, st); + } + } + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ + return "IBM 7080 CPU"; +} + +t_stat +cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "The CPU can be set to a IBM 702, IBM 705, IBM 705/3 or IBM 7080\n"); + fprintf (st, "The type of CPU can be set by one of the following commands\n\n"); + fprintf (st, " sim> set CPU 702 sets IBM 704 emulation\n"); + fprintf (st, " sim> set CPU 705 sets IBM 705 emulation\n"); + fprintf (st, " sim> set CPU 7053 sets IBM 705/3 emulation\n"); + fprintf (st, " sim> set CPU 7080 sets IBM 7080 emulation\n\n"); + fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); + fprintf (st, " -c examine/deposit characters\n"); + fprintf (st, " -s examine 50 characters\n"); + fprintf (st, " -d examine 50 characters\n"); + fprintf (st, " -m examine/deposit IBM 7080 instructions\n\n"); + fprintf (st, "The memory of the CPU can be set in 10K incrememts from 10K to 160K with the\n\n"); + fprintf (st, " sim> SET CPU xK\n\n"); + fprintf (st, "For the IBM 7080 the following options can be enabled\n\n"); + fprintf (st, " sim> SET CPU EMU40K enables memory above 40K\n"); + fprintf (st, " sim> SET CPU NOEMU40K disables memory above 40K\n\n"); + fprintf (st, " sim> SET CPU EMU705 enables IBM7080 to support 705 Emulation.\n"); + fprintf (st, " sim> SET CPU NOEMU705 disables IBM7080 to support 705 Emulation.\n\n"); + fprintf (st, " sim> SET CPU NOSTOP CPU will not stop on invalid conditions\n"); + fprintf (st, " sim> SET CPU PRORAM CPU stop under program control\n\n"); + fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n"); + fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n"); + fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); + fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); + fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); + fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + + return SCPE_OK; +} + diff --git a/I7000/i7080_defs.h b/I7000/i7080_defs.h new file mode 100644 index 00000000..ea91ead0 --- /dev/null +++ b/I7000/i7080_defs.h @@ -0,0 +1,103 @@ +/* i7080_defs.h: IBM 7080 simulator definitions + + Copyright (c) 2006-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 "sim_defs.h" /* simulator defns */ + +#include "i7000_defs.h" + +/* Memory */ +#define MEM_ADDR_OK(x) ((x) < MEMSIZE) +extern uint8 M[MAXMEMSIZE]; +extern uint32 EMEMSIZE; /* Size of emulated memory */ + +/* Issue a command to a channel */ +int chan_cmd(uint16 dev, uint16 cmd, uint32 addr); +/* Map device to channel */ +int chan_mapdev(uint16 dev); +/* Process the CHR 3 13 command and abort all channel activity */ +void chan_chr_13(); +uint32 load_addr(int loc); +void store_addr(uint32 addr, int loc); + +/* Opcode definitions. */ +#define OP_TR CHR_1 +#define OP_SEL CHR_2 +#define OP_CTL CHR_3 +#define OP_CMP CHR_4 +#define OP_SPR CHR_5 +#define OP_ADM CHR_6 +#define OP_UNL CHR_7 +#define OP_LOD CHR_8 +#define OP_TMT CHR_9 +#define OP_TRS CHR_O +#define OP_NOP CHR_A +#define OP_SET CHR_B +#define OP_SHR CHR_C +#define OP_LEN CHR_D +#define OP_RND CHR_E +#define OP_ST CHR_F +#define OP_ADD CHR_G +#define OP_RAD CHR_H +#define OP_TRA CHR_I +#define OP_HLT CHR_J +#define OP_TRH CHR_K +#define OP_TRE CHR_L +#define OP_TRP CHR_M +#define OP_TRZ CHR_N +#define OP_SUB CHR_P +#define OP_RSU CHR_Q +#define OP_WR CHR_R +#define OP_RWW CHR_S +#define OP_SGN CHR_T +#define OP_RCV CHR_U +#define OP_MPY CHR_V +#define OP_DIV CHR_W +#define OP_NTR CHR_X +#define OP_RD CHR_Y +#define OP_WRE CHR_Z +#define OP_AAM CHR_QUOT +#define OP_CTL2 CHR_COM +#define OP_LDA CHR_EQ +#define OP_ULA CHR_STAR +#define OP_SND CHR_SLSH +#define OP_BLM CHR_DOL +#define OP_SBZ CHR_RPARN +#define OP_TZB CHR_DOT +#define OP_CTL3 CHR_LPARN +#define OP_SMT CHR_TRM + +/* Channel options */ +#define CHAN_NOREC 0001 /* Don't stop at record */ +#define CHAN_8BIT 0002 /* Send 8 bit data */ +#define CHAN_SNS 0010 /* Issue sense command */ +#define CHAN_CTL 0020 /* Issue control command */ +#define CHAN_ZERO 0040 /* Zero memory after write */ +#define CHAN_SKIP 0040 /* Don't read data */ +#define CHAN_END 0100 /* Last location */ +#define CHAN_RECCNT 0200 /* Last was set record counter */ +#define CHAN_CMD 0400 /* Opcode in high order bits */ + +#define CHAN_AFULL 01000 /* A buffer has data */ +#define CHAN_BFULL 02000 /* B buffer has data */ +#define CHAN_BFLAG 04000 /* Write/read B buffer */ + diff --git a/I7000/i7080_drum.c b/I7000/i7080_drum.c new file mode 100644 index 00000000..bcc0183a --- /dev/null +++ b/I7000/i7080_drum.c @@ -0,0 +1,195 @@ +/* i7080_drum.c: IBM 7080 Drum + + Copyright (c) 2007-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. + + Provides support for 702/705 drums. + + Drums are arranged in tracks of 200 characters each. + + Writing continues until a end of record is recieved. At which point a + drum mark is written. If more then 200 characters are written the next + track is automaticaly selected. + + Reading continues until a drum mark is read. + + Drums address is 1000-1999 + +*/ + +#include "i7080_defs.h" + +#ifdef NUM_DEVS_DR +#define UNIT_DRM UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | \ + UNIT_BUFABLE | UNIT_MUSTBUF + +/* Device status information stored in u5 */ +#define DRMSTA_READ 000001 /* Unit is in read */ +#define DRMSTA_WRITE 000002 /* Unit is in write */ +#define DRMSTA_CMD 000004 /* Unit has recieved a cmd */ +#define DRMSTA_START 000200 /* Drum has started to transfer */ +#define DRMWORDTIME 20 /* Number of cycles per drum word */ +#define DRMCHARTRK 200 /* Characters per track */ + +uint32 drm_cmd(UNIT *, uint16, uint16); +t_stat drm_srv(UNIT *); +t_stat drm_boot(int32, DEVICE *); +void drm_ini(UNIT *, t_bool); +t_stat drm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *drm_description (DEVICE *dptr); +extern t_stat chan_boot(int32, DEVICE *); + +UNIT drm_unit[] = { + {UDATA(&drm_srv, UNIT_S_CHAN(0) | UNIT_DRM, DRMCHARTRK * 1000), 0, 0}, +}; + +DEVICE drm_dev = { + "DR", drm_unit, NULL /* Registers */ , NULL, + 1, 8, 15, 1, 8, 8, + NULL, NULL, NULL, &drm_boot, NULL, NULL, + &drm_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &drm_help, NULL, NULL, &drm_description +}; + +uint32 drm_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int addr = dev; + + addr -= drm_dib.addr * DRMCHARTRK; + if (addr > (int32)uptr->capac) + return SCPE_NODEV; + if ((uptr->flags & UNIT_ATT) != 0) { + switch (cmd) { + case IO_RDS: + /* Start device */ + uptr->u5 = DRMSTA_READ | DRMSTA_CMD; + sim_debug(DEBUG_CMD, &drm_dev, "RDS %o\n", dev); + chan_set_sel(chan, 0); + break; + case IO_WRS: + /* Start device */ + uptr->u5 = DRMSTA_WRITE | DRMSTA_CMD; + uptr->hwmark = uptr->capac; + sim_debug(DEBUG_CMD, &drm_dev, "WRS %o\n", dev); + chan_set_sel(chan, 1); + break; + default: + return SCPE_IOERR; + } + /* Choose which part to use */ + uptr->u6 = addr; /* Set drum address */ + chan_clear(chan, CHS_ATTN); /* Clear attentions */ + /* Make sure drum is spinning */ + sim_activate(uptr, DRMWORDTIME); + return SCPE_OK; + } + return SCPE_IOERR; +} + +t_stat drm_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + uint8 *buf = (uint8 *)uptr->filebuf; + t_stat r; + + /* Channel has disconnected, abort current read. */ + if (uptr->u5 & DRMSTA_CMD && chan_stat(chan, DEV_DISCO)) { + uptr->u5 = 0; + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &drm_dev, "Disconnect\n"); + return SCPE_OK; + } + + /* Check if we have a address match */ + if ((chan_flags[chan] & (STA_ACTIVE | DEV_SEL)) == (STA_ACTIVE | DEV_SEL) + && (uptr->u5 & (DRMSTA_READ | DRMSTA_WRITE))) { + if (uptr->u6 > (int32)uptr->capac) { + uptr->u5 = DRMSTA_CMD; + chan_set(chan, CHS_ATTN); + sim_activate(uptr, DRMWORDTIME); + return SCPE_OK; + } + + /* Try and transfer a word of data */ + if (uptr->u5 & DRMSTA_READ) { + uint8 ch = buf[uptr->u6++]; + r = chan_write_char(chan, &ch, (buf[uptr->u6] == 0)? DEV_REOR:0); + } else { + r = chan_read_char(chan, &buf[uptr->u6], 0); + uptr->u6++; + } + switch (r) { + case DATA_OK: + sim_debug(DEBUG_DATA, &drm_dev, "loc %6d data %02o\n", uptr->u6, + buf[uptr->u6]); + break; + + case END_RECORD: + case TIME_ERROR: + /* If no data, disconnect */ + sim_debug(DEBUG_DATA, &drm_dev, "loc %6d done\n", uptr->u6); + if (uptr->u5 & DRMSTA_WRITE) + buf[uptr->u6] = 0; /* Write mark */ + uptr->u5 = DRMSTA_CMD; + break; + } + } + sim_activate(uptr, DRMWORDTIME); + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +drm_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ +/* Init for a read */ + if (drm_cmd(uptr, IO_RDS, drm_dib.addr) != SCPE_OK) + return STOP_IONRDY; + return chan_boot(unit_num, dptr); +} + +void +drm_ini(UNIT *uptr, t_bool f) { + uptr->u5 = 0; +} + +t_stat +drm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { + fprintf(st, "Drum device for IBM 702 and 705\n\n"); + fprintf(st, "The Drum had 1000 tracks with the capacity of %d digits ", + DRMCHARTRK); + fprintf(st, "per track\n"); + fprintf(st, "The drum does not have any settings to change\n"); + return SCPE_OK; +} + + +const char * +drm_description (DEVICE *dptr) { + return "Drum"; +} + +#endif + diff --git a/I7000/i7080_sys.c b/I7000/i7080_sys.c new file mode 100644 index 00000000..c27d9981 --- /dev/null +++ b/I7000/i7080_sys.c @@ -0,0 +1,729 @@ +/* i7090_sys.c: IBM 705 Simulator system interface. + + Copyright (c) 2006-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 "i7080_defs.h" +#include "sim_card.h" +#include + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 7080"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 50; + +#ifdef NUM_DEVS_CDP +extern DEVICE stack_dev[]; +#endif + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#if NUM_DEVS_CDR > 0 + &cdr_dev, +#endif +#if NUM_DEVS_CDP > 0 + &cdp_dev, +#endif +#ifdef STACK_DEV + &stack_dev, +#endif +#if NUM_DEVS_LPR > 0 + &lpr_dev, +#endif +#if NUM_DEVS_CON > 0 + &con_dev, +#endif +#if NUM_DEVS_MT > 0 + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#if NUM_DEVS_MT > 2 + &mtc_dev, +#if NUM_DEVS_MT > 3 + &mtd_dev, +#endif +#endif +#endif +#endif +#if NUM_DEVS_DR > 0 + &drm_dev, +#endif +#if NUM_DEVS_HT > 0 + &hta_dev, +#if NUM_DEVS_HT > 1 + &htb_dev, +#endif +#endif +#if NUM_DEVS_DSK > 0 + &dsk_dev, +#endif +#if NUM_DEVS_COM > 0 + &coml_dev, + &com_dev, +#endif +#if NUM_DEVS_CHRON > 0 + &chron_dev, +#endif + NULL +}; + +/* Device addressing words */ +#ifdef NUM_DEVS_CDP +DIB cdp_dib = { CH_TYP_UREC, 1, 0x300, 0xff00, &cdp_cmd, &cdp_ini }; +#endif +#ifdef NUM_DEVS_CDR +DIB cdr_dib = { CH_TYP_UREC, 1, 0x100, 0xff00, &cdr_cmd, NULL }; +#endif +#ifdef NUM_DEVS_LPR +DIB lpr_dib = { CH_TYP_UREC, 1, 0x400, 0xff00, &lpr_cmd, &lpr_ini }; +#endif +#ifdef NUM_DEVS_CON +DIB con_dib = { CH_TYP_UREC, 1, 0x500, 0xff00, &con_cmd, &con_ini }; +#endif +#ifdef NUM_DEVS_DR +DIB drm_dib = { CH_TYP_UREC, 1, 0x1000, 0xff00, &drm_cmd, &drm_ini }; +#endif +#ifdef NUM_DEVS_MT +DIB mt_dib = { CH_TYP_76XX|CH_TYP_754, NUM_UNITS_MT, 0x200, 0xff00, &mt_cmd, &mt_ini }; +#endif +#ifdef NUM_DEVS_CHRON +DIB chron_dib = { CH_TYP_76XX|CH_TYP_UREC, 1, 0x200, 0xff00, &chron_cmd, NULL }; +#endif +#ifdef NUM_DEVS_HT +DIB ht_dib = { CH_TYP_79XX, NUM_UNITS_HT, 0, 0, &ht_cmd, NULL }; +#endif +#ifdef NUM_DEVS_DSK +DIB dsk_dib = { CH_TYP_79XX, 0, 0, 0, &dsk_cmd, &dsk_ini }; +#endif +#ifdef NUM_DEVS_COM +DIB com_dib = { CH_TYP_79XX, 0, 0, 0, &com_cmd, NULL }; +#endif + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Error1", /* Ind limit */ /* Not on 7080 */ + "Error2", /* XEC limit */ /* Not on 7080 */ + "I/O Check opcode", + "Machine Check", /* MM in trap */ + "7750 invalid line number", + "7750 invalid message", + "7750 No free output buffers", + "7750 No free input buffers", + "Overflow Check", /* Field overflow */ + "Sign Check", /* Sign change */ + "Divide error", + "Error6", /* Alpha index */ /* Not on 7080 */ + "No word mark", + "Invalid Address", + "Record Check", + "Program Check", + "Protect Check", + 0, +}; + + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {0, 0} +}; + +DEBTAB crd_debug[] = { + {"CHAN", DEBUG_CHAN}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"CARD", DEBUG_CARD}, + {0, 0} +}; + + +const char mem_to_ascii[64] = { + 'a', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', 's', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '#', ',', '(', '`', '\\', '_', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '|', + /*Sq*/ /*GM*/ +}; + +t_stat parse_sym(CONST char *, t_addr, UNIT *, t_value *, int32); + + + +/* Load BCD card image into memory, following 705 standard load format */ +int +load_rec(uint8 *image) { + extern uint8 bcd_bin[16]; + extern uint32 IC; + uint32 addr; + int len, i; + + /* Convert blanks to space code */ + for(i = 0; i < 80; i++) + if (image[i] == 0) + image[i] = 020; + + addr = bcd_bin[image[12] & 0xf]; + addr += 10 * bcd_bin[image[11] & 0xf]; + addr += 100 * bcd_bin[image[10] & 0xf]; + addr += 1000 * bcd_bin[image[9] & 0xf]; + i = (image[9] & 060) >> 4; /* Handle zones */ + i |= (image[12] & 040) >> 3; + i |= (image[12] & 020) >> 1; + addr += 10000 * i; + while(addr > EMEMSIZE) addr -= EMEMSIZE; /* Wrap around */ + len = bcd_bin[image[14] & 0xf]; + len += 10 * bcd_bin[image[13] & 0xf]; + if (len > 65) + len = 65; + if (len == 0) { + IC = addr; + return 1; + } + for(i = 0; i < len; i++) { + uint8 ch = image[15+i]; + if (ch == 075) + ch = 077; + M[addr++] = ch; + } + return 0; +} + +/* Load a card image file into memory. */ +t_stat +sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + char buffer[160]; + int i, j; + + if (match_ext(fnam, "crd")) { + uint8 image[80]; + + while (sim_fread(buffer, 1, 160, fileref) == 160) { + /* Convert bits into image */ + for (j = i = 0; j < 80; j++) { + uint16 x; + x = buffer[i++]; + x |= buffer[i++] << 8; + image[j] = sim_hol_to_bcd(x); + } + if (load_rec(image)) + return SCPE_OK; + } + return SCPE_OK; + } else if (match_ext(fnam, "cbn")) { + uint8 image[80]; + + while (sim_fread(buffer, 1, 160, fileref) == 160) { + /* Convert bits into image */ + for (j = i = 0; j < 80; j++) { + uint16 x; + x = buffer[i++]; + x |= buffer[i++] << 8; + image[j] = sim_hol_to_bcd(x); + } + if (load_rec(image)) + return SCPE_OK; + } + return SCPE_OK; + } else if (match_ext(fnam, "dck")) { + extern char ascii_to_six[128]; + while (fgets(buffer, 160, fileref) != 0) { + uint8 image[80]; + /* Convert bits into image */ + memset(image, 0, sizeof(image)); + for (j = 0; j < 80; j++) { + if (buffer[j] == '\n' || buffer[j] == '\0') + break; + image[j] = sim_ascii_to_six[buffer[j]&0177]; + } + if (load_rec(image)) + return SCPE_OK; + } + return SCPE_OK; + } else + return SCPE_ARG; + + + + return SCPE_ARG; +} + +/* Symbol tables */ +typedef struct _opcode +{ + uint32 opbase; + const char *name; + uint8 type; +} +t_opcode; + +const char *chname[11] = { + "*", "20", "21", "22", "23", "40", "41", "44", "45", "46", "47" +}; + +#define TYPE_A 1 /* Standard memory operation */ +#define TYPE_B 2 /* ASU encoded operation */ +#define TYPE_C 3 /* MA encoded operation MA < 100 */ +#define TYPE_D 4 /* MA + ASU fixed MA < 100 */ + +t_opcode optbl[] = { + {OP_ADD, "ADD", TYPE_A}, + {OP_RAD, "RAD", TYPE_A}, + {OP_SUB, "SUB", TYPE_A}, + {OP_RSU, "RSU", TYPE_A}, + {OP_MPY, "MPY", TYPE_A}, + {OP_DIV, "DIV", TYPE_A}, + {OP_ST, "ST", TYPE_A}, + {OP_ADM, "ADM", TYPE_A}, + {OP_AAM, "AAM", TYPE_A}, + {OP_SGN, "SGN", TYPE_A}, + {OP_SET, "SET", TYPE_A}, + {OP_SHR, "SHR", TYPE_A}, + {OP_LEN, "LNG", TYPE_A}, + {OP_RND, "RND", TYPE_A}, + {OP_LOD, "LOD", TYPE_A}, + {OP_UNL, "UNL", TYPE_A}, + {OP_LDA, "LDA", TYPE_A}, + {OP_ULA, "ULA", TYPE_A}, + {OP_SPR, "SPR", TYPE_A}, + {OP_RCV, "RCV", TYPE_A}, + {OP_SND, "SND", TYPE_A}, + {OP_CMP, "CMP", TYPE_A}, + {OP_TRE, "TRE", TYPE_A}, + {OP_TRH, "TRH", TYPE_A}, + {OP_NTR, "NTR", TYPE_A}, + {OP_TRP, "TRP", TYPE_A}, + {OP_TRZ, "TRZ", TYPE_A}, + {OP_NOP, "NOP", TYPE_A}, + {OP_TR|000100, "TSL", TYPE_B}, + {OP_TR, "TR", TYPE_A}, + {OP_TRA|000100, "TAA", TYPE_B}, + {OP_TRA|000200, "TAB", TYPE_B}, + {OP_TRA|000300, "TAC", TYPE_B}, + {OP_TRA|000400, "TAD", TYPE_B}, + {OP_TRA|000500, "TAE", TYPE_B}, + {OP_TRA|000600, "TAF", TYPE_B}, + {OP_TRA|000700, "TNS", TYPE_B}, + {OP_TRA, "TRA", TYPE_A}, + {OP_TRS|000100, "TRR", TYPE_B}, + {OP_TRS|000200, "TTC", TYPE_B}, + {OP_TRS|000300, "TSA", TYPE_B}, + {OP_TRS|001100, "TAR", TYPE_B}, + {OP_TRS|001200, "TIC", TYPE_B}, + {OP_TRS|001300, "TMC", TYPE_B}, + {OP_TRS|001400, "TRC", TYPE_B}, + {OP_TRS|001500, "TEC", TYPE_B}, + {OP_TRS|001600, "TOC", TYPE_B}, + {OP_TRS|001700, "TSC", TYPE_B}, + {OP_TRS, "TRS", TYPE_A}, + {OP_TMT, "TMT", TYPE_A}, + {OP_CTL2|000000,"SPC", TYPE_B}, + {OP_CTL2|000200,"LFC", TYPE_B}, + {OP_CTL2|000300,"UFC", TYPE_B}, + {OP_CTL2|000400,"LSB", TYPE_B}, + {OP_CTL2|000500,"USB", TYPE_B}, + {OP_CTL2|000600,"EIM", TYPE_B}, + {OP_CTL2|000700,"LIM", TYPE_B}, + {OP_CTL2|001000,"TCT", TYPE_B}, + {OP_CTL2|001100,"B", TYPE_B}, + {OP_CTL2|001200,"EIA", TYPE_B}, + {OP_CTL2|001300,"CNO", TYPE_B}, + {OP_CTL2|001400,"TLU", TYPE_B}, + {OP_CTL2|001500,"TLH", TYPE_B}, + {OP_CTL2|001600,"TIP", TYPE_B}, + {OP_CTL2|001700,"LIP", TYPE_B}, + {OP_CTL2, "CTL2", TYPE_A}, + {OP_BLM|000100, "BLMS", TYPE_B}, + {OP_BLM, "BLM", TYPE_A}, + {OP_SEL, "SEL", TYPE_A}, + {OP_CTL|001400, "ECB", TYPE_B}, + {OP_CTL|001500, "CHR", TYPE_B}, + {OP_CTL|001600, "EEM", TYPE_B}, + {OP_CTL|001700, "LEM", TYPE_B}, + {OP_CTL|0010000, "WTM", TYPE_D}, + {OP_CTL|0020100, "RUN", TYPE_D}, + {OP_CTL|0020000, "RWD", TYPE_D}, + {OP_CTL|0030000, "ION", TYPE_D}, + {OP_CTL|0040100, "BSF", TYPE_D}, + {OP_CTL|0040000, "BSP", TYPE_D}, + {OP_CTL|0050000, "SUP", TYPE_C}, + {OP_CTL|0110000, "SKP", TYPE_C}, + {OP_CTL|0450000, "SDL", TYPE_C}, + {OP_CTL|0460000, "SDH", TYPE_C}, + {OP_CTL|0000000, "IOF", TYPE_D}, + {OP_CTL, "CTL", TYPE_A}, + {OP_HLT, "HLT", TYPE_A}, + {OP_WR|000500, "WMC", TYPE_B}, + {OP_WR|000400, "CWR", TYPE_B}, + {OP_WR|000300, "SCC", TYPE_B}, + {OP_WR|000200, "SRC", TYPE_B}, + {OP_WR|000100, "DMP", TYPE_B}, + {OP_WR, "WR", TYPE_A}, + {OP_RWW, "RWW", TYPE_A}, + {OP_RD|000500, "RMB", TYPE_B}, + {OP_RD|000400, "CRD", TYPE_B}, + {OP_RD|000300, "SST", TYPE_B}, + {OP_RD|000200, "RMA", TYPE_B}, + {OP_RD|000100, "FSP", TYPE_B}, + {OP_RD, "RD", TYPE_A}, + {OP_WRE|000100, "WRZ", TYPE_B}, + {OP_WRE, "WRE", TYPE_A}, + {OP_SBZ|000100, "SBZ1", TYPE_B}, + {OP_SBZ|000200, "SBZ2", TYPE_B}, + {OP_SBZ|000300, "SBZ3", TYPE_B}, + {OP_SBZ|000400, "SBZ4", TYPE_B}, + {OP_SBZ|000500, "SBZ5", TYPE_B}, + {OP_SBZ|000600, "SBZ6", TYPE_B}, + {OP_SBZ|000700, "SBA", TYPE_B}, + {OP_SBZ|001000, "SBR", TYPE_B}, + {OP_SBZ|001100, "SBN1", TYPE_B}, + {OP_SBZ|001200, "SBN2", TYPE_B}, + {OP_SBZ|001300, "SBN3", TYPE_B}, + {OP_SBZ|001400, "SBN4", TYPE_B}, + {OP_SBZ|001500, "SBN5", TYPE_B}, + {OP_SBZ|001600, "SBN6", TYPE_B}, + {OP_SBZ, "SBZ", TYPE_A}, + {OP_TZB, "TZB", TYPE_A}, + {OP_SMT|001600, "SMT", TYPE_A}, + {0, NULL, 0}, +}; + + +/* Print out a address plus index */ +t_stat fprint_addr (FILE *of, uint32 addr) { + fprintf(of, "%d", addr); + return SCPE_OK; +} + +/* Register change decode + + Inputs: + *of = output stream + inst = mask bits +*/ + +t_stat +fprint_reg (FILE *of, uint32 rdx, t_value *val, UNIT *uptr, int32 sw) +{ + fprintf(of, "Register(%d, %x)", rdx, *val); + return SCPE_OK; +} + + + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ +int32 i, t; +uint8 op; + +if (sw & SIM_SW_REG) + return fprint_reg(of, addr, val, uptr, sw); + +if (sw & SWMASK ('C')) { /* character? */ + t = val[0]; + fprintf (of, " %c<%02o> ", mem_to_ascii[t & 077], t & 077); + return SCPE_OK; + } +if ((uptr != NULL) && (uptr != &cpu_unit)) return SCPE_ARG; /* CPU? */ +if (sw & SWMASK ('D')) { /* dump? */ + for (i = 0; i < 50; i++) fprintf (of, "%c", mem_to_ascii[val[i]&077]) ; + return -(i - 1); + } +if (sw & SWMASK ('S')) { /* string? */ + i = 0; + do { + t = val[i++]; + fprintf (of, "%c", mem_to_ascii[t & 077]); + } while (i < 50); + return -(i - 1); + } +if (sw & SWMASK ('M')) { /* machine code? */ + uint32 addr; + t_opcode *tab; + uint8 zone; + uint8 reg; + uint16 opvalue; + + i = 0; + op = val[i++] & 077; + t = val[i++]; /* First address char */ + zone = (t & 060) >> 4; + t &= 0xf; + if (t == 10) + t = 0; + addr = t * 1000; + t = val[i++]; /* Second address char */ + reg = (t & 060) >> 2; + t &= 0xf; + if (t == 10) + t = 0; + addr += t * 100; + t = val[i++]; /* Third address char */ + reg |= (t & 060) >> 4; + t &= 0xf; + if (t == 10) + t = 0; + addr += t * 10; + t = val[i++]; /* Forth address char */ + zone |= (t & 060) >> 2; + /* Switch BA bits in high zone */ + zone = (zone & 03) | ((zone & 04) << 1) | ((zone & 010) >> 1); + t &= 0xf; + if (t == 10) + t = 0; + addr += t; + opvalue = op | (reg << 6); + addr += zone * 10000; + for(tab = optbl; tab->name != NULL; tab++) { + if (tab->type == TYPE_A && op == tab->opbase) + break; + if (tab->type == TYPE_B && opvalue == tab->opbase) + break; + if (tab->type == TYPE_C && addr < 100 && + (op|(addr << 12)) == tab->opbase) + break; + if (tab->type == TYPE_D && addr < 100 && + (opvalue|(addr << 12)) == tab->opbase) + break; + } + + if (tab->name == NULL) + fprintf(of, "%c<%02o>\t", mem_to_ascii[op], op); + else + fprintf(of, "%s\t", tab->name); + + switch(tab->type) { + case TYPE_A: + fprintf(of, "%d", addr); + if (reg != 0) + fprintf(of, ",%d", reg); + break; + case TYPE_B: + fprintf(of, "%d", addr); + break; + case TYPE_C: /* No operand required for type C or D */ + case TYPE_D: + break; + } + return -(i - 1); +} +fprintf (of, " %02o ", val[0] & 077); +return SCPE_OK; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + t_value d; + char buffer[100]; + extern char ascii_to_six[]; + + while (isspace(*cptr)) + cptr++; + d = 0; + i = 0; + if (sw & SWMASK('C')) { + while (*cptr != '\0') { + d = sim_ascii_to_six[0177 & *cptr++]; + if (d == 0) + d = 020; + val[i++] = d; + } + if (i == 0) + return SCPE_ARG; + return -(i - 1); + } else if (sw & SWMASK('M')) { + t_opcode *op; + uint32 addr = 0; + uint8 asu = 0; + uint8 zone; + uint8 t; + + i = 0; + /* Grab opcode */ + cptr = get_glyph(cptr, buffer, 0); + if ((op = find_opcode(buffer, optbl)) == 0) + return STOP_UUO; + if (op->type == TYPE_C || op->type == TYPE_D) { + addr = op->opbase >> 12; + val[i++] = op->opbase & 077; + val[i++] = 10; + val[i++] = ((op->opbase & 01400) >> 4) | 10; + t = addr / 10; + if (t == 0) + t = 10; + val[i++] = ((op->opbase & 00300) >> 2) | t; + t = addr % 10; + if (t == 0) + t = 10; + val[i++] = t; + return -(i - 1); + } + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + /* Collect address */ + while(*cptr >= '0' && *cptr <= '9') + addr = (addr * 10) + (*cptr++ - '0'); + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + if (*cptr == ',') { /* Collect a ASU */ + while(*cptr >= '0' && *cptr <= '9') + asu = (asu * 10) + (*cptr++ - '0'); + + } + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + if (*cptr != '\0') + return SCPE_ARG; + /* Type B's can't have ASU */ + if (op->type == TYPE_B) { + if (asu != 0) + return STOP_UUO; + asu = (op->opbase >> 6) & 017; + } + /* Check if ASU out of range */ + if (asu > 16) + return SCPE_ARG; + zone = addr / 10000; + if (zone > 16) + return SCPE_ARG; + addr %= 10000; + val[i++] = op->opbase & 077; + t = addr / 1000; + if (t == 0) + t = 10; + addr %= 1000; + val[i++] = t | ((zone << 4) & 060); + t = addr / 100; + if (t == 0) + t = 10; + addr %= 100; + val[i++] = t | ((asu << 2) & 060); + t = addr / 10; + if (t == 0) + t = 10; + addr %= 10; + val[i++] = t | ((asu << 4) & 060); + t = addr; + if (t == 0) + t = 10; + addr %= 10; + val[i++] = t | ((zone << 2) & 060); + return -(i - 1); + } else { + int sign = 0; + + i = 0; + while (*cptr != '\0') { + sign = 0; + /* Skip blanks */ + while(isspace(*cptr)) cptr++; + if (*cptr == '+') { + cptr++; + sign = 1; + } else if (*cptr == '-') { + cptr++; + sign = -1; + } + if (!(*cptr >= '0' && *cptr <= '9')) + return SCPE_ARG; + while(*cptr >= '0' && *cptr <= '9') { + d = *cptr++ - '0'; + if (d == 0) + d = 10; + val[i++] = d; + } + if (*cptr == ',') + cptr++; + if(sign) + val[i-1] |= (sign==-1)?040:060; /* Set sign last digit */ + } + if (i == 0) + return SCPE_ARG; + return -(i - 1); + } + return SCPE_OK; +} diff --git a/I7000/i7090_cdp.c b/I7000/i7090_cdp.c new file mode 100644 index 00000000..86826723 --- /dev/null +++ b/I7000/i7090_cdp.c @@ -0,0 +1,302 @@ +/* i7090_cdp.c: IBM 7090 Card punch. + + 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. + + This is the standard card punch. + +*/ + +#include "i7090_defs.h" +#include "sim_card.h" +#ifdef NUM_DEVS_CDP +#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE + + +/* std devices. data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +/* Device status information stored in u5 */ +#define CDPSTA_PUNCH 0004000 /* Punch strobe during run */ +#define CDPSTA_POSMASK 0770000 +#define CDPSTA_POSSHIFT 12 + +t_stat cdp_srv(UNIT *); +t_stat cdp_reset(DEVICE *); +t_stat cdp_attach(UNIT *, CONST char *); +t_stat cdp_detach(UNIT *); +t_stat cdp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cdp_description (DEVICE *dptr); + +UNIT cdp_unit[] = { +#if NUM_DEVS_CDP > 1 + {UDATA(&cdp_srv, UNIT_S_CHAN(CHAN_A) | UNIT_CDP, 0), 6000}, /* A */ +#endif +#if NUM_DEVS_CDP > 2 + {UDATA(&cdp_srv, UNIT_S_CHAN(CHAN_C) | UNIT_CDP, 0), 6000}, /* B */ +#endif +#if NUM_DEVS_CDP > 3 + {UDATA(&cdp_srv, UNIT_S_CHAN(CHAN_E) | UNIT_CDP | UNIT_DIS, 0), 6000}, /* C */ +#endif + {UDATA(&cdp_srv, UNIT_S_CHAN(CHAN_CHPIO) | UNIT_CDP, 0), 6000}, /* D */ +}; + +MTAB cdp_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL}, +#if NUM_CHAN != 1 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL}, +#endif + {0} +}; + +DEVICE cdp_dev = { + "CDP", cdp_unit, NULL, cdp_mod, + NUM_DEVS_CDP, 8, 15, 1, 8, 36, + NULL, NULL, &cdp_reset, NULL, &cdp_attach, &cdp_detach, + &cdp_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, + NULL, NULL, &cdp_help, NULL, NULL, &cdp_description +}; + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + + +uint32 cdp_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdp_unit); + extern uint16 IC; + + if ((uptr->flags & UNIT_ATT) != 0 && cmd == IO_WRS) { + /* Start device */ + if (!(uptr->u5 & URCSTA_CMD)) { + dev_pulse[chan] &= ~PUNCH_M; + uptr->u5 &= ~CDPSTA_PUNCH; + if ((uptr->u5 & URCSTA_ON) == 0) { + uptr->wait = 330; /* Startup delay */ + } else if (uptr->u5 & URCSTA_IDLE && uptr->wait <= 30) { + uptr->wait += 85; /* Wait for next latch point */ + } + uptr->u5 |= (URCSTA_WRITE | URCSTA_CMD); + uptr->u5 &= ~CDPSTA_POSMASK; + chan_set_sel(chan, 1); + chan_clear_status(chan); + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + sim_debug(DEBUG_CMD, &cdp_dev, "%05o WRS unit=%d\n", IC, u); + return SCPE_OK; + } + } + chan_set_attn(chan); + return SCPE_IOERR; +} + +t_stat cdp_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdp_unit); + int pos; + t_uint64 wd; + int bit; + t_uint64 mask; + int b; + int col; + struct _card_data *data; + + /* Channel has disconnected, abort current card. */ + if (uptr->u5 & URCSTA_CMD && chan_stat(chan, DEV_DISCO)) { + if ((uptr->u5 & CDPSTA_POSMASK) != 0) { + sim_debug(DEBUG_DETAIL, &cdp_dev, "punch card\n"); + sim_punch_card(uptr, NULL); + uptr->u5 &= ~CDPSTA_PUNCH; + } + uptr->u5 &= ~(URCSTA_WRITE | URCSTA_CMD | CDPSTA_POSMASK); + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &cdp_dev, "unit=%d disconnect\n", u); + } + + /* Check to see if we have timed out */ + if (uptr->wait != 0) { + uptr->wait--; + /* If at end of record and channel is still active, do another read */ + if ( + ((uptr->u5 & (URCSTA_CMD | URCSTA_IDLE | URCSTA_WRITE | URCSTA_ON)) + == (URCSTA_CMD | URCSTA_IDLE | URCSTA_ON)) && uptr->wait > 30 + && chan_test(chan, STA_ACTIVE)) { + uptr->u5 |= URCSTA_WRITE; + uptr->u5 &= ~URCSTA_IDLE; + chan_set(chan, DEV_WRITE); + chan_clear(chan, DEV_WEOR); + sim_debug(DEBUG_CHAN, &cdp_dev, "unit=%d restarting\n", u); + } + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + return SCPE_OK; + } + + /* If no write request, go to idle mode */ + if ((uptr->u5 & URCSTA_WRITE) == 0) { + if ((uptr->u5 & (URCSTA_IDLE | URCSTA_ON)) == + (URCSTA_IDLE | URCSTA_ON)) { + uptr->wait = 85; /* Delay 85ms */ + uptr->u5 &= ~URCSTA_IDLE; /* Not running */ + sim_activate(uptr, us_to_ticks(1000)); + } else { + uptr->u5 &= ~URCSTA_ON; /* Turn motor off */ + } + return SCPE_OK; + } + + /* Motor is up to speed now */ + uptr->u5 |= URCSTA_ON; + uptr->u5 &= ~URCSTA_IDLE; /* Not running */ + + if (dev_pulse[chan] & PUNCH_M) + uptr->u5 |= CDPSTA_PUNCH; + + pos = (uptr->u5 & CDPSTA_POSMASK) >> CDPSTA_POSSHIFT; + if (pos == 24) { + if (chan_test(chan, STA_ACTIVE)) { + sim_debug(DEBUG_CHAN, &cdp_dev, "unit=%d set EOR\n", u); + chan_set(chan, DEV_REOR); + } else { + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &cdp_dev, "unit=%d disconnect\n", u); + } + sim_debug(DEBUG_DETAIL, &cdp_dev, "punch card full\n"); + sim_punch_card(uptr, NULL); + uptr->u5 |= URCSTA_IDLE; + uptr->u5 &= ~(URCSTA_WRITE | CDPSTA_POSMASK | CDPSTA_PUNCH); + uptr->wait = 85; + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; + } + + + + sim_debug(DEBUG_DATA, &cdp_dev, "unit=%d write column %d ", u, pos); + wd = 0; + data = (struct _card_data *)uptr->up7; + switch (chan_read(chan, &wd, 0)) { + case DATA_OK: + sim_debug(DEBUG_DATA, &cdp_dev, " %012llo\n", wd); + /* Bit flip into temp buffer */ + bit = 1 << (pos / 2); + mask = 1; + b = (pos & 1)?36:0; + + for (col = 35; col >= 0; mask <<= 1, col--) { + if (wd & mask) + data->image[col + b] |= bit; + } + pos++; + uptr->wait = 0; + uptr->u5 &= ~CDPSTA_POSMASK; + uptr->u5 |= (pos << CDPSTA_POSSHIFT) & CDPSTA_POSMASK; + sim_activate(uptr, (pos & 1) ? us_to_ticks(300) : us_to_ticks(8000)); + return SCPE_OK; + case END_RECORD: + sim_debug(DEBUG_DATA, &cdp_dev, "eor\n"); + uptr->wait = 8 * (12 - (pos / 2)) /*+ 85*/; + uptr->u5 &= ~(CDPSTA_POSMASK); + uptr->u5 |= (24 << CDPSTA_POSSHIFT) & CDPSTA_POSMASK; + break; + case TIME_ERROR: + sim_debug(DEBUG_DATA, &cdp_dev, "no data\n"); + chan_set_attn(chan); + uptr->wait = 8 * (12 - (pos / 2)) /*+ 85*/; + uptr->u5 &= ~(CDPSTA_POSMASK); + uptr->u5 |= (24 << CDPSTA_POSSHIFT) & CDPSTA_POSMASK; + break; + } + + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; +} + +void +cdp_ini(UNIT * uptr, t_bool f) +{ + uptr->u5 = 0; +} + +t_stat +cdp_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +t_stat +cdp_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = CDPSTA_POSMASK; + return SCPE_OK; +} + +t_stat +cdp_detach(UNIT * uptr) +{ + return sim_card_detach(uptr); +} + +t_stat +cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + const char *cpu = cpu_description(&cpu_dev); + + fprintf (st, "%s\n\n", cdp_description(dptr)); +#if NUM_DEVS_CDP > 3 + fprintf (st, "The %s supports up to four card punches\n", cpu); +#elif NUM_DEVS_CDP > 2 + fprintf (st, "The %s supports up to three card punches\n", cpu); +#elif NUM_DEVS_CDP > 1 + fprintf (st, "The %s supports up to two card punches\n", cpu); +#elif NUM_DEVS_CDP > 0 + fprintf (st, "The %s supports one card punch\n", cpu); +#endif + help_set_chan_type(st, dptr, "Card punches"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprintf (st, "\n"); + sim_card_attach_help(st, dptr, uptr, flag, cptr); + return SCPE_OK; +} + +const char * +cdp_description(DEVICE *dptr) +{ + return "721 Card Punch"; +} + + +#endif + diff --git a/I7000/i7090_cdr.c b/I7000/i7090_cdr.c new file mode 100644 index 00000000..e82938f6 --- /dev/null +++ b/I7000/i7090_cdr.c @@ -0,0 +1,339 @@ +/* i7090_cdr.c: IBM 7090 Card Read. + + 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. + + This is the standard card reader. + +*/ + +#include "i7090_defs.h" +#include "sim_card.h" + +#ifdef NUM_DEVS_CDR + +#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | UNIT_ROABLE |\ + MODE_026 + + +/* std devices. data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +/* Device status information stored in u5 */ +#define CDRSTA_EOR 002000 /* Hit end of record */ +#define CDRPOSMASK 0770000 /* Bit Mask to retrive drum position */ +#define CDRPOSSHIFT 12 + + +t_stat cdr_srv(UNIT *); +t_stat cdr_boot(int32, DEVICE *); +t_stat cdr_reset(DEVICE *); +t_stat cdr_attach(UNIT *, CONST char *); +t_stat cdr_detach(UNIT *); +t_stat cdr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cdr_description (DEVICE *dptr); + +UNIT cdr_unit[] = { +#if NUM_DEVS_CDR > 1 + {UDATA(&cdr_srv, UNIT_S_CHAN(CHAN_A) | UNIT_CDR, 0), 3000}, /* A */ +#endif +#if NUM_DEVS_CDR > 2 + {UDATA(&cdr_srv, UNIT_S_CHAN(CHAN_C) | UNIT_CDR, 0), 3000}, /* B */ +#endif +#if NUM_DEVS_CDR > 3 + {UDATA(&cdr_srv, UNIT_S_CHAN(CHAN_E) | UNIT_CDR | UNIT_DIS, 0), 3000}, /* C */ +#endif + {UDATA(&cdr_srv, UNIT_S_CHAN(CHAN_CHPIO) | UNIT_CDR, 0), 3000}, /* D */ +}; + +MTAB cdr_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL}, +#if NUM_CHAN != 1 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL}, +#endif + {0} +}; + +DEVICE cdr_dev = { + "CDR", cdr_unit, NULL, cdr_mod, + NUM_DEVS_CDR, 8, 15, 1, 8, 36, + NULL, NULL, &cdr_reset, &cdr_boot, &cdr_attach, &cdr_detach, + &cdr_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, + NULL, NULL, &cdr_help, NULL, NULL, &cdr_description +}; + + +uint32 cdr_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + + if ((uptr->flags & UNIT_ATT) != 0 && cmd == IO_RDS) { + int u = (uptr - cdr_unit); + + /* Start device */ + if ((uptr->u5 & URCSTA_CMD) == 0) { + if ((uptr->u5 & (URCSTA_ON | URCSTA_IDLE)) == + (URCSTA_ON | URCSTA_IDLE) && (uptr->wait <= 60)) { + uptr->wait += 100; /* Wait for next latch point */ + } else + uptr->wait = 75; /* Startup delay */ + uptr->u5 |= URCSTA_READ | URCSTA_CMD | CDRPOSMASK; + chan_set_sel(chan, 0); + chan_clear_status(chan); + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + sim_debug(DEBUG_CMD, &cdr_dev, "RDS unit=%d\n", u); + return SCPE_OK; + } + return SCPE_BUSY; + } + chan_set_attn(chan); + return SCPE_NODEV; +} + +t_stat cdr_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - cdr_unit); + int pos, col, b; + uint16 bit; + t_uint64 mask, wd; + struct _card_data *data; + + /* Channel has disconnected, abort current read. */ + if (uptr->u5 & URCSTA_CMD && chan_stat(chan, DEV_DISCO)) { + uptr->u5 &= ~(URCSTA_READ | URCSTA_CMD); + uptr->u5 |= CDRPOSMASK; + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &cdr_dev, "unit=%d disconnecting\n", u); + } + + /* Check to see if we have timed out */ + if (uptr->wait != 0) { + /* If at end of record and channel is still active, do another read */ + if (uptr->wait == 30 + && ((uptr->u5 & (URCSTA_CMD|URCSTA_IDLE|URCSTA_READ|URCSTA_ON)) + == (URCSTA_CMD | URCSTA_IDLE | URCSTA_ON)) + && chan_test(chan, STA_ACTIVE)) { + uptr->u5 |= URCSTA_READ; + sim_debug(DEBUG_CHAN, &cdr_dev, "unit=%d restarting\n", u); + } + uptr->wait--; + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + return SCPE_OK; + } + + /* If no read request, go to idle mode */ + if ((uptr->u5 & URCSTA_READ) == 0) { + if ((uptr->u5 & URCSTA_EOF) || (uptr->u5 & URCSTA_IDLE)) { + uptr->u5 &= ~(URCSTA_ON | URCSTA_IDLE); /* Turn motor off */ + } else { + uptr->wait = 85; /* Delay 85ms */ + uptr->u5 |= URCSTA_IDLE; /* Go idle */ + sim_activate(uptr, us_to_ticks(1000)); + } + return SCPE_OK; + } + + /* Motor is up to speed now */ + uptr->u5 |= URCSTA_ON; + uptr->u5 &= ~URCSTA_IDLE; + + pos = (uptr->u5 & CDRPOSMASK) >> CDRPOSSHIFT; + if (pos == (CDRPOSMASK >> CDRPOSSHIFT)) { + switch (sim_read_card(uptr)) { + case SCPE_UNATT: + case SCPE_IOERR: + sim_debug(DEBUG_EXP, &cdr_dev, "unit=%d Setting ATTN\n", u); + chan_set_error(chan); + chan_set_attn(chan); + uptr->u5 &= ~URCSTA_READ; + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; + case SCPE_EOF: + sim_debug(DEBUG_EXP, &cdr_dev, "unit=%d EOF\n", u); + chan_set_eof(chan); + chan_set_attn(chan); + uptr->u5 &= ~URCSTA_READ; + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; + case SCPE_OK: + break; + } + pos = 0; + } + + /* Check if everything read in, if so return EOR now */ + if (pos == 24) { + sim_debug(DEBUG_CHAN, &cdr_dev, "unit=%d set EOR\n", u); + chan_set(chan, DEV_REOR); + uptr->u5 &= ~URCSTA_READ; + uptr->u5 |= CDRSTA_EOR | CDRPOSMASK; + uptr->wait = 86; + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; + } + + data = (struct _card_data *)uptr->up7; + /* Bit flip into read buffer */ + bit = 1 << (pos / 2); + mask = 1; + wd = 0; + b = (pos & 1)?36:0; + + for (col = 35; col >= 0; mask <<= 1) { + if (data->image[col-- + b] & bit) + wd |= mask; + } + + switch (chan_write (chan, &wd,/* (pos == 23) ? DEV_REOR :*/ 0)) { + case DATA_OK: + sim_debug(DEBUG_DATA, &cdr_dev, "unit=%d read row %d %012llo\n", u, + pos, wd); + pos++; + uptr->u5 &= ~CDRPOSMASK; + uptr->u5 |= pos << CDRPOSSHIFT; + uptr->wait = 0; + sim_activate(uptr, (pos & 1) ? us_to_ticks(300) : us_to_ticks(8000)); + return SCPE_OK; + + case END_RECORD: + sim_debug(DEBUG_CHAN, &cdr_dev, "unit=%d got EOR\n", u); + uptr->u5 &= ~CDRPOSMASK; + uptr->u5 |= 24 << CDRPOSSHIFT; + uptr->wait = 8 * (12 - (pos / 2)) /*+ 86*/; + break; + + case TIME_ERROR: + sim_debug(DEBUG_EXP, &cdr_dev, "unit=%d no data\n", u); + uptr->u5 &= ~CDRPOSMASK; + uptr->u5 |= 24 << CDRPOSSHIFT; + uptr->wait = 8 * (12 - (pos / 2)) /*+ 85*/; + break; + } + + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +cdr_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags); + t_stat r; + int pos; + struct _card_data *data; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + uptr->u5 = 0; +/* Init for a read */ + if (cdr_cmd(uptr, IO_RDS, cdr_dib.addr) != SCPE_OK) + return STOP_IONRDY; + r = sim_read_card(uptr); + if (r != SCPE_OK) + return r; + +/* Copy first three records. */ + data = (struct _card_data *)uptr->up7; + uptr->u5 &= ~CDRPOSMASK; + for(pos = 0; pos <3; pos++) { + uint16 bit = 1 << (pos / 2); + t_uint64 mask = 1; + int b = (pos & 1)?36:0; + int col; + + if (pos == 2 && chan == 0) + break; + M[pos] = 0; + for (col = 35; col >= 0; mask <<= 1) { + if (data->image[col-- + b] & bit) + M[pos] |= mask; + } + sim_debug(DEBUG_DATA, &cdr_dev, "boot read row %d %012llo\n", + pos, M[pos]); + } + uptr->u5 |= pos << CDRPOSSHIFT; +/* Make sure channel is set to start reading rest. */ + return chan_boot(unit_num, dptr); +} + +t_stat +cdr_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +t_stat +cdr_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + uptr->u4 = 0; + return SCPE_OK; +} + +t_stat +cdr_detach(UNIT * uptr) +{ + return sim_card_detach(uptr); +} + +t_stat +cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + const char *cpu = cpu_description(&cpu_dev); + + fprintf (st, "%s\n\n", cdr_description(dptr)); +#if NUM_DEVS_CDR > 3 + fprintf (st, "The %s supports up to four card readers\n\n", cpu); +#elif NUM_DEVS_CDR > 2 + fprintf (st, "The %s supports up to three card readers\n\n", cpu); +#elif NUM_DEVS_CDR > 1 + fprintf (st, "The %s supports up to two card readers\n\n", cpu); +#elif NUM_DEVS_CDR > 0 + fprintf (st, "The %s supports one card reader\n\n", cpu); +#endif + help_set_chan_type(st, dptr, "Card readers"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + fprintf (st, "\n"); + sim_card_attach_help(st, dptr, uptr, flag, cptr); + return SCPE_OK; +} + +const char * +cdr_description(DEVICE *dptr) +{ + return "711 Card Reader"; +} + +#endif diff --git a/I7000/i7090_chan.c b/I7000/i7090_chan.c new file mode 100644 index 00000000..3daf25ea --- /dev/null +++ b/I7000/i7090_chan.c @@ -0,0 +1,1710 @@ +/* i7090_chan.c: IBM 7090 Channel simulator + + 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. + + channel + + The system state for the IBM 7090 channel is: + There are 4 types of channel: + 704: Basic polled mode transfer. Channel only manages + status and disconnect of devices. + 7607: Basic channel. + 7909: Enhanced channel for disk, hypertape and com controlers. + 7289: Special CTSS channel, like 7607, but first command + is drum address. + + Common registers to all but 704 channels. + ADDR<0:16> Address of next command. + CMD<0:6> Channel command. + WC<0:15> Word count remaining. + ASM<0:35> Assembled data from devices. + LOCATION<0:16> Location to read or write next word from. + + 7909 adds following two registers. + SMS<0:6> Select register. + COUNT<0:6> Counter. + + Simulation registers to handle device handshake. + STATUS<0:16> Simulated register for basic channel status. + SENSE<0:16> Additional flags for 7909 channels. +*/ + +#include "i7090_defs.h" + +extern uint16 iotraps; +extern uint8 iocheck; +extern uint8 dualcore; +extern UNIT cpu_unit; +extern uint16 IC; +extern t_uint64 MQ; +extern uint32 drum_addr; +extern uint32 hsdrm_addr; + +t_stat chan_reset(DEVICE * dptr); +void chan_fetch(int chan); +t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *chan_description (DEVICE *dptr); +void chan9_seqcheck(int chan); +uint32 dly_cmd(UNIT *, uint16, uint16); + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +uint16 caddr[NUM_CHAN]; /* Channel memory address */ +uint8 bcnt[NUM_CHAN]; /* Channel character count */ +uint8 cmd[NUM_CHAN]; /* Current command */ +uint16 wcount[NUM_CHAN]; /* Word count */ +t_uint64 assembly[NUM_CHAN]; /* Assembly register */ +uint16 location[NUM_CHAN]; /* Pointer to next opcode */ +uint32 chan_flags[NUM_CHAN]; /* Unit status */ +uint16 chan_info[NUM_CHAN]; /* Private channel info */ +uint8 counter[NUM_CHAN]; /* Channel counter */ +uint8 sms[NUM_CHAN]; /* Channel mode infomation */ +uint8 chan_irq[NUM_CHAN]; /* Channel has a irq pending */ + +/* 7607 channel commands */ +#define IOCD 000 +#define TCH 010 +#define IORP 020 +#define IORT 030 +#define IOCP 040 +#define IOCT 050 +#define IOSP 060 +#define IOST 070 + +/* 7909 channel commands */ +#define WTR 000 +#define WTRX 004 +#define XMT 001 +#define XMTX 005 +#define TCH9 010 +#define TCHX 014 +#define LIPT 011 +#define LIPTX 015 +#define CTL 020 +#define CTLR 021 +#define CTLW 024 +#define SNS 025 +#define LAR 030 +#define SAR 031 +#define TWT 034 +#define XXXX 035 +#define CPYP 040 +#define CPYP2 041 +#define CPYP3 044 +#define CPYP4 045 +#define CPYD 050 +#define TCM 051 +#define CPYDX 054 +#define TCMX 055 +#define XXXZ 060 +#define LIP 061 +#define TDC 064 +#define LCC 065 +#define SMS 070 +#define ICC 071 +#define ICCX 075 + +/* Values for chan_info */ +#define CHAINF_START 1 /* Channel started */ +#define CHAINF_RUN 2 /* Transfer in progress */ + +#define nxt_chan_addr(chan) caddr[chan] = \ + ((dualcore) ? (0100000 & caddr[chan]) : 0) | \ + ((caddr[chan] + 1) & MEMMASK); + +/* Globally visible flags */ + +const char *chan_type_name[] = { + "Polled", "Unit Record", "7607", "7909", "7289"}; + +/* Delay device for IOD instruction */ +DIB dly_dib = + { CH_TYP_PIO, 1, 0333, 07777, &dly_cmd, NULL }; + + +UNIT chan_unit[] = { + /* Puesdo channel for 704 devices */ + {UDATA(NULL, UNIT_DISABLE | CHAN_SET | + CHAN_S_TYPE(CHAN_PIO)|UNIT_S_CHAN(0), 0)}, + /* Normal channels */ +#if NUM_CHAN > 1 + {UDATA(NULL, CHAN_AUTO | CHAN_SET | CHAN_S_TYPE(CHAN_7607)| + UNIT_S_CHAN(CHAN_A), 0)}, /* A */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_B), 0)}, /* B */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_C), 0)}, /* C */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_D), 0)}, /* D */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_E), 0)}, /* E */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_F), 0)}, /* F */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_G), 0)}, /* G */ + {UDATA(NULL, UNIT_DISABLE | CHAN_AUTO|UNIT_S_CHAN(CHAN_H), 0)} /* H */ +#endif +}; + +REG chan_reg[] = { + {BRDATA(ADDR, caddr, 8, 16, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(WC, wcount, 8, 15, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(ASM, assembly, 8, 36, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(LOCATION, location, 8, 16, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT}, + {BRDATA(COUNTER, counter, 8, 6, NUM_CHAN), REG_RO|REG_FIT }, + {BRDATA(SMS, sms, 2, 6, NUM_CHAN), REG_RO|REG_FIT}, + {NULL} +}; + +MTAB chan_mod[] = { +#ifdef I7090 + {CHAN_MODEL, CHAN_S_TYPE(CHAN_PIO), "704 Channel", NULL, NULL, NULL, NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7607), "7607", "7607", NULL, NULL, NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7909), "7909", "7909", NULL, NULL, NULL}, + {CHAN_MODEL, CHAN_S_TYPE(CHAN_7289), "7289", "7289", NULL, NULL, NULL}, + {CHAN_AUTO, 0, "FIXED", "FIXED", NULL, NULL, NULL}, + {CHAN_AUTO, CHAN_AUTO, "AUTO", "AUTO", NULL, NULL, NULL}, + {CHAN_SET, CHAN_SET, "set", NULL, NULL, NULL, NULL}, + {MTAB_VUN, 0, "Units", NULL, NULL, &print_chan, NULL}, +#endif + {0} +}; + +/* Simulator debug controls */ +DEBTAB chn_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {"CH0", 0x0100 << 0}, + {"CHA", 0x0100 << 1}, + {"CHB", 0x0100 << 2}, + {"CHC", 0x0100 << 3}, + {"CHD", 0x0100 << 4}, + {"CHE", 0x0100 << 5}, + {"CHF", 0x0100 << 6}, + {"CHG", 0x0100 << 7}, + {"CHH", 0x0100 << 8}, + {0, 0} +}; + +DEVICE chan_dev = { + "CH", chan_unit, chan_reg, chan_mod, + NUM_CHAN, 8, 15, 1, 8, 36, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + &dly_dib, DEV_DEBUG, 0, chn_debug, + NULL, NULL, &chan_help, NULL, NULL, &chan_description +}; + + +/* Nothing special to do, just return true if cmd is write and we got here */ +uint32 dly_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + if (cmd == IO_WRS) + return SCPE_OK; + return SCPE_NODEV; +} + + + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + if (chan_unit[i].flags & CHAN_AUTO) + chan_unit[i].flags &= ~CHAN_SET; + else + chan_unit[i].flags |= CHAN_SET; + chan_flags[i] = 0; + chan_info[i] = 0; + caddr[i] = 0; + cmd[i] = 0; + sms[i] = 0; + bcnt[i] = 6; + chan_irq[i] = 0; + wcount[i] = 0; + location[i] = 0; + counter[i] = 0; + } + return chan_set_devs(dptr); +} + +/* Boot from given device */ +t_stat +chan_boot(int32 unit_num, DEVICE * dptr) +{ + /* Tell device to do a read, 3 records */ + /* Set channel address = 0, wc = 3, location = 0, CMD=0 */ + /* Set M[1] = TCO? 1, IC = 1 */ + UNIT *uptr = &dptr->units[unit_num]; + int chan = UNIT_G_CHAN(uptr->flags); + + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) { + IC = 0; + } else { + IC = 1; + /* Grab next channel command */ + location[chan] = 0; + chan_fetch(chan); + } + chan_flags[chan] |= STA_ACTIVE; + chan_flags[chan] &= ~STA_PEND; + return SCPE_OK; +} + +/* Preform BCD to binary translation for 7909 channel */ +void +bcd_xlat(int chan, int direction) +{ + int i; + t_uint64 na = 0; + + for (i = 30; i >= 0; i -= 6) { + uint8 ch = (uint8)(assembly[chan] >> i) & 077; + + if (direction) { /* D->M Read */ + switch (ch & 060) { + case 000: + if (ch == 0) + ch = 060; + else if (ch == 012) + ch = 0; + break; + case 020: + case 060: + ch ^= 040; + case 040: + break; + } + } else { /* M->D Write */ + switch (ch & 060) { + case 000: + if (ch == 0) + ch = 012; + else if (ch == 012) + ch = 020; + break; + case 060: + if (ch == 060) + ch = 060; /* => 000 */ + case 020: + ch ^= 040; + case 040: + break; + } + } + na |= ((t_uint64) ch) << i; + } + assembly[chan] = na; +} + +/* Execute the next channel instruction. */ +void +chan_proc() +{ + int chan; + int cmask; + + /* Scan channels looking for work */ + for (chan = 0; chan < NUM_CHAN; chan++) { + /* Skip if channel is disabled */ + if (chan_unit[chan].flags & UNIT_DIS) + continue; + + /* If channel is disconnecting, do nothing */ + if (chan_flags[chan] & DEV_DISCO) + continue; + + cmask = 0x0100 << chan; + switch (CHAN_G_TYPE(chan_unit[chan].flags)) { + case CHAN_PIO: + if (chan_flags[chan] & CHS_ATTN) { + chan_flags[chan] &= + ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); + } + if ((chan_flags[chan] & (DEV_REOR|DEV_SEL|DEV_FULL)) == + (DEV_SEL|DEV_REOR)) { + sim_debug(DEBUG_DETAIL, &chan_dev, "chan got EOR\n"); + chan_flags[chan] |= (DEV_DISCO); + } + + break; +#ifdef I7090 + case CHAN_7289: /* Special channel for HS drum */ + /* On first command, copy it to drum address and load another */ + if ((chan_info[chan] & (CHAINF_RUN | CHAINF_START)) == + CHAINF_START) { + hsdrm_addr = (int)M[location[chan] - 1]; + chan_info[chan] |= CHAINF_RUN; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d HDaddr %06o\n", + chan, hsdrm_addr); + chan_fetch(chan); + continue; + } + if ((chan_info[chan] & CHAINF_START) == 0) + continue; + /* Fall through and behave like 7607 from now on */ + case CHAN_7607: + /* If no select, stop channel */ + if ((chan_flags[chan] & DEV_SEL) == 0 + && (chan_flags[chan] & STA_TWAIT)) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap\n", + chan); + iotraps |= 1 << chan; + chan_flags[chan] &= + ~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT); + chan_info[chan] = 0; + continue; + } + + /* If device requested attention, abort current command */ + if (chan_flags[chan] & CHS_ATTN) { + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO); + chan_flags[chan] &= + ~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); + chan_info[chan] = 0; + switch(cmd[chan]) { + case IORT: + case IOCT: + case IOST: + iotraps |= 1 << chan; + break; + } + iotraps |= 1LL << (chan + 18); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d attn< %o\n", chan, cmd[chan] & 070); + continue; + } + + /* If we are waiting and get EOR, then continue along */ + if ((chan_flags[chan] & (STA_WAIT|DEV_REOR|DEV_FULL)) == + (STA_WAIT|DEV_REOR)) { + chan_flags[chan] &= ~(STA_WAIT|DEV_WEOR); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, "chan %d clr wait EOR\n", + chan); + } + + /* All done if waiting for EOR */ + if (chan_flags[chan] & STA_WAIT) + continue; + + /* No activity, nothing happening here folks, move along */ + if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) { + /* Check if Trap wait and no pending LCHx, force disconnect */ + if ((chan_flags[chan] & (STA_TWAIT|STA_PEND|DEV_SEL)) + == (STA_TWAIT|DEV_SEL)) + chan_flags[chan] |= DEV_DISCO|DEV_WEOR; + continue; + } + + /* If command is a transfer, Do transfer */ + if ((cmd[chan] & 070) == TCH) { + location[chan] = caddr[chan]; + chan_fetch(chan); + /* Give up bus if next command is a tranfer. */ + if ((cmd[chan] & 070) == TCH) + continue; + } + + /* None disabled, active channel is if transfering */ + switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) { + /* Device has given us a dataword */ + case DEV_FULL: + /* If we are not waiting EOR save it in memory */ + if ((cmd[chan] & 1) == 0) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, "chan %d data < %012llo\n", + chan, assembly[chan]); + M[caddr[chan]] = assembly[chan]; + } else { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, "chan %d data * %012llo\n", + chan, assembly[chan]); + } + nxt_chan_addr(chan); + assembly[chan] = 0; + bcnt[chan] = 6; + wcount[chan]--; + chan_flags[chan] &= ~DEV_FULL; + + /* Device does not need a word and has not given us one */ + case 0: + /* Device idle, expecting data from it */ + + /* Check if got EOR */ + if (chan_flags[chan] & DEV_REOR) { + switch (cmd[chan] & 070) { + case IORP: + case IOSP: + chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR/* | STA_WAIT*/); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR< %o\n", + chan, cmd[chan] & 070); + chan_fetch(chan); + chan_flags[chan] |= STA_ACTIVE; + continue; /* Handle new command next time */ + case IORT: + case IOST: + chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR); + chan_flags[chan] &= ~(STA_ACTIVE/*|STA_WAIT*/); + chan_flags[chan] |= STA_TWAIT; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR< %o\n", + chan, cmd[chan] & 070); + continue; + } + } + + /* Done with transfer */ + if (wcount[chan] == 0 + /* && (chan_flags[chan] & STA_WAIT) == 0*/) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d < WC0 %o\n", chan, + cmd[chan] & 070); + switch (cmd[chan] & 070) { + case IOCD: /* Transfer and disconnect */ + chan_flags[chan] |= DEV_DISCO | DEV_WEOR; + chan_flags[chan] &= + ~(STA_START | STA_ACTIVE | STA_PEND); + if (CHAN_G_TYPE(chan_unit[chan].flags) == + CHAN_7289) { + iotraps |= 1 << chan; + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap\n", + chan); + } + chan_info[chan] = 0; + break; + + case IORP: /* Transfer until end of record */ + chan_flags[chan] |= STA_WAIT | DEV_WEOR; + break; + case IOSP: /* Transfer and proceed */ + case IOCP: /* Transfer and proceed, no eor */ + chan_fetch(chan); + break; + + case IORT: /* Transfer, continue if LCH pending, */ + /* else trap, Skip rest of record */ + chan_flags[chan] |= STA_WAIT | DEV_WEOR /*| STA_TWAIT*/; + break; + case IOST: /* Transfer, continue if LCH, else trap */ + case IOCT: /* Transfer but no end of record, else trap */ + chan_flags[chan] &= ~(STA_ACTIVE/*|STA_WAIT*/); + chan_flags[chan] |= STA_TWAIT; + break; + } + } + + /* Check if device left us */ + if ((chan_flags[chan] & DEV_SEL) == 0) { + switch (cmd[chan] & 070) { + case IOCP: + case IORP: + case IOSP: + case IOCD: + chan_flags[chan] &= ~(STA_START|STA_ACTIVE|STA_WAIT); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d -Sel< %o\n", chan, cmd[chan] & 070); + continue; /* Handle new command next time */ + case IOCT: + case IORT: + case IOST: /* Behave like EOR */ + chan_flags[chan] &= ~(STA_ACTIVE|STA_WAIT); + chan_flags[chan] |= STA_TWAIT; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d -Sel< %o\n", chan, cmd[chan] & 070); + continue; + } + } + + break; + + /* Device has word, but has not taken it yet */ + case DEV_WRITE | DEV_FULL: + if (chan_flags[chan] & DEV_REOR) { + switch (cmd[chan] & 070) { + case IORP: + case IORT: + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR>+ %o\n", chan, cmd[chan] & 070); + chan_flags[chan] &= ~DEV_FULL; + } + } + continue; /* Do nothing if no data xfer pending */ + + /* Device needs a word of data */ + case DEV_WRITE: /* Device needs data word */ + /* Check if device left us */ + if ((chan_flags[chan] & DEV_SEL) == 0) { + switch (cmd[chan] & 070) { + case IOCP: + case IORP: + case IOSP: + chan_fetch(chan); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d -Sel< %o\n", chan, cmd[chan] & 070); + continue; /* Handle new command next time */ + case IOCD: + chan_flags[chan] &= ~(STA_START|STA_ACTIVE); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d -Sel< %o\n", chan, cmd[chan] & 070); + continue; + case IOCT: + case IORT: + case IOST: + chan_flags[chan] &= ~(STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d -Sel< %o\n", chan, cmd[chan] & 070); + continue; + } + } + + /* Wait for device to recognize EOR */ + if (chan_flags[chan] & DEV_WEOR) + continue; + + /* Check if got EOR */ + if (chan_flags[chan] & DEV_REOR) { + switch (cmd[chan] & 070) { + case IORP: + chan_flags[chan] &= ~(DEV_REOR); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR> %o\n", chan, cmd[chan] & 070); + chan_fetch(chan); + chan_flags[chan] |= STA_ACTIVE; + break; + case IORT: + chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE); + chan_flags[chan] |= STA_TWAIT; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR> %o\n", chan, cmd[chan] & 070); + continue; + } + } + + /* Give device new word if we have one */ + if (wcount[chan] != 0) { + + if (cmd[chan] & 1) { + assembly[chan] = 0; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > *\n", chan); + } else { + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > %012llo\n", chan, + assembly[chan]); + } + nxt_chan_addr(chan); + bcnt[chan] = 6; + wcount[chan]--; + chan_flags[chan] |= DEV_FULL; + continue; /* Don't start next command until data taken */ + } + + /* Get here if wcount == 0 */ + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d > WC0 %o stat=%08x\n", + chan, cmd[chan] & 070, chan_flags[chan]); + + switch (cmd[chan] & 070) { + case IOCD: /* Transfer and disconnect */ + chan_flags[chan] |= DEV_DISCO | DEV_WEOR; + chan_flags[chan] &= + ~(STA_START | STA_ACTIVE | STA_PEND); + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7289) + iotraps |= 1 << chan; + chan_info[chan] = 0; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d > DISCO\n", chan); + break; + + case IORP: /* Transfer until end of record */ + chan_flags[chan] |= DEV_WEOR|STA_WAIT; + break; + case IOSP: /* Transfer and proceed */ + case IOCP: /* Transfer and proceed, no eor */ + chan_fetch(chan); + break; + + case IORT: /* Transfer, continue if LCH pending, */ + /* else trap, Skip rest of record */ + chan_flags[chan] |= DEV_WEOR|STA_WAIT; + break; + case IOST: /* Transfer, continue if LCH, else trap */ + case IOCT: /* Transfer but no end of record, else trap */ + chan_flags[chan] &= ~STA_ACTIVE; + chan_flags[chan] |= STA_TWAIT; + break; + } + } + break; + + case CHAN_7909: + again: + /* If waiting for EOR just spin */ + if (chan_flags[chan] & STA_WAIT) { + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(STA_WAIT|DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d EOR Continue\n", chan); + } + continue; + } + + /* Nothing more to do if not active. */ + if (chan_flags[chan] & STA_ACTIVE) { + /* Execute the next command */ + switch (cmd[chan]) { + case XXXZ: + case XXXX: + case TWT: + /* Check if command not allowed */ + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d CPU Trap\n", + chan); + iotraps |= 1 << chan; + chan_flags[chan] |= CTL_INHB; + case WTR: + case WTRX: + /* Check if command not allowed */ + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + /* Go into a wait state */ + chan_flags[chan] &= ~STA_ACTIVE; + location[chan]--; + break; + case XMT: + case XMTX: + /* Check if command not allowed */ + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + if (wcount[chan] == 0) + break; + wcount[chan]--; + M[caddr[chan]] = M[location[chan]]; + nxt_chan_addr(chan); + bcnt[chan] = 6; + location[chan]++; + continue; + case LIPT: + case LIPTX: + chan_flags[chan] &= ~(SNS_IRQ | SNS_IMSK | SNS_UEND); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d %02o LIPT\n", + chan, chan_flags[chan] & 077); + /* Fall through */ + + case TCH9: + case TCHX: + location[chan] = caddr[chan]; + break; + case LIP: + chan_flags[chan] &= ~(SNS_IRQ | SNS_IMSK | SNS_UEND); + location[chan] = (uint16)M[040 + (2 * chan)] & MEMMASK; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan %d %02o LIP\n", + chan, chan_flags[chan] & 077); + break; + case CTL: + if (chan_flags[chan] & CTL_CNTL) + goto xfer; + if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) { + chan9_seqcheck(chan); + continue; + } + chan_flags[chan] |= CTL_CNTL; + goto finddev; + case CTLR: + if (chan_flags[chan] & CTL_CNTL) + goto xfer; + if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) { + chan9_seqcheck(chan); + break; + } + chan_flags[chan] |= CTL_CNTL | CTL_PREAD; + goto finddev; + case CTLW: + if (chan_flags[chan] & CTL_CNTL) + goto xfer; + if (chan_flags[chan] & (CTL_READ | CTL_WRITE | CTL_SNS)) { + chan9_seqcheck(chan); + break; + } + chan_flags[chan] |= CTL_CNTL | CTL_PWRITE; + goto finddev; + case SNS: + if (chan_flags[chan] & (CTL_CNTL | CTL_READ | CTL_WRITE)) { + chan9_seqcheck(chan); + break; + } + chan_flags[chan] |= CTL_SNS; + finddev: + chan_flags[chan] &= ~(DEV_REOR|CTL_END|DEV_WEOR); + { + DEVICE **dptr; + UNIT *uptr; + DIB *dibp; + + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int num = (*dptr)->numunits; + int j; + + dibp = (DIB *) (*dptr)->ctxt; + /* If not device or 7909 type, just skip */ + if (dibp == 0 || (dibp->ctype & CH_TYP_79XX) == 0) + continue; + uptr = (*dptr)->units; + for (j = 0; j < num; j++, uptr++) { + if ((uptr->flags & UNIT_DIS) == 0 && + UNIT_G_CHAN(uptr->flags) == + (unsigned int)chan && + (sms[chan] & 1) == + ((UNIT_SELECT & uptr->flags) != 0)) { + goto found; + } + } + } + /* If no device, stop right now */ + chan9_set_error(chan, SNS_ADCHECK); + chan_flags[chan] &= ~(CTL_PREAD | CTL_PWRITE | CTL_SNS | + CTL_CNTL); + iotraps |= 1 << chan; + chan_flags[chan] &= ~STA_ACTIVE; + break; + found: + /* Get channel ready to transfer */ + chan_flags[chan] &= + ~(CTL_END|CTL_SEL|DEV_REOR|DEV_FULL); + bcnt[chan] = 6; + + /* Call device to start it running */ + if (sms[chan] & 1) + chan_flags[chan] |= CTL_SEL; + switch (dibp->cmd(uptr, cmd[chan], sms[chan])) { + case SCPE_IOERR: + case SCPE_NODEV: + chan9_set_error(chan, SNS_IOCHECK); + iotraps |= 1 << chan; + chan_flags[chan] &= + ~(CTL_PREAD|CTL_PWRITE|CTL_SNS|CTL_CNTL|STA_ACTIVE); + continue; + case SCPE_BUSY: /* Device not ready yet, wait */ + continue; + case SCPE_OK: /* Device will be waiting for command */ + break; + } + } + /* Special out for sense command */ + if (cmd[chan] == SNS) { + chan_flags[chan] &= ~DEV_WRITE; + chan_flags[chan] |= DEV_SEL; + break; + } + chan_flags[chan] |= DEV_WRITE; + xfer: + /* Check if comand tranfer done */ + if (chan_flags[chan] & DEV_REOR) { + chan_flags[chan] &= + ~(DEV_WRITE | DEV_REOR | DEV_FULL); + chan_flags[chan] &= ~(CTL_READ | CTL_WRITE); + if ((chan_flags[chan] & CTL_END) == 0) + chan_flags[chan] |= (chan_flags[chan] & + (CTL_PREAD | CTL_PWRITE)) >> 2; + if ((chan_flags[chan] & (SNS_UEND|CTL_END)) == + (SNS_UEND|CTL_END) && (sms[chan] & 010) == 0) + chan_flags[chan] &= ~STA_ACTIVE; + chan_flags[chan] &= ~(CTL_CNTL | CTL_PREAD | + CTL_PWRITE | CTL_END); + if (chan_flags[chan] & CTL_WRITE) + chan_flags[chan] |= DEV_WRITE; + bcnt[chan] = 6; + break; + } + + /* Check if device ready for next command word */ + if ((chan_flags[chan] & (DEV_WRITE | DEV_FULL)) == + DEV_WRITE) { + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CMD, &chan_dev, + "chan %d cmd > %012llo\n", + chan, assembly[chan]); + nxt_chan_addr(chan); + bcnt[chan] = 6; + chan_flags[chan] |= DEV_FULL; + } + continue; + + case LAR: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CMD, &chan_dev, + "chan %d LAR > %012llo\n", + chan, assembly[chan]); + break; + case SAR: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_CMD, &chan_dev, + "chan %d SAR < %012llo\n", + chan, assembly[chan]); + M[caddr[chan]] = assembly[chan]; + break; + case CPYP: + case CPYP2: + case CPYP3: + case CPYP4: + if (chan_flags[chan] & (DEV_REOR|CTL_END)) { + if (sms[chan] & 0100) { + chan9_set_error(chan, SNS_UEND); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO | DEV_WEOR); + chan_flags[chan] &= + ~(STA_WAIT|DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE); + break; + } + if (wcount[chan] != 0) + chan_flags[chan] &= ~(DEV_REOR); + } + case CPYD: + case CPYDX: + if ((chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS))==0) { + chan9_seqcheck(chan); + break; + } + + + if ((chan_flags[chan] & DEV_FULL) == 0) { + /* Check if we still have a select signal */ + if (wcount[chan] != 0 && + (chan_flags[chan] & DEV_SEL) == 0) { + chan9_seqcheck(chan); + break; + } + + /* Check if last word transfered */ + if (wcount[chan] == 0) { + if (cmd[chan] == CPYD || cmd[chan] == CPYDX || + chan_flags[chan] & SNS_UEND) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d DISC %o\n", chan, cmd[chan] & 070); + if (sms[chan] & 0100 && + (chan_flags[chan] & DEV_REOR) == 0) + chan9_set_error(chan, SNS_UEND); + chan_flags[chan] |= (DEV_WEOR); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= + ~(CTL_SNS | CTL_READ | CTL_WRITE); + if ((chan_flags[chan] & (SNS_UEND|CTL_END)) == + (SNS_UEND|CTL_END) && + (sms[chan] & 010) == 0) + chan_flags[chan] &= ~STA_ACTIVE; + } else { + if (chan_flags[chan] & DEV_REOR) + chan_flags[chan] &= ~DEV_REOR; + } + break; + } + + /* Check for record end in non-concurrent IRQ mode*/ + if (chan_flags[chan] & DEV_REOR && sms[chan] & 0100) { + chan9_set_error(chan, SNS_UEND); + chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= (DEV_DISCO | DEV_WEOR); + break; + } + } + + /* Check if ready to transfer something */ + switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) { + case DEV_WRITE | DEV_FULL: + case 0: + /* If device ended, quit transfer */ + if (chan_flags[chan] & CTL_END) { + /* Disconnect channel if select still active */ + if (chan_flags[chan] & DEV_SEL) { + chan_flags[chan] |= (DEV_DISCO); + chan_flags[chan] &= ~(STA_WAIT); + } + if (sms[chan] & 0100 && wcount[chan] != 0) + chan9_set_error(chan, SNS_UEND); + chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL|DEV_REOR| + CTL_SNS|CTL_READ|CTL_WRITE|CTL_END); + /* Get new command ready after disco */ + chan_fetch(chan); + } + continue; /* Do nothing if no data xfer */ + case DEV_WRITE: /* Device needs data word */ + assembly[chan] = M[caddr[chan]]; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data > %012llo\n", + chan, assembly[chan]); + if (sms[chan] & 020) /* BCD Xlat mode */ + bcd_xlat(chan, 0); + if (sms[chan] & 040) { /* Read backward */ + caddr[chan] = + ((dualcore) ? (0100000 & caddr[chan]) : 0) | + ((caddr[chan] - 1) & MEMMASK); + } else { + nxt_chan_addr(chan); + } + bcnt[chan] = 6; + wcount[chan]--; + chan_flags[chan] |= DEV_FULL; + break; + case DEV_FULL: /* Device has given us a dataword */ + if (bcnt[chan] != 0) + assembly[chan] <<= 6 * bcnt[chan]; + if (sms[chan] & 020) /* BCD Xlat mode */ + bcd_xlat(chan, 1); + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DATA, &chan_dev, + "chan %d data < %012llo\n", + chan, assembly[chan]); + M[caddr[chan]] = assembly[chan]; + if (sms[chan] & 040) { /* Read backward */ + caddr[chan] = + ((dualcore) ? (0100000 & caddr[chan]) : 0) | + ((caddr[chan] - 1) & MEMMASK); + } else { + nxt_chan_addr(chan); + } + assembly[chan] = 0; + bcnt[chan] = 6; + wcount[chan]--; + chan_flags[chan] &= ~DEV_FULL; + break; + } + + continue; + + case TCM: + case TCMX: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } else { + /* Compare wordcount high to wordcound low */ + /* 0 = chan check, 1-6 = assmebly, 7 = 0 */ + uint8 v; + uint8 ch = wcount[chan] >> 12; + uint8 mask = wcount[chan] & 077; + uint8 flag = wcount[chan] & 0100; + + if (ch == 0) { + v = (chan_flags[chan] >> 5) & 077; + } else if (ch == 7) { + v = 0; + } else { + v = (uint8)(077 & (assembly[chan] >> (6 * (6-ch)))); + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "TCM %d:%02o & %02o\n\r", ch, v, mask); + if ((v == mask && flag == 0) + || ((v & mask) == mask && flag != 0)) + location[chan] = caddr[chan]; + } + break; + case TDC: + if (counter[chan] != 0) { + location[chan] = caddr[chan]; + counter[chan]--; + } + break; + case LCC: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + counter[chan] = caddr[chan] & 077; + break; + case SMS: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan %d SMS %03o -> %03o %03o ", + chan, sms[chan], caddr[chan] & 0177, + (SNS_IRQS & chan_flags[chan])>>5); + sms[chan] = caddr[chan] & 0177; + /* Check to see if IRQ still pending */ + if ((chan_flags[chan] & CTL_INHB) == 0 && + chan_flags[chan] & SNS_IRQS & + (~((sms[chan] << 5) & (SNS_IMSK ^ SNS_IRQS)))) { + chan_irq[chan] = 1; + } + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, "Irqs = %03o %o\n", + ((chan_flags[chan] & SNS_IRQS)>>5) & + ((sms[chan] ^ 016) | 061), chan_irq[chan]); + break; + case ICC: + case ICCX: + if (chan_flags[chan] & DEV_SEL) { + chan9_seqcheck(chan); + break; + } + /* transfer counter from wordcount high to assembly */ + /* 0 = SMS to 6, 1-6 = assmebly, 7 = nop */ + { + t_uint64 v = counter[chan] & 077; + uint8 ch = wcount[chan] >> 12; + + if (ch == 0) { + /* Not what POO says, but what diags want */ + /* POO says other digits not affected. */ + assembly[chan] = sms[chan] & 00137; + } else if (ch != 7) { + assembly[chan] &= ~(077L << (6 * (6 - ch))); + assembly[chan] |= (v << (6 * (6 - ch))); + } + } + break; + } + } + + /* Check for intrupts */ + if (chan_irq[chan] || + /* Can only interupt when channel inactive */ + ((chan_flags[chan] & (DEV_SEL | STA_ACTIVE | CTL_CNTL | CTL_SNS + | SNS_IRQ | CTL_INHB | CTL_READ | CTL_WRITE)) == 0 && + cmd[chan] != TWT && + (chan_flags[chan] & SNS_IRQS & + (((sms[chan] ^ 016) | 061) << 5)))) { + uint8 ocmd = cmd[chan]; + M[040 + (chan * 2)] = location[chan] & MEMMASK; + M[040 + (chan * 2)] |= ((t_uint64) caddr[chan]) << 18; + chan_flags[chan] |= STA_ACTIVE|CTL_INHB; + location[chan] = 041 + (chan * 2); + chan_irq[chan] = 0; + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_TRAP, &chan_dev, "chan irq %d\n\r", chan); + chan_fetch(chan); + /* Fake a xec type trap */ + if ((ocmd & 073) == WTR || ocmd == TWT) + location[chan] = (uint16)(M[040 + (chan * 2)] + 1)& MEMMASK; + else + location[chan] = (uint16)M[040 + (chan * 2)] & MEMMASK; + goto again; + } + + if (chan_flags[chan] & STA_ACTIVE) { + uint8 c = cmd[chan]; + chan_fetch(chan); + /* Check if we should interupt during unusual end */ + if (sms[chan] & 0100 && (c & 070) == CPYP && + (cmd[chan] & 071) == CPYD && wcount[chan] == 0) { + if (chan_dev.dctrl & cmask) + sim_debug(DEBUG_DETAIL, &chan_dev, + "chan non-concur %d\n\r", chan); + chan9_set_error(chan, SNS_UEND); + chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE); + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_WEOR|DEV_DISCO; + chan_fetch(chan); + } + if (cmd[chan] != TCM && (chan_flags[chan] & DEV_DISCO) == 0) + goto again; + } +#endif + } + } +} + +void +chan_fetch(int chan) +{ + uint16 loc; + t_uint64 temp; + + sim_interval--; + loc = location[chan] & MEMMASK; + if (dualcore) + loc |= location[chan] & 0100000; + temp = M[loc]; + location[chan] = ((loc + 1) & MEMMASK) | (loc & 0100000); + cmd[chan] = (uint8)(((temp >> 30) & 074) | ((temp >> 16) & 1)); + wcount[chan] = (uint16)(temp >> 18) & 077777; + caddr[chan] = (uint16)temp & MEMMASK; + if (dualcore) + caddr[chan] |= temp & 0100000; + /* Check indirect bit */ + if (temp & 0400000) { + caddr[chan] = (uint16)M[caddr[chan]]; + if (dualcore) + caddr[chan] &= 0100000 | MEMMASK; + else + caddr[chan] &= MEMMASK; + sim_interval--; + } + /* Clear pending IO traps for channel */ + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CHAN, &chan_dev, + "chan %d fetch adr=%05o cmd=%03o caddr=%05o wcount=%05o\n", + chan, location[chan], cmd[chan], caddr[chan], + wcount[chan]); +} + +/* Reset the channel, clear any pending device */ +void +chan_rst(int chan, int type) +{ + /* Issure reset command to device */ + if (type == 0 && CHAN_G_TYPE(chan_unit[chan].flags) != CHAN_7909) + return; + if (type != 0 && CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) + return; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CHAN, &chan_dev, "Reset channel\n"); + /* Clear outstanding traps on reset */ + if (type) + iotraps &= ~(1 << chan); + chan_info[chan] &= ~(CHAINF_START | CHAINF_RUN); + chan_flags[chan] &= (CHS_EOF|CHS_BOT|CHS_EOT|DEV_DISCO|DEV_SEL); + caddr[chan] = 0; + cmd[chan] = 0; + sms[chan] = 0; + chan_irq[chan] = 0; + wcount[chan] = 0; + location[chan] = 0; + counter[chan] = 0; /* Channel memory address */ +} + +/* Issue a command to a channel */ +int +chan_cmd(uint16 dev, uint16 dcmd) +{ + UNIT *uptr; + uint32 chan; + DEVICE **dptr; + DIB *dibp; + int j; + + /* Find device on given channel and give it the command */ + chan = (dev >> 9) & 017; + /* If no channel device, quick exit */ + if (chan_unit[chan].flags & UNIT_DIS) + return SCPE_IOERR; + /* On 704 device new command aborts current operation */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO && + (chan_flags[chan] & (DEV_SEL | DEV_DISCO)) == DEV_SEL) { + /* Check if device picked up last transfer */ + if ((chan_flags[chan] & (DEV_FULL|DEV_WRITE)) == (DEV_FULL|DEV_WRITE)) + return SCPE_BUSY; + /* Yes, disconnect device and tell it to write a EOR */ + chan_flags[chan] |= DEV_DISCO | DEV_WEOR; + return SCPE_BUSY; + } + /* Unit is busy doing something, wait */ + if (chan_flags[chan] & (DEV_SEL | DEV_DISCO | STA_TWAIT | STA_WAIT)) + return SCPE_BUSY; + chan_flags[chan] &= ~(DEV_REOR|DEV_WEOR|DEV_FULL|DEV_WRITE|STA_WAIT); + /* Ok, try and find the unit */ + dev &= 07777; + for (dptr = sim_devices; *dptr != NULL; dptr++) { + int r; + + dibp = (DIB *) (*dptr)->ctxt; + /* If no DIB, not channel device */ + if (dibp == NULL || dibp->ctype == CHAN_7909 || + (dibp->addr & dibp->mask) != (dev & dibp->mask)) + continue; + uptr = (*dptr)->units; + if (dibp->upc == 1) { + int num = (*dptr)->numunits; + + for (j = 0; j < num; j++) { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) { + bcnt[chan] = 6; + cmd[chan] = 0; + caddr[chan] = 0; + location[chan] = 0; + return r; + } + } + uptr++; + } + } else { + if (UNIT_G_CHAN(uptr->flags) == chan) { + r = dibp->cmd(uptr, dcmd, dev); + if (r != SCPE_NODEV) { + bcnt[chan] = 6; + cmd[chan] = 0; + caddr[chan] = 0; + location[chan] = 0; + return r; + } + } + } + } + return SCPE_NODEV; +} + + +/* Give channel a new address to start working at */ +int +chan_start(int chan, uint16 addr) +{ + /* Hold this command until after channel has disconnected */ + if (chan_flags[chan] & DEV_DISCO) + return SCPE_BUSY; + + /* Depending on channel type controls how command works */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) { + if (chan_flags[chan] & STA_ACTIVE) + return SCPE_BUSY; + chan_flags[chan] &= + ~(CTL_CNTL | CTL_SNS | CTL_READ | CTL_PREAD | CTL_INHB | + CTL_WRITE | CTL_PWRITE | SNS_UEND | SNS_IOCHECK); + } else { + /* All clear, start ball rolling on new command */ + /* Force iocheck if attempt to load inactive channel with command */ + if ((chan_flags[chan] & DEV_SEL) == 0) { + /* Fetch next command */ + location[chan] = addr; + chan_fetch(chan); + return SCPE_IOERR; + } + } + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CHAN, &chan_dev, + "chan %d start IC=%05o addr=%o\n\r", chan, IC - 1, addr); + /* Fetch next command */ + location[chan] = addr; + chan_fetch(chan); + chan_flags[chan] &= ~(STA_PEND|STA_TWAIT|STA_WAIT|DEV_WEOR|DEV_FULL); + chan_flags[chan] |= STA_START | STA_ACTIVE; + chan_info[chan] |= CHAINF_START; + return SCPE_OK; +} + +/* Give channel a new address to start working at */ +int +chan_load(int chan, uint16 addr) +{ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) { + if (chan_flags[chan] & STA_ACTIVE) + return SCPE_BUSY; + /* Ignore command if this waiting on IRQ */ + if (cmd[chan] == TWT && iotraps & (1 << chan)) + return SCPE_OK; + chan_flags[chan] &= ~(CTL_INHB); + location[chan] = caddr[chan]; + } else { + /* Force iocheck if attempt to load channel with command, + that has not been started or is not in select state */ + if ((chan_flags[chan] & (DEV_SEL | STA_START)) != (DEV_SEL|STA_START)) + return SCPE_IOERR; + + /* If channel active, or waiting EOR, should hold CPU */ + if (chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) { + chan_flags[chan] |= STA_PEND; + return SCPE_BUSY; + } + chan_flags[chan] &= ~(STA_PEND|STA_TWAIT); + location[chan] = addr; + } + /* All clear, start ball rolling on new command */ + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_CHAN, &chan_dev, + "chan %d load IC=%05o addr=%o stat=%08x\n\r", chan, IC - 1, + addr, chan_flags[chan]); + chan_fetch(chan); + chan_flags[chan] |= STA_ACTIVE; + return SCPE_OK; +} + +/* return the channels current command address */ +void +chan_store(int chan, uint16 loc) +{ + t_uint64 reg = 0LL; + + /* Check if channel has units attached */ + if (chan_unit[chan].flags & CHAN_SET) { + /* Return command/addr/xcmd/location */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) { + reg = location[chan] & MEMMASK; + reg |= ((t_uint64) caddr[chan]) << 18; + /* Check if channel has units attached */ + } else { + /* If doing a TCH, process it */ + if ((cmd[chan] & 070) == TCH) + chan_proc(); + reg = caddr[chan]; + reg |= ((t_uint64) (location[chan] & MEMMASK)) << 18; + reg |= ((t_uint64) (cmd[chan] & 070)) << 30; + reg |= ((t_uint64) (cmd[chan] & 01)) << 16; + } + } + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_SNS, &chan_dev, "chan %d status %012llo\n\r", + chan,reg); + M[loc & (MEMMASK | 0100000)] = reg; +} + +/* Store channel diagnostic bits */ +void +chan_store_diag(int chan, uint16 loc) +{ + t_uint64 reg; + int results; + + /* Counter[6], iocheck,seq check, unusal end, attn 1, + * attn 2, adpter check, prepare to read, prepare to write, + * read status, write status, interupt */ + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7909) { + reg = ((t_uint64) counter[chan]) << 30; + results = SNS_MASK & chan_flags[chan]; + if (results & (((sms[chan] ^ 016) | 061) << 5)) + results |= 1; + reg |= ((t_uint64) (results)) << 19; + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_SNS, &chan_dev, "chan %d diags %012llo\n\r", + chan,reg); + M[loc & (MEMMASK | 0100000)] = reg; + } +} + +/* + * Write a word to the assembly register. + */ +int +chan_write(int chan, t_uint64 * data, int flags) +{ + + /* Check if last data still not taken */ + if (chan_flags[chan] & DEV_FULL) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR|DEV_FULL); + return END_RECORD; + } + + /* If active set attention, report IO error if + device does not want to disconnect */ + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + + /* If requested, force disconnect */ + chan_flags[chan] |= DEV_DISCO & flags; + + return TIME_ERROR; + } else { + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) + MQ = *data; + assembly[chan] = *data; + bcnt[chan] = 6; + chan_flags[chan] |= DEV_FULL; + chan_flags[chan] &= ~DEV_WRITE; + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_REOR; + } + } + + return DATA_OK; +} + +/* + * Read next word from assembly register. + */ +int +chan_read(int chan, t_uint64 * data, int flags) +{ + + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* Check if no data waiting */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_WRITE; + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* If active set attention, report IO error if + device does not want to disconnect */ + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + + /* If requested, force disconnect */ + chan_flags[chan] |= DEV_DISCO & flags; + + return TIME_ERROR; + } else { + *data = assembly[chan]; + bcnt[chan] = 6; + chan_flags[chan] &= ~DEV_FULL; + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE); + chan_flags[chan] |= DEV_REOR; + } else + chan_flags[chan] |= DEV_WRITE; + } + return DATA_OK; +} + +/* + * Write a char to the assembly register. + */ +int +chan_write_char(int chan, uint8 * data, int flags) +{ + /* If Writing end of record, abort */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR); + return END_RECORD; + } + + /* Check if last data still not taken */ + if (chan_flags[chan] & DEV_FULL) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR|DEV_FULL); + return END_RECORD; + } + + /* If active set attention, report IO error if + device does not want to disconnect */ + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + + /* If requested, force disconnect */ + chan_flags[chan] |= DEV_DISCO & flags; + + return TIME_ERROR; + } else { + int cnt = --bcnt[chan]; + t_uint64 wd; + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) + wd = MQ; + else + wd = assembly[chan]; + wd &= 0007777777777LL; + wd <<= 6; + wd |= (*data) & 077; + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) + MQ = wd; + else + assembly[chan] = wd; + + if (cnt == 0) { + chan_flags[chan] |= DEV_FULL; + chan_flags[chan] &= ~DEV_WRITE; + } + if (flags & DEV_REOR) { + chan_flags[chan] |= DEV_FULL|DEV_REOR; + chan_flags[chan] &= ~DEV_WRITE; + } + } + + + return DATA_OK; +} + +/* + * Read next char from assembly register. + */ +int +chan_read_char(int chan, uint8 * data, int flags) +{ + + /* Return END_RECORD if requested */ + if (flags & DEV_WEOR) { + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* Check if he write out last data */ + if ((chan_flags[chan] & DEV_FULL) == 0) { + /* Nope, see if we are waiting for end of record. */ + if (chan_flags[chan] & DEV_WEOR) { + chan_flags[chan] |= DEV_WRITE|DEV_REOR; + chan_flags[chan] &= ~(DEV_WEOR); + return END_RECORD; + } + + /* If active set attention, report IO error if + device does not want to disconnect */ + if (chan_flags[chan] & STA_ACTIVE) { + chan_flags[chan] |= CHS_ATTN; + if ((flags & DEV_DISCO) == 0) + iocheck = 1; + } + + /* If requested, force disconnect */ + chan_flags[chan] |= DEV_DISCO & flags; + + return TIME_ERROR; + } else { + int cnt = --bcnt[chan]; + t_uint64 wd = assembly[chan]; + *data = (uint8)(077 & (wd >> 30)); + wd <<= 6; + wd |= 077 & (wd >> 36); + wd &= 0777777777777LL; + if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_PIO) + MQ = wd; + assembly[chan] = wd; + if (cnt == 0) { + chan_flags[chan] &= ~DEV_FULL; + bcnt[chan] = 6; + } + /* If end of record, don't transfer any data */ + if (flags & DEV_REOR) { + chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL); + chan_flags[chan] |= DEV_REOR; + } else + chan_flags[chan] |= DEV_WRITE; + } + return DATA_OK; +} + +void +chan9_seqcheck(int chan) +{ + /* Disconnect channel if active */ + if (chan_flags[chan] & DEV_SEL) + chan_flags[chan] |= DEV_DISCO; + chan_flags[chan] &= ~(CTL_READ|CTL_WRITE|CTL_SNS|STA_ACTIVE); + if (chan_dev.dctrl & (0x0100 << chan)) + sim_debug(DEBUG_EXP, &chan_dev, "chan %d seq\n", chan); + chan9_set_error(chan, SNS_SEQCHECK); +} + +void +chan9_set_error(int chan, uint32 mask) +{ + if (chan_flags[chan] & mask) + return; + chan_flags[chan] |= mask; + if (mask & (~((sms[chan] << 5) & (SNS_IMSK ^ SNS_IRQS)))) { + chan_irq[chan] = 1; + } +} + +t_stat +chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +#ifdef I7090 + fprintf(st, "%s\n\n", chan_description(dptr)); + fprintf (st, "The 7090 supports up to 8 channels. Channel models include\n\n"); + fprintf (st, " Unit record Polled mode I/O devices\n"); + fprintf (st, " 7607 standard multiplexor channel\n"); + fprintf (st, " 7909 advanced capabilities channel\n"); + fprintf (st, " 7289 special channel for high speed drum\n\n"); + fprintf (st, "Channels can be reconfigured on the 7090, this generally "); + fprintf (st, "happens automatically.\nHowever at times it can be useful to "); + fprintf (st, "force a channel to a specific device. If\ndevices are attached"); + fprintf (st, "to incorrect channel types an error will be reported at sim\n"); + fprintf (st, "start. The first channel is fixed for Polled mode devices.\n\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); +#else + fprintf(st, "IBM 704 Channel\n\n"); + fprintf(st, "Psuedo device to display IBM 704 I/O. The IBM 704 used polled"); + fprintf(st, " I/O,\nThe assembly register and the flags can be displayed\n"); + fprintf(st, "There are no options for the this device\n"); +#endif +return SCPE_OK; +} + +const char * +chan_description(DEVICE *dptr) +{ + return "IBM 7090 channel controller"; +} diff --git a/I7000/i7090_cpu.c b/I7000/i7090_cpu.c new file mode 100644 index 00000000..73c9f0f2 --- /dev/null +++ b/I7000/i7090_cpu.c @@ -0,0 +1,4435 @@ +/* i7090_cpu.c: IBM 7090 CPU simulator + + 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. + + cpu 7094 central processor + rtc real time clock + + The IBM 7090 was first introduced as the IBM 704. This led to the 709, + 7090, 7094, 7040 and 7044. All where 36 bit signed magnitude machines. + They were single address machines and had 3 or more index registers. + These were the first machines to include indexing. Also first machines + to impliment indirect addressing, built in floating point, first Fortran + compiler, first TimeShareing Sytem CTSS. + + IBM 704: Announced May 7, 1954 withdrawn April 7, 1960. + First floating point. + First index addressing. + Memory to 32k words. + + IBM 709: Announced January 2, 1957 withdrawn April 7, 1960. + First indirect addressing. + First I/O channel. + Added indicator register. + + IBM 7090: Announced Decemeber 30, 1958 withdrawn July 14, 1969. + Transister version of 709. + + IBM 7094 & IBM 7094/II: Announced January 15, 1962 withdrawn July 14, 1969. + Added double precision floating point. + Added up to 7 index registers. + + IBM 7040 Announced April 1961. + Transisterize 704, with channel. + Character operate instructions. + Currently not implmented in simulator. + + IBM 7044 Announced 1961 + Enhanced verion of 7040. + Currently not implmented in simulator. + + The system state for the IBM 7090 is: + + AC AC register + MQ MQ register + XR<0:15>[8] XR index registers register + IC<0:15> program counter + SSW<0:5> sense switches + SLT<0:3> sense lights + ID indicators lights + ACOVF AC overflow + MQOVF MQ overflow + DVC divide check + IOC I/O check + TM transfer trap mode + CTM copy trap mode (for 709 compatibility) + FTM floating trap mode (off is 704 compatibility) + STM select trap mode + NMODE storage nullifcation mode + MTM multi-tag mode (7090 compatibility) + + CTSS required a set of special features: memory extension (to 65K), + protection, and relocation. Additional state: + + INST_BASE instruction memory select (A vs B core) + DATA_BASE data memory select (A vs B core) + BASE<0:6> start address block + LIMIT<0:6> limit address block + + The 7094 had five instruction formats: memory reference, + memory reference with count, convert, decrement, and immediate. + + 00000000011 11 1111 112 222222222333333 + S12345678901 23 4567 890 123456789012345 + +------------+--+----+---+---------------+ + | opcode |ND|0000|tag| address | memory reference + +------------+--+----+---+---------------+ + + 00000000011 111111 112 222222222333333 + S12345678901 234567 890 123456789012345 + +------------+------+---+---------------+ + | opcode | count|tag| address | memory reference + +------------+------+---+---------------+ with count + + 000000000 11111111 11 2 222222222333333 + S123456789 01234567 89 0 123456789012345 + +----------+--------+--+-+---------------+ + | opcode | count |00|X| address | convert + +----------+--------+--+-+---------------+ + + 00 000000011111111 112 222222222333333 + S12 345678901234567 890 123456789012345 + +---+---------------+---+---------------+ + |opc| decrement |tag| address | decrement + +---+---------------+---+---------------+ + + 00000000011 111111 112222222222333333 + S12345678901 234567 890123456789012345 + +------------+------+------------------+ + | opcode |000000| immediate | immediate + +------------+------+------------------+ + + This routine is the instruction decode routine for the 7094. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until a stop condition occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + illegal instruction + illegal I/O operation for device + illegal I/O operation for channel + breakpoint encountered + nested XEC's exceeding limit + divide check + I/O error in I/O simulator + + 2. Data channel traps. The 7094 is a channel-based system. + Channels can generate traps for errors and status conditions. + Channel trap state: + + iotraps[0..7] flags for channels A..H + ioflags channel trap enables + itrap channel trap inhibit due to trap (cleared by RCT) + ihold channel trap inhibit due to XEC, ENAB, RCT, RDS, + or WDS (cleared after one instruction) + + 3. Arithmetic. The 7094 uses signed magnitude arithmetic for + integer and floating point calculations, and 2's complement + arithmetic for indexing calculations. + + 4. Adding I/O devices. These modules must be modified: + + i7094_defs.h add device definitions + i7094_chan.c add channel subsystem + i7094_sys.c add sim_devices table entry +*/ + +#include "i7090_defs.h" +#include "sim_timer.h" +#include +#include +#ifdef CPANEL +#include "cpanel.h" +#endif + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) +#define UNIT_MSIZE (7 << UNIT_V_MSIZE) +#define UNIT_V_CPUMODEL (UNIT_V_UF + 4) +#define UNIT_MODEL (0x3 << UNIT_V_CPUMODEL) +#define CPU_MODEL ((cpu_unit.flags >> UNIT_V_CPUMODEL) & 0x3) +#define MODEL(x) (x << UNIT_V_CPUMODEL) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) +#define UNIT_DUALCORE (1 << (UNIT_V_CPUMODEL + 2)) +#define UNIT_FASTIO (1 << (UNIT_V_CPUMODEL + 3)) +#define OPTION_EFP (1 << (UNIT_V_CPUMODEL + 4)) +#define OPTION_TIMER (1 << (UNIT_V_CPUMODEL + 5)) +#define OPTION_FPSM (1 << (UNIT_V_UF_31)) + +#define CPU_704 0 +#define CPU_709 1 +#define CPU_7090 2 +#define CPU_7094 3 + +#define TMR_RTC 0 + +#define HIST_XCT 1 /* instruction */ +#define HIST_INT 2 /* interrupt cycle */ +#define HIST_TRP 3 /* trap cycle */ +#define HIST_MIN 64 +#define HIST_MAX 10000 +#define HIST_NOEA 0x40000000 +#define HIST_PC 0x10000 + +struct InstHistory +{ + t_int64 ac; + t_int64 mq; + t_int64 op; + t_int64 sr; + uint32 ic; + uint16 ea; + uint16 xr1; + uint16 xr2; + uint16 xr4; +}; + +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + CONST void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +uint32 cpu_cmd(UNIT * uptr, uint16 cmd, uint16 dev); +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + +/* Interval timer option */ +t_stat rtc_srv(UNIT * uptr); +t_stat rtc_reset(DEVICE * dptr); +int32 rtc_tps = 60; + +t_uint64 M[MAXMEMSIZE] = { 0 }; /* memory */ +t_uint64 AC, MQ; /* registers */ +uint16 XR[8]; /* Index registers */ +uint16 IC; /* program counter */ +uint16 IR; /* Instruction register */ +uint16 MA; /* Memory Address resister */ +t_uint64 ID; /* Indicator registers */ +t_uint64 SR; /* Internal temp register */ +t_uint64 KEYS; /* Console Keys */ +uint8 SL; /* Sense lights */ +uint16 SW = 0; /* Sense switch */ +uint8 MTM; /* Multi Index mode */ +uint8 TM; /* Trap mode */ +uint8 STM; /* Special trap mode */ +uint8 CTM; /* Copy trap mode */ +uint8 FTM; /* Floating trap mode */ +uint8 nmode; /* Storage null mode */ +uint8 smode; /* Signifigance mode */ +uint8 itrap; /* Can take io traps */ +uint8 dcheck; /* Divide check */ +uint8 acoflag; /* AC Overflow */ +uint8 mqoflag; /* MQ Overflow */ +uint8 ihold = 0; /* Hold interrupts */ +uint8 interval_irq = 0; /* Interval timer IRQ */ +uint16 iotraps; /* IO trap flags */ +t_uint64 ioflags = 0; /* Trap enable flags */ +uint8 iocheck; +uint8 prot_pend; /* Protection mode pending */ +uint8 relo_mode; /* Relocation mode */ +uint8 relo_pend; /* Relocation mode pending */ +uint8 hltinst; /* Executed halt instruction */ +uint8 iowait; /* Waiting on io */ +uint16 relocaddr = 0; /* Relocation. */ +uint16 baseaddr = 0; /* Base Address. */ +uint16 limitaddr = 077777; /* High limit */ +uint16 memmask = 077777; /* Mask for memory access */ +uint8 bcore; /* Access to B core memory */ + /* 00 - Execute A Core, Eff A */ + /* 01 - Execute A core, Eff B */ + /* 10 - Execute B core, Eff A */ + /* 11 - Execute B core, Eff B */ + /* 1xx - Protection mode */ + /* 1xxx - Relocation mode */ +uint8 dualcore; /* Set to true if dual core in + use */ + +uint16 dev_pulse[NUM_CHAN]; /* SPRA device pulses */ +int cycle_time = 12; /* Cycle time in 100ns */ +uint8 exe_KEYS = 0; /* Execute one instruction + from KEYS, used by CPANEL */ + +/* History information */ +int32 hst_p = 0; /* History pointer */ +int32 hst_lnt = 0; /* History length */ +struct InstHistory *hst = NULL; /* History stack */ +extern uint32 drum_addr; + +#define DP_FLOAT 1 +#define CHANNEL 2 +#define MULTIIX 4 +#define TIMER 7 +#define INDICATORS 16 +#define PROTECT 32 +#define DUALCORE 64 + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = + { UDATA(rtc_srv, UNIT_BINK | MODEL(CPU_7090) | MEMAMOUNT(4), + MAXMEMSIZE/2 ), 120 }; + +REG cpu_reg[] = { + {ORDATAD(IC, IC, 15, "Instruction Counter"), REG_FIT}, + {ORDATAD(IR, IR, 10, "Instruction Register"), REG_FIT}, + {ORDATAD(AC, AC, 38, "Accumulator"), REG_FIT}, + {ORDATAD(MQ, MQ, 36, "Multiplier Quotent"), REG_FIT}, + {BRDATAD(XR, XR, 8, 15, 8, "Index registers"), REG_FIT}, + {ORDATAD(ID, ID, 36, "Indicator Register")}, + {ORDATAD(MA, MA, 15, "Memory Address Register"), REG_FIT}, +#ifdef EXTRA_SL + {ORDATAD(SL, SL, 8, "Sense Lights"), REG_FIT}, +#else + {ORDATAD(SL, SL, 4, "Sense Lights"), REG_FIT}, +#endif +#ifdef EXTRA_SW + {ORDATAD(SW, SW, 12, "Sense Switches"), REG_FIT}, +#else + {ORDATAD(SW, SW, 6, "Sense Switches"), REG_FIT}, +#endif + {FLDATA(SW1, SW, 0), REG_FIT}, + {FLDATA(SW2, SW, 1), REG_FIT}, + {FLDATA(SW3, SW, 2), REG_FIT}, + {FLDATA(SW4, SW, 3), REG_FIT}, + {FLDATA(SW5, SW, 4), REG_FIT}, + {FLDATA(SW6, SW, 5), REG_FIT}, +#ifdef EXTRA_SW + {FLDATA(SW7, SW, 6), REG_FIT}, + {FLDATA(SW8, SW, 7), REG_FIT}, + {FLDATA(SW9, SW, 8), REG_FIT}, + {FLDATA(SW10, SW, 9), REG_FIT}, + {FLDATA(SW11, SW, 10), REG_FIT}, + {FLDATA(SW12, SW, 11), REG_FIT}, +#endif + {ORDATAD(KEYS, KEYS, 36, "Console Key Register"), REG_FIT}, + {ORDATAD(MTM, MTM, 1, "Multi Index registers"), REG_FIT}, + {ORDATAD(TM, TM, 1, "Trap mode"), REG_FIT}, + {ORDATAD(STM, STM, 1, "Select trap mode"), REG_FIT}, + {ORDATAD(CTM, CTM, 1, "Copy Trap Mode"), REG_FIT}, + {ORDATAD(FTM, FTM, 1, "Floating trap mode"), REG_FIT}, + {ORDATAD(NMODE, nmode, 1, "Storage null mode"), REG_FIT}, + {ORDATAD(ACOVF, acoflag, 1, "AC Overflow Flag"), REG_FIT}, + {ORDATAD(MQOVF, mqoflag, 1, "MQ Overflow Flag"), REG_FIT}, + {ORDATAD(IOC, iocheck, 1, "I/O Check flag"), REG_FIT}, + {ORDATAD(DVC, dcheck, 1, "Divide Check flag"), REG_FIT}, + {ORDATAD(RELOC, relocaddr, 14, "Relocation offset"), REG_FIT}, + {ORDATAD(BASE, baseaddr, 14, "Relocation base"), REG_FIT}, + {ORDATAD(LIMIT, limitaddr, 14, "Relocation limit"), REG_FIT}, + {ORDATAD(ENB, ioflags, 36, "I/O Trap Flags"), REG_FIT}, + {FLDATA(INST_BASE, bcore, 0), REG_FIT}, + {FLDATA(DATA_BASE, bcore, 1), REG_FIT}, + {NULL} +}; + +MTAB cpu_mod[] = { + {UNIT_MODEL, MODEL(CPU_704), "704", "704", NULL, NULL, NULL}, +#ifdef I7090 + {UNIT_MODEL, MODEL(CPU_709), "709", "709", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(CPU_7090), "7090", "7090", NULL, NULL, NULL}, + {UNIT_MODEL, MODEL(CPU_7094), "7094", "7094", NULL, NULL, NULL}, +#endif + {UNIT_MSIZE, MEMAMOUNT(0), "4K", "4K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(1), "8K", "8K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(2), "16K", "16K", &cpu_set_size}, + {UNIT_MSIZE, MEMAMOUNT(4), "32K", "32K", &cpu_set_size}, +#ifdef I7090 + {UNIT_FASTIO, 0, NULL, "TRUEIO", NULL, NULL, NULL, + "True I/O mode"}, + {UNIT_FASTIO, UNIT_FASTIO, "FASTIO", "FASTIO", NULL, NULL, NULL, + "Fast I/O mode"}, + {OPTION_EFP, 0, NULL, "NOEFP", NULL, NULL, NULL}, + {OPTION_EFP, OPTION_EFP, "EFP", "EFP", NULL, NULL, NULL, "Extended FP"}, + {OPTION_FPSM, 0, NULL, "NOFPSM", NULL, NULL, NULL}, + {OPTION_FPSM, OPTION_FPSM, "FPSM", "FPSM", NULL, NULL, NULL, "Signfigance mode"}, + {OPTION_TIMER, 0, NULL, "NOCLOCK", NULL, NULL, NULL}, + {OPTION_TIMER, OPTION_TIMER, "CLOCK", "CLOCK", NULL, NULL, NULL}, + {UNIT_DUALCORE, 0, NULL, "STANDARD", NULL, NULL, NULL}, + {UNIT_DUALCORE, UNIT_DUALCORE, "CTSS", "CTSS", NULL, NULL, NULL, "CTSS support"}, +#endif + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 16, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description +}; + + +#define T_B 0x0001 /* Do load with indirection */ +#define T_D 0x0002 /* Do ea, but no indirection */ +#define T_F 0x0004 /* Ea = Y, no indirection */ +#define T_T 0x0008 /* Do ea with indirection, no load */ +#define S_B 0x0010 /* Do ea, but store at end */ +#define S_F 0x0020 /* Ea = Y, no indirection, store */ +#define S_X 0x0040 /* Store index registers with SR */ +#define T_N 0x0100 /* Nop op addressing, but instruct */ +#define I_9 0x0200 /* 709 and up */ +#define I_94 0x0400 /* 7094 only */ +#define I_D 0x0800 /* Need dual core */ +#define X_T 0x1000 /* Trap if in io trap mode or protection */ +#define X_P 0x2000 /* Trap if in protection enabled */ +#define X_C 0x4000 /* Trap if in copy trap mode or protection */ +#define N 0x0000 /* No instruction */ + +/* + Opcode flags, and execution. + + T_B T_D T_T T_F S_B S_X S_F + * * * * SR <- MA <- Y - xr + * * * SR <- MA <- [MA] if ind + * * * SR <- [MA] + * * [MA] <- SR + * xr <- SR +*/ + +/* Positive opcodes */ +uint16 pos_opcode_flags[01000] = { + /* 0 1 2 3 4 5 6 7 */ + /*HTR*/ +/* 0000 */ T_T, N, N, N, N, N, N, N, +/* 0010 */ N, N, N, N, N, N, N, N, + /*TRA TTR TRCA TRCC TRCE TRCG */ +/* 0020 */ T_T, T_T, X_T|T_T,N, X_T|T_T,N, X_T|T_T,X_T|T_T, + /* TEFA TEFC TEFE TEFG */ +/* 0030 */ X_T|T_T, X_T|T_T,X_T|T_T,X_T|T_T, N, N, N, N, + /* TLQ IIA TIO OAI PAI TIF */ +/* 0040 */ T_T, I_9, I_9|T_T,I_9, I_9, N, I_9|T_T, N, + /* IIR RFT SIR RNT RIR */ +/* 0050 */ N, I_9, N, N, I_9, I_9, I_9, I_9, + /* TCOA TCOB TCOC TCOD TCOE TCOF TCOG TCOH */ +/* 0060 */ X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, + /* TSX */ +/* 0070 */ N, N, N, N, S_X, N, N, N, + /* TZE TIA */ +/* 0100 */ T_T, I_D|X_P|T_T,N, N, N, N, N, N, + /* CVR CVR+100*/ +/* 0110 */ N, N, N, N, I_9, I_9, I_9, I_9, + /* TPL */ +/* 0120 */ T_T, N, N, N, N, N, N, N, + /* XCA */ +/* 0130 */ N, I_9|T_N, N, N, N, N, N, N, + /* TOV */ +/* 0140 */ T_T, N, N, N, N, N, N, N, +/* 0150 */ N, N, N, N, N, N, N, N, + /* TQO TQP */ +/* 0160 */ N, T_T, T_T, N, N, N, N, N, +/* 0170 */ N, N, N, N, N, N, N, N, + /* MPY VLM VLM + 100 */ +/* 0200 */ T_B, N, N, N, I_9|T_B, I_9|T_B,N, N, +/* 0210 */ N, N, N, N, N, N, N, N, + /* DVH DVP VDH VDP VDH+200 VDP+200 */ +/* 0220 */ T_B, T_B, N, N, I_9|T_B,I_9|T_B,I_9|T_B,I_9|T_B, +/* 0230 */ N, N, N, N, N, N, N, N, + /* FDH FDP*/ +/* 0240 */ T_B, T_B, N, N, N, N, N, N, +/* 0250 */ N, N, N, N, N, N, N, N, + /* FMP DFMP */ +/* 0260 */ T_B, I_94|T_B,N, N, N, N, N, N, +/* 0270 */ N, N, N, N, N, N, N, N, + /* FAD DFAD FSB DFSB FAM DFAM FSM DFSM */ +/* 0300 */ T_B, I_94|T_B, T_B, I_94|T_B, I_9|T_B,I_94|T_B,I_9|T_B,I_94|T_B, +/* 0310 */ N, N, N, N, N, N, N, N, + /* ANS ERA */ +/* 0320 */ T_B|S_B,N, I_9|T_B, N, N, N, N, N, +/* 0330 */ N, N, N, N, N, N, N, N, + /* CAS */ +/* 0340 */ T_B, N, N, N, N, N, N, N, +/* 0350 */ N, N, N, N, N, N, N, N, + /* ACL */ +/* 0360 */ N, T_B, N, N, N, N, N, N, +/* 0370 */ N, N, N, N, N, N, N, N, + /* ADD ADM SUB */ +/* 0400 */ T_B, T_B, T_B, N, N, N, N, N, +/* 0410 */ N, N, N, N, N, N, N, N, + /* HPR */ +/* 0420 */ T_N, N, N, N, N, N, N, N, +/* 0430 */ N, N, N, N, N, N, N, N, + /* IIS LDI OSI DLD OFT RIS ONT */ +/* 0440 */I_9|T_B,I_9|T_B,I_9|T_B,I_94|T_B,I_9|T_B,I_9|T_B,I_9|T_B, N, +/* 0450 */ N, N, N, N, N, N, N, N, + /* LDA */ +/* 0460 */X_C|T_B,N, N, N, N, N, N, N, +/* 0470 */ N, N, N, N, N, N, N, N, + /* CLA CLS */ +/* 0500 */ T_B, N, T_B, N, N, N, N, N, +/* 0510 */ N, N, N, N, N, N, N, N, + /* ZET XEC */ +/* 0520 */ T_B, N, I_9|T_B, N, N, N, N, N, + /* LXA LAC */ +/* 0530 */ N, N, N, N, S_X|T_F,T_F|S_X,N, N, + /* RSCA RSCC RSCE RSCG STCA STCC STCE STCG */ +/* 0540 */ X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, +/* 0550 */ N, N, N, N, N, N, N, N, + /* LDQ ECA LRI ENB */ +/* 0560 */ T_B, T_B|S_B,I_D|X_P|T_B,N, I_9|T_B,N, N, N, +/* 0570 */ N, N, N, N, N, N, N, N, + /* STZ STO SLW STI*/ +/* 0600 */ S_B, S_B, S_B, N, I_9|S_B,N, N, N, +/* 0610 */ N, N, N, N, N, N, N, N, + /* STA STD STT */ +/* 0620 */ N, T_B|S_B, T_B|S_B,N, N, T_B|S_B,N, N, + /* STP SXA SCA */ +/* 0630 */ T_B|S_B,N, N, N, I_9|S_F, N, I_9|S_F,N, + /* SCHA SCHC SCHE SCHG SCDA SCDC SCDE SCDG */ +/* 0640 */ T_T, T_T, T_T, T_T, X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, +/* 0650 */ N, N, N, N, N, N, N, N, +/* 0660 */ N, N, N, N, N, N, N, N, + /* ELD EAD EDP EMP */ +/* 0670 */ T_B, T_B, T_B, T_B, N, N, N, N, + /* CPY */ +/* 0700 */ X_C|T_B,N, N, N, N, N, N, N, +/* 0710 */ N, N, N, N, N, N, N, N, +/* 0720 */ N, N, N, N, N, N, N, N, + /* PAX PAC */ +/* 0730 */ N, N, N, N, S_X, N, N, S_X|I_9, +/* 0740 */ N, N, N, N, N, N, N, N, + /* PXA PCA */ +/* 0750 */ N, N, N, N, T_N, N, T_N, N, + /* PSE NOP RDS LLS BSR LRS WRS ALS */ +/* 0760 */ T_D, T_N, X_T|T_D, T_D, X_T|T_D, T_D, X_T|T_D, T_D, + /* WEF ARS REW AXT DRS SDN */ +/* 0770 */ X_T|T_D,T_D, X_T|T_D, N, S_X, X_T|T_D,X_T|T_D, N +}; + +/* Negative opcodes */ +uint16 neg_opcode_flags[01000] = { +/* 4000 */ N, N, N, N, N, N, N, N, +/* 4010 */ N, N, N, N, N, N, N, N, + /* ESNT TRCB TRCD TRCF TRCH */ +/* 4020 */ N, I_9|T_T,X_T|T_T,N, X_T|T_T,N, X_T|T_T,X_T|T_T, + /* TEFB TEFD TEFF TEFH */ +/* 4030 */ X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,N, N, N, N, + /* RIA PIA */ +/* 4040 */ N, N, I_9, N, N, N, I_9, N, + /* IIL LFT SIL LNT RIL */ +/* 4050 */ N, I_9, N, N, I_9, I_9, I_9, I_9, + /* TCNA TCNB TCNC TNCD TNCE TNCF TNCG TNCH */ +/* 4060 */X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, +/* 4070 */ N, N, N, N, N, N, N, N, + /* TNZ TIB */ +/* 4100 */ T_T,I_D|X_P|T_T, N, N, N, N, N, N, + /* CAQ CAQ +100 */ +/* 4110 */ N, N, N, N, I_9, I_9, I_9, I_9, + /* TMI */ +/* 4120 */ T_T, N, N, N, N, N, N, N, + /* XCL */ +/* 4130 */I_9|T_N, N, N, N, N, N, N, N, + /* TNO */ +/* 4140 */ T_T, N, N, N, N, N, N, N, + /* CRQ CRQ+100 */ +/* 4150 */ N, N, N, N, I_9, I_9, I_9, I_9, +/* 4160 */ N, N, N, N, N, N, N, N, +/* 4170 */ N, N, N, N, N, N, N, N, + /* MPR */ +/* 4200 */ T_B, N, N, N, N, N, N, N, +/* 4210 */ N, N, N, N, N, N, N, N, +/* 4220 */ N, N, N, N, N, N, N, N, +/* 4230 */ N, N, N, N, N, N, N, N, + /* DFDH DFDP */ +/* 4240 */ I_94|T_B, I_94|T_B,N,N, N, N, N, N, +/* 4250 */ N, N, N, N, N, N, N, N, + /* UFM DUFM */ +/* 4260 */ T_B, I_94|T_B, N, N, N, N, N, N, +/* 4270 */ N, N, N, N, N, N, N, N, + /* UFA DUFA UFS DUFS UAM DUAM USM DUSM */ +/* 4300 */ T_B, I_94|T_B, T_B, I_94|T_B, I_9|T_B, I_94|T_B, I_9|T_B, I_94|T_B, +/* 4310 */ N, N, N, N, N, N, N, N, + /* ANA */ +/* 4320 */ T_B, N, N, N, N, N, N, N, +/* 4330 */ N, N, N, N, N, N, N, N, + /* LAS */ +/* 4340 */ I_9|T_B,N, N, N, N, N, N, N, +/* 4350 */ N, N, N, N, N, N, N, N, +/* 4360 */ N, N, N, N, N, N, N, N, +/* 4370 */ N, N, N, N, N, N, N, N, + /* SBM */ +/* 4400 */ T_B, N, N, N, N, N, N, N, +/* 4410 */ N, N, N, N, N, N, N, N, +/* 4420 */ N, N, N, N, N, N, N, N, +/* 4430 */ N, N, N, N, N, N, N, N, +/* 4440 */ N, N, N, N, N, N, N, N, +/* 4450 */ N, N, N, N, N, N, N, N, +/* 4460 */ N, N, N, N, N, N, N, N, +/* 4470 */ N, N, N, N, N, N, N, N, + /* CAL ORA */ +/* 4500 */ T_B, T_B, N, N, N, N, N, N, +/* 4510 */ N, N, N, N, N, N, N, N, + /* NZT */ +/* 4520 */ I_9|T_B, N, N, N, N, N, N, N, + /* LXD LDC */ +/* 4530 */ N, N, N, N, T_F|S_X,I_9|T_F|S_X, N, N, + /* RSCB RSCD RSCF RSCH STCB STCD STCF STCH */ +/* 4540 */ X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, +/* 4550 */ N, N, N, N, N, N, N, N, + /* ECQ LPI */ +/* 4560 */ N, S_B|T_B,N, N,I_D|X_P|T_B, N, N, N, +/* 4570 */ N, N, N, N, N, N, N, N, + /* STQ SRI ORS DST SPI*/ +/* 4600 */ S_B, I_D|S_B, T_B|S_B,I_94|S_B,I_D|S_B,N,N, N, +/* 4610 */ N, N, N, N, N, N, N, N, + /* SLQ STL */ +/* 4620 */ S_B|T_B,N, N, N, N, T_B|I_9|S_B,N, N, + /* SXD SCD */ +/* 4630 */ N, N, N, N, S_F, N, S_F, N, + /* SCHB SCHD SCHF SCHH SCDB SCDD SCDF SCDH */ +/* 4640 */ T_T, T_T, T_T, T_T,X_T|T_T,X_T|T_T,X_T|T_T,X_T|T_T, +/* 4650 */ N, N, N, N, N, N, N, N, +/* 4660 */ N, N, N, N, N, N, N, N, + /* ESB EUA EST */ +/* 4670 */ N, T_B, T_B, T_B|S_B,N, N, N, N, + /* CAD */ +/* 4700 */ X_C|T_B,N, N, N, N, N, N, N, +/* 4710 */ N, N, N, N, N, N, N, N, +/* 4720 */ N, N, N, N, N, N, N, N, + /* PDX PDC */ +/* 4730 */ N, N, N, N, S_X, N, N, S_X, +/* 4740 */ N, N, N, N, N, N, N, N, + /* PXD PCD */ +/* 4750 */ N, N, N, N, S_X, N, S_X, N, + /* MSE SPOPS LGL BSF LGR */ +/* 4760 */ T_D, T_D, N, T_D, X_T|T_D, T_D, N, N, + /* RUN RQL AXC TRS */ +/* 4770 */ N, N, X_T|T_D,T_D, S_X, X_T|T_D,N, N +}; + +#define CORE_B 0100000 /* Core B base address. */ + +#define do_trapmode \ + if (TM) { \ + sim_interval = sim_interval - 1; /* count down */ \ + M[0] &= ~AMASK; \ + M[0] |= (IC - 1) & memmask; \ + ihold = 1; \ + } + +#define do_transfer(new_pc) IC = (TM)? 1 :(new_pc) + +#define update_xr(t, v) \ + if ((t)) { \ + if (MTM) { \ + if ((t)&04) XR[4] = (uint16)(v); \ + if ((t)&02) XR[2] = (uint16)(v); \ + if ((t)&01) XR[1] = (uint16)(v); \ + } else { \ + XR[(t)] = (uint16)(v); \ + } \ + } + +#define get_xr(t) \ + (((t)) ? ((MTM) ? (XR[(t)&04] | XR[(t)&02] | XR[(t)&01]) : XR[(t)]) : 0) + +#define ReadMem(ind, reg) \ + /* In address nulify mode, half address */ \ + MA &= memmask; \ + if (bcore & 10) /* Relocation enabled? adjust address */ \ + MA = AMASK & (MA + relocaddr); \ + if (bcore & 4) { /* Protection enabled? check address */ \ + if (((MA & 077400) < baseaddr) || ((MA & 077400) >limitaddr)) { \ + /* Trap to A core */ \ + /* bcore to D3,4, IC=ADDR MA is going to DECR */ \ + M[032] = (((t_uint64)bcore & 3) << 31) | (((t_uint64)MA) << 18) | IC;\ + /* store 32, IC = 33 */ \ + IC = 033; \ + bcore = 0; \ + prot_pend = 0; \ + tbase = 0; \ + goto next_exe; \ + } \ + } \ + if ((ind) == 0 && bcore & 1) /* Data in B core */ \ + MA |= CORE_B; \ + if ((ind) == 1 && bcore & 2) /* Code in B core */ \ + MA |= CORE_B; \ + sim_interval = sim_interval - 1; /* count down */ \ + reg = ReadP(MA); + + +#define WriteMem() \ + /* In address nulify mode, half address */ \ + MA &= memmask; \ + if (bcore & 10) /* Relocation enabled? adjust address */ \ + MA = AMASK & (MA + relocaddr); \ + if (bcore & 4) { /* Protection enabled? check address */ \ + if (((MA & 077400) < baseaddr) || ((MA & 077400) >limitaddr)) { \ + /* Trap to A core */ \ + /* bcore to D3,4, IC=ADDR MA is going to DECR */ \ + M[032] = (((t_uint64)bcore & 3) << 31) | (((t_uint64)MA) << 18) | IC;\ + /* store 32, IC = 33 */ \ + IC = 033; \ + bcore = 0; \ + prot_pend = 0; \ + tbase = 0; \ + goto next_exe; \ + } \ + } \ + if (bcore & 1) /* Data in B core */ \ + MA |= CORE_B; \ + sim_interval = sim_interval - 1; /* count down */ \ + WriteP(MA, SR); + +t_stat +sim_instr(void) +{ + t_stat reason; + t_uint64 temp = 0LL; +#ifdef I7090 + t_uint64 ibr; +#endif + uint16 opcode; + uint8 tag; + uint16 decr; + uint16 xr; + uint16 opinfo; + int fptemp, fptemp2; + uint8 f; + uint16 tbase; + int xeccnt = 15; + int shiftcnt; + int stopnext = 0; + int first_rdwr = 0; + int instr_count = 0; /* Number of instructions to execute */ + + if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); + } + + /* Set cycle time for delays */ + switch(CPU_MODEL) { + case CPU_704: + case CPU_709: cycle_time = 120; break; /* 83,333 cycles per second */ + default: + case CPU_7090: cycle_time = 22; break; /* 454,545 cycles per second */ + case CPU_7094: cycle_time = 18; break; /* 555,555 cycles per second */ + } + + reason = 0; + hltinst = 0; + + /* Enable timer if option set */ + if (cpu_unit.flags & OPTION_TIMER) { + sim_activate(&cpu_unit, 10000); + } + interval_irq = 0; + +/* Main instruction fetch/decode loop */ + tbase = 0; + if (bcore & 010) + tbase = relocaddr; + if (bcore & 2) + tbase |= CORE_B; + + iowait = 0; + while (reason == 0) { /* loop until halted */ + + if (exe_KEYS) { + SR = KEYS; + hltinst = 1; + exe_KEYS = 0; + goto next_xec; + } + +#ifdef I7090 /* I704 did not have interrupts */ + hltloop: +#endif +/* If doing fast I/O don't sit in idle loop */ + if (iowait && (cpu_unit.flags & UNIT_FASTIO)) + sim_interval = 0; + if (iowait == 0 && stopnext) + return SCPE_STEP; + + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + if (reason == SCPE_STEP && iowait) + stopnext = 1; + else + break; /* process */ + } + } + +#if defined(CPANEL) + if (cpanel_interval > 0) { + if (cpanel_interval > 1) { + cpanel_interval--; + } else { + reason = ControlPanel_Refresh_CPU_Running(); + /* do control panel refresh and user clicks event processing */ + if (reason != SCPE_OK) + break; + } + } +#endif + + + if (iowait == 0 && sim_brk_summ && + sim_brk_test(((bcore & 2)? CORE_B:0)|IC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + +/* Check if we need to take any traps */ +#ifdef I7090 /* I704 did not have interrupts */ + if (itrap && ihold == 0 && iowait == 0 && ioflags != 0) { + t_uint64 mask = 00000001000001LL; + + MA = 012; + f = 0; + + for (shiftcnt = 1; shiftcnt < NUM_CHAN; shiftcnt++) { + /* CRC *//* Trap *//* EOF */ + /* Wait until channel stops to trigger interupts */ + if (ioflags & mask) { + f = 0; + if (mask & AMASK & ioflags) { + if (chan_stat(shiftcnt, CHS_EOF)) + f |= 4; /* We have a EOF */ + if (iotraps & (1 << shiftcnt)) { + f |= 1; /* We have a IOCT/IORT/IOST */ + iotraps &= ~(1 << shiftcnt); + } + } + if (mask & DMASK & ioflags && chan_stat(shiftcnt, CHS_ERR)) + f |= 2; /* We have device error */ + /* check if we need to perform a trap */ + if (f) { + /* HTR/HPR behave like wait if protected */ + hltinst = 0; + temp = (((t_uint64) bcore & 3) << 31) | + (((t_uint64) f) << 18) | (IC & memmask); + sim_interval = sim_interval - 1; /* count down */ + WriteP(MA, temp); + if (nmode) { + memmask <<= 1; + memmask |= 1; + nmode = 0; + } + MA++; + tbase = 0; + prot_pend = itrap = bcore = iowait = 0; + ihold = 1; + sim_interval = sim_interval - 1; /* count down */ + SR = ReadP(MA); + sim_debug(DEBUG_TRAP, &cpu_dev, + "Doing trap chan %c %o >%012llo loc %o %012llo\n", + shiftcnt + 'A' - 1, f, temp, MA, SR); + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = MA | HIST_PC | (bcore << 18); + hst[hst_p].ea = 0; + hst[hst_p].op = SR; + hst[hst_p].ac = AC; + hst[hst_p].mq = MQ; + hst[hst_p].xr1 = XR[1]; + hst[hst_p].xr2 = XR[2]; + hst[hst_p].xr4 = XR[4]; + hst[hst_p].sr = 0; + } + goto next_xec; + } + } + MA += 2; + mask <<= 1; + } + + /* Interval timer has lower priority then I/O traps */ + if (interval_irq && (ioflags & 0400000)) { + /* HTR/HPR behave like wait if protected */ + hltinst = 0; + temp = (((t_uint64) bcore & 3) << 31) | (IC & memmask) | + (relo_mode << 21); + sim_interval = sim_interval - 1; /* count down */ + MA = 6; + WriteP(MA, temp); + if (nmode) { + memmask <<= 1; + memmask |= 1; + nmode = 0; + } + MA++; + prot_pend = 0; + interval_irq = prot_pend = itrap = bcore = iowait = 0; + ihold = 1; + sim_interval = sim_interval - 1; /* count down */ + SR = ReadP(MA); + sim_debug(DEBUG_DETAIL, &cpu_dev, + "Doing timer trap >%012llo loc %o %012llo\n", temp, + MA, SR); + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = MA | HIST_PC | (bcore << 18); + hst[hst_p].ea = 0; + hst[hst_p].op = SR; + hst[hst_p].ac = AC; + hst[hst_p].mq = MQ; + hst[hst_p].xr1 = XR[1]; + hst[hst_p].xr2 = XR[2]; + hst[hst_p].xr4 = XR[4]; + hst[hst_p].sr = 0; + } + goto next_xec; + } + } + + if (hltinst) { + t_uint64 mask = 00000001000001LL; + if (CPU_MODEL == CPU_704) { + reason = STOP_HALT; + break; + } + /* Hold out until all channels have idled out */ + sim_interval = 0; + sim_process_event(); + chan_proc(); + f = chan_active(0); + for (shiftcnt = 1; shiftcnt < NUM_CHAN; shiftcnt++) { + f |= chan_active(shiftcnt); + /* CRC *//* Trap *//* EOF */ + /* Wait until channel stops to trigger interupts */ + if (ioflags & mask) { + if (mask & AMASK & ioflags) { + if (chan_stat(shiftcnt, CHS_EOF)) + f = 1; /* We have a EOF */ + if (iotraps & (1 << shiftcnt)) + f = 1; /* We have a IOCT/IORT/IOST */ + } + if (mask & DMASK & ioflags && chan_stat(shiftcnt, CHS_ERR)) + f = 1; /* We have device error */ + } + } + + /* If all channels idle and not in protected mode, real halt */ + if (f == 0 && (bcore & 4) == 0) { + reason = STOP_HALT; + break; + } + goto hltloop; + } +#else /* Handle halt on 704 */ + if (hltinst) { + reason = STOP_HALT; + break; + } +#endif + +/* Split out current instruction */ + next_exe: + if (iowait) { + /* If we are awaiting I/O complete, don't fetch. */ + sim_interval--; + SR = temp; + iowait = 0; + } else { + xeccnt = 15; + MA = IC; + ReadMem(1, SR); + temp = SR; + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].ic = MA | HIST_PC | (bcore << 18); + hst[hst_p].ea = 0; + hst[hst_p].op = SR; + hst[hst_p].ac = AC; + hst[hst_p].mq = MQ; + hst[hst_p].xr1 = XR[1]; + hst[hst_p].xr2 = XR[2]; + hst[hst_p].xr4 = XR[4]; + hst[hst_p].sr = 0; + } + IC = memmask & (IC + 1); + } + if (ihold != 0) + ihold--; + else if (relo_pend || prot_pend) { + bcore = (bcore & 3) | (relo_pend << 3) | (prot_pend << 2); + relo_pend = 0; + prot_pend = 0; + } + next_xec: + opcode = (uint16)(SR >> 24); + IR = opcode; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].op = SR; + } + MA = (uint16)(SR & AMASK); + tag = (uint8)(SR >> 15) & 07; + decr = (uint16)((SR >> 18) & AMASK); + /* Set flags to D to start with */ + xr = get_xr(tag); + iowait = 0; /* Kill iowait */ + sim_interval--; /* one cycle for execute */ + + switch (opcode & 07000) { + case (OP_TXI << 9): /* Transfer and inc XR[T] += D, IC <- Y */ + do_trapmode; + + decr &= memmask; + xr += decr; + xr &= memmask; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = decr; + hst[hst_p].sr = xr; + } + + /* Save register */ + update_xr(tag, xr); + do_transfer(MA); + break; + + case (OP_TXH << 9): /* Transfer on High Index XR[T] > D, IC <- Y */ + do_trapmode; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = decr; + hst[hst_p].sr = xr; + } + xr &= memmask; + decr &= memmask; + if (tag && xr > decr) + do_transfer(MA); + break; + case (OP_TNX << 9): /* Transfer No index XR[T] */ + do_trapmode; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = decr; + hst[hst_p].sr = xr; + } + xr &= memmask; + decr &= memmask; + if (tag && xr > decr) { + xr = AMASK & (xr - decr); + update_xr(tag, xr); + } else { + do_transfer(MA); + } + break; + case (OP_TXL << 9): /* Transfer on Low Index XR[T] <= D, IC <- Y */ + do_trapmode; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = decr; + hst[hst_p].sr = xr; + } + xr &= memmask; + decr &= memmask; + if (tag == 0 || xr <= decr) + do_transfer(MA); + break; + case (OP_TIX << 9): /* Transfer and Index XR */ + do_trapmode; + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = decr; + hst[hst_p].sr = xr; + } + xr &= memmask; + decr &= memmask; + if (tag && xr > decr) { + xr = AMASK & (xr - decr); + update_xr(tag, xr); + do_transfer(MA); + } + break; + case (OP_STR << 9): + M[tbase] &= ~AMASK; + M[tbase] |= IC & memmask; + if (hst_lnt) /* history enabled? */ + hst[hst_p].ea = tbase; + IC = 2; + break; + case (4 << 9): /* Neg opcodes */ + opinfo = neg_opcode_flags[opcode & 0777]; + goto proc_op; + case 0: /* Pos opcodes */ + opinfo = pos_opcode_flags[opcode]; + proc_op: + /* If proc does not support this opcode, just skip it */ + if (opinfo & I_9 && CPU_MODEL == CPU_704) + break; + if (opinfo & I_94 && CPU_MODEL != CPU_7094) + break; + if (opinfo & (X_P|X_T) && (bcore & 4)) { + /* Trap to 0 and drop out of B core */ +prottrap: + MA = 032; + if (nmode) { + memmask <<= 1; + memmask |= 1; + } + temp = ((t_uint64) (bcore & 3) << 31) | IC; + tbase = 0; + prot_pend = nmode = bcore = STM = CTM = 0; + WriteP(MA, temp); + IC = MA + 1; + break; + } + /* Check for traping conditions */ + /* Check for protection mode */ + if (opinfo & X_T && STM) { + /* Trap to 40001 */ + MA = MEMSIZE >> 1; + if (nmode) { + memmask <<= 1; + memmask |= 1; + } + temp = ((t_uint64) (bcore & 3) << 31) | IC; + tbase = 0; + prot_pend = nmode = bcore = STM = CTM = 0; + WriteP(MA, temp); + IC = MA + 1; + break; + } + /* Check for protection mode */ + if (opinfo & X_C && CTM) { + /* Trap to 40002 */ + MA = MEMSIZE >> 1; + if (nmode) { + memmask <<= 1; + memmask |= 1; + } + temp = ((t_uint64) (bcore & 3) << 31) | IC; + tbase = 0; + prot_pend = nmode = bcore = STM = CTM = 0; + WriteP(MA, temp); + IC = MA + 2; + break; + } + /* Merge in index register if needed */ + if (opinfo & (T_B | T_D | T_T | S_B)) + MA = memmask & (MA - xr); + decr &= 077; /* Set flags to D to start with */ + /* If indirect flag and indirect instruction do memory access */ + if ((CPU_MODEL != CPU_704) && ((decr & 060) == 060) && + (opinfo & (T_B | T_T | S_B))) { + ReadMem(1, SR); + tag = (uint8)((SR >> 15) & 07); + xr = get_xr(tag); + MA = (uint16)(memmask & (SR - xr)); + } + MA &= memmask; + if (opinfo & (T_B | T_F | S_F)) { + ReadMem(opcode == OP_XEC, SR); + } + if (hst_lnt) { /* history enabled? */ + hst[hst_p].ea = MA; + hst[hst_p].sr = SR; + } + switch (opcode) { + case 0760: /* PSE */ +/* Positive 0760 opcodes */ + switch (MA) { +#ifdef I7090 /* Not on 704 */ + case OP_RDCA: /* Reset data channel */ + case OP_RDCB: + case OP_RDCC: + case OP_RDCD: + case OP_RDCE: + case OP_RDCF: + case OP_RDCG: + case OP_RDCH: + if (CPU_MODEL == CPU_704) + break; + if ((bcore & 4) || STM) + goto seltrap; + f = (MA >> 9) & 017; + chan_rst(f, 1); + break; + case OP_RICA: /* Reset channel */ + case OP_RICB: + case OP_RICC: + case OP_RICD: + case OP_RICE: + case OP_RICF: + case OP_RICG: + case OP_RICH: + if (CPU_MODEL == CPU_704) + break; + if ((bcore & 4) || STM) + goto seltrap; + chan_rst((MA >> 9) & 017, 0); + break; + /* Not great coding, but keeps multiple copys out of code */ +#endif + seltrap: + /* Trap to 40000 or 0 depending on mode */ + if (bcore & 4) + MA = 032; + else + MA = MEMSIZE >> 1; + if (nmode) { + memmask <<= 1; + memmask |= 1; + } + temp = ((t_uint64) (bcore & 3) << 31) | IC; + tbase = 0; + prot_pend = nmode = bcore = STM = CTM = 0; + WriteP(MA, temp); + IC = MA + 1; + break; + + case OP_BTTA: /* Skip BOT */ + case OP_BTTB: /* Skip BOT */ + case OP_BTTC: /* Skip BOT */ + case OP_BTTD: /* Skip BOT */ + case OP_BTTE: /* Skip BOT */ + case OP_BTTF: /* Skip BOT */ + case OP_BTTG: /* Skip BOT */ + case OP_BTTH: /* Skip BOT */ + if (CPU_MODEL == CPU_704) + break; + if ((bcore & 4) || STM) + goto seltrap; + if (chan_stat((MA >> 9) & 017, CHS_BOT) == 0) + IC++; + break; + case OP_SLF: + SL = 0; + break; + case OP_SLN1: + case OP_SLN2: + case OP_SLN3: + case OP_SLN4: +#ifdef EXTRA_SL + case OP_SLN5: + case OP_SLN6: + case OP_SLN7: + case OP_SLN8: +#endif + SL |= 1 << (MA - OP_SLN1); + break; + case OP_SWT1: + case OP_SWT2: + case OP_SWT3: + case OP_SWT4: + case OP_SWT5: + case OP_SWT6: + if (SW & (1 << (MA - OP_SWT1))) + IC++; + break; + case OP_LBT: + if (AC & 1) + IC++; + break; + case OP_CLM: + AC &= AMSIGN; + break; + case OP_CHS: + AC ^= AMSIGN; + break; + case OP_SSP: + AC &= AMMASK; + break; + case OP_COM: + AC ^= AMMASK; + break; + case OP_ENK: + MQ = KEYS; + break; + case OP_IOT: /* Skip & clear ioready */ + if (iocheck == 0) + IC++; + iocheck = 0; + break; + case OP_ETM: + if (bcore & 4) + goto prottrap; + TM = 1; + break; + case OP_RND: + if (MQ & ONEBIT) { + SR = 1; + goto iadd; + } + break; + case OP_FRN: + /* Extract AC char */ + temp = 0; + if (MQ & FPNBIT) { + /* Save everything but characterist, +1 to fraction */ + SR = (AC & (FPMMASK | AMSIGN | AQSIGN | APSIGN)) + 1; + /* If overflow, normalize */ + if (SR & FPOBIT) { + SR >>= 1; /* Move right one bit */ + if ((AC & (AQSIGN | APSIGN | FPCMASK)) == + FPCMASK) { + temp = FPOVERR | FPACERR; + } + AC += FPOBIT; /* Fix characteristic */ + /* Fix the sign */ + AC &= AMMASK; + AC |= (SR & AQSIGN) << 1; /* Fix sign */ + } + /* Restore fixed ac */ + AC &= ~FPMMASK; + AC |= SR & FPMMASK; + if (temp != 0) + goto dofptrap; + } + break; + case OP_DCT: + if (dcheck == 0) + IC++; + dcheck = 0; + break; +#ifdef I7090 /* Not on 704 */ + case OP_RCT: + if (bcore & 4) + goto prottrap; + if (CPU_MODEL != CPU_704) { + sim_debug(DEBUG_TRAP, &cpu_dev, "RCT %012llo\n", ioflags); + if ((bcore & 4) || STM) + goto seltrap; + itrap = 1; + if (CPU_MODEL == CPU_709) + ihold = 1; + else + ihold = 2; + } + break; + case OP_LMTM: + if (CPU_MODEL != CPU_704) + MTM = 0; + break; +#endif + default: + if ((bcore & 4) || STM) + goto seltrap; + f = MA >> 9; + if (f < 11) { + MA &= 0777; + if (MA >= 0341 && MA <= 0372) { + MA -= 0341; + if (MA < PUNCH_M) { + dev_pulse[f] |= 1 << MA; + } else { + MA -= 13; + if (MA == 2) { + if (dev_pulse[f] & PRINT_I) + IC++; + dev_pulse[f] &= ~PRINT_I; + } else { + dev_pulse[f] |= 1 << MA; + } + } + } + } + break; + } + break; + case 04760: /* MSE */ +/* Negative 04760 opcodes */ + switch (MA) { + case OP_ETTA: /* Transfer on EOT */ + case OP_ETTB: /* Transfer on EOT */ + case OP_ETTC: /* Transfer on EOT */ + case OP_ETTD: /* Transfer on EOT */ + case OP_ETTE: /* Transfer on EOT */ + case OP_ETTF: /* Transfer on EOT */ + case OP_ETTG: /* Transfer on EOT */ + case OP_ETTH: /* Transfer on EOT */ + if ((bcore & 4) || STM) + goto seltrap; + if (chan_stat((MA >> 9) & 017, CHS_EOT) == 0) + IC++; + break; + case OP_PBT: + if (AC & APSIGN) + IC++; + break; + case OP_EFTM: + if (CPU_MODEL != CPU_704) + FTM = 1; + break; + case OP_SSM: + AC |= AMSIGN; + break; +#ifdef I7090 /* Not on 704 */ + case OP_LFTM: + if (bcore & 4) + goto prottrap; + if (CPU_MODEL != CPU_704) + acoflag = mqoflag = FTM = 0; + break; + case OP_ESTM: + if (bcore & 4) + goto prottrap; + if (CPU_MODEL != CPU_704) + STM = 1; + break; + case OP_ECTM: + if (bcore & 4) + goto prottrap; + if (CPU_MODEL != CPU_704) + CTM = 1; + break; + case OP_EMTM: + if (CPU_MODEL != CPU_704) + MTM = 1; + break; +#endif + case OP_LTM: + if (bcore & 4) + goto prottrap; + TM = 0; + break; + case OP_LSNM: + if (nmode) { + memmask <<= 1; + memmask |= 1; + } + nmode = 0; + break; + case OP_ETT: + if ((bcore & 4) || STM) + goto seltrap; + if (chan_stat(0, CHS_EOT) == 0) + IC++; + break; + case OP_RTT: + if ((bcore & 4) || STM) + goto seltrap; + /* Check ERR on channel 0 */ + if (chan_stat(0, CHS_ERR) == 0) + IC++; + break; + case OP_SLT1: + case OP_SLT2: + case OP_SLT3: + case OP_SLT4: +#ifdef EXTRA_SL + case OP_SLT5: + case OP_SLT6: + case OP_SLT7: + case OP_SLT8: +#endif + f = 1 << (MA - OP_SLN1); + if (SL & f) + IC++; + SL &= ~f; + break; +#ifdef EXTRA_SW + case OP_SWT7: + case OP_SWT8: + case OP_SWT9: + case OP_SWT10: + case OP_SWT11: + case OP_SWT12: + if (SW & (1 << (6 + MA - OP_SWT7))) + IC++; + break; +#endif + default: + break; + } + break; +/* Transfer opcodes */ + case OP_HTR: + /* Stop at HTR instruction if trapped */ + IC--; + /* Fall through */ + case OP_HPR: + halt: + hltinst = 1; + if (opcode == OP_HTR) + IC = MA; + break; + case OP_XEC: + opcode = (uint16)(SR >> 24); + if (opcode != OP_XEC) { + xeccnt = 15; + goto next_xec; + } + if (xeccnt-- != 0) { + iowait = 1; + goto next_xec; + } + reason = STOP_XECLIM; + break; + case OP_NOP: + break; + case OP_TTR: + IC = MA; + break; + case OP_TLQ: + do_trapmode; + /* Is AC - and MQ + */ + if ((MQ & MSIGN) == 0 && (AC & AMSIGN) != 0) + break; + /* Same sign, compare magintudes */ + if (((MQ & MSIGN) == 0 && (AC & AMSIGN) == 0)) { + SR = (MQ & PMASK) - (AC & AQMASK); + if ((SR & AMSIGN) == 0) + break; + } else if (((MQ & MSIGN) != 0 && (AC & AMSIGN) != 0)) { + SR = (AC & AQMASK) - (MQ & PMASK); + if ((SR & AMSIGN) == 0) + break; + } + /* Nope, MQ bigger, so take branch */ + do_transfer(MA); + break; + case OP_TRA: + do_trapmode; + do_transfer(MA); + break; + case OP_TSX: + do_trapmode; + /* Exchange SR and IC */ + SR = AMASK & (-(IC - 1)); + do_transfer(MA); + break; + case OP_TZE: + f = (AC & AMMASK) == 0; + branch: + do_trapmode; + if (f) { + do_transfer(MA); + } + break; + case OP_TOV: + f = acoflag; + acoflag = 0; + goto branch; + case OP_TQP: + f = ((MQ & MSIGN) == 0); + goto branch; + case OP_TQO: + if ((CPU_MODEL == CPU_704) || FTM == 0) { + f = mqoflag; + mqoflag = 0; + goto branch; + } + break; + case OP_TPL: + f = ((AC & AMSIGN) == 0); + goto branch; + case OP_TNZ: + f = ((AC & AMMASK) != 0); + goto branch; + case OP_TMI: + f = ((AC & AMSIGN) != 0); + goto branch; + case OP_TNO: + f = !acoflag; + acoflag = 0; + goto branch; + case OP_NZT: + if ((SR & PMASK) != 0) + IC++; + break; + case OP_ZET: + if ((SR & PMASK) == 0) + IC++; + break; + case OP_ESNT: + IC = MA; + if (!nmode) + memmask >>= 1; + nmode = 1; + break; +#ifdef I7090 /* Not on 704 */ +/* Indicator opcodes */ + case OP_IIA: + ID ^= AC & AQMASK; + break; + case OP_IIS: + ID ^= SR; + break; + case OP_IIR: + ID ^= SR & RMASK; + break; + case OP_IIL: + ID ^= (SR & RMASK) << 18; + break; + case OP_OAI: + ID |= AC & AQMASK; + break; + case OP_OSI: + ID |= SR; + break; + case OP_SIR: + ID |= (SR & RMASK); + break; + case OP_SIL: + ID |= (SR & RMASK) << 18; + break; + case OP_RIA: + ID &= ~AC; + break; + case OP_RIS: + ID &= ~SR; + break; + case OP_RIR: + ID &= ~(SR & RMASK); + break; + case OP_RIL: + ID &= ~((SR & RMASK) << 18); + break; + case OP_PIA: + AC = ID & AQMASK; + break; + case OP_PAI: + ID = AC & AQMASK; + break; + case OP_LDI: + ID = SR; + break; + case OP_STI: + SR = ID; + break; + case OP_ONT: + if ((ID & SR) == SR) + IC++; + break; + case OP_OFT: + if ((ID & SR) == 0) + IC++; + break; + case OP_RFT: + if (0 == (SR & ID & RMASK)) + IC++; + break; + case OP_LFT: + if (0 == (((SR & RMASK) << 18) & ID)) + IC++; + break; + case OP_RNT: + if ((SR & RMASK) == (SR & ID & RMASK)) + IC++; + break; + case OP_LNT: + if ((SR & RMASK) == (SR & (ID >> 18) & RMASK)) + IC++; + break; + case OP_TIO: + do_trapmode; + if ((ID & AC) == (AC & AQMASK)) + do_transfer(MA); + break; + case OP_TIF: + do_trapmode; + if ((ID & AC) == 0) + do_transfer(MA); + break; +#endif +/* General index and load store opcodes */ + case OP_XCA: + SR = (AC & (PMASK)); + if (AC & AMSIGN) + SR |= MSIGN; + AC = MQ; + if (AC & APSIGN) + AC ^= AMSIGN | APSIGN; + MQ = SR; + break; + case OP_XCL: + SR = AC & AQMASK; + AC = MQ & AQMASK; + MQ = SR; + break; + case OP_AXC: + SR = (t_uint64)(-(t_int64)SR); + break; + case OP_AXT: + break; + case OP_LXA: + SR &= memmask; + break; + case OP_LAC: + SR = (t_uint64)(-(t_int64)SR); + SR &= memmask; + break; + case OP_LDQ: + MQ = SR; + break; + case OP_LXD: + SR >>= 18; + SR &= memmask; + break; + case OP_LDC: + SR >>= 18; + SR = (t_uint64)(-(t_int64)SR); + SR &= memmask; + break; + case OP_CLA: + AC = ((SR & MSIGN) << 2) | (SR & PMASK); + break; + case OP_CLS: + AC = (((SR & MSIGN) ^ MSIGN) << 2) | (SR & PMASK); + break; + case OP_CAL: + AC = SR; + break; + case OP_STQ: + SR = MQ; + break; + case OP_ECA: + temp = AC; + AC = SR; + SR = temp; + break; + case OP_ECQ: + temp = MQ; + MQ = SR; + SR = temp; + break; + case OP_SLQ: + SR = (SR & RMASK) | (MQ & LMASK); + break; + case OP_STL: + SR &= ~AMASK; + SR |= IC & memmask; + break; + case OP_STZ: + SR = 0; + break; + case OP_STO: + SR = AC & PMASK; + if (AC & AMSIGN) + SR |= MSIGN; + break; + case OP_SLW: + SR = AC & AQMASK; + break; + case OP_STA: + SR &= ~AMASK; + SR |= AC & AMASK; + break; + case OP_STD: + SR &= ~DMASK; + SR |= AC & DMASK; + break; + case OP_STT: + SR &= ~TMASK; + SR |= AC & TMASK; + break; + case OP_STP: + SR &= ~PREMASK; + SR |= AC & PREMASK; + break; + case OP_SXA: + SR &= ~AMASK; + SR |= memmask & xr; + update_xr(tag, xr); + break; + case OP_SCA: + SR &= ~AMASK; + SR |= memmask & (-xr); + update_xr(tag, xr); + break; + case OP_SCD: + SR &= ~DMASK; + temp = (-xr) & memmask; + temp &= AMASK; + temp <<= 18; + SR |= temp; + update_xr(tag, xr); + break; + case OP_SXD: + SR &= ~DMASK; + temp = xr & memmask; + temp &= AMASK; + temp <<= 18; + SR |= temp; + update_xr(tag, xr); + break; + case OP_PDX: + SR = memmask & (AC >> 18); + break; + case OP_PDC: + SR = (t_uint64)(memmask & (-(t_int64)(AC >> 18))); + break; + case OP_PXD: + SR = AC = xr & memmask; + AC <<= 18; + break; + case OP_PCD: + AC = (-xr) & memmask; + AC <<= 18; + SR = xr & memmask; + break; + case OP_PAX: + SR = memmask & AC; + break; + case OP_PAC: + SR = (t_uint64)(memmask & (-(t_int64)AC)); + break; + case OP_PXA: + AC = memmask & xr; + SR = xr & memmask; + break; + case OP_PCA: + AC = AMASK & (-xr); + SR = xr & AMASK; + break; +/* Integer math */ + case OP_CAS: + if (AC & AMSIGN) { + if (SR & MSIGN) { + if ((AC & AMMASK) == (SR & PMASK)) + IC++; + else if (((SR & PMASK) - (AC & AMMASK)) & AMSIGN) + IC += 2; + } else + IC += 2; + } else { + if ((SR & MSIGN) == 0) { + if ((AC & AMMASK) == (SR & PMASK)) + IC++; + else if (((AC & AMMASK) - (SR & PMASK)) & AMSIGN) + IC += 2; + } + } + break; + case OP_LAS: + SR = (AC & AMMASK) - SR; + if (SR == 0) + IC++; + if ((SR & AMSIGN)) + IC += 2; + break; + case OP_ACL: + ladd: + SR += (AC & AQMASK); + if (SR & AQSIGN) + SR++; + AC = (AC & (AMSIGN | AQSIGN)) | (SR & AQMASK); + break; + case OP_SBM: + SR |= MSIGN; + goto iadd; + case OP_ADM: + SR &= PMASK; + goto iadd; + case OP_SUB: + SR ^= MSIGN; + /* Fall through */ + + case OP_ADD: + iadd: + f = 0; + /* Make AC Positive */ + if (AC & AMSIGN) { + f = 2; + AC &= AMMASK; + } + if (AC & APSIGN) + f |= 8; + /* Check signes of SR & AC */ + if (((SR & MSIGN) && ((f & 2) == 0)) || + (((SR & MSIGN) == 0) && ((f & 2) != 0))) { + AC ^= AMMASK; /* One's compliment */ + f |= 1; + } + AC = AC + (SR & PMASK); + /* Check carry from Q */ + if (f & 1) { /* Check if signs were not same */ + if (AC & AMSIGN) { + f ^= 2; + AC++; + if (((AC & APSIGN) != 0) != ((f & 8) != 0)) + acoflag = 1; + } else { + AC ^= AMMASK; /* One's compliment */ + } + } else { + if (((AC & APSIGN) != 0) != ((f & 8) != 0)) + acoflag = 1; + } + /* Restore sign to AC */ + AC &= AMMASK; + if (f & 2) + AC |= AMSIGN; + break; + case OP_MPY: + case OP_MPR: + decr = 043; + /* Fall through */ + + case OP_VLM + 1: + case OP_VLM: + shiftcnt = decr; + if (shiftcnt == 0) + break; + f = 0; + /* Save sign */ + if (MQ & MSIGN) + f |= 1; + if (SR & MSIGN) + f |= 2; + SR &= PMASK; + MQ &= PMASK; + AC = 0; /* Clear AC */ + if (SR == 0) { + MQ = 0; + } else { + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= ONEBIT; + AC >>= 1; + } + } + if (opcode == OP_MPR && MQ & ONEBIT) + AC++; + if (f & 2) + f ^= 1; + if (f & 1) { + MQ |= MSIGN; + AC |= AMSIGN; + } + break; + case OP_DVH: + case OP_DVP: + decr = 043; + /* Fall through */ + + case OP_VDH + 2: + case OP_VDH: + case OP_VDP + 2: + case OP_VDP: + shiftcnt = decr; + if (shiftcnt == 0) + break; + /* Save sign */ + if (SR & MSIGN) { + SR &= PMASK; + f = 1; + } else + f = 0; + + if (AC & AMSIGN) + f |= 2; + + /* Check if SR less then AC */ + if (((SR - (AC & AMMASK)) & AMSIGN) || + (SR == (AC & AMMASK))) { + dcheck = 1; + if (CPU_MODEL < CPU_7090) { + MQ &= PMASK; + if (f == 2 || f == 1) + MQ |= MSIGN; + } + if (opcode == OP_DVH || opcode == OP_VDH + || opcode == (OP_VDH + 2)) { + goto halt; + } + break; + } + /* Clear signs */ + MQ &= PMASK; + AC &= AMMASK; + sim_interval = sim_interval - shiftcnt; + /* Do divide operation */ + do { + AC <<= 1; + AC &= AMMASK; + MQ <<= 1; + if (MQ & MSIGN) { + MQ ^= MSIGN; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + switch (f) { + case 0: + break; + case 3: + AC |= AMSIGN; + break; + case 2: + AC |= AMSIGN; + /* FALL THRU */ + case 1: + MQ |= MSIGN; + break; + } + break; +/* Floating point */ + case OP_USM: + case OP_FSM: + SR |= MSIGN; + goto fpadd; + case OP_FSB: + case OP_UFS: + SR ^= MSIGN; /* Reverse sign */ + goto fpadd; + case OP_FAM: + case OP_UAM: + SR &= PMASK; /* Clear SR sign */ + case OP_FAD: + case OP_UFA: + fpadd: + temp = 0; /* Steal temp for errors */ + MQ = 0; + f = 0; + /* Extract AC char */ + shiftcnt = (int)(AC >> 27) & 01777; /* Include P&Q */ + /* Diff SR char */ + shiftcnt -= (int)((SR >> 27) & 0377); + if (shiftcnt > 0) { /* AC Bigger */ + /* Exchange AC & SR */ + AC ^= SR; + SR ^= AC; + AC ^= SR; + /* Fix up signs */ + if (SR & AMSIGN) + SR |= MSIGN; + AC &= AMMASK; + if (AC & APSIGN) + AC ^= (AMSIGN | APSIGN); + } else /* SR Bigger then AC, AC Smaller */ + shiftcnt = -shiftcnt; /* Change sign */ + fptemp = (int)((SR >> 27) & 0377); /* Get exponent */ + /* Save AC & SR signs */ + if (AC & AMSIGN) + f |= 1; + if (SR & MSIGN) + f |= 2; + /* Clear sign */ + SR &= PMASK; + AC &= FPMMASK; /* Clear char and sign */ + shiftcnt &= 0377; + if (shiftcnt >= 0 && shiftcnt < 077) { + sim_interval--; + while (shiftcnt > 0) { + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + shiftcnt--; + } + } else + AC = 0; + sim_interval--; + + /* Do add */ + if (f == 2 || f == 1) { + AC -= (SR & FPMMASK); + /* If AC < 0 then SR was larger */ + if (AC & AMSIGN) { + AC = ~AC; + if ((MQ & FPMMASK) != 0) { + MQ ^= FPMMASK; + MQ++; + } else + AC++; + } else + f ^= 2; /* Change sign of AC */ + } else + AC += SR & FPMMASK; + + /* Check for overflow */ + if (AC & FPOBIT) { + if (AC & 1) + MQ |= FPOBIT; + AC >>= 1; + MQ >>= 1; + /* OV check */ + if (fptemp == 0377) + temp |= FPACERR | FPOVERR; + fptemp++; + } + /* Are we normalizing */ + if (smode == 0 && + (opcode == OP_FAD || opcode == OP_FSB || + opcode == OP_FAM || opcode == OP_FSM)) { + sim_interval--; + while ((AC & FPNBIT) == 0 && + ((AC & FPMMASK) != 0 || (MQ & FPMMASK) != 0)) { + /* 704 does not check MQ when normalizing */ + if (CPU_MODEL == CPU_704 && (AC & FPMMASK) == 0) + break; + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) { + AC |= 1; + MQ &= ~FPOBIT; + } + if (fptemp == 0 && (temp & FPOVERR) == 0) + temp |= FPACERR; + fptemp--; /* UF Check */ + } + if (AC == 0 && MQ == 0) { + fptemp = 0; + f |= f << 1; + } + } + + /* Handle signifigance mode */ + if (smode && MQ & FPNBIT && + (opcode == OP_FAD || opcode == OP_FSB || + opcode == OP_FAM || opcode == OP_FSM)) { + sim_interval--; /* Extra cycle */ + /* If overflow, normalize */ + AC++; + if (AC & FPOBIT) { + AC >>= 1; /* Move right one bit */ + /* OV check */ + if (fptemp == 0377) + temp |= FPACERR | FPOVERR; + fptemp++; + } + } + + /* Put pieces back together */ + AC &= FPMMASK; + MQ &= FPMMASK; + AC |= ((t_uint64) (fptemp & 01777)) << 27; + if (AC != 0) { + if (fptemp < 27 && (temp & FPOVERR) == 0) + temp |= FPMQERR; + fptemp -= 27; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; + } + if (f & 2) { + AC |= AMSIGN; + MQ |= MSIGN; + } + if (temp == 0) + break; + dofptrap: + if (CPU_MODEL != CPU_704 && FTM) { + sim_interval = sim_interval - 1; /* count down */ + M[0] &= ~(AMASK | DMASK); + M[0] |= temp | (IC & memmask); + IC = 010; + } else { + if (temp & FPMQERR) + mqoflag = 1; + if (temp & FPACERR) + acoflag = 1; + } + break; + case OP_UFM: + case OP_FMP: + AC = 0; + temp = 0; + /* Quick out for times 0 */ + if (SR == 0) { + MQ &= MSIGN; + if (MQ & MSIGN) + AC |= AMSIGN; + break; + } + /* Result sign */ + if ((MQ & MSIGN) != (SR & MSIGN)) + f = 1; + else + f = 0; + /* 7090 checks MQ for zero before multipling */ + if (CPU_MODEL == CPU_7090 && (MQ & PMASK) == 0) { + if (f) + AC |= AMSIGN; + break; + } + + /* Handle signifigance mode */ + if (smode) { + /* Find larger operand and move to MQ */ + if ((MQ & FPMMASK) < (SR & FPMMASK)) { + MQ ^= SR; + SR ^= MQ; + MQ ^= SR; + } + /* Now normalize number in MQ */ + fptemp = (int)(MQ >> 27) & 0377; + MQ &= FPMMASK; + while ((MQ & FPNBIT) == 0 && MQ != 0) { + fptemp--; + MQ <<= 1; + } + /* If zero time zero, fix exponent */ + if (MQ == 0 && (SR & FPMMASK) == 0) { + fptemp -= 27; + MQ = FPNBIT; + } + } else + /* Extract characteristic */ + fptemp = (int)(MQ >> 27) & 0377; + fptemp += (int)(SR >> 27) & 0377; + fptemp -= 128; + MQ &= FPMMASK; + SR &= FPMMASK; + /* Do multiply */ + shiftcnt = 27; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + AC &= FPMMASK; + } + + /* Normalize the result */ + if (opcode == OP_FMP) { + if ((AC & FPNBIT) == 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) + AC |= 1; + MQ &= FPMMASK; + fptemp--; + if (smode && (AC & FPNBIT) == 0 && + (AC & (FPNBIT >> 1)) == 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) + AC |= 1; + MQ &= FPMMASK; + fptemp--; + } + } + if (smode && MQ & FPNBIT) { + sim_interval--; /* Extra cycle */ + /* If overflow, normalize */ + AC++; + if (AC & FPOBIT) { + AC >>= 1; /* Move right one bit */ + fptemp++; + } + } + if (AC == 0) + fptemp = 0; + } + + if (AC != 0 || opcode == OP_UFM || smode) { + if (fptemp < 0) + temp |= FPACERR; + else if (fptemp > 0377) + temp |= FPOVERR | FPACERR; + AC |= ((t_uint64) (fptemp & 01777)) << 27; + fptemp -= 27; + if (fptemp < 0) + temp |= FPMQERR; + else if (fptemp > 0377) + temp |= FPOVERR | FPMQERR; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; /* UF Check */ + } + if (f & 1) { + MQ |= MSIGN; + AC |= AMSIGN; + } + if (temp != 0) + goto dofptrap; + break; + case OP_FDH: + case OP_FDP: + /* Sign of SR => MQ */ + if ((SR & MSIGN) != ((AC >> 2) & MSIGN)) + f = 1; + else + f = 0; + if (AC & AMSIGN) + f |= 2; + if (CPU_MODEL != CPU_704) + MQ = 0; + shiftcnt = 27; + + /* Handle signifigance mode */ + if (smode) { + /* Divide check if 0 divisor */ + if ((SR & FPMMASK) == 0) { + dcheck = 1; + if (f & 1) + MQ |= MSIGN; + if (opcode == OP_FDH) + goto halt; + } + fptemp2 = (int)(AC >> 27) & 0377; + AC &= FPMMASK; + /* If dividend 0, adjust exponent */ + if (AC == 0) { + while ((SR & FPNBIT) == 0) { + SR <<= 1; + fptemp2--; + } + if (fptemp2 < 0) + temp |= FPSPERR | FPMQERR; + AC = ((t_uint64) (fptemp2 & 01777)) << 27; + if (FTM && CPU_MODEL != CPU_704 && fptemp2 < 27) + temp |= FPSPERR | FPACERR; + fptemp2 -= 27; + MQ = ((t_uint64) (fptemp2 & 0377)) << 27; + /* Fix signs */ + if (f & 1) + MQ |= MSIGN; + if (f & 2) /* Sign does not change */ + AC |= AMSIGN; + break; + } + fptemp = (int)(SR >> 27) & 0377; + SR &= FPMMASK; + /* Normalize dividend if larger fraction */ + if (AC > (SR & FPMMASK)) { + while ((AC & FPOBIT) == 0) { + fptemp2--; + AC <<= 1; + } + /* Normalize SR, denomalize AC */ + /* We checked before for SR==0 */ + while ((SR & FPOBIT) == 0) { + SR <<= 1; + fptemp2--; + AC >>= 1; + fptemp++; + } + } else if (AC < (SR & FPMMASK)) { + /* Normalize SR, denomalize AC */ + /* We checked before for SR==0 */ + while ((SR & FPOBIT) == 0) { + SR <<= 1; + fptemp--; + AC >>= 1; + fptemp2++; + } + } + if ((SR & (FPOBIT >> 1)) == 0) + shiftcnt--; + goto fpdivide; + } + + /* Begin common FDP/FDH code */ + temp = (AC & FPMMASK) - ((SR & FPMMASK) << 1); + if ((temp & AMSIGN) == 0 || (SR & FPMMASK) == 0) { + dcheck = 1; + if (f & 1) + MQ |= MSIGN; + if (opcode == OP_FDH) + goto halt; + break; + } + temp = 0; + /* Check for divide by 0 */ + if ((AC & FPMMASK) == 0) { + AC = 0; + if (CPU_MODEL != CPU_704) + f &= 1; + } else { + /* Split appart fraction and charateristics */ + fptemp2 = (int)(AC >> 27) & 0377; + fptemp = (int)(SR >> 27) & 0377; + AC &= FPMMASK; + SR &= FPMMASK; + fpdivide: + /* Precheck SR less then AC */ + if (((AC - SR) & AMSIGN) == 0) { + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + fptemp2++; + } + /* Do actual divide */ + do { + AC <<= 1; + MQ <<= 1; + if (MQ & FPOBIT) { + MQ &= ~FPOBIT; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + + /* Compute new characteristic */ + AC &= FPMMASK; + fptemp = (fptemp2 - fptemp) + 128; /* UF check */ + if (fptemp > 0377) + temp |= FPSPERR | FPOVERR | FPMQERR; + else if (fptemp < 0) + temp |= FPSPERR | FPMQERR; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; + if (FTM && CPU_MODEL != CPU_704 && fptemp2 < 27) + temp |= FPSPERR | FPACERR; + fptemp2 -= 27; + AC |= ((t_uint64) (fptemp2 & 01777)) << 27; /* UF check */ + } + /* Fix signs on results */ + if (f & 1) + MQ |= MSIGN; + if (f & 2) /* Sign does not change */ + AC |= AMSIGN; + if (temp != 0) + goto dofptrap; + if (smode) { + sim_interval = sim_interval - 1; /* count down */ + M[0] &= ~(AMASK | DMASK); + M[0] |= (IC & memmask); + IC = 011; + } + break; +#ifdef I7090 /* Not on 704 */ +/* Double precision floating point */ + case OP_DFSM: + case OP_DUSM: + SR |= MSIGN; + goto dfpadd; + case OP_DFSB: + case OP_DUFS: + SR ^= MSIGN; /* Reverse sign */ + goto dfpadd; + case OP_DFAM: + case OP_DUAM: + SR &= PMASK; /* Clear SR sign */ + case OP_DFAD: + case OP_DUFA: + dfpadd: + temp = 0; /* Steal temp for errors */ + if (MA & 1 && FTM) { + temp = FPDPERR; + goto dofptrap; + } + + shiftcnt = (int)(AC >> 27) & 01777; /* Include P&Q */ + shiftcnt -= (int)(SR >> 27) & 0377; + + f = 0; + /* Save AC & ID signs */ + if (AC & AMSIGN) + f |= 1; + if (SR & MSIGN) + f |= 2; + MA |= 1; /* Point to second word */ + if (shiftcnt > 0) { /* AC Bigger */ + fptemp = (int)(AC >> 27) & 0377; + if (shiftcnt <= 0100) { + ID = AC; + if (f & 1) /* Copy sign */ + ID |= MSIGN; + } + f = (f >> 1) | ((1 & f) << 1); /* Exchange signs */ + if (shiftcnt > 077) { + if ((AC & FPNBIT) == 0) + ID = AC; + goto dpfnorm; + } else { + t_uint64 t; + AC &= ~FPMMASK; + AC |= SR & FPMMASK; + SR &= ~FPMMASK; + SR |= MQ & FPMMASK; + MQ &= ~FPMMASK; + ReadMem(0, t); + MQ |= t & FPMMASK; + } + /* AC=c, MQ=d SR=b, ID=a */ + } else { /* ID Bigger then AC, AC Smaller */ + t_uint64 t; + shiftcnt = -shiftcnt; /* Change sign */ + fptemp = (int)(SR >> 27) & 0377; + if (shiftcnt > 077) { + if (SR & FPNBIT) { + /* Early exit */ + AC = SR; + fptemp = (int)(AC >> 27) & 0377; + ID = (SR & (~FPMMASK)) | (MQ & FPMMASK); + ReadMem(0, MQ); + goto dpdone; + } + MQ &= ~FPMMASK; + AC &= ~FPMMASK; + } + ID = SR; + SR &= ~FPMMASK; + ReadMem(0, t); + SR |= t & FPMMASK; + } + /* Clear sign */ + AC &= FPMMASK; /* Clear char and sign */ + MQ &= FPMMASK; + shiftcnt &= 0377; + if (shiftcnt >= 0 && shiftcnt < 0177) { + sim_interval--; + while (shiftcnt > 0) { + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + shiftcnt--; + } + } else { + AC = 0; + MQ = 0; + } + sim_interval--; + + /* Do add */ + if (f == 2 || f == 1) { + /* Ones compliment AC/MQ */ + MQ ^= FPMMASK; + AC ^= FPMMASK; + /* Form 2's compliment */ + MQ++; + if (MQ & FPOBIT) { + AC++; + MQ ^= FPOBIT; + } + /* Subract ID/SR */ + MQ += (SR & FPMMASK); + if (MQ & FPOBIT) { + AC++; + MQ ^= FPOBIT; + } + AC += (ID & FPMMASK); + /* If AC,MQ < 0 then ID,SR was larger */ + if (AC & FPOBIT) + AC ^= FPOBIT; + else { + f ^= 2; /* Change sign of AC */ + MQ ^= FPMMASK; + AC ^= FPMMASK; + MQ++; + if (MQ & FPOBIT) { + AC++; + MQ ^= FPOBIT; + } + } + } else { + MQ += SR & FPMMASK; + /* Propegate carry */ + if (MQ & FPOBIT) { + AC++; + MQ ^= FPOBIT; + } + AC += ID & FPMMASK; + } + + /* Check for overflow */ + if (AC & FPOBIT) { + if (AC & 1) + MQ |= FPOBIT; + AC >>= 1; + MQ >>= 1; + /* OV check */ + if (fptemp == 0377) + temp |= FPACERR | FPOVERR; + fptemp++; + } + + dpfnorm: + /* Are we normalizing */ + if (opcode == OP_DFAD || opcode == OP_DFSB || + opcode == OP_DFAM || opcode == OP_DFSM) { + sim_interval--; + /* Preshift before we normalize */ + if ((AC & FPMMASK) == 0 && (MQ & FPMMASK) != 0) { + AC |= MQ & FPMMASK; + MQ &= ~FPMMASK; + if (fptemp < 27) + temp |= FPACERR; + fptemp -= 27; + } + while ((AC & FPNBIT) == 0 && (AC & FPMMASK) != 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) { + AC |= 1; + MQ &= ~FPOBIT; + } + if (fptemp == 0 && (temp & FPOVERR) == 0) + temp |= FPACERR; + fptemp--; /* UF Check */ + } + if (AC == 0 && MQ == 0) { + fptemp = 0; + f |= f << 1; + } + } + dpdone: + /* Put pieces back together */ + AC &= FPMMASK; + MQ &= FPMMASK; + AC |= ((t_uint64) (fptemp & 01777)) << 27; + if (AC != 0) { + if (fptemp < 27 && (temp & FPOVERR) == 0) + temp |= FPMQERR; + fptemp -= 27; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; + } + if (f & 2) + AC |= AMSIGN; + if (f & 2) + MQ |= MSIGN; + if (temp != 0) + goto dofptrap; + break; + case OP_DFMP: + case OP_DUFM: + temp = 0; + if (MA & 1) { + temp |= FPDPERR; + if (FTM) + goto dofptrap; + } + /* Quick out for zero result. */ + fptemp = (int)(SR >> 27) & 0377; + if ((SR & PMASK) == 0) { + AC = MQ = 0; + break; + } + + /* Compute exponent */ + fptemp += (int)(AC >> 27) & 0377; + fptemp -= 128; + + /* Figure out sign */ + if (((AC & AMSIGN) != 0) != (0 != (SR & MSIGN))) + f = 1; + else + f = 0; + + /* Prepare for first multiply */ + MQ &= FPMMASK; /* B */ + ID = AC & FPMMASK; /* A */ + if (AC == 0 && MQ == 0) { + ID = SR & (MSIGN | FPCMASK); + AC = (f) ? AMSIGN : 0; + MQ = (f) ? MSIGN : 0; + if (temp != 0) + goto dofptrap; + break; + } + + AC = 0; + /* First multiply B * C */ + if ((SR & FPMMASK) != 0 && MQ != 0) { + SR &= FPMMASK; + shiftcnt = 27; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + AC &= FPMMASK; + } + } + + /* Adjust registers for second multiply */ + /* ID:A=xxx-0, MQ:b =0 SR:c = xx:xx d=xx:xxx */ + ID ^= SR; /* A, C */ + SR ^= ID; + ID ^= SR; /* C, A */ + MA |= 1; + ReadMem(0, MQ); /* D */ + if (MQ == 0 || (SR & FPMMASK) == 0) { + /* Early out two. */ + if ((SR & FPMMASK) == 0 && opcode == OP_DFMP) { + AC = (f) ? AMSIGN : 0; + MQ = (f) ? MSIGN : 0; + if (temp != 0) + goto dofptrap; + break; + } + MQ = SR; /* A */ + SR = ID; /* C */ + /* Early out three. */ + if ((SR & FPMMASK) == 0 && opcode == OP_DFMP) { + AC = (f) ? AMSIGN : 0; + MQ = (f) ? MSIGN : 0; + ID &= FPMMASK; + if (temp != 0) + goto dofptrap; + break; + } + ID &= ~FPMMASK; + ID |= FPMMASK & AC; /* BC */ + } else { + ibr = AC & FPMMASK; /* BC */ + MQ &= FPMMASK; + AC = 0; + /* Second multiply A * D */ + shiftcnt = 27; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + AC &= FPMMASK; + } + MQ = SR; /* A */ + SR = ID; /* C */ + ID = FPMMASK & ibr; /* BC */ + AC += ibr; /* AD + BC */ + } + SR &= FPMMASK; + + /* AC:A=220-77, MQ:b =0 SR:c = 220-77 d=0 mq = 7601 */ + /* third multiply high * high */ + if (MQ == 0 || SR == 0) { + /* MQ == A, AC = AD + BC, ID = BC, SR = C */ + MQ = AC; + AC = 0; + if (opcode == OP_DFMP && SR == 0) + ID &= FPMMASK; + } else { + MQ &= FPMMASK; /* Just to be sure */ + ID &= FPMMASK; /* Clear char */ + shiftcnt = 27; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + AC &= FPMMASK; + } + } + + /* Normalize if DFMP */ + if (opcode == OP_DFMP) { + /* Check for true zero result */ + if (MQ == 0 && AC == 0) { + fptemp = 0; + } else if ((AC & FPNBIT) == 0 && (AC & FPMMASK) != 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) + AC |= 1; + MQ &= FPMMASK; + fptemp--; /* UF check */ + } + } + + /* Fix exponents and check for over/underflow */ + if (fptemp != 0) { + if (fptemp < 0) + temp |= FPACERR | FPMQERR; + else if (fptemp < 27) + temp |= FPMQERR; + else if (fptemp > 0377) + temp |= FPOVERR | FPACERR; + AC |= ((t_uint64) (fptemp & 01777)) << 27; + fptemp -= 27; + if (fptemp > 0377) + temp |= FPOVERR | FPMQERR; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; /* UF Check */ + } + + /* Restore signs */ + if (f) { + MQ |= MSIGN; + AC |= AMSIGN; + } + + /* Handle trapping */ + if (temp != 0) + goto dofptrap; + break; + + case OP_DFDH: + case OP_DFDP: + if (MA & 1) { + temp = FPDPERR; + if (FTM) + goto dofptrap; + } + /* Sign of SR => MQ */ + temp = (AC & FPMMASK) - ((SR & FPMMASK) << 1); + if ((temp & AMSIGN) == 0 || (SR & FPMMASK) == 0) { + dcheck = 1; + if (opcode == OP_DFDH) + goto halt; + break; + } + + /* Result sign */ + if (((AC & AMSIGN) != 0) != (0 != (SR & MSIGN))) + f = 1; + else + f = 0; + if (AC & AMSIGN) /* Sign A+B */ + f |= 2; + if (SR & MSIGN) /* Sign C+D */ + f |= 4; + + /* Check for divide by 0 */ + if ((MQ & FPMMASK) == 0 && (AC & FPMMASK) == 0) { + /* Divide check by 0 */ + ID = MQ = (f & 1) ? MSIGN : 0; + AC = (f & 1) ? AMSIGN : 0; + break; + } + /* Split appart fraction and charateristics */ + fptemp2 = (int)(AC >> 27) & 01777; + fptemp = (int)(SR >> 27) & 0377; + fptemp = fptemp2 - fptemp; + fptemp += 0200; + + ID = SR & FPMMASK; /* ID = C */ + AC &= FPMMASK; /* A */ + MQ &= FPMMASK; /* B */ + SR &= FPMMASK; /* C */ + MA |= 1; + ReadMem(0, ibr); + ibr &= FPMMASK; /* D */ + + /* Precheck SR less then AC */ + if (((AC - SR) & AMSIGN) == 0) { + if (AC & 1) + MQ |= FPOBIT; + MQ >>= 1; + AC >>= 1; + f |= 16; /* Q>1 trigger */ + } + + /* Divide AB / C => AC=R, MQ=Q1 */ + shiftcnt = 27; + do { + AC <<= 1; + MQ <<= 1; + if (MQ & FPOBIT) { + MQ &= ~FPOBIT; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + /* ID=xxx.0, SR=C, ibr=D, AC=R1, MQ=Q1 */ + /* Set up for multiply */ + SR = MQ; /* SR <- Q1' */ + MQ = ibr; /* MQ <- D */ + ibr = AC; /* ibr <- R1 */ + AC = 0; + /* ID=xxx.C, SR=Q1, ibr=R1, AC=0, MQ=D */ + /* Multiply Q1*D => AC,MQ */ + shiftcnt = 27; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= FPNBIT; + AC >>= 1; + AC &= FPMMASK; + } + /* ID=C.C, SR=Q1, ibr=R1, AC=Q1Dh, MQ=Q1Dl */ + /* AC <- R1 - Q1D */ + if (ibr < AC) { + AC = AC - ibr; + f |= 8; /* Sign R1 - Q1D */ + } else + AC = ibr - AC; + MQ = 0; /* MQ <- 0 */ + ID ^= SR; /* SR<>ID Q1<>C */ + SR ^= ID; + ID ^= SR; + + /* Divide R1 - Q1D / C => AC=R, MQ=Q */ + if (f & 16) + fptemp++; + /* Adjust ID register to correct exponent */ + ID |= ((t_uint64) (fptemp & 0377)) << 27; + if (f & 1) + ID |= MSIGN; + /* Check before final divide */ + temp = AC - (SR << 1); + if ((temp & AMSIGN) == 0 || SR == 0) { + if ((f & 0xa) == 2 || (f & 0xa) == 8) { + MQ |= MSIGN; + AC |= AMSIGN; + } + dcheck = 1; + if (opcode == OP_DFDH) + goto halt; + break; + } + + /* Check Quotient > 1 */ + if (((AC - SR) & AMSIGN) == 0) { + if (AC & 1) + MQ |= FPNBIT; + MQ >>= 1; + AC >>= 1; + f |= 32; /* Q2>1 trigger */ + } + + /* Actual divide (R1-Q1D/C) => AC=R2, MQ=Q2 */ + shiftcnt = 27; + do { + AC <<= 1; + MQ <<= 1; + if (MQ & FPOBIT) { + MQ &= ~FPOBIT; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + /* ID=Q1, SR=C, ibr=R1, AC=R1-Q1D/C, MQ=Q2 */ + AC = 0; + if (f & 32) { + MQ <<= 1; + if (MQ & FPOBIT) { + AC |= 1; + MQ ^= FPOBIT; + } + } + + /* MQ = Q2, ID=Q1, SR=C, ibr=R1, AC=xxx */ + /* Sign Q2 = sign C+D, sign Q1 = sign R1-Q1D */ + temp = (MA & 1) ? FPDPERR : 0; /* Restore errors */ + + SR = ID & FPMMASK; /* SR <- Q1 */ + if ((f & 8)) { + AC = SR - AC; + MQ ^= FPMMASK; + MQ++; + if (MQ & FPOBIT) { + MQ &= FPMMASK; + } else { + AC--; + } + } else { + AC += SR; + } + /* Check for overflow */ + if (AC & FPOBIT) { + if (AC & 1) + MQ |= FPOBIT; + AC >>= 1; + MQ >>= 1; + /* OV check */ + if (fptemp == 0377) + temp |= FPACERR | FPOVERR; + fptemp++; + } + + /* Normalize results */ + while ((AC & FPNBIT) == 0 && + ((AC & FPMMASK) != 0 || (MQ & FPMMASK) != 0)) { + MQ <<= 1; + AC <<= 1; + if (MQ & FPOBIT) { + AC |= 1; + MQ ^= FPOBIT; + } + if (fptemp == 0 && (temp & FPOVERR) == 0) + temp |= FPACERR; + fptemp--; /* UF Check */ + } + MQ &= FPMMASK; + if (AC == 0 && MQ == 0) + fptemp = 0; + /* Compute new characteristic */ + if (fptemp > 0377) + temp |= FPOVERR | FPACERR; + else if (fptemp < 0) + temp |= FPACERR | FPMQERR; + else if (fptemp < 27) + temp |= FPMQERR; + AC |= ((t_uint64) (fptemp & 01777)) << 27; + fptemp -= 27; + if (fptemp > 0377) + temp |= FPOVERR | FPMQERR; + MQ |= ((t_uint64) (fptemp & 0377)) << 27; + /* Fix signs on results */ + if (f & 1) { /* Sign does not change */ + MQ |= MSIGN; + AC |= AMSIGN; + } + if (temp != 0) + goto dofptrap; + break; + case OP_DLD: + AC = ((SR & MSIGN) << 2) | (SR & PMASK); + f = MA & 1; + MA |= 1; + ReadMem(0, MQ); + if (f) { + temp = FPDPERR; + if (FTM) { + goto dofptrap; + } + } + break; + case OP_DST: + SR = (AC & (APSIGN - 1)); + if (AC & AMSIGN) + SR |= MSIGN; + WriteMem(); + MA = (MA + 1); + SR = MQ; + break; +#endif + +/* Logic operations */ + case OP_ORA: + AC |= SR & AQMASK; + break; + case OP_ORS: + SR |= AC; + SR &= AQMASK; + break; + case OP_ANA: + AC &= SR; + AC &= AQMASK; + break; + case OP_ANS: + SR &= AC; + SR &= AQMASK; + break; + case OP_ERA: + AC ^= SR; + AC &= AQMASK; /* Clear S & Q */ + break; + +#ifdef I7090 /* Not on 704 */ +/* Conversion */ + case OP_CVR + 3: + case OP_CVR + 2: + case OP_CVR + 1: + case OP_CVR: + shiftcnt = (int)(SR >> 18L) & 0377; + if (AC & AMSIGN) { + f = 1; + AC &= AMMASK; + } else + f = 0; + while (shiftcnt != 0) { + MA += (uint16)(AC & 077); + ReadMem(0, SR); + MA = (uint16)(AMASK & SR); + AC >>= 6; + AC |= SR & (077LL << 30); + shiftcnt--; + } + /* Save XR if tag set */ + if (tag & 1) + XR[1] = (uint16)(MA & AMASK); + /* restore sign */ + if (f) + AC |= AMSIGN; + break; + + case OP_CAQ + 3: + case OP_CAQ + 2: + case OP_CAQ + 1: + case OP_CAQ: + shiftcnt = (int)(SR >> 18L) & 0377; + while (shiftcnt != 0) { + MA += (uint16)(MQ >> 30) & 077; + ReadMem(0, SR); + MA = (uint16)(AMASK & SR); + MQ <<= 6; + MQ |= (MQ >> 36) & 077; + MQ &= WMASK; + AC += SR; + AC &= AMMASK; + shiftcnt--; + } + if (tag & 1) + XR[1] = (uint16)(MA & AMASK); + break; + + case OP_CRQ + 3: + case OP_CRQ + 2: + case OP_CRQ + 1: + case OP_CRQ: + shiftcnt = (int)(SR >> 18L) & 0377; + while (shiftcnt != 0) { + MA += (uint16)(MQ >> 30) & 077; + ReadMem(0, SR); + MA = (uint16)(AMASK & SR); + MQ <<= 6; + MQ &= (WMASK ^ 077); + MQ |= (SR >> 30) & 077; + shiftcnt--; + } + if (tag & 1) + XR[1] = (uint16)(MA & AMASK); + break; +#endif + +/* Shift */ + case OP_LLS: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (MQ & MSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AQMASK; + while (shiftcnt-- > 0) { + MQ <<= 1; + AC <<= 1; + if (MQ & MSIGN) + AC |= 1; + if (AC & APSIGN) + acoflag = 1; + } + /* Restore sign when done */ + AC &= AMMASK; + MQ &= PMASK; + if (f) { + AC |= AMSIGN; + MQ |= MSIGN; + } + break; + case OP_LRS: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + MQ &= PMASK; + while (shiftcnt-- > 0) { + if (AC & 1) + MQ |= MSIGN;; + MQ >>= 1; + AC >>= 1; + } + /* Restore sign when done */ + AC &= AMMASK; + if (f) { + AC |= AMSIGN; + MQ |= MSIGN; + } + break; + case OP_ALS: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AQMASK; + while (shiftcnt-- > 0) { + AC <<= 1; + if (AC & APSIGN) + acoflag = 1; + } + /* Restore sign and overflow when done */ + AC &= AMMASK; + if (f) + AC |= AMSIGN; + break; + case OP_ARS: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + AC >>= shiftcnt; + /* Restore sign when done */ + if (f) + AC |= AMSIGN; + break; + + case OP_LGL: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + while (shiftcnt-- > 0) { + AC <<= 1; + if (MQ & MSIGN) + AC |= 1; + MQ <<= 1; + if (AC & APSIGN) + acoflag = 1; + } + /* Restore sign when done */ + AC &= AMMASK; + MQ &= WMASK; + if (f) + AC |= AMSIGN; + break; + case OP_LGR: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + /* Save sign */ + if (AC & AMSIGN) + f = 1; + else + f = 0; + /* Clear it for now */ + AC &= AMMASK; + while (shiftcnt-- > 0) { + MQ >>= 1; + if (AC & 1) + MQ |= MSIGN; + AC >>= 1; + } + /* Restore sign when done */ + AC &= AMMASK; + if (f) + AC |= AMSIGN; + break; + case OP_RQL: + shiftcnt = MA & 0377; + sim_interval = sim_interval - (shiftcnt >> 6); + while (shiftcnt-- > 0) { + MQ <<= 1; + if (MQ & AQSIGN) + MQ |= 1; + MQ &= WMASK; + } + break; + +/* 704 Input output Instructions */ + case OP_LDA: + if (chan_select(0)) { + extern DEVICE drm_dev; + drum_addr = (uint32)(MQ = SR); + sim_debug(DEBUG_DETAIL, &drm_dev, + "set address %06o\n", drum_addr); + MQ <<= 1; + chan_clear(0, DEV_FULL); /* In case we read something + before we got here */ + } else + iocheck = 1; + break; + case OP_CPY: + case OP_CAD: + /* If no channel, set Iocheck and treat as nop */ + if (chan_unit[0].flags & UNIT_DIS) { + iocheck = 1; + break; + } + + /* If device disconnecting, just wait */ + if (chan_test(0, DEV_DISCO)) { + iowait = 1; + break; + } + + /* Instruct is NOP first time */ + /* Incomplete last word leaves result in MQ */ + if (chan_select(0)) { + extern uint8 bcnt[NUM_CHAN]; + chan_set(0, STA_ACTIVE); + switch (chan_flags[0] & (DEV_WRITE | DEV_FULL)) { + case DEV_WRITE | DEV_FULL: + case 0: + /* On EOR skip 1, on EOF skip two */ + if (chan_test(0, CHS_EOF|CHS_EOT|DEV_REOR)) + chan_set(0, DEV_DISCO); + iowait = 1; + break; + case DEV_WRITE: + MQ = assembly[0] = SR; + bcnt[0] = 6; + chan_set(0, DEV_FULL); + if (opcode == OP_CAD) + goto ladd; + break; + case DEV_FULL: + SR = MQ; + WriteP(MA, MQ); + bcnt[0] = 6; + chan_clear(0, DEV_FULL); + if (opcode == OP_CAD) + goto ladd; + break; + } + } else { + /* If channel not active, turn on io-check */ + if (chan_test(0, STA_ACTIVE) == 0) { + iocheck = 1; + break; + } + + if (chan_stat(0, CHS_EOF|CHS_EOT)) { + IC++; + /* On EOR skip two */ + } else if (chan_stat(0, DEV_REOR)) { + IC += 2; + /* Advance 1 on Error and set iocheck */ + } else if (chan_stat(0, CHS_ERR)) { + iocheck = 1; + IC++; + } + chan_clear(0, STA_ACTIVE|DEV_REOR|CHS_ERR); + break; + } + break; + +#ifdef I7090 /* Not on 704 */ +/* Input/Output Instuctions */ + case OP_ENB: + ioflags = SR; + itrap = 1; + ihold = 1; + + /* + * IBSYS can't have an trap right after ENB or it will hang + * on a TTR * in IBNUC. + */ + if (CPU_MODEL >= CPU_7090) + break; + + /* Don't wait if EOF or Error is pending but hold if trap */ + temp = 00000001000001LL; + for (shiftcnt = 1; shiftcnt < NUM_CHAN; shiftcnt++) { + if (chan_active(shiftcnt) == 0) { + if (temp & ioflags & AMASK && + iotraps & 1 << shiftcnt) + break; + if ((temp & ioflags & AMASK && + chan_test(shiftcnt, CHS_EOF)) || + (temp & ioflags & DMASK && + chan_test(shiftcnt, CHS_ERR))) { + ihold = 0; + break; + } + } + temp <<= 1; + } + break; +#endif + + case OP_RDS: /* Read select */ + if (CPU_MODEL == CPU_704) + MQ = 0; + else if (first_rdwr == 0) { + iotraps &= ~(1 << ((MA >> 9) & 017)); + first_rdwr = 1; + } + opcode = IO_RDS; + goto docmd; + case OP_WRS: /* Write select */ + if (CPU_MODEL != CPU_704 && first_rdwr == 0) { + first_rdwr = 1; + } + opcode = IO_WRS; + goto docmd; + case OP_WEF: /* Write EOF */ + opcode = IO_WEF; + goto docmd; + case OP_BSR: /* Backspace */ + opcode = IO_BSR; + goto docmd; + case OP_BSF: /* Backspace File */ + opcode = IO_BSF; + goto docmd; + case OP_REW: /* Rewind */ + opcode = IO_REW; + goto docmd; + case OP_RUN: /* Rewind unload */ + opcode = IO_RUN; + goto docmd; + case OP_SDN: /* Set density */ + opcode = (MA & 020) ? IO_SDH: IO_SDL; + goto docmd; + case OP_DRS: /* Drop ready status */ + opcode = IO_DRS; + docmd: + switch (chan_cmd(MA, opcode)) { + case SCPE_BUSY: + iowait = 1; /* Channel is active, hold */ + break; + case SCPE_OK: + ihold = 1; /* Hold interupts for one cycle */ + first_rdwr = 0; + break; + case SCPE_IOERR: + iocheck = 1; + first_rdwr = 0; + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + } + break; + + case OP_TRS: /* Test ready status */ + switch (chan_cmd(MA, IO_TRS)) { + case SCPE_BUSY: + iowait = 1; /* Channel is active, hold */ + break; + case SCPE_OK: /* Ready, skip one */ + IC++; + ihold = 2; /* Hold interupts for two */ + case SCPE_IOERR: /* Not ready, just return */ + break; + case SCPE_NODEV: + reason = STOP_IOCHECK; + break; + } + break; + +#ifdef I7090 /* Not on 704 */ + case OP_TRCA: /* Transfer on Redundancy check */ + if ((1LL << 18) & ioflags) + break; + f = chan_stat(1, CHS_ERR); + goto branch; + case OP_TRCB: /* Transfer on Redundancy check */ + if ((1LL << 19) & ioflags) + break; + f = chan_stat(2, CHS_ERR); + goto branch; + case OP_TRCC: + if ((1LL << 20) & ioflags) + break; + f = chan_stat(3, CHS_ERR); + goto branch; + case OP_TRCD: + if ((1LL << 21) & ioflags) + break; + f = chan_stat(4, CHS_ERR); + goto branch; + case OP_TRCE: + if ((1LL << 22) & ioflags) + break; + f = chan_stat(5, CHS_ERR); + goto branch; + case OP_TRCF: + if ((1LL << 23) & ioflags) + break; + f = chan_stat(6, CHS_ERR); + goto branch; + case OP_TRCG: + if ((1LL << 24) & ioflags) + break; + f = chan_stat(7, CHS_ERR); + goto branch; + case OP_TRCH: + if ((1LL << 25) & ioflags) + break; + f = chan_stat(8, CHS_ERR); + goto branch; +#endif + + case OP_TEFA: /* Transfer on channel EOF */ + if ((1LL << 0) & ioflags) + break; + f = chan_stat(1, CHS_EOF); + goto branch; +#ifdef I7090 /* Not on 704 */ + case OP_TEFB: /* Transfer on EOF */ + if ((1LL << 1) & ioflags) + break; + f = chan_stat(2, CHS_EOF); + goto branch; + case OP_TEFC: + if ((1LL << 2) & ioflags) + break; + f = chan_stat(3, CHS_EOF); + goto branch; + case OP_TEFD: + if ((1LL << 3) & ioflags) + break; + f = chan_stat(4, CHS_EOF); + goto branch; + case OP_TEFE: + if ((1LL << 4) & ioflags) + break; + f = chan_stat(5, CHS_EOF); + goto branch; + case OP_TEFF: + if ((1LL << 5) & ioflags) + break; + f = chan_stat(6, CHS_EOF); + goto branch; + case OP_TEFG: + if ((1LL << 6) & ioflags) + break; + f = chan_stat(7, CHS_EOF); + goto branch; + case OP_TEFH: + if ((1LL << 7) & ioflags) + break; + f = chan_stat(8, CHS_EOF); + goto branch; + case OP_TCOA: /* Transfer if channel in operation */ + case OP_TCOB: + case OP_TCOC: + case OP_TCOD: + case OP_TCOE: + case OP_TCOF: + case OP_TCOG: + case OP_TCOH: + f = chan_active((opcode & 017) + 1); + /* Check if TCOx * */ + if ((cpu_unit.flags & UNIT_FASTIO) && f && MA == (IC - 1)) + iowait = 1; + goto branch; + case OP_TCNA: /* Transfer on channel not in operation */ + case OP_TCNB: + case OP_TCNC: + case OP_TCND: + case OP_TCNE: + case OP_TCNF: + case OP_TCNG: + case OP_TCNH: + f = !chan_active((opcode & 017) + 1); + goto branch; + + case OP_RSCA: /* Reset and load channel */ + f = 1; + goto chanrst; + case OP_RSCB: /* Reset and load channel */ + f = 2; + goto chanrst; + case OP_RSCC: + f = 3; + goto chanrst; + case OP_RSCD: + f = 4; + goto chanrst; + case OP_RSCE: + f = 5; + goto chanrst; + case OP_RSCF: + f = 6; + goto chanrst; + case OP_RSCG: + f = 7; + goto chanrst; + case OP_RSCH: + f = 8; + chanrst: + /* 7607 channel, start imedately */ + /* 7909 channel, wait until channel not active */ + if (bcore & 1) + MA |= CORE_B; + switch (chan_start(f, MA)) { + case SCPE_IOERR: + iocheck = 1; + break; + case SCPE_BUSY: + iowait = 1; + break; + case SCPE_OK: + ihold = 1; + break; + } + break; + case OP_STCA: /* Load channel, 7909 Start NEA */ + f = 1; + goto chanst; + case OP_STCB: + f = 2; + goto chanst; + case OP_STCC: + f = 3; + goto chanst; + case OP_STCD: + f = 4; + goto chanst; + case OP_STCE: + f = 5; + goto chanst; + case OP_STCF: + f = 6; + goto chanst; + case OP_STCG: + f = 7; + goto chanst; + case OP_STCH: + f = 8; + chanst: + /* 7907 channel, set new address */ + /* 7909 channel, wait until channel idle, and start */ + if (bcore & 1) + MA |= CORE_B; + switch (chan_load(f, MA)) { + case SCPE_IOERR: + iocheck = 1; + break; + case SCPE_BUSY: + iowait = 1; + case SCPE_OK: + break; + } + break; + case OP_SCHA: /* Store data channel */ + if (bcore & 1) + MA |= CORE_B; + chan_store(1, MA); + break; + case OP_SCHB: + if (bcore & 1) + MA |= CORE_B; + chan_store(2, MA); + break; + case OP_SCHC: + if (bcore & 1) + MA |= CORE_B; + chan_store(3, MA); + break; + case OP_SCHD: + if (bcore & 1) + MA |= CORE_B; + chan_store(4, MA); + break; + case OP_SCHE: + if (bcore & 1) + MA |= CORE_B; + chan_store(5, MA); + break; + case OP_SCHF: + if (bcore & 1) + MA |= CORE_B; + chan_store(6, MA); + break; + case OP_SCHG: + if (bcore & 1) + MA |= CORE_B; + chan_store(7, MA); + break; + case OP_SCHH: + if (bcore & 1) + MA |= CORE_B; + chan_store(8, MA); + break; + case OP_SCDA: /* Store channel diags 7909 */ + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(1, MA); + break; + case OP_SCDB: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(2, MA); + break; + case OP_SCDC: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(3, MA); + break; + case OP_SCDD: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(4, MA); + break; + case OP_SCDE: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(5, MA); + break; + case OP_SCDF: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(6, MA); + break; + case OP_SCDG: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(7, MA); + break; + case OP_SCDH: + if (bcore & 1) + MA |= CORE_B; + chan_store_diag(8, MA); + break; + +/* Optional RPQ instructions */ +/* Extended precision floating point */ + case OP_ESB: + SR ^= MSIGN; /* Reverse sign */ + case OP_EAD: + case OP_EUA: + if ((cpu_unit.flags & OPTION_EFP) == 0) + break; + temp = 0; /* Steal temp for errors */ + f = 0; + /* Extract AC char */ + fptemp = (int)(AC >> 18) & AMASK; /* Include P&Q? */ + /* Diff SR char */ + fptemp -= (int)(SR >> 18) & AMASK; + if (AC & AMSIGN) + f |= 2; + if (SR & MSIGN) + f |= 1; + /* Get mem frac */ + MA = (MA + 1); + ReadMem(0, ibr); + if (fptemp >= 0) { /* AC Bigger */ + /* Exchange MQ and ibr */ + SR = MQ; + MQ = ibr; + } else { /* ibr Bigger then MQ, MQ Smaller */ + fptemp = -fptemp; /* Change sign */ + /* Set exponent of result */ + AC &= ~DMASK; + AC |= SR & DMASK; + SR = ibr; + f = ((f >> 1) & 1) | ((f & 1) << 1); + } + AC &= DMASK; + /* Clear sign */ + MQ &= PMASK; + + /* Adjust smaller number */ + if (fptemp >= 0 && fptemp < 044) { + sim_interval--; + shiftcnt = fptemp; + while (shiftcnt > 0) { + MQ >>= 1; + shiftcnt--; + } + } else + MQ = 0; + sim_interval--; + + /* Check signes of SR & AC */ + if (f == 2 || f == 1) { + MQ ^= PMASK; + MQ += SR & PMASK; + /* If MQ < 0 then SR was larger */ + if (MQ & MSIGN) { + MQ++; + MQ &= PMASK; + } else { + MQ ^= PMASK; + if (MQ != 0) + f ^= 2; /* Change sign of Result */ + } + } else + MQ += SR & PMASK; + + /* Check for overflow */ + temp = 0; + if (MQ & MSIGN) { + MQ >>= 1; + AC += 00000001000000LL; + /* OV check */ + if (AC & APSIGN) + temp |= FPSPERR | FPACERR | FPOVERR; + } + + /* Are we normalizing */ + if (opcode == OP_EAD || opcode == OP_ESB) { + sim_interval--; + while ((MQ & ONEBIT) == 0 && ((MQ & PMASK) != 0)) { + MQ <<= 1; + AC -= 00000001000000LL; + } + if (MQ == 0) { + AC = 0; + } + } + + /* Check underflow */ + if (AC & AMSIGN) { + temp |= FPSPERR | FPMQERR; + if (AC & APSIGN) + temp |= FPSPERR | FPOVERR | FPACERR; + } else if (AC & (AQSIGN | PREMASK)) + temp |= FPOVERR | FPACERR; + AC &= AMMASK; + /* Set signs */ + if (f & 2) { + MQ |= MSIGN; + AC |= AMSIGN; + } + if (temp != 0) { + doefptrap: + if (FTM && CPU_MODEL != CPU_704) { + sim_interval = sim_interval - 1; /* count down */ + temp &= ~(FPMQERR | FPACERR); + M[0] &= ~(AMASK | DMASK); + M[0] |= temp | (IC & memmask); + IC = 010; + } else { + if (temp & FPMQERR) + mqoflag = 1; + if (temp & FPACERR) + acoflag = 1; + } + } + break; + case OP_EMP: + if ((cpu_unit.flags & OPTION_EFP) == 0) + break; + temp = 0; + + /* Result sign */ + if (SR & MSIGN) + f = 1; + else + f = 0; + if (AC & AMSIGN) + f ^= 1; + + MQ &= PMASK; + /* Quick out for times 0 */ + if (MQ == 0) { + AC &= RMASK; + if (f) { + MQ |= MSIGN; + AC |= AMSIGN; + } + break; + } + + /* Extract AC char */ + fptemp = (int)(AC >> 18) & AMASK; + /* Diff SR char */ + fptemp += (int)(SR >> 18) & AMASK; + fptemp -= 040000; + + /* Get mem frac */ + MA = MA + 1; + ReadMem(0, SR); + SR &= PMASK; + + /* Quick out for times 0 */ + if (SR == 0) { + MQ = 0; + AC &= RMASK; + if (f) { + MQ |= MSIGN; + AC |= AMSIGN; + } + break; + } + + AC = 0; + /* Do multiply */ + shiftcnt = 043; + while (shiftcnt-- > 0) { + if (MQ & 1) + AC += SR; + MQ >>= 1; + if (AC & 1) + MQ |= ONEBIT; + AC >>= 1; + } + + /* Normalize result */ + if ((AC & ONEBIT) == 0) { + AC <<= 1; + if (MQ & ONEBIT) + AC |= 1; + fptemp--; + } + + /* Move results to MQ. */ + MQ = AC; + + if (MQ == 0) { + AC = 0; + } else { + /* Put exponent in place. */ + AC = ((t_uint64) (fptemp)) << 18; + /* Check underflow */ + if (AC & AMSIGN) { + temp |= FPSPERR | FPMQERR; + if (AC & APSIGN) + temp |= FPSPERR | FPOVERR | FPACERR; + } else if (AC & (AQSIGN | PREMASK)) + temp |= FPOVERR | FPACERR; + /* Clear sign */ + AC &= AMMASK; + } + if (f) { + MQ |= MSIGN; + AC |= AMSIGN; + } + if (temp != 0) + goto doefptrap; + break; + case OP_EDP: + if ((cpu_unit.flags & OPTION_EFP) == 0) + break; + + /* Result sign */ + if (SR & MSIGN) + f = 1; + else + f = 0; + if (AC & AMSIGN) + f ^= 1; + + /* Extract AC char */ + fptemp = (int)(AC >> 18) & AMASK; /* Include P&Q */ + /* Extract SR char */ + fptemp -= (int)(SR >> 18) & AMASK; + fptemp += 040000; /* UF check */ + + /* Get mem frac */ + MA = MA + 1; + ReadMem(0, SR); + + temp = 0; + /* Check for divide by 0 */ + MQ &= PMASK; + if (MQ == 0) { + AC = MQ = 0; + } else { + SR &= PMASK; + if (((MQ - (SR << 1)) & AMSIGN) == 0 || SR == 0) { + dcheck = 1; + AC &= DMASK; + AC |= MQ & RMASK; + if (f) { + MQ |= MSIGN; + AC |= AMSIGN; + } + break; + } + + /* Move MQ to AC */ + AC = MQ & PMASK; + + /* Clear MQ before starting */ + MQ = 0; + shiftcnt = 043; + /* Precheck SR less then AC */ + if (((AC - SR) & AMSIGN) == 0) { + if (AC & 1) + MQ |= ONEBIT; + AC >>= 1; + fptemp++; + f |= 2; + } + + /* Do divide operation */ + sim_interval = sim_interval - shiftcnt; + do { + AC <<= 1; + MQ <<= 1; + if (MQ & MSIGN) { + MQ ^= MSIGN; + AC |= 1; + } + if (SR <= AC) { + AC -= SR; + MQ |= 1; + } + } while (--shiftcnt != 0); + /* Fix things if we didn't preshifted */ + if ((f & 2) == 0 && AC != 0) + MQ &= ~1; + AC = 0; + if (f & 2) { + if ((MQ & ONEBIT) == 0) + MQ <<= 1; + } else { + AC = RMASK; + } + + /* Put exponent in place. */ + AC |= ((t_uint64) (fptemp)) << 18; + /* Check underflow */ + if (AC & AMSIGN) { + temp |= FPSPERR | FPMQERR; + if (AC & APSIGN) + temp |= FPSPERR | FPOVERR | FPACERR; + } else if (AC & (AQSIGN | PREMASK)) + temp |= FPOVERR | FPACERR; + /* Clear sign */ + AC &= AMMASK; + } + /* Fix signs on results */ + if (f & 1) { + MQ |= MSIGN; + AC |= AMSIGN; + } + if (temp != 0) + goto doefptrap; + break; + case OP_EST: + if ((cpu_unit.flags & OPTION_EFP) == 0) + break; + SR &= RMASK; + if (AC & AMSIGN) + SR |= MSIGN; + SR |= LMASK & PMASK & AC; + /* Clear P+Q and 18-35 */ + AC &= AMSIGN | (PMASK & LMASK); + WriteMem(); + MA = memmask & (MA + 1); + SR = MQ; + break; + case OP_ELD: + if ((cpu_unit.flags & OPTION_EFP) == 0) + break; + AC = ((SR & MSIGN) << 2) | (SR & PMASK); + MA = memmask & (MA + 1); + ReadMem(0, MQ); + break; + +/* Special CTSS modes */ + case OP_TIA: + /* Regular xfer in A core, B core trap */ + bcore &= ~2; + sim_debug(DEBUG_PROT, &cpu_dev, "TIA %07o %07o\n", IC, MA); + IC = MA; + tbase = (relo_mode)?relocaddr:0; + break; + case OP_TIB: + /* In A core xfer to B core, B core trap */ + bcore |= 2; + sim_debug(DEBUG_PROT, &cpu_dev, "TIB %07o %07o\n", IC, MA); + IC = MA; + tbase = ((relo_mode)?relocaddr:0); + break; + case OP_LRI: + /* In B core trap, else load relocation */ + relocaddr = (uint16)(SR & 077400); + relo_pend = (SR & MSIGN) ? 0: 1; + ihold = 1; + sim_debug(DEBUG_PROT, &cpu_dev, "LRI %07o %012llo\n", IC, SR); + break; + case OP_LPI: + /* In B core trap, else load protection */ + baseaddr = (uint16)(SR & 077400); + limitaddr = (uint16)((SR >> 18) & 077400); + ihold = 1; + prot_pend = (SR & MSIGN)?0:1; + sim_debug(DEBUG_PROT, &cpu_dev, "LPI %07o %012llo\n", IC, SR); + break; + case OP_SRI: + /* In B core trap, else store relocation */ + SR = relocaddr | ((relo_mode)? (MSIGN >> 1) : 0); + sim_debug(DEBUG_PROT, &cpu_dev, "SRI %07o %012llo\n", IC, SR); + break; + case OP_SPI: + /* In B core trap, else store protection */ + SR = ((t_uint64)limitaddr) << 18 | + ((t_uint64)baseaddr); + sim_debug(DEBUG_PROT, &cpu_dev, "SPI %07o %012llo\n", IC, SR); + break; + + case OP_SPOP: + switch (MA) { + /* Direct data disconnect */ + case 0: + /* Should do disco on channel 0 */ + break; + /* Handle signifigence mode */ + case OP_ESM: + if (cpu_unit.flags & OPTION_FPSM) + smode = 1; + break; + case OP_TSM: + if (cpu_unit.flags & OPTION_FPSM && smode) + IC++; + smode = 0; + break; + /* Special CTSS memory mods */ + case OP_SEA: + if ((cpu_unit.flags & UNIT_DUALCORE) == 0) + break; + /* CTSS Special, set effective to A, Acore only */ + if (bcore & 4) + goto prottrap; + bcore &= ~1; + ihold = 1; + break; + case OP_SEB: + if ((cpu_unit.flags & UNIT_DUALCORE) == 0) + break; + /* CTSS Special, set effective to B, Acore only */ + if (bcore & 4) + goto prottrap; + bcore |= 1; + ihold = 1; + break; + case OP_IFT: + if ((cpu_unit.flags & UNIT_DUALCORE) == 0) + break; + /* CTSS Special, skip if instruction A, Acore only */ + if (bcore & 4) + goto prottrap; + if ((bcore & 1) == 0) + IC++; + break; + case OP_EFT: + if ((cpu_unit.flags & UNIT_DUALCORE) == 0) + break; + /* CTSS Special, skip if effective A, Acore only */ + if (bcore & 4) + goto prottrap; + if ((bcore & 2) == 0) + IC++; + break; + } + break; +#endif + + default: + fprintf(stderr, "Invalid opcode %o IC=%o %012llo\n", + opcode, IC, temp); + reason = STOP_UUO; + break; + } + if (opinfo & (S_B | S_F)) { + WriteMem(); + } + /* Store result into an index register */ + if ((opinfo & S_X)) { + SR &= AMASK; + update_xr(tag, SR); + } + break; + } + + chan_proc(); /* process any pending channel events */ + if (instr_count != 0 && --instr_count == 0) + return SCPE_STEP; + } /* end while */ + +/* Simulation halted */ + + return reason; +} + +/* Nothing special to do, just return true if cmd is write and we got here */ +uint32 cpu_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + if (cmd == OP_WRS) + return 1; + return -1; +} + + +/* Reset routine */ + +t_stat +cpu_reset(DEVICE * dptr) +{ + int i; + + AC = 0; + MQ = 0; + SR = 0; + dualcore = 0; + if (cpu_unit.flags & UNIT_DUALCORE) + dualcore = 1; + for (i = 0; i < 7; i++) + XR[i] = 0; + MTM = 1; + TM = STM = CTM = nmode = smode = 0; + FTM = 1; + itrap = 1; + iotraps = baseaddr = bcore = 0; + ioflags = 0; + interval_irq = dcheck = acoflag = mqoflag = iocheck = 0; + sim_brk_types = sim_brk_dflt = SWMASK('E'); + limitaddr = 077777; + memmask = MEMMASK; + if (cpu_unit.flags & OPTION_TIMER) { + sim_rtcn_init_unit (&cpu_unit, cpu_unit.wait, TMR_RTC); + sim_activate(&cpu_unit, cpu_unit.wait); + } + + return SCPE_OK; +} + +/* Interval timer routines */ +t_stat +rtc_srv(UNIT * uptr) +{ + if (cpu_unit.flags & OPTION_TIMER) { + int32 t; + t = sim_rtcn_calb (rtc_tps, TMR_RTC); + sim_activate_after(uptr, 1000000/rtc_tps); + M[5] += 1; + if (M[5] & MSIGN) + interval_irq = 1; + } + return SCPE_OK; +} + +/* Memory examine */ + +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = M[addr] & 0777777777777LL; + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE) + return SCPE_NXM; + M[addr] = val & 0777777777777LL; + return SCPE_OK; +} + +t_stat +cpu_set_size(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + t_uint64 mc = 0; + uint32 i; + int32 v; + + v = val >> UNIT_V_MSIZE; + v *= 8192; + if (v == 0) + v = 4096; + if ((v < 0) || (v > MAXMEMSIZE) || ((v & 07777) != 0)) + return SCPE_ARG; + for (i = v-1; i < MEMSIZE; i++) + mc |= M[i]; + if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + MEMSIZE = v; + memmask = v - 1; + cpu_unit.flags &= ~UNIT_MSIZE; + cpu_unit.flags |= val; + for (i = MEMSIZE; i < MAXMEMSIZE; i++) + M[i] = 0; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].ic = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + t_stat r; + t_value sim_eval; + struct InstHistory *h; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, +"IC AC MQ EA SR XR1 XR2 XR4\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ic & HIST_PC) { /* instruction? */ + fprintf(st, "%06o%c", h->ic & 077777, ((h->ic>>19)&1)?'b':' '); + switch ((h->ac & (AMSIGN | AQSIGN | APSIGN)) >> 35L) { + case (AMSIGN | AQSIGN | APSIGN) >> 35L: + fprintf(st, "-QP"); + break; + case (AMSIGN | AQSIGN) >> 35L: + fprintf(st, " -Q"); + break; + case (AMSIGN | APSIGN) >> 35L: + fprintf(st, " -P"); + break; + case (AMSIGN) >> 35L: + fprintf(st, " -"); + break; + case (AQSIGN | APSIGN) >> 35L: + fprintf(st, " QP"); + break; + case (AQSIGN) >> 35L: + fprintf(st, " Q"); + break; + case (APSIGN) >> 35L: + fprintf(st, " P"); + break; + case 0: + fprintf(st, " "); + break; + } + fprint_val(st, h->ac & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + if (h->mq & MSIGN) + fputc('-', st); + else + fputc(' ', st); + fprint_val(st, h->mq & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->ea, 8, 16, PV_RZRO); + fputc(((h->ic>>18)&1)?'b':' ', st); + if (h->sr & MSIGN) + fputc('-', st); + else + fputc(' ', st); + fprint_val(st, h->sr & PMASK, 8, 35, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->xr1, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->xr2, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->xr4, 8, 15, PV_RZRO); + fputc(' ', st); + sim_eval = h->op; + if ( + (fprint_sym + (st, h->ic & AMASK, &sim_eval, &cpu_unit, + SWMASK('M'))) > 0) fprintf(st, "(undefined) %012llo", h->op); + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ +#ifdef I7090 + return "IBM 709x CPU"; +#else + return "IBM 704 CPU"; +#endif +} + +t_stat +cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +#ifdef I7090 +fprintf (st, "The CPU can be set to a IBM 704, IBM 709, IBM 7090 or IBM 7094\n"); +fprintf (st, "The type of CPU can be set by one of the following commands\n\n"); +fprintf (st, " sim> set CPU 704 sets IBM 704 emulation\n"); +fprintf (st, " sim> set CPU 709 sets IBM 709 emulation\n"); +fprintf (st, " sim> set CPU 7090 sets IBM 7090 emulation\n"); +fprintf (st, " sim> set CPU 7094 sets IBM 7094 emulation\n\n"); +#else +fprintf (st, "The CPU behaves as a IBM 704\n"); +#endif +fprintf (st, "These switches are recognized when examining or depositing in CPU memory:\n\n"); +fprintf (st, " -c examine/deposit characters, 6 per word\n"); +fprintf (st, " -l examine/deposit half words\n"); +fprintf (st, " -m examine/deposit IBM 709 instructions\n\n"); +fprintf (st, "The memory of the CPU can be set in 4K incrememts from 4K to 32K with the\n\n"); +fprintf (st, " sim> SET CPU xK\n\n"); +#ifdef I7090 +fprintf (st, "For systems like IBSYS FASTIO can be enabled. This causes the CPU to finish\n"); +fprintf (st, "all outstanding I/O requests when it detects an IDLE loop. This is detected\n"); +fprintf (st, "by a TCOx to itself. TRUEIO waits until the given timeout. "); +fprintf (st, "For faster\noperation FASTIO can speed up execution, by eliminating"); +fprintf (st, "waits on devices.\nThe default is TRUEIO.\n\n"); +fprintf (st, "For the IBM 709x the following options can be enabled\n\n"); +fprintf (st, " sim> SET CPU EFP enables extended Floating Point\n"); +fprintf (st, " sim> SET CPU NOEFP disables extended Floating Point\n\n"); +fprintf (st, " sim> SET CPU FPSM enables significance mode Floating Point\n"); +fprintf (st, " sim> SET CPU NOFPSM disables significance mode Floating Point\n\n"); +fprintf (st, " sim> SET CPU CLOCK enables clock in memory location 5\n"); +fprintf (st, " sim> SET CPU NOCLOCK disables the clock in memory location 5\n\n"); +fprintf (st, " sim> SET CPU STANDARD sets generic IBM 709x CPU\n"); +fprintf (st, " sim> SET CPU CTSS enables RPQ options, DUAL Core and extended memory for\n"); +fprintf (st, " CTSS support\n\n"); +#endif +fprintf (st, "The CPU can maintain a history of the most recently executed instructions.\n" +); +fprintf (st, "This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:\n\n" +); +fprintf (st, " sim> SET CPU HISTORY clear history buffer\n"); +fprintf (st, " sim> SET CPU HISTORY=0 disable history\n"); +fprintf (st, " sim> SET CPU HISTORY=n{:file} enable history, length = n\n"); +fprintf (st, " sim> SHOW CPU HISTORY print CPU history\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + +return SCPE_OK; +} + diff --git a/I7000/i7090_defs.h b/I7000/i7090_defs.h new file mode 100644 index 00000000..56815369 --- /dev/null +++ b/I7000/i7090_defs.h @@ -0,0 +1,437 @@ +/* i7090_defs.h: IBM 7090 simulator definitions + + Copyright (c) 2005, 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 "sim_defs.h" /* simulator defns */ +#include "i7000_defs.h" + +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEM_ADDR_OK(x) (((uint16) (x&077777)) < MEMSIZE) +#define ReadP(x) (M[x]) +#define WriteP(x,y) if (MEM_ADDR_OK (x)) M[x] = y +extern t_uint64 M[MAXMEMSIZE]; + +/* Processor specific masks */ +#define ONEBIT 00200000000000LL +#define PMASK 00377777777777LL +#define RMASK 00000000777777LL +#define LMASK 00777777000000LL +#define AMSIGN 02000000000000LL +#define AMMASK 01777777777777LL +#define AQSIGN 01000000000000LL +#define AQMASK 00777777777777LL +#define APSIGN 00400000000000LL +#define PREMASK 00700000000000LL +#define AMASK 00000000077777LL +#define TMASK 00000000700000LL +#define DMASK 00077777000000LL +#define MSIGN 00400000000000LL +#define WMASK 00777777777777LL +#define FPCMASK 00377000000000LL +#define FPMMASK 00000777777777LL +#define FPOBIT 00001000000000LL +#define FPNBIT 00000400000000LL +#define FPMQERR 00000001000000LL /* Bit 17 */ +#define FPACERR 00000002000000LL /* Bit 16 */ +#define FPOVERR 00000004000000LL /* Bit 15 */ +#define FPSPERR 00000010000000LL /* Bit 14 */ +#define FPDPERR 00000040000000LL /* Bit 12 */ + +/* 7090 specific channel functions */ +/* Reset the channel, clear any pending device */ +void chan_rst(int chan, int type); + +/* Issue a command to a channel */ +int chan_cmd(uint16 dev, uint16 cmd); + +/* Give channel a command, any executing command is aborted */ +int chan_start(int chan, uint16 addr); + +/* Give channel a new address to start working at */ +int chan_load(int chan, uint16 addr); + +/* return the channels current command address */ +void chan_store(int chan, uint16 addr); + +/* Nop for the momement */ +void chan_store_diag(int chan, uint16 addr); + +/* Channel data handling */ +int chan_write(int chan, t_uint64 *data, int flags); +int chan_read(int chan, t_uint64 *data, int flags); + +void chan_proc(); + +extern uint16 dev_pulse[NUM_CHAN]; /* Device pulse */ +#define PUNCH_1 000001 +#define PUNCH_2 000002 +#define PUNCH_M 000003 +#define PRINT_I 000004 +#define PRINT_1 000010 +#define PRINT_2 000020 +#define PRINT_3 000040 +#define PRINT_4 000100 +#define PRINT_5 000200 +#define PRINT_6 000400 +#define PRINT_7 001000 +#define PRINT_8 002000 +#define PRINT_9 004000 +#define PRINT_10 010000 +#define PRINT_M 017770 + +/* Opcodes */ +#define OP_TXI 1 +#define OP_TIX 2 +#define OP_TXH 3 +#define OP_STR 5 +#define OP_TNX 6 +#define OP_TXL 7 + +/* Positive opcodes */ +#define OP_HTR 0000 +#define OP_TRA 0020 +#define OP_TTR 0021 +#define OP_TRCA 0022 +#define OP_TRCC 0024 +#define OP_TRCE 0026 +#define OP_TRCG 0027 +#define OP_TEFA 0030 +#define OP_TEFC 0031 +#define OP_TEFE 0032 +#define OP_TEFG 0033 +#define OP_TLQ 0040 +#define OP_IIA 0041 +#define OP_TIO 0042 +#define OP_OAI 0043 +#define OP_PAI 0044 +#define OP_TIF 0046 +#define OP_IIR 0051 +#define OP_RFT 0054 +#define OP_SIR 0055 +#define OP_RNT 0056 +#define OP_RIR 0057 +#define OP_TCOA 0060 +#define OP_TCOB 0061 +#define OP_TCOC 0062 +#define OP_TCOD 0063 +#define OP_TCOE 0064 +#define OP_TCOF 0065 +#define OP_TCOG 0066 +#define OP_TCOH 0067 +#define OP_TSX 0074 +#define OP_TZE 0100 +#define OP_TIA 0101 +#define OP_CVR 0114 +#define OP_TPL 0120 +#define OP_XCA 0131 +#define OP_TOV 0140 +#define OP_TQP 0162 +#define OP_TQO 0161 +#define OP_MPY 0200 +#define OP_VLM 0204 +#define OP_DVH 0220 +#define OP_DVP 0221 +#define OP_VDH 0224 +#define OP_VDP 0225 +#define OP_FDH 0240 +#define OP_FDP 0241 +#define OP_FMP 0260 +#define OP_DFMP 0261 +#define OP_FAD 0300 +#define OP_DFAD 0301 +#define OP_FSB 0302 +#define OP_DFSB 0303 +#define OP_FAM 0304 +#define OP_DFAM 0305 +#define OP_FSM 0306 +#define OP_DFSM 0307 +#define OP_ANS 0320 +#define OP_ERA 0322 +#define OP_CAS 0340 +#define OP_ACL 0361 +#define OP_HPR 0420 +#define OP_OSI 0442 +#define OP_ADD 0400 +#define OP_ADM 0401 +#define OP_SUB 0402 +#define OP_IIS 0440 +#define OP_LDI 0441 +#define OP_DLD 0443 +#define OP_OFT 0444 +#define OP_RIS 0445 +#define OP_ONT 0446 +#define OP_LDA 0460 /* 704 only */ +#define OP_CLA 0500 +#define OP_CLS 0502 +#define OP_ZET 0520 +#define OP_XEC 0522 +#define OP_LXA 0534 +#define OP_LAC 0535 +#define OP_RSCA 0540 +#define OP_RSCC 0541 +#define OP_RSCE 0542 +#define OP_RSCG 0543 +#define OP_STCA 0544 +#define OP_STCC 0545 +#define OP_STCE 0546 +#define OP_STCG 0547 +#define OP_LDQ 0560 +#define OP_ECA 0561 +#define OP_LRI 0562 +#define OP_ENB 0564 +#define OP_STZ 0600 +#define OP_STO 0601 +#define OP_SLW 0602 +#define OP_STI 0604 +#define OP_STA 0621 +#define OP_STD 0622 +#define OP_STT 0625 +#define OP_STP 0630 +#define OP_SXA 0634 +#define OP_SCA 0636 +#define OP_SCHA 0640 +#define OP_SCHC 0641 +#define OP_SCHE 0642 +#define OP_SCHG 0643 +#define OP_SCDA 0644 +#define OP_SCDC 0645 +#define OP_SCDE 0646 +#define OP_SCDG 0647 +#define OP_ELD 0670 +#define OP_EAD 0671 +#define OP_EDP 0672 +#define OP_EMP 0673 +#define OP_CPY 0700 /* 704 only */ +#define OP_PAX 0734 +#define OP_PAC 0737 +#define OP_PXA 0754 +#define OP_PCA 0756 +#define OP_NOP 0761 +#define OP_RDS 0762 +#define OP_LLS 0763 +#define OP_BSR 0764 +#define OP_LRS 0765 +#define OP_WRS 0766 +#define OP_ALS 0767 +#define OP_WEF 0770 +#define OP_ARS 0771 +#define OP_REW 0772 +#define OP_AXT 0774 +#define OP_DRS 0775 +#define OP_SDN 0776 + +/* Negative opcodes */ +#define OP_ESNT 04021 +#define OP_TRCB 04022 +#define OP_TRCD 04024 +#define OP_TRCF 04026 +#define OP_TRCH 04027 +#define OP_TEFB 04030 +#define OP_TEFD 04031 +#define OP_TEFF 04032 +#define OP_TEFH 04033 +#define OP_RIA 04042 +#define OP_PIA 04046 +#define OP_IIL 04051 +#define OP_LFT 04054 +#define OP_SIL 04055 +#define OP_LNT 04056 +#define OP_RIL 04057 +#define OP_TCNA 04060 +#define OP_TCNB 04061 +#define OP_TCNC 04062 +#define OP_TCND 04063 +#define OP_TCNE 04064 +#define OP_TCNF 04065 +#define OP_TCNG 04066 +#define OP_TCNH 04067 +#define OP_TNZ 04100 +#define OP_TIB 04101 +#define OP_CAQ 04114 +#define OP_TMI 04120 +#define OP_XCL 04130 +#define OP_TNO 04140 +#define OP_CRQ 04154 +#define OP_DUFA 04301 +#define OP_DUAM 04305 +#define OP_DUFS 04303 +#define OP_DUSM 04307 +#define OP_DUFM 04261 +#define OP_DFDH 04240 +#define OP_DFDP 04241 +#define OP_MPR 04200 +#define OP_UFM 04260 +#define OP_UFA 04300 +#define OP_UFS 04302 +#define OP_UAM 04304 +#define OP_USM 04306 +#define OP_ANA 04320 +#define OP_LAS 04340 +#define OP_SBM 04400 +#define OP_CAL 04500 +#define OP_ORA 04501 +#define OP_NZT 04520 +#define OP_LXD 04534 +#define OP_LDC 04535 +#define OP_RSCB 04540 +#define OP_RSCD 04541 +#define OP_RSCF 04542 +#define OP_RSCH 04543 +#define OP_STCB 04544 +#define OP_STCD 04545 +#define OP_STCF 04546 +#define OP_STCH 04547 +#define OP_ECQ 04561 +#define OP_LPI 04564 +#define OP_STQ 04600 +#define OP_SRI 04601 +#define OP_ORS 04602 +#define OP_DST 04603 +#define OP_SPI 04604 +#define OP_SLQ 04620 +#define OP_STL 04625 +#define OP_SCD 04636 +#define OP_SXD 04634 +#define OP_SCHB 04640 +#define OP_SCHD 04641 +#define OP_SCHF 04642 +#define OP_SCHH 04643 +#define OP_SCDB 04644 +#define OP_SCDD 04645 +#define OP_SCDF 04646 +#define OP_SCDH 04647 +#define OP_ESB 04671 +#define OP_EUA 04672 +#define OP_EST 04673 +#define OP_CAD 04700 /* 704 only */ +#define OP_PDX 04734 +#define OP_PDC 04737 +#define OP_PXD 04754 +#define OP_PCD 04756 +#define OP_SPOP 04761 +#define OP_LGL 04763 +#define OP_BSF 04764 +#define OP_LGR 04765 +#define OP_RUN 04772 +#define OP_RQL 04773 +#define OP_AXC 04774 +#define OP_TRS 04775 + +/* Positive 0760 opcodes */ +#define OP_CLM 000000 +#define OP_LBT 000001 +#define OP_CHS 000002 +#define OP_SSP 000003 +#define OP_ENK 000004 +#define OP_IOT 000005 +#define OP_COM 000006 +#define OP_ETM 000007 +#define OP_RND 000010 +#define OP_FRN 000011 +#define OP_DCT 000012 +#define OP_RCT 000014 +#define OP_LMTM 000016 +#define OP_RDCA 001352 +#define OP_RDCB 002352 +#define OP_RDCC 003352 +#define OP_RDCD 004352 +#define OP_RDCE 005352 +#define OP_RDCF 006352 +#define OP_RDCG 007352 +#define OP_RDCH 010352 +#define OP_RICA 001350 +#define OP_RICB 002350 +#define OP_RICC 003350 +#define OP_RICD 004350 +#define OP_RICE 005350 +#define OP_RICF 006350 +#define OP_RICG 007350 +#define OP_RICH 010350 +#define OP_SLF 000140 +#define OP_SLN1 000141 +#define OP_SLN2 000142 +#define OP_SLN3 000143 +#define OP_SLN4 000144 +#define OP_SLN5 000145 +#define OP_SLN6 000146 +#define OP_SLN7 000147 +#define OP_SLN8 000150 +#define OP_SWT1 000161 +#define OP_SWT2 000162 +#define OP_SWT3 000163 +#define OP_SWT4 000164 +#define OP_SWT5 000165 +#define OP_SWT6 000166 +#define OP_BTTA 001000 +#define OP_BTTB 002000 +#define OP_BTTC 003000 +#define OP_BTTD 004000 +#define OP_BTTE 005000 +#define OP_BTTF 006000 +#define OP_BTTG 007000 +#define OP_BTTH 010000 +#define OP_PSE 0 + +/* Negative 0760 opcodes */ +#define OP_ETTA 001000 +#define OP_ETTB 002000 +#define OP_ETTC 003000 +#define OP_ETTD 004000 +#define OP_ETTE 005000 +#define OP_ETTF 006000 +#define OP_ETTG 007000 +#define OP_ETTH 010000 +#define OP_PBT 000001 +#define OP_EFTM 000002 +#define OP_SSM 000003 +#define OP_LFTM 000004 +#define OP_ESTM 000005 +#define OP_ECTM 000006 +#define OP_LTM 000007 +#define OP_LSNM 000010 +#define OP_ETT 000011 +#define OP_RTT 000012 +#define OP_EMTM 000016 +#define OP_SLT1 000141 +#define OP_SLT2 000142 +#define OP_SLT3 000143 +#define OP_SLT4 000144 +#define OP_SLT5 000145 +#define OP_SLT6 000146 +#define OP_SLT7 000147 +#define OP_SLT8 000150 +#define OP_SWT7 000161 +#define OP_SWT8 000162 +#define OP_SWT9 000163 +#define OP_SWT10 000164 +#define OP_SWT11 000165 +#define OP_SWT12 000166 +#define OP_MSE 0 + +/* Special Ops -0761 */ +#define OP_SEA 000041 +#define OP_SEB 000042 +#define OP_IFT 000043 +#define OP_EFT 000044 +#define OP_ESM 000140 +#define OP_TSM 000141 + diff --git a/I7000/i7090_drum.c b/I7000/i7090_drum.c new file mode 100644 index 00000000..a80a972e --- /dev/null +++ b/I7000/i7090_drum.c @@ -0,0 +1,287 @@ +/* i7090_drum.c: IBM 7090 Drum + + 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. + + This supports the direct channel and 704 type drums. + +*/ + +#include "i7090_defs.h" + +#ifdef NUM_DEVS_DR +#define UNIT_DRM UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | \ + UNIT_BUFABLE | UNIT_MUSTBUF + +/* Device status information stored in u5 */ +#define DRMSTA_READ 000001 /* Unit is in read */ +#define DRMSTA_WRITE 000002 /* Unit is in write */ +#define DRMSTA_CMD 000004 /* Unit has recieved a cmd */ +#define DRMSTA_UNIT 000170 /* Unit mask */ +#define DRMSTA_UNITSHIFT 3 +#define DRMSTA_START 000200 /* Drum has started to transfer */ +#define DRMWORDTIME us_to_ticks(96) /* Number of cycles per drum word */ +#define DRMSIZE 2048 /* Number words per drum */ +#define DRMMASK (DRMSIZE-1)/* Mask of drum address */ + +uint32 drm_cmd(UNIT *, uint16, uint16); +t_stat drm_srv(UNIT *); +t_stat drm_boot(int32, DEVICE *); +void drm_ini(UNIT *, t_bool); +t_stat drm_reset(DEVICE *); +extern t_stat chan_boot(int32, DEVICE *); +uint32 drum_addr; /* Read/write drum address */ +t_stat set_units(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat drm_attach(UNIT * uptr, CONST char *file); +t_stat drm_detach(UNIT * uptr); + +t_stat get_units(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +t_stat drm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *drm_description (DEVICE *dptr); + + +UNIT drm_unit[] = { + {UDATA(&drm_srv, UNIT_S_CHAN(0) | UNIT_DRM, NUM_UNITS_DR * DRMSIZE), 0, + NUM_UNITS_DR}, +}; + +MTAB drm_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "UNITS", "UNITS", &set_units, + &get_units, NULL}, +#if NUM_CHAN != 1 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, &get_chan, + NULL}, +#endif + {0} +}; + +DEVICE drm_dev = { + "DR", drm_unit, NULL /* Registers */ , drm_mod, + NUM_DEVS_DR, 8, 15, 1, 8, 36, + NULL, NULL, &drm_reset, &drm_boot, &drm_attach, &drm_detach, + &drm_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &drm_help, NULL, NULL, &drm_description +}; + +uint32 drm_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = dev; + + u -= drm_dib.addr; + if (u > uptr->u3) + return SCPE_NODEV; + if ((uptr->flags & UNIT_ATT) != 0) { + switch (cmd) { + case IO_RDS: + /* Start device */ + uptr->u5 = DRMSTA_READ | DRMSTA_CMD; + sim_debug(DEBUG_CMD, &drm_dev, "RDS %o\n", dev); + chan_set_sel(chan, 0); + break; + case IO_WRS: + /* Start device */ + uptr->u5 = DRMSTA_WRITE | DRMSTA_CMD; + sim_debug(DEBUG_CMD, &drm_dev, "WRS %o\n", dev); + chan_set_sel(chan, 1); + break; + default: + return SCPE_IOERR; + } + /* Choose which part to use */ + uptr->u5 |= u << DRMSTA_UNITSHIFT; + drum_addr = 0; /* Set drum address */ + chan_clear(chan, CHS_ATTN); /* Clear attentions */ + /* Make sure drum is spinning */ + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + return SCPE_IOERR; +} + +t_stat drm_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + t_uint64 *buf = (t_uint64*)uptr->filebuf; + t_stat r; + + uptr->u6++; /* Adjust rotation */ + uptr->u6 &= DRMMASK; + /* Channel has disconnected, abort current read. */ + if (uptr->u5 & DRMSTA_CMD && chan_stat(chan, DEV_DISCO)) { + uptr->u5 = 0; + chan_clear(chan, DEV_WEOR | DEV_SEL | STA_ACTIVE); + sim_debug(DEBUG_CHAN, &drm_dev, "Disconnect\n"); + } + + /* Check if we have a address match */ + if ((chan_flags[chan] & (STA_ACTIVE | DEV_SEL)) == (STA_ACTIVE | DEV_SEL) + && (uptr->u5 & (DRMSTA_READ | DRMSTA_WRITE)) + && (uint32)uptr->u6 == (drum_addr & DRMMASK)) { + uint32 addr = + (((uptr->u5 & DRMSTA_UNIT) >> DRMSTA_UNITSHIFT) << 11) + + (drum_addr & DRMMASK); + + /* Try and transfer a word of data */ + if (uptr->u5 & DRMSTA_READ) { + r = chan_write(chan, &buf[addr], DEV_DISCO); + } else { + if (addr >= uptr->hwmark) + uptr->hwmark = (uint32)addr + 1; + r = chan_read(chan, &buf[addr], DEV_DISCO); + } + switch (r) { + case DATA_OK: + sim_debug(DEBUG_DATA, &drm_dev, "loc %6o data %012llo\n", addr, + buf[addr]); + addr++; + addr &= DRMMASK; + drum_addr &= ~DRMMASK; + drum_addr |= addr; + break; + + case END_RECORD: + case TIME_ERROR: + /* If no data, disconnect */ + sim_debug(DEBUG_DATA, &drm_dev, "loc %6o missed\n", addr); + chan_clear(chan, STA_ACTIVE | DEV_SEL); + uptr->u5 = DRMSTA_CMD; + break; + } + } + /* Increase delay for index time */ + if (uptr->u6 == 0) + sim_activate(uptr, us_to_ticks(120)); + else + sim_activate(uptr, DRMWORDTIME); + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +drm_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + t_uint64 *buf = (t_uint64*)uptr->filebuf; + int addr; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ +/* Init for a read */ + if (drm_cmd(uptr, IO_RDS, 0301) != SCPE_OK) + return STOP_IONRDY; +/* Copy first three records. */ + addr = 0; + M[0] = buf[addr++]; + M[1] = buf[addr++]; + drum_addr = 2; + return chan_boot(unit_num, dptr); +} + +void +drm_ini(UNIT * uptr, t_bool f) +{ + uptr->u5 = 0; +} + +t_stat +drm_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +/* Sets the number of drum units */ +t_stat +set_units(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 > NUM_UNITS_DR) + return SCPE_ARG; + uptr->capac = i * 2048; + uptr->u3 = i; + return SCPE_OK; +} + +t_stat +get_units(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "Units=%d", uptr->u3); + return SCPE_OK; +} + +t_stat +drm_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + return SCPE_OK; +} + +t_stat +drm_detach(UNIT * uptr) +{ + sim_cancel(uptr); + return detach_unit(uptr); +} + +t_stat +drm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + const char *cpu = cpu_description(&cpu_dev); + DIB *dibp = (DIB *) dptr->ctxt; + int ctype = dibp->ctype; + + fprintf (st, "%s\n\n", drm_description(dptr)); + fprintf (st, "Up to %d units of drum could be used\n", NUM_UNITS_DR); + fprintf (st, " sim> set %s UNITS=n to set number of units\n", dptr->name); + help_set_chan_type(st, dptr, "Drums"); + fprintf (st, "Drums could be booted\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + + return SCPE_OK; +} + +const char * +drm_description (DEVICE *dptr) +{ + return "IBM 704/709 Drum"; +} + +#endif + diff --git a/I7000/i7090_hdrum.c b/I7000/i7090_hdrum.c new file mode 100644 index 00000000..f52461b9 --- /dev/null +++ b/I7000/i7090_hdrum.c @@ -0,0 +1,253 @@ +/* i7090_drum.c: IBM 7320A High Speed Drum + + 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. + + High speed drum for CTSS + +*/ + +#include "i7090_defs.h" + +#ifdef NUM_DEVS_HD +#define UNIT_DRM UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | \ + UNIT_BUFABLE | UNIT_MUSTBUF + +/* Device status information stored in u5 */ +#define DRMSTA_READ 000001 /* Unit is in read */ +#define DRMSTA_WRITE 000002 /* Unit is in write */ +#define DRMSTA_START 000004 /* Which half of drum accessing */ +#define DRMSTA_CMD 000010 /* Unit has recieved a cmd */ +#define DRMSTA_UNIT 000700 /* Unitmask */ +#define DRMSTA_SHFT 6 + +uint32 hsdrm_cmd(UNIT *, uint16, uint16); +t_stat hsdrm_srv(UNIT *); +void hsdrm_ini(UNIT *, t_bool); +t_stat hsdrm_reset(DEVICE *); +t_uint64 hsdrm_addr; /* Read/write drum address */ +t_stat set_hunits(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat get_hunits(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +t_stat hsdrm_attach(UNIT * uptr, CONST char *file); +t_stat hsdrm_detach(UNIT * uptr); +t_stat hsdrm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *hsdrm_description (DEVICE *dptr); + + +UNIT hsdrm_unit[] = { + {UDATA (&hsdrm_srv, UNIT_S_CHAN(7) | UNIT_DRM, + NUM_UNITS_HD * 8 * 32767), 0, NUM_UNITS_HD}, +}; + +MTAB hsdrm_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "UNITS", "UNITS", + &set_hunits, &get_hunits, NULL}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", + &set_chan, &get_chan, NULL}, + {0} +}; + +DEVICE hsdrm_dev = { + "HD", hsdrm_unit, NULL /* Registers */ , hsdrm_mod, + NUM_DEVS_HD, 8, 15, 1, 8, 36, + NULL, NULL, &hsdrm_reset, NULL, &hsdrm_attach, &hsdrm_detach, + &hsdrm_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &hsdrm_help, NULL, NULL, &hsdrm_description +}; + +uint32 hsdrm_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + + if ((uptr->flags & UNIT_ATT) != 0) { + /* Delay if transfer still in progress. */ + if (chan_active(chan)) + return SCPE_BUSY; + /* Wait for device to time out */ + if (uptr->u5 & DRMSTA_CMD) + return SCPE_BUSY; + switch (cmd) { + case IO_RDS: + /* Start device */ + uptr->u5 = DRMSTA_READ | DRMSTA_CMD; + chan_set_sel(chan, 0); + sim_debug(DEBUG_CMD, &hsdrm_dev, "RDS dev %o\n", dev); + break; + case IO_WRS: + /* Start device */ + uptr->hwmark = uptr->capac; /* Mark as changed */ + uptr->u5 = DRMSTA_WRITE | DRMSTA_CMD; + chan_set_sel(chan, 1); + sim_debug(DEBUG_CMD, &hsdrm_dev, "WRS dev %o\n", dev); + break; + default: + return SCPE_IOERR; + } + hsdrm_addr = 0; /* Set drum address */ + if (!sim_is_active(uptr)) + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + return SCPE_IOERR; +} + +t_stat hsdrm_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + t_uint64 *buf = (t_uint64 *)uptr->filebuf; + t_stat r; + + /* Channel has disconnected, abort current read. */ + if (uptr->u5 & DRMSTA_CMD && chan_stat(chan, DEV_DISCO)) { + uptr->u5 = 0; + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &hsdrm_dev, "disconnecting\n"); + sim_activate(uptr, us_to_ticks(50)); + } + + uptr->u6++; /* Adjust rotation */ + uptr->u6 &= 007777; + + /* Check if we have a address match */ + if ((chan_flags[chan] & (STA_ACTIVE | DEV_SEL)) == (STA_ACTIVE | DEV_SEL) + && uptr->u5 & (DRMSTA_READ | DRMSTA_WRITE) + && (uint32)uptr->u6 == (hsdrm_addr & 007777)) { + int addr = + ((hsdrm_addr >> 12) & 07000000) | + ((hsdrm_addr >> 3) & 0700000) | + (hsdrm_addr & 077777); + sim_debug(DEBUG_DETAIL, &hsdrm_dev, "drum addr %o\n\r", addr); + if (((addr >> 18) & 07) > uptr->u3) { + chan_set(chan, DEV_REOR | CHS_ATTN | CHS_ERR); + goto next; + } + /* Flag to disconnect without setting iocheck */ + if (uptr->u5 & DRMSTA_READ) + r = chan_write(chan, &buf[addr], DEV_DISCO); + else + r = chan_read(chan, &buf[addr], DEV_DISCO); + switch (r) { + case DATA_OK: + sim_debug(DEBUG_DATA, &hsdrm_dev, + "transfer %s %o: %012llo\n\r", + (uptr->u5 & DRMSTA_READ) ? "read" : "write", + addr, buf[addr]); + hsdrm_addr++; + hsdrm_addr &= 070007077777LL; + if ((hsdrm_addr & (2048 - 1)) == 0) + chan_set(chan, DEV_REOR); + break; + + case END_RECORD: + case TIME_ERROR: + uptr->u5 = DRMSTA_CMD; + break; + } + } + next: + sim_activate(uptr, us_to_ticks(20)); + return SCPE_OK; +} + +void +hsdrm_ini(UNIT * uptr, t_bool f) +{ + uptr->u5 = 0; +} + +t_stat +hsdrm_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +/* Sets the number of drum units */ +t_stat +set_hunits(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 > NUM_UNITS_HD) + return SCPE_ARG; + uptr->capac = i * 32767 * 8; + uptr->u3 = i; + return SCPE_OK; +} + +t_stat +get_hunits(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "Units=%d", uptr->u3); + return SCPE_OK; +} + +t_stat +hsdrm_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; +} + +t_stat +hsdrm_detach(UNIT * uptr) +{ + sim_cancel(uptr); + return detach_unit(uptr); +} + +t_stat +hsdrm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s\n\n", hsdrm_description(dptr)); + fprintf (st, "The High speed drum supports up to %d units of storage\n", NUM_UNITS_HD); + fprintf (st, "Each unit held 265k words of data\n"); + help_set_chan_type(st, dptr, "High speed drum"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + + +const char * +hsdrm_description (DEVICE *dptr) +{ + return "IBM 7320A Drum for CTSS"; +} + +#endif + diff --git a/I7000/i7090_lpr.c b/I7000/i7090_lpr.c new file mode 100644 index 00000000..28969c71 --- /dev/null +++ b/I7000/i7090_lpr.c @@ -0,0 +1,696 @@ +/* i7090_lpr.c: IBM 7090 Standard line printer. + + 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. + + This is the standard line printer that all 70xx systems have. + + For WRS read next 24 words and fill print buffer. + Row 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 11, 12 + For RDS read rows 9, 8, 7, 6, 5, 4, 3, 2, 1, + Echo 8|4 + read row 10 + Echo 8|3 + read row 11 + Echo 9 + read row 12 + Echo 8, 7, 6, 5, 4, 3, 2, 1 + +*/ + +#include "i7090_defs.h" +#include "sim_console.h" +#include "sim_card.h" + +#ifdef NUM_DEVS_LPR + +#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE +#define ECHO (1 << UNIT_V_LOCAL) + + +/* std devices. data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +/* Output selection is stored in u3 */ +/* Line count is stored in u4 */ +/* Device status information stored in u5 */ +/* Position is stored in u6 */ +#define LPRSTA_RCMD 002000 /* Read command */ +#define LPRSTA_WCMD 004000 /* Write command */ + +#define LPRSTA_EOR 010000 /* Hit end of record */ +#define LPRSTA_BINMODE 020000 /* Line printer started in bin mode */ +#define LPRSTA_CHANGE 040000 /* Turn DEV_WRITE on */ +#define LPRSTA_COL72 0100000 /* Mask to last column printed */ +#define LPRSTA_IMAGE 0200000 /* Image to print */ + + +struct _lpr_data +{ + t_uint64 wbuff[24]; /* Line buffer */ + char lbuff[74]; /* Output line buffer */ +} +lpr_data[NUM_DEVS_LPR]; + +uint32 lpr_cmd(UNIT *, uint16, uint16); +t_stat lpr_srv(UNIT *); +void lpr_ini(UNIT *, t_bool); +t_stat lpr_reset(DEVICE *); +t_stat lpr_attach(UNIT *, CONST char *); +t_stat lpr_detach(UNIT *); +t_stat lpr_setlpp(UNIT *, int32, CONST char *, void *); +t_stat lpr_getlpp(FILE *, UNIT *, int32, CONST void *); +t_stat lpr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *lpr_description (DEVICE *dptr); + +extern char six_to_ascii[64]; + +UNIT lpr_unit[] = { +#if NUM_DEVS_LPR > 1 + {UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_A) | UNIT_LPR | ECHO, 55)}, /* A */ +#endif +#if NUM_DEVS_LPR > 2 + {UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_C) | UNIT_LPR, 55)}, /* B */ +#endif +#if NUM_DEVS_LPR > 3 + {UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_E) | UNIT_LPR | UNIT_DIS, 55)}, /* C */ +#endif + {UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_CHPIO) | UNIT_LPR, 55)}, /* 704 */ +}; + +MTAB lpr_mod[] = { + {ECHO, 0, NULL, "NOECHO", NULL, NULL, NULL, "Done echo to console"}, + {ECHO, ECHO, "ECHO", "ECHO", NULL, NULL, NULL, "Echo output to console"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"}, +#if NUM_CHAN != 1 + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan, + &get_chan, NULL}, +#endif + {0} +}; + +DEVICE lpr_dev = { + "LP", lpr_unit, NULL, lpr_mod, + NUM_DEVS_LPR, 8, 15, 1, 8, 36, + NULL, NULL, &lpr_reset, NULL, &lpr_attach, &lpr_detach, + &lpr_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lpr_help, NULL, NULL, &lpr_description +}; + +/* Line printer routines +*/ + +/* + * Line printer routines + */ + +t_stat +lpr_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = 0; + while(*cptr != '\0') { + if (*cptr < '0' || *cptr > '9') + return SCPE_ARG; + i = (i * 10) + (*cptr++) - '0'; + } + if (i < 20 || i > 100) + return SCPE_ARG; + uptr->capac = i; + uptr->u4 = 0; + return SCPE_OK; +} + +t_stat +lpr_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "linesperpage=%d", uptr->capac); + return SCPE_OK; +} + +t_stat +print_line(UNIT * uptr, int chan, int unit) +{ +/* Convert word record into column image */ +/* Check output type, if auto or text, try and convert record to bcd first */ +/* If failed and text report error and dump what we have */ +/* Else if binary or not convertable, dump as image */ + + uint16 buff[80]; /* Temp conversion buffer */ + int i, j; + int outsel = uptr->u3; + int prt_flg = 1; + + if ((uptr->flags & (UNIT_ATT | ECHO)) == 0) + return SCPE_UNATT; /* attached? */ + + if (outsel & PRINT_3) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + } + uptr->u5 &= ~LPRSTA_COL72; + uptr->u4++; + } + + if (outsel & PRINT_4) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n\r\n", 1, 4, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + sim_putchar('\r'); + sim_putchar('\n'); + } + uptr->u5 &= ~LPRSTA_COL72; + uptr->u4++; + uptr->u4++; + } + + + /* Try to convert to text */ + memset(buff, 0, sizeof(buff)); + /* Bit flip into temp buffer */ + for (i = 0; i < 24; i++) { + int bit = 1 << (i / 2); + t_uint64 mask = 1; + t_uint64 wd = 0; + int b = 36 * (i & 1); + int col; + + wd = lpr_data[unit].wbuff[i]; + for (col = 35; col >= 0; mask <<= 1, col--) { + if (wd & mask) + buff[col + b] |= bit; + } + lpr_data[unit].wbuff[i] = 0; + } + + /* Space out printer based on last output */ + if ((outsel & PRINT_9)) { + /* Trim trailing spaces */ + for (j = 72; j > 0 && lpr_data[unit].lbuff[j] == ' '; j--) ; + j++; + if ((uptr->u5 & LPRSTA_COL72) == 0) + j = 0; + + for (i = j; i < 72; i++) { + if (uptr->flags & UNIT_ATT) + sim_fwrite(" ", 1, 1, uptr->fileref); + if (uptr->flags & ECHO) + sim_putchar(' '); + } + } else { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\n\r", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\n'); + sim_putchar('\r'); + } + uptr->u4++; + uptr->u5 &= ~LPRSTA_COL72; + } + + /* Scan each column */ + for (i = 0; i < 72; i++) { + int bcd = sim_hol_to_bcd(buff[i]); + + if (bcd == 0x7f) + lpr_data[unit].lbuff[i] = '{'; + else { + if (bcd == 020) + bcd = 10; + if (uptr->u5 & LPRSTA_BINMODE) { + char ch = (buff[i] != 0) ? '1' : ' '; + lpr_data[unit].lbuff[i] = ch; + } else + lpr_data[unit].lbuff[i] = sim_six_to_ascii[bcd]; + } + } + sim_debug(DEBUG_DETAIL, &lpr_dev, "WRS unit=%d %3o [%72s]\n", unit, + outsel >> 3, &lpr_data[unit].lbuff[0]); + + /* Trim trailing spaces */ + for (j = 71; j > 0 && lpr_data[unit].lbuff[j] == ' '; j--) ; + + /* Print out buffer */ + if (uptr->flags & UNIT_ATT) + sim_fwrite(lpr_data[unit].lbuff, 1, j+1, uptr->fileref); + if (uptr->flags & ECHO) { + for(i = 0; i <= j; i++) + sim_putchar(lpr_data[unit].lbuff[i]); + } + uptr->u5 |= LPRSTA_COL72; + + /* Put output to column where we left off */ + if (outsel != 0) { + uptr->u5 &= ~LPRSTA_COL72; + } + + /* Space printer */ + if (outsel & PRINT_2) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + } + uptr->u4++; + } + + if (outsel & PRINT_1) { + while (uptr->u4 < (int32)uptr->capac) { + if (uptr->flags & UNIT_ATT) + sim_fwrite("\r\n", 1, 2, uptr->fileref); + if (uptr->flags & ECHO) { + sim_putchar('\r'); + sim_putchar('\n'); + } + uptr->u4++; + } + } + + if (uptr->u4 >= (int32)uptr->capac) { + uptr->u4 -= (int32)uptr->capac; + dev_pulse[chan] |= PRINT_I; + } + + return SCPE_OK; +} + +uint32 lpr_cmd(UNIT * uptr, uint16 cmd, uint16 dev) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - lpr_unit); + int i; + + /* Check if valid */ + if ((dev & 03) == 0 || (dev & 03) == 3) + return SCPE_NODEV; + /* Check if attached */ + if ((uptr->flags & (UNIT_ATT | ECHO)) == 0) { + chan_set_error(chan); + sim_debug(DEBUG_EXP, &lpr_dev, "unit=%d not ready\n", u); + return SCPE_IOERR; + } + /* Check if still active */ + if (uptr->u5 & URCSTA_CMD) { + sim_debug(DEBUG_EXP, &lpr_dev, "unit=%d busy\n", u); + return SCPE_BUSY; + } + /* Ok, issue command if correct */ + if (cmd == IO_WRS || cmd == IO_RDS) { + /* Start device */ + if (((uptr->u5 & (URCSTA_ON | URCSTA_IDLE)) == + (URCSTA_ON | URCSTA_IDLE)) && uptr->wait <= 30) { + uptr->wait += 85; /* Wait for next latch point */ + } else + uptr->wait = 330; /* Startup delay */ + for (i = 0; i < 24; lpr_data[u].wbuff[i++] = 0) ; + uptr->u6 = 0; + uptr->u5 &= ~(LPRSTA_WCMD | LPRSTA_RCMD | URCSTA_WRITE | URCSTA_READ); + uptr->u3 = 0; + dev_pulse[chan] = 0; + if (cmd == IO_WRS) { + sim_debug(DEBUG_CMD, &lpr_dev, "WRS %o unit=%d %d\n", dev, u, uptr->wait); + uptr->u5 |= LPRSTA_WCMD | URCSTA_CMD | URCSTA_WRITE; + } else { + sim_debug(DEBUG_CMD, &lpr_dev, "RDS %o unit=%d %d\n", dev, u, uptr->wait); + uptr->u5 |= LPRSTA_RCMD | URCSTA_CMD | URCSTA_READ; + } + if ((dev & 03) == 2) + uptr->u5 |= LPRSTA_BINMODE; + else + uptr->u5 &= ~LPRSTA_BINMODE; + chan_set_sel(chan, 1); + chan_clear_status(chan); + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + return SCPE_OK; + } else { + chan_set_attn(chan); + } + return SCPE_IOERR; +} + +t_stat lpr_srv(UNIT * uptr) +{ + int chan = UNIT_G_CHAN(uptr->flags); + int u = (uptr - lpr_unit); + int pos; + int r; + int eor = 0; + + /* Channel has disconnected, abort current line. */ + if (uptr->u5 & URCSTA_CMD && chan_stat(chan, DEV_DISCO)) { + print_line(uptr, chan, u); + uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ | URCSTA_CMD | LPRSTA_EOR | LPRSTA_CHANGE); + uptr->u6 = 0; + chan_clear(chan, DEV_WEOR | DEV_SEL); + sim_debug(DEBUG_CHAN, &lpr_dev, "unit=%d disconnect\n", u); + return SCPE_OK; + } + + /* If change requested, do that first */ + if (uptr->u5 & LPRSTA_CHANGE) { + /* Wait until word read by CPU or timeout */ + if (chan_test(chan, DEV_FULL)) { + uptr->wait -= 50; + if (uptr->wait == 50) + uptr->u5 &= ~LPRSTA_CHANGE; + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } else { + chan_set(chan, DEV_WRITE); + sim_activate(uptr, uptr->wait); + uptr->u5 &= ~LPRSTA_CHANGE; + uptr->wait = 0; + return SCPE_OK; + } + } + + /* Check to see if we have timed out */ + if (uptr->wait != 0) { + uptr->wait--; + /* If at end of record and channel is still active, do another print */ + if (((uptr->u5 & (URCSTA_IDLE|URCSTA_CMD|URCSTA_WRITE|URCSTA_READ| + URCSTA_ON)) == (URCSTA_IDLE|URCSTA_CMD|URCSTA_ON)) + && uptr->wait == 1 && chan_test(chan, STA_ACTIVE)) { + /* Restart same command */ + uptr->u5 |= (URCSTA_WRITE | URCSTA_READ) & (uptr->u5 >> 5); + uptr->u6 = 0; + chan_set(chan, DEV_WRITE); + sim_debug(DEBUG_CHAN, &lpr_dev, "unit=%d restarting\n", u); + } + sim_activate(uptr, us_to_ticks(1000)); /* activate */ + return SCPE_OK; + } + + /* If no request, go to idle mode */ + if ((uptr->u5 & (URCSTA_READ | URCSTA_WRITE)) == 0) { + if ((uptr->u5 & (URCSTA_IDLE | URCSTA_ON)) == (URCSTA_IDLE | URCSTA_ON)) { + uptr->wait = 85; /* Delay 85ms */ + uptr->u5 &= ~URCSTA_IDLE; /* Not running */ + sim_activate(uptr, us_to_ticks(1000)); + } else { + uptr->wait = 330; /* Delay 330ms */ + uptr->u5 &= ~URCSTA_ON; /* Turn motor off */ + } + return SCPE_OK; + } + + /* Motor is on and up to speed */ + uptr->u5 |= URCSTA_ON; + uptr->u5 &= ~URCSTA_IDLE; + pos = uptr->u6; + + uptr->u3 |= dev_pulse[chan] & PRINT_M; + + /* Check if he write out last data */ + if (uptr->u5 & URCSTA_READ) { + int wrow = 0; + t_uint64 wd = 0; + int action = 0; + + /* Case 0: Read word from MF memory, DEV_WRITE=1 */ + /* Case 1: Read word from MF memory, write echo back */ + /* Case 2: Write echoback, after gone switch to read */ + /* Case 3: Write echoback */ + /* Case 4: No update, DEV_WRITE=1 */ + eor = (uptr->u5 & LPRSTA_BINMODE) ? 1 : 0; + switch (pos) { + case 46: + print_line(uptr, chan, u); + pos = 0; + /* Fall through */ + case 0: + case 1: /* Row 9 */ + case 2: + case 3: /* Row 8 */ + case 4: + case 5: /* Row 7 */ + case 6: + case 7: /* Row 6 */ + case 8: + case 9: /* Row 5 */ + case 10: + case 11: /* Row 4 */ + case 12: + case 13: /* Row 3 */ + case 14: + case 15: /* Row 2 */ + case 16: /* Row 1R */ + wrow = pos; + break; + case 17: /* Row 1L and start Echo */ + wrow = pos; + action = 1; + break; + case 18: /* Echo 8-4 R */ + wd = lpr_data[u].wbuff[2]; + wd &= lpr_data[u].wbuff[10]; + action = 2; + wrow = pos; + break; + case 19: /* Echo 8-4 L */ + wd = lpr_data[u].wbuff[3]; + wd &= lpr_data[u].wbuff[11]; + action = 3; + wrow = pos; + break; + case 20: /* Row 10 R */ + wrow = 18; + break; + case 21: /* Row 10 L */ + wrow = 19; + action = 1; + break; + case 22: /* Echo 8-3 */ + /* Fill for echo back */ + wd = lpr_data[u].wbuff[12]; + wd &= lpr_data[u].wbuff[2]; + action = 2; + wrow = pos; + break; + case 23: + wd = lpr_data[u].wbuff[13]; + wd &= lpr_data[u].wbuff[3]; + action = 3; + wrow = pos; + break; + case 24: /* Row 11 R */ + wrow = 20; + break; + case 25: /* Row 11 L */ + wrow = 21; + action = 1; + break; + case 26: /* Echo 9 */ + wd = lpr_data[u].wbuff[0]; + action = 2; + wrow = pos; + break; + case 27: + wd = lpr_data[u].wbuff[1]; + action = 3; + wrow = pos; + break; + case 28: + wrow = 22; + break; + case 29: /* Row 12 */ + wrow = 23; + action = 1; + break; + case 45: /* Echo 1 */ + eor = 1; + /* Fall through */ + + case 30: + case 31: /* Echo 8 */ + case 32: + case 33: /* Echo 7 */ + case 34: + case 35: /* Echo 6 */ + case 36: + case 37: /* Echo 5 */ + case 38: + case 39: /* Echo 4 */ + case 40: + case 41: /* Echo 3 */ + case 42: + case 43: /* Echo 2 */ + case 44: /* Echo 1 */ + wrow = pos - 28; + wd = lpr_data[u].wbuff[wrow]; + action = 2; + break; + } + + if (action == 0 || action == 1) { + /* If reading grab next word */ + r = chan_read(chan, &lpr_data[u].wbuff[wrow], 0); + sim_debug(DEBUG_DATA, &lpr_dev, "print read row < %d %d %012llo eor=%d\n", + pos, wrow, lpr_data[u].wbuff[wrow], 0); + if (action == 1) + chan_clear(chan, DEV_WRITE); + } else { /* action == 2 || action == 3 */ + /* Place echo data in buffer */ + sim_debug(DEBUG_DATA, &lpr_dev, "print read row > %d %d %012llo eor=%d\n", + pos, wrow, wd, eor); + r = chan_write(chan, &wd, 0); + /* Change back to reading */ + if (action == 3) { + uptr->wait = 650; + uptr->u6 = ++pos; + uptr->u5 &= ~(LPRSTA_EOR); + uptr->u5 |= LPRSTA_CHANGE; + sim_activate(uptr, us_to_ticks(100)); + return SCPE_OK; + } + } + } else { + eor = (pos == 23 || (uptr->u5 & LPRSTA_BINMODE && pos == 1)) ? 1 : 0; + if (pos == 24 || (uptr->u5 & LPRSTA_BINMODE && pos == 2)) { + print_line(uptr, chan, u); + pos = 0; + } + r = chan_read(chan, &lpr_data[u].wbuff[pos], 0); + sim_debug(DEBUG_DATA, &lpr_dev, "print row %d %012llo %d\n", pos, + lpr_data[u].wbuff[pos], eor); + } + + uptr->u6 = pos + 1; + switch (r) { + case END_RECORD: + uptr->wait = 100; /* Print wheel gap */ + uptr->u5 |= LPRSTA_EOR | URCSTA_IDLE; + uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ); + chan_set(chan, DEV_REOR); + break; + case DATA_OK: + if (eor) { + uptr->wait = 100; /* Print wheel gap */ + uptr->u5 |= LPRSTA_EOR | URCSTA_IDLE; + uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ); + chan_set(chan, DEV_REOR); + } else { + uptr->wait = 0; + uptr->u5 &= ~(LPRSTA_EOR); + sim_activate(uptr, (pos & 1) ? us_to_ticks(500) : us_to_ticks(16000)); + return SCPE_OK; + } + break; + case TIME_ERROR: + chan_set_attn(chan); + chan_set(chan, DEV_REOR); + uptr->wait = 13 * (12 - (pos / 2)) + 85; + uptr->u5 &= ~(URCSTA_READ | URCSTA_WRITE); + uptr->u5 |= URCSTA_IDLE; + break; + } + + sim_activate(uptr, us_to_ticks(1000)); + return SCPE_OK; +} + +void +lpr_ini(UNIT * uptr, t_bool f) +{ + int u = (uptr - lpr_unit); + + uptr->u3 = 0; + uptr->u4 = 0; + uptr->u5 = 0; + memset(&lpr_data[u].lbuff, ' ', sizeof(lpr_data[u].lbuff)); +} + +t_stat +lpr_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +t_stat +lpr_attach(UNIT * uptr, CONST char *file) +{ + t_stat r; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + return SCPE_OK; +} + +t_stat +lpr_detach(UNIT * uptr) +{ + int u = (uptr - lpr_unit); + + return detach_unit(uptr); +} + +t_stat +lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + const char *cpu = cpu_description(&cpu_dev); + extern void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent); + + fprintf (st, "%s\n\n", lpr_description(dptr)); +#if NUM_DEVS_LPR > 3 + fprintf (st, "The %s supports up to four line printers", cpu); +#elif NUM_DEVS_LPR > 2 + fprintf (st, "The %s supports up to three line printers", cpu); +#elif NUM_DEVS_LPR > 1 + fprintf (st, "The %s supports up to two line printers", cpu); +#elif NUM_DEVS_LPR > 0 + fprintf (st, "The %s supports one line printer", cpu); +#endif + fprintf (st, "by default. The Line printer can\n"); + fprintf (st, "The printer acted as the console printer:\n\n"); + fprintf (st, " sim> SET %s ECHO\n\n", dptr->name); + fprintf (st, "Causes all output sent to printer to also go to console.\n"); + help_set_chan_type(st, dptr, "Line printers"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +lpr_description(DEVICE *dptr) +{ + return "716 Line Printer"; +} + + +#endif diff --git a/I7000/i7090_sys.c b/I7000/i7090_sys.c new file mode 100644 index 00000000..f3e30fd9 --- /dev/null +++ b/I7000/i7090_sys.c @@ -0,0 +1,1056 @@ +/* i7090_sys.c: IBM 7090 Simulator system interface. + + 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 "i7090_defs.h" +#include "sim_card.h" +#include + +t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +#ifdef I704 +char sim_name[] = "IBM 704"; +#else +char sim_name[] = "IBM 7090"; +#endif + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#ifdef CPANEL + &cp_dev, +#endif +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#if NUM_DEVS_MT > 0 + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#if NUM_DEVS_MT > 2 + &mtc_dev, +#if NUM_DEVS_MT > 3 + &mtd_dev, +#if NUM_DEVS_MT > 4 + &mte_dev, +#if NUM_DEVS_MT > 5 + &mtf_dev, +#endif /* 5 */ +#endif /* 4 */ +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ +#ifdef MT_CHANNEL_ZERO + &mtz_dev, +#endif +#if NUM_DEVS_HT > 0 + &hta_dev, +#if NUM_DEVS_HT > 1 + &htb_dev, +#endif +#endif +#ifdef NUM_DEVS_HD + &hsdrm_dev, +#endif +#ifdef NUM_DEVS_DR + &drm_dev, +#endif +#ifdef NUM_DEVS_DSK + &dsk_dev, +#endif +#ifdef NUM_DEVS_COM + &coml_dev, + &com_dev, +#endif +#ifdef NUM_DEVS_CHRON + &chron_dev, +#endif + NULL +}; + +/* Device addressing words */ +#ifdef NUM_DEVS_DR +DIB drm_dib = { CH_TYP_PIO, 1, 0301, 0740, &drm_cmd, &drm_ini }; +#endif +#ifdef NUM_DEVS_CDP +DIB cdp_dib = { CH_TYP_PIO|CH_TYP_76XX, 1, 0341, 0777, &cdp_cmd, &cdp_ini }; +#endif +#ifdef NUM_DEVS_CDR +DIB cdr_dib = { CH_TYP_PIO|CH_TYP_76XX, 1, 0321, 0777, &cdr_cmd, NULL }; +#endif +#ifdef NUM_DEVS_LPR +DIB lpr_dib = { CH_TYP_PIO|CH_TYP_76XX, 1, 0361, 0774, &lpr_cmd, &lpr_ini }; +#endif +#if (NUM_DEVS_MT > 0) || defined(MT_CHANNEL_ZERO) +DIB mt_dib = { CH_TYP_PIO|CH_TYP_76XX, NUM_UNITS_MT, 0200, 0740, &mt_cmd, + &mt_ini }; +#endif +#ifdef NUM_DEVS_CHRON +DIB chron_dib = { CH_TYP_PIO|CH_TYP_76XX, 1, 0200, 0740, &chron_cmd, NULL }; +#endif +#ifdef NUM_DEVS_DSK +DIB dsk_dib = { CH_TYP_79XX, 0, 0, 0, &dsk_cmd, &dsk_ini }; +#endif +#ifdef NUM_DEVS_HT +DIB ht_dib = { CH_TYP_79XX, NUM_UNITS_HT, 0, 0, &ht_cmd, NULL }; +#endif +#ifdef NUM_DEVS_COM +DIB com_dib = { CH_TYP_79XX, 0, 0, 0, &com_cmd, NULL }; +#endif +#ifdef NUM_DEVS_HD +DIB hsdrm_dib = { CH_TYP_SPEC, 1, 0330, 0777, &hsdrm_cmd, &hsdrm_ini }; +#endif + + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Nested indirects exceed limit", + "Nested XEC's exceed limit", + "I/O check error", + "Memory management trap during trap", + "7750 invalid line number", + "7750 invalid message", + "7750 No free output buffers", + "7750 No free input buffers", "Error?", "Error2", 0 +}; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"TRAP", DEBUG_TRAP}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"SENSE", DEBUG_SNS}, + {"CTSS", DEBUG_CTSS}, + {"PROT", DEBUG_PROT}, + {0, 0} +}; + +DEBTAB crd_debug[] = { + {"CHAN", DEBUG_CHAN}, + {"CMD", DEBUG_CMD}, + {"DATA", DEBUG_DATA}, + {"DETAIL", DEBUG_DETAIL}, + {"EXP", DEBUG_EXP}, + {"CARD", DEBUG_CARD}, + {0, 0} +}; + + +/* Character conversion tables */ +const char mem_to_ascii[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'b', '=', '\'', ':', '>', '%', /* 17 = box */ + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */ + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */ + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '@', ',', '(', '~', '\\', '#' +}; /* 72 = rec mark */ + /* 75 = squiggle, 77 = del */ + +const char ascii_to_mem[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 060, 052, -1, 077, 053, 017, -1, 014, /* 40 - 77 */ + 074, 034, 054, 020, 073, 040, 033, 061, + 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 036, 013, 016, 072, + 037, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, 035, 076, 055, 057, 012, + 000, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, -1, -1, -1, -1, -1 +}; + + +/* Load a card image file into memory. */ + +t_stat +sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + t_uint64 wd; + t_uint64 mask; + uint8 buffer[160]; + int addr = 0; + int dlen = 0; + char *p; + int i, j; + + if (match_ext(fnam, "crd")) { + int firstcard = 1; + uint16 image[80]; + t_uint64 lbuff[24]; + + while (sim_fread(buffer, 1, 160, fileref) == 160) { + /* Convert bits into image */ + for (j = i = 0; j < 80; j++) { + image[j] = buffer[i++]; + image[j] |= buffer[i++] << 8; + } + + /* Bit flip into read buffer */ + for (i = 0; i < 24; i++) { + int bit = 1 << (i / 2); + int b = 36 * (i & 1); + int col; + + mask = 1; + wd = 0; + for (col = 35; col >= 0; mask <<= 1) { + if (image[col-- + b] & bit) + wd |= mask; + } + lbuff[i] = wd; + } + if (firstcard) { + addr = 0; + dlen = 3 + (int)((lbuff[0] >> 18) & AMASK); + firstcard = 0; + i = 0; + } else if (dlen == 0) { + addr = (int)(lbuff[0] & AMASK); + dlen = (int)(lbuff[0] >> 18) & AMASK; + i = 2; + } else + i = 0; + for (; i < 24 && dlen > 0; i++) { + M[addr++] = lbuff[i]; + dlen--; + } + } + } else if (match_ext(fnam, "cbn")) { + int firstcard = 1; + uint16 image[80]; + t_uint64 lbuff[24]; + + while (sim_fread(buffer, 1, 160, fileref) == 160) { + /* Convert bits into image */ + for (j = i = 0; j < 80; j++) { + image[j] = buffer[i++] & 077; + image[j] |= (buffer[i++] & 077) << 6; + } + + /* Bit flip into read buffer */ + for (i = 0; i < 24; i++) { + int bit = 1 << (i / 2); + int b = 36 * (i & 1); + int col; + + mask = 1; + wd = 0; + for (col = 35; col >= 0; mask <<= 1) { + if (image[col-- + b] & bit) + wd |= mask; + } + lbuff[i] = wd; + } + if (firstcard) { + addr = 0; + dlen = 3 + (int)((lbuff[0] >> 18) & AMASK); + firstcard = 0; + i = 0; + } else if (dlen == 0) { + addr = (int)(lbuff[0] & AMASK); + dlen = (int)(lbuff[0] >> 18) & AMASK; + i = 2; + } else + i = 0; + for (; i < 24 && dlen > 0; i++) { + M[addr++] = lbuff[i]; + dlen--; + } + } + } else if (match_ext(fnam, "oct")) { + while (fgets((char *)buffer, 80, fileref) != 0) { + for(p = (char *)buffer; *p == ' ' || *p == '\t'; p++); + /* Grab address */ + for(addr = 0; *p >= '0' && *p <= '7'; p++) + addr = (addr << 3) + *p - '0'; + while(*p != '\n' && *p != '\0') { + for(; *p == ' ' || *p == '\t'; p++); + for(wd = 0; *p >= '0' && *p <= '7'; p++) + wd = (wd << 3) + *p - '0'; + if (addr < MAXMEMSIZE) + M[addr++] = wd; + } + } + + } else if (match_ext(fnam, "sym")) { + while (fgets((char *)buffer, 80, fileref) != 0) { + for(p = (char *)buffer; *p == ' ' || *p == '\t'; p++); + /* Grab address */ + for(addr = 0; *p >= '0' && *p <= '7'; p++) + addr = (addr << 3) + *p - '0'; + while(*p == ' ' || *p == '\t') p++; + if(sim_strncasecmp(p, "BCD", 3) == 0) { + p += 4; + parse_sym(++p, addr, &cpu_unit, &M[addr], SWMASK('C')); + } else if (sim_strncasecmp(p, "OCT", 3) == 0) { + p += 4; + for(; *p == ' ' || *p == '\t'; p++); + parse_sym(p, addr, &cpu_unit, &M[addr], 0); + } else { + parse_sym(p, addr, &cpu_unit, &M[addr], SWMASK('M')); + } + } + + } else + return SCPE_ARG; + return SCPE_OK; +} + +/* Symbol tables */ +typedef struct _opcode +{ + uint16 opbase; + const char *name; + uint8 type; +} +t_opcode; + +#define TYPE_A 0 /* Basic opcode format */ +#define TYPE_B 1 /* OpcodeF Y[,T] */ +#define TYPE_C 2 /* Opcode Y,C */ +#define TYPE_D 3 /* Opcode Y[,T] */ +#define TYPE_E 4 /* Opcode */ +#define TYPE_F 5 /* Opcode T */ +#define TYPE_G 6 /* Opcode K */ +#define TYPE_P 8 /* Positive 0760 instructs */ +#define TYPE_N 9 /* Negitive 0760 instructs */ +#define TYPE_X 10 + +/* Opcodes */ +t_opcode base_ops[] = { + {OP_TXI, "TXI", TYPE_A}, + {OP_TIX, "TIX", TYPE_A}, + {OP_TXH, "TXH", TYPE_A}, + {OP_STR, "STR", TYPE_E}, + {OP_TNX, "TNX", TYPE_A}, + {OP_TXL, "TXL", TYPE_A}, + {0, NULL, TYPE_X} +}; + +/* Positive opcodes */ +t_opcode pos_ops[] = { + {0760, "", TYPE_P}, + {OP_HTR, "HTR", TYPE_B}, + {OP_TRA, "TRA", TYPE_B}, + {OP_TTR, "TTR", TYPE_B}, + {OP_TRCA, "TRCA", TYPE_B}, + {OP_TRCC, "TRCC", TYPE_B}, + {OP_TRCE, "TRCE", TYPE_B}, + {OP_TRCG, "TRCG", TYPE_B}, + {OP_TEFA, "TEFA", TYPE_B}, + {OP_TEFC, "TEFC", TYPE_B}, + {OP_TEFE, "TEFE", TYPE_B}, + {OP_TEFG, "TEFG", TYPE_B}, + {OP_TLQ, "TLQ", TYPE_B}, + {OP_IIA, "IIA", TYPE_E}, + {OP_TIO, "TIO", TYPE_B}, + {OP_OAI, "OAI", TYPE_E}, + {OP_PAI, "PAI", TYPE_E}, + {OP_TIF, "TIF", TYPE_B}, + {OP_IIR, "IIR", TYPE_G}, + {OP_RFT, "RFT", TYPE_G}, + {OP_SIR, "SIR", TYPE_G}, + {OP_RNT, "RNT", TYPE_G}, + {OP_RIR, "RIR", TYPE_G}, + {OP_TCOA, "TCOA", TYPE_B}, + {OP_TCOB, "TCOB", TYPE_B}, + {OP_TCOC, "TCOC", TYPE_B}, + {OP_TCOD, "TCOD", TYPE_B}, + {OP_TCOE, "TCOE", TYPE_B}, + {OP_TCOF, "TCOF", TYPE_B}, + {OP_TCOG, "TCOG", TYPE_B}, + {OP_TCOH, "TCOH", TYPE_B}, + {OP_TSX, "TSX", TYPE_D}, + {OP_TZE, "TZE", TYPE_B}, + {OP_CVR, "CVR", TYPE_C}, + {OP_TPL, "TPL", TYPE_B}, + {OP_XCA, "XCA", TYPE_E}, + {OP_TOV, "TOV", TYPE_B}, + {OP_TQP, "TQP", TYPE_B}, + {OP_TQO, "TQO", TYPE_B}, + {OP_MPY, "MPY", TYPE_B}, + {OP_VLM, "VLM", TYPE_C}, + {OP_DVH, "DVH", TYPE_B}, + {OP_DVP, "DVP", TYPE_B}, + {OP_VDH, "VDH", TYPE_C}, + {OP_VDP, "VDP", TYPE_C}, + {OP_FDH, "FDH", TYPE_B}, + {OP_FDP, "FDP", TYPE_B}, + {OP_FMP, "FMP", TYPE_B}, + {OP_DFMP, "DFMP", TYPE_B}, + {OP_FAD, "FAD", TYPE_B}, + {OP_DFAD, "DFAD", TYPE_B}, + {OP_FSB, "FSB", TYPE_B}, + {OP_DFSB, "DFSB", TYPE_B}, + {OP_FAM, "FAM", TYPE_B}, + {OP_DFAM, "DFAM", TYPE_B}, + {OP_FSM, "FSM", TYPE_B}, + {OP_DFSM, "DFSM", TYPE_B}, + {OP_ANS, "ANS", TYPE_B}, + {OP_ERA, "ERA", TYPE_B}, + {OP_CAS, "CAS", TYPE_B}, + {OP_ACL, "ACL", TYPE_B}, + {OP_HPR, "HPR", TYPE_E}, + {OP_OSI, "OSI", TYPE_B}, + {OP_ADD, "ADD", TYPE_B}, + {OP_ADM, "ADM", TYPE_B}, + {OP_SUB, "SUB", TYPE_B}, + {OP_IIS, "IIS", TYPE_B}, + {OP_LDI, "LDI", TYPE_B}, + {OP_DLD, "DLD", TYPE_B}, + {OP_ONT, "ONT", TYPE_B}, + {OP_RIS, "RIS", TYPE_B}, + {OP_OFT, "OFT", TYPE_B}, + {OP_CLA, "CLA", TYPE_B}, + {OP_CLS, "CLS", TYPE_B}, + {OP_ZET, "ZET", TYPE_B}, + {OP_XEC, "XEC", TYPE_B}, + {OP_LXA, "LXA", TYPE_D}, + {OP_LAC, "LAC", TYPE_D}, + {OP_ECA, "ECA", TYPE_B}, + {OP_LRI, "LRI", TYPE_B}, + {OP_RSCA, "RSCA", TYPE_B}, + {OP_RSCC, "RSCC", TYPE_B}, + {OP_RSCE, "RSCE", TYPE_B}, + {OP_RSCG, "RSCG", TYPE_B}, + {OP_STCA, "STCA", TYPE_B}, + {OP_STCC, "STCC", TYPE_B}, + {OP_STCE, "STCE", TYPE_B}, + {OP_STCG, "STCG", TYPE_B}, + {OP_LDA, "LDA", TYPE_B}, + {OP_LDQ, "LDQ", TYPE_B}, + {OP_ENB, "ENB", TYPE_B}, + {OP_STZ, "STZ", TYPE_B}, + {OP_STO, "STO", TYPE_B}, + {OP_SLW, "SLW", TYPE_B}, + {OP_STI, "STI", TYPE_B}, + {OP_STA, "STA", TYPE_B}, + {OP_STD, "STD", TYPE_B}, + {OP_STT, "STT", TYPE_B}, + {OP_STP, "STP", TYPE_B}, + {OP_SXA, "SXA", TYPE_D}, + {OP_SCA, "SCA", TYPE_D}, + {OP_TIA, "TIA", TYPE_B}, + {OP_SCHA, "SCHA", TYPE_B}, + {OP_SCHC, "SCHC", TYPE_B}, + {OP_SCHE, "SCHE", TYPE_B}, + {OP_SCHG, "SCHG", TYPE_B}, + {OP_SCDA, "SCDA", TYPE_B}, + {OP_SCDC, "SCDC", TYPE_B}, + {OP_SCDE, "SCDE", TYPE_B}, + {OP_SCDG, "SCDG", TYPE_B}, + {OP_ELD, "ELD", TYPE_B}, + {OP_EAD, "EAD", TYPE_B}, + {OP_EDP, "EDP", TYPE_B}, + {OP_EMP, "EMP", TYPE_B}, + {OP_PAX, "PAX", TYPE_F}, + {OP_PAC, "PAC", TYPE_F}, + {OP_PXA, "PXA", TYPE_F}, + {OP_PCA, "PCA", TYPE_F}, + {OP_CPY, "CPY", TYPE_B}, + {OP_NOP, "NOP", TYPE_E}, + {OP_RDS, "RDS", TYPE_D}, + {OP_BSR, "BSR", TYPE_D}, + {OP_LLS, "LLS", TYPE_D}, + {OP_LRS, "LRS", TYPE_D}, + {OP_WRS, "WRS", TYPE_D}, + {OP_ALS, "ALS", TYPE_D}, + {OP_WEF, "WEF", TYPE_D}, + {OP_ARS, "ARS", TYPE_D}, + {OP_REW, "REW", TYPE_D}, + {OP_AXT, "AXT", TYPE_D}, + {OP_DRS, "DRS", TYPE_D}, + {0, NULL, TYPE_X} +}; + +/* Negative opcodes */ +t_opcode neg_ops[] = { + {04760, "", TYPE_N}, + {OP_TRCB, "TRCB", TYPE_B}, + {OP_TRCD, "TRCD", TYPE_B}, + {OP_TRCF, "TRCF", TYPE_B}, + {OP_TRCH, "TRCH", TYPE_B}, + {OP_TEFB, "TEFB", TYPE_B}, + {OP_TEFD, "TEFD", TYPE_B}, + {OP_TEFF, "TEFF", TYPE_B}, + {OP_TEFH, "TEFH", TYPE_B}, + {OP_RIA, "RIA", TYPE_B}, + {OP_PIA, "PIA", TYPE_E}, + {OP_IIL, "IIL", TYPE_G}, + {OP_LFT, "LFT", TYPE_G}, + {OP_SIL, "SIL", TYPE_G}, + {OP_LNT, "LNT", TYPE_G}, + {OP_RIL, "RIL", TYPE_G}, + {OP_TCNA, "TCNA", TYPE_B}, + {OP_TCNB, "TCNB", TYPE_B}, + {OP_TCNC, "TCNC", TYPE_B}, + {OP_TCND, "TCND", TYPE_B}, + {OP_TCNE, "TCNE", TYPE_B}, + {OP_TCNF, "TCNF", TYPE_B}, + {OP_TCNG, "TCNG", TYPE_B}, + {OP_TCNH, "TCNH", TYPE_B}, + {OP_ESNT, "ESNT", TYPE_B}, + {OP_TNZ, "TNZ", TYPE_B}, + {OP_CAQ, "CAQ", TYPE_C}, + {OP_TMI, "TMI", TYPE_B}, + {OP_XCL, "XCL", TYPE_E}, + {OP_TNO, "TNO", TYPE_B}, + {OP_CRQ, "CRQ", TYPE_C}, + {OP_DUFA, "DUFA", TYPE_B}, + {OP_DUAM, "DUAM", TYPE_B}, + {OP_DUFS, "DUFS", TYPE_B}, + {OP_DUSM, "DUSM", TYPE_B}, + {OP_DUFM, "DUFM", TYPE_B}, + {OP_DFDH, "DFDH", TYPE_B}, + {OP_DFDP, "DFDP", TYPE_B}, + {OP_MPR, "MPR", TYPE_B}, + {OP_UFM, "UFM", TYPE_B}, + {OP_UFA, "UFA", TYPE_B}, + {OP_UFS, "UFS", TYPE_B}, + {OP_UAM, "UAM", TYPE_B}, + {OP_USM, "USM", TYPE_B}, + {OP_ANA, "ANA", TYPE_B}, + {OP_LAS, "LAS", TYPE_B}, + {OP_SBM, "SBM", TYPE_B}, + {OP_CAL, "CAL", TYPE_B}, + {OP_ORA, "ORA", TYPE_B}, + {OP_NZT, "NZT", TYPE_B}, + {OP_LXD, "LXD", TYPE_D}, + {OP_LDC, "LDC", TYPE_D}, + {OP_RSCB, "RSCB", TYPE_B}, + {OP_RSCD, "RSCD", TYPE_B}, + {OP_RSCF, "RSCF", TYPE_B}, + {OP_RSCH, "RSCH", TYPE_B}, + {OP_STCB, "STCB", TYPE_B}, + {OP_STCD, "STCD", TYPE_B}, + {OP_STCF, "STCF", TYPE_B}, + {OP_STCH, "STCH", TYPE_B}, + {OP_STQ, "STQ", TYPE_B}, + {OP_ORS, "ORS", TYPE_B}, + {OP_DST, "DST", TYPE_B}, + {OP_SLQ, "SLQ", TYPE_B}, + {OP_STL, "STL", TYPE_B}, + {OP_SCD, "SCD", TYPE_D}, + {OP_SXD, "SXD", TYPE_D}, + {OP_SRI, "SRI", TYPE_B}, + {OP_SPI, "SPI", TYPE_B}, + {OP_LPI, "LPI", TYPE_B}, + {OP_PDX, "PDX", TYPE_F}, + {OP_PDC, "PDC", TYPE_F}, + {OP_ECQ, "ECQ", TYPE_B}, + {OP_TIB, "TIB", TYPE_B}, + {OP_SCHB, "SCHB", TYPE_B}, + {OP_SCHD, "SCHD", TYPE_B}, + {OP_SCHF, "SCHF", TYPE_B}, + {OP_SCHH, "SCHH", TYPE_B}, + {OP_SCDB, "SCDB", TYPE_B}, + {OP_SCDD, "SCDD", TYPE_B}, + {OP_SCDF, "SCDF", TYPE_B}, + {OP_SCDH, "SCDH", TYPE_B}, + {OP_ESB, "ESB", TYPE_B}, + {OP_EUA, "EUA", TYPE_B}, + {OP_EST, "EST", TYPE_B}, + {OP_PXD, "PXD", TYPE_F}, + {OP_PCD, "PCD", TYPE_F}, + {OP_LGL, "LGL", TYPE_D}, + {OP_BSF, "BSF", TYPE_D}, + {OP_LGR, "LGR", TYPE_D}, + {OP_CAD, "CAD", TYPE_B}, + {OP_SPOP, "RPQ", TYPE_B}, + {OP_RUN, "RUN", TYPE_D}, + {OP_RQL, "RQL", TYPE_D}, + {OP_AXC, "AXC", TYPE_D}, + {OP_TRS, "TRS", TYPE_D}, + {0, NULL, TYPE_X} +}; + +/* Positive 0760 opcodes */ +t_opcode pos_760[] = { + {OP_CLM, "CLM", TYPE_E}, + {OP_RDCA, "RDCA", TYPE_E}, + {OP_RDCB, "RDCB", TYPE_E}, + {OP_RDCC, "RDCC", TYPE_E}, + {OP_RDCD, "RDCD", TYPE_E}, + {OP_RDCE, "RDCE", TYPE_E}, + {OP_RDCF, "RDCF", TYPE_E}, + {OP_RDCG, "RDCG", TYPE_E}, + {OP_RDCH, "RDCH", TYPE_E}, + {OP_RICA, "RICA", TYPE_E}, + {OP_RICB, "RICB", TYPE_E}, + {OP_RICC, "RICC", TYPE_E}, + {OP_RICD, "RICD", TYPE_E}, + {OP_RICE, "RICE", TYPE_E}, + {OP_RICF, "RICF", TYPE_E}, + {OP_RICG, "RICG", TYPE_E}, + {OP_RICH, "RICH", TYPE_E}, + {OP_BTTA, "BTTA", TYPE_E}, + {OP_BTTB, "BTTB", TYPE_E}, + {OP_BTTC, "BTTC", TYPE_E}, + {OP_BTTD, "BTTD", TYPE_E}, + {OP_BTTE, "BTTE", TYPE_E}, + {OP_BTTF, "BTTF", TYPE_E}, + {OP_BTTG, "BTTG", TYPE_E}, + {OP_BTTH, "BTTH", TYPE_E}, + {OP_LBT, "LBT", TYPE_E}, + {OP_CHS, "CHS", TYPE_E}, + {OP_SSP, "SSP", TYPE_E}, + {OP_ENK, "ENK", TYPE_E}, + {OP_IOT, "IOT", TYPE_E}, + {OP_COM, "COM", TYPE_E}, + {OP_ETM, "ETM", TYPE_E}, + {OP_RND, "RND", TYPE_E}, + {OP_FRN, "FRN", TYPE_E}, + {OP_DCT, "DCT", TYPE_E}, + {OP_RCT, "RCT", TYPE_E}, + {OP_LMTM, "LMTM", TYPE_E}, + {OP_SLF, "SLF", TYPE_E}, + {OP_SLN1, "SLN1", TYPE_E}, + {OP_SLN2, "SLN2", TYPE_E}, + {OP_SLN3, "SLN3", TYPE_E}, + {OP_SLN4, "SLN4", TYPE_E}, + {OP_SLN5, "SLN5", TYPE_E}, + {OP_SLN6, "SLN6", TYPE_E}, + {OP_SLN7, "SLN7", TYPE_E}, + {OP_SLN8, "SLN8", TYPE_E}, + {OP_SWT1, "SWT1", TYPE_E}, + {OP_SWT2, "SWT2", TYPE_E}, + {OP_SWT3, "SWT3", TYPE_E}, + {OP_SWT4, "SWT4", TYPE_E}, + {OP_SWT5, "SWT5", TYPE_E}, + {OP_SWT6, "SWT6", TYPE_E}, + {OP_PSE, "PSE", TYPE_E}, + {0, NULL, TYPE_X} +}; + +/* Negative 0760 opcodes */ +t_opcode neg_760[] = { + {OP_ETTA, "ETTA", TYPE_E}, + {OP_ETTB, "ETTB", TYPE_E}, + {OP_ETTC, "ETTC", TYPE_E}, + {OP_ETTD, "ETTD", TYPE_E}, + {OP_ETTE, "ETTE", TYPE_E}, + {OP_ETTF, "ETTF", TYPE_E}, + {OP_ETTG, "ETTG", TYPE_E}, + {OP_ETTH, "ETTH", TYPE_E}, + {OP_PBT, "PBT", TYPE_E}, + {OP_EFTM, "EFTM", TYPE_E}, + {OP_SSM, "SSM", TYPE_E}, + {OP_LFTM, "LFTM", TYPE_E}, + {OP_ESTM, "ESTM", TYPE_E}, + {OP_ECTM, "ECTM", TYPE_E}, + {OP_LTM, "LTM", TYPE_E}, + {OP_EMTM, "EMTM", TYPE_E}, + {OP_RTT, "RTT", TYPE_E}, + {OP_ETT, "ETT", TYPE_E}, + {OP_SLT1, "SLT1", TYPE_E}, + {OP_SLT2, "SLT2", TYPE_E}, + {OP_SLT3, "SLT3", TYPE_E}, + {OP_SLT4, "SLT4", TYPE_E}, + {OP_SLT5, "SLT5", TYPE_E}, + {OP_SLT6, "SLT6", TYPE_E}, + {OP_SLT7, "SLT7", TYPE_E}, + {OP_SLT8, "SLT8", TYPE_E}, + {OP_SWT7, "SWT7", TYPE_E}, + {OP_SWT8, "SWT8", TYPE_E}, + {OP_SWT9, "SWT9", TYPE_E}, + {OP_SWT10, "SWT10", TYPE_E}, + {OP_SWT11, "SWT11", TYPE_E}, + {OP_SWT12, "SWT12", TYPE_E}, + {OP_MSE, "MSE", TYPE_D}, + {0, NULL, TYPE_X} +}; + +const char *chname[11] = { + "*", "A", "B", "C", "D", "E", "F", "G", "H" +}; + +void +lookup_sopcode(FILE * of, t_value val, t_opcode * tab) +{ + uint16 op = (uint16)(val & 07777); + + while (tab->name != NULL) { + if (tab->opbase == op) { + fputs(tab->name, of); + switch (tab->type) { + case TYPE_D: + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + if ((val & TMASK) != 0) { + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } + return; + case TYPE_E: + if ((val & TMASK) != 0) { + fputc(' ', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } + return; + default: + return; + } + } + tab++; + } + tab--; + fputs(tab->name, of); + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + if ((val & TMASK) != 0) { + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } +} + +void +lookup_opcode(FILE * of, t_value val, t_opcode * tab) +{ + uint16 op = (uint16)(val >> 24) & 07777; + + while (tab->name != NULL) { + if (tab->opbase == op) { + fputs(tab->name, of); + switch (tab->type) { + case TYPE_B: + if ((val & 0000060000000LL) == 0000060000000LL) + fputc('*', of); + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + if ((val & TMASK) != 0) { + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } + return; + case TYPE_C: + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + fputc(',', of); + fprint_val(of, (val >> 17) & 0000777LL, 8, 9, PV_RZRO); + if ((val & TMASK) != 0) { + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } + return; + case TYPE_D: + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + if ((val & TMASK) != 0) { + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + } + return; + case TYPE_E: + return; + case TYPE_F: + fputc(' ', of); + fprint_val(of, val & AMASK, 8, 14, PV_RZRO); + fputc(',', of); + fputc('0' + (7 & (int)(val >> 15)), of); + return; + case TYPE_G: + fputc(' ', of); + fprint_val(of, val & RMASK, 8, 18, PV_RZRO); + return; + case TYPE_P: + lookup_sopcode(of, val, pos_760); + return; + case TYPE_N: + lookup_sopcode(of, val, neg_760); + return; + default: + return; + } + } + tab++; + } + fprintf(of, " %o Unknown opcode", op); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat +fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw) +{ + t_uint64 inst = *val; + +/* Print value in octal first */ + fputc(' ', of); + if (inst & MSIGN) + fputc('-', of); + else + fputc(' ', of); + fprint_val(of, inst & PMASK, 8, 35, PV_RZRO); + + if (sw & SWMASK('L')) { + t_uint64 v; + + fputs(" L ", of); + v = (inst >> 18) & AMASK; + v = AMASK & ((AMASK ^ v) + 1); + fprint_val(of, v, 8, 15, PV_RZRO); + fputs(", ", of); + v = AMASK & ((AMASK ^ inst) + 1); + fprint_val(of, v, 8, 15, PV_RZRO); + } + + if (sw & SWMASK('C')) { + int i; + + fputs(" '", of); + for (i = 5; i >= 0; i--) { + int ch; + + ch = (int)(inst >> (6 * i)) & 077; + fputc(mem_to_ascii[ch], of); + } + fputc('\'', of); + } + + /* -m only valid on CPU */ + if ((uptr != NULL) && (uptr != &cpu_unit)) + return SCPE_ARG; /* CPU? */ + if (sw & SWMASK('M')) { + fputs(" ", of); + switch (07 & (inst >> 33)) { + case OP_TXI: + fputs("TXI ", of); + goto type_a; + case OP_TIX: + fputs("TIX ", of); + goto type_a; + case OP_TXH: + fputs("TXH ", of); + goto type_a; + case OP_STR: + fputs("STR ", of); + break; + case OP_TNX: + fputs("TNX ", of); + goto type_a; + case OP_TXL: + fputs("TXL ", of); + goto type_a; + + type_a: + fprint_val(of, inst & AMASK, 8, 14, PV_RZRO); + fputc(',', of); + fputc('0' + (7 & (int)(inst >> 15)), of); + fputc(',', of); + fprint_val(of, (inst >> 18) & AMASK, 8, 14, PV_RZRO); + break; + case 04: + lookup_opcode(of, inst, neg_ops); + break; + case 00: + lookup_opcode(of, inst, pos_ops); + break; + } + } + return SCPE_OK; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + t_value d, tag; + int sign; + char buffer[100]; + t_stat r; + + while (isspace(*cptr)) + cptr++; + d = 0; + if (sw & SWMASK('M')) { + t_opcode *op; + + sign = 0; + /* Grab opcode */ + cptr = get_glyph(cptr, buffer, 0); + /* Check for indirection */ + i = strlen(buffer); + if (i > 0 && buffer[i-1] == '*') { + buffer[i-1] = '\0'; + sign = 1; + } + if ((op = find_opcode(buffer, base_ops)) != 0) { + d = (t_uint64) op->opbase << 33; + if (sign) + return STOP_UUO; + } else if ((op = find_opcode(buffer, pos_ops)) != 0 || + (op = find_opcode(buffer, neg_ops)) != 0) { + d = (t_uint64) op->opbase << 24; + if (sign) + d |= 03LL << 22; + } else if ((op = find_opcode(buffer, pos_760)) != 0) { + d = 00760LL << 24; + d += op->opbase; + } else if ((op = find_opcode(buffer, neg_760)) != 0) { + d = 04760LL << 24; + d += op->opbase; + } else { + return STOP_UUO; + } + if (op->type == TYPE_E) { + *val = d; + return SCPE_OK; + } + + /* Collect first argument if there is one */ + cptr = get_glyph(cptr, buffer, ','); + tag = get_uint (buffer, 8, + (op->type == TYPE_G) ? RMASK: AMASK, &r); + if (r != SCPE_OK) + return r; + d += tag; + if (*cptr != '\0') { + tag = 0; + /* Collect second argument if there is one */ + cptr = get_glyph(cptr, buffer, ','); + if (buffer[0] != '\0') { + tag = get_uint (buffer, 8, 07LL, &r); + if (r != SCPE_OK) + return r; + d += tag << 15; + } + if (*cptr != '\0') { + cptr = get_glyph(cptr, buffer, 0); + if (buffer[0] != '\0') { + tag = get_uint (buffer, 8, AMASK, &r); + if (r != SCPE_OK) + return r; + if (op->type == TYPE_C) + d += tag << 15; + else + d += tag << 18; + } + } + } + if (*cptr != '\0') + return STOP_UUO; + *val = d; + return SCPE_OK; + } else if (sw & SWMASK('C')) { + i = 0; + while (*cptr != '\0' && i < 6) { + d <<= 6; + if (ascii_to_mem[0177 & *cptr] != -1) + d |= ascii_to_mem[0177 & *cptr]; + cptr++; + i++; + } + while (i < 6) { + d <<= 6; + d |= 060; + i++; + } + } else { + if (*cptr == '-') { + sign = 1; + cptr++; + } else { + sign = 0; + if (*cptr == '+') + cptr++; + } + d = get_uint(cptr, 8, WMASK, &r); + if (r != SCPE_OK) + return r; + if (sign) + d |= MSIGN; + } + *val = d; + return SCPE_OK; + +} diff --git a/README.md b/README.md index e00da007..2e9301d7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ #### Gerardo Ospina has implemented a Manchester University SSEM (Small Scale Experimental Machine) simulator. -#### Richard Cornwell has implemented a Burroughs B5500 simulator. +#### Richard Cornwell has implemented a Burroughs B5500. + +#### Richard Cornwell has implemented the IBM 701, IBM 704, IBM 7010/1410, IBM 7070/7074, IBM 7080/702/705/7053 and IBM 7090/7094/709/704 simulators. #### Dave Bryan has implemented an HP-3000 Series III simulator. diff --git a/Visual Studio Projects/I701.vcproj b/Visual Studio Projects/I701.vcproj new file mode 100644 index 00000000..584e1005 --- /dev/null +++ b/Visual Studio Projects/I701.vcproj @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I7010.vcproj b/Visual Studio Projects/I7010.vcproj new file mode 100644 index 00000000..14b1a80a --- /dev/null +++ b/Visual Studio Projects/I7010.vcproj @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I704.vcproj b/Visual Studio Projects/I704.vcproj new file mode 100644 index 00000000..52957917 --- /dev/null +++ b/Visual Studio Projects/I704.vcproj @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I7070.vcproj b/Visual Studio Projects/I7070.vcproj new file mode 100644 index 00000000..460f83df --- /dev/null +++ b/Visual Studio Projects/I7070.vcproj @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I7080.vcproj b/Visual Studio Projects/I7080.vcproj new file mode 100644 index 00000000..44711cea --- /dev/null +++ b/Visual Studio Projects/I7080.vcproj @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/I7090.vcproj b/Visual Studio Projects/I7090.vcproj new file mode 100644 index 00000000..01c00443 --- /dev/null +++ b/Visual Studio Projects/I7090.vcproj @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln index a004b0b0..f14d7ea1 100644 --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -259,6 +259,33 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "3B2", "3B2.vcproj", "{56178 {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I701", "I701.vcproj", "{F1F44607-FB9E-428C-AD8F-56F98699D121}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I7090", "I7090.vcproj", "{33EE34FC-A12F-47FE-9FD6-8B74D08718C7}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I704", "I704.vcproj", "{91A7D475-1238-4872-BEAE-143E1FCEA297}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I7080", "I7080.vcproj", "{BF1E708D-D374-4DE1-A0D3-6D8DB4B4F7FA}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I7070", "I7070.vcproj", "{F55D43D3-AD63-4B19-B67A-47064227F3E3}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I7010", "I7010.vcproj", "{55A727F0-B5C8-48E8-9EF2-D5DAF679C520}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -481,6 +508,30 @@ Global {56178F08-8783-4ADA-820C-20C06412678E}.Debug|Win32.Build.0 = Debug|Win32 {56178F08-8783-4ADA-820C-20C06412678E}.Release|Win32.ActiveCfg = Release|Win32 {56178F08-8783-4ADA-820C-20C06412678E}.Release|Win32.Build.0 = Release|Win32 + {F1F44607-FB9E-428C-AD8F-56F98699D121}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1F44607-FB9E-428C-AD8F-56F98699D121}.Debug|Win32.Build.0 = Debug|Win32 + {F1F44607-FB9E-428C-AD8F-56F98699D121}.Release|Win32.ActiveCfg = Release|Win32 + {F1F44607-FB9E-428C-AD8F-56F98699D121}.Release|Win32.Build.0 = Release|Win32 + {33EE34FC-A12F-47FE-9FD6-8B74D08718C7}.Debug|Win32.ActiveCfg = Debug|Win32 + {33EE34FC-A12F-47FE-9FD6-8B74D08718C7}.Debug|Win32.Build.0 = Debug|Win32 + {33EE34FC-A12F-47FE-9FD6-8B74D08718C7}.Release|Win32.ActiveCfg = Release|Win32 + {33EE34FC-A12F-47FE-9FD6-8B74D08718C7}.Release|Win32.Build.0 = Release|Win32 + {91A7D475-1238-4872-BEAE-143E1FCEA297}.Debug|Win32.ActiveCfg = Debug|Win32 + {91A7D475-1238-4872-BEAE-143E1FCEA297}.Debug|Win32.Build.0 = Debug|Win32 + {91A7D475-1238-4872-BEAE-143E1FCEA297}.Release|Win32.ActiveCfg = Release|Win32 + {91A7D475-1238-4872-BEAE-143E1FCEA297}.Release|Win32.Build.0 = Release|Win32 + {BF1E708D-D374-4DE1-A0D3-6D8DB4B4F7FA}.Debug|Win32.ActiveCfg = Debug|Win32 + {BF1E708D-D374-4DE1-A0D3-6D8DB4B4F7FA}.Debug|Win32.Build.0 = Debug|Win32 + {BF1E708D-D374-4DE1-A0D3-6D8DB4B4F7FA}.Release|Win32.ActiveCfg = Release|Win32 + {BF1E708D-D374-4DE1-A0D3-6D8DB4B4F7FA}.Release|Win32.Build.0 = Release|Win32 + {F55D43D3-AD63-4B19-B67A-47064227F3E3}.Debug|Win32.ActiveCfg = Debug|Win32 + {F55D43D3-AD63-4B19-B67A-47064227F3E3}.Debug|Win32.Build.0 = Debug|Win32 + {F55D43D3-AD63-4B19-B67A-47064227F3E3}.Release|Win32.ActiveCfg = Release|Win32 + {F55D43D3-AD63-4B19-B67A-47064227F3E3}.Release|Win32.Build.0 = Release|Win32 + {55A727F0-B5C8-48E8-9EF2-D5DAF679C520}.Debug|Win32.ActiveCfg = Debug|Win32 + {55A727F0-B5C8-48E8-9EF2-D5DAF679C520}.Debug|Win32.Build.0 = Debug|Win32 + {55A727F0-B5C8-48E8-9EF2-D5DAF679C520}.Release|Win32.ActiveCfg = Release|Win32 + {55A727F0-B5C8-48E8-9EF2-D5DAF679C520}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/doc/i7010_doc.doc b/doc/i7010_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..00c9c22b7b171bb34c960b591d46392d3ac9dba0 GIT binary patch literal 193024 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 literal 0 HcmV?d00001 diff --git a/doc/i701_doc.doc b/doc/i701_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..0790bd417f095a260c0cc8434881a4680f0955d0 GIT binary patch literal 146432 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 P&w0e(h4|+tv-bZ1!fxOB literal 0 HcmV?d00001 diff --git a/doc/i7070_doc.doc b/doc/i7070_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..a3508aff66872440173e4fd191b114a82a6d876b GIT binary patch literal 190976 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

aI{LwhwiX?k#InZIoYXu11Z< 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 literal 0 HcmV?d00001 diff --git a/doc/i7080_doc.doc b/doc/i7080_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..564e0b61a6f252da729a10f3d4b4c856b18ba488 GIT binary patch literal 200704 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 literal 0 HcmV?d00001 diff --git a/doc/i7090_doc.doc b/doc/i7090_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..d4559257d0f05073a02273b071101b0b16cfec98 GIT binary patch literal 229376 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 A!2kdN literal 0 HcmV?d00001 diff --git a/makefile b/makefile index 7e8def82..37e57e22 100644 --- a/makefile +++ b/makefile @@ -1309,6 +1309,51 @@ I1620 = ${I1620D}/i1620_cd.c ${I1620D}/i1620_dp.c ${I1620D}/i1620_pt.c \ ${I1620D}/i1620_fp.c ${I1620D}/i1620_sys.c I1620_OPT = -I ${I1620D} +I7000D = I7000 +I7090 = ${I7000D}/i7090_cpu.c ${I7000D}/i7090_sys.c ${I7000D}/i7090_chan.c \ + ${I7000D}/i7090_cdr.c ${I7000D}/i7090_cdp.c ${I7000D}/i7090_lpr.c \ + ${I7000D}/i7000_chan.c ${I7000D}/i7000_mt.c ${I7000D}/i7090_drum.c \ + ${I7000D}/i7090_hdrum.c ${I7000D}/i7000_chron.c ${I7000D}/i7000_dsk.c \ + ${I7000D}/i7000_com.c ${I7000D}/i7000_ht.c +I7090_OPT = -I $(I7000D) -DUSE_INT64 -DI7090 -DUSE_SIM_CARD + +I7080D = I7000 +I7080 = ${I7000D}/i7080_cpu.c ${I7000D}/i7080_sys.c ${I7000D}/i7080_chan.c \ + ${I7000D}/i7080_drum.c ${I7000D}/i7000_cdp.c ${I7000D}/i7000_cdr.c \ + ${I7000D}/i7000_con.c ${I7000D}/i7000_chan.c ${I7000D}/i7000_lpr.c \ + ${I7000D}/i7000_mt.c ${I7000D}/i7000_chron.c ${I7000D}/i7000_dsk.c \ + ${I7000D}/i7000_com.c ${I7000D}/i7000_ht.c +I7080_OPT = -I $(I7000D) -DI7080 -DUSE_SIM_CARD + +I7070D = I7000 +I7070 = ${I7000D}/i7070_cpu.c ${I7000D}/i7070_sys.c ${I7000D}/i7070_chan.c \ + ${I7000D}/i7000_cdp.c ${I7000D}/i7000_cdr.c ${I7000D}/i7000_con.c \ + ${I7000D}/i7000_chan.c ${I7000D}/i7000_lpr.c ${I7000D}/i7000_mt.c \ + ${I7000D}/i7000_chron.c ${I7000D}/i7000_dsk.c ${I7000D}/i7000_com.c \ + ${I7000D}/i7000_ht.c +I7070_OPT = -I $(I7000D) -DUSE_INT64 -DI7070 -DUSE_SIM_CARD + +I7010D = I7000 +I7010 = ${I7000D}/i7010_cpu.c ${I7000D}/i7010_sys.c ${I7000D}/i7010_chan.c \ + ${I7000D}/i7000_cdp.c ${I7000D}/i7000_cdr.c ${I7000D}/i7000_con.c \ + ${I7000D}/i7000_chan.c ${I7000D}/i7000_lpr.c ${I7000D}/i7000_mt.c \ + ${I7000D}/i7000_chron.c ${I7000D}/i7000_dsk.c ${I7000D}/i7000_com.c \ + ${I7000D}/i7000_ht.c +I7010_OPT = -I $(I7010D) -DI7010 -DUSE_SIM_CARD + +I704D = I7000 +I704 = ${I7000D}/i7090_cpu.c ${I7000D}/i7090_sys.c ${I7000D}/i7090_chan.c \ + ${I7000D}/i7090_cdr.c ${I7000D}/i7090_cdp.c ${I7000D}/i7090_lpr.c \ + ${I7000D}/i7000_mt.c ${I7000D}/i7090_drum.c ${I7000D}/i7000_chan.c +I704_OPT = -I $(I7000D) -DUSE_INT64 -DI704 -DUSE_SIM_CARD + + +I701D = I7000 +I701 = ${I7000D}/i701_cpu.c ${I7000D}/i701_sys.c ${I7000D}/i701_chan.c \ + ${I7000D}/i7090_cdr.c ${I7000D}/i7090_cdp.c ${I7000D}/i7090_lpr.c \ + ${I7000D}/i7000_mt.c ${I7000D}/i7090_drum.c ${I7000D}/i7000_chan.c +I701_OPT = -I $(I7000D) -DUSE_INT64 -DI701 -DUSE_SIM_CARD + I7094D = I7094 I7094 = ${I7094D}/i7094_cpu.c ${I7094D}/i7094_cpu1.c ${I7094D}/i7094_io.c \ @@ -1622,7 +1667,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri \ i7094 ibm1130 id16 id32 sds lgp h316 cdc1700 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 \ - isys8030 isys8024 imds-225 scelbi 3b2 + isys8030 isys8024 imds-225 scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 all : ${ALL} @@ -2006,6 +2051,42 @@ ${BIN}3b2${EXE} : ${ATT3B2} ${SIM} ${BUILD_ROMS} ${MKDIRBIN} ${CC} ${ATT3B2} ${SIM} ${ATT3B2_OPT} $(CC_OUTSPEC) ${LDFLAGS} +i7090 : $(BIN)i7090$(EXE) + +${BIN}i7090${EXE} : ${I7090} ${SIM} + ${MKDIRBIN} + ${CC} ${I7090} ${SIM} ${I7090_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i7080 : $(BIN)i7080$(EXE) + +${BIN}i7080${EXE} : ${I7080} ${SIM} + ${MKDIRBIN} + ${CC} ${I7080} ${SIM} ${I7080_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i7070 : $(BIN)i7070$(EXE) + +${BIN}i7070${EXE} : ${I7070} ${SIM} + ${MKDIRBIN} + ${CC} ${I7070} ${SIM} ${I7070_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i7010 : $(BIN)i7010$(EXE) + +${BIN}i7010${EXE} : ${I7010} ${SIM} + ${MKDIRBIN} + ${CC} ${I7010} ${SIM} ${I7010_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i704 : $(BIN)i704$(EXE) + +${BIN}i704${EXE} : ${I704} ${SIM} + ${MKDIRBIN} + ${CC} ${I704} ${SIM} ${I704_OPT} $(CC_OUTSPEC) ${LDFLAGS} + +i701 : $(BIN)i701$(EXE) + +${BIN}i701${EXE} : ${I701} ${SIM} + ${MKDIRBIN} + ${CC} ${I701} ${SIM} ${I701_OPT} $(CC_OUTSPEC) ${LDFLAGS} + # Front Panel API Demo/Test program frontpaneltest : ${BIN}frontpaneltest${EXE}