mirror of
https://github.com/rcornwell/sims.git
synced 2026-04-25 19:51:57 +00:00
842 lines
27 KiB
C
842 lines
27 KiB
C
/* ibm360_vma.c: ibm 360 Virtual Memory Assists for VM/370.
|
|
|
|
Copyright (c) 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.
|
|
|
|
*/
|
|
|
|
#include "ibm360_defs.h" /* simulator defns */
|
|
|
|
extern uint32 *M;
|
|
extern uint8 key[MAXMEMSIZE / 2048];
|
|
extern uint32 PC; /* Program counter */
|
|
extern uint32 regs[16]; /* CPU Registers */
|
|
extern uint32 cregs[16]; /* Control registers /67 or 370 only */
|
|
extern uint8 cc; /* CC */
|
|
extern uint8 pmsk; /* Program mask */
|
|
extern uint8 st_key; /* Storage key */
|
|
extern int per_en; /* PER mode enable */
|
|
|
|
#define AMASK 0x00ffffff /* Mask address bits */
|
|
#define MSIGN 0x80000000 /* Minus sign */
|
|
|
|
#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)
|
|
|
|
extern int ReadFull(uint32 addr, uint32 *data);
|
|
extern int ReadByte(uint32 addr, uint32 *data);
|
|
extern int ReadHalf(uint32 addr, uint32 *data);
|
|
extern int WriteFull(uint32 addr, uint32 data);
|
|
extern int WriteByte(uint32 addr, uint32 data);
|
|
extern int WriteHalf(uint32 addr, uint32 data);
|
|
|
|
/* Handle VM Assists for RRB instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_rrb(uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 micrseg;
|
|
uint32 page;
|
|
uint32 seg;
|
|
uint32 segpage;
|
|
uint32 pagswp;
|
|
uint32 swpflg;
|
|
uint32 pagcore;
|
|
uint8 stk;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "RRB check %08x\n", addr1);
|
|
/* Check if enabled */
|
|
if ((cregs[6] & 0xe0000000) != MSIGN)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch SEGPAGE */
|
|
micrseg = M[micblok];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Micrseg %08x\n", micrseg);
|
|
if (micrseg & 0x2)
|
|
return 0;
|
|
/* Compute address to operate on */
|
|
page = addr1 >> 12;
|
|
if (micrseg & 0x1) {
|
|
seg = page >> 7;
|
|
page &= 0x7f;
|
|
} else {
|
|
seg = page >> 4;
|
|
page &= 0xf;
|
|
}
|
|
segpage = M[((micrseg & AMASK) >> 2) + seg];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Segpage %08x s=%x p=%x\n", segpage, seg, page);
|
|
if ((segpage >> 24) <= (addr1 >> 20))
|
|
return 0;
|
|
pagswp = M[((segpage & AMASK) >> 2) - 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagswp %08x\n", pagswp);
|
|
swpflg = M[((pagswp + (8 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "swpflg %08x\n", swpflg);
|
|
pagcore = M[((segpage + (2 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagcore %08x\n", pagcore);
|
|
/* Get key value */
|
|
if (addr1 & 0x800) {
|
|
stk = swpflg & 0xfe;
|
|
} else {
|
|
stk = (swpflg >> 8) & 0xfe;
|
|
}
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "stk %02x\n", stk);
|
|
/* Check if page is valid */
|
|
if ((page & 0x1) == 0)
|
|
pagcore >>= 16;
|
|
if ((pagcore & 0xe) == 0) {
|
|
pagcore &= 0xfff0;
|
|
pagcore <<= 8;
|
|
pagcore |= addr1 & 0x800;
|
|
stk |= key[pagcore>>11] & 0x6;
|
|
key[pagcore>>11] &= 0xfb; /* Clear reference bit */
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "real addr %08x %02x\n", pagcore, stk);
|
|
}
|
|
/* Clear virtual reference bits */
|
|
if (addr1 & 0x800) {
|
|
swpflg &= 0xfcfffffb;
|
|
} else {
|
|
swpflg &= 0xf3fffbff;
|
|
}
|
|
M[((pagswp + (8 * page)) & AMASK) >> 2] = swpflg;
|
|
/* Update the CC */
|
|
cc = (stk >> 1) & 03;
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for B2 instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_370(int reg, uint32 addr1)
|
|
{
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "B2%02x %08x check\n", reg, addr1);
|
|
switch(reg) {
|
|
default:
|
|
case 0x2: /* STIDP */
|
|
case 0x3: /* STIDC */
|
|
case 0x4: /* SCK */
|
|
case 0x5: /* STCK */
|
|
case 0x6: /* SCKC */
|
|
case 0x7: /* STCKC */
|
|
break;
|
|
case 0x8: /* SPT */
|
|
break;
|
|
case 0x9: /* STPT */
|
|
break;
|
|
case 0xd: /* PTLB */
|
|
break;
|
|
case 0xa: /* SPKA */
|
|
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
|
break;
|
|
}
|
|
if (cregs[6] & 0x10000000)
|
|
break;
|
|
micvpsw = M[((cregs[6] & 0xfffff8) >> 2) + 2];
|
|
vpsw = M[(micvpsw & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x\n", vpsw);
|
|
vpsw &= 0xff0fffff;
|
|
vpsw |= (0xf0 & addr1) << 16;
|
|
M[micvpsw & AMASK] = vpsw;
|
|
st_key = 0xf0 & addr1;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "New VPSW %08x New key %02x \n", vpsw, st_key);
|
|
return 1;
|
|
|
|
case 0xb: /* IPK */
|
|
if ((cpu_unit[0].flags & FEAT_PROT) == 0) {
|
|
break;
|
|
}
|
|
if (cregs[6] & 0x10000000)
|
|
break;
|
|
micvpsw = M[((cregs[6] & 0xfffff8) >> 2) + 2];
|
|
vpsw = M[(micvpsw & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x\n", vpsw);
|
|
regs[2] = (regs[2] & 0xffffff00) | ((vpsw >> 16) & 0xf0);
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Reg2 %08x\n", regs[2]);
|
|
return 1;
|
|
|
|
case 0x13: /* RRB */
|
|
return vma_rrb(addr1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Handle VM Assists for SSM instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_ssm(uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 miccreg;
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
uint32 temp;
|
|
int flags;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM check %08x\n", addr1);
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Check if function enabled */
|
|
if ((cregs[6] & 0x01000000) != 0) {
|
|
miccreg = M[micblok + 5];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM micacf %08x\n", miccreg);
|
|
if ((miccreg & 0x00800000) == 0)
|
|
return 0;
|
|
}
|
|
/* Fetch Virtual Cr 0 */
|
|
miccreg = M[micblok + 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM miccreg %08x\n", miccreg);
|
|
temp = M[(miccreg & AMASK) >> 2];
|
|
if ((temp & 0x40000000) != 0)
|
|
return 0;
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = M[micblok + 2];
|
|
vpsw = M[(micvpsw & AMASK) >> 2];
|
|
/* Fetch new mask */
|
|
if (ReadByte(addr1, &temp))
|
|
return 0;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x d=%08x\n", vpsw, temp);
|
|
flags = vpsw >> 24;
|
|
flags ^= temp;
|
|
if (vpsw & 0x80000) {
|
|
/* If bits are not zero, exit */
|
|
if (temp & 0xb8)
|
|
return 0;
|
|
/* Check if DAT or PER changed */
|
|
if (flags & 0x44)
|
|
return 0;
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((micvpsw & MSIGN) && (flags & temp & 0x3) != 0)
|
|
return 0;
|
|
} else {
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((micvpsw & MSIGN) && (flags & temp) != 0)
|
|
return 0;
|
|
}
|
|
vpsw &= 0xffffff;
|
|
vpsw |= temp << 24;
|
|
M[(micvpsw & AMASK) >> 2] = vpsw;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new VPSW %08x\n", vpsw);
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for LPSW instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_lpsw(uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
uint32 npsw1;
|
|
uint32 npsw2;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "LPSW check %08x\n", addr1);
|
|
/* Quick check to exit */
|
|
if (per_en || (addr1 & 0x7) != 0)
|
|
return 0;
|
|
/* Check if function enabled */
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch new PSW */
|
|
if (ReadFull(addr1, &npsw1))
|
|
return 0;
|
|
if (ReadFull(addr1 + 4, &npsw2))
|
|
return 0;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new %08x %08x\n", npsw1, npsw2);
|
|
/* New PSW has WAIT or invalid ECmode bits */
|
|
if ((npsw1 & 0x20000) != 0)
|
|
return 0;
|
|
if ((npsw1 & 0x80000) != 0 &&
|
|
((npsw1 & 0xf800c0ff) != 0 || (npsw2 & 0xff000000) != 0))
|
|
return 0;
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = (M[micblok + 2] & AMASK) >> 2;
|
|
vpsw = M[micvpsw];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x %08x\n", vpsw, M[micvpsw + 1]);
|
|
/* Check if old PSW has PER set */
|
|
if ((vpsw & 0x40080000) == 0x40080000)
|
|
return 0;
|
|
/* Check if PSW changing BC/EC mode */
|
|
if (((vpsw ^ npsw1) & 0x80000) != 0)
|
|
return 0;
|
|
/* Check for interrupt */
|
|
if (npsw1 & 0x80000) { /* EC Mode */
|
|
/* Check if DAT changed */
|
|
if (((npsw1 ^ vpsw) & 0x04000000) != 0)
|
|
return 0;
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((M[micblok + 2] & MSIGN) && (npsw1 & 0x3000000) != 0
|
|
&& ((vpsw ^ npsw1) & npsw1 & 0x3000000) != 0)
|
|
return 0;
|
|
pmsk = (npsw1 >> 8) & 0xf;
|
|
cc = (npsw1 >> 12) & 3;
|
|
} else {
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((M[micblok + 2] & MSIGN) && (((vpsw ^ npsw1) & npsw1) & 0xff000000) != 0)
|
|
return 0;
|
|
pmsk = (npsw2 >> 24) & 0xf;
|
|
cc = (npsw2 >> 28) & 0x3;
|
|
}
|
|
M[micvpsw] = npsw1;
|
|
M[micvpsw + 1] = npsw2;
|
|
st_key = (npsw1 >> 16) & 0xf0;
|
|
/* Set CR6 to new problem state */
|
|
if (npsw1 & 0x10000)
|
|
cregs[6] |= 0x40000000;
|
|
else
|
|
cregs[6] &= 0xbfffffff;
|
|
PC = npsw2 & AMASK;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new VPSW %08x %08x\n", npsw1, cregs[6]);
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for SSK instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stssk(uint32 src1, uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 micrseg;
|
|
uint32 page;
|
|
uint32 seg;
|
|
uint32 segpage;
|
|
uint32 pagswp;
|
|
uint32 swpflg;
|
|
uint32 pagcore;
|
|
uint8 stk;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSK check %08x %08x\n", src1, addr1);
|
|
/* Check if enabled */
|
|
if ((cregs[6] & 0xe0000000) != MSIGN || (src1 & 0xf) != 0)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch SEGPAGE */
|
|
micrseg = M[micblok];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Micrseg %08x\n", micrseg);
|
|
if (micrseg & 0x2)
|
|
return 0;
|
|
/* Compute address to operate on */
|
|
page = addr1 >> 12;
|
|
if (micrseg & 0x1) {
|
|
seg = page >> 7;
|
|
page &= 0x7f;
|
|
} else {
|
|
seg = page >> 4;
|
|
page &= 0xf;
|
|
}
|
|
segpage = M[((micrseg & AMASK) >> 2) + seg];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Segpage %08x s=%x p=%x\n", segpage, seg, page);
|
|
if ((segpage >> 24) <= (addr1 >> 20))
|
|
return 0;
|
|
pagswp = M[((segpage & AMASK) >> 2) - 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagswp %08x\n", pagswp);
|
|
swpflg = M[((pagswp + (8 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "swpflg %08x\n", swpflg);
|
|
pagcore = M[((segpage + (2 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagcore %08x\n", pagcore);
|
|
/* Check if page is valid */
|
|
if ((page & 0x1) == 0)
|
|
pagcore >>= 16;
|
|
if (pagcore & 0xe)
|
|
return 0;
|
|
pagcore &= 0xfff0;
|
|
pagcore <<= 8;
|
|
pagcore |= addr1 & 0x800;
|
|
stk = key[pagcore>>11];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "real addr %08x %02x\n", pagcore, stk);
|
|
key[pagcore>>11] = (stk & 0xf) | (src1 & 0xf0);
|
|
if (addr1 & 0x800) {
|
|
swpflg = (swpflg & 0xfcffff00) | (((uint32)stk & 0x6) << 25) | (src1 & 0xff);
|
|
} else {
|
|
swpflg = (swpflg & 0xf3ff00ff) | (((uint32)stk & 0x6) << 23) | ((src1 & 0xff) << 8);
|
|
}
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "swpflg %08x\n", swpflg);
|
|
M[((pagswp + (8 * page)) & AMASK) >> 2] = swpflg;
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for ISK instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stisk(uint8 reg1, uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 micrseg;
|
|
uint32 page;
|
|
uint32 seg;
|
|
uint32 segpage;
|
|
uint32 pagswp;
|
|
uint32 swpflg;
|
|
uint32 pagcore;
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
uint8 stk;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "ISK check %02x %08x\n", reg1, addr1);
|
|
/* Check if enabled */
|
|
if ((cregs[6] & 0xe0000000) != MSIGN)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch SEGPAGE */
|
|
micrseg = M[micblok];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Micrseg %08x\n", micrseg);
|
|
if (micrseg & 0x2)
|
|
return 0;
|
|
/* Compute address to operate on */
|
|
page = addr1 >> 12;
|
|
if (micrseg & 0x1) {
|
|
seg = page >> 7;
|
|
page &= 0x7f;
|
|
} else {
|
|
seg = page >> 4;
|
|
page &= 0xf;
|
|
}
|
|
segpage = M[((micrseg & AMASK) >> 2) + seg];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Segpage %08x s=%x p=%x\n", segpage, seg, page);
|
|
if ((segpage >> 24) <= (addr1 >> 20))
|
|
return 0;
|
|
pagswp = M[((segpage & AMASK) >> 2) - 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagswp %08x\n", pagswp);
|
|
swpflg = M[((pagswp + (8 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "swpflg %08x\n", swpflg);
|
|
pagcore = M[((segpage + (2 * page)) & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagcore %08x\n", pagcore);
|
|
/* Get key value */
|
|
if (addr1 & 0x800) {
|
|
stk = swpflg & 0xfe;
|
|
} else {
|
|
stk = (swpflg >> 8) & 0xfe;
|
|
}
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "stk %02x\n", stk);
|
|
/* Check if page is valid */
|
|
if ((page & 0x1) == 0)
|
|
pagcore >>= 16;
|
|
if ((pagcore & 0xe) == 0) {
|
|
pagcore &= 0xfff0;
|
|
pagcore <<= 8;
|
|
pagcore |= addr1 & 0x800;
|
|
stk |= key[pagcore>>11] & 0x6;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "real addr %08x %02x\n", pagcore, stk);
|
|
}
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = (M[micblok + 2] & AMASK) >> 2;
|
|
vpsw = M[micvpsw];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x %08x\n", vpsw, M[micvpsw + 1]);
|
|
if ((vpsw & 0x80000) == 0) { /* BC Mode */
|
|
stk &= 0xf0;
|
|
}
|
|
regs[reg1] = (regs[reg1] & 0xffffff00) | (uint32)stk;
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for SVC instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stsvc(uint8 reg)
|
|
{
|
|
uint32 micblok;
|
|
uint32 micrseg;
|
|
uint32 micvpsw;
|
|
uint32 segpage;
|
|
uint32 pagcore;
|
|
uint32 psa;
|
|
uint32 vpsw;
|
|
uint32 npsw1;
|
|
uint32 npsw2;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SVC check %02x\n", reg);
|
|
if (per_en || reg == 76)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = (M[micblok + 2] & AMASK) >> 2;
|
|
vpsw = M[micvpsw];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x %08x\n", vpsw, M[micvpsw + 1]);
|
|
/* Check if old PSW has PER set */
|
|
if ((vpsw & 0x40080000) == 0x40080000)
|
|
return 0;
|
|
/* Fetch SEGPAGE */
|
|
micrseg = M[micblok];
|
|
segpage = M[(micrseg & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Segpage %08x\n", segpage);
|
|
pagcore = M[(segpage & AMASK) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "pagcore %08x\n", pagcore);
|
|
psa = pagcore >> 16;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "psa %08x\n", psa);
|
|
/* determine if page is valid */
|
|
/* Check if 4k or 2k paging */
|
|
if ((micrseg & 02) != 0) {
|
|
/* 2K paging */
|
|
if (psa & 0x6)
|
|
return 0;
|
|
psa &= 0xfff8;
|
|
psa <<= 7;
|
|
} else {
|
|
/* 4K paging */
|
|
if (psa & 0xe)
|
|
return 0;
|
|
psa &= 0xfff0;
|
|
psa <<= 8;
|
|
}
|
|
/* PSA now points correctly */
|
|
npsw1 = M[(psa + 0x60) >> 2];
|
|
npsw2 = M[(psa + 0x64) >> 2];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new PSW %08x %08x\n", npsw1, npsw2);
|
|
/* New PSW has WAIT or invalid ECmode bits */
|
|
if ((npsw1 & 0x20000) != 0)
|
|
return 0;
|
|
if ((npsw1 & 0x80000) != 0 &&
|
|
((npsw1 & 0xf800c0ff) != 0 || (npsw2 & 0xff000000) != 0))
|
|
return 0;
|
|
/* Check if PSW changing BC/EC mode */
|
|
if (((vpsw ^ npsw1) & 0x80000) != 0)
|
|
return 0;
|
|
/* Check for interrupt */
|
|
if (npsw1 & 0x80000) {
|
|
/* Check if DAT changed */
|
|
if (((npsw1 ^ vpsw) & 0x04000000) != 0)
|
|
return 0;
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((M[micblok + 2] & MSIGN) && (npsw1 & 0x3000000) != 0
|
|
&& ((vpsw ^ npsw1) & npsw1 & 0x3000000) != 0)
|
|
return 0;
|
|
} else {
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((M[micblok + 2] & MSIGN) && (((vpsw ^ npsw1) & npsw1) & 0xff000000) != 0)
|
|
return 0;
|
|
}
|
|
/* Construct old PSW */
|
|
if (vpsw & 0x80000) {
|
|
uint32 temp1;
|
|
/* Generate first word */
|
|
temp1 = (vpsw & 0xff0f0f00) |
|
|
(((uint32)st_key) << 16) |
|
|
(((uint32)cc) << 12) |
|
|
(((uint32)pmsk) << 8);
|
|
/* Save old PSW */
|
|
M[(psa + 0x20) >> 2] = temp1;
|
|
M[(psa + 0x24) >> 2] = PC & AMASK;
|
|
M[(psa + 0x88) >> 2] = (1 << 17) | reg;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Old PSW %08x %08x\n", temp1, PC);
|
|
} else {
|
|
uint32 temp1;
|
|
uint32 temp2;
|
|
/* Generate first word */
|
|
temp1 = (vpsw & 0xff0f0000) |
|
|
(((uint32)st_key) << 16) |
|
|
((uint32)reg);
|
|
/* Generate second word. */
|
|
temp2 = (((uint32)1) << 30) |
|
|
(((uint32)cc) << 28) |
|
|
(((uint32)pmsk) << 24) |
|
|
(PC & AMASK);
|
|
/* Save old PSW */
|
|
M[(psa + 0x20) >> 2] = temp1;
|
|
M[(psa + 0x24) >> 2] = temp2;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Old PSW %08x %08x\n", temp1, temp2);
|
|
}
|
|
/* Set machine for new PSW */
|
|
M[micvpsw] = npsw1;
|
|
M[micvpsw + 1] = npsw2;
|
|
if (npsw1 & 0x80000) {
|
|
pmsk = (npsw1 >> 8) & 0xf;
|
|
cc = (npsw1 >> 12) & 3;
|
|
} else {
|
|
pmsk = (npsw2 >> 24) & 0xf;
|
|
cc = (npsw2 >> 28) & 0x3;
|
|
}
|
|
st_key = (npsw1 >> 16) & 0xf0;
|
|
/* Set CR6 to new problem state */
|
|
if (npsw1 & 0x10000)
|
|
cregs[6] |= 0x40000000;
|
|
else
|
|
cregs[6] &= 0xbfffffff;
|
|
PC = npsw2 & AMASK;
|
|
key[psa >> 11] |= 0x6;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new VPSW %08x %08x %08x\n", npsw1, npsw2, cregs[6]);
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for LRA instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_lra(uint8 reg, uint32 addr1, uint8 *cc)
|
|
{
|
|
#if 0
|
|
uint32 micblok;
|
|
uint32 micrseg;
|
|
uint32 miccreg;
|
|
uint32 extcr0;
|
|
uint32 extcr1;
|
|
uint32 page;
|
|
uint32 seg;
|
|
uint32 v_seg_len;
|
|
uint32 r_seg_len;
|
|
uint32 v_seg_tbl;
|
|
uint32 r_seg_tbl;
|
|
uint32 segpage;
|
|
uint32 pagswp;
|
|
uint32 swpflg;
|
|
uint32 pagcore;
|
|
uint32 micvpsw;
|
|
uint32 temp;
|
|
uint32 addr2;
|
|
uint8 stk;
|
|
int page_shift;
|
|
int page_mask;
|
|
int page_index;
|
|
int pte_avail;
|
|
int pte_mbz;
|
|
int pte_shift;
|
|
int pte_len_shift;
|
|
int seg_shift;
|
|
int seg_mask;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "LRA check %02x %08x\n", reg, addr1);
|
|
/* Check if enabled */
|
|
if ((cregs[6] & 0xd0000000) != MSIGN)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Fetch SEGPAGE */
|
|
micrseg = M[micblok];
|
|
miccreg = M[micblok + 4];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Micrseg %08x\n", micrseg);
|
|
extcr0 = M[miccreg];
|
|
extcr1 = M[miccreg + 1];
|
|
v_seg_len = (((extcr1 >> 24) & 0xff) + 1) << 4;
|
|
r_seg_len = (((micrseg >> 24) & 0xff) + 1) << 4;
|
|
v_seg_tbl = extcr1 & AMASK;
|
|
r_seg_tbl = micrseg & AMASK;
|
|
/* 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((extcr0 >> 22) & 03) {
|
|
default: /* Let operating system handle this. */
|
|
return 0;
|
|
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((extcr0 >> 19) & 07) {
|
|
default: /* Let operating system handle this. */
|
|
return 0;
|
|
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;
|
|
|
|
addr1 &= AMASK;
|
|
/* Segment number to word address */
|
|
seg = (addr1 >> seg_shift) & seg_mask;
|
|
if (seg > v_seg_len) {
|
|
*cc = 3;
|
|
regs[reg] = addr1;
|
|
return 1;
|
|
}
|
|
addr2 = (((seg << 2) + r_seg_tbl) & AMASK);
|
|
if (ReadFull(addr2, &temp))
|
|
return 0;
|
|
/* Check if entry valid */
|
|
if (temp & 0xf000007) {
|
|
return 0; /* Nope, let OS handle it */
|
|
}
|
|
page = (addr1 >> page_shift) & page_index;
|
|
addr2 = (temp >> 28) + 1;
|
|
/* Check if over end of table */
|
|
if ((page >> pte_len_shift) >= addr2) {
|
|
return 0; /* Let OS handle it */
|
|
}
|
|
|
|
/* Now we need to fetch the actual entry */
|
|
addr2 = (temp & 0x00fffffe) + (page << 1);
|
|
addr2 &= AMASK;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "Segpage %08x s=%x p=%x\n", segpage, seg, page);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Handle VM Assists for STNSM instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stnsm(uint8 reg, uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 miccreg;
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
uint32 temp;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "STNSM check %02x %08x\n", reg, addr1);
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Check if function enabled */
|
|
if ((cregs[6] & 0x01000000) != 0) {
|
|
miccreg = M[micblok + 5];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM micacf %08x\n", miccreg);
|
|
if ((miccreg & 0x00800000) == 0)
|
|
return 0;
|
|
}
|
|
/* Fetch Virtual Cr 0 */
|
|
miccreg = M[micblok + 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM miccreg %08x\n", miccreg);
|
|
temp = M[(miccreg & AMASK) >> 2];
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = M[micblok + 2];
|
|
vpsw = M[(micvpsw & AMASK) >> 2];
|
|
/* Don't allow change of PER or DAT */
|
|
if (vpsw & 0x80000 && (reg & 0x44) != 0x44)
|
|
return 0;
|
|
/* Update Mask */
|
|
temp = (vpsw >> 24) & 0xff;
|
|
vpsw &= ((uint32)reg << 24) | 0xffffff;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x d=%08x\n", vpsw, temp);
|
|
/* Save old mask in addr1 */
|
|
if (WriteByte(addr1, temp))
|
|
return 0;
|
|
M[(micvpsw & AMASK) >> 2] = vpsw;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new VPSW %08x\n", vpsw);
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for STOSM instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stosm(uint8 reg, uint32 addr1)
|
|
{
|
|
uint32 micblok;
|
|
uint32 miccreg;
|
|
uint32 micvpsw;
|
|
uint32 vpsw;
|
|
uint32 temp;
|
|
int flags;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "STOSM check %02x %08x\n", reg, addr1);
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Check if function enabled */
|
|
if ((cregs[6] & 0x01000000) != 0) {
|
|
miccreg = M[micblok + 5];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM micacf %08x\n", miccreg);
|
|
if ((miccreg & 0x00800000) == 0)
|
|
return 0;
|
|
}
|
|
/* Fetch Virtual Cr 0 */
|
|
miccreg = M[micblok + 1];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "SSM miccreg %08x\n", miccreg);
|
|
temp = M[(miccreg & AMASK) >> 2];
|
|
if ((temp & 0x40000000) != 0)
|
|
return 0;
|
|
/* Fetch the virtual PSW */
|
|
micvpsw = M[micblok + 2];
|
|
vpsw = M[(micvpsw & AMASK) >> 2];
|
|
flags = (vpsw >> 24) ^ reg;
|
|
if (vpsw & 0x80000) {
|
|
/* Don't allow change of PER or DAT */
|
|
if ((flags & 0xfc) != 0x0)
|
|
return 0;
|
|
/* If enabling an interrupt and one pending, abort */
|
|
if ((micvpsw & MSIGN) && (flags & reg & 0x3) != 0)
|
|
return 0;
|
|
} else {
|
|
/* Check if IRQ pending and enable interrupt */
|
|
if ((vpsw & MSIGN) && ((flags & reg) & 0xff) != 0)
|
|
return 0;
|
|
}
|
|
/* Save old mask */
|
|
temp = (vpsw >> 24) & 0xff;
|
|
/* Update Mask */
|
|
vpsw |= ((uint32)reg << 24);
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "VPSW %08x d=%08x\n", vpsw, temp);
|
|
if (WriteByte(addr1, temp))
|
|
return 0;
|
|
M[(micvpsw & AMASK) >> 2] = vpsw;
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "new VPSW %08x\n", vpsw);
|
|
return 1;
|
|
}
|
|
|
|
/* Handle VM Assists for STCTL instructions
|
|
* return 0 if assist could not be completed. Return 1 if successful */
|
|
int
|
|
vma_stctl(uint8 reg, uint32 addr1)
|
|
{
|
|
int reg1 = R1(reg);
|
|
uint32 miccreg;
|
|
uint32 micblok;
|
|
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "STCL check %02x %08x\n", reg, addr1);
|
|
if ((addr1 & 0x3) != 0)
|
|
return 0;
|
|
micblok = (cregs[6] & 0xfffff8) >> 2;
|
|
/* Check if function enabled */
|
|
if ((cregs[6] & 0x01000000) != 0) {
|
|
miccreg = M[micblok + 5];
|
|
sim_debug(DEBUG_VMA, &cpu_dev, "STCL micacf %08x\n", miccreg);
|
|
if ((miccreg & 0x00800000) == 0)
|
|
return 0;
|
|
}
|
|
/* Point to CR0 */
|
|
miccreg = M[micblok + 1];
|
|
reg = R2(reg);
|
|
for (;;) {
|
|
uint32 temp;
|
|
temp = M[miccreg + reg1];
|
|
if (WriteFull(addr1, temp))
|
|
return 0;
|
|
if (reg1 == reg)
|
|
break;
|
|
reg1++;
|
|
reg1 &= 0xf;
|
|
addr1 += 4;
|
|
} ;
|
|
return 1;
|
|
}
|
|
|