1900 lines
71 KiB
C
1900 lines
71 KiB
C
/* i7094_io.c: IBM 7094 I/O subsystem (channels)
|
|
|
|
Copyright (c) 2003-2012, Robert M. Supnik
|
|
|
|
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.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
chana..chanh I/O channels
|
|
|
|
19-Mar-12 RMS Fixed declaration of breakpoint variables (Mark Pizzolato)
|
|
|
|
Notes on channels and CTSS.
|
|
|
|
- CTSS B-core is supported by the addition of a 16th bit to the current
|
|
address field of the channel command. Both the channel location counter
|
|
and the channel current address register are widened to 16b. Thus,
|
|
channel programs can run in B-core, and channel transfers can access B-core.
|
|
CTSS assumes that a channel command which starts a transfer in B-core
|
|
will not access A-core; the 16th bit does not increment.
|
|
- The channel start commands (RCHx and LCHx) incorporate the A-core/B-core
|
|
select as part of effective address generation. CTSS does not relocate
|
|
RCHx and LCHx target addresses; because the relocation indicator is
|
|
always zero, it's impossible to tell whether the protection indicator
|
|
affects address generation.
|
|
- The CTSS protection RPQ does not cover channel operations. Thus, CTSS
|
|
must inspect and vet all channel programs initiated by user mode programs,
|
|
notably the background processor FMS. CTSS inspects in-progress 7607
|
|
channel programs to make sure than either the nostore bit or the B-core
|
|
bit is set; thus, SCHx must store all 16b of the current address.
|
|
*/
|
|
|
|
#include "i7094_defs.h"
|
|
|
|
#define CHAMASK ((cpu_model & I_CT)? PAMASK: AMASK) /* chan addr mask */
|
|
#define CHAINC(x) (((x) & ~AMASK) | (((x) + 1) & AMASK))
|
|
|
|
typedef struct {
|
|
char *name;
|
|
uint32 flags;
|
|
} DEV_CHAR;
|
|
|
|
uint32 ch_sta[NUM_CHAN]; /* channel state */
|
|
uint32 ch_dso[NUM_CHAN]; /* data select op */
|
|
uint32 ch_dsu[NUM_CHAN]; /* data select unit */
|
|
uint32 ch_ndso[NUM_CHAN]; /* non-data select op */
|
|
uint32 ch_ndsu[NUM_CHAN]; /* non-data select unit */
|
|
uint32 ch_flags[NUM_CHAN]; /* flags */
|
|
uint32 ch_clc[NUM_CHAN]; /* chan loc ctr */
|
|
uint32 ch_op[NUM_CHAN]; /* channel op */
|
|
uint32 ch_wc[NUM_CHAN]; /* word count */
|
|
uint32 ch_ca[NUM_CHAN]; /* core address */
|
|
uint32 ch_lcc[NUM_CHAN]; /* control cntr (7909) */
|
|
uint32 ch_cnd[NUM_CHAN]; /* cond reg (7909) */
|
|
uint32 ch_sms[NUM_CHAN]; /* cond mask reg (7909) */
|
|
t_uint64 ch_ar[NUM_CHAN]; /* assembly register */
|
|
uint32 ch_idf[NUM_CHAN]; /* channel input data flags */
|
|
DEVICE *ch2dev[NUM_CHAN] = { NULL };
|
|
uint32 ch_tpoll = 5; /* channel poll */
|
|
|
|
extern t_uint64 *M;
|
|
extern uint32 cpu_model, data_base;
|
|
extern uint32 hst_ch;
|
|
extern uint32 ch_req;
|
|
extern uint32 chtr_inht, chtr_inhi, chtr_enab;
|
|
extern uint32 ind_ioc;
|
|
extern uint32 chtr_clk;
|
|
extern DEVICE cdr_dev, cdp_dev;
|
|
extern DEVICE lpt_dev;
|
|
extern DEVICE mt_dev[NUM_CHAN];
|
|
extern DEVICE drm_dev;
|
|
extern DEVICE dsk_dev;
|
|
extern DEVICE com_dev;
|
|
extern uint32 sim_brk_summ;
|
|
|
|
t_stat ch_reset (DEVICE *dptr);
|
|
t_stat ch6_svc (UNIT *uptr);
|
|
t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc);
|
|
t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc);
|
|
DEVICE *ch_find_dev (uint32 ch, uint32 unit);
|
|
t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta);
|
|
t_bool ch6_rd_putw (uint32 ch);
|
|
t_stat ch6_wr_getw (uint32 ch, t_bool eorz);
|
|
t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld);
|
|
t_stat ch6_ioxt (uint32 ch);
|
|
void ch6_iosp_cclr (uint32 ch);
|
|
t_stat ch9_new_cmd (uint32 ch);
|
|
t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir);
|
|
t_stat ch9_sel (uint32 ch, uint32 sel);
|
|
t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl);
|
|
t_stat ch9_rd_putw (uint32 ch);
|
|
t_stat ch9_wr_getw (uint32 ch);
|
|
void ch9_eval_int (uint32 ch, uint32 iflags);
|
|
DEVICE *ch_map_flags (uint32 ch, int32 fl);
|
|
|
|
extern CTAB *sim_vm_cmd;
|
|
extern t_stat ch_bkpt (uint32 ch, uint32 clc);
|
|
|
|
const uint32 col_masks[12] = { /* row 9,8,..,0,11,12 */
|
|
00001, 00002, 00004,
|
|
00010, 00020, 00040,
|
|
00100, 00200, 00400,
|
|
01000, 02000, 04000
|
|
};
|
|
|
|
const t_uint64 bit_masks[36] = {
|
|
0000000000001, 0000000000002, 0000000000004,
|
|
0000000000010, 0000000000020, 0000000000040,
|
|
0000000000100, 0000000000200, 0000000000400,
|
|
0000000001000, 0000000002000, 0000000004000,
|
|
0000000010000, 0000000020000, 0000000040000,
|
|
0000000100000, 0000000200000, 0000000400000,
|
|
0000001000000, 0000002000000, 0000004000000,
|
|
0000010000000, 0000020000000, 0000040000000,
|
|
0000100000000, 0000200000000, 0000400000000,
|
|
0001000000000, 0002000000000, 0004000000000,
|
|
0010000000000, 0020000000000, 0040000000000,
|
|
0100000000000, 0200000000000, 0400000000000
|
|
};
|
|
|
|
const DEV_CHAR dev_table[] = {
|
|
{ "729", 0 },
|
|
{ "TAPE", 0 },
|
|
{ "7289", DEV_7289 },
|
|
{ "DRUM", DEV_7289 },
|
|
{ "7631", DEV_7909|DEV_7631 },
|
|
{ "FILE", DEV_7909|DEV_7631 },
|
|
{ "7750", DEV_7909|DEV_7750 },
|
|
{ "COMM", DEV_7909|DEV_7750 },
|
|
{ NULL },
|
|
};
|
|
|
|
const char *sel_name[] = {
|
|
"UNK", "RDS", "WRS", "SNS", "CTL", "FMT", "UNK", "UNK",
|
|
"WEF", "WBT", "BSR", "BSF", "REW", "RUN", "SDN", "UNK"
|
|
};
|
|
|
|
/* Channel data structures */
|
|
|
|
UNIT ch_unit[NUM_CHAN] = {
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) },
|
|
{ UDATA (&ch6_svc, 0, 0) }
|
|
};
|
|
|
|
MTAB ch_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL,
|
|
NULL, &ch_show_type, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "ENABLED",
|
|
&ch_set_enable, NULL, NULL },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "DISABLED",
|
|
&ch_set_disable, NULL, NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
REG cha_reg[] = {
|
|
{ ORDATA (STA, ch_sta[CH_A], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_A], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_A], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_A], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_A], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_A], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_A], 2) },
|
|
{ ORDATA (OP, ch_op[CH_A], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_A], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_A], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_A], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_A], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_A], 6), REG_HRO },
|
|
{ ORDATA (LCC, ch_lcc[CH_A], 6), REG_HRO },
|
|
{ ORDATA (SMS, ch_sms[CH_A], 7), REG_HRO },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chb_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_B], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_B], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_B], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_B], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_B], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_B], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_B], 2) },
|
|
{ ORDATA (OP, ch_op[CH_B], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_B], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_B], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_B], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_B], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_B], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_B], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_B], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chc_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_C], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_C], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_C], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_C], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_C], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_C], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_C], 2) },
|
|
{ ORDATA (OP, ch_op[CH_C], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_C], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_C], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_C], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_C], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_C], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_C], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_C], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chd_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_D], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_D], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_D], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_D], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_D], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_D], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_D], 2) },
|
|
{ ORDATA (OP, ch_op[CH_D], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_D], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_D], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_D], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_D], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_D], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_D], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_D], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG che_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_E], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_E], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_E], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_E], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_E], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_E], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_E], 2) },
|
|
{ ORDATA (OP, ch_op[CH_E], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_E], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_E], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_E], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_E], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_E], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_E], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_E], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chf_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_F], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_F], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_F], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_F], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_F], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_F], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_F], 2) },
|
|
{ ORDATA (OP, ch_op[CH_F], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_F], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_F], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_F], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_F], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_F], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_F], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_F], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chg_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_G], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_G], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_G], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_G], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_G], 9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_G], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_G], 2) },
|
|
{ ORDATA (OP, ch_op[CH_G], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_G], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_G], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_G], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_G], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_G], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_G], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_G], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
REG chh_reg[] = {
|
|
{ ORDATA (STATE, ch_sta[CH_H], 8) },
|
|
{ ORDATA (DSC, ch_dso[CH_H], 4) },
|
|
{ ORDATA (DSU, ch_dsu[CH_H], 9) },
|
|
{ ORDATA (NDSC, ch_ndso[CH_H], 4) },
|
|
{ ORDATA (NDSU, ch_ndsu[CH_H],9) },
|
|
{ ORDATA (FLAGS, ch_flags[CH_H], 30) },
|
|
{ ORDATA (IDF, ch_idf[CH_H], 2) },
|
|
{ ORDATA (OP, ch_op[CH_H], 5) },
|
|
{ ORDATA (CLC, ch_clc[CH_H], 16) },
|
|
{ ORDATA (WC, ch_wc[CH_H], 15) },
|
|
{ ORDATA (CA, ch_ca[CH_H], 16) },
|
|
{ ORDATA (AR, ch_ar[CH_H], 36) },
|
|
{ ORDATA (CND, ch_cnd[CH_H], 6) },
|
|
{ ORDATA (LCC, ch_lcc[CH_H], 6) },
|
|
{ ORDATA (SMS, ch_sms[CH_H], 7) },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE ch_dev[NUM_CHAN] = {
|
|
{
|
|
"CHANA", &ch_unit[CH_A], cha_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, 0
|
|
},
|
|
{
|
|
"CHANB", &ch_unit[CH_B], chb_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHANC", &ch_unit[CH_C], chc_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHAND", &ch_unit[CH_D], chd_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHANE", &ch_unit[CH_E], che_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHANF", &ch_unit[CH_F], chf_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHANG", &ch_unit[CH_G], chg_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
},
|
|
{
|
|
"CHANH", &ch_unit[CH_H], chh_reg, ch_mod,
|
|
1, 8, 8, 1, 8, 8,
|
|
NULL, NULL, &ch_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
}
|
|
};
|
|
|
|
/* 7607 channel overview
|
|
|
|
Channel variables:
|
|
|
|
ch_sta channel state
|
|
ch_dso, ch_dsu operation and unit for current data select
|
|
ch_ndso, ch_ndsu operation and unit for current non-data select
|
|
ch_clc current location counter
|
|
ch_ca memory addres
|
|
ch_wc word count
|
|
ch_op channel opcode (bits <S,1:2,19>)
|
|
ch_flags channel flags
|
|
|
|
States of a channel
|
|
|
|
IDLE - channel is not in operation
|
|
|
|
RDS, WDS: -> DSW if device is idle, schedule device
|
|
device timeout drives next transition
|
|
-> stall if device is busy
|
|
repeat until device is idle
|
|
other I/O: -> NDS if device is idle, schedule device
|
|
device timeout drives next transition
|
|
-> stall if device is busy
|
|
repeat until device is idle
|
|
chan reset: -> IDLE
|
|
|
|
PDS (PNDS) - channel is polling device to start data (non-data) select
|
|
|
|
chan timeout: -> DSW (NDS) if device is idle
|
|
device timeout drives next transition
|
|
-> no change if device is busy, schedule channel
|
|
chan reset: -> IDLE
|
|
|
|
DSW - channel is waiting for channel start command
|
|
|
|
dev timeout: -> IDLE if no stacked non-data select
|
|
-> PNDS if stacked non-data select
|
|
channel timeout drives next transition
|
|
start chan: -> DSX if chan program transfers data
|
|
device timeout drives next transition
|
|
-> IDLE if channel disconnects, no stacked NDS
|
|
-> PNDS if channel disconnects, stacked NDS
|
|
channel timeout drives next transition
|
|
chan reset: -> IDLE
|
|
|
|
DSX - channel is executing data select
|
|
|
|
dev timeout: -> DSX if transfer not complete, reschedule device
|
|
device timeout drives next transition
|
|
-> DSW if channel command completes, CHF_LDW set
|
|
-> IDLE if transfer complete, no stacked NDS, or
|
|
if channel command completes, CHF_LDW clear
|
|
-> PNDS if channel disconnects, stacked NDS
|
|
channel timeout drives next transition
|
|
start chan: -> DSX with CHF_LDW, CPU stall
|
|
chan reset: -> IDLE
|
|
|
|
NDS - channel is executing non-data select
|
|
|
|
dev timeout: -> IDLE if transfer complete, no stacked DS
|
|
-> PDS if channel disconnects, stacked DS
|
|
channel timeout drives next transition
|
|
chan reset: -> IDLE
|
|
|
|
The channel has two interfaces to a device. The select routine:
|
|
|
|
dev_select (uint32 ch, uint32 sel, uint32 unit)
|
|
|
|
Returns can include device errors and ERR_STALL. If ERR_STALL, the
|
|
device is busy. For I/O instructions, ERR_STALL stalls execution of
|
|
the instruction until the device is not busy. For stacked command
|
|
polls, ERR_STALL causes the poll to be repeated after a delay.
|
|
|
|
The device write routine is used to place output data in the device
|
|
write buffer.
|
|
|
|
Channel transfers are driven by the channel. When a device needs to
|
|
read or write data, it sets a channel request in ch_req. The channel
|
|
process transfers the data and updates channel control parameters
|
|
accordingly. Note that the channel may disconnect; in this case, the
|
|
transfer completes 'correctly' from the point of view of the device.
|
|
|
|
The channel transfer commands (IOxT) require the channel to 'hold'
|
|
a new channel command in anticipation of the current transfer. If
|
|
the channel is currently executing (CH6S_DSX) and a channel start
|
|
is issued by the CPU, a 'start pending' flag is set and the CPU is
|
|
stalled. When the channel reaches the end of an IOxT command, it
|
|
checks the 'start pending' flag. If the flag is set, the channel
|
|
sets itself to waiting and then requeues itself for one cycle later.
|
|
The CPU tries the channel start, sees that the channel is waiting,
|
|
and issues the new channel command.
|
|
|
|
state op device channel
|
|
|
|
IDLE RDS,WDS start I/O ->DSW
|
|
|
|
DSW LCHx (timed wait) ->DSX
|
|
|
|
DSX -- timeout, req svc
|
|
(timed wait) transfer word
|
|
timeout, req svc
|
|
(timed wait)
|
|
LCHx, stalls :
|
|
timeout, EOR/EOC IOxT: ->DSW, resched
|
|
DSW LCHx (timed wait) ->DSX, etc
|
|
|
|
7909 channel overview
|
|
|
|
Channel variables:
|
|
|
|
ch_sta channel state
|
|
ch_clc current location counter
|
|
ch_ca memory addres
|
|
ch_wc word count
|
|
ch_op channel opcode (bits <S,1:3,19>)
|
|
ch_sms status mask
|
|
ch_cond interrupt conditions
|
|
ch_lcc control counter
|
|
ch_flags channel flags
|
|
|
|
States of a channel
|
|
|
|
IDLE - channel is not in operation
|
|
|
|
RDCx, SDCx, interrupt -> DSX
|
|
|
|
DSX - channel is executing data select
|
|
|
|
TWT, WTR -> IDLE
|
|
|
|
The 7909 is more capable than the 7607 but also simpler in some ways.
|
|
It has many more instructions, built in counters and status checking,
|
|
and interrupts. But it has only two states and no concept of records.
|
|
|
|
The 7909 read process is driven by the device:
|
|
|
|
channel CTLR/SNS: send select
|
|
device: schedule timeout
|
|
device timeout: device to AR, request channel
|
|
channel: AR to memory
|
|
device timeout: device to AR, request channel
|
|
channel: AR to memory
|
|
:
|
|
device timeout: set end, request channel
|
|
channel: disconnect on CPYD, send STOP
|
|
|
|
The 7909 write process is also driven by the device:
|
|
|
|
channel CTL/CTLW: send select
|
|
device: schedule timeout, request channel
|
|
channel: memory to output buffer
|
|
device timeout: output buffer to device, request channel
|
|
channel: memory to output buffer
|
|
device timeout: output buffer to device, request channel
|
|
:
|
|
channel: memory to output buffer
|
|
device timeout: output buffer to device, set end, request channel
|
|
channel: disconnect on CPYD, send STOP
|
|
|
|
For both reads and writes, devices must implement an 'interblock' or
|
|
'interrecord' state that is long enough for the channel to see the
|
|
end, disconnect, and send a stop signal.
|
|
*/
|
|
|
|
/* Data select - called by RDS or WDS instructions - 7607/7289 only
|
|
|
|
- Channel is from address and has been corrected
|
|
- Channel must be an enabled 7607
|
|
- If data select already in use, stall CPU
|
|
- If non-data select is a write end-of-file, stall CPU
|
|
- If channel is busy, stack command
|
|
- Otherwise, start IO, set channel to waiting */
|
|
|
|
t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit)
|
|
{
|
|
t_stat r;
|
|
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_7909) /* 7909? stop */
|
|
return STOP_7909;
|
|
if (ch_dso[ch]) /* DS in use? */
|
|
return ERR_STALL;
|
|
if (ch_ndso[ch] == CHSL_WEF) /* NDS = WEF? */
|
|
return ERR_STALL;
|
|
if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */
|
|
r = ch6_sel (ch, ds, unit, CH6S_DSW); /* select device */
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
}
|
|
ch_dso[ch] = ds; /* set command, unit */
|
|
ch_dsu[ch] = unit;
|
|
ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_CMD); /* clear flags */
|
|
ch_idf[ch] = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Non-data select - called by BSR, BSF, WEF, REW, RUN, SDS instructions - 7607 only
|
|
|
|
- Channel is from address and has been corrected
|
|
- Channel must be an enabled 7607
|
|
- If non-data select already in use, stall CPU
|
|
- If data select is card or printer, stall CPU
|
|
- If channel is busy, stack command
|
|
- Otherwise, start IO, set channel to waiting */
|
|
|
|
t_stat ch_op_nds (uint32 ch, uint32 nds, uint32 unit)
|
|
{
|
|
DEVICE *dptr;
|
|
t_stat r;
|
|
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_7909) /* 7909? stop */
|
|
return STOP_7909;
|
|
if (ch_ndso[ch]) /* NDS in use? */
|
|
return ERR_STALL;
|
|
if (ch_dso[ch] && (dptr = ch_find_dev (ch, ch_dsu[ch])) /* DS, cd or lpt? */
|
|
&& (dptr->flags & DEV_CDLP))
|
|
return ERR_STALL;
|
|
if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */
|
|
r = ch6_sel (ch, nds, unit, CH6S_NDS); /* select device */
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
}
|
|
ch_ndso[ch] = nds; /* set command, unit */
|
|
ch_ndsu[ch] = unit;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* End of data select - called from channel - 7607/7289 only
|
|
|
|
- If executing, set command trap flag
|
|
- Set channel idle
|
|
- If stacked nds, set up immediate channel timeout */
|
|
|
|
t_stat ch6_end_ds (uint32 ch)
|
|
{
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return STOP_NXCHN;
|
|
ch_dso[ch] = ch_dsu[ch] = 0; /* no data select */
|
|
if (ch_ndso[ch]) { /* stacked non-data sel? */
|
|
sim_activate (ch_dev[ch].units, 0); /* immediate poll */
|
|
ch_sta[ch] = CH6S_PNDS; /* state = polling */
|
|
}
|
|
else ch_sta[ch] = CHXS_IDLE; /* else state = idle */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* End of non-data select - called from I/O device completion - 7607/7289 only
|
|
|
|
- Set channel idle
|
|
- If stacked ds, set up immediate channel timeout */
|
|
|
|
t_stat ch6_end_nds (uint32 ch)
|
|
{
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return STOP_NXCHN;
|
|
ch_ndso[ch] = ch_ndsu[ch] = 0; /* no non-data select */
|
|
if (ch_dso[ch]) { /* stacked data sel? */
|
|
sim_activate (ch_dev[ch].units, 0); /* immediate poll */
|
|
ch_sta[ch] = CH6S_PDS; /* state = polling */
|
|
}
|
|
else ch_sta[ch] = CHXS_IDLE; /* else state = idle */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Send select to device - 7607/7289 only */
|
|
|
|
t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta)
|
|
{
|
|
DEVICE *dptr;
|
|
DIB *dibp;
|
|
t_stat r;
|
|
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return STOP_NXCHN;
|
|
dptr = ch_find_dev (ch, unit); /* find device */
|
|
if (dptr == NULL) /* invalid device? */
|
|
return STOP_NXDEV;
|
|
dibp = (DIB *) dptr->ctxt;
|
|
r = dibp->chsel (ch, sel, unit); /* select device */
|
|
if (r == SCPE_OK) /* set status */
|
|
ch_sta[ch] = sta;
|
|
return r;
|
|
}
|
|
|
|
/* Channel unit service - called to start stacked command - 7607 only */
|
|
|
|
t_stat ch6_svc (UNIT *uptr)
|
|
{
|
|
uint32 ch = uptr - &ch_unit[0]; /* get channel */
|
|
t_stat r;
|
|
|
|
if (ch >= NUM_CHAN) /* invalid chan? */
|
|
return SCPE_IERR;
|
|
switch (ch_sta[ch]) { /* case on state */
|
|
|
|
case CH6S_PDS: /* polling for ds */
|
|
r = ch6_sel (ch, ch_dso[ch], ch_dsu[ch], CH6S_DSW);
|
|
break;
|
|
|
|
case CH6S_PNDS: /* polling for nds */
|
|
r = ch6_sel (ch, ch_ndso[ch], ch_ndsu[ch], CH6S_NDS);
|
|
break;
|
|
|
|
default:
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (r == ERR_STALL) { /* stalled? */
|
|
sim_activate (uptr, ch_tpoll); /* continue poll */
|
|
return SCPE_OK;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/* Map channel and unit number to device - all channels */
|
|
|
|
DEVICE *ch_find_dev (uint32 ch, uint32 unit)
|
|
{
|
|
if (ch >= NUM_CHAN) /* invalid arg? */
|
|
return NULL;
|
|
if (ch_dev[ch].flags & (DEV_7909|DEV_7289))
|
|
return ch2dev[ch];
|
|
unit = unit & 0777;
|
|
if (((unit >= U_MTBCD) && (unit <= (U_MTBCD + MT_NUMDR))) ||
|
|
((unit >= U_MTBIN) && (unit <= (U_MTBIN + MT_NUMDR))))
|
|
return ch2dev[ch];
|
|
if (ch != 0)
|
|
return NULL;
|
|
if (unit == U_CDR)
|
|
return &cdr_dev;
|
|
if (unit == U_CDP)
|
|
return &cdp_dev;
|
|
if ((unit == U_LPBCD) || (unit == U_LPBIN))
|
|
return &lpt_dev;
|
|
return NULL;
|
|
}
|
|
|
|
/* Start channel - channel is from opcode
|
|
|
|
7607: channel should have a data select operation pending (DSW state)
|
|
7909: channel should be idle (IDLE state) */
|
|
|
|
t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset)
|
|
{
|
|
t_uint64 ir;
|
|
t_stat r;
|
|
|
|
clc = clc | data_base; /* add A/B select */
|
|
if (ch >= NUM_CHAN) /* invalid argument? */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_DIS) /* disabled? stop */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_7909) { /* 7909? */
|
|
if (ch_sta[ch] != CHXS_IDLE) /* must be idle */
|
|
return ERR_STALL;
|
|
if (reset) { /* RDCx? */
|
|
ch_cnd[ch] = 0; /* clear conditions */
|
|
ch_clc[ch] = clc; /* set clc */
|
|
}
|
|
else { /* SDCx */
|
|
if (BIT_TST (chtr_enab, CHTR_V_TWT + ch) && /* pending trap? */
|
|
(ch_flags[ch] & CHF_TWT))
|
|
return ERR_STALL;
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* finish WTR, TWT */
|
|
}
|
|
ch_flags[ch] &= ~CHF_CLR_7909; /* clear flags, not IP */
|
|
ch_idf[ch] = 0;
|
|
ch_sta[ch] = CHXS_DSX; /* set state */
|
|
return ch9_new_cmd (ch); /* start executing */
|
|
}
|
|
/* 7607, 7289 */
|
|
if (reset) { /* reset? */
|
|
if (ch_sta[ch] == CHXS_DSX)
|
|
ch_sta[ch] = CH6S_DSW;
|
|
ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_TRC|CHF_CMD);
|
|
ch_idf[ch] = 0;
|
|
}
|
|
|
|
switch (ch_sta[ch]) { /* case on chan state */
|
|
|
|
case CHXS_IDLE: /* idle */
|
|
ind_ioc = 1; /* IO check */
|
|
ir = ReadP (clc); /* get chan word */
|
|
ch_clc[ch] = CHAINC (clc); /* incr chan pc */
|
|
ch_wc[ch] = GET_DEC (ir); /* get word cnt */
|
|
ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */
|
|
ch_op[ch] = (GET_OPD (ir) << 1) | /* get opcode */
|
|
((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */
|
|
break;
|
|
|
|
case CH6S_PNDS: /* NDS polling */
|
|
case CH6S_PDS: /* DS polling */
|
|
case CH6S_NDS: /* NDS executing */
|
|
return ERR_STALL; /* wait it out */
|
|
|
|
case CH6S_DSW: /* expecting command */
|
|
ch_sta[ch] = CHXS_DSX; /* update state */
|
|
if (ch_dev[ch].flags & DEV_7289) { /* drum channel? */
|
|
ir = ReadP (clc); /* read addr */
|
|
ch_clc[ch] = CHAINC (clc); /* incr chan pc */
|
|
if ((r = ch9_wr (ch, ir, 0))) /* write to dev */
|
|
return r;
|
|
}
|
|
else ch_clc[ch] = clc; /* set clc */
|
|
return ch6_new_cmd (ch, TRUE); /* start channel */
|
|
|
|
case CHXS_DSX: /* executing */
|
|
ch_flags[ch] = ch_flags[ch] | CHF_LDW; /* flag pending LCH */
|
|
return ERR_STALL; /* stall */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Store channel
|
|
|
|
7607/7289 stores op,ca,nostore,clc
|
|
7909 stores clc,,ca */
|
|
|
|
t_stat ch_op_store (uint32 ch, t_uint64 *dat)
|
|
{
|
|
if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS))
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_7909)
|
|
*dat = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
|
|
(((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_ADDR);
|
|
else *dat = (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_DEC) |
|
|
(((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_ADDR) |
|
|
(((t_uint64) (ch_op[ch] & 1)) << 16) |
|
|
(((t_uint64) (ch_op[ch] & 016)) << 32);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Store channel diagnostic
|
|
|
|
7607 is undefined
|
|
7289 stores IOC+???
|
|
7909 stores 7909 lcc+flags */
|
|
|
|
t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat)
|
|
{
|
|
extern t_uint64 drm_sdc (uint32 ch);
|
|
|
|
if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS))
|
|
return STOP_NXCHN;
|
|
if (ch_flags[ch] & DEV_7289)
|
|
*dat = drm_sdc (ch);
|
|
else if (ch_flags[ch] & DEV_7909)
|
|
*dat = (((t_uint64) (ch_lcc[ch] & CHF_M_LCC)) << CHF_V_LCC) |
|
|
(ch_flags[ch] & CHF_SDC_7909);
|
|
else *dat = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset data channel
|
|
|
|
7607 responds to RDC
|
|
7909 responds to RIC */
|
|
|
|
t_stat ch_op_reset (uint32 ch, t_bool ch7909)
|
|
{
|
|
DEVICE *dptr;
|
|
|
|
if (ch >= NUM_CHAN) /* invalid argument? */
|
|
return STOP_NXCHN;
|
|
if (ch_dev[ch].flags & DEV_DIS) /* disabled? ok */
|
|
return SCPE_OK;
|
|
if (ch_dev[ch].flags & DEV_7909) { /* 7909? */
|
|
if (!ch7909) /* wrong reset is NOP */
|
|
return SCPE_OK;
|
|
dptr = ch2dev[ch]; /* get device */
|
|
}
|
|
else { /* 7607, 7289 */
|
|
if (ch7909) /* wrong reset is err */
|
|
return STOP_NT7909;
|
|
dptr = ch_find_dev (ch, ch_ndsu[ch]); /* find device */
|
|
}
|
|
ch_reset (&ch_dev[ch]); /* reset channel */
|
|
if (dptr && dptr->reset) /* reset device */
|
|
dptr->reset (dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Channel process - called from main CPU loop. If the channel is unable
|
|
to get a valid command, it will reschedule itself for the next cycle.
|
|
|
|
The read process is basically synchronous with the device timeout routine.
|
|
The device requests the channel and supplies the word to be stored in memory.
|
|
In the next time slot, the channel stores the word in memory. */
|
|
|
|
t_stat ch_proc (uint32 ch)
|
|
{
|
|
t_stat r;
|
|
|
|
if (ch >= NUM_CHAN) /* bad channel? */
|
|
return SCPE_IERR;
|
|
ch_req &= ~REQ_CH (ch); /* clear request */
|
|
if (ch_dev[ch].flags & DEV_DIS) /* disabled? */
|
|
return SCPE_IERR;
|
|
if (ch_dev[ch].flags & DEV_7909) { /* 7909 */
|
|
|
|
t_uint64 sr;
|
|
uint32 csel, sc, tval, mask, ta;
|
|
t_bool xfr;
|
|
|
|
if (ch_flags[ch] & CHF_IRQ) { /* interrupt? */
|
|
ta = CHINT_CHA_SAV + (ch << 1); /* save location */
|
|
if (ch_sta[ch] == CHXS_IDLE) /* waiting? */
|
|
sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
|
|
((t_uint64) ch_clc[ch] & CHAMASK); /* save CLC */
|
|
else sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) |
|
|
((t_uint64) CHAINC (ch_clc[ch])); /* no, save CLC+1 */
|
|
ch_sta[ch] = CHXS_DSX; /* set running */
|
|
ch_flags[ch] = (ch_flags[ch] | CHF_INT) & /* set intr state */
|
|
~(CHF_IRQ|CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); /* clr flags */
|
|
WriteP (ta, sr); /* write ca,,clc */
|
|
sr = ReadP (ta + 1); /* get chan cmd */
|
|
return ch9_exec_cmd (ch, sr); /* exec cmd */
|
|
}
|
|
|
|
switch (ch_op[ch] & CH9_OPMASK) { /* switch on op */
|
|
|
|
case CH9_TWT: /* transfer of TWT */
|
|
case CH9_WTR: /* transfer of WTR */
|
|
case CH9_TCH: /* transfer */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */
|
|
break;
|
|
|
|
case CH9_TDC: /* decr & transfer */
|
|
if (ch_lcc[ch] != 0) { /* counter != 0? */
|
|
ch_lcc[ch]--; /* decr counter */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */
|
|
}
|
|
break;
|
|
|
|
case CH9_TCM: /* transfer on cond */
|
|
csel = CH9D_COND (ch_wc[ch]);
|
|
mask = CH9D_MASK (ch_wc[ch]);
|
|
if (csel == 7) /* C = 7? mask mbz */
|
|
xfr = (mask == 0);
|
|
else { /* C = 0..6 */
|
|
if (csel == 0) /* C = 0? test cond */
|
|
tval = ch_cnd[ch];
|
|
else tval = (uint32) (ch_ar[ch] >> (6 * (6 - csel))) & 077;
|
|
if (ch_wc[ch] & CH9D_B11)
|
|
xfr = ((tval & mask) == mask);
|
|
else xfr = (tval == mask);
|
|
}
|
|
if (xfr) /* change CLC */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK;
|
|
break;
|
|
|
|
case CH9_LIP: /* leave interrupt */
|
|
ta = CHINT_CHA_SAV + (ch << 1); /* save location */
|
|
ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */
|
|
ch_cnd[ch] = 0; /* clear channel cond */
|
|
ch_clc[ch] = (uint32) ReadP (ta) & CHAMASK;
|
|
break;
|
|
|
|
case CH9_LIPT: /* leave intr, transfer */
|
|
ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */
|
|
ch_cnd[ch] = 0; /* clear channel cond */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */
|
|
break;
|
|
|
|
case CH9_LAR: /* load assembly reg */
|
|
ch_ar[ch] = ReadP (ch_ca[ch]);
|
|
break;
|
|
|
|
case CH9_SAR: /* store assembly reg */
|
|
WriteP (ch_ca[ch], ch_ar[ch]);
|
|
break;
|
|
|
|
case CH9_SMS: /* load SMS reg */
|
|
ch_sms[ch] = CH9A_SMS (ch_ca[ch]); /* from eff addr */
|
|
if (!(ch_sms[ch] & CHSMS_IATN1) && /* atn inhbit off */
|
|
(ch_flags[ch] & CHF_ATN1)) /* and atn pending? */
|
|
ch9_eval_int (ch, 0); /* force int eval */
|
|
break;
|
|
|
|
case CH9_LCC: /* load control cntr */
|
|
ch_lcc[ch] = CH9A_LCC (ch_ca[ch]); /* from eff addr */
|
|
break;
|
|
|
|
case CH9_ICC: /* insert control cntr */
|
|
case CH9_ICCA:
|
|
csel = CH9D_COND (ch_wc[ch]); /* get C */
|
|
if (csel == 0) ch_ar[ch] = /* C = 0? read SMS */
|
|
(ch_ar[ch] & 0777777770000) | ((t_uint64) ch_sms[ch]);
|
|
else if (csel < 7) { /* else read cond cntr */
|
|
sc = 6 * (6 - csel);
|
|
ch_ar[ch] = (ch_ar[ch] & ~(((t_uint64) 077) << sc)) |
|
|
(((t_uint64) ch_lcc[ch]) << sc);
|
|
}
|
|
break;
|
|
|
|
case CH9_XMT: /* transmit */
|
|
if (ch_wc[ch] == 0)
|
|
break;
|
|
sr = ReadP (ch_clc[ch]); /* next word */
|
|
WriteP (ch_ca[ch], sr);
|
|
ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr pointers */
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]);
|
|
ch_wc[ch] = ch_wc[ch] - 1; /* decr count */
|
|
ch_req |= REQ_CH (ch); /* go again */
|
|
return SCPE_OK;
|
|
|
|
case CH9_SNS: /* sense */
|
|
if ((r = ch9_sel (ch, CHSL_SNS))) /* send sense to dev */
|
|
return r;
|
|
ch_flags[ch] |= CHF_PRD; /* prepare to read */
|
|
break; /* next command */
|
|
|
|
case CH9_CTL:
|
|
case CH9_CTLR:
|
|
case CH9_CTLW: /* control */
|
|
if (((ch_wc[ch] & CH9D_NST) == 0) && /* N = 0 and */
|
|
!(ch_flags[ch] & CHF_EOR)) { /* end not set? */
|
|
sr = ReadP (ch_ca[ch]);
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */
|
|
return ch9_wr (ch, sr, 0); /* write ctrl wd */
|
|
}
|
|
ch_flags[ch] &= ~CHF_EOR; /* clear end */
|
|
if (ch_op[ch] == CH9_CTLR) { /* CTLR? */
|
|
if ((r = ch9_sel (ch, CHSL_RDS))) /* send read sel */
|
|
return r;
|
|
ch_flags[ch] |= CHF_PRD; /* prep to read */
|
|
ch_idf[ch] = 0;
|
|
}
|
|
else if (ch_op[ch] == CH9_CTLW) { /* CTLW? */
|
|
if ((r = ch9_sel (ch, CHSL_WRS))) /* end write sel */
|
|
return r;
|
|
ch_flags[ch] |= CHF_PWR; /* prep to write */
|
|
}
|
|
break;
|
|
|
|
case CH9_CPYD: /* copy & disc */
|
|
if ((ch_wc[ch] == 0) || (ch_flags[ch] & CHF_EOR)) { /* wc == 0 or EOR? */
|
|
if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) {
|
|
ch_flags[ch] &= ~(CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS);
|
|
if ((r = ch9_wr (ch, 0, CH9DF_STOP))) /* send stop */
|
|
return r;
|
|
}
|
|
if (ch_flags[ch] & CHF_EOR) { /* EOR? */
|
|
ch_flags[ch] &= ~CHF_EOR; /* clear flag */
|
|
break; /* new command */
|
|
}
|
|
return SCPE_OK; /* wait for end */
|
|
}
|
|
if (ch_flags[ch] & CHF_RDS) /* read? */
|
|
return ch9_rd_putw (ch);
|
|
return ch9_wr_getw (ch); /* no, write */
|
|
|
|
case CH9_CPYP: /* anything to do? */
|
|
if (ch_wc[ch] == 0) /* (new, wc = 0) next */
|
|
break;
|
|
if (ch_flags[ch] & CHF_EOR) /* end? */
|
|
ch_flags[ch] &= ~CHF_EOR; /* ignore */
|
|
else if (ch_flags[ch] & CHF_RDS) /* read? */
|
|
ch9_rd_putw (ch);
|
|
else if ((r = ch9_wr_getw (ch))) /* no, write */
|
|
return r;
|
|
if (ch_wc[ch] == 0) /* done? get next */
|
|
break;
|
|
return SCPE_OK; /* more to do */
|
|
|
|
default:
|
|
return STOP_ILLIOP;
|
|
}
|
|
|
|
return ch9_new_cmd (ch); /* next command */
|
|
}
|
|
|
|
else if (ch_flags[ch] & CHF_RDS) { /* 7607 read? */
|
|
|
|
if (ch_sta[ch] != CHXS_DSX) /* chan exec? no, disc */
|
|
return ch6_end_ds (ch);
|
|
switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */
|
|
|
|
case CH6_TCH: /* transfer */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */
|
|
return ch6_new_cmd (ch, FALSE); /* unpack new cmd */
|
|
|
|
case CH6_IOCD: /* IOCD */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if (ch6_rd_putw (ch)) /* store; more? cont */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_end_ds (ch); /* no, disconnect */
|
|
|
|
case CH6_IOCP: /* IOCP */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if (ch6_rd_putw (ch)) /* store; more? cont */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_new_cmd (ch, FALSE); /* unpack new cmd */
|
|
|
|
case CH6_IOCT: /* IOCT */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if (ch6_rd_putw (ch)) /* store; more? cont */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_ioxt (ch); /* unstall or disc */
|
|
|
|
case CH6_IOSP: /* IOSP */
|
|
if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_new_cmd (ch, FALSE); /* get next cmd */
|
|
}
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR))
|
|
return SCPE_OK; /* yes, store; more? */
|
|
ch6_iosp_cclr (ch); /* cond clear eor */
|
|
}
|
|
return ch6_new_cmd (ch, FALSE); /* next cmd */
|
|
|
|
case CH6_IOST: /* IOST */
|
|
if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_ioxt (ch); /* get next cmd */
|
|
}
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR))
|
|
return SCPE_OK; /* yes, store; more? */
|
|
ch6_iosp_cclr (ch); /* cond clear eor */
|
|
}
|
|
return ch6_ioxt (ch); /* unstall or disc */
|
|
|
|
case CH6_IORP: /* IORP */
|
|
if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_new_cmd (ch, FALSE); /* get next cmd */
|
|
}
|
|
ch6_rd_putw (ch); /* store wd; ignore wc */
|
|
if (ch_flags[ch] & CHF_EOR) { /* EOR? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_new_cmd (ch, FALSE); /* get next cmd */
|
|
}
|
|
return SCPE_OK; /* done */
|
|
|
|
case CH6_IORT: /* IORT */
|
|
if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_ioxt (ch); /* get next cmd */
|
|
}
|
|
ch6_rd_putw (ch); /* store wd; ignore wc */
|
|
if (ch_flags[ch] & CHF_EOR) { /* EOR? */
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */
|
|
return ch6_ioxt (ch); /* unstall or disc */
|
|
}
|
|
return SCPE_OK; /* done */
|
|
|
|
default:
|
|
return SCPE_IERR;
|
|
} /* end case */
|
|
} /* end if read */
|
|
|
|
else { /* 7607 write */
|
|
|
|
if (ch_sta[ch] != CHXS_DSX) /* chan exec? no, disc */
|
|
return ch6_end_ds (ch);
|
|
switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */
|
|
|
|
case CH6_TCH: /* transfer */
|
|
ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */
|
|
return ch6_new_cmd (ch, FALSE); /* unpack new cmd */
|
|
|
|
case CH6_IOCD: /* IOCD */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if ((r = ch6_wr_getw (ch, TRUE))) /* send wd to dev; err? */
|
|
return r;
|
|
if (ch_wc[ch]) /* more to do? */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_end_ds (ch); /* disconnect */
|
|
|
|
case CH6_IOCP: /* IOCP */
|
|
case CH6_IOSP: /* IOSP */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if ((r = ch6_wr_getw (ch, FALSE))) /* send wd to dev; err? */
|
|
return r;
|
|
if (ch_wc[ch]) /* more to do? */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_new_cmd (ch, FALSE); /* get next cmd */
|
|
|
|
case CH6_IOCT: /* IOCT */
|
|
case CH6_IOST: /* IOST */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if ((r = ch6_wr_getw (ch, FALSE))) /* send wd to dev; err? */
|
|
return r;
|
|
if (ch_wc[ch]) /* more to do? */
|
|
return SCPE_OK;
|
|
}
|
|
return ch6_ioxt (ch); /* get next cmd */
|
|
|
|
case CH6_IORP: /* IORP */
|
|
if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */
|
|
if ((r = ch6_wr_getw (ch, TRUE))) /* send wd to dev; err? */
|
|
return r;
|
|
if (ch_wc[ch]) /* more to do? */
|
|
return SCPE_OK;
|
|
}
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */
|
|
return ch6_new_cmd (ch, FALSE); /* get next cmd */
|
|
|
|
case CH6_IORT: /* IORT */
|
|
if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */
|
|
if ((r = ch6_wr_getw (ch, TRUE))) /* send wd to dev; err? */
|
|
return r;
|
|
if (ch_wc[ch]) /* more to do? */
|
|
return SCPE_OK;
|
|
}
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */
|
|
return ch6_ioxt (ch); /* unstall or disc */
|
|
|
|
default:
|
|
return SCPE_IERR;
|
|
} /* end switch */
|
|
} /* end else write */
|
|
}
|
|
|
|
/* 7607 channel support routines */
|
|
|
|
/* 7607 channel input routine - put one word to memory */
|
|
|
|
t_bool ch6_rd_putw (uint32 ch)
|
|
{
|
|
if (ch_idf[ch] & CH6DF_EOR) /* eor from dev? */
|
|
ch_flags[ch] |= CHF_EOR;
|
|
else ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* set/clr chan eor */
|
|
ch_idf[ch] = 0; /* clear eor, valid */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
if ((ch_op[ch] & 1) == 0) { /* do store? */
|
|
WriteP (ch_ca[ch], ch_ar[ch]);
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */
|
|
}
|
|
ch_wc[ch] = ch_wc[ch] - 1;
|
|
}
|
|
return (ch_wc[ch]? TRUE: FALSE);
|
|
}
|
|
|
|
/* 7607 channel output routine - get one word from memory */
|
|
|
|
t_stat ch6_wr_getw (uint32 ch, t_bool eorz)
|
|
{
|
|
DEVICE *dptr;
|
|
DIB *dibp;
|
|
uint32 eorfl;
|
|
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clr eor */
|
|
if (ch_wc[ch]) {
|
|
ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */
|
|
ch_wc[ch] = ch_wc[ch] - 1;
|
|
}
|
|
else ch_ar[ch] = 0;
|
|
if (eorz && (ch_wc[ch] == 0)) /* eor on wc = 0? */
|
|
eorfl = 1;
|
|
else eorfl = 0;
|
|
dptr = ch_find_dev (ch, ch_dsu[ch]); /* find device */
|
|
if (dptr && /* valid device? */
|
|
(dibp = (DIB *) dptr->ctxt) && /* with DIB? */
|
|
dibp->write) /* and write routine? */
|
|
return dibp->write (ch, ch_ar[ch], eorfl);
|
|
return SCPE_IERR; /* huh? */
|
|
}
|
|
|
|
/* 7607 channel new command - on channel load, check for disconnects
|
|
|
|
The protocol for new commands is as follows:
|
|
- If IOCD 0,,0, disconnect immediately
|
|
- If IOCT 0,,0 or IOST 0,,0 and loaded by RCHA, disconnect immediately
|
|
- If an effective NOP (TCH, IOCx 0,,0, IOSx 0,,0), force a channel
|
|
cycle to retire the channel comand as quickly as possible.
|
|
- If an IORx and EOR is set, force a channel cycle to retire the
|
|
channel command as quickly as possible.
|
|
*/
|
|
|
|
t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld)
|
|
{
|
|
t_uint64 ir;
|
|
uint32 op, t;
|
|
|
|
ir = ReadP (t = ch_clc[ch]); /* read cmd */
|
|
ch_wc[ch] = GET_DEC (ir); /* get word cnt */
|
|
ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */
|
|
op = GET_OPD (ir) << 1; /* get opcode */
|
|
ch_op[ch] = op | ((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */
|
|
if ((ir & CHI_IND) && (ch_wc[ch] || /* indirect? */
|
|
((op != CH6_IOCP) && (op != CH6_IOSP)))) { /* wc >0, or !IOxP? */
|
|
t_uint64 sr = ReadP (ch_ca[ch] & AMASK); /* read indirect */
|
|
ch_ca[ch] = ((uint32) sr) & ((cpu_model & I_CT)? PAMASK: AMASK);
|
|
}
|
|
if (hst_ch)
|
|
cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0);
|
|
ch_clc[ch] = (ch_clc[ch] + 1) & AMASK; /* incr chan pc */
|
|
|
|
switch (op) { /* case on opcode */
|
|
|
|
case CH6_IOCD: /* IOCD */
|
|
if (ch_wc[ch] == 0) /* wc 0? end now */
|
|
ch6_end_ds (ch);
|
|
break;
|
|
|
|
case CH6_IOST: /* IOST */
|
|
if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */
|
|
ch_req |= REQ_CH (ch);
|
|
case CH6_IOCT: /* IOCT */
|
|
if (ch_wc[ch] == 0) { /* wc 0? */
|
|
if (ch_ld) /* load? end now */
|
|
ch6_end_ds (ch);
|
|
else ch_req |= REQ_CH (ch); /* else immed ch req */
|
|
}
|
|
break;
|
|
|
|
case CH6_IOSP: /* IOSP */
|
|
if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */
|
|
ch_req |= REQ_CH (ch);
|
|
case CH6_IOCP: /* IOCP */
|
|
if (ch_wc[ch] == 0) /* wc 0? immed ch req */
|
|
ch_req |= REQ_CH (ch);
|
|
break;
|
|
|
|
case CH6_IORT: /* IORT */
|
|
case CH6_IORP: /* IORP */
|
|
if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */
|
|
ch_req |= REQ_CH (ch);
|
|
break;
|
|
|
|
case CH6_TCH: /* TCH */
|
|
ch_req |= REQ_CH (ch); /* immed ch req */
|
|
break;
|
|
|
|
default: /* all others */
|
|
break;
|
|
} /* end case */
|
|
|
|
if (sim_brk_summ && sim_brk_test (t, SWMASK ('E')))
|
|
return ch_bkpt (ch, t);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 7607 channel IOxT: if LCH stall, set state back to DSW; else disconnect and trap */
|
|
|
|
t_stat ch6_ioxt (uint32 ch)
|
|
{
|
|
if (ch_flags[ch] & CHF_LDW) { /* LCH cmd pending? */
|
|
ch_flags[ch] &= ~CHF_LDW; /* clr pending flag */
|
|
ch_sta[ch] = CH6S_DSW; /* unstall pending LCH */
|
|
}
|
|
else {
|
|
ch_flags[ch] |= CHF_CMD; /* set cmd trap flag */
|
|
ch6_end_ds (ch); /* disconnect */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 7607 conditionally clear EOR on IOSx completion */
|
|
|
|
void ch6_iosp_cclr (uint32 ch)
|
|
{
|
|
uint32 i, op;
|
|
|
|
if (ch_wc[ch] == 0) { /* wc = 0? */
|
|
uint32 ccnt = 5; /* allow 5 for CPU */
|
|
for (i = 0; i < NUM_CHAN; i++) { /* test channels */
|
|
if (ch_sta[ch] != CHXS_DSX) /* idle? skip */
|
|
continue;
|
|
op = ch_op[ch] & ~1; /* get op */
|
|
ccnt++; /* 1 per active ch */
|
|
if ((op == CH6_IOCP) || (op == CH6_IORP) || /* 1 per proceed */
|
|
(op == CH6_IOSP))
|
|
ccnt++;
|
|
}
|
|
if (ccnt <= 11) /* <= 11? ok */
|
|
return;
|
|
}
|
|
ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear eor */
|
|
return;
|
|
}
|
|
|
|
/* 7607 external interface routines */
|
|
|
|
/* Input - store word, request channel input service */
|
|
|
|
t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 fl)
|
|
{
|
|
if (ch6_qconn (ch, unit)) { /* ch conn to caller? */
|
|
if (ch_idf[ch] & CH6DF_VLD) /* overrun? */
|
|
ind_ioc = 1;
|
|
ch_idf[ch] = CH6DF_VLD; /* set ar valid */
|
|
if (fl) /* set eor if requested */
|
|
ch_idf[ch] |= CH6DF_EOR;
|
|
ch_req |= REQ_CH (ch); /* request chan */
|
|
ch_flags[ch] |= CHF_RDS;
|
|
ch_ar[ch] = val & DMASK; /* save data */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Disconnect on error */
|
|
|
|
t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 fl)
|
|
{
|
|
if (ch6_qconn (ch, unit)) { /* ch conn to caller? */
|
|
ch_flags[ch] |= fl; /* set flag */
|
|
return ch6_end_ds (ch); /* disconnect */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Output - request channel output service */
|
|
|
|
t_bool ch6_req_wr (uint32 ch, uint32 unit)
|
|
{
|
|
if (ch6_qconn (ch, unit)) { /* ch conn to caller? */
|
|
ch_req |= REQ_CH (ch);
|
|
ch_flags[ch] &= ~CHF_RDS;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set/read channel flags */
|
|
|
|
uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags)
|
|
{
|
|
if (ch6_qconn (ch, unit)) { /* ch conn to caller? */
|
|
ch_flags[ch] = ch_flags[ch] | flags;
|
|
return ch_flags[ch];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Channel connected to unit? */
|
|
|
|
t_bool ch6_qconn (uint32 ch, uint32 unit)
|
|
{
|
|
if ((ch < NUM_CHAN) && /* valid chan */
|
|
(ch_dsu[ch] == unit)) /* for right unit? */
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* 7909 channel support routines */
|
|
|
|
/* 7909 channel input routine - put one word to memory */
|
|
|
|
t_stat ch9_rd_putw (uint32 ch)
|
|
{
|
|
ch_idf[ch] = 0; /* invalidate */
|
|
if (ch_wc[ch]) { /* wc > 0? */
|
|
WriteP (ch_ca[ch], ch_ar[ch]);
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]);
|
|
ch_wc[ch] = ch_wc[ch] - 1;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 7909 channel output routine - get one word from memory */
|
|
|
|
t_stat ch9_wr_getw (uint32 ch)
|
|
{
|
|
if (ch_wc[ch]) {
|
|
ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */
|
|
ch_ca[ch] = CHAINC (ch_ca[ch]);
|
|
ch_wc[ch] = ch_wc[ch] - 1;
|
|
}
|
|
else ch_ar[ch] = 0;
|
|
return ch9_wr (ch, ch_ar[ch], 0); /* write to device */
|
|
}
|
|
|
|
/* 7909 send select to device */
|
|
|
|
t_stat ch9_sel (uint32 ch, uint32 sel)
|
|
{
|
|
DEVICE *dptr = ch2dev[ch];
|
|
DIB *dibp;
|
|
|
|
if (dptr == NULL)
|
|
return SCPE_IERR;
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp && dibp->chsel)
|
|
return dibp->chsel (ch, sel, 0);
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
/* 7909 send word to device */
|
|
|
|
t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl)
|
|
{
|
|
DEVICE *dptr = ch2dev[ch];
|
|
DIB *dibp;
|
|
|
|
if (dptr == NULL)
|
|
return SCPE_IERR;
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp && dibp->write)
|
|
return dibp->write (ch, dat, fl);
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
/* 7909 channel new command */
|
|
|
|
t_stat ch9_new_cmd (uint32 ch)
|
|
{
|
|
t_uint64 ir;
|
|
uint32 t;
|
|
t_stat r;
|
|
|
|
ir = ReadP (t = ch_clc[ch]); /* read cmd */
|
|
r = ch9_exec_cmd (ch, ir); /* exec cmd */
|
|
if (ch_sta[ch] != CHXS_IDLE) /* chan running? */
|
|
ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr chan pc */
|
|
if ((r == SCPE_OK) && sim_brk_summ && sim_brk_test (t, SWMASK ('E')))
|
|
return ch_bkpt (ch, t);
|
|
return r;
|
|
}
|
|
|
|
t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir)
|
|
{
|
|
uint32 op;
|
|
|
|
ch_wc[ch] = GET_DEC (ir); /* get word cnt */
|
|
ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */
|
|
op = (GET_OPD (ir) << 2); /* get opcode */
|
|
ch_op[ch] = op | ((((uint32) ir) & 0200000)? 1: 0) | /* plus bit<19> */
|
|
(((op & 010) && (ch_wc[ch] & 040000))? 2: 0); /* plus bit 3 if used */
|
|
if (ir & CHI_IND) { /* indirect? */
|
|
t_uint64 sr = ReadP (ch_ca[ch] & CHAMASK); /* read indirect */
|
|
ch_ca[ch] = ((uint32) sr) & CHAMASK; /* get address */
|
|
}
|
|
if (hst_ch)
|
|
cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0);
|
|
|
|
switch (ch_op[ch]) { /* check initial cond */
|
|
|
|
case CH9_LAR: /* misc processing */
|
|
case CH9_SAR:
|
|
case CH9_ICC:
|
|
case CH9_ICCA:
|
|
case CH9_XMT:
|
|
case CH9_LCC:
|
|
case CH9_SMS:
|
|
if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
|
|
ch9_eval_int (ch, CHINT_SEQC); /* not during data */
|
|
/* fall through */
|
|
case CH9_TCM: /* jumps */
|
|
case CH9_TCH:
|
|
case CH9_TDC:
|
|
case CH9_LIPT:
|
|
case CH9_LIP:
|
|
ch_req |= REQ_CH (ch); /* process in chan */
|
|
break;
|
|
|
|
case CH9_CTL: /* control */
|
|
case CH9_CTLR:
|
|
case CH9_CTLW:
|
|
if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
|
|
ch9_eval_int (ch, CHINT_SEQC); /* not during data */
|
|
ch_flags[ch] &= ~CHF_EOR;
|
|
if (ch_wc[ch] & CH9D_NST) /* N set? proc in chan */
|
|
ch_req |= REQ_CH (ch);
|
|
else return ch9_sel (ch, CHSL_CTL); /* sel, dev sets ch_req! */
|
|
break;
|
|
|
|
case CH9_SNS: /* sense */
|
|
if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))
|
|
ch9_eval_int (ch, CHINT_SEQC);
|
|
ch_flags[ch] &= ~CHF_EOR;
|
|
ch_req |= REQ_CH (ch); /* process in chan */
|
|
break;
|
|
|
|
case CH9_CPYD: /* data transfers */
|
|
case CH9_CPYP:
|
|
if ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) == 0)
|
|
ch9_eval_int (ch, CHINT_SEQC); /* not unless data */
|
|
if (ch_flags[ch] & CHF_PRD)
|
|
ch_flags[ch] |= CHF_RDS;
|
|
else if (ch_flags[ch] & CHF_PWR)
|
|
ch_flags[ch] |= CHF_WRS;
|
|
ch_flags[ch] &= ~(CHF_EOR|CHF_PRD|CHF_PWR);
|
|
if ((ch_op[ch] == CH9_CPYP) && (ch_wc[ch] == 0))
|
|
ch_req |= REQ_CH (ch); /* CPYP x,,0? */
|
|
break; /* dev sets ch_req! */
|
|
|
|
case CH9_WTR: /* wait */
|
|
ch_sta[ch] = CHXS_IDLE; /* stop */
|
|
break;
|
|
|
|
case CH9_TWT: /* trap and wait */
|
|
ch_sta[ch] = CHXS_IDLE; /* stop */
|
|
ch_flags[ch] |= CHF_TWT; /* set trap */
|
|
break;
|
|
|
|
default:
|
|
return STOP_ILLIOP;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 7909 external interface routines */
|
|
|
|
/* Input - store word, request channel input service */
|
|
|
|
t_stat ch9_req_rd (uint32 ch, t_uint64 val)
|
|
{
|
|
if (ch < NUM_CHAN) { /* valid chan? */
|
|
if (ch_idf[ch] & CH9DF_VLD) /* prev still valid? io chk */
|
|
ch9_set_ioc (ch);
|
|
ch_idf[ch] = CH9DF_VLD; /* set ar valid */
|
|
ch_req |= REQ_CH (ch); /* request chan */
|
|
ch_ar[ch] = val & DMASK; /* save data */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set attention */
|
|
|
|
void ch9_set_atn (uint32 ch)
|
|
{
|
|
if (ch < NUM_CHAN)
|
|
ch9_eval_int (ch, CHINT_ATN1);
|
|
return;
|
|
}
|
|
|
|
/* Set IO check - UEND will occur at end - not recognized in int mode */
|
|
|
|
void ch9_set_ioc (uint32 ch)
|
|
{
|
|
if ((ch < NUM_CHAN) && !(ch_flags[ch] & CHF_INT)) {
|
|
ind_ioc = 1; /* IO check */
|
|
ch_flags[ch] |= CHF_IOC; /* ch IOC for end */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Set end */
|
|
|
|
void ch9_set_end (uint32 ch, uint32 iflags)
|
|
{
|
|
if (ch < NUM_CHAN) { /* valid chan? */
|
|
ch_flags[ch] |= CHF_EOR;
|
|
ch9_eval_int (ch, iflags);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Test connected */
|
|
|
|
t_bool ch9_qconn (uint32 ch)
|
|
{
|
|
if ((ch < NUM_CHAN) && (ch_sta[ch] == CHXS_DSX))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Evaluate interrupts
|
|
|
|
- Interrupt requests set flags in the channel flags word
|
|
- If an interrupt is not in progress, interrupt requests are evaluated
|
|
- If an interrupt request is found, the interruptable flags are
|
|
transferred to the channel condition register and cleared in
|
|
the channel flags
|
|
|
|
This provides an effective stage of buffering for interrupt requests
|
|
that are not immediately serviced */
|
|
|
|
void ch9_eval_int (uint32 ch, uint32 iflags)
|
|
{
|
|
uint32 ireq;
|
|
|
|
ch_flags[ch] |= (iflags << CHF_V_COND); /* or into chan flags */
|
|
if ((ch_flags[ch] & CHF_INT) == 0) { /* int not in prog? */
|
|
ireq = ((ch_flags[ch] >> CHF_V_COND) & CHF_M_COND) &
|
|
~(((ch_sms[ch] & CHSMS_IUEND)? CHINT_UEND: 0) |
|
|
((ch_sms[ch] & CHSMS_IATN1)? CHINT_ATN1: 0) |
|
|
((ch_sms[ch] & CHSMS_IATN2)? CHINT_ATN2: 0) |
|
|
((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))? CHINT_SEQC: 0));
|
|
if (ireq) { /* int pending? */
|
|
ch_cnd[ch] = ireq; /* set cond reg */
|
|
ch_flags[ch] &= ~(ireq << CHF_V_COND); /* clear chan flags */
|
|
ch_flags[ch] |= CHF_IRQ; /* set int req */
|
|
ch_req |= REQ_CH (ch); /* request channel */
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Test for all channels idle */
|
|
|
|
t_bool ch_qidle (void)
|
|
{
|
|
uint32 i;
|
|
|
|
for (i = 0; i < NUM_CHAN; i++) {
|
|
if (ch_sta[i] != CHXS_IDLE)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Evaluate/execute channel traps */
|
|
|
|
uint32 chtr_eval (uint32 *decr)
|
|
{
|
|
uint32 i, cme;
|
|
|
|
if (!chtr_inht && !chtr_inhi && chtr_enab) {
|
|
if (BIT_TST (chtr_enab, CHTR_V_CLK) && chtr_clk) { /* clock trap? */
|
|
if (decr) { /* exec? */
|
|
chtr_clk = 0; /* clr flag */
|
|
*decr = 0;
|
|
}
|
|
return CHTR_CLK_SAV;
|
|
}
|
|
for (i = 0; i < NUM_CHAN; i++) { /* loop thru chan */
|
|
cme = BIT_TST (chtr_enab, CHTR_V_CME + i); /* cmd/eof enab? */
|
|
if (cme && (ch_flags[i] & CHF_CMD)) { /* cmd enab and set? */
|
|
if (decr) { /* exec? */
|
|
ch_flags[i] &= ~CHF_CMD; /* clr flag */
|
|
*decr = CHTR_F_CMD;
|
|
}
|
|
return (CHTR_CHA_SAV + (i << 1));
|
|
}
|
|
if (cme && (ch_flags[i] & CHF_EOF)) { /* eof enab and set? */
|
|
if (decr) { /* exec? */
|
|
ch_flags[i] &= ~CHF_EOF; /* clr flag */
|
|
*decr = CHTR_F_EOF;
|
|
}
|
|
return (CHTR_CHA_SAV + (i << 1));
|
|
}
|
|
if (BIT_TST (chtr_enab, CHTR_V_TRC + i) && /* trc enab? */
|
|
(ch_flags[i] & CHF_TRC)) { /* trc flag? */
|
|
if (decr) { /* exec? */
|
|
ch_flags[i] &= ~CHF_TRC; /* clr flag */
|
|
*decr = CHTR_F_TRC;
|
|
}
|
|
return (CHTR_CHA_SAV + (i << 1));
|
|
} /* end if BIT_TST */
|
|
} /* end for */
|
|
} /* end if !chtr_inht */
|
|
if (decr)
|
|
*decr = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Channel reset */
|
|
|
|
t_stat ch_reset (DEVICE *dptr)
|
|
{
|
|
uint32 ch = dptr - &ch_dev[0]; /* get channel */
|
|
|
|
if (ch == CH_A) /* channel A fixed */
|
|
ch2dev[ch] = &mt_dev[0];
|
|
ch_sta[ch] = 0;
|
|
ch_flags[ch] = 0;
|
|
ch_idf[ch] = 0;
|
|
ch_dso[ch] = 0;
|
|
ch_dsu[ch] = 0;
|
|
ch_ndso[ch] = 0;
|
|
ch_ndsu[ch] = 0;
|
|
ch_op[ch] = 0;
|
|
ch_clc[ch] = 0;
|
|
ch_wc[ch] = 0;
|
|
ch_ca[ch] = 0;
|
|
ch_ar[ch] = 0;
|
|
ch_sms[ch] = 0;
|
|
ch_cnd[ch] = 0;
|
|
ch_lcc[ch] = 0;
|
|
sim_cancel (&ch_unit[ch]);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show channel type */
|
|
|
|
t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc)
|
|
{
|
|
DEVICE *dptr;
|
|
|
|
dptr = find_dev_from_unit (uptr);
|
|
if (dptr == NULL)
|
|
return SCPE_IERR;
|
|
if (dptr->flags & DEV_7909)
|
|
fputs ("7909", st);
|
|
else if (dptr->flags & DEV_7289)
|
|
fputs ("7289", st);
|
|
else fputs ("7607", st);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Enable channel, assign device */
|
|
|
|
t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
DEVICE *dptr, *dptr1;
|
|
char gbuf[CBUFSIZE];
|
|
uint32 i, ch;
|
|
|
|
dptr = find_dev_from_unit (uptr);
|
|
if (dptr == NULL)
|
|
return SCPE_IERR;
|
|
ch = dptr - &ch_dev[0];
|
|
if ((ch == 0) || !(dptr->flags & DEV_DIS))
|
|
return SCPE_ARG;
|
|
if (cptr == NULL)
|
|
cptr = "TAPE";
|
|
get_glyph (cptr, gbuf, 0);
|
|
for (i = 0; dev_table[i].name; i++) {
|
|
if (strcmp (dev_table[i].name, gbuf) == 0) {
|
|
dptr1 = ch_map_flags (ch, dev_table[i].flags);
|
|
if (!dptr1 || !(dptr1->flags & DEV_DIS))
|
|
return SCPE_ARG;
|
|
dptr->flags &= ~(DEV_DIS|DEV_7909|DEV_7289|DEV_7750|DEV_7631);
|
|
dptr->flags |= dev_table[i].flags;
|
|
dptr1->flags &= ~DEV_DIS;
|
|
ch2dev[ch] = dptr1;
|
|
return reset_all (0);
|
|
}
|
|
}
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
/* Map device flags to device pointer */
|
|
|
|
DEVICE *ch_map_flags (uint32 ch, int32 fl)
|
|
{
|
|
if (fl & DEV_7289)
|
|
return &drm_dev;
|
|
if (!(fl & DEV_7909))
|
|
return &mt_dev[ch];
|
|
if (fl & DEV_7631)
|
|
return &dsk_dev;
|
|
if (fl & DEV_7750)
|
|
return &com_dev;
|
|
return NULL;
|
|
}
|
|
|
|
/* Set up channel map */
|
|
|
|
void ch_set_map (void)
|
|
{
|
|
uint32 i;
|
|
|
|
for (i = 0; i < NUM_CHAN; i++) {
|
|
if (ch_dev[i].flags & DEV_DIS)
|
|
ch2dev[i] = NULL;
|
|
else ch2dev[i] = ch_map_flags (i, ch_dev[i].flags);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Disable channel, deassign device */
|
|
|
|
t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc)
|
|
{
|
|
DEVICE *dptr, *dptr1;
|
|
UNIT *uptr1;
|
|
uint32 i, ch;
|
|
|
|
dptr = find_dev_from_unit (uptr);
|
|
if (dptr == NULL)
|
|
return SCPE_IERR;
|
|
ch = dptr - &ch_dev[0];
|
|
if ((ch == 0) || (dptr->flags & DEV_DIS) || (cptr != NULL))
|
|
return SCPE_ARG;
|
|
dptr1 = ch2dev[ch];
|
|
if (dptr1 == NULL)
|
|
return SCPE_IERR;
|
|
if (dptr1->units) {
|
|
for (i = 0; i < dptr1->numunits; i++) {
|
|
uptr1 = dptr1->units + i;
|
|
if (dptr1->detach)
|
|
dptr1->detach (uptr1);
|
|
else detach_unit (uptr1);
|
|
}
|
|
}
|
|
dptr->flags &= ~(DEV_7909|DEV_7289);
|
|
dptr->flags |= DEV_DIS;
|
|
dptr1->flags |= DEV_DIS;
|
|
return reset_all (0);
|
|
}
|
|
|
|
/* Show channel that device is on (tapes, 7289, 7909 only) */
|
|
|
|
t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
|
|
{
|
|
DEVICE *dptr;
|
|
uint32 i;
|
|
|
|
dptr = find_dev_from_unit (uptr);
|
|
if (dptr) {
|
|
for (i = 0; i < NUM_CHAN; i++) {
|
|
if (ch2dev[i] == dptr) {
|
|
fprintf (st, "channel %c", 'A' + i);
|
|
return SCPE_OK;
|
|
}
|
|
}
|
|
}
|
|
fprintf (st, "not assigned to channel");
|
|
return SCPE_OK;
|
|
}
|