WARNING: V2.10 has reorganized and renamed some of the definition files for the PDP-10, PDP-11, and VAX. Be sure to delete all previous source files before you unpack the Zip archive, or unpack it into a new directory structure. WARNING: V2.10 has a new, more comprehensive save file format. Restoring save files from previous releases will cause 'invalid register' errors and loss of CPU option flags, device enable/ disable flags, unit online/offline flags, and unit writelock flags. WARNING: If you are using Visual Studio .NET through the IDE, be sure to turn off the /Wp64 flag in the project settings, or dozens of spurious errors will be generated. WARNING: Compiling Ethernet support under Windows requires extra steps; see the Ethernet readme file. Ethernet support is currently available only for Windows, Linux, NetBSD, and OpenBSD. 1. New Features 1.1 SCP and Libraries - The VT emulation package has been replaced by the capability to remote the console to a Telnet session. Telnet clients typically have more complete and robust VT100 emulation. - Simulated devices may now have statically allocated buffers, in addition to dynamically allocated buffers or disk-based data stores. - The DO command now takes substitutable arguments (max 9). In command files, %n represents substitutable argument n. - The initial command line is now interpreted as the command name and substitutable arguments for a DO command. This is backward compatible to prior versions. - The initial command line parses switches. -Q is interpreted as quiet mode; informational messages are suppressed. - The HELP command now takes an optional argument. HELP <cmd> types help on the specified command. - Hooks have been added for implementing GUI-based consoles, as well as simulator-specific command extensions. A few internal data structures and definitions have changed. - Two new routines (tmxr_open_master, tmxr_close_master) have been added to sim_tmxr.c. The calling sequence for sim_accept_conn has been changed in sim_sock.c. - The calling sequence for the VM boot routine has been modified to add an additional parameter. - SAVE now saves, and GET now restores, controller and unit flags. - Library sim_ether.c has been added for Ethernet support. 1.2 VAX - Non-volatile RAM (NVR) can behave either like a memory or like a disk-based peripheral. If unattached, it behaves like memory and is saved and restored by SAVE and RESTORE, respectively. If attached, its contents are loaded from disk by ATTACH and written back to disk at DETACH and EXIT. - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape) has been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from vax_stddev.c and now references a common implementation file, dec_pt.h. - Examine and deposit switches now work on all devices, not just the CPU. - Device address conflicts are not detected until simulation starts. 1.3 PDP-11 - SHOW <device> VECTOR displays the device's interrupt vector. Most devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk), RX211 (double density floppy), and KW11P programmable clock have been added. - The DEQNA/DELQA (Qbus Ethernet controllers) have been added. - Autoconfiguration support has been added. - The paper tape reader has been removed from pdp11_stddev.c and now references a common implementation file, dec_pt.h. - Device bootstraps now use the actual CSR specified by the SET ADDRESS command, rather than just the default CSR. Note that PDP-11 operating systems may NOT support booting with non-standard addresses. - Specifying more than 256KB of memory, or changing the bus configuration, causes all peripherals that are not compatible with the current bus configuration to be disabled. - Device address conflicts are not detected until simulation starts. 1.4 PDP-10 - SHOW <device> VECTOR displays the device's interrupt vector. A few devices allow the vector to be changed with SET <device> VECTOR=nnn. - SHOW CPU IOSPACE displays the I/O space address map. - The RX211 (double density floppy) has been added; it is off by default. - The paper tape now references a common implementation file, dec_pt.h. - Device address conflicts are not detected until simulation starts. 1.5 PDP-1 - DECtape (then known as MicroTape) support has been added. - The line printer and DECtape can be disabled and enabled. 1.6 PDP-8 - The RX28 (double density floppy) has been added as an option to the existing RX8E controller. - SHOW <device> DEVNO displays the device's device number. Most devices allow the device number to be changed with SET <device> DEVNO=nnn. - Device number conflicts are not detected until simulation starts. 1.7 IBM 1620 - The IBM 1620 simulator has been released. 1.8 AltairZ80 - A hard drive has been added for increased storage. - Several bugs have been fixed. 1.9 HP 2100 - The 12845A has been added and made the default line printer (LPT). The 12653A has been renamed LPS and is off by default. It also supports the diagnostic functions needed to run the DCPC and DMS diagnostics. - The 12557A/13210A disk defaults to the 13210A (7900/7901). - The 12559A magtape is off by default. - New CPU options (EAU/NOEAU) enable/disable the extended arithmetic instructions for the 2116. These instructions are standard on the 2100 and 21MX. - New CPU options (MPR/NOMPR) enable/disable memory protect for the 2100 and 21MX. - New CPU options (DMS/NODMS) enable/disable the dynamic mapping instructions for the 21MX. - The 12539 timebase generator autocalibrates. 1.10 Simulated Magtapes - Simulated magtapes recognize end of file and the marker 0xFFFFFFFF as end of medium. Only the TMSCP tape simulator can generate an end of medium marker. - The error handling in simulated magtapes was overhauled to be consistent through all simulators. 1.11 Simulated DECtapes - Added support for RT11 image file format (256 x 16b) to DECtapes. 2. Release Notes 2.1 Bugs Fixed - TS11/TSV05 was not simulating the XS0_MOT bit, causing failures under VMS. In addition, two of the CTL options were coded interchanged. - IBM 1401 tape was not setting a word mark under group mark for load mode reads. This caused the diagnostics to crash. - SCP bugs in ssh_break and set_logon were fixed (found by Dave Hittner). - Numerous bugs in the HP 2100 extended arithmetic, floating point, 21MX, DMS, and IOP instructions were fixed. Bugs were also fixed in the memory protect and DMS functions. The moving head disks (DP, DQ) were revised to simulate the hardware more accurately. Missing functions in DQ (address skip, read address) were added. 2.2 HP 2100 Debugging - The HP 2100 CPU nows runs all of the CPU diagnostics. - The peripherals run most of the peripheral diagnostics. There is still a problem in overlapped seek operation on the disks. See the file hp2100_diag.txt for details. 3. In Progress These simulators are not finished and are available in a separate Zip archive distribution. - Interdata 16b/32b: coded, partially tested. See the file id_diag.txt for details. - SDS 940: coded, partially tested.
1305 lines
35 KiB
C
1305 lines
35 KiB
C
#include "ibm1130_defs.h"
|
|
|
|
/* ibm1130_cr.c: IBM 1130 1442 Card Reader simulator
|
|
|
|
Based on the SIMH package written by Robert M Supnik
|
|
|
|
* (C) Copyright 2002, Brian Knittel.
|
|
* You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
|
|
* RISK basis, there is no warranty of fitness for any purpose, and the rest of the
|
|
* usual yada-yada. Please keep this notice and the copyright in any distributions
|
|
* or modifications.
|
|
*
|
|
* This is not a supported product, but I welcome bug reports and fixes.
|
|
* Mail to sim@ibm1130.org
|
|
|
|
NOTE - there is a problem with this code. The Device Status Word (DSW) is
|
|
computed from current conditions when requested by an XIO load status
|
|
command; the value of DSW available to the simulator's examine & save
|
|
commands may NOT be accurate. This should probably be fixed.
|
|
|
|
* Update 2002-02-29: Added deck-list option. If you issue an attach
|
|
command and specify the filename as "@filename", the named file is interpreted
|
|
as a list of filenames to be read in sequence; the effect is that the reader
|
|
sees the concatenation of all of the files named. "reset" rewinds the deck
|
|
list. Filenames can be followed by whitespace and the letter "a" or "b",
|
|
which indicates "ascii to 029" or "binary", respectively. Example:
|
|
|
|
attach cr @decklist
|
|
|
|
where file "decklist" contains:
|
|
|
|
file01 a
|
|
file02 b
|
|
file03 b
|
|
file04 b
|
|
|
|
If "a" or "b" is not specified, the device mode setting is used.
|
|
|
|
('a' means 029, so, if you need 026 coding, specify the
|
|
device default as the correct 026 code and omit the 'a' on the text files lines).
|
|
|
|
* note: I'm not sure but I think we'll need a way to simulate the 'start'
|
|
button. What may end up being necessary is to fake this in the 'attach'
|
|
command. In a GUI build we may want to wait until they press a button.
|
|
Have to research: does DMS issue a read request which is only
|
|
satisfied when START is pressed, or does START cause an interrupt that
|
|
then asks DMS to issue a read request. I think it's the former but need
|
|
to check. After all the status register says "empty" and "not ready"
|
|
when the hopper is empty. So what gives? On the 360 I think the start
|
|
button causes issues some sort of attention request.
|
|
|
|
* Card image format.
|
|
Card files can be ascii text or binary. There are several ASCII modes:
|
|
CODE_029, CODE_26F, etc, corresponding to different code sets.
|
|
Punch and reader modes can be set independently.
|
|
|
|
The 1442 card read/punch has several cycles:
|
|
|
|
feed cycle: moves card from hopper to read station
|
|
card from read station to punch station
|
|
card from punch station to stacker
|
|
|
|
read or punch: operates on card at read or punch station (but not both).
|
|
|
|
The simulator requires input cards to be read from the file attached
|
|
to the card reader unit. A feed cycle reads one line (text mode) or
|
|
160 bytes (binary mode) from the input file to the read station buffer,
|
|
copies the read station buffer to the punch station buffer, and if
|
|
the punch unit is attached to a file, writes the punch station buffer to
|
|
the output file.
|
|
|
|
The read and punch cycles operate on the appropriate card buffer.
|
|
|
|
Detaching the card punch flushes the punch station buffer if necessary.
|
|
|
|
As does the 1442, a read or punch cycle w/o a feed cycle causes a
|
|
feed cycle first.
|
|
|
|
A feed cycle on an empty deck (reader unattaced or at EOF) clears
|
|
the appropriate buffer, so you can punch w/o attaching a deck to
|
|
the card reader.
|
|
|
|
// -- this may need changing depending on how things work in hardware. TBD.
|
|
|| A read cycle on an empty deck causes an error.
|
|
|| Hmmm -- what takes the place of the Start button on
|
|
\\ the card reader?
|
|
|
|
Binary format is stored using fxwrite of short ints, in this format:
|
|
|
|
1 1
|
|
2 2 0 1 2 3 4 5 6 7 8 9
|
|
* * * * * * * * * * * * 0 0 0 0
|
|
|
|
MSB LSB
|
|
byte 0 [ 6] [ 7] [ 8] [ 9] 0 0 0 0
|
|
byte 1 [12] [11] [ 0] [ 1] [ 2] [ 3] [ 4] [ 5]
|
|
|
|
This means we can read words (little endian) and get this in memory:
|
|
|
|
12 11 0 1 2 3 4 5 6 7 8 9 - - - -
|
|
|
|
which is what the 1130 sees.
|
|
|
|
ASCII can be read in blocks of 80 characters but can be terminated by newline prematurely.
|
|
|
|
Booting: card reader IPL loads 80 columns (1 card) into memory starting
|
|
at location 0 in a split fashion:
|
|
|
|
________________ _ _ _
|
|
/
|
|
12 |
|
|
11 |
|
|
0 |
|
|
1 |
|
|
2 |
|
|
3 | Punched card
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
+------------------ - - -
|
|
|
|
12 11 0 1 2 3 4 5 6 7 8 9
|
|
| | | | | 0 0 0 / \ | | | | | |
|
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|
|
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
| OPCODE | F| Tag | DISPLACEMENT |
|
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
|
|
The zeros mean that all IPL instructions are short form,
|
|
nonindexed. The 3 column is repeated in bits 8 and 9 so
|
|
it's a sign bit.
|
|
|
|
Boot command on a binary deck does this. Boot on an unattached
|
|
reader loads the standard boot2 card image. Boot with an ASCII
|
|
deck will not be very helpful.
|
|
*/
|
|
|
|
#define READ_DELAY 35 // see how small a number we can get away with
|
|
#define PUNCH_DELAY 35
|
|
#define FEED_DELAY 25
|
|
|
|
// #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
|
|
|
|
extern int32 sim_switches;
|
|
|
|
static t_stat cr_svc (UNIT *uptr);
|
|
static t_stat cr_reset (DEVICE *dptr);
|
|
static t_stat cr_set_code (UNIT *uptr, int32 match);
|
|
static t_stat cr_attach (UNIT *uptr, char *cptr);
|
|
static t_stat cr_detach (UNIT *uptr);
|
|
|
|
static t_stat cp_reset (DEVICE *dptr);
|
|
static t_stat cp_set_code (UNIT *uptr, int32 match);
|
|
static t_stat cp_detach (UNIT *uptr);
|
|
|
|
static int16 cr_dsw = 0; /* device status word */
|
|
static int32 cr_wait = READ_DELAY; /* read per-column wait */
|
|
static int32 cf_wait = PUNCH_DELAY; /* punch per-column wait */
|
|
static int32 cp_wait = FEED_DELAY; /* feed op wait */
|
|
|
|
#define UNIT_V_OPERATION (UNIT_V_UF + 0) /* operation in progress */
|
|
#define UNIT_V_CODE (UNIT_V_UF + 2)
|
|
#define UNIT_V_EMPTY (UNIT_V_UF + 4)
|
|
#define UNIT_V_SCRATCH (UNIT_V_UF + 5)
|
|
#define UNIT_V_QUIET (UNIT_V_UF + 6)
|
|
#define UNIT_V_DEBUG (UNIT_V_UF + 7)
|
|
|
|
#define UNIT_V_LASTPUNCH (UNIT_V_UF + 0) /* bit in unit_cp flags */
|
|
|
|
#define UNIT_OP (3u << UNIT_V_OPERATION) /* two bits */
|
|
#define UNIT_CODE (3u << UNIT_V_CODE) /* two bits */
|
|
#define UNIT_EMPTY (1u << UNIT_V_EMPTY)
|
|
#define UNIT_SCRATCH (1u << UNIT_V_SCRATCH) /* temp file */
|
|
#define UNIT_QUIET (1u << UNIT_V_QUIET)
|
|
#define UNIT_DEBUG (1u << UNIT_V_DEBUG)
|
|
|
|
#define UNIT_LASTPUNCH (1u << UNIT_V_LASTPUNCH)
|
|
|
|
#define OP_IDLE (0u << UNIT_V_OPERATION)
|
|
#define OP_READING (1u << UNIT_V_OPERATION)
|
|
#define OP_PUNCHING (2u << UNIT_V_OPERATION)
|
|
#define OP_FEEDING (3u << UNIT_V_OPERATION)
|
|
|
|
#define SET_OP(op) {cr_unit.flags &= ~UNIT_OP; cr_unit.flags |= op;}
|
|
|
|
#define CURRENT_OP (cr_unit.flags & UNIT_OP)
|
|
|
|
#define CODE_029 (0u << UNIT_V_CODE)
|
|
#define CODE_026F (1u << UNIT_V_CODE)
|
|
#define CODE_026C (2u << UNIT_V_CODE)
|
|
#define CODE_BINARY (3u << UNIT_V_CODE)
|
|
|
|
#define SET_CODE(un,cd) {un.flags &= ~UNIT_CODE; un.flags |= cd;}
|
|
|
|
#define COLUMN u4 /* column field in unit record */
|
|
|
|
UNIT cr_unit = { UDATA (&cr_svc, UNIT_ATTABLE|UNIT_ROABLE, 0) };
|
|
UNIT cp_unit = { UDATA (NULL, UNIT_ATTABLE, 0) };
|
|
|
|
MTAB cr_mod[] = {
|
|
{ UNIT_CODE, CODE_029, "029", "029", &cr_set_code},
|
|
{ UNIT_CODE, CODE_026F, "026F", "026F", &cr_set_code},
|
|
{ UNIT_CODE, CODE_026C, "026C", "026C", &cr_set_code},
|
|
{ UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cr_set_code},
|
|
{ 0 } };
|
|
|
|
MTAB cp_mod[] = {
|
|
{ UNIT_CODE, CODE_029, "029", "029", &cp_set_code},
|
|
{ UNIT_CODE, CODE_026F, "026F", "026F", &cp_set_code},
|
|
{ UNIT_CODE, CODE_026C, "026C", "026C", &cp_set_code},
|
|
{ UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cp_set_code},
|
|
{ 0 } };
|
|
|
|
REG cr_reg[] = {
|
|
{ HRDATA (CRDSW, cr_dsw, 16) }, /* device status word */
|
|
{ DRDATA (CRTIME, cr_wait, 24), PV_LEFT }, /* operation wait */
|
|
{ DRDATA (CFTIME, cf_wait, 24), PV_LEFT }, /* operation wait */
|
|
{ NULL } };
|
|
|
|
REG cp_reg[] = {
|
|
{ DRDATA (CPTIME, cp_wait, 24), PV_LEFT }, /* operation wait */
|
|
{ NULL } };
|
|
|
|
DEVICE cr_dev = {
|
|
"CR", &cr_unit, cr_reg, cr_mod,
|
|
1, 16, 16, 1, 16, 16,
|
|
NULL, NULL, cr_reset,
|
|
cr_boot, cr_attach, cr_detach};
|
|
|
|
DEVICE cp_dev = {
|
|
"CP", &cp_unit, cp_reg, cp_mod,
|
|
1, 16, 16, 1, 16, 16,
|
|
NULL, NULL, cp_reset,
|
|
NULL, NULL, cp_detach};
|
|
|
|
#define CR_DSW_READ_RESPONSE 0x8000 /* device status word bits */
|
|
#define CR_DSW_PUNCH_RESPONSE 0x4000
|
|
#define CR_DSW_ERROR_CHECK 0x2000
|
|
#define CR_DSW_LAST_CARD 0x1000
|
|
#define CR_DSW_OP_COMPLETE 0x0800
|
|
#define CR_DSW_FEED_CHECK 0x0100
|
|
#define CR_DSW_BUSY 0x0002
|
|
#define CR_DSW_NOT_READY 0x0001
|
|
|
|
typedef struct {
|
|
int hollerith;
|
|
char ascii;
|
|
} CPCODE;
|
|
|
|
static CPCODE cardcode_029[] =
|
|
{
|
|
0x0000, ' ',
|
|
0x8000, '&', // + in 026 Fortran
|
|
0x4000, '-',
|
|
0x2000, '0',
|
|
0x1000, '1',
|
|
0x0800, '2',
|
|
0x0400, '3',
|
|
0x0200, '4',
|
|
0x0100, '5',
|
|
0x0080, '6',
|
|
0x0040, '7',
|
|
0x0020, '8',
|
|
0x0010, '9',
|
|
0x9000, 'A',
|
|
0x8800, 'B',
|
|
0x8400, 'C',
|
|
0x8200, 'D',
|
|
0x8100, 'E',
|
|
0x8080, 'F',
|
|
0x8040, 'G',
|
|
0x8020, 'H',
|
|
0x8010, 'I',
|
|
0x5000, 'J',
|
|
0x4800, 'K',
|
|
0x4400, 'L',
|
|
0x4200, 'M',
|
|
0x4100, 'N',
|
|
0x4080, 'O',
|
|
0x4040, 'P',
|
|
0x4020, 'Q',
|
|
0x4010, 'R',
|
|
0x3000, '/',
|
|
0x2800, 'S',
|
|
0x2400, 'T',
|
|
0x2200, 'U',
|
|
0x2100, 'V',
|
|
0x2080, 'W',
|
|
0x2040, 'X',
|
|
0x2020, 'Y',
|
|
0x2010, 'Z',
|
|
0x0820, ':',
|
|
0x0420, '#', // = in 026 Fortran
|
|
0x0220, '@', // ' in 026 Fortran
|
|
0x0120, '\'',
|
|
0x00A0, '=',
|
|
0x0060, '"',
|
|
0x8820, 'c', // cent
|
|
0x8420, '.',
|
|
0x8220, '<', // ) in 026 Fortran
|
|
0x8120, '(',
|
|
0x80A0, '+',
|
|
0x8060, '|',
|
|
0x4820, '!',
|
|
0x4420, '$',
|
|
0x4220, '*',
|
|
0x4120, ')',
|
|
0x40A0, ';',
|
|
0x4060, 'n', // not
|
|
0x2820, 'x', // what?
|
|
0x2420, ',',
|
|
0x2220, '%', // ( in 026 Fortran
|
|
0x2120, '_',
|
|
0x20A0, '>',
|
|
0x2060, '>',
|
|
};
|
|
|
|
static CPCODE cardcode_026F[] = // 026 fortran
|
|
{
|
|
0x0000, ' ',
|
|
0x8000, '+',
|
|
0x4000, '-',
|
|
0x2000, '0',
|
|
0x1000, '1',
|
|
0x0800, '2',
|
|
0x0400, '3',
|
|
0x0200, '4',
|
|
0x0100, '5',
|
|
0x0080, '6',
|
|
0x0040, '7',
|
|
0x0020, '8',
|
|
0x0010, '9',
|
|
0x9000, 'A',
|
|
0x8800, 'B',
|
|
0x8400, 'C',
|
|
0x8200, 'D',
|
|
0x8100, 'E',
|
|
0x8080, 'F',
|
|
0x8040, 'G',
|
|
0x8020, 'H',
|
|
0x8010, 'I',
|
|
0x5000, 'J',
|
|
0x4800, 'K',
|
|
0x4400, 'L',
|
|
0x4200, 'M',
|
|
0x4100, 'N',
|
|
0x4080, 'O',
|
|
0x4040, 'P',
|
|
0x4020, 'Q',
|
|
0x4010, 'R',
|
|
0x3000, '/',
|
|
0x2800, 'S',
|
|
0x2400, 'T',
|
|
0x2200, 'U',
|
|
0x2100, 'V',
|
|
0x2080, 'W',
|
|
0x2040, 'X',
|
|
0x2020, 'Y',
|
|
0x2010, 'Z',
|
|
0x0420, '=',
|
|
0x0220, '\'', // ' in 026 Fortran
|
|
0x8420, '.',
|
|
0x8220, ')',
|
|
0x4420, '$',
|
|
0x4220, '*',
|
|
0x2420, ',',
|
|
0x2220, '(',
|
|
};
|
|
|
|
static CPCODE cardcode_026C[] = // 026 commercial
|
|
{
|
|
0x0000, ' ',
|
|
0x8000, '+',
|
|
0x4000, '-',
|
|
0x2000, '0',
|
|
0x1000, '1',
|
|
0x0800, '2',
|
|
0x0400, '3',
|
|
0x0200, '4',
|
|
0x0100, '5',
|
|
0x0080, '6',
|
|
0x0040, '7',
|
|
0x0020, '8',
|
|
0x0010, '9',
|
|
0x9000, 'A',
|
|
0x8800, 'B',
|
|
0x8400, 'C',
|
|
0x8200, 'D',
|
|
0x8100, 'E',
|
|
0x8080, 'F',
|
|
0x8040, 'G',
|
|
0x8020, 'H',
|
|
0x8010, 'I',
|
|
0x5000, 'J',
|
|
0x4800, 'K',
|
|
0x4400, 'L',
|
|
0x4200, 'M',
|
|
0x4100, 'N',
|
|
0x4080, 'O',
|
|
0x4040, 'P',
|
|
0x4020, 'Q',
|
|
0x4010, 'R',
|
|
0x3000, '/',
|
|
0x2800, 'S',
|
|
0x2400, 'T',
|
|
0x2200, 'U',
|
|
0x2100, 'V',
|
|
0x2080, 'W',
|
|
0x2040, 'X',
|
|
0x2020, 'Y',
|
|
0x2010, 'Z',
|
|
0x0420, '=',
|
|
0x0220, '\'', // ' in 026 Fortran
|
|
0x8420, '.',
|
|
0x8220, ')',
|
|
0x4420, '$',
|
|
0x4220, '*',
|
|
0x2420, ',',
|
|
0x2220, '(',
|
|
};
|
|
|
|
extern int cgi;
|
|
extern void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *arg[]);
|
|
|
|
static int16 ascii_to_card[256];
|
|
|
|
static CPCODE *cardcode;
|
|
static int ncardcode;
|
|
static int32 active_cr_code; /* the code most recently specified */
|
|
static FILE *deckfile = NULL;
|
|
static char tempfile[128];
|
|
static int cardnum;
|
|
static int any_punched = 0;
|
|
|
|
#define MAXARG 80 /* saved arguments to attach command */
|
|
static char list_save[MAXARG][10], *list_arg[MAXARG];
|
|
static int list_nargs = 0;
|
|
|
|
static int16 punchstation[80];
|
|
static int16 readstation[80];
|
|
static enum {STATION_EMPTY, STATION_LOADED, STATION_READ, STATION_PUNCHED} punchstate = STATION_EMPTY, readstate = STATION_EMPTY;
|
|
|
|
static t_bool nextdeck (void);
|
|
static void checkdeck (void);
|
|
|
|
/* lookup_codetable - use code flag setting to get code table pointer and length */
|
|
|
|
static t_bool lookup_codetable (int32 match, CPCODE **pcode, int *pncode)
|
|
{
|
|
switch (match) {
|
|
case CODE_029:
|
|
*pcode = cardcode_029;
|
|
*pncode = sizeof(cardcode_029) / sizeof(CPCODE);
|
|
break;
|
|
|
|
case CODE_026F:
|
|
*pcode = cardcode_026F;
|
|
*pncode = sizeof(cardcode_026F) / sizeof(CPCODE);
|
|
break;
|
|
|
|
case CODE_026C:
|
|
*pcode = cardcode_026C;
|
|
*pncode = sizeof(cardcode_026C) / sizeof(CPCODE);
|
|
break;
|
|
|
|
case CODE_BINARY:
|
|
*pcode = NULL;
|
|
*pncode = 0;
|
|
break;
|
|
|
|
default:
|
|
printf("Eek! Undefined code table index");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
t_stat set_active_cr_code (int match)
|
|
{
|
|
CPCODE *code;
|
|
int i, ncode;
|
|
|
|
active_cr_code = match;
|
|
|
|
if (! lookup_codetable(match, &code, &ncode))
|
|
return SCPE_ARG;
|
|
|
|
memset(ascii_to_card, 0, sizeof(ascii_to_card));
|
|
|
|
for (i = 0; i < ncode; i++) // set ascii to card code table
|
|
ascii_to_card[code[i].ascii] = (int16) code[i].hollerith;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cr_set_code (UNIT *uptr, int32 match)
|
|
{
|
|
return set_active_cr_code(match);
|
|
}
|
|
|
|
t_stat cp_set_code (UNIT *uptr, int32 match)
|
|
{
|
|
CPCODE *code;
|
|
int ncode;
|
|
|
|
if (! lookup_codetable(match, &code, &ncode))
|
|
return SCPE_ARG;
|
|
|
|
cardcode = code; // save code table for punch output
|
|
ncardcode = ncode;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat load_cr_boot (int drvno)
|
|
{
|
|
/* this is from the "boot2" cold start card. Columns have been */
|
|
/* expanded already from 12 to 16 bits. */
|
|
|
|
static unsigned short boot2_data[] = {
|
|
0xc80a, 0x18c2, 0xd008, 0xc019, 0x8007, 0xd017, 0xc033, 0x100a,
|
|
0xd031, 0x7015, 0x000c, 0xe800, 0x0020, 0x08f8, 0x4828, 0x7035,
|
|
0x70fa, 0x4814, 0xf026, 0x2000, 0x8800, 0x9000, 0x9800, 0xa000,
|
|
0xb000, 0xb800, 0xb810, 0xb820, 0xb830, 0xb820, 0x3000, 0x08ea,
|
|
0xc0eb, 0x4828, 0x70fb, 0x9027, 0x4830, 0x70f8, 0x8001, 0xd000,
|
|
0xc0f4, 0xd0d9, 0xc01d, 0x1804, 0xe8d6, 0xd0d9, 0xc8e3, 0x18d3,
|
|
0xd017, 0x18c4, 0xd0d8, 0x9016, 0xd815, 0x90db, 0xe8cc, 0xd0ef,
|
|
0xc016, 0x1807, 0x0035, 0x00d0, 0xc008, 0x1803, 0xe8c4, 0xd00f,
|
|
0x080d, 0x08c4, 0x1003, 0x4810, 0x70d9, 0x3000, 0x08df, 0x3000,
|
|
0x7010, 0x00d1, 0x0028, 0x000a, 0x70f3, 0x0000, 0x00d0, 0xa0c0
|
|
};
|
|
int i;
|
|
|
|
if (drvno >= 0) /* if specified, set toggle switches to disk drive no */
|
|
CES = drvno; /* so BOOT DSK1 will work correctly */
|
|
|
|
IAR = 0; /* clear IAR */
|
|
|
|
for (i = 0; i < 80; i++) /* copy memory */
|
|
WriteW(i, boot2_data[i]);
|
|
|
|
#ifdef GUI_SUPPORT
|
|
if (! cgi)
|
|
remark_cmd("Loaded BOOT2 cold start card\n");
|
|
#endif
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cr_boot (int unitno)
|
|
{
|
|
t_stat rval;
|
|
short buf[80];
|
|
int i;
|
|
|
|
if ((rval = reset_all(0)) != SCPE_OK)
|
|
return rval;
|
|
|
|
if (! (cr_unit.flags & UNIT_ATT)) // no deck; load standard boot anyway
|
|
return load_cr_boot(-1);
|
|
|
|
if ((active_cr_code & UNIT_CODE) != CODE_BINARY) {
|
|
printf("Can only boot from card reader when set to BINARY mode");
|
|
return SCPE_IOERR;
|
|
}
|
|
|
|
if (fxread(buf, sizeof(short), 80, cr_unit.fileref) != 80)
|
|
return SCPE_IOERR;
|
|
|
|
IAR = 0; /* Program Load sets IAR = 0 */
|
|
|
|
for (i = 0; i < 80; i++) /* shift 12 bits into 16 */
|
|
WriteW(i, (buf[i] & 0xF800) | ((buf[i] & 0x0400) ? 0x00C0 : 0x0000) | ((buf[i] & 0x03F0) >> 4));
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
char card_to_ascii (int16 hol)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ncardcode; i++)
|
|
if (cardcode[i].hollerith == hol)
|
|
return cardcode[i].ascii;
|
|
|
|
return ' ';
|
|
}
|
|
|
|
// hollerith_to_ascii - provide a generic conversion for simulator debugging
|
|
|
|
char hollerith_to_ascii (int16 hol)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ncardcode; i++)
|
|
if (cardcode_029[i].hollerith == hol)
|
|
return cardcode[i].ascii;
|
|
|
|
return ' ';
|
|
}
|
|
|
|
/* feedcycle - move cards to next station */
|
|
|
|
static void feedcycle (t_bool load, t_bool punching)
|
|
{
|
|
char buf[84], *x;
|
|
int i, nread, nwrite, ch;
|
|
|
|
/* write punched card if punch is attached to a file */
|
|
if (cp_unit.flags & UNIT_ATT) {
|
|
if (any_punched && punchstate != STATION_EMPTY) {
|
|
if ((cp_unit.flags & UNIT_CODE) == CODE_BINARY) {
|
|
fxwrite(punchstation, sizeof(short), 80, cp_unit.fileref);
|
|
}
|
|
else {
|
|
for (i = 80; --i >= 0; ) { /* find last nonblank column */
|
|
if (buf[i] != 0)
|
|
break;
|
|
}
|
|
|
|
/* i is now index of last character to output or -1 if all blank */
|
|
|
|
for (nwrite = 0; nwrite <= i; nwrite++) { /* convert characters */
|
|
buf[nwrite] = card_to_ascii(punchstation[nwrite]);
|
|
}
|
|
|
|
/* nwrite is now number of characters to output */
|
|
|
|
buf[nwrite++] = '\n'; /* append newline */
|
|
fxwrite(buf, sizeof(char), nwrite, cp_unit.fileref);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! load) // all we wanted to do was flush the punch
|
|
return;
|
|
|
|
/* slide cards from reader to punch. If we know we're punching,
|
|
* generate a blank card in any case. Otherwise, it should take two feed
|
|
* cycles to get a read card from the hopper to punch station */
|
|
|
|
if (readstate == STATION_EMPTY) {
|
|
if (punching) {
|
|
memset(punchstation, 0, sizeof(punchstation));
|
|
punchstate = STATION_LOADED;
|
|
}
|
|
else
|
|
punchstate = STATION_EMPTY;
|
|
}
|
|
else {
|
|
memcpy(punchstation, readstation, sizeof(punchstation));
|
|
punchstate = STATION_LOADED;
|
|
}
|
|
|
|
/* load card into read station */
|
|
|
|
again: /* jump here if we've loaded a new deck after emptying the previous one */
|
|
|
|
if (cr_unit.flags & UNIT_ATT) {
|
|
|
|
memset(readstation, 0, sizeof(readstation)); /* blank out the card image */
|
|
|
|
if (cr_unit.fileref == NULL)
|
|
nread = 0;
|
|
|
|
else if ((active_cr_code & UNIT_CODE) == CODE_BINARY) /* binary read is straightforward */
|
|
nread = fxread(readstation, sizeof(short), 80, cr_unit.fileref);
|
|
|
|
else if (fgets(buf, sizeof(buf), cr_unit.fileref) == NULL) /* read up to 80 chars */
|
|
nread = 0; /* hmm, end of file */
|
|
|
|
else { /* check for newline */
|
|
if ((x = strchr(buf, '\r')) == NULL)
|
|
x = strchr(buf, '\n');
|
|
|
|
if (x == NULL) { /* there were no delimiters, burn rest of line */
|
|
while ((ch = getc(cr_unit.fileref)) != EOF) { /* get character */
|
|
if (ch == '\n') /* newline, done */
|
|
break;
|
|
|
|
if (ch == '\r') { /* CR, try to take newline too */
|
|
ch = getc(cr_unit.fileref);
|
|
if (ch != EOF && ch != '\n') /* hmm, put it back */
|
|
ungetc(ch, cr_unit.fileref);
|
|
|
|
break;
|
|
}
|
|
}
|
|
nread = 80; /* take just the first 80 characters */
|
|
}
|
|
else
|
|
nread = x-buf; /* reduce length of string */
|
|
|
|
upcase(buf); /* force uppercase */
|
|
|
|
for (i = 0; i < nread; i++) /* convert ascii to punch code */
|
|
readstation[i] = ascii_to_card[buf[i]];
|
|
|
|
nread = 80; /* even if line was blank consider it present */
|
|
}
|
|
|
|
if (nread <= 0) { /* set hopper flag accordingly */
|
|
if (deckfile != NULL && nextdeck())
|
|
goto again;
|
|
|
|
SETBIT(cr_unit.flags, UNIT_EMPTY);
|
|
readstate = STATION_EMPTY;
|
|
cardnum = -1; /* nix the card counter */
|
|
}
|
|
else {
|
|
CLRBIT(cr_unit.flags, UNIT_EMPTY);
|
|
readstate = STATION_LOADED;
|
|
cardnum++; /* advance card counter */
|
|
}
|
|
}
|
|
else
|
|
readstate = STATION_EMPTY;
|
|
|
|
cr_unit.COLUMN = -1; /* neither device is currently cycling */
|
|
cp_unit.COLUMN = -1;
|
|
}
|
|
|
|
#ifdef NO_USE_FOR_THIS_CURRENTLY
|
|
|
|
/* this routine should probably be hooked up to the GUI somehow */
|
|
|
|
/* NPRO - nonprocess runout, flushes out the reader/punch */
|
|
|
|
static void npro (void)
|
|
{
|
|
if (cr_unit.flags & UNIT_ATT)
|
|
fseek(cr_unit.fileref, 0, SEEK_END); /* push reader to EOF */
|
|
if (deckfile != NULL)
|
|
fseek(deckfile, 0, SEEK_END); /* skip to end of deck list */
|
|
|
|
cardnum = -1; /* nix the card counter */
|
|
|
|
if (punchstate == STATION_PUNCHED)
|
|
feedcycle(FALSE, FALSE); /* flush out card just punched */
|
|
|
|
readstate = punchstate = STATION_EMPTY;
|
|
cr_unit.COLUMN = -1; /* neither device is currently cycling */
|
|
cp_unit.COLUMN = -1;
|
|
SETBIT(cr_unit.flags, UNIT_EMPTY); /* set hopper empty */
|
|
}
|
|
|
|
#endif
|
|
|
|
/* skipbl - skip leading whitespace in a string */
|
|
|
|
static char * skipbl (char *str)
|
|
{
|
|
while (*str && *str <= ' ')
|
|
str++;
|
|
|
|
return str;
|
|
}
|
|
|
|
/* alltrim - remove all leading and trailing whitespace from a string */
|
|
|
|
static char * alltrim (char *str)
|
|
{
|
|
char *s, *lastnb;
|
|
|
|
if ((s = skipbl(str)) != str) /* slide down over leading whitespace */
|
|
strcpy(str, s);
|
|
|
|
for (lastnb = str-1, s = str; *s; s++) /* point to last nonblank characteter in string */
|
|
if (*s > ' ')
|
|
lastnb = s;
|
|
|
|
lastnb[1] = '\0'; /* clip just after it */
|
|
|
|
return str;
|
|
}
|
|
|
|
/* checkdeck - set hopper empty status based on condition of current reader file */
|
|
|
|
static void checkdeck (void)
|
|
{
|
|
t_bool empty;
|
|
|
|
if (cr_unit.fileref == NULL) { /* there is no open file */
|
|
empty = TRUE;
|
|
}
|
|
else {
|
|
fseek(cr_unit.fileref, 0, SEEK_END);
|
|
empty = ftell(cr_unit.fileref) <= 0; /* see if file has anything) */
|
|
fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind deck */
|
|
cardnum = 0; /* reset card counter */
|
|
}
|
|
|
|
if (empty) {
|
|
SETBIT(cr_unit.flags, UNIT_EMPTY);
|
|
if (cr_unit.fileref != NULL) /* real file but it's empty, hmmm, try another */
|
|
nextdeck();
|
|
}
|
|
else
|
|
CLRBIT(cr_unit.flags, UNIT_EMPTY);
|
|
}
|
|
|
|
/* nextdeck - attempt to load a new file from the deck list into the hopper */
|
|
|
|
static t_bool nextdeck (void)
|
|
{
|
|
char buf[200], tmpbuf[200], *fname, *mode, *tn;
|
|
int code;
|
|
long fpos;
|
|
static char white[] = " \t\r\n";
|
|
|
|
cardnum = 0; /* reset card counter */
|
|
|
|
if (deckfile == NULL) /* we can't help */
|
|
return FALSE;
|
|
|
|
code = cr_unit.flags & UNIT_CODE; /* default code */
|
|
|
|
if (cr_unit.fileref != NULL) { /* this pulls the rug out from under scp */
|
|
fclose(cr_unit.fileref); /* since the attach flag is still set. be careful! */
|
|
cr_unit.fileref = NULL;
|
|
|
|
if (cr_unit.flags & UNIT_SCRATCH) {
|
|
unlink(tempfile);
|
|
CLRBIT(cr_unit.flags, UNIT_SCRATCH);
|
|
}
|
|
}
|
|
|
|
for (;;) { /* get a filename */
|
|
if (fgets(buf, sizeof(buf), deckfile) == NULL)
|
|
break; /* oops, no more names */
|
|
|
|
alltrim(buf);
|
|
if (! *buf)
|
|
continue; /* empty line */
|
|
|
|
if (strnicmp(buf, "!BREAK", 6) == 0) { /* stop the simulation */
|
|
break_simulation(STOP_DECK_BREAK);
|
|
continue;
|
|
}
|
|
|
|
if (buf[0] == '!') { /* literal text line, make a temporary file */
|
|
if (*tempfile == '\0') {
|
|
if ((tn = tempnam(".", "1130")) == NULL) {
|
|
printf("Cannot create temporary card file name\n");
|
|
break_simulation(STOP_DECK_BREAK);
|
|
return 0;
|
|
}
|
|
strcpy(tempfile, tn);
|
|
strcat(tempfile, ".tmp");
|
|
}
|
|
|
|
if ((cr_unit.fileref = fopen(tempfile, "wb+")) == NULL) {
|
|
printf("Cannot create temporary file %s\n", tempfile);
|
|
break_simulation(STOP_DECK_BREAK);
|
|
return 0;
|
|
}
|
|
|
|
SETBIT(cr_unit.flags, UNIT_SCRATCH);
|
|
|
|
for (;;) { /* store literal cards into temporary file */
|
|
upcase(buf+1);
|
|
fputs(buf+1, cr_unit.fileref);
|
|
putc('\n', cr_unit.fileref);
|
|
|
|
trace_io("(Literal card %s\n)", buf+1);
|
|
if (! (cr_unit.flags & UNIT_QUIET))
|
|
printf("(Literal card %s)\n", buf+1);
|
|
|
|
fpos = ftell(deckfile);
|
|
if (fgets(buf, sizeof(buf), deckfile) == NULL)
|
|
break; /* oops, end of file */
|
|
if (buf[0] != '!' || strnicmp(buf, "!BREAK", 6) == 0)
|
|
break;
|
|
alltrim(buf);
|
|
}
|
|
fseek(deckfile, fpos, SEEK_SET); /* restore deck file to just before non-literal card */
|
|
|
|
fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind scratch file for reading */
|
|
code = CODE_029; /* assume keycode 029 */
|
|
break;
|
|
}
|
|
|
|
sub_args(buf, tmpbuf, sizeof(buf), list_nargs, list_arg); /* substitute in stuff from the attach command line */
|
|
|
|
if ((fname = strtok(buf, white)) == NULL)
|
|
continue;
|
|
|
|
if (*fname == '#' || *fname == '*' || *fname == ';')
|
|
continue; /* comment */
|
|
|
|
if ((mode = strtok(NULL, white)) != NULL) {
|
|
if (*mode == 'b' || *mode == 'B')
|
|
code = CODE_BINARY;
|
|
else if (*mode == 'a' || *mode == 'A')
|
|
code = CODE_029;
|
|
}
|
|
|
|
if ((cr_unit.fileref = fopen(fname, "rb")) == NULL)
|
|
printf("File '%s' specified in deck file '%s' cannot be opened\n", fname, cr_unit.filename+1);
|
|
else {
|
|
trace_io("(Opened %s deck %s)\n", (code == CODE_BINARY) ? "binary" : "text", fname);
|
|
if (! (cr_unit.flags & UNIT_QUIET))
|
|
printf("(Opened %s deck %s)\n", (code == CODE_BINARY) ? "binary" : "text", fname);
|
|
break;
|
|
}
|
|
}
|
|
|
|
checkdeck();
|
|
|
|
set_active_cr_code(code); /* set specified code */
|
|
|
|
return (cr_unit.flags & UNIT_EMPTY) == 0; /* return TRUE if a deck has been loaded */
|
|
}
|
|
|
|
static t_stat cr_reset (DEVICE *dptr)
|
|
{
|
|
cr_set_code(&cr_unit, active_cr_code & UNIT_CODE); /* reset to specified code table */
|
|
|
|
readstate = STATION_EMPTY;
|
|
|
|
cr_dsw = 0;
|
|
sim_cancel(&cr_unit); /* cancel any pending ops */
|
|
calc_ints();
|
|
|
|
SET_OP(OP_IDLE);
|
|
|
|
SETBIT(cr_unit.flags, UNIT_EMPTY); /* assume hopper empty */
|
|
|
|
if (cr_unit.flags & UNIT_ATT) {
|
|
// if (deckfile != NULL) {
|
|
// fseek(deckfile, 0, SEEK_SET);
|
|
// nextdeck();
|
|
// }
|
|
// else
|
|
// checkdeck();
|
|
|
|
if (cr_unit.fileref != NULL)
|
|
feedcycle(FALSE, FALSE);
|
|
}
|
|
|
|
cr_unit.COLUMN = -1; /* neither device is currently cycling */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat cp_reset (DEVICE *dptr)
|
|
{
|
|
cp_set_code(&cp_unit, cp_unit.flags & UNIT_CODE);
|
|
punchstate = STATION_EMPTY;
|
|
|
|
cp_unit.COLUMN = -1;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat cr_attach (UNIT *uptr, char *cptr)
|
|
{
|
|
t_stat rval;
|
|
t_bool use_decklist;
|
|
char *c, *arg, quote;
|
|
|
|
// no - don't cancel pending read?
|
|
// sim_cancel(uptr); /* cancel pending operations */
|
|
|
|
|
|
CLRBIT(uptr->flags, UNIT_QUIET|UNIT_DEBUG); /* set debug/quiet flags */
|
|
if (sim_switches & SWMASK('D')) SETBIT(uptr->flags, UNIT_DEBUG);
|
|
else if (sim_switches & SWMASK('Q')) SETBIT(uptr->flags, UNIT_QUIET);
|
|
|
|
cr_detach(uptr); /* detach file and possibly deckfile */
|
|
CLRBIT(uptr->flags, UNIT_SCRATCH);
|
|
|
|
c = cptr;
|
|
for (list_nargs = 0; list_nargs < 10; ) { /* extract arguments */
|
|
while (*c && (*c <= ' ')) /* skip blanks */
|
|
c++;
|
|
if (! *c)
|
|
break; /* all done */
|
|
|
|
arg = c; /* save start */
|
|
|
|
while (*c && (*c > ' ')) {
|
|
if (*c == '\'' || *c == '"') { /* quoted string */
|
|
for (quote = *c++; *c;)
|
|
if (*c++ == quote)
|
|
break;
|
|
}
|
|
else c++;
|
|
}
|
|
if (*c)
|
|
*c++ = 0; /* term arg at space */
|
|
|
|
list_arg[list_nargs] = list_save[list_nargs]; /* set pointer to permanent storage location */
|
|
strncpy(list_arg[list_nargs++], arg, MAXARG); /* store copy */
|
|
}
|
|
|
|
if (list_nargs <= 0) /* need at least 1 */
|
|
return SCPE_2FARG;
|
|
|
|
cptr = list_arg[0]; /* filename is first argument */
|
|
|
|
use_decklist = (*cptr == '@'); /* filename starts with @: it's a deck list */
|
|
if (use_decklist)
|
|
cptr++;
|
|
|
|
if (strcmp(cptr, "-") == 0 && ! use_decklist) { /* standard input */
|
|
if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
|
|
uptr->filename = calloc(CBUFSIZE, sizeof(char));
|
|
strcpy(uptr->filename, "(stdin)");
|
|
uptr->fileref = stdin;
|
|
SETBIT(uptr->flags, UNIT_ATT);
|
|
uptr->pos = 0;
|
|
}
|
|
else if ((rval = attach_unit(uptr, cptr)) != SCPE_OK)
|
|
return rval;
|
|
|
|
if (use_decklist) { /* if we skipped the '@', store the actually-specified name */
|
|
strncpy(uptr->filename, cptr-1, CBUFSIZE);
|
|
deckfile = cr_unit.fileref; /* save the deck file stream in our local variable */
|
|
cr_unit.fileref = NULL;
|
|
nextdeck();
|
|
}
|
|
else
|
|
checkdeck();
|
|
|
|
// there is a read pending. Pull the card in to make it go
|
|
if (CURRENT_OP == OP_READING || CURRENT_OP == OP_PUNCHING || CURRENT_OP == OP_FEEDING)
|
|
feedcycle(TRUE, CURRENT_OP == OP_PUNCHING);
|
|
|
|
// no - don't reset the reader
|
|
// cr_reset(&cr_dev); /* reset the whole thing */
|
|
// cp_reset(&cp_dev);
|
|
|
|
cardnum = 0; /* reset card counter */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat cr_detach (UNIT *uptr)
|
|
{
|
|
t_stat rval;
|
|
|
|
if (cr_unit.flags & UNIT_ATT && deckfile != NULL) {
|
|
if (cr_unit.fileref != NULL) /* close the active card deck */
|
|
fclose(cr_unit.fileref);
|
|
|
|
if (cr_unit.flags & UNIT_SCRATCH) {
|
|
unlink(tempfile);
|
|
CLRBIT(cr_unit.flags, UNIT_SCRATCH);
|
|
}
|
|
|
|
cr_unit.fileref = deckfile; /* give scp a file to close */
|
|
}
|
|
|
|
if (uptr->fileref == stdout) {
|
|
CLRBIT(uptr->flags, UNIT_ATT);
|
|
free(uptr->filename);
|
|
uptr->filename = NULL;
|
|
rval = SCPE_OK;
|
|
}
|
|
else
|
|
rval = detach_unit(uptr);
|
|
|
|
return rval;
|
|
}
|
|
|
|
static t_stat cp_detach (UNIT *uptr)
|
|
{
|
|
if (cp_unit.flags & UNIT_ATT)
|
|
if (punchstate == STATION_PUNCHED)
|
|
feedcycle(FALSE, FALSE); /* flush out card just punched */
|
|
|
|
any_punched = 0; /* reset punch detected */
|
|
|
|
return detach_unit(uptr);
|
|
}
|
|
|
|
static void op_done (void)
|
|
{
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("!CR Op Complete, card %d", cardnum);
|
|
|
|
SET_OP(OP_IDLE);
|
|
SETBIT(cr_dsw, CR_DSW_OP_COMPLETE);
|
|
SETBIT(ILSW[4], ILSW_4_1442_CARD);
|
|
calc_ints();
|
|
}
|
|
|
|
static t_stat cr_svc (UNIT *uptr)
|
|
{
|
|
switch (CURRENT_OP) {
|
|
case OP_IDLE:
|
|
break;
|
|
|
|
case OP_FEEDING:
|
|
op_done();
|
|
break;
|
|
|
|
case OP_READING:
|
|
if (readstate == STATION_EMPTY) { /* read active but no cards? hang */
|
|
sim_activate(&cr_unit, cf_wait);
|
|
break;
|
|
}
|
|
|
|
if (++cr_unit.COLUMN < 80) {
|
|
SETBIT(cr_dsw, CR_DSW_READ_RESPONSE);
|
|
SETBIT(ILSW[0], ILSW_0_1442_CARD);
|
|
calc_ints();
|
|
sim_activate(&cr_unit, cr_wait);
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("!CR Read Response %d : %d", cardnum, cr_unit.COLUMN+1);
|
|
}
|
|
else {
|
|
readstate = STATION_READ;
|
|
op_done();
|
|
}
|
|
break;
|
|
|
|
case OP_PUNCHING:
|
|
if (punchstate == STATION_EMPTY) { /* punch active but no cards? hang */
|
|
sim_activate(&cr_unit, cf_wait);
|
|
break;
|
|
}
|
|
|
|
if (cp_unit.flags & UNIT_LASTPUNCH) {
|
|
punchstate = STATION_PUNCHED;
|
|
op_done();
|
|
}
|
|
else {
|
|
SETBIT(cr_dsw, CR_DSW_PUNCH_RESPONSE);
|
|
SETBIT(ILSW[0], ILSW_0_1442_CARD);
|
|
calc_ints();
|
|
sim_activate(&cr_unit, cp_wait);
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("!CR Punch Response");
|
|
}
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
void xio_1142_card (int32 addr, int32 func, int32 modify)
|
|
{
|
|
char msg[80];
|
|
int ch;
|
|
int16 wd;
|
|
t_bool lastcard;
|
|
|
|
switch (func) {
|
|
case XIO_SENSE_DEV:
|
|
if (cp_unit.flags & UNIT_ATT)
|
|
lastcard = FALSE; /* if punch file is open, assume infinite blank cards in reader */
|
|
else if ((cr_unit.flags & UNIT_ATT) == 0)
|
|
lastcard = TRUE; /* if nothing to read, hopper's empty */
|
|
else if (readstate == STATION_LOADED)
|
|
lastcard = FALSE;
|
|
else if (cr_unit.fileref == NULL)
|
|
lastcard = TRUE;
|
|
else if ((ch = getc(cr_unit.fileref)) != EOF) {
|
|
ungetc(ch, cr_unit.fileref); /* put character back; hopper's not empty */
|
|
lastcard = FALSE;
|
|
}
|
|
else if (deckfile != NULL && nextdeck())
|
|
lastcard = FALSE;
|
|
else
|
|
lastcard = TRUE; /* there is nothing left to read for a next card */
|
|
|
|
CLRBIT(cr_dsw, CR_DSW_LAST_CARD|CR_DSW_BUSY|CR_DSW_NOT_READY);
|
|
|
|
if (lastcard)
|
|
SETBIT(cr_dsw, CR_DSW_LAST_CARD);
|
|
|
|
if (CURRENT_OP != OP_IDLE)
|
|
SETBIT(cr_dsw, CR_DSW_BUSY|CR_DSW_NOT_READY);
|
|
else if (readstate == STATION_EMPTY && punchstate == STATION_EMPTY && lastcard)
|
|
SETBIT(cr_dsw, CR_DSW_NOT_READY);
|
|
|
|
if (modify & 0x01) { /* reset interrupts */
|
|
CLRBIT(cr_dsw, CR_DSW_READ_RESPONSE|CR_DSW_PUNCH_RESPONSE);
|
|
CLRBIT(ILSW[0], ILSW_0_1442_CARD);
|
|
}
|
|
|
|
if (modify & 0x02) {
|
|
CLRBIT(cr_dsw, CR_DSW_OP_COMPLETE);
|
|
CLRBIT(ILSW[4], ILSW_4_1442_CARD);
|
|
}
|
|
|
|
ACC = cr_dsw; /* return the DSW */
|
|
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw, (modify & 1) ? " RESET0" : "", (modify & 2) ? " RESET4" : "");
|
|
break;
|
|
|
|
case XIO_READ: /* get card data into word pointed to in IOCC packet */
|
|
if (cr_unit.flags & OP_READING) {
|
|
if (cr_unit.COLUMN < 0) {
|
|
xio_error("1442: Premature read!");
|
|
}
|
|
else if (cr_unit.COLUMN < 80) {
|
|
WriteW(addr, readstation[cr_unit.COLUMN]);
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Read %03x", (readstation[cr_unit.COLUMN] >> 4));
|
|
}
|
|
else if (cr_unit.COLUMN == 80) {
|
|
xio_error("1442: Read past column 80!");
|
|
cr_unit.COLUMN++; // don't report it again
|
|
}
|
|
}
|
|
else {
|
|
xio_error("1442: Read when not in a read cycle!");
|
|
}
|
|
break;
|
|
|
|
case XIO_WRITE:
|
|
if (cr_unit.flags & OP_PUNCHING) {
|
|
if (cp_unit.COLUMN < 0) {
|
|
xio_error("1442: Premature write!");
|
|
}
|
|
else if (cp_unit.flags & UNIT_LASTPUNCH) {
|
|
xio_error("1442: Punch past last-punch column!");
|
|
cp_unit.COLUMN = 81;
|
|
}
|
|
else if (cp_unit.COLUMN < 80) {
|
|
wd = ReadW(addr); /* store one word to punch buffer */
|
|
punchstation[cp_unit.COLUMN] = wd & 0xFFF0;
|
|
if (wd & 0x0008) /* mark this as last column to be punched */
|
|
SETBIT(cp_unit.flags, UNIT_LASTPUNCH);
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Punch %03x%s", (wd >> 4) & 0xFFF, (wd & 8) ? " LAST" : "");
|
|
}
|
|
else if (cp_unit.COLUMN == 80) {
|
|
xio_error("1442: Punch past column 80!");
|
|
cp_unit.COLUMN++; // don't report it again
|
|
}
|
|
}
|
|
else {
|
|
xio_error("1442: Write when not in a punch cycle!");
|
|
}
|
|
break;
|
|
|
|
case XIO_CONTROL:
|
|
switch (modify & 7) {
|
|
case 1: /* start punch */
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Start Punch");
|
|
if (punchstate != STATION_LOADED)
|
|
feedcycle(TRUE, TRUE);
|
|
|
|
SET_OP(OP_PUNCHING);
|
|
cp_unit.COLUMN = -1;
|
|
|
|
CLRBIT(cp_unit.flags, UNIT_LASTPUNCH);
|
|
|
|
any_punched = 1; /* we've started punching, so enable writing to output deck file */
|
|
|
|
sim_cancel(&cr_unit);
|
|
sim_activate(&cr_unit, cp_wait);
|
|
break;
|
|
|
|
case 2: /* feed cycle */
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Feed");
|
|
feedcycle(TRUE, FALSE);
|
|
|
|
SET_OP(OP_FEEDING);
|
|
|
|
sim_cancel(&cr_unit);
|
|
sim_activate(&cr_unit, cf_wait);
|
|
break;
|
|
|
|
case 4: /* start read */
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR Start read");
|
|
if (readstate != STATION_LOADED)
|
|
feedcycle(TRUE, FALSE);
|
|
|
|
SET_OP(OP_READING);
|
|
cr_unit.COLUMN = -1;
|
|
|
|
sim_cancel(&cr_unit);
|
|
sim_activate(&cr_unit, cr_wait);
|
|
break;
|
|
|
|
case 0:
|
|
if (cr_unit.flags & UNIT_DEBUG)
|
|
DEBUG_PRINT("#CR NOP");
|
|
break;
|
|
|
|
default:
|
|
sprintf(msg, "1442: Multiple operations in XIO_CONTROL: %x", modify);
|
|
xio_error(msg);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
sprintf(msg, "Invalid 1442 XIO function %x", func);
|
|
xio_error(msg);
|
|
break;
|
|
}
|
|
}
|