nerdletter/print/content/archival/Atari-Forth Factory-Utilities.html

458 lines
22 KiB
HTML
Raw Normal View History

<!-- saved from url=(0052)https://www.atarimagazines.com/v1n2/forthfactory.php -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Forth Factory: Utilities</title>
<meta name="Author" content="Bob Gonsalves">
<meta name="description" content="Forth Factory: Utilities. From Antic Vol. 1, No. 2 / June 1982">
</head>
<body text="#000000" bgcolor="#FFFFFF" link="#0000EE" vlink="#800080" alink="#FF0000"><pre><a href="https://www.atarimagazines.com/antic/"><img src="./Atari-Forth Factory-Utilities_files/antic.gif" height="46" width="152" alt="Classic Computer Magazine Archive" align="center" border="0"></a> ANTIC VOL. 1, NO. 2 / JUNE 1982</pre><hr>
<div name="topadholder" id="topadholder" style="display: block; min-width:800px; text-align:left;padding:25px 10px 10px 20px;line-height:9.5pt;clear:both;"></div>
<table style="zoom:1; min-width:800px; border-width: 1px; border-spacing: 2px; border-style: dotted; border-color: gray; border-collapse: separate;">
<tbody><tr><td style="vertical-align:top; zoom:1; min-width:400px;">
<div id="main_article" name="main_article" style="zoom: 1; min-width:400px;">
<div id="spacer" style="width:400px;"></div>
<h2>FORTH FACTORY</h2>
<h1>Utilities</h1>
<p><i>by Bob Gonsalves</i></p>
<p>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. </p>
<p><b>SOME QUICKIES</b></p>
<p>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.</p>
<p>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. &gt;&lt; uses these two operations
to reverse the two halves of a 16-bit stack value.</p>
<p>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:</p>
<p><tt>0 VAR temp.cell / our specific instance<br>
20 TO temp.cell / store 20 in the variable <br>
temp.cell . 20/ prints the contents of temp.cell</tt></p>
<p><b>FMS 'n' FIELDS</b></p>
<p>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:</p>
<p><tt>3 TO START.SECTOR</tt></p>
<p>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.)</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p><b>FOR MORE INFO</b></p>
<p>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.</p>
<pre>----------------------------------------
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 ;
: &gt;&lt; \ byteswap
dup lsbyte 100 * swap msbyte + ;
0 variable TOFLAG
: TO 1 toflag ! ;
: VAR &lt;builds ,
does&gt; toflag @
if ! else @ then 0 toflag ! ;
decimal ;s
----------------------------------------
\ 31 fields in directory rfg18apr82
0 variable DIR.ADDR
\ points to directory entry in buffers
: FIELDER &lt;builds c, \ offset into field
does&gt; 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 &lt;builds c,
does&gt; 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 &gt;&lt; 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 ;
----------------------------------------</pre>
</div>
</td><td style="width:330px; min-width:330px; vertical-align:top; zoom:1; ">
<div id="sideadplaceholder" style=" width:340px; min-width:340px;">
</div>
</td></tr></tbody></table>
<div style="display: none">
<!--[if !IE]-->
<script type="text/javascript">
var adcountoffset=0;
var aheightoffset=0;
var aheightfactor=0;
</script>
<!--[endif]-->
<!--[if IE]>
<script type="text/javascript">
var adcountoffset=0;
var aheightoffset=19;
var aheightfactor=0.023;
</script>
<![endif]-->
<script type="text/javascript">
var maxAds = 20;
google_max_num_ads = 6;
var elementId = "main_article";
var col = document.getElementById(elementId); // determining the height of column to adjust number of ads
var article_height=parseInt(col.offsetHeight-aheightoffset-((col.offsetHeight-aheightoffset)*aheightfactor)); //compensate for IE
if (col) // if defined, trying to calculate how many ads to show
{
var article_height=col.offsetHeight-aheightoffset;
var adHeight = 80;
numberOfAds = Math.floor(article_height / adHeight);
numberOfAds = parseInt(numberOfAds);
numberOfAds = ((numberOfAds+5+adcountoffset) >= maxAds) ? maxAds : (numberOfAds+5+adcountoffset); //limiting numberOfAds
google_max_num_ads=(numberOfAds > google_max_num_ads) ? numberOfAds : google_max_num_ads;
}
function google_ad_request_done(google_ads){
var s = '<div style="width:330px; text-align:left;padding:25px 10px 0px 10px; line-height:15pt;" >';
s += '<a href=\"' + google_info.feedback_url + '\" style="clear:both; margin-left:0px;margin-top:-15px;display:block;color:#666666;font-family:verdana,arial,sans-serif; text-decoration: none; font-weight:500; font-size:9pt;">Ads by Google</a><div>';
var i;
if (google_ads.length == 0) {
return;
}
if (google_ads[0].type == "flash") {
s += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' +
' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" WIDTH="' +
google_ad.image_width +
'" HEIGHT="' +
google_ad.image_height +
'"> <PARAM NAME="movie" VALUE="' +
google_ad.image_url +
'">' +
'<PARAM NAME="quality" VALUE="high">' +
'<PARAM NAME="AllowScriptAccess" VALUE="never">' +
'<EMBED src="' +
google_ad.image_url +
'" WIDTH="' +
google_ad.image_width +
'" HEIGHT="' +
google_ad.image_height +
'" TYPE="application/x-shockwave-flash"' +
' AllowScriptAccess="never" ' +
' PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"></EMBED></OBJECT>';
if(document.getElementById('topadholder')) {
document.getElementById('topadholder').innerHTML = s;
}
}
else
if (google_ads[0].type == "image") {
s += '<a href="' +
google_ads[0].url +
'" target="_top" title="go to ' +
google_ads[0].visible_url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[0].visible_url +
'\';return true"><img border="0" src="' +
google_ads[0].image_url +
'"width="' +
google_ads[0].image_width +
'"height="' +
google_ads[0].image_height +
'"></a>';
if(document.getElementById('topadholder')) {
document.getElementById('topadholder').innerHTML = s;
}
}
else
if (google_ads[0].type == "html") {
s += google_ads[0].snippet;
if(document.getElementById('topadholder')) {
document.getElementById('topadholder').innerHTML = s;
}
}
else {
if (google_ads.length == 1) {
s += '<a style="line-height:18pt; letter-spacing: 2px; color:blue;font-family:verdana,arial,sans-serif;" href="' +
google_ads[0].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[0].visible_url +
'\';return true"> <span style="font-size:18pt;font-weight:bold;"> ' +
google_ads[0].line1 +
'</span></a> <span style="line-height:18pt; letter-spacing: 2px; text-decoration:none;color:#0e0e0e;font-family:verdana,arial,sans-serif;font-size:14pt;font-weight:400;">&nbsp;-&nbsp;' +
google_ads[0].line2 +
'&nbsp;' +
google_ads[0].line3 +
'</span><br /><span><a style="color:blue;letter-spacing: 1.5px;font-size:13pt;font-weight:400;text-decoration:none;font-family:verdana,arial,sans-serif;" href="' +
google_ads[0].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[0].visible_url +
'\';return true">' +
google_ads[0].visible_url +
'</span></a><br>';
if(document.getElementById('topadholder')) {
document.getElementById('topadholder').innerHTML = s;
}
}
else
{
if (google_ads.length > 1) {
var s1 = '<div style="display: block; min-width:800px; text-align:left;padding:25px 10px 10px 20px;line-height:9.5pt;clear:both;" >';
s1 += '<a href=\"' + google_info.feedback_url + '\" style="clear:both;margin-left:0px;margin-top:-15px;position:absolute;display:block;color:#666666;font-family:verdana,arial,sans-serif; text-decoration: none; font-weight:500; font-size:9pt;">Ads by Google</a><div>';
for (i = 0; i < 5; ++i) {
s1 += '<div style="display: block; margin-right:2px;">';
s1 += '<a style="letter-spacing: 1.5px;color:blue;font-family:verdana,arial,sans-serif;font-size:10pt;font-weight:bold;" href="' +
google_ads[i].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[i].visible_url +
'\';return true">' +
google_ads[i].line1 +
'</a> <span style="text-decoration:none;letter-spacing: 1.5px;color:#0e0e0e;font-family:verdana,arial,sans-serif;font-size:10pt;font-weight:400;">' +
google_ads[i].line2 +
'&nbsp;' +
google_ads[i].line3 +
'</span> <span><a style="line-height:normal;color:blue;letter-spacing: 1.2px;font-size:9pt;font-weight:400;text-decoration:none;font-family:verdana,arial,sans-serif;" href="' +
google_ads[i].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[i].visible_url +
'\';return true">' +
google_ads[i].visible_url +
'</span></a>';
s1 += "</div><br />"
}
if(document.getElementById('topadholder')) {
document.getElementById('topadholder').innerHTML = s1;
}
if (google_ads.length>5)
{
s2=s;
for (i = 5; i < google_ads.length; ++i) {
s2 += '<div style="margin-right:2px;">';
s2 += '<a style="color:blue;font-family:courier new,verdana;font-size:12pt;font-weight:bold;" href="' +
google_ads[i].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[i].visible_url +
'\';return true">' +
google_ads[i].line1 +
'</a><br /><span style="text-decoration:none;letter-spacing: 1.5px;color:#0e0e0e;font-family:verdana,arial,sans-serif;font-size:10pt;font-weight:400;">' +
google_ads[i].line2 +
'&nbsp;' +
google_ads[i].line3 +
'</span> <span><a style="line-height:normal;color:blue;letter-spacing: 1.2px;font-size:9pt;font-weight:400;text-decoration:none;font-family:verdana,arial,sans-serif;" href="' +
google_ads[i].url +
'" onmouseout="window.status=\'\'" onmouseover="window.status=\'go to ' +
google_ads[i].visible_url +
'\';return true">' +
google_ads[i].visible_url +
'</span></a><br /><br />';
s2 += "</div>";
}
if(document.getElementById('sideadplaceholder')) {
document.getElementById('sideadplaceholder').innerHTML = s2;
}
}
}
}
}
return;
}
google_ad_output = 'js';
google_feedback = 'on';
google_ad_client = "pub-0754410284344153";
google_alternate_color = "FFFFFF";
google_ad_channel ="1234567886";
google_ad_type = "text";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000EE";
google_color_url = "0000EE";
google_color_text = "000000";
</script>
</div>
<hr>
<ul>
<li><a href="javascript:history.back()">Back to previous page</a>
</li><li><a href="https://www.atarimagazines.com/index/?issue=v1n2">View this issue's table of contents</a>
</li></ul>
<protonpass-root-b2d3 data-protonpass-role="root" data-protonpass-theme="dark"><template shadowrootmode="closed"></template></protonpass-root-b2d3></body></html>