| /* 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; | |
| } | |