diff --git a/B5500/b5500_cpu.c b/B5500/b5500_cpu.c new file mode 100644 index 00000000..1ebfd3d0 --- /dev/null +++ b/B5500/b5500_cpu.c @@ -0,0 +1,3903 @@ +/* b5500_cpu.c: burroughs 5500 cpu simulator + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "b5500_defs.h" +#include "sim_timer.h" +#include +#include + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) +#define UNIT_MSIZE (7 << UNIT_V_MSIZE) +#define MEMAMOUNT(x) (x << UNIT_V_MSIZE) + +#define TMR_RTC 1 + +#define HIST_MAX 5000 +#define HIST_MIN 64 + +t_uint64 bit_mask[64] = { + 00000000000000001LL, + 00000000000000002LL, + 00000000000000004LL, + 00000000000000010LL, + 00000000000000020LL, + 00000000000000040LL, + 00000000000000100LL, + 00000000000000200LL, + 00000000000000400LL, + 00000000000001000LL, + 00000000000002000LL, + 00000000000004000LL, + 00000000000010000LL, + 00000000000020000LL, + 00000000000040000LL, + 00000000000100000LL, + 00000000000200000LL, + 00000000000400000LL, + 00000000001000000LL, + 00000000002000000LL, + 00000000004000000LL, + 00000000010000000LL, + 00000000020000000LL, + 00000000040000000LL, + 00000000100000000LL, + 00000000200000000LL, + 00000000400000000LL, + 00000001000000000LL, + 00000002000000000LL, + 00000004000000000LL, + 00000010000000000LL, + 00000020000000000LL, + 00000040000000000LL, + 00000100000000000LL, + 00000200000000000LL, + 00000400000000000LL, + 00001000000000000LL, + 00002000000000000LL, + 00004000000000000LL, + 00010000000000000LL, + 00020000000000000LL, + 00040000000000000LL, + 00100000000000000LL, + 00200000000000000LL, + 00400000000000000LL, + 01000000000000000LL, + 02000000000000000LL, + 04000000000000000LL, + 0 +}; + +uint8 bit_number[64] = { + /* 00 01 02 03 04 05 06 07 */ + 47, 46, 45, 44, 43, 42, 42, 42, /* 00 */ + 41, 40, 39, 38, 37, 36, 36, 36, /* 10 */ + 35, 34, 33, 32, 31, 30, 30, 30, /* 20 */ + 29, 28, 27, 26, 25, 24, 24, 24, /* 30 */ + 23, 22, 21, 20, 19, 18, 18, 18, /* 40 */ + 17, 16, 15, 14, 13, 12, 12, 12, /* 50 */ + 11, 10, 9, 8, 7, 6, 6, 6, /* 60 */ + 5, 4, 3, 2, 1, 0, 0, 0, /* 70 */ +}; + +uint8 rank[64] = { + /* 00 01 02 03 04 05 06 07 */ + 53, 54, 55, 56, 57, 58, 59, 60, /* 00 */ + /* 8 9 # @ ? : > ge */ + 61, 62, 19, 20, 63, 21, 22, 23, /* 10 */ + /* + A B C D E F G */ + 24, 25, 26, 27, 28, 29, 30, 31, /* 20 */ + /* H I . [ & ( < ar */ + 32, 33, 1, 2, 6, 3, 4, 5, /* 30 */ + /* ti J K L M N O P */ + 34, 35, 36, 37, 38, 39, 40, 41, /* 40 */ + /* Q R $ * - ) ; le */ + 42, 43, 7, 8, 12, 9, 10, 11, /* 50 */ + /* bl / S T U V W X */ + 0, 13, 45, 46, 47, 48, 49, 50, /* 60 */ + /* Y Z , % ne = ] " */ + 51, 52, 14, 15, 44, 16, 17, 18, /* 70 */ +}; + + +int cpu_index; /* Current running cpu */ +t_uint64 M[MAXMEMSIZE] = { 0 }; /* memory */ +t_uint64 a_reg[2]; /* A register */ +t_uint64 b_reg[2]; /* B register */ +t_uint64 x_reg[2]; /* extension to B */ +t_uint64 y_reg[2]; /* extension to A not original */ +uint8 arof_reg[2]; /* True if A full */ +uint8 brof_reg[2]; /* True if B full */ +uint8 gh_reg[2]; /* G & H source char selectors */ +uint8 kv_reg[2]; /* K & V dest char selectors */ +uint16 ma_reg[2]; /* M memory address regiser */ +uint16 s_reg[2]; /* S Stack pointer */ +uint16 f_reg[2]; /* F MCSV pointer */ +uint16 r_reg[2]; /* R PRT pointer */ +t_uint64 p_reg[2]; /* P insruction buffer */ +uint8 prof_reg[2]; /* True if P valid */ +uint16 t_reg[2]; /* T current instruction */ +uint8 trof_reg[2]; /* True if T valid */ +uint16 c_reg[2]; /* C program counter */ +uint16 l_reg[2]; /* L current syllable pointer */ +uint8 ncsf_reg[2]; /* True if normal state */ +uint8 salf_reg[2]; /* True if subrogram mode */ +uint8 cwmf_reg[2]; /* True if character mode */ +uint8 hltf[2]; /* True if processor halted */ +uint8 msff_reg[2]; /* Mark stack flag Word mode */ +#define TFFF MSFF /* True state in Char mode */ +uint8 varf_reg[2]; /* Variant Flag */ +uint8 q_reg[2]; /* Holds error code */ +uint16 IAR; /* Interrupt register */ +uint32 iostatus; /* Hold status of devices */ +uint8 RTC; /* Real time clock counter */ +uint8 loading; /* Set when loading */ +uint8 HALT; /* Set when halt requested */ +uint8 P1_run; /* Run flag for P1 */ +uint8 P2_run; /* Run flag for P2 */ + + +struct InstHistory +{ + uint16 c; + uint16 op; + uint16 s; + uint16 f; + uint16 r; + uint16 ma; + t_uint64 a_reg; + t_uint64 b_reg; + t_uint64 x_reg; + uint8 flags; + uint8 gh; + uint8 kv; + uint16 l; + uint8 q; + uint8 cpu; + uint16 iar; +}; + +struct InstHistory *hst = NULL; +int32 hst_p = 0; +int32 hst_lnt = 0; + +#define F_AROF 00001 +#define F_BROF 00002 +#define F_CWMF 00004 +#define F_NCSF 00010 +#define F_SALF 00020 +#define F_MSFF 00040 +#define F_VARF 00100 +#define HIST_PC 0100000 + +t_stat cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_dep(t_value val, t_addr addr, UNIT * uptr, + int32 sw); +t_stat cpu_reset(DEVICE * dptr); +t_stat cpu_set_size(UNIT * uptr, int32 val, char *cptr, + void *desc); +t_stat cpu_show_size(FILE * st, UNIT * uptr, int32 val, + void *desc); +t_stat cpu_show_hist(FILE * st, UNIT * uptr, int32 val, + void *desc); +t_stat cpu_set_hist(UNIT * uptr, int32 val, char *cptr, + void *desc); +t_stat cpu_help(FILE *, DEVICE *, UNIT *, int32, const char *); +/* Interval timer */ +t_stat rtc_srv(UNIT * uptr); + +int32 rtc_tps = 60 ; + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit[] = + {{ UDATA(rtc_srv, MEMAMOUNT(7), MAXMEMSIZE ), 16667 }, + { UDATA(0, UNIT_DISABLE|UNIT_DIS, 0 ), 0 }}; + +REG cpu_reg[] = { + {BRDATAD(C, c_reg, 8,15,2, "Instruction pointer"), REG_FIT}, + {BRDATAD(L, l_reg, 8,2,2, "Sylable pointer")}, + {BRDATA(A, a_reg, 8,48,2), REG_FIT}, + {BRDATA(B, b_reg, 8,48,2), REG_FIT}, + {BRDATA(X, x_reg, 8,39,2), REG_FIT}, + {BRDATA(GH, gh_reg, 8,6,2)}, + {BRDATA(KV, kv_reg, 8,6,2)}, + {BRDATAD(MA, ma_reg, 8,15,2, "Memory address")}, + {BRDATAD(S, s_reg, 8,15,2, "Stack pointer")}, + {BRDATAD(F, f_reg, 8,15,2, "Frame pointer")}, + {BRDATAD(R, r_reg, 8,15,2, "PRT pointer/Tally")}, + {BRDATAD(P, p_reg, 8,48,2, "Last code word cache")}, + {BRDATAD(T, t_reg, 8,12,2, "Current instruction")}, + {BRDATAD(Q, q_reg, 8,9,2, "Error condition")}, + {BRDATA(AROF, arof_reg, 2,1,2)}, + {BRDATA(BROF, brof_reg, 2,1,2)}, + {BRDATA(PROF, prof_reg, 2,1,2)}, + {BRDATA(TROF, trof_reg, 2,1,2)}, + {BRDATA(NCSF, ncsf_reg, 2,1,2)}, + {BRDATA(SALF, salf_reg, 2,1,2)}, + {BRDATA(CWMF, cwmf_reg, 2,1,2)}, + {BRDATA(MSFF, msff_reg, 2,1,2)}, + {BRDATA(VARF, varf_reg, 2,1,2)}, + {BRDATA(HLTF, hltf, 2,1,2)}, + {ORDATAD(IAR, IAR, 15, "Interrupt pending")}, + {ORDATAD(TUS, iostatus, 32, "Perpherial ready status")}, + {FLDATA(HALT, HALT, 0)}, + {NULL} +}; + +MTAB cpu_mod[] = { + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(0), NULL, "4K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(1), NULL, "8K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(2), NULL, "12K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(3), NULL, "16K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(4), NULL, "20K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(5), NULL, "24K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(6), NULL, "28K", &cpu_set_size}, + {UNIT_MSIZE|MTAB_VDV, MEMAMOUNT(7), NULL, "32K", &cpu_set_size}, + {MTAB_VDV, 0, "MEMORY", NULL, NULL, &cpu_show_size}, + {MTAB_XTD | MTAB_VDV | MTAB_NMO | MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist}, + {0} +}; + +DEVICE cpu_dev = { + "CPU", cpu_unit, cpu_reg, cpu_mod, + 2, 8, 15, 1, 8, 48, + &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cpu_help +}; + + + +/* Define registers */ +#define A a_reg[cpu_index] +#define B b_reg[cpu_index] +#define C c_reg[cpu_index] +#define L l_reg[cpu_index] +#define X x_reg[cpu_index] +#define Y y_reg[cpu_index] +#define Q q_reg[cpu_index] +#define GH gh_reg[cpu_index] +#define KV kv_reg[cpu_index] +#define Ma ma_reg[cpu_index] +#define S s_reg[cpu_index] +#define F f_reg[cpu_index] +#define R r_reg[cpu_index] +#define P p_reg[cpu_index] +#define T t_reg[cpu_index] +#define AROF arof_reg[cpu_index] +#define BROF brof_reg[cpu_index] +#define PROF prof_reg[cpu_index] +#define TROF trof_reg[cpu_index] +#define NCSF ncsf_reg[cpu_index] +#define SALF salf_reg[cpu_index] +#define CWMF cwmf_reg[cpu_index] +#define MSFF msff_reg[cpu_index] +#define VARF varf_reg[cpu_index] +#define HLTF hltf[cpu_index] + +/* Definitions to help extract fields */ +#define FF(x) ((uint16)(((x) >> FFIELD_V) & CORE)) +#define CF(x) ((uint16)((x) & CORE)) +#define LF(x) ((uint8)(((x) & RL) >> RL_V) & 03) +#define RF(x) (uint16)(((x) & RFIELD) >> RFIELD_V) + +#define toF(x) (((t_uint64)((x) & CORE)) << FFIELD_V) +#define toC(x) ((t_uint64)((x) & CORE)) +#define toL(x) (((t_uint64)(x)) << RL_V) +#define toR(x) (((t_uint64)(x)) << RFIELD_V) + +#define replF(y, x) ((y & ~FFIELD) | toF(x)) +#define replC(y, x) ((y & ~CORE) | toC(x)) + +#define next_addr(x) (x = (x + 1) & CORE) +#define prev_addr(x) (x = (x - 1) & CORE) + +/* Definitions to handle building of control words */ +#define MSCW (FLAG | DFLAG | toR(R) | toF(F) | \ + ((MSFF)?SMSFF:0) | ((SALF)?SSALF:0)) +#define ICW (FLAG | DFLAG | toR(R) | ((VARF)?SVARF:0) | \ + ((MSFF)?SMSFF:0) | ((SALF)?SSALF:0)) | toC(Ma) +#define Pointer(x) ((t_uint64)((((x) & 070) >> 3) | ((x & 07) << 8))) +#define RCW(x) (FLAG | DFLAG | toF(F) | toC(C) | toL(L) | \ + (Pointer(GH) << RGH_V) | (Pointer(KV) << RKV_V)) | \ + ((x)?PRESENT:0) +#define LCW(f, x) toF(f) | toC(C) | toL(L) | \ + (((t_uint64)(x) << REPFLD_V) & REPFLD) +#define VARIANT(x) ((x) >> 6) + + +/* E + 2 A = M[S] + 3 B = M[S] + 4 A = M[Ma] + 5 B = M[Ma] + 6 Ma = M[Ma]<18:32> + 10 M[S] = A 1010 + 11 M[S] = B 1011 + 12 M[Ma] = A 1100 + 13 M[Ma] = B 1101 + + 1 B/A + 2 S + 4 Ma + 8 Write/Read + 16 Fetch +*/ + +int memory_cycle(uint8 E) { + uint16 addr = 0; + + sim_interval--; + if (E & 2) + addr = S; + if (E & 4) + addr = Ma; + if (E & 020) + addr = C; + if (addr > MEMSIZE) { + Q |= INVALID_ADDR; + return 1; + } + if (NCSF && addr < 01000) { + Q |= INVALID_ADDR; + return 1; + } + if (E & 020) { + P = M[addr]; + PROF = 1; + return 0; + } + if (E & 010) { + if (E & 1) + M[addr] = B; + else + M[addr] = A; + } else { + if (E == 6) { + B = M[addr]; + Ma = FF(B); + } else if (E & 1) { + B = M[addr]; + BROF = 1; + } else { + A = M[addr]; + AROF = 1; + } + } + return 0; +} + + +/* Set registers based on MSCW */ +void set_via_MSCW(t_uint64 word) { + F = FF(word); + R = RF(word); + MSFF = (word & SMSFF) != 0; + SALF = (word & SSALF) != 0; +} + +/* Set registers based on RCW. + if no_set_lc is non-zero don't set LC from RCW. + if no_bits is non-zero don't set GH and KV, + return BROF flag */ +int set_via_RCW(t_uint64 word, int no_set_lc, int no_bits) { + if (!no_set_lc) { + L = LF(word); + C = CF(word); + PROF = 0; + } + F = FF(word); + if (!no_bits) { + uint16 t; + t = (uint16)((word & RGH) >> RGH_V); + GH = ((t << 3) & 070) | ((t >> 8) & 07); + t = (uint16)((word & RKV) >> RKV_V); + KV = ((t << 3) & 070) | ((t >> 8) & 07); + } + return (word & PRESENT) != 0; +} + +/* Set the stack pointer from INCW */ +void set_via_INCW(t_uint64 word) { + S = CF(word); + CWMF = (word & SCWMF) != 0; +} + +/* Set registers from ICW */ +void set_via_ICW(t_uint64 word) { + Ma = CF(word); + MSFF = (word & SMSFF) != 0; + SALF = (word & SSALF) != 0; + VARF = (word & SVARF) != 0; + R = RF(word); +} + +/* Make sure that B is empty */ +void B_empty() { + if (BROF) { + next_addr(S); + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(013); /* Save B */ + BROF = 0; + } +} + +/* Make sure A is empty, push to B if not */ +void A_empty() { + if (AROF) { + B_empty(); + B = A; + AROF = 0; + BROF = 1; + } +} + +/* Make sure both A and B are empty */ +void AB_empty() { + B_empty(); + if (AROF) { + next_addr(S); + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(012); /* Save A */ + AROF = 0; + } +} + +/* Make sure that A is valid, copy from B or memory */ +void A_valid() { + if (!AROF) { + if (BROF) { /* Transfer B to A */ + A = B; + AROF = 1; + BROF = 0; + } else { + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(2); /* Read A */ + prev_addr(S); + } + } +} + +/* Make sure both A and B are valid */ +void AB_valid() { + A_valid(); + if (!BROF) { + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(3); /* Read B */ + prev_addr(S); + } +} + +/* Make sure A is empty and B is valid */ +void B_valid() { + A_empty(); + if (!BROF) { + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(3); /* Read B */ + prev_addr(S); + } +} + +/* Make sure B is valid, don't care about A */ +void B_valid_and_A() { + if (!BROF) { + if (NCSF && (S & 077700) == R) { + Q |= STK_OVERFL; /* Stack fault */ + return; + } + memory_cycle(3); /* Read B */ + prev_addr(S); + } +} + +/* Saves the top word on the stack into MA */ +void save_tos() { + if (AROF) { + memory_cycle(014); /* Store A in Ma */ + AROF = 0; + } else if (BROF) { + memory_cycle(015); /* Store B in Ma */ + BROF = 0; + } else { /* Fetch B then Store */ + A_valid(); /* Use A register since it is quicker */ + memory_cycle(014); + AROF = 0; + } +} + +/* Enter a subroutine, flag true for descriptor, false for opdc */ +void enterSubr(int flag) { + /* Program descriptor */ + if ((A & ARGF) != 0 && MSFF == 0) { + return; + } + if ((A & MODEF) != 0 && (A & ARGF) == 0) { + return; + } + B_empty(); + /* Check if accidental entry */ + if ((A & ARGF) == 0) { + B = MSCW; + BROF = 1; + B_empty(); + F = S; + } + B = RCW(flag); + BROF = 1; + B_empty(); + C = CF(A); + L = 0; + if ((A & ARGF) == 0) { + F = FF(A); + } else { + F = S; + } + AROF = 0; + BROF = 0; + SALF = 1; + MSFF = 0; + PROF = 0; + if (A & MODEF) { + CWMF = 1; + R = 0; + X = toF(S); + S = 0; + } +} + +/* Make B register into an integer, return 1 if failed */ +int mkint() { + int exp_b; + int last_digit; + int f = 0; + + /* Extract exponent */ + exp_b = (B & EXPO) >> EXPO_V; + if (exp_b == 0) + return 0; + if (B & ESIGN) + exp_b = -exp_b; + if (B & MSIGN) + f = 1; + B &= MANT; + /* Adjust if exponent less then zero */ + last_digit = 0; + if (exp_b < 0) { + while (exp_b < 0 && B != 0) { + last_digit = B & 7; + B >>= 3; + exp_b++; + } + if (exp_b != 0) { + B = 0; + return 0; + } + if (f ? (last_digit > 4) : (last_digit >= 4)) + B++; + } else { + /* Now handle when exponent plus */ + while(exp_b > 0) { + if ((B & NORM) != 0) + return 1; + B <<= 3; + exp_b--; + } + if (exp_b != 0) { + B = 0; + return 1; + } + } + if (f && B != 0) + B |= MSIGN; + return 0; +} + +/* Compute an index word return true if failed. */ +int indexWord() { + if (A & WCOUNT) { + B_valid_and_A(); + if (mkint()) { + if (NCSF) + Q |= INT_OVER; + return 1; + } + if (B & MSIGN && (B & MANT) != 0) { + if (NCSF) + Q |= INDEX_ERROR; + return 1; + } + if ((B & 01777) >= ((A & WCOUNT) >> WCOUNT_V)) { + if (NCSF) + Q |= INDEX_ERROR; + return 1; + } + Ma = (A + (B & 01777)) & CORE; + A &= ~(WCOUNT|CORE); + A |= Ma; + BROF = 0; + } else { + Ma = CF(A); + } + return 0; +} + + +/* Character mode helper routines */ + +/* Adjust source bit pointers to point to char */ +void adjust_source() { + if (GH & 07) { + GH &= 070; + GH += 010; + if (GH > 077) { + AROF = 0; + GH = 0; + next_addr(Ma); + } + } +} + +/* Adjust destination bit pointers to point to char */ +void adjust_dest() { + if (KV & 07) { + KV &= 070; + KV += 010; + if (KV > 075) { + if (BROF) + memory_cycle(013); + BROF = 0; + KV = 0; + next_addr(S); + } + } +} + +/* Advance to next destination bit/char */ +void next_dest(int bit) { + if (bit) + KV += 1; + else + KV |= 7; + if ((KV & 07) > 5) { + KV &= 070; + KV += 010; + } + if (KV > 075) { + if (BROF) + memory_cycle(013); + BROF = 0; + KV = 0; + next_addr(S); + } +} + +/* Advance to previous destination bit/char */ +void prev_dest(int bit) { + if (bit) { + if ((KV & 07) == 0) { + if (KV == 0) { + if (BROF) + memory_cycle(013); + BROF = 0; + prev_addr(S); + KV = 076; + } else { + KV = ((KV - 010) & 070) | 06; + } + } + KV -= 1; + } else { + KV &= 070; + if (KV == 0) { + if (BROF) + memory_cycle(013); + BROF = 0; + prev_addr(S); + KV = 070; + } else + KV -= 010; + } +} + +/* Make sure destination have valid data */ +void fill_dest() { + if (BROF == 0) { + memory_cycle(3); + BROF = 1; + } +} + +/* Advance source to next bit/char */ +void next_src(int bit) { + if (bit) + GH += 1; + else + GH |= 7; + if ((GH & 07) > 5) { + GH &= 070; + GH += 010; + } + if (GH > 075) { + AROF = 0; + GH = 0; + next_addr(Ma); + } +} + +/* Advance source to previous bit/char */ +void prev_src(int bit) { + if (bit) { + if ((GH & 07) == 0) { + if (GH == 0) { + AROF = 0; + prev_addr(Ma); + GH = 076; + } else { + GH = ((GH - 010) & 070) | 06; + } + } + GH -= 1; + } else { + GH &= 070; + if (GH == 0) { + AROF = 0; + prev_addr(Ma); + GH = 070; + } else + GH -= 010; + } +} + +/* Make sure source has valid data */ +void fill_src() { + if (AROF == 0) { + memory_cycle(4); + AROF = 1; + } +} + + +/* Helper routines for managing processor */ + +/* Fetch next program sylable */ +void next_prog() { + if (!PROF) + memory_cycle(020); + T = (P >> ((3 - L) * 12)) & 07777; + if ( L++ == 3) { + C++; + L = 0; + PROF = 0; + } + TROF = 1; +} + +/* Initiate a processor, A must contain the ICW */ +void initiate() { + int brflg, arflg, temp; + + set_via_INCW(A); /* Set up Stack */ + AROF = 0; + memory_cycle(3); /* Fetch IRCW from stack */ + prev_addr(S); + brflg = set_via_RCW(B, 0, 0); + memory_cycle(3); /* Fetch ICW from stack */ + prev_addr(S); + set_via_ICW(B); + BROF = 0; /* Note memory_cycle set this */ + if (CWMF) { + memory_cycle(3); /* Fetch LCW from stack */ + prev_addr(S); + arflg = (B & PRESENT) != 0; + X = B & MANT; + if (brflg) { + memory_cycle(3); /* Load B via S */ + prev_addr(S); + } + if (arflg) { + memory_cycle(2); /* Load A via S */ + prev_addr(S); + } + AROF = arflg; + BROF = brflg; + temp = S; + S = FF(X); + X = replF(X, temp); + } + NCSF = 1; + PROF = 0; + TROF = 0; +} + +/* Save processor state in case of error or halt */ +void storeInterrupt(int forced, int test) { + int f; + t_uint64 temp; + + if (forced || test) + NCSF = 0; + f = BROF; + if (CWMF) { + int i = AROF; + temp = S; + S = FF(X); + X = replF(X, temp); + if (AROF || test) { /* Push A First */ + next_addr(S); + memory_cycle(10); + } + if (BROF || test) { /* Push B second */ + next_addr(S); + memory_cycle(11); + } + /* Make ILCW */ + B = X | ((i)? PRESENT : 0) | FLAG | DFLAG; + next_addr(S); /* Save B */ + memory_cycle(11); + } else { + if (BROF || test) { /* Push B First */ + next_addr(S); + memory_cycle(11); + } + if (AROF || test) { /* Push A Second */ + next_addr(S); + memory_cycle(10); + } + } + AROF = 0; + B = ICW; /* Set ICW into B */ + next_addr(S); /* Save B */ + memory_cycle(11); + B = RCW(f); /* Save IRCW */ + next_addr(S); /* Save B */ + memory_cycle(11); + if (CWMF) { + /* Get the correct value of R */ + Ma = F; + memory_cycle(6); /* Load B via Ma, Indirect */ + memory_cycle(5); /* Load B via Ma */ + R = RF(B); + B = FLAG|DFLAG|SCWMF|toC(S); + } else { + B = FLAG|DFLAG|toC(S); + } + B |= ((t_uint64)Q) << 35; + Ma = R | 010; + memory_cycle(015); /* Store B in Ma */ + R = 0; + BROF = 0; + MSFF = 0; + SALF = 0; + F = S; + if (forced || test) + CWMF = 0; + PROF = 0; + if (test) { + Ma = 0; + memory_cycle(5); /* Load location 0 to B. */ + BROF = 0; + C = CF(B); + L = 0; + KV = 0; + GH = 0; + } else if (forced) { + if (cpu_index) { + P2_run = 0; /* Clear halt flag */ + hltf[1] = 0; + cpu_index = 0; + } else { + T = WMOP_ITI; + TROF = 1; + } + } +} + + +/* Math helper routines. */ + +/* Compare A and B, + return 1 if B = A. + return 2 if B > A + return 4 if B < A +*/ +uint8 compare() { + int sign_a, sign_b; + int exp_a, exp_b; + t_uint64 ma, mb; + + sign_a = (A & MSIGN) != 0; + sign_b = (B & MSIGN) != 0; + + /* next grab exponents and mantissa */ + ma = A & MANT; + mb = B & MANT; + if (ma == 0) { + if (mb == 0) + return 1; + return (sign_b ? 2 : 4); + } else { + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + } + if (mb == 0) { + return (sign_a ? 4 : 2); + } else { + exp_b = (B & EXPO) >> EXPO_V; + if (B & ESIGN) + exp_b = -exp_b; + } + + /* If signs are different return differnce */ + if (sign_a != sign_b) + return (sign_b ? 2 : 4); + + /* Normalize both */ + while((ma & NORM) == 0 && exp_a != exp_b) { + ma <<= 3; + exp_a--; + } + + while((mb & NORM) == 0 && exp_a != exp_b) { + mb <<= 3; + exp_b--; + } + + /* Check exponents first */ + if (exp_a != exp_b) { + if (exp_b > exp_a) { + return (sign_b ? 2 : 4); + } else { + return (sign_b ? 4 : 2); + } + } + + /* Exponents same, check mantissa */ + if (ma != mb) { + if (mb > ma) { + return (sign_b ? 2 : 4); + } else if (mb != ma) { + return (sign_b ? 4 : 2); + } + } + + /* Ok, must be identical */ + return 1; +} + +/* Handle addition instruction. + A & B valid. */ +void add(int opcode) { + int exp_a, exp_b; + int sa, sb; + int rnd; + + AB_valid(); + if (opcode == WMOP_SUB) /* Subtract */ + A ^= MSIGN; + AROF = 0; + X = 0; + /* Check if Either argument already zero */ + if ((A & MANT) == 0) { + if ((B & MANT) == 0) + B = 0; + return; + } + if ((B & MANT) == 0) { + B = A; + return; + } + + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + exp_b = (B & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + if (B & ESIGN) + exp_b = -exp_b; + /* Larger exponent to A */ + if (exp_b > exp_a) { + t_uint64 temp; + temp = A; + A = B; + B = temp; + sa = exp_a; + exp_a = exp_b; + exp_b = sa; + } + /* Extract signs, clear upper bits */ + sa = (A & MSIGN) != 0; + A &= MANT; + sb = (B & MSIGN) != 0; + B &= MANT; + /* While the exponents are not equal, adjust */ + while(exp_a != exp_b && (A & NORM) == 0) { + A <<= 3; + exp_a--; + } + while(exp_a != exp_b && B != 0) { + X |= (B & 07) << EXPO_V; + X >>= 3; + B >>= 3; + exp_b++; + } + if (exp_a != exp_b) { + exp_b = exp_a; + B = 0; + X = 0; + } + if (sa) { /* A is negative. */ + A ^= FWORD; + A++; + } + if (sb) { /* B is negative */ + X ^= MANT; + B ^= FWORD; + X++; + if (X & EXPO) { + B++; + X &= MANT; + } + } + B = A + B; /* Do final math. */ + if (B & MSIGN) { /* Result is negative, switch */ + sb = 1; + X ^= MANT; + B ^= FWORD; + X++; + if (X & EXPO) { + B++; + X &= MANT; + } + } else + sb = 0; + if (B & EXPO) { /* Handle overflow */ + rnd = B & 07; + B >>= 3; + exp_b++; + } else if ((B & NORM) == 0) { + if ((X & NORM) == 0) { + rnd = 0; + } else { + X <<= 3; + B <<= 3; + B |= (X >> EXPO_V) & 07; + X &= MANT; + rnd = X >> (EXPO_V - 3); + exp_b--; + } + } else { + rnd = X >> (EXPO_V - 3); + } + if (rnd >= 4 && B != MANT) { + B++; + } + + B &= MANT; + if ((exp_b != 0) && (exp_b < -64) && (B & NORM) == 0) { + B <<= 3; + exp_b--; + } + if (B == 0) + return; + if (exp_b < 0) { /* Handle underflow */ + if (exp_b < -64 && NCSF) + Q |= EXPO_UNDER; + exp_b = ((-exp_b) & 077)|0100; + } else { + if (exp_b > 64 && NCSF) + Q |= EXPO_OVER; + exp_b &= 077; + } + B = (B & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | + ((sb) ? MSIGN: 0); +} + +/* + * Perform a 40 bit multiply on A and B, result into B,X + */ +void mult_step(t_uint64 a, t_uint64 *b, t_uint64 *x) { + t_uint64 u0,u1,v0,v1,t,w1,w2,w3,k; + + /* Split into 32 bit and 8 bit */ + u0 = a >> 32; u1 = a & 0xffffffff; + v0 = *b >> 32; v1 = *b & 0xffffffff; + /* Multiply lower halfs to 64 bits */ + t = u1*v1; + /* Lower 32 bits to w3. */ + w3 = t & 0xffffffff; + /* Upper 32 bits to k */ + k = t >> 32; + /* Add in partial product of upper & lower */ + t = u0*v1 + k; + w2 = t & 0xffffffff; + w1 = t >> 32; + t = u1*v0 + w2; + k = t >> 32; + /* Put result back together */ + *b = u0*v0 + w1 + k; + *x = (t << 32) + w3; + /* Put into 2 40 bit numbers */ + *b <<= 25; + *b |= (*x >> EXPO_V); + *x &= MANT; +} + +/* Do multiply instruction */ +void multiply() { + int exp_a, exp_b; + int f; + int int_f; + + AB_valid(); + AROF = 0; + /* Check if Either argument already zero */ + if ((A & MANT) == 0 || (B & MANT) == 0) { + B = 0; + return; + } + + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + exp_b = (B & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + if (B & ESIGN) + exp_b = -exp_b; + /* Extract signs, clear upper bits */ + f = (A & MSIGN) != 0; + A &= MANT; + f ^= ((B & MSIGN) != 0); + B &= MANT; + /* Flag if both exponents zero */ + int_f = (exp_a == 0) & (exp_b == 0); + if (int_f == 0) { + while ((A & NORM) == 0) { + A <<= 3; + exp_a--; + } + while ((B & NORM) == 0) { + B <<= 3; + exp_b--; + } + } + + mult_step(A, &B, &X); + + /* If integer and B is zero */ + if (int_f && B == 0) { + B = X; + X = 0; + exp_b = 0; + } else { + exp_b = exp_a + exp_b + 13; + while ((B & NORM) == 0) { + if (exp_b < -64) + break; + B <<= 3; + X <<= 3; + B |= (X >> EXPO_V) & 07; + X &= MANT; + exp_b--; + } + } + /* After B is normalize, check high order digit of X */ + if (X & ROUND) { + B++; + if (B & EXPO) { + B >>= 3; + exp_b++; + } + } + /* Check for over/underflow */ + if (exp_b < 0) { + if (exp_b < -64) { + if (NCSF) + Q |= EXPO_UNDER; + B = 0; + return; + } + exp_b = ((-exp_b) & 077) | 0100; + } else { + if (exp_b > 64) { + if (NCSF) + Q |= EXPO_OVER; + } + exp_b &= 077; + } + /* Put the pieces back together */ + B = (B & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | (f? MSIGN: 0); +} + + +/* Do divide instruction */ +void divide(int op) { + int exp_a, exp_b, q, sa, sb; + t_uint64 t; + + AB_valid(); + AROF = 0; + t = B; + + if ((A & MANT) == 0) { /* if A mantissa is zero */ + if (NCSF) /* and we're in Normal State */ + Q |= DIV_ZERO; + return; + } + + if ((B & MANT) == 0) { /* otherwise, if B is zero, */ + A = B = 0; /* result is all zeroes */ + return; + } + + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + exp_b = (B & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + if (B & ESIGN) + exp_b = -exp_b; + + /* Extract signs, clear upper bits */ + sb = (B & MSIGN) != 0; + sa = (A & MSIGN) != 0; + A &= MANT; + B &= MANT; + /* Normalize A and B */ + while ((A & NORM) == 0) { + A <<= 3; + exp_a--; + } + while ((B & NORM) == 0) { + B <<= 3; + exp_b--; + } + + if (op != WMOP_DIV && exp_a > exp_b) { /* Divisor has greater magnitude */ + /* Quotent is < 1, so set result to zero */ + A = 0; + B = (op == WMOP_RDV)? (t & FWORD) : 0; + return; + } + + if (op != WMOP_RDV) + sb ^= sa; /* positive if signs are same, negative if different */ + X = 0; + /* Now we step through the development of the quotient one octade at a time, + tallying the shifts in n until the high-order octade of X is non-zero + (i.e., normalized). The divisor is in ma and the dividend (which becomes + the remainder) is in mb. Since the operands are normalized, this will + take either 13 or 14 shifts. We do the X shift at the top of the loop so + that the 14th (rounding) digit will be available in q at the end. + The initial shift has no effect, as it operates using zero values for X + and q. */ + do { + q = 0; /* initialize the quotient digit */ + while (B >= A) { + ++q; /* bump the quotient digit */ + B -= A; /* subtract divisor from reAinder */ + } + + if (op == WMOP_DIV) { + if ((X & NORM) != 0) { + break; /* quotient has become normalized */ + } else { + B <<= 3; /* shift the remainder left one octade */ + X = (X<<3) + (t_uint64)q; /* shift quotient digit into the + working quotient */ + --exp_b; + } + } else { + X = (X<<3) + (t_uint64)q; /* shift quotient digit into the + working quotient */ + if ((X & NORM) != 0) { + break; /* quotient has become normalized */ + } else if (exp_a >= exp_b) { + break; + } else { + B <<= 3; /* shift the remainder left one octade */ + --exp_b; /* decrement the B exponent */ + } + } + } while (1); + + if (op == WMOP_DIV) { + exp_b -= exp_a - 1; /* compute the exponent, accounting for the shifts*/ + + /* Round the result (it's already normalized) */ + if (q >= 4) { /* if high-order bit of last quotient digit is 1 */ + if (X < MANT) { /* if the rounding would not cause overflow */ + ++X; /* round up the result */ + } + } + } else if (op == WMOP_IDV) { + if (exp_a == exp_b) { + exp_b = 0; /* integer result developed */ + } else { + if (NCSF) /* integer overflow result */ + Q |= INT_OVER; + exp_b -= exp_a; + } + } else { + X = B; /* Result in B */ + if (exp_a == exp_b) { /* integer result developed */ + if (X == 0) /* if B mantissa is zero, then */ + exp_b = sb = 0; /* assure result will be all zeroes */ + } else { + if (NCSF) /* integer overflow result */ + Q |= INT_OVER; + X = exp_b = sb = 0; /* result in B will be all zeroes */ + } + } + + /* Check for exponent under/overflow */ + if (exp_b > 63) { + exp_b &= 077; + if (NCSF) { + Q |= EXPO_OVER; + } + } else if (exp_b < 0) { + if (exp_b < -63) { + if (NCSF) + Q |= EXPO_UNDER; + } + exp_b = ((-exp_b) & 077) | 0100; + } + + /* Put the pieces back together */ + B = (X & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | (sb? MSIGN: 0); +} + + +/* Double precision addition. + A & Y (not in real B5500) have operand 1. + B & X have operand 2 */ +void double_add(int opcode) { + int exp_a, exp_b; + int sa, sb; + int ld; + t_uint64 temp; + + AB_valid(); + X = A; /* Save registers. X = H, Y=L*/ + Y = B; + AROF = BROF = 0; + AB_valid(); /* Grab other operand */ + temp = A; /* Oprands A,Y and B,X */ + A = X; + X = B; + B = temp; + + if (opcode == WMOP_DLS) /* Double Precision Subtract */ + A ^= MSIGN; + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + exp_b = (B & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + if (B & ESIGN) + exp_b = -exp_b; + /* Larger exponent to A */ + if (exp_b > exp_a) { + t_uint64 temp; + temp = A; + A = B; + B = temp; + temp = Y; + Y = X; + X = temp; + sa = exp_a; + exp_a = exp_b; + exp_b = sa; + } + /* Extract signs, clear upper bits */ + sa = (A & MSIGN) != 0; + A &= MANT; + Y &= MANT; + sb = (B & MSIGN) != 0; + B &= MANT; + X &= MANT; + ld = 0; + /* While the exponents are not equal, adjust */ + while(exp_a != exp_b) { + if ((A & NORM) == 0) { + A <<= 3; + Y <<= 3; + A |= (Y >> EXPO_V) & 07; + Y &= MANT; + exp_a--; + } else { + X |= (B & 07) << EXPO_V; + ld = (X & 07); + X >>= 3; + B >>= 3; + exp_b++; + if (B == 0 && X == 0) + break; + } + } + if (exp_a != exp_b) { + exp_b = exp_a; + B = 0; + X = 0; + } + if (sa) { /* A is negative. */ + Y ^= MANT; + A ^= FWORD; + Y++; + if (Y & EXPO) { + Y &= MANT; + A++; + } + } + if (sb) { /* B is negative */ + X ^= MANT; + B ^= FWORD; + X++; + if (X & EXPO) { + X &= MANT; + B++; + } + } + X = Y + X; + B = A + B; /* Do final math. */ + if (X & EXPO) { + B += X >> (EXPO_V); + X &= MANT; + } + + if (B & MSIGN) { /* Result is negative, switch */ + sb = 1; + X ^= MANT; + B ^= FWORD; + X++; + if (X & EXPO) { + X &= MANT; + B++; + } + } else { + sb = 0; + } + + while (B & EXPO) { /* Handle overflow */ + X |= (B & 07) << EXPO_V; + ld = X & 07; + B >>= 3; + X >>= 3; + exp_b++; + } + + if (ld >= 4 && X != MANT && B != MANT) { + X++; + if (X & EXPO) { + X &= MANT; + B++; + } + } + + while(exp_b > -63 && (B & NORM) == 0) { + B <<= 3; + X <<= 3; + B |= (X >> EXPO_V) & 07; + X &= MANT; + exp_b--; + } + + B &= MANT; + X &= MANT; + if (exp_b < 0) { /* Handle underflow */ + if (exp_b < -64 && NCSF) + Q |= EXPO_UNDER; + exp_b = ((-exp_b) & 077)|0100; + } else { + if (exp_b > 64 && NCSF) + Q |= EXPO_OVER; + exp_b &= 077; + } + A = (B & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | + (sb ? MSIGN: 0); + B = X; +} + +/* Double precision multiply. + A & Y (not in real B5500) have operand 1. + B & X have operand 2 */ +void double_mult() { + int exp_a, exp_b; + int f; + int ld; + t_uint64 m7, m6; + + AB_valid(); + X = A; /* Save registers. X = H, Y=L*/ + Y = B; + AROF = BROF = 0; + AB_valid(); /* Grab other operand */ + m7 = A; /* Oprands A,Y and B,X */ + A = X; + X = B; + B = m7; + + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + exp_b = (B & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + if (B & ESIGN) + exp_b = -exp_b; + /* Extract signs, clear upper bits */ + f = (A & MSIGN) != 0; + A &= MANT; + Y &= MANT; + f ^= ((B & MSIGN) != 0); + B &= MANT; + X &= MANT; + + /* Normalize the operands */ + for(ld = 0; (B & NORM) == 0 && ld < 13 ; ld++) { + B <<= 3; + B |= (X >> 36) & 07; + X <<= 3; + X &= MANT; + exp_b--; + } + + for(ld = 0; (A & NORM) == 0 && ld < 13 ; ld++) { + A <<= 3; + A |= (Y >> 36) & 07; + Y <<= 3; + Y &= MANT; + exp_a--; + } + + if ((X == 0 && B == 0) || (Y == 0 && A == 0)) { + A = B = 0; + return; + } + exp_b += exp_a + 13; + /* A = M3, Y = m3 */ + /* B = M4, X = m4 */ + /* B * Y => M6(Y),m6(m6) */ + /* A * X => M7(X) m7(m7) */ + /* Add m6(m7) + m7(m6) save High order digit of m7 */ + /* X = M7(X) + M6(Y) */ + /* A * B => Mx(B),mx(m6) */ + /* Add M7 to mx => M8 + m8 */ + /* Add M6 to m8 => M9(M8) + m9 */ + /* M6 m6 = M4 * m3 */ + mult_step(B, &Y, &m6); /* Y = M6, m6 = m6 */ + /* M7 m7 = (M3 * m4) */ + mult_step(A, &X, &m7); /* X = M7, m7 = m7 */ + m6 += m7; + ld = m6 >> (EXPO_V-3); /* High order digit */ + /* M8 m8 = (M4 * M3) */ + mult_step(A, &B, &m6); /* B = M8, m6 = m8 */ + /* M8 m8 = (M4 * M3) + M7 + M6 */ + m6 += X + Y; + /* M9 m9 = M8 + (m8 + M6) */ + /* M10m10= M9 + m9 + (high order digit of m7) */ + A = B + (m6 >> EXPO_V); + B = m6 & MANT; + + if ((A & EXPO) != 0) { + ld = B&7; + B |= (A & 07) << EXPO_V; + B >>= 3; + A >>= 3; + exp_b ++; + } + if ((A & NORM) == 0) { + A <<= 3; + A |= (B >> 36) & 07; + B <<= 3; + B += ld; + ld = 0;; + B &= MANT; + exp_b --; + } + if (ld >= 4 && A != MANT && B != MANT) { + B++; + if (B & EXPO) { + B &= MANT; + A++; + } + } + + if (exp_b < 0) { /* Handle underflow */ + if (exp_b < -64 && NCSF) + Q |= EXPO_UNDER; + exp_b = ((-exp_b) & 077)|0100; + } else { + if (exp_b > 64 && NCSF) + Q |= EXPO_OVER; + exp_b &= 077; + } + A = (A & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | + (f ? MSIGN: 0); +} + +/* Double precision divide. + A & Y (not in real B5500) have operand 1. + B & X have operand 2 */ +void double_divide() { + int exp_a, exp_b; + int f; + int n; + int q; + t_uint64 Q1, q1; + + AB_valid(); + X = A; /* Save registers. X = H, Y=L*/ + Y = B; + AROF = BROF = 0; + AB_valid(); /* Grab other operand */ + Q1 = A; /* Oprands A,Y and B,X */ + A = X; + X = B; + B = Q1; + + /* Extract exponent */ + exp_a = (A & EXPO) >> EXPO_V; + if (A & ESIGN) + exp_a = -exp_a; + /* Extract signs, clear upper bits */ + f = (A & MSIGN) != 0; + A &= MANT; + Y &= MANT; + /* Normalize A */ + for(n = 0; (A & NORM) == 0 && n < 13 ; n++) { + A <<= 3; + A |= (Y >> 36) & 07; + Y <<= 3; + Y &= MANT; + exp_a--; + } + + /* Extract B */ + exp_b = (B & EXPO) >> EXPO_V; + if (B & ESIGN) + exp_b = -exp_b; + f ^= ((B & MSIGN) != 0); + B &= MANT; + X &= MANT; + for(n = 0; (B & NORM) == 0 && n < 13 ; n++) { + B <<= 3; + B |= (X >> 36) & 07; + X <<= 3; + X &= MANT; + exp_b--; + } + + /* Check for divisor of 0 */ + if ((B == 0) && (X == 0)) { + A = 0; + return; + } + + /* Check for divide by 0 */ + if ((A == 0) && (Y == 0)) { + if (NCSF) + Q |= DIV_ZERO; + A = B; + B = X; + return; + } + + exp_b = exp_b - exp_a + 1; + /* B,X = M4,m4 A,Y = M3,m3 */ + + /* Divide M4,m4 by M3 resulting in Q1, R1 */ + do { + q = 0; /* initialize the quotient digit */ + while (B >= A) { + ++q; /* bump the quotient digit */ + B -= A; /* subtract divisor from reAinder */ + } + + B <<= 3; /* shift the remainder left one octade */ + X = (X<<3) + (t_uint64)q; /* shift quotient digit into the + working quotient */ + --exp_b; + n++; + } while (n < 13); + + if (exp_b < 0) { /* Handle underflow */ + if (exp_b < -64 && NCSF) + Q |= EXPO_UNDER; + exp_b = ((-exp_b) & 077)|0100; + } else { + if (exp_b > 64 && NCSF) + Q |= EXPO_OVER; + exp_b &= 077; + } + + /* Save Q1 in x R1 in B */ + Q1 = (X & MANT) | ((t_uint64)(exp_b & 0177) << EXPO_V) | + (f ? MSIGN: 0); + X = 0; + /* Now divide R1 by M3 resulting in q1, R2 */ + /* A=M3, B=R1, X=q1, B=R2 */ + for (n = 0; n < 13; n++) { + q = 0; /* initialize the quotient digit */ + while (B >= A) { + ++q; /* bump the quotient digit */ + B -= A; /* subtract divisor from reAinder */ + } + + B <<= 3; /* shift the remainder left one octade */ + X = (X<<3) + (t_uint64)q; /* shift quotient digit into the + working quotient */ + } + + q1 = X; + B = Y; + Y = X; + X = 0; + /* Now divide m3 by M3 resulting in q2, R3 */ + /* q2 in X, R3 in B */ + for (n = 0; n < 13; n++) { + q = 0; /* initialize the quotient digit */ + while (B >= A) { + ++q; /* bump the quotient digit */ + B -= A; /* subtract divisor from reAinder */ + } + + B <<= 3; /* shift the remainder left one octade */ + X = (X<<3) + (t_uint64)q; /* shift quotient digit into the + working quotient */ + } + + if (X == 0) { + A = Q1; + B = q1; + } else { + /* Load in Q1,q1 into B */ + A = 01157777777777777; + Y = MANT ^ X; /* Load q2 into A */ + B = Q1; + X = q1; + double_mult(); + } +} + +void relativeAddr(int store) { + uint16 base = R; + uint16 addr = (A & 01777); + + if (SALF) { + switch ((addr >> 7) & 7) { + case 0: + case 1: + case 2: + case 3: + default: /* To avoid compiler warnings */ + break; + + case 4: + case 5: + addr &= 0377; + if (MSFF) { + Ma = R+7; + memory_cycle(4); + base = FF(A); + } else + base = F; + break; + + case 6: + addr &= 0177; + base = (store)? R : ((L) ? C : C-1); + break; + + case 7: + addr = -(addr & 0177); + if (MSFF) { + Ma = R+7; + memory_cycle(4); + base = FF(A); + } else + base = F; + break; + } + } + Ma = (base + addr) & CORE; +} + +t_stat +sim_instr(void) +{ + t_stat reason; + t_uint64 temp = 0LL; + uint16 atemp; + uint8 opcode; + uint8 field; + int bit_a; + int bit_b; + int f; + int i; + int j; + + reason = 0; + hltf[0] = 0; + hltf[1] = 0; + P1_run = 1; + + while (reason == 0) { /* loop until halted */ + if (P1_run == 0) + return SCPE_STOP; + while (loading) { + sim_interval = -1; + reason = sim_process_event(); + if (reason != SCPE_OK) { + break; /* process */ + } + } + if (sim_interval <= 0) { /* event queue? */ + reason = sim_process_event(); + if (reason != SCPE_OK) { + break; /* process */ + } + } + + if (sim_brk_summ) { + if(sim_brk_test((C << 3) | L, SWMASK('E'))) { + reason = SCPE_STOP; + break; + } + + if (sim_brk_test((c_reg[0] << 3) | l_reg[0], + SWMASK('A'))) { + reason = SCPE_STOP; + break; + } + + if (sim_brk_test((c_reg[1] << 3) | l_reg[1], + SWMASK('B'))) { + reason = SCPE_STOP; + break; + } + } + + /* Toggle between two CPU's. */ + if (cpu_index == 0 && P2_run == 1) { + cpu_index = 1; + /* Check if interrupt pending. */ + if (TROF == 0 && NCSF && ((Q != 0) || HLTF)) + /* Force a SFI */ + storeInterrupt(1,0); + } else { + cpu_index = 0; + + /* Check if interrupt pending. */ + if (TROF == 0 && NCSF && ((Q != 0) || + (IAR != 0))) + /* Force a SFI */ + storeInterrupt(1,0); + } + if (TROF == 0) + next_prog(); + + /* TODO: Add support to detect IDLE loop */ +crf_loop: + opcode = T & 077; + field = (T >> 6) & 077; + TROF = 0; + + if (hst_lnt) { /* history enabled? */ + /* Ignore idle loop when recording history */ + /* DCMCP XIII */ + /* if ((C & 077774) != 01140) { */ + /* DCMCP XV */ + /* if ((C & 077774) != 01254) { */ + /* TSMCP XV */ + /* if ((C & 077774) != 01324) { */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) { + hst_p = 0; + } + hst[hst_p].c = (C) | HIST_PC; + hst[hst_p].op = T; + hst[hst_p].s = S; + hst[hst_p].f = F; + hst[hst_p].r = R; + hst[hst_p].ma = Ma; + hst[hst_p].a_reg = A; + hst[hst_p].b_reg = B; + hst[hst_p].x_reg = X; + hst[hst_p].gh = GH; + hst[hst_p].kv = KV; + hst[hst_p].l = L; + hst[hst_p].q = Q; + hst[hst_p].cpu = cpu_index; + hst[hst_p].iar = IAR; + hst[hst_p].flags = ((AROF)? F_AROF : 0) | \ + ((BROF)? F_BROF : 0) | \ + ((CWMF)? F_CWMF : 0) | \ + ((NCSF)? F_NCSF : 0) | \ + ((SALF)? F_SALF : 0) | \ + ((MSFF)? F_MSFF : 0) | \ + ((VARF)? F_VARF : 0); + /* } */ + } + + /* Check if Character or Word Mode */ + if (CWMF) { + /* Character mode source word in A + addressed by M,G,H. + destination in B + addressed by S,K,V. + R = tally. + X = loop control. + F = return control word + */ + switch(opcode) { + case CMOP_EXC: /* EXIT char mode */ + if (BROF) { + memory_cycle(013); + } + S = F; + AROF = 0; + memory_cycle(3); /* Load B from S */ + if ((B & FLAG) == 0) { + if (NCSF) + Q |= FLAG_BIT; + break; + } + f = set_via_RCW(B, (field & 1), 0); + S = F; + memory_cycle(3); /* Load MSW from S to B */ + set_via_MSCW(B); + prev_addr(S); + CWMF = 0; + if (MSFF && SALF) { + Ma = F; + do { + /* B = M[FIELD], Ma = B[FIELD]; */ + memory_cycle(6); /* Grab previous MCSW */ + } while(B & SMSFF); + Ma = R | 7; + memory_cycle(015); /* Store B in Ma */ + } + BROF = 0; + X = 0; + if ((field & 1) == 0) + PROF = 0; + break; + + case CMOP_BSD: /* Skip Bit Destiniation */ + if (BROF) { + memory_cycle(013); + } + while(field > 0) { + field--; + next_dest(1); + } + break; + + case CMOP_SRS: /* Skip Reverse Source */ + adjust_source(); + while(field > 0) { + field--; + prev_src(0); + } + break; + + case CMOP_SFS: /* Skip Forward Source */ + adjust_source(); + while(field > 0) { + field--; + next_src(0); + } + break; + + case CMOP_BSS: /* SKip Bit Source */ + while(field > 0) { + field--; + next_src(1); + } + break; + + case CMOP_SFD: /* Skip Forward Destination */ + adjust_dest(); + while(field > 0) { + field--; + next_dest(0); + } + break; + + case CMOP_SRD: /* Skip Reverse Destination */ + adjust_dest(); + while(field > 0) { + field--; + prev_dest(0); + } + break; + + case CMOP_RSA: /* Recall Source Address */ + Ma = (F - field) & CORE; + memory_cycle(4); + AROF = 0; + if (A & FLAG) { + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + GH = 0; + } else { + GH = (A >> 12) & 070; + } + Ma = CF(A); + break; + + case CMOP_RDA: /* Recall Destination Address */ + if (BROF) + memory_cycle(013); + S = (F - field) & CORE; + memory_cycle(3); + BROF = 0; + if (B & FLAG) { + if ((B & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + KV = 0; + } else { + KV = (B >> 12) & 070; + } + S = CF(B); + break; + + case CMOP_RCA: /* Recall Control Address */ + AROF = BROF; + A = B; /* Save B temporarly */ + atemp = S; /* Save S */ + S = (F - field) & CORE; + memory_cycle(3); /* Load word in B */ + S = atemp; /* Restore S */ + if (B & FLAG) { + if ((B & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + C = CF(B); + L = 0; + } else { + C = CF(B); + L = RF(B) + 1; + if (L > 3) { + L = 0; + next_addr(C); + } + } + B = A; + BROF = AROF; + AROF = 0; + PROF = 0; + break; + + case CMOP_SED: /* Set Destination Address */ + if (BROF) + memory_cycle(013); + S = (F - field) & CORE; + KV = 0; + BROF = 0; + break; + + case CMOP_SES: /* Set Source Address */ + Ma = (F - field) & CORE; + GH = 0; + AROF = 0; + break; + + case CMOP_TSA: /* Transfer Source Address */ + if (BROF) + memory_cycle(013); + BROF = 0; + adjust_source(); + field = 3; + while(field > 0) { + fill_src(); + i = (A >> bit_number[GH | 07]) & 077; + B <<= 6; + B |= i; + next_src(0); + field--; + } + B &= FLAG|FWORD; + GH = (B >> 12) & 070; + Ma = CF(B); + break; + + case CMOP_TDA: /* Transfer Destination Address */ + if (BROF) + memory_cycle(013); + BROF = 0; + adjust_dest(); + field = 3; + temp = 0; + while(field > 0) { + fill_dest(); + i = (B >> bit_number[KV | 07]) & 077; + temp <<= 6; + temp |= i; + next_dest(0); + field--; + } + BROF = 0; + KV = (temp >> 12) & 070; + S = CF(temp); + break; + + case CMOP_SCA: /* Store Control Address */ + A = B; + AROF = BROF; + B = toF(F) | toL(L) | toC(C); + F = S; + S = CF(FF(B) - field); + memory_cycle(013); /* Store B in S */ + S = F; + F = FF(B); + B = A; + BROF = AROF; + AROF = 0; + break; + + case CMOP_SDA: /* Store Destination Address */ + adjust_dest(); + A = B; + AROF = BROF; + B = ((t_uint64)(KV & 070) << (FFIELD_V - 3)) | toC(S); + S = (F - field) & CORE; + memory_cycle(013); /* Store B in S */ + S = CF(B); + B = A; + BROF = AROF; + AROF = 0; + break; + + case CMOP_SSA: /* Store Source Address */ + adjust_source(); + A = B; + AROF = BROF; + B = ((t_uint64)(GH & 070) << (FFIELD_V - 3)) | toC(Ma); + Ma = (F - field) & CORE; + memory_cycle(015); /* Store B in Ma */ + Ma = CF(B); + B = A; + BROF = AROF; + AROF = 0; + break; + + case CMOP_TRW: /* Transfer Words */ + if (BROF) { + memory_cycle(013); + BROF = 0; + } + if (GH != 0) { + next_addr(Ma); + GH = 0; + AROF = 0; + } + if (KV != 0) { + next_addr(S); + KV = 0; + } + while(field > 0) { + field--; + memory_cycle(4); + memory_cycle(012); + next_addr(Ma); + next_addr(S); + } + BROF = 0; + AROF = 0; + break; + + case CMOP_TEQ: /* Test for Equal 24 */ + case CMOP_TNE: /* Test for Not-Equal 25 */ + case CMOP_TEG: /* Test for Greater Or Equal 26 */ + case CMOP_TGR: /* Test For Greater 27 */ + case CMOP_TEL: /* Test For Equal or Less 34 */ + case CMOP_TLS: /* Test For Less 35 */ + case CMOP_TAN: /* Test for Alphanumeric 36 */ + adjust_source(); + fill_src(); + i = rank[(A >> bit_number[GH | 07]) & 077]; + j = rank[field]; + if (i == j) + f = 1; + else if (i < j) + f = 2; + else + f = 4; + switch(opcode) { + case CMOP_TEQ: /* Test for Equal 24 */ + TFFF = (f == 1); break; + case CMOP_TNE: /* Test for Not-Equal 25 */ + TFFF = (f != 1); break; + case CMOP_TEG: /* Test for Greater Or Equal 26 */ + TFFF = ((f & 5) != 0); break; + case CMOP_TGR: /* Test For Greater 27 */ + TFFF = (f == 4); break; + case CMOP_TEL: /* Test For Equal or Less 34 */ + TFFF = ((f & 3) != 0); break; + case CMOP_TLS: /* Test For Less 35 */ + TFFF = (f == 2); break; + case CMOP_TAN: /* Test for Alphanumeric 36 */ + if (f & 4) { + TFFF = !((i == 34) | (i == 44)); + } else { + TFFF = f & 1; + } + break; + } + break; + + case CMOP_BIS: /* Set Bit */ + case CMOP_BIR: /* Reet Bit */ + while(field > 0) { + field--; + fill_dest(); + temp = bit_mask[bit_number[KV]]; + if (opcode & 1) + B &= ~temp; + else + B |= temp; + next_dest(1); + } + break; + + case CMOP_BIT: /* Test Bit */ + fill_src(); + i = (A >> bit_number[GH]) & 01; + TFFF = (i == (field & 1)); + break; + + case CMOP_INC: /* Increase Tally */ + R = (R + (field<<6)) & 07700; + break; + + case CMOP_STC: /* Store Tally */ + if (BROF) + memory_cycle(11); + AROF = 0; + BROF = 0; + A = toC(F); + B = R >> 6; + F = S; + S = CF(CF(A) - field); + memory_cycle(11); + S = F; + F = CF(A); + break; + + case CMOP_SEC: /* Set Tally */ + R = T & 07700; + break; + + case CMOP_CRF: /* Call repeat Field */ + /* Save B in A */ + AROF = BROF; + A = B; + /* Save F in B */ + atemp = F; + /* Exchange S & F */ + F = S; + /* Decrement S */ + S = (atemp - field) & CORE; + /* Read value to B, S <= F, F <= B */ + memory_cycle(3); + /* field = B & 077 */ + field = B & 077; + /* Restore B */ + S = F; + F = atemp; + B = A; + BROF = AROF; + AROF = 0; + /* fetch_next; */ + next_prog(); + /* If field == 0, opcode |= 4; field = T */ + if (field == 0) { + T &= 07700; + T |= CMOP_JFW; + } else { + T &= 077; + T |= field << 6; + } + /* Goto crf_loop */ + goto crf_loop; + + case CMOP_JNC: /* Jump Out Of Loop Conditional */ + if (TFFF) + break; + + /* Fall through */ + case CMOP_JNS: /* Jump out of loop unconditional */ + /* Read Loop/Return control word */ + atemp = S; + S = FF(X); + memory_cycle(2); /* Load S to A */ + AROF = 0; + X = (A & MANT); + S = atemp; + if (field > 0) { + i = (C << 2) | L; /* Make into syllable pointer */ + i += field; + L = i & 3; /* Convert back to pointer */ + C = (i >> 2) & CORE; + PROF = 0; + } + break; + + case CMOP_JFC: /* Jump Forward Conditional */ + case CMOP_JRC: /* Jump Reverse Conditional */ + if (TFFF != 0) + break; + + /* Fall through */ + case CMOP_JFW: /* Jump Forward Unconditional */ + case CMOP_JRV: /* Jump Reverse Unconditional */ + i = (C << 2) | L; /* Make into syllable pointer */ + if (opcode & 010) { /* Forward */ + i -= field; + } else { /* Backward */ + i += field; + } + L = i & 3; /* Convert back to pointer */ + C = (i >> 2) & CORE; + PROF = 0; + break; + + case CMOP_ENS: /* End Loop */ + A = B; + AROF = BROF; + B = X; + field = (uint8)((B & REPFLD) >> REPFLD_V); + if (field) { + X &= ~REPFLD; + X |= ((t_uint64)(field - 1) << REPFLD_V) & REPFLD; + L = LF(B); + C = CF(B); + PROF = 0; + memory_cycle(020); + } else { + atemp = S; + S = FF(X); + memory_cycle(3); /* Load B */ + X = B & MANT; + S = atemp; + } + B = A; + BROF = AROF; + AROF = 0; + break; + + case CMOP_BNS: /* Begin Loop */ + A = B; /* Save B */ + AROF = BROF; + B = X | FLAG | DFLAG; + if (field != 0) + field--; + atemp = S; + S = FF(B); + next_addr(S); + memory_cycle(013); + X = LCW(S, field); + S = atemp; + B = A; + BROF = AROF; + AROF = 0; + break; + + case CMOP_OCV: /* Output Convert */ + adjust_dest(); + if (BROF) { + memory_cycle(013); + BROF = 0; + } + /* Adjust source to word boundry */ + if (GH) { + GH = 0; + next_addr(Ma); + AROF = 0; + } + + if (field == 0) + break; + + /* Load word into A */ + fill_src(); + next_addr(Ma); + AROF = 0; + B = 0; + temp = 0; + f = (A & MSIGN) != 0; + TFFF = 1; + A &= MANT; + if (A == 0) + f = 0; + i = 39; + /* We loop over the bit in A and add one to B + each time we have the msb of A set. For each + step we BCD double the number in B */ + while(i > 0) { + /* Compute carry to next digit */ + temp = (B + 0x33333333LL) & 0x88888888LL; + B <<= 1; /* Double it */ + /* Add 6 from every digit that overflowed */ + temp = (temp >> 1) | (temp >> 2); + B += temp; + /* Lastly Add in new digit */ + j = (A & ROUND) != 0; + A &= ~ROUND; + B += (t_uint64)j; + A <<= 1; + i--; + } + A = B; + field = field & 07; + if (field == 0) + field = 8; + /* Now we put each digit into the destination string */ + for(i = 8; i >= 0; i--) { + j = (A >> (i * 4)) & 0xF; + if (i >= field) { + if (j != 0) + TFFF = 0; + } else { + fill_dest(); + temp = 077LL << bit_number[KV | 07]; + B &= ~temp; + if (i == 0 && f) + j |= 040; + B |= ((t_uint64)j) << bit_number[KV | 07]; + BROF = 1; + next_dest(0); + } + } + break; + + case CMOP_ICV: /* Input Convert */ + adjust_source(); + if (BROF) { + memory_cycle(013); + BROF = 0; + } + /* Align result to word boundry */ + if (KV) { + KV = 0; + next_addr(S); + } + + if (field == 0) + break; + B = 0; + temp = 0; + f = 0; + /* Collect the source field into a string of BCD digits */ + while(field > 0) { + fill_src(); + i = (int)(A >> bit_number[GH | 07]); + B = (B << 4) | (i & 017); + f = (i & 060) == 040; /* Keep sign */ + field = (field - 1) & 07; + next_src(0); + } + /* We loop over the BCD number in B, dividing it by 2 + each cycle, while shifting the lsb into the top of + the A register */ + field = 28; + A = 0; + while(field > 0) { + A >>= 1; + if (B & 1) + A |= ((t_uint64)1) << 27; + /* BCD divide by 2 */ + temp = B & 0x0011111110LL; + temp = (temp >> 4) | (temp >> 3); + B = (B >> 1) - temp; + field--; + } + if (f && A != 0) + A |= MSIGN; + memory_cycle(012); + AROF = 0; + next_addr(S); + break; + + case CMOP_CEQ: /* Compare Equal 60 */ + case CMOP_CNE: /* COmpare for Not Equal 61 */ + case CMOP_CEG: /* Compare For Greater Or Equal 62 */ + case CMOP_CGR: /* Compare For Greater 63 */ + case CMOP_CEL: /* Compare For Equal or Less 70 */ + case CMOP_CLS: /* Compare for Less 71 */ + case CMOP_FSU: /* Field Subtract 72 */ + case CMOP_FAD: /* Field Add 73 */ + adjust_source(); + adjust_dest(); + TFFF = 1; /* flag to show greater */ + f = 1; /* Still comparaing */ + while(field > 0) { + fill_src(); + fill_dest(); + if (f) { + i = (A >> bit_number[GH | 07]) & 077; + j = (B >> bit_number[KV | 07]) & 077; + if (i != j) { + switch(opcode) { + case CMOP_FSU: /* Field Subtract */ + case CMOP_FAD: /* Field Add */ + /* Do numeric compare */ + i &= 017; + j &= 017; + if (i != j) + f = 0; /* Different, so stop check */ + if (i < j) + TFFF = 0; + break; + case CMOP_CEQ: /* Compare Equal 60 */ + case CMOP_CNE: /* Compare for Not Equal 61 */ + case CMOP_CEG: /* Compare For Greater Or Equal 62 */ + case CMOP_CGR: /* Compare For Greater 63 */ + case CMOP_CEL: /* Compare For Equal or Less 70 */ + case CMOP_CLS: /* Compare for Less 71 */ + f = 0; /* No need to continue; */ + if (rank[i] < rank[j]) + TFFF = 0; + break; + } + } + } + next_src(0); + next_dest(0); + field--; + } + /* If F = 1, fields are equal. + If F = 0 and TFFF = 0 S < D. + If F = 0 and TFFF = 1 S > D. + */ + switch(opcode) { + case CMOP_FSU: /* Field Subtract */ + case CMOP_FAD: /* Field Add */ + { + int ss, sd, sub; + int c; + /* Back up one location */ + prev_src(0); + prev_dest(0); + fill_src(); + fill_dest(); + field = (T >> 6) & 077; + i = (A >> bit_number[GH | 07]) & 077; + j = (B >> bit_number[KV | 07]) & 077; + /* Compute Sign */ + ss = (i & 060) == 040; /* S */ + sd = (j & 060) == 040; /* D */ + sub = (ss == sd) ^ (opcode == CMOP_FAD); + /* + ss sd sub f=1 TFFF=0 TFFF=1 + add 0 0 0 0 0 0 + add 1 0 1 0 0 1 + add 0 1 1 0 1 0 + add 1 1 0 1 1 1 + sub 0 0 1 0 0 1 + sub 1 0 0 0 0 0 + sub 0 1 0 1 1 1 + sub 1 1 1 0 1 0 + */ + f = (f & sd & ss & (!sub)) | + (f & sd & (!ss) & (!sub)) | + ((!f) & (!TFFF) & sd) | + ((!f) & TFFF & (ss ^ (opcode == CMOP_FSU))); + c = 0; + if (sub) { + c = 1; + if (TFFF) { /* If S < D */ + ss = 0; + sd = 1; + } else { + ss = 1; + sd = 0; + } + } else { + sd = ss = 0; + } + /* Do the bulk of adding. */ + i &= 017; + j &= 017; + while (field > 0) { + i = (ss ? 9-i : i ) + (sd ? 9-j : j) + c; + if (i < 10) { + c = 0; + } else { + c = 1; + i -= 10; + } + if (f) { + i += 040; + f = 0; + } + temp = 077LL << bit_number[KV | 07]; + B &= ~temp; + B |= ((t_uint64)i) << bit_number[KV | 07]; + prev_src(0); + prev_dest(0); + fill_src(); + fill_dest(); + i = (A >> bit_number[GH | 07]) & 017; + j = (B >> bit_number[KV | 07]) & 017; + field--; + } + /* Set overflow flag */ + TFFF = sub ^ c; + } + /* Lastly back to end of field. */ + field = (T >> 6) & 077; + next_src(0); + next_dest(0); + while (field > 0) { + next_src(0); + next_dest(0); + field--; + } + break; + case CMOP_CEQ: /* Compare Equal 60 */ + TFFF = f; + break; + case CMOP_CNE: /* Compare for Not Equal 61 */ + TFFF = !f; + break; + case CMOP_CEG: /* Compare For Greater Or Equal 62 */ + TFFF |= f; + break; + case CMOP_CGR: /* Compare For Greater 63 */ + TFFF &= !f; + break; + case CMOP_CEL: /* Compare For Equal or Less 70 */ + TFFF = !TFFF; + TFFF |= f; + break; + case CMOP_CLS: /* Compare for Less 71 */ + TFFF = !TFFF; + TFFF &= !f; + break; + } + break; + + case CMOP_TRP: /* Transfer Program Characters 74 */ + adjust_dest(); + while(field > 0) { + fill_dest(); + if (!TROF) + next_prog(); + if (field & 1) { + i = T & 077; + TROF = 0; + } else { + i = (T >> 6) & 077; + } + temp = 077LL << bit_number[KV | 07]; + B &= ~temp; + B |= ((t_uint64)i) << bit_number[KV | 07]; + next_dest(0); + field--; + } + TROF = 0; + break; + + case CMOP_TRN: /* Transfer Numeric 75 */ + case CMOP_TRZ: /* Transfer Zones 76 */ + case CMOP_TRS: /* Transfer Source Characters 77 */ + adjust_source(); + adjust_dest(); + while(field > 0) { + fill_dest(); + fill_src(); + i = (int)(A >> bit_number[GH | 07]); + if (opcode == CMOP_TRS) { + i &= 077; + temp = 077LL << bit_number[KV | 07]; + } else if (opcode == CMOP_TRN) { + if (field == 1) { + if ((i & 060) == 040) + TFFF = 1; + else + TFFF = 0; + } + i &= 017; + temp = 077LL << bit_number[KV | 07]; + } else { + i &= 060; + temp = 060LL << bit_number[KV | 07]; + } + B &= ~temp; + B |= ((t_uint64)i) << bit_number[KV | 07]; + next_src(0); + next_dest(0); + field--; + } + break; + + case CMOP_TBN: /* Transfer Blanks for Non-Numerics 12 */ + adjust_dest(); + TFFF = 1; + while(field > 0) { + fill_dest(); + i = (B >> bit_number[KV | 07]) & 077; + if (i > 0 && i <= 9) { + TFFF = 0; + break; + } + B &= ~(077LL << bit_number[KV | 07]); + B |= 060LL << bit_number[KV | 07]; + next_dest(0); + field--; + } + break; + + case 0011: + goto control; + } + } else { + /* Word mode opcodes */ + switch(opcode & 03) { + case WMOP_LITC: /* Load literal */ + A_empty(); + A = toC(T >> 2); + AROF = 1; + break; + + case WMOP_OPDC: /* Load operand */ + A_empty(); + A = toC(T >> 2); + relativeAddr(0); + memory_cycle(4); + SALF |= VARF; + VARF = 0; +opdc: + if (A & FLAG) { + /* Check if it is a control word. */ + if ((A & DFLAG) != 0 && (A & PROGF) == 0) { + break; + } + /* Check if descriptor present */ + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + /* Program Descriptor */ + if ((A & (DFLAG|PROGF)) == (DFLAG|PROGF)) { + enterSubr(0); + } else { + if (indexWord()) + break; + memory_cycle(4); + if (NCSF && (A & FLAG)) + Q |= FLAG_BIT; + } + } + break; + + case WMOP_DESC: /* Load Descriptor */ + A_empty(); + A = toC(T >> 2); + relativeAddr(0); + memory_cycle(4); + SALF |= VARF; + VARF = 0; +desc: + if (A & FLAG) { + /* Check if it is a control word. */ + if ((A & DFLAG) != 0 && (A & PROGF) == 0) { + A = toC(Ma) | PRESENT | FLAG; + break; + } + /* Check if descriptor present */ + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + /* Data descriptor */ + if ((A & (DFLAG|PROGF)) == (DFLAG|PROGF)) { + enterSubr(1); + } else { + if (indexWord()) + break; + A |= FLAG|PRESENT; + } + } else { + A = toC(Ma) | FLAG | PRESENT; + } + break; + + case WMOP_OPR: /* All other operators */ + switch(opcode) { + case 0001: + switch(field) { + case VARIANT(WMOP_SUB): /* Subtract */ + case VARIANT(WMOP_ADD): /* Add */ + add(T); + break; + case VARIANT(WMOP_MUL): /* Multiply */ + multiply(); + break; + case VARIANT(WMOP_DIV): /* Divide */ + case VARIANT(WMOP_IDV): /* Integer Divide Integer */ + case VARIANT(WMOP_RDV): /* Remainder Divide */ + divide(T); + break; + } + break; + + case 0005: + switch(field) { + case VARIANT(WMOP_DLS): /* Double Precision Subtract */ + case VARIANT(WMOP_DLA): /* Double Precision Add */ + double_add(T); + break; + case VARIANT(WMOP_DLM): /* Double Precision Multiply */ + double_mult(); + break; + case VARIANT(WMOP_DLD): /* Double Precision Divide */ + double_divide(); + break; + } + break; + + case 0011: + /* Control functions same as Character mode, + although most operators do not make sense in + Character mode. */ +control: + switch(field) { + /* Different in Character mode */ + case VARIANT(WMOP_SFT): /* Store for Test */ + storeInterrupt(0,1); + break; + + case VARIANT(WMOP_SFI): /* Store for Interrupt */ + storeInterrupt(0,0); + break; + + case VARIANT(WMOP_ITI): /* Interrogate interrupt */ + if (NCSF) /* Nop in normal state */ + break; + + if (q_reg[0] & MEM_PARITY) { + C = PARITY_ERR; + q_reg[0] &= ~MEM_PARITY; + } else if (q_reg[0] & INVALID_ADDR) { + C = INVADR_ERR; + q_reg[0] &= ~INVALID_ADDR; + } else if (IAR) { + uint16 x; + C = INTER_TIME; + for(x = 1; (IAR & x) == 0; x <<= 1) + C++; + /* Release the channel after we got it */ + if (C >= IO1_FINISH && C <= IO4_FINISH) + chan_release(C - IO1_FINISH); + IAR &= ~x; + } else if ((q_reg[0] & 0170) != 0) { + C = 060 + (q_reg[0] >> 3); + q_reg[0] &= 07; + } else if (q_reg[0] & STK_OVERFL) { + C = STK_OVR_LOC; + q_reg[0] &= ~STK_OVERFL; + } else if (P2_run == 0 && q_reg[1] != 0) { + if (q_reg[1] & MEM_PARITY) { + C = PARITY_ERR2; + q_reg[1] &= ~MEM_PARITY; + } else if (q_reg[1] & INVALID_ADDR) { + C = INVADR_ERR2; + q_reg[1] &= ~INVALID_ADDR; + } else if ((q_reg[1] & 0170) != 0) { + C = 040 + (q_reg[1] >> 3); + q_reg[1] &= 07; + } else if (q_reg[1] & STK_OVERFL) { + C = STK_OVR_LOC2; + q_reg[1] &= ~STK_OVERFL; + } + } else { + /* False IRQ */ + break; + } + sim_debug(DEBUG_DETAIL, &cpu_dev, "IAR=%05o Q=%03o\n\r", + IAR,Q); + L = 0; + S = 0100; + CWMF = 0; + PROF = 0; + break; + + case VARIANT(WMOP_IOR): /* I/O Release */ + if (NCSF) /* Nop in normal state */ + break; + + /* Fall through */ + case VARIANT(WMOP_PRL): /* Program Release */ + A_valid(); + if ((A & FLAG) == 0) { + relativeAddr(1); + } else if (A & PRESENT) { + Ma = CF(A); + } else { + if (NCSF) + Q |= PRES_BIT; + break; + } + memory_cycle(4); /* Read Ma to A */ + if (NCSF) { /* Can't occur for IOR */ + Q |= (A & CONTIN) ? CONT_BIT : PROG_REL; + A = toC(Ma); + Ma = R | 9; + } else { + if (field == VARIANT(WMOP_PRL)) + A &= ~PRESENT; + else + A |= PRESENT; + } + memory_cycle(014); /* Store A to Ma */ + AROF = 0; + break; + + case VARIANT(WMOP_RTR): /* Read Timer */ + if (!NCSF) { + A_empty(); + A = RTC; + if (IAR & IRQ_0) + A |= 0100; + AROF = 1; + } + break; + + case VARIANT(WMOP_COM): /* Communication operator */ + if (NCSF) { + Ma = R|9; + save_tos(); + Q |= COM_OPR; + } + break; + + case VARIANT(WMOP_ZP1): /* Conditional Halt */ + if (NCSF) + break; + if (!HLTF) + break; + if (!HALT) + break; + hltf[0] = 1; + P1_run = 0; + break; + + case VARIANT(WMOP_HP2): /* Halt P2 */ + /* Control state only */ + if (NCSF) + break; + /* If CPU 2 is not running, or disabled nop */ + if (P2_run == 0 || (cpu_unit[1].flags & UNIT_DIS)) { + break; + } + sim_debug(DEBUG_DETAIL, &cpu_dev, "HALT P2\n\r"); + /* Flag P2 to stop */ + hltf[1] = 1; + TROF = 1; /* Reissue until CPU2 stopped */ + break; + + case VARIANT(WMOP_IP1): /* Initiate P1 */ + if (NCSF) + break; + A_valid(); /* Load ICW */ + sim_debug(DEBUG_DETAIL, &cpu_dev, "INIT P1\n\r"); + initiate(); + break; + + case VARIANT(WMOP_IP2): /* Initiate P2 */ + if (NCSF) + break; + Ma = 010; + save_tos(); + /* If CPU is operating, or disabled, return busy */ + if (P2_run != 0 || (cpu_unit[1].flags & UNIT_DIS)) { + IAR |= IRQ_11; /* Set CPU 2 Busy */ + break; + } + /* Ok we are going to initiate B. + load the initiate word from 010. */ + hltf[1] = 0; + P2_run = 1; + cpu_index = 1; /* To CPU 2 */ + Ma = 010; + memory_cycle(4); + sim_debug(DEBUG_DETAIL, &cpu_dev, "INIT P2\n\r"); + initiate(); + break; + + case VARIANT(WMOP_IIO): /* Initiate I/O */ + if (NCSF) + break; + Ma = 010; + save_tos(); + start_io(); /* Start an I/O channel */ + break; + + /* Currently not implimented. */ + case VARIANT(WMOP_IFT): /* Test Initiate */ + /* Supports the ablity to start operand during any + cycle, since this simulator does not function the + same as the original hardware, this function can't + really be implemented. + It is currently only used in diagnostics. */ + /* Halt execution if this instruction attempted */ + reason = SCPE_NOFNC; + break; + } + break; + + case 0015: + switch(field) { + case VARIANT(WMOP_LNG): /* Logical Negate */ + A_valid(); + A = (A ^ FWORD); + break; + + case VARIANT(WMOP_LOR): /* Logical Or */ + AB_valid(); + A = (A & FWORD) | B; + BROF = 0; + break; + + case VARIANT(WMOP_LND): /* Logical And */ + AB_valid(); + A = (A & B & FWORD) | (B & FLAG); + BROF = 0; + break; + + case VARIANT(WMOP_LQV): /* Logical Equivalence */ + AB_valid(); + B = ((~(A ^ B) & FWORD)) | (B & FLAG); + AROF = 0; + break; + + case VARIANT(WMOP_MOP): /* Reset Flag bit */ + A_valid(); + A &= ~FLAG; + break; + + case VARIANT(WMOP_MDS): /* Set Flag Bit */ + A_valid(); + A |= FLAG; + break; + } + break; + + case 0021: + switch(field) { + case VARIANT(WMOP_CID): /* 01 Conditional Integer Store Destructive */ + case VARIANT(WMOP_CIN): /* 02 Conditional Integer Store non-destructive */ + case VARIANT(WMOP_ISD): /* 41 Interger Store Destructive */ + case VARIANT(WMOP_ISN): /* 42 Integer Store Non-Destructive */ + case VARIANT(WMOP_STD): /* 04 B Store Destructive */ + case VARIANT(WMOP_SND): /* 10 B Store Non-destructive */ + AB_valid(); + if (A & FLAG) { + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + Ma = CF(A); + } else { + relativeAddr(1); + } + SALF |= VARF; + VARF = 0; + if ((field & 03) != 0) { /* Integer store */ + if ((B & EXPO) != 0) { + /* Check if force to integer */ + if ((A & INTEGR) != 0 || (field & 040) != 0) { + if (mkint()) { + /* Fail if not an integer */ + if (NCSF) + Q |= INT_OVER; + break; + } + } + } + } + AROF = 0; + memory_cycle(015); /* Store B in Ma */ + if (field & 5) /* Destructive store */ + BROF = 0; + break; + + case VARIANT(WMOP_LOD): /* Load */ + A_valid(); + if (A & FLAG) { + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + Ma = CF(A); + } else { + relativeAddr(0); + } + SALF |= VARF; + VARF = 0; + memory_cycle(4); /* Read Ma to A */ + break; + } + break; + + case 0025: + switch(field) { + case VARIANT(WMOP_GEQ): /* B greater than or equal to A */ + case VARIANT(WMOP_GTR): /* B Greater than A */ + case VARIANT(WMOP_NEQ): /* B Not equal to A */ + case VARIANT(WMOP_LEQ): /* B Less Than or Equal to A */ + case VARIANT(WMOP_LSS): /* B Less Than A */ + case VARIANT(WMOP_EQL): /* B Equal A */ + AB_valid(); + f = 0; + i = compare(); + switch(field) { + case VARIANT(WMOP_GEQ): + if ((i & 5) != 0) f = 1; break; + case VARIANT(WMOP_GTR): + if (i == 4) f = 1; break; + case VARIANT(WMOP_NEQ): + if (i != 1) f = 1; break; + case VARIANT(WMOP_LEQ): + if ((i & 3) != 0) f = 1; break; + case VARIANT(WMOP_LSS): + if (i == 2) f = 1; break; + case VARIANT(WMOP_EQL): + if (i == 1) f = 1; break; + } + B = f; + AROF = 0; + break; + + case VARIANT(WMOP_XCH): /* Exchange */ + AB_valid(); + temp = A; + A = B; + B = temp; + break; + + case VARIANT(WMOP_FTF): /* Transfer F Field to F Field */ + AB_valid(); + B &= ~FFIELD; + B |= (A & FFIELD); + AROF = 0; + break; + + case VARIANT(WMOP_FTC): /* Transfer F Field to Core Field */ + AB_valid(); + B &= ~CORE; + B |= (A & FFIELD) >> FFIELD_V; + AROF = 0; + break; + + case VARIANT(WMOP_CTC): /* Transfer Core Field to Core Field */ + AB_valid(); + B &= ~CORE; + B |= (A & CORE); + AROF = 0; + break; + + case VARIANT(WMOP_CTF): /* Transfer Core Field to F Field */ + AB_valid(); + B &= ~FFIELD; + B |= FFIELD & (A << FFIELD_V); + AROF = 0; + break; + + case VARIANT(WMOP_DUP): /* Duplicate */ + if (AROF && BROF) { + B_empty(); + B = A; + BROF = 1; + } else if (AROF || BROF) { + if (AROF) + B = A; + else + A = B; + AROF = BROF = 1; + } else { + A_valid(); /* Make A Valid */ + B = A; + BROF = 1; + } + break; + } + break; + + case 0031: + switch(field) { + case VARIANT(WMOP_BFC): /* Branch Forward Conditional 0231 */ + case VARIANT(WMOP_BBC): /* Branch Backward Conditional 0131 */ + case VARIANT(WMOP_LFC): /* Word Branch Forward Conditional 2231 */ + case VARIANT(WMOP_LBC): /* Word Branch Backward Conditional 2131 */ + AB_valid(); + BROF = 0; + if (B & 1) { + AROF = 0; + break; + } + + /* Fall through */ + case VARIANT(WMOP_BFW): /* Branch Forward Unconditional 4231 */ + case VARIANT(WMOP_BBW): /* Banch Backward Unconditional 4131 */ + case VARIANT(WMOP_LFU): /* Word Branch Forward Unconditional 6231*/ + case VARIANT(WMOP_LBU): /* Word Branch Backward Unconditional 6131 */ + A_valid(); + if (A & FLAG) { + if ((A & PRESENT) == 0) { + if (L == 0) /* Back up to branch word */ + prev_addr(C); + if (NCSF) + Q |= PRES_BIT; + if (field & 020) + BROF = 1; + break; + } + C = CF(A); + L = 0; + } else { + if (L == 0) { /* Back up to branch op */ + prev_addr(C); + L = 3; + } else { + L--; + } + /* Follow logic based on real hardware */ + if ((field & 020) == 0) { /* Syllable branch */ + if (field & 02) { /* Forward */ + if (A & 1) { /* N = 0 */ + L++; C += (L >> 2); L &= 3; + } + A >>= 1; + if (A & 1) { /* Bump L by 2 */ + L+=2; C += (L >> 2); L &= 3; + } + A >>= 1; + } else { /* Backward */ + if (A & 1) { /* N = 0 */ + if (L == 0) { + C--; L = 3; + } else { + L--; + } + } + A >>= 1; + if (A & 1) { /* N = 1 */ + if (L < 2) { + C--; L += 2; + } else { + L -= 2; + } + } + A >>= 1; + } + /* Fix up for backward step */ + if (L == 3) { /* N = 3 */ + C++; L = 0; + } else { + L++; + } + } else { + L = 0; + } + if (field & 02) { /* Forward */ + C += A & 01777; + } else { /* Backward */ + C -= A & 01777; + } + C &= CORE; + } + AROF = 0; + PROF = 0; + break; + + case VARIANT(WMOP_SSN): /* Set Sign Bit */ + A_valid(); + A |= MSIGN; + break; + + case VARIANT(WMOP_CHS): /* Change sign bit */ + A_valid(); + A ^= MSIGN; + break; + + case VARIANT(WMOP_SSP): /* Reset Sign Bit */ + A_valid(); + A &= ~MSIGN; + break; + + case VARIANT(WMOP_TOP): /* Test Flag Bit */ + B_valid(); /* Move result to B */ + if (B & FLAG) + A = 0; + else + A = 1; + AROF = 1; + break; + + case VARIANT(WMOP_TUS): /* Interrogate Peripheral Status */ + A_empty(); + A = iostatus; + AROF = 1; + break; + + case VARIANT(WMOP_TIO): /* Interrogate I/O Channels */ + A_empty(); + A = find_chan(); + AROF = 1; + break; + + case VARIANT(WMOP_FBS): /* Flag Bit Search */ + A_valid(); + Ma = CF(A); + memory_cycle(4); /* Read A */ + while((A & FLAG) == 0) { + next_addr(Ma); + memory_cycle(4); + } + A = toC(Ma) | FLAG | PRESENT; + break; + } + break; + + case 0035: + switch(field) { + case VARIANT(WMOP_BRT): /* Branch Return */ + B_valid(); + if ((B & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + f = set_via_RCW(B, 0, 1); /* Restore registers */ + L = 0; + S = F; + memory_cycle(3); /* Read B */ + prev_addr(S); + set_via_MSCW(B); + BROF = 0; + PROF = 0; + break; + + case VARIANT(WMOP_RTN): /* Return normal 02 */ + case VARIANT(WMOP_RTS): /* Return Special 12 */ + A_valid(); + if (A & FLAG) { + if ((A & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + } + + /* Fall through */ + case VARIANT(WMOP_XIT): /* Exit 04 */ + if (field & 04) + AROF = 0; + BROF = 0; + PROF = 0; + if ((field & 010) == 0) /* normal return & XIT */ + S = F; + memory_cycle(3); /* Restore RCW to B*/ + if ((B & FLAG) == 0) { + if (NCSF) + Q |= FLAG_BIT; + break; + } + f = set_via_RCW(B, 0, 0); /* Restore registers */ + S = F; + BROF = 0; + memory_cycle(3); /* Read B */ + prev_addr(S); + set_via_MSCW(B); + if (MSFF && SALF) { + Ma = F; + do { + /* B = M[FIELD], Ma = B[FIELD]; */ + memory_cycle(6); /* Grab previous MCSW */ + } while(B & SMSFF); + Ma = R | 7; + memory_cycle(015); /* Store B in Ma */ + } + BROF = 0; + if (field & 2) { /* RTS and RTN */ + if (f) + goto desc; + else + goto opdc; + } + break; + } + break; + + case 0041: + A_valid(); + switch(field) { + case VARIANT(WMOP_INX): /* Index */ + AB_valid(); + A = (A & (~CORE)) | ((A + B) & CORE); + BROF = 0; + break; + + case VARIANT(WMOP_COC): /* Construct Operand Call */ + case VARIANT(WMOP_CDC): /* Construct descriptor call */ + AB_valid(); + temp = A; + A = B | FLAG; + B = temp; + if (field & 010) /* COC or CDC */ + goto desc; + else + goto opdc; + /* Not reached */ + break; + + case VARIANT(WMOP_SSF): /* Set or Store S or F registers */ + AB_valid(); + switch( A & 03) { + case 0: /* F => B */ + B = replF(B, F); + break; + case 1: /* S => B */ + B = replC(B, S); + break; + case 2: + F = FF(B); + SALF = 1; + BROF = 0; + break; + case 3: + S = CF(B); + BROF = 0; + break; + } + AROF = 0; + break; + + case VARIANT(WMOP_LLL): /* Link List Look-up */ + AB_valid(); + A = MANT ^ A; + do { + Ma = CF(B); + memory_cycle(5); + temp = (B & MANT) + (A & MANT); + } while ((temp & EXPO) == 0); + A = FLAG | PRESENT | toC(Ma); + break; + + case VARIANT(WMOP_CMN): /* Enter Character Mode In Line */ + A_valid(); /* Make sure TOS is in A. */ + AB_empty(); /* Force A&B to stack */ + B = RCW(0); /* Build RCW word */ + BROF = 1; + B_empty(); /* Save return control word */ + CWMF = 1; + SALF = 1; + MSFF = 0; + B = A; /* A still had copy of TOS */ + AROF = 0; + BROF = 0; + R = 0; + F = S; /* Set F and X */ + X = toF(S); + if (B & FLAG) { + if ((B & PRESENT) == 0) { + if (NCSF) + Q |= PRES_BIT; + break; + } + KV = 0; + } else { + KV = (uint8)((B >> (FFIELD_V - 3)) & 070); + } + S = CF(B); + break; + + case VARIANT(WMOP_MKS): /* Mark Stack */ + AB_empty(); + B = MSCW; + BROF = 1; + B_empty(); + F = S; + if (!MSFF && SALF) { + Ma = R | 7; + memory_cycle(015); /* Store B in Ma */ + } + MSFF = 1; + break; + } + break; + + case 0051: /* Conditional bit field */ + if ((field & 074) == 0) { /* DEL Operator */ + if (AROF) + AROF = 0; + else if (BROF) + BROF = 0; + else + prev_addr(S); + break; + } + AB_valid(); + f = 0; + bit_b = bit_number[GH]; + if (field & 2) + BROF = 0; + for (i = (field >> 2) & 017; i > 0; i--) { + if (B & bit_mask[bit_b-i]) + f = 1; + } + if (f) { + T = (field & 1) ? WMOP_BBW : WMOP_BFW; + TROF = 1; + } else { + AROF = 0; + } + break; + + case WMOP_DIA: /* Dial A XX */ + if (field != 0) + GH = field; + break; + + case WMOP_DIB: /* Dial B XX Upper 6 not Zero */ + if (field != 0) + KV = field; + else { /* Set Variant */ + VARF |= SALF; + SALF = 0; + } + break; + + case WMOP_ISO: /* Variable Field Isolate XX */ + A_valid(); + if ((field & 070) != 0) { + bit_a = bit_number[GH | 07]; /* First Character */ + X = A >> bit_a; /* Get first char */ + X &= 077LL >> (GH & 07); /* Mask first character */ + GH &= 070; /* Clear H */ + while(field > 017) { /* Transfer chars. */ + bit_a -= 6; + X = (X << 6) | ((A >> bit_a) & 077); + field -= 010; + GH += 010; /* Bump G for each char */ + GH &= 070; + } + X >>= (field & 07); /* Align with remaining bits. */ + A = X & MANT; /* Max is 39 bits */ + } + break; + + case WMOP_TRB: /* Transfer Bits XX */ + case WMOP_FCL: /* Compare Field Low XX */ + case WMOP_FCE: /* Compare Field Equal XX */ + AB_valid(); + f = 1; + bit_a = bit_number[GH]; + bit_b = bit_number[KV]; + for(; field > 0 && bit_a >= 0 && bit_b >= 0; + field--, bit_a--, bit_b--) { + int ba, bb; + ba = (bit_mask[bit_a] & A) != 0; + switch(opcode) { + case WMOP_TRB: /* Just copy bit */ + B &= ~bit_mask[bit_b]; + if (ba) + B |= bit_mask[bit_b]; + break; + case WMOP_FCL: /* Loop until unequal */ + case WMOP_FCE: + bb = (bit_mask[bit_b] & B) != 0; + if (ba != bb) { + if (opcode == WMOP_FCL) + f = ba; + else + f = 0; + i = field; + } + break; + } + } + if (opcode != WMOP_TRB) { + A = f; + } else { + AROF = 0; + } + break; + } + } + } + } /* end while */ +/* Simulation halted */ + + return reason; +} + +/* Interval timer routines */ +t_stat +rtc_srv(UNIT * uptr) +{ + int32 t; + + t = sim_rtcn_calb(rtc_tps, TMR_RTC); + sim_activate_after(uptr, 1000000/rtc_tps); + tmxr_poll = t; + RTC++; + if (RTC & 0100) { + IAR |= IRQ_0; + } + RTC &= 077; + return SCPE_OK; +} +/* Reset routine */ + +t_stat +cpu_reset(DEVICE * dptr) +{ + /* Reset CPU 2 first */ + cpu_index = 1; + C = 020; + S = F = R = T = 0; + L = 0; + A = B = X = P = 0; + AROF = BROF = TROF = PROF = NCSF = SALF = CWMF = MSFF = VARF = 0; + GH = KV = Q = 0; + hltf[1] = 0; + P2_run = 0; + /* Reset CPU 1 now */ + cpu_index = 0; + C = 020; + S = F = R = T = IAR = 0; + L = 0; + A = B = X = P = 0; + AROF = BROF = TROF = PROF = NCSF = SALF = CWMF = MSFF = VARF = 0; + GH = KV = Q = 0; + hltf[0] = 0; + P1_run = 0; + + sim_brk_types = sim_brk_dflt = SWMASK('E') | SWMASK('A') | SWMASK('B'); + hst_p = 0; + + sim_register_clock_unit (&cpu_unit[0]); + sim_rtcn_init (cpu_unit[0].wait, TMR_RTC); + sim_activate(&cpu_unit[0], cpu_unit[0].wait) ; + + return SCPE_OK; +} + + +/* Memory examine */ + +t_stat +cpu_ex(t_value * vptr, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE) + return SCPE_NXM; + if (vptr != NULL) + *vptr = (t_value)(M[addr] & (FLAG|FWORD)); + + return SCPE_OK; +} + +/* Memory deposit */ + +t_stat +cpu_dep(t_value val, t_addr addr, UNIT * uptr, int32 sw) +{ + if (addr >= MAXMEMSIZE) + return SCPE_NXM; + M[addr] = val & (FLAG|FWORD); + return SCPE_OK; +} + +t_stat +cpu_show_size(FILE *st, UNIT *uptr, int32 val, void *desc) +{ + fprintf(st, "%dK", MEMSIZE/1024); + return SCPE_OK; +} + +t_stat +cpu_set_size(UNIT * uptr, int32 val, char *cptr, void *desc) +{ + t_uint64 mc = 0; + uint32 i; + + cpu_unit[0].flags &= ~UNIT_MSIZE; + cpu_unit[0].flags |= val; + cpu_unit[1].flags &= ~UNIT_MSIZE; + cpu_unit[1].flags |= val; + val >>= UNIT_V_MSIZE; + val = (val + 1) * 4096; + if ((val < 0) || (val > MAXMEMSIZE)) + return SCPE_ARG; + for (i = val; i < MEMSIZE; i++) + mc |= M[i]; + if ((mc != 0) && (!get_yn("Really truncate memory [N]?", FALSE))) + return SCPE_OK; + MEMSIZE = val; + for (i = MEMSIZE; i < MAXMEMSIZE; i++) + M[i] = 0; + return SCPE_OK; +} + +/* Handle execute history */ + +/* Set history */ +t_stat +cpu_set_hist(UNIT * uptr, int32 val, char *cptr, void *desc) +{ + int32 i, lnt; + t_stat r; + + if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].c = 0; + hst_p = 0; + return SCPE_OK; + } + lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r); + if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; + hst_p = 0; + if (hst_lnt) { + free(hst); + hst_lnt = 0; + hst = NULL; + } + if (lnt) { + hst = calloc(sizeof(struct InstHistory), lnt); + + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } + return SCPE_OK; +} + +/* Show history */ + +t_stat +cpu_show_hist(FILE * st, UNIT * uptr, int32 val, void *desc) +{ + int32 k, di, lnt; + char *cptr = (char *) desc; + t_stat r; + t_value sim_eval; + struct InstHistory *h; + extern void print_opcode(FILE * ofile, t_value val, t_opcode *); + extern t_opcode word_ops[1], char_ops[1]; + char flags[] = "ABCNSMV"; + + if (hst_lnt == 0) + return SCPE_NOFNC; /* enabled? */ + if (cptr) { + lnt = (int32) get_uint(cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } else + lnt = hst_lnt; + di = hst_p - lnt; /* work forward */ + if (di < 0) + di = di + hst_lnt; + fprintf(st, "P CL A B " + " X S F R M GH KV Flags" + " Q Intruction IAR\n\n"); + for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->c & HIST_PC) { /* instruction? */ + int i; + fprintf(st, "%o %05o%o ", h->cpu, h->c & 077777, h->l); + sim_eval = (t_value)h->a_reg; + fprint_sym(st, 0, &sim_eval, &cpu_unit[0], SWMASK('B')); + fputc((h->flags & F_AROF) ? '^': ' ', st); + fputc(' ', st); + sim_eval = (t_value)h->b_reg; + fprint_sym(st, 0, &sim_eval, &cpu_unit[0], SWMASK('B')); + fputc((h->flags & F_BROF) ? '^': ' ', st); + fputc(' ', st); + fprint_val(st, (t_value)h->x_reg, 8, 39, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->s, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->f, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->r, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->ma, 8, 15, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->gh, 8, 6, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->kv, 8, 6, PV_RZRO); + fputc(' ', st); + for(i = 2; i < 8; i++) { + fputc (((1 << i) & h->flags) ? flags[i] : ' ', st); + } + fprint_val(st, h->q, 8, 9, PV_RZRO); + fputc(' ', st); + fprint_val(st, h->op, 8, 12, PV_RZRO); + fputc(' ', st); + print_opcode(st, h->op, + ((h->flags & F_CWMF) ? char_ops : word_ops)); + fputc(' ', st); + fprint_val(st, h->iar, 8, 16, PV_RZRO); + fputc('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ + return SCPE_OK; +} + + +t_stat cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "B5500 CPU\n\n"); + fprintf(st, "The B5500 could support up to two CPU's the second CPU is disabled by\n"); + fprintf(st, "default. Use:\n"); + fprintf(st, " sim> SET CPU1 ENABLE enable second CPU\n"); + fprintf(st, "The primary CPU can't be disabled. Memory is shared between the two\n"); + fprintf(st, "CPU's. Memory can be configured in 4K increments up to 32K total.\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} diff --git a/B5500/b5500_defs.h b/B5500/b5500_defs.h new file mode 100644 index 00000000..64847ead --- /dev/null +++ b/B5500/b5500_defs.h @@ -0,0 +1,570 @@ +/* b5500_defs.h: Burroughs 5500 simulator definitions + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. +*/ + + +#ifndef _B5500_H_ +#define _B5500_H_ + +#include "sim_defs.h" /* simulator defns */ + +/* Definitions for each supported CPU */ + +#define NUM_DEVS_CDR 2 +#define NUM_DEVS_CDP 1 +#define NUM_DEVS_LPR 2 +#define NUM_DEVS_CON 1 +#define NUM_DEVS_DR 2 +#define NUM_DEVS_MT 16 +#define NUM_DEVS_DSK 2 +#define NUM_DEVS_DTC 1 +#define NUM_CHAN 4 +#define MAXMEMSIZE 32768 +#define CHARSPERWORD 8 + +extern t_uint64 M[]; /* Main Memory */ +extern uint16 IAR; /* Interrupt pending register */ +extern uint32 iostatus; /* Active device status register */ +extern uint8 loading; /* System booting flag */ + +/* Memory */ +#define MEMSIZE (cpu_unit[0].capac) /* actual memory size */ +#define MEMMASK (MEMSIZE - 1) /* Memory bits */ + + +/* Debuging controls */ +#define DEBUG_CHAN 0x0000001 /* Show channel fetchs */ +#define DEBUG_TRAP 0x0000002 /* Show CPU Traps */ +#define DEBUG_CMD 0x0000004 /* Show device commands */ +#define DEBUG_DATA 0x0000008 /* Show data transfers */ +#define DEBUG_DETAIL 0x0000010 /* Show details */ +#define DEBUG_EXP 0x0000020 /* Show error conditions */ +#define DEBUG_SNS 0x0000040 /* Shows sense data for 7909 devs */ +#define DEBUG_CTSS 0x0000080 /* Shows CTSS specail instructions */ +#define DEBUG_PROT 0x0000100 /* Protection traps */ + +extern DEBTAB dev_debug[]; + + +/* Returns from device commands */ +#define SCPE_BUSY (1) /* Device is active */ +#define SCPE_NODEV (2) /* No device exists */ + +/* Symbol tables */ +typedef struct _opcode +{ + uint16 op; + uint8 type; + char *name; +} +t_opcode; + +/* I/O Command codes */ +#define IO_RDS 1 /* Read record */ +#define IO_BSR 2 /* Backspace one record */ +#define IO_BSF 3 /* Backspace one file */ +#define IO_WRS 4 /* Write one record */ +#define IO_WEF 5 /* Write eof */ +#define IO_REW 6 /* Rewind */ +#define IO_DRS 7 /* Set unit offline */ +#define IO_SDL 8 /* Set density low */ +#define IO_SDH 9 /* Set density high */ +#define IO_RUN 10 /* Rewind and unload unit */ +#define IO_TRS 11 /* Check it unit ready */ +#define IO_CTL 12 /* Io control device specific */ +#define IO_RDB 13 /* Read backwards */ +#define IO_SKR 14 /* Skip record forward */ +#define IO_ERG 15 /* Erase next records from tape */ + + +t_stat chan_reset(DEVICE *); +t_stat chan_boot(t_uint64); +int find_chan(); +void chan_release(int); +void start_io(); +void chan_set_end(int) ; +void chan_set_parity(int) ; +void chan_set_eof(int) ; +void chan_set_read(int) ; +void chan_set_wcflg(int) ; +void chan_set_gm(int) ; +void chan_set_error(int) ; +void chan_set_notrdy(int) ; +void chan_set_bot(int) ; +void chan_set_eot(int) ; +void chan_set_wrp(int) ; +void chan_set_blank(int) ; +void chan_set_wc(int, uint16); +int chan_write_char(int, uint8 *, int) ; +int chan_read_char(int, uint8 *, int) ; +int chan_read_disk(int, uint8 *, int) ; +int chan_write_drum(int, uint8 *, int) ; +int chan_read_drum(int, uint8 *, int) ; + +extern uint8 parity_table[64]; +extern uint8 mem_to_ascii[64]; +extern const char con_to_ascii[64]; +extern const char ascii_to_con[128]; +extern t_stat fprint_sym(FILE *, t_addr, t_value *, UNIT *, int32); +extern int32 tmxr_poll; + +/* Generic devices common to all */ +extern DEVICE cpu_dev; +extern UNIT cpu_unit[]; +extern REG cpu_reg[]; +extern DEVICE chan_dev; + +/* Global device definitions */ +#if (NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0) +extern DEVICE cdr_dev; +extern t_stat card_cmd(uint16, uint16, uint8, uint16 *); +#endif + +#if (NUM_DEVS_CDP > 0) +extern DEVICE cdp_dev; +#endif + +#if (NUM_DEVS_LPR > 0) +extern DEVICE lpr_dev; +extern t_stat lpr_cmd(uint16, uint16, uint8, uint16 *); +#endif + +#if (NUM_DEVS_CON > 0) +extern DEVICE con_dev; +extern t_stat con_cmd(uint16, uint16, uint8, uint16 *); +#endif + +#if (NUM_DEVS_DTC > 0) +extern DEVICE dtc_dev; +extern t_stat dtc_cmd(uint16, uint16, uint8, uint16 *); +#endif + +#if (NUM_DEVS_DR > 0) +extern DEVICE drm_dev; +extern t_stat drm_cmd(uint16, uint16, uint8, uint16 *, uint8); +#endif + +#if (NUM_DEVS_DSK > 0) +extern DEVICE dsk_dev; +extern t_stat dsk_cmd(uint16, uint16, uint8, uint16 *); +extern DEVICE esu_dev; +#endif + +#if (NUM_DEVS_MT > 0) +extern DEVICE mt_dev; +extern t_stat mt_cmd(uint16, uint16, uint8, uint16 *); +#endif /* NUM_DEVS_MT */ + +/* Character codes */ +#define CHR_ABLANK 000 +#define CHR_MARK CHR_ABLANK +#define CHR_1 001 +#define CHR_2 002 +#define CHR_3 003 +#define CHR_4 004 +#define CHR_5 005 +#define CHR_6 006 +#define CHR_7 007 +#define CHR_8 010 +#define CHR_9 011 +#define CHR_0 012 +#define CHR_EQ 013 +#define CHR_QUOT 014 /* Also @ */ +#define CHR_COL 015 +#define CHR_GT 016 +#define CHR_TRM 017 +#define CHR_BLANK 020 +#define CHR_SLSH 021 +#define CHR_S 022 +#define CHR_T 023 +#define CHR_U 024 +#define CHR_V 025 +#define CHR_W 026 +#define CHR_X 027 +#define CHR_Y 030 +#define CHR_Z 031 +#define CHR_RM 032 +#define CHR_COM 033 +#define CHR_RPARN 034 /* Also % */ +#define CHR_WM 035 +#define CHR_BSLSH 036 +#define CHR_UND 037 +#define CHR_MINUS 040 +#define CHR_J 041 +#define CHR_K 042 +#define CHR_L 043 +#define CHR_M 044 +#define CHR_N 045 +#define CHR_O 046 +#define CHR_P 047 +#define CHR_Q 050 +#define CHR_R 051 +#define CHR_EXPL 052 +#define CHR_DOL 053 +#define CHR_STAR 054 +#define CHR_LBRK 055 +#define CHR_SEMI 056 +#define CHR_CART 057 +#define CHR_PLUS 060 +#define CHR_A 061 +#define CHR_B 062 +#define CHR_C 063 +#define CHR_D 064 +#define CHR_E 065 +#define CHR_F 066 +#define CHR_G 067 +#define CHR_H 070 +#define CHR_I 071 +#define CHR_QUEST 072 +#define CHR_DOT 073 +#define CHR_LPARN 074 /* Also Square */ +#define CHR_RBRAK 075 +#define CHR_LESS 076 +#define CHR_GM 077 + +/* Word mode opcodes */ +#define WMOP_LITC 00000 /* Load literal */ +#define WMOP_OPDC 00002 /* Load operand */ +#define WMOP_DESC 00003 /* Load Descriptor */ +#define WMOP_OPR 00001 /* Operator */ +#define WMOP_DEL 00065 /* Delete top of stack */ +#define WMOP_NOP 00055 /* Nop operation */ +#define WMOP_XRT 00061 /* Set Variant */ +#define WMOP_ADD 00101 /* Add */ +#define WMOP_DLA 00105 /* Double Precision Add */ +#define WMOP_PRL 00111 /* Program Release */ +#define WMOP_LNG 00115 /* Logical Negate */ +#define WMOP_CID 00121 /* Conditional Integer Store Destructive */ +#define WMOP_GEQ 00125 /* WMOP_B greater than or equal to A */ +#define WMOP_BBC 00131 /* Branch Backward Conditional */ +#define WMOP_BRT 00135 /* Branch Return */ +#define WMOP_INX 00141 /* Index */ +#define WMOP_ITI 00211 /* Interrogate interrupt */ +#define WMOP_LOR 00215 /* Logical Or */ +#define WMOP_CIN 00221 /* Conditional Integer Store non-destructive */ +#define WMOP_GTR 00225 /* B Greater than A */ +#define WMOP_BFC 00231 /* Branch Forward Conditional */ +#define WMOP_RTN 00235 /* Return normal */ +#define WMOP_COC 00241 /* Construct Operand Call */ +#define WMOP_SUB 00301 /* Subtract */ +#define WMOP_DLS 00305 /* WMOP_Double Precision Subtract */ +#define WMOP_MUL 00401 /* Multiply */ +#define WMOP_DLM 00405 /* Double Precision Multiply */ +#define WMOP_RTR 00411 /* Read Timer */ +#define WMOP_LND 00415 /* Logical And */ +#define WMOP_STD 00421 /* B Store Destructive */ +#define WMOP_NEQ 00425 /* B Not equal to A */ +#define WMOP_SSN 00431 /* Set Sign Bit */ +#define WMOP_XIT 00435 /* Exit */ +#define WMOP_MKS 00441 /* Mark Stack */ +#define WMOP_DIV 01001 /* Divide */ +#define WMOP_DLD 01005 /* Double Precision Divide */ +#define WMOP_COM 01011 /* Communication operator */ +#define WMOP_LQV 01015 /* Logical Equivalence */ +#define WMOP_SND 01021 /* B Store Non-destructive */ +#define WMOP_XCH 01025 /* Exchange */ +#define WMOP_CHS 01031 /* Change sign bit */ +#define WMOP_RTS 01235 /* Return Special */ +#define WMOP_CDC 01241 /* Construct descriptor call */ +#define WMOP_FTC 01425 /* Transfer F Field to Core Field */ +#define WMOP_MOP 02015 /* Reset Flag bit */ +#define WMOP_LOD 02021 /* Load */ +#define WMOP_DUP 02025 /* Duplicate */ +#define WMOP_TOP 02031 /* Test Flag Bit */ +#define WMOP_IOR 02111 /* I/O Release */ +#define WMOP_LBC 02131 /* Word Branch Backward Conditional */ +#define WMOP_SSF 02141 /* Set or Store S or F registers */ +#define WMOP_HP2 02211 /* Halt P2 */ +#define WMOP_LFC 02231 /* Word Branch Forward Conditional */ +#define WMOP_ZP1 02411 /* Conditional Halt */ +#define WMOP_TUS 02431 /* Interrogate Peripheral Status */ +#define WMOP_LLL 02541 /* Link List Look-up */ +#define WMOP_IDV 03001 /* Integer Divide Integer */ +#define WMOP_SFI 03011 /* Store for Interrupt */ +#define WMOP_SFT 03411 /* Store for Test */ +#define WMOP_FTF 03425 /* Transfer F Field to F Field */ +#define WMOP_MDS 04015 /* Set Flag Bit */ +#define WMOP_IP1 04111 /* Initiate P1 */ +#define WMOP_ISD 04121 /* Interger Store Destructive */ +#define WMOP_LEQ 04125 /* B Less Than or Equal to A */ +#define WMOP_BBW 04131 /* Banch Backward Conditional */ +#define WMOP_IP2 04211 /* Initiate P2 */ +#define WMOP_ISN 04221 /* Integer Store Non-Destructive */ +#define WMOP_LSS 04225 /* B Less Than A */ +#define WMOP_BFW 04231 /* Branch Forward Unconditional */ +#define WMOP_IIO 04411 /* Initiate I/O */ +#define WMOP_EQL 04425 /* B Equal A */ +#define WMOP_SSP 04431 /* Reset Sign Bit */ +#define WMOP_CMN 04441 /* Enter Character Mode In Line */ +#define WMOP_IFT 05111 /* Test Initiate */ +#define WMOP_CTC 05425 /* Transfer Core Field to Core Field */ +#define WMOP_LBU 06131 /* Word Branch Backward Unconditional */ +#define WMOP_LFU 06231 /* Word Branch Forward Unconditional */ +#define WMOP_TIO 06431 /* Interrogate I/O Channels */ +#define WMOP_RDV 07001 /* Remainder Divide */ +#define WMOP_FBS 07031 /* Flag Bit Search */ +#define WMOP_CTF 07425 /* Transfer Core Field to F Field */ +#define WMOP_ISO 00045 /* Variable Field Isolate XX */ +#define WMOP_CBD 00351 /* Non-Zero Field Branch Backward Destructive Xy */ +#define WMOP_CBN 00151 /* Non-Zero Field Branch Backward Non-Destructive Xy */ +#define WMOP_CFD 00251 /* Non-Zero Field Branch Forward Destructive Xy */ +#define WMOP_CFN 00051 /* Non-Zero Field Branch Forward Non-Destructive Xy */ +#define WMOP_DIA 00055 /* Dial A XX */ +#define WMOP_DIB 00061 /* Dial B XX Upper 6 not Zero */ +#define WMOP_TRB 00065 /* Transfer Bits XX */ +#define WMOP_FCL 00071 /* Compare Field Low XX */ +#define WMOP_FCE 00075 /* Compare Field Equal XX */ + +/* Character Mode */ +#define CMOP_EXC 00000 /* CMOP_Exit Character Mode */ +#define CMOP_CMX 00100 /* Exit Character Mode In Line */ +#define CMOP_BSD 00002 /* Skip Bit Destiniation */ +#define CMOP_BSS 00003 /* SKip Bit Source */ +#define CMOP_RDA 00004 /* Recall Destination Address */ +#define CMOP_TRW 00005 /* Transfer Words */ +#define CMOP_SED 00006 /* Set Destination Address */ +#define CMOP_TDA 00007 /* Transfer Destination Address */ +#define CMOP_TBN 00012 /* Transfer Blanks for Non-Numerics */ +#define CMOP_SDA 00014 /* Store Destination Address */ +#define CMOP_SSA 00015 /* Store Source Address */ +#define CMOP_SFD 00016 /* Skip Forward Destination */ +#define CMOP_SRD 00017 /* Skip Reverse Destination */ +#define CMOP_SES 00022 /* Set Source Address */ +#define CMOP_TEQ 00024 /* Test for Equal */ +#define CMOP_TNE 00025 /* Test for Not-Equal */ +#define CMOP_TEG 00026 /* Test for Greater Or Equal */ +#define CMOP_TGR 00027 /* Test For Greater */ +#define CMOP_SRS 00030 /* Skip Reverse Source */ +#define CMOP_SFS 00031 /* Skip Forward Source */ +#define CMOP_TEL 00034 /* Test For Equal or Less */ +#define CMOP_TLS 00035 /* Test For Less */ +#define CMOP_TAN 00036 /* Test for Alphanumeric */ +#define CMOP_BIT 00037 /* Test Bit */ +#define CMOP_INC 00040 /* Increase Tally */ +#define CMOP_STC 00041 /* Store Tally */ +#define CMOP_SEC 00042 /* Set Tally */ +#define CMOP_CRF 00043 /* Call repeat Field */ +#define CMOP_JNC 00044 /* Jump Out Of Loop Conditional */ +#define CMOP_JFC 00045 /* Jump Forward Conditional */ +#define CMOP_JNS 00046 /* Jump out of loop unconditional */ +#define CMOP_JFW 00047 /* Jump Forward Unconditional */ +#define CMOP_RCA 00050 /* Recall Control Address */ +#define CMOP_ENS 00051 /* End Loop */ +#define CMOP_BNS 00052 /* Begin Loop */ +#define CMOP_RSA 00053 /* Recall Source Address */ +#define CMOP_SCA 00054 /* Store Control Address */ +#define CMOP_JRC 00055 /* Jump Reverse Conditional */ +#define CMOP_TSA 00056 /* Transfer Source Address */ +#define CMOP_JRV 00057 /* Jump Reverse Unconditional */ +#define CMOP_CEQ 00060 /* Compare Equal */ +#define CMOP_CNE 00061 /* COmpare for Not Equal */ +#define CMOP_CEG 00062 /* Compare For Greater Or Equal */ +#define CMOP_CGR 00063 /* Compare For Greater */ +#define CMOP_BIS 00064 /* Set Bit */ +#define CMOP_BIR 00065 /* Reet Bit */ +#define CMOP_OCV 00066 /* Output Convert */ +#define CMOP_ICV 00067 /* Input Convert */ +#define CMOP_CEL 00070 /* Compare For Equal or Less */ +#define CMOP_CLS 00071 /* Compare for Less */ +#define CMOP_FSU 00072 /* Field Subtract */ +#define CMOP_FAD 00073 /* Field Add */ +#define CMOP_TRP 00074 /* Transfer Program Characters */ +#define CMOP_TRN 00075 /* Transfer Numeric */ +#define CMOP_TRZ 00076 /* Transfer Zones */ +#define CMOP_TRS 00077 /* Transfer Source Characters */ + +/* Error codes for Q */ /* P1 P2 */ +#define MEM_PARITY 00001 /* 060 040 */ +#define INVALID_ADDR 00002 /* 061 041 */ +#define STK_OVERFL 00004 /* 062 042 */ +#define COM_OPR 00040 /* 064 +00 044 */ +#define PROG_REL 00050 /* 065 +01 045 */ +#define CONT_BIT 00060 /* 066 +02 046 */ +#define PRES_BIT 00070 /* 067 +03 047 */ +#define FLAG_BIT 00100 /* 070 +04 050 */ +#define INDEX_ERROR 00110 /* 071 +05 051 */ +#define EXPO_UNDER 00120 /* 072 +06 052 */ +#define EXPO_OVER 00130 /* 073 +07 053 */ +#define INT_OVER 00140 /* 074 +10 054 */ +#define DIV_ZERO 00150 /* 075 +11 055 */ + +/* Addresses for Interrupts */ +#define INTER_TIME 022 +#define IO_BUSY 023 +#define KEY_REQ 024 +#define PRT1_FINISH 025 +#define PRT2_FINISH 026 +#define IO1_FINISH 027 +#define IO2_FINISH 030 +#define IO3_FINISH 031 +#define IO4_FINISH 032 +#define INQ_REQ 033 +#define SPEC_IRQ1 035 +#define DSK1_RDCHK 036 +#define DSK2_RDCHK 037 +#define PARITY_ERR 060 +#define INVADR_ERR 061 +#define STK_OVR_LOC 062 +#define COM_OPR_LOC 064 +#define PROG_REL_LOC 065 +#define CONT_BIT_LOC 066 +#define PRES_BIT_LOC 067 +#define FLAG_BIT_LOC 070 +#define INDEX_BIT_LOC 071 +#define EXP_UND_LOC 072 +#define EXP_OVR_LOC 073 +#define INT_OVR_LOC 074 +#define DIV_ZER_LOC 075 +#define PARITY_ERR2 040 +#define INVADR_ERR2 041 +#define STK_OVR_LOC2 042 +#define COM_OPR_LOC2 044 +#define PROG_REL_LOC2 045 +#define CONT_BIT_LOC2 046 +#define PRES_BIT_LOC2 047 +#define FLAG_BIT_LOC2 050 +#define INDEX_BIT_LOC2 051 +#define EXP_UND_LOC2 052 +#define EXP_OVR_LOC2 053 +#define INT_OVR_LOC2 054 +#define DIV_ZER_LOC2 055 + +/* IAR BITS */ +#define IAR6 040 /* Set if IRQ from Q */ +#define IAR5 020 /* Set if IRQ from P1 */ +#define IAR4 010 /* Q bit 3 */ +#define IAR3 004 /* Q bit 4 */ +#define IAR2 002 /* Q bit 5 */ +#define IAR1 001 /* Q bit 6 or Q bit 2 */ +#define IAR0 000 /* Q bit 7 or Q bit 1 */ + +#define IRQ_0 000001 /* Interval Timer */ +#define IRQ_1 000002 /* I/O Busy */ +#define IRQ_2 000004 /* Keyboard Request */ +#define IRQ_3 000010 /* Printer 1 Finished */ +#define IRQ_4 000020 /* Printer 2 Finished */ +#define IRQ_5 000040 /* I/O Finish 1 */ +#define IRQ_6 000100 /* I/O Finish 2 */ +#define IRQ_7 000200 /* I/O Finish 3 */ +#define IRQ_10 000400 /* I/O Finish 4 */ +#define IRQ_11 001000 /* P2 Busy */ +#define IRQ_12 002000 /* Inquiry Request */ +#define IRQ_13 004000 /* Special IRQ 1 */ +#define IRQ_14 010000 /* Disk Read Check 1 */ +#define IRQ_15 020000 /* Disk Read Check 2 */ + +/* Masks */ +#define FLAG 04000000000000000LL /* Operand Flag */ +#define FWORD 03777777777777777LL /* Full word mask */ +#define MSIGN 02000000000000000LL /* Operator Word */ +#define ESIGN 01000000000000000LL +#define EXPO 00770000000000000LL +#define EXPO_V 39 +#define MANT 00007777777777777LL +#define NORM 00007000000000000LL +#define ROUND 00004000000000000LL +#define PRESENT 01000000000000000LL /* Oprand Type */ +#define DFLAG 02000000000000000LL /* Descriptor */ +#define WCOUNT 00017770000000000LL +#define WCOUNT_V 30 +#define INTEGR 00000002000000000LL +#define CONTIN 00000001000000000LL +#define CORE 00000000000077777LL +#define RFIELD 00077700000000000LL /* Mark Stack Control Word */ +#define RFIELD_V 27 /* Shift off by 6 bits */ +#define SMSFF 00000020000000000LL +#define SSALF 00000010000000000LL +#define SVARF 00000000100000000LL +#define SCWMF 00000000000100000LL +#define FFIELD 00000007777700000LL +#define FFIELD_V 15 +#define REPFLD 00000770000000000LL +#define REPFLD_V 30 +#define MODEF 00200000000000000LL /* Program Descriptor +FFIELD and CORE */ +#define ARGF 00100000000000000LL +#define PROGF 00400000000000000LL +#define RGH 00340700000000000LL /* Return Control Word +FFIELD and CORE */ +#define RGH_V 33 +#define RKV 00034070000000000LL +#define RKV_V 30 +#define RL 00003000000000000LL /* Save L register */ +#define RL_V 36 +#define LMASK 00000000007777777LL +#define HMASK 00007777770000000LL +#define DEV_DRUM_RD 01000000000000000LL +#define DEVMASK 00760000000000000LL +#define D_MASK 00777777777777777LL +#define DEV_V 40 +#define DEV_WC 00017770000000000LL +#define DEV_WC_V 30 +#define DEV_CMD 00000007777700000LL +#define DEV_CMD_V 15 +#define DEV_INHTRF 00000004000000000LL +#define DEV_XXX 00000002000000000LL +#define DEV_XXY 00000001000000000LL +#define DEV_BIN 00000000400000000LL +#define DEV_BACK 00000000200000000LL +#define DEV_WCFLG 00000000100000000LL +#define DEV_IORD 00000000040000000LL +#define DEV_OPT 00000000007700000LL /* Print Space, Disk Segments */ +#define CORE 00000000000077777LL + +#define DEV_BUSY 00000000000100000LL /* D16 */ +#define DEV_MEMPAR 00000000000200000LL /* D17 */ +#define DEV_NOTRDY 00000000000400000LL /* D18 */ +#define DEV_PARITY 00000000001000000LL /* D19 */ +#define DEV_ERROR 00000000002000000LL /* D20 */ +#define DEV_EOF 00000000004000000LL /* D21 */ +#define DEV_MEMERR 00000000010000000LL /* D22 */ +#define DEV_RESULT 00000000037700000LL +#define DEV_EOT 01000100001000000LL +#define DEV_BOT 01000200001000000LL +#define DEV_BLANK 01000400001000000LL + +#define DRUM1_DEV 004 /* 00100 (4) */ +#define DSK1_DEV 006 /* 00110 (6) */ +#define DRUM2_DEV 010 /* 01000 (8) */ +#define CARD1_DEV 012 /* 01010 (10) */ +#define DSK2_DEV 014 /* 01100 (12) */ +#define CARD2_DEV 016 /* 01110 (14) */ +#define DTC_DEV 020 /* 10000 (16) */ +#define PT1_DEV 022 /* 10010 (20) */ +#define PT2_DEV 024 /* 10100 (22) */ +#define PRT1_DEV 026 /* 10110 (24) */ +#define PRT2_DEV 032 /* 11010 (26) */ +#define SPO_DEV 036 /* 11110 (30) */ +#define DRUM1_FLAG 00000000000200000LL +#define DRUM2_FLAG 00000000000400000LL +#define DSK1_FLAG 00000000001000000LL +#define DSK2_FLAG 00000000002000000LL +#define PRT1_FLAG 00000000004000000LL +#define PRT2_FLAG 00000000010000000LL +#define PUNCH_FLAG 00000000020000000LL +#define CARD1_FLAG 00000000040000000LL +#define CARD2_FLAG 00000000100000000LL +#define SPO_FLAG 00000000200000000LL +#define PTP1_FLAG 00000000400000000LL +#define PTR1_FLAG 00000001000000000LL +#define PTR2_FLAG 00000002000000000LL +#define PTP2_FLAG 00000004000000000LL +#define DTC_FLAG 00000010000000000LL + +#endif /* _B5500_H_ */ diff --git a/B5500/b5500_dk.c b/B5500/b5500_dk.c new file mode 100644 index 00000000..61a329ef --- /dev/null +++ b/B5500/b5500_dk.c @@ -0,0 +1,578 @@ +/* b5500_dk.c: Burrioughs 5500 Disk controller + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "b5500_defs.h" + +#if (NUM_DEVS_DSK > 0) + + +/* in u3 is device address */ +/* in u4 is current buffer position */ +/* in u5 Bits 30-16 of W */ +#define URCSTA_SKIP 000017 /* Skip mask */ +#define URCSTA_SINGLE 000020 /* Single space skip. */ +#define URCSTA_DOUBLE 000040 /* Double space skip */ +#define URCSTA_READ 000400 /* Read flag */ +#define URCSTA_WC 001000 /* Use word count */ +#define URCSTA_DIRECT 002000 /* Direction, Long line */ +#define URCSTA_BINARY 004000 /* Binary transfer */ +#define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */ + +#define DK_CHAN 0000003 /* Channel number */ +#define DK_CTRL 0000004 /* Disk controller unit attached too */ +#define DK_WC 0000010 /* Use word count */ +#define DK_BSY 0000020 /* Drive is busy. */ +#define DK_RD 0000040 /* Executing a read command */ +#define DK_WR 0000100 /* Executing a write command */ +#define DK_RDCK 0000200 /* Executing a read check command */ +#define DK_ADDR 0000400 /* Drive has an address. */ +#define DK_BIN 0001000 /* Binary mode */ +#define DK_WCZERO 0002000 /* Word count Zero */ +#define DK_SECMASK 0770000 /* Number of segments to transfer */ +#define DK_SECT 0010000 /* One segment */ +#define DK_SEC_SIZE 240 /* Sector size */ +#define DK_MAXSEGS 200000 /* Max segments for MOD I ESU */ +#define DK_MAXSEGS2 400000 /* Max segments for MOD IB ESU */ +#define DFX_V (UNIT_V_UF + 1) +#define MODIB_V (UNIT_V_UF + 2) +#define DFX (1 << DFX_V) +#define MODIB (1 << MODIB_V) + +t_stat dsk_cmd(uint16, uint16, uint8, uint16 *); +t_stat dsk_srv(UNIT *); +t_stat dsk_boot(int32, DEVICE *); +t_stat dsk_help (FILE *, DEVICE *, UNIT *, int32, const char *); +const char *dsk_description (DEVICE *); +t_stat esu_srv(UNIT *); +t_stat esu_attach(UNIT *, char *); +t_stat esu_detach(UNIT *); +t_stat esu_help (FILE *, DEVICE *, UNIT *, int32, const char *); +const char *esu_description (DEVICE *); + +uint8 dsk_buffer[NUM_DEVS_DSK][DK_SEC_SIZE]; +t_stat set_mod(UNIT *uptr, int32 val, char *cptr, + void *desc); + +#define ESU_TYPE UDATA(&esu_srv, UNIT_ATTABLE+UNIT_DISABLE+ \ + UNIT_FIX, DK_SEC_SIZE * DK_MAXSEGS) + +UNIT esu_unit[] = { + {ESU_TYPE, DK_MAXSEGS}, /* 0 */ + {ESU_TYPE, DK_MAXSEGS}, /* 1 */ + {ESU_TYPE, DK_MAXSEGS}, /* 2 */ + {ESU_TYPE, DK_MAXSEGS}, /* 3 */ + {ESU_TYPE, DK_MAXSEGS}, /* 4 */ + {ESU_TYPE, DK_MAXSEGS}, /* 5 */ + {ESU_TYPE, DK_MAXSEGS}, /* 6 */ + {ESU_TYPE, DK_MAXSEGS}, /* 7 */ + {ESU_TYPE, DK_MAXSEGS}, /* 8 */ + {ESU_TYPE, DK_MAXSEGS}, /* 9 */ + {ESU_TYPE, DK_MAXSEGS}, /* 10 */ + {ESU_TYPE, DK_MAXSEGS}, /* 11 */ + {ESU_TYPE, DK_MAXSEGS}, /* 12 */ + {ESU_TYPE, DK_MAXSEGS}, /* 13 */ + {ESU_TYPE, DK_MAXSEGS}, /* 14 */ + {ESU_TYPE, DK_MAXSEGS}, /* 15 */ + {ESU_TYPE, DK_MAXSEGS}, /* 16 */ + {ESU_TYPE, DK_MAXSEGS}, /* 17 */ + {ESU_TYPE, DK_MAXSEGS}, /* 18 */ + {ESU_TYPE, DK_MAXSEGS}, /* 19 */ +}; + +MTAB esu_mod[] = { + {MODIB, 0, "MODI", "MODI", &set_mod, NULL, NULL, + "Sets ESU to Fast Mod I drive"}, + {MODIB, MODIB, "MODIB", "MODIB", &set_mod, NULL, NULL, + "Sets ESU to Slow Mod IB drive"}, + {0} +}; + + +DEVICE esu_dev = { + "ESU", esu_unit, NULL, esu_mod, + 20, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &esu_attach, &esu_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &esu_help, NULL, NULL, &esu_description +}; + +MTAB dsk_mod[] = { + {DFX, 0, NULL, "NODFX", NULL, NULL, NULL, + "Disables drive sharing, use only on DK1"}, + {DFX, DFX, "DFX", "DFX", NULL, NULL, NULL, + "Enables drive sharing, use only on DK1"}, + {0} +}; + + +UNIT dsk_unit[] = { + {UDATA(&dsk_srv, UNIT_DISABLE, 0)}, /* DKA */ + {UDATA(&dsk_srv, UNIT_DIS | UNIT_DISABLE, 0)}, /* DKB */ +}; + +DEVICE dsk_dev = { + "DK", dsk_unit, NULL, dsk_mod, + NUM_DEVS_DSK, 8, 15, 1, 8, 8, + NULL, NULL, NULL, &dsk_boot, NULL, NULL, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dsk_help, NULL, NULL, &dsk_description +}; + + + +/* Start off a disk command */ +t_stat dsk_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr; + int u = (dev==DSK1_DEV)? 0: 1; + + uptr = &dsk_unit[u]; + + /* If unit disabled return error */ + if (uptr->flags & UNIT_DIS) + return SCPE_NODEV; + + /* Check if drive is ready to recieve a command */ + if ((uptr->u5 & DK_BSY)) + return SCPE_BUSY; + + uptr->u5 = chan|DK_BSY; + if (dev == DSK2_DEV) + uptr->u5 |= DK_CTRL; + uptr->u5 |= (cmd & 077) << 12; + if (cmd & URCSTA_INHIBIT) + uptr->u5 |= DK_RDCK; + else if (cmd & URCSTA_READ) + uptr->u5 |= DK_RD; + else + uptr->u5 |= DK_WR; + if (cmd & URCSTA_WC) { + uptr->u5 |= DK_WC; + if (*wc == 0) + uptr->u5 |= DK_WCZERO; + } + if (cmd & URCSTA_BINARY) + uptr->u5 |= DK_BIN; + if (loading) { + uptr->u4 = 1; + uptr->u3 = 0; + } else { + uptr->u5 |= DK_ADDR; + } + sim_activate(uptr, 100); + return SCPE_OK; +} + + +/* Handle processing disk controller commands */ +t_stat dsk_srv(UNIT * uptr) +{ + int chan = uptr->u5 & DK_CHAN; + DEVICE *dptr = find_dev_from_unit(uptr); + int i; + int addr; + uint8 abuf[8]; /* x-esu-disk-track-segment */ + int u = uptr - dsk_unit; + int esu; + UNIT *eptr; + + if ((uptr->u5 & DK_BSY) == 0) + return SCPE_OK; + + /* Read in first word, which a address. */ + /* Note special read routine since address is not included + in the word count */ + if (uptr->u5 & DK_ADDR) { + /* Read in 8 characters which are the address */ + for (i = 0; i < 8; i++) { + if (chan_read_disk(chan, &abuf[i], 0)) + break; + abuf[i] &= 017; /* Mask zone out */ + if (abuf[i] == 012) /* Zero to zero */ + abuf[i] = 0; + } + + /* extract ESU and Address */ + esu = abuf[1]; + addr = 0; + for (i = 2; i < 8; i++) { + addr = (addr * 10) + abuf[i]; + } + uptr->u5 &= ~DK_ADDR; + uptr->u4 = addr; + + /* Map to ESU */ + if (u && (dsk_unit[u].flags & DFX) == 0) + esu += 10; + sim_debug(DEBUG_DETAIL, dptr, "Disk access %d %s %02o %d,%d\n\r", u, + (uptr->u5 & DK_RDCK) ? "rcheck" : + (uptr->u5 & DK_RD) ? "read" : + (uptr->u5 & DK_WR)? "write" : "nop", (uptr->u5 >> 9) & 077, + esu, addr); + + uptr->u3 = esu; + eptr = &esu_unit[uptr->u3]; + /* Check if valid */ + if ((eptr->flags & UNIT_DIS) || (eptr->flags & UNIT_ATT) == 0) { + /* Set not ready and end channel */ + chan_set_notrdy(chan); + uptr->u5 = 0; + return SCPE_OK; + } + + /* Check if Read Check or Write Check */ + if ((uptr->u5 & (DK_WCZERO|DK_WC|DK_SECMASK)) == (DK_WCZERO|DK_WC)) { + if (uptr->u4 >= eptr->wait) + chan_set_eof(chan); + + if (uptr->u5 & DK_WR) { + sim_debug(DEBUG_DETAIL, dptr, "Disk write int %d %d %o\n\r", + uptr->u3, uptr->u4, uptr->u5); + } + if (uptr->u5 & DK_RD) { + sim_debug(DEBUG_DETAIL, dptr, "Disk read int %d %d %o\n\r", + uptr->u3, uptr->u4, uptr->u5); + if (eptr->flags & MODIB) + chan_set_error(chan); + } + chan_set_end(chan); + uptr->u5 = 0; + return SCPE_OK; + } + + sim_activate(uptr, 5000); + return SCPE_OK; + } + + /* Kick off actual transfer to ESU */ + if (((uptr->u5 & DK_ADDR) == 0) && + ((uptr->u5 & (DK_RDCK|DK_RD|DK_WR)) != 0)) { + eptr = &esu_unit[uptr->u3]; + + /* Wait until unit is ready for new access */ + if ((eptr->u5 & DK_BSY) == 0) { + + eptr->u3 = (uptr->u5 & DK_WR) ? 0 : DK_SEC_SIZE; + eptr->u4 = uptr->u4; /* Disk address */ + eptr->u5 = uptr->u5; /* Command */ + if (uptr->u5 & DK_RDCK) { + uptr->u5 = 0; + chan_set_end(chan); + } else + uptr->u5 &= ~(DK_RDCK|DK_RD|DK_WR); + sim_activate(eptr, 500); + return SCPE_OK; + } + sim_activate(uptr, 100); + } + return SCPE_OK; +} + +void esu_set_end(UNIT *uptr, int err) { + int chan = uptr->u5 & DK_CHAN; + int dsk = ((uptr->u5 & DK_CTRL) != 0); + DEVICE *dptr = find_dev_from_unit(uptr); + + sim_debug(DEBUG_DETAIL, dptr, "Disk done %d %d %o\n\r", uptr->u3, + uptr->u4, uptr->u5); + if (err) + chan_set_error(chan); + uptr->u5 = 0; + dsk_unit[dsk].u5 = 0; + chan_set_end(chan); +} + +/* Handle processing esu controller commands */ +t_stat esu_srv(UNIT * uptr) +{ + int chan = uptr->u5 & DK_CHAN; + DEVICE *dptr = find_dev_from_unit(uptr); + int u = uptr - esu_unit; + int dsk = ((uptr->u5 & DK_CTRL) != 0); + int wc; + + + /* Process for each unit */ + if (uptr->u5 & DK_RD) { + /* Check if at start of segment */ + if (uptr->u3 >= DK_SEC_SIZE) { + int da = (uptr->u4 * DK_SEC_SIZE); + + /* Check if end of operation */ + if ((uptr->u5 & (DK_SECMASK)) == 0) { + esu_set_end(uptr, 0); + return SCPE_OK; + } + + /* Check if over end of disk */ + if (uptr->u4 >= uptr->wait) { + sim_debug(DEBUG_DETAIL, dptr, "Disk read over %d %d %o\n\r", + uptr->u3, uptr->u4, uptr->u5); + chan_set_eof(chan); + esu_set_end(uptr, 0); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "Disk read %d %d %d %o %d\n\r", + u,uptr->u3, uptr->u4, uptr->u5, da); + + if (sim_fseek(uptr->fileref, da, SEEK_SET) < 0) { + esu_set_end(uptr, 1); + return SCPE_OK; + } + wc = sim_fread(&dsk_buffer[dsk][0], 1, DK_SEC_SIZE, + uptr->fileref); + for (; wc < DK_SEC_SIZE; wc++) + dsk_buffer[dsk][wc] = (uptr->u5 & DK_BIN) ? 0 :020; + uptr->u3 = 0; + uptr->u4++; /* Advance disk address */ + uptr->u5 -= DK_SECT; + } + /* Transfer one Character */ + if (chan_write_char(chan, &dsk_buffer[dsk][uptr->u3], 0)) { + esu_set_end(uptr, 0); + return SCPE_OK; + } + uptr->u3++; + } + + if (uptr->u5 & DK_RDCK) { + if (uptr->u3 >= DK_SEC_SIZE) { + + /* Check if over end of disk */ + if (uptr->u4 >= uptr->wait) { + sim_debug(DEBUG_DETAIL, dptr, "Disk rdchk over %d %d %o\n\r", + uptr->u3, uptr->u4, uptr->u5); + uptr->u5 = 0; + IAR |= IRQ_14 << dsk; + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "Disk rdchk %d %d %d %o\n\r", u, + uptr->u3, uptr->u4, uptr->u5); + + uptr->u4++; /* Advance disk address */ + uptr->u5 -= DK_SECT; + uptr->u3 = 0; + + /* Check if end of operation */ + if ((uptr->u5 & (DK_SECMASK)) == 0) { + uptr->u5 = 0; + IAR |= IRQ_14 << dsk; + return SCPE_OK; + } + } + /* Check if at end of segment */ + uptr->u3++; + } + + /* Process for each unit */ + if (uptr->u5 & DK_WR) { + /* Check if end of operation */ + if ((uptr->u5 & (DK_SECMASK)) == 0) { + esu_set_end(uptr, 0); + return SCPE_OK; + } + + /* Transfer one Character */ + if (chan_read_char(chan, &dsk_buffer[dsk][uptr->u3], 0)) { + if (uptr->u3 != 0) { + while (uptr->u3 < DK_SEC_SIZE) + dsk_buffer[dsk][uptr->u3++] = (uptr->u5 & DK_BIN) ? 0 :020; + } + } + uptr->u3++; + + /* Check if at end of segment */ + if (uptr->u3 >= DK_SEC_SIZE) { + int da = (uptr->u4 * DK_SEC_SIZE); + + /* Check if over end of disk */ + if (uptr->u4 >= uptr->wait) { + sim_debug(DEBUG_DETAIL, dptr, "Disk write over %d %d %o\n\r", + uptr->u3, uptr->u4, uptr->u5); + chan_set_eof(chan); + esu_set_end(uptr, 0); + return SCPE_OK; + } + + sim_debug(DEBUG_DETAIL, dptr, "Disk write %d %d %d %o %d\n\r", + u, uptr->u3, uptr->u4, uptr->u5, da); + if (sim_fseek(uptr->fileref, da, SEEK_SET) < 0) { + esu_set_end(uptr, 1); + return SCPE_OK; + } + + wc = sim_fwrite(&dsk_buffer[dsk][0], 1, DK_SEC_SIZE, + uptr->fileref); + if (wc != DK_SEC_SIZE) { + esu_set_end(uptr, 1); + return SCPE_OK; + } + uptr->u3 = 0; + uptr->u4++; /* Advance disk address */ + uptr->u5 -= DK_SECT; + } + } + sim_activate(uptr, (uptr->flags & MODIB) ? 200 :100); + return SCPE_OK; +} + +t_stat +set_mod(UNIT *uptr, int32 val, char *cptr, void *desc) { + if (uptr == NULL) return SCPE_IERR; + if (val == MODIB) + uptr->wait = DK_MAXSEGS2; + else + uptr->wait = DK_MAXSEGS; + uptr->capac = DK_SEC_SIZE * uptr->wait; + return SCPE_OK; +} + + +/* Boot from given device */ +t_stat +dsk_boot(int32 unit_num, DEVICE * dptr) +{ + int dev = (unit_num)? DSK2_DEV:DSK1_DEV; + t_uint64 desc; + int i; + + for(i = 0; i < 20; i++) + esu_unit[i].u5 = 0; + + desc = (((t_uint64)dev)<= 10 || (dsk_unit[1].flags & DFX) != 0) { + iostatus |= DSK2_FLAG; + } + return SCPE_OK; +} + +t_stat +esu_detach(UNIT * uptr) +{ + t_stat r; + int u = uptr-esu_unit; + int i, mask, lim; + + if ((r = detach_unit(uptr)) != SCPE_OK) + return r; + /* Determine which controller */ + if (u < 10) { + mask = DSK1_FLAG; + /* If DFX, then both controllers */ + if ((dsk_unit[1].flags & DFX) != 0) + mask |= DSK2_FLAG; + lim = 10; + i = 0; + } else { + /* If DFX, then drive not attached */ + if ((dsk_unit[1].flags & DFX) != 0) + return r; + mask = DSK2_FLAG; + lim = 20; + i = 10; + } + /* Scan to see if any disks still attached */ + while (i < lim) { + if (esu_unit[i].flags & UNIT_ATT) + return r; /* Something still there */ + i++; + } + /* There are no longer any drives attached to + this controller */ + iostatus &= ~mask; + return r; +} + +t_stat +dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B5470 Disk Controller\n\n"); + fprintf (st, "By default the second disk controller is not enabled.\n\n"); + fprintf (st, " sim> SET DK1 ENABLE to enable second disk controller for use\n"); + fprintf (st, "The B5500 could have up to two disk controllers that could talk\n"); + fprintf (st, "to up to 10 ESU. Each ESU held up to 5 storage units. By uses of\n"); + fprintf (st, "a exchange unit (DFX), the second controller could talk to the\n"); + fprintf (st, "same drives as the first controller. To use the second disk controller\n"); + fprintf (st, "to share the same drives as the first (after enabling DK1):\n\n"); + fprintf (st, " sim> SET DK1 DFX enable disk exchange\n\n"); + fprintf (st, "If you want to support more then 10 ESU units you will first\n"); + fprintf (st, "need to generate a new version of MCP without the DFX option\n"); + fprintf (st, "for MCP XV you also need to SET DKBNODFX TRUE when building the\n"); + fprintf (st, "system file.\n"); + fprintf (st, "ESU units 0-9 attach to DK0, or DK1 if DFX\n"); + fprintf (st, "ESU units 10-19 attach to DK1 only\n\n"); + fprintf (st, "The DK unit supports the BOOT command.\n\n"); + fprint_set_help (st, dptr) ; + fprint_show_help (st, dptr) ; + return SCPE_OK; +} + +const char * +dsk_description (DEVICE *dptr) +{ + return "B5470 disk controller module"; +} + +t_stat +esu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B471 ESU with 5 B457 storage units\n\n"); + fprintf (st, "Each ESU unit represents the electronics unit and 5 storage units\n"); + fprintf (st, "MOD I units could handle about 48 million characters.\n"); + fprintf (st, "MOD IB units could handle about 96 million characters.\n"); + fprintf (st, "MOD IB units operated at half the speed of MOD I units.\n"); + fprintf (st, "ESU units can be added to a system after it has been booted,\n"); + fprintf (st, "however they can't be removed. The configuration of disks must\n"); + fprintf (st, "be the same each time the same system is booted.\n"); + fprintf (st, "To use larger slower drives do:\n"); + fprintf (st, " sim> SET ESUn MODIB before the unit is attached\n"); + fprintf (st, "To use smaller faster drives do (default):\n"); + fprintf (st, " sim> SET ESUn MODI before the unit is attached\n\n"); + fprint_set_help (st, dptr) ; + fprint_show_help (st, dptr) ; + return SCPE_OK; +} + +const char * +esu_description (DEVICE *dptr) +{ + return "B471 electrontics unit and 5 B457 storage units."; +} +#endif + + diff --git a/B5500/b5500_dr.c b/B5500/b5500_dr.c new file mode 100644 index 00000000..0dbc5a29 --- /dev/null +++ b/B5500/b5500_dr.c @@ -0,0 +1,258 @@ +/* b5500_dr.c: Burrioughs 5500 Drum controller + + Copyright (c) 2015, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "b5500_defs.h" + +#if (NUM_DEVS_DR > 0) + +#define UNIT_DR UNIT_ATTABLE | UNIT_DISABLE | UNIT_FIX | \ + UNIT_BUFABLE | UNIT_MUSTBUF + + +/* in u3 is device address */ +/* in u4 is current address */ +/* in u5 Bits 30-16 of W */ +#define DR_CHAN 000003 /* Channel number */ +#define DR_RD 000004 /* Executing a read command */ +#define DR_WR 000010 /* Executing a write command */ +#define DR_RDY 000040 /* Device Ready */ + +#define AUXMEM (1 << UNIT_V_UF) + +t_stat drm_srv(UNIT *); +t_stat drm_boot(int32, DEVICE *); +t_stat drm_attach(UNIT *, char *); +t_stat drm_detach(UNIT *); +t_stat set_drum(UNIT * uptr, int32 val, char *cptr, + void *desc); +t_stat set_auxmem(UNIT * uptr, int32 val, char *cptr, + void *desc); +t_stat drm_help (FILE *, DEVICE *, UNIT *, int32, const char *); +const char *drm_description (DEVICE *); + + +MTAB drm_mod[] = { + {AUXMEM, 0, "DRUM", "DRUM", &set_drum, NULL, "Device is drum"}, + {AUXMEM, AUXMEM, "AUXMEM", "AUXMEM", &set_auxmem, NULL, "Device is memory unit"}, + {0} +}; + + +UNIT drm_unit[] = { + {UDATA(&drm_srv, UNIT_DR, 32*1024)}, /* DRA */ + {UDATA(&drm_srv, UNIT_DR, 32*1024)}, /* DRB */ +}; + +DEVICE drm_dev = { + "DR", drm_unit, NULL, drm_mod, + NUM_DEVS_DR, 8, 15, 1, 8, 64, + NULL, NULL, NULL, &drm_boot, &drm_attach, &drm_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &drm_help, NULL, NULL, + &drm_description +}; + + + + +/* Start off a disk command */ +t_stat drm_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc, uint8 rd_flg) +{ + UNIT *uptr; + int u = (dev==DRUM1_DEV)? 0: 1; + + uptr = &drm_unit[u]; + + /* If unit disabled return error */ + if (uptr->flags & UNIT_DIS) { + return SCPE_NODEV; + } + + if ((uptr->flags & (UNIT_BUF)) == 0) { + sim_debug(DEBUG_CMD, &drm_dev, "Drum not buffered\n\r"); + return SCPE_UNATT; + } + + /* Check if drive is ready to recieve a command */ + if ((uptr->u5 & DR_RDY) == 0) + return SCPE_BUSY; + + uptr->u5 = chan; + if (rd_flg) + uptr->u5 |= DR_RD; + else + uptr->u5 |= DR_WR; + uptr->u4 = cmd << 3; + sim_debug(DEBUG_CMD, &drm_dev, "Drum access %s %06o\n\r", + (uptr->u5 & DR_RD) ? "read" : "write", uptr->u4); + sim_activate(uptr, 100); + return SCPE_OK; +} + + +/* Handle processing disk controller commands */ +t_stat drm_srv(UNIT * uptr) +{ + int chan = uptr->u5 & DR_CHAN; + uint8 *ch = &(((uint8 *)uptr->filebuf)[uptr->u4]); + + + /* Process for each unit */ + if (uptr->u5 & DR_RD) { + /* Transfer one Character */ + if (chan_write_drum(chan, ch, 0)) { + uptr->u5 = DR_RDY; + chan_set_end(chan); + return SCPE_OK; + } + uptr->u4++; + if (uptr->u4 > ((int32)uptr->capac << 3)) { + sim_debug(DEBUG_CMD, &drm_dev, "Drum overrun\n\r"); + uptr->u5 = DR_RDY; + chan_set_error(chan); + chan_set_end(chan); + return SCPE_OK; + } + sim_activate(uptr, 40); + } + + /* Process for each unit */ + if (uptr->u5 & DR_WR) { + /* Transfer one Character */ + if (chan_read_drum(chan, ch, 0)) { + uptr->u5 = DR_RDY; + chan_set_end(chan); + return SCPE_OK; + } + uptr->u4++; + if (uptr->u4 > ((int32)uptr->capac << 3)) { + sim_debug(DEBUG_CMD, &drm_dev, "Drum overrun\n\r"); + uptr->u5 = DR_RDY; + chan_set_error(chan); + chan_set_end(chan); + return SCPE_OK; + } + sim_activate(uptr, 40); + } + + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +drm_boot(int32 unit_num, DEVICE * dptr) +{ + int dev = (unit_num)? DRUM2_DEV:DRUM1_DEV; + t_uint64 desc; + + desc = (((t_uint64)dev)<u5 |= DR_RDY; + uptr->hwmark = uptr->capac; + if (u) + iostatus |= DRUM2_FLAG; + else + iostatus |= DRUM1_FLAG; + return SCPE_OK; +} + +t_stat +drm_detach(UNIT * uptr) +{ + t_stat r; + int u = uptr - drm_unit; + if ((r = detach_unit(uptr)) != SCPE_OK) + return r; + uptr->u5 = 0; + if (u) + iostatus &= ~DRUM2_FLAG; + else + iostatus &= ~DRUM1_FLAG; + return SCPE_OK; +} + +t_stat +set_drum(UNIT * uptr, int32 val, char *cptr, void *desc) { + if ((uptr->flags & AUXMEM) == 0) + return SCPE_OK; + if (uptr->flags & UNIT_ATT) + drm_detach(uptr); + uptr->flags |= UNIT_ATTABLE; + return SCPE_OK; +} + +t_stat +set_auxmem(UNIT * uptr, int32 val, char *cptr, void *desc) { + int u = uptr - drm_unit; + + if (uptr->flags & AUXMEM) + return SCPE_OK; + if (uptr->flags & UNIT_ATT) + detach_unit(uptr); + uptr->flags &= ~UNIT_ATTABLE; + if (uptr->filebuf == 0) { + uptr->filebuf = calloc(uptr->capac, 8); + uptr->flags |= UNIT_BUF; + } + uptr->u5 = DR_RDY; + if (u) + iostatus |= DRUM2_FLAG; + else + iostatus |= DRUM1_FLAG; + + return SCPE_OK; +} + +t_stat +drm_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B430 Magnetic Drum or B6500 memory module\n\n"); + fprintf (st, "There are up to two drum units DR0 and DR1. These can either\n"); + fprintf (st, "be attached to a file or set to AUXMEM. Setting to AUXMEM causes\n"); + fprintf (st, "them to exist only during the given sim run. Setting back to DRUM\n"); + fprintf (st, "will clear whatever was stored on the drum. If the device is set\n"); + fprintf (st, "to DRUM it must be attached to a file which it will buffer until\n"); + fprintf (st, "the unit is detached, or the sim exits. MCP must be configured to\n"); + fprintf (st, "the drum\n\n"); + fprint_set_help (st, dptr) ; + fprint_show_help (st, dptr) ; + return SCPE_OK; +} + +const char * +drm_description (DEVICE *dptr) +{ + return "B430 Magnetic Drum or B6500 memory module"; +} +#endif diff --git a/B5500/b5500_dtc.c b/B5500/b5500_dtc.c new file mode 100644 index 00000000..bdf61550 --- /dev/null +++ b/B5500/b5500_dtc.c @@ -0,0 +1,895 @@ +/* b5500_dtc.c: Burrioughs 5500 Data Communications + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "b5500_defs.h" +#include "sim_timer.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#if (NUM_DEVS_DTC > 0) + +#define UNIT_DTC UNIT_ATTABLE | UNIT_DISABLE | UNIT_IDLE + +#define DTC_MLINES 32 /* mux lines */ +#define DTC_TLINES 8 +#define DTC_BUFSIZ 112 /* max chan transfer */ + + +#define DTCSTA_READ 000400 /* Read flag */ +#define DTCSTA_BINARY 004000 /* Bypass translation */ +#define DTCSTA_INHIBIT 040000 /* Interrogate or Read/Write */ + +/* Flags in WC Field */ +#define DTCSTA_TTU 0740 /* TTU number */ +#define DTCSTA_GM 0020 /* Ignore GM on transfer */ +#define DTCSTA_BUF 0017 /* Buffer Number */ + +/* Interrogate + D28 - Busy DEV_ERROR + D27 - Write Ready DEV_EOF + D24 - Read Ready DEV_IORD */ +/* Abnormal flag = DEV_WCFLG */ +/* Buffer full/GM flag = D25 */ +/* Buffer wrong state = DEV_EOF D27 */ +/* Buffer Busy = DEV_ERROR D28 */ +/* DTC not ready or buffer, DEV_NOTRDY D30 */ +/* in u3 is device address */ +/* in u4 is current address */ +/* in u5 Line number */ +#define DTC_CHAN 000003 /* Channel number */ +#define DTC_RD 000004 /* Executing a read command */ +#define DTC_WR 000010 /* Executing a write command */ +#define DTC_INQ 000020 /* Executing an interragte command */ +#define DTC_RDY 000040 /* Device Ready */ +#define DTC_BIN 000100 /* Transfer in Binary mode */ +#define DTC_IGNGM 000200 /* Ignore GM on transfer */ + +#define BufNotReady 0 /* Device not connected */ +#define BufIdle 1 /* Buffer in Idle state */ +#define BufInputBusy 2 /* Buffer being filled */ +#define BufReadRdy 3 /* Buffer ready to read */ +#define BufWrite 4 /* Buffer writing */ +#define BufWriteRdy 5 /* Buffer ready for write */ +#define BufOutBusy 6 /* Buffer outputing */ +#define BufRead 7 /* Buffer reading */ +#define BufSMASK 7 /* Buffer state mask */ +#define BufAbnormal 010 /* Abnornmal flag */ +#define BufGM 020 /* Buffer term with GM */ +#define BufIRQ 040 /* Buffer ready */ + +/* Not connected line: + BufNotReady. + + Write: + BufNotReady -> 0x34 (EOF,ERROR,NR) + BufIdle -> BufWrite (set GM if set.) + BufReadReady -> 0x20 (EOF). + BufInputBusy, BufWrite -> 0x30 + -> 0x34 (EOF,ERROR) + BufWriteRdy -> BufWrite. + + Write Done: + BufOutBusy. + + Read: + BufNotReady -> 0x34 (EOF,ERROR,NR) + BufIdle -> 0x34 (EOF,ERROR,NR) + BufInputBusy, BufOutBusy -> 0x30 (EOF,ERROR) + BufReadRdy -> return buffer. -> BufIdle + + Interogate: + return BufWriteRdy/BufWriteFull + return BufReadRdy. + + Recieve Char: + + Connect: + State BufWriteRdy. + + Output Done: + BufGM -> BufIdle + -> BufWriteRdy +*/ + +/* Translate chars + + output: + ! -> LF. + < -> RO. + > -> X-OFF + } -> Disconnect line + ~ -> End of message. + + input: + ~/_/CR -> End of message. + BufReadRdy, IRQ. + Back up one char. + !/ -> Disconnect insert } + BufReadRdy, IRQ. + ^B -> Clear input. + BufIdle + ^E -> set abnormal, buffer to BufWriteRdy. + ^L -> Clear input. + BufIdle + ? -> Set abnormal + Char: Buf to BufInputBsy. Insert char. + if Fullbuff, BufReadRdy, IRQ, + +*/ + + + +t_stat dtc_srv(UNIT *); +t_stat dtco_srv(UNIT *); +t_stat dtc_attach(UNIT *, char *); +t_stat dtc_detach(UNIT *); +t_stat dtc_reset(DEVICE *); +t_stat dtc_setnl (UNIT *, int32, char *, void *); +t_stat dtc_set_log (UNIT *, int32, char *, void *); +t_stat dtc_set_nolog (UNIT *, int32, char *, void *); +t_stat dtc_show_log (FILE *, UNIT *, int32, void *); +t_stat dtc_help(FILE *, DEVICE *, UNIT *, int32, const char *); +t_stat dtc_help_attach (FILE *, DEVICE *, UNIT *, int32, const char *); +const char *dtc_description(DEVICE *); + + +int32 tmxr_poll; + +uint8 dtc_buf[DTC_MLINES][DTC_BUFSIZ]; +TMLN dtc_ldsc[DTC_MLINES]; /* line descriptors */ +TMXR dtc_desc = { DTC_TLINES, 0, 0, dtc_ldsc }; /* mux descriptor */ +uint8 dtc_lstatus[DTC_MLINES]; /* Line status */ +uint16 dtc_bufptr[DTC_MLINES]; /* Buffer pointer */ +uint16 dtc_bsize[DTC_MLINES]; /* Buffer size */ +uint16 dtc_blimit[DTC_MLINES]; /* Buffer size */ + + +MTAB dtc_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dtc_desc, "Disconnect a specific line" }, + { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, + NULL, &tmxr_show_summ, (void *) &dtc_desc, "Display a summary of line states" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &dtc_desc, "Display current connections" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &dtc_desc, "Display multiplexer statistics" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &dtc_setnl, &tmxr_show_lines, (void *) &dtc_desc, "Display number of lines" }, + { MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file", + &dtc_set_log, NULL, &dtc_desc }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG", + &dtc_set_nolog, NULL, &dtc_desc, "Disable logging on designated line" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL, + NULL, &dtc_show_log, &dtc_desc, "Display logging for all lines" }, + {0} +}; + + +UNIT dtc_unit[] = { + {UDATA(&dtc_srv, UNIT_DTC, 0)}, /* DTC */ + {UDATA(&dtco_srv, UNIT_DIS, 0)}, /* DTC server process */ +}; + +DEVICE dtc_dev = { + "DTC", dtc_unit, NULL, dtc_mod, + 2, 8, 15, 1, 8, 64, + NULL, NULL, &dtc_reset, NULL, &dtc_attach, &dtc_detach, + NULL, DEV_DISABLE | DEV_DEBUG | DEV_MUX, 0, dev_debug, + NULL, NULL, &dtc_help, &dtc_help_attach, (void *)&dtc_desc, + &dtc_description +}; + + + + +/* Start off a terminal controller command */ +t_stat dtc_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr; + int ttu; + int buf; + + uptr = &dtc_unit[0]; + + /* If unit disabled return error */ + if (uptr->flags & UNIT_DIS) + return SCPE_NODEV; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + + /* Check if drive is ready to recieve a command */ + if ((uptr->u5 & DTC_RDY) == 0) + return SCPE_BUSY; + + uptr->u5 = chan; + ttu = (*wc & DTCSTA_TTU) >> 5; + buf = (*wc & DTCSTA_BUF); + /* Set the Terminal unit. */ + if (ttu == 0) + uptr->u4 = -1; + else { + uptr->u4 = buf + ((ttu-1) * 15); + } + if (*wc & DTCSTA_GM) + uptr->u5 |= DTC_IGNGM; + if (cmd & DTCSTA_READ) + uptr->u5 |= DTC_RD; + else if (cmd & DTCSTA_INHIBIT) + uptr->u5 |= DTC_INQ; + else + uptr->u5 |= DTC_WR; + + if (cmd & DTCSTA_BINARY) + uptr->u5 |= DTC_BIN; + + sim_debug(DEBUG_CMD, &dtc_dev, "Datacomm access %s %06o %d %04o\n", + (uptr->u5 & DTC_RD) ? "read" : ((uptr->u5 & DTC_INQ) ? "inq" : + ((uptr->u5 & DTC_WR) ? "write" : "unknown")), + uptr->u5, uptr->u4, *wc); + sim_activate(uptr, 5000); + return SCPE_OK; +} + + +/* Handle processing terminal controller commands */ +t_stat dtc_srv(UNIT * uptr) +{ + int chan = uptr->u5 & DTC_CHAN; + uint8 ch; + int ttu; + int buf; + int i; + int line = uptr->u4; + + + + /* Process interrage command */ + if (uptr->u5 & DTC_INQ) { + if (line == -1) { + buf = -1; + for(i = 0; i < DTC_MLINES; i++) { + if (dtc_lstatus[i]& BufIRQ) { + if ((dtc_lstatus[i] & BufSMASK) == BufReadRdy) + buf = i; + if ((dtc_lstatus[i] & BufSMASK) == BufWriteRdy || + (dtc_lstatus[i] & BufSMASK) == BufIdle) { + line = i; + break; + } + } + } + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm inqury found %d %d ", + line, buf); + if (line != -1) { + chan_set_eof(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " writerdy "); + } else if (buf != -1) { + chan_set_read(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " readrdy "); + line = buf; + } + + if (line != -1) { + if (dtc_lstatus[line] & BufAbnormal) { + chan_set_wcflg(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " abnormal "); + } + dtc_lstatus[line] &= ~BufIRQ; + } + sim_debug(DEBUG_DETAIL, &dtc_dev, " %03o ", dtc_lstatus[i]); + } else { + if (line > dtc_desc.lines) { + chan_set_notrdy(chan); + } else { + switch(dtc_lstatus[line] & BufSMASK) { + case BufReadRdy: + chan_set_read(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " readrdy "); + break; + case BufWriteRdy: + chan_set_eof(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " writerdy "); + break; + default: + chan_set_error(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " busy "); + break; + } + } + if (dtc_lstatus[line] & BufAbnormal) { + chan_set_wcflg(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " abnormal "); + } + dtc_lstatus[line] &= ~BufIRQ; + chan_set_wc(uptr->u4, 0); + chan_set_end(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, " %03o ", dtc_lstatus[line]); + } + if (line != -1) { + for (ttu = 1; line > 15; ttu++) + line -= 15; + } else { + ttu = line = 0; + } + chan_set_wc(chan, (ttu << 5) | line); + chan_set_end(chan); + uptr->u5 = DTC_RDY; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm inqury %d %d\n", + ttu, line); + } + /* Process for each unit */ + if (uptr->u5 & DTC_WR) { + if (line > dtc_desc.lines || line == -1) { + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write invalid %d\n", + line); + chan_set_notrdy(chan); + chan_set_end(chan); + uptr->u5 = DTC_RDY; + return SCPE_OK; + } + /* Validate that we can send data to buffer */ + i = dtc_lstatus[line] & BufSMASK; + switch(i) { + case BufNotReady: + chan_set_notrdy(chan); + case BufInputBusy: + case BufRead: + case BufReadRdy: + chan_set_error(chan); + case BufOutBusy: + chan_set_eof(chan); + chan_set_end(chan); + uptr->u5 = DTC_RDY; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write busy %d %d\n", + line, i); + return SCPE_OK; + + /* Ok to start filling */ + case BufIdle: + case BufWriteRdy: + dtc_lstatus[line] = BufWrite; + dtc_bufptr[line] = 0; + dtc_bsize[line] = 0; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write start %d\n", + line); + break; + + /* Continue filling */ + case BufWrite: + break; + } + + if (chan_read_char(chan, &ch, dtc_bufptr[line] >= dtc_blimit[line])) { + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm write done %d %d ", + line, dtc_bufptr[line]); + if (dtc_lstatus[line] & BufAbnormal) { + chan_set_wcflg(chan); + } + dtc_lstatus[line] = BufOutBusy; + /* Check if we filled up buffer */ + if (dtc_bufptr[line] >= dtc_blimit[line]) { + chan_set_gm(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, "full "); + } else { + dtc_lstatus[line] |= BufGM; + sim_debug(DEBUG_DETAIL, &dtc_dev, "gm "); + } + sim_debug(DEBUG_DETAIL, &dtc_dev, "\n"); + dtc_bsize[line] = dtc_bufptr[line]; + dtc_bufptr[line] = 0; + chan_set_end(chan); + uptr->u5 = DTC_RDY; + return SCPE_OK; + } else { + dtc_buf[line][dtc_bufptr[line]++] = ch & 077; + sim_debug(DEBUG_DATA, &dtc_dev, "Datacomm write data %d %02o %d\n", + line, ch&077, dtc_bufptr[line]); + } + sim_activate(uptr, 5000); + } + + if (uptr->u5 & DTC_RD) { + if (line > dtc_desc.lines || line == -1) { + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read nothing %d\n", + line); + chan_set_notrdy(chan); + chan_set_end(chan); + uptr->u5 = DTC_RDY; + return SCPE_OK; + } + /* Validate that we can send data to buffer */ + i = dtc_lstatus[line] & BufSMASK; + switch(i) { + case BufNotReady: + chan_set_notrdy(chan); + case BufInputBusy: + chan_set_error(chan); + case BufWriteRdy: + case BufOutBusy: + case BufIdle: + case BufWrite: + chan_set_eof(chan); + chan_set_end(chan); + uptr->u5 = DTC_RDY; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read busy %d %d\n", + line, i); + return SCPE_OK; + + /* Ok to start filling */ + case BufReadRdy: + dtc_lstatus[line] = (dtc_lstatus[line] & 030) | BufRead; + dtc_bufptr[line] = 0; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read starting %d\n", + line); + break; + + /* Continue filling */ + case BufRead: + break; + } + + ch = dtc_buf[line][dtc_bufptr[line]++]; + /* If no buffer, error out */ + if (chan_write_char(chan, &ch, dtc_bufptr[line] >= dtc_bsize[line])) { + /* Check if we filled up buffer */ + if (dtc_lstatus[line] & BufGM) { + chan_set_gm(chan); + sim_debug(DEBUG_DETAIL, &dtc_dev, "gm "); + } + if (dtc_lstatus[line] & BufAbnormal) + chan_set_wcflg(chan); + if (dtc_ldsc[line].conn == 0) /* connected? */ + dtc_lstatus[line] = BufNotReady; + else + dtc_lstatus[line] = BufIdle; + dtc_bsize[line] = 0; + chan_set_end(chan); + uptr->u5 = DTC_RDY; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm read done %d\n", + line); + return SCPE_OK; + } else { + sim_debug(DEBUG_DATA, &dtc_dev, "Datacomm read data %d %02o %d\n", + line, ch & 077, dtc_bufptr[line]); + } + sim_activate(uptr, 5000); + } + return SCPE_OK; +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections */ + +t_stat +dtco_srv(UNIT * uptr) +{ + int c, ln, t, c1; + + sim_clock_coschedule(uptr, tmxr_poll); + ln = tmxr_poll_conn(&dtc_desc); /* look for connect */ + if (ln >= 0) { /* got one? */ + dtc_blimit[ln] = DTC_BUFSIZ-1; + dtc_lstatus[ln] = BufIRQ|BufAbnormal|BufWriteRdy; + IAR |= IRQ_12; + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm connect %d\n", ln); + } + + /* For each line that is in idle state enable recieve */ + for (ln = 0; ln < dtc_desc.lines; ln++) { + if (dtc_ldsc[ln].conn && + (dtc_lstatus[ln] & BufSMASK) == BufIdle) { + dtc_ldsc[ln].rcve = 1; + } + } + tmxr_poll_rx(&dtc_desc); /* poll for input */ + for (ln = 0; ln < DTC_MLINES; ln++) { /* loop thru mux */ + /* Check for disconnect */ + if (dtc_ldsc[ln].conn == 0) { /* connected? */ + switch(dtc_lstatus[ln] & BufSMASK) { + case BufIdle: /* Idle, throw in EOT */ + case BufWriteRdy: /* Awaiting output, terminate */ + dtc_bufptr[ln] = 0; + case BufInputBusy: /* reading, terminate with EOT */ + dtc_buf[ln][dtc_bufptr[ln]++] = 017; + dtc_bsize[ln] = dtc_bufptr[ln]; + dtc_lstatus[ln] = BufIRQ|BufAbnormal|BufReadRdy; + IAR |= IRQ_12; + break; + case BufOutBusy: /* Terminate Output */ + dtc_lstatus[ln] = BufIRQ|BufIdle; + dtc_bsize[ln] = 0; + IAR |= IRQ_12; + break; + default: /* Other cases, ignore until + in better state */ + break; + break; + } + continue; /* Skip if not connected */ + } + switch(dtc_lstatus[ln] & BufSMASK) { + case BufIdle: + /* If we have any data to receive */ + if (tmxr_rqln(&dtc_ldsc[ln]) > 0) + dtc_lstatus[ln] = BufInputBusy; + else + break; /* Nothing to do */ + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm recieve %d idle\n", + ln); + dtc_bufptr[ln] = 0; + dtc_bsize[ln] = 0; + case BufInputBusy: + t = 1; + while (t && tmxr_rqln(&dtc_ldsc[ln]) != 0) { + c = tmxr_getc_ln(&dtc_ldsc[ln]) & 0x7f; /* get char */ + c1 = ascii_to_con[c]; + switch(c) { + case '\005': /* ^E ENQ who-are-you */ + dtc_lstatus[ln] &= ~(BufSMASK); + dtc_lstatus[ln] |= BufIRQ|BufAbnormal|BufWriteRdy; + IAR |= IRQ_12; + sim_debug(DEBUG_DETAIL, &dtc_dev, + "Datacomm recieve ENQ %d\n", ln); + t = 0; + break; + case '}': + dtc_buf[ln][dtc_bufptr[ln]++] = 017; + dtc_lstatus[ln] |= BufAbnormal; + /* Fall through to next */ + + case '\r': + case '\n': + case '~': + dtc_lstatus[ln] &= ~BufSMASK; + dtc_lstatus[ln] |= BufIRQ|BufReadRdy; + /* Force at least one character for GM */ + dtc_buf[ln][dtc_bufptr[ln]++] = 077; + dtc_bsize[ln] = dtc_bufptr[ln]; + IAR |= IRQ_12; + t = 0; + sim_debug(DEBUG_DETAIL, &dtc_dev, + "Datacomm recieve %d return\n", ln); + break; + case '\025': /* Control U clear input buffer. */ + dtc_bsize[ln] = 0; + c1 = 0; + break; + case '\b': + case 0x7f: + if (dtc_bufptr[ln] > 0) { + tmxr_putc_ln(&dtc_ldsc[ln], '\b'); + tmxr_putc_ln(&dtc_ldsc[ln], ' '); + tmxr_putc_ln(&dtc_ldsc[ln], '\b'); + dtc_bufptr[ln]--; + } else { + tmxr_putc_ln(&dtc_ldsc[ln], '\007'); + } + c1 = 0; + sim_debug(DEBUG_DATA, &dtc_dev, + "Datacomm recieve %d backspace\n", ln); + break; + case '?': + sim_debug(DEBUG_DATA, &dtc_dev, + "Datacomm recieve %d ?\n", ln); + dtc_lstatus[ln] |= BufAbnormal; + c1 = 0; + break; + default: + sim_debug(DEBUG_DATA, &dtc_dev, + "Datacomm recieve %d %02x %c %02o\n", ln, c, c, c1); + } + if (t && c1) { + tmxr_putc_ln(&dtc_ldsc[ln], con_to_ascii[c1]); + dtc_buf[ln][dtc_bufptr[ln]++] = c1; + } + if (dtc_bufptr[ln] >= dtc_blimit[ln]) { + sim_debug(DEBUG_DETAIL, &dtc_dev, + "Datacomm recieve %d full\n", ln); + dtc_lstatus[ln] &= ~(BufSMASK); + dtc_lstatus[ln] |= BufGM|BufIRQ|BufReadRdy; + dtc_bsize[ln] = dtc_bufptr[ln]; + IAR |= IRQ_12; + t = 0; + break; + } + } + + break; + case BufOutBusy: + /* Get next char and send to output */ + t = 1; + while(t && dtc_bufptr[ln] < dtc_bsize[ln] && dtc_ldsc[ln].xmte) { + c = dtc_buf[ln][dtc_bufptr[ln]++]; + c1 = con_to_ascii[c]; + switch(c) { + case 057: /* { */ + c1 = '\r'; /* CR */ + break; + case 032: /* ! */ + c1 = '\n'; /* LF */ + break; + case 076: /* < */ + c1 = 0; /* X-ON */ + break; + case 016: /* > */ + c1 = 0; /* DEL */ + break; + case 017: /* } */ + /* Disconnect line */ + tmxr_reset_ln(&dtc_ldsc[ln]); + sim_debug(DEBUG_DETAIL, &dtc_dev, + "Datacomm disconnect %d\n", ln); + t = 0; + continue; /* On to next line */ + } + sim_debug(DEBUG_DATA, &dtc_dev, + "Datacomm transmit %d %02o %c\n", ln, c&077, c1); + tmxr_putc_ln(&dtc_ldsc[ln], c1); + if (c1 == '\n') { + tmxr_putc_ln(&dtc_ldsc[ln], '\r'); + } + } + if (dtc_bufptr[ln] >= dtc_bsize[ln]) { + if (dtc_lstatus[ln] & BufGM) { + sim_debug(DEBUG_DETAIL, &dtc_dev, + "Datacomm idle %d\n", ln); + dtc_lstatus[ln] = BufIRQ|BufIdle; + } else { + sim_debug(DEBUG_DETAIL, &dtc_dev, "Datacomm writerdy %d\n", + ln); + dtc_lstatus[ln] = BufIRQ|BufWriteRdy; + } + IAR |= IRQ_12; + } + break; + default: + /* Other states are an ignore */ + break; + } + } /* end for */ + tmxr_poll_tx(&dtc_desc); /* poll xmt */ + + return SCPE_OK; +} + + +t_stat +dtc_reset(DEVICE *dptr) { + if (dtc_unit[0].flags & UNIT_ATT) { + sim_activate(&dtc_unit[1], 100); /* quick poll */ + iostatus |= DTC_FLAG; + } else { + sim_cancel(&dtc_unit[1]); + iostatus &= ~DTC_FLAG; + } + return SCPE_OK; +} + +/* Attach master unit */ +t_stat +dtc_attach(UNIT * uptr, char *cptr) +{ + int i; + t_stat r; + + r = tmxr_attach(&dtc_desc, uptr, cptr); /* attach */ + if (r != SCPE_OK) + return r; /* error */ + sim_activate(&dtc_unit[1], 100); /* quick poll */ + for (i = 0; i < DTC_MLINES; i++) { + dtc_lstatus[i] = BufNotReady; /* Device not connected */ + } + uptr->u5 = DTC_RDY; + iostatus |= DTC_FLAG; + return SCPE_OK; +} + +/* Detach master unit */ + +t_stat +dtc_detach(UNIT * uptr) +{ + int i; + t_stat r; + + r = tmxr_detach(&dtc_desc, uptr); /* detach */ + for (i = 0; i < dtc_desc.lines; i++) + dtc_ldsc[i].rcve = 0; /* disable rcv */ + sim_cancel(uptr); /* stop poll */ + uptr->u5 = 0; + iostatus &= ~DTC_FLAG; + return r; +} + +/* SET LINES processor */ + +t_stat dtc_setnl (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + int32 newln, i, t; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + newln = (int32) get_uint (cptr, 10, DTC_MLINES, &r); + if ((r != SCPE_OK) || (newln == dtc_desc.lines)) + return r; + if ((newln == 0) || (newln > DTC_MLINES)) + return SCPE_ARG; + if (newln < dtc_desc.lines) { + for (i = newln, t = 0; i < dtc_desc.lines; i++) + t = t | dtc_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < dtc_desc.lines; i++) { + if (dtc_ldsc[i].conn) { + tmxr_linemsg (&dtc_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_send_buffered_data (&dtc_ldsc[i]); + } + tmxr_detach_ln (&dtc_ldsc[i]); /* completely reset line */ + } + } + if (dtc_desc.lines < newln) + memset (dtc_ldsc + dtc_desc.lines, 0, sizeof(*dtc_ldsc)*(newln-dtc_desc.lines)); + dtc_desc.lines = newln; + return dtc_reset (&dtc_dev); /* setup lines and auto config */ +} + +/* SET LOG processor */ + +t_stat dtc_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + char *tptr; + t_stat r; + int32 ln; + + if (cptr == NULL) + return SCPE_ARG; + tptr = strchr (cptr, '='); + if ((tptr == NULL) || (*tptr == 0)) + return SCPE_ARG; + *tptr++ = 0; + ln = (int32) get_uint (cptr, 10, dtc_desc.lines, &r); + if ((r != SCPE_OK) || (ln >= dtc_desc.lines)) + return SCPE_ARG; + return tmxr_set_log (NULL, ln, tptr, desc); +} + +/* SET NOLOG processor */ + +t_stat dtc_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + t_stat r; + int32 ln; + + if (cptr == NULL) + return SCPE_ARG; + ln = (int32) get_uint (cptr, 10, dtc_desc.lines, &r); + if ((r != SCPE_OK) || (ln >= dtc_desc.lines)) + return SCPE_ARG; + return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +t_stat dtc_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) +{ + int32 i; + + for (i = 0; i < dtc_desc.lines; i++) { + fprintf (st, "line %d: ", i); + tmxr_show_log (st, NULL, i, desc); + fprintf (st, "\n"); + } + return SCPE_OK; +} + +/* Show summary processor */ + +t_stat +dtc_summ(FILE * st, UNIT * uptr, int32 val, void *desc) +{ + uint32 i, t; + + t = 0; + for (i = 0; i < DTC_MLINES; i++) + t = t + (dtc_ldsc[i].conn != 0); + if (t == 1) + fprintf(st, "1 connection"); + else + fprintf(st, "%d connections", t); + return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat +dtc_show(FILE * st, UNIT * uptr, int32 val, void *desc) +{ + int32 i, cc; + + for (cc = 0; (cc < DTC_MLINES) && dtc_ldsc[cc].conn; cc++) ; + if (cc) { + for (i = 0; i < DTC_MLINES; i++) { + if (dtc_ldsc[i].conn) { + if (val) + tmxr_fconns(st, &dtc_ldsc[i], i); + else + tmxr_fstats(st, &dtc_ldsc[i], i); + } + } + } else + fprintf(st, "all disconnected\n"); + return SCPE_OK; +} + +t_stat dtc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + +fprintf (st, "B249 Terminal Control Unit\n\n"); +fprintf (st, "The B249 is a terminal multiplexor. Up to %d lines are supported.\n", DTC_MLINES); +fprintf (st, "The default number of lines is %d. The number of lines can\n", DTC_MLINES); +fprintf (st, "be changed with the command\n\n"); +fprintf (st, " sim> SET %s LINES=n set line count to n\n\n", dptr->name); +fprintf (st, "The B249 supports logging on a per-line basis. The command\n\n"); +fprintf (st, " sim> SET %s LOG=n=filename\n\n", dptr->name); +fprintf (st, "enables logging for the specified line(n) to the indicated file. The command\n\n"); +fprintf (st, " sim> SET %s NOLOG=line\n\n", dptr->name); +fprintf (st, "disables logging for the specified line and closes any open log file. Finally,\n"); +fprintf (st, "the command:\n\n"); +fprintf (st, " sim> SHOW %s LOG\n\n", dptr->name); +fprintf (st, "displays logging information for all %s lines.\n\n", dptr->name); +fprintf (st, "Once the B249 is attached and the simulator is running, the B249 will listen for\n"); +fprintf (st, "connections on the specified port. It assumes that the incoming connections\n"); +fprintf (st, "are Telnet connections. The connection remains open until disconnected by the\n"); +fprintf (st, "simulated program, the Telnet client, a SET %s DISCONNECT command, or a\n", dptr->name); +fprintf (st, "DETACH %s command.\n\n", dptr->name); +fprintf (st, "Other special %s commands:\n\n", dptr->name); +fprintf (st, " sim> SHOW %s CONNECTIONS show current connections\n", dptr->name); +fprintf (st, " sim> SHOW %s STATISTICS show statistics for active connections\n", dptr->name); +fprintf (st, " sim> SET %s DISCONNECT=linenumber disconnects the specified line.\n\n\n", dptr->name); +fprintf (st, "All open connections are lost when the simulator shuts down or the %s is\n", dptr->name); +fprintf (st, "detached.\n\n"); +dtc_help_attach (st, dptr, uptr, flag, cptr); +return SCPE_OK; +} + +t_stat dtc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +tmxr_attach_help (st, dptr, uptr, flag, cptr); +fprintf (st, "The terminal lines perform input and output through Telnet sessions connected\n"); +fprintf (st, "to a user-specified port. The ATTACH command specifies the port to be used:\n\n"); +fprintf (st, " sim> ATTACH %s {interface:}port set up listening port\n\n", dptr->name); +fprintf (st, "where port is a decimal number between 1 and 65535 that is not being used for\n"); +fprintf (st, "other TCP/IP activities. All terminals are considered Dialup to the B249.\n"); +return SCPE_OK; +} + +const char *dtc_description (DEVICE *dptr) +{ + return "B249 Terminal Control Unit"; +} + +#endif diff --git a/B5500/b5500_io.c b/B5500/b5500_io.c new file mode 100644 index 00000000..7ca3d4fd --- /dev/null +++ b/B5500/b5500_io.c @@ -0,0 +1,795 @@ +/* B5500_io.c: Burroughs 5500 I/O System. + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "b5500_defs.h" + +#define EOR 1 +#define USEGM 2 + +t_stat chan_reset(DEVICE * dptr); + +/* Channel data structures + + chan_dev Channel device descriptor + chan_unit Channel unit descriptor + chan_reg Channel register list + chan_mod Channel modifiers list +*/ + +t_uint64 D[NUM_CHAN]; /* Current I/O instruction */ +uint8 CC[NUM_CHAN]; /* Channel character count */ +t_uint64 W[NUM_CHAN]; /* Assembly register */ +uint8 status[NUM_CHAN]; /* Channel status */ +uint8 cstatus; /* Active status */ + +#define WC(x) (uint16)(((x) & DEV_WC) >> DEV_WC_V) +#define toWC(x) (((t_uint64)(x) << DEV_WC_V) & DEV_WC) + + +UNIT chan_unit[] = { + /* Normal channels */ + {UDATA(NULL,UNIT_DISABLE,0)},/* A */ + {UDATA(NULL,UNIT_DISABLE,0)},/* B */ + {UDATA(NULL,UNIT_DISABLE,0)},/* C */ + {UDATA(NULL,UNIT_DISABLE,0)},/* D */ +}; + +REG chan_reg[] = { + {BRDATA(D, D, 8, 48, NUM_CHAN), REG_RO}, + {BRDATA(CC, CC, 7, 6, NUM_CHAN), REG_RO}, + {BRDATA(W, W, 8, 48, NUM_CHAN), REG_RO}, + {NULL} +}; + + +/* Simulator debug controls */ +DEBTAB chn_debug[] = { + {"CHANNEL", DEBUG_CHAN}, + {"DETAIL", DEBUG_DETAIL}, + {"DATA", DEBUG_DATA}, + {"CH0", 0x0100 << 0}, + {"CH1", 0x0100 << 1}, + {"CH2", 0x0100 << 2}, + {"CH3", 0x0100 << 3}, + {0, 0} +}; + +DEVICE chan_dev = { + "IO", chan_unit, chan_reg, NULL, + NUM_CHAN, 10, 18, 1, 10, 44, + NULL, NULL, &chan_reset, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, chn_debug +}; + + + +t_stat +chan_reset(DEVICE * dptr) +{ + int i; + int j = 1; + + cstatus = 0; + /* Clear channel assignment */ + for (i = 0; i < NUM_CHAN; i++) { + status[i] = 0; + D[i] = 0; + W[i] = 0; + CC[i] = 0; + if (chan_unit[i].flags & UNIT_DIS) + cstatus |= j; + j <<= 1; + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +chan_boot(t_uint64 desc) +{ + M[020] = desc; + M[010] = 020; + loading = 1; + start_io(); + return SCPE_OK; +} + +int +find_chan() { + int i; + int chan; + + i = 1; + for(chan = 0; chan < NUM_CHAN; chan++) { + if ((cstatus & i) == 0) + break; + i <<= 1; + } + + if (chan == NUM_CHAN) { + return 0; + } + return chan + 1; +} + +void +chan_release(int chan) { + cstatus &= ~(1 << chan); +} + +int +chan_advance(int chan) { + uint16 addr = (uint16)(D[chan] & CORE); + + if (D[chan] & DEV_WCFLG) { + uint16 wc = WC(D[chan]); + if (wc == 0) { + status[chan] |= EOR; + return 1; + } + D[chan] &= ~DEV_WC; + D[chan] |= toWC(wc-1); + } + if (addr > MEMSIZE) { + D[chan] |= DEV_MEMERR; + status[chan] |= EOR; + return 1; + } + W[chan] = M[addr]; + D[chan] &= ~CORE; + if (D[chan] & DEV_BACK) + D[chan] |= (t_uint64)((addr - 1) & CORE); + else + D[chan] |= (t_uint64)((addr + 1) & CORE); + CC[chan] = 0; + return 0; +} + +void +start_io() { + int i; + int chan; + t_stat r; + uint16 dev; + uint16 cmd; + uint16 wc; + int addr; + + chan = find_chan(); + + addr = M[010] & CORE; + + if (chan == 0) { + IAR |= IRQ_1; + return; + } + chan--; + i = 1 << chan; + sim_debug(DEBUG_DETAIL, &chan_dev, "strtio(%016llo %d)\n", M[addr], chan); + D[chan] = M[addr] & D_MASK; + CC[chan] = 0; + W[chan] = 0; + dev = (uint16)((D[chan] & DEVMASK) >> DEV_V); + cmd = (uint16)((D[chan] & DEV_CMD) >> DEV_CMD_V); + wc = WC(D[chan]); + D[chan] &= ~DEV_RESULT; + status[chan] = 0; + if (dev & 1) { +#if (NUM_DEVS_MT > 0) + status[chan] = USEGM; + r = mt_cmd(cmd, dev, chan, &wc); +#else + r = SCPE_UNATT; +#endif + } else { + switch(dev) { +#if (NUM_DEVS_DR > 0) + case DRUM1_DEV: + case DRUM2_DEV: + r = drm_cmd(cmd, dev, chan, &wc, (uint8)((M[addr] & PRESENT)!=0)); + break; +#endif + +#if (NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0) + case CARD1_DEV: + case CARD2_DEV: + r = card_cmd(cmd, dev, chan, &wc); + break; +#endif + +#if (NUM_DEVS_DSK > 0) + case DSK1_DEV: + case DSK2_DEV: + /* Need to pass word count to identify interrogates */ + r = dsk_cmd(cmd, dev, chan, &wc); + break; +#endif + +#if (NUM_DEVS_DTC > 0) + case DTC_DEV: + status[chan] = USEGM; + /* Word count is TTU and BUF number */ + r = dtc_cmd(cmd, dev, chan, &wc); + if (r == SCPE_OK) + D[chan] &= ~DEV_WC; + D[chan] &= ~(DEV_BIN|DEV_WCFLG); + wc = 0; + break; +#endif + +#if (NUM_DEVS_LPR > 0) + case PRT1_DEV: + case PRT2_DEV: + r = lpr_cmd(cmd, dev, chan, &wc); + if (r == SCPE_OK) + D[chan] &= ~DEV_BACK; /* Clear this bit, since printer + uses this to determine 120/132 + char line */ + break; +#endif + +#if (NUM_DEVS_CON > 0) + case SPO_DEV: + status[chan] = USEGM; + r = con_cmd(cmd, dev, chan, &wc); + break; +#endif + default: + r = SCPE_UNATT; + break; + } + } + if (wc != 0) { + D[chan] &= ~DEV_WC; + D[chan] |= toWC(wc) | DEV_WCFLG; + } + switch(r) { + case SCPE_OK: + cstatus |= i; + return; + case SCPE_NXDEV: + case SCPE_UNATT: + D[chan] |= DEV_NOTRDY; + break; + case SCPE_BUSY: + D[chan] |= DEV_BUSY; + break; + case SCPE_EOF: + D[chan] |= DEV_EOF; + break; + } + chan_set_end(chan); +} + + +void +chan_set_end(int chan) { + uint16 dev; + dev = (uint16)((D[chan] & DEVMASK) >> DEV_V); + /* Set character count if reading and tape */ + if ((dev & 1) && (D[chan] & DEV_IORD)) { + D[chan] &= ~((7LL)< 1010 + 00 0001 00 0001 + 00 0010 00 0010 + 00 0011 00 0011 + 00 0100 00 0100 + 00 0101 00 0101 + 00 0110 00 0110 + 00 0111 00 0111 + 00 1000 00 1000 + 00 1001 00 1001 + 00 1010 00 1011 1010 -> 1011 + 00 1011 00 1100 1011 -> 1100 + 00 1100 00 0000 1100 -> 0000 + 00 1101 00 1101 + 00 1110 00 1110 + 00 1111 00 1111 + 01 0000 11 1010 0000 -> 1010 10 + 01 0001 11 0001 1 + 01 0010 11 0010 2 + 01 0011 11 0011 3 + 01 0100 11 0100 + 01 0101 11 0101 + 01 0110 11 0110 + 01 0111 11 0111 + 01 1000 11 1000 8 + 01 1001 11 1001 9 + 01 1010 11 1011 1010 -> 1011 10 + 01 1011 11 1100 1011 -> 1100 11 + 01 1100 11 0000 1100 -> 0000 12 + 01 1101 11 1101 13 + 01 1110 11 1110 14 + 01 1111 11 1111 15 + 10 0000 10 1010 0000 -> 1010 + 10 0001 10 0001 + 10 0010 10 0010 + 10 0011 10 0011 + 10 0100 10 0100 + 10 0101 10 0101 + 10 0110 10 0110 + 10 0111 10 0111 + 10 1000 10 1000 + 10 1001 10 1001 + 10 1010 10 1011 1010 -> 1011 + 10 1011 10 1100 1011 -> 1100 + 10 1100 10 0000 1100 -> 0000 + 10 1101 10 1101 + 10 1110 10 1110 + 10 1111 10 1111 + 11 0000 01 0000 + 11 0001 01 0001 + 11 0010 01 0010 + 11 0011 01 0011 + 11 0100 01 0100 + 11 0101 01 0101 + 11 0110 01 0110 + 11 0111 01 0111 + 11 1000 01 1000 + 11 1001 01 1001 + 11 1010 01 1011 1010 -> 1011 + 11 1011 01 1100 1011 -> 1100 + 11 1100 01 1010 1100 -> 1010 + 11 1101 01 1101 + 11 1110 01 1110 + 11 1111 01 1111 */ + +/* returns 1 when channel can take no more characters. + A return of 1 indicates that the character was not + processed. +*/ +int chan_write_char(int chan, uint8 *ch, int flags) { + uint8 c; + + if (status[chan] & EOR) + return 1; + + if (D[chan] & DEV_INHTRF) { + status[chan] |= EOR; + return 1; + } + + /* Check if first word */ + if (CC[chan] == 0) { + uint16 wc = WC(D[chan]); + + if (D[chan] & DEV_WCFLG && wc == 0) { + sim_debug(DEBUG_DATA, &chan_dev, "zerowc(%d)\n", chan); + status[chan] |= EOR; + return 1; + } + W[chan] = 0; + } + + c = *ch & 077; + if ((D[chan] & DEV_BIN) == 0) { + /* Translate BCL to BCD */ + uint8 cx = c & 060; + + c &= 017; + switch(c) { + case 0: + /* 11-0 -> 01 C */ + /* 10-0 -> 10 C */ + /* 01-0 -> 11 0 */ + /* 00-0 -> 00 C */ + if (cx != 020) + c = 0xc; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 0xd: + case 0xe: + case 0xf: + break; + case 0xa: + /* 11-A -> 01 0 */ + /* 10-A -> 10 0 */ + /* 01-A -> 11 C */ + /* 00-A -> 00 0 */ + if (cx == 020) + c = 0xc; + else + c = 0; + break; + case 0xb: + c = 0xa; + break; + case 0xc: + c = 0xb; + break; + } + c |= cx ^ ((cx & 020)<<1); + } + + if (D[chan] & DEV_BACK) + W[chan] |= ((t_uint64)c) << ((CC[chan]) * 6); + else + W[chan] |= ((t_uint64)c) << ((7 - CC[chan]) * 6); + CC[chan]++; + + if (CC[chan] == 8) { + uint16 addr = (uint16)(D[chan] & CORE); + + M[addr] = W[chan]; + sim_debug(DEBUG_DATA, &chan_dev, "write(%d, %05o, %016llo)\n", + chan, addr, W[chan]); + + if (chan_advance(chan)) + return 1; + } + if (flags) { + if ((D[chan] & (DEV_BIN|DEV_WCFLG)) == 0) { + /* Insert group mark */ + if (D[chan] & DEV_BACK) { + int i = CC[chan]; + W[chan] |= (t_uint64)037LL << ((CC[chan]) * 6); + + while(i < 8) { + W[chan] |= (t_uint64)014LL << (i * 6); + i++; + } + } else { + W[chan] |= 037LL << ((7 - CC[chan]) * 6); + } + CC[chan]++; + } + + /* Flush last word */ + if (CC[chan] != 0) { + uint16 addr = (uint16)(D[chan] & CORE); + + M[addr] = W[chan]; + sim_debug(DEBUG_DATA, &chan_dev, "write(%d, %05o, %016llo)\n", + chan, addr, W[chan]); + (void)chan_advance(chan); + } + status[chan] |= EOR; + return 1; + } + + return 0; +} + +/* Returns 1 on last character. If it returns 1, the + character in ch is not valid. If flag is set to 1, then + this is the last character the device will request. +*/ +int chan_read_char(int chan, uint8 *ch, int flags) { + uint8 c; + int gm; + + if (status[chan] & EOR) + return 1; + + if (D[chan] & DEV_INHTRF) { + status[chan] |= EOR; + return 1; + } + + if (CC[chan] == 0) { + uint16 addr = (uint16)(D[chan] & CORE); + if (chan_advance(chan)) + return 1; + sim_debug(DEBUG_DATA, &chan_dev, "read(%d, %05o, %016llo)\n", chan, + addr, W[chan]); + } + + if (D[chan] & DEV_BACK) + c = 077 & (W[chan] >> ((CC[chan]) * 6)); + else + c = 077 & (W[chan] >> ((7 - CC[chan]) * 6)); + gm = (c == 037); + CC[chan]++; + if (CC[chan] == 8) { + CC[chan] = 0; + } + if ((D[chan] & DEV_BIN) == 0) { + /* Translate BCD to BCL */ + uint8 cx = c & 060; + c &= 017; + switch(c) { + case 0: + if (cx != 060) + c = 0xa; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 0xd: + case 0xe: + case 0xf: + break; + case 0xa: + c = 0xb; + break; + case 0xb: + c = 0xc; + break; + case 0xc: + if (cx == 060) + c = 0xa; + else + c = 0; + break; + } + c |= cx ^ ((cx & 020)<<1); + } + *ch = c; + if ((status[chan] & USEGM) != 0 && (D[chan] & DEV_WCFLG) == 0 && gm) { + status[chan] |= EOR; + return 1; + } + if (flags) { + status[chan] |= EOR; + } + return 0; +} + +/* Same as chan_read_char, however we do not check word count + nor do we advance it. +*/ +int chan_read_disk(int chan, uint8 *ch, int flags) { + uint8 c; + + if (CC[chan] == 0) { + uint16 addr = (uint16)(D[chan] & CORE); + if (addr > MEMSIZE) { + D[chan] |= DEV_MEMERR; + return 1; + } + + W[chan] = M[addr]; + D[chan] &= ~CORE; + D[chan] |= (addr + 1) & CORE; + } + + c = 077 & (W[chan] >> ((7 - CC[chan]) * 6)); + CC[chan]++; + *ch = c; + if (CC[chan] == 8) { + CC[chan] = 0; + return 1; + } + return 0; +} + +int +chan_advance_drum(int chan) { + uint16 addr = (uint16)(D[chan] & CORE); + uint16 wc = WC(D[chan]); + if (wc == 0) { + status[chan] |= EOR; + return 1; + } + D[chan] &= ~DEV_WC; + D[chan] |= toWC(wc-1); + if (addr > MEMSIZE) { + D[chan] |= DEV_MEMERR; + status[chan] |= EOR; + return 1; + } + + W[chan] = M[addr]; + D[chan] &= ~CORE; + D[chan] |= (addr + 1) & CORE; + CC[chan] = 0; + return 0; +} + +/* returns 1 when channel can take no more characters. + A return of 1 indicates that the character was not + processed. +*/ +int chan_write_drum(int chan, uint8 *ch, int flags) { + uint8 c; + + if (status[chan] & EOR) + return 1; + + /* Check if first word */ + if (CC[chan] == 0) { + uint16 wc = WC(D[chan]); + + if (wc == 0) { + status[chan] |= EOR; + return 1; + } + W[chan] = 0; + } + + c = *ch; + c &= 077; + + W[chan] |= (t_uint64)c << ((7 - CC[chan]) * 6); + CC[chan]++; + + if (CC[chan] == 8) { + uint16 addr = (uint16)(D[chan] & CORE); + + M[addr] = W[chan]; + if (chan_advance_drum(chan)) + return 1; + } + if (flags) { + /* Flush last word */ + if (CC[chan] != 0) { + uint16 addr = (uint16)(D[chan] & CORE); + + M[addr] = W[chan]; + (void)chan_advance_drum(chan); + } + status[chan] |= EOR; + return 1; + } + + return 0; +} + +/* Returns 1 on last character. If it returns 1, the + character in ch is not valid. If flag is set to 1, then + this is the last character the device will request. +*/ +int chan_read_drum(int chan, uint8 *ch, int flags) { + uint8 c; + + if (status[chan] & EOR) + return 1; + + + if (CC[chan] == 0) { + if (chan_advance_drum(chan)) + return 1; + } + + c = 077 & (W[chan] >> ((7 - CC[chan]) * 6)); + CC[chan]++; + if (CC[chan] == 8) { + CC[chan] = 0; + } + *ch = c; + if (flags) { + status[chan] |= EOR; + } + return 0; +} + diff --git a/B5500/b5500_mt.c b/B5500/b5500_mt.c new file mode 100644 index 00000000..c43772ef --- /dev/null +++ b/B5500/b5500_mt.c @@ -0,0 +1,629 @@ +/* b5500_mt.c: Burrioughs 5500 Magnetic tape controller + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "b5500_defs.h" +#include "sim_tape.h" + +#if (NUM_DEVS_MT > 0) + +#define BUFFSIZE 10240 +#define UNIT_MT UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE +#define HT 500 /* Time per char high density */ + +/* in u3 is device address */ +/* in u4 is current buffer position */ +/* in u5 Bits 30-16 of W */ +#define URCSTA_SKIP 000017 /* Skip mask */ +#define URCSTA_SINGLE 000020 /* Single space skip. */ +#define URCSTA_DOUBLE 000040 /* Double space skip */ +#define URCSTA_READ 000400 /* Read flag */ +#define URCSTA_WC 001000 /* Use word count */ +#define URCSTA_DIRECT 002000 /* Direction, Long line */ +#define URCSTA_BINARY 004000 /* Binary transfer */ +#define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */ + +#define MT_CHAN 0000003 /* Channel active on */ +#define MT_BIN 0000004 /* Binary/BCD */ +#define MT_BACK 0000010 /* Backwards */ +#define MT_CMD 0000070 /* Command to tape drive */ +#define MT_INT 0000010 /* Interrogate */ +#define MT_RD 0000020 /* Reading */ +#define MT_RDBK 0000030 /* Reading Backwards */ +#define MT_WR 0000040 /* Writing */ +#define MT_REW 0000050 /* Rewind */ +#define MT_FSR 0000060 /* Space Forward */ +#define MT_BSR 0000070 /* Space Backward record */ +#define MT_RDY 0000100 /* Device is ready for command */ +#define MT_IDLE 0000200 /* Tape still in motion */ +#define MT_MARK 0001000 /* Hit tape mark */ +#define MT_EOT 0002000 /* At End Of Tape */ +#define MT_BOT 0004000 /* At Beginning Of Tape */ +#define MT_EOR 0010000 /* Set EOR on next record */ +#define MT_BSY 0020000 /* Tape busy after operation */ +#define MT_LOADED 0040000 /* Tape loaded, return ready */ + + +t_stat mt_srv(UNIT *); +t_stat mt_attach(UNIT *, char *); +t_stat mt_detach(UNIT *); +t_stat mt_reset(DEVICE *); +t_stat mt_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *mt_description(DEVICE *dptr); + +/* Channel level activity */ +uint8 mt_chan[NUM_CHAN]; + +uint16 mt_busy = 0; /* Busy bits */ + +/* One buffer per channel */ +uint8 mt_buffer[NUM_CHAN][BUFFSIZE]; + +UNIT mt_unit[] = { +/* Controller 1 */ +#if (NUM_DEVS_MT > 0) + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 3 */ +#if (NUM_DEVS_MT > 3) + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT, 0), 0}, /* 7 */ +#if (NUM_DEVS_MT > 7) + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 8 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 9 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 10 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 11 */ +#if (NUM_DEVS_MT > 11) + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 12 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 13 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 14 */ + {UDATA(&mt_srv, UNIT_MT|UNIT_DIS, 0), 0}, /* 15 */ +#endif +#endif +#endif +#endif +}; + +MTAB mt_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL, NULL, + "Write ring in place"}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL, NULL, + "no Write ring in place"}, + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL, + "Set/Display tape format (SIMH, E11, TPC, P7B)" }, + {MTAB_XTD | MTAB_VUN, 0, "LENGTH", "LENGTH", + &sim_tape_set_capac, &sim_tape_show_capac, NULL, + "Set unit n capacity to arg MB (0 = unlimited)" }, + {MTAB_XTD | MTAB_VUN, 0, "DENSITY", "DENSITY", + NULL, &sim_tape_show_dens, NULL}, + {0} +}; + + +DEVICE mt_dev = { + "MT", mt_unit, NULL, mt_mod, + NUM_DEVS_MT, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, NULL, &mt_attach, &mt_detach, + NULL, DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, + &mt_description +}; + + + + +/* Start off a mag tape command */ +t_stat +mt_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr; + int unit = dev >> 1; + + /* Make sure valid drive number */ + if (unit > NUM_DEVS_MT || unit < 0) + return SCPE_NODEV; + + uptr = &mt_unit[unit]; + /* If unit disabled return error */ + if (uptr->flags & UNIT_DIS) + return SCPE_NODEV; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + + /* Not there until loading done */ + if ((uptr->u5 & MT_LOADED)) + return SCPE_UNATT; + + /* Check if drive is ready to recieve a command */ + if ((uptr->u5 & MT_BSY) != 0) + return SCPE_BUSY; + + /* Determine actual command */ + uptr->u5 &= ~(MT_RDY|MT_CHAN|MT_CMD|MT_BIN); + uptr->u5 |= chan; + if (cmd & URCSTA_BINARY) + uptr->u5 |= MT_BIN; + if (cmd & URCSTA_READ) { + if ((cmd & URCSTA_WC) && *wc == 0) + uptr->u5 |= MT_FSR; + else + uptr->u5 |= MT_RD; + } else { + /* Erase gap not supported on sim, treat as + write of null record */ + if ((cmd & URCSTA_WC) && *wc == 0) + uptr->u5 |= MT_INT; + else + uptr->u5 |= MT_WR; + } + + *wc = 0; /* So no overide occurs */ + + /* Convert command to correct type */ + if (cmd & URCSTA_DIRECT) + uptr->u5 |= MT_BACK; + uptr->u6 = 0; + uptr->hwmark = -1; + sim_debug(DEBUG_CMD, &mt_dev, "Command %d %o %o\n\r", unit, uptr->u5, cmd); + if ((uptr->u5 & MT_IDLE) == 0) { + sim_activate(uptr,50000); + } + return SCPE_OK; +} + + + +/* Map simH errors into machine errors */ +t_stat mt_error(UNIT * uptr, int chan, t_stat r, DEVICE * dptr) +{ + switch (r) { + case MTSE_OK: /* no error */ + sim_debug(DEBUG_EXP, dptr, "OK "); + break; + + case MTSE_EOM: /* end of medium */ + sim_debug(DEBUG_EXP, dptr, "EOT "); + if (uptr->u5 & MT_BOT) { + chan_set_blank(chan); + } else { + uptr->u5 &= ~MT_BOT; + uptr->u5 |= MT_EOT; + chan_set_eot(chan); + } + break; + + case MTSE_TMK: /* tape mark */ + sim_debug(DEBUG_EXP, dptr, "MARK "); + uptr->u5 &= ~(MT_BOT|MT_EOT); + chan_set_eof(chan); + break; + + case MTSE_WRP: /* write protected */ + sim_debug(DEBUG_EXP, dptr, "WriteLocked "); + chan_set_wrp(chan); + break; + + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + case MTSE_RECE: /* error in record */ + chan_set_error(chan); /* Force redundency error */ + sim_debug(DEBUG_EXP, dptr, "ERROR %d ", r); + break; + case MTSE_BOT: /* beginning of tape */ + uptr->u5 &= ~MT_EOT; + uptr->u5 |= MT_BOT; + chan_set_bot(chan); /* Set flag */ + sim_debug(DEBUG_EXP, dptr, "BOT "); + break; + case MTSE_UNATT: /* unattached */ + default: + sim_debug(DEBUG_EXP, dptr, "%d ", r); + } + uptr->u5 &= ~(MT_CMD|MT_BIN); + uptr->u5 |= MT_RDY|MT_IDLE; + chan_set_end(chan); + return SCPE_OK; +} + +/* Handle processing of tape requests. */ +t_stat mt_srv(UNIT * uptr) +{ + int chan = uptr->u5 & MT_CHAN; + int unit = uptr - mt_unit; + int cmd = uptr->u5 & MT_CMD; + DEVICE *dptr = find_dev_from_unit(uptr); + t_mtrlnt reclen; + t_stat r = SCPE_ARG; /* Force error if not set */ + uint8 ch; + int mode; + t_mtrlnt loc; + + + /* Simulate tape load delay */ + if (uptr->u5 & MT_LOADED) { + uptr->u5 &= ~MT_LOADED; + uptr->u5 |= MT_BSY|MT_RDY; + sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Loaded\n\r", unit); + sim_activate(uptr, 50000); + return SCPE_OK; + } + + if (uptr->u5 & MT_BSY) { + uptr->u5 &= ~MT_BSY; + sim_debug(DEBUG_DETAIL, dptr, "Unit=%d Online\n\r", unit); + iostatus |= 1 << (uptr - mt_unit); + if (uptr->u5 & MT_IDLE) + sim_activate(uptr, 50000); + return SCPE_OK; + } + + if (uptr->u5 & MT_IDLE) { + uptr->u5 &= ~MT_IDLE; + if (uptr->u5 & MT_RDY) { + sim_debug(DEBUG_DETAIL, dptr, "Unit=%d idling\n\r", unit); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "Unit=%d start %02o\n\r", unit, cmd); + } + + switch (cmd) { + /* Handle interrogate */ + case MT_INT: + if (sim_tape_wrp(uptr)) + chan_set_wrp(chan); + uptr->u5 &= ~(MT_CMD|MT_BIN); + uptr->u5 |= MT_RDY; + chan_set_end(chan); + sim_debug(DEBUG_DETAIL, dptr, "Status\n\r"); + return SCPE_OK; + + case MT_RD: /* Read */ + /* If at end of record, fill buffer */ + if (uptr->hwmark == -1) { + sim_debug(DEBUG_DETAIL, dptr, "Read unit=%d %s ", unit, + (uptr->u5 & MT_BIN)? "bin": "bcd"); + if (sim_tape_eot(uptr)) { + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_EOM, dptr); + } + r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); + if (r != MTSE_OK) { + if (r == MTSE_TMK) { + sim_debug(DEBUG_DETAIL, dptr, "TM\n\r"); + ch = 017; + (void)chan_write_char(chan, &ch, 1); + sim_activate(uptr, 4000); + } else { + sim_debug(DEBUG_DETAIL, dptr, "r=%d\n\r", r); + sim_activate(uptr, 5000); + } + return mt_error(uptr, chan, r, dptr); + } else { + uptr->u5 &= ~(MT_BOT|MT_EOT); + uptr->hwmark = reclen; + } + sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark); + uptr->u6 = 0; + if ((uptr->u5 & MT_BIN) == 0) + mode = 0100; + else + mode = 0; + for (loc = 0; loc < reclen; loc++) { + ch = mt_buffer[chan][loc] & 0177; + if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) { + chan_set_error(chan); + break; + } + } + } + ch = mt_buffer[chan][uptr->u6++] & 0177; + /* 00 characters are not transfered in BCD mode */ + if (ch == 0) { + if (((uint32)uptr->u6) >= uptr->hwmark) { + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_OK, dptr); + } else { + sim_activate(uptr, HT); + return SCPE_OK; + } + } + + if (chan_write_char(chan, &ch, + (((uint32)uptr->u6) >= uptr->hwmark) ? 1 : 0)) { + sim_debug(DEBUG_DATA, dptr, "Read unit=%d %d EOR\n\r", unit, + uptr->hwmark-uptr->u6); + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_OK, dptr); + } else { + sim_debug(DEBUG_DATA, dptr, "Read data unit=%d %d %03o\n\r", + unit, uptr->u6, ch); + sim_activate(uptr, HT); + } + return SCPE_OK; + + case MT_RDBK: /* Read Backword */ + /* If at end of record, fill buffer */ + if (uptr->hwmark == -1) { + sim_debug(DEBUG_DETAIL, dptr, "Read back unit=%d %s ", unit, + (uptr->u5 & MT_BIN)? "bin": "bcd"); + if (sim_tape_bot(uptr)) { + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_BOT, dptr); + } + r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); + if (r != MTSE_OK) { + if (r == MTSE_TMK) { + sim_debug(DEBUG_DETAIL, dptr, "TM\n\r"); + ch = 017; + (void)chan_write_char(chan, &ch, 1); + sim_activate(uptr, 4000); + } else { + uptr->u5 |= MT_BSY; + sim_debug(DEBUG_DETAIL, dptr, "r=%d\n\r", r); + sim_activate(uptr, 100); + } + return mt_error(uptr, chan, r, dptr); + } else { + uptr->u5 &= ~(MT_BOT|MT_EOT); + uptr->hwmark = reclen; + } + sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark); + uptr->u6 = uptr->hwmark; + if ((uptr->u5 & MT_BIN) == 0) + mode = 0100; + else + mode = 0; + for (loc = 0; loc < reclen; loc++) { + ch = mt_buffer[chan][loc] & 0177; + if (((parity_table[ch & 077]) ^ (ch & 0100) ^ mode) == 0) { + chan_set_error(chan); + break; + } + } + } + ch = mt_buffer[chan][--uptr->u6] & 0177; + /* 00 characters are not transfered in BCD mode */ + if (ch == 0) { + if (uptr->u6 <= 0) { + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_OK, dptr); + } else { + sim_activate(uptr, HT); + return SCPE_OK; + } + } + + if (chan_write_char(chan, &ch, (uptr->u6 > 0) ? 0 : 1)) { + sim_debug(DEBUG_DATA, dptr, "Read back unit=%d %d EOR\n\r", + unit, uptr->hwmark-uptr->u6); + sim_activate(uptr, 100); + return mt_error(uptr, chan, MTSE_OK, dptr); + } else { + sim_debug(DEBUG_DATA, dptr, "Read back data unit=%d %d %03o\n\r", + unit, uptr->u6, ch); + sim_activate(uptr, HT); + } + return SCPE_OK; + + case MT_WR: /* Write */ + /* Check if write protected */ + if (uptr->u6 == 0 && sim_tape_wrp(uptr)) { + sim_activate(uptr, 100); + return mt_error(uptr, chan, MTSE_WRP, dptr); + } + if (chan_read_char(chan, &ch, + (uptr->u6 > BUFFSIZE) ? 1 : 0)) { + reclen = uptr->u6; + /* If no transfer, then either erase */ + if (reclen == 0) { + sim_debug(DEBUG_DETAIL, dptr, "Erase\n\r"); + r = MTSE_OK; + } else if ((reclen == 1) && (cmd & MT_BIN) == 0 && + (mt_buffer[chan][0] == 017)) { + /* Check if write rtape mark */ + sim_debug(DEBUG_DETAIL, dptr, "Write Mark unit=%d\n\r", unit); + r = sim_tape_wrtmk(uptr); + } else { + sim_debug(DEBUG_DETAIL, dptr, + "Write unit=%d Block %d %s chars\n\r", unit, reclen, + (uptr->u5 & MT_BIN)? "bin": "bcd"); + r = sim_tape_wrrecf(uptr, &mt_buffer[chan][0], reclen); + } + uptr->u5 &= ~(MT_BOT|MT_EOT); + sim_activate(uptr, 4000); + return mt_error(uptr, chan, r, dptr); /* Record errors */ + } else { + /* Copy data to buffer */ + ch &= 077; + ch |= parity_table[ch]; + if ((uptr->u5 & MT_BIN)) + ch ^= 0100; + /* Don't write out even parity zeros */ + if (ch != 0) + mt_buffer[chan][uptr->u6++] = ch; + sim_debug(DEBUG_DATA, dptr, "Write data unit=%d %d %03o\n\r", + unit, uptr->u6, ch); + uptr->hwmark = uptr->u6; + } + sim_activate(uptr, HT); + return SCPE_OK; + + case MT_FSR: /* Space forward one record */ + if (uptr->hwmark == -1) { + /* If at end of record, fill buffer */ + sim_debug(DEBUG_DETAIL, dptr, "Space unit=%d ", unit); + if (sim_tape_eot(uptr)) { + uptr->u5 &= ~MT_BOT; + sim_debug(DEBUG_DETAIL, dptr, "EOT\r\n"); + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_EOM, dptr); + } + r = sim_tape_rdrecf(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); + if (r != MTSE_OK) { + if (r == MTSE_TMK) { + sim_debug(DEBUG_DETAIL, dptr, "TM "); + reclen = 1; + chan_set_eof(chan); + } else { + sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r); + reclen = 10; + } + } + uptr->u5 &= ~(MT_BOT|MT_EOT); + uptr->hwmark = reclen; + sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark); + sim_activate(uptr, uptr->hwmark * HT); + return SCPE_OK; + } + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_OK, dptr); + + case MT_BSR: /* Backspace record */ + if (uptr->hwmark == -1) { + /* If at end of record, fill buffer */ + sim_debug(DEBUG_DETAIL, dptr, "backspace unit=%d ", unit); + if (sim_tape_bot(uptr)) { + sim_debug(DEBUG_DETAIL, dptr, "BOT\n\r"); + sim_activate(uptr, 100); + return mt_error(uptr, chan, MTSE_BOT, dptr); + } + r = sim_tape_rdrecr(uptr, &mt_buffer[chan][0], &reclen, BUFFSIZE); + if (r != MTSE_OK) { + if (r == MTSE_TMK) { + sim_debug(DEBUG_DETAIL, dptr, "TM "); + reclen = 1; + chan_set_eof(chan); + } else { + reclen = 10; + sim_debug(DEBUG_DETAIL, dptr, "r=%d ", r); + } + } + uptr->u5 &= ~(MT_BOT|MT_EOT); + uptr->hwmark = reclen; + sim_debug(DEBUG_DETAIL, dptr, "%d chars\n\r", uptr->hwmark); + sim_activate(uptr, uptr->hwmark * HT); + return SCPE_OK; + } + sim_activate(uptr, 4000); + return mt_error(uptr, chan, MTSE_OK, dptr); + + case MT_REW: /* Rewind */ + sim_debug(DEBUG_DETAIL, dptr, "Rewind unit=%d pos=%d\n\r", unit, + uptr->pos); + uptr->u5 &= ~(MT_CMD | MT_BIN | MT_IDLE | MT_RDY); + uptr->u5 |= MT_BSY|MT_RDY; + iostatus &= ~(1 << (uptr - mt_unit)); + sim_activate(uptr, (uptr->pos/100) + 100); + r = sim_tape_rewind(uptr); + uptr->u5 &= ~MT_EOT; + uptr->u5 |= MT_BOT; + chan_set_end(chan); + return r; + } + return mt_error(uptr, chan, r, dptr); +} + + +t_stat +mt_attach(UNIT * uptr, char *file) +{ + t_stat r; + + if ((r = sim_tape_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 |= MT_LOADED|MT_BOT; + sim_activate(uptr, 50000); + return SCPE_OK; +} + +t_stat +mt_detach(UNIT * uptr) +{ + uptr->u5 = 0; + iostatus &= ~(1 << (uptr - mt_unit)); + return sim_tape_detach(uptr); +} + +t_stat +mt_reset(DEVICE *dptr) +{ + int i; + + /* Scan all devices and enable those that + are loaded. This is to allow tapes that + are mounted prior to boot to be recognized + at later. Also disconnect all devices no + longer connected. */ + for ( i = 0; i < NUM_DEVS_MT; i++) { + mt_unit[i].dynflags = MT_DENS_556 << UNIT_V_DF_TAPE; + if ((mt_unit[i].flags & UNIT_ATT) == 0) + iostatus &= ~(1 << i); + else if (mt_unit[i].u5 & (MT_LOADED|MT_RDY)) { + iostatus |= 1 << i; + mt_unit[i].u5 &= ~(MT_LOADED); + mt_unit[i].u5 |= MT_RDY; + } + } + return SCPE_OK; +} + +t_stat +mt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B422/B423 Magnetic tape unit\n\n"); + fprintf (st, "The magnetic tape controller assumes that all tapes are 7 track\n"); + fprintf (st, "with valid parity. Tapes are assumed to be 555.5 characters per\n"); + fprintf (st, "inch. To simulate a standard 2400foot tape, do:\n"); + fprintf (st, " sim> SET MTn LENGTH 15\n\n"); + fprintf (st, "By default only 8 drives are enabled, additional units up to 15 supported.\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +mt_description(DEVICE *dptr) +{ + return "B422/B423 Magnetic tape unit"; +} +#endif + + diff --git a/B5500/b5500_sys.c b/B5500/b5500_sys.c new file mode 100644 index 00000000..8ac940a7 --- /dev/null +++ b/B5500/b5500_sys.c @@ -0,0 +1,541 @@ +/* b5500_sys.c: Burroughs 5500 Simulator system interface. + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + +*/ + +#include "sim_defs.h" +#include "b5500_defs.h" +#include "sim_card.h" +#include + +t_stat parse_sym(char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "B5500"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, +#if NUM_DEVS_CDR > 0 + &cdr_dev, +#endif +#if NUM_DEVS_CDP > 0 + &cdp_dev, +#endif +#if NUM_DEVS_LPR > 0 + &lpr_dev, +#endif +#if NUM_DEVS_CON > 0 + &con_dev, +#endif +#if NUM_DEVS_MT > 0 + &mt_dev, +#endif +#if NUM_DEVS_DR > 0 + &drm_dev, +#endif +#if NUM_DEVS_DSK > 0 + &esu_dev, + &dsk_dev, +#endif +#if NUM_DEVS_DTC > 0 + &dtc_dev, +#endif + NULL +}; + +/* Simulator stop codes */ +const char *sim_stop_messages[] = { + 0, +}; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {0, 0} +}; + + +uint8 parity_table[64] = { + /* 0 1 2 3 4 5 6 7 */ + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000 +}; + +uint8 mem_to_ascii[64] = { + /* x0 x1 x2 x3 x4 x5 x6 x7 */ + '0', '1', '2', '3', '4', '5', '6', '7', /* 0x */ + '8', '9', '#', '@', '?', ':', '>', '}', /* 1x */ + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 2x */ + 'H', 'I', '.', '[', '&', '(', '<', '~', /* 3x */ + '|', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 4x */ + 'Q', 'R', '$', '*', '-', ')', ';', '{', /* 5x */ + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 6x */ + 'Y', 'Z', ',', '%', '!', '=', ']', '"' /* 7x */ +}; + +const char con_to_ascii[64] = { + '?', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ':', '>', '}', /* 17 = box */ + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '!', ',', '%', '=', ']', '"', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '|', '$', '*', ')', ';', '{', /* 57 = triangle */ + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '+', '.', '[', '(', '<', '~', /* 37 = stop code */ +}; /* 72 = rec mark */ + /* 75 = squiggle, 77 = del */ + +const char ascii_to_con[128] = { + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /*sp ! " # $ % & ' */ + 020, 032, 037, 013, 053, 017, 060, 014, /* 40 - 77 */ + /* ( ) * + , - . / */ + 075, 055, 054, 072, 033, 040, 073, 021, + /* 0 1 2 3 4 5 6 7 */ + 012, 001, 002, 003, 004, 005, 006, 007, + /* 8 9 : ; < = > ? */ + 010, 011, 015, 056, 076, 035, 016, 000, + /* @ A B C D E F G */ + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + /* H I J K L M N O */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* P Q R S T U V W */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* X Y Z [ \ ] ^ _ */ + 027, 030, 031, 075, 036, 055, 057, 000, + /* ` a b c d e f g */ + 035, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + /* h i j k l m n o */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* p q r s t u v w */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* x y z { | } ~ del*/ + 027, 030, 031, 057, 077, 017, -1, -1 +}; + + +/* Load a card image file into memory. */ + +t_stat +sim_load(FILE * fileref, char *cptr, char *fnam, int flag) +{ + /* Currently not implimented until I know format of load files */ + return SCPE_NOFNC; +} + +#define TYPE_A 1 /* Full 12 bit opcode */ +#define TYPE_B 2 /* 6 Bit Opcode with 6 bit field */ +#define TYPE_C 3 /* 8 Bit opcode with 4 bit field */ +#define TYPE_D 4 /* 2 bit opcode, 10 bit field */ + +/* Opcodes */ +t_opcode word_ops[] = { +/* Word mode opcodes */ + WMOP_LITC, TYPE_D, "LITC", /* Load literal */ + WMOP_OPDC, TYPE_D, "OPDC", /* Load operand */ + WMOP_DESC, TYPE_D, "DESC", /* Load Descriptor */ + WMOP_DEL, TYPE_A, "DEL", /* Delete top of stack */ + WMOP_NOP, TYPE_A, "NOP", /* Nop operation */ + WMOP_XRT, TYPE_A, "XRT", /* Set Variant */ + WMOP_ADD, TYPE_A, "ADD", /* Add */ + WMOP_DLA, TYPE_A, "DLA", /* Double Precision Add */ + WMOP_PRL, TYPE_A, "PRL", /* Program Release */ + WMOP_LNG, TYPE_A, "LNG", /* Logical Negate */ + WMOP_CID, TYPE_A, "CID", /* Conditional Integer Store Destructive */ + WMOP_GEQ, TYPE_A, "GEQ", /* B greater than or equal to A */ + WMOP_BBC, TYPE_A, "BBC", /* Branch Backward Conditional */ + WMOP_BRT, TYPE_A, "BRT", /* Branch Return */ + WMOP_INX, TYPE_A, "INX", /* Index */ + WMOP_ITI, TYPE_A, "ITI", /* Interrogate interrupt */ + WMOP_LOR, TYPE_A, "LOR", /* Logical Or */ + WMOP_CIN, TYPE_A, "CIN", /* Conditional Integer Store non-destructive */ + WMOP_GTR, TYPE_A, "GTR", /* B Greater than A */ + WMOP_BFC, TYPE_A, "BFC", /* Branch Forward Conditional */ + WMOP_RTN, TYPE_A, "RTN", /* Return normal */ + WMOP_COC, TYPE_A, "COC", /* Construct Operand Call */ + WMOP_SUB, TYPE_A, "SUB", /* Subtract */ + WMOP_DLS, TYPE_A, "DLS", /* Double Precision Subtract */ + WMOP_MUL, TYPE_A, "MUL", /* Multiply */ + WMOP_DLM, TYPE_A, "DLM", /* Double Precision Multiply */ + WMOP_RTR, TYPE_A, "RTR", /* Read Timer */ + WMOP_LND, TYPE_A, "LND", /* Logical And */ + WMOP_STD, TYPE_A, "STD", /* B Store Destructive */ + WMOP_NEQ, TYPE_A, "NEQ", /* B Not equal to A */ + WMOP_SSN, TYPE_A, "SSN", /* Set Sign Bit */ + WMOP_XIT, TYPE_A, "XIT", /* Exit */ + WMOP_MKS, TYPE_A, "MKS", /* Mark Stack */ + WMOP_DIV, TYPE_A, "DIV", /* Divide */ + WMOP_DLD, TYPE_A, "DLD", /* Double Precision Divide */ + WMOP_COM, TYPE_A, "COM", /* Communication operator */ + WMOP_LQV, TYPE_A, "LQV", /* Logical Equivalence */ + WMOP_SND, TYPE_A, "SND", /* B Store Non-destructive */ + WMOP_XCH, TYPE_A, "XCH", /* Exchange */ + WMOP_CHS, TYPE_A, "CHS", /* Change sign bit */ + WMOP_RTS, TYPE_A, "RTS", /* Return Special */ + WMOP_CDC, TYPE_A, "CDC", /* Construct descriptor call */ + WMOP_FTC, TYPE_A, "FTC", /* Transfer F Field to Core Field */ + WMOP_MOP, TYPE_A, "MOP", /* Reset Flag bit */ + WMOP_LOD, TYPE_A, "LOD", /* Load */ + WMOP_DUP, TYPE_A, "DUP", /* Duplicate */ + WMOP_TOP, TYPE_A, "TOP", /* Test Flag Bit */ + WMOP_IOR, TYPE_A, "IOR", /* I/O Release */ + WMOP_LBC, TYPE_A, "LBC", /* Word Branch Backward Conditional */ + WMOP_SSF, TYPE_A, "SSF", /* Set or Store S or F registers */ + WMOP_HP2, TYPE_A, "HP2", /* Halt P2 */ + WMOP_LFC, TYPE_A, "LFC", /* Word Branch Forward Conditional */ + WMOP_ZP1, TYPE_A, "ZP1", /* Conditional Halt */ + WMOP_TUS, TYPE_A, "TUS", /* Interrogate Peripheral Status */ + WMOP_LLL, TYPE_A, "LLL", /* Link List Look-up */ + WMOP_IDV, TYPE_A, "IDV", /* Integer Divide Integer */ + WMOP_SFI, TYPE_A, "SFI", /* Store for Interrupt */ + WMOP_SFT, TYPE_A, "SFT", /* Store for Test */ + WMOP_FTF, TYPE_A, "FTF", /* Transfer F Field to F Field */ + WMOP_MDS, TYPE_A, "MDS", /* Set Flag Bit */ + WMOP_IP1, TYPE_A, "IP1", /* Initiate P1 */ + WMOP_ISD, TYPE_A, "ISD", /* Interger Store Destructive */ + WMOP_LEQ, TYPE_A, "LEQ", /* B Less Than or Equal to A */ + WMOP_BBW, TYPE_A, "BBW", /* Banch Backward Conditional */ + WMOP_IP2, TYPE_A, "IP2", /* Initiate P2 */ + WMOP_ISN, TYPE_A, "ISN", /* Integer Store Non-Destructive */ + WMOP_LSS, TYPE_A, "LSS", /* B Less Than A */ + WMOP_BFW, TYPE_A, "BFW", /* Branch Forward Unconditional */ + WMOP_IIO, TYPE_A, "IIO", /* Initiate I/O */ + WMOP_EQL, TYPE_A, "EQL", /* B Equal A */ + WMOP_SSP, TYPE_A, "SSP", /* Reset Sign Bit */ + WMOP_CMN, TYPE_A, "CMN", /* Enter Character Mode In Line */ + WMOP_IFT, TYPE_A, "IFT", /* Test Initiate */ + WMOP_CTC, TYPE_A, "CTC", /* Transfer Core Field to Core Field */ + WMOP_LBU, TYPE_A, "LBU", /* Word Branch Backward Unconditional */ + WMOP_LFU, TYPE_A, "LFU", /* Word Branch Forward Unconditional */ + WMOP_TIO, TYPE_A, "TIO", /* Interrogate I/O Channels */ + WMOP_RDV, TYPE_A, "RDV", /* Remainder Divide */ + WMOP_FBS, TYPE_A, "FBS", /* Flag Bit Search */ + WMOP_CTF, TYPE_A, "CTF", /* Transfer Core Field to F Field */ + WMOP_ISO, TYPE_B, "ISO", /* Variable Field Isolate XX */ + WMOP_CBD, TYPE_C, "CBD", /* Non-Zero Field Branch Backward Destructive Xy */ + WMOP_CBN, TYPE_C, "CBN", /* Non-Zero Field Branch Backward Non-Destructive Xy */ + WMOP_CFD, TYPE_C, "CFD", /* Non-Zero Field Branch Forward Destructive Xy */ + WMOP_CFN, TYPE_B, "CFN", /* Non-Zero Field Branch Forward Non-Destructive Xy */ + WMOP_DIA, TYPE_B, "DIA", /* Dial A XX */ + WMOP_DIB, TYPE_B, "DIB", /* Dial B XX Upper 6 not Zero */ + WMOP_TRB, TYPE_B, "TRB", /* Transfer Bits XX */ + WMOP_FCL, TYPE_B, "FCL", /* Compare Field Low XX */ + WMOP_FCE, TYPE_B, "FCE", /* Compare Field Equal XX */ + {0, 0, NULL}, +}; + +t_opcode char_ops[] = { +/* Character Mode */ + CMOP_EXC, TYPE_A, "EXC", /* Exit Character Mode */ + CMOP_CMX, TYPE_A, "CMX", /* Exit Character Mode In Line */ + CMOP_BSD, TYPE_B, "BSD", /* Skip Bit Destiniation */ + CMOP_BSS, TYPE_B, "BSS", /* SKip Bit Source */ + CMOP_RDA, TYPE_B, "RDA", /* Recall Destination Address */ + CMOP_TRW, TYPE_B, "TRW", /* Transfer Words */ + CMOP_SED, TYPE_B, "SED", /* Set Destination Address */ + CMOP_TDA, TYPE_B, "TDA", /* Transfer Destination Address */ + CMOP_TBN, TYPE_B, "TBN", /* Transfer Blanks for Non-Numerics */ + WMOP_ITI, TYPE_A, "ITI", /* Interrogate interrupt */ + WMOP_SFI, TYPE_A, "SFI", /* Store for Interrupt */ + WMOP_SFT, TYPE_A, "SFT", /* Store for Test */ + WMOP_ZP1, TYPE_A, "ZP1", /* Conditional Halt */ + WMOP_HP2, TYPE_A, "HP2", /* Halt P2 */ + CMOP_SDA, TYPE_B, "SDA", /* Store Destination Address */ + CMOP_SSA, TYPE_B, "SSA", /* Store Source Address */ + CMOP_SFD, TYPE_B, "SFD", /* Skip Forward Destination */ + CMOP_SRD, TYPE_B, "SRD", /* Skip Reverse Destination */ + CMOP_SES, TYPE_B, "SES", /* Set Source Address */ + CMOP_TEQ, TYPE_B, "TEQ", /* Test for Equal */ + CMOP_TNE, TYPE_B, "TNE", /* Test for Not-Equal */ + CMOP_TEG, TYPE_B, "TEG", /* Test for Greater Or Equal */ + CMOP_TGR, TYPE_B, "TGR", /* Test For Greater */ + CMOP_SRS, TYPE_B, "SRS", /* Skip Reverse Source */ + CMOP_SFS, TYPE_B, "SFS", /* Skip Forward Source */ + CMOP_TEL, TYPE_B, "TEL", /* Test For Equal or Less */ + CMOP_TLS, TYPE_B, "TLS", /* Test For Less */ + CMOP_TAN, TYPE_B, "TAN", /* Test for Alphanumeric */ + CMOP_BIT, TYPE_B, "BIT", /* Test Bit */ + CMOP_INC, TYPE_B, "INC", /* Increase Tally */ + CMOP_STC, TYPE_B, "STC", /* Store Tally */ + CMOP_SEC, TYPE_B, "SEC", /* Set Tally */ + CMOP_CRF, TYPE_B, "CRF", /* Call repeat Field */ + CMOP_JNC, TYPE_B, "JNC", /* Jump Out Of Loop Conditional */ + CMOP_JFC, TYPE_B, "JFC", /* Jump Forward Conditional */ + CMOP_JNS, TYPE_B, "JNS", /* Jump out of loop unconditional */ + CMOP_JFW, TYPE_B, "JFW", /* Jump Forward Unconditional */ + CMOP_RCA, TYPE_B, "RCA", /* Recall Control Address */ + CMOP_ENS, TYPE_B, "ENS", /* End Loop */ + CMOP_BNS, TYPE_B, "BNS", /* Begin Loop */ + CMOP_RSA, TYPE_B, "RSA", /* Recall Source Address */ + CMOP_SCA, TYPE_B, "SCA", /* Store Control Address */ + CMOP_JRC, TYPE_B, "JRC", /* Jump Reverse Conditional */ + CMOP_TSA, TYPE_B, "TSA", /* Transfer Source Address */ + CMOP_JRV, TYPE_B, "JRV", /* Jump Reverse Unconditional */ + CMOP_CEQ, TYPE_B, "CEQ", /* Compare Equal */ + CMOP_CNE, TYPE_B, "CNE", /* COmpare for Not Equal */ + CMOP_CEG, TYPE_B, "CEG", /* Compare For Greater Or Equal */ + CMOP_CGR, TYPE_B, "CGR", /* Compare For Greater */ + CMOP_BIS, TYPE_B, "BIS", /* Set Bit */ + CMOP_BIR, TYPE_B, "BIR", /* Reet Bit */ + CMOP_OCV, TYPE_B, "OCV", /* Output Convert */ + CMOP_ICV, TYPE_B, "ICV", /* Input Convert */ + CMOP_CEL, TYPE_B, "CEL", /* Compare For Equal or Less */ + CMOP_CLS, TYPE_B, "CLS", /* Compare for Less */ + CMOP_FSU, TYPE_B, "FSU", /* Field Subtract */ + CMOP_FAD, TYPE_B, "FAD", /* Field Add */ + CMOP_TRP, TYPE_B, "TRP", /* Transfer Program Characters */ + CMOP_TRN, TYPE_B, "TRN", /* Transfer Numeric */ + CMOP_TRZ, TYPE_B, "TRZ", /* Transfer Zones */ + CMOP_TRS, TYPE_B, "TRS", /* Transfer Source Characters */ + {0, 0, NULL}, +}; + + +/* Print out an instruction */ +void +print_opcode(FILE * of, t_value val, t_opcode * tab) +{ + uint16 op; + + op = val; + while (tab->name != NULL) { + switch(tab->type) { + case TYPE_A: + if (op != tab->op) + break; + fputs(tab->name, of); + fputs(" ",of); + return; + case TYPE_B: + if ((op & 077) != tab->op) + break; + fputs(tab->name, of); + fputc(' ',of); + fputc(' ',of); + fprint_val(of, (val >> 6), 8, 6, 0); + fputs(" ",of); + return; + case TYPE_C: + if ((op & 0377) != tab->op) + break; + fputs(tab->name, of); + fputc(' ',of); + fprint_val(of, (val >> 8), 8, 4, 0); + fputs(" ",of); + return; + case TYPE_D: + if ((op & 03) != tab->op) + break; + fputs(tab->name, of); + fputc(' ',of); + fprint_val(of, (val >> 2), 8, 10, 0); + fputc(' ',of); + return; + } + tab++; + } + fprintf(of, "*%04o uuo ", op); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat +fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw) +{ + t_value inst = *val; + int i; + + fputc(' ', of); + fprint_val(of, inst, 8, 48, PV_RZRO); + + if (sw & SWMASK('W')) { /* Word mode opcodes */ + fputs(" ", of); + for (i = 36; i >= 0; i-=12) { + int op = (int)(inst >> i) & 07777; + print_opcode(of, op, word_ops); + } + } + if (sw & SWMASK('C')) { /* Char mode opcodes */ + fputs(" ", of); + for (i = 36; i >= 0; i-=12) { + int op = (int)(inst >> i) & 07777; + print_opcode(of, op, char_ops); + } + } + if (sw & SWMASK('B')) { /* BCD mode */ + fputs(" '", of); + for (i = 42; i >= 0; i-=6) { + int ch; + + ch = (int)(inst >> i) & 077; + fputc(mem_to_ascii[ch], of); + } + fputc('\'', of); + } + if (sw & SWMASK('F')) { /* Floating point/Descriptor */ + } + return SCPE_OK; +} + +t_opcode * +find_opcode(char *op, t_opcode * tab) +{ + while (tab->name != NULL) { + if (*tab->name != '\0' && strcmp(op, tab->name) == 0) + return tab; + tab++; + } + return NULL; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat +parse_sym(char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw) +{ + int i; + t_value d; + int opr; + char opcode[100]; + + while (isspace(*cptr)) + cptr++; + d = 0; + if (sw & (SWMASK('W')|SWMASK('C'))) { + t_opcode *op; + + /* Grab opcode */ + cptr = get_glyph(cptr, opcode, 0); + + op = 0; + opr = -1; + if((op = find_opcode(opcode, + (SWMASK('W') ? word_ops : char_ops))) == 0) { + return SCPE_UNK; + } + + while (*cptr == ' ' || *cptr == '\t') + cptr++; + /* Collect first argument if there is one */ + while (*cptr >= '0' && *cptr <= '7') + opr = (opr << 3) + (*cptr++ - '0'); + /* Skip blanks */ + while (*cptr == ' ' || *cptr == '\t') + cptr++; + switch (op->type) { + case TYPE_A: + if (opr >= 0) + return SCPE_2MARG; + *val = op->op; + return SCPE_OK; + + case TYPE_B: + if (opr < 0 || opr > 64) + return SCPE_ARG; + *val = (opr << 6) | op->op; + return SCPE_OK; + + case TYPE_C: + if (opr < 0 || opr > 16) + return SCPE_ARG; + *val = (opr << 8) | op->op; + return SCPE_OK; + + case TYPE_D: + if (opr < 0 || opr > 1024) + return SCPE_ARG; + *val = (opr << 2) | op->op; + return SCPE_OK; + } + } else if (sw & SWMASK('B')) { + i = 0; + while (*cptr != '\0' && i < 8) { + d <<= 6; + d |= (int)sim_ascii_to_six[0177 & *cptr]; + cptr++; + i++; + } + d <<= 6 * (8 - i); + } else { + while (*cptr >= '0' && *cptr <= '7') { + d <<= 3; + d |= *cptr++ - '0'; + } + } + *val = d; + return SCPE_OK; + +/* Symbolic input, continued */ + + if (*cptr != 0) + return SCPE_ARG; /* junk at end? */ + return SCPE_OK; +} diff --git a/B5500/b5500_urec.c b/B5500/b5500_urec.c new file mode 100644 index 00000000..f6f7b4c6 --- /dev/null +++ b/B5500/b5500_urec.c @@ -0,0 +1,1007 @@ +/* b5500_urec.c: Burrioughs 5500 Unit record devices. + + Copyright (c) 2016, Richard Cornwell + + 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 + RICHARD CORNWELL 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. + + This is the standard card reader. 10,14 + This is the standard card punch. 10 + This is the standard line printer. 22,26 + This is the standard operators console. 30 + +*/ + +#include "b5500_defs.h" +#include "sim_card.h" +#include "sim_defs.h" +#include "sim_console.h" + +#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | UNIT_ROABLE | \ + MODE_029 +#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029 +#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE + +/* For Card reader, when set returns end of file at end of deck. */ +/* Reset after sent to system */ +#define MODE_EOF (0x40 << UNIT_V_MODE) + + + +/* std devices. data structures + + cdr_dev Card Reader device descriptor + cdr_unit Card Reader unit descriptor + cdr_reg Card Reader register list + cdr_mod Card Reader modifiers list +*/ + +/* Device status information stored in u5 */ +#define URCSTA_CHMASK 0003 /* Mask of I/O channel to send data on */ +#define URCSTA_CARD 0004 /* Unit has card in buffer */ +#define URCSTA_FULL 0004 /* Unit has full buffer */ +#define URCSTA_BUSY 0010 /* Device is busy */ +#define URCSTA_BIN 0020 /* Card reader in binary mode */ +#define URCSTA_ACTIVE 0040 /* Unit is active */ +#define URCSTA_EOF 0100 /* Flag the end of file */ +#define URCSTA_INPUT 0200 /* Console fill buffer from keyboard */ +#define URCSTA_FILL 010000 /* Fill unit buffer */ +#define URCSTA_CMD_V 16 + +#define URCSTA_SKIP 000017 /* Skip mask */ +#define URCSTA_DOUBLE 000020 /* Double space skip */ +#define URCSTA_SINGLE 000040 /* Single space skip. */ +#define URCSTA_READ 000400 /* Read flag */ +#define URCSTA_WC 001000 /* Use word count */ +#define URCSTA_DIRECT 002000 /* Direction, Long line */ +#define URCSTA_BINARY 004000 /* Binary transfer */ +#define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */ + +/* Simulator debug controls */ +DEBTAB cdr_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show console data"}, + {"CARD", DEBUG_CARD, "Show Card read/punches"}, + {0, 0} +}; + + +#if NUM_DEVS_CDR > 0 +t_stat cdr_boot(int32, DEVICE *); +t_stat cdr_srv(UNIT *); +t_stat cdr_attach(UNIT *, char *); +t_stat cdr_detach(UNIT *); +t_stat cdr_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *cdr_description(DEVICE *dptr); +#endif + +#if NUM_DEVS_CDP > 0 +t_stat cdp_srv(UNIT *); +t_stat cdp_attach(UNIT *, char *); +t_stat cdp_detach(UNIT *); +t_stat cdp_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *cdp_description(DEVICE *dptr); +#endif + +#if NUM_DEVS_LPR > 0 +struct _lpr_data +{ + uint8 lbuff[145]; /* Output line buffer */ +} +lpr_data[NUM_DEVS_LPR]; + +t_stat lpr_srv(UNIT *); +t_stat lpr_attach(UNIT *, char *); +t_stat lpr_detach(UNIT *); +t_stat lpr_setlpp(UNIT *, int32, char *, void *); +t_stat lpr_getlpp(FILE *, UNIT *, int32, void *); +t_stat lpr_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *lpr_description(DEVICE *dptr); +#endif + +#if NUM_DEVS_CON > 0 +struct _con_data +{ + uint8 ibuff[145]; /* Input line buffer */ + uint8 inptr; + uint8 outptr; +} +con_data[NUM_DEVS_CON]; + +t_stat con_ini(DEVICE *); +t_stat con_srv(UNIT *); +t_stat con_attach(UNIT *, char *); +t_stat con_detach(UNIT *); +t_stat con_help(FILE *, DEVICE *, UNIT *, int32, const char *); +const char *con_description(DEVICE *dptr); +#endif + + + +#if NUM_DEVS_CDR > 0 +UNIT cdr_unit[] = { + {UDATA(cdr_srv, UNIT_CDR, 0)}, /* A */ +#if NUM_DEVS_CDR > 1 + {UDATA(cdr_srv, UNIT_CDR|UNIT_DIS, 0)}, /* B */ +#endif +}; + +MTAB cdr_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL, + "Sets card format"}, + {MODE_EOF, MODE_EOF, "EOF", "EOF", NULL, NULL, + "Causes EOF to be set when reader empty"}, + {0} +}; + +DEVICE cdr_dev = { + "CR", cdr_unit, NULL, cdr_mod, + NUM_DEVS_CDR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, &cdr_boot, &cdr_attach, &cdr_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, + NULL, NULL, &cdr_help, NULL, NULL, + &cdr_description +}; +#endif + +#if NUM_DEVS_CDP > 0 +UNIT cdp_unit[] = { + {UDATA(cdp_srv, UNIT_CDP, 0)}, /* A */ +}; + +MTAB cdp_mod[] = { + {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_card_set_fmt, &sim_card_show_fmt, NULL, + "Sets card format"}, + {0} +}; + +DEVICE cdp_dev = { + "CP", cdp_unit, NULL, cdp_mod, + NUM_DEVS_CDP, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, + NULL, NULL, &cdp_help, NULL, NULL, + &cdp_description +}; + +#endif + +#if NUM_DEVS_LPR > 0 +UNIT lpr_unit[] = { + {UDATA(lpr_srv, UNIT_LPR, 59)}, /* A */ +#if NUM_DEVS_LPR > 1 + {UDATA(lpr_srv, UNIT_LPR|UNIT_DIS, 59)}, /* B */ +#endif +}; + +MTAB lpr_mod[] = { + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpr_setlpp, &lpr_getlpp, NULL, + "Sets number of lines on a printed page"}, + {0} +}; + +DEVICE lpr_dev = { + "LP", lpr_unit, NULL, lpr_mod, + NUM_DEVS_LPR, 8, 15, 1, 8, 8, + NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lpr_help, NULL, NULL, + &lpr_description +}; +#endif + +#if NUM_DEVS_CON > 0 +UNIT con_unit[] = { + {UDATA(con_srv, 0, 0), 0}, /* A */ +}; + +DEVICE con_dev = { + "CON", con_unit, NULL, NULL, + NUM_DEVS_CON, 8, 15, 1, 8, 8, + NULL, NULL, con_ini, NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &con_help, NULL, NULL, + &con_description +}; +#endif + + + +#if ((NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0)) +/* + * Device entry points for card reader. + * And Card punch. + */ +t_stat card_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr; + int u; + + if (dev == CARD1_DEV) + u = 0; + else if (dev == CARD2_DEV) + u = 1; + else + return SCPE_NXDEV; + /* Check if card reader or card punch */ + if (cmd & URCSTA_READ) { + uptr = &cdr_unit[u]; + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + + /* Are we currently tranfering? */ + if (uptr->u5 & URCSTA_ACTIVE) + return SCPE_BUSY; + + /* Check if we ran out of cards */ + if (uptr->u5 & URCSTA_EOF) { + /* If end of file, return to system */ + if (uptr->flags & MODE_EOF) { + sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d report eof\n", u, + chan); + chan_set_eof(chan); + uptr->flags &= ~MODE_EOF; + } + /* Clear unit ready */ + iostatus &= ~(CARD1_FLAG << u); + return SCPE_UNATT; + } + + if (cmd & URCSTA_BINARY) { + uptr->u5 |= URCSTA_BIN; + *wc = 20; + } else { + uptr->u5 &= ~URCSTA_BIN; + *wc = 10; + } + + uptr->u5 &= ~URCSTA_CHMASK; + uptr->u5 |= URCSTA_ACTIVE|chan; + uptr->u4 = 0; + + sim_activate(uptr, 500000); + return SCPE_OK; + } else { + /* Talking to punch */ + if (u != 0) + return SCPE_NXDEV; + sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d start\n", u, chan); + uptr = &cdp_unit[0]; + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + if (uptr->u5 & URCSTA_ACTIVE) + return SCPE_BUSY; + uptr->u5 &= ~URCSTA_CHMASK; + uptr->u5 |= URCSTA_ACTIVE|chan; + uptr->u4 = 0; + *wc = 10; + + sim_activate(uptr, 500000); + sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d go\n", u, chan); + return SCPE_OK; + } + return SCPE_IOERR; +} + +/* Handle transfer of data for card reader */ +t_stat +cdr_srv(UNIT *uptr) { + int chan = URCSTA_CHMASK & uptr->u5; + int u = (uptr - cdr_unit); + + if (uptr->u5 & URCSTA_EOF) { + sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d unready\n", u, chan); + iostatus &= ~(CARD1_FLAG << u); + uptr->u5 &= ~ URCSTA_EOF; + return SCPE_OK; + } + + + /* Check if new card requested. */ + if (uptr->u4 == 0 && uptr->u5 & URCSTA_ACTIVE && + (uptr->u5 & URCSTA_CARD) == 0) { + switch(sim_read_card(uptr)) { + case SCPE_UNATT: + iostatus &= ~(CARD1_FLAG << u); + uptr->u5 &= ~(URCSTA_ACTIVE); + iostatus &= ~(CARD1_FLAG << u); + chan_set_notrdy(chan); + break; + case SCPE_EOF: + /* If end of file, return to system */ + if (uptr->flags & MODE_EOF) { + sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d set eof\n", u, chan); + chan_set_eof(chan); + uptr->flags &= ~MODE_EOF; + } + uptr->u5 &= ~(URCSTA_ACTIVE); + uptr->u5 |= URCSTA_EOF; + chan_set_notrdy(chan); + sim_activate(uptr, 500); + break; + case SCPE_IOERR: + chan_set_error(chan); + uptr->u5 &= ~(URCSTA_ACTIVE); + uptr->u5 |= URCSTA_EOF; + chan_set_end(chan); + break; + case SCPE_OK: + uptr->u5 |= URCSTA_CARD; + sim_activate(uptr, 500); + break; + } + return SCPE_OK; + } + + + /* Copy next column over */ + if (uptr->u5 & URCSTA_CARD && + uptr->u4 <= ((uptr->u5 & URCSTA_BIN) ? 160 : 80)) { + struct _card_data *data; + uint8 ch = 0; + int u = (uptr - cdr_unit); + + data = (struct _card_data *)uptr->up7; + + if (uptr->u5 & URCSTA_BIN) { + ch = (data->image[uptr->u4 >> 1] >> + ((uptr->u4 & 1)? 0 : 6)) & 077; + } else { + ch = sim_hol_to_bcd(data->image[uptr->u4]); + /* Remap some characters from 029 to BCL */ + switch(ch) { + case 0: ch = 020; break; /* Translate blanks */ + case 10: /* Check if 0 punch of 82 punch */ + if (data->image[uptr->u4] != 0x200) { + ch = 0; + if (uptr->u4 == 0) + chan_set_parity(chan); + } + break; + case 0111: + ch = 0; + /* Handle invalid punch */ + chan_set_parity(chan); + break; /* Translate ? to error*/ + } + } + if(chan_write_char(chan, &ch, 0)) { + uptr->u5 &= ~(URCSTA_ACTIVE|URCSTA_CARD); + chan_set_end(chan); + /* Drop ready a bit after the last card is read */ + if (sim_card_eof(uptr)) { + uptr->u5 |= URCSTA_EOF; + sim_activate(uptr, 100); + } + } else { + uptr->u4++; + sim_activate(uptr, 100); + } + sim_debug(DEBUG_DATA, &cdr_dev, "cdr %d: Char > %03o '%c' %d\n", u, ch, + sim_six_to_ascii[ch & 077], uptr->u4); + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +cdr_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + uint8 dev; + t_uint64 desc; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + dev = (uptr == &cdr_unit[0]) ? CARD1_DEV : CARD2_DEV; + uptr->u5 &= ~URCSTA_ACTIVE; + desc = ((t_uint64)dev) << DEV_V | DEV_IORD| DEV_BIN | 020LL; + /* Read in one record */ + return chan_boot(desc); +} + +t_stat +cdr_attach(UNIT * uptr, char *file) +{ + t_stat r; + int u = uptr-cdr_unit; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 &= URCSTA_BUSY; + uptr->u4 = 0; + uptr->u6 = 0; + iostatus |= (CARD1_FLAG << u); + return SCPE_OK; +} + +t_stat +cdr_detach(UNIT * uptr) +{ + int u = uptr-cdr_unit; + + iostatus &= ~(CARD1_FLAG << u); + return sim_card_detach(uptr); +} + +t_stat +cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B124 Card Reader\n\n"); + fprintf (st, "The system supports up to two card readers, the second one is disabled\n"); + fprintf (st, "by default. To have the card reader return the EOF flag when the deck\n"); + fprintf (st, "has finished reading do:\n"); + fprintf (st, " sim> SET CRn EOF\n"); + fprintf (st, "This flag is cleared each time a deck has been read, so it must be set\n"); + fprintf (st, "again after each deck. MCP does not require this to be set as long as\n"); + fprintf (st, "the deck includes a ?END card\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cdr_description(DEVICE *dptr) +{ + return "B124 Card Reader"; +} + +#endif + + + +#if NUM_DEVS_CDR > 0 | NUM_DEVS_CDP > 0 + +/* Handle transfer of data for card punch */ +t_stat +cdp_srv(UNIT *uptr) { + int chan = URCSTA_CHMASK & uptr->u5; + int u = (uptr - cdp_unit); + + if (uptr->u5 & URCSTA_BUSY) { + /* Done waiting, punch card */ + if (uptr->u5 & URCSTA_FULL) { + sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d punch\n", u, chan); + switch(sim_punch_card(uptr, NULL)) { + case SCPE_EOF: + case SCPE_UNATT: + sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d set eof\n", u, + chan); + chan_set_eof(chan); + break; + /* If we get here, something is wrong */ + case SCPE_IOERR: + chan_set_error(chan); + break; + case SCPE_OK: + break; + } + uptr->u5 &= ~URCSTA_FULL; + chan_set_end(chan); + } + uptr->u5 &= ~URCSTA_BUSY; + } + + /* Copy next column over */ + if (uptr->u5 & URCSTA_ACTIVE && uptr->u4 <= 80) { + struct _card_data *data; + uint8 ch = 0; + + data = (struct _card_data *)uptr->up7; + + if(chan_read_char(chan, &ch, 0)) { + uptr->u5 |= URCSTA_BUSY|URCSTA_FULL; + uptr->u5 &= ~URCSTA_ACTIVE; + } else { + sim_debug(DEBUG_DATA, &cdp_dev, "cdp %d: Char %d < %02o\n", u, + uptr->u4, ch); + data->image[uptr->u4++] = sim_bcd_to_hol(ch & 077); + } + sim_activate(uptr, 10); + } + return SCPE_OK; +} + + +t_stat +cdp_attach(UNIT * uptr, char *file) +{ + t_stat r; + + if ((r = sim_card_attach(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + iostatus |= PUNCH_FLAG; + return SCPE_OK; +} + +t_stat +cdp_detach(UNIT * uptr) +{ + if (uptr->u5 & URCSTA_FULL) + sim_punch_card(uptr, NULL); + iostatus &= ~PUNCH_FLAG; + return sim_card_detach(uptr); +} + +t_stat +cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B303 Card Punch\n\n"); + fprintf (st, "The B303 Card Punch is only capable of punching text decks, binary decks\n"); + fprintf (st, "where not supported.\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cdp_description(DEVICE *dptr) +{ + return "B303 Card Punch"; +} + +#endif + + +/* Line printer routines +*/ + +#if NUM_DEVS_LPR > 0 +t_stat +lpr_setlpp(UNIT *uptr, int32 val, char *cptr, void *desc) +{ + int i; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = 0; + while(*cptr != '\0') { + if (*cptr < '0' || *cptr > '9') + return SCPE_ARG; + i = (i * 10) + (*cptr++) - '0'; + } + if (i < 20 || i > 100) + return SCPE_ARG; + uptr->capac = i; + uptr->u4 = 0; + return SCPE_OK; +} + +t_stat +lpr_getlpp(FILE *st, UNIT *uptr, int32 v, void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "linesperpage=%d", uptr->capac); + return SCPE_OK; +} + +void +print_line(UNIT * uptr, int unit) +{ +/* Convert word record into column image */ +/* Check output type, if auto or text, try and convert record to bcd first */ +/* If failed and text report error and dump what we have */ +/* Else if binary or not convertable, dump as image */ + + char out[150]; /* Temp conversion buffer */ + int i; + int chan = uptr->u5 & URCSTA_CHMASK; + + if ((uptr->flags & (UNIT_ATT)) == 0) + return; /* attached? */ + + if (uptr->u3 > 0) { + /* Try to convert to text */ + memset(out, 0, sizeof(out)); + + /* Scan each column */ + for (i = 0; i < uptr->u3; i++) { + int bcd = lpr_data[unit].lbuff[i] & 077; + + out[i] = con_to_ascii[bcd]; + } + + /* Trim trailing spaces */ + for (--i; i > 0 && out[i] == ' '; i--) ; + out[i+1] = '\0'; + + sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr print %s\n", out); + if (uptr->u5 & (URCSTA_DOUBLE << URCSTA_CMD_V)) { + out[++i] = '\r'; + out[++i] = '\n'; + uptr->u4 ++; + } + out[++i] = '\r'; + out[++i] = '\n'; + uptr->u4++; + out[++i] = '\0'; + + /* Print out buffer */ + sim_fwrite(&out, 1, i, uptr->fileref); + uptr->u5 &= ~URCSTA_EOF; + } + + + switch ((uptr->u5 >> URCSTA_CMD_V) & URCSTA_SKIP) { + case 0: /* No special skip */ + break; + case 1: + case 2: /* Skip to top of form */ + case 12: + uptr->u4 = uptr->capac+1; + break; + + case 3: /* Even lines */ + if ((uptr->u4 & 1) == 1) { + sim_fwrite("\r", 1, 1, uptr->fileref); + sim_fwrite("\n", 1, 1, uptr->fileref); + uptr->u4++; + uptr->u5 &= ~URCSTA_EOF; + } + break; + case 4: /* Odd lines */ + if ((uptr->u4 & 1) == 0) { + sim_fwrite("\r", 1, 1, uptr->fileref); + sim_fwrite("\n", 1, 1, uptr->fileref); + uptr->u4++; + uptr->u5 &= ~URCSTA_EOF; + } + break; + case 5: /* Half page */ + while((uptr->u4 != (uptr->capac/2)) || + (uptr->u4 != (uptr->capac))) { + sim_fwrite("\r", 1, 1, uptr->fileref); + sim_fwrite("\n", 1, 1, uptr->fileref); + uptr->u4++; + if (((uint32)uptr->u4) > uptr->capac) { + uptr->u4 = 1; + break; + } + uptr->u5 &= ~URCSTA_EOF; + } + break; + case 6: /* 1/4 Page */ + while((uptr->u4 != (uptr->capac/4)) || + (uptr->u4 != (uptr->capac/2)) || + (uptr->u4 != (uptr->capac/2+uptr->capac/4)) || + (uptr->u4 != (uptr->capac))) { + sim_fwrite("\r", 1, 1, uptr->fileref); + sim_fwrite("\n", 1, 1, uptr->fileref); + uptr->u4++; + if (((uint32)uptr->u4) > uptr->capac) { + uptr->u4 = 1; + break; + } + uptr->u5 &= ~URCSTA_EOF; + } + break; + case 7: /* User defined, now 1 line */ + case 8: + case 9: + case 10: + case 11: + sim_fwrite("\r", 1, 1, uptr->fileref); + sim_fwrite("\n", 1, 1, uptr->fileref); + uptr->u4++; + break; + } + + + if (((uint32)uptr->u4) > uptr->capac) { + uptr->u4 = 1; + uptr->u5 |= URCSTA_EOF; + sim_fwrite("\f", 1, 1, uptr->fileref); + sim_fseek(uptr->fileref, 0, SEEK_CUR); + sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr %d page\n", unit); + } + +} + + + +t_stat lpr_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr; + int u; + + if (dev == PRT1_DEV) + u = 0; + else if (dev == PRT2_DEV) + u = 1; + else + return SCPE_NXDEV; + uptr = &lpr_unit[u]; + + /* Are we currently tranfering? */ + if (uptr->u5 & URCSTA_BUSY) + return SCPE_BUSY; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + + if (*wc == 0 && (cmd & URCSTA_INHIBIT) == 0) + *wc = (cmd & URCSTA_DIRECT) ? 17 : 15; + + /* Remember not to drop the FULL */ + uptr->u5 &= ~((077 << URCSTA_CMD_V) | URCSTA_CHMASK); + uptr->u5 |= URCSTA_BUSY|chan; + uptr->u5 |= (cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE)) + << URCSTA_CMD_V; + uptr->u3 = 0; + sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd WRS %d %02o %o\n", u, chan, + cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE),uptr->u5); + sim_activate(uptr, 100); + return SCPE_OK; +} + +/* Handle transfer of data for printer */ +t_stat +lpr_srv(UNIT *uptr) { + int chan = URCSTA_CHMASK & uptr->u5; + int u = (uptr - lpr_unit); + + if (uptr->u5 & URCSTA_FULL) { + sim_debug(DEBUG_CMD, &lpr_dev, "lpr %d: done\n", u); + uptr->u5 &= ~URCSTA_FULL; + IAR |= (IRQ_3 << u); + } + + /* Copy next column over */ + if ((uptr->u5 & URCSTA_BUSY) != 0) { + if(chan_read_char(chan, &lpr_data[u].lbuff[uptr->u3], 0)) { + /* Done waiting, print line */ + print_line(uptr, u); + memset(&lpr_data[u].lbuff[0], 0, 144); + uptr->u5 |= URCSTA_FULL; + uptr->u5 &= ~URCSTA_BUSY; + chan_set_wc(chan, (uptr->u3/8)); + chan_set_end(chan); + sim_activate(uptr, 20000); + return SCPE_OK; + } else { + sim_debug(DEBUG_DATA, &lpr_dev, "lpr %d: Char < %02o\n", u, + lpr_data[u].lbuff[uptr->u3]); + uptr->u3++; + } + sim_activate(uptr, 50); + } + return SCPE_OK; +} + +t_stat +lpr_attach(UNIT * uptr, char *file) +{ + t_stat r; + int u = (uptr - lpr_unit); + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + uptr->u5 = 0; + uptr->u4 = 0; + uptr->u3 = 0; + iostatus |= PRT1_FLAG << u; + return SCPE_OK; +} + +t_stat +lpr_detach(UNIT * uptr) +{ + int u = (uptr - lpr_unit); + if (uptr->u5 & URCSTA_FULL) + print_line(uptr, u); + iostatus &= ~(PRT1_FLAG << u); + return detach_unit(uptr); +} + +t_stat +lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "B320 Line Printer\n\n"); + fprintf (st, "The system supports up to two line printers, the second one is disabled\n"); + fprintf (st, "by default. The B320 Line printer can be configured to any number of\n"); + fprintf (st, "lines per page with the:\n"); + fprintf (st, " sim> SET LPn LINESPERPAGE=n\n\n"); + fprintf (st, "The default is 59 lines per page. The Line Printer has the following\n"); + fprintf (st, "control tape attached.\n"); + fprintf (st, " Channel 1: Skip to top of page\n"); + fprintf (st, " Channel 2: Skip to top of page\n"); + fprintf (st, " Channel 3: Skip to next even line\n"); + fprintf (st, " Channel 4: Skip to next odd line\n"); + fprintf (st, " Channel 5: Skip to middle or top of page\n"); + fprintf (st, " Channel 6: Skip 1/4 of page\n"); + fprintf (st, " Channel 7: Skip one linee\n"); + fprintf (st, " Channel 8: Skip one linetop of page\n"); + fprintf (st, " Channel 9: Skip one linetop of page\n"); + fprintf (st, " Channel 10: Skip one linetop of page\n"); + fprintf (st, " Channel 11: Skip one linetop of page\n"); + fprintf (st, " Channel 12: Skip to top of page\n"); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +lpr_description(DEVICE *dptr) +{ + return "B320 Line Printer"; +} + +#endif + + + + +#if NUM_DEVS_CON > 0 +/* + * Console printer routines. + */ +t_stat +con_ini(DEVICE *dptr) { + UNIT *uptr = &con_unit[0]; + uptr->u5 = 0; + iostatus |= SPO_FLAG; + if (!sim_is_active(uptr)) + sim_activate(uptr, 1000); + return SCPE_OK; +} + +t_stat +con_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) +{ + UNIT *uptr = &con_unit[0]; + + /* Are we currently tranfering? */ + if (uptr->u5 & (URCSTA_READ|URCSTA_FILL|URCSTA_BUSY|URCSTA_INPUT)) + return SCPE_BUSY; + + if (cmd & URCSTA_READ) { + if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) + return SCPE_BUSY; + /* Activate input so we can get response */ + uptr->u5 = 0; + uptr->u5 |= URCSTA_INPUT|chan; + sim_putchar('I'); + sim_putchar(' '); + sim_debug(DEBUG_CMD, &con_dev, ": Cmd RDS\n"); + uptr->u3 = 0; + } else { + if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) + return SCPE_BUSY; + sim_putchar('R'); + sim_putchar(' '); + sim_debug(DEBUG_CMD, &con_dev, ": Cmd WRS\n"); + uptr->u5 = 0; + uptr->u5 |= URCSTA_FILL|chan; + uptr->u3 = 0; + } + return SCPE_OK; +} + +/* Handle transfer of data for printer */ +t_stat +con_srv(UNIT *uptr) { + t_stat r; + uint8 ch; + int chan = uptr->u5 & URCSTA_CHMASK; + + + uptr->u5 &= ~URCSTA_BUSY; /* Clear busy */ + + /* Copy next column over */ + if (uptr->u5 & URCSTA_FILL) { + if(chan_read_char(chan, &ch, 0)) { + sim_putchar('\r'); + sim_putchar('\n'); + sim_debug(DEBUG_EXP, &con_dev, "\n\r"); + uptr->u5 &= ~URCSTA_FILL; + chan_set_end(chan); + } else { + ch &= 077; + sim_debug(DEBUG_EXP, &con_dev, "%c", con_to_ascii[ch]); + sim_putchar((int32)con_to_ascii[ch]); + } + } + + if (uptr->u5 & URCSTA_READ) { + ch = con_data[0].ibuff[con_data[0].outptr++]; + + if(chan_write_char(chan, &ch, + (con_data[0].inptr == con_data[0].outptr))) { + sim_putchar('\r'); + sim_putchar('\n'); + sim_debug(DEBUG_EXP, &con_dev, "\n\r"); + uptr->u5 &= ~URCSTA_READ; + chan_set_end(chan); + } + } + + r = sim_poll_kbd(); + if (r & SCPE_KFLAG) { + ch = r & 0377; + if (uptr->u5 & URCSTA_INPUT) { + /* Handle end of buffer */ + switch (ch) { + case 033: + con_data[0].inptr = 0; + case '\r': + case '\n': + uptr->u5 &= ~URCSTA_INPUT; + uptr->u5 |= URCSTA_READ; + break; + case '\b': + case 0x7f: + if (con_data[0].inptr != 0) { + con_data[0].inptr--; + sim_putchar('\b'); + sim_putchar(' '); + sim_putchar('\b'); + } + break; + default: + if (con_data[0].inptr < sizeof(con_data[0].ibuff)) { + ch = ascii_to_con[0177&ch]; + if (ch == 0xff) { + sim_putchar('\007'); + break; + } + sim_putchar((int32)con_to_ascii[ch]); + con_data[0].ibuff[con_data[0].inptr++] = ch; + } + break; + } + } else { + if (ch == 033) { + IAR |= IRQ_2; + con_data[0].inptr = 0; + con_data[0].outptr = 0; + } + } + } + sim_activate(uptr, 1000); + return SCPE_OK; +} + +t_stat +con_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "Supervisory Printer\n\n"); + fprintf (st, "This is the interface from the operator to the system. The printer\n"); + fprintf (st, "operated in a half duplex mode. To request the system to accept input\n"); + fprintf (st, "press the key and wait until the system responds with a line with\n"); + fprintf (st, "I as the first character. When you have finished typing your line, press\n"); + fprintf (st, "return or enter key. Backspace will delete the last character.\n"); + fprintf (st, "All responses from the system are prefixed with a R and blank as the\n"); + fprintf (st, "first character\n"); + return SCPE_OK; +} + +const char * +con_description(DEVICE *dptr) +{ + return "Supervisory Printer"; +} + +#endif + diff --git a/B5500/sim_card.c b/B5500/sim_card.c new file mode 100644 index 00000000..e37af15d --- /dev/null +++ b/B5500/sim_card.c @@ -0,0 +1,1050 @@ +/* Card read/punch routines for 7000 simulators. + + Copyright (c) 2005, Richard Cornwell + + 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. + + This is the standard card reader. + This is the standard card punch. + + Input formats are accepted in a variaty of formats: + Standard ASCII: one record per line. + returns are ignored. + tabs are expanded to modules 8 characters. + ~ in first column is treated as a EOF. + + Binary Card format: + Each record 160 characters. + First characters 6789---- + Second character 21012345 + 111 + Top 4 bits of second character are 0. + It is unlikely that any other format could + look like this. + + BCD Format: + Each record variable length (80 chars or less). + Record mark has bit 7 set. + Bit 6 is even parity. + Bits 5-0 are character. + + CBN Format: + Each record 160 charaters. + First char has bit 7 set. Rest set to 0. + Bit 6 is odd parity. + Bit 5-0 of first character are top 6 bits + of card. + Bit 5-0 of second character are lower 6 bits + of card. + + For autodetection of card format, there can be no parity errors. + All undeterminate formats are treated as ASCII. + + Auto output format is ASCII if card has only printable characters + or card format binary. + + The card module uses up7 to hold a buffer for the card being translated + and the backward translation table. Which is generated from the table. +*/ + +#include +#include "sim_defs.h" +#include "sim_card.h" + + +/* Character conversion tables */ + +const char sim_six_to_ascii[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', '%', /* 17 = box */ + '_', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '@', ',', '(', '~', '\\', '#', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */ + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */ +}; /* 72 = rec mark */ + /* 75 = squiggle, 77 = del */ + +static const uint16 ascii_to_hol_026[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none Y28 78 T28 Y38 T48 X 48 */ + 0x000, 0x482, 0x006, 0x282, 0x442, 0x222, 0x800, 0x022, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* T48 X48 Y48 X T38 T X38 T1 */ + 0x222, 0x822, 0x422, 0x800, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 58 Y68 X68 38 68 X28 */ + 0x002, 0x001, 0x012, 0x40A, 0x80A, 0x042, 0x00A, 0x882, + /* @ A B C D E F G */ + /* 82 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 X58 X68 T58 T78 28 */ + 0x204, 0x202, 0x201, 0x812, 0x20A, 0x412, 0x406, 0x082, + /* ` a b c d e f g */ + 0x212, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* Y78 X78 78 79 */ + 0x604, 0x602, 0x601, 0x406, 0x806,0x0006,0x0005,0xf000 +}; + +/* Set for Burrough codes */ +static const uint16 ascii_to_hol_029[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none T28 T78 38 Y38 T48 X 58 */ + 0x000, 0x282, 0x206, 0x042, 0x442, 0x222, 0x800, 0x012, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* X58 Y58 Y48 XT T38 Y X38 T1 */ + 0x812, 0x412, 0x422, 0xA00, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 58 Y68 X68 T85 68 28 */ + 0x002, 0x001, 0x012, 0x40A, 0x80A, 0x212, 0x00A, 0x082, + /* @ A B C D E F G */ + /* 48 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 X48 X68 T68 T78 T58 */ + 0x204, 0x202, 0x201, 0x822, 0x20A, 0x20A, 0x406, 0xf000, + /* ` a b c d e f g */ + 0xf000,0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* Y78 YT 78 X78 */ + 0x604, 0x602, 0x601, 0x406, 0x600, 0x006, 0x806,0xf000 +}; + +static const uint16 ascii_to_hol_ebcdic[128] = { + /* Control */ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/ + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /*Control*/ + 0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, + /* sp ! " # $ % & ' */ + /* none Y28 78 38 Y38 T48 X 58 */ + 0x000, 0x482, 0x006, 0x042, 0x442, 0x222, 0x800, 0x012, /* 40 - 77 */ + /* ( ) * + , - . / */ + /* X58 Y58 Y48 X T38 Y X38 T1 */ + 0x812, 0x412, 0x422, 0x800, 0x242, 0x400, 0x842, 0x300, + /* 0 1 2 3 4 5 6 7 */ + /* T 1 2 3 4 5 6 7 */ + 0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004, + /* 8 9 : ; < = > ? */ + /* 8 9 28 Y68 X48 68 T68 T78 */ + 0x002, 0x001, 0x082, 0x40A, 0x822, 0x00A, 0x20A, 0x206, + /* @ A B C D E F G */ + /* 48 X1 X2 X3 X4 X5 X6 X7 */ + 0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */ + /* H I J K L M N O */ + /* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */ + 0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408, + /* P Q R S T U V W */ + /* Y7 Y8 Y9 T2 T3 T4 T5 T6 */ + 0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208, + /* X Y Z [ \ ] ^ _ */ + /* T7 T8 T9 X28 X68 T28 T78 X58 */ + 0x204, 0x202, 0x201, 0x882, 0x20A, 0x482, 0x406, 0x212, + /* ` a b c d e f g */ + 0x212, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */ + /* h i j k l m n o */ + 0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08, + /* p q r s t u v w */ + 0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608, + /* x y z { | } ~ del */ + /* Y78 X78 78 79 */ + 0x604, 0x602, 0x601, 0x406, 0x806,0x0006,0x0005,0xf000 +}; + +const char sim_ascii_to_six[128] = { + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* Control */ + -1, -1, -1, -1, -1, -1, -1, -1, + /*sp ! " # $ % & ' */ + 000, 052, -1, 032, 053, 017, 060, 014, /* 40 - 77 */ + /* ( ) * + , - . / */ + 034, 074, 054, 060, 033, 040, 073, 021, + /* 0 1 2 3 4 5 6 7 */ + 012, 001, 002, 003, 004, 005, 006, 007, + /* 8 9 : ; < = > ? */ + 010, 011, 015, 056, 076, 013, 016, 032, + /* @ A B C D E F G */ + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + /* H I J K L M N O */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* P Q R S T U V W */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* X Y Z [ \ ] ^ _ */ + 027, 030, 031, 075, 036, 055, 057, 020, + /* ` a b c d e f g */ + 035, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + /* h i j k l m n o */ + 070, 071, 041, 042, 043, 044, 045, 046, + /* p q r s t u v w */ + 047, 050, 051, 022, 023, 024, 025, 026, + /* x y z { | } ~ del*/ + 027, 030, 031, 057, 077, 017, -1, -1 +}; + +const uint8 sim_parity_table[64] = { + /* 0 1 2 3 4 5 6 7 */ + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100, + 0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000 +}; + +struct card_formats { + uint32 mode; + char *name; +}; + +static struct card_formats fmts[] = { + {MODE_AUTO, "AUTO"}, + {MODE_BIN, "BIN"}, + {MODE_TEXT, "TEXT"}, + {MODE_BCD, "BCD"}, + {MODE_CBN, "CBN"}, + {0, 0}, +}; + + +/* Conversion routines */ + +/* Convert BCD character into hollerith code */ +uint16 +sim_bcd_to_hol(uint8 bcd) { + uint16 hol; + + /* Handle space correctly */ + if (bcd == 0) /* 0 to 82 punch */ + return 0x82; + if (bcd == 020) /* 20 no punch */ + return 0; + + /* Convert to top column */ + switch (bcd & 060) { + default: + case 000: + hol = 0x000; + break; + case 020: + hol = 0x200; + break; + case 040: + hol = 0x400; + break; + case 060: + hol = 0x800; + break; + } + + /* Handle case of 10 special */ + /* Only 032 is punched as 8-2 */ + if ((bcd & 017) == 10 && (bcd & 060) != 020) { + hol |= 1 << 9; + return hol; + } + /* Convert to 0-9 row */ + bcd &= 017; + if (bcd > 9) { + hol |= 0x2; /* Col 8 */ + bcd -= 8; + } + if (bcd != 0) + hol |= 1 << (9 - bcd); + return hol; +} + +/* Returns the BCD of the hollerith code or 0x7f if error */ +uint8 +sim_hol_to_bcd(uint16 hol) { + uint8 bcd; + + /* Convert 10,11,12 rows */ + switch (hol & 0xe00) { + case 0x000: + bcd = 0; + break; + case 0x200: + if ((hol & 0x1ff) == 0) + return 10; + bcd = 020; + break; + case 0x400: + bcd = 040; + break; + case 0x600: /* 11-10 Punch */ + bcd = 052; + break; + case 0x800: + bcd = 060; + break; + case 0xA00: /* 12-10 Punch */ + bcd = 072; + break; + default: /* Double punch in 10,11,12 rows */ + return 0x7f; + } + + hol &= 0x1ff; /* Mask rows 0-9 */ + /* Check row 8 punched */ + if (hol & 0x2) { + bcd += 8; + hol &= ~0x2; + } + + /* Convert rows 0-9 */ + while (hol != 0 && (hol & 0x200) == 0) { + bcd++; + hol <<= 1; + } + + /* Any more columns punched? */ + if ((hol & 0x1ff) != 0) + return 0x7f; + return bcd; +} + +/* Convert EBCDIC character into hollerith code */ +uint16 +sim_ebcdic_to_hol(uint8 ebcdic) { + uint16 hol = 0; + + /* Convert middle two bits first */ + switch (ebcdic & 0x30) { + default: + case 0x00: + hol = 0x800; + break; + case 0x10: + hol = 0x400; + break; + case 0x20: + hol = 0x200; + break; + case 0x30: + hol = 0x000; + break; + } + + /* Now convert the two two bits */ + switch (ebcdic & 0xc0) { + case 0x00: + hol |= 0x001; /* Col 9 */ + break; + case 0x40: + case 0xc0: /* No change */ + break; + case 0x80: + /* Add in correct over punch */ + switch (ebcdic & 0x30) { + default: + case 0x00: + hol = 0x200; + break; + case 0x10: + hol = 0x800; + break; + case 0x20: + hol = 0x400; + break; + case 0x30: + hol = 0x000; + break; + } + } + + + /* Convert lower four bits next */ + if ((ebcdic & 0xf) > 9) { + hol |= 0x2; /* Col 8 */ + hol |= 0x100 >> ((ebcdic & 0xf) - 10); + } else { + hol = 0x200 >> (ebcdic & 0xf); + } + + return hol; +} + +/* Returns the BCD of the hollerith code or 0x7f if error */ +uint8 +sim_hol_to_ebcdic(uint16 hol) { + uint8 ebcdic; + + /* Quick check for odd punch codes */ + if (hol == 0) + return 0x20; + if (hol == 0x800) /* 12 punch only */ + return 0x50; + if (hol == 0x400) /* 11 punch only */ + return 0x50; + if (hol == 0xA83) /* 12-0-1-8-9 */ + return 0x00; + if (hol == 0x683) /* 11-0-1-8-9 */ + return 0x20; + + /* Convert 10,11,12 rows */ + switch (hol & 0xe00) { + case 0x000: /* No punch */ + ebcdic = 0xf0; + break; + case 0x200: /* 10 punch */ + ebcdic = 0xe0; + break; + case 0x400: /* 11 Punch */ + ebcdic = 0xd0; + break; + case 0x800: /* 12 Punch */ + ebcdic = 0xc0; + break; + case 0x600: /* 11-10 Punch */ + ebcdic = 0xa0; + break; + case 0xA00: /* 12-10 Punch */ + ebcdic = 0x80; + break; + case 0xc00: /* 12-11 Punch */ + ebcdic = 0x90; + break; + default: /* Double punch in 10,11,12 rows */ + return 0xff; + } + + hol &= 0x1ff; /* Mask rows 0-9 */ + /* Check row 8 punched */ + if (hol & 0x2) { + ebcdic += 8; + hol &= ~0x2; + } + + /* Check if 9 overpunch */ + if ((hol & 0x1) && (hol & 0x3fc) != 0) { + ebcdic &= 0x30; + hol &= ~ 0x1; + } + + /* Convert rows 0-9 */ + while (hol != 0 && (hol & 0x200) == 0) { + ebcdic++; + hol <<= 1; + } + + /* Remap over punchs */ + if ((ebcdic & 0xc0) == 0xc0) { + if ((ebcdic & 0xf) > 9) + ebcdic &= 0x7f; + } + + /* Any more columns punched? */ + if ((hol & 0x1ff) != 0) + return 0xff; + return ebcdic; +} + + + +static int cmpcard(char *p, char *s) { + int i; + if (p[0] != '~') + return 0; + for(i = 0; i < 3; i++) { + if (tolower(p[i+1]) != s[i]) + return 0; + } + return 1; +} + + +t_stat +sim_read_card(UNIT * uptr) +{ + int i, j; + char c; + uint16 temp; + int mode; + int len; + int size; + int col; + struct _card_data *data; + DEVICE *dptr; + t_stat r = SCPE_OK; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + dptr = find_dev_from_unit( uptr); + data = (struct _card_data *)uptr->up7; + sim_debug(DEBUG_CARD, dptr, "Read card "); + +/* Move data to start at begining of buffer */ + if (data->ptr > 0) { + int ptr = data->ptr; + int start = 0; + + while (ptr < sizeof(data->cbuff)) + (data->cbuff)[start++] = (data->cbuff)[ptr++]; + data->len -= data->ptr; + /* On eof, just return */ + if (!feof(uptr->fileref)) + len = sim_fread(&data->cbuff[start], 1, + sizeof(data->cbuff) - start, uptr->fileref); + else + len = 0; + data->len += len; + size = data->len; + } else { + /* Load rest of buffer */ + if (!feof(uptr->fileref)) { + len = sim_fread(&data->cbuff[0], 1, sizeof(data->cbuff), uptr->fileref); + size = len; + } else + len = size = 0; + data->len = size; + } + + if ((len < 0 || size == 0) && feof(uptr->fileref)) { + sim_debug(DEBUG_CARD, dptr, "EOF\n"); + return SCPE_EOF; + } + + if (ferror(uptr->fileref)) { /* error? */ + perror("Card reader I/O error"); + clearerr(uptr->fileref); + return SCPE_IOERR; + } + + /* Clear image buffer */ + for (col = 0; col < 80; data->image[col++] = 0); + + mode = MODE_TEXT; /* Default is text */ + /* Check buffer to see if binary card in it. */ + for (i = 0, temp = 0; i < 160; i+=2) + temp |= data->cbuff[i]; + /* Check if every other char < 16 & full buffer */ + if (size == 160 && (temp & 0x0f) == 0) + mode = MODE_BIN; /* Probably binary */ + /* Check if maybe BCD or CBN */ + if (data->cbuff[0] & 0x80) { + int odd = 0; + int even = 0; + + /* Clear record mark */ + data->cbuff[0] &= 0x7f; + /* Check all chars for correct parity */ + for(i = 0, temp = 0; i < size; i++) { + uint8 ch = data->cbuff[i]; + /* Stop at EOR */ + if (ch & 0x80) + break; + /* Try matching parity */ + if (sim_parity_table[(ch & 077)] == (ch & 0100)) + even++; + else + odd++; + } + /* Restore it */ + data->cbuff[0] |= 0x80; + if (i == 160 && odd == i) + mode = MODE_CBN; + else if (i < 80 && even == i) + mode = MODE_BCD; + } + + /* Check if modes match */ + if ((uptr->flags & UNIT_MODE) != MODE_AUTO && + (uptr->flags & UNIT_MODE) != mode) { + sim_debug(DEBUG_CARD, dptr, "invalid mode\n\r"); + return SCPE_IOERR; + } + + switch(mode) { + case MODE_TEXT: + sim_debug(DEBUG_CARD, dptr, "text: ["); + /* Check for special codes */ + if (cmpcard(&data->cbuff[0], "raw")) { + int j = 0; + for(col = 0, i = 4; col < 80; i++) { + if (data->cbuff[i] >= '0' && data->cbuff[i] <= '7') { + data->image[col] = (data->image[col] << 3) | + (data->cbuff[i] - '0'); + j++; + } else if (data->cbuff[i] == '\n' || + data->cbuff[i] == '\r') { + break; + } else { + r = SCPE_IOERR; + break; + } + if (j == 4) { + col++; + j = 0; + } + } + } else if (cmpcard(&data->cbuff[0], "eor")) { + data->image[0] = 07; /* 7/8/9 punch */ + i = 4; + } else if (cmpcard(&data->cbuff[0], "eof")) { + data->image[0] = 015; /* 6/7/9 punch */ + i = 4; + } else if (cmpcard(&data->cbuff[0], "eoi")) { + data->image[0] = 017; /* 6/7/8/9 punch */ + i = 4; + } else { + /* Convert text line into card image */ + for (col = 0, i = 0; col < 80 && i < size; i++) { + c = data->cbuff[i]; + switch (c) { + case '\0': + case '\r': + break; /* Ignore these */ + case '\t': + col = (col | 7) + 1; /* Mult of 8 */ + break; + case '\n': + col = 80; + break; + case '~': /* End of file mark */ + if (col == 0) { + r = SCPE_EOF; + break; + } + default: + sim_debug(DEBUG_CARD, dptr, "%c", c); + if ((uptr->flags & MODE_LOWER) == 0) + c = toupper(c); + switch(uptr->flags & MODE_CHAR) { + case 0: + case MODE_026: + temp = ascii_to_hol_026[(int)c]; + break; + case MODE_029: + temp = ascii_to_hol_029[(int)c]; + break; + case MODE_EBCDIC: + temp = ascii_to_hol_ebcdic[(int)c]; + break; + } + if (temp & 0xf000) + r = SCPE_IOERR; + data->image[col++] = temp & 0xfff; + /* Eat cr if line exactly 80 columns */ + if (col == 80) { + if (data->cbuff[i + 1] == '\n') + i++; + } + } + } + } + if (data->cbuff[i] == '\n') + i++; + if (data->cbuff[i] == '\r') + i++; + sim_debug(DEBUG_CARD, dptr, "]\r\n"); + break; + + case MODE_BIN: + temp = 0; + sim_debug(DEBUG_CARD, dptr, "bin\r\n"); + /* Move data to buffer */ + for (j = i = 0; i < size;) { + temp |= data->cbuff[i]; + data->image[j] = (data->cbuff[i++] >> 4) & 0xF; + data->image[j++] |= ((uint16)data->cbuff[i++]) << 4; + } + /* Check if format error */ + if (temp & 0xF) + r = SCPE_IOERR; + + /* If not full record, return error */ + if (size != 160) + r = SCPE_IOERR; + break; + + case MODE_CBN: + sim_debug(DEBUG_CARD, dptr, "cbn\r\n"); + /* Check if first character is a tape mark */ + if (size == 1 && ((uint8)data->cbuff[0]) == 0217) { + r = SCPE_EOF; + break; + } + + /* Clear record mark */ + data->cbuff[0] &= 0x7f; + + /* Convert card and check for errors */ + for (j = i = 0; i < size;) { + uint8 c; + + if (data->cbuff[i] & 0x80) + break; + c = data->cbuff[i] & 077; + if (sim_parity_table[(int)c] == (data->cbuff[i++] & 0100)) + r = SCPE_IOERR; + data->image[j] = ((uint16)c) << 6; + if (data->cbuff[i] & 0x80) + break; + c = data->cbuff[i] & 077; + if (sim_parity_table[(int)c] == (data->cbuff[i++] & 0100)) + r = SCPE_IOERR; + data->image[j++] |= c; + } + + /* If not full record, return error */ + if (size != 160) { + r = SCPE_IOERR; + } + break; + + case MODE_BCD: + sim_debug(DEBUG_CARD, dptr, "bcd ["); + /* Check if first character is a tape mark */ + if (size == 1 && ((uint8)data->cbuff[0]) == 0217) { + r = SCPE_EOF; + break; + } + + /* Clear record mark */ + data->cbuff[0] &= 0x7f; + + /* Convert text line into card image */ + for (col = 0, i = 0; col < 80 && i < size; i++) { + if (data->cbuff[i] & 0x80) + break; + c = data->cbuff[i] & 077; + if (sim_parity_table[(int)c] != (data->cbuff[i] & 0100)) + r = SCPE_IOERR; + sim_debug(DEBUG_CARD, dptr, "%c", sim_six_to_ascii[(int)c]); + /* Convert to top column */ + data->image[col++] = sim_bcd_to_hol(c); + } + sim_debug(DEBUG_CARD, dptr, "]\r\n"); + } + if (i < size) + data->ptr = i; + else + data->ptr = 0; + return r; +} + +/* Check if reader is at last card. + * + */ +int +sim_card_eof(UNIT *uptr) +{ + struct _card_data *data; + + if ((uptr->flags & UNIT_ATT) == 0) + return 1; /* attached? */ + + data = (struct _card_data *)uptr->up7; + + if (data->ptr > 0) { + if ((data->ptr - data->len) == 0 && feof(uptr->fileref)) + return 1; + } else { + if (feof(uptr->fileref)) + return 1; + } + return 0; +} + + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + +t_stat +sim_punch_card(UNIT * uptr, UNIT *stkuptr) +{ +/* Convert word record into column image */ +/* Check output type, if auto or text, try and convert record to bcd first */ +/* If failed and text report error and dump what we have */ +/* Else if binary or not convertable, dump as image */ + + /* Try to convert to text */ + uint8 out[160]; + int i; + FILE *fo = uptr->fileref; + int mode = uptr->flags & UNIT_MODE; + int ok = 1; + struct _card_data *data; + DEVICE *dptr; + + if ((uptr->flags & UNIT_ATT) == 0) { + if (stkuptr != NULL && stkuptr->flags & UNIT_ATT) { + fo = stkuptr->fileref; + if ((stkuptr->flags & UNIT_MODE) != MODE_AUTO) + mode = stkuptr->flags & UNIT_MODE; + } else + return SCPE_UNATT; /* attached? */ + } + + data = (struct _card_data *)uptr->up7; + dptr = find_dev_from_unit(uptr); + + /* Fix mode if in auto mode */ + if (mode == MODE_AUTO) { + /* Try to convert each column to ascii */ + for (i = 0; i < 80; i++) { + out[i] = data->hol_to_ascii[data->image[i]]; + if (out[i] == 0xff) { + ok = 0; + } + } + mode = ok?MODE_TEXT:MODE_BIN; + } + + switch(mode) { + default: + case MODE_TEXT: + /* Scan each column */ + sim_debug(DEBUG_CARD, dptr, "text: ["); + for (i = 0; i < 80; i++) { + out[i] = data->hol_to_ascii[data->image[i]]; + if (out[i] == 0xff) + out[i] = '?'; + sim_debug(DEBUG_CARD, dptr, "%c", out[i]); + } + sim_debug(DEBUG_CARD, dptr, "]\r\n"); + /* Trim off trailing spaces */ + while (i > 0 && out[--i] == ' ') ; + out[++i] = '\n'; + out[++i] = '\0'; + break; + case MODE_BIN: + sim_debug(DEBUG_CARD, dptr, "bin\r\n"); + for (i = 0; i < 80; i++) { + uint16 col = data->image[i]; + out[i*2] = (col & 0x00f) << 4; + out[i*2+1] = (col & 0xff0) >> 4; + } + i = 160; + break; + case MODE_CBN: + sim_debug(DEBUG_CARD, dptr, "cbn\r\n"); + /* Fill buffer */ + for (i = 0; i < 80; i++) { + uint16 col = data->image[i]; + out[i*2] = (col >> 6) & 077; + out[i*2+1] = col & 077; + } + /* Now set parity */ + for (i = 0; i < 160; i++) + out[i] |= 0100 ^ sim_parity_table[(int)out[i]]; + out[0] |= 0x80; /* Set record mark */ + i = 160; + break; + case MODE_BCD: + sim_debug(DEBUG_CARD, dptr, "bcd ["); + for (i = 0; i < 80; i++) { + out[i] = sim_hol_to_bcd(data->image[i]); + if (out[i] != 0x7f) + out[i] |= sim_parity_table[(int)out[i]]; + else + out[i] = 077; + sim_debug(DEBUG_CARD, dptr, "%c", + sim_six_to_ascii[(int)out[i]]); + } + sim_debug(DEBUG_CARD, dptr, "]\r\n"); + out[0] |= 0x80; /* Set record mark */ + while (i > 0 && out[--i] == 0); + i++; + break; + } + sim_fwrite(out, 1, i, fo); + memset(&data->image[0], 0, sizeof(data->image)); + return SCPE_OK; +} + +/* Set card format */ +t_stat sim_card_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + int f; + + if (uptr == NULL) return SCPE_IERR; + if (cptr == NULL) return SCPE_ARG; + for (f = 0; fmts[f].name != 0; f++) { + if (strcmp (cptr, fmts[f].name) == 0) { + uptr->flags = (uptr->flags & ~UNIT_MODE) | fmts[f].mode; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +/* Show card format */ + +t_stat sim_card_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc) +{ + int f; + + for (f = 0; fmts[f].name != 0; f++) { + if ((uptr->flags & UNIT_MODE) == fmts[f].mode) { + fprintf (st, "%s format", fmts[f].name); + return SCPE_OK; + } + } + fprintf (st, "invalid format"); + return SCPE_OK; +} + + +t_stat +sim_card_attach(UNIT * uptr, char *cptr) +{ + t_stat r; + struct _card_data *data; + char gbuf[30]; + int i; + + if (sim_switches & SWMASK ('F')) { /* format spec? */ + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + if (sim_card_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } + + if ((r = attach_unit(uptr, cptr)) != SCPE_OK) + return r; + + /* Initialize reverse mapping if not initialized */ + /* Set all to invalid */ + /* Allocate a buffer if one does not exist */ + if (uptr->up7 == 0) { + uptr->up7 = malloc(sizeof(struct _card_data)); + data = (struct _card_data *)uptr->up7; + } else { + data = (struct _card_data *)uptr->up7; + } + memset(&data->hol_to_ascii[0], 0xff, 4096); + for(i = 0; i < (sizeof(ascii_to_hol_026)/sizeof(uint16)); i++) { + uint16 temp; + switch(uptr->flags & MODE_CHAR) { + default: + case 0: + case MODE_026: + temp = ascii_to_hol_026[i]; + break; + case MODE_029: + temp = ascii_to_hol_029[i]; + break; + case MODE_EBCDIC: + temp = ascii_to_hol_ebcdic[i]; + break; + } + if ((temp & 0xf000) == 0) { + data->hol_to_ascii[temp] = i; + } + } + + memset(data, 0, sizeof(struct _card_data)); + data->ptr = 0; /* Set for initial read */ + data->len = 0; + return SCPE_OK; +} + +t_stat +sim_card_detach(UNIT * uptr) +{ + /* Free buffer if one allocated */ + if (uptr->up7 != 0) { + free((void *)uptr->up7); + uptr->up7 = 0; + } + return detach_unit(uptr); +} + +t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s Card Attach Help\n\n", dptr->name); + if (0 == (uptr-dptr->units)) { + if (dptr->numunits > 1) { + uint32 i; + + for (i=0; i < dptr->numunits; ++i) + if (dptr->units[i].flags & UNIT_ATTABLE) + fprintf (st, " sim> ATTACH {switches} %s%d carddeck\n\n", dptr->name, i); + } + else + fprintf (st, " sim> ATTACH {switches} %s carddeck\n\n", dptr->name); + } + else + fprintf (st, " sim> ATTACH {switches} %s carddeck\n\n", dptr->name); + fprintf (st, "Attach command switches\n"); + fprintf (st, " -F Open the indicated card deck in a specific format (default\n"); + fprintf (st, " is AUTO, alternatives are BIN, TEXT, BCD and CBN)\n"); + return SCPE_OK; +} + + diff --git a/B5500/sim_card.h b/B5500/sim_card.h new file mode 100644 index 00000000..a76f27b0 --- /dev/null +++ b/B5500/sim_card.h @@ -0,0 +1,113 @@ +/* Card read/punch routines for 7000 simulators. + + Copyright (c) 2005, Richard Cornwell + + 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. + + This is the standard card reader. + This is the standard card punch. + + Input formats are accepted in a variaty of formats: + Standard ASCII: one record per line. + returns are ignored. + tabs are expanded to modules 8 characters. + ~ in first column is treated as a EOF. + + Binary Card format: + Each record 160 characters. + First character 21012345 + 111 + Second characters 6789---- + Top 4 bits of second character are 0. + It is unlikely that ascii text or BCD format + text could produce similar profile. + + BCD Format: + Each record variable length (80 chars or less). + Record mark has bit 7 set. + Bit 6 is even parity. + Bits 5-0 are character. + + CBN Format: + Each record 160 charaters. + First char has bit 7 set. Rest set to 0. + Bit 6 is odd parity. + Bit 5-0 of first character are top 6 bits + of card. + Bit 5-0 of second character are lower 6 bits + of card. + + For autodetection of BCD card format, there can be no parity errors. + All undeterminate formats are treated as ASCII. + + Auto output format is ASCII if card has only printable characters + or card format binary. +*/ + + +#define DEBUG_CARD 0x0000010 /* Show details */ + +/* Flags for punch and reader. */ +#define UNIT_V_MODE (UNIT_V_UF + 0) +#define UNIT_MODE (7 << UNIT_V_MODE) +#define MODE_AUTO (0 << UNIT_V_MODE) +#define MODE_BIN (1 << UNIT_V_MODE) +#define MODE_TEXT (2 << UNIT_V_MODE) +#define MODE_BCD (3 << UNIT_V_MODE) +#define MODE_CBN (4 << UNIT_V_MODE) +/* Allow lower case letters */ +#define MODE_LOWER (8 << UNIT_V_MODE) +#define MODE_026 (0x10 << UNIT_V_MODE) +#define MODE_029 (0x20 << UNIT_V_MODE) +#define MODE_EBCDIC (0x30 << UNIT_V_MODE) +#define MODE_CHAR (0x30 << UNIT_V_MODE) + + +struct _card_data +{ + int ptr; /* Pointer in buffer */ + int len; /* Length of buffer */ + char cbuff[1024]; /* Read in buffer for cards */ + uint16 image[80]; /* Image */ + uint8 hol_to_ascii[4096]; /* Back conversion table */ +}; + +/* Generic routines. */ +t_stat sim_read_card(UNIT * uptr); +int sim_card_eof(UNIT * uptr); +t_stat sim_punch_card(UNIT * uptr, UNIT *stkptr); +t_stat sim_card_attach(UNIT * uptr, char *file); +t_stat sim_card_detach(UNIT *uptr); + +/* Conversion routines to save code */ +uint16 sim_bcd_to_hol(uint8 bcd); +uint16 sim_ebcdic_to_hol(uint8 ebcdic); +uint8 sim_hol_to_bcd(uint16 hol); +uint8 sim_hol_to_ebbcd(uint16 hol); + +/* Format control routines. */ +t_stat sim_card_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_card_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* Help information */ +t_stat sim_card_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); + +/* Translation tables */ +const char sim_six_to_ascii[64]; +const char sim_ascii_to_six[128]; +const uint8 sim_parity_table[64];