mirror of
https://github.com/rcornwell/sims.git
synced 2026-01-31 13:52:50 +00:00
5592 lines
196 KiB
C
5592 lines
196 KiB
C
/* ibm360_cpu.c: ibm 360 cpu simulator.
|
||
|
||
Copyright (c) 2019-2021, Richard Cornwell
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a
|
||
copy of this software and associated documentation files (the "Software"),
|
||
to deal in the Software without restriction, including without limitation
|
||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
and/or sell copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in
|
||
all copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
Introduced by IBM on April 7th, 1964. The IBM360 was designed to be a
|
||
replacement for the IBM7000 series machines, offering on common instruction
|
||
set to be used over a wide range of system configurations.
|
||
|
||
The IBM 360 supported 32 bit memory and 16 32 bit registers. Optionally
|
||
it could have 4 64 bit floating point registers. Optionally the machine
|
||
could also process packed decimal numbers directly. There was also a
|
||
64 bit processor status word. Up to 16MB of memory could be supported,
|
||
however 1MB or less was much more common. 8MB being the practical max.
|
||
|
||
Instructions ranged from 2 bytes to up to 6 bytes. In the following formats:
|
||
Address are a 12 bit offset and one or two index registers. Index register
|
||
0 results in a zero value.
|
||
|
||
RR format: (Register to Register).
|
||
|
||
+----+----+----+----+
|
||
| op | R1 | R2 |
|
||
+----+----+----+----+
|
||
* R1 could be register or 4 bit mask.
|
||
|
||
RX format: (Memory to Register).
|
||
+----+----+----+----+----+----+----+----+
|
||
| op | R1 | B2 | D2 | Offset2 |
|
||
+----+----+----+----+----+----+----+----+
|
||
|
||
RS format: (Memory to Register).
|
||
+----+----+----+----+----+----+----+----+
|
||
| op | R1 | R3 | D2 | Offset2 |
|
||
+----+----+----+----+----+----+----+----+
|
||
* R3 could be register or 4 bit mask.
|
||
|
||
SI format: (Immediate to Memory).
|
||
+----+----+----+----+----+----+----+----+
|
||
| op | Immed | D1 | Offset1 |
|
||
+----+----+----+----+----+----+----+----+
|
||
|
||
SS format: (Memory to Memory).
|
||
+----+----+----+----+----+----+----+----+----+----+----+----+
|
||
| op | Length | D1 | Offset1 | D2 | Offset2 |
|
||
+----+----+----+----+----+----+----+----+----+----+----+----+
|
||
* Length could be either 1 byte or to 4 bit lengths.
|
||
|
||
The IBM360/67 was introduced in August 1965, this added Dynamic
|
||
Address Translation to the IBM360 for support of CP/67 and TSS/360.
|
||
This also added in 16 32 bit control registers.
|
||
|
||
The IBM370 was the successor to the IBM360 which was introduced on
|
||
June 30th, 1970. This added in some new instructions, DAT and some
|
||
other options.
|
||
|
||
*/
|
||
|
||
#include "ibm360_defs.h" /* simulator defns */
|
||
|
||
#define MEMAMOUNT(x) (x)
|
||
#define TMR_RTC 0
|
||
|
||
#define HIST_MAX 5000000
|
||
#define HIST_MIN 64
|
||
#define HIST_PC 0x1000000
|
||
#define HIST_SPW 0x2000000
|
||
#define HIST_LPW 0x4000000
|
||
|
||
uint32 *M = NULL;
|
||
uint8 key[MAXMEMSIZE/2048];
|
||
uint32 regs[16]; /* CPU Registers */
|
||
uint32 PC; /* Program counter */
|
||
uint32 iPC; /* Initial PC for instruction */
|
||
t_uint64 fpregs[8]; /* Floating point registers */
|
||
uint32 cregs[16]; /* Control registers /67 or 370 only */
|
||
uint16 sysmsk; /* Interupt mask */
|
||
uint8 ext_en; /* Enable external and timer IRQ's */
|
||
uint8 irq_en; /* Enable channel IRQ's */
|
||
uint8 tod_en; /* Enable TOD compare irq's */
|
||
uint8 intval_en; /* Enable interval irq's */
|
||
uint8 st_key; /* Storage key */
|
||
uint8 ec_mode; /* EC mode PSW */
|
||
uint8 cc; /* CC */
|
||
uint8 ilc; /* Instruction length code */
|
||
uint8 pmsk; /* Program mask */
|
||
uint16 irqcode; /* Interupt code */
|
||
uint8 flags; /* Misc flags */
|
||
uint16 irqaddr; /* Address of IRQ vector */
|
||
uint16 loading; /* Doing IPL */
|
||
uint8 interval_irq = 0; /* Interval timer IRQ */
|
||
uint8 dat_en = 0; /* Translate addresses */
|
||
uint32 tlb[256]; /* Translation look aside buffer */
|
||
int page_shift; /* Amount to shift for page */
|
||
int page_index; /* Mask of page index feild */
|
||
int page_mask; /* Mask of bits in page address */
|
||
int seg_shift; /* Amount to shift for segment */
|
||
int seg_mask; /* Mask bits for segment */
|
||
uint32 seg_len; /* Length of segment table */
|
||
int pte_len_shift; /* Shift to Check if out out page table */
|
||
int seg_addr; /* Address of segment table */
|
||
int pte_avail; /* Mask of available bit in PTE */
|
||
int pte_mbz; /* Bits that must be zero in PTE */
|
||
int pte_shift; /* Bits to shift a PTE entry */
|
||
int per_en; /* Enable PER tracing */
|
||
uint32 per_mod; /* Module modification mask */
|
||
int per_code; /* Code for PER */
|
||
uint32 per_addr; /* Address of last reference */
|
||
uint32 tod_clock[2]; /* Current Time of Day Clock */
|
||
uint32 clk_cmp[2]; /* Clock compare value */
|
||
uint32 cpu_timer[2]; /* CPU timer value */
|
||
uint8 clk_irq; /* Clock compare IRQ */
|
||
uint8 tod_irq; /* TOD compare IRQ */
|
||
int clk_state;
|
||
int timer_tics; /* Interval Timer is ever 3 tics */
|
||
uint32 idle_stop_msec = 0; // msec allowed after before stop cpu if idle
|
||
uint32 idle_stop_tm0; // sim_os_msec when start counting for idle time
|
||
|
||
|
||
#define CLOCK_UNSET 0 /* Clock not set */
|
||
#define CLOCK_SET 1 /* Clock set */
|
||
|
||
|
||
#define DAT_ENABLE 0x01 /* DAT enabled */
|
||
|
||
|
||
#define ASCII 0x08 /* ASCII/EBCDIC mode */
|
||
#define MCHECK 0x04 /* Machine check flag */
|
||
#define WAIT 0x02
|
||
#define PROBLEM 0x01 /* Problem state */
|
||
|
||
#define FIXOVR 0x08 /* Fixed point overflow */
|
||
#define DECOVR 0x04 /* Decimal overflow */
|
||
#define EXPUND 0x02 /* Exponent overflow */
|
||
#define SIGMSK 0x01 /* Significance */
|
||
|
||
|
||
/* low addresses */
|
||
#define IPSW 0x00 /* IPSW */
|
||
#define ICCW1 0x08 /* ICCW1 */
|
||
#define ICCW2 0x10 /* ICCW2 */
|
||
#define OEPSW 0x18 /* External old PSW */
|
||
#define OSPSW 0x20 /* Supervisior call old PSW */
|
||
#define OPPSW 0x28 /* Program old PSW */
|
||
#define OMPSW 0x30 /* Machine check PSW */
|
||
#define OIOPSW 0x38 /* IO old PSW */
|
||
#define CSW 0x40 /* CSW */
|
||
#define CAW 0x48 /* CAW */
|
||
#define TIMER 0x50 /* timer */
|
||
#define NEPSW 0x58 /* External new PSW */
|
||
#define NSPSW 0x60 /* SVC new PSW */
|
||
#define NPPSW 0x68 /* Program new PSW */
|
||
#define NMPSW 0x70 /* Machine Check PSW */
|
||
#define NIOPSW 0x78 /* IOPSW */
|
||
#define DIAGAREA 0x80 /* Diag scan area. */
|
||
|
||
|
||
#define IRC_OPR 0x0001 /* Operations exception */
|
||
#define IRC_PRIV 0x0002 /* Privlege violation */
|
||
#define IRC_EXEC 0x0003 /* Execution */
|
||
#define IRC_PROT 0x0004 /* Protection violation */
|
||
#define IRC_ADDR 0x0005 /* Address error */
|
||
#define IRC_SPEC 0x0006 /* Specification error */
|
||
#define IRC_DATA 0x0007 /* Data exception */
|
||
#define IRC_FIXOVR 0x0008 /* Fixed point overflow */
|
||
#define IRC_FIXDIV 0x0009 /* Fixed point divide */
|
||
#define IRC_DECOVR 0x000a /* Decimal overflow */
|
||
#define IRC_DECDIV 0x000b /* Decimal divide */
|
||
#define IRC_EXPOVR 0x000c /* Exponent overflow */
|
||
#define IRC_EXPUND 0x000d /* Exponent underflow */
|
||
#define IRC_SIGNIF 0x000e /* Significance error */
|
||
#define IRC_FPDIV 0x000f /* Floating pointer divide */
|
||
#define IRC_SEG 0x0010 /* Segment translation */
|
||
#define IRC_PAGE 0x0011 /* Page translation */
|
||
#define IRC_TRANS 0x0012 /* Translation special */
|
||
#define IRC_SPOP 0x0013 /* Special operation */
|
||
#define IRC_MCE 0x0040 /* Monitor event */
|
||
#define IRC_PER 0x0080 /* Per event */
|
||
|
||
#define AMASK 0x00ffffff /* Mask address bits */
|
||
#define MSIGN 0x80000000 /* Minus sign */
|
||
#define MMASK 0x00ffffff /* Mantissa mask */
|
||
#define EMASK 0x7f000000 /* Exponent mask */
|
||
#define XMASK 0x0fffffff /* Working FP mask */
|
||
#define HMASK 0x7fffffff /* Working FP mask */
|
||
#define FMASK 0xffffffff /* Working FP mask */
|
||
#define CMASK 0x10000000 /* Carry mask */
|
||
#define NMASK 0x00f00000 /* Normalize mask */
|
||
#define SNMASK 0x0f000000 /* Short normal mask */
|
||
|
||
#define HMASKL 0xffffffff00000000LL
|
||
#define LMASKL 0x00000000ffffffffLL
|
||
#define MSIGNL 0x8000000000000000LL
|
||
#define MMASKL 0x00ffffffffffffffLL
|
||
#define CMASKL 0x1000000000000000LL
|
||
#define EMASKL 0x7f00000000000000LL
|
||
#define XMASKL 0x0fffffffffffffffLL
|
||
#define NMASKL 0x00f0000000000000LL
|
||
#define UMASKL 0x0ffffffffffffff0LL
|
||
#define SNMASKL 0x0f00000000000000LL
|
||
#define LDDBL(r,x) x = (((t_uint64)regs[r]) << 32) | ((t_uint64)regs[r|1]); \
|
||
if (hst_lnt) { hst[hst_p].src1 = ((x) >> 32); \
|
||
hst[hst_p].src2 = (x) & FMASK; }
|
||
#define STDBL(r,x) regs[r|1] = (uint32)((x) & FMASK); \
|
||
regs[r] = (uint32)(((x) >> 32) & FMASK); \
|
||
per_mod |= 3 << r;
|
||
|
||
#define R1(x) (((x)>>4) & 0xf)
|
||
#define R2(x) ((x) & 0xf)
|
||
#define B1(x) (((x) >> 12) & 0xf)
|
||
#define D1(x) ((x) & 0xfff)
|
||
#define X2(x) R2(x)
|
||
#define NEG(x) (uint32)(-(int32)(x))
|
||
|
||
#define PTE_LEN 0xff000000 /* Page table length */
|
||
#define PTE_ADR 0x00fffffe /* Address of table */
|
||
#define PTE_VALID 0x00000001 /* table valid */
|
||
|
||
#define TLB_SEG 0x0001f000 /* Segment address */
|
||
#define TLB_VALID 0x80000000 /* Entry valid */
|
||
#define TLB_PHY 0x00000fff /* Physical page */
|
||
|
||
#define SEG_MASK 0xfffff000 /* Mask segment */
|
||
|
||
#define Q360 (cpu_unit[0].flags & FEAT_370) == 0
|
||
#define Q370 (cpu_unit[0].flags & FEAT_370) != 0
|
||
|
||
#define QVMA ((cpu_unit[0].flags & (FEAT_370|FEAT_VMA)) == (FEAT_370|FEAT_VMA) && \
|
||
((cregs[6] & 0xc0000000) == 0x80000000))
|
||
|
||
int hst_lnt;
|
||
int hst_p;
|
||
struct InstHistory
|
||
{
|
||
uint32 pc;
|
||
uint32 addr1;
|
||
uint32 addr2;
|
||
uint32 src1;
|
||
uint32 src2;
|
||
uint32 dest;
|
||
uint16 inst[3];
|
||
uint8 op;
|
||
uint8 reg;
|
||
uint8 cc;
|
||
};
|
||
|
||
struct InstHistory *hst = NULL;
|
||
|
||
/* Forward and external declarations */
|
||
|
||
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_bool cpu_fprint_stopped (FILE *st, t_stat v);
|
||
t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat cpu_set_idle_stop (UNIT *uptr, int32 val, CONST char *cptr, 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_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
||
const char *cptr);
|
||
const char *cpu_description (DEVICE *dptr);
|
||
|
||
void dec_srp(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2);
|
||
void dec_add(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2);
|
||
void dec_mul(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2);
|
||
void dec_div(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2);
|
||
extern int vma_370(int reg, uint32 addr1);
|
||
extern int vma_ssm(uint32 addr1);
|
||
extern int vma_lpsw(uint32 addr1);
|
||
extern int vma_stssk(uint32 src1, uint32 addr1);
|
||
extern int vma_stisk(uint8 reg1, uint32 addr1);
|
||
extern int vma_stsvc(uint8 reg);
|
||
extern int vma_lra(uint8 reg, uint32 addr1, uint8 *cc);
|
||
extern int vma_stnsm(uint8 reg, uint32 addr1);
|
||
extern int vma_stosm(uint8 reg, uint32 addr1);
|
||
extern int vma_stctl(uint8 reg, uint32 addr1);
|
||
void check_tod_irq();
|
||
|
||
t_bool build_dev_tab (void);
|
||
|
||
/* Interval timer option */
|
||
t_stat rtc_srv(UNIT * uptr);
|
||
t_stat rtc_reset(DEVICE * dptr);
|
||
int32 rtc_tps = 300;
|
||
|
||
|
||
/* 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 (&rtc_srv, UNIT_IDLE|UNIT_BINK|UNIT_FIX, MAXMEMSIZE)} };
|
||
|
||
REG cpu_reg[] = {
|
||
{ HRDATA (PC, PC, 24) },
|
||
{ HRDATA (CC, cc, 2) },
|
||
{ HRDATA (PMASK, pmsk, 4) },
|
||
{ HRDATA (FLAGS, flags, 4) },
|
||
{ HRDATA (KEY, st_key, 4) },
|
||
{ HRDATA (R0, regs[00], 32) },
|
||
{ HRDATA (R1, regs[01], 32) },
|
||
{ HRDATA (R2, regs[02], 32) },
|
||
{ HRDATA (R3, regs[03], 32) },
|
||
{ HRDATA (R4, regs[04], 32) },
|
||
{ HRDATA (R5, regs[05], 32) },
|
||
{ HRDATA (R6, regs[06], 32) },
|
||
{ HRDATA (R7, regs[07], 32) },
|
||
{ HRDATA (R8, regs[010], 32) },
|
||
{ HRDATA (R9, regs[011], 32) },
|
||
{ HRDATA (R10, regs[012], 32) },
|
||
{ HRDATA (R11, regs[013], 32) },
|
||
{ HRDATA (R12, regs[014], 32) },
|
||
{ HRDATA (R13, regs[015], 32) },
|
||
{ HRDATA (R14, regs[016], 32) },
|
||
{ HRDATA (R15, regs[017], 32) },
|
||
{ BRDATA (R, regs, 16, 32, 16) },
|
||
{ HRDATA (FP0, fpregs[00], 64) },
|
||
{ HRDATA (FP2, fpregs[02], 64) },
|
||
{ HRDATA (FP4, fpregs[04], 64) },
|
||
{ HRDATA (FP6, fpregs[06], 64) },
|
||
{ BRDATA (FP, fpregs, 16, 64, 8) },
|
||
{ BRDATA (CR, cregs, 16, 32, 16) },
|
||
{ NULL }
|
||
};
|
||
|
||
MTAB cpu_mod[] = {
|
||
{ MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
|
||
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
|
||
{ MTAB_VDV, MEMAMOUNT(1), NULL, "16K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(2), NULL, "32K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(4), NULL, "64K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(8), NULL, "128K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(12), NULL, "196K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(16), NULL, "256K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(32), NULL, "512K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(64), NULL, "1M", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(64), NULL, "1024K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(64), NULL, "1536K", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(128), NULL, "2M", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(256), NULL, "4M", &cpu_set_size },
|
||
{ MTAB_VDV, MEMAMOUNT(512), NULL, "8M", &cpu_set_size },
|
||
{ FEAT_370, FEAT_370, "IBM370", "IBM370", NULL, NULL, NULL, "Sets CPU to be a IBM370"},
|
||
{ FEAT_370, 0, "IBM360", "IBM360", NULL, NULL, NULL, "Sets CPU to be a IBM360"},
|
||
{ FEAT_PROT, 0, NULL, "NOPROT", NULL, NULL, NULL, "No Storage protection"},
|
||
{ FEAT_PROT, FEAT_PROT, "PROT", "PROT", NULL, NULL, NULL, "Storage protection"},
|
||
{ FEAT_UNIV, FEAT_UNIV, "UNIV", "UNIV", NULL, NULL, NULL, "Universal instruction"},
|
||
{ FEAT_UNIV, 0, NULL, "NOUNIV", NULL, NULL, NULL, "Basic instructions"},
|
||
{ FEAT_UNIV, FEAT_FLOAT, "FLOAT", "FLOAT", NULL, NULL, NULL,
|
||
"Floating point instructions"},
|
||
{ FEAT_FLOAT, 0, NULL, "NOFLOAT", NULL, NULL, NULL, "No floating point instructions"},
|
||
{ FEAT_UNIV, FEAT_DEC, "DECIMAL", "DECIMAL", NULL, NULL, NULL, "Decimal instruction set"},
|
||
{ FEAT_DEC, 0, NULL, "NODECIMAL", NULL, NULL, NULL, "No decimal instructions"},
|
||
{ FEAT_EFP|FEAT_FLOAT, FEAT_EFP|FEAT_FLOAT, "EFLOAT", "EFLOAT", NULL, NULL, NULL,
|
||
"Extended Floating point instruction"},
|
||
{ FEAT_EFP, 0, NULL, "NOEFLOAT", NULL, NULL, NULL, "No extended floating point"},
|
||
{ FEAT_STOR, FEAT_STOR, "STORE", "STORE", NULL, NULL, NULL, "No storage alignment"},
|
||
{ FEAT_STOR, 0, NULL, "NOSTORE", NULL, NULL, NULL},
|
||
{ FEAT_TIMER, FEAT_TIMER, "TIMER", "TIMER", NULL, NULL, NULL, "Interval timer"},
|
||
{ FEAT_TIMER, 0, NULL, "NOTIMER", NULL, NULL},
|
||
{ FEAT_DAT, FEAT_DAT, "DAT", "DAT", NULL, NULL, NULL, "DAT /67"},
|
||
{ FEAT_DAT, 0, NULL, "NODAT", NULL, NULL},
|
||
{ FEAT_VMA, FEAT_VMA, "VMA", "VMA", NULL, NULL, NULL, "Enable VM Assists"},
|
||
{ FEAT_VMA, 0, NULL, "NOVMA", NULL, NULL},
|
||
{ EXT_IRQ, 0, "NOEXT", NULL, NULL, NULL},
|
||
{ EXT_IRQ, EXT_IRQ, "EXT", "EXT", NULL, NULL, NULL,
|
||
"SET CPU EXT causes external interrupt"},
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IDLESTOP", "IDLESTOP", &cpu_set_idle_stop, NULL,
|
||
NULL, "SET CPU IDLESTOP stops cpu after waiting n seconds for IRQ"},
|
||
{ 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, 24, 1, 16, 8,
|
||
&cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL,
|
||
NULL, DEV_DEBUG, 0, cpu_debug,
|
||
NULL, NULL, &cpu_help, NULL, NULL, &cpu_description
|
||
};
|
||
|
||
void
|
||
post_extirq()
|
||
{
|
||
cpu_unit[0].flags |= EXT_IRQ;
|
||
}
|
||
|
||
|
||
void
|
||
storepsw(uint32 addr, uint16 ircode)
|
||
{
|
||
uint32 word;
|
||
uint32 word2;
|
||
irqaddr = addr + 0x40;
|
||
|
||
if (addr == OPPSW && per_en && per_code)
|
||
ircode |= IRC_PER;
|
||
if (ec_mode) {
|
||
/* Generate first word */
|
||
if (Q370) { /* IBM 370 in EC mode */
|
||
word = (((uint32)dat_en) << 26) |
|
||
((per_en) ? 1<<30:0) |
|
||
((irq_en) ? 1<<25:0) |
|
||
((ext_en) ? 1<<24:0) |
|
||
0x80000 |
|
||
(((uint32)st_key) << 16) |
|
||
(((uint32)flags) << 16) |
|
||
(((uint32)cc) << 12) |
|
||
(((uint32)pmsk) << 8);
|
||
/* Save code where 370 expects it to be */
|
||
switch(addr) {
|
||
case OEPSW:
|
||
M[0x84 >> 2] = (M[0x84 >> 2] & 0xffff0000) | ircode;
|
||
break;
|
||
case OSPSW:
|
||
M[0x88 >> 2] = (ilc << 17) | ircode;
|
||
break;
|
||
case OPPSW:
|
||
M[0x8C >> 2] = (ilc << 17) | ircode;
|
||
break;
|
||
case OIOPSW:
|
||
M[0xB8 >> 2] = ircode;
|
||
break;
|
||
}
|
||
if (ircode & IRC_PER) {
|
||
M[150 >> 2] = ((uint32)per_code << 16) | (per_addr >> 16);
|
||
M[154 >> 2] = ((per_addr & 0xffff) << 16) | (M[154 >> 2] & 0xFFFF);
|
||
}
|
||
} else { /* IBM 360/67 under EC mode */
|
||
word = (((uint32)dat_en) << 26) |
|
||
((irq_en) ? 1<<25:0) |
|
||
((ext_en) ? 1<<24:0) |
|
||
(((uint32)st_key) << 16) |
|
||
(((uint32)flags) << 16) |
|
||
(((uint32)ilc) << 14) |
|
||
(((uint32)cc) << 12) |
|
||
(((uint32)pmsk) << 8);
|
||
/* Save code where 360/67 expects it to be */
|
||
switch(addr) {
|
||
case OEPSW:
|
||
M[0xc >> 2] = (M[0xc >> 2] & 0xffff0000) | ircode;
|
||
break;
|
||
case OSPSW:
|
||
M[0x10 >> 2] = (M[0x10 >> 2] & 0x0000ffff) | ((uint32)ircode << 16);
|
||
break;
|
||
case OPPSW:
|
||
M[0x10 >> 2] = (M[0x10 >> 2] & 0xffff0000) | ircode;
|
||
break;
|
||
case OIOPSW:
|
||
M[0x14 >> 2] = (M[0x14 >> 2] & 0xffff0000) | ircode;
|
||
break;
|
||
}
|
||
}
|
||
/* Generate second word. */
|
||
word2 = PC;
|
||
} else {
|
||
/* Generate first word */
|
||
word = ((uint32)(ext_en) << 24) |
|
||
((uint32)(sysmsk & 0xfe00) << 16) |
|
||
(((uint32)st_key) << 16) |
|
||
(((uint32)flags) << 16) |
|
||
((uint32)ircode);
|
||
/* Generate second word. */
|
||
word2 = (((uint32)ilc) << 30) |
|
||
(((uint32)cc) << 28) |
|
||
(((uint32)pmsk) << 24) |
|
||
(PC & AMASK);
|
||
}
|
||
M[addr >> 2] = word;
|
||
addr += 4;
|
||
M[addr >> 2] = word2;
|
||
key[0] |= 0x6;
|
||
/* Update history */
|
||
if (hst_lnt) {
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt)
|
||
hst_p = 0;
|
||
hst[hst_p].pc = addr | HIST_SPW;
|
||
hst[hst_p].src1 = word;
|
||
hst[hst_p].src2 = word2;
|
||
hst[hst_p].addr1 = ircode;
|
||
}
|
||
sim_debug(DEBUG_INST, &cpu_dev, "store %02x %d %x %03x PSW=%08x %08x\n", addr, ilc,
|
||
cc, ircode, word, word2);
|
||
irqcode = ircode;
|
||
}
|
||
|
||
/*
|
||
* For 370
|
||
* PS = 2K page_shift = 11 pte_avail = 0x4 pte_mbz = 0x2 pte_shift = 3
|
||
* PS = 4K page_shift = 12 pte_avail = 0x8 pte_mbz = 0x6 pte_shift = 4
|
||
*
|
||
* For 360/67
|
||
* page_shift = 12 pte_avail = 0x8 pte_mbz = 0x6 pte_shift = 4
|
||
*
|
||
* For 370
|
||
* SS = 64K page_mask = 0x1F PS=4K page_mask = 0xF
|
||
* seg_shift = 16
|
||
* seg_mask = 0xff
|
||
* SS = 1M page_mask = 0xff PS=4k page_mask = 07F
|
||
* seg_shift = 20
|
||
* seg_mask = 0xF
|
||
* For 360/67
|
||
* page_shift = 12
|
||
* page_mask = 0xff
|
||
* seg_shift = 20
|
||
* seg_mask = 0xfff
|
||
*/
|
||
|
||
/*
|
||
* Translate an address from virtual to physical.
|
||
*/
|
||
int
|
||
TransAddr(uint32 va, uint32 *pa)
|
||
{
|
||
uint32 seg;
|
||
uint32 page;
|
||
uint32 entry;
|
||
uint32 addr;
|
||
|
||
/* Check address in range */
|
||
va &= AMASK;
|
||
|
||
if (!dat_en) {
|
||
if (va >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
return 1;
|
||
}
|
||
*pa = va;
|
||
return 0;
|
||
}
|
||
|
||
page = (va >> page_shift);
|
||
seg = (page & 0x1f00) << 4;
|
||
/* Quick check if TLB correct */
|
||
entry = tlb[page & 0xff];
|
||
if ((entry & TLB_VALID) != 0 && ((entry ^ seg) & TLB_SEG) == 0) {
|
||
*pa = (va & page_mask) | ((entry & TLB_PHY) << page_shift);
|
||
if (*pa >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
/* Clear whatever was in entry */
|
||
tlb[page & 0xff] = 0;
|
||
/* TLB not correct, try loading correct entry */
|
||
seg = (va >> seg_shift) & seg_mask; /* Segment number to word address */
|
||
page = (va >> page_shift) & page_index;
|
||
|
||
/* Check address against length of segment table */
|
||
if (seg > seg_len) {
|
||
if (Q370) {
|
||
M[0x90 >> 2] = va;
|
||
key[0] |= 0x6;
|
||
PC = iPC;
|
||
} else {
|
||
cregs[2] = va;
|
||
}
|
||
storepsw(OPPSW, IRC_SEG);
|
||
/* Not valid address */
|
||
return 1;
|
||
}
|
||
addr = (((seg << 2) + seg_addr) & AMASK);
|
||
if (addr >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
return 1;
|
||
}
|
||
/* Get pointer to page table */
|
||
entry = M[addr >> 2];
|
||
key[addr >> 11] |= 0x4;
|
||
if (Q370) {
|
||
addr = (entry >> 28) + 1;
|
||
/* Check if entry valid and in correct length */
|
||
if (entry & PTE_VALID || (page >> pte_len_shift) >= addr) {
|
||
M[0x90 >> 2] = va;
|
||
key[0] |= 0x6;
|
||
PC = iPC;
|
||
storepsw(OPPSW, (entry & PTE_VALID) ? IRC_SEG : IRC_PAGE);
|
||
return 1;
|
||
}
|
||
} else {
|
||
addr = (entry >> 24);
|
||
/* Check if entry valid and in correct length */
|
||
if (entry & PTE_VALID || page > addr) {
|
||
cregs[2] = va;
|
||
storepsw(OPPSW, IRC_PAGE);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* Now we need to fetch the actual entry */
|
||
addr = ((entry & PTE_ADR) + (page << 1)) & AMASK;
|
||
if (addr >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
return 1;
|
||
}
|
||
entry = M[addr >> 2];
|
||
key[addr >> 11] |= 0x4;
|
||
entry >>= (addr & 2) ? 0 : 16;
|
||
entry &= 0xffff;
|
||
|
||
if ((entry & pte_mbz) != 0) {
|
||
if (Q370) {
|
||
M[0x90 >> 2] = va;
|
||
key[0] |= 0x6;
|
||
PC = iPC;
|
||
} else {
|
||
cregs[2] = va;
|
||
}
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return 1;
|
||
}
|
||
|
||
/* Check if entry valid and in correct length */
|
||
if (entry & pte_avail) {
|
||
if (Q370) {
|
||
M[0x90 >> 2] = va;
|
||
key[0] |= 0x6;
|
||
PC = iPC;
|
||
} else {
|
||
cregs[2] = va;
|
||
}
|
||
storepsw(OPPSW, IRC_PAGE);
|
||
return 1;
|
||
}
|
||
|
||
/* Compute correct entry */
|
||
entry >>= pte_shift; /* Move physical to correct spot */
|
||
page = (va >> page_shift);
|
||
entry |= ((page & 0x1f00) << 4) | TLB_VALID;
|
||
tlb[page & 0xff] = entry;
|
||
*pa = ((va & page_mask) | ((entry & TLB_PHY) << page_shift)) & AMASK;
|
||
if (*pa >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Read a full word from memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
ReadFull(uint32 addr, uint32 *data)
|
||
{
|
||
uint32 pa;
|
||
int offset;
|
||
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa))
|
||
return 1;
|
||
|
||
/* Check storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
k = key[pa >> 11];
|
||
if ((k & 0x8) != 0 && (k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
offset = pa & 0x3;
|
||
|
||
/* If not on word boundry and store feature not set */
|
||
if (offset != 0 && (cpu_unit[0].flags & FEAT_STOR) == 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return 1;
|
||
}
|
||
|
||
/* Actual read */
|
||
*data = M[pa >> 2];
|
||
|
||
/* Handle unaligned access */
|
||
if (offset != 0) {
|
||
uint32 temp;
|
||
uint8 k;
|
||
|
||
temp = pa + 4;
|
||
if ((temp & 0x7FC) == 0) {
|
||
/* Check if possible next page */
|
||
if (TransAddr(addr + 4, &temp))
|
||
return 1;
|
||
/* Check access protection */
|
||
if (st_key != 0) {
|
||
k = key[temp >> 11];
|
||
if ((k & 0x8) != 0 && (k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Combine result */
|
||
key[temp >> 11] |= 0x4;
|
||
temp >>= 2;
|
||
temp = M[temp];
|
||
*data <<= 8 * offset;
|
||
temp >>= 8 * (4 - offset);
|
||
*data |= temp;
|
||
}
|
||
|
||
/* Update access flag */
|
||
key[pa >> 11] |= 0x4;
|
||
|
||
/* sim_debug(DEBUG_DATA, &cpu_dev, "RD A=%08x %08x\n", addr, *data); */
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Read a byte from memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
ReadByte(uint32 addr, uint32 *data)
|
||
{
|
||
|
||
if (ReadFull(addr & (~0x3), data))
|
||
return 1;
|
||
*data >>= 8 * (3 - (addr & 0x3));
|
||
*data &= 0xff;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Read a half word from memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
ReadHalf(uint32 addr, uint32 *data)
|
||
{
|
||
|
||
/* Check if unaligned access */
|
||
if (addr & 0x1) {
|
||
if ((cpu_unit[0].flags & FEAT_STOR) == 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return 1;
|
||
}
|
||
|
||
/* Check if past a word */
|
||
if (addr & 0x2) {
|
||
uint32 temp = 0;
|
||
if (ReadFull(addr + 1, &temp))
|
||
return 1;
|
||
if (ReadFull(addr & (~0x3), data))
|
||
return 1;
|
||
*data <<= 8;
|
||
*data |= (temp >> 24);
|
||
} else {
|
||
if (ReadFull(addr & (~0x3), data))
|
||
return 1;
|
||
*data >>= 8;
|
||
}
|
||
} else {
|
||
if (ReadFull(addr & (~0x3), data))
|
||
return 1;
|
||
*data >>= (addr & 2) ? 0 : 16;
|
||
}
|
||
*data &= 0xffff;
|
||
if (*data & 0x8000)
|
||
*data |= 0xffff0000;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Update a full word in memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
WriteFull(uint32 addr, uint32 data)
|
||
{
|
||
int offset;
|
||
uint32 pa;
|
||
uint32 pa2;
|
||
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa))
|
||
return 1;
|
||
|
||
/* Check if in storage area */
|
||
if (per_en && (cregs[9] & 0x20000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (addr >= cregs[10] && addr <= cregs[11]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
} else {
|
||
if (addr >= cregs[11] || addr <= cregs[10]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
}
|
||
}
|
||
|
||
offset = pa & 0x3;
|
||
|
||
if (offset != 0 && (cpu_unit[0].flags & FEAT_STOR) == 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return 1;
|
||
}
|
||
/* Check storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
k = key[pa >> 11];
|
||
if ((k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
pa2 = pa + 4;
|
||
/* Check if we handle unaligned access */
|
||
if (offset != 0 && (pa2 & 0x7FC) == 0) {
|
||
addr += 4;
|
||
/* Check if in storage area */
|
||
if (per_en && (cregs[9] & 0x20000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (addr >= cregs[10] && addr <= cregs[11]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
} else {
|
||
if (addr >= cregs[11] || addr <= cregs[10]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa2))
|
||
return 1;
|
||
|
||
/* Check against storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
k = key[pa2 >> 11];
|
||
if ((k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
key[pa2 >> 11] |= 0x6;
|
||
}
|
||
|
||
key[pa >> 11] |= 0x6;
|
||
|
||
pa >>= 2;
|
||
pa2 >>= 2;
|
||
|
||
/* Put data in correct locations */
|
||
switch (offset) {
|
||
case 0:
|
||
M[pa] = data;
|
||
break;
|
||
case 1:
|
||
M[pa] &= 0xff000000;
|
||
M[pa] |= 0xffffff & (data >> 8);
|
||
M[pa2] &= 0xffffff;
|
||
M[pa2] |= 0xff000000 & (data << 24);
|
||
break;
|
||
case 2:
|
||
M[pa] &= 0xffff0000;
|
||
M[pa] |= 0xffff & (data >> 16);
|
||
M[pa2] &= 0xffff;
|
||
M[pa2] |= 0xffff0000 & (data << 16);
|
||
break;
|
||
case 3:
|
||
M[pa] &= 0xffffff00;
|
||
M[pa] |= 0xff & (data >> 24);
|
||
M[pa2] &= 0xff;
|
||
M[pa2] |= 0xffffff00 & (data << 8);
|
||
break;
|
||
}
|
||
/* sim_debug(DEBUG_DATA, &cpu_dev, "WR A=%08x %08x\n", addr, data); */
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Update a byte in memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
WriteByte(uint32 addr, uint32 data)
|
||
{
|
||
uint32 mask;
|
||
uint32 pa;
|
||
int offset;
|
||
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa))
|
||
return 1;
|
||
|
||
/* Check if in storage area */
|
||
if (per_en && (cregs[9] & 0x20000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (addr >= cregs[10] && addr <= cregs[11]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
} else {
|
||
if (addr >= cregs[11] || addr <= cregs[10]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Check storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
k = key[pa >> 11];
|
||
if ((k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* Flag as modified */
|
||
key[pa >> 11] |= 0x6;
|
||
|
||
/* Do actual update */
|
||
offset = 8 * (3 - (pa & 0x3));
|
||
pa >>= 2;
|
||
mask = 0xff;
|
||
data &= mask;
|
||
/* sim_debug(DEBUG_DATA, &cpu_dev, "WR A=%08x %02x\n", addr, data); */
|
||
data <<= offset;
|
||
mask <<= offset;
|
||
M[pa] &= ~mask;
|
||
M[pa] |= data;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Update a half word in memory, checking protection
|
||
* and alignment restrictions. Return 1 if failure, 0 if
|
||
* success.
|
||
*/
|
||
int
|
||
WriteHalf(uint32 addr, uint32 data)
|
||
{
|
||
uint32 mask;
|
||
uint32 pa;
|
||
uint32 pa2;
|
||
int offset;
|
||
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa))
|
||
return 1;
|
||
|
||
/* Check if in storage area */
|
||
if (per_en && (cregs[9] & 0x20000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (addr >= cregs[10] && addr <= cregs[11]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
} else {
|
||
if (addr >= cregs[11] || addr <= cregs[10]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
}
|
||
}
|
||
|
||
offset = pa & 0x3;
|
||
/* Check if we handle unaligned access */
|
||
if ((offset & 1) != 0 && (cpu_unit[0].flags & FEAT_STOR) == 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return 1;
|
||
}
|
||
|
||
/* Check storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
k = key[pa >> 11];
|
||
if ((k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
if (offset == 3) {
|
||
addr += 4;
|
||
/* Check if in storage area */
|
||
if (per_en && (cregs[9] & 0x20000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (addr >= cregs[10] && addr <= cregs[11]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
} else {
|
||
if (addr >= cregs[11] || addr <= cregs[10]) {
|
||
per_code |= 0x2000;
|
||
}
|
||
}
|
||
}
|
||
|
||
pa2 = pa + 4;
|
||
if ((pa2 & 0x7FC) == 0) {
|
||
/* Validate address */
|
||
if (TransAddr(addr, &pa2))
|
||
return 1;
|
||
|
||
/* Check against storage key */
|
||
if (st_key != 0) {
|
||
uint8 k;
|
||
|
||
k = key[(pa2) >> 11];
|
||
if ((k & 0xf0) != st_key) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
key[pa >> 11] |= 0x6;
|
||
key[pa2 >> 11] |= 0x6;
|
||
pa >>= 2;
|
||
pa2 >>= 2;
|
||
M[pa] &= 0xffffff00;
|
||
M[pa] |= 0xff & (data >> 8);
|
||
M[pa2] &= 0x00ffffff;
|
||
M[pa2] |= 0xff000000 & (data << 24);
|
||
return 0;
|
||
}
|
||
|
||
/* Flag as modified */
|
||
key[pa >> 11] |= 0x6;
|
||
|
||
pa >>= 2;
|
||
|
||
mask = 0xffff;
|
||
data &= mask;
|
||
/* sim_debug(DEBUG_DATA, &cpu_dev, "WR A=%08x %04x\n", addr, data); */
|
||
switch (offset) {
|
||
case 0:
|
||
M[pa] &= ~(mask << 16);
|
||
M[pa] |= data << 16;
|
||
break;
|
||
case 1:
|
||
M[pa] &= ~(mask << 8);
|
||
M[pa] |= data << 8;
|
||
break;
|
||
case 2:
|
||
M[pa] &= ~mask;
|
||
M[pa] |= data;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
t_stat
|
||
sim_instr(void)
|
||
{
|
||
t_stat reason;
|
||
uint32 src1; /* 1st Source low and high */
|
||
uint32 src1h;
|
||
uint32 src2; /* 2st Source low and high */
|
||
uint32 src2h;
|
||
uint32 dest; /* Destination low and high */
|
||
uint32 desth;
|
||
uint32 addr1; /* Address of 1st source */
|
||
uint32 addr2; /* Address of 2st source */
|
||
uint16 ops[3]; /* Current instruction */
|
||
uint8 op; /* Opcode of current instruction */
|
||
uint8 fill; /* Holds fill and other temp flags */
|
||
uint8 digit; /* Holds digit during ED instruction */
|
||
uint8 reg; /* Second byte of instruction */
|
||
uint8 reg1; /* First register */
|
||
uint8 zone; /* Holds zone during PACK & ED instructions */
|
||
uint16 irq; /* Holds current irq code */
|
||
int e1, e2; /* Exponent 1 & 2 and various flags */
|
||
int temp;
|
||
t_uint64 src1L; /* 64 bit source 1 and 2 */
|
||
t_uint64 src2L;
|
||
t_uint64 destL; /* 64 bit destination */
|
||
t_uint64 dest2L; /* Upper 64 bit for product */
|
||
|
||
/* Initialize DAT translation values */
|
||
if (Q370) {
|
||
switch((cregs[0] >> 22) & 03) {
|
||
default: /* Generate translation exception */
|
||
case 1: /* 2K pages */
|
||
page_shift = 11;
|
||
page_mask = 0x7ff;
|
||
pte_avail = 0x4;
|
||
pte_mbz = 0x2;
|
||
pte_shift = 3;
|
||
pte_len_shift = 1;
|
||
break;
|
||
case 2: /* 4K pages */
|
||
page_shift = 12;
|
||
page_mask = 0xfff;
|
||
pte_avail = 0x8;
|
||
pte_mbz = 0x6;
|
||
pte_shift = 4;
|
||
pte_len_shift = 0;
|
||
break;
|
||
}
|
||
switch((cregs[0] >> 19) & 07) {
|
||
default: /* Generate translation exception */
|
||
case 0: /* 64K segments */
|
||
seg_shift = 16;
|
||
seg_mask = AMASK >> 16;
|
||
break;
|
||
case 2: /* 1M segments */
|
||
seg_shift = 20;
|
||
seg_mask = AMASK >> 20;
|
||
pte_len_shift += 4;
|
||
break;
|
||
}
|
||
seg_addr = cregs[1] & AMASK;
|
||
seg_len = (((cregs[1] >> 24) & 0xff) + 1) << 4;
|
||
} else {
|
||
/* 360/67 only supports 4k pages */
|
||
page_shift = 12;
|
||
page_mask = 0xfff;
|
||
pte_avail = 0x8;
|
||
pte_mbz = 0x7;
|
||
pte_shift = 4;
|
||
pte_len_shift = 0;
|
||
seg_shift = 20;
|
||
seg_mask = AMASK >> 20;
|
||
seg_addr = cregs[0] & AMASK;
|
||
seg_len = (((cregs[0] >> 24) & 0xff) + 1) << 4;
|
||
}
|
||
/* Generate pte index mask */
|
||
page_index = ((~(seg_mask << seg_shift) & ~page_mask) & AMASK) >> page_shift;
|
||
reason = SCPE_OK;
|
||
ilc = 0;
|
||
irq_en |= (loading != 0);
|
||
|
||
while (reason == SCPE_OK) {
|
||
wait_loop:
|
||
if (sim_interval <= 0) {
|
||
reason = sim_process_event();
|
||
if (reason != SCPE_OK)
|
||
return reason;
|
||
}
|
||
|
||
/* Check if we should see if an IRQ is pending */
|
||
irq = scan_chan(sysmsk, irq_en);
|
||
if (irq != NO_DEV) {
|
||
ilc = 0;
|
||
sim_debug(DEBUG_DETAIL, &cpu_dev, "IRQ=%04x %08x\n", irq, PC);
|
||
if (loading) {
|
||
irqcode = irq;
|
||
loading = 0;
|
||
irqaddr = 0;
|
||
} else
|
||
storepsw(OIOPSW, irq);
|
||
goto supress;
|
||
}
|
||
|
||
/* Check for external interrupts */
|
||
if (ext_en) {
|
||
if ((cpu_unit[0].flags & EXT_IRQ) != 0) {
|
||
if (!ec_mode ||
|
||
(Q370 && ((cregs[0] & 0x20) != 0)) ||
|
||
(Q360 && ((cregs[6] & 0x40) != 0))) {
|
||
ilc = 0;
|
||
cpu_unit[0].flags &= ~EXT_IRQ;
|
||
storepsw(OEPSW, 0x40);
|
||
goto supress;
|
||
}
|
||
}
|
||
|
||
if (interval_irq &&
|
||
((((cpu_unit[0].flags & (FEAT_370|FEAT_DAT)) != 0 &&
|
||
((cregs[0] & 0x80) != 0))) ||
|
||
((cpu_unit[0].flags & (FEAT_TIMER)) != 0))) {
|
||
ilc = 0;
|
||
interval_irq = 0;
|
||
storepsw(OEPSW, 0x80);
|
||
goto supress;
|
||
}
|
||
if (clk_irq && intval_en) {
|
||
ilc = 0;
|
||
clk_irq = 0;
|
||
storepsw(OEPSW, 0x1005);
|
||
goto supress;
|
||
}
|
||
if (tod_irq && tod_en) {
|
||
ilc = 0;
|
||
tod_irq = 0;
|
||
storepsw(OEPSW, 0x1004);
|
||
goto supress;
|
||
}
|
||
}
|
||
|
||
/* If we have wait flag or loading, nothing more to do */
|
||
if (loading || flags & WAIT) {
|
||
/* CPU IDLE */
|
||
if (flags & WAIT && irq_en == 0 && ext_en == 0)
|
||
return STOP_HALT;
|
||
if (idle_stop_msec) {
|
||
/* check idle time */
|
||
if (idle_stop_tm0 == 0) {
|
||
idle_stop_tm0 = sim_os_msec(); /* init idle time */
|
||
} else {
|
||
if (sim_os_msec() - idle_stop_tm0 > idle_stop_msec) {
|
||
/* reset idle countdown because max time expired */
|
||
idle_stop_tm0=0;
|
||
return STOP_IBKPT;
|
||
}
|
||
}
|
||
}
|
||
if (sim_idle_enab)
|
||
sim_idle(TMR_RTC, FALSE);
|
||
sim_interval--;
|
||
goto wait_loop;
|
||
}
|
||
/* reset idle countdown because some activity */
|
||
idle_stop_tm0=0;
|
||
|
||
if (sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) {
|
||
return STOP_IBKPT;
|
||
}
|
||
|
||
if (PC & 1) {
|
||
ilc = 0;
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
|
||
if (hst_lnt) {
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt)
|
||
hst_p = 0;
|
||
hst[hst_p].pc = PC | HIST_PC;
|
||
hst[hst_p].inst[0] = 0x00;
|
||
}
|
||
if (sim_deb && (cpu_dev.dctrl & DEBUG_INST)) {
|
||
if (ec_mode) {
|
||
if (Q370)
|
||
sim_debug(DEBUG_INST, &cpu_dev, "PSW=%08x %08x ",
|
||
(((uint32)dat_en) << 26) | ((per_en) ? 1<<30:0) | ((irq_en) ? 1<<25:0) |
|
||
((ext_en) ? 1<<24:0) | 0x80000 | (((uint32)st_key) << 16) |
|
||
(((uint32)flags) << 16) | (((uint32)cc) << 12) | (((uint32)pmsk) << 8), PC);
|
||
else
|
||
sim_debug(DEBUG_INST, &cpu_dev, "PSW=%08x %08x ",
|
||
(((uint32)dat_en) << 26) | ((irq_en) ? 1<<25:0) | ((ext_en) ? 1<<24:0) |
|
||
(((uint32)st_key) << 16) | (((uint32)flags) << 16) | (((uint32)ilc) << 14) |
|
||
(((uint32)cc) << 12) | (((uint32)pmsk) << 8), PC);
|
||
} else {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "PSW=%08x %08x ",
|
||
((uint32)(ext_en) << 24) | (((uint32)sysmsk & 0xfe00) << 16) |
|
||
(((uint32)st_key) << 16) | (((uint32)flags) << 16) | ((uint32)irqcode),
|
||
(((uint32)ilc) << 30) | (((uint32)cc) << 28) | (((uint32)pmsk) << 24) | PC);
|
||
}
|
||
}
|
||
per_mod = 0;
|
||
per_code = 0;
|
||
per_addr = PC;
|
||
iPC = PC;
|
||
ilc = 0;
|
||
|
||
/* Fetch the next instruction */
|
||
if (ReadHalf(PC, &dest))
|
||
goto supress;
|
||
if (per_en && (cregs[9] & 0x40000000) != 0) {
|
||
if (cregs[10] <= cregs[11]) {
|
||
if (PC >= cregs[10] && PC <= cregs[11]) {
|
||
per_code |= 0x4000;
|
||
}
|
||
} else {
|
||
if (PC >= cregs[11] || PC <= cregs[10]) {
|
||
per_code |= 0x4000;
|
||
}
|
||
}
|
||
}
|
||
ops[0] = dest;
|
||
ilc = 1;
|
||
if (hst_lnt)
|
||
hst[hst_p].inst[0] = ops[0];
|
||
PC += 2;
|
||
reg = (uint8)(ops[0] & 0xff);
|
||
reg1 = R1(reg);
|
||
op = (uint8)(ops[0] >> 8);
|
||
/* Check if RX, RR, SI, RS, SS opcode */
|
||
if ((op & 0xc0) != 0) {
|
||
ilc = 2;
|
||
if (ReadHalf(PC, &dest))
|
||
goto supress;
|
||
ops[1] = dest;
|
||
PC += 2;
|
||
if (hst_lnt)
|
||
hst[hst_p].inst[1] = ops[1];
|
||
/* Check if SS */
|
||
if ((op & 0xc0) == 0xc0) {
|
||
ilc = 3;
|
||
if (ReadHalf(PC, &dest))
|
||
goto supress;;
|
||
ops[2] = dest;
|
||
PC += 2;
|
||
if (hst_lnt)
|
||
hst[hst_p].inst[2] = ops[2];
|
||
}
|
||
}
|
||
|
||
opr:
|
||
if (sim_deb && (cpu_dev.dctrl & DEBUG_INST)) {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "%d %d INST=%04x", ilc, cc, ops[0]);
|
||
if (ops[0] & 0xc000) {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "%04x", ops[1]);
|
||
if ((ops[0] & 0xc000) == 0xc000)
|
||
sim_debug(DEBUG_INST, &cpu_dev, "%04x", ops[2]);
|
||
else
|
||
sim_debug(DEBUG_INST, &cpu_dev, " ");
|
||
} else
|
||
sim_debug(DEBUG_INST, &cpu_dev, " ");
|
||
sim_debug(DEBUG_INST, &cpu_dev, " ");
|
||
fprint_inst(sim_deb, ops);
|
||
sim_debug(DEBUG_INST, &cpu_dev, "\n");
|
||
}
|
||
|
||
/* If RX or RS or SS SI etc compute first address */
|
||
if ((op & 0xc0) != 0) {
|
||
uint32 temp;
|
||
|
||
temp = B1(ops[1]);
|
||
addr1 = D1(ops[1]);
|
||
if (temp)
|
||
addr1 = (addr1 + regs[temp]) & AMASK;
|
||
/* Handle RX type operands */
|
||
if ((op & 0x80) == 0 && X2(reg) != 0)
|
||
addr1 = (addr1 + regs[X2(reg)]) & AMASK;
|
||
/* Check if SS */
|
||
if ((op & 0xc0) == 0xc0) {
|
||
temp = B1(ops[2]);
|
||
addr2 = D1(ops[2]);
|
||
if (temp)
|
||
addr2 = (addr2 + regs[temp]) & AMASK;
|
||
}
|
||
}
|
||
|
||
sim_debug(DEBUG_TRACE, &cpu_dev, "OP=%02x R=%02x A1=%06x A2=%06x ",
|
||
op, reg, addr1, addr2);
|
||
/* update history */
|
||
if (hst_lnt) {
|
||
hst[hst_p].op = op;
|
||
hst[hst_p].reg = reg;
|
||
hst[hst_p].addr1 = addr1;
|
||
hst[hst_p].addr2 = addr2;
|
||
hst[hst_p].src1 = 0;
|
||
hst[hst_p].src2 = 0;
|
||
}
|
||
|
||
/* Check if floating point */
|
||
if ((op & 0xA0) == 0x20) {
|
||
if ((cpu_unit[0].flags & FEAT_FLOAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if (reg1 & 0x9) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
/* Load operands */
|
||
src1L = fpregs[reg1];
|
||
if ((op & 0x10) != 0) {
|
||
src1L = src1L & HMASKL;
|
||
}
|
||
if ((op & 0x40) != 0) {
|
||
if (ReadFull(addr1, &src2))
|
||
goto supress;
|
||
if ((op & 0x10) == 0) {
|
||
if ((addr1 & 0x3) != 0 && (cpu_unit[0].flags & FEAT_STOR) == 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
if (ReadFull(addr1 + 4, &src2h))
|
||
goto supress;
|
||
} else {
|
||
src2h = 0;
|
||
}
|
||
src2L = (((t_uint64)src2) << 32) | ((t_uint64)src2h);
|
||
} else {
|
||
if (reg & 0x9) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
src2L = fpregs[R2(reg)];
|
||
if ((op & 0x10) != 0) {
|
||
src2L = src2L & HMASKL;
|
||
}
|
||
}
|
||
/* All RR opcodes */
|
||
} else if ((op & 0xe0) == 0) {
|
||
src1 = regs[reg1];
|
||
dest = src2 = regs[R2(reg)];
|
||
addr1 = dest & AMASK;
|
||
/* All RX integer ops */
|
||
} else if ((op & 0xe0) == 0x40) {
|
||
dest = src1 = regs[reg1];
|
||
/* Read half word if 010010xx or 01001100 */
|
||
if ((op & 0xfc) == 0x48 || op == OP_MH) {
|
||
if (ReadHalf(addr1, &src2))
|
||
goto supress;
|
||
/* Read full word if 0101xxx and not xxxx00xx (ST) */
|
||
} else if ((op & 0x10) && (op & 0x0c) != 0) {
|
||
if (ReadFull(addr1, &src2))
|
||
goto supress;
|
||
} else
|
||
src2 = addr1;
|
||
}
|
||
if (hst_lnt) {
|
||
hst[hst_p].src1 = src1;
|
||
hst[hst_p].src2 = src2;
|
||
}
|
||
sim_debug(DEBUG_TRACE, &cpu_dev, "S1=%08x S2=%08x ", src1, src2);
|
||
|
||
/* Preform opcode */
|
||
switch (op) {
|
||
case OP_SPM:
|
||
dest = src1;
|
||
pmsk = (src1 >> 24) & 0xf;
|
||
cc = (src1 >> 28) & 0x3;
|
||
break;
|
||
|
||
case OP_BASR:
|
||
case OP_BAS:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
} else {
|
||
dest = PC;
|
||
if (op != OP_BASR || R2(reg) != 0) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
}
|
||
break;
|
||
|
||
case OP_BALR:
|
||
case OP_BAL:
|
||
dest = (((uint32)ilc) << 30) |
|
||
((uint32)(cc & 03) << 28) |
|
||
(((uint32)pmsk) << 24) | PC;
|
||
if (op != OP_BALR || R2(reg) != 0) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_BCTR:
|
||
case OP_BCT:
|
||
dest = src1 - 1;
|
||
if (dest != 0 && (op != OP_BCTR || R2(reg) != 0)) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_BCR:
|
||
case OP_BC:
|
||
dest = src1;
|
||
if (((0x8 >> cc) & reg1) != 0 && (op != OP_BCR || R2(reg) != 0)) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
break;
|
||
|
||
case OP_BXH:
|
||
reg = R2(reg);
|
||
src1 = regs[reg|1];
|
||
dest = regs[reg1] = regs[reg1] + regs[reg];
|
||
per_mod |= 1 << reg1;
|
||
if ((int32)dest > (int32)src1) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
break;
|
||
|
||
case OP_BXLE:
|
||
reg = R2(reg);
|
||
src1 = regs[reg|1];
|
||
dest = regs[reg1] = regs[reg1] + regs[reg];
|
||
per_mod |= 1 << reg1;
|
||
if ((int32)dest <= (int32)src1) {
|
||
if (per_en && (cregs[9] & 0x80000000) != 0) {
|
||
per_code |= 0x8000; /* Set PER branch */
|
||
}
|
||
PC = addr1 & AMASK;
|
||
}
|
||
break;
|
||
|
||
case OP_SSK:
|
||
dest = src1;
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
} else if (flags & PROBLEM) {
|
||
/* Try to do quick SSK */
|
||
if (QVMA && vma_stssk(src1, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
} else if ((addr1 & 0xF) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else if (addr1 >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
goto supress;
|
||
} else {
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0)
|
||
key[addr1 >> 11] = src1 & 0xfe;
|
||
else
|
||
key[addr1 >> 11] = src1 & 0xf8;
|
||
}
|
||
break;
|
||
|
||
case OP_ISK:
|
||
dest = src1;
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_PROT);
|
||
} if (flags & PROBLEM) {
|
||
/* Try to do quick ISK */
|
||
if (QVMA && vma_stisk(reg1, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else if ((addr1 & 0xF) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
} else if (addr1 >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
} else {
|
||
dest &= 0xffffff00;
|
||
if (ec_mode)
|
||
dest |= key[addr1 >> 11] & 0xfe;
|
||
else
|
||
dest |= key[addr1 >> 11] & 0xf8;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
}
|
||
break;
|
||
|
||
case OP_SVC:
|
||
/* Try to do quick SVC */
|
||
if ((flags & PROBLEM) != 0 && \
|
||
(cpu_unit[0].flags & (FEAT_370|FEAT_VMA)) == (FEAT_370|FEAT_VMA) && \
|
||
(cregs[6] & 0x88000000) == MSIGN && vma_stsvc(reg))
|
||
break;
|
||
storepsw(OSPSW, reg);
|
||
break;
|
||
|
||
case OP_SSM:
|
||
if ((flags & PROBLEM)) {
|
||
sim_debug(DEBUG_VMA, &cpu_dev, "SSM CR6 %08x\n", cregs[6]);
|
||
if (QVMA && vma_ssm(addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
} else {
|
||
if (Q370 && (cregs[0] & 0x40000000) != 0) {
|
||
storepsw(OPPSW, IRC_SPOP);
|
||
goto supress;
|
||
}
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
ext_en = (src1 & 01) != 0;
|
||
if (ec_mode) {
|
||
irq_en = (src1 & 02) != 0;
|
||
dat_en = (src1 & 04) != 0;
|
||
if (Q370) {
|
||
per_en = ((src1 & 0x40) != 0);
|
||
sysmsk = irq_en ? (cregs[2] >> 16) : 0;
|
||
} else { /* IBM 360/67 */
|
||
if (irq_en) {
|
||
sysmsk = (cregs[4] >> 16) & 0xfe00;
|
||
sysmsk |= (cregs[4] >> 15) & 0x01ff;
|
||
} else {
|
||
sysmsk = 0;
|
||
}
|
||
}
|
||
if (src1 & 0xb8) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
} else {
|
||
sysmsk = (src1 & 0xfc) << 8;
|
||
irq_en = (sysmsk != 0);
|
||
dat_en = 0;
|
||
if (src1 & 0x2) {
|
||
if (Q370)
|
||
sysmsk |= (cregs[2] >> 16) & 0x3ff;
|
||
else
|
||
sysmsk |= 0x3ff;
|
||
}
|
||
}
|
||
}
|
||
irq_pend = 1;
|
||
break;
|
||
|
||
case OP_LPSW:
|
||
if (flags & PROBLEM) {
|
||
if (QVMA && vma_lpsw(addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
} else if ((addr1 & 0x7) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else {
|
||
if (ReadFull(addr1, &src1))
|
||
goto supress;
|
||
if (ReadFull(addr1+4, &src2))
|
||
goto supress;
|
||
if (hst_lnt) {
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt)
|
||
hst_p = 0;
|
||
hst[hst_p].pc = irqaddr | HIST_LPW;
|
||
hst[hst_p].src1 = src1;
|
||
hst[hst_p].src2 = src2;
|
||
}
|
||
goto lpsw;
|
||
}
|
||
break;
|
||
|
||
case OP_SIO:
|
||
if (flags & PROBLEM)
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
else
|
||
cc = startio(addr1 & 0x1fff);
|
||
break;
|
||
|
||
case OP_TIO:
|
||
if (flags & PROBLEM)
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
else
|
||
cc = testio(addr1 & 0x1fff);
|
||
break;
|
||
|
||
case OP_HIO:
|
||
if (flags & PROBLEM)
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
else
|
||
cc = haltio(addr1 & 0x1fff);
|
||
break;
|
||
|
||
case OP_TCH:
|
||
if (flags & PROBLEM)
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
else
|
||
cc = testchan(addr1 & 0x1fff);
|
||
break;
|
||
|
||
case OP_DIAG:
|
||
if (flags & PROBLEM)
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
else
|
||
storepsw(OMPSW, reg);
|
||
break;
|
||
|
||
case OP_LPR:
|
||
if ((dest & MSIGN) == 0)
|
||
goto set_cc;
|
||
/* Fall through */
|
||
|
||
case OP_LCR:
|
||
if (dest == MSIGN)
|
||
goto set_cc3;
|
||
dest = NEG(dest);
|
||
/* Fall through */
|
||
|
||
case OP_LTR:
|
||
set_cc:
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
cc = (dest & MSIGN) ? 1 : (dest == 0) ? 0 : 2;
|
||
break;
|
||
|
||
case OP_LNR:
|
||
if ((dest & MSIGN) == 0)
|
||
dest = NEG(dest);
|
||
goto set_cc;
|
||
|
||
case OP_LA:
|
||
case OP_L:
|
||
case OP_LH:
|
||
case OP_LR:
|
||
dest = src2;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_C:
|
||
case OP_CR:
|
||
case OP_CH:
|
||
dest = src1;
|
||
cc = 0;
|
||
if ((int32)(src1) > (int32)(src2))
|
||
cc = 2;
|
||
else if (src1 != src2)
|
||
cc = 1;
|
||
break;
|
||
|
||
case OP_S:
|
||
case OP_SR:
|
||
case OP_SH:
|
||
src2 ^= FMASK;
|
||
dest = src1 + src2 + 1;
|
||
src1h = (src1 & src2) | ((src1 ^ src2) & ~dest);
|
||
if ((((src1h << 1) ^ src1h) & MSIGN) != 0) {
|
||
goto set_cc3;
|
||
}
|
||
goto set_cc;
|
||
|
||
case OP_A:
|
||
case OP_AR:
|
||
case OP_AH:
|
||
dest = src1 + src2;
|
||
src1h = (src1 & src2) | ((src1 ^ src2) & ~dest);
|
||
if ((((src1h << 1) ^ src1h) & MSIGN) != 0) {
|
||
set_cc3:
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
cc = 3;
|
||
if (pmsk & FIXOVR) {
|
||
storepsw(OPPSW, IRC_FIXOVR);
|
||
}
|
||
break;
|
||
}
|
||
goto set_cc;
|
||
|
||
case OP_SL:
|
||
case OP_SLR:
|
||
src2 ^= FMASK;
|
||
dest = src1 + src2 + 1;
|
||
src1h = ((src1 & src2) | ((src1 ^ src2) & ~dest)) & MSIGN;
|
||
cc = (dest != 0);
|
||
if (src1h != 0)
|
||
cc |= 2;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_AL:
|
||
case OP_ALR:
|
||
dest = src1 + src2;
|
||
src1h = ((src1 & src2) | ((src1 ^ src2) & ~dest)) & MSIGN;
|
||
cc = (dest != 0);
|
||
if (src1h != 0)
|
||
cc |= 2;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_CL:
|
||
case OP_CLR:
|
||
dest = src1;
|
||
cc = 0;
|
||
if ((uint32)src1 > (uint32)src2)
|
||
cc = 2;
|
||
else if (src1 != src2)
|
||
cc = 1;
|
||
break;
|
||
|
||
case OP_M:
|
||
case OP_MR:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
break;
|
||
}
|
||
src1 = regs[reg1|1];
|
||
case OP_MH:
|
||
src1L = (((t_int64) ((t_uint64)src1 << 32)) >> 32) * (int32)src2;
|
||
if (op != OP_MH) {
|
||
STDBL(reg1, src1L);
|
||
} else {
|
||
regs[reg1] = (uint32)(src1L & FMASK);
|
||
per_mod |= 1 << reg1;
|
||
}
|
||
break;
|
||
|
||
case OP_D:
|
||
case OP_DR:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
if (src2 == 0) {
|
||
storepsw(OPPSW, IRC_FIXDIV);
|
||
goto supress;
|
||
}
|
||
fill = 0;
|
||
LDDBL(reg1, src1L);
|
||
if (src1L & MSIGNL) {
|
||
fill = 3;
|
||
src1L = NEG(src1L);
|
||
}
|
||
if (src2 & MSIGN) {
|
||
fill ^= 1;
|
||
src2 = NEG(src2);
|
||
}
|
||
src2L = src1L % (t_uint64)src2;
|
||
src1L = src1L / (t_uint64)src2;
|
||
|
||
/* Check for overflow */
|
||
if ((src1L & 0xFFFFFFFF80000000LL) != 0) {
|
||
storepsw(OPPSW, IRC_FIXDIV);
|
||
goto supress;
|
||
}
|
||
|
||
src1 = (uint32)(src2L & FMASK);
|
||
dest = (uint32)(src1L & FMASK);
|
||
if (fill & 1)
|
||
dest = NEG(dest);
|
||
if (fill & 2)
|
||
src1 = NEG(src1);
|
||
regs[reg1] = src1;
|
||
regs[reg1|1] = dest;
|
||
per_mod |= 3 << reg1;
|
||
break;
|
||
|
||
case OP_NR:
|
||
case OP_N:
|
||
dest = src1 & src2;
|
||
cc = (dest == 0) ? 0 : 1;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_OR:
|
||
case OP_O:
|
||
dest = src1 | src2;
|
||
cc = (dest == 0) ? 0 : 1;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_XR:
|
||
case OP_X:
|
||
dest = src1 ^ src2;
|
||
cc = (dest == 0) ? 0 : 1;
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_MVI:
|
||
src1 = reg;
|
||
/* Fall through */
|
||
|
||
case OP_STC:
|
||
WriteByte(addr1, src1);
|
||
break;
|
||
|
||
case OP_NI:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest &= reg;
|
||
char_save:
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
cc = (dest == 0) ? 0 : 1;
|
||
break;
|
||
|
||
case OP_OI:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest |= reg;
|
||
goto char_save;
|
||
|
||
case OP_XI:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest ^= reg;
|
||
goto char_save;
|
||
|
||
case OP_CLI:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest &= 0xff;
|
||
cc = (dest == reg) ? 0 : (dest < reg) ? 1 : 2;
|
||
break;
|
||
|
||
case OP_IC:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
dest = (src1 & 0xffffff00) | (dest & 0xff);
|
||
regs[reg1] = dest;
|
||
break;
|
||
|
||
case OP_ST:
|
||
dest = src1;
|
||
WriteFull(addr1, dest);
|
||
break;
|
||
|
||
case OP_STH:
|
||
dest = src1;
|
||
WriteHalf(addr1, dest);
|
||
break;
|
||
|
||
case OP_TS:
|
||
dest = 0xff;
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = src1;
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
cc = (src1 & 0x80) ? 1 : 0;
|
||
break;
|
||
|
||
case OP_TM:
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest &= reg;
|
||
if (dest != 0) {
|
||
if (reg == dest)
|
||
cc = 3;
|
||
else
|
||
cc = 1;
|
||
} else
|
||
cc = 0;
|
||
break;
|
||
|
||
case OP_SRL:
|
||
dest = regs[reg1];
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest = ((uint32)dest) >> (addr1 & 0x3f);
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_SLL:
|
||
dest = regs[reg1];
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest = ((uint32)dest) << (addr1 & 0x3f);
|
||
regs[reg1] = dest;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
|
||
case OP_SRA:
|
||
dest = regs[reg1];
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
dest = (int32)dest >> (addr1 & 0x3f);
|
||
goto set_cc;
|
||
|
||
case OP_SLA:
|
||
dest = regs[reg1];
|
||
if (hst_lnt)
|
||
hst[hst_p].src1 = dest;
|
||
src2 = dest & MSIGN;
|
||
dest &= ~MSIGN;
|
||
addr1 &= 0x3f;
|
||
cc = 0;
|
||
while (addr1 > 0) {
|
||
dest <<= 1;
|
||
if ((dest & MSIGN) != src2)
|
||
cc = 3;
|
||
addr1--;
|
||
}
|
||
dest &= ~MSIGN;
|
||
dest |= src2;
|
||
if (cc == 3)
|
||
goto set_cc3;
|
||
else
|
||
goto set_cc;
|
||
break;
|
||
|
||
case OP_SRDL:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else {
|
||
addr1 &= 0x3f;
|
||
LDDBL(reg1, src1L);
|
||
src1L >>= addr1;
|
||
STDBL(reg1, src1L);
|
||
dest = regs[reg1];
|
||
}
|
||
break;
|
||
|
||
case OP_SLDL:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else {
|
||
addr1 &= 0x3f;
|
||
LDDBL(reg1, src1L);
|
||
src1L <<= addr1;
|
||
STDBL(reg1, src1L);
|
||
dest = regs[reg1];
|
||
}
|
||
break;
|
||
|
||
case OP_SLDA:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else {
|
||
addr1 &= 0x3f;
|
||
cc = 0;
|
||
LDDBL(reg1, src1L);
|
||
src2L = src1L & MSIGNL;
|
||
while (addr1 > 0) {
|
||
src1L <<= 1;
|
||
if (src2L != (src1L & MSIGNL))
|
||
cc = 3;
|
||
addr1--;
|
||
}
|
||
src1L &= ~MSIGNL;
|
||
src1L |= src2L;
|
||
save_dbl:
|
||
STDBL(reg1, src1L);
|
||
if (cc != 3 && src1L != 0)
|
||
cc = (src1L & MSIGNL) ? 1 : 2;
|
||
if (cc == 3 && (pmsk & FIXOVR)) {
|
||
storepsw(OPPSW, IRC_FIXOVR);
|
||
}
|
||
dest = regs[reg1];
|
||
}
|
||
break;
|
||
|
||
case OP_SRDA:
|
||
if (reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
} else {
|
||
addr1 &= 0x3f;
|
||
cc = 0;
|
||
LDDBL(reg1, src1L);
|
||
src2L = src1L & MSIGNL;
|
||
while (addr1 > 0) {
|
||
src1L >>= 1;
|
||
src1L |= src2L;
|
||
addr1--;
|
||
}
|
||
goto save_dbl;
|
||
}
|
||
break;
|
||
|
||
case OP_STM:
|
||
reg &= 0xf;
|
||
for (;;) {
|
||
if (WriteFull(addr1, regs[reg1]))
|
||
goto supress;
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
}
|
||
break;
|
||
|
||
case OP_LM:
|
||
reg &= 0xf;
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
temp = reg1;
|
||
/* Find last location. */
|
||
addr2 = addr1;
|
||
for (;;) {
|
||
if (temp == reg)
|
||
break;
|
||
temp++;
|
||
temp &= 0xf;
|
||
addr2 += 4;
|
||
}
|
||
/* If we can acces this, we will fault on first */
|
||
if (TransAddr(addr2, &src1))
|
||
goto supress;
|
||
}
|
||
for (;;) {
|
||
if (ReadFull(addr1, ®s[reg1]))
|
||
goto supress;
|
||
per_mod |= 1 << reg1;
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
}
|
||
break;
|
||
|
||
case OP_STMC:
|
||
if (Q370) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (flags & PROBLEM) {
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else {
|
||
reg = R2(reg);
|
||
for (;;) {
|
||
dest = 0;
|
||
switch (reg1) {
|
||
case 0x0: /* Segment table address */
|
||
case 0x6: /* Masks */
|
||
case 0x4: /* Extended mask */
|
||
case 0x2: /* Translation execption */
|
||
dest = cregs[reg1];
|
||
break;
|
||
case 0x8: /* Partitioning register */
|
||
dest = 0x8c8c8c8c;
|
||
break;
|
||
case 0x9: /* Partitioning register */
|
||
/* Compute amount of memory and
|
||
assign in 256k blocks to CPU 1 */
|
||
dest = 0x8c8c0000;
|
||
break;
|
||
case 0xA: /* Partitioning register */
|
||
/* Address each 256k bank to 0-0xF */
|
||
dest = 0x02468ace;
|
||
break;
|
||
case 0xB: /* Partitioning register */
|
||
dest = 0x88000000;
|
||
break;
|
||
case 0xC: /* Partitioning register */
|
||
dest = 0xFFFFFFFF;
|
||
break;
|
||
case 0xD: /* Partitioning register */
|
||
dest = 0xFFFFFFFF;
|
||
break;
|
||
case 0xE: /* Partitioning register */
|
||
dest = 0x00000200;
|
||
break;
|
||
/* Return 0 */
|
||
case 0x1: /* Unassigned */
|
||
case 0x3: /* Unassigned */
|
||
case 0x5: /* Unassigned */
|
||
case 0x7: /* Unassigned */
|
||
case 0xF: /* Unassigned */
|
||
dest = 0;
|
||
break;
|
||
}
|
||
if (WriteFull(addr1, dest))
|
||
goto supress;
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case OP_LMC:
|
||
if (Q370) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (flags & PROBLEM) {
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else {
|
||
reg = R2(reg);
|
||
for (;;) {
|
||
if (ReadFull(addr1, &dest))
|
||
goto supress;
|
||
cregs[reg1] = dest;
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"Loading: CR %x %06x %08x IC=%08x %x\n",
|
||
reg1, addr1, dest, PC, reg);
|
||
switch (reg1) {
|
||
case 0x0: /* Segment table address */
|
||
for (addr2 = 0;
|
||
addr2 < sizeof(tlb)/sizeof(uint32);
|
||
addr2++)
|
||
tlb[addr2] = 0;
|
||
if ((dest & 0x3f) != 0)
|
||
storepsw(OPPSW, IRC_DATA);
|
||
seg_addr = dest & AMASK;
|
||
seg_len = (((dest >> 24) & 0xff) + 1) << 4;
|
||
break;
|
||
case 0x4: /* Extended mask */
|
||
cregs[reg] &= 0xfefe0000;
|
||
if (dest & 0xfe000000)
|
||
cregs[reg] |= 0x1000000;
|
||
if (dest & 0x00fe0000)
|
||
cregs[reg] |= 0x0010000;
|
||
if (ec_mode) {
|
||
if (irq_en) {
|
||
sysmsk = (dest >> 16) & 0xfe00;
|
||
sysmsk |= (dest >> 15) & 0x01ff;
|
||
} else {
|
||
sysmsk = 0;
|
||
}
|
||
irq_pend = 1;
|
||
}
|
||
break;
|
||
case 0x6: /* Masks */
|
||
ec_mode = (dest & 0x00800000) != 0;
|
||
if (ec_mode) {
|
||
if (irq_en) {
|
||
sysmsk = (cregs[4] >> 16) & 0xfe00;
|
||
sysmsk |= (cregs[4] >> 15) & 0x01ff;
|
||
} else {
|
||
sysmsk = 0;
|
||
}
|
||
irq_pend = 1;
|
||
}
|
||
cregs[reg] &= 0xf08000ff;
|
||
break;
|
||
case 0x2: /* Translation execption */
|
||
break;
|
||
case 0x1: /* Unassigned */
|
||
case 0x3: /* Unassigned */
|
||
case 0x5: /* Unassigned */
|
||
case 0x7: /* Unassigned */
|
||
case 0x8: /* Partitioning register */
|
||
case 0x9: /* Partitioning register */
|
||
case 0xA: /* Partitioning register */
|
||
case 0xB: /* Partitioning register */
|
||
case 0xC: /* Partitioning register */
|
||
case 0xD: /* Partitioning register */
|
||
case 0xE: /* Partitioning register */
|
||
case 0xF: /* Unassigned */
|
||
break;
|
||
}
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case OP_LRA:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (flags & PROBLEM) {
|
||
/* RX in RS range */
|
||
if (X2(reg) != 0)
|
||
addr1 += regs[X2(reg)];
|
||
|
||
/* Try to do quick LRA */
|
||
if (QVMA && vma_lra(R1(reg), addr1, &cc))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else {
|
||
uint32 seg;
|
||
uint32 page;
|
||
uint32 entry;
|
||
|
||
/* RX in RS range */
|
||
if (X2(reg) != 0)
|
||
addr1 += regs[X2(reg)];
|
||
|
||
addr1 &= AMASK;
|
||
/* Segment number to word address */
|
||
seg = (addr1 >> seg_shift) & seg_mask;
|
||
page = (addr1 >> page_shift) & page_index;
|
||
if (seg > seg_len) {
|
||
cc = 3;
|
||
regs[reg1] = addr1;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
}
|
||
addr2 = (((seg << 2) + seg_addr) & AMASK);
|
||
if (addr2 >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
goto supress;
|
||
}
|
||
entry = M[addr2 >> 2];
|
||
key[addr2 >> 11] |= 0x4;
|
||
/* Check if entry valid */
|
||
if (entry & PTE_VALID) {
|
||
cc = 1;
|
||
regs[reg1] = addr2;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
}
|
||
|
||
if (Q370) {
|
||
addr2 = (entry >> 28) + 1;
|
||
/* Check if over end of table */
|
||
if ((page >> pte_len_shift) >= addr2) {
|
||
cc = 3;
|
||
regs[reg1] = addr2;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
}
|
||
} else { /* IBM 360/67 */
|
||
addr2 = (entry >> 24);
|
||
/* Check if in correct length */
|
||
if (page > addr2) {
|
||
cc = 2;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Now we need to fetch the actual entry */
|
||
addr2 = ((entry & PTE_ADR) + (page << 1)) & AMASK;
|
||
if (addr2 >= MEMSIZE) {
|
||
storepsw(OPPSW, IRC_ADDR);
|
||
goto supress;
|
||
}
|
||
|
||
/* Fetch PTE entry */
|
||
entry = M[addr2 >> 2];
|
||
key[addr2 >> 11] |= 0x4;
|
||
entry >>= (addr2 & 2) ? 0 : 16;
|
||
entry &= 0xffff;
|
||
|
||
/* Check if entry valid */
|
||
if (entry & (pte_avail|pte_mbz)) {
|
||
cc = 2;
|
||
regs[reg1] = addr2;
|
||
per_mod |= 1 << reg1;
|
||
break;
|
||
}
|
||
|
||
/* Convert to address */
|
||
entry >>= pte_shift;
|
||
addr2 = ((addr1 & page_mask) | ((entry & TLB_PHY) << page_shift)) & AMASK;
|
||
cc = 0;
|
||
regs[reg1] = addr2;
|
||
per_mod |= 1 << reg1;
|
||
}
|
||
break;
|
||
|
||
case OP_MVCIN:
|
||
if (Q370) {
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1) ||
|
||
TransAddr(addr1+reg, &src1) ||
|
||
TransAddr(addr2, &src1) ||
|
||
TransAddr(addr2+reg, &src1)) {
|
||
goto supress;
|
||
}
|
||
}
|
||
addr2 += reg;
|
||
do {
|
||
if (ReadByte(addr2, &src1))
|
||
goto supress;
|
||
dest = src1;
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1++;
|
||
addr2--;
|
||
reg--;
|
||
} while (reg != 0xff);
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_NC:
|
||
case OP_OC:
|
||
case OP_XC:
|
||
case OP_MVN:
|
||
case OP_MVZ:
|
||
case OP_MVC:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1) || TransAddr(addr1+reg, &src1) ||
|
||
TransAddr(addr2, &src1) || TransAddr(addr2+reg, &src1)) {
|
||
goto supress;
|
||
}
|
||
}
|
||
|
||
if (op == OP_NC || op == OP_OC || op == OP_XC)
|
||
cc = 0;
|
||
do {
|
||
if (ReadByte(addr2, &src1))
|
||
goto supress;
|
||
if (op != OP_MVC) {
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
switch(op) {
|
||
case OP_MVZ: dest = (dest & 0x0f) | (src1 & 0xf0); break;
|
||
case OP_MVN: dest = (dest & 0xf0) | (src1 & 0x0f); break;
|
||
case OP_NC: dest &= src1; if (dest != 0) cc = 1; break;
|
||
case OP_OC: dest |= src1; if (dest != 0) cc = 1; break;
|
||
case OP_XC: dest ^= src1; if (dest != 0) cc = 1; break;
|
||
}
|
||
} else {
|
||
dest = src1;
|
||
}
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1++;
|
||
addr2++;
|
||
reg--;
|
||
} while (reg != 0xff);
|
||
break;
|
||
|
||
case OP_CLC:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1) || TransAddr(addr1+reg, &src1) ||
|
||
TransAddr(addr2, &src1) || TransAddr(addr2+reg, &src1)) {
|
||
goto supress;
|
||
}
|
||
}
|
||
cc = 0;
|
||
do {
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
if (ReadByte(addr2, &src2))
|
||
goto supress;
|
||
if (src1 != src2) {
|
||
if ((uint32)src1 > (uint32)src2)
|
||
cc = 2;
|
||
else
|
||
cc = 1;
|
||
break;
|
||
}
|
||
addr1++;
|
||
addr2++;
|
||
reg--;
|
||
} while(reg != 0xff);
|
||
break;
|
||
|
||
case OP_TR:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1) || TransAddr(addr1+reg, &src1) ||
|
||
TransAddr(addr2, &src1) || TransAddr(addr2+256, &src1)) {
|
||
goto supress;
|
||
}
|
||
}
|
||
do {
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
if (ReadByte(addr2 + (src1 & 0xff), &dest))
|
||
goto supress;
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1++;
|
||
reg--;
|
||
} while (reg != 0xff);
|
||
break;
|
||
|
||
case OP_TRT:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1) || TransAddr(addr1+reg, &src1) ||
|
||
TransAddr(addr2, &src1) || TransAddr(addr2+256, &src1)) {
|
||
goto supress;
|
||
}
|
||
}
|
||
cc = 0;
|
||
do {
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
if (ReadByte(addr2 + (src1 & 0xff), &dest))
|
||
goto supress;
|
||
if (dest != 0) {
|
||
regs[1] &= 0xff000000;
|
||
regs[1] |= addr1 & AMASK;
|
||
regs[2] &= 0xffffff00;
|
||
regs[2] |= dest & 0xff;
|
||
per_mod |= 6;
|
||
cc = (reg == 0) ? 2 : 1;
|
||
break;
|
||
}
|
||
addr1++;
|
||
reg--;
|
||
} while(reg != 0xff);
|
||
break;
|
||
|
||
case OP_PACK:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1))
|
||
goto supress;
|
||
if (TransAddr(addr2, &src1))
|
||
goto supress;
|
||
}
|
||
reg &= 0xf;
|
||
addr2 += reg;
|
||
addr1 += reg1;
|
||
/* Flip first location */
|
||
if (ReadByte(addr2, &dest))
|
||
goto supress;
|
||
dest = ((dest >> 4) & 0xf) | ((dest << 4) & 0xf0);
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1--;
|
||
addr2--;
|
||
dest = 0;
|
||
while(reg != 0 && reg1 != 0) {
|
||
if (ReadByte(addr2, &dest))
|
||
goto supress;
|
||
dest &= 0xf;
|
||
addr2--;
|
||
reg--;
|
||
if (reg != 0) {
|
||
if (ReadByte(addr2, &src1))
|
||
goto supress;
|
||
dest |= (src1 << 4) & 0xf0;
|
||
addr2--;
|
||
reg--;
|
||
}
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1--;
|
||
reg1--;
|
||
};
|
||
dest = 0;
|
||
while(reg1 != 0) {
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1--;
|
||
reg1--;
|
||
};
|
||
break;
|
||
|
||
case OP_UNPK:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1))
|
||
goto supress;
|
||
if (TransAddr(addr2, &src1))
|
||
goto supress;
|
||
}
|
||
reg &= 0xf;
|
||
addr2 += reg;
|
||
addr1 += reg1;
|
||
if (ReadByte(addr2, &dest))
|
||
goto supress;
|
||
dest = ((dest >> 4) & 0xf) | ((dest << 4) & 0xf0);
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1--;
|
||
addr2--;
|
||
zone = (flags & ASCII)? 0x50 : 0xf0;
|
||
while(reg != 0 && reg1 != 0) {
|
||
if (ReadByte(addr2, &dest))
|
||
goto supress;
|
||
addr2--;
|
||
reg--;
|
||
src1 = (dest & 0xf) | zone;
|
||
if (WriteByte(addr1, src1))
|
||
goto supress;
|
||
addr1--;
|
||
reg1--;
|
||
if (reg1 != 0) {
|
||
src1 = ((dest >> 4) & 0xf) | zone;
|
||
if (WriteByte(addr1, src1))
|
||
goto supress;
|
||
addr1--;
|
||
reg1--;
|
||
}
|
||
};
|
||
while(reg1 != 0) {
|
||
if (WriteByte(addr1, zone))
|
||
goto supress;
|
||
addr1--;
|
||
reg1--;
|
||
};
|
||
break;
|
||
|
||
/* Move with offset, packed odd shift */
|
||
case OP_MVO:
|
||
if ((cpu_unit[0].flags & FEAT_DAT) != 0) {
|
||
/* Make sure we can access whole area */
|
||
if (TransAddr(addr1, &src1))
|
||
goto supress;
|
||
if (TransAddr(addr2, &src1))
|
||
goto supress;
|
||
}
|
||
reg &= 0xf;
|
||
addr2 += reg;
|
||
addr1 += reg1;
|
||
if (ReadByte(addr1, &dest))
|
||
goto supress;
|
||
if (ReadByte(addr2, &src1))
|
||
goto supress;
|
||
addr2--;
|
||
dest = (dest & 0xf) | ((src1 << 4) & 0xf0);
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
addr1--;
|
||
while(reg1 != 0) {
|
||
dest = (src1 >> 4) & 0xf;
|
||
if (reg != 0) {
|
||
if (ReadByte(addr2, &src1))
|
||
goto supress;
|
||
addr2--;
|
||
reg--;
|
||
} else
|
||
src1 = 0;
|
||
dest |= (src1 << 4) & 0xf0;
|
||
if (WriteByte(addr1, dest))
|
||
goto supress;
|
||
reg1--;
|
||
addr1--;
|
||
};
|
||
break;
|
||
|
||
/* Convert packed decimal to binary */
|
||
case OP_CVB:
|
||
if (ReadFull(addr1, &src1))
|
||
goto supress;
|
||
if (ReadFull(addr1+4, &src1h))
|
||
goto supress;
|
||
fill = src1h & 0xf; /* Save away sign */
|
||
if (fill < 0xA) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
goto supress;
|
||
}
|
||
dest = 0;
|
||
/* Convert upper first */
|
||
for(temp = 28; temp >= 0; temp-=4) {
|
||
int d = (src1 >> temp) & 0xf;
|
||
if (d >= 0xA) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
goto supress;
|
||
}
|
||
dest = (dest * 10) + d;
|
||
}
|
||
/* Convert lower */
|
||
for(temp = 28; temp > 0; temp-=4) {
|
||
int d = (src1h >> temp) & 0xf;
|
||
if (d >= 0xA) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
goto supress;
|
||
}
|
||
dest = (dest * 10) + d;
|
||
}
|
||
/* Check if too big */
|
||
if (dest & MSIGN) {
|
||
storepsw(OPPSW, IRC_FIXDIV);
|
||
regs[reg1] = dest;
|
||
goto supress;
|
||
}
|
||
/* Twos compliment if needed */
|
||
if (fill == 0xB || fill == 0xD)
|
||
dest = NEG(dest);
|
||
regs[reg1] = dest;
|
||
break;
|
||
|
||
/* Convert binary to packed decimal */
|
||
case OP_CVD:
|
||
dest = regs[reg1];
|
||
src1 = 0;
|
||
src1h = 0;
|
||
if (dest & MSIGN) { /* Save sign */
|
||
dest = NEG(dest);
|
||
fill = 1;
|
||
} else
|
||
fill = 0;
|
||
temp = 4;
|
||
while (dest != 0) { /* Convert digits until zero */
|
||
int d = dest % 10;
|
||
dest /= 10;
|
||
if (temp >= 32)
|
||
src1 |= (d << (temp - 32));
|
||
else
|
||
src1h |= (d << temp);
|
||
temp += 4;
|
||
}
|
||
if (fill) { /* Set sign */
|
||
src1h |= ((flags & ASCII)? 0xb : 0xd);
|
||
} else {
|
||
src1h |= ((flags & ASCII)? 0xa : 0xc);
|
||
}
|
||
|
||
if (WriteFull(addr1, src1))
|
||
goto supress;
|
||
if (WriteFull(addr1+4, src1h))
|
||
goto supress;
|
||
break;
|
||
|
||
/* Edit string, mark saves address of significant digit */
|
||
case OP_ED:
|
||
case OP_EDMK:
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
zone = (flags & ASCII) ? 0x50: 0xf0;
|
||
fill = digit = (uint8)src1;
|
||
temp = 0; /* Hold zero flag */
|
||
e2 = 0; /* Significance indicator */
|
||
e1 = 1; /* Need another source char */
|
||
cc = 0;
|
||
for (;;) {
|
||
uint8 t;
|
||
switch(digit) {
|
||
case 0x21: /* Signficance starter */
|
||
case 0x20: /* Digit selector */
|
||
/* If we have not run out of source, grab next pair */
|
||
if (e1) {
|
||
if (ReadByte(addr2, &src2))
|
||
goto supress;
|
||
addr2++;
|
||
/* Check if valid */
|
||
if (src2 > 0xa0) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
goto supress;
|
||
}
|
||
}
|
||
/* Split apart. */
|
||
t = (src2 >> 4) & 0xf;
|
||
e1 = !e1;
|
||
src2 = (src2 & 0xf) << 4; /* Prepare for next trip */
|
||
/* Doing Edit and have seperator */
|
||
if (op == OP_EDMK && !e2 && t) {
|
||
regs[1] &= 0xff000000;
|
||
regs[1] |= addr1 & AMASK;
|
||
per_mod |= 2;
|
||
}
|
||
/* Found none zero */
|
||
if (t)
|
||
temp = 2; /* Set positive */
|
||
/* Select digit or fill */
|
||
if (t || e2)
|
||
digit = zone | t;
|
||
else
|
||
digit = fill;
|
||
if (src1 == 0x21 || t)
|
||
e2 = 1;
|
||
/* If sign, update status */
|
||
if (!e1) { /* Check if found sign */
|
||
switch(src2) {
|
||
case 0xa0: /* Minus */
|
||
case 0xc0:
|
||
case 0xe0:
|
||
case 0xf0:
|
||
e2 = 0;
|
||
/* Fall through */
|
||
case 0xb0:
|
||
case 0xd0:
|
||
e1 = 1;
|
||
}
|
||
}
|
||
break;
|
||
case 0x22: /* Field separator */
|
||
e2 = 0;
|
||
digit = fill;
|
||
temp = 0; /* Set zero */
|
||
break;
|
||
default: /* Anything else */
|
||
if (!e2)
|
||
digit = fill;
|
||
}
|
||
if (WriteByte(addr1, digit))
|
||
goto supress;
|
||
addr1++;
|
||
if (reg == 0)
|
||
break;
|
||
reg --;
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
digit = src1;
|
||
}
|
||
cc = temp;
|
||
if (e2 && cc == 2)
|
||
cc = 1;
|
||
break;
|
||
|
||
/* Execute instruction at address with R1 modify */
|
||
case OP_EX:
|
||
if (addr1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
if (ReadHalf(addr1, &dest))
|
||
goto supress;
|
||
ops[0] = dest;
|
||
if (reg1) {
|
||
ops[0] |= src1 & 0xff;
|
||
}
|
||
reg = ops[0] & 0xff;
|
||
reg1 = R1(reg);
|
||
op = ops[0] >> 8;
|
||
if (hst_lnt) {
|
||
hst[hst_p].cc = cc;
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt)
|
||
hst_p = 0;
|
||
hst[hst_p].pc = addr1 | HIST_PC;
|
||
hst[hst_p].inst[0] = ops[0];
|
||
}
|
||
addr1 += 2;
|
||
if (op == OP_EX)
|
||
storepsw(OPPSW, IRC_EXEC);
|
||
/* Check if RX, RR, SI, RS, SS opcode */
|
||
else {
|
||
if ((op & 0xc0) != 0) {
|
||
if (ReadHalf(addr1, &dest))
|
||
goto supress;
|
||
ops[1] = dest;
|
||
addr1+=2;
|
||
/* Check if SS */
|
||
if ((op & 0xc0) == 0xc0) {
|
||
if(ReadHalf(addr1, &dest))
|
||
goto supress;
|
||
ops[2] = dest;
|
||
}
|
||
if (hst_lnt) {
|
||
hst[hst_p].inst[1] = ops[1];
|
||
hst[hst_p].inst[2] = ops[2];
|
||
}
|
||
}
|
||
goto opr;
|
||
}
|
||
break;
|
||
|
||
/* 370 specific instructions */
|
||
case OP_ICM:
|
||
if (Q370) {
|
||
int i, j;
|
||
|
||
/* Fetch register */
|
||
dest = regs[reg1];
|
||
/* If no bits, just read byte and trap if needed */
|
||
if (R2(reg) == 0) {
|
||
if(ReadByte(addr1, &src1))
|
||
goto supress;
|
||
cc = 0;
|
||
break;
|
||
}
|
||
/* Set flag to check first bit */
|
||
fill = 0x80;
|
||
digit = 0;
|
||
/* Scan from Bit 12 to bit 15 */
|
||
for (i = 0x8, j=24; i != 0; i >>= 1, j-=8) {
|
||
/* If the bit is one, read in byte */
|
||
if ((R2(reg) & i) != 0) {
|
||
if(ReadByte(addr1, &src1))
|
||
goto supress;
|
||
addr1++;
|
||
/* Make mask */
|
||
src2 = 0xff << j;
|
||
/* Put byte into place */
|
||
dest = (dest & ~src2) | ((src1 & 0xff) << j);
|
||
/* If byte not zero, compute new CC */
|
||
if (src1 != 0) {
|
||
if ((src1 & fill) != 0)
|
||
digit = 1;
|
||
else if (digit == 0)
|
||
digit = 2;
|
||
}
|
||
fill = 0;
|
||
}
|
||
}
|
||
regs[reg1] = dest;
|
||
cc = digit;
|
||
per_mod |= 1 << reg1;
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_STCM:
|
||
if (Q370) {
|
||
uint32 rval[4];
|
||
int i, k = 0;
|
||
|
||
dest = regs[reg1];
|
||
if ((R2(reg) & 0x8) != 0) rval[k++] = (dest >> 24) & 0xff;
|
||
if ((R2(reg) & 0x4) != 0) rval[k++] = (dest >> 16) & 0xff;
|
||
if ((R2(reg) & 0x2) != 0) rval[k++] = (dest >> 8) & 0xff;
|
||
if ((R2(reg) & 0x1) != 0) rval[k++] = dest & 0xff;
|
||
for (i = 0; i < k; i++) {
|
||
if(WriteByte(addr1, rval[i]))
|
||
goto supress;
|
||
addr1++;
|
||
}
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_CLM:
|
||
if (Q370) {
|
||
uint8 rval[4];
|
||
int i, k = 0;
|
||
|
||
cc = 0;
|
||
if (R2(reg) == 0) {
|
||
if(ReadByte(addr1, &src1))
|
||
goto supress;
|
||
break;
|
||
}
|
||
dest = regs[reg1];
|
||
if ((R2(reg) & 0x8) != 0) rval[k++] = (uint8)((dest >> 24) & 0xff);
|
||
if ((R2(reg) & 0x4) != 0) rval[k++] = (uint8)((dest >> 16) & 0xff);
|
||
if ((R2(reg) & 0x2) != 0) rval[k++] = (uint8)((dest >> 8) & 0xff);
|
||
if ((R2(reg) & 0x1) != 0) rval[k++] = (uint8)(dest & 0xff);
|
||
for (i = 0; i < k; i++) {
|
||
uint8 m;
|
||
if (ReadByte(addr1, &src1))
|
||
goto supress;
|
||
addr1++;
|
||
m = (uint8)(src1 & 0xff);
|
||
if (rval[i] != m) {
|
||
if (rval[i] > m)
|
||
cc = 2;
|
||
else
|
||
cc = 1;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_370:
|
||
if (Q370) {
|
||
if (reg > 0x13) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if (reg != 5 && flags & PROBLEM) {
|
||
/* Try to do quick IPK */
|
||
if (QVMA && vma_370(reg, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
}
|
||
switch(reg) {
|
||
case 0x0: /* CONCS */
|
||
/* Connect channel set */
|
||
case 0x1: /* DISCS */
|
||
/* Disconnect channel set */
|
||
cc = 3;
|
||
break;
|
||
|
||
case 0x2: /* STIDP */
|
||
/* Store CPUID in double word */
|
||
dest = 100;
|
||
if (WriteFull(addr1, dest))
|
||
goto supress;
|
||
dest = 0x145 << 16;
|
||
if (WriteFull(addr1 + 4, dest))
|
||
goto supress;
|
||
break;
|
||
|
||
case 0x3: /* STIDC */
|
||
/* Store channel id */
|
||
src1 = (addr1 >> 8) & 0xff;
|
||
if (src1 > MAX_CHAN) {
|
||
cc = 3;
|
||
break;
|
||
}
|
||
dest = 0;
|
||
if ((chan_unit[src1].flags & UNIT_M_CTYPE) == UNIT_MUX) {
|
||
dest = 0x10000000;
|
||
}
|
||
if ((chan_unit[src1].flags & UNIT_M_CTYPE) == UNIT_BMUX) {
|
||
dest = 0x20000000;
|
||
}
|
||
addr1 = 0xA8;
|
||
if (WriteFull(addr1, dest))
|
||
goto supress;
|
||
cc = 0;
|
||
break;
|
||
|
||
case 0x4: /* SCK */
|
||
/* Load clock with double word */
|
||
if (ReadFull(addr1, &src1))
|
||
goto supress;
|
||
if (ReadFull(addr1 + 4, &src2))
|
||
goto supress;
|
||
tod_clock[0] = src1;
|
||
tod_clock[1] = src2;
|
||
clk_state = CLOCK_SET;
|
||
check_tod_irq();
|
||
cc = 0;
|
||
break;
|
||
case 0x5: /* STCK */
|
||
/* Store TOD clock in location */
|
||
src1 = tod_clock[0];
|
||
src1h = tod_clock[1];
|
||
if (clk_state && sim_is_active(&cpu_unit[0])) {
|
||
double us = (1000000.0/(double)rtc_tps);
|
||
us -= sim_activate_time_usecs(&cpu_unit[0]);
|
||
dest = src1h + (((int)us) << 12);
|
||
if (dest < src1h)
|
||
src1++;
|
||
src1h = dest;
|
||
}
|
||
src1h &= ~0xfff;
|
||
if (WriteFull(addr1, src1))
|
||
goto supress;
|
||
if (WriteFull(addr1+4, src1h))
|
||
goto supress;
|
||
cc = (clk_state == CLOCK_UNSET);
|
||
break;
|
||
case 0x6: /* SCKC */
|
||
/* Load clock compare with double word */
|
||
if (ReadFull(addr1, &src1))
|
||
goto supress;
|
||
if (ReadFull(addr1+4, &src1h))
|
||
goto supress;
|
||
clk_cmp[0] = src1;
|
||
clk_cmp[1] = src1h;
|
||
check_tod_irq();
|
||
break;
|
||
case 0x7: /* STCKC */
|
||
/* Store clock compare in double word */
|
||
if (WriteFull(addr1, clk_cmp[0]))
|
||
goto supress;
|
||
if (WriteFull(addr1+4, clk_cmp[1]))
|
||
goto supress;
|
||
break;
|
||
case 0x8: /* SPT */
|
||
/* Set the CPU timer with double word */
|
||
if (ReadFull(addr1, &src1))
|
||
goto supress;
|
||
if (ReadFull(addr1+4, &src1h))
|
||
goto supress;
|
||
cpu_timer[0] = src1;
|
||
cpu_timer[1] = src1h;
|
||
if (sim_is_active(&cpu_unit[0])) {
|
||
double nus = sim_activate_time_usecs(&cpu_unit[0]);
|
||
timer_tics = (int)(nus);
|
||
}
|
||
clk_irq = (cpu_timer[0] & MSIGN) != 0;
|
||
break;
|
||
case 0x9: /* STPT */
|
||
/* Store the CPU timer in double word */
|
||
src1 = cpu_timer[0];
|
||
src1h = cpu_timer[1];
|
||
if (sim_is_active(&cpu_unit[0])) {
|
||
double nus = sim_activate_time_usecs(&cpu_unit[0]);
|
||
int tics = (int)(timer_tics - nus) ;
|
||
dest = src1h - (tics << 12);
|
||
if (dest > src1h) {
|
||
src1--;
|
||
}
|
||
src1h = dest;
|
||
}
|
||
if (WriteFull(addr1, src1))
|
||
goto supress;
|
||
if (WriteFull(addr1+4, src1h))
|
||
goto supress;
|
||
break;
|
||
case 0xa: /* SPKA */
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
st_key = 0xf0 & addr1;
|
||
break;
|
||
case 0xb: /* IPK */
|
||
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
regs[2] = (regs[2] & 0xffffff00) | (st_key & 0xf0);
|
||
per_mod |= 1 << 2;
|
||
break;
|
||
case 0xd: /* PTLB */
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
for (addr2 = 0; addr2 < sizeof(tlb)/sizeof(uint32); addr2++)
|
||
tlb[addr2] = 0;
|
||
break;
|
||
case 0x10: /* SPX */
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
case 0x11: /* STPX */
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
case 0x12: /* STAP */
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
case 0x13: /* RRB */
|
||
/* Set storage block reference bit to zero */
|
||
addr1 &= AMASK;
|
||
addr1 >>= 11;
|
||
dest = key[addr1];
|
||
key[addr1] &= 0xfb; /* Clear reference bit */
|
||
cc = (dest >> 1) & 03;
|
||
break;
|
||
default:
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_STNSM:
|
||
if (Q370) {
|
||
if (flags & PROBLEM) {
|
||
/* Try to do quick STNSM */
|
||
if (QVMA && vma_stnsm(reg, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
}
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
src1 = 0;
|
||
if (ec_mode) {
|
||
if (dat_en)
|
||
src1 |= 0x4;
|
||
if (irq_en)
|
||
src1 |= 0x2;
|
||
if (per_en)
|
||
src1 |= 0x40;
|
||
if (ext_en)
|
||
src1 |= 0x1;
|
||
} else {
|
||
src1 = ((sysmsk >> 8) & 0xfe) | ext_en;
|
||
}
|
||
dest = reg & src1;
|
||
if (WriteByte(addr1, src1))
|
||
break;
|
||
if (ec_mode) {
|
||
dat_en = ((dest & 0x4) != 0);
|
||
irq_en = ((dest & 0x2) != 0);
|
||
per_en = ((dest & 0x40) != 0);
|
||
sysmsk = irq_en ? cregs[2] >> 16 : 0;
|
||
} else {
|
||
sysmsk = (dest << 8) & 0xfc00;
|
||
if (dest & 0x2)
|
||
sysmsk |= (cregs[2] >> 16) & 0x3ff;
|
||
irq_en = (sysmsk != 0);
|
||
}
|
||
irq_pend = 1;
|
||
ext_en = (dest & 1);
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_STOSM:
|
||
if (Q370) {
|
||
if (flags & PROBLEM) {
|
||
/* Try to do quick STOSM */
|
||
if (QVMA && vma_stosm(reg, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
}
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
src1 = 0;
|
||
if (ec_mode) {
|
||
if (dat_en)
|
||
src1 |= 0x4;
|
||
if (irq_en)
|
||
src1 |= 0x2;
|
||
if (per_en)
|
||
src1 |= 0x40;
|
||
if (ext_en)
|
||
src1 |= 0x1;
|
||
} else {
|
||
src1 = ((sysmsk >> 8) & 0xfe) | ext_en;
|
||
}
|
||
dest = reg | src1;
|
||
if (WriteByte(addr1, src1))
|
||
break;
|
||
ext_en = (dest & 1);
|
||
irq_pend = 1;
|
||
if (ec_mode) {
|
||
dat_en = ((dest & 0x4) != 0);
|
||
irq_en = ((dest & 0x2) != 0);
|
||
per_en = ((dest & 0x40) != 0);
|
||
sysmsk = irq_en ? cregs[2] >> 16 : 0;
|
||
if ((dest & 0xb8) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
} else {
|
||
sysmsk = (dest << 8) & 0xfc00;
|
||
if (dest & 0x2)
|
||
sysmsk |= (cregs[2] >> 16) & 0x3ff;
|
||
irq_en = (sysmsk != 0);
|
||
}
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_SIGP:
|
||
if (Q370) {
|
||
if (flags & PROBLEM) {
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
goto supress;
|
||
}
|
||
}
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
|
||
case OP_MC:
|
||
if (Q370) {
|
||
if ((reg & 0xf0) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
if ((cregs[8] & (1 << reg)) != 0) {
|
||
src1 = reg;
|
||
addr2 = (0x94 >> 2);
|
||
M[addr2] &= 0xffff;
|
||
M[addr2] |= src1 << 16;
|
||
M[0x9C >> 2] = addr1;
|
||
key[0] |= 0x6;
|
||
storepsw(OPPSW, IRC_MCE);
|
||
goto supress;
|
||
}
|
||
} else {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
break;
|
||
|
||
case OP_LCTL:
|
||
temp = 0;
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (flags & PROBLEM) {
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else {
|
||
reg = R2(reg);
|
||
for (;;) {
|
||
if (ReadFull(addr1, &dest))
|
||
goto supress;
|
||
cregs[reg1] = dest;
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"Loading: CR %x %06x %08x IC=%08x %x\n",
|
||
reg1, addr1, dest, PC, reg);
|
||
switch (reg1) {
|
||
case 0x0: /* General control register */
|
||
/* CR0 values
|
||
| | | | | | | |
|
||
0 0 0 00000 00 1 11 111 1111222222222231|
|
||
0 1 2 34567 89 0 12 345 6789012345678901|
|
||
b s t xxxxx ps 0 ss xxx iiiiiixxiiixxxxx|
|
||
m s d mmmmct iIE |
|
||
*/
|
||
page_shift = 0;
|
||
seg_shift = 0;
|
||
switch((dest >> 22) & 03) {
|
||
default: /* Generate translation exception */
|
||
case 1: /* 2K pages */
|
||
page_shift = 11;
|
||
page_mask = 0x7ff;
|
||
pte_avail = 0x4;
|
||
pte_mbz = 0x2;
|
||
pte_shift = 3;
|
||
pte_len_shift = 1;
|
||
break;
|
||
case 2: /* 4K pages */
|
||
page_shift = 12;
|
||
page_mask = 0xfff;
|
||
pte_avail = 0x8;
|
||
pte_mbz = 0x6;
|
||
pte_shift = 4;
|
||
pte_len_shift = 0;
|
||
break;
|
||
}
|
||
switch((dest >> 19) & 07) {
|
||
default: /* Generate translation exception */
|
||
case 0: /* 64K segments */
|
||
seg_shift = 16;
|
||
seg_mask = AMASK >> 16;
|
||
break;
|
||
case 2: /* 1M segments */
|
||
seg_shift = 20;
|
||
seg_mask = AMASK >> 20;
|
||
pte_len_shift += 4;
|
||
break;
|
||
}
|
||
/* Generate pte index mask */
|
||
page_index = ((~(seg_mask << seg_shift) &
|
||
~page_mask) & AMASK) >> page_shift;
|
||
intval_en = ((dest & 0x400) != 0);
|
||
tod_en = ((dest & 0x800) != 0);
|
||
// temp = 1;
|
||
break;
|
||
case 0x1: /* Segment table address and length */
|
||
temp = 1;
|
||
seg_addr = dest & AMASK;
|
||
seg_len = (((dest >> 24) & 0xff) + 1) << 4;
|
||
break;
|
||
case 0x2: /* Masks */
|
||
if (ec_mode)
|
||
sysmsk = irq_en ? (dest >> 16) : 0;
|
||
irq_pend = 1;
|
||
break;
|
||
case 0x6: /* Assist function */
|
||
sim_debug(DEBUG_VMA, &cpu_dev, "set CR6 %08x\n", dest);
|
||
break;
|
||
case 0x3: /* Unassigned */
|
||
case 0x4: /* Unassigned */
|
||
case 0x5: /* Unassigned */
|
||
case 0x7: /* Unassigned */
|
||
case 0x8: /* Monitor mask register */
|
||
case 0x9: /* PER register */
|
||
case 0xA: /* PER register */
|
||
case 0xB: /* PER register */
|
||
case 0xC: /* Partitioning register */
|
||
case 0xD: /* Partitioning register */
|
||
case 0xE: /* Partitioning register */
|
||
case 0xF: /* Unassigned */
|
||
break;
|
||
}
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
};
|
||
/* Purge TLB if segment pointer updated */
|
||
if (temp) {
|
||
for (addr2 = 0; addr2 < sizeof(tlb)/sizeof(uint32); addr2++)
|
||
tlb[addr2] = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case OP_STCTL:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if ((cpu_unit[0].flags & FEAT_DAT) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (flags & PROBLEM) {
|
||
/* Try to do quick STCLT */
|
||
if (QVMA && vma_stctl(reg, addr1))
|
||
break;
|
||
storepsw(OPPSW, IRC_PRIV);
|
||
} else {
|
||
reg = R2(reg);
|
||
for (;;) {
|
||
if (WriteFull(addr1, cregs[reg1]))
|
||
goto supress;
|
||
if (reg1 == reg)
|
||
break;
|
||
reg1++;
|
||
reg1 &= 0xf;
|
||
addr1 += 4;
|
||
} ;
|
||
}
|
||
break;
|
||
|
||
case OP_CS:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if ((addr1 & 0x3) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
} else {
|
||
if (ReadFull(addr1, &src2))
|
||
goto supress;
|
||
src1 = regs[reg1];
|
||
dest = regs[R2(reg)];
|
||
if (src1 == src2) {
|
||
if (WriteFull(addr1, dest))
|
||
goto supress;
|
||
cc = 0;
|
||
} else {
|
||
regs[reg1] = src2;
|
||
per_mod |= 1 << reg1;
|
||
cc = 1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case OP_CDS:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if ((addr1 & 0x7) != 0 || (reg1 & 1) != 0 || (reg & 1) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
} else {
|
||
if (ReadFull(addr1, &src2))
|
||
goto supress;
|
||
if (ReadFull(addr1+4, &src2h))
|
||
goto supress;
|
||
src1 = regs[reg1];
|
||
src1h = regs[reg1|1];
|
||
dest = regs[R2(reg)];
|
||
desth = regs[R2(reg)|1];
|
||
if (src1 == src2 && src1h == src2h) {
|
||
if (WriteFull(addr1, dest))
|
||
goto supress;
|
||
if (WriteFull(addr1+4, desth))
|
||
goto supress;
|
||
cc = 0;
|
||
} else {
|
||
regs[reg1] = src2;
|
||
regs[reg1|1] = src2h;
|
||
per_mod |= 3 << reg1;
|
||
cc = 1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case OP_SRP:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
} else if ((cpu_unit[0].flags & FEAT_DEC) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
} else {
|
||
dec_srp(op, addr1, reg1, addr2, reg & 0xf);
|
||
}
|
||
break;
|
||
|
||
case OP_MVCL:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (reg & 1 || reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
} else {
|
||
addr1 = regs[reg1] & AMASK;
|
||
src1 = regs[reg1|1] & AMASK;
|
||
addr2 = regs[R2(reg)] & AMASK;
|
||
src2 = regs[R2(reg)|1];
|
||
fill = (src2 >> 24) & 0xff;
|
||
src2 &= AMASK;
|
||
|
||
if (src1 > 1 && src2 > 1) {
|
||
dest = addr2 + ((src2 < src1) ? src2 : src1) - 1;
|
||
dest &= AMASK;
|
||
if ((dest > addr2 && (addr1 > addr2 && addr1 <= dest)) ||
|
||
(dest <= addr2 && (addr1 > addr2 || addr1 <= dest))) {
|
||
cc = 3;
|
||
break;
|
||
}
|
||
}
|
||
if (src1 == 0)
|
||
cc = (src2 == 0) ? 0 : 2;
|
||
else
|
||
cc = (src1 == src2) ? 0 : 1;
|
||
while (src1 != 0) {
|
||
if (src2 == 0) {
|
||
desth = fill;
|
||
} else {
|
||
if (ReadByte(addr2, &desth))
|
||
break;
|
||
}
|
||
if (WriteByte(addr1, desth))
|
||
break;
|
||
if (src2 != 0) {
|
||
addr2 ++;
|
||
addr2 &= AMASK;
|
||
src2 --;
|
||
}
|
||
if (src1 != 0) {
|
||
addr1 ++;
|
||
addr1 &= AMASK;
|
||
src1 --;
|
||
}
|
||
}
|
||
regs[reg1] = addr1 & AMASK;
|
||
regs[reg1|1] &= ~AMASK;
|
||
regs[reg1|1] |= src1 & AMASK;
|
||
regs[R2(reg)] = addr2 & AMASK;
|
||
regs[R2(reg)|1] &= ~AMASK;
|
||
regs[R2(reg)|1] |= src2 & AMASK;
|
||
per_mod |= 3 << reg1;
|
||
per_mod |= 3 << R2(reg);
|
||
if (src1 == 0 && src2 != 0)
|
||
cc = 1;
|
||
}
|
||
break;
|
||
|
||
case OP_CLCL:
|
||
if (Q360) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
} else if (reg & 1 || reg1 & 1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
} else {
|
||
addr1 = regs[reg1] & AMASK;
|
||
src1 = regs[reg1|1] & AMASK;
|
||
addr2 = regs[R2(reg)] & AMASK;
|
||
src2 = regs[R2(reg)|1];
|
||
fill = (src2 >> 24) & 0xff;
|
||
src2 &= AMASK;
|
||
cc = 0;
|
||
while (src1 != 0 || src2 != 0) {
|
||
if (src1 == 0) {
|
||
dest = fill;
|
||
} else {
|
||
if (ReadByte(addr1, &dest))
|
||
break;
|
||
}
|
||
if (src2 == 0) {
|
||
desth = fill;
|
||
} else {
|
||
if (ReadByte(addr2, &desth))
|
||
break;
|
||
}
|
||
if (dest != desth) {
|
||
if ((uint32)dest > (uint32)desth)
|
||
cc = 2;
|
||
else
|
||
cc = 1;
|
||
break;
|
||
}
|
||
if (src1 != 0) {
|
||
addr1 ++;
|
||
addr1 &= AMASK;
|
||
src1 --;
|
||
}
|
||
if (src2 != 0) {
|
||
addr2 ++;
|
||
addr2 &= AMASK;
|
||
src2 --;
|
||
}
|
||
}
|
||
regs[reg1] = addr1 & AMASK;
|
||
regs[reg1|1] &= ~AMASK;
|
||
regs[reg1|1] |= src1 & AMASK;
|
||
regs[R2(reg)] = addr2 & AMASK;
|
||
regs[R2(reg)|1] &= ~AMASK;
|
||
regs[R2(reg)|1] |= src2 & AMASK;
|
||
per_mod |= 3 << reg1;
|
||
per_mod |= 3 << R2(reg);
|
||
}
|
||
break;
|
||
|
||
/* Floating Half register */
|
||
case OP_HDR:
|
||
case OP_HER:
|
||
/* Split number apart */
|
||
e1 = (src2L & EMASKL) >> 56;
|
||
destL = src2L & MSIGNL;
|
||
/* Create guard digit */
|
||
src2L = (src2L & MMASKL) << 4;
|
||
/* Divide by 2 */
|
||
src2L >>= 1;
|
||
/* If not zero, normalize result */
|
||
if (src2L != 0) {
|
||
while((src2L & SNMASKL) == 0) {
|
||
src2L <<= 4;
|
||
e1--;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
destL = e1 = 0;
|
||
}
|
||
}
|
||
/* Remove guard digit */
|
||
src2L >>= 4;
|
||
}
|
||
|
||
/* Check for zero */
|
||
if (src2L == 0)
|
||
destL = e1 = 0;
|
||
|
||
/* Restore result */
|
||
src2L |= (((t_uint64)e1 << 56) & EMASKL) | destL;
|
||
|
||
/* Fall through */
|
||
|
||
/* Floating Load register */
|
||
case OP_LER:
|
||
case OP_LDR:
|
||
case OP_LE:
|
||
case OP_LD:
|
||
if ((op & 0x10) == 0) {
|
||
fpregs[reg1] = src2L;
|
||
} else {
|
||
fpregs[reg1] = (src2L & HMASKL) | (fpregs[reg1] & LMASKL);
|
||
}
|
||
break;
|
||
|
||
/* Floating Load register change sign */
|
||
case OP_LPDR:
|
||
case OP_LNDR:
|
||
case OP_LTDR:
|
||
case OP_LCDR:
|
||
case OP_LPER:
|
||
case OP_LNER:
|
||
case OP_LTER:
|
||
case OP_LCER:
|
||
if ((op & 0x2) == 0) /* LP, LN */
|
||
src2L &= ~MSIGNL;
|
||
if ((op & 0x1)) /* LN, LC */
|
||
src2L ^= MSIGNL;
|
||
cc = 0;
|
||
src1L = src2L & (~MSIGNL);
|
||
if ((op & 0x10) == 0) {
|
||
fpregs[reg1] = src2L;
|
||
} else {
|
||
fpregs[reg1] = (src2L & HMASKL) | (fpregs[reg1] & LMASKL);
|
||
}
|
||
if (src1L != 0)
|
||
cc = (src2L & MSIGNL) ? 1 : 2;
|
||
break;
|
||
|
||
/* Floating Store register */
|
||
case OP_STD:
|
||
if ((addr1 & 0x3) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
src1h = (uint32)(src1L & LMASKL);
|
||
if (WriteFull(addr1 + 4, src1h))
|
||
break;
|
||
/* Fall through */
|
||
|
||
case OP_STE:
|
||
src1 = (uint32)((src1L >> 32) & LMASKL);
|
||
WriteFull(addr1, src1);
|
||
break;
|
||
|
||
/* Floating Compare */
|
||
case OP_CE: /* 79 */
|
||
case OP_CER: /* 39 */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src1L & MSIGNL)
|
||
fill |= 2;
|
||
if (src2L & MSIGNL)
|
||
fill |= 1;
|
||
/* Make 32 bit and create guard digit */
|
||
src1 = (uint32)((src1L >> 28) & XMASK);
|
||
src2 = (uint32)((src2L >> 28) & XMASK);
|
||
temp = e1 - e2;
|
||
if (temp > 0) {
|
||
if (temp > 8) {
|
||
src2 = 0;
|
||
} else {
|
||
/* Shift src2 right if src1 larger expo - expo */
|
||
src2 >>= 4 * temp;
|
||
}
|
||
} else if (temp < 0) {
|
||
if (temp < -8) {
|
||
src1 = 0;
|
||
} else {
|
||
/* Shift src1 right if src2 larger expo - expo */
|
||
src1 >>= 4 * -temp;
|
||
}
|
||
}
|
||
|
||
/* Exponents should be equal now. */
|
||
|
||
/* Subtract results */
|
||
if (fill == 3 || fill == 0) {
|
||
/* Same signs do subtract */
|
||
src2 ^= XMASK;
|
||
dest = src1 + src2 + 1;
|
||
if (dest & CMASK)
|
||
dest &= XMASK;
|
||
else {
|
||
fill ^= 2;
|
||
dest ^= XMASK;
|
||
dest++;
|
||
}
|
||
} else {
|
||
dest = src1 + src2;
|
||
}
|
||
/* If src1 not normal shift left + expo */
|
||
if (dest & CMASK)
|
||
dest >>= 4;
|
||
|
||
/* Set condition codes */
|
||
cc = 0;
|
||
if (dest != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
break;
|
||
|
||
/* Floating Subtract */
|
||
case OP_SE: /* 7B */
|
||
case OP_SU: /* 7F */
|
||
case OP_SER: /* 3B */
|
||
case OP_SUR: /* 3F */
|
||
src2L ^= MSIGNL;
|
||
/* Fall through */
|
||
|
||
/* Floating Add */
|
||
case OP_AE: /* 7A */
|
||
case OP_AU: /* 7E */
|
||
case OP_AER: /* 3A */
|
||
case OP_AUR: /* 3E */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src1L & MSIGNL)
|
||
fill |= 2;
|
||
if (src2L & MSIGNL)
|
||
fill |= 1;
|
||
/* Make 32 bit and create guard digit */
|
||
src1 = (uint32)((src1L >> 28) & (MMASK << 4));
|
||
src2 = (uint32)((src2L >> 28) & (MMASK << 4));
|
||
temp = e1 - e2;
|
||
if (temp > 0) {
|
||
if (temp > 8) {
|
||
src2 = 0;
|
||
} else {
|
||
/* Shift src2 right if src1 larger expo - expo */
|
||
src2 >>= 4 * temp;
|
||
}
|
||
} else if (temp < 0) {
|
||
if (temp < -8) {
|
||
src1 = 0;
|
||
} else {
|
||
/* Shift src1 right if src2 larger expo - expo */
|
||
src1 >>= 4 * -temp;
|
||
}
|
||
e1 = e2;
|
||
}
|
||
|
||
/* Exponents should be equal now. */
|
||
|
||
/* Add results */
|
||
if (fill == 2 || fill == 1) {
|
||
/* Different signs do subtract */
|
||
src2 ^= XMASK;
|
||
dest = src1 + src2 + 1;
|
||
if (dest & CMASK)
|
||
dest &= XMASK;
|
||
else {
|
||
fill ^= 2;
|
||
dest ^= XMASK;
|
||
dest++;
|
||
}
|
||
} else {
|
||
dest = src1 + src2;
|
||
}
|
||
/* If overflow, shift right 4 bits */
|
||
if (dest & CMASK) {
|
||
dest >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
|
||
/* Set condition codes */
|
||
cc = 0;
|
||
if ((op & 0xE) != 0xE) {
|
||
if (dest != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
else
|
||
dest = e1 = fill = 0;
|
||
} else {
|
||
if ((dest & 0xffffff0) != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
else
|
||
dest = e1 = fill = 0;
|
||
}
|
||
|
||
/* Check signifigance exception */
|
||
if (cc == 0 && pmsk & SIGMSK) {
|
||
storepsw(OPPSW, IRC_SIGNIF);
|
||
goto fpstore1;
|
||
}
|
||
|
||
/* Check if we are normalized addition */
|
||
if ((op & 0xE) != 0xE) {
|
||
if (cc != 0) { /* Only if non-zero result */
|
||
while ((dest & SNMASK) == 0) {
|
||
dest <<= 4;
|
||
e1 --;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
dest = 0;
|
||
fill = e1 = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Remove DP Guard bit */
|
||
dest >>= 4;
|
||
|
||
fpstore1:
|
||
/* Store result */
|
||
dest |= (e1 << 24) & EMASK;
|
||
if (cc != 0 && fill & 2)
|
||
dest |= MSIGN;
|
||
fpregs[reg1] = ((t_uint64)dest << 32) | (fpregs[reg1] & LMASKL);
|
||
break;
|
||
|
||
/* Floating Compare */
|
||
case OP_CD: /* 69 */
|
||
case OP_CDR: /* 29 */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src1L & MSIGNL)
|
||
fill |= 2;
|
||
if (src2L & MSIGNL)
|
||
fill |= 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
temp = e1 - e2;
|
||
src1L <<= 4;
|
||
src2L <<= 4;
|
||
if (temp > 0) {
|
||
if (temp > 17) {
|
||
src2L = 0;
|
||
} else {
|
||
/* Shift src2 right if src1 larger expo - expo */
|
||
src2L >>= 4 * temp;
|
||
}
|
||
} else if (temp < 0) {
|
||
if (temp < -17) {
|
||
src1L = 0;
|
||
} else {
|
||
/* Shift src1 right if src2 larger expo - expo */
|
||
src1L >>= 4 * -temp;
|
||
}
|
||
e1 = e2;
|
||
}
|
||
|
||
/* Exponents should be equal now. */
|
||
|
||
/* Add results */
|
||
if (fill == 3 || fill == 0) {
|
||
/* Different signs do subtract */
|
||
src2L ^= XMASKL;
|
||
destL = src1L + src2L + 1;
|
||
if (destL & CMASKL)
|
||
destL &= XMASKL;
|
||
else {
|
||
fill ^= 2;
|
||
destL ^= XMASKL;
|
||
destL ++;
|
||
}
|
||
} else {
|
||
destL = src1L + src2L;
|
||
}
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & CMASKL) {
|
||
destL >>= 4;
|
||
}
|
||
cc = 0;
|
||
if (destL != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
break;
|
||
|
||
/* Floating Subtract */
|
||
case OP_SD: /* 6B */
|
||
case OP_SW: /* 6F */
|
||
case OP_SDR: /* 2B */
|
||
case OP_SWR: /* 2F */
|
||
src2L ^= MSIGNL;
|
||
/* Fall through */
|
||
|
||
/* Floating Add */
|
||
case OP_AD: /* 6A */
|
||
case OP_AW: /* 6E */
|
||
case OP_ADR: /* 2A */
|
||
case OP_AWR: /* 2E */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src1L & MSIGNL)
|
||
fill |= 2;
|
||
if (src2L & MSIGNL)
|
||
fill |= 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
src1L <<= 4;
|
||
src2L <<= 4;
|
||
temp = e1 - e2;
|
||
if (temp > 0) {
|
||
if (temp > 17) {
|
||
src2L = 0;
|
||
} else {
|
||
/* Shift src2 right if src1 larger expo - expo */
|
||
src2L >>= 4 * temp;
|
||
}
|
||
} else if (temp < 0) {
|
||
if (temp < -17) {
|
||
src1L = 0;
|
||
} else {
|
||
/* Shift src1 right if src2 larger expo - expo */
|
||
src1L >>= 4 * (-temp);
|
||
}
|
||
e1 = e2;
|
||
}
|
||
|
||
/* Exponents should be equal now. */
|
||
|
||
/* Add results */
|
||
if (fill == 2 || fill == 1) {
|
||
/* Different signs do subtract */
|
||
src2L ^= XMASKL;
|
||
src2L++;
|
||
destL = src1L + src2L;
|
||
if (destL & CMASKL)
|
||
destL &= XMASKL;
|
||
else {
|
||
fill ^= 2;
|
||
destL ^= XMASKL;
|
||
destL ++;
|
||
}
|
||
} else {
|
||
destL = src1L + src2L;
|
||
}
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & CMASKL) {
|
||
destL >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
|
||
/* Set condition codes */
|
||
cc = 0;
|
||
if ((op & 0xE) != 0xE) {
|
||
if (destL != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
else
|
||
e1 = fill = 0;
|
||
} else {
|
||
if ((destL & UMASKL) != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
else
|
||
destL = e1 = fill = 0;
|
||
}
|
||
|
||
/* Check signifigance exception */
|
||
if (cc == 0 && pmsk & SIGMSK) {
|
||
storepsw(OPPSW, IRC_SIGNIF);
|
||
e1 = fill = 0;
|
||
destL = 0;
|
||
goto fpstore;
|
||
}
|
||
|
||
/* Check if we are normalized addition */
|
||
if ((op & 0xE) != 0xE) {
|
||
if (cc != 0) { /* Only if non-zero result */
|
||
while ((destL & SNMASKL) == 0) {
|
||
destL <<= 4;
|
||
e1 --;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
destL = 0;
|
||
fill = e1 = 0;
|
||
}
|
||
}
|
||
} else {
|
||
fill = e1 = 0; /* Return true zero */
|
||
}
|
||
} else {
|
||
if (cc == 0)
|
||
fill = e1 = 0; /* Return true zero */
|
||
}
|
||
|
||
/* Remmove the guard digit */
|
||
destL >>= 4;
|
||
fpstore:
|
||
/* Store result */
|
||
destL |= (((t_uint64)e1) << 56) & EMASKL;
|
||
if (cc != 0 && fill & 2)
|
||
destL |= MSIGNL;
|
||
fpregs[reg1] = destL;
|
||
break;
|
||
|
||
/* Multiply */
|
||
case OP_MDR: /* 2c */
|
||
case OP_MER: /* 3c */
|
||
case OP_ME: /* 7c */
|
||
case OP_MD: /* 6c */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if ((src1L & MSIGNL) != (src2L & MSIGNL))
|
||
fill = 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
|
||
/* Pre-nomalize src2 and src1 */
|
||
if (src2L != 0) {
|
||
while ((src2L & NMASKL) == 0) {
|
||
src2L <<= 4;
|
||
e2 --;
|
||
}
|
||
}
|
||
if (src1L != 0) {
|
||
while ((src1L & NMASKL) == 0) {
|
||
src1L <<= 4;
|
||
e1 --;
|
||
}
|
||
}
|
||
|
||
/* Compute exponent */
|
||
e1 = e1 + e2 - 64;
|
||
|
||
destL = 0;
|
||
/* Do multiply */
|
||
for (temp = 0; temp < 56; temp++) {
|
||
/* Add if we need too */
|
||
if (src1L & 1)
|
||
destL += src2L;
|
||
/* Shift right by one */
|
||
src1L >>= 1;
|
||
destL >>= 1;
|
||
}
|
||
fpnorm:
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & EMASKL) {
|
||
destL >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
/* Align the results */
|
||
if ((destL) != 0) {
|
||
while ((destL & NMASKL) == 0) {
|
||
destL <<= 4;
|
||
e1 --;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
destL = 0;
|
||
fill = e1 = 0;
|
||
}
|
||
}
|
||
} else
|
||
e1 = fill = 0;
|
||
destL |= (((t_uint64)e1) << 56) & EMASKL;
|
||
if (fill)
|
||
destL |= MSIGNL;
|
||
if ((op & 0x10) == 0 || (op & 0xf) == 0xc) {
|
||
fpregs[reg1] = destL;
|
||
} else {
|
||
fpregs[reg1] = (destL & HMASKL) | (fpregs[reg1] & LMASKL);
|
||
}
|
||
break;
|
||
|
||
/* Divide */
|
||
case OP_DER: /* 3d */
|
||
case OP_DDR: /* 2d */
|
||
case OP_DD: /* 6d */
|
||
case OP_DE: /* 7d */
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if ((src1L & MSIGNL) != (src2L & MSIGNL))
|
||
fill = 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
if (src2L == 0) {
|
||
storepsw(OPPSW, IRC_FPDIV);
|
||
break;
|
||
}
|
||
|
||
/* Pre-nomalize src2 and src1 */
|
||
if (src2L != 0) {
|
||
while ((src2L & NMASKL) == 0) {
|
||
src2L <<= 4;
|
||
e2 --;
|
||
}
|
||
}
|
||
if (src1L != 0) {
|
||
while ((src1L & NMASKL) == 0) {
|
||
src1L <<= 4;
|
||
e1 --;
|
||
}
|
||
}
|
||
|
||
/* Compute exponent */
|
||
e1 = e1 - e2 + 64;
|
||
|
||
/* Shift numbers up 4 bits so as not to lose precision below */
|
||
src2L <<= 4;
|
||
src1L <<= 4;
|
||
|
||
|
||
/* Check if we need to adjust divsor it larger then dividend */
|
||
if (src1L > src2L) {
|
||
src1L >>= 4;
|
||
e1++;
|
||
}
|
||
|
||
/* Change sign of src2 so we can add */
|
||
src2L ^= XMASKL;
|
||
src2L++;
|
||
destL = 0;
|
||
/* Do divide */
|
||
for (temp = 56; temp > 0; temp--) {
|
||
t_uint64 t;
|
||
|
||
/* Shift left by one */
|
||
src1L <<= 1;
|
||
/* Subtract remainder to dividend */
|
||
t= src1L + src2L;
|
||
|
||
/* Shift quotent left one bit */
|
||
destL <<= 1;
|
||
|
||
/* If remainder larger then divisor replace */
|
||
if ((t & CMASKL) != 0) {
|
||
src1L = t;
|
||
destL |= 1;
|
||
}
|
||
}
|
||
|
||
/* Compute one final set to see if rounding needed */
|
||
/* Shift left by one */
|
||
src1L <<= 1;
|
||
/* Subtract remainder to dividend */
|
||
src1L += src2L;
|
||
|
||
/* If .5 off, round */
|
||
if ((src1L & MSIGNL) != 0) {
|
||
destL++;
|
||
}
|
||
goto fpnorm;
|
||
|
||
/* Decimal operations */
|
||
case OP_CP: /* 1001 */
|
||
case OP_SP: /* 1011 */
|
||
case OP_ZAP: /* 1000 */
|
||
case OP_AP: /* 1010 */
|
||
if ((cpu_unit[0].flags & FEAT_DEC) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
dec_add(op, addr1, reg1, addr2, reg & 0xf);
|
||
break;
|
||
|
||
case OP_MP:
|
||
if ((cpu_unit[0].flags & FEAT_DEC) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
dec_mul(op, addr1, reg1, addr2, reg & 0xf);
|
||
break;
|
||
|
||
case OP_DP:
|
||
if ((cpu_unit[0].flags & FEAT_DEC) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
dec_div(op, addr1, reg1, addr2, reg & 0xf);
|
||
break;
|
||
|
||
/* Extended precision load round */
|
||
case OP_LRER:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if (fpregs[R2(reg)] & (t_uint64)MSIGN) {
|
||
e1 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src2L & MSIGNL)
|
||
fill = 1;
|
||
src2 = (uint32)((src2L >> 32) & MMASK);
|
||
dest = src2 + 1;
|
||
/* If overflow, shift right 4 bits */
|
||
if (dest & CMASK) {
|
||
dest >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
|
||
/* Store result */
|
||
dest |= (e1 << 24) & EMASK;
|
||
if (fill)
|
||
dest |= MSIGN;
|
||
} else
|
||
dest = src2;
|
||
fpregs[reg1] = (((t_uint64)dest) << 32) | (fpregs[reg1] & LMASKL);
|
||
break;
|
||
|
||
case OP_LRDR:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if (reg & 0xB) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
if (fpregs[R2(reg)|2] & 0x0080000000000000LL) {
|
||
/* Extract numbers and adjust */
|
||
e1 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src2L & MSIGNL)
|
||
fill = 2;
|
||
src2L &= MMASKL;
|
||
|
||
/* Add round */
|
||
destL = src2L + 1;
|
||
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & CMASKL) {
|
||
destL >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
/* Store result */
|
||
destL |= ((t_uint64)e1 << 56) & EMASKL;
|
||
if (fill)
|
||
destL |= MSIGNL;
|
||
} else {
|
||
destL = src2L;
|
||
}
|
||
fpregs[reg1] = destL;
|
||
break;
|
||
|
||
case OP_MXD:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if ((reg1 & 0xB) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
|
||
/* src2L already has DP number */
|
||
|
||
/* Fall through */
|
||
case OP_MXDR:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if ((reg1 & 0xB) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if ((src1L & MSIGNL) != (src2L & MSIGNL))
|
||
fill = 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
|
||
/* Pre-nomalize src2 and src1 */
|
||
if (src2L != 0) {
|
||
while ((src2L & NMASKL) == 0) {
|
||
src2L <<= 4;
|
||
e2 --;
|
||
}
|
||
}
|
||
if (src1L != 0) {
|
||
while ((src1L & NMASKL) == 0) {
|
||
src1L <<= 4;
|
||
e1 --;
|
||
}
|
||
}
|
||
|
||
/* Compute exponent */
|
||
e1 = e1 + e2 - 64;
|
||
|
||
destL = 0;
|
||
/* Do multiply */
|
||
for (temp = 0; temp < 56; temp++) {
|
||
/* Add if we need too */
|
||
if (src1L & 1)
|
||
destL += src2L;
|
||
/* Shift right by one */
|
||
src1L >>= 1;
|
||
if (destL & 1)
|
||
src1L |= MSIGNL;
|
||
destL >>= 1;
|
||
}
|
||
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & EMASKL) {
|
||
src1L >>= 4;
|
||
src1L |= (destL & 0xF) << 60;
|
||
destL >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
|
||
/* Align the results */
|
||
if ((destL | src1L) != 0) {
|
||
while ((destL & NMASKL) == 0) {
|
||
destL <<= 4;
|
||
destL |= (src1L >> 60) & 0xf;
|
||
src1L <<= 4;
|
||
e1 --;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
destL = src1L = 0;
|
||
fill = e1 = 0;
|
||
}
|
||
}
|
||
} else
|
||
e1 = fill = 0;
|
||
if (e1) {
|
||
destL |= (((t_uint64)e1) << 56) & EMASKL;
|
||
src1L |= ((t_uint64)(e1 - 14) << 56) & EMASKL;
|
||
if (fill) {
|
||
destL |= MSIGNL;
|
||
src1L |= MSIGNL;
|
||
}
|
||
}
|
||
fpregs[reg1] = destL;
|
||
fpregs[reg1|2] = src1L;
|
||
break;
|
||
|
||
case OP_SXR:
|
||
case OP_AXR:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if ((reg & 0xBB) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
|
||
src1L = fpregs[reg1];
|
||
src2L = fpregs[R2(reg)];
|
||
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if (src1L & MSIGNL)
|
||
fill |= 2;
|
||
if (src2L & MSIGNL)
|
||
fill |= 1;
|
||
if (op & 1)
|
||
fill ^= 1;
|
||
src1L &= MMASKL;
|
||
src2L &= MMASKL;
|
||
temp = e1 - e2;
|
||
if (temp > 0) {
|
||
if (temp > 15) {
|
||
src1L = src2L = 0;
|
||
} else {
|
||
src1L = src2L;
|
||
src2L = (fpregs[R2(reg)|2] << 4) & UMASKL;
|
||
/* Shift src2 right if src1 larger expo - expo */
|
||
while (temp-- != 0) {
|
||
src2L >>= 4;
|
||
src2L |= (src1L & 0xf) << 60;
|
||
src1L >>= 4;
|
||
}
|
||
}
|
||
} else {
|
||
/* Flip operands around */
|
||
src2L = (fpregs[reg1|2] << 4) & UMASKL;
|
||
fill = ((fill & 2) >> 1) | ((fill & 1) << 1);
|
||
if (temp < -15) {
|
||
fpregs[reg1] = 0;
|
||
fpregs[reg1|2] = 0;
|
||
e1 = e2;
|
||
} else {
|
||
/* Shift src1 right if src2 larger expo - expo */
|
||
fpregs[reg1] = fpregs[R2(reg)];
|
||
fpregs[reg1|2] = fpregs[R2(reg)|2];
|
||
while (temp++ != 0) {
|
||
src2L >>= 4;
|
||
src2L |= (src1L & 0xf) << 60;
|
||
src1L >>= 4;
|
||
}
|
||
}
|
||
e1 = e2;
|
||
}
|
||
|
||
/* Exponents should be equal now. */
|
||
|
||
/* Add results */
|
||
if (fill == 2 || fill == 1) {
|
||
t_uint64 th, tl;
|
||
/* Different signs do subtract */
|
||
src2L ^= XMASKL;
|
||
src1L ^= XMASKL;
|
||
if (src2L == XMASKL)
|
||
src1L++;
|
||
src2L++;
|
||
tl = fpregs[reg1];
|
||
th = fpregs[reg1|2];
|
||
destL = tl + src2L;
|
||
src1L = th + src1L;
|
||
if (destL & CMASKL) {
|
||
destL &= XMASKL;
|
||
src1L++;
|
||
}
|
||
if (src1L & CMASKL)
|
||
src1L &= XMASKL;
|
||
else {
|
||
fill ^= 2;
|
||
destL ^= XMASKL;
|
||
src1L ^= XMASKL;
|
||
if (destL == XMASKL)
|
||
src1L++;
|
||
destL++;
|
||
}
|
||
} else {
|
||
t_uint64 th, tl;
|
||
tl = fpregs[reg1];
|
||
th = fpregs[reg1|2];
|
||
destL = tl + src2L;
|
||
src1L = th + src1L;
|
||
if (destL & CMASKL) {
|
||
destL &= XMASKL;
|
||
src1L ++;
|
||
}
|
||
}
|
||
/* If overflow, shift right 4 bits */
|
||
if (src1L & NMASKL) {
|
||
destL >>= 4;
|
||
destL |= (src1L & 0xf) << 60;
|
||
src1L >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
|
||
/* Set condition codes */
|
||
cc = 0;
|
||
if ((destL | src1L) != 0)
|
||
cc = (fill & 2) ? 1 : 2;
|
||
else
|
||
e1 = fill = 0;
|
||
|
||
/* Check signifigance exception */
|
||
if (cc == 0 && pmsk & SIGMSK) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
fpregs[reg1] = 0;
|
||
fpregs[reg1|2] = 0;
|
||
break;
|
||
}
|
||
|
||
/* Check if we are normalized addition */
|
||
if (cc != 0) { /* Only if non-zero result */
|
||
while ((src1L & NMASKL) == 0) {
|
||
src1L <<= 4;
|
||
src1L |= (destL >> 60) & 0xf;
|
||
destL <<= 4;
|
||
destL &= UMASKL;
|
||
e1 --;
|
||
}
|
||
/* Check if underflow */
|
||
if (e1 < 0) {
|
||
if (pmsk & EXPUND) {
|
||
storepsw(OPPSW, IRC_EXPUND);
|
||
} else {
|
||
src2L = destL = 0;
|
||
fill = e1 = 0;
|
||
}
|
||
}
|
||
} else {
|
||
fill = e1 = 0; /* Return true zero */
|
||
}
|
||
|
||
/* Remmove the guard digit */
|
||
destL >>= 4;
|
||
/* Store result */
|
||
if (e1) {
|
||
src1L |= (((t_uint64)e1) << 56) & EMASKL;
|
||
destL |= ((t_uint64)(e1 - 14) << 56) & EMASKL;
|
||
if (fill) {
|
||
destL |= MSIGNL;
|
||
src1L |= MSIGNL;
|
||
}
|
||
}
|
||
fpregs[reg1] = src1L;
|
||
fpregs[reg1|2] = destL;
|
||
break;
|
||
|
||
case OP_MXR:
|
||
if ((cpu_unit[0].flags & FEAT_EFP) == 0) {
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if ((reg1 & 0xBB) != 0) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
goto supress;
|
||
}
|
||
|
||
/* Extract numbers and adjust */
|
||
e1 = (src1L & EMASKL) >> 56;
|
||
e2 = (src2L & EMASKL) >> 56;
|
||
fill = 0;
|
||
if ((src1L & MSIGNL) != (src2L & MSIGNL))
|
||
fill = 1;
|
||
src1L &= MMASKL;
|
||
src2L = fpregs[reg1|2] & MMASKL;
|
||
/* Normalize first operand */
|
||
if (src1L != 0) {
|
||
while ((src1L & NMASKL) == 0) {
|
||
src1L <<= 4;
|
||
src1L |= (src2L >> 56) & 0xf;
|
||
src2L <<= 4;
|
||
e1 --;
|
||
}
|
||
}
|
||
src2L <<= 4;
|
||
src2L &= UMASKL;
|
||
|
||
/* Normalize second operand. */
|
||
fpregs[reg1|2] = fpregs[R2(reg)|2] & MMASKL;
|
||
fpregs[reg1] = fpregs[R2(reg)] & MMASKL;
|
||
/* Save second operand in result */
|
||
destL = fpregs[reg1] | fpregs[reg1|2];
|
||
if (destL != 0) {
|
||
while ((fpregs[reg1] & 0x00f00000000000LL) == 0) {
|
||
fpregs[reg1] <<= 4;
|
||
fpregs[reg1|2] <<= 4;
|
||
fpregs[reg1] |= fpregs[reg1|2] >> 56;
|
||
e2--;
|
||
}
|
||
fpregs[reg1|2] &= MMASKL;
|
||
}
|
||
|
||
/* Compute exponent */
|
||
e1 = e1 + e2 - 64;
|
||
|
||
/* Do multiply */
|
||
destL = 0;
|
||
dest2L = 0;
|
||
for (temp = 0; temp < 112; temp++) {
|
||
/* Add if we need too */
|
||
if (fpregs[reg1|2] & 1) {
|
||
destL += src1L;
|
||
dest2L += src2L;
|
||
if (dest2L & CMASKL)
|
||
destL ++;
|
||
dest2L &= XMASKL;
|
||
}
|
||
/* Shift right by one */
|
||
dest2L >>= 1;
|
||
destL >>= 1;
|
||
if (destL & 0x8) {
|
||
dest2L |= 0x0800000000000000LL;
|
||
}
|
||
if (fpregs[reg1] & 1) {
|
||
fpregs[reg1|2] |= CMASKL >> 4;
|
||
}
|
||
fpregs[reg1|2] >>= 1;
|
||
fpregs[reg1] >>= 1;
|
||
}
|
||
/* If overflow, shift right 4 bits */
|
||
if (destL & EMASKL) {
|
||
src1L >>= 4;
|
||
src1L |= (destL & 0xF) << 60;
|
||
destL >>= 4;
|
||
e1 ++;
|
||
if (e1 >= 128) {
|
||
storepsw(OPPSW, IRC_EXPOVR);
|
||
}
|
||
}
|
||
src1L >>= 4;
|
||
if (e1) {
|
||
destL |= (((t_uint64)e1) << 56) & EMASKL;
|
||
src1L |= ((t_uint64)(e1 - 14) << 56) & EMASKL;
|
||
if (fill) {
|
||
destL |= MSIGNL;
|
||
src1L |= MSIGNL;
|
||
}
|
||
}
|
||
fpregs[reg1] = destL;
|
||
fpregs[reg1|2] = src1L;
|
||
break;
|
||
|
||
default: /* Unknown op code */
|
||
storepsw(OPPSW, IRC_OPR);
|
||
goto supress;
|
||
}
|
||
if (per_en && (cregs[9] & 0x10000000) != 0 && (cregs[9] & 0xffff & per_mod) != 0)
|
||
per_code |= 0x1000;
|
||
|
||
if (hst_lnt) {
|
||
hst[hst_p].dest = dest;
|
||
hst[hst_p].cc = cc;
|
||
}
|
||
|
||
sim_debug(DEBUG_TRACE, &cpu_dev, "D=%08x CC=%d\n", dest, cc);
|
||
if (sim_deb && (cpu_dev.dctrl & DEBUG_INST)) {
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"GR00=%08x GR01=%08x GR02=%08x GR03=%08x\n",
|
||
regs[0], regs[1], regs[2], regs[3]);
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"GR04=%08x GR05=%08x GR06=%08x GR07=%08x\n",
|
||
regs[4], regs[5], regs[6], regs[7]);
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"GR08=%08x GR09=%08x GR10=%08x GR11=%08x\n",
|
||
regs[8], regs[9], regs[10], regs[11]);
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"GR12=%08x GR13=%08x GR14=%08x GR15=%08x\n",
|
||
regs[12], regs[13], regs[14], regs[15]);
|
||
if ((op & 0xA0) == 0x20) {
|
||
sim_debug(DEBUG_INST, &cpu_dev,
|
||
"FP00=%016llx FP02=%016llx\nFP04=%016llx FP06=%016llx\n",
|
||
fpregs[0], fpregs[2], fpregs[4], fpregs[6]);
|
||
}
|
||
}
|
||
if (per_en && per_code)
|
||
storepsw(OPPSW, 0);
|
||
|
||
if (irqaddr != 0) {
|
||
supress:
|
||
sim_debug(DEBUG_TRACE, &cpu_dev, "suppress\n");
|
||
src1 = M[irqaddr>>2];
|
||
/* For IPL, save the device after reading the
|
||
* first part of the new PSW */
|
||
if (irqaddr == 0) {
|
||
(void)WriteHalf(0x2, irq);
|
||
if (Q370) {
|
||
(void)WriteHalf(0xBA, irq);
|
||
}
|
||
}
|
||
key[0] |= 0x4;
|
||
if (hst_lnt) {
|
||
hst_p = hst_p + 1;
|
||
if (hst_p >= hst_lnt)
|
||
hst_p = 0;
|
||
hst[hst_p].pc = irqaddr | HIST_LPW;
|
||
hst[hst_p].src1 = src1;
|
||
}
|
||
irqaddr += 4;
|
||
src2 = M[irqaddr>>2];
|
||
if (hst_lnt) {
|
||
hst[hst_p].src2 = src2;
|
||
}
|
||
lpsw:
|
||
irqaddr = 0;
|
||
if (Q370)
|
||
ec_mode = (src1 & 0x00080000) != 0;
|
||
else if ((cpu_unit[0].flags & FEAT_DAT) != 0)
|
||
ec_mode = (cregs[6] & 0x00800000) != 0;
|
||
else
|
||
ec_mode = 0;
|
||
ext_en = (src1 & 0x01000000) != 0;
|
||
if (ec_mode) {
|
||
irq_en = (src1 & 0x02000000) != 0;
|
||
dat_en = (src1 & 0x04000000) != 0;
|
||
cc = (src1 >> 12) & 3;
|
||
pmsk = (src1 >> 8) & 0xf;
|
||
if (Q370) {
|
||
per_en = (src1 & 0x40000000) != 0;
|
||
sysmsk = irq_en ? (cregs[2] >> 16) : 0;
|
||
} else { /* IBM 360/67 in EC mode */
|
||
if (irq_en) {
|
||
sysmsk = (cregs[4] >> 16) & 0xfe00;
|
||
sysmsk |= (cregs[4] >> 15) & 0x01ff;
|
||
} else {
|
||
sysmsk = 0;
|
||
}
|
||
}
|
||
} else {
|
||
sysmsk = (src1 >> 16) & 0xfc00;
|
||
if ((src1 & 0x2000000) != 0) {
|
||
if ((cpu_unit[0].flags & FEAT_370) != 0)
|
||
sysmsk |= (cregs[2] >> 16) & 0x3ff;
|
||
else
|
||
sysmsk |= 0x3ff;
|
||
}
|
||
dat_en = 0;
|
||
irq_en = (sysmsk != 0);
|
||
per_en = 0;
|
||
cc = (src2 >> 28) & 0x3;
|
||
pmsk = (src2 >> 24) & 0xf;
|
||
}
|
||
irq_pend = 1;
|
||
st_key = (src1 >> 16) & 0xf0;
|
||
if (Q370) /* Don't allow ASCII bit to be set */
|
||
flags = (src1 >> 16) & 0x7;
|
||
else
|
||
flags = (src1 >> 16) & 0xf;
|
||
PC = src2 & AMASK;
|
||
sim_debug(DEBUG_INST, &cpu_dev, "PSW=%08x %08x ", src1, src2);
|
||
if (ec_mode && ((src1 & 0xb800c0ff) != 0 || (src2 & 0xff000000) != 0)) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
}
|
||
}
|
||
sim_interval--;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/*
|
||
* Load a decimal number into temp storage.
|
||
* return 1 if error.
|
||
* return 0 if ok.
|
||
*/
|
||
int dec_load(uint8 *data, uint32 addr, int len, int *sign)
|
||
{
|
||
uint32 temp;
|
||
int i, j;
|
||
int err = 0;
|
||
|
||
addr += len; /* Point to end */
|
||
memset(data, 0, 32);
|
||
j = 0;
|
||
/* Read it into temp backwards */
|
||
for (i = 0; i <= len; i++) {
|
||
int t;
|
||
if (ReadByte(addr, &temp))
|
||
return 1;
|
||
t = temp & 0xf;
|
||
if (j != 0 && t > 0x9)
|
||
err = 1;
|
||
data[j++] = t;
|
||
t = (temp >> 4) & 0xf;
|
||
if (t > 0x9)
|
||
err = 1;
|
||
data[j++] = t;
|
||
addr--;
|
||
}
|
||
/* Check if sign valid and return it */
|
||
if (data[0] == 0xB || data[0] == 0xD)
|
||
*sign = 1;
|
||
else if (data[0] < 0xA)
|
||
err = 1;
|
||
else
|
||
*sign = 0;
|
||
if (err) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Store a decimal number into memory storage.
|
||
* return 1 if error.
|
||
* return 0 if ok.
|
||
*/
|
||
int dec_store(uint8 *data, uint32 addr, int len, int sign)
|
||
{
|
||
uint32 temp;
|
||
int i, j;
|
||
addr += len;
|
||
|
||
if (sign) {
|
||
data[0] = ((flags & ASCII)? 0xb : 0xd);
|
||
} else {
|
||
data[0] = ((flags & ASCII)? 0xa : 0xc);
|
||
}
|
||
j = 0;
|
||
for (i = 0; i <= len; i++) {
|
||
temp = data[j++] & 0xf;
|
||
temp |= (data[j++] & 0xf) << 4;
|
||
if (WriteByte(addr, temp))
|
||
return 1;
|
||
addr--;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Handle SRP instruction.
|
||
*
|
||
*/
|
||
void
|
||
dec_srp(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2)
|
||
{
|
||
uint8 a[32];
|
||
int i, j;
|
||
uint8 acc;
|
||
uint8 cy;
|
||
int shift = addr2 & 0x3f;
|
||
int sa;
|
||
int ov = 0;
|
||
int zero;
|
||
|
||
/* Load first operand. */
|
||
if (dec_load(a, addr1, (int)len1, &sa))
|
||
return;
|
||
|
||
cy = 0;
|
||
zero = 1;
|
||
if (shift & 0x20) { /* Shift to right */
|
||
shift = 0x3F & (~shift + 1);
|
||
cy = (a[shift] + len2) > 0x9;
|
||
j = shift+1;
|
||
for (i = 1; i < len1; i++, j++) {
|
||
if (j > len1)
|
||
acc = cy;
|
||
else
|
||
acc = a[j] + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
if ((acc & 0xf) != 0)
|
||
zero = 0;
|
||
}
|
||
} else if (shift != 0) { /* Shift to left */
|
||
/* Check if we would move out any non-zero digits */
|
||
for (j = len1; j > shift; j--) {
|
||
if (a[j] != 0)
|
||
ov = 1;
|
||
}
|
||
/* Now shift digits */
|
||
for (i = len1; j > 0; i--, j--) {
|
||
a[i] = a[j];
|
||
if (a[i] != 0)
|
||
zero = 0;
|
||
}
|
||
/* Now fill zeros until at bottom */
|
||
for (; i > 0; i--)
|
||
a[i] = 0;
|
||
} else {
|
||
/* Check if number is zero */
|
||
for (i = 1; i < len1; i++) {
|
||
if (a[i] != 0) {
|
||
zero = 0;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (zero && !ov)
|
||
sa = 0;
|
||
cc = 0;
|
||
if (!zero) /* Really not zero */
|
||
cc = (sa)? 1: 2;
|
||
dec_store(a, addr1, (int)len1, sa);
|
||
if (ov)
|
||
cc = 3;
|
||
if (ov && pmsk & DECOVR)
|
||
storepsw(OPPSW, IRC_DECOVR);
|
||
}
|
||
|
||
/*
|
||
* Handle AP, SP, CP and ZAP instructions.
|
||
*
|
||
* ZAP = F8 00
|
||
* CP = F9 01
|
||
* AP = FA 10
|
||
* SP = FB 11
|
||
*/
|
||
void
|
||
dec_add(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2)
|
||
{
|
||
uint8 a[32];
|
||
uint8 b[32];
|
||
int i;
|
||
uint8 acc;
|
||
uint8 cy;
|
||
int len = (int)len1;
|
||
int sa, sb;
|
||
int addsub = 0;
|
||
int zero;
|
||
int ov = 0;
|
||
|
||
if (len2 > len1)
|
||
len = (int)len2;
|
||
/* Always load second operand */
|
||
if (dec_load(b, addr2, (int)len2, &sb))
|
||
return;
|
||
|
||
if (op & 1)
|
||
sb = !sb;
|
||
/* Length is 1 plus number of digits times two, including the sign nibble. */
|
||
len = 2*(len+1)-1;
|
||
/* On all but ZAP load first operand */
|
||
if ((op & 3) != 0) {
|
||
if (dec_load(a, addr1, (int)len1, &sa))
|
||
return;
|
||
} else {
|
||
/* For ZAP just clear A */
|
||
memset(a, 0, 32);
|
||
sa = 0;
|
||
}
|
||
if (sa != sb)
|
||
addsub = 1;
|
||
cy = addsub;
|
||
zero = 1;
|
||
/* Add numbers together */
|
||
for (i = 1; i <= len; i++) {
|
||
acc = b[i] + ((addsub)? (0x9 - a[i]):a[i]) + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
if ((acc & 0xf) != 0)
|
||
zero = 0;
|
||
}
|
||
if (cy) {
|
||
if (addsub)
|
||
sa = !sa;
|
||
else
|
||
ov = 1;
|
||
} else {
|
||
if (addsub) {
|
||
/* We need to recomplent the result */
|
||
cy = 1;
|
||
zero = 1;
|
||
for (i = 1; i <= len; i++) {
|
||
acc = (0x9 - a[i]) + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
if ((acc & 0xf) != 0)
|
||
zero = 0;
|
||
}
|
||
}
|
||
}
|
||
if (zero && !ov)
|
||
sa = 0;
|
||
cc = 0;
|
||
if (!zero) /* Really not zero */
|
||
cc = (sa)? 1: 2;
|
||
if ((op & 3) != 1) {
|
||
if (!zero && !ov) {
|
||
/* Start at len1 and go to len2 and see if any non-zero digits */
|
||
for (i = (len1+1)*2; i <= len; i++) {
|
||
if (a[i] != 0) {
|
||
ov = 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
dec_store(a, addr1, (int)len1, sa);
|
||
if (ov)
|
||
cc = 3;
|
||
if (ov && pmsk & DECOVR)
|
||
storepsw(OPPSW, IRC_DECOVR);
|
||
}
|
||
}
|
||
|
||
void
|
||
dec_mul(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2)
|
||
{
|
||
uint8 a[32];
|
||
uint8 b[32];
|
||
int i;
|
||
int j;
|
||
int k;
|
||
uint8 acc;
|
||
uint8 cy;
|
||
int sa, sb;
|
||
int mul;
|
||
int len;
|
||
|
||
if (len2 == len1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return;
|
||
}
|
||
if (len2 > 7 || len2 >= len1) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
return;
|
||
}
|
||
if (dec_load(b, addr2, (int)len2, &sb))
|
||
return;
|
||
if (dec_load(a, addr1, (int)len1, &sa))
|
||
return;
|
||
len = (int)len1;
|
||
len1 = (len1 + 1) * 2;
|
||
len2 = (len2 + 1) * 2;
|
||
/* Verify that we have len2 zeros at start of a */
|
||
for (i = len1 - len2; i < len1; i++) {
|
||
if (a[i] != 0) {
|
||
storepsw(OPPSW, IRC_DATA);
|
||
return;
|
||
}
|
||
}
|
||
sa ^= sb; /* Compute sign */
|
||
/* Start at end and work backwards */
|
||
for (j = len1-len2; j > 0; j--) {
|
||
mul = a[j];
|
||
a[j] = 0;
|
||
while(mul != 0) {
|
||
/* Add multiplier to multiplican */
|
||
cy = 0;
|
||
for (i = j, k = 1; i < len1; i++, k++) {
|
||
acc = a[i] + b[k] + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
}
|
||
mul--;
|
||
}
|
||
}
|
||
dec_store(a, addr1, len, sa);
|
||
}
|
||
|
||
void
|
||
dec_div(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2)
|
||
{
|
||
uint8 a[33];
|
||
uint8 b[32];
|
||
uint8 c[32];
|
||
int i;
|
||
int j;
|
||
int k;
|
||
uint8 acc;
|
||
uint8 cy;
|
||
int sa, sb;
|
||
int q;
|
||
int len;
|
||
|
||
if (len2 > 7 || len2 >= len1) {
|
||
storepsw(OPPSW, IRC_SPEC);
|
||
return;
|
||
}
|
||
if (dec_load(b, addr2, (int)len2, &sb))
|
||
return;
|
||
if (dec_load(a, addr1, (int)len1, &sa))
|
||
return;
|
||
memset(c, 0, 32);
|
||
len = (int)len1;
|
||
len1 = (len1 + 1) * 2;
|
||
len2 = (len2 + 1) * 2;
|
||
sb ^= sa; /* Compute sign */
|
||
for (j = len1 - len2; j > 0; j--) {
|
||
q = 0;
|
||
do {
|
||
/* Subtract divisor */
|
||
cy = 1;
|
||
for (i = j, k = 1; k < len2; i++, k++) {
|
||
c[i] = a[i]; /* Save if we divide too far */
|
||
acc = a[i] + (0x9 - b[k]) + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
}
|
||
/* Plus one more digit */
|
||
if (i < 31) {
|
||
acc = a[i] + 9 + cy;
|
||
if (acc > 0x9)
|
||
acc += 0x6;
|
||
a[i] = acc & 0xf;
|
||
cy = (acc >> 4) & 0xf;
|
||
}
|
||
/* If no borrow, so we are done with this digit */
|
||
if (!cy) {
|
||
/* It is a no-no to have non-zero digit above size */
|
||
if (q > 0 && (i+1) >= len1) {
|
||
storepsw(OPPSW, IRC_DECDIV);
|
||
return;
|
||
}
|
||
a[i+1] = q; /* Save quotient digit */
|
||
for (i = j; k > 1; i++, k--)
|
||
a[i] = c[i]; /* Restore previous */
|
||
} else {
|
||
q++;
|
||
}
|
||
if (q > 9) {
|
||
storepsw(OPPSW, IRC_DECDIV);
|
||
return;
|
||
}
|
||
} while(cy != 0);
|
||
}
|
||
/* Set sign of quotient */
|
||
if (sb) {
|
||
a[len2] = ((flags & ASCII)? 0xb : 0xd);
|
||
} else {
|
||
a[len2] = ((flags & ASCII)? 0xa : 0xc);
|
||
}
|
||
dec_store(a, addr1, len, sa);
|
||
}
|
||
|
||
|
||
/* Reset */
|
||
|
||
t_stat
|
||
cpu_reset (DEVICE *dptr)
|
||
{
|
||
int i;
|
||
|
||
sim_vm_fprint_stopped = &cpu_fprint_stopped;
|
||
/* Create memory array if it does not exist. */
|
||
if (M == NULL) { /* first time init? */
|
||
sim_brk_types = sim_brk_dflt = SWMASK ('E');
|
||
M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32));
|
||
if (M == NULL)
|
||
return SCPE_MEM;
|
||
}
|
||
/* Set up channels */
|
||
chan_set_devs();
|
||
|
||
sysmsk = irqcode = irqaddr = loading = 0;
|
||
st_key = cc = pmsk = ec_mode = interval_irq = flags = 0;
|
||
dat_en = irq_en = ext_en = per_en = 0;
|
||
clk_state = CLOCK_UNSET;
|
||
for (i = 0; i < 256; i++)
|
||
tlb[i] = 0;
|
||
for (i = 0; i < 4096; i++)
|
||
key[i] = 0;
|
||
for (i = 0; i < 16; i++)
|
||
cregs[i] = 0;
|
||
clk_cmp[0] = clk_cmp[1] = 0xffffffff;
|
||
if (Q370) {
|
||
if (clk_state == CLOCK_UNSET) {
|
||
/* Set TOD to current time */
|
||
time_t seconds = sim_get_time(NULL);
|
||
t_uint64 lsec = (t_uint64)seconds;
|
||
/* IBM measures time from 1900, Unix starts at 1970 */
|
||
/* Add in number of years from 1900 to 1970 + 17 leap days */
|
||
lsec += ((70 * 365) + 17) * 86400ULL;
|
||
lsec *= 1000000ULL;
|
||
lsec <<= 12;
|
||
tod_clock[0] = (uint32)(lsec >> 32);
|
||
tod_clock[1] = (uint32)(lsec & FMASK);
|
||
clk_state = CLOCK_SET;
|
||
}
|
||
cregs[0] = 0x000000e0;
|
||
cregs[2] = 0xffffffff;
|
||
cregs[14] = 0xc2000000;
|
||
cregs[15] = 512;
|
||
}
|
||
|
||
if (cpu_unit[0].flags & (FEAT_370|FEAT_TIMER)) {
|
||
sim_rtcn_init_unit (&cpu_unit[0], cpu_unit[0].wait, TMR_RTC);
|
||
sim_activate(&cpu_unit[0], 10000);
|
||
}
|
||
idle_stop_tm0 = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
/* Interval timer routines */
|
||
t_stat
|
||
rtc_srv(UNIT * uptr)
|
||
{
|
||
(void)sim_rtcn_calb (rtc_tps, TMR_RTC);
|
||
sim_activate_after(uptr, 1000000/rtc_tps);
|
||
if ((M[0x50>>2] & 0xfffff00) == 0) {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "TIMER IRQ %08x\n", M[0x50>>2]);
|
||
interval_irq = 1;
|
||
}
|
||
M[0x50>>2] -= 0x100;
|
||
key[0] |= 0x6;
|
||
sim_debug(DEBUG_INST, &cpu_dev, "TIMER = %08x\n", M[0x50>>2]);
|
||
/* Time of day clock and timer on IBM 370 */
|
||
if (Q370) {
|
||
uint32 t;
|
||
if (clk_state && (cregs[0] & 0x20000000) == 0) {
|
||
t = tod_clock[1] + (13333333);
|
||
if (t < tod_clock[1])
|
||
tod_clock[0]++;
|
||
tod_clock[1] = t;
|
||
sim_debug(DEBUG_INST, &cpu_dev, "TOD = %08x %08x\n", tod_clock[0], tod_clock[1]);
|
||
check_tod_irq();
|
||
}
|
||
t = cpu_timer[1] - (timer_tics << 12);
|
||
if (t > cpu_timer[1])
|
||
cpu_timer[0]--;
|
||
cpu_timer[1] = t;
|
||
sim_debug(DEBUG_INST, &cpu_dev, "INTER = %08x %08x\n", cpu_timer[0], cpu_timer[1]);
|
||
timer_tics = 3333;
|
||
if (cpu_timer[0] & MSIGN) {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "CPU TIMER IRQ %08x%08x\n", cpu_timer[0],
|
||
cpu_timer[1]);
|
||
clk_irq = 1;
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
void
|
||
check_tod_irq()
|
||
{
|
||
tod_irq = 0;
|
||
if ((clk_cmp[0] < tod_clock[0]) ||
|
||
((clk_cmp[0] == tod_clock[0]) && (clk_cmp[1] < tod_clock[1]))) {
|
||
sim_debug(DEBUG_INST, &cpu_dev, "CPU TIMER CCK IRQ %08x %08x\n", clk_cmp[0],
|
||
clk_cmp[1]);
|
||
tod_irq = 1;
|
||
}
|
||
}
|
||
|
||
|
||
/* Memory examine */
|
||
|
||
t_stat
|
||
cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw)
|
||
{
|
||
uint32 addr = (uint32) exta;
|
||
uint32 byte;
|
||
uint32 offset = 8 * (3 - (addr & 0x3));
|
||
|
||
if (vptr == NULL)
|
||
return SCPE_ARG;
|
||
if (sw & SWMASK ('V')) {
|
||
/* Virtual address, simulate LRA */
|
||
uint32 seg;
|
||
uint32 page;
|
||
uint32 entry;
|
||
uint32 addr1, addr2;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0)
|
||
return SCPE_NXM;
|
||
|
||
/* Segment number to word address */
|
||
addr1 = addr & AMASK;
|
||
seg = (addr1 >> seg_shift) & seg_mask;
|
||
page = (addr1 >> page_shift) & page_index;
|
||
if (seg > seg_len)
|
||
return SCPE_NXM;
|
||
addr2 = (((seg << 2) + seg_addr) & AMASK);
|
||
if (addr2 >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
/* Check if entry valid */
|
||
entry = M[addr2 >> 2];
|
||
if (entry & PTE_VALID)
|
||
return SCPE_NXM;
|
||
|
||
if (Q370) {
|
||
addr2 = (entry >> 28) + 1;
|
||
/* Check if over end of table */
|
||
if ((page >> pte_len_shift) > addr2)
|
||
return SCPE_NXM;
|
||
} else {
|
||
addr2 = (entry >> 24);
|
||
if (page > addr2)
|
||
return SCPE_NXM;
|
||
}
|
||
|
||
/* Now we need to fetch the actual entry */
|
||
addr2 = (entry & PTE_ADR) + (page << 1);
|
||
addr2 &= AMASK;
|
||
if (addr2 >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
entry = M[addr2 >> 2];
|
||
entry >>= (addr2 & 2) ? 0 : 16;
|
||
entry &= 0xffff;
|
||
|
||
/* Check if entry valid */
|
||
if (entry & pte_avail)
|
||
return SCPE_NXM;
|
||
|
||
addr = (addr1 & page_mask) | ((entry & 0xfff8) << 8);
|
||
}
|
||
|
||
/* Real address, just return it, but ignore high order bits */
|
||
addr &= AMASK;
|
||
if (addr >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
addr >>= 2;
|
||
byte = M[addr] >> offset;
|
||
byte &= 0xff;
|
||
*vptr = byte;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory deposit */
|
||
|
||
t_stat
|
||
cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw)
|
||
{
|
||
uint32 addr = (uint32) exta;
|
||
uint32 offset = 8 * (3 - (addr & 0x3));
|
||
uint32 word;
|
||
uint32 mask;
|
||
|
||
if (sw & SWMASK ('V')) {
|
||
/* Virtual address, simulate LRA */
|
||
uint32 seg;
|
||
uint32 page;
|
||
uint32 entry;
|
||
uint32 addr1, addr2;
|
||
|
||
if ((cpu_unit[0].flags & FEAT_DAT) == 0)
|
||
return SCPE_NXM;
|
||
|
||
/* Segment number to word address */
|
||
addr1 = addr & AMASK;
|
||
seg = (addr1 >> seg_shift) & seg_mask;
|
||
page = (addr1 >> page_shift) & page_index;
|
||
if (seg > seg_len)
|
||
return SCPE_NXM;
|
||
addr2 = (((seg << 2) + seg_addr) & AMASK);
|
||
if (addr2 >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
/* Check if entry valid */
|
||
entry = M[addr2 >> 2];
|
||
if (entry & PTE_VALID)
|
||
return SCPE_NXM;
|
||
|
||
if (Q370) {
|
||
addr2 = (entry >> 28) + 1;
|
||
/* Check if over end of table */
|
||
if ((page >> pte_len_shift) > addr2)
|
||
return SCPE_NXM;
|
||
} else {
|
||
addr2 = (entry >> 24);
|
||
if (page > addr2)
|
||
return SCPE_NXM;
|
||
}
|
||
|
||
/* Now we need to fetch the actual entry */
|
||
addr2 = (entry & PTE_ADR) + (page << 1);
|
||
addr2 &= AMASK;
|
||
if (addr2 >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
entry = M[addr2 >> 2];
|
||
entry >>= (addr2 & 2) ? 0 : 16;
|
||
entry &= 0xffff;
|
||
|
||
/* Check if entry valid */
|
||
if (entry & pte_avail)
|
||
return SCPE_NXM;
|
||
|
||
addr = (addr1 & page_mask) | ((entry & 0xfff8) << 8);
|
||
}
|
||
|
||
/* Ignore high order bits */
|
||
addr &= AMASK;
|
||
if (addr >= MEMSIZE)
|
||
return SCPE_NXM;
|
||
|
||
addr >>= 2;
|
||
mask = 0xff << offset;
|
||
word = M[addr];
|
||
word &= ~mask;
|
||
word |= (val & 0xff) << offset;
|
||
M[addr] = word;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Memory allocation */
|
||
|
||
t_stat
|
||
cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
int32 mc = 0;
|
||
int32 i, clim;
|
||
uint32 *nM = NULL;
|
||
int32 max = MEMSIZE >> 2;
|
||
|
||
val = 16 * 1024 * val;
|
||
if ((val <= 0) || (val > MAXMEMSIZE))
|
||
return SCPE_ARG;
|
||
for (i = val>>2; i < max; i++)
|
||
mc = mc | M[i];
|
||
if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE))
|
||
return SCPE_OK;
|
||
nM = (uint32 *) calloc (val >> 2, sizeof (uint32));
|
||
if (nM == NULL)
|
||
return SCPE_MEM;
|
||
clim = ((t_addr)val < MEMSIZE)? val >> 2: max;
|
||
for (i = 0; i < clim; i++)
|
||
nM[i] = M[i];
|
||
free (M);
|
||
M = nM;
|
||
fprintf(stderr, "Mem size=%x\r\n", val);
|
||
MEMSIZE = val;
|
||
reset_all (0);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* RSV: Set CPU IDLESTOP=<val>
|
||
* <val>=number of seconds.
|
||
*
|
||
* Sets max time in secounds CPU is IDLE but waiting for interrupt
|
||
* from device. if <val> not zero, simulated CPU will wait for this wallclock
|
||
* number of seconds, then stop. This allows to script a BOOT command and the
|
||
* continue automatically when IPL has finished. Set to zero to disable.
|
||
*/
|
||
|
||
t_stat cpu_set_idle_stop (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
int32 n;
|
||
t_stat r;
|
||
|
||
if (cptr == NULL) {
|
||
return SCPE_ARG;
|
||
}
|
||
n = (int32) get_uint(cptr, 10, 60, &r);
|
||
if (r != SCPE_OK) return SCPE_ARG;
|
||
idle_stop_msec = n * 1000;
|
||
idle_stop_tm0 = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_bool
|
||
cpu_fprint_stopped (FILE *st, t_stat v)
|
||
{
|
||
if (ec_mode) {
|
||
if (Q370)
|
||
fprintf(st, " PSW=%08x %08x\n",
|
||
(((uint32)dat_en) << 26) | ((per_en) ? 1<<30:0) | ((irq_en) ? 1<<25:0) |
|
||
((ext_en) ? 1<<24:0) | 0x80000 | (((uint32)st_key) << 16) |
|
||
(((uint32)flags) << 16) | (((uint32)cc) << 12) | (((uint32)pmsk) << 8), PC);
|
||
else
|
||
fprintf(st, " PSW=%08x %08x\n",
|
||
(((uint32)dat_en) << 26) | ((irq_en) ? 1<<25:0) | ((ext_en) ? 1<<24:0) |
|
||
(((uint32)st_key) << 16) | (((uint32)flags) << 16) |
|
||
(((uint32)ilc) << 14) | (((uint32)cc) << 12) | (((uint32)pmsk) << 8), PC);
|
||
} else {
|
||
fprintf(st, " PSW=%08x %08x\n",
|
||
((uint32)(ext_en) << 24) | (((uint32)sysmsk & 0xfe00) << 16) |
|
||
(((uint32)st_key) << 16) | (((uint32)flags) << 16) | ((uint32)irqcode),
|
||
(((uint32)ilc) << 30) | (((uint32)cc) << 28) | (((uint32)pmsk) << 24) | PC);
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/* Handle execute history */
|
||
|
||
/* 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].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 = (struct InstHistory *)calloc(sizeof(struct InstHistory), lnt);
|
||
|
||
if (hst == NULL)
|
||
return SCPE_MEM;
|
||
hst_lnt = lnt;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show history */
|
||
|
||
t_stat
|
||
cpu_show_hist(FILE * st, UNIT * uptr, int32 val, CONST void *desc)
|
||
{
|
||
int32 k, di, lnt;
|
||
const char *cptr = (const char *) desc;
|
||
t_stat r;
|
||
struct InstHistory *h;
|
||
|
||
if (hst_lnt == 0)
|
||
return SCPE_NOFNC; /* enabled? */
|
||
if (cptr) {
|
||
lnt = (int32) get_uint(cptr, 10, hst_lnt, &r);
|
||
if ((r != SCPE_OK) || (lnt == 0))
|
||
return SCPE_ARG;
|
||
} else
|
||
lnt = hst_lnt;
|
||
di = hst_p - lnt; /* work forward */
|
||
if (di < 0)
|
||
di = di + hst_lnt;
|
||
fprintf(st, "PC A1 A2 D1 D2 RESULT CC\n\n");
|
||
for (k = 0; k < lnt; k++) { /* print specified */
|
||
h = &hst[(++di) % hst_lnt]; /* entry pointer */
|
||
if (h->pc & HIST_PC) { /* instruction? */
|
||
fprintf(st, "%06x %06x %06x %08x %08x %08x %1x %04x ",
|
||
h->pc & PAMASK, h->addr1 & PAMASK, h->addr2 & PAMASK,
|
||
h->src1, h->src2, h->dest, h->cc, h->inst[0]);
|
||
if ((h->op & 0xc0) != 0)
|
||
fprintf(st, "%04x ", h->inst[1]);
|
||
else
|
||
fprintf(st, " ");
|
||
if ((h->op & 0xc0) == 0xc0)
|
||
fprintf(st, "%04x ", h->inst[2]);
|
||
else
|
||
fprintf(st, " ");
|
||
fprintf(st, " ");
|
||
fprint_inst(st, h->inst);
|
||
fputc('\n', st); /* end line */
|
||
} /* end else instruction */
|
||
if (h->pc & HIST_LPW) { /* load PSW */
|
||
fprintf(st," LPSW %06x %08x %08x\n", h->pc & PAMASK, h->src1, h->src2);
|
||
} /* end else instruction */
|
||
if (h->pc & HIST_SPW) { /* load PSW */
|
||
fprintf(st," SPSW %06x %08x %08x %04x\n", h->pc & PAMASK, h->src1, h->src2, h->addr1);
|
||
} /* end else instruction */
|
||
} /* end for */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
t_stat
|
||
cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf(st, "IBM360 CPU\n\n");
|
||
fprint_set_help(st, dptr);
|
||
fprint_show_help(st, dptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *
|
||
cpu_description (DEVICE *dptr)
|
||
{
|
||
return "IBM 360 CPU";
|
||
}
|
||
|