Encoder Front Page
SRS Home | Front Page | Monthly Issue | Index
Search WWW Search seattlerobotics.org

Programming the 68HC12

You have seen some information about the 68HC12 in the SRS pages before ( An Overview of the 68HC12 ). This article is going to describe the basics of how to program the 68HC12 and some of the really great new features of the chip.

If you have programmed the 68HC11 before, then programming the 68HC12 is 'almost' easy. I say almost because its quite possible that you will completely ignore some of the new instructions and addressing modes. Make an effort not to! There are some really fine additions to the instruction set that should make your life much easier, your code smaller, and the speed faster.

A Look at the Code

This article is going to point out some coding examples that are part of the file skeleton.asm that you are free to download and modify for your own purposes. If you find nothing else useful in this article, at least you will have someone who already typed in all of the port definitions for you! I wrote the code for the 68HC812A4, but it will work equally well on the 68HC912B32.

For Starters

The EEPROM on the 68HC812A4 defaults at locations $F000-$FFFF, and the RAM locations are from $0800-$0BFF

        ORG     $F000
        ; Set the top of the stack. Using $0C00 is OK, because the 68HC12
        ; decrements BEFORE pushing. Many people set it to $BFF, but having
        ; a word aligned stack is better.
        lds     #$0C00

        ; The 68HC12, unlike the 68HC11, defaults with the COP turned on.
        ; The COP is a timer function that requires the software to
        ; periodically write a specific pair of values to a register. If
        ; this isn't done in time, the processor will reset every 1.04
        ; seconds or so.
        ; For this module, the COP is going to be disabled. The following
        ; line disables the COP by setting the COP Watchdog Rate to zero

        clr     COPCTL          ; Store zero in COP Control Register

        jsr     serial_init

        ldx     #HelloWorld
        jsr     outstr

Take special note of the Computer Operating Properly timer being disabled! This has caused a number of very experienced 68HC11 programmers to lose lots of sleep by missing this point. The COP is on by default on the 68HC12 but not on the 68HC11. Setting the stack and recognizing the COP are two extremely important features of your program.

Serial Output

The 68HC812A4 has 2 serial ports, which is mighty nice. Each port has its own set of control registers and data registers, so you can setup each port to operate in an independent mode. Skeleton.asm defines a serial_init routine, and to output functions

        ; To use the serial port, one must set the baud rate. The 68HC12
        ; is capable of speeds up to 38400. Here, we set it to 9600
        ; baud. The value here is a 16 bit divisor.
        ldd     #52     ; Value from Baud Rate Generation Table
                        ; Other good ones are 26 for 19200, or 13 for 38400
        std     SC0BD

        ldaa    #$0C    ; Enable transceiver
        staa    SC0CR2

The serial ports are numbered 0 and 1. The above example is setting up serial port 0. To setup port 1, use the SC1BD and SC1CR2 registers. Be sure to enable the Receiver and Transmitter (transceiver) by setting $0C into the control register. HC11 programmers beware, the HC11's value is $C0 which is different than $0C (uh, er, yeah, it took me about an hour to find that bug!).

; putchar outputs a character to serial port 0
; Call with character in register A
        brclr   SC0SR1, #$80 putchar
        staa    SC0DRL

The SC0SR1 register holds the current status. This routine loops waiting for the previous character to be sent.

; outstr outputs a NULL terminated character string
; On input, register X points to string in memory. Note the use of
; the index auto post increment addressing mode!
      jsr       putchar
      ldaa      1,X+            ; Auto increment X by one
      bne       outstr0         ; If not a NULL character, send it

outstr demonstrates one of the really great new addressing modes: indexed auto-post-increment. There is also auto-pre-increment. The instruction

ldaa 1,X+

loads the byte value pointed to by X, then automatically adds 1 to the X register. You can add or subtract up to 8 using this method. For examples:

ldaa	5,X+	; Load A from [X], add 5 to X
ldaa	8,X-	; Load A from [X], subtract 8 from X
staa	1,Y+	; Store at [Y], add 1 to Y
ldaa	5,+X	; Add 5 to X, Load A from [X] (Pre-Increment)

The MOV instructions

On a machine that has basically only 3 working registers (D,X,Y) you often find yourself resource bound when trying to do more complex tasks. You always feel that if you had one more register, life would be so much better. While the 68HC12 didn't add any more general registers, it did add a couple of instructions to make your life easier. They are the MOV instructions. These move instructions allow you to move values directly between memory locations. You can also move immediate values directly. This means you don't have to trash a register to do a simple operation. Here are some examples.

f00f 18 0c 08 00 08 01          movb    Byte1,Byte2
f015 18 04 08 02 08 04          movw    Word1,Word2

f01b b6 08 00                   lda     Byte1           ; Compare to the movb instruction
f01e 7a 08 01                   sta     Byte2

                   ; You can also move an immediate value, which is handy. 

f021 18 0b 12 08 00             movb    #$12,Byte1
f026 18 03 ff 11 08 02          movw    #$FF11,Word1

Note that I put in the listing file this time to show you the byte counts. The first line is a movb, which moves a byte value directly from one memory location to another. No working register required. I put in the listing file output to demonstrate that while you didn't use a register, you also didn't save any space! As you can see, the lda Byte1, sta Byte2 combination requires 6 bytes of memory. The movb instruction also required 6 bytes of instruction space. However, if you had needed to preserve the contents of the A register, then you actually save 2 bytes because the PSHA and PULA instructions are no longer required.

To really see the power of the MOV instruction, one only needs to look at a memory copy routine. The MOV instructions, in combination with the Post-Increment form of the indexed addressing, make extremely short work of memcpy

; memcpy
;       D       Number of bytes to copy: Must be at least 1
;       X       Source address
;       Y       Destination Address
;               Trashes D, X, Y
        movb    1,x+ 1,y+
        dbne    D,memcpy
; strcpy
;       X       Source address of null terminated string
;       Y       Destination address of string
        movb    0,x 1,y+        ; Move the byte,
        tst     1,x+            ; Test the source against NULL
        bne     strcpy

See how memcpy uses the movb instruction with a post-increment version of index addressing. It then uses a new instruction called Decrement Branch Not Equal (DBNE). This does a decrement of a register (D in this case), and a branch instruction.

strcpy uses the same techniques, with a slight twist. The increment on the X register is done on a TST instruction rather than the MOVB instruction.

Indexed on X,Y,SP,PC

The 68HC11 has the ability to do indexed addressing on two registers, X and Y. The 68HC12 can do indexed addressing on all 4 registers, X, Y, SP, and PC. In addition, the indexed addressing on the 68HC12 is very feature rich, allowing for auto increments, and variable sized offsets. We have already seen examples of indexed addressing with post-auto-increments in the memory move instructions. Another useful thing to do is to use SP as the index register.

When writing a subroutine, it is often extremely useful to use some local variables. On the 68HC11, this can often be a pain because you have to either dedicate one of the index registers (X or Y) to the task of being a local variable pointer, or you have to do a convoluted set of instructions to pick them up. Often times, you end up just using global variables.

The 68HC12 helps out by allowing you to index onto the stack using an offset and the SP register. For example,


DelayCountL	equ	2
DelayCountH	equ	1
DelayCountW	equ	1	; Address of the WORD DelayCount
NextNumber	equ	0

	leas	-3,sp		; make room for 3 bytes of variables

	ldaa	#9
	staa	NextNumber,sp	; Here we can address our variables
				; using an offset from SP
	; Setup delay loop
	movw	#$FFFF DelayCountW,sp

	dec	DelayCountL,sp	; Address is SP+2, which is the low byte of
	bne	NumLoop1	; the WORD sized counter.
	dec	DelayCountH,sp
	bne	NumLoop1

PrintSomeNumbers defines 3 bytes of variables. Local variable names are defined using equates. The equates stand for the offset onto the stack. DelayCount is a WORD. The variables define the WORD, and the offsets of the high and low bytes. In addition, another variable is defined, NextNumber. If you do the substitutions with the equates, on the dec DelayCountL,sp instruction for example, you end up with an instruction that looks like:

	dec	2,sp

The effective address for this instruction calculated by adding offset 2 to the current value of SP. The effective address is the pointer into memory of the operand for the instruction. If SP currently equals 0B00, then the EA (Effective Address) of this instruction would be 0B02. The offset values can range from -32768 to + 32767.

The LEA instruction

Speaking of effective addresses, yet another extremely useful addition to the group is the LEA instructions. The LEA instruction loads the effective address into a register. There are three such instructions: LEAS, LEAX, LEAY. The LEA instructions add an extremely flexible way of doing pointer manipulation, and also for doing some great math tricks. Using the same values as the above example, with the current SP equal to 0B00, then

	leax	2,sp

would result in the value 0B02 being loaded into the X register. Note that the addition here did NOT change the condition registers. This is an important point to remember. It can be viewed as both useful and a drag. Useful because the condition registers are not changed, allowing you to stick a LEA instruction in the middle of another calculation. It might be viewed as a drag because it stops you just short of having a whole other set of math instructions. I find it extremely useful most of the time.

One of my only gripes with the 68HC11 is the lack of instructions for adding to the X or Y registers. The LEA instruction helps to solve this limitation by allowing me to add values to X or Y, and to store them elsewhere. For example,

        leax    -127,x          ; Subtract 127 from X
        leax    45789,x         ; Add 45789 to X

        leay    128,x           ; Add 128 to X, store it in Y
        leax    -14,sp          ; Subtract 14 from SP, store in X

Reset Vectors

The last important point is the number of reset vectors. There are quite a few more than the 68HC11. Two things to note:

  1. I defined vec_Unexpected to be a little routine that does a BGND instruction. If you are running with Background Debug Mode active, then the BGND will cause the CPU to break into the debugger. If not, the BGND instruction is ignored and...
  2. I use an index indirect jmp to jump through the reset vector.
; Interrupt vectors. When the CPU starts, or encounters an interrupt, it
; will read this table to determine where to jump to in the code.
; Note: If you are using the 5G18E pre production mask, there was an
; error in the addressing of the interrupt vectors. Get a newer part, as
; there were plenty of other errors on that chip as well.

	ldx	_int_Reset
	jmp	[0,x]

	; Must start at this specific address

	ORG     $FFCE

_int_Key_Wakeup_H           dw      vec_Unexpected
_int_Key_Wakeup_J           dw      vec_Unexpected
_int_ATD                    dw      vec_Unexpected
_int_SCI1                   dw      vec_Unexpected
_int_SCI0                   dw      vec_Unexpected
_int_SPI_STC                dw      vec_Unexpected
_int_PAIE                   dw      vec_Unexpected
_int_PAO                    dw      vec_Unexpected
_int_Timer_Overflow         dw      vec_Unexpected
_int_Timer_7   		    dw      vec_Unexpected
_int_Timer_6   		    dw      vec_Unexpected
_int_Timer_5           	    dw      vec_Unexpected
_int_Timer_4                dw      vec_Unexpected
_int_Timer_3                dw      vec_Unexpected
_int_Timer_2                dw      vec_Unexpected
_int_Timer_1                dw      vec_Unexpected
_int_Timer_0                dw      vec_Unexpected
_int_Real_Time_Int	    dw      vec_Unexpected
_int_IRQ_Key_Wakeup_D       dw      vec_Unexpected
_int_XIRQ                   dw      vec_Unexpected
_int_SWI                    dw      vec_Unexpected
_int_UIT                    dw      vec_Unexpected
_int_COP_Failure            dw      vec_Unexpected
_int_COP_Clock_Monitor_Fail dw      vec_Unexpected
_int_Reset                  dw      Start



The 68HC12 is a great CPU. I look forward to doing more and more with it in the future. The new instructions, such as MOV and LEA, in combination with all of the new indexed addressing modes, will make the programmers life much more pleasant. There are many other great enhancements to the instruction set that you should look into yourself.

If you are an old hand at the 68HC11, you will find programming the 68HC12 just as easy. Do yourself a big favor though, be sure to give the CPU12 Reference manual at least the once over. You will find solutions to many of those old HC11 hacks.


List Of Tools and Documentation

Here are links and instructions for getting 68HC12 resources


as12 assembler is available from Karl Lunt, and is the freeware assembler for the HC12 family.

SBasic is Karl Lunts version of BASIC for the 68HC12. This is a good high level language.

Robotic and Microcontrollers Products is my page that allows you to order the SRS 68HC812A4 Development board, as well as my BDM12 interface board, and the new 68HC912B32 development board.

Imagecraft has a great C compiler for the 68HC12. The compiler runs around $150, but is a great bargin if you would like to write software in C for the HC12.

Documentation (online versions)

You can get online versions of the 68HC12 documentation from Motorolas website. Here are the files to grab. You can also get printed versions as described below.

CPU12RM-AD Index is an index to a series of .PDF files that create the Motorola CPU12 Reference Manual. This describes the instruction set and programming model for the chip. It is definitely a must have. There is also an Online Version at CPU12RM-AD or http://www.mcu.motsps.com/lit/manuals/cpu12/outline1.html

a4.pdf is the .PDF file of the 68HC812A4 Technical Summary that contains information specific to the 68HC812A4 version of the chip. This is the one with 4k of EEPROM, 1k RAM, and tons of I/O. This explains all of the systems on the chip, where the registers are, etc. You should definitely have one of these if you are using the 68HC812A4.

(250K bytes)
is a PDF file with the electrical characteristics of the 68HC812A4. Timing diagrams, electrical capacities and absolutes, accuracies, etc.

b32ts.pdf is the .PDF file of the 68HC912B32 Technical Summary that contains information specific to the 68HC912B32 version of the chip. This is the one with 32k of Flash, 768 bytes EEPROM, 1k RAM, and still lots of I/O. This explains all of the systems on the chip, where the registers are, etc. You should definitely have one of these if you are using the 68HC912B32.

(272K bytes)
is a PDF file with the electrical characteristics of the 68HC912B32. Timing diagrams, electrical capacities and absolutes, accuracies, etc.

Printed Versions

Motorola will also allow you to order printed documentation online at http://design-net.com/home2/lit_ord.html where you will find a form to fill out your name and address, then a spot on the bottom for document numbers. The following are the documents that you might be interested in

CPU12RM/AD The CPU12 Reference Manual (Must Have)
MC68HC812A4EC/D 68HC812A4 Electrical Characteristics
MC68HC812A4PP/D 68HC812A4 Product Preview (Not worth the effort!)
MC68HC812A4TS/D 68HC812A4 Technical Summary (Must Have)
MC68HC912B32ECD 68HC912B32 Electrical Characteristics
MC68HC912B32PPD 68HC912B32 Product Preview (Not worth the effort!)
MC68HC912B32TSD 68HC912B32 Technical Summary (Must Have)