
                       ASSEMBLER MACRO TUTORIAL (DEVPAC)
                       =================================

                        By Peter Hibbs and Simon Rigby

    An assembler MACRO definition is a system for equating a single word to
    a group of machine code  instructions  which  can  then  be used in the
    program source code. The main object  of  MACROs  is to make the source
    code easier to read and also easier  to program. Used with care, MACROs
    can be a great help to the  programmer although there are some pitfalls
    which should be  avoided.  Usually  (but  not  always)  the  MACROs are
    defined in a separate file  and  'included'  in the main program source
    code  which  avoids  cluttering  up   the   source  file.  Since  MACRO
    definitions must be defined  BEFORE  they  are  used  in a program, the
    MACRO file must be included near the beginning of the source file.

    This tutorial refers  entirely  to  HiSofts  Devpac assembler. Although
    most modern assemblers can use MACROs  there may be some differences in
    the details, refer to your assembler manual for further information.

    MACRO FORMAT
    
    To define a MACRO a label is used  which is the name given to the MACRO
    followed by the machine code  instructions  and  the ENDM command which
    terminates the MACRO definition. A simple  example is shown below which
    executes a TOS call to wait for the next vertical sync pulse.

    v_sync         MACRO
                   movem.l   d0-d2/a0-a2,-(sp)   save registers
                   move      #37,-(sp)           function 37
                   trap      #14                 call TOS trap 14
                   addq.l    #2,sp               correct stack
                   movem.l   (sp)+,d0-d2/a0-a2   restore registers
                   ENDM

    In the user program the word  v_sync  is  used in the instruction field
    wherever the above sequence of instructions is required. For example :-


                   move      #24,d0              load register
    loop           v_sync                        execute macro
                   dbra      d0,loop             repeat 23 times
                   ..

    In the MACRO  definition  the  words  MACRO  and  ENDM  are  written in
    capitals, this is not necessary but  is  usually  done to make the code
    easier to understand. Note that in  this example the registers that are
    (or may be) used by the trap  call  are saved at the start and restored
    at the end. However, in all  HiSofts  MACROs and in the TOSMACRO.S file
    which accompanies this document, this is  not the case. This highlights
    an important point concerning  MACRO  definition files especially where
    there are a large number in one file, that is that all MACROs should be
    properly documented so  that  the  programmer  knows  exactly what each
    MACRO does, the parameters  which  need  to  be  passed  to  it and any
    parameters returned. If this is not done  properly a lot of time can be
    wasted trying to sort out why the MACRO code doesn't work especially as
    the MACRO code itself could be in another file hidden away somewhere on
    the disk. A text file should be  written  and printed out with the name
    of each MACRO and its required parameters and kept as a quick reference
    guide.

    MACRO SIZE
    
    A mistake that is easy to make is to overdo the MACRO size. It is not a
    good idea to make a MACRO too  large,  say  more than about 10 lines of
    code. Don't forget that wherever the MACRO is used, all the code in the
    MACRO will be inserted into the object  code. If a MACRO is used dozens
    of times in a program the  source  code  file  will still look nice and
    small but the object code will be much larger than it needs to be. Also
    debugging a program is  more  difficult,  for  example  if  you need to
    single step through a section of code which has several large MACROs it
    can lead to more wasted time since the chances are the MACRO code works
    OK as it has probably been  tested  elsewhere. For large blocks of code
    which are called repeatedly, a normal sub-routine should be used. Also,
    with HiSofts debugger, a sub-routine can  be executed without having to
    single step through it  which  you  cannot  do  with  a MACRO. The sub-
    routine call itself could  still  be  made  into  a MACRO, if required,
    although this is a bit pointless.

    BLACK BOX THEORY
    
    Ideally all commonly used  sub-routines  should  be  made  into a Macro
    file, as later changes in the way data is passed to the sub-routine, or
    additions to the number of parameters  can  be  catered for in a macro,
    but would have to involve changing all your old program code (or making
    a new subroutine with a different name) otherwise.

    A case in point is Fsel_input which  now  has two GEM calls, one with a
    title and one without. The macro could  be  made to count the number of
    parameters passed and decide which one to use.

    PASSING PARAMETERS TO MACROs
    
    It is often required to  pass  parameters  to  some of the instructions
    within the MACRO  definition.  Since  these  are  not  always  known at
    assembly time there is a  system  for  passing  parameters to the MACRO
    during program execution. A common  type  of  MACRO are the various TOS
    calls using the GEMDOS,  BIOS  and  XBIOS  some  of which require other
    variables when used. For example,  the  GEMDOS call #2 (c_conout) which
    outputs a  character  to  the  screen  requires  the  character  to  be
    displayed to be pushed onto the  stack  when  called. This value can be
    passed to the MACRO as a constant,  in  a register or in a memory store
    when the program is run.  It  is  defined  in  the  MACRO itself as a \
    character followed by an alpha-numeric  character  1-9, a-z or A-Z. For
    example the c_conout MACRO could be defined as :-

    c_conout       MACRO               \1=character
                   move      \1,-(sp)  push chr onto stack
                   move      #2,-(sp)  function No
                   trap      #1        GEMDOS call
                   addq.l    #4,sp     correct stack
                   ENDM

    In the program source code the MACRO would be called like this :-

                   c_conout  #'A'

    and would display the character A.  The  \1  value is replaced with the
    data following the MACRO call. This  value  can be a constant (preceded
    by a # character),  a  register  (d0-d7  or  a0-a6)  or  a memory store
    without changing the MACRO itself. For example the following two MACROs
    both do the same thing as the one above :-

                   move      #'A',d0
                   c_conout  d0

    or

                   move      #'A',store
                   c_conout  store
                   ..
    store          ds.w      1

    The assembler automatically works out  what  format the parameter is in
    and generates the object code accordingly.  Up  to 36 parameters can be
    passed to  a  MACRO,  subsequent  parameters  should  follow  the first
    separated by commas. MACROs can also be  nested as shown in the example
    below :-


    gemdos         MACRO               1\function,2\stack size
                   move      \1,-(sp)  push function number to stack
                   trap      #1        execute
                   add       \2,sp     correct stack
                   ENDM


    f_read         MACRO               1\buffer,2\count,3\fhandle
                   move.l    \1,-(sp)  push buffer address to stack
                   move.l    \2,-(sp)  push count value to stack
                   move      \3,-(sp)  push file handle to stack
                   gemdos    #63,#12   execute GEMDOS MACRO call 63
                   ENDM

    The first MACRO (gemdos) takes two  parameters, the function number and
    the stack correction value  which  is  then  used  by  the second MACRO
    (f_read). So when the f_read MACRO is invoked as :-

                   move.l    #2000,d0
                   f_read    #file_buff,d0,fhandle

    the code would be expanded to :-

                   move.l    #file_buff,-(sp)
                   move.l    d0,-(sp)
                   move      fhandle,-(sp)
                   move      #63,-(sp)
                   trap      #1
                   add.l     #12,sp
                   ..
    fhandle        ds.w      1                   file handle
    file_buff      ds.b      2000                file buffer

    Note that it is not necessary  to  define  the value sizes in the MACRO
    call as this is done in  the  MACRO  definition itself although it is a
    good idea to specify the size in  the  document file to remind the user
    what size parameter is required. The # character is necessary for fixed
    values or addresses but not for  registers  or the contents of a memory
    store. Note also that  the  names  shown  on  the  line after the MACRO
    pseudo-op are comments to  remind  the  programmer which parameters are
    being used within the MACRO.


    NEW INSTRUCTIONS
    
    One trivial use for MACROs is to  invent new instructions for the 68000
    CPU.  On some  processors there  are  bit instructions BZ and BNZ which
    branch if  a  bit  value  is  zero  or  non-zero.  Although  there  are
    equivalents on the 68000 (beq and bne) they are not so obvious. The two
    MACROs below allow the use of 'bz' if a bit = 0 and 'bnz' if a bit <> 0
    without increasing the number of instructions in the object code.

    bz             MACRO
                   beq       \1
                   ENDM

    bnz            MACRO
                   bne       \1
                   ENDM

    In the source code the instructions would be used in the normal way :-

                   btst      #1,d0     test bit 1 in reg d0
                   bz        address   branch if bit=0

    or

                   btst      #0,flags  test bit 0 in (flags)
                   bnz       address   branch if bit<>0


    TESTING PARAMETERS
    
    When a MACRO requires  parameters  the  programmer  usually enters them
    into the code, however the MACRO itself can test whether a parameter is
    missing and generate an assembly error to warn that something is wrong.
    In the example above the MACRO  can  test  whether the address has been
    provided after the bz or bnz instruction. For example :-

    bz             MACRO
                   IFC       '','\1'   test for missing parameter
                   FAIL                generate error
                   MEXIT               exit macro prematurely
                   ENDC                end of IF
                   beq       \1
                   ENDM


    The MACRO first checks if there is a string as a parameter and skips to
    the ENDC pseudo-op if there is.  If  there  is no address parameter the
    FAIL pseudo-op generates an  assembly  error  and  then exits the MACRO
    with the MEXIT pseudo-op.  If  the  MEXIT  command  is not included the
    assembler generates another error when it gets to the 'beq' instruction
    because of the missing address label. The line with the FAIL command is
    stored in the  error  file  but  the  cursor  is  positioned  on the bz
    instruction in the source file.

    NUMBER OF ARGUMENTS
    
    It is sometimes necessary for a  MACRO  to know how many arguments have
    been passed to it  from  the  main  program.  The  NARG reserved symbol
    defines the number of arguments actually following a MACRO call and can
    be used within the MACRO  using  the  IF  pseudo-ops. One example is to
    check whether the correct number of  arguments have been defined in the
    MACRO call.  In  the  example  below  the  rsconf  MACRO  requires  six
    arguments which means that the NARG  symbol  will have the value six if
    all the arguments have been entered  after  the MACRO call, by checking
    that this value is six within the  MACRO, the assembler can generate an
    error if it is incorrect. The MACRO definition would be as follows :-

    rsconf         MACRO              1\scr,2\tsr,3\rsr,4\ucr,5\ctrl,6\baud
                   IFNE      6-NARG   if 6-NARG NotEqual to zero
                   FAIL      Invalid parameters
                   MEXIT               exit MACRO
                   ENDC
                   move      \1,-(sp)
                   move      \2,-(sp)
                   move      \3,-(sp)
                   move      \4,-(sp)
                   move      \5,-(sp)
                   move      \6,-(sp)
                   move      #15,-(sp)
                   trap      #14
                   add       #14,sp
                   ENDM

    If less than (or more than) six  parameters  are used in the MACRO call
    the assembler will flag an error  during assembly. Note that the symbol
    \# can also be used in place of the NARG symbol.

    INSTRUCTION SIZE
    
    The symbol \0 can also  be  used  to  indicate an instruction size i.e.
    byte, word or longword. The following  example (from the DevPac manual)
    illustrates this. An INC or DEC  instruction can be easily simulated on
    the 68000 CPU with the following MACRO :-

    inc            MACRO               1\register
                   addq.\0   #1,\1     add 1 to register
                   ENDM

    In the source code the instructions would be formed as :-

                   inc.b     d0        increment low byte

    or             inc.w     d1        increment low word

    or             inc.l     a0        increment whole register

    The \0 symbol passes the size of the  register to the MACRO so that the
    correct part of the register is  incremented.  Note  that if no size is
    provided the instruction defaults to .w  (16 bits). The dec instruction
    will be the same except the addq will be subq.

    MACRO LABELS
    
    It is sometimes useful to have a  branch (or jump) instruction within a
    more complicated MACRO. It is  not  possible,  however, to use a normal
    label because if the MACRO is  used  more  than once the assembler will
    see duplicate label symbols. The \@  symbol  provides a method of using
    MACRO labels, the assembler  generates  a  different  unique label each
    time the MACRO is used. The example below shows this technique :-

    clr_screen     MACRO
                   move.l    screen,a0           fetch screen address
                   move      #32000/4-1,d0       set loop counter
    \@             clr.l     (a0)+               clear word and inc
                   dbra      d0,\@               dec count and repeat
                   ENDM

    Another example using both of the above symbols is a Memory Move, where
    you can move bytes, words or longs  and it shows how MACROS can improve
    efficiency at the same time...

    Move block of memory in bytes, words or longs, uses registers d0/a0-a1

    Mem_move       MACRO     from,to,count (in bytes,words or longs)
                   move.w    \3,d0
                   move.l    \1,a0
                   move.l    \2,a1
    .\@            move.\0   (a0)+,(a1)+
                   dbra      d0,.\@
                   ENDM

    and in the program use one of the following :-

         Mem_move.b          from_addr,to_addr,number_of_bytes
         Mem_move.w          from_addr,to_addr,number_of_words
         Mem_move.l          from_addr,to_addr,number_of_longs

    Note also that the 'full  stop'  character  can  be  used  as part of a
    label.

    MACRO TABLE
    
    The MACRO symbol definitions  are  stored  in  a  separate table to the
    normal source code symbols  so  that  MACRO  names  can  be the same as
    labels without causing the assembler to  flag  up an error. For example
    the following sub-routine is valid if a little confusing :-


    v_sync         movem.l   d0-d2/a0-a2,-(sp)   save registers
                   v_sync                        call macro
                   movem.l   (sp)+,d0-d2/a0-a2   restore registers
                   rts

    OTHER SYMBOLS
    
    There are a few more symbols associated with MACROs which are described
    in the DevPac manual but as  they  are  not  used  very much I have not
    mentioned them in this document.

    MACRO FILE DOCUMENTATION
    
    As mentioned above it is  important  to  keep  a printed document which
    details the function of each  MACRO  together with any parameters which
    are passed to it and any  returned.  Since  MACRO files can get changed
    from time to time as more  options  are  added or bugs corrected, it is
    also prudent to include the issue number and any changes that have been
    made to the file. These are best  placed  at the beginning of the MACRO
    file and the document file  together  with  the description and date of
    each change.

    USING DEVPAC AND ICTARI MACRO FILES
    
    HiSoft supply all the standard GEM  function  calls for the AES and VDI
    as a MACRO file with the DevPac assembler (GEMMACRO.I or GEMMACRO.S for
    DevPac 2) together  with  the  AES  and  VDI  library  files. Since all
    programmers with DevPac will have  these  files, members should use the
    MACROs in any source code that is  sent  in  to ICTARI to make the code
    shorter and easier to understand.

    Unfortunately HiSoft do not provide  a  MACRO  file  for the TOS system
    calls so we have provided two versions  on  this disk which can be used
    as a standard  MACRO  files.  The  file  TOSMACRO.S  in  folder MACRO_1
    defines all the TOS calls (but not the extra ones added to the STE TOS)
    in a simple  form.  The  associated  text  file  TOSMACRO.TXT shows the
    format for each MACRO in the source  file.  The second MACRO file is in
    the MACRO_2 folder and is  called  MACRO_V1.I  which, in turn, uses the
    other .I files in the same  folder. These MACROs are more comprehensive
    and include various error  checking  facilities.  There  is no separate
    text file but each  MACRO  has  an  explanation  of  its use within the
    source file which could be  printed  out  for reference. The programmer
    can decide which one  he  wants  to  use  (or  even  use both) and copy
    it/them to his  INCDIR  folder  or  wherever  the  'include'  files are
    stored. We would encourage members sending in source code which use TOS
    calls to use one of these MACRO  files  (don't forget to say which) and
    use the appropriate MACROs in the code to make it easier to understand.

    If members send in some source  code  which  uses  a MACRO which is NOT
    defined in the MACRO  files  just  mentioned  above,  would they please
    include the MACRO definition in the  source  code itself or send in the
    MACRO file together with the source code. Obviously any MACROs that are
    used in the code are useless without the MACRO code itself.

    CONCLUSION
    
    It should be  clear  from  this  article  that  MACROs  can improve the
    clarity of complex assembler  source  code  providing  they are used in
    moderation. It would be  possible,  for  example,  to  make every small
    section of code into a MACRO  which  would  then defeat the main aim of
    using MACROs. Also, creating standard MACRO files which all programmers
    can use, makes source code simpler  and  easier  to follow which is the
    primary aim of a programmers user group  such as ICTARI. If any members
    have any further comments to make  regarding this subject, ICTARI would
    be very interested to hear them.
                             ____________________
