"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.