📄 fs2.c
字号:
#define FS_CALL_ANDOVER(lxd, src, len, dest) (lxd->andover(lxd, (long)(src), \
(word)(len), (char *)(dest)))
#define FS_CALL_ERASE(lxd, dest) (lxd->erase(lxd, (char *)(dest)))
#define FS_CALL_WRITE(lxd, src, len, dest) (lxd->write(lxd, (long)(src), \
(word)(len), (char *)(dest)))
#define FS_CALL_INIT(lxd) (lxd->init(lxd))
#define FS_CALL_FLUSH(lxd) (lxd->flush(lxd))
// Macros for accessing the lstab. These are sensitive to the size of FSLSnum!
// Convert LS number to physical address in lstab.
#define FS_LSTABPHYS(lxd, ls) ((lxd)->lstab + ((ls) << 1))
#define FS_LS2TABENT(tab, ls) ((tab) + ((ls) << 1))
// Function expects 1st arg (physical address) in BCDE. Returns 2-byte value at that
// addr.
root FSLSnum fs_lstabent(long cur_lsp);
// Function expects 1st arg (physical address) in BCDE. 2-byte value to put at that
// address is 2nd arg (on stack).
root void fs_set_lstabent(long lsp, FSLSnum ls);
// Filesystem checksumming routines. Use 1's complement addition. If a checksum
// evaluates to 0x0000, it is set to 0xFFFF (which is equivalent in 1's complement)
// then, when stored in its complemented form, will ensure it is not stored as 0xFFFF.
// If *chk is not null, it contains an initial sum. Otherwise, the initial sum is
// assumed to be zero. Return value is 0 if the result is 0xFFFF, else 1.
root int fs_checksum(FSchecksum * chk, void * buf, int len);
root int fs_checksum_x(FSchecksum * chk, long buf, int len);
/*** EndHeader */
#ifndef __TESTENV__
fs_nodebug root FSLSnum fs_lstabent(long cur_lsp)
{
#asm
ld a,c
ex de,hl
ldp hl,(hl)
#endasm
}
fs_nodebug root void fs_set_lstabent(long lsp, FSLSnum ls)
{
#asm
ld a,c
ex de,hl
ld ix,hl
ld hl,(sp+@sp+ls)
ldp (ix),hl
#endasm
}
#asm fs_nodebug
; Standalone core checksumming routine. On entry, IX=buffer address,
; HL=pointer to partial checksum (or NULL), IY=byte count.
; Returns HL=0 iff checksum totalled to 0xFFFF.
fs_ones_cpl_sum::
push hl ; Save partial chk addr
push iy ; Save length
ld a,h
or l
jr nz,load_init
ld d,a
ld e,a ; Init to zero if null ptr
jr cont
load_init:
ld e,(hl)
inc hl
ld d,(hl) ; DE = initial checksum
cont:
pop hl ; Get length
rr hl ; Round down to whole word count
jr z,lessthan2
push af ; Preserve C flag for odd byte
ld c,h ; Set up BC appropriately for loop count
ld b,l
ld a,b
or a ; Test B for zero and reset C flag
jr z,loop
inc c
loop:
ld hl,(ix)
adc hl,de
ex de,hl
inc ix
inc ix
djnz loop
dec c
jr nz,loop
jr nc,nocy
inc de
nocy:
pop af
lessthan2:
jr nc,nocy2
bool hl ; H = 0
ld l,(ix)
add hl,de
ex de,hl
jr nc,nocy2
inc de
nocy2:
ld a,d
or e
jr nz,nodec
dec de ; Turn 0 into 0xFFFF
nodec:
pop hl ; Get partial chk addr
ld a,h
or l
jr z,nostore
ld (hl),e
inc hl
ld (hl),d
nostore:
ex de,hl
inc hl
bool hl ; Return 0 iff result == 0xFFFF
ret
#endasm
fs_nodebug root int fs_checksum(FSchecksum * chk, void * buf, int len)
{
#asm
; HL = chk
ld ix,(sp+@sp+buf) ; Buffer address
ld iy,(sp+@sp+len) ; Length
call fs_ones_cpl_sum ; Call it and return
#endasm
}
fs_nodebug root int fs_checksum_x(FSchecksum * chk, long buf, int len)
{
// Do this by mapping buf into xpc window. Solves lot of problems
// related to ldp wrap-around.
auto int expc; // saved XPC
#asm
ld a,xpc
ld l,a
ld (sp+@sp+expc),hl ; save current xpc in expc
ld hl,(sp+@sp+buf)
ex de,hl
ld hl,(sp+@sp+buf+2) ; HLDE = buf
ld h,l
ld l,d
rr hl
rr hl
rr hl
rr hl
ld a,0xF2
add a,l
ld xpc,a
ld a,0x0F
and d
or 0xE0
ld l,e
ld h,a
ld ix,hl
ld iy,(sp+@sp+len)
ld hl,(sp+@sp+chk)
call fs_ones_cpl_sum
ex de,hl
ld hl,(sp+@sp+expc)
ld a,l
ld xpc,a
ex de,hl
#endasm
}
#endif // !__TESTENV__
/*** BeginHeader fs_get_headers */
root int fs_get_headers(FS_lxd * lxd, FSLSnum ls, FS_w * w, FS_h * h, FS_w * w1);
/*** EndHeader */
fs_nodebug root int fs_get_headers(FS_lxd * lxd, FSLSnum ls, FS_w * w, FS_h * h, FS_w * w1)
{
// Read appropriate header(s) from a given LS. Caller sets w and h to point to
// allocated structures which are filled in. FS_h is also used for FS_b.
// If no W-blocks are used, then *w is not touched. Otherwise, *w is set
// to the LS wear block (which may be dummy). If w1 is not NULL, then it will
// be set to the real W-block, which may be different from *w if the LS is not the
// first in its PS. w may also be NULL if not interested in dummy W-block.
// Note that the W-block (dummy or real) in the LS is used for checksumming.
auto char * maplog;
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = FS_CALL_MAP(lxd, FS_LS2OFFSET(lxd, ls));
if (lxd->wear_leveling) {
if (w) memcpy(w, maplog, sizeof(*w));
if (h) memcpy(h, maplog + sizeof(*w), sizeof(*h));
if (w1) {
if (lxd->ls_size < lxd->ps_size && ((FS_w *)maplog)->magic != FSMAGIC_W) {
// Real wear block is at start of PS.
maplog = FS_CALL_MAP(lxd, FS_PS2OFFSET(lxd, FS_LS2PS(lxd, ls)));
memcpy(w1, maplog, sizeof(*w1));
}
else
memcpy(w1, maplog, sizeof(*w1));
if (w1->magic == FSMAGIC_F) {
// Set initial pseudo wear to 0 if not known
w1->wear = 0;
w1->rwear = 0;
w1->wlo_num = 0;
}
}
}
else
if (h) memcpy(h, maplog, sizeof(*h));
#ifndef __TESTENV__
#asm
FS_RESTORE_XPC
#endasm
#endif
return 0;
}
/*** BeginHeader fs_insert_chain, fs_update_wear, fs_erase_physical, fs_erase_logical,
fs_erase_pbuf, fs_purge_file, fs_erase_chain, fs_expurgate, fs_free_logical */
void fs_insert_chain(FS_lxd * lxd, long chainp, FSLSnum entls);
void fs_update_wear(FS_w * w);
root int fs_erase_physical(FS_lxd * lxd, FSPSnum ps, FSwear wearlevel);
root int fs_erase_logical(FS_lxd * lxd, FSLSnum ls, char * erased);
void fs_erase_pbuf(word len);
int fs_purge_file(FS_ef * ef);
int fs_erase_chain(FS_lxd * lxd, long lsp, unsigned flags, int max_count,
FSseq while_seq, FSLSnum * count);
int fs_expurgate(FS_lxd * lxd, FSfilenum fn);
int fs_free_logical(FS_lxd * lxd, long lstabp, FSLSnum ls);
/*** EndHeader */
fs_nodebug void fs_insert_chain(FS_lxd * lxd, long chainp, FSLSnum entls)
{
// Insert LS entls into chain belonging to
// lxd. The start of the chain is indicated by the LS number at *chainp (which
// may not be in the lstab itself).
// The list is maintained in order of increasing wear count, so that the next
// free block (taken from the head of list) is the least worn. For the same
// wear count, the order is in increasing LS number. For non wear-leveling,
// the wear count is always zero, thus the list is simply in order of LS number.
// FIXME: this is a linear search. Need to improve efficiency.
auto FSLSnum chainls;
auto long entp;
auto FS_w w;
auto long wear;
int count;
count = 0;
w.wear = 0;
entp = FS_LSTABPHYS(lxd, entls);
chainls = fs_lstabent(chainp);
if (lxd->wear_leveling)
fs_get_headers(lxd, entls, NULL, NULL, &w); // Get new block's wear
wear = w.wear;
TRACE((" fs_insert chain: ls=%d, wear=%ld\n", (int)entls, wear));
for (;;) {
if (chainls == FS_INVALID_LS)
break;
if (lxd->wear_leveling)
fs_get_headers(lxd, chainls, NULL, NULL, &w);
if (w.wear > wear || w.wear == wear && chainls > entls)
break;
count++;
chainp = FS_LSTABPHYS(lxd, chainls);
chainls = fs_lstabent(chainp);
}
fs_set_lstabent(entp, chainls);
fs_set_lstabent(chainp, entls);
TRACE((" count=%d\n", count));
}
fs_nodebug void fs_update_wear(FS_w * w)
{
if (w->magic != FSMAGIC_W) {
w->magic = FSMAGIC_W;
w->wear = 0;
w->rwear = 0;
w->wlo_num = 0;
}
else {
w->wear++;
w->rwear++;
if (!w->rwear)
w->rwear = 0xFFFF;
}
}
fs_nodebug root int fs_erase_physical(FS_lxd * lxd, FSPSnum ps, FSwear wearlevel)
{
// Erase the specified physical sector if it is not already erased. Wear counts
// are incremented. This routine does _not_ update the lxtab.
auto int wblock;
auto FS_w_aug w;
auto long pw; // Physical addr of w.w or w.magic above.
auto word len; // Length of w+mag or just mag
auto char * maplog;
auto char * p;
auto int skip_erase;
auto long offs;
auto int ioffs;
auto long i;
auto long secsize;
auto int rc;
rc = 0;
secsize = lxd->ps_size;
wblock = lxd->wear_leveling;
if (!secsize)
secsize = lxd->ls_size; // For NVRAM with no physical sectors
else if (lxd->ps_per_ls > 1) {
if (ps & lxd->ps_per_ls-1)
// no wear block on PSs which are not first in LS
wblock = 0; // trick works because ls_size is power of 2
}
if (wblock)
ioffs = sizeof(FS_w);
else
ioffs = 0;
offs = FS_PS2OFFSET(lxd, ps);
#ifndef __TESTENV__
#asm
FS_SAVE_XPC
#endasm
#endif
maplog = (char *)FS_CALL_MAP(lxd, offs);
#ifndef __TESTENV__
#asm
FS_SAVE_XPC2
#endasm
#endif
if (wblock && /* (byte)*maplog != FSMAGIC_BAD && */ (byte)*maplog != FSMAGIC_W) {
// If the W-block is invalid, initialise it to the specified wearlevel and
// force a write.
skip_erase = 0;
}
else if (FS_IS_SSW(lxd)) {
// For SSW we deem the sector to be already erased if it starts with 0xFF (after
// the W-block, if any).
if (wblock && (byte)maplog[sizeof(FS_w)] == FSMAGIC_F ||
!wblock && (byte)maplog[0] == FSMAGIC_F)
skip_erase = 1;
else
skip_erase = 0;
}
else {
// Otherwise, the whole sector must be 0xFF.
#ifdef __TESTENV__
for (i = ioffs, p = maplog + ioffs;
i < secsize;
i++, p++)
if ((byte)*p != 0xFF) // unerased
break;
skip_erase = i == secsize;
#else
skip_erase = 0;
secsize-ioffs; // Load PS size (minus initial offset) into BCDE
#asm
; Loop based on B for innermost, DE for outer
ld b,e
ld e,d
ld d,c
ld a,b
or a
jr z,noinc
inc de
noinc:
exx
ld hl,(sp+@sp+ioffs)
ex de,hl
ld hl,(sp+@sp+maplog)
add hl,de ; HL points to logical data start
ld de,1 ; Incrementor
exx
loop2:
ld a,0xFF ; Comparand
exx
loop:
cp (hl)
jr nz,exit_loop
add hl,de
jr nc,noadj
ld hl,0xE000
ld a,xpc
add a,2
ld xpc,a
ld a,0xFF
noadj:
djnz loop
exx
dec de
ld a,d
or e
jr nz,loop2
ld hl,1
ld (sp+@sp+skip_erase),hl
exit_loop:
#endasm
#endif
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -