;*******************************************************************************
;* Program   : TBOOT.ASM
;* Programmer: Tony Papadimitriou <tonyp@acm.org>
;* Purpose   : Always-present Tiny Bootloader
;* Language  : Motorola/Freescale/NXP HC08/9S08 Assembly Language (aspisys.com/ASM8)
;* Status    : Copyright (c) 2017 by Tony Papadimitriou <tonyp@acm.org>
;* Segments  : RAM    : Variables
;*           : ROM    : Code
;* Note(s)   : User vectors are automatically redirected.
;*******************************************************************************

#ifdef ?
  #Hint ****************************************************
  #Hint * Available conditionals (for use with -Dx option) *
  #Hint ****************************************************
  #Hint * KHZ..............: MCU effective clock
  #Hint * FLASH_DATA_SIZE..: Flash size for user data
  #Hint * ALLOW_EEPROM.....: Allow EEPROM address range
  #Hint * QG8..............: Target is 9S08QG8
  #Hint * QE32.............: Target is 9S08QE32
  #Hint * QE128............: Target is 9S08QE128
  #Hint * DZ32.............: Target is 9S08DZ32
  #Hint * DZ60.............: Target is 9S08DZ60
  #Hint * FL16.............: Target is 9S08FL16
  #Hint * HARD_FLOW_CONTROL: For RTS/CTS control
  #Hint * RXINV............: SCI RX line inverted
  #Hint * TXINV............: SCI TX line inverted
  #Hint * BPS..............: BPS = 24/48/96/192/384/576(00)
  #Hint * SCI..............: SCI = 1 (SCI1) or 2 (SCI2)
  #Hint * ENABLE_RUN.......: Enable [R]un command
  #Hint * DISABLE_IRQ......: Disable IRQ pin test
  #Hint * DISABLE_SURE.....: Disable 'Sure?' message
  #Hint * DEBUG............: For debugging only
  #Hint ****************************************************
  #Fatal Run ASM8 -Dx (where x is any of the above)
#endif

BOOTROM_VERSION     def       116                 ;version as x.xx


;-------------------------------------------------------------------------------

SCI                 def       1                   ;SCI to use (1 or 2)

;-------------------------------------------------------------------------------

          #ifdef QE128
BOOTROM             def       $F800               ;QE128 version is a bit larger
          #else ifdef DZ32
BOOTROM             def       $FA00               ;DZ has different Flash protection
          #else ifdef DZ60
BOOTROM             def       $FA00               ;DZ has different Flash protection
          #endif
BOOTROM             def       $FC00

          #ifnz BOOTROM\512
                    #Error    BOOTROM is not on a 512-byte page boundary
          #endif

;-------------------------------------------------------------------------------

?                   macro
          #ifdef ~1~
FLASH_DATA_SIZE     def       ~2~
          #endif
                    endm

                    @?        QE128,1024
                    @?        DZ32,0              ; config storage in EEPROM, not Flash
                    @?        DZ60,0              ; config storage in EEPROM, not Flash
                    @?        GB60,1920

FLASH_DATA_SIZE     def       512

;-------------------------------------------------------------------------------

          #ifdef PRIVATE
NVOPT_VALUE         def       %10000000           ; NVOPT transfers to FOPT on reset
          #endif             ; ||||||||
NVOPT_VALUE         def       %00000010           ; NVOPT transfers to FOPT on reset
          #ifdef DZ32        ; ||||||||
NVOPT_VALUE  set  NVOPT_VALUE|%00100000           ; EPGMOD = 1 (8-byte mode)
          #else ifdef DZ60   ; ||||||||
NVOPT_VALUE  set  NVOPT_VALUE|%00100000           ; EPGMOD = 1 (8-byte mode)
          #endif             ; ||||||||
                             ; ||||||++---------- SEC00 \ 00:secure  10:unsecure
                             ; ||||||++---------- SEC01 / 01:secure  11:secure
                             ; |||+++------------ Not Used (Always 0)
                             ; ||+--------------- EPGMOD - EEPROM Sector Mode (DZ only) 1=8-byte mode
                             ; |+---------------- FNORED - No Vector Redirection
                             ; ++---------------- KEYEN - Backdoor key mechanism enable

          #ifndef MAP
                    #MapOff
          #endif
;-------------------------------------------------------------------------------
#ifmain
ROM                 equ       BOOTROM
;-------------------------------------------------------------------------------
          #ifdef QE128
HZ                  def       32768*512           ;MCU & Cyclone's default
BDIV                def       1
                    #MMU
                    #Uses     qe128.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef QE32
HZ                  def       32768*512           ;MCU & Cyclone's default
BDIV                def       1
                    #Uses     qe32.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef FL16
HZ                  def       16777216            ;Cyclone default 32768*512
BDIV                def       1
                    #Uses     fl16.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef GB60
HZ                  def       243000*64           ;MCU's default
BDIV                equ       1                   ;(actually, no BDIV in GB60)
                    #Uses     gb60.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef QD2
            #ifnz SCI
                    #Warning  No SCI with this MCU. What are you going to do?
            #endif
HZ                  def       32768*512           ;MCU & Cyclone's default
BDIV                def       1
                    #Uses     qd2.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef QD4
            #ifnz SCI
                    #Warning  No SCI with this MCU. What are you going to do?
            #endif
HZ                  def       32768*512           ;MCU & Cyclone's default
BDIV                def       1
                    #Uses     qd4.inc
          #endif
;-------------------------------------------------------------------------------
          #ifdef DZ32
HZ                  def       32000000            ;MCU & Cyclone's default
BDIV                def       1
                    #Uses     dz32.inc
                    #temp     NVOPT_VALUE>5&1     ;isolate EPGMOD
                    #Message  EPGMOD = {:temp} ({:temp*4+4}-byte mode)
          #endif
;-------------------------------------------------------------------------------
          #ifdef DZ60
HZ                  def       32000000            ;MCU & Cyclone's default
BDIV                def       1
                    #Uses     dz60.inc
                    #temp     NVOPT_VALUE>5&1     ;isolate EPGMOD
                    #Message  EPGMOD = {:temp} ({:temp*4+4}-byte mode)
          #endif
;-------------------------------------------------------------------------------
          #ifndef RAM
                    #Uses     qg8.inc
          #endif
;-------------------------------------------------------------------------------
                    #ListOn
                    #MapOn
                    #MCF
;-------------------------------------------------------------------------------
#endif
;-------------------------------------------------------------------------------

RVECTORS            def       BOOTROM-1&VECTORS

APP_CODE_START      def       TRUE_ROM+FLASH_DATA_SIZE
APP_CODE_END        def       BOOTROM-1
#Message  AppSpace: {APP_CODE_START(h)}-{APP_CODE_END(h)} ({APP_CODE_END-APP_CODE_START+1} bytes) RVECTORS: {RVECTORS(h)}

;-------------------------------------------------------------------------------

                    #XRAM
                    org       RAM                 ;used only for boot

                    #ROM
                    org       BOOTROM

;*******************************************************************************
; Macros
;*******************************************************************************

#ifnomdef ?Print
?Print              macro
                    mset      #
                    !jsr      Print
                    fcs       ~1~
                    endm
#endif

;-------------------------------------------------------------------------------

Page                macro
                    mset      #
                    #Message  +-------------------------------------------------
                    #Message  | ~1~
                    #Message  +-------------------------------------------------
                    endm

;*******************************************************************************

          #ifndef MAP
                    #MapOff
          #endif
LF2CRLF             def       *

;*******************************************************************************
@Page SCI module starts here
;*******************************************************************************

                    @ConstMinMax SCI,1,2

#ifdef HARD_FLOW_CONTROL
 #ifndef RTS_LINE
          #ifdef QE128
RTS_LINE            @pin      PORTE,5             ;RTS is input to MCU
          #else ifdef QE32
RTS_LINE            @pin      PORTC,6             ;RTS is input to MCU
          #endif
 #endif

 #ifndef CTS_LINE
          #ifdef QE128
CTS_LINE            @pin      PORTE,6             ;/CTS is output from MCU
          #else ifdef QE32
CTS_LINE            @pin      PORTC,7             ;/CTS is output from MCU
          #endif
 #endif
                    @CheckPin RTS_LINE,CTS_LINE
#endif

?                   macro
          #ifparm ~1~ = 1                         ;if SCI1
            #ifdef SCIBDH                         ;but unnumbered SCI case
                    mset      1                   ;remove number
            #endif
          #endif
                    #Message  Using SCI~1~
?SCIBDH             equ       SCI~1~BDH,1
?SCIBDL             equ       SCI~1~BDL,1
?SCIC1              equ       SCI~1~C1,1
?SCIC2              equ       SCI~1~C2,1
?SCIC3              equ       SCI~1~C3,1
?SCIS1              equ       SCI~1~S1,1
?SCIS2              equ       SCI~1~S2,1
?SCID               equ       SCI~1~D,1
                    endm

                    @?        {SCI}

                    @StandardBaudRates            ; Attempt to define all standard bps rates

#ifdef BPS
?                   macro
          #if BPS = ~{:loop}.~
MY_BPS_RATE         def       bps_~{:loop}.~
          #endif
                    mtop      :n
                    endm

                    @?        2400,4800,9600,19200,38400,57600
#endif

MY_BPS_RATE         def       bps_max

                    #Message  ==================================================
                    #Message  >>> Actual SCI speed: {BUS_HZ/16/MY_BPS_RATE} bps <<<
                    #Message  ==================================================

;*******************************************************************************
                    #ROM
;*******************************************************************************

                    @cop      #SAVE#

;*******************************************************************************
; Purpose: Set SCI BAUD rate to the specified value
; Input  : HX = Needed baud rate (must have taken care of BUSCLK as shown below)
; Note(s):                                                      BUSCLK     HZ
;        : Baud is calculated using this formula: SBR12:SBR0 = ------- = -------
;        :                                                     16*BAUD   32*BAUD
;        : Example: 9600 baud @ 20MHz bus speed, use value: 130

SetBAUD             proc
                    sthx      ?SCIBDH
#ifz ]?SCIC2
                    mov       #TE_|RE_,?SCIC2     ;Polled RX and TX mode
          #ifdef RXINV
                    #Message  SCI RX inverted
                    mov       #RXINV_,?SCIS2      ;RX inverted
          #else
                    clr       ?SCIS2
          #endif
          #ifdef TXINV
                    #Message  SCI TX inverted
                    mov       #TXINV_,?SCIC3      ;TX inverted
          #else
                    clr       ?SCIC3
          #endif
#else
                    lda       #TE_|RE_            ;Polled RX and TX mode
                    sta       ?SCIC2
          #ifdef RXINV
                    #Message  SCI RX inverted
                    lda       #RXINV_             ;RX inverted
          #else
                    clra
          #endif
                    sta       ?SCIS2
          #ifdef TXINV
                    #Message  SCI TX inverted
                    lda       #TXINV_             ;TX inverted
          #else
                    clra
          #endif
                    sta       ?SCIC3
#endif
          #ifdef HARD_FLOW_CONTROL

                    #Message  HARD_FLOW_CONTROL (RTS/CTS) enabled

                    @pullup   RTS_LINE,,-1

                    @Off      CTS_LINE            ;start with enabled RX (output)
                    @Input    RTS_LINE            ;always an input
          #endif
                    rts

;*******************************************************************************
; Purpose: Read SCI char into RegA
; Input  : None
; Output : A = received character
; Note(s):
                    #spauto

GetChar             proc
Loop@@
          #ifdef HARD_FLOW_CONTROL
                    bclr      CTS_LINE
          #endif
                    @cop

                    lda       ?SCIS1              ;wait for a character
                    bit       #RDRF_
                    beq       Loop@@

                    lda       ?SCID               ;get received character

                    beq       Loop@@              ;ignore Nulls
                    cbeqa     #LF,Loop@@          ;ignore LineFeeds

                    clc                           ;never an error from here
                    rts

;*******************************************************************************
; Purpose: Write RegA character to the SCI
; Input  : A = character to send to the SCI
; Output : None
; Note(s):
                    #spauto

PutChar             proc
          #ifdef LF2CRLF
                    cmpa      #LF                 ;is it LF?
                    bne       Print@@             ;no, continue as usual

                    lda       #CR                 ;yes, first print a CR
                    bsr       Print@@

                    lda       #LF                 ;next, print a LF
          #endif

Print@@             @cop

          #ifdef HARD_FLOW_CONTROL
                    brset     RTS_LINE,Print@@    ;wait for handshake
          #endif
          #ifz ]?SCIS1
                    tst       ?SCIS1
          #else
                    psha
                    lda       ?SCIS1
                    pula
          #endif
                    bpl       Print@@
                    sta       ?SCID
                    rts                           ;Carry Clear when exiting


;*******************************************************************************
@Page Common print I/O routines
;*******************************************************************************

;*******************************************************************************
; Purpose: Print constant ASCIZ string following caller instruction (with FCS)
; Input  : None
; Output : None
; Note(s):
                    #spauto

Print               proc
pc@@                equ       ::,2                ;SP offset to return address
                    pshhx
                    ldhx      pc@@,sp             ;get return address
                    bsr       ?PrintString
                    sthx      pc@@,sp
                    pulhx
                    rts                           ;back to updated return address

;*******************************************************************************
; Purpose: Write (send) a string to the SCI
; Input  : HX -> ASCIZ string, ie., Char1,Char2,...,0
; Output : None
; Note(s):
                    #spauto

WriteZ              proc
                    pshhx
                    bsr       ?PrintString
                    pulhx
                    rts

;*******************************************************************************
; Purpose: (LOCAL) Write (send) a string to the SCI
; Input  : HX -> ASCIZ string, ie., Char1,Char2,...,0
; Output : HX -> past ASCIZ string
; Note(s):
                    #spauto

?PrintString        proc
                    psha
Loop@@              lda       ,x                  ;get char to print
                    !aix      #1                  ;bump up pointer
                    beq       Done@@              ;on terminator, done
                    bsr       PutChar             ;print character
                    bra       Loop@@              ;repeat for all chars
Done@@              pula
                    rts

;*******************************************************************************
; Purpose: Display the copyright message on the SCI terminal
; Input  : None
; Output : None
; Note(s):
                    #spauto

ShowCopyright       proc
                    ldhx      #?CopyrightMsg
                    bsr       WriteZ
;                   bra       ShowSerial

;*******************************************************************************

                    #spauto

ShowSerial          proc
          #ifdef SERIAL_NUMBER
                    ldhx      #SERIAL_NUMBER

                    lda       ,x
                    cbeqa     #[ERASED_STATE,Done@@         ;All S/N should not have erased first byte ($FF)

                    @?print   'S/N: '
                    bsr       WriteZ
Done@@
          #endif
;                   bra       NewLine

;*******************************************************************************
; Purpose: Advance a line sending a CR,LF pair to the SCI
; Input  : None
; Output : None
; Note(s):
                    #spauto

NewLine             proc
                    psha
          #ifndef LF2CRLF
                    lda       #CR                 ;send a CR
                    bsr       PutChar
          #endif
                    lda       #LF                 ;send a LF
                    bsr       PutChar
                    pula
                    rts

;*******************************************************************************
@Page S19 module starts here
;*******************************************************************************


;*******************************************************************************
                    #XRAM
;*******************************************************************************

line_crc            rmb       1                   ;S-record CRC
address             rmb       2                   ;S-record address field
length              rmb       1                   ;S-record length field
rec_type            rmb       1                   ;S-record type field

;*******************************************************************************
                    #ROM
;*******************************************************************************

;*******************************************************************************
; Routine: LoadS19
; Purpose: Load an S19 file through the primary SCI port
; Input  : None
; Output : None
; Note(s): Only addresses within APP_CODE_START and APP_CODE_END and VECTORS
;        : are processed.
;        : ESC aborts

                    #spauto

LoadS19             proc
MainLoop@@          clr       line_crc            ;Initialize CRC to zero

SkipBlanks@@        jsr       ?GetChar            ;Get first/next character
                    beq       SkipBlanks@@        ;if EOL, skip blank lines
                    bcs       ??Error             ;abort on ESC

                    cbeqa     #'S',S@@            ;Probable S record

SkipLine@@          jsr       ?SkipToEOL          ;ignore rest of line
                    beq       MainLoop@@
                    bra       ??Error             ;abort on ESC

S@@                 jsr       ?GetChar            ;Get next character
                    bcs       ??Error             ;if ESC, get out with error

                    cbeqa     #'9',S9@@           ;Terminator record
                    cbeqa     #'1',S1@@           ;Code/data record
          #ifdef PPAGE
                    cbeqa     #'8',S8@@           ;Extended terminator record
                    cbeqa     #'2',S2@@           ;Extended address code/data record
          #endif
                    bra       SkipLine@@          ;skip S0 (header) or other lines

??Error             !jmp      ?Error

;-------------------------------------------------------------------------------
; Purpose: (LOCAL) Adjust the running CRC for the current record
; Input  : A = current byte
; Output : None
; Note(s):
                    #spauto

?UpdateCRC          proc
                    psha
                    add       line_crc
                    sta       line_crc
                    pula
                    rts
                    endp

;-------------------------------------------------------------------------------

S8@@
S9@@                @?print   '!'
                    bra       OK@@

S2@@
S1@@                @?print   '.'

OK@@                sta       rec_type            ;Save the record type

          ;Get length of Record Bytes (including 16-bit address and 8-bit CRC)

                    jsr       ReadHex             ;Get next 2 characters in binary
                    bcs       ??Error             ;if something wrong, get out with error
                    bsr       ?UpdateCRC

                    sub       #3                  ;adjust for 2-byte address and 1-byte CRC
                    sta       length              ;save Length of record (without address & CRC)

          ;Now, get the load address

          #ifdef PPAGE
                    mov       #2,PPAGE            ;assume default PPAGE for every new S record

                    lda       rec_type
                    cmpa      #'2'                ;S2 type record?
                    bne       GetAddress@@        ;if not, continue as usual

                    dec       length              ;adjust for 3rd address byte
                    jsr       ReadHex             ;get extended address byte (ppage)
                    bcs       ??Error
                    bsr       ?UpdateCRC
                    sta       PPAGE               ;update PPAGE for this record
          #endif

GetAddress@@        jsr       ReadHex             ;Get MSB of address
                    bcs       ??Error

                    sta       address             ;Save MSB of address
                    bsr       ?UpdateCRC

                    jsr       ReadHex             ;Get LSB of address
                    bcs       ?Error

                    sta       address+1           ;Save LSB of address
                    bsr       ?UpdateCRC

          ;Now, get the code/data bytes

                    tst       length              ;Check Length of zero
                    beq       DoCRC@@             ;Empty code/data section of record

Loop@@              jsr       ReadHex             ;get first/next data byte
                    bcs       ?Error              ;if something wrong, get out with error

                    bsr       ?UpdateCRC

          ;load-time CRC calculation may be added here, if required

          ;save byte and advance pointer

                    ldhx      address             ;Get address in HX
                    bsr       CheckAddr@@         ;Check address to be
                    bcs       RangeError@@        ; within valid Flash limits

                    cphx      #VECTORS
                    blo       Save@@

          #ifz RVECTORS-VECTORS&$FF
                    psha
                    tha
                    add       #RVECTORS-VECTORS>8&$FF
                    tah
                    pula
          #else
                    aix       #RVECTORS-VECTORS   ;redirector to user vectors
          #endif

Save@@              jsr       FlashWrite          ;Save to Flash
                    beq       NextByte@@

                    @?print   BS,'F'              ;Flash error indicator

NextByte@@          ldhx      address
                    !aix      #1                  ;Adjust the PC value by 1
                    sthx      address
                    dbnz      length,Loop@@       ;One less byte to read

;-------------------------------------------------------------------------------

DoCRC@@             bsr       ReadHex             ;Get CRC byte
                    bcs       ?Error              ;if something wrong, get out with error

                    com       line_crc            ;Get one's complement of final CRC value
                    cbeq      line_crc,GoNext@@   ;Is it the same as the one calculated. Yes, continue

                    @?print   BS,'C'              ;CRC error indicator

          ;See if we're done (i.e., if we just processed an S9 record)

GoNext@@            bsr       ?SkipToEOL          ;Clean up to the end of line
                    lda       rec_type            ;Check record type
                    cbeqa     #'9',?Success       ;Done, get out without errors
          #ifdef PPAGE
                    cbeqa     #'8',?Success       ;Done, get out without errors
          #endif
                    jmp       MainLoop@@          ;Go back to read another line

;-------------------------------------------------------------------------------

RangeError@@        @?print   BS,'R'              ;Address Range error indicator
                    bra       NextByte@@          ;skip error byte

;-------------------------------------------------------------------------------

?SkipToEOL          proc
                    bsr       ?GetChar
                    bcc       ?SkipToEOL
?Error              sec
                    rts
                    endp

;-------------------------------------------------------------------------------
; Purpose: Check address to be within range
; Input  : HX = address
; Output : Carry Clear if within valid ranges
;        : Carry Set if outside valid ranges
; Note(s):

CheckAddr@@
          #ifdef PPAGE

          ;PPAGE 2 (startup default) has a different allowable range

                    @cmp.s    PPAGE,#2            ;for all but the default page
                    beq       CheckAddr@@@

          ;do a single PPAGE address range check and exit

                    cphx      #:PAGE_START
                    blo       ?Error

                    cphx      #:PAGE_END
                    bhi       ?Error

                    clc
                    rts
          #endif

CheckAddr@@@

          #ifdef ALLOW_EEPROM
                    #Message  EEPROM is allowed
                    cphx      #EEPROM
                    blo       Go@@

                    cphx      #EEPROM_END
                    bls       ?Success
          #endif
Go@@                cphx      #APP_CODE_START     ;Check address to be
                    blo       ?Error              ; within valid Flash

                    cphx      #VECTORS            ; limits.
                    bhs       ?Success            ;Vectors are passed

                    cphx      #APP_CODE_END       ; as is, and redirected
                    bhi       ?Error              ; automatically by loader.

          #if HighRegs > APP_CODE_START
                    cphx      #HighRegs           ;Check address for hole
                    blo       ?Success            ; in Flash created by

                    cphx      #HighRegs_End       ; HighRegs (certain MCUs only)
                    bls       ?Error
          #endif
?Success            clc                           ;no errors from here
                    rts

;*******************************************************************************
; Purpose: Read next character from S19 file
; Input  : None
; Output : A = next S19 file character converted to uppercase
;        : CCR[C] = 1 and CCR[Z] = 1 if CR received (normal end-of-line)
;        : CCR[C] = 1 and CCR[Z] = 0 if ESC received (abort)
;        : CCR[C] = 0 and CCR[Z] = 0 for any other character
; Note(s):
                    #spauto

?GetChar            proc
                    jsr       GetChar             ;Get a character

                    cmpa      #CR                 ;Z=1 if CR found, Z=0 anything else
                    beq       ?Error              ;(do NOT change to CBEQA)

                    clc                           ;assume no error

                    cbeqa     #ESC,?Error         ;ESC cancels
;                   bra       Upcase

;*******************************************************************************
; Purpose: Convert one character to uppercase
; Input  : A = character
; Output : A = CHARACTER
; Note(s): Protects caller's CCR

                    #spauto

Upcase              proc
                    pshx
                    tpx                           ;(transfer CCR to X)

                    cmpa      #'a'
                    blo       Done@@

                    cmpa      #'z'
                    bhi       Done@@

                    add       #'A'-'a'

Done@@              txp                           ;(transfer X to CCR)
                    pulx
                    rts

;*******************************************************************************
; Purpose: Read two-digit ASCII hex from SCI and convert to binary value in A
; Input  : None
; Output : A = binary value
; Note(s): Destroys HX

                    #spauto

ReadHex             proc
                    bsr       ?GetChar            ;Get next character
                    bcs       Done@@              ;if EOL, get out with error

                    jsr       HexByte             ;Convert from Hex to Binary
                    bcs       Done@@              ;if EOL, get out with error

                    nsa                           ;Move to high nibble
                    tax                           ;save temporarily in X

                    bsr       ?GetChar            ;Get next character
                    bcs       Done@@              ;if EOL, get out with error

                    jsr       HexByte             ;Convert from Hex to Binary
                    bcs       Done@@              ;Invalid character, ignore rest of line

                    pshx      tmp@@
                    tsx                           ;20131006 addition
                    ora       tmp@@,spx           ;combine LSN with MSN
                    pulx

;                   clc                           ;indicate "no error" (valid since BCS fall thru)
Done@@              rts

;*******************************************************************************
@Page Flash module starts here
;*******************************************************************************

;*******************************************************************************
; Flash programming command codes

mBlank              def       $05                 ;Blank check
mByteProg           def       $20                 ;Byte programming
mBurstProg          def       $25                 ;Burst programming
mPageErase          def       $40                 ;Page erase
mMassErase          def       $41                 ;Mase erase

;*******************************************************************************
; Purpose: RAM routine to do the job we can't do from Flash
; Input  : A = value to program
; Output : None
; Note(s): This routine is modified in RAM at zero-based offsets
;        : @1, @2 (address) and @4 (Flash command)
;        : RAM needed: 20 bytes

                    #spauto

RAM_Code            proc
                    sta       $FFFF               ;Step 1 - Latch data/address
Addr_Offset         equ       :pc-RAM_Code-2,2    ;$FFFF (@1,@2) replaced with actual address during RAM copying
                    lda       #mByteProg          ;mByteProg (@4) replaced with actual command during RAM copying
Cmd_Offset          equ       :pc-RAM_Code-1,1
                    sta       FCMD                ;Step 2 - Write command to FCMD

                    lda       #FCBEF_             ;Step 3 - Write FCBEF_ in FSTAT
                    sta       FSTAT

                    lsra                          ;min delay before checking FSTAT (four bus cycles)
                                                  ;instead of NOP (moves FCBEF -> FCCF for later BIT)
Loop@@              bit       FSTAT               ;Step 4 - Wait for completion
                    beq       Loop@@              ;check FCCF_ for completion
                    rts                           ;after exit, check FSTAT for FPVIOL and FACCERR

                    #size     RAM_Code

;*******************************************************************************
                    #XRAM
;*******************************************************************************

BurnRoutine         rmb       ::RAM_Code

BurnAddress         equ       BurnRoutine+Addr_Offset,2
BurnCommand         equ       BurnRoutine+Cmd_Offset,1

;*******************************************************************************
                    #ROM
;*******************************************************************************

;*******************************************************************************
; Purpose: Program an internal Flash location
; Input  : HX -> Flash memory location to program
;        : A = value to write
; Output : CCR[Z] on success
; Note(s): Does not program (skips) non-erased locations

                    #spauto

FlashWrite          proc
                    cmpa      ,x                  ;(do NOT replace with CBEQ)
                    beq       Done@@              ;value already there, no need to update

          #ifz ERASED_STATE
                    tst       ,x                  ;test if erased, and if not
          #else
                    psha
                    lda       ,x                  ;if not erased already
                    coma
                    pula
          #endif
                    bne       Done@@              ;skip (and report as failure, CCR[Z]=0)

                    bsr       PrepareFlash

                    mov       #mByteProg,BurnCommand  ;save command within LDA #?? instruction
;                   bra       ?FlashIt

Done@@              equ       :AnRTS

;-------------------------------------------------------------------------------
; Purpose: Call RAM routine
; Input  : HX = address to Flash
;        : BurnCommand already set with Flash command
;        : A = value to program
; Output : A = FSTAT
;        : CCR[Z] = 1 on success
; Note(s): Destroys all registers

                    #spauto

?FlashIt            proc
                    sthx      BurnAddress         ;save HX within STA $FFFF instruction
;                   pshcc
;                   sei                           ;disable interrupts (never enabled in this app)
                    @cop                          ;reset COP (for maximum tolerance)
                    jsr       BurnRoutine         ;execute RAM routine to perform Flash command
;                   pulcc

                    lda       FSTAT
                    bit       #FPVIOL_|FACCERR_
                    rts

;*******************************************************************************
; Purpose: Erase an internal Flash page by address
; Input  : HX -> location within page to erase
; Output : CCR[Z] on success
; Note(s): Forces address past HighRegs (if any).

                    #spauto

FlashErase          proc
          #ifdef HighRegs
                    cphx      #HighRegs
                    blo       Continue@@

                    cphx      #HighRegs_End
                    bhi       Continue@@

                    ldhx      #HighRegs_End+1
Continue@@
          #endif
                    bsr       PrepareFlash

                    mov       #mPageErase,BurnCommand  ;save command within LDA #?? instruction
                    bra       ?FlashIt

;*******************************************************************************
;*                         Supporting routines
;*******************************************************************************

;*******************************************************************************
; Purpose: Prepare Flash for programming
; Input  : None
; Output : None
; Note(s): Makes FCLK fall between 150-200KHz [FCLK=FBUS/(DIV+1)] and DIV=0..63

                    #spauto

PrepareFlash        proc
                    push

                    lda       FCDIV
                    bmi       Done@@

                    lda       #FLASH_CLK_VALUE    ;required to allow further
                    sta       FCDIV               ;access to Flash programming

Done@@              lda       #FPVIOL_|FACCERR_
                    sta       FSTAT               ;clear possible errors

                    pull
                    rts

;*******************************************************************************

                    #MapOn

?CopyrightMsg       fcc       ASCII_FF            ;a Form Feed (for CLS)
CopyrightMsg        fcc       'TBoot v{BOOTROM_VERSION(2)} (c) {:year} ASPiSYS'
                    fcs       ' {HZ/1000(3)} MHz'

;*******************************************************************************
; Purpose: Initialize the MCU with default settings
; Input  : None
; Output : None
; Note(s):
                    #spauto

Initialize          proc

          #ifdef NVICSTRM
                    lda       NVICSTRM
                    cbeqa     #[ERASED_STATE,SkipTrim@@
                    sta       ICSTRM
SkipTrim@@          lda       NVFTRIM
                    and       #FTRIM_
            #ifdef DRS1_&DRS0_
              #if MHZ >= 48
                    ora       #DRS1_              ;high DCO range (%01) x1536
              #else if MHZ >= 32
                    ora       #DRS0_              ;middle DCO range (%01) x1024
              #endif
            #endif
                    sta       ICSSC
          #endif

SOPT_VALUE          def       %00100010
                             ; ||||||||
                             ; |||||||+---------  RSTPE - RST pin enable
                             ; ||||||+----------  BKGDPE - BKGD/MS pin for debugging only
                             ; |||||+-----------  RSTOPE - RTSO pin enable
                             ; |||++------------  Not Used
                             ; ||+--------------  STOPE - Stop Mode Enable
                             ; |+---------------  COPT - COP Timeout (0=Short [32msec], 1=Long [256msec])
                             ; +----------------  COPE - COP Enable

          #ifdef QE128
          #ifnz SOPT_VALUE&COPE_
                    #Warning  COPE does not work well with buggy (QE128) chips
          #endif
          #endif

                    lda       #SOPT_VALUE
                    sta       SOPT                ;write-once register

;         #ifdef ICSC1
;           #ifz ]ICSC1
;                   mov       #%00000111|RDIV_,ICSC1
;                             ; |||||||+--------- IREFSTEN
;                             ; ||||||+---------- IRCLKEN
;                             ; |||||+----------- IREFS
;                             ; ||+++------------ RDIV
;                             ; ++--------------- CLKS
;           #else
;                   lda       #%00000111|RDIV_
;                   sta       ICSC1
;           #endif
;         #endif

          #ifdef ICSC2
            #ifz ]ICSC2
                    mov       #%00000000|BDIV_|RANGE_,ICSC2
                              ; |||||||+--------- EREFSTEN
                              ; ||||||+---------- ERCLKEN
                              ; |||||+----------- EREFS
                              ; ||||+------------ LP
                              ; |||+------------- HGO
                              ; ||+-------------- RANGE
                              ; ++--------------- BDIV
            #else
                    lda       #%00000000|BDIV_|RANGE_
                    sta       ICSC2
            #endif
          #endif
;                   bra       CopyRamCode

;*******************************************************************************
; Purpose: Copy programming routine from Flash to RAM
; Input  : None
; Output : None
; Note(s):
                    #spauto

CopyRamCode         proc
          #if ::RAM_Code <= 256                   ;(most likely scenario)
                    ldhx      #::RAM_Code         ;(do NOT use CLRH, LDX)
Loop@@              lda       RAM_Code-1,x
                    sta       BurnRoutine-1,x
                    dbnzx     Loop@@              ;repeat for all bytes
          #else
                    clrhx
Loop@@              lda       RAM_Code,x
                    sta       BurnRoutine,x
                    !aix      #1                  ;point to next byte to process
                    cphx      #::RAM_Code         ;are we done?
                    blo       Loop@@
          #endif
                    rts

;*******************************************************************************
; Fixed vectors are always negative offsets in multiples of 2 from Reset Vector
; From your app, you can access this way:
;                   ldhx      Vreset
;                   ldhx      -2,x
; (where -2 is the appropriate object offset)
;*******************************************************************************

                    dw        Monitor             ;@-4 Monitor entry point
                    dw        CopyrightMsg        ;@-2 Copyright ASCIZ string

;*******************************************************************************
;                    PROGRAM EXECUTION STARTS HERE
;*******************************************************************************

                    #spauto

Start               proc
                    @rsp
                    @cop

                    bmc       Monitor             ;in user code entry ints are enabled

                    mov       #IRQPE_,IRQSC
                              ; |||||||+--------- IRQMOD (0=edge, 1=level)
                              ; ||||||+---------- IRQIE (1=Interrupts Enabled)
                              ; |||||+----------- IRQACK
                              ; ||||+------------ IRQF
                              ; |||+------------- IRQPE (1=Pin Enabled)
                              ; ||+-------------- IRQEDG (0=Falling Edge)
                              ; |+--------------- IRQPDD (1=Pulls Disabled)
                              ; +---------------- Always zero

                    !jsr      cmdRun
;                   bra       Monitor

;*******************************************************************************

Monitor             proc
                    sei                           ;just in case we entered from user code
                    @rsp                          ;         -//-
                    bsr       Initialize

          ;--- Initialize the RS232 communications channel

          #ifdef SetBAUD
                    ldhx      #MY_BPS_RATE
                    jsr       SetBAUD
          #endif
                    jsr       ShowCopyright       ;display copyright message for boot loader
;                   bra       MainLoop

;*******************************************************************************
; Main loop accepts three commands (E-rase, L-oad, or ESC-ape)
;*******************************************************************************

                    #spauto

MainLoop            proc
Loop@@
          #ifdef ENABLE_RUN
                    @?print   LF,'[E]rase, [L]oad, [R]un, [ESC]ape:'
          #else
                    @?print   LF,'[E]rase, [L]oad, [ESC]ape:'
          #endif
                    jsr       GetChar
                    jsr       Upcase

                    cbeqa     #ESC,ForceReset     ;(does not return)
                    cbeqa     #'E',Erase@@
                    cbeqa     #'L',Load@@
          #ifdef ENABLE_RUN
                    cbeqa     #'R',Run@@
          #endif
                    bra       Fail@@              ;print ERROR on wrong cmd
          #ifdef ENABLE_RUN
Run@@               !jsr      RunNow              ;we need JSR in case no code is found
                    bra       Fail@@
          #endif
Erase@@
          #ifndef DISABLE_SURE
                    @?print   LF,'Sure?'
                    jsr       GetChar
                    cmpa      #'Y'
                    bne       Fail@@
          #endif
                    @?print   CR,'Erasing'
                    !jsr      EraseFlash
                    bcc       Loop@@
                    bra       Fail@@

Load@@              @?print   LF,'Loading'
                    jsr       LoadS19
                    bcc       Loop@@

Fail@@              @?print   ' Error'
                    bra       Loop@@

;*******************************************************************************

ForceReset          reset                         ; force a reset

;*******************************************************************************
; Purpose: Check if app firmware is available (based on relocated reset vector)
; Input  : None
; Output : HX = execution address
;        : Carry Clear = BOOTMODE enabled
; Note(s):
                    #spauto

IsAppPresent        proc

                    ldhx      Vreset&$FF|RVECTORS

                    cphx      #APP_CODE_START
                    blo       Fail@@

                    cphx      #BOOTROM
                    bhs       Fail@@

                    clc
                    rts
?No
Fail@@              sec
                    rts

;*******************************************************************************
;                     M O N I T O R   C O M M A N D S
;*******************************************************************************

                    #spauto

cmdRun              proc
          #ifdef DISABLE_IRQ
                    brn       *                   ;keep image size the same as when BIL is used
          #else
                    bil       Done@@              ;force entry to Monitor mode with low IRQ pin
          #endif
;                   bra       RunNow
Done@@              equ       :AnRTS

;*******************************************************************************

RunNow              proc
                    bsr       IsAppPresent
                    bcs       Done@@              ;erased vector never executes
                    clra
                    sta       IRQSC               ;restore IRQ to reset default
          #ifdef ?SCIC1
                    sta       ?SCIC1              ;restore all SCI registers
                    sta       ?SCIC2              ;... to their default status
                    sta       ?SCIBDH
                    lda       #4
                    sta       ?SCIBDL
          #endif
                    sthx      $FE                 ;address at top of page zero
                    @lds      #$FE                ;set default out-of-reset SP
                    clra
                    clrhx
Done@@              RTS                           ;return OR execute user code

;*******************************************************************************
; Purpose: Convert an ASCII hex byte '0' to 'F' to binary value
; Input  : A = hex ASCII
; Output : A = binary equivalent, Carry Clear
;        : Carry Set if digits not in 0-9, A-F/a-f character set
; Note(s):
                    #spauto

HexByte             proc
                    jsr       Upcase

                    cmpa      #'0'
                    blo       Done@@              ;Fail@@ really (but CCR[C]=1 already)

                    cmpa      #'F'
                    bhi       Fail@@

                    cmpa      #'9'
                    bls       Number@@

                    cmpa      #'A'
                    blo       Done@@              ;Fail@@ really (but CCR[C]=1 already)

                    sub       #'A'-10-'0'

Number@@            sub       #'0'
;                   clc                           ;(redundant due to positive SUB result)
Done@@              rts

Fail@@              equ       ?No


;*******************************************************************************

                    #spauto

          #ifdef PPAGE

EraseFlash          proc
                    clr       PPAGE

NewPage@@           ldhx      #:PAGE_START        ;user firmware's first page

Loop@@              pshhx
                    jsr       FlashErase
                    pulhx
;;;;;;;;;;;;;;;;;;; bne       Fail@@

                    aix       #FLASH_PAGE_SIZE

                    cphx      #:PAGE_END
                    blo       Loop@@              ;repeat for all Flash pages

                    inc       PPAGE
                    tst       PPAGE               ;required, INC result CCR[Z] is invalid
                    bne       NewPage@@           ;repeat for all PPAGEs

                    clc
                    rts

;-------------------------------------------------------------------------------
          #else
;-------------------------------------------------------------------------------

EraseFlash          proc
                    ldhx      #FLASH_START        ;user firmware's first page

Loop@@              pshhx
                    jsr       FlashErase
                    pulhx
                    bne       Fail@@

                    aix       #FLASH_PAGE_SIZE
                    cphx      #BOOTROM            ;user firmware's last page
                    blo       Loop@@              ;repeat for all Flash pages
                    rts

Fail@@              equ       ?No
          #endif

;*******************************************************************************
          #ifdef QE128

?                   macro     RealVector
                    mset      2,VECTORS-RVECTORS  ;offset for moving vectors
?~1.2~_vector       set       ~1~-{~2~}
                    endm

                    @?        Vtpm3ovf            ;TPM3 overflow vector
                    @?        Vtpm3ch5            ;TPM3 channel 5 vector
                    @?        Vtpm3ch4            ;TPM3 channel 4 vector
                    @?        Vtpm3ch3            ;TPM3 channel 3 vector
                    @?        Vtpm3ch2            ;TPM3 channel 2 vector
                    @?        Vtpm3ch1            ;TPM3 channel 1 vector
                    @?        Vtpm3ch0            ;TPM3 channel 0 vector
                    @?        Vrtc                ;RTC vector
                    @?        Vsci2tx             ;SCI2 TX vector
                    @?        Vsci2rx             ;SCI2 RX vector
                    @?        Vsci2err            ;SCI2 Error vector
                    @?        Vacmpx              ;ACMP - Analog Comparator
                    @?        Vadc                ;Analog-to-Digital conversion
                    @?        Vkeyboard           ;Keyboard vector
                    @?        Viicx               ;IIC vector
                    @?        Vsci1tx             ;SCI1 TX vector
                    @?        Vsci1rx             ;SCI1 RX vector
                    @?        Vsci1err            ;SCI1 Error vector
                    @?        Vspi1               ;SPI1 vector
                    @?        Vspi2               ;SPI2 vector
                    @?        Vtpm2ovf            ;TPM2 overflow vector
                    @?        Vtpm2ch2            ;TPM2 channel 2 vector
                    @?        Vtpm2ch1            ;TPM2 channel 1 vector
                    @?        Vtpm2ch0            ;TPM2 channel 0 vector
                    @?        Vtpm1ovf            ;TPM1 overflow vector
                    @?        Vtpm1ch2            ;TPM1 channel 2 vector
                    @?        Vtpm1ch1            ;TPM1 channel 1 vector
                    @?        Vtpm1ch0            ;TPM1 channel 0 vector
                    @?        Vlvd                ;low voltage detect vector
                    @?        Virq                ;IRQ pin vector
                    @?        Vswi                ;SWI vector

;===============================================================================

?                   macro
?~1~                @ReVector ?~1~_vector
                    endm

                    @?        tpm3ovf
                    @?        tpm3ch5
                    @?        tpm3ch4
                    @?        tpm3ch3
                    @?        tpm3ch2
                    @?        tpm3ch1
                    @?        tpm3ch0
                    @?        rtc
                    @?        sci2tx
                    @?        sci2rx
                    @?        sci2err
                    @?        acmpx
                    @?        adc
                    @?        keyboard
                    @?        iicx
                    @?        sci1tx
                    @?        sci1rx
                    @?        sci1err
                    @?        spi1
                    @?        spi2
                    @?        tpm2ovf
                    @?        tpm2ch2
                    @?        tpm2ch1
                    @?        tpm2ch0
                    @?        tpm1ovf
                    @?        tpm1ch2
                    @?        tpm1ch1
                    @?        tpm1ch0
                    @?        lvd
                    @?        irq
                    #Cycles
                    @?        swi
                    #Message  Vector redirection overhead: {:cycles} cycles
          #endif

;*******************************************************************************

          #ifdef QE128
?                   macro
                    @vector   ~1~,?~1.2~
                    endm

                    @?        Vtpm3ovf            ; TPM3 overflow vector
                    @?        Vtpm3ch5            ; TPM3 channel 5 vector
                    @?        Vtpm3ch4            ; TPM3 channel 4 vector
                    @?        Vtpm3ch3            ; TPM3 channel 3 vector
                    @?        Vtpm3ch2            ; TPM3 channel 2 vector
                    @?        Vtpm3ch1            ; TPM3 channel 1 vector
                    @?        Vtpm3ch0            ; TPM3 channel 0 vector
                    @?        Vrtc                ; RTC (periodic interrupt) vector
                    @?        Vsci2tx             ; SCI2 transmit vector
                    @?        Vsci2rx             ; SCI2 receive vector
                    @?        Vsci2err            ; SCI2 error vector
                    @?        Vacmpx              ; ACMPX vector
                    @?        Vadc                ; Analog-to-Digital conversion
                    @?        Vkeyboard           ; Keyboard vector
                    @?        Viicx               ; IIC vector
                    @?        Vsci1tx             ; SCI1 transmit vector
                    @?        Vsci1rx             ; SCI1 receive vector
                    @?        Vsci1err            ; SCI1 error vector
                    @?        Vspi1               ; SPI1 vector
                    @?        Vspi2               ; SPI2 vector
                    @?        Vtpm2ovf            ; TPM2 overflow vector
                    @?        Vtpm2ch2            ; TPM2 channel 2 vector
                    @?        Vtpm2ch1            ; TPM2 channel 1 vector
                    @?        Vtpm2ch0            ; TPM2 channel 0 vector
                    @?        Vtpm1ovf            ; TPM1 overflow vector
                    @?        Vtpm1ch2            ; TPM1 channel 2 vector
                    @?        Vtpm1ch1            ; TPM1 channel 1 vector
                    @?        Vtpm1ch0            ; TPM1 channel 0 vector
                    @?        Vlvd                ; Low Voltage Detect vector
                    @?        Virq                ; /IRQ pin vector
                    @?        Vswi                ; SWI vector
          #endif
                    @vector   Vreset,Start        ; /RESET vector

                    end       :s19crc

;*******************************************************************************

BOOTRAM_END         def       :RAM

;*******************************************************************************
                    #Export   APP_CODE_START,APP_CODE_END
                    #Export   BOOTROM,BOOTRAM_END,BOOTROM_VERSION
                    #Export   RVECTORS
;*******************************************************************************
                    @EndStats