|| (C) Copyright 1979 Tripos Research Group || University of Cambridge || Computer Laboratory || Disc Editor for TRIPOS || For version 2 of the filing system (Jan 1979) || Intel 8086 version for MDS 721 floppy discs SECTION "Disced1" GET "DISCED" LET start(zero.or.packet) BE $( LET match = ? LET temp = ? LET currword = 0 TEST zero.or.packet \= 0 THEN $( qpkt(zero.or.packet) initio() selectinput(findinput("**")) selectoutput(findoutput("**")) $) ELSE UNTIL (ch = '*N') | (ch = '*E') | (ch = ';') DO ch := rdch() // Initialization of 'file-handler' globals size.block := 256 sector.origin := 0 n.surfaces := 1 n.sectorsperblock := 1 n.blockspertrack := 13 n.reservedblocks := 1 preallocationfactor := 8 interleavefactor := 1 lowercylinder := 15 uppercylinder := 76 n.blockspercylinder := n.blockspertrack*n.surfaces keylowerbound := n.reservedblocks keyupperbound := [uppercylinder-lowercylinder+1]* n.blockspercylinder-1 n.blocks := keyupperbound-keylowerbound+1 rootkey := [keyupperbound+keylowerbound]/2 b.file.secondarytype := size.block-1 b.file.infobits := size.block-2 b.file.parentdir := size.block-3 b.file.hashchain := size.block-4 b.file.filename := size.block-20 b.file.creationdate := size.block-23 b.file.infobase := size.block-50 b.dir.hashtablelimit := b.file.infobase-1 size.hashtable := b.dir.hashtablelimit+1-b.dir.hashtablebase size.fcb := fcb.xvec+preallocationfactor+2 size.bitmap := [n.blocks-1]/bitsperword+1 writes("TRIPOS (floppy) Disc Editor version 2.0*N") // Code to get cylinder count right on floppies FOR z=0 TO ndrives-1 DO $( sendpkt(notinuse, dddevid, act.seek, ?, ?, ?, ?, z, 2) sendpkt(notinuse, dddevid, act.seek, ?, ?, ?, ?, z, -80) $) block.read := FALSE style := " %N " drive := 0 cyl, sur, sec := -1, -1, -1 cylbase := LowerCylinder write.protected := TRUE writef("Write protect mode set*N* *Cylinder base set to %N*N", cylbase) lbnbase := 0 lbn := -1 stringstyle := FALSE charstyle := FALSE mask, pattern := -1, 0 nullnumber := FALSE blockbuff := getvec(size.block - 1) IF blockbuff = 0 THEN $( WRITES("No space for buffer*N"); STOP(20) $) blockwrittenback := TRUE writes("# *E") $( || Main loop ch := rdch() SWITCHON capitalch(ch) INTO $( CASE '*E': newline() CASE '*N': writes("# *E") ENDCASE CASE '*S': ENDCASE CASE 'B': || Reset logical block || number base temp := readvalue() UNLESS 0 <= temp < nblocks THEN $( writes(" Silly value %N*N", temp) ENDCASE $) lbnbase := temp writef(" Logical block number * *base set to %N*N", lbnbase) ENDCASE CASE 'C': || Print n characters check.block.read() writes(" '") FOR j = 0 TO readvalue() - 1 DO wrch(getbyte(blockbuff+currword,j)) writes("'*N") ENDCASE CASE 'D': || Set the disc drive number temp := readvalue() UNLESS 0 <= temp < ndrives THEN $( writef(" Silly drive number %N*N", temp) ENDCASE $) drive := temp writef(" Drive %N selected*N",drive) ENDCASE CASE 'G': || Get block from disc $( LET oldlbn = lbn LET addrok = readdiscaddr() LET newlbn = lbn UNLESS blockwrittenback | NOT block.read THEN $( writes(" Last block not * *yet written back!!*N* * Type N if you do not * *want it updated *E") UNTIL rdch()='*N' LOOP temp := rdch() UNLESS (temp='N') | (temp='n') THEN $( unrdch() lbn := oldlbn convertdiscaddr(oldlbn+lbnbase) UNLESS discaction(act.write) THEN ENDCASE lbn := newlbn convertdiscaddr(newlbn+lbnbase) $) $) IF addrok THEN discaction(act.read) $) ENDCASE CASE 'H': || Calculate hash value given || string $( LET s = VEC filenameupb+1 LET p = 0 LET h = 0 skiplayout() ch := rdch() IF ch = '"' THEN ch := readchar() UNTIL (ch = '*N') | (ch = '"') | (ch = ' ') | (p=filenameupb) DO $( p := p + 1 s!p := capitalch(ch) ch := rdch() $) IF ch = '*N' THEN unrdch() h := p FOR z = 1 TO p DO h := (h*13 + s!z) & #X7FF writef(" Hash value = %N*N", B.Dir.HashTableBase + (h REM Size.HashTable)) ENDCASE $) CASE 'I': || Print file info || Print the information in the || first few words of the block $( LET type = blockbuff ! B.File.Type LET data.block = ? check.block.read() IF (type & T.Deleted) \= 0 THEN writes(" Deleted") type := type & (NOT T.deleted) writef(" %S*N", VALOF SWITCHON type INTO $( CASE T.Long: RESULTIS "Long file" CASE T.Short: RESULTIS "Short file" CASE T.Data: RESULTIS "Data block" CASE T.List: RESULTIS "List" DEFAULT: RESULTIS "Corrupt type" $) ) data.block := type = T.Data writef(" Header key: %N*N", blockbuff ! B.File.Headerkey) TEST data.block THEN writes(" Sequence number: ") ELSE writes(" Highest seq num: ") writef("%N*N", blockbuff ! B.File.HighestSeq) writef(" Data size: %N*N", blockbuff ! B.File.Datasize) TEST data.block THEN writes(" Next data block: ") ELSE writes(" First data block: ") writef("%N*N", blockbuff ! B.File.FirstDataKey) writef(" Checksum: %N*N", blockbuff ! B.File.Checksum) UNLESS data.block THEN $( LET datvec = VEC 14 writef(" Secondary type: %S*N", VALOF SWITCHON blockbuff!B.File.Secondarytype INTO $( CASE ST.File: RESULTIS "File" CASE ST.Root: RESULTIS "Root block" CASE ST.UserDirectory: RESULTIS "User directory" DEFAULT: RESULTIS "(Unset)" $) ) writef(" Info bits: #X%X4*N", blockbuff ! B.File.Infobits) writef(" Parent Directory: %N*N", blockbuff ! B.File.ParentDir) writef(" Hash Chain: %N*N", blockbuff ! B.File.HashChain) writef(" Filename: *"%S*"*N", blockbuff + B.File.Filename) IF zero.or.packet = 0 THEN $( // Running as command callseg(":L.dat-to-strings", blockbuff+B.File.CreationDate, datvec) writef(" Created: %S %S %S*N", datvec+10, datvec, datvec+5) $) $) $) ENDCASE CASE 'K': || Calculate and check checksum check.block.read() $( LET cs = block.checksum() TEST cs = 0 THEN writes(" Checksum correct*N") ELSE $( LET bad.cs = blockbuff ! B.File.Checksum LET correct.cs = 0-(cs-bad.cs) blockbuff ! B.File.Checksum := correct.cs writef(" Checksum incorrect*N* * Value in block was %N*N* * Corrected to %N*N", bad.cs, correct.cs) blockwrittenback := FALSE $) ENDCASE $) CASE 'L': || Locate words which match || pattern under mask match := TRUE search: $( LET lwb = readvalue() LET upb = size.block - 1 LET maskedpat = mask & pattern check.block.read() skiplayout() UNLESS nullnumber THEN $( UNLESS 0 <= lwb < size.block THEN $( writes(" Invalid lower * *bound*N") lwb := 0 $) upb := readvalue() UNLESS 0 <= upb < size.block THEN $( writes(" Invalid upper * *bound*N") upb := size.block - 1 $) $) FOR j = lwb TO upb DO $( LET v = blockbuff ! j IF TESTFLAGS(1) BREAK IF ((v & mask) = maskedpat) = match THEN $( writef(" %I3:", j) printloc(j) $) $) $) ENDCASE CASE 'M': || Set locate mask mask := readvalue() ENDCASE CASE 'N': // Locate words which don't // match pattern under mask match := false GOTO SEARCH CASE 'P': || Put block back on disc IF readdiscaddr() THEN discaction(act.write) ENDCASE CASE 'Q': || Quit freevec(blockbuff) RETURN CASE 'R': || Identify Root Block writef(" Root Block is block %N*N", RootKey) ENDCASE CASE 'S': || Set printing style stringstyle := FALSE charstyle := FALSE style := VALOF SWITCHON capitalch(rdch()) INTO $( CASE 'C': charstyle := TRUE RESULTIS style CASE 'S': stringstyle := TRUE RESULTIS style CASE 'O': RESULTIS " %O6 " CASE 'X': RESULTIS " %X4 " CASE 'D': RESULTIS " %N " DEFAULT: writes(" Try c,s,o* *,x or d*N") RESULTIS style $) printloc(currword) ENDCASE CASE 'T': || Type range of locations $( LET lwb = readvalue() LET upb = readvalue() check.block.read() IF nullnumber THEN upb := lwb FOR j = lwb TO upb DO $( IF TESTFLAGS(1) BREAK writef(" %I3: ", j) printloc(j) $) $) ENDCASE CASE 'V': || Set value for locate pattern := readvalue() ENDCASE CASE 'W': || Windup UNLESS blockwrittenback THEN UNLESS discaction(act.write) THEN ENDCASE freevec(blockbuff) RETURN CASE 'X': || Invert write protect mode write.protected := NOT write.protected writef(" Write protect mode %Sset*N", write.protected -> "","un") ENDCASE CASE 'Y': || Set cylinder base temp := readvalue() UNLESS 0 <= temp <= maxcyl THEN $( writef("Value should be in range* * 0 to %N*N", maxcyl) ENDCASE $) cylbase := temp writef(" Cylinder base set to %N*N", cylbase) ENDCASE CASE 'Z': || Zero buffer FOR i = 0 TO size.block - 1 DO blockbuff ! i := 0 ENDCASE CASE '-': CASE '+': CASE '#': CASE '0': CASE '1': CASE '2': CASE '3': CASE '4': CASE '5': CASE '6': CASE '7': CASE '8': CASE '9': || Read the offset in the buffer || of the new current word $( LET n = ? unrdch() n := readvalue() IF (n < 0) | (n >= size.block) THEN $( writef(" Offset %N invalid*N", n) ENDCASE $) currword := n $) ENDCASE CASE '=': || Print default values set writef(" Disc drive %N*N",drive) writef(" Block number %N: cyl %N,* * sur %N, sec %N*N", lbn, cyl, sur, sec) writef(" Cylinder base = %N*N* * Write protect mode is %Sset*N* * Current block is %Sup to date* * on disc*N", cylbase, write.protected -> "", "not ", (blockwrittenback -> "", "not ") ) writef(" Current word offset = * *%N*N", currword) writef(" Logical block number * *base = %N*N", lbnbase) ENDCASE CASE '/': || Open location $( LET v = readvalue() check.block.read() TEST nullnumber THEN printloc(currword) ELSE $( blockbuff ! currword := v blockwrittenback := FALSE $) $) ENDCASE CASE '*'': || Row of characters $( LET stringbase = currword*bytesperword LET boffset = stringbase LET bofflim = size.block * bytesperword check.block.read() ch := readchar() UNTIL (ch = '*'') DO $( IF boffset >= bofflim THEN $( writes(" Attempt to * *overflow block*N") BREAK $) putbyte(blockbuff, boffset, ch) boffset := boffset + 1 ch := readchar() $) $) blockwrittenback := FALSE ENDCASE CASE '"': || String $( LET stringbase = currword*bytesperword LET boffset = stringbase + 1 LET bofflim = size.block * bytesperword check.block.read() ch := readchar() UNTIL (ch = '"') DO $( IF boffset >= bofflim THEN $( writes(" Attempt to overflow* * block*N") BREAK $) putbyte(blockbuff, boffset, ch) boffset := boffset + 1 ch := readchar() $) || Fill in the length putbyte(blockbuff,stringbase, boffset-1-stringbase) $) blockwrittenback := FALSE ENDCASE CASE '?': writes(" See file :Doc.DiscedSpec* * for a list of commands * *available*N") ENDCASE unknown: DEFAULT: writef(" Unknown command %C*N", ch) ch := rdch() REPEATUNTIL ch = '*N' unrdch() $) $) REPEAT $) . SECTION "Disced2" GET "DISCED" LET skiplayout() BE $( LET c = rdch() WHILE (c = ' ') | (c = '*T') DO c := rdch() unrdch() $) AND printloc(offset) BE $( IF (offset < 0) | (offset >= size.block) THEN $( writef(" Offset %N invalid*N", offset) RETURN $) TEST stringstyle THEN writef(" *"%S*" *N", blockbuff + offset) ELSE TEST charstyle THEN $( writes(" '") FOR j = 0 TO bytesperword-1 DO wrch(getbyte(blockbuff+offset, j)) writes("' *N") $) ELSE $( writef(style, blockbuff ! offset, (blockbuff!offset) >> 8) newline() $) $) AND discaction(action) = VALOF $( || Does a disc read or write as specified by action, || using blockbuff. The disc address is given by the || globals cyl, sur and sec. LET s = ? IF action = act.write THEN $( IF block.checksum() \= 0 THEN writes(" ******Warning - * *block has incorrect * *checksum*N") IF write.protected THEN $( writes(" Write protect mode is set: block* * not written back*N") RESULTIS FALSE $) $) UNLESS 0 <= cyl <= maxcyl THEN $( writef(" Cylinder %N is out of range*N", cyl) RESULTIS FALSE $) FOR r = 1 TO 10 DO $( s := sendpkt(notinuse,dddevid,action,?,?, blockbuff,size.block,drive,cyl,sur,sec) IF s = 0 THEN $( IF r > 2 THEN writef(" %N tries needed to transfer* * cyl %N, sur %N, sec %N*N", r, cyl, sur, sec) IF action = act.read THEN block.read := TRUE blockwrittenback := TRUE writef(" Block %N %S (cyl %N, sur %N, sec %N)*N", lbn, (action=act.write-> "updated","read"), cyl, sur, sec) RESULTIS TRUE $) $) writef(" Transfer failed: cyl %N, sur %N, sec %N* *, status %X4, error %X4*N",cyl,sur,sec,result2,s) RESULTIS FALSE $) AND readdiscaddr() = VALOF $( || Reads a new disc address. || There are 2 possible cases: || (a) No numbers after the command - keep || the current address || || (b) A new block number is specified. || Set globals cyl, sur and sec from it. || || Exits with lbn, cyl, sur and sec set. Returns || TRUE if the address is valid, FALSE if an || error is detected. LET val = readvalue() UNLESS nullnumber THEN $( lbn := val; skiplayout() $) UNLESS convertdiscaddr(lbn+lbnbase) THEN RESULTIS FALSE RESULTIS TRUE $) AND readchar() = VALOF $( || Returns the next character, after interpreting || escape combinations || If the character is the result of an escape || combination, then bit #X100 is set in the || result. This is so that unescaped quotes || can be detected by the caller. ch := rdch() TEST ch = '**' THEN $( ch := capitalch( rdch() ) SWITCHON ch INTO $( CASE '**':RESULTIS '**' | escbit CASE '*'':RESULTIS '*'' | escbit CASE '*"':RESULTIS '*"' | escbit CASE 'N': RESULTIS '*N' | escbit CASE 'E': RESULTIS '*E' | escbit CASE 'S': RESULTIS '*S' | escbit CASE 'T': RESULTIS '*T' | escbit CASE 'B': RESULTIS '*B' | escbit CASE 'X': || Read 2 digit hex value RESULTIS ((hexval(rdch()) << 4) + hexval(rdch())) | escbit CASE '0': CASE '1': CASE '2': CASE '3': CASE '4': CASE '5': CASE '6': CASE '7': || Read a 3 digit octal value $( LET val = ch - '0' FOR i = 1 TO 2 DO $( ch := rdch() UNLESS '0' <= ch <= '7' THEN $( writef(" Invalid octal * *digit %C*N", ch) BREAK $) val := val*8 + ch - '0' $) IF val > 255 THEN writef(" Value %O3 too big*N", val) RESULTIS val | escbit $) DEFAULT: writef(" Unknown escape %C*N",ch) RESULTIS ch | escbit $) $) ELSE $( IF (ch = '*N') | (ch = '*E') THEN unrdch() RESULTIS ch $) $) AND hexval(c) = VALOF $( || Gives the value of the hex digit c IF '0' <= c <= '9' THEN RESULTIS c - '0' IF 'a' <= c <= 'f' THEN RESULTIS c - 'a' + 10 IF 'A' <= c <= 'F' THEN RESULTIS c - 'A' + 10 writef(" Invalid hex digit %C*N", c) RESULTIS 0 $) AND convertdiscaddr(blockno) = VALOF $( || Sets globals cyl sur and sec. || Returns TRUE if blockno is valid, FALSE otherwise. IF (blockno >= nblocks) | (blockno < 0) THEN $( writef(" Block number %N is invalid*N", blockno) RESULTIS FALSE $) cyl := blockno/(N.Surfaces * N.blockspertrack) + cylbase sur := blockno REM N.Surfaces sec := (blockno/N.Surfaces) REM N.blockspertrack * N.SectorsPerBlock + Sector.Origin RESULTIS TRUE $) AND readvalue() = VALOF $( || Reads: optionally signed decimal number || # octal number || #X hex number || '<single char> nullnumber := FALSE skiplayout() ch := rdch() SWITCHON ch INTO $( CASE '-': RESULTIS -readvalue() CASE '+': RESULTIS readvalue() CASE '#': ch := rdch() TEST (ch = 'X') | (ch = 'x') THEN RESULTIS readhexnum() ELSE $( unrdch() RESULTIS readoctnum() $) CASE '*'':RESULTIS rdch() CASE '0': CASE '1': CASE '2': CASE '3': CASE '4': CASE '5': CASE '6': CASE '7': CASE '8': CASE '9': unrdch() RESULTIS readn() DEFAULT: nullnumber := TRUE unrdch() RESULTIS 0 $) $) AND readhexnum() = VALOF $( LET val = 0 ch := rdch() UNLESS ('0' <= ch <= '9') | ('A' <= capitalch(ch) <= 'F') THEN nullnumber := TRUE WHILE ('0' <= ch <= '9') | ('A' <= capitalch(ch) <= 'F') DO $( val := (val << 4) + hexval(ch) ch := rdch() $) unrdch() RESULTIS val $) AND readoctnum() = VALOF $( LET val = 0 ch := rdch() UNLESS '0' <= ch <= '7' THEN nullnumber := TRUE WHILE ('0' <= ch <= '7') DO $( val := (val << 3) + ch - '0' ch := rdch() $) unrdch() RESULTIS val $) AND check.block.read() BE UNLESS block.read THEN writes("****** WARNING - No block has been* * read into buffer!*N") AND block.checksum() = VALOF $( LET cs = 0 FOR z=0 TO size.block-1 DO cs := cs + blockbuff!z RESULTIS cs $)