ENTC 4327 - "Intro to 68HC11 Programming"


Topic: This lecture is supposed to support both last week's assembly language exercise and this week's programming lab.


Review From Last Week

From Wednesday's notes, we began discussing the format of a line of assembly language code, i.e., the code you will be writing for the 68HC11 processor. The format of the code looked something like this:

LABEL    opcode    operand    Comments
  • The LABEL was optional, and was used to reference the line of code if you wanted to jump to it. It must begin in column 1. A label or a comment started with an asterisk are the only things that can begin in column 1.
  • The opcode was the instruction or command in assembly language.
  • The operand was the data that the command operated on.
  • The Comments were included so you could document what the heck it was that you were doing.

Then we discussed the different types of opcodes, and I introduced you to Appendix A of the 68HC11 Reference Manual. This Appendix manual described how each command worked, how it affected the flags in the condition code register, and the machine code that the assembly language converted to. It is a great reference for your programming, and you should have access to it anytime you're programming in 68HC11 assembly (including during the test).

We then talked about addressing modes. This covered the forms that the operand would take. It included:

  • Immediate -- This is when you want the operand to represent a specific number. For example, if I wished to load accumulator ACCA with the hexadecimal number $45, this would be immediate addressing. You specify this mode by putting a "#" before the data. For example, LDAA #$45 loads a hexadecimal $45 into ACCA.
  • Extended -- The operand in this case is an address from $0100 to $FFFF. (I'll explain in a second why it starts at $0100). The actual data that will be used is the data stored at the address referred to by the operand. It is indicated by an absence of the pound sign. For example, LDAA $1045 retrieves the data stored in address $1045 and puts it into ACCA.
  • Direct -- This is just like extended, except that the address range is from $00 to $FF. I.e., the operand is an eight-bit address. It is used instead of extended because it takes less room to store and it's quicker to fetch data in this region of memory. As an example, LDAA $45 retrieves the data stored in address $45 and puts it into ACCA.
  • Indexed -- The operand in this case is an integer followed by a comma and either an X or a Y. The operand represents an address, determined by adding the offset (the integer before the comma) to the value in either IX or IY. For example, if IX contained the address $1000, then the command LDAA 9,X would retrieve the data stored in address $1009 and put it into ACCA.
  • Relative -- This method of addressing takes the operand, which can only be a one-byte value from $00 to $FF, and adds it to the program counter. This will cause the next instruction to be executed at the new address in the program counter. Remember that only the "branch" instructions use relative addressing. The addition is 2's complement signed addition, so any number from -128 to 127 can be added. (I.e., the program counter can be forced to jump up to 128 addresses backwards or 127 addresses forward.) If you get outside of this range, you need to use the JMP instruction. Example, BRA $24 adds a hexadecimal $24 to the current address in the program counter.
  • Inherent -- In this case there is no operand. This is due to the assembly language commands that operate on no data. For example, INCA increments the accumulator ACCA. No data is needed.

One last refresher. You may use any type of data in your assembly language code. You need to tell the assembler, however, what type you are using. We use different symbols to indicated hex and binary.

$14 -- A dollar sign represents hexadecimal
%10010110 -- A percent sign represents binary
34 -- No sign represents decimal

If you are using immediate addressing, i.e., you need to put the "#" in front of the number, do it before the data type sign. For example, LDAA #$15 would load accumulator ACCA with the hexadecimal number $15.

Machine Code

The microprocessor understands sequences of 1's and 0's. You understand the pseudo-English assembly language. How do you get from one to the other? Well, a program called an assembler takes each assembly language instruction (opcode) that you've created and finds the corresponding machine code that the processor understands as that instruction. Once it does that, it follows the machine code with any data that corresponds to the instruction.

Now machine code is quite detail oriented. The machine code for individual opcodes is different for different types of addressing too. For example, open your 68HC11 Reference Manual to Appendix A and the command LDAA. You will notice that the machine code for immediate addressing for the LDAA command is $86, yet for extended addressing, it is $B6.

Once the assembler has converted the opcode to machine language it needs to add the operand too. So, the command LDAA #$15 actually turns into the sequence $86 $15 whereas the command LDAA $1045 (extended addressing) becomes $B6 $10 $45.

The memory where the microprocessor's code is stored is simply a sequence of these machine code values for each opcode followed by the corresponding operand. The labels and comments do not get converted to data to download to the microprocessor.

Opcodes were originally designed to be only 1 byte long. That was before the 68HC11 came along with the index register IY. This caused them to make more than 256 opcode/addressing mode combinations, so they had to extend the machine code. They did this by adding a prebyte to commands that used the IY register. This prebyte is $18.

Operands can be from 0 to 3 bytes long.

As an example, the code below loads data from address $01, adds 34 hex to it, then stores the result at address $02. The highlighted code is your assembly language code; the un-highlighted code is the resulting machine code.

$96 $01   LDAA   $01
$C6 $34   
LDAB   #$34
$1B       
ABA
$97 $02   
STAA   $02

This stores in memory as the following sequence of values:

$96 $01 $C6 $34 $1B $97 $02

Selecting the Right Command

At first, determining the proper instruction to use can be confusing. It can be frustrating to find the one you need out of the myriad of available instructions. The following list breaks the instruction set into ten categories. Before I get to the list, however, I have given you a definition of the superscripts used in the list.

1 - Follows rules of 8-bit register commands. Commands ALWAYS end in A or B to indicate which register is being used.

2 - Can also be used on an 8-bit location in memory

Jumping to different parts of the code (Program Counter Manipulation):

These commands always start with a ‘B’ for "branch" and refer to "short" jumps (i.e. less then 127 bytes forward or 128 bytes backward). The "conditional branches", the ones that "branch if", are always executed based on previous command. (e.g., a compare or load) Note that "Equal to Zero" could also mean "Equal" in the case of a compare.

  • BCC - Branch if Carry Clear
  • BCS - Branch if Carry Set (Same as BLO)
  • BEQ - Branch if Equal to Zero
  • BGE - Branch if Greater than or Equal to Zero
  • BGT - Branch if Greater than Zero
  • BHI - Branch if Higher
  • BHS - Branch if Higher or Same (Same as BCC)
  • BLE - Branch if Less than or Equal to Zero
  • BLO - Branch if Lower (Same as BCS)
  • BLS - Branch if Lower or Same
  • BLT - Branch if Less than Zero
  • BMI - Branch if Minus
  • BNE - Branch if Not Equal to Zero
  • BPL - Branch if Plus
  • BRA - Branch ALWAYS
  • BRCLR - Branch if Bit(s) Clear
  • BRN - Branch NEVER (Has same effect as NOP)
  • BRSET - Branch if Bit(s) Set
  • BVC - Branch if Overflow Clear
  • BVS - Branch if Overflow Set

Long jumps (i.e. across any number of bytes):

  • JMP - Unconditional jump (Always jump)

Comparing

These operations affect the condition flags, but leave the data unchanged in the two locations being compared.

  • CBA - Compare Accumulators
  • CMP1 - Compare to ACCA or ACCB
  • CPD - Compare with Double Accumulator
  • CPX - Compare with Index Register X
  • CPY - Compare with Index Register Y

Subroutines

  • BSR - Short subroutine calls (i.e. less then 127 bytes forward or 128 bytes backward)
  • JSR - Long subroutine calls (i.e. any number of bytes away from program counter)
  • RTS - Return from Subroutine

Interrupts

  • SWI - Software Interrupt
  • WAI - Wait for Interrupt (Pushes processor state and registers to stack and waits for an interrupt)
  • RTI - Return from Interrupt and restore processor/register status

Loading and Storing Register Values:

  • LDA1 - Load Accumulator
  • LDD - Load Double Accumulator
  • LDX - Load Index Register X
  • LDY - Load Index Register Y
  • STA1 - Store Accumulator
  • STD - Store Double Accumulator
  • STX - Store Index Register X
  • STY - Store Index Register Y
  • TAB - Transfer Accumulator A (ACCA) to Accumulator B (ACCB)
  • TBA - Transfer Accumulator B (ACCB) to Accumulator A (ACCA)
  • XGDX - Exchange Double Accumulator and Index Register X
  • XGDY - Exchange Double Accumulator and Index Register Y

Logical/bitwise operations:

  • AND1 - Logical AND (Performs bit-by-bit AND)
  • ASL12 - Arithmetic Shift Left (Same as LSL)
  • ASLD - Arithmetic Shift Left Double Accumulator (Same as LSLD)
  • ASR12 - Arithmetic Shift Right (Maintains sign bit)
  • BCLR - Clear Bit(s) in Memory
  • BSET - Set Bit(s) in Memory
  • BIT1 - Bit Test (Affects flags like AND, but doesn't effect registers)
  • CLR12 - Clear (Set all bits to zero)
  • COM12 - Complement (Inverts each bit)
  • EOR1 - Exclusive-OR (Performs bit-by-bit exclusive-OR)
  • LSL12 - Logical Shift Left (Same as ASL)
  • LSLD - Logical Shift Left Double Accumulator (Same as ASLD)
  • LSR12 - Logical Shift Right
  • ORA1 - Inclusive-OR (Performs bit-by-bit inclusive-OR)
  • ROL12 - Rotate Left through the carry bit
  • ROR12 - Rotate Right through the carry bit

Condition Code Register Operations:

  • CLC - Clear Carry (Set Carry bit to Zero)
  • CLI - Clear Interrupt Mask (Enables interrupts)
  • CLV - Clear Twos-Complement Overflow Bit
  • SEC - Set Carry (Set Carry bit to One)
  • SEI - Set Interrupt Mask (Disables/inhibits interrupts)
  • SEV - Set Twos-Complement Overflow Bit
  • TAP - Transfer Accumulator A (ACCA) to Condition Code Register
  • TPA - Transfer Condition Code Register to Accumulator A (ACCA)

Stack/Stack Pointer Operations:

  • DES - Decrement Stack Pointer (Subtract 1 from SP)
  • INS - Increment Stack Pointer (Add 2 to SP)
  • LDS - Load Stack pointer
  • PSH1 - Push ACCA or ACCB onto the stack
  • PSHX - Push Index Register X onto the stack
  • PSHY - Push Index Register Y onto the stack
  • PUL1 - Pull/Restore ACCA or ACCB from the stack
  • PULX- Pull/Restore Index Register X from the stack
  • PULY- Pull/Restore Index Register Y from the stack
  • STS - Store Stack Pointer
  • TSX - Transfer from Stack Pointer to Index Register X
  • TSY - Transfer from Stack Pointer to Index Register Y
  • TXS - Transfer from Index Register X to Stack Pointer
  • TYS - Transfer from Index Register Y to Stack Pointer

Arithmetic operations:

  • ABA - Add Accumulator B to Accumulator A
  • ABX - Add Accumulator B to Index Register X
  • ABY - Add Accumulator B to Index Register Y
  • ADC1 - Add to ACCA or ACCB with Carry
  • ADD1 - Add to ACCA or ACCB without Carry
  • ADDD - Add to Double Accumulator
  • DAA - If ABA, ADD, or ADC is performed on BCD operands, DAA converts result back to BCD
  • DEC12 - Decrement (Subtract 1)
  • DEX - Decrement Index Register X
  • DEY - Decrement Index Register Y
  • FDIV - Fractional Divide (i.e. Divides where the result will be less than zero)
  • IDIV - Integer Divide
  • INC12 - Increment (Add 1)
  • INX - Increment Index Register X
  • INY - Increment Index Register Y
  • MUL - Unsigned Multiply of ACCA and ACCB into Double Accumulator (ACCD)
  • NEG12 - Negate (Subtract from 0)
  • SBA - Subtract ACCB from ACCA
  • SBC1 - Subtract operand and carry from Accumulator (ACCA or ACCB)
  • SUB1 - Subtract from Accumulator (ACCA or ACCB)
  • SUBD - Subtract from Double Accumulator
  • Miscellaneous:

  • NOP - No Operation
  • STOP - Stop processing (If CCR is clear, processor halts completely. Can only be started through reset. If CCR is set, nothing happens.)
  • TEST - Test Operation (Test Mode Only)
  • TST12 - Test (Sets CCR flags by subtracting 0 from register/memory)

Two-pass assemblers

Our assemblers are known as two-pass assemblers. In other words, they examine our code twice in an effort to create the final machine code that is downloaded to the processor. The passes do something like the following:

  • First pass
    • Records any known address information
    • Records all labels and symbols
    • Converts opcodes and operands to machine language
    • Determines how much space each section of code will take
  • Second pass
    • fills in missing addressing information for labels and symbols
    • Produces final object code

As we discussed last week, the assembler takes as its input an assembly language file. Please review last Wednesday's notes if you don't remember the format of an assembly language file.

The assembler has two files as its output: the object file (*.S19) and the listing file (*.LST). The following part of this document describes their format.

Object file

The object file is an ASCII text file containing the machine code to be downloaded to the microprocessor. In the case of the 68HC11, this object file should be in a Motorola S-record file. There is also an Intel hex format that will not be used in this course, but is also widely used in industry.

The Motorola S-record file contains two types of records: a data record and an end record. Each one of these records is a single line within the S-record file.

Data record format

The data record contains the following pieces of data in order and without any delimiters.

  1. The text string "S1"
  2. Two-character (1 hex byte) data byte count (number of bytes from 2 address bytes to checksum inclusive = # of data bytes + 3)
  3. Four-character starting load address (2 bytes)
  4. Data byte characters
  5. Two character (1 byte) checksum (last byte of sum from address to checksum should equal $FF)
  6. Carriage return
  7. Linefeed

Below, I have presented an example of a data record that is missing two pieces of information: the data byte count and the checksum.

S1__0100235AD6FF3F4323__

There are ten bytes from 01 to checksum, therefore the data length (byte immediately after "S1") is $0A (hex for a decimal 10).

Begin calculating the checksum by adding all digits from data length to last data byte. Result should be $302. Checksum is last byte of 1's complement (bit-wise inverse) of this sum. ^$02 = ^%00000010 = %11111101 = $FD

The checksum can be verified by adding every byte after "S1". The result should have a last byte equal to $FF. In the case of this example, the sum is $3FF.

The resulting line of code should be:

S10A0100235AD6FF3F4323FD

End record format

The end record contains the following pieces of data in order and without any delimiters.

  1. ‘S9’
  2. Data byte count = ‘03’
  3. Four character start address of memory
  4. Two character checksum
  5. Carriage return
  6. Linefeed

Listing file

The listing file is a text file that provides feedback to the user on the results of assembly. It provides information on errors, assigned addresses for code and data, and resulting machine code. In general, each line of the listing file has the following format:

LLLL   AAAA   VV VV VV   Source code

where:

  • LLLL - Source code line number from your assembly language file.
  • AAAA - Assigned starting address for machine code to be located.
  • VV VV VV - Resulting machine code for this line of code. It is usually broken into individual bytes with spaces. Could be any number of bytes.
  • Source code - a copy of your line of source code including comments.
  • Listing files might also contain a symbol table. A symbol table is simply a list of the labels used by the code each followed by the symbol's assigned address.