/* 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, char *cptr, void *desc); | |
t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); | |
t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat cpu_set_exec (UNIT *uptr, int32 val, 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, 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, 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, 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, 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, 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, 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, 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; | |
} |