; ; Skeleton 68HC812A4 program. ; ; The intent of this file is to get a program up and running on ; an 68HC812A4 microcontroller. It provides a skeleton by which ; you can create your own programs. It also demonstrates some ; of the really cool features of the HC12, including some of the ; new instructions and the new addressing modes. ; ; History: ; 10-07-97 KevinRo@nwlink.com ; Initial version written. ; ; ; The following are port definitions. You can save them to a ; file and use them in your own programs. I recommend using a ; file called hc12regs.inc that you can include with the following ; ; #include hc32regs.inc ; ; I stuck them into this file to keep the number of files ; low ; ;****************************************************************** PORTA equ $00 ;Port A Data PORTB equ $01 ;Port B Data DDRA equ $02 ;Port A Data Direction DDRB equ $03 ;Port B Data Direction PORTC equ $04 ;Port C Data PORTD equ $05 ;Port D Data DDRC equ $06 ;Port C Data Direction DDRD equ $07 ;Port D Data Direction PORTE equ $08 ;Port E Data DDRE equ $09 ;Port E Data Direction PEAR equ $0A ;Port E Assigment MODE equ $0B ;Mode PUCR equ $0C ;Pull Up Control RDRIV equ $0D ;Reduced Drive INITRM equ $10 ;RAM Position INITRG equ $11 ;Register Position INITEE equ $12 ;EEPROM Position MISC equ $13 RTICTL equ $14 ;Real Time Interrupt Control RTIFLG equ $15 ;Real Time Interrupt Flag COPCTL equ $16 ;COP Control COPRST equ $17 ;Arm/Reset COP Timer ITST0 equ $18 ;Internal Test 0 ITST1 equ $19 ;Internal Test 1 ITST2 equ $1A ;Internal Test 2 ITST3 equ $1B ;Internal Test 3 INTCR equ $1E ;Interrupt Control HPRIO equ $1F ;Highest Priority Interrupt KWIED equ $20 ;Key Wakeup Port D Interrupt Enable KWIFD equ $21 ;Key Wakeup Port D Flag RES22 equ $22 ;Reserved on 812A4 RES23 equ $23 ;Reserved on 812A4 PORTH equ $24 ;Port H Data DDRH equ $25 ;Port H Data Direction KWIEH equ $26 ;Key Wakeup Port H Interrupt Enable KWIFH equ $27 ;Key Wakeup Port H Flag PORTJ equ $28 ;Port J Data DDRJ equ $29 ;Port J Data Direction KWIEJ equ $2A ;Key Wakeup Port J Interrupt Enable KWIFJ equ $2B ;Key Wakeup Port J Flag KPOLJ equ $2C ;Key Wakeup Port J Polarity PUPSJ equ $2D ;Key Wakeup Port J Pull-up / Pull-down Select PULEJ equ $2E ;Key Wakeup Port J Pull-up / Pull-down Enable RES2F equ $2F ;Reserved on 812A4 PORTF equ $30 ;Port F Data PORTG equ $31 ;Port G Data DDRF equ $32 ;Port F Data Direction DDRG equ $33 ;Port G Data Direction DPAGE equ $34 ;Data Page PPAGE equ $35 ;Program Page EPAGE equ $36 ;Extra Page WINDEF equ $37 ;Window Definition MXAR equ $38 ;Memory Expansion Assignment RES39 equ $39 ;Reserved on 812A4 RES3A equ $3A ;Reserved on 812A4 RES3B equ $3B ;Reserved on 812A4 CSCTL0 equ $3C ;Chip Select Control 0 CSCTL1 equ $3D ;Chip Select Control 1 CSSTR0 equ $3E ;Chip Select Stretch 0 CSSTR1 equ $3F ;Chip Select Stretch 1 LDV equ $40 ;Loop Divider LDVL equ $41 ;Loop Divider Low Byte RDV equ $42 ;Reference Divider RVDL equ $43 ;Reference Divider Low Byte CLKCTL equ $47 ;Clock Control ; $48-$5F are all reserved on the 812A4 ATDCTL0 equ $60 ;Reserved ATDCTL1 equ $61 ;Reserved ATDCTL2 equ $62 ;ATD Control 2 ATDCTL3 equ $63 ;ATD Control 3 ATDCTL4 equ $64 ;ATD Control 4 ATDCTL5 equ $65 ;ATD Control 5 ATDSTAT equ $66 ;ATD Status ATDSTATL equ $67 ;ATD Status Low Byte ATDTEST equ $68 ;ATD Test ATDTESTL equ $69 ;ATD Test Low Byte PORTAD equ $6F ;Port AD Data Input ; $6A - $6E are all reserved on the 812A4 ADR0 equ $70 ;A/D Converter Result0 RES71 equ $71 ;Reserved on 812A4 ADR1 equ $72 ;A/D Converter Result 1 RES73 equ $73 ;Reserved on 812A4 ADR2 equ $74 ;A/D Converter Result 2 RES75 equ $75 ;Reserved on 812A4 ADR3 equ $76 ;A/D Converter Result 3 RES77 equ $77 ;Reserved on 812A4 ADR4 equ $78 ;A/D Converter Result 4 RES79 equ $79 ;Reserved on 812A4 ADR5 equ $7A ;A/D Converter Result 5 RES7B equ $7B ;Reserved on 812A4 ADR6 equ $7C ;A/D Converter Result 6 RES7D equ $7D ;Reserved on 812A4 ADR7 equ $7E ;A/D Converter Result 7 RES7F equ $7D ;Reserved on 812A4 TIOS equ $80 ;Timer Input Capture/Output Compare Select CFORC equ $81 ;Timer Compare Force OC7M equ $82 ;Output Compare 7 Mask OC7D equ $83 ;Output Compare 7 Data TCNT equ $84 ;Timer Counter TCNTL equ $85 ;Timer Counter Low Byte TSCR equ $86 ;Timer System Control TQCR equ $87 ;Reserved TCTL1 equ $88 ;Timer Control 1 TCTL2 equ $89 ;Timer Control 2 TCTL3 equ $8A ;Timer Control 3 TCTL4 equ $8B ;Timer Control 4 TMSK1 equ $8C ;Timer Interrupt Mask 1 TMSK2 equ $8D ;Timer Interrupt Mask 2 TFLG1 equ $8E ;Timer Interrupt Flag 1 TFLG2 equ $8F ;Timer Interrupt Flag 2 TC0 equ $90 ;TIC/TOC 0 TC0L equ $91 ;TIC/TOC 0 Low Byte TC1 equ $92 ;TIC/TOC 1 TC1L equ $93 ;TIC/TOC 1 Low Byte TC2 equ $94 ;TIC/TOC 2 TC2L equ $95 ;TIC/TOC 2 Low Byte TC3 equ $96 ;TIC/TOC 3 TC3L equ $97 ;TIC/TOC 3 Low TC4 equ $98 ;TIC/TOC 4 TC4L equ $99 ;TIC/TOC 4 Low Byte TC5 equ $9A ;TIC/TOC 5 TC5L equ $9B ;TIC/TOC 5 Low Byte TC6 equ $9C ;TIC/TOC 6 TC6L equ $9D ;TIC/TOC 6 Low Byte TC7 equ $9E ;TIC/TOC 7 TC7L equ $9F ;TIC/TOC 7 Low Byte PACTL equ $A0 ;Pulse Accumulator Control PAFLG equ $A1 ;Pulse Accumulator Flag PACNT equ $A2 ;Pulse Accumulator Count PACNTL equ $A3 ;Pulse Accumulator Counter Low Byte ; $A4 - AC are reserved on 812A4 TIMTST equ $AD ;Timer Test PORTT equ $AE ;Timer Port T Data DDRT equ $AF ;Timer Port T Data Direction ; $B0 - BF are reserved on 812A4 SC0BD equ $C0 ;SCI 0 Baud Rate SC0BDL equ $C1 ;SCI 0 Baud Rate Low Byte SC0CR1 equ $C2 ;SCI 0 Control 1 SC0CR2 equ $C3 ;SCI 0 Control 2 SC0SR1 equ $C4 ;SCI 0 Status 1 SC0SR2 equ $C5 ;SCI 0 Status 2 SC0DR equ $C6 ;SCI 0 Data SC0DRL equ $C7 ;SCI 0 Data Low Byte SC1BD equ $C8 ;SCI 1 Baud Rate SC1BDL equ $C9 ;SCI 1 Baud Rate Low Byte SC1CR1 equ $CA ;SCI 1 Control 1 SC1CR2 equ $CB ;SCI 1 Control 2 SC1SR1 equ $CC ;SCI 1 Status 1 SC1SR2 equ $CD ;SCI 1 Status 2 SC1DR equ $CE ;SCI 1 Data SC1DRL equ $CF ;SCI 1 Data Low Byte SP0CR1 equ $D0 ;SPI 0 Control 1 SP0CR2 equ $D1 ;SPI 0 Control 2 SP0BR equ $D2 ;SPI 0 Baud Rate SP0SR equ $D3 ;SPI 0 Status SP0DR equ $D5 ;SPI 0 Data RESD4 equ $D4 ;Reserved on 812A4 PORTS equ $D6 ;Port S Data DDRS equ $D7 ;Port S Data Direction ; $D8 - EF are reserved on 812A4 EEMCR equ $F0 ;EEPROM Module Configuration EEPROT equ $F1 ;EEPROM Block Protect EETST equ $F2 ;EEPROM Test EEPROG equ $F3 ;EEPROM Control ; $F4 - $1FF are reserved ;****************************************************************** ;****************************************************************** ; Declaring some storage space. Using the 'ds' instruction allocates ; the bytes, but doesn't try to initialize them like the 'db' or 'dw' ; instructions. This is mighty important since you don't want to ; initialize RAM variables! ; ; RAM is mapped from $0800 - $0BFF ; ORG $0800 Byte1: ds 1 Byte2: ds 1 Word1: ds 2 Word2: ds 2 Buffer1: ds 64 Buffer2: ds 64 ;****************************************************************** ; The ORG statement is set to $F000, which is the default starting ; address for the 4k of EEPROM on the 68HC812A4. ; 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 MainLoop: ; ; Lets take a quick peek at some of the cool new instructions and ; addressing modes. ; ; One of my personal favorites is the new MOV instructions. These ; are really great because they will do memory to memory transfers ; without an intermediate register. Since this machine only has ; 3 registers to work with, this is a real saver. You can move words ; or bytes. movb Byte1,Byte2 movw Word1,Word2 lda Byte1 ; Compare to the movb instruction sta Byte2 ; You can also move an immediate value, which is handy. Note that ; this instruction movb #$12,Byte1 movw #$FF11,Word1 ; ; memcpy is a nice demonstration of the mov instructions. ; ldd #64 ; Size of buffer into D ldy #Buffer2 ; Y is the destination register ldx #Buffer1 ; X is the source register jsr memcpy ; Go check it out. ; ; strcpy shows how the different addressing modes can be put to ; extremely good work. ; ldx #HelloWorld ldy #Buffer2 jsr strcpy ; ; The LEA instruction does a Load Effective Address. Most addressing ; modes have some sort of computation involved. The LEA instruction ; allows you to grab the computed address, instead of the data at ; that address. It is used quite often in adjusting the stack ; pointer when you want to declare local variables ; leas -8,sp ; Subtract 8 from SP and store in SP leas 8,sp ; Add 8 to SP and store in SP ; ; Call a routine that uses local variables and index SP addressing ; jsr PrintSomeNumbers ; ; Hopefully, the usefulness of the lea instruction popped out and ; nailed you right in the head. If not, allow me to explain. ; ; One of the big issues I have had with the 68HC11 is the lack ; of a nice set of addition/subtraction primatives. In essence, ; you are stuck using accumulator D to do all of your 16-bit math, ; even when you just wanted to add a simple value to X. ; ; The LEA instruction helps fill this gap. Its really more of a ; universal math routine, except it doesn't set the condition ; codes. But thats OK most of the time. ; ; Take a look ; 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 ; ; Though it is the end of the main loop, check out whats below! ; bra MainLoop ;************************************************************************* ; 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 ;************************************************************************* ; 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 ;*************************************** ; Test program strings ; HelloWorld: fcc "Hello World!" db 13,10,0 CurrentNumber: fcc "Current Number: " db 0 CRLF: db 13,10,0 ;************************************************************************* ; ; Some serial port routines. 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 ; ; putchar outputs a character to serial port 0 ; Call with character in register A ; putchar: brclr SC0SR1, #$80 putchar staa SC0DRL rts ; ; ; 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 ;****************************************************************** ; A nice new feature of the HC12 is the ability to have index ; addressing based on the X, Y, SP, or PC. For example, if you ; need a local variable, then you can just create some! ; ; The little section has a routine that counts down. Check ; out how the local variables, indexed from sp, are used. This ; is really useful since you don't have to dedicate X or Y to ; being your 'frame' pointer. This saves lots of space because ; the free register can be used for other things. ; PrintSomeNumbers: DelayCountL equ 2 DelayCountH equ 1 DelayCountW equ 1 ; Address of the WORD DelayCount NextNumber equ 0 ldx #CurrentNumber jsr outstr 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 ; This instruction does the same thing, moving #9 into ; the NextNumber variable. movb #9 NextNumber,sp ; Load value into the first byte NumLoop: ; Print out a number ldaa NextNumber,sp ; Address is SP + 0 adda #$30 ; Add ASCII 0 jsr putchar ; Print the digit ldaa #$20 jsr putchar ldaa NextNumber,sp beq NumLoopEnd ; Decrement by one dec NextNumber,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 bra NumLoop NumLoopEnd: ldx #CRLF jsr outstr ; ; Load effective address allows you to grab the computed address ; value. Here, it is used to load the effective address of SP + 3 ; into the SP register. This trick allows us to do math on the ; SP register. ; leas 3,sp ; Remove the two local variables rts ;************************************************************************* ; ; 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 end