Part Six:
Arithmetic and Bit Instructions

    Arithmetic is essential to any programming language; almost every application requires the use of simple arithmetic functions.  All of the Z80’s 8-bit arithmetic instructions operate using the A register.  These instructions are: add (add), adc (add with carry), sub (subtract), sbc (subtract with carry), AND (logical and), OR (logical or), XOR (exclusive or).
    Add will add the instruction operand to the A register, storing the result in A.  <add a,b>, <add a,(hl)>, <add a,$10>.  The flags affected are the carry (set if the addition results in a number larger than 255, otherwise reset); the zero (set if the result is zero, otherwise reset); the sign (set if the resulting bit 7 is set, otherwise reset); parity / overflow (set if overflow -- not important).
    Subtract is similar except that A isn’t written explicitly as the operator.  <sub 32>, <sub c>, <sub (ix)>.  Each of these subtract the operand from the A register.  The carry is set if a borrow is needed (if the operand is greater than A), otherwise reset (if the operand is less than or equal to A).  The zero flag is set if the resulting value is zero (operand and A are equal), otherwise reset (not equal).  The sign flag is set and reset under the same circumstances as the carry flag (result positive or negative).
    The idea of adding and subtracting with a carry is useful in situations where you are combining registers unconventionally, forming 24-bit and 32-bit register aggregations.  Part of these instructions input is the carry flag: if the carry is set, then <adc a,7> will add one to whatever the result is; <add hl,$f000 / adc a,0> will handle ahl as a 24-bit register troika (not very uncommon, as you will see).
    Add with carry adds the operand and the carry flag to A.  Flags are set identically to the add instruction in any condition.
    Subtract with carry (sbc) subtracts the operand and the carry flag from A.  Flags are set identically to the sub instruction in any condition.

    ANDing, ORing and XORing must be done on the binary level.  A logical AND takes two binary numbers and sets the resulting bit iff the same bit is set in both the operand and the operator.  For instance, <and c> will the have the following result with %11010110 in A and %01000101 in C.
 
 
 
 

%11010110
%01000101
and:
%01000100

    AND is used most often as a bitmask.  If you only care about, say, the bottom four bits of (hl), then <ld a,%1111 / and (hl)>.  It will and A with (HL), storing the resulting number to A. If (HL) is %10110010, then:
 
 
 

%10110010
%00001111
and:
%00000010

    If you want the result to be remembered in (HL), then you need an additional <ld (hl),a>.
    OR is similar, save that it only sets the resulting bit iff the same bit is set in either the operand or the operator.  <or $30> will have the following result when A is %10110001:
 
 
 

%10110001
%00110000
or:
%10110001

    Exclusive OR is the same as OR, except that if both bits are set, then the resulting bit will be reset.  XOR is useful in toggling between bits or bytes.  If you have a bit in a number that unknown, then you can use XOR to complement the bit.  <ld a,%1000 / xor (iy+5) / ld (iy+5),a> will complement bit 3 of (iy+5).  Bit 3 is set in a and XORed with (iy+5), loading the result back into (iy+5).  These three instructions will toggle the text inverse flag:
 
 
 

%11101011
%00001000
xor:
%11100011

    Bit toggling can be accomplished in this way and byte toggling can be done similarly.  Suppose that it’s necessary to switch between two numbers each time a certain portion of code is executed.  There will always be some value that can be XORed against one of the numbers, which will derive the other.  For instance, if you wanted to switch between $3c and $0a, you could use %110110 as the toggle byte:
 
 
 

%00111100 ($3c)
%00110110 (toggle byte)
xor:
%00001010 ($0a)

 
 
 
 
 

%00001010 ($0a)
%00110110 (toggle byte)
xor:
%00111100 ($3c)

    AND, OR and XOR will affect the flags in the following manner:  the zero flag is set iff the result of the operation is zero; the sign flag is set if the bit 7 of the result is set; the carry flag is always reset; the parity / overflow flag is set iff the parity is even.

    The Z80 also has arithmetic instructions that handle 16-bit numbers.  These instructions are add, adc and sbc and require HL as the operator:  <add hl,de>, <adc hl,$fc00>, <sbc hl, bc>.
    You can never use IX or IY in the same instruction as HL (because the opcodes add $dd or $fd, if you recollect).  But you can, however, change any instruction that uses HL to use IX and IY: <add ix,de>, <sbc iy,6> are permissible, but you cannot do <adc ix,hl>.  Also, SP is a valid operand for all 16-bit arithmetic instructions: <add ix,sp>, <add hl,sp>.  You can load a register pair with SP in this way: <ld ix,0 / add ix,sp>.  Also these three instructions do exactly what you would expect with the flags, except add, which will set the carry iff there is a carry, but affecting no other flags.
 One last thing you may notice is that there is no 16-bit sub instruction.  Since AND and OR reset the carry, though, you can do: <or a / sbc hl,de>.  ANDing or ORing A with itself has no affect on the A register, but always resets the carry.

    Here's an example of a call that will determine whether the square on a chess / checker board (numbered 0-63, starting at the top left) is white or black, while at the same time returning the row and column number of that square in registers B and C:
 

return_info:

 and %111111   ;interest in only bottom six bits of A (0-63); modulus 63
 ld b,a    ;load a into b, a and b are now both the same
 srl b    ;divide b by 2
 srl b    ;divide b by 2 again
 srl b    ;and again for a result of b = original b / 8

;the original number between 0 and 63, or %0 and %111111 is now a number between 0 and 7.
;Because of the special way binary works, this number is the row number of the number we
;started with (still in A).  The third bit of A will be incremented every 8th increment, making the
;third bit and the two bits above it representive of the row and bits 0,1 and 2 representive of the
;column.  By dividing by 8, we put bits 3,4 and 5 into where bits 0,1 and 2 were (every division by
;two shifts all bits one to the right).

 and %111

;the original number in A is now only the bottom three bits of that number, or the column

 ld c,a    ;load the column into c; we now have a & c = col, b = row
 xor b

;XOR A with B, column with row.  B is still the row, C is still the column.  The effect of this XOR
;is that bit 0 of A is set iff the square is black.  Bit 0 of the row and bit 0 of the column tell
;whether the row / col is odd or even, or if the first obtained value for row is white or black in the
;fist column of rows and vice versa.  Looking at a chess board, you will see that when a column in
;the first row is white and when a row in the first column is white, the square that the row and
;column have in common will be white, in other words, the square represented by that row and
;column number will be white if both the row and column numbers are even.  If either the row or
;the column is black (odd), then the square will be black, but if both the row and the column are
;black, then the square will be white.  So, if either operand is set, the result will be set, if both are
;set or if neither are set, the result will be reset: XOR!  So, bit 0 of A is now set if the square is
;black, reset if the square if white.

 and %1
 ret

;get rid of all bits but bit 0, another affect of the AND instruction is that if the resulting number in
;a is zero, then the zero flag will be set, and reset if it isn't zero (the only other option is one).  So,
;to check whether the square is white or black after returning you could either check the zero flag
;or check to see if A is 1 or 0.  The column is returned in C and the row is returned in B, as well.
 

    Don't worry if you didn't understand this example -- it requires an excellent understanding of binary.  You should at the very least see how useful ANDing bitmasks can be in a program.