diff --git a/IBM360/ibm360_cpu.c b/IBM360/ibm360_cpu.c index 14dd526..6f80d7b 100644 --- a/IBM360/ibm360_cpu.c +++ b/IBM360/ibm360_cpu.c @@ -1,6 +1,6 @@ /* ibm360_cpu.c: ibm 360 cpu simulator. - Copyright (c) 2019-2020, Richard Cornwell + 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"), @@ -242,6 +242,9 @@ int timer_tics; /* Interval Timer is ever 3 tics */ #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 @@ -277,6 +280,16 @@ 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); +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); @@ -365,6 +378,8 @@ MTAB cpu_mod[] = { { 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"}, @@ -377,16 +392,20 @@ 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, dev_debug, + NULL, DEV_DEBUG, 0, cpu_debug, NULL, NULL, &cpu_help, NULL, NULL, &cpu_description }; -void post_extirq() { +void +post_extirq() +{ cpu_unit[0].flags |= EXT_IRQ; } -void storepsw(uint32 addr, uint16 ircode) { +void +storepsw(uint32 addr, uint16 ircode) +{ uint32 word; uint32 word2; irqaddr = addr + 0x40; @@ -508,7 +527,9 @@ void storepsw(uint32 addr, uint16 ircode) { /* * Translate an address from virtual to physical. */ -int TransAddr(uint32 va, uint32 *pa) { +int +TransAddr(uint32 va, uint32 *pa) +{ uint32 seg; uint32 page; uint32 entry; @@ -640,7 +661,9 @@ int TransAddr(uint32 va, uint32 *pa) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int ReadFull(uint32 addr, uint32 *data) { +int +ReadFull(uint32 addr, uint32 *data) +{ uint32 pa; int offset; @@ -713,7 +736,9 @@ int ReadFull(uint32 addr, uint32 *data) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int ReadByte(uint32 addr, uint32 *data) { +int +ReadByte(uint32 addr, uint32 *data) +{ if (ReadFull(addr & (~0x3), data)) return 1; @@ -727,7 +752,9 @@ int ReadByte(uint32 addr, uint32 *data) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int ReadHalf(uint32 addr, uint32 *data) { +int +ReadHalf(uint32 addr, uint32 *data) +{ /* Check if unaligned access */ if (addr & 0x1) { @@ -766,7 +793,9 @@ int ReadHalf(uint32 addr, uint32 *data) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int WriteFull(uint32 addr, uint32 data) { +int +WriteFull(uint32 addr, uint32 data) +{ int offset; uint32 pa; uint32 pa2; @@ -877,7 +906,9 @@ int WriteFull(uint32 addr, uint32 data) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int WriteByte(uint32 addr, uint32 data) { +int +WriteByte(uint32 addr, uint32 data) +{ uint32 mask; uint32 pa; int offset; @@ -934,7 +965,9 @@ int WriteByte(uint32 addr, uint32 data) { * and alignment restrictions. Return 1 if failure, 0 if * success. */ -int WriteHalf(uint32 addr, uint32 data) { +int +WriteHalf(uint32 addr, uint32 data) +{ uint32 mask; uint32 pa; uint32 pa2; @@ -1492,6 +1525,9 @@ opr: 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) { @@ -1513,6 +1549,9 @@ opr: 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); @@ -1530,11 +1569,19 @@ opr: 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) { + 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 { @@ -1580,6 +1627,8 @@ opr: case OP_LPSW: if (flags & PROBLEM) { + if (QVMA && vma_lpsw(addr1)) + break; storepsw(OPPSW, IRC_PRIV); goto supress; } else if ((addr1 & 0x7) != 0) { @@ -2343,6 +2392,9 @@ save_dbl: if ((cpu_unit[0].flags & FEAT_DAT) == 0) { storepsw(OPPSW, IRC_OPR); } else if (flags & PROBLEM) { + /* Try to do quick LRA */ + if (QVMA && vma_lra(reg, addr1)) + break; storepsw(OPPSW, IRC_PRIV); } else { uint32 seg; @@ -2383,7 +2435,7 @@ save_dbl: /* Check if over end of table */ if ((page >> pte_len_shift) >= addr2) { cc = 3; - regs[reg1] = addr1; + regs[reg1] = addr2; per_mod |= 1 << reg1; break; } @@ -2982,6 +3034,9 @@ save_dbl: 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; } @@ -3104,7 +3159,7 @@ save_dbl: storepsw(OPPSW, IRC_OPR); goto supress; } - regs[2] = (regs[2] & 0xffffff00) | st_key; + regs[2] = (regs[2] & 0xffffff00) | (st_key & 0xf0); break; case 0xd: /* PTLB */ if ((cpu_unit[0].flags & FEAT_DAT) == 0) { @@ -3145,6 +3200,9 @@ save_dbl: 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; } @@ -3190,6 +3248,9 @@ save_dbl: 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; } @@ -3343,6 +3404,9 @@ save_dbl: 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 */ @@ -3372,6 +3436,9 @@ save_dbl: } 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); @@ -5434,8 +5501,8 @@ lpsw: dat_en = 0; irq_en = (sysmsk != 0); per_en = 0; - pmsk = (src2 >> 24) & 0xf; cc = (src2 >> 28) & 0x3; + pmsk = (src2 >> 24) & 0xf; } irq_pend = 1; st_key = (src1 >> 16) & 0xf0; @@ -5827,10 +5894,11 @@ dec_div(int op, uint32 addr1, uint8 len1, uint32 addr2, uint8 len2) dec_store(a, addr1, len, sa); } - + /* Reset */ -t_stat cpu_reset (DEVICE *dptr) +t_stat +cpu_reset (DEVICE *dptr) { int i; @@ -5941,7 +6009,8 @@ check_tod_irq() /* Memory examine */ -t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +t_stat +cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; uint32 byte; @@ -6016,7 +6085,8 @@ t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) /* Memory deposit */ -t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +t_stat +cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) { uint32 addr = (uint32) exta; uint32 offset = 8 * (3 - (addr & 0x3)); @@ -6092,7 +6162,8 @@ t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) /* Memory allocation */ -t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +t_stat +cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { int32 mc = 0; int32 i, clim; @@ -6228,7 +6299,8 @@ 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) +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); diff --git a/IBM360/ibm360_defs.h b/IBM360/ibm360_defs.h index 77373ce..3e19561 100644 --- a/IBM360/ibm360_defs.h +++ b/IBM360/ibm360_defs.h @@ -105,7 +105,8 @@ typedef struct dib { #define FEAT_DAT (1 << (UNIT_V_UF + 5)) /* Dynamic address translation */ #define FEAT_EFP (1 << (UNIT_V_UF + 6)) /* Extended floating point */ #define FEAT_370 (1 << (UNIT_V_UF + 7)) /* Is a 370 */ -#define EXT_IRQ (1 << (UNIT_V_UF + 8)) /* External interrupt */ +#define FEAT_VMA (1 << (UNIT_V_UF + 8)) /* Enable VM assists */ +#define EXT_IRQ (1 << (UNIT_V_UF + 9)) /* External interrupt */ /* low addresses */ #define IPSW 0x00 /* IPSW */ @@ -367,8 +368,10 @@ extern const uint8 ebcdic_to_ascii[256]; #define DEBUG_IRQ 0x0000100 /* Show IRQ requests */ #define DEBUG_CDATA 0x0000200 /* Show channel data */ #define DEBUG_TRACE 0x0000400 /* Show instruction trace */ +#define DEBUG_VMA 0x0000800 /* Show VM Assist functions */ extern DEBTAB dev_debug[]; +extern DEBTAB cpu_debug[]; extern DEBTAB crd_debug[]; extern DEVICE cpu_dev; diff --git a/IBM360/ibm360_sys.c b/IBM360/ibm360_sys.c index 9d45606..0516039 100644 --- a/IBM360/ibm360_sys.c +++ b/IBM360/ibm360_sys.c @@ -96,9 +96,19 @@ DEBTAB dev_debug[] = { {"DETAIL", DEBUG_DETAIL, "Show details about device"}, {"EXP", DEBUG_EXP, "Show exception information"}, {"POS", DEBUG_POS, "Dasd positioning information"}, + {0, 0} +}; + +/* Simulator debug controls */ +DEBTAB cpu_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, {"INST", DEBUG_INST, "Show instruction execution"}, {"CDATA", DEBUG_CDATA, "Show channel data"}, {"TRACE", DEBUG_TRACE, "Show instruction history"}, + {"VMA", DEBUG_VMA, "Show assist history"}, {0, 0} }; diff --git a/IBM360/ibm360_vma.c b/IBM360/ibm360_vma.c new file mode 100644 index 0000000..9357fee --- /dev/null +++ b/IBM360/ibm360_vma.c @@ -0,0 +1,731 @@ +/* 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 uint8 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; + uint32 micvpsw; + uint32 vpsw; + 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 micseg; + uint32 vpsw; + uint32 page; + uint32 seg; + + 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 miccreg; + uint32 micvpsw; + uint32 vpsw; + uint32 vpsw2; + 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) +{ + sim_debug(DEBUG_VMA, &cpu_dev, "LRA check %02x %08x\n", reg, addr1); + 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; + int flags; + + 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; +} + diff --git a/doc/ibm360.doc b/doc/ibm360.doc index c101a0d..a9d359b 100644 Binary files a/doc/ibm360.doc and b/doc/ibm360.doc differ diff --git a/makefile b/makefile index ec871c9..3f4de00 100644 --- a/makefile +++ b/makefile @@ -2064,7 +2064,7 @@ IBM360D = ${SIMHD}/IBM360 IBM360 = ${IBM360D}/ibm360_cpu.c ${IBM360D}/ibm360_sys.c ${IBM360D}/ibm360_con.c \ ${IBM360D}/ibm360_chan.c ${IBM360D}/ibm360_cdr.c ${IBM360D}/ibm360_cdp.c \ ${IBM360D}/ibm360_mt.c ${IBM360D}/ibm360_lpr.c ${IBM360D}/ibm360_dasd.c \ - ${IBM360D}/ibm360_com.c ${IBM360D}/ibm360_scom.c + ${IBM360D}/ibm360_com.c ${IBM360D}/ibm360_scom.c ${IBM360D}/ibm360_vma.c IBM360_OPT = -I $(IBM360D) -DIBM360 -DUSE_64BIT -DUSE_SIM_CARD IBM360_OPT32 = -I $(IBM360D) -DIBM360 -DUSE_SIM_CARD