Forth News, Tricks
Volume Number: 9
Issue Number: 9
Column Tag: Jörg's Folder
Forth Tools, News and Tricks 
Creative Solutions is distributing some excellent solutions for real
problems
By Jörg Langowski, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
OK, here I’m back after one month’s break. And I’ll finally give you some news
from the Forth scene. Actually, it is nice to see that there is still an ‘ecological niche’
for Forth programmers in the Macintosh world - with all the Bedrock and class
libraries and what-not out there that try to abstract (distract?) you more and more
from the actual machine you’re working on. Although this is nice for those of you
planning the program that’s going to sell a million copies on Macs, Windows, and
X-Windows environments, there still seem to be quite a few individuals that want to
make the Mac do something quickly and elegantly, without producing 3 megabytes of
code (and we do get feedback from you, and are very happy about that). Forth seems to
be one of the languages of choice for those ‘individualized’ tasks, and fortunately there
is MacForth around, with a big user base and a CompuServe forum. Then, of course,
there are the object-oriented Mops and Yerk Forth environments which you’ve read
about. For those of you interested in chasing lost dreams, the basically defunct Mach2
Forth may be on its way to resurrection. First, though, I’d like to show you a couple of
interesting tools and demos that can be found on the MacForth Tools disk, which is
available for $25 from Creative Solutions (see their ad in this issue).
The MacForth 4.0 Tools
This disk contains a wealth of useful Forth code, a lot of it in the public domain
and from other sources. Those of you with no access to CompuServe, like myself, will
appreciate this collection. The most important single piece of code is an upgrade to the
editor integrated into MacForth. Even in its original version, the Sibley Editor is
already one of the main reasons why MacForth is such an appealing development
system - it features multiple windows in which Forth code can be executed
interactively (similar to MPW worksheet windows), automatic highlighting of the
place where an error occurred, scroll bars with line/page number display, setting
markers, and more. Best of all, the full source code is included with MacForth, so you
can customize the editor if you like. That is what Jeff Dripps did with his extensions to
the editor, and now the Sibley editor has really grown into a fancy programming tool.
Now the editor features color windows, and parameters like foreground and background
colors, scroll modes, word wrap, the current selection etc. are held in resources in the
edited text file. The maximum number of open windows is 24, and in the next version
there will be no limit. There is also a very extensive search/replace facility which
allows you to work on all files in a folder at once. All in all, this makes the MacForth
editor almost as powerful as the MPW worksheets; alas, MPW doesn’t give you a Forth
environment
A Control Device in MacForth
Another piece of code that I liked was an example by Scott Squires on
programming a CDEV and a desk accessory. There is support for stand-alone code in
MacForth, which adds a ‘mini-kernel’ to your Forth code so it can be called as a code
resource from the Mac system. I enclose the control panel device on the source code
disk; it shows a simple window with two controls and a clickable user item (Fig. 1).
The resulting code is only 11390 bytes long, and the actual executable code (Forth
mini-kernel + Forth code) is only 2170 bytes out of that! The compactness of Forth
code really shows here - of course, it would be even more striking for more
complicated applications. The example listing shows an excerpt of the Forth source
code for the CDEV. Writing such a control device is rather straightforward, it is a code
resource which is called through a standardized interface; your code has to do certain
actions depending which message it receives when it is called.
The system calls the code contained in the ‘cdev’ resource as if it were a Pascal
function defined in the following way:
FUNCTION cdev (message, Item, numItems, CPanelID: INTEGER;
VAR theEvent: EventRecord; cdevValue: LONGINT;
CPDialog: DialogPtr ) : LONGINT;
Here message contains the type of action that the CDEV has to perform, item is the
number of the item that was hit in the control panel, numItems the number of dialog
items in the control panel, CPanelID gives the resource ID of the Control Panel driver
(numItems and CPanelID are really only relevant for System 6.x, where the Control
Panel is a desk accessory), theEvent is the event record corresponding to the event that
caused the message, cdevValue is the return value from the last access of the CDEV
(which is given back here and can therefore be used as an intermediate storage, e.g.,
for a handle to a data block), and CPDialog is the dialog pointer of the Control Panel
dialog box.
Fig. 1: A CDEV written in MacForth
The main body of the control device code then consists of a CASE statement which
calls the appropriate handlers (see listing). In the example, I have only printed a few
of those handlers, to give you an idea how a control device can be written in MacForth. I
leave the code for the other handlers to your imagination.
Vertical blanking tasks
Another contributor to the MacForth tools disk is Arden Henderson (who put all
of his code in the public domain). Among others, he gives an example of a vertical
blanking task written in Forth. Remember, unlike Desk Accessories and MacForth
multitasking, VBL Tasks execute without calling SYSTEM.TASK (or DO.EVENTS).
Therefore, they run despite menus, dialogs, mouse downs, synchronous i/o, other
applications using the CPU, etc. However, a VBL Task may not use any traps that
allocate, move, or resize heap objects through the memory manager. Years ago, in
volume 2#6 of (then) MacTutor, we had an example of a screen saver written as a VBL
task in Mach2. Well, here is a similar example in MacForth. As for the CDEV, a
mini-kernel is needed to interpret the token-threaded MacForth code in order to
produce stand-alone code that can be integrated into the vertical blanking task queue.
Ward McFarland, that all-time MacForth hero and generous contributor to the Tools
disk, has written the support code that installs MacForth VBL tasks. With this code
present, you can define a VBL task in a very simple way (see listing):
• put a tick value after which the VBL task should execute for the first time into
the VBLTask.Duration field of the VBL queue element;
• put the tick value after which the VBL task should be re-executed (zero if no
re-execution) into the VBLTask.AutoRepeat field of the VBL queue element;
• put the token corresponding to the Forth word to be executed in the VBL task into
the VBLTask.token field of the VBL queue element;
• and call add.vblTask with the address of the VBL queue element on the stack.
The extra information necessary for executing MacForth code from a VBL task is
appended to the standard VBL queue element, and the MacForth VBL queue element is
defined as:
\1
STRUCTURE VBLTASK \ MacForth VBL Task
\ long: ( relative chain linkage to VBLTask.LINKAGE )
\ long: ( CFA of MacForth VBL Task )
\ the first 14 bytes of the PFA define a standard Macintosh
VBLQueueEntry:
long: ^vblQ.Link
\ pointer to next element in chain - set by Mac
short: ^vblQ.Type
\ queue type = 01 for VBLQueueEntry
long: ^vblQ.Addr
\ address of code routine to be executed at timeout
short: ^vblQ.Duration
\ number of ticks remaining before timeout
short: ^vblQ.Phase \ see IM
\ The next 16+ bytes is specific to MacForth VBL Tasks:
short: ^vbl.AutoRepeat
\ #Ticks to setup for subsequent timeouts
long: ^vbl.Addr
\ Non-relocatable address of user Code routine
short: ^vbl.Token
\ Token to execute
long: ^vbl.Addr.Rel
\ Relocatable Address of user code routine
long: ^vbl.Spare \ User specifiable long word
\ room for extra stuff here if you need it...
\ (an event number to post, flags,
\ data storage area, etc.)
byte: ^Dflt.VBL.Code
\ start of short assembly routine for ^vblQ.Addr
\ this is where the Forth ‘mini-kernel’ goes
STRUCTURE.END
When the VBL task is executed, a jump is taken to the mini-kernel, which starts
Forth execution at the token contained in vbl.token (at least that’s it in a nutshell). For
more details, you should get the MacForth Tools disk, or look in the CompuServe
MacForth forum.
Dissect - a recursive Forth decompiler
The last MacForth tool I’d like to present is a very powerful decompiler that
re- creates the Forth code from a compiled definition. Since Forth code is threaded, that
means highly structured, the words from which a definition is compiled will in turn
reference definitions that consist of several words. With the decompiler Dissect 4.0
(by Ward McFarland), you can choose the depth to which a Forth definition will be
dissected. For instance, if you define the word
: hello.world .” hello world” cr ;
and then
: hi 10 0 do hello.world loop ;
you can view your definition by typing
0 rdef hi
which will give:
'NFA' Name Token CFA (CFA) (PFA)
19A0 HI CAAC 54008A 4EAE004A 00E6 000A 05AA 020A
found in FORTH ( in "Dissect 4.0" )
$54008E WLIT 10
$540092 0
$540094 (DO) $54009C
$540098 HELLO.WORLD
$54009A (LOOP)
$54009C (;)
and with
1 rdef hi
you step down one more level and will see the definitions of each word of which hi is
composed:
'NFA' Name Token CFA (CFA) (PFA)
19A0 HI CAAC 54008A 4EAE004A 00E6 000A 05AA 020A
found in FORTH ( in "Dissect 4.0" )
$54008E WLIT 10 Machine Language
( token = $ E6 , CFA = $51F5F8 )
0051,F5F8: 305A MOVEA.W (A2)+,A0
0051,F5FA: 2F08 MOVE.L A0,-(A7)
0051,F5FC: 361A MOVE.W (A2)+,D3
0051,F5FE: 4EF6 3000 JMP (0,A6,D3.W)
$540092 0 WConstant = 0
$540094 (DO) $54009C Machine Language
( token = $ 20A , CFA = $51F71C )
0051,F71C: 2706 MOVE.L D6,-(A3)
0051,F71E: 2705 MOVE.L D5,-(A3)
0051,F720: 548A ADDQ.L #2,A2
0051,F722: 270A MOVE.L A2,-(A3)
0051,F724: 2A3C 8000 0000 MOVE.L #-80000000,D5
0051,F72A: 261F MOVE.L (A7)+,D3
0051,F72C: 2C1F MOVE.L (A7)+,D6
0051,F72E: 9686 SUB.L D6,D3
0051,F730: 67C0 BEQ.B 51F6F2
0051,F732: DC85 ADD.L D5,D6
0051,F734: DA83 ADD.L D3,D5
0051,F736: 361A MOVE.W (A2)+,D3
0051,F738: 4EF6 3000 JMP (0,A6,D3.W)
$540098 HELLO.WORLD
$540078 >1 (.") "hello world
$540086 >1 CR
$540088 >1 (;)