6. Hawk Three Register Instructions

Part of the Hawk Manual
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

Contents

6.1. Format
6.2. Shift
6.3. Byte and Halfword Stuffing
6.4. Byte and Halfword Extraction
6.5. Add and Subtract


6.1. Format

Three Register Format

 _______________________________
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
|15   12|11    8|7     4|3     0|
|       |  dst  |  s1   |  s2   |

The three register arithmetic format instructions are 16 bits each, specifying two source registers and a destination register.

 

6.2. Shift

 1 0 1 1                          MOVSL  r[dst] = setccv(r[s1]<<s2)
 1 0 1 0                          ADDSL  r[dst] = setccv((r[dst]<<s2)+r[s1])
 1 0 0 1                          ADDSR  r[dst] = setcc(sxt(r[dst]+r[s1])>>s2)
 1 0 0 0                          ADDSRU r[dst] = setcc(pad(r[dst]+r[s1])>>s2)

All shift instructions use the s2 field as a shift count, where shift counts from 1 to 15 are coded using the usual binary codes, and the code s2=0000 is used for a count of 16. The MOVSL instruction moves an operand from register to register with a left shift, while the ADDSx instructions add r[s1] to r[dst] with a shift of up to 16 places left or right. ADDSL shifts r[dst] left prior to addition. ADDSR and ADDSRU shift r[dst] right after addition. ADDSR shifts copies of the sign bit into the most significant bits of r[dst], while ADDSRU shifts zero into the most significant bits of the sign bit.

After MOVSL and ADDSL, the conditon codes will be set to reflect the result; if any one bits are shifted out of r[dst] or if the add results in a carry out, the C bit will be set; if the sign of r[dst] is different from the expected sign, the V bit will be set, and if the result is negative or zero, the N and Z bits will be set accordingly.

After ADDSR and ADDSRU, the N and Z bits will be set as usual. The V bit will be set if any one bits are shifted out of the result, and the C bit will be set to the last bit shifted out.

The ADDSL instruction is primarily intended to speed fast multiplication and array indexing computations. Given that R1 points to an array and that R2 contains a displacement into the array, where array elements have sizes that are powers of two, ADDSL R1,R2,X can be used to compute the address of the addressed array element in R1. Single ADDS instructions can be used to multiply and divide by many common constants:

        ADDSL  R1,R0,1    ; multiply by 2
        ADDSL  R1,R1,1    ; multiply by 3
        ADDSL  R1,R0,2    ; multiply by 4
        ADDSL  R1,R1,2    ; multiply by 5
        ADDSL  R1,R0,3    ; multiply by 8
        ADDSL  R1,R1,3    ; multiply by 9

        ADDSR  R1,R0,1    ; signed divide by 2, truncating downward
        ADDSRU R1,R0,1    ; unsigned divide by 2, truncated
        ADDSR  R1,R0,2    ; signed divide by 4, truncating downward
        ADDSRU R1,R0,4    ; unsigned divide by 8, truncated

For more details on multiplication and division, see the discussions in the chapter on multiplication and division.

Note that when the second operand of the ADDS instructions is R0, these instructions simply shift their first operand. Assemblers should support the SL, SR and SRU, shift left, shift right and shift right unsigned, symbolic opcode names for thiese uses:

 1 0 1 0         0 0 0 0          SL     r[dst] = setcc(r[dst]<<s2)
 1 0 0 1         0 0 0 0          SR     r[dst] = setcc(sxt(r[dst]>>s2))
 1 0 0 0         0 0 0 0          SRU    r[dst] = setcc(pad(r[dst]>>s2))

The MOVSL instruction, with R0 as the destination register, moves one of the most significant 16 bits of S1 into the C condition code, while the ADDSR instruction, with R0 as the destination register, moves one of the least significant 16 bits of S1 into C. As a result, these are the preferred instructions for testing individual bits in a word. Assemblers should support the BITTST symbolic opcode name for this function:

 1 0 1 1 0 0 0 0                  BITTST C = setccv(s1>>32-s2)
 1 0 0 1 0 0 0 0                  BITTST C = setcc(s1<<s2+1)

The assembler should generate one or the other of the above BITTST instructions, depending on the bit number provided, so that BITTST r,0 tests the least significant bit and BITTST r,31 tests the most significant bit.

 

6.3. Byte and Halfword Stuffing

 0 1 1 1                          STUFFB bytealign(r[dst],r[s1],r[s2])
 0 1 1 0                          STUFFH halfalign(r[dst],r[s1],r[s2])

The stuff instructions take the least significant byte or halfword from r[s1] and move it into the byte or halfword within r[dst] specified by the least significant bits of r[s2]. Formally:

        bytealign(dst,src,pos)
	   shift = (pos & 3)*8
	   mask = ~(#FF << shift)
	   dst  = (dst & mask) | ((src & #FF) << shift)

        halfalign(dst,src,pos)
	   shift = (pos & 2)*8
	   mask = ~(#FFFF << shift)
	   dst  = (dst & mask) | ((src & #FFFF) << shift)

The stuff operations have no effect on the conditon codes. Their primary use is in storing bytes and halfwords in memory. For example, if R2 holds a character and if R1 is a pointer to an arbitrary byte in memory, the following instruction sequence will store that character in that byte:

	LOADS  R3,R1
	STUFFB R3,R2,R1
	STORES R3,R1

This idea comes from the DEC Alpha architecture; on 32 bit machines that allow programmers to store bytes in arbitrary memory locations, the sequence of operations performed inside the CPU is generally as complex as this instruction sequence.

 

6.4. Byte and Halfword Extraction

 0 1 0 1                          EXTB   r[dst] = setccx(bytesel(r[s1],r[s2]))
 0 1 0 0                          EXTH   r[dst] = setccx(halfsel(r[s1],r[s2]))

The extract instructions take use the least significant two bits of r[s2] to select a byte or halfword from r[s1] and store that byte or word in the least significant byte or halfword of r[dst]. Formally:

        bytesel(src,pos)
	   shift = (pos & 3)*8
	   return sxt((src >> shift) & #FF)

        halfsel(src,pos)
	   shift = (pos & 2)*8
	   return sxt((src >> shift) & #FFFF)

The extract operations set the N and Z condition codes to report whether the result was negative or zero. The V and C condition codes are set to report on the bits to the left of the extracted field. C is set if any of these bits are nonzero, and V is set if any of these bits differ from the sign bit of the extracted field.

The primary use of these instructions is for loading unsigned bytes and halfwords from memory. For example, if R1 is a pointer to an arbitrary byte in memory, the following instruction sequence will load a character from that byte into R2.

        LOADS  R3,R1
        EXTB   R2,R3,R1

This idea comes from the DEC Alpha architecture.

The setting of the C condition code for these truncate operations allows testing for unsigned values out of bounds, and the setting of the V condition code allows for testing of signed values out of bounds.

 

6.5. Add and Subtract

 0 0 1 1                          ADD    r[dst] = setccv(r[s1]+r[s2])
 0 0 1 0                          SUB    r[dst] = setccv(r[s1]-r[s2])
                                                = setccv(r[s1]+~r[s2]+1)

The add and subtract instructions are used to perform integer arithmetic. Subtraction is done by adding the two's complement of the subtrahend. The condition codes are set to report on the result, with N and Z reporting whether the result is negative or zero, V reporting whether two's complement overflow occurred, and C reporting whether there was a carry out of the high bit.

Note that, for subtraction, a carry out of the high bit means that there was no borrow out of the high bit.

To compute the two's complement of a number in a register, subtract it from zero. To compare two numbers, subtract one from the other and discard the result by storing it in r[0]. These operations are distinctive enough to merit their own instruction mnemonics:

 0 0 1 0         0 0 0 0          NEG    r[dst] = setccv(-r[s2])
 0 0 1 0 0 0 0 0                  CMP    setccv(r[s1]-r[s2])