FORTH FACTORY
+ +Utilities
+ +by Bob Gonsalves
+ +In this installment we'll present some utility definitions that you may +easily add to your system. The first set of words can be used in many Forth +systems, the second set are designed to access the Atari disk file management +system.
+ +SOME QUICKIES
+ +Screen 30 shows some useful extensions to fig-Forth systems. Following +/, all characters on a line will be ignored; the text following the / is +used for commenting. NOT is used to reverse the logical state of the top +stack item and is often used before conditional testing words, such as IF, +UNTIL.
+ +Another group of words facilitates operations on bytes in 16-bit word. +LSBYTE will leave the least significant byte of the top stack value, while +MSBYTE leaves the most significant byte. >< uses these two operations +to reverse the two halves of a 16-bit stack value.
+ +Our final category introduces a new data type. The 'TO' type variable, +introduced by Paul Bartholdi in Forth Dimensions, improves readability and +reliability of Forth programs by reducing the number of @ (fetch) and ! +(store) operations that must be included in the source text. If the VAR +defined variable is preceded by TO, a stack parameter will be stored in +the variable. Otherwise, the variable simply leaves its contents. The following +should illustrate:
+ +0 VAR temp.cell / our specific instance
+20 TO temp.cell / store 20 in the variable
+temp.cell . 20/ prints the contents of temp.cell
FMS 'n' FIELDS
+ +With extensions such as these, we're now ready to suggest some ways of +accessing FMS formatted files. As shown in figure 1, both the FMS directory +entries and the individual sectors of a FMS file share a similar structure. +They both feature range of disk space, with certain bytes or 16-bit words +having specific significance. In the case of a directory entry, locations +are used to store the state of the file (open, closed, etc), its length +and starting sector, plus the characters that make up its name. These locations +are offset from an address DIR.ADDR, which contains the address, within +the disk buffers, of the start of the directory entry. The defining word +FIELDER will create some words that allow us to access these fields. These +access words are structured like the TO variables mentioned above. Thus, +START.SECTOR for example, will normally leave the starting sector number +of a file on the stack. If it is preceded by TO, however, as in:
+ +3 TO START.SECTOR
+ +then a value is stored to the START.SECTOR field. (This doesn't work +as well for FLAG.BYTE, which is only a single byte location.)
+ +Three additional words on screen 32 show ways to make use of the data +from these fields. ?NULLED examines the least significant byte of the FLAG.BYTE +of a directory entry, and leaves a true flag on the stack if the directory +entry is not an active one. PNAME will print the name of an entry. BUMPSEC +will print out the number of sectors used by an individual file, and increments +a counter containing the number of sectors used on the disk.
+ +Our DIR example makes use of all of the above definitions to print out +the directory of a FMS disk. It does so by examining sectors 361 through +368 for valid directory entries. Each entry is 16 bytes long (8 per sector) +and is checked to see if it is null. If it isn't, its name is printed and +the file length is added to the running total (#SEC). At the end of the +directory listing, the total number of sectors used by the files is printed, +as well as the number of sectors available (according to the FMS Volume +Table of Contents), using .USED.
+ +In the case of an actual data sector from a file, the words FILE, POINTER +and BCOUNT (all defined by DATA) return the values stored at the end of +a data sector. Because of various tricks that are performed (to save disk +space) additional words are required to convert the values into a useable +format. The word #FILE returns the position of the file in the directory. +The next sector number in the file is returned by #POINT, which equals 0 +if this sector is the end of the file. #BYTES returns the number of data +bytes in the sector, which may range from 125 to 1.
+ +Two other words are useful in this context. DATA.FIELD leaves the address, +in the disk buffers, of the start of the sector. ?LAST leaves a false flag, +plus the next sector number of the file, if the end of the file has not +been reached. Otherwise a true flag is left on the stack.
+ +Our final example, on screen 36, illustrates what it might take to list +a file. Assuming that the value of DIR.ADDR has been set to point to the +directory entry in the disk buffers, PRINT.FILE starts at the first sector +of the file, and types #BYTES from the DATA.FIELD of the sector, until the +last sector is reached.
+ +FOR MORE INFO
+ +Because of space limitations, we'll skip over exactly how one locates +a specific directory entry. This could be done by simply DUMPing the contents +of a disk block, or by actually accepting text from a user and performing +a string match against the FMS directory. Other applications for this system, +such as loading character fonts into memory and repairing 'damaged' files, +can be obtained by writing to the author c/o Pink Noise Studios, P.0. Box +785 Crockett, CA 94525. Please include a self-addressed, stamped envelope.
+ +---------------------------------------- +pink noise studios/fig-forth 1/82 +\ 30 extensions for others rfg20apr82 +: \ in @ c/l / 1+ c/l * in ! ; + immediate \ from Henry Laxen +: NOT 0= ; + hex +: MSBYTE 0 100 u/ swap drop ; +: LSBYTE ff and ; +: >< \ byteswap + dup lsbyte 100 * swap msbyte + ; +0 variable TOFLAG +: TO 1 toflag ! ; +: VAR <builds , + does> toflag @ + if ! else @ then 0 toflag ! ; +decimal ;s +---------------------------------------- +\ 31 fields in directory rfg18apr82 +0 variable DIR.ADDR +\ points to directory entry in buffers +: FIELDER <builds c, \ offset into field + does> c@ dir.addr @ + \ compute addr + toflag @ if ! else @ then 0 toflag ! ; +0 fielder FLAG.BYTE +1 fielder SECTOR.COUNT +3 fielder START.SECTOR +: NAME.FIELD dir.addr @ 5 + ; +;s +---------------------------------------- +\ 32 utilities for DIR rfg18apr82 +: ?NULLED \ return true if nulled out + flag.byte lsbyte dup 80 = + swap 0= or sector.count 0= or ; + hex +: PNAME + name.field dup 8 type 2e emit + 8 + 3 type ; +0 variable #SEC +: BUMPSEC \ increment total and print + sector.count dup #sec +! 4 .r ; + decimal +: .USED \ according to VTOC + 359 block 3 + @ 4 .r + ," sectors available " cr ; +;s +---------------------------------------- +pink noise studios/fig-forth 1/82 +\ 33 directory of FMS disks rfg18apr82 + decimal +: DIR 0 #sec ! cr + 368 360 \ directory sectors + do i block dup b/buf + swap + do i dir.addr ! + ?nulled not + if pname bumpsec cr then + 16 +loop + loop cr #sec @ 4 .r + ." sectors used by files " cr .used ; +;s +---------------------------------------- +\ 34 fields in sectors rfg18apr82 + decimal + 0 variable SECTOR +: DATA <builds c, + does> c@ sector @ 1- block + +toflag @ + if ! else @ then 0 toflag ! ; +125 data FILE +125 data POINTER +127 data BCOUNT + hex +\ below return values +: #FILE file lsbyte 4 / ; +: #POINT pointer >< 3ff and ; +: #BYTES bcount 7f and 7d min ; +\above accounts for short sectors +;s +---------------------------------------- +\ 35 dos access utilities rfg18apr82 +: DATA.FIELD \ first storage location + sector @ 1- block ; +: ?LAST #point -dup 0= ; +\ leave true or false + link +\ to next sector +---------------------------------------- + pink noise studios/fig-forth 1/82 +\ 36 printing a file rfg18apr82 +\ assumes dir.addr points to +\ directory entry in buffers +: PRINT.FILE + start.sector sector ! + begin data.field #bytes type + ?last not + while sector ! + repeat ; +----------------------------------------+ + + + +