/* i650_cdr.c: IBM 650 Card reader. Copyright (c) 2018, Roberto Sancho 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 ROBERTO SANCHO 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 card reader. 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 BCD characters. */ #include "i650_defs.h" #include "sim_card.h" #define UNIT_CDR UNIT_ATTABLE | UNIT_RO | MODE_026 | MODE_LOWER /* std devices. data structures cdr_dev Card Reader device descriptor cdr_unit Card Reader unit descriptor cdr_reg Card Reader register list cdr_mod Card Reader modifiers list */ uint32 cdr_cmd(UNIT *, uint16, uint16); t_stat cdr_srv(UNIT *); t_stat cdr_reset(DEVICE *); t_stat cdr_attach(UNIT *, CONST char *); t_stat cdr_detach(UNIT *); t_stat cdr_help(FILE *, DEVICE *, UNIT *, int32, const char *); const char *cdr_description(DEVICE *dptr); t_stat cdr_set_wiring (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat cdr_show_wiring (FILE *st, UNIT *uptr, int32 val, CONST void *desc); UNIT cdr_unit[4] = { {UDATA(cdr_srv, UNIT_CDR, 0), 300}, // Unit 0 used internally for carddeck operations simulator specific command {UDATA(cdr_srv, UNIT_CDR, 0), 300}, // unit 1 is default for initial model (1954) {UDATA(cdr_srv, UNIT_CDR, 0), 300}, // storage unit (1955) allows two extra card/readers for a total of 3 {UDATA(cdr_srv, UNIT_CDR, 0), 300}, }; MTAB cdr_mod[] = { {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_card_set_fmt, &sim_card_show_fmt, NULL, "Set card format"}, {MTAB_XTD | MTAB_VUN, 0, "WIRING", "WIRING", &cdr_set_wiring, &cdr_show_wiring, NULL, "Set card read control panel Wiring"}, {0} }; DEVICE cdr_dev = { "CDR", cdr_unit, NULL, cdr_mod, 4, 8, 15, 1, 8, 8, NULL, NULL, NULL, NULL, &cdr_attach, &sim_card_detach, &cdr_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, NULL, NULL, &cdr_help, NULL, NULL, &cdr_description }; // buffer to hold read cards in take hopper of each unit // to be printed by carddeck command char ReadHopper[3 * MAX_CARDS_IN_READ_TAKE_HOPPER * 80]; int ReadHopperLast[3]; // get 10 digits word with sign from card buf (the data struct). return 1 if HiPunch set on any digit int decode_8word_wiring(struct _card_data * data, int bCheckForHiPunch) { // decode up to 8 numerical words per card // input card // NNNNNNNNNN ... 8 times // If last digit of word has X(11) punch whole word is set as negative value // If N is non numeric, a 0 is assumed // put the decoded data in IO Sync buffer (if bCheckForHiPunch = 1 -> do not store in IO Sync Buffer) // return 1 if any colum has Y(12) hi-punch set uint16 c1,c2; int wn,iCol,iDigit; int HiPunch, NegPunch, NegZero; t_int64 d; NegZero = 0; // flag set if negative zero is read HiPunch = 0; // set to 1 if Y(12) high punch found iCol = 0; // current read colum in card for (wn=0;wn<8;wn++) { // one card generates 8 words in drum mem d = NegPunch = 0; // read word digits for (iDigit=0;iDigit<10;iDigit++) { c1 = data->image[iCol++]; c2 = data->hol_to_ascii[c1]; // convert to ascii if ((c1 == 0xA00) || (c2 == '?')) { c1 = 0xA00; c2 = '?'; // the punched value +0 should be represented by ascii ? } if ((c2 == '+') && (iCol == 1)) { // on IT control card, first char is a Y(12) punch to make control card a load card. c1 = 0xA00; c2 = '?'; // Digit interpreted as +0 } if (strchr(digits_ascii, c2) == NULL) { // scan digits ascii to check if this is a valid numeric digit with Y or X punch c1 = 0; // nondigits chars interpreted as blank } if (c1 & 0x800) HiPunch = 1; // if column has Hi Punch Y(12) set, signal it NegPunch = (c1 & 0x400) ? 1:0; // if column has minus X(11) set, signal it c1 = c1 & 0x3FF; // remove X and Y punches c2 = data->hol_to_ascii[c1]; // convert to ascii again c2 = c2 - '0'; // convert ascii to binary digit if (c2 > 9) c2 = 0; // nondigits chars interpreted as zero d = d * 10 + c2; } // end of word. set sign if (NegPunch) { // has last digit a minus X(11) punch set? d = -d; // yes, change sign of word read if (d == 0) NegZero=1; // word read is minus zero } if (bCheckForHiPunch == 0) { IOSync [wn]=d; IOSync_NegativeZeroFlag[wn]=NegZero; } } return HiPunch; } // load soap symbolic info, This is a facility to help debugging of soap programs into SimH // does not exist in real hw void decode_soap_symb_info(struct _card_data * data) { t_int64 d; int op,da,ia,i,i2,p; char buf[81]; uint16 c1,c2; // check soap 1-word load card initial word d = IOSync[0]; if (d != 6919541953LL) return; // not a 1-word load card // get the address where the 1-word card will be loaded (into da) d = IOSync[2]; op = Shift_Digits(&d, 2); // current inst opcode da = Shift_Digits(&d, 4); // addr of data ia = Shift_Digits(&d, 4); // addr of next instr if ((op != 24) && (ia != 8000)) return; // not a 1-word load card if (da >= (int)DRUMSIZE) return; // symbolic info can only be associated to drum addrs // convert card image punches to ascii buf for processing, starting at col 40 // keep 026 fortran charset for (i=40;i<80;i++) { c1 = data->image[i]; c2 = data->hol_to_ascii[c1]; c2 = (strchr(mem_to_ascii, toupper(c2))) ? c2:' '; if (c2 == '~') c2 = ' '; buf[i] = (char) c2; } buf[80] = 0; // terminate string // copy soap symbolic info i2 = 80; while (1) { // calc i2 = last non space char to copy if (--i2 < 41) return; // noting to copy if (buf[i2] > 32) break; } p = da * 80; for (i=0;i<80;i++) DRUM_Symbolic_Buffer[p+i] = 0; // clear drum[da] symbolic info for (i=41;i<=i2;i++) { if ((i==47) || (i==50) || (i==55)) DRUM_Symbolic_Buffer[p++] = 32; // add space separation between op, da, ia fields DRUM_Symbolic_Buffer[p++] = buf[i]; } } t_int64 decode_num_word(char * buf, int nDigits, int bSpaceIsZero) { t_int64 d; int i,c; d = 0; for (i=0;i '9')) { d = -1; // not a number break; } d = d * 10 + c - '0'; } if (d < 0) { // not a number -> return all 9's d = 0; for (i=0;i | Alphabetic // 1952: | <- Data Addr -> | Alphabetic // 1953: | <- Inst Addr -> | Alphabetic // +-+-+-|-+-+-|-+-|-+-| // 1954: | Op Code |DTg|ITg| Alphabetic // +-+-+-|-+-+-|-+-|-+-| // 1955: | <- Remarks -> | Alphabetic // 1956: | <- Remarks -> | Alphabetic // +-+-+-+-+-+-|-+-+-+-| // 1957: | |N N N N| L Absolute Part // 1958: | |N N N N| D Absolute Part // 1959: | |N N N N| I Absolute Part // 1960: | |T b n| T=Type (0 if Blank), b=0/8 (for non blank type), n=0/8 (for negative) // +-------------------+ // int ty,neg; char buf[81]; int i; uint16 c1,c2; // convert card image punches to ascii buf for processing // keep 026 fortran charset for (i=0;i<80;i++) { c1 = data->image[i]; c2 = data->hol_to_ascii[c1]; c2 = (strchr(mem_to_ascii, toupper(c2))) ? c2:' '; if (c2 == '~') c2 = ' '; buf[i] = (char) c2; } buf[80] = 0; // terminate string IOSync[0] = decode_alpha_word(&buf[42], 5); // Location (5 chars) IOSync[1] = decode_alpha_word(&buf[50], 5); // Data Addr (5 chars) IOSync[2] = decode_alpha_word(&buf[56], 5); // Inst Addr (5 chars) IOSync[3] = decode_alpha_word(&buf[47], 3) * D4 + // OpCode (3 chars only) decode_alpha_word(&buf[55], 1) * 100 + // Data Addr Tag (1 char only) decode_alpha_word(&buf[61], 1); // Instr Addr Tag (1 char only) IOSync[4] = decode_alpha_word(&buf[62], 5); // Remarks IOSync[5] = decode_alpha_word(&buf[67], 5); // Remarks IOSync[6] = decode_num_word(&buf[43], 4, 0); // Absolute Part of location IOSync[7] = decode_num_word(&buf[51], 4, 0); // Absolute Part of Data Addr IOSync[8] = decode_num_word(&buf[57], 4, 0); // Absolute Part of Instr Addr ty = buf[40] - '0'; if ((ty < 0) || (ty > 9)) ty = 0; neg = (buf[41] == '-') ? 8:0; IOSync[9] = ty * 100 + (ty ? 80:0) + neg; // |T b n| T=Type (0 if Blank), b=0/8 (for non blank type), n=0/8 (for negative) } int sformat(char * buf, const char * match) { char m,c; while(1) { m = *match++; if (m == 0) break; c = *buf++; if (c == 0) return 0; // end of buf str before end of match string -> return 0 -> buf does not match if ((m == ' ') && (c == ' ')) continue; if ((m == 'N') && (c >= '0') && (c <= '9')) continue; if ((m == '+') && ((c == '+') || (c == '-'))) continue; return 0; // buf does not match -> return 0 -> buf does not match } return 1; // end of match string -> return 1 -> buf matches } void decode_is_wiring(struct _card_data * data) { // decode Floationg Decimal Interpretive System (IS) card simulating control panel wiring for 533 as described // in manual at http://www.bitsavers.org/pdf/ibm/650/28-4024_FltDecIntrpSys.pdf // input card // Column: 1 2 3 4 | 5 6 | 7 8 9 | 10 | 11 | 12 - 21 | 22 | 23 - 32 | 33 | 34 - 43 | 44 | 45 - 54 | 55 | 56 - 65 | 66 | 67 - 76 | 77 78 79 | 80 // Card | | Location | wc | s1 | Word1 | s2 | Word2 | s3 | Word3 | s4 | Word4 | s5 | Word5 | s6 | Word6 | Problem | // Num | Num // // wc = Word Count (range 0 to 6, space for 1) // s1 = sign of word 1 (-, + or (same as +)) // Tr = Tracing identification // Word = word in format NNNNNNNNNN // N is 0..9, (same as 0) // // Alternate input format to allow system deck loading // Column: 1 2 | 3 | 4 5 6 | 7 | 8 9 10 11 | 12 | 13 - 24 // Deck | sp | Card | | NNNN | | NN NNNN NNNN // Num | | Num | // // Alternate input format to allow IT source program loading // Column: 1 2 3 4 | 5 6 | 7 8 9 | 10 | 11 | 12 - 24 // Card | Blank | Location | | sg | N NNN NNN NNN <- This is an IS instruction (format O1 A B C) // Num | // Column: 1 2 3 4 | 5 6 | 7 8 9 | 10 | 11 | 12 - 23 // Card | Blank | Location | | sg | N NNNNNNN NN <- This is an IS float numeric constant (mantissa and exponent) // Num | // Column: 1 2 3 4 | 5 6 | 7 8 9 | 10 - 23 // Card | Blank | Location | blanks <- This is an IS transfer card (location is start of IT program) // Num | // // storage in input block // +-+-+-+-+-+-|-+-+-+-| // Word 1951: | |N N N N| | Location // 1952: | |N N N N| | Word Count // +-------------------+ // 1953: | word1 | // 1954: | word2 | // 1955: | word3 | // 1956: | word4 | // 1957: | word5 | // 1958: | word6 | // +-------------------+ // 1959: | Problem Number | // +-------------------+ // // card number is ignored on reading int wc,neg,i; int NegZero; t_int64 d; char buf[81]; uint16 c1,c2; // convert card image punches to ascii buf for processing // keep 0..9,+,-,, replace anything else by for (i=0;i<80;i++) { c1 = data->image[i]; c2 = data->hol_to_ascii[c1]; buf[i] = (strchr("+-0123456789", c2)) ? ((char) (c2)):' '; } buf[80] = 0; // terminate string if ( sformat(&buf[6], " ")) { // card with firsts 26 cols blank = blank card: read as all zero, one word count // this allows to have blank cards/comments card as long as the comment starts on column 27 of more IOSync[1] = 1 * D4; // word count } else if ( sformat(&buf[5], " NNN ")) { // alternate format for loading IT program (IT transfer card) IOSync[0] = decode_num_word(&buf[6], 3, 0) * D4; // start location (3 digits) IOSync[1] = 0; // word count = 0 } else if ( sformat(&buf[5], " NNN +N NNN NNN NNN ")) { // alternate format for loading IT program (IT instruction) IOSync[0] = decode_num_word(&buf[6], 3, 0) * D4; // location (3 digits) IOSync[1] = 1 * D4; // word count NegZero = 0; neg = (buf[10] == '-') ? 1:0; d = decode_num_word(&buf[11], 1, 0) * 10 * D8 + // O1 decode_num_word(&buf[13], 3, 0) * 100 * D4 + // O2 or A decode_num_word(&buf[17], 3, 0) * 1000 + // B decode_num_word(&buf[21], 3, 0); // C if (neg) { d=-d; if (d==0) NegZero = 1; } IOSync [2]=d; IOSync_NegativeZeroFlag[2]=NegZero; } else if ( sformat(&buf[5], " NNN +N NNNNNNN NN ")) { // alternate format for loading IT program (numeric constant in float format) IOSync[0] = decode_num_word(&buf[6], 3, 0) * D4; // location (3 digits) IOSync[1] = 1 * D4; // word count NegZero = 0; neg = (buf[10] == '-') ? 1:0; d = decode_num_word(&buf[11], 1, 0) * 10 * D8 + // integer part of mantissa decode_num_word(&buf[13], 7, 0) * 100 + // factional part of mantissa decode_num_word(&buf[21], 2, 0); // exponent if (neg) { d=-d; if (d==0) NegZero = 1; } IOSync [2]=d; IOSync_NegativeZeroFlag[2]=NegZero; } else if ( (sformat(&buf[6], " NNNN NN NNNN NNNN ")) || (sformat(&buf[6], " NNNN NN NNNN ")) || (sformat(&buf[6], " NNNN NN NNNN ")) || (sformat(&buf[6], " NNNN NN ")) ) { // alternate format for loading main IT system deck IOSync[0] = decode_num_word(&buf[7], 4, 0) * D4; // location (4 digits) IOSync[1] = 1 * D4; // word count = 1 IOSync[2] = decode_num_word(&buf[12], 2, 1) * D8 + // op decode_num_word(&buf[15], 4, 1) * D4 + // data address decode_num_word(&buf[20], 4, 1); // instr addr, no negative zero allowed } else { // regular IT read/punch format IOSync[0] = decode_num_word(&buf[6], 3, 0) * D4; // location (3 digits) wc = (int) decode_num_word(&buf[9], 1, 1); if (wc > 6) wc = 6; IOSync[1] = wc * D4; // word count for (i=0;i | Alphabetic // 0052: | <- Statement -> | Alphabetic // 0053: | <- Statement -> | Alphabetic // 0054: | <- Statement -> | Alphabetic // 0055: | <- Statement -> | Alphabetic // 0056: | <- Statement -> | Alphabetic // +-+-+-+-+-+-|-+-+-+-| // 0057: | |N N N N| Statement Number // +-+-+-+-+-+-|-+-+-+-| // 0058: | | Not used // 0059: | | Not used // 0060: | | Not used // +-------------------+ // // type 1 data input card // Column: 1 2 | 3 | 4 5 6 | 7 8 9 10 | 11 - 20 | // VV | + | N N N | D D D D | Word // | Y(12) | // | Punch | // VV = IT variable being loaded: 01 -> I type, 02 -> Y type, 03 -> C type // N N N = variable number (I5 -> 01 + 005) // D D D D = variable arbitrary non-zero identification number // Word = word to be loaded into IT variable. If type I, is an integer. If type C or Y // type is word is float (M MMMMMMM EE -> M=mantisa, EE=exponent) // if word is negative, last digit get X(11) overpunch // up to 4 pairs var-word per card // last card signaed with a X(11) overpunch in col 10 // space is considered as zero // type 2 data input card is a load card. No spaces are allowed char buf[81]; int i; uint16 c1,c2; // convert card image punches to ascii buf for processing // keep 026 fortran charset for (i=0;i<80;i++) { c1 = data->image[i]; c2 = data->hol_to_ascii[c1]; c2 = (strchr(mem_to_ascii, toupper(c2))) ? c2:' '; if (c2 == '~') c2 = ' '; buf[i] = (char) c2; } buf[80] = 0; // terminate string if (buf[2] == '+') { // type 1 data card // re-read as 8 word per card decode_8word_wiring(data, 0); return; } IOSync[0] = decode_alpha_word(&buf[42], 5); // Statement (5 chars) IOSync[1] = decode_alpha_word(&buf[47], 5); // Statement (5 chars) IOSync[2] = decode_alpha_word(&buf[52], 5); // Statement (5 chars) IOSync[3] = decode_alpha_word(&buf[57], 5); // Statement (5 chars) IOSync[4] = decode_alpha_word(&buf[62], 5); // Statement (5 chars) IOSync[5] = decode_alpha_word(&buf[67], 3); // Statement (3 chars) IOSync[6] = decode_num_word(&buf[0], 4, 1); // Statement Number (space is read as digit zero) } void decode_fortransit_wiring(struct _card_data * data) { // decode FORTRANSIT translator card simulating control panel wiring for 533 // from FORTRANSIT manual at http://bitsavers.org/pdf/ibm/650/28-4028_FOR_TRANSIT.pdf // implemented Fortransit II (S) // fortran source program input card // Column: 1 | 2 3 4 5 | 6 | 7 - 36 | 37 - 80 | // C | N N N N | cont | Statement | Blank | // // C = Blank or Comment if C is present // NNNN = Blank or statement number // cont = Blank or non-blank/non-zero for continuation card // // storage in input block // +-------------------+ // Word 1951: | <- Statement -> | Alphabetic // 1952: | <- Statement -> | Alphabetic // 1953: | <- Statement -> | Alphabetic // 1954: | <- Statement -> | Alphabetic // 1955: | <- Statement -> | Alphabetic // 1956: | <- Statement -> | Alphabetic // +-------------------+ // 1957: | | Not used // 1958: | | Not used // 1959: | | Not used // +-+-+-------+-------+ // 1960: |m t| |N N N N| m = 8/0 (8 -> comment card) // +---+-------+-------+ t = 8/0 (8 -> continuatin card) // NNNN = statement sumber // // it source program input card // Column: 1 2 3 4 | 5 | 6 - 42 | 43 - 70 | 71 72 | 73 - 80 | // N N N N | + | | Statement | | Comments | // Statement | Y(12) | | max 28 | | max 8 | // Number | Punch | | chars | | chars | // // storage in input block // +-------------------+ // Word 0051: | <- Statement -> | Alphabetic // 0052: | <- Statement -> | Alphabetic // 0053: | <- Statement -> | Alphabetic // 0054: | <- Statement -> | Alphabetic // 0055: | <- Statement -> | Alphabetic // 0056: | <- Statement -> | Alphabetic // +-+-+-+-+-+-|-+-+-+-| // 0057: | |N N N N| Statement Number // +-+-+-+-+-+-|-+-+-+-| // 0058: | | Not used // 0059: | | Not used // 0060: | | Not used // +-------------------+ // // fortransit input data card // Column: 1 - 10 | 11 - 20 | 21 - 30 | 31 - 40 | 41 - 50 | 51 - 60 | 61 - 70 | 71 72 | 73 | 74 - 80 | // Word1 | Word2 | Word3 | Word4 | Word5 | Word6 | Word7 | | + | // | Y(12) | // Word = word to be loaded into FORTRANSITIT variable. Must match the variable type where it is read in // float (MMMMMMMM EE -> M=mantisa, EE=exponent, 1000000051 is 1.0) // fixed (NNNNNNNNNN -> 000000030J is -302) // if word is negative, last digit get X(11) overpunch // If last digit of word has X(11) punch whole word is set as negative value // If N is non numeric, a 0 is assumed // // storage in input block // +-------------------+ // Word 1951: | <- Word1 -> | // 1952: | <- Word2 -> | // 1953: | <- Word3 -> | // 1954: | <- Word4 -> | // 1955: | <- Word5 -> | // 1956: | <- Word6 -> | // 1957: | <- Word7 -> | // +-------------------+ // 1958: | | Not used // 1959: | | Not used // 1960: | | Not used // +-------------------+ // char buf[81]; int i; uint16 c1,c2; // convert card image punches to ascii buf for processing // keep 026 fortran charset for (i=0;i<80;i++) { c1 = data->image[i]; c2 = data->hol_to_ascii[c1]; c2 = toupper(c2); c2 = (strchr(mem_to_ascii, c2)) ? c2:' '; if (c2 == '~') c2 = ' '; buf[i] = (char) c2; } buf[80] = 0; // terminate string if (buf[72] == '+') { // read data card input for READ fortransit command // re-read as 8 word per card decode_8word_wiring(data, 0); return; } else if (buf[4] == '+') { // it source statement IOSync[0] = decode_alpha_word(&buf[42], 5); // Statement (5 chars) IOSync[1] = decode_alpha_word(&buf[47], 5); // Statement (5 chars) IOSync[2] = decode_alpha_word(&buf[52], 5); // Statement (5 chars) IOSync[3] = decode_alpha_word(&buf[57], 5); // Statement (5 chars) IOSync[4] = decode_alpha_word(&buf[62], 5); // Statement (5 chars) IOSync[5] = decode_alpha_word(&buf[67], 5); // Statement (5 chars) IOSync[6] = decode_num_word(&buf[0], 4, 1); // Statement Number (space is read as digit zero) } else { // fortran source statement IOSync[0] = decode_alpha_word(&buf[6], 5); // Statement (5 chars) IOSync[1] = decode_alpha_word(&buf[11], 5); // Statement (5 chars) IOSync[2] = decode_alpha_word(&buf[16], 5); // Statement (5 chars) IOSync[3] = decode_alpha_word(&buf[21], 5); // Statement (5 chars) IOSync[4] = decode_alpha_word(&buf[26], 5); // Statement (5 chars) IOSync[5] = decode_alpha_word(&buf[31], 5); // Statement (5 chars) IOSync[9] = ( (buf[0] == 'C') ? (t_int64) 80 * D8 : 0 ) + // is a comment card ( ((buf[5] != ' ') && (buf[5] != 0)) ? (t_int64) 8 * D8 : 0 ) + // continuation line ( decode_num_word(&buf[1], 4, 1) ); // statement number } } /* * Device entry points for card reader. */ uint32 cdr_cmd(UNIT * uptr, uint16 cmd, uint16 addr) { struct _card_data *data; uint32 wiring; int i; char cbuf[81]; int ncdr, ic; /* Are we currently tranfering? */ if (uptr->u5 & URCSTA_BUSY) return SCPE_BUSY; // clear IO Sync buffer (where words read from cards will be stored) for (i=0;i<10;i++) { IOSync [i]=0; IOSync_NegativeZeroFlag[i]=0; } /* Test ready */ if ((uptr->flags & UNIT_ATT) == 0) { sim_debug(DEBUG_CMD, &cdr_dev, "No cards (no file attached)\n"); return SCPE_NOCARDS; } /* read the cards */ sim_debug(DEBUG_CMD, &cdr_dev, "READ\n"); uptr->u5 |= URCSTA_BUSY; switch(sim_read_card(uptr)) { case SCPE_EOF: sim_debug(DEBUG_DETAIL, &cdr_dev, "EOF\n"); uptr->u5 = 0; return SCPE_NOCARDS; case SCPE_UNATT: sim_debug(DEBUG_DETAIL, &cdr_dev, "Not Attached\n"); uptr->u5 = 0; return SCPE_NOCARDS; case SCPE_IOERR: sim_debug(DEBUG_DETAIL, &cdr_dev, "ERR\n"); uptr->u5 = 0; return SCPE_NOCARDS; case SCPE_OK: break; } data = (struct _card_data *)uptr->up7; // make local copy of card for debug output for (i=0; i<80; i++) cbuf[i] = data->hol_to_ascii[data->image[i]]; cbuf[80] = 0; // terminate string sim_debug(DEBUG_DETAIL, &cpu_dev, "Read Card: %s\n", sim_trim_endspc(cbuf)); // save read card in last read card buffer to be eventually printed // by carddec echolast scp command ncdr = uptr - &cdr_unit[1]; // ncdr is the card reader: 0 for cdr1, 1 for cdr2, 2 for cdr3 if ((ncdr >= 0) && (ncdr < 3)) { // safety check, not needed (should allways be true) but just to be sure // advance read buffer last card ReadHopperLast[ncdr] = (ReadHopperLast[ncdr] + 1) % MAX_CARDS_IN_READ_TAKE_HOPPER; // save card in read card hopper buffer ic = (ncdr * MAX_CARDS_IN_READ_TAKE_HOPPER + ReadHopperLast[ncdr]) * 80; for (i=0; i<80; i++) ReadHopper[ic + i] = cbuf[i]; } // uint16 data->image[] array that holds the actual punched rows on card // using this codification: // // Row Name value in image[] comments // // Y 0x800 Hi Punch Y(12) // X 0x400 Minus Punch X(11) // 0 0x200 also called T (Ten, 10) // 1 0x100 // 2 0x080 // 3 0x040 // 4 0x020 // 5 0x010 // 6 0x008 // 7 0x004 // 8 0x002 // 9 0x001 // // If several columns are punched, the values are ORed: eg char A is represented as a punch // on row Y and row 1, so it value in image array will be 0x800 | 0x100 -> 0x900 // check if it is a load card (Y(12) = HiPunch set on any column of card) signales it if (decode_8word_wiring(data, 1)) { uptr->u5 |= URCSTA_LOAD; } else { uptr->u5 &= ~URCSTA_LOAD; } wiring = (uptr->flags & UNIT_CARD_WIRING); // translate chars read from card and copy to memory words // using the control panel wiring. if (uptr->u5 & URCSTA_LOAD) { // load card -> use 8 words per card encoding decode_8word_wiring(data, 0); if (uptr->u5 & URCSTA_SOAPSYMB) { // requested to load soap symb info decode_soap_symb_info(data); } } else if (wiring == WIRING_SOAP) { // decode soap card simulating soap control panel wiring for 533 (gasp!) decode_soap_wiring(data); } else if (wiring == WIRING_IS) { // decode floating point interpretive system (bell interpreter) card decode_is_wiring(data); } else if (wiring == WIRING_IT) { // decode Carnegie Internal Translator compiler card decode_it_wiring(data); } else if (wiring == WIRING_FORTRANSIT) { // decode Fortransit translator card decode_fortransit_wiring(data); } else { // default wiring: decode up to 8 numerical words per card. Can be a load card decode_8word_wiring(data, 0); } uptr->u5 &= ~URCSTA_BUSY; return SCPE_OK; } /* Handle transfer of data for card reader */ t_stat cdr_srv(UNIT *uptr) { // I/O is synchronous. No need to set up svr return SCPE_OK; } /* Set card read/punch control panel wiring */ t_stat cdr_set_wiring (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { int f; if (uptr == NULL) return SCPE_IERR; if (cptr == NULL) return SCPE_ARG; for (f = 0; wirings[f].name != 0; f++) { if (strcmp (cptr, wirings[f].name) == 0) { uptr->flags = (uptr->flags & ~UNIT_CARD_WIRING) | wirings[f].mode; return SCPE_OK; } } return SCPE_ARG; } /* Show card read/punch control panel wiring */ t_stat cdr_show_wiring (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int f; for (f = 0; wirings[f].name != 0; f++) { if ((uptr->flags & UNIT_CARD_WIRING) == wirings[f].mode) { fprintf (st, "%s wiring", wirings[f].name); return SCPE_OK; } } fprintf (st, "invalid control panel wiring (%d)", uptr->flags & UNIT_CARD_WIRING); return SCPE_OK; } t_stat cdr_attach(UNIT * uptr, CONST char *file) { t_stat r; int ncdr, ic1, ic2, i; if (uptr->flags & UNIT_ATT) // remove current deck in read hopper before attaching sim_card_detach(uptr); // the new one r = sim_card_attach(uptr, file); if (SCPE_BARE_STATUS(r) != SCPE_OK) return r; uptr->u5 = 0; uptr->u4 = 0; uptr->u6 = 0; if (sim_switches & SWMASK ('L')) { /* Load Symbolic SOAP info? */ uptr->u5 |= URCSTA_SOAPSYMB; } // clear read card take hopper buffer ncdr = uptr - &cdr_unit[1]; // ncdr is the card reader: 0 for cdr1, 1 for cdr2, 2 for cdr3 if ((ncdr >= 0) && (ncdr < 3)) { // safety check, not needed (should allways be true) but just to be sure // reset last read card number ReadHopperLast[ncdr] = 0; // clear buffer ic1 = (ncdr * MAX_CARDS_IN_READ_TAKE_HOPPER) * 80; ic2 = ic1 + MAX_CARDS_IN_READ_TAKE_HOPPER * 80; for (i=ic1; i