/* sigma_cpu.c: XDS Sigma CPU simulator | |
Copyright (c) 2007-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 central processor | |
The system state for the Sigma CPU is as follows: | |
RF[0:15][0:31]<0:31> register blocks | |
PSW1<0:31> processor status word 1 | |
CC<0:3> condition codes | |
PC<0:17> program counter (called IA in Sigma documentation) | |
PSW2<0:31> processor status word 2 | |
PSW2_WLK<0:3> write key (2b on S5-9) | |
PSW4<0:31> processor status word 4 (5X0 only) | |
MAP[0:511]<0:10> memory map (8b on S5-8) | |
WLK[0:2047]<0:3> write locks (256 2b entries on S5-9) | |
SSW<0:3> sense switches | |
PDF processor detected fault flag (S8-9, 5X0 only) | |
Notes on features not documented in the Reference Manuals: | |
1. Memory mapping was available for the Sigma 5 (see map diagnostic). | |
2. The Sigma 6/7 were field retrofitted with the LAS/LMS instructions | |
(see auto diagnostic). | |
3. The Sigma 8/9 returned different results for WD .45 (see Telefile | |
System exerciser). | |
4. Expanded memory beyond 128KB was retrofitted to the Sigma 5/6/7, | |
creating the so-called "Big 5/6/7." As a minimum, these systems | |
also included the "mode altered" feature and the 11b relocation map. | |
The Sigma CPU has two instruction formats, memory reference and immediate. | |
The memory reference format is: | |
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | |
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
|I| | | | | | |
|N| opcode | R | X | address | memory | |
|D| | | | | reference | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
where | |
IND = indirect flag | |
opcode = operation code | |
R = source/destination register | |
X = index register (0 if none) | |
address = operand address | |
The immediate format is: | |
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | |
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
| | | | | | |
|0| opcode | R | immediate | immediate | |
| | | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
This routine is the instruction decode routine for the Sigma CPU. | |
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: | |
HALT instruction | |
breakpoint encountered | |
invalid instruction and stop_op flag set | |
I/O error in I/O simulator | |
EXU loop exceeding limit | |
illegal interrupt or trap instruction | |
illegal register pointer | |
illegal vector | |
2. Interrupts. The interrupt structure consists of the following: | |
Each interrupt is part of a group that determines its priority. | |
The interrupt group is either controlled by a PSW inhibit or is | |
unconditional. Interrupts can be armed or disarmed (which controls | |
whether they are recognized at all) and enabled or disabled (which | |
controls whether they occur). See the sigma_io.c module for details. | |
3. Channels. The Sigma system has a channel-based I/O structure. Each | |
channel is represented by a set of registers. Channels test the | |
I/O transfer requests from devices. | |
4. Non-existent memory. On the Sigma, accesses to non-existent memory | |
trap. | |
5. Adding I/O devices. These modules must be modified: | |
sigma_defs.h add definitions | |
sigma_io.c add dispatches | |
sigma_sys.c add pointer to data structures to sim_devices | |
*/ | |
#include "sigma_io_defs.h" | |
#define CPUF_V_MODEL (UNIT_V_UF + 6) /* CPU model */ | |
#define CPUF_M_MODEL 0x7 | |
#define CPUF_MODEL (CPUF_M_MODEL << CPUF_V_MODEL) | |
#define CPUF_S5 (CPU_V_S5 << CPUF_V_MODEL) | |
#define CPUF_S6 (CPU_V_S6 << CPUF_V_MODEL) | |
#define CPUF_S7 (CPU_V_S7 << CPUF_V_MODEL) | |
#define CPUF_S8 (CPU_V_S8 << CPUF_V_MODEL) | |
#define CPUF_S7B (CPU_V_S7B << CPUF_V_MODEL) | |
#define CPUF_S9 (CPU_V_S9 << CPUF_V_MODEL) | |
#define CPUF_550 (CPU_V_550 << CPUF_V_MODEL) | |
#define CPUF_560 (CPU_V_560 << CPUF_V_MODEL) | |
#define CPUF_GETMOD(x) (((x) >> CPUF_V_MODEL) & CPUF_M_MODEL) | |
#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] = real_pc; | |
#define HIST_MIN 64 | |
#define HIST_MAX (1u << 20) | |
#define H_INST 0x00800000 | |
#define H_CHAN 0x00400000 | |
#define H_ITRP 0x00200000 | |
#define H_ABRT 0x00100000 | |
typedef struct { | |
uint32 typ_cc_pc; | |
uint32 ir; | |
uint32 rn; | |
uint32 rn1; | |
uint32 x; /* unused */ | |
uint32 ea; | |
uint32 op; | |
uint32 op1; | |
} InstHistory; | |
uint32 cpu_model = CPU_V_S7; /* CPU model */ | |
uint32 *M; /* memory */ | |
uint32 rf[RF_NBLK * RF_NUM] = { 0 }; /* register files */ | |
uint32 *R = rf; /* cur reg file */ | |
uint32 PSW1 = PSW1_DFLT; /* PSD */ | |
uint32 PSW2 = PSW2_DFLT; | |
uint32 PSW4 = 0; /* 5X0 only */ | |
uint32 CC; | |
uint32 PC; | |
uint32 PSW2_WLK = 0; /* write lock key */ | |
uint32 PSW_QRX9; /* Sigma 9 real extended */ | |
uint32 bvamqrx = BVAMASK; /* BVA mask, 17b/20b */ | |
uint32 SSW = 0; /* sense switches */ | |
uint32 cpu_pdf = 0; /* proc detected fault */ | |
uint32 cons_alarm = 0; /* console alarm */ | |
uint32 cons_alarm_enb = 0; /* alarm enable */ | |
uint32 cons_pcf = 0; | |
uint32 rf_bmax = 4; /* num reg blocks */ | |
uint32 exu_lim = 32; /* nested EXU limit */ | |
uint32 stop_op = 0; /* stop on ill op */ | |
uint32 cpu_astop = 0; /* address stop */ | |
uint32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ | |
int32 pcq_p = 0; /* PC queue ptr */ | |
REG *pcq_r = NULL; /* PC queue reg ptr */ | |
int32 hst_p = 0; /* history pointer */ | |
int32 hst_lnt = 0; /* history length */ | |
InstHistory *hst = NULL; /* inst history */ | |
extern uint32 int_hiact; /* highest act int */ | |
extern uint32 int_hireq; /* highest int req */ | |
t_stat cpu_svc (UNIT *uptr); | |
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, CONST char *cptr, void *desc); | |
t_stat cpu_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_set_opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_clr_opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_set_rblks (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_show_rblks (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
t_stat cpu_set_alarm (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cpu_show_alarm (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
t_stat cpu_show_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
void set_rf_display (uint32 *rfbase); | |
void inst_hist (uint32 ir, uint32 pc, uint32 typ); | |
uint32 cpu_one_inst (uint32 real_pc, uint32 IR); | |
uint32 ImmOp (uint32 ir, uint32 *imm); | |
uint32 EaP20 (uint32 IR, uint32 *bva, uint32 lnt); | |
uint32 EaSh (uint32 ir, uint32 *stype, uint32 *sc); | |
uint32 Add32 (uint32 s1, uint32 s2, uint32 cin); | |
uint32 SMul64 (uint32 a, uint32 b, uint32 *lo); | |
t_bool SDiv64 (uint32 dvdh, uint32 dvdl, uint32 dvr, uint32 *res, uint32 *rem); | |
uint32 Cmp32 (uint32 a, uint32 b); | |
uint32 Shift (uint32 rn, uint32 stype, uint32 sc); | |
uint32 TestSP1 (uint32 sp1, int32 mod); | |
uint32 ModWrSP (uint32 bva, uint32 sp, uint32 sp1, int32 mod); | |
uint32 cpu_int_mtx (uint32 vec, uint32 *cc); | |
uint32 cpu_trap_or_int (uint32 vec); | |
uint32 cpu_xpsd (uint32 ir, uint32 bva, uint32 ra); | |
uint32 cpu_pss (uint32 ir, uint32 bva, uint32 acc); | |
uint32 cpu_pls (uint32 IR); | |
void cpu_assemble_PSD (void); | |
uint32 cpu_new_PSD (uint32 lrp, uint32 p1, uint32 p2); | |
uint32 cpu_new_RP (uint32 rp); | |
uint32 cpu_new_PC (uint32 bva); | |
uint32 cpu_add_PC (uint32 pc, uint32 val); | |
t_stat cpu_bad_rblk (UNIT *uptr); | |
void cpu_fprint_one_inst (FILE *st, uint32 tcp, uint32 ir, uint32 rn, | |
uint32 rn1, uint32 ea, uint32 op, uint32 op1); | |
extern uint32 fp (uint32 op, uint32 rn, uint32 bva); | |
extern uint32 cis_dec (uint32 op, uint32 rn, uint32 bva); | |
extern uint32 cis_ebs (uint32 rn, uint32 disp); | |
extern void ShiftF (uint32 rn, uint32 stype, uint32 sc); | |
extern uint32 map_mmc (uint32 rn, uint32 map); | |
extern uint32 map_lra (uint32 rn, uint32 inst); | |
extern uint32 map_las (uint32 rn, uint32 bva); | |
extern uint32 map_lms (uint32 rn, uint32 bva); | |
extern t_stat io_init (void); | |
extern uint32 io_eval_int (void); | |
extern uint32 io_actv_int (void); | |
extern t_bool io_poss_int (void); | |
extern uint32 io_ackn_int (uint32 hireq); | |
extern uint32 io_rels_int (uint32 hiact, t_bool arm); | |
extern uint32 io_rwd (uint32 op, uint32 rn, uint32 bva); | |
extern uint32 io_sio (uint32 rn, uint32 bva); | |
extern uint32 io_tio (uint32 rn, uint32 bva); | |
extern uint32 io_tdv (uint32 rn, uint32 bva); | |
extern uint32 io_hio (uint32 rn, uint32 bva); | |
extern uint32 io_aio (uint32 rn, uint32 bva); | |
extern t_stat int_reset (DEVICE *dev); | |
extern void io_set_eimax (uint32 lnt); | |
extern void io_sclr_req (uint32 inum, uint32 val); | |
extern void io_sclr_arm (uint32 inum, uint32 val); | |
extern t_stat io_set_nchan (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
extern t_stat io_show_nchan (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
/* CPU data structures | |
cpu_dev CPU device descriptor | |
cpu_unit CPU unit | |
cpu_reg CPU register list | |
cpu_mod CPU modifier list | |
*/ | |
UNIT cpu_unit = { | |
UDATA (&cpu_svc, UNIT_FIX+CPUF_S7+CPUF_ALLOPT+UNIT_BINK, MAXMEMSIZE), | |
}; | |
UNIT cpu_rblk_unit = { | |
UDATA (&cpu_bad_rblk, UNIT_DIS, 0) | |
}; | |
REG cpu_reg[] = { | |
{ GRDATA (PC, PSW1, 16, VASIZE, PSW1_V_PC) }, | |
{ HRDATA (R0, rf[0], 32) }, /* addr in memory */ | |
{ HRDATA (R1, rf[1], 32) }, /* modified at exit */ | |
{ HRDATA (R2, rf[2], 32) }, /* to SCP */ | |
{ HRDATA (R3, rf[3], 32) }, | |
{ HRDATA (R4, rf[4], 32) }, | |
{ HRDATA (R5, rf[5], 32) }, | |
{ HRDATA (R6, rf[6], 32) }, | |
{ HRDATA (R7, rf[7], 32) }, | |
{ HRDATA (R8, rf[8], 32) }, | |
{ HRDATA (R9, rf[9], 32) }, | |
{ HRDATA (R10, rf[10], 32) }, | |
{ HRDATA (R11, rf[11], 32) }, | |
{ HRDATA (R12, rf[12], 32) }, | |
{ HRDATA (R13, rf[13], 32) }, | |
{ HRDATA (R14, rf[14], 32) }, | |
{ HRDATA (R15, rf[15], 32) }, | |
{ HRDATA (PSW1, PSW1, 32) }, | |
{ HRDATA (PSW2, PSW2, 32) }, | |
{ HRDATA (PSW4, PSW4, 32) }, | |
{ GRDATA (CC, PSW1, 16, 4, PSW1_V_CC) }, | |
{ GRDATA (RP, PSW2, 16, 4, PSW2_V_RP) }, | |
{ FLDATA (SSW1, SSW, 3) }, | |
{ FLDATA (SSW2, SSW, 2) }, | |
{ FLDATA (SSW3, SSW, 1) }, | |
{ FLDATA (SSW4, SSW, 0) }, | |
{ FLDATA (PDF, cpu_pdf, 0) }, | |
{ FLDATA (ALARM, cons_alarm, 0) }, | |
{ FLDATA (ALENB, cons_alarm_enb, 0), REG_HRO }, | |
{ FLDATA (PCF, cons_pcf, 0) }, | |
{ DRDATA (EXULIM, exu_lim, 8), PV_LEFT + REG_NZ }, | |
{ FLDATA (STOP_ILL, stop_op, 0) }, | |
{ BRDATA (REG, rf, 16, 32, RF_NUM * RF_NBLK) }, | |
{ DRDATA (RBLKS, rf_bmax, 5), REG_HRO }, | |
{ BRDATA (PCQ, pcq, 16, VASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, | |
{ DRDATA (PCQP, pcq_p, 6), REG_HRO }, | |
{ HRDATA (WRU, sim_int_char, 8) }, | |
{ NULL } | |
}; | |
MTAB cpu_mod[] = { | |
{ CPUF_MODEL, CPUF_S5, "Sigma 5", "SIGMA5", &cpu_set_type }, | |
{ CPUF_MODEL, CPUF_S6, "Sigma 6", "SIGMA6", &cpu_set_type }, | |
{ CPUF_MODEL, CPUF_S7, "Sigma 7", "SIGMA7", &cpu_set_type }, | |
{ CPUF_MODEL, CPUF_S7B, "Sigma 7 BigMem", "SIGMA7B", & cpu_set_type }, | |
// { CPUF_MODEL, CPUF_S8, "Sigma 8", "SIGMA8", &cpu_set_type }, | |
// { CPUF_MODEL, CPUF_S9, "Sigma 9", "SIGMA9", &cpu_set_type }, | |
// { CPUF_MODEL, CPUF_550, "550", "550", &cpu_set_type }, | |
// { CPUF_MODEL, CPUF_560, "560", "560", &cpu_set_type }, | |
{ MTAB_XTD|MTAB_VDV, 0, "register blocks", "RBLKS", | |
&cpu_set_rblks, &cpu_show_rblks }, | |
{ MTAB_XTD|MTAB_VDV, 0, "channels", "CHANNELS", | |
&io_set_nchan, &io_show_nchan }, | |
{ CPUF_FP, CPUF_FP, "floating point", "FP", &cpu_set_opt }, | |
{ CPUF_FP, 0, "no floating point", NULL }, | |
{ MTAB_XTD|MTAB_VDV, CPUF_FP, NULL, "NOFP", &cpu_clr_opt }, | |
{ CPUF_DEC, CPUF_DEC, "decimal", "DECIMAL", &cpu_set_opt }, | |
{ CPUF_DEC, 0, "no decimal", NULL }, | |
{ MTAB_XTD|MTAB_VDV, CPUF_DEC, NULL, "NODECIMAL", &cpu_clr_opt }, | |
{ CPUF_LAMS, CPUF_LAMS, "LAS/LMS", "LASLMS", &cpu_set_opt }, | |
{ CPUF_LAMS, 0, "no LAS/LMS", NULL }, | |
{ MTAB_XTD|MTAB_VDV, CPUF_LAMS, NULL, "NOLASLMS", &cpu_clr_opt }, | |
{ CPUF_MAP, CPUF_MAP, "map", "MAP", &cpu_set_opt }, | |
{ CPUF_MAP, 0, "no map", NULL }, | |
{ MTAB_XTD|MTAB_VDV, CPUF_MAP, NULL, "NOMAP", &cpu_clr_opt }, | |
{ CPUF_WLK, CPUF_WLK, "write lock", "WRITELOCK", &cpu_set_opt }, | |
{ CPUF_WLK, 0, "no write lock", NULL }, | |
{ MTAB_XTD|MTAB_VDV, CPUF_WLK, NULL, "NOWRITELOCK", &cpu_clr_opt }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "ALARM", "ALON", &cpu_set_alarm, &cpu_show_alarm }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ALOFF", & cpu_set_alarm }, | |
{ CPUF_MSIZE, (1u << 15), NULL, "32K", &cpu_set_size }, | |
{ CPUF_MSIZE, (1u << 16), NULL, "64K", &cpu_set_size }, | |
{ CPUF_MSIZE, (1u << 17), NULL, "128K", &cpu_set_size }, | |
{ CPUF_MSIZE, (1u << 18), NULL, "256K", &cpu_set_size }, | |
{ CPUF_MSIZE, (1u << 19), NULL, "512K", &cpu_set_size }, | |
{ CPUF_MSIZE, (1u << 20), NULL, "1M", &cpu_set_size }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, BY, "BA", NULL, | |
NULL, &cpu_show_addr }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, HW, "HA", NULL, | |
NULL, &cpu_show_addr }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, WD, "WA", NULL, | |
NULL, &cpu_show_addr }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, DW, "DA", NULL, | |
NULL, &cpu_show_addr }, | |
{ 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, | |
1, 16, 20, 1, 16, 32, | |
&cpu_ex, &cpu_dep, &cpu_reset, | |
NULL, NULL, NULL, | |
}; | |
static uint8 anlz_tab[128] = { | |
0x9, 0x9, 0x9, 0x9, 0x8, 0x8, 0x8, 0x8, /* 00 - 0F */ | |
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, | |
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, /* 10 - 1F */ | |
0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, | |
0x9, 0x9, 0x9, 0x9, 0x8, 0x8, 0x8, 0x8, /* 20 - 2F */ | |
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, | |
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, /* 30 - 3F */ | |
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, | |
0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x8, 0x8, /* 40 - 4F */ | |
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, | |
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, /* 50 - 5F */ | |
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, | |
0x1, 0x1, 0x1, 0x1, 0x8, 0x8, 0x8, 0x8, /* 60 - 6F */ | |
0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, | |
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* 70 - 7F */ | |
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 | |
}; | |
cpu_var_t cpu_tab[] = { | |
/* psw1_mbz psw2_mbz m_map1 pamask eint chan | |
cc standard optional */ | |
{ 0x080E0000, 0xC8FFFE0F, 0x0FC, PAMASK17, 14, 8, /* S5 */ | |
CC1|CC2, 0, CPUF_MAP|CPUF_WLK|CPUF_FP }, | |
{ 0x080E0000, 0xC8FFFE0F, 0x0FC, PAMASK17, 14, 8, /* S6 */ | |
CC1|CC2, CPUF_STR|CPUF_MAP|CPUF_WLK|CPUF_DEC, CPUF_FP|CPUF_LAMS }, | |
{ 0x080E0000, 0xC8FFFE0F, 0x0FC, PAMASK17, 14, 8, /* S7 */ | |
CC1|CC2, CPUF_STR|CPUF_MAP|CPUF_WLK, CPUF_FP|CPUF_DEC|CPUF_LAMS }, | |
{ 0x080E0000, 0xC8FFFE0F, 0x0FC, PAMASK20, 14, 8, /* S7B */ | |
CC1|CC2, CPUF_STR|CPUF_MAP|CPUF_WLK, CPUF_FP|CPUF_DEC|CPUF_LAMS }, | |
{ 0x084E0000, 0xC8FF00C7, 0x0FC, PAMASK17, 14, 8, /* S8 */ | |
CC1|CC2|CC3, CPUF_STR|CPUF_FP|CPUF_WLK|CPUF_LAMS, 0 }, | |
{ 0x08060000, 0xC8400007, 0x0FC, PAMASK22, 14, 8, /* S9 */ | |
CC1|CC2|CC3, CPUF_STR|CPUF_MAP|CPUF_WLK|CPUF_DEC|CPUF_FP|CPUF_LAMS, 0 }, | |
{ 0x002E0000, 0x080FFFC3, 0x7FE, PAMASK20, 4, 4, /* 550 */ | |
CC1|CC2|CC3|CC4, CPUF_MAP|CPUF_WLK|CPUF_LAMS, CPUF_FP }, | |
{ 0x000E0000, 0x080FFFC3, 0x7FE, PAMASK20, 4, 4, /* 560 */ | |
CC1|CC2|CC3|CC4, CPUF_STR|CPUF_MAP|CPUF_WLK|CPUF_DEC|CPUF_FP|CPUF_LAMS, 0 } | |
}; | |
/* Simulation loop */ | |
t_stat sim_instr (void) | |
{ | |
uint32 ir, rpc, old_PC; | |
t_stat reason, tr, tr2; | |
/* Restore register state */ | |
if (io_init ()) /* init IO; conflict? */ | |
return STOP_INVIOC; | |
reason = 0; | |
if (cpu_new_PSD (1, PSW1, PSW2)) /* restore PSD, RP etc */ | |
return STOP_INVPSD; | |
int_hireq = io_eval_int (); | |
/* Main instruction fetch/decode loop */ | |
while (reason == 0) { /* loop until stop */ | |
PSW2 &= ~PSW2_RA; /* clr reg altered */ | |
if (cpu_astop) { /* debug stop? */ | |
cpu_astop = 0; | |
return STOP_ASTOP; | |
} | |
if (sim_interval <= 0) { /* event queue? */ | |
if ((reason = sim_process_event ())) /* process */ | |
break; | |
int_hireq = io_eval_int (); /* re-evaluate intr */ | |
} | |
sim_interval = sim_interval - 1; /* count down */ | |
if (int_hireq < NO_INT) { /* interrupt req? */ | |
uint32 sav_hi, vec, wd, op; | |
sav_hi = int_hireq; /* save level */ | |
vec = io_ackn_int (int_hireq); /* get vector */ | |
if (vec == 0) { /* illegal vector? */ | |
reason = STOP_ILLVEC; /* something wrong */ | |
break; | |
} | |
ReadPW (vec, &wd); /* read vector */ | |
op = I_GETOP (wd); /* get opcode */ | |
if ((op == OP_MTB) || (op == OP_MTH) || (op == OP_MTW)) { | |
uint32 res; | |
tr2 = cpu_int_mtx (vec, &res); /* do single cycle */ | |
io_sclr_req (sav_hi, 0); /* clear request */ | |
io_sclr_arm (sav_hi, 1); /* set armed */ | |
if ((res == 0) && /* count overflow */ | |
((vec >= VEC_C1P) && (vec <= VEC_C4P))) /* on clock? */ | |
io_sclr_req (INTV (INTG_CTR, vec - VEC_C1P), 1); | |
int_hiact = io_actv_int (); /* re-eval active */ | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
} | |
else tr2 = cpu_trap_or_int (vec); /* XPSD/PSS intr */ | |
if (tr2 & TR_FL) { /* trap? */ | |
if (QCPU_S89_5X0) /* S89 or 5X0? */ | |
tr2 = cpu_trap_or_int (tr2); /* try again */ | |
reason = (tr2 == TR_INVTRP)? STOP_ILLTRP: STOP_TRPT; | |
} | |
else reason = tr2; /* normal status code */ | |
} | |
else { /* normal instruction */ | |
if (sim_brk_summ && | |
sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ | |
reason = STOP_IBKPT; /* stop simulation */ | |
sim_interval++; /* don't count non-executed instruction */ | |
break; | |
} | |
if (PSW_QRX9 && (PC & PSW1_XA)) /* S9 real ext && ext? */ | |
rpc = (PSW2 & PSW2_EA) | (PC & ~PSW1_XA); /* 22b phys address */ | |
else rpc = PC; /* standard 17b PC */ | |
old_PC = PC; /* save PC */ | |
PC = cpu_add_PC (PC, 1); /* increment PC */ | |
if (((tr = ReadW (rpc << 2, &ir, VI)) != 0) || /* fetch inst, err? */ | |
((tr = cpu_one_inst (rpc, ir)) != 0)) { /* exec inst, error? */ | |
if (tr & TR_FL) { /* trap? */ | |
PC = old_PC; /* roll back PC */ | |
tr2 = cpu_trap_or_int (tr); /* do trap */ | |
if (tr2 & TR_FL) { /* trap? */ | |
if (QCPU_S89_5X0) /* S89 or 5X0? */ | |
tr2 = cpu_trap_or_int (tr2); /* try again */ | |
reason = (tr2 == TR_INVTRP)? STOP_ILLTRP: STOP_TRPT; | |
} /* end if trap-in-trap */ | |
else reason = tr2; /* normal status */ | |
} /* end if trap */ | |
else reason = tr; /* normal status */ | |
if ((reason >= STOP_ROLLBACK) && /* roll back PC? */ | |
(reason <= STOP_MAX)) | |
PC = old_PC; | |
} /* end if abnormal status */ | |
} /* end else normal */ | |
} /* end while */ | |
/* Simulation halted */ | |
pcq_r->qptr = pcq_p; /* update pc q ptr */ | |
cpu_assemble_PSD (); /* visible PSD */ | |
set_rf_display (R); /* visible registers */ | |
return reason; | |
} | |
/* Execute one instruction */ | |
uint32 cpu_one_inst (uint32 real_pc, uint32 IR) | |
{ | |
uint32 op, rn, bva, opnd, opnd1, opnd2, t; | |
uint32 res, res1, tr, stype, sc, cnt; | |
uint32 sa, da, mask, c, c1, i, lim, aop, exu_cnt; | |
int32 sop, sop1; | |
t_bool mprot; | |
exu_cnt = 0; /* init EXU count */ | |
EXU_LOOP: | |
if (hst_lnt) /* history? record */ | |
inst_hist (IR, real_pc, H_INST); | |
op = I_GETOP (IR); /* get opcode */ | |
rn = I_GETRN (IR); /* get reg num */ | |
switch (op) { | |
/* Loads and stores */ | |
case OP_LI: /* load immediate */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sext to 32b */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LB: /* load byte */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadB (bva, &opnd, VR)) != 0) /* read byte */ | |
return tr; | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LH: /* load halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = SEXT_H_W (opnd) & WMASK; /* sext to 32b */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LCH: /* load comp hw */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = SEXT_H_W (opnd); /* sext to 32b */ | |
opnd = NEG_W (opnd); /* negate */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LAH: /* load abs hw */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
if (opnd & HSIGN) /* negative? */ | |
opnd = NEG_W (opnd) & HMASK; /* negate */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LW: /* load word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_LCW: /* load comp word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
opnd = NEG_W (opnd); /* negate */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
if (opnd == WSIGN) { /* overflow? */ | |
CC |= CC2; /* set CC2 */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else CC &= ~CC2; /* no, clear CC2 */ | |
break; | |
case OP_LAW: /* load abs word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
if (opnd & WSIGN) /* negative? */ | |
opnd = NEG_W (opnd); /* take abs value */ | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
if (opnd == WSIGN) { /* overflow? */ | |
CC |= CC2; /* set CC2 */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else CC &= ~CC2; /* no, clear CC2 */ | |
break; | |
case OP_LS: /* load selective */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = (R[rn] & ~R[rn|1]) | (opnd & R[rn|1]); /* load under mask */ | |
CC34_W (res); /* set cc's */ | |
R[rn] = res; /* store result */ | |
break; | |
case OP_LAS: /* load and set */ | |
if ((cpu_unit.flags & CPUF_LAMS) == 0) /* not included? */ | |
return TR_NXI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = map_las (rn, bva)) != 0) /* do instruction */ | |
return tr; | |
CC34_W (R[rn]); /* set CC's */ | |
break; | |
case OP_LVAW: /* load virt addr */ | |
if (!QCPU_5X0) /* 5X0 only */ | |
return TR_NXI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
R[rn] = bva >> 2; /* store */ | |
break; | |
case OP_LD: /* load doubleword */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
if ((opnd == 0) && (opnd1 != 0)) /* 0'non-zero? */ | |
CC = (CC & ~CC4) | CC3; /* set CC3 */ | |
else CC34_W (opnd); /* else hi sets CC */ | |
R[rn|1] = opnd1; /* store result */ | |
R[rn] = opnd; | |
break; | |
case OP_LCD: /* load comp double */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
NEG_D (opnd, opnd1); /* negate */ | |
if ((opnd == 0) && (opnd1 != 0)) /* 0'non-zero? */ | |
CC = (CC & ~CC4) | CC3; /* set CC3 */ | |
else CC34_W (opnd); /* else hi sets CC */ | |
R[rn|1] = opnd1; /* store result */ | |
R[rn] = opnd; | |
if ((opnd == WSIGN) && (opnd1 == 0)) { /* overflow? */ | |
CC |= CC2; /* set CC2 */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else CC &= ~CC2; /* no, clear CC2 */ | |
break; | |
case OP_LAD: /* load abs double */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
if (opnd & WSIGN) /* negative? */ | |
NEG_D (opnd, opnd1); /* take abs value */ | |
if ((opnd == 0) && (opnd1 != 0)) /* 0'non-zero? */ | |
CC = (CC & ~CC4) | CC3; /* set CC3 */ | |
else CC34_W (opnd); /* else hi sets CC */ | |
R[rn|1] = opnd1; /* store result */ | |
R[rn] = opnd; | |
if ((opnd == WSIGN) && (opnd1 == 0)) { /* overflow? */ | |
CC |= CC2; /* set CC2 */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else CC &= ~CC2; /* no, clear CC2 */ | |
break; | |
/* Note: the Sigma 7 does not prove the instruction can execute successfully | |
before starting to load registers; the Sigma 9 (and the simulator) do. */ | |
case OP_LM: /* load multiple */ | |
lim = CC? CC: 16; /* CC sets count */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW ((bva + ((lim - 1) << 2)) & bvamqrx, &opnd, VR)) != 0) | |
return tr; /* test readability */ | |
for (i = 0; i < lim; i++) { /* loop thru reg */ | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read next */ | |
return tr; | |
R[rn] = opnd; /* store in reg */ | |
bva = (bva + 4) & bvamqrx; /* next word */ | |
rn = (rn + 1) & RNMASK; /* next reg */ | |
PSW2 |= PSW2_RA; /* reg altered */ | |
} | |
break; | |
case OP_LCFI: /* load cc, flt immed */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
if (IR & IRB(10)) /* load CC's? */ | |
CC = (opnd >> 4) & 0xF; | |
if (IR & IRB(11)) /* load FP ctrls? */ | |
PSW1 = ((PSW1 & ~PSW1_FPC) | /* set ctrls */ | |
((opnd & PSW1_M_FPC) << PSW1_V_FPC)) & | |
~cpu_tab[cpu_model].psw1_mbz; /* clear mbz */ | |
break; | |
case OP_LCF: /* load cc, flt */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadB (bva, &opnd, VR)) != 0) /* get byte */ | |
return tr; | |
if (IR & IRB(10)) /* load CC's? */ | |
CC = (opnd >> 4) & 0xF; | |
if (IR & IRB(11)) /* load FP ctrls? */ | |
PSW1 = ((PSW1 & ~PSW1_FPC) | /* set ctrls */ | |
((opnd & PSW1_M_FPC) << PSW1_V_FPC)) & | |
~cpu_tab[cpu_model].psw1_mbz; /* clear mbz */ | |
break; | |
case OP_XW: /* exchange word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
if ((tr = WriteW (bva, R[rn], VW)) != 0) /* write reg */ | |
return tr; | |
CC34_W (opnd); /* set cc's */ | |
R[rn] = opnd; /* store result */ | |
break; | |
case OP_STB: /* store byte */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = WriteB (bva, R[rn], VW)) != 0) /* store */ | |
return tr; | |
break; | |
case OP_STH: /* store halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = WriteH (bva, R[rn], VW)) != 0) /* store */ | |
return tr; | |
if (R[rn] == (SEXT_H_W (R[rn]) & WMASK)) /* rn a sext hw? */ | |
CC &= ~CC2; /* yes, clr CC2 */ | |
else CC |= CC2; | |
break; | |
case OP_STW: /* store word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = WriteW (bva, R[rn], VW)) != 0) /* store */ | |
return tr; | |
break; | |
case OP_STD: /* store doubleword */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = WriteD (bva, R[rn], R[rn|1], VW)) != 0) /* store */ | |
return tr; | |
break; | |
case OP_STS: /* store selective */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = (opnd & ~R[rn|1]) | (R[rn] & R[rn|1]); /* set under mask */ | |
if ((tr = WriteW (bva, res, VW)) != 0) /* store */ | |
return tr; | |
break; | |
/* Note: the Sigma 7 does not prove the instruction can execute successfully | |
before starting to store registers; the Sigma 9 (and the simulator) do. */ | |
case OP_STM: /* store multiple */ | |
lim = CC? CC: 16; /* CC sets count */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW ((bva + ((lim - 1) << 2)) & bvamqrx, &opnd, VW)) != 0) | |
return tr; /* test writeability */ | |
for (i = 0; i < lim; i++) { /* loop thru reg */ | |
if ((tr = WriteW (bva, R[rn], VW)) != 0) /* write reg */ | |
return tr; | |
bva = (bva + 4) & bvamqrx; /* next address */ | |
rn = (rn + 1) & RNMASK; /* next register */ | |
} | |
break; | |
case OP_STCF: /* store cc, flt */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
res = (CC << 4) | ((PSW1 >> PSW1_V_FPC) & PSW1_M_FPC); | |
if ((tr = WriteB (bva, res, VW)) != 0) /* store */ | |
return tr; | |
break; | |
/* Analyze: Sigma 9 uses <5:7> for trap codes, the 5X0 uses <1:3> */ | |
case OP_ANLZ: /* analyze */ | |
mprot = ((PSW1 & (PSW1_MS|PSW1_MM)) == PSW1_MM) && | |
((PSW2 & (PSW2_MA9|PSW2_MA5X0)) != 0); /* mstr prot */ | |
sc = QCPU_5X0? 4: 0; /* 5X0 vs S9 */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) { /* get eff address */ | |
if (mprot && QCPU_S9) { /* S9 mprot? */ | |
R[rn] = 0x07000000 | (bva >> 2); /* show failure */ | |
break; | |
} | |
return tr; /* others trap */ | |
} | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) { /* get word */ | |
if (mprot) { /* trap, mprot? */ | |
R[rn] = (0x30000000 >> sc) | (bva >> 2); /* show failure */ | |
break; | |
} | |
return tr; /* others trap */ | |
} | |
aop = I_GETOP (opnd); /* get opcode */ | |
CC = anlz_tab[aop] & (CC1|CC2|CC4); /* set CC1,CC2,CC4 */ | |
if (TST_IND (opnd)) /* indirect? */ | |
CC |= CC3; /* set CC3 */ | |
if ((anlz_tab[aop] & CC4) == 0) { /* mem ref? */ | |
uint32 aln = anlz_tab[aop] >> 2; /* get length */ | |
if ((tr = Ea (opnd, &bva, VR, aln)) != 0) { /* get eff addr */ | |
if (mprot) { /* trap, mprot? */ | |
R[rn] = (0x10000000 >> sc) | (bva >> 2); /* show failure */ | |
break; | |
} | |
return tr; /* others trap */ | |
} | |
R[rn] = bva >> aln; /* cvt addr */ | |
} | |
break; | |
/* Interpret */ | |
case OP_INT: /* interpret */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* get word */ | |
return tr; | |
CC = (opnd >> 28) & 0xF; /* <0:3> -> CC */ | |
R[rn] = (opnd >> 16) & 0xFFF; /* <4:15> -> Rn */ | |
R[rn|1] = opnd & 0xFFFF; /* <16:31> -> Rn|1 */ | |
break; | |
/* Arithmetic */ | |
case OP_AI: /* add immediate */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sext to 32b */ | |
res = Add32 (R[rn], opnd, 0); /* add, set CC's */ | |
R[rn] = res; /* store result */ | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovfo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_AH: /* add halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = SEXT_H_W (opnd) & WMASK; /* sext to 32b */ | |
res = Add32 (R[rn], opnd, 0); /* add, set CC's */ | |
R[rn] = res; /* store result */ | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_AW: /* add word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = Add32 (R[rn], opnd, 0); /* add, set CC's */ | |
R[rn] = res; /* store result */ | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_AD: /* add doubleword */ | |
if (QCPU_S89_5X0 && (rn & 1)) /* invalid reg? */ | |
return TR_INVREG; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
res1 = Add32 (R[rn|1], opnd1, 0); /* add low, high */ | |
res = Add32 (R[rn], opnd, (CC & CC1) != 0); | |
if ((res == 0) && (res1 != 0)) /* 0'non-zero? */ | |
CC = (CC & ~CC4) | CC3; /* set CC3 */ | |
R[rn|1] = res1; /* store */ | |
R[rn] = res; | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_AWM: /* add word to memory */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = Add32 (R[rn], opnd, 0); /* add, set CC's */ | |
if ((tr = WriteW (bva, res, VW)) != 0) /* store */ | |
return tr; | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_SH: /* subtract halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = SEXT_H_W (opnd) & WMASK; /* sext to 32b */ | |
res = Add32 (R[rn], opnd ^ WMASK, 1); /* subtract, set CC's */ | |
R[rn] = res; /* store */ | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_SW: /* subtract word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = Add32 (R[rn], opnd ^ WMASK, 1); /* subtract */ | |
R[rn] = res; /* store */ | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_SD: /* subtract doubleword */ | |
if (QCPU_S89_5X0 && (rn & 1)) /* invalid reg? */ | |
return TR_INVREG; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
res1 = Add32 (R[rn|1], opnd1 ^ WMASK, 1); /* sub low, high */ | |
res = Add32 (R[rn], opnd ^ WMASK, (CC & CC1) != 0); | |
if ((res == 0) && (res1 != 0)) /* 0'non-zero? */ | |
CC = (CC & ~CC4) | CC3; /* set CC3 */ | |
R[rn|1] = res1; /* store */ | |
R[rn] = res; | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_MI: /* multiply immed */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sext to 32b */ | |
res = SMul64 (R[rn|1], opnd, &res1); /* 64b product */ | |
R[rn] = res; /* store */ | |
R[rn|1] = res1; | |
break; | |
case OP_MH: /* multiply half */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
sop = (int32) SEXT_H_W (R[rn]); /* sext operands */ | |
sop1 = (int32) SEXT_H_W (opnd); | |
res = (uint32) ((sop * sop1) & WMASK); /* product */ | |
CC34_W (res); /* set CC's */ | |
R[rn|1] = res; /* store */ | |
break; | |
case OP_MW: /* multiply word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
res = SMul64 (R[rn|1], opnd, &res1); /* 64b product */ | |
R[rn] = res; /* store */ | |
R[rn|1] = res1; | |
break; | |
case OP_DH: /* divide halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
sop = (int32) R[rn]; /* sext operands */ | |
sop1 = (int32) SEXT_H_W (opnd); | |
if ((opnd == 0) || /* div by zero? */ | |
((R[rn] == WSIGN) && /* -2^31 / -1? */ | |
(opnd == HMASK))) { | |
CC |= CC2; /* overflow */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else { | |
res = (uint32) ((sop / sop1) & WMASK); /* quotient */ | |
CC &= ~CC2; /* no overflow */ | |
CC34_W (res); /* set CC's */ | |
R[rn] = res; /* store */ | |
} | |
break; | |
case OP_DW: /* divide word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd2, VR)) != 0) /* get divisor */ | |
return tr; | |
if (rn & 1) /* odd reg? */ | |
opnd = (R[rn] & WSIGN)? WMASK: 0; /* high is sext low */ | |
else opnd = R[rn]; | |
opnd1 = R[rn|1]; /* low divd */ | |
if (SDiv64 (opnd, opnd1, opnd2, &res, &res1)) { /* 64b/32b divide */ | |
CC |= CC2; /* overflow, set CC2 */ | |
if (PSW1 & PSW1_AM) /* trap if enabled */ | |
return TR_FIX; | |
} | |
else { | |
CC &= ~CC2; /* clear CC2 */ | |
CC34_W (res); /* set CC's from quo */ | |
R[rn] = res1; /* store */ | |
R[rn|1] = res; | |
} | |
break; | |
case OP_MTB: /* mod and test byte */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadB (bva, &opnd, VR)) != 0) /* read byte */ | |
return tr; | |
opnd1 = SEXT_RN_W (rn) & BMASK; /* mod is sext rn */ | |
res = (opnd + opnd1) & BMASK; /* do zext add */ | |
if (res < opnd) /* carry out? */ | |
CC = CC1; | |
else CC = 0; | |
CC34_W (res); /* set cc's */ | |
if (rn && /* any mod? */ | |
((tr = WriteB (bva, res, VW)) != 0)) /* rewrite */ | |
return tr; | |
break; | |
case OP_MTH: /* mod and test half */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = opnd & HMASK; /* 16 operands */ | |
opnd1 = SEXT_RN_W (rn) & HMASK; /* mod is sext rn */ | |
res = opnd + opnd1; /* 16b add */ | |
if ((res & HMASK) == 0) /* 16b CC tests */ | |
CC = 0; | |
else if (res & HSIGN) | |
CC = CC4; | |
else CC = CC3; | |
if ((res & ~HMASK) != 0) /* carry? */ | |
CC |= CC1; | |
if (((opnd ^ ~opnd1) & (opnd ^ res)) & HSIGN) /* set overflow */ | |
CC |= CC2; | |
if (rn && /* any mod? */ | |
((tr = WriteH (bva, res, VW)) != 0)) /* rewrite */ | |
return tr; | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
case OP_MTW: /* mod and test word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* get word */ | |
return tr; | |
opnd1 = SEXT_RN_W (rn) & WMASK; /* mod is sext rn */ | |
res = Add32 (opnd, opnd1, 0); /* do add */ | |
if (rn && /* any mod? */ | |
((tr = WriteW (bva, res, VW)) != 0)) /* rewrite */ | |
return tr; | |
if ((CC & CC2) && (PSW1 & PSW1_AM)) /* ovflo, enabled? */ | |
return TR_FIX; | |
break; | |
/* Logical */ | |
case OP_AND: /* and */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* get word */ | |
return tr; | |
res = R[rn] & opnd; | |
CC34_W (res); /* set CC's */ | |
R[rn] = res; /* store */ | |
break; | |
case OP_OR: /* or */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* get word */ | |
return tr; | |
res = R[rn] | opnd; | |
CC34_W (res); /* set CC's */ | |
R[rn] = res; /* store */ | |
break; | |
case OP_EOR: /* xor */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* get word */ | |
return tr; | |
res = R[rn] ^ opnd; | |
CC34_W (res); /* set CC's */ | |
R[rn] = res; /* store */ | |
break; | |
/* Compares */ | |
case OP_CI: /* compare immed */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sext to 32b */ | |
CC234_CMP (R[rn], opnd); /* set CC's */ | |
break; | |
case OP_CB: /* compare byte */ | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadB (bva, &opnd, VR)) != 0) /* read byte */ | |
return tr; | |
opnd1 = R[rn] & BMASK; /* zext operand */ | |
CC234_CMP (opnd1, opnd); /* set CC's */ | |
break; | |
case OP_CH: /* compare halfword */ | |
if ((tr = Ea (IR, &bva, VR, HW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadH (bva, &opnd, VR)) != 0) /* read halfword */ | |
return tr; | |
opnd = SEXT_H_W (opnd) & WMASK; /* sext to 32b */ | |
CC234_CMP (R[rn], opnd); /* set CC's */ | |
break; | |
case OP_CW: /* compare word */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
CC234_CMP (R[rn], opnd); /* set CC's */ | |
break; | |
case OP_CD: /* compare double */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read doubleword */ | |
return tr; | |
CC &= ~(CC3|CC4); | |
if (R[rn] != opnd) /* hi unequal? */ | |
CC |= Cmp32 (R[rn], opnd); | |
else if (R[rn|1] != opnd1) /* low unequal? */ | |
CC |= (R[rn|1] < opnd1)? CC4: CC3; /* like signs */ | |
break; | |
case OP_CS: /* compare select */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
opnd1 = R[rn] & R[rn|1]; /* mask operands */ | |
opnd = opnd & R[rn|1]; | |
if (opnd1 < opnd) /* unsigned compare */ | |
CC = (CC & ~CC3) | CC4; | |
else if (opnd1 > opnd) | |
CC = (CC & ~CC4) | CC3; | |
else CC &= ~(CC3|CC4); | |
break; | |
case OP_CLR: /* compare limit reg */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
CC = Cmp32 (R[rn], opnd) | /* compare high reg */ | |
(Cmp32 (R[rn|1], opnd) << 2); /* compare low reg */ | |
break; | |
case OP_CLM: /* compare limit mem */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* get doubleword */ | |
return tr; | |
CC = Cmp32 (R[rn], opnd) | /* compare high mem */ | |
(Cmp32 (R[rn], opnd1) << 2); /* compare low mem */ | |
break; | |
/* Shift and convert instructions */ | |
case OP_S: /* shift */ | |
if ((tr = EaSh (IR, &stype, &sc)) != 0) /* get type, count */ | |
return tr; | |
if ((stype >= 0x6) && QCPU_S567) /* invalid, S5-7? */ | |
stype = 0x4; /* treat as arith */ | |
CC = (CC & ~(CC1|CC2|CC4)) | /* shift, set CC's */ | |
Shift (rn, stype, sc); | |
break; | |
case OP_SF: /* shift floating */ | |
if ((tr = EaSh (IR, &stype, &sc)) != 0) /* get type, count */ | |
return tr; | |
ShiftF (rn, stype & 1, sc); /* shift, set CC's */ | |
break; | |
case OP_CVA: /* cvt by addition */ | |
if (QCPU_S5) /* not on Sigma 5 */ | |
return TR_NXI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
CC &= ~CC1; /* clear CC1 (carry) */ | |
for (i = 0, res = 0; i < 32; i++) { /* 32 iterations */ | |
if ((R[rn|1] >> (31 - i)) & 1) { /* bit set? */ | |
uint32 ad = (bva + (i << 2)) & bvamqrx; /* table offset */ | |
if ((tr = ReadW (ad, &opnd, VR)) != 0) | |
return tr; /* read table word */ | |
res = (res + opnd) & WMASK; /* add into result */ | |
if (res < opnd) /* carry? set CC1 */ | |
CC |= CC1; | |
} /* end bit set */ | |
} /* end for */ | |
CC34_W (res); /* set CC's */ | |
R[rn] = res; /* store */ | |
break; | |
case OP_CVS: /* cvt by subtraction */ | |
if (QCPU_S5) /* not on Sigma 5 */ | |
return TR_NXI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
for (i = 0, res = R[rn], res1 = 0; i < 32; i++) { /* 32 iterations */ | |
uint32 ad = (bva + (i << 2)) & bvamqrx; /* table offset */ | |
if ((tr = ReadW (ad, &opnd, VR)) != 0) /* read table word */ | |
return tr; | |
if (opnd <= res) { /* residue >= entry? */ | |
res = (res - opnd) & WMASK; /* subtract entry */ | |
res1 |= 1u << (31 - i); /* set bit */ | |
} | |
} | |
CC34_W (res1); /* set CC's */ | |
R[rn] = res; /* store */ | |
R[rn|1] = res1; | |
break; | |
/* Push down instructions */ | |
case OP_PSW: /* push word */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VW)) != 0) /* read stack ptr */ | |
return tr; | |
if ((tr = TestSP1 (opnd1, 1)) != 0) /* will push work? */ | |
return ((tr & WSIGN)? 0: tr); | |
if ((tr = WriteW (((opnd + 1) << 2) & bvamqrx, R[rn], VW)) != 0) | |
return tr; /* push word */ | |
if ((tr = ModWrSP (bva, opnd, opnd1, 1)) != 0) /* mod, rewrite sp */ | |
return tr; | |
break; | |
case OP_PLW: /* pull word */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VW)) != 0) /* read stack ptr */ | |
return tr; | |
if ((tr = TestSP1 (opnd1, -1)) != 0) /* will pull work? */ | |
return ((tr & WSIGN)? 0: tr); | |
if ((tr = ReadW (opnd << 2, &res, VR)) != 0) /* pull word */ | |
return tr; | |
if ((tr = ModWrSP (bva, opnd, opnd1, -1)) != 0) /* mod, rewrite sp */ | |
return tr; | |
R[rn] = res; | |
break; | |
case OP_PSM: /* push multiple */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VW)) != 0) /* read stack ptr */ | |
return tr; | |
lim = CC? CC: 16; /* words to push */ | |
if ((tr = TestSP1 (opnd1, lim)) != 0) /* will push work? */ | |
return ((tr & WSIGN)? 0: tr); | |
if ((tr = ReadW (((opnd + lim) << 2) & bvamqrx, &res, VW)) != 0) | |
return tr; /* will last work? */ | |
for (i = 0; i < lim; i++) { /* loop thru reg */ | |
if ((tr = WriteW (((opnd + i + 1) << 2) & bvamqrx, R[rn], VW)) != 0) | |
return tr; /* push word */ | |
rn = (rn + 1) & RNMASK; | |
} | |
if ((tr = ModWrSP (bva, opnd, opnd1, lim)) != 0) /* mod, rewrite sp */ | |
return tr; | |
break; | |
case OP_PLM: /* pull multiple */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VW)) != 0) /* read stack ptr */ | |
return tr; | |
lim = CC? CC: 16; /* words to pull */ | |
if ((tr = TestSP1 (opnd1, -((int32)lim))) != 0) /* will pull work? */ | |
return ((tr & WSIGN)? 0: tr); | |
rn = (rn + lim - 1) & RNMASK; | |
if ((tr = ReadW (((opnd - (lim - 1)) << 2) & bvamqrx, &res, VR)) != 0) | |
return tr; /* will last work? */ | |
for (i = 0; i < lim; i++) { /* loop thru reg */ | |
if ((tr = ReadW (((opnd - i) << 2) & bvamqrx, &res, VR)) != 0) | |
return tr; /* pull word */ | |
R[rn] = res; | |
rn = (rn - 1) & RNMASK; | |
} | |
if ((tr = ModWrSP (bva, opnd, opnd1, -((int32) lim))) != 0) | |
return tr; | |
break; | |
case OP_MSP: /* modify stack ptr */ | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VW)) != 0) /* read stack ptr */ | |
return tr; | |
sop = SEXT_H_W (R[rn]); /* get modifier */ | |
if ((tr = TestSP1 (opnd1, sop)) != 0) /* will mod work? */ | |
return ((tr & WSIGN)? 0: tr); | |
if ((tr = ModWrSP (bva, opnd, opnd1, sop)) != 0) /* mod, rewrite sp */ | |
return tr; | |
break; | |
/* Control instructions */ | |
case OP_EXU: /* execute */ | |
if (exu_cnt++ > exu_lim) /* too many? */ | |
return STOP_EXULIM; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
IR = opnd; /* new instruction */ | |
goto EXU_LOOP; | |
case OP_BCS: /* branch cond set */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((CC & rn) != 0) { /* branch taken? */ | |
if ((tr = ReadW (bva, &opnd, VI)) != 0) /* new PC readable? */ | |
return tr; | |
PCQ_ENTRY; | |
PC = cpu_new_PC (bva); /* branch */ | |
} | |
break; | |
case OP_BCR: /* branch cond reset */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((CC & rn) == 0) { /* branch taken? */ | |
if ((tr = ReadW (bva, &opnd, VI)) != 0) /* new PC readable? */ | |
return tr; | |
PCQ_ENTRY; | |
PC = cpu_new_PC (bva); /* branch */ | |
} | |
break; | |
case OP_BIR: /* branch incr reg */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
res = (R[rn] + 1) & WMASK; /* ++R[rn] */ | |
if ((res & WSIGN) != 0) { /* < 0? */ | |
if ((tr = ReadW (bva, &opnd, VI)) != 0) /* new PC readable? */ | |
return tr; | |
PCQ_ENTRY; | |
PC = cpu_new_PC (bva); /* branch */ | |
} | |
R[rn] = res; /* actual increment */ | |
break; | |
case OP_BDR: /* branch decr reg */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
res = (R[rn] - 1) & WMASK; /* --R[rn] */ | |
if (((res & WSIGN) == 0) && (res != 0)) { /* > 0? */ | |
if ((tr = ReadW (bva, &opnd, VI)) != 0) /* new PC readable? */ | |
return tr; | |
PCQ_ENTRY; | |
PC = cpu_new_PC (bva); /* branch */ | |
} | |
R[rn] = res; /* actual decrement */ | |
break; | |
case OP_BAL: /* branch and link */ | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VI)) != 0) /* new PC readable? */ | |
return tr; | |
R[rn] = cpu_add_PC (real_pc, 1); /* save incr PC */ | |
PCQ_ENTRY; | |
PC = cpu_new_PC (bva); /* branch */ | |
break; | |
case OP_CAL1: /* CALL 1 */ | |
return TR_C1 (rn); | |
case OP_CAL2: /* CALL 2 */ | |
return TR_C2 (rn); | |
case OP_CAL3: /* CALL 3 */ | |
return TR_C3 (rn); | |
case OP_CAL4: /* CALL 4 */ | |
return TR_C4 (rn); | |
/* Privileged instructions */ | |
case OP_MMC: /* MMC */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if (TST_IND (IR) && /* indirect? */ | |
((tr = ReadW (I_GETADDR (IR) << 2, &opnd, VNT)) != 0)) | |
return tr; | |
return map_mmc (rn, I_GETXR (IR)); | |
case OP_LPSD: /* load PSD */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadD (bva, &opnd, &opnd1, VR)) != 0) /* read stack ptr */ | |
return tr; | |
if ((tr = cpu_new_PSD (IR & IRB (8), opnd, opnd1)) != 0) | |
return tr; | |
PCQ_ENTRY; /* no traps, upd PCQ */ | |
if (IR & IRB (10)) /* clr hi pri int? */ | |
int_hireq = io_rels_int (int_hiact, IR & IRB (11)); | |
else if (IR & IRB (11)) /* clr PDF flag? */ | |
cpu_pdf = 0; | |
break; | |
case OP_XPSD: /* exchange PSD */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = cpu_xpsd (IR & ~IRB (11), bva, VR)) != 0) /* do XPSD */ | |
return tr; | |
PCQ_ENTRY; /* no traps, upd PCQ */ | |
break; | |
case OP_LRP: | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = ReadW (bva, &opnd, VR)) != 0) /* read word */ | |
return tr; | |
return cpu_new_RP (opnd); /* update RP */ | |
case OP_RD: /* direct I/O */ | |
case OP_WD: | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_rwd (op, rn, bva)) != 0) /* do direct I/O */ | |
return tr; | |
int_hiact = io_actv_int (); /* re-eval active */ | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_WAIT: /* wait for int */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if (!io_poss_int ()) /* intr possible? */ | |
return STOP_WAITNOINT; /* machine is hung */ | |
// put idle here | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_AIO: /* acknowledge int */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_aio (rn, bva)) != 0) /* do AIO */ | |
return tr; | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_SIO: /* start IO */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_sio (rn, bva)) != 0) /* do SIO */ | |
return tr; | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_HIO: /* halt IO */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_hio (rn, bva)) != 0) /* do HIO */ | |
return tr; | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_TIO: /* test IO */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_tio (rn, bva)) != 0) /* do AIO */ | |
return tr; | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_TDV: /* test device */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = io_tdv (rn, bva)) != 0) /* do I/O */ | |
return tr; | |
int_hireq = io_eval_int (); /* re-eval intr */ | |
break; | |
case OP_LRA: /* load real addr */ | |
if (QCPU_S89_5X0) { /* late models only */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
return map_lra (rn, IR); /* in map */ | |
} | |
return (PSW1 & PSW1_MS)? TR_NXI|TR_PRV: TR_NXI; | |
case OP_LMS: /* load mem system */ | |
if ((cpu_unit.flags & CPUF_LAMS) == 0) /* implemented? */ | |
return (PSW1 & PSW1_MS)? TR_NXI|TR_PRV: TR_NXI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
if (QCPU_S567) /* old CPU? */ | |
R[rn] = IR; /* loads inst to IR */ | |
else if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
else return map_lms (rn, bva); /* in map */ | |
break; | |
case OP_PSS: /* push status */ | |
if (QCPU_5X0) { /* 5X0 only */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = cpu_pss (IR, bva, VR)) != 0) /* push status */ | |
return tr; | |
PCQ_ENTRY; | |
break; | |
} | |
return (PSW1 & PSW1_MS)? TR_NXI|TR_PRV: TR_NXI; | |
case OP_PLS: /* pull status */ | |
if (QCPU_5X0) { /* 5X0 only */ | |
if (PSW1 & PSW1_MS) /* slave mode? */ | |
return TR_PRV; | |
if ((tr = cpu_pls (IR)) != 0) /* pull status */ | |
return tr; | |
PCQ_ENTRY; | |
break; | |
} | |
return (PSW1 & PSW1_MS)? TR_NXI|TR_PRV: TR_NXI; | |
/* String instructions */ | |
case OP_MBS: /* move byte string */ | |
if ((cpu_unit.flags & CPUF_STR) == 0) /* not implemented? */ | |
return TR_UNI; | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sign extend */ | |
if ((cnt = S_GETMCNT (R[rn|1])) != 0) { /* any move? */ | |
sa = (opnd + (rn? R[rn] + cnt - 1: 0)) & bvamqrx; /* last src addr */ | |
da = (R[rn|1] + cnt - 1) & bvamqrx; /* last dst addr */ | |
if (((tr = ReadB (sa, &c, VR)) != 0) || /* test last bytes */ | |
((tr = ReadB (da, &c, VW)) != 0)) | |
return tr; | |
} | |
while (S_GETMCNT (R[rn|1])) { /* while count */ | |
sa = (opnd + (rn? R[rn]: 0)) & bvamqrx; /* src addr */ | |
da = R[rn|1] & bvamqrx; /* dst addr */ | |
if ((tr = ReadB (sa, &c, VR)) != 0) /* read src */ | |
return tr; | |
if ((tr = WriteB (da, c, VW)) != 0) /* write dst */ | |
return tr; | |
if (rn && !(rn & 1)) /* rn even, > 0? */ | |
R[rn] = (R[rn] + 1) & WMASK; /* inc saddr */ | |
R[rn|1] = (R[rn|1] + S_ADDRINC) & WMASK; /* inc daddr, dec cnt */ | |
} | |
break; | |
case OP_CBS: /* compare byte str */ | |
if ((cpu_unit.flags & CPUF_STR) == 0) /* not implemented? */ | |
return TR_UNI; | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sign extend */ | |
if ((cnt = S_GETMCNT (R[rn|1])) != 0) { /* any compare? */ | |
sa = (opnd + (rn? R[rn] + cnt - 1: 0)) & bvamqrx; /* last src addr */ | |
da = (R[rn|1] + cnt - 1) & bvamqrx; /* last dst addr */ | |
if (((tr = ReadB (sa, &c, VR)) != 0) || /* test last bytes */ | |
((tr = ReadB (da, &c, VR)) != 0)) | |
return tr; | |
} | |
CC = CC & ~(CC3|CC4); /* assume equal */ | |
while (S_GETMCNT (R[rn|1])) { /* while count */ | |
sa = (opnd + (rn? R[rn]: 0)) & bvamqrx; /* src addr */ | |
da = R[rn|1] & bvamqrx; /* dst addr */ | |
if ((tr = ReadB (sa, &c, VR)) != 0) /* read src */ | |
return tr; | |
if ((tr = ReadB (da, &c1, VR)) != 0) /* read dst */ | |
return tr; | |
if (c != c1) { /* not a match */ | |
CC |= ((c < c1)? CC4: CC3); | |
break; /* set CC's, done */ | |
} | |
if (rn && !(rn & 1)) /* rn even, > 0? */ | |
R[rn] = (R[rn] + 1) & WMASK; /* inc saddr */ | |
R[rn|1] = (R[rn|1] + S_ADDRINC) & WMASK; /* inc daddr, dec cnt */ | |
} | |
break; | |
case OP_TBS: /* xlate byte string */ | |
if ((cpu_unit.flags & CPUF_STR) == 0) /* not implemented? */ | |
return TR_UNI; | |
if (QCPU_S89_5X0 && (rn & 1)) /* invalid reg? */ | |
return TR_INVREG; | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sign extend */ | |
if ((cnt = S_GETMCNT (R[rn|1])) != 0) { /* any translate? */ | |
da = (R[rn] + cnt - 1) & bvamqrx; /* last dst addr */ | |
if ((tr = ReadB (da, &c, VW)) != 0) /* test last byte */ | |
return tr; | |
} | |
while (S_GETMCNT (R[rn|1])) { /* while count */ | |
sa = (opnd + (rn? R[rn]: 0)) & bvamqrx; /* src addr */ | |
da = R[rn|1] & bvamqrx; /* dst addr */ | |
if ((tr = ReadB (da, &c, VR)) != 0) /* read dst */ | |
return tr; | |
if ((tr = ReadB ((sa + c) & bvamqrx, &c1, VR)) != 0) | |
return tr; /* translate byte */ | |
if ((tr = WriteB (da, c1, VW)) != 0) /* write dst */ | |
return tr; | |
R[rn|1] = (R[rn|1] + S_ADDRINC) & WMASK; /* inc daddr, dec cnt */ | |
} | |
break; | |
case OP_TTBS: /* xlate, test string */ | |
if ((cpu_unit.flags & CPUF_STR) == 0) /* not implemented? */ | |
return TR_UNI; | |
if (QCPU_S89_5X0 && (rn & 1)) /* invalid reg? */ | |
return TR_INVREG; | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
opnd = SEXT_LIT_W (opnd) & WMASK; /* sign extend */ | |
mask = rn? S_GETMCNT (R[rn]): 0xFF; /* get mask */ | |
if ((cnt = S_GETMCNT (R[rn|1])) != 0) { /* any translate? */ | |
da = (R[rn] + cnt - 1) & bvamqrx; /* last dst addr */ | |
if ((tr = ReadB (da, &c, VR)) != 0) /* test last byte */ | |
return tr; | |
} | |
CC &= ~CC4; /* clear CC4 */ | |
while (S_GETMCNT (R[rn|1])) { /* while count */ | |
sa = (opnd + (rn? R[rn]: 0)) & bvamqrx; /* src addr */ | |
da = R[rn|1] & bvamqrx; /* dst addr */ | |
if ((tr = ReadB (da, &c, VR)) != 0) /* read dst */ | |
return tr; | |
if ((tr = ReadB ((sa + c) & bvamqrx, &c1, VR)) != 0) | |
return tr; /* translate byte */ | |
if ((t = c1 & mask) != 0) { /* byte & mask != 0? */ | |
if (rn) /* if !r0, repl mask */ | |
R[rn] = (R[rn] & ~S_MCNT) | (t << S_V_MCNT); | |
CC |= CC4; /* set CC4, stop */ | |
break; | |
} | |
R[rn|1] = (R[rn|1] + S_ADDRINC) & WMASK; /* inc daddr, dec cnt */ | |
} | |
break; | |
/* Optional floating point instructions */ | |
case OP_FAS: | |
case OP_FSS: | |
case OP_FMS: | |
case OP_FDS: /* short fp */ | |
if((cpu_unit.flags & CPUF_FP) == 0) /* option present? */ | |
return TR_UNI; | |
if ((tr = Ea (IR, &bva, VR, WD)) != 0) /* get eff addr */ | |
return tr; | |
return fp (op, rn, bva); /* go process */ | |
case OP_FAL: | |
case OP_FSL: | |
case OP_FML: | |
case OP_FDL: /* long fp */ | |
if (QCPU_S89_5X0 && (rn & 1)) /* invalid reg? */ | |
return TR_INVREG; | |
if((cpu_unit.flags & CPUF_FP) == 0) /* option present? */ | |
return TR_UNI; | |
if ((tr = Ea (IR, &bva, VR, DW)) != 0) /* get eff addr */ | |
return tr; | |
return fp (op, rn, bva); /* go process */ | |
/* Optional decimal instructions */ | |
case OP_DL: | |
case OP_DST: | |
case OP_DA: | |
case OP_DS: | |
case OP_DM: | |
case OP_DD: | |
case OP_DC: | |
case OP_DSA: | |
case OP_PACK: | |
case OP_UNPK: /* decimal */ | |
if((cpu_unit.flags & CPUF_DEC) == 0) /* option present? */ | |
return TR_UNI; | |
if ((tr = Ea (IR, &bva, VR, BY)) != 0) /* get eff addr */ | |
return tr; | |
if ((tr = cis_dec (op, rn, bva)) & WSIGN) /* process, abort? */ | |
return 0; | |
else return tr; | |
case OP_EBS: /* edit byte string */ | |
if ((tr = ImmOp (IR, &opnd)) != 0) /* get immed opnd */ | |
return tr; | |
if ((cpu_unit.flags & CPUF_DEC) == 0) /* option present? */ | |
return TR_UNI; | |
if (QCPU_S89_5X0 && ((rn == 0) || (rn & 1))) /* invalid reg? */ | |
return TR_INVREG; | |
if ((tr = cis_ebs (rn, opnd)) & WSIGN) /* process, abort? */ | |
return 0; | |
else return tr; | |
default: /* undefined inst */ | |
return (stop_op? STOP_ILLEG: TR_NXI); | |
} | |
return 0; | |
} | |
/* Execute MTx in an interrupt location | |
Sigma 5/6/7/8 - 17b virtual or real addressing | |
Sigma 9/5X0 - 17b virtual or 20b real addressing, no indexing | |
acc is either PH (physical) or VNT (no traps) | |
Memory map traps are suppressed, NXM's cause undefined behavior | |
(returns a nested trap fault) */ | |
uint32 cpu_int_mtx (uint32 vec, uint32 *res) | |
{ | |
uint32 IR, bva, wd, op, rn, lnt, acc; | |
ReadPW (vec, &IR); /* get instruction */ | |
op = I_GETOP (IR); /* get op */ | |
lnt = 3 - (op >> 5); /* 73, 53, 33 */ | |
acc = (vec == VEC_C4P)? VNT: PH; /* access */ | |
rn = I_GETRN (IR); /* register */ | |
if (hst_lnt) /* if history */ | |
inst_hist (IR, PC, H_ITRP); /* record inst */ | |
if ((acc || QCPU_S567)? /* virt or S5-7? */ | |
(Ea (IR, &bva, acc, lnt) != 0): /* get eff addr */ | |
(EaP20 (IR, &bva, lnt) != 0)) /* get real addr */ | |
return TR_NESTED; | |
switch (lnt) { | |
case BY: | |
if (ReadB (bva, &wd, acc) != 0) /* read byte */ | |
return TR_NESTED; | |
wd = (wd + SEXT_RN_W (rn)) & BMASK; /* modify */ | |
if (rn && (WriteB (bva, wd, acc) != 0)) /* if mod, rewrite */ | |
return TR_NESTED; | |
break; | |
case HW: | |
if (ReadH (bva, &wd, acc) != 0) /* read halfword */ | |
return TR_NESTED; | |
wd = (wd + SEXT_RN_W (rn)) & HMASK; /* modify */ | |
if (rn && (WriteB (bva, wd, acc) != 0)) /* if mod, rewrite */ | |
return TR_NESTED; | |
break; | |
case WD: | |
if (ReadW (bva, &wd, acc) != 0) /* read word */ | |
return TR_NESTED; | |
wd = (wd + SEXT_RN_W (rn)) & WMASK; /* modify */ | |
if (rn && (WriteW (bva, wd, acc) != 0)) /* if mod, rewrite */ | |
return TR_NESTED; | |
break; | |
} | |
*res = wd; | |
return 0; | |
} | |
/* Execute XSPD or PSS in trap or interrupt location */ | |
uint32 cpu_trap_or_int (uint32 vec) | |
{ | |
uint32 IR, op, bva, acc, cc; | |
ReadPW (TR_GETVEC (vec), &IR); /* read vector */ | |
op = I_GETOP (IR); /* get op */ | |
if (hst_lnt) { /* if history */ | |
if (vec & TR_FL) /* trap? */ | |
hst[hst_p].typ_cc_pc |= H_ABRT; /* mark prev abt */ | |
inst_hist (IR, PC, H_ITRP); /* record inst */ | |
} | |
if (vec & TR_FL) { /* trap? */ | |
if (QCPU_S89) /* Sigma 89? */ | |
PSW2 = (PSW2 & ~PSW2_TSF) | ((vec & PSW2_M_TSF) << PSW2_V_TSF); | |
if (vec == TR_INVRPN) /* non-trap reg ptr? */ | |
vec = TR_INVRPT; /* cvt to trapped */ | |
if (vec & TR_PDF) /* trap sets PDF? */ | |
cpu_pdf = 1; | |
} | |
if (op == OP_XPSD) { /* XPSD? */ | |
acc = (IR & IRB (10))? VNT: PH; /* virt vs phys */ | |
if ((acc || QCPU_S567)? /* virt or S5-7? */ | |
(Ea (IR, &bva, acc, DW) != 0): /* get eff addr */ | |
(EaP20 (IR, &bva, DW) != 0)) /* get real addr */ | |
return TR_NESTED; | |
if (cpu_xpsd (IR, bva, acc) != 0) /* do XPSD */ | |
return TR_NESTED; | |
if ((cc = TR_GETCC (vec)) != 0) { /* modify CC's? */ | |
CC = CC | cc; /* modify new CC's */ | |
if (IR & IRB (9)) /* and maybe new PC */ | |
PC = cpu_add_PC (PC, cc); | |
} | |
return 0; | |
} | |
if (QCPU_5X0 && (op == OP_PSS)) { /* 5X0 PSS? */ | |
if (EaP20 (IR, &bva, DW) != 0) /* get real addr */ | |
return TR_NESTED; | |
if (cpu_pss (IR, bva, PH)) /* do PSS */ | |
return TR_NESTED; | |
} | |
return TR_INVTRP; | |
} | |
/* Immediate operand */ | |
uint32 ImmOp (uint32 IR, uint32 *imm) | |
{ | |
if (TST_IND (IR)) /* indirect traps */ | |
return TR_NXI; | |
*imm = I_GETLIT (IR); | |
if (hst_lnt) /* record history */ | |
hst[hst_p].ea = hst[hst_p].op = *imm; | |
return 0; | |
} | |
/* Calculate effective address for normal instructions | |
Note that in the event of a failure reading the ind addr, | |
Ea must return that value in bva (for ANLZ) */ | |
uint32 Ea (uint32 IR, uint32 *bva, uint32 acc, uint32 lnt) | |
{ | |
uint32 ad, xr, wd; | |
uint32 tr; | |
xr = I_GETXR (IR); /* get index reg */ | |
ad = I_GETADDR (IR) << 2; /* get byte address */ | |
if (TST_IND (IR)) { /* indirect */ | |
if ((tr = ReadW (ad, &wd, acc)) != 0) { /* read ind word */ | |
*bva = ad; /* err? return addr */ | |
return tr; | |
} | |
if (PSW_QRX9 && (wd & WSIGN)) { /* S9 real ext special? */ | |
wd = wd & VAMASK; /* use only 17b */ | |
ad = (wd & PSW1_XA)? /* extended word? */ | |
(PSW2 & PSW2_EA) | (wd & ~PSW1_XA): wd; | |
ad = ad << 2; | |
} | |
else ad = (wd & bvamqrx) << 2; /* get byte address */ | |
} | |
*bva = (ad + (xr? (R[xr] << lnt): 0)) & bvamqrx; /* index, mask */ | |
if (hst_lnt) { /* history? */ | |
hst[hst_p].ea = *bva; | |
ReadHist (*bva, &hst[hst_p].op, &hst[hst_p].op1, acc? VNT: PH, lnt); | |
} | |
return 0; | |
} | |
/* Calculate effective address for 20b interrupt/trap instructions */ | |
uint32 EaP20 (uint32 IR, uint32 *bva, uint32 lnt) | |
{ | |
uint32 pa, wd; | |
pa = I_GETADDR20 (IR); /* get 20b ref addr */ | |
if (TST_IND (IR)) { /* indirect? */ | |
if (ReadPW (pa, &wd)) { /* valid? */ | |
*bva = pa << 2; | |
return TR_NXM; | |
} | |
pa = wd & cpu_tab[cpu_model].pamask; /* get indirect */ | |
} | |
*bva = pa << 2; | |
if (hst_lnt) { /* history? */ | |
hst[hst_p].ea = *bva; | |
ReadHist (*bva, &hst[hst_p].op, &hst[hst_p].op1, PH, lnt); | |
} | |
return 0; | |
} | |
/* Calculate effective address for shift */ | |
uint32 EaSh (uint32 IR, uint32 *stype, uint32 *sc) | |
{ | |
uint32 ad, xr, wd, tr; | |
xr = I_GETXR (IR); /* get index reg */ | |
ad = I_GETADDR (IR); /* get word addr */ | |
if (TST_IND (IR)) { /* indirect? */ | |
if ((tr = ReadW (ad << 2, &wd, VR)) != 0) /* read ind word */ | |
return tr; | |
ad = I_GETADDR (wd); /* get word addr */ | |
} | |
if (xr) | |
ad = (ad & ~SHF_M_SC) | ((ad + R[xr]) & SHF_M_SC); /* indexing? */ | |
*stype = SHF_GETSOP (ad); /* extract type */ | |
*sc = SHF_GETSC (ad); /* extract count */ | |
if (hst_lnt) { | |
hst[hst_p].ea = ad << 2; | |
hst[hst_p].op = ad; | |
} | |
return 0; | |
} | |
/* Shift routines */ | |
uint32 Shift (uint32 rn, uint32 stype, uint32 sc) | |
{ | |
uint32 i, opnd, opnd1, t, cc; | |
opnd = R[rn]; /* get operand(s) */ | |
opnd1 = R[rn|1]; | |
cc = CC & CC4; | |
if (sc & SCSIGN) { /* right? */ | |
sc = SHF_M_SC + 1 - sc; | |
switch (stype) { | |
case 0x0: /* right log sgl */ | |
if (sc > 31) /* >31? res = 0 */ | |
R[rn] = 0; | |
else R[rn] = R[rn] >> sc; | |
break; | |
case 0x1: /* right log dbl */ | |
if (sc > 63) /* >63? res = 0 */ | |
opnd = opnd1 = 0; | |
else if (sc > 31) { /* >31? */ | |
sc = sc - 32; | |
opnd1 = opnd >> sc; | |
opnd = 0; | |
} | |
else { | |
opnd1 = ((opnd1 >> sc) | (opnd << (32 - sc))) & WMASK; | |
opnd = opnd >> sc; | |
} | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
break; | |
case 0x2: /* right circ sgl */ | |
sc = sc % 32; /* mod 32 */ | |
R[rn] = ((R[rn] >> sc) | (R[rn] << (32 - sc))) & WMASK; | |
break; | |
case 0x3: /* right circ dbl */ | |
sc = sc % 64; /* mod 64 */ | |
t = opnd; | |
if (sc > 31) { /* >31? */ | |
sc = sc - 32; | |
opnd = ((opnd1 >> sc) | (opnd << (32 - sc))) & WMASK; | |
opnd1 = ((t >> sc) | (opnd1 << (32 - sc))) & WMASK; | |
} | |
else { | |
opnd = ((opnd >> sc) | (opnd1 << (32 - sc))) & WMASK; | |
opnd1 = ((opnd1 >> sc) | (t << (32 - sc))) & WMASK; | |
} | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
break; | |
case 0x4: /* right arith sgl */ | |
t = (R[rn] & WSIGN)? WMASK: 0; | |
if (sc > 31) /* >31? res = sign */ | |
R[rn] = t; | |
else R[rn] = ((R[rn] >> sc) | (t << (32 - sc))) & WMASK; | |
break; | |
case 0x5: /* right arith dbl */ | |
t = (R[rn] & WSIGN)? WMASK: 0; | |
if (sc > 63) /* >63? res = sign */ | |
opnd = opnd1 = t; | |
else if (sc > 31) { /* >31? */ | |
sc = sc - 32; | |
opnd1 = ((opnd >> sc) | (t << (32 - sc))) & WMASK; | |
opnd = t; | |
} | |
else { | |
opnd1 = ((opnd1 >> sc) | (opnd << (32 - sc))) & WMASK; | |
opnd = ((opnd >> sc) | (t << (32 - sc))) & WMASK; | |
} | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
break; | |
case 0x6: /* right search sgl */ | |
for (i = 0; (i < sc) && !(opnd & WSIGN); i++) { | |
opnd = ((opnd >> 1) | (opnd << 31)) & WMASK; | |
} | |
cc = (opnd & WSIGN)? (cc | CC4): (cc & ~CC4); | |
R[rn] = opnd; | |
R[1] = sc - i; | |
break; | |
case 0x7: /* right search dbl */ | |
for (i = 0; (i < sc) & !(opnd & WSIGN); i++) { | |
t = opnd; | |
opnd = ((opnd >> 1) | (opnd1 << 31)) & WMASK; | |
opnd1 = ((opnd1 >> 1) | (t << 31)) & WMASK; | |
} | |
cc = (opnd & WSIGN)? (cc | CC4): (cc & ~CC4); | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
R[1] = sc - i; | |
break; | |
} | |
} /* end if */ | |
else { /* left shift */ | |
switch (stype) { /* switch on type */ | |
case 0x0: /* left log sgl */ | |
case 0x4: /* left arith sgl */ | |
for (i = 0; i < sc; i++) { | |
if (opnd & WSIGN) /* count 1's */ | |
cc = cc ^ CC1; | |
opnd = (opnd << 1) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
R[rn] = opnd; | |
break; | |
case 0x1: /* left log dbl */ | |
case 0x5: /* left arith dbl */ | |
for (i = 0; i < sc; i++) { | |
if (opnd & WSIGN) /* count 1's */ | |
cc = cc ^ CC1; | |
opnd = ((opnd << 1) | (opnd1 >> 31)) & WMASK; | |
opnd1 = (opnd1 << 1) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
break; | |
case 0x2: /* left circ sgl */ | |
for (i = 0; i < sc; i++) { | |
if (opnd & WSIGN) /* count 1's */ | |
cc = cc ^ CC1; | |
opnd = ((opnd << 1) | (opnd >> 31)) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
R[rn] = opnd; | |
break; | |
case 0x3: /* left circ dbl */ | |
for (i = 0; i < sc; i++) { | |
if ((t = opnd & WSIGN) != 0) /* count 1's */ | |
cc = cc ^ CC1; | |
opnd = ((opnd << 1) | (opnd1 >> 31)) & WMASK; | |
opnd1 = ((opnd1 << 1) | (t >> 31)) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
break; | |
case 0x6: /* left search sgl */ | |
for (i = 0; (i < sc) & !(opnd & WSIGN); i++) { | |
opnd = ((opnd << 1) | (opnd >> 31)) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
cc = (opnd & WSIGN)? (cc | CC4): (cc & ~CC4); | |
R[rn] = opnd; | |
R[1] = sc - i; | |
break; | |
case 0x7: /* left search dbl */ | |
for (i = 0; (i < sc) & !(opnd & WSIGN); i++) { | |
t = opnd; | |
opnd = ((opnd << 1) | (opnd1 >> 31)) & WMASK; | |
opnd1 = ((opnd1 << 1) | (t >> 31)) & WMASK; | |
if ((opnd ^ R[rn]) & WSIGN) /* sign change? */ | |
cc |= CC2; | |
} | |
cc = (opnd & WSIGN)? (cc | CC4): (cc & ~CC4); | |
R[rn|1] = opnd1; | |
R[rn] = opnd; | |
R[1] = sc - i; | |
break; | |
} /* end switch */ | |
} /* end else */ | |
return cc; | |
} | |
/* Arithmetic routines */ | |
uint32 Add32 (uint32 s1, uint32 s2, uint32 cin) | |
{ | |
uint32 t = (s1 + s2 + cin) & WMASK; /* add + carry in */ | |
if (t & WSIGN) /* set CC34 */ | |
CC = CC4; | |
else if (t != 0) | |
CC = CC3; | |
else CC = 0; | |
if (cin? (t <= s1): (t < s1)) /* set carry out */ | |
CC |= CC1; | |
if (((s1 ^ ~s2) & (s1 ^ t)) & WSIGN) /* set overflow */ | |
CC |= CC2; | |
return t; | |
} | |
uint32 SMul64 (uint32 a, uint32 b, uint32 *lo) | |
{ | |
uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2, sign; | |
CC &= CC1; /* clr CC2-4 */ | |
if ((a == 0) || (b == 0)) { /* zero argument? */ | |
*lo = 0; /* result is zero */ | |
return 0; | |
} | |
sign = a ^ b; /* sign of result */ | |
if (a & WSIGN) /* |a|, |b| */ | |
a = NEG_W (a); | |
if (b & WSIGN) | |
b = NEG_W (b); | |
ah = (a >> 16) & HMASK; /* split operands */ | |
bh = (b >> 16) & HMASK; /* into 16b chunks */ | |
al = a & HMASK; | |
bl = b & HMASK; | |
rhi = ah * bh; /* high result */ | |
rmid1 = ah * bl; | |
rmid2 = al * bh; | |
rlo = al * bl; | |
rhi = rhi + ((rmid1 >> 16) & HMASK) + ((rmid2 >> 16) & HMASK); | |
rmid1 = (rlo + (rmid1 << 16)) & WMASK; /* add mid1 to lo */ | |
if (rmid1 < rlo) /* carry? incr hi */ | |
rhi = rhi + 1; | |
rmid2 = (rmid1 + (rmid2 << 16)) & WMASK; /* add mid2 to to */ | |
if (rmid2 < rmid1) /* carry? incr hi */ | |
rhi = rhi + 1; | |
rhi = rhi & WMASK; | |
if (sign & WSIGN) /* neg result? */ | |
NEG_D (rhi, rmid2); | |
if (rhi & WSIGN) /* < 0, set CC4 */ | |
CC |= CC4; | |
else if (rhi || rmid2) /* > 0, set CC3 */ | |
CC |= CC3; | |
if (rhi != ((rmid2 & WSIGN)? WMASK: 0)) /* fit in 32b? */ | |
CC |= CC2; /* set CC2 */ | |
*lo = rmid2; | |
return rhi; | |
} | |
t_bool SDiv64 (uint32 dvdh, uint32 dvdl, uint32 dvr, uint32 *res, uint32 *rem) | |
{ | |
uint32 i, quo, quos, rems; | |
quos = dvdh ^ dvr; | |
rems = dvdh; | |
if (dvdh & WSIGN) { /* |dividend| */ | |
NEG_D (dvdh, dvdl); | |
} | |
if (dvr & WSIGN) /* |divisor| */ | |
dvr = NEG_W (dvr); | |
if (dvdh >= dvr) /* divide work? */ | |
return TRUE; | |
for (i = quo = 0; i < 32; i++) { /* 32 iterations */ | |
quo = (quo << 1) & WMASK; /* shift quotient */ | |
dvdh = ((dvdh << 1) | (dvdl >> 31)) & WMASK; /* shift dividend */ | |
dvdl = (dvdl << 1) & WMASK; | |
if (dvdh >= dvr) { /* step work? */ | |
dvdh = (dvdh - dvr) & WMASK; /* subtract dvr */ | |
quo = quo + 1; | |
} | |
} | |
if (quo & WSIGN) /* quotient ovflo? */ | |
return TRUE; | |
*rem = (rems & WSIGN)? NEG_W (dvdh): dvdh; /* sign of rem */ | |
*res = (quos & WSIGN)? NEG_W (quo): quo; | |
return FALSE; /* no overflow */ | |
} | |
uint32 Cmp32 (uint32 a, uint32 b) | |
{ | |
if (a == b) /* ==? */ | |
return 0; | |
if ((a ^ b) & WSIGN) /* unlike signs? */ | |
return (a & WSIGN)? CC4: CC3; | |
return (a < b)? CC4: CC3; /* like signs */ | |
} | |
/* Test stack pointer space/words to see if it can be modified - | |
returns special abort status (WSIGN) */ | |
uint32 TestSP1 (uint32 sp1, int32 mod) | |
{ | |
int32 spc, wds; | |
uint32 cc; | |
cc = 0; | |
spc = (int32) SP_GETSPC (sp1); /* get space */ | |
wds = (int32) SP_GETWDS (sp1); /* get words */ | |
if (((wds + mod) > SP_M_WDS) || ((wds + mod) < 0)) { /* words overflow? */ | |
if ((sp1 & SP_TW) == 0) /* trap if enabled */ | |
return TR_PSH; | |
cc |= CC3; | |
} | |
if (((spc - mod) > SP_M_WDS) || ((spc - mod) < 0)) { /* space overflow? */ | |
if ((sp1 & SP_TS) == 0) /* trap if enabled */ | |
return TR_PSH; | |
cc |= CC1; | |
} | |
CC = cc; | |
if (cc || (mod == 0)) { /* mod fails? */ | |
CC |= ((spc? 0: CC2) | (wds? 0: CC4)); | |
return WSIGN; | |
} | |
return 0; | |
} | |
/* Actually modify stack pointer space/words and set CC's, | |
used by PSW/PLW/PSM/PLM */ | |
uint32 ModWrSP (uint32 bva, uint32 sp, uint32 sp1, int32 mod) | |
{ | |
uint32 tr; | |
sp = (sp + mod) & WMASK; | |
sp1 = (sp1 & (SP_TS|SP_TW)) | | |
(((SP_GETSPC (sp1) - mod) & SP_M_SPC) << SP_V_SPC) | | |
(((SP_GETWDS (sp1) + mod) & SP_M_WDS) << SP_V_WDS); | |
if ((tr = WriteD (bva, sp, sp1, VW)) != 0) | |
return tr; | |
if ((mod > 0) && SP_GETSPC (sp1) == 0) | |
CC |= CC2; | |
if ((mod < 0) && SP_GETWDS (sp1) == 0) | |
CC |= CC4; | |
return 0; | |
} | |
/* XPSD instruction */ | |
uint32 cpu_xpsd (uint32 IR, uint32 bva, uint32 ra) | |
{ | |
uint32 wa, wd, wd1, wd3; | |
uint32 tr; | |
if (ra == VR) /* virtual? */ | |
wa = VW; /* set write virt */ | |
else wa = ra; /* no, phys */ | |
cpu_assemble_PSD (); | |
wd = PSW1; /* no more changes */ | |
wd1 = PSW2; | |
wd3 = PSW4; | |
if ((tr = WriteD (bva, wd, wd1, wa)) != 0) /* write curr PSD */ | |
return tr; | |
bva = bva + 8; | |
if (QCPU_5X0 && (IR & IRB (11))) { /* extra words? */ | |
if ((tr = WriteW (bva | 4, wd3, VW)) != 0) | |
return tr; | |
bva = bva + 8; | |
} | |
if ((tr = ReadD (bva, &wd, &wd1, ra)) != 0) /* read new PSD */ | |
return tr; | |
wd1 = (wd1 & ~(cpu_tab[cpu_model].psw2_mbz)) | /* merge inhibits */ | |
(PSW2 & PSW2_ALLINH); | |
if ((tr = cpu_new_PSD (IR & IRB (8), wd, wd1)) != 0) /* update PSD */ | |
return tr; | |
return 0; | |
} | |
/* PSS instruction */ | |
uint32 cpu_pss (uint32 IR, uint32 bva, uint32 acc) | |
{ | |
uint32 i, wd, wd1, tos, swc; | |
uint32 tr; | |
cpu_assemble_PSD (); /* get old PSD */ | |
if ((tr = ReadD (bva, &wd, &wd1, acc)) != 0) /* read new PSD */ | |
return tr; | |
ReadPW (SSP_TOS, &tos); /* read system SP */ | |
ReadPW (SSP_SWC, &swc); | |
for (i = 0; i < RF_NUM; i++) { /* push registers */ | |
if (WritePW (tos + SSP_FR_RN + i + 1, R[i])) | |
return TR_NXM; | |
} | |
if (WritePW (tos + SSP_FR_PSW1 + 1, PSW1) || /* push PSD */ | |
WritePW (tos + SSP_FR_PSW2 + 1, PSW2)) | |
return TR_NXM; | |
WritePW (SSP_TOS, (tos + SSP_FR_LNT) & WMASK); /* tos + 28 */ | |
swc = (swc & (SP_TS|SP_TW)) | /* spc-28, wds+28 */ | |
(((SP_GETWDS (swc) + SSP_FR_LNT) & SP_M_WDS) << SP_V_WDS) | | |
(((SP_GETSPC (swc) - SSP_FR_LNT) & SP_M_SPC) << SP_V_SPC); | |
if (SP_GETWDS (swc) < SSP_FR_LNT) /* wds overflow? */ | |
swc |= SP_TW; /* set sticky bit */ | |
WritePW (SSP_SWC, swc); | |
wd1 = (wd1 & ~(cpu_tab[cpu_model].psw2_mbz)) | /* merge inhibits */ | |
(PSW2 & PSW2_ALLINH); | |
if ((tr = cpu_new_PSD (IR & IRB (8), wd, wd1)) != 0) /* update PSD */ | |
return tr; | |
return 0; | |
} | |
/* PLS instruction */ | |
uint32 cpu_pls (uint32 IR) | |
{ | |
uint32 i, wd, wd1, tos, swc, spc; | |
uint32 tr; | |
ReadPW (SSP_TOS, &tos); /* read system SP */ | |
ReadPW (SSP_SWC, &swc); | |
spc = SP_GETSPC (swc); /* space left */ | |
if (spc == 0) { /* none? */ | |
ReadPW (SSP_DFLT_PSW1, &wd); /* use default PSD */ | |
ReadPW (SSP_DFLT_PSW2, &wd1); | |
} | |
else if (spc < SSP_FR_LNT) /* not enough? */ | |
return TR_INVSSP; | |
else { | |
tos = (tos - SSP_FR_LNT) & WMASK; /* modify TOS */ | |
for (i = 0; i < RF_NUM; i++) { /* pull registers */ | |
if (ReadPW (tos + SSP_FR_RN + i + 1, &wd)) | |
return TR_NXM; | |
R[i] = wd; | |
} | |
if (ReadPW (tos + SSP_FR_PSW1 + 1, &wd) || /* pull new PSD */ | |
ReadPW (tos + SSP_FR_PSW2 + 1, &wd1)) | |
return TR_NXM; | |
WritePW (SSP_TOS, tos); /* rewrite SP */ | |
swc = (swc & (SP_TS|SP_TW)) | /* spc+28, wds-28 */ | |
(((SP_GETWDS (swc) - SSP_FR_LNT) & SP_M_WDS) << SP_V_WDS) | | |
(((SP_GETSPC (swc) + SSP_FR_LNT) & SP_M_SPC) << SP_V_SPC); | |
if (SP_GETSPC (swc) < SSP_FR_LNT) /* spc overflow? */ | |
swc |= SP_TS; /* set sticky bit */ | |
WritePW (SSP_SWC, swc); | |
} | |
wd1 = (wd1 & ~(cpu_tab[cpu_model].psw2_mbz)) | /* merge inhibits */ | |
(PSW2 & PSW2_ALLINH); | |
if ((tr = cpu_new_PSD (IR & IRB (8), wd, wd1)) != 0) /* update PSD */ | |
return tr; | |
if (IR & IRB (10)) /* clr hi pri int? */ | |
int_hireq = io_rels_int (int_hiact, IR & IRB (11)); | |
else if (IR & IRB (11)) /* clr PDF flag? */ | |
cpu_pdf = 0; | |
return 0; | |
} | |
/* Load new PSD */ | |
uint32 cpu_new_PSD (uint32 lrp, uint32 p1, uint32 p2) | |
{ | |
uint32 tr; | |
PSW1 = p1 & ~cpu_tab[cpu_model].psw1_mbz; /* clear mbz bits */ | |
PSW2 = ((p2 & ~PSW2_RP) | (PSW2 & PSW2_RP)) & /* save reg ptr */ | |
~cpu_tab[cpu_model].psw2_mbz; | |
if (lrp && /* load reg ptr? */ | |
((tr = cpu_new_RP (p2)) != 0)) /* invalid? */ | |
return tr; /* trap */ | |
CC = PSW1_GETCC (PSW1); /* extract CC's */ | |
PC = PSW1_GETPC (PSW1); /* extract PC */ | |
PSW2_WLK = PSW2_GETWLK (PSW2); /* extract lock */ | |
int_hireq = io_eval_int (); /* update intr */ | |
if ((PSW1 & PSW1_MM) || /* mapped or */ | |
((PSW2 & (PSW2_MA9|PSW2_MA5X0)) == 0)) { /* not real ext? */ | |
bvamqrx = BVAMASK; /* 17b masks */ | |
PSW_QRX9 = 0; | |
} | |
else { /* phys real ext */ | |
if ((PSW_QRX9 = PSW2 & PSW2_MA9) != 0) /* Sigma 9? */ | |
bvamqrx = BPAMASK22; /* yes, 22b masks */ | |
else bvamqrx = BPAMASK20; /* no, 20b masks */ | |
} | |
return 0; | |
} | |
/* Load new RP */ | |
uint32 cpu_new_RP (uint32 rp) | |
{ | |
uint32 rp1, j; | |
PSW2 = (PSW2 & ~PSW2_RP) | (rp & PSW2_RP); /* merge to PSW2 */ | |
PSW2 = PSW2 & ~cpu_tab[cpu_model].psw2_mbz; /* clear nx bits */ | |
rp1 = PSW2_GETRP (rp); | |
if (rp1 >= rf_bmax) { /* nx reg file? */ | |
if (QCPU_S89) | |
return TR_INVRPN; | |
if (QCPU_5X0) | |
return TR_INVREG; | |
for (j = 0; j < RF_NUM; j++) /* clear nx set */ | |
rf[(rp1 * RF_NUM) + j] = 0; | |
sim_activate (&cpu_rblk_unit, 1); /* sched cleaner */ | |
} | |
R = rf + (rp1 * RF_NUM); | |
return 0; | |
} | |
/* This routine is scheduled if the current registr block doesn't exist */ | |
t_stat cpu_bad_rblk (UNIT *uptr) | |
{ | |
uint32 rp1, j; | |
rp1 = PSW2_GETRP (PSW2); /* get reg ptr */ | |
if (rp1 >= rf_bmax) { /* still bad? */ | |
for (j = 0; j < RF_NUM; j++) /* clear nx set */ | |
rf[(rp1 * RF_NUM) + j] = 0; | |
sim_activate (uptr, 1); /* sched again */ | |
} | |
return SCPE_OK; | |
} | |
/* Load new PC for branch instruction */ | |
uint32 cpu_new_PC (uint32 bva) | |
{ | |
uint32 npc = bva >> 2; | |
if (PSW_QRX9 && (npc & PSW1_XA)) /* S9 real ext, XA? */ | |
PSW2 = (PSW2 & ~PSW2_EA) | (npc & PSW2_EA); /* change PSW2 EA */ | |
return npc & BVAMASK; | |
} | |
/* Add value to PC for fetch, BAL, trap */ | |
uint32 cpu_add_PC (uint32 pc, uint32 inc) | |
{ | |
if (PSW_QRX9) /* S9 real ext? */ | |
return ((pc & ~(PSW1_M_PC & ~PSW1_XA)) | /* modulo 16b inc */ | |
((pc + inc) & (PSW1_M_PC & ~PSW1_XA))); | |
return ((pc + inc) & BVAMASK); /* no, mod 17b inc */ | |
} | |
/* Assemble PSD */ | |
void cpu_assemble_PSD (void) | |
{ | |
PSW1 = (PSW1 & ~(PSW1_CCMASK|PSW1_PCMASK|cpu_tab[cpu_model].psw1_mbz)) | | |
(CC << PSW1_V_CC) | (PC << PSW1_V_PC); | |
PSW2 = (PSW2 & ~(PSW2_WLKMASK|cpu_tab[cpu_model].psw2_mbz)) | | |
(PSW2_WLK << PSW2_V_WLK); | |
return; | |
} | |
/* Reset routine */ | |
t_stat cpu_reset (DEVICE *dptr) | |
{ | |
cpu_new_PSD (1, PSW1_DFLT | (PSW1 & PSW1_PCMASK), PSW2_DFLT); | |
cpu_pdf = 0; | |
cons_alarm = 0; | |
cons_pcf = 0; | |
set_rf_display (R); | |
if (M == NULL) | |
M = (uint32 *) calloc (MAXMEMSIZE, sizeof (uint32)); | |
if (M == NULL) | |
return SCPE_MEM; | |
pcq_r = find_reg ("PCQ", NULL, dptr); | |
if (pcq_r) pcq_r->qptr = 0; | |
else return SCPE_IERR; | |
sim_brk_types = sim_brk_dflt = SWMASK ('E'); | |
rtc_register (RTC_ALARM, RTC_HZ_2, &cpu_unit); | |
return int_reset (dptr); | |
} | |
/* Memory examine */ | |
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) | |
{ | |
uint32 lnt; | |
if (sw & SWMASK ('C')) | |
lnt = 2; | |
else if ((sw & SWMASK ('B')) || (sw & SWMASK ('A')) || (sw & SWMASK ('E'))) | |
lnt = 0; | |
else if (sw & SWMASK ('H')) | |
lnt = 1; | |
else lnt = 2; | |
if (sw & SWMASK ('V')) { | |
if (ReadW (addr << lnt, vptr, VNT) != 0) | |
return SCPE_REL; | |
} | |
else if (ReadW (addr << lnt, vptr, PH) != 0) | |
return SCPE_NXM; | |
return SCPE_OK; | |
} | |
/* Memory deposit */ | |
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) | |
{ | |
uint32 lnt; | |
if (sw & SWMASK ('C')) | |
lnt = 2; | |
else if ((sw & SWMASK ('B')) || (sw & SWMASK ('A')) || (sw & SWMASK ('E'))) | |
lnt = 0; | |
else if (sw & SWMASK ('H')) | |
lnt = 1; | |
else lnt = 2; | |
if (sw & SWMASK ('V')) { | |
if (WriteW (addr << lnt, val, VNT) != 0) | |
return SCPE_REL; | |
} | |
else if (WriteW (addr << lnt, val, PH) != 0) | |
return SCPE_NXM; | |
return SCPE_OK; | |
} | |
/* CPU configuration management | |
These routines (for type, memory size, options, number of reg blocks, | |
number of external int blocks) must generate a consistent result. | |
To assure this, all changes (except memory size) reset the CPU. */ | |
/* Set CPU type */ | |
t_stat cpu_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
uint32 model = CPUF_GETMOD (val); | |
if (model == cpu_model) /* no change? */ | |
return SCPE_OK; | |
cpu_reset (&cpu_dev); | |
if (MEMSIZE > (cpu_tab[cpu_model].pamask + 1)) | |
cpu_set_size (uptr, cpu_tab[cpu_model].pamask + 1, NULL, (void *) uptr); | |
cpu_model = model; | |
cpu_unit.flags = (cpu_unit.flags | cpu_tab[model].std) & ~cpu_tab[model].opt; | |
rf_bmax = RF_DFLT; | |
io_set_eimax (EIGRP_DFLT); | |
return SCPE_OK; | |
} | |
/* Set memory size */ | |
t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
uint32 mc = 0; | |
uint32 i; | |
if ((val <= 0) || (val > (int32)(cpu_tab[cpu_model].pamask + 1))) | |
return SCPE_ARG; | |
if (!desc) { /* force trunc? */ | |
for (i = val; i < MEMSIZE; i++) | |
mc = 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; | |
} | |
/* Set and clear options */ | |
t_stat cpu_set_opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
if ((val & (cpu_tab[cpu_model].std | cpu_tab[cpu_model].opt)) == 0) | |
return SCPE_NOFNC; | |
cpu_unit.flags |= val; | |
return SCPE_OK; | |
} | |
t_stat cpu_clr_opt (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
if (val & cpu_tab[cpu_model].std) | |
return SCPE_NOFNC; | |
cpu_unit.flags &= ~val; | |
return SCPE_OK; | |
} | |
/* Set/show register blocks */ | |
t_stat cpu_set_rblks (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int32 invmask, lnt, i, j; | |
t_stat r; | |
if (QCPU_5X0) /* 5X0 fixed */ | |
return SCPE_NOFNC; | |
if (cptr == NULL) | |
return SCPE_ARG; | |
invmask = PSW2_GETRP (cpu_tab[cpu_model].psw2_mbz); | |
if (QCPU_S89) | |
invmask |= 0x10; | |
lnt = (int32) get_uint (cptr, 10, RF_NBLK, &r); | |
if ((r != SCPE_OK) || (lnt == 0) || (lnt & invmask)) | |
return SCPE_ARG; | |
cpu_reset (&cpu_dev); | |
rf_bmax = lnt; | |
for (i = rf_bmax; i < RF_NBLK; i++) { /* zero unused */ | |
for (j = 0; j < RF_NUM; j++) | |
rf[(i * RF_NUM) + j] = 0; | |
} | |
return SCPE_OK; | |
} | |
t_stat cpu_show_rblks (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
fprintf (st, "register blocks=%d", rf_bmax); | |
return SCPE_OK; | |
} | |
/* Set current register file pointers for SCP */ | |
void set_rf_display (uint32 *rfbase) | |
{ | |
REG *rptr; | |
uint32 i; | |
rptr = find_reg ("R0", NULL, &cpu_dev); | |
if (rptr == NULL) return; | |
for (i = 0; i < RF_NUM; i++, rptr++) | |
rptr->loc = (void *) (rfbase + i); | |
return; | |
} | |
/* Front panael alarm */ | |
t_stat cpu_set_alarm (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
cons_alarm_enb = val; | |
return SCPE_OK; | |
} | |
t_stat cpu_show_alarm (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
fputs (cons_alarm_enb? "alarm enabled\n": "alarm disabled\n", st); | |
return SCPE_OK; | |
} | |
t_stat cpu_svc (UNIT *uptr) | |
{ | |
if (cons_alarm && cons_alarm_enb) | |
sim_putchar ('\a'); | |
return SCPE_OK; | |
} | |
/* Address converter and display */ | |
/* Virtual address translation */ | |
t_stat cpu_show_addr (FILE *of, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
t_stat r; | |
const char *cptr = (const char *) desc; | |
uint32 ad, bpa, dlnt, virt; | |
static const char *lnt_str[] = { | |
"byte", | |
"halfword", | |
"word", | |
"doubleword", | |
}; | |
extern uint32 map_reloc (uint32 bva, uint32 acc, uint32 *bpa); | |
if ((val < 0) || (val > DW)) | |
return SCPE_IERR; | |
virt = (sim_switches & SWMASK ('V'))? 1: 0; | |
if (cptr) { | |
ad = (uint32) get_uint (cptr, 16, virt? VAMASK: PAMASK22, &r); | |
if (r == SCPE_OK) { | |
if (sim_switches & SWMASK ('B')) | |
dlnt = 0; | |
else if (sim_switches & SWMASK ('H')) | |
dlnt = 1; | |
else if (sim_switches & SWMASK ('D')) | |
dlnt = 3; | |
else dlnt = 2; | |
bpa = ad << val; | |
if (virt && map_reloc (bpa, VNT, &bpa)) | |
fprintf (of, "Virtual address %-X: memory management error\n", ad); | |
else fprintf (of, "%s %s %-X: physical %s %-X\n", | |
((virt)? "Virtual": "Physical"), lnt_str[val], ad, lnt_str[dlnt], bpa >> dlnt); | |
return SCPE_OK; | |
} | |
} | |
fprintf (of, "Invalid argument\n"); | |
return SCPE_OK; | |
} | |
/* Record history */ | |
void inst_hist (uint32 ir, uint32 pc, uint32 tp) | |
{ | |
uint32 rn = I_GETRN (ir); | |
hst_p = (hst_p + 1); /* next entry */ | |
if (hst_p >= hst_lnt) | |
hst_p = 0; | |
hst[hst_p].typ_cc_pc = (CC << PSW1_V_CC) | pc | tp; | |
hst[hst_p].ir = ir; | |
hst[hst_p].rn = R[rn]; | |
hst[hst_p].rn1 = R[rn|1]; | |
return; | |
} | |
/* Set history */ | |
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int32 i, lnt; | |
t_stat r; | |
if (cptr == NULL) { | |
for (i = 0; i < hst_lnt; i++) | |
hst[i].typ_cc_pc = 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 = (InstHistory *) calloc (lnt, sizeof (InstHistory)); | |
if (hst == NULL) | |
return SCPE_MEM; | |
hst_lnt = lnt; | |
} | |
return SCPE_OK; | |
} | |
/* Print one instruction */ | |
void cpu_fprint_one_inst (FILE *st, uint32 tcp, uint32 ir, uint32 rn, uint32 rn1, | |
uint32 ea, uint32 opnd, uint32 opnd1) | |
{ | |
t_value sim_val; | |
if (tcp & (H_INST|H_ITRP)) { /* instr or trap? */ | |
uint32 op = I_GETOP (ir); | |
uint32 cc = PSW1_GETCC (tcp); | |
uint32 pc = tcp & PAMASK20; | |
uint8 fl = anlz_tab[op]; | |
fprintf (st, "%c %05X %X %08X %08X ", /* standard fields */ | |
((tcp & H_INST)? ' ': 'T'), pc, cc, rn, rn1); | |
if (tcp & H_ABRT) /* aborted? */ | |
fputs ("aborted ", st); | |
else { | |
if (fl & CC4) /* immediate? */ | |
fprintf (st, "%05X ", ea); | |
else if ((fl >> 2) != DW) /* byte/half/word? */ | |
fprintf (st, "%05X %08X ", ea >> 2, opnd); | |
else fprintf (st, "%05X %08X %08X ", ea >> 2, opnd, opnd1); | |
} | |
sim_val = ir; | |
if ((fprint_sym (st, pc, &sim_val, NULL, SWMASK ('M'))) > 0) | |
fprintf (st, "(undefined) %08X", ir); | |
fputc ('\n', st); /* end line */ | |
} | |
return; | |
} | |
/* Show history */ | |
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
int32 k, di, lnt; | |
t_stat r; | |
const char *cptr = (const char *) desc; | |
InstHistory *h; | |
if (hst_lnt == 0) /* enabled? */ | |
return SCPE_NOFNC; | |
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, " PC CC Rn Rn|1 EA operand operand1 IR\n\n"); | |
for (k = 0; k < lnt; k++) { /* print specified */ | |
h = &hst[(++di) % hst_lnt]; /* entry pointer */ | |
if (h->typ_cc_pc) /* instruction? */ | |
cpu_fprint_one_inst (st, h->typ_cc_pc, h->ir, h->rn, h->rn1, h->ea, h->op, h->op1); | |
} /* end for */ | |
return SCPE_OK; | |
} |