diff options
Diffstat (limited to 'appl/cmd/disk/ftl.b')
| -rw-r--r-- | appl/cmd/disk/ftl.b | 911 |
1 files changed, 911 insertions, 0 deletions
diff --git a/appl/cmd/disk/ftl.b b/appl/cmd/disk/ftl.b new file mode 100644 index 00000000..750defb7 --- /dev/null +++ b/appl/cmd/disk/ftl.b @@ -0,0 +1,911 @@ +# +# basic Flash Translation Layer driver +# see for instance the Intel technical paper +# ``Understanding the Flash Translation Layer (FTL) Specification'' +# Order number 297816-001 (online at www.intel.com) +# +# a public driver by David Hinds, dhinds@allegro.stanford.edu +# further helps with some details. +# +# this driver uses the common simplification of never storing +# the VBM on the medium (a waste of precious flash!) but +# rather building it on the fly as the block maps are read. +# +# Plan 9 driver (c) 1997 by C H Forsyth (forsyth@caldo.demon.co.uk) +# This driver may be used or adapted by anyone for any non-commercial purpose. +# +# adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (byteles@vitanuova.com) +# +# C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies +# to use this driver freely for any Inferno-related purposes whatever, +# including commercial applications. +# +# TO DO: +# check error handling details for get/put flash +# bad block handling +# reserved space in formatted size +# possibly block size as parameter +# fetch parameters from header on init +# +# Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited +# usage : ftl flashsize secsize inputfile outputfile +# outputfile will then be a ftl image of inputfile +# nb assumes the base address is zero +# +# Converted to limbo for Inferno 2000 by JR Firth, Vita Nuova Holdings Limited +# + +implement Ftlimage; + +include "sys.m"; +include "draw.m"; + +sys : Sys; + OREAD, OWRITE, FD, open, create, read, write, print, fprint : import sys; + +Ftlimage : module +{ + init : fn(nil : ref Draw->Context, argv : list of string); +}; + +stderr : ref FD; + +flashsize, secsize : int; +flashm : array of byte; +trace : int = 0; + +Eshift : con 18; # 2^18=256k; log2(eraseunit) +Flashseg : con 1<<Eshift; +Bshift : con 9; # 2^9=512 +Bsize : con 1<<Bshift; +BAMoffset : con 16r100; +Nolimit : con ~0; +USABLEPCT : con 95; # release only this % to client + +FTLDEBUG : con 0; + +# erase unit header (defined by FTL specification) +# offsets into Merase +O_LINKTUPLE : con 0; +O_ORGTUPLE : con 5; +O_NXFER : con 15; +O_NERASE : con 16; +O_ID : con 20; +O_BSHIFT : con 22; +O_ESHIFT : con 23; +O_PSTART : con 24; +O_NUNITS : con 26; +O_PSIZE : con 28; +O_VBMBASE : con 32; +O_NVBM : con 36; +O_FLAGS : con 38; +O_CODE : con 39; +O_SERIAL : con 40; +O_ALTOFFSET : con 44; +O_BAMOFFSET : con 48; +O_RSV2 : con 52; + +ERASEHDRLEN : con 64; + +# special unit IDs +XferID : con 16rffff; +XferBusy : con 16r7fff; + +# special BAM addresses +Bfree : con -1; #16rffffffff +Bwriting : con -2; #16rfffffffe +Bdeleted : con 0; + +# block types +TypeShift : con 7; +BlockType : con (1<<TypeShift)-1; +ControlBlock : con 16r30; +DataBlock : con 16r40; +ReplacePage : con 16r60; +BadBlock : con 16r70; + +BNO(va : int) : int +{ + return va>>Bshift; +} +MKBAM(b : int,t : int) : int +{ + return (b<<Bshift)|t; +} + +Terase : adt { + x : int; + id : int; + offset : int; + bamoffset : int; + nbam : int; + bam : array of byte; + bamx : int; + nfree : int; + nused : int; + ndead : int; + nbad : int; + nerase : int; +}; + +Ftl : adt { + base : int; # base of flash region + size : int; # size of flash region + segsize : int; # size of flash segment (erase unit) + eshift : int; # log2(erase-unit-size) + bshift : int; # log2(bsize) + bsize : int; + nunit : int; # number of segments (erase units) + unit : array of ref Terase; + lastx : int; # index in unit of last allocation + xfer : int; # index in unit of current transfer unit (-1 if none) + nfree : int; # total free space in blocks + nblock : int; # total space in blocks + rwlimit : int; # user-visible block limit (`formatted size') + vbm : array of int; # virtual block map + fstart : int; # address of first block of data in a segment + trace : int; # (debugging) trace of read/write actions + detach : int; # free Ftl on last close + + # scavenging variables + needspace : int; + hasproc : int; +}; + +# Ftl.detach +Detached : con 1; # detach on close +Deferred : con 2; # scavenger must free it + +ftls : ref Ftl; + +ftlstat(sz : int) +{ + print("16r%x:16r%x:16r%x\n", ftls.rwlimit*Bsize, sz, flashsize); + print("%d:%d:%d in 512b blocks\n", ftls.rwlimit, sz>>Bshift, flashsize>>Bshift); +} + +ftlread(buf : array of byte, n : int, offset : int) : int +{ + ftl : ref Ftl; + e : ref Terase; + nb : int; + a : int; + pb : int; + mapb : int; + + if(n <= 0 || n%Bsize || offset%Bsize) { + fprint(stderr, "ftl: bad read\n"); + exit; + } + ftl = ftls; + nb = n/Bsize; + offset /= Bsize; + if(offset >= ftl.rwlimit) + return 0; + if(offset+nb > ftl.rwlimit) + nb = ftl.rwlimit - offset; + a = 0; + for(n = 0; n < nb; n++){ + (mapb, e, pb) = mapblk(ftl, offset+n); + if(mapb) + getflash(ftl, buf[a:], e.offset + pb*Bsize, Bsize); + else + memset(buf[a:], 0, Bsize); + a += Bsize; + } + return a; +} + +ftlwrite(buf : array of byte, n : int, offset : int) : int +{ + ns, nb : int; + a : int; + e, oe : ref Terase; + ob, v : int; + ftl : ref Ftl; + mapb : int; + + if(n <= 0) + return 0; + ftl = ftls; + if(n <= 0 || n%Bsize || offset%Bsize) { + fprint(stderr, "ftl: bad write\n"); + exit; + } + nb = n/Bsize; + offset /= Bsize; + if(offset >= ftl.rwlimit) + return 0; + if(offset+nb > ftl.rwlimit) + nb = ftl.rwlimit - offset; + a = 0; + for(n = 0; n < nb; n++){ + ns = 0; + while((v = allocblk(ftl)) == 0) + if(!scavenge(ftl) || ++ns > 3){ + fprint(stderr, "ftl: flash memory full\n"); + } + (mapb, oe, ob) = mapblk(ftl, offset+n); + if(!mapb) + oe = nil; + e = ftl.unit[v>>16]; + v &= 16rffff; + putflash(ftl, e.offset + v*Bsize, buf[a:], Bsize); + putbam(ftl, e, v, MKBAM(offset+n, DataBlock)); + # both old and new block references exist in this window (can't be closed?) + ftl.vbm[offset+n] = (e.x<<16) | v; + if(oe != nil){ + putbam(ftl, oe, ob, Bdeleted); + oe.ndead++; + } + a += Bsize; + } + return a; +} + +mkftl(fname : string, base : int, size : int, eshift : int, op : string) : ref Ftl +{ + i, j, nov, segblocks : int; + limit : int; + e : ref Terase; + + ftl := ref Ftl; + ftl.lastx = 0; + ftl.detach = 0; + ftl.needspace = 0; + ftl.hasproc = 0; + ftl.trace = 0; + limit = flashsize; + if(size == Nolimit) + size = limit-base; + if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) { + fprint(stderr, "bad flash space parameters"); + exit; + } + if(FTLDEBUG || ftl.trace || trace) + print("%s flash %s #%x:#%x limit #%x\n", op, fname, base, size, limit); + ftl.base = base; + ftl.size = size; + ftl.bshift = Bshift; + ftl.bsize = Bsize; + ftl.eshift = eshift; + ftl.segsize = 1<<eshift; + ftl.nunit = size>>eshift; + nov = ((ftl.segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; # number of overhead blocks per segment (header, and BAM itself) + ftl.fstart = nov; + segblocks = ftl.segsize/Bsize - nov; + ftl.nblock = ftl.nunit*segblocks; + if(ftl.nblock >= 16r10000) + ftl.nblock = 16r10000; + ftl.vbm = array[ftl.nblock] of int; + ftl.unit = array[ftl.nunit] of ref Terase; + if(ftl.vbm == nil || ftl.unit == nil) { + fprint(stderr, "out of mem"); + exit; + } + for(i=0; i<ftl.nblock; i++) + ftl.vbm[i] = 0; + if(op == "format"){ + for(i=0; i<ftl.nunit-1; i++) + eraseinit(ftl, i*ftl.segsize, i, 1); + eraseinit(ftl, i*ftl.segsize, XferID, 1); + } + ftl.xfer = -1; + for(i=0; i<ftl.nunit; i++){ + e = eraseload(ftl, i, i*ftl.segsize); + if(e == nil){ + fprint(stderr, "ftl: logical segment %d: bad format\n", i); + continue; + } + if(e.id == XferBusy){ + e.nerase++; + eraseinit(ftl, e.offset, XferID, e.nerase); + e.id = XferID; + } + for(j=0; j<ftl.nunit; j++) + if(ftl.unit[j] != nil && ftl.unit[j].id == e.id){ + fprint(stderr, "ftl: duplicate erase unit #%x\n", e.id); + erasefree(e); + e = nil; + break; + } + if(e != nil){ + ftl.unit[e.x] = e; + if(e.id == XferID) + ftl.xfer = e.x; + if (FTLDEBUG || ftl.trace || trace) + fprint(stderr, "ftl: unit %d:#%x used %d free %d dead %d bad %d nerase %d\n", + e.x, e.id, e.nused, e.nfree, e.ndead, e.nbad, e.nerase); + } + } + if(ftl.xfer < 0 && ftl.nunit <= 0 || ftl.xfer >= 0 && ftl.nunit <= 1) { + fprint(stderr, "ftl: no valid flash data units"); + exit; + } + if(ftl.xfer < 0) + fprint(stderr, "ftl: no transfer unit: device is WORM\n"); + else + ftl.nblock -= segblocks; # discount transfer segment + if(ftl.nblock >= 1000) + ftl.rwlimit = ftl.nblock-100; # TO DO: variable reserve + else + ftl.rwlimit = ftl.nblock*USABLEPCT/100; + return ftl; +} + +ftlfree(ftl : ref Ftl) +{ + if(ftl != nil){ + ftl.unit = nil; + ftl.vbm = nil; + ftl = nil; + } +} + +# +# this simple greedy algorithm weighted by nerase does seem to lead +# to even wear of erase units (cf. the eNVy file system) +# + +bestcopy(ftl : ref Ftl) : ref Terase +{ + e, be : ref Terase; + i : int; + + be = nil; + for(i=0; i<ftl.nunit; i++) + if((e = ftl.unit[i]) != nil && e.id != XferID && e.id != XferBusy && e.ndead+e.nbad && + (be == nil || e.nerase <= be.nerase && e.ndead >= be.ndead)) + be = e; + return be; +} + +copyunit(ftl : ref Ftl, from : ref Terase, too : ref Terase) : int +{ + i, nb : int; + id := array[2] of byte; + bam : array of byte; + buf : array of byte; + v, bno : int; + + if(FTLDEBUG || ftl.trace || trace) + print("ftl: copying %d (#%x) to #%x\n", from.id, from.offset, too.offset); + too.nbam = 0; + too.bam = nil; + bam = nil; + buf = array[Bsize] of byte; + if(buf == nil) + return 0; + PUT2(id, XferBusy); + putflash(ftl, too.offset+O_ID, id, 2); + # make new BAM + nb = from.nbam*4; + bam = array[nb] of byte; + memmove(bam, from.bam, nb); + too.nused = 0; + too.nbad = 0; + too.nfree = 0; + too.ndead = 0; + for(i = 0; i < from.nbam; i++) + bv := GET4(bam[4*i:]); + case(bv){ + Bwriting or + Bdeleted or + Bfree => + PUT4(bam[4*i:], Bfree); + too.nfree++; + break; + * => + case(bv&BlockType){ + DataBlock or + ReplacePage => + v = bv; + bno = BNO(v & ~BlockType); + if(i < ftl.fstart || bno >= ftl.nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%x\n", from.x, from.id, i, v); + too.nfree++; + PUT4(bam[4*i:], Bfree); + break; + } + getflash(ftl, buf, from.offset+i*Bsize, Bsize); + putflash(ftl, too.offset+i*Bsize, buf, Bsize); + too.nused++; + break; + ControlBlock => + too.nused++; + break; + * => + # case BadBlock: # it isn't necessarily bad in this unit + too.nfree++; + PUT4(bam[4*i:], Bfree); + break; + } + } + # for(i=0; i<from.nbam; i++){ + # v = GET4(bam[4*i:]); + # if(v != Bfree && ftl.trace > 1) + # print("to[%d]=#%x\n", i, v); + # PUT4(bam[4*i:], v); + # } + putflash(ftl, too.bamoffset, bam, nb); # BUG: PUT4 ? IS IT ? + # for(i=0; i<from.nbam; i++){ + # v = GET4(bam[4*i:]); + # PUT4(bam[4*i:], v); + # } + too.id = from.id; + PUT2(id, too.id); + putflash(ftl, too.offset+O_ID, id, 2); + too.nbam = from.nbam; + too.bam = bam; + ftl.nfree += too.nfree - from.nfree; + buf = nil; + return 1; +} + +mustscavenge(a : ref Ftl) : int +{ + return a.needspace || a.detach == Deferred; +} + +donescavenge(a : ref Ftl) : int +{ + return a.needspace == 0; +} + +scavengeproc(arg : ref Ftl) +{ + ftl : ref Ftl; + i : int; + e, ne : ref Terase; + + ftl = arg; + if(mustscavenge(ftl)){ + if(ftl.detach == Deferred){ + ftlfree(ftl); + fprint(stderr, "scavenge out of memory\n"); + exit; + } + if(FTLDEBUG || ftl.trace || trace) + print("ftl: scavenge %d\n", ftl.nfree); + e = bestcopy(ftl); + if(e == nil || ftl.xfer < 0 || (ne = ftl.unit[ftl.xfer]) == nil || ne.id != XferID || e == ne) + ; + else if(copyunit(ftl, e, ne)){ + i = ne.x; ne.x = e.x; e.x = i; + ftl.unit[ne.x] = ne; + ftl.unit[e.x] = e; + ftl.xfer = e.x; + e.id = XferID; + e.nbam = 0; + e.bam = nil; + e.bamx = 0; + e.nerase++; + eraseinit(ftl, e.offset, XferID, e.nerase); + } + if(FTLDEBUG || ftl.trace || trace) + print("ftl: end scavenge %d\n", ftl.nfree); + ftl.needspace = 0; + } +} + +scavenge(ftl : ref Ftl) : int +{ + if(ftl.xfer < 0 || bestcopy(ftl) == nil) + return 0; # you worm! + + if(!ftl.hasproc){ + ftl.hasproc = 1; + } + ftl.needspace = 1; + + scavengeproc(ftls); + + return ftl.nfree; +} + +putbam(ftl : ref Ftl, e : ref Terase, n : int, entry : int) +{ + b := array[4] of byte; + + PUT4(e.bam[4*n:], entry); + PUT4(b, entry); + putflash(ftl, e.bamoffset + n*4, b, 4); +} + +allocblk(ftl : ref Ftl) : int +{ + e : ref Terase; + i, j : int; + + i = ftl.lastx; + do{ + e = ftl.unit[i]; + if(e != nil && e.id != XferID && e.nfree){ + ftl.lastx = i; + for(j=e.bamx; j<e.nbam; j++) + if(GET4(e.bam[4*j:])== Bfree){ + putbam(ftl, e, j, Bwriting); + ftl.nfree--; + e.nfree--; + e.bamx = j+1; + return (e.x<<16) | j; + } + e.nfree = 0; + print("ftl: unit %d:#%x nfree %d but not free in BAM\n", e.x, e.id, e.nfree); + } + if(++i >= ftl.nunit) + i = 0; + }while(i != ftl.lastx); + return 0; +} + +mapblk(ftl : ref Ftl, bno : int) : (int, ref Terase, int) +{ + v : int; + x : int; + + if(bno < ftl.nblock){ + v = ftl.vbm[bno]; + if(v == 0 || v == ~0) + return (0, nil, 0); + x = v>>16; + if(x >= ftl.nunit || x == ftl.xfer || ftl.unit[x] == nil){ + print("ftl: corrupt format: bad block mapping %d . unit #%x\n", bno, x); + return (0, nil, 0); + } + return (1, ftl.unit[x], v & 16rFFFF); + } + return (0, nil, 0); +} + +eraseinit(ftl : ref Ftl, offset : int, id : int, nerase : int) +{ + m : array of byte; + bam : array of byte; + i, nov : int; + + nov = ((ftl.segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; # number of overhead blocks (header, and BAM itself) + if(nov*Bsize >= ftl.segsize) { + fprint(stderr, "ftl -- too small for files"); + exit; + } + eraseflash(ftl, offset); + m = array[ERASEHDRLEN] of byte; + if(m == nil) { + fprint(stderr, "nomem\n"); + exit; + } + memset(m, 16rFF, len m); + m[O_LINKTUPLE+0] = byte 16r13; + m[O_LINKTUPLE+1] = byte 16r3; + memmove(m[O_LINKTUPLE+2:], array of byte "CIS", 3); + m[O_ORGTUPLE+0] = byte 16r46; + m[O_ORGTUPLE+1] = byte 16r57; + m[O_ORGTUPLE+2] = byte 16r00; + memmove(m[O_ORGTUPLE+3:], array of byte "FTL100\0", 7); + m[O_NXFER] = byte 1; + PUT4(m[O_NERASE:], nerase); + PUT2(m[O_ID:], id); + m[O_BSHIFT] = byte ftl.bshift; + m[O_ESHIFT] = byte ftl.eshift; + PUT2(m[O_PSTART:], 0); + PUT2(m[O_NUNITS:], ftl.nunit); + PUT4(m[O_PSIZE:], ftl.size - nov*Bsize); + PUT4(m[O_VBMBASE:], -1); # we always calculate the VBM (16rffffffff) + PUT2(m[O_NVBM:], 0); + m[O_FLAGS] = byte 0; + m[O_CODE] = byte 16rFF; + memmove(m[O_SERIAL:], array of byte "Inf1", 4); + PUT4(m[O_ALTOFFSET:], 0); + PUT4(m[O_BAMOFFSET:], BAMoffset); + putflash(ftl, offset, m, ERASEHDRLEN); + m = nil; + if(id == XferID) + return; + nov *= 4; # now bytes of BAM + bam = array[nov] of byte; + if(bam == nil) { + fprint(stderr, "nomem"); + exit; + } + for(i=0; i<nov; i += 4) + PUT4(bam[i:], ControlBlock); # reserve them + putflash(ftl, offset+BAMoffset, bam, nov); + bam = nil; +} + +eraseload(ftl : ref Ftl, x : int, offset : int) : ref Terase +{ + m : array of byte; + e : ref Terase; + i, nbam : int; + bno, v : int; + + m = array[ERASEHDRLEN] of byte; + if(m == nil) { + fprint(stderr, "nomem"); + exit; + } + getflash(ftl, m, offset, ERASEHDRLEN); + if(memcmp(m[O_ORGTUPLE+3:], array of byte "FTL100\0", 7) != 0 || + memcmp(m[O_SERIAL:], array of byte "Inf1", 4) != 0){ + m = nil; + return nil; + } + e = ref Terase; + if(e == nil){ + m = nil; + fprint(stderr, "nomem"); + exit; + } + e.x = x; + e.id = GET2(m[O_ID:]); + e.offset = offset; + e.bamoffset = GET4(m[O_BAMOFFSET:]); + e.nerase = GET4(m[O_NERASE:]); + e.bamx = 0; + e.nfree = 0; + e.nused = 0; + e.ndead = 0; + e.nbad = 0; + m = nil; + if(e.bamoffset != BAMoffset){ + e = nil; + return nil; + } + e.bamoffset += offset; + if(e.id == XferID || e.id == XferBusy){ + e.bam = nil; + e.nbam = 0; + return e; + } + nbam = ftl.segsize/Bsize; + e.bam = array[4*nbam] of byte; + e.nbam = nbam; + getflash(ftl, e.bam, e.bamoffset, nbam*4); + # scan BAM to build VBM + e.bamx = 0; + for(i=0; i<nbam; i++){ + v = GET4(e.bam[4*i:]); + if(v == Bwriting || v == Bdeleted) + e.ndead++; + else if(v == Bfree){ + if(e.bamx == 0) + e.bamx = i; + e.nfree++; + ftl.nfree++; + }else{ + case(v & BlockType){ + ControlBlock => + break; + DataBlock => + # add to VBM + if(v & (1<<31)) + break; # negative => VBM page, ignored + bno = BNO(v & ~BlockType); + if(i < ftl.fstart || bno >= ftl.nblock){ + print("ftl: unit %d:#%x bad bam[%d]=#%x\n", e.x, e.id, i, v); + e.nbad++; + break; + } + ftl.vbm[bno] = (e.x<<16) | i; + e.nused++; + break; + ReplacePage => + # replacement VBM page; ignored + break; + BadBlock => + e.nbad++; + break; + * => + print("ftl: unit %d:#%x bad bam[%d]=%x\n", e.x, e.id, i, v); + } + } + } + return e; +} + +erasefree(e : ref Terase) +{ + e.bam = nil; + e = nil; +} + +eraseflash(ftl : ref Ftl, offset : int) +{ + offset += ftl.base; + if(FTLDEBUG || ftl.trace || trace) + print("ftl: erase seg @#%x\n", offset); + memset(flashm[offset:], 16rff, secsize); +} + +putflash(ftl : ref Ftl, offset : int, buf : array of byte, n : int) +{ + offset += ftl.base; + if(ftl.trace || trace) + print("ftl: write(#%x, %d)\n", offset, n); + memmove(flashm[offset:], buf, n); +} + +getflash(ftl : ref Ftl, buf : array of byte, offset : int, n : int) +{ + offset += ftl.base; + if(ftl.trace || trace) + print("ftl: read(#%x, %d)\n", offset, n); + memmove(buf, flashm[offset:], n); +} + +BUFSIZE : con 8192; + +main(argv : list of string) +{ + k, r, sz, offset : int = 0; + buf, buf1 : array of byte; + fd1, fd2 : ref FD; + + if (len argv != 5) { + fprint(stderr, "usage: %s flashsize secsize kfsfile flashfile\n", hd argv); + exit; + } + flashsize = atoi(hd tl argv); + secsize = atoi(hd tl tl argv); + fd1 = open(hd tl tl tl argv, OREAD); + fd2 = create(hd tl tl tl tl argv, OWRITE, 8r644); + if (fd1 == nil || fd2 == nil) { + fprint(stderr, "bad io files\n"); + exit; + } + if(secsize == 0 || secsize > flashsize || secsize&(secsize-1) || 0&(secsize-1) || flashsize == 0 || flashsize != Nolimit && flashsize&(secsize-1)) { + fprint(stderr, "ftl: bad sizes\n"); + exit; + } + for(k=0; k<32 && (1<<k) != secsize; k++) + ; + flashm = array[flashsize] of byte; + buf = array[BUFSIZE] of byte; + if (flashm == nil) { + fprint(stderr, "ftl: no mem for flash\n"); + exit; + } + ftls = mkftl("FLASH", 0, Nolimit, k, "format"); + for (;;) { + r = read(fd1, buf, BUFSIZE); + if (r <= 0) + break; + if (ftlwrite(buf, r, offset) != r) { + fprint(stderr, "ftl: ftlwrite failed - input file too big\n"); + exit; + } + offset += r; + } + write(fd2, flashm, flashsize); + fd1 = fd2 = nil; + ftlstat(offset); + # ftls = mkftl("FLASH", 0, Nolimit, k, "init"); + sz = offset; + offset = 0; + buf1 = array[BUFSIZE] of byte; + fd1 = open(hd tl tl tl argv, OREAD); + for (;;) { + r = read(fd1, buf1, BUFSIZE); + if (r <= 0) + break; + if (ftlread(buf, r, offset) != r) { + fprint(stderr, "ftl: ftlread failed\n"); + exit; + } + if (memcmp(buf, buf1, r) != 0) { + fprint(stderr, "ftl: bad read\n"); + exit; + } + offset += r; + } + fd1 = nil; + if (offset != sz) { + fprint(stderr, "ftl: bad final offset\n"); + exit; + } + exit; +} + +init(nil : ref Draw->Context, argl : list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + main(argl); +} + +memset(d : array of byte, v : int, n : int) +{ + for (i := 0; i < n; i++) + d[i] = byte v; +} + +memmove(d : array of byte, s : array of byte, n : int) +{ + d[0:] = s[0:n]; +} + +memcmp(s1 : array of byte, s2 : array of byte, n : int) : int +{ + for (i := 0; i < n; i++) { + if (s1[i] < s2[i]) + return -1; + if (s1[i] > s2[i]) + return 1; + } + return 0; +} + +atoi(s : string) : int +{ + v : int; + base := 10; + n := len s; + neg := 0; + + for (i := 0; i < n && (s[i] == ' ' || s[i] == '\t'); i++) + ; + if (s[i] == '+' || s[i] == '-') { + if (s[i] == '-') + neg = 1; + i++; + } + if (n-i >= 2 && s[i] == '0' && s[i+1] == 'x') { + base = 16; + i += 2; + } + else if (n-i >= 1 && s[i] == '0') { + base = 8; + i++; + } + m := 0; + for(; i < n; i++) { + c := s[i]; + case c { + 'a' to 'z' => + v = c - 'a' + 10; + 'A' to 'Z' => + v = c - 'A' + 10; + '0' to '9' => + v = c - '0'; + * => + fprint(stderr, "ftl: bad character in number %s\n", s); + exit; + } + if(v >= base) { + fprint(stderr, "ftl: character too big for base in %s\n", s); + exit; + } + m = m * base + v; + } + if(neg) + m = -m; + return m; +} + +# little endian + +GET2(b : array of byte) : int +{ + return ((int b[1]) << 8) | (int b[0]); +} + +GET4(b : array of byte) : int +{ + return ((int b[3]) << 24) | ((int b[2]) << 16) | ((int b[1]) << 8) | (int b[0]); +} + +PUT2(b : array of byte, v : int) +{ + b[1] = byte (v>>8); + b[0] = byte v; +} + +PUT4(b : array of byte, v : int) +{ + b[3] = byte (v>>24); + b[2] = byte (v>>16); + b[1] = byte (v>>8); + b[0] = byte v; +} |
