This looks like a single genius strike
Na, not really, they are essentially the flags that can come out of an ALU, or more correct, they are the outputs that can be brought directly out of an ALU.
Just imagine the idea is to separate instructions and branching to reduce instruction set size and improve reusability. Looking at an ALU with this goal in mind will directly lead to these flags.
So if at all, the genius is to do away with complex test-and-branch instructions and create a modular set of branches to be used independent from test/arithmetic/logic instructions.
How this scheme has been invented?
Two reasons:
separating ALU instructions and branching
simplifying logic to create less expensive machines.
These flags come right out of the ALU, so the most simple logic is to store them right away. Using a CC based logic, like the /360 does means they need to be converted into a (more compact) condition code.
Where did it Come From
Both approaches (Flags and CC) are a step away from branch instructions being part of computational operations - that is testing for implied operations, like the IBM 7030 did with its byzantine flood of branch instructions or the PDP-8 with its way smaller, but still entangled set (*1). Such instructions would perform a certain test - like if the accumulator is zero and branch within the same operation.
It might not seem big at first, after all, doing a Branch When Zero after an Addition feels like testing the accumulator for zero. But it isn't, it now tests a recorded result. Branches and condition yielding operations are now separate entities and can be combined in new ways. New operations that need to be tested, like I/O, do no longer need a separate set instructions with or without testing. Call it a step toward RISC if you like.
Essentially to approaches were to be taken:
- Using hardware related flags, or
- Using abstract conditions
Using Flags simplifies the separation of operation and related branch by using the ALU output as a logical layer of separation, while condition code goes a step further by freeing the branching from any fixed framework defined by ALU operation or whatever the source of the previous operation is.
Lets take a look at them in detail:
Using Flags
Advantages of Using Flags:
- Simple hardware
- Straight implementation
- No layer of abstraction to be performed
- One pair of branches per flag
Disadvantages of Using Flags:
- Weak abstraction layer
- Testing flags is testing hardware states, not logical conditions.
- Meaning logical ties to ALU operations
- Extensions will need their own flags
- Or their meaning gets rather confusing (*2)
- Getting quite bloaty when extended or non ALU operations are added
- Each additional flag adds two more branch instructions
Using Condition Codes
Advantages of Using a Condition Code
- Abstraction layer between CC generation and CC usage
- Hardware independent meaning of the used codes
- Easy reuse for non ALU operations
- Only 8 (*3) branch variations needed to test them
- Using 16 branch variations (*4) allows to test arbitrary combinations (*5)
- Improving code density.
Disadvantages of Using Condition Codes
- Higher hardware effort for encoding and decoding
Conclusion
The use of flags on the PDP-11 offers the freedom of separation of branching from testing/computing, while needing the least hardware effort, while, just 4 flags, being still not far from the instruction set cost (*6) of using condition codes.
The important part here is to keep in mind that the PDP series was always about cost - the PDP-11 especially as being driven by the success of the DG Nova heavy cutting DEC's PDP-8 sales. And cost (in terms of needed gates) was again what drove (early) microprocessor development.
The rest is history.
*1 - Well, With the Link (Carry) bit the PDP-8 had already one of them as dedicated flag, as its value was as well needed for multi precision operations.
*2 - The Z80s implementation of Overflow being a nice example
*3 - The fact that a CC encoding needs only 8 branches to do the same where a flag encoding need 16 is simply due redundancies within flag encoding. For example an outcome can never be ZERO and NEGATIV at teh same time. CC encoding does not cover this, thus being more compact.
*4 - Like the /360 did.
*5 - For example ne branch can test for Zero or Negative at once. Doing the same for a 4 bit flag word requires 256 branches.
*6 - Number of instruction encoding needed.