How to produce a middle C on intel 8080?

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?

• Nobody here uses the 8080 anymore, so you might have more luck if you get your question migrated to retrocomputing – PlasmaHH Oct 22 '18 at 6:46
• The code is correct, and will produce desired result at any wires connected to 8-bit data lines of port 4. Even does not care the initial value of A. What is "With a clock frequency of 1 Hz" - do you clock 8080 with 1 Hz clock? And what problem do you have, exactly? Can you read 8080 datasheet - in particular description and operation of its commands? – Anonymous Oct 22 '18 at 8:55
• I don't understand why you think the accumulator contains 0 after the timing loop ends; C is used as the counter, not the accumulator. – Jules Oct 22 '18 at 10:03
• "With a clock frequency of 1 Hz" - that seems rather slow. Did you mean "1 MHz"? – Bob Jarvis Oct 22 '18 at 11:10
• @Ayatana, that's simply not the case. Read righto.com/2013/07/… – Russell Borogove Oct 22 '18 at 16:20

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.

• The DCR C first copies the content of C register to accumulator, decrements it and then stores the data back into C register. Because the ALU can only work on accumulator. Also, JNZ is a ALU flag which only checks the accumulator. If DCR C didn't affect the accumulator, then, JNZ LOOP2 instruction will be run forever. – Ayatana Oct 22 '18 at 4:26
• I don’t believe that’s true — I’m not an 8080 expert by any means but the sources I’ve looked at don’t indicate that the accumulator is affected by DCR (except DCR A, of course). The flags are affected, however. An internal register in the ALU is changed but written back to C, not A. – Russell Borogove Oct 22 '18 at 4:35
• There’s only one set of flags. I believe that the ALU has internal registers separate from the accumulator A. I don’t see any hint in several different online 8080 instruction references that DCR C affects the contents of the A register. – Russell Borogove Oct 22 '18 at 4:47
• This article describes the 8080/8085 ALU in detail, showing two temporaries in addition to the A register. righto.com/2013/07/… — “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... 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. – Russell Borogove Oct 22 '18 at 5:10
• @Ayatana The status flags in the CPU store the results of the last executed arithmetic instruction; they can be evalutated (but not changed) via e.g. conditional jumps. Hence, JNZ means: "jump if the last executed arithmetic instruction did not result in 0". In your case, the last arithmetic instruction then was DCR C, so the flags are set according to the result of that instruction. When testing the flags, no other register besides the status register is involved. – JimmyB Oct 22 '18 at 10:16