Debugger for the PC
What is it?This Debugger enables you to observe the behaviour of the 8088/8086/80x86 processor step by step. I built it to debug some hardware problems I had with some of my PCs. It can only be used in a PC with an ISA bus.
The storyIn 1986 I got a Junior for free but it was broken. For those who don't know the Junior, it is a 6502 based computer, quite the same as the KIM. It can be extended with lots of cards. I made some of the cards myself but they did not always work well right from the start. So I decided to build a device to check the status of the bus at a given moment: the Debugger.
Later I started to work for a company that assembled PCs on request. Quite soon I ran into motherboards that didn't work at all so I decoded to build the PC version of the Debugger.
I decided to build a debugger that can be used both in the 8-bits ISA slot of a XT as in a 16-bits ISA slot of an 80286 or better.
The ideaThe idea itself is quite simple: stop the processor (CPU) and check the levels of all the lines you are interested in. But how are we going to stop the CPU? Most CPUs I know have an input to perform this job. For the PC this is the IOCHRDY input at pin A10 of the ISA bus. Make it (L) and the processor stops. Almost as simple like that!
The next thing is to determine the levels of all the lines you are interested in. The simplest way is to use some buffers and a lot of LEDs. But to make life easier I suggest using an intelligent hexadecimal display. Just connect the address and data lines to these displays and you have a nice hexadecimal output.
Almost as simple like that!If you pull IOCHRDY of an IBM PC/XT or compatible (L), the 8088 outputs the address 0FFFF0h. Normally you should find the value 0EAh at the data bus. 0EAh is the opcode for the instruction "jmp far". If not, there is something wrong.
If the values are OK, there is nothing we can do now unless we install a circuit that enables us to step to the next address. Now we can check what the PC is doing byte by byte.
Clicking and clicking and clicking.....Using the Debugger with the Junior I soon discovered a serious shortcoming: the usability to handle loops and subroutines. This meant you could be clicking that little knob for hours before you reached the address where the fault appeared. So I added an address discriminator. This little device stops the processor at the chosen address. If the CPU doesn't stop, it didn't arrive at the chosen address which means that it started doing something else then you expected. The reason can be faulty hardware, an error in the software or you, misinterpreting the hardware and/or software :-).
The hardwareThe schematic can be divided in six parts:
- The display part.
- En- or disabling the various parts of the circuitry.
- The logic to step the processor.
- The address discriminator.
The display part
The main part of the display are the HP HTIL311A intelligent LED displays. They are called "intelligent" because in the display are included the needed drivers for the actual LEDs and the logic to translate four digits into hexadecimal numbers.
The various data inputs are not driven directly by the data and address lines but are driven by 74ALS573 latches first. Why? I had troubles the original Debugger when connecting it with a Commodore 64 And I had to make sure that the data and address were latched during the upper phase of the clock PHI2. The HTIL311A has its own latch pin but for one or another reason it didn't work good enough. Since then I always used the 573s and so far they never let me down.
The two 573s for the data lines just act like two buffers. The three 573s buffering the address lines are clocked by the ALE (Address Latch Enable) signal.
When the board is used to check a XT or compatible, the data bits D8..15 and addresslines A20..23 don't need to be displayed. I connected the BI (Blanking Input) of the HTIL311As directly to the GND input at pin D18. Is the card inserted into a 16-bits slot, then this pin will be connected to GND, the BI pins will be activated and the HTIL311As will be enabled. Will the card be inserted into an 8-bits slot, Resistor R5 will make sure that the HTILS311As wil stay disabled.
En- or disabling the various parts of the circuitry
Before the rest of board is explained, I first have to explain the use of the various switches on the board. Except the dip switches, actually there are none present. Instead you will find the jumpers J1, J2 and J3. My original hand made board had those switches on the board and that did not work out well. You had to stick your hand in the machine, running the risk to be scratched by the pins of ICs, and then had to set very tiny switches.
J1 replaces a push button with tw states. While in rest, pins 1 and 2 are connected, the NC (Normally Closed) state. When pushed, pins 3 and 2 are connected. These two pins are NO (Normally Open).
The four pin block J2 "contains" two switches:
- an On/Off switch between pin 1 and 2, J2a, to en- or disable the address discriminator. Open means disabled.
- an On/Off switch between pin 3 and 4, J2b, to en- or disable the whole board. Open means disabled.
J3 does nothing and is purely used as a dummy. But three together have 9 pins and placed in such a way on the board that they a 10-pin header wher pin 10 serves as a key pin. Now you can connect a little box using a flatcable and 10-pin connector to the board. It is obvious that the box contains the actual switches.
What about the dip swiches? I first thought about replacing the dip switches by 8-pins headers but that only made creating the board more difficult. So I decided to keep the original dip switches in place. If you want to place the dip switches or something else in the controller box as well, solder headers in the place of the switches.
The logic to step the processor
First we have to debounce the switch that triggers the stepping. This is done by a flipflop, made out of IC3CC and D, two 74LS00 NAND gates. The output of IC3D is (L) when the push button for stepping is not pressed. This (L) causes IC1A, a 74LS393 4-bits counter, to count. Let's see what the various outputd do:
count D C B A 0 L L L L 1 L L L H 2 L L H L 3 L L H H 4 L H L LThe 393 counts up on the negative edge on its clock input at pin 1. This clock is fed by the CLK signal from the ISA bus but not directly: it first goes through an OR gate, IC4D. Output B of the 393 is in fact the pin that controls IOCHRDY. (forget about IC4C, IC3A and IC3B for the moment) The first two clocks nothing will happen; B will stay (L). The next two clocks output B will become (H) what will allow the CPU to end the "waiting" state and to start the next cycle. At the fifth clock output B will become (L) again and halt the CPU. At the same time output C wil become (H). As output C has been connected to the second input of OR gate, its output becomes (H) as well and therefore blocking any further clock cycles towards the 393.
When pushing the button, the output of NAND gate IC3D becomes (H), which will reset the 393. Output B was (L) and will stay (L) during this phase.
When releasing the button, the 393 start counting again and the CPU will show the next address and byte(s).
Enabling the stepping circuitry
Of course it makes no sense in connecting output B of the 393 directly to the IOCHRDY, in this way the PC can never run at full speed. That's where OR gate IC4C comes in. It combines the output of output B of the 393 with the state of the switch from jumper J2b. As long it is switched off, pull-up resistor R2 makes sure taht the input at pin 10 will be (H) and thus the OR gate will only output a (H) as well. The moment the switch is closed, the stepping circuit will halt the PC.
The address discriminator
The next step in the evolution of the debugger was adding the address discriminator. It enabled me to stop the PC when the program reached a certain address. It is made out of three 74LS688s, IC5, IC2 and IC11. The 688 is a 8-bit comparator. One side is connected to the address bus, the other one to an 8-bit dipswitch plus pull-up resistor-array. The idea is simple: once the G-input is enabled and both 8-bit sides see the same input, pin 19 outputs a (L).
Enabling the address discriminator
Pull-up resistor R1 and the switch at J2a take care of that. The moment the switch is open, the (H) at the G input of IC5 will block any action. Closing the switch will enable the address discriminator.
Mixing the output of the address discriminator and the stepping circuit All is needed to mix these two signals is an AND gate. We don't have one at hand but the two left-over NAND gates can perform the same trick.
How to use the debuggerOne important remark has to be made first: the debugger cannot be used to debug programs running in RAM on normal PCs. Normally PCs use dynamic RAM as storage and that has to be refreshed from time to time. And then we are talking about miliseconds. The moment we halt the CPU, we also halt the refreshing mechanism and the content of the RAM will fade away. The data read from the next addresses will be unreliable and the PC will certainly crash when the debugger is switched off.
Exception are, for example, Sergey's XI-8088 and his Micro 8088. They use static RAM and thus can be halted without any problem.
So the debugger can be used for those parts of the BIOS where no RAM is needed.
If a PC won't start up, that is: no screen, no sound, then it is a good moment to insert the debugger card. Before doing that, the PC should be turned off, of course. Make sure that the stepper is enabled and the address discriminator disabled. Now turn the PC on. Assuming that it is a XT or compatible you should see "FFFF0 EA" on the displays. When clicking the stepper again and again you most probably see:
FFFF0 EA FFFF1 5B FFFF2 E0 FFFF3 00 FFFF4 F0 FE05B ??The first 5 bytes mean "jmp far F000:E05B" and are used by most PCs. But what you find at the location 0FE05Bh really depends on the BIOS. This means you have to have the source code of the used BIOS so you will know what can be expected to be seen. If you don't have the source code, making further use of the card has no added value because, unless you are a genius, the numbers on the screen won't probably make any sense.
Regarding the "most probably" here above: all BIOSes I have seen so far start with "EA" as the first byte at 0FFFF0h. But it is not a must. Not seeing "EA" does not mean you're in trouble but only the source code can explain why you don't see that "EA" there. If you don't have the source code, IMHO you are in trouble.
But what if you don't see "FFFF0" but something else? You SHOULD see "FFFF0", anything else is bad. Any BIOS for the PC is based on the fact that a 80x86 CPU starts at address 0FFFF0h and nothing else.
A real example: I have seen an IBM XT starting up with "FF" in the data display. This was caused by a brooken 74LS245 between the CPU and the ROM.
Having the source code will easy things enormously. But even then you can be clicking for "hours" before getting to the part in the BIOS that causes the problem. That's where the address dicriminator will easy the job. Set an address what the CPU should pass in your opinion, enable the discriminator and disable the stepper. If things go fine, the CPU should stop at this address. If it doesn't, something has gone wrong: either you misinterpreted what the CPU should do or the CPU took another path for one or another reason. I will assume from now on that you know what the CPU normally should do.
Just set another address more towards the start and reset the PC. At the end you should get to the part where the CPU behaves in another way than you would have expected. The most simple thing would be that the CPU does so because an error situation has been detetected and therefor the CPU was told to act differently. I have seen a 65816 behaving weird because its BEQ (Branch if EQual) instruction was brooken.
Another real example: my 6502 equiped Elektor Junior didn't start up. After clicking and clicking and clicking I just couldn't find an error. This particular case was the reason why I added the address discriminator. Using the discriminator I found out that the Junior even crashed when asking to go to addresses that I could reach by only clicking. So I replaced the EPROM. And then the Junior worked fine. My conclusion: the original EPROM had become too slow to output all of its bits within the time window that the 6502 expected valid data to be seen. But not too slow for the programmer to verify its content and certainly not too slow for the debugger.
You can email me here.