711 lines
27 KiB
Plaintext
711 lines
27 KiB
Plaintext
![]() |
{{
|
|||
|
' fsrw.spin 1.6 Copyright 2008 Radical Eye Software
|
|||
|
'
|
|||
|
' See end of file for terms of use.
|
|||
|
'
|
|||
|
' This object provides FAT16 file read/write access on a block device.
|
|||
|
' Only one file open at a time. Open modes are 'r' (read), 'a' (append),
|
|||
|
' 'w' (write), and 'd' (delete). Only the root directory is supported.
|
|||
|
' No long filenames are supported. We also support traversing the
|
|||
|
' root directory.
|
|||
|
'
|
|||
|
' In general, negative return values are errors; positive return
|
|||
|
' values are success. Other than -1 on popen when the file does not
|
|||
|
' exist, all negative return values will be "aborted" rather than
|
|||
|
' returned.
|
|||
|
'
|
|||
|
' Changes:
|
|||
|
' v1.1 28 December 2006 Fixed offset for ctime
|
|||
|
' v1.2 29 December 2006 Made default block driver be fast one
|
|||
|
' v1.3 6 January 2007 Added some docs, and a faster asm
|
|||
|
' v1.4 4 February 2007 Rearranged vars to save memory;
|
|||
|
' eliminated need for adjacent pins;
|
|||
|
' reduced idle current consumption; added
|
|||
|
' sample code with abort code data
|
|||
|
' v1.5 7 April 2007 Fixed problem when directory is larger
|
|||
|
' than a cluster.
|
|||
|
' v1.6 23 September 2008 Fixed a bug found when mixing pputc
|
|||
|
' with pwrite. Also made the assembly
|
|||
|
' routines a bit more cautious.
|
|||
|
}}
|
|||
|
'
|
|||
|
' Constants describing FAT volumes.
|
|||
|
'
|
|||
|
con
|
|||
|
SECTORSIZE = 512
|
|||
|
SECTORSHIFT = 9
|
|||
|
DIRSIZE = 32
|
|||
|
DIRSHIFT = 5
|
|||
|
'
|
|||
|
' The object that provides the block-level access.
|
|||
|
'
|
|||
|
obj
|
|||
|
sdspi: "admflash-sdspiqasm"
|
|||
|
|
|||
|
var
|
|||
|
'
|
|||
|
'
|
|||
|
' Variables concerning the open file.
|
|||
|
'
|
|||
|
long fclust ' the current cluster number
|
|||
|
long filesize ' the total current size of the file
|
|||
|
long floc ' the seek position of the file
|
|||
|
long frem ' how many bytes remain in this cluster from this file
|
|||
|
long bufat ' where in the buffer our current character is
|
|||
|
long bufend ' the last valid character (read) or free position (write)
|
|||
|
long direntry ' the byte address of the directory entry (if open for write)
|
|||
|
long writelink ' the byte offset of the disk location to store a new cluster
|
|||
|
long fatptr ' the byte address of the most recently written fat entry
|
|||
|
|
|||
|
long fsize ' dateigr<67><72>e
|
|||
|
long fattrib ' dateiattribute
|
|||
|
long ftime ' zeitstempel
|
|||
|
'
|
|||
|
' Variables used when mounting to describe the FAT layout of the card.
|
|||
|
'
|
|||
|
long rootdir ' the byte address of the start of the root directory
|
|||
|
long rootdirend ' the byte immediately following the root directory.
|
|||
|
long dataregion ' the start of the data region, offset by two sectors
|
|||
|
long clustershift ' log base 2 of blocks per cluster
|
|||
|
long fat1 ' the block address of the fat1 space
|
|||
|
long totclusters ' how many clusters in the volume
|
|||
|
long sectorsperfat ' how many sectors per fat
|
|||
|
'
|
|||
|
' Variables controlling the caching.
|
|||
|
'
|
|||
|
long lastread ' the block address of the buf2 contents
|
|||
|
long dirty ' nonzero if buf2 is dirty
|
|||
|
'
|
|||
|
' Buffering: two sector buffers. These two buffers must be longword
|
|||
|
' aligned! To ensure this, make sure they are the first byte variables
|
|||
|
' defined in this object.
|
|||
|
'
|
|||
|
byte buf[SECTORSIZE] ' main data buffer
|
|||
|
byte buf2[SECTORSIZE] ' main metadata buffer
|
|||
|
byte padname[11] ' filename buffer
|
|||
|
|
|||
|
CON { SEKTORINTERFACE
|
|||
|
das sektorinterface arbeitet mit zusammenh<6E>ngenden containerdateien und erm<72>glicht einen wahlfreien
|
|||
|
zugriff auf die daten. die containerdatei werden mit dem entsprechenden tool auf einer leeren und
|
|||
|
frisch formatierten karte erzeugt.
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
PUB sec_start(strptr): s | err 'sec: startsektor einer containerdatei ermitteln
|
|||
|
{{ conainerdatei wird ge<67>ffnet und startsektor berechnet. die sektornummer wird zur<75>ckgeliefert;
|
|||
|
bei einem fehler der wert -1}}
|
|||
|
s~
|
|||
|
err := popen(strptr,"r") 'containerdatei <20>ffnen
|
|||
|
ifnot err 'kein fehler bei <20>ffnen des containers
|
|||
|
s := (fclust << clustershift) + dataregion 'startsektor berechnen
|
|||
|
pclose 'containerdatei schlie<69>en
|
|||
|
return s
|
|||
|
else
|
|||
|
return -1
|
|||
|
|
|||
|
' sequenz aus pfillbuf:
|
|||
|
' sdspi.readblock(datablock, @buf) 'cluster einlesen
|
|||
|
|
|||
|
' sequenz aus datablock:
|
|||
|
' return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1))
|
|||
|
|
|||
|
|
|||
|
PUB sec_readblock(secnr, badr)
|
|||
|
sdspi.readblock(secnr, badr)
|
|||
|
|
|||
|
PUB sec_writeblock(secnr, badr)
|
|||
|
sdspi.writeblock(secnr, badr)
|
|||
|
|
|||
|
|
|||
|
CON { FAT16-Code
|
|||
|
|
|||
|
}
|
|||
|
pri writeblock2(n, b)
|
|||
|
'
|
|||
|
' On metadata writes, if we are updating the FAT region, also update
|
|||
|
' the second FAT region.
|
|||
|
'
|
|||
|
sdspi.writeblock(n, b)
|
|||
|
if (n => fat1 and n < fat1 + sectorsperfat)
|
|||
|
sdspi.writeblock(n+sectorsperfat, b)
|
|||
|
|
|||
|
pri flushifdirty
|
|||
|
'
|
|||
|
' If the metadata block is dirty, write it out.
|
|||
|
'
|
|||
|
if (dirty)
|
|||
|
writeblock2(lastread, @buf2)
|
|||
|
dirty := 0
|
|||
|
|
|||
|
pri readblockc(n)
|
|||
|
'
|
|||
|
' Read a block into the metadata buffer, if that block is not already
|
|||
|
' there.
|
|||
|
'
|
|||
|
if (n <> lastread)
|
|||
|
flushifdirty
|
|||
|
sdspi.readblock(n, @buf2)
|
|||
|
lastread := n
|
|||
|
|
|||
|
pri brword(b)
|
|||
|
'
|
|||
|
' Read a byte-reversed word from a (possibly odd) address.
|
|||
|
'
|
|||
|
return (byte[b]) + ((byte[b][1]) << 8)
|
|||
|
|
|||
|
pri brlong(b)
|
|||
|
'
|
|||
|
' Read a byte-reversed long from a (possibly odd) address.
|
|||
|
'
|
|||
|
return brword(b) + (brword(b+2) << 16)
|
|||
|
|
|||
|
pri brwword(w, v)
|
|||
|
'
|
|||
|
' Write a byte-reversed word to a (possibly odd) address, and
|
|||
|
' mark the metadata buffer as dirty.
|
|||
|
'
|
|||
|
byte[w++] := v
|
|||
|
byte[w] := v >> 8
|
|||
|
dirty := 1
|
|||
|
|
|||
|
pri brwlong(w, v)
|
|||
|
'
|
|||
|
' Write a byte-reversed long to a (possibly odd) address, and
|
|||
|
' mark the metadata buffer as dirty.
|
|||
|
'
|
|||
|
brwword(w, v)
|
|||
|
brwword(w+2, v >> 16)
|
|||
|
|
|||
|
pub mount(basepin) | start, sectorspercluster, reserved, rootentries, sectors
|
|||
|
{{
|
|||
|
' Mount a volume. The address passed in is passed along to the block
|
|||
|
' layer; see the currently used block layer for documentation. If the
|
|||
|
' volume mounts, a 0 is returned, else abort is called.
|
|||
|
}}
|
|||
|
sdspi.start(basepin)
|
|||
|
lastread := -1
|
|||
|
dirty := 0
|
|||
|
sdspi.readblock(0, @buf)
|
|||
|
if (brlong(@buf+$36) == constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)))
|
|||
|
start := 0
|
|||
|
else
|
|||
|
start := brlong(@buf+$1c6)
|
|||
|
sdspi.readblock(start, @buf)
|
|||
|
if (brlong(@buf+$36) <> constant("F" + ("A" << 8) + ("T" << 16) + ("1" << 24)) or buf[$3a] <> "6")
|
|||
|
return 1 ' not a fat16 volume
|
|||
|
if (brword(@buf+$0b) <> SECTORSIZE)
|
|||
|
return 2 ' bad bytes per sector
|
|||
|
sectorspercluster := buf[$0d]
|
|||
|
if (sectorspercluster & (sectorspercluster - 1))
|
|||
|
return 3 ' bad sectors per cluster
|
|||
|
clustershift := 0
|
|||
|
repeat while (sectorspercluster > 1)
|
|||
|
clustershift++
|
|||
|
sectorspercluster >>= 1
|
|||
|
sectorspercluster := 1 << clustershift
|
|||
|
reserved := brword(@buf+$0e)
|
|||
|
if (buf[$10] <> 2)
|
|||
|
return 4 ' not two FATs
|
|||
|
rootentries := brword(@buf+$11)
|
|||
|
sectors := brword(@buf+$13)
|
|||
|
if (sectors == 0)
|
|||
|
sectors := brlong(@buf+$20)
|
|||
|
sectorsperfat := brword(@buf+$16)
|
|||
|
if (brword(@buf+$1fe) <> $aa55)
|
|||
|
return 5 ' bad FAT signature
|
|||
|
fat1 := start + reserved
|
|||
|
rootdir := (fat1 + 2 * sectorsperfat) << SECTORSHIFT
|
|||
|
rootdirend := rootdir + (rootentries << DIRSHIFT)
|
|||
|
dataregion := 1 + ((rootdirend - 1) >> SECTORSHIFT) - 2 * sectorspercluster
|
|||
|
totclusters := ((sectors - dataregion + start) >> clustershift)
|
|||
|
if (totclusters > $fff0)
|
|||
|
return 6 ' too many clusters
|
|||
|
return 0
|
|||
|
|
|||
|
pri readbytec(byteloc)
|
|||
|
'
|
|||
|
' Read a byte address from the disk through the metadata buffer and
|
|||
|
' return a pointer to that location.
|
|||
|
'
|
|||
|
readblockc(byteloc >> SECTORSHIFT)
|
|||
|
return @buf2 + (byteloc & constant(SECTORSIZE - 1))
|
|||
|
|
|||
|
pri readfat(clust)
|
|||
|
'
|
|||
|
' Read a fat location and return a pointer to the location of that
|
|||
|
' entry.
|
|||
|
'
|
|||
|
fatptr := (fat1 << SECTORSHIFT) + (clust << 1)
|
|||
|
return readbytec(fatptr)
|
|||
|
|
|||
|
pri followchain | clust
|
|||
|
'
|
|||
|
' Follow the fat chain and update the writelink.
|
|||
|
'
|
|||
|
clust := brword(readfat(fclust))
|
|||
|
writelink := fatptr
|
|||
|
return clust
|
|||
|
|
|||
|
pri nextcluster | clust
|
|||
|
'
|
|||
|
' Read the next cluster and return it. Set up writelink to
|
|||
|
' point to the cluster we just read, for later updating. If the
|
|||
|
' cluster number is bad, return a negative number.
|
|||
|
'
|
|||
|
clust := followchain
|
|||
|
if (clust < 2 or clust => totclusters)
|
|||
|
abort(-9) ' bad cluster value
|
|||
|
return clust
|
|||
|
|
|||
|
pri freeclusters(clust) | bp
|
|||
|
'
|
|||
|
' Free an entire cluster chain. Used by remove and by overwrite.
|
|||
|
' Assumes the pointer has already been cleared/set to $ffff.
|
|||
|
'
|
|||
|
repeat while (clust < $fff0)
|
|||
|
if (clust < 2)
|
|||
|
abort(-26) ' bad cluster number")
|
|||
|
bp := readfat(clust)
|
|||
|
clust := brword(bp)
|
|||
|
brwword(bp, 0)
|
|||
|
flushifdirty
|
|||
|
|
|||
|
pri datablock
|
|||
|
'
|
|||
|
' Calculate the block address of the current data location.
|
|||
|
'
|
|||
|
return (fclust << clustershift) + dataregion + ((floc >> SECTORSHIFT) & ((1 << clustershift) - 1))
|
|||
|
|
|||
|
pri uc(c)
|
|||
|
'
|
|||
|
' Compute the upper case version of a character.
|
|||
|
'
|
|||
|
if ("a" =< c and c =< "z")
|
|||
|
return c - 32
|
|||
|
return c
|
|||
|
|
|||
|
pri pflushbuf(r, metadata) | cluststart, newcluster, count, i
|
|||
|
'
|
|||
|
' Flush the current buffer, if we are open for write. This may
|
|||
|
' allocate a new cluster if needed. If metadata is true, the
|
|||
|
' metadata is written through to disk including any FAT cluster
|
|||
|
' allocations and also the file size in the directory entry.
|
|||
|
'
|
|||
|
if (direntry == 0)
|
|||
|
abort(-27) ' not open for writing
|
|||
|
if (r > 0) ' must *not* allocate cluster if flushing an empty buffer
|
|||
|
if (frem < SECTORSIZE)
|
|||
|
' find a new clustercould be anywhere! If possible, stay on the
|
|||
|
' same page used for the last cluster.
|
|||
|
newcluster := -1
|
|||
|
cluststart := fclust & constant(!((SECTORSIZE >> 1) - 1))
|
|||
|
count := 2
|
|||
|
repeat
|
|||
|
readfat(cluststart)
|
|||
|
repeat i from 0 to constant(SECTORSIZE - 2) step 2
|
|||
|
if (buf2[i]==0 and buf2[i+1]==0)
|
|||
|
newcluster := cluststart + (i >> 1)
|
|||
|
if (newcluster => totclusters)
|
|||
|
newcluster := -1
|
|||
|
quit
|
|||
|
if (newcluster > 1)
|
|||
|
brwword(@buf2+i, -1)
|
|||
|
brwword(readbytec(writelink), newcluster)
|
|||
|
writelink := fatptr + i
|
|||
|
fclust := newcluster
|
|||
|
frem := SECTORSIZE << clustershift
|
|||
|
quit
|
|||
|
else
|
|||
|
cluststart += constant(SECTORSIZE >> 1)
|
|||
|
if (cluststart => totclusters)
|
|||
|
cluststart := 0
|
|||
|
count--
|
|||
|
if (count < 0)
|
|||
|
r := -5 ' No space left on device
|
|||
|
quit
|
|||
|
if (frem => SECTORSIZE)
|
|||
|
sdspi.writeblock(datablock, @buf)
|
|||
|
if (r == SECTORSIZE) ' full buffer, clear it
|
|||
|
floc += r
|
|||
|
frem -= r
|
|||
|
bufat := 0
|
|||
|
bufend := r
|
|||
|
else
|
|||
|
' not a full blockleave pointers alone
|
|||
|
if (r < 0 or metadata) ' update metadata even if error
|
|||
|
readblockc(direntry >> SECTORSHIFT) ' flushes unwritten FAT too
|
|||
|
brwlong(@buf2+(direntry & constant(SECTORSIZE-1))+28, floc+bufat)
|
|||
|
flushifdirty
|
|||
|
if (r < 0)
|
|||
|
abort(r)
|
|||
|
return r
|
|||
|
|
|||
|
pub pflush
|
|||
|
{{
|
|||
|
' Call flush with the current data buffer location, and the flush
|
|||
|
' metadata flag set.
|
|||
|
}}
|
|||
|
return pflushbuf(bufat, 1)
|
|||
|
|
|||
|
pri pfillbuf | r
|
|||
|
'
|
|||
|
' Get some data into an empty buffer. If no more data is available,
|
|||
|
' return -1. Otherwise return the number of bytes read into the
|
|||
|
' buffer.
|
|||
|
'
|
|||
|
if (floc => filesize)
|
|||
|
return -1
|
|||
|
if (frem == 0)
|
|||
|
fclust := nextcluster 'n<>chster cluster
|
|||
|
frem := SECTORSIZE << clustershift
|
|||
|
if (frem + floc > filesize)
|
|||
|
frem := filesize - floc
|
|||
|
sdspi.readblock(datablock, @buf) 'cluster einlesen
|
|||
|
r := SECTORSIZE
|
|||
|
if (floc + r => filesize)
|
|||
|
r := filesize - floc
|
|||
|
floc += r
|
|||
|
frem -= r
|
|||
|
bufat := 0
|
|||
|
bufend := r
|
|||
|
return r
|
|||
|
|
|||
|
pub pclose | r
|
|||
|
{{
|
|||
|
' Flush and close the currently open file if any. Also reset the
|
|||
|
' pointers to valid values. If there is no error, 0 will be returned.
|
|||
|
}}
|
|||
|
r := 0
|
|||
|
if (direntry)
|
|||
|
r := pflush
|
|||
|
bufat := 0
|
|||
|
bufend := 0
|
|||
|
filesize := 0
|
|||
|
floc := 0
|
|||
|
frem := 0
|
|||
|
writelink := 0
|
|||
|
direntry := 0
|
|||
|
fclust := 0
|
|||
|
return r
|
|||
|
|
|||
|
pri pdate
|
|||
|
{{
|
|||
|
' Get the current date and time, as a long, in the format required
|
|||
|
' by FAT16. Right now it"s hardwired to return the date this
|
|||
|
' software was created on (April 7, 2007). You can change this
|
|||
|
' to return a valid date/time if you have access to this data in
|
|||
|
' your setup.
|
|||
|
}}
|
|||
|
return constant(((2007-1980) << 25) + (1 << 21) + (7 << 16) + (4 << 11))
|
|||
|
|
|||
|
pub popen(s, mode) | i, sentinel, dirptr, freeentry
|
|||
|
{{
|
|||
|
' Close any currently open file, and open a new one with the given
|
|||
|
' file name and mode. Mode can be "r" "w" "a" or "d" (delete).
|
|||
|
' If the file is opened successfully, 0 will be returned. If the
|
|||
|
' file did not exist, and the mode was not "w" or "a", -1 will be
|
|||
|
' returned. Otherwise abort will be called with a negative error
|
|||
|
' code.
|
|||
|
'
|
|||
|
' s - zeiger auf string mit dateinamen
|
|||
|
' mode - r, w, a, d
|
|||
|
}}
|
|||
|
pclose
|
|||
|
' ----------------------------------------------------------- dateinamen aufbereiten
|
|||
|
i := 0
|
|||
|
repeat while (i<8 and byte[s] and byte[s] <> ".") 'kopiert dateinamen ohne extender
|
|||
|
padname[i++] := uc(byte[s++]) 'uc wandelt in kleinbuchstaben
|
|||
|
repeat while (i<8)
|
|||
|
padname[i++] := " "
|
|||
|
repeat while (byte[s] and byte[s] <> ".")
|
|||
|
s++
|
|||
|
if (byte[s] == ".")
|
|||
|
s++
|
|||
|
repeat while (i<11 and byte[s])
|
|||
|
padname[i++] := uc(byte[s++])
|
|||
|
repeat while (i < 11)
|
|||
|
padname[i++] := " "
|
|||
|
' ----------------------------------------------------------- datei im verzeichnis suchen
|
|||
|
sentinel := 0
|
|||
|
freeentry := 0
|
|||
|
repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE
|
|||
|
s := readbytec(dirptr)
|
|||
|
if (freeentry == 0 and (byte[s] == 0 or byte[s] == $e5))
|
|||
|
freeentry := dirptr
|
|||
|
if (byte[s] == 0)
|
|||
|
sentinel := dirptr
|
|||
|
quit
|
|||
|
repeat i from 0 to 10 'vergleicht eintrag mit dateinamen
|
|||
|
if (padname[i] <> byte[s][i]) 'bei gleichheit i == 11
|
|||
|
quit
|
|||
|
if (i == 11 and 0 == (byte[s][$0b] & $18)) 'dateiname gefunden und kein verzeichnis/volume
|
|||
|
fclust := brword(s+$1a) 'startcluster der datei lesen und als aktuell setzen
|
|||
|
filesize := brlong(s+$1c) 'dateigr<67><72>e der datei lesen
|
|||
|
fsize := filesize 'dateigr<67><72>e der datei lesen
|
|||
|
|
|||
|
if (mode == "r") 'DATEI LESEN
|
|||
|
frem := SECTORSIZE << clustershift
|
|||
|
if (frem > filesize)
|
|||
|
frem := filesize
|
|||
|
return 0
|
|||
|
|
|||
|
if (byte[s][11] & $d9) ' datei ist schreibgesch<63>tzt
|
|||
|
abort(-6) ' no permission to write
|
|||
|
|
|||
|
if (mode == "d") 'DATEI L<>SCHEN
|
|||
|
brwword(s, $e5) 'verzeichniseintrag als gel<65>scht markieren
|
|||
|
freeclusters(fclust) 'cluster freigeben
|
|||
|
flushifdirty
|
|||
|
return 0
|
|||
|
|
|||
|
if (mode == "w") 'DATEI SCHREIBEN/<2F>BERSCHREIBEN
|
|||
|
brwword(s+26, -1) 'bestehende clusterreferenz ung<6E>ltig machen
|
|||
|
brwlong(s+28, 0) 'gr<67><72>e der neuen datei auf 0 setzen
|
|||
|
writelink := dirptr + 26
|
|||
|
direntry := dirptr
|
|||
|
freeclusters(fclust) 'bestehende clusterkette freigeben
|
|||
|
bufend := SECTORSIZE
|
|||
|
fclust := 0
|
|||
|
filesize := 0
|
|||
|
frem := 0
|
|||
|
return 0
|
|||
|
|
|||
|
elseif (mode == "a") 'DATEI ANH<4E>NGEN
|
|||
|
' this code will eventually be moved to seek
|
|||
|
frem := filesize
|
|||
|
freeentry := SECTORSIZE << clustershift 'freeentry = clustergr<67><72>e in bytes
|
|||
|
if (fclust => $fff0)
|
|||
|
fclust := 0
|
|||
|
repeat while (frem > freeentry) 'bis zum letzten cluster springen
|
|||
|
if (fclust < 2)
|
|||
|
abort(-7) ' eof repeat while following chain
|
|||
|
fclust := nextcluster 'springe zum n<>chsten cluster
|
|||
|
frem -= freeentry 'berechne neue gr<67><72>e bis dateiende
|
|||
|
'in frem bleiben anzahl bytes im letzten cluster
|
|||
|
floc := filesize & constant(!(SECTORSIZE - 1)) 'sektornummer dateiende berechnen
|
|||
|
bufend := SECTORSIZE
|
|||
|
bufat := frem & constant(SECTORSIZE - 1)
|
|||
|
writelink := dirptr + 26
|
|||
|
direntry := dirptr
|
|||
|
if (bufat)
|
|||
|
sdspi.readblock(datablock, @buf)
|
|||
|
frem := freeentry - (floc & (freeentry - 1))
|
|||
|
else
|
|||
|
if (fclust < 2 or frem == freeentry)
|
|||
|
frem := 0
|
|||
|
else
|
|||
|
frem := freeentry - (floc & (freeentry - 1))
|
|||
|
if (fclust => 2)
|
|||
|
followchain
|
|||
|
return 0
|
|||
|
else
|
|||
|
abort(-3) ' bad argument
|
|||
|
|
|||
|
' ----------------------------------------------------------- datei nicht gefunden, neue datei erzeugen
|
|||
|
|
|||
|
if (mode <> "w" and mode <> "a")
|
|||
|
return -1 ' not found
|
|||
|
direntry := freeentry
|
|||
|
if (direntry == 0)
|
|||
|
abort(-2) ' no empty directory entry
|
|||
|
' write (or new append): create valid directory entry
|
|||
|
s := readbytec(direntry)
|
|||
|
bytefill(s, 0, DIRSIZE)
|
|||
|
bytemove(s, @padname, 11)
|
|||
|
brwword(s+26, -1)
|
|||
|
i := pdate
|
|||
|
brwlong(s+$e, i) ' write create time and date
|
|||
|
brwlong(s+$16, i) ' write last modified date and time
|
|||
|
if (direntry == sentinel and direntry + DIRSIZE < rootdirend)
|
|||
|
brwword(readbytec(direntry+DIRSIZE), 0)
|
|||
|
flushifdirty
|
|||
|
writelink := direntry + 26
|
|||
|
fclust := 0
|
|||
|
bufend := SECTORSIZE
|
|||
|
return 0
|
|||
|
|
|||
|
pub pseek(bytenr) | freeentry,dirptr 'setzt zeiger auf position
|
|||
|
{{
|
|||
|
- springt erst alle ganzen cluster weiter
|
|||
|
- restbytes im letzten cluster werden geladen
|
|||
|
|
|||
|
- code funktioniert nicht!!!
|
|||
|
}}
|
|||
|
|
|||
|
{{
|
|||
|
frem := bytenr
|
|||
|
freeentry := SECTORSIZE << clustershift 'freeentry = clustergr<67><72>e in bytes
|
|||
|
if (fclust => $fff0)
|
|||
|
fclust := 0
|
|||
|
repeat while (frem > freeentry) 'bis zum cluster springen
|
|||
|
if (fclust < 2)
|
|||
|
abort(-7) 'eof repeat while following chain
|
|||
|
fclust := nextcluster 'springe zum n<>chsten cluster
|
|||
|
frem -= freeentry 'berechne neue gr<67><72>e bis dateiende
|
|||
|
'in frem bleiben anzahl bytes im letzten cluster
|
|||
|
floc := frem & constant(!(SECTORSIZE - 1)) 'sektornummer berechnen
|
|||
|
bufend := SECTORSIZE
|
|||
|
bufat := frem & constant(SECTORSIZE - 1) 'restbytes <20>ber clustergrenze berechnen
|
|||
|
writelink := dirptr + 26
|
|||
|
direntry := dirptr
|
|||
|
if (bufat)
|
|||
|
sdspi.readblock(datablock, @buf) 'restbytes einlesen in buf
|
|||
|
frem := freeentry - (floc & (freeentry - 1))
|
|||
|
else
|
|||
|
if (fclust < 2 or frem == freeentry)
|
|||
|
frem := 0
|
|||
|
else
|
|||
|
frem := freeentry - (floc & (freeentry - 1))
|
|||
|
if (fclust => 2)
|
|||
|
followchain
|
|||
|
return 0
|
|||
|
}}
|
|||
|
|
|||
|
|
|||
|
pub pread(ubuf, count) | r, t
|
|||
|
{{
|
|||
|
' Read count bytes into the buffer ubuf. Returns the number of bytes
|
|||
|
' successfully read, or a negative number if there is an error.
|
|||
|
' The buffer may be as large as you want.
|
|||
|
}}
|
|||
|
r := 0
|
|||
|
repeat while (count > 0)
|
|||
|
if (bufat => bufend)
|
|||
|
t := pfillbuf
|
|||
|
if (t =< 0)
|
|||
|
if (r > 0)
|
|||
|
return r
|
|||
|
return t
|
|||
|
t := bufend - bufat
|
|||
|
if (t > count)
|
|||
|
t := count
|
|||
|
bytemove(ubuf, @buf+bufat, t)
|
|||
|
bufat += t
|
|||
|
r += t
|
|||
|
ubuf += t
|
|||
|
count -= t
|
|||
|
return r
|
|||
|
|
|||
|
pub pgetc | t
|
|||
|
{{
|
|||
|
' Read and return a single character. If the end of file is
|
|||
|
' reached, -1 will be returned. If an error occurs, a negative
|
|||
|
' number will be returned.
|
|||
|
}}
|
|||
|
if (bufat => bufend)
|
|||
|
t := pfillbuf
|
|||
|
if (t =< 0)
|
|||
|
return -1
|
|||
|
return (buf[bufat++])
|
|||
|
|
|||
|
pub pwrite(ubuf, count) | r, t
|
|||
|
{{
|
|||
|
' Write count bytes from the buffer ubuf. Returns the number of bytes
|
|||
|
' successfully written, or a negative number if there is an error.
|
|||
|
' The buffer may be as large as you want.
|
|||
|
}}
|
|||
|
t := 0
|
|||
|
repeat while (count > 0)
|
|||
|
if (bufat => bufend)
|
|||
|
t := pflushbuf(bufat, 0)
|
|||
|
t := bufend - bufat
|
|||
|
if (t > count)
|
|||
|
t := count
|
|||
|
bytemove(@buf+bufat, ubuf, t)
|
|||
|
r += t
|
|||
|
bufat += t
|
|||
|
ubuf += t
|
|||
|
count -= t
|
|||
|
return t
|
|||
|
|
|||
|
pub pputc(c)
|
|||
|
{{
|
|||
|
' Write a single character into the file open for write. Returns
|
|||
|
' 0 if successful, or a negative number if some error occurred.
|
|||
|
}}
|
|||
|
if (bufat == SECTORSIZE) 'ist sectorende erreicht?
|
|||
|
pflushbuf(SECTORSIZE, 0)
|
|||
|
buf[bufat++] := c
|
|||
|
return 0
|
|||
|
|
|||
|
pub opendir | off
|
|||
|
{{
|
|||
|
' Close the currently open file, and set up the read buffer for
|
|||
|
' calls to nextfile.
|
|||
|
}}
|
|||
|
pclose
|
|||
|
off := rootdir - (dataregion << SECTORSHIFT)
|
|||
|
fclust := off >> (clustershift + SECTORSHIFT)
|
|||
|
floc := off - (fclust << (clustershift + SECTORSHIFT))
|
|||
|
frem := rootdirend - rootdir
|
|||
|
filesize := floc + frem
|
|||
|
return 0
|
|||
|
|
|||
|
pub nextfile(fbuf) | i, t, at, lns
|
|||
|
{{
|
|||
|
' Find the next file in the root directory and extract its
|
|||
|
' (8.3) name into fbuf. Fbuf must be sized to hold at least
|
|||
|
' 13 characters (8 + 1 + 3 + 1). If there is no next file,
|
|||
|
' -1 will be returned. If there is, 0 will be returned.
|
|||
|
}}
|
|||
|
repeat
|
|||
|
if (bufat => bufend)
|
|||
|
t := pfillbuf
|
|||
|
if (t < 0)
|
|||
|
return t
|
|||
|
if (((floc >> SECTORSHIFT) & ((1 << clustershift) - 1)) == 0)
|
|||
|
fclust++
|
|||
|
at := @buf + bufat
|
|||
|
|
|||
|
if (byte[at] == 0) 'verzeichnisende erreicht
|
|||
|
return -1
|
|||
|
|
|||
|
bufat += DIRSIZE
|
|||
|
if (byte[at] <> $e5 and (byte[at][$0b] & $18) == 0)
|
|||
|
fsize := brlong(at+$1c) 'dateigr<67><72>e der datei lesen
|
|||
|
fattrib := byte[at][$0b] 'attribute setzen
|
|||
|
ftime := brlong(at+$0e) 'zeitstempel setzen
|
|||
|
lns := fbuf
|
|||
|
repeat i from 0 to 10
|
|||
|
byte[fbuf] := byte[at][i]
|
|||
|
fbuf++
|
|||
|
if (byte[at][i] <> " ")
|
|||
|
lns := fbuf
|
|||
|
if (i == 7 or i == 10)
|
|||
|
fbuf := lns
|
|||
|
if (i == 7)
|
|||
|
byte[fbuf] := "."
|
|||
|
fbuf++
|
|||
|
byte[fbuf] := 0
|
|||
|
return 0
|
|||
|
|
|||
|
pub getfsize 'dateigr<67><72>e <20>bergeben
|
|||
|
return fsize
|
|||
|
|
|||
|
pub getfattrib 'attribute <20>bergeben
|
|||
|
return fattrib
|
|||
|
|
|||
|
pub getftime 'zeitstempel <20>bergeben
|
|||
|
return ftime
|
|||
|
|
|||
|
|
|||
|
{{
|
|||
|
' Permission is hereby granted, free of charge, to any person obtaining
|
|||
|
' a copy of this software and associated documentation files
|
|||
|
' (the "Software"), to deal in the Software without restriction,
|
|||
|
' including without limitation the rights to use, copy, modify, merge,
|
|||
|
' publish, distribute, sublicense, and/or sell copies of the Software,
|
|||
|
' and to permit persons to whom the Software is furnished to do so,
|
|||
|
' subject to the following conditions:
|
|||
|
'
|
|||
|
' The above copyright notice and this permission notice shall be included
|
|||
|
' in all copies or substantial portions of the Software.
|
|||
|
'
|
|||
|
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|||
|
' EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|||
|
' MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|||
|
' IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|||
|
' CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|||
|
' TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|||
|
' SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
}}
|