; flash eeprom library ; by JHusak , 04.01.2020 ; free to use. FRAME_FEEDBACK=1 icl "lib_28sf0x0.asm" icl "lib_29f0x0.asm" icl "lib_39sf0x0.asm" num_mems = 3 ; CONSTANTS m_offsets softid_entry = 0 softid_exit = 3 flashoppreamble = 6 flash_lockchip = 9 flash_unlockchip = 12 flash_wait_unit = 15 flash_idstr = 18 ; rw section, may be moved to ZP if needed M_VECTOR .word 0 tmpa .byte 0 m_vendor .byte 0 m_kind .byte 0 m_iter .byte 0 ; ro section again ; Protocols for known kinds of memory: ; 28sf0x0 protokol unlock/write ; 39sf0x0 protokol 5555/AA;2aaa/55 ; 29f0x0 protokol 555/AA;2aa/55 ; Working scan order; scanning from the end; M_CHECK_VECS .word M_VECTORS_39SF, M_VECTORS_29F, M_VECTORS_28SF ; -------------------------------------------------------------------- ;Problems to solve 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; ; But we will rely rather on user's choice not to wear memory ; First detection is to read raw memory and id and compare results. However, we do not want to keep all those ids to recognise. ; Second detection is to compare contents. But not very reliable as contents may repeat. ; And ome issues may occur when no memory inserted. ; Eventually, for flash recognition ; - format, ; - write 128k-1 byte, read 2*128k-1 -> if not ff flash is 128k ; - write 256k-1 byte, read 2*256k-1 -> if not ff flash is 256kB, else is 512kb ; ; ??? c parameter as format/writebyte ; ??? for compatibility, 5555_2aaa only ; ------------------------------------------------------------------------ ; -------------------------- ; PROCEDURE ; x = 0 or 0x40 - flash chip address. ; stores proper vector table pointer if worked ; this fails only when somebody stores vendor and product bytes ; at the proper cells. ; ; then in the code we call lda #offset/jsr jsrtovectorproc check_type ldy #(2*(num_mems-1)) ?again sty m_iter ; store default values jsr flashsetbank lda $a000 sta m_vendor lda $a001 sta m_kind jsr jsrtosoftidentry jsr flashsetbank lda $a000 ; vendor cmp m_vendor sta m_vendor beq ?next lda $a001 ; id cmp m_kind sta m_kind beq ?next bne ?OK ?next ldy m_iter dey dey bpl ?again ; error sec rts ?OK lda M_CHECK_VECS+1,y sta M_VECTOR+1 lda M_CHECK_VECS,y sta M_VECTOR jsrtosoftidexit lda #softid_exit jsr jsrtovectorproc lda $d013 sta $3fa clc rts jsrtosoftidentry lda M_CHECK_VECS+1,y ; first is softid entry pha lda M_CHECK_VECS,y ; first is softid entry pha php rti ; jsr to tabled func ; PROCEDURE ; performs jump to vector table at offset in A provided ; y passed to the procedure called jsrtovectorproc php ; preserve C clc adc M_VECTOR sta tmpa lda M_VECTOR+1 adc #0 plp ; restore C pha lda tmpa pha php rti ; -------------------------- flashformatchip2 ldx #$40 dta { bit.w } flashformatchip1 ldx #$0 ; -------------------------- ; PROCEDURE ; x = 0 or 0x40 - flash chip address. flashformatchip sei stx store_x sec ; will format flash! lda #flashoppreamble jsr jsrtovectorproc ; preserves A ; 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 ; IT IS LONG because it has to be save, ie not format if formatted etc. ; first check if all ff ; this is to avoid wear stx flashformatstorex sta flashformatstorea sei jsr flashsetbank ; store #$a0 or #$b0 sta flashformataddrcheck + 2 jsr flashchecksectorformatted bcc flashsectorformatgood sei flashformatstorex equ * + 1 ldx #0 ; filled before ; check least sector bit sec lda #flashoppreamble ; does not touch A,X jsr jsrtovectorproc 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 jsr flashsetbank ; 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 FEEDBACK inc flashformataddrcheck + 2 dey bne flashformataddrcheck FEEDBACKEND flashsectorformatgood jsr flashcartoff clc rts flashsectorformaterror jsr flashcartoff sec rts .macro FEEDBACK .if (FRAME_FEEDBACK)>=1 php pha .if (FRAME_FEEDBACK&1)==1 inc colbaks lda colbaks sta colbak .endif .if (FRAME_FEEDBACK&2)==2 lda #0 sta dmactl sta sdmctl .endif pla plp .endif .endm .macro FEEDBACKEND .if FRAME_FEEDBACK>=1 php pha lda #0 sta colbaks sta colbak lda #34 sta sdmctl sta dmactl pla plp .endif .endm ; --------------------- ; 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 clc ; byte program preamble lda #flashoppreamble ; preserves A,X jsr jsrtovectorproc pla ; set right bank sta $D500,x ldy #{ sta.w } jsr flashprocessbyte ; WRITE BYTE INVOKED ! wait4flashcheckresult ; sei mode mva #0 flashcnt sta flashcnt+1 ; ldy #1 ; first time wait short first turn to speed up byte write. beq @+ flashwaitfordone ; WARNING! 29f040 erases even 10 seconds! ; 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 39sf040 ; max chip erase by datasheet: 20 ms 28sf040 ; max chip erase by datasheet: 10000 ms 29f040 ;ldy#250 sta WSYNC @ ;lda #flash_wait_unit ;jsr jsrtovectorproc FEEDBACK ;dey ;bne @- @ ldy #{ lda.w } jsr flashprocessbyte sta flashval ldy #{ eor.w } jsr flashprocessbyte inc flashcnt bne @+ inc flashcnt+1 bne @+ jsr flashcartoff lda #$ff ; status rts @ and #$40 bne flashwaitfordone jsr flashcartoff flashval equ *+1 lda #0 flashcmp equ *+1 cmp #0 ; when byte compare non zero = error rts flashcnt dta 0,0 ; ---------------------- ; PROCEDURE flashprocessbyte ; y - byteop for cpu to do with byte ; flashaddr - stored address sty flashbyteop flashaddr equ *+1 flashbyteop sta $aaaa rts flashend FEEDBACKEND rts flashincaddr inw flashaddr rts flashsetaddr stx flashaddr sty flashaddr+1 rts flashsetbank sta $d500,x lda $d013 sta $3fa rts flashcartoff pha sta $d580 lda $d013 sta $3fa cli pla rts