

               ASSEMBLER PROGRAMMING TECHNIQUES FOR THE ATARI ST
               =================================================
                                by Peter Hibbs.

                  PASSING IN-LINE PARAMETERS TO SUB-ROUTINES
                  ==========================================


    In common with all  micro-processors,  the  ATARIs 68000 processor uses
    sub-routines for sections of code which  may be repeated more than once
    within a program and it  is  often  necessary  to  pass  data to a sub-
    routine from the calling program.

    There are  several  methods  of  passing  data  to  a  sub-routine. The
    simplest method is to load  the  required  values  into data or address
    registers and then call  the  sub-routine  which  then fetches the data
    from these registers. This system is  useful  where the data values are
    variable and sufficient registers are available  but is not ideal where
    a large amount of data (such as a string of text) is to be transferred.

    Another method (which is used extensively by the ATARI GEM and BIOS) is
    to push the data values (or the  address  of a string of data) onto the
    stack and then the sub-routine has to  recover the data from the stack.
    This method is fairly flexible but  is somewhat untidy especially where
    several strings of data may need to be passed to the routine.

    A third method (which does not seem  to be used much in ATARI software)
    is to pass the data  as  in-line  data  embedded in the calling program
    immediately after the sub-routine  call.  This  method is only suitable
    for fixed data, however,  since  its  value  is  determined at assembly
    time. This system has the advantage  though  that the data concerned is
    easily visible at the point in the program  where it is used and not in
    some look-up table somewhere else in  the source code.  Also the values
    (such as the string data  or  addresses)  can  be easily altered during
    program development.

    This article describes this technique with  an example of a sub-routine
    which use in-line data.  With the powerful instruction set of the 68000
    processor the task of accessing  the  data  in the calling program from
    the sub-routine is quite  simple.  For  example, consider the following
    code where the data string 'text string',0  is to be passed to the sub-
    routine PRINT which then sends it to the printer port.

                    bsr        print                call sub-routine
                    dc.b       'text string',0      data to be passd
                    even

    The calling program  calls  the  required  sub-routine  and immediately
    follows  it  with  the  byte  constants  of  the  required  string  and
    terminated with  a  NUL  (0)  character.  Note  that  since  the string
    consists of byte size data it could  end  on an odd address which would
    cause the processor to generate  an  'address'  error  on return to the
    calling program. The 'even' pseudo-instruction pads out the string with
    a NUL character so  that  the  next  instruction  after the call always
    starts at an even address.

    In the sub-routine the code would look something like this :-

          print     movem.l    a0/d0,-(sp)          save any registers
                    move.l     8(sp),a0             fetch string address

                    sub-routine code                execute sub-routine

                    move.l     a0,8(sp)             a0=end of string+1
                    movem.l    (sp)+,a0/d0          restore registers
                    rts                             return

    When a sub-routine is called,  the  stack  pointer is first decremented
    and then the current return address is  pushed onto the stack (4 bytes)
    with the stack pointer pointing at the last byte saved. On entry to the
    routine any registers which may be  corrupted  by the routine are first
    pushed onto the stack (as  required).  The  next instruction (using the
    'address register indirect with  displacement'  addressing mode) copies
    the return address from the  stack  into  register a0. The displacement
    value required in this case is 8 since the previous instruction saved 8
    bytes (i.e. two longwords) onto the stack. Address register a0 will now
    be pointing at the first byte of the data string in the calling program
    code, the sub-routine fetches the  data  and  processes it as required.
    When all the data  has  been  processed,  address  register  a0 must be
    pointing at the next word after the  data string in the calling program
    code (i.e. the next main program instruction).

    At the end of the  sub-routine  the  address  in address register a0 is
    copied back into the stack  using  the same displacement instruction so
    that the  return  instruction  will  return  program  execution  to the
    correct point in the calling program.  The saved registers are restored
    and the RTS instruction returns control to the calling program.

    The following sub-routine  demonstrates  a  practical  example  of this
    technique.

    It is often necessary in programs to  load  a memory buffer with a text
    string such as a filename or  message.  This example copies a string of
    data  bytes  into  a  specified  memory  buffer.  The  calling  program
    specifies the address  of the buffer  (as one longword) followed by the
    data string which  is  terminated  with  a  NUL  value.  Since all data
    strings in the ATARI operating system are usually terminated with a NUL
    value, this character is also copied into  the buffer as well as acting
    as a terminator.

    The format for the call from the main program is :-

                    bsr        copy_string          call sub-routine
                    dc.l       buffer               address of buffer
                    dc.b       'text string',0      string data+NUL
                    even                            ensure even address
                    .
                    .

          buffer    ds.b       20                   destination buffer

    The operation of the  sub-routine  is  fairly self-explanatory, address
    register a0 is used as a  pointer  to  the  data string, register a1 is
    used as a pointer to the memory  buffer  and register d0 holds the data
    between each fetch and store instruction.

    One problem which arises when byte size  data is being passed to a sub-
    routine is that the data may end on an odd address. Since the processor
    can only fetch instructions  from  even  addresses,  the return address
    must be incremented to the next even  address  if it finishes on an odd
    address but not changed if  it  finishes  on  an even address. When the
    data transfer has been completed bit  0  of  the address register a0 is
    tested to see  if  it  is  a  1  (odd  address)  or  0  (even address).
    Unfortunately, the 68000 instruction set  does  not  include a bit test
    instruction which allows this to be done  on an address register so the
    a0 register is  first  copied  into  the  data  register  d0. The least
    significant bit  of d0 is  tested  and  if  it  is  a  0 the routine is
    terminated after first restoring the saved registers. If the bit is a 1
    (an odd address) the a0 register is  incremented by one and the routine
    terminated as before.  The  'even'  pseudo-instruction  in  the calling
    program ensures that the  processor  fetches  the next instruction from
    the correct address.

                           COPY_STRING SUB-ROUTINE.

    ;ENTRY  destination address defined as in-line data.
    ;       data string defined as in-line data.
    ;EXIT   string data copied into defined buffer
    ;       no registers changed.

    copy_string     movem.l    a0-a1/d0,-(sp)       save regs (12 bytes)
                    move.l     12(sp),a0            fetch return address
                    move.l     (a0)+,a1             fetch destination addr
    copy_string1    move.b     (a0)+,(a1)+          copy byte to buffer
                    bne        copy_string1         was it 0 ?, no repeat
                    move.l     a0,d0                copy a0 to d0
                    btst       #0,d0                is address even ?
                    beq        copy_string2         branch if yes
                    addq.l     #1,a0                inc address if not
    copy_string2    move.l     a0,12(sp)            copy ret addr to stack
                    movem.l    (sp)+,a0-a1/d0       restore regs
                    rts                             return


    The above  routine  was  written  with  the  DEVPAC  2 editor/assembler
    program from HiSoft. There is one point that the programmer should note
    however. When using the Monitor program  to debug the main program, the
    in-line data  following  the  sub-routine  will  confuse  the  debugger
    program when single stepping with the  CTRL  T option (executing a sub-
    routine without stepping through  it).  When  single stepping through a
    program the CTRL Z option  should  be  used and the sub-routine stepped
    through otherwise the debugger program will try and execute the in-line
    data as instructions.
