From 0a33758e47016f39b00021b9588692aac84612ad Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Sat, 6 Feb 2016 15:51:04 -0500 Subject: [PATCH] B5500: Initial checking of simulator for current simh The B5500 simulator supports the following peripherals. Two CPUs with between 4K and 32K of memory. The second CPU can be enabled with "set cpu1 enable". "set cpu1 disable" disables the second CPU. Up to 4 floating IO channels. Individual channels can be enabled with "set io# enable", or "set io# disable". There are two card readers. The second reader is disabled by default. There is one Card Punch. The Card reader and Card Punch support the following options: set cr format= auto - will automatically determine the format based on the text it recieves. text Text based cards. Tabs are converted to the correct number of spaces. A record of ~raw octal will enter a binary card. ~eor will enter a 7/8/9 punch in column 1. ~eof will enter a 6/7/9 punch in column 1. ~eoi will enter a 6/7/8/9 punch in column 1. ~~ will enter a ~ as the first character. bin 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 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 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 punch format of auto if the card can't be converted to text it is output as a raw record. There are two line printers, the second one is disabled by default. The LP supports the option "set lp# linesperpage=#" which determines when the printer will force out a page break. There are up to 16 mag tape drives, the format is controlled by the standard simh format control for tapes. These are 6 bit tapes, 1 character per record with parity. Units 8-16 are disabled by default. There are up to two drum units DR0 and DR1. These can either be attached to a file or set to AUXMEM. Setting to AUXMEM causes them to exist only during the given simh run. Setting back to DRUM will clear whatever was stored on the drum. To enable use of DRUM on XV the following options should be turned on "DRA,DRB,CODEOLAY,DATAOLAY". MCP will then use the drum as a overlay device instead of the disk system. Disks can be attached to the various ESU's, ESU0-9 are on DKA by default, ESU10-19 are on DKB. If "set dk1 dfx" is set, then ESU10-19 are not used and the disks are shared by both DKA and DKB. To use more then 10 ESU's in a non shared mode, a new version of MCP must be created. MCP must be compiled with DFX option set to false. For MCP XV DKBNODFX must also be set to true. ESU units can be set to MODI or MODIB. MODIB will double the size of the drive. The DTC can be attached to a telnet port with "attach dtc #" to enable dialup access to the sim. The loader card for the card reader is: ~raw0104441100204231524012004000004444550211002041317700000000000024045303040243 00050000006501004131011041310055005500000062005042310000006600304231000000720010 42310000007675610165001002310010413100040107202500440106202533554061256520252265 20251765202514655355536117650000004401062025155522610165225572610465044101160500 4131 This card should be all in one line. --- B5500/b5500_cpu.c | 3903 ++++++++++++++++++++++++++++++++++++++++++++ B5500/b5500_defs.h | 570 +++++++ B5500/b5500_dk.c | 578 +++++++ B5500/b5500_dr.c | 258 +++ B5500/b5500_dtc.c | 895 ++++++++++ B5500/b5500_io.c | 795 +++++++++ B5500/b5500_mt.c | 629 +++++++ B5500/b5500_sys.c | 541 ++++++ B5500/b5500_urec.c | 1007 ++++++++++++ B5500/sim_card.c | 1050 ++++++++++++ B5500/sim_card.h | 113 ++ 11 files changed, 10339 insertions(+) create mode 100644 B5500/b5500_cpu.c create mode 100644 B5500/b5500_defs.h create mode 100644 B5500/b5500_dk.c create mode 100644 B5500/b5500_dr.c create mode 100644 B5500/b5500_dtc.c create mode 100644 B5500/b5500_io.c create mode 100644 B5500/b5500_mt.c create mode 100644 B5500/b5500_sys.c create mode 100644 B5500/b5500_urec.c create mode 100644 B5500/sim_card.c create mode 100644 B5500/sim_card.h 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];