Operations we'll get to know (and love):
The rules for doing the arithmetic operations vary depending on what representation is implied.
For adding unsigned integers, use the following truth table as a guide.
carry in a b | sum carry out ---------------+---------------- 0 0 0 | 0 0 0 0 1 | 1 0 0 1 0 | 1 0 0 1 1 | 0 1 1 0 0 | 1 0 1 0 1 | 0 1 1 1 0 | 0 1 1 1 1 | 1 1 |
a 0011 +b +0001 -- ----- sum 0100
It is really just like we do for decimal!
0 + 0 = 0 1 + 0 = 1 1 + 1 = 2 which is 10 in binary, sum is 0 and carry the 1. 1 + 1 + 1 = 3 sum is 1, and carry a 1.
It is just like the simple addition given above.
Examples:
100001 00001010 (10) +011101 +00001110 (14) ------- --------- 111110 00011000 (24)
Ignore (throw away) carry out of the msb.
We do unsigned addition with the assumption that we are working of a fixed precision value. Fixed precision means that the number of bits can not change.
The rules:
Examples:
00011 (3) 101000 111111 (-1) + 11100 (-4) + 010000 + 001000 (8) ------------ -------- -------- 11111 (-1) 111000 1 000111 (7) ^ this bit is thrown away
general rules:
1 - 1 = 0 0 - 0 = 0 1 - 0 = 1 10 - 1 = 1 0 - 1 = borrow!
It only makes sense to subtract a smaller number from a larger one.
Example:
1011 (11) must borrow - 0111 (7) ------------ 0100 (4)
Change the problem to addition!
a - b becomes a + (-b)
So, get the additive inverse of b, and do addition.
Example:
examples 10110 (-10) - 00011 (3) --> 00011 ------------ 11100 (after taking the 1's complement) + 1 ------- 11101 (-3) so do 10110 (-10) + 11101 (-3) ------------ 10011 (-13) (throw away carry out)
For unsigned integers, overflow occurs when there is a carry out of the msb.
1000 (8) +1001 (9) ----- 1 0001 (1)
For 2's complement integers, overflow occurs when the signs of the addends are the same, and the sign of the result is different
0011 (3) + 0110 (6) ---------- 1001 (-7) (note that a correct answer would be 9, but 9 cannot be represented in 4-bit 2's complement)
A detail: you will never get overflow when adding 2 numbers of opposite signs.
0 x 0 = 0 0 x 1 = 0 1 x 0 = 0 1 x 1 = 1
Longhand, it looks just like decimal.
The result can require 2 times as many bits as operands.
Example:
0111 (7) * 0110 (6) ----------- 0000 0111 0111 + 0000 ----------- 101010 (42)
If more bits are needed for the result, zero-extend this sum.
101010 becomes 00101010
quotient ---------------- divisor | dividend
Or, written as fractions:
dividend -------- divisor
Example:
15 / 3 1111 / 011 0101 ----------- 011 | 1111 -0 -- 11 - 11 ---- 01 -0 -- 11 - 11 ---- 0 (remainder)
Another example:
20 / 3 010100 / 011
000110 (quotient = 6) ----------- 011 | 010100 -0 --- 01 -0 --- 010 -0 --- 0101 - 011 ---- 00100 - 011 ---- 0010 - 0 ---- 10 (remainder = 2)
They are done bitwise. (Just like in C, Java, and C++.)
Bitwise means corresponding bits of the operands are operated on, with the single bit result going into the corresponding bit of the result.
Bit 0 of X goes with bit 0 of Y, and the result goes in bit 0.
For example:
X = 0011 Y = 1010 X AND Y is 0010 X OR Y is 1011 X NOR Y is 0100 X XOR Y is 1001 etc.
This is a way of moving bits around within a word. It exists in many high level languages (such as C), but is less commonly used, since there is little call for it in high level language source code.
A shift by n places is the same as n shifts by 1 place.
There are 3 types, and each type can go either left or right.
An example:
00110101 01101010 (logically left shifted by 1 bit)
An example:
00110101 00011010 (logically right shifted by 1 bit)
Examples:
00110101 -> 00011010 (arithmetically right shifted by 1 bit) 1100 -> 1110 (arithmetically right shifted by 1 bit)
Examples:
00110101 -> 01101010 (rotated left by 1 place) 1100 -> 1001 (rotated left by 1 place)
Examples:
00110101 -> 10011010 (rotated right by 1 place) 1100 -> 0110 (rotated right by 1 place)
All architectures provide instructions to do a variety of bitwise logical operations and shift operations. Which are included in the instruction set varies, but will form a sufficient set for doing any necessary operations.
For the examples that follow, pretend that an architecture has the following insturctions. This set corresponds to available instructions on the MIPS architecture, although simpifications have been made with respect to where the operands are.
All the variables are presumed to be integer sized.
not x, y x <- NOT (y) and x, y, z x <- (y) AND (z) or x, y, z x <- (y) OR (z) nor x, y, z x <- (y) NOR (z) nand x, y, z x <- (y) NAND (z) xor x, y, z x <- (y) XOR (z) xnor x, y, z x <- (y) XNOR (z) sll x, y, value x <- (y), logically left shifted by value bits srl x, y, value x <- (y), logically right shifted by value bits sra x, y, value x <- (y), arithmetically right shifted by value bits rol x, y, value x <- (y), rotated left by value bits ror x, y, value x <- (y), rotated right by value bits
A common student question occurs as a result of learning of the shift and logical operations: what are they used for?
A certain program has a bunch of boolean values that it will need to maintain.
Place all these (bit-sized) variables into one (word-sized) integer variable:
Bit number: 31 30 . . . . 2 1 0 Contents var31 var30 var2 var1 var0
So, we have up to 32 variables placed into one integer.
Let this integer be called var
.
What would we want to do to these boolean variables? Answer: individually set (make a 1) or clear (make a 0) a variable.
code to set var5:
or var, var, 0x00000020
Whatever was in all bits other than bit 5 remain the same, and bit 5 (called var5 for this example, but not in code) gets the value 1.
code to clear var12 and var3:
and var, var, 0xffffeff7
Whatever was in all bits other than bits 12 and 3 remain the same, and bits 12 and 3 get the value 0.
The value 0xffffeff7 (or 0x00000020 in the previous example) is called a mask. It specifies the settings of bits. Here is the mapping of some of the bits to show how the mask was created.
bit # 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 contents 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1
Suppose that we want to do operations on strings. (A string is a bunch of characters that happen to reside next to each other in memory.)
What operations might be appropriate? Insert characters? Concatenate strings? Switch order of characters? Change lower case to upper case?
What do strings look like?
The ASCII characters are placed into memory one following
another. Yet, to operate on parts of strings, we might
want to work on integer-sized chunks. . .
An operand the size of an integer is most often called
a word on a computer.
Part of a string in a 32-bit word:
------------------------- | 'A' | 'B' | 'C' | 'D' | -------------------------
Call this variable x
.
Here is a code fragment that
reverses the middle two characters, without changing
the rest.
and temp1, x, 0x00ff0000 # extract out the 'B' byte and temp2, x, 0x0000ff00 # extract out the 'C' byte and x, x, 0xff0000ff # clear middle 2 bytes srl temp1, temp1, 8 # move the 'B' 1 byte to right sll temp2, temp2, 8 # move the 'C' 1 byte to left or x, x, temp1 # merge moved 'B' back in or x, x, temp2 # merge moved 'C' back in
Copyright © Karen Miller, 2006 |