Skip to main content

 "I wasn't born Republican, Democrat, or yesterday."

                YOUR SECOND GFA BASIC 3.XX MANUAL
                             - or -
        HOW I LEARNED TO STOP WORRYING AND LOVE GFA-BASIC
                             PART 20
               CHAPTER NINETEEN - PROGRAM CONTROL
                          by Han Kempen

GOSUB

 A Procedure can be called in one of the following ways:

     GOSUB proc
     @proc               ! my favourite
     proc

 I  prefer  the @-method,  because this is spotted  easily  in  a
listing. Also, the same method can be used to call a function.

 Procedures  in which you use an 'OK-flag' (to confirm that  some
operation was successful) can be substituted by Functions:

     @proc(1,flag!,x)
     IF flag!
       ' do this
     ELSE
       ' do that
     ENDIF
     '
     PROCEDURE proc(a,VAR ok!,b)
       IF a=1
         b=10
         ok!=TRUE
       ELSE
         b=0
         ok!=FALSE
       ENDIF
     RETURN

 Using a Function this becomes:

     IF @func(1,x)
       ' do this
     ELSE
       ' do that
     ENDIF
     '
     FUNCTION func(a,VAR b)
       IF a=1
         b=10
         RETURN TRUE
       ELSE
         b=0
         RETURN FALSE
       ENDIF
     ENDFUNC

LOCAL

 As you know,  I am a strong advocate of declaring variables in a
Procedure  as  LOCAL  whenever  possible.   In  GFA-Basic   local
variables are not as local as they should be:

     global.x=5
     PRINT "main program: ";
     @print
     @proc_1
     PRINT "main program: ";
     @print
     '
     PROCEDURE print
       PRINT "global.x=";global.x;" local.x1=";local.x1;
       PRINT " local.x2=";local.x2
     RETURN
     '
     PROCEDURE proc_1
       LOCAL local.x1
       local.x1=10
       PRINT "in Proc_1: ";
       @print
       @proc_2                     ! change: @proc_2(local.x1)
       PRINT "in Proc_1: ";
       @print
     RETURN
     '
     PROCEDURE proc_2              ! change: proc_2(VAR local.x1)
       LOCAL local.x2
       local.x2=20
       PRINT "in Proc_2: ";
       @print
     RETURN

 In  Proc_2  you can use local.x1,  although it was  declared  as
local in Proc_1.  Or in general:  a local variable in Procedure P
is  seen  as  'semi-global' by Procedures you  call  from  within
Procedure  P.  I advise you to call variables by reference  (with
VAR) if you need them. Make the indicated changes in the previous
listing  and run again.  Everything is exactly the same,  so  why
bother?  Because  in most languages 'local in P' means local  for
that Procedure only,  so you can't use a local variable as 'semi-
global'  in Procedures you call from Procedure P.  E.g.  in  GFA-
Basic  for MS-DOS,  local.x1 is printed as '0' in Proc_2  in  the
first listing,  but as '10' in the changed listing. If you really
need  variable  local.x1  in  Proc_2,   you  should  call  it  by
reference.  Try  to remember this now and future  conversions  of
your  GFA-Basic  programs  to other  languages  and/or  computers
should become easier.

ON BREAK GOSUB

 In a Break-Procedure (activated with ON BREAK GOSUB) you  should
use  ON BREAK CONT to prevent calling the Break-Procedure  twice.
Nobody  can release the <Control> <Shift> <Alternate>  keys  fast
enough:

     ON BREAK GOSUB break
     (...)
     PROCEDURE break
       ON BREAK CONT              ! switch Break off immediately
       (...)                      ! do something
       ON BREAK GOSUB break       ! activate Break again
     RETURN

ON ERROR GOSUB

 It  should  be  possible to use ON ERROR  GOSUB  in  a  compiled
program:

     $U+
     ON ERROR GOSUB error_procedure
     (...)
     $U-
     PROCEDURE error_procedure
       ' This Procedure must be located outside the $U+/$U- block
     RETURN

ERROR

 You can simulate ERRORs with values from -127 to 127.  For  GFA-
errors use values from 0 to 93,  for bomb-errors 102 (2 bombs) to
109 (9 bombs) and for TOS-errors -1 to -67.

EVERY and AFTER

 It's  not  possible  to use EVERY and AFTER  at  the  same  time
because you can use only one interrupt-routine in GFA-Basic.  You
can only call Procedures without parameters.  Both commands don't
work  during a long PAUSE (or any other command that takes a  lot
of  time).   Waiting  for  any  keypress  with  ~INP(2)  is  also
impossible, you have to use:

     REPEAT
     UNTIL LEN(INKEY$)

 Although one tick for EVERY/AFTER is 1/200th second,  you should
use multiples of 4 only.  The operating system can't digest steps
smaller than 1/50th (4/200th) second.

 Don't  make the Procedure too long,  or it may be  called  while
being processed!  You could start the Procedure with 'EVERY STOP'
and end with 'EVERY CONT',  but you still need a short  Procedure
or  it will be called again immediately,  and  again,  and  again
while your main program stops completely.

 In a compiled program you have to incorporate '$I+ U+', or EVERY
and AFTER can't be used.

REM

 Do use comments in your programs,  the more the better. Yes, the
program  will  become  longer,  but  it's  nice  to  be  able  to
understand  a  well-documented  program that  you've  never  seen
before.  Or one of your own masterpieces that you haven't  looked
at  for a couple of years.  Don't worry about the speed  of  your
program,  except  in loops or often called  Procedures/Functions.
There  a  comment-line (beginning with REM or ')  will  slow  the
interpreter  down.  A comment after '!' has no influence  on  the
speed of a program, so you can use these everywhere.

GOTO

 You can't use GOTO in a Procedure,  a Function or a FOR ... NEXT
loop.  Some people think you can't use GOTO in other cases either
because it's almost unethical to jump around in your program with
GOTO.  I don't mind as long as you know what you're doing.  It is
possible  to  write a well-structured program with  a  couple  of
GOTO's.  It's also possible to write a spaghetti-program  without
any  GOTO.  No,  this time I'm not interested in your opinion  at
all. GOTO DELAY-bug.

DELAY-bug

 The  DELAY-command does not operate correctly.  During  DELAY  a
Break  is  impossible.  A nastier bug is the  appearance  of  the
mouse-cursor during DELAY,  even after HIDEM.  You are advised to
use PAUSE instead:

     DELAY seconds                 ! buggy
     PAUSE seconds*50              ! use this

STOP

 After STOP the GFA-editor does not close channels (open  files),
so  you  are able to continue the  interrupted  GFA-program  with
CONT. Be careful not to do anything stupid in Direct Mode such as
switching  your  ST off.  Use the CLOSE-command  in  Direct  Mode
before you do anything that could be dangerous for open files.

CHAIN

 You  can  CHAIN both GFA-programs (using  the  interpreter)  and
compiled programs (from a compiled program):

     CHAIN "TEST.GFA"    ! only possible in interpreted program
     CHAIN "TEST.PRG"    ! only possible in compiled program

 In  a GFA-Basic program all variables and arrays are lost  after
CHAINing the next program.  However,  you could use the  160-byte
buffer  of the scrap-library to pass a short message to the  next
program:

     buffer$=SPACE$(160)           ! 160 bytes maximum ?
     message$="this message was sent by the previous program"+
                                                          CHR$(0)
     LSET buffer$=message$
     r%=SCRP_WRITE(buffer$)        ! r%=0 if error
     CHAIN file$

 Read the message with:

     buffer$=SPACE$(160)
     r%=SCRP_READ(buffer$)
     message$=CHAR{V:buffer$}
     PRINT message$

 The use of this buffer is completely illegal,  but who cares  if
you don't use a scrap-library? Unfortunately the GFA-editor seems
to  think  so  too,  so you should  experiment  a  little  before
trusting this method.

CALL

 If  you load an assembly-routine from disk you can skip the  28-
byte header of the program as follows:

     OPEN "I",#1,file$
     length%=LOF(#1)-28
     DIM buffer|(length%-1)
     adr%=V:buffer|(0)
     SEEK #1,28
     BGET #1,adr%,length%
     CLOSE #1
     CALL adr%()              ! or ~C:adr%()

 With  an assembly-routine in an INLINE-line you'll get  problems
after   compiling  unless  the  registers  a3,a4,a5,a6  and   the
stackpointer a7 are restored by the routine. Use '$C+' to restore
all registers if you don't know whether the routine restores  the
mentioned registers.

EXEC

 If you are going to run another program (*.PRG) more than  once,
you'll have to use EXEC 3:

     base%=EXEC(3,file$,"","")       ! load, but don't start yet
     base$=STR$(base%)
     cmdl$=CHR$(LEN(base$)+1)+base$  ! create command line
     (...)
     r%=EXEC(4,"",cmdl$,"")          ! now run it

 The variable r% contains a value returned by the program (or -39
if not enough memory was available).  Repeat the last line if you
want  to run the loaded program again.  Of course you should  use
EXEC 0 if you're going to run the program one time only. Read the
paragraph  'RESERVE' in chapter 4 (page 4-2) if you are going  to
use  EXEC  3.  In GFA 3.5 the function EXEC 3 doesn't  return  an
address in base% (GFA-bug).

 Instead of EXEC 4 you can also use:

     prg%=base%+256                ! skip the Basepage
     ~C:prg%([parameter-list])     ! now run it

 If  you call a '*.PRG'-program with EXEC 0,  you pass the  null-
string ("") as the command-line.  You need the command-line  only
if you call a '*.TTP'-program.  The command-line is converted  to
upper  case  and can't exceed 125 bytes.  The first byte  of  the
command-line (usually) determines the length of the line,  so the
command-line can't contain more than 124 characters.  You can use
this  in  a TTP-program (compiled  GFA-Basic  program,  extension
changed to TTP) to read the command-line:

     adr%=BASEPAGE+&H80
     last={adr%}
     FOR i=1 to last
       cmdl$=cmdl$+CHR$({adr%+i})
     NEXT i

 It's easier to read the command-line with:

     cmdl$=CHAR{BASEPAGE+&H81}

 You  can use the described method also to examine if a  compiled
GFA-program  (*.PRG)  was started directly from  the  desktop  or
indirectly  through  an  installed  application  (see  also   the
paragraph 'Application',  page 1-2).  If the user  double-clicked
the  program,  the  command-line is empty.  If the  user  double-
clicked  an  application for the program,  the  filename  of  the
application  can  be  found  in  the  command-line.  Examine  the
command-line early:  after DIR or FSFIRST/FSNEXT the command-line
is overwritten by the DTA-buffer.

 If you would like to run a GFA-program from a compiled  program,
you can tell the interpreter which file (*.GFA) to load for you:

     r%=EXEC(0,"\GFABASIC.PRG"," - "+file$,"")

 You'll have to start the GFA-program in the usual way after  the
GFA-editor has appeared.

                     Procedures (CHAPTER.19)

Execute_prg                                               EXECUTE
 Run  another  program (after reserving enough  memory  for  that
program):
     @execute_prg(program$,50000,"",ok!,r%)
     ' back to this GFA-Basic program
     IF ok!
       PRINT r%
     ENDIF
 After  reserving  50000  bytes,   the  program  is  started.  If
everything  went all right,  ok!=TRUE and r% contains  the  value
returned by the program.

Scrap_read and Scrap_write                    SCRPREAD & SCRPWRIT
 Use the Scrap-library from a GFA-Basic program:
     ' in one program do this:
     @scrap_write("Greetings from one program",ok!)
     ' in another program do this:
     @scrap_read(t$,ok!)
     IF ok!
       PRINT t$
     ENDIF 

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.