SEL32: Update Ping and ICMP support code to use correct packet size. SEL32: Update SetupNet script to support latest Fedora release. SEL32: Improve disk write speed. SEL32: Add .tap file reassignent support in sel32_mt.c.
695 lines
33 KiB
C
695 lines
33 KiB
C
/* sel32_con.c: SEL 32 Class F IOP processor console.
|
|
|
|
Copyright (c) 2018-2023, James C. Bevier
|
|
Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers
|
|
|
|
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
|
|
JAMES C. BEVIER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
This is the standard console interface. It is subchannel of the IOP 7e00.
|
|
|
|
These units each buffer one record in local memory and signal
|
|
ready when the buffer is full or empty. The channel must be
|
|
ready to recieve/transmit data when they are activated since
|
|
they will transfer their block during chan_cmd. All data is
|
|
transmitted as ASCII characters.
|
|
*/
|
|
|
|
#include "sel32_defs.h"
|
|
#include "sim_tmxr.h"
|
|
|
|
#if NUM_DEVS_CON > 0
|
|
|
|
extern uint32 CPUSTATUS;
|
|
extern uint32 attention_trap;
|
|
|
|
#define UNIT_CON UNIT_DISABLE
|
|
#define CON_WAIT 1000
|
|
#define CMD u3
|
|
/* Held in u3 is the device command and status */
|
|
#define CON_INCH 0x00 /* Initialize channel command */
|
|
#define CON_INCH2 0xf0 /* Initialize channel command for processing */
|
|
#define CON_WR 0x01 /* Write console */
|
|
#define CON_RD 0x02 /* Read console */
|
|
#define CON_NOP 0x03 /* No op command */
|
|
#define CON_SNS 0x04 /* Sense command */
|
|
#define CON_ECHO 0x0a /* Read with Echo */
|
|
#define CON_RDBWD 0x0c /* Read backward */
|
|
#define CON_CON 0x1f /* connect line */
|
|
#define CON_DIS 0x23 /* disconnect line */
|
|
#define CON_RWD 0x37 /* TOF and write line */
|
|
|
|
#define CON_MSK 0xff /* Command mask */
|
|
|
|
/* Status held in u3 */
|
|
/* controller/unit address in upper 16 bits */
|
|
#define CON_ATAT 0x4000 /* working on @@A input */
|
|
#define CON_READ 0x2000 /* Read mode selected */
|
|
#define CON_OUTPUT 0x1000 /* Output ready for unit */
|
|
#define CON_EKO 0x0800 /* Echo input character */
|
|
#define CON_REQ 0x0400 /* Request key pressed */
|
|
#define CON_CR 0x0200 /* Output at beginning of line */
|
|
#define CON_INPUT 0x0100 /* Input ready for unit */
|
|
|
|
/* Input buffer pointer held in u4 */
|
|
|
|
#define SNS u5
|
|
/* in u5 packs sense byte 0,1 and 3 */
|
|
/* Sense byte 0 */
|
|
#define SNS_CMDREJ 0x80000000 /* Command reject */
|
|
#define SNS_INTVENT 0x40000000 /* Unit intervention required */
|
|
/* sense byte 3 */
|
|
#define SNS_RDY 0x80 /* device ready */
|
|
#define SNS_ONLN 0x40 /* device online */
|
|
#define SNS_DSR 0x08 /* data set ready */
|
|
#define SNS_DCD 0x04 /* data carrier detect */
|
|
|
|
/* std devices. data structures
|
|
con_dev Console device descriptor
|
|
con_unit Console unit descriptor
|
|
con_reg Console register list
|
|
con_mod Console modifiers list
|
|
*/
|
|
|
|
struct _con_data
|
|
{
|
|
uint8 incnt; /* char count */
|
|
uint8 ibuff[145]; /* Input line buffer */
|
|
}
|
|
con_data[NUM_UNITS_CON];
|
|
|
|
uint32 atbuf=0; /* attention buffer */
|
|
uint32 outbusy = 0; /* output waiting on timeout */
|
|
uint32 inbusy = 0; /* input waiting on timeout */
|
|
|
|
/* forward definitions */
|
|
t_stat con_preio(UNIT *uptr, uint16 chan);
|
|
t_stat con_startcmd(UNIT*, uint16, uint8);
|
|
void con_ini(UNIT*, t_bool);
|
|
t_stat con_srvi(UNIT*);
|
|
t_stat con_srvo(UNIT*);
|
|
t_stat con_haltio(UNIT *);
|
|
t_stat con_rschnlio(UNIT *uptr); /* Reset Channel */
|
|
t_stat con_poll(UNIT *);
|
|
t_stat con_reset(DEVICE *);
|
|
|
|
/* channel program information */
|
|
CHANP con_chp[NUM_UNITS_CON] = {0};
|
|
|
|
MTAB con_mod[] = {
|
|
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL},
|
|
{0}
|
|
};
|
|
|
|
UNIT con_unit[] = {
|
|
{UDATA(&con_srvi, UNIT_CON|UNIT_IDLE, 0), CON_WAIT, UNIT_ADDR(0x7EFC)}, /* 0 Input */
|
|
{UDATA(&con_srvo, UNIT_CON, 0), CON_WAIT, UNIT_ADDR(0x7EFD)}, /* 1 Output */
|
|
};
|
|
|
|
DIB con_dib = {
|
|
con_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */
|
|
con_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
|
|
con_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */
|
|
NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */
|
|
NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */
|
|
NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */
|
|
con_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */
|
|
NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */
|
|
con_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */
|
|
con_unit, /* UNIT* units */ /* Pointer to units structure */
|
|
con_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */
|
|
NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */
|
|
NUM_UNITS_CON, /* uint8 numunits */ /* number of units defined */
|
|
0x03, /* uint8 mask */ /* 2 devices - device mask */
|
|
0x7e00, /* uint16 chan_addr */ /* parent channel address */
|
|
0, /* uint32 chan_fifo_in */ /* fifo input index */
|
|
0, /* uint32 chan_fifo_out */ /* fifo output index */
|
|
{0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */
|
|
};
|
|
|
|
DEVICE con_dev = {
|
|
"CON", con_unit, NULL, con_mod,
|
|
NUM_UNITS_CON, 8, 15, 1, 8, 8,
|
|
// We must always have a consol
|
|
// NULL, NULL, &con_reset, NULL, &con_attach, &con_detach,
|
|
NULL, NULL, &con_reset, NULL, NULL, NULL,
|
|
&con_dib, DEV_DIS|DEV_DISABLE|DEV_DEBUG, 0, dev_debug
|
|
};
|
|
|
|
/*
|
|
* Console print routines.
|
|
*/
|
|
/* initialize the console chan/unit */
|
|
void con_ini(UNIT *uptr, t_bool f) {
|
|
int unit = (uptr - con_unit); /* unit 0 */
|
|
|
|
uptr->u4 = 0; /* no input count */
|
|
con_data[unit].incnt = 0; /* no input data */
|
|
uptr->CMD &= LMASK; /* leave only chsa */
|
|
uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */
|
|
sim_cancel(uptr); /* stop input poll */
|
|
if (unit == 0) {
|
|
sim_activate(uptr, 1000); /* start input poll */
|
|
}
|
|
}
|
|
|
|
/* start a console operation */
|
|
t_stat con_preio(UNIT *uptr, uint16 chan) {
|
|
DEVICE *dptr = get_dev(uptr);
|
|
int unit = (uptr - dptr->units);
|
|
|
|
if ((uptr->CMD & CON_MSK) != 0) { /* just return if busy */
|
|
sim_debug(DEBUG_CMD, dptr, "con_preio unit=%02x BUSY\n", unit);
|
|
return SNS_BSY;
|
|
}
|
|
|
|
sim_debug(DEBUG_CMD, dptr, "con_preio unit=%02x OK\n", unit);
|
|
return SCPE_OK; /* good to go */
|
|
}
|
|
|
|
/* start an I/O operation */
|
|
t_stat con_startcmd(UNIT *uptr, uint16 chan, uint8 cmd) {
|
|
DEVICE *dptr = uptr->dptr;
|
|
int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
uint8 ch;
|
|
|
|
if ((uptr->CMD & CON_MSK) != 0) { /* is unit busy */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd unit %01x chan %02x cmd %02x BUSY cmd %02x uptr %p\n",
|
|
unit, chan, cmd, uptr->CMD, uptr);
|
|
return SNS_BSY; /* yes, return busy */
|
|
}
|
|
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd @%s unit %01x chan %04x cmd %02x CMD %04x enter\n",
|
|
(CPUSTATUS & ONIPU)?"IPU":"CPU", unit, chsa, cmd, uptr->CMD);
|
|
|
|
/* substitute CON_INCH2 for CON_INCH for processing */
|
|
if (cmd == CON_INCH)
|
|
cmd = CON_INCH2; /* save INCH command as 0xf0 */
|
|
|
|
/* process the commands */
|
|
switch (cmd & 0xFF) {
|
|
|
|
case CON_CON: /* 0x1f */ /* Connect, return Data Set ready */
|
|
uptr->SNS |= (SNS_DSR|SNS_DCD); /* Data set ready, Data Carrier detected */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd CON CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
return SNS_CHNEND|SNS_DEVEND;
|
|
break;
|
|
|
|
case CON_DIS: /* 0x23 */ /* Disconnect has do nothing */
|
|
uptr->SNS &= ~(SNS_DSR|SNS_DCD); /* Data set not ready */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd DIS CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
return SNS_CHNEND|SNS_DEVEND;
|
|
break;
|
|
|
|
case CON_SNS: /* 0x04 */ /* Sense */
|
|
/* value 4 is Data Set Ready */
|
|
/* value 5 is Data carrier detected n/u */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd cmd %04x: Cmd Sense %02x\n", chsa, uptr->SNS);
|
|
ch = uptr->SNS & 0xff; /* Sense byte 3 */
|
|
if (chan_write_byte(chsa, &ch)) { /* write byte to memory */
|
|
/* write error */
|
|
cmd = 0; /* no cmd now */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd write error unit %02x: CMD %08x read %02x u4 %02x ccw_count %02x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, chp->ccw_count);
|
|
}
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
return SNS_CHNEND|SNS_DEVEND;
|
|
break;
|
|
|
|
/* if input tried from output device, error */
|
|
case CON_RD: /* 0x02 */ /* Read command */
|
|
case CON_ECHO: /* 0x0a */ /* Read command w/ECHO */
|
|
case CON_RDBWD: /* 0x0c */ /* Read Backward */
|
|
if (unit == 1) {
|
|
/* if input requested for output device, give error */
|
|
uptr->SNS |= SNS_CMDREJ; /* command rejected */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd Read to output device CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* unit check */
|
|
break;
|
|
}
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd READ %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
uptr->CMD &= ~CON_MSK; /* remove old CMD */
|
|
uptr->CMD |= (cmd & CON_MSK); /* save command */
|
|
if (cmd == CON_ECHO) /* 0x0a */
|
|
uptr->CMD |= CON_EKO; /* save echo status */
|
|
atbuf = 0; /* reset attention buffer */
|
|
uptr->CMD |= CON_READ; /* show read mode */
|
|
uptr->SNS |= (SNS_RDY|SNS_ONLN); /* status is online & ready */
|
|
/* input is polled, so no start needed */
|
|
sim_activate_abs(uptr, 250); /* start us off */
|
|
return SCPE_OK; /* no status change */
|
|
break;
|
|
|
|
case CON_RWD: /* 0x37 */ /* TOF and write line */
|
|
case CON_WR: /* 0x01 */ /* Write command */
|
|
if (unit == 0) {
|
|
/* if output requested for input device, give error */
|
|
uptr->SNS |= SNS_CMDREJ; /* command rejected */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd Write to input device CMD %08x chsa %04x cmd = %02x\n",
|
|
uptr->CMD, chsa, cmd);
|
|
return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK; /* unit check */
|
|
break;
|
|
}
|
|
uptr->SNS |= (SNS_RDY|SNS_ONLN); /* status is online & ready */
|
|
uptr->CMD &= ~CON_MSK; /* remove old CMD */
|
|
uptr->CMD |= (cmd & CON_MSK); /* save command */
|
|
/* using value 500 or larger causes diag to fail on 32/27 */
|
|
/* using value 230 or larger causes UTX21B to fail on 32/67 */
|
|
/* 32/67 error sequence */
|
|
/* Starting Syslog Daemon */
|
|
/* ioi: sio at 801 failed, cc=2, retry=0 */
|
|
/* panic: ioi: sio - bad cc */
|
|
//11523 sim_activate(uptr, 200); /* start us off */
|
|
sim_activate_abs(uptr, 200); /* start us off */
|
|
return SCPE_OK; /* no status change */
|
|
break;
|
|
|
|
case CON_INCH2: /* 0xf0 */ /* INCH command */
|
|
case CON_NOP: /* 0x03 */ /* NOP has do nothing */
|
|
uptr->SNS |= (SNS_RDY|SNS_ONLN); /* status is online & ready */
|
|
uptr->CMD &= ~CON_MSK; /* remove old CMD */
|
|
uptr->CMD |= (cmd & CON_MSK); /* save command */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd @%s INCH/NOP cmd CMD %08x chsa %04x cmd = %02x\n",
|
|
(CPUSTATUS & ONIPU)?"IPU":"CPU", uptr->CMD, chsa, cmd);
|
|
/* input is polled, so no start needed */
|
|
if (unit == 1) { /* doing output */
|
|
//11523 sim_activate(uptr, 250); /* start us off */
|
|
sim_activate_abs(uptr, 250); /* start us off */
|
|
} else {
|
|
sim_activate_abs(uptr, 250); /* start us off */
|
|
}
|
|
return SCPE_OK; /* no status change */
|
|
break;
|
|
|
|
default: /* invalid command */
|
|
break;
|
|
}
|
|
/* invalid command */
|
|
uptr->SNS |= SNS_CMDREJ; /* command rejected */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_startcmd %04x: Invalid command %02x Sense %02x\n",
|
|
chan, cmd, uptr->SNS);
|
|
return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK;
|
|
}
|
|
|
|
/* Handle output transfers for console */
|
|
t_stat con_srvo(UNIT *uptr) {
|
|
DEVICE *dptr = uptr->dptr;
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */
|
|
int cmd = uptr->CMD & CON_MSK;
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
int len = chp->ccw_count; /* INCH command count */
|
|
uint32 mema = chp->ccw_addr; /* get inch or buffer addr */
|
|
uint32 tstart;
|
|
uint8 ch;
|
|
static uint32 dexp;
|
|
static int cnt = 0;
|
|
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo enter CMD %08x chsa %04x cmd %02x iocla %06x cnt %04x\n",
|
|
uptr->CMD, chsa, cmd, chp->chan_caw, chp->ccw_count);
|
|
|
|
switch (cmd) {
|
|
|
|
case CON_INCH2: /* 0xf0 */ /* INCH command */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo INCH unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x inch %06x in2 %06x\n",
|
|
unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4, mema, M[mema>>2]);
|
|
|
|
/* now call set_inch() function to write and test inch buffer addresses */
|
|
tstart = set_inch(uptr, mema, 1); /* new address & 1 entry */
|
|
if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */
|
|
/* we have error, bail out */
|
|
uptr->SNS |= SNS_CMDREJ;
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo INCH Error unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x\n",
|
|
unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
|
break;
|
|
}
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo INCH CMD %08x chsa %04x len %02x inch %06x in2 %06x\n",
|
|
uptr->CMD, chsa, len, mema, M[mema>>2]);
|
|
/* WARNING, if SNS_DEVEND is not set, diags fail by looping in CON diag */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */
|
|
break;
|
|
|
|
case CON_NOP: /* 0x03 */ /* NOP has do nothing */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo NOP CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */
|
|
break;
|
|
|
|
case CON_RWD: /* 0x37 */ /* TOF and write line */
|
|
case CON_WR: /* 0x01 */ /* Write command */
|
|
cnt = 0; /* zero count */
|
|
/*RTC*/ outbusy = 1; /* tell clock output waiting */
|
|
mema = chp->ccw_addr; /* get buffer addr */
|
|
/* Write to device */
|
|
while (chan_read_byte(chsa, &ch) == SCPE_OK) { /* get byte from memory */
|
|
/* HACK HACK HACK */
|
|
ch &= 0x7f; /* make 7 bit w/o parity */
|
|
dexp = dexp<<8; /* move up last chars */
|
|
dexp |= ch; /* insert new char */
|
|
sim_putchar(ch); /* output next char to device */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo write addr %06x chsa %04x cmd %02x byte %d = %02x [%c]\n",
|
|
mema, chsa, cmd, cnt, ch, ch);
|
|
else
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo write addr %06x chsa %04x cmd %02x byte %d = %02x\n",
|
|
mema, chsa, cmd, cnt, ch);
|
|
mema = chp->ccw_addr; /* get next buffer addr */
|
|
cnt++; /* count chars output */
|
|
}
|
|
/* write is complete, post status */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvo write CMD %08x chsa %04x cmd %02x complete\n",
|
|
uptr->CMD, chsa, cmd);
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
/*RTC*/ outbusy = 0; /* output done */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* done */
|
|
break;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Handle input transfers for console */
|
|
t_stat con_srvi(UNIT *uptr) {
|
|
DEVICE *dptr = uptr->dptr;
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int unit = (uptr - con_unit); /* unit 0 is read, unit 1 is write */
|
|
int cmd = uptr->CMD & CON_MSK;
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
int len = chp->ccw_count; /* INCH command count */
|
|
uint32 mema = chp->ccw_addr; /* get inch or buffer addr */
|
|
uint32 tstart;
|
|
uint8 ch;
|
|
t_stat r;
|
|
|
|
switch (cmd) {
|
|
|
|
case CON_INCH2: /* 0xf0 */ /* INCH command */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi INCH unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x inch %06x in2 %06x\n",
|
|
unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4, mema, M[mema>>2]);
|
|
|
|
/* now call set_inch() function to write and test inch buffer addresses */
|
|
tstart = set_inch(uptr, mema, 1); /* new address & 1 entry */
|
|
if ((tstart == SCPE_MEM) || (tstart == SCPE_ARG)) { /* any error */
|
|
/* we have error, bail out */
|
|
uptr->SNS |= SNS_CMDREJ;
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi INCH Error unit %02x: CMD %08x cmd %02x incnt %02x u4 %02x\n",
|
|
unit, uptr->CMD, cmd, con_data[unit].incnt, uptr->u4);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);
|
|
break;
|
|
}
|
|
con_data[unit].incnt = 0; /* buffer empty */
|
|
uptr->u4 = 0; /* no I/O yet */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi INCH CMD %08x chsa %04x len %02x inch %06x in2 %06x\n",
|
|
uptr->CMD, chsa, len, mema, M[mema>>2]);
|
|
/* WARNING, if SNS_DEVEND is not set, diags fail by looping in CON diag */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */
|
|
/* drop through to poll input */
|
|
break;
|
|
|
|
case CON_NOP: /* 0x03 */ /* NOP has do nothing */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi NOP CMD %08x chsa %04x cmd = %02x\n", uptr->CMD, chsa, cmd);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* return OK */
|
|
/* drop through to poll input */
|
|
break;
|
|
|
|
case CON_ECHO: /* 0x0a */ /* read from device w/ECHO */
|
|
uptr->CMD |= CON_EKO; /* save echo status */
|
|
case CON_RD: /* 0x02 */ /* read from device */
|
|
case CON_RDBWD: /* 0x0c */ /* Read Backward */
|
|
|
|
if ((uptr->u4 != con_data[unit].incnt) || /* input empty */
|
|
(uptr->CMD & CON_INPUT)) { /* input waiting? */
|
|
ch = con_data[unit].ibuff[uptr->u4]; /* get char from read buffer */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_IRQ, dptr,
|
|
"con_srvi readbuf unit %02x: CMD %08x read %02x [%c] incnt %02x u4 %02x len %02x\n",
|
|
unit, uptr->CMD, ch, ch, con_data[unit].incnt, uptr->u4, chp->ccw_count);
|
|
else
|
|
sim_debug(DEBUG_IRQ, dptr,
|
|
"con_srvi readbuf unit %02x: CMD %08x read %02x incnt %02x u4 %02x len %02x\n",
|
|
unit, uptr->CMD, ch, con_data[unit].incnt, uptr->u4, chp->ccw_count);
|
|
|
|
/* process any characters */
|
|
if (uptr->u4 != con_data[unit].incnt) { /* input available */
|
|
ch = con_data[unit].ibuff[uptr->u4]; /* get char from read buffer */
|
|
/* this fixes mpx1x time entry on startup */
|
|
if (uptr->CMD & CON_EKO) /* ECHO requested */
|
|
sim_putchar(ch); /* ECHO the char */
|
|
if (chan_write_byte(chsa, &ch)) { /* write byte to memory */
|
|
/* write error */
|
|
cmd = 0; /* no cmd now */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi write error unit %02x: CMD %08x read %02x u4 %02x ccw_count %02x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, chp->ccw_count);
|
|
uptr->CMD &= ~CON_MSK; /* remove old CMD */
|
|
uptr->CMD &= ~CON_INPUT; /* input waiting? */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */
|
|
break;
|
|
}
|
|
/* character accepted, bump buffer pointer */
|
|
uptr->u4++; /* next char position */
|
|
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi write to mem unit %02x: CMD %08x read %02x u4 %02x incnt %02x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt);
|
|
|
|
/* see if at end of buffer */
|
|
if (uptr->u4 >= (int32)sizeof(con_data[unit].ibuff))
|
|
uptr->u4 = 0; /* reset pointer */
|
|
|
|
/* user want more data? */
|
|
if ((test_write_byte_end(chsa)) == 0) {
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi need more unit %02x CMD %08x u4 %02x ccw_count %02x incnt %02x\n",
|
|
unit, uptr->CMD, uptr->u4, chp->ccw_count, con_data[unit].incnt);
|
|
/* user wants more, look next time */
|
|
if (uptr->u4 == con_data[unit].incnt) { /* input empty */
|
|
uptr->CMD &= ~CON_INPUT; /* no input available */
|
|
}
|
|
break;
|
|
}
|
|
/* command is completed */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi read done unit %02x CMD %08x read %02x [%c] u4 %02x ccw_count %02x incnt %02x\n",
|
|
unit, uptr->CMD, ch, ch, uptr->u4, chp->ccw_count, con_data[unit].incnt);
|
|
else
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi read done unit %02x CMD %08x read %02x u4 %02x ccw_count %02x incnt %02x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, chp->ccw_count, con_data[unit].incnt);
|
|
cmd = 0; /* no cmd now */
|
|
uptr->CMD &= LMASK; /* nothing left, command complete */
|
|
if (uptr->u4 != con_data[unit].incnt) { /* input empty */
|
|
uptr->CMD |= CON_INPUT; /* input still available */
|
|
}
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* we done */
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* check for next input if reading or @@A sequence */
|
|
r = sim_poll_kbd(); /* poll for a char */
|
|
if (r & SCPE_KFLAG) { /* got a char */
|
|
ch = r & 0xff; /* drop any extra bits */
|
|
if ((uptr->CMD & CON_READ)) { /* looking for input? */
|
|
atbuf = 0; /* reset attention buffer */
|
|
uptr->CMD &= ~CON_ATAT; /* no @@A input */
|
|
if (ch == '@') { /* maybe for console int */
|
|
atbuf = (ch)<<8; /* start anew */
|
|
uptr->CMD |= CON_ATAT; /* show getting @ */
|
|
}
|
|
if (ch == '\n') /* convert newline */
|
|
ch = '\r'; /* make newline into carriage return */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi handle readch unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x r %x\n",
|
|
unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt, r);
|
|
else
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi handle readch unit %02x: CMD %08x read %02x u4 %02x incnt %02x r %x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt, r);
|
|
|
|
/* put char in buffer */
|
|
con_data[unit].ibuff[con_data[unit].incnt++] = ch;
|
|
|
|
/* see if count at max, if so reset to start */
|
|
if (con_data[unit].incnt >= sizeof(con_data[unit].ibuff))
|
|
con_data[unit].incnt = 0; /* reset buffer cnt */
|
|
|
|
uptr->CMD |= CON_INPUT; /* we have a char available */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi readch unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x\n",
|
|
unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt);
|
|
else
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi readch unit %02x: CMD %08x read %02x u4 %02x incnt %02x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt);
|
|
|
|
sim_activate(uptr, 500); /* do this again */
|
|
return SCPE_OK;
|
|
}
|
|
/* not looking for input, look for attn or wakeup */
|
|
if (ch == '?') {
|
|
/* set ring bit? */
|
|
set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND); /* tell user */
|
|
}
|
|
/* not wanting input, but we have a char, look for @@A */
|
|
if (uptr->CMD & CON_ATAT) { /* looking for @@A */
|
|
/* we have at least one @, look for another */
|
|
if (ch == '@' || ch == 'A' || ch == 'a') {
|
|
uint8 cc = ch;
|
|
if (cc == 'a')
|
|
cc = 'A'; /* make uppercase */
|
|
sim_putchar(ch); /* ECHO the char */
|
|
atbuf = (atbuf|cc)<<8; /* merge new char */
|
|
if (atbuf == 0x40404100) {
|
|
attention_trap = CONSOLEATN_TRAP; /* console attn (0xb4) */
|
|
atbuf = 0; /* reset attention buffer */
|
|
uptr->CMD &= ~CON_ATAT; /* no @@A input */
|
|
sim_putchar('\r'); /* return char */
|
|
sim_putchar('\n'); /* line feed char */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi unit %02x: CMD %08x read @@A Console Trap\n", unit, uptr->CMD);
|
|
uptr->u4 = 0; /* no input count */
|
|
con_data[unit].incnt = 0; /* no input data */
|
|
}
|
|
sim_activate(uptr, 500); /* do this again */
|
|
return SCPE_OK;
|
|
}
|
|
/* char not for us, so keep looking */
|
|
atbuf = 0; /* reset attention buffer */
|
|
uptr->CMD &= ~CON_ATAT; /* no @@A input */
|
|
}
|
|
/* not looking for input, look for attn or wakeup */
|
|
if (ch == '@') {
|
|
atbuf = (atbuf|ch)<<8; /* merge in char */
|
|
uptr->CMD |= CON_ATAT; /* show getting @ */
|
|
sim_putchar(ch); /* ECHO the char */
|
|
}
|
|
/* assume it is for next read request, so save it */
|
|
/* see if count at max, if so reset to start */
|
|
if (con_data[unit].incnt >= sizeof(con_data[unit].ibuff))
|
|
con_data[unit].incnt = 0; /* reset buffer cnt */
|
|
|
|
/* put char in buffer */
|
|
con_data[unit].ibuff[con_data[unit].incnt++] = ch;
|
|
|
|
uptr->CMD |= CON_INPUT; /* we have a char available */
|
|
if (isprint(ch))
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi readch2 unit %02x: CMD %08x read %02x [%c] u4 %02x incnt %02x r %x\n",
|
|
unit, uptr->CMD, ch, ch, uptr->u4, con_data[unit].incnt, r);
|
|
else
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"con_srvi readch2 unit %02x: CMD %08x read %02x u4 %02x incnt %02x r %x\n",
|
|
unit, uptr->CMD, ch, uptr->u4, con_data[unit].incnt, r);
|
|
}
|
|
sim_clock_coschedule(uptr, 1000); /* continue poll */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat con_reset(DEVICE *dptr) {
|
|
tmxr_set_console_units (&con_unit[0], &con_unit[1]);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Handle rschnlio cmds for console */
|
|
t_stat con_rschnlio(UNIT *uptr) {
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int cmd = uptr->CMD & CON_MSK;
|
|
con_ini(uptr, 0); /* reset the unit */
|
|
sim_debug(DEBUG_EXP, &con_dev, "con_rschnl chsa %04x cmd = %02x\n", chsa, cmd);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Handle haltio transfers for console */
|
|
t_stat con_haltio(UNIT *uptr) {
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int cmd = uptr->CMD & CON_MSK;
|
|
int unit = (uptr - con_unit); /* unit # 0 is read, 1 is write */
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
|
|
sim_debug(DEBUG_EXP, &con_dev,
|
|
"con_haltio enter chsa %04x cmd = %02x\n", chsa, cmd);
|
|
|
|
/* terminate any input command */
|
|
/* UTX wants SLI bit, but no unit exception */
|
|
/* status must not have an error bit set */
|
|
/* otherwise, UTX will panic with "bad status" */
|
|
if ((uptr->CMD & CON_MSK) != 0) { /* is unit busy */
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"con_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n",
|
|
chsa, cmd, chp->ccw_count);
|
|
sim_cancel(uptr); /* stop timer */
|
|
/* stop any I/O and post status and return error status */
|
|
chp->ccw_count = 0; /* zero the count */
|
|
chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */
|
|
uptr->CMD &= LMASK; /* make non-busy */
|
|
uptr->u4 = 0; /* no I/O yet */
|
|
con_data[unit].incnt = 0; /* no input data */
|
|
uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"con_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */
|
|
return CC2BIT | SCPE_IOERR; /* tell chan code to post status */
|
|
}
|
|
uptr->CMD &= LMASK; /* make non-busy */
|
|
uptr->SNS = SNS_RDY|SNS_ONLN; /* status is online & ready */
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"con_haltio HIO not busy chsa %04x cmd = %02x ccw_count %02x\n",
|
|
chsa, cmd, chp->ccw_count);
|
|
return CC1BIT | SCPE_OK; /* not busy */
|
|
}
|
|
#endif /* #if NUM_DEVS_CON > 0 */
|
|
|