1
0
mirror of https://github.com/simh/simh.git synced 2026-04-24 19:33:40 +00:00

Notes For V2.9-11

1. New Features

1.1 GRI-909

- This is a new simulator for the GRI-909.
- It has been hand-tested; so far, no software has been discovered.

1.2 VAX

- SET CPU CONHALT will cause a HALT instruction to return to the
  boot ROM console rather than to SIMH.  SET CPU SIMHALT restores
  the default behavior.
- BRB/W self at IPL 1F stops the simulator.  This is the default
  behavior of VMS at exit.

1.3 PDP-18b

- ATTACH -A PTR/PTP attaches the reader and punch in ASCII mode.
  In ASCII mode, the reader automatically sets the high order bit
  of incoming alphabetic data, and the punch clears the high order
  bit of outgoing data.

1.4 SCP

- DO -V echoes commands from the file as they are executed.
- Under Windows, execution priority is set BELOW_NORMAL when the
  simulator is running.

2. Release Notes

2.1 Bugs Fixed

- PDP-11 CPU: fixed updating of MMR0 on a memory management error.
- VAX FPA: changed function names to avoid conflict with C math library.
- 1401 MT: read end of record generates group mark without word mark.
- 1401 DP: fixed address generation and checking.
- SCP: an EXIT within a DO command will cause the simulator to exit.

3. In Progress

- Interdata 16b/32b: coded, not tested.
- SDS 940: coded, not tested.
- IBM 1620: coded, not tested.

If you would like to help with the debugging of the untested simulators,
they can be made available by special request.
This commit is contained in:
Bob Supnik
2002-07-14 15:20:00 -07:00
committed by Mark Pizzolato
parent 701f0fe028
commit df6475181c
179 changed files with 36441 additions and 4464 deletions

View File

@@ -1,6 +1,6 @@
/* i1401_cd.c: IBM 1402 card reader/punch
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -35,6 +35,8 @@
Cards are represented as ASCII text streams terminated by newlines.
This allows cards to be created and edited as normal files.
30-May-02 RMS Widened POS to 32b
30-Jan-02 RMS New zero footprint card bootstrap from Van Snyder
29-Nov-01 RMS Added read only unit support
13-Apr-01 RMS Revised for register arrays
*/
@@ -68,7 +70,7 @@ REG cdr_reg[] = {
{ FLDATA (ERR, ind[IN_READ], 0) },
{ FLDATA (S1, s1sel, 0) },
{ FLDATA (S2, s2sel, 0) },
{ DRDATA (POS, cdr_unit.pos, 31), PV_LEFT },
{ DRDATA (POS, cdr_unit.pos, 32), PV_LEFT },
{ DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT },
{ BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) },
{ NULL } };
@@ -93,7 +95,7 @@ REG cdp_reg[] = {
{ FLDATA (ERR, ind[IN_PNCH], 0) },
{ FLDATA (S4, s4sel, 0) },
{ FLDATA (S8, s8sel, 0) },
{ DRDATA (POS, cdp_unit.pos, 31), PV_LEFT },
{ DRDATA (POS, cdp_unit.pos, 32), PV_LEFT },
{ NULL } };
DEVICE cdp_dev = {
@@ -117,10 +119,10 @@ UNIT stack_unit[] = {
{ UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } };
REG stack_reg[] = {
{ DRDATA (POS0, stack_unit[0].pos, 31), PV_LEFT },
{ DRDATA (POS1, stack_unit[1].pos, 31), PV_LEFT },
{ DRDATA (POS28, stack_unit[2].pos, 31), PV_LEFT },
{ DRDATA (POS4, stack_unit[4].pos, 31), PV_LEFT },
{ DRDATA (POS0, stack_unit[0].pos, 32), PV_LEFT },
{ DRDATA (POS1, stack_unit[1].pos, 32), PV_LEFT },
{ DRDATA (POS28, stack_unit[2].pos, 32), PV_LEFT },
{ DRDATA (POS4, stack_unit[4].pos, 32), PV_LEFT },
{ NULL } };
DEVICE stack_dev = {
@@ -261,14 +263,11 @@ return attach_unit (uptr, cptr);
/* Bootstrap routine */
#define BOOT_START 100
#define BOOT_START 0
#define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned char))
static const unsigned char boot_rom[] = {
OP_R + WM, /* R */
OP_SWM + WM, BCD_ZERO, BCD_ZERO, BCD_ONE, /* SWM 001 */
OP_CS + WM, BCD_ZERO, BCD_ZERO, BCD_ONE, /* CS 001 111 */
BCD_ONE, BCD_ONE, BCD_ONE };
OP_R + WM, OP_NOP + WM }; /* R, NOP */
t_stat cdr_boot (int32 unitno)
{

View File

@@ -1,6 +1,6 @@
/* i1401_cpu.c: IBM 1401 CPU simulator
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -23,6 +23,9 @@
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
03-Jun-03 RMS Added 1311 support
22-May-02 RMS Added multiply and divide
30-Dec-01 RMS Added old PC queue
30-Nov-01 RMS Added extended SET/SHOW support
10-Aug-01 RMS Removed register in declarations
07-Dec-00 RMS Fixed bugs found by Charles Owen
@@ -106,22 +109,33 @@
#include "i1401_defs.h"
#define PCQ_SIZE 64 /* must be 2**n */
#define PCQ_MASK (PCQ_SIZE - 1)
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_IS
/* These macros validate addresses. If an addresses error is detected,
they return an error status to the caller. These macros should only
be used in a routine that returns a t_stat value.
*/
#define MM(x) x = x - 1; \
if (x < 0) { \
x = BA + MAXMEMSIZE - 1; \
reason = STOP_WRAP; \
break; }
break; }
#define PP(x) x = x + 1; \
if (ADDR_ERR (x)) { \
x = BA + (x % MAXMEMSIZE); \
reason = STOP_WRAP; \
break; }
#define BRANCH if (ADDR_ERR (AS)) { \
reason = STOP_INVBR; \
break; } \
if (cpu_unit.flags & XSA) BS = IS; \
else BS = BA + 0; \
oldIS = saved_IS; \
PCQ_ENTRY; \
IS = AS;
uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */
@@ -129,7 +143,9 @@ int32 saved_IS = 0; /* saved IS */
int32 AS = 0; /* AS */
int32 BS = 0; /* BS */
int32 as_err = 0, bs_err = 0; /* error flags */
int32 oldIS = 0; /* previous IS */
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
int32 pcq_p = 0; /* PC queue ptr */
REG *pcq_r = NULL; /* PC queue reg ptr */
int32 ind[64] = { 0 }; /* indicators */
int32 ssa = 1; /* sense switch A */
int32 prchk = 0; /* process check stop */
@@ -144,6 +160,9 @@ t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
int32 store_addr_h (int32 addr);
int32 store_addr_t (int32 addr);
int32 store_addr_u (int32 addr);
int32 div_add (int32 ap, int32 bp, int32 aend);
int32 div_sub (int32 ap, int32 bp, int32 aend);
void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp);
t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr);
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod);
@@ -154,6 +173,7 @@ extern t_stat carriage_control (int32 mod);
extern t_stat write_line (int32 ilnt, int32 mod);
extern t_stat inq_io (int32 flag, int32 mod);
extern t_stat mt_io (int32 unit, int32 flag, int32 mod);
extern t_stat dp_io (int32 fnc, int32 flag, int32 mod);
extern t_stat mt_func (int32 unit, int32 mod);
extern t_stat sim_activate (UNIT *uptr, int32 delay);
@@ -188,7 +208,8 @@ REG cpu_reg[] = {
{ FLDATA (OVF, ind[IN_OVF], 0) },
{ FLDATA (IOCHK, iochk, 0) },
{ FLDATA (PRCHK, prchk, 0) },
{ DRDATA (OLDIS, oldIS, 14), REG_RO + PV_LEFT },
{ BRDATA (ISQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC },
{ ORDATA (ISQP, pcq_p, 6), REG_HRO },
{ ORDATA (WRU, sim_int_char, 8) },
{ NULL } };
@@ -205,6 +226,8 @@ MTAB cpu_mod[] = {
{ MR, 0, "no MR", "NOMR", NULL },
{ EPE, EPE, "EPE", "EPE", NULL },
{ EPE, 0, "no EPE", "NOEPE", NULL },
{ MDV, MDV, "MDV", "MDV", NULL },
{ MDV, 0, "no MDV", "NOMDV", NULL },
{ UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size },
{ UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size },
{ UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size },
@@ -217,6 +240,8 @@ DEVICE cpu_dev = {
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL };
/* Tables */
/* Opcode table - length, dispatch, and option flags. This table is also
used by the symbolic input routine to validate instruction lengths */
@@ -321,10 +346,10 @@ const int32 one_table[64] = {
BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007,
12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 };
static const int32 bin_to_bcd[16] = {
const int32 bin_to_bcd[16] = {
10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
static const int32 bcd_to_bin[16] = {
const int32 bcd_to_bin[16] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 };
/* ASCII to BCD conversion */
@@ -358,15 +383,6 @@ char bcd_to_ascii[64] = {
'Q', 'R', '!', '$', '*', ']', ';', '_',
'&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', '?', '.', ')', '[', '<', '"' };
t_stat sim_instr (void)
{
extern int32 sim_interval;
int32 IS, D, ilnt, flags;
int32 op, xa, t, wm, dev, unit;
int32 a, b, i, bsave, carry;
int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal;
t_stat reason, r1, r2;
/* Indicator resets - a 1 marks an indicator that resets when tested */
@@ -392,24 +408,71 @@ static const int32 col_table[64] = {
006, 032, 033, 034, 035, 036, 037, 040,
041, 042, 031, 001, 002, 003, 004, 005 };
/* Summing table for two decimal digits, converted back to BCD */
/* Summing table for two decimal digits, converted back to BCD
Also used for multiplying two decimal digits, converted back to BCD,
with carry forward
*/
static const int32 sum_table[20] = {
10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
static const int32 sum_table[100] = {
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE,
BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR,
BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE };
static const int32 cry_table[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
/* Legal modifier tables */
static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 };
static const int32 ss_mod[] = { 1, 2, 4, 8, -1 };
static const int32 mtf_mod[] = { BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 };
t_stat sim_instr (void)
{
extern int32 sim_interval;
int32 IS, D, ilnt, flags;
int32 op, xa, t, wm, dev, unit;
int32 a, b, i, asave, bsave;
int32 carry, lowprd, sign, ps;
int32 quo, ahigh, qs;
int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal;
t_stat reason, r1, r2;
/* Restore saved state */
IS = saved_IS;
if (as_err) AS = AS | BA; /* flag bad addresses */
if (bs_err) BS = BS | BA;
as_err = bs_err = 0; /* reset error flags */
D = 0;
reason = 0;
/* Main instruction fetch/decode loop */
while (reason == 0) { /* loop until halted */
@@ -551,6 +614,7 @@ case OP_MSZ: /* move suppress zero */
wm = M[AS];
MM (AS); MM (BS); } /* decr pointers */
while ((wm & WM) == 0); /* stop on A WM */
if (reason) break; /* addr err? stop */
do { PP (BS); /* adv B */
t = M[BS]; /* get B, cant be WM */
if ((t == BCD_ZERO) || (t == BCD_COMMA)) {
@@ -681,6 +745,7 @@ case OP_A: case OP_S: /* add/sub */
else M[BS] = (b & WM) + sum_table[t]; /* normal add */
MM (BS); }
while ((b & WM) == 0); /* stop on B WM */
if (reason) break; /* address err? */
if (qsign && (carry == 0)) { /* recompl, no carry? */
M[bsave] = M[bsave] ^ ABIT; /* XOR sign */
for (carry = 1; bsave != BS; --bsave) { /* rescan */
@@ -863,6 +928,7 @@ case OP_MCE: /* edit */
MM (BS); } /* decr B pointer */
while ((b & WM) == 0); /* stop on B WM */
if (reason) break; /* address err? */
if (!qawm || !qzero) { /* rescan? */
if (qdollar) reason = STOP_MCE3; /* error if $ */
break; }
@@ -913,6 +979,141 @@ case OP_MCE: /* edit */
BS--; } /* end for */
break; /* done at last! */
/* Multiply. Comments from the PDP-10 based simulator by Len Fehskens.
Multiply, with variable length operands, is necessarily done the same
way you do it with paper and pencil, except that partial products are
added into the incomplete final product as they are computed, rather
than at the end. The 1401 multiplier format allows the product to
be developed in place, without scratch storage.
The A field contains the multiplicand, length LD. The B field must be
LD + 1 + length of multiplier. Locate the low order multiplier digit,
and at the same time zero out the product field. Then compute the sign
of the result.
*/
case OP_MUL:
asave = AS; bsave = lowprd = BS; /* save AS, BS */
do { a = M[AS]; /* get mpcd char */
M[BS] = BCD_ZERO; /* zero prod */
MM (AS); MM (BS); } /* decr pointers */
while ((a & WM) == 0); /* until A WM */
if (reason) break; /* address err? */
M[BS] = BCD_ZERO; /* zero hi prod */
MM (BS); /* addr low mpyr */
sign = ((M[asave] & ZONE) == BBIT) ^ ((M[BS] & ZONE) == BBIT);
/* Outer loop on multiplier (BS) and product digits (ps),
inner loop on multiplicand digits (AS).
AS and ps cannot produce an address error.
*/
do { ps = bsave; /* ptr to prod */
AS = asave; /* ptr to mpcd */
carry = 0; /* init carry */
b = M[BS]; /* get mpyr char */
do { a = M[AS]; /* get mpcd char */
t = (bcd_to_bin[a & DIGIT] * /* mpyr * mpcd */
bcd_to_bin[b & DIGIT]) + /* + c + partial prod */
carry + bcd_to_bin[M[ps] & DIGIT];
carry = cry_table[t];
M[ps] = (M[ps] & WM) | sum_table[t];
MM (AS); ps--; }
while ((a & WM) == 0); /* until mpcd done */
M[BS] = (M[BS] & WM) | BCD_ZERO; /* zero mpyr just used */
t = bcd_to_bin[M[ps] & DIGIT] + carry; /* add carry to prod */
M[ps] = (M[ps] & WM) | sum_table[t]; /* store */
bsave--; /* adv prod ptr */
MM (BS); } /* adv mpyr ptr */
while ((b & WM) == 0); /* until mpyr done */
M[lowprd] = M[lowprd] | ZONE; /* assume + */
if (sign) M[lowprd] = M[lowprd] & ~ABIT; /* if minus, B only */
break;
/* Divide. Comments from the PDP-10 based simulator by Len Fehskens.
Divide is done, like multiply, pretty muchy the same way you do it with
pencil and paper; successive subtraction of the divisor from a substring
of the dividend while counting up the corresponding quotient digit.
Let LS be the length of the divisor, LD the length of the dividend:
- AS points to the low order divisor digit.
- BS points to the high order dividend digit.
- The low order dividend digit is identified by sign (zone) bits.
- To the left of the dividend is a zero field of length LS + 1.
The low quotient is at low dividend - LS - 1. As BS points to the
high dividend, the low dividend is at BS + LD - 1, so the low
quotient is at BS + LD - LS - 2. The longest possible quotient is
LD - LS + 1, so the first possible non-zero quotient bit will be
found as BS - 2.
This pointer calculation assumes that the divisor has no leading zeroes.
For each leading zero, the start of the quotient will be one position
further left.
Start by locating the high order non-zero digit of the divisor. This
also tests for a divide by zero.
*/
case OP_DIV:
asave = AS; ahigh = -1;
do { a = M[AS]; /* get dvr char */
if ((a & CHAR) != BCD_ZERO) ahigh = AS; /* mark non-zero */
MM (AS); }
while ((a & WM) == 0);
if (reason) break; /* address err? */
if (ahigh < 0) { /* div? by zero */
ind[IN_OVF] = 1; /* set ovf indic */
qs = bsave = BS; /* quo, dividend */
do { b = M[bsave]; /* find end divd */
PP (bsave); } /* marked by zone */
while ((b & ZONE) == 0);
if (reason) break; /* address err? */
if (ADDR_ERR (qs)) { /* address err? */
reason = STOP_WRAP; /* address wrap? */
break; }
div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */
BS = (BS - 2) - (asave - (AS + 1)); /* final bs */
break; }
bsave = BS + (asave - ahigh); /* end subdivd */
qs = (BS - 2) - (ahigh - (AS + 1)); /* quo start */
/* Divide loop - done with subroutines to keep the code clean.
In the loop,
asave = low order divisor
bsave = low order subdividend
qs = current quotient digit
*/
do { quo = 0; /* clear quo digit */
if (ADDR_ERR (qs) || ADDR_ERR (bsave)) {
reason = STOP_WRAP; /* address wrap? */
break; }
b = M[bsave]; /* save low divd */
do { t = div_sub (asave, bsave, ahigh); /* subtract */
quo++; } /* incr quo digit */
while (t == 0); /* until borrow */
div_add (asave, bsave, ahigh); quo--; /* restore */
M[qs] = (M[qs] & WM) | sum_table[quo]; /* store quo digit */
bsave++; qs++; } /* adv divd, quo */
while ((b & ZONE) == 0); /* until B sign */
if (reason) break; /* address err? */
/* At this point,
AS = high order divisor - 1
asave = unit position of divisor
b = unit character of dividend
bsave = unit position of remainder + 1
qs = unit position of quotient + 1
*/
div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */
BS = qs - 2; /* BS = quo 10's pos */
break;
/* Miscellaneous instructions A check B check
SWM: set WM on A char and B char fetch fetch
@@ -970,14 +1171,17 @@ default:
reason = STOP_NXI; /* unimplemented */
break; } /* end switch */
} /* end while */
/* Simulation halted */
as_err = (AS > ADDRMASK);
bs_err = (BS > ADDRMASK);
as_err = ADDR_ERR (AS); /* get addr err flags */
bs_err = ADDR_ERR (BS);
AS = AS & ADDRMASK; /* clean addresses */
BS = BS & ADDRMASK;
pcq_r -> qptr = pcq_p; /* update pc q ptr */
return reason;
} /* end sim_instr */
/* store addr_x - convert address to BCD character in x position
Inputs:
@@ -1006,6 +1210,59 @@ int32 thous;
thous = (addr / 1000) & 014;
return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2));
}
/* div_add - add string for divide */
int32 div_add (int32 ap, int32 bp, int32 aend)
{
int32 a, b, c, r;
c = 0; /* init carry */
do { a = M[ap]; b = M[bp]; /* get operands */
r = bcd_to_bin[b & DIGIT] + /* sum digits + c */
bcd_to_bin[a & DIGIT] + c;
c = (r >= 10); /* set carry out */
M[bp] = sum_table[r]; /* store result */
ap--; bp--; }
while (ap >= aend);
return c;
}
/* div_sub - substract string for divide */
int32 div_sub (int32 ap, int32 bp, int32 aend)
{
int32 a, b, c, r;
c = 0; /* init borrow */
do { a = M[ap]; b = M[bp]; /* get operands */
r = bcd_to_bin[b & DIGIT] - /* a - b - borrow */
bcd_to_bin[a & DIGIT] - c;
c = (r < 0); /* set borrow out */
M[bp] = sum_table[r + 10]; /* store result */
ap--; bp--; }
while (ap >= aend);
b = M[bp] & CHAR; /* borrow position */
if (b != BCD_ZERO) { /* non-zero? */
r = bcd_to_bin[b & DIGIT] - c; /* subtract borrow */
M[bp] = sum_table[r]; /* store result */
return 0; } /* subtract worked */
return c; /* return borrow */
}
/* div_sign - set signs for divide */
void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp)
{
int32 sign = dvrc & ZONE; /* divisor sign */
M[rp] = M[rp] | ZONE; /* assume rem pos */
if (sign == BBIT) M[rp] = M[rp] & ~ABIT; /* if dvr -, rem - */
M[qp] = M[qp] | ZONE; /* assume quo + */
if (((dvdc & ZONE) == BBIT) ^ (sign == BBIT)) /* dvr,dvd diff? */
M[qp] = M[qp] & ~ABIT; /* make quo - */
return;
}
/* iomod - check on I/O modifiers
@@ -1038,6 +1295,7 @@ return STOP_INVM;
t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod)
{
if (dev == IO_INQ) return inq_io (flag, mod); /* inq terminal? */
if (dev == IO_DP) return dp_io (unit, flag, mod); /* disk pack? */
if (dev == IO_MT) return mt_io (unit, flag, mod); /* magtape? */
if (dev == IO_MTB) { /* binary? */
if (flag == MD_WM) return STOP_INVM; /* invalid */
@@ -1055,6 +1313,9 @@ for (i = 0; i < 64; i++) ind[i] = 0;
ind[IN_UNC] = 1;
AS = 0; as_err = 1;
BS = 0; bs_err = 1;
pcq_r = find_reg ("ISQ", NULL, dptr);
if (pcq_r) pcq_r -> qptr = 0;
else return SCPE_IERR;
sim_brk_types = sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}

32
I1401/i1401_dat.h Normal file
View File

@@ -0,0 +1,32 @@
/* ASCII to BCD conversion */
const char ascii_to_bcd[128] = {
000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */
000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000, 000, 000, 000,
000, 000, 000, 000, 000, 000, 000, 000,
000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */
017, 074, 054, 037, 033, 040, 073, 021,
012, 001, 002, 003, 004, 005, 006, 007,
010, 011, 015, 056, 076, 035, 016, 072,
014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */
070, 071, 041, 042, 043, 044, 045, 046,
047, 050, 051, 022, 023, 024, 025, 026,
027, 030, 031, 075, 036, 055, 020, 057,
000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */
070, 071, 041, 042, 043, 044, 045, 046,
047, 050, 051, 022, 023, 024, 025, 026,
027, 030, 031, 000, 000, 000, 000, 000 };
/* BCD to ASCII conversion - also the "full" print chain */
char bcd_to_ascii[64] = {
' ', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '0', '#', '@', ':', '>', '(',
'^', '/', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '\'', ',', '%', '=', '\\', '+',
'-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', '!', '$', '*', ']', ';', '_',
'&', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', '?', '.', ')', '[', '<', '"' };

View File

@@ -1,6 +1,6 @@
/* i1401_defs.h: IBM 1401 simulator definitions
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -23,6 +23,7 @@
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
03-Jun-03 RMS Added 1311 support
14-Apr-99 RMS Converted t_addr to unsigned
This simulator is based on the 1401 simulator written by Len Fehskens
@@ -56,6 +57,15 @@
#define STOP_MCE2 19 /* MCE short B field */
#define STOP_MCE3 20 /* MCE hanging $ */
#define STOP_IOC 21 /* I/O check */
#define STOP_INVDSC 22 /* invalid disk sector */
#define STOP_INVDCN 23 /* invalid disk count */
#define STOP_INVDSK 24 /* invalid disk unit */
#define STOP_INVDFN 25 /* invalid disk func */
#define STOP_INVDLN 26 /* invalid disk reclen */
#define STOP_WRADIS 27 /* write address dis */
#define STOP_WRCHKE 28 /* write check error */
#define STOP_INVDAD 39 /* invalid disk addr */
#define STOP_INVDCY 30 /* invalid direct seek */
/* Memory and devices */
@@ -111,7 +121,7 @@
#define HLE (1 << (UNIT_V_UF + 6)) /* high/low/equal */
#define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* fake flag */
#define ALLOPT (MDV + MR + XSA + EPE + MA + BBE + HLE)
#define STDOPT (MR + XSA + EPE + MA + BBE + HLE)
#define STDOPT (MDV + MR + XSA + EPE + MA + BBE + HLE)
/* Fetch control */
@@ -238,13 +248,14 @@
#define IN_EQU 022 /* equal */
#define IN_LOW 023 /* low */
#define IN_HGH 024 /* high */
#define IN_PAR 025 /* parity check */
#define IN_DPW 025 /* parity/compare check */
#define IN_LNG 026 /* wrong lnt record */
#define IN_UNA 027 /* unequal addr cmp */
#define IN_DSK 030 /* disk error */
#define IN_OVF 031 /* overflow */
#define IN_LPT 032 /* printer error */
#define IN_PRO 034 /* process check */
#define IN_DBY 036 /* disk busy */
#define IN_END 042 /* end indicator */
#define IN_TAP 043 /* tape error */
#define IN_ACC 045 /* access error */
@@ -261,3 +272,5 @@
#define IN_SSF 066 /* sense switch F */
#define IN_SSG 067 /* sense switch G */
#define IN_READ 072 /* reader error */
#define CRETIOE(f,c) return ((f)? (c): SCPE_OK)

View File

@@ -1,14 +1,14 @@
To: Users
From: Bob Supnik
Subj: IBM 1401 Simulator Usage
Date: 1-Dec-01
Date: 15-Jun-2002
COPYRIGHT NOTICE
The following copyright notice applies to both the SIMH source and binary:
Original code published in 1993-2001, written by Robert M Supnik
Copyright (c) 1993-2001, Robert M Supnik
Original code published in 1993-2002, written by Robert M Supnik
Copyright (c) 1993-2002, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -43,8 +43,10 @@ sim/ sim_defs.h
sim/i1401/ i1401_defs.h
i1401_cpu.c
i1401_cd.c
i1401_iq.c
i1401_lp.c
i1401_dp.c
i1401_mt.c
i1401_sys.c
@@ -59,6 +61,7 @@ CPU IBM 1401 CPU with 16K of memory
CDR,CDP IBM 1402 card reader/punch
LPT IBM 1403 line printer
INQ IBM 1407 inquiry terminal
DP IBM 1311 disk pack with five drives
MT IBM 729 7-track magnetic tape controller with six drives
The IBM 1401 simulator implements many unique stop conditions. On almost
@@ -83,8 +86,13 @@ any kind of error the simulator stops:
single character B field in MCE
hanging $ in MCE with EPE enabled
I/O check with I/O stop switch set
invalid disk drive
invalid disk sector address
invalid disk sector count
invalid disk address compare
The LOAD and DUMP commands are not implemented.
The LOAD command is used to load a line printer carriage-control tape.
The DUMP command is not implemented.
2.1 CPU
@@ -102,6 +110,8 @@ when memory size is greater than 4K.
SET CPU NOMR disable move record
SET CPU EPE enable extended print edit special feature
SET CPU NOEPE disable extended print edit
SET CPU MDV enable multiply/divide special feature
SET CPU NOMDV disable multiply/divide
SET CPU 4K set memory size = 4K
SET CPU 8K set memory size = 8K
SET CPU 12K set memory size = 12K
@@ -147,7 +157,8 @@ interrupt system.
OVF 1 overflow indicator
IOCHK 1 I/O check switch
PRCHK 1 process check switch
OLDIS 1 IS prior to last branch
ISQ 1 IS prior to last branch;
most recent IS change first
WRU 8 interrupt character
2.2 1402 Card Reader/Punch (CDR, CDP, STKR)
@@ -177,7 +188,7 @@ The reader/punch registers are:
ERR 1 error indicator
S1 1 stacker 1 select flag
S2 1 stacker 2 select flag
POS 31 position
POS 32 position
TIME 24 delay window for stacker select
BUF[0:79] 8 reader buffer
@@ -185,10 +196,10 @@ The reader/punch registers are:
S4 1 stacker 4 select flag
S8 1 stacker 8 select flag
STKR POS0 31 position, normal reader stack
POS1 31 position, reader stacker 1
POS2 31 position, shared stacker 2/8
POS4 31 position, punch stacker 4
STKR POS0 32 position, normal reader stack
POS1 32 position, reader stacker 1
POS2 32 position, shared stacker 2/8
POS4 32 position, punch stacker 4
Error handling is as follows:
@@ -245,7 +256,7 @@ The line printer registers are:
CCTP 8 carriage control tape pointer
CCTL 8 carriage control tape length (read only)
ERR 1 error indicator
POS 31 position
POS 32 position
CCT[0:131] 32 carriage control tape array
Error handling is as follows:
@@ -278,18 +289,68 @@ character is typed in. The latter cancels the type-in and sets INC.
The inquiry terminal has no errors.
2.5 729 Magnetic Tape (MT)
2.5 1311 Disk Pack (DP)
The disk pack controller supports 5 drives, numbered 0 through 4. Disk
pack options include the ability to enable address writing (formatting).
SET DPn ADDROFF set unit n address enable off
SET DPn ADDRON set unit n address enable on
Units can also be set ONLINE or OFFLINE. Disk data is buffered in
memory; IO errors cannot occur.
Unlike most simulated disks, the 1311 includes explicit representation
for sector addresses. This is to support non-standard formats, such as
the inclusion of the drive number in the sector address. As a result,
1311 sectors are 106 characters long: 6 address characters and 100
data characters. If the 1311 has not been formatted, the addresses
are blanks and are synthesized, if needed, based on the sector number.
The 1311 also supports two modes of operation: move mode and load mode.
In move mode, word marks are ignored on writes and left untouched on reads,
and sectors hold 100 characters. In load mode, word marks are included
on writes and stored on reads, and sectors hold 90 characters. No attempt
is made to deal with sectors written in load mode and read in move mode,
or vice versa; on a real 1401, this causes a fatal parity error.
The disk pack controller implements these registers:
name size comments
ACC 1 access error indicator
PWC 1 parity or write check error indicator
WLR 1 wrong length record error indicator
UNA 1 unequal address compare error indicator
DSK 1 any disk error indicator
BSY 1 disk access busy indicator
LASTF 3 most recent function
TIME 24 seek time
Error handling is as follows:
error processed as
not attached set DSK indicator
if IOCHK set, report error and stop
The 1311 has a primative overlapped seek capability. If TIME is set
non-zero, the 1311 will report itself busy for the specified amount
of time following a seek. This allows programs to utilize the seek
time for processing.
2.6 729 Magnetic Tape (MT)
The magnetic tape controller supports six drives, numbered 1 through 6.
Magnetic tape options include the ability to make units write enabled or
or write locked.
SET MTn LOCKED set unit n write locked
SET MTn ENABLED set unit n write enabled
SET MTn WRITEENABLED set unit n write enabled
Units can also be REMOVEd or ADDed to the configuration. The magnetic
tape simulator supports the BOOT command. BOOT MT reads the first
record off tape, starting at location 1, and then branches to it.
Units can also be set ONLINE or OFFLINE. The magnetic tape simulator
supports the BOOT command. BOOT MT reads the first record off tape,
starting at location 1, and then branches to it.
The magnetic tape controller implements these registers:
@@ -298,7 +359,7 @@ The magnetic tape controller implements these registers:
END 1 end of file indicator
ERR 1 error indicator
PAR 1 parity error indicator
POS1..6 31 position, drives 1..6
POS1..6 32 position, drives 1..6
Error handling is as follows:
@@ -313,7 +374,7 @@ Error handling is as follows:
if IOCHK set, report error and stop
otherwise, set ERR indicator
2.6 Symbolic Display and Input
2.7 Symbolic Display and Input
The IBM 1401 simulator implements symbolic display and input. Display is
controlled by command line switches:
@@ -324,6 +385,8 @@ controlled by command line switches:
(CPU only)
-m display instruction mnemonics
(CPU only)
-d display 50 characters per line, with word
marks denoted by "1" on the line below
In a CPU character display, word marks are denoted by ~.

554
I1401/i1401_dp.c Normal file
View File

@@ -0,0 +1,554 @@
/* i1401_dp.c: IBM 1311 disk simulator
Copyright (c) 2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dp 1311 disk pack
15-Jun-02 Reworked address comparison logic
The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track.
Each sector contains 106 characters of information:
6c sector address
100c sector data
By default, a sector's address field will be '000000', which is illegal.
This is interpreted to mean the implied sector number that would be in
place if the disk pack had been formatted with sequential sector numbers.
The sector data can be 100 characters without word marks, or 90 characters
with word marks. Load mode transfers 90 characters per sector with
word marks, move mode transfers 100 characters per sector without word
marks. No attempt is made to catch incompatible writes (eg, load mode
write followed by move mode read).
*/
#include "i1401_defs.h"
#define DP_NUMDR 5 /* #drives */
#define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */
#define UNIT_WAE (1 << UNIT_V_WAE)
#define UNIT_W_UF 2 /* #save flags */
/* Disk format */
#define DP_ADDR 6 /* address */
#define DP_DATA 100 /* data */
#define DP_NUMCH (DP_ADDR + DP_DATA)
#define DP_NUMSC 20 /* #sectors */
#define DP_NUMSF 10 /* #surfaces */
#define DP_NUMCY 100 /* #cylinders */
#define DP_TOTSC (DP_NUMCY*DP_NUMSF*DP_NUMSC)
#define DP_SIZE (DP_TOTSC*DP_NUMCH)
/* Disk control field */
#define DCF_DRV 0 /* drive select */
#define DCF_SEC 1 /* sector addr */
#define DCF_SEC_LEN 6
#define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */
#define DCF_CNT_LEN 3
#define DCF_LEN (DCF_CNT + DCF_CNT_LEN)
#define DCF_DIR 1 /* direct seek */
#define DCF_DIR_LEN 4
#define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */
#define DCF_DSEEK 0xB
/* Functions */
#define FNC_SEEK 0 /* seek */
#define FNC_CHECK 3 /* check */
#define FNC_READ 1 /* read sectors */
#define FNC_RSCO 5 /* read sec cnt overlay */
#define FNC_RTRK 6 /* read track */
#define FNC_WOFF 10 /* offset for write */
#define FNC_WRITE 11 /* write sectors */
#define FNC_WRSCO 15 /* write sec cnt overlay */
#define FNC_WRTRK 16 /* write track */
#define CYL u3 /* current cylinder */
extern uint8 M[]; /* memory */
extern int32 ind[64];
extern int32 AS, BS, iochk;
extern int32 bcd_to_bin[16];
extern int32 bin_to_bcd[16];
extern UNIT cpu_unit;
int32 dp_lastf = 0; /* prior function */
int32 dp_time = 0; /* seek time */
t_stat dp_reset (DEVICE *dptr);
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk);
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg);
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg);
int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf, int32 flg);
t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf, int32 flg);
t_bool dp_zeroad (uint8 *ap);
t_bool dp_cmp_ad (uint8 *ap, int32 dcf, int32 flg);
int32 dp_trkop (int32 drv, int32 sec);
int32 dp_cvt_bcd (int32 ad, int32 len);
void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg);
int32 dp_get_cnt (int32 dcf);
void dp_fill (UNIT *uptr, uint32 da, int32 cnt);
/* DP data structures
dp_dev DSK device descriptor
dp_unit DSK unit list
dp_reg DSK register list
dp_mod DSK modifier list
*/
UNIT dp_unit[] = {
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) },
{ UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE +
UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } };
REG dp_reg[] = {
{ FLDATA (ACC, ind[IN_ACC], 0) },
{ FLDATA (PWC, ind[IN_DPW], 0) },
{ FLDATA (WLR, ind[IN_LNG], 0) },
{ FLDATA (UNA, ind[IN_UNA], 0) },
{ FLDATA (ERR, ind[IN_DSK], 0) },
{ FLDATA (BSY, ind[IN_DBY], 0) },
{ DRDATA (LASTF, dp_lastf, 3) },
{ DRDATA (TIME, dp_time, 24), PV_LEFT },
{ URDATA (CYL, dp_unit[0].CYL, 10, 8, 0,
DP_NUMDR, PV_LEFT + REG_RO) },
{ URDATA (FLG, dp_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
DP_NUMDR, REG_HRO) },
{ NULL } };
MTAB dp_mod[] = {
{ UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL },
{ UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL },
{ 0 } };
DEVICE dp_dev = {
"DP", dp_unit, dp_reg, dp_mod,
DP_NUMDR, 10, 21, 1, 8, 7,
NULL, NULL, &dp_reset,
NULL, NULL, NULL };
/* Disk IO routine
Inputs:
fnc = function character
flg = load vs move mode
mod = modifier character
Outputs:
status = status
*/
t_stat dp_io (int32 fnc, int32 flg, int32 mod)
{
int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff;
UNIT *uptr;
t_stat r = SCPE_OK;
dcf = BS; /* save DCF addr */
qwc = 0; /* not wcheck */
ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */
ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */
return SCPE_OK; } /* done */
AS = dcf + 6; /* AS for most ops */
BS = dcf + DCF_CNT - 1; /* minimum DCF */
if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */
if (M[dcf] & BBIT) drv = M[dcf + DCF_SEC + 1] & 0xE; /* impl sel? cyl 8-4-2 */
else drv = M[dcf] & DIGIT; /* get drive sel */
if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */
return STOP_INVDSK;
drv = bcd_to_bin[drv] >> 1; /* convert */
uptr = dp_dev.units + drv; /* get unit ptr */
if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */
ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */
CRETIOE (iochk, SCPE_UNATT); }
if ((fnc == FNC_SEEK) && /* seek and */
(M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */
diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */
if (diff < 0) return STOP_INVDSC; /* error? */
diff = diff >> 1; /* diff is *2 */
if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT)
diff = -diff; /* get sign */
uptr -> CYL = uptr -> CYL + diff; /* bound seek */
if (uptr -> CYL < 0) uptr -> CYL = 0;
else if (uptr -> CYL >= DP_NUMCY) { /* too big? */
uptr -> CYL = 0; /* system hangs */
return STOP_INVDCY; }
sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
return SCPE_OK; } /* done! */
sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */
if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */
return STOP_INVDSC;
if (fnc == FNC_SEEK) { /* seek? */
uptr -> CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */
DP_NUMCY;
sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */
return SCPE_OK; } /* done! */
BS = dcf + DCF_LEN; /* full DCF */
if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */
cnt = dp_get_cnt (dcf); /* get count */
if (cnt < 0) return STOP_INVDCN; /* bad count? */
if (fnc >= FNC_WOFF) return STOP_INVDFN; /* invalid func */
if (mod == BCD_W) { /* write? */
if (fnc == FNC_CHECK) { /* write check? */
qwc = 1; /* special read */
fnc = dp_lastf; } /* use last func */
else { dp_lastf = fnc; /* save func */
fnc = fnc + FNC_WOFF; } } /* change to write */
else if (mod == BCD_R) dp_lastf = fnc; /* read? save func */
else return STOP_INVM; /* other? error */
psec = dp_fndsec (uptr, sec, dcf, flg); /* find sector */
switch (fnc) { /* case on function */
case FNC_RSCO: /* read sec cnt ov */
BS = dcf + DCF_CNT; /* set count back */
/* fall thru */
case FNC_READ: /* read */
if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
for (;;) { /* loop */
qzr = (--cnt == 0); /* set zero latch */
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */
break;
cnt = dp_get_cnt (dcf); /* get new count */
if (cnt < 0) return STOP_INVDCN; /* bad count? */
if (qzr) break; /* zero latch? done */
sec++; psec++; /* next sector */
dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
if (r = dp_nexsec (uptr, psec, dcf, flg)) break; /* find next */
}
break; /* done, clean up */
case FNC_RTRK: /* read track */
AS = dcf + 9; /* special AS */
psec = dp_trkop (drv, sec); /* start of track */
for (;;) { /* loop */
qzr = (--cnt == 0); /* set zero latch */
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */
break; /* error? */
if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */
break; /* error? */
cnt = dp_get_cnt (dcf); /* get new count */
if (cnt < 0) return STOP_INVDCN; /* bad count? */
if (qzr) break; /* zero latch? done */
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
break; /* done, clean up */
case FNC_WRSCO: /* write sec cnt ov */
BS = dcf + DCF_CNT; /* set count back */
/* fall through */
case FNC_WRITE: /* read */
if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */
for (;;) { /* loop */
qzr = (--cnt == 0); /* set zero latch */
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */
if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
if (qzr) break; /* zero latch? done */
sec++; psec++; /* next sector */
dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */
if (r = dp_nexsec (uptr, psec, dcf, flg)) break; /* find next */
}
break; /* done, clean up */
case FNC_WRTRK: /* write track */
if ((uptr -> flags & UNIT_WAE) == 0) /* enabled? */
return STOP_WRADIS;
AS = dcf + 9; /* special AS */
psec = dp_trkop (drv, sec); /* start of track */
for (;;) { /* loop */
qzr = (--cnt == 0); /* set zero latch */
dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */
if (r = dp_wradr (uptr, psec, flg)) break; /* write addr */
if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */
if (qzr) break; /* zero latch? done */
psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); }
break; /* done, clean up */
default: /* unknown */
return STOP_INVDFN; }
if (r == SCPE_OK) { /* normal so far? */
BS++; /* advance BS */
if (ADDR_ERR (BS)) return STOP_WRAP; /* address error? */
if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */
ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */
r = STOP_INVDLN; } }
CRETIOE (iochk || !ind[IN_DSK], r); /* return status */
}
/* Read or compare address with memory */
t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
{
int32 i;
uint8 ac;
int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da; /* buf ptr */
t_bool zad = dp_zeroad (ap); /* zero address */
static const int32 dec_tab[DP_ADDR] = /* powers of 10 */
{ 100000, 10000, 1000, 100, 10, 1} ;
for (i = 0; i < DP_ADDR; i++) { /* copy address */
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
return STOP_INVDLN; }
if (zad) { /* addr zero? */
ac = sec / dec_tab[i]; /* get addr digit */
sec = sec % dec_tab[i]; /* get remainder */
ac = bcd_to_bin[ac]; } /* cvt to BCD */
else ac = *ap; /* addr char */
if (qwc) { /* wr chk? zad ok */
if (!zad && (flg? (M[BS] != *ap): /* L? cmp with WM */
((M[BS] & CHAR) != (*ap & CHAR)))) { /* M? cmp w/o WM */
ind[IN_DPW] = ind[IN_DSK] = 1;
return STOP_WRCHKE; } }
else if (flg) M[BS] = *ap & CHAR; /* load mode */
else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* move mode */
ap++; BS++; /* adv ptrs */
if (ADDR_ERR (BS)) return STOP_WRAP; }
return SCPE_OK;
}
/* Read or compare data with memory */
t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc)
{
int32 i, lim;
int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da + DP_ADDR; /* buf ptr */
lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
for (i = 0; i < lim; i++) { /* copy data */
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
return STOP_INVDLN; }
if (qwc) { /* write check? */
if (flg? (M[BS] != *ap): /* load mode cmp */
((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */
ind[IN_DPW] = ind[IN_DSK] = 1; /* error */
return STOP_WRCHKE; } }
else if (flg) M[BS] = *ap & (WM | CHAR); /* load mode */
else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */
ap++; BS++; /* adv ptrs */
if (ADDR_ERR (BS)) return STOP_WRAP; }
return SCPE_OK;
}
/* Write address to disk */
t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg)
{
int32 i;
uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da; /* buf ptr */
for (i = 0; i < DP_ADDR; i++) { /* copy address */
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
return STOP_INVDLN; }
if (flg) *ap = M[BS] & (WM | CHAR); /* L? copy WM */
else *ap = M[BS] & CHAR; /* M? strip WM */
if (da >= uptr -> hwmark) uptr -> hwmark = da + 1;
da++; ap++; BS++; /* adv ptrs */
if (ADDR_ERR (BS)) return STOP_WRAP; }
return SCPE_OK;
}
/* Write data to disk */
t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg)
{
int32 i, lim;
uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da; /* buf ptr */
lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */
for (i = 0; i < lim; i++) { /* copy data */
if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */
dp_fill (uptr, da, DP_DATA - i); /* fill, set err */
ind[IN_LNG] = ind[IN_DSK] = 1; /* error */
return STOP_INVDLN; }
if (flg) *ap = M[BS] & (WM | CHAR); /* load, copy WM */
else *ap = M[BS] & CHAR; /* move, strip WM */
if (da >= uptr -> hwmark) uptr -> hwmark = da + 1;
da++; ap++; BS++; /* adv ptrs */
if (ADDR_ERR (BS)) return STOP_WRAP; }
return SCPE_OK;
}
/* Find sector */
int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf, int32 flg)
{
int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
int32 psec = ((uptr -> CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk;
int32 da = psec * DP_NUMCH; /* char number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da; /* buf ptr */
int32 i;
if (dp_zeroad (ap)) return psec; /* addr zero? ok */
if (dp_cmp_ad (ap, dcf, flg)) return psec; /* addr comp? ok */
psec = psec - (psec % DP_NUMSC); /* sector 0 */
for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */
da = psec * DP_NUMCH; /* char number */
ap = ((uint8 *) uptr -> filebuf) + da; /* word pointer */
if (dp_zeroad (ap)) continue; /* no implicit match */
if (dp_cmp_ad (ap, dcf, flg)) return psec; } /* match? */
ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */
return -1;
}
/* Find next sector - must be sequential, cannot cross cylinder boundary */
t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf, int32 flg)
{
int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */
int32 da = psec * DP_NUMCH; /* word number */
uint8 *ap = ((uint8 *) uptr -> filebuf) + da; /* buf ptr */
if (ctrk) { /* not trk zero? */
if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */
if (dp_cmp_ad (ap, dcf, flg)) return SCPE_OK; }/* addr comp? ok */
ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */
return STOP_INVDAD;
}
/* Test for zero address */
t_bool dp_zeroad (uint8 *ap)
{
int32 i;
for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
if (*ap & CHAR) return FALSE; } /* nonzero? lose */
return TRUE; /* all zeroes */
}
/* Compare disk address to memory sector address - always omit word marks */
t_bool dp_cmp_ad (uint8 *ap, int32 dcf, int32 flg)
{
int32 i;
uint8 c;
for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */
c = M[dcf + DCF_SEC + i]; /* sector addr char */
if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */
return FALSE; }
return TRUE; /* compare ok */
}
/* Track operation setup */
int32 dp_trkop (int32 drv, int32 sec)
{
int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF;
return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) +
(ctrk * DP_NUMSC));
}
/* Convert DCF BCD field to binary */
int32 dp_cvt_bcd (int32 ad, int32 len)
{
uint8 c;
int32 r;
for (r = 0; len > 0; len--) { /* loop thru char */
c = M[ad] & DIGIT; /* get digit */
if ((c == 0) || (c > BCD_ZERO)) return -1; /* invalid? */
r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */
ad++; } /* next digit */
return r;
}
/* Convert binary to DCF BCD field */
void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg)
{
int32 r;
for ( ; len > 0; len--) { /* loop thru char */
r = val % 10; /* get digit */
if (flg) M[ad + len - 1] = bin_to_bcd[r]; /* load mode? */
else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r];
val = val / 10; }
return;
}
/* Get and validate count */
int32 dp_get_cnt (int32 dcf)
{
int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */
if (cnt < 0) return -1; /* bad count? */
if (cnt == 0) return 1000; /* 0 => 1000 */
return cnt;
}
/* Fill sector buffer with blanks */
void dp_fill (UNIT *uptr, uint32 da, int32 cnt)
{
while (cnt-- > 0) { /* fill with blanks */
*(((uint8 *) uptr -> filebuf) + da) = BCD_BLANK;
if (da >= uptr -> hwmark) uptr -> hwmark = da + 1;
da++; }
return;
}
/* Reset routine */
t_stat dp_reset (DEVICE *dptr)
{
int32 i;
for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */
dp_lastf = 0; /* clear state */
ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */
ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0;
sim_cancel (&dp_unit[0]); /* cancel timer */
return SCPE_OK;
}

View File

@@ -1,6 +1,6 @@
/* i1401_iq.c: IBM 1407 inquiry terminal
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@@ -1,6 +1,6 @@
/* i1401_lp.c: IBM 1403 line printer simulator
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -25,6 +25,7 @@
lpt 1403 line printer
30-May-02 RMS Widened POS to 32b
13-Apr-01 RMS Revised for register arrays
*/
@@ -83,7 +84,7 @@ UNIT lpt_unit = {
REG lpt_reg[] = {
{ FLDATA (ERR, ind[IN_LPT], 0) },
{ DRDATA (POS, lpt_unit.pos, 31), PV_LEFT },
{ DRDATA (POS, lpt_unit.pos, 32), PV_LEFT },
{ BRDATA (CCT, cct, 8, 32, CCT_LNT) },
{ DRDATA (LINES, lines, 8), PV_LEFT },
{ DRDATA (CCTP, cctptr, 8), PV_LEFT },

View File

@@ -1,6 +1,6 @@
/* i1401_mt.c: IBM 1401 magnetic tape simulator
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -25,6 +25,13 @@
mt 7-track magtape
12-Jun-02 RMS End-of-record on read sets GM without WM
(found by Van Snyder)
03-Jun-02 RMS Modified for 1311 support
30-May-02 RMS Widened POS to 32b
22-Apr-02 RMS Added protection against bad record lengths
30-Jan-02 RMS New zero footprint tape bootstrap from Van Snyder
20-Jan-02 RMS Changed write enabled modifier
29-Nov-01 RMS Added read only unit support
18-Apr-01 RMS Changed to rewind tape before boot
07-Dec-00 RMS Widened display width from 6 to 8 bits to see record lnt
@@ -54,12 +61,13 @@
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_W_UF 2 /* #save flags */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
#define MT_MAXFR (MAXMEMSIZE * 2) /* max transfer */
extern uint8 M[]; /* memory */
extern int32 ind[64];
extern int32 BS, iochk;
extern UNIT cpu_unit;
unsigned int8 dbuf[MAXMEMSIZE * 2]; /* tape buffer */
uint8 dbuf[MT_MAXFR]; /* tape buffer */
t_stat mt_reset (DEVICE *dptr);
t_stat mt_boot (int32 unitno);
UNIT *get_unit (int32 unit);
@@ -90,13 +98,12 @@ UNIT mt_unit[] = {
REG mt_reg[] = {
{ FLDATA (END, ind[IN_END], 0) },
{ FLDATA (ERR, ind[IN_TAP], 0) },
{ FLDATA (PAR, ind[IN_PAR], 0) },
{ DRDATA (POS1, mt_unit[1].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS2, mt_unit[2].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS3, mt_unit[3].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS4, mt_unit[4].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS5, mt_unit[5].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS6, mt_unit[6].pos, 31), PV_LEFT + REG_RO },
{ DRDATA (POS1, mt_unit[1].pos, 32), PV_LEFT + REG_RO },
{ DRDATA (POS2, mt_unit[2].pos, 32), PV_LEFT + REG_RO },
{ DRDATA (POS3, mt_unit[3].pos, 32), PV_LEFT + REG_RO },
{ DRDATA (POS4, mt_unit[4].pos, 32), PV_LEFT + REG_RO },
{ DRDATA (POS5, mt_unit[5].pos, 32), PV_LEFT + REG_RO },
{ DRDATA (POS6, mt_unit[6].pos, 32), PV_LEFT + REG_RO },
{ GRDATA (FLG1, mt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
REG_HRO },
{ GRDATA (FLG2, mt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1),
@@ -112,7 +119,7 @@ REG mt_reg[] = {
{ NULL } };
MTAB mt_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ 0 } };
@@ -211,13 +218,20 @@ case BCD_R: /* read */
ind[IN_END] = 1; /* set end mark */
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
break; }
tbc = MTRL (tbc); /* ignore error flag */
tbc = MTRL (tbc); /* ignore error flag */
if (tbc > MT_MAXFR) return SCPE_MTRLNT; /* record too long? */
i = fxread (dbuf, sizeof (int8), tbc, uptr -> fileref);
for ( ; i < tbc; i++) dbuf[i] = 0; /* fill with 0's */
err = ferror (uptr -> fileref);
if (err = ferror (uptr -> fileref)) break; /* I/O error? */
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) +
(2 * sizeof (t_mtrlnt));
for (i = 0; (i < tbc) && (M[BS] != (BCD_GRPMRK + WM)); i++) {
for (i = 0; i < tbc; i++) { /* loop thru buf */
if (M[BS] == (BCD_GRPMRK + WM)) { /* GWM in memory? */
BS++; /* incr BS */
if (ADDR_ERR (BS)) { /* test for wrap */
BS = BA | (BS % MAXMEMSIZE);
return STOP_WRAP; }
return SCPE_OK; } /* done */
t = dbuf[i]; /* get char */
if ((flag != MD_BIN) && (t == BCD_ALT)) t = BCD_BLANK;
if (flag == MD_WM) { /* word mk mode? */
@@ -226,10 +240,11 @@ case BCD_R: /* read */
wm_seen = 0; } }
else M[BS] = (M[BS] & WM) | (t & CHAR);
if (!wm_seen) BS++;
if (ADDR_ERR (BS)) {
if (ADDR_ERR (BS)) { /* check next BS */
BS = BA | (BS % MAXMEMSIZE);
return STOP_NXM; } }
M[BS++] = BCD_GRPMRK + WM; /* end of record */
return STOP_WRAP; } }
if (flag == MD_WM) M[BS] = BCD_GRPMRK; /* load? clear WM */
else M[BS] = (M[BS] & WM) | BCD_GRPMRK; /* move? save WM */
break;
case BCD_W:
@@ -241,16 +256,19 @@ case BCD_W:
if (((t & CHAR) == BCD_BLANK) && (flag != MD_BIN))
dbuf[tbc++] = BCD_ALT;
else dbuf[tbc++] = t & CHAR;
if (ADDR_ERR (BS)) {
if (ADDR_ERR (BS)) { /* check next BS */
BS = BA | (BS % MAXMEMSIZE);
return STOP_NXM; } }
return STOP_WRAP; } }
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
fxwrite (dbuf, sizeof (int8), (tbc + 1) & ~1, uptr -> fileref);
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
err = ferror (uptr -> fileref);
if (err = ferror (uptr -> fileref)) break; /* I/O error? */
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) +
(2 * sizeof (t_mtrlnt));
if (ADDR_ERR (BS)) { /* check final BS */
BS = BA | (BS % MAXMEMSIZE);
return STOP_WRAP; }
break;
default:
return STOP_INVM; }
@@ -258,8 +276,8 @@ default:
if (err != 0) { /* I/O error */
perror ("MT I/O error");
clearerr (uptr -> fileref);
if (iochk) return SCPE_IOERR;
ind[IN_TAP] = 1; } /* flag error */
ind[IN_TAP] = 1; /* flag error */
if (iochk) return SCPE_IOERR; }
return SCPE_OK;
}
@@ -275,29 +293,19 @@ else return mt_dev.units + unit;
t_stat mt_reset (DEVICE *dptr)
{
ind[IN_END] = ind[IN_PAR] = ind[IN_TAP] = 0; /* clear indicators */
ind[IN_END] = ind[IN_TAP] = 0; /* clear indicators */
return SCPE_OK;
}
/* Bootstrap routine */
#define BOOT_START 3980
#define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned char))
static const unsigned char boot_rom[] = {
OP_LCA + WM, BCD_PERCNT, BCD_U, BCD_ONE,
BCD_ZERO, BCD_ZERO, BCD_ONE, BCD_R, /* LDA %U1 001 R */
OP_B + WM, BCD_ZERO, BCD_ZERO, BCD_ONE, /* B 001 */
OP_H + WM }; /* HALT */
t_stat mt_boot (int32 unitno)
{
int32 i;
extern int32 saved_IS;
mt_unit[unitno].pos = 0;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
M[BOOT_START + 3] = unitno & 07;
saved_IS = BOOT_START;
mt_unit[unitno].pos = 0; /* force rewind */
BS = 1; /* set BS = 001 */
mt_io (unitno, MD_WM, BCD_R); /* LDA %U1 001 R */
saved_IS = 1;
return SCPE_OK;
}

View File

@@ -1,6 +1,6 @@
/* i1401_sys.c: IBM 1401 simulator interface
Copyright (c) 1993-2001, Robert M. Supnik
Copyright (c) 1993-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -23,6 +23,9 @@
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
03-Jun-02 RMS Added 1311 support
18-May-02 RMS Added -D feature from Van Snyder
26-Jan-02 RMS Fixed H, NOP with no trailing wm (found by Van Snyder)
17-Sep-01 RMS Removed multiconsole support
13-Jul-01 RMS Fixed bug in symbolic output (found by Peter Schorn)
27-May-01 RMS Added multiconsole support
@@ -37,7 +40,7 @@
#define LINE_LNT 80
extern DEVICE cpu_dev, inq_dev, lpt_dev;
extern DEVICE cdr_dev, cdp_dev, stack_dev;
extern DEVICE mt_dev;
extern DEVICE dp_dev, mt_dev;
extern UNIT cpu_unit;
extern REG cpu_reg[];
extern uint8 M[];
@@ -63,9 +66,16 @@ REG *sim_PC = &cpu_reg[0];
int32 sim_emax = LINE_LNT;
DEVICE *sim_devices[] = { &cpu_dev, &inq_dev,
&cdr_dev, &cdp_dev, &stack_dev, &lpt_dev,
&mt_dev, NULL };
DEVICE *sim_devices[] = {
&cpu_dev,
&inq_dev,
&cdr_dev,
&cdp_dev,
&stack_dev,
&lpt_dev,
&mt_dev,
&dp_dev,
NULL };
const char *sim_stop_messages[] = {
"Unknown error",
@@ -89,7 +99,17 @@ const char *sim_stop_messages[] = {
"MCE data field too short",
"MCE control field too short",
"MCE EPE hanging $",
"I/O check" };
"I/O check",
"Invalid disk sector address",
"Invalid disk sector count",
"Invalid disk unit",
"Invalid disk function",
"Invalid disk record length",
"Write track while disabled",
"Write check error",
"Disk address miscompare",
"Direct seek cylinder exceeds maximum"
};
/* Binary loader -- load carriage control tape
@@ -187,6 +207,11 @@ if (sw & SWMASK ('C')) { /* character? */
else fprintf (of, FMTASC (t & 0177));
return SCPE_OK; }
if ((uptr != NULL) && (uptr != &cpu_unit)) return SCPE_ARG; /* CPU? */
if (sw & SWMASK ('D')) { /* dump? */
for (i = 0; i < 50; i++) fprintf (of, "%c", bcd_to_ascii[val[i]&CHAR]) ;
fprintf (of, "\n\t");
for (i = 0; i < 50; i++) fprintf (of, (val[i]&WM)? "1": " ") ;
return -(i - 1); }
if (sw & SWMASK ('S')) { /* string? */
i = 0;
do { t = val[i++];
@@ -199,15 +224,15 @@ if ((val[0] & WM) == 0) return STOP_NOWM; /* WM under op? */
op = val[0]& CHAR; /* isolate op */
flags = op_table[op]; /* get flags */
for (ilnt = 1; ilnt < sim_emax; ilnt++) if (val[ilnt] & WM) break;
if (flags & HNOP) ilnt = 1; /* halt, nop? */
else if ((flags & NOWM) && (ilnt > 7)) ilnt = 7; /* cs, swm? */
if ((flags & (NOWM | HNOP)) && (ilnt > 7)) ilnt = 7; /* cs, swm, h, nop? */
else if ((op == OP_B) && (ilnt > 4) && (val[4] == BCD_BLANK)) ilnt = 4;
else if (ilnt > 8) ilnt = 8; /* cap length */
if ((flags & len_table[ilnt]) == 0) return STOP_INVL; /* legal length? */
else if ((ilnt > 8) && (op != OP_NOP)) ilnt = 8; /* cap length */
if (((flags & len_table[ilnt]) == 0) && /* valid lnt, */
((op != OP_NOP) == 0)) return STOP_INVL; /* nop? */
fprintf (of, "%s",opcode[op]); /* print opcode */
if (ilnt > 2) { /* A address? */
if ((flags & IO) && (val[1] == BCD_PERCNT)) fprintf (of, " %%%c%c",
bcd_to_ascii[val[2]], bcd_to_ascii[val[3]]);
if (((flags & IO) || (op == OP_NOP)) && (val[1] == BCD_PERCNT))
fprintf (of, " %%%c%c", bcd_to_ascii[val[2]], bcd_to_ascii[val[3]]);
else fprint_addr (of, &val[1]); }
if (ilnt > 5) fprint_addr (of, &val[4]); /* B address? */
if ((ilnt == 2) || (ilnt == 5) || (ilnt == 8)) /* d character? */
@@ -291,12 +316,12 @@ for (op = 0; op < 64; op++) /* look it up */
if (opcode[op] && strcmp (gbuf, opcode[op]) == 0) break;
if (op >= 64) return SCPE_ARG; /* successful? */
val[0] = op | WM; /* store opcode */
cptr = get_glyph (cptr, gbuf, ' '); /* get addr or d */
cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */
if (((op_table[op] && IO) && (get_io (gbuf, &val[1]) == SCPE_OK)) ||
(get_addr (gbuf, &val[1]) == SCPE_OK)) {
cptr = get_glyph (cptr, gbuf, ' '); /* get addr or d */
cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */
if (get_addr (gbuf, &val[4]) == SCPE_OK) {
cptr = get_glyph (cptr, gbuf, ' '); /* get d */
cptr = get_glyph (cptr, gbuf, ','); /* get d */
ilnt = 7; } /* a and b addresses */
else ilnt = 4; } /* a address */
else ilnt = 1; /* no addresses */
@@ -306,6 +331,7 @@ if ((gbuf[0] == '\'') || (gbuf[0] == '"')) { /* d character? */
return SCPE_ARG; /* end and legal? */
val[ilnt] = ascii_to_bcd[t]; /* save D char */
ilnt = ilnt + 1; }
else if (gbuf[0] != 0) return SCPE_ARG; /* not done? */
if ((op_table[op] & len_table[ilnt]) == 0) return STOP_INVL;
return -(ilnt - 1);
}