/* GENERAL2.C (c) Copyright Roger Bowler, 1994-2012 */ /* (c) Copyright Jan Jaeger, 1999-2012 */ /* Hercules CPU Emulator - Instructions N-Z */ /* */ /* Released under "The Q Public License Version 1" */ /* (http://www.hercules-390.org/herclic.html) as modifications to */ /* Hercules. */ /* UPT & CFC (c) Copyright Peter Kuschnerus, 1999-2009*/ /* Interpretive Execution - (c) Copyright Jan Jaeger, 1999-2012 */ /* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2012 */ /*-------------------------------------------------------------------*/ /* This module implements general instructions N-Z of the */ /* S/370 and ESA/390 architectures, as described in the manuals */ /* GA22-7000-03 System/370 Principles of Operation */ /* SA22-7201-06 ESA/390 Principles of Operation */ /*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* Additional credits: */ /* Corrections to shift instructions by Jay Maynard, Jan Jaeger */ /* Branch tracing by Jan Jaeger */ /* Instruction decode by macros - Jan Jaeger */ /* Prevent TOD from going backwards in time - Jan Jaeger */ /* Fix STCKE instruction - Bernard van der Helm */ /* Instruction decode rework - Jan Jaeger */ /* Make STCK update the TOD clock - Jay Maynard */ /* Fix address wraparound in MVO - Jan Jaeger */ /* PLO instruction - Jan Jaeger */ /* Modifications for Interpretive Execution (SIE) by Jan Jaeger */ /* Clear TEA on data exception - Peter Kuschnerus v209*/ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #if !defined(_HENGINE_DLL_) #define _HENGINE_DLL_ #endif #if !defined(_GENERAL2_C_) #define _GENERAL2_C_ #endif #include "hercules.h" #include "opcode.h" #include "inline.h" #include "clock.h" /*-------------------------------------------------------------------*/ /* 16 OR - Or Register [RR] */ /*-------------------------------------------------------------------*/ DEF_INST(or_register) { int r1, r2; /* Values of R fields */ RR0(inst, regs, r1, r2); /* OR second operand with first and set condition code */ regs->psw.cc = ( regs->GR_L(r1) |= regs->GR_L(r2) ) ? 1 : 0; } /*-------------------------------------------------------------------*/ /* 56 O - Or [RX] */ /*-------------------------------------------------------------------*/ DEF_INST(or) { int r1; /* Value of R field */ int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ U32 n; /* 32-bit operand values */ RX(inst, regs, r1, b2, effective_addr2); /* Load second operand from operand address */ n = ARCH_DEP(vfetch4) ( effective_addr2, b2, regs ); /* OR second operand with first and set condition code */ regs->psw.cc = ( regs->GR_L(r1) |= n ) ? 1 : 0; } /*-------------------------------------------------------------------*/ /* 96 OI - Or Immediate [SI] */ /*-------------------------------------------------------------------*/ DEF_INST(or_immediate) { BYTE i2; /* Immediate operand byte */ int b1; /* Base of effective addr */ VADR effective_addr1; /* Effective address */ BYTE *dest; /* Pointer to target byte */ SI(inst, regs, i2, b1, effective_addr1); ITIMER_SYNC(effective_addr1,1,regs); /* Get byte mainstor address */ dest = MADDR (effective_addr1, b1, regs, ACCTYPE_WRITE, regs->psw.pkey ); /* OR byte with immediate operand, setting condition code */ regs->psw.cc = ((*dest |= i2) != 0); ITIMER_UPDATE(effective_addr1,1,regs); } /*-------------------------------------------------------------------*/ /* D6 OC - Or Characters [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(or_character) { int len, len2, len3; /* Lengths to copy */ int b1, b2; /* Base register numbers */ VADR addr1, addr2; /* Virtual addresses */ BYTE *dest1, *dest2; /* Destination addresses */ BYTE *source1, *source2; /* Source addresses */ BYTE *sk1, *sk2; /* Storage key addresses */ int i; /* Loop counter */ int cc = 0; /* Condition code */ SS_L(inst, regs, len, b1, addr1, b2, addr2); ITIMER_SYNC(addr1,len,regs); ITIMER_SYNC(addr2,len,regs); /* Quick out for 1 byte (no boundary crossed) */ if (unlikely(len == 0)) { source1 = MADDR (addr2, b2, regs, ACCTYPE_READ, regs->psw.pkey); dest1 = MADDR (addr1, b1, regs, ACCTYPE_WRITE, regs->psw.pkey); *dest1 |= *source1; regs->psw.cc = (*dest1 != 0); ITIMER_UPDATE(addr1,len,regs); return; } /* There are several scenarios (in optimal order): * (1) dest boundary and source boundary not crossed * (2) dest boundary not crossed and source boundary crossed * (3) dest boundary crossed and source boundary not crossed * (4) dest boundary and source boundary are crossed * (a) dest and source boundary cross at the same time * (b) dest boundary crossed first * (c) source boundary crossed first */ /* Translate addresses of leftmost operand bytes */ dest1 = MADDRL (addr1, len+1, b1, regs, ACCTYPE_WRITE_SKP, regs->psw.pkey); sk1 = regs->dat.storkey; source1 = MADDR (addr2, b2, regs, ACCTYPE_READ, regs->psw.pkey); if ( NOCROSS2K(addr1,len) ) { if ( NOCROSS2K(addr2,len) ) { /* (1) - No boundaries are crossed */ for (i = 0; i <= len; i++) if ( (*dest1++ |= *source1++) ) cc = 1; } else { /* (2) - Second operand crosses a boundary */ len2 = 0x800 - (addr2 & 0x7FF); source2 = MADDR ((addr2 + len2) & ADDRESS_MAXWRAP(regs), b2, regs, ACCTYPE_READ, regs->psw.pkey); for ( i = 0; i < len2; i++) if ( (*dest1++ |= *source1++) ) cc = 1; len2 = len - len2; for ( i = 0; i <= len2; i++) if ( (*dest1++ |= *source2++) ) cc = 1; } *sk1 |= (STORKEY_REF | STORKEY_CHANGE); } else { /* First operand crosses a boundary */ len2 = 0x800 - (addr1 & 0x7FF); dest2 = MADDR ((addr1 + len2) & ADDRESS_MAXWRAP(regs), b1, regs, ACCTYPE_WRITE_SKP, regs->psw.pkey); sk2 = regs->dat.storkey; if ( NOCROSS2K(addr2,len) ) { /* (3) - First operand crosses a boundary */ for ( i = 0; i < len2; i++) if ( (*dest1++ |= *source1++) ) cc = 1; len2 = len - len2; for ( i = 0; i <= len2; i++) if ( (*dest2++ |= *source1++) ) cc = 1; } else { /* (4) - Both operands cross a boundary */ len3 = 0x800 - (addr2 & 0x7FF); source2 = MADDR ((addr2 + len3) & ADDRESS_MAXWRAP(regs), b2, regs, ACCTYPE_READ, regs->psw.pkey); if (len2 == len3) { /* (4a) - Both operands cross at the same time */ for ( i = 0; i < len2; i++) if ( (*dest1++ |= *source1++) ) cc = 1; len2 = len - len2; for ( i = 0; i <= len2; i++) if ( (*dest2++ |= *source2++) ) cc = 1; } else if (len2 < len3) { /* (4b) - First operand crosses first */ for ( i = 0; i < len2; i++) if ( (*dest1++ |= *source1++) ) cc = 1; len2 = len3 - len2; for ( i = 0; i < len2; i++) if ( (*dest2++ |= *source1++) ) cc = 1; len2 = len - len3; for ( i = 0; i <= len2; i++) if ( (*dest2++ |= *source2++) ) cc = 1; } else { /* (4c) - Second operand crosses first */ for ( i = 0; i < len3; i++) if ( (*dest1++ |= *source1++) ) cc = 1; len3 = len2 - len3; for ( i = 0; i < len3; i++) if ( (*dest1++ |= *source2++) ) cc = 1; len3 = len - len2; for ( i = 0; i <= len3; i++) if ( (*dest2++ |= *source2++) ) cc = 1; } } *sk1 |= (STORKEY_REF | STORKEY_CHANGE); *sk2 |= (STORKEY_REF | STORKEY_CHANGE); } regs->psw.cc = cc; ITIMER_UPDATE(addr1,len,regs); } /*-------------------------------------------------------------------*/ /* F2 PACK - Pack [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(pack) { int l1, l2; /* Lenght values */ int b1, b2; /* Values of base registers */ VADR effective_addr1, effective_addr2; /* Effective addresses */ int i, j; /* Loop counters */ BYTE sbyte; /* Source operand byte */ BYTE dbyte; /* Destination operand byte */ SS(inst, regs, l1, l2, b1, effective_addr1, b2, effective_addr2); /* If operand 1 crosses a page, make sure both pages are accessable */ if((effective_addr1 & PAGEFRAME_PAGEMASK) != ((effective_addr1 + l1) & PAGEFRAME_PAGEMASK)) ARCH_DEP(validate_operand) (effective_addr1, b1, l1, ACCTYPE_WRITE_SKP, regs); /* If operand 2 crosses a page, make sure both pages are accessable */ if((effective_addr2 & PAGEFRAME_PAGEMASK) != ((effective_addr2 + l2) & PAGEFRAME_PAGEMASK)) ARCH_DEP(validate_operand) (effective_addr2, b2, l2, ACCTYPE_READ, regs); /* Exchange the digits in the rightmost byte */ effective_addr1 += l1; effective_addr2 += l2; sbyte = ARCH_DEP(vfetchb) ( effective_addr2, b2, regs ); dbyte = ( (sbyte << 4) | (sbyte >> 4) ) & 0xff; ARCH_DEP(vstoreb) ( dbyte, effective_addr1, b1, regs ); /* Process remaining bytes from right to left */ for (i = l1, j = l2; i > 0; i--) { /* Fetch source bytes from second operand */ if (j-- > 0) { sbyte = ARCH_DEP(vfetchb) ( --effective_addr2, b2, regs ); dbyte = sbyte & 0x0F; if (j-- > 0) { effective_addr2 &= ADDRESS_MAXWRAP(regs); sbyte = ARCH_DEP(vfetchb) ( --effective_addr2, b2, regs ); dbyte |= (sbyte << 4) & 0xff; } } else { dbyte = 0; } /* Store packed digits at first operand address */ ARCH_DEP(vstoreb) ( dbyte, --effective_addr1, b1, regs ); /* Wraparound according to addressing mode */ effective_addr1 &= ADDRESS_MAXWRAP(regs); effective_addr2 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ } #if defined(FEATURE_PERFORM_LOCKED_OPERATION) /*-------------------------------------------------------------------*/ /* EE PLO - Perform Locked Operation [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(perform_locked_operation) { int r1, r3; /* Lenght values */ int b2, b4; /* Values of base registers */ VADR effective_addr2, effective_addr4; /* Effective addresses */ SS(inst, regs, r1, r3, b2, effective_addr2, b4, effective_addr4); if(regs->GR_L(0) & PLO_GPR0_RESV) regs->program_interrupt(regs, PGM_SPECIFICATION_EXCEPTION); if(regs->GR_L(0) & PLO_GPR0_T) switch(regs->GR_L(0) & PLO_GPR0_FC) { case PLO_CL: case PLO_CLG: case PLO_CS: case PLO_CSG: case PLO_DCS: case PLO_DCSG: case PLO_CSST: case PLO_CSSTG: case PLO_CSDST: case PLO_CSDSTG: case PLO_CSTST: case PLO_CSTSTG: #if defined(FEATURE_ESAME) case PLO_CLGR: case PLO_CLX: case PLO_CSGR: case PLO_CSX: case PLO_DCSGR: case PLO_DCSX: case PLO_CSSTGR: case PLO_CSSTX: case PLO_CSDSTGR: case PLO_CSDSTX: case PLO_CSTSTGR: case PLO_CSTSTX: #endif /*defined(FEATURE_ESAME)*/ /* Indicate function supported */ regs->psw.cc = 0; break; default: PTT_ERR("*PLO",regs->GR_L(0),regs->GR_L(r1),regs->psw.IA_L); /* indicate function not supported */ regs->psw.cc = 3; break; } else { /* gpr1/ar1 indentify the program lock token, which is used to select a lock from the model dependent number of locks in the configuration. We simply use 1 lock which is the main storage access lock which is also used by CS, CDS and TS. *JJ */ OBTAIN_MAINLOCK(regs); switch(regs->GR_L(0) & PLO_GPR0_FC) { case PLO_CL: regs->psw.cc = ARCH_DEP(plo_cl) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CLG: regs->psw.cc = ARCH_DEP(plo_clg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CS: regs->psw.cc = ARCH_DEP(plo_cs) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSG: regs->psw.cc = ARCH_DEP(plo_csg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_DCS: regs->psw.cc = ARCH_DEP(plo_dcs) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_DCSG: regs->psw.cc = ARCH_DEP(plo_dcsg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSST: regs->psw.cc = ARCH_DEP(plo_csst) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSSTG: regs->psw.cc = ARCH_DEP(plo_csstg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSDST: regs->psw.cc = ARCH_DEP(plo_csdst) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSDSTG: regs->psw.cc = ARCH_DEP(plo_csdstg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSTST: regs->psw.cc = ARCH_DEP(plo_cstst) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSTSTG: regs->psw.cc = ARCH_DEP(plo_cststg) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; #if defined(FEATURE_ESAME) case PLO_CLGR: regs->psw.cc = ARCH_DEP(plo_clgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CLX: regs->psw.cc = ARCH_DEP(plo_clx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSGR: regs->psw.cc = ARCH_DEP(plo_csgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSX: regs->psw.cc = ARCH_DEP(plo_csx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_DCSGR: regs->psw.cc = ARCH_DEP(plo_dcsgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_DCSX: regs->psw.cc = ARCH_DEP(plo_dcsx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSSTGR: regs->psw.cc = ARCH_DEP(plo_csstgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSSTX: regs->psw.cc = ARCH_DEP(plo_csstx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSDSTGR: regs->psw.cc = ARCH_DEP(plo_csdstgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSDSTX: regs->psw.cc = ARCH_DEP(plo_csdstx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSTSTGR: regs->psw.cc = ARCH_DEP(plo_cststgr) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; case PLO_CSTSTX: regs->psw.cc = ARCH_DEP(plo_cststx) (r1, r3, effective_addr2, b2, effective_addr4, b4, regs); break; #endif /*defined(FEATURE_ESAME)*/ default: regs->program_interrupt(regs, PGM_SPECIFICATION_EXCEPTION); } /* Release main-storage access lock */ RELEASE_MAINLOCK(regs); if(regs->psw.cc && sysblk.cpus > 1) { PTT_CSF("*PLO",regs->GR_L(0),regs->GR_L(r1),regs->psw.IA_L); sched_yield(); } } } #endif /*defined(FEATURE_PERFORM_LOCKED_OPERATION)*/ #if defined(FEATURE_STRING_INSTRUCTION) /*-------------------------------------------------------------------*/ /* B25E SRST - Search String [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(search_string) { int r1, r2; /* Values of R fields */ int i; /* Loop counter */ VADR addr1, addr2; /* End/start addresses */ BYTE sbyte; /* String character */ BYTE termchar; /* Terminating character */ RRE(inst, regs, r1, r2); /* Program check if bits 0-23 of register 0 not zero */ if ((regs->GR_L(0) & 0xFFFFFF00) != 0) regs->program_interrupt (regs, PGM_SPECIFICATION_EXCEPTION); /* Load string terminating character from register 0 bits 24-31 */ termchar = regs->GR_LHLCL(0); /* Determine the operand end and start addresses */ addr1 = regs->GR(r1) & ADDRESS_MAXWRAP(regs); addr2 = regs->GR(r2) & ADDRESS_MAXWRAP(regs); /* Search up to 256 bytes or until end of operand */ for (i = 0; i < 0x100; i++) { /* If operand end address has been reached, return condition code 2 and leave the R1 and R2 registers unchanged */ if (addr2 == addr1) { regs->psw.cc = 2; return; } /* Fetch a byte from the operand */ sbyte = ARCH_DEP(vfetchb) ( addr2, r2, regs ); /* If the terminating character was found, return condition code 1 and load the address of the character into R1 */ if (sbyte == termchar) { SET_GR_A(r1, regs, addr2); regs->psw.cc = 1; return; } /* Increment operand address */ addr2++; addr2 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ /* Set R2 to point to next character of operand */ SET_GR_A(r2, regs, addr2); /* Return condition code 3 */ regs->psw.cc = 3; } /* end DEF_INST(search_string) */ #endif /*defined(FEATURE_STRING_INSTRUCTION)*/ #if defined(FEATURE_ACCESS_REGISTERS) /*-------------------------------------------------------------------*/ /* B24E SAR - Set Access Register [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(set_access_register) { int r1, r2; /* Values of R fields */ RRE0(inst, regs, r1, r2); /* Copy R2 general register to R1 access register */ regs->AR(r1) = regs->GR_L(r2); SET_AEA_AR(regs, r1); } #endif /*defined(FEATURE_ACCESS_REGISTERS)*/ /*-------------------------------------------------------------------*/ /* 04 SPM - Set Program Mask [RR] */ /*-------------------------------------------------------------------*/ DEF_INST(set_program_mask) { int r1, r2; /* Values of R fields */ RR0(inst, regs, r1, r2); /* Set condition code from bits 2-3 of R1 register */ regs->psw.cc = ( regs->GR_L(r1) & 0x30000000 ) >> 28; /* Set program mask from bits 4-7 of R1 register */ regs->psw.progmask = ( regs->GR_L(r1) >> 24) & PSW_PROGMASK; } /*-------------------------------------------------------------------*/ /* 8F SLDA - Shift Left Double [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_left_double) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* 32-bit operand values */ U64 dreg; /* Double register work area */ U32 h, i, j, m; /* Integer work areas */ RS(inst, regs, r1, r3, b2, effective_addr2); ODD_CHECK(r1, regs); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Load the signed value from the R1 and R1+1 registers */ dreg = (U64)regs->GR_L(r1) << 32 | regs->GR_L(r1+1); m = ((S64)dreg < 0) ? 1 : 0; /* Shift the numeric portion of the value */ for (i = 0, j = 0; i < n; i++) { /* Shift bits 1-63 left one bit position */ dreg <<= 1; /* Overflow if bit shifted out is unlike the sign bit */ h = ((S64)dreg < 0) ? 1 : 0; if (h != m) j = 1; } /* Load updated value into the R1 and R1+1 registers */ regs->GR_L(r1) = (dreg >> 32) & 0x7FFFFFFF; if (m) regs->GR_L(r1) |= 0x80000000; regs->GR_L(r1+1) = dreg & 0xFFFFFFFF; /* Condition code 3 and program check if overflow occurred */ if (j) { regs->psw.cc = 3; if ( FOMASK(®s->psw) ) regs->program_interrupt (regs, PGM_FIXED_POINT_OVERFLOW_EXCEPTION); return; } /* Set the condition code */ regs->psw.cc = (S64)dreg > 0 ? 2 : (S64)dreg < 0 ? 1 : 0; } /*-------------------------------------------------------------------*/ /* 8D SLDL - Shift Left Double Logical [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_left_double_logical) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* 32-bit operand values */ U64 dreg; /* Double register work area */ RS(inst, regs, r1, r3, b2, effective_addr2); ODD_CHECK(r1, regs); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the R1 and R1+1 registers */ dreg = (U64)regs->GR_L(r1) << 32 | regs->GR_L(r1+1); dreg <<= n; regs->GR_L(r1) = dreg >> 32; regs->GR_L(r1+1) = dreg & 0xFFFFFFFF; } /*-------------------------------------------------------------------*/ /* 8B SLA - Shift Left Single [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_left_single) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n, n1, n2; /* 32-bit operand values */ U32 i, j; /* Integer work areas */ RS(inst, regs, r1, r3, b2, effective_addr2); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Fast path if no possible overflow */ if (regs->GR_L(r1) < 0x10000 && n < 16) { regs->GR_L(r1) <<= n; regs->psw.cc = regs->GR_L(r1) ? 2 : 0; return; } /* Load the numeric and sign portions from the R1 register */ n1 = regs->GR_L(r1) & 0x7FFFFFFF; n2 = regs->GR_L(r1) & 0x80000000; /* Shift the numeric portion left n positions */ for (i = 0, j = 0; i < n; i++) { /* Shift bits 1-31 left one bit position */ n1 <<= 1; /* Overflow if bit shifted out is unlike the sign bit */ if ((n1 & 0x80000000) != n2) j = 1; } /* Load the updated value into the R1 register */ regs->GR_L(r1) = (n1 & 0x7FFFFFFF) | n2; /* Condition code 3 and program check if overflow occurred */ if (j) { regs->psw.cc = 3; if ( FOMASK(®s->psw) ) regs->program_interrupt (regs, PGM_FIXED_POINT_OVERFLOW_EXCEPTION); return; } /* Set the condition code */ regs->psw.cc = (S32)regs->GR_L(r1) > 0 ? 2 : (S32)regs->GR_L(r1) < 0 ? 1 : 0; } /*-------------------------------------------------------------------*/ /* 89 SLL - Shift Left Single Logical [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_left_single_logical) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* Integer work areas */ RS0(inst, regs, r1, r3, b2, effective_addr2); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the R1 register */ regs->GR_L(r1) = n > 31 ? 0 : regs->GR_L(r1) << n; } /*-------------------------------------------------------------------*/ /* 8E SRDA - Shift Right Double [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_right_double) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* 32-bit operand values */ U64 dreg; /* Double register work area */ RS(inst, regs, r1, r3, b2, effective_addr2); ODD_CHECK(r1, regs); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the R1 and R1+1 registers */ dreg = (U64)regs->GR_L(r1) << 32 | regs->GR_L(r1+1); dreg = (U64)((S64)dreg >> n); regs->GR_L(r1) = dreg >> 32; regs->GR_L(r1+1) = dreg & 0xFFFFFFFF; /* Set the condition code */ regs->psw.cc = (S64)dreg > 0 ? 2 : (S64)dreg < 0 ? 1 : 0; } /*-------------------------------------------------------------------*/ /* 8C SRDL - Shift Right Double Logical [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_right_double_logical) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* 32-bit operand values */ U64 dreg; /* Double register work area */ RS(inst, regs, r1, r3, b2, effective_addr2); ODD_CHECK(r1, regs); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the R1 and R1+1 registers */ dreg = (U64)regs->GR_L(r1) << 32 | regs->GR_L(r1+1); dreg >>= n; regs->GR_L(r1) = dreg >> 32; regs->GR_L(r1+1) = dreg & 0xFFFFFFFF; } /*-------------------------------------------------------------------*/ /* 8A SRA - Shift Right Single [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_right_single) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* Integer work areas */ RS0(inst, regs, r1, r3, b2, effective_addr2); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the signed value of the R1 register */ regs->GR_L(r1) = n > 30 ? ((S32)regs->GR_L(r1) < 0 ? -1 : 0) : (S32)regs->GR_L(r1) >> n; /* Set the condition code */ regs->psw.cc = ((S32)regs->GR_L(r1) > 0) ? 2 : (((S32)regs->GR_L(r1) < 0) ? 1 : 0); } /*-------------------------------------------------------------------*/ /* 88 SRL - Shift Right Single Logical [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(shift_right_single_logical) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ U32 n; /* Integer work areas */ RS0(inst, regs, r1, r3, b2, effective_addr2); /* Use rightmost six bits of operand address as shift count */ n = effective_addr2 & 0x3F; /* Shift the R1 register */ regs->GR_L(r1) = n > 31 ? 0 : regs->GR_L(r1) >> n; } #if defined(FEATURE_ACCESS_REGISTERS) /*-------------------------------------------------------------------*/ /* 9B STAM - Store Access Multiple [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(store_access_multiple) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ int i, m, n; /* Integer work area */ U32 *p1, *p2 = NULL; /* Mainstor pointers */ RS(inst, regs, r1, r3, b2, effective_addr2); FW_CHECK(effective_addr2, regs); /* Calculate number of regs to store */ n = ((r3 - r1) & 0xF) + 1; /* Calculate number of words to next boundary */ m = (0x800 - (effective_addr2 & 0x7ff)) >> 2; /* Address of operand beginning */ p1 = (U32*)MADDRL(effective_addr2, n, b2, regs, ACCTYPE_WRITE, regs->psw.pkey); /* Get address of next page if boundary crossed */ if (unlikely (m < n)) p2 = (U32*)MADDR(effective_addr2 + (m*4), b2, regs, ACCTYPE_WRITE, regs->psw.pkey); else m = n; /* Store to first page */ for (i = 0; i < m; i++) store_fw (p1++, regs->AR((r1 + i) & 0xF)); /* Store to next page */ for ( ; i < n; i++) store_fw (p2++, regs->AR((r1 + i) & 0xF)); } #endif /*defined(FEATURE_ACCESS_REGISTERS)*/ /*-------------------------------------------------------------------*/ /* BE STCM - Store Characters under Mask [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(store_characters_under_mask) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ int i; /* Integer work area */ BYTE rbyte[4]; /* Byte work area */ RS(inst, regs, r1, r3, b2, effective_addr2); switch (r3) { case 7: /* Optimized case */ store_fw(rbyte, regs->GR_L(r1)); ARCH_DEP(vstorec) (rbyte+1, 2, effective_addr2, b2, regs); break; case 15: /* Optimized case */ ARCH_DEP(vstore4) (regs->GR_L(r1), effective_addr2, b2, regs); break; default: /* Extract value from register by mask */ i = 0; if (r3 & 0x8) rbyte[i++] = (regs->GR_L(r1) >> 24) & 0xFF; if (r3 & 0x4) rbyte[i++] = (regs->GR_L(r1) >> 16) & 0xFF; if (r3 & 0x2) rbyte[i++] = (regs->GR_L(r1) >> 8) & 0xFF; if (r3 & 0x1) rbyte[i++] = (regs->GR_L(r1) ) & 0xFF; if (i) ARCH_DEP(vstorec) (rbyte, i-1, effective_addr2, b2, regs); #if defined(MODEL_DEPENDENT_STCM) /* If the mask is all zero, we nevertheless access one byte from the storage operand, because POP states that an access exception may be recognized on the first byte */ else ARCH_DEP(validate_operand) (effective_addr2, b2, 0, ACCTYPE_WRITE, regs); #endif break; } /* switch (r3) */ } /*-------------------------------------------------------------------*/ /* B205 STCK - Store Clock [S] */ /* B27C STCKF - Store Clock Fast [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_clock) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ U64 dreg; /* Double word work area */ ETOD ETOD; /* Extended TOD clock */ S(inst, regs, b2, effective_addr2); #if defined(_FEATURE_SIE) if(SIE_STATB(regs, IC2, STCK)) longjmp(regs->progjmp, SIE_INTERCEPT_INST); #endif /*defined(_FEATURE_SIE)*/ #if defined(FEATURE_STORE_CLOCK_FAST) if (inst[1] == 0x7C) // STCKF only { /* Retrieve the TOD clock value without embedded CPU address */ etod_clock(regs, &ETOD, ETOD_fast); } else { #endif /*defined(FEATURE_STORE_CLOCK_FAST)*/ /* Perform serialization before fetching clock */ PERFORM_SERIALIZATION (regs); /* Retrieve the TOD clock value with embedded CPU address*/ etod_clock(regs, &ETOD, ETOD_standard); #if defined(FEATURE_STORE_CLOCK_FAST) } #endif /*defined(FEATURE_STORE_CLOCK_FAST)*/ /* Shift out epoch */ dreg = ETOD2TOD(ETOD); // /*debug*/logmsg("Store TOD clock=%16.16" I64_FMT "X\n", dreg); /* Store TOD clock value at operand address */ ARCH_DEP(vstore8) ( dreg, effective_addr2, b2, regs ); #if defined(FEATURE_STORE_CLOCK_FAST) if(inst[1] != 0x7C) // not STCKF #endif /*defined(FEATURE_STORE_CLOCK_FAST)*/ /* Perform serialization after storing clock */ PERFORM_SERIALIZATION (regs); /* Set condition code zero */ regs->psw.cc = 0; } #if defined(FEATURE_EXTENDED_TOD_CLOCK) /*-------------------------------------------------------------------*/ /* B278 STCKE - Store Clock Extended [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_clock_extended) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ ETOD ETOD; /* Extended clock work area */ S(inst, regs, b2, effective_addr2); #if defined(_FEATURE_SIE) if(SIE_STATB(regs, IC2, STCK)) longjmp(regs->progjmp, SIE_INTERCEPT_INST); #endif /*defined(_FEATURE_SIE)*/ /* Perform serialization before fetching clock */ PERFORM_SERIALIZATION (regs); /* Check that all 16 bytes of the operand are accessible */ ARCH_DEP(validate_operand) (effective_addr2, b2, 15, ACCTYPE_WRITE, regs); /* Retrieve the extended format TOD clock */ etod_clock(regs, &ETOD, ETOD_extended); // /*debug*/logmsg("Store TOD clock extended: +0=%16.16" I64_FMT "X\n", // /*debug*/ dreg); /* Store the 8 bit TOD epoch, clock bits 0-51, and bits 20-23 of the TOD uniqueness value at operand address */ ARCH_DEP(vstore8) ( ETOD.high, effective_addr2, b2, regs ); // /*debug*/logmsg("Store TOD clock extended: +8=%16.16" I64_FMT "X\n", // /*debug*/ dreg); /* Store second doubleword value at operand+8 */ effective_addr2 += 8; effective_addr2 &= ADDRESS_MAXWRAP(regs); /* Store nonzero value in pos 72 to 111 */ ARCH_DEP(vstore8) ( ETOD.low, effective_addr2, b2, regs ); /* Perform serialization after storing clock */ PERFORM_SERIALIZATION (regs); /* Set condition code zero */ regs->psw.cc = 0; } #endif /*defined(FEATURE_EXTENDED_TOD_CLOCK)*/ /*-------------------------------------------------------------------*/ /* 40 STH - Store Halfword [RX] */ /*-------------------------------------------------------------------*/ DEF_INST(store_halfword) { int r1; /* Value of R field */ int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ RX(inst, regs, r1, b2, effective_addr2); /* Store rightmost 2 bytes of R1 register at operand address */ ARCH_DEP(vstore2) ( regs->GR_LHL(r1), effective_addr2, b2, regs ); } /*-------------------------------------------------------------------*/ /* 90 STM - Store Multiple [RS] */ /*-------------------------------------------------------------------*/ DEF_INST(store_multiple) { int r1, r3; /* Register numbers */ int b2; /* effective address base */ VADR effective_addr2; /* effective address */ int i, m, n; /* Integer work areas */ U32 *p1, *p2; /* Mainstor pointers */ BYTE *bp1; /* Unaligned mainstor ptr */ RS(inst, regs, r1, r3, b2, effective_addr2); /* Calculate number of bytes to store */ n = (((r3 - r1) & 0xF) + 1) << 2; /* Calculate number of bytes to next boundary */ m = 0x800 - ((VADR_L)effective_addr2 & 0x7ff); /* Get address of first page */ bp1 = (BYTE*)MADDRL(effective_addr2, n, b2, regs, ACCTYPE_WRITE, regs->psw.pkey); p1 = (U32*)bp1; if (likely(n <= m)) { /* boundary not crossed */ n >>= 2; #if defined(OPTION_STRICT_ALIGNMENT) if(likely(!(((uintptr_t)effective_addr2)&0x03))) { #endif for (i = 0; i < n; i++) store_fw (p1++, regs->GR_L((r1 + i) & 0xF)); #if defined(OPTION_STRICT_ALIGNMENT) } else { for (i = 0; i < n; i++,bp1+=4) store_fw (bp1, regs->GR_L((r1 + i) & 0xF)); } #endif ITIMER_UPDATE(effective_addr2,(n*4)-1,regs); } else { /* boundary crossed, get address of the 2nd page */ effective_addr2 += m; effective_addr2 &= ADDRESS_MAXWRAP(regs); p2 = (U32*)MADDR(effective_addr2, b2, regs, ACCTYPE_WRITE, regs->psw.pkey); if (likely((m & 0x3) == 0)) { /* word aligned */ m >>= 2; for (i = 0; i < m; i++) store_fw (p1++, regs->GR_L((r1 + i) & 0xF)); n >>= 2; for ( ; i < n; i++) store_fw (p2++, regs->GR_L((r1 + i) & 0xF)); } else { /* worst case */ U32 rwork[16]; BYTE *b1, *b2; for (i = 0; i < (n >> 2); i++) rwork[i] = CSWAP32(regs->GR_L((r1 + i) & 0xF)); b1 = (BYTE *)&rwork[0]; b2 = (BYTE *)p1; for (i = 0; i < m; i++) *b2++ = *b1++; b2 = (BYTE *)p2; for ( ; i < n; i++) *b2++ = *b1++; } } } /* end DEF_INST(store_multiple) */ /*-------------------------------------------------------------------*/ /* 1B SR - Subtract Register [RR] */ /*-------------------------------------------------------------------*/ DEF_INST(subtract_register) { int r1, r2; /* Values of R fields */ RR(inst, regs, r1, r2); /* Subtract signed operands and set condition code */ regs->psw.cc = sub_signed (&(regs->GR_L(r1)), regs->GR_L(r1), regs->GR_L(r2)); /* Program check if fixed-point overflow */ if ( regs->psw.cc == 3 && FOMASK(®s->psw) ) regs->program_interrupt (regs, PGM_FIXED_POINT_OVERFLOW_EXCEPTION); } /*-------------------------------------------------------------------*/ /* 5B S - Subtract [RX] */ /*-------------------------------------------------------------------*/ DEF_INST(subtract) { int r1; /* Value of R field */ int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ U32 n; /* 32-bit operand values */ RX(inst, regs, r1, b2, effective_addr2); /* Load second operand from operand address */ n = ARCH_DEP(vfetch4) ( effective_addr2, b2, regs ); /* Subtract signed operands and set condition code */ regs->psw.cc = sub_signed (&(regs->GR_L(r1)), regs->GR_L(r1), n); /* Program check if fixed-point overflow */ if ( regs->psw.cc == 3 && FOMASK(®s->psw) ) regs->program_interrupt (regs, PGM_FIXED_POINT_OVERFLOW_EXCEPTION); } /*-------------------------------------------------------------------*/ /* 4B SH - Subtract Halfword [RX] */ /*-------------------------------------------------------------------*/ DEF_INST(subtract_halfword) { int r1; /* Value of R field */ int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ U32 n; /* 32-bit operand values */ RX(inst, regs, r1, b2, effective_addr2); /* Load 2 bytes from operand address */ n = (S16)ARCH_DEP(vfetch2) ( effective_addr2, b2, regs ); /* Subtract signed operands and set condition code */ regs->psw.cc = sub_signed (&(regs->GR_L(r1)), regs->GR_L(r1), n); /* Program check if fixed-point overflow */ if ( regs->psw.cc == 3 && FOMASK(®s->psw) ) regs->program_interrupt (regs, PGM_FIXED_POINT_OVERFLOW_EXCEPTION); } /*-------------------------------------------------------------------*/ /* 1F SLR - Subtract Logical Register [RR] */ /*-------------------------------------------------------------------*/ DEF_INST(subtract_logical_register) { int r1, r2; /* Values of R fields */ RR0(inst, regs, r1, r2); /* Subtract unsigned operands and set condition code */ if (likely(r1 == r2)) { regs->psw.cc = 2; regs->GR_L(r1) = 0; } else regs->psw.cc = sub_logical (&(regs->GR_L(r1)), regs->GR_L(r1), regs->GR_L(r2)); } #ifdef OPTION_OPTINST /* Optimized case (r1 equal r2) is optimized by compiler */ #define SLRgen(r1, r2) \ DEF_INST(1F ## r1 ## r2) \ { \ UNREFERENCED(inst); \ INST_UPDATE_PSW(regs, 2, 0); \ regs->psw.cc = sub_logical(&(regs->GR_L(0x ## r1)), regs->GR_L(0x ## r1), regs->GR_L(0x ## r2)); \ } #define SLRgenr2(r1) \ SLRgen(r1, 0) \ SLRgen(r1, 1) \ SLRgen(r1, 2) \ SLRgen(r1, 3) \ SLRgen(r1, 4) \ SLRgen(r1, 5) \ SLRgen(r1, 6) \ SLRgen(r1, 7) \ SLRgen(r1, 8) \ SLRgen(r1, 9) \ SLRgen(r1, A) \ SLRgen(r1, B) \ SLRgen(r1, C) \ SLRgen(r1, D) \ SLRgen(r1, E) \ SLRgen(r1, F) SLRgenr2(0) SLRgenr2(1) SLRgenr2(2) SLRgenr2(3) SLRgenr2(4) SLRgenr2(5) SLRgenr2(6) SLRgenr2(7) SLRgenr2(8) SLRgenr2(9) SLRgenr2(A) SLRgenr2(B) SLRgenr2(C) SLRgenr2(D) SLRgenr2(E) SLRgenr2(F) #endif /* #ifdef OPTION_OPTINST */ /*-------------------------------------------------------------------*/ /* 5F SL - Subtract Logical [RX] */ /*-------------------------------------------------------------------*/ DEF_INST(subtract_logical) { int r1; /* Value of R field */ int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ U32 n; /* 32-bit operand values */ RX(inst, regs, r1, b2, effective_addr2); /* Load second operand from operand address */ n = ARCH_DEP(vfetch4) ( effective_addr2, b2, regs ); /* Subtract unsigned operands and set condition code */ regs->psw.cc = sub_logical (&(regs->GR_L(r1)), regs->GR_L(r1), n); } /*-------------------------------------------------------------------*/ /* 0A SVC - Supervisor Call [RR] */ /*-------------------------------------------------------------------*/ DEF_INST(supervisor_call) { BYTE i; /* Instruction byte 1 */ PSA *psa; /* -> prefixed storage area */ RADR px; /* prefix */ int rc; /* Return code */ RR_SVC(inst, regs, i); #if defined(FEATURE_ECPSVM) if(ecpsvm_dosvc(regs,i)==0) { return; } #endif #if defined(_FEATURE_SIE) if(SIE_MODE(regs) && ( (regs->siebk->svc_ctl[0] & SIE_SVC0_ALL) || ( (regs->siebk->svc_ctl[0] & SIE_SVC0_1N) && regs->siebk->svc_ctl[1] == i) || ( (regs->siebk->svc_ctl[0] & SIE_SVC0_2N) && regs->siebk->svc_ctl[2] == i) || ( (regs->siebk->svc_ctl[0] & SIE_SVC0_3N) && regs->siebk->svc_ctl[3] == i) )) longjmp(regs->progjmp, SIE_INTERCEPT_INST); #endif /*defined(_FEATURE_SIE)*/ px = regs->PX; SIE_TRANSLATE(&px, ACCTYPE_WRITE, regs); /* Set the main storage reference and change bits */ STORAGE_KEY(px, regs) |= (STORKEY_REF | STORKEY_CHANGE); /* Use the I-byte to set the SVC interruption code */ regs->psw.intcode = i; /* Point to PSA in main storage */ psa = (void*)(regs->mainstor + px); #if defined(FEATURE_BCMODE) /* For ECMODE, store SVC interrupt code at PSA+X'88' */ if ( ECMODE(®s->psw) ) #endif /*defined(FEATURE_BCMODE)*/ { psa->svcint[0] = 0; psa->svcint[1] = REAL_ILC(regs); psa->svcint[2] = 0; psa->svcint[3] = i; } /* Store current PSW at PSA+X'20' */ ARCH_DEP(store_psw) ( regs, psa->svcold ); /* Load new PSW from PSA+X'60' */ if ( (rc = ARCH_DEP(load_psw) ( regs, psa->svcnew ) ) ) regs->program_interrupt (regs, rc); /* Perform serialization and checkpoint synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); RETURN_INTCHECK(regs); } /*-------------------------------------------------------------------*/ /* 93 TS - Test and Set [S] */ /*-------------------------------------------------------------------*/ DEF_INST(test_and_set) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ BYTE *main2; /* Mainstor address */ BYTE old; /* Old value */ S(inst, regs, b2, effective_addr2); ITIMER_SYNC(effective_addr2,0,regs); /* Perform serialization before starting operation */ PERFORM_SERIALIZATION (regs); /* Get operand absolute address */ main2 = MADDR (effective_addr2, b2, regs, ACCTYPE_WRITE, regs->psw.pkey); /* Obtain main-storage access lock */ OBTAIN_MAINLOCK(regs); /* Get old value */ old = *main2; /* Attempt to exchange the values */ while (cmpxchg1(&old, 255, main2)); regs->psw.cc = old >> 7; /* Release main-storage access lock */ RELEASE_MAINLOCK(regs); /* Perform serialization after completing operation */ PERFORM_SERIALIZATION (regs); if (regs->psw.cc == 1) { #if defined(_FEATURE_SIE) if(SIE_STATB(regs, IC0, TS1)) { if( !OPEN_IC_PER(regs) ) longjmp(regs->progjmp, SIE_INTERCEPT_INST); else longjmp(regs->progjmp, SIE_INTERCEPT_INSTCOMP); } else #endif /*defined(_FEATURE_SIE)*/ if (sysblk.cpus > 1) sched_yield(); } else { ITIMER_UPDATE(effective_addr2,0,regs); } } /*-------------------------------------------------------------------*/ /* 91 TM - Test under Mask [SI] */ /*-------------------------------------------------------------------*/ DEF_INST(test_under_mask) { BYTE i2; /* Immediate operand */ int b1; /* Base of effective addr */ VADR effective_addr1; /* Effective address */ BYTE tbyte; /* Work byte */ SI(inst, regs, i2, b1, effective_addr1); /* Fetch byte from operand address */ tbyte = ARCH_DEP(vfetchb) ( effective_addr1, b1, regs ); /* AND with immediate operand mask */ tbyte &= i2; /* Set condition code according to result */ regs->psw.cc = ( tbyte == 0 ) ? 0 : /* result all zeroes */ ( tbyte == i2) ? 3 : /* result all ones */ 1 ; /* result mixed */ } #ifdef OPTION_OPTINST #define TMgen(i2) \ DEF_INST(91 ## i2) \ { \ int b1; \ VADR effective_addr1; \ SIIX(inst, regs, b1, effective_addr1); \ if(ARCH_DEP(vfetchb)(effective_addr1, b1, regs) & 0x ## i2) \ regs->psw.cc = 3; \ else \ regs->psw.cc = 0; \ } TMgen(80) TMgen(40) TMgen(20) TMgen(10) TMgen(08) TMgen(04) TMgen(02) TMgen(01) #endif /* OPTION_OPTINST */ #if defined(FEATURE_IMMEDIATE_AND_RELATIVE) /*-------------------------------------------------------------------*/ /* A7x0 TMH - Test under Mask High [RI] */ /*-------------------------------------------------------------------*/ DEF_INST(test_under_mask_high) { int r1; /* Register number */ int opcd; /* Opcode */ U16 i2; /* 16-bit operand values */ U16 h1; /* 16-bit operand values */ U16 h2; /* 16-bit operand values */ RI0(inst, regs, r1, opcd, i2); /* AND register bits 0-15 with immediate operand */ h1 = i2 & regs->GR_LHH(r1); /* Isolate leftmost bit of immediate operand */ for ( h2 = 0x8000; h2 != 0 && (h2 & i2) == 0; h2 >>= 1 ); /* Set condition code according to result */ regs->psw.cc = ( h1 == 0 ) ? 0 : /* result all zeroes */ ( h1 == i2) ? 3 : /* result all ones */ ((h1 & h2) == 0) ? 1 : /* leftmost bit zero */ 2; /* leftmost bit one */ } #endif /*defined(FEATURE_IMMEDIATE_AND_RELATIVE)*/ #if defined(FEATURE_IMMEDIATE_AND_RELATIVE) /*-------------------------------------------------------------------*/ /* A7x1 TML - Test under Mask Low [RI] */ /*-------------------------------------------------------------------*/ DEF_INST(test_under_mask_low) { int r1; /* Register number */ int opcd; /* Opcode */ U16 i2; /* 16-bit operand values */ U16 h1; /* 16-bit operand values */ U16 h2; /* 16-bit operand values */ RI0(inst, regs, r1, opcd, i2); /* AND register bits 16-31 with immediate operand */ h1 = i2 & regs->GR_LHL(r1); /* Isolate leftmost bit of immediate operand */ for ( h2 = 0x8000; h2 != 0 && (h2 & i2) == 0; h2 >>= 1 ); /* Set condition code according to result */ regs->psw.cc = ( h1 == 0 ) ? 0 : /* result all zeroes */ ( h1 == i2) ? 3 : /* result all ones */ ((h1 & h2) == 0) ? 1 : /* leftmost bit zero */ 2; /* leftmost bit one */ } #endif /*defined(FEATURE_IMMEDIATE_AND_RELATIVE)*/ /*-------------------------------------------------------------------*/ /* DC TR - Translate [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(translate) { int len, len2 = -1; /* Lengths */ int b1, b2; /* Values of base field */ int i, b, n; /* Work variables */ VADR addr1, addr2; /* Effective addresses */ BYTE *dest, *dest2 = NULL, *tab, *tab2; /* Mainstor pointers */ SS_L(inst, regs, len, b1, addr1, b2, addr2); /* Get destination pointer */ dest = MADDRL (addr1, len+1, b1, regs, ACCTYPE_WRITE, regs->psw.pkey); /* Get pointer to next page if destination crosses a boundary */ if (CROSS2K (addr1, len)) { len2 = len; len = 0x7FF - (addr1 & 0x7FF); len2 -= (len + 1); dest2 = MADDR ((addr1+len+1) & ADDRESS_MAXWRAP(regs), b1, regs, ACCTYPE_WRITE, regs->psw.pkey); } /* Fast path if table does not cross a boundary */ if (NOCROSS2K (addr2, 255)) { tab = MADDR (addr2, b2, regs, ACCTYPE_READ, regs->psw.pkey); /* Perform translate function */ for (i = 0; i <= len; i++) dest[i] = tab[dest[i]]; for (i = 0; i <= len2; i++) dest2[i] = tab[dest2[i]]; } else { n = 0x800 - (addr2 & 0x7FF); b = dest[0]; /* Referenced part of the table may or may not span boundary */ if (b < n) { tab = MADDR (addr2, b2, regs, ACCTYPE_READ, regs->psw.pkey); for (i = 1; i <= len && b < n; i++) b = dest[i]; for (i = 0; i <= len2 && b < n; i++) b = dest2[i]; tab2 = b < n ? NULL : MADDR ((addr2+n) & ADDRESS_MAXWRAP(regs), b2, regs, ACCTYPE_READ, regs->psw.pkey); } else { tab2 = MADDR ((addr2+n) & ADDRESS_MAXWRAP(regs), b2, regs, ACCTYPE_READ, regs->psw.pkey); for (i = 1; i <= len && b >= n; i++) b = dest[i]; for (i = 0; i <= len2 && b >= n; i++) b = dest2[i]; tab = b >= n ? NULL : MADDR (addr2, b2, regs, ACCTYPE_READ, regs->psw.pkey); } /* Perform translate function */ for (i = 0; i <= len; i++) dest[i] = dest[i] < n ? tab[dest[i]] : tab2[dest[i]-n]; for (i = 0; i <= len2; i++) dest2[i] = dest2[i] < n ? tab[dest2[i]] : tab2[dest2[i]-n]; } /* Translate table spans a boundary */ } /*-------------------------------------------------------------------*/ /* DD TRT - Translate and Test [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(translate_and_test) { int l; /* Lenght byte */ int b1, b2; /* Values of base field */ VADR effective_addr1, effective_addr2; /* Effective addresses */ int cc = 0; /* Condition code */ BYTE sbyte; /* Byte work areas */ BYTE dbyte; /* Byte work areas */ int i; /* Integer work areas */ SS_L(inst, regs, l, b1, effective_addr1, b2, effective_addr2); /* Process first operand from left to right */ for ( i = 0; i <= l; i++ ) { /* Fetch argument byte from first operand */ dbyte = ARCH_DEP(vfetchb) ( effective_addr1, b1, regs ); /* Fetch function byte from second operand */ sbyte = ARCH_DEP(vfetchb) ( (effective_addr2 + dbyte) & ADDRESS_MAXWRAP(regs), b2, regs ); /* Test for non-zero function byte */ if (sbyte != 0) { /* Store address of argument byte in register 1 */ #if defined(FEATURE_ESAME) if(regs->psw.amode64) regs->GR_G(1) = effective_addr1; else #endif if ( regs->psw.amode ) regs->GR_L(1) = effective_addr1; else regs->GR_LA24(1) = effective_addr1; /* Store function byte in low-order byte of reg.2 */ regs->GR_LHLCL(2) = sbyte; /* Set condition code 2 if argument byte was last byte of first operand, otherwise set condition code 1 */ cc = (i == l) ? 2 : 1; /* Terminate the operation at this point */ break; } /* end if(sbyte) */ /* Increment first operand address */ effective_addr1++; effective_addr1 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ /* Update the condition code */ regs->psw.cc = cc; } #ifdef FEATURE_EXTENDED_TRANSLATION /*-------------------------------------------------------------------*/ /* B2A5 TRE - Translate Extended [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(translate_extended) { int r1, r2; /* Values of R fields */ int i; /* Loop counter */ int cc = 0; /* Condition code */ VADR addr1, addr2; /* Operand addresses */ GREG len1; /* Operand length */ BYTE byte1, byte2; /* Operand bytes */ BYTE tbyte; /* Test byte */ BYTE trtab[256]; /* Translate table */ RRE(inst, regs, r1, r2); ODD_CHECK(r1, regs); /* Load the test byte from bits 24-31 of register 0 */ tbyte = regs->GR_LHLCL(0); /* Load the operand addresses */ addr1 = regs->GR(r1) & ADDRESS_MAXWRAP(regs); addr2 = regs->GR(r2) & ADDRESS_MAXWRAP(regs); /* Load first operand length from R1+1 */ len1 = GR_A(r1+1, regs); /* Fetch second operand into work area. [7.5.101] Access exceptions for all 256 bytes of the second operand may be recognized, even if not all bytes are used */ ARCH_DEP(vfetchc) ( trtab, 255, addr2, r2, regs ); /* Process first operand from left to right */ for (i = 0; len1 > 0; i++) { /* If 4096 bytes have been compared, exit with condition code 3 */ if (i >= 4096) { cc = 3; break; } /* Fetch byte from first operand */ byte1 = ARCH_DEP(vfetchb) ( addr1, r1, regs ); /* If equal to test byte, exit with condition code 1 */ if (byte1 == tbyte) { cc = 1; break; } /* Load indicated byte from translate table */ byte2 = trtab[byte1]; /* Store result at first operand address */ ARCH_DEP(vstoreb) ( byte2, addr1, r1, regs ); addr1++; addr1 &= ADDRESS_MAXWRAP(regs); len1--; /* Update the registers */ SET_GR_A(r1, regs, addr1); SET_GR_A(r1+1, regs, len1); } /* end for(i) */ /* Set condition code */ regs->psw.cc = cc; } /* end translate_extended */ #endif /*FEATURE_EXTENDED_TRANSLATION*/ /*-------------------------------------------------------------------*/ /* F3 UNPK - Unpack [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(unpack) { int l1, l2; /* Register numbers */ int b1, b2; /* Base registers */ VADR effective_addr1, effective_addr2; /* Effective addressES */ int i, j; /* Loop counters */ BYTE sbyte; /* Source operand byte */ BYTE rbyte; /* Right result byte of pair */ BYTE lbyte; /* Left result byte of pair */ SS(inst, regs, l1, l2, b1, effective_addr1, b2, effective_addr2); /* If operand 1 crosses a page, make sure both pages are accessable */ if((effective_addr1 & PAGEFRAME_PAGEMASK) != ((effective_addr1 + l1) & PAGEFRAME_PAGEMASK)) ARCH_DEP(validate_operand) (effective_addr1, b1, l1, ACCTYPE_WRITE_SKP, regs); /* If operand 2 crosses a page, make sure both pages are accessable */ if((effective_addr2 & PAGEFRAME_PAGEMASK) != ((effective_addr2 + l2) & PAGEFRAME_PAGEMASK)) ARCH_DEP(validate_operand) (effective_addr2, b2, l2, ACCTYPE_READ, regs); /* Exchange the digits in the rightmost byte */ effective_addr1 += l1; effective_addr2 += l2; sbyte = ARCH_DEP(vfetchb) ( effective_addr2, b2, regs ); rbyte = ((sbyte << 4) | (sbyte >> 4)) & 0xff; ARCH_DEP(vstoreb) ( rbyte, effective_addr1, b1, regs ); /* Process remaining bytes from right to left */ for (i = l1, j = l2; i > 0; i--) { /* Fetch source byte from second operand */ if (j-- > 0) { sbyte = ARCH_DEP(vfetchb) ( --effective_addr2, b2, regs ); rbyte = (sbyte & 0x0F) | 0xF0; lbyte = (sbyte >> 4) | 0xF0; } else { rbyte = 0xF0; lbyte = 0xF0; } /* Store unpacked bytes at first operand address */ ARCH_DEP(vstoreb) ( rbyte, --effective_addr1, b1, regs ); if (--i > 0) { effective_addr1 &= ADDRESS_MAXWRAP(regs); ARCH_DEP(vstoreb) ( lbyte, --effective_addr1, b1, regs ); } /* Wraparound according to addressing mode */ effective_addr1 &= ADDRESS_MAXWRAP(regs); effective_addr2 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ } /*-------------------------------------------------------------------*/ /* 0102 UPT - Update Tree [E] */ /* (c) Copyright Peter Kuschnerus, 1999-2009 */ /* (c) Copyright "Fish" (David B. Trout), 2005-2009 */ /*-------------------------------------------------------------------*/ DEF_INST(update_tree) { GREG index; /* tree index */ GREG nodecode; /* current node's codeword */ GREG nodedata; /* current node's other data */ VADR nodeaddr; /* work addr of current node */ #if defined(FEATURE_ESAME) BYTE a64 = regs->psw.amode64; /* 64-bit mode flag */ #endif E(inst, regs); UNREFERENCED(inst); /* ** GR0, GR1 node values (codeword and other data) of node ** with "highest encountered codeword value" ** GR2, GR3 node values (codeword and other data) from whichever ** node we happened to have encountered that had a code- ** word value equal to our current "highest encountered ** codeword value" (e.g. GR0) (cc0 only) ** GR4 pointer to one node BEFORE the beginning of the tree ** GR5 current node index (tree displacement to current node) */ /* Check GR4, GR5 for proper alignment */ if (0 || ( GR_A(4,regs) & UPT_ALIGN_MASK ) || ( GR_A(5,regs) & UPT_ALIGN_MASK ) ) regs->program_interrupt (regs, PGM_SPECIFICATION_EXCEPTION); /* Bubble the tree by moving successively higher nodes towards the front (beginning) of the tree, only stopping whenever we either: 1. reach the beginning of the tree, -OR- 2. encounter a node with a negative codeword value, -OR- 3. encounter a node whose codeword is equal to our current "highest encountered codeword". Thus, when we're done, GR0 & GR1 will then contain the node values of the node with the highest encountered codeword value, and all other traversed nodes will have been reordered into descending code- word sequence (i.e. from highest codeword value to lowest codeword value; this is after all an instruction used for sorting/merging). */ for (;;) { /* Calculate index value of next node to be examined (half as far from beginning of tree to where we currently are) */ index = (GR_A(5,regs) >> 1) & UPT_SHIFT_MASK; /* Exit with cc1 when we've gone as far as we can go */ if ( !index ) { regs->psw.cc = 1; break; } /* Exit with cc3 when we encounter a negative codeword value (i.e. any codeword value with its highest-order bit on) */ if ( GR_A(0,regs) & UPT_HIGH_BIT ) { regs->psw.cc = 3; break; } /* Retrieve this node's values for closer examination... */ nodeaddr = regs->GR(4) + index; #if defined(FEATURE_ESAME) if ( a64 ) { nodecode = ARCH_DEP(vfetch8) ( (nodeaddr+0) & ADDRESS_MAXWRAP(regs), AR4, regs ); nodedata = ARCH_DEP(vfetch8) ( (nodeaddr+8) & ADDRESS_MAXWRAP(regs), AR4, regs ); } else #endif { nodecode = ARCH_DEP(vfetch4) ( (nodeaddr+0) & ADDRESS_MAXWRAP(regs), AR4, regs ); nodedata = ARCH_DEP(vfetch4) ( (nodeaddr+4) & ADDRESS_MAXWRAP(regs), AR4, regs ); } /* GR5 must remain UNCHANGED if the execution of a unit of operation is nullified or suppressed! Thus it must ONLY be updated/committed AFTER we've successfully retrieved the node data (since the storage access could cause a program-check thereby nullifying/suppressing the instruction's "current unit of operation") */ SET_GR_A(5,regs,index); // (do AFTER node data is accessed!) /* Exit with cc0 whenever we reach a node whose codeword is equal to our current "highest encountered" codeword value (i.e. any node whose codeword matches our current "highest" (GR0) value) */ if ( nodecode == GR_A(0,regs) ) { /* Load GR2 and GR3 with the equal codeword node's values */ SET_GR_A(2,regs,nodecode); SET_GR_A(3,regs,nodedata); regs->psw.cc = 0; return; } /* Keep resequencing the tree's nodes, moving successively higher nodes to the front (beginning of tree)... */ if ( nodecode < GR_A(0,regs) ) continue; /* This node has a codeword value higher than our currently saved highest encountered codeword value (GR0). Swap our GR0/1 values with this node's values, such that GR0/1 always hold the values from the node with the highest encountered codeword value... */ /* Store obsolete GR0 and GR1 values into this node's entry */ #if defined(FEATURE_ESAME) if ( a64 ) { ARCH_DEP(vstore8) ( GR_A(0,regs), (nodeaddr+0) & ADDRESS_MAXWRAP(regs), AR4, regs ); ARCH_DEP(vstore8) ( GR_A(1,regs), (nodeaddr+8) & ADDRESS_MAXWRAP(regs), AR4, regs ); } else #endif { ARCH_DEP(vstore4) ( GR_A(0,regs), (nodeaddr+0) & ADDRESS_MAXWRAP(regs), AR4, regs ); ARCH_DEP(vstore4) ( GR_A(1,regs), (nodeaddr+4) & ADDRESS_MAXWRAP(regs), AR4, regs ); } /* Update GR0 and GR1 with the new "highest encountered" values */ SET_GR_A(0,regs,nodecode); SET_GR_A(1,regs,nodedata); } /* Commit GR5 with the actual index value we stopped on */ SET_GR_A(5,regs,index); } #if defined(FEATURE_EXTENDED_TRANSLATION_FACILITY_3) /*-------------------------------------------------------------------*/ /* B9B0 CU14 - Convert UTF-8 to UTF-32 [RRF] */ /*-------------------------------------------------------------------*/ DEF_INST(convert_utf8_to_utf32) { VADR dest; /* Destination address */ GREG destlen; /* Destination length */ int r1; int r2; int read; /* Bytes read */ VADR srce; /* Source address */ GREG srcelen; /* Source length */ BYTE utf32[4]; /* utf32 character(s) */ BYTE utf8[4]; /* utf8 character(s) */ #if defined(FEATURE_ETF3_ENHANCEMENT) int wfc; /* Well-Formedness-Checking (W) */ #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ int xlated; /* characters translated */ // NOTE: it's faster to decode with RRE format // and then to handle the 'wfc' flag separately... //RRF_M(inst, regs, r1, r2, wfc); RRE(inst, regs, r1, r2); ODD2_CHECK(r1, r2, regs); /* Get paramaters */ dest = regs->GR(r1) & ADDRESS_MAXWRAP(regs); destlen = GR_A(r1 + 1, regs); srce = regs->GR(r2) & ADDRESS_MAXWRAP(regs); srcelen = GR_A(r2 + 1, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) if(inst[2] & 0x10) wfc = 1; else wfc = 0; #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* Every valid utf-32 starts with 0x00 */ utf32[0] = 0x00; /* Initialize number of translated charachters */ xlated = 0; while(xlated < 4096) { /* Check end of source or destination */ if(srcelen < 1) { regs->psw.cc = 0; return; } if(destlen < 4) { regs->psw.cc = 1; return; } /* Fetch a byte */ utf8[0] = ARCH_DEP(vfetchb)(srce, r2, regs); if(utf8[0] < 0x80) { /* xlate range 00-7f */ /* 0jklmnop -> 00000000 00000000 00000000 0jklmnop */ utf32[1] = 0x00; utf32[2] = 0x00; utf32[3] = utf8[0]; read = 1; } else if(utf8[0] >= 0xc0 && utf8[0] <= 0xdf) { #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellFormednessChecking */ if(wfc) { if(utf8[0] <= 0xc1) { regs->psw.cc = 2; return; } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* Check end of source */ if(srcelen < 2) { regs->psw.cc = 0; /* Strange but stated in POP */ return; } /* Get the next byte */ utf8[1] = ARCH_DEP(vfetchb)(srce + 1, r2, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellFormednessChecking */ if(wfc) { if(utf8[1] < 0x80 || utf8[1] > 0xbf) { regs->psw.cc = 2; return; } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* xlate range c000-dfff */ /* 110fghij 10klmnop -> 00000000 00000000 00000fgh ijklmnop */ utf32[1] = 0x00; utf32[2] = (utf8[0] & 0x1c) >> 2; utf32[3] = (utf8[0] << 6) | (utf8[1] & 0x3f); read = 2; } else if(utf8[0] >= 0xe0 && utf8[0] <= 0xef) { /* Check end of source */ if(srcelen < 3) { regs->psw.cc = 0; /* Strange but stated in POP */ return; } /* Get the next 2 bytes */ ARCH_DEP(vfetchc)(&utf8[1], 1, srce + 1, r2, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellformednessChecking */ if(wfc) { if(utf8[0] == 0xe0) { if(utf8[1] < 0xa0 || utf8[1] > 0xbf || utf8[2] < 0x80 || utf8[2] > 0xbf) { regs->psw.cc = 2; return; } } if((utf8[0] >= 0xe1 && utf8[0] <= 0xec) || (utf8[0] >= 0xee && utf8[0] <= 0xef)) { if(utf8[1] < 0x80 || utf8[1] > 0xbf || utf8[2] < 0x80 || utf8[2] > 0xbf) { regs->psw.cc = 2; return; } } if(utf8[0] == 0xed) { if(utf8[1] < 0x80 || utf8[1] > 0x9f || utf8[2] < 0x80 || utf8[2] > 0xbf) { regs->psw.cc = 2; return; } } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* xlate range e00000-efffff */ /* 1110abcd 10efghij 10klmnop -> 00000000 00000000 abcdefgh ijklmnop */ utf32[1] = 0x00; utf32[2] = (utf8[0] << 4) | ((utf8[1] & 0x3c) >> 2); utf32[3] = (utf8[1] << 6) | (utf8[2] & 0x3f); read = 3; } else if(utf8[0] >= 0xf0 && utf8[0] <= 0xf7) { #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellFormednessChecking */ if(wfc) { if(utf8[0] > 0xf4) { regs->psw.cc = 2; return; } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* Check end of source */ if(srcelen < 4) { regs->psw.cc = 0; /* Strange but stated in POP */ return; } /* Get the next 3 bytes */ ARCH_DEP(vfetchc)(&utf8[1], 2, srce + 1, r2, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellFormdnessChecking */ if(wfc) { if(utf8[0] == 0xf0) { if(utf8[1] < 0x90 || utf8[1] > 0xbf || utf8[2] < 0x80 || utf8[2] > 0xbf || utf8[3] < 0x80 || utf8[3] > 0xbf) { regs->psw.cc = 2; return; } } if(utf8[0] >= 0xf1 && utf8[0] <= 0xf3) { if(utf8[1] < 0x80 || utf8[1] > 0xbf || utf8[2] < 0x80 || utf8[2] > 0xbf || utf8[3] < 0x80 || utf8[3] > 0xbf) { regs->psw.cc = 2; return; } } if(utf8[0] == 0xf4) { if(utf8[1] < 0x80 || utf8[1] > 0x8f || utf8[2] < 0x80 || utf8[2] > 0xbf || utf8[3] < 0x80 || utf8[3] > 0xbf) { regs->psw.cc = 2; return; } } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* xlate range f0000000-f7000000 */ /* 1110uvw 10xyefgh 10ijklmn 10opqrst -> 00000000 000uvwxy efghijkl mnopqrst */ utf32[1] = ((utf8[0] & 0x07) << 2) | ((utf8[1] & 0x30) >> 4); utf32[2] = (utf8[1] << 4) | ((utf8[2] & 0x3c) >> 2); utf32[3] = (utf8[2] << 6) | (utf8[3] & 0x3f); read = 4; } else { regs->psw.cc = 2; return; } /* Write and commit registers */ ARCH_DEP(vstorec)(utf32, 3, dest, r1, regs); SET_GR_A(r1, regs, (dest + 4) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r1 + 1, regs, destlen - 4); SET_GR_A(r2, regs, (srce + read) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r2 + 1, regs, srcelen - read); xlated += read; } /* CPU determined number of characters reached */ regs->psw.cc = 3; } /*-------------------------------------------------------------------*/ /* B9B1 CU24 - Convert UTF-16 to UTF-32 [RRF] */ /*-------------------------------------------------------------------*/ DEF_INST(convert_utf16_to_utf32) { VADR dest; /* Destination address */ GREG destlen; /* Destination length */ int r1; int r2; int read; /* Bytes read */ VADR srce; /* Source address */ GREG srcelen; /* Source length */ BYTE utf16[4]; /* utf16 character(s) */ BYTE utf32[4]; /* utf328 character(s) */ BYTE uvwxy; /* Work value */ #if defined(FEATURE_ETF3_ENHANCEMENT) int wfc; /* Well-Formedness-Checking (W) */ #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ int xlated; /* characters translated */ // NOTE: it's faster to decode with RRE format // and then to handle the 'wfc' flag separately... //RRF_M(inst, regs, r1, r2, wfc); RRE(inst, regs, r1, r2); ODD2_CHECK(r1, r2, regs); /* Get paramaters */ dest = regs->GR(r1) & ADDRESS_MAXWRAP(regs); destlen = GR_A(r1 + 1, regs); srce = regs->GR(r2) & ADDRESS_MAXWRAP(regs); srcelen = GR_A(r2 + 1, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) if(inst[2] & 0x10) wfc = 1; else wfc = 0; #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* Every valid utf-32 starts with 0x00 */ utf32[0] = 0x00; /* Initialize number of translated charachters */ xlated = 0; while(xlated < 4096) { /* Check end of source or destination */ if(srcelen < 2) { regs->psw.cc = 0; return; } if(destlen < 4) { regs->psw.cc = 1; return; } /* Fetch 2 bytes */ ARCH_DEP(vfetchc)(utf16, 1, srce, r2, regs); if(utf16[0] <= 0xd7 || utf16[0] >= 0xdc) { /* xlate range 0000-d7fff and dc00-ffff */ /* abcdefgh ijklmnop -> 00000000 00000000 abcdefgh ijklmnop */ utf32[1] = 0x00; utf32[2] = utf16[0]; utf32[3] = utf16[1]; read = 2; } else { /* Check end of source */ if(srcelen < 4) { regs->psw.cc = 0; /* Strange but stated in POP */ return; } /* Fetch another 2 bytes */ ARCH_DEP(vfetchc)(&utf16[2], 1, srce, r2, regs); #if defined(FEATURE_ETF3_ENHANCEMENT) /* WellFormednessChecking */ if(wfc) { if(utf16[2] < 0xdc && utf16[2] > 0xdf) { regs->psw.cc = 2; return; } } #endif /*defined(FEATURE_ETF3_ENHANCEMENT)*/ /* xlate range d800-dbff */ /* 110110ab cdefghij 110111kl mnopqrst -> 00000000 000uvwxy efghijkl mnopqrst */ /* 000uvwxy = 0000abcde + 1 */ uvwxy = (((utf16[0] & 0x03) << 2) | (utf16[1] >> 6)) + 1; utf32[1] = uvwxy; utf32[2] = (utf16[1] << 2) | (utf16[2] & 0x03); utf32[3] = utf16[3]; read = 4; } /* Write and commit registers */ ARCH_DEP(vstorec)(utf32, 3, dest, r1, regs); SET_GR_A(r1, regs, (dest + 4) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r1 + 1, regs, destlen - 4); SET_GR_A(r2, regs, (srce + read) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r2 + 1, regs, srcelen - read); xlated += read; } /* CPU determined number of characters reached */ regs->psw.cc = 3; } /*-------------------------------------------------------------------*/ /* B9B2 CU41 - Convert UTF-32 to UTF-8 [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(convert_utf32_to_utf8) { VADR dest; /* Destination address */ GREG destlen; /* Destination length */ int r1; int r2; VADR srce; /* Source address */ GREG srcelen; /* Source length */ BYTE utf32[4]; /* utf32 character(s) */ BYTE utf8[4]; /* utf8 character(s) */ int write; /* Bytes written */ int xlated; /* characters translated */ RRE(inst, regs, r1, r2); ODD2_CHECK(r1, r2, regs); /* Get paramaters */ dest = regs->GR(r1) & ADDRESS_MAXWRAP(regs); destlen = GR_A(r1 + 1, regs); srce = regs->GR(r2) & ADDRESS_MAXWRAP(regs); srcelen = GR_A(r2 + 1, regs); /* Initialize number of translated charachters */ xlated = 0; write = 0; while(xlated < 4096) { /* Check end of source or destination */ if(srcelen < 4) { regs->psw.cc = 0; return; } if(destlen < 1) { regs->psw.cc = 1; return; } /* Get 4 bytes */ ARCH_DEP(vfetchc)(utf32, 3, srce, r2, regs); if(utf32[0] != 0x00) { regs->psw.cc = 2; return; } else if(utf32[1] == 0x00) { if(utf32[2] == 0x00) { if(utf32[3] <= 0x7f) { /* xlate range 00000000-0000007f */ /* 00000000 00000000 00000000 0jklmnop -> 0jklmnop */ utf8[0] = utf32[3]; write = 1; } } else if(utf32[2] <= 0x07) { /* Check destination length */ if(destlen < 2) { regs->psw.cc = 1; return; } /* xlate range 00000080-000007ff */ /* 00000000 00000000 00000fgh ijklmnop -> 110fghij 10klmnop */ utf8[0] = 0xc0 | (utf32[2] << 2) | (utf32[2] >> 6); utf8[1] = 0x80 | (utf32[2] & 0x3f); write = 2; } else if(utf32[2] <= 0xd7 || utf32[2] > 0xdc) { /* Check destination length */ if(destlen < 3) { regs->psw.cc = 1; return; } /* xlate range 00000800-0000d7ff and 0000dc00-0000ffff */ /* 00000000 00000000 abcdefgh ijklnmop -> 1110abcd 10efghij 10klmnop */ utf8[0] = 0xe0 | (utf32[2] >> 4); utf8[1] = 0x80 | ((utf32[2] & 0x0f) << 2) | (utf32[3] >> 6); utf8[2] = 0x80 | (utf32[3] & 0x3f); write = 3; } else { regs->psw.cc = 2; return; } } else if(utf32[1] >= 0x01 && utf32[1] <= 0x10) { /* Check destination length */ if(destlen < 4) { regs->psw.cc = 1; return; } /* xlate range 00010000-0010ffff */ /* 00000000 000uvwxy efghijkl mnopqrst -> 11110uvw 10xyefgh 10ijklmn 10opqrst */ utf8[0] = 0xf0 | (utf32[1] >> 2); utf8[1] = 0x80 | ((utf32[1] & 0x03) << 4) | (utf32[2] >> 4); utf8[2] = 0x80 | ((utf32[2] & 0x0f) << 2) | (utf32[3] >> 6); utf8[3] = 0x80 | (utf32[3] & 0x3f); write = 4; } else { regs->psw.cc = 2; return; } /* Write and commit registers */ ARCH_DEP(vstorec)(utf8, write - 1, dest, r1, regs); SET_GR_A(r1, regs, (dest + write) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r1 + 1, regs, destlen - write); SET_GR_A(r2, regs, (srce + 4) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r2 + 1, regs, srcelen - 4); xlated += 4; } /* CPU determined number of characters reached */ regs->psw.cc = 3; } /*-------------------------------------------------------------------*/ /* B9B3 CU42 - Convert UTF-32 to UTF-16 [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(convert_utf32_to_utf16) { VADR dest; /* Destination address */ GREG destlen; /* Destination length */ int r1; int r2; VADR srce; /* Source address */ GREG srcelen; /* Source length */ BYTE utf16[4]; /* utf16 character(s) */ BYTE utf32[4]; /* utf32 character(s) */ int write; /* Bytes written */ int xlated; /* characters translated */ BYTE zabcd; /* Work value */ RRE(inst, regs, r1, r2); ODD2_CHECK(r1, r2, regs); /* Get paramaters */ dest = regs->GR(r1) & ADDRESS_MAXWRAP(regs); destlen = GR_A(r1 + 1, regs); srce = regs->GR(r2) & ADDRESS_MAXWRAP(regs); srcelen = GR_A(r2 + 1, regs); /* Initialize number of translated charachters */ xlated = 0; while(xlated < 4096) { /* Check end of source or destination */ if(srcelen < 4) { regs->psw.cc = 0; return; } if(destlen < 2) { regs->psw.cc = 1; return; } /* Get 4 bytes */ ARCH_DEP(vfetchc)(utf32, 3, srce, r2, regs); if(utf32[0] != 0x00) { regs->psw.cc = 2; return; } else if(utf32[1] == 0x00 && (utf32[2] <= 0xd7 || utf32[2] >= 0xdc)) { /* xlate range 00000000-0000d7ff and 0000dc00-0000ffff */ /* 00000000 00000000 abcdefgh ijklmnop -> abcdefgh ijklmnop */ utf16[0] = utf32[2]; utf16[1] = utf32[3]; write = 2; } else if(utf32[1] >= 0x01 && utf32[1] <= 0x10) { /* Check end of destination */ if(destlen < 4) { regs->psw.cc = 1; return; } /* xlate range 00010000-0010ffff */ /* 00000000 000uvwxy efghijkl mnopqrst -> 110110ab cdefghij 110111kl mnopqrst */ /* 000zabcd = 000uvwxy - 1 */ zabcd = (utf32[1] - 1) & 0x0f; utf16[0] = 0xd8 | (zabcd >> 2); utf16[1] = (zabcd << 6) | (utf32[2] >> 2); utf16[2] = 0xdc | (utf32[2] & 0x03); utf16[3] = utf32[3]; write = 4; } else { regs->psw.cc = 2; return; } /* Write and commit registers */ ARCH_DEP(vstorec)(utf16, write - 1, dest, r1, regs); SET_GR_A(r1, regs, (dest + write) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r1 + 1, regs, destlen - write); SET_GR_A(r2, regs, (srce + 4) & ADDRESS_MAXWRAP(regs)); SET_GR_A(r2 + 1, regs, srcelen - 4); xlated += 4; } /* CPU determined number of characters reached */ regs->psw.cc = 3; } /*-------------------------------------------------------------------*/ /* B9BE SRSTU - Search String Unicode [RRE] */ /*-------------------------------------------------------------------*/ DEF_INST(search_string_unicode) { VADR addr1, addr2; /* End/start addresses */ int i; /* Loop counter */ int r1, r2; /* Values of R fields */ U16 sbyte; /* String character */ U16 termchar; /* Terminating character */ RRE(inst, regs, r1, r2); /* Program check if bits 0-15 of register 0 not zero */ if(regs->GR_L(0) & 0xFFFF0000) regs->program_interrupt (regs, PGM_SPECIFICATION_EXCEPTION); /* Load string terminating character from register 0 bits 16-31 */ termchar = (U16) regs->GR(0); /* Determine the operand end and start addresses */ addr1 = regs->GR(r1) & ADDRESS_MAXWRAP(regs); addr2 = regs->GR(r2) & ADDRESS_MAXWRAP(regs); /* Search up to 256 bytes or until end of operand */ for(i = 0; i < 0x100; i++) { /* If operand end address has been reached, return condition code 2 and leave the R1 and R2 registers unchanged */ if(addr2 == addr1) { regs->psw.cc = 2; return; } /* Fetch 2 bytes from the operand */ sbyte = ARCH_DEP(vfetch2)(addr2, r2, regs ); /* If the terminating character was found, return condition code 1 and load the address of the character into R1 */ if(sbyte == termchar) { SET_GR_A(r1, regs, addr2); regs->psw.cc = 1; return; } /* Increment operand address */ addr2 += 2; addr2 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ /* Set R2 to point to next character of operand */ SET_GR_A(r2, regs, addr2); /* Return condition code 3 */ regs->psw.cc = 3; } /*-------------------------------------------------------------------*/ /* D0 TRTR - Translate and Test Reverse [SS] */ /*-------------------------------------------------------------------*/ DEF_INST(translate_and_test_reverse) { int b1, b2; /* Values of base field */ int cc = 0; /* Condition code */ BYTE dbyte; /* Byte work areas */ VADR effective_addr1; VADR effective_addr2; /* Effective addresses */ int i; /* Integer work areas */ int l; /* Lenght byte */ BYTE sbyte; /* Byte work areas */ SS_L(inst, regs, l, b1, effective_addr1, b2, effective_addr2); /* Process first operand from right to left*/ for(i = 0; i <= l; i++) { /* Fetch argument byte from first operand */ dbyte = ARCH_DEP(vfetchb)(effective_addr1, b1, regs); /* Fetch function byte from second operand */ sbyte = ARCH_DEP(vfetchb)((effective_addr2 + dbyte) & ADDRESS_MAXWRAP(regs), b2, regs); /* Test for non-zero function byte */ if(sbyte != 0) { /* Store address of argument byte in register 1 */ #if defined(FEATURE_ESAME) if(regs->psw.amode64) regs->GR_G(1) = effective_addr1; else #endif if(regs->psw.amode) { /* Note: TRTR differs from TRT in 31 bit mode. TRTR leaves bit 32 unchanged, TRT clears bit 32 */ regs->GR_L(1) &= 0x80000000; regs->GR_L(1) |= effective_addr1; } else regs->GR_LA24(1) = effective_addr1; /* Store function byte in low-order byte of reg.2 */ regs->GR_LHLCL(2) = sbyte; /* Set condition code 2 if argument byte was last byte of first operand, otherwise set condition code 1 */ cc = (i == l) ? 2 : 1; /* Terminate the operation at this point */ break; } /* end if(sbyte) */ /* Decrement first operand address */ effective_addr1--; /* Another difference with TRT */ effective_addr1 &= ADDRESS_MAXWRAP(regs); } /* end for(i) */ /* Update the condition code */ regs->psw.cc = cc; } #endif /*defined(FEATURE_EXTENDED_TRANSLATION_FACILITY_3)*/ #ifdef FEATURE_PARSING_ENHANCEMENT_FACILITY /*-------------------------------------------------------------------*/ /* B9BF TRTE - Translate and Test Extended [RRF] */ /*-------------------------------------------------------------------*/ DEF_INST(translate_and_test_extended) { int a_bit; /* Argument-Character Control (A) */ U32 arg_ch; /* Argument character */ VADR buf_addr; /* first argument address */ GREG buf_len; /* First argument length */ int f_bit; /* Function-Code Control (F) */ U32 fc; /* Function-Code */ VADR fct_addr; /* Function-code table address */ int l_bit; /* Argument-Character Limit (L) */ int m3; int processed; /* # bytes processed */ int r1; int r2; RRF_M(inst, regs, r1, r2, m3); a_bit = ((m3 & 0x08) ? 1 : 0); f_bit = ((m3 & 0x04) ? 1 : 0); l_bit = ((m3 & 0x02) ? 1 : 0); buf_addr = regs->GR(r1) & ADDRESS_MAXWRAP(regs); buf_len = GR_A(r1 + 1, regs); fct_addr = regs->GR(1) & ADDRESS_MAXWRAP(regs); if(unlikely((a_bit && (buf_len % 1)) || r1 & 0x01)) regs->program_interrupt(regs, PGM_SPECIFICATION_EXCEPTION); fc = 0; processed = 0; while(buf_len && !fc && processed < 16384) { if(a_bit) { arg_ch = ARCH_DEP(vfetch2)(buf_addr, r1, regs); } else { arg_ch = ARCH_DEP(vfetchb)(buf_addr, r1, regs); } if(l_bit && arg_ch > 255) fc = 0; else { if(f_bit) fc = ARCH_DEP(vfetch2)((fct_addr + (arg_ch * 2)) & ADDRESS_MAXWRAP(regs), 1, regs); else fc = ARCH_DEP(vfetchb)((fct_addr + arg_ch) & ADDRESS_MAXWRAP(regs), 1, regs); } if(!fc) { if(a_bit) { buf_len -= 2; processed += 2; buf_addr = (buf_addr + 2) & ADDRESS_MAXWRAP(regs); } else { buf_len--; processed++; buf_addr = (buf_addr + 1) & ADDRESS_MAXWRAP(regs); } } } /* Commit registers */ SET_GR_A(r1, regs, buf_addr); SET_GR_A(r1 + 1, regs, buf_len); /* Check if CPU determined number of bytes have been processed */ if(buf_len && !fc) { regs->psw.cc = 3; return; } /* Set function code */ if(likely(r2 != r1 && r2 != r1 + 1)) SET_GR_A(r2, regs, fc); /* Set condition code */ if(fc) regs->psw.cc = 1; else regs->psw.cc = 0; } /*-------------------------------------------------------------------*/ /* B9BD TRTRE - Translate and Test Reverse Extended [RRF] */ /*-------------------------------------------------------------------*/ DEF_INST(translate_and_test_reverse_extended) { int a_bit; /* Argument-Character Control (A) */ U32 arg_ch; /* Argument character */ VADR buf_addr; /* first argument address */ GREG buf_len; /* First argument length */ int f_bit; /* Function-Code Control (F) */ U32 fc; /* Function-Code */ VADR fct_addr; /* Function-code table address */ int l_bit; /* Argument-Character Limit (L) */ int m3; int processed; /* # bytes processed */ int r1; int r2; RRF_M(inst, regs, r1, r2, m3); a_bit = ((m3 & 0x08) ? 1 : 0); f_bit = ((m3 & 0x04) ? 1 : 0); l_bit = ((m3 & 0x02) ? 1 : 0); buf_addr = regs->GR(r1) & ADDRESS_MAXWRAP(regs); buf_len = GR_A(r1 + 1, regs); fct_addr = regs->GR(1) & ADDRESS_MAXWRAP(regs); if(unlikely((a_bit && (buf_len % 1)) || r1 & 0x01)) regs->program_interrupt(regs, PGM_SPECIFICATION_EXCEPTION); fc = 0; processed = 0; while(buf_len && !fc && processed < 16384) { if(a_bit) { arg_ch = ARCH_DEP(vfetch2)(buf_addr, r1, regs); } else { arg_ch = ARCH_DEP(vfetchb)(buf_addr, r1, regs); } if(l_bit && arg_ch > 255) fc = 0; else { if(f_bit) fc = ARCH_DEP(vfetch2)((fct_addr + (arg_ch * 2)) & ADDRESS_MAXWRAP(regs), 1, regs); else fc = ARCH_DEP(vfetchb)((fct_addr + arg_ch) & ADDRESS_MAXWRAP(regs), 1, regs); } if(!fc) { if(a_bit) { buf_len -= 2; processed += 2; buf_addr = (buf_addr - 2) & ADDRESS_MAXWRAP(regs); } else { buf_len--; processed++; buf_addr = (buf_addr - 1) & ADDRESS_MAXWRAP(regs); } } } /* Commit registers */ SET_GR_A(r1, regs, buf_addr); SET_GR_A(r1 + 1, regs, buf_len); /* Check if CPU determined number of bytes have been processed */ if(buf_len && !fc) { regs->psw.cc = 3; return; } /* Set function code */ if(likely(r2 != r1 && r2 != r1 + 1)) SET_GR_A(r2, regs, fc); /* Set condition code */ if(fc) regs->psw.cc = 1; else regs->psw.cc = 0; } #endif /* FEATURE_PARSING_ENHANCEMENT_FACILITY */ #if !defined(_GEN_ARCH) #if defined(_ARCHMODE2) #define _GEN_ARCH _ARCHMODE2 #include "general2.c" #endif #if defined(_ARCHMODE3) #undef _GEN_ARCH #define _GEN_ARCH _ARCHMODE3 #include "general2.c" #endif #endif /*!defined(_GEN_ARCH)*/