0 [if] \ Write support is complicated by the need to erase before \ writing and the possibly-different erase and write granularity. \ \ For NOR FLASH, where you can write as many times as you want \ while turning 1's into 0's, the algorithm is: \ \ Break the entire write range into pieces each contained in one \ erase unit. For each piece: \ \ Compare the existing and new contents to see if the unit needs erasing \ \ If no bits need to go from 0 to 1, erase is unnecessary, so just write. \ (It's a little more complicated if the write granularity is >1 byte.) \ \ Otherwise, copy the existing contents of the erase unit to a buffer, \ merge in the new data, erase, then write back the buffer. [then] \ dev /flash [ifndef] flash-write-enable also forth definitions defer flash-write-enable ( -- ) defer flash-write-disable ( -- ) defer flash-erase-block ( offset -- ) defer flash-write ( adr len offset -- ) defer flash-read ( adr len offset -- ) defer flash-verify ( adr len offset -- ) h# 10.0000 value /flash h# 10000 value /flash-block previous definitions [then] : left-in-block ( len offset -- #left ) \ Determine how many bytes are left in the page containing offset /flash-block swap /flash-block 1- and - ( len left-in-page ) min ( #left ) ; : must-erase? ( adr len -- flag ) device-base seek-ptr + ( adr len dev-adr ) swap 0 ?do ( adr dev-adr ) over i + c@ over i + c@ ( adr dev-adr new-byte old-byte ) \ Must erase if a bit in old-byte is 0 and that bit in new-byte is 1 invert and if ( adr dev-adr ) 2drop true unloop exit then ( adr dev-adr ) loop ( adr dev-adr ) 2drop false ; : erase+write ( adr len -- ) dup /flash-block = if \ If we are going to overwrite the entire block, there's no need to \ preserve the old data. This can only happen if we are already \ aligned on an erase block boundary. seek-ptr flash-erase-block ( adr len ) seek-ptr flash-write ( ) else \ Allocate a buffer to save the old block contents /flash-block alloc-mem >r ( adr len ) seek-ptr /flash-block round-down ( adr len block-start ) \ Copy existing data from FLASH block to the buffer dup device-base + r@ /flash-block lmove ( adr len block-start ) \ Merge new bytes into the buffer -rot ( block-start adr len ) seek-ptr /flash-block mod ( block-start adr len buf-offset ) r@ + swap move ( block-start ) \ Erase the block and rewrite it from the buffer dup flash-erase-block ( block-start ) r@ /flash-block rot flash-write ( ) \ Release the buffer r> /flash-block free-mem then ; : handle-block ( adr len -- adr' len' ) dup seek-ptr left-in-block ( adr len #left ) >r ( adr len r: #left ) over r@ must-erase? if ( adr len r: #left ) over r@ erase+write ( adr len r: #left ) else ( adr len r: #left ) over r@ seek-ptr flash-write ( adr len r: #left ) then ( adr len r: #left ) seek-ptr r@ + to seek-ptr ( adr len r: #left ) r> /string ( adr' len' ) ; : write ( adr len -- #written ) flash-write-enable tuck ( len adr len ) begin dup while handle-block repeat ( len adr' remain' ) 2drop ( len ) flash-write-disable ; \ dend