Skip to main content

 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.