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.
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.
The EEPROM on the 68HC812A4 defaults at locations $F000-$FFFF, and the RAM locations are from $0800-$0BFF
ORG $F000 Start: ; ; 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.
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
serial_init: ; ; 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 rts
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 ; putchar: brclr SC0SR1, #$80 putchar staa SC0DRL rts
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! ; outstr0: jsr putchar outstr: ldaa 1,X+ ; Auto increment X by one bne outstr0 ; If not a NULL character, send it rts
outstr demonstrates one of the really great new addressing modes: indexed auto-post-increment. There is also auto-pre-increment. The instruction
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 Xldaa 8,X- ; Load A from [X], subtract 8 from Xstaa 1,Y+ ; Store at [Y], add 1 to Yldaa 5,+X ; Add 5 to X, Load A from [X] (Pre-Increment)
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 memcpy: movb 1,x+ 1,y+ dbne D,memcpy rts
;********************************************************** ; strcpy ; X Source address of null terminated string ; Y Destination address of string strcpy: movb 0,x 1,y+ ; Move the byte, tst 1,x+ ; Test the source against NULL bne strcpy rts
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.
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,
PrintSomeNumbers: 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 NumLoop1: 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:
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.
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
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
The last important point is the number of reset vectors. There are quite a few more than the 68HC11. Two things to note:
;************************************************************************* ; ; 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. ; vec_Unexpected: bgnd 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.
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.
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.
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)