...|....|....|....|....|....|...|....|....|....|....|....|....|.....


                   Alphanso's Assembler Assylum

          Assembly Language Programming for the Beginner

                              by SDH


                   Creating a User Defined MACRO

   This month's session will discuss the steps necessary to create
your own MACROs. A macro is an "opcode" that YOU may define within
your source code. Once you have defined the macro, you may employ
this macro as many times as you like from within your source code.
What a macro does is generate similar blocks of code that tend to
repeat within your program. The main objective behind defining a
macro is for program compactness and readability. This topic may be a
bit confusing to individuals who are unfamiliar with higher level
language procedures or functions similar to those found in Pascal,
but hopefully you will get the idea from the examples presented
below. For further details beyond what is presented, see the Assembly
Language Programmer's Manual - chapter 6.

First, an Analogy
   To give you an idea of how a macro works, examine the following
analogy. We are all familiar with the TYPE < > monitor call. With
this call, any characters within the < > are typed out to the
terminal each time we "employ" TYPE. Think of the ascii characters
contained within the < > as variables "passed" to the call TYPE.
These variable are then used by TYPE, the results being the
characters typed to the screen.
   There is a similar action when a macro is called. However, the
macro is used within the source code to generate more SOURCE CODE.
Just as TYPE uses the characters within the < >, a defined macro will
use any variables following a macro call. The variables "passed" to
the macro will "customize" the block of repeating code. This probably
sounds confusing, so let's take a look at an example that does NOT
employ a macro, and then re-customize this source code file to use a
macro and notice the results.

An Example Without a Macro
   There are many times when I am on the computer via modem and I
want to see what other JOBs are doing. One way to find this out is to
type in SYSTAT, WATCH, SPSTAT, or any other "dot" command that
informs me of the system status. The problem with the above solutions
is that it takes forever to transmit this information at 1200 baud!
Wouldn't it be nice to have a quick-down and dirty status report for
modem work? YES! This is the objective of the following code I
created called JOBRUN. What JOBRUN does is report the JOB NAME and
JOB PROGRAM to the screen, and that's it. Very efficent, very fast.
Just what the doctor ordered.
   Before we take a quick look at the code, let's take note of the
following: both the JOB NAME and JOB PROGRAM are stored inside each
JOB's JCB (Job Control Block) as packed RAD50 data. Both are two
words in length. In order for JOBRUN to produce the above stats in
readable form, both the JOB NAME and JOB PROGRAM must be "unpacked"
into ascii format. It just so happens that the code for this
translation is almost identical. Keep this in mind as you read the
following code. If you are having trouble following the code below,
almost all of the lines below are thoroughly discussed in the
Janurary .LOG Assylum. Also, the M68 SIG PPN [100,133] contains this
information in a file called ASSEM2.TXT and ASSEM2.M68.

;*****************************************
;*      JOBRUN.M68
;*
;*      Scans the system and prints
;*      every defined job to the screen
;*      AND the program they are running.
;*
;*      Usage: JOBRUN
;*
;*      by Dave Heyliger - AMUS Staff
;*****************************************

       OBJNAM  JOBRUN.LIT              ;Define the final product

       SEARCH SYS                      ;Grab all MONITOR calls
       SEARCH SYSSYM
       SEARCH TRM

       VMAJOR=1.                       ;Major version number of the program
       VMINOR=0.                       ;Minor version number of the program
       VEDIT=100.                      ;the edit number of the program

       .OFINI                          ;OFfset INItialization:
       .OFDEF  BUFFER,7.               ;OFfset DEFinition - "BUFFER"
       .OFSIZ  IMPSIZ                  ;IMPSIZ is the final size of bytes (7)

       PHDR    -1,0,PH$REE!PH$REU      ;Program is Re-entrant & Re-useable
       GETIMP  IMPSIZ,A5               ;Reg. A5 now ^'s to byte "0" of BUFFER
       MOV     JOBTBL,A0               ;Get base of JOB TABLE into A0
       TYPECR  < Job     Program>      ;type the column headings out
       TYPECR  <-------+-------->      ;and a "fancy" underline

LOOP:   MOV     (A0)+,A4                ;and let A4 point to each JBC
       MOV     A4,D0                   ;this will set the flags 2 B checked
       BMI     EXIT                    ;end of the JOB TABLE on a "-1"
       BEQ     LOOP                    ;goto the top on a "0"
       LEA     A1,JOBNAM(A4)           ;A1 points to variable to the JOBNAM
       LEA     A2,BUFFER(A5)           ;and ^ A2 to byte "0" of "BUFFER"
       UNPACK                          ;get the letters unpacked
       UNPACK                          ;  ...for up to full letters
       CLRB    @A2                     ;Place a "null" after the characters
       LEA     A2,BUFFER(A5)           ;Repoint A2 to byte "0" of "BUFFER"
       TTYL    @A2                     ;Print out the JOB NAME
       TYPE    <   >                   ;type some spaces
       LEA     A1,JOBPRG(A4)           ;A1 points to the JOB PROGRAM name
       LEA     A2,BUFFER(A5)           ;and ^ A2 to byte "0" of "BUFFER"
       UNPACK                          ;get the letters unpacked
       UNPACK                          ;  ...for up to full letters
       CLRB    @A2                     ;Place a "null" after the characters
       LEA     A2,BUFFER(A5)           ;Repoint A2 to byte "0" of "BUFFER"
       TTYL    @A2                     ;Print out the JOB PROGRAM
       CRLF                            ;Carriage return & line feed
       JMP     LOOP                    ;Time for the next JOB in the table

EXIT:   EXIT                            ;return to AMOS

       END                             ;of source code

Notice that the code that is extremely similar has been displayed in
italics. In fact, the only difference between the two sections is
JOBNAM (the JOB NAME offset in the JCB) and JOBPRG (the JOB PROGRAM
offset in the JCB). Let's create a macro that takes this difference
into consideration and creates the exact same binary code (exact same
final result). The way we will handle the "difference" above is by
"passing" JOBNAM and JOBPRG as variables to the macro code generation
routine.

;*****************************************
;*      JOBRUN.M68
;*
;*      Scans the system and prints
;*      every defined job to the screen
;*      AND the program they are running.
;*
;*      Usage: JOBRUN
;*
;*      by Dave Heyliger - AMUS Staff
;*****************************************

       OBJNAM  JOBRUN.LIT              ;Define the final product

       SEARCH SYS                      ;Grab all MACRO definitions
       SEARCH SYSSYM
       SEARCH TRM

       VMAJOR=1.                       ;Major version number of the program
       VMINOR=0.                       ;Minor version number of the program
       VEDIT=100.                      ;the edit number of the program

       .OFINI                          ;OFfset INItialization:
       .OFDEF  BUFFER,7.               ;OFfset DEFinition - "BUFFER"
       .OFSIZ  IMPSIZ                  ;IMPSIZ is the final size of bytes (7)

DEFINE  TYPEIT  V,B
;+---
;| TYPEIT will process a JCB variable that is two words packed RAD50 data
;|        and type this variable to the screen in ascii format.
;| where
;|        V = variable to be UNPACKED
;|        B = BUFFER area to place unpacked characters
;+--------------------------------------------------------------------------
       LEA     A1,V(A4)                ;A1 points to variable to be unpacked
       LEA     A2,B(A5)                ;and ^ A2 to byte "0" of "BUFFER"
       UNPACK                          ;get the letters unpacked
       UNPACK                          ;  ...for up to full letters
       CLRB    @A2                     ;Place a "null" after the characters
       LEA     A2,B(A5)                ;Repoint A2 to byte "0" of "BUFFER"
       TTYL    @A2                     ;Print out all chars until a null
ENDM                                    ;END of the Macro definition

       PHDR    -1,0,PH$REE!PH$REU      ;Program is Re-entrant & Re-useable
       GETIMP  IMPSIZ,A5               ;Reg. A5 now ^'s to byte "0" of BUFFER
       MOV     JOBTBL,A0               ;Get base of JOB TABLE into A0
       TYPECR  < Job     Program>      ;type out column headings
       TYPECR  <-------+-------->      ;and a fancy underline

LOOP:   MOV     (A0)+,A4                ;and let A4 point to each JBC
       MOV     A4,D0                   ;this will set the flags 2 B checked
       BMI     EXIT                    ;end of the JOB TABLE on a "-1"
       BEQ     LOOP                    ;goto the top on a "0"
       TYPEIT  JOBNAM,BUFFER           ;type out the JOB NAME
       TYPE    <   >                   ;type some spaces
       TYPEIT  JOBPRG,BUFFER           ;type out the PROGRAM NAME
       CRLF                            ;Carriage return & line feed
       JMP     LOOP                    ;Time for the next JOB in the table

EXIT:   EXIT                            ;return to AMOS

       END

Line-by-Line, Byte-by-Byte
   I am assuming that most readers are familiar with JOBLST.M68 - a
program that has shown up quite often in this series, so again, I
will only go over the new features of the above code since it
resembles JOBLST's algorithm.

DEFINE   TYPEIT    V,B
   DEFINE "TYPEIT" as a source code generation routine (our macro)
that requires two variable to be "passed" to this routine from within
the main body of the program. "V" and "B" will be replaced by
whatever variables follow TYPEIT from within the program, i.e., if
later in the program there is an instruction line TYPEIT
JOBNAM,BUFFER , then "V" will be replaced with "JOBNAM" and "B" will
be replaced with "BUFFER". Every line following the DEFINE
instruction up to but not including the line ENDM (for END MACRO)
will replace the TYPEIT var1,var2 call from within the main body of
the source code with the instructions contained in the macro
definition. If there are variables to be "swapped out", then these
instructions will include the swaps.

LEA A1,V(A4)
LEA A2,B(A5)
   Here is where the "passed" variables from the TYPEIT calls in the
main body of the program actually get "swapped out". Notice that
within the main LOOP of the program, TYPEIT gets called twice. Once
in the form TYPEIT JOBNAM,BUFFER, and once in the form TYPEIT
JOBPRG,BUFFER. In the first form, V is replaced with JOBNAM, B is
replaced with BUFFER. In the second form, V is replaced with JOBPRG,
B is replaced with BUFFER.

UNPACK
 |
 |
ENDM
   The remaining lines of the macro produce source code lines
exactly like the portions of JOBRUN that are in italics starting at
UNPACK. When the assembler (the program that converts your source
code file into a "runable" .LIT file) reaches an ENDM, the assembler
knows that it is through generating the block of code that will
replace TYPEIT in the main body of the program. The assembler will
then continue with source code to binary code conversion at the line
below the TYPEIT macro call.

Looking in the main body of JOBPRG:

TYPEIT   JOBNAM,BUFFER
   This is the actual call to the macro we defined above. It is this
line of code that is actually replaced by the code generated from the
macro TYPEIT, i.e., before we had
                      TYPEIT   JOBNAM,BUFFER
and what the assembler will actually create is
                       LEA     A1,JOBNAM(A4)
                       LEA     A2,BUFFER(A5)
                       UNPACK
                       UNPACK
                       CLRB    @A2
                       LEA     A2,BUFFER(A5)
                       TTYL    @A2
A similar set of instructions will be generated from the macro call
TYPEIT JOBPRG,BUFFER, except that JOBNAM will now be JOBPRG.

Wrapping Up
   The key idea behind creating user defined macros is to generate
repeated blocks of source code easily while allowing for small
variations in these blocks. The way the slight variations are handled
is by allowing variables to be "passed" into the macro. You may
define macros to have no variables passed or a series of variables to
be passed. Remember that each defined "dummy variable" in the macro
definition REQUIRES an actual variable in the macro call line.
   To actually see that the macro generates the exact same binary
code, type in both of the above files and assemble them (be sure to
rename one .LIT file to a different name so both may exist at the
same time). After assembly, do a DIR/H on the two files and notice
the exact same hash total. Also notice the readability of the second
source code presented today, and be sure you understand the "passing"
and "swapping out" of the variables.
   Since this topic is deeper than what meets the eye, Alphanso and
I will present more macro definitions next month that will further
enlighten you on the theory and practicality of user defined macros.
We will also explore the creation of our own user library file that
will contain all of our frequently used macros! Boy, pretty soon this
column will have to be retitle to "Assembly Language Programming for
the Intermediate!!" Hope you are enjoying, and I'll see you next
month. Bye!