**************************************************************************
*              Amiga.lib - How to use it with Assembly Code
*                          By Steve Marshall
**************************************************************************

   Quite a while ago I  promised  Mark  that  I  would  send  an article on
amiga.lib and how to use it with  assembly  language. Well, I've eventually
got around to putting finger to  keyboard.  I hope that this article proves
to be of some use. I think  this  is  about  the  most  complete  source of
information an amiga.lib around - well I hope it is 

What is Amiga.lib ?
~~~~~~~~~~~~~~~~~~~
   Amiga.lib is a linker library which contains some exec support routines,
a few number coversions, some output routines  and all the library offsets.
It is  a  good  idea  to  think  twice  before  using  the  floating  point
conversions. You may want floating point so that you can use libraries like
the mathtrans.library. Beware  these  are  quite  slow and it may be a good
idea to stick with integer math. For  instance,  at  first it may seem that
you would need mathtrans for 3d  rotations,  however, it is quite possible,
and preferable to stick  with  integers.  You  need to use a few tricks but
these are fairly standard  programming  techniques. But I digress - perhaps
more on this some other  time.  Amiga.lib  can  not  be obtained through PD
libraries, but if you have  Devpac  V2  you  will  find it in the libraries
directory of disk 2 (DevpacAm2:). 
In order to use amiga.lib you will  also  require  a copy of Blink. This is
also on disk 2 and can also be found on previous ACC disks or in the public
domain.

What Routines are in Amiga.lib
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   There are a number of routines  in  amiga.lib,  some  are useful for the
assembly language programmer  and  some  that  are only really useful for C
programmers. These routines are (in alphabetical order) :

   AddTOF
   afp
   arnd
   BeginIO
   CreateExtIO
   CreatePort
   CreateTask
   dbf
   DeleteExtIO
   DeletePort
   DeleteTask
   FastRand
   fpa
   fclose
   fgetc   
   fpbcd
   fputc
   fputs
   getchar
   NewList
   printf
   putchar
   puts
   RangeRand
   RemTOF
   sprintf
   stdio

How do I use these routines
~~~~~~~~~~~~~~~~~~~~~~~~~~~
   To use the routines in amiga.lib we must remember a few things.

   1)  Most C compilers add an  underscore to the beginning of the routines
names  so  we  must  do  the    same   for  the   name  to  be  recognized.
Amiga.lib was intended for use with C.

   2)  We must also tell the assembler that  these routines are external to                       
      the program being compiled. We do this by adding the line :
      
      xref    _CreatePort

      or something similar.

   3)  The program  being  compiled must be  linkable  and not  executable.

   4)  All  parameters  are passed  to the  routines on the  stack  and not                                                  
       through registers.  The results  are always  passed back in register                              
       d0 or  d0 and d1.  It is left to you to  remove anything pushed onto 
       the stack.

   5)  Some routines need to access variables from your code. Routines like  
       printf, fprintf, CreatePort,  DeletePort,  CreateExtIO,  DeleteExtIO
etc. will need these variables. The variables are:

       _SysBase, _DOSBase, _stdout, _stdin.

       You tell the linker about these with xdef ie.

       xdef    _SysBase

      _SysBase should contain the exec libs base address, that is the value 
       that is found at the long word starting at $00000004.
 
   6)  We must use Alink  or better still  Blink to  link the program using                      
       amiga.lib as the linker library, more on this later.

   We will now take a closer look at the routines found in amiga.lib.

AddTOF
~~~~~~
   Add a task to the Top Of Frame (Vertical  Blank) interrupt server chain.
This routine is intended for  C  programmers  and  it is just as easy to do
this for yourself in assembler. The format for this routine is :

   AddTOF (IntServer,Task,Data)

   Where IntServer is a pointer to an  Interrupt  server structure, Task is
the routine to be called by the interrupt and Data is a pointer to an array 
of long words used to pass arguments to the interrupt routine.
  The arguments are pushed onto the stack  in reverse order so in assembler
this would become :

   xref   _AddTOF  
   pea    Data
   pea    Task
   pea    IntServer
   jsr    _AddTOF
   lea    12(sp),sp

   We would use pea to push the addresses  onto the stack as Data, Task and
IntServer would all be labels within our code.

afp
~~~
   This converts an ASCII numerical string to a fast floating point number.  
The results are supposed to be returned  in both d0 and d1, however, I have
yet to get any result in d1. I  have  tried  all  types of string, some too
large, some  with  illegal  characters  in  them,  but  nothing seems to be
returned in d1. The Motorola Fast  Floating  Point (FFP) number is returned
in d0. afp takes the form :

   FFPNum=afp (FFPString)

 or in assembler
 
   xref   _afp
   pea    FFPString
   jsr    _afp
   addq.l  #4,sp
   move.l  FFPNum
 
arnd
~~~~
   arnd rounds a FFP number contained  in  a an ASCII string. The format of
arnd is :

   arnd (Place,Exp,FFPString)

 Where Place is the  number  of  decimal  places  to  round  to, Exp is the
exponent value of  the  string  and  FFPString  is  a  pointer  to an ASCII
numerical string. The  result  is  placed  back  into  the  same string. In
assembler this would be :

   xref   _arnd
   pea    FFPString
   move.l #Exp,-(sp)
   move.l #Place,-(sp)
   jsr    _arnd
   lea    16(sp),sp

BeginIO
~~~~~~~
   Once again this is mainly  of  use  to  the  C  programmer. The assembly
programmer would be better off using  the  macro BEGINIO which can be found
in the file 'exec/io.i'.
  BeginIO takes the form :

  BeginIO (IORequest)

  Where IORequest is the IO request that you wish to initiate. In assembler  
this would be :

   xref    _BeginIO
   move.l  IORequest,-(sp)
   jsr     _BeginIO
   addq.l  #4,sp

   Here we use move.l to push  the  parameter  onto  the  stack. The reason
being that IORequest  would  usually  be  a  variable  in  our code. The IO
request would usually be created  within  our  code and it's address stored
here.

CreateExtIO
~~~~~~~~~~~
   This routine is of some use to the  assembly  programmer.  It creates an
IORequest structure like the one mentioned in the BeginIO.
   CreateExtIO is called as shown below :

   IORequest=CreateExtIO (ReplyPort,Size)

   Where ReplyPort is a pointer to a  message  port and Size is the size of
the IORequest block. In assembler this would be :

   xref    _CreateExtIO
   move.l  #Size,-(sp)
   move.l  ReplyPort,-(sp)
   jsr     _CreateExtIO
   addq.l  #8,sp
   move.l  d0,IORequest
   beq     NoIORequest

CreatePort
~~~~~~~~~~
   This is another useful routine for the assembly programmer, it creates a 
message port like the one used in CreateExtIO. It is used as shown below :

   ReplyPort=CreatePort (Name,Pri)

   Where Name is a pointer to the name to  be used for this port and Pri is
the priority that this port is to be given. If you pass a null instead of a 
pointer to the name string,  the  port  will  be created as a private port.
That means that it will not be added  to  the  system list -(using AddPort)
but will be private to our own task.  No  other task should be able to mess
with it. In assembler this would
look something like :

   xref    _CreatePort
   move.l  #Pri,-(sp)
   pea     Name
   jsr     _CreatePort
   addq.l  #8,sp
   move.l  d0,ReplyPort
   beq     NoPort

CreateTask
~~~~~~~~~~
   This is of limited use. CreateTask creates  an Exec task. Exec tasks may
not call DOS either directly or indirectly.  This means that it is not safe
to open libraries or fonts etc. Even if the library or font is currently in 
rom, it is possible  that  future  versions  of  KickStart  may  have these
removed thereby breaking your code.  If  you  don't need to access DOS then
this may prove useful having a lower  overhead  than CreateProc -(which you
should use if you need DOS). It is 
alled as shown below :

   Task=CreateTask (Name,Pri,InitPC,StackSize)

   Where Nane is a pointer to the tasks  name,  Pri  is the priority of the
created task -(usually zero, careful  if  you  use higher priorities as you
may  lock the system  up),  InitPC  is  the  starting  value of the program
counter -(the start of the tasks cod
) and StackSize is the tasks stack size. A common value for StackSize would
be 4000 -(same as CLI default). In assembler this would look something like
:

   xref    _CreateTask
   move.l  #4000,-(sp)
   pea     InitPC
   moveq   #0,d0
   move.l  d0,-(sp)
   pea     Name
   jsr     _CreateTask
   lea     16(sp),sp
   move.l  d0,Task
   beq     NoTask

DeleteExtIO
~~~~~~~~~~~
   This is the opposite  of  CreateExtIO,  that  is it de-allocates all the
memory allocated by CreateExtIO. It takes the form :

   DeleteExtIO (IORequest)

   Where IORequest is a pointer to the IORequest  block to be de-allocated.
In assembler this would be :

   xref    _DeleteExtIO
   move.l  IORequest,-(sp)
   jsr     _DeleteExtIO
   addq.l  #4,sp
 
DeletePort
~~~~~~~~~~
   This is the opposite of  CreatePort,  that  is  it  de-allocates all the
memory allocated by CreatePort. It takes the form :

   DeletePort (ReplyPort)

   Where IORequest is a pointer to the IORequest  block to be de-allocated.
In assembler this would be :

   xref    _DeletePort
   move.l  ReplyPort,-(sp)
   jsr     _DeletePort
   addq.l  #4,sp
 
DeleteTask
~~~~~~~~~~
   This routine is used to delete a task created with CreateTask. Note that 
it isn't safe to simply  call  DeleteTask  from  the  parent program. It is
safer for a child task to DeleteTask itself. If the parent task must delete
the child task, then you must make sure  that it is not currently executing
any system code -(like using 
 device) that may try to signal the task after  it has been deleted. Delete
task may be called as shown below :

   DeleteTask (Task)

   Where Task is a pointer to the  Task  to  be  deleted. In assembler this
would be. 

   xref    _DeleteTask
   move.l  Task,-(sp)
   jsr     _DeleteTask
   addq.l  #4,sp
 
dbf
~~~
   Converts a dual binary format floating  point number into an FFP number.
It takes the form :

   FFPNum=dbf (Exp,Mant)

  Where Exp is the exponent part of  the  number.  This is 16 bits long and
takes the form :

   Bit 16       = The sign Bit of the exponent
   Bits 15 - 0  = The base 10 exponent number

  Mant is the mantissa -(the number itself). In assembler this would be :

   xref    _dbf
   move.l  #Mant,-(sp) 
   move.l  #Exp,-(sp)
   jsr     _dbf
   addq.l  #8,sp
   move.l  d0,FFPNum

FastRand
~~~~~~~~
   This can be used to quickly generate a random number, however we do need 
a random number to seed it. It is called as shown below :

   Number=FastRand (Seed)

   Where Seed is a random number which  is  used  to seed the random number
generator. Once we have our first  random  number we can use this to reseed
FastRand for the next number and so  on.  As a random number generator this
is pretty crude. In assembler
this would be :

   xref    _FastRand
   move.l  #Seed,-(sp)
   jsr     _FastRand
   addq.l  #4,sp
   move.l  d0,Number

fclose
~~~~~~
   Closes a file which was opened with DOS Open(). It's format is :

   Char=fclose (FileHandle)

  and in assembler

   xref   _fclose
   move.l FileHandle,-(sp)
   jsr    _fclose
   addq.l #4,sp

fgetc
~~~~~
   Gets a single character from a file. It is called as shown below :

   xref   _fgetc
   move.l FileHandle,-(sp)
   jsr    _fgetc
   addq.l #4,sp
   move.b d0,Char 

fpa
~~~
   Converts a FFP number into an  ASCII  string  -(opposite  of afp). It is
called as shown below :

   Exp=fpa (FFPNum,String)

  Where FFPNum is a FFP number and string is the buffer where the string is 
to be created. The string buffer  should  be 16 bytes long. Exp is the base
10 exponent of the number which is returned  in d0. In assembler this would
be.

   xref    _fpa
   pea     String
   move.l  #FFPNum,-(sp)
   jsr     _fpa
   addq.l  #8,sp
   move.l  d0,Exp

fpbcd
~~~~~
   This takes a FFP number and converts it to a BCD -(Binary Coded Decimal)
number. It takes the form :
 
   fpbcd (FFPNum,String)

  Where FFPNum is a FFP number and String is the buffer where the result is 
to be stored. The buffer should be 8 bytes long. The result takes the form:

  Mantissa  = 4 Bytes each byte containing two BCD digits.
  Sign      = 1 Byte containing the sign of the mantissa $00 = positive and
$ff = negative.
  Exponent  = 1 Byte containing two BCD digits describing the exponent.
  Sign      = 1 Byte containing the sign of the exponent.
  Exponent2 = 1 Byte containing the two's complement  representation of the
exponent.

  In assembler this would be :

   xref    _fpbcd
   pea     String
   move.l  #FFPNum,-(sp)
   jsr     _fpbcd
   addq.l  #8,sp

   Note: This according to Includes and AutoDocs should be correct, however 
I have yet to get it to  work  correctly.  It  seems  to corrupt the stack.
Perhaps it should be an IEEE floating point  number and not a Fast Floating
point number. I'll look into this  further.  You  will probably have little
use for this anyway.
 
fprintf
~~~~~~~
   Works just like printf except  that  you  provide  the FileHandle of the
file that the text is to be output. If you were to pass _stdout as the file 
handle then the result would be  exactly  the same as printf. The format of
fprintf is :

   Count=fprintf (FileHandle,String,Values....)

 Where FileHandle is the handle of the output  file, String is a pointer to
the format string and values is any number of values pushed onto the stack.
These values will be used in the format string. Remember to push the values
on in reverse order. In assembler this would be :

   xref   _fprintf
   move.l  #$0f0ff084,d0
   move.l  d0,-(sp)
   move.l  d0,-(sp)
   pea     String
   move.l  FileHandle,-(sp)
   jsr     _printf
   lea     16(sp),sp
   rts

String:
   dc.b   'Number in Hex is %lx and in Decimal is %ld',0

NOTE:
 The version of fprintf and printf in aniga.lib does not appear to support
 the escape codes, ie  \n for newline. You will have to code the values in
 for yourselves. For example  'This is a test',$0a,0 would do the same job
 as 'This is a test\n',0. 

fputc
~~~~~
   Outputs a single character to a file. The format for this routine is :

   Error=fputc(Char,FileHandle)

  Where Char is the character to  be  output  and  FileHandle is the output
files handle. In assembler this would be :

   xref   _fputc
   move.l FileHandle,-(sp)
   move.l Char,-(sp)
   jsr    _fputc
   addq.l #8,sp
   cmpi.l #-1,d0
   beq    WriteError                            

fputs
~~~~~
   Outputs a C type (Null  terminated)  string  to  a  file. The format for
fputs is :

   Error=fputs(String,FileHandle)

  Where String is the C string to  be output  and  FileHandle is the output
files handle. In assembler this would be :

   xref   _fputs
   pea    String
   move.l FileHandle,-(sp) 
   jsr    _fputs
   addq.l #8,sp
   cmpi.l #-1,d0
   beq    WriteError 

getchar
~~~~~~~
   Gets a character from _stdin. This can be used to read the keyboard. The                 
format of getchar is :

   Char=getchar ()

  and in assembler

   xref   _getchar
   xdef   _stdin
   xdef   _DOSBase
   jsr    _getchar
   move.l d0,Char   ;or move.b d0,Char

NewList
~~~~~~~
   Prepares a linked list for use. Assembly  programmers would be better of
using the NEWLIST macro in 'exec/lists.i'. NewList is used as shown below :

   NewList (List)

  Where list is a pointer to the node  of  a list header. In assembler this
would be :

   xref    _NewList
   pea     List
   jsr     _NewList
   addq.l  #4,sp
 
printf
~~~~~~
   Prints out a formatted string. For the  options see my printf routine in
a previous ACC disk or refer to  RawDoFmt()  in  the Includes and AutoDocs.
printf takes the form :

   Count=printf (String,Values....)

  Where String is a  format  string  and  Values  are  any number of values
pushed onto the stack. Note that there is no fixed number for the number of  
values. Count is the number of chars output  or -1 in the case of an error.
An example of printf in assembler may be :

   xref    _printf
   xdef   _DOSBase
   xdef   _stdout

   move.l  #$0f0ff084,d0
   move.l  d0,-(sp)
   move.l  d0,-(sp)
   pea     String
   jsr     _printf
   lea     12(sp),sp
   rts

String:
   dc.b   'Number in Hex is %lx and in Decimal is %ld',0

NOTE:
   In order to use printf you  must  have  initialized  the global variable
_stdout. This is done as shown below.

   CALLDOS   Output
   move.l    d0,_stdout

  If Output returns null then there is  no  standard output file handle. In
this case you should not call  printf.  You  could store another filehandle
there -(like a console filehandle) to redirect output from printf there. 
You will also have to open the DOS lib  and store it's base in _DOSBase. In
order for the linker to find these variables you must add the lines :

   xdef   _DOSBase
   xdef   _stdout

Last point is that unfortunately the  version  of  printf in amiga.lib does
not handle floating point numbers.

putchar
~~~~~~~
   Outputs a single character to _stdout. The format of putchar is :

   putchar(Char)

  Where Char is the character to be output. In assembler this would be

   xref   _putchar
   xdef   _DOSBase
   xdef   _stdout

   move.l Char,-(sp)     ;or perhaps move.l #'A',-(sp)
   jsr    _putchar
   addq.l #4,sp

puts
~~~
   Outputs a C type string to _stdout.  puts  does no formatting and always
adds a newline to the end of the string. The format of puts is :

   puts (String)

  Where String is a pointer to the  string  to be output. In assembler this
would be 

   xref   _puts
   xdef   _DOSBase
   xdef   _stdout

   pea    String
   jsr    _puts
   addq.l #4,sp
 
String:
   dc.b   'Testing the puts routine',0

RangeRand
~~~~~~~~~
   Generates a random number which  ranges  from zero to the integer number
passed as an argument. The format of RangeRand is:

   rnd=RangeRand (Range)

  Where range is the highest integer  acceptable.  In  assembler this would
be :  

   xref    _RangeRand
   move.l  #Range,-(sp)
   jsr     _RangeRand
   addq.l  #4,sp
   move.l  d0,rnd
   
RemTOF
~~~~~~
   Removes a task from the Top Of Frame  -(VBlank)  interrupt. This is used
to 
clean up after AddTOP and effectively undoes  what AddTOF did. It is called
as shown below :

   RemTOF (IntServer)

  Where IntServer is the  interrupt  server  structure  that  was passed to
AddTOF. In assembler this would be :

   xref    _RemTOF
   pea     IntServer
   jsr     _RemTOF
   addq.l  #4,sp

sprintf
~~~~~~~
   outputs a formatted string into a  destination  string.  Works just like
printf except that the destination is a string in memory. It's format is :

   sprintf (DestString,FmtString,Values....)

  Where DestString is a string buffer which  will hold the result. It is up
to you to make sure that the  buffer  is  large  enough to hold the result.
FmtString is a format string just like the one used with printf. The Values
are any number of values pu
hed onto the stack. Note that there  is  no  fixed number for the number of
values. sprintf is very  useful  for  converting  numbers  into  ASCII.  An
example of printf in assembler may be :

   xref    _sprintf
   move.l  #$0f0ff084,d0
   move.l  d0,-(sp)
   move.l  d0,-(sp)
   pea     FmtString
   pea     DestString
   jsr     _sprintf
   lea     16(sp),sp
   rts

FmtString:
   dc.b    'Number in Hex is %lx and in Decimal is %ld',0

DestString:
   dcb.b   54,0

Assembling and Linking
~~~~~~~~~~~~~~~~~~~~~~
   You assemble in the normal way,  except  that you must assemble linkable
code. The demo code should  be  assembled  to  ram:LibTest.o. The .o is the
standard post-fix for linkable files.
   To link the file you will need  a  copy  of  BLink and Amiga.lib. If you
have Devpac 2 you will find these on disk  2. You would link with something
like.

   DevpacAm2:c/Blink RAM:LibTest.o LIBRARY DevpacAm2:Library/amiga.lib

 Alternatively you could use the  supplied  'WITH' file. These usually have
the post-fix .lnk. Try the following command from CLI.

   DevpacAm2:c/BLink WITH DF0:Source/LibTest.lnk

  WITH files are made up as shown below :

  FROM     Files      ;the files to link
  TO       File       ;the destination file (executable)
  LIBRARY  Files      ;the names of the linker libraries (like amiga.lib)
  MAP      File       ;the name of the file used output the link map
  XREF     File       ;the destination of the cross referenced output
  OVERLAY  Files
           Files
           Etc.
           #          ;end of overlay

  The overlay is a fairly complex subject  and is used to allow the program
to use less memory when running. This is  achieved by only loading hunks as
they are needed. You  specify  how  the  hunks  should  be  loaded with the
overlay directive. The system  overlay  supervisor will take care of things
at run time. Perhaps  I  will  look  into  this  more  closely  in a future
article.

 The demo program supplied doesn't do anything  useful but just serves as a
demonstration of how easy it is to  use  amiga.lib.  Do not run the program
from the WorkBench - it has no  WBench  startup  and will just quit anyway.
The reason for this is that some of the routines (like printf) are meant to      
output to the calling console.

  I hope that this is  of  use  to  somebody.  I  may  write  a  version of
amiga.lib (for  the  ACC  assembler  project)  that  will  be  strictly for
assembler code. That is all parameter passing will be through the registers
and not the stack. The code would be  optimized  for size and speed. On the
other hand it may be more useful for Devpac (or other commercial Assemblers
(except Seka - yeuck!)) users  to  create  an  include file with macros for
each routine.
  Well - that's all from me for this month.  Next month (hopefully) I shall
be starting a  short  series  of  articles  which  will  cover  devices and
resources. See you then - I think!

  Steve Marshall 














































