1 #include "ibm1130_defs.h"
2 #include "ibm1130_fmt.h"
5 # include <io.h> /* Microsoft puts definition of mktemp into io.h rather than stdlib.h */
8 /* ibm1130_cr.c: IBM 1130 1442 Card Reader simulator
10 Based on the SIMH package written by Robert M Supnik
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
18 * This is not a supported product, but I welcome bug reports and fixes.
19 * Mail to simh@ibm1130.org
21 * Update 2006-01-23 More fixes, in call to mktemp and in 2501 support, also thanks
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.
29 * Update 2005-05-19 Added support for 2501 reader
31 * Update 2004-11-08 Merged in correct physical card reader code
33 * Update 2004-11-02: Added -s to boot command: don't touch console switches.
35 * Update 2004-06-05: Removed "feedcycle" from cr_reset. Reset should not touch the card reader.
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.
40 * Update 2003-11-25: Physical card reader support working, may not be perfect.
41 Changed magic filename for stdin to "(stdin)".
43 * Update 2003-07-23: Added autodetect for card decks (029 vs binary),
44 made this the default.
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.
50 * Update 2002-02-29: Added deck-list option.
52 * Update 2003-02-08: Fixed error in declaration of array list_save, pointed
55 * -----------------------------------------------------------------------
57 * -----------------------------------------------------------------------
61 The ATTACH CR command accepts several command-line switches
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,
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).
73 -d prints a lot of simulator debugging information
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)
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)
83 The ATTACH CP command accepts the -d switch.
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.
91 Filenames may be quoted if they contain spaces.
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:
98 attach @deckfile %1 %2 %3
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
105 would be substituted accordingly.
107 Blank lines and lines starting with ; # or * are ignored as comments.
109 Filenames may be followed by whitespace and one or more mode options:
110 The mode options are:
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
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
122 If a tab mode is not specified, tabs are left unmolested (and are treated as invalid characters)
128 reads filenames from file "decklist," which might contain:
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).
138 Literal text cards can be entered in deck files by preceding an input
139 line with an exclamation point. For example,
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
151 %n tokens are not replaced in literal cards.
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.
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
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
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.
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
185 Input lines of the form
187 [label]<tab>statement
191 [label]<tab>+continuation
193 (where + is any nonalphabetic character) are rearranged in the
197 12345678901234567890...
198 ------------------------
202 However, you must take care that you don't end up with statement text after column 72.
204 Input lines with * or C in column 1 (comments and directives) and lines without tabs
207 (The ! escape is not used before Fortran directives as before Assembler directives)
210 Input lines of the form
212 [label]<whitespace>[opcode]<tab>[tag][L]<tab>[argument]
214 are rearranged so that the input fields are placed in the appropriate columns
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.
221 Input lines with * in column 1 and blank lines are turned into Assembler comments,
222 with the * in the Opcode field.
224 Assembler directive lines at the beginning of the deck must be preceded by
225 ! to indicate that they are not comments. For example,
231 Tabs are replaced with spaces. Tab settings are assumed to be eight characters wide,
232 as is standard for vi, notepad, etc.
234 * CGI mode note: The command
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
244 * -----------------------------------------------------------------------
246 * -----------------------------------------------------------------------
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).
255 The 1442 card read/punch has several cycles:
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
261 read or punch: operates on card at read or punch station (but not both).
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
270 The read and punch cycles operate on the appropriate card buffer.
272 Detaching the card punch flushes the punch station buffer if necessary.
274 As does the 1442, a read or punch cycle w/o a feed cycle causes a
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
281 (Note: Carl Claunch determined by examining DUP code that a feed cycle
282 does not cause an operation complete interrupt).
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
289 Binary format is stored using fxwrite of short ints, in this format:
292 2 2 0 1 2 3 4 5 6 7 8 9
293 * * * * * * * * * * * * 0 0 0 0
296 byte 0 [ 6] [ 7] [ 8] [ 9] 0 0 0 0
297 byte 1 [12] [11] [ 0] [ 1] [ 2] [ 3] [ 4] [ 5]
299 This means we can read words (little endian) and get this in memory:
301 12 11 0 1 2 3 4 5 6 7 8 9 - - - -
303 which is what the 1130 sees.
305 ASCII can be read in blocks of 80 characters but can be terminated by newline prematurely.
307 Booting: card reader IPL loads 80 columns (1 card) into memory starting
308 at location 0 in a split fashion:
310 ________________ _ _ _
324 +------------------ - - -
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 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
334 The zeros mean that all IPL instructions are short form,
335 nonindexed. The 3 column is repeated in bits 8 and 9 so
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
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
348 /* umm, this is a weird little future project of mine. */
350 #define ENABLE_PHYSICAL_CARD_READER_SUPPORT
352 extern int32 sim_switches
;
353 extern UNIT cpu_unit
;
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
);
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
);
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 */
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)
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)
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)
406 #define SET_OP(op) {cr_unit.flags &= ~UNIT_OP; cr_unit.flags |= (op);}
407 #define CURRENT_OP (cr_unit.flags & UNIT_OP)
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)
415 #define GET_CODE(un) (un.flags & UNIT_CODE)
416 #define SET_CODE(un,cd) {un.flags &= ~UNIT_CODE; un.flags |= (cd);}
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))
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);}
427 #define COLUMN u4 /* column field in unit record */
429 UNIT cr_unit
= { UDATA (&cr_svc
, UNIT_ATTABLE
|UNIT_ROABLE
|UNIT_CR_EMPTY
, 0) };
430 UNIT cp_unit
= { UDATA (NULL
, UNIT_ATTABLE
, 0) };
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
},
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
},
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 */
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 */
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
};
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
};
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
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
500 static CPCODE cardcode_029
[] =
503 0x8000, '&', /* + in 026 Fortran */
543 0x0420, '#', /* = in 026 Fortran */
544 0x0220, '@', /* ' in 026 Fortran */
548 0x8820, (unsigned char) '\xA2', /* cent, in MS-DOS encoding (this is in guess_cr_code as well) */
550 0x8220, '<', /* ) in 026 Fortran */
559 0x4060, (unsigned char) '\xAC', /* not, in MS-DOS encoding (this is in guess_cr_code as well) */
561 0x2220, '%', /* ( in 026 Fortran */
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 */
602 static CPCODE cardcode_026F
[] = /* 026 fortran */
654 static CPCODE cardcode_026C
[] = /* 026 commercial */
707 extern void sub_args (char *instr
, char *tmpbuf
, int32 maxstr
, int32 nargs
, char *arg
[]);
709 static int16 ascii_to_card
[256];
711 static CPCODE
*cardcode
;
712 static int ncardcode
;
713 static FILE *deckfile
= NULL
;
714 static char tempfile
[128];
715 static int any_punched
= 0;
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 */
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
;
727 static t_bool
nextdeck (void);
728 static void checkdeck (void);
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);
738 /* lookup_codetable - use code flag setting to get code table pointer and length */
740 static t_bool
lookup_codetable (int32 match
, CPCODE
**pcode
, int *pncode
)
744 *pcode
= cardcode_029
;
745 *pncode
= sizeof(cardcode_029
) / sizeof(CPCODE
);
749 *pcode
= cardcode_026F
;
750 *pncode
= sizeof(cardcode_026F
) / sizeof(CPCODE
);
754 *pcode
= cardcode_026C
;
755 *pncode
= sizeof(cardcode_026C
) / sizeof(CPCODE
);
764 printf("Eek! Undefined code table index");
770 t_stat
set_active_cr_code (int match
)
775 SET_ACTCODE(cr_unit
, match
);
777 if (! lookup_codetable(match
, &code
, &ncode
))
780 memset(ascii_to_card
, 0, sizeof(ascii_to_card
));
782 for (i
= 0; i
< ncode
; i
++) /* set ascii to card code table */
783 ascii_to_card
[code
[i
].ascii
] = code
[i
].hollerith
;
788 static t_stat
cr_set_code (UNIT
*uptr
, int32 match
, char *cptr
, void *desc
)
790 if (match
== CODE_AUTO
)
791 match
= guess_cr_code();
793 return set_active_cr_code(match
);
796 static int32
guess_cr_code (void)
802 uint16 w
[80]; /* one card image, viewed as 80 short words */
803 char c
[160]; /* same, viewed as 160 characters */
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.
821 guess
= CODE_029
; /* assume ASCII, 029 */
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 */
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 */
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 */
849 fseek(cr_unit
.fileref
, filepos
, SEEK_SET
); /* return to original position */
855 static t_stat
cp_set_code (UNIT
*uptr
, int32 match
, char *cptr
, void *desc
)
860 if (! lookup_codetable(match
, &code
, &ncode
))
863 cardcode
= code
; /* save code table for punch output */
869 t_stat
load_cr_boot (int drvno
, int switches
)
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
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
911 if ((switches
& SWMASK('A')) && (switches
& SWMASK('P'))) {
912 boot
= aplp_boot_data
;
913 name
= "APL\\1130 Privileged";
916 else if (switches
& SWMASK('A')) {
917 boot
= apl_boot_data
;
922 boot
= dms_boot_data
;
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 */
931 IAR
= 0; /* clear IAR */
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 */
936 word
= (word
& 0xF800) | ((word
& 0x0400) ? 0x00C0 : 0x0000) | ((word
& 0x03F0) >> 4);
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
);
954 t_stat
cr_boot (int unitno
, DEVICE
*dptr
)
959 if ((rval
= reset_all(0)) != SCPE_OK
)
962 if (! (cr_unit
.flags
& UNIT_ATT
)) /* no deck; load standard boot anyway */
963 return load_cr_boot(-1, 0);
965 if (GET_ACTCODE(cr_unit
) != CODE_BINARY
) {
966 printf("Can only boot from card reader when set to BINARY mode");
970 if (cr_unit
.fileref
== NULL
) /* this will happen if no file in deck file can be opened */
973 feedcycle(TRUE
, FALSE
);
975 /* if (fxread(buf, sizeof(buf[0]), 80, cr_unit.fileref) != 80) */
976 /* return SCPE_IOERR; */
978 IAR
= 0; /* Program Load sets IAR = 0 */
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));
986 char card_to_ascii (uint16 hol
)
990 for (i
= 0; i
< ncardcode
; i
++)
991 if (cardcode
[i
].hollerith
== hol
)
992 return (char) cardcode
[i
].ascii
;
997 /* hollerith_to_ascii - provide a generic conversion for simulator debugging */
999 char hollerith_to_ascii (uint16 hol
)
1003 for (i
= 0; i
< ncardcode
; i
++)
1004 if (cardcode_029
[i
].hollerith
== hol
)
1005 return (char) cardcode
[i
].ascii
;
1010 /* feedcycle - move cards to next station */
1012 static void feedcycle (t_bool load
, t_bool punching
)
1014 char buf
[84], *x
, *result
;
1015 int i
, nread
, nwrite
, ch
;
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
);
1024 for (i
= 80; --i
>= 0; ) { /* find last nonblank column */
1025 if (punchstation
[i
] != 0)
1029 /* i is now index of last character to output or -1 if all blank */
1031 for (nwrite
= 0; nwrite
<= i
; nwrite
++) { /* convert characters */
1032 buf
[nwrite
] = card_to_ascii(punchstation
[nwrite
]);
1035 /* nwrite is now number of characters to output */
1038 buf
[nwrite
++] = '\r'; /* add CR before NL for microsoft */
1040 buf
[nwrite
++] = '\n'; /* append newline */
1041 fxwrite(buf
, sizeof(char), nwrite
, cp_unit
.fileref
);
1048 if (! load
) /* all we wanted to do was flush the punch */
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 */
1056 if (readstate
== STATION_EMPTY
|| (cr_unit
.flags
& UNIT_2501
)) {
1058 memset(punchstation
, 0, sizeof(punchstation
));
1059 punchstate
= STATION_LOADED
;
1062 punchstate
= STATION_EMPTY
;
1065 memcpy(punchstation
, readstation
, sizeof(punchstation
));
1066 punchstate
= STATION_LOADED
;
1069 /* load card into read station */
1071 again
: /* jump here if we've loaded a new deck after emptying the previous one */
1073 if (cr_unit
.flags
& UNIT_ATT
) {
1075 memset(readstation
, 0, sizeof(readstation
)); /* blank out the card image */
1077 if (cr_unit
.fileref
== NULL
)
1080 else if (GET_ACTCODE(cr_unit
) == CODE_BINARY
) /* binary read is straightforward */
1081 nread
= fxread(readstation
, sizeof(readstation
[0]), 80, cr_unit
.fileref
);
1083 else if (fgets(buf
, sizeof(buf
), cr_unit
.fileref
) == NULL
) /* read up to 80 chars */
1084 nread
= 0; /* hmm, end of file */
1086 else { /* check for CRLF or newline */
1087 if ((x
= strchr(buf
, '\r')) == NULL
)
1088 x
= strchr(buf
, '\n');
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 */
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
);
1103 if ((nread
= strlen(buf
)) > 80) /* use the line as read, at most 80 characters */
1107 nread
= x
-buf
; /* reduce length of string */
1109 if (! (cr_unit
.flags
& UNIT_LOWERCASE
))
1110 upcase(buf
); /* force uppercase */
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 */
1120 for (i
= 0; i
< nread
; i
++) /* convert ascii to punch code */
1121 readstation
[i
] = ascii_to_card
[(unsigned char) result
[i
]];
1123 nread
= 80; /* even if line was blank consider it present */
1126 if (nread
<= 0) { /* set hopper flag accordingly */
1127 if (deckfile
!= NULL
&& nextdeck())
1130 if (punching
) /* pretend we loaded a blank card */
1135 SETBIT(cr_unit
.flags
, UNIT_CR_EMPTY
);
1136 readstate
= STATION_EMPTY
;
1137 cr_count
= -1; /* nix the card counter */
1140 CLRBIT(cr_unit
.flags
, UNIT_CR_EMPTY
);
1141 readstate
= STATION_LOADED
;
1147 /* readstate = STATION_EMPTY; */
1149 cr_unit
.COLUMN
= -1; /* neither device is currently cycling */
1150 cp_unit
.COLUMN
= -1;
1153 #ifdef NO_USE_FOR_THIS_CURRENTLY
1155 /* this routine should probably be hooked up to the GUI somehow */
1157 /* NPRO - nonprocess runout, flushes out the reader/punch */
1159 static void npro (void)
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 */
1166 cr_count
= -1; /* nix the card counter */
1168 if (punchstate
== STATION_PUNCHED
)
1169 feedcycle(FALSE
, FALSE
); /* flush out card just punched */
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 */
1179 /* skipbl - skip leading whitespace in a string */
1181 static char * skipbl (char *str
)
1183 while (*str
&& *str
<= ' ')
1189 static char * trim (char *str
)
1193 for (lastnb
= str
-1, s
= str
; *s
; s
++) /* point to last nonblank characteter in string */
1197 lastnb
[1] = '\0'; /* clip just after it */
1202 /* alltrim - remove all leading and trailing whitespace from a string */
1204 static char * alltrim (char *str
)
1208 if ((s
= skipbl(str
)) != str
) /* slide down over leading whitespace */
1211 for (lastnb
= str
-1, s
= str
; *s
; s
++) /* point to last nonblank characteter in string */
1215 lastnb
[1] = '\0'; /* clip just after it */
1220 /* checkdeck - set hopper empty status based on condition of current reader file */
1222 static void checkdeck (void)
1226 if (cr_unit
.fileref
== NULL
) { /* there is no open file */
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 */
1234 fseek(cr_unit
.fileref
, 0, SEEK_SET
); /* rewind deck */
1238 SETBIT(cr_unit
.flags
, UNIT_CR_EMPTY
);
1239 if (cr_unit
.fileref
!= NULL
) /* real file but it's empty, hmmm, try another */
1243 CLRBIT(cr_unit
.flags
, UNIT_CR_EMPTY
);
1247 /* nextdeck - attempt to load a new file from the deck list into the hopper */
1249 static t_bool
nextdeck (void)
1251 char buf
[200], tmpbuf
[200], *fname
, *c
, quote
, *mode
;
1255 cr_count
= 0; /* clear read count */
1258 if (deckfile
== NULL
) /* we can't help */
1261 code
= GET_CODE(cr_unit
); /* default code as set */
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
;
1267 if (cr_unit
.flags
& UNIT_SCRATCH
) {
1269 CLRBIT(cr_unit
.flags
, UNIT_SCRATCH
);
1273 for (;;) { /* get a filename */
1274 tab_proc
= NULL
; /* default: no tab editing */
1276 if (fgets(buf
, sizeof(buf
), deckfile
) == NULL
)
1277 break; /* oops, no more names */
1279 alltrim(buf
); /* remove leading and trailing spaces */
1282 continue; /* empty line */
1284 if (*buf
== '#' || *buf
== '*' || *buf
== ';')
1285 continue; /* comment */
1287 if (strnicmp(buf
, "!BREAK", 6) == 0) { /* stop the simulation */
1288 break_simulation(STOP_DECK_BREAK
);
1292 if (buf
[0] == '!') { /* literal text line, make a temporary file */
1294 #if defined (__GNUC__) && !defined (_WIN32) /* GCC complains about mktemp & always provides mkstemp */
1296 if (*tempfile
== '\0') { /* first time, open guaranteed-unique file */
1299 strcpy(tempfile
, "tempXXXXXX"); /* get modifiable copy of name template */
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
);
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
);
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
);
1320 #else /* ANSI standard C always provides mktemp */
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
);
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
);
1338 SETBIT(cr_unit
.flags
, UNIT_SCRATCH
);
1340 for (;;) { /* store literal cards into temporary file */
1342 fputs(buf
+1, cr_unit
.fileref
);
1343 putc('\n', cr_unit
.fileref
);
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);
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)
1357 fseek(deckfile
, fpos
, SEEK_SET
); /* restore deck file to just before non-literal card */
1359 fseek(cr_unit
.fileref
, 0, SEEK_SET
); /* rewind scratch file for reading */
1360 code
= CODE_029
; /* assume literal cards use keycode 029 */
1364 sub_args(buf
, tmpbuf
, sizeof(buf
), list_nargs
, list_arg
); /* substitute in stuff from the attach command line */
1366 c
= buf
; /* pick filename from string */
1368 while (*c
&& *c
<= ' ') /* skip leading blanks (there could be some now after subsitution) */
1371 fname
= c
; /* remember start */
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 */
1379 else { /* not quoted; look for terminating whitespace */
1380 while (*c
&& (*c
> ' '))
1385 *c
++ = 0; /* term arg at space or closing quote & move to next character */
1387 if (! *fname
) /* blank line, no filename */
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);
1395 mode
= c
= skipbl(c
); /* skip to next token, which would be mode, if present */
1400 code
= CODE_BINARY
; /* force code */
1401 c
++; /* accept mode character by moving past it */
1409 switch (*c
) { /* is ascii mode followed by another character? */
1412 tab_proc
= EditToFortran
;
1418 tab_proc
= EditToAsm
;
1424 tab_proc
= EditToWhitespace
;
1430 if (code
== CODE_AUTO
) /* otherwise if mode is auto, guess it, otherwise use default */
1431 code
= guess_cr_code();
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
) : "");
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
) : "");
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 */
1447 return (cr_unit
.flags
& UNIT_CR_EMPTY
) == 0;/* return TRUE if a deck has been loaded */
1450 static t_stat
cr_reset (DEVICE
*dptr
)
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*/
1455 cr_set_code(&cr_unit
, GET_ACTCODE(cr_unit
), NULL
, NULL
); /* reset to specified code table */
1457 readstate
= STATION_EMPTY
;
1460 sim_cancel(&cr_unit
); /* cancel any pending ops */
1465 cr_unit
.COLUMN
= -1; /* neither device is currently cycling */
1467 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
1475 static t_stat
cp_reset (DEVICE
*dptr
)
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 */
1480 cp_set_code(&cp_unit
, GET_CODE(cp_unit
), NULL
, NULL
);
1481 punchstate
= STATION_EMPTY
;
1483 cp_unit
.COLUMN
= -1;
1487 t_stat
cr_rewind (void)
1489 if ((cr_unit
.flags
& UNIT_ATT
) == 0)
1493 fseek(deckfile
, 0, SEEK_SET
);
1497 fseek(cr_unit
.fileref
, 0, SEEK_SET
);
1499 cr_set_code(&cr_unit
, GET_CODE(cr_unit
), NULL
, NULL
);
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);
1511 static t_stat
cr_attach (UNIT
*uptr
, char *cptr
)
1514 t_bool use_decklist
;
1515 char *c
, *arg
, quote
;
1517 cr_detach(uptr
); /* detach file and possibly deck file */
1519 CLRBIT(uptr
->flags
, UNIT_SCRATCH
|UNIT_QUIET
|UNIT_DEBUG
|UNIT_PHYSICAL
|UNIT_LOWERCASE
); /* set options */
1522 use_decklist
= FALSE
;
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
);
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
;
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). */
1535 c
= cptr
; /* extract arguments */
1536 for (list_nargs
= 0; list_nargs
< MAXARGS
; list_nargs
++) {
1537 while (*c
&& (*c
<= ' ')) /* skip blanks */
1541 break; /* all done */
1543 if (list_nargs
== 0 && *c
== '@') { /* @ might occur before a quoted name; check first */
1545 use_decklist
= TRUE
;
1548 if (*c
== '\'' || *c
== '"') { /* quoted string */
1550 arg
= c
; /* save start */
1551 while (*c
&& (*c
!= quote
))
1555 arg
= c
; /* save start */
1556 while (*c
&& (*c
> ' '))
1561 *c
++ = 0; /* term arg at space or closing quote */
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 */
1567 if (list_nargs
<= 0) /* need at least 1 */
1570 cr_count
= 0; /* reset card counter */
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
;
1578 else if (sim_switches
& SWMASK('P')) { /* open physical card reader device */
1579 return pcr_attach(uptr
, cptr
);
1582 if (list_nargs
> 1 && ! use_decklist
) /* if not using deck file, there should have been only one name */
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
);
1593 else if ((rval
= attach_unit(uptr
, cptr
)) != SCPE_OK
) {
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);
1601 deckfile
= cr_unit
.fileref
; /* save the deck file stream in our local variable */
1602 cr_unit
.fileref
= NULL
;
1607 cr_set_code(&cr_unit
, GET_CODE(cr_unit
), NULL
, NULL
);
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);
1617 t_stat
cr_detach (UNIT
*uptr
)
1621 cr_count
= 0; /* clear read count */
1623 if (cr_unit
.flags
& UNIT_PHYSICAL
)
1624 return pcr_detach(uptr
);
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
);
1630 if (cr_unit
.flags
& UNIT_SCRATCH
) {
1632 CLRBIT(cr_unit
.flags
, UNIT_SCRATCH
);
1635 cr_unit
.fileref
= deckfile
; /* give scp a file to close */
1638 if (uptr
->fileref
== stdin
) {
1639 CLRBIT(uptr
->flags
, UNIT_ATT
);
1640 free(uptr
->filename
);
1641 uptr
->filename
= NULL
;
1642 uptr
->fileref
= NULL
;
1646 rval
= detach_unit(uptr
);
1651 static t_stat
cp_attach (UNIT
*uptr
, char *cptr
)
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
);
1656 return attach_unit(uptr
, quotefix(cptr
)); /* fix quotes in filenames & attach */
1659 static t_stat
cp_detach (UNIT
*uptr
)
1661 if (cp_unit
.flags
& UNIT_ATT
)
1662 if (punchstate
== STATION_PUNCHED
)
1663 feedcycle(FALSE
, FALSE
); /* flush out card just punched */
1665 any_punched
= 0; /* reset punch detected */
1666 cp_count
= 0; /* clear punch count */
1668 return detach_unit(uptr
);
1671 static void op_done (UNIT
*u
, t_bool issue_intr
)
1673 if (u
->flags
& UNIT_DEBUG
)
1674 DEBUG_PRINT("!CR Op Complete, card %d", cr_count
);
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
);
1681 CLRBIT(cr_dsw
, CR_DSW_1442_BUSY
); /* this is trickier. 1442 cr and cp share a dsw */
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
);
1689 SETBIT(cr_dsw
, CR_DSW_1442_OP_COMPLETE
);
1690 SETBIT(ILSW
[4], ILSW_4_1442_CARD
);
1696 static t_stat
cr_svc (UNIT
*uptr
)
1700 if (uptr
->flags
& UNIT_PHYSICAL
)
1701 return pcr_svc(uptr
);
1703 switch (CURRENT_OP
) {
1708 op_done(&cr_unit
, FALSE
);
1712 if (readstate
== STATION_EMPTY
) { /* read active but no cards? hang */
1713 sim_activate(&cr_unit
, cf_wait
);
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
];
1721 readstate
= STATION_READ
;
1722 if (cr_unit
.flags
& UNIT_DEBUG
)
1723 DEBUG_PRINT("!CR Op Complete, card %d", cr_count
);
1725 op_done(&cr_unit
, TRUE
);
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
);
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);
1735 else { /* ... then issues op-complete */
1736 readstate
= STATION_READ
;
1737 op_done(&cr_unit
, TRUE
);
1742 if (punchstate
== STATION_EMPTY
) { /* punch active but no cards? hang */
1743 sim_activate(&cr_unit
, cf_wait
);
1747 if (cp_unit
.flags
& UNIT_LASTPUNCH
) {
1748 punchstate
= STATION_PUNCHED
;
1749 op_done(&cp_unit
, TRUE
);
1751 else if (++cp_unit
.COLUMN
< 80) {
1752 SETBIT(cr_dsw
, CR_DSW_1442_PUNCH_RESPONSE
);
1753 SETBIT(ILSW
[0], ILSW_0_1442_CARD
);
1755 sim_activate(&cr_unit
, cp_wait
);
1756 if (cr_unit
.flags
& UNIT_DEBUG
)
1757 DEBUG_PRINT("!CR Punch Response");
1760 punchstate
= STATION_PUNCHED
;
1761 op_done(&cp_unit
, TRUE
);
1769 void xio_2501_card (int32 addr
, int32 func
, int32 modify
)
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. */
1780 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
1781 pcr_xio_sense(modify
);
1785 // the following part is questionable -- the 2501 might need to be more picky about setting
1786 // the LAST_CARD bit...
1788 if ((cr_unit
.flags
& UNIT_ATT
) == 0)
1789 lastcard
= TRUE
; /* if nothing to read, hopper's empty */
1790 else if (readstate
== STATION_LOADED
)
1792 else if (cr_unit
.fileref
== NULL
)
1794 else if ((ch
= getc(cr_unit
.fileref
)) != EOF
) {
1795 ungetc(ch
, cr_unit
.fileref
); /* put character back; hopper's not empty */
1798 else if (deckfile
!= NULL
&& nextdeck())
1801 lastcard
= TRUE
; /* there is nothing left to read for a next card */
1803 CLRBIT(cr_dsw
, CR_DSW_2501_LAST_CARD
|CR_DSW_2501_BUSY
|CR_DSW_2501_NOT_READY
);
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
1809 if (CURRENT_OP
!= OP_IDLE
)
1810 SETBIT(cr_dsw
, CR_DSW_2501_BUSY
|CR_DSW_2501_NOT_READY
);
1812 ACC
= cr_dsw
; /* return the DSW */
1814 if (cr_unit
.flags
& UNIT_DEBUG
)
1815 DEBUG_PRINT("#CR Sense %04x%s", cr_dsw
& 0xFFFF, (modify
& 1) ? " RESET" : "");
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
);
1826 if (cr_unit
.flags
& UNIT_DEBUG
)
1827 DEBUG_PRINT("#CR Start read");
1829 cr_unit
.COLUMN
= -1;
1831 cr_cols
= M
[addr
& mem_mask
]; /* save column count and transfer address */
1834 if ((cr_cols
< 0) || (cr_cols
> 80)) /* this is questionable -- what would hardware do? */
1837 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
1838 pcr_xio_startread();
1842 if (readstate
!= STATION_LOADED
)
1843 feedcycle(TRUE
, (cp_unit
.flags
& UNIT_ATT
) != 0);
1846 sim_cancel(&cr_unit
);
1847 sim_activate(&cr_unit
, cr_wait2501
);
1851 sprintf(msg
, "Invalid 2501 XIO function %x", func
);
1857 void xio_1142_card (int32 addr
, int32 func
, int32 modify
)
1866 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
1867 pcr_xio_sense(modify
);
1872 * have to separate out what status is 1442 is punch only and 2501 is the reader */
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
)
1880 else if (cr_unit
.fileref
== NULL
)
1882 else if ((ch
= getc(cr_unit
.fileref
)) != EOF
) {
1883 ungetc(ch
, cr_unit
.fileref
); /* put character back; hopper's not empty */
1886 else if (deckfile
!= NULL
&& nextdeck())
1889 lastcard
= TRUE
; /* there is nothing left to read for a next card */
1891 CLRBIT(cr_dsw
, CR_DSW_1442_LAST_CARD
| CR_DSW_1442_BUSY
| CR_DSW_1442_NOT_READY
);
1894 SETBIT(cr_dsw
, CR_DSW_1442_LAST_CARD
);
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
);
1901 ACC
= cr_dsw
; /* return the DSW */
1903 if (cr_unit
.flags
& UNIT_DEBUG
)
1904 DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw
& 0xFFFF, (modify
& 1) ? " RESET0" : "", (modify
& 2) ? " RESET4" : "");
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
);
1911 if (modify
& 0x02) {
1912 CLRBIT(cr_dsw
, CR_DSW_1442_OP_COMPLETE
);
1913 CLRBIT(ILSW
[4], ILSW_4_1442_CARD
);
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!");
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));
1927 else if (cr_unit
.COLUMN
== 80) {
1928 xio_error("1442: Read past column 80!");
1929 cr_unit
.COLUMN
++; /* don't report it again */
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!"); */
1941 if (cr_unit
.flags
& OP_PUNCHING
) {
1942 if (cp_unit
.COLUMN
< 0) {
1943 xio_error("1442: Premature write!");
1945 else if (cp_unit
.flags
& UNIT_LASTPUNCH
) {
1946 xio_error("1442: Punch past last-punch column!");
1947 cp_unit
.COLUMN
= 81;
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" : "");
1957 else if (cp_unit
.COLUMN
== 80) {
1958 xio_error("1442: Punch past column 80!");
1959 cp_unit
.COLUMN
++; /* don't report it again */
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!"); */
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
);
1978 SET_OP(OP_PUNCHING
);
1979 cp_unit
.COLUMN
= -1;
1981 CLRBIT(cp_unit
.flags
, UNIT_LASTPUNCH
);
1983 any_punched
= 1; /* we've started punching, so enable writing to output deck file */
1985 sim_cancel(&cr_unit
);
1986 sim_activate(&cr_unit
, cp_wait
);
1989 case 2: /* feed cycle */
1990 if (cr_unit
.flags
& UNIT_DEBUG
)
1991 DEBUG_PRINT("#CR Feed");
1993 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
1994 pcr_xio_feedcycle();
1998 feedcycle(TRUE
, (cp_unit
.flags
& UNIT_ATT
) != 0);
2001 sim_cancel(&cr_unit
);
2002 sim_activate(&cr_unit
, cf_wait
);
2005 case 4: /* start read */
2006 if (cr_unit
.flags
& UNIT_DEBUG
)
2007 DEBUG_PRINT("#CR Start read");
2009 cr_unit
.COLUMN
= -1;
2011 if (cr_unit
.flags
& UNIT_PHYSICAL
) {
2012 pcr_xio_startread();
2016 if (readstate
!= STATION_LOADED
)
2017 feedcycle(TRUE
, (cp_unit
.flags
& UNIT_ATT
) != 0);
2020 sim_cancel(&cr_unit
);
2021 sim_activate(&cr_unit
, cr_wait
);
2025 if (cr_unit
.flags
& UNIT_DEBUG
)
2026 DEBUG_PRINT("#CR NOP");
2030 sprintf(msg
, "1442: Multiple operations in XIO_CONTROL: %x", modify
);
2038 sprintf(msg
, "Invalid 1442 XIO function %x", func
);
2044 #if ! (defined(ENABLE_PHYSICAL_CARD_READER_SUPPORT) && defined(WIN32))
2046 /* stub out the physical card reader routines */
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) {}
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
2063 #include <windows.h>
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
2071 #define PCR_STATUS_MSEC 150 /* when idle, get status every 150 msec */
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 */
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
);
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
= '?';
2105 /* pcr_attach - perform attach function to physical card reader */
2107 static t_stat
pcr_attach (UNIT
*uptr
, char *devname
)
2112 pcr_state
= PCR_STATE_CLOSED
;
2114 cr_unit
.COLUMN
= -1; /* device is not currently cycling */
2116 if ((rval
= pcr_open_controller(devname
)) != SCPE_OK
)
2119 if (hPickEvent
== INVALID_HANDLE_VALUE
)
2120 hPickEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
2122 if (hResetEvent
== INVALID_HANDLE_VALUE
)
2123 hResetEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
2125 pcr_status
= PCR_STATUS_HEMPTY
; /* set default status: offline, no cards */
2126 pcr_state
= PCR_STATE_IDLE
;
2128 cr_dsw
= CR_DSW_1442_LAST_CARD
| CR_DSW_1442_NOT_READY
;
2130 set_active_cr_code(CODE_BINARY
); /* force binary mode */
2132 if (CreateThread(NULL
, 0, pcr_thread
, NULL
, 0, &thread_id
) == NULL
) {
2133 pcr_state
= PCR_STATE_CLOSED
;
2135 hpcr
= INVALID_HANDLE_VALUE
;
2136 printf("Error creating card reader thread\n");
2140 SETBIT(uptr
->flags
, UNIT_PHYSICAL
|UNIT_ATT
); /* mark device as attached */
2141 uptr
->filename
= malloc(strlen(devname
)+1);
2142 strcpy(uptr
->filename
, devname
);
2147 /* pcr_open_controller - open the USB device's virtual COM port and configure the interface */
2149 static t_stat
pcr_open_controller (char *devname
)
2155 if (hpcr
!= INVALID_HANDLE_VALUE
)
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
;
2162 memset(&dcb
, 0, sizeof(dcb
)); /* set communications parameters */
2164 dcb
.DCBlength
= sizeof(DCB
);
2165 dcb
.BaudRate
= CBR_115200
; /* for the USB virtual com port, baud rate is irrelevant */
2168 dcb
.fOutxCtsFlow
= 0;
2169 dcb
.fOutxDsrFlow
= 0;
2170 dcb
.fDtrControl
= DTR_CONTROL_ENABLE
;
2171 dcb
.fDsrSensitivity
= FALSE
;
2172 dcb
.fTXContinueOnXoff
= 0;
2177 dcb
.fRtsControl
= RTS_CONTROL_ENABLE
;
2178 dcb
.fAbortOnError
= 0;
2182 dcb
.Parity
= NOPARITY
;
2183 dcb
.StopBits
= ONESTOPBIT
;
2190 if (! SetCommState(hpcr
, &dcb
)) {
2192 hpcr
= INVALID_HANDLE_VALUE
;
2193 printf("Call to SetCommState failed\n");
2194 return SCPE_OPENERR
;
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) */
2201 cto
.WriteTotalTimeoutMultiplier
= 0;
2202 cto
.WriteTotalTimeoutConstant
= 200; /* allow 200 msec for a write */
2204 if (! SetCommTimeouts(hpcr
, &cto
)) {
2206 hpcr
= INVALID_HANDLE_VALUE
;
2207 printf("Call to SetCommTimeouts failed\n");
2208 return SCPE_OPENERR
;
2211 PurgeComm(hpcr
, PURGE_TXABORT
|PURGE_RXABORT
|PURGE_TXCLEAR
|PURGE_RXCLEAR
);
2212 ClearCommError(hpcr
, &nerr
, NULL
);
2217 /* pcr_detach - detach physical reader from CR device */
2219 static t_stat
pcr_detach (UNIT
*uptr
)
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
;
2226 free(uptr
->filename
); /* release the name copy */
2227 uptr
->filename
= NULL
;
2230 CLRBIT(cr_unit
.flags
, UNIT_PHYSICAL
|UNIT_ATT
); /* drop the attach and physical bits */
2234 /* pcr_xio_sense - perform XIO sense function on physical card reader */
2236 static void pcr_xio_sense (int modify
)
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
);
2243 if (modify
& 0x02) {
2244 CLRBIT(cr_dsw
, CR_DSW_1442_OP_COMPLETE
);
2245 CLRBIT(ILSW
[4], ILSW_4_1442_CARD
);
2248 ACC
= cr_dsw
; /* DSW was set in real-time, just return the DSW */
2250 if (cr_unit
.flags
& UNIT_DEBUG
)
2251 DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw
, (modify
& 1) ? " RESET0" : "", (modify
& 2) ? " RESET4" : "");
2254 /* report_error - issue detailed report of Windows IO error */
2256 static void report_error (char *msg
, DWORD err
)
2258 char *lpMessageBuffer
= NULL
;
2261 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
2262 FORMAT_MESSAGE_FROM_SYSTEM
,
2265 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), /* The user default language */
2266 (LPTSTR
) &lpMessageBuffer
,
2270 printf("GetOverlappedResult failed, %s, %s\n",
2271 msg
, lpMessageBuffer
);
2273 LocalFree(lpMessageBuffer
);
2276 /* pcr_thread - thread to handle card reader interface communications */
2278 static DWORD CALLBACK
pcr_thread (LPVOID arg
)
2281 long nrcvd
, nread
, nwritten
;
2283 BOOL pick_queued
= FALSE
, reset_queued
= FALSE
;
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 */
2292 objs
[0] = ovRd
.hEvent
;
2293 objs
[1] = ovWr
.hEvent
;
2294 objs
[2] = hResetEvent
;
2295 objs
[3] = hPickEvent
;
2297 while (hpcr
!= INVALID_HANDLE_VALUE
) {
2298 if (pcr_state
== PCR_STATE_IDLE
) {
2301 pick_queued
= FALSE
;
2303 pcr_state
= PCR_STATE_WAIT_PICK_CMD_RESPONSE
;
2305 else if (reset_queued
) {
2307 reset_queued
= FALSE
;
2308 pcr_state
= PCR_STATE_WAIT_CMD_RESPONSE
;
2312 event
= WaitForMultipleObjects(4, objs
, FALSE
, PCR_STATUS_MSEC
);
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
);
2323 case WAIT_OBJECT_0
+1: /* write complete */
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
);
2332 case WAIT_OBJECT_0
+2: /* reset request from simulator */
2333 reset_queued
= TRUE
;
2334 pick_queued
= FALSE
;
2337 case WAIT_OBJECT_0
+3: /* pick request from simulator */
2342 if (pcr_state
== PCR_STATE_IDLE
) {
2343 pcr_state
= PCR_STATE_WAIT_CMD_RESPONSE
;
2344 ovRd
.Offset
= ovRd
.OffsetHigh
= 0;
2347 else if (pcr_state
== PCR_STATE_WAIT_CMD_RESPONSE
&& ++nwaits
>= 6) {
2348 printf("Requesting status again!\n");
2349 ovRd
.Offset
= ovRd
.OffsetHigh
= 0;
2355 printf("Unexpected pcr_wait result %08lx", event
);
2359 /* We only get here if read event occurred */
2361 switch (pcr_state
) {
2362 case PCR_STATE_IDLE
: /* nothing expected from the interface */
2363 PurgeComm(hpcr
, PURGE_RXCLEAR
|PURGE_RXABORT
);
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
;
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
;
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");
2383 continue; /* reader is not ready */
2386 if (cr_unit
.flags
& UNIT_DEBUG
) /* (this could take an indefinite amount of time) */
2387 printf("PCR: GOT %c\n", response_byte
);
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
);
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 */
2402 default: /* anything else is a datacomm error, or something */
2403 /* indicate read check or something */
2404 /* pcr_state = PCR_STATE_IDLE; */
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
);
2416 begin_pcr_critical_section();
2417 pcr_nready
+= nrcvd
;
2418 end_pcr_critical_section();
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
);
2427 pcr_state
= PCR_STATE_WAIT_PICK_FINAL_RESPONSE
;
2428 pcr_cmd('\0'); /* queue read */
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
;
2442 CloseHandle(ovRd
.hEvent
);
2443 CloseHandle(ovWr
.hEvent
);
2448 /* pcr_cmd - issue command byte to interface. Read of response byte is queued */
2450 static void pcr_cmd (char cmd
)
2452 long nwritten
, nrcvd
;
2456 if (cr_unit
.flags
& UNIT_DEBUG
/* && (cmd != 'S' || cmd != lastcmd) */)
2457 printf("PCR: SENT %c\n", cmd
);
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");
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");
2473 /* if (cr_unit.flags & UNIT_DEBUG)
2475 * printf("PCR: NO RESPONSE\n");
2477 * printf("PCR: RESPONSE %c\n", response_byte); */
2482 /* pcr_handle_status_byte - handle completion of read of response byte */
2484 static BOOL
pcr_handle_status_byte (int nrcvd
)
2486 static char prev_status
= '?';
2492 pcr_status
= response_byte
; /* save new status */
2494 show
= lastcmd
!= 'S' || pcr_status
!= prev_status
;
2496 if ((cr_unit
.flags
& UNIT_DEBUG
) && show
) {
2497 printf("PCR: status %c\n", pcr_status
);
2498 prev_status
= pcr_status
;
2501 pcr_set_dsw_from_status(FALSE
);
2506 /* pcr_set_dsw_from_status - construct device status word from current physical reader status */
2508 static void pcr_set_dsw_from_status (BOOL post_pick
)
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
);
2513 if (pcr_status
& PCR_STATUS_HEMPTY
)
2514 SETBIT(cr_dsw
, CR_DSW_1442_LAST_CARD
| CR_DSW_1442_NOT_READY
);
2516 if (pcr_status
& PCR_STATUS_ERROR
)
2517 SETBIT(cr_dsw
, CR_DSW_1442_ERROR_CHECK
);
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 */
2522 if ((! post_pick
) && ! (pcr_status
& PCR_STATUS_READY
))
2523 SETBIT(cr_dsw
, CR_DSW_1442_NOT_READY
);
2525 if (CURRENT_OP
!= OP_IDLE
)
2526 SETBIT(cr_dsw
, CR_DSW_1442_BUSY
| CR_DSW_1442_NOT_READY
);
2529 static void pcr_xio_feedcycle (void)
2532 cr_unit
.COLUMN
= -1;
2533 SetEvent(hPickEvent
);
2534 sim_activate(&cr_unit
, cr_wait
); /* keep checking frequently */
2537 static void pcr_xio_startread (void)
2540 cr_unit
.COLUMN
= -1;
2543 SetEvent(hPickEvent
);
2544 sim_activate(&cr_unit
, cr_wait
); /* keep checking frequently */
2547 static void pcr_reset (void)
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
;
2553 sim_cancel(&cr_unit
);
2555 SetEvent(hResetEvent
);
2558 /* pcr_trigger_interrupt_0 - simulate a read response interrupt so OS will read queued column data */
2560 static void pcr_trigger_interrupt_0 (void)
2562 if (++cr_unit
.COLUMN
< 80) {
2563 SETBIT(cr_dsw
, CR_DSW_1442_READ_RESPONSE
);
2564 SETBIT(ILSW
[0], ILSW_0_1442_CARD
);
2567 begin_pcr_critical_section();
2569 end_pcr_critical_section();
2571 if (cr_unit
.flags
& UNIT_DEBUG
)
2572 printf("SET IRQ0 col %d\n", cr_unit
.COLUMN
+1);
2576 static t_stat
pcr_svc (UNIT
*uptr
)
2578 switch (CURRENT_OP
) {
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 */
2585 pcr_trigger_interrupt_0();
2586 sim_activate(&cr_unit
, cr_wait
); /* keep checking frequently */
2588 else if (pcr_done
) {
2591 op_done(&cr_unit
, TRUE
);
2592 pcr_set_dsw_from_status(TRUE
);
2595 sim_activate(&cr_unit
, cr_wait
); /* keep checking frequently */
2601 op_done(&cr_unit
, FALSE
);
2602 pcr_set_dsw_from_status(TRUE
);
2605 sim_activate(&cr_unit
, cr_wait
); /* keep checking frequently */
2610 return cr_svc(uptr
);
2616 static CRITICAL_SECTION pcr_critsect
;
2618 static void begin_pcr_critical_section (void)
2620 static BOOL mustinit
= TRUE
;
2623 InitializeCriticalSection(&pcr_critsect
);
2627 EnterCriticalSection(&pcr_critsect
);
2630 static void end_pcr_critical_section (void)
2632 LeaveCriticalSection(&pcr_critsect
);