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