ACE Conversations, Hints and Tips
__ __ __ __ __ _____
/ \ / \ / \ / \ | | / \ |
|__| | |_ | |__| |__| |
| | | | | | | | | |
| | \__/ \__/ \__/ | | | | |
(ACE Conversations, Hints, And Tips)
Introduction
~~~~~~~~~~~~
This file is an attempt by myself and David Benn to gather together
in one place as much as possible of the interaction taking place between
ACE users. At the moment the format is very informal, but that may
change in the future. The various entries have been grouped together
under the broad headings of:
General Info (not directly related to code)
Help! (various Q&A's dealing with specific code problems)
Optimization Tips (David's long awaited optimization guide! :)
This format is very much subject to change, and it is the hope of
both David and myself that ACE users will send in many more entries. As
the file grows, the format will evolve.
Please send your submissions to this file to the ACE list, with a
subject containing 'ACE CHAT submission' or something similar.
Rich Allen
rico@wsnet.com
I'd just like to add a vote of thanks to Rich for putting this material
together from ACE list archives and some messages I sent him. Credit also
goes to him for dreaming up the name "ACHAT" :).
It was my initial suggestion that led to Rich starting work on an ACE FAQ,
but as he rightly pointed out to me recently, a FAQ is less appropriate
than what you now see before you.
ACHAT seems to be the natural place for my ACE source-level optimisation
guide, and I will add more to the Optimisation Tips section of this
document as time goes by.
Let me repeat Rich's exhortation for input from other ACE list members.
The problem with the list is that unless archived, we can lose the distilled
wisdom of its members. This project is an effort to prevent that.
Enjoy.
David Benn
D.Benn@appcomp.utas.edu.au
General Info
~~~~~~~~~~~~
1) Where can I get ACE?
2) Where are the FDs?
3) How can I print ace.doc easily?
1) Where can I get the latest version of ACE?
You can get it from:
Aminet sites in dev/basic
or
ftp.appcomp.utas.edu.au in /pub/ACE/dist
The latter is David Benn's local FTP site and has little traffic.
You might also want to try the ACE WWW page if you have access to a
web browser. The URL is:
http://www.appcomp.utas.edu.au/users/dbenn/
****
2) Where can I get the FDs for the most common libraries?
You can obtain the FDs by anonymous FTP from:
ftp.dfv.rwth-aachen.de (/cdrom/bbs/cbm)
You need the file:
nduk-vNN.lha
where NN is the version number of your Amiga's OS. Use the VERSION command
from the shell to determine this (eg. Wb 2.04 is version 37, Wb 3.0 is
version 39 and Wb 3.1 is version 40). This will give you includes, FDs etc.
Also (from Bill Maddock):
You can get the .fd files for versions 37, 38, 39 & 40 of the OS from the
fresh fish CD-ROM volume 7 (or someone who has it with THEIR cd-rom drive)
or you can get the version 34 .fd files off of a workbench extras 1.3 disk.
Not very adequate, I know, but if you're not doing version-specific stuff,
they should suffice.
****
3) How can I print ace.doc easily (page-wise).
Compile and run ACE:utils/pager/pager.b specifying ace.doc as the file to
be printed when prompted on the command line.
****
HELP! (Q&A's)
~~~~~~~~~~~~~
1) Array trouble
2) Default string size
3) Event handling
4) Modifying slider gadgets
5) SLEEP and INKEY$ from the CLI/SHELL
6) Reading special keys (eg. shift, ctrl, arrow keys, F1..F10)
7) Software reboot
8) Window titlebar
9) C structures vs ACE structures.
1) I DIMensioned a string array using the SIZE option. When the array is
filled with strings of the size I specified in the DIM statement, I get weird
results. Why aren't the array elements storing my strings?
It sounds as if the terminating null character is getting over-written.
Each element in a string array ends with ASCII 0. This means that you
must specifiy a SIZE value that is at least one greater than the maximum
string length you are storing in the array. For example, an array of
strings in which the maximum length of string will be eight must have a
SIZE parameter of at least NINE.
****
2) I have a small program that reads in a file and sends it to the printer
device. The program worked perfectly in AmigaBASIC but after compiling it
with ACE I get a visit from the GURU. Here's the code that is causing the
problem:
OPEN "O",2,"prt:"
OPEN "I",1,"app.form"
a$=INPUT$(LOF(1),1)
PRINT #2,a$
END
If the file you are opening is larger than 1024 bytes (the default string
size) then you are attempting to put more information than can fit into a$.
You may either set the string size of a$ to a larger value, eg.
STRING a$ SIZE 32768
to get a 32K string, or you can dynamically allocate it at run-time:
buffer = ALLOC(LOF(1)+1) '..file size + 1 for null-terminator
IF buffer = 0& THEN STOP
STRING a$ ADDRESS buffer
Is your file longer than 32K? If so, then that's the answer. As
documented in ref.doc under INPUT$, the buffer for INPUT$ is currently
32K. If you want to handle bigger files, allocate a buffer with ALLOC
and read the file with xRead, so the above code becomes:
DECLARE FUNCTION xRead& LIBRARY
DECLARE FUNCTION xWrite LIBRARY
OPEN "O",2,"prt:"
OPEN "I",1,"app.form"
ADDRESS buffer
buffer = ALLOC(LOF(1)) : IF buffer = 0& THEN cleanup
length = xRead(HANDLE(1),buffer,LOF(1))
IF length > 0 THEN xWrite(HANDLE(1),buffer,length)
cleanup:
CLOSE 1,2
END
****
3) What's the deal with changing the status of trapping for a particular
event?
In ACE, ON | STOP | OFF must be used AT THE PLACE IN THE CODE
WHERE THE EFFECT IS DESIRED. You have to think of these ON|OFF|STOP
commands as compiler directives.
This means that you can't expect to do the following:
GOSUB change_break_status
WHILE 1
PRINT "Gee I hope ctrl-c works here!"
WEND
change_break_status:
ON BREAK GOTO quit
BREAK ON
quit:
PRINT "***break!"
END
This won't work and your program will loop forever. The following _will_
work however:
ON BREAK GOTO quit
BREAK ON
WHILE 1
PRINT "Gee I hope ctrl-c works here!"
WEND
quit:
PRINT "***break!"
END
****
4) Is it possible to change the value in a slider gadget after it has
been created?
Yes, there is a way to do this. You can use GADGET MOD for this purpose
(I assume you are using ACE v2.3 or higher?).
The syntax is: GADGET MOD id,knob-position[,max-position]
So, one can say something like:
GADGET 1,ON,10,(50,50)-(100,60),POTX
GADGET MOD 1,5
which would create a horiz. slider and then change the knob position to 5.
As the syntax indicates one can also change the number of knob positions
(and hence the size of the knob).
See ref.doc for further details under GADGET MOD and also
ACE:prgs/GUI/ACEgadgets.b for an example of where this is used (click on
the "Move" button).
****
5) If I do not open a new window or screen from the ACE program, the
INKEY$ function seems not to work flawlessly for me. My mistake, but
I would like to learn why. I have set up a WHILE..WEND loop, like the
following:
WHILE lastkey$<>""
SLEEP
lastkey$=inkey$
WEND
IF lastkey$="A" THEN GOTO ENDPROGRAM
IF lastkey$="L" THEN '.... whatever follows
Well, I press "A" then I may type on infinitely until I do not press
return. Then the program finally stops, I get back to the SHELL/CLI
and the keyboard buffer empties into the CLI, so all but the first
character will be sent to the CLI which definitely replies with an
"Unknown command" message. Why is it? Shall I empty the keyboard buffer
before exiting a program? If so, how? Or what is the problem? I have
tried the same code but with opening and appropriately closing new windows
and screens for my ACE prog, and then the whole program runs just fine.
What am I doing wrong? Cannot the system use INKEY$ in a CLI?
It uses INKEY$ in a CLI okay, but it doesn't return after each call to
INKEY$ like it does in a window or screen. I _could_ have ACE put the CLI
into RAW mode each time it called INKEY$, but didn't. I have never been
certain that INKEY$ is very useful in a CLI. It just seems to me that the
CLI is more typically used for simpler I/O.
By the way, SLEEP has no effect when in a cli/shell.
****
6) How do I read such keys as F1..F10, the arrow keys etc?
A simple way to do this is to hit the hardware directly. Yes, I know that
this is generally considered to be a Bad Thing (tm), but it's a simple
method to use in a few cases. In general of course, you should either use
an intrinsic ACE command or function or use operating system services.
BFEC01 is the register which holds the special key values as follows:
shift left &H3F
shift right &H3D
control &H39
Alt &H37
Amiga left &H33
Amiga right &H31
The function keys (F1..F10) has values ranging from 77 to 95 (STEP 2 :).
Try other keys such as Del and Help also.
Use the following loop to help you explore the above keys (and others):
WHILE NOT MOUSE(0)
PRINT PEEK(&HBFEC01)
WEND
****
7) How do I exactly ColdReboot the Amiga from an ACE program? Where is
the ColdReboot function? How can I include it into ACE or a C program?
A snippet would be most appreciated.
Here's the ACE way:
LIBRARY "exec.library"
DECLARE FUNCTION ColdReboot() LIBRARY exec
ColdReboot
Note that "exec" at the end of the function declaration is optional but
will speed things up at compile time.
****
8) How do I get rid of the title bar at the top of a window I've opened?
I tried WINDOW 1,"",(x1,y1)-(x2,y2),32 but all I get is a blank bar.
Try this command instead:
WINDOW 1,,(x1,y1)-(x2,y2),32
An empty string is different than *no* string at all.
****
9) C structures vs ACE structures.
ACE supports the following struct member types: BYTE, SHORTINT, LONGINT,
ADDRESS, SINGLE, STRING, struct. See ace.doc, pages 21-23.
All numeric data types are signed in ACE and this goes for struct members
also, however, signed and unsigned integers occupy the same number of
bytes of storage (eg. ULONG = LONG = 4 bytes in C).
Here are some common C datatypes (at least, their equivalents as found on
the Amiga in exec/types.h etc) and their ACE counterparts:
C ACE
- ---
BYTE BYTE
UBYTE BYTE
SHORT SHORTINT
USHORT SHORTINT
WORD SHORTINT
UWORD SHORTINT
LONG LONGINT
ULONG LONGINT
STRPTR ADDRESS
APTR ADDRESS
BPTR ADDRESS
BSTR ADDRESS
Any pointer ADDRESS
Struct Name of struct (eg. see page 21 in ace.doc)
Char arrays STRING (eg. char x[105] = STRING x SIZE 105)
Other arrays STRING (eg. WORD y[10] = STRING y SIZE 10*SIZEOF(SHORTINT))
Any struct member or variable in C with a "*" as part of the declaration is
a pointer. Examples of C pointer variables are:
struct DateStamp *ds; [ in ACE: declare struct DateStamp *ds ]
and
char *buffer;
Since a pointer is in fact simply a variable which holds the address of
an object of some particular datatype (eg. int, struct DateStamp, char),
ACE represents pointers as generic ADDRESSes for intrinsic types (SHORTINT,
LONGINT, ADDRESS, SINGLE, STRING) and pointers to structures insofar as
variables are concerned. The only difference for struct members is that
if the member is a pointer to a structure, it must be represented as an
ADDRESS, and later assigned to a variable of the specific struct type, eg.
STRUCT DateStamp
LONGINT days
LONGINT secs
LONGINT ticks
END STRUCT
STRUCT myStruct
ADDRESS *ds '..a pointer to a DateStamp struct
.
.
END STRUCT
Then later:
DECLARE STRUCT myStruct *x
DECLARE STRUCT DateStamp *myDS
'..Allocate memory for structs.
x = ALLOC(SIZEOF(myStruct))
x->ds = ALLOC(SIZEOF(DateStamp))
'..Do some procesing.
myDS = x->ds
'..Do some more processing.
Note, that currently no support exists for array struct members except via
STRING. Better support may be added in a future release of ACE.
Note also that a string member's size MUST be specified unless you
specifically want such a member to have a size of 1024 bytes.
If it's been awhile since you've read ace.doc's section on structures
(pages 21 to 23) it would be worthwhile for you to re-read it keeping the
above information in mind.
****
Optimization and Efficiency Tips
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1) Integer/floating point optimizations
a) Integer vs single-precision loop variables
b) Single-precision vs long integer division
c) Integer multiplication/division by left/right bitwise shift
d) Short integer vs long integer multiplication
2) What is 'peephole' optimization
3) GOSUB vs CALL
4) System friendly (keypress) loops
1) Integer/floating point optimizations
a) Integer vs single-precision loop variables
When looping through an integer range, use an integer variable as the
counter. This is a great deal more efficient at run-time than using a
floating-point (SINGLE) variable. For example, run the following program:
SINGLE i '..declare i as a single-precision variable
SINGLE t1,t2
t1 = TIMER
FOR i=1 TO 100000:NEXT
t2 = TIMER
PRINT "Elapsed time:";t2-t1;"seconds."
Now change the first line of code above to:
SHORTINT i '..declare i as a short integer variable
then re-compile and run the program.
Do you notice any difference in elapsed time? I hope so. This is explained
by realising that floating-point arithmetic is more complex and therefore
less efficient than integer arithmetic.
b) Single-precision vs long integer division
The following code performs 10,000 single-precision floating-point
divisions. Elapsed time was 2.484375 seconds on my unaccelerated A1200.
LONGINT a,b,x
a = 123456789
b = 87654
t0 = TIMER
FOR i% = 1 TO 10000
x = a / b
NEXT
t1 = TIMER
PRINT "Elapsed time:";t1-t0;"seconds."
Replacing the above division with:
x = a \ b
resulted in an elapsed time of 0.835937 seconds on my A1200.
The time difference results from the inefficiency of floating-point
division.
c) Integer multiplication or division by left or right bitwise shift.
The following code performs 10,000 long integer multiplications.
Elapsed time was 0.41992189 seconds on my A1200.
LONGINT n,m
m = 1234
t0 = TIMER
FOR i% = 1 TO 10000
n = m * 8
NEXT
t1 = TIMER
PRINT "Elapsed time:";t1-t0;"seconds."
Replacing the multiplication line with:
n = SHL(m,3)
resulted in an elapsed time of 0.14013672 seconds on my A1200.
A similar outcome was observed for integer division vs right bit shift.
d) Short integer vs long integer multiplication
Consider again the code from part c) but with the two variables (n and m)
being short integers instead of long integers.
SHORTINT n,m
m = 1234
t0 = TIMER
FOR i% = 1 TO 10000
n = m * 8
NEXT
t1 = TIMER
PRINT "Elapsed time:";t1-t0;"seconds."
Elapsed time was 0.12011719 seconds on my A1200, marginally better than
using SHL. Of course, use of short integers is only suitable when you
are using integers in the range -32768..32767.
Note that while ACE supports long and short integer multiplication, it
only supports long integer division (and of course single-precision
multiplication and division). So, short integers are coerced to long
integers prior to an integer division being carried out. See page 10
of ace.doc for more about this.
****
2) What is 'peephole' optimization?
When ACE first generates 68000 code, it contains redundancies. The goal
of peephole optimisation is to remove the chaff, making the code more
efficient (and readable). There are other forms of optimisation, but ACE
doesn't yet support them. Peephole optimsation can lead to big
improvements in execution speed.
Basically, it works by looking at pairs of assembly code instructions
and trying to either reduce them to a single instruction or to remove
them altogether.
****
3) Which is more efficient, GOSUB or CALL?
GOSUB involves less overhead than CALL (or implicit CALL to a subprogram)
since something like:
GOSUB mylabel
in ACE, translates to:
jsr MYLABEL
in assembly. When you call a subprogram with no parameters, there is
a little bit more overhead (eg. setting up for local/shared variables), but
not much more. When passing parameters, the overhead increases, albeit not
dramatically. Of course, if you are calling a SUB in a tight loop, there
will be some kind of performance hit. I don't have any hard figures for
you, but in short, I don't believe the overhead to be HUGE.
****
4) What would be the most efficient way to get a key from the user while
waiting for mouse actions and stuff? Basically, I'm wanting something
like a$=inkey$, but in a way that isn't as harsh on other tasks.
Try the following bit of code.
WHILE NOT finished
SLEEP '..Wait for an intuition event
'..(intuiticks occur once every tenth
'..of a second so the next line gets
'..a chance often but without unacceptable
'..efficiency loss).
k$ = INKEY$ : IF k$<>"" THEN finished = -1
WEND
As an alternative to SLEEP, you should check out SLEEP FOR in
the ACE command and function reference.