home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpm / asmutl / meyertut.ark / MEYER02.TXT < prev    next >
Encoding:
Text File  |  1987-12-04  |  10.5 KB  |  217 lines

  1.                      CP/M Assembly Language
  2.                       Part II: The 8080 CPU
  3.                           by Eric Meyer
  4.  
  5.      Last time we discussed the assembler itself, some basic
  6. assembler directives, and their use in patching (modifying)
  7. existing programs. Now we'll begin to investigate writing our own
  8. programs, which will first require learning something about the
  9. 8080 chip itself. We will be using the standard Intel mnemonics
  10. for 8080 instructions.
  11.      (All of what follows will apply equally well to the Z80,
  12. which simply has more registers and instructions. Unfortunately
  13. there's a further complication: the Zilog instruction set,
  14. besides being larger, also uses different names for all the CPU
  15. instructions. We may deal with this in a later installment -- for
  16. now everything applies to an 8080 assembler.)
  17.      I'm assuming you have some programming experience so won't
  18. explain such concepts as loops, subroutines, etc., though I will
  19. discuss ways in which assembly language differs from the higher
  20. level languages with which you may be familiar.
  21.  
  22.  
  23. 1. 8080 Registers
  24.      The first thing to become accustomed to is the idea of a
  25. register. There are no variables and types (real number,
  26. character string) in assembly language, just numerical values and
  27. the place where they are stored. At any given time, a program
  28. will have most of its data stored someplace in memory, but what
  29. it is working on immediately must be fetched into the CPU itself.
  30. The CPU contains a number of registers for this purpose. By
  31. convention they have one-letter names, and each can hold one byte
  32. of data. Often they are depicted like this:
  33.  
  34. +-------+=======+
  35. |   A   |   F   |
  36. +-------+=======+
  37. |   B   |   C   |
  38. +-------+-------+
  39. |   D   |   E   |
  40. +-------+-------+          +-------+
  41. |   H   |   L   | - - - >  |  (M)  |
  42. +-------+-------+          +-------+
  43.  
  44.      Seven of the eight registers "A-L" are used to hold data
  45. bytes; the "F" (Flag) register serves a different purpose, which
  46. we'll discuss later.
  47.      The "A" register (accumulator) is where most of the
  48. arithmetic and logical operations take place, though it also can
  49. simply hold a byte for a while. The "B-C", "D-E", and "H-L"
  50. registers can function either separately or together in pairs to
  51. hold a word (two bytes) of data, often a memory address. The "M"
  52. register isn't really in the CPU at all -- it represents the
  53. contents of the memory at the address in the H-L register pair.
  54. Thus you can operate with a byte of data stored in memory just as
  55. you would one stored in the CPU itself, by putting its address in
  56. the H-L registers and referring to it as "register M".
  57.      Typically the H-L pair is used as a pointer in this fashion.
  58. The B, C, D, and E registers are used for a variety of tasks: as
  59. pointers, holding counter values, storing bytes temporarily.
  60.  
  61.  
  62. 2. The MVI and MOV Instructions
  63.      The simplest things you can do with a CPU register are put a
  64. data byte into it, and move a byte from one register to another.
  65. The MVI (move immediate) instruction puts a byte into a register:
  66. MVI  A,13 and puts the value 13 (decimal, unless followed by "H"
  67. for hex or "B" for binary) into the A register.
  68.      The MOV (move) instruction simply moves whatever's in one
  69. register into another: MOV  B,A takes the contents of the A
  70. register (at the moment, 13) and puts that value in the B
  71. register too. (The A register remains unchanged.)  Note that in
  72. both cases, the destination comes first, then the source. Thus
  73. you read "MOV B,A" as "MOVe into B, the value in A".
  74.      Let'consider what's going on at the gut level. The assembler
  75. will translate each "MOV" instruction into one instruction byte;
  76. e.g., "MOV B,A" turns out to be 47H. Each "MVI" is also one byte,
  77. followed by a second byte to tell it what value is being put in
  78. the register, e.g., "MVI A," is 3EH so the instruction "MVI A,13"
  79. will become the two bytes 3EH, 0DH. This is typical of how all
  80. the assembly language instructions wind up in the executable
  81. program. The sequence of hex bytes 3E,0D,47 in a COM file would,
  82. when the program runs, put the value 13 into the A register, and
  83. then into the B register too. While we will mostly be discussing
  84. things at the level of the mnemonics (like "MVI") that you write,
  85. it helps to know what's ultimately happening.
  86.  
  87.  
  88. 3. The LXI and XCHG Instructions
  89.      Similar instructions let you manipulate 16-bit values in
  90. register pairs though Intel mnemonics give these instructions
  91. totally different names. The LXI instruction (load index
  92. immediate) puts a word value into a register pair, referred to by
  93. the first register name: LXI  H,801H, loads the H-L register pair
  94. with the value 801 hex.
  95.      The high byte (08H) goes into the first register (H), the
  96. low byte (01H) into the second (L). (Note that this differs from
  97. the "backward" order in which 16-bit values are stored in memory,
  98. which we discussed last time.)  In fact, you could have done
  99. exactly the same thing with the pair of instructions:
  100.  
  101. MVI  H,8
  102. MVI  L,1
  103.  
  104. except that the "LXI" instruction makes it clearer what you're
  105. doing (and in fact takes only 3 bytes of code, as opposed to 4
  106. for the two "MVI"s).
  107.      When you need to move 16-bit values around, you do in fact
  108. have to use pairs of "MOV" instructions, e.g., to move this value
  109. from the H-L register pair into B-C now would require:
  110.  
  111. MOV  B,H
  112. MOV  C,L
  113.  
  114.      There is one exception. There are times when you would find
  115. it convenient to exchange the contents of the D-E and H-L
  116. register pairs, and this can be done by the simple instruction
  117. XCHG.
  118.  
  119.  
  120. 4. The INR, DCR and INX, DCX Instructions
  121.      The need to "increment" and "decrement" (add and subtract 1)
  122. is very common. The INR and DCR instructions increment and
  123. decrement a single register, e.g., INR  A adds one to whatever
  124. value was in the A register previously.
  125.      A similar pair, INX and DCX, work with 16-bit values in
  126. register pairs: DCX  H would subtract one from the value in the
  127. H-L register pair. If H-L still contained the value 801H we put
  128. in a moment ago, it would now contain 800H, (i.e., H would still
  129. contain 08H, and L would contain 00H.)  Note that this is not the
  130. same as DCR  H which would decrement the H register as a single
  131. byte, and not affect the L register at all. (If H-L had contained
  132. 801H, it would now contain 701H.)
  133.      All arithmetic is cyclical here: negative numbers are
  134. represented by their complements. For example, if you do this:
  135.  
  136. MVI  A,0
  137. DCR  A
  138.  
  139. the A register will contain the value FFH, which you may
  140. interpret as either 255 or -1, depending on the circumstances. If
  141. you increment that, of course you will get zero again. Most
  142. assemblers in fact allow you to write a statement like MVI  A,-1
  143. which is actually exactly equivalent to "MVI A,255".
  144.  
  145.  
  146. 5. Moving Bytes Around
  147.      It's time to see how these instructions fit together to
  148. accomplish something potentially useful. Let's consider moving
  149. several bytes of data (this could easily be text) from one place
  150. to another.
  151.  
  152.              ORG  0100H         ;code begins here
  153. 0100 211101  LXI  H,SOURCE      ;point to source with H-L
  154. 0103 111301  LXI  D,DEST        ;point to destination with D-E
  155. 0106 7E      MOV  A,M           ;fetch byte from memory into A
  156. 0107 EB      XCHG               ;exchange so H-L is now dest
  157. 0108 77      MOV  M,A           ;store byte at destination
  158. 0109 EB      XCHG               ;now H-L is source again
  159. 010A 23      INX  H             ;point to the next byte
  160. 010B 13      INX  D             ;and the next destination
  161. 010C 7E      MOV  A,M           ;get another byte
  162. 010D EB      XCHG               ;switch to destination again
  163. 010E 77      MOV  M,A           ;store the byte
  164. 010F EB      XCHG               ;switch back to source
  165. 0110 C9      RET                ;all done, return.
  166. 0111 6869    SOURCE: DB   'hi'  ;data: the bytes we will move,
  167. 0113 3F3F    DEST:   DB   '??'  ;and where we'll put them
  168. 0115         END                ;end of source file
  169.  
  170.      In the middle is the source code, with comments to the
  171. right. On the left I have put the actual addresses and
  172. instruction bytes that will result. (Most assemblers can produce
  173. a "listing" [LST or PRN] file just like this, for your
  174. reference.) If you actually assembled this "program" into a COM
  175. file, it would contain the 21 bytes of instructions and data
  176. shown on the left, at addresses 0100-0115H.
  177.      What's going on here?  "SOURCE:" and "DEST:" are labels --
  178. the assembler will figure out the actual addresses where they
  179. will wind up and will keep track of those (16-bit) values. When
  180. you refer to SOURCE, for example, the assembler will substitute
  181. the address of the label -- in this case 0111H -- so the
  182. statement "LXI H,SOURCE" is actually "LXI H,0111H". Similarly for
  183. DEST. The "DB" instruction will accept text characters in single
  184. quotes as shown. (We could have written "DB 68H,69H" instead of
  185. "DB 'hi'", since those are in fact the ASCII codes for these
  186. letters, but it wouldn't have been as clear what we meant).
  187.      Remember that the "M register" actually refers to whatever's
  188. in memory at the address in the H-L register. Putting the address
  189. SOURCE in the H-L pair automatically makes "M" refer to the byte
  190. at that address, namely the 'h'. So "MOV A,M" fetches that 'h'
  191. into the A register. Then the program exchanges D-E and H-L, so
  192. that it's now DEST in the H-L pair, and "M" refers to the byte
  193. there (the first '?'); and it stores the 'h' there. Then it
  194. exchanges back again, increments both "pointers" so that they
  195. point to the next byte of data (the 'i') and the next destination
  196. (the second '?'), and does it again. Then we're finished, and the
  197. program returns control to the operating system.
  198.      You can write, assemble, and run this little program. You
  199. might even try modifiying it, e.g., extend it to move three or
  200. four bytes instead of just two. But, it is ridiculously easy to
  201. crash your computer when doing assembly language programming, so
  202. don't leave disks in the machine that you don't have copies of,
  203. and don't be afraid to push the Reset button if disaster strikes.
  204.      If you had used some higher-level language to write
  205. something like:
  206.  
  207. 100 DEST$="??"
  208. 110 SOURCE$="hi"
  209. 120 DEST$=SOURCE$
  210.  
  211. your compiler would have generated machine instructions similar
  212. to those above, but about 10 times more. (When you program in
  213. assembly language, you don't have to include a single byte that
  214. you don't need.)
  215.      This program was no big deal and nothing visible happened.
  216. But be patient; there's a lot more to learn.
  217.