1
0
mirror of https://github.com/rcornwell/sims.git synced 2026-01-16 08:23:21 +00:00

IBM360: Initial commit

This commit is contained in:
Richard Cornwell 2017-10-15 13:22:36 -04:00
parent 39ee29e66a
commit eb4320e2e4
13 changed files with 7670 additions and 0 deletions

68
IBM360/Makefile Normal file
View File

@ -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}

40
IBM360/STATUS.txt Normal file
View File

@ -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.

229
IBM360/ibm360_cdp.c Normal file
View File

@ -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

252
IBM360/ibm360_cdr.c Normal file
View File

@ -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

799
IBM360/ibm360_chan.c Normal file
View File

@ -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;
}

304
IBM360/ibm360_com.c Normal file
View File

@ -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

333
IBM360/ibm360_con.c Normal file
View File

@ -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 <ctype.h>
#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

2152
IBM360/ibm360_cpu.c Normal file

File diff suppressed because it is too large Load Diff

1295
IBM360/ibm360_dasd.c Normal file

File diff suppressed because it is too large Load Diff

372
IBM360/ibm360_defs.h Normal file
View File

@ -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 *);

288
IBM360/ibm360_lpr.c Normal file
View File

@ -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 <ctype.h>
#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

891
IBM360/ibm360_mt.c Normal file
View File

@ -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

647
IBM360/ibm360_sys.c Normal file
View File

@ -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 <ctype.h>
#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;
}