Archive for October, 2007

HCS12 Instruction Set

October 22, 07 by admin

The MC68HCS12 microcontroller contains a CISC processor, not a RISC processor.

Load/Store/Load Effective Address [2]

LDAA Load A (M) -> A
LDAB Load B (M)-> B
LDD Load D (M : M + 1) -> (A:B)
LDS Load SP (M : M + 1) -> (SP_h:SP_l)
LDX Load Index Register X (M : M + 1) -> X_h:X_l
LDY Load Index Register Y (M : M + 1) -> Y_h:Y_l
     
STAA Store A (A) -> M
STAB Store B (B) -> M
STD Store D (A) -> M, (B) -> M+1
STS Store SP (SP_h:SP_l) -> M : M+1
STX Store X (X_h:X_l) -> M : M+1
STY Store Y (Y_h:Y_l) -> M : M+1
     
LEAS Load effective address into SP Effective Address -> SP
LEAX Load effective address into X Effective Address ->X
LEAY Load effective address into Y Effective Address ->Y

Ex: X = $1234, Y=$1000, SP=$0A00

Instruction Result
LEAX 10,X X=$1234+$000A = $123E
LEAX $10,Y Y=$1000+$0010 = $1010
LEAS -10,SP SP=$0A00-$000A = $09F6

Stack Instructions [2]
SP is decremented before information is transferred

PSHA Push A (SP) - 1 -> SP; (A) -> M_(sp)
PSHB Push B (SP) - 1 -> SP; (B) -> M_(sp)
PSHC Push CCR (SP) - 1 -> SP; (CCR) -> M_(sp)
PSHD Push D (SP) - 2 -> SP; (A : B) -> M_(sp):M_(sp+1)
PSHX Push X (SP) - 2 -> SP; (X) -> M_(sp):M_(sp+1)
PSHY Push Y (SP) - 2 -> SP; (Y) -> M_(sp):M_(sp+1)
     
PULA Pull A (M_(sp))-> A; (SP) + 1 -> SP
PULB Pull B (M_(sp))-> B; (SP) + 1 -> SP
PULC Pull CCR (M_(sp))-> CCR; (SP) + 1 -> SP
PULD Pull D (M_(sp): M_(sp+1))-> A:B; (SP) + 2 -> SP
PULX Pull X (M_(sp): M_(sp+1))-> X; (SP) + 2 -> SP
PULY Pull Y (M_(sp): M_(sp+1))-> Y; (SP) + 2 -> SP

Increment/Decrement [2]

DEC Decrement Memory (M) - $01 -> M
DECA Decrement A (A) - $01 -> A
DECB Decrement B (B) - $01 -> B
DES Decrement SP (SP) - $0001 -> SP
DEX Decrement X (X) - $0001 -> X
DEY Decrement Y (Y) - $0001 -> Y
     
INC Increment Memory (M) + $01 -> M
INCA Increment A (A) + $01 -> A
INCB Increment B (B) + $01 -> B
INS Increment SP (SP) + $0001 -> SP
INX Increment X (X) + $0001 -> X
INY Increment Y (Y) + $0001 -> Y

Addition and Subtraction [2]

ABA Add B to A (A) + (B) -> A
ADCA Add with carry to A (A) + (M) + C -> A
ADCB Add with carry to B (B) + (M) + C -> B
ADDA Add without carry to A (A) + (M) -> A
ADDB Add without carry to B (B) + (M) -> B
ADDD Add to D (A:B) + (M:M+1) -> A:B
SBA Subtract B from A (A) - (B) -> A
SBCA Subtract with borrow from A (A) - (M) - C -> A
SBCB Subtract with borrow from B (B) - (M) - C -> B
SUBA Subtract memory from A (A) - (M) -> A
SUBB Subtract memory from B (B) - (M) -> B
SUBD Subtract memory from D (A:B) (D) - (M:M+1) -> D

[1] Fredrick M. Cady, Software and Hardware Engineering: Assembly and C Programming for the Freescale HCS12 Microcontroller
[2] Prof. Gilbert Arbez, University of Ottawa CSI3531 Course Notes, Module 4

Introduction to Assembly Language Programming

October 21, 07 by admin

Assembly programming is low-level programming using some basic syntax to represent machine code for a specific CPU. An assembler is used to translate the assembly code into the machine code for the target computer.

A program created from assemblhy can be more efficient and faster than a program created with a compiler. One example of why this occurs is because a compiler will store intermediate values used in a calculation in memory whereas a program written in assembly can store the intermediate values in a register which is much faster. However, while there may be performance benefits to programming so close to the machine level, there is a great deal of added complexity which a high-level programming language can remove.

Table 5.1 Source Code Fields [1]

Label Field Opcode Field Operand(s) Field Comment Field
Example: ldaa #$64 ; Initialize A with hexadecimal 64
  • A label must start with an alphabetic character
  • The label must start in the first column of the source code line unless it ends with a colon.
  • Uppercase and lowercase characters are distinct by default but case sensitivity may be turned off.
  • A label may end with a colon (:)
  • A label may appear on a line by itself
  • The label is optional but when used it can provide a symbolic memory reference such as a branch instruction address, or a symbol for a constant.
  • The opcode field contains either a mnemonic for the operation, an assembler directive or pseudo-operation, or a macro name. For the opcode field, the assembler will convert all uppercase letters to lowercase so this field is not case sensitive.
  • The interpretation of the operand field depends on the opcode.
  • The operand must follow the opcode.
  • Operands can be the symbols, constants, or expressions that are evaluated by the assembler.
  • The operand field also specifies the addressing mode for the instruction.

Using Macros

A macro can be used to give a symbolic name to a group of instructions that are used often. In the source code the macro should be given a name using the label field and then two commands (MACRO & ENDM) are used to indicate the start and finish of the macro. The assembler will then replace every occurrence of the macro with the set of instructions.

Add_B_and_C: MACRO
  MOV A,C
ADD A,B
MOV C,A
ENDM

Macro vs Sub-Routine

A macro and a sub-routine are similar except for the following differences:

  • With a sub-routine the code is present only once. With a macro, the code is inserted in the place of the macro whenever it is used. Therefore, if macros are used instead of sub-routines, the code will end up being longer.
  • With a sub-routine a jump must occur in order to find and execute the sub-routine code. With a macro, this is not required because the code has already been inserted directly. Therefore, a sub-routine will be slightly slower than a macro.

Using either a sub-routine or a macro makes changing the code much easier because changes can be done in one place rather than in each specific location where the code would be used. They also make the code easier to understand.

Assembler Instructions

ORG - Set the program counter to the origin of the program

EQU - Associate the value of an expression with a symbol

SET - Can be used instead of equal. With set, the value can be redefined

DC - Define a constant value

DCB - Define a constant block

DS - Define storage

Example 5-9 ORG - Set Program Counter to Origin for Absolute Assembly [1]

0000 c000 ROM: EQU $c000 ; Location of ROM
0000 0800 RAM: EQU $0800 ; Location of RAM
0000 0A00 STACK: EQU $0a00 ; Location of stack
  ;      
    ORG ROM ; Set program counter to ROM
  ;      
00c000 CF0a 00   lds #STACK ; Initialize SP
00c003 B608 00   ldaa Data_1 ; Load from memory address RAM
  ;      
    ORG RAM ; set program counter
        ; to RAM for the data
000800 Data_1: DS.B $20 ; Set aside $20 bytes

Software Development Process

When programming in any language, following a good development process is necessary in order to deliver a successful product. It is important to resist the urge to jump right into the code immediately, especially when programming in assembly which is extremely complex.

The first step should always be to clearly identify the problem and define a solution to solve that problem. Create and understand the solution in diagrams or pseudo-code before beginning to code in assembly. Translating these diagrams into code will be much easier than trying envision the solution entirely in your mind.

Debugging and tracing through your assembly program will also be a very good way at finding and correcting bugs.

Typical Bugs Include:

  • Incorrect transfer to sub-routine
  • Forgetting to initialize the stack pointer
  • Not enough memory in the stack
  • Sub-routines corrupting registers
  • Forgetting to initialize index registers
  • Modifying the condition code registers before branching
  • Using the wrong branching instruction
  • Using the wrong addressing mode

D-Bug12
The D-Bug12 is the debugging tool stored in the EEPROM of our microcontroller that will be used for debugging as well as for downloading programs onto the card and controlling the assembler.

D-Bug12 Commands

ASM - Assemble/Dissasemble

BF - Block Fill Memory

BR - Set Breakpoint

CALL - Call and execute subroutine

G - Go, run program

GT - Go till an address

HELP - Prints summary of available commands

LOAD - Set in state ready to accept program download from MiniIDE

MD - Display Contents of Memory

MM - Modify Memory

MOVE - Copy block of memory

NOBR - Remove breakpoints

RD - Display register contents

T - Trace through program

UPLOAD - Upload memory to PC

USEHDB - Use EVB/Target hardware breakpoints

VERIF - Compare memory to download file

[1] Fredrick M. Cady, Software and Hardware Engineering: Assembly and C Programming for the Freescale HCS12 Microcontroller
[2] Prof. Gilbert Arbez, University of Ottawa CSI3531 Course Notes, Module 3

CPU, Registers, Condition Code Bits and Addressing Modes

October 21, 07 by admin

CPU Registers

Accumulators A, B, and D - There are two 8-bit accumulators A and B. These accumulators can be concatenated such that the two 8-bit accumulators are treated as a single 16-bit accumulator named D. Therefore any changes to D also modify A and B. Accumulator A is the most significant octet of D. The accumulator registers are used for storing intermediate arithmetic and logic results which without accumulator registers, would need to be written to main memory after each calculation. Access to main memory is slower than access to registers, so the use of registers is very important, especially if the value calculated is to be used again immediately in the next operation.

Index Registers X and Y - The X and Y registers are both 16-bit index registers used primarily as pointers or indexed addressing. Also used by arithmetic instructions.

Stack Pointer - The stack pointer is used to make sub-routines possible. By always pointing to the last used memory location, a push command can be used to add onto the stack and a pull to retrieve the last operation. Therefore, before executing a set of instructions, the current address can be pushed to the stack and at the end of the execution of the set of instructions, the address can be pulled from the stack making it possible to return to the previous location. The stack pointer must be initialized before it can be used.

Program Counter - Programmers do not have direct control over the program counter like they do with other registers. The program counter indicates where the CPU is in its current instruction sequence.

Condition Code Register - Contains flags set by the processor during the execution of instructions.

HCS12 Condition Code Register Bits (Table 4.2 From [1])

Bits Modified by Various Instructions

Bit Flag Conditions for Setting
0 C If a carry or borrow occurs
1 V If a two’s complement overflow occurs
2 Z If the result is zero
3 N If the most significant bit of the result is set
5 H This is the half-carry bit and is set if a carry or borrow out of bit 3 of the result occurs

Bits Associated with HCS12 Control

Bit Flag Conditions for Setting
4 I Interrupt mask
6 X X interrupt mask
7 S Stop disable

HCS12 CPU[2]

C - Carry/Borrow Bit

Overflow/Underflow - The result of the addition or subtraction is too big or too small to be represented with the available bits.

The C bit is set to 1 if an addition produces and overflow or if a subtraction produces an underflow.

V - 2’s Complement Overflow

The carry bit cannot indicate an overflow for signed 2’s complement numbers. This is why the V bit is necessary for such cases. An overflow with with 2’s Complement Numbers is not possible when both numbers have different signs. To determine the value of the V bit, the following algorithm can be used:

  • Check that both numbers have the same sign
  • If the sign of the result of the addition or subtraction gives a different sign, then overflow occurred and the V bit should be set to 1.

N - Sign Bit

Takes the value of the most significant bit in the calculated result. If using signed 2’s complement, then the N bit indicates the sign of the calculated result.

Z - Zero Bit

The Z bit is set to 1 when the value of the calculated result is equal to zero.

H - Half Carry Bit

The H bit is set to 1 when a carry or borrow takes place at bit number 3 of the result.

How the CCR (Condition Code Register) Bits are Used

As explained above, the CCR bits are set to either 0 or 1 during the execution of various instructions. Branching instructions then use the values of the CCR bits in order to determine whether or not to branch.

For example, we could use “bne” (Branch if Not Equal) after doing a comparison of two numbers. The “bne” will check the value of the “Z” bit, which will be set to 1 if the result of the comparison was 0 (indicating no difference between the compared numbers meaning they are the same). So, if the Z bit = 1, no branch will occur, but if the Z bit = 0 (branch if not equal), the execution will branch.

Understanding Memory

Physical Address - The address of the memory cell

Segment Address - The location of a block of memory

Offset - The offset is a number representing the size of the segment. Given a segment address (pointing to the start of the address) the offset tells us how long the segment is so we know where it ends.

Logical Address - The logical address is the address used by the program which can then be translated into a physical address. A table will define how logical addresses are associated with physical addresses.

Effective Address - The effective address is calculated by the processor and can be either a physical or logical address.

The MC68HCS12 uses 16 bit addresses. Therefore 2^16 = 64KB address space.

Addressing Modes

Inherent - This type of addressing means that all data for the instruction is within the CPU.

Immediate Addressing - The operand is a known constant and the data immediately follows the instruction. Immediate addressing can be used to initialize registers with constants. The # sign is used to tell the assembler that immediate addressing mode is being used. The # symbol must appear before the operand. (Ex: ldaa #$32 will put hexadecimal 32 into register A)

Direct Addressing - The operand following the op code contains the address in memory. Can address an operand in the first 256 bytes of memory ($0000-$00FF).

Extended Addressing - Uses a 16-bit address to specify a location in the entire 64kb address space. (Direct addressing uses 8-bits, therefore can only address the first 256 bytes). Extended addressing instructions require 3 bytes.

Indexed Addressing - The effective address will be the sum of a 5-, 9-, or 16-bit signed constant and the contents of either the X, Y, SP, or PC register.

Table 4-6 Summary of HCS12 Indexed Operations [1]

Operand Syntax Comments
ldaa ,r 5-, 9-, or 16 bit signed constant offset
ldaa n,r n=-16 to +15 for 5-bit offset
n=-256 to +256 for 9-bit offset
n=-32768 to +32767 for 16-bit offset
r= X,Y,SP, or PC
ldaa n, -r n= 1 to 8 and is subtracted from the contents of register r before the data value is fetched
ldaa n, +r n=1 to 8 and is added to the contents of register r before the data value is fetched
ldaa n, r- n=1 to 8 and is subtracted from the contents of register r after the data value is fetched
ldaa n, r+ n=1 to 8 and is added to the contents of register r after the data value is fetched
ldaa A, r
B, r
D, r
The contents of accumulator A, B, or D are used as a 16-bit unsigned offset
ldaa [n, r] Rather than store r + n -> A, r + n will give an address, and the contents at this address will be stored in A
ldaa [D, r] Same as above, but using D instead of n

Indirect Addressing - An instruction gives an address which points to a location in memory which contains another address. This second address is the address of where the data is stored.

Relative Addressing - Relative addressing is used for branch instructions (jump instructions use extended or indexed addressing). For relative addressing, the assembler calculates the offset automatically based on the label to which we wish to branch. PC + offset = address of next instruction when branching is used.

Stack Addressing - When branching to a sub-routine (BSR), the return address is placed on the stack. The return address is the address of the instruction following the the branch instruction. At the end of the sub-routine the return instruction (RTS) will pull the return address from the stack which will update the Program Counter (PC) with the return address.

[1] Fredrick M. Cady, Software and Hardware Engineering: Assembly and C Programming for the Freescale HCS12 Microcontroller
[2] Prof. Gilbert Arbez, University of Ottawa CSI3531 Course Notes, Module 2

Computers, Microprocessors, Microcomputers, Microcontrollers

October 20, 07 by admin

NOTE: This course gives specific information and examples for the Freescale HCS12 Microcontroller.

The below image represents a computer system based on the von Neumann architecture. The HCS12 microcontroller is a von Neumann architecture machine.

Von Neumann Computer System[2]

The term microprocessor first came into use at Intel in 1972 and generally refers to the implementation of the central processor unit functions of a computer in a single, large scale integrated circuit. A microcomputer is a computer built using a microprocessor and a few other components for the memory and I/O.

A microcontroller is a microcomputer with its memory and I/O integrated into a single chip. In 2004, the industry delivered 6.8 billion microcontroller units. [1]

Important Notation: [1]

$ Hexadecimal numbers are denoted by a leading $: For example, $FFFF is the hexadecimal number FFFF. When two memory locations are to be identified, the starting and ending adresses are given as $FFFE:FFFF.

% Binary numbers are denoted by a leading %: for example, $F may be written as %1111.

@ A base-8 octal number is preceded by @: for example $F = @17

# A # indicates immediate addressing mode.

x An x indicates a don’t care bit. x could be either 0 or 1.

A computer is not some sort of mystical creature that is beyond comprehension. It is a human made device composed of basic digital logical components that anybody could design.

A typical microcontroller consists of the following elements: [1]

  • A central processor unit (CPU) that contains registers, an arithmetic and logic unit (ALU), and a sequence controller to control all activities of the microcontroller.
  • Read only memory (ROM) to hold a program and any constant data. Modern microcontrollers have reprogrammable types of read only memory such as flash memory, which is a particular type of electrically erasable programmable read only memory (EEPROM).
  • Random access memory (RAM) to store variable data.
  • An input/output (I/O) interface to connect the micrcontroller to the real world. The I/O interface in most microcontrollers contains other useful functions such as timers, pulse-width modulators, and other special I/O functions.

When a program is written in a high-level language like C or C++ it must then be converted to bytes representing the operation that must be done and the operands that are being operated upon. There is a unique code for each operation to be executed by the microcontroller. For example:

CF 0A 00

The above is an example of instruction code bytes. All addresses and instruction code bytes are in hexadecimal format which will then be translated to binary format.

In the above example, the instruction code bytes correspond to the following in Assembly code: LDS #$0A00. CF is the opcode byte for LDS and the following two bytes 0A 00 are the bytes for the operands.

A computer instruction is the combination of an operation (what the computer is to do) and zero, one, or more operands (what the computer is going to do it to). Again, given the above example, the instruction tells the microcontroller to load or initialize the stack pointer register with the value $0A00.

The central processing unit (CPU) contains registers. There are accumulator registers and registers used to access memory.

Accumulators A and B: The two 8-bit accumulators, A and B, may be a source or destination operand for 8-bit instructions. for example, if we were to retrieve a byte of data from memory we may put it in accumulator A or B. The registers are called accumulators because the results of an arithmetic or logic operation may accumulate there.

Program Counter (PC): Although the program counter is usually shown in the programmer’s model, the programmer does not have direct control over it like the other registers. The number of bits in the program counter shows how much memory can be directly addressed.

Index Register: The program may use an index register to access memory. As we will see when we find out more about the HCS12 microcontroller, there are a variety of instructions that access memory using this type of register.

The Instruction Execution Cycle [1]

The process by which the microcontroller executes each instruction in a program is called the instruction cycle. When an instruction opcode is to be fetched from ROM, the memory must be supplied with the address of the opcode and the read control signal asserted. This is how the instruction cycle starts and it continues by fetching the rest of the instruction bytes, doing whatever is required by the instruction, incrementing the program counter to point to the next opcode, and then repeats.

  • The CPU’s program counter contains the address of the first byte of the instruction to be executed. We say the program counter points to the opcode. The CPU places that address on the address bus.
  • The sequence controller asserts the Read control signal on the control bus.
  • After a small delay, called the memory access time, the ROM places the contents of the addressed memory location on the data bus.
  • The sequence controller writes this byte into the instruction decoder.
  • The instruction decoder holds the opcode byte and decodes it for the sequence controller.
  • The decoded instruction causes the sequence controller to go through a sequence of actions that complete the execution on the instruction. These include fetching operands from memory, loading registers, performing an arithmetic or logical operation on a pair of operands, and incrementing the program counter.
  • When the instruction execution is complete, the program counter is pointing to the next opcode to be fetched and executed. The instruction execution cycle then repeats.

The instruction execution cycle continues forever, or at least until the power is turned off or a special instruction that stops it is encountered.

Instruction Cycle[2]

Sequence Controller [1]

The sequence controller generates control signals required by the currently executing instruction, at the correct time, to accomplish the information transfer operation. The sequence controller is designed to allow different instructions to be executed in different amounts of time. For example, take an 8-bit immediate addressing instruction that transfers a byte from the memory immediately following the opcode byte to some register in the CPU. In the freescale HCS12 instruction set this could be the instruction that loads accumulator A with the data $22.

ldaa #$22

At the start of the instruction execution cycle, the program counter is pointing at the opcode, and the execution cycle starts by fetching the opcode. Next the program counter will be incremented, the byte will be transferred from memory to the register and the program counter will be incremented again to point to the next opcode. This process will then repeat as the execution cycle will start be fetching the next opcode, etc…

Arithmetic and Logic Unit (ALU)

The ALU contains the digital logic to operate on operands in the way specified by the opcode. It does arithmetic, logic and other operations such as shifts, rotates, increments, and decrements. The ALU receives inputs from the accumulator registers and the data bus and places its outputs to accumulator registers and the data bus. [1]

System Structure[2]

[1] Fredrick M. Cady, Software and Hardware Engineering: Assembly and C Programming for the Freescale HCS12 Microcontroller
[2] Prof. Gilbert Arbez, University of Ottawa CSI3531 Course Notes, Module 1

HelloWorld.asm

October 20, 07 by admin

The following file is the source code required to print “Hello World” to the screen using assembly.