diff --git a/IBM360/Makefile b/IBM360/Makefile new file mode 100644 index 0000000..0c65c4e --- /dev/null +++ b/IBM360/Makefile @@ -0,0 +1,68 @@ +# CC Command +# +# Note: -O2 is sometimes broken in GCC when setjump/longjump is being +# used. Try -O2 only with released simulators. +# +#ifeq ($(WIN32),) +#Unix Environments +#ifeq ($(OSTYPE),solaris) +#OS_CCDEFS = -lsocket -lnsl -lpthread -D_GNU_SOURCE +#else +#OS_CCDEFS = -D_GNU_SOURCE -I . +#endif +#CC = gcc -std=c99 -O2 -U__STRICT_ANSI__ -g -lm $(OS_CCDEFS) -I . +#ifeq ($(USE_NETWORK),) +#else +#NETWORK_OPT = -DUSE_NETWORK -isystem /usr/local/include /usr/local/lib/libpcap.a +#endif +#else +##Win32 Environments +LDFLAGS = -lm -lwsock32 +#CC = gcc -std=c99 -U__STRICT_ANSI__ -O0 -I. +#EXE = .exe +#ifeq ($(USE_NETWORK),) +#else +#NETWORK_OPT = -DUSE_NETWORK -lwpcap -lpacket +#endif +#endif +CC=gcc -g -std=c99 -U__STRICT_ANSI__ -D_GNU_SOURCE -I. -DUSE_SIM_CARD +LDFLAGS = -lm -lrt + + +# +# Common Libraries +# +BIN = ./ +SIM = ../scp.c ../sim_console.c ../sim_fio.c ../sim_timer.c ../sim_tape.c \ + ../sim_sock.c ../sim_tmxr.c ../sim_ether.c ../sim_video.c ../sim_serial.c \ + ../sim_disk.c ../sim_card.c + + +# +# Emulator source files and compile time options +# +IBM360D = ./ +IBM360 = ${IBM360D}ibm360_cpu.c ${IBM360D}ibm360_sys.c ${IBM360D}ibm360_con.c \ + ${IBM360D}ibm360_chan.c ${IBM360D}ibm360_cdr.c ${IBM360D}ibm360_cdp.c \ + ${IBM360D}ibm360_mt.c ${IBM360D}ibm360_lpr.c ${IBM360D}ibm360_dasd.c \ + ${IBM360D}ibm360_com.c +IBM360_OPT = -I.. -DIBM360 + +# +# Build everything +# +all : ${BIN}ibm360${EXE} + +clean : +ifeq ($(WIN32),) + ${RM} ${ALL} +else + if exist BIN\*.exe del /q BIN\*.exe +endif +# +# Individual builds +# +${BIN}ibm360${EXE} : ${IBM360} ${SIM} + ${CC} ${IBM360} ${SIM} ${IBM360_OPT} -o $@ ${LDFLAGS} + + diff --git a/IBM360/STATUS.txt b/IBM360/STATUS.txt new file mode 100644 index 0000000..d25dbdc --- /dev/null +++ b/IBM360/STATUS.txt @@ -0,0 +1,40 @@ +Current status: + +Operating Systems: +TOS/360: Will IPL, and read IPL deck. Will not run any test jobs. + +DOS/360: Will IPL disk init job. + +OS/360: Will IPL disk init job. + +ibm360_cpu.c: + Need to complete floating point and decimal instructions. + +ibm360_chan.c + Need to move fprintf's to sim_debug functions. + Add number of Channels and subchannels as option. + +ibm360_cdp.c: + Add support for binary mode. + +ibm360_cdr.c: + Add support for binary mode. + +ibm360_com.c: + Not implemented. + +ibm360_con.c: + Appears to be working. + +ibm360_dasd.c: + Need to complete. + +ibm360_lpr.c: + Need to add in support for line skip and skip control. + +ibm360_mt.c: + Appears to be working. + +ibm360_sys.c: + Need to add in deposit function. + diff --git a/IBM360/ibm360_cdp.c b/IBM360/ibm360_cdp.c new file mode 100644 index 0000000..143d6ba --- /dev/null +++ b/IBM360/ibm360_cdp.c @@ -0,0 +1,229 @@ +/* ibm360_urec.c: IBM 360 Unit record devices. + + Copyright (c) 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. + This is the standard card punch. + This is the standard line printer. + 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 "ibm360_defs.h" +#include "sim_defs.h" +#include "sim_card.h" + +#ifdef NUM_DEVS_CDP +#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE + + +#define CHN_SNS 0x04 /* Sense command */ + +/* Device status information stored in u3 */ +#define CDR_RD 0x02 /* Read command */ +#define CDR_FEED 0x03 /* Feed next card */ +#define CDP_CMDMSK 0x27 /* Mask command part. */ +#define CDR_MODE 0x20 /* Mode operation */ +#define CDR_STKMSK 0xC0 /* Mask for stacker */ +#define CDP_WR 0x09 /* Punch command */ +#define CDP_CARD 0x80 /* Unit has card in buffer */ + + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ +#define SNS_BUSCHK 0x20 /* Parity error on bus */ +#define SNS_EQUCHK 0x10 /* Equipment check */ +#define SNS_DATCHK 0x08 /* Data Check */ +#define SNS_OVRRUN 0x04 /* Data overrun */ +#define SNS_SEQUENCE 0x02 /* Unusual sequence */ +#define SNS_CHN9 0x01 /* Channel 9 on printer */ + + + +/* 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 +*/ + +uint8 cdp_startcmd(UNIT *, uint16, uint8); +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 *); + +UNIT cdp_unit[] = { + {UDATA(cdp_srv, UNIT_CDP, 0), 600, UNIT_ADDR(0x00D) }, /* A */ +#if NUM_DEVS_CDP > 1 + {UDATA(cdp_srv, UNIT_CDP, 0), 600, UNIT_ADDR(0x01D)}, /* A */ +#endif +}; + + +MTAB cdp_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +struct dib cdp_dib = { 0xFF, 1, NULL, cdp_startcmd, NULL, cdp_unit, NULL}; + +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_UADDR | DEV_DISABLE | DEV_DEBUG, 0, crd_debug +}; + + + + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + + +uint8 cdp_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + + uint8 ch; + + if ((uptr->u3 & (CDP_CARD|CDP_CMDMSK)) != 0) { + if ((uptr->flags & UNIT_ATT) != 0) + return SNS_BSY; + return SNS_DEVEND|SNS_UNITCHK; + } + + switch (cmd & 0x7) { + case 1: /* Write command */ + uptr->u3 &= ~(CDP_CMDMSK); + uptr->u3 |= (cmd & CDP_CMDMSK); + sim_activate(uptr, 1000); /* Start unit off */ + uptr->u4 = 0; + uptr->u5 = 0; + return 0; + + case 3: + if (cmd != 0x3) { + uptr->u5 |= SNS_CMDREJ; + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + return SNS_CHNEND|SNS_DEVEND; + + case 0: /* Status */ + break; + case 4: /* Sense */ + ch = uptr->u5; + chan_write_byte(GET_UADDR(uptr->u3), &ch); + return SNS_CHNEND|SNS_DEVEND; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; + break; + } + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle transfer of data for card punch */ +t_stat +cdp_srv(UNIT *uptr) { + uint16 addr = GET_UADDR(uptr->u3); + /* Waiting for disconnect */ + + if (uptr->u5 & CDP_CARD) { + /* Done waiting, punch card */ + uptr->u5 &= ~CDP_CARD; + switch(sim_punch_card(uptr, NULL)) { + /* If we get here, something is wrong */ + case SCPE_IOERR: + chan_end(addr, SNS_DEVEND|SNS_UNITCHK); + default: + chan_end(addr, SNS_DEVEND); + } + } + + /* Copy next column over */ + if (uptr->u4 < 80) { + struct _card_data *data; + int u = uptr-cdp_unit; + uint8 ch = 0; + + data = (struct _card_data *)uptr->up7; + + if (chan_read_byte(addr, &ch)) { + uptr->u3 |= CDP_CARD; + } else { + sim_debug(DEBUG_DATA, &cdp_dev, "%d: Char < %02o\n", u, ch); + data->image[uptr->u4++] = sim_ebcdic_to_hol(ch); + if (uptr->u4 == 80) { + uptr->u3 |= CDP_CARD; + } + } + if (uptr->u3 & CDP_CARD) { + uptr->u3 &= ~(CDP_CMDMSK); + chan_end(addr, SNS_CHNEND); + sim_activate(uptr, 1000); + } else + 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 & CDP_CARD) + sim_punch_card(uptr, NULL); + return sim_card_detach(uptr); +} +#endif + diff --git a/IBM360/ibm360_cdr.c b/IBM360/ibm360_cdr.c new file mode 100644 index 0000000..906f023 --- /dev/null +++ b/IBM360/ibm360_cdr.c @@ -0,0 +1,252 @@ +/* ibm360_urec.c: IBM 360 Card Reader. + + Copyright (c) 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 "ibm360_defs.h" +#include "sim_defs.h" +#include "sim_card.h" + +#ifdef NUM_DEVS_CDR +#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | UNIT_ROABLE | MODE_029 + + +#define CHN_SNS 0x04 /* Sense command */ + +/* Device status information stored in u3 */ +#define CDR_RD 0x02 /* Read command */ +#define CDR_FEED 0x03 /* Feed next card */ +#define CDR_CMDMSK 0x27 /* Mask command part. */ +#define CDR_MODE 0x20 /* Mode operation */ +#define CDR_STKMSK 0xC0 /* Mask for stacker */ +#define CDP_WR 0x09 /* Punch command */ +#define CDR_CARD 0x80 /* Unit has card in buffer */ + + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ +#define SNS_BUSCHK 0x20 /* Parity error on bus */ +#define SNS_EQUCHK 0x10 /* Equipment check */ +#define SNS_DATCHK 0x08 /* Data Check */ +#define SNS_OVRRUN 0x04 /* Data overrun */ +#define SNS_SEQUENCE 0x02 /* Unusual sequence */ +#define SNS_CHN9 0x01 /* Channel 9 on printer */ + + + +/* 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 +*/ + + + + +uint8 cdr_startcmd(UNIT *, uint16, uint8); +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 *); + + + +UNIT cdr_unit[] = { + {UDATA(cdr_srv, UNIT_CDR, 0), 300, UNIT_ADDR(0x0C)}, /* A */ +#if NUM_DEVS_CDR > 1 + {UDATA(cdr_srv, UNIT_CDR, 0), 300, UNIT_ADDR(0x1C)}, /* B */ +#endif +}; + +MTAB cdr_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +struct dib cdr_dib = { 0xFF, 1, NULL, cdr_startcmd, NULL, cdr_unit}; + +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_UADDR | DEV_DISABLE | DEV_DEBUG, 0, crd_debug +}; + + +uint8 cdr_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + uint8 ch; + + if ((uptr->u3 & CDR_CMDMSK) != 0) { + if ((uptr->flags & UNIT_ATT) != 0) + return SNS_BSY; + return SNS_DEVEND; + } + + sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %x", unit, cmd); + switch (cmd & 0x7) { + case 2: /* Read command */ + if ((cmd & 0xc0) != 0xc0) + uptr->u3 &= ~CDR_CARD; + uptr->u3 &= ~(CDR_CMDMSK); + uptr->u3 |= (cmd & CDR_CMDMSK); + sim_activate(uptr, 1000); /* Start unit off */ + uptr->u4 = 0; + uptr->u5 = 0; + return 0; + + case 3: /* Control */ + uptr->u5 = 0; + uptr->u3 &= ~(CDR_CMDMSK|CDR_CARD); + if (cmd == 0x3) + return SNS_CHNEND|SNS_DEVEND; + if ((cmd & 0x30) != 0x20 || (cmd & 0xc0) == 0xc0) { + uptr->u5 |= SNS_CMDREJ; + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + uptr->u3 |= (cmd & CDR_CMDMSK); + uptr->u4 = 0; + sim_activate(uptr, 1000); /* Start unit off */ + return 0; + + case 0: /* Status */ + break; + + case 4: /* Sense */ + ch = uptr->u5; + chan_write_byte(GET_UADDR(uptr->u3), &ch); + return SNS_CHNEND|SNS_DEVEND; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; + break; + } + + if (uptr->u5) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle transfer of data for card reader */ +t_stat +cdr_srv(UNIT *uptr) { + int addr = GET_UADDR(uptr->u3); + + /* Check if new card requested. */ + if ((uptr->u3 & CDR_CARD) == 0) { + switch(sim_read_card(uptr)) { + case SCPE_EOF: + case SCPE_UNATT: + chan_end(addr, SNS_DEVEND|SNS_UNITEXP); + uptr->u5 = SNS_INTVENT; + uptr->u3 &= ~CDR_CMDMSK; + return SCPE_OK; + case SCPE_IOERR: + chan_end(addr, SNS_DEVEND|SNS_UNITCHK); + uptr->u5 = SNS_INTVENT; + uptr->u3 &= ~CDR_CMDMSK; + return SCPE_OK; + case SCPE_OK: + uptr->u3 |= CDR_CARD; + if ((uptr->u3 & CDR_CMDMSK) == CDR_FEED) { + chan_end(addr, SNS_DEVEND); + uptr->u3 &= ~(CDR_CMDMSK); + return SCPE_OK; + } + break; + } + } + + /* Copy next column over */ + if ((uptr->u3 & CDR_CMDMSK) == CDR_RD) { + struct _card_data *data; + int u = uptr-cdr_unit; + uint8 ch = 0; + + data = (struct _card_data *)uptr->up7; + ch = sim_hol_to_ebcdic(data->image[uptr->u4]); + + if (ch == 0x100) { + uptr->u5 |= SNS_DATCHK; + ch = 0x00; + } + if (chan_write_byte(addr, &ch)) { + uptr->u3 &= ~(CDR_CMDMSK); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|(uptr->u5 ? SNS_UNITCHK:0)); + return SCPE_OK; + } else { + uptr->u4++; + sim_debug(DEBUG_DATA, &cdr_dev, "%d: Char > %02o\n", u, ch); + } + if (uptr->u4 == 80) { + uptr->u3 &= ~(CDR_CMDMSK); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|(uptr->u5 ? SNS_UNITCHK:0)); + } + 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? */ + return chan_boot(GET_UADDR(uptr->u3), dptr); +} + +t_stat +cdr_attach(UNIT * uptr, CONST char *file) +{ + int addr = GET_UADDR(uptr->u3); + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + set_devattn(addr, SNS_DEVEND); + uptr->u3 &= ~(CDR_CARD); + uptr->u4 = 0; + uptr->u6 = 0; + return SCPE_OK; +} + +#endif diff --git a/IBM360/ibm360_chan.c b/IBM360/ibm360_chan.c new file mode 100644 index 0000000..4081e25 --- /dev/null +++ b/IBM360/ibm360_chan.c @@ -0,0 +1,799 @@ +/* ibm360_chan.c: IBM 360 Channel functions. + + Copyright (c) 2007, 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 "ibm360_defs.h" +#include "sim_defs.h" + + +#define CCMDMSK 0xff000000 /* Mask for command */ +#define CDADRMSK 0x00ffffff /* Mask for data address */ +#define CCNTMSK 0x0000ffff /* Mask for data count */ +#define CD 0x80000000 /* Chain data */ +#define CC 0x40000000 /* Chain command */ +#define SLI 0x20000000 /* Suppress length indication */ +#define SKIP 0x10000000 /* Skip flag */ +#define PCI 0x08000000 /* Program controlled interuption */ + +/* Command masks */ +#define CMD_TYPE 0x3 /* Type mask */ +#define CMD_CHAN 0x0 /* Channel command */ +#define CMD_WRITE 0x1 /* Write command */ +#define CMD_READ 0x2 /* Read command */ +#define CMD_CTL 0x3 /* Control command */ +#define CMD_SENSE 0x4 /* Sense channel command */ +#define CMD_TIC 0x8 /* Transfer in channel */ +#define CMD_RDBWD 0xc /* Read backward */ + +#define STATUS_ATTN 0x8000 /* Device raised attention */ +#define STATUS_MOD 0x4000 /* Status modifier */ +#define STATUS_CTLEND 0x2000 /* Control end */ +#define STATUS_BUSY 0x1000 /* Device busy */ +#define STATUS_CEND 0x0800 /* Channel end */ +#define STATUS_DEND 0x0400 /* Device end */ +#define STATUS_CHECK 0x0200 /* Unit check */ +#define STATUS_EXPT 0x0100 /* Unit excpetion */ +#define STATUS_PCI 0x0080 /* Program interupt */ +#define STATUS_LENGTH 0x0040 /* Incorrect lenght */ +#define STATUS_PCHK 0x0020 /* Program check */ +#define STATUS_PROT 0x0010 /* Protection check */ +#define STATUS_CDATA 0x0008 /* Channel data check */ +#define STATUS_CCNTL 0x0004 /* Channel control check */ +#define STATUS_INTER 0x0002 /* Channel interface check */ +#define STATUS_CHAIN 0x0001 /* Channel chain check */ + +#define FLAG_CD 0x8000 /* Chain data */ +#define FLAG_CC 0x4000 /* Chain command */ +#define FLAG_SLI 0x2000 /* Suppress length indicator */ +#define FLAG_SKIP 0x1000 /* Suppress memory write */ +#define FLAG_PCI 0x0800 /* Program controled interrupt */ + +#define BUFF_EMPTY 0x4 /* Buffer is empty */ +#define BUFF_DIRTY 0x8 /* Buffer is dirty flag */ +#define BUFF_NEWCMD 0x10 /* Channel ready for new command */ +#define BUFF_CHNEND 0x20 /* Channel end */ + +#define AMASK 0x00ffffff +#define PMASK 0xf0000000 /* Storage protection mask */ +extern uint32 *M; +extern uint8 key[MAXMEMSIZE / 2048]; +extern UNIT cpu_unit; + +#define MAX_DEV (MAX_CHAN * 256) +int subchannels = SUB_CHANS; /* Number of subchannels */ +int irq_pend = 0; +uint32 caw[256]; /* Channel command address word */ +uint32 ccw_addr[256]; /* Channel address */ +uint16 ccw_count[256]; /* Channel count */ +uint8 ccw_cmd[256]; /* Channel command and flags */ +uint16 ccw_flags[256]; /* Channel flags */ +uint16 chan_status[256]; /* Channel status */ +uint16 chan_dev[256]; /* Device on channel */ +uint32 chan_buf[256]; /* Channel data buffer */ +uint8 chan_byte[256]; /* Current byte, dirty/full */ +DIB *dev_unit[MAX_DEV]; /* Pointer to Device info block */ +uint8 dev_status[MAX_DEV]; /* last device status flags */ + +/* Find unit pointer for given device */ +UNIT * +find_chan_dev(uint16 addr) { + struct dib *dibp; + UNIT *uptr; + int i; + + dibp = dev_unit[addr]; + if (dibp == 0) + return NULL; + uptr = dibp->units; + if (dibp->mask == 0) { + for (i = 0; i < dibp->numunits; i++) { + if (addr == GET_UADDR(uptr->u3)) + return uptr; + uptr++; + } + } else { + return uptr + (addr & ~dibp->mask & 0xff); + } + return NULL; +} + +/* channel: + subchannels = 128 + 0 - 7 0x80-0xff + 8 - 127 0x00-0x7f + 128 - +6 0x1xx - 0x6xx +*/ + +/* look up device to find subchannel device is on */ +int +find_subchan(uint16 device) { + int chan; + if (device > MAX_DEV) + return -1; + if (device > 0xff) { + chan = (device >> 8) & 0x7; + if (chan > MAX_CHAN) + return -1; + return subchannels + chan; + } + if (device < subchannels) + return device; + return ((device - subchannels)>>4) & 0xf; +} + +int +readfull(int chan, uint32 addr, uint32 *word) { + int sk, k; + if ((addr & AMASK) > MEMSIZE) { + chan_status[chan] |= STATUS_PCHK; + return 1; + } + sk = (addr >> 24) & 0xff; + if (sk != 0) { + if ((cpu_dev.flags & PROTECT) == 0) { + chan_status[chan] |= STATUS_PROT; + return 1; + } + k = key[(addr & 0xfffc00) >> 10]; + if ((k & 0x8) != 0 && (k & 0xf0) != sk) { + chan_status[chan] |= STATUS_PROT; + return 1; + } + } + addr &= AMASK; + addr >>= 2; + *word = M[addr]; + return 0; +} + +int writefull(int chan, uint32 addr, uint32 *word) { + int sk, k; + if ((addr & AMASK) > MEMSIZE) { + chan_status[chan] |= STATUS_PCHK; + return 1; + } + sk = (addr >> 24) & 0xff; + if (sk != 0) { + if ((cpu_dev.flags & PROTECT) == 0) { + chan_status[chan] |= STATUS_PROT; + return 1; + } + k = key[(addr & 0xfffc00) >> 10]; + if ((k & 0x8) != 0 && (k & 0xf0) != sk) { + chan_status[chan] |= STATUS_PROT; + return 1; + } + } +fprintf(stderr, "Channel write %02x %06x %08x %08x '", chan, addr, *word, ccw_count[chan]); + addr &= AMASK; + addr >>= 2; + M[addr] = *word; + for(k = 24; k >= 0; k -= 8) { + char ch = ebcdic_to_ascii[(*word >> k) & 0xFF]; + if (ch < 0x20 || ch == 0xff) + ch = '.'; + fprintf(stderr, "%c", ch); + } + + fprintf(stderr, "'\n\r"); + return 0; +} + +int +load_ccw(uint16 chan, int tic_ok) { + uint32 word; + int cmd = 0; + UNIT *uptr; + +loop: + if ((caw[chan] & 0x7) != 0) { + chan_status[chan] |= STATUS_PCHK; + return 1; + } + /* Check if we have status modifier set */ + if (chan_status[chan] & STATUS_MOD) { + caw[chan]+=8; + caw[chan] &= PMASK|AMASK; /* Mask overflow bits */ + chan_status[chan] &= ~STATUS_MOD; + } + readfull(chan, caw[chan], &word); +fprintf(stderr, "Channel read ccw %02x %06x %08x\n\r", chan, caw[chan], word); + if (((word >> 24) & 0xf) == CMD_TIC) { + if (tic_ok) { + caw[chan] = (caw[chan] & PMASK) | (word & AMASK); + tic_ok = 0; + goto loop; + } + chan_status[chan] |= STATUS_PCHK; + irq_pend = 1; + return 1; + } + caw[chan] += 4; + caw[chan] &= PMASK|AMASK; /* Mask overflow bits */ + /* Check if fetch ok */ + if ((ccw_flags[chan] & FLAG_CD) == 0) { + ccw_cmd[chan] = (word >> 24) & 0xff; + cmd = 1; + } + ccw_addr[chan] = word & AMASK; + ccw_addr[chan] |= caw[chan] & PMASK; /* Copy key */ + readfull(chan, caw[chan], &word); +fprintf(stderr, "Channel read ccw2 %02x %06x %08x\n\r", chan, caw[chan], word); + caw[chan]+=4; + caw[chan] &= PMASK|AMASK; /* Mask overflow bits */ + ccw_count[chan] = word & 0xffff; + ccw_flags[chan] = (word >> 16) & 0xffff; + chan_byte[chan] = BUFF_EMPTY; + if (ccw_flags[chan] & FLAG_PCI) { + chan_status[chan] |= STATUS_PCI; + irq_pend = 1; + } + /* Check invalid count */ + if (ccw_count[chan] == 0) { + chan_status[chan] |= STATUS_PCHK; + irq_pend = 1; + return 1; + } + if (cmd) { + DIB *dibp = dev_unit[chan_dev[chan]]; + /* Check if invalid command */ + if ((ccw_cmd[chan] & 0xF) == 0) { + chan_status[chan] |= STATUS_PCHK; + irq_pend = 1; + return 1; + } + uptr = find_chan_dev(chan_dev[chan]); + if (uptr == 0) + return 1; + chan_status[chan] &= 0xff; + chan_status[chan] |= dibp->start_cmd(uptr, chan, ccw_cmd[chan]) << 8; + if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) { + chan_status[chan] |= STATUS_CEND; + ccw_flags[chan] = 0; + ccw_cmd[chan] = 0; + irq_pend = 1; + return 1; + } + if (chan_status[chan] & (STATUS_DEND|STATUS_CEND)) { + chan_status[chan] |= STATUS_CEND; + chan_byte[chan] = BUFF_NEWCMD; + ccw_cmd[chan] = 0; + irq_pend = 1; + } + } + return 0; +} + + +/* read byte from memory */ +int +chan_read_byte(uint16 addr, uint8 *data) { + int chan = find_subchan(addr); + int byte; + int k; + + if ((ccw_cmd[chan] & 0x1) == 0) { + return 1; + } + if (chan_byte[chan] == BUFF_CHNEND) + return 1; + if (chan_byte[chan] == BUFF_EMPTY) { + if (readfull(chan, ccw_addr[chan], &chan_buf[chan])) { + chan_byte[chan] = BUFF_CHNEND; + irq_pend = 1; + return 1; + } +fprintf(stderr, "Channel read %02x %06x %08x %08x '", chan, ccw_addr[chan], chan_buf[chan], ccw_count[chan]); + for(k = 24; k >= 0; k -= 8) { + char ch = ebcdic_to_ascii[(chan_buf[chan] >> k) & 0xFF]; + if (ch < 0x20 || ch == 0xff) + ch = '.'; + fprintf(stderr, "%c", ch); + } + + fprintf(stderr, "'\n\r"); + chan_byte[chan] = ccw_addr[chan] & 0x3; + ccw_addr[chan] += 4 - chan_byte[chan]; + } + ccw_count[chan]--; + byte = (chan_buf[chan] >> (8 * (3 - (chan_byte[chan] & 0x3)))) & 0xff; + chan_byte[chan]++; + *data = byte; + if (ccw_count[chan] == 0) { + if (ccw_flags[chan] & FLAG_CD) + return load_ccw(chan, 1); + else { + chan_status[chan] |= STATUS_CEND; + chan_byte[chan] = BUFF_CHNEND; + } +fprintf(stderr, "chan_read_end\n\r"); + return 1; + } + return 0; +} + +/* write byte to memory */ +int +chan_write_byte(uint16 addr, uint8 *data) { + int chan = find_subchan(addr); + int byte; + int offset; + uint32 mask; + + if ((ccw_cmd[chan] & 0x1) != 0) { + return 1; + } + if (chan_byte[chan] == BUFF_CHNEND) { + if ((ccw_flags[chan] & FLAG_SLI) == 0) { + chan_status[chan] |= STATUS_LENGTH; + } + return 1; + } + if (ccw_flags[chan] & FLAG_SKIP) { + ccw_count[chan]--; + if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD) + ccw_addr[chan]--; + else + ccw_addr[chan]++; + if (ccw_count[chan] == 0) { + if (ccw_flags[chan] & FLAG_CD) + return load_ccw(chan, 1); + else + chan_byte[chan] = BUFF_CHNEND; + return 1; + } + return 0; + } + if (chan_byte[chan] == (BUFF_EMPTY|BUFF_DIRTY)) { + if (writefull(chan, ccw_addr[chan], &chan_buf[chan])) { + chan_byte[chan] = BUFF_CHNEND; + irq_pend = 1; + return 1; + } + if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD) + ccw_addr[chan] -= 1 + (ccw_addr[chan] & 0x3); + else + ccw_addr[chan] += 4 - (ccw_addr[chan] & 0x3); + chan_byte[chan] = BUFF_EMPTY; + } + if (chan_byte[chan] == BUFF_EMPTY) { + if (readfull(chan, ccw_addr[chan], &chan_buf[chan])) { + chan_byte[chan] = BUFF_CHNEND; + irq_pend = 1; + return 1; + } + chan_byte[chan] = ccw_addr[chan] & 0x3; + } + ccw_count[chan]--; + offset = 8 * (chan_byte[chan] & 0x3); + mask = 0xff000000 >> offset; + chan_buf[chan] &= ~mask; + chan_buf[chan] |= ((uint32)(*data)) << (24 - offset); + if ((ccw_cmd[chan] & 0xf) == CMD_RDBWD) { + if (chan_byte[chan] & 0x3) + chan_byte[chan]--; + else + chan_byte[chan] = BUFF_EMPTY; + } else + chan_byte[chan]++; + chan_byte[chan] |= BUFF_DIRTY; + if (ccw_count[chan] == 0) { + if (writefull(chan, ccw_addr[chan], &chan_buf[chan])) { + chan_byte[chan] = BUFF_CHNEND; + return 1; + } + if (ccw_flags[chan] & FLAG_CD) + return load_ccw(chan, 1); + chan_byte[chan] = BUFF_CHNEND; +fprintf(stderr, "chan_write_end\n\r"); + return 1; + } + return 0; +} + +void +set_devattn(uint16 addr, uint8 flags) { + int chan = find_subchan(addr); + + if (chan_dev[chan] == addr && (chan_status[chan] & STATUS_CEND) != 0 && + (flags & SNS_DEVEND) != 0) { + chan_status[chan] |= ((uint16)flags) << 8; + } else + dev_status[addr] = flags; +fprintf(stderr, "set_devattn(%x, %x) %x\n\r", addr, flags, chan_dev[chan]); + irq_pend = 1; +} + +void +chan_end(uint16 addr, uint8 flags) { + int chan = find_subchan(addr); + +fprintf(stderr, "chan_end(%x, %x)\n\r", addr, flags); + if (chan_byte[chan] & BUFF_DIRTY) { + writefull(chan, ccw_addr[chan], &chan_buf[chan]); + chan_byte[chan] = BUFF_EMPTY; + } + chan_status[chan] |= STATUS_CEND; + chan_status[chan] |= ((uint16)flags) << 8; + ccw_cmd[chan] = 0; + if (ccw_count[chan] != 0 && (ccw_flags[chan] & FLAG_SLI) == 0) { + chan_status[chan] |= STATUS_LENGTH; + ccw_flags[chan] = 0; + } + if (flags & (SNS_ATTN|SNS_UNITCHK|SNS_UNITEXP)) { + ccw_flags[chan] = 0; + } + + if (chan_status[chan] & (STATUS_DEND|STATUS_CEND)) { + chan_byte[chan] = BUFF_NEWCMD; + } + + irq_pend = 1; +} + +int +store_csw(uint16 chan) { + M[0x40 >> 2] = caw[chan]; + M[0x44 >> 2] = (((uint32)ccw_count[chan])) | ((uint32)chan_status[chan]<<16); +fprintf(stderr, "Channel store csw %02x %06x %08x\n\r", chan, M[0x40>>2], M[0x44 >> 2]); + return chan_dev[chan]; +} + + +int startio(uint16 addr) { + int chan = find_subchan(addr); + DIB *dibp = dev_unit[addr]; + UNIT *uptr; + uint8 status; + +fprintf(stderr, "SIO %x %x %x %x\n\r", addr, chan, ccw_cmd[chan], ccw_flags[chan]); + if (chan < 0 || dibp == 0) + return 3; + uptr = find_chan_dev(addr); + if (uptr == 0) + return 3; + if (ccw_cmd[chan] != 0 || (ccw_flags[chan] & (FLAG_CD|FLAG_CC)) != 0) + return 2; + chan_status[chan] = 0; + dev_status[addr] = 0; + caw[chan] = M[0x48>>2]; + chan_dev[chan] = addr; + if (dibp->start_io != NULL) { + chan_status[chan] = dibp->start_io(uptr, chan) << 8; + if (chan_status[chan] != 0) { + M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | M[0x44 >> 2] & 0xffff; + fprintf(stderr, "Channel store csw %02x %08x\n\r", chan, M[0x44 >> 2]); + chan_status[chan] = 0; + return 1; + } + } + if (load_ccw(chan, 0) || (chan_status[chan] & (STATUS_PCI))) { + M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | M[0x44 >> 2] & 0xffff; + fprintf(stderr, "Channel store csw %02x %08x\n\r", chan, M[0x44 >> 2]); + chan_status[chan] &= ~STATUS_PCI; + dev_status[addr] = 0; + return 1; + } + return 0; +} + +int testio(uint16 addr) { + int chan = find_subchan(addr); + DIB *dibp = dev_unit[addr]; + UNIT *uptr; + uint8 status; + + if (chan < 0 || dibp == 0) + return 3; + uptr = find_chan_dev(addr); + if (uptr == 0) + return 3; + if (ccw_cmd[chan] != 0 || (ccw_flags[chan] & (FLAG_CD|FLAG_CC)) != 0) + return 2; + if (chan_dev[chan] != addr) + return 2; + if (ccw_cmd[chan] == 0 && chan_status[chan] != 0) { + store_csw(chan); + chan_status[chan] = 0; + dev_status[addr] = 0; + return 1; + } + if (dev_status[addr] & (SNS_ATTN|SNS_UNITCHK|SNS_UNITEXP)) { + M[0x40 >> 2] = 0; + M[0x44 >> 2] = ((uint32)dev_status[addr]) << 24; + dev_status[addr] = 0; + return 1; + } +fprintf(stderr, "Testio %x %x\n\r", addr, chan_status[chan]); + chan_status[chan] = dibp->start_cmd(uptr, chan, 0) << 8; + if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) { + M[0x44 >> 2] = ((uint32)chan_status[chan]<<16) | M[0x44 >> 2] & 0xffff; + chan_status[chan] = 0; + dev_status[addr] = 0; + return 1; + } + chan_status[chan] = 0; + return 0; +} + +int haltio(uint16 addr) { + int chan = find_subchan(addr); + DIB *dibp = dev_unit[addr]; + UNIT *uptr; + uint8 status; + + if (chan < 0 || dibp == 0) + return 3; + uptr = find_chan_dev(chan_dev[chan]); + if (uptr == 0) + return 3; + if (ccw_cmd[chan]) { + chan_byte[chan] = BUFF_CHNEND; + return 2; + } + if (dibp->halt_io != NULL) + chan_status[chan] = dibp->halt_io(uptr) << 8; + if (chan_status[chan] & (STATUS_ATTN|STATUS_PCI|STATUS_EXPT|STATUS_CHECK| + STATUS_PROT|STATUS_CDATA|STATUS_CCNTL|STATUS_INTER| + STATUS_CHAIN)) + return 0; + return 0; +} + +int testchan(uint16 channel) { + uint16 st = 0; + channel >>= 8; + if (channel = 0) + return 0; + st = chan_status[subchannels + channel]; + if (st & STATUS_BUSY) + return 2; + if (st & (STATUS_ATTN|STATUS_PCI|STATUS_EXPT|STATUS_CHECK| + STATUS_PROT|STATUS_CDATA|STATUS_CCNTL|STATUS_INTER| + STATUS_CHAIN)) + return 1; + return 0; +} + +t_stat chan_boot(uint16 addr, DEVICE *dptyr) { + int chan = find_subchan(addr); + DIB *dibp = dev_unit[addr]; + UNIT *uptr; + uint8 status; + int i; + + if (chan < 0 || dibp == 0) + return SCPE_IOERR; + for (i = 0; i < MAX_DEV; i++) { + dev_status[i] = 0; + } + for (i = 0; i < 256; i++) { + ccw_cmd[i] = 0; + ccw_flags[i] = 0; + } + uptr = find_chan_dev(addr); + chan_status[chan] = 0; + dev_status[addr] = 0; + caw[chan] = 0x8; + chan_dev[chan] = addr; + ccw_count[chan] = 24; + ccw_flags[chan] = FLAG_CC|FLAG_SLI; + chan_byte[chan] = BUFF_EMPTY; + ccw_cmd[chan] = 0x2; + chan_status[chan] &= 0xff; + chan_status[chan] |= dibp->start_cmd(uptr, chan, ccw_cmd[chan]) << 8; + if (chan_status[chan] & (STATUS_ATTN|STATUS_CHECK|STATUS_EXPT)) { + ccw_flags[chan] = 0; + return SCPE_IOERR; + } + loading = addr; + return SCPE_OK; +} + +/* Scan all channels and see if one is ready to start or has + interrupt pending. +*/ +uint16 scan_chan(uint8 mask) { + int i; + int pend = 0; /* No device */ + int imask = 0x80; + + if (irq_pend == 0) + return 0; + irq_pend = 0; + for (i = 0; i < subchannels + MAX_CHAN; i++) { + if (i >= subchannels) + imask = imask / 2; + + /* If channel end, check if we should continue */ + if (chan_status[i] & STATUS_CEND) { + if (ccw_flags[i] & FLAG_CC) { +//fprintf(stderr, "Scan(%x %x) CC\n\r", i, chan_status[i]); + if (chan_status[i] & STATUS_DEND) + (void)load_ccw(i, 1); + else + irq_pend = 1; + } else { +fprintf(stderr, "Scan(%x %x %x %x) end\n\r", i, chan_status[i], imask, mask); + if ((imask & mask) != 0 || loading != 0) { + pend = chan_dev[i]; + break; + } + } + } + } + if (pend) { + irq_pend = 1; + i = find_subchan(pend); +fprintf(stderr, "Scan end (%x %x)\n\r", chan_dev[i], pend); + store_csw(i); + chan_status[i] = 0; + chan_dev[i] = 0; + dev_status[pend] = 0; + } else { + for (i = 0; i < MAX_DEV; i++) { + if (dev_status[i] != 0) { + pend = find_subchan(i); + if (ccw_cmd[pend] == 0 && mask & (0x80 >> (i >> 8))) { + irq_pend = 1; +fprintf(stderr, "Set atten %03x %02x\n\r", i, dev_status[i]); +// M[0x40 >> 2] = 0; + M[0x44 >> 2] = (((uint32)dev_status[i]) << 24) | + (M[0x44>>2] & 0xffff); + dev_status[i] = 0; + return i; + } + } + } + pend = 0; + } + return pend; +} + + +t_stat +chan_set_devs() +{ + int i, j; + + for(i = 0; i < MAX_DEV; i++) { + dev_unit[i] = NULL; /* Device pointer */ + } + /* Build channel array */ + for (i = 0; sim_devices[i] != NULL; i++) { + DEVICE *dptr = sim_devices[i]; + UNIT *uptr = dptr->units; + DIB *dibp = (DIB *) dptr->ctxt; + int addr; + int chan; + + /* If no DIB, not channel device */ + if (dibp == NULL) + continue; + /* Skip disabled devices */ + if (dptr->flags & DEV_DIS) + continue; + /* Check if address is in unit or dev entry */ + for (j = 0; j < dptr->numunits; j++) { + addr = GET_UADDR(uptr->u3); + if ((uptr->flags & UNIT_DIS) == 0) + dev_unit[addr] = dibp; + if (dibp->dev_ini != NULL) + dibp->dev_ini(uptr, 1); + uptr++; + + } + } + return SCPE_OK; +} + +/* Sets the device onto a given channel */ +t_stat +set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value newdev; + t_stat r; + int num; + int type; + int i; + int devaddr; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + newdev = get_uint (cptr, 16, 0xfff, &r); + + if (r != SCPE_OK) + return r; + + devaddr = GET_UADDR(uptr->u3); + if (newdev > MAX_DEV) + return SCPE_ARG; + + /* Clear out existing entry */ + if (dptr->flags & DEV_UADDR) { + dev_unit[devaddr] = NULL; + } else { + devaddr &= dibp->mask | 0x700; + for (i = 0; i < dibp->numunits; i++) + dev_unit[devaddr + i] = NULL; + } + + /* Check if device already at newdev */ + if (dptr->flags & DEV_UADDR) { + if (dev_unit[newdev] != NULL) + r = SCPE_ARG; + } else { + newdev &= dibp->mask | 0x700; + for (i = 0; i < dibp->numunits; i++) { + if (dev_unit[newdev + i] != NULL) + r = SCPE_ARG; + } + } + + /* If not, point to new dev, else restore old */ + if (r == SCPE_OK) + devaddr = newdev; + + /* Update device entry */ + if (dptr->flags & DEV_UADDR) { + dev_unit[devaddr] = dibp; + fprintf(stderr, "Set dev %x\n\r", devaddr); + } else { + for (i = 0; i < dibp->numunits; i++) { + dev_unit[devaddr + i] = dibp; + fprintf(stderr, "Set dev %x\n\r", devaddr + i); + } + } + return r; +} + +t_stat +show_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + DEVICE *dptr; + DIB *dibp; + int addr; + + + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + addr = GET_UADDR(uptr->u3); + fprintf(st, "%03x", addr); + return SCPE_OK; +} + diff --git a/IBM360/ibm360_com.c b/IBM360/ibm360_com.c new file mode 100644 index 0000000..9c02c50 --- /dev/null +++ b/IBM360/ibm360_com.c @@ -0,0 +1,304 @@ +/* ibm360_com.c: IBM 360 2703 communications controller + + Copyright (c) 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 "ibm360_defs.h" + +#ifdef NUM_DEVS_COM +#define UNIT_COM UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE + + +/* u3 */ +#define DK_NOP 0x03 /* Nop operation */ +#define DK_RELEASE 0x17 /* Release from channel */ +#define DK_RESTORE 0x13 /* Restore */ +#define DK_SEEK 0x07 /* Seek */ +#define DK_SEEKCYL 0x0B /* Seek Cylinder */ +#define DK_SEEKHD 0x09 /* Seek Head */ +#define DK_SETMSK 0x1f /* Set file mask */ +#define DK_SPACE 0x0f /* Space record */ +#define DK_SRCH_HAEQ 0x39 /* Search HA equal */ +#define DK_SRCH_IDEQ 0x31 /* Search ID equal */ +#define DK_SRCH_IDGT 0x51 /* Search ID greater */ +#define DK_SRCH_IDGE 0x71 /* Search ID greater or equal */ +#define DK_SRCH_KYEQ 0x29 /* Search Key equal */ +#define DK_SRCH_KYGT 0x49 /* Search Key greater */ +#define DK_SRCH_KYGE 0x69 /* Search Key greater or equal */ +#define DK_RD_IPL 0x02 /* Read IPL record */ +#define DK_RD_HA 0x1A /* Read home address */ +#define DK_RD_CNT 0x12 /* Read count */ +#define DK_RD_R0 0x16 /* Read R0 */ +#define DK_RD_D 0x06 /* Read Data */ +#define DK_RD_KD 0x0e /* Read key and data */ +#define DK_RD_CKD 0x1e /* Read count, key and data */ +#define DK_WR_HA 0x19 /* Write home address */ +#define DK_WR_R0 0x15 /* Write R0 */ +#define DK_WR_D 0x05 /* Write Data */ +#define DK_WR_KD 0x0d /* Write key and data */ +#define DK_WR_CKD 0x1d /* Write count, key and data */ +#define DK_WR_SCKD 0x01 /* Write special count, key and data */ +#define DK_ERASE 0x11 /* Erase to end of track */ +#define DK_MT 0x80 /* Multi track flag */ + +#define DK_CYL_DIRTY 0x100 /* Current cylinder dirty */ + +#define DK_MSK_INHWR0 0x00 /* Inhbit writing of HA/R0 */ +#define DK_MSK_INHWRT 0x40 /* Inhbit all writes */ +#define DK_MSK_ALLWRU 0x80 /* Allow all updates */ +#define DK_MSK_ALLWRT 0xc0 /* Allow all writes */ +#define DK_MSK_WRT 0xc0 /* Write mask */ + +#define DK_MSK_SKALLSKR 0x00 /* Allow all seek/recal */ +#define DK_MSK_SKALLCLY 0x01 /* Allow cyl/head only */ +#define DK_MSK_SKALLHD 0x02 /* Allow head only */ +#define DK_MSK_SKNONE 0x03 /* Allow no seeks */ +#define DK_MSK_SK 0x03 /* Seek mask */ + + +/* u4 */ +/* Position around disk */ + +/* u5 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x01 /* Command reject */ +#define SNS_INTVENT 0x02 /* Unit intervention required */ +#define SNS_BUSCHK 0x04 /* Parity error on bus */ +#define SNS_EQUCHK 0x08 /* Equipment check */ +#define SNS_DATCHK 0x10 /* Data Check */ +#define SNS_OVRRUN 0x20 /* Data overrun */ +#define SNS_TRKCND 0x40 /* Track Condition */ +#define SNS_SEEKCK 0x80 /* Seek Check */ + +/* Sense byte 1 */ +#define SNS_DCCNT 0x01 /* Data Check Count */ +#define SNS_TRKOVR 0x02 /* Track Overrun */ +#define SNS_ENDCYL 0x04 /* End of Cylinder */ +#define SNS_INVSEQ 0x08 /* Invalid Sequence */ +#define SNS_NOREC 0x10 /* No record found */ +#define SNS_WRP 0x20 /* Write Protect */ +#define SNS_ADDR 0x40 /* Missing Address Mark */ +#define SNS_OVRINC 0x80 /* Overflow Incomplete */ + +/* Sense byte 2 */ +#define SNS_BYTE2 0x00 /* Diags Use */ +/* Sense byte 3 */ +#define SNS_BYTE3 0x00 /* Diags Use */ + +/* u6 */ + +uint8 com_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +uint8 com_haltio(uint16 addr); +t_stat com_srv(UNIT *); +void com_ini(UNIT *, t_bool); +t_stat com_reset(DEVICE *); +t_stat com_attach(UNIT *, CONST char *); +t_stat com_detach(UNIT *); +t_stat com_boot(int32, DEVICE *); + + + +MTAB com_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +UNIT com_unit[] = { + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x030)}, /* 0 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x031)}, /* 1 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x032)}, /* 2 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x033)}, /* 3 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x034)}, /* 4 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x035)}, /* 5 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x036)}, /* 6 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x037)}, /* 7 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x038)}, /* 8 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x039)}, /* 9 */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03A)}, /* A */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03B)}, /* B */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03C)}, /* C */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03D)}, /* D */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03E)}, /* E */ + {UDATA(&com_srv, UNIT_COM, 0), 0, UNIT_ADDR(0x03F)}, /* F */ +}; + +struct dib com_dib = { 0xF0, NUM_UNITS_MT, NULL, com_startcmd, NULL, com_unit, NULL}; + +DEVICE com_dev = { + "COM", com_unit, NULL, com_mod, + NUM_UNITS_COM, 8, 15, 1, 8, 8, + NULL, NULL, &com_reset, NULL, &com_attach, &com_detach, + &com_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug +}; + + + +uint8 com_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + uint8 ch; + + if ((uptr->u3 & 0xff) != 0) { + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %x", unit, cmd); + + switch (cmd & 0x3) { + case 0x3: /* Control */ + if ((cmd & 0xfc) == 0) + return SNS_CHNEND|SNS_DEVEND; + case 0x1: /* Write command */ + case 0x2: /* Read command */ + uptr->u3 |= cmd; + return 0; + + case 0x0: /* Status */ + if (cmd == 0x4) { /* Sense */ + uptr->u3 |= cmd; + return 0; + } + break; + } + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle processing of disk requests. */ +t_stat com_srv(UNIT * uptr) +{ + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + struct com_t *data = (struct com_t *)(uptr->up7); + int unit = (uptr - dptr->units); + int cmd = uptr->u3 & 0xff; + uint8 ch; + + + switch (cmd) { + case 0: /* No command, stop tape */ + sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit); + break; + + case 0x4: + ch = uptr->u5 & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = (uptr->u5 >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0xc0; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = (uptr->u5 >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0; + chan_write_byte(addr, &ch) ; + chan_write_byte(addr, &ch); + uptr->u3 &= ~0xff; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + + case DK_RELEASE: /* Release from channel */ + case DK_RESTORE: /* Restore */ + case DK_SEEK: /* Seek */ + case DK_SEEKCYL: /* Seek Cylinder */ + case DK_SEEKHD: /* Seek Head */ + case DK_SETMSK: /* Set file mask */ + case DK_SPACE: /* Space record */ + case DK_SRCH_HAEQ: /* Search HA equal */ + case DK_SRCH_IDEQ: /* Search ID equal */ + case DK_SRCH_IDGT: /* Search ID greater */ + case DK_SRCH_IDGE: /* Search ID greater or equal */ + case DK_SRCH_KYEQ: /* Search Key equal */ + case DK_SRCH_KYGT: /* Search Key greater */ + case DK_SRCH_KYGE: /* Search Key greater or equal */ + case DK_RD_IPL: /* Read IPL record */ + case DK_RD_HA: /* Read home address */ + case DK_RD_CNT: /* Read count */ + case DK_RD_R0: /* Read R0 */ + case DK_RD_D: /* Read Data */ + case DK_RD_KD: /* Read key and data */ + case DK_RD_CKD: /* Read count, key and data */ + case DK_WR_HA: /* Write home address */ + case DK_WR_R0: /* Write R0 */ + case DK_WR_D: /* Write Data */ + case DK_WR_KD: /* Write key and data */ + case DK_WR_CKD: /* Write count, key and data */ + case DK_WR_SCKD: /* Write special count, key and data */ + case DK_ERASE: /* Erase to end of track */ + break; + } + return SCPE_OK; +} + +void +com_ini(UNIT * uptr, t_bool f) +{ + DEVICE *dptr = find_dev_from_unit(uptr); +} + +t_stat +com_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +t_stat +com_attach(UNIT * uptr, CONST char *file) +{ + uint16 addr = GET_UADDR(uptr->u3); + t_stat r; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + set_devattn(addr, SNS_DEVEND); + return SCPE_OK; +} + +t_stat +com_detach(UNIT * uptr) +{ + uptr->u3 = 0; + return detach_unit(uptr); +} + +t_stat com_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr) +{ + int i; +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +return SCPE_OK; +} + +const char *com_description (DEVICE *dptr) +{ +return "IBM 2703 communications controller"; +} + +#endif diff --git a/IBM360/ibm360_con.c b/IBM360/ibm360_con.c new file mode 100644 index 0000000..fcb473b --- /dev/null +++ b/IBM360/ibm360_con.c @@ -0,0 +1,333 @@ +/* ibm360_urec.c: IBM 360 Unit record devices. + + Copyright (c) 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. + This is the standard card punch. + This is the standard line printer. + 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 "ibm360_defs.h" +#include "sim_defs.h" +#include + +#ifdef NUM_DEVS_CON + +/* Held in u3 */ +#define CHN_SNS 0x04 /* Sense command */ +#define CON_WR 0x01 /* Write console */ +#define CON_ACR 0x09 /* Auto carrage return */ +#define CON_RD 0x0a /* Read console */ +#define CON_NOP 0x03 /* No op command */ +#define CON_MSK 0x0f /* Command mask */ + +/* Status held in u3 */ +#define CON_INPUT 0x100 /* Input ready for unit */ +#define CON_CR 0x200 /* Output at beginning of line */ +#define CON_REQ 0x400 /* Request key pressed */ + +/* Input buffer pointer held in u4 */ + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ + + + +/* std devices. data structures + + con_dev Console device descriptor + con_unit Console unit descriptor + con_reg Console register list + con_mod Console modifiers list +*/ + + +struct _con_data +{ + uint8 ibuff[145]; /* Input line buffer */ + uint8 inptr; +} +con_data[NUM_DEVS_CON]; + +uint8 con_startcmd(UNIT *, uint16, uint8); +void con_ini(UNIT *, t_bool); +t_stat con_srv(UNIT *); +t_stat con_reset(DEVICE *); +t_stat con_attach(UNIT *, char *); +t_stat con_detach(UNIT *); + + +UNIT con_unit[] = { + {UDATA(con_srv, 0, 0), 0, UNIT_ADDR(0x1F)}, /* A */ +}; + +struct dib con_dib = { 0xFF, 1, NULL, con_startcmd, NULL, con_unit, con_ini}; + +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_UADDR | DEV_DISABLE | DEV_DEBUG, 0, dev_debug +}; + + + + +/* + * 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); +} + +uint8 con_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + int u = (uptr - con_unit); + uint8 ch; + + if ((uptr->u3 & CON_MSK) != 0) + return SNS_BSY; + + if ((cmd & 0xf0) != 0) { + uptr->u5 |= SNS_CMDREJ; + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + switch (cmd & 0x7) { + case 2: /* Read command */ + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd RD\n", u); + if (uptr->u3 & CON_REQ) { + return SNS_ATTN|SNS_BSY; + } + + if ((uptr->u3 & CON_INPUT) == 0 && + (con_data[u].inptr == 0 || uptr->u3 & CON_CR)) { + /* Activate input so we can get response */ + sim_putchar('I'); + sim_putchar(' '); + uptr->u5 &= ~CON_CR; + } + uptr->u4 = 0; + uptr->u3 |= cmd & CON_MSK; + uptr->u5 = 0; + return 0; + + case 1: /* Write command */ + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd WR\n", u); + if (uptr->u3 & CON_REQ) { + return SNS_ATTN|SNS_BSY; + } + uptr->u3 |= cmd & CON_MSK; + uptr->u5 = 0; + if (uptr->u3 & CON_CR) { + sim_putchar('R'); + sim_putchar(' '); + uptr->u3 &= ~CON_CR; + } + return 0; + + case 3: /* Control */ + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd NOP\n", u); + if (uptr->u3 & CON_REQ) { + return SNS_ATTN|SNS_BSY; + } + uptr->u5 = 0; + return SNS_CHNEND|SNS_DEVEND; + + case 0: /* Status */ + break; + + case 4: /* Sense */ + sim_debug(DEBUG_CMD, &con_dev, "%d: Cmd SNS %02x\n", u, uptr->u5); + /* Check if request pending */ + ch = uptr->u5; + chan_write_byte(GET_UADDR(uptr->u3), &ch); + return SNS_CHNEND|SNS_DEVEND; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; + break; + } + + if (uptr->u5) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle transfer of data for printer */ +t_stat +con_srv(UNIT *uptr) { + uint16 addr = GET_UADDR(uptr->u3); + int u = (uptr - con_unit); + int cmd = uptr->u3 & CON_MSK; + t_stat r = SCPE_ARG; /* Force error if not set */ + uint8 ch; + int i; + + + switch (cmd) { + case CON_WR: + case CON_ACR: + if (chan_read_byte(addr, &ch)) { + if (cmd == CON_ACR) { + sim_putchar('\r'); + sim_putchar('\n'); + uptr->u3 |= CON_CR; + } + uptr->u3 &= ~CON_MSK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } else { + ch = ebcdic_to_ascii[ch]; + if (!isprint(ch)) + ch = '_'; + sim_putchar(ch); + } + break; + + case CON_RD: + if (uptr->u3 & CON_INPUT) { + uptr->u3 &= ~CON_REQ; + if (con_data[u].inptr == 0) { + uptr->u3 &= ~CON_INPUT; + con_data[u].inptr = 0; + cmd = 0; + uptr->u3 &= ~(CON_MSK); + sim_debug(DEBUG_CMD, &con_dev, "%d: devend\n", u); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + } + + ch = con_data[u].ibuff[uptr->u4++]; + sim_debug(DEBUG_CMD, &con_dev, "%d: rd %02x\n", u, ch); + if (chan_write_byte(addr, &ch)) { + uptr->u3 &= ~CON_INPUT; + con_data[u].inptr = 0; + cmd = 0; + uptr->u3 &= ~(CON_MSK); + sim_debug(DEBUG_CMD, &con_dev, "%d: devend input\n", u); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } else { + if (uptr->u4 == con_data[u].inptr) { + uptr->u3 &= ~CON_INPUT; + con_data[u].inptr = 0; + cmd = 0; + uptr->u3 &= ~(CON_MSK); + sim_debug(DEBUG_CMD, &con_dev, "%d: devend\n", u); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + } + } + break; + } + + r = sim_poll_kbd(); + if (r & SCPE_KFLAG) { + ch = r & 0377; + if ((uptr->u3 & CON_INPUT) == 0) { + /* Handle end of buffer */ + switch (ch) { + case '\r': + case '\n': + sim_debug(DEBUG_CMD, &con_dev, "%d: ent\n", u); +// if (con_data[u].inptr != 0) + uptr->u3 |= CON_INPUT; + uptr->u3 |= CON_CR; + sim_putchar('\r'); + sim_putchar('\n'); + /* Fall through */ + + case 033: /* request key */ + if (cmd != CON_RD) { + uptr->u3 |= CON_REQ; + } + break; + case 0177: + case '\b': + if (con_data[u].inptr != 0) { + con_data[u].inptr--; + sim_putchar('\b'); + sim_putchar(' '); + sim_putchar('\b'); + } + break; + case 030: /* ^X Post external interrupt */ + sim_debug(DEBUG_CMD, &con_dev, "%d: ext\n", u); + post_extirq(); + break; + case 03: /* ^C */ + case 025: /* ^U clear line */ + for (i = con_data[u].inptr; i> 0; i--) { + sim_putchar('\b'); + sim_putchar(' '); + sim_putchar('\b'); + } + con_data[u].inptr = 0; + break; + + default: + sim_debug(DEBUG_CMD, &con_dev, "%d: key '%c'\n", u, ch); + if (con_data[u].inptr < sizeof(con_data[u].ibuff)) { + ch = ascii_to_ebcdic[ch]; + if (ch == 0xff) { + sim_putchar('\007'); + break; + } + sim_putchar(ebcdic_to_ascii[ch]); + con_data[u].ibuff[con_data[u].inptr++] = ch; + } + break; + } + } else { + if (cmd == CON_RD && ch == 03) { /* Cancel */ + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + uptr->u3 &= ~CON_INPUT; + con_data[u].inptr = 0; + cmd = 0; + } else { + sim_putchar('\007'); + } + } + } + + if (cmd == 0 && uptr->u3 & CON_REQ) { + sim_debug(DEBUG_CMD, &con_dev, "%d: setattn %x\n", u, addr); + set_devattn(addr, SNS_ATTN); + uptr->u3 &= ~CON_REQ; + } + sim_activate(uptr, 500); + return SCPE_OK; +} + +#endif + + diff --git a/IBM360/ibm360_cpu.c b/IBM360/ibm360_cpu.c new file mode 100644 index 0000000..961e088 --- /dev/null +++ b/IBM360/ibm360_cpu.c @@ -0,0 +1,2152 @@ +/* ibm360_cpu.c: ibm 360 cpu simulator. + + Copyright (c) 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 "ibm360_defs.h" /* simulator defns */ + + +#define FEAT_PROT (1 << (UNIT_V_UF + 8)) /* Storage protection feature */ +#define FEAT_DEC (1 << (UNIT_V_UF + 9)) /* Decimal instruction set */ +#define FEAT_FLOAT (1 << (UNIT_V_UF + 10)) /* Floating point instruction set */ +#define FEAT_UNIV (3 << (UNIT_V_UF + 9)) /* All instructions */ +#define FEAT_STOR (1 << (UNIT_V_UF + 11)) /* No alignment restrictions */ +#define FEAT_TIMER (1 << (UNIT_V_UF + 12)) /* Interval timer */ +#define EXT_IRQ (1 << (UNIT_V_UF_31)) /* External interrupt */ + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ +#define UNIT_MSIZE (0xff << UNIT_V_MSIZE) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) + +#define HIST_MAX 50000 +#define HIST_MIN 64 +#define HIST_PC 0x1000000 +#define HIST_SPW 0x2000000 +#define HIST_LPW 0x4000000 + +uint32 *M = NULL; +uint8 key[MAXMEMSIZE/2048]; +uint32 regs[16]; /* CPU Registers */ +uint32 PC; /* Program counter */ +uint32 fpregs[8]; /* Floating point registers */ +uint32 cregs[16]; /* Control registers /67 or 370 only */ +uint8 sysmsk; /* Interupt mask */ +uint8 st_key; /* Storage key */ +uint8 cc; /* CC */ +uint8 pmsk; /* Program mask */ +uint16 irqcode; /* Interupt code */ +uint8 flags; /* Misc flags */ +uint16 irqaddr; /* Address of IRQ vector */ +uint16 loading; /* Doing IPL */ + +#define ASCII 0x08 /* ASCII/EBCDIC mode */ +#define MCHECK 0x04 /* Machine check flag */ +#define WAIT 0x02 +#define PROBLEM 0x01 /* Problem state */ +#define ILMASK 0xC0 /* Instruction length mask */ + +#define FIXOVR 0x08 /* Fixed point overflow */ +#define DECOVR 0x04 /* Decimal overflow */ +#define EXPOVR 0x02 /* Exponent overflow */ +#define SIGMSK 0x01 /* Significance */ + + +/* low addresses */ +#define IPSW 0x00 /* IPSW */ +#define ICCW1 0x08 /* ICCW1 */ +#define ICCW2 0x10 /* ICCW2 */ +#define OEPSW 0x18 /* External old PSW */ +#define OSPSW 0x20 /* Supervisior call old PSW */ +#define OPPSW 0x28 /* Program old PSW */ +#define OMPSW 0x30 /* Machine check PSW */ +#define OIOPSW 0x38 /* IO old PSW */ +#define CSW 0x40 /* CSW */ +#define CAW 0x48 /* CAW */ +#define TIMER 0x50 /* timer */ +#define NEPSW 0x58 /* External new PSW */ +#define NSPSW 0x60 /* SVC new PSW */ +#define NPPSW 0x68 /* Program new PSW */ +#define NMPSW 0x70 /* Machine Check PSW */ +#define NIOPSW 0x78 /* IOPSW */ +#define DIAGAREA 0x80 /* Diag scan area. */ + + +#define IRC_OPR 0x0001 /* Operations exception */ +#define IRC_PRIV 0x0002 /* Privlege violation */ +#define IRC_EXEC 0x0003 /* Execution */ +#define IRC_PROT 0x0004 /* Protection violation */ +#define IRC_ADDR 0x0005 /* Address error */ +#define IRC_SPEC 0x0006 /* Specification error */ +#define IRC_DATA 0x0007 /* Data exception */ +#define IRC_FIXOVR 0x0008 /* Fixed point overflow */ +#define IRC_FIXDIV 0x0009 /* Fixed point divide */ +#define IRC_DECOVR 0x000a /* Decimal overflow */ +#define IRC_DECDIV 0x000b /* Decimal divide */ +#define IRC_EXPOVR 0x000c /* Exponent overflow */ +#define IRC_EXPUND 0x000d /* Exponent underflow */ +#define IRC_SIGNIF 0x000e /* Significance error */ +#define IRC_FPDIV 0x000f /* Floating pointer divide */ + +#define MSIGN 0x80000000 /* Minus sign */ +#define MMASK 0x00ffffff /* Mantissa mask */ +#define EMASK 0x7f000000 /* Exponent mask */ +#define XMASK 0x0fffffff /* Working FP mask */ +#define CMASK 0xf0000000 /* Carry mask */ + +int hst_lnt; +int hst_p; +struct InstHistory +{ + uint32 pc; + uint32 addr1; + uint32 addr2; + uint32 src1; + uint32 src2; + uint32 dest; + uint16 inst[3]; + uint8 op; + uint8 reg; + uint8 cc; +}; + +struct InstHistory *hst = NULL; + +/* Forward and external declarations */ + +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_set_hist (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_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); + +t_bool build_dev_tab (void); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (/*&rtc_srv*/NULL, UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { HRDATA (PC, PC, 24) }, + { HRDATA (R0, regs[00], 32) }, /* addr in memory */ + { HRDATA (R1, regs[01], 32) }, /* modified at exit */ + { HRDATA (R2, regs[02], 32) }, /* to SCP */ + { HRDATA (R3, regs[03], 32) }, + { HRDATA (R4, regs[04], 32) }, + { HRDATA (R5, regs[05], 32) }, + { HRDATA (R6, regs[06], 32) }, + { HRDATA (R7, regs[07], 32) }, + { HRDATA (R8, regs[010], 32) }, + { HRDATA (R9, regs[011], 32) }, + { HRDATA (R10, regs[012], 32) }, + { HRDATA (R11, regs[013], 32) }, + { HRDATA (R12, regs[014], 32) }, + { HRDATA (R13, regs[015], 32) }, + { HRDATA (R14, regs[016], 32) }, + { HRDATA (R15, regs[017], 32) }, + { BRDATA (R, regs, 16, 32, 017) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, MEMAMOUNT(1), "16K", "16K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(2), "32K", "32K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(4), "64K", "64K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(8), "128K", "128K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(12), "196K", "196K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(16), "256K", "256K", &cpu_set_size }, + { UNIT_MSIZE, MEMAMOUNT(32), "512K", "512K", &cpu_set_size }, + { FEAT_PROT, 0, NULL, "NOPROT", NULL, NULL, NULL, "No Storage protection"}, + { FEAT_PROT, FEAT_PROT, "PROT", "PROT", NULL, NULL, NULL, "Storage protection"}, + { FEAT_DEC, 0, NULL, "NODECIMAL", NULL, NULL, NULL}, + { FEAT_DEC, FEAT_DEC, "DECIMAL", "DECIMAL", NULL, NULL, NULL, "Decimal instruction set"}, + { FEAT_FLOAT, 0, NULL, "NOFLOAT", NULL, NULL, NULL}, + { FEAT_FLOAT, FEAT_FLOAT, "FLOAT", "FLOAT", NULL, NULL, NULL, "Floating point instruction"}, + { FEAT_UNIV, FEAT_UNIV, NULL, "UNIV", NULL, NULL, NULL, "Universal instruction"}, + { FEAT_STOR, 0, NULL, "NOSTORE", NULL, NULL, NULL}, + { FEAT_STOR, FEAT_STOR, "STORE", "DECIMAL", NULL, NULL, NULL, "No storage alignment"}, + { FEAT_TIMER, 0, "NOTIMER", NULL, NULL, NULL}, + { FEAT_TIMER, FEAT_TIMER, "TIMER", "TIMER", NULL, NULL, NULL, "Interval timer"}, + { EXT_IRQ, 0, "NOEXT", NULL, NULL, NULL}, + { EXT_IRQ, EXT_IRQ, "EXT", "EXT", NULL, NULL, NULL, "External Irq"}, + { 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, 16, 24, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, NULL, 0, 0, NULL, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description + }; + +#if 0 /* 370 Operators */ +MVCL 0e +CLCL 0f +STNSM ac SI +STOSM ad SI +SIGP ae RS +MC af SI +LRA B1 RX +spec B2 S +STCTL B6 RS +LCTL B7 RS +CS BA RS +CDS bb RS +CLM bd RS +STCM BE RS +ICM BF RS + +#endif + +void post_extirq() { + cpu_unit.flags |= EXT_IRQ; +} + + +void storepsw(uint32 addr, uint16 ircode) { + uint32 word; + irqaddr = addr + 0x40; + word = ((uint32)sysmsk) << 24 | + ((uint32)st_key) << 16 | + ((uint32)flags) << 16 | + ((uint32)ircode); + M[addr >> 2] = word; + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = addr | HIST_SPW; + hst[hst_p].src1 = word; + } + addr += 4; + word = ((uint32)pmsk) << 24 | + ((uint32)cc) << 28 | + PC; + M[addr >> 2] = word; + if (hst_lnt) { + hst[hst_p].src2 = word; + } + irqcode = ircode; +} + +/* + * Read a full word from memory, checking protection + * and alignment restrictions. Return 1 if failure, 0 if + * success. + */ +int ReadFull(uint32 addr, uint32 *data) { + uint32 temp; + int offset; + uint8 k; + + /* Ignore high order bits */ + addr &= 0xffffff; + if (addr >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + return 1; + } + + offset = addr & 0x3; + addr >>= 2; + + /* Check storage key */ + if (st_key != 0) { + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + k = key[addr >> 10]; + if ((k & 0x8) != 0 && (k & 0xf0) != st_key) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + } + + if (offset != 0) { + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + temp = M[addr]; + } + *data = M[addr]; + return 0; +} + +int ReadByte(uint32 addr, uint32 *data) { + + if (ReadFull(addr & (~0x3), data)) + return 1; + *data >>= 8 * (3 - (addr & 0x3)); + *data &= 0xff; + return 0; +} + +int ReadHalf(uint32 addr, uint32 *data) { + if (addr & 0x1) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + if (ReadFull(addr & (~0x3), data)) + return 1; + *data >>= (addr & 2) ? 0 : 16; + *data &= 0xffff; + if (*data & 0x8000) + *data |= 0xffff0000; + return 0; +} + +int WriteFull(uint32 addr, uint32 data) { + int offset; + uint8 k; + /* Ignore high order bits */ + addr &= 0xffffff; + if (addr >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + return 1; + } +// if ((addr& 0xffff00) == 0x1800) { +// fprintf(stderr, "Write %x %x\n\r", addr, data); +// } + + offset = addr & 0x3; + addr >>= 2; + + /* Check storage key */ + if (st_key != 0) { + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + k = key[addr >> 10]; + if ((k & 0xf0) != st_key) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + } + + switch (offset) { + case 0: + M[addr] = data; + break; + case 1: + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + M[addr] &= 0xff000000; + M[addr] |= 0xffffff & (data >> 8); + M[addr+1] &= 0xffffff; + M[addr+1] |= 0xff000000 & (data << 24); + break; + case 2: + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + M[addr] &= 0xffff0000; + M[addr] |= 0xffff & (data >> 16); + M[addr+1] &= 0xffff; + M[addr+1] |= 0xffff0000 & (data << 16); + break; + case 3: + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + M[addr] &= 0xffffff00; + M[addr] |= 0xff & (data >> 24); + M[addr+1] &= 0xff; + M[addr+1] |= 0xffffff00 & (data << 8); + break; + } + return 0; +} + +int WriteByte(uint32 addr, uint32 data) { + uint32 mask; + uint8 k; + int offset; + + /* Ignore high order bits */ + addr &= 0xffffff; + if (addr >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + return 1; + } +// if ((addr& 0xffff00) == 0x1800) { +// fprintf(stderr, "Write Byte %x %x\n\r", addr, data); +// } + + offset = 8 * (3 - (addr & 0x3)); + addr >>= 2; + + /* Check storage key */ + if (st_key != 0) { + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + k = key[addr >> 10]; + if ((k & 0xf0) != st_key) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + } + + mask = 0xff; + data &= mask; + data <<= offset; + mask <<= offset; + M[addr] &= ~mask; + M[addr] |= data; + return 0; +} + +int WriteHalf(uint32 addr, uint32 data) { + uint32 mask; + uint8 k; + int offset; + int o; + + /* Ignore high order bits */ + addr &= 0xffffff; + if (addr >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + return 1; + } +// if ((addr& 0xffff00) == 0x1800) { +// fprintf(stderr, "Write Half %x %x\n\r", addr, data); +// } + + offset = addr & 0x3; + addr >>= 2; + + /* Check storage key */ + if (st_key != 0) { + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + k = key[addr >> 10]; + if ((k & 0xf0) != st_key) { + storepsw(OPPSW, IRC_PROT); + return 1; + } + } + + mask = 0xffff; + data &= mask; + switch (offset) { + case 0: + M[addr] &= ~(mask << 16); + M[addr] |= data << 16; + break; + case 1: + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + M[addr] &= ~(mask << 8); + M[addr] |= data << 8; + break; + case 2: + M[addr] &= ~mask; + M[addr] |= data; + break; + case 3: + if ((cpu_unit.flags & FEAT_STOR) == 0) { + storepsw(OPPSW, IRC_SPEC); + return 1; + } + M[addr] &= 0xffffff00; + M[addr] |= 0xff & (data >> 8); + M[addr+1] &= 0x00ffffff; + M[addr+1] |= 0xff000000 & (data << 24); + break; + } + return 0; +} + + + +t_stat +sim_instr(void) +{ + t_stat reason; + uint32 src1; + uint32 src1h; + uint32 src2; + uint32 src2h; + uint32 dest; + uint32 addr1; + uint32 addr2; + uint8 op; + uint8 fill; + uint8 reg; + uint8 reg1; + int e1, e2; + int temp, temp2; + t_uint64 t64; + t_uint64 t64a; + + reason = SCPE_OK; + while (reason == SCPE_OK) { + +wait_loop: + if (sim_interval <= 0) { + reason = sim_process_event(); + if (reason != SCPE_OK) + return reason; + } + + irqcode = scan_chan(sysmsk); + if (irqcode != 0) { + if (loading) { + (void)WriteHalf(0x2, irqcode); + loading = 0; + irqaddr = 0; + } else + storepsw(OIOPSW, irqcode); + goto supress; + } + + if ((cpu_unit.flags & EXT_IRQ) && (sysmsk & 01)) { + cpu_unit.flags &= ~EXT_IRQ; + storepsw(OEPSW, 0x40); + goto supress; + } + + if (loading || flags & WAIT) { + /* CPU IDLE */ + sim_interval = -1; + goto wait_loop; + } + + if (sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) { + return STOP_IBKPT; + } + + if (PC & 1) { + storepsw(OPPSW, IRC_SPEC); + goto supress; + } + + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = PC | HIST_PC; + } + + if (ReadHalf(PC, &src1)) + goto supress; + if (hst_lnt) + hst[hst_p].inst[0] = src1; + PC += 2; + pmsk &= ~ILMASK; + reg = (uint8)(src1 & 0xff); + reg1 = (reg >> 4) & 0xf; + op = (uint8)(src1 >> 8); + pmsk += 0x40; + /* Check if RX, RR, SI, RS, SS opcode */ + if (op & 0xc0) { + pmsk += 0x40; + if (ReadHalf(PC, &addr1)) + goto supress; + if (hst_lnt) + hst[hst_p].inst[1] = addr1; + PC += 2; + /* Check if SS */ + if ((op & 0xc0) == 0xc0) { + pmsk += 0x40; + if (ReadHalf(PC, &addr2)) + goto supress;; + PC += 2; + if (hst_lnt) + hst[hst_p].inst[2] = addr2; + } + } + + /* Add in history here */ +opr: + if (op & 0xc0) { + uint32 temp; + + temp = addr1 & 0xf000; + addr1 &= 0xfff; + if (temp) + addr1 += regs[(temp >> 12) & 0xf]; + /* Handle RX type operands */ + if ((op & 0x80) == 0 && (reg & 0xf) != 0) + addr1 += regs[reg & 0xf]; + addr1 &= 0xffffff; + if ((op & 0xc0) == 0xc0) { + temp = addr2 & 0xf000; + addr2 &= 0xfff; + if (temp) + addr2 += regs[(temp >> 12) & 0xf]; + addr2 &= 0xffffff; + } + } + + /* Check if floating point */ + if ((op & 0xA0) == 0x20) { + if ((cpu_unit.flags & FEAT_FLOAT) == 0) { + storepsw(OPPSW, IRC_OPR); + goto supress; + } + if (reg1 & 0x9) { + reason=1; + storepsw(OPPSW, IRC_SPEC); + goto supress; + } + src1 = fpregs[reg1]; + src1h = fpregs[reg1|1]; + if (op & 0x40) { + if ((op & 0x10) != 0 && (addr1 & 0x7) != 0) { + storepsw(OPPSW, IRC_SPEC); + goto supress; + } + + if (ReadFull(addr1, &src2)) + goto supress; + if (op & 0x10) { + if (ReadFull(addr1+ 4, &src2h)) + goto supress; + } else + src2h = 0; + } else { + if (reg & 0x9) { + storepsw(OPPSW, IRC_SPEC); + goto supress; + } + src2 = fpregs[reg]; + src2h = fpregs[reg|1]; + } + /* All RR opcodes */ + } else if ((op & 0xe0) == 0) { + src1 = regs[reg1]; + dest = addr1 = src2 = regs[reg & 0xf]; + /* All RX integer ops */ + } else if ((op & 0xe0) == 0x40) { + dest = src1 = regs[reg1]; + if ((op & 0x1c) == 0x08 || op == OP_MH) { + if (ReadHalf(addr1, &src2)) + goto supress; + } else if ((op & 0x10) && (op & 0x0c) != 0) { + if (ReadFull(addr1, &src2)) + goto supress; + } else + src2 = addr1; + } + + if (hst_lnt) { + hst[hst_p].op = op; + hst[hst_p].reg = reg; + hst[hst_p].addr1 = addr1; + hst[hst_p].addr2 = addr2; + hst[hst_p].src1 = src1; + hst[hst_p].src2 = src2; + hst[hst_p].dest = dest; + } + + /* Preform opcode */ + switch (op) { + case OP_SPM: + dest = src1; + pmsk &= ~0xf; + pmsk |= (src1 >> 24) & 0xf; + cc = (src1 >> 28) & 0x3; + break; + + case OP_BALR: + case OP_BAL: + dest = ((((cc & 03) << 4) | pmsk) << 24) | PC; + if (op != OP_BALR || (reg & 0xf)) + PC = addr1 & 0xffffff; + regs[reg1] = dest; + break; + + case OP_BCTR: + case OP_BCT: + dest = src1 - 1; + if (dest != 0 && (op != OP_BCTR || (reg & 0xf) != 0)) + PC = addr1 & 0xffffff; + regs[reg1] = dest; + break; + + case OP_BCR: + case OP_BC: + dest = src1; + if (((0x8 >> cc) & reg1) != 0 && (op != OP_BCR || (reg & 0xf) != 0)) + PC = addr1 & 0xffffff; + break; + + case OP_BXH: + reg &= 0xf; + dest = regs[reg1] = regs[reg1] + regs[reg]; + if (regs[reg1] > regs[reg|1]) + PC = addr1 & 0xffffff; + break; + + case OP_BXLE: + reg &= 0xf; + dest = regs[reg1] = regs[reg1] + regs[reg]; + if (regs[reg1] <= regs[reg|1]) + PC = addr1 & 0xffffff; + break; + + case OP_SSK: + dest = src1; + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_OPR); + } else if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else if ((addr1 & 0xF) != 0) { + storepsw(OPPSW, IRC_SPEC); + } else if (addr1 >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + } else if (cpu_unit.flags & FEAT_PROT) { + key[addr1 >> 12] = src1 & 0xf8; + } + break; + + case OP_ISK: + dest = src1; + if ((cpu_unit.flags & FEAT_PROT) == 0) { + storepsw(OPPSW, IRC_PROT); + } if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else if ((addr1 & 0xF) != 0) { + storepsw(OPPSW, IRC_SPEC); + } else if (addr1 >= MEMSIZE) { + storepsw(OPPSW, IRC_ADDR); + } else { + dest &= 0xffffff00; + dest |= key[addr1 >> 12]; + regs[reg1] = dest; + } + break; + + case OP_SVC: + storepsw(OSPSW, reg); + break; + + case OP_SSM: + if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else { + ReadByte(addr1, &src1); + sysmsk = src1 & 0xff; + irq_pend = 1; + } + break; + + case OP_LPSW: + if (flags & PROBLEM) { + storepsw(OPPSW, IRC_PRIV); + } else if ((addr1 & 0x7) != 0) { + storepsw(OPPSW, IRC_SPEC); + } else { + irqaddr = addr1; + irqcode = 0; + goto supress; + } + break; + + case OP_SIO: + if (flags & PROBLEM) + storepsw(OPPSW, IRC_PRIV); + else + cc = startio(addr1); + break; + + case OP_TIO: + if (flags & PROBLEM) + storepsw(OPPSW, IRC_PRIV); + else + cc = testio(addr1); + break; + + case OP_HIO: + if (flags & PROBLEM) + storepsw(OPPSW, IRC_PRIV); + else + cc = haltio(addr1); + break; + + case OP_TCH: + if (flags & PROBLEM) + storepsw(OPPSW, IRC_PRIV); + else + cc = testchan(addr1); + break; + + case OP_DIAG: + if (flags & PROBLEM) + storepsw(OPPSW, IRC_PRIV); + else + storepsw(OMPSW, reg); + break; + + case OP_LPR: + if ((dest & MSIGN) == 0) + goto set_cc; + /* Fall through */ + + case OP_LCR: + if (dest == MSIGN) + goto set_cc3; + dest = -dest; + /* Fall through */ + + case OP_LTR: +set_cc: + regs[reg1] = dest; + cc = (dest & MSIGN) ? 1 : (dest == 0) ? 0 : 2; + break; + + case OP_LNR: + if ((dest & MSIGN) == 0) + dest = -dest; + goto set_cc; + + case OP_LA: + case OP_L: + case OP_LH: + case OP_LR: + dest = src2; + regs[reg1] = dest; + break; + + case OP_C: + case OP_CR: + case OP_CH: + dest = src1; + cc = 0; + if ((int32)(src1) > (int32)(src2)) + cc = 2; + else if (src1 != src2) + cc = 1; + break; + + case OP_S: + case OP_SR: + case OP_SH: + src2 = -src2; + /* Fall through */ + + case OP_A: + case OP_AR: + case OP_AH: + dest = src1 + src2; + src1 = (src1 & MSIGN) != 0; + src2 = (src2 & MSIGN) != 0; + if ((src1 && src2 && (dest & MSIGN) == 0) || + (!src1 && !src2 && (dest & MSIGN) != 0)) { +set_cc3: + regs[reg1] = dest; + cc = 3; + if (pmsk & FIXOVR) { + storepsw(OPPSW, IRC_FIXOVR); + reason =1; + } + } + goto set_cc; + + case OP_SL: + case OP_SLR: + src2 = -src2; + /* Fall through */ + + case OP_AL: + case OP_ALR: + dest = src1 + src2; + cc = 0; + if ((uint32)src1 > (uint32)src2) + cc |= 2; + if (dest == 0) + cc |= 1; + regs[reg1] = dest; + break; + + case OP_CL: + case OP_CLR: + dest = src1; + cc = 0; + if ((uint32)(src1) > (uint32)(src2)) + cc = 2; + else if (src1 != src2) + cc = 1; + break; + + case OP_M: + case OP_MR: + if (reg1 & 1) { + storepsw(OPPSW, IRC_SPEC); + break; + } + case OP_MH: + fill = 0; + fprintf(stderr, "Mul %d x %d = ", src1, src2); + + if (src1 & MSIGN) { + fill = 1; + src1 = -src1; + } + if (src2 & MSIGN) { + fill ^= 1; + src2 = -src2; + } + dest = 0; + if (src1 != 0 || src2 != 0) { + for (reg = 32; reg > 0; reg--) { + if (src1 & 1) + dest += src2; + src1 >>= 1; + if (dest & 1) + src1 |= MSIGN; + dest >>= 1; + } + } + if (fill) { + dest ^= 0xffffffff; + src1 ^= 0xffffffff; + if (src1 == 0xfffffff) + dest ++; + src1++; + } + if (op != OP_MH) { + regs[reg1|1] = src1; + regs[reg1] = dest; + } else { + regs[reg1] = src1; + } +fprintf(stderr, " %d %08x %08x\n\r", src1, dest, src1); +// reason =1; + break; + +// dest = (uint32)((int32)src1 * (int32)src2); + // regs[reg1] = dest; + // reason =1; + // break; + + case OP_D: + case OP_DR: + if (reg1 & 1) { + storepsw(OPPSW, IRC_SPEC); + break; + } + fill = 0; + dest = src2; + src2 = regs[reg1|1]; + src1 = regs[reg1]; + fprintf(stderr, "Div %08x %08x / %08x = ", src1, src2, dest); + + if (dest == 0) { + storepsw(OPPSW, IRC_FIXDIV); + fprintf(stderr, "zero\n\r"); + break; + } + t64 = (((t_uint64)src1) << 32) | ((t_uint64)src2); + if (src1 & MSIGN) { + fill = 3; + t64 = -t64; + } + if (dest & MSIGN) { + fill ^= 1; + dest = -dest; + } + fprintf(stderr, "%lld / %d ", t64, dest); + t64a = t64 % (t_uint64)dest; + t64 = t64 / (t_uint64)dest; + fprintf(stderr, "%lld , %lld ", t64, t64a); + if ((t64 & 0xffffffff80000000) != 0) { + storepsw(OPPSW, IRC_FIXDIV); + fprintf(stderr, "zero\n\r"); + break; + } + + src1 = (uint32)t64; + src2 = (uint32)t64a; +#if 0 + if (src2 & MSIGN) { + fill = 3; + src2 ^= 0xffffffff; + src1 ^= 0xffffffff; + if (src1 == 0xfffffff) + src2 ++; + src1++; + } + if (dest & MSIGN) { + fill ^= 1; + dest = -dest; + } + src2 <<= 1; + if (src1 & MSIGN) + src2 |= 1; + src1 <<= 1; + for (reg = 0; reg < 32; reg++) { + int f = 0; + if (src1 < dest) { + src1 = src1 - dest; + f = 1; + } + src2 <<= 1; + src2 |= f; + if (src1 & MSIGN) + src2 |= 1; + src1 <<= 1; + } +#endif + if (fill & 1) + src1 = -src1; + if (fill & 2) + src2 = -src2; + regs[reg1|1] = src2; + regs[reg1] = src1; +fprintf(stderr, " %08x %08x\n\r", src1, src2); +// reason =1; + break; + + case OP_NR: + case OP_N: + dest = src1 & src2; + cc = (dest == 0) ? 0 : 1; + regs[reg1] = dest; + break; + + case OP_OR: + case OP_O: + dest = src1 | src2; + cc = (dest == 0) ? 0 : 1; + regs[reg1] = dest; + break; + + case OP_XR: + case OP_X: + dest = src1 ^ src2; + cc = (dest == 0) ? 0 : 1; + regs[reg1] = dest; + break; + + case OP_MVI: + src1 = reg; + /* Fall through */ + + case OP_STC: + WriteByte(addr1, src1); + break; + + case OP_NI: + if (ReadByte(addr1, &dest)) + break; + if (hst_lnt) + hst[hst_p].src1 = dest; + dest &= reg; +char_save: + cc = (dest == 0) ? 0 : 1; + WriteByte(addr1, dest); + break; + + case OP_OI: + if (ReadByte(addr1, &dest)) + break; + if (hst_lnt) + hst[hst_p].src1 = dest; + dest |= reg; + goto char_save; + + case OP_XI: + if (ReadByte(addr1, &dest)) + break; + if (hst_lnt) + hst[hst_p].src1 = dest; + dest ^= reg; + goto char_save; + + case OP_CLI: + if (ReadByte(addr1, &dest)) + break; + if (hst_lnt) + hst[hst_p].src1 = dest; + dest &= 0xff; + cc = (dest == reg) ? 0 : (dest < reg) ? 1 : 2; + break; + + case OP_IC: + if (ReadByte(addr1, &dest)) + break; + dest = (src1 & 0xffffff00) | (dest & 0xff); + regs[reg1] = dest; + break; + + case OP_ST: + dest = src1; + WriteFull(addr1, dest); + break; + + case OP_STH: + dest = src1; + WriteHalf(addr1, dest); + break; + + case OP_TS: + dest = 0xff; + if (ReadByte(addr1, &src2)) + break; + cc = (src2 & 0x80) ? 1 : 0; + WriteByte(addr1, src1); + break; + + case OP_TM: + if (ReadByte(addr1, &dest)) + break; + if (hst_lnt) + hst[hst_p].src1 = dest; + dest &= reg; + if (dest != 0) { + cc = 1; + if (reg == dest) + cc = 3; + } else + cc = 0; + break; + + case OP_SRL: + dest = regs[reg1]; + dest = ((uint32)dest) >> (addr1 & 0x3f); + regs[reg1] = dest; + break; + + case OP_SLL: + dest = regs[reg1]; + dest = ((uint32)dest) << (addr1 & 0x3f); + regs[reg1] = dest; + break; + + case OP_SRA: + dest = regs[reg1]; + dest = (int32)dest >> (addr1 & 0x3f); + goto set_cc; + + case OP_SLA: + dest = regs[reg1]; + src2 = dest & MSIGN; + addr1 &= 0x3f; + cc = 0; + while (addr1 > 0) { + dest <<= 1; + if ((dest & MSIGN) != src2) + cc = 3; + addr1--; + } + if (cc == 3) + goto set_cc3; + else + goto set_cc; + break; + + case OP_SRDL: + if (reg & 1) + storepsw(OPPSW, IRC_SPEC); + else { + src1 = regs[reg1]; + src1h = regs[reg1|1]; + if (hst_lnt) { + hst[hst_p].src1 = src1; + hst[hst_p].src2 = src1h; + } + addr1 &= 0x3f; + while(addr1 > 0) { + src1h >>= 1; + if (src1 & 1) + src1h |= MSIGN; + src1 >>= 1; + addr1--; + } + regs[reg1|1] = src1h; + regs[reg1] = src1; + dest = src1; + } + break; + + case OP_SLDL: + if (reg & 1) + storepsw(OPPSW, IRC_SPEC); + else { + src1 = regs[reg1]; + src1h = regs[reg1|1]; + if (hst_lnt) { + hst[hst_p].src1 = src1; + hst[hst_p].src2 = src1h; + } + addr1 &= 0x3f; + while(addr1 > 0) { + src1 <<= 1; + if (src1h & MSIGN) + src1 |= 1; + src1h <<= 1; + addr1--; + } + regs[reg1|1] = src1h; + regs[reg1] = src1; + dest = src1; + } + break; + + case OP_SLDA: + if (reg & 1) + storepsw(OPPSW, IRC_SPEC); + else { + src1 = regs[reg1]; + src1h = regs[reg1|1]; + if (hst_lnt) { + hst[hst_p].src1 = src1; + hst[hst_p].src2 = src1h; + } + dest = src1 & MSIGN; + addr1 &= 0x3f; + cc = 0; + while(addr1 > 0) { + src1 <<= 1; + if ((src1 & MSIGN) != dest) + cc = 3; + if (src1h & MSIGN) + src1 |= 1; + src1h <<= 1; + addr1--; + } +save_dbl: + regs[reg1|1] = src1h; + regs[reg1] = src1; + dest = src1; + if (cc != 3 && (src1 | src1h) != 0) + cc = (src1 & MSIGN) ? 1 : 2; + if (cc == 3 && (pmsk & FIXOVR)) + storepsw(OPPSW, IRC_FIXOVR); + } + break; + + case OP_SRDA: + if (reg & 1) + storepsw(OPPSW, IRC_SPEC); + else { + src1 = regs[reg1]; + src1h = regs[reg1|1]; + dest = src1 & MSIGN; + addr1 &= 0x3f; + cc = 0; + while(addr1 > 0) { + src1h >>= 1; + if (src1 & 1) + src1h |= MSIGN; + src1 >>= 1; + src1 |= dest; + addr1--; + } + goto save_dbl; + } + break; + + case OP_STM: + reg &= 0xf; + for (;;) { + if (WriteFull(addr1, regs[reg1])) + goto supress; + if (reg1 == reg) + break; + reg1++; + reg1 &= 0xf; + addr1 += 4; + } ; + break; + + case OP_LM: + reg &= 0xf; + for (;;) { + if (ReadFull(addr1, ®s[reg1])) + goto supress; + if (reg1 == reg) + break; + reg1++; + reg1 &= 0xf; + addr1 += 4; + }; + break; + + case OP_NC: + case OP_OC: + case OP_XC: + cc = 0; + /* Fall through */ + + case OP_MVN: + case OP_MVZ: + case OP_MVC: +//fprintf(stderr, "Mov %x %x %x, ", op, addr1, addr2); + do { + if (ReadByte(addr2, &src1)) + break; + if (op != OP_MVC) { + if (ReadByte(addr1, &dest)) + break; + switch(op) { + case OP_MVZ: dest = (dest & 0x0f) | (src1 & 0xf0); break; + case OP_MVN: dest = (dest & 0xf0) | (src1 & 0x0f); break; + case OP_NC: dest &= src1; if (dest != 0) cc = 1; break; + case OP_OC: dest |= src1; if (dest != 0) cc = 1; break; + case OP_XC: dest ^= src1; if (dest != 0) cc = 1; break; + } + } else { + dest = src1; + } +//fprintf(stderr, " %x -> %x", src1, dest); + if (WriteByte(addr1, dest)) + break; + addr1++; + addr2++; + reg--; + } while (reg != 0xff); +//fprintf(stderr, "\n\r"); + break; + + case OP_CLC: + cc = 0; +//fprintf(stderr, "CLC %d %06x %06x - ", reg+1, addr1, addr2); + do { + if (ReadByte(addr1, &src1)) + break; + if (ReadByte(addr2, &src2)) + break; +//fprintf(stderr, "%02x %02x, ", src1 &0xff, src2 &0xff); + if (src1 != src2) { + dest = src1 - src2; + cc = (dest & MSIGN) ? 1 : (dest == 0) ? 0 : 2; + break; + } + addr1++; + addr2++; + reg--; + } while(reg != 0xff); +//fprintf(stderr, "cc=%d\n\r",cc); + break; + + case OP_TR: + do { + if (ReadByte(addr1, &src1)) + break; + if (ReadByte(addr2 + (src1 & 0xff), &dest)) + break; + if (WriteByte(addr1, dest)) + break; + addr1++; + reg--; + } while (reg != 0xff); + break; + + case OP_TRT: + cc = 0; + do { + if (ReadByte(addr1, &src1)) + break; + if (ReadByte(addr2 + (src1 & 0xff), &dest)) + break; + if (dest != 0) { + regs[1] &= 0xff000000; + regs[1] |= addr1 & 0xffffff; + regs[2] &= 0xffffff00; + regs[2] |= dest & 0xff; + cc = (reg == 0) ? 2 : 1; + break; + } + addr1++; + reg--; + } while(reg != 0xff); + break; + + case OP_PACK: + reg &= 0xf; +//fprintf(stderr, "PACK %d, %d %06x %06x - ", reg, reg1, addr1, addr2); + addr2 += reg; + addr1 += reg1; +//fprintf(stderr, " %06x %06x - ", addr1, addr2); + /* Flip first location */ + if (ReadByte(addr2, &dest)) + break; +//fprintf(stderr, "%02x ", dest &0xff); + dest = ((dest >> 4) & 0xf) | ((dest << 4) & 0xf0); + if (WriteByte(addr1, dest)) + break; +//fprintf(stderr, "%02x, ", dest &0xff); + addr1--; + addr2--; + dest = 0; + while(reg != 0 && reg1 != 0) { +//fprintf(stderr, "(%06x %06x) - ", addr1, addr2); + if (ReadByte(addr2, &dest)) + goto supress; +//fprintf(stderr, "%02x ", dest &0xff); + dest &= 0xf; + addr2--; + reg--; + if (reg != 0) { + if (ReadByte(addr2, &src1)) + goto supress; +//fprintf(stderr, "%02x ", src1 &0xff); + dest |= (src1 << 4) & 0xf0; + addr2--; + reg--; + } + if (WriteByte(addr1, dest)) + goto supress; +//fprintf(stderr, "->%02x, ", dest &0xff); + addr1--; + reg1--; + }; + dest = 0; + while(reg1 != 0) { + if (WriteByte(addr1, dest)) + break; +//fprintf(stderr, "%02x, ", dest &0xff); + addr1--; + reg1--; + }; +//fprintf(stderr, "\n\r"); + break; + + case OP_UNPK: + reg &= 0xf; +//fprintf(stderr, "UNPK %d, %d %06x %06x - ", reg, reg1, addr1, addr2); + addr2 += reg; + addr1 += reg1; + if (ReadByte(addr2, &dest)) + break; + dest = ((dest >> 4) & 0xf) | ((dest << 4) & 0xf0); +//fprintf(stderr, "(%06x %06x) - %02x ", addr1, addr2, dest &0xff); + if (WriteByte(addr1, dest)) + break; + addr1--; + addr2--; + src2 = (flags & ASCII)? 0x50 : 0xf0; + while(reg != 0 && reg1 != 0) { + if (ReadByte(addr2, &dest)) + goto supress; + addr2--; + reg--; + src1 = (dest & 0xf) | src2; +//fprintf(stderr, "(%06x %06x) - %02x ", addr1, addr2, src1); + if (WriteByte(addr1, src1)) + goto supress; + addr1--; + reg1--; + if (reg1 != 0) { + src1 = ((dest >> 4) & 0xf) | src2; +//fprintf(stderr, "(%06x %06x) - %02x ", addr1, addr2, src1); + if (WriteByte(addr1, src1)) + goto supress; + addr1--; + reg1--; + } + }; + while(reg1 != 0) { + if (WriteByte(addr1, src2)) + break; + addr1--; + reg1--; + }; +//fprintf(stderr, "\n\r"); + break; + + case OP_MVO: + reg &= 0xf; + addr2 += reg; + addr1 += reg1; + if (ReadByte(addr1, &dest)) + break; + if (ReadByte(addr2, &src1)) + break; + addr2--; + dest = (dest & 0xf) | ((src1 << 4) & 0xf0); + WriteByte(addr1, dest); + addr1--; + while(reg1 != 0) { + dest = (src1 >> 4) & 0xf; + if (reg != 0) { + if (ReadByte(addr2, &src1)) + break; + addr2--; + reg--; + } else + src1 = 0; + dest |= (src1 << 4) & 0xf0; + if (WriteByte(addr1, dest)) + break; + reg1--; + addr1--; + }; + break; + + case OP_CVB: + if (ReadFull(addr1, &src1)) + break; + if (ReadFull(addr1+4, &src1h)) + break; +//fprintf(stderr, "CVB: %08x %08x ", src1, src2); + fill = src1h & 0xf; /* Save away sign */ + if (fill < 0xA) { + storepsw(OPPSW, IRC_DATA); +//fprintf(stderr, "\n\r"); + break; + } + if (fill == 0xB || fill == 0xD) + fill = 1; + else + fill = 0; +//fprintf(stderr, "%d ", fill); + dest = 0; + /* Convert upper first */ + for(temp = 28; temp >= 0; temp-=4) { + int d = (src1 >> temp) & 0xf; +//fprintf(stderr, "%d %d ", temp, d); + if (d > 0xA) { + storepsw(OPPSW, IRC_DATA); +//fprintf(stderr, "\n\r"); + break; + } + dest = (dest * 10) + d; + } +//fprintf(stderr, "- "); + /* Convert lower */ + for(temp = 28; temp > 0; temp-=4) { + int d = (src1h >> temp) & 0xf; +//fprintf(stderr, "%d %d ", temp, d); + if (d > 0xA) { + storepsw(OPPSW, IRC_DATA); +//fprintf(stderr, "\n\r"); + break; + } + dest = (dest * 10) + d; + } + if (dest & MSIGN) { + storepsw(OPPSW, IRC_FIXDIV); + reason =1; +//fprintf(stderr, "\n\r"); + break; + } + if (fill) + dest = -dest; + regs[reg1] = dest; +//fprintf(stderr, "%d \n\r", dest); + break; + + case OP_CVD: + dest = regs[reg1]; + src1 = 0; + src1h = 0; +//fprintf(stderr, "CVD: %08x ", dest); + if (dest & MSIGN) { + dest = -dest; + fill = 1; + } else + fill = 0; +//fprintf(stderr, "%d ", fill); + temp = 4; + while (dest != 0) { + int d = dest % 10; + dest /= 10; + if (temp > 32) + src1 |= (d << (temp - 32)); + else + src1h |= (d << temp); + temp += 4; +//fprintf(stderr, "%d %d ", temp, d); + } + if (fill) { + src1h |= ((flags & ASCII)? 0xb : 0xd); + } else { + src1h |= ((flags & ASCII)? 0xa : 0xc); + } +//fprintf(stderr, "-> %08x %08x\n\r", src1, src2); + + if (WriteFull(addr1, src1)) + break; + WriteFull(addr1+4, src1h); + break; + + case OP_ED: + case OP_EDMK: + ReadByte(addr1, &src1); + fill = src1; + addr1++; + src2 = 0; + src1 = 1; + cc = 0; + while(reg != 0) { + uint8 t; + uint32 temp; + ReadByte(addr1, &temp); + t = temp; + if (src1) { + ReadByte(addr2, &dest); + addr2--; + reg --; + } + if (t == 0x21) { + src2 = 1; + t = 0x20; + if (op == OP_EDMK) + regs[1] = addr1; + } + if (t == 0x20) { + if (src2 || (dest >> (4*src1)) & 0xf) { + t = (dest >> (4*src1)) & 0xf; + t |= (flags & ASCII)?0x5:0xf; + if (!src2) { + src2 = 1; + if (op == OP_EDMK) + regs[1] = addr1; + } + if (t & 0xf) + cc = 2; + } else + t = fill; + WriteByte(addr1, t); + } else if (t == 0x22) { + src2 = 0; + t = fill; + WriteByte(addr1, t); + } + addr1--; + reg --; + src1 = !src1; + }; + dest &= 0xf; + if (cc != 0 && (dest == 0xd || dest == 0xb)) + cc = 1; + break; + + case OP_EX: + if (addr1 & 1) { + storepsw(OPPSW, IRC_SPEC); + break; + } + if (ReadHalf(addr1, &dest)) + break; + if (reg1) { + dest |= src1 & 0xff; + } + reg = dest & 0xff; + reg1 = (reg >> 4) & 0xf; + op = dest >> 8; + if (hst_lnt) { + hst[hst_p].cc = cc; + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = addr1 | HIST_PC; + hst[hst_p].inst[0] = dest; + } + addr1 += 2; + if (op == OP_EX) + storepsw(OPPSW, IRC_EXEC); + /* Check if RX, RR, SI, RS, SS opcode */ + else { + if (op & 0xc0) { + if (ReadHalf(addr1, &src1)) + break; + addr1+=2; + /* Check if SS */ + if ((op & 0xc0) == 0xc0) { + if(ReadHalf(addr1, &src2)) + break; + addr2 = src2; + } + addr1 = src1; + if (hst_lnt) { + hst[hst_p].inst[1] = addr1; + hst[hst_p].inst[2] = addr2; + } + } + goto opr; + } + break; + + + /* Floating Half register */ + case OP_HDR: + src2h >>= 1; + if (src2 & 1) + src2h |= MSIGN; + /* Fall through */ + + case OP_HER: + src2 = (src2 & (EMASK|MSIGN)) | ((src2 & MMASK) >> 1); + /* Fall through */ + + /* Floating Load register */ + case OP_LER: + case OP_LDR: + case OP_LE: + case OP_LD: + if ((op & 0x10) == 0) + fpregs[reg1|1] = src2h; + fpregs[reg1] = src2; + break; + + /* Floating Load register change sign */ + case OP_LPDR: + case OP_LNDR: + case OP_LTDR: + case OP_LCDR: + case OP_LPER: + case OP_LNER: + case OP_LTER: + case OP_LCER: + if ((op & 0x2) == 0) /* LP, LT */ + src2 &= ~MSIGN; + if ((op & 0x1)) /* LN, LC */ + src2 ^= MSIGN; + cc = 0; + src1 = src2 & MMASK; + if ((op & 0x10) == 0) { + fpregs[reg1|1] = src2h; + src1 |= src2h; + } + if (src1 != 0) + cc = (src2 & MSIGN) ? 1 : 2; + fpregs[reg1] = src2; + break; + + /* Floating Store register */ + case OP_STD: + if (WriteFull(addr2 + 4, src1h)) + break; + /* Fall through */ + + case OP_STE: + WriteFull(addr2, src1); + break; + + /* Floating Compare */ + case OP_CE: /* 79 */ + case OP_CD: /* 69 */ + case OP_CER: /* 39 */ + case OP_CDR: /* 29 */ + + /* Floating Subtract */ + case OP_SE: /* 7B */ + case OP_SD: /* 6B */ + case OP_SW: /* 6F */ + case OP_SU: /* 7F */ + case OP_SER: /* 3B */ + case OP_SDR: /* 2B */ + case OP_SWR: /* 2F */ + case OP_SUR: /* 3F */ + src2 ^= MSIGN; + /* Fall through */ + + /* Floating Add */ + case OP_AE: /* 7A */ + case OP_AD: /* 6A */ + case OP_AW: /* 6E */ + case OP_AU: /* 7E */ + case OP_AER: /* 3A */ + case OP_ADR: /* 2A */ + case OP_AWR: /* 2E */ + case OP_AUR: /* 3E */ + /* Extract numbers and adjust */ + e1 = (src1 & EMASK) >> 24; + e2 = (src2 & EMASK) >> 24; + fill = 0; + if (src1 & MSIGN) + fill |= 1; + if (src2 & MSIGN) + fill |= 2; + /* Create gaurd digit */ + src1 = ((src1 & MMASK) << 4) | ((src1h >> 28) & 0xf); + src2 = ((src2 & MMASK) << 4) | ((src2h >> 28) & 0xf); + src1h &= XMASK; + src2h &= XMASK; + /* Shift src2 right if src1 larger expo - expo */ + while (e1 > e2) { + src2h >>= 4; + src2h |= (src2 & 0xf) << 24; + src2 >>= 4; + e2 ++; + } + /* Shift src1 right if src2 larger expo - expo */ + while (e2 > e1) { + src1h >>= 4; + src1h |= (src1 & 0xf) << 24; + src1 >>= 4; + e1 ++; + } + /* Exponents should be equal now. */ + /* Add results */ + if (fill == 2 || fill == 1) { + /* Different signs do subtract */ + src1 ^= XMASK; + src1h ^= XMASK; + src1h++; + if (src1 & CMASK) { + src1++; + src1h &= XMASK; + } + src1h += src2h; + if (src1h & CMASK) { + src1 += ((src1h >> 28) & 0xf); + src1h &= XMASK; + } + src1 += src2; + if (src1 & CMASK) + src1 ^= CMASK; + else { + fill ^= 2; + src1 ^= XMASK; + src1h ^= XMASK; + src1h++; + if (src1 & CMASK) { + src1++; + src1h &= XMASK; + } + } + } else { + src1h += src2h; + if (src1h & CMASK) { + src1 += ((src1h >> 28) & 0xf); + src1h &= XMASK; + } + src1 += src2; + } + /* If src1 not normal shift left + expo */ + if (src1 & CMASK) { + src1h >>= 4; + src1h |= (src1 & 0xf) << 24; + src1 >>= 4; + e1 ++; + } + /* Compute sign of result */ + cc = 0; + if ((src1h | src1) != 0) + cc = (temp & 2) ? 1 : 2; + /* Set condition codes */ + /* If (op & 0xF) == 0x9, compare */ + if ((op & 0xF) == 0x9) /* Compare operator */ + break; /* All done */ + /* If (op & 0xE) != 0xE, normalize */ + if ((op & 0xE) != 0xE && cc != 0) { /* Normalize operator */ + while ((src1 & EMASK) == 0) { + src1 = (src1 << 4) | ((src1h >> 24) & 0xf); + src1h <<= 4; + src1h &= XMASK; + e1 --; + } + } + /* Store result */ + src1h |= (src1 & 0xf) << 28; + src1 >>= 4; + /* If exp < 0, underflow */ + /* If exp > 64 overflow */ + src1 |= (e1 << 24) & EMASK; + if (fill & 2) + src1 |= MSIGN; + if ((op & 0x10) == 0) { + fpregs[reg1|1] = src2h; + src1 |= src2h; + } + fpregs[reg1] = src2; + break; + + /* Multiply */ + case OP_MDR: + case OP_MER: + case OP_ME: + case OP_MD: + + /* Divide */ + case OP_DER: + case OP_DDR: + case OP_DD: + case OP_DE: + + /* Decimal operations */ + case OP_CP: + case OP_SP: + case OP_ZAP: + case OP_AP: + /* Get sign of second operand */ + /* If op & 1 flip sign */ + /* Get sign of first operand */ + /* Compute sign of result */ + case OP_MP: + case OP_DP: +// storepsw(OPPSW, IRC_OPR); + // goto supress; + break; + + /* Extended precision load round */ + case OP_LRER: + case OP_LRDR: + case OP_SXR: + case OP_AXR: + case OP_MXR: + case OP_MXDR: + case OP_MXD: + default: + reason=1; + storepsw(OPPSW, IRC_OPR); + goto supress; + } + + if (hst_lnt) { + hst[hst_p].dest = dest; + hst[hst_p].cc = cc; + } + + if (irqaddr != 0) { +supress: + src1 = M[irqaddr>>2]; + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = irqaddr | HIST_LPW; + hst[hst_p].src1 = src1; + } + irqaddr += 4; + src2 = M[irqaddr>>2]; + if (hst_lnt) { + hst[hst_p].src2 = src2; + } + sysmsk = (src1 >> 24) & 0xff; + st_key = (src1 >> 16) & 0xf0; + flags = (src1 >> 16) & 0xf; + irqcode = 0; + irqaddr = 0; + pmsk = (src2 >> 24) & 0xcf; + cc = (src2 >> 28) & 0x3; + PC = src2 & 0xffffff; + irq_pend = 1; + } + sim_interval--; + } +} + +/* Reset */ + +t_stat cpu_reset (DEVICE *dptr) +{ + st_key = cc = pmsk = irqcode = flags = irqaddr = loading = 0; + chan_set_devs(); + if (M == NULL) { /* first time init? */ + sim_brk_types = sim_brk_dflt = SWMASK ('E'); + M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); + if (M == NULL) + return SCPE_MEM; + } + return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +int32 st; +uint32 addr = (uint32) exta; +uint32 byte; + +if (vptr == NULL) + return SCPE_ARG; + /* Ignore high order bits */ + addr &= 0xffffff; + if (addr >= MEMSIZE) + return SCPE_NXM; + + byte = M[addr >> 2] >> (8 * (3 - (addr & 0x3))); + byte &= 0xff; +*vptr = byte; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +int32 st; +uint32 addr = (uint32) exta; + +if (WriteByte (addr, val)) + return SCPE_NXM; +return SCPE_OK; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 mc = 0; +int32 i, clim; +uint32 *nM = NULL; +int32 max = MEMSIZE >> 2; + +val = val >> UNIT_V_MSIZE; +val = 16 * 1024 * val; +if ((val <= 0) || (val > MAXMEMSIZE)) + return SCPE_ARG; +for (i = val>>2; i < max; i++) + mc = mc | M[i]; +if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; +nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); +if (nM == NULL) + return SCPE_MEM; +clim = (val < MEMSIZE)? val >> 2: max; +for (i = 0; i < clim; i++) + nM[i] = M[i]; +free (M); +M = nM; +fprintf(stderr, "Mem size=%x\n\r", val); +MEMSIZE = val; +cpu_unit.flags &= ~UNIT_MSIZE; +cpu_unit.flags |= val / (16 * 1024); +reset_all (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].pc = 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; + const char *cptr = (const 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, "PC A1 A2 D1 D2 RESULT CC\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + int i; + fprintf(st, "%06x %06x %06x %08x %08x %08x %1x %04x ", + h->pc & PAMASK, h->addr1 & PAMASK, h->addr2 & PAMASK, + h->src1, h->src2, h->dest, h->cc, h->inst[0]); + if ((h->op & 0xc0) != 0) + fprintf(st, "%04x ", h->inst[1]); + else + fprintf(st, " "); + if ((h->op & 0xc0) == 0xc0) + fprintf(st, "%04x ", h->inst[2]); + else + fprintf(st, " "); + fprintf(st, " "); + fprint_inst(st, h->inst); + fputc('\n', st); /* end line */ + } /* end else instruction */ + if (h->pc & HIST_LPW) { /* load PSW */ + int i; + fprintf(st, " LPSW %06x %08x %08x\n", h->pc & PAMASK, h->src1, h->src2); + } /* end else instruction */ + if (h->pc & HIST_SPW) { /* load PSW */ + int i; + fprintf(st, " SPSW %06x %08x %08x\n", h->pc & PAMASK, h->src1, h->src2); + } /* 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, "IBM360 CPU\n\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ + return "IBM 360 CPU"; +} + diff --git a/IBM360/ibm360_dasd.c b/IBM360/ibm360_dasd.c new file mode 100644 index 0000000..5abfb53 --- /dev/null +++ b/IBM360/ibm360_dasd.c @@ -0,0 +1,1295 @@ +/* ibm360_dasd.c: IBM 360 2311/2314 Disk controller + + Copyright (c) 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. + + Structure of a disk. See Hercules CKD disks. + + Numbers are stored least to most significant. + + Devid = "CKD_P370" + + uint8 devid[8] device header. + uint32 heads number of heads per cylinder + uint32 tracksize size of track + uint8 devtype Hex code of last two digits of device type. + uint8 fileseq always 0. + uint16 highcyl highest cylinder. + + uint8 resv[492] pad to 512 byte block + + Each Track has: + uint8 bin Track header. + uint16 cyl Cylinder number + uint16 head Head number. + + Each Record has: + uint16 cyl Cylinder number <- tpos + uint16 head Head number + uint8 rec Record id. + uint8 klen Length of key + uint16 dlen Length of data + + uint8 key[klen] Key data. + uint8 data[dlen] Data len. + + cpos points to where data is actually read/written from + + Pad to being track to multiple of 512 bytes. + + Last record has cyl and head = 0xffffffff + +*/ + +#include "ibm360_defs.h" + +#ifdef NUM_DEVS_DASD +#define UNIT_V_TYPE (UNIT_V_UF + 0) +#define UNIT_TYPE (0xf << UNIT_V_TYPE) + +#define GET_TYPE(x) ((UNIT_TYPE & (x)) >> UNIT_V_TYPE) +#define SET_TYPE(x) (UNIT_TYPE & ((x) << UNIT_V_TYPE)) +#define UNIT_DASD UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | \ + UNIT_FIX | SET_TYPE(6) + + +/* u3 */ +#define DK_NOP 0x03 /* Nop operation */ +#define DK_RELEASE 0x17 /* Release from channel */ +#define DK_RESTORE 0x13 /* Restore */ +#define DK_SEEK 0x07 /* Seek */ +#define DK_SEEKCYL 0x0B /* Seek Cylinder */ +#define DK_SEEKHD 0x1B /* Seek Head */ +#define DK_SETMSK 0x1f /* Set file mask */ +#define DK_SPACE 0x0f /* Space record */ +#define DK_SRCH_HAEQ 0x39 /* Search HA equal */ +#define DK_SRCH_IDEQ 0x31 /* Search ID equal */ +#define DK_SRCH_IDGT 0x51 /* Search ID greater */ +#define DK_SRCH_IDGE 0x71 /* Search ID greater or equal */ +#define DK_SRCH_KYEQ 0x29 /* Search Key equal */ +#define DK_SRCH_KYGT 0x49 /* Search Key greater */ +#define DK_SRCH_KYGE 0x69 /* Search Key greater or equal */ +#define DK_RD_IPL 0x02 /* Read IPL record */ +#define DK_RD_HA 0x1A /* Read home address */ +#define DK_RD_CNT 0x12 /* Read count */ +#define DK_RD_R0 0x16 /* Read R0 */ +#define DK_RD_D 0x06 /* Read Data */ +#define DK_RD_KD 0x0e /* Read key and data */ +#define DK_RD_CKD 0x1e /* Read count, key and data */ +#define DK_WR_HA 0x19 /* Write home address */ +#define DK_WR_R0 0x15 /* Write R0 */ +#define DK_WR_D 0x05 /* Write Data */ +#define DK_WR_KD 0x0d /* Write key and data */ +#define DK_WR_CKD 0x1d /* Write count, key and data */ +#define DK_WR_SCKD 0x01 /* Write special count, key and data */ +#define DK_ERASE 0x11 /* Erase to end of track */ +#define DK_MT 0x80 /* Multi track flag */ + +#define DK_INDEX 0x100 /* Index seen in command */ +#define DK_NOEQ 0x200 /* Not equal compare */ +#define DK_HIGH 0x400 /* High compare */ +#define DK_PARAM 0x800 /* Parameter in u4 */ +#define DK_MSET 0x1000 /* Mode set command already */ +#define DK_SHORTSRC 0x2000 /* Last search was short */ +#define DK_SRCOK 0x4000 /* Last search good */ +#define DK_CYL_DIRTY 0x8000 /* Current cylinder dirty */ + +#define DK_MSK_INHWR0 0x00 /* Inhbit writing of HA/R0 */ +#define DK_MSK_INHWRT 0x40 /* Inhbit all writes */ +#define DK_MSK_ALLWRU 0x80 /* Allow all updates */ +#define DK_MSK_ALLWRT 0xc0 /* Allow all writes */ +#define DK_MSK_WRT 0xc0 /* Write mask */ + +#define DK_MSK_SKALLSKR 0x00 /* Allow all seek/recal */ +#define DK_MSK_SKALLCLY 0x08 /* Allow cyl/head only */ +#define DK_MSK_SKALLHD 0x10 /* Allow head only */ +#define DK_MSK_SKNONE 0x18 /* Allow no seeks */ +#define DK_MSK_SK 0x18 /* Seek mask */ + + +/* u4 */ +/* Holds the current track and head */ +#define DK_V_TRACK 8 +#define DK_M_TRACK 0x3ff00 /* Max 1024 cylinders */ +#define DK_V_HEAD 0 +#define DK_M_HEAD 0xff /* Max 256 heads */ +#define DK_V_FILEMSK 18 +#define DK_M_FILEMSK 0xFF /* File mask */ + +/* u5 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x01 /* Command reject */ +#define SNS_INTVENT 0x02 /* Unit intervention required */ +#define SNS_BUSCHK 0x04 /* Parity error on bus */ +#define SNS_EQUCHK 0x08 /* Equipment check */ +#define SNS_DATCHK 0x10 /* Data Check */ +#define SNS_OVRRUN 0x20 /* Data overrun */ +#define SNS_TRKCND 0x40 /* Track Condition */ +#define SNS_SEEKCK 0x80 /* Seek Check */ + +/* Sense byte 1 */ +#define SNS_DCCNT 0x01 /* Data Check Count */ +#define SNS_TRKOVR 0x02 /* Track Overrun */ +#define SNS_ENDCYL 0x04 /* End of Cylinder */ +#define SNS_INVSEQ 0x08 /* Invalid Sequence */ +#define SNS_NOREC 0x10 /* No record found */ +#define SNS_WRP 0x20 /* Write Protect */ +#define SNS_ADDR 0x40 /* Missing Address Mark */ +#define SNS_OVRINC 0x80 /* Overflow Incomplete */ + +/* Sense byte 2 */ +#define SNS_BYTE2 0x00 /* Diags Use */ +/* Sense byte 3 */ +#define SNS_BYTE3 0x00 /* Diags Use */ + +/* saved in state field of data */ +/* Record position, high 4 bits, low internal short count */ +#define DK_POS_INDEX 0x00 /* At Index Mark */ +#define DK_POS_HA 0x10 /* In home address (c) */ +#define DK_POS_CNT 0x20 /* In count (c) */ +#define DK_POS_KEY 0x30 /* In Key area */ +#define DK_POS_DATA 0x40 /* In Data area */ +#define DK_POS_AM 0x50 /* Address mark before record */ +#define DK_POS_END 0x80 /* Past end of data */ +#define DK_POS_SEEK 0xF0 /* In seek */ + +/* u6 holds last command */ +/* Held in ccyl entry */ + +/* Pointer held in up7 */ +struct dasd_t +{ + uint8 *cbuf; /* Cylinder buffer */ + uint32 cpos; /* Position of head of cylinder in file */ + uint32 tstart; /* Location of start of track */ + uint16 ccyl; /* Current Cylinder number */ + uint16 cyl; /* Cylinder head at */ + uint16 tpos; /* Track position */ + uint16 rpos; /* Start of current record */ + uint16 dlen; /* remaining in data */ + uint16 tsize; /* Size of one track include rounding */ + uint8 state; /* Current state */ + uint8 klen; /* remaining in key */ + uint8 filemsk; /* Current file mask */ + uint8 rec; /* Current record number */ + uint16 count; /* Remaining in current operation */ +}; + +struct disk_t +{ + char *name; /* Type Name */ + int cyl; /* Number of cylinders */ + int heads; /* Number of heads/cylinder */ + unsigned int bpt; /* Max bytes per track */ + uint8 dev_type; /* Device type code */ +} +disk_type[] = +{ + {"2301", 1, 200, 20483, 0x01}, /* 4.1 M */ + {"2302", 250, 46, 4984, 0x02}, /* 57.32 M 50ms, 120ms/10, 180ms> 10 */ + {"2303", 80, 10, 4984, 0x03}, /* 4.00 M */ + {"2305", 48, 8, 14568, 0x05}, /* 5.43 M */ + {"2305-2",96, 8, 14858, 0x05}, /* 11.26 M */ + {"2311", 202, 10, 3625, 0x11}, /* 7.32 M 156k/s 30 ms 145 full */ + {"2314", 203, 20, 7294, 0x14}, /* 29.17 M */ + {"3330", 411, 19, 13165, 0x30}, /* 100.00 M */ + {"3330-2",815, 19, 13165, 0x30}, +#if 0 + {"3340", 349, 12, 8535}, /* 34.94 M */ + {"3340-2",698, 12, 8535}, /* 69.89 M */ + {"3350", 560, 12, 19254}, /* */ +#endif + {NULL, 0} +}; + + +/* Header block */ +struct dasd_header +{ + uint8 devid[8]; /* device header. */ + uint32 heads; /* number of heads per cylinder */ + uint32 tracksize; /* size of track */ + uint8 devtype; /* Hex code of last two digits of device type. */ + uint8 fileseq; /* always 0. */ + uint16 highcyl; /* highest cylinder. */ + uint8 resv[492]; /* pad to 512 byte block */ +}; + +uint8 dasd_startio(UNIT *uptr, uint16 chan) ; +uint8 dasd_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +uint8 dasd_haltio(uint16 addr); +t_stat dasd_srv(UNIT *); +t_stat dasd_boot(int32, DEVICE *); +void dasd_ini(UNIT *, t_bool); +t_stat dasd_reset(DEVICE *); +t_stat dasd_attach(UNIT *, CONST char *); +t_stat dasd_detach(UNIT *); +t_stat dasd_boot(int32, DEVICE *); +t_stat dasd_set_type(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat dasd_get_type(FILE * st, UNIT * uptr, int32 v, + CONST void *desc); +t_stat dasd_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *dasd_description (DEVICE *dptr); + +MTAB dasd_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &dasd_set_type, &dasd_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +UNIT dda_unit[] = { + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x130)}, /* 0 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x131)}, /* 1 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x132)}, /* 2 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x133)}, /* 3 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x134)}, /* 4 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x135)}, /* 5 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x136)}, /* 6 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x137)}, /* 7 */ +}; + +struct dib dda_dib = { 0xF8, NUM_UNITS_MT, NULL, dasd_startcmd, NULL, dda_unit, dasd_ini}; + +DEVICE dda_dev = { + "DA", dda_unit, NULL, dasd_mod, + NUM_UNITS_DASD, 8, 15, 1, 8, 8, + NULL, NULL, &dasd_reset, &dasd_boot, &dasd_attach, &dasd_detach, + &dda_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dasd_help, NULL, NULL, &dasd_description +}; + +#if NUM_DEVS_DASD > 1 +UNIT ddb_unit[] = { + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x230)}, /* 0 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x231)}, /* 1 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x232)}, /* 2 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x233)}, /* 3 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x234)}, /* 4 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x235)}, /* 5 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x236)}, /* 6 */ + {UDATA(&dasd_srv, UNIT_DASD, 0), 0, UNIT_ADDR(0x237)}, /* 7 */ +}; + +struct dib ddb_dib = { 0xF8, NUM_UNITS_MT, NULL, dasd_startcmd, NULL, ddb_unit, dasd_ini}; + +DEVICE ddb_dev = { + "DB", ddb_unit, NULL, dasd_mod, + NUM_UNITS_DASD, 8, 15, 1, 8, 8, + NULL, NULL, &dasd_reset, &dasd_boot, &dasd_attach, &dasd_detach, + &ddb_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dasd_help, NULL, NULL, &dasd_description +}; +#endif + +uint8 dasd_startio(UNIT *uptr, uint16 chan) { + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + + if ((uptr->u3 & 0xff) != 0) { + return SNS_BSY; + } + uptr->u3 &= ~(DK_INDEX|DK_NOEQ|DK_HIGH|DK_PARAM|DK_MSET); + uptr->u4 &= ~(DK_M_FILEMSK << DK_V_FILEMSK); + sim_debug(DEBUG_CMD, dptr, "start io unit=%d\n", unit); + return 0; +} + +uint8 dasd_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + uint8 ch; + + if ((uptr->u3 & 0xff) != 0) { + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %02x\n", unit, cmd); + + switch (cmd & 0x3) { + case 0x3: /* Control */ + if ((cmd & 0xfc) == 0 || cmd == DK_RELEASE) + return SNS_CHNEND|SNS_DEVEND; + case 0x1: /* Write command */ + case 0x2: /* Read command */ + uptr->u3 |= cmd; + return 0; + + case 0x0: /* Status */ + if (cmd == 0x4) { /* Sense */ + uptr->u3 |= cmd; + return 0; + } + break; + } + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle processing of disk requests. */ +t_stat dasd_srv(UNIT * uptr) +{ + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + struct dasd_t *data = (struct dasd_t *)(uptr->up7); + int unit = (uptr - dptr->units); + int cmd = uptr->u3 & 0xff; + int type = GET_TYPE(uptr->flags); + int state = data->state; + int trk; + int i; + int rd = ((cmd & 0x3) == 0x1) | ((cmd & 0x3) == 0x2); + uint8 *rec; + uint8 ch; + uint8 buf[8]; + + /* Check if read or write command, if so grab correct cylinder */ + if (rd && data->cyl != data->ccyl) { + int tsize = data->tsize * disk_type[type].heads; + if (uptr->u3 & DK_CYL_DIRTY) { + sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + sim_fwrite(data->cbuf, 1, tsize, uptr->fileref); + uptr->u3 &= ~DK_CYL_DIRTY; + } + data->ccyl = data->cyl; + data->cpos = sizeof(struct dasd_header) + (data->ccyl * tsize); + sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + sim_fread(data->cbuf, 1, tsize, uptr->fileref); + } + + switch(state & 0xF0) { + case DK_POS_INDEX: /* At Index Mark */ + /* Read and multi-track advance to next head */ + if ((cmd & 0x83) == 0x81) { + data->tstart += data->tsize; + uptr->u4 ++; + uptr->u3 &= ~DK_INDEX; + } + if (data->tstart > (data->tsize * disk_type[type].heads)) { + uptr->u5 |= (SNS_ENDCYL << 8); + data->tstart = 0; + uptr->u4 &= ~0xff; + uptr->u3 &= ~0xff; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + /* If INDEX set signal no record if read */ + if (rd && uptr->u3 & DK_INDEX) { + uptr->u5 |= (SNS_NOREC << 8); + uptr->u3 &= ~0xff; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->u3 |= DK_INDEX; + data->tpos = data->rpos = 0; + data->state = DK_POS_HA; + data->rec = 0; + sim_activate(uptr, 100); + break; + + case DK_POS_HA: /* In home address (c) */ + data->tpos = data->count; + if (data->count == 5) { + data->rpos = 5; + data->state = DK_POS_CNT; + rec = &data->cbuf[data->rpos]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + sim_activate(uptr, 100); + } else + sim_activate(uptr, 20); + break; + case DK_POS_CNT: /* In count (c) */ + data->tpos++; + if (data->count == 8) { + rec = &data->cbuf[data->rpos]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) { + state = DK_POS_END; + data->state = DK_POS_END; + } + data->klen = rec[5]; + data->dlen = (rec[6] << 8) | rec[7]; + data->state = DK_POS_KEY; + if (data->klen == 0) + data->state = DK_POS_DATA; + sim_activate(uptr, 100); + } else { + sim_activate(uptr, 20); + } + break; + case DK_POS_KEY: /* In Key area */ + if (data->count == data->klen) { + data->state = DK_POS_DATA; + sim_activate(uptr, 100); + } else { + sim_activate(uptr, 20); + data->tpos++; + } + break; + case DK_POS_DATA: /* In Data area */ + if (data->count == data->dlen) { + data->state = DK_POS_AM; + sim_activate(uptr, 100); + } else { + data->tpos++; + sim_activate(uptr, 20); + } + break; + case DK_POS_AM: /* Beginning of record */ + data->rpos += data->dlen + data->klen + 8; + data->rec++; + data->state = DK_POS_CNT; + rec = &data->cbuf[data->rpos]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + sim_activate(uptr, 100); + break; + case DK_POS_END: /* Past end of data */ + data->tpos++; + data->count = 0; + data->klen = 0; + data->dlen = 0; + if (data->tpos >= data->tsize) { + data->state = DK_POS_INDEX; + sim_activate(uptr, 100); + } else + sim_activate(uptr, 20); + break; + case DK_POS_SEEK: /* In seek */ + /* Compute delay based of difference. */ + /* Set next state = index */ + i = (uptr->u4 >> 8) - data->cyl; + if (i == 0) { + data->state = DK_POS_INDEX; + set_devattn(addr, SNS_DEVEND); + sim_activate(uptr, 100); + } else if (i > 0 ) { + if (i > 10) { + data->cyl += 10; + sim_activate(uptr, 4000); + } else { + data->cyl ++; + sim_activate(uptr, 500); + } + } else { + if (i < 10) { + data->cyl -= 10; + sim_activate(uptr, 4000); + } else { + data->cyl --; + sim_activate(uptr, 500); + } + } + break; + } + + switch (cmd & 0x7f) { + case 0: /* No command, stop tape */ + sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit); + break; + + case 0x4: + ch = uptr->u5 & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = (uptr->u5 >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = unit; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0; + chan_write_byte(addr, &ch) ; + uptr->u3 &= ~0xff; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + + case DK_SEEK: /* Seek */ + case DK_SEEKCYL: /* Seek Cylinder */ + case DK_SEEKHD: /* Seek Head */ + if ((uptr->u3 & DK_PARAM) != 0) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_DEVEND); + break; + } + + /* Check if seek valid */ + i = data->filemsk & DK_MSK_SK; + if (i == DK_MSK_SKNONE) { /* No seeks allowed, error out */ + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + uptr->u5 |= SNS_CMDREJ; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (i != DK_MSK_SKALLSKR) { /* Some restrictions */ + if ((cmd == DK_SEEKHD && i != DK_MSK_SKALLHD) || (cmd == DK_SEEK)) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + uptr->u5 |= SNS_CMDREJ; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + + /* Read in 6 character seek code */ + for (i = 0; i < 6; i++) { + if (chan_read_byte(addr, &buf[i])) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + uptr->u5 |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + trk = (buf[2] << 8) | buf[3]; + + /* Check if seek valid */ + if ((buf[0] | buf[1] | buf[4]) != 0 || trk == 0 || + trk > disk_type[type].cyl || buf[5] > disk_type[type].heads) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + uptr->u5 |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (cmd == DK_SEEKHD && ((uptr->u4 >> 8) & 0x7fff) != trk) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + uptr->u5 |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + chan_end(addr, SNS_CHNEND); + uptr->u3 |= DK_PARAM; + data->tstart = buf[5] * data->tsize; /* Point to start of record */ + uptr->u4 = (trk << 8) | buf[5]; + /* Check if on correct cylinder */ + if (trk != data->cyl) { + /* Do seek */ + data->state = DK_POS_SEEK; + } + return SCPE_OK; + + case DK_RESTORE: /* Restore */ + if ((uptr->u3 & DK_PARAM) != 0) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_DEVEND); + break; + } + if ((data->filemsk & DK_MSK_SK) != DK_MSK_SKALLSKR) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + uptr->u3 |= DK_PARAM; + uptr->u4 = 0; + data->tstart = 0; + chan_end(addr, SNS_CHNEND); + if (data->cyl != 0) { + /* Do a seek */ + data->state = DK_POS_SEEK; + } + return SCPE_OK; + + case DK_SETMSK: /* Set file mask */ + /* If mask already set, error */ + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + if (uptr->u3 & DK_MSET) { + uptr->u6 = 0; + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + /* Grab mask */ + if (chan_read_byte(addr, &ch)) { + uptr->u6 = 0; + uptr->u5 |= SNS_CMDREJ; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + /* Save */ + if ((ch & ~(DK_MSK_SK|DK_MSK_WRT)) != 0) { + uptr->u6 = 0; + uptr->u5 |= SNS_CMDREJ; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + data->filemsk = ch; + uptr->u3 |= DK_MSET; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + + case DK_SPACE: /* Space record */ + break; + case DK_SRCH_HAEQ: /* Search HA equal */ + if (state == DK_POS_INDEX) { + uptr->u3 &= ~DK_SRCOK; + uptr->u3 |= DK_PARAM; + break; + } + + if (uptr->u3 & DK_PARAM && state == DK_POS_HA) { + uptr->u3 &= ~DK_INDEX; + if (chan_read_byte(addr, &ch)) { + if (data->count != 0x5) + uptr->u3 |= DK_SHORTSRC; + } else if (ch != data->cbuf[data->tpos]) { + uptr->u3 |= DK_NOEQ; + } + if (data->count == 5 || uptr->u3 & DK_SHORTSRC) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + if (uptr->u3 & DK_NOEQ) + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + else { + uptr->u3 |= DK_SRCOK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_SMS); + } + } + } + break; + + case DK_RD_CNT: /* Read count */ + if (state == DK_POS_AM) { + uptr->u3 |= DK_PARAM; + } + + if (uptr->u3 & DK_PARAM && state == DK_POS_CNT) { + uptr->u3 &= ~DK_INDEX; + ch = data->cbuf[data->tpos]; + if (chan_write_byte(addr, &ch) || data->count == 8) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_SRCH_IDEQ: /* Search ID equal */ + case DK_SRCH_IDGT: /* Search ID greater */ + case DK_SRCH_IDGE: /* Search ID greater or equal */ + if (state == DK_POS_CNT) { + uptr->u3 &= ~(DK_SRCOK|DK_SHORTSRC); + uptr->u3 |= DK_PARAM; + } + + if (uptr->u3 & DK_PARAM) { + uptr->u3 &= ~DK_INDEX; + /* Wait for start of record */ + if (chan_read_byte(addr, &ch)) { + uptr->u3 |= DK_SHORTSRC; + } else if (ch != data->cbuf[data->tpos]) { + if ((uptr->u3 & DK_NOEQ) == 0) { + uptr->u3 |= DK_NOEQ; + if (ch > *rec) + uptr->u3 |= DK_HIGH; + } + } + if (data->count == 5 || uptr->u3 & DK_SHORTSRC) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + i = 0; + if ((cmd & 0x2) && (uptr->u3 & DK_NOEQ) == 0) + i = SNS_SMS; + if ((cmd & 0x4) && (uptr->u3 & DK_HIGH)) + i = SNS_SMS; + if (i) + uptr->u3 |= DK_SRCOK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|i); + } + } + break; + + case DK_SRCH_KYEQ: /* Search Key equal */ + case DK_SRCH_KYGT: /* Search Key greater */ + case DK_SRCH_KYGE: /* Search Key greater or equal */ + if (state == DK_POS_AM) + uptr->u3 &= ~DK_SRCOK; + if (state == DK_POS_KEY && data->count == 0) { + if (data->rec == 0 && (uptr->u3 & DK_SRCOK) == 0) + break; + uptr->u3 &= ~(DK_SRCOK|DK_SHORTSRC); + uptr->u3 |= DK_PARAM; + } + if (uptr->u3 & DK_PARAM) { + /* Wait for key */ + if (chan_read_byte(addr, &ch)) { + uptr->u3 |= DK_SHORTSRC; + } else if (ch != data->cbuf[data->tpos]) { + if ((uptr->u3 & DK_NOEQ) == 0) { + uptr->u3 |= DK_NOEQ; + if (ch > *rec) + uptr->u3 |= DK_HIGH; + } + } + if (data->count == data->klen || uptr->u3 & DK_SHORTSRC) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + i = 0; + if ((cmd & 0x2) && (uptr->u3 & DK_NOEQ) == 0) + i = SNS_SMS; + if ((cmd & 0x4) && (uptr->u3 & DK_HIGH)) + i = SNS_SMS; + if (i) + uptr->u3 |= DK_SRCOK; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|i); + } + } + break; + + case DK_RD_HA: /* Read home address */ + if (state == DK_POS_INDEX) { + uptr->u3 |= DK_PARAM; + } + + if (uptr->u3 & DK_PARAM && (state & 0xF0) == DK_POS_HA) { + uptr->u3 &= ~DK_INDEX; + ch = data->cbuf[data->tpos]; + if (chan_write_byte(addr, &ch) || (state & 0xF) == 5) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_RD_IPL: /* Read IPL record */ + if (data->count == 0 && state == DK_POS_CNT && data->rec == 1) { + uptr->u3 &= ~DK_INDEX; + uptr->u3 |= DK_PARAM; + } + + case DK_RD_R0: /* Read R0 */ + if (data->count == 0 && state == DK_POS_CNT && data->rec == 0) { + uptr->u3 |= DK_PARAM; + uptr->u3 &= ~DK_INDEX; + } + goto rd; + + case DK_RD_CKD: /* Read count, key and data */ + if (data->count == 0 && state == DK_POS_CNT) { + uptr->u3 |= DK_PARAM; + uptr->u3 &= ~DK_INDEX; + } + + case DK_RD_KD: /* Read key and data */ + if (data->count == 0 && state == DK_POS_KEY) { + uptr->u3 |= DK_PARAM; + uptr->u3 &= ~DK_INDEX; + } + + case DK_RD_D: /* Read Data */ + if (data->count == 0 && state == DK_POS_DATA) { + uptr->u3 |= DK_PARAM; + uptr->u3 &= ~DK_INDEX; + } + +rd: + if (uptr->u3 & DK_PARAM) { + /* Check for end of file */ + if (state == DK_POS_DATA && data->count == 0 && data->dlen == 0) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + ch = data->cbuf[data->tpos]; + if (chan_write_byte(addr, &ch)) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + if (state == DK_POS_DATA && data->count == data->dlen) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_WR_HA: /* Write home address */ + if (state == DK_POS_INDEX) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) != DK_MSK_ALLWRT) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + uptr->u3 |= DK_PARAM; + break; + } + + if (uptr->u3 & DK_PARAM) { + uptr->u3 &= ~DK_INDEX; + if (chan_read_byte(addr, &ch)) { + ch = 0; + } + data->cbuf[data->tpos] = ch; + if (data->count == 5) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + for(i = 0; i < 4; i++) + data->cbuf[data->tpos+i] = 0xff; + for(; i < 8; i++) + data->cbuf[data->tpos+i] = 0; + } + } + break; + + case DK_WR_R0: /* Write R0 */ + if ((state == DK_POS_CNT || state == DK_POS_END) + && data->rec == 0 && data->count == 0) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) != DK_MSK_ALLWRT) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->u6 == DK_WR_HA || + (uptr->u6 == DK_SRCH_HAEQ && + (uptr->u3 & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + state = data->state = DK_POS_CNT; + uptr->u3 |= DK_PARAM; + } else { + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + goto wrckd; + + case DK_WR_SCKD: /* Write special count, key and data */ + case DK_WR_CKD: /* Write count, key and data */ + if ((state == DK_POS_CNT || state == DK_POS_END) + && data->rec != 0 && data->count == 0) { + /* Check if command ok based on mask */ + i = data->filemsk & DK_MSK_WRT; + if (i != DK_MSK_ALLWRT || i != DK_MSK_INHWR0) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->u6 == DK_WR_R0 || uptr->u6 == DK_WR_CKD || + ((uptr->u6 & 0x3) == 1 && (uptr->u6 & 0xE0) != 0 && + (uptr->u3 & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + state = data->state = DK_POS_CNT; + uptr->u3 |= DK_PARAM; + } else { + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + +wrckd: + if (uptr->u3 & DK_PARAM) { + uptr->u3 &= ~DK_INDEX; + if (chan_read_byte(addr, &ch)) { + ch = 0; + } + data->cbuf[data->tpos] = ch; + if (state == DK_POS_CNT && data->count == 8) { + rec = &data->cbuf[data->rpos]; + data->klen = rec[5]; + data->dlen = (rec[6] << 8) | rec[7]; + data->state = DK_POS_KEY; + if (data->klen == 0) + data->state = DK_POS_DATA; + } else if (state == DK_POS_DATA && data->count == data->dlen) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + for(i = 0; i < 4; i++) + data->cbuf[data->tpos+i] = 0xff; + for(; i < 8; i++) + data->cbuf[data->tpos+i] = 0; + } + } + break; + + case DK_WR_KD: /* Write key and data */ + if ((state == DK_POS_KEY) && data->rec != 0 && data->count == 0) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) == DK_MSK_INHWRT) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((uptr->u6 & 0x13) == 0x11 && + (uptr->u3 & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + uptr->u3 |= DK_PARAM; + } else { + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + goto wr; + + case DK_WR_D: /* Write Data */ + if ((state == DK_POS_DATA) && data->rec != 0 && data->count == 0) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) == DK_MSK_INHWRT) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((uptr->u6 & 0x3) == 1 && (uptr->u6 & 0xE0) != 0 && + (uptr->u3 & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + uptr->u3 |= DK_PARAM; + } else { + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + +wr: + if (uptr->u3 & DK_PARAM) { + uptr->u3 &= ~DK_INDEX; + /* Check for end of file */ + if (state == DK_POS_DATA && data->count == 0 && data->dlen == 0) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + if (chan_read_byte(addr, &ch)) { + ch = 0; + } + data->cbuf[data->tpos] = ch; + if (state == DK_POS_DATA && data->count == data->dlen) { + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_ERASE: /* Erase to end of track */ + if (state == DK_POS_AM || state == DK_POS_END) { + /* Check if command ok based on mask */ + i = data->filemsk & DK_MSK_WRT; + if (i != DK_MSK_ALLWRT || i != DK_MSK_INHWR0) { + uptr->u5 |= SNS_CMDREJ; + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->u6 == DK_WR_R0 || uptr->u6 == DK_WR_CKD || + ((uptr->u6 & 0x3) == 1 && (uptr->u6 & 0xE0) != 0 && + (uptr->u3 & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + state = data->state = DK_POS_END; + uptr->u3 |= DK_PARAM; + } else { + uptr->u5 |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->u6 = 0; + uptr->u3 &= ~(0xff); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + + if (uptr->u3 & DK_PARAM) { + uptr->u3 &= ~DK_INDEX; + uptr->u6 = uptr->u3 & 0xff; + uptr->u3 &= ~(0xff|DK_PARAM); + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + /* Write end mark */ + for(i = 0; i < 4; i++) + data->cbuf[data->rpos+i] = 0xff; + for(; i < 8; i++) + data->cbuf[data->rpos+i] = 0; + } + break; + } + if (state == data->state) + data->count++; + else + data->count = 0; + return SCPE_OK; +} + +void +dasd_ini(UNIT * uptr, t_bool f) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + int i = GET_TYPE(uptr->flags); + uptr->capac = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; +} + +t_stat +dasd_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +int +dasd_format(UNIT * uptr) { + struct dasd_header hdr; + struct dasd_t *data; + int type = GET_TYPE(uptr->flags); + int tsize; + int cyl; + int hd; + int pos; + + if (!get_yn("Initialize dasd? [Y]", TRUE)) { + return 1; + } + memset(&hdr, 0, sizeof(struct dasd_header)); + strncpy(&hdr.devid[0], "CKD_P370", 8); + hdr.heads = disk_type[type].heads; + hdr.tracksize = (disk_type[type].bpt | 0x1ff) + 1; + hdr.devtype = disk_type[type].dev_type; + sim_fseek(uptr->fileref, 0, SEEK_SET); + sim_fwrite(&hdr, 1, sizeof(struct dasd_header), uptr->fileref); + if ((data = (struct dasd_t *)calloc(1, sizeof(struct dasd_t))) == 0) + return 1; + uptr->up7 = (void *)data; + tsize = hdr.tracksize * hdr.heads; + data->tsize = hdr.tracksize; + if ((data->cbuf = (uint8 *)calloc(tsize, sizeof(uint8))) == 0) + return 1; + for (cyl = 0; cyl < disk_type[type].cyl; cyl++) { + pos = 0; + for (hd = 0; hd < disk_type[type].heads; hd++) { + data->cbuf[pos++] = 0; + data->cbuf[pos++] = (cyl >> 8); + data->cbuf[pos++] = (cyl & 0xff); + data->cbuf[pos++] = (hd >> 8); + data->cbuf[pos++] = (hd & 0xff); + data->cbuf[pos++] = 0xff; + data->cbuf[pos++] = 0xff; + data->cbuf[pos++] = 0xff; + data->cbuf[pos++] = 0xff; + pos += data->tsize - 9; + } + sim_fwrite(data->cbuf, 1, tsize, uptr->fileref); + if ((cyl % 10) == 0) + fputc('.', stderr); + } + sim_fseek(uptr->fileref, sizeof(struct dasd_header), SEEK_SET); + sim_fread(data->cbuf, 1, tsize, uptr->fileref); + data->cpos = sizeof(struct dasd_header); + data->ccyl = 0; + data->ccyl = 0; + data->cyl = 2000; + data->state = DK_POS_SEEK; + sim_activate(uptr, 100); + fputc('\n', stderr); + fputc('\r', stderr); + return 0; +} + +t_stat +dasd_attach(UNIT * uptr, CONST char *file) +{ + uint16 addr = GET_UADDR(uptr->u3); + t_stat r; + int i; + struct dasd_header hdr; + struct dasd_t *data; + int tsize; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + if (sim_fread(&hdr, 1, sizeof(struct dasd_header), uptr->fileref) != + sizeof(struct dasd_header) || strncmp(&hdr.devid[0], "CKD_P370", 8) != 0) { + if (dasd_format(uptr)) { + detach_unit(uptr); + return SCPE_FMT; + } + return SCPE_OK; + } + + fprintf(stderr, "%8s %d %d %02x %d\n\r", hdr.devid, hdr.heads, hdr.tracksize, + hdr.devtype, hdr.highcyl); + for (i = 0; disk_type[i].name != 0; i++) { + tsize = (disk_type[i].bpt | 0x1ff) + 1; + if (hdr.devtype == disk_type[i].dev_type && hdr.tracksize == tsize && + hdr.heads == disk_type[i].heads) { + if (GET_TYPE(uptr->flags) != i) { + /* Ask if we should change */ + fprintf(stderr, "Wrong type %s\n\r", disk_type[i].name); + if (!get_yn("Update dasd type? [N]", FALSE)) { + detach_unit(uptr); + return SCPE_FMT; + } + uptr->flags &= ~UNIT_TYPE; + uptr->flags |= SET_TYPE(i); + uptr->capac = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; + } + break; + } + } + if (disk_type[i].name == 0) { + detach_unit(uptr); + return SCPE_FMT; + } + if ((data = (struct dasd_t *)calloc(1, sizeof(struct dasd_t))) == 0) + return 0; + uptr->up7 = (void *)data; + tsize = hdr.tracksize * hdr.heads; + data->tsize = hdr.tracksize; + if ((data->cbuf = (uint8 *)calloc(tsize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + sim_fseek(uptr->fileref, sizeof(struct dasd_header), SEEK_SET); + sim_fread(data->cbuf, 1, tsize * hdr.heads, uptr->fileref); + data->cpos = sizeof(struct dasd_header); + data->ccyl = 0; + data->cyl = 2000; + data->state = DK_POS_SEEK; + sim_activate(uptr, 100); + return SCPE_OK; +} + +t_stat +dasd_detach(UNIT * uptr) +{ + struct dasd_t *data = (struct dasd_t *)uptr->up7; + int type = GET_TYPE(uptr->flags); + + if (uptr->u3 & DK_CYL_DIRTY) { + sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + sim_fwrite(data->cbuf, 1, data->tsize * disk_type[type].heads, uptr->fileref); + uptr->u3 &= ~DK_CYL_DIRTY; + } + if (data != 0) { + free(data->cbuf); + free(data); + } + uptr->up7 = 0; + uptr->u3 = 0; + return detach_unit(uptr); +} + +t_stat +dasd_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? */ + return chan_boot(GET_UADDR(uptr->u3), dptr); +} + +/* Disk option setting commands */ + +t_stat +dasd_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->flags &= ~UNIT_TYPE; + uptr->flags |= SET_TYPE(i); + uptr->capac = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat +dasd_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(disk_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + + +t_stat dasd_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr) +{ + int i; + fprintf (st, "IBM 2840 Disk File Controller\n\n"); + fprintf (st, "Use:\n\n"); + fprintf (st, " sim> SET %sn TYPE=type\n", dptr->name); + 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].bpt * disk_type[i].heads * disk_type[i].cyl; + char sm = 'K'; + size /= 1024; + size = (10 * size) / 1024; + fprintf(st, " %-8s %4d.%1dMB\n", disk_type[i].name, size/10, size%10); + } + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + return SCPE_OK; +} + +const char *dasd_description (DEVICE *dptr) +{ + return "IBM 2840 disk file controller"; +} + +#endif diff --git a/IBM360/ibm360_defs.h b/IBM360/ibm360_defs.h new file mode 100644 index 0000000..1a4f8b0 --- /dev/null +++ b/IBM360/ibm360_defs.h @@ -0,0 +1,372 @@ +/* ibm360_defs.h: IBM 360 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 + ROBERT M SUPNIK 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 */ + +/* 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_INVINS 5 /* invalid instr */ +#define STOP_INVIOP 6 /* invalid I/O op */ +#define STOP_INDLIM 7 /* indirect limit */ +#define STOP_XECLIM 8 /* XEC limit */ +#define STOP_IOCHECK 9 /* IOCHECK */ +#define STOP_MMTRP 10 /* mm in trap */ +#define STOP_TRPINS 11 /* trap inst not BRM */ +#define STOP_RTCINS 12 /* rtc inst not MIN/SKR */ +#define STOP_ILLVEC 13 /* zero vector */ +#define STOP_CCT 14 /* runaway CCT */ + +/* Conditional error returns */ + +/* Memory */ + +#define MAXMEMSIZE (16*1024*1024) /* max memory size */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((x)) < MEMSIZE) + +/* Device information block */ +typedef struct dib { + uint8 mask; /* Device mask */ + uint8 numunits; /* Number of units */ + /* Start I/O */ + uint8 (*start_io)(UNIT *uptr, uint16 chan); + /* Start a command */ + uint8 (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd); + /* Stop I/O */ + uint8 (*halt_io)(UNIT *uptr); + UNIT *units; /* Pointer to units structure */ + void (*dev_ini)(UNIT *, t_bool); + +} DIB; + +#define DEV_V_ADDR DEV_V_UF /* Pointer to device address */ +#define DEV_ADDR_MASK (0x7ff << DEV_V_ADDR) +#define DEV_V_UADDR (DEV_V_UF + 12) /* Device address in Unit */ +#define DEV_UADDR (1 << DEV_V_UADDR) +#define GET_DADDR(x) (0x7ff & ((x) >> DEV_V_ADDR)) +#define DEV_ADDR(x) ((x) << DEV_V_ADDR) + +#define UNIT_V_ADDR 21 +#define UNIT_ADDR_MASK (0x7ff << UNIT_V_ADDR) +#define GET_UADDR(x) ((UNIT_ADDR_MASK & x) >> UNIT_V_ADDR) +#define UNIT_ADDR(x) ((x) << UNIT_V_ADDR) + +#define PROTECT_V UNIT_V_UF+12 +#define PROTECT (1 << PROTECT_V) + +/* Arithmetic */ + +/* Instruction format */ + +/* Globally visible flags */ + +/* I/O routine functions */ + + +/* low addresses */ +#define IPSW 0x00 /* IPSW */ +#define ICCW1 0x08 /* ICCW1 */ +#define ICCW2 0x10 /* ICCW2 */ +#define OEPSW 0x18 /* Exteranl old PSW */ +#define OSPSW 0x20 /* Supervisior call old PSW */ +#define OPPSW 0x28 /* Program old PSW */ +#define OMPSW 0x30 /* Machine check PSW */ +#define OIOPSW 0x38 /* IO old PSW */ +#define CSW 0x40 /* CSW */ +#define CAW 0x48 /* CAW */ +#define TIMER 0x50 /* timer */ +#define NEPSW 0x58 /* External new PSW */ +#define NSPSW 0x60 /* SVC new PSW */ +#define NPPSW 0x68 /* Program new PSW */ +#define NMPSW 0x70 /* Machine Check PSW */ +#define NIOPSW 0x78 /* IOPSW */ +#define DIAGAREA 0x80 /* Diag scan area. */ + +/* Opcode definitions */ +#define OP_SPM 0x04 +#define OP_BALR 0x05 +#define OP_BCTR 0x06 +#define OP_BCR 0x07 +#define OP_SSK 0x08 +#define OP_ISK 0x09 +#define OP_SVC 0x0A +#define OP_LPR 0x10 +#define OP_LNR 0x11 +#define OP_LTR 0x12 +#define OP_LCR 0x13 +#define OP_NR 0x14 +#define OP_CLR 0x15 +#define OP_OR 0x16 +#define OP_XR 0x17 +#define OP_CR 0x19 +#define OP_LR 0x18 +#define OP_AR 0x1A +#define OP_SR 0x1B +#define OP_MR 0x1C +#define OP_DR 0x1D +#define OP_ALR 0x1E +#define OP_SLR 0x1F +#define OP_LPDR 0x20 +#define OP_LNDR 0x21 +#define OP_LTDR 0x22 +#define OP_LCDR 0x23 +#define OP_HDR 0x24 +#define OP_LRDR 0x25 +#define OP_MXR 0x26 +#define OP_MXDR 0x27 +#define OP_LDR 0x28 +#define OP_CDR 0x29 +#define OP_ADR 0x2A +#define OP_SDR 0x2B +#define OP_MDR 0x2C +#define OP_DDR 0x2D +#define OP_AWR 0x2E +#define OP_SWR 0x2F +#define OP_LPER 0x30 +#define OP_LNER 0x31 +#define OP_LTER 0x32 +#define OP_LCER 0x33 +#define OP_HER 0x34 +#define OP_LRER 0x35 +#define OP_AXR 0x36 +#define OP_SXR 0x37 +#define OP_LER 0x38 +#define OP_CER 0x39 +#define OP_AER 0x3A +#define OP_SER 0x3B +#define OP_MER 0x3C +#define OP_DER 0x3D +#define OP_AUR 0x3E +#define OP_SUR 0x3F +#define OP_STH 0x40 +#define OP_LA 0x41 +#define OP_STC 0x42 +#define OP_IC 0x43 +#define OP_EX 0x44 +#define OP_BAL 0x45 +#define OP_BCT 0x46 +#define OP_BC 0x47 +#define OP_LH 0x48 +#define OP_CH 0x49 +#define OP_AH 0x4A +#define OP_SH 0x4B +#define OP_MH 0x4C +#define OP_CVD 0x4E +#define OP_CVB 0x4F +#define OP_ST 0x50 +#define OP_N 0x54 +#define OP_CL 0x55 +#define OP_O 0x56 +#define OP_X 0x57 +#define OP_L 0x58 +#define OP_C 0x59 +#define OP_A 0x5A +#define OP_S 0x5B +#define OP_M 0x5C +#define OP_D 0x5D +#define OP_AL 0x5E +#define OP_SL 0x5F +#define OP_STD 0x60 +#define OP_MXD 0x67 +#define OP_LD 0x68 +#define OP_CD 0x69 +#define OP_AD 0x6A +#define OP_SD 0x6B +#define OP_MD 0x6C +#define OP_DD 0x6D +#define OP_AW 0x6E +#define OP_SW 0x6F +#define OP_STE 0x70 +#define OP_LE 0x78 +#define OP_CE 0x79 +#define OP_AE 0x7A +#define OP_SE 0x7B +#define OP_ME 0x7C +#define OP_DE 0x7D +#define OP_AU 0x7E +#define OP_SU 0x7F +#define OP_SSM 0x80 +#define OP_LPSW 0x82 +#define OP_DIAG 0x83 +#define OP_BXH 0x86 +#define OP_BXLE 0x87 +#define OP_SRL 0x88 +#define OP_SLL 0x89 +#define OP_SRA 0x8A +#define OP_SLA 0x8B +#define OP_SRDL 0x8C +#define OP_SLDL 0x8D +#define OP_SRDA 0x8E +#define OP_SLDA 0x8F +#define OP_STM 0x90 +#define OP_TM 0x91 +#define OP_MVI 0x92 +#define OP_TS 0x93 +#define OP_NI 0x94 +#define OP_CLI 0x95 +#define OP_OI 0x96 +#define OP_XI 0x97 +#define OP_LM 0x98 +#define OP_SIO 0x9C +#define OP_TIO 0x9D +#define OP_HIO 0x9E +#define OP_TCH 0x9F +#define OP_MVN 0xD1 +#define OP_MVC 0xD2 +#define OP_MVZ 0xD3 +#define OP_NC 0xD4 +#define OP_CLC 0xD5 +#define OP_OC 0xD6 +#define OP_XC 0xD7 +#define OP_TR 0xDC +#define OP_TRT 0xDD +#define OP_ED 0xDE +#define OP_EDMK 0xDF +#define OP_MVO 0xF1 +#define OP_PACK 0xF2 +#define OP_UNPK 0xF3 +#define OP_ZAP 0xF8 +#define OP_CP 0xF9 +#define OP_AP 0xFA +#define OP_SP 0xFB +#define OP_MP 0xFC +#define OP_DP 0xFD + +/* Channel sense bytes */ +#define SNS_ATTN 0x80 /* Unit attention */ +#define SNS_SMS 0x40 /* Status modifier */ +#define SNS_CTLEND 0x20 /* Control unit end */ +#define SNS_BSY 0x10 /* Unit Busy */ +#define SNS_CHNEND 0x08 /* Channel end */ +#define SNS_DEVEND 0x04 /* Device end */ +#define SNS_UNITCHK 0x02 /* Unit check */ +#define SNS_UNITEXP 0x01 /* Unit exception */ + +/* Command masks */ +#define CMD_TYPE 0x3 /* Type mask */ +#define CMD_CHAN 0x0 /* Channel command */ +#define CMD_WRITE 0x1 /* Write command */ +#define CMD_READ 0x2 /* Read command */ +#define CMD_CTL 0x3 /* Control command */ +#define CMD_SENSE 0x4 /* Sense channel command */ +#define CMD_TIC 0x8 /* Transfer in channel */ +#define CMD_RDBWD 0xc /* Read backward */ + +#define STATUS_ATTN 0x8000 /* Device raised attention */ +#define STATUS_MOD 0x4000 /* Status modifier */ +#define STATUS_CTLEND 0x2000 /* Control end */ +#define STATUS_BUSY 0x1000 /* Device busy */ +#define STATUS_CEND 0x0800 /* Channel end */ +#define STATUS_DEND 0x0400 /* Device end */ +#define STATUS_CHECK 0x0200 /* Unit check */ +#define STATUS_EXPT 0x0100 /* Unit excpetion */ +#define STATUS_PCI 0x0080 /* Program interupt */ +#define STATUS_LENGTH 0x0040 /* Incorrect lenght */ +#define STATUS_PCHK 0x0020 /* Program check */ +#define STATUS_PROT 0x0010 /* Protection check */ +#define STATUS_CDATA 0x0008 /* Channel data check */ +#define STATUS_CCNTL 0x0004 /* Channel control check */ +#define STATUS_INTER 0x0002 /* Channel interface check */ +#define STATUS_CHAIN 0x0001 /* Channel chain check */ + +/* channel: + subchannels = 128 + 0 - 7 0x80-0xff + 8 - 127 0x00-0x7f + 128 - +6 0x1xx - 0x6xx + + Devices on channel 0 below number of subchannels have there own + virtual channel. + Devices on channel 0 above the number of subchannels are mapped in + groups of 16 into channels 0 to n. + + Channels 1-n run on channels virtual channels above subchannels. +*/ + +#define MAX_CHAN 3 +#define SUB_CHANS 128 + +void post_extirq(); + +/* look up device to find subchannel device is on */ +int find_subchan(uint16 device); +int chan_read_byte(uint16 chan, uint8 *data); +int chan_write_byte(uint16 chan, uint8 *data); +void set_devattn(uint16 addr, uint8 flags); +void chan_end(uint16 chan, uint8 flags); +int startio(uint16 addr) ; +int testio(uint16 addr); +int haltio(uint16 addr); +int testchan(uint16 channel); +uint16 scan_chan(uint8 mask); +t_stat chan_boot(uint16 addr, DEVICE *dptr); +t_stat chan_set_devs(); +t_stat set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc); +t_stat show_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc); +uint16 loading; +int irq_pend; + +const char ascii_to_ebcdic[128]; +const char ebcdic_to_ascii[256]; + +/* Debuging controls */ +#define DEBUG_CMD 0x0000001 /* Show device commands */ +#define DEBUG_DATA 0x0000002 /* Show data transfers */ +#define DEBUG_DETAIL 0x0000004 /* Show details */ +#define DEBUG_EXP 0x0000008 /* Show error conditions */ +#define DEBUG_CONI 0x0000020 /* Show CONI instructions */ +#define DEBUG_CONO 0x0000040 /* Show CONO instructions */ +#define DEBUG_DATAIO 0x0000080 /* Show DATAI/O instructions */ +#define DEBUG_IRQ 0x0000100 /* Show IRQ requests */ + +extern DEBTAB dev_debug[]; +extern DEBTAB crd_debug[]; + +extern DEVICE cdp_dev; +extern DEVICE cdr_dev; +extern DEVICE lpr_dev; +extern DEVICE con_dev; +extern DEVICE mta_dev; +extern DEVICE mtb_dev; +extern DEVICE dda_dev; +extern DEVICE ddb_dev; +extern DEVICE com_dev; + +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_CDR 1 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_LPR 1 +#define NUM_DEVS_MT 1 +#define NUM_UNITS_MT 8 +#define NUM_DEVS_DASD 2 +#define NUM_UNITS_DASD 8 +#define NUM_DEVS_COM 1 +#define NUM_UNITS_COM 16 + +extern DEVICE cpu_dev; +extern void fprint_inst(FILE *, uint16 *); diff --git a/IBM360/ibm360_lpr.c b/IBM360/ibm360_lpr.c new file mode 100644 index 0000000..56205ac --- /dev/null +++ b/IBM360/ibm360_lpr.c @@ -0,0 +1,288 @@ +/* ibm360_urec.c: IBM 360 Line Printer + + Copyright (c) 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. + 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 "ibm360_defs.h" +#include "sim_defs.h" +#include + +#ifdef NUM_DEVS_LPR +#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE + + +/* u3 hold command and status information */ +#define CHN_SNS 0x04 /* Sense command */ + +#define LPR_WR 0x01 /* Write command */ +#define LPR_SPKCMD 0x03 /* Skip command */ +#define LPR_SPCMSK 0x18 /* Space after printing */ +#define LPR_SKIP 0x80 /* Skip Flag */ +#define LPR_SKPCHN 0x78 /* Skip Channel */ +#define LPR_CMDMSK 0xff /* Mask command part. */ +#define LPR_FULL 0x100 /* Buffer full */ + +/* u4 holds current line */ +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ +#define SNS_BUSCHK 0x20 /* Parity error on bus */ +#define SNS_EQUCHK 0x10 /* Equipment check */ +#define SNS_DATCHK 0x08 /* Data Check */ +#define SNS_OVRRUN 0x04 /* Data overrun */ +#define SNS_SEQUENCE 0x02 /* Unusual sequence */ +#define SNS_CHN9 0x01 /* Channel 9 on printer */ +/* u6 hold buffer position */ + + +/* 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 */ + uint8 fcs[256]; /* Form control buffer */ +} +lpr_data[NUM_DEVS_LPR]; + +uint8 lpr_startcmd(UNIT *, uint16, uint8); +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 *); + +UNIT lpr_unit[] = { + {UDATA(lpr_srv, UNIT_LPR, 55), 300, UNIT_ADDR(0x0E)}, /* A */ +#if NUM_DEVS_LPR > 1 + {UDATA(lpr_srv, UNIT_LPR, 55), 300, UNIT_ADDR(0x1E)}, /* B */ +#endif +}; + +MTAB lpr_mod[] = { + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +struct dib lpr_dib = { 0xFF, 1, NULL, lpr_startcmd, NULL, lpr_unit}; + +DEVICE lpr_dev = { + "LPR", lpr_unit, NULL, lpr_mod, + NUM_DEVS_LPR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, + &lpr_dib, DEV_UADDR | DEV_DISABLE | DEV_DEBUG, 0, dev_debug +}; + + +/* 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; +} + +void +print_line(UNIT * uptr) +{ + + char out[150]; /* Temp conversion buffer */ + int i; + int u = (uptr - lpr_unit); + + /* Try to convert to text */ + memset(out, ' ', sizeof(out)); + + /* Scan each column */ + for (i = 0; i < uptr->u6; i++) { + int ch = lpr_data[u].lbuff[i]; + + ch = ebcdic_to_ascii[ch]; + if (!isprint(ch)) + ch = '.'; + out[i] = ch; + } + + /* Trim trailing spaces */ + for (--i; i > 0 && out[i] == ' '; i--) ; + out[++i] = '\n'; + out[++i] = '\r'; + out[++i] = '\0'; + + /* Print out buffer */ + sim_fwrite(&out, 1, i, uptr->fileref); +fprintf(stderr, "%s", out); + uptr->u4++; + if (uptr->u4 > uptr->capac) { + uptr->u4 = 1; + } + + memset(&lpr_data[u].lbuff[0], 0, 144); +} + + +uint8 lpr_startcmd(UNIT * uptr, uint16 chan, uint8 cmd) +{ + uint8 ch; + + if ((uptr->u3 & LPR_CMDMSK) != 0) { + if ((uptr->flags & UNIT_ATT) != 0) + return SNS_BSY; + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + switch (cmd & 0x7) { + case 1: /* Write command */ + case 3: + uptr->u3 &= ~(LPR_CMDMSK); + uptr->u3 |= (cmd & LPR_CMDMSK); + sim_activate(uptr, 10); /* Start unit off */ + uptr->u5 = 0; + uptr->u6 = 0; + return 0; + + case 0: /* Status */ + break; + + case 4: /* Sense */ + uptr->u3 &= ~(LPR_CMDMSK); + uptr->u3 |= (cmd & LPR_CMDMSK); + sim_activate(uptr, 10); /* Start unit off */ + return 0; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; + break; + } + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Handle transfer of data for printer */ +t_stat +lpr_srv(UNIT *uptr) { + int addr = GET_UADDR(uptr->u3); + int u = (uptr - lpr_unit); + + if ((uptr->u3 & 0xFF) == 4) { + uint8 ch = uptr->u5; + chan_write_byte(GET_UADDR(uptr->u3), &ch); + chan_end(addr, SNS_DEVEND|SNS_CHNEND); + return SCPE_OK; + } + + if ((uptr->u3 & LPR_FULL) || (uptr->u3 & 0x7) == 0x3) { + print_line(uptr); + uptr->u3 &= ~(LPR_FULL|LPR_CMDMSK); + uptr->u6 = 0; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); +// set_devattn(addr, SNS_DEVEND); + } + + /* Copy next column over */ + if ((uptr->u3 & 0x7) == 1 && uptr->u6 < 144) { + if(chan_read_byte(addr, &lpr_data[u].lbuff[uptr->u6++])) { + uptr->u3 |= LPR_FULL; + } else { + sim_activate(uptr, 10); + } + if (uptr->u3 & LPR_FULL || uptr->u6 >= 144) { + uptr->u3 |= LPR_FULL; +// chan_end(addr, SNS_CHNEND); + sim_activate(uptr, 1000); + } + } + 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->u3 &= ~(LPR_FULL|LPR_CMDMSK); + uptr->u4 = 0; + uptr->u5 = 0; + return SCPE_OK; +} + +t_stat +lpr_detach(UNIT * uptr) +{ + if (uptr->u3 & LPR_FULL) + print_line(uptr); + return detach_unit(uptr); +} + +#endif diff --git a/IBM360/ibm360_mt.c b/IBM360/ibm360_mt.c new file mode 100644 index 0000000..0d0a523 --- /dev/null +++ b/IBM360/ibm360_mt.c @@ -0,0 +1,891 @@ +/* ibm360_mt.c: IBM 360 2400 Magnetic tape controller + + Copyright (c) 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 "ibm360_defs.h" +#include "sim_tape.h" + +#ifdef NUM_DEVS_MT +#define BUFFSIZE (64 * 1024) +#define MTUF_9TR (1 << MTUF_V_UF) +#define UNIT_MT UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | MTUF_9TR +#define DEV_BUF_NUM(x) (((x) & 07) << DEV_V_UF) +#define GET_DEV_BUF(x) (((x) >> DEV_V_UF) & 07) + + + +#define MT_WRITE 0x01 /* Write command */ +#define MT_READ 0x02 /* Read command */ +#define MT_RDBK 0x0c /* Read Backward */ +#define MT_SENSE 0x04 /* Sense command */ +#define MT_REW 0x07 /* Rewind command */ +#define MT_RUN 0x0f /* Rewind and unload */ +#define MT_ERG 0x17 /* Erase Gap */ +#define MT_WTM 0x1f /* Write Tape Mark */ +#define MT_BSR 0x27 /* Back space record */ +#define MT_BSF 0x2f /* Back space file */ +#define MT_FSR 0x37 /* Forward space record */ +#define MT_FSF 0x3f /* Forward space file */ +#define MT_MODE 0x03 /* Mode command */ +#define MT_MODEMSK 0x07 /* Mode Mask */ + +#define MT_MDEN_200 0x00 /* 200 BPI mode 7 track only */ +#define MT_MDEN_556 0x40 /* 556 BPI mode 7 track only */ +#define MT_MDEN_800 0x80 /* 800 BPI mode 7 track only */ +#define MT_MDEN_1600 0xc0 /* 1600 BPI mode 9 track only */ +#define MT_MDEN_MSK 0xc0 /* Density mask */ + +#define MT_CTL_MSK 0x38 /* Mask for control flags */ +#define MT_CTL_NOP 0x00 /* Nop control mode */ +#define MT_CTL_NRZI 0x08 /* 9 track 800 bpi mode */ +#define MT_CTL_RST 0x10 /* Set density, odd, convert on, trans off */ +#define MT_CTL_NOP2 0x18 /* 9 track 1600 NRZI mode */ +#define MT_CTL_MD0 0x20 /* Set density, even, convert off, trans off */ +#define MT_CTL_MD1 0x28 /* Set density, even, convert off, trans on */ +#define MT_CTL_MD2 0x30 /* Set density, odd, convert off, trans off */ +#define MT_CTL_MD3 0x38 /* Set density, odd, convert off, trans on */ + +/* in u3 is device command code and status */ +#define MT_CMDMSK 0x003f /* Command being run */ +#define MT_READDONE 0x0400 /* Read finished, end channel */ +#define MT_MARK 0x0800 /* Sensed tape mark in move command */ +#define MT_ODD 0x1000 /* Odd parity */ +#define MT_TRANS 0x2000 /* Translation turned on ignored 9 track */ +#define MT_CONV 0x4000 /* Data converter on ignored 9 track */ +#define MT_BUSY 0x8000 /* Flag to send a CUE */ +#define MT_CHAN_MSK 0xff0000 /* Channel mask */ +#define MT_CHAN_V 16 /* Channel shift */ + +/* in u4 is current buffer position */ + +/* in u5 packs sense byte 0,1 and 3 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ +#define SNS_BUSCHK 0x20 /* Parity error on bus */ +#define SNS_EQUCHK 0x10 /* Equipment check */ +#define SNS_DATCHK 0x08 /* Data Check */ +#define SNS_OVRRUN 0x04 /* Data overrun */ +#define SNS_WCZERO 0x02 /* Write with no data */ +#define SNS_CVTCHK 0x01 /* Data conversion error */ + +/* Sense byte 1 */ +#define SNS_NOISE 0x80 /* Noise record */ +#define SNS_TUASTA 0x40 /* Selected and ready */ +#define SNS_TUBSTA 0x20 /* Not ready, rewinding. */ +#define SNS_7TRACK 0x10 /* Seven track unit */ +#define SNS_LOAD 0x08 /* Load Point */ +#define SNS_WR 0x04 /* Unit write */ +#define SNS_WRP 0x02 /* No write ring */ +#define SNS_DENS 0x01 /* Density error 9tr only */ + +/* Sense byte 2 */ +#define SNS_BYTE2 0xc0 /* Not supported feature */ + +/* Sense byte 3 */ +#define SNS_VRC 0x80 /* Veritical parity error */ +#define SNS_LRCR 0x40 /* Logituntial parity error */ +#define SNS_SKEW 0x20 /* Skew */ +#define SNS_CRC 0x10 /* CRC error. 9t only */ +#define SNS_SKEWVRC 0x08 /* VRC Skew */ +#define SNS_PE 0x04 /* Phase encoding */ +#define SNS_BACK 0x01 /* tape in backward status */ + +#define SNS_BYTE4 0x00 /* Hardware errors not supported */ +#define SNS_BYTE5 0x00 /* Hardware errors not supported */ + +#define MT_CONV1 0x40 +#define MT_CONV2 0x80 +#define MT_CONV3 0xc0 + +/* u6 holds the packed characters and unpack counter */ +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF + +uint8 mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) ; +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_boot(int32, DEVICE *); + + +/* One buffer per channel */ +uint8 mt_buffer[NUM_DEVS_MT][BUFFSIZE]; +uint8 mt_busy[NUM_DEVS_MT]; + +MTAB mt_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL}, + {MTUF_9TR, 0, "7 track", "7T", NULL}, + {MTUF_9TR, MTUF_9TR, "9 track", "9T", NULL}, + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, + &show_dev_addr, NULL}, + {0} +}; + +UNIT mta_unit[] = { + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x180)}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x181)}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x182)}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x183)}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x184)}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x185)}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x186)}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x187)}, /* 7 */ +}; + +struct dib mta_dib = { 0xF8, NUM_UNITS_MT, NULL, mt_startcmd, NULL, mta_unit, NULL}; + +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, + &mta_dib, DEV_BUF_NUM(0) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug +}; + +#if NUM_DEVS_MT > 1 +UNIT mtb_unit[] = { + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x280)}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x281)}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x282)}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x283)}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x284)}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x285)}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x286)}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0, UNIT_ADDR(0x287)}, /* 7 */ +}; + +struct dib mtb_dib = { 0xF8, NUM_UNITS_MT, NULL, mt_startcmd, NULL, mtb_unit, NULL}; + +DEVICE mtb_dev = { + "MTB", mtb_unit, NULL, mt_mod, + NUM_UNITS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mtb_dib, DEV_BUF_NUM(1) | DEV_DISABLE | DEV_DEBUG, 0, dev_debug +}; +#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 +}; + +uint8 bcd_to_ebcdic[64] = { + 0x40, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xf0, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x7a, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xe0, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x60, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xd0, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x50, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xc0, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}; + + +uint8 mt_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) { + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + uint8 ch; + + if (mt_busy[GET_DEV_BUF(dptr->flags)] != 0 || (uptr->u3 & MT_CMDMSK) != 0) { + sim_debug(DEBUG_CMD, dptr, "CMD busy unit=%d %x", unit, cmd); + uptr->flags |= MT_BUSY; /* Flag we need to send CUE */ + mt_busy[GET_DEV_BUF(dptr->flags)] |= 2; + return SNS_BSY; + } + + sim_debug(DEBUG_CMD, dptr, "CMD unit=%d %x", unit, cmd); + + switch (cmd & 0xF) { + case 0x7: /* Tape motion */ + case 0xf: /* Tape motion */ + case 0x1: /* Write command */ + case 0x2: /* Read command */ + case 0xc: /* Read backward */ + uptr->u5 = 0; + uptr->u5 |= SNS_TUASTA << 8; + if ((uptr->flags & MTUF_9TR) == 0) + uptr->u5 |= (SNS_7TRACK << 8); + if (sim_tape_wrp(uptr)) + uptr->u5 |= (SNS_WRP << 8); + if (sim_tape_bot(uptr)) + uptr->u5 |= (SNS_LOAD << 8); + /* Fall through */ + + case 0x4: /* Sense */ + uptr->u3 &= ~(MT_CMDMSK); + uptr->u3 |= cmd & MT_CMDMSK; + sim_activate(uptr, 1000); /* Start unit off */ + CLR_BUF(uptr); + uptr->u4 = 0; + uptr->u6 = 0; + mt_busy[GET_DEV_BUF(dptr->flags)] = 1; + if ((cmd & 0x7) == 0x7) /* Quick end channel on control */ + return SNS_CHNEND; + return 0; + + case 0x3: /* Control */ + case 0xb: /* Control */ + if ((uptr->flags & MTUF_9TR) == 0) { + uptr->u5 |= (SNS_7TRACK << 8); + if ((cmd & 0x30) == 0) + return SNS_DEVEND; + if ((cmd & 0x38) == 0x18) + return SNS_DEVEND; + if ((cmd & 0xc0) != 0xc0) { + uptr->u5 |= SNS_CMDREJ; + } else { + uptr->u3 &= ~MT_MDEN_MSK; + uptr->u3 |= (cmd & MT_MDEN_MSK); + } + uptr->u3 &= ~(MT_ODD|MT_TRANS); + uptr->u3 |= MT_CONV; + if (cmd & 0x10) + uptr->u3 |= MT_ODD; + if (cmd & 0x20) + uptr->u3 &= ~MT_CONV; + if (cmd & 0x08) + uptr->u3 |= MT_TRANS; + } else { + if ((cmd & 0xf0) != 0xc0) { + uptr->u5 |= SNS_CMDREJ; + } + uptr->u3 &= ~MT_MDEN_MSK; + if (cmd & 0x8) + uptr->u3 |= MT_MDEN_800; + else + uptr->u3 |= MT_MDEN_1600; + } + uptr->u5 = 0; + break; + + case 0x0: /* Status */ + break; + + default: /* invalid command */ + uptr->u5 |= SNS_CMDREJ; + break; + } + if (uptr->u5 & 0xff) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + return SNS_CHNEND|SNS_DEVEND; +} + +/* Map simH errors into machine errors */ +t_stat mt_error(UNIT * uptr, uint16 addr, t_stat r, DEVICE * dptr) +{ + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_TMK: /* tape mark */ + sim_debug(DEBUG_EXP, dptr, "MARK "); + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + return SCPE_OK; + + case MTSE_WRP: /* write protected */ + case MTSE_UNATT: /* unattached */ + sim_debug(DEBUG_EXP, dptr, "ATTENTION %d ", r); + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + case MTSE_RECE: /* error in record */ + sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r); + break; + case MTSE_BOT: /* beginning of tape */ + sim_debug(DEBUG_EXP, dptr, "BOT "); + break; + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_EOM: /* end of medium */ + sim_debug(DEBUG_EXP, dptr, "EOT "); + break; + } + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; +} + +/* Handle processing of tape requests. */ +t_stat mt_srv(UNIT * uptr) +{ + uint16 addr = GET_UADDR(uptr->u3); + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units); + int cmd = uptr->u3 & 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; + + if ((uptr->flags & UNIT_ATT) == 0) { + uptr->u5 |= SNS_INTVENT; + if (cmd != MT_SENSE) + return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; + } + + switch (cmd) { + case 0: /* No command, stop tape */ + sim_debug(DEBUG_DETAIL, dptr, "Idle unit=%d\n", unit); + break; + + case MT_SENSE: + ch = uptr->u5 & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = (uptr->u5 >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0xc0; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = (uptr->u5 >> 16) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch); + chan_write_byte(addr, &ch) ; + ch = 0; + chan_write_byte(addr, &ch) ; + chan_write_byte(addr, &ch); + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + + case MT_READ: + + if (uptr->u3 & MT_READDONE) { + uptr->u3 &= ~(MT_CMDMSK|MT_READDONE); + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + break; + } + + /* If empty buffer, fill */ + if (BUF_EMPTY(uptr)) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d ", unit); + if ((r = sim_tape_rdrecf(uptr, &mt_buffer[bufnum][0], &reclen, + BUFFSIZE)) != MTSE_OK) { + uptr->u3 &= ~(MT_CMDMSK|MT_READDONE); + return mt_error(uptr, addr, r, dptr); + } + uptr->u4 = 0; + uptr->hwmark = reclen; + sim_debug(DEBUG_DETAIL, dptr, "Block %d chars\n", reclen); + } + + ch = mt_buffer[bufnum][uptr->u4++]; + /* if we are a 7track tape, handle conversion */ + if ((uptr->flags & MTUF_9TR) == 0) { + mode = (uptr->u3 & MT_ODD) ? 0100 : 0; + if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { + sim_debug(DEBUG_DETAIL, dptr, "Parity error unit=%d %d %03o\n", + unit, uptr->u4-1, ch); + uptr->u5 |= (SNS_VRC << 16) | SNS_DATCHK; + } + ch &= 077; + if (uptr->u3 & MT_TRANS) + ch = bcd_to_ebcdic[ch]; + if (uptr->u3 & MT_CONV) { + sim_debug(DEBUG_DATA, dptr, "Read raw data unit=%d %d %02x %02x\n", + unit, uptr->u4, ch, uptr->u6); + if (uptr->u6 == 0 && uptr->u4 < uptr->hwmark) { + uptr->u6 = MT_CONV1 | ch; + sim_activate(uptr, 20); + return SCPE_OK; + } else if ((uptr->u6 & 0xc0) == MT_CONV1) { + int t = uptr->u6 & 0x3F; + uptr->u6 = MT_CONV2 | ch; + ch = (t << 2) | ((ch >> 4) & 03); + } else if ((uptr->u6 & 0xc0) == MT_CONV2) { + int t = uptr->u6 & 0xf; + uptr->u6 = MT_CONV3 | ch; + ch = (t << 4) | ((ch >> 2) & 0xf); + } else if ((uptr->u6 & 0xc0) == MT_CONV3) { + ch |= ((uptr->u6 & 0x3) << 6); + uptr->u6 = 0; + } + } + } + + /* Send character over to channel */ + if (chan_write_byte(addr, &ch)) { + sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n\r", unit); + /* If not read whole record, skip till end */ + if (uptr->u4 < uptr->hwmark) { + /* Send dummy character to force SLI */ + chan_write_byte(addr, &ch); + sim_activate(uptr, (uptr->hwmark-uptr->u4) * 20); + uptr->u3 |= MT_READDONE; + break; + } + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_DEVEND); + } else { + sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02x\n\r", + unit, uptr->u4, ch); + if (uptr->u4 >= uptr->hwmark) { /* In IRG */ + /* Handle end of record */ + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } else + sim_activate(uptr, 20); + } + break; + + + case MT_WRITE: + /* Check if write protected */ + if (sim_tape_wrp(uptr)) { + uptr->u5 |= SNS_CMDREJ; + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + /* Grab data until channel has no more */ + if (chan_read_byte(addr, &ch)) { + if (uptr->u4 > 0) { /* Only if data in record */ + reclen = uptr->hwmark; + sim_debug(DEBUG_DETAIL, dptr, "Write unit=%d Block %d chars\n", + unit, reclen); + r = sim_tape_wrrecf(uptr, &mt_buffer[bufnum][0], reclen); + uptr->u4 = 0; + uptr->u3 &= ~MT_CMDMSK; + mt_error(uptr, addr, r, dptr); /* Record errors */ + } else { + uptr->u5 |= SNS_WCZERO; /* Write with no data */ + } + } else { + if ((uptr->flags & MTUF_9TR) == 0) { + mode = (uptr->u3 & MT_ODD) ? 0100 : 0; + if (uptr->u3 & MT_TRANS) + ch = (ch & 0xf) | ((ch & 0x30) ^ 0x30); + if (uptr->u3 & MT_CONV) { + if (uptr->u6 == 0) { + uptr->u6 = MT_CONV1 | (ch & 0x3); + ch >>= 2; + } else if ((uptr->u6 & 0xc0) == MT_CONV1) { + int t = uptr->u6 & 0x3; + uptr->u6 = MT_CONV2 | (ch & 0xf); + ch = (t << 4) | ((ch >> 4) & 0xf); + } else if ((uptr->u6 & 0xc0) == MT_CONV2) { + int t = uptr->u6 & 0xf; + ch = (t << 2) | ((ch >> 6) & 0x3); + ch ^= parity_table[ch & 077] ^ mode; + mt_buffer[bufnum][uptr->u4++] = ch; + uptr->u6 = 0; + } + } + ch &= 077; + ch |= parity_table[ch] ^ mode; + } + mt_buffer[bufnum][uptr->u4++] = ch; + sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %02o\n\r", + unit, uptr->u4, ch); + uptr->hwmark = uptr->u4; + break; + } + sim_activate(uptr, 20); + break; + + case MT_RDBK: + if (uptr->u3 & MT_READDONE) { + uptr->u3 &= ~(MT_CMDMSK|MT_READDONE); + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + return SCPE_OK; + } + + /* If at end of record, fill buffer */ + if (BUF_EMPTY(uptr)) { + if (sim_tape_bot(uptr)) { + uptr->u3 &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "Read backward unit=%d ", unit); + if ((r = sim_tape_rdrecr(uptr, &mt_buffer[bufnum][0], &reclen, + BUFFSIZE)) != MTSE_OK) { + uptr->u3 &= ~(MT_CMDMSK|MT_READDONE); + return mt_error(uptr, addr, r, dptr); + } + uptr->u4 = reclen; + uptr->hwmark = reclen; + sim_debug(DEBUG_DETAIL, dptr, "Binary Block %d chars\n", reclen); + } + + ch = mt_buffer[bufnum][--uptr->u4]; + if ((uptr->flags & MTUF_9TR) == 0) { + mode = (uptr->u3 & MT_ODD) ? 0100 : 0; + ch &= 077; + if ((parity_table[ch & 077] ^ (ch & 0100) ^ mode) == 0) { + uptr->u5 |= (SNS_VRC << 16) | SNS_DATCHK; + } + if (uptr->u3 & MT_TRANS) + ch = bcd_to_ebcdic[ch]; + if (uptr->u3 & MT_CONV) { + if (uptr->u6 == 0 && uptr->u4 < uptr->hwmark) { + uptr->u6 = MT_CONV1 | ch; + sim_activate(uptr, 20); + return SCPE_OK; + } else if ((uptr->u6 & 0xc0) == MT_CONV1) { + int t = uptr->u6 & 0x3F; + uptr->u6 = MT_CONV2 | ch; + ch = t | ((ch << 6) & 0xc0); + } else if ((uptr->u6 & 0xc0) == MT_CONV2) { + int t = uptr->u6 & 0x3C; + uptr->u6 = MT_CONV3 | ch; + ch = (t >> 2) | ((ch << 4) & 0xf0); + } else if ((uptr->u6 & 0xc0) == MT_CONV3) { + ch |= ((uptr->u6 & 0x30) >> 4); + uptr->u6 = 0; + } + } + } + + if (chan_write_byte(addr, &ch)) { + sim_debug(DEBUG_DATA, dptr, "Read unit=%d EOR\n\r", unit); + /* If not read whole record, skip till end */ + if (uptr->u4 >= 0) { + sim_activate(uptr, (uptr->u4) * 20); + uptr->u3 |= MT_READDONE; + return SCPE_OK; + } + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } else { + sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %02o\n\r", + unit, uptr->u4, ch); + if (uptr->u4 == 0) { /* In IRG */ + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + chan_end(addr, SNS_CHNEND|SNS_DEVEND); + } else + sim_activate(uptr, 20); + } + break; + + case MT_WTM: + if (uptr->u4 == 0) { + if (sim_tape_wrp(uptr)) { + uptr->u5 |= SNS_CMDREJ; + uptr->u3 &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->u4 ++; + sim_activate(uptr, 500); + } else { + sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n", unit); + uptr->u3 &= ~(MT_CMDMSK); + r = sim_tape_wrtmk(uptr); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + } + break; + + case MT_BSR: + switch (uptr->u4 ) { + case 0: + if (sim_tape_bot(uptr)) { + uptr->u3 &= ~MT_CMDMSK; + mt_busy[GET_DEV_BUF(dptr->flags)] &= ~1; + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + return SCPE_OK; + } + uptr->u4 ++; + sim_activate(uptr, 500); + break; + case 1: + uptr->u4++; + sim_debug(DEBUG_DETAIL, dptr, "Backspace rec unit=%d ", unit); + r = sim_tape_sprecr(uptr, &reclen); + /* We don't set EOF on BSR */ + if (r == MTSE_TMK) { + uptr->u4++; + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, 50); + } else { + sim_debug(DEBUG_DETAIL, dptr, "%d \n", reclen); + sim_activate(uptr, 10 + (10 * reclen)); + } + break; + case 2: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + break; + case 3: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITEXP); + mt_busy[bufnum] &= ~1; + break; + } + break; + + case MT_BSF: + switch(uptr->u4) { + case 0: + if (sim_tape_bot(uptr)) { + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + break; + } + uptr->u4 ++; + sim_activate(uptr, 500); + break; + case 1: + sim_debug(DEBUG_DETAIL, dptr, "Backspace file unit=%d\n", unit); + r = sim_tape_sprecr(uptr, &reclen); + if (r == MTSE_TMK) { + uptr->u4++; + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, 50); + } else if (r == MTSE_BOT) { + uptr->u4+= 2; + sim_activate(uptr, 50); + } else { + sim_activate(uptr, 10 + (10 * reclen)); + } + break; + case 2: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITEXP); + mt_busy[bufnum] &= ~1; + break; + case 3: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + mt_busy[bufnum] &= ~1; + break; + } + break; + + case MT_FSR: + switch(uptr->u4) { + case 0: + uptr->u4 ++; + sim_activate(uptr, 500); + break; + case 1: + uptr->u4++; + sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit); + r = sim_tape_sprecf(uptr, &reclen); + if (r == MTSE_TMK) { + uptr->u4 = 3; + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, 50); + } else if (r == MTSE_EOM) { + uptr->u4 = 4; + sim_activate(uptr, 50); + } else { + sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen); + sim_activate(uptr, 10 + (10 * reclen)); + } + break; + case 2: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + break; + case 3: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITEXP); + mt_busy[bufnum] &= ~1; + break; + case 4: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + mt_busy[bufnum] &= ~1; + break; + } + break; + + case MT_FSF: + switch(uptr->u4) { + case 0: + uptr->u4 ++; + sim_activate(uptr, 500); + break; + case 1: + sim_debug(DEBUG_DETAIL, dptr, "Skip rec unit=%d ", unit); + r = sim_tape_sprecf(uptr, &reclen); + if (r == MTSE_TMK) { + uptr->u4++; + sim_debug(DEBUG_DETAIL, dptr, "MARK\n"); + sim_activate(uptr, 50); + } else if (r == MTSE_EOM) { + uptr->u4+= 2; + sim_activate(uptr, 50); + } else { + sim_debug(DEBUG_DETAIL, dptr, "%d\n", reclen); + sim_activate(uptr, 10 + (10 * reclen)); + } + break; + case 2: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + sim_debug(DEBUG_DETAIL, dptr, "Skip done unit=%d\n", unit); + break; + case 3: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + mt_busy[bufnum] &= ~1; + break; + } + break; + + + case MT_ERG: + switch (uptr->u4) { + case 0: + if (sim_tape_wrp(uptr)) { + uptr->u5 |= SNS_CMDREJ; + uptr->u3 &= ~MT_CMDMSK; + mt_busy[bufnum] &= ~1; + set_devattn(addr, SNS_DEVEND|SNS_UNITCHK); + } else { + uptr->u4 ++; + sim_activate(uptr, 500); + } + break; + case 1: + sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d\n", unit); + r = sim_tape_wrgap(uptr, 35); + sim_activate(uptr, 5000); + uptr->u4++; + break; + case 2: + uptr->u3 &= ~(MT_CMDMSK); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + } + break; + + case MT_REW: + if (uptr->u4 == 0) { + uptr->u4 ++; + sim_activate(uptr, 30000); + } else { + sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d\n", unit); + uptr->u3 &= ~(MT_CMDMSK); + r = sim_tape_rewind(uptr); + set_devattn(addr, SNS_DEVEND); + mt_busy[bufnum] &= ~1; + } + break; + + case MT_RUN: + if (uptr->u4 == 0) { + uptr->u4 ++; + sim_activate(uptr, 30000); + } else { + sim_debug(DEBUG_DETAIL, dptr, "Unload unit=%d\n", unit); + uptr->u3 &= ~(MT_CMDMSK); + r = sim_tape_detach(uptr); + mt_busy[bufnum] &= ~1; + } + break; + } + return SCPE_OK; +} + +void +mt_ini(UNIT * uptr, t_bool f) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + + uptr->u3 = MT_ODD|MT_DENS_800; + mt_busy[GET_DEV_BUF(dptr->flags)] = 0; +} + +t_stat +mt_reset(DEVICE * dptr) +{ + return SCPE_OK; +} + +t_stat +mt_attach(UNIT * uptr, CONST char *file) +{ + uint16 addr = GET_UADDR(uptr->u3); + t_stat r; + + if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) + return r; + set_devattn(addr, SNS_DEVEND); + return SCPE_OK; +} + +t_stat +mt_detach(UNIT * uptr) +{ + uptr->u3 = 0; + return sim_tape_detach(uptr); +} + +t_stat +mt_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? */ + uptr->u3 |= MT_CONV; + return chan_boot(GET_UADDR(uptr->u3), dptr); +} + +#endif diff --git a/IBM360/ibm360_sys.c b/IBM360/ibm360_sys.c new file mode 100644 index 0000000..58c66c2 --- /dev/null +++ b/IBM360/ibm360_sys.c @@ -0,0 +1,647 @@ +/* ibm360_sys.c: IBM 360 Simulator system interface. + + 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 + ROBERT M SUPNIK 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 "ibm360_defs.h" +#include +#include "sim_card.h" + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint32 M[MAXMEMSIZE]; + +/* 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 360"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 16; + +DEVICE *sim_devices[] = { + &cpu_dev, +#ifdef NUM_DEVS_CON + &con_dev, +#endif +#ifdef NUM_DEVS_CDR + &cdr_dev, +#endif +#ifdef NUM_DEVS_CDP + &cdp_dev, +#endif +#ifdef NUM_DEVS_LPR + &lpr_dev, +#endif +#ifdef NUM_DEVS_MT + &mta_dev, +#if NUM_DEVS_MT > 1 + &mtb_dev, +#endif +#endif +#ifdef NUM_DEVS_DASD + &dda_dev, +#if NUM_DEVS_DASD > 1 + &ddb_dev, +#endif +#endif +#ifdef NUM_DEVS_COM + &com_dev, +#endif + NULL }; + + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"CONI", DEBUG_CONI, "Show coni instructions"}, + {"CONO", DEBUG_CONO, "Show coni instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + {0, 0} +}; + +/* Simulator debug controls */ +DEBTAB crd_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"CONI", DEBUG_CONI, "Show coni instructions"}, + {"CONO", DEBUG_CONO, "Show coni instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + {"CARD", DEBUG_CARD, "Show Card read/punches"}, + {0, 0} +}; + + +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Unknown Opcode", + "Invalid instruction", + "Invalid I/O operation", + "Nested indirects exceed limit", + "Nested XEC's exceed limit", + "I/O Check opcode", + "Memory management trap during trap", + "Trap instruction not BRM", + "RTC instruction not MIN or SKR", + "Interrupt vector zero", + "Runaway carriage control tape" }; + +const char ascii_to_ebcdic[128] = { + /* Control */ + 0x01,0x02,0x03,0xFF,0x00,0x00,0x00,0x00, /*0-37*/ + /*Control*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + /*Control*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + /*Control*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + /* sp ! " # $ % & ' */ + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 40 - 77 */ + /* ( ) * + , - . / */ + 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, + /* 0 1 2 3 4 5 6 7 */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + /* 8 9 : ; < = > ? */ + 0xf8, 0xf9, 0x7a, 0x6e, 0x4c, 0x7e, 0x6e, 0x6f, + /* @ A B C D E F G */ + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 100 - 137 */ + /* H I J K L M N O */ + 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + /* P Q R S T U V W */ + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + /* X Y Z [ \ ] ^ _ */ + 0xe7, 0xe8, 0xe9, 0x4a, 0xff, 0x5a, 0x5f, 0x6d, + /* ` a b c d e f g */ + 0x7c, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 140 - 177 */ + /* h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /* p q r s t u v w */ + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + /* x y z { | } ~ del */ + 0xa7, 0xa8, 0xa9, 0xff, 0x47, 0xff, 0xff, 0x6d, +}; + +const char ebcdic_to_ascii[256] = { +/* 0 1 2 3 4 5 6 7 */ + 0x00, 0x01, 0x02, 0x03, 0xFF, 0x09, 0xff, 0x7f, /* 0x */ + 0xff, 0xff, 0xff, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x19, 0x0a, 0x08, 0x08, 0xff, /* 1x */ + 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1c, 0xff, 0xff, 0x0a, 0xff, 0xff, /* 2x */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x06, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e, 0xff, 0xff, /* 3x */ + 0xff, 0xff, 0xff, 0xff, 0x14, 0x15, 0xff, 0xff, + ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 4x */ + 0xff, 0xff, '[', '.', '<', '(', '+', '|', + '&', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 5x */ + 0xff, 0xff, ']', '$', '*', ')', ';', '^', + '-', '/', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 6x */ + 0xff, 0xff, 0xff, ',', '%', '_', '>', '?', + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 7x */ + 0xff, 0xff, ':', '#', '@', '\'', '=', '"', + 0xff, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 8x */ + 'h', 'i', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 'j', 'k', 'l', 'm', 'n', 'o', 'p', /* 9x */ + 'q', 'r', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 's', 't', 'u', 'v', 'w', 'x', /* Ax */ + 'y', 'z', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Bx */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* Cx */ + 'H', 'I', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* Dx */ + 'Q', 'R', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 'S', 'T', 'U', 'V', 'W', 'X', /* Ex */ + 'Y', 'Z', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + '0', '1', '2', '3', '4', '5', '6', '7', /* Fx */ + '8', '9', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + + +/* 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 { + uint8 opbase; + char *name; + uint8 type; +} t_opcode; + +#define RR 01 +#define RX 02 +#define RS 03 +#define SI 04 +#define SS 05 +#define LNMSK 07 +#define ONEOP 010 +#define IMDOP 020 +#define TWOOP 030 +#define ZEROOP 040 +#define OPMSK 070 + +t_opcode optab[] = { + { OP_SPM, "SPM", RR|ONEOP }, + { OP_BALR, "BALR", RR }, + { OP_BCTR, "BCTR", RR }, + { OP_BCR, "BCR", RR }, + { OP_SSK, "SSK", RR }, + { OP_ISK, "ISK", RR }, + { OP_SVC, "SVC", RR|IMDOP }, + { OP_LPR, "LPR", RR }, + { OP_LNR, "LNR", RR }, + { OP_LTR, "LTR", RR }, + { OP_LCR, "LCR", RR }, + { OP_NR, "NR", RR }, + { OP_OR, "OR", RR }, + { OP_XR, "XR", RR }, + { OP_CLR, "CLR", RR }, + { OP_CR, "CR", RR }, + { OP_LR, "LR", RR }, + { OP_AR, "AR", RR }, + { OP_SR, "SR", RR }, + { OP_MR, "MR", RR }, + { OP_DR, "DR", RR }, + { OP_ALR, "ALR", RR }, + { OP_SLR, "SLR", RR }, + { OP_LPDR, "LPDR", RR }, + { OP_LNDR, "LNDR", RR }, + { OP_LTDR, "LTDR", RR }, + { OP_LCDR, "LCDR", RR }, + { OP_HDR, "HDR", RR }, + { OP_LRDR, "LRDR", RR }, + { OP_MXR, "MXR", RR }, + { OP_MXDR, "MXDR", RR }, + { OP_LDR, "LDR", RR }, + { OP_CDR, "CDR", RR }, + { OP_ADR, "ADR", RR }, + { OP_SDR, "SDR", RR }, + { OP_MDR, "MDR", RR }, + { OP_DDR, "DDR", RR }, + { OP_AWR, "AWR", RR }, + { OP_SWR, "SWR", RR }, + { OP_LPER, "LPER", RR }, + { OP_LNER, "LNER", RR }, + { OP_LTER, "LTER", RR }, + { OP_LCER, "LCER", RR }, + { OP_HER, "HER", RR }, + { OP_LRER, "LRER", RR }, + { OP_AXR, "AXR", RR }, + { OP_SXR, "SXR", RR }, + { OP_LER, "LER", RR }, + { OP_CER, "CER", RR }, + { OP_AER, "AER", RR }, + { OP_SER, "SER", RR }, + { OP_MER, "MER", RR }, + { OP_DER, "DER", RR }, + { OP_AUR, "AUR", RR }, + { OP_SUR, "SUR", RR }, + { OP_STH, "STH", RX }, + { OP_LA, "LA", RX }, + { OP_STC, "STC", RX }, + { OP_IC, "IC", RX }, + { OP_EX, "EX", RX }, + { OP_BAL, "BAL", RX }, + { OP_BCT, "BCT", RX }, + { OP_BC, "BC", RX }, + { OP_LH, "LH", RX }, + { OP_CH, "CH", RX }, + { OP_AH, "AH", RX }, + { OP_SH, "SH", RX }, + { OP_MH, "MH", RX }, + { OP_CVD, "CVD", RX }, + { OP_CVB, "CVB", RX }, + { OP_ST, "ST", RX }, + { OP_N, "N", RX }, + { OP_CL, "CL", RX }, + { OP_O, "O", RX }, + { OP_X, "X", RX }, + { OP_L, "L", RX }, + { OP_C, "C", RX }, + { OP_A, "A", RX }, + { OP_S, "S", RX }, + { OP_M, "M", RX }, + { OP_D, "D", RX }, + { OP_AL, "AL", RX }, + { OP_SL, "SL", RX }, + { OP_STD, "STD", RX }, + { OP_MXD, "MXD", RX }, + { OP_LD, "LD", RX }, + { OP_CD, "CD", RX }, + { OP_AD, "AD", RX }, + { OP_SD, "SD", RX }, + { OP_MD, "MD", RX }, + { OP_DD, "DD", RX }, + { OP_AW, "AW", RX }, + { OP_SW, "SW", RX }, + { OP_STE, "STE", RX }, + { OP_LE, "LE", RX }, + { OP_CE, "CE", RX }, + { OP_AE, "AE", RX }, + { OP_SE, "SE", RX }, + { OP_ME, "ME", RX }, + { OP_DE, "DE", RX }, + { OP_AU, "AU", RX }, + { OP_SU, "SU", RX }, + { OP_SSM, "SSM", SI|ZEROOP }, + { OP_LPSW, "LPSW", SI|ZEROOP }, + { OP_DIAG, "DIAG", SI }, + { OP_BXH, "BXH", RS }, + { OP_BXLE, "BXLE", RS }, + { OP_SRL, "SRL", RS|ZEROOP }, + { OP_SLL, "SLL", RS|ZEROOP }, + { OP_SRA, "SRA", RS|ZEROOP }, + { OP_SLA, "SLA", RS|ZEROOP }, + { OP_SRDL, "SRDL", RS|ZEROOP }, + { OP_SLDL, "SLDL", RS|ZEROOP }, + { OP_SRDA, "SRDA", RS|ZEROOP }, + { OP_SLDA, "SLDA", RS|ZEROOP }, + { OP_STM, "STM", RS|TWOOP }, + { OP_TM, "TM", SI }, + { OP_MVI, "MVI", SI }, + { OP_TS, "TS", SI|ZEROOP }, + { OP_NI, "NI", SI }, + { OP_CLI, "CLI", SI }, + { OP_OI, "OI", SI }, + { OP_XI, "XI", SI }, + { OP_LM, "LM", RS|TWOOP }, + { OP_SIO, "SIO", SI|ZEROOP }, + { OP_TIO, "TIO", SI|ZEROOP }, + { OP_HIO, "HIO", SI|ZEROOP }, + { OP_TCH, "TCH", SI|ZEROOP }, + { OP_MVN, "MVN", SS }, + { OP_MVC, "MVC", SS }, + { OP_MVZ, "MVZ", SS }, + { OP_NC, "NC", SS }, + { OP_CLC, "CLC", SS }, + { OP_OC, "OC", SS }, + { OP_XC, "XC", SS }, + { OP_TR, "TR", SS }, + { OP_TRT, "TRT", SS }, + { OP_ED, "ED", SS }, + { OP_EDMK, "EDMK", SS }, + { OP_MVO, "MVO", SS|TWOOP }, + { OP_PACK, "PACK", SS|TWOOP }, + { OP_UNPK, "UNPK", SS|TWOOP }, + { OP_ZAP, "ZAP", SS|TWOOP }, + { OP_CP, "CP", SS|TWOOP }, + { OP_AP, "AP", SS|TWOOP }, + { OP_SP, "SP", SS|TWOOP }, + { OP_MP, "MP", SS|TWOOP }, + { OP_DP, "DP", SS|TWOOP }, + { 0, NULL, 0 } +}; + + +/* Register change decode + + Inputs: + *of = output stream + inst = mask bits +*/ + +//void fprint_reg (FILE *of, int32 inst) +//{ +//int32 i, j, sp; +#if 0 +inst = inst & ~(I_M_OP << I_V_OP); /* clear opcode */ +for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == I_V_REG) && (opc_val[i] & inst)) { /* reg class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; } } +#endif +//return; +//} + +void fprint_inst(FILE *of, uint16 *val) { +uint8 inst = (val[0] >> 8) & 0xff; +int i; +int l = 1; +t_opcode *tab; + + for (tab = optab; tab->name != NULL; tab++) { + if (tab->opbase == inst) { + fputs(tab->name, of); + fputc(' ', of); + switch (tab->type & LNMSK) { + case RR: + if (tab->type & IMDOP) { + fprint_val(of, val[0] & 0xff, 16, 8, PV_RZRO); + } else { + if (tab->type & ONEOP) + fprintf(of, "%d", (val[0] >> 4) & 0xf); + else + fprintf(of, "%d,%d", (val[0] >> 4) & 0xf, val[0] & 0xf); + } + break; + case RX: + fprintf(of, "%d,", (val[0] >> 4) & 0xf); + fprint_val(of, val[1] & 0xfff, 16, 12, PV_RZRO); + fprintf(of, "(%d,%d)", val[0] & 0xf, (val[1] >> 12) & 0xf); + break; + case RS: + fprintf(of, "%d,", (val[0] >> 4) & 0xf); + if ((tab->type & ZEROOP) == 0) + fprintf(of, "%d,", val[0] & 0xf); + fprint_val(of, val[1] & 0xfff, 16, 12, PV_RZRO); + if (val[1] & 0xf000) + fprintf(of, "(%d)", (val[1] >> 12) & 0xf); + break; + case SI: + fprint_val(of, val[1] & 0xfff, 16, 12, PV_RZRO); + if (val[1] & 0xf000) + fprintf(of, "(%d)", (val[1] >> 12) & 0xf); + if ((tab->type & ZEROOP) == 0) + fprintf(of, ",%02x", val[0] & 0xff); + break; + case SS: + fprint_val(of, val[1] & 0xfff, 16, 12, PV_RZRO); + if (tab->type & TWOOP) { + fprintf(of, "(%d", (val[0] >> 4) & 0xf); + } else { + fprintf(of, "(%d", val[0] & 0xff); + } + if (val[1] & 0xf000) + fprintf(of, ",%d", (val[1] >> 12) & 0xf); + fprintf(of, "),"); + fprint_val(of, val[2] & 0xfff, 16, 12, PV_RZRO); + if (tab->type & TWOOP) { + fprintf(of, "(%d,", val[0] & 0xf); + } else { + fprintf(of, "("); + } + fprintf(of, "%d)", (val[2] >> 12) & 0xf); + break; + } + } + } +} + +/* 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) +{ +uint8 inst = *val; +int i; +int l = 1; +t_opcode *tab; + +if (sw & SWMASK ('M')) { + for (tab = optab; tab->name != NULL; tab++) { + if (tab->opbase == inst) { + switch (tab->type & LNMSK) { + case RR: + l = 2; + break; + case RX: + case RS: + case SI: + l = 4; + break; + case SS: + l = 6; + } + } + } + sw &= ~ SWMASK('F'); /* Can't do F and M at same time */ +} else if (sw & SWMASK('F')) { + l = 4; +} else if (sw & SWMASK('W')) { + l = 2; +} + +for(i = 0; i < l; i++) { + fprintf(of, "%02x ", val[i] & 0xFF); +} + +if (sw & SWMASK ('C')) { + fputc('\'', of); + for(i = 0; i < l; i++) { + char ch = ebcdic_to_ascii[val[i] & 0xff]; + if (ch >= 0x20 && ch <= 0x7f) + fprintf(of, "%c", ch); + else + fputc('_', of); + } + fputc('\'', of); +} +if (sw & SWMASK ('W')) { + if (sw & SWMASK('M')) { + for(i = l; i <= 6; i++) { + fputs(" ", of); + if (sw & SWMASK('C')) + fputs(" ", of); + } + if (sw & SWMASK('C')) + fputs(" ", of); + } + for(i = 0; i < l; i+=2) + fprintf(of, "%02x%02x ", val[i] & 0xff, val[i+1] & 0xff); + if (sw & SWMASK('M')) { + for(i = l; i <= 6; i+=2) { + fputs(" ", of); + } + } +} + +if (sw & SWMASK ('F')) { + fprintf(of, "%02x%02x%02x%02x ", val[0] & 0xff, val[1] & 0xff, val[2] & 0xff, val[3] & 0xff); + return -3; +} + +if (sw & SWMASK ('M')) { + fputs(" ", of); + if ((sw & SWMASK('W')) == 0) { + if (sw & SWMASK('M')) { + for(i = l; i <= 6; i++) { + fputs(" ", of); + if (sw & SWMASK('C')) + fputs(" ", of); + } + if (sw & SWMASK('C')) + fputs(" ", of); + } + } + for (tab = optab; tab->name != NULL; tab++) { + if (tab->opbase == inst) { + fputs(tab->name, of); + fputc(' ', of); + switch (tab->type & LNMSK) { + case RR: + if (tab->type & IMDOP) { + fprint_val(of, val[1], 16, 8, PV_RZRO); + } else { + if (tab->type & ONEOP) + fprintf(of, "%d", (val[1] >> 4) & 0xf); + else + fprintf(of, "%d,%d", (val[1] >> 4) & 0xf, val[1] & 0xf); + } + break; + case RX: + fprintf(of, "%d, %x(", (val[1] >> 4) & 0xf, ((val[2] << 8) & 0xf00) | val[3]); + fprintf(of, "%d,%d)", val[1] & 0xf, (val[2] >> 4) & 0xf); + break; + case RS: + fprintf(of, "%d,", (val[1] >> 4) & 0xf); + if ((tab->type & ZEROOP) == 0) + fprintf(of, "%d,", val[1] & 0xf); + fprintf(of, "%x", ((val[2] << 8) & 0xf00) | val[3]); + if (val[2] & 0xf0) + fprintf(of, "(%d)", (val[2] >> 4) & 0xf); + break; + case SI: + fprintf(of, "%x", ((val[2] << 8) & 0xf00) | val[3]); + if (val[2] & 0xf0) + fprintf(of, "(%d)", (val[2] >> 4) & 0xf); + if ((tab->type & ZEROOP) == 0) + fprintf(of, ",%2x", val[1]); + break; + case SS: + fprintf(of, "%x", ((val[2] << 8) & 0xf00) | val[3]); + if (tab->type & TWOOP) { + fprintf(of, "(%d", (val[1] >> 4) & 0xf); + } else { + fprintf(of, "(%d", val[1] & 0xff); + } + if (val[2] & 0xf0) + fprintf(of, ",%d", (val[1] >> 4) & 0xf); + fprintf(of, "),"); + fprintf(of, "%x", ((val[4] << 8) & 0xf00) | val[5]); + if (tab->type & TWOOP) { + fprintf(of, "(%d,", val[1] & 0xf); + } else { + fprintf(of, "("); + } + fprintf(of, "%d)", (val[4] >> 4) & 0xf); + break; + } + } + } +} + +//fprint_val (of, inst, 16, 8, PV_RZRO); +return -(l-1); + +return SCPE_OK; +} + + +/* 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) +{ +int32 i, j, k; +t_value d, tag; +t_stat r; + +while (isspace (*cptr)) cptr++; + return SCPE_OK; + +/* Symbolic input, continued */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} +