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.