The MC68HCS12 microcontroller contains a CISC processor, not a RISC processor.
CPU, Registers, Condition Code Bits and Addressing Modes
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 |
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
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.
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.
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]
—
[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
The following file is the source code required to print “Hello World” to the screen using assembly.