blob: 8a94d69056c944d61d0cc103cf2a2c786b2ca419 [file] [log] [blame]
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