/* i7070_chan.c: IBM 7070 Channel simulator | |
Copyright (c) 2005-2016, Richard Cornwell | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
channel | |
The system state for the IBM 7070 channel is: | |
There are 4 types of channel: | |
PIO: Basic polled mode transfer. Channel only manages | |
status and disconnect of devices. | |
7604: Basic channel. | |
7907: Enhanced channel for disk, hypertape and com controlers. | |
Common registers to all but PIO channels. | |
ADDR<0:16> Location to read or write next word from. | |
CMD<0:6> Channel command. | |
LIMIT<0:16> Transfer limit | |
ASM<0:44> Assembled data from devices. | |
LOCATION<0:16> Address of next command. | |
Simulation registers to handle device handshake. | |
STATUS<0:16> Simulated register for basic channel status. | |
SENSE<0:16> Additional flags for 7907 channels. | |
*/ | |
#include "i7070_defs.h" | |
extern UNIT cpu_unit; | |
#define CHAN_DEF UNIT_DISABLE|CHAN_SET | |
t_stat chan_reset(DEVICE * dptr); | |
void chan_fetch(int chan); | |
t_stat chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, | |
const char *cptr); | |
const char *chan_description (DEVICE *dptr); | |
/* Channel data structures | |
chan_dev Channel device descriptor | |
chan_unit Channel unit descriptor | |
chan_reg Channel register list | |
chan_mod Channel modifiers list | |
*/ | |
uint32 location[NUM_CHAN]; /* Location of RDW instruction*/ | |
uint32 caddr[NUM_CHAN]; /* Channel memory address */ | |
uint8 bcnt[NUM_CHAN]; /* Channel character count */ | |
uint8 cmd[NUM_CHAN]; /* Current command */ | |
uint8 op[NUM_CHAN]; /* Operators for 7907 channel */ | |
uint32 limit[NUM_CHAN]; /* Word count */ | |
t_uint64 assembly[NUM_CHAN]; /* Assembly register */ | |
uint32 chan_flags[NUM_CHAN]; /* Unit status */ | |
uint32 chan_info[NUM_CHAN]; /* Private channel info */ | |
uint8 chan_irq[NUM_CHAN]; /* Channel has a irq pending */ | |
extern uint16 pri_latchs[10]; | |
#define CHAN_OUTDEV 0x010000 /* Type out device */ | |
#define CHAN_PRIO 0x008000 /* Channel has priority pending */ | |
#define CHAN_TWE 0x004000 /* Channel format error */ | |
#define CHAN_SEOR 0x002000 /* Channel saw a eor */ | |
#define CHAN_NORDW 0x020000 /* No RDW for this command */ | |
#define CHAN_SEOS 0x040000 /* Channel saw a end of segment */ | |
#define CHAN_SCLR 0x080000 /* Short record */ | |
#define CHAN_FIRST 0x100000 /* First tranfered word */ | |
#define CHAN_START 0x200000 /* Channel has just started */ | |
#define CHAN_OCTAL 0x400000 /* Octal conversion */ | |
const char *chan_type_name[] = { | |
"Polled", "Unit Record", "7604", "7907", ""}; | |
UNIT chan_unit[] = { | |
/* Puesdo channel for PIO devices */ | |
{UDATA(NULL, CHAN_SET|CHAN_S_TYPE(CHAN_UREC)|UNIT_S_CHAN(CHAN_CHUREC),0)}, | |
/* Normal channels */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_A)|CHAN_S_TYPE(CHAN_7604),0)},/* A */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_B)|CHAN_S_TYPE(CHAN_7604),0)},/* B */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_C)|CHAN_S_TYPE(CHAN_7604),0)},/* C */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_D)|CHAN_S_TYPE(CHAN_7604),0)},/* D */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_E)|CHAN_S_TYPE(CHAN_7907),0)},/* E */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_F)|CHAN_S_TYPE(CHAN_7907),0)},/* F */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_G)|CHAN_S_TYPE(CHAN_7907),0)},/* G */ | |
{UDATA(NULL, CHAN_DEF|UNIT_S_CHAN(CHAN_H)|CHAN_S_TYPE(CHAN_7907),0)} /* H */ | |
}; | |
REG chan_reg[] = { | |
{BRDATA(ADDR, caddr, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, | |
{BRDATA(CMD, cmd, 8, 6, NUM_CHAN), REG_RO|REG_FIT}, | |
{BRDATA(LIMIT, limit, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, | |
{BRDATA(ASM, assembly, 16, 44, NUM_CHAN), REG_VMIO|REG_RO|REG_FIT}, | |
{BRDATA(LOCATION, location, 10, 18, NUM_CHAN), REG_RO|REG_FIT}, | |
{BRDATA(FLAGS, chan_flags, 2, 32, NUM_CHAN), REG_RO|REG_FIT}, | |
{NULL} | |
}; | |
MTAB chan_mod[] = { | |
{CHAN_MODEL, CHAN_S_TYPE(CHAN_UREC), "UREC", NULL, NULL,NULL,NULL}, | |
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7604), "7604", NULL, NULL, NULL, NULL}, | |
{CHAN_MODEL, CHAN_S_TYPE(CHAN_7907), "7907", NULL, NULL, NULL, NULL}, | |
{MTAB_VUN, 0, "UNITS", NULL, NULL, &print_chan, NULL, "Show units on channel"}, | |
{0} | |
}; | |
/* Simulator debug controls */ | |
DEBTAB chn_debug[] = { | |
{"CHANNEL", DEBUG_CHAN}, | |
{"TRAP", DEBUG_TRAP}, | |
{"CMD", DEBUG_CMD}, | |
{"DATA", DEBUG_DATA}, | |
{"DETAIL", DEBUG_DETAIL}, | |
{"EXP", DEBUG_EXP}, | |
{"SENSE", DEBUG_SNS}, | |
{"CH0", 0x0100 << 0}, | |
{"CH1", 0x0100 << 1}, | |
{"CH2", 0x0100 << 2}, | |
{"CH3", 0x0100 << 3}, | |
{"CH4", 0x0100 << 4}, | |
{"CHA", 0x0100 << 5}, | |
{"CHB", 0x0100 << 6}, | |
{"CHC", 0x0100 << 7}, | |
{"CHD", 0x0100 << 8}, | |
{0, 0} | |
}; | |
DEVICE chan_dev = { | |
"CH", chan_unit, chan_reg, chan_mod, | |
NUM_CHAN, 10, 18, 1, 10, 44, | |
NULL, NULL, &chan_reset, NULL, NULL, NULL, | |
NULL, DEV_DEBUG, 0, chn_debug, | |
NULL, NULL, &chan_help, NULL, NULL, &chan_description | |
}; | |
#define DELTA_CHAR 057 | |
#define SM_CHAR 037 | |
#define SM_MEM 0x39 | |
#define RM_CHAR 0x80 | |
/* Translation tables */ | |
uint8 bcd_mem[64] = { | |
/* ? 1 2 3 4 5 6 7 */ | |
/* 00 */ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, | |
/* 8 9 0 =/# !/@ ? ? tm */ | |
/* 10 */ 0x98, 0x99, 0x90, 0x45, 0x46, 0x47, 0x48, 0x49, | |
/* sp / S T U V W X */ | |
/* 20 */ 0x60, 0x31, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, | |
/* Y Z rm , %/( ? ? sm */ | |
/* 30 */ 0x88, 0x89, 0x80, 0x35, 0x36, 0x37, 0x38, 0x39, | |
/* - J K L M N O P */ | |
/* 40 */ 0x30, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, | |
/* Q R -0 $ * ? ? del */ | |
/* 50 */ 0x78, 0x79, 0x70, 0x25, 0x26, 0x27, 0x28, 0xFF, | |
/*+/& A B C D E F G */ | |
/* 60 */ 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, | |
/* H I +0 . sq ? ? gm */ | |
/* 70 */ 0x68, 0x69, 0x60, 0x15, 0x16, 0x17, 0x18, 0x19 | |
}; | |
uint8 mem_bcd[256] = { | |
/* sp */ | |
/* 00 */ 020, 000, 000, 000, 000, 000, 000, 000, | |
/* */ | |
/* 08 */ 000, 000, 000, 000, 000, 000, 000, 000, | |
/* sq ? */ | |
/* 10 */ 000, 000, 000, 000, 000, 073, 074, 075, | |
/* ? ? */ | |
/* 18 */ 076, 077, 000, 000, 000, 000, 000, 000, | |
/*+/- $ * ? */ | |
/* 20 */ 060, 000, 000, 000, 000, 053, 054, 055, | |
/* ? +/- */ | |
/* 28 */ 056, 060, 000, 000, 000, 000, 000, 000, | |
/* - / , %/( ? */ | |
/* 30 */ 040, 021, 000, 000, 000, 033, 034, 035, | |
/* ? sm */ | |
/* 38 */ 036, 037, 000, 000, 000, 000, 000, 000, | |
/* =/# !/@ ? */ | |
/* 40 */ 000, 000, 000, 000, 000, 013, 014, 015, | |
/* ? tm */ | |
/* 48 */ 016, 017, 000, 000, 000, 000, 000, 000, | |
/* */ | |
/* 50 */ 000, 000, 000, 000, 000, 000, 000, 000, | |
/* */ | |
/* 58 */ 000, 000, 000, 000, 000, 000, 000, 000, | |
/* +0 A B C D E F G */ | |
/* 60 */ 072, 061, 062, 063, 064, 065, 066, 067, | |
/* H I */ | |
/* 68 */ 070, 071, 000, 000, 000, 000, 000, 000, | |
/* -0 J K L M N O P */ | |
/* 70 */ 052, 041, 042, 043, 044, 045, 046, 047, | |
/* Q R */ | |
/* 78 */ 050, 051, 000, 000, 000, 000, 000, 000, | |
/* rm S T U V W X */ | |
/* 80 */ 032, 000, 022, 023, 024, 025, 026, 027, | |
/* Y Z */ | |
/* 88 */ 030, 031, 000, 000, 000, 000, 000, 000, | |
/* 0 1 2 3 4 5 6 7 */ | |
/* 90 */ 012, 001, 002, 003, 004, 005, 006, 007, | |
/* 8 9 */ | |
/* 98 */ 010, 011, 000, 000, 000, 000, 000, 000 | |
}; | |
t_stat | |
chan_reset(DEVICE * dptr) | |
{ | |
int i; | |
/* Clear channel assignment */ | |
for (i = 0; i < NUM_CHAN; i++) { | |
chan_flags[i] = 0; | |
chan_info[i] = 0; | |
caddr[i] = 0; | |
cmd[i] = 0; | |
bcnt[i] = 10; | |
chan_irq[i] = 0; | |
limit[i] = 0; | |
location[i] = 0; | |
} | |
return chan_set_devs(dptr); | |
} | |
/* Boot from given device */ | |
t_stat | |
chan_boot(int32 unit_num, DEVICE * dptr) | |
{ | |
return SCPE_NOFNC; /* Not implimented until I know how boot work */ | |
} | |
t_stat | |
chan_issue_cmd(uint16 chan, uint16 dcmd, uint16 dev) { | |
DEVICE **dptr; | |
DIB *dibp; | |
uint32 j; | |
UNIT *uptr; | |
for (dptr = sim_devices; *dptr != NULL; dptr++) { | |
int r; | |
dibp = (DIB *) (*dptr)->ctxt; | |
/* If no DIB, not channel device */ | |
if (dibp == 0) | |
continue; | |
uptr = (*dptr)->units; | |
/* If this is a 79XX device, check it */ | |
if (dibp->ctype & CH_TYP_79XX) { | |
for (j = 0; j < (*dptr)->numunits; j++, uptr++) { | |
if ((uptr->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(uptr->flags) == chan && | |
(dev == ((UNIT_SELECT & uptr->flags) != 0))) { | |
r = dibp->cmd(uptr, dcmd, dev); | |
if (r != SCPE_NODEV) | |
return r; | |
} | |
} | |
} else if ((dibp->addr & dibp->mask) == (dev & dibp->mask)) { | |
if (dibp->upc == 1) { | |
for (j = 0; j < (*dptr)->numunits; j++) { | |
if ((uptr->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(uptr->flags) == chan) { | |
r = dibp->cmd(uptr, dcmd, dev); | |
if (r != SCPE_NODEV) | |
return r; | |
} | |
uptr++; | |
} | |
} else { | |
if ((uptr->flags & UNIT_DIS) == 0 && | |
UNIT_G_CHAN(uptr->flags) == chan) { | |
r = dibp->cmd(uptr, dcmd, dev); | |
if (r != SCPE_NODEV) | |
return r; | |
} | |
} | |
} | |
} | |
return SCPE_NODEV; | |
} | |
/* Execute the next channel instruction. */ | |
void | |
chan_proc() | |
{ | |
int chan; | |
int cmask; | |
/* Scan channels looking for work */ | |
for (chan = 0; chan < NUM_CHAN; chan++) { | |
/* Skip if channel is disabled */ | |
if (chan_unit[chan].flags & UNIT_DIS) | |
continue; | |
cmask = 0x0100 << chan; | |
switch (CHAN_G_TYPE(chan_unit[chan].flags)) { | |
case CHAN_UREC: | |
case CHAN_7604: | |
/* If channel is disconnecting, do nothing */ | |
if (chan_flags[chan] & DEV_DISCO) | |
continue; | |
/* If device requested attention, abort current command */ | |
if (chan_flags[chan] & CHS_ATTN) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, "chan %d Attn\n", | |
chan); | |
chan_flags[chan] &= | |
~(CHS_ATTN | STA_START | STA_ACTIVE | STA_WAIT); | |
/* Disconnect if selected */ | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= (DEV_DISCO); | |
continue; | |
} | |
/* If no select, stop channel */ | |
if ((chan_flags[chan] & DEV_SEL) == 0 | |
&& (chan_flags[chan] & STA_TWAIT)) { | |
t_uint64 temp; | |
int adr; | |
chan_trap: | |
if (chan != 0) { | |
adr = 100 + (chan * 10) + (chan_info[chan] & 0xf); | |
temp = 2; | |
if (chan_info[chan] & CHAN_TWE) | |
temp = 0; | |
else if (chan_flags[chan] & CHS_ERR) | |
temp = 1; | |
else if (chan_flags[chan] & CHS_EOF) | |
temp = 5; | |
else if (chan_info[chan] & CHAN_SEOS) | |
temp = 6; | |
else if (chan_info[chan] & CHAN_SCLR) | |
temp = 7; | |
else if ((chan_info[chan] & CHAN_NORDW) == 0) { | |
if ((chan_info[chan] & CHAN_SEOR) == 0 && | |
caddr[chan] > limit[chan]) | |
temp = 4; | |
else if (caddr[chan] < limit[chan]) | |
temp = 3; | |
} | |
chan_flags[chan] &= ~(CHS_ERR|CHS_EOF); | |
temp <<= 32; | |
if (chan_info[chan] & CHAN_NORDW) { | |
temp |= M[adr] & 0xFFFFFFFFLL; | |
} else { | |
upd_idx(&temp, caddr[chan]); | |
bin_dec(&temp, location[chan], 0, 4); | |
} | |
temp |= PSIGN; | |
/* Copy over flag */ | |
temp |= M[adr] & 0xF000000000LL; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_TRAP, &chan_dev, | |
"chan %d Trap: %012llx prio=%d\n\r", chan, | |
temp, (chan_info[chan]&CHAN_PRIO)?1:0); | |
M[adr] = temp; | |
if ((chan_info[chan] & CHAN_PRIO) || | |
((temp >> 32) & 0xf) != 2) | |
pri_latchs[chan] |= 1 << (chan_info[chan] & 0xF); | |
chan_info[chan] &= ~CHAN_PRIO; | |
} else if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap %04x\n", | |
chan, chan_info[chan]); | |
chan_flags[chan] &= | |
~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT); | |
continue; | |
} | |
/* No activity, nothing happening here folks, move along */ | |
if ((chan_flags[chan] & (STA_ACTIVE | STA_WAIT)) == 0) { | |
/* This could be a no data command, if pending priorty | |
request, ask device if it is ready */ | |
if ((cmd[chan] & CHN_SEGMENT) == 0 && | |
chan_info[chan] & CHAN_PRIO && | |
chan_issue_cmd(chan, IO_TRS, chan_info[chan]&0xf) | |
== SCPE_OK) | |
goto chan_trap; | |
continue; | |
} | |
/* If first time through here, load up channel control */ | |
if (chan_flags[chan] & STA_ACTIVE && chan_info[chan] & CHAN_START) | |
chan_fetch(chan); | |
/* Process reading of a segment command */ | |
if ((cmd[chan] & (CHN_SEGMENT|CHN_RM_FND)) == | |
(CHN_SEGMENT|CHN_RM_FND)) { | |
/* Two backspaces and a read */ | |
switch (cmd[chan] & (CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS)) { | |
case CHN_RM_FND: | |
if (chan_issue_cmd(chan, IO_BSR, | |
chan_info[chan]&0xf) == SCPE_OK) { | |
cmd[chan] |= CHN_COMPRESS; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"segment %d bsr 2\n\r", chan); | |
} | |
break; | |
case CHN_RM_FND|CHN_COMPRESS: | |
if (chan_issue_cmd(chan, IO_BSR, | |
chan_info[chan]&0xf) == SCPE_OK) { | |
cmd[chan] |= CHN_NUM_MODE; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"segment %d bsr 2\n\r", chan); | |
if (chan_flags[chan] & CHS_BOT) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
goto chan_trap; | |
} | |
} | |
break; | |
case CHN_RM_FND|CHN_NUM_MODE|CHN_COMPRESS: | |
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST); | |
if ( chan_issue_cmd(chan, IO_RDS, | |
chan_info[chan]&0xf) == SCPE_OK) { | |
cmd[chan] &= ~(CHN_NUM_MODE|CHN_COMPRESS|CHN_RM_FND); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"segment %d read\n\r", chan); | |
chan_flags[chan] &= ~(STA_WAIT|DEV_REOR); | |
} | |
break; | |
} | |
/* Device ready, decide command to issue */ | |
if (cmd[chan] & CHN_RECORD) { | |
if (chan_flags[chan] & CHS_BOT) { | |
chan_flags[chan] |= STA_TWAIT; | |
} | |
/* Two backspaces and a read */ | |
} else if (chan_flags[chan] & CHS_EOT) { | |
chan_flags[chan] |= STA_TWAIT; | |
} | |
continue; | |
} | |
/* None disabled, active channel is if transfering */ | |
switch (chan_flags[chan] & (DEV_WRITE | DEV_FULL)) { | |
/* Device has given us a dataword */ | |
case DEV_FULL: | |
/* Process reading of a segment command */ | |
if (cmd[chan] & CHN_SEGMENT) { | |
/* Check if hit end of record */ | |
#if 0 /* Check segment operation correct before removing */ | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"chk segment %d %d data = %012llx", | |
chan, bcnt[chan], assembly[chan]); | |
if ((chan_flags[chan] & (STA_WAIT|DEV_REOR)) | |
== (STA_WAIT|DEV_REOR)) { | |
chan_flags[chan] &= ~(STA_WAIT|DEV_REOR|DEV_FULL); | |
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST); | |
continue; | |
} | |
if (bcnt[chan] >= 6 && chan_flags[chan] & DEV_REOR) { | |
if (chan_info[chan] & CHAN_SEOS) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, " found\n"); | |
/* Correct error */ | |
chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR); | |
/* What we were looking for? */ | |
if (caddr[chan] >= limit[chan]) { | |
chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF); | |
chan_flags[chan] |= STA_TWAIT; | |
chan_info[chan] |= CHAN_SEOR; | |
} else | |
caddr[chan]++; | |
} | |
chan_info[chan] &= ~(CHAN_SEOS|CHAN_FIRST|CHAN_TWE); | |
chan_flags[chan] &= ~(DEV_FULL|DEV_REOR|CHS_ERR); | |
} else | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, " search\n"); | |
/* How about regular record */ | |
chan_flags[chan] &= ~DEV_FULL; | |
/* Wait for next record */ | |
chan_flags[chan] |= STA_WAIT|DEV_DISCO; | |
if (cmd[chan] & CHN_RECORD) | |
cmd[chan] |= CHN_RM_FND; | |
else | |
cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE; | |
assembly[chan] = 0; | |
bcnt[chan] = 10; | |
#endif | |
continue; | |
} | |
/* If we are not waiting EOR save it in memory */ | |
if ((chan_flags[chan] & STA_WAIT) == 0) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d data < %011llx\n", | |
chan, assembly[chan]); | |
if (caddr[chan] < MEMSIZE) | |
M[caddr[chan]] = assembly[chan]; | |
if (bcnt[chan] != 0) | |
chan_info[chan] |= CHAN_SCLR; | |
else | |
chan_info[chan] &= ~CHAN_SCLR; | |
/* Check if last transfer before final */ | |
if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT; | |
} else | |
caddr[chan]++; | |
/* Update channel status word */ | |
if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) { | |
int adr = 100 + (chan * 10) + (chan_info[chan]&0xf); | |
upd_idx(&M[adr], caddr[chan]); | |
bin_dec(&M[adr], location[chan], 0, 4); | |
} | |
/* Check for record mark */ | |
if ((cmd[chan] & CHN_RECORD) && | |
(assembly[chan] & SMASK) == ASIGN && | |
(assembly[chan] & 0xFF) == RM_CHAR) { | |
if (cmd[chan] & CHN_LAST) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT; | |
} else | |
chan_fetch(chan); | |
} | |
bcnt[chan] = 10; | |
assembly[chan] = 0; | |
} | |
chan_info[chan] |= CHAN_FIRST; /* Saved one char */ | |
chan_flags[chan] &= ~DEV_FULL; | |
/* Device does not need a word and has not given us one */ | |
case 0: | |
if (chan_flags[chan] & DEV_REOR) { | |
/* Check EOR at end of segment */ | |
if (cmd[chan] & CHN_SEGMENT) { | |
if ((chan_info[chan] & CHAN_FIRST) == 0 && | |
bcnt[chan] == 8 && | |
assembly[chan] == (ASIGN|(((t_uint64)SM_MEM) << 32))){ | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"chk segment %d %d data = %012llx found\n\r", | |
chan, bcnt[chan], assembly[chan]); | |
/* What we were looking for? */ | |
if (caddr[chan] >= limit[chan]) { | |
chan_flags[chan] &= ~(STA_ACTIVE|CHS_EOF); | |
chan_flags[chan] |= STA_TWAIT; | |
} else | |
caddr[chan]++; | |
} else | |
/* Check if hit end of record */ | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DETAIL, &chan_dev, | |
"chk segment %d %d data = %012llx search\n\r", | |
chan, bcnt[chan], assembly[chan]); | |
/* Correct error */ | |
chan_info[chan] &= ~(CHAN_TWE|CHAN_SEOS|CHAN_SCLR); | |
assembly[chan] = 0; | |
bcnt[chan] = 10; | |
chan_flags[chan] &= ~(DEV_REOR|DEV_FULL); | |
/* Wait for next record */ | |
chan_flags[chan] |= STA_WAIT|DEV_DISCO; | |
if (cmd[chan] & CHN_RECORD) | |
cmd[chan] |= CHN_RM_FND; | |
else | |
cmd[chan] |= CHN_RM_FND|CHN_COMPRESS|CHN_NUM_MODE; | |
continue; | |
} | |
/* Device idle, expecting data from it */ | |
/* Check if got EOR */ | |
chan_flags[chan] &= ~(DEV_REOR|STA_ACTIVE|STA_WAIT); | |
chan_flags[chan] |= STA_TWAIT|DEV_DISCO; | |
chan_info[chan] |= CHAN_SEOR; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, "chan %d EOR< %o\n", | |
chan, cmd[chan]); | |
continue; | |
} | |
if (caddr[chan] > limit[chan] | |
&& (chan_flags[chan] & STA_WAIT) == 0) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, | |
"chan %d < WC0 %o\n", chan, cmd[chan]); | |
if (cmd[chan] & CHN_LAST) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_DISCO; | |
chan_info[chan] &= ~CHAN_SEOR; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, | |
"chan %d < DISCO\n", chan); | |
} else | |
chan_fetch(chan); | |
} | |
break; | |
/* Device has word, but has not taken it yet */ | |
case DEV_WRITE | DEV_FULL: | |
continue; /* Do nothing if no data xfer pending */ | |
/* Device needs a word of data */ | |
case DEV_WRITE: /* Device needs data word */ | |
/* If we are waiting on EOR, do nothing */ | |
if (chan_flags[chan] & STA_WAIT) | |
continue; | |
/* Special for write segment mark command */ | |
if (cmd[chan] & CHN_SEGMENT) { | |
/* Send one char */ | |
assembly[chan] = SM_MEM; | |
bcnt[chan] = 2; | |
caddr[chan] = limit[chan]+1; | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT|DEV_FULL|DEV_WEOR; | |
cmd[chan] = CHN_ALPHA|CHN_SEGMENT; | |
chan_info[chan] |= CHAN_NORDW; | |
continue; | |
} | |
/* Give device new word if we have one */ | |
if (caddr[chan] <= limit[chan]) { | |
/* Check if got EOR */ | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= ~(STA_WAIT | DEV_REOR | |
| STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, | |
"chan %d EOR> %o\n", chan, cmd[chan] & 070); | |
continue; | |
} | |
if (caddr[chan] < MEMSIZE) | |
assembly[chan] = M[caddr[chan]]; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d data > %011llx\n", chan, | |
assembly[chan]); | |
bcnt[chan] = 10; | |
chan_flags[chan] |= DEV_FULL; | |
/* Check if last transfer before final */ | |
if (caddr[chan] >= limit[chan] && cmd[chan] & CHN_LAST) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT; | |
} else | |
caddr[chan]++; | |
/* Update channel status word */ | |
if (chan != 0 && (chan_info[chan] & CHAN_NORDW) == 0) { | |
int adr = 100 + (chan * 10) + (chan_info[chan]&0xf); | |
upd_idx(&M[adr], caddr[chan]); | |
bin_dec(&M[adr], location[chan], 0, 4); | |
} | |
/* Check for record mark */ | |
if ((cmd[chan] & CHN_RECORD) && | |
(assembly[chan] & SMASK) == ASIGN && | |
(assembly[chan] & 0xFF) == RM_CHAR) { | |
if (cmd[chan] & CHN_LAST) { | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT|STA_WAIT; | |
} else | |
chan_fetch(chan); | |
} | |
continue; /* Don't start next command until data taken */ | |
} | |
/* Wait for device to recognize EOR */ | |
if (chan_flags[chan] & DEV_WEOR) | |
continue; | |
/* Get here if passed limit */ | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, "chan %d > WC0 %o stat=%x\n", | |
chan, cmd[chan] & 070, chan_flags[chan]); | |
if (cmd[chan] & CHN_LAST) { | |
chan_flags[chan] |= DEV_DISCO | DEV_WEOR | STA_TWAIT; | |
chan_flags[chan] &= ~(STA_START | STA_ACTIVE); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_EXP, &chan_dev, | |
"chan %d > DISCO\n", chan); | |
} else | |
chan_fetch(chan); | |
} | |
break; | |
case CHAN_7907: | |
/* If channel is disconnecting, just hold on */ | |
if (chan_flags[chan] & DEV_DISCO) | |
continue; | |
/* If no select, stop channel */ | |
if ((chan_flags[chan] & DEV_SEL) == 0 | |
&& (chan_flags[chan] & STA_TWAIT)) { | |
t_uint64 temp; | |
temp = 2; | |
if (chan_info[chan] & CHAN_TWE) | |
temp = 1; | |
else if (chan_flags[chan] & SNS_UEND) | |
temp = 5; | |
else if ((chan_info[chan] & CHAN_SEOR) == 0 && op[chan] == 1) | |
temp = 4; | |
else if (caddr[chan] <= limit[chan] && | |
(op[chan] == 1 || op[chan] == 3)) | |
temp = 3; | |
temp <<= 36; | |
chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2); | |
temp |= (chan_irq[chan])?MSIGN:PSIGN; | |
chan_flags[chan] &= ~(SNS_UEND|CTL_CNTL|CTL_SNS|CTL_READ| | |
CTL_WRITE|CTL_PREAD|CTL_PWRITE); | |
upd_idx(&temp, caddr[chan]); | |
bin_dec(&temp, location[chan], 0, 4); | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Trap: %012llx\n", | |
chan, temp); | |
M[(chan - 4) + 300] = temp; | |
if ((chan_info[chan] & CHAN_PRIO) || ((temp >> 36) & 0xf) != 2) | |
pri_latchs[8] |= 1<<(4-chan); | |
chan_flags[chan] &= | |
~(STA_START | STA_ACTIVE | STA_WAIT | STA_TWAIT); | |
chan_info[chan] &= ~CHAN_PRIO; | |
continue; | |
} | |
/* Check if device raised attention */ | |
if ((chan_flags[chan] & (STA_ACTIVE|DEV_SEL|STA_TWAIT)) == 0 && | |
(chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2))) { | |
t_uint64 temp; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_TRAP, &chan_dev, "chan %d Attn Trap\n", | |
chan); | |
temp = 2; | |
if (chan_flags[chan] & SNS_UEND) | |
temp = 5; | |
temp <<= 36; | |
temp |= MSIGN; | |
chan_irq[chan] |= chan_flags[chan] & (SNS_ATTN1|SNS_ATTN2); | |
chan_flags[chan] &= ~(SNS_ATTN1|SNS_ATTN2|SNS_UEND); | |
upd_idx(&temp, caddr[chan]); | |
bin_dec(&temp, location[chan], 0, 4); | |
M[(chan - 4) + 300] = temp; | |
pri_latchs[9] |= 1 << (4 - chan); | |
continue; | |
} | |
/* Nothing more to do if not active. */ | |
if (chan_flags[chan] & STA_ACTIVE) { | |
t_uint64 temp; | |
/* Execute the next command */ | |
switch (op[chan]) { | |
case 6: /* Tranfer in channel */ | |
/* I am not sure if this is correct, but it passes | |
diagnostics */ | |
location[chan] = limit[chan]; | |
break; | |
case 0: /* Write status */ | |
temp = PSIGN|(2LL << 36); | |
upd_idx(&temp, caddr[chan]); | |
bin_dec(&temp, location[chan], 0, 4); | |
M[caddr[chan]] = temp; | |
break; | |
case 1: /* Read */ | |
/* Check if in other mode */ | |
if (chan_flags[chan] & (CTL_CNTL)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d read busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= | |
~(CTL_CNTL|DEV_REOR|DEV_WRITE); | |
} else | |
continue; | |
} | |
/* Check if last command not finished */ | |
if (chan_flags[chan] & (CTL_SNS|CTL_WRITE)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d read busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= DEV_DISCO; | |
chan_flags[chan] &= ~(CTL_SNS|CTL_WRITE); | |
} | |
continue; | |
} | |
/* If not already in read mode, set read mode */ | |
if ((chan_flags[chan] & CTL_READ) == 0) { | |
chan_flags[chan] |= CTL_READ; | |
chan_flags[chan] &= ~(DEV_FULL|DEV_WRITE|DEV_REOR); | |
chan_info[chan] &= ~CHAN_SEOR; | |
bcnt[chan] = 10; | |
assembly[chan] = 0; | |
continue; | |
} | |
/* Has device given us a word */ | |
if (chan_flags[chan] & DEV_FULL) { | |
/* Check if record mark */ | |
if ((cmd[chan] & CHN_RECORD) && | |
(assembly[chan] & SMASK) == ASIGN && | |
(assembly[chan] & 0xFF) == RM_CHAR) { | |
break; | |
} | |
/* Check if ready to transfer something */ | |
if (caddr[chan] <= limit[chan]) { | |
M[caddr[chan]] = assembly[chan]; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d data > %012llx\n", | |
chan, assembly[chan]); | |
caddr[chan]++; | |
bcnt[chan] = 10; | |
assembly[chan] = 0; | |
chan_flags[chan] &= ~DEV_FULL; | |
/* Check if last word transfered */ | |
if (caddr[chan] > limit[chan]) | |
break; | |
} | |
/* Check if we still have a select signal */ | |
if ((chan_flags[chan] & DEV_SEL) == 0) { | |
chan_info[chan] |= CHAN_TWE; | |
chan_flags[chan] &= | |
~(CTL_WRITE|CTL_END|STA_ACTIVE); | |
break; | |
} | |
/* Device gave us a EOR, get next word */ | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_info[chan] |= CHAN_SEOR; | |
chan_flags[chan] &= ~DEV_REOR; | |
break; | |
} | |
continue; | |
} | |
/* Abort if we get control end */ | |
if (chan_flags[chan] & CTL_END) { | |
/* Disconnect channel if select still active */ | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= (DEV_DISCO); | |
} | |
if (chan_flags[chan] & DEV_REOR) | |
chan_info[chan] |= CHAN_SEOR; | |
chan_flags[chan] &= | |
~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END); | |
break; | |
} | |
continue; | |
case 3: /* Write */ | |
/* Check if in other mode */ | |
if (chan_flags[chan] & (CTL_CNTL)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d write busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= | |
~(CTL_CNTL|DEV_REOR|DEV_WRITE); | |
} else | |
continue; | |
} | |
/* Check if last command not finished */ | |
if (chan_flags[chan] & (CTL_SNS|CTL_READ)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d write busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= DEV_DISCO; | |
chan_flags[chan] &= ~(CTL_READ|CTL_SNS); | |
} | |
continue; | |
} | |
/* If not in write, flag as write */ | |
if ((chan_flags[chan] & CTL_WRITE) == 0) { | |
chan_flags[chan] |= CTL_WRITE|DEV_WRITE; | |
} | |
/* Command finished?*/ | |
if (chan_flags[chan] & CTL_END) { | |
/* Disconnect channel if select still active */ | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= (DEV_DISCO); | |
} | |
chan_flags[chan] &= | |
~(DEV_REOR|CTL_SNS|CTL_READ|CTL_WRITE|CTL_END); | |
break; | |
} | |
/* Check if we still have a select signal */ | |
if ((chan_flags[chan] & DEV_SEL) == 0 && | |
caddr[chan] < limit[chan]) { | |
chan_info[chan] |= CHAN_TWE; | |
chan_flags[chan] &= ~(CTL_WRITE|CTL_END|STA_ACTIVE); | |
break; | |
} | |
/* Check if device needs data */ | |
if ((chan_flags[chan] & DEV_FULL) == 0) { | |
/* Got EOR? */ | |
if (chan_flags[chan] & DEV_REOR) { | |
if (caddr[chan] > limit[chan]) { | |
chan_info[chan] |= CHAN_SEOR; | |
} | |
chan_flags[chan] |= DEV_DISCO; | |
chan_flags[chan] &= ~DEV_REOR; | |
break; | |
} | |
/* Check if ready to transfer something */ | |
if (caddr[chan] <= limit[chan]) { | |
assembly[chan] = M[caddr[chan]]; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d data > %012llx\n", | |
chan, assembly[chan]); | |
caddr[chan]++; | |
bcnt[chan] = 10; | |
chan_flags[chan] |= DEV_FULL; | |
/* Check if record mark */ | |
if ((cmd[chan] & CHN_RECORD) && | |
(assembly[chan] & SMASK) == ASIGN && | |
(assembly[chan] & 0xFF) == RM_CHAR) { | |
chan_flags[chan] |= DEV_WEOR; | |
break; | |
} | |
continue; | |
} | |
chan_info[chan] |= CHAN_SEOR; | |
break; | |
} | |
continue; | |
case 5: /* Sense */ | |
/* Check if in other mode */ | |
if (chan_flags[chan] & (CTL_CNTL)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d sense busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= | |
~(CTL_CNTL|DEV_REOR|DEV_WRITE); | |
} else | |
continue; | |
} | |
if (chan_flags[chan] & CTL_SNS) { | |
/* Check if we still have a select signal */ | |
if ((chan_flags[chan] & DEV_SEL) == 0) { | |
chan_info[chan] |= CHAN_TWE; | |
chan_flags[chan] &= ~(CTL_SNS|STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT; | |
break; | |
} | |
/* If device ended, quit transfer */ | |
if ((chan_flags[chan] & DEV_FULL) == 0) { | |
if (chan_flags[chan] & CTL_END) { | |
/* Disconnect channel if select still active */ | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= (DEV_DISCO); | |
} | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_info[chan] |= CHAN_SEOR; | |
chan_flags[chan] &= ~DEV_REOR; | |
} | |
chan_flags[chan] &= ~(CTL_SNS); | |
break; | |
} | |
/* Check if last word transfered */ | |
if (caddr[chan] > limit[chan]) { | |
if (chan_flags[chan] & SNS_UEND) { | |
chan_flags[chan] |= | |
(DEV_DISCO | DEV_WEOR); | |
chan_flags[chan] &= ~(DEV_SEL); | |
} else { | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= ~DEV_REOR; | |
chan_info[chan] |= CHAN_SEOR; | |
} | |
} | |
chan_flags[chan] &= ~(CTL_SNS); | |
break; | |
} | |
} else { | |
/* Device has given us a dataword */ | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d data < %012llx\n", | |
chan, assembly[chan]); | |
M[caddr[chan]] = assembly[chan]; | |
assembly[chan] = 0; | |
bcnt[chan] = 10; | |
chan_flags[chan] &= ~DEV_FULL; | |
if (caddr[chan] >= limit[chan]) | |
break; | |
caddr[chan]++; | |
} | |
/* Handle EOR on sense */ | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= ~(CTL_SNS|DEV_REOR); | |
chan_info[chan] |= CHAN_SEOR; | |
break; | |
} | |
continue; | |
} | |
/* Check if in other mode */ | |
if (chan_flags[chan] & (CTL_CNTL|CTL_READ|CTL_WRITE)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d sense busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT; | |
} | |
chan_flags[chan] &= ~(CTL_CNTL|CTL_READ|CTL_WRITE); | |
continue; | |
} | |
/* Start sense command */ | |
chan_flags[chan] |= CTL_SNS; | |
chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL); | |
switch(chan_issue_cmd(chan,0,chan_test(chan, CTL_SEL))){ | |
case SCPE_IOERR: | |
case SCPE_NODEV: | |
chan_info[chan] |= CHAN_TWE; | |
chan_flags[chan] &= ~STA_ACTIVE; | |
case SCPE_BUSY: /* Device not ready yet, wait */ | |
chan_flags[chan] &= ~(CTL_SNS); | |
continue; | |
case SCPE_OK: | |
/* Device will be waiting for command */ | |
break; | |
} | |
/* Get channel ready to transfer */ | |
chan_flags[chan] &= ~DEV_WRITE; | |
chan_flags[chan] |= DEV_SEL; | |
continue; | |
case 4: /* Transfer command */ | |
if (chan_flags[chan] & CTL_CNTL) | |
goto xfer; | |
if (chan_flags[chan] & (CTL_READ|CTL_WRITE|CTL_SNS)) { | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_DATA, &chan_dev, | |
"chan %d control busy %04x\n", | |
chan, chan_flags[chan]); | |
if (chan_flags[chan] & DEV_SEL) { | |
chan_flags[chan] |= DEV_DISCO|DEV_WEOR|STA_WAIT; | |
} | |
chan_flags[chan] &= ~(CTL_SNS|CTL_READ|CTL_WRITE); | |
continue; | |
} | |
chan_flags[chan] |= CTL_CNTL; | |
/* Get channel ready to transfer */ | |
chan_flags[chan] &= ~(CTL_END|DEV_REOR|DEV_FULL); | |
switch(chan_issue_cmd(chan,0,chan_stat(chan,CTL_SEL))){ | |
case SCPE_IOERR: | |
case SCPE_NODEV: | |
chan_info[chan] |= CHAN_TWE; | |
chan_flags[chan] &= | |
~(CTL_SNS|CTL_CNTL|STA_ACTIVE); | |
continue; | |
case SCPE_BUSY: | |
/* Device not ready yet, wait */ | |
continue; | |
case SCPE_OK: | |
/* Device will be waiting for command */ | |
break; | |
} | |
chan_flags[chan] |= DEV_WRITE; | |
xfer: | |
/* Check if comand tranfer done */ | |
if (chan_flags[chan] & DEV_REOR) { | |
chan_flags[chan] &= | |
~(DEV_WRITE|DEV_REOR|DEV_FULL|CTL_CNTL); | |
chan_info[chan] |= CHAN_SEOR; | |
break; | |
} | |
/* Wait for device to grab the command */ | |
if (chan_flags[chan] & DEV_FULL) | |
continue; | |
/* Check if device ready for next command word */ | |
if ((chan_flags[chan] & (DEV_WRITE | DEV_FULL)) == | |
DEV_WRITE) { | |
if (caddr[chan] <= limit[chan]) { | |
assembly[chan] = M[caddr[chan]]; | |
chan_flags[chan] |= DEV_FULL; | |
bcnt[chan] = 10; | |
if (chan_dev.dctrl & cmask) | |
sim_debug(DEBUG_CMD, &chan_dev, | |
"chan %d cmd > %012llx\n", | |
chan, assembly[chan]); | |
if (caddr[chan] < limit[chan]) | |
caddr[chan]++; | |
continue; | |
} | |
} | |
break; | |
case 2: /* Read Backwards */ | |
/* Unknown commands at moment */ | |
case 7: | |
case 8: | |
case 9: | |
chan_info[chan] |= CHAN_TWE; | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_DISCO; | |
chan_flags[chan] &= | |
~(STA_ACTIVE|CTL_WRITE|CTL_READ|CTL_CNTL|CTL_SNS); | |
break; | |
} | |
/* If last all done */ | |
if (cmd[chan] & CHN_LAST || chan_flags[chan] & (SNS_UEND) | |
|| chan_info[chan] & CHAN_TWE) { | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= DEV_DISCO; | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
chan_flags[chan] |= STA_TWAIT; | |
} else | |
chan_fetch(chan); | |
} | |
continue; | |
} | |
} | |
} | |
void | |
chan_fetch(int chan) | |
{ | |
uint32 loc = location[chan]; | |
t_uint64 temp; | |
sim_interval--; | |
chan_info[chan] &= ~CHAN_START; | |
if (loc < MEMSIZE) | |
temp = M[loc]; | |
else { | |
cmd[chan] |= CHN_LAST; | |
return; | |
} | |
location[chan] = (loc + 1); | |
if ((temp & SMASK) == MSIGN) | |
cmd[chan] |= CHN_LAST; | |
get_rdw(temp, &caddr[chan], &limit[chan]); | |
op[chan] = (temp >> 36) & 0xf; | |
if (chan_dev.dctrl & (0x0100 << chan)) | |
sim_debug(DEBUG_CHAN, &chan_dev, | |
"chan %d fetch adr=%05d op=%d cmd=%03o caddr=%05d limit=%05d\n", | |
chan, loc, op[chan], cmd[chan], caddr[chan], limit[chan]); | |
} | |
void chan_set_attn_a(int chan) { | |
pri_latchs[0] |= 0x002; | |
} | |
void chan_set_attn_b(int chan) { | |
pri_latchs[0] |= 0x004; | |
} | |
void chan_set_attn_inq(int chan) { | |
if (chan == CHAN_UREC) | |
pri_latchs[0] |= 0x080; | |
else | |
pri_latchs[0] |= 0x100; | |
} | |
void chan_clear_attn_inq(int chan) { | |
if (chan == CHAN_UREC) | |
pri_latchs[0] &= ~0x080; | |
else | |
pri_latchs[0] &= ~0x100; | |
} | |
/* Issue a command to a channel */ | |
int | |
chan_cmd(uint16 dev, uint16 dcmd, uint16 addr) | |
{ | |
uint32 chan; | |
int prio; | |
t_stat r; | |
/* Find device on given channel and give it the command */ | |
chan = (dev >> 8) & 0xf; | |
/* If no channel device, quick exit */ | |
if (chan_unit[chan].flags & UNIT_DIS) | |
return SCPE_IOERR; | |
/* Unit is busy doing something, wait */ | |
if (chan_flags[chan] & (DEV_SEL|DEV_DISCO|STA_TWAIT|STA_WAIT|STA_ACTIVE)) | |
return SCPE_BUSY; | |
/* Ok, try and find the unit */ | |
prio = (dev & 0x1000)? 1: 0; | |
dev &= 0xff; | |
location[chan] = addr; | |
cmd[chan] = dcmd & 0xff; | |
dcmd >>= 8; | |
chan_info[chan] = (dev & 0xf) | (chan << 4); | |
chan_info[chan] |= CHAN_START; | |
/* Special check for console */ | |
if (chan == 0 && dev == 0) | |
chan_info[chan] |= CHAN_OUTDEV; | |
/* Check for octal translation */ | |
if (chan == 1 && dev & 020) | |
chan_info[chan] |= CHAN_OCTAL; | |
/* Enable priority */ | |
if (prio) | |
chan_info[chan] |= CHAN_PRIO; | |
assembly[chan] = 0; | |
bcnt[chan] = 10; | |
if (CHAN_G_TYPE(chan_unit[chan].flags) == CHAN_7907) { | |
chan_flags[chan] |= STA_ACTIVE; | |
if (dev & 1) | |
chan_flags[chan] |= CTL_SEL; | |
else | |
chan_flags[chan] &= ~CTL_SEL; | |
chan_fetch(chan); | |
return SCPE_OK; | |
} | |
r = chan_issue_cmd(chan, dcmd, dev); | |
if (r != SCPE_OK) { | |
/* No device, kill active */ | |
chan_flags[chan] &= ~(STA_ACTIVE); | |
} else { | |
extern uint32 IC; | |
/* If transfering data, activate channel */ | |
if (chan_flags[chan] & DEV_SEL) | |
chan_flags[chan] |= STA_ACTIVE; | |
if (chan_dev.dctrl & (0x0100 << chan)) | |
sim_debug(DEBUG_CMD, &chan_dev, | |
"chan %d cmd=%o IC=%05d addr=%05d\n\r", chan, dcmd, IC, addr); | |
} | |
return r; | |
} | |
/* | |
* Write a word to the assembly register. | |
*/ | |
int | |
chan_write(int chan, t_uint64 * data, int flags) | |
{ | |
return TIME_ERROR; | |
} | |
/* | |
* Read next word from assembly register. | |
*/ | |
int | |
chan_read(int chan, t_uint64 * data, int flags) | |
{ | |
return TIME_ERROR; | |
} | |
/* | |
* Write a char to the assembly register. | |
*/ | |
int | |
chan_write_char(int chan, uint8 * data, int flags) | |
{ | |
uint8 ch = *data; | |
/* Check if last data still not taken */ | |
if (chan_flags[chan] & DEV_FULL) { | |
/* Nope, see if we are waiting for end of record. */ | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] &= ~DEV_WEOR; | |
chan_flags[chan] |= DEV_REOR; | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; /* We had error */ | |
} | |
if (chan == 0) { | |
chan_flags[chan] |= DEV_DISCO; | |
} | |
return TIME_ERROR; | |
} | |
#if 0 /* Check segment working before removing */ | |
/* Check for segment mark */ | |
if (flags & DEV_REOR && (chan_info[chan] & CHAN_FIRST) == 0) { | |
if (ch == SM_CHAR) | |
chan_info[chan] |= CHAN_SEOS; | |
assembly[chan] |= ASIGN|(((t_uint64)bcd_mem[ch]) << 32); | |
if (cmd[chan] & CHN_ALPHA) | |
chan_flags[chan] |= DEV_FULL|DEV_REOR; | |
else | |
chan_flags[chan] |= DEV_REOR; | |
chan_flags[chan] &= ~DEV_WRITE; | |
chan_proc(); | |
return END_RECORD; | |
} | |
/* Clear first char in record flag */ | |
chan_info[chan] |= CHAN_FIRST; | |
#endif | |
if (ch == DELTA_CHAR && (cmd[chan] & CHN_ALPHA) == 0) { | |
if (bcnt[chan] == 10) | |
cmd[chan] ^= CHN_NUM_MODE; | |
else | |
chan_info[chan] |= CHAN_TWE; | |
} else { | |
if (chan_flags[chan] & CTL_SNS) { | |
/* Set sign based on attention signal */ | |
if (bcnt[chan] == 10) { | |
if (chan_irq[chan] & (SNS_ATTN1 >> (chan_info[chan] & 1))) | |
assembly[chan] = PSIGN; | |
else | |
assembly[chan] = MSIGN; | |
chan_irq[chan] &= ~(SNS_ATTN1 >> (chan_info[chan] & 1)); | |
} | |
/* Store character */ | |
ch &= 0x17; | |
if (ch & 0x04) | |
ch ^= 0x24; /* Bit move */ | |
ch |= 0x44; | |
bcnt[chan]-=2; | |
assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan]); | |
} else if (cmd[chan] & CHN_NUM_MODE) { | |
ch &= 0xf; | |
if (ch == 0 || ch > 10) | |
chan_info[chan] |= CHAN_TWE; | |
else if (ch == 10) | |
ch = 0; | |
bcnt[chan]--; | |
assembly[chan] |= ((t_uint64)ch) << (4 * bcnt[chan])|PSIGN; | |
/* Check for sign digit */ | |
switch(*data & 060) { | |
case 0: /* Normal digit */ | |
case 020: /* error */ | |
if (bcnt[chan] == 0) | |
chan_info[chan] |= CHAN_TWE; | |
break; | |
case 040: | |
if (bcnt[chan] > 5) | |
chan_info[chan] |= CHAN_TWE; | |
assembly[chan] &= DMASK; | |
/* Put number in right location */ | |
while(bcnt[chan] != 0) { | |
bcnt[chan]--; | |
assembly[chan] >>= 4; | |
} | |
assembly[chan] |= MSIGN; | |
break; | |
case 060: | |
if (bcnt[chan] > 5) | |
chan_info[chan] |= CHAN_TWE; | |
assembly[chan] &= DMASK; | |
/* Put number in right location */ | |
while(bcnt[chan] != 0) { | |
bcnt[chan]--; | |
assembly[chan] >>= 4; | |
} | |
assembly[chan] |= PSIGN; | |
break; | |
} | |
} else { | |
if (chan_info[chan] & CHAN_OCTAL) | |
ch = ((ch & 070) << 1) | (ch & 07); | |
else | |
ch = bcd_mem[ch]; | |
if (ch == 0xFF) { | |
chan_info[chan] |= CHAN_TWE; | |
ch = 0; | |
} | |
bcnt[chan] -= 2; | |
assembly[chan] |= ((t_uint64)ch) << (8 * (bcnt[chan] / 2)); | |
assembly[chan] |= (chan_info[chan] & CHAN_OCTAL)?PSIGN:ASIGN; | |
} | |
} | |
if (flags & DEV_REOR) { | |
chan_flags[chan] |= DEV_FULL|DEV_REOR; | |
chan_flags[chan] &= ~DEV_WRITE; | |
if (bcnt[chan] != 0 && ((cmd[chan] & (CHN_NUM_MODE)) == 0 || | |
(cmd[chan] & (CHN_ALPHA)) != 0)) | |
chan_info[chan] |= CHAN_SCLR; | |
chan_info[chan] |= CHAN_SEOR; | |
chan_proc(); | |
return END_RECORD; | |
} else if (bcnt[chan] == 0) { | |
chan_flags[chan] |= DEV_FULL; | |
chan_flags[chan] &= ~DEV_WRITE; | |
chan_proc(); | |
} | |
/* If Writing end of record, abort */ | |
if (flags & DEV_WEOR) { | |
chan_flags[chan] &= ~(DEV_FULL | DEV_WEOR); | |
return END_RECORD; | |
} | |
return DATA_OK; | |
} | |
/* | |
* Read next char from assembly register. | |
*/ | |
int | |
chan_read_char(int chan, uint8 * data, int flags) | |
{ | |
uint8 ch; | |
/* Return END_RECORD if requested */ | |
if (flags & DEV_WEOR) { | |
chan_flags[chan] &= ~(DEV_WEOR /*| STA_WAIT*/); | |
return END_RECORD; | |
} | |
chan_proc(); | |
/* Check if he write out last data */ | |
if ((chan_flags[chan] & DEV_FULL) == 0) { | |
if (chan_flags[chan] & DEV_WEOR) { | |
chan_flags[chan] &= ~(DEV_WEOR | STA_WAIT | DEV_WRITE); | |
chan_flags[chan] |= DEV_REOR|DEV_DISCO; | |
return END_RECORD; | |
} | |
if (chan_flags[chan] & STA_ACTIVE) { | |
chan_flags[chan] |= CHS_ATTN; | |
} | |
if (chan == 0) { | |
chan_flags[chan] |= DEV_DISCO; | |
} | |
return TIME_ERROR; | |
} | |
/* Send control words differently */ | |
if (chan_flags[chan] & CTL_CNTL) { | |
if ((assembly[chan] & SMASK) == ASIGN) { | |
bcnt[chan] -= 2; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; | |
*data = mem_bcd[ch]; | |
} else { | |
bcnt[chan]--; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; | |
if (ch == 0) | |
ch = 10; | |
*data = ch; | |
} | |
goto done; | |
} | |
/* Handle console type out */ | |
if (chan_info[chan] & CHAN_OUTDEV) { | |
if (bcnt[chan] == 10 && (cmd[chan] & CHN_NUM_MODE) == 0) { | |
switch(assembly[chan] & SMASK) { | |
case ASIGN: break; | |
case PSIGN: *data = 060; | |
cmd[chan] |= CHN_NUM_MODE; | |
return SCPE_OK; | |
case MSIGN: *data = 040; | |
cmd[chan] |= CHN_NUM_MODE; | |
return SCPE_OK; | |
} | |
} | |
if (cmd[chan] & CHN_NUM_MODE) { | |
bcnt[chan]--; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; | |
if (ch == 0) | |
ch = 10; | |
if (bcnt[chan] == 0) | |
cmd[chan] &= ~CHN_NUM_MODE; | |
*data = ch; | |
} else { | |
bcnt[chan] -= 2; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; | |
*data = mem_bcd[ch]; | |
} | |
goto done; | |
} | |
/* Check for mode change */ | |
if (bcnt[chan] == 10 && (cmd[chan] & (CHN_ALPHA)) == 0) { | |
if (((assembly[chan] & SMASK) == ASIGN && | |
(cmd[chan] & CHN_NUM_MODE) == CHN_NUM_MODE) | |
||((assembly[chan] & SMASK) != ASIGN && | |
(cmd[chan] & CHN_NUM_MODE) == CHN_ALPHA_MODE)) { | |
*data = DELTA_CHAR; | |
cmd[chan] ^= CHN_NUM_MODE; | |
return DATA_OK; | |
} | |
/* Handle zero compression here */ | |
if ((cmd[chan] & (CHN_NUM_MODE|CHN_COMPRESS)) == | |
(CHN_NUM_MODE|CHN_COMPRESS)) { | |
while((assembly[chan] >> (4 * bcnt[chan]) & 0xf) == 0 && | |
bcnt[chan] < 5) | |
bcnt[chan]--; | |
} | |
} | |
/* If in number mode, dump as number */ | |
if (cmd[chan] & CHN_NUM_MODE) { | |
bcnt[chan]--; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xf; | |
if (ch == 0) | |
ch = 10; | |
if (bcnt[chan] == 0) | |
ch |= ((assembly[chan] & SMASK) == MSIGN)? 040: 060; | |
*data = ch; | |
} else { | |
bcnt[chan] -= 2; | |
ch = (assembly[chan] >> (4 * bcnt[chan])) & 0xff; | |
*data = mem_bcd[ch]; | |
} | |
done: | |
if (bcnt[chan] == 0) { | |
chan_flags[chan] &= ~DEV_FULL; | |
bcnt[chan] = 10; | |
} | |
/* If end of record, don't transfer any data */ | |
if (flags & DEV_REOR) { | |
chan_flags[chan] &= ~(DEV_WRITE|DEV_FULL); | |
chan_flags[chan] |= DEV_REOR; | |
chan_proc(); | |
} else | |
chan_flags[chan] |= DEV_WRITE; | |
return DATA_OK; | |
} | |
void | |
chan_set_load_mode(int chan) | |
{ | |
cmd[chan] &= ~CHN_ALPHA; | |
cmd[chan] |= CHN_NUM_MODE; | |
} | |
void | |
chan9_set_error(int chan, uint32 mask) | |
{ | |
if (chan_flags[chan] & mask) | |
return; | |
chan_flags[chan] |= mask; | |
} | |
t_stat | |
chan_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "%s\n\n", chan_description(dptr)); | |
fprintf (st, "The 7070 supports up to 8 channels. Channel models include\n\n"); | |
fprintf (st, " 7604 standard multiplexor channel\n"); | |
fprintf (st, " 7907 advanced capabilities channel\n\n"); | |
fprintf (st, "Channels are fixed on the 7070.\n\n"); | |
fprintf (st, "Channel * is for unit record devices.\n"); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
chan_description(DEVICE *dptr) | |
{ | |
return "IBM 7070 channel controller"; | |
} | |