Introduction to Binary Computers

advertisement
Modified version
Introduction to Binary Computers
Steve Jost
January 1997
Table of Contents
1. A Brief History of Computers 1
2. Binary and Hexadecimal Numbers 2
3. Decimal-to-Binary Conversion 3
4. Binary Arithmetic 5
5. Representation of Negative Numbers 7
6. Bitwise Operations 8
7. Introduction to MACHINE-16 9
8. Addressing Modes 15
1. A Brief History of Computers.
1200 The modern abacus was invented.
1642 Blaise Pascal invented the first calculating machine at the age of 19.
1672 Gottfried Leibniz built a calculating machine that could add, subtract, multiply, and divide.
1837 Charles Babbage built the first mechanical computer capable of multistep programs. This machine
was about 100 years ahead of its time and little further progress was made until the 20th century.
1842 Probably the first programmer was Ada Augusta, the Countess of Lovelace. She worked with Babbage helping him write programs for his “analytical engine.” She noted that the machine could not “originate anything” but could only do “what we know how to order it to perform.” The computer language ADA
developed in the 1970's was named after her.
1887 Dr. Herman Hollerith developed a punched card reading machine to assist in tabulating data for the
1890 U. S. census. Without this machine, processing this data would have taken more than ten years! With
the machine, the data processing for the census only took three years.
1937 Howard Aitken who was a professor at Harvard built the Mark I digital computer. It was controlled
with electromagnetic relays and received its input from punched cards.
1939 The first electronic computer using vacuum tubes for switching was constructed by Dr. John Vincent
Atanasoff at Iowa State college and was called the ABC (Atanasoff-Berry Computer). It was built for solving systems of simultaneous equations.
1940 The first general purpose computer was built by Atanasoff together with John Mauchly and J. Presper
Eckert. This computer was called ENIAC (Electronic Numerical Integrator and Calculator) and funded by
the U.S. army. It could do 300 multiplications per second which was 300 times faster than any other machine of the day, but is contained 18,000 vacuum tubes and weighed 30 tons. The ENIAC was programmed
by externally manipulating plugs and switches. The machine was used by the army until 1955.
1
1945 John von Neumann proposed two ideas which made modern high speed computers possible. These
ideas were using binary numbers to store data, and storing instructions as data rather than entering them by
switches or plugs as was done in earlier machines.
1949 The transistor was invented by Bardeen, Braitain, and Shockley at Bell Telephone Laboratories, who
shared the Nobel Prize for this invention which made modern electronics possible.
1949 The first stored program electronic computer was developed at Cambridge University. It was built by
M.V. Wilkes and called EDSAC (Electronic Delay Storage Automatic Calculator.)
1950 Computer industry forcasters concluded that about 10 stored memory computers would meet the demand for the entire U.S for years to come. This turned out to be one of the worst forecasts in history.
1954 International Business machines (IBM) sold the first computer for record keeping and business organization. This machine was less expensive than other computers available at the time and was widely
accepted. These early computers were called first generation computers. They were programmed in binary
machine language which a difficult and error prone process. First generation computers were originally
designed for scientific operations.
1952 The first high level language was invented by Dr. Grace Hopper. She developed a compiler for the
language called A-2 which converted instructions into machine language.
1954 The FORTRAN (FORmula TRANslator) language was developed at IBM by a programming team
headed by John Backus. FORTRAN is still widely used for scientific applications.
1959 The first second generation computers were introduced which were smaller and faster than the first
generation ones. They used solid state devices such as diodes and transistors instead of vacuum tubes. In
addition computers which accepted instructions in high level languages became widespread.
1959 To meet the increasing demand for data processing, the language COBOL (COmmon Business Oriented Language) was developed. This language became popular because it was written in a quasi-English
form that could be more easily understood by nonprogrammers than other languages of that time.
1963 The BASIC (Beginners All Purpose Symbolic Instruction Code) language was developed at Dartmouth college by John Kemeny and Thomas Kurtz. It was introduced as a language which was easy for
students to learn, and was available on a timesharing computer which allowed several users to take turns
sharing the central processing unit of the computer.
1970 The C programming language was designed by Dennis Ritchie. Its name comes from the fact that it
was an improved version of the language BCPL or B for short. Many applications which formerly were
written in assembly language are now written in C.
1976 The first Cray supercomputer was built. It is capable of performing more than 100 million floating
point operations per second (100 megaflops). Computers with the capability of performing more than 10
billion floating point operations per second (10 gigaflops) will be possible within the next ten years.
2. Binary Numbers for Computers
Many electronic hardware devices have two natural states such as conducting or not conducting,
magnetized or not magnetized, positive or negative, on or off, etc. Using the binary numbers 0 or 1 greatly
simplifies the design of electronic computers. However, because long sequences of 0's and 1's are difficult
to read and understand, binary digits are conventionally grouped to make them more comprehensible. The
following table shows the terms that are sometimes used to denote various sized groups of binary digits.
2
Number of Binary Digits
=======================
1
4
8
16
32
Term
====
bit
nibble
byte
word
longword
Here are some of the common C datatypes and their properties.
C Datatype
char
unsigned char
short int
unsigned short int
long int
unsigned long int
float
double
Number of bits
8
8
16
16
32
32
32
64
Number of bytes
1
1
2
2
4
4
4
8
Minimum Value
-128
0
-32,768
0
-2,147,483,648
0
-3.40 x 10^38
-1.79 x 10^308
Maximum Value
127
255
32,767
65,536
2,147,483,647
4,294,967,296
3.40 x 10^38
1.79 x 10^308
The size of the datatype int depends on the particular implementation of C being used. On a mainframe or minicomputer, the likely size of an int is 4 bytes, while on a personal computer or microcomputer,
its size is probably 2 bytes. As an introduction to digital computers, we will study a simplified computer
called MACHINE-16 which only uses numbers of length 8 bits or 1 byte. The reason for the name MACHINE-16 is that it has 16 instructions for manipulating data. MACHINE-16 is a Von Neumann machine,
described in Section 1, in the sense that both data and instructions can be stored in its memory. The instructions are executed sequentially one at a time, with the possibility of looping back to repeat a block of instructions several times. Although modern assembly languages are much more powerful than the language
of MACHINE-16, the 16 instructions in its instruction set are powerful enough to solve a wide variety of
problems. Some of these problems will be discussed in Section 6.
3. Decimal-to-Binary Conversion
Because MACHINE-16 uses 8 bit binary arithmetic, all of our examples will be with 8 bit numbers. Initially, we will only consider positive or unsigned binary numbers. Later, we will see how negative
numbers can be represented with eight bits. To convert a binary number into base 10 or vice versa, it is
convenient to use the Binary-Hex-Decimal conversion table shown in below. A binary 1 means that that
power of two is present while a 0 means that it is absent. Here is a table showing the various powers of two
that we will need:
Binary
========
1
10
100
1000
10000
100000
1000000
10000000
Decimal
=======
1
2
4
8
16
32
64
128
For example 01001101 = 64 + 8 + 4 + 1 = 77.
3
To convert a decimal number to binary, the procedure is reversed: express the decimal number as a
sum of powers of 2 and then write this sum as a sequence of binary bits. For example, to convert the decimal number 77 into binary, we write
77
-64
-13
- 8
-5
- 4
-1
- 1
-0
We select for each subtraction, the largest power of 2 which is less than or equal to the amount remaining. We then represent those powers of 2 present as 1 and those powers of 2 missing as 0. This gives
64 + 8 + 4 + 1 = 01001101.
Because long binary numbers are hard to read, computer programmers prefer to express them in a
form that is more easily read. The most common choice of notation today is the hexadecimal (base 16)
representation where each four bit group of digits (nibble) is represented by a single hexadecimal digit.
These digits are shown in the following Binary-Hex-Conversion table. Because we will be using the hex
representation for machine code input to MACHINE-16, it is essential that this table be memorized.
Binary Hex
====== ===
0000
0
0001
1
0010
2
0011
3
0100
4
0101
5
0110
6
0111
7
1000
8
1001
9
1010
a
1011
b
1100
c
1101
d
1110
e
1111
f
Decimal
=======
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
To represent a binary number in hex notation, simply replace each nibble by its corresponding hex digit:
0110
6
1001
9
1110
e
1001
9
0100
4
1100
c
0101
5
0001
1
The binary numbers we consider will be 8 bit numbers, each of which can be represented by 2 hex
digits. We will employ the C language representation which uses the prefix “0x” (zero x) for hex numbers.
For example the expression 0x5d represents the binary number 01011101. It is important to note that the
internal binary representation of an integer is independent of the form that the number takes when it is printed. The binary number 01011101 looks like 93 when printed with the C++ manipulator dec , like
5d when printed with hex, and like ]when printed as a regular char.
Exercises
4
Assume that all binary numbers consist of 8 bits and are unsigned, that is, are in the range 0 to 255. Signed
binary numbers are discussed in Section 5.
1. Convert the following binary numbers to decimal:
01101011, 01110000, 00000111,
11111111,
01010101
2. Convert the following decimal numbers to binary:
49, 7, 153, 200, 191, 128, 93
3. Convert the following hex numbers to binary:
0x41, 0x3f, 0x83, 0xef, 0x20,
4.
Convert the following binary numbers to hex:
1101101, 11101101, 11010000,
0x2a,
00000111,
0xbb
11111111
5. Binary Arithmetic
The four basic operations addition, subtraction, multiplication, and division are all easily performed with binary numbers. In fact they are actually easier to perform in binary because the addition and
multiplication tables are so small. Here are basic addition facts in binary.
0
0
1
1
+
+
+
+
0
1
0
1
= 0
= 1
= 1
= 10
0
0
1
1
x
x
x
x
0
1
0
1
=
=
=
=
/* Result is 0, carry 1 */
0
0
0
1
Carrying and borrowing are also performed as in decimal arithmetic. To add the numbers 0x3a and
0x27, first convert to binary, and add from right to left, carrying 1 to the next column when the result is 10.
58
+39
-97
0x3a
+0x27
---0x61
--->
--->
<---
00111010
00100111
-------01100001
To subtract two binary numbers, borrow 1 when the bottom digit is 1 and the top digit is 0. For example, to
compute 0x3a - 0x27,
5
58
-39
-19
0x3a
- 0x27
---0x13
--->
--->
<---
00111010
00100111
-------00010011
Binary multiplication is also similar to decimal multiplication. The top factor is used as a partial
product for each binary 1 in the bottom factor. The top factor is shifted by as many places to the left as the
power of 2 that the binary 1 represents. To multiply 0x1e by 0x06,
20
x 6
---
120
0x14
x 0x06
----
0x78
--->
--->
00010100
00000110
-------000101000
000101000
-----------<--- 0001111000
Shift by 1 bit
Shift by 2 bits
Only the rightmost eight bits are retained in the answer. Any bits to the left of these 8 bits are called overflow bits. In case any of the overflow bits are nonzero, an overflow has occurred and the rightmost eight bits
which are retained in the answer are invalid. Because integer arithmetic operations are not checked for
overflow in the C language, it is possible to obtain an incorrect answer when two numbers are multiplied or
added. For example, when we multiply 58 and 39 and we store the result as unsigned char (8 bits), we obtain the answer 214 when the actual answer is 2262. The following calculation shows why:
58
x 39
--
0x3a
x 0x27
----
--->
--->
00111010
00100111
-------00111010
00111010
00111010
00111010
------------0100011010110
Retain eight low order bits
Only the rightmost eight bits 11010110 (hex 0xd6 or 214 decimal) are retained.
Division is also performed as it is for decimal numbers. Unlike base 10 long division, no guesswork is involved to decide how many times the divisor goes into the trial dividend. Either it goes (quotient
= 1) or it doesn't (quotient = 0). As an example, divide 213 (binary 1101010, hex 0xd5) by 9 (binary
00001001, hex 0x09).
10111
-------1001 ) 11010101
10010000
-------1000101
100100
------1111
1001
---110
<-- quotient
<-- remainder
The answer is 23 (binary 00010111, hex 0x17) with a remainder of 6 (binary 00000110).
Exercise
6
Convert the following hex numbers to binary, perform the following operations, and convert back.
0x45 + 0x3f, 0x5a + 0x74, 0xe2 - 0xaf, 0xa4 - 0x9c,
0x0a x 0x2f, 0x47 x 0x1b, 0xc4 / 0x2e, 0xbe / 0x19
5. Representation of Negative Numbers
To represent negative numbers, we use the analogy of an automobile odometer. Because an odometer display has only five decimal digits to the left of the decimal point, only numbers in the range 0 to
99999 can be shown even though the actual number of miles may be much greater. The number 0 represents 0, 100000, 200000, etc., but it can also represent the negative numbers -100000, -200000, etc. The
number 99999 represents 199999, 299999, and also -1, -100001, -200001, etc. By adopting the convention
that 1 to 99999 represent positive numbers and 50000 to 99999 represent negative numbers, all numbers
from starting from -50000 to 49999 can be represented. In the case of 8 bit binary numbers, we let
00000000 through 01111111 represent positive numbers and 10000000 through 11111111 represent
negative numbers. By this convention, if the leftmost bit is 0, the number is positive, and if the leftmost bit
is 1, the number is negative. In this way, an 8 bit binary number can represent all integers between -128
and 127. Such a representation is called a signed integer. The following table shows how to represent
negative numbers in binary.
Binary
01111111
01111110
..
00000010
00000001
00000000
11111111
11111110
..
10000001
10000000
Hex
7f
7e
..
02
01
00
ff
fe
..
81
80
Decimal
127
126
..
2
1
0
-1
-2
..
-127
-128
Converting a an eight bit binary positive number into the corresponding negative one involves subtracting it from 100000000. (The eight bit representation of 100000000 is 00000000.) For example, to
convert 0x05 = 00000101 to a negative number, subtract it from 100000000 to obtain 11111011. This
operation is called obtaining the two's complement of a binary number. In general the two's complement of
a binary number is formed by subtracting it from 2n where n is the number of bits. Another way of performing the two's complement is to complement each digit (replace 0 by 1 and 1 by 0 ) and add 1 to the result. The complement of 00000101 is 11111010 and adding 1 produces 11111011. To verify that these
numbers are actually negatives of each other, we can add them to see that the sum is 100000000 =
00000000 when truncated to eight bits.
The beauty of two's complement arithmetic is that no special rules are needed to accommodate
negative numbers. Merely perform the operation and keep the rightmost eight bits. Here are some examples:
7
- 29
+ 42
---13
-29
- (-42)
----13
(-6)
x (-5)
-----
30
<---
--->
--->
<----->
--->
<---
11100011
00101010
-------00001101
11100011
11010110
-------00001101
--->
--->
11111010
11111011
-------...111111111111010
...11111111111010
...111111111010
...11111111010
...1111111010
...111111010
...11111010
--------...000000000011110
retain
8 bits
6. Bitwise Operations
In addition to the 4 basic operations discussed in Section 3, C also allows the following operations: bitwise and ( & ), bitwise or ( | ), bitwise exclusive or ( ^ ), and bitwise complement (~). These bitwise operations are defined by the following tables.
x
0
1
0
1
y
0
0
1
1
x&y
0
0
0
1
x
0
1
0
1
y
0
0
1
1
x|y
0
1
1
1
x
0
1
0
1
y
0
0
1
1
x^y
0
1
1
0
x
0
1
!x
1
0
8
For eight bit binary numbers, these operations are performed on each column of bit pairs separately. For example, to compute 52 & 45, first convert to binary, perform the bitwise and 00110100 &
00101101 = 00100100, and convert back to decimal which gives 36. The bitwise and is useful for isolating
certain bits in a binary number to use in further computations. In the implementation of MACHINE-16
which is discussed in Sections 7 to 9, an eight bit machine instruction can be broken down into two parts,
the opcode which consists of bits 1 to 4, and the addressing mode which consists of bits 7 and 8. (Bits 5
and 6 will not be used in our implementation.)
To isolate the opcode, we can perform a bitwise and with the mask 0xf0 = 11110000. The bits 1 to 4 will
remain unchanged, but the bits 5 to 8 will be changed to zero. This can be performed by the C statement
opcode = instruction & 0xf 0; To obtain the addressing mode, use the statement
mode = instruction & 0x03;
because 0x03 = 00000011.
Exercises
1. Find the values of the following expressions:
0x75 & 0xfb, 0x3c | 0xe9,
0xfa ^ 0xa5,
~c7
2. In a hypothetical operating system, the bits 5 to 27 in the the 4 byte integer variable psw (processor status word) represent the state of the system. The 32 bits are numbered from 0 to 31 from left to right. Write
a hex mask which will isolate these bits (Hint: First write the mask in binary).
7. Introduction to MACHINE-16
The MACHINE-16 is a simple hypothetical stored memory computer which has 64 bytes of
memory and 16 instructions. Its capabilities are comparable to the earliest computers built in the 1930's, but
it is thousands of times faster because it will be implemented on a modern computer. The memory can be
thought of as an array of bytes (sometimes called cells) with addresses numbered 0x00 to 0x3f.
This memory array will be represented by the global array of char. We will refer to each byte as a memory
cell which will be abbreviated as MC. Here is an example:
01010110 01001001 00101001 00101001 01001010 10010100 ...
MC 0
MC 1
MC 2
MC 3
MC 4
MC 5 ...
Memory cells with addresses 0x00 and 0x01 (referred to in the C program as mem[0] and
mem[1]) each have a special purpose. The memory cell with address 0x00 is called the program counter
(PC) and is used to hold the address of the instruction which is currently executing. (This will make sense
later when you see what the instructions are.) The memory cell with address 0x01 is called the accumulator
(AC) and is used to hold the result of an arithmetic operation.
A MACHINE-16 instruction consists of two bytes (four nibbles or 16 bits). The first nibble is the
opcode and the second nibble is the addressing mode. The table below describes the 16 MACHINE-16
opcodes. In these descriptions, PC refers to the program counter, AC refers to the accumulator. The opcodes are referred to as 0x00, 0x10, 0x20 rather than 0x0, 0x1, 0x2b because the second nibble is cleared
to zero using a bitmask (see Section 9).
9
Opcode
0x00
0x10
0x20
0x30
0x40
0x50
0x60
0x70
0x80
0x90
0xa0
Mnemonic
STOP
LOAD
STORE
CLEAR
INCR
DECR
ADD
SUBT
MULT
DIV
JUMP
0xb0
BGZ
0xc0
0xd0
0xe0
0xf0
PRIND
PRINC
SCAND
SCANC
Meaning
exit(1);
AC = MC;
MC = AC;
MC = 0;
MC++;
MC--;
AC += MC;
AC -= MC;
AC *= MC;
AC /= MC;
Jump to address
in MC
If AC > 0 jump to
address in MC
Print MC as int
Print MC as char
Read MC as int
Read MC as char
The second byte (nibbles 3 and 4) of the instruction determines the memory cell (MC) which contains the information to be used by the instructions 0x00, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0,
and 0xd0. It can also determine the address where information is to be put (instructions 0x20, 0x30, 0xe0,
0xf0). In the preceding table of operands, the symbol = means assignment as in C++, ++ means increment,
and -- means decrement. The symbols +=, -=, *=, and /= have the same meanings as the corresponding
C++ assignment operators. We distinguish between assignment (A=5;) and initialization (char A=5;) in
exactly the same way that the C++ language does.
The addressing mode is the second nibble of the instruction and determines how to find the
memory cell on which the instruction operates MC from the operand. The four possible addressing modes
are shown in the following table, and are discussed in Section 8.
Addressing Mode
0x00
0x01
0x02
0x03
Mode Name
IMMEDIATE
DIRECT
INDIRECT
DINDIRECT
For simplicity, we only discuss the IMMEDIATE addressing mode in this section, which has the
code 0x00. If the addressing mode is IMMEDIATE, the address of MC is one greater than the address of
the current instruction. In other words, the operand contains literally the value to be supplied to the instruction.
In the IMMEDIATE mode, the operand is used as a literal constant. For example, the following
diagram shows the memory cells 0x00 to 0x07. Since the value of PC is 0x03, the instruction in cell
0x03 executes. The left nibble is 0x6 which means ADD. The right nibble is 0x0 which means IMMEDIATE addressing mode. We denote the opcode as 0x60 to show that the addressing mode has been zeroed
out and the addressing mode as 0x00. Next the PC is incremented by one byte so that it has the value 0x04
and it contains the address of the operand. Since the operand contains 0x05, this literal value is added to
AC which contains 0x07, so the new value of AC is 0c or decimal 12.
00 01 02 03 04 05 06 07 opcode: address byte 03, nibble 1, value 60
+--+--+--+--+--+--+--+--+ mode:
address byte 03, nibble 2, value 03
|03|07|00|60|05|00|00|00| operand: address byte 04,
value 05
10
+--+--+--+--+--+--+--+--+
Even though the IMMEDIATE mode is a legal addressing mode for all instructions, there are 3
instructions for which this mode is not generally useful. These instructions are STORE, SCAND, and
SCANC. These three instructions all deposit a value in MC which will overwrite the operand if the mode is
IMMEDIATE. The instructions INC and DEC are also not very useful in IMMEDIATE mode because
they change the value of the operand. For these instructions one of the other operating modes discussed in
Section 8 is more appropriate. We now look at two examples of programs written in the MACHINE-16
language. These examples use only the IMMEDIATE addressing mode.
Program 1: Add the numbers 5 and 7 and put the result in AC.
The sequence of instructions is
LOAD 5 IMMEDIATE
ADD 7 IMMEDIATE
STOP 0
Load the literal value 5 into AC
Add the literal value
7 to AC, the answer is in AC
Stop execution, 0 is a dummy operand.
To actually run program 1 (see Source Code for Program 1 below) on the MACHINE-16 simulator
we need to translate the mnemonic instructions into hex numbers. These numbers will occupy the first two
characters of each line of the source file. The source code contains six columns: the machine instructions
and operands, the address, the label, the mnemonic, the addressing mode, and the equivalent C instruction.
Of all these columns, only the first is read by MACHINE-16. There must also be no blank lines in the
source code.
As mentioned earlier, the first memory location (address 0x00) is the program counter (PC) and
the second location (address 0x01) is the accumulator (AC). (Pay special attention to this point. A common mistake when writing MACHINE-16 programs is to begin numbering the addresses at 0x01 rather
than at 0x00.) The PC is initialized to the starting address of the program. For this example, we start the
program at address 2, immediately after the PC and the AC. The accumulator AC can be initialized to any
value, say 0. The complete source code for program 1 is shown is as follows:
Source Code for Program 1: Add the numbers 5 and 7 and put the result in AC.
02
00
10
05
60
07
00
00
00
01
02
03
04
05
06
07
PC:
START
AC:
0
START: LOAD
5
ADD
7
STOP
0
IMMEDIATE
char PC = START,
char AC = 0;
AC = 5;
IMMEDIATE
AC += 7;
IMMEDIATE
exit(1);
To run this program use a text editor to create a file named file.sou. You can replace the name file
with any name you want, but you must have the .sou extension which indicates that it is source code for
MACHINE 16. Then type
mach file
at the DOS prompt to run the machine. See your instructor for details of how to obtain the executable for
MACHINE 16.
Dump for Program 1.
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
00 | 02 00 10 05 60 07 00 00 00 00 00 00 00 00 00 00 |
11
10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
MC
MC
COUNTER
PC INSTRUC OPCODE MODE
PC ADDRESS CONTENTS
=======
== ======= ====== ====
== ======= ========
1
02
10
10
00
03
03
05
2
04
60
60
00
05
05
07
3
06
00
00
00
07
07
00
00
10
20
30
AC
==
05
0c
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 07 0c 10 05 60 07 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
The dump will be written to the file named file.dmp. The dump shows the state of memory immediately after the instructions are loaded from the source file. The contents of all 64 memory locations (0063) are shown. To find, for example, the contents of the cell with address 0x2a, look in the row 20 and the
column a. We see that the contents are 00.
After the initial dump, a detailed program trace is shown. The meanings of each column in the
trace are as follows: the COUNTER sequentially numbers the instruction executions. In the current implementation of MACHINE-16, a maximum of 200 instruction execution steps are allowed to prevent infinite loops which will fill up the file system. The PC is the current value of the program counter. A valid
program may also exceed the 200 step limit, but this will be rare because the programs we will be executing
are short. This instruction is then broken up into the OPCODE and the addressing MODE which are
shown next. If the addressing mode is IMMEDIATE, the PC is now incremented by 1 to read the operand,
so PC is listed again to be sure that its value has actually changed.
If the value of MODE is 0 (IMMEDIATE), the value of this address is set to be the current value
of the PC because this is the address containing the operand. Finally the value of the accumulator AC is
shown. The value of AC is not shown for the STOP instruction 3 because the program exits before the
final value of AC is printed.
After the program terminates because of the STOP instruction, a final dump is shown of the
memory locations immediately after program termination. In this case, the dump is identical to the preliminary dump except for the change in the value of AC; it has been changed from 0x00 to 0x0c (decimal
12), the result of 5+7. This shows that the program has performed correctly. The dump and program trace
are valuable tools for debugging MACHINE-16 programs.
12
Program 2: Print the message HELLO.
MACHINE-16 is not limited to numeric computations. The Program 2 shows how to use it to print the
message “HELLO” to the screen. A new line character (ASCII code 0x0a) is printed at the end of the message so that the prompt ends up at the beginning of the next line. The dump shows that the only memory
location which has changed is the PC which has changed from its initial value 0x02 to 0x0f, the address
after the STOP instruction. Use the following table assist you in translating characters into their hexadecimal ASCII codes.
A Table of ASCII Codes.
char dec hex
\0
0 00
^A
1 01
^B
2 02
^C
3 03
^D
4 04
^E
5 05
^F
6 06
\a
7 07
\b
8 08
\t
9 09
\n
10 0a
\v
11 0b
\f
12 0c
\r
13 0d
^N
14 0e
^O
15 0f
^P
16 10
^Q
17 11
^R
18 12
^S
19 13
^T
20 14
^U
21 15
^V
22 16
^W
23 17
^X
24 18
^Y
25 19
^Z
26 1a
^[
27 1b
^\
28 1c
^]
29 1d
^^
30 1e
^_
31 1f
char dec hex
32 20
!
33 21
"
34 22
#
35 23
$
36 24
%
37 25
&
38 26
'
39 27
(
40 28
)
41 29
*
42 2a
+
43 2b
,
44 2c
45 2d
.
46 2e
/
47 2f
0
48 30
1
49 31
2
50 32
3
51 33
4
52 34
5
53 35
6
54 36
7
55 37
8
56 38
9
57 39
:
58 3a
;
59 3b
<
60 3c
=
61 3d
>
62 3e
?
63 3f
char dec hex
@
64 40
A
65 41
B
66 42
C
67 43
D
68 44
E
69 45
F
70 46
G
71 47
H
72 48
I
73 49
J
74 4a
K
75 4b
L
76 4c
M
77 4d
N
78 4e
O
79 4f
P
80 50
Q
81 51
R
82 52
S
83 53
T
84 54
U
85 55
V
86 56
W
87 57
X
88 58
Y
89 59
Z
90 5a
[
91 5b
\
92 5c
]
93 5d
^
94 5e
_
95 5f
13
char
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
^?
dec hex
96 60
97 61
98 62
99 63
100 64
101 65
102 66
103 67
104 68
105 69
106 6a
107 6b
108 6c
109 6d
110 6e
111 6f
112 70
113 71
114 72
115 73
116 74
117 75
118 76
119 77
120 78
121 79
122 7a
123 7b
124 7c
125 7d
126 7e
127 7f
Source Code for Program 2: Print the Message “Hello”.
02
00
d0
48
d0
45
d0
4c
d0
4c
d0
4f
d0
0a
00
00
00
01
02
03
04
05
06
07
08
09
0a
0b
0c
0d
0e
0f
PC:
AC:
START:
START
0
PRINC
'H'
PRINC
'E'
PRINC
'L'
PRINC
'L'
PRINC
'O'
PRINC
'\n'
STOP
#0
IMMEDIATE
IMMEDIATE
IMMEDIATE
IMMEDIATE
IMMEDIATE
IMMEDIATE
char PC = START,
AC = 0,
cout << ‘H’;
/*'H' == 0x48*/
cout << 'E';
/*'E' == 0x45*/
cout << 'L';
/*'L' == 0x4c*/
cout << 'L';
/*'L' == 0x4c*/
cout << 'O';
/*'O' == 0x4f*/
cout << '\n';
/*'\n'== 0x0a*/
exit(1);
Dump for Program 2
00
10
20
30
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 02 00 d0 48 d0 45 d0 4c d0 4c d0 4f d0 0a 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
COUNTER
=======
1
2
3
4
5
6
7
00
10
20
30
PC INSTRUC OPCODE MODE
== ======= ====== ====
02
d0
d0
00
04
d0
d0
00
06
d0
d0
00
08
d0
d0
00
0a
d0
d0
00
0c
d0
d0
00
0e
00
00
00
PC ADDRESS CONTENTS
== ======= ========
03
03
48
05
05
45
07
07
4c
09
09
4c
0b
0b
4f
0d
0d
0a
0f
0f
00
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 0f 00 d0 48 d0 45 d0 4c d0 4c D0 4f d0 0a 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
14
AC
==
00
00
00
00
00
00
Exercises
1. Write a program which will compute the expression
(-2+8)*(3+13) and print out the answer.
2. Print out a message of your choice.
8. Addressing Modes
We have already discussed the IMMEDIATE addressing mode in the last section. In this section
we introduce the DIRECT and INDIRECT addressing modes. The following Figure illustrates the four
addressing modes. Recall that the opcode 0x60 means ADD. In this Figure, 6 is the opcode and 0 is the
addressing mode, so the operand 02 is contained in the address immediately following the instruction in the
address 04, which means that the literal constant 02 is added to the value of AC which is 05, changing
AC to 07.
Here are illustrations of the four addressing modes: In each case, the addressing mode is the second nibble of the byte at address 0x03.
00 01 02 03 04 05 06 07
+--+--+--+--+--+--+--+--+
|03|05|07|60|02|06|02|05| IMMEDIATE
+--+--+--+--+--+--+--+--+
PC AC
^
00 01 02 03 04 05 06 07
+--+--+--+--+--+--+--+--+
|03|05|07|61|02|06|02|05| DIRECT
+--+--+--+--+--+--+--+--+
^
|
+----+
00 01 02 03 04 05 06 07
+--+--+--+--+--+--+--+--+
|03|05|07|62|02|06|02|05| INDIRECT
+--+--+--+--+--+--+--+--+
|^
|
^
|+----+
|
+---------------+
00 01 02 03 04 05 06 07
+--+--+--+--+--+--+--+--+
|03|05|07|63|02|06|02|05| DINDIRECT
+--+--+--+--+--+--+--+--+
|^
|
^
|^
|+----+
+----+|
+---------------+
To implement variables, the DIRECT addressing mode is most useful. In this case, the operand does not
contain a literal constant, but the address of a variable. The operand contains 02 which is used as an address to obtain the value 07 which is in memory cell 02. This value 07 is now added to the value 05 in
AC to obtain 0c (decimal 12).
When pointers are necessary, we use the INDIRECT addressing mode, which uses the address
stored at the opcode as the address containing the value of interest. In other words, you don't use the address
contained in the operand to find the value of interest, you use the address in the operand as the address of
15
the value you want, just like ordinary pointers in C++. The mode 02 denotes INDIRECT addressing, the
operand contains the address 02, memory location 02 contains the address 07 which contains the value
05. This is added to the 05 in AC to produce 0a.
The DINDIRECT (double indirect) addressing mode which is needed to implement double pointers (declared as char **p;. or char *p[]; in C++). Double pointers are used in situations such as the address of the beginning of an array of pointers. We will not discuss the DINDIRECT addressing mode in
CSC310.
Program 3: Adding two variables using the DIRECT addressing mode.
This program which initializes variables to values of 5 and 7, adds the contents of these variables,
and prints the result as a decimal number. The DIRECT mode must be used because the operand of the
ADD command does not contain the literal value to be added, but contains the address of the variable which
contains the value. This output is printed at the screen, even though the dump is sent to the dump file. If
you want to save the output in a file, redirect the output as usual from the DOS prompt
mach file > file.out
Source Code for Program 3: Adding Two Variables using DIRECT addressing mode.
04
00
05
07
11
02
61
03
c1
01
00
00
00
01
02
03
04
05
06
07
08
09
0a
0b
PC:
AC:
X:
Y:
START:
START
0
5
7
LOAD
X
ADD
Y
PRIND
AC
STOP
0
DIRECT
char
char
char
char
AC =
PC = START;
AC = 0;
X = 5;
Y = 7;
X;
DIRECT
AC += Y;
DIRECT
cout << int(AC);
IMMEDIATE exit(1);
The label X denotes the address of the variable X, which is 0x02. The address of Y is 0x03.
This example illustrates that MACHINE-16, like other Von Neumann digital computers, makes no distinction between data and instructions in its memory cells; everything is merely eight bit binary numbers. It is
up the program to interpret these numbers. The dump is shown in next and the output from the program to
the screen is 12, which is 5+7.
16
Dump for Program 3:
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
00 | 04 00 05 07 11 02 61 03 c1 01 00 00 00 00 00 00 |
10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
MC
MC
COUNTER
PC INSTRUC OPCODE MODE
PC ADDRESS CONTENTS
=======
== ======= ====== ====
== ======= ========
1
04
11
10
01
05
02
05
2
06
61
60
01
07
03
07
3
08
c1
c0
01
09
01
0c
4
0a
00
00
00
0b
0b
00
00
10
20
30
AC
==
05
0c
0c
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 0b 0c 05 07 11 02 61 03 c1 01 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
Program 4: Adding Together the Elements of an Array.
The fourth example of a MACHINE-16 program is one that adds together all the elements of an array. To reference the elements of the array, the pointer P is used which is initialized to the address ARRAY and then is incremented so that it points to each of the array elements in turn. The command BGZ in
address 0x16 checks to see if P is beyond the end of the array, and if it is, the program prints the value of
the sum and stops. Note that the INDIRECT addressing mode is used in the ADD command in address
0x0c because P does not contain the value of the to be added to SUM, it contains the address where the
value is to be found in the array.
Source Code for Program 4: Adding Together the Elements of an Array.
0a
00
01
03
05
07
09
05
02
00
11
09
62
08
21
09
41
08
11
08
70
06
00
01
02
03
04
05
06
07
08
09
0a
0b
0c
0d
0e
0f
10
11
12
13
14
15
PC:
START
AC:
0
ARRAY: 1
3
5
7
LAST: 9
N:
5
P:
ARRAY
SUM:
0
START: LOAD DIRECT
SUM
ADD INDIRECT
P
STORE DIRECT
SUM
INCR DIRECT
P
LOAD DIRECT
P
SUB IMMEDIATE
LAST
char PC = START;
char AC = 0;
char ARRAY[] = {1,
3,
5,
7,
9};
char N = 5;
char P = ARRAY;
char SUM = 0;
AC = SUM;
AC += *P;
SUM = AC;
P++;
AC = P;
AC -= LAST;
17
b0
1a
a0
0a
c1
09
00
00
16
17
18
19
1a
1b
1c
1d
END:
BGZ IMMEDIATE
END
JMP
START
PRIND DIRECT
SUM
STOP
0
if (AC > LAST) goto END;
goto START;
cout << int(SUM);
Dump for Program 4:
0
1
00
10
20
30
2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 0a 00 01 03 05 07 09 05 02 00 11 09 62 08 21 09 |
| 41 08 11 08 70 06 b0 1a a0 0a c1 09 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
Trace omitted ....
00
10
20
30
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 1d 01 01 03 05 07 09 05 07 19 11 09 62 08 21 09 |
| 41 08 11 08 70 06 b0 1a a0 0a c1 09 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
Program 5: Computing the first ten Fibonacci numbers.
The final example we discuss in this Section is a program which prints out the first 10 Fibonacci
numbers. Recall that a Fibonacci number is defined by the recurrence relation
x1 = 0
x2 = 1
xn = xn-1 + xn-2,
when n>2. For example, if we have already computed x3 = 2, x4 = 3, and x5 = 5, then x6 = x5 + x4= 5+3 = 8.
We can write a C program to compute the first 12 Fibonacci numbers as follows:
18
void main()
{
int a=0, b=1, c, n;
for (n=10; n>0; n--)
{
c = a + b;
a = b;
b = c;
cout << c << endl;
}
}
The first 12 Fibonacci numbers are 0, 1, 1, 2, 3, 5, 8, 11, 19, 30, 49, 79. We initialize n to 10 because we don't print the first two numbers in the sequence. We can't calculate more terms even if we wanted
to with 8 bit memory cells because the next number in the sequence is 128 which would cause an overflow.
Source Code for Program 5: Compute the First 12 Fibonacci numbers.
06
00
00
01
00
0a
11
02
61
03
21
04
11
03
21
02
11
04
21
03
c1
04
d0
0a
51
05
11
05
b0
06
00
00
00
PC: START
01
AC: 0
02
A: 0
03
B: 1
04
C: 0
05
N: 10
06 START: LOAD
07
A
08
ADD
09
B
0a
STORE
0b
C
0c
LOAD
0d
B
0e
STORE
0f
A
10
LOAD
11
C
12
STORE
13
B
14
PRIND
15
C
16
PRINC
17
'\n'
18
DECR
19
N
1a
LOAD
1b
N
1c
BGZ
1d
START
1e
STOP
1f
0
DIRECT
char
char
char
char
char
char
AC =
PC = START;
AC = 0;
A = 0;
B = 1;
C = 0;
N = 10;
A;
DIRECT
AC += B;
DIRECT
C = AC;
DIRECT
AC = B;
DIRECT
A = AC;
DIRECT
AC = C;
DIRECT
B = AC;
DIRECT
cout << int(C);
IMMEDIATE cout << ‘\n’;
DIRECT
N--;
DIRECT
AC := N;
IMMEDIATE if (AC>0)
goto start;
IMMEDIATE exit(1);
Dump for Program 5:
0
1
2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
00 | 06 00 00 01 00 0a 11 02 61 03 21 04 11 03 21 02 |
10 | 11 04 21 03 c1 04 d0 0a 51 05 11 05 b0 06 00 00 |
20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
19
30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
...... Trace omitted
0 1 2 3 4 5 6 7 8 9 a b c d e f
+-------------------------------------------------+
| 1f 00 37 59 59 00 11 02 61 03 21 04 11 03 21 02 |
| 11 04 21 03 c1 04 d0 0a 51 05 11 05 b0 06 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
+-------------------------------------------------+
00
10
20
30
Exercises
1. Write MACHINE-16 source code to perform the following tasks.
2. Write a MACHINE-16 program which will read two decimal numbers at the keyboard (Instruction
SCANF), add then, and print out the result.
3. Read a decimal number and print out its absolute value.
4. Read A and B in at the keyboard and print out A mod B which is computed by
A - (A / B) * B
The symbol “ /” represents integer division.
5.
Compute the length of a line of characters entered at the keyboard. The line should be terminated by
\n which is 0x0a.
6. Add up all numbers from 1 to 12 using a loop.
7. Compute terms of the recurrence relation until a negative result denoting overflow is encountered.
x1
x2
x3
xn

=
=
=
=
0
3
1
xn-1 + 2xn-2 + xn-3
20
Download