;-------------------------------------------------------------------------- 
; Copyright (c) 1989,1990,1991,1992 by SAS Institute Inc., Cary NC
;
; Name       : avg2.a
; Author     : Michael S. Whitcher
; Date       : 02June92
; Description:
; Buildline  : asm avg2.a
;
;  Example assembler routine to demonstrate how 
;
;  -- to set up parameters being called from a 
;     C routine using stack and register passing conventions
;  -- to call a link library function using registerized parameters
;                     
;-------------------------------------------------------------------------- 

   INCLUDE "exec/macros.i"
   INCLUDE "exec/libraries.i"
   INCLUDE "dos/dos_lib.i"

;-------------------------------------------------------------------------- 
;  These are the entrypoints defined in this module.
   xdef _avg
   xdef @avg
   xdef _prnts

;-------------------------------------------------------------------------- 
;  External routines
   xref @__stcd_l
  
;-------------------------------------------------------------------------- 
;  External data that I reference
;  This is actually defined in the startup code.
   xref _DOSBase



   section text,code

;-------------------------------------------------------------------------- 
; long avg(register __d0 int argc, register __a0 char **argv);
;      -- Computes the average of an array of numbers
;-------------------------------------------------------------------------- 
;  This is the stack based entry point into this function  
;  Move the parameters off the stack and back into register so that
;  we can drop through to the rest of the routine.
_avg:
   MOVE.L   4(SP),D0
   MOVE.L   8(SP),A0

;-------------------------------------------------------------------------- 
;  This is the registerized entry point into this function
;  Nothing extra to do in this case.
@avg:   

;  At function entry register D0 holds argc and A0 holds argv

;  This is how to allocate storage for auto variables.
;  In this case, we need 4bytes to store the return value from a 
;  call to stcd_l() 
   SUBQ.W      #4,SP
   
;  Save the current value of these registers so that we can reuse them
;  below.  We will restore these registers at function exit.
   MOVEM.L     D5-D7/A3,-(SP)

;  Since argc and argv are both in scratch registers (registers that are 
;  not saved over function calls), move them to a location where they 
;  won't be destroyed.
   MOVEA.L     A0,A3
   MOVE.L      D0,D7
   
;  Do some error checking to make sure argc looks reasonable.
   TST.L       D7
   BGT.B       sumval

;  Error case (argc <= 0). Return an average of zero.
   MOVEQ       #00,D0
   BRA.B       avgret

sumval:
;  Convert the strings into numbers and sum them into register D6
;  If we get to here we are guaranteed to execute the loop at least once.
   MOVEQ       #00,D6

;  Use D5 as our loop counter
   MOVEQ       #00,D5

avgloop:
;  Convert the number from string format into numeric form
;  Note:  The '@' on the JSR instruction means that this is the 
;  registerized parameter version.
   MOVEA.L     (A3)+,A0
   LEA         0010(SP),A1
   JSR         @__stcd_l(PC)

;  Accumulate our total
   ADD.L       0010(SP),D6

;  See if there is more to do
;  Increment our loop counter and compare against our termination value
   ADDQ.L      #1,D5
   CMP.L       D7,D5
   BLT.B       avgloop
   
;  Now that we have the sum, compute the average as (total/argc).
   DIVS.W      D7,D6
   EXT.L       D6
   MOVE.L      D6,D0      

;  Done; cleanup and return
avgret:

;  Restore saved registers
   MOVEM.L     (SP)+,D5-D7/A3

;  Release our automatic storage
   ADDQ.W      #4,SP

;  Away we go   
   RTS

;-----------------------------------------------------------------
; int __asm prnts(register __d0 BPTR fh,  register __a0 char *str);
;      -- write a string out to the given AmigaDos file handle
;      -- no error checking is done on the input file handle
;-----------------------------------------------------------------
; Setup a single entrypoint for this routine.   Parameters will 
; always be passed in register.  
_prnts   
;  Here is our list of saved registers
   MOVEM.L     D3/A3/A6,-(SP)

;  Save the start of the string, because we will need it later
   MOVEA.L     A0,A3
 
;  We need to find the length of the string
;  Loop to count the number of characters until we find a NULL byte.
;  Note:  I use D3 here to hold the length of the string so that I
;  don't have to do a move when I go to do the write.

   MOVEQ       #00,D3
prntloop:   
   TST.B       (A0)+
   BEQ.B       wrtstr
   ADDQ.L      #1,D3
   BRA.B       prntloop


;  Load up the needed registers to make the call to AmigaDos to write
;  out the string.  Note:  I don't have to worry about opening up dos.library
;  here because it is done for me in the startup code.     
;  By convention, calls into the AmigaDos libraries are done off of A6, and 
;  the macros in the header files reflect this.  If you were making several 
;  calls you would not have to reload this register before each call unless 
;  you had used it in the interum.
wrtstr:
   MOVE.L      D0,D1
   MOVE.L      A3,D2
   MOVEA.L     _DOSBase(A4),A6
   JSR         _LVOWrite(A6)
   

;  The return value from Write() comes back in register D0 which is just where
;  we want it to return to our caller.


;  Restore saved registers
   MOVEM.L     (SP)+,D3/A3/A6

;  Away we go   
   RTS
   END
