; flash eeprom library ; by JHusak , 04.01.2020 ; free to use. ; Nice to have procedures, because mads may exclude unused procedures (-x in cmdl) ;.def STRIPPED ;.def FLASH_INCLUDE_ALL ;FLASH_TYPE equ M_TYPE_28SF ;FLASH_TYPE equ M_TYPE_29SF FLASH_TYPE equ M_TYPE_39SF ;FLASH_TYPE equ M_TYPE_29F ; x - 0x0 or 0x40 - chip select; ; a - $80 - format, $90 - enter id mode, $a0 - writebyte C_ID_MODE equ $90 M_TYPE_28SF equ $88 M_TYPE_29SF equ $89 M_TYPE_39SF equ $99 M_TYPE_29F equ $09 ; used in MaxFlash BM29F040 M_SSIZE_28SF equ $0100 ; sector size M_SSIZE_29SF equ $0080 ; sector size M_SSIZE_39SF equ $1000 ; sector size M_SSIZE_29F equ $10000 ; sector size; MAXFlash, protocol compatible with 39sf0x0 ;Problems with writing: ; - check flash presence ; - flash protocol ; - size of flash 1,2 ; - size of sector in some cases ; - number of flashes (easy, they do not overlap) ; All can be read by erasing memory, writing several bytes and reading them ; For flash recognition ; - format, ; - write 128 kbytes, read more -> if not ff, flash is 128k ; - write additional 128KB, read more ->if not ff flash is 256kB, else is 512kb ; ; First detection is to read raw memory and id and compare results. Some issues may occur when no memory inserted. ;flash_detect_protocol: ; lda #ID_MODE ; jsr flashoppreamble_5555_2aaa ; c parameter as format/writebyte ; for compatibility, 5555_2aaa only .IF (FLASH_TYPE = M_TYPE_39SF) .or .def FLASH_INCLUDE_ALL TRIGGER_FORMAT equ $10 C_FORMAT equ $80 C_BYTE_PROG equ $a0 flashoppreamble pha lda #C_BYTE_PROG scc lda #C_FORMAT ; only if c set sta command pla jmp @+ flashoppreamble_acc ; 39sf0x0, 29F040 sta command @ txa pha ; when write byte x must be set to either 0 or 40 temporarily and #$40 tax sta $d502,x mva #$aa $b555 ; $5555<$aa sta $d501,x mva #$55 $aaaa ; $2aaa<$55 ; $5555<$80 sta $d502,x command equ *+1 mva #$ff $b555; will become command: FORMAT/ID_MODE/BYTE_PROG cmp #C_FORMAT bne @+ ; if not FORMAT, procedure finishes ; FORMAT part, more to write sta $d502,x mva #$aa $b555 ; $5555<$aa sta $d501,x mva #$55 $aaaa ; $2aaa<$55 @ pla tax rts .endif .if (FLASH_TYPE=M_TYPE_29SF) .or .def FLASH_INCLUDE_ALL TRIGGER_FORMAT equ $10 C_FORMAT equ $80 C_BYTE_PROG equ $a0 flashoppreamble pha lda #C_BYTE_PROG scc lda #C_FORMAT ; only if c set sta command pla jmp @+ flashoppreamble_acc ; 29sf040 sta command @ txa pha ; when write byte x must be set to either 0 or 40 temporarily and #$40 tax sta $d500,x mva #$aa $a555 ; $555<$aa mva #$55 $a2aa ; $2aa<$55 ; $555 failed ; if c cleared, x=vendor, y=product code ; there are some memories which need multiple read, but we do not abuse them. check_vendor lda #C_ID_MODE jsr flashoppreamble_acc ldx $a000 ; vendor ldy $a001 ; id mva #$f0 $a000 ; exit read_id mva #0 flash_size cpx #$BF; SST bne next1 ; nice to store that this is SST cpy #$B5 bne @+ lda #$0f @ cpy #$B6 bne @+ lda #$1f @ cpy #$B7 bne @+ lda #$3f @ sta flash_size clc rts next1 .if 0 lda #C_ID_MODE jsr flashoppreamble_acc ldx $a000 ; vendor ldy $a001 ; id mva #$f0 $a000 ; exit read_id mva #0 flash_size cpx #$BF; SST bne cvexit ; nice to store that this is SST cpy #$24 bne @+ lda #$1f @ cpy #$13 bne @+ lda #$3f @ sta flash_size clc rts .endif cvexit sec rts .endif ; -------------------------- flashformatchip2 ldx #$40 dta { bit.w } flashformatchip1 ldx #$0 ; -------------------------- ; PROCEDURE ; x = 0 or 0x40 - flash chip address. flashformatchip sei stx store_x lda #C_FORMAT jsr flashoppreamble_acc ; does not touch A sta $d502,x lda #TRIGGER_FORMAT sta $b555 ; FORMAT HERE TRIGGERED! ; not needed to mva $ff flashcmp jsr wait4flashcheckresult ; waits for format finished ; then check number of banks for FFs lda #$3f ; this depends on flash size, $0f, $1f, $3f sta flashformatcounter flashbankloop sei ldx store_x flashformatcounter equ*+1 sta $d5FF,x ; set chip (x) and bank ; set pages count ldy #$20 ; reset address lda #$a0 sta flashformataddrcheck + 2 ; check whole sector against 0xff jsr flashchecksectorformatted_bare ; destroys x bcs flashformatexit ; format error if c set dec flashformatcounter bpl flashbankloop flashformatexit jmp flashcartoff ; preserves C store_x dta 0 ; -------------------------- ; PROCEDURE flashformatsector ; x - bank number 00 - 7f (even sector>>1) ; a - erase 4KB from $B000 if A=$B0, FROM $A000 IF A=$A0 ; format 4kb evensector ; strange form - easily maps to cartridge banks ; to format bank, must format sector (x<<1) and (x<<1) +1 ; first check if all ff ; this is to avoid wear stx flashformatstorex sta flashformatstorea sei sta $d500,x ; store #$a0 or #$b0 sta flashformataddrcheck + 2 jsr flashchecksectorformatted bcc flashsectorformatgood sei flashformatstorex equ * + 1 ldx #0 ; filled before ; check least sector bit lda #C_FORMAT jsr flashoppreamble_acc ; does not touch A,X sta $D500,x ; A must be either $A0 or $B0 flashformatstorea equ * + 1 lda #0 ; filled before sta flashtmpaddr+1 sta flashformataddrcheck + 2 lda #$30 flashtmpaddr equ *+1 sta $a000 ; SECTOR FORMAT INVOKED HERE! jsr wait4flashcheckresult ; sei lda flashformatstorea sta flashformataddrcheck+2 ldx flashformatstorex sta $d500,x ; check if all data in sector is $ff flashchecksectorformatted ldy #$10 flashchecksectorformatted_bare lda #$ff ldx #0 flashformataddrcheck cmp $a000,x bne flashsectorformaterror inx bne flashformataddrcheck inc flashformataddrcheck + 2 dey bne flashformataddrcheck flashsectorformatgood jsr flashcartoff clc rts flashsectorformaterror jsr flashcartoff sec rts ; --------------------- ; PROCEDURE flashwritebyte ; a - byte to write ; x - 8kb bank to switch, $00 to $7f, also chip select ; flashaddr - addres in flash - must be a000-offset ; do not programm byte if already good sei sta $D500,x ; select bank, chip ldy #{ cmp.w } jsr flashprocessbyte bne byte_differs sta $D580 cli clc rts byte_differs sta flashcmp sei pha lda #C_BYTE_PROG jsr flashoppreamble_acc ; preserves A,X pla ; set right bank sta $D500,x ldy #{ sta.w } jsr flashprocessbyte ; WRITE BYTE INVOKED ! wait4flashcheckresult ; sei mode mva #0 flashcnt ldy #1 ; first time wait short first turn to speed up byte write. bne @+ flashwaitfordone ; approx 100ms in overall for chip erase: ; as many cycles needed, as 256*cycles >100ms * (1+epsilon) ; 100 ms is 180000 cycles ; so max 256 rough loops must last longer ; 180000 / 256 = 703 ; 700 cycles by 6 cycles loop lasts = 116. ; so flipipng values, and adding margin, ; we count 128*6 cycles in inner loop. ; max sector erase by datasheet: 25 ms ; max chip erase by datasheet: 100 ms ldy#250 @ dey nop bne @- @ ldy #{ lda.w } jsr flashprocessbyte sta flashval ldy #{ eor.w } jsr flashprocessbyte inc flashcnt bne @+ sta $d580 lda #$ff ; status rts @ and #$40 bne flashwaitfordone sta $d580 flashval equ *+1 lda #0 flashcmp equ *+1 cmp #0 ; when byte compare non zero = error rts flashcnt dta 0 ; ---------------------- ; PROCEDURE flashprocessbyte ; y - byteop for cpu to do with byte ; flashaddr - stored address sty flashbyteop flashaddr equ *+1 flashbyteop sta $aaaa rts flashincaddr inw flashaddr rts flashsetaddr stx flashaddr sty flashaddr+1 rts flashcartoff pha sta $d580 lda $d013 sta $3fa cli pla rts