diff --git a/scp.c b/scp.c index 50514eea..55e4625c 100644 --- a/scp.c +++ b/scp.c @@ -2096,9 +2096,13 @@ if (dptr == NULL) /* found dev? */ if (uptr == NULL) /* valid unit? */ return SCPE_NXUN; if (uptr->flags & UNIT_ATT) { /* already attached? */ - r = scp_detach_unit (dptr, uptr); /* detach it */ - if (r != SCPE_OK) /* error? */ - return r; + if (!(uptr->dynflags & UNIT_ATTMULT)) { + r = scp_detach_unit (dptr, uptr); /* detach it */ + if (r != SCPE_OK) /* error? */ + return r; + } + else + return SCPE_ALATT; } sim_trim_endspc (cptr); /* trim trailing spc */ return scp_attach_unit (dptr, uptr, cptr); /* attach */ diff --git a/sim_card.c b/sim_card.c new file mode 100644 index 00000000..f679cce1 --- /dev/null +++ b/sim_card.c @@ -0,0 +1,1495 @@ +/* Generic Card read/punch routines for simulators. + + Copyright (c) 2005, 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 + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + This is the standard card reader. + This is the standard card punch. + + Input formats are accepted in a variaty of formats: + Standard ASCII: one record per line. + returns are ignored. + tabs are expanded to modules 8 characters. + ~ in first column is treated as a EOF. + + 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 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 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. + + ASCII mode recognizes some additional forms of input which allows the + intermixing of binary cards with text cards. + + Lines beginning with ~raw are taken as a number of 4 digit octal values + with represent each column of the card from 12 row down to 9 row. If there + is not enough octal numbers to span a full card the remainder of the + card will not be punched. + + Also ~eor, will generate a 7/8/9 punch card. An ~eof will gernerate a + 6/7/9 punch card, and a ~eoi will generate a 6/7/8/9 punch. + + A single line of ~ will set the EOF flag when that card is read. + + For autodetection of card format, there can be no parity errors. + All undeterminate formats are treated as ASCII. + + Auto output format is ASCII if card has only printable characters + or card format binary. + + The card module uses up7 to hold a buffer for the card being translated + and the backward translation table. Which is generated from the table. +*/ + + +#include +#include "sim_defs.h" +#include "sim_card.h" + +#if defined(USE_SIM_CARD) + +#define card_ctx up8 + +#define CARD_EOF 0x1000 /* This card is end of file card. */ +#define CARD_ERR 0x2000 /* Return error for this card */ +#define DECK_SIZE 1000 /* Number of cards to allocate at a time */ + + +struct card_context +{ + t_addr punch_count; /* Number of cards punched */ + char cbuff[1024]; /* Read in buffer for cards */ + uint8 hol_to_ascii[4096]; /* Back conversion table */ + t_addr hopper_size; /* Size of hopper */ + t_addr hopper_cards; /* Number of cards in hopper */ + uint16 (*images)[1][80]; +}; + +/* Character conversion tables */ + +const char sim_six_to_ascii[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', '%', /* 17 = box */ + '_', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '@', ',', '(', '~', '\\', '#', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */ + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */ +}; /* 72 = rec mark */ + /* 75 = squiggle, 77 = del */ + +static const uint16 ascii_to_hol_026[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none Y28 78 T28 Y38 T48 XT 48 */ + 0x000, 0x600, 0x006, 0x282, 0x442, 0x222, 0xA00, 0x022, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* T48 X48 Y48 X T38 T X38 T1 */ + 0x222, 0x822, 0x422, 0x800, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 58 Y68 X68 38 68 X28 */ + 0x002, 0x001, 0x012, 0x40A, 0x80A, 0x042, 0x00A, 0x882, + /* @ A B C D E F G */ + /* 82 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 X58 X68 T58 T78 28 */ + 0x204, 0x202, 0x201, 0x812, 0x20A, 0x412, 0x406, 0x082, + /* ` a b c d e f g */ + 0x212, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* T79 X78 Y79 79 */ + 0x604, 0x602, 0x601, 0x406, 0x806, 0x805, 0x005, 0xf000 +}; + +/* Set for Burrough codes */ +static const uint16 ascii_to_hol_029[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none X28 78 38 Y38 T48 X 58 */ + 0x000, 0x482, 0x006, 0x042, 0x442, 0x222, 0x800, 0x012, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* X58 Y58 Y48 X68 T38 Y X38 T1 */ + 0x812, 0x412, 0x422, 0x80A, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 28 Y68 X48 68 T68 28 */ + 0x002, 0x001, 0x082, 0x40A, 0x822, 0x00A, 0x20A, 0x206, + /* @ A B C D E F G */ + /* 48 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 TY028 T28 TY038 Y78 T58 */ + 0x204, 0x202, 0x201, 0xE82, 0x282, 0xE42, 0x406, 0x212, + /* ` a b c d e f g */ + 0x102 ,0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* Y78 X78 X79 XTY18 */ + 0x604, 0x602, 0x601, 0x406, 0x806, 0x805, 0xF02,0xf000 +}; + +static const uint16 ascii_to_hol_ebcdic[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none Y28 78 38 Y38 T48 X 58 */ + 0x000, 0x482, 0x006, 0x042, 0x442, 0x222, 0x800, 0x012, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* X58 Y58 Y48 X T38 Y X38 T1 */ + 0x812, 0x412, 0x422, 0x800, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 28 Y68 X48 68 T68 T78 */ + 0x002, 0x001, 0x082, 0x40A, 0x822, 0x00A, 0x20A, 0x206, + /* @ A B C D E F G */ + /* 48 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 X28 X68 Y28 Y78 X58 */ + 0x204, 0x202, 0x201, 0x882, 0x20A, 0x482, 0x406, 0x212, + /* ` a b c d e f g */ + 0x102, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* X18 X78 Y18 XYT18 */ + 0x604, 0x602, 0x601, 0x902, 0x806, 0x502, 0xF02,0xf000 +}; + +const char sim_ascii_to_six[128] = { + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /*sp ! " # $ % & ' */ + 000, 052, -1, 032, 053, 017, 060, 014, /* 40 - 77 */ + /* ( ) * + , - . / */ + 034, 074, 054, 060, 033, 040, 073, 021, + /* 0 1 2 3 4 5 6 7 */ + 012, 001, 002, 003, 004, 005, 006, 007, + /* 8 9 : ; < = > ? */ + 010, 011, 015, 056, 076, 013, 016, 072, + /* @ A B C D E F G */ + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + /* H I J K L M N O */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* P Q R S T U V W */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* X Y Z [ \ ] ^ _ */ + 027, 030, 031, 075, 036, 055, 057, 020, + /* ` a b c d e f g */ + 035, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + /* h i j k l m n o */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* p q r s t u v w */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* x y z { | } ~ del*/ + 027, 030, 031, 057, 077, 017, -1, -1 +}; + +static uint16 ebcdic_to_hol[256] = { + /* T918 T91 T92 T93 T94 T95 T96 T97 0x0x */ + 0xB03, 0x901, 0x881, 0x841, 0x821, 0x811, 0x809, 0x805, + /* T98, T189 , T289, T389, T489, T589, T689, T789 */ + 0x803, 0x903, 0x883, 0x843, 0x823, 0x813, 0x80B, 0x807, + /* TE189 E91 E92 E93 E94 E95 E96 E97 0x1x */ + 0xD03, 0x501, 0x481, 0x441, 0x421, 0x411, 0x409, 0x405, + /* E98 E918 E928 E938 E948 E958 E968 E978 */ + 0x403, 0x503, 0x483, 0x443, 0x423, 0x413, 0x40B, 0x407, + /* E0918 091 092 093 094 095 096 097 0x2x */ + 0x703, 0x301, 0x281, 0x241, 0x221, 0x211, 0x209, 0x205, + /* 098 0918 0928 0938 0948 0958 0968 0978 */ + 0x203, 0x303, 0x283, 0x243, 0x223, 0x213, 0x20B, 0x207, + /* TE0918 91 92 93 94 95 96 97 0x3x */ + 0xF03, 0x101, 0x081, 0x041, 0x021, 0x011, 0x009, 0x005, + /* 98 189 289 389 489 589 689 789 */ + 0x003, 0x103, 0x083, 0x043, 0x023, 0x013, 0x00B, 0x007, + /* T091 T092 T093 T094 T095 T096 T097 0x4x */ + 0x000, 0xB01, 0xA81, 0xA41, 0xA21, 0xA11, 0xA09, 0xA05, + /* T098 T18 T28 T38 T48 T58 T68 T78 */ + 0xA03, 0x902, 0x882, 0x842, 0x822, 0x812, 0x80A, 0x806, + /* T TE91 TE92 TE93 TE94 TE95 TE96 TE97 0x5x */ + 0x800, 0xD01, 0xC81, 0xC41, 0xC21, 0xC11, 0xC09, 0xC05, + /* TE98 E18 E28 E38 E48 E58 E68 E78 */ + 0xC03, 0x502, 0x482, 0x442, 0x422, 0x412, 0x40A, 0x406, + /* E 01 E092 E093 E094 E095 E096 E097 0x6x */ + 0x400, 0x300, 0x681, 0x641, 0x621, 0x611, 0x609, 0x605, + /* E098 018 TE 038 048 68 068 078 */ + 0x603, 0x302, 0xC00, 0x242, 0x222, 0x212, 0x20A, 0x206, + /* TE0 TE091 TE092 TE093 TE094 TE095 TE096 TE097 0x7x */ + 0xE00, 0xF01, 0xE81, 0xE41, 0xE21, 0xE11, 0xE09, 0xE05, + /* TE098 18 28 38 48 58 68 78 */ + 0xE03, 0x102, 0x082, 0x042, 0x022, 0x012, 0x00A, 0x006, + /* T018 T01 T02 T03 T04 T05 T06 T07 0x8x */ + 0xB02, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, + /* T08 T09 T028 T038 T048 T058 T068 T078 */ + 0xA02, 0xA01, 0xA82, 0xA42, 0xA22, 0xA12, 0xA0A, 0xA06, + /* TE18 TE1 TE2 TE3 TE4 TE5 TE6 TE7 0x9x */ + 0xD02, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, 0xC04, + /* TE8 TE9 TE28 TE38 TE48 TE58 TE68 TE78 */ + 0xC02, 0xC01, 0xC82, 0xC42, 0xC22, 0xC12, 0xC0A, 0xC06, + /* E018 E01 E02 E03 E04 E05 E06 E07 0xax */ + 0x702, 0x700, 0x680, 0x640, 0x620, 0x610, 0x608, 0x604, + /* E08 E09 E028 E038 E048 E058 E068 E078 */ + 0x602, 0x601, 0x682, 0x642, 0x622, 0x612, 0x60A, 0x606, + /* TE018 TE01 TE02 TE03 TE04 TE05 TE06 TE07 0xbx */ + 0xF02, 0xF00, 0xE80, 0xE40, 0xE20, 0xE10, 0xE08, 0xE04, + /* TE08 TE09 TE028 TE038 TE048 TE058 TE068 TE078 */ + 0xE02, 0xE01, 0xE82, 0xE42, 0xE22, 0xE12, 0xE0A, 0xE06, + /* T0 T1 T2 T3 T4 T5 T6 T7 0xcx */ + 0xA00, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, + /* T8 T9 T0928 T0938 T0948 T0958 T0968 T0978 */ + 0x802, 0x801, 0xA83, 0xA43, 0xA23, 0xA13, 0xA0B, 0xA07, + /* E0 E1 E2 E3 E4 E5 E6 E7 0xdx */ + 0x600, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, 0x404, + /* E8 E9 TE928 TE938 TE948 TE958 TE968 TE978 */ + 0x402, 0x401, 0xC83, 0xC43, 0xC23, 0xC13, 0xC0B, 0xC07, + /* 028 E091 02 03 04 05 06 07 0xex */ + 0x282, 0x701, 0x280, 0x240, 0x220, 0x210, 0x208, 0x204, + /* 08 09 E0928 E0938 E0948 E0958 E0968 E0978 */ + 0x202, 0x201, 0x683, 0x643, 0x623, 0x613, 0x60B, 0x607, + /* 0 1 2 3 4 5 6 7 0xfx */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 TE0928 TE0938 TE0948 TE0958 TE0968 TE0978 */ + 0x002, 0x001, 0xE83, 0xE43, 0xE23, 0xE13, 0xE0B, 0xE07 +}; + +static uint16 hol_to_ebcdic[4096]; + +const uint8 sim_parity_table[64] = { + /* 0 1 2 3 4 5 6 7 */ + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000 +}; + +struct card_formats { + uint32 mode; + const char *name; +}; + +static struct card_formats fmts[] = { + {MODE_AUTO, "AUTO"}, + {MODE_BIN, "BIN"}, + {MODE_TEXT, "TEXT"}, + {MODE_BCD, "BCD"}, + {MODE_CBN, "CBN"}, + {MODE_EBCDIC,"EBCDIC"}, + {MODE_OCTAL, "OCTAL"}, + {0, 0}, +}; + + +/* Conversion routines */ + +/* Convert BCD character into hollerith code */ +uint16 +sim_bcd_to_hol(uint8 bcd) { + uint16 hol; + + /* Handle space correctly */ + if (bcd == 0) /* 0 to 82 punch */ + return 0x82; + if (bcd == 020) /* 20 no punch */ + return 0; + + /* Convert to top column */ + switch (bcd & 060) { + default: + case 000: + hol = 0x000; + break; + case 020: + hol = 0x200; + break; + case 040: + hol = 0x400; + break; + case 060: + hol = 0x800; + break; + } + + /* Handle case of 10 special */ + /* Only 032 is punched as 8-2 */ + if ((bcd & 017) == 10 && (bcd & 060) != 020) { + hol |= 1 << 9; + return hol; + } + /* Convert to 0-9 row */ + bcd &= 017; + if (bcd > 9) { + hol |= 0x2; /* Col 8 */ + bcd -= 8; + } + if (bcd != 0) + hol |= 1 << (9 - bcd); + return hol; +} + +/* Returns the BCD of the hollerith code or 0x7f if error */ +uint8 +sim_hol_to_bcd(uint16 hol) { + uint8 bcd; + + /* Convert 10,11,12 rows */ + switch (hol & 0xe00) { + case 0x000: + bcd = 0; + break; + case 0x200: + if ((hol & 0x1ff) == 0) + return 10; + bcd = 020; + break; + case 0x400: + bcd = 040; + break; + case 0x600: /* 11-10 Punch */ + bcd = 052; + break; + case 0x800: + bcd = 060; + break; + case 0xA00: /* 12-10 Punch */ + bcd = 072; + break; + default: /* Double punch in 10,11,12 rows */ + return 0x7f; + } + + hol &= 0x1ff; /* Mask rows 0-9 */ + /* Check row 8 punched */ + if (hol & 0x2) { + bcd += 8; + hol &= ~0x2; + } + + /* Convert rows 0-9 */ + while (hol != 0 && (hol & 0x200) == 0) { + bcd++; + hol <<= 1; + } + + /* Any more columns punched? */ + if ((hol & 0x1ff) != 0) + return 0x7f; + return bcd; +} + +/* Convert EBCDIC character into hollerith code */ +uint16 +sim_ebcdic_to_hol(uint8 ebcdic) { + return ebcdic_to_hol[ebcdic]; +} + + + +/* Returns the BCD of the hollerith code or 0x7f if error */ +uint16 +sim_hol_to_ebcdic(uint16 hol) { + return hol_to_ebcdic[hol]; +} + + + +t_addr +sim_hopper_size(UNIT * uptr) { + struct card_context *data = (struct card_context *)uptr->card_ctx; + if (data == NULL) + return 0; + return data->hopper_cards; +} + +t_addr +sim_punch_count(UNIT * uptr) { + struct card_context *data = (struct card_context *)uptr->card_ctx; + if (data == NULL) + return 0; + return data->punch_count; +} + +t_addr +sim_card_input_hopper_count(UNIT *uptr) { + struct card_context *data = (struct card_context *)uptr->card_ctx; + uint16 col; + + if (data == NULL || data->images == NULL) + return 0; /* attached? */ + + if (uptr->pos >= data->hopper_cards) + return 0; + + col = (*data->images)[data->hopper_cards-1][0]; + + return (int)((data->hopper_cards - uptr->pos) - ((col & CARD_EOF) ? 1 : 0)); +} + +t_addr +sim_card_output_hopper_count(UNIT *uptr) { + struct card_context *data = (struct card_context *)uptr->card_ctx; + + if (data == NULL) + return 0; /* attached? */ + + return (int)data->punch_count; +} + + +t_cdstat +sim_read_card(UNIT * uptr, uint16 image[80]) +{ + int i; + struct card_context *data = (struct card_context *)uptr->card_ctx; + DEVICE *dptr; + uint16 (*img)[80]; + t_stat r = CDSE_OK; + + if (data == NULL || (uptr->flags & UNIT_ATT) == 0) + return CDSE_EMPTY; /* attached? */ + if (data->hopper_cards == 0 || uptr->pos >= data->hopper_cards) + return CDSE_EMPTY; + + dptr = find_dev_from_unit( uptr); + img = &(*data->images)[uptr->pos]; + if (sim_deb && dptr && ((dptr)->dctrl & DEBUG_CARD)) { + if (image[0] & CARD_EOF) { + sim_debug(DEBUG_CARD, dptr, "Read hopper EOF\n"); + } else if (image[0] & CARD_ERR) { + sim_debug(DEBUG_CARD, dptr, "Read hopper ERR\n"); + } else { + uint8 out[81]; + int ok = 1; + for (i = 0; i < 80; i++) { + out[i] = data->hol_to_ascii[(int)(*img)[i]]; + if (out[i] == 0xff) { + ok = 0; + } + } + if (ok) { + sim_debug(DEBUG_CARD, dptr, "Read hopper: ["); + for (i = 0; i < 80; i++) { + sim_debug(DEBUG_CARD, dptr, "%c", out[i]); + } + sim_debug(DEBUG_CARD, dptr, "]\n"); + } else { + sim_debug(DEBUG_CARD, dptr, "Read hopper binary\n"); + } + } + } + if ((*img)[0] & CARD_EOF) + r = CDSE_EOF; + else if ((*img)[0] & CARD_ERR) + r = CDSE_ERROR; + uptr->pos++; + data->punch_count++; + memcpy(image, img, 80 * sizeof(uint16)); + image[0] &= 0xfff; /* Remove any CARD_EOF and CARD_ERR Flags */ + return r; +} + +/* + * Check if reader is at last card. + */ +int +sim_card_eof(UNIT *uptr) +{ + struct card_context *data = (struct card_context *)uptr->card_ctx; + uint16 col; + + if (data == NULL || data->images == NULL) + return SCPE_UNATT; /* attached? */ + + if (uptr->pos >= data->hopper_cards) + return SCPE_UNATT; + + col = (*data->images)[uptr->pos][0]; + + if (col & CARD_EOF) + return 1; + return 0; +} + + + +struct _card_buffer { + uint8 buffer[8192+500]; /* Buffer data */ + int len; /* Amount of data in buffer */ + int size; /* Size of last card read */ +}; + +static int _cmpcard(const uint8 *p, const char *s) { + int i; + if (p[0] != '~') + return 0; + for(i = 0; i < 3; i++) { + if (tolower(p[i+1]) != s[i]) + return 0; + } + return 1; +} + +t_stat +_sim_parse_card(UNIT *uptr, DEVICE *dptr, struct _card_buffer *buf, uint16 (*image)[80]) { + unsigned int mode; + uint16 temp; + int i; + char c; + int col; + + sim_debug(DEBUG_CARD, dptr, "Read card "); + if ((uptr->flags & UNIT_CARD_MODE) == MODE_AUTO) { + mode = MODE_TEXT; /* Default is text */ + + /* Check buffer to see if binary card in it. */ + for (i = 0, temp = 0; i < 160 && i len; i+=2) + temp |= (uint16)(buf->buffer[i] & 0xFF); + /* Check if every other char < 16 & full buffer */ + if ((temp & 0x0f) == 0 && i == 160) + mode = MODE_BIN; /* Probably binary */ + /* Check if maybe BCD or CBN */ + if (buf->buffer[0] & 0x80) { + int odd = 0; + int even = 0; + + /* Clear record mark */ + buf->buffer[0] &= 0x7f; + /* Check all chars for correct parity */ + for(i = 0, temp = 0; i < buf->len; i++) { + uint8 ch = buf->buffer[i]; + /* Stop at EOR */ + if (ch & 0x80) + break; + /* Try matching parity */ + if (sim_parity_table[(ch & 077)] == (ch & 0100)) + even++; + else + odd++; + } + /* Restore it */ + buf->buffer[0] |= 0x80; + if (i == 160 && odd == i) + mode = MODE_CBN; + else if (i < 80 && even == i) + mode = MODE_BCD; + } + + /* Check if modes match */ + if ((uptr->flags & UNIT_CARD_MODE) != MODE_AUTO && + (uptr->flags & UNIT_CARD_MODE) != mode) { + (*image)[0] = CARD_ERR; + sim_debug(DEBUG_CARD, dptr, "invalid mode\n"); + return SCPE_OPENERR; + } + } else + mode = uptr->flags & UNIT_CARD_MODE; + + switch(mode) { + default: + case MODE_TEXT: + sim_debug(DEBUG_CARD, dptr, "text: ["); + /* Check for special codes */ + if (buf->buffer[0] == '~') { + int f = 1; + for(col = i = 1; col < 80 && f && i < buf->len; i++) { + c = buf->buffer[i]; + switch (c) { + case '\n': + case '\0': + case '\r': + col = 80; + case ' ': + break; /* Ignore these */ + case '\t': + col = (col | 7) + 1; /* Mult of 8 */ + break; + default: + f = 0; + break; + } + } + if (f) { + (*image)[0] |= CARD_EOF; + goto end_card; + } + } + if (_cmpcard(&buf->buffer[0], "raw")) { + int j = 0; + sim_debug(DEBUG_CARD, dptr, "-octal-"); + for(col = 0, i = 4; col < 80 && i < buf->len; i++) { + if (buf->buffer[i] >= '0' && buf->buffer[i] <= '7') { + (*image)[col] = ((*image)[col] << 3) | (buf->buffer[i] - '0'); + j++; + } else if (buf->buffer[i] == '\n' || buf->buffer[i] == '\r') { + break; + } else { + (*image)[0] = CARD_ERR; + break; + } + if (j == 4) { + col++; + j = 0; + } + } + } else if (_cmpcard(&buf->buffer[0], "eor")) { + sim_debug(DEBUG_CARD, dptr, "-eor-"); + (*image)[0] = 07; /* 7/8/9 punch */ + i = 4; + } else if (_cmpcard(&buf->buffer[0], "eof")) { + sim_debug(DEBUG_CARD, dptr, "-eof-"); + (*image)[0] = 015; /* 6/7/9 punch */ + i = 4; + } else if (_cmpcard(&buf->buffer[0], "eoi")) { + sim_debug(DEBUG_CARD, dptr, "-eoi-"); + (*image)[0] = 017; /* 6/7/8/9 punch */ + i = 4; + } else { + /* Convert text line into card image */ + for (col = 0, i = 0; col < 80 && i < buf->len; i++) { + c = buf->buffer[i]; + switch (c) { + case '\0': + case '\r': + break; /* Ignore these */ + case '\t': + col = (col | 7) + 1; /* Mult of 8 */ + break; + case '\n': + col = 80; + i--; + break; + default: + sim_debug(DEBUG_CARD, dptr, "%c", c); + if ((uptr->flags & MODE_LOWER) == 0) + c = toupper(c); + switch(uptr->flags & MODE_CHAR) { + default: + case MODE_026: + temp = ascii_to_hol_026[(int)c]; + break; + case MODE_029: + temp = ascii_to_hol_029[(int)c]; + break; + case MODE_EBCDIC: + temp = ascii_to_hol_ebcdic[(int)c]; + break; + } + if (temp & 0xf000) + (*image)[0] |= CARD_ERR; + (*image)[col++] = temp & 0xfff; + } + } + } + end_card: + sim_debug(DEBUG_CARD, dptr, "-%d-", i); + + /* Scan to end of line, ignore anything after last column */ + while (buf->buffer[i] != '\n' && buf->buffer[i] != '\r' && i < buf->len) { + i++; + } + if (buf->buffer[i] == '\r') + i++; + if (buf->buffer[i] == '\n') + i++; + sim_debug(DEBUG_CARD, dptr, "]\n"); + break; + + case MODE_BIN: + temp = 0; + sim_debug(DEBUG_CARD, dptr, "bin\n"); + if (buf->len < 160) { + (*image)[0] = CARD_ERR; + return SCPE_OPENERR; + } + /* Move data to buffer */ + for (col = i = 0; i < 160;) { + temp |= (uint16)(buf->buffer[i] & 0xff); + (*image)[col] = (buf->buffer[i++] >> 4) & 0xF; + (*image)[col++] |= ((uint16)buf->buffer[i++] & 0xff) << 4; + } + /* Check if format error */ + if (temp & 0xF) + (*image)[0] |= CARD_ERR; + + break; + + case MODE_CBN: + sim_debug(DEBUG_CARD, dptr, "cbn\n"); + /* Check if first character is a tape mark */ + if (buf->buffer[0] == 0217 && + (buf->len == 1 || (buf->buffer[1] & 0200) != 0)) { + i = 1; + (*image)[0] |= CARD_EOF; + break; + } + + /* Clear record mark */ + buf->buffer[0] &= 0x7f; + + /* Convert card and check for errors */ + for (col = i = 0; i < buf->len && col < 80;) { + uint8 c; + + if (buf->buffer[i] & 0x80) + break; + c = buf->buffer[i] & 077; + if (sim_parity_table[(int)c] == (buf->buffer[i++] & 0100)) + (*image)[0] |= CARD_ERR; + (*image)[col] = ((uint16)c) << 6; + if (buf->buffer[i] & 0x80) + break; + c = buf->buffer[i] & 077; + if (sim_parity_table[(int)c] == (buf->buffer[i++] & 0100)) + (*image)[0] |= CARD_ERR; + (*image)[col++] |= c; + } + + if (i < buf->len && col >= 80 && (buf->buffer[i] & 0x80) == 0) { + (*image)[0] |= CARD_ERR; + } + /* Record over length of card, skip until next */ + while ((buf->buffer[i] & 0x80) == 0) { + if (i > buf->len) + break; + i++; + } + break; + + case MODE_BCD: + sim_debug(DEBUG_CARD, dptr, "bcd ["); + /* Check if first character is a tape mark */ + if (buf->buffer[0] == 0217 && (buf->buffer[1] & 0200) != 0) { + i = 1; + (*image)[0] |= CARD_EOF; + break; + } + + /* Clear record mark */ + buf->buffer[0] &= 0x7f; + + /* Convert text line into card image */ + for (col = 0, i = 0; col < 80 && i < buf->len; i++) { + if (buf->buffer[i] & 0x80) + break; + c = buf->buffer[i] & 077; + if (sim_parity_table[(int)c] != (buf->buffer[i] & 0100)) + (*image)[0] |= CARD_ERR; + sim_debug(DEBUG_CARD, dptr, "%c", sim_six_to_ascii[(int)c]); + /* Convert to top column */ + (*image)[col++] = sim_bcd_to_hol(c); + } + + if (i < buf->len && col >= 80 && (buf->buffer[i] & 0x80) == 0) { + (*image)[0] |= CARD_ERR; + } + + /* Record over length of card, skip until next */ + while ((buf->buffer[i] & 0x80) == 0) { + if (i > buf->len) + break; + i++; + } + + sim_debug(DEBUG_CARD, dptr, "]\n"); + break; + + case MODE_EBCDIC: + sim_debug(DEBUG_CARD, dptr, "ebcdic\n"); + if (buf->len < 80) + (*image)[0] |= CARD_ERR; + /* Move data to buffer */ + for (i = 0; i < 80 && i < buf->len; i++) { + temp = (uint16)(buf->buffer[i]) & 0xFF; + (*image)[i] = ebcdic_to_hol[temp]; + } + break; + + } + buf->size = i; + return SCPE_OK; +} + +t_stat +_sim_read_deck(UNIT * uptr, int eof) +{ + struct _card_buffer buf; + struct card_context *data; + DEVICE *dptr; + int i; + int j; + int l; + int cards = 0; + t_stat r = SCPE_OK; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + dptr = find_dev_from_unit( uptr); + data = (struct card_context *)uptr->card_ctx; + + buf.len = 0; + buf.size = 0; + buf.buffer[0] = 0; /* Initialize bufer to empty */ + + /* Slurp up current file */ + do { + if (buf.len < 500 && !feof(uptr->fileref)) { + l = sim_fread(&buf.buffer[buf.len], 1, 8192, uptr->fileref); + if (l < 0) + r = SCPE_OPENERR; + else + buf.len += l; + } + + /* Allocate space for some more cards if needed */ + if (data->hopper_cards >= data->hopper_size) { + data->hopper_size += DECK_SIZE; + data->images = (uint16 (*)[1][80])realloc(data->images, + (size_t)data->hopper_size * sizeof(*(data->images))); + memset(&data->images[data->hopper_cards], 0, + (size_t)(data->hopper_size - data->hopper_cards) * + sizeof(*(data->images))); + } + + /* Process one card */ + cards++; + if (_sim_parse_card(uptr, dptr, &buf, &(*data->images)[data->hopper_cards]) + != SCPE_OK) { + r = sim_messagef(SCPE_OPENERR, "%s: %s Error (%s) in card %d\n", + sim_uname(uptr), uptr->filename, sim_error_text(r), cards); + } + data->hopper_cards++; + /* Move data to start at begining of buffer */ + /* Data is moved down to simplify the decoding of one card */ + l = buf.len - buf.size; + j = buf.size; + for(i = 0; i < l; i++, j++) + buf.buffer[i] = buf.buffer[j]; + buf.len -= buf.size; + } while (buf.len > 0 && r == SCPE_OK); + + /* If there is an error, free just read deck */ + if (r == SCPE_OK) { + if (eof) { + /* Allocate space for some more cards if needed */ + if (data->hopper_cards >= data->hopper_size) { + data->hopper_size += DECK_SIZE; + data->images = (uint16 (*)[1][80])realloc(data->images, + (size_t)data->hopper_size * sizeof(*(data->images))); + memset(&data->images[data->hopper_cards], 0, + (size_t)(data->hopper_size - data->hopper_cards) * + sizeof(*(data->images))); + } + + /* Create empty card */ + (*data->images)[data->hopper_cards][0] = CARD_EOF; + data->hopper_cards++; + } + } + return r; +} + + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + + +t_stat +sim_punch_card(UNIT * uptr, uint16 image[80]) +{ +/* Convert word record into column image */ +/* Check output type, if auto or text, try and convert record to bcd first */ +/* If failed and text report error and dump what we have */ +/* Else if binary or not convertable, dump as image */ + + /* Try to convert to text */ + uint8 out[512]; + int i; + int outp = 0; + int mode = uptr->flags & UNIT_CARD_MODE; + int ok = 1; + struct card_context *data; + DEVICE *dptr; + + dptr = find_dev_from_unit(uptr); + data = (struct card_context *)uptr->card_ctx; + + if (data == NULL || (uptr->flags & UNIT_ATT) == 0) + return CDSE_EMPTY; /* attached? */ + + /* Fix mode if in auto mode */ + if (mode == MODE_AUTO) { + /* Try to convert each column to ascii */ + for (i = 0; i < 80; i++) { + out[i] = data->hol_to_ascii[image[i]]; + if (out[i] == 0xff) { + ok = 0; + } + } + mode = ok?MODE_TEXT:MODE_OCTAL; + } + + switch(mode) { + default: + case MODE_TEXT: + /* Scan each column */ + sim_debug(DEBUG_CARD, dptr, "text: ["); + for (i = 0; i < 80; i++, outp++) { + out[outp] = data->hol_to_ascii[image[i]]; + if (out[outp] == 0xff) { + out[outp] = '?'; + } + sim_debug(DEBUG_CARD, dptr, "%c", out[outp]); + } + sim_debug(DEBUG_CARD, dptr, "]\n"); + /* Trim off trailing spaces */ + while (outp > 0 && out[--outp] == ' ') ; + out[++outp] = '\n'; + out[++outp] = '\0'; + break; + + case MODE_OCTAL: + sim_debug(DEBUG_CARD, dptr, "octal: ["); + out[outp++] = '~'; + for (i = 79; i >= 0; i--) { + if (image[i] != 0) + break; + } + /* Check if special card */ + if (i == 0) { + out[outp++] = 'e'; + out[outp++] = 'o'; + if (image[0] == 07) { + out[outp++] = 'r'; + out[outp++] = '\n'; + sim_debug(DEBUG_CARD, dptr, "eor\n"); + break; + } + if (image[0] == 015) { + out[outp++] = 'f'; + out[outp++] = '\n'; + sim_debug(DEBUG_CARD, dptr, "eof\n"); + break; + } + if (image[0] == 017) { + out[outp++] = 'f'; + out[outp++] = '\n'; + sim_debug(DEBUG_CARD, dptr, "eoi\n"); + break; + } + } + out[outp++] = 'r'; + out[outp++] = 'a'; + out[outp++] = 'w'; + for (i = 0; i < 80; i++) { + uint16 col = image[i]; + out[outp++] = ((col >> 9) & 07) + '0'; + out[outp++] = ((col >> 6) & 07) + '0'; + out[outp++] = ((col >> 3) & 07) + '0'; + out[outp++] = (col & 07) + '0'; + } + out[outp++] = '\n'; + sim_debug(DEBUG_CARD, dptr, "%s", &out[4]); + break; + + + case MODE_BIN: + sim_debug(DEBUG_CARD, dptr, "bin\n"); + for (i = 0; i < 80; i++) { + uint16 col = image[i]; + out[outp++] = (col & 0x00f) << 4; + out[outp++] = (col & 0xff0) >> 4; + } + break; + + case MODE_CBN: + sim_debug(DEBUG_CARD, dptr, "cbn\n"); + /* Fill buffer */ + for (i = 0; i < 80; i++) { + uint16 col = image[i]; + out[outp++] = (col >> 6) & 077; + out[outp++] = col & 077; + } + /* Now set parity */ + for (i = 0; i < 160; i++) + out[i] |= 0100 ^ sim_parity_table[(int)out[i]]; + out[0] |= 0x80; /* Set record mark */ + break; + + case MODE_BCD: + sim_debug(DEBUG_CARD, dptr, "bcd ["); + for (i = 0; i < 80; i++, outp++) { + out[outp] = sim_hol_to_bcd(image[i]); + if (out[outp] != 0x7f) + out[outp] |= sim_parity_table[(int)out[outp]]; + else + out[outp] = 077; + sim_debug(DEBUG_CARD, dptr, "%c", + sim_six_to_ascii[(int)out[outp]]); + } + sim_debug(DEBUG_CARD, dptr, "]\n"); + out[0] |= 0x80; /* Set record mark */ + while (outp > 0 && out[--outp] == 0); + outp++; + break; + + case MODE_EBCDIC: + sim_debug(DEBUG_CARD, dptr, "ebcdic\n"); + /* Fill buffer */ + for (i = 0; i < 80; i++, outp++) { + uint16 col = image[i]; + out[outp] = 0xff & hol_to_ebcdic[col]; + } + break; + } + data->punch_count++; + sim_fwrite(out, 1, outp, uptr->fileref); + uptr->pos = ftell (uptr->fileref); + /* Clear image buffer */ + for (i = 0; i < 80; image[i++] = 0); + return CDSE_OK; +} + +/* Set card format */ +t_stat sim_card_set_fmt (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; fmts[f].name != 0; f++) { + if (strcmp (cptr, fmts[f].name) == 0) { + uptr->flags = (uptr->flags & ~UNIT_CARD_MODE) | fmts[f].mode; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +/* Show card format */ + +t_stat sim_card_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int f; + + for (f = 0; fmts[f].name != 0; f++) { + if ((uptr->flags & UNIT_CARD_MODE) == fmts[f].mode) { + fprintf (st, "%s format", fmts[f].name); + return SCPE_OK; + } + } + fprintf (st, "invalid format"); + return SCPE_OK; +} + + +t_stat +sim_card_attach(UNIT * uptr, CONST char *cptr) +{ + t_stat r = SCPE_OK; + int eof = 0; + struct card_context *data; + char gbuf[30]; + unsigned int i; + char *saved_filename; + t_bool was_attached = (uptr->flags & UNIT_ATT); + t_addr saved_pos; + static int ebcdic_init = 0; + + if ((uptr->flags & UNIT_RO) && /* Attaching a Reader */ + strchr (cptr, ',')) { /* Restoring Attach list of files? */ + char tbuf[10*CBUFSIZE]; + char *tptr = tbuf; + int32 saved_switches = sim_switches; + + strlcpy (tbuf, cptr, sizeof(tbuf)); + tptr = strtok (tptr, ","); + while (tptr) { + cptr = tptr; + while (isspace(*cptr)) + ++cptr; + r = sim_card_attach (uptr, cptr); + if (r != SCPE_OK) + return r; + tptr = strtok (NULL, ","); + sim_switches = saved_switches & ~SWMASK('F'); + sim_switches |= SWMASK('S'); + } + sim_switches = saved_switches; + return SCPE_OK; + } + + cptr = get_sim_sw (cptr); /* Pickup optional format specifier during RESTORE */ + if (sim_switches & SWMASK ('F')) { /* format spec? */ + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + if (sim_card_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } + + if (sim_switches & SWMASK ('E')) + eof = 1; + + saved_filename = uptr->filename; + uptr->filename = NULL; + saved_pos = uptr->pos; + if ((r = attach_unit(uptr, cptr)) != SCPE_OK) { + uptr->filename = saved_filename; + uptr->pos = saved_pos; + return r; + } + + + /* Initialize reverse mapping if not initialized */ + /* Set all to invalid */ + /* Allocate a buffer if one does not exist */ + if (uptr->card_ctx == 0) { + uptr->card_ctx = malloc(sizeof(struct card_context)); + data = (struct card_context *)uptr->card_ctx; + memset(data, 0, sizeof(struct card_context)); + } else { + data = (struct card_context *)uptr->card_ctx; + } + + if (!ebcdic_init) { + for (i = 0; i < 4096; i++) + hol_to_ebcdic[i] = 0x100; + for (i = 0; i < 256; i++) { + uint16 temp = ebcdic_to_hol[i]; + if (hol_to_ebcdic[temp] != 0x100) { + fprintf(stderr, "Translation error %02x is %03x and %03x\n", + i, temp, hol_to_ebcdic[temp]); + } else { + hol_to_ebcdic[temp] = i; + } + } + ebcdic_init = 1; + } + + memset(&data->hol_to_ascii[0], 0xff, 4096); + for(i = 0; i < (sizeof(ascii_to_hol_026)/sizeof(uint16)); i++) { + uint16 temp; + switch(uptr->flags & MODE_CHAR) { + default: + case 0: + case MODE_026: + temp = ascii_to_hol_026[i]; + break; + case MODE_029: + temp = ascii_to_hol_029[i]; + break; + } + if ((temp & 0xf000) == 0) { + data->hol_to_ascii[temp] = i; + } + } + + if (uptr->flags & UNIT_RO) { /* Card Reader? */ + t_addr previous_cards = data->hopper_cards; + + /* Check if we should append to end of existing */ + if ((sim_switches & SWMASK ('S')) == 0) { + previous_cards = 0; + data->hopper_cards = 0; + data->hopper_size = 0; + data->punch_count = 0; + free(data->images); + data->images = NULL; + free(saved_filename); + saved_filename = NULL; + saved_pos = 0; + } + + /* Go read the deck */ + r = _sim_read_deck(uptr, eof); + uptr->pos = saved_pos; + detach_unit(uptr); + if (was_attached) { + uptr->flags |= UNIT_ATT; + uptr->filename = saved_filename; + } + if (r == SCPE_OK) { + const char *fmt = "AUTO"; + unsigned int mode = uptr->flags & UNIT_CARD_MODE; + for (i = 0; fmts[i].name != 0; i++) { + if (fmts[i].mode == mode) { + fmt = fmts[i].name; + break; + } + } + uptr->flags |= UNIT_ATT; + uptr->dynflags |= UNIT_ATTMULT; + if (saved_filename) { + uptr->filename = (char *)malloc (32 + strlen (cptr) + strlen (saved_filename)); + sprintf (uptr->filename, "%s, %s-F %s %s", saved_filename, + (eof)? "-E ": "", fmt, cptr); + free(saved_filename); + } else { + uptr->filename = (char *)malloc (32 + strlen (cptr)); + sprintf (uptr->filename, "%s-F %s %s", (eof)?"-E ": "", fmt, cptr); + } + r = sim_messagef(SCPE_OK, "%s: %d card Deck Loaded from %s\n", + sim_uname(uptr), (int)(data->hopper_cards - previous_cards), cptr); + } else { + if (uptr->dynflags & UNIT_ATTMULT) + uptr->flags |= UNIT_ATT; + detach_unit(uptr); + return r; + } + } + + return r; +} + +t_stat +sim_card_detach(UNIT * uptr) +{ + /* Free buffer if one allocated */ + if (uptr->card_ctx != 0) { + struct card_context * data = (struct card_context *)uptr->card_ctx; + /* No clear any existing decks on stack */ + free(data->images); + free(uptr->card_ctx); + uptr->card_ctx = 0; + } + if (uptr->flags & UNIT_RO) { /* Card Reader? */ + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + uptr->dynflags &= ~UNIT_ATTMULT; + return SCPE_OK; /* Already detached */ + } + return detach_unit(uptr); +} + +t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + uint32 i, readers = 0, punches = 0; + + for (i=0; i < dptr->numunits; ++i) + if (dptr->units[i].flags & UNIT_ATTABLE) { + readers += ((dptr->units[i].flags & UNIT_RO) != 0); + punches += ((dptr->units[i].flags & UNIT_RO) == 0); + } + if (uptr == NULL) + uptr = dptr->units; + fprintf (st, "%s Card %s%s%sAttach Help\n\n", dptr->name, + readers ? "Reader " : "", readers & punches ? "& " : "", punches ? "Punch ": ""); + if (0 == (uptr-dptr->units)) { + if (dptr->numunits > 1) { + uint32 i; + + for (i=0; i < dptr->numunits; ++i) + if (dptr->units[i].flags & UNIT_ATTABLE) + fprintf (st, " sim> ATTACH {switches} %s carddeck%s\n", sim_uname (&dptr->units[i]), ((dptr->units[i].flags & UNIT_RO) != 0) ? "{,extra-cards,...}" : ""); + fprintf (st, "\n"); + } + else + fprintf (st, " sim> ATTACH {switches} %s carddeck%s\n\n", sim_uname (uptr), ((dptr->units[0].flags & UNIT_RO) != 0) ? "{,extra-cards,...}" : ""); + } + else + fprintf (st, " sim> ATTACH {switches} %s carddeck%s\n\n", sim_uname (uptr), (readers > 0) ? "{,extra-cards,...}" : ""); + fprintf (st, "Attach command switches\n"); + fprintf (st, " -F Open the indicated card deck in a specific format (default\n"); + fprintf (st, " is AUTO, alternatives are BIN, TEXT, BCD, CBN and EBCDIC)\n"); + if (punches != 0) { + fprintf (st, " -N Create a new punch output file (default is to append to\n"); + fprintf (st, " an existing file if it exists)\n"); + } + if (readers != 0) { + fprintf (st, " -E Return EOF after deck read\n"); + fprintf (st, " -S Append deck to cards currently waiting to be read\n"); + } + return SCPE_OK; +} + +static t_stat create_card_file (const char *filename, int cards) +{ +FILE *f; +int i; + +f = fopen (filename, "w"); +if (f == NULL) + return SCPE_OPENERR; +for (i=0; i + +t_stat sim_card_test (DEVICE *dptr) +{ +t_stat stat = SCPE_OK; +#if defined(USE_SIM_CARD) && defined(SIM_CARD_API) && (SIM_MAJOR > 3) +char cmd[CBUFSIZE]; +char saved_filename[4*CBUFSIZE]; +uint16 card_image[80]; +SIM_TEST_INIT; + +if ((dptr->units->flags & UNIT_RO) == 0) /* Punch device? */ + return SCPE_OK; + +sim_printf ("Testing %s device sim_card APIs\n", dptr->name); + +(void)remove("file1.deck"); +(void)remove("file2.deck"); +(void)remove("file3.deck"); +(void)remove("file4.deck"); + +SIM_TEST(create_card_file ("File10.deck", 10)); +SIM_TEST(create_card_file ("File20.deck", 20)); +SIM_TEST(create_card_file ("File30.deck", 30)); +SIM_TEST(create_card_file ("File40.deck", 40)); + +sprintf (cmd, "%s File10.deck", dptr->name); +SIM_TEST(attach_cmd (0, cmd)); +sprintf (cmd, "%s File20.deck", dptr->name); +SIM_TEST(attach_cmd (0, cmd)); +sprintf (cmd, "%s -S File30.deck", dptr->name); +SIM_TEST(attach_cmd (0, cmd)); +sprintf (cmd, "%s -S -E File40.deck", dptr->name); +SIM_TEST(attach_cmd (0, cmd)); +sprintf (saved_filename, "%s %s", dptr->name, dptr->units->filename); +show_cmd (0, dptr->name); +sim_printf ("Input Hopper Count: %d\n", (int)sim_card_input_hopper_count(dptr->units)); +sim_printf ("Output Hopper Count: %d\n", (int)sim_card_output_hopper_count(dptr->units)); +while (!sim_card_eof (dptr->units)) + SIM_TEST(sim_read_card (dptr->units, card_image)); +sim_printf ("Input Hopper Count: %d\n", (int)sim_card_input_hopper_count(dptr->units)); +sim_printf ("Output Hopper Count: %d\n", (int)sim_card_output_hopper_count(dptr->units)); +sim_printf ("Detaching %s\n", dptr->name); +SIM_TEST(detach_cmd (0, dptr->name)); +show_cmd (0, dptr->name); +sim_printf ("Input Hopper Count: %d\n", (int)sim_card_input_hopper_count(dptr->units)); +sim_printf ("Output Hopper Count: %d\n", (int)sim_card_output_hopper_count(dptr->units)); +sim_printf ("Attaching Saved Filenames: %s\n", saved_filename + strlen(dptr->name)); +SIM_TEST(attach_cmd (0, saved_filename)); +show_cmd (0, dptr->name); +sim_printf ("Input Hopper Count: %d\n", (int)sim_card_input_hopper_count(dptr->units)); +sim_printf ("Output Hopper Count: %d\n", (int)sim_card_output_hopper_count(dptr->units)); +SIM_TEST(detach_cmd (0, dptr->name)); +(void)remove ("file10.deck"); +(void)remove ("file20.deck"); +(void)remove ("file30.deck"); +(void)remove ("file40.deck"); +#endif /* defined(USE_SIM_CARD) && defined(SIM_CARD_API) */ +return stat; +} + +#else /* !defined(USE_SIM_CARD) */ + +t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + return SCPE_OK; +} + +t_stat sim_card_test (DEVICE *dptr) +{ + return SCPE_OK; +} + +#endif /* USE_SIM_CARD */ diff --git a/sim_card.h b/sim_card.h new file mode 100644 index 00000000..066c59e6 --- /dev/null +++ b/sim_card.h @@ -0,0 +1,153 @@ +/* Generic Card read/punch routines for simulators. + + Copyright (c) 2005, 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 + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + This is the standard card reader. + This is the standard card punch. + + Input formats are accepted in a variaty of formats: + Standard ASCII: one record per line. + returns are ignored. + tabs are expanded to modules 8 characters. + ~ in first column is treated as a EOF. + + Binary Card format: + Each record 160 characters. + First character 21012345 + 111 + Second characters 6789---- + Top 4 bits of second character are 0. + It is unlikely that ascii text or BCD format + text could produce similar profile. + + 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 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 autodetection of BCD card format, there can be no parity errors. + All undeterminate formats are treated as ASCII. + + ASCII mode recognizes some additional forms of input which allows the + intermixing of binary cards with text cards. + + Lines beginning with ~raw are taken as a number of 4 digit octal values + with represent each column of the card from 12 row down to 9 row. If there + is not enough octal numbers to span a full card the remainder of the + card will not be punched. + + Also ~eor, will generate a 7/8/9 punch card. An ~eof will gernerate a + 6/7/9 punch card, and a ~eoi will generate a 6/7/8/9 punch. + + A single line of ~ will set the EOF flag when that card is read. + + Auto output format is ASCII if card has only printable characters + or card format binary. +*/ + +#ifndef SIM_CARD_H_ +#define SIM_CARD_H_ 0 + +#ifdef __cplusplus +extern "C" { +#endif + +#define SIM_CARD_API 2 /* API Version */ + +#define DEBUG_CARD 0x80000000 /* Show details */ + +/* Flags for punch and reader. */ +#define UNIT_V_CARD_MODE (UNIT_V_UF + 0) +#define UNIT_CARD_MODE (7 << UNIT_V_CARD_MODE) +#define MODE_AUTO (0 << UNIT_V_CARD_MODE) +#define MODE_BIN (1 << UNIT_V_CARD_MODE) +#define MODE_TEXT (2 << UNIT_V_CARD_MODE) +#define MODE_BCD (3 << UNIT_V_CARD_MODE) +#define MODE_CBN (4 << UNIT_V_CARD_MODE) +#define MODE_EBCDIC (5 << UNIT_V_CARD_MODE) +#define MODE_OCTAL (6 << UNIT_V_CARD_MODE) +/* Allow lower case letters */ +#define MODE_LOWER (8 << UNIT_V_CARD_MODE) +#define MODE_026 (0x10 << UNIT_V_CARD_MODE) +#define MODE_029 (0x20 << UNIT_V_CARD_MODE) +#define MODE_CHAR (0x70 << UNIT_V_CARD_MODE) + + +/* Card Reader Return Status code */ +typedef int t_cdstat; +#define CDSE_OK 0 /* Good */ +#define CDSE_EOF 1 /* End of File */ +#define CDSE_EMPTY 2 /* Input Hopper Empty */ +#define CDSE_ERROR 3 /* Error Card Read */ + +/* Generic routines. */ + + /* Read next card into image row 12,11,10,1-9 */ + /* Return SCPE_EOF if end file detected. */ +t_cdstat sim_read_card(UNIT * uptr, uint16 image[80]); + /* Punch card from image row 12,11,10,1-9 */ +t_cdstat sim_punch_card(UNIT * uptr, uint16 image[80]); + /* Check if next card to be read is EOF */ +int sim_card_eof(UNIT * uptr); + /* Return number of cards yet to read */ +t_addr sim_hopper_size(UNIT * uptr); + /* Return number of cards punched */ +t_addr sim_punch_count(UNIT * uptr); +t_addr sim_card_input_hopper_count(UNIT *uptr); +t_addr sim_card_output_hopper_count(UNIT *uptr); +t_stat sim_card_attach(UNIT * uptr, CONST char *file); +t_stat sim_card_detach(UNIT *uptr); + +/* Conversion routines to save code */ +uint16 sim_bcd_to_hol(uint8 bcd); +uint16 sim_ebcdic_to_hol(uint8 ebcdic); +uint8 sim_hol_to_bcd(uint16 hol); +uint16 sim_hol_to_ebcdic(uint16 hol); + +/* Format control routines. */ +t_stat sim_card_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat sim_card_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +/* Help information */ +t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +/* Translation tables */ +extern const char sim_six_to_ascii[64]; /* Map BCD to ASCII */ +extern const char sim_ascii_to_six[128]; /* Map 7 bit ASCII to BCD */ +extern const uint8 sim_parity_table[64]; /* 64 entry odd parity table */ + +/* Unit test routine */ +extern t_stat sim_card_test (DEVICE *dptr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sim_defs.h b/sim_defs.h index a1c288bc..440520e6 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -1,6 +1,6 @@ /* sim_defs.h: simulator definitions - Copyright (c) 1993-2019, Robert M Supnik + Copyright (c) 1993-2020, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 09-Nov-20 RMS More V4.X compatibility hooks (Mark Pizzolato) 26-Oct-19 RMS Removed MTAB_VAL definition 23-Jun-17 RMS Added #include sim_rev.h (Mark Pizzolato) 25-Sep-16 RMS Removed KBD_WAIT and friends @@ -353,6 +354,7 @@ struct sim_device { #define DEV_MUX (DEV_NET) /* (4.0 dummy) */ #define DEV_DISPLAY 0 /* (4.0 dummy) */ #define DEV_ETHER 0 /* (4.0 dummy) */ +#define DEV_CARD 0 /* (4.0 dummy) */ #define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1)) #define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1)) @@ -415,12 +417,12 @@ struct sim_unit { #define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) #define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */ -/* Unit dynamic flags (dynflags) (from 4.0) */ +/* Unit dynamic flags (dynflags) (from 4.0) + These flags are only set dynamically */ -/* These flags are only set dynamically */ - -#define UNIT_V_DF_TAPE 3 /* Bit offset for Tape Density reservation */ -#define UNIT_W_DF_TAPE 3 /* Bits Reserved for Tape Density */ +#define UNIT_ATTMULT 000001 /* allow multiple ATT cmd */ +#define UNIT_V_DF_TAPE 3 /* tape density reservation */ +#define UNIT_W_DF_TAPE 3 /* Register data structure */