File "MON12D60.asm"
Full Path: /home/analogde/www/chart-export-handler/Projet/Example/HC12/D60/NoICE/MON12D60.asm
File size: 41.2 KB
MIME-type: text/plain
Charset: utf-8
* 68HC12 Debug monitor for use with NOICE12
*
* Copyright (c) 2001 by John Hartman
*
* Modification History:
*
* Axiom modifications:
* 1) ported to hc912d60, removed excess for A4 paging
*
*============================================================================
* To customize for a given target, you must change code in the
* hardware equates, the string TSTG, and the routines RESET and REWDT.
* You may or may not need to change GETCHAR, PUTCHAR, depending on
* how peculiar your UART is.
*
* For more information, refer to the NoICE help file monitor.htm
*
* To enable banked or paged memory support
* 1) Define page latch port PPAGE to match your target chip ($1035 on the A4)
* 2) Search for and modify PPAGE and REG_PAGE usage below
* 3) In TSTG below edit "LOW AND HIGH LIMIT OF PAGED MEM"
* to appropriate range (8000H to BFFFH for the HC12's built-in banking)
*
* For more information, refer to the NoICE help file 2bitmmu.htm
*
* If you are using one of the boards shown below, just define the
* appropriate symbol.
* DEFINE ONLY ONE SYMBOL AT A TIME.
*
* This file has been assembled with the Motorola Freeware assembler
* available from the Motorola Freeware BBS and elsewhere.
*
* This file may also be assembled with the Dunfield assembler
*
*============================================================================
* Axiom CME12D60
* - Operates in either expanded wide or single-chip
* - Comes with 64K of RAM
*
* Define operating memory map, registers are moved to
* allow full 2K ram access
CHIP_RAM EQU $0000 ;START OF HC12 ON-CHIP 2K RAM
INITSTACK EQU $07FF+1 ;INIT USER STACK: TOP+1 OF ON-CHIP RAM
CHIP_IO EQU $0800 ;START OF HC12 ON-CHIP I/O
CHIP_EEPROM EQU $0C00 ;START OF HC12 ON-CHIP 1K EEPROM
USER_VECTORS EQU $06C0 ;START OF Interrupt HARDWARE VECTORS
RAM_START EQU $0700 ;START OF MONITOR RAM
ROM_START EQU $FC00 ;START OF MONITOR CODE
HARD_VECT EQU $FFC0 ;START OF HARDWARE VECTORS
*============================================================================
* Define HC12 I/O register locations (68HC12D60)
*
PORTA: EQU CHIP_IO+0 ;port A = Address lines A8 - A15
PORTB: EQU CHIP_IO+1 ;port B = Address lines A0 - A7
DDRA: EQU CHIP_IO+2 ;port A direction register
DDRB: EQU CHIP_IO+3 ;port A direction register
PORTE: EQU CHIP_IO+8 ;port E = mode,IRQandcontrolsignals
DDRE: EQU CHIP_IO+9 ;port E direction register
PEAR: EQU CHIP_IO+$A ;port E assignments
MODE: EQU CHIP_IO+$B ;Mode register
PUCR: EQU CHIP_IO+$C ;port pull-up control register
RDRIV: EQU CHIP_IO+$D ;port reduced drive control register
INITRM: EQU CHIP_IO+$10 ;Ram location register
INITRG: EQU CHIP_IO+$11 ;Register location register
INITEE: EQU CHIP_IO+$12 ;EEprom location register
MISC: EQU CHIP_IO+$13 ;Miscellaneous Mapping control
RTICTL: EQU CHIP_IO+$14 ;Real time clock control
RTIFLG: EQU CHIP_IO+$15 ;Real time clock flag
COPCTL: EQU CHIP_IO+$16 ;Clock operating properly control
COPRST: EQU CHIP_IO+$17 ;COP reset register
INTCR: EQU CHIP_IO+$1E ;interrupt control register
HPRIO: EQU CHIP_IO+$1F ;high priority reg
BRKCT0: EQU CHIP_IO+$20 ;Break control register
BRKCT1: EQU CHIP_IO+$21 ;Break control register
BRKAH: EQU CHIP_IO+$22 ; Break address register high
BRKAL: EQU CHIP_IO+$23 ; Break address register low
BRKDH: EQU CHIP_IO+$24 ; Break data register high
BRKDL: EQU CHIP_IO+$25 ; Break data register low
PORTG: EQU CHIP_IO+$28 ;port G = KEY PORT
PORTH: EQU CHIP_IO+$29 ;port H = Key port
DDRG: EQU CHIP_IO+$2A ;port G direction register
DDRH: EQU CHIP_IO+$2B ;port H direction register
KWIEG: EQU CHIP_IO+$2C ;Key wake-up port G enable
KWIEH: EQU CHIP_IO+$2D ;Key wake-up port H enable
KWIFG: EQU CHIP_IO+$2E ;Key wake-up port G flags
KWIFH: EQU CHIP_IO+$2F ;Key wake-up port H flags
SYNR: EQU CHIP_IO+$38 ; Synthesizer / multiplier register
REFDV: EQU CHIP_IO+$39 ; Reference divider register
CGTFLG: EQU CHIP_IO+$3A ; RESERVED
PLLFLG: EQU CHIP_IO+$3B ; PLL flags register
PLLCR: EQU CHIP_IO+$3C ; PLL control register
CLKSEL: EQU CHIP_IO+$3D ; Clock select register
SLOW: EQU CHIP_IO+$3E ; Slow mode divider register
PWCLK: EQU CHIP_IO+$40 ;PWM clock register
PWPOL: EQU CHIP_IO+$41 ;PWM clock select and polarity
PWEN: EQU CHIP_IO+$42 ;PWM enable register
PWPRES: EQU CHIP_IO+$43 ;PWM Prescale register
PWSCAL0: EQU CHIP_IO+$44 ;PWM Scale 0
PWSCNT0: EQU CHIP_IO+$45 ;PWM scale counter 0
PWSCAL1: EQU CHIP_IO+$46 ;PWM scale 1
PWSCNT1: EQU CHIP_IO+$47 ;PWM scale counter 1
PWCNT0: EQU CHIP_IO+$48 ;PWM channel 0 counter
PWCNT1: EQU CHIP_IO+$49 ;PWM channel 1 counter
PWCNT2: EQU CHIP_IO+$4A ;PWM channel 2 counter
PWCNT3: EQU CHIP_IO+$4B ;PWM channel 3 counter
PWPER0: EQU CHIP_IO+$4C ;PWM channel 0 period
PWPER1: EQU CHIP_IO+$4D ;PWM channel 1 period
PWPER2: EQU CHIP_IO+$4E ;PWM channel 2 period
PWPER3: EQU CHIP_IO+$4F ;PWM channel 3 period
PWDTY0: EQU CHIP_IO+$50 ;PWM channel 0 duty cycle
PWDTY1: EQU CHIP_IO+$51 ;PWM channel 1 duty cycle
PWDTY2: EQU CHIP_IO+$52 ;PWM channel 2 duty cycle
PWDTY3: EQU CHIP_IO+$53 ;PWM channel 3 duty cycle
PWCTL: EQU CHIP_IO+$54 ;PWM control register
PWTST: EQU CHIP_IO+$55 ;reserved
PORTP: EQU CHIP_IO+$56 ;Port P data register
DDRP: EQU CHIP_IO+$57 ;Port P data direction register
ATDCTL0: EQU CHIP_IO+$60 ;ADC control 0 (reserved)
ATDCTL1: EQU CHIP_IO+$61 ;ADC control 1 (reserved)
ATDCTL2: EQU CHIP_IO+$62 ;ADC control 2
ATDCTL3: EQU CHIP_IO+$63 ;ADC control 3
ATDCTL4: EQU CHIP_IO+$64 ;ADC control 4
ATDCTL5: EQU CHIP_IO+$65 ;ADC control 5
ATDSTAT: EQU CHIP_IO+$66 ;ADC status register hi
;ATDSTAT EQU CHIP_IO+$67 ;ADC status register lo
ATDTEST: EQU CHIP_IO+$68 ;ADC test (reserved)
;ATDTEST EQU CHIP_IO+$69
PORTAD: EQU CHIP_IO+$6F ;port ADC = input only
ADR0H: EQU CHIP_IO+$70 ;ADC result 0 register
ADR1H: EQU CHIP_IO+$72 ;ADC result 1 register
ADR2H: EQU CHIP_IO+$74 ;ADC result 2 register
ADR3H: EQU CHIP_IO+$76 ;ADC result 3 register
ADR4H: EQU CHIP_IO+$78 ;ADC result 4 register
ADR5H: EQU CHIP_IO+$7A ;ADC result 5 register
ADR6H: EQU CHIP_IO+$7C ;ADC result 6 register
ADR7H: EQU CHIP_IO+$7E ;ADC result 7 register
TIOS: EQU CHIP_IO+$80 ;timer input/output select
CFORC: EQU CHIP_IO+$81 ;timer compare force
OC7M: EQU CHIP_IO+$82 ;timer output compare 7 mask
OC7D: EQU CHIP_IO+$83 ;timer output compare 7 data
TCNT: EQU CHIP_IO+$84 ;timer counter register hi
;TCNT EQU CHIP_IO+$85 ;timer counter register lo
TSCR: EQU CHIP_IO+$86 ;timer system control register
TQCR: EQU CHIP_IO+$87 ;reserved
TCTL1: EQU CHIP_IO+$88 ;timer control register 1
TCTL2: EQU CHIP_IO+$89 ;timer control register 2
TCTL3: EQU CHIP_IO+$8A ;timer control register 3
TCTL4: EQU CHIP_IO+$8B ;timer control register 4
TMSK1: EQU CHIP_IO+$8C ;timer interrupt mask 1
TMSK2: EQU CHIP_IO+$8D ;timer interrupt mask 2
TFLG1: EQU CHIP_IO+$8E ;timer flags 1
TFLG2: EQU CHIP_IO+$8F ;timer flags 2
TC0: EQU CHIP_IO+$90 ;timer capture/compare register 0
;TC0 EQU CHIP_IO+$91
TC1: EQU CHIP_IO+$92 ;timer capture/compare register 1
;TC1 EQU CHIP_IO+$93
TC2: EQU CHIP_IO+$94 ;timer capture/compare register 2
;TC2 EQU CHIP_IO+$95
TC3: EQU CHIP_IO+$96 ;timer capture/compare register 3
;TC3 EQU CHIP_IO+$97
TC4: EQU CHIP_IO+$98 ;timer capture/compare register 4
;TC4 EQU CHIP_IO+$99
TC5: EQU CHIP_IO+$9A ;timer capture/compare register 5
;TC5 EQU CHIP_IO+$9B
TC6: EQU CHIP_IO+$9C ;timer capture/compare register 6
;TC6 EQU CHIP_IO+$9D
TC7: EQU CHIP_IO+$9E ;timer capture/compare register 7
;TC7 EQU CHIP_IO+$9F
PACTL: EQU CHIP_IO+$A0 ;pulse accumulator controls
PAFLG: EQU CHIP_IO+$A1 ;pulse accumulator flags
PACN3: EQU CHIP_IO+$A2 ;pulse accumulator counter 3
PACN2: EQU CHIP_IO+$A3 ;pulse accumulator counter 2
PACN1: EQU CHIP_IO+$A4 ;pulse accumulator counter 1
PACN0: EQU CHIP_IO+$A5 ;pulse accumulator counter 0
MCCTL: EQU CHIP_IO+$A6 ;Modulus down conunter control
MCFLG: EQU CHIP_IO+$A7 ;down counter flags
ICPACR: EQU CHIP_IO+$A8 ;Input pulse accumulator control
DLYCT: EQU CHIP_IO+$A9 ;Delay count to down counter
ICOVW: EQU CHIP_IO+$AA ;Input control overwrite register
ICSYS: EQU CHIP_IO+$AB ;Input control system control
TIMTST: EQU CHIP_IO+$AD ;timer test register
PORTT: EQU CHIP_IO+$AE ;port T = Timer port
DDRT: EQU CHIP_IO+$AF ;port T direction register
PBCTL: EQU CHIP_IO+$B0 ; Pulse accumulator B control
PBFLG: EQU CHIP_IO+$B1 ; Pulse accumulator B flags
PA3H: EQU CHIP_IO+$B2 ; Pulse Accumulator counter 3
PA2H: EQU CHIP_IO+$B3 ; Pulse Accumulator counter 2
PA1H: EQU CHIP_IO+$B4 ; Pulse Accumulator counter 1
pA0H: EQU CHIP_IO+$B5 ; Pulse Accumulator counter 0
MCCNT: EQU CHIP_IO+$B6 ; Modulus down counter register
*MCCNTL: EQU CHIP_IO+$B7 ; low byte
TCOH: EQU CHIP_IO+$B8 ; Capture 0 holding register
TC1H: EQU CHIP_IO+$BA ; Capture 1 holding register
TC2H: EQU CHIP_IO+$BC ; Capture 2 holding register
TC3H: EQU CHIP_IO+$BE ; Capture 3 holding register
SC0BDH: EQU CHIP_IO+$C0 ;sci 0 baud reg hi byte
SC0BDL: EQU CHIP_IO+$C1 ;sci 0 baud reg lo byte
SC0CR1: EQU CHIP_IO+$C2 ;sci 0 control1 reg
SC0CR2: EQU CHIP_IO+$C3 ;sci 0 control2 reg
SC0SR1: EQU CHIP_IO+$C4 ;sci 0 status reg 1
SC0SR2: EQU CHIP_IO+$C5 ;sci 0 status reg 2
SC0DRH: EQU CHIP_IO+$C6 ;sci 0 data reg hi
SC0DRL: EQU CHIP_IO+$C7 ;sci 0 data reg lo
SC1BDH: EQU CHIP_IO+$C8 ;sci 1 baud reg hi byte
SC1BDL: EQU CHIP_IO+$C9 ;sci 1 baud reg lo byte
SC1CR1: EQU CHIP_IO+$CA ;sci 1 control1 reg
SC1CR2: EQU CHIP_IO+$CB ;sci 1 control2 reg
SC1SR1: EQU CHIP_IO+$CC ;sci 1 status reg 1
SC1SR2: EQU CHIP_IO+$CD ;sci 1 status reg 2
SC1DRH: EQU CHIP_IO+$CE ;sci 1 data reg hi
SC1DRL: EQU CHIP_IO+$CF ;sci 1 data reg lo
SP0CR1: EQU CHIP_IO+$D0 ;spi 0 control1 reg
SP0CR2: EQU CHIP_IO+$D1 ;spi 0 control2 reg
SP0BR: EQU CHIP_IO+$D2 ;spi 0 baud reg
SP0SR: EQU CHIP_IO+$D3 ;spi 0 status reg hi
SP0DR: EQU CHIP_IO+$D5 ;spi 0 data reg
PORTS: EQU CHIP_IO+$D6 ;port S = Serial port
DDRS: EQU CHIP_IO+$D7 ;port S direction register
PURDS: EQu CHIP_IO+$D9 ;port S pull-ups register
EEMCR: EQU CHIP_IO+$F0 ;EEprom mode control
EEPROT: EQU CHIP_IO+$F1 ;EEprom block protect reg
EETST: EQU CHIP_IO+$F2 ;EEprom test register
EEPROG: EQU CHIP_IO+$F3 ;EEprom program reg
* Lots more registers to go....
*
*============================================================================
* HARDWARE PLATFORM CUSTOMIZATIONS
*============================================================================
*
* Put you UART equates here
* (These are for the SCI)
SER_STATUS EQU SC0SR1 STATUS FROM SCI
SER_RXDATA EQU SC0DRL DATA FROM SCI
SER_TXDATA EQU SC0DRL DATA TO SCI
RXRDY EQU $20
TXRDY EQU $80 TRANSMIT COMPLETE (FOR TURNOFF)
*
*============================================================================
* RAM interrupt vectors. Equivalent to vectors FFC0 to FFFF
ORG USER_VECTORS
RAMVEC RMB 2*32
*
*============================================================================
* Monitor RAM definitions
ORG RAM_START
*
* Target registers: order must match that in TRGHC12.C
TASK_REGS
REG_STATE RMB 1
REG_PAGE RMB 1
REG_SP RMB 2
REG_Y RMB 2
REG_X RMB 2
REG_B RMB 1 B BEFORE A, SO D IS LEAST SIG. FIRST
REG_A RMB 1
REG_CC RMB 1
REG_PC RMB 2
TASK_REG_SZ EQU *-TASK_REGS
*
* Communications buffer
* (Must be at least as long as the longer of TASK_REG_SZ or TSTG_SIZE.
* At least 19 bytes recommended. Larger values may improve speed of NoICE
* download and memory move commands. Maximum 128.)
COMBUF_SIZE EQU 128 DATA SIZE FOR COMM BUFFER
COMBUF RMB 2+COMBUF_SIZE+1 BUFFER ALSO HAS FN, LEN, AND CHECK
*
* Don't let this overlap the user vectors...
RAM_END EQU * ADDRESS OF TOP+1 OF RAM
*
*===========================================================================
ORG ROM_START
*
* On-chip I/O initialization. Each entry is (port, value)
* Table ends with port=$FF.
*
* The monitor assumes operation in either Expanded or Special Expanded mode.
* The exact initialization required here will depend on which variant of
* the 68HC12 you have, and on your hardware layout. The following is
* basic, and may not be sufficient for your case.
*
* Many of these registers can be written only once after reset. They are
* set here, often to their default values, so that an errant user program
* doesn't change them (and possibly lock up the target). If you want your
* user program to be responsible for setting them, either remove this code,
* or change to "special" mode, in which these registers can be written at
* will. However, note that in special mode many registers must be written
* TWICE, as the first write is ignored.
*
* CHIP SELECTS
* If E is 8 MHz (16 MHz crystal), then 1 cycle is 125 nsec and
* - 0 wait states requires 80 nsec access time
* - 1 wait states requires 200 nsec access time
* - 2 wait states requires 330 nsec access time
* - 3 wait states requires 455 nsec access time
*
* Any "real" program should enable the COP. However, we disable it
* for the convenience of your initial testing.
* You should enable the clock monitor unless you are going to use
* the STOP instruction for lowest power consumption.
*
* We have made internal bus operations visible, so that you can see them
* with a scope or logic analyzer. For production systems, you may wish
* to disable this in order to reduce emissions.
*
INIT_TABLE:
FCB INITRM-CHIP_IO,$00 ;locate RAM
FCB INITEE-CHIP_IO,CHIP_EEPROM/256+1 ;locate and enable EEPROM
FCB COPCTL-CHIP_IO,$00 ;DISABLE CLOCK MONITOR, NO COP TIMEOUT
* FCB PLLCR-CHIP_IO, $00 ; Force pll off
* FCB CLKSEL-CHIP_IO,$00 ; Clock select is EXtal input
FCB MODE-CHIP_IO, $F0 ;NORMAL EXPANDED WIDE, INVISIBLE BUS, E stretches
FCB PEAR-CHIP_IO, $0C ;PE4=E, PE3=LSTRB, PE2=R/W
FCB MISC-CHIP_IO, $74 ;Flash off, p-sel stretch = 3, mem = 1
INIT_TABLE_END: ;END OF TABLE
*
*======================================================================
* Response string for GET TARGET STATUS request
* Reply describes target:
TSTG FCB 13 ;2: PROCESSOR TYPE = 68HC12
FCB COMBUF_SIZE ;3: SIZE OF COMMUNICATIONS BUFFER
* FCB $80 ;4: has CALL
FCB 0 ;4: No CALL function
FDB 0 ;5,6: BOTTOM OF PAGED MEM
FDB 0 ;7,8: TOP OF PAGED MEM
FCB B1-B0 ;9 BREAKPOINT INSTR LENGTH
B0 SWI ;10+ BREKAPOINT INSTRUCTION
B1 FCC '68HC12D60 monitor V1.0 ' ;DESCRIPTION
FCC 'for Axiom CME12D60 wide'
FCB 0
FCB 0 ;page of CALL breakpoint
FDB B0 ;address of CALL breakpoint
TSTG_SIZE EQU *-TSTG ;SIZE OF STRING
*
*===========================================================================
* Initialize the NoICE UART
*
* Default implementation uses SCI
INITUART
*
* Baud DIV = MCLK / (16 * Baud_Rate)
* MCLK = crystal / 2
* With a 16 Mhz crystal, DIV = 8,000,000/(16*Baud_Rate) = 500000/Baud_Rate
* For 19200 baud, DIV = 26 (actual baud rate = 19231, or +0.16%)
LDD #26 ;BAUD RATE CONSTANT
STD SC0BDH ;WRITE HIGH BYTE, LOW BYTE
LDAA #$00 ;NO LOOP, 8-BIT, NO WAKE, NO PARITY
STAA SC0CR1
LDAA #$0C ;NO INTS, ENABLE TRANSMIT AND RECEIVE
STAA SC0CR2
RTS
*
*===========================================================================
* Get a character from UART to A
*
* Return A=char, CY=0 if data received
* CY=1 if timeout (0.5 seconds)
*
* Uses 6 bytes of stack including return address
*
GETCHAR
PSHX
LDX #0 ;LONG TIMEOUT
GC10 BSR REWDT ;PREVENT WATCHDOG TIMEOUT
DEX
*;* BEQ GC90 ;EXIT IF TIMEOUT
*(Disable timeout in most cases...)
BRCLR SER_STATUS,#RXRDY, GC10 ;NOT READY YET.
*
* Data received: return CY=0. data in A
CLC ;CY=0
LDAA SER_RXDATA ;READ DATA
PULX
RTS
*
* Timeout: return CY=1
GC90 SEC ;CY=1
PULX
RTS
*
*===========================================================================
* Output character in A to UART
*
* Uses 5 bytes of stack including return address
*
PUTCHAR
PSHA
PC10 BSR REWDT ;PREVENT WATCHDOG TIMEOUT
BRCLR SER_STATUS, #TXRDY, PC10
PULA
STAA SER_TXDATA ;TRANSMIT CHAR.
RTS
*
*======================================================================
* Reset watchdog timer. Must be called at least once every little while
* or COP interrupt will occur
*
* Uses 2 bytes of stack including return address
*
REWDT LDAA #$55
STAA COPRST
LDAA #$AA
STAA COPRST
RTS
*
*======================================================================
* Power on reset
RESET
*
* Set CPU mode to safe state
SEI INTERRUPTS OFF (WE MAY JUMP HERE)
CLRB STATE 0 = "RESET"
BRA RES10
*
*------------------------------------------------
* COP reset
COP_ENT
LDAB #4 STATE 4 = "COP"
BRA RES10
*
*------------------------------------------------
* Clock Fail reset
CLOCK_ENT
LDAB #3 STATE 3 = "Clock fail"
* BE SURE THAT "B" REMAINS INTACT UNTIL IT IS STORED TO REG_STATE BELOW!
RES10
*
* CAUTION: DON'T USE I/O ADDRESS EQUATES UNTIL INITRG IS WRITTEN
* TO SET THE I/O BASE TO MATCH OUR EQUATES!
LDAA #$08 ; Set register location
STAA $0011 ;POST-RESET LOCATION OF INITRG
*
* Copy init table to on-chip registers (without using B)
LDX #INIT_TABLE
RES15 LDAA 1,X+ ;A=PORT
LDY #CHIP_IO
LEAY A,Y ;Y POINTS AT PORT
LDAA 1,X+ ;A=DATA
STAA 0,Y
STAA 0,Y ;store twice: first may be ignored in SPECIAL mode
CPX #INIT_TABLE_END
BNE RES15 ; loop until table end
*
* Save reset type (RESET, COP, or Clock Fail)
STAB REG_STATE ;SAVE STATE
LDS #INITSTACK ;Initial stack value
BSR INITUART
*
* Initialize RAM interrupt vectors
LDY #INT_ENTRY ;ADDRESS OF DEFAULT HANDLER
LDX #RAMVEC ;POINTER TO RAM VECTORS
LDAB #NVEC/2 ;NUMBER OF VECTORS
RST10 STY 2,X+ ;SET VECTOR
DBNE B,RST10
*
* Initialize user registers
LDD #INITSTACK
STAA REG_SP+1 ;INIT USER'S STACK POINTER MSB
STAB REG_SP ;LSB
CLRA
CLRB
STD REG_PC
STAA REG_A
STAA REG_B
STD REG_X
STD REG_Y
*
* Initialize memory paging variables and hardware
* LDAA PPAGE
LDAA #$0 ;
STAA REG_PAGE ;PAGE FROM HARDWARE (or zero)
*
* Initialize non-zero registers
LDAA #$50 ;disable interrupts in user program
STAA REG_CC
*
* Set function code for "GO". Then if we are here because of a reset
* (such as a COP timeout or clock fail) after being told to GO, we will
* come back with registers so user can see the reset
LDAA #FN_RUN_TARG
STAA COMBUF
JMP RETURN_REGS ;DUMP REGS, ENTER MONITOR
*
*======================================================================
* HARDWARE PLATFORM INDEPENDENT EQUATES AND CODE
*
* Communications function codes.
FN_GET_STAT EQU $FF ;reply with device info
FN_READ_MEM EQU $FE ;reply with data
FN_WRITE_M EQU $FD ;reply with status (+/-)
FN_READ_RG EQU $FC ;reply with registers
FN_WRITE_RG EQU $FB ;reply with status
FN_RUN_TARG EQU $FA ;reply (delayed) with registers
FN_SET_BYTE EQU $F9 ;reply with data (truncate if error)
FN_IN EQU $F8 ;input from port
FN_OUT EQU $F7 ;output to port
*
FN_MIN EQU $F0 ;MINIMUM RECOGNIZED FUNCTION CODE
FN_ERROR EQU $F0 ;error reply to unknown op-code
*
*===========================================================================
* Common handler for default interrupt handlers
* Enter with A=interrupt code = processor state
* All registers stacked, PC=next instruction
INT_ENTRY
STAA REG_STATE ;SAVE STATE
*
* Save registers from stack to reg block for return to master
* Host wants least significant bytes first, so flip as necessary
PULA
STAA REG_CC ;CONDITION CODES
PULA
STAA REG_B
PULA
STAA REG_A
PULD
STAA REG_X+1 ;MSB
STAB REG_X ;LSB
PULD
STAA REG_Y+1 ;MSB
STAB REG_Y ;LSB
*
* If this is a breakpoint (state = 1), then back up PC to point at SWI
PULX ;PC AFTER INTERRUPT
LDAA REG_STATE
CMPA #1
BNE NOTBP ;BR IF NOT A BREAKPOINT
LEAX B0-B1,X ;ELSE BACK UP TO POINT AT SWI LOCATION
NOTBP TFR X,D
STAA REG_PC+1 ;MSB
STAB REG_PC ;LSB
TFR SP,D ;USER STACK POINTER
STAA REG_SP+1 ;MSB
STAB REG_SP ;LSB
*
* Save memory page
* (Could make this conditional on WINDEF PWEN bit: set page 0 if bit clear)
* LDAA PPAGE ;GET CURRENT USER PAGE
LDAA #0 ... OR ZERO IF UNPAGED TARGET
STAA REG_PAGE ;SAVE USER'S PAGE
*
* Return registers to master
JMP RETURN_REGS
*
*===========================================================================
* Main loop: wait for command frame from master
*
* Uses 7 bytes of stack before jump to handlers
*
MAIN
LDX #COMBUF ;BUILD MESSAGE HERE
*
* First byte is a function code
JSR GETCHAR ;GET A FUNCTION
BCS MAIN ;JIF TIMEOUT: RESYNC
CMPA #FN_MIN
BLO MAIN ;JIF BELOW MIN: ILLEGAL FUNCTION
STAA 1,X+ ;SAVE FUNCTION CODE
*
* Second byte is data byte count (may be zero)
JSR GETCHAR ;GET A LENGTH BYTE
BCS MAIN ;JIF TIMEOUT: RESYNC
CMPA #COMBUF_SIZE
BHI MAIN ;JIF TOO LONG: ILLEGAL LENGTH
STAA 1,X+ ;SAVE LENGTH
BEQ MA80 ;SKIP DATA LOOP IF LENGTH = 0
*
* Loop for data
TFR A,B ;SAVE LENGTH FOR LOOP
MA10 JSR GETCHAR ;GET A DATA BYTE
BCS MAIN ;JIF TIMEOUT: RESYNC
STAA 1,X+ ;SAVE DATA BYTE
DBNE B,MA10
*
* Get the checksum
MA80 JSR GETCHAR ;GET THE CHECKSUM
BCS MAIN ;JIF TIMEOUT: RESYNC
PSHA ;SAVE CHECKSUM
*
* Compare received checksum to that calculated on received buffer
* (Sum should be 0)
JSR CHECKSUM
ADDA 1,SP+
BNE MAIN ;JIF BAD CHECKSUM
*
* Process the message.
LDX #COMBUF
LDD 2,X+ ;A= FUNCTION CODE, B= LENGTH, X=DATA
CMPA #FN_GET_STAT
BEQ TARGET_STAT
CMPA #FN_READ_MEM
BEQ READ_MEM
CMPA #FN_WRITE_M
BEQ WRITE_MEM
CMPA #FN_READ_RG
LBEQ READ_REGS
CMPA #FN_WRITE_RG
LBEQ WRITE_REGS
CMPA #FN_RUN_TARG
LBEQ RUN_TARGET
CMPA #FN_SET_BYTE
LBEQ SET_BYTES
CMPA #FN_IN
LBEQ IN_PORT
CMPA #FN_OUT
LBEQ OUT_PORT
*
* Error: unknown function. Complain
LDAA #FN_ERROR
STAA COMBUF ;SET FUNCTION AS "ERROR"
LDAA #1
JMP SEND_STATUS ;VALUE IS "ERROR"
*===========================================================================
*
* Target Status: FN, len
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
TARGET_STAT
LDY #TSTG ;DATA FOR REPLY
LDAB #TSTG_SIZE ;LENGTH OF REPLY
STAB COMBUF+1
TS10 LDAA 1,Y+ ;MOVE REPLY DATA TO BUFFER
STAA 1,X+
DBNE B,TS10
*
* Compute checksum on buffer, and send to master, then return
JMP SEND
*===========================================================================
*
* Read Memory: FN, len, page, Alo, Ahi, Nbytes
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
READ_MEM
*
* Set page
* LDAA 0,X
* STAA PPAGE
*
* Get address
LDAA 2,X ;MSB OF ADDRESS IN A
LDAB 1,X ;LSB OF ADDRESS IN B
TFR D,Y ;ADDRESS IN Y
*
* Prepare return buffer: FN (unchanged), LEN, DATA
LDAB 3,X ;NUMBER OF BYTES TO RETURN
STAB COMBUF+1 ;RETURN LENGTH = REQUESTED DATA
BEQ GLP90 ;JIF NO BYTES TO GET
*
* Read the requested bytes from local memory
GLP LDAA 1,Y+ ;GET BYTE
STAA 1,X+ ;STORE TO RETURN BUFFER
DBNE B,GLP
*
* Compute checksum on buffer, and send to master, then return
GLP90 JMP SEND
*===========================================================================
*
* Write Memory: FN, len, page, Alo, Ahi, (len-3 bytes of Data)
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
* Uses 6 bytes of stack
*
WRITE_MEM
*
* Set page
* LDAA 0,X
* STAA PPAGE
*
* Get address
LDAA 2,X ;MSB OF ADDRESS IN A
LDAB 1,X ;LSB OF ADDRESS IN B
TFR D,Y ;ADDRESS IN Y
*
* Prepare return buffer: FN (unchanged), LEN, DATA
LDAB COMBUF+1 ;NUMBER OF BYTES TO RETURN
SUBB #3 ;MINUS PAGE AND ADDRESS
BEQ WLP50 ;JIF NO BYTES TO PUT
*
* Write the specified bytes to local memory
PSHB
PSHX
PSHY
WLP LDAA 3,X ;GET BYTE TO WRITE
STAA 1,Y+ ;STORE THE BYTE AT AAAA,Y
INX
DBNE B,WLP
*
* Compare to see if the write worked
PULY
PULX
PULB
WLP20 LDAA 3,X ;GET BYTE JUST WRITTEN
CMPA 1,Y+
BNE WLP80 ;BR IF WRITE FAILED
INX
DBNE B,WLP20
*
* Write succeeded: return status = 0
WLP50 CLRA ;RETURN STATUS = 0
BRA WLP90
*
* Write failed: return status = 1
WLP80 LDAA #1
*
* Return OK status
WLP90 JMP SEND_STATUS
*===========================================================================
*
* Read registers: FN, len=0
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
READ_REGS
*
* Enter here from SWI after "RUN" and "STEP" to return task registers
* CAUTION: in this case, assume no registers!
RETURN_REGS
LDY #TASK_REGS ;POINTER TO REGISTERS
LDAB #TASK_REG_SZ ;NUMBER OF BYTES
STAB COMBUF+1 ;SAVE RETURN DATA LENGTH
*
* Copy the registers
LDX #COMBUF+2 ;POINTER TO RETURN BUFFER
GRLP LDAA 1,Y+ ;GET BYTE TO A
STAA 1,X+ ;STORE TO RETURN BUFFER
DBNE B,GRLP
*
* Compute checksum on buffer, and send to master, then return
JMP SEND
*===========================================================================
*
* Write registers: FN, len, (register image)
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
WRITE_REGS
TSTB ;NUMBER OF BYTES
BEQ WRR80 ;JIF NO REGISTERS
*
* Copy the registers
LDY #TASK_REGS ;POINTER TO REGISTERS
WRRLP LDAA 1,X+ ;GET BYTE TO A
STAA 1,Y+ ;STORE TO REGISTER RAM
DBNE B,WRRLP
*
* Reload SP, in case it has changed
LDAB REG_SP
LDAA REG_SP+1
TFR D,SP
*
* Return OK status
WRR80 LDAA #0
JMP SEND_STATUS
*===========================================================================
*
* Run Target: FN, len
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
RUN_TARGET
*
* Restore user's page
* LDAA REG_PAGE
* STAA PPAGE
*
* Switch to user stack, if not already running on it
** LDAB REG_SP ;BACK TO USER STACK
** LDAA REG_SP+1
** TFR D,SP
*
* Restore registers
LDAA REG_PC+1 ;MSB USER PC FOR RTI
LDAB REG_PC ;LSB
PSHD
*
LDAA REG_Y+1
LDAB REG_Y
PSHD
*
LDAA REG_X+1
LDAB REG_X
PSHD
*
LDAA REG_A
PSHA
LDAA REG_B
PSHA
*
LDAA REG_CC ;SAVE USER CONDITION CODES FOR RTI
PSHA
*
* Return to user
RTI
*
*===========================================================================
*
* Set target byte(s): FN, len { (page, alow, ahigh, data), (...)... }
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
* Return has FN, len, (data from memory locations)
*
* If error in insert (memory not writable), abort to return short data
*
* This function is used primarily to set and clear breakpoints
*
* Uses 3 bytes of stack
*
SET_BYTES
LDY #COMBUF+1 ;POINTER TO RETURN BUFFER
LDAA #0
STAA 1,Y+ ;SET RETURN COUNT =0, POINT AT FIRST RETURN DATA BYTE
LSRB
LSRB ;LEN/4 = NUMBER OF BYTES TO SET
BEQ SB99 ;JIF NO BYTES (COMBUF+1 = 0)
*
* Loop on inserting bytes
SB10 PSHB ;SAVE LOOP COUNTER
PSHY ;SAVE RETURN BUFFER POINTER
*
* Set page
* LDAA 0,X
* STAA PPAGE
*
* Get address
LDAA 2,X ;MSB OF ADDRESS IN A
LDAB 1,X ;LSB OF ADDRESS IN B
TFR D,Y ;MEMORY ADDRESS IN Y
*
* Read current data at byte location
LDAA 0,Y
*
* Insert new data at byte location
LDAB 3,X ;GET BYTE TO STORE
STAB 0,Y ;WRITE TARGET MEMORY
*
* Verify write
CMPB 0,Y ;READ TARGET MEMORY
PULY ;RESTORE RETURN PTR (CC'S INTACT)
PULB ;RESTORE LOOP COUNTER (CC'S INTACT)
BNE SB90 ;BR IF INSERT FAILED: ABORT
*
* Save target byte in return buffer
STAA 1,Y+
INC COMBUF+1 ;COUNT ONE RETURN BYTE
*
* Loop for next byte
LEAX 4,X ;STEP TO NEXT BYTE SPECIFIER
CMPB COMBUF+1
BNE SB10 ;LOOP FOR ALL BYTES
*
* Return buffer with data from byte locations
SB90
*
* Compute checksum on buffer, and send to master, then return
SB99 BRA SEND
*===========================================================================
*
* Input from port: FN, len, PortAddressLo, PAhi (=0)
*
* While the HC12 has no input or output instructions, we retain these
* to allow write-without-verify
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
IN_PORT
*
* Get port address
LDAA 1,X ;MSB OF ADDRESS IN A
LDAB 0,X ;LSB OF ADDRESS IN B
TFR D,Y ;MEMORY ADDRESS IN Y
*
* Read the requested byte from local memory
LDAA 0,Y
*
* Return byte read as "status"
BRA SEND_STATUS
*===========================================================================
*
* Output to port: FN, len, PortAddressLo, PAhi (=0), data
*
* Entry with A=function code, B=data size, X=COMBUF+2
*
OUT_PORT
*
* Get port address
LDAA 1,X ;MSB OF ADDRESS IN A
LDAB 0,X ;LSB OF ADDRESS IN B
TFR D,Y ;MEMORY ADDRESS IN Y
*
* Get data
LDAA 2,X
*
* Write value to port
STAA 0,Y
*
* Do not read port to verify (some I/O devices don't like it)
*
* Return status of OK
LDAA #0
BRA SEND_STATUS
*===========================================================================
* Build status return with value from "A"
*
SEND_STATUS
STAA COMBUF+2 ;SET STATUS
LDAA #1
STAA COMBUF+1 ;SET LENGTH
** BRA SEND
*===========================================================================
* Append checksum to COMBUF and send to master
*
SEND BSR CHECKSUM ;GET A=CHECKSUM, X->checksum location
NEGA
STAA 0,X ;STORE NEGATIVE OF CHECKSUM
*
* Send buffer to master
LDX #COMBUF ;POINTER TO DATA
LDAB 1,X ;LENGTH OF DATA
ADDB #3 ;PLUS FUNCTION, LENGTH, CHECKSUM
SND10 LDAA 1,X+
JSR PUTCHAR ;SEND A BYTE
DBNE B,SND10
*
JMP MAIN ;BACK TO MAIN LOOP
*===========================================================================
* Compute checksum on COMBUF. COMBUF+1 has length of data,
* Also include function byte and length byte
*
* Returns:
* A = checksum
* X = pointer to next byte in buffer (checksum location)
* B is scratched
*
* Uses 2 bytes of stack including return address
*
CHECKSUM
LDX #COMBUF ;pointer to buffer
LDAB 1,X ;length of message
ADDB #2 ;plus function, length
CLRA ;init checksum to 0
CHK10 ADDA 1,X+
DBNE B,CHK10 ;loop for all
RTS ;return with checksum in A
***********************************************************************
*
* Interrupt handlers to catch unused interrupts and traps
* Registers are stacked. Jump through RAM vector using X, type in A
*
* This will affect only interrupt routines looking for register values!
*
* Our default handler uses the code in "A" as the processor state to be
* passed back to the host.
*
IC0_ENTRY LDAA #31 ;ffc0
LDX RAMVEC+0
JMP 0,X
IC2_ENTRY LDAA #30 ;ffc2
LDX RAMVEC+2
JMP 0,X
IC4_ENTRY LDAA #29 ;ffc4
LDX RAMVEC+4
JMP 0,X
IC6_ENTRY LDAA #28 ;ffc6
LDX RAMVEC+6
JMP 0,X
IC8_ENTRY LDAA #27 ;ffc8
LDX RAMVEC+8
JMP 0,X
ICA_ENTRY LDAA #26 ;ffca
LDX RAMVEC+$A
JMP 0,X
ICC_ENTRY LDAA #25 ;ffcc
LDX RAMVEC+$C
JMP 0,X
ICE_ENTRY LDAA #24 ;ffce
LDX RAMVEC+$E
JMP 0,X
ID0_ENTRY LDAA #23 ;ffd0
LDX RAMVEC+$10
JMP 0,X
ID2_ENTRY LDAA #22 ;ffd2 ATD
LDX RAMVEC+$12
JMP 0,X
ID4_ENTRY LDAA #21 ;ffd4 Serial Comm Port 1
LDX RAMVEC+$14
JMP 0,X
SCI0_ENTRY LDAA #20 ;ffd6 Serial Comm Port 0
LDX RAMVEC+$16
JMP 0,X
SPI_ENTRY LDAA #19 ;ffd8 Serial Peripheral Port
LDX RAMVEC+$18
JMP 0,X
PAIE_ENTRY LDAA #18 ;ffda Pulse Accumulator input
LDX RAMVEC+$1A
JMP 0,X
PAO_ENTRY LDAA #17 ;ffdc Pulse Accumulator overflow
LDX RAMVEC+$1C
JMP 0,X
TOF_ENTRY LDAA #16 ;ffde Timer Overflow
LDX RAMVEC+$1E
JMP 0,X
TC7_ENTRY LDAA #15 ;ffe0 Timer Channel 7
LDX RAMVEC+$20
JMP 0,X
TC6_ENTRY LDAA #14 ;ffe2 Timer Channel 6
LDX RAMVEC+$22
JMP 0,X
TC5_ENTRY LDAA #13 ;ffe4 Timer Channel 5
LDX RAMVEC+$24
JMP 0,X
TC4_ENTRY LDAA #12 ;ffe6 Timer Channel 4
LDX RAMVEC+$26
JMP 0,X
TC3_ENTRY LDAA #11 ;ffe8 Timer Channel 3
LDX RAMVEC+$28
JMP 0,X
TC2_ENTRY LDAA #10 ;ffea Timer Channel 2
LDX RAMVEC+$2A
JMP 0,X
TC1_ENTRY LDAA #9 ;ffec Timer Channel 1
LDX RAMVEC+$2C
JMP 0,X
TC0_ENTRY LDAA #8 ;ffee Timer Channel 0
LDX RAMVEC+$2E
JMP 0,X
RTI_ENTRY LDAA #7 ;fff0 Real Time Interrupt
LDX RAMVEC+$30
JMP 0,X
IRQ_ENT LDAA #6 ;fff2
LDX RAMVEC+$32
JMP 0,X
*
* Non-RAM vectored
SWI_ENTRY LDAA #1
JMP INT_ENTRY
XIRQ_ENTRY LDAA #2
JMP INT_ENTRY
ILLOP_ENT LDAA #5
JMP INT_ENTRY
*
* INTERRUPT VECTORS
ORG HARD_VECT
*
* VECTORS THROUGH RAM
* HC12D60 Interrupt Vectors....
VEC0 FDB IC0_ENTRY ;ffc0
FDB IC2_ENTRY ;ffc2
FDB IC4_ENTRY ;ffc4
FDB IC6_ENTRY ;ffc6
FDB IC8_ENTRY ;ffc8
FDB ICA_ENTRY ;ffca
FDB ICC_ENTRY ;ffcc
FDB ICE_ENTRY ;ffce
FDB ID0_ENTRY ;ffd0
FDB ID2_ENTRY ;ffd2
FDB ID4_ENTRY ;ffd4
FDB SCI0_ENTRY ;ffd6 Serial Comm Port 0
FDB SPI_ENTRY ;ffd8 Serial Peripheral Port
FDB PAIE_ENTRY ;ffda Pulse Accumulator input
FDB PAO_ENTRY ;ffdc Pulse Accumulator overflow
FDB TOF_ENTRY ;ffde Timer Overflow
FDB TC7_ENTRY ;ffe0 Timer Channel 7
FDB TC6_ENTRY ;ffe2 Timer Channel 6
FDB TC5_ENTRY ;ffe4 Timer Channel 5
FDB TC4_ENTRY ;ffe6 Timer Channel 4
FDB TC3_ENTRY ;ffe8 Timer Channel 3
FDB TC2_ENTRY ;ffea Timer Channel 2
FDB TC1_ENTRY ;ffec Timer Channel 1
FDB TC0_ENTRY ;ffee Timer Channel 0
FDB RTI_ENTRY ;fff0 Real Time Interrupt
FDB IRQ_ENT ;fff2
NVEC EQU *-VEC0 ;number of vector bytes
*
* The remaining interrupts are permanently trapped to the monitor
FDB XIRQ_ENTRY ;fff4 (non-maskable interrupt)
FDB SWI_ENTRY ;fff6 SWI/breakpoint
FDB ILLOP_ENT ;fff8 illegal op-code
FDB COP_ENT ;fffa Watchdog timeout
FDB CLOCK_ENT ;fffc clock monitor reset
FDB RESET ;fffe reset
*
END RESET