*  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
