




























Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
A comprehensive introduction to the 6811 microprocessor, covering its architecture, programming concepts, and various applications. It delves into memory organization, registers, addressing modes, data types, arithmetic operations, and control mechanisms. The document also explores the 6811's specialized features, including its motor port, digital sensor port, analog input port, timers, and serial interface. It concludes with a discussion of assembly language programming techniques and assembler specifics.
Typology: Lecture notes
1 / 36
This page cannot be seen from the preview
Don't miss anything!
Most humans, having ten fingers, think in decimal numbers. In computers, in- formation is represented with voltages, and it is most convenient for the voltage levels to represent only two states: a binary one or binary zero. Thus computers process binary digits, or bits. For convenience, microprocessors group bits together into words. The first mi- croprocessor, the Intel 4004, operated on a word composed of four bits. Nowadays, many microprocessors use eight bit words, called bytes. In an eight bit numeral, 256 different states can be represented (2^8 = 256). Programmers use these 256 states to represent different things. Some common usages of a byte of data are:
a natural number from 0 to 255;
an integer in the range of -128 to 127;
a character of data (a letter, number, or printable symbol). The Media Laboratory at the Massachusetts Institute of Technology, 20 Ames Street Room E15–320, Cambridge, MA 02139. E-mail: fredm@media.mit.edu. This document is Copy- right c 1992–94 by Fred G. Martin. It may distributed freely in verbatim form provided that no fee is collected for its distribution (other than reasonable reproduction costs) and this copyright notice is included. An electronic version of this document is available via anonymous FTP from cherupakha.media.mit.edu (Internet 18.85.0.47).
When programmers need to represent larger numerals, they group bytes to- gether. A common grouping is two bytes, often called a (16-bit) word. A word can have 65536 states, since 2^16 = 65536. Decimal numbers are painful to use when talking about binary information. To make life easier, programmers started to use the base 16 hexadecimal (or hex for short) numbering system when talking about bits, bytes, and other binary data. The hex system uses 16 different digits to represent each place value of a numeral. Using hex, one would count as follows: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10 : : : etc. The letters A though F are then used to represent the values of (decimal) 10 through 15, respectively. This is wonderful, because a hex digit (of 16 possible states) is equivalent to four bits exactly. Then, a byte can be represented by exactly two hex digits, and a sixteen bit word by four of them. The following conventions are supported by Motorola’s software products for their microprocessors. Binary numbers are represented by the prefix %. Hexadec- imal numbers are specified by $. Decimal numbers don’t have a prefix. (These aren’t the only conventions that are used in the computer world, but they will be standard throughout these notes and Motorola software.) Let’s examine some of the numeric conversions in Figure 1. Notice that four bits equal one hex digit. This is helpful in converting binary to hex. Notice some entries don’t have their decimal values filled in. This is to make the point that it’s easy to transcribe between binary and hexadecimal representation, but using decimal is often cumbersome. It’s good to know some general quantities. For example, eight bits, or one byte, is 256 values. Then the largest unsigned integer representable in a byte is
(^1) ASCII is pronounced as in “ ass-key .”
So, there are actually only 128 ASCII characters, using the values $00 to $7F hex. Printable characters start at $20 hex (32 decimal). The codes from $0 to $1F are used for things like cursor control, line feeds, and the like. Knowing the ASCII characters become important if when doing interactive programming on your 6811, in which case the user might type ASCII information to the 6811 over the serial line, and it would respond in kind. Then, the programmer must deal with the characters as bytes, and the ASCII codes become important.
2 Introduction to the 6811
Microprocessors store their programs and data in memory. Memory is organized as a contiguous string of addresses , or locations. Each memory location contains eight bits of data (this is because the 6811 is an 8-bit micro; other processors can have 16 or 32 bits of data at each memory location). The entire amount of memory that a processor can access is called its address space. The 6811 has an address space of 65,536 memory locations, corresponding exactly to 16 bits of address information. This mean that a 16-bit numeral can be used to point at, or address, any of the memory bytes in the address space of the 6811. Thus four hexadecimal digits (4 bits per digit 4 digits) can exactly specify one memory location (in which one will find one byte of information). In general, any area of memory should be equivalent to any other. Because the 6811 is a special-purpose chip, and all of its memory is etched right on the microprocessor chip itself, its designers had to dedicate portions of its memory to particular functions. Figure 2 shows a “memory map” of the 6811 chip. Let’s go over this map in detail. The first area of memory, from address $00 to $FF, is the chip’s random access memory , or RAM. RAM can be both written and erased. It’s “volatile,” which means that when power is removed from the chip, it loses its state. RAM is typically used for storing programs and data. When you program the 6811, however, RAM is not typically used to store programs, because there’s only 256 bytes of it ($00 to $FF is 256 bytes). It is normally used it to store data and variable values that the program will use while it’s running. Programs will reside normally in the EEPROM, an acronym for electrically
Memory Address Function $0000 RAM memory $00FF $0100 unused $0FFF $1000 special $103F registers $1040 unused $F7FF $F800 EEPROM memory $FFFF
Figure 2: Memory Map of the MC68HC811E2FN Microprocessor
erasable programmable read-only memory. EEPROM is the culmination of a trend in programmable, yet permanent, memory technology. Read-only memory (ROM) means what it suggests: that memory can only be read, not written to like RAM. It is programmed at the factory, in mass quantities. This is good for companies that are selling a production version, but to satisfy R & D engineers, PROM ( programmable read-only memory ) was developed. PROM chips can’t be erased, so when in order to make changes to code, the chip is throw away and a new one is used. PROM chips aren’t horribly expensive, but this process still imposes a high development cost. EPROM, or erasable programmable read only memory , was the next step. Most EPROM chips are erased by exposing the chip to ultraviolet light for half an hour. This is a vast improvement over PROM, but unless there is a large supply of blank chips for reprogramming, the programmer will have a long wait time between code downloads. The Motorola MC68HC811E2FN chip has the latest development in ROM technology: EEPROM, which is electrically erasable. This means that the chip can erase its own ROM, and download new data to be written into it. It’s the ultimate thing for microprocessor hackers, because new programs can be downloaded into the chip in just ten seconds or so. Also, because it’s ROM, when the micro is powered down, its program doesn’t go away. EEPROM isn’t a substitute for RAM: writing new data in is extremely slow
7 A 7 B 0 8-bit accumulators A and B 15 D 0 or 16-bit double accumulator D
15 X 0 index register X
15 Y 0 index register Y
15 SP 0 stack pointer
15 PC 0 program counter
Figure 3: Programmer’s Model of 6811 Registers
The Stack Pointer, or SP register, is used to store the location of the program stack. The stack, which is explained in detail later, is used for temporary storage of data, and to store the return address before a subroutine is called. The Program Counter, or PC, is used to keep track of the current instruction being executed. The PC is automatically incremented as the microprocessor proceeds through the instruction stream.
3 Programming the 6811
When a microprocessor runs a program, it advances sequentially through memory, fetching and executing one instruction at a time. As mentioned earlier, the PC (program counter) register keeps track of the address of the instruction currently being executed. The microprocessor automatically advances the PC to the next instruction after it is finished executing the current one. Let’s look at typical instruction: load a number into the A register. The machine code for this instruction is (in hex): 86 nn, where nn is the byte to be loaded into the register. The hex value $86 is called the operational code , or op-code , that signifies the “load A register” instruction. Instructions may be one, two, three, or four bytes long, depending on what their function is. When the microprocessor encounters the byte $86 in the instruction stream, it knows, “I’m going to fetch the next byte of data, and load that into my A register.” After the microprocessor evaluates the first byte of an instruction, it
knows how many more bytes it needs to fetch to complete the instruction, if it is longer than one byte. Then it executes the next instruction, and so on, ad infinitum. Instructions take varying numbers of machine cycles to execute, depending on their complexity. The 6811 we’re using operates at a frequency of 2 megahertz (Mhz.), meaning that it executes 2,000,000 machine cycles per second. The period of a machine cycle is then 0.5 microseconds (sec), so an instruction that requires 3 machine cycles will take 1.5 sec of real time to execute. In general, longer instructions (those needing two, three, or four bytes) take longer (more machine cycles) to execute, although there are some exceptions to this rule.
People often speak of machine code and assembly language. Both of these terms refer to the same thing: the program that is executed directly by the microprocessor. However, these terms refer to that program in different states of development. Let me explain. Machine code usually refers to the raw data stored as a microprocessor’s program. This is commonly described in the hexadecimal notation we’ve been using. Assembly language is a set of mnemonics , or names, and a notation that is a readable yet efficient way of writing down the machine instructions. Usually, a program that is written in assembly language is processed by an assembler program , that converts the mnemonic instructions into machine code. This out- put from the assembler program is often called the object code , which can then executed directly by the microprocessor. In the 6811 assembly language, the “Load A register” instruction that we discussed earlier is written as follows:
LDAA #$
The word “LDAA” is the assembly language mnemonic for “LoaD Accumulator A.” Then, the #$80 is the hexadecimal value to be loaded (I just chose the value $80 at random). When a 6811 assembler program processes an input file, it knows the mnemon- ics for all of the 6811 instructions, plus their corresponding op-codes. It uses this information to create the object code file.
In the direct mode, only one byte of address data is required to specify the memory address, since it is known to be in the first 256 bytes of memory. Instruc- tions using direct addressing may require only two bytes: one for the op-code, and one for the address information. They execute in fewer cycles as a result of this savings. The 6811 assembler will automatically choose the direct addressing mode if the address specified is in the range $00 to $FF. Extended addressing could also be used to access this portion of memory, but it would rarely be preferable. The indexed addressing mode uses the X or Y register as a pointer into memory. The value contained in the index register is and an offset byte are added to specify the location of the desired memory byte or word. Let’s look at an example to make this clear. Suppose the X register currently has the value $1000. Then the instruction
LDAA 0,X
will load the A register with the contents of location $1000, and the instruction
LDAA 5,X
will load the A register with the contents of location $1005. The offset value is contained in one byte of data, and only positive or zero offsets are allowed. As such, only offsets in the range of 0 to 255 decimal are possible. Why would a programmer use the indexed addressing mode, when the extended addressing mode will access the desired byte directly? The reason to use the indexed modes, with their associated offset bytes, is when repeatedly accessing locations from a particular region of memory. For example, the 6811 special register area begins at location $1000 and ends at location $103F. Suppose there were a series of instructions that accessed the registers located in this area. We could then set up the X register as a base pointer — pointing to the beginning of this area of memory (we’d load the X register with $1000: LDX #$1000). Then, we can use the two-byte indexed instructions to do a series of loads, stores, or whatever to the locations in this region that we were interested in. This is good programming practice because each indexed instruction saves a byte over the extended instruction. Once the cost is paid of loading the X register with the base address (a three byte instruction), each use of an indexed instruction will save code space and execution time.
Indexed addressing really is most useful when working with arrays of common data structures. Then, one can set up an index register to point at the base of each data structure, and use indexed operations to access individual fields of that data element. To move to the next data element, only the index base pointer needs to be changed, the offsets will then access the subsequent structure. Finally, there are a few instructions that do not support the extended addressing mode (they support only direct and indexed addressing), so if one must work with a byte not in the direct addressing area, then indexed addressing must be used.
Here is a list of all of the addressing modes that are supported on the 6811 architecture:
Immediate Data is part of the instruction itself. This mode is specified with the use of the prefix “#” before the data byte or word. Example: LDAA #$ loads the A register with the hex number $80.
Direct Data is located in RAM (within addresses $0000 to $00FF). One byte is used to specify which RAM location is to be used. Example: STAA $ stores the A register to the memory location $0080.
Extended Location of data is specified by a 16-bit address given in the instruc- tion. Example: STAA #$1000 stores the contents of the A register at memory location $1000.
Indexed Location of data is specified by the sum of a 16-bit index register (register X or Y) and an offset value that is part of the instruction. Example: LDAA 5,X loads the A register with the memory byte located at the address that is the sum of the value currently in the X register and 5 (decimal). Offsets range in value from 0 to 255.
Inherent Data is “inherent” to the microprocessor and does not require and external memory address. Example: TAB transfers the contents of the A register to to the B register. No external memory address is required.
Relative Location is specified by an offset value from the address of the instruc- tion currently being executed. Example: BRA 5 causes a branch that skips five bytes ahead in the instruction stream. Relative addressing is only used in branching instructions. offsets range in value from -128 to +127, allowing jumps both forward and backward in the instruction stream.
Logical AND for 8-bit values. This instruction performs a bit-wise “and” oper- ation on two pieces of data. The result of an AND operation is 1 if and only if both of its operands are 1. (e.g., %11110010 ANDed with % yields %11000010.
Logical OR for 8-bit values. This instruction performs a bit-wise “or” operation on two pieces of data. The result of an OR operation is 1 if either or both of its operands is 1.
Logical Exclusive OR for 8-bit values. The result of an exclusive-OR opera- tion (called “EOR”) is 1 if either, but not both, of its inputs are 1.
Arithmetic Shift operations on 8-bit and 16-bit values. The Arithmetic Shift operation moves all the bits in an operand to the left or to the right by one bit position. This is equivalent to a multiplication or division by 2 (respectively) upon the operand.
Rotation operations on 8-bit values. These are similar to the shift operations except that the bit that gets shifted out of the high or low bit position (depending on the direction of the rotate) gets placed in the bit position vacated on the other side of the byte. Example: rotate right (ROR) of %11011001 produces %11101100.
Bitwise Set and Clear operations on 8-bit values. These operations set or clear bits at specified bit positions within an eight-bit data byte.
Clear operations on 8-bit memory bytes or registers. This instruction is equiv- alent to writing a zero into the memory location or 6811 register, but does so more quickly.
There are a few more arithmetic instructions not mentioned here, but they are relatively obscure.
There are two methods of representing binary numbers that are commonly used by microprocessors. Using these two methods, the same string of 1’s and 0’s that comprise a byte or word can represent two different numbers, depending on which method is being used.
The two methods are: unsigned binary format and two’s-complement signed binary format. The unsigned format is used to represent numbers in the range of 0 to 255 (one byte of data) or 0 to 65535 (one word of data). This is the more simple way of representing data; it’s easy to understand because there is a direct translation from the binary digits into the actual numeric value. But, the unsigned format has the limitation that values less than zero cannot be represented. Here are some unsigned binary numbers and their decimal equivalents:
binary decimal %0000 0 %0001 1 %0010 2 %0011 3 %0100 4 %0101 5 %0110 6 %10011100 156 %11100011 227 %11111111 255
Signed values are represented using the “two’s complement” binary format. In this format, a byte can represent a value from 128 to +127, and a word can represent a number from 32768 to +32767. The highest bit (most significant, or left-most bit) of the number is used to represent the sign. A “0” in the high bit indicates a positive or zero value, and a “1” in the high bit indicates a negative value. If the number is positive or zero, then the signed representation is exactly equivalent to the unsigned one. For example, the largest binary number repre- sentable in a byte, using the signed format is %01111111. The leading zero is the sign bit, indicating a non-negative number; the seven ones that follow are the significant digits. If the number is negative, then the following process determines its value: invert the significant digits (change zero’s to one’s and one’s to zero’s), and add one. Put a minus sign in front of the number, and that is the equivalent value. For example, let’s figure out the value of the signed number %10011011. We know this is a negative number, since its high bit is one. To find its value, we take the significant digits (%0011011) and invert them, obtaining %1100100. We
There is a special register in the 6811, called the condition code register , or CCR, where this information is kept. Each condition is represented by a one-bit flag in the CCR; if the flag is 1, then the condition is true. The CCR has eight flags in all; four more in addition to the four mentioned. Each flag has a name: the zero flag is called Z; the overflow flag is V, the negative flag is N, and the carry flag is C. The usefulness of these flags is that programs may branch depending on the value of a particular flag or combination of flags. For example, the following fragment of code will repeatedly decrement the A register until it is zero. This code fragment uses the “branch if not equal to zero” instruction (BNE) to loop until the A register equals zero.
Loop DECA * decrement A register BNE Loop * if not zero, jump back to "Loop" ... * program execution continues here ... * after A is zero
An entire set of these conditional branching instructions allows the programmer to test if the result of an operation was equal to zero, not equal to zero, greater than zero, less than zero, etc. Some of the conditional branching instructions are designed for testing results of two’s complement operations, while others expect to test results of unsigned operations. As mentioned earlier, the same arithmetic operations can be used on both signed and unsigned data. This is true, but the way that one must interpret the condition codes of the result is different. Fortunately, the 6811 branch instructions will perform this interpretation properly, provided the correct instruction is used for the type of data the programmer has in mind. Here is a list of some of the conditional branching instructions supported by the 6811:
result).
a non-zero result).
a carry did not result from the last operation.
occurred.
zero. This instruction works correctly when using unsigned data.
tion is greater than or equal to zero. This instruction works correctly only when using unsigned data.
Other branching instructions work with signed data and check the proper combination of flags to tell if results are greater or less than zero, etc. One important thing to remember about branching instructions is that they use the relative addressing mode , which means that the destination of a branch is specified by a one-byte offset from the location of the branch instruction. As such, branches may only jump forward or backward a maximum of about 128 bytes from the location of the branch instruction. If it is necessary to branch to a location further away, the JuMP instruction (JMP) should be used, which takes an absolute two-byte address for the destination. The destination of a JMP instruction thus may be anywhere in memory. If necessary, use a conditional branch instruction to jump to a JMP instruction that jumps to far-away locations.
All microprocessors support a special type of data structure called the stack. A stack stores data in a last-in, first-out (LIFO) method. To visualize the stack, one may imagine a dishwasher who is washing a sink full of dishes. As he washes a dish, he places it on top of a pile of already-washed dishes. When a chef removes dishes from the pile, the dish that she removes is the last dish that the dishwasher placed on the pile. In this way, the stack of dishes stores the dishes using a last-in, first-out algorithm. The stack on the 6811 works the same way. Instead of a stack of dishes, the 6811 stores bytes in a contiguous area of memory. Instead of a dishwasher and a chef, the 6811 uses a special register, called the stack pointer or SP , to keep track of the location of the stack in memory.
the second subroutine call occurred. It can be seen that advanced computer science ideas like recursion are based in this principle of a stack. One detail worth mentioning about the stack’s implementation on the 6811 is that the stack builds downwards in memory. That is, when a number is pushed on the stack, the stack pointer is actually decremented to point to the next available memory location. This is somewhat counter-intuitive, but it doesn’t matter in how the stack functions. Since the stack is a dynamic structure, it must be located somewhere in 6811 RAM (read/write memory). As shown in Figure 2, the RAM is located from addresses $0000 to $00FF. It’s customary to initialize the stack at the top of RAM —address $00FF. Then, as the stack grows, it moves downwards towards location $0000. A good way to crash the processor is to repeatedly push a value on to the stack and forget to pull it off. If this mistake is made inside a program loop, all of RAM will easily be filled with garbage. When a subroutine attempts to return to its caller, the return address will be nowhere in sight. Just remember: each stack push must be matched with a stack pull. Each subroutine call must be matched with a return from subroutine. And don’t try to execute too many nested subroutine calls, since the 6811 has only 256 bytes of RAM for stack space.
Interrupt routines are a type of subroutine that gets executed when special events happen. These special events are often called interrupts , and they may be generated by a variety of sources. Examples of things that may generate interrupts are: a byte coming in over the serial line, a programmable timer triggering, or a sensor line changing state. When an interrupt happens, the 6811 stops what it is doing, saves its local state (the contents of all registers), and processes the interrupt. Each interrupt has code associated with it; it is this code that is executed when the interrupt occurs. Interrupts may be used to give the appearance that the 6811 is doing several things at once. There are a several reasons for this:
The main code doesn’t have to know when an interrupt occurs. This is because after the interrupt finishes, control is returned to the main code
Stack SP Program Counter (low byte) SP before interrupt SP - 1 Program Counter (hi byte) SP - 2 Y Register (low byte) SP - 3 Y Register (hi byte) SP - 4 X Register (low byte) SP - 5 X Register (hi byte) SP - 6 A Register SP - 7 B Register SP - 8 Condition Code Reg. SP - 9 SP after interrupt
Figure 4: Diagram of Stack After an Interrupt Call
exactly where it left off. No information is lost.
The interrupt servicing process is automatic. In this way, it’s different from a subroutine call, which must be explicitly done each time it is required.
Many interrupts can be enabled at the same time. Whenever they occur, they are serviced. Many “background jobs” can be taken care of independently of each other.
If multiple interrupts are being used, it is possible for an interrupt to occur during the servicing of a different interrupt routine. Typically, interrupting an interrupt routine is not a good idea. The 6811 deals with this nested interrupt condition by queueing up the interrupts and processing them sequentially, based on a predetermined interrupt priority scheme. In their usage of the stack, interrupts are implemented quite like subroutines. When an interrupt call is processed, however, the state of all of the 6811 registers is saved on the stack, not just the return address. This way, when the interrupt routine returns, the processor can continue executing the main code in exactly the same state that it left it. Figure 4 shows the configuration of the stack immediately after an interrupt call.