This guy goes on vacation to a tropical island. As soon as he
gets off the plane, he hears drums. He thinks "Wow, this is
cool." He goes to the beach, he hears the drums, he eats lunch,
he hears drums, he goes to a luau, he hears drums. He TRIES to go
to sleep, he hears drums.
This goes on for several nights, and gets to the point where the
guy can't sleep at night because of the drums. Finally, he goes
down to the front desk.
When he gets there, he asks the manager "Hey! What's with these
drums. Don't they ever stop? I can't get any sleep."
The manager says, "No! Drums must NEVER stop. Very bad if drums
stop."
"Why?"
"When drums stop... bass solo begins."
Source: Internet rec.funny.humour
GFA BASIC TIPS & TRICKS
by Richard Karsmakers
I spent most of the summer holiday reprogramming my "Ultimate
Virus Killer". It was quite an enormous piece of work, and I have
been frustrated more than once. Now I come to mention it,
frustration doesn't quite cover the emotions I had.
Anyway, being the good sort of chap I am, I decided to share
with you some of my coding experiences. While all you lazy gits
were lying in the sun, ass up, I was working myself in a sweat
behind the keyboard of my ever faithful MEGA ST. I came up with
another bug in "GfA Basic", and I came up (with assistance of
Kai Holst, I should add) with some rather interesting ways to
speed up program execution.
First, however, I would like to spend some space on crediting a
German chap by the name of Gregor Duchalski who has written a set
of shareware routines all ready to be merged into "GfA Basic"
that make it literally a piece of cake to work with GEM (and most
particularly so-called dialog boxes). It is called "Flydials" and
may be obtained (and registered) by sending DM 30,- or more to
Gregor Duchalski, Baueracker 15a, W-44627 Herne, Germany. You
won't regret it if you want to make use of flying dialogs, radio
buttons, popup buttons, keyboard shortcuts and whatnot within
your own programs.
The library of routines is some 25 Kb in size and allows you to
display, evaluate and remove entire dialog trees or objects with
perfect ease. One line is needed to put the whole thing on the
screen, a DO-LOOP can be used to evaluate which objects are
pressed (they can also be edited, etc.), and another line is
needed to remove the dialog again. Of course the entire
background is buffered properly.
Upon registering you get additional routines to allow for
resource files being included in your source file, and quite a
large library of other handy routines. No serious application
programmer that uses "GfA Basic" should be without this.
The rest of this article will be short. The tricks I want to
give don't need extensive explanation, but I just wanted you to
share in this experience which may save you a few grey hairs and
a couple of hours programming.
The MENU_REGISTER command
I can be quite short about this one. This is the command needed
to register the name of an accessory on the leftmost pull-down
menu list on the desktop.
The syntax for this command is the following:
MENU_REGISTER(ap_id,m_text$)
Here, ap_id is the value you get from the APPL_INIT call (i.e.
the application identification, higher than 0 if you use a
program as accessory or with a multi-tasking OS), and m_text$ is
a text containing the name you want to use, ending with a
chr$(0).
This usually works, but not all the time I should hasten to add.
The MENU_REGISTER command passes the parameters on to the AES,
which will then use the address where the string is stored each
time a MENU_DRAW command is used (by the desktop or by another
program). Unfortunately, "GfA Basic" needs you to supply the
string contents instead of the address, which means the string
contents may be lost or moved somewhere else by internal variable
reorganisation. In those cases one may encounter bomb crashes.
To make everything work 100% proof, instead of the regular
MENU_REGISTER use the following instead:
PROCEDURE menu_register(ap_id&,nam$)
nam$=nam$+CHR$(0)
BMOVE V:nam$,BASEPAGE+192,LEN(nam$)
DPOKE GINTIN,ap_id&
LPOKE ADDRIN,BASEPAGE+192
GEMSYS 35
RETURN
This routine can be called using the syntax:
@menu_register(ap_id&," Menu Title ")
Instead of just passing the string on to the AES and then
leaving it be, it stored the string in an empty bit of the
program's basepage (i.e. the basepage of the program currently
being executed, which is that of the program itself unless you're
in the "GfA Interpreter" in which case it will be the basepage of
the interpreter). This will not be changed and remains at a set
address, thus alleviating the problem.
I have good experience with using two spaces before the name.
You will see that most accessories do that. And you'd better not
make your menu titles too long.
Use GEMDOS commands
Whilst walking through the mountains with Miranda and Kai, I
couldn't help but talk shop for a while. I encouraged Kai to
explain to me how come his virus killer link virus check routine
was faster than mine. After all, all I did was "OPEN" it, then
"SEEK" and then "GET", using the built-in "GfA Basic" commands.
That was the only thing I did, really.
"Ah," he said, "I see. What about using GEMDOS commands instead
of 'GfA Basic' ones?"
And, indeed, that was the thing that made my virus killer so
slow. After I'd come home I sat down and reprogrammed the link
virus check routine using GEMDOS commands only. It worked, and it
was much faster.
Instead of using:
OPEN "I",#1,"Filename.txt"
One has to use:
lo$="Filename.txt"+CHR$(0)
handle&=GEMDOS(&H3D,L:V:lo$,0) !GEMDOS "OPEN"
The filename has to be specified to GEMDOS, which means that you
have to add a CHR$(0). Whereas the "OPEN" command requires a
channel to be opened (in my case #1), the GEMDOS "OPEN" command
instead gives back a unique 'handle' that you should further
refer to if you're ever to read from or write to that opened
file.
So instead of:
SEEK #1,&h1c
You would use:
VOID GEMDOS(&H42,L:&H1C,handle&,0) !GEMDOS "SEEK"
The &H42 is the GEMDOS function call, the L:&H1C is the offset
(in this case hex 1c), the word after that is the handle& given
back by the GEMDOS "OPEN" command, and the last 0 not really
important for our purpose.
And now, how to actually get bytes?
Instead of:
a$=SPACE$(512) !Buffer address
BGET #1,V:a$,4 !Get four bytes into a$
You would want to use:
VOID GEMDOS(&H3F,handle&,L:4,L:V:a$) !GEMDOS "READ"
Here, the &H3f is the function number, the handle& is the handle
we've mentioned already, the longword after that is the amount of
bytes to get (here 4) and the last longword is the address of the
buffer where those bytes should be put (in this case the VARPTR
of a$).
Once you've finished all stuff, you should CLOSE the file again.
Instead of
CLOSE #1
You now have to use:
VOID GEMDOS(&H3E,handle&)
And that's all.
One problem that may occur is that, for example, you might want
to find out the file length. Normally one would use the command
"LOF(#1)" for that, but as there's no channel open you can't use
that. You have to get it in an alternative way, which means you
have to read it directly from the DTA (Disk Transfer Area).
The size of the currently opened file can be found out with the
following line of code:
size%=LONG{(GEMDOS(&H2F)+26)} !GEMDOS "GET DTA"
And that's it for this bit.
Use of BYTE command and CASE SELECT
"GfA Basic" has some very fast commands to get values of memory
contents. One of these is the BYTE command, which gets the value
of one byte into a variable. Its syntax is the following:
a|=BYTE{address%}
This example would get the value of the byte on the address
contained in the longword variable address% into the byte
variable a|.
With this knowledge, it's possible to have much faster string
analysis. In my virus killer, for example, I often had to
determine what the value of the leftmost character in a string
was, and then do several things depending on the outcome.
If you take the example below:
IF LEFT$(a$)="A"
@a
ELSE IF LEFT$(a$)="B"
@b
...
ELSE IF LEFT$(a$)="Z"
@z
ENDIF
You can contrive the following method to do the same thing much
quicker, also by using a CASE...SELECT structure.
SELECT BYTE{V:a$} !Gets leftmost byte of a$
CASE 65 !ASCII for "A"
@a
CASE 66
@b
...
CASE 89 !ASCII for "Z"
@z
ENDSELECT
However, in practice this method may prove to get the wrong
results. If a$ would happen to be empty (i.e. it contains "", a 0
length string) the CASE routine would assume it has the contents
that it previously had. For example, if a$ was "A" before and was
consecutively emptied, the string contents are still "A" but the
length is 0 which means that it's empty. The BYTE command doesn't
care about string length and finds "A" instead of nothing.
This can be solved by using the following SELECT line instead of
the one above:
SELECT ABS(BYTE{V:a$}*(LEN(a$)>0))
This line takes the value of the leftmost byte in the string,
and then multiplies it by TRUE or FALSE (i.e. -1 or 0) depending
on whether a$ has a length longer than 0 yes or no. So if a$ is
not empty the byte value would be multiplied by -1, if empty it
would be multiplied by 0 and thus get the value you want. The ABS
command gets rid of the negative sign.
Of course, this line is a bit slower than the first
CASE...SELECT solution, but still considerably faster than
LEFT$() commands. You need not use the extensive solution if
you're sure the string will never have to be empty, i.e. when
there is no CASE 0.
Of course you can also mimic the RIGHT$() command with the
BYTE{} command. You only need to add the length by means of the
LEN command. The LEN command is just as fast as getting the
string length directly from the string descriptor using ARRPTR,
so it's easier just to use LEN.
Determining whether a program is ACC, under MTOS, and what's AES?
The bits in this part are all derived from parts of Gregor
Duchalski's "Flydials" source. I think they may be very useful.
To determine whether a programing is running under MultiTOS on a
multi-tasking compatible machine, use the following line:
mtos!=INT{ADD({ADD(GB,4)},2)}=-1 AND
INT{ADD({ADD(GB,4)},0)}>=&H400
(Of course you should enter these both on one line)
The boolean variable mtos! will be TRUE when "MultiTOS" is
present, FALSE if not.
To get the AES version number, use the following line:
aesver$=HEX$(INT{ADD({ADD(GB,4)},0)})
Sometimes it's important to know whether a program is installed
as accessory or not. In the olden days you could simply use
APPL_INIT(), which would be "0" if it wasn't an accessory and 1-6
if it was an accessory. With the occurrence of multi-tasking
systems, however, this is no longer the case. This can be very
confusing, as regular programs running in multi-tasking mode
would then be designated to be accessories.
The way to determine whether your program is run as an accessory
is the following:
DEFFN acc=({ADD(BASEPAGE,36)}=0)
This definines a one-line function called "acc". Look at a
further example below for explanation.
IF @acc
PRINT "Accessory!"
ELSE
PRINT "No accessory!"
ENDIF
This DEFFN function will be used in a further example as well,
below.
How to find the Accessory Path
Many of you are probably aware of an accessory called
"Chameleon" by Karsten Isakovics. It's an accessory that can load
and unload any other accessory, the only limit being that
accessories can't be unloaded if they've bent system vectors.
However, there's a bit of a problem involved where accessories
with resource files (RSC files) are concerned. Accessories would
have to be present on drive A: or something for those accessories
to be found, i.e. normally they aren't found when you're working
from hard disk. I believe this is a problem in GEM or AES, but
there's a way around it. It's not perfect (not even eloquent),
but it works.
The necessary code may be found below:
DEFFN search_file(filename$)=GEMDOS(78,L:V:filename$,0)>-1
IF @acc !Accessory? (See article)
paas$=CHAR{@find_the_path}
ENDIF
FUNCTION find_the_path
LOCAL device$,boot&,foutje!,x|
boot&=DPEEK(&H446) !Boot device-1
device$=CHR$(boot&+65)+":"
IF @search_file(device$+DIR$(boot&+1)+"\TEST.RSC"+CHR$(0))
paas$=device$+DIR$(boot&+1)+"\"+CHR$(0)
ELSE
IF boot&<2 !Floppy
x|=0
ELSE
x|=2
ENDIF
REPEAT
INC x|
IF BTST(BIOS(10),x|-1)=0 !Invalid drive
foutje!=TRUE
EXIT IF TRUE
ELSE
foutje!=FALSE
ENDIF
device$=CHR$(x|+64)+":"
UNTIL @search_file(device$+DIR$(x|)+"\TEST.RSC"+CHR$(0))=-1
IF foutje!
paas$=empty$+CHR$(0)
ELSE
paas$=device$+DIR$(x|)+"\"+CHR$(0)
ENDIF
ENDIF
RETURN V:paas$
ENDFUNC
Some remarks to this piece of code: The DEFFN search_file
replaces the "GfA Basic" EXIST command which cannot be trusted
(and surely not when you're in an accessory).
The actual find_the_path function first determines whether
you've booted from hard disk or floppy. In the first case it will
not bother checking floppy because that would notably slow down
the path-finding process. Once it has determined that, it checks
all partitions attached (their current directories) to see if the
RSC file is present.
There is one 'but' with this: The resource file will have to be
in an ACTIVE directory, or in a ROOT directory. if you want to
get rid of this limit, you have to write a tree-search routine or
something, but I couldn't be buggered to do that.
Basically, if the resource file is not already in a root
directory, you should just open its folder and only then load the
accessory from within "Chameleon". Only if you open a folder will
it be present in the internal "GfA Basic" DIR$() array that
contains all active directories.
And that's all there is to this article. If you have any
suggestions on how to improve the techniques mentioned here,
please tell me about it so that I can build them into the next
version of my virus killer.
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.