;  - this sample software was tested on MC9S12C32 Controller Board
;  - the board includes 8.000MHz crystal
;  - no PLL => busclock = 4MHz
;  - the mask set number was 1L45J
;  - the sample software demonstrates FLASH programming as a part of application
;  - the sample application writes a few words into FLASH locations
;-------------------------------------------------------------------------------
; export symbols
;-------------------------------------------------------------------------------
  XDEF Entry, main
;-------------------------------------------------------------------------------
; include derivative specific macros
  INCLUDE 'mc9s12c32.inc'
;_______________________________________________________________________________
; definitions
;_______________________________________________________________________________
;--- FLASH ERRORS ----------
FLASH_ODD_ACCESS:         EQU  1
FLASH_NOT_ERASED:		      EQU  2
ACCESS_ERROR:  			      EQU  3
PROTECTION_ERROR:		      EQU  4
NOT_STARTOFSECTOR_ERROR   EQU  5
PROGRAM_ERASE_IMPOSSIBLE  EQU  6
;--- FLASH COMMANDS --------
PROG:	                  EQU $20           ; Word Program command
ERASE:	                  EQU $40           ; Sector Erase command
;--- FLASH CONSTANTS -------
FLASH_SECTOR_SIZE:        EQU 512           ; flash sector size

;--- FLASH PROGRAM DEFs FOR BLOCK E/W FUNCTIONS ---
START_NUMBER:             EQU 1             ; start number for memory writing
MEM_CYCLE_START:          EQU $4000         ; flash sector start address
MEM_CYCLE_END:            EQU $4FFF         ; flash sector start address
;_______________________________________________________________________________
; variable/data section
;_______________________________________________________________________________
DEFAULT_RAM:  SECTION
write_address     DS.W  1                   ; address for sector erase or data write
write_data        DS.W  1                   ; data to be written
err_code          DS.B  1 									; return value from flash erase/write functions
;_______________________________________________________________________________
; code DEFAULT_ROM (C000~FFFF) section
;_______________________________________________________________________________
DEFAULT_ROM:      SECTION
      ORG   $D000                           ; set Default Rom and specific address inside the section
Entry:
main:
      LDS   #$F00                           ; set STACK; reset status: RAM is placed 800~FFF
      LDAB  #$0D
      STAB  MISC                            ; enable lower half of memory (4000~7FFF)
      LDD   #65535                          ; DDRAB = 0xFFFF; // output (word) port
      STD   DDRAB
      ;-------------------------------------
      ; FLASH 4000~4FFF E_R_W initialization of the FLASH module - use calculation routine place under the code
      ;-------------------------------------
      ; Oscillator clock is 8.000MHz, bus clock is 4MHz => tbus=250ns
      ; result for FCLKDIV is 170 (0xAA)
      LDAB  #$AA
      STAB  FCLKDIV
      LDAB  #$30
      STAB  FSTAT
      ;-------------------------------------
      SEI                                   ;   disable interrupts, it is necessity since FLASH is inaccessible during
      CLR   COPCTL                          ;   COPCTL = 0x00; // disable the COP
      ;-------------------------------------
loop:																				; for(;;) 
                                            ;  {
      ;-------------------------------------
      ; FLASH 4000~4FFF E_R_W 
      ;-------------------------------------
			LDD   #$4000													; set address of sector to be errased
			STD   write_address
      JSR   Flash_Erase_Sector              ; Flash_Erase_Sector(0x4000~0x41FF);  
			;......................................
			LDD   #$1111													; write data 0x1111
			STD   write_data											;
			LDD   #$4000													; to the address 0x4000
			STD   write_address										;
      JSR   Flash_Write_Word                ; write data 0x1111 to the address 0x4000
      ;--- check err_code and do something in the case of error ---
      ; it's up to you - depends on your application
      NOP ;--- do something instead of NOP ---
      ;---
      ;--- check written data and do something in the case of error ---
      ; it's up to you - depends on your application
      NOP ;--- do something instead of NOP ---
			;......................................
			LDD   #$2222													; write data 0x2222
			STD   write_data											;
			LDD   #$4002													; to the address 0x4002
			STD   write_address										;
      JSR   Flash_Write_Word                ; write data 0x2222 to the address 0x4002
      ;--- check err_code and do something in the case of error ---
      ; it's up to you - depends on your application
      NOP ;--- do something instead of NOP ---
      ;---
      ;--- check written data and do something in the case of error ---
      ; it's up to you - depends on your application
      NOP ;--- do something instead of NOP ---
			;......................................

      ;-------------------------------------
      ; erase flash memory 4000~4FFF. 
      ; 8 sectors. Start addresses are 4000,4200,4400,4600,4800,4A00,4C00,4E00
      ;-------------------------------------
      LDD   #MEM_CYCLE_START								;for(addr=0x4000L;addr<0x4FFFL;addr+=0x200L)
      STD   write_address
cycle01:																		;	{
      JSR   Flash_Erase_Sector              ;  Flash_Erase_Sector(addr);  
      LDD   write_address
      ADDD  #FLASH_SECTOR_SIZE
      STD   write_address
      CPD   #MEM_CYCLE_END
      BCS   cycle01													; }
      ;-------------------------------------
      ; write  data to memory 4000~4FFF and check them
      ;-------------------------------------
      LDD   #START_NUMBER                   ; write_data = START_NUMBER;
      STD   write_data
      LDD   #MEM_CYCLE_START                ; for(addr=0x4000;addr<0x4FFF;addr+=2)
      STD   write_address										;	 {
                                            ;   err_code = Flash_Write_Word((unsigned int *)addr,data);         // write data (an integer is written)
cycle02:                                    ;
      JSR   Flash_Write_Word                ; write_data = write_data +1
      ;--- check err_code and do something in the case of error ---
      ; it's up to you - depends on your application
      NOP ;--- do something instead of NOP ---
      ;---
      ;--- check written data and do something in the case of error ---
      ; it's up to you - depends on your application
      LDX   write_address                   ; read data from address "write_address"
      LDD   0,X
      CPD   write_data                      ; compare written and read data
      BEQ   TEST_OK                         ; if OK jump
      NOP ;--- do something instead of NOP ---
      ;---
TEST_OK:      
      LDD   write_data
      ADDD  #1
      STD   write_data

      LDD   write_address                   ; write_address = write_address + 1
      ADDD  #2
      STD   write_address
      CPD   #MEM_CYCLE_END
      BCS   cycle02													; }
      ;-------------------------------------
      BRA   loop                            ;} endless loop (for(;;))
;_______________________________________________________________________________
; signed char Flash_Write_Word(write_address,write_data)
;_______________________________________________________________________________
Flash_Write_Word:
  BRCLR write_address+1,#1,jumpFWW01        ; if(addr & 0x0001) {err=Flash_Odd_Access;} // get error Aligned word?
  LDAB  #FLASH_ODD_ACCESS
  STAB  err_code
  RTS
jumpFWW01:
  LDX   write_address                       ; if((*((unsigned int*)addr)) != 0xFFFF) {err=Flash_Not_Erased;} // get error Is it erased?
  LDD   0,X
  IBEQ  D,jumpFWW02
  LDAB  #FLASH_NOT_ERASED
  STAB  err_code
  RTS
jumpFWW02:
  LDAB  #$30                                ; FSTAT = 0x30;
  STAB  FSTAT
  LDD   write_data                          ; *((unsigned int*)addr) = write_address;         // Store desired data to address being programmed
  STD   0,X
  LDAB  #PROG                               ; FCMD  = PROG;  // Store programming command in FCMD
  STAB  FCMD
  LDX   write_address
  JSR   DoOnStack
  LDAB  FSTAT
  STAB  err_code

  BRCLR err_code,#16,jumpFWW03                 ; if(FSTAT & FSTAT_ACCERR_MASK) {err = Access_Error;}			 // get error
  LDAA  #ACCESS_ERROR
  STAA  err_code
  RTS
jumpFWW03:
  BRCLR err_code,#32,jumpFWW04                 ; if(FSTAT & FSTAT_PVIOL_MASK)  {err = Protection_Error;}	 // get error
  LDAA  #PROTECTION_ERROR
  STAA  err_code
  RTS
jumpFWW04:
  RTS
;_______________________________________________________________________________
; signed char Flash_Erase_Sector(write_address)
;_______________________________________________________________________________
Flash_Erase_Sector:
  BRCLR write_address+1,#1,jumpFES01        ; if(addr & 0x0001) {err=Flash_Odd_Access;} // get error Aligned word?
  LDAB  #FLASH_ODD_ACCESS
  STAB  err_code
  RTS
jumpFES01:
  LDD   write_address                       ; if(addr % Flash_Sector_Size !=0) {err = Not_StartofSector_Error;}
  LDX   #FLASH_SECTOR_SIZE 
  IDIV 
  TBEQ  D,jumpFES02
  LDAB  #NOT_STARTOFSECTOR_ERROR
  STAB  err_code
  RTS
jumpFES02:
  LDAB  #$30                                ; FSTAT = 0x30;
  STAB  FSTAT
  LDX   write_address                       ; (*((unsigned int*)addr)) = 0xFFFF;   // Dummy store to page to be erased
  LDD   #65535
  STD   0,X
  LDAB  #ERASE                                 ; FCMD = ERASE;
  STAB  FCMD
  CLRB
  LDX   write_address
  JSR   DoOnStack
  LDAB  FSTAT
  STAB  err_code
  BRCLR err_code,#16,jumpFES03                 ; if(FSTAT & FSTAT_ACCERR_MASK) {err = Access_Error;}			 // get error
  LDAA  #ACCESS_ERROR
  STAA  err_code
  RTS
jumpFES03:
  BRCLR err_code,#32,jumpFES04                 ; if(FSTAT & FSTAT_PVIOL_MASK)  {err = Protection_Error;}	 // get error
  LDAA  #PROTECTION_ERROR
  STAA  err_code
  RTS
jumpFES04:
  RTS
;_______________________________________________________________________________
; DoOnStack(write_address)
;_______________________________________________________________________________
DoOnStack:
            pshb ;save B - PPAGE
            ldx #SpSubEnd-2 ;point at last word to move to stack
SpmoveLoop: ldd 2,x- ;read from flash
            pshd ;move onto stack
            cpx #SpSub-2 ;past end?
            bne SpmoveLoop ;loop till whole sub on stack
            tfr sp,x ;point to sub on stack
            ldaa #128        ;FSTAT_CBEIF ;preload mask to register command
            call 0,x,00 ;execute the sub on the stack
            leas SpSubEnd-SpSub,sp ;de-allocate space used by sub
            pulb ;restore B
            rts ;to flash where DoOnStack was called
;...........................................
            EVEN ;Make code start word aliened
SpSub:
            ;ldab SpSubEnd-SpSub+2,sp ;get PPAGE back
            ldab SpSubEnd-SpSub+3,sp ;get PPAGE back
            stab PPAGE ;Store the PPAGE address
            tfr ccr,b ;get copy of ccr
            orcc #$10 ;disable interrupts
            staa FSTAT ;[PwO] register command
            nop ;[O] wait min 4~ from w cycle to r
            nop ;[O]		
            nop ;[O]
            nop ;[O]         !!!!!!! Added to make even bytes routine
            brclr FSTAT,#64,*  ; FSTAT_CCIF ;[rfPPP] wait for queued commands to finish
            brclr FSTAT,#128,* ; FSTAT_CBEIF;[rfPPP] wait for queued commands to finish 
            tfr b,ccr ;restore ccr and int condition
            rtc ;back into DoOnStack in flash
SpSubEnd:
;_______________________________________________________________________________
;_______________________________________________________________________________

;*******************************************************************************
;_______________________________________________________________________________
;_______________________________________________________________________________
; routine for ECLKDIV vlaue calculation for flash E/W timing
;_______________________________________________________________________________
;_______________________________________________________________________________
;signed char Flash_Init(unsigned long oscclk, unsigned long tbus) // [kHz,ns]
;{
;  unsigned long fclk_val;
;  unsigned long temp;
;
;  if(tbus > 1000L) return PROGRAM_ERASE_IMPOSSIBLE; // tbus > 1us (1000ns) (page 35 S12FTS256KV2.pdf )
;  temp = (oscclk*1000) % 8;
;  fclk_val = (oscclk  * (5000UL+tbus)) / 1000000UL;
;
;  if (oscclk >= 12800UL)
;    { fclk_val = fclk_val / 8;
;      if(temp) // oscclk % 8 is not an integer
;       { fclk_val = fclk_val - 1; }
;      FCLKDIV = (unsigned char) (fclk_val | 0x40);
;    }
;  else
;    {if(temp) // oscclk % 8 is not an integer
;       { fclk_val = (fclk_val-1); }
;     FCLKDIV = (unsigned char) fclk_val;
;    }  
;
;  // test computed parameters
;  if (oscclk >= 12800L)
;    {temp = 8UL*1000000UL*(1+FCLKDIV&0x3F) / oscclk + tbus;  }
;  else
;    {temp = 1000000UL*(1+FCLKDIV&0x3F) / oscclk + tbus;      }  
;  temp = temp / 1000;
;  
;  if(temp < 5UL) return PROGRAM_ERASE_IMPOSSIBLE; 
;}

























