Code_ The Hidden Language of Computer Hardware and Software - Charles Petzold [108]
Multiply: PUSH PSW ; Save registers being altered
PUSH BC
SUB H,H ; Set HL (result) to 0000h
SUB L,L
MOV A,B ; The multiplier goes in A
CPI A,00h ; If it's 0, we're finished.
JZ AllDone
MVI B,00h ; Set high byte of BC to 0
MultLoop: DAD HL,BC ; Add BC to HL
DEC A ; Decrement multiplier
JNZ MultLoop ; Loop if it's not 0
AllDone: POP BC ; Restore saved registers
POP PSW
RET ; Return
Notice that the first line of the subroutine begins with a label, which is the word Multiply. This label, of course, actually corresponds to a memory address where the subroutine is located. The subroutine begins with two PUSH instructions. Usually a subroutine should attempt to save (and later restore) any registers that it might need to use.
The subroutine then sets the contents of the H and L registers to 0. It could have used the MVI (Move Immediate) instructions rather than SUB instructions for this job, but that would have required 4 instruction bytes rather than 2. The register pair HL will hold the result of the multiplication when the subroutine is completed.
Next the subroutine moves the contents of register B (the multiplier) into A and checks if it's 0. If it's 0, the multiplication subroutine is complete because the product is 0. Since registers H and L are already 0, the subroutine can just use the JZ (Jump If Zero) instruction to skip to the two POP instructions at the end.
Otherwise, the subroutine sets register B to 0. Now the register pair BC contains a 16-bit multiplicand and A contains the multiplier. The DAD instruction adds BC (the multiplicand) to HL (the result). The multiplier in A is decremented and, as long as it's not 0, the JNZ (Jump If Not Zero) instruction causes BC to be added to HL again. This little loop will continue until BC is added to HL a number of times equal to the multiplier. (It's possible to write a more efficient multiplication subroutine using the 8080 shift instructions.)
A program that wishes to make use of this subroutine to multiply (for example) 25h by 12h uses the following code:
MVI B,25h
MVI C,12h
CALL Multiply
The CALL instruction saves the value of the Program Counter on the stack. The value saved on the stack is the address of the next instruction after the CALL instruction. Then the CALL instruction causes a jump to the instruction identified by the label Multiply. That's the beginning of the subroutine. When the subroutine has calculated the product, it executes a RET (Return) instruction, which causes the Program Counter to be popped from the stack. The program continues with the next statement after the CALL instruction.
The 8080 instruction set includes conditional Call instructions and conditional Return instructions, but these are used much less than the conditional Jump instructions. The complete array of these instructions is shown in the following table:
Condition
Opcode
Instruction
Opcode
Instruction
Opcode
Instruction
None
C9
RET
C3
JMP aaaa
CD
CALL aaaa
Z not set
C0
RNZ
C2
JNZ aaaa
C4
CNZ aaaa
Z set
C8
RZ
CA
JZ aaaa
CC
CZ aaaa
C not set
D0
RNC
D2
JNC aaaa
D4
CNC aaaa
C set
D8
RC
DA
JC aaaa
DC
CC aaaa
Odd parity
E0
RPO
E2
JPO aaaa
E4
CPO aaaa
Even parity
E8
RPE
EA
JPE aaaa
EC
CPE aaaa
S not set
F0
RP
F2
JP aaaa
F4
CP aaaa
S set
F8
RM
FA
JM aaaa
FC
CM aaaa
As you probably know, memory isn't the only thing connected to a microprocessor. A computer system usually requires input and output (I/O) devices that make it easier for humans to communicate with the machine. These input devices usually include a keyboard and a video display.
How does the microprocessor communicate with these peripherals (as anything connected to a microprocessor that isn't memory is called)? Peripherals are built so that they have an interface similar to memory. A microprocessor can write into and read from a peripheral by specifying certain addresses that the peripheral responds to. In some microprocessors,