simple on microprocessor by ronaldtamarind


More Info
									                           Microprocessors I
Lesson 1: Basics
The majority of people think that computers are some kind of complicated device that is
impossible to learn and infinitely intelligent, able to think better than a person. The truth
is much less glamorous.

A computer can only do what the programmer has told it to do, in the form of a
program. A program is just a sequence of very simple commands that lead the computer
to solve some problem. Once the program is written and debugged (you hardly ever get it
right the first time), the computer can execute the instructions very fast, and always do it
the same, every time, without a mistake.

And herein lies the power of a computer. Even though the program consists of very
simple instructions, the overall result can be very impressive, due mostly to the speed at
which the computer can process the instructions. Even though each step in the program is
very simple, the sequence of instructions, executing at millions of steps per second, can
appear to be very complicated, when taken as a whole. The trick is not to think of it as a
whole, but as a series of very simple steps, or commands.

The microprocessor itself is usually a single integrated circuit (IC). Most
microprocessors, or very small computers, (here after referred to simply as micro's) have
much the same commands or instructions that they can perform. They vary mostly in the
names used to describe each command. In a typical micro, there are commands to move
data around, do simple math (add, subtract, multiply, and divide), bring data into the
micro from the outside world, and send data out of the micro to the outside world. Sounds
too simple....right?

A typical micro has three basic parts inside. They are the Program Counter (PC),
Memory, and Input / Output (I/O). The Program Counter keeps track of which command
is to be executed. The Memory contains the commands to be executed. The Input /
Output handles the transfer of data to and from the outside world (outside the micro's
physical package). The micro we'll be using is housed inside a 40 pin package, or chip or
IC. There are many other actual parts inside our micro however, we will learn about each
and every single one, one step at a time.

A Simple Program

As stated before, a program is a sequence or series of very simple commands or
instructions. A real world example program might be the problem of crossing a busy

Step 1: Walk up to the traffic lights and stop.
Step 2: Look at the traffic light.

Step 3: Is your light green?

Step 4: If the light is red, goto step 2. (otherwise continue to step 5)

Step 5: Look to the left.

Step 6: Are there cars still passing by?

Step 7: If yes, goto step 5. (otherwise continue to step 8).

Step 8: Look to the right.

Step 9: Are there cars still passing by? (there shouldn't be any by now, but, you never

Step 10: If yes, goto step 8. (otherwise continue to step 11)

Step 11: Proceed across the street, carefully!!

Now this may seem childish at first glance, but this is exactly what you do every time you
cross a busy street, that has a traffic light (at least, I hope you do). This is also exactly
how you would tell a micro to cross the street, if one could. This is what I mean by a
sequence or series of very simple steps. Taken as a whole, the steps lead you across a
busy intersection, which, if a computer did it, would seem very intelligent. It is
intelligence, people are intelligent. A programmer that programmed these steps into a
micro, would impart that intelligence to the micro.

The micro would not, however, in this case, know what to do when it got to the other
side, since we didn't tell it. A person, on the other hand, could decide what to do next, at a
moments notice, without any apparent programming. In the case of a person, though,
there has been some programming, it's called past experiences.

Another program might be to fill a glass with water from a tap.

Step 1: Turn on the water.

Step 2: Put the glass under the tap.

Step 3: Look at the glass.

Step 4: Is it full?

Step 5: If no, goto step 3.(otherwise, continue to step 6)
Step 6: Remove the glass from under the tap

Step 7: Turn off the water.

This is a simpler program, with fewer steps, but it solves a problem, to fill a glass with
water. In a micro, the problems are different but the logical steps to solve the problem
are similar, that is, a series of very simple steps, leading to the solution of a larger

Also notice that since the steps are numbered, 1 through 7, that is the order in which
they're executed. The Program Counter, in this case, is you, reading each line, starting
with 1 and ending with 7, doing what each one says. In a micro, the Program Counter
automatically advances to the next step, after doing what the current step says, unless a
branch, or jump, is encountered. A branch is an instruction that directs the Program
Counter to go to a specific step, other than the next in the sequence. The branch in this
example is step 5. Not only is this a branch, but it is a conditional branch. In other words,
based on whether the glass is full or not, the branch is taken, or not. A micro has both
branch and conditional branch instructions. Without this ability to reuse instructions, in a
sort of looping action, a solution would take many more steps, if it would be possible at

The point of this lesson is to show how a simple set of instructions can solve a bigger
problem. Taken as a whole, the solution could appear to be more complicated than any of
the separate steps it took to solve it.

The most difficult problem to be solved in programming a micro is to define the problem
you are trying to solve. Sounds silly but I assure you, it's not. This is the Logical Thought
Process I mentioned earlier. It is having a good understanding of the problem you're
trying to solve. You must understand the information I'm presenting in order pass the
course. Trying to remember everything does not work at university.

Decimal, Binary & Hex
Most people have learned to use the Decimal numbering system for counting and
calculations. But micros use a different system. It's called Binary and that’s why our
computers are called binary computers! All numbering systems follow the same rules.
Decimal is Base 10, Binary is Base 2, and Hex(adecimal) is Base 16. The base of a
system refers to how many possible numbers can be in each digit position. In decimal, a
single digit number is 0 through 9. In binary a single digit number is 0 or 1. In hex a
single digit number is 0 through 9, A,B,C,D,E, and F.

In decimal, as you count up from 0, when you reach 9, you add 1 more, then you have to
add another digit position to the left and carry a 1 into it to get 10 (ten). Ten is a two digit
decimal number.

In binary, as you count up from 0, when you reach 1, you add 1 more, then you have to
add another digit position to the left and carry a 1 into it to get 10B (two decimal). While
this is exactly what you do in decimal, the result looks like ten so we usually write the
letter ‘B’ after to distinguish it from ten decimal. [ We should really denote ten decimal
as 10D but we usually leave off the ‘D’]. So while decimal 10 (ten) looks like binary 10
(two decimal) they represent different decimal values. It is still useful to think in decimal,
since that's what we're used to, but we have to get used to seeing numbers represented in

In hexadecimal (hex), as you count up from 0, when you reach 9, you then go to A ten
decimal), then B (eleven decimal), etc., until you reach F (15 decimal). When you reach
F, you then add 1 more, then you have to add another digit position to the left and carry a
1 into it to get 10H (sixteen decimal). While this is exactly what you do in decimal, the
result looks like ten or 10B, so we usually write the letter ‘H’ after to distinguish it from
ten decimal and binary 10B. So while decimal 10 (ten) and binary 10B looks like hex
10H, they represent different values. It is still useful to think in decimal, since that's what
we're used to, but we have to get used to seeing numbers represented in binary and hex.

Another small difference between decimal terminology and binary is that in binary a digit
is called a bit. It gets even more confusing by the fact that 4 bits make a nibble. Two
nibbles make a byte. Two bytes make a word. Most numbers used in a micro don't go
beyond this, although there are others. Using what I've just said, if two nibbles make a
byte, you could also say that a byte is eight bits.

To represent a binary number larger than 4 bits, or a nibble, a different numbering system
is normally used. It is called hexadecimal, or Base 16. A shorter name for hexadecimal is
simply hex, and that's what we'll use here after. In this system there are 16 possible
numbers for each digit. For the first 10, 0 through 9, it looks like decimal. Unlike
decimal, when you add 1 more to 9, you get A. I know that having a letter to represent a
number is really confusing, but they had to call it something, and A is what they chose.
So a hex A is a decimal 10 (ten). The numbers count up from A through F. Just to clarify
this here is the sequence of counting in hex from 0 to where you have to add another digit
position to the left... 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F (no G). This represents a
decimal count of 0 through 15. At a count of F (15 decimal), if you add 1 more you get
10 (oh no! .. not another 10 that means something else!!). Sad but true.

Let's regroup here. A binary 10 (one zero) is decimal 2, a decimal 10 is ten, and a hex 10
is decimal 16. If you can get this concept, you will have conquered the most difficult part
of learning micros.
I need to get past one more obstacle, the idea of significance. In a decimal number like
123, 3 is the least significant digit position (the right most digit) and 1 is the most
significant digit position (the left most digit). Significance means the relative value of one
digit to the next. In the number 123 (one hundred twenty three) , each number in the right
hand most digit position (3) is worth 1. The value of each number in the next most
significant digit position (2) is worth ten and in the most significant digit position (1)
each is worth a hundred. I'm not trying to insult your intelligence here, but rather to point
out the rule behind this. The rule is that no matter what base you're working in, as you
start adding digits to the left, each one is worth the base times (multiplied) the digit to the
right. In the decimal number 123 (base 10), the 2 digit position is worth 10 times the 3
digit position and the 1 digit position is worth 10 times the 2 digit position. Hence the
familiar units, tens, hundreds, and so on. For some reason, for most people, this makes
sense for decimal (base 10) but not for any other base numbering system.

The very same is true for binary. The only difference is the base. Binary is base 2. So in
binary the least significant bit (remember bits?) is worth 1 ( this happens to be the same
for all bases). The next most significant bit is worth 2, the next worth 4, the next worth 8,
and so on. Each is 2 times (base 2) the previous one. So in an 8 bit binary number (or
byte, remember bytes?), starting from the right and moving left, the values for each of the
8 bit positions are 1, 2, 4, 8, 16, 32, 64 , and 128. If you've got this, you have passed a
major milestone, you should go celebrate your passage. If you haven't, I would re-read
the above until you do, and then go celebrate!! ( By the way, if you didn't get this the first
time through, join the crowd. I didn't either!!)

In hex (base 16) the same rule applies. In a 4 digit hex number, starting at the right and
working left, the first digit is worth 1 ( hey that's just like decimal and binary!!), the next
is worth 16 (base times the previous digit), the next is worth 256 (16 X 16), and the most
significant is worth 4096 (256 X 16). One last note, hex is just binary described another
way. A hex digit is a binary nibble ( remember nibbles?). Both are 4 bit binary values.

Trying to wrap all this confusion up in review, 4 bits is a nibble or a hex digit. Two hex
digits is a byte or 8 bit binary. A 4 digit hex number is a word, or 16 bit binary or 4
nibbles, or 2 bytes. You may have to review the previous paragraphs a few times (I did!!)
to get all the relationships down pat, but it is crucial that you are comfortable with all I've
said, so that what follows will make more sense.

Let's take a few example numbers and find the decimal equivalent of each. Let's start
with the binary number 1011, a nibble. Starting at the right and moving left, the first digit
is worth one, because there is a 1 there. The next is worth two, because there is a 1 in it.
The next would be worth 4, but since there is a 0, it's worth zero. The last or most
significant is worth eight, since there is a 1 in it. Add all these up and you get eleven. So
a binary 1011 is decimal 11. Also, since this could be a hex digit, the hex value would be

Let's take a longer binary number 10100101. Starting at the right moving left, the first is
worth 1, the next is worth 0, the next is worth 4, the next is 0, the next is 0, the next is
worth 32, the next is 0, and the last is 128. Adding all these up you get decimal 165.
Dividing this number up into two hex digits, you get A5. So binary 10100101, decimal
165, and hex A5 are all the same value. Using hex, the rightmost digit is worth 5, and the
most significant digit is worth 160 (10 X 16), resulting in decimal 165. If you understand
this, you’re ready to move on, leaving the different systems behind. If you don't, keep
reviewing and studying until you do. If you don't understand and still continue, you will
be confused by what follows. I promise that once you get this, the rest is easier.

This ends the second lesson. I hope this wasn't too daunting or difficult, but there was a
lot to get through. The rest of the course should be a little easier. Learning a new
numbering system is like learning a new language. It's a little cumbersome at first, but it
gets easier.

Microprocessor Instructions
As mentioned earlier, we refer to a binary number like 1111 as 1111b, a decimal number
like 123 as 123, and a hex number like A5 as A5h. So don't be confused by the letters
following numbers, we use both caps and lowercase to denote binary, decimal, or
hexadecimal so there is no doubt what base a multidigit, or multibit, number is in.

Also there is another kind of memory, called flags. Flags are single bit numbers used to
indicate different conditions. They are called flags because they flag the program of
events or conditions. If a flag is raised, or has a 1 in it, it is said to be SET. If it is a 0, it is
said to be CLEARED or RESET.

One other thing, in an 8-bit byte, the 8 bits are referred to as bits 0 through 7, with bit 0
being the right most, or least significant (lsb), and bit 7 as the left most or most
significant (msb).

Lastly, there are various Registers inside a microprocessor or Central Processing Unit
(CPU). These vary from CPU to CPU, but all contain a register called the Accumulator. It
is also referred to in some as the A register. We will be using the accumulator in the
following discussion. It is a type of memory for storing temporary results and is 8 bits
wide, or a byte, as are most places that data can be put inside the CPU.
In the CPU we will be using, there are 5 different types of instructions and several
variations of each, resulting in over 100 different instructions. These 4 types are


The arithmetic instructions usually include addition, subtraction, division, multiplication,
incrementing, and decrementing although division & multiplication were not available in
most early CPU’s. There are two flags used with arithmetic that tell the program what
was the outcome of an instruction. One is the Carry (C) flag. The other is the Zero (Z)
flag. The C flag will be explained in the following example of addition. The Z flag, if set,
says that the result of the instruction left a value of 0 in the accumulator. We will see the
Z flag used in a later lesson.


This is straightforward and is simply to add two numbers together and get the result.
However there is one more thing. If, in the addition, the result was too big to fit into the
accumulator, part of it might be lost. There is a safeguard against this. Take the case of
11111111b (255) and 11111111b (255). These are the largest numbers that can fit into an
8-bit register or memory location. You can add these as decimal numbers, since I gave
you their values in decimal also, and you would get 510. The binary value for 510 is
111111110b (9 bits). The accumulator is only 8 bits wide, it is a byte. How do you fit a 9-
bit number into 8 bits of space? The answer is, you can't, and it’s called an OVERFLOW
condition. So how do we get around this dilemma? We do it with the CARRY (C) flag. If
the result of the addition is greater than 8 bits, the CARRY (C) flag will hold the 9 th bit.
In this case the accumulator would have in 11111110b (254) and the C flag would be a 1,
or set. This 1 has the value of 256 because this is the 9th bit. We haven't covered a 9-bit
number, but they come up all the time as overflows in addition. Since we are using base
2, and we found out in lesson 2 that the 8th bit (bit 7) in a byte is worth 128, then the 9th
bit is worth 2 times that, or 256. Adding 254 and 256, we get 510, the answer, and we
didn't loose anything, because of the C flag. Had the result of the addition not caused an
overflow, the C flag would be 0, or cleared.


In the case of subtraction, the process is more difficult to explain, and as such, I'm not
going to cover it here. It involves 1's compliment and 2's compliment representation. But
I will tell you this, you can subtract two numbers and if there is an under flow, the C flag
will be a 1, otherwise it will be a 0. An under flow is where you subtract a larger number
from a smaller number.

Multiplication and Division
In the micro we will be using, the 8085, multiply and divide instructions are not
available so we will wait till later (EL31G-Microprocessors II) to talk about them. They
do, however, do just what the names suggest.

Increment & Decrement

Two other instructions are included in the arithmetic group. They are increment and
decrement. These instructions are used to count events or loops in a program. Each time
an increment is executed, the value is incremented by 1. A decrement, decrements the
value by 1. These can be used with conditional jumps to loop a section of program, a
certain number of times. We will see these used later.


In micros there are other mathematical instructions called logical instructions. These are
OR , AND, XOR, ROTATE, COMPLEMENT and CLEAR. These commands are
usually not concerned with the value of the data they work with, but, instead, the value,
or state, of each bit in the data.


The OR function can be demonstrate by taking two binary numbers, 1010b and 0110b.
When OR'ing two numbers, it doesn't matter at which end you start, right or left. Let's
start from the left. In the first bit position there is a 1 in the first number and a 0 in the
second number. This would result in a 1. The next bit has a 0 in the first number and a 1
in the second number. The result would be 1. The next bit has a 1 in the first number and
a 1 in the second number. The result would be a 1. The last bit has a 0 in the first number
and a 0 in the second number, resulting in a 0. So the answer would be 1110b. The rule
that gives this answer says that with an OR, a 1 in either number result in a 1, or said
another way, any 1 in, gives a 1 out.


AND'ing uses a different rule. The rule here is a 0 in either number will result in a 0 , for
each corresponding bit position. Using the same two numbers 1010b and 0110b the result
would be 0010b. You can see that every bit position except the third has a zero in one or
the other number. Another way of defining an AND is to say that a 1 AND a 1 results in a

XOR (eXclusive OR)

XOR'ing is similar to OR'ing with one exception. An OR can also be called an inclusive
OR. This means that a 1 in either number or both will result in a 1. An eXclusive OR says
that if either number has a 1 in it, but not both, a 1 will result. A seemingly small
difference, but crucial. Using the same two numbers, the result would be 1100b. The first
two bits have a 1 in either the first or the second number but not both. The third bit has a
1 in both numbers, which results in a 0. The fourth has no 1's at all, so the result is 0. The
difference may seem small, even though the OR and XOR result in different answers.
The main use of an XOR is to test two numbers against each other. If they are the same,
the result will be all 0's, otherwise the answer will have 1's where there are differences.


Complimenting a number results in the opposite state of all the 1's and 0's. Take the
number 1111b. Complimenting results in 0000b. This is the simplest operator of all and
the easiest to understand. Its uses are varied, but necessary, as you'll see later.


These instructions rotate bits in a byte. The rotation can be left or right, and is done one
bit each instruction. An example might be where the accumulator has a 11000011b in it.
If we rotate left, the result will be 10000111b. You can see that bit 7 has now been moved
into bit 0 and all the other bits have move 1 bit position in, the left direction.


This instruction clears, or zero's out the accumulator. This is the same as moving a 0 into
the accumulator. This also clears the C flag and sets the Z flag.


There are also program flow commands. These are branches or jumps. They have several
different names reflecting the way they do the jump or on what condition causes the
jump, like an overflow or under flow, or the results being zero or not zero. But all stop
the normal sequential execution of the program, and jump to another location, other than
the next instruction in sequence.

Jump on Condition (of a Bit)

These instructions let you make a jump based on whether a certain bit is set (a 1) or
cleared (a 0). This bit can be the CY (carry) flag, the Z (zero) flag, or any other bit.


There is also a variation on a jump that is referred to as a CALL. A CALL does a jump,
but then eventually comes back to the place where the CALL instruction was executed
and continues with the next instruction after the CALL. This allows the programmer to
create little sub-programs, or subroutines, that do repetitive tasks needed by the main
program. This saves programming time because once the subroutine is written, it can be
used by the main program whenever it needs it, a kind of way to create your own


These instructions do exactly what you would think. They move data around between the
various registers and memory.


Exchanging is a variation on the move instruction. Here data is exchanged between two

This is the end of lesson 3. I've tried to briefly explain all the possible instructions
without actually showing each. I will, in a later lesson, go into each of the 100+ different
instructions and explain each one. In the next lesson we will learn more about memory
and all the possible ways to get to a memory location.

Memory and Addressing
There are several different types of memory in a micro. One is Program memory. This is
where the program is located. Another is Data memory. This is where data, that might be
used by the program, is located. The neat (or strange) thing is that they both reside in the
same memory space and can be altered by the program. That’s right, a program can
actually alter itself if that was necessary. Two terms are used when talking about
memory. Reading (load) is getting a value from memory and Writing (store) is putting a
value into memory.

There are three buses (not the kind you ride in) associated with the memory subsystem.
One is the address bus, the second is the data bus, and the third is the control bus. It's
important for you to know exactly how all this works, because these busses transport data
and addresses everywhere. All three are connected to the memory subsystem. Its also
good to know the function of each to better understand what's happening. In the 8085
CPU, the address bus is 16 bits wide. It acts to select one of the unique 216 (64K) memory
locations. The control bus determines whether this will be a read or a write. In the case of
an instruction fetch, the control bus is set up for a read operation. Data is read or written
through the data bus, which is 8 bits wide. This is why all registers and memory are 8 bits
wide, it's the width of the data bus on the 8085 CPU. A bus is just a group of connections
that all share a common function. Instead of speaking of each bit or connection in the
address separately, for example, all 16 are taken together and referred to simply as the
address bus. The same is true for the control and data buses.

A byte is the most used number in a micro because each memory location or register is
one byte wide. Memory has to be thought of as a sort of file cabinet with each location in
it being a folder in the cabinet. In a file cabinet, you go through the tabs on the folders
until you find the right one. To get to each memory location, a different method is used.
Instead, a unique address is assigned to each location. In most micros this address is a
word or 16 bits, or 4 digit hex. This allows for a maximum of 65536 (216 or 64K) unique
addresses or memory locations that can be accessed. These addresses are usually referred
to by a 4 digit hex number. Memory usually starts at address 0000h and could go up to
FFFFh (216 or 64K or 65536 in total). To access these locations, a 16 bit address is
presented to memory and the byte at that location is either read or written.

The Program Counter is what holds this address when the micro is executing instructions.
The reason instructions are read sequentially, is because the program counter
automatically increments after fetching the current instruction. It does this even before
the current instruction is acted upon. The sequence is that the program counter's contents
are placed on the memory address bus and the instruction is fetched from memory
through the data bus, and immediately the program counter is incremented by 1. Then the
micro looks at the instruction and starts processing it. If the instruction is not some kind
of jump or call, the instruction is completed and the program counter is presented to the
memory address bus again and the next instruction is fetched, the program counter is
incremented and the process starts over. This is referred to, in computer jargon, as fetch,
decode, and execute.

In the case of reading or writing data, the process is a little different. Data can be read
from or written to memory in similar fashion to the fetch. But data does not need the
decode & execute steps. In the next lesson we will see this in more detail.

In the next lesson we start looking at the micro we will be using – the INTEL 8085.
The 8085 Microprocessor
Well, finally, this is what all the previous lessons have been getting you ready for. To
start looking at the micro we will be using in this course. All the previous lessons have
been laying the ground work and basic concepts common to most micros. The one we
will be using is the INTEL 8085 microprocessor. This chip was the last 8-bit general
purpose CPU made by INTEL and has 40 pins.

The address bus requires 16 pins and the data bus requires 8 pins but INTEL cleverly
decided to share or multiplex these two busses so the data bus share the lower (A0-A7) 8
pins of the address bus. This caused no problem since address and data are never on the
bus at the same time.

Two pins are for serial communications with the 8085. Through these pins, serial data
can be sent or received with another computer. This is how we will load a program into
the 8085 kit used in the lab, from a PC.

Five more pins are for a different kind of input called interrupts. In our example in lesson
1 of the program where we are standing at the street corner, watching the light and the
traffic, if a person walked up and tapped us on the shoulder and asked what time it is, this
would be an example of an interrupt. It doesn't alter the program we are doing, it just
temporarily stops us while we tell the time to the person. As soon as we tell the time, we
go back to watching the lights and traffic as before. This describes the action of an

The interrupt has a program associated with it to guide the micro through a problem. In
the case of the above example, this program would be to look at our watch, read the time,
and then tell it to the person. This is called an interrupt service routine (ISR). Each time
an interrupt occurs, the current program is temporarily stopped and the service routine is
executed and when complete, returns to the current program. We will spend a lot more
time later describing interrupts and how we'll use them.

Inside the 8085 there are 10 seperate registers. They are called A, B, C, D, E, H, L, PSW,
PC, and SP. All but the PSW, PC, and SP registers are used for temporary storage of
whatever is needed by the program. The accumulator called A is also different from the
other registers. It is used to accumulate the results of various instructions like add or sub
(subtract). The Program Counter (PC) we have already mentioned while the Stack Pointer
(SP) actually holds addresses and is 16 bits wide. All the others are 8 bits wide.

There are other features to be covered later, as they come up. In the next lesson we will
start looking at assembly language, the method we will use to write a program.
What is Assembly Language?
Inside the 8085, instructions are really stored as binary numbers, not a very good way to
look at them and extremely difficult to decipher. An assembler is a program that allows
you to write instructions in, more or less, English form, much more easily read and
understood, and then converted or assembled into hex numbers and finally into binary

The program is written with a text editor (NOTEPAD or similar), saved as an ASM file,
and then assembled by the assembler (TASM or MASM or similar) program. The final
result is an OBJ file you download to the 8085. Here is an example of the problem of
adding 2 plus 2:

         mvi A,2       ; move 2 into the A register
         mvi B,2       ; move 2 into the B register
         add B         ;add reg. B to reg. A, store result in reg. A

The first line moves a 2 into register A. The second moves a 2 into register B. This is all
the data we need for the program. The third line adds the accumulator with register B and
stores the result back into the accumulator, destroying the 2 that was originally in it. The
accumulator has a 4 in it now and B still has a 2 in it. In the program above all text after
the ‘;’ are treated as comments, and not executed. This is a very important habit to

Assembly language follows some rules that I will describe as they come up. With most
instructions, especially those involving data transfer, the instruction is first, followed by
at least 1 space, then the destination followed by a comma, and then the source. The
destination is where the result of the instruction will end up and the source is where the
data is coming from.

Next we will read a switch, and light an LED if the switch is pressed. This happens quite
often in your lab experiments. Bit 0 of Port 0 will be the switch. When the switch is
closed or pressed, bit 0 will be a 1, and if the switch is open or not pressed, bit 0 will be a

Bit 0 of Port l be the LED. If bit 0 is a 0 the LED is off and if bit 0 is a 1, the LED will be
on. All the other bits of reg. A will be ignored and assumed to be all 0's, for the sake of

start:   IN    0               ; read Port 0 into reg. A

         CMP   1               ; compare reg. A with the value 1

         JNZ   start           ; jump to start if the comparison does not yield 0

         OUT   1               ; send a 1 to Port 1, turning the LED on
         JMP     start

The first line has something new. It's called a label. In this case it is start: . A label is a
way of telling the assembler that this line has a name that can be referred to later to get
back to it. All labels are followed by the symbol : , which tells the assembler that this is a
label. In the first line we also read the switch by reading Port and putting it into the
accumulator. Reg. A is the only register that can read in/send out data via ports or
perform compares. Thus, we need not write ‘A’ in the command…it’s implied!

The next line compares the value in reg. A with the value 1. If they are equal, the Zero
flag is set (to 1). The next line then jumps to start: only if the Zero flag is not set ie: the
value in reg. A is not 1 therefore the switch was not pressed. The program will therefore
keep looping until the switch is pressed!

If the switch is pressed then the penultimate line writes the value 1 to the accumulator,
therefore bit 0 = 1, and the LED comes on.

The last line jumps back to start. This completes the loop of reading the switch and
writing to the LED.

This particular problem could have been solved with just a switch connected to an LED,
like a light is connected to a wall switch in your house. But with a micro in the loop,
much more could be done. We could have a clock that also turns on and off the LED
based on time. Or we could monitor the temperature and turn the LED on and off based
on what temperature it is. Or we could monitor several switches and turn the LED on and
off based on a combination of switches, etc….it’s up to the imagination what can be

In the above example we assumed that the other bits of ports 0 and 1 were all zeros. But
in reality, each of these bits could have a function assigned to them. Then we would need
to look only at bit 0 in port 0 and bit 0 in port 1. This further complicates the problem.
Also, we assume that port 0 was previously defined as an input port whereas port 1 was
defined as an output port.

In assembly we can assign a name to a port and refer to it by that name, instead of port 0
or port 1. This is done with an equate directive. Directives are assembler commands that
don't result in program but instead direct the assembler to some action. All directives start
with a period.

          .equ       switch, 0          ;port 0 is now called switch

          .equ       LED,1              ;port 1 is now called LED

start:   IN      switch          ; read Port 0 into reg. A

         CMP     1               ; compare reg. A with the value 1
        JNZ     start           ; jump to start if the comparison does not yield 0

        OUT     LED             ; send a 1 to Port 1, turning the LED on

        JMP     start

This has the same result as the previous program. Also the equate only has to be made
once at the start of the program, and thereafter the name or label is used instead of the
port number. This makes things much simpler for the programmer. All equates must be
defined before they are used in a program. This holds true for labels also. Another
advantage of naming ports with an equate is that if, later in the design process, you decide
to use a different port for the LED or the switch, only the equate has to be changed, not
the program itself.

Please note that comments are very important. When you initially write a program, the
tendancy is not to write much in the comment field because you're in a hurry. But if you
have to come back to it a few weeks later, it's much easier to understand what you've
written if you've taken the time to write good comments. Also good comments help in

To digress just a little here, an instruction like add B is a one byte instruction. In other
words this instruction would end up inside the 8085 as one byte. Part of the byte is the
opcode and the other part is which register is affected or used. The reason for this is that a
prime concern in programming a micro is how may bytes the program will actually take
up inside the micro, after it's been assembled. The idea is to cram as much as possible
into as few bytes as possible. This is why implied addressing is used. It limits choices in
the use of the instruction, you always have to use the accumulator as either the source or
the destination, but it shrinks the size of the instruction, so that more instructions can fit
inside the micro. This is a choice made by the maker of the micro, and is not up for
discussion. It's a trade off of flexibility vs. size. That's why you'll see lots of instructions
that use the accumulator. This is the best way to describe implied addressing.

In the case of an instruction like mvi A,1 ,two bytes are assembled. The first byte says
that this is an move instruction and that the accumulator is the destination. The second
byte is the immediate data itself. Thus we see that an instruction can have it’s data ‘next’
to it. It is transparent to the programmer where the bytes are actually stored in memory.
Once we can ‘find’ it by an instruction is all that matters. We will get into this again,

Another form of addressing variables is called register indirect or just plain indirect
addressing. This is a little more complicated. Here the address is held in a register,
usually the H & L registers but sometimes the B & C or D & E. Since an address is 16
bits long, we need two registers (a register pair) to store an address. We will also get into
this again, later.

Lastly, I want to explain something else about the assembler. The source file is what the
above program, or any program that has been written, is referred to. It is the source for
the assembler, or the file that is going to be read by the assembler to generate the object
file (the object of the assembler) from. The object file is the file that will be download to
the 8085 kit in the lab. They are two different files. One you've written with a text editor
(the source or ASM file) and the other is created by the assembler (the object or OBJ file)
when you assemble the source file. You use an assembler with the object in mind of
generating a file to download to the micro, hence the name, object file.

I've left out some directives, for simplicities sake, that I need to mention now. One is the
.org directive. It is the originate or origin directive. This tells the assembler at what
address the first byte of assembled code is to be placed inside the 8085. It is the origin of
the program or the beginning. Here's how this would look for our last example program:

          .org 2000H            ; begin using memory address 2000H

Well we've covered quite a lot in this lesson, and I hope you've gotten most of it. If not, I
would suggest re-reading it until you do. I would also suggest that you print out all of
these lessons so you can refer to them later. In the next lesson we will actually be
assembling some programs and looking at the object files.

Using an Assembler
If you are using a DOS based assembler, you'll need to get to DOS.

The first step in writing a program is to define the problem as completely as possible.
You will always think of things as you go along that you left out. After thinking out the
problem, you start by typing up a source file. Source files all end with the .asm extension.
The extensions created by the assembler are .obj which is the object file and .lst which
is the listing file. The listing file shows all the addresses resolved by the assembler, all the
code generated and all the comments. This file is very important in for assisting you in
the lab. The listing file also shows any errors in the assembly process so that you can
correct them. It is crucial that you look at the listing file to be sure that there are no errors
listed. The way you would use a program in real life is to write each program, assemble
it, and then execute it.

For instructions on how to use the 8085 microprocessor kit used in the lab (the SDK-85),
please purchase the book “Using the SDK-85 Microprocessor Trainer” which is a
required text for the course and available in the university bookstore.
Address Decoding
As an assembly language programmer, you know that the CPU registers have no address.
Instead, you specify them by name eg: A, B, C, etc.. As a result, it may seem confusing to
you that to access an I/O port, you use an address, even though they may be thought of as
similar to registers. CPU registers use the fastest (and most expensive) memory
technology, then cache memory uses a slightly slower technology and then system RAM
uses a slower and cheaper technology. Nevertheless, all are forms of memory. In fact,
you could think of standard RAM as a large collection of slow registers. The difference is
simply in the way that we name them.

You should also realize (especially if you have installed additional memory in your own
computer) that RAM consists of multiple chips, each of which contains a number of
memory locations. Each chip is physically just like every other chip. There is nothing
about the chip itself that makes it hold a particular range of addresses. The locations on a
single chips are linearly ordered, but there is no inherent ordering among the separate
chips. The ordering comes from the way the chips are connected to the address bus.
When you specify a particular address, the corresponding location exists only in one of
those chips. In a very real sense, part of the address selects the correct chip (the upper
part of the address), while the rest of the address selects the correct location on that chip.
You can look at the low order bits as forming an offset from the first location on the chip
to the correct location on the chip for the address you are specifying. The method that we
use to select the correct location on the correct chip is called address decoding and we
use the voltages carried on the wires of the address bus to accomplish the selection.
Notice that it is critical that each address selects a unique location.

In additon to talking about memory addresses in memory space (or area), we also have an
area the microprocessor treats slightly different called the port (I/O) space or area. On
the 8085 CPU, we know the memory space is 64K bytes as there are 16 address lines.
The port (I/O) space on the 8085 is 256 bytes as there are only 8 address lines used to
select a port address. More on this later.

Each chip (whether it is a memory chip or a peripheral device chip) has an input called
chip select ( ) or similar. To activate the chip, we must send a logic "0" (0 volts) to this
input because it uses negative logic. If there is a logic "1" (+5 volts) on the wire
connected to this input, the chip is inactive. Some chips also have an enable (EN) input.
Chips of this type must receive a logic "1" on this input and a logic "0" on the        input
to be active. The fact that we can "turn on" (activate) or "turn off" (deactivate) a chip
using signals like these allows us to select the correct chip for a particular address.

In order to see how this works in practice, we can design an address decoder for a very
tiny memory, made up in part of "conventional RAM" and in part of I/O ports. You will
recall from the beginning of the semester that the number of wires on the address bus
determines the number of memory locations to which we have access. Conversely, the
number of locations to which we need access determines the number of address lines we
need. For this example, there will be sixteen 8-bit memory locations to which we want
access. This means that we will need four address lines (24 = 16). Assume that we have
eight memory locations in conventional memory and eight I/O ports. If we assume that
each conventional memory chip contains four memory locations, we will need two chips
or banks of memory. Assume the I/O ports are on eight separate chips, each with one
register (in reality, peripheral devices typically have several registers each, but the
concept is the same). Arbitrarily, we will say that the eight lowest addresses will be on
the conventional memory chips and the eight highest addresses will correspond to the I/O
ports on peripheral devices. First, we will specify all sixteen addresses in binary, so that
we can see easily which address lines will have high voltage and which will have low
voltage for any particular address.

                                       A3   A2   A1   A0
                                        0    0    0    0
                                        0    0    0    1
                                        0    0    1    0
                                        0    0    1    1
                                        0    1    0    0
                                        0    1    0    1
                                        0    1    1    0
                                        0    1    1    1

                                        1   0    0    0
                                        1   0    0    1
                                        1   0    1    0
                                        1   0    1    1
                                        1   1    0    0
                                        1   1    0    1
                                        1   1    1    0
                                        1   1    1    1

The first thing we need to do is figure out how we can use the first eight patterns to
activate the conventional memory chips and the second eight patterns to activate the I/O
ports. We will use the EN input to enable the appropriate chips. Later, we will need to
worry about the         input, because both the EN and        must receive appropriate inputs
in order to activate a chip, but for now, we will just consider how to "enable" some of the
chips. We need something that is common to all eight of the first set of patterns and that
distinguishes them from the second set of eight patterns. If you look at the values of the
A3 line, you can see that this line is always 0 for the first eight addresses and always 1 for
the second set of eight addresses. We can use this line to provide the EN signal for both
conventional memory and I/O ports (provided that the           input also receives an
appropriate input). Since A3 = 0 for the conventional memory addresses, we must invert
it to obtain a 1 for the corresponding EN inputs. Since A3 = 1 for the I/O port addresses,
we simply feed A3 directly to the EN inputs of the I/O ports.
                       Address Line A3 Connected to the EN Inputs

Now, we need to activate a particular chip. Since we have "used up" line A3, we are
down to three bits of the address bus. Logically, we have "eliminated" the most
significant bit of the address and we can now consider a three-bit address that we will use
to access bank 0 or bank 1 of conventional memory (when A3 is 0) or a three-bit address
that we will use to access one of the I/O ports (when A3 is 1). We will consider the
conventional memory chips first.

We have essentially the same problem that we had before. We need to find something in
common for the first four addresses that distinguishes them from the second four
addresses in order to activate (via the       input) either bank 0 or bank 1, but not both.
Again, if we look at the high-order bit of these 3-bit addresses, we see that line A2 carries
0 volts for the first four addresses and +5 volts for the second four addresses. We can use
this line to activate (select) one or the other of the memory banks. In this case, we want
bank 0 to be active if A2 is 0 and bank 1 to be active if A2 is 1. The      input is active
low, however, so we will need to invert line A2 for bank 1 and feed it straight through to
bank 0 in order to accomplish our goal.
    Address Line A2 Connected to the         Inputs of the Conventional Memory Chips

We have a different problem to solve when we look at the I/O ports. We have eight
different chips and we must somehow turn on exactly one of them with the           inputs.
Obviously, we cannot use a single bit. In fact, we will need to use all three of the
remaining bits to differentiate the eight   inputs. We want each pattern to activate one
and only one of the chips. In other words, we want the pattern 000 to put a 0 on the
input of port 0 and a 1 on the     inputs of all the other ports; we want the pattern 001 to
put a 0 on the     input of port 1 and a 1 on the     inputs of all the other ports, and so
on. Fortunately, there is a commercially available chip that will do exactly what we want:
a 3-to-8 decoder (this is similar to the 74LS138 or 8205 chip we saw in a lecture, but
without the gating signals). We can feed lines A0, A1 and A2 to the inputs of this
decoder and connect one of its active-low outputs to each of the I/O ports.
Address Lines A0, A1 and A2 Connected to the          Inputs on the I/O Ports via a 3-to-8

Finally, we can use lines A0 and A1 to select one of the four possible memory locations
on either of the banks of conventional memory. You should notice that even though all
the address lines reach every chip, unless a chip is active, the values on the low-order
lines are immaterial.
                           Full 4-bit Address Decoder

Diane Law & Jorge Orejel
In a typical computer system, the software can be divided into 3 possible groups. One is
the Operating Loop, another is the Interrupt Service Routines, and the last is the
BIOS/OS functions and subroutines. The Operating Loop is the main part of the system.
It will usually end up being a sequence of calls to BIOS/OS subroutines, arranged in an
order that accomplishes what we set out to do, with a little manipulation and data transfer
in between. At the same time, at least it looks like it's happening at the same time,
interrupts are being serviced as they happen. In the 8085, there are thirteen (13) possible
events that can trigger an interrupt. Five of them are from external hardware interrupt
inputs (TRAP, RST 7.5, 6.5, 5.5, and INTR), that can be from whatever hardware we've
added to the 8085 that we deem to need servicing as soon as they happen. The remainder
are software instructions that cause an interrupt when they are executed (RST 0 – 7).

To digress just a moment, there are two ways to service, or act on, events that happen in
the system. One is to scan or poll them and the other is to use interrupts. Scanning is
just what is sounds like. Each possible event is scanned in a sequence, one at a time. This
is ok for things that don't require immediate action. Interrupts, on the other hand, cause
the current process to be suspended temporarily and the event that caused the interrupt is
serviced, or handled, immediately. The routine that is executed as a result of an interrupt
is called the interrupt service routine (ISR), or recently, the interrupt handler routine.

In the 8085, as with any CPU that has interrupt capability, there is a method by which the
interrupt gets serviced in a timely manner. When the interrupt occurs, and the current
instruction that is being processed is finished, the address of the next instruction to be
executed is pushed onto the Stack. Then a jump is made to a dedicated location where the
ISR is located.. Some interrupts have their own vector, or unique location where it's
service routine starts. These are hard coded into the 8085 and can't be changed (see

TRAP - has highest priority and cannot be masked or disabled. A rising-edge pulse will
cause a jump to location 0024H.

RST 7.5- 2nd priority and can be masked or disabled. Rising-edge pulse will cause a jump
to location 7.5 * 8 = 003CH.

          This interrupt is latched internally and must be reset before it can be used

RST 6.5 – 3rd priority and can be masked or disabled. A high logic level will cause a
jump to location 6.5 * 8 = 0034H.

RST 5.5 – 4th priority and can be masked or disabled. A high logic level will cause a
jump to location 5.5 * 8 = 002CH.
INTR – 5th priority and can be masked or disabled. A high logic level will cause a
jump to specific location as follows:

When the interrupt request (INTR) is made, the CPU first completes it’s current
execution. Provided no other interrupts are pending, the CPU will take the INTA pin low
thereby acknowledging the interrupt. It is up to the hardware device that first triggered
the interrupt, to now place an 8-bit number on the data bus, as the CPU will then read
whatever number it finds on that data bus and do the following: multiply it by 8 and jump
to the resulting address location. Since the 8-bit data bus can hold any number from 00 –
FFH (0 – 255) then this interrupt can actually jump you to any area of memory between
0*8 and 255*8 ie: 0000 and 07FFH ( a 2K space). N.B: This interrupt does not save the
PC on the stack, like all other hardware and software interrupts!

You will notice that there isn't many locations between vector addresses. What is
normally done is that at the start of each vector address, a jump instruction (3 bytes) is
placed, that jumps to the actual start of the service routine which may be in RAM.. This
way the service routines can be anywhere in program memory. The vector address jumps
to the service routine. There is more than enough room between each vector address to
put a jump instruction. Looking at the table above, there are at least 8 locations for each
of the vectors except RST 5.5, 6.5, and 7.5. When actually writing the software, at
address 0000h will be a jump instruction that jumps around the other vector locations.

Besides being able to disable/enable all of the interrupts at once (DI / EI) ie: except
TRAP, there is a way to enable or disable them individually using the SIM instruction
and also, check their status using RIM.

There are other things about interrupts that we will cover as they come up, but this lesson
was to get you used to the idea of interrupts and what they're used for in a typical system.
It’s similar to the scene where one is standing at a busy intersection waiting for the traffic
light to change, when a person came up and tapped us on the shoulder and asked what
time it was. It didn't stop us from going across the street, it just temporarily interrupted us
long enough to tell them what time it was. This is the essence of interrupts. They
interrupt normal program execution long enough to handle some event that has occurred
in the system.

Polling, or scanning, is the other method used to handle events in the system. It is much
slower than interrupts because the servicing of any single event has to wait its turn in line
while other events are checked to see if they have occurred. There can be any number of
polled events but a limited number of interrupt driven events. The choice of which
method to use is determined by the speed at which the event must be handled.

The software interrupts are the instructions RST n, where n = 0 – 7. The value n is
multiplied by 8 and the result forms an address that the program jumps to as it vector
address ie: RST 4 would jump to location 4*8 = 32 (20H).
The stack is one of the most important things you must know when programming. Think
of the stack as a deck of cards. When you put a card on the deck, it will be the top card.
Then you put another card, then another. When you remove the cards, you remove them
backwards, the last card first and so on. The stack works the same way, you put (push)
words (addresses or register pairs) on the stack and then remove (pop) them backwards.
That's called LIFO, Last In First Out.

The 8085 uses a 16 bit register to know where the stack top is located, and that register is
called the SP (Stack Pointer). There are instructions that allow you to modify it’s contents
but you should NOT change the contents of that register if you don't know what you're


As you may have guessed, push and pop “pushes” bytes on the stack and then takes them
off. When you push something, the stack counter will decrease with 2 (the stack "grows"
down, from higher addresses to lower) and then the register pair is loaded onto the stack.
When you pop, the register pair is first lifted of the stack, and then SP increases by 2.

N.B: Push and Pop only operate on words (2 bytes ie: 16 bits).

You can push (and pop) all register pairs: BC, DE, HL and PSW (Register A and Flags).
When you pop PSW, remember that all flags may be changed. You can't push an
immediate value. If you want, you'll have to load a register pair with the value and then
push it. Perhaps it's worth noting that when you push something, the contents of the
registers will still be the same; they won't be erased or something. Also, if you push DE,
you can pop it back as HL (you don't have to pop it back to the same register where you
got it from).

The stack is also updated when you CALL and RETurn from subroutines. The PC
(program counter which points at the next instruction to be executed) is pushed to the
stack and the calling address is loaded into PC. When returning, the PC is loaded with the
word popped from the top of the stack (TOS).

So, when is this useful? It's almost always used when you call subroutines. For example,
you have an often used value stored in HL. You have to call a subroutine that you know
will destroy HL (with destroy I mean that HL will be changed to another value, which
you perhaps don't know). Instead of first saving HL in a memory location and then
loading it back after the subroutine, you can push HL before calling and directly after the
calling pop it back. Of course, it's often better to use the pushes and pops inside the
subroutine. All registers you know will be changed are often pushed in the beginning of a
subroutine and then popped at the end, in reverse order! Don't forget - last in first out.
If you want to only push one 8 bit register, you still have to push it's "friend". Therefore,
be aware that if you want to store away D with pushing and popping, remember that E
will also be changed back to what it was before. In those cases, if you don't want that to
happen, you should try first to change register (try to store the information in E in another
register if you can) or else you have to store it in a temporary variable.

Before executing a program, you should keep track of your pushes and pops, since they
are responsible for 99% of all computer crashes! For example, if you push HL and then
forget to pop it back, the next RET instruction will cause a jump to HL, which can be
anywhere in the ROM/RAM and the ccomputer will crash. Note however, it’s also a way
to jump to the location stored in HL, but then you should really use the JMP instruction,
to do the same thing.

Push and pop doesn't change any flags, so you can use them between a compare and jump
instructions, depending on a condition, which is often very useful.

Analog to Digital Conversion
A/D & D/A chips allows us to interface with the analog (real) world. Most sensors and
many output devices are analog in operation. What follows below is a attempt to fill in
some gaps left over from the lectures.

Basic interface

To control an A/D from a microprocessor, the A/D converter must have, at least, the
following three (3) signals:

Start Conversion (SC) – sets the A/D converting

Output Enable (OE) – places the digital reading on the microprocessor’s data bus/port

End of Conversion (EOC) --- tells the microprocessor when the A/D is busy (optional)

These signals may come under other names but their operation would be similar.


The resolution of an A/D conversion is defined as the smallest voltage difference that can
be detected. The resolution is also referred to as the magnitude of the least significant bit
(LSB) in the conversion. The fact that an A/D conversion has a finite (non-zero)
resolution results in what is called quantization error. Quantization error results because a
continuously varying analog signal is represented digitally as a series of discrete steps
differing by the resolution of the conversion process. The resolution depends on two
other quantities:
Number of digital bits in the conversion

This is the length of the digital word that the A/D conversion produces as its output.
Typical values for this are 8, 12 and 16 bits. The higher the number of bits the longer the
conversion takes and the more accurate it is. This number is fixed for a given converter.
The number of discrete values that can represented (quantized) by a given length digital
word is equal to 2 raised to the number of bits. For example, if the converter is a 12 bit
system then 212 = 4096 values can be represented.

Input voltage range

This is the total range in volts of the A/D converter and depends on the amount of gain
that the converter has. Typically the amount of gain is adjustable.

The resolution in volts is defined as:

                  Resolution = (Input Voltage Range) / (2 Number of bits - 1)

For example,

Input Voltage Range = ±10 V = 20 V
Number of Bits = 12

Resolution = 20 V / (2¹² - 1) = 0.0049 Volts

Note that if the Input Voltage Range is decreased to 0.050 (50 millivolts) the resolution =
0.10 / 4095 = 0.0000244 volts or 2.e-5 volts.

The choice of the input range value is critical in ensuring that we obtain enough
resolution to accurately measure the input signal.

In both cases above, the resolution expressed an a percentage of the Input Voltage Range
is the same.

Resolution / Input Voltage Range * 100
0.0049 / 20 * 100 = 0.0245 %
2.e-5 / 0.10 * 100 = 0.02 %

This is certainly low enough for almost any application.

However, unless our signals use the input voltage range we do not obtain this percent
resolution. Consider the case of measuring a signal of 0.1 volts using a input voltage
range of 10 volts. The percent error in our measurement of this signal using a 12 bit A/D

0.0049 / 0.1 * 100 = 4.9 %
which is probably not acceptable. In this case an input range of 1.0 or 0.1 volts should be
used, giving either 0.49 or 0.049 percent resolution.

An alternative to adjusting the gain of the A/D is to provide the A/D with signals that use
the entire ±10 volt range. This is generally preferable due to signal noise. Each
amplification stage introduces noise into the signal and each succeeding amplification
amplifies the noise from the previous stage. Thus it is generally preferable to have the
first stage provide as much gain as possible. And the A/D, which is the last analog stage,
should have the least amount of gain necessary to resolve the signals. That is, the best
procedure is to choose the gain range on the the load cells, etc., so that a ±10 volt range
on the A/D results in acceptable resolution of the signals.

Sampling Frequency

The basic idea behind sampling frequency considerations is that the A/D conversion must
occur quickly enough to capture the rate at which the signal to be measured is changing.
The primary consideration is to obtain a reasonable amount of data. This is enough data
so that the changes in the signals can be resolved (sometimes this topic is called
consideration of the time resolution of the A/D process). But not so much data that it is
difficult to analyze. In fact sometimes that lack of resolution in time can be put to
advantage by sampling too slow to resolve high frequency noise in the signal.

The time resolution of a A/D conversion is usually expressed in terms of the maximum
frequency that can be resolved. This frequency is called the Nyquist frequency and it is
equal to half of the sampling frequency. The basic idea is that at least two data points are
required in each cycle of a waveform to just start to resolve it (one data point at the
maximum and one at the minimum in the waveform). Note that any shape signal
(sinusoidal, triangular, or square wave) with the Nyquist frequency will appear the same
digitally. Thus in practice a signal must be significantly below the Nyquist frequency (in
the area of biomechanics it is often up to ten times bleow) to be accurately measured and
its wave form determined. For example audio CD's are a digital recording of sounds and
they use a sampling frequency of 44 kHz. This is roughly 3 times the maximum
frequency of human hearing which is roughly 18 kHz.

The minimum frequency that can be resolved is determined by the length of time data is
collected. The total time of the data collection is equal to the number of scans collected
divided by the number of scans per second. For example if 100 scans are collected at 100
scans/second then the total data collection time = 100 scans / (100 scans/sec) = 1 second.
The lowest freqency that can be resolved has a period equal to the data collection time.
For this example a period of 1 second corresponds to a frequency of 1 Hz.

Multiple Channel A/D Conversion

Typically one wishes to measure several analog signals simultaneously. There are several
methods this can be accomplished:
      One A/D for each channel (expensive!)
      One A/D multiplexed (switched) between the channels, using either
          o a sample and hold amplifier for each channel (also expensive)
          o a single sample and hold after the multiplexer (our case)
          o no sample and hold amplifiers

To understand this you first need the following definition:

Sample and Hold Amplifier

This is an amplifier (usually with a gain of 1.0) which can store an analog signal for a
period of time, which is typically just longer than an A/D conversion takes. A capacitor is
a crude sample and hold amplifier, in that it can store a voltage for a given amount of
time determined by the size of the capacitor and the resistance (load) that is across the

Because an A/D conversion takes time, reading multiple channels simultaneously is a
problem. It is possible with multiple A/D converters, but that is expensive. Instead if truly
simultaneous readings are required (say for real time control) then a sample and hold
amplifier can be used to remember the signals at the same time and then a single A/D
converter can be used to convert the stored voltages. However this multiplexed approach
reduces the maximum sampling frequency, because the conversions are done serially
instead of at the same time.

A sample and hold amplifier will also tend to increase the accuracy of the conversion. If
the input voltage to an A/D converter is changing while the conversion takes place errors
can result. This is because the A/D conversion process reads the input voltage several
times (typically once for each bit) during the conversion process. A sample and hold is
used to keep the voltage the A/D converter sees during the conversion process from

How does it work?

The typical A/D converter uses some variation of a successive approximation scheme to
determine what the input voltage is. This process uses a digital to analog converter (DAC,
described below) and compares the output of theDAC to the input signal. The procedure
works as follows. The A/D converter sets the highest order bit on the D/A to 1 and all
other bits to 0. It then compares the DAC output voltage to the input signal (using an
analog comparator). If the input is higher than the DAC signal, the bit is left at 1, if the
input is lower the bit it set to 0. Then the procedure is repeated with the next lower order
bit leaving the higher order bit(s) with the previously determined value(s). Thus the
digital value of the input signal is successively approximated until finally the least
significant bit (LSB) is determined and the conversion is complete. This process is all
controlled by a clock that must run at the number of bits times the maximum sampling
frequency (at least). This is why for a given clock speed, a conversion with more bits
takes longer.
A DAC is a much simpler device which essentially consists of a summing amplifier with
an input for each bit. When a bit is set the summing amplifier adds in the voltage
corresponding to that bit. This process operates at the speed at which the summing
amplifier can settle (reach a stable output value), which is extremely short compared to
the time an A/D conversion takes with its iterative successive approximation process.
Here are some A/D types:

Flash Analog-to-Digital Converter

       Flash Analog-to-Digital Converters are used for systems that need the
       highest speeds available. Some applications of flash ADC include radar,
       high speed test equipment, medical imaging and digital communication.
       The difference with this and other types of ADC is that the input signals
       are processed in a parallel method. Flash converters operate by
       simultaneously comparing the input signal with unique reference levels
       spaced 1 least significant bit apart. This requires many front-end
       comparators and a large digital encoding section. Simultaneously, each
       comparator generates an output to a priority encoder which then produces
       the digital representation of the input signal level. In order for this to
       work you have to use one comparator for each least significant bit.
       Therefore, a 8-bit flash converter requires 255 comparators along with
       high speed logic to encode the comparator outputs.

Like in the diagram note that input signal is simultaneously measured by
each comparator which has a unique resistor ladder generated reference.
This produces a series of 1s and 0s such that the outputs will be all 1s
when below the reference levels. The comparator output is called a
thermometer. Following the comparator outputs is the digital section
consisting of several logic gates for encoding the thermo codes. The
thermometer decoder determines the point where the series of 1s and 0s
form a boundary. The priority encoder encoder uses this boundary
threshold for conversion to binary output. Output from the priority
encoder are then available to the system memory. It is important that the
memory system be designed properly to prevent lost data since every new
conversion will overwrite the previous result.


There are two types of Flash ADC where one is bipolar and the other is
CMOS. The difference is in how the front end of the comparators are
created. CMOS is used for the ease of using analog switches and
capacitors. CMOS flash converters can equal the speed of all except the
bipolar designs with emitter-coupled logic. The advantage of using
CMOS is that the power consumption is less with the N and P channels.
     Bipolar Flash ADC

     Using Bipolar components gives a different frequency response limitation
     due to the transistors. Using buffers are used to prevent the input and
     reference signal from excessive comparator loading. These buffers
     responsible for the dynamic performance of ADC. Although it is possible
     to use TTL or CMOS, ECL (emitter-coupled logic) is used for the highest
     speed. The high speed is possible by using ECL for the encoding stage
     which requires a negative supply voltage. This means that the bipolar
     comparators also need a negative supply voltage. ECL is faster because is
     keeps the logic transistors from operation in the saturated state (restricted
     to either cutoff or active). This eliminates the charge storage delays that
     occur when a transistor is driven in the saturated mode.

Tracking Analog-to-Digital Converter

     Tracking uses a up down counter and is faster that the digital ramp single
     or multi-slope ADC because the counter is not reset after each sample. It
     tracks analog input hence the name tracking. In order for this to work the
     output reference voltage should be lower than the analog input. When the
     comparator output is high the counter is in counting up mode of binary
     numbers. This as a result increases stair step reference voltage out until
     the ramp reaches the input voltage amount. When reference voltage
     equals the input voltage the comparators output is switched to low mode
     and starts counting down. If the analog input is decreasing the counter
     will continue to back down to track input. If the analog input is increasing
     the counter will go down one count and resume counting up to follow the
     curve or until the comparison occurs.

     An 8 - bit tracking ADC
Single-Slope Analog-to-Digital Converter

     Single-slope ADCs are appropriate for very high accuracy of high-
     resolution measurements where the input signal bandwidth is relatively
     low. Besides the accuracy these types of converters offer a low-cost
     alternative to others such as the successive-approximation approach.
     Typical applications for these are digital voltmeters, weighing scales, and
     process control. They are also found in battery-powered instrumentation
     due to the capability for very low power consumption.

     The name implies that single-slope ADC use only one ramp cycle to
     measure each input signal. The single-scope ADC can be used for up to
     14-bit accuracy. The reason for only 14-bit accuracy is because single-
     slope ADC is more susceptible to noise. Because this converter uses a
     fixed ramp for comparing the input signal, any noise present at the
     comparator input while the ramp is near the threshold crossing can cause

     The basic idea behind the single slope converter is to time how long it
     takes for a ramp to equal and input signal at a comparator. Absolute
     measurements require that an accurate reference (Vref) matching the
     desired accuracy be used for comparing the time with the unknown input
     measurement. Therefore the input unknown (Vun) can be determined by:

     Vun = Vref(Tun/Tref)

     Where the ration is directly proportional to the difference in magnitudes.
     The main part of the single-slope analog to digital converter is the ramp
     voltage required to compare with the input signal. If the ramp function is
     highly linear then the system errors will be completely cancelled. Since
     each input is measured with the same ramp signal and hardware, the
     component tolerances are exactly the same for each measurement.
     Regardless of the initial conditions or temperature drifts, no calibration or
     auto zero function is required

Dual-Slope Analog-to-Digital Converter

     Dual-Slope ADC operate on the principle of integrating the unknown
     input and then comparing the integration times with a reference cycle.
     The The basic way is to use two slopes (dual) as in this diagram:
    This circuit operates by switching in thr unknown input signal and then
    integrating for full scale number counts. During this cycle the reference is
    switched in and if the reference is of opposite polarity, the ramp will be
    driven back towards ground. The time that is takes for the ramp to again
    reach the comparator threshold of ground will be directly proportional to
    the unknown input signal. Since the circuit uses the same time constant
    for the integrator, the component tolerances will be the same for both the
    integration and differentiation cycles. Therefore the errors will cancel
    except for the offset voltage that will be additive during both the cycles.

    The main benefits of this Type is the increased range, the increased
    accuracy and resolution, and the increased speed.

Successive-Approximation Analog-to-Digital Converter

    The successive-approximation ADC is also called a sampling ADC. The
    term successive-approximation comes from testing each bit of resolution
    from the most significant bit to the lowest. These are the most popular
    today because there is a wide range in performances and levels of
    integration to fit a different amount of task. Newer successive-
    approximation converters sample only sample the input once per
    conversion, where as the earlier ones sampled as many times as the
    number of bits. Sampled converters have the advantage over the one that
    don't sample because they can tolerate input signals changing between bit
    tests. Non sampled converters performance would be downgraded if the
    input only changed more than a 1/2 if a least significant bit. This is due to
    the inputs being sampled n times for every conversion, where n is the bits
    of resolution.

    The basic principle behind this device is to use a DAC approximation of
    the input and make a comparison with the input for each bit of resolution.
        The most significant bit is tested first generating 1/2Vref with the DAC
        and comparing it to the sampled input signal. the successive-
        approximation register (SAR) drives the DAC to produce estimates of the
        input signal and continuing this process to the least significant bit. Each
        estimation more accurately closes in on the input level. For each bit test,
        the comparator output will determine if the estimate should stay as a 1 or 0
        in the result register. If the comparator indicates that the estimated value
        is under the input level, then the bit stays set. Otherwise, the bit is reset in
        the result register.

        An Example of successive-approximation (sampling) ADC:

Acknowledgements: Ray Smith, Fardeen Haji , Eric Jeeboo , Richard Phagu
The 8085 Instruction Set
As I promised, in an earlier lesson, I am going to go through an in-depth explaination of
ALL the 8085 instructions.

|                                                                |
|                                                                |
|                               Intel                            |
|                                                                |
|          88888        000       88888    5555555         A     |
|        8       8    0     0   8       8  5              A A    |
|        8       8  0     0 0   8       8  5            A    A   |
|          88888    0 0 0         88888    555555     AAAAAAA    |
|        8       8  0 0       0 8       8          5  A        A |
|        8       8    0     0   8       8          5  A        A |
|          88888        000       88888    555555     A        A |
|                                                                |
|          8085A MICROPROCESSOR Instruction Set Summary          |
|                                                                |
|                                                                |
|                                                                |
|                                                                |
|                                                                |
|                       _________     _________                  |
|                   _|            \__/          |_               |
|           --> X1 |_|1                      40|_| Vcc (+5V)     |
|                   _|                          |_               |
|           --> X2 |_|2                      39|_| HOLD <--      |
|                   _|                          |_               |
|   <-- RESET OUT |_|3                       38|_| HLDA -->      |
|                   _|                          |_               |
|          <-- SOD |_|4                      37|_| CLK (OUT) --> |
|                   _|                          |_ ________      |
|          --> SID |_|5                      36|_| RESET IN <--  |
|                   _|                          |_               |
|        --> TRAP |_|6                       35|_| READY <--     |
|                   _|                          |_      _        |
|     --> RST 7.5 |_|7                       34|_| IO/M -->      |
|                   _|                          |_               |
|     --> RST 6.5 |_|8                       33|_| S1 -->        |
|                   _|                          |_ __            |
|     --> RST 5.5 |_|9                       32|_| RD -->        |
|                   _|                          |_ __            |
|        --> INTR |_|10         8085A        31|_| WR -->        |
|             ____ _|                           |_               |
|        <-- INTA |_|11                      30|_| ALE -->       |
|                   _|                          |_               |
|        <--> AD0 |_|12                      29|_| S0 -->        |
|                   _|                          |_               |
|        <--> AD1 |_|13                      28|_| A15 -->       |
|                   _|                          |_               |
|        <--> AD2 |_|14                      27|_| A14 -->       |
|                   _|                          |_               |
|      <--> AD3 |_|15                  26|_|   A13 -->   |
|                _|                      |_              |
|      <--> AD4 |_|16                  25|_|   A12 -->   |
|                _|                      |_              |
|      <--> AD5 |_|17                  24|_|   A11 -->   |
|                _|                      |_              |
|      <--> AD6 |_|18                  23|_|   A10 -->   |
|                _|                      |_              |
|      <--> AD7 |_|19                  22|_|   A9 -->    |
|                _|                      |_              |
|     (Gnd) Vss |_|20                  21|_|   A8 -->    |
|                 |______________________|               |
|                                                        |
|                                                        |
|                                                        |

Instructions can be categorized according to their method
of addressing the hardware registers and/or memory.

Implied Addressing:
The addressing mode of certain instructions is implied by
the instruction’s function. For example, the STC (set carry
flag) instruction deals only with the carry flag, the DAA
(decimal adjust accumulator) instruction deals with the

Register Addressing:
Quite a large set of instructions call for register
addressing. With these instructions, you must specify one
of the registers A through E, H or L as well as the
operation code. With these instructions, the accumulator is
implied as a second operand. For example, the instruction
CMP E may be interpreted as 'compare the contents of the E
register with the contents of the accumulator.

Most of the instructions that use register addressing deal
8-bit values. However, a few of these instructions deal
with 16-bit register pairs. For example, the PCHL
instruction exchanges the contents of the program counter
with the contents of the H and L registers.

Immediate Addressing:
Instructions that use immediate addressing have data
assembled as a part of the instruction itself. For example,
the instruction CPI 'C' may be interpreted as ‘compare the
contents of the accumulator with the letter C. When
assembled, this instruction has the hexadecimal value FE43.
Hexadecimal 43 is the internal representation for the
letter C. When this instruction is executed, the processor
fetches the first instruction byte and determines that it
must fetch one more byte. The processor fetches the next
byte into one of its internal registers and then performs
the compare operation.

Notice that the names of the immediate instructions
indicate that they use immediate data. Thus, the name of an
add instruction is ADD; the name of an add immediate
instruction is ADI.

All but two of the immediate instructions uses the
accumulator as an implied operand, as in the CPI
instruction shown previously. The MVI (move immediate)
instruction can move its immediate data to any of the
working registers including the accumulator or to memory.
Thus, the instruction MVI D, OFFH moves the hexadecimal
value FF to the D register.

The LXI instruction (load register pair immediate) is even
more unusual in that its immediate data is a 16-bit value.
This instruction is commonly used to load addresses into a
register pair. As mentioned previously, your program must
initialize the stack pointer; LXI is the instruction most
commonly used for this purpose. For example, the
instruction LXI SP,3OFFH loads the stack pointer with the
hexadecimal value 30FF.

Direct Addressing:
Jump instructions include a 16-bit address as part of the
instruction. For example, the instruction JMP 1000H causes
a jump to the hexadecimal address 1000 by replacing the
current contents of the program counter with the new value
Instructions that include a direct address require three
bytes of storage: one for the instruction code, and two for
the 16-bit address

Register Indirect Addressing:
Register indirect instructions reference memory via a
register pair. Thus, the instruction MOV M,C moves the
contents of the C register into the memory address stored
in the H and L register pair. The instruction LDAX B loads
the accumulator with the byte of data specified by the
address in the B and C register pair.
Combined Addressing Modes:
Some instructions use a combination of addressing modes. A
CALL instruction, for example, combines direct addressing
and register indirect addressing. The direct address in a
CALL instruction specifies the address of the desired
subroutine; the register indirect address is the stack
pointer. The CALL instruction pushes the current contents
of the program counter into the memory location specified
by the stack pointer.

Timing Effects of Addressing Modes:
Addressing modes affect both the amount of time required
for executing an instruction and the amount of memory
required for its storage. For example, instructions that
use implied or register addressing, execute very quickly
since they deal directly with the processor’s hardware or
with data already present in hardware registers. Most
important, however is that the entire instruction can be
fetched with a single memory access. The number of memory
accesses required is the single greatest factor in
determining execution timing. More memory accesses
therefore require more execution time. A CALL instruction
for example, requires five memory accesses: three to access
the entire instruction and two more to push the contents of
the program counter onto the stack.

The processor can access memory once during each processor
cycle. Each cycle comprises a variable number of states.
(See below and the appendix of “USING THE SDK-85
MICROPROCESSOR TRAINER”). The length of a state depends on
the clock frequency specified for your system, and may
range from 480 nanoseconds to 2 microseconds. Thus, the
timing for a four state instruction may range from 1.920
microseconds through 8 microseconds. (The 8085 have a
maximum clock frequency of 5 MHz and therefore a minimum
state length of 200 nanoseconds.)

Instruction Naming Conventions:
The mnemonics assigned to the instructions are designed to
indicate the function of the instruction. The instructions
fall into the following functional categories:

Data Transfer Croup:
The data transfer instructions move data between registers
or between memory and registers.
MOV            Move
MVI            Move Immediate
LDA            Load Accumulator Directly from Memory
STA            Store Accumulator Directly in Memory
LHLD           Load H & L Registers Directly from Memory
SHLD           Store H & L Registers Directly in Memory

An 'X' in the name of a data transfer instruction implies
that it deals with a register pair (16-bits);

LXI            Load Register Pair with Immediate data
LDAX           Load Accumulator from Address in Register
STAX           Store Accumulator in Address in Register
XCHG           Exchange H & L with D & E
XTHL           Exchange Top of Stack with H & L

Arithmetic Group:
The arithmetic instructions add, subtract, increment, or
decrement data in registers or memory.

ADD            Add   to Accumulator
ADI            Add   Immediate Data to Accumulator
ADC            Add   to Accumulator Using Carry Flag
ACI            Add   Immediate data to Accumulator Using
SUB            Subtract from Accumulator
SUI            Subtract Immediate Data from Accumulator
SBB            Subtract from Accumulator Using Borrow
(Carry) Flag
SBI            Subtract Immediate from Accumulator Using
               Borrow (Carry) Flag
INR            Increment Specified Byte by One
DCR            Decrement Specified Byte by One
INX            Increment Register Pair by One
DCX            Decrement Register Pair by One
DAD            Double Register Add; Add Content of Register
               Pair to H & L Register Pair

Logical Group:
This group performs logical (Boolean) operations on data in
registers and memory and on condition flags.

The logical AND, OR, and Exclusive OR instructions enable
you to set specific bits in the accumulator ON or OFF.
ANA            Logical AND with Accumulator
ANI            Logical AND with Accumulator Using Immediate
ORA            Logical OR with Accumulator
OR             Logical OR with Accumulator Using Immediate
XRA            Exclusive Logical OR with Accumulator
XRI            Exclusive OR Using Immediate Data

The Compare instructions compare the content of an 8-bit
value with the contents of the accumulator;

CMP            Compare
CPI            Compare Using Immediate Data

The rotate instructions shift the contents of the
accumulator one bit position to the left or right:

RLC           Rotate    Accumulator Left
RRC           Rotate    Accumulator Right
RAL           Rotate    Left Through Carry
RAR           Rotate    Right Through Carry

Complement and carry flag instructions:

CMA            Complement Accumulator
CMC            Complement Carry Flag
STC            Set Carry Flag

Branch Group:
The branching instructions alter normal sequential program
flow, either unconditionally or conditionally. The
unconditional branching instructions are as follows:

JMP            Jump
CALL           Call
RET            Return

Conditional branching instructions examine the status of
one of four condition flags to determine whether the
specified branch is to be executed. The conditions that may
be specified are as follows:

NZ             Not Zero (Z = 0)
Z              Zero (Z = 1)
NC             No Carry (C = 0)
C              Carry (C = 1)
PO                Parity Odd (P = 0)
PE                Parity Even (P = 1)
P                 Plus (S = 0)
M                 Minus (S = 1)

Thus, the conditional branching instructions are specified
as follows:

Jumps             Calls           Returns
C                 CC              RC         (Carry)
INC               CNC             RNC        (No Carry)
JZ                CZ              RZ         (Zero)
JNZ               CNZ             RNZ        (Not Zero)
JP                CP              RP         (Plus)
JM                CM              RM         (Minus)
JPE               CPE             RPE        (Parity Even)
JP0               CPO             RPO        (Parity Odd)

Two other instructions can affect a branch by replacing the
contents or the program counter:

PCHL              Move H & L to Program Counter
RST               Special Restart Instruction Used
                  with Interrupts

Stack I/O, and Machine Control Instructions:
The following instructions affect the Stack and/or Stack

PUSH              Push Two bytes of Data onto the Stack
POP               Pop Two Bytes of Data off the Stack
XTHL              Exchange Top of Stack with H & L
SPHL              Move content of H & L to Stack Pointer

The I/0 instructions are as follows:

IN                Initiate Input Operation
OUT               Initiate Output Operation

The Machine Control instructions are as follows:
EI             Enable Interrupt System
DI             Disable Interrupt System
HLT            Halt
NOP            No Operation
|Mnemonic |Op|SZAPC|~s|Description               |Notes        |
|ACI n    |CE|*****| 7|Add with Carry Immediate |A=A+n+CY       |
|ADC r    |8F|*****| 4|Add with Carry            |A=A+r+CY(21X)|
|ADC M    |8E|*****| 7|Add with Carry to Memory |A=A+[HL]+CY |
|ADD r    |87|*****| 4|Add                       |A=A+r    (20X)|
|ADD M    |86|*****| 7|Add to Memory             |A=A+[HL]      |
|ADI n    |C6|*****| 7|Add Immediate             |A=A+n         |
|ANA r    |A7|****0| 4|AND Accumulator           |A=A&r    (24X)|
|ANA M    |A6|****0| 7|AND Accumulator and Memory|A=A&[HL]      |
|ANI n    |E6|**0*0| 7|AND Immediate             |A=A&n         |
|CALL a   |CD|-----|18|Call unconditional        |-[SP]=PC,PC=a|
|CC a     |DC|-----| 9|Call on Carry             |If CY=1(18~s)|
|CM a     |FC|-----| 9|Call on Minus             |If S=1 (18~s)|
|CMA      |2F|-----| 4|Complement Accumulator    |A=~A          |
|CMC      |3F|----*| 4|Complement Carry          |CY=~CY        |
|CMP r    |BF|*****| 4|Compare                   |A-r      (27X)|
|CMP M    |BF|*****| 7|Compare with Memory       |A-[HL]        |
|CNC a    |D4|-----| 9|Call on No Carry          |If CY=0(18~s)|
|CNZ a    |C4|-----| 9|Call on No Zero           |If Z=0 (18~s)|
|CP a     |F4|-----| 9|Call on Plus              |If S=0 (18~s)|
|CPE a    |EC|-----| 9|Call on Parity Even       |If P=1 (18~s)|
|CPI n    |FE|*****| 7|Compare Immediate         |A-n           |
|CPO a    |E4|-----| 9|Call on Parity Odd        |If P=0 (18~s)|
|CZ a     |CC|-----| 9|Call on Zero              |If Z=1 (18~s)|
|DAA      |27|*****| 4|Decimal Adjust Accumulator|A=BCD format |
|DAD B    |09|----*|10|Double Add BC to HL       |HL=HL+BC      |
|DAD D    |19|----*|10|Double Add DE to HL       |HL=HL+DE      |
|DAD H    |29|----*|10|Double Add HL to HL       |HL=HL+HL      |
|DAD SP   |39|----*|10|Double Add SP to HL       |HL=HL+SP      |
|DCR r    |3D|****-| 4|Decrement                 |r=r-1    (0X5)|
|DCR M    |35|****-|10|Decrement Memory          |[HL]=[HL]-1 |
|DCX B    |0B|-----| 6|Decrement BC              |BC=BC-1       |
|DCX D    |1B|-----| 6|Decrement DE              |DE=DE-1       |
|DCX H    |2B|-----| 6|Decrement HL              |HL=HL-1       |
|DCX SP   |3B|-----| 6|Decrement Stack Pointer   |SP=SP-1       |
|DI       |F3|-----| 4|Disable Interrupts        |              |
|EI       |FB|-----| 4|Enable Interrupts         |              |
|HLT      |76|-----| 5|Halt                      |              |
|IN p     |DB|-----|10|Input                     |A=[p]         |
|INR r    |3C|****-| 4|Increment                 |r=r+1    (0X4)|
|INR M    |3C|****-|10|Increment Memory          |[HL]=[HL]+1 |
|INX B    |03|-----| 6|Increment BC              |BC=BC+1       |
|INX D    |13|-----| 6|Increment DE              |DE=DE+1       |
|INX H    |23|-----| 6|Increment HL              |HL=HL+1       |
|INX SP   |33|-----| 6|Increment Stack Pointer   |SP=SP+1       |
|JMP a    |C3|-----| 7|Jump unconditional        |PC=a          |
|JC a     |DA|-----| 7|Jump on Carry             |If CY=1(10~s)|
|JM a     |FA|-----| 7|Jump on Minus             |If S=1 (10~s)|
|JNC a    |D2|-----| 7|Jump on No Carry          |If CY=0(10~s)|
|JNZ a    |C2|-----| 7|Jump on No Zero           |If Z=0 (10~s)|
|JP a     |F2|-----| 7|Jump on Plus              |If S=0 (10~s)|
|JPE a    |EA|-----| 7|Jump on Parity Even       |If P=1 (10~s)|
|JPO a    |E2|-----| 7|Jump on Parity Odd        |If P=0 (10~s)|
|JZ a     |CA|-----| 7|Jump on Zero              |If Z=1 (10~s)|
|LDA a    |3A|-----|13|Load Accumulator direct   |A=[a]         |
|LDAX B   |0A|-----| 7|Load Accumulator indirect |A=[BC]        |
|LDAX D   |1A|-----| 7|Load Accumulator indirect |A=[DE]        |
|LHLD a   |2A|-----|16|Load HL Direct            |HL=[a]        |
|LXI B,nn |01|-----|10|Load Immediate BC          |BC=nn         |
|LXI D,nn |11|-----|10|Load Immediate DE          |DE=nn         |
|LXI H,nn |21|-----|10|Load Immediate HL          |HL=nn         |
|LXI SP,nn|31|-----|10|Load Immediate Stack Ptr |SP=nn           |
|MOV r1,r2|7F|-----| 4|Move register to register |r1=r2     (1XX)|
|MOV M,r |77|-----| 7|Move register to Memory     |[HL]=r (16X)|
|MOV r,M |7E|-----| 7|Move Memory to register     |r=[HL] (1X6)|
|MVI r,n |3E|-----| 7|Move Immediate              |r=n      (0X6)|
|MVI M,n |36|-----|10|Move Immediate to Memory |[HL]=n           |
|NOP      |00|-----| 4|No Operation               |              |
|ORA r    |B7|**0*0| 4|Inclusive OR Accumulator |A=Avr      (26X)|
|ORA M    |B6|**0*0| 7|Inclusive OR Accumulator |A=Av[HL]        |
|ORI n    |F6|**0*0| 7|Inclusive OR Immediate     |A=Avn         |
|OUT p    |D3|-----|10|Output                     |[p]=A         |
|PCHL     |E9|-----| 6|Jump HL indirect           |PC=[HL]       |
|POP B    |C1|-----|10|Pop BC                     |BC=[SP]+      |
|POP D    |D1|-----|10|Pop DE                     |DE=[SP]+      |
|POP H    |E1|-----|10|Pop HL                     |HL=[SP]+      |
|POP PSW |F1|-----|10|Pop Processor Status Word |{PSW,A}=[SP]+|
|Mnemonic |Op|SZAPC|~s|Description                |Notes         |
|PUSH B   |C5|-----|12|Push BC                    |-[SP]=BC      |
|PUSH D   |D5|-----|12|Push DE                    |-[SP]=DE      |
|PUSH H   |E5|-----|12|Push HL                    |-[SP]=HL      |
|PUSH PSW |F5|-----|12|Push Processor Status Word|-[SP]={PSW,A}|
|RAL      |17|----*| 4|Rotate Accumulator Left    |A={CY,A}<-    |
|RAR      |1F|----*| 4|Rotate Accumulator Righ    |A=->{CY,A}    |
|RET      |C9|-----|10|Return                     |PC=[SP]+      |
|RC       |D8|-----| 6|Return on Carry            |If CY=1(12~s)|
|RIM      |20|-----| 4|Read Interrupt Mask        |A=mask        |
|RM       |F8|-----| 6|Return on Minus            |If S=1 (12~s)|
|RNC      |D0|-----| 6|Return on No Carry         |If CY=0(12~s)|
|RNZ      |C0|-----| 6|Return on No Zero          |If Z=0 (12~s)|
|RP       |F0|-----| 6|Return on Plus             |If S=0 (12~s)|
|RPE      |E8|-----| 6|Return on Parity Even      |If P=1 (12~s)|
|RPO      |E0|-----| 6|Return on Parity Odd       |If P=0 (12~s)|
|RZ       |C8|-----| 6|Return on Zero             |If Z=1 (12~s)|
|RLC      |07|----*| 4|Rotate Left Circular       |A=A<-         |
|RRC      |0F|----*| 4|Rotate Right Circular      |A=->A         |
|RST z    |C7|-----|12|Restart               (3X7)|-[SP]=PC,PC=z|
|SBB r    |9F|*****| 4|Subtract with Borrow       |A=A-r-CY      |
|SBB M    |9E|*****| 7|Subtract with Borrow       |A=A-[HL]-CY |
|SBI n    |DE|*****| 7|Subtract with Borrow Immed|A=A-n-CY       |
|SHLD a   |22|-----|16|Store HL Direct            |[a]=HL        |
|SIM      |30|-----| 4|Set Interrupt Mask         |mask=A        |
|SPHL     |F9|-----| 6|Move HL to SP              |SP=HL         |
|STA a    |32|-----|13|Store Accumulator          |[a]=A         |
|STAX B   |02|-----| 7|Store Accumulator indirect|[BC]=A         |
|STAX D   |12|-----| 7|Store Accumulator indirect|[DE]=A         |
|STC      |37|----1| 4|Set Carry                  |CY=1          |
|SUB r    |97|*****| 4|Subtract                   |A=A-r    (22X)|
|SUB M    |96|*****| 7|Subtract Memory            |A=A-[HL]      |
|SUI n    |D6|*****| 7|Subtract Immediate         |A=A-n         |
|XCHG     |EB|-----| 4|Exchange HL with DE        |HL<->DE       |
|XRA r    |AF|**0*0| 4|Exclusive OR Accumulator |A=Axr     (25X)|
|XRA M     |AE|**0*0| 7|Exclusive OR Accumulator |A=Ax[HL]      |
|XRI n     |EE|**0*0| 7|Exclusive OR Immediate    |A=Axn        |
|XTHL      |E3|-----|16|Exchange stack Top with HL|[SP]<->HL    |
| PSW         |-*01 | |Flag unaffected/affected/reset/set       |
| S           |S    | |Sign (Bit 7)                             |
| Z           | Z   | |Zero (Bit 6)                             |
| AC          | A | |Auxilary Carry (Bit 4)                     |
| P           |   P | |Parity (Bit 2)                           |
| CY          |    C| |Carry (Bit 0)                            |
| a p                  |Direct addressing                       |
| M z                  |Register indirect addressing            |
| n nn                 |Immediate addressing                    |
| r                    |Register addressing                     |
|DB n(,n)              |Define Byte(s)                          |
|DB 'string'           |Define Byte ASCII character string      |
|DS nn                 |Define Storage Block                    |
|DW nn(,nn)            |Define Word(s)                          |
| A B C D E H L |Registers (8-bit)                              |
| BC DE HL             |Register pairs (16-bit)                 |
| PC                   |Program Counter register (16-bit)       |
| PSW                  |Processor Status Word (8-bit)           |
| SP                   |Stack Pointer register (16-bit)         |
| a nn                 |16-bit address/data (0 to 65535)        |
| n p                  |8-bit data/port (0 to 255)              |
| r                    |Register (X=B,C,D,E,H,L,M,A)            |
| z                    |Vector (X=0H,8H,10H,18H,20H,28H,30H,38H)|
| + -                  |Arithmetic addition/subtraction         |
| & ~                  |Logical AND/NOT                         |
| v x                  |Logical inclusive/exclusive OR          |
| <- ->                |Rotate left/right                       |
| <->                  |Exchange                                |
| [ ]                  |Indirect addressing                     |
| [ ]+ -[ ]            |Indirect address auto-inc/decrement     |
| { }                  |Combination operands                    |
| ( X )                |Octal op code where X is a 3-bit code   |
| If ( ~s)             |Number of cycles if condition true      |

To top