home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / MACLIB / MACROS.TIP < prev    next >
Text File  |  2000-06-30  |  7KB  |  200 lines

  1.  
  2.      Here  is a    tip for    users of 8080 macro assemblers which
  3. I've never seen    in print, but which I've found to be useful.
  4. It  pertains  to the use of the    standard condition codes (Z,
  5. NZ,  C,     NC,  M,  P,  PO,  PE)    as  parameters    of  a  macro
  6. instruction.
  7.      Let's say you've written a    useful macro to    do something
  8. or  other.  As an example most CP/M programmers    are familiar
  9. with,    let's  consider     the  CPM  macro.  It  usually    goes
  10. something like this:
  11.  
  12. CPM    MACRO    FUNC,VALUE
  13.      IF     NOT NUL VALUE
  14.      LXI     D,VALUE     ;;Load    D&E with value.
  15.      ENDIF
  16.      IF     NOT NUL FUNC
  17.      MVI     C,FUNC         ;;Load    C reg with Function.
  18.      ENDIF
  19.      CALL     BDOS
  20.      ENDM
  21.  
  22.      The  examples  are    written    for the    Digital    Research MAC
  23. assembler,  but    would probably be similar for others. To use
  24. the macro, one might say:
  25.  
  26.      CPM     CON,CR     ;Send carriage    return to console
  27.  
  28. Assuming  that    CON had    been EQUated to    2, the CP/M function
  29. for  console  output,  and  CR    had been EQUated to 0DH, the
  30. macro would expand to:
  31.  
  32.      LXI     D,0DH
  33.      MVI     C,02H
  34.      CALL     0005H
  35.  
  36. which    would    send  a     carriage  return  to  the  console.
  37. Similarly,  if    KEY had    been EQUated to    1, the console input
  38. function, the macro call
  39.  
  40.      CPM     KEY
  41.  
  42. would expand to:
  43.  
  44.      MVI     C,01H
  45.      CALL     0005H
  46.  
  47. which  would  get a keyboard character into register A.    This
  48. macro  is  in fact used    quite frequently by many programmers
  49. who work with CP/M. Note that the IF's prevent unneeded    code
  50. from being assembled where the parameter is not    supplied.
  51.      Now  consider  a  case  where we want to read a console
  52. character only when the    carry flag is set. Perhaps the carry
  53. flag  indicates     an error condition, and we want the program
  54. to pause. The usual way    of doing this is:
  55.  
  56.      ...         ;Code to set carry on error
  57.      JNC     OK     ;Jump around input routine
  58.      CPM     KEY     ;Get a    character from console.
  59. OK:
  60.      ...         ;Continue with    operation.
  61.  
  62.      To    save programmer    time, we can modify the    CPM macro to
  63. allow a    condition code to be specified as a third parameter.
  64. Then we    could write lines like:
  65.  
  66.      CPM     KEY,,C         ;Get char if Carry set
  67.      ...or...
  68.      PUSH     PSW         ;Save output char.
  69.      MOV     E,A         ;Move to E for    output
  70.      CPM     CON         ;Output it
  71.      POP     PSW         ;Get back character
  72.      CPI     CR         ;Was it a carriage return?
  73.      CPM     CON,LF,Z     ;If so, follow    with Line Feed.
  74.      ...
  75.  
  76. without     having     to  code  the    jump  instructions which are
  77. necessary  to  avoid executing the macro code. We do this by
  78. coding    the  jump instruction into the macro itself. This is
  79. made  easier  by  the  fact  that most macro assemblers    will
  80. allow  us  to  use  the     value    of  an    opcode    as data. For
  81. compatability  with  Intel  standards,    it  should  be coded
  82. within    parentheses  (required    by  some  assemblers) and if
  83. we're  using  MAC,  should  have spaces    around the opcode to
  84. avoid a    little-known glitch in MAC.
  85.      Let's  work  with an even simpler macro to    see how    this
  86. might  operate.     We'll    invent    the JUMPIF macro, which    does
  87. nothing    but cause a conditional    jump.
  88.  
  89. JUMPIF    MACRO    COND,ADDR
  90.      DB     ( J&COND )
  91.      DW     ADDR
  92.      ENDM
  93.  
  94.      In    this simple example, coding the    line:
  95.      JUMPIF     NC,EOJ
  96. would expand to:
  97.      DB     ( JNC )
  98.      DW     EOJ
  99.      "Big deal!", you say. Why not just    code:
  100.      JNC     EOJ
  101.      Well,  we want to work the    condition code into a larger
  102. macro,    and  generate  a  jump around the inline code if the
  103. condition  is  false.  Ah, but there's the rub.    In order for
  104. the macro to work properly, the    jump instruction has to    jump
  105. if  the    condition given    is NOT true, and fall through to the
  106. inline    code  if it IS true. To    illustrate, let's invent the
  107. opposite  of  the  JUMPIF  macro, the JMPUNLES (Jump Unless)
  108. macro. We could    do something like this:
  109.  
  110. JMPUNLES MACRO    COND,ADDR
  111.      J&COND     LABL1     ;Jump around the next instr.
  112.      JMP     ADDR     ;Do the real jump.
  113. LABL1:
  114.      ENDM
  115.  
  116. But  this requires the use of two jump instructions where we
  117. know  only  one     is  required. In practice, of course, LABL1
  118. would be declared LOCAL    or we could only use this macro    once
  119. per program without getting duplicate label errors. There is
  120. a  way    out.  In  all  8080 instructions involving condition
  121. codes,    there  is  a  one bit difference between a condition
  122. code  and  it's    opposite, and it is always the same bit. The
  123. bit  used  is  the  bit     with  a  value    of 8. Compare the JZ
  124. instruction with the JNZ instruction:
  125.  
  126.      1     1   0     0   1     0   1     0     Jump If Zero
  127.      1     1   0     0   0     0   1     0     Jump If Not Zero
  128.    --- --- --- --- --- --- --- ---
  129.    128    64  32    16  *8*     4   2     1     Bit Values
  130.  
  131. Changing  this    one bit    changes    a JC to    a JNC, a JP to a JM,
  132. and  a    JPO  to     a JPE.    The bit    has the    same function in the
  133. conditional call and return instructions as well.
  134.      Combining    all  these facts, we can write our CPM macro
  135. with the condition code    parameter. It comes out    looking    like
  136. this:
  137.  
  138. CPM    MACRO    FUNC,VALUE,COND
  139.      LOCAL     LABLX         ;Generate one-time label
  140.      IF     NOT NUL COND     ;Generate jump    only when needed
  141.      DB     ( J&COND ) XOR    8 ;Change condition code to its
  142.                  ;    opposite.
  143.      DW     LABLX         ;Address field    of jump    instr.
  144.      ENDIF
  145.      IF     NOT NUL VALUE
  146.      LXI     D,VALUE
  147.      ENDIF
  148.      IF     NOT NUL FUNC
  149.      MVI     C,FUNC
  150.      ENDIF
  151.      CALL     BDOS
  152. LABLX:    ;Jump here if COND wasn't true.     Continue...
  153.      ENDM
  154.  
  155.      Coding  CPM     CON,LF,NZ    would cause the    opcode to be
  156. expanded as:
  157.      DB     ( JNZ ) XOR 8
  158. which is the same as:
  159.      DB     ( JZ )
  160. Note  that  the     COND  parameter  could    have been any of the
  161. valid 8080 condition codes: Z, NZ, C, NC, P, M,    PO, PE.
  162.      Also,  we    could  use  conditional     calls ( C&COND    ) or
  163. conditional  returns  (    R&COND)    and changed the    sense of the
  164. condition  by  XORing  the  opcode  with  8  to     change     the
  165. necessary  bit.    This technique can be quite useful in macros
  166. of all types to    allow the use of condition codes on the    same
  167. line   as  the    macro  call,  saving  programmer  time,     and
  168. lessening  the    chances    of error. For your information,    here
  169. is  a  list  of     8080  opcodes    which are changed into their
  170. opposites by XORing with 8. Although the ones with condition
  171. codes  will  probably be the most useful, some of the others
  172. may  come in handy some    day. The instruction on    the left has
  173. the "8's" bit as a 0, on the right as a    1.
  174.      STAX     LDAX
  175.      RLC     RRC
  176.      RAL     RAR
  177.      INX     DCX
  178.      SHLD     LHLD
  179.      JNC     JC
  180.      JNZ     JZ
  181.      JP     JM
  182.      JPO     JPE
  183.      CNC     CC
  184.      CNZ     CZ
  185.      CP     CM
  186.      CPO     CPE
  187.      RNC     RC
  188.      RNZ     RZ
  189.      RP     RM
  190.      RPO     RPE
  191.      DI     EI
  192.      OUT     IN
  193.  
  194. Do yourself a favor and    use this trick only where it will
  195. genuinely improve efficiency, not just to make simple code hard
  196. to read.  Happy    Hacking!
  197.  
  198.             Gary P. Novosielski
  199.            (201) 935-4087 Eve's.
  200.