home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Falcon 030 Power 2
/
F030_POWER2.iso
/
ST_STE
/
MAGS
/
ICTARI09.ARJ
/
ictari.09
/
ASSEMBLY
/
MACROS
/
MACROTUT.TXT
Wrap
Text File
|
1994-04-17
|
20KB
|
409 lines
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.
____________________