This morning I figured out what I did wrong with the SAP-1 Program Counter. But I think I also found a potential bug in the original design.
The problem with the Program Counter nagged at me all day yesterday. Why would it work in the PC testbench but not in the larger system?
I determined that the timing of the clock signal and the PC increment enable lines were different. The wave output display didn't tell me what I needed to know about the state of the J/K inputs at the time the always block in the flip-flop was triggered so I added a $display call -- the Verilog equivalent of debugging with printf calls. This showed me that the FF was behaving exactly as I expected, and it was the timing of its inputs that was causing the problem.
Although I remembered double-checking it, I decided to check again the polarity of the clock signal driving the PC. And found that it was the inverted clock (written CLK) that was supposed to be driving the PC, and I'd used the non-inverted clock. Another oops. This is why verifying with simulation is so handy.
With the correct clock passed to the PC module, the CPU steps through all 6 instructions and halts (instruction at location 05h is a HLT instruction). I have not yet verified the proper execution of these instructions, and the output to the simulated LEDs looks suspicious (4 bits of data, 4 bits of high-impedance).
With the PC appearing to work properly using the edge-clocked version, I decided to debug the level-clocked master/slave version. My implementation using always blocks wasn't working in the SAP-1 environment, so I swapped it out for an version based on a literal translation of a J/K master/slave flip-flop circuit implemented using NAND gates. This passed all my testbench tests, but double-incremented in the SAP-1. It appears that the SAP-1 changes the J and K inputs while the clock input is high. This does not affect the A-suffix, edge-triggered version, but causes the non-A, master/slave version to double-count.
I'm not terribly surprised this doesn't work, as the datasheet for the SN74107 explicitly warns against this, stating "For these devices, the J and K inputs must be stable while the clock is high." I would consider this a flaw in the book, as the text discusses only the master/slave version of the 74107 flip-flop, calls out the non-existent 74LS107 in the parts list and schematic, and does not mention that the 74LS107A is an edge-triggered device.
Oh well... they're not going to issue a correction for an out-of-print book that was last updated 25 years ago.
After I get the entire SAP-1 emulation running properly I'm planning to update all the part definitions to include typical propagation delays. Maybe after I do this I'll revisit the master/slave version of the 74107 and see if it behaves better.