First Commit of my working state
[simh.git] / Ibm1130 / ibm1130_cr.c
CommitLineData
196ba1fc
PH
1#include "ibm1130_defs.h"\r
2#include "ibm1130_fmt.h"\r
3\r
4#ifdef _WIN32\r
5# include <io.h> /* Microsoft puts definition of mktemp into io.h rather than stdlib.h */\r
6#endif\r
7\r
8/* ibm1130_cr.c: IBM 1130 1442 Card Reader simulator\r
9\r
10 Based on the SIMH package written by Robert M Supnik\r
11\r
12 * (C) Copyright 2002, Brian Knittel.\r
13 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r
14 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r
15 * usual yada-yada. Please keep this notice and the copyright in any distributions\r
16 * or modifications.\r
17 *\r
18 * This is not a supported product, but I welcome bug reports and fixes.\r
19 * Mail to simh@ibm1130.org\r
20\r
21 * Update 2006-01-23 More fixes, in call to mktemp and in 2501 support, also thanks\r
22 to Carl Claunch.\r
23\r
24 * Update 2006-01-03 Fixed bug found by Carl Claunch: feed function does not\r
25 cause an operation complete interrupt. Standard DMS routines were not\r
26 sensitive to this but DUP uses its own interrupt handler, and this\r
27 is why DUP would hang at end of deck.\r
28 \r
29 * Update 2005-05-19 Added support for 2501 reader\r
30\r
31 * Update 2004-11-08 Merged in correct physical card reader code\r
32\r
33 * Update 2004-11-02: Added -s to boot command: don't touch console switches.\r
34\r
35 * Update 2004-06-05: Removed "feedcycle" from cr_reset. Reset should not touch the card reader.\r
36\r
37 * Update 2004-04-12: Changed ascii field of CPCODE to unsigned char, caught a couple\r
38 other potential problems with signed characters used as subscript indexes.\r
39\r
40 * Update 2003-11-25: Physical card reader support working, may not be perfect.\r
41 Changed magic filename for stdin to "(stdin)".\r
42\r
43 * Update 2003-07-23: Added autodetect for card decks (029 vs binary),\r
44 made this the default.\r
45 \r
46 * Update 2003-06-21: Fixed bug in XIO_SENSE: op_complete and response\r
47 bits were being cleared before the DSW was saved in ACC. Somehow DMS\r
48 worked with this, but APL didn't.\r
49\r
50 * Update 2002-02-29: Added deck-list option.\r
51\r
52 * Update 2003-02-08: Fixed error in declaration of array list_save, pointed\r
53 out by Ray Comas.\r
54\r
55* -----------------------------------------------------------------------\r
56* USAGE NOTES\r
57* -----------------------------------------------------------------------\r
58\r
59* Attach switches:\r
60\r
61 The ATTACH CR command accepts several command-line switches\r
62\r
63 -q quiet mode, the simulator will not print the name of each file it opens\r
64 while processing deck files (which are discussed below). For example,\r
65 \r
66 ATTACH -q @deckfile\r
67\r
68 -l makes the simulator convert lower case letters in text decks\r
69 to the IBM lower-case Hollerith character codes. Normally, the simulator\r
70 converts lower case input to the uppercase Hollerith character codes.\r
71 (Lowercase codes are used in APL\1130 save decks).\r
72\r
73 -d prints a lot of simulator debugging information\r
74\r
75 -f converts tabs in an ascii file to spaces according to Fortran column conventions\r
76 -a converts tabs in an ascii file to spaces according to 1130 Assembler column conventions\r
77 -t converts tabs in an ascii file to spaces, with tab settings every 8 columns\r
78 (See below for a discussion of tab formatting)\r
79\r
80 -p means that filename is a COM port connected to a physical card reader using\r
81 the CARDREAD interface (see http://ibm1130.org/sim/downloads)\r
82\r
83 The ATTACH CP command accepts the -d switch.\r
84\r
85* Deck lists\r
86 If you issue an attach command and specify the filename as\r
87 "@filename", the file is interpreted as a list of filenames to\r
88 be read in sequence; the effect is that the reader sees the concatenation\r
89 of all of the files listed. The simulator "reset" does NOT rewind the deck list.\r
90\r
91 Filenames may be quoted if they contain spaces.\r
92\r
93 The strings %1, %2, etc, if they appear, are replaced with arguments passed\r
94 on the attach command line after the name of the deckfile. These can be the\r
95 arguments to ibm1130, or to the "do" command if a "do" script is executing, if the\r
96 attach command is constructed this way:\r
97\r
98 attach @deckfile %1 %2 %3\r
99 \r
100 This will pass the ibm1130 or do script arguments to attach, which will make\r
101 them available in the deckfile. Then, for instance the line\r
102\r
103 %1.for\r
104\r
105 would be substituted accordingly.\r
106\r
107 Blank lines and lines starting with ; # or * are ignored as comments.\r
108\r
109 Filenames may be followed by whitespace and one or more mode options:\r
110 The mode options are:\r
111 \r
112 b forces interpration as raw binary\r
113 a forces conversion from ascii to 029 coding, tabs are left alone\r
114 af forces 029 ascii conversion, and interprets tabs in Fortran mode\r
115 aa forces 029 ascii conversion, and interprets tabs in 1130 Assembler mode\r
116 at forces 029 ascii conversion, and interprets tabs with settings every 8 spaces\r
117\r
118 If "a" or "b" mode is not specified, the device mode setting is used. In this case,\r
119 if the mode is "auto", the simulator will select binary or 029 by inspecting each\r
120 file in turn.\r
121\r
122 If a tab mode is not specified, tabs are left unmolested (and are treated as invalid characters)\r
123\r
124 Example:\r
125 \r
126 attach cr @decklist\r
127\r
128 reads filenames from file "decklist," which might contain:\r
129 \r
130 file01.for xf\r
131 file02.dat a\r
132 file03 bin b\r
133 file04 bin\r
134\r
135 ('a' means 029, so, if you need 026 coding, specify the\r
136 device default as the correct 026 code and omit the 'a' on the text files lines).\r
137\r
138 Literal text cards can be entered in deck files by preceding an input\r
139 line with an exclamation point. For example,\r
140\r
141 !// JOB\r
142 !// FOR\r
143 program.for\r
144 !// XEQ\r
145 program.dat\r
146 \r
147 looks like two literal supervisor control cards, followed by the contents\r
148 of file program.for, followed by an // XEQ card, followed by the contents\r
149 of file program.dat.\r
150\r
151 %n tokens are not replaced in literal cards.\r
152\r
153 The line\r
154\r
155 !BREAK\r
156 \r
157 has a special meaning: when read from a deck file, it stops the\r
158 emulator as if "IMMEDIATE STOP" was pressed. This returns control to\r
159 the command interpreter or to the current DO command script.\r
160 \r
161* Card image format.\r
162 Card files can be ascii text or binary. There are several ASCII modes:\r
163 CODE_029, CODE_26F, etc, corresponding to different code sets.\r
164 Punch and reader modes can be set independently using\r
165\r
166 set cr binary set cp binary *\r
167 set cr 029 set cp 029\r
168 set cr 026f set cp 026f\r
169 set cr 026c set cp 026c\r
170 set cr auto *\r
171\r
172 (* = default mode)\r
173\r
174 In "auto" mode, the card reader will examine the first 160 bytes of\r
175 the deck and guess whether the card is binary or 029 text encoded.\r
176 When a deck file is used with auto mode, the simulator guesses for\r
177 each file named in the deck file.\r
178\r
179* Tab formatting. The attach command and deckfile entries can indicate\r
180 that tabs are to be converted to spaces, to help let you write free-form\r
181 source files. There are three tab conversion modes, which are set\r
182 with the attach command or in a decklist, as discussed earlier\r
183\r
184 Fortran mode:\r
185 Input lines of the form\r
186\r
187 [label]<tab>statement\r
188\r
189 or\r
190\r
191 [label]<tab>+continuation\r
192\r
193 (where + is any nonalphabetic character) are rearranged in the\r
194 appropriate manner:\r
195\r
196 1 2\r
197 12345678901234567890...\r
198 ------------------------\r
199 label statement\r
200 label+continuation\r
201\r
202 However, you must take care that you don't end up with statement text after column 72.\r
203\r
204 Input lines with * or C in column 1 (comments and directives) and lines without tabs\r
205 are left alone.\r
206\r
207 (The ! escape is not used before Fortran directives as before Assembler directives)\r
208\r
209 Assembler mode:\r
210 Input lines of the form\r
211\r
212 [label]<whitespace>[opcode]<tab>[tag][L]<tab>[argument]\r
213\r
214 are rearranged so that the input fields are placed in the appropriate columns\r
215\r
216 The label must start on the first character of the line. If there is no label, \r
217 the first character(s) before the opcode must be whitespace. Following the opcode, there\r
218 MUST be a tab character, followed by the format and tag. Following the format and tag \r
219 may be exactly one whitespace character, and then starts the argument.\r
220\r
221 Input lines with * in column 1 and blank lines are turned into Assembler comments,\r
222 with the * in the Opcode field.\r
223\r
224 Assembler directive lines at the beginning of the deck must be preceded by\r
225 ! to indicate that they are not comments. For example,\r
226\r
227 !*LIST\r
228 * This is a comment\r
229\r
230 Plain Tab mode:\r
231 Tabs are replaced with spaces. Tab settings are assumed to be eight characters wide,\r
232 as is standard for vi, notepad, etc.\r
233\r
234* CGI mode note: The command\r
235\r
236 attach cr (stdin)\r
237\r
238 will attach the card reader to stdin. However, this is not compatible\r
239 with the default encoding autodetect feature, so the command must be\r
240 preceded with\r
241\r
242 set cr 029\r
243\r
244* -----------------------------------------------------------------------\r
245* PROGRAMMING NOTES\r
246* -----------------------------------------------------------------------\r
247\r
248NOTE - there is a problem with this code. The Device Status Word (DSW) is\r
249computed from current conditions when requested by an XIO load status\r
250command; the value of DSW available to the simulator's examine & save\r
251commands may NOT be accurate. This should probably be fixed. (I think there's\r
252a way to have the expression evaluator call a routine? That would be one\r
253way to solve the problem, the other is to keep DSW up-to-date all the time).\r
254\r
255 The 1442 card read/punch has several cycles:\r
256\r
257 feed cycle: moves card from hopper to read station\r
258 card from read station to punch station\r
259 card from punch station to stacker\r
260\r
261 read or punch: operates on card at read or punch station (but not both).\r
262\r
263 The simulator requires input cards to be read from the file attached\r
264 to the card reader unit. A feed cycle reads one line (text mode) or\r
265 160 bytes (binary mode) from the input file to the read station buffer,\r
266 copies the read station buffer to the punch station buffer, and if\r
267 the punch unit is attached to a file, writes the punch station buffer to\r
268 the output file.\r
269 \r
270 The read and punch cycles operate on the appropriate card buffer.\r
271\r
272 Detaching the card punch flushes the punch station buffer if necessary.\r
273\r
274 As does the 1442, a read or punch cycle w/o a feed cycle causes a\r
275 feed cycle first.\r
276\r
277 A feed cycle on an empty deck (reader unattaced or at EOF) clears\r
278 the appropriate buffer, so you can punch w/o attaching a deck to\r
279 the card reader.\r
280\r
281 (Note: Carl Claunch determined by examining DUP code that a feed cycle\r
282 does not cause an operation complete interrupt).\r
283\r
284-- -- this may need changing depending on how things work in hardware. TBD.\r
285|| A read cycle on an empty deck causes an error.\r
286|| Hmmm -- what takes the place of the Start button on\r
287-- the card reader?\r
288\r
289 Binary format is stored using fxwrite of short ints, in this format:\r
290\r
291 1 1\r
292 2 2 0 1 2 3 4 5 6 7 8 9\r
293 * * * * * * * * * * * * 0 0 0 0\r
294\r
295 MSB LSB\r
296 byte 0 [ 6] [ 7] [ 8] [ 9] 0 0 0 0\r
297 byte 1 [12] [11] [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] \r
298\r
299 This means we can read words (little endian) and get this in memory:\r
300\r
301 12 11 0 1 2 3 4 5 6 7 8 9 - - - -\r
302\r
303 which is what the 1130 sees.\r
304\r
305 ASCII can be read in blocks of 80 characters but can be terminated by newline prematurely.\r
306\r
307 Booting: card reader IPL loads 80 columns (1 card) into memory starting\r
308 at location 0 in a split fashion:\r
309\r
310 ________________ _ _ _\r
311 /\r
312 12 |\r
313 11 |\r
314 0 |\r
315 1 |\r
316 2 |\r
317 3 | Punched card\r
318 4 |\r
319 5 |\r
320 6 |\r
321 7 |\r
322 8 |\r
323 9 |\r
324 +------------------ - - -\r
325\r
326 12 11 0 1 2 3 4 5 6 7 8 9 <- columns of cold start card\r
327 | | | | | 0 0 0 / \ | | | | | |\r
328 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
329 | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|\r
330 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
331 | OPCODE | F| Tag | DISPLACEMENT |\r
332 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\r
333\r
334 The zeros mean that all IPL instructions are short form,\r
335 nonindexed. The 3 column is repeated in bits 8 and 9 so\r
336 it's a sign bit.\r
337\r
338 Boot command on a binary deck does this. Boot on an unattached\r
339 reader loads one of the built-in boot card images. Boot with an ASCII\r
340 deck isn't allowed.\r
341*/\r
342\r
343#define READ_DELAY 35 /* see how small a number we can get away with */\r
344#define PUNCH_DELAY 35\r
345#define FEED_DELAY 25\r
346#define READ_2501_DELAY 500\r
347\r
348/* umm, this is a weird little future project of mine. */\r
349\r
350#define ENABLE_PHYSICAL_CARD_READER_SUPPORT\r
351\r
352extern int32 sim_switches;\r
353extern UNIT cpu_unit;\r
354\r
355static t_stat cr_svc (UNIT *uptr);\r
356static t_stat cr_reset (DEVICE *dptr);\r
357static t_stat cr_set_code (UNIT *uptr, int32 match, char *cptr, void *desc);\r
358static t_stat cr_attach (UNIT *uptr, char *cptr);\r
359static int32 guess_cr_code (void);\r
360static void feedcycle (t_bool load, t_bool punching);\r
361\r
362static t_stat cp_reset (DEVICE *dptr);\r
363static t_stat cp_set_code (UNIT *uptr, int32 match, char *cptr, void *desc);\r
364static t_stat cp_attach (UNIT *uptr, char *cptr);\r
365static t_stat cp_detach (UNIT *uptr);\r
366\r
367static int16 cr_dsw = 0; /* device status word */\r
368static int32 cr_wait = READ_DELAY; /* read per-column wait */\r
369static int32 cr_wait2501 = READ_2501_DELAY; /* read card wait for 2501 reader */\r
370static int32 cf_wait = PUNCH_DELAY; /* punch per-column wait */\r
371static int32 cp_wait = FEED_DELAY; /* feed op wait */\r
372static int32 cr_count= 0; /* read and punch card count */\r
373static int32 cp_count= 0;\r
374static int32 cr_addr = 0; /* 2501 reader transfer address */\r
375static int32 cr_cols = 0; /* 2501 reader column count */\r
376\r
377#define UNIT_V_OPERATION (UNIT_V_UF + 0) /* operation in progress */\r
378#define UNIT_V_CODE (UNIT_V_UF + 2) /* three bits */\r
379#define UNIT_V_CR_EMPTY (UNIT_V_UF + 5) /* NOTE: THIS MUST BE SET IN ibm1130_gui.c too */\r
380#define UNIT_V_SCRATCH (UNIT_V_UF + 6)\r
381#define UNIT_V_QUIET (UNIT_V_UF + 7)\r
382#define UNIT_V_DEBUG (UNIT_V_UF + 8)\r
383#define UNIT_V_PHYSICAL (UNIT_V_UF + 9) /* NOTE: THIS MUST BE SET IN ibm1130_gui.c too */\r
384#define UNIT_V_LASTPUNCH (UNIT_V_UF + 10) /* used in unit_cp only */\r
385#define UNIT_V_LOWERCASE (UNIT_V_UF + 10) /* used in unit_cr only */\r
386#define UNIT_V_ACTCODE (UNIT_V_UF + 11) /* used in unit_cr only, 3 bits */\r
387#define UNIT_V_2501 (UNIT_V_UF + 14)\r
388\r
389#define UNIT_OP (3u << UNIT_V_OPERATION) /* two bits */\r
390#define UNIT_CODE (7u << UNIT_V_CODE) /* three bits */\r
391#define UNIT_CR_EMPTY (1u << UNIT_V_CR_EMPTY) \r
392#define UNIT_SCRATCH (1u << UNIT_V_SCRATCH) /* temp file */\r
393#define UNIT_QUIET (1u << UNIT_V_QUIET)\r
394#define UNIT_DEBUG (1u << UNIT_V_DEBUG)\r
395#define UNIT_PHYSICAL (1u << UNIT_V_PHYSICAL)\r
396#define UNIT_LASTPUNCH (1u << UNIT_V_LASTPUNCH)\r
397#define UNIT_LOWERCASE (1u << UNIT_V_LOWERCASE) /* permit lowercase input (needed for APL) */\r
398#define UNIT_ACTCODE (7u << UNIT_V_ACTCODE)\r
399#define UNIT_2501 (1u << UNIT_V_2501)\r
400\r
401#define OP_IDLE (0u << UNIT_V_OPERATION)\r
402#define OP_READING (1u << UNIT_V_OPERATION)\r
403#define OP_PUNCHING (2u << UNIT_V_OPERATION)\r
404#define OP_FEEDING (3u << UNIT_V_OPERATION)\r
405\r
406#define SET_OP(op) {cr_unit.flags &= ~UNIT_OP; cr_unit.flags |= (op);}\r
407#define CURRENT_OP (cr_unit.flags & UNIT_OP)\r
408\r
409#define CODE_AUTO (0u << UNIT_V_CODE)\r
410#define CODE_029 (1u << UNIT_V_CODE)\r
411#define CODE_026F (2u << UNIT_V_CODE)\r
412#define CODE_026C (3u << UNIT_V_CODE)\r
413#define CODE_BINARY (4u << UNIT_V_CODE)\r
414\r
415#define GET_CODE(un) (un.flags & UNIT_CODE)\r
416#define SET_CODE(un,cd) {un.flags &= ~UNIT_CODE; un.flags |= (cd);}\r
417\r
418#define ACTCODE_029 (CODE_029 << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* these are used ONLY in MTAB. Elsewhere */\r
419#define ACTCODE_026F (CODE_026F << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* we use values CODE_xxx with macros */\r
420#define ACTCODE_026C (CODE_026C << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* GET_ACTCODE and SET_ACTCODE. */\r
421#define ACTCODE_BINARY (CODE_BINARY << (UNIT_V_ACTCODE-UNIT_V_CODE))\r
422\r
423 /* get/set macros for actual-code field, these use values like CODE_029 meant for the UNIT_CODE field */\r
424#define GET_ACTCODE(un) ((un.flags & UNIT_ACTCODE) >> (UNIT_V_ACTCODE-UNIT_V_CODE))\r
425#define SET_ACTCODE(un,cd) {un.flags &= ~UNIT_ACTCODE; un.flags |= (cd) << (UNIT_V_ACTCODE-UNIT_V_CODE);}\r
426\r
427#define COLUMN u4 /* column field in unit record */\r
428\r
429UNIT cr_unit = { UDATA (&cr_svc, UNIT_ATTABLE|UNIT_ROABLE|UNIT_CR_EMPTY, 0) };\r
430UNIT cp_unit = { UDATA (NULL, UNIT_ATTABLE, 0) };\r
431\r
432MTAB cr_mod[] = {\r
433 { UNIT_CODE, CODE_029, "029", "029", &cr_set_code},\r
434 { UNIT_CODE, CODE_026F, "026F", "026F", &cr_set_code},\r
435 { UNIT_CODE, CODE_026C, "026C", "026C", &cr_set_code},\r
436 { UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cr_set_code},\r
437 { UNIT_CODE, CODE_AUTO, "AUTO", "AUTO", &cr_set_code},\r
438 { UNIT_ACTCODE, ACTCODE_029, "(029)", NULL, NULL}, /* display-only, shows current mode */\r
439 { UNIT_ACTCODE, ACTCODE_026F, "(026F)", NULL, NULL},\r
440 { UNIT_ACTCODE, ACTCODE_026C, "(026C)", NULL, NULL},\r
441 { UNIT_ACTCODE, ACTCODE_BINARY, "(BINARY)", NULL, NULL},\r
442 { UNIT_2501, 0, "1442", "1442", NULL},\r
443 { UNIT_2501, UNIT_2501, "2501", "2501", NULL},\r
444 { 0 } };\r
445\r
446MTAB cp_mod[] = {\r
447 { UNIT_CODE, CODE_029, "029", "029", &cp_set_code},\r
448 { UNIT_CODE, CODE_026F, "026F", "026F", &cp_set_code},\r
449 { UNIT_CODE, CODE_026C, "026C", "026C", &cp_set_code},\r
450 { UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cp_set_code},\r
451 { 0 } };\r
452\r
453REG cr_reg[] = {\r
454 { HRDATA (CRDSW, cr_dsw, 16) }, /* device status word */\r
455 { DRDATA (CRTIME, cr_wait, 24), PV_LEFT }, /* operation wait for 1442 column read*/\r
456 { DRDATA (2501TIME, cr_wait2501, 24), PV_LEFT }, /* operation wait for 2501 whole card read*/\r
457 { DRDATA (CFTIME, cf_wait, 24), PV_LEFT }, /* operation wait */\r
458 { DRDATA (CRCOUNT, cr_count, 32),PV_LEFT }, /* number of cards read since last attach cmd */\r
459 { HRDATA (CRADDR, cr_addr, 32) }, /* 2501 reader transfer address */\r
460 { HRDATA (CRCOLS, cr_cols, 32) }, /* 2501 reader column count */\r
461 { NULL } };\r
462\r
463REG cp_reg[] = {\r
464 { DRDATA (CPTIME, cp_wait, 24), PV_LEFT }, /* operation wait */\r
465 { DRDATA (CPCOUNT, cp_count, 32),PV_LEFT }, /* number of cards punched since last attach cmd */\r
466 { NULL } };\r
467\r
468DEVICE cr_dev = {\r
469 "CR", &cr_unit, cr_reg, cr_mod,\r
470 1, 16, 16, 1, 16, 16,\r
471 NULL, NULL, cr_reset,\r
472 cr_boot, cr_attach, cr_detach};\r
473\r
474DEVICE cp_dev = {\r
475 "CP", &cp_unit, cp_reg, cp_mod,\r
476 1, 16, 16, 1, 16, 16,\r
477 NULL, NULL, cp_reset,\r
478 NULL, cp_attach, cp_detach};\r
479\r
480#define CR_DSW_1442_READ_RESPONSE 0x8000 /* device status word bits */\r
481#define CR_DSW_1442_PUNCH_RESPONSE 0x4000\r
482#define CR_DSW_1442_ERROR_CHECK 0x2000\r
483#define CR_DSW_1442_LAST_CARD 0x1000\r
484#define CR_DSW_1442_OP_COMPLETE 0x0800\r
485#define CR_DSW_1442_FEED_CHECK 0x0100\r
486#define CR_DSW_1442_BUSY 0x0002\r
487#define CR_DSW_1442_NOT_READY 0x0001\r
488\r
489#define CR_DSW_2501_ERROR_CHECK 0x2000 /* DSW for 2501 reader */\r
490#define CR_DSW_2501_LAST_CARD 0x1000\r
491#define CR_DSW_2501_OP_COMPLETE 0x0800\r
492#define CR_DSW_2501_BUSY 0x0002\r
493#define CR_DSW_2501_NOT_READY 0x0001\r
494\r
495typedef struct {\r
496 uint16 hollerith;\r
497 unsigned char ascii;\r
498} CPCODE;\r
499\r
500static CPCODE cardcode_029[] =\r
501{\r
502 0x0000, ' ',\r
503 0x8000, '&', /* + in 026 Fortran */\r
504 0x4000, '-',\r
505 0x2000, '0',\r
506 0x1000, '1',\r
507 0x0800, '2',\r
508 0x0400, '3',\r
509 0x0200, '4',\r
510 0x0100, '5',\r
511 0x0080, '6',\r
512 0x0040, '7',\r
513 0x0020, '8',\r
514 0x0010, '9',\r
515 0x9000, 'A',\r
516 0x8800, 'B',\r
517 0x8400, 'C',\r
518 0x8200, 'D',\r
519 0x8100, 'E',\r
520 0x8080, 'F',\r
521 0x8040, 'G',\r
522 0x8020, 'H',\r
523 0x8010, 'I',\r
524 0x5000, 'J',\r
525 0x4800, 'K',\r
526 0x4400, 'L',\r
527 0x4200, 'M',\r
528 0x4100, 'N',\r
529 0x4080, 'O',\r
530 0x4040, 'P',\r
531 0x4020, 'Q',\r
532 0x4010, 'R',\r
533 0x3000, '/',\r
534 0x2800, 'S',\r
535 0x2400, 'T',\r
536 0x2200, 'U',\r
537 0x2100, 'V',\r
538 0x2080, 'W',\r
539 0x2040, 'X',\r
540 0x2020, 'Y',\r
541 0x2010, 'Z',\r
542 0x0820, ':',\r
543 0x0420, '#', /* = in 026 Fortran */\r
544 0x0220, '@', /* ' in 026 Fortran */\r
545 0x0120, '\'',\r
546 0x00A0, '=',\r
547 0x0060, '"',\r
548 0x8820, (unsigned char) '\xA2', /* cent, in MS-DOS encoding (this is in guess_cr_code as well) */\r
549 0x8420, '.',\r
550 0x8220, '<', /* ) in 026 Fortran */\r
551 0x8120, '(',\r
552 0x80A0, '+',\r
553 0x8060, '|',\r
554 0x4820, '!',\r
555 0x4420, '$',\r
556 0x4220, '*',\r
557 0x4120, ')',\r
558 0x40A0, ';',\r
559 0x4060, (unsigned char) '\xAC', /* not, in MS-DOS encoding (this is in guess_cr_code as well) */\r
560 0x2420, ',',\r
561 0x2220, '%', /* ( in 026 Fortran */\r
562 0x2120, '_',\r
563 0x20A0, '>',\r
564 0xB000, 'a',\r
565 0xA800, 'b',\r
566 0xA400, 'c',\r
567 0xA200, 'd',\r
568 0xA100, 'e',\r
569 0xA080, 'f',\r
570 0xA040, 'g',\r
571 0xA020, 'h',\r
572 0xA010, 'i',\r
573 0xD000, 'j',\r
574 0xC800, 'k',\r
575 0xC400, 'l',\r
576 0xC200, 'm',\r
577 0xC100, 'n',\r
578 0xC080, 'o',\r
579 0xC040, 'p',\r
580 0xC020, 'q',\r
581 0xC010, 'r',\r
582 0x6800, 's',\r
583 0x6400, 't',\r
584 0x6200, 'u',\r
585 0x6100, 'v',\r
586 0x6080, 'w',\r
587 0x6040, 'x',\r
588 0x6020, 'y',\r
589 0x6010, 'z', /* these odd punch codes are used by APL: */\r
590 0x1010, '\001', /* no corresponding ASCII using ^A */\r
591 0x0810, '\002', /* SYN using ^B */\r
592 0x0410, '\003', /* no corresponding ASCII using ^C */\r
593 0x0210, '\004', /* PUNCH ON using ^D */\r
594 0x0110, '\005', /* READER STOP using ^E */\r
595 0x0090, '\006', /* UPPER CASE using ^F */\r
596 0x0050, '\013', /* EOT using ^K */\r
597 0x0030, '\016', /* no corresponding ASCII using ^N */\r
598 0x1030, '\017', /* no corresponding ASCII using ^O */\r
599 0x0830, '\020', /* no corresponding ASCII using ^P */\r
600};\r
601\r
602static CPCODE cardcode_026F[] = /* 026 fortran */\r
603{\r
604 0x0000, ' ',\r
605 0x8000, '+',\r
606 0x4000, '-',\r
607 0x2000, '0',\r
608 0x1000, '1',\r
609 0x0800, '2',\r
610 0x0400, '3',\r
611 0x0200, '4',\r
612 0x0100, '5',\r
613 0x0080, '6',\r
614 0x0040, '7',\r
615 0x0020, '8',\r
616 0x0010, '9',\r
617 0x9000, 'A',\r
618 0x8800, 'B',\r
619 0x8400, 'C',\r
620 0x8200, 'D',\r
621 0x8100, 'E',\r
622 0x8080, 'F',\r
623 0x8040, 'G',\r
624 0x8020, 'H',\r
625 0x8010, 'I',\r
626 0x5000, 'J',\r
627 0x4800, 'K',\r
628 0x4400, 'L',\r
629 0x4200, 'M',\r
630 0x4100, 'N',\r
631 0x4080, 'O',\r
632 0x4040, 'P',\r
633 0x4020, 'Q',\r
634 0x4010, 'R',\r
635 0x3000, '/',\r
636 0x2800, 'S',\r
637 0x2400, 'T',\r
638 0x2200, 'U',\r
639 0x2100, 'V',\r
640 0x2080, 'W',\r
641 0x2040, 'X',\r
642 0x2020, 'Y',\r
643 0x2010, 'Z',\r
644 0x0420, '=',\r
645 0x0220, '\'',\r
646 0x8420, '.',\r
647 0x8220, ')',\r
648 0x4420, '$',\r
649 0x4220, '*',\r
650 0x2420, ',',\r
651 0x2220, '(',\r
652};\r
653\r
654static CPCODE cardcode_026C[] = /* 026 commercial */\r
655{\r
656 0x0000, ' ',\r
657 0x8000, '+',\r
658 0x4000, '-',\r
659 0x2000, '0',\r
660 0x1000, '1',\r
661 0x0800, '2',\r
662 0x0400, '3',\r
663 0x0200, '4',\r
664 0x0100, '5',\r
665 0x0080, '6',\r
666 0x0040, '7',\r
667 0x0020, '8',\r
668 0x0010, '9',\r
669 0x9000, 'A',\r
670 0x8800, 'B',\r
671 0x8400, 'C',\r
672 0x8200, 'D',\r
673 0x8100, 'E',\r
674 0x8080, 'F',\r
675 0x8040, 'G',\r
676 0x8020, 'H',\r
677 0x8010, 'I',\r
678 0x5000, 'J',\r
679 0x4800, 'K',\r
680 0x4400, 'L',\r
681 0x4200, 'M',\r
682 0x4100, 'N',\r
683 0x4080, 'O',\r
684 0x4040, 'P',\r
685 0x4020, 'Q',\r
686 0x4010, 'R',\r
687 0x3000, '/',\r
688 0x2800, 'S',\r
689 0x2400, 'T',\r
690 0x2200, 'U',\r
691 0x2100, 'V',\r
692 0x2080, 'W',\r
693 0x2040, 'X',\r
694 0x2020, 'Y',\r
695 0x2010, 'Z',\r
696 0x0420, '=',\r
697 0x0220, '\'',\r
698 0x8420, '.',\r
699 0x8220, ')',\r
700 0x4420, '$',\r
701 0x4220, '*',\r
702 0x2420, ',',\r
703 0x2220, '(',\r
704};\r
705\r
706extern int cgi;\r
707extern void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *arg[]);\r
708\r
709static int16 ascii_to_card[256];\r
710\r
711static CPCODE *cardcode;\r
712static int ncardcode;\r
713static FILE *deckfile = NULL;\r
714static char tempfile[128];\r
715static int any_punched = 0;\r
716\r
717#define MAXARGLEN 80 /* max length of a saved attach command argument */\r
718#define MAXARGS 10 /* max number of arguments to save */\r
719static char list_save[MAXARGS][MAXARGLEN], *list_arg[MAXARGLEN];\r
720static int list_nargs = 0;\r
721static char* (*tab_proc)(char*) = NULL; /* tab reformatting routine */\r
722\r
723static uint16 punchstation[80];\r
724static uint16 readstation[80];\r
725static enum {STATION_EMPTY, STATION_LOADED, STATION_READ, STATION_PUNCHED} punchstate = STATION_EMPTY, readstate = STATION_EMPTY;\r
726\r
727static t_bool nextdeck (void);\r
728static void checkdeck (void);\r
729\r
730static t_stat pcr_attach(UNIT *uptr, char *devname);\r
731static t_stat pcr_detach(UNIT *uptr);\r
732static t_stat pcr_svc(UNIT *uptr);\r
733static void pcr_xio_sense(int modify);\r
734static void pcr_xio_feedcycle(void);\r
735static void pcr_xio_startread(void);\r
736static void pcr_reset(void);\r
737\r
738/* lookup_codetable - use code flag setting to get code table pointer and length */\r
739\r
740static t_bool lookup_codetable (int32 match, CPCODE **pcode, int *pncode)\r
741{\r
742 switch (match) {\r
743 case CODE_029:\r
744 *pcode = cardcode_029;\r
745 *pncode = sizeof(cardcode_029) / sizeof(CPCODE);\r
746 break;\r
747\r
748 case CODE_026F:\r
749 *pcode = cardcode_026F;\r
750 *pncode = sizeof(cardcode_026F) / sizeof(CPCODE);\r
751 break;\r
752\r
753 case CODE_026C:\r
754 *pcode = cardcode_026C;\r
755 *pncode = sizeof(cardcode_026C) / sizeof(CPCODE);\r
756 break;\r
757\r
758 case CODE_BINARY:\r
759 *pcode = NULL;\r
760 *pncode = 0;\r
761 break;\r
762\r
763 default:\r
764 printf("Eek! Undefined code table index");\r
765 return FALSE;\r
766 }\r
767 return TRUE;\r
768}\r
769\r
770t_stat set_active_cr_code (int match)\r
771{\r
772 CPCODE *code;\r
773 int i, ncode;\r
774\r
775 SET_ACTCODE(cr_unit, match);\r
776\r
777 if (! lookup_codetable(match, &code, &ncode))\r
778 return SCPE_ARG;\r
779\r
780 memset(ascii_to_card, 0, sizeof(ascii_to_card));\r
781\r
782 for (i = 0; i < ncode; i++) /* set ascii to card code table */\r
783 ascii_to_card[code[i].ascii] = code[i].hollerith;\r
784\r
785 return SCPE_OK;\r
786}\r
787\r
788static t_stat cr_set_code (UNIT *uptr, int32 match, char *cptr, void *desc)\r
789{\r
790 if (match == CODE_AUTO)\r
791 match = guess_cr_code();\r
792\r
793 return set_active_cr_code(match);\r
794}\r
795\r
796static int32 guess_cr_code (void)\r
797{\r
798 int i;\r
799 long filepos;\r
800 int32 guess;\r
801 union {\r
802 uint16 w[80]; /* one card image, viewed as 80 short words */\r
803 char c[160]; /* same, viewed as 160 characters */\r
804 } line;\r
805\r
806 /* here, we can see if the attached file is binary or ascii and auto-set the\r
807 * mode. If we the file is a binary deck, we should be able to read a record of 80 short\r
808 * words, and the low 4 bits of each word must be zero. If the file was an ascii deck,\r
809 * then these low 4 bits are the low 4 bits of every other character in the first 160\r
810 * chararacters of the file. They would all only be 0 if all of these characters were\r
811 * in the following set: {NUL ^P space 0 @ P ` p} . It seems very unlikely that\r
812 * this would happen, as even if the deck consisted of untrimmed card images and\r
813 * the first two lines were blank, the 81'st character would be a newline, and it would\r
814 * appear at one of the every-other characters seen on little-endian machines, anyway.\r
815 * So: if the code mode is AUTO, we can use this test and select either BINARY or 029.\r
816 * Might as well also check for the all-blanks and newlines case in case this is a\r
817 * big-endian machine.\r
818 */\r
819\r
820\r
821 guess = CODE_029; /* assume ASCII, 029 */\r
822\r
823 if ((cr_unit.flags & UNIT_ATT) && (cr_unit.fileref != NULL)) {\r
824 filepos = ftell(cr_unit.fileref); /* remember current position in file */\r
825 fseek(cr_unit.fileref, 0, SEEK_SET); /* go to first record of file */\r
826 /* read card image; if file too short, leave guess set to 029 */\r
827 if (fxread(line.w, sizeof(line.w[0]), 80, cr_unit.fileref) == 80) {\r
828 guess = CODE_BINARY; /* we got a card image, assume binary */\r
829\r
830 for (i = 0; i < 80; i++) { /* make sure low bits are zeroes, which our binary card format promises */\r
831 if (line.w[i] & 0x000F) {\r
832 guess = CODE_029; /* low bits set, must be ascii text */\r
833 break;\r
834 }\r
835 }\r
836\r
837 if (guess == CODE_BINARY) { /* if we saw no low bits, it could have been all spaces. */\r
838 guess = CODE_029; /* so now assume file is text */\r
839 for (i = 0; i < 160; i++) { /* ensure all 160 characters are 7-bit ASCII (or not or cent) */\r
840 /* 3.0-3, changed test for > 0x7f to & 0x80 */\r
841 if ((strchr("\r\n\t\xA2\xAC", line.c[i]) == NULL) && ((line.c[i] < ' ') || (line.c[i] & 0x80))) {\r
842 guess = CODE_BINARY; /* oops, null or weird character, it's binary after all */\r
843 break;\r
844 }\r
845 }\r
846 }\r
847 }\r
848\r
849 fseek(cr_unit.fileref, filepos, SEEK_SET); /* return to original position */\r
850 }\r
851\r
852 return guess;\r
853}\r
854\r
855static t_stat cp_set_code (UNIT *uptr, int32 match, char *cptr, void *desc)\r
856{\r
857 CPCODE *code;\r
858 int ncode;\r
859\r
860 if (! lookup_codetable(match, &code, &ncode))\r
861 return SCPE_ARG;\r
862\r
863 cardcode = code; /* save code table for punch output */\r
864 ncardcode = ncode;\r
865\r
866 return SCPE_OK;\r
867}\r
868\r
869t_stat load_cr_boot (int drvno, int switches)\r
870{\r
871 int i;\r
872 char *name, msg[80];\r
873 t_bool expand;\r
874 uint16 word, *boot;\r
875 static uint16 dms_boot_data[] = { /* DMSV2M12, already expanded to 16 bits */\r
876 0xc80a, 0x18c2, 0xd008, 0xc019, 0x8007, 0xd017, 0xc033, 0x100a,\r
877 0xd031, 0x7015, 0x000c, 0xe800, 0x0020, 0x08f8, 0x4828, 0x7035,\r
878 0x70fa, 0x4814, 0xf026, 0x2000, 0x8800, 0x9000, 0x9800, 0xa000,\r
879 0xb000, 0xb800, 0xb810, 0xb820, 0xb830, 0xb820, 0x3000, 0x08ea,\r
880 0xc0eb, 0x4828, 0x70fb, 0x9027, 0x4830, 0x70f8, 0x8001, 0xd000,\r
881 0xc0f4, 0xd0d9, 0xc01d, 0x1804, 0xe8d6, 0xd0d9, 0xc8e3, 0x18d3,\r
882 0xd017, 0x18c4, 0xd0d8, 0x9016, 0xd815, 0x90db, 0xe8cc, 0xd0ef,\r
883 0xc016, 0x1807, 0x0035, 0x00d0, 0xc008, 0x1803, 0xe8c4, 0xd00f,\r
884 0x080d, 0x08c4, 0x1003, 0x4810, 0x70d9, 0x3000, 0x08df, 0x3000,\r
885 0x7010, 0x00d1, 0x0028, 0x000a, 0x70f3, 0x0000, 0x00d0, 0xa0c0\r
886 };\r
887 static uint16 apl_boot_data[] = { /* APLIPL, already expanded */\r
888 0x7021, 0x3000, 0x7038, 0xa0c0, 0x0002, 0x4808, 0x0003, 0x0026,\r
889 0x0001, 0x0001, 0x000c, 0x0000, 0x0000, 0x0800, 0x48f8, 0x0027,\r
890 0x7002, 0x08f2, 0x3800, 0xe0fe, 0x18cc, 0x100e, 0x10c1, 0x4802,\r
891 0x7007, 0x4828, 0x7005, 0x4804, 0x7001, 0x70f3, 0x08e7, 0x70e1,\r
892 0x08ed, 0x70f1, 0xc0e0, 0x1807, 0xd0de, 0xc0df, 0x1801, 0xd0dd,\r
893 0x800d, 0xd00c, 0xc0e3, 0x1005, 0xe80a, 0xd009, 0xc0d8, 0x1008,\r
894 0xd0d6, 0xc0dd, 0x1008, 0x80d4, 0xd0da, 0x1000, 0xb000, 0x00f6,\r
895 0x70e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
896 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
897 0x9000, 0x4004, 0x40c0, 0x8001, 0x4004, 0x40c0, 0x0000, 0x0000 };\r
898 static uint16 aplp_boot_data[] = { /* APLIPL Privileged, already expanded */\r
899 0x7021, 0x3000, 0x7038, 0xa0c0, 0x0002, 0x4808, 0x0003, 0x0026,\r
900 0x0001, 0x0001, 0x000c, 0x0000, 0x0000, 0x0800, 0x48f8, 0x0027,\r
901 0x7002, 0x08f2, 0x3800, 0xe0fe, 0x18cc, 0x100e, 0x10c1, 0x4802,\r
902 0x7007, 0x4828, 0x7005, 0x4804, 0x7001, 0x70f3, 0x08e7, 0x70e1,\r
903 0x08ed, 0x70f1, 0xc0e0, 0x1807, 0xd0de, 0xc0df, 0x1801, 0xd0dd,\r
904 0x800d, 0xd00c, 0xc0e3, 0x1005, 0xe80a, 0xd009, 0xc0d8, 0x1008,\r
905 0xd0d6, 0xc0dd, 0x1008, 0x80d4, 0xd0da, 0x1002, 0xb000, 0x00f6,\r
906 0x70e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
907 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
908 0x9000, 0x4004, 0x40c0, 0x8001, 0x4004, 0x40c0, 0x4004, 0x4001\r
909 };\r
910\r
911 if ((switches & SWMASK('A')) && (switches & SWMASK('P'))) {\r
912 boot = aplp_boot_data;\r
913 name = "APL\\1130 Privileged";\r
914 expand = FALSE;\r
915 }\r
916 else if (switches & SWMASK('A')) {\r
917 boot = apl_boot_data;\r
918 name = "APL\\1130";\r
919 expand = FALSE;\r
920 }\r
921 else {\r
922 boot = dms_boot_data;\r
923 name = "DMS V2M12";\r
924 expand = FALSE;\r
925 }\r
926\r
927 if (drvno >= 0 && ! (switches & SWMASK('S'))) /* if specified, set toggle switches to disk drive no */\r
928 CES = drvno; /* so BOOT DSK1 will work correctly (DMS boot uses this) */\r
929 /* but do not touch switches if -S was specified */\r
930\r
931 IAR = 0; /* clear IAR */\r
932\r
933 for (i = 0; i < 80; i++) { /* store the boot image to core words 0..79 */\r
934 word = boot[i]; /* expanding the 12-bit card data to 16 bits if not already expanded */\r
935 if (expand)\r
936 word = (word & 0xF800) | ((word & 0x0400) ? 0x00C0 : 0x0000) | ((word & 0x03F0) >> 4);\r
937\r
938 WriteW(i, word);\r
939 }\r
940 /* quiet switch or CGI mode inhibit the boot remark */\r
941 if (((switches & SWMASK('Q')) == 0) && ! cgi) { /* 3.0-3, parenthesized & operation, per lint check */\r
942 sprintf(msg, "Loaded %s cold start card\n", name);\r
943\r
944#ifdef GUI_SUPPORT\r
945 remark_cmd(msg);\r
946#else\r
947 printf(msg);\r
948#endif\r
949 }\r
950\r
951 return SCPE_OK;\r
952}\r
953\r
954t_stat cr_boot (int unitno, DEVICE *dptr)\r
955{\r
956 t_stat rval;\r
957 int i;\r
958\r
959 if ((rval = reset_all(0)) != SCPE_OK)\r
960 return rval;\r
961\r
962 if (! (cr_unit.flags & UNIT_ATT)) /* no deck; load standard boot anyway */\r
963 return load_cr_boot(-1, 0);\r
964\r
965 if (GET_ACTCODE(cr_unit) != CODE_BINARY) {\r
966 printf("Can only boot from card reader when set to BINARY mode");\r
967 return SCPE_IOERR;\r
968 }\r
969\r
970 if (cr_unit.fileref == NULL) /* this will happen if no file in deck file can be opened */\r
971 return SCPE_IOERR;\r
972\r
973 feedcycle(TRUE, FALSE);\r
974\r
975/* if (fxread(buf, sizeof(buf[0]), 80, cr_unit.fileref) != 80) */\r
976/* return SCPE_IOERR; */\r
977\r
978 IAR = 0; /* Program Load sets IAR = 0 */\r
979\r
980 for (i = 0; i < 80; i++) /* shift 12 bits into 16 */\r
981 WriteW(i, (readstation[i] & 0xF800) | ((readstation[i] & 0x0400) ? 0x00C0 : 0x0000) | ((readstation[i] & 0x03F0) >> 4));\r
982\r
983 return SCPE_OK;\r
984}\r
985\r
986char card_to_ascii (uint16 hol)\r
987{\r
988 int i;\r
989\r
990 for (i = 0; i < ncardcode; i++)\r
991 if (cardcode[i].hollerith == hol)\r
992 return (char) cardcode[i].ascii;\r
993\r
994 return '?';\r
995}\r
996\r
997/* hollerith_to_ascii - provide a generic conversion for simulator debugging */\r
998\r
999char hollerith_to_ascii (uint16 hol)\r
1000{\r
1001 int i;\r
1002\r
1003 for (i = 0; i < ncardcode; i++)\r
1004 if (cardcode_029[i].hollerith == hol)\r
1005 return (char) cardcode[i].ascii;\r
1006\r
1007 return ' ';\r
1008}\r
1009\r
1010/* feedcycle - move cards to next station */\r
1011\r
1012static void feedcycle (t_bool load, t_bool punching)\r
1013{\r
1014 char buf[84], *x, *result;\r
1015 int i, nread, nwrite, ch;\r
1016\r
1017 /* write punched card if punch is attached to a file */\r
1018 if (cp_unit.flags & UNIT_ATT) {\r
1019 if (any_punched && punchstate != STATION_EMPTY) {\r
1020 if (GET_CODE(cp_unit) == CODE_BINARY) {\r
1021 fxwrite(punchstation, sizeof(punchstation[0]), 80, cp_unit.fileref);\r
1022 }\r
1023 else {\r
1024 for (i = 80; --i >= 0; ) { /* find last nonblank column */\r
1025 if (punchstation[i] != 0)\r
1026 break;\r
1027 }\r
1028\r
1029 /* i is now index of last character to output or -1 if all blank */\r
1030\r
1031 for (nwrite = 0; nwrite <= i; nwrite++) { /* convert characters */\r
1032 buf[nwrite] = card_to_ascii(punchstation[nwrite]);\r
1033 }\r
1034\r
1035 /* nwrite is now number of characters to output */\r
1036\r
1037#ifdef WIN32\r
1038 buf[nwrite++] = '\r'; /* add CR before NL for microsoft */\r
1039#endif\r
1040 buf[nwrite++] = '\n'; /* append newline */\r
1041 fxwrite(buf, sizeof(char), nwrite, cp_unit.fileref);\r
1042 }\r
1043 }\r
1044\r
1045 cp_count++;\r
1046 }\r
1047\r
1048 if (! load) /* all we wanted to do was flush the punch */\r
1049 return;\r
1050\r
1051 /* slide cards from reader to punch. If we know we're punching,\r
1052 * generate a blank card in any case. Otherwise, it should take two feed\r
1053 * cycles to get a read card from the hopper to punch station. Also when\r
1054 * the reader is a 2501, we assume the 1442 is a punch only */\r
1055\r
1056 if (readstate == STATION_EMPTY || (cr_unit.flags & UNIT_2501)) {\r
1057 if (punching) {\r
1058 memset(punchstation, 0, sizeof(punchstation));\r
1059 punchstate = STATION_LOADED;\r
1060 }\r
1061 else\r
1062 punchstate = STATION_EMPTY;\r
1063 }\r
1064 else {\r
1065 memcpy(punchstation, readstation, sizeof(punchstation));\r
1066 punchstate = STATION_LOADED;\r
1067 }\r
1068\r
1069 /* load card into read station */\r
1070\r
1071again: /* jump here if we've loaded a new deck after emptying the previous one */\r
1072\r
1073 if (cr_unit.flags & UNIT_ATT) {\r
1074\r
1075 memset(readstation, 0, sizeof(readstation)); /* blank out the card image */\r
1076\r
1077 if (cr_unit.fileref == NULL)\r
1078 nread = 0;\r
1079\r
1080 else if (GET_ACTCODE(cr_unit) == CODE_BINARY) /* binary read is straightforward */\r
1081 nread = fxread(readstation, sizeof(readstation[0]), 80, cr_unit.fileref);\r
1082\r
1083 else if (fgets(buf, sizeof(buf), cr_unit.fileref) == NULL) /* read up to 80 chars */\r
1084 nread = 0; /* hmm, end of file */\r
1085\r
1086 else { /* check for CRLF or newline */\r
1087 if ((x = strchr(buf, '\r')) == NULL)\r
1088 x = strchr(buf, '\n');\r
1089\r
1090 if (x == NULL) { /* there were no delimiters, burn rest of line */\r
1091 while ((ch = getc(cr_unit.fileref)) != EOF) { /* get character */\r
1092 if (ch == '\n') /* newline, done */\r
1093 break;\r
1094\r
1095 if (ch == '\r') { /* CR, try to take newline too */\r
1096 ch = getc(cr_unit.fileref);\r
1097 if (ch != EOF && ch != '\n') /* hmm, put it back */\r
1098 ungetc(ch, cr_unit.fileref);\r
1099\r
1100 break;\r
1101 }\r
1102 }\r
1103 if ((nread = strlen(buf)) > 80) /* use the line as read, at most 80 characters */\r
1104 nread = 80;\r
1105 }\r
1106 else\r
1107 nread = x-buf; /* reduce length of string */\r
1108\r
1109 if (! (cr_unit.flags & UNIT_LOWERCASE))\r
1110 upcase(buf); /* force uppercase */\r
1111\r
1112 if (tab_proc != NULL) { /* apply tab editing, if specified */\r
1113 buf[nread] = '\0'; /* .. be sure string is terminated */\r
1114 result = (*tab_proc)(buf); /* .. convert tabs spaces */\r
1115 nread = strlen(result); /* .. set new read length */\r
1116 }\r
1117 else\r
1118 result = buf;\r
1119\r
1120 for (i = 0; i < nread; i++) /* convert ascii to punch code */\r
1121 readstation[i] = ascii_to_card[(unsigned char) result[i]];\r
1122\r
1123 nread = 80; /* even if line was blank consider it present */\r
1124 }\r
1125\r
1126 if (nread <= 0) { /* set hopper flag accordingly */\r
1127 if (deckfile != NULL && nextdeck())\r
1128 goto again;\r
1129\r
1130 if (punching) /* pretend we loaded a blank card */\r
1131 nread = 80;\r
1132 }\r
1133\r
1134 if (nread == 0) {\r
1135 SETBIT(cr_unit.flags, UNIT_CR_EMPTY);\r
1136 readstate = STATION_EMPTY;\r
1137 cr_count = -1; /* nix the card counter */\r
1138 }\r
1139 else {\r
1140 CLRBIT(cr_unit.flags, UNIT_CR_EMPTY);\r
1141 readstate = STATION_LOADED;\r
1142 cr_count++;\r
1143 cr_unit.pos++;\r
1144 }\r
1145 }\r
1146/* else */\r
1147/* readstate = STATION_EMPTY; */\r
1148\r
1149 cr_unit.COLUMN = -1; /* neither device is currently cycling */\r
1150 cp_unit.COLUMN = -1;\r
1151}\r
1152\r
1153#ifdef NO_USE_FOR_THIS_CURRENTLY\r
1154\r
1155/* this routine should probably be hooked up to the GUI somehow */\r
1156\r
1157/* NPRO - nonprocess runout, flushes out the reader/punch */\r
1158\r
1159static void npro (void)\r
1160{\r
1161 if (cr_unit.flags & UNIT_ATT)\r
1162 fseek(cr_unit.fileref, 0, SEEK_END); /* push reader to EOF */\r
1163 if (deckfile != NULL)\r
1164 fseek(deckfile, 0, SEEK_END); /* skip to end of deck list */\r
1165\r
1166 cr_count = -1; /* nix the card counter */\r
1167\r
1168 if (punchstate == STATION_PUNCHED)\r
1169 feedcycle(FALSE, FALSE); /* flush out card just punched */\r
1170\r
1171 readstate = punchstate = STATION_EMPTY;\r
1172 cr_unit.COLUMN = -1; /* neither device is currently cycling */\r
1173 cp_unit.COLUMN = -1;\r
1174 SETBIT(cr_unit.flags, UNIT_CR_EMPTY); /* set hopper empty */\r
1175}\r
1176\r
1177#endif\r
1178\r
1179/* skipbl - skip leading whitespace in a string */\r
1180\r
1181static char * skipbl (char *str)\r
1182{\r
1183 while (*str && *str <= ' ')\r
1184 str++;\r
1185\r
1186 return str;\r
1187}\r
1188\r
1189static char * trim (char *str)\r
1190{\r
1191 char *s, *lastnb;\r
1192\r
1193 for (lastnb = str-1, s = str; *s; s++) /* point to last nonblank characteter in string */\r
1194 if (*s > ' ')\r
1195 lastnb = s;\r
1196\r
1197 lastnb[1] = '\0'; /* clip just after it */\r
1198\r
1199 return str;\r
1200}\r
1201\r
1202/* alltrim - remove all leading and trailing whitespace from a string */\r
1203\r
1204static char * alltrim (char *str)\r
1205{\r
1206 char *s, *lastnb;\r
1207\r
1208 if ((s = skipbl(str)) != str) /* slide down over leading whitespace */\r
1209 strcpy(str, s);\r
1210\r
1211 for (lastnb = str-1, s = str; *s; s++) /* point to last nonblank characteter in string */\r
1212 if (*s > ' ')\r
1213 lastnb = s;\r
1214\r
1215 lastnb[1] = '\0'; /* clip just after it */\r
1216\r
1217 return str;\r
1218}\r
1219\r
1220/* checkdeck - set hopper empty status based on condition of current reader file */\r
1221\r
1222static void checkdeck (void)\r
1223{\r
1224 t_bool empty;\r
1225\r
1226 if (cr_unit.fileref == NULL) { /* there is no open file */\r
1227 empty = TRUE;\r
1228 }\r
1229 else {\r
1230 fseek(cr_unit.fileref, 0, SEEK_END); /* seek to end of file */\r
1231 empty = ftell(cr_unit.fileref) <= 0; /* file is empty if there was nothing in it*/\r
1232 cr_count = 0; /* reset card counter */\r
1233 cr_unit.pos = 0;\r
1234 fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind deck */\r
1235 }\r
1236\r
1237 if (empty) {\r
1238 SETBIT(cr_unit.flags, UNIT_CR_EMPTY);\r
1239 if (cr_unit.fileref != NULL) /* real file but it's empty, hmmm, try another */\r
1240 nextdeck();\r
1241 }\r
1242 else {\r
1243 CLRBIT(cr_unit.flags, UNIT_CR_EMPTY);\r
1244 }\r
1245}\r
1246\r
1247/* nextdeck - attempt to load a new file from the deck list into the hopper */\r
1248\r
1249static t_bool nextdeck (void)\r
1250{\r
1251 char buf[200], tmpbuf[200], *fname, *c, quote, *mode;\r
1252 int code;\r
1253 long fpos;\r
1254\r
1255 cr_count = 0; /* clear read count */\r
1256 cr_unit.pos = 0;\r
1257\r
1258 if (deckfile == NULL) /* we can't help */\r
1259 return FALSE;\r
1260\r
1261 code = GET_CODE(cr_unit); /* default code as set */\r
1262\r
1263 if (cr_unit.fileref != NULL) { /* this pulls the rug out from under scp */\r
1264 fclose(cr_unit.fileref); /* since the attach flag is still set. be careful! */\r
1265 cr_unit.fileref = NULL;\r
1266\r
1267 if (cr_unit.flags & UNIT_SCRATCH) {\r
1268 remove(tempfile);\r
1269 CLRBIT(cr_unit.flags, UNIT_SCRATCH);\r
1270 }\r
1271 }\r
1272\r
1273 for (;;) { /* get a filename */\r
1274 tab_proc = NULL; /* default: no tab editing */\r
1275\r
1276 if (fgets(buf, sizeof(buf), deckfile) == NULL)\r
1277 break; /* oops, no more names */\r
1278\r
1279 alltrim(buf); /* remove leading and trailing spaces */\r
1280\r
1281 if (! *buf)\r
1282 continue; /* empty line */\r
1283\r
1284 if (*buf == '#' || *buf == '*' || *buf == ';')\r
1285 continue; /* comment */\r
1286\r
1287 if (strnicmp(buf, "!BREAK", 6) == 0) { /* stop the simulation */\r
1288 break_simulation(STOP_DECK_BREAK);\r
1289 continue;\r
1290 }\r
1291\r
1292 if (buf[0] == '!') { /* literal text line, make a temporary file */\r
1293\r
1294#if defined (__GNUC__) && !defined (_WIN32) /* GCC complains about mktemp & always provides mkstemp */\r
1295\r
1296 if (*tempfile == '\0') { /* first time, open guaranteed-unique file */\r
1297 int fh; \r
1298\r
1299 strcpy(tempfile, "tempXXXXXX"); /* get modifiable copy of name template */\r
1300\r
1301 if ((fh = mkstemp(tempfile)) == -1) { /* open file. Actual name is set by side effect */\r
1302 printf("Cannot create temporary deck file\n");\r
1303 break_simulation(STOP_DECK_BREAK);\r
1304 return 0;\r
1305 }\r
1306 /* get FILE * from the file handle */\r
1307 if ((cr_unit.fileref = fdopen(fh, "w+b")) == NULL) {\r
1308 printf("Cannot use temporary deck file %s\n", tempfile);\r
1309 break_simulation(STOP_DECK_BREAK);\r
1310 return 0;\r
1311 }\r
1312 }\r
1313 else { /* on later opens, just reuse the old name */\r
1314 if ((cr_unit.fileref = fopen(tempfile, "w+b")) == NULL) {\r
1315 printf("Cannot create temporary file %s\n", tempfile);\r
1316 break_simulation(STOP_DECK_BREAK);\r
1317 return 0;\r
1318 }\r
1319 }\r
1320#else /* ANSI standard C always provides mktemp */\r
1321\r
1322 if (*tempfile == '\0') { /* first time, construct unique name */\r
1323 strcpy(tempfile, "tempXXXXXX"); /* make a modifiable copy of the template */\r
1324 if (mktemp(tempfile) == NULL) {\r
1325 printf("Cannot create temporary card file name\n");\r
1326 break_simulation(STOP_DECK_BREAK);\r
1327 return 0;\r
1328 }\r
1329 }\r
1330 /* (re)create file */\r
1331 if ((cr_unit.fileref = fopen(tempfile, "w+b")) == NULL) {\r
1332 printf("Cannot create temporary file %s\n", tempfile);\r
1333 break_simulation(STOP_DECK_BREAK);\r
1334 return 0;\r
1335 }\r
1336#endif\r
1337\r
1338 SETBIT(cr_unit.flags, UNIT_SCRATCH);\r
1339\r
1340 for (;;) { /* store literal cards into temporary file */\r
1341 upcase(buf+1);\r
1342 fputs(buf+1, cr_unit.fileref);\r
1343 putc('\n', cr_unit.fileref);\r
1344\r
1345 if (cpu_unit.flags & UNIT_ATT)\r
1346 trace_io("(Literal card %s\n)", buf+1);\r
1347 if (! (cr_unit.flags & UNIT_QUIET))\r
1348 printf( "(Literal card %s)\n", buf+1);\r
1349\r
1350 fpos = ftell(deckfile);\r
1351 if (fgets(buf, sizeof(buf), deckfile) == NULL)\r
1352 break; /* oops, end of file */\r
1353 if (buf[0] != '!' || strnicmp(buf, "!BREAK", 6) == 0)\r
1354 break;\r
1355 alltrim(buf);\r
1356 }\r
1357 fseek(deckfile, fpos, SEEK_SET); /* restore deck file to just before non-literal card */\r
1358\r
1359 fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind scratch file for reading */\r
1360 code = CODE_029; /* assume literal cards use keycode 029 */\r
1361 break;\r
1362 }\r
1363\r
1364 sub_args(buf, tmpbuf, sizeof(buf), list_nargs, list_arg); /* substitute in stuff from the attach command line */\r
1365\r
1366 c = buf; /* pick filename from string */\r
1367\r
1368 while (*c && *c <= ' ') /* skip leading blanks (there could be some now after subsitution) */\r
1369 c++;\r
1370\r
1371 fname = c; /* remember start */\r
1372\r
1373 if (*c == '\'' || *c == '"') { /* quoted string */\r
1374 quote = *c++; /* remember the quote type */\r
1375 fname++; /* skip the quote */\r
1376 while (*c && (*c != quote))\r
1377 c++; /* skip to end of quote */\r
1378 }\r
1379 else { /* not quoted; look for terminating whitespace */\r
1380 while (*c && (*c > ' '))\r
1381 c++;\r
1382 }\r
1383\r
1384 if (*c)\r
1385 *c++ = 0; /* term arg at space or closing quote & move to next character */\r
1386\r
1387 if (! *fname) /* blank line, no filename */\r
1388 continue;\r
1389\r
1390 if ((cr_unit.fileref = fopen(fname, "rb")) == NULL) {\r
1391 printf("File '%s' specified in deck file '%s' cannot be opened\n", fname, cr_unit.filename+1);\r
1392 continue;\r
1393 }\r
1394\r
1395 mode = c = skipbl(c); /* skip to next token, which would be mode, if present */\r
1396\r
1397 switch (*c) {\r
1398 case 'b':\r
1399 case 'B':\r
1400 code = CODE_BINARY; /* force code */\r
1401 c++; /* accept mode character by moving past it */\r
1402 break;\r
1403\r
1404 case 'a':\r
1405 case 'A':\r
1406 code = CODE_029;\r
1407 c++;\r
1408\r
1409 switch (*c) { /* is ascii mode followed by another character? */\r
1410 case 'F':\r
1411 case 'f':\r
1412 tab_proc = EditToFortran;\r
1413 c++;\r
1414 break;\r
1415\r
1416 case 'A':\r
1417 case 'a':\r
1418 tab_proc = EditToAsm;\r
1419 c++;\r
1420 break;\r
1421\r
1422 case 't':\r
1423 case 'T':\r
1424 tab_proc = EditToWhitespace;\r
1425 c++;\r
1426 break;\r
1427 }\r
1428 }\r
1429\r
1430 if (code == CODE_AUTO) /* otherwise if mode is auto, guess it, otherwise use default */\r
1431 code = guess_cr_code();\r
1432\r
1433 if (cpu_unit.flags & UNIT_ATT)\r
1434 trace_io("(Opened %s deck %s%s)\n", (code == CODE_BINARY) ? "binary" : "text", fname, tab_proc ? (*tab_proc)(NULL) : "");\r
1435\r
1436 if (! (cr_unit.flags & UNIT_QUIET))\r
1437 printf( "(Opened %s deck %s%s)\n", (code == CODE_BINARY) ? "binary" : "text", fname, tab_proc ? (*tab_proc)(NULL) : "");\r
1438\r
1439 break;\r
1440 }\r
1441\r
1442 checkdeck();\r
1443\r
1444 if (code != CODE_AUTO) /* if code was determined, set it */\r
1445 set_active_cr_code(code); /* (it may be left at CODE_AUTO when deckfile is exhausted */\r
1446\r
1447 return (cr_unit.flags & UNIT_CR_EMPTY) == 0;/* return TRUE if a deck has been loaded */\r
1448}\r
1449\r
1450static t_stat cr_reset (DEVICE *dptr)\r
1451{\r
1452 if (GET_ACTCODE(cr_unit) == CODE_AUTO)\r
1453 SET_ACTCODE(cr_unit, CODE_029); /* if actual code is not yet set, select 029 for now*/\r
1454\r
1455 cr_set_code(&cr_unit, GET_ACTCODE(cr_unit), NULL, NULL); /* reset to specified code table */\r
1456\r
1457 readstate = STATION_EMPTY;\r
1458\r
1459 cr_dsw = 0;\r
1460 sim_cancel(&cr_unit); /* cancel any pending ops */\r
1461 calc_ints();\r
1462\r
1463 SET_OP(OP_IDLE);\r
1464\r
1465 cr_unit.COLUMN = -1; /* neither device is currently cycling */\r
1466\r
1467 if (cr_unit.flags & UNIT_PHYSICAL) {\r
1468 pcr_reset();\r
1469 return SCPE_OK;\r
1470 }\r
1471\r
1472 return SCPE_OK;\r
1473}\r
1474\r
1475static t_stat cp_reset (DEVICE *dptr)\r
1476{\r
1477 if (GET_CODE(cp_unit) == CODE_AUTO)\r
1478 SET_CODE(cp_unit, CODE_BINARY); /* punch is never in auto mode; turn it to binary on startup */\r
1479\r
1480 cp_set_code(&cp_unit, GET_CODE(cp_unit), NULL, NULL);\r
1481 punchstate = STATION_EMPTY;\r
1482\r
1483 cp_unit.COLUMN = -1;\r
1484 return SCPE_OK;\r
1485}\r
1486\r
1487t_stat cr_rewind (void)\r
1488{\r
1489 if ((cr_unit.flags & UNIT_ATT) == 0)\r
1490 return SCPE_UNATT;\r
1491\r
1492 if (deckfile) {\r
1493 fseek(deckfile, 0, SEEK_SET);\r
1494 nextdeck();\r
1495 }\r
1496 else {\r
1497 fseek(cr_unit.fileref, 0, SEEK_SET);\r
1498 checkdeck();\r
1499 cr_set_code(&cr_unit, GET_CODE(cr_unit), NULL, NULL);\r
1500 }\r
1501\r
1502 cr_unit.pos = 0;\r
1503\r
1504 /* there is a read pending. Pull the card in to make it go */\r
1505 if (CURRENT_OP == OP_READING || CURRENT_OP == OP_PUNCHING || CURRENT_OP == OP_FEEDING)\r
1506 feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0);\r
1507\r
1508 return SCPE_OK;\r
1509}\r
1510\r
1511static t_stat cr_attach (UNIT *uptr, char *cptr)\r
1512{\r
1513 t_stat rval;\r
1514 t_bool use_decklist;\r
1515 char *c, *arg, quote;\r
1516\r
1517 cr_detach(uptr); /* detach file and possibly deck file */\r
1518\r
1519 CLRBIT(uptr->flags, UNIT_SCRATCH|UNIT_QUIET|UNIT_DEBUG|UNIT_PHYSICAL|UNIT_LOWERCASE); /* set options */\r
1520\r
1521 tab_proc = NULL;\r
1522 use_decklist = FALSE;\r
1523\r
1524 if (sim_switches & SWMASK('D')) SETBIT(uptr->flags, UNIT_DEBUG);\r
1525 if (sim_switches & SWMASK('Q')) SETBIT(uptr->flags, UNIT_QUIET);\r
1526 if (sim_switches & SWMASK('L')) SETBIT(uptr->flags, UNIT_LOWERCASE);\r
1527\r
1528 if (sim_switches & SWMASK('F')) tab_proc = EditToFortran;\r
1529 if (sim_switches & SWMASK('A')) tab_proc = EditToAsm;\r
1530 if (sim_switches & SWMASK('T')) tab_proc = EditToWhitespace;\r
1531\r
1532 /* user can specify multiple names on the CR attach command if using a deck file. The deck file \r
1533 * can contain %n tokens to pickup the additional name(s). */\r
1534\r
1535 c = cptr; /* extract arguments */\r
1536 for (list_nargs = 0; list_nargs < MAXARGS; list_nargs++) {\r
1537 while (*c && (*c <= ' ')) /* skip blanks */\r
1538 c++;\r
1539\r
1540 if (! *c)\r
1541 break; /* all done */\r
1542\r
1543 if (list_nargs == 0 && *c == '@') { /* @ might occur before a quoted name; check first */\r
1544 c++;\r
1545 use_decklist = TRUE;\r
1546 }\r
1547\r
1548 if (*c == '\'' || *c == '"') { /* quoted string */\r
1549 quote = *c++;\r
1550 arg = c; /* save start */\r
1551 while (*c && (*c != quote))\r
1552 c++;\r
1553 }\r
1554 else {\r
1555 arg = c; /* save start */\r
1556 while (*c && (*c > ' '))\r
1557 c++;\r
1558 }\r
1559\r
1560 if (*c)\r
1561 *c++ = 0; /* term arg at space or closing quote */\r
1562\r
1563 list_arg[list_nargs] = list_save[list_nargs]; /* set pointer to permanent storage location */\r
1564 strncpy(list_arg[list_nargs], arg, MAXARGLEN); /* store copy */\r
1565 }\r
1566\r
1567 if (list_nargs <= 0) /* need at least 1 */\r
1568 return SCPE_2FARG;\r
1569\r
1570 cr_count = 0; /* reset card counter */\r
1571\r
1572 cptr = list_arg[0]; /* filename is first argument */\r
1573 if (*cptr == '@') { /* @ might also occur inside a quoted name; check afterwards too */\r
1574 use_decklist = TRUE;\r
1575 cptr++;\r
1576 }\r
1577\r
1578 else if (sim_switches & SWMASK('P')) { /* open physical card reader device */\r
1579 return pcr_attach(uptr, cptr);\r
1580 }\r
1581\r
1582 if (list_nargs > 1 && ! use_decklist) /* if not using deck file, there should have been only one name */\r
1583 return SCPE_2MARG;\r
1584\r
1585 if (strcmp(cptr, "(stdin)") == 0 && ! use_decklist) { /* standard input */\r
1586 if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */\r
1587 uptr->filename = calloc(CBUFSIZE, sizeof(char));\r
1588 strcpy(uptr->filename, "(stdin)");\r
1589 uptr->fileref = stdin;\r
1590 SETBIT(uptr->flags, UNIT_ATT);\r
1591 uptr->pos = 0;\r
1592 }\r
1593 else if ((rval = attach_unit(uptr, cptr)) != SCPE_OK) {\r
1594 return rval;\r
1595 }\r
1596\r
1597 if (use_decklist) { /* if we skipped the '@', store the actually-specified name */\r
1598 uptr->filename[0] = '@';\r
1599 strncpy(uptr->filename+1, cptr, CBUFSIZE-1);\r
1600\r
1601 deckfile = cr_unit.fileref; /* save the deck file stream in our local variable */\r
1602 cr_unit.fileref = NULL;\r
1603 nextdeck();\r
1604 }\r
1605 else {\r
1606 checkdeck();\r
1607 cr_set_code(&cr_unit, GET_CODE(cr_unit), NULL, NULL);\r
1608 }\r
1609\r
1610 /* there is a read pending. Pull the card in to make it go */\r
1611 if (CURRENT_OP == OP_READING || CURRENT_OP == OP_PUNCHING || CURRENT_OP == OP_FEEDING)\r
1612 feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0);\r
1613\r
1614 return SCPE_OK;\r
1615}\r
1616\r
1617t_stat cr_detach (UNIT *uptr)\r
1618{\r
1619 t_stat rval;\r
1620\r
1621 cr_count = 0; /* clear read count */\r
1622\r
1623 if (cr_unit.flags & UNIT_PHYSICAL)\r
1624 return pcr_detach(uptr);\r
1625\r
1626 if (cr_unit.flags & UNIT_ATT && deckfile != NULL) {\r
1627 if (cr_unit.fileref != NULL) /* close the active card deck */\r
1628 fclose(cr_unit.fileref);\r
1629\r
1630 if (cr_unit.flags & UNIT_SCRATCH) {\r
1631 remove(tempfile);\r
1632 CLRBIT(cr_unit.flags, UNIT_SCRATCH);\r
1633 }\r
1634\r
1635 cr_unit.fileref = deckfile; /* give scp a file to close */\r
1636 }\r
1637\r
1638 if (uptr->fileref == stdin) {\r
1639 CLRBIT(uptr->flags, UNIT_ATT);\r
1640 free(uptr->filename);\r
1641 uptr->filename = NULL;\r
1642 uptr->fileref = NULL;\r
1643 rval = SCPE_OK;\r
1644 }\r
1645 else\r
1646 rval = detach_unit(uptr);\r
1647\r
1648 return rval;\r
1649}\r
1650\r
1651static t_stat cp_attach (UNIT *uptr, char *cptr)\r
1652{\r
1653 /* if -d is specified turn on debugging (bit is in card reader UNIT) */\r
1654 if (sim_switches & SWMASK('D')) SETBIT(cr_unit.flags, UNIT_DEBUG);\r
1655\r
1656 return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */\r
1657}\r
1658\r
1659static t_stat cp_detach (UNIT *uptr)\r
1660{\r
1661 if (cp_unit.flags & UNIT_ATT)\r
1662 if (punchstate == STATION_PUNCHED)\r
1663 feedcycle(FALSE, FALSE); /* flush out card just punched */\r
1664\r
1665 any_punched = 0; /* reset punch detected */\r
1666 cp_count = 0; /* clear punch count */\r
1667\r
1668 return detach_unit(uptr);\r
1669}\r
1670\r
1671static void op_done (UNIT *u, t_bool issue_intr)\r
1672{\r
1673 if (u->flags & UNIT_DEBUG)\r
1674 DEBUG_PRINT("!CR Op Complete, card %d", cr_count);\r
1675\r
1676 SET_OP(OP_IDLE);\r
1677\r
1678 if (u->flags & UNIT_2501) /* we use u-> not cr_unit. because PUNCH is always a 1442 */\r
1679 CLRBIT(cr_dsw, CR_DSW_2501_BUSY);\r
1680 else\r
1681 CLRBIT(cr_dsw, CR_DSW_1442_BUSY); /* this is trickier. 1442 cr and cp share a dsw */\r
1682\r
1683 if (issue_intr) { /* issue op-complete interrupt for read and punch ops but not feed */\r
1684 if (u->flags & UNIT_2501) {\r
1685 SETBIT(cr_dsw, CR_DSW_2501_OP_COMPLETE);\r
1686 SETBIT(ILSW[4], ILSW_4_2501_CARD);\r
1687 }\r
1688 else {\r
1689 SETBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE);\r
1690 SETBIT(ILSW[4], ILSW_4_1442_CARD);\r
1691 }\r
1692 calc_ints();\r
1693 }\r
1694}\r
1695\r
1696static t_stat cr_svc (UNIT *uptr)\r
1697{\r
1698 int i;\r
1699\r
1700 if (uptr->flags & UNIT_PHYSICAL)\r
1701 return pcr_svc(uptr);\r
1702\r
1703 switch (CURRENT_OP) {\r
1704 case OP_IDLE:\r
1705 break;\r
1706\r
1707 case OP_FEEDING:\r
1708 op_done(&cr_unit, FALSE);\r
1709 break;\r
1710\r
1711 case OP_READING:\r
1712 if (readstate == STATION_EMPTY) { /* read active but no cards? hang */\r
1713 sim_activate(&cr_unit, cf_wait);\r
1714 break;\r
1715 }\r
1716\r
1717 if (cr_unit.flags & UNIT_2501) { /* 2501 transfers entire card then interrupts */\r
1718 for (i = 0; i < cr_cols; i++) /* (we wait until end of delay time before transferring data) */\r
1719 M[(cr_addr + i) & mem_mask] = readstation[i];\r
1720\r
1721 readstate = STATION_READ;\r
1722 if (cr_unit.flags & UNIT_DEBUG)\r
1723 DEBUG_PRINT("!CR Op Complete, card %d", cr_count);\r
1724\r
1725 op_done(&cr_unit, TRUE);\r
1726 }\r
1727 else if (++cr_unit.COLUMN < 80) { /* 1442 interrupts on each column... */\r
1728 SETBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE);\r
1729 SETBIT(ILSW[0], ILSW_0_1442_CARD);\r
1730 calc_ints();\r
1731 sim_activate(&cr_unit, cr_wait);\r
1732 if (cr_unit.flags & UNIT_DEBUG)\r
1733 DEBUG_PRINT("!CR Read Response %d : %d", cr_count, cr_unit.COLUMN+1);\r
1734 }\r
1735 else { /* ... then issues op-complete */\r
1736 readstate = STATION_READ;\r
1737 op_done(&cr_unit, TRUE);\r
1738 }\r
1739 break;\r
1740\r
1741 case OP_PUNCHING:\r
1742 if (punchstate == STATION_EMPTY) { /* punch active but no cards? hang */\r
1743 sim_activate(&cr_unit, cf_wait);\r
1744 break;\r
1745 }\r
1746\r
1747 if (cp_unit.flags & UNIT_LASTPUNCH) {\r
1748 punchstate = STATION_PUNCHED;\r
1749 op_done(&cp_unit, TRUE);\r
1750 }\r
1751 else if (++cp_unit.COLUMN < 80) {\r
1752 SETBIT(cr_dsw, CR_DSW_1442_PUNCH_RESPONSE);\r
1753 SETBIT(ILSW[0], ILSW_0_1442_CARD);\r
1754 calc_ints();\r
1755 sim_activate(&cr_unit, cp_wait);\r
1756 if (cr_unit.flags & UNIT_DEBUG)\r
1757 DEBUG_PRINT("!CR Punch Response");\r
1758 }\r
1759 else {\r
1760 punchstate = STATION_PUNCHED;\r
1761 op_done(&cp_unit, TRUE);\r
1762 }\r
1763 break;\r
1764 }\r
1765\r
1766 return SCPE_OK;\r
1767}\r
1768\r
1769void xio_2501_card (int32 addr, int32 func, int32 modify)\r
1770{\r
1771 char msg[80];\r
1772 int ch;\r
1773 t_bool lastcard;\r
1774\r
1775/* it would be nice for simulated reader to be able to use 2501 mode -- much more\r
1776 * efficient. Using the 1403 printer and 2501 reader speeds things up quite considerably. */\r
1777\r
1778 switch (func) {\r
1779 case XIO_SENSE_DEV:\r
1780 if (cr_unit.flags & UNIT_PHYSICAL) {\r
1781 pcr_xio_sense(modify);\r
1782 break;\r
1783 }\r
1784\r
1785// the following part is questionable -- the 2501 might need to be more picky about setting\r
1786// the LAST_CARD bit...\r
1787\r
1788 if ((cr_unit.flags & UNIT_ATT) == 0)\r
1789 lastcard = TRUE; /* if nothing to read, hopper's empty */\r
1790 else if (readstate == STATION_LOADED)\r
1791 lastcard = FALSE;\r
1792 else if (cr_unit.fileref == NULL)\r
1793 lastcard = TRUE;\r
1794 else if ((ch = getc(cr_unit.fileref)) != EOF) {\r
1795 ungetc(ch, cr_unit.fileref); /* put character back; hopper's not empty */\r
1796 lastcard = FALSE;\r
1797 }\r
1798 else if (deckfile != NULL && nextdeck())\r
1799 lastcard = FALSE;\r
1800 else\r
1801 lastcard = TRUE; /* there is nothing left to read for a next card */\r
1802\r
1803 CLRBIT(cr_dsw, CR_DSW_2501_LAST_CARD|CR_DSW_2501_BUSY|CR_DSW_2501_NOT_READY);\r
1804\r
1805 if (lastcard)\r
1806 SETBIT(cr_dsw, CR_DSW_2501_LAST_CARD|CR_DSW_2501_NOT_READY);\r
1807 // don't clear it here -- modify bit must be set before last card can be cleared\r
1808\r
1809 if (CURRENT_OP != OP_IDLE)\r
1810 SETBIT(cr_dsw, CR_DSW_2501_BUSY|CR_DSW_2501_NOT_READY);\r
1811\r
1812 ACC = cr_dsw; /* return the DSW */\r
1813\r
1814 if (cr_unit.flags & UNIT_DEBUG)\r
1815 DEBUG_PRINT("#CR Sense %04x%s", cr_dsw & 0xFFFF, (modify & 1) ? " RESET" : "");\r
1816\r
1817 if (modify & 0x01) { /* reset interrupts */\r
1818// if (! lastcard) /* (lastcard is reset only when modify bit is set) */\r
1819 CLRBIT(cr_dsw, CR_DSW_2501_LAST_CARD);\r
1820 CLRBIT(cr_dsw, CR_DSW_2501_OP_COMPLETE);\r
1821 CLRBIT(ILSW[4], ILSW_4_2501_CARD);\r
1822 }\r
1823 break;\r
1824\r
1825 case XIO_INITR:\r
1826 if (cr_unit.flags & UNIT_DEBUG)\r
1827 DEBUG_PRINT("#CR Start read");\r
1828\r
1829 cr_unit.COLUMN = -1;\r
1830\r
1831 cr_cols = M[addr & mem_mask]; /* save column count and transfer address */\r
1832 cr_addr = addr+1;\r
1833\r
1834 if ((cr_cols < 0) || (cr_cols > 80)) /* this is questionable -- what would hardware do? */\r
1835 cr_cols = 80;\r
1836\r
1837 if (cr_unit.flags & UNIT_PHYSICAL) {\r
1838 pcr_xio_startread();\r
1839 break;\r
1840 }\r
1841\r
1842 if (readstate != STATION_LOADED)\r
1843 feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0);\r
1844\r
1845 SET_OP(OP_READING);\r
1846 sim_cancel(&cr_unit);\r
1847 sim_activate(&cr_unit, cr_wait2501);\r
1848 break;\r
1849\r
1850 default:\r
1851 sprintf(msg, "Invalid 2501 XIO function %x", func);\r
1852 xio_error(msg);\r
1853 break;\r
1854 }\r
1855}\r
1856\r
1857void xio_1142_card (int32 addr, int32 func, int32 modify)\r
1858{\r
1859 char msg[80];\r
1860 int ch;\r
1861 uint16 wd;\r
1862 t_bool lastcard;\r
1863\r
1864 switch (func) {\r
1865 case XIO_SENSE_DEV:\r
1866 if (cr_unit.flags & UNIT_PHYSICAL) {\r
1867 pcr_xio_sense(modify);\r
1868 break;\r
1869 }\r
1870\r
1871/* glunk\r
1872 * have to separate out what status is 1442 is punch only and 2501 is the reader */\r
1873\r
1874 if (cp_unit.flags & UNIT_ATT)\r
1875 lastcard = FALSE; /* if punch file is open, assume infinite blank cards in reader */\r
1876 else if ((cr_unit.flags & UNIT_ATT) == 0)\r
1877 lastcard = TRUE; /* if nothing to read, hopper's empty */\r
1878 else if (readstate == STATION_LOADED)\r
1879 lastcard = FALSE;\r
1880 else if (cr_unit.fileref == NULL)\r
1881 lastcard = TRUE;\r
1882 else if ((ch = getc(cr_unit.fileref)) != EOF) {\r
1883 ungetc(ch, cr_unit.fileref); /* put character back; hopper's not empty */\r
1884 lastcard = FALSE;\r
1885 }\r
1886 else if (deckfile != NULL && nextdeck())\r
1887 lastcard = FALSE;\r
1888 else\r
1889 lastcard = TRUE; /* there is nothing left to read for a next card */\r
1890\r
1891 CLRBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY);\r
1892\r
1893 if (lastcard)\r
1894 SETBIT(cr_dsw, CR_DSW_1442_LAST_CARD);\r
1895\r
1896 if (CURRENT_OP != OP_IDLE)\r
1897 SETBIT(cr_dsw, CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY);\r
1898 else if (readstate == STATION_EMPTY && punchstate == STATION_EMPTY && lastcard)\r
1899 SETBIT(cr_dsw, CR_DSW_1442_NOT_READY);\r
1900\r
1901 ACC = cr_dsw; /* return the DSW */\r
1902\r
1903 if (cr_unit.flags & UNIT_DEBUG)\r
1904 DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw & 0xFFFF, (modify & 1) ? " RESET0" : "", (modify & 2) ? " RESET4" : "");\r
1905\r
1906 if (modify & 0x01) { /* reset interrupts */\r
1907 CLRBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE | CR_DSW_1442_PUNCH_RESPONSE);\r
1908 CLRBIT(ILSW[0], ILSW_0_1442_CARD);\r
1909 }\r
1910\r
1911 if (modify & 0x02) {\r
1912 CLRBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE);\r
1913 CLRBIT(ILSW[4], ILSW_4_1442_CARD);\r
1914 }\r
1915 break;\r
1916\r
1917 case XIO_READ: /* get card data into word pointed to in IOCC packet */\r
1918 if (cr_unit.flags & OP_READING) {\r
1919 if (cr_unit.COLUMN < 0) {\r
1920 xio_error("1442: Premature read!");\r
1921 }\r
1922 else if (cr_unit.COLUMN < 80) {\r
1923 WriteW(addr, readstation[cr_unit.COLUMN]);\r
1924 if (cr_unit.flags & UNIT_DEBUG)\r
1925 DEBUG_PRINT("#CR Read %03x", (readstation[cr_unit.COLUMN] >> 4));\r
1926 }\r
1927 else if (cr_unit.COLUMN == 80) {\r
1928 xio_error("1442: Read past column 80!");\r
1929 cr_unit.COLUMN++; /* don't report it again */\r
1930 }\r
1931 }\r
1932 else {\r
1933/* don't complain: APL\1130 issues both reads and writes on every interrupt\r
1934 * (probably to keep the code small). Apparently it's just ignored if corresponding\r
1935 * control didn't initiate a read cycle.\r
1936 * xio_error("1442: Read when not in a read cycle!"); */\r
1937 }\r
1938 break;\r
1939\r
1940 case XIO_WRITE:\r
1941 if (cr_unit.flags & OP_PUNCHING) {\r
1942 if (cp_unit.COLUMN < 0) {\r
1943 xio_error("1442: Premature write!");\r
1944 }\r
1945 else if (cp_unit.flags & UNIT_LASTPUNCH) {\r
1946 xio_error("1442: Punch past last-punch column!");\r
1947 cp_unit.COLUMN = 81;\r
1948 }\r
1949 else if (cp_unit.COLUMN < 80) {\r
1950 wd = (uint16) ReadW(addr); /* store one word to punch buffer */\r
1951 punchstation[cp_unit.COLUMN] = wd & 0xFFF0;\r
1952 if (wd & 0x0008) /* mark this as last column to be punched */\r
1953 SETBIT(cp_unit.flags, UNIT_LASTPUNCH);\r
1954 if (cr_unit.flags & UNIT_DEBUG)\r
1955 DEBUG_PRINT("#CR Punch %03x%s", (wd >> 4) & 0xFFF, (wd & 8) ? " LAST" : "");\r
1956 }\r
1957 else if (cp_unit.COLUMN == 80) {\r
1958 xio_error("1442: Punch past column 80!");\r
1959 cp_unit.COLUMN++; /* don't report it again */\r
1960 }\r
1961 }\r
1962 else {\r
1963/* don't complain: APL\1130 issues both reads and writes on every interrupt\r
1964 * (probably to keep the code small). Apparently it's just ignored if corresponding\r
1965 * control didn't initiate a punch cycle.\r
1966 * xio_error("1442: Write when not in a punch cycle!"); */\r
1967 }\r
1968 break;\r
1969\r
1970 case XIO_CONTROL:\r
1971 switch (modify & 7) {\r
1972 case 1: /* start punch */\r
1973 if (cr_unit.flags & UNIT_DEBUG)\r
1974 DEBUG_PRINT("#CR Start Punch");\r
1975 if (punchstate != STATION_LOADED)\r
1976 feedcycle(TRUE, TRUE);\r
1977\r
1978 SET_OP(OP_PUNCHING);\r
1979 cp_unit.COLUMN = -1;\r
1980\r
1981 CLRBIT(cp_unit.flags, UNIT_LASTPUNCH);\r
1982\r
1983 any_punched = 1; /* we've started punching, so enable writing to output deck file */\r
1984\r
1985 sim_cancel(&cr_unit);\r
1986 sim_activate(&cr_unit, cp_wait);\r
1987 break;\r
1988\r
1989 case 2: /* feed cycle */\r
1990 if (cr_unit.flags & UNIT_DEBUG)\r
1991 DEBUG_PRINT("#CR Feed");\r
1992\r
1993 if (cr_unit.flags & UNIT_PHYSICAL) {\r
1994 pcr_xio_feedcycle();\r
1995 break;\r
1996 }\r
1997\r
1998 feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0);\r
1999\r
2000 SET_OP(OP_FEEDING);\r
2001 sim_cancel(&cr_unit);\r
2002 sim_activate(&cr_unit, cf_wait);\r
2003 break;\r
2004\r
2005 case 4: /* start read */\r
2006 if (cr_unit.flags & UNIT_DEBUG)\r
2007 DEBUG_PRINT("#CR Start read");\r
2008\r
2009 cr_unit.COLUMN = -1;\r
2010\r
2011 if (cr_unit.flags & UNIT_PHYSICAL) {\r
2012 pcr_xio_startread();\r
2013 break;\r
2014 }\r
2015\r
2016 if (readstate != STATION_LOADED)\r
2017 feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0);\r
2018\r
2019 SET_OP(OP_READING);\r
2020 sim_cancel(&cr_unit);\r
2021 sim_activate(&cr_unit, cr_wait);\r
2022 break;\r
2023\r
2024 case 0:\r
2025 if (cr_unit.flags & UNIT_DEBUG)\r
2026 DEBUG_PRINT("#CR NOP");\r
2027 break;\r
2028\r
2029 default:\r
2030 sprintf(msg, "1442: Multiple operations in XIO_CONTROL: %x", modify);\r
2031 xio_error(msg);\r
2032 return;\r
2033 }\r
2034\r
2035 break;\r
2036\r
2037 default:\r
2038 sprintf(msg, "Invalid 1442 XIO function %x", func);\r
2039 xio_error(msg);\r
2040 break;\r
2041 }\r
2042}\r
2043\r
2044#if ! (defined(ENABLE_PHYSICAL_CARD_READER_SUPPORT) && defined(WIN32))\r
2045\r
2046 /* stub out the physical card reader routines */\r
2047\r
2048 static t_stat pcr_attach (UNIT *uptr, char *devname) {return SCPE_ARG;}\r
2049 static t_stat pcr_detach (UNIT *uptr) {return detach_unit(uptr);}\r
2050 static t_stat pcr_svc (UNIT *uptr) {return SCPE_OK;}\r
2051 static void pcr_xio_sense (int modify) {}\r
2052 static void pcr_xio_feedcycle (void) {}\r
2053 static void pcr_xio_startread (void) {}\r
2054 static void pcr_reset (void) {}\r
2055\r
2056#else\r
2057\r
2058/*\r
2059 * This code supports a physical card reader interface I built. Interface schematic\r
2060 * and documentation can be downloaded from http://ibm1130.org/sim/downloads/cardread.zip\r
2061 */\r
2062\r
2063#include <windows.h>\r
2064\r
2065#define PCR_STATUS_READY 1 /* bits in interface reply byte */\r
2066#define PCR_STATUS_ERROR 2\r
2067#define PCR_STATUS_HEMPTY 4\r
2068#define PCR_STATUS_EOF 8\r
2069#define PCR_STATUS_PICKING 16\r
2070\r
2071#define PCR_STATUS_MSEC 150 /* when idle, get status every 150 msec */\r
2072\r
2073typedef enum {\r
2074 PCR_STATE_IDLE, /* nothing expected from the interface */\r
2075 PCR_STATE_WAIT_CMD_RESPONSE, /* waiting for response from any command other than P */\r
2076 PCR_STATE_WAIT_PICK_CMD_RESPONSE, /* waiting for response from P command */\r
2077 PCR_STATE_WAIT_DATA_START, /* waiting for introduction to data from P command */\r
2078 PCR_STATE_WAIT_DATA, /* waiting for data from P command */\r
2079 PCR_STATE_WAIT_PICK_FINAL_RESPONSE, /* waiting for status byte after last of the card data */\r
2080 PCR_STATE_CLOSED\r
2081} PCR_STATE;\r
2082\r
2083static void pcr_cmd (char cmd);\r
2084static DWORD CALLBACK pcr_thread (LPVOID arg);\r
2085static BOOL pcr_handle_status_byte (int nrcvd);\r
2086static void pcr_trigger_interrupt_0(void);\r
2087static void begin_pcr_critical_section (void);\r
2088static void end_pcr_critical_section (void);\r
2089static void pcr_set_dsw_from_status (BOOL post_pick);\r
2090static t_stat pcr_open_controller (char *devname);\r
2091\r
2092static PCR_STATE pcr_state = PCR_STATE_CLOSED; /* current state of connection to physical card reader interface */\r
2093static char pcr_status = 0; /* last status byte received from the interface */\r
2094static int pcr_nleft; /* number of bytes still expected from pick command */\r
2095static int pcr_nready; /* number of bytes waiting in the input buffer for simulator to read */\r
2096static BOOL pcr_done;\r
2097static HANDLE hpcr = INVALID_HANDLE_VALUE;\r
2098static HANDLE hPickEvent = INVALID_HANDLE_VALUE;\r
2099static HANDLE hResetEvent = INVALID_HANDLE_VALUE;\r
2100static OVERLAPPED ovRd, ovWr; /* overlapped IO structures for reading from, writing to device */\r
2101static int nwaits; /* number of timeouts waiting for response from interface */\r
2102static char response_byte; /* buffer to receive command/status response byte from overlapped read */\r
2103static char lastcmd = '?';\r
2104\r
2105/* pcr_attach - perform attach function to physical card reader */\r
2106\r
2107static t_stat pcr_attach (UNIT *uptr, char *devname)\r
2108{\r
2109 DWORD thread_id;\r
2110 t_stat rval;\r
2111\r
2112 pcr_state = PCR_STATE_CLOSED;\r
2113 sim_cancel(uptr);\r
2114 cr_unit.COLUMN = -1; /* device is not currently cycling */\r
2115\r
2116 if ((rval = pcr_open_controller(devname)) != SCPE_OK)\r
2117 return rval;\r
2118\r
2119 if (hPickEvent == INVALID_HANDLE_VALUE)\r
2120 hPickEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
2121\r
2122 if (hResetEvent == INVALID_HANDLE_VALUE)\r
2123 hResetEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\r
2124\r
2125 pcr_status = PCR_STATUS_HEMPTY; /* set default status: offline, no cards */\r
2126 pcr_state = PCR_STATE_IDLE;\r
2127 pcr_done = FALSE;\r
2128 cr_dsw = CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY;\r
2129\r
2130 set_active_cr_code(CODE_BINARY); /* force binary mode */\r
2131\r
2132 if (CreateThread(NULL, 0, pcr_thread, NULL, 0, &thread_id) == NULL) {\r
2133 pcr_state = PCR_STATE_CLOSED;\r
2134 CloseHandle(hpcr);\r
2135 hpcr = INVALID_HANDLE_VALUE;\r
2136 printf("Error creating card reader thread\n");\r
2137 return SCPE_IERR;\r
2138 }\r
2139\r
2140 SETBIT(uptr->flags, UNIT_PHYSICAL|UNIT_ATT); /* mark device as attached */\r
2141 uptr->filename = malloc(strlen(devname)+1);\r
2142 strcpy(uptr->filename, devname);\r
2143\r
2144 return SCPE_OK;\r
2145}\r
2146\r
2147/* pcr_open_controller - open the USB device's virtual COM port and configure the interface */\r
2148\r
2149static t_stat pcr_open_controller (char *devname)\r
2150{\r
2151 DCB dcb;\r
2152 COMMTIMEOUTS cto;\r
2153 DWORD nerr;\r
2154\r
2155 if (hpcr != INVALID_HANDLE_VALUE)\r
2156 return SCPE_OK;\r
2157 /* open the COM port */\r
2158 hpcr = CreateFile(devname, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
2159 if (hpcr == INVALID_HANDLE_VALUE)\r
2160 return SCPE_OPENERR;\r
2161\r
2162 memset(&dcb, 0, sizeof(dcb)); /* set communications parameters */\r
2163\r
2164 dcb.DCBlength = sizeof(DCB);\r
2165 dcb.BaudRate = CBR_115200; /* for the USB virtual com port, baud rate is irrelevant */\r
2166 dcb.fBinary = 1;\r
2167 dcb.fParity = 0;\r
2168 dcb.fOutxCtsFlow = 0;\r
2169 dcb.fOutxDsrFlow = 0;\r
2170 dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2171 dcb.fDsrSensitivity = FALSE;\r
2172 dcb.fTXContinueOnXoff = 0;\r
2173 dcb.fOutX = 0;\r
2174 dcb.fInX = 0;\r
2175 dcb.fErrorChar = 0;\r
2176 dcb.fNull = 0;\r
2177 dcb.fRtsControl = RTS_CONTROL_ENABLE; \r
2178 dcb.fAbortOnError = 0;\r
2179 dcb.XonLim = 0;\r
2180 dcb.XoffLim = 0;\r
2181 dcb.ByteSize = 8;\r
2182 dcb.Parity = NOPARITY;\r
2183 dcb.StopBits = ONESTOPBIT;\r
2184 dcb.XonChar = 0;\r
2185 dcb.XoffChar = 0;\r
2186 dcb.ErrorChar = 0;\r
2187 dcb.EofChar = 0;\r
2188 dcb.EvtChar = 0;\r
2189\r
2190 if (! SetCommState(hpcr, &dcb)) {\r
2191 CloseHandle(hpcr);\r
2192 hpcr = INVALID_HANDLE_VALUE;\r
2193 printf("Call to SetCommState failed\n");\r
2194 return SCPE_OPENERR;\r
2195 }\r
2196\r
2197 cto.ReadIntervalTimeout = 100; /* stop if 100 msec elapses between two received bytes */\r
2198 cto.ReadTotalTimeoutMultiplier = 0; /* no length sensitivity */\r
2199 cto.ReadTotalTimeoutConstant = 400; /* allow 400 msec for a read (reset command can take a while) */\r
2200\r
2201 cto.WriteTotalTimeoutMultiplier = 0;\r
2202 cto.WriteTotalTimeoutConstant = 200; /* allow 200 msec for a write */\r
2203\r
2204 if (! SetCommTimeouts(hpcr, &cto)) {\r
2205 CloseHandle(hpcr);\r
2206 hpcr = INVALID_HANDLE_VALUE;\r
2207 printf("Call to SetCommTimeouts failed\n");\r
2208 return SCPE_OPENERR;\r
2209 }\r
2210\r
2211 PurgeComm(hpcr, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);\r
2212 ClearCommError(hpcr, &nerr, NULL);\r
2213\r
2214 return SCPE_OK;\r
2215}\r
2216\r
2217/* pcr_detach - detach physical reader from CR device */\r
2218\r
2219static t_stat pcr_detach (UNIT *uptr)\r
2220{\r
2221 if (cr_unit.flags & UNIT_ATT) {\r
2222 CloseHandle(hpcr); /* close the COM port (this will lead to the thread closing) */\r
2223 hpcr = INVALID_HANDLE_VALUE;\r
2224 pcr_state = PCR_STATE_CLOSED;\r
2225\r
2226 free(uptr->filename); /* release the name copy */\r
2227 uptr->filename = NULL;\r
2228 }\r
2229\r
2230 CLRBIT(cr_unit.flags, UNIT_PHYSICAL|UNIT_ATT); /* drop the attach and physical bits */\r
2231 return SCPE_OK;\r
2232}\r
2233\r
2234/* pcr_xio_sense - perform XIO sense function on physical card reader */\r
2235\r
2236static void pcr_xio_sense (int modify)\r
2237{\r
2238 if (modify & 0x01) { /* reset simulated interrupts */\r
2239 CLRBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE | CR_DSW_1442_PUNCH_RESPONSE);\r
2240 CLRBIT(ILSW[0], ILSW_0_1442_CARD);\r
2241 }\r
2242\r
2243 if (modify & 0x02) {\r
2244 CLRBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE);\r
2245 CLRBIT(ILSW[4], ILSW_4_1442_CARD);\r
2246 }\r
2247\r
2248 ACC = cr_dsw; /* DSW was set in real-time, just return the DSW */\r
2249\r
2250 if (cr_unit.flags & UNIT_DEBUG)\r
2251 DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw, (modify & 1) ? " RESET0" : "", (modify & 2) ? " RESET4" : "");\r
2252}\r
2253\r
2254/* report_error - issue detailed report of Windows IO error */\r
2255\r
2256static void report_error (char *msg, DWORD err)\r
2257{\r
2258 char *lpMessageBuffer = NULL;\r
2259 \r
2260 FormatMessage(\r
2261 FORMAT_MESSAGE_ALLOCATE_BUFFER |\r
2262 FORMAT_MESSAGE_FROM_SYSTEM,\r
2263 NULL,\r
2264 GetLastError(),\r
2265 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* The user default language */\r
2266 (LPTSTR) &lpMessageBuffer,\r
2267 0,\r
2268 NULL );\r
2269\r
2270 printf("GetOverlappedResult failed, %s, %s\n",\r
2271 msg, lpMessageBuffer);\r
2272\r
2273 LocalFree(lpMessageBuffer);\r
2274}\r
2275\r
2276/* pcr_thread - thread to handle card reader interface communications */\r
2277\r
2278static DWORD CALLBACK pcr_thread (LPVOID arg)\r
2279{\r
2280 DWORD event;\r
2281 long nrcvd, nread, nwritten;\r
2282 HANDLE objs[4];\r
2283 BOOL pick_queued = FALSE, reset_queued = FALSE;\r
2284\r
2285 nwaits = 0;\r
2286\r
2287 ZeroMemory(&ovRd, sizeof(ovRd));\r
2288 ZeroMemory(&ovWr, sizeof(ovWr));\r
2289 ovRd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* create an event for async IO reads */\r
2290 ovWr.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* create an event for async IO writes */\r
2291\r
2292 objs[0] = ovRd.hEvent;\r
2293 objs[1] = ovWr.hEvent;\r
2294 objs[2] = hResetEvent;\r
2295 objs[3] = hPickEvent;\r
2296\r
2297 while (hpcr != INVALID_HANDLE_VALUE) {\r
2298 if (pcr_state == PCR_STATE_IDLE) {\r
2299 if (pick_queued) {\r
2300 pcr_cmd('P');\r
2301 pick_queued = FALSE;\r
2302 pcr_done = FALSE;\r
2303 pcr_state = PCR_STATE_WAIT_PICK_CMD_RESPONSE;\r
2304 }\r
2305 else if (reset_queued) {\r
2306 pcr_cmd('X');\r
2307 reset_queued = FALSE;\r
2308 pcr_state = PCR_STATE_WAIT_CMD_RESPONSE;\r
2309 }\r
2310 }\r
2311\r
2312 event = WaitForMultipleObjects(4, objs, FALSE, PCR_STATUS_MSEC);\r
2313\r
2314 switch (event) {\r
2315 case WAIT_OBJECT_0+0: /* read complete */\r
2316 ResetEvent(ovRd.hEvent);\r
2317 if (! GetOverlappedResult(hpcr, &ovRd, &nrcvd, TRUE))\r
2318 report_error("PCR_Read", GetLastError());\r
2319 else if (cr_unit.flags & UNIT_DEBUG)\r
2320 printf("PCR_Read: event, %d rcvd\n", nrcvd);\r
2321 break;\r
2322\r
2323 case WAIT_OBJECT_0+1: /* write complete */\r
2324 nwritten = 0;\r
2325 ResetEvent(ovWr.hEvent);\r
2326 if (! GetOverlappedResult(hpcr, &ovWr, &nwritten, TRUE))\r
2327 report_error("PCR_Write", GetLastError());\r
2328 else if (cr_unit.flags & UNIT_DEBUG)\r
2329 printf("PCR_Write: event, %d sent\n", nwritten);\r
2330 continue;\r
2331\r
2332 case WAIT_OBJECT_0+2: /* reset request from simulator */\r
2333 reset_queued = TRUE;\r
2334 pick_queued = FALSE;\r
2335 continue;\r
2336\r
2337 case WAIT_OBJECT_0+3: /* pick request from simulator */\r
2338 pick_queued = TRUE;\r
2339 continue;\r
2340\r
2341 case WAIT_TIMEOUT:\r
2342 if (pcr_state == PCR_STATE_IDLE) {\r
2343 pcr_state = PCR_STATE_WAIT_CMD_RESPONSE;\r
2344 ovRd.Offset = ovRd.OffsetHigh = 0;\r
2345 pcr_cmd('S');\r
2346 }\r
2347 else if (pcr_state == PCR_STATE_WAIT_CMD_RESPONSE && ++nwaits >= 6) {\r
2348 printf("Requesting status again!\n");\r
2349 ovRd.Offset = ovRd.OffsetHigh = 0;\r
2350 pcr_cmd('S');\r
2351 }\r
2352 continue;\r
2353\r
2354 default:\r
2355 printf("Unexpected pcr_wait result %08lx", event);\r
2356 continue;\r
2357 }\r
2358\r
2359 /* We only get here if read event occurred */\r
2360\r
2361 switch (pcr_state) {\r
2362 case PCR_STATE_IDLE: /* nothing expected from the interface */\r
2363 PurgeComm(hpcr, PURGE_RXCLEAR|PURGE_RXABORT);\r
2364 break;\r
2365\r
2366 case PCR_STATE_WAIT_CMD_RESPONSE: /* waiting for response from any command other than P */\r
2367 if (pcr_handle_status_byte(nrcvd))\r
2368 pcr_state = PCR_STATE_IDLE;\r
2369 break;\r
2370\r
2371 case PCR_STATE_WAIT_PICK_CMD_RESPONSE: /* waiting for response from P command */\r
2372 if (pcr_handle_status_byte(nrcvd)) {\r
2373 pcr_cmd('\0'); /* queue a response read */\r
2374 pcr_state = PCR_STATE_WAIT_DATA_START;\r
2375 }\r
2376 break;\r
2377\r
2378 case PCR_STATE_WAIT_DATA_START: /* waiting for leadin character from P command (= or !) */\r
2379 if (nrcvd <= 0) { /* (this could take an indefinite amount of time) */\r
2380 if (cr_unit.flags & UNIT_DEBUG) \r
2381 printf("PCR: NO RESP YET\n");\r
2382\r
2383 continue; /* reader is not ready */\r
2384 }\r
2385\r
2386 if (cr_unit.flags & UNIT_DEBUG) /* (this could take an indefinite amount of time) */\r
2387 printf("PCR: GOT %c\n", response_byte);\r
2388\r
2389 switch (response_byte) {\r
2390 case '=': /* = means pick in progress, 160 bytes of data will be coming */\r
2391 pcr_state = PCR_STATE_WAIT_DATA;\r
2392 ovRd.Offset = ovRd.OffsetHigh = 0;\r
2393 nread = 20; /* initiate a read */\r
2394 ReadFile(hpcr, ((char *) readstation), nread, &nrcvd, &ovRd);\r
2395 break;\r
2396\r
2397 case '!': /* ! means pick has been canceled, status will be coming next */\r
2398 pcr_state = PCR_STATE_WAIT_CMD_RESPONSE;\r
2399 pcr_cmd('\0'); /* initiate read */\r
2400 break;\r
2401\r
2402 default: /* anything else is a datacomm error, or something */\r
2403 /* indicate read check or something */\r
2404/* pcr_state = PCR_STATE_IDLE; */\r
2405 break;\r
2406 }\r
2407 break;\r
2408\r
2409 case PCR_STATE_WAIT_DATA: /* waiting for data from P command */\r
2410 if (cr_unit.flags & UNIT_DEBUG)\r
2411 printf((nrcvd <= 0) ? "PCR: NO RESP!\n" : "PCR: GOT %d BYTES\n", nrcvd);\r
2412\r
2413 if (nrcvd > 0) {\r
2414 pcr_nleft -= nrcvd;\r
2415\r
2416 begin_pcr_critical_section();\r
2417 pcr_nready += nrcvd;\r
2418 end_pcr_critical_section();\r
2419 }\r
2420\r
2421 if (pcr_nleft > 0) {\r
2422 ovRd.Offset = ovRd.OffsetHigh = 0;\r
2423 nread = min(pcr_nleft, 20);\r
2424 ReadFile(hpcr, ((char *) readstation)+160-pcr_nleft, nread, &nrcvd, &ovRd);\r
2425 }\r
2426 else {\r
2427 pcr_state = PCR_STATE_WAIT_PICK_FINAL_RESPONSE;\r
2428 pcr_cmd('\0'); /* queue read */\r
2429 }\r
2430 break;\r
2431\r
2432 case PCR_STATE_WAIT_PICK_FINAL_RESPONSE: /* waiting for status byte after last of the card data */\r
2433 if (pcr_handle_status_byte(nrcvd)) {\r
2434 readstate = STATION_READ;\r
2435 pcr_state = PCR_STATE_IDLE;\r
2436 pcr_done = TRUE;\r
2437 }\r
2438 break;\r
2439 }\r
2440 }\r
2441\r
2442 CloseHandle(ovRd.hEvent);\r
2443 CloseHandle(ovWr.hEvent);\r
2444\r
2445 return 0;\r
2446}\r
2447\r
2448/* pcr_cmd - issue command byte to interface. Read of response byte is queued */\r
2449\r
2450static void pcr_cmd (char cmd)\r
2451{\r
2452 long nwritten, nrcvd;\r
2453 int status;\r
2454\r
2455 if (cmd != '\0') {\r
2456 if (cr_unit.flags & UNIT_DEBUG /* && (cmd != 'S' || cmd != lastcmd) */)\r
2457 printf("PCR: SENT %c\n", cmd);\r
2458\r
2459 lastcmd = cmd;\r
2460\r
2461 ResetEvent(ovWr.hEvent);\r
2462 ovWr.Offset = ovWr.OffsetHigh = 0;\r
2463 status = WriteFile(hpcr, &cmd, 1, &nwritten, &ovWr);\r
2464 if (status == 0 && GetLastError() != ERROR_IO_PENDING)\r
2465 printf("Error initiating write in pcr_cmd\n");\r
2466 }\r
2467\r
2468 ovRd.Offset = ovRd.OffsetHigh = 0;\r
2469 status = ReadFile(hpcr, &response_byte, 1, &nrcvd, &ovRd); /* if no bytes ready, just return -- a later wait-event will catch it */\r
2470 if (status == 0 && GetLastError() != ERROR_IO_PENDING)\r
2471 printf("Error initiating read in pcr_cmd\n");\r
2472\r
2473/* if (cr_unit.flags & UNIT_DEBUG)\r
2474 * if (nrcvd == 0)\r
2475 * printf("PCR: NO RESPONSE\n");\r
2476 * else\r
2477 * printf("PCR: RESPONSE %c\n", response_byte); */\r
2478\r
2479 nwaits = 0;\r
2480}\r
2481\r
2482/* pcr_handle_status_byte - handle completion of read of response byte */\r
2483\r
2484static BOOL pcr_handle_status_byte (int nrcvd)\r
2485{\r
2486 static char prev_status = '?';\r
2487 BOOL show;\r
2488\r
2489 if (nrcvd <= 0)\r
2490 return FALSE;\r
2491\r
2492 pcr_status = response_byte; /* save new status */\r
2493\r
2494 show = lastcmd != 'S' || pcr_status != prev_status;\r
2495\r
2496 if ((cr_unit.flags & UNIT_DEBUG) && show) {\r
2497 printf("PCR: status %c\n", pcr_status);\r
2498 prev_status = pcr_status;\r
2499 }\r
2500\r
2501 pcr_set_dsw_from_status(FALSE);\r
2502\r
2503 return TRUE;\r
2504}\r
2505\r
2506/* pcr_set_dsw_from_status - construct device status word from current physical reader status */\r
2507\r
2508static void pcr_set_dsw_from_status (BOOL post_pick)\r
2509{\r
2510 /* set 1130 status word bits */\r
2511 CLRBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY | CR_DSW_1442_ERROR_CHECK);\r
2512\r
2513 if (pcr_status & PCR_STATUS_HEMPTY) \r
2514 SETBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY);\r
2515\r
2516 if (pcr_status & PCR_STATUS_ERROR)\r
2517 SETBIT(cr_dsw, CR_DSW_1442_ERROR_CHECK);\r
2518\r
2519 /* we have a problem -- ready doesn't come back up right away after a pick. */\r
2520 /* I think I'll fudge this and not set NOT_READY immediately after a pick */\r
2521\r
2522 if ((! post_pick) && ! (pcr_status & PCR_STATUS_READY))\r
2523 SETBIT(cr_dsw, CR_DSW_1442_NOT_READY);\r
2524\r
2525 if (CURRENT_OP != OP_IDLE)\r
2526 SETBIT(cr_dsw, CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY);\r
2527}\r
2528\r
2529static void pcr_xio_feedcycle (void)\r
2530{\r
2531 SET_OP(OP_FEEDING);\r
2532 cr_unit.COLUMN = -1;\r
2533 SetEvent(hPickEvent);\r
2534 sim_activate(&cr_unit, cr_wait); /* keep checking frequently */\r
2535}\r
2536\r
2537static void pcr_xio_startread (void)\r
2538{\r
2539 SET_OP(OP_READING);\r
2540 cr_unit.COLUMN = -1;\r
2541 pcr_nleft = 160;\r
2542 pcr_nready = 0;\r
2543 SetEvent(hPickEvent);\r
2544 sim_activate(&cr_unit, cr_wait); /* keep checking frequently */\r
2545}\r
2546\r
2547static void pcr_reset (void)\r
2548{\r
2549 pcr_status = PCR_STATUS_HEMPTY; /* set default status: offline, no cards */\r
2550 pcr_state = PCR_STATE_IDLE;\r
2551 cr_dsw = CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY;\r
2552\r
2553 sim_cancel(&cr_unit);\r
2554\r
2555 SetEvent(hResetEvent);\r
2556}\r
2557\r
2558/* pcr_trigger_interrupt_0 - simulate a read response interrupt so OS will read queued column data */\r
2559\r
2560static void pcr_trigger_interrupt_0 (void)\r
2561{\r
2562 if (++cr_unit.COLUMN < 80) {\r
2563 SETBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE);\r
2564 SETBIT(ILSW[0], ILSW_0_1442_CARD);\r
2565 calc_ints();\r
2566\r
2567 begin_pcr_critical_section();\r
2568 pcr_nready -= 2;\r
2569 end_pcr_critical_section();\r
2570\r
2571 if (cr_unit.flags & UNIT_DEBUG)\r
2572 printf("SET IRQ0 col %d\n", cr_unit.COLUMN+1);\r
2573 }\r
2574}\r
2575\r
2576static t_stat pcr_svc (UNIT *uptr)\r
2577{\r
2578 switch (CURRENT_OP) {\r
2579 case OP_IDLE:\r
2580 break;\r
2581\r
2582 case OP_READING:\r
2583 if (pcr_nready >= 2) { /* if there is a whole column buffered, simulate column interrupt/* pcr_trigger_interrupt_0 - simulate a read response interrupt so OS will read queued column data */\r
2584\r
2585 pcr_trigger_interrupt_0();\r
2586 sim_activate(&cr_unit, cr_wait); /* keep checking frequently */\r
2587 }\r
2588 else if (pcr_done) {\r
2589 pcr_done = FALSE;\r
2590 cr_count++;\r
2591 op_done(&cr_unit, TRUE);\r
2592 pcr_set_dsw_from_status(TRUE);\r
2593 }\r
2594 else\r
2595 sim_activate(&cr_unit, cr_wait); /* keep checking frequently */\r
2596 break;\r
2597\r
2598 case OP_FEEDING:\r
2599 if (pcr_done) {\r
2600 cr_count++;\r
2601 op_done(&cr_unit, FALSE);\r
2602 pcr_set_dsw_from_status(TRUE);\r
2603 }\r
2604 else\r
2605 sim_activate(&cr_unit, cr_wait); /* keep checking frequently */\r
2606\r
2607 break;\r
2608\r
2609 case OP_PUNCHING:\r
2610 return cr_svc(uptr);\r
2611 }\r
2612\r
2613 return SCPE_OK;\r
2614}\r
2615\r
2616static CRITICAL_SECTION pcr_critsect;\r
2617\r
2618static void begin_pcr_critical_section (void)\r
2619{\r
2620 static BOOL mustinit = TRUE;\r
2621\r
2622 if (mustinit) {\r
2623 InitializeCriticalSection(&pcr_critsect);\r
2624 mustinit = FALSE;\r
2625 }\r
2626\r
2627 EnterCriticalSection(&pcr_critsect);\r
2628}\r
2629\r
2630static void end_pcr_critical_section (void)\r
2631{\r
2632 LeaveCriticalSection(&pcr_critsect);\r
2633}\r
2634\r
2635#endif\r
2636\r