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