home *** CD-ROM | disk | FTP | other *** search
- CP/M Assembly Language
- Part IV: Flags
- by Eric Meyer
-
- Last time we learned how to make an assembly language
- program talk. I hope this was entertaining enough to keep your
- interest a while. There are many dozen 8080 instructions, and
- you'll need to know a lot more of them before you can attempt
- more sophisticated tasks.
- This time we'll learn about that mysterious "F" register,
- the Flags: what they are, and how to use them to make decisions.
- But first (you'll see why) we must learn a little more
- arithmetic.
-
-
- 1. Addition and Subtraction
- We've already seen the INC and DEC instructions, which add
- or subtract 1 from any register. The accumulator ("A" register)
- can also do fancier arithmetic.
- The ADI and SUI (add and subtract immediate) instructions
- can be used to add or subtract a value from the accumulator. For
- example:
-
- ADI 12
-
- would add 12 to whatever was in "A" before.
- Similarly, the ADD and SUB instructions add or subtract the
- contents of any other register from the accumulator; thus:
-
- SUB C
-
- would subtract the value in "C" from the value in "A".
- You can even use the instructions ADD A, which adds A to
- itself (doubles A), and SUB A, which subtracts it from itself
- (leaving, oddly enough, zero).
-
-
- 2. The Flags
- The really powerful feature of a computer program is that it
- can make decisions; depending on the data you supply, it doesn't
- have to execute the same way every time. No doubt you're already
- familiar with statements like:
-
- 210 IF RESULT<0 THEN GOTO 1000
-
- The same sort of thing can be done in assembly language, but
- it's more cumbersome, and it happens in stages. You perform some
- operation, like arithmetic or comparison, and one or more flags
- are set as a byproduct; then you can JMP or CALL conditionally,
- depending on the state of the flags.
- The "F" register contains 8 bits, each of which can be
- thought of as a little flag that's either on or off, yes or no.
- The two most-commonly used flags are called Zero and Carry, or Z
- and C for short.
- Basically, the Zero flag is set when an operation results in
- a register going to zero; and the Carry (or borrow) flag is set
- when an accumulator operation results in overflow or underflow.
- Each flag retains its status until changed by the next operation
- that affects it.
- As a result, for each arithmetic operation, you have to learn
- exactly how it affects the flags. Here's a summary so far:
-
- INR, DCR -- Z means register is now 0
- C NOT affected
- ADD, ADI -- Z means A is now 0
- C means overflow (>255)
- SUB, SUI -- Z means A is now 0
- C means underflow (<0)
-
- Occasionally this is real confusing. Notice, for example,
- that while SUI 1 affects the Carry flag, DCR A (which is
- otherwise identical) does not.
- Note also that 16-bit operations INX and DCX do not affect
- any of the flags. If you want to know whether a 16-bit number has
- been decremented to zero, you have to do something devious, like
- add its two bytes together and then look at the Z flag.
-
-
- 3. Comparison
- The comparison operation is just like subtraction, but it
- doesn't change the accumulator; it only changes the flags. The
- instruction is CMP for a register, or CPI for an immediate data
- value. Thus, for example,
-
- CPI 12
-
- affects the flags exactly as SUI 12 would have, but without
- actually doing the subtraction. In particular, if the value in A
- had actually been 12, the Zero flag would now be set.
- Thus the Z flag tends to mean "Yes, that's it", as well as
- literally "Zero". Similarly, if the value in "A" had been less
- than 12, the Carry flag would now be set. So the C flag tends to
- mean "Less than", as well as literally "Carry/borrow".
-
-
- 4. Conditional Branches
- It should now come as no surprise that there's a whole set
- of JMP and CALL instructions that execute conditionally,
- according to the status of the flags. Some of these are:
-
- JZ -- JMP if Z flag set CZ -- CALL if Z flag set
- JNZ -- JMP if Z flag Not set CNZ -- CALL if Z flag Not set
- JC -- JMP if C flag set CC -- CALL if C flag set
- JNC -- JMP if C flag Not set CNC -- CALL if C flag Not set
-
- In order to illustrate, let's rewrite a program from Part
- III to show that your computer really cares:
-
- BDOS EQU 0005H
- ORG 0100H
- MVI C,9 ;print string
- LXI D,QUESTN ;(this one)
- CALL BDOS
- MVI C,1 ;get input
- CALL BDOS ;character is now in A
- LXI D,YESMSG ;start with YESMSG
- CPI 'Y' ;was input "Y"?
- JZ YES ;jump to YES if so
- NO: LXI D,NOMSG ;NO, change to NOMSG
- YES: MVI C,9
- CALL BDOS ;print the answer
- RET ;all done
- ;data
- QUESTN: DB 'Are you excited? (Y/N) $'
- YESMSG: DB ' Great!$'
- NOMSG: DB ' Hm, too bad.$'
- END
-
- As you can see, after asking the question and getting one
- character from the keyboard, the program sets up to reply with
- YESMSG. It then compares the character to "Y": if it matches, the
- Z flag is set, so it JMPs to YES and prints YESMSG. If it doesn't
- match, it falls through to NO instead, and switches to NOMSG.
-
-
- 5. Loops
- Another very common use of the Z flag is in loops. Suppose
- you had expanded the BYTEMOV program of Part III to copy 10
- characters; you would have had to write:
-
- CALL MOVBYT, CALL MOVBYT, ...
-
- 10 times. Now you have a far better way to do this:
- MVI B,10 ;do ten times
- LOOP: CALL MOVBYT ;move a byte
- DCR B ;count down
- JNZ LOOP ;loop if not zero yet
-
- Obviously, you can have the loop execute as many times as
- needed just by changing the value in "B". This is the assembly
- language equivalent of a FOR...NEXT or WHILE loop.
-
-
- 6. The Carry Flag: 16-bit Arithmetic
- Let's end as we began, learning instructions for arithmetic.
- You can also add and subtract 16-bit (word) values, although it's
- not quite as simple as with single registers. For addition, the
- H-L registers can function as a sort of "accumulator": the
- instruction DAD (double add) adds the contents of a register pair
- to H-L.
- Thus DAD B adds B-C to H-L. Similarly DAD D adds D-E. (You
- can even use the instruction DAD H, which adds H-L to itself.)
- The DAD instruction does not affect the Z flag; but it does
- use the C flag to indicate whether (16-bit) overflow occurred.
- There is no "immediate" 16-bit add; you have to load the value
- into a register pair, then add it.
- Subtraction is harder; the Z80 CPU has a double-subtract
- instruction, but the 8080 does not. You have to do it one byte at
- a time, in the accumulator. If you wanted, say, to subtract D-E
- from H-L, you might try the following:
-
- MOV A,L ;get L (the low byte)
- SUB E ;subtract E from it
- MOV L,A ;and put it back
- MOV A,H ;get H (the high byte)
- SUB D ;subtract D from it
- MOV H,A ;and put it back
-
- Unfortunately, this would sometimes not work, because it
- doesn't allow "borrowing" from the "H" register in the event that
- "E" is bigger than "L".
- The second subtraction (SUB D) isn't affected by the outcome
- of the first. (Imagine subtracting 9 from 26; if you forget to
- borrow, you get 27 instead of 17.)
- This is where the Carry flag comes in. Remember that if "E"
- is bigger, the "SUB E" instruction is going to give a result
- modulo 256, and the C flag will be set. Then, you need to
- remember to subtract an extra 1 from the high byte if the C flag
- is set.
- This is where a new set of arithmetic instructions comes in:
-
- ADD, ADI ---> ADC, ACI : "add with carry"
- SUB, SUI ---> SBB, SBI : "subtract with borrow"
-
- These add (or subtract) 1 more if the C flag is set. If you
- change the "SUB D" above to "SBB D", the problem will be solved.
- This is why the C flag is called "Carry", and functions as it
- does.
-
-
- 7. Coming Up . . .
- Next time we'll look at the remaining big mystery: the
- stack. Then we can begin to write some useful routines.