/* 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;
}

