home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
224b.lha
/
A4th.doc
< prev
next >
Wrap
Text File
|
1989-04-08
|
30KB
|
679 lines
A4TH.DOC Notes on..
-------------------
A4TH. A Public Domain Forth system for Amiga's.
(based on Laxen & Perry's F83)
This Forth system is Public Domain. You may freely distribute,
copy and use it, for any legal purposes. I cannot be held
responsible for any errors and/or omissions, I do not warrant
this system. I bear no responsibility for any use or abuse,
with or without intend.
The system is composed of three 'arced' files:
( Arc program: AmigArc version 0.23, compatible with ARC v 5.0 )
A4THSYS.ARC The system and system source files.
A4th The executable kernel.
Akernel.blk The source for the kernel.
Util.blk The Utilities to extend the kernel.
Cpu68k.blk Assembler and debugger support
Meta.blk The Meta compiler source.
Include.blk The Amiga structures support source
Tasks.blk Task support source.
README Small warning that the file Util.blk has to be
renamed to Utilities.blk before using the system.
A4THINC.ARC The compressed and include files to be used with Include.blk
D1.ARC The Dx.ARC files are 'arced' directories.
D2.ARC
D3.ARC
D4.ARC
D5.ARC
D6.ARC
D7.ARC
D8.ARC
names A note on naming the romcalls and structures.
readme Instructions on how to create the include files.
unpack.bat Execute this batch file to recreate the include files.
A4THDOC.ARC Documentation and examples.
A4th.doc This file.
metavocs.txt Multiple vocabularies using Meta compiler.
infix.txt How the infix parser works.
hello.world example from intuition manual.
dotty.blk dotty window as in demo
dotty.c the example for dotty.blk
gad.blk gadget example
gad.c the example for gad.blk
Small.blk A real small hello
How to 'un-arc' the system.
---------------------------
I'll describe the way I have my system set up. It could work for you also.
Start off with a Workbench disk, with some room on it. Discard what you
think will not be usefull. You will need minimally 25% space on it. That is
what it will take for the include files. Please leave most of the files in
the c directory, enough to execute a batch file.
Create a directory on it, I call mine Includes, but you can use any name.
Move the file A4THINC.ARC to that directory.
Copy your favorite arc utility to your C: directory and name it 'arc'.
Unarc A4THINC.ARC. One of the files will be unpack.bat, which will restore
all the directories and files in the directories for you. Execute it and
let it go.
In your startup-sequence routine enter the following:
assign i: df0:includes
or the directory name you have chosen. That will make access to the include
files simple from anywhere. ( example: tload i:exec/ports )
Format a new diskette and move the files A4THSYS.ARC and A4THDOC.ARC to it.
Unarc the files, they are simple arced files, not directories.
The file Util.blk in the A4THSYS.ARC file must be renamed to Utilities.blk
before you run A4th.
In my setup (Amiga1000, 2drives, 512k) I use an interlaced screen
(640x400). That way I can have two A4th systems going at the same time, or
run A4th with a text editor, behind which Dos resides of course. Several
advantages, the biggest one is that I have twice the information in front
of me. I have been using an interlaced screen for some time now, and when I
switch back to a 640x200 display I feel hemmed in. If I enlarge the A4th
window to cover 640x400, I can dump more at the same time, see more words
at the same time etc. I did not change the editor to take advantage of the
large screen, it still expects 640x200, but will work in 640x400.
I also copy some commonly used routines to a ram: disk in order to speed up
some operations. My startup-sequence is as follows:
echo "A4th disk. Release 1.2 version 33.47"
echo " "
echo "current date"
date
echo "enter correct date"
date ?
date
date to df0:s/last-date
stack 8000
addbuffers df0: 25
addbuffers df1: 25
makedir ram:c
copy c:dir ram:c
copy c:type ram:c
copy c:cd ram:c
path ram:c
BindDrivers
assign i: df0:includes
cd df1:forth
Starting the kernel.
--------------------
FROM THE CLI, of course, you can start it by entering:
A4th
by itself, and the kernel will execute. After it is loaded, you can load
the Utilities by entering, in the forth window:
open Utilities.blk ok
which will load the extension as listed on screen 1 in the file
Utilities.blk.
Doing it this way will give you 64k bytes of space to use for anything
defined. The utilities will be located in that area, and take about 32k if
the distributed version is loaded as is.
Another problem is that you will loose the services of Amiga Dos. This
system does not have a directory utility for instance. It relies on the
Amiga multitasking capabilities for that.
Better to start A4th as a seperate task:
run A4th 18000 open Utilities.blk ok
That will open a 96k byte usable space and load the Utilities in. Make a
batch file out of it if you like. The number is a hex number.
About the system.
-----------------
GENERAL.
Although the excellent Public Domain system from Laxen & Perry was a
starting point, this system is NOT the same NOR is it F-83 standard. A
lot of the utilities present in F83 are also present in A4th.
If you expect lightning speed or super compactness, you will be
disappointed. It does not crawl, on the contrary, I find it responsive.
But then I might be a little prejudiced. :)
Try it out.
If you want support, or more speed, or compactness, or all of these, I
would suggest you part with (about) $100 and buy one (or both) of the
commercially available systems:
JFORTH Multi-Forth for Amiga
Box 1051 Creative Solutions Inc.
San Rafael, CA 94915 4701 Randolph Road, Suite 12
415-485-6867 Rockville, MD 20852
301-984-0262
Both of these are excellent systems and well worth the (just under)
$100.
Why did I write this, if the above commercially available systems are
so good? Well, first of all, I like to be in control of the entire
system. Secondly I cut my Forth teeth on L&P F83, like so many others,
and was looking for something similar.
The above mentioned systems use different threading schemes then the
L&P.
( If you are not entirely in agreement with the way this turned out,
you can always change it! And if you think the idea is good, share
it with the rest of the Forth community. )
The system features:
Indirect threaded
All numbers default to the address size of 32 bits.
Stack works on 32 bits at a time.
Uses two section, a precompiled kernel loaded by Dos, and a
usable user dictionary, size of which can be specified at
startup.
Word dictionary is inline, same as L&P, with 32 bit links.
Since the Amiga is multitasking and has no hardware memory management,
each program that is to be run must be in a special loader format. It
will not be run in the same location, and therefore needs relocation
information with the file.
That makes it impossible (well not really, nothing in Forth is
impossible) to save the current version from within the system. In
order to create a new kernel, you will have to Meta compile the kernel.
The Meta compiler saves the system in the proper format.
There are ways around this of course, but I chose not to use any of
them. For instance, use your own loader, or use a different threading
scheme.
Finally I tried to make a system that doesn't use all of the cpu's
registers. Naturally I set aside the Forth register:
SP(a7) RP(a6) IP(a5) W(a4)
The only additional register I used is >next(a3) which is the address
of the next routine. All other registers are available.
To add to that, another 'goal' of mine was to make it possible to read
some program in one of the Amiga Manuals and convert it for Forth.
Since most of the published stuff for Amiga is in C, I made some moves
towards that area, instead of staying more on the Forth track. (see the
infix parser in the include.blk file.)
KERNEL
- This kernel supports multitasking, using Amiga Exec, see also TASKS.
Some variables need to be different for each task. This kernel puts
these task or as they were called in L&P, user variables ahead of the
next routine.
The advantage is that no additional register needs to be used. In L&P
UP was used as the base address for the tasks. Here I use >next which
is cpu register a3 as a base register. The variables are located ahead
of the next routine. The disadvantage is that each task needs its own
next routine, as it turns out that is exactly what I wanted. One has to
metacompile in order to increase the number of 'user' variables.
The 'rcall' routine is now (as opposed to version 1.0) right after the
next routine. It is the bases for calling Amiga Rom routines.
It is a bit of self modifying code (heavens!). Because of that it
cannot be used by any other tasks. Each task will need its own copy.
Since it is now directly after next, it can be copied to any new task,
together with 'next' and task, or 'user', variables. All that the new
task will have to do is change the >next register to point to its own
'next' routine. (Of course the other registers must also be set).
The 'callrom' routine on screen 81 is the routine that sets the code in
rcall. It uses relative addressing (relative to >next), each task will
thus use their own rcall, but can use the common callrom.
Calculating the address of a task variable is a matter of adding the
>next register to the offset. The same is true for task deferred words.
- You realize that having Exec provide the task switching releaves the
kernel of the 'pause' word, that was used in L&P.
- The cmove and cmove> and also fill routines are limited to 64k bytes.
- In the previous version, I used a stdbuffer for output of single
characters and multiple characters, such as spaces.
In this version I use pad for this purpose. Stdbuffer is no longer.
- Note that the common character output routine is 'type', not 'emit' as
in L&P. In other words, emit calls type, in L&P type called emit.
- In the kernel you will find, on screen 67, 68 and 75 some references to
words having brackets around them. For example <find>. These words are
a way to Meta compile in different vocabularies, see Meta compiler
explanation for more info. Normally these have no effect on finding and
defining words.
- Doubles are really 64 bits wide. */mod is internally 64 bits. Large
enough to not need floating point? But you can use Amiga Rom math if
you want to.
- Defined is now deferred, and defaults to (defined). It is used in
structure support.
- Of course I have added support for the task variables. Screen 79.
- In the previous version I had a linked list, linking all the declared
Amiga Library bases together. When the system was exited the list was
traversed and each library was closed. That caused some complications
when defining a library base.
In this version I have made an array of opened library bases, or
devices for that matter. When the system is exited the array is checked
and each open library is closed.
Access to each base is via an index, which are constants with names
referring to the library, such as >Exec, >Dos and >Intuition.
These three are opened by the kernel,as they are needed to run the
kernel. One more is defined, >Graphics, but is opened in the
Utilities.blk file: it is not required by the kernel, or the Utilities
for that matter. It is opened as a convenience. (screen 81)
- These library base indices are used to define an Amiga Rom routine. On
screen 83 are some examples. The registers used in each call are
defined between (r and r), which simply sets the base to hex and treats
the registers as hex numbers. It then calculates the mask. No specific
order of registers is required, I put them in the order they will be
filled from the stack. In the first call defined, OpenLibrary, the name
will be stuck in the a1 register, the version in d0.
The offset and the flag indicating a return value (^) are assembled
into three words, as tight as I could make it.
In the Utilities.blk is support in the decompiler to 'see' these
defined rom call entry points.
- The change in (type), screen 88, was to accomodate the task variable
#out. Before it was not a task variable. Now it is, so it needs to be
calculated at run time. It could be used by any task. This is easily
done in high level. At the end of (type) you can see I switch from code
to high level using 'c:' but I don't return to code, I simply end the
word as if it was a colon definition. Works ok.
Similarily, here and pad are now also in high level, to let the system
calculate the proper address at runtime.
- To see an example of opening a library look at screen 91, or screen 98.
That is the only time you need to be concerned with the library base.
Most of the routines are already declared in the include files, later.
- In the Intuition vocabulary I have added the romcalls, AllocRemember
and FreeRemember. They support allocating memory and freeing it again.
A linked list pointer, mem-link, helps to find them. When forgetting
the list is traversed and any allocated memory, about to be forgotten
will be released before access to the RememberKey is lost. Tasks use
them. This method is available to any.
When exiting A4th close-mem will release all.
- Some new stuff in screens 99-101. The first bunch is some bitfiddling,
used in Meta, but available for any bitarray. The words >> and << are
here to make it a little easier to translate programs from 'C' to
forth.
Next is a random number generator. I am no statistician, works ok for
me, if you have a better one, let me 'ave it.(pleese). Randomseed is
there to be used, if you need variety.
Tinit is a small code routine used by tasks. I put it here, in order to
be able to not include Cpu68k.blk. That would leave out the assembler
and the debugger. Still enough of the system to be usefull, but
significantly smaller.
The last ones are some C like string support, that is strings without
the length byte and with an ending null byte.
- I have added a 'fence' variable. You can set it to prevent forgetting
by mistake.
Forgetting now traverses the file linked list, the mem linked list and
the vocabulary linked list.
- The window declared in the Intuition screen is now called 4thw,
previous version was Window, which collided with the Window structure
declaration in the Intuition include file.
- There are now dp0 a variable holding the address of the start of the
usable dictionary, and dpsize a variable holding the size of the usable
dictionary area. Before they were simple stored in some 'unnamed'
cells.
- The cold start routine now sets the task variable dp, which has to be
the first task variable.
- User or task variables are set by temporarily setting the (meta) dp to
the user area, then filling the locations. This requires that all the
task variables are set. Screen 111
- All variables are now set in screen 113, again when meta compiling.
META
- Gone is the dump utility, use the kernel's by not forgetting it. Forget
'out' should be sufficient.
- This Meta compiler supports vocabularies in the target. An additional
file, METAVOCS.TXT, explains it more.
What the end result is, is that you are now able to declare
vocabularies in the target, and declare the same word in different
vocabularies. You will get the one you want if you control the target
search order correctly. This is particularily important if you intend
to Meta compile more then just the kernel.
For instance, you could compile the assembler in the kernel and make it
part of the kernel. The system will load faster.
Enlarging the target buffer (now 32k in Akernel.blk screen 1) you could
compile a lot, possibly an application.
The only hitch is that defining words ( create..does> ) must be handled
when compiling. The assembler has a few of them. To handle that I made:
h: ado (s n1 n2 -- n1 ) \ used after does> word is defined
target-create over ,-tr ,-t ;
Then to use the above, eg with sz screen 3 Cpu68k.blk
: sz (s n -- ) does> @ size @ and or ;
00300 ado sz3 00400 ado sz4
30000 ado sz300 drop
Since does> in Meta leaves the address for patching, it works.
There are other problems, all solvable.
Don't forget the width, setting it to 0 will create a headerless
system, or manipulate it while compiling, and make words disappear.
- A user vocabulary is added to Meta.
- Removed the bitfiddlers, they exist in the kernel.
- Removed .relocations, wasn't very usefull.
- Made saving the target system a little more readable. Created some
words to take the noise out of it.
But more importantly, block storage section or bss is now optional.
Before you had to declare some, even if you didn't need it. The target
is saved with the proper header, and relocation information.
- Vocabulary creates a vocabulary in the target (no kidding). It also
creates a vocabulary with the same name in the symbol table. Symbols
are linked to the last one, depending on the vocabulary in <current>
- The Meta compiler determines the size of the task variable area, see
screen 23.
- Screen 24 allows target symbol table vocabulary manipulation.
Gone is .symbols, it's not needed, use <words> to see the symbols.
UTILITIES
- Screen 1 determines the utilities to be loaded, change as required.
- Open the Graphics library, screen 13.
- The editor is unchanged from the previous version. See the alternate or
help screens for more information.
- The decompiler has been extended to show a" strings, or amiga
compatible strings. It also shows each library rom call if you want.
Typing 'see OpenLibrary' gives some detailed information about the
call.
User or task variables and deferred words are also decompiled.
- I did not implement any printing by pressing control-P, not in the
previous version or this one. I hate noisy printers, I love laser
printers, but they to expensive. So I very rarely print. If you want
to, take a look at how the printer is intialized. 'init-pr' is deferred
and is right now set to some control characters that initialize my
printer to 132 columns. You will have to set this.
Also the deferred p-name determines where the printer is connected to.
Normally it is PRT: for my setup it is SER:. Change it accordingly.
ASSEMBLER
- The assembler is not changed. A small correction in one of the words.
Take a look at the Akernel.blk for example usage of the assembler.
- The debugger is changed. Since >next must not change in each task,
the debugger had to be changed. (Before I changed >next). Now a jump to
debnext is put in the place of the next routine. Unbug will fix the
next routine. You will invite problems if you declare a new task when
debug is in effect.
INCLUDE
- The first part of the utility to load the predefined structures and
definitions for the Amiga, is a parser.
This one is infix, and is extensively used in the Include files, which
are text files. It also has some use in filling structures, see {fill.
The text file INFIX.TXT explains the parser in more detail.
The parser is in screens 4 to 8.
- The next part, screens 12-18, are textfile loading.
I prefer regular Forth blocks. The textfile loading does not support
nesting. It was not intended to be the principal method of loading
programs. I wrote it to load the Include files, outside of the Forth
blocks. These are left alone, the text file loading opens a buffer for
its own use, and when done, releases it.
In screen 14 some additional Dos words are defined. I load these prior
to any Include files, as a result these definitions are commented out
in the include file libraries/dos. The same goes for all the words
already defined in the kernel. Intuition calls, Exec calls etc
defined in the kernel are all commented out.
The text file loading obtains a Lock before it opens the file. Once the
files has been opened it maintains some pointers into the buffer. It
scans for LineFeeds as line terminators.
Getline gets the next line. The line start will be returned by the word
'line' and the length is returned by the word 'llen'.
The above three words are in the 'text' vocabulary, hidden normally.
The words visible are: more?, ttype, tload.
More? is a small keyboard pause word while listing a file via ttype.
Ttype, or text-type, takes a file name and lists it. Any key will
pause, and a return key will stop listing.
Tload takes the next word in the input stream as a text file and loads
it. It must be loadable, if it is not the system will not be forgiving;
it will hang.
See the examples and Tasks.blk for some example text file loading.
- I tried to keep Include files small. They do not rely on other include
files, with the exception of libraries/dos and libraries/dosextens.
The intuition file that comes with the assembler or a C compiler is
large. I made it a collection of files, each with ROM calls defined at
the end.
My model for these include files was Draco. Present on FredFish #76/77.
It is a compiled language in C and Pascal spirit written by Chris Gray.
- The last part of the Include.blk file is the structures.
- Structures are defined with a {s..s} pair. The field names for the
structure are hidden in a single linked list of words. The start of
that word list is put in a variable, scontext, when a structure defined
with the template, is used. An example is probably a better
explanation..
In the file i:exec/lists is the List structure defined:
{s List
APTR lh_Head
APTR lh_Tail
APTR lh_TailPred
BYTE lh_Type
BYTE l_pad
s}
The opening {s takes the next word and makes it a defining word. This
word will have the pointer to the linked list of field words and the
total size of the structure in the parameter field. (The size of a
structure is <=64k ). To make a definition using the List word:
List MemEntry
In this case List creates an actual structure, List is just a template.
The structure has the name MemEntry, and when it is executed it will
put the address of the structure on the stack and stick the pointer to
the linked list of field words in scontext.
The search order, which was made deferred in the kernel, is now altered
so that the field words are checked FIRST. Then the rest of the search
order as set in 'context' is searched.
For instance, if you want to get the value from the lh_Type field:
MemEntry lh_Type s@
That would return the type byte on the stack. What happens, MemEntry
puts the address of the structure on the stack. Next lh_Type is found
and executed. It alters the address by the offset of lh_Type to the
start of the structure and sets the deferred word s@ and s! to that of
a character fetch and store. Executing s@ will get the byte from that
address.
- Another example is in i:exec/ports, there the structure for MsgPort is
defined as follows:
{s MsgPort
struct 14 mp_Node ( { Node=14;exec/nodes )
BYTE mp_Flags
BYTE mp_SigBit
APTR mp_SigTask -4 soffset +!
APTR mp_SoftInt ( union )
struct 14 mp_MsgList ( { List=14;exec/lists )
s}
The field defining word 'struct' is actually a synonym for BYTES, but
in this case it indicates that the next 14 bytes are a nested structure
with the name mp_Node. At first I concidered nesting of substructures,
but rejected it. As a compromise I added the reference after the field
definition in the Include file. Here the nested structure is a Node, is
14 bytes large and the defininition is in the file exec/nodes. If you
require access to the nested structure in mp_Node you must load the
include file i:exec/nodes also. For example if you require the list
type of mp_MsgList which is a List in the occurance of a MsgPort called
e.g. myport:
myport mp_MsgList { List lh_Type s@
The sequence { List forces the field names for a List structure to
become available. The '{' is immediate, and must be used in colon
definitions, because if the occurance of a structure is compiled, it
does not set the scontext variable to point to its field words.
: ........... myport { MsgPort mp_Flags s@ ......... ;
The MsgPort field words will be available until the next '{'. Examples
of its use are in the examples provided.
( The major objection to this way of accessing structures )
- Another peculiarity is the -4 soffset +! sequence. That sequence
resets 'soffset' to what it was before APRT mp_SigTask was defined.
The next line APTR mp_SoftInt will point to the same offset as
mp_SigTask. This is the way I handled unions as they are called in C.
Not many are present in the include files.
- The field defining words are:
BYTE BYTES WORD WORDS LONG LONGS STRUCT
APTR is a synonym for LONG. The plurals expect a value before the
actual field name. The infix parser is used to get the number.
STRUCT is used if the structure is defined and known. The next word
will be the structure template that provides the number of bytes to
reserve as the fieldsize for the word following.
These field definitions, when used, set the structured fetch/store
words, s@/s!, to the appropriate size. If you do not know what the
field size is you can use s@/s!, or if you do know use the regular
Forth fetch/store.
- After using a structure word or after setting scontext to a specific
structure you can see the names of the fields using 'swords'. It will
print a list of field names, backwards. For instance when the system is
loaded, as distributed, enter:
Exec { Task swords
This will print all the fieldnames, use any keypress to stop/start the
listing.
Another way of showing what a structure consists of is:
.{ Task
This will show the fieldname and what type it is. And this time in the
declared order, again use key to stop/start.
The last aid is {?}. It takes an address of the stack and uses the
structure which is currently in scontext to show each field, the type,
the offset and the contents of it in hex. The use of that? Try:
Exec definitions
tload i:exec/execbase
{ ExecBase 4 @ {?}
And the execbase is visible.
- An additional help word is {fill. It is used in one of the example
files. It uses the infix parser to fill the structure, and all of it,
from the input. See the example file Gad.blk for its usage.
- As a final note on structures;
I had to redefine 'forget' since 'defined' would get messed up. The new
forget will work properly and can even forget itself.
TASKS
- Tasks use Exec as scheduler. They are simple tasks and require their
own stack and return stack and task area.
Task area is taken from the dictionary, in which the task local
variables, the next routine and the rcall routine are copied.
The stacks are obtained from Exec and kept in an Intuition Remember
key, which is linked into the mem-link so the task can be forgotten.
- Tasks can be given parameters, they will be copied to the task stack
before the task is started. No parameters are expected in return.
- Tasks should not use the A4th console input and output, they should
define their own input and output file or device. A tasks output file
could be a window of course.
- Tasks should also have one entrance and one means of exit. If you use
an abort, things will not work properly.
- Priority of tasks are set at -25 that puts them below any system task.
This makes sure it behaves as a background task. A4th will continue
running at full speed, and when time is available the background task
will run. As it should.
If for some reason the task must execute at a different priority, you
can alter it at any time, using 'priority'.
- Tasks cannot compile or do any interpreting, the dp for the task is
mainly used for number conversion and to find pad which is needed for
emit.
- The example on screen 17 shows how tasks can be used.
- Each task needs a 'stop', and 'activate' requires the task on the stack
under which should be the parameter count (0 for none) and the
parameters under that.
Tasks can be reassigned on the fly. But be carefull the resources
allocated by the task will not be released; if a window was opened it
will stay open. That could be exactly what you want of course.
- The tasks are a minimal approach and it's up to you to make it better
and/or more fool proof.
I appologize for any mistakes I have made that might haunt you, I do hope
you will be able to use this system as a tool and have some fun with Forth.
Peter Appelman
05Oct88