Electronic – How to produce a middle C on intel 8080

8080assemblysound

This is a example problem in my book. Assuming pin 5 of port 4 is connected to an amplifier that drives a loudspeaker, the solution is given as,

The frequency of middle C is $$f=261.63\ \text{Hz}$$
So, the time period is,
$$T=\frac1{f}=3822\ \mu s$$

The program to produce a square wave with that period is,

LOOP1:    OUT 4H     ;Send bit to speaker
          MVI C,86H  ;Set count to 134
LOOP2:    DCR C      ;Count down
          JNZ LOOP2  ;Test count
          CMA        ;Reset bit 5
          NOP        ;Fine tune
          NOP        ;Fine tune
          JMP LOOP1  ;Go for next half cycle

The number of T states is given as, OUT(10), MVI(7), DCR(4), JNZ(10 if true, else 7), CMA(4), NOP(4), JMP(10).

With a clock frequency of 1 MHz, LOOP2(for half cycles) runs for 1912 microseconds, which is close enough. LOOP1 should run again sending the complement of what was previously in bit 5 of port 4. But I think it doesn't.

When LOOP2 ends, the accumulator has 00H left over from C register. CMA changes the accumulator to FFH. NOP and JMP don't change the accumulator. So then the LOOP1 iterates for the next half cycle, OUT sents accumulator contents to port 4, i.e, FFH whose bit 5 is 1, everytime. So there is not a square wave, it's just a high signal. Then how does it produce a middle C?

Best Answer

DCR C decrements the C register and sets flags; it does not affect the accumulator (aka A register). The only instruction in this sequence which affects the accumulator is the CMA. Thus on each pass through LOOP1, the accumulator will be complemented - bit 5 high on one cycle and low on the next.

Many sources including the 8085 datasheet describe the 8080/8085 ALU as operating directly on the accumulator, but this is an oversimplification. As described in this Ken Shirriff article, the ALU has two temporary registers:

The ALU uses two temporary registers that are not directly visible to the programmer. The Accumulator Temporary register (ACT) holds the accumulator value while an ALU operation is performed. This allows the accumulator to be updated with the new value without causing a race condition. The second temporary register (TMP) holds the other argument for the ALU operation. The TMP register typically holds a value from memory or another register.

...

The ACT register has several important functions. First, it holds the input to the ALU. This allows the results from the ALU to be written back to the accumulator without disturbing the input, which would cause instability. Second, the ACT can hold constant values (e.g. for incrementing or decrementing, or decimal adjustment) without affecting the accumulator. Finally, the ACT allows ALU operations that don't use the accumulator.

For the DCR instructions, the ACT holds a constant, the TMP receives the current contents of the operand register, and an ADD operation is performed; for DCR C, the accumulator remains untouched:

The control lines allow the ACT register to be loaded with a variety of constants. The 0/fe_to_act control line loads either 0 or 0xfe into the ACT; the value is selected by the sel_0_fe control line. The value 0 has a variety of uses. ORing a value with 0 allows the value to pass through the ALU unchanged. If the carry is set, ADDing to 0 performs an increment. The value 0xfe (signed -2) is used only for the DCR (decrement by 1) instruction. You might think the value 0xff (signed -1) would be more appropriate, but if the carry is set, ADDing 0xfe decrements by 1. I think the motivation is so both increments and decrements have the carry set, and thus can use the same logic to control the carry.

Since the 8085 has a 16-bit increment/decrement circuit, you might wonder why the ALU is also used for increment/decrement. The main reason is that using the ALU allows the condition flags to be set by INR and DCR. In contrast, the 16-bit increment and decrement instructions (INX and DCX) use the incrementer/decrementer, and as a consequence the flags are not updated.

There is only a single set of flags in the 8080; DCR affects the Zero, Sign, Parity, and Aux-carry flags.