;   ***************************************************************
;   * Copyright (C) 2003, Embed Inc (http://www.embedinc.com)     *
;   *                                                             *
;   * Permission to copy this file is granted as long as this     *
;   * copyright notice is included in its entirety at the         *
;   * beginning of the file, whether the file is copied in whole  *
;   * or in part and regardless of whether other information is   *
;   * added to the copy.                                          *
;   *                                                             *
;   * The contents of this file may be used in any way,           *
;   * commercial or otherwise.  This file is provided "as is",    *
;   * and Embed Inc makes no claims of suitability for a          *
;   * particular purpose nor assumes any liability resulting from *
;   * its use.                                                    *
;   ***************************************************************
;   Top module for the QQ2 project.  See the QQ2.INS.DSPIC include
;   file for a description of the project.
/include "qq2.ins.dspic"

  /var local s string

  /set s [str "Fosc = " [eng freq_osc oscdig] "Hz"]
  /set s [str s ", Fcy = " [eng freq_inst oscdig] "Hz"]
  /set s [str s ", Tcy = " [eng [/ 1 freq_inst] oscdig] "s"]
  /show "  " s
/if [exist "fwtype"]
  /then                      ;FWTYPE exists
    /show "  Firmware type ID = " fwtype
  /else                      ;FWTYPE does not exist
    /show "  Firmware type ID not defined"
/if debug_icd then
  /show "  RealIce debugging enabled"

;   Static processor configuration settings.
.section .configbits, code, address(0x557EC)

         .pword  0xFFFFFF    ;reserved
         .pword  0xFFFFFF    ;reserved

         .pword  0b000000001111111111001111 ;FICD, 557F0
                 ; 0000000011111111-------- unused, high 8 bits 0 makes NOP instruction
                 ; ----------------1------- reserved, must be set to 1
                 ; -----------------1------ unused
                 ; ------------------0----- disable JTAG
                 ; -------------------0---- reserved, must be set to 0
                 ; --------------------1--- reserved, must be set to 1
                 ; ---------------------1-- unused
                 ; ----------------------11 ICD interface uses PGEC1/PGED1

         .pword  0b000000001111111100110111 ;FPOR, 557F2
                 ; 1111111111111111-------- unused, all 1s makes NOP instruction
                 ; ----------------00------ WDT window is 75% of period
                 ; ------------------1----- IIC2 uses SDA2/SCL2 pins
                 ; -------------------1---- IIC1 users SDA1/SCL1 pins
                 ; --------------------0--- disable brownout reset
                 ; ---------------------111 unused

         .pword  0b000000001111111101111111 ;FWDT, 557F4
                 ; 1111111111111111-------- unused, all 1s makes NOP instruction
                 ; ----------------0------- WDT not always enabled
                 ; -----------------1------ WDT in non-window mode
                 ; ------------------1----- enable PLL lock
                 ; -------------------1---- WDT prescalser 128, not 32
                 ; --------------------1111 WDT postscaler to max of 32768

         .pword  0b000000001111111101011110 ;FOSC, 557F6
                 ; 1111111111111111-------- unused, all 1s makes NOP instruction
                 ; ----------------01------ clock switching enabled, failsafe clk disabled
                 ; ------------------0----- allow multiple PPS changes
                 ; -------------------11--- unused
                 ; ---------------------1-- not used with external crystal oscillator
                 ; ----------------------10 primary oscillator mode is HS

         .pword  0b000000001111111100111010 ;FOSCSEL, 557F8
                 ; 1111111111111111-------- unused, all 1s makes NOP instruction
                 ; ----------------0------- start with selected osc mode, not FRC
                 ; -----------------0------ don't require key sequence for PWM registers
                 ; ------------------111--- unused
                 ; ---------------------010 start with primary osc, not PLL

         .pword  0b000000001111111111111111 ;FGS, 557FA
                 ; 1111111111111111-------- unused, all 1s makes NOP instruction
                 ; ----------------111111-- unused
                 ; ----------------------1- disable user memory code protection
                 ; -----------------------1 disable user memory write protection

         .pword  0x00FFFF    ;reserved, 557FC
         .pword  0x00FFFF    ;reserved, 557FE

;   Reserve RAM for the ICD2.  It needs exclusive control over the first 80
;   bytes.
/if debug_icd then
.equiv   __ICD2RAM, 1        ;linker reserves ICD RAM when this symbol defined
.global  __ICD2RAM

;   Define the global flag words.  These are defined in near RAM so that bit
;   manipulation instructions can be used on them directly.  NFLAGB flag words
;   need to be defined.

;   Constants in program memory so that HEX file editing tools know the version
;   of this firmware.
.section .code_fwinfo, code, address(0x800)
         .pword  fwtype | (fwver << 8) | (fwseq << 16)

.section .code_strt, code
;   Start of exeuctable code.
/if using_c
         glbent  _main       ;C runtime library jumps here after initialization
         glbent  __reset     ;jumps here directly from reset vector
;   Initialize the interrupt system.
         clr     Iec0        ;disable all interrupts
.ifdef Iec1
         clr     Iec1
.ifdef Iec2
         clr     Iec2
.ifdef Iec3
         clr     Iec3
.ifdef Iec4
         clr     Iec4

.ifdef Ipc0                  ;init all interrupt priorties to 0 (disabled)
         clr     Ipc0
.ifdef Ipc1
         clr     Ipc1
.ifdef Ipc2
         clr     Ipc2
.ifdef Ipc3
         clr     Ipc3
.ifdef Ipc4
         clr     Ipc4
.ifdef Ipc5
         clr     Ipc5
.ifdef Ipc6
         clr     Ipc6
.ifdef Ipc7
         clr     Ipc7
.ifdef Ipc8
         clr     Ipc8
.ifdef Ipc9
         clr     Ipc9
.ifdef Ipc10
         clr     Ipc10
.ifdef Ipc11
         clr     Ipc11
.ifdef Ipc12
         clr     Ipc12
.ifdef Ipc13
         clr     Ipc13
.ifdef Ipc14
         clr     Ipc14
.ifdef Ipc15
         clr     Ipc15
.ifdef Ipc16
         clr     Ipc16
.ifdef Ipc17
         clr     Ipc17
.ifdef Ipc18
         clr     Ipc18
.ifdef Ipc19
         clr     Ipc19
.ifdef Ipc20
         clr     Ipc20

         clr     Intcon1     ;initialize interrupt system to defaults
         clr     Intcon2
         clr     Sr          ;make sure running with interrupt priority 0
.ifdef Gie
         bset    Intcon2, #Gie ;globally allow interrupts

         flags_clear         ;initialize all the global flags to off
;   Set up the stack and the heap if the Embed DYMEM heap is in use.  The linker
;   is set up so that the suggested SPLIM value is the first address past the
;   end of the stack.  In other words, no guard band is reserved at the end of
;   the stack.  We set up the task 0 stack here with 6 bytes (3 words) of guard
;   band.
/if dymem_heap               ;set W0 to first address past the stack
  /then                      ;using the Embed DYMEM heap, MINSTACK0 exists
         mov     #__SP_init + [v minstack0], w0
  /else                      ;no heap, set up stack as defined by linker
         mov     #__SPLIM_init, w0
         sub     w0, #6, w1  ;make first stack address to causes trap when accessed
         mov     w1, Splim   ;set hardware stack limit detector
         nop                 ;needed after changing SPLIM

         mov     #__SP_init, w15 ;initialize the stack pointer
         ;   Set up the Embed DYMEM heap, if enabled.  W0 is the first address
         ;   past the stack.
/if dymem_heap then          ;set up the Embed DYMEM heap ?
         mov     #__SPLIM_init - 2, w1 ;pass adr of last word of the heap
         gcall   dymem_init  ;init the heap
;   Set up the system clock as defined in the LIB file.  The processor is
;   currently running directly from the external crystal.  The PLL will be
;   switched on.
;   The following constants are set in the LIB file to define the clock
;   chain:
;     CLKPRE  -  PLL block prescaler divider, 2-33
;     CLKMUL  -  PLL block multiplier, 2-513
;     CLKPOS  -  PLL block postscaler divider, 2,4,8
         ;   Set the CPU clock chain configuration as described above.  This
         ;   does not actually change the clock, but configures how the PLL
         ;   block will work when it is enabled.
.set     ii,     #0b0000000000000000 ;init the fixed CLKDIV fields
                 ;  0--------------- interrupts don't change doze mode
                 ;  -000------------ clock divided by 1 from normal in doze mode
                 ;  ----0----------- disable doze mode
                 ;  -----000-------- divide FRC by 1
                 ;  --------XX------ PLL output divide select filled in below
                 ;  ----------X----- unused
                 ;  -----------XXXXX PLL input divide select filled in below
.set     ii,     ii | ([- [div clkpos 2] 1] << 6) ;merge in PLL postscaler
.set     ii,     ii | [- clkpre 2] ;merge in PLL prescaler
         mov     #ii, w0
         mov     w0, Clkdiv  ;set clock divisor register

         mov     #[- clkmul 2], w0 ;set PLL multiplier value
         mov     w0, Pllfbd
         ;   Switch to using the primary oscillator with the PLL.  We are
         ;   currently using the primary oscillator without the PLL block.  The
         ;   designers of this clock chain hardware were paranoid about errant
         ;   code unintentionally switching the clock, so certain incantations
         ;   must be muttered to "unlock" the high and low bytes of OSCCON
         ;   separately to allow the new information for switching the clock to
         ;   be written.
         mov     #0b00000011, w0 ;get new value for OSCCON high byte
                 ;  X------- unused
                 ;  -XXX---- current oscillator selection (read only)
                 ;  ----X--- unused
                 ;  -----011 select primary oscillator with PLL
         mov     #Osccon+1, w1 ;set pointer to high byte of OSCCON
         mov     #0x78, w2   ;get the unlock values ready
         mov     #0x9A, w3
         mov.b   w2, [w1]    ;write the high byte unlock sequence
         mov.b   w3, [w1]
         mov.b   w0, [w1]    ;write new value to OSCCON high byte

         mov     #0b00000001, w0 ;get new value for OSCCON low byte
                 ;  0------- do not disable clock switching
                 ;  -0------ allow peripheral pin select changes
                 ;  --X----- read-only status bit
                 ;  ---X---- unused
                 ;  ----0--- clear any clock failure detected (not used)
                 ;  -----X-- unused
                 ;  ------0- disable secondary low power oscillator
                 ;  -------1 start switchover from old to new clock source
         mov     #Osccon, w1 ;set pointer to low byte of OSCCON
         mov     #0x46, w2   ;get the unlock values ready
         mov     #0x57, w3
         mov.b   w2, [w1]    ;write the low byte unlock sequence
         mov.b   w3, [w1]
         mov.b   w0, [w1]    ;write new value to OSCCON low byte

/if [or [not debugging] debug_icd] then
wait_clkswitch:              ;wait for clock switch
         btsc    Osccon, #Oswen ;oscillator switchover is complete ?
         jump    wait_clkswitch ;no, go back and check again
;   Initialize the separate modules.
         gcall   trap_init   ;init traps handler module
         gcall   port_init   ;init I/O ports
         gcall   task_init   ;init multi-tasking manager
         gcall   clock_init  ;init clock tick hardware and ticks generator
         gcall   spi_init    ;init SPI bus interface
         gcall   nvol_init   ;init non-volatile memory access
         gcall   nvmem_ready ;check NV mem, erase if corrupt, set flags
         gcall   ad_init     ;init A/D handler and start getting analog readings
         gcall   uart_init   ;init low level UART driver
         gcall   cmd_init    ;init host command stream processing module
         gcall   cmds_init   ;init command routines module
         gcall   ledstat_init ;init Status LED controller

         gjump   init        ;continue with system-wide initialization