user3856804
user3856804

Reputation: 65

Bit shifting in IBM assembler

In IBM OS/390 assembly, I am trying to do the following:

I have a set of bits that all end in 2 zeroes:

00xxxxxx 00yyyyyy 00zzzzzz

I want to compress them into the following format:

xxxxxxyy yyyyzzzz zz...

I figure that the order will something like

ICM  R7,B'1111',FIRSTBUF       LOAD 4 BYTES FROM FIRSTBUF INTO REGISTER 7
SLL  R7,2                      SHIFT ALL BITS LEFT BY 2
STCM R7,B'1000',FINALBUF       PASTE THE LEFTMOST BYTE ONLY
SLL  R7,2                      SHIFT ALL BITS LEFT BY 2
(somehow overwrite only the rightmost 2 bits of the leftmost byte)
STCM R7,B'0100',FINALBUF       PASTE SECOND LEFTMOST BYTE
SLL  R7,2                      SHIFT ALL BITS LEFT BY 2
(somehow overwrite only the right 4 bits of the second byte)
STCM R7,B'0010',FINALBUF       PASTE SECOND RIGHTMOST BYTE
SLL  R7,2                      SHIFT ALL BITS LEFT BY 2
....

Am I on the right track here?

Upvotes: 1

Views: 1819

Answers (5)

zArchJon
zArchJon

Reputation: 41

I know you were asking about OS/390 architecture. But if you are running on a z10 or later machine the ROTATE AND INSERT SELECTED BITS (RISBG) may be helpful.

Upvotes: 2

dstaudacher
dstaudacher

Reputation: 556

* ASSUME R1 -> TWO ENTRY PARM LIST
* PARM ONE -> 4 BYTES WITH BITS = 00XXXXXX 00YYYYYY 00ZZZZZZ ...
* PARM TWO -> 4 BYTES TO RECEIVE BITS = XXXXXXYY YYYYZZZZ ZZ000000 ...
U3856804 CSECT
         SAVE (14,12) "STM 14,12,12(13)"
         USING U3856804,15
         LM 14,15,0(1) R14 -> PARM ONE, R15 -> PARM TWO
         IC 0,0(14)    R0 LOW-ORDER BYTE = 00XXXXXX
         IC 1,1(14)    R1 LOW-ORDER BYTE = 00YYYYYY
         SLL 1,26      R1 HIGH-ORDER BYTE = YYYYYY00
         SLDL 0,6      R0 LOW-ORDER HALFWORD = 0000XXXX XXYYYYYY
         IC 1,2(14)    R1 LOW-ORDER BYTE = 00ZZZZZZ
         SLL 1,26      R1 HIGH-ORDER BYTE = ZZZZZZ00
         SLDL 0,20     R0 = XXXXXXYY YYYYZZZZ ZZZZ.... ........
         ST 0,0(,15)   STORE RESULT
         RETURN (14,12) "LM 14,12,12(13)" "BR 14"
         END

Upvotes: 0

Valerie R
Valerie R

Reputation: 1817

As a die-hard assembler lover, I was going to offer a slight variation on the example above...

First, if you have available registers and can arrange for the original values to be adjacent in memory, a simple LOAD MULTIPLE (LM Rx,Ry,data) and three STORE CHARACTERS UNDER MASK (STCM) would do the trick. Total of four instructions.

But then it occurred to me that there's an even better way to do this with a single instruction using TRANSLATE (TR):

IN1    DC  A(0)                      <- Input fields...they must be contiguous   
IN2    DC  A(0)
IN3    DC  A(0)
     . . .
TARGET DC  XL9'010203050607090A0B'   <- Note the pattern
     . . .
       TR  TARGET,IN1                <- Magic!

The TRANSLATE instruction replaces values in the target using the second operand as a lookup table. While this is most commonly used for things like transforming ASCII to EBCDIC, it can also be used to "rearrange" data, much as the original poster wanted. TR takes the first byte (X'01'), indexes into the second operand by this amount, and replaces the value in the input with whatever it finds. When the instruction completes, the TARGET field contains exactly what's requested - and this approach only consumes a single CPU instruction.

I rarely get to show off my knowledge of mainframe assembler, so thanks for giving me the opportunity!

Upvotes: 3

Bill Woodger
Bill Woodger

Reputation: 13076

I think you've gone around the houses a bit on this.

BUF01    DS    CL4                First data location
...
BUF02    DS    CL4                Second data location
...
BUF03    DS    CL4                Third data location
...
FINBUF   DS    0CL9               Final location
FINB01   DS    CL3                First final part
FINB02   DS    CL3                Second final part
FINB03   DS    CL3                Third final part

         L     R7,BUF01          Load first data to available gp register    
         STCM  R7,B'0111',FINB01 Store low-order three where needed

         L     R7,BUF02          Load second data to available gp register    
         STCM  R7,B'0111',FINB02 Store low-order three where needed

         L     R7,BUF03          Load third data to available gp register    
         STCM  R7,B'0111',FINB03 Store low-order three where needed
         ETVOILA                 Required data arrives at this point in FINBUF

If your data (or any of it) happens to already be in registers, ditch the LOAD and change the R7 to the appropriate register.

The advantage of ICM with B'1111', which you have used in your question, is that it sets the Condition Code, a LOAD does not. If you do not need the CC, then ICM is slower than LOAD, so you'd not use it.

Here, there is no problem with blindly loading four bytes to a register, then storing the low-order three. Lots of variations possible, but no reason to keep it other than simple.

The entire four bytes are loaded into the register. Only the three low-order bytes of the register are stored in memory, due to the mask being B'0111'. Any bytes from the register which correspond to a 1 in the mask will be stored contiguously starting at the address of the second operand. You get your contiguous nine bytes by just not storing the first byte ever. No need for shift and then combine.

Here is an extract from the explanation of the Store Characters Under Mask (STCM) instruction, from page 313 of Chapter 7. General Instructions of the current (from the z/OS 2.1 elements and features page linking to documentation) z/Architecture Principles of Operation, which is your reference guide for Mainframe Assembler.

Bytes selected from general register R(1) under control of a mask are placed at contiguous byte locations beginning at the second-operand address.

The contents of the M(3) field are used as a mask. These four bits, left to right, correspond one for one with four bytes, left to right, of general register R(1). For STORE CHARACTERS UNDER MASK (STCM, STCMY), the four bytes to which the mask bits correspond > are in bit positions 32-63 of general register R(1)... The bytes corresponding to ones in the mask are placed in the same order at successive and contiguous storage locations beginning at the second-operand address. When the mask is not zero, the length of the second operand is equal to the number of ones in the mask. The contents of the general register remain unchanged.

Note, your CPU may not be the latest, but the description and use of all the instructions in your question will be the same. Ask your Technical Support which POP (Principles of Operation) you should use to match your actual CPU.

Upvotes: 1

Michael Dorgan
Michael Dorgan

Reputation: 12515


ICM  R7,B'1111',FIRSTBUF       LOAD 4 BYTES FROM FIRSTBUF INTO REGISTER 7

SLL  R7,2       ; Shift away zeros
LR   R8,R7       ; Move to a work register
AND  R8, 0x3F   ; Clear out extra bits.
; First Character complete.

SLL  R7,8       ; Remove 1 character and lower 2 bit 0s by shifting away.
LR   R9,R7       ; Move to another work register
AND  R9, 0x3F    ; Clear out extra bits.
SLL  R9, 6       ; Shift up to proper location
O    R8, R9      ; Drop it in
; Second Character complete.

SLL  R7,8       ; Remove 1 character and lower 2 bit 0s by shifting away.
LR   R9,R7       ; Move to register
AND  R9, 0x3F    ; Clear out extra bits.
SLL  R9, 14      ; Shift up to proper location
O    R8, R9      ; Drop it in
; Third Character complete.

SLL  R7,8        ; Remove 1 character and lower 2 bit 0s by shifting away.
LR   R9,R7       ; Move to register
AND  R9, 0x3F    ; Clear out extra bits.
SLL  R9, 22      ; Shift up to proper location
O    R8, R9      ; Drop it in

; And so on until you want to store the result, or your holder register is full.

I have not coded in this assembler before, but one assembler is a lot like another and the above should demonstrate the ideas of bit manipulation with bitwise and/or combined with shifting. It also buffers I/O by not constatnly writing data to memory, instead using registers to speed things up.

Good luck!

Upvotes: 1

Related Questions