Here  is a tip for users of 8080 macro assemblers which
I've never seen in print, but which I've found to be useful.
It  pertains  to the use of the standard condition codes (Z,
NZ,  C,  NC,  M,  P,  PO,  PE)  as  parameters  of  a  macro
instruction.
    Let's say you've written a useful macro to do something
or  other.  As an example most CP/M programmers are familiar
with,   let's  consider  the  CPM  macro.  It  usually  goes
something like this:

CPM     MACRO   FUNC,VALUE
        IF      NOT NUL VALUE
        LXI     D,VALUE         ;;Load D&E with value.
        ENDIF
        IF      NOT NUL FUNC
        MVI     C,FUNC          ;;Load C reg with Function.
        ENDIF
        CALL    BDOS
        ENDM

    The  examples  are written for the Digital Research MAC
assembler,  but would probably be similar for others. To use
the macro, one might say:

        CPM     CON,CR  ;Send carriage return to console

Assuming  that  CON had been EQUated to 2, the CP/M function
for  console  output,  and  CR  had been EQUated to 0DH, the
macro would expand to:

        LXI     D,0DH
        MVI     C,02H
        CALL    0005H

which   would   send  a  carriage  return  to  the  console.
Similarly,  if  KEY had been EQUated to 1, the console input
function, the macro call

        CPM     KEY

would expand to:

        MVI     C,01H
        CALL    0005H

which  would  get a keyboard character into register A. This
macro  is  in fact used quite frequently by many programmers
who work with CP/M. Note that the IF's prevent unneeded code
from being assembled where the parameter is not supplied.
    Now  consider  a  case  where we want to read a console
character only when the carry flag is set. Perhaps the carry
flag  indicates  an error condition, and we want the program
to pause. The usual way of doing this is:

        ...             ;Code to set carry on error
        JNC     OK      ;Jump around input routine
        CPM     KEY     ;Get a character from console.
OK:
        ...             ;Continue with operation.

    To save programmer time, we can modify the CPM macro to
allow a condition code to be specified as a third parameter.
Then we could write lines like:

        CPM     KEY,,C          ;Get char if Carry set
        ...or...
        PUSH    PSW             ;Save output char.
        MOV     E,A             ;Move to E for output
        CPM     CON             ;Output it
        POP     PSW             ;Get back character
        CPI     CR              ;Was it a carriage return?
        CPM     CON,LF,Z        ;If so, follow with Line Feed.
        ...

without  having  to  code  the  jump  instructions which are
necessary  to  avoid executing the macro code. We do this by
coding  the  jump instruction into the macro itself. This is
made  easier  by  the  fact  that most macro assemblers will
allow  us  to  use  the  value  of  an  opcode  as data. For
compatability  with  Intel  standards,  it  should  be coded
within  parentheses  (required  by  some  assemblers) and if
we're  using  MAC,  should  have spaces around the opcode to
avoid a little-known glitch in MAC.
    Let's  work  with an even simpler macro to see how this
might  operate.  We'll  invent  the JUMPIF macro, which does
nothing but cause a conditional jump.

JUMPIF  MACRO   COND,ADDR
        DB      ( J&COND )
        DW      ADDR
        ENDM

    In this simple example, coding the line:
        JUMPIF  NC,EOJ
would expand to:
        DB      ( JNC )
        DW      EOJ
    "Big deal!", you say. Why not just code:
        JNC     EOJ
    Well,  we want to work the condition code into a larger
macro,  and  generate  a  jump around the inline code if the
condition  is  false.  Ah, but there's the rub. In order for
the macro to work properly, the jump instruction has to jump
if  the condition given is NOT true, and fall through to the
inline  code  if it IS true. To illustrate, let's invent the
opposite  of  the  JUMPIF  macro, the JMPUNLES (Jump Unless)
macro. We could do something like this:

JMPUNLES MACRO  COND,ADDR
        J&COND  LABL1   ;Jump around the next instr.
        JMP     ADDR    ;Do the real jump.
LABL1:
        ENDM

But  this requires the use of two jump instructions where we
know  only  one  is  required. In practice, of course, LABL1
would be declared LOCAL or we could only use this macro once
per program without getting duplicate label errors. There is
a  way  out.  In  all  8080 instructions involving condition
codes,  there  is  a  one bit difference between a condition
code  and  it's opposite, and it is always the same bit. The
bit  used  is  the  bit  with  a  value of 8. Compare the JZ
instruction with the JNZ instruction:

    1   1   0   0   1   0   1   0     Jump If Zero
    1   1   0   0   0   0   1   0     Jump If Not Zero
  --- --- --- --- --- --- --- ---
  128  64  32  16  *8*  4   2   1     Bit Values

Changing  this  one bit changes a JC to a JNC, a JP to a JM,
and  a  JPO  to  a JPE. The bit has the same function in the
conditional call and return instructions as well.
    Combining  all  these facts, we can write our CPM macro
with the condition code parameter. It comes out looking like
this:

CPM     MACRO   FUNC,VALUE,COND
        LOCAL   LABLX           ;Generate one-time label
        IF      NOT NUL COND    ;Generate jump only when needed
        DB      ( J&COND ) XOR 8 ;Change condition code to its
                                ;    opposite.
        DW      LABLX           ;Address field of jump instr.
        ENDIF
        IF      NOT NUL VALUE
        LXI     D,VALUE
        ENDIF
        IF      NOT NUL FUNC
        MVI     C,FUNC
        ENDIF
        CALL    BDOS
LABLX:  ;Jump here if COND wasn't true.  Continue...
        ENDM

    Coding  CPM     CON,LF,NZ  would cause the opcode to be
expanded as:
        DB      ( JNZ ) XOR 8
which is the same as:
        DB      ( JZ )
Note  that  the  COND  parameter  could have been any of the
valid 8080 condition codes: Z, NZ, C, NC, P, M, PO, PE.
    Also,  we  could  use  conditional  calls ( C&COND ) or
conditional  returns  ( R&COND) and changed the sense of the
condition  by  XORing  the  opcode  with  8  to  change  the
necessary  bit. This technique can be quite useful in macros
of all types to allow the use of condition codes on the same
line   as  the  macro  call,  saving  programmer  time,  and
lessening  the  chances of error. For your information, here
is  a  list  of  8080  opcodes  which are changed into their
opposites by XORing with 8. Although the ones with condition
codes  will  probably be the most useful, some of the others
may  come in handy some day. The instruction on the left has
the "8's" bit as a 0, on the right as a 1.
        STAX    LDAX
        RLC     RRC
        RAL     RAR
        INX     DCX
        SHLD    LHLD
        JNC     JC
        JNZ     JZ
        JP      JM
        JPO     JPE
        CNC     CC
        CNZ     CZ
        CP      CM
        CPO     CPE
        RNC     RC
        RNZ     RZ
        RP      RM
        RPO     RPE
        DI      EI
        OUT     IN

Do yourself a favor and use this trick only where it will
genuinely improve efficiency, not just to make simple code hard
to read.  Happy Hacking!

                   Gary P. Novosielski
                  (201) 935-4087 Eve's.