Developing Software in Assembly Language
Overview
By Jonathan W. Valvano

     This article, which discusses assembly language programming, accompanies the book Embedded Microcomputer Systems: Real Time Interfacing published by Brooks-Cole 1999. This document has four overall parts
     Overview (this document)
     Syntax (fields, pseudo ops)
     Local variables
     Examples


     
Writing assembly language software is similar to other software development processes. For general information on the process of software development see Chapter 2 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano. Again, this article will focus on assembly language developing using the TExaS application. There are two types of commands that exist in our assembly source code. Op codes are regular instructions that translate into machine code to be executed by the computer when the program runs. We use pseudo-op codes in our source code to give instructions to the assembler itself. The following lists the default color settings of the TExaS editor.
     The labels are shown in purple
     
The op codes are shown in blue
     
The pseudo-op codes are shown in gray
     
The numbers are shown in dark blue
     
The strings are shown in magneta
     
The operands are shown in black
     The comments are shown in green
     
The assembly errors are shown in red

--------------------------------------------------------------------------------------
Case Study: Microcomputer-Based Lock
      
To illustrate the software development process, we will implement a simple digital lock. The lock system has 7 toggle switches and a solenoid as shown in the following figure. If the 7-bit binary pattern on Port A bits 6-0 becomes 0100011 for at least 10 ms, then the solenoid will activate. The 10 ms delay will compensate for the switch bounce. For information on switches and solenoids see Chapter 8 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano. For now what we need to understand is that Port A bits 6-0 are input signals to the computer and Port A bit 7 is an output signal.
      

Before we write assembly code, we need to develop a software plan. Software development is an iterative process. Even though we list steps the development process in a 1,2,3... order, in reality we iterative these steps over and over.
      1) We begin with a list of the inputs and outputs. We specify the range of values and their significance. In this example we will use PORTA. Bits 6-0 will be inputs. The 7 input signals represent an unsigned integer from 0 to 127. Port A bit 7 will be an output. If PA7 is 1 then the solenoid will activate and the door will be unlocked. In assembly language, we use equ pseudoops to assign a symbolic names, PORTA DDRA, to the corresponding addresses of the ports, $0000 $0002.
PORTA  equ   $0000   ; PA6-PA0 switches, PA7 solenoid lock
DDRA   equ   $0002   ; specifies input or output

      2) Next, we make a list of the required data structures. Data structures are used to save information. If the data needs to be permanent, then it is allocates in global space. If the software will change its value then it will be allocated in RAM. In this example we need a 16-bit unsigned counter. We use the org pseudoop to place the data structures in RAM, $0800. The rmb pseudoop reserves bytes for the structure. These lines also assign the symbolic name CNT to the corresponding address of the information $0800.
cnt    rmb   2       ; 16-bit counter
      
 org   $F000   ; EEPROM

If data structure can be defined at assembly time and will remain fixed, then it can be allocated in EEPROM. In this example we will define an 8 bit fixed constant to hold the key code, which the operator needs to set to unlock the door. We will place these lines directly after the program so that they will be defined in ROM or EEPROM memory. The fcb pseudoop defines an 8-bit constant. This line also assigns the symbolic name key to the corresponding address of the information.
key    fcb   %00100011    ; key code

It is not real clear at this point exactly where in EEPROM this constant will be, but luckily for us, the assembler will calculate the exact address automatically. After the program is assembled, we can look at the line in the listing file or in the symbol table to see where in memory each structure is allocated.
      3) Next we develop the software algorithm, which is a sequence of operations we wish to execute. There are many approaches to describing the plan. Experienced programmers can develop the algorithm directly in assembly language. On the other hand, most of us need an abstractive method to document the desired sequence of actions. Flowcharts, pseudo code, and high-level language code are three common descriptive formats. The TExaS application is unique in regards that if you draw the flowchart on the computer, you can paste it directly into the program as a comment. There are no formal rules regarding pseudo code, rather it is a shorthand for describing what to do and when to do it. We can place our pseudo code as documentation into the comment fields of our program. The following shows a flowchart on the left and pseudo code and C code on the right for our digital lock example.


Normally we place the programs in ROM or EEPROM. On the MC68HC812A4 EEPROM begins at $F000, so we put the following org pseudoop before the program.
      org   $F000   ; EEPROM

Even though this program will not use the stack, we will initialize the stack pointer to the last location of RAM. On the 6812, we execute the following instruction to initialize the stack.
main  lds   #$0C00
Next we write assembly code to implement the algorithm as illustrated in the above flowchart and pseudo code. In step 1), we initialize Port A so that PA7 is an output and PA6-PA0 are inputs. For information on ports and direction registers see Chapter 1 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano.
      movb  #$80,DDRA   ; PA6-PA0 input, PA7 output

In step 2), we turn off the solenoid.
      bclr  PORTA,#$80  ; disable solenoid lock

In step 3), we initialize the counter to 4000, which is the number of loops required to wait 10 ms.
      movw  #4000,cnt  ; 10,000,000ns/(125*20)

In step 4) we implement the indefinite loop. We place an assembly label at the program locations to which we wish to branch. The bra instruction is an unconditional branch.
loop
      bra   loop

Inside the indefinite loop we test to see if the switch pattern matches the key code. In this implementation we branch to off if the switches do not match the key code. If they do match we will execute the instruction immediately after the bne off.
loop   ldaa  PORTA   ; [3] input from 7 switches
       
anda  #$7F    ; [1]
       
cmpa  key     ; [3] match key code?
       
bne   off     ; [3]

If the switches match the key code, then the 16-bit counter is decremented.
       
ldx   cnt     ; [3]
       
dex           ; [1]
       
stx   cnt     ; [3]

If the counter becomes zero, then the door is unlocked. The bne instruction will go to loop if cnt is not equal to zero. The bset instruction will set PA7 to 1.
       
bne   loop    ; [2]=20 cycles/loop
       
bset  PORTA,#$80   ; enable solenoid lock
       
bra   loop 

If the switches do not match the key code, then the solenoid is turned off and the cnt set back to 4000.
off    movw  #4000,cnt    ; 10,000,000ns/(125*20)
       bclr  PORTA,#$80   ; disable solenoid lock
       
bra   loop   

We put the above pieces together to create the source code. The order of the instructions is very important because it determines the sequence of execution. The last two lines will define where the computer will start execution after a reset.
; ******lock.rtf************
; activate solenoid (PA7=1) if switches match key code
; switch bounce is less than 10 ms
PORTA  equ   $0000   ; PA6-PA0 switches, PA7 solenoid lock
DDRA   equ   $0002   ; specifies input or output
       org   $0800   ; RAM
cnt    rmb   2       ; 16-bit counter
      
 org   $F000   ; EEPROM
main   lds   #$0C00
      
 movb  #$80,DDRA   ; PA6-PA0 input, PA7 output
       bclr  PORTA,#$80  ; disable solenoid lock
       
movw  #4000,cnt   ; 10,000,000ns/(125*20)
loop   ldaa  PORTA   ; [3] input from 7 switches
       
anda  #$7F    ; [1]
       
cmpa  key     ; [3] match key code?
       
bne   off     ; [3]
       
ldx   cnt     ; [3]
       
dex           ; [1]
       
stx   cnt     ; [3]
       
bne   loop    ; [2]=20 cycles/loop
; 7 switches match key code for more than 10 ms
       
bset  PORTA,#$80   ; enable solenoid lock
       
bra   loop   
off    movw  #4000,cnt    ; 10,000,000ns/(125*20)
       bclr  PORTA,#$80   ; disable solenoid lock
       
bra   loop   
key    fcb   %00100011    ; key code
       
org   $FFFE
       
fdb   main

   4) The last stage is debugging. You can run this example on the TExaS simulator by loading the files lock.rtf, lock.uc and lock.io. There are versions for all four microcomputers, located in the MC6805, MC6808, MC6811 and MC6812 directories. For information on debugging see Chapter 2 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano.

--------------------------------------------------------------------------------------
Assembler Basics
     Assemblers are programs that process assembly language source program statements and translate them into executable machine language object files. Cross assemblers (such as TExaS) allow source programs written and edited on one computer (the host) to generate executable code for another computer (the target). In our case the target is microcomputer simulator, but since TExaS generates S19 records, you could download the S19 file onto a real computer for execution.
     The symbolic language used to code source programs to be processed by the Assembler is called assembly language. The language is a collection of mnemonic symbols representing: operations (i.e., machine instruction mnemonics or directives to the assembler), symbolic names, operators, and special symbols. The assembly language provides mnemonic operation codes for all machine instructions in the instruction set. The instructions are defined and explained in the Programming Reference Manual (for the 6805, 6808, 6811 or 6812), which can be found in the pdf directory of the CD. Printed copies of these Motorola manuals can be obtained from the Motorola Literature Center. A brief overview of each instruction and example usage can be found by searching the Contents page of the help engine included with the TExaS application. The assembly language also contains mnemonic directives that specify auxiliary actions to be performed by the Assembler. These directives are not always translated into machine language. Many pseudo-op codes are supported by this assembler.

 

     The TExaS assembler is a two-pass assembler. During the first pass, the source program is read to develop the symbol table. During the second pass, the object file is created (assembled) using the symbol table developed in pass one. It is during the second pass that the source program listing is also produced. The symbol table is recreated in the second pass. A Phasing Error occurs if the symbol table values calculated in the two passes are different.
      Errors that occur during the assembly process (e.g., undefined symbol, illegal op code, branch destination too far, etc.) are explained in the listing file (I shortened the comments so each line would fit on the page).
Copyright 1999-2000 Test EXecute And Simulate - Version 1.00
                                           ; ******lock.rtf*******
                                           ; activate solenoid (PA7=1)
                                           ; bounce is less than 10 ms
$0000                                      PORTA equ  $0000
$0002                                      DDRA  equ  $0002
$0800                                            org  $0800
$0800                                      cnt   rmb  2
$F000                                            org  $F000
$F000 CF0C00          [ 2](  0){OP        }main  lds  #$0C00
$F003 180B800002      [ 4](  2){OPwP      }      movb #$80,DDRA
$F008 4D0080          [ 4](  6){rPOw      }off   bclr PORTA,#$80
$F00B CC115C          [ 2]( 10){OP        }look  ldd  #4444
$F00E 7C0800          [ 3]( 12){WOP       }      std  cnt
$F011 9600            [ 3]( 15){rfP       }loop  ldaa PORTA 
$F013 847F            [ 1]( 18){P         }      anda #$7F
$F015 B1F028          [ 3]( 19){rOP       }      cmpa key
$F018 26EE            [ 3]( 22){PPP/P     }      bne  off
$F01A FE0800          [ 3]( 25){ROP       }      ldx  cnt
$F01D 09              [ 1]( 28){O         }      dex 
$F01E 7E0800          [ 3]( 29){WOP       }      stx  cnt
$F021 26EE            [ 3]( 32){PPP/P     }      bne  loop
                                           ; 7 switches match key code
$F023 4C0080          [ 4]( 35){rPOw      }      bset PORTA,#$80
$F026 20E3            [ 3]( 39){PPP       }      bra  look
$F028 23                                   key   fcb  %00100011
$FFFE                                            org  $FFFE
$FFFE F000                                       fdb  main
                                           
***************Symbol Table*********************
DDRA         $0002 
PORTA        $0000 
cnt          $0800 
key          $F028 
look         $F00B 
loop         $F011 
main         $F000 
off          $F008 
Assembly successful

     The source code is a file of ASCII characters usually created with an editor. Each source statement is processed completely before the next source statement is read. As each statement is processed, the Assembler examines the label, operation code, and operand fields. The operation code table is scanned for a match with a known opcode. During the processing of a standard operation code mnemonic, the standard machine code is inserted into the object file. If an assembler directive is being processed, the proper action is taken.
     Any errors that are detected by the Assembler are displayed after the actual line containing the error is printed. If no source listing is being produced, error messages are still displayed in the TheLog.RTF to indicate that the assembly process did not proceed normally. Object code is the binary values (instructions and data) that, when executed by the computer, perform the intended function. The listing file contains the address, object code, and a copy of the source code. You can optionally include the number of cycles to execute the instruction, a running total of cycles, and explicit details of the cycles types required to execute the instruction. The listing file also provides a symbol table describing where in memory the program and data will be loaded. The symbol table is a list of all the names used in the program along with the values. A symbol is created when you put a label starting in column 1. Examples of this type are DATA, CNT, Start, Rep, Inp, NAME and SIZE. The symbol table value for this type is the absolute memory address where the instruction, variable or constant will reside in memory. The second type of label is created by the equ pseudo-op. The value for this type of symbol is simply the number specified in the operand field. When the assembler processes an instruction with a symbol in it, it simply substitutes the fixed value in place of the symbol. Therefore we will use symbols to clarify (make it easier to understand) our programs. The symbol table for this example is
***************Symbol Table*********************
      DDRA     $0002
      PORTA    $0000
      cnt      $0800
      key      $F033
      loop     $F011
      main     $F000
      off      $F028

     A compiler converts high level language source code into object code. A cross-compiler also converts source code into object code and creates a listing file except that the object code is created for a target machine that is different from the machine running the cross-compiler. The TExaS is a cross-assembler because it runs on an Intel computer but creates 6805/6808/6811/6812 object code. Hiware and ImageCraft's ICC11 and ICC12 include both a cross-assembler and a cross-compilers because they run on the PC and create 6805/6808/6811/6812 object code. For general information on C language software development see Chapter 2 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano.

     Motorola uses S-records to store object code. The 16 bit values are stored in memory with the most significant byte first. For more information about the specific format of the S-record see S19 records contain object code. The S-record for the above example is as follows. The actual object code is colored in blue.

S121F000CF0C00180B8000024D008018030FA008009600847FB1F033260EFE0800091F
S119F01E
7E080026EE4C008020E918030FA008004D008020DE23A9
S105FFFE
F0000D
S903F0000C

--------------------------------------------------------------------------------------
Running on an Evaluation Board
     
When debugging involves controlling real hardware, we must use an actual microcomputer running in real time. Because the programs are stored in ROM or EEPROM on the single chip microcomputer it is difficult to debug. To solve this problem there are evaluation boards that run the microcomputer in expanded mode and include a debugger. With this development system, we also use a cross-assembler or a cross-compiler to convert our source programs into a listing file and object file. The object file is transmitted via the serial port to the evaluation board and loaded into RAM. A simple debugger (like the 6811 EVB using the BUFFALO debugger) allows us to test our software. Typical debugger features include:
     start execution at a specific address
     set breakpoint at a specific address
     read/modify microcomputer registers
     read/modify microcomputer I/O ports
     read/modify RAM
     read/erase/program EEPROM
     load object code into RAM received from the HOST PC.
Sophisticated development environments integrate the editor, compiler, assembler, serial port communication and debugger into a single application running on the PC. For more information on software development see Chapter 2 of Embedded Microcomputer Systems: Real Time Interfacing by Jonathan W. Valvano. For information about hardware development platforms, see the Lab Manual that accompanies this book.

--------------------------------------------------------------------------------------
Simulation
     
When developing software using a simulator, we use a cross-assembler or a cross-compiler running on a PC to convert our source programs into a listing file and object file. We then "run" our program on a simulator that emulates microcomputer with its external component. Simulation in this environment is more difficult than other computer application because of the software execution is tightly coupled to (extremely dependent on) the hardware. Simulation of an embedded system is only effective if all the software, computer, external mechanical, and external electrical components are modeled. Another complicating issue is the real time nature of the external mechanical and electrical devices. The following figure outlines the software development process using a simulator.

Additional topics include.
    Assembly Language Syntax
    Label Field
    Operation Field
    Operand Field
    Comment Field
    Assembly listing gives results of the assembly
    Assembly errors lists the types of assembly errors
    Assembly pseudo-op's are assembly-time operations
    S19 records contain object code

For a detailed explanation of the instructions and their addressing modes, see the help system with the TExaS application. Sometimes the best way to learn a new technique is to observe examples. See the ReadMe.txt file for a list of examples that can be run on the simulator. There are many assembly language examples included with the TExaS application. Some simple 6812 examples are included in this document.

This document has four overall parts
     Overview (this document)
     Syntax (fields, pseudo ops)
     Local variables
     Examples