; 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 ; jump table softid_entry = 0 softid_exit = 3 flash_formatchip = 6 flash_formatsector = 9 flash_writebyte = 12 flash_lockchip = 15 flash_unlockchip = 18 flash_wait_unit = 21 ; data table ; 1 byte flash_sectorsizeMSB = 24 ; var bytes 0 terminated flash_idstr = 25 ; rw section, may be moved to ZP if needed M_VECTOR .word 0 m_vendor .byte 0 m_kind .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 ; this fails only when somebody stores vendor and product bytes ; at the proper cells. ; The second case is that there is no mem at all. ; Then two cases are possible: random number OR fixed ff. ; ; then in the code we call lda #offset/jsr jsrtovectorproc .if 0 check_type ldy #(2*(num_mems-1)) ?again sty m_iter ?again_loop ; store default values jsr flashsetbank0 ; load real values, may be random if no chip. lda $a000 sta m_vendor lda $a001 sta m_kind jsr jsrtosoftidentry jsr flashsetbank0 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 ; failed, no chip rts ?check_again ; if zero, decrement check_bad_counter sne dec check_bad_counter dec check_counter bne ?again_loop lda check_bad_counter cmp #CNTINIT ; if check_counter is 0 and check_bad_counter is CNTINIT then ok, else ; go to ?next bne ?next .endif check_type ldy #(2*(num_mems-1)) ?again sty ?m_iter lda #0 sta ?or_val sta ?or_val+1 sta ?c_iter lda #$ff sta ?and_val sta ?and_val+1 ?repeat jsr flashsetbank0 jsr jsrtosoftidentry lda $A000 sta ?_id_m lda $A001 sta ?_id_m+1 jsr jsrtosoftidexit lda $A000 sta ?_no_m lda $A001 sta ?_no_m+1 lda ?_id_m and:sta ?and_val lda ?_id_m+1 and:sta ?and_val+1 lda ?_id_m ora:sta ?or_val lda ?_id_m+1 ora:sta ?or_val+1 dec ?c_iter bne ?repeat ;@ ; lda #$ff ; sta $d01a ; lda #0 ; sta $d01a ; jmp @- cpw ?and_val ?or_val bne ?next_chip ; empty slot, random values, no chip, check next chip cpw ?and_val ?_no_m beq ?next_chip ; the came content, stable values, no response so no chip, check next chip ?OK lda ?_id_m sta m_vendor lda ?_id_m+1 sta m_vendor+1 lda M_CHECK_VECS+1,y sta M_VECTOR+1 lda M_CHECK_VECS,y sta M_VECTOR clc rts ?next_chip php ; store zero flag pla ldy ?m_iter dey dey jpl ?again pha ; restored zero flag plp sec rts ?or_val dta 0,0 ?and_val dta 0,0 ?_id_m dta 0,0 ?_no_m dta 0,0 ?c_iter .byte 0 ?m_iter .byte 0 jsrtosoftidexit lda #softid_exit jsr jsrtovectorproc lda $d013 sta $3fa clc rts jsrtosoftidentry lda M_CHECK_VECS+1,y ; first is softid entry sta M_VECTOR+1 pha lda M_CHECK_VECS,y ; first is softid entry sta M_VECTOR 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:#0 pha php rti ; a - vector value offset ; returns y-LSB A-MSB getvectorvalue clc adc M_VECTOR tay lda M_VECTOR+1 adc #0 rts ; -------------------------- flashformatchip2 ldx #$40 dta { bit.w } flashformatchip1 ldx #$0 ; -------------------------- ; PROCEDURE ; x = 0 or 0x40 - flash chip address. flashformatchip ; first check if not formatted stx store_x jsr flashcheckempty bcc flashformatexit sei ldx store_x lda #flash_formatchip jsr jsrtovectorproc ; preserves A ; not needed to mva $ff flashcmp jsr wait4flashcheckresult ; waits for format finished ; then check number of banks for FFs flashcheckempty lda #$3f ; this depends on flash size, $0f, $1f, $3f sta flashformatcounter flashbankloop sei ldx store_x sta flashformatcounter:$d5FF,x ; set chip (x) and bank ; set pages count, 8kB 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 flashcartoff ; preserves C pha sta $d580 lda $d013 sta $3fa cli pla rts 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 clc rts flashsectorformaterror jsr flashcartoff sec rts ; -------------------------- store_x dta 0 ; -------------------------- ; PROCEDURE flashformatsector ; x - bank number 00 - 7f (even sector>>1) ; a - page number in 128byte units $0-$3f ; erase sector containing address bank number * $2000 + page number * 128 stx flashformatstorex sei ; calculate lower 13 bits of sector ; (address in 8kb bank) ldx #0 ; convert page in A to cartridge a000-bfff address flashtmpaddr ; 0,1 -> 0 ; 2,3 -> 256 ; 4,5 -> 512 etc lsr ora #$A0 tay jsr flashsetaddr ;(x/y) store address for write byte to format sector ; ldx flashformatstorex:#0 ; filled before, 8k bank number lda #flash_formatsector ; does not touch A,X? jsr jsrtovectorproc ldy #{ sta.w } jsr flashprocessbyte ; format sector INVOKED ! jsr wait4flashcheckresult ; ;sei ldx flashformatstorex jsr flashsetbank0 cli ; check if all data in sector is $ff flashsectorformatgood ;jsr flashcartoff clc 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 .if (FRAME_FEEDBACK&2)==2 lda #34 ; ??? sta sdmctl sta dmactl .endif 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 lda #flash_writebyte ; 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 ; reset counter sta flashcnt+1 ; ldy #1 ; first time wait short first turn to speed up byte write. beq skipwsync 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 skipwsync ;lda #flash_wait_unit ;jsr jsrtovectorproc FEEDBACK ;dey ;bne @- ldy #{ lda.w } jsr flashprocessbyte sta flashval ldy #{ eor.w } jsr flashprocessbyte inc flashcnt bne cont inc flashcnt+1 bne cont jsr flashcartoff FEEDBACKEND lda #$ff ; status rts cont and #$40 bne flashwaitfordone jsr flashcartoff FEEDBACKEND lda flashval:#0 cmp flashcmp:#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 flashbyteop sta flashaddr:$aaaa rts flashend FEEDBACKEND rts flashincaddr inw flashaddr rts flashsetaddr stx flashaddr sty flashaddr+1 rts flashunlockchip lda #flash_unlockchip jmp jsrtovectorproc flashlockchip lda #flash_lockchip jmp jsrtovectorproc flashsetbank0 sta $d500,x pha lda $d013 sta $3fa pla rts