These changes facilitate more robust parameter type checking and helps to identify unexpected coding errors. Most simulators can now also be compiled with a C++ compiler without warnings. Additionally, these changes have also been configured to facilitate easier backporting of simulator and device simulation modules to run under the simh v3.9+ SCP framework.
763 lines
28 KiB
C
763 lines
28 KiB
C
/* lgp_cpu.c: LGP CPU simulator
|
|
|
|
Copyright (c) 2004-2008, Robert M. Supnik
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
cpu LGP-30 [LGP-21] CPU
|
|
|
|
22-Sep-05 RMS Fixed declarations (Sterling Garwood)
|
|
04-Sep-05 RMS Fixed missing returns (Peter Schorn)
|
|
04-Jan-05 RMS Modified VM pointer setup
|
|
|
|
The system state for the LGP-30 [LGP-21] is:
|
|
|
|
A<0:31> accumulator
|
|
C<0:11> counter (PC)
|
|
OVF overflow flag [LGP-21 only]
|
|
|
|
The LGP-30 [LGP-21] has just one instruction format:
|
|
|
|
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|S| |opcode | | operand address | |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
LGP-30 instructions:
|
|
|
|
<0,12:15> operation
|
|
|
|
0 stop
|
|
1 A <- M[ea]
|
|
2 M[ea]<addr> <- A<addr>
|
|
3 M[ea]<addr> <- C + 1
|
|
4 input
|
|
5 A <- A / M[ea]
|
|
6 A <- A * M[ea], low result
|
|
7 A <- A * M[ea], high result
|
|
8 output
|
|
9 A <- A & M[ea]
|
|
A C <- ea
|
|
B C <- ea if A < 0
|
|
-B C <- ea if (A < 0) || T-switch set
|
|
C M[ea] <- A
|
|
D M[ea] <- A, A <- 0
|
|
E A <- A + M[ea]
|
|
F A <- A - M[ea]
|
|
|
|
LGP-21 instructions:
|
|
|
|
<0,12:15> operation
|
|
|
|
0 stop; sense and skip
|
|
-0 stop; sense overflow and skip
|
|
1 A <- M[ea]
|
|
2 M[ea]<addr> <- A<addr>
|
|
3 M[ea]<addr> <- C + 1
|
|
4 6b input
|
|
-4 4b input
|
|
5 A <- A / M[ea]
|
|
6 A <- A * M[ea], low result
|
|
7 A <- A * M[ea], high result
|
|
8 6b output
|
|
-8 4b output
|
|
9 A <- A & M[ea]
|
|
A C <- ea
|
|
B C <- ea if A < 0
|
|
-B C <- ea if (A < 0) || T-switch set
|
|
C M[ea] <- A
|
|
D M[ea] <- A, A <- 0
|
|
E A <- A + M[ea]
|
|
F A <- A - M[ea]
|
|
|
|
The LGP-30 [LGP-21] has 4096 32b words of memory. The low order
|
|
bit is always read and stored as 0. The LGP-30 uses a drum for
|
|
memory, with 64 tracks of 64 words. The LGP-21 uses a disk for
|
|
memory, with 32 tracks of 128 words.
|
|
|
|
This routine is the instruction decode routine for the LGP-30
|
|
[LGP-21]. It is called from the simulator control program to
|
|
execute instructions in simulated memory, starting at the simulated
|
|
PC. It runs until 'reason' is set non-zero.
|
|
|
|
General notes:
|
|
|
|
1. Reasons to stop. The simulator can be stopped by:
|
|
|
|
STOP instruction
|
|
breakpoint encountered
|
|
overflow [LGP-30]
|
|
I/O error in I/O simulator
|
|
|
|
2. Interrupts. There are no interrupts.
|
|
|
|
3. Non-existent memory. All of memory always exists.
|
|
|
|
4. Adding I/O devices. The LGP-30 could not support additional
|
|
I/O devices. The LGP-21 could but none are known.
|
|
*/
|
|
|
|
#include "lgp_defs.h"
|
|
|
|
#define PCQ_SIZE 64 /* must be 2**n */
|
|
#define PCQ_MASK (PCQ_SIZE - 1)
|
|
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC - 1) & AMASK;
|
|
#define M16 0xFFFF
|
|
#define M32 0xFFFFFFFF
|
|
#define NEG(x) ((~(x) + 1) & DMASK)
|
|
#define ABS(x) (((x) & SIGN)? NEG (x): (x))
|
|
|
|
uint32 M[MEMSIZE] = { 0 }; /* memory */
|
|
uint32 PC = 0; /* counter */
|
|
uint32 A = 0; /* accumulator */
|
|
uint32 IR = 0; /* instr register */
|
|
uint32 OVF = 0; /* overflow indicator */
|
|
uint32 t_switch = 0; /* transfer switch */
|
|
uint32 bp32 = 0; /* BP32 switch */
|
|
uint32 bp16 = 0; /* BP16 switch */
|
|
uint32 bp8 = 0; /* BP8 switch */
|
|
uint32 bp4 = 0; /* BP4 switch */
|
|
uint32 inp_strt = 0; /* input started */
|
|
uint32 inp_done = 0; /* input done */
|
|
uint32 out_strt = 0; /* output started */
|
|
uint32 out_done = 0; /* output done */
|
|
uint32 lgp21_sov = 0; /* LGP-21 sense pending */
|
|
int32 delay = 0;
|
|
int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
|
|
int32 pcq_p = 0; /* PC queue ptr */
|
|
REG *pcq_r = NULL; /* PC queue reg ptr */
|
|
|
|
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_model (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
t_stat cpu_set_30opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_set_fill (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_set_exec (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
t_stat cpu_one_inst (uint32 opc, uint32 ir);
|
|
uint32 Mul64 (uint32 a, uint32 b, uint32 *low);
|
|
t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q);
|
|
uint32 I_delay (uint32 opc, uint32 ea, uint32 op);
|
|
uint32 shift_in (uint32 a, uint32 dat, uint32 sh4);
|
|
|
|
extern t_stat op_p (uint32 dev, uint32 ch);
|
|
extern t_stat op_i (uint32 dev, uint32 ch, uint32 sh4);
|
|
extern void lgp_vm_init (void);
|
|
|
|
/* 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 (NULL, UNIT_FIX+UNIT_IN4B+UNIT_TTSS_D, MEMSIZE) };
|
|
|
|
REG cpu_reg[] = {
|
|
{ DRDATA (C, PC, 12), REG_VMAD },
|
|
{ HRDATA (A, A, 32), REG_VMIO },
|
|
{ HRDATA (IR, IR, 32), REG_VMIO },
|
|
{ FLDATA (OVF, OVF, 0) },
|
|
{ FLDATA (TSW, t_switch, 0) },
|
|
{ FLDATA (BP32, bp32, 0) },
|
|
{ FLDATA (BP16, bp16, 0) },
|
|
{ FLDATA (BP8, bp8, 0) },
|
|
{ FLDATA (BP4, bp4, 0) },
|
|
{ FLDATA (INPST, inp_strt, 0) },
|
|
{ FLDATA (INPDN, inp_done, 0) },
|
|
{ FLDATA (OUTST, out_strt, 0) },
|
|
{ FLDATA (OUTDN, out_done, 0) },
|
|
{ DRDATA (DELAY, delay, 7) },
|
|
{ BRDATA (CQ, pcq, 16, 12, PCQ_SIZE), REG_RO + REG_CIRC },
|
|
{ HRDATA (CQP, pcq_p, 6), REG_HRO },
|
|
{ HRDATA (WRU, sim_int_char, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB cpu_mod[] = {
|
|
{ UNIT_LGP21, UNIT_LGP21, "LGP-21", "LGP21", &cpu_set_model, &cpu_show_model },
|
|
{ UNIT_LGP21, 0, "LGP-30", "LGP30", &cpu_set_model, &cpu_show_model },
|
|
{ UNIT_TTSS_D, UNIT_TTSS_D, 0, "TRACK" },
|
|
{ UNIT_TTSS_D, 0, 0, "NORMAL" },
|
|
{ UNIT_LGPH_D, UNIT_LGPH_D, 0, "LGPHEX" },
|
|
{ UNIT_LGPH_D, 0, 0, "STANDARDHEX" },
|
|
{ UNIT_MANI, UNIT_MANI, NULL, "MANUAL" },
|
|
{ UNIT_MANI, 0, NULL, "TAPE" },
|
|
{ UNIT_IN4B, UNIT_IN4B, NULL, "4B", &cpu_set_30opt },
|
|
{ UNIT_IN4B, 0, NULL, "6B", &cpu_set_30opt },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "INPUT", &cpu_set_30opt_i },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "OUTPUT", &cpu_set_30opt_o },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "EXECUTE", &cpu_set_exec },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "FILL", &cpu_set_fill },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE cpu_dev = {
|
|
"CPU", &cpu_unit, cpu_reg, cpu_mod,
|
|
1, 10, 12, 1, 16, 32,
|
|
&cpu_ex, &cpu_dep, &cpu_reset,
|
|
NULL, NULL, NULL
|
|
};
|
|
|
|
/* Timing tables */
|
|
|
|
/* Optimization minima and maxima
|
|
Z B Y R I D N M P E U T H C A S */
|
|
|
|
static const int32 min_30[16] = {
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
|
};
|
|
static const int32 max_30[16] = {
|
|
7, 7, 7, 7, 7, 5, 8, 6, 7, 7, 0, 0, 7, 7, 7, 7
|
|
};
|
|
static const int32 min_21[16] = {
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
|
};
|
|
static const int32 max_21[16] = {
|
|
0, 16, 16, 16, 0, 58, 81, 79, 0, 16, 0, 0, 16, 16, 16, 16
|
|
};
|
|
|
|
static const uint32 log_to_phys_30[NSC_30] = { /* drum interlace chart */
|
|
0, 57, 50, 43, 36, 29, 22, 15, 8 ,
|
|
1, 58, 51, 44, 37, 30, 23, 16, 9 ,
|
|
2, 59, 52, 45, 38, 31, 24, 17, 10,
|
|
3, 60, 53, 46, 39, 32, 25, 18, 11,
|
|
4, 61, 54, 47, 40, 33, 26, 19, 12,
|
|
5, 62, 55, 48, 41, 32, 27, 20, 13,
|
|
6, 63, 56, 49, 42, 33, 28, 21, 14,
|
|
7
|
|
};
|
|
|
|
static const uint32 log_to_phys_21[NSC_21] = { /* disk interlace chart */
|
|
0, 64, 57, 121, 50, 114, 43, 107, 36, 100, 29, 93, 22, 86, 15, 79, 8, 72,
|
|
1, 65, 58, 122, 51, 115, 44, 108, 37, 101, 30, 94, 23, 87, 16, 80, 9, 73,
|
|
2, 66, 59, 123, 52, 116, 45, 109, 38, 102, 31, 95, 24, 88, 17, 81, 10, 74,
|
|
3, 67, 60, 124, 53, 117, 46, 110, 39, 103, 32, 96, 25, 89, 18, 82, 11, 75,
|
|
4, 68, 61, 125, 54, 118, 47, 111, 40, 104, 33, 97, 26, 90, 19, 83, 12, 76,
|
|
5, 69, 62, 126, 55, 119, 48, 112, 41, 105, 34, 98, 27, 91, 20, 84, 12, 77,
|
|
6, 70, 63, 127, 56, 120, 49, 113, 42, 106, 35, 99, 28, 92, 21, 85, 13, 78,
|
|
7, 71
|
|
};
|
|
|
|
t_stat sim_instr (void)
|
|
{
|
|
t_stat r = 0;
|
|
uint32 oPC;
|
|
|
|
/* Restore register state */
|
|
|
|
PC = PC & AMASK; /* mask PC */
|
|
sim_cancel_step (); /* defang SCP step */
|
|
if (lgp21_sov) { /* stop sense pending? */
|
|
lgp21_sov = 0;
|
|
if (!OVF) /* ovf off? skip */
|
|
PC = (PC + 1) & AMASK;
|
|
else OVF = 0; /* on? reset */
|
|
}
|
|
|
|
/* Main instruction fetch/decode loop */
|
|
|
|
do {
|
|
if (sim_interval <= 0) { /* check clock queue */
|
|
if ((r = sim_process_event ()))
|
|
break;
|
|
}
|
|
|
|
if (delay > 0) { /* delay to next instr */
|
|
delay = delay - 1; /* count down delay */
|
|
sim_interval = sim_interval - 1;
|
|
continue; /* skip execution */
|
|
}
|
|
|
|
if (sim_brk_summ && /* breakpoint? */
|
|
sim_brk_test (PC, SWMASK ('E'))) {
|
|
r = STOP_IBKPT; /* stop simulation */
|
|
break;
|
|
}
|
|
|
|
IR = Read (oPC = PC); /* get instruction */
|
|
PC = (PC + 1) & AMASK; /* increment PC */
|
|
sim_interval = sim_interval - 1;
|
|
|
|
if ((r = cpu_one_inst (oPC, IR))) { /* one instr; error? */
|
|
if (r == STOP_STALL) { /* stall? */
|
|
PC = oPC; /* back up PC */
|
|
delay = r = 0; /* no delay */
|
|
}
|
|
else break;
|
|
}
|
|
|
|
if (sim_step && (--sim_step <= 0)) /* do step count */
|
|
r = SCPE_STOP;
|
|
|
|
} while (r == 0); /* loop until halted */
|
|
pcq_r->qptr = pcq_p; /* update pc q ptr */
|
|
return r;
|
|
}
|
|
|
|
/* Execute one instruction */
|
|
|
|
t_stat cpu_one_inst (uint32 opc, uint32 ir)
|
|
{
|
|
uint32 ea, op, dat, res, dev, sh4, ch;
|
|
t_bool ovf_this_cycle = FALSE;
|
|
t_stat reason = 0;
|
|
|
|
op = I_GETOP (ir); /* opcode */
|
|
ea = I_GETEA (ir); /* address */
|
|
switch (op) { /* case on opcode */
|
|
|
|
/* Loads, stores, transfers instructions */
|
|
|
|
case OP_B: /* bring */
|
|
A = Read (ea); /* A <- M[ea] */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_H: /* hold */
|
|
Write (ea, A); /* M[ea] <- A */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_C: /* clear */
|
|
Write (ea, A); /* M[ea] <- A */
|
|
A = 0; /* A <- 0 */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_Y: /* store address */
|
|
dat = Read (ea); /* get operand */
|
|
dat = (dat & ~I_EA) | (A & I_EA); /* merge address */
|
|
Write (ea, dat);
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_R: /* return address */
|
|
dat = Read (ea); /* get operand */
|
|
dat = (dat & ~I_EA) | (((PC + 1) & AMASK) << I_V_EA);
|
|
Write (ea, dat);
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_U: /* uncond transfer */
|
|
PCQ_ENTRY;
|
|
PC = ea; /* transfer */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_T: /* conditional transfer */
|
|
if ((A & SIGN) || /* A < 0 or */
|
|
((ir & SIGN) && t_switch)) { /* -T and Tswitch set? */
|
|
PCQ_ENTRY;
|
|
PC = ea; /* transfer */
|
|
}
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
/* Arithmetic and logical instructions */
|
|
|
|
case OP_A: /* add */
|
|
dat = Read (ea); /* get operand */
|
|
res = (A + dat) & DMASK; /* add */
|
|
if ((~A ^ dat) & (dat ^ res) & SIGN) /* calc overflow */
|
|
ovf_this_cycle = TRUE;
|
|
A = res; /* save result */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_S: /* sub */
|
|
dat = Read (ea); /* get operand */
|
|
res = (A - dat) & DMASK; /* subtract */
|
|
if ((A ^ dat) & (~dat ^ res) & SIGN) /* calc overflow */
|
|
ovf_this_cycle = TRUE;
|
|
A = res;
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_M: /* multiply high */
|
|
dat = Read (ea); /* get operand */
|
|
A = (Mul64 (A, dat, NULL) << 1) & DMASK; /* multiply */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_N: /* multiply low */
|
|
dat = Read (ea); /* get operand */
|
|
Mul64 (A, dat, &res); /* multiply */
|
|
A = res; /* keep low result */
|
|
delay = I_delay (opc, ea, op); /* total delay */
|
|
break;
|
|
|
|
case OP_D: /* divide */
|
|
dat = Read (ea); /* get operand */
|
|
if (Div32 (A, dat, &A)) /* divide; overflow? */
|
|
ovf_this_cycle = TRUE;
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
case OP_E: /* extract */
|
|
dat = Read (ea); /* get operand */
|
|
A = A & dat; /* and */
|
|
delay = I_delay (opc, ea, op);
|
|
break;
|
|
|
|
/* IO instructions */
|
|
|
|
case OP_P: /* output */
|
|
if (Q_LGP21) { /* LGP-21 */
|
|
ch = A >> 26; /* char, 6b */
|
|
if (ir & SIGN) /* 4b? convert */
|
|
ch = (ch & 0x3C) | 2;
|
|
dev = I_GETTK (ir); /* device select */
|
|
}
|
|
else { /* LGP-30 */
|
|
ch = I_GETTK (ir); /* char, always 6b */
|
|
dev = Q_OUTPT? DEV_PT: DEV_TT; /* device select */
|
|
}
|
|
reason = op_p (dev & DEV_MASK, ch); /* output */
|
|
delay = I_delay (sim_grtime (), ea, op); /* next instruction */
|
|
break;
|
|
|
|
case OP_I: /* input */
|
|
if (Q_LGP21) { /* LGP-21 */
|
|
ch = 0; /* initial shift */
|
|
sh4 = ir & SIGN; /* 4b/6b select */
|
|
dev = I_GETTK (ir); /* device select */
|
|
}
|
|
else { /* LGP-30 */
|
|
ch = I_GETTK (ir); /* initial shift */
|
|
sh4 = Q_IN4B; /* 4b/6b select */
|
|
dev = Q_INPT? DEV_PT: DEV_TT; /* device select */
|
|
}
|
|
if (dev == DEV_SHIFT) /* shift? */
|
|
A = shift_in (A, 0, sh4); /* shift 4/6b */
|
|
else reason = op_i (dev & DEV_MASK, ch, sh4); /* input */
|
|
delay = I_delay (sim_grtime (), ea, op); /* next instruction */
|
|
break;
|
|
|
|
case OP_Z:
|
|
if (Q_LGP21) { /* LGP-21 */
|
|
if (ea & 0xF80) { /* no stop? */
|
|
if (((ea & 0x800) && !bp32) || /* skip if any */
|
|
((ea & 0x400) && !bp16) || /* selected switch */
|
|
((ea & 0x200) && !bp8) || /* is off */
|
|
((ea & 0x100) && !bp4) || /* or if */
|
|
((ir & SIGN) && !OVF)) /* ovf sel and off */
|
|
PC = (PC + 1) & AMASK;
|
|
if (ir & SIGN) /* -Z? clr overflow */
|
|
OVF = 0;
|
|
}
|
|
else { /* stop */
|
|
lgp21_sov = (ir & SIGN)? 1: 0; /* pending sense? */
|
|
reason = STOP_STOP; /* stop */
|
|
}
|
|
}
|
|
else { /* LGP-30 */
|
|
if (out_done) /* P complete? */
|
|
out_done = 0;
|
|
else if (((ea & 0x800) && bp32) || /* bpt switch set? */
|
|
((ea & 0x400) && bp16) ||
|
|
((ea & 0x200) && bp8) ||
|
|
((ea & 0x100) && bp4)) ; /* don't stop or stall */
|
|
else if (out_strt) /* P pending? stall */
|
|
reason = STOP_STALL;
|
|
else reason = STOP_STOP; /* no, stop */
|
|
}
|
|
delay = I_delay (sim_grtime (), ea, op); /* next instruction */
|
|
break; /* end switch */
|
|
}
|
|
|
|
if (ovf_this_cycle) {
|
|
if (Q_LGP21) /* LGP-21? set OVF */
|
|
OVF = 1;
|
|
else reason = STOP_OVF; /* LGP-30? stop */
|
|
}
|
|
return reason;
|
|
}
|
|
|
|
/* Support routines */
|
|
|
|
uint32 Read (uint32 ea)
|
|
{
|
|
return M[ea] & MMASK;
|
|
}
|
|
|
|
void Write (uint32 ea, uint32 dat)
|
|
{
|
|
M[ea] = dat & MMASK;
|
|
return;
|
|
}
|
|
|
|
/* Input shift */
|
|
|
|
uint32 shift_in (uint32 a, uint32 dat, uint32 sh4)
|
|
{
|
|
if (sh4)
|
|
return (((a << 4) | (dat >> 2)) & DMASK);
|
|
return (((a << 6) | dat) & DMASK);
|
|
}
|
|
|
|
/* 32b * 32b multiply, signed */
|
|
|
|
uint32 Mul64 (uint32 a, uint32 b, uint32 *low)
|
|
{
|
|
uint32 sgn = a ^ b;
|
|
uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2;
|
|
|
|
if ((a == 0) || (b == 0)) { /* zero argument? */
|
|
if (low)
|
|
*low = 0;
|
|
return 0;
|
|
}
|
|
a = ABS (a);
|
|
b = ABS (b);
|
|
ah = (a >> 16) & M16; /* split operands */
|
|
bh = (b >> 16) & M16; /* into 16b chunks */
|
|
al = a & M16;
|
|
bl = b & M16;
|
|
rhi = ah * bh; /* high result */
|
|
rmid1 = ah * bl;
|
|
rmid2 = al * bh;
|
|
rlo = al * bl;
|
|
rhi = rhi + ((rmid1 >> 16) & M16) + ((rmid2 >> 16) & M16);
|
|
rmid1 = (rlo + (rmid1 << 16)) & M32; /* add mid1 to lo */
|
|
if (rmid1 < rlo) /* carry? incr hi */
|
|
rhi = rhi + 1;
|
|
rmid2 = (rmid1 + (rmid2 << 16)) & M32; /* add mid2 to to */
|
|
if (rmid2 < rmid1) /* carry? incr hi */
|
|
rhi = rhi + 1;
|
|
if (sgn & SIGN) { /* result negative? */
|
|
rmid2 = NEG (rmid2); /* negate */
|
|
rhi = (~rhi + (rmid2 == 0)) & M32;
|
|
}
|
|
if (low) /* low result */
|
|
*low = rmid2;
|
|
return rhi & M32;
|
|
}
|
|
|
|
/* 32b/32b divide (done as 32b'0/32b) */
|
|
|
|
t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q)
|
|
{
|
|
uint32 sgn = dvd ^ dvr;
|
|
uint32 i, quo;
|
|
|
|
dvd = ABS (dvd);
|
|
dvr = ABS (dvr);
|
|
if (dvd >= dvr)
|
|
return TRUE;
|
|
for (i = quo = 0; i < 31; i++) { /* 31 iterations */
|
|
quo = quo << 1; /* shift quotient */
|
|
dvd = dvd << 1; /* shift dividend */
|
|
if (dvd >= dvr) { /* step work? */
|
|
dvd = (dvd - dvr) & M32; /* subtract dvr */
|
|
quo = quo + 1;
|
|
}
|
|
}
|
|
quo = (quo + 1) & MMASK; /* round low bit */
|
|
if (sgn & SIGN) /* result -? */
|
|
quo = NEG (quo);
|
|
if (q) /* return quo */
|
|
*q = quo;
|
|
return FALSE; /* no overflow */
|
|
}
|
|
|
|
/* Rotational delay */
|
|
|
|
uint32 I_delay (uint32 opc, uint32 ea, uint32 op)
|
|
{
|
|
uint32 tmin = Q_LGP21? min_21[op]: min_30[op];
|
|
uint32 tmax = Q_LGP21? max_21[op]: max_30[op];
|
|
uint32 nsc, curp, newp, oprp, pcdelta, opdelta;
|
|
|
|
if (Q_LGP21) { /* LGP21 */
|
|
nsc = NSC_21; /* full rotation delay */
|
|
curp = log_to_phys_21[opc & SCMASK_21]; /* current phys pos */
|
|
newp = log_to_phys_21[PC & SCMASK_21]; /* new PC phys pos */
|
|
oprp = log_to_phys_21[ea & SCMASK_21]; /* ea phys pos */
|
|
pcdelta = (newp - curp + NSC_21) & SCMASK_21;
|
|
opdelta = (oprp - curp + NSC_21) & SCMASK_21;
|
|
}
|
|
else {
|
|
nsc = NSC_30;
|
|
curp = log_to_phys_30[opc & SCMASK_30];
|
|
newp = log_to_phys_30[PC & SCMASK_30];
|
|
oprp = log_to_phys_30[ea & SCMASK_30];
|
|
pcdelta = (newp - curp + NSC_30) & SCMASK_30;
|
|
opdelta = (oprp - curp + NSC_30) & SCMASK_30;
|
|
}
|
|
if (tmax == 0) { /* skip ea calc? */
|
|
if (pcdelta >= tmin) /* new PC >= min? */
|
|
return pcdelta - 1;
|
|
return pcdelta + nsc - 1;
|
|
}
|
|
if ((opdelta >= tmin) && (opdelta <= tmax))
|
|
return pcdelta - 1;
|
|
return pcdelta + nsc - 1;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat cpu_reset (DEVICE *dptr)
|
|
{
|
|
OVF = 0;
|
|
inp_strt = 0;
|
|
inp_done = 0;
|
|
out_strt = 0;
|
|
out_done = 0;
|
|
lgp21_sov = 0;
|
|
delay = 0;
|
|
lgp_vm_init ();
|
|
pcq_r = find_reg ("CQ", NULL, dptr);
|
|
if (pcq_r)
|
|
pcq_r->qptr = 0;
|
|
else return SCPE_IERR;
|
|
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Validate option, must be LGP30 */
|
|
|
|
t_stat cpu_set_30opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (Q_LGP21)
|
|
return SCPE_ARG;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Validate input option, must be LGP30 */
|
|
|
|
t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (Q_LGP21 || (cptr == NULL))
|
|
return SCPE_ARG;
|
|
if (strcmp (cptr, "TTI") == 0)
|
|
uptr->flags = uptr->flags & ~UNIT_INPT;
|
|
else if (strcmp (cptr, "PTR") == 0)
|
|
uptr->flags = uptr->flags | UNIT_INPT;
|
|
else return SCPE_ARG;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Validate output option, must be LGP30 */
|
|
|
|
t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (Q_LGP21 || (cptr == NULL))
|
|
return SCPE_ARG;
|
|
if (strcmp (cptr, "TTO") == 0)
|
|
uptr->flags = uptr->flags & ~UNIT_OUTPT;
|
|
else if (strcmp (cptr, "PTP") == 0)
|
|
uptr->flags = uptr->flags | UNIT_OUTPT;
|
|
else return SCPE_ARG;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set CPU to LGP21 or LPG30 */
|
|
|
|
t_stat cpu_set_model (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
if (val)
|
|
uptr->flags = uptr->flags & ~(UNIT_IN4B|UNIT_INPT|UNIT_OUTPT);
|
|
return reset_all (0);
|
|
}
|
|
|
|
/* Show CPU type and all options */
|
|
|
|
t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fputs (Q_LGP21? "LGP-21": "LGP-30", st);
|
|
if (uptr->flags & UNIT_TTSS_D)
|
|
fputs (", track/sector", st);
|
|
if (uptr->flags & UNIT_LGPH_D)
|
|
fputs (", LGP hex", st);
|
|
fputs (Q_MANI? ", manual": ", tape", st);
|
|
if (!Q_LGP21) {
|
|
fputs (Q_IN4B? ", 4b": ", 6b", st);
|
|
fputs (Q_INPT? ", in=PTR": ", in=TTI", st);
|
|
fputs (Q_OUTPT? ", out=PTP": ", out=TTO", st);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory examine */
|
|
|
|
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
if (vptr != NULL)
|
|
*vptr = Read (addr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory deposit */
|
|
|
|
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
if (addr >= MEMSIZE)
|
|
return SCPE_NXM;
|
|
Write (addr, val);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Execute */
|
|
|
|
t_stat cpu_set_exec (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
uint32 inst;
|
|
t_stat r;
|
|
|
|
if (cptr) {
|
|
inst = get_uint (cptr, 16, DMASK, &r);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
}
|
|
else inst = IR;
|
|
while ((r = cpu_one_inst (PC, inst)) == STOP_STALL) {
|
|
sim_interval = 0;
|
|
if ((r = sim_process_event ()))
|
|
return r;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/* Fill */
|
|
|
|
t_stat cpu_set_fill (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
uint32 inst;
|
|
t_stat r;
|
|
|
|
if (cptr) {
|
|
inst = get_uint (cptr, 16, DMASK, &r);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
IR = inst;
|
|
}
|
|
else IR = A;
|
|
return SCPE_OK;
|
|
}
|