;******************************************************************************* ;* Module : FLASH.SUB ;* Programmer: Tony Papadimitriou <tonyp@acm.org> ;* Purpose : Routines to program the Flash of 9S08 GB, QG8, QE, or compatible ;* Language : Motorola/Freescale/NXP HC08/9S08 Assembly Language (aspisys.com/ASM8) ;* Status : FREEWARE Copyright (c) 2021 by Tony Papadimitriou <tonyp@acm.org> ;* Original : http://www.aspisys.com/code/hc08/flash.html ;* Note(s) : This version tested on 9S08GB60, 9S08QG8, 9S08QE32, 9S08QE128. ;* : ;* : Synopsis -- The following calls are currently available: ;* : ;* : Flash_Write - Program a single Flash location ;* : Flash_Erase - Erase a single Flash page by address ;* : Flash_ErasePage - Erase a single Flash page by page # ;* : Flash_Normalize - Normalize address to start of Flash page ;* : Flash_CopyBlock - Copy a block from A to B in Flash ;* : Flash_CopyPage - Copy a Flash page by page # ;* : Flash_PageToPtr - Return address for given Flash page # ;* : ;* History : 10.02.01 v1.00 Original (based on 2010.02.01 OS8/FLASH_GB.MOD) ;* : 10.04.17 Minor optimization (combined some exits for size) ;* : 10.10.01 Added macros for each call ;* : 11.04.21 Changed test code a bit ;* : 11.11.11 Minor optimization(s) ;* : 11.11.16 Added PSHCC and PULCC around RAM routine call ;* : 11.11.19 Improved some macros for X-indexed mode ;* : Optimized some more SP => SPX ;* : 12.11.07 Improved Flash_CopyPage macro ;* : 13.09.24 v1.01 Simpified FCDIV initialization in ?StackRoutine [-7 bytes] ;* : 17.10.01 Removed use of deprecated _push_ macro call ;* : 18.02.11 Added FLASH_TEST_FIRST condition for safer writes [+6 bytes] ;* : 18.04.30 Optimized ?StackRoutine for no PSHCC/PULCC [-12 bytes] ;* : 20.07.21 Removed warnings from non-GB60 stand-alone assembly ;* : 20.12.31 Added ! prefix to an RTC instruction to silence warning ;* : 21.05.13 Replaced ?RAM_Execute_End label with computation ;******************************************************************************* #ifmain ;----------------------------------------------------------------------- #ifdef GB60 FLASH_DATA_SIZE def 60288 #endif FLASH_DATA_SIZE def 1024 #ListOff #Uses mcu.inc #ListOn #endif ;------------------------------------------------------------------------ #ifnhcs #Fatal FLASH.SUB requires 9S08 MCU (#HcsOn directive) #endif ;******************************************************************************* ; Flash programming command codes ?mByteProg equ $20 ;Byte programming ?mPageErase equ $40 ;Page erase ;?mBlank equ $05 ;Blank check ;?mBurstProg equ $25 ;Burst programming ;?mMassErase equ $41 ;Mase erase ;******************************************************************************* #ROM ;******************************************************************************* ?_OBJECT_? ;******************************************************************************* ; 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 by its loader at zero-based offsets ; : @1, @2 (?FlashAddress) and @4 (?FlashCommand) ; : Stack needed: 20 bytes + 2 for JSR ?RAM_Execute proc sta $FFFF ;Step 1 - Latch data/address ?FlashAddress equ *-?RAM_Execute-2,2 ;$FFFF (@1,@2) replaced with actual address during RAM copying lda #?mByteProg ;mByteProg (@4) replaced with actual command during RAM copying ?FlashCommand equ *-?RAM_Execute-1,1 ;offset into command byte 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_Execute ;******************************************************************************* ; Purpose: Program an internal Flash location ; Input : HX -> Flash memory location to program ; : A = value to write ; Output : None Flash_Write macro [Address],[Value] #push #spauto :sp #ifparm ~2~ psha lda ~2~ #endif #ifparm ~1~ pshhx ldhx ~1~ #endif call ~0~ #ifparm ~1~ pulhx #endif #ifparm ~2~ pula #endif #pull endm ;------------------------------------------------------------------------------- #spauto Flash_Write proc push #ifdef FLASH_TEST_FIRST #ifz ERASED_STATE tst ,x ;test if erased, and if not #else psha lda ,x ;if not erased already coma pula #endif bne ?Failure ;failure #endif cbeq x+,??PullSuccess ;value already there, no need to update lda #?mByteProg ;command to execute ; bra ?StackRoutine ;******************************************************************************* ; Purpose: Copy RAM_Execute routine to stack (read backwards so it turns out correctly) ; Input : HX -> Flash memory location ; : A = Flash command to perform ; Output : None ; Note(s): #spauto ?StackRoutine proc #temp :: addr@@ next :temp,2 val@@ next :temp ;-------------------------------------- ; Prepare the Flash memory for programming ; FCLK must fall between 150-200KHz [FCLK=FBUS/(DIV+1)] and DIV=0..63 ;-------------------------------------- ; ldx FCDIV ;(redundant check) ; bmi DoFSTAT@@ ldx #FLASH_CLK_VALUE ;required to allow further stx FCDIV ;access to Flash programming ;DoFSTAT@@ ldx #FPVIOL_|FACCERR_ ;clear possible errors stx FSTAT ;-------------------------------------- ;copy the routine to stack RAM (backwards) ldhx #?RAM_Execute+::?RAM_Execute-1&$FFFF ;HX -> end of routine Loop@@ psha code@@ ;save command for later use by RAM routine psha lda ,x sta code@@,sp pula aix #-1 ;one less routine byte to process cphx #[[?RAM_Execute ;are we done? bhs Loop@@ #spadd ::?RAM_Execute-1 ;account for stacked routine tsx ;HX -> routine's start in RAM sta ?FlashCommand,x ;save command within LDA #?? instruction lda addr@@,spx sta ?FlashAddress,x ;save H within STA $FFxx instruction lda addr@@+1,spx sta ?FlashAddress+1,x ;save X within STA $xxFF instruction tpa psha ;save CCR[I] sei ;disable interrupts @cop ;reset COP (for maximum tolerance) lda val@@,spx ;get value to write (don't care for erase) jsr ,x ;execute RAM routine to perform Flash command pula ;restore CCR[I] tap ais #:ais ;de-allocate temporaries lda FSTAT bit #FPVIOL_|FACCERR_ beq ?PullSuccess ?Failure sec ;indicate "error" pull !rtc ;error code is propagated to caller ;******************************************************************************* ; Purpose: Erase an internal Flash page by page number ; Input : A = page number to erase ($00..$?F) ; Output : None Flash_ErasePage macro [PageNumber] #ifparm ~1~ #push #spauto :sp psha lda ~1~ call ~0~ pula #pull mexit #endif call ~0~ endm ;------------------------------------------------------------------------------- #spauto Flash_ErasePage proc push call Flash_PageToPtr ;HX -> beginning of page call Flash_Erase ;NOTE: Do NOT just fall thru to ?Erase ??PullSuccess bra ?PullSuccess ;******************************************************************************* ; Purpose: Erase an internal Flash page by address ; Input : HX -> location within page to erase ; Output : None ; Note(s): Forces address past HighRegs (if any). Flash_Erase macro [Address] #ifparm ~1~ #push #spauto :sp pshhx ldhx ~1~ call ~0~ pulhx #pull mexit #endif call ~0~ endm ;------------------------------------------------------------------------------- #spauto Flash_Erase proc push #ifdef HighRegs cphx #HighRegs blo Cont@@ cphx #HighRegs_End bhi Cont@@ ldhx #HighRegs_End+1 Cont@@ #endif lda #?mPageErase ;command to execute bra ?StackRoutine ;******************************************************************************* ; Purpose: Normalize pointer to beginning of Flash page ; Input : HX -> anywhere within Flash page ; Output : HX -> beginning of Flash page Flash_Normalize macro [FlashAddress] #ifparm ~1~ ldhx ~1~ #endif call ~0~ endm ;------------------------------------------------------------------------------- #spauto Flash_Normalize proc push addr@@,2 pshh ;TOS = MSB txa ;A = LSB tsx and #[FLASH_PAGE_MASK sta addr@@+1,spx pula ;A = MSB and #]FLASH_PAGE_MASK sta addr@@,spx ?PullSuccess clc ?PullOut pull rtc ;******************************************************************************* ; Purpose: Copy a block of Flash memory from point A to point B inclusive ; Input : CALLERSTACK+4 -> Source Begin ; : CALLERSTACK+2 -> Source End ; : CALLERSTACK+0 -> Destination ; Output : Stacked parameters updated, accordingly ; Note(s): At least one byte is copied (when SourceBegin and SourceEnd are equal) ; : Calling sequence: ; : ldhx #SourceBegin ; : pshhx ; : ldhx #SourceEnd ; : pshhx ; : ldhx #Destination ; : pshhx ; : call Flash_CopyBlock ; : ais #6 ; : bcs ProcessError Flash_CopyBlock macro SourceBegin,SourceEnd,Destination mreq 1,2,3:SourceBegin,SourceEnd,Destination #push #spauto :sp pshhx #psp ldhx ~1~ pshhx @@_ldhx_ ~2~ 1,psp pshhx @@_ldhx_ ~3~ pshhx call ~0~ ais #:psp pulhx #pull endm ;------------------------------------------------------------------------------- #spauto :ab ;account for RTS/RTC Flash_CopyBlock proc @parms .dst,.to,.from push Loop@@ ldhx .from@@,sp ;compare current pointer cphx .to@@,sp ; with ending pointer bhi Done@@ ;if above, we're done lda ,x ;get data to copy aix #1 ;bump up source pointer sthx .from@@,sp ;and save it ldhx .dst@@,sp ;get destination pointer call Flash_Write ;write data to destination bcs Fail@@ ;on error, exit aix #1 ;bump up destination pointer sthx .dst@@,sp ;and save it bra Loop@@ ;repeat for all bytes Fail@@ equ ?PullOut Done@@ equ ?PullSuccess ;******************************************************************************* ; Purpose: Copy one flash page to another ; Input : A = source page index ($00..$?F) ; : X = destination page index ($00..$?F) ; Output : None Flash_CopyPage macro [SourcePage,DestinationPage] #ifnoparm ~@~ call ~0~ mexit #endif #push #spauto :sp pshxa lda ~1~ ldx ~2~ call ~0~ pulxa #pull endm ;------------------------------------------------------------------------------- #spauto Flash_CopyPage proc psha pshx dp@@ pshh #ais call Flash_PageToPtr ;convert source page to pointer pshhx ;save source pointer addhx #FLASH_PAGE_SIZE-1 ;end of source page pshhx lda dp@@,sp ;get destination page call Flash_PageToPtr ;convert to pointer call Flash_Erase ;erase the destination before writing pshhx ;save destination pointer call Flash_CopyBlock ais #:ais ;de-allocate temporaries bra Done@@ Done@@ equ ?PullOut ;******************************************************************************* ; Purpose: Offset HX to the start of the requested page ; Input : A = flash page number ($00..$?F) counting from zero ; Output : HX -> absolute start of flash page Flash_PageToPtr macro [FlashPageNumber] #ifparm ~1~ #push #spauto :sp psha lda ~1~ call ~0~ pula #pull mexit #endif call ~0~ endm ;------------------------------------------------------------------------------- #spauto Flash_PageToPtr proc psha pg@@ pshhx ans@@ #ais #ifz ]FLASH_PAGE_SIZE ldx #FLASH_PAGE_SIZE mul ;XA = pointer stx ans@@,sp sta ans@@+1,sp #else ldx #]FLASH_PAGE_SIZE mul ;XA = pointer tsx sta ans@@,spx clr ans@@+1,spx #endif bra ?PullSuccess #sp ;cancel all SP offsets ;******************************************************************************* #Exit ;******************************************************************************* @EndStats Start proc @rsp ;test the various calls (and macro expansions) @Flash_Write #EEPROM,#$AA @Flash_Write #EEPROM @Flash_Write ,#$AA @Flash_Write @Flash_ErasePage @Flash_ErasePage #0 @Flash_Erase @Flash_Erase #EEPROM @Flash_Normalize @Flash_Normalize #EEPROM+1 @Flash_CopyPage #0,#1 @Flash_CopyPage @Flash_CopyBlock #EEPROM,#EEPROM_END,#RAM @Flash_CopyBlock, ,x 2,x 4,x @Flash_PageToPtr @Flash_PageToPtr #0 bra * @vector Vreset,Start