The B5500 simulator supports the following peripherals. Two CPUs with between 4K and 32K of memory. The second CPU can be enabled with "set cpu1 enable". "set cpu1 disable" disables the second CPU. Up to 4 floating IO channels. Individual channels can be enabled with "set io# enable", or "set io# disable". There are two card readers. The second reader is disabled by default. There is one Card Punch. The Card reader and Card Punch support the following options: set cr format= auto - will automatically determine the format based on the text it recieves. text Text based cards. Tabs are converted to the correct number of spaces. A record of ~raw octal will enter a binary card. ~eor will enter a 7/8/9 punch in column 1. ~eof will enter a 6/7/9 punch in column 1. ~eoi will enter a 6/7/8/9 punch in column 1. ~~ will enter a ~ as the first character. bin Binary Card format: Each record 160 characters. First characters 6789---- Second character 21012345 111 Top 4 bits of second character are 0. It is unlikely that any other format could look like this. bcd BCD Format: Each record variable length (80 chars or less). Record mark has bit 7 set. Bit 6 is even parity. Bits 5-0 are character. cbn CBN Format: Each record 160 charaters. First char has bit 7 set. Rest set to 0. Bit 6 is odd parity. Bit 5-0 of first character are top 6 bits of card. Bit 5-0 of second character are lower 6 bits of card. For punch format of auto if the card can't be converted to text it is output as a raw record. There are two line printers, the second one is disabled by default. The LP supports the option "set lp# linesperpage=#" which determines when the printer will force out a page break. There are up to 16 mag tape drives, the format is controlled by the standard simh format control for tapes. These are 6 bit tapes, 1 character per record with parity. Units 8-16 are disabled by default. There are up to two drum units DR0 and DR1. These can either be attached to a file or set to AUXMEM. Setting to AUXMEM causes them to exist only during the given simh run. Setting back to DRUM will clear whatever was stored on the drum. To enable use of DRUM on XV the following options should be turned on "DRA,DRB,CODEOLAY,DATAOLAY". MCP will then use the drum as a overlay device instead of the disk system. Disks can be attached to the various ESU's, ESU0-9 are on DKA by default, ESU10-19 are on DKB. If "set dk1 dfx" is set, then ESU10-19 are not used and the disks are shared by both DKA and DKB. To use more then 10 ESU's in a non shared mode, a new version of MCP must be created. MCP must be compiled with DFX option set to false. For MCP XV DKBNODFX must also be set to true. ESU units can be set to MODI or MODIB. MODIB will double the size of the drive. The DTC can be attached to a telnet port with "attach dtc #" to enable dialup access to the sim. The loader card for the card reader is: ~raw0104441100204231524012004000004444550211002041317700000000000024045303040243 00050000006501004131011041310055005500000062005042310000006600304231000000720010 42310000007675610165001002310010413100040107202500440106202533554061256520252265 20251765202514655355536117650000004401062025155522610165225572610465044101160500 4131 This card should be all in one line.
795 lines
21 KiB
C
795 lines
21 KiB
C
/* B5500_io.c: Burroughs 5500 I/O System.
|
||
|
||
Copyright (c) 2016, Richard Cornwell
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a
|
||
copy of this software and associated documentation files (the "Software"),
|
||
to deal in the Software without restriction, including without limitation
|
||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
and/or sell copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
*/
|
||
|
||
#include "b5500_defs.h"
|
||
|
||
#define EOR 1
|
||
#define USEGM 2
|
||
|
||
t_stat chan_reset(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
|
||
*/
|
||
|
||
t_uint64 D[NUM_CHAN]; /* Current I/O instruction */
|
||
uint8 CC[NUM_CHAN]; /* Channel character count */
|
||
t_uint64 W[NUM_CHAN]; /* Assembly register */
|
||
uint8 status[NUM_CHAN]; /* Channel status */
|
||
uint8 cstatus; /* Active status */
|
||
|
||
#define WC(x) (uint16)(((x) & DEV_WC) >> DEV_WC_V)
|
||
#define toWC(x) (((t_uint64)(x) << DEV_WC_V) & DEV_WC)
|
||
|
||
|
||
UNIT chan_unit[] = {
|
||
/* Normal channels */
|
||
{UDATA(NULL,UNIT_DISABLE,0)},/* A */
|
||
{UDATA(NULL,UNIT_DISABLE,0)},/* B */
|
||
{UDATA(NULL,UNIT_DISABLE,0)},/* C */
|
||
{UDATA(NULL,UNIT_DISABLE,0)},/* D */
|
||
};
|
||
|
||
REG chan_reg[] = {
|
||
{BRDATA(D, D, 8, 48, NUM_CHAN), REG_RO},
|
||
{BRDATA(CC, CC, 7, 6, NUM_CHAN), REG_RO},
|
||
{BRDATA(W, W, 8, 48, NUM_CHAN), REG_RO},
|
||
{NULL}
|
||
};
|
||
|
||
|
||
/* Simulator debug controls */
|
||
DEBTAB chn_debug[] = {
|
||
{"CHANNEL", DEBUG_CHAN},
|
||
{"DETAIL", DEBUG_DETAIL},
|
||
{"DATA", DEBUG_DATA},
|
||
{"CH0", 0x0100 << 0},
|
||
{"CH1", 0x0100 << 1},
|
||
{"CH2", 0x0100 << 2},
|
||
{"CH3", 0x0100 << 3},
|
||
{0, 0}
|
||
};
|
||
|
||
DEVICE chan_dev = {
|
||
"IO", chan_unit, chan_reg, NULL,
|
||
NUM_CHAN, 10, 18, 1, 10, 44,
|
||
NULL, NULL, &chan_reset, NULL, NULL, NULL,
|
||
NULL, DEV_DEBUG, 0, chn_debug
|
||
};
|
||
|
||
|
||
|
||
t_stat
|
||
chan_reset(DEVICE * dptr)
|
||
{
|
||
int i;
|
||
int j = 1;
|
||
|
||
cstatus = 0;
|
||
/* Clear channel assignment */
|
||
for (i = 0; i < NUM_CHAN; i++) {
|
||
status[i] = 0;
|
||
D[i] = 0;
|
||
W[i] = 0;
|
||
CC[i] = 0;
|
||
if (chan_unit[i].flags & UNIT_DIS)
|
||
cstatus |= j;
|
||
j <<= 1;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Boot from given device */
|
||
t_stat
|
||
chan_boot(t_uint64 desc)
|
||
{
|
||
M[020] = desc;
|
||
M[010] = 020;
|
||
loading = 1;
|
||
start_io();
|
||
return SCPE_OK;
|
||
}
|
||
|
||
int
|
||
find_chan() {
|
||
int i;
|
||
int chan;
|
||
|
||
i = 1;
|
||
for(chan = 0; chan < NUM_CHAN; chan++) {
|
||
if ((cstatus & i) == 0)
|
||
break;
|
||
i <<= 1;
|
||
}
|
||
|
||
if (chan == NUM_CHAN) {
|
||
return 0;
|
||
}
|
||
return chan + 1;
|
||
}
|
||
|
||
void
|
||
chan_release(int chan) {
|
||
cstatus &= ~(1 << chan);
|
||
}
|
||
|
||
int
|
||
chan_advance(int chan) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
if (D[chan] & DEV_WCFLG) {
|
||
uint16 wc = WC(D[chan]);
|
||
if (wc == 0) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= toWC(wc-1);
|
||
}
|
||
if (addr > MEMSIZE) {
|
||
D[chan] |= DEV_MEMERR;
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
W[chan] = M[addr];
|
||
D[chan] &= ~CORE;
|
||
if (D[chan] & DEV_BACK)
|
||
D[chan] |= (t_uint64)((addr - 1) & CORE);
|
||
else
|
||
D[chan] |= (t_uint64)((addr + 1) & CORE);
|
||
CC[chan] = 0;
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
start_io() {
|
||
int i;
|
||
int chan;
|
||
t_stat r;
|
||
uint16 dev;
|
||
uint16 cmd;
|
||
uint16 wc;
|
||
int addr;
|
||
|
||
chan = find_chan();
|
||
|
||
addr = M[010] & CORE;
|
||
|
||
if (chan == 0) {
|
||
IAR |= IRQ_1;
|
||
return;
|
||
}
|
||
chan--;
|
||
i = 1 << chan;
|
||
sim_debug(DEBUG_DETAIL, &chan_dev, "strtio(%016llo %d)\n", M[addr], chan);
|
||
D[chan] = M[addr] & D_MASK;
|
||
CC[chan] = 0;
|
||
W[chan] = 0;
|
||
dev = (uint16)((D[chan] & DEVMASK) >> DEV_V);
|
||
cmd = (uint16)((D[chan] & DEV_CMD) >> DEV_CMD_V);
|
||
wc = WC(D[chan]);
|
||
D[chan] &= ~DEV_RESULT;
|
||
status[chan] = 0;
|
||
if (dev & 1) {
|
||
#if (NUM_DEVS_MT > 0)
|
||
status[chan] = USEGM;
|
||
r = mt_cmd(cmd, dev, chan, &wc);
|
||
#else
|
||
r = SCPE_UNATT;
|
||
#endif
|
||
} else {
|
||
switch(dev) {
|
||
#if (NUM_DEVS_DR > 0)
|
||
case DRUM1_DEV:
|
||
case DRUM2_DEV:
|
||
r = drm_cmd(cmd, dev, chan, &wc, (uint8)((M[addr] & PRESENT)!=0));
|
||
break;
|
||
#endif
|
||
|
||
#if (NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0)
|
||
case CARD1_DEV:
|
||
case CARD2_DEV:
|
||
r = card_cmd(cmd, dev, chan, &wc);
|
||
break;
|
||
#endif
|
||
|
||
#if (NUM_DEVS_DSK > 0)
|
||
case DSK1_DEV:
|
||
case DSK2_DEV:
|
||
/* Need to pass word count to identify interrogates */
|
||
r = dsk_cmd(cmd, dev, chan, &wc);
|
||
break;
|
||
#endif
|
||
|
||
#if (NUM_DEVS_DTC > 0)
|
||
case DTC_DEV:
|
||
status[chan] = USEGM;
|
||
/* Word count is TTU and BUF number */
|
||
r = dtc_cmd(cmd, dev, chan, &wc);
|
||
if (r == SCPE_OK)
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] &= ~(DEV_BIN|DEV_WCFLG);
|
||
wc = 0;
|
||
break;
|
||
#endif
|
||
|
||
#if (NUM_DEVS_LPR > 0)
|
||
case PRT1_DEV:
|
||
case PRT2_DEV:
|
||
r = lpr_cmd(cmd, dev, chan, &wc);
|
||
if (r == SCPE_OK)
|
||
D[chan] &= ~DEV_BACK; /* Clear this bit, since printer
|
||
uses this to determine 120/132
|
||
char line */
|
||
break;
|
||
#endif
|
||
|
||
#if (NUM_DEVS_CON > 0)
|
||
case SPO_DEV:
|
||
status[chan] = USEGM;
|
||
r = con_cmd(cmd, dev, chan, &wc);
|
||
break;
|
||
#endif
|
||
default:
|
||
r = SCPE_UNATT;
|
||
break;
|
||
}
|
||
}
|
||
if (wc != 0) {
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= toWC(wc) | DEV_WCFLG;
|
||
}
|
||
switch(r) {
|
||
case SCPE_OK:
|
||
cstatus |= i;
|
||
return;
|
||
case SCPE_NXDEV:
|
||
case SCPE_UNATT:
|
||
D[chan] |= DEV_NOTRDY;
|
||
break;
|
||
case SCPE_BUSY:
|
||
D[chan] |= DEV_BUSY;
|
||
break;
|
||
case SCPE_EOF:
|
||
D[chan] |= DEV_EOF;
|
||
break;
|
||
}
|
||
chan_set_end(chan);
|
||
}
|
||
|
||
|
||
void
|
||
chan_set_end(int chan) {
|
||
uint16 dev;
|
||
dev = (uint16)((D[chan] & DEVMASK) >> DEV_V);
|
||
/* Set character count if reading and tape */
|
||
if ((dev & 1) && (D[chan] & DEV_IORD)) {
|
||
D[chan] &= ~((7LL)<<DEV_WC_V);
|
||
/* If not data transfered, return zero code */
|
||
if ((D[chan] & DEV_BACK) != 0 &&
|
||
(status[chan] & EOR) != 0)
|
||
D[chan] |= ((t_uint64)((7-CC[chan]) & 07)) << DEV_WC_V;
|
||
else
|
||
D[chan] |= ((t_uint64)(CC[chan] & 07)) << DEV_WC_V;
|
||
}
|
||
|
||
/* Flush last buffer if short write */
|
||
if ((D[chan] & DEV_IORD) && CC[chan] != 0) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
if ((D[chan] & (DEV_BIN|DEV_WCFLG)) == 0) {
|
||
/* Insert group mark */
|
||
if (D[chan] & DEV_BACK) {
|
||
W[chan] |= (t_uint64)037LL << ((CC[chan]) * 6);
|
||
CC[chan]++;
|
||
} else {
|
||
while(CC[chan] != 8) {
|
||
W[chan] |= 037LL << ((7 - CC[chan]) * 6);
|
||
CC[chan]++;
|
||
}
|
||
}
|
||
}
|
||
|
||
M[addr] = W[chan];
|
||
sim_debug(DEBUG_DATA, &chan_dev, "write(%d, %05o, %016llo)f\n",
|
||
chan, addr, W[chan]);
|
||
(void)chan_advance(chan);
|
||
}
|
||
M[014+chan] = D[chan];
|
||
if (loading == 0)
|
||
IAR |= IRQ_5 << chan;
|
||
else
|
||
loading = 0;
|
||
sim_debug(DEBUG_DETAIL, &chan_dev, "endio (%016llo %o)\n", D[chan], chan);
|
||
}
|
||
|
||
void
|
||
chan_set_eof(int chan) {
|
||
D[chan] |= DEV_EOF;
|
||
}
|
||
|
||
void
|
||
chan_set_parity(int chan) {
|
||
D[chan] |= DEV_PARITY;
|
||
}
|
||
|
||
void
|
||
chan_set_error(int chan) {
|
||
D[chan] |= DEV_ERROR;
|
||
}
|
||
|
||
void
|
||
chan_set_wcflg(int chan) {
|
||
D[chan] |= DEV_WCFLG;
|
||
}
|
||
|
||
void
|
||
chan_set_read(int chan) {
|
||
D[chan] |= DEV_IORD;
|
||
}
|
||
|
||
void
|
||
chan_set_gm(int chan) {
|
||
D[chan] |= DEV_BACK;
|
||
}
|
||
|
||
void
|
||
chan_set_notrdy(int chan) {
|
||
D[chan] |= DEV_NOTRDY;
|
||
chan_set_end(chan);
|
||
}
|
||
|
||
void
|
||
chan_set_eot(int chan) {
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= DEV_EOT;
|
||
}
|
||
|
||
void
|
||
chan_set_bot(int chan) {
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= DEV_BOT;
|
||
}
|
||
|
||
void
|
||
chan_set_blank(int chan) {
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= DEV_BLANK;
|
||
}
|
||
|
||
void
|
||
chan_set_wrp(int chan) {
|
||
D[chan] |= DEV_ERROR|DEV_MEMERR;
|
||
}
|
||
|
||
void
|
||
chan_set_wc(int chan, uint16 wc) {
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= toWC(wc);
|
||
}
|
||
|
||
|
||
/*
|
||
Internal BCD
|
||
00 0000 00 1010 0000 -> 1010
|
||
00 0001 00 0001
|
||
00 0010 00 0010
|
||
00 0011 00 0011
|
||
00 0100 00 0100
|
||
00 0101 00 0101
|
||
00 0110 00 0110
|
||
00 0111 00 0111
|
||
00 1000 00 1000
|
||
00 1001 00 1001
|
||
00 1010 00 1011 1010 -> 1011
|
||
00 1011 00 1100 1011 -> 1100
|
||
00 1100 00 0000 1100 -> 0000
|
||
00 1101 00 1101
|
||
00 1110 00 1110
|
||
00 1111 00 1111
|
||
01 0000 11 1010 0000 -> 1010 10
|
||
01 0001 11 0001 1
|
||
01 0010 11 0010 2
|
||
01 0011 11 0011 3
|
||
01 0100 11 0100
|
||
01 0101 11 0101
|
||
01 0110 11 0110
|
||
01 0111 11 0111
|
||
01 1000 11 1000 8
|
||
01 1001 11 1001 9
|
||
01 1010 11 1011 1010 -> 1011 10
|
||
01 1011 11 1100 1011 -> 1100 11
|
||
01 1100 11 0000 1100 -> 0000 12
|
||
01 1101 11 1101 13
|
||
01 1110 11 1110 14
|
||
01 1111 11 1111 15
|
||
10 0000 10 1010 0000 -> 1010
|
||
10 0001 10 0001
|
||
10 0010 10 0010
|
||
10 0011 10 0011
|
||
10 0100 10 0100
|
||
10 0101 10 0101
|
||
10 0110 10 0110
|
||
10 0111 10 0111
|
||
10 1000 10 1000
|
||
10 1001 10 1001
|
||
10 1010 10 1011 1010 -> 1011
|
||
10 1011 10 1100 1011 -> 1100
|
||
10 1100 10 0000 1100 -> 0000
|
||
10 1101 10 1101
|
||
10 1110 10 1110
|
||
10 1111 10 1111
|
||
11 0000 01 0000
|
||
11 0001 01 0001
|
||
11 0010 01 0010
|
||
11 0011 01 0011
|
||
11 0100 01 0100
|
||
11 0101 01 0101
|
||
11 0110 01 0110
|
||
11 0111 01 0111
|
||
11 1000 01 1000
|
||
11 1001 01 1001
|
||
11 1010 01 1011 1010 -> 1011
|
||
11 1011 01 1100 1011 -> 1100
|
||
11 1100 01 1010 1100 -> 1010
|
||
11 1101 01 1101
|
||
11 1110 01 1110
|
||
11 1111 01 1111 */
|
||
|
||
/* returns 1 when channel can take no more characters.
|
||
A return of 1 indicates that the character was not
|
||
processed.
|
||
*/
|
||
int chan_write_char(int chan, uint8 *ch, int flags) {
|
||
uint8 c;
|
||
|
||
if (status[chan] & EOR)
|
||
return 1;
|
||
|
||
if (D[chan] & DEV_INHTRF) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
|
||
/* Check if first word */
|
||
if (CC[chan] == 0) {
|
||
uint16 wc = WC(D[chan]);
|
||
|
||
if (D[chan] & DEV_WCFLG && wc == 0) {
|
||
sim_debug(DEBUG_DATA, &chan_dev, "zerowc(%d)\n", chan);
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
W[chan] = 0;
|
||
}
|
||
|
||
c = *ch & 077;
|
||
if ((D[chan] & DEV_BIN) == 0) {
|
||
/* Translate BCL to BCD */
|
||
uint8 cx = c & 060;
|
||
|
||
c &= 017;
|
||
switch(c) {
|
||
case 0:
|
||
/* 11-0 -> 01 C */
|
||
/* 10-0 -> 10 C */
|
||
/* 01-0 -> 11 0 */
|
||
/* 00-0 -> 00 C */
|
||
if (cx != 020)
|
||
c = 0xc;
|
||
break;
|
||
case 1:
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
case 5:
|
||
case 6:
|
||
case 7:
|
||
case 8:
|
||
case 9:
|
||
case 0xd:
|
||
case 0xe:
|
||
case 0xf:
|
||
break;
|
||
case 0xa:
|
||
/* 11-A -> 01 0 */
|
||
/* 10-A -> 10 0 */
|
||
/* 01-A -> 11 C */
|
||
/* 00-A -> 00 0 */
|
||
if (cx == 020)
|
||
c = 0xc;
|
||
else
|
||
c = 0;
|
||
break;
|
||
case 0xb:
|
||
c = 0xa;
|
||
break;
|
||
case 0xc:
|
||
c = 0xb;
|
||
break;
|
||
}
|
||
c |= cx ^ ((cx & 020)<<1);
|
||
}
|
||
|
||
if (D[chan] & DEV_BACK)
|
||
W[chan] |= ((t_uint64)c) << ((CC[chan]) * 6);
|
||
else
|
||
W[chan] |= ((t_uint64)c) << ((7 - CC[chan]) * 6);
|
||
CC[chan]++;
|
||
|
||
if (CC[chan] == 8) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
M[addr] = W[chan];
|
||
sim_debug(DEBUG_DATA, &chan_dev, "write(%d, %05o, %016llo)\n",
|
||
chan, addr, W[chan]);
|
||
|
||
if (chan_advance(chan))
|
||
return 1;
|
||
}
|
||
if (flags) {
|
||
if ((D[chan] & (DEV_BIN|DEV_WCFLG)) == 0) {
|
||
/* Insert group mark */
|
||
if (D[chan] & DEV_BACK) {
|
||
int i = CC[chan];
|
||
W[chan] |= (t_uint64)037LL << ((CC[chan]) * 6);
|
||
|
||
while(i < 8) {
|
||
W[chan] |= (t_uint64)014LL << (i * 6);
|
||
i++;
|
||
}
|
||
} else {
|
||
W[chan] |= 037LL << ((7 - CC[chan]) * 6);
|
||
}
|
||
CC[chan]++;
|
||
}
|
||
|
||
/* Flush last word */
|
||
if (CC[chan] != 0) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
M[addr] = W[chan];
|
||
sim_debug(DEBUG_DATA, &chan_dev, "write(%d, %05o, %016llo)\n",
|
||
chan, addr, W[chan]);
|
||
(void)chan_advance(chan);
|
||
}
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Returns 1 on last character. If it returns 1, the
|
||
character in ch is not valid. If flag is set to 1, then
|
||
this is the last character the device will request.
|
||
*/
|
||
int chan_read_char(int chan, uint8 *ch, int flags) {
|
||
uint8 c;
|
||
int gm;
|
||
|
||
if (status[chan] & EOR)
|
||
return 1;
|
||
|
||
if (D[chan] & DEV_INHTRF) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
|
||
if (CC[chan] == 0) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
if (chan_advance(chan))
|
||
return 1;
|
||
sim_debug(DEBUG_DATA, &chan_dev, "read(%d, %05o, %016llo)\n", chan,
|
||
addr, W[chan]);
|
||
}
|
||
|
||
if (D[chan] & DEV_BACK)
|
||
c = 077 & (W[chan] >> ((CC[chan]) * 6));
|
||
else
|
||
c = 077 & (W[chan] >> ((7 - CC[chan]) * 6));
|
||
gm = (c == 037);
|
||
CC[chan]++;
|
||
if (CC[chan] == 8) {
|
||
CC[chan] = 0;
|
||
}
|
||
if ((D[chan] & DEV_BIN) == 0) {
|
||
/* Translate BCD to BCL */
|
||
uint8 cx = c & 060;
|
||
c &= 017;
|
||
switch(c) {
|
||
case 0:
|
||
if (cx != 060)
|
||
c = 0xa;
|
||
break;
|
||
case 1:
|
||
case 2:
|
||
case 3:
|
||
case 4:
|
||
case 5:
|
||
case 6:
|
||
case 7:
|
||
case 8:
|
||
case 9:
|
||
case 0xd:
|
||
case 0xe:
|
||
case 0xf:
|
||
break;
|
||
case 0xa:
|
||
c = 0xb;
|
||
break;
|
||
case 0xb:
|
||
c = 0xc;
|
||
break;
|
||
case 0xc:
|
||
if (cx == 060)
|
||
c = 0xa;
|
||
else
|
||
c = 0;
|
||
break;
|
||
}
|
||
c |= cx ^ ((cx & 020)<<1);
|
||
}
|
||
*ch = c;
|
||
if ((status[chan] & USEGM) != 0 && (D[chan] & DEV_WCFLG) == 0 && gm) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
if (flags) {
|
||
status[chan] |= EOR;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Same as chan_read_char, however we do not check word count
|
||
nor do we advance it.
|
||
*/
|
||
int chan_read_disk(int chan, uint8 *ch, int flags) {
|
||
uint8 c;
|
||
|
||
if (CC[chan] == 0) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
if (addr > MEMSIZE) {
|
||
D[chan] |= DEV_MEMERR;
|
||
return 1;
|
||
}
|
||
|
||
W[chan] = M[addr];
|
||
D[chan] &= ~CORE;
|
||
D[chan] |= (addr + 1) & CORE;
|
||
}
|
||
|
||
c = 077 & (W[chan] >> ((7 - CC[chan]) * 6));
|
||
CC[chan]++;
|
||
*ch = c;
|
||
if (CC[chan] == 8) {
|
||
CC[chan] = 0;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
chan_advance_drum(int chan) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
uint16 wc = WC(D[chan]);
|
||
if (wc == 0) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
D[chan] &= ~DEV_WC;
|
||
D[chan] |= toWC(wc-1);
|
||
if (addr > MEMSIZE) {
|
||
D[chan] |= DEV_MEMERR;
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
|
||
W[chan] = M[addr];
|
||
D[chan] &= ~CORE;
|
||
D[chan] |= (addr + 1) & CORE;
|
||
CC[chan] = 0;
|
||
return 0;
|
||
}
|
||
|
||
/* returns 1 when channel can take no more characters.
|
||
A return of 1 indicates that the character was not
|
||
processed.
|
||
*/
|
||
int chan_write_drum(int chan, uint8 *ch, int flags) {
|
||
uint8 c;
|
||
|
||
if (status[chan] & EOR)
|
||
return 1;
|
||
|
||
/* Check if first word */
|
||
if (CC[chan] == 0) {
|
||
uint16 wc = WC(D[chan]);
|
||
|
||
if (wc == 0) {
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
W[chan] = 0;
|
||
}
|
||
|
||
c = *ch;
|
||
c &= 077;
|
||
|
||
W[chan] |= (t_uint64)c << ((7 - CC[chan]) * 6);
|
||
CC[chan]++;
|
||
|
||
if (CC[chan] == 8) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
M[addr] = W[chan];
|
||
if (chan_advance_drum(chan))
|
||
return 1;
|
||
}
|
||
if (flags) {
|
||
/* Flush last word */
|
||
if (CC[chan] != 0) {
|
||
uint16 addr = (uint16)(D[chan] & CORE);
|
||
|
||
M[addr] = W[chan];
|
||
(void)chan_advance_drum(chan);
|
||
}
|
||
status[chan] |= EOR;
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Returns 1 on last character. If it returns 1, the
|
||
character in ch is not valid. If flag is set to 1, then
|
||
this is the last character the device will request.
|
||
*/
|
||
int chan_read_drum(int chan, uint8 *ch, int flags) {
|
||
uint8 c;
|
||
|
||
if (status[chan] & EOR)
|
||
return 1;
|
||
|
||
|
||
if (CC[chan] == 0) {
|
||
if (chan_advance_drum(chan))
|
||
return 1;
|
||
}
|
||
|
||
c = 077 & (W[chan] >> ((7 - CC[chan]) * 6));
|
||
CC[chan]++;
|
||
if (CC[chan] == 8) {
|
||
CC[chan] = 0;
|
||
}
|
||
*ch = c;
|
||
if (flags) {
|
||
status[chan] |= EOR;
|
||
}
|
||
return 0;
|
||
}
|
||
|