diff options
Diffstat (limited to 'target/s390x/mem_helper.c')
-rw-r--r-- | target/s390x/mem_helper.c | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index 218f1ab1fd..b87e41764f 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -1008,6 +1008,57 @@ void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest, } } +uint32_t HELPER(unpka)(CPUS390XState *env, uint64_t dest, uint32_t destlen, + uint64_t src) +{ + uintptr_t ra = GETPC(); + int i; + uint32_t cc; + uint8_t b; + /* The source operand is always 16 bytes long. */ + const int srclen = 16; + + /* The operands are processed from right to left. */ + src += srclen - 1; + dest += destlen - 1; + + /* Check for the sign. */ + b = cpu_ldub_data_ra(env, src, ra); + src--; + switch (b & 0xf) { + case 0xa: + case 0xc: + case 0xe ... 0xf: + cc = 0; /* plus */ + break; + case 0xb: + case 0xd: + cc = 1; /* minus */ + break; + default: + case 0x0 ... 0x9: + cc = 3; /* invalid */ + break; + } + + /* Now pad every nibble with 0x30, advancing one nibble at a time. */ + for (i = 0; i < destlen; i++) { + if (i == 31) { + /* If length is 32 bytes, the leftmost byte is 0. */ + b = 0; + } else if (i % 2) { + b = cpu_ldub_data_ra(env, src, ra); + src--; + } else { + b >>= 4; + } + cpu_stb_data_ra(env, dest, 0x30 + (b & 0xf), ra); + dest--; + } + + return cc; +} + static uint32_t do_helper_tr(CPUS390XState *env, uint32_t len, uint64_t array, uint64_t trans, uintptr_t ra) { |