/* 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 <math.h> | |
#include <time.h> | |
#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 */ | |
uint16 idle_addr = 0; /* Address of idle loop */ | |
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)|UNIT_IDLE, 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, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, | |
{MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, | |
{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) >> FFIELD_V) | |
#define CF(x) (uint16) ((x) & CORE) | |
#define LF(x) (uint16)(((x) & RL) >> RL_V) | |
#define RF(x) (uint16)(((x) & RFIELD) >> RFIELD_V) | |
#define toF(x) ((((t_uint64)(x)) << FFIELD_V) & FFIELD) | |
#define toC(x) (((t_uint64)(x)) & CORE) | |
#define toL(x) ((((t_uint64)(x)) << RL_V) & RL) | |
#define toR(x) ((((t_uint64)(x)) << RFIELD_V) & RFIELD) | |
#define replF(y, x) ((y & ~FFIELD) | toF(x)) | |
#define replC(y, x) ((y & ~CORE) | toC(x)) | |
#define next_addr(x) (x = (x + 1) & 077777) | |
#define prev_addr(x) (x = (x - 1) & 077777) | |
/* 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; | |
} | |
} | |
} | |
/* Check if in idle loop. | |
assume that current instruction is ITI */ | |
/* Typical idle loop for MCP is: | |
-1 ITI 0211 | |
+0 TUS 2431 | |
+1 OPDC address1 xxx2 | |
+2 LOR 0215 | |
+3 OPDC address2 xxx2 | |
+4 NEQ 0425 | |
+5 LITC 010 LITC 1 0040 0004 | |
+6 BBC LBC 0131 2131 | |
*/ | |
int check_idle() { | |
static uint16 loop_data[7] = { | |
WMOP_TUS, WMOP_OPDC, WMOP_LOR, WMOP_OPDC, | |
WMOP_NEQ, WMOP_LITC, WMOP_BBC }; | |
static uint16 loop_mask[7] = { | |
07777, 00003, 07777, 00003, | |
07777, 07733, 05777}; | |
t_uint64 data; | |
uint16 addr = C; | |
int l = (3 - L) * 12; | |
uint16 word; | |
int i; | |
/* Quick check to see if not correct location */ | |
if (idle_addr != 0 && idle_addr != addr) | |
return 0; | |
/* If address same, then idle loop */ | |
if (idle_addr == addr) | |
return 1; | |
/* Not set, see if this could be loop */ | |
data = M[addr]; | |
for (i = 0; i < 7; i++) { | |
word = (uint16)(data >> l) & 07777; | |
if ((word & loop_mask[i]) != loop_data[i]) | |
return 0; | |
if (l == 0) { | |
addr++; | |
l = 3 * 12; | |
data = M[addr]; | |
} else { | |
l -= 12; | |
} | |
} | |
idle_addr = C; | |
return 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 = (uint16)(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 = LF(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 = (uint8)((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 = (uint8)((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 = FF(B); | |
S = (S - field) & CORE; | |
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(A); | |
S = (S - field) & CORE; | |
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 = FLAG | PRESENT | toC(Ma); | |
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 = FLAG | PRESENT | toC(Ma); | |
} | |
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 { | |
/* Could be an idle loop, if P2 running, continue */ | |
if (P2_run) | |
break; | |
if (sim_idle_enab) { | |
/* Check if possible idle loop */ | |
if (check_idle()) | |
sim_idle (TMR_RTC, FALSE); | |
} | |
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 = FLAG | PRESENT | toC(Ma); | |
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; | |
idle_addr = 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; | |
} |