View Full Version : Running DDT and needing console imput

March 25th, 2018, 09:05 AM
I'm trying to debug a program that needs console input while running under DDT, is that possible? I'm using "t" to step though the program. When I get to the input portion it loops waiting for input. Does DDT have a function that allows that? I see using 'i" I can stuff data into the FCB block. I'm sure I'm overlooking something obvious.


March 25th, 2018, 10:23 AM
I think you mean that the program stops and waits for console input? That is "normal" behavior, as soon as the program calls the BDOS then DDT stops controlling it. Trying to work around that gets tricky, as DDT also must be able to call BDOS. The problem is that your program is designed to get console input, so DDT allows it to do that. Does DDT take control again after you enter the program's input?

You will also need to take care when operating a program under DDT when using DDT commands like 'i'. That will corrupt the FCB and DMA area, which your program would probably not expect. Once you start running a program, you need to take care what DDT command you use - if you want to be able to continue running the program.

Maybe I need a little more information about what you're trying to accomplish.

March 25th, 2018, 10:38 AM
There are probably several ways to skin this cat.

The first is to set a breakpoint just before your program calls for input, then set a breakpoint on the return. Clumsy, but it can work.

If you have a second console, you might be able to cobble up a custom DDT that uses this code (=https://www.math.purdue.edu/~wilker/misc/DEVEL/0043/V20BOOT/NDDT.ASM) modified to use a different console device. Sounds like a pretty handy thing to have.

March 25th, 2018, 01:07 PM
Ok, maybe a different approach...

When I read the manual on ddt it says that is awalys loads in the place of the ccp? Is that true?

Can I load and run a program to termination, then load ddt and inspect the memory?

Running ddt after the fact will destroy the dma area when it loads, I assume?

The program I am working on takes a stream of hex and writes it to disk as the binary equivalant. It uses the fcb dma area for the converted hex.

March 25th, 2018, 02:09 PM
Sure, SAVE xxx file should work okay--SAVE is a CCP-resident command, so it won't overwrite anything. Note that SAVE only saves starting from 0100h, however.

March 25th, 2018, 02:57 PM
Ok, maybe a different approach...

When I read the manual on ddt it says that is awalys loads in the place of the ccp? Is that true?

Can I load and run a program to termination, then load ddt and inspect the memory?

Running ddt after the fact will destroy the dma area when it loads, I assume?

The program I am working on takes a stream of hex and writes it to disk as the binary equivalant. It uses the fcb dma area for the converted hex.

If you need to inspect the system area 005CH-00FFH you probably want to run it under DDT, as you originally planned. Does the program exit by "JMP 0"? Or by returning to "CCP"? If you run a program under DDT and it "returns" then DDT should intercept that. Otherwise I believe you'll need to set a breakpoint at 0000H in order to intercepts program exit via "JMP 0". You can simply replace the JMP (0C3H) opcode at 0000H with a RST 7 (0FFH) and leave the BIOS wboot address alone, making it easier to repair later. Don't forget to restore the JMP opcode before trying to exit DDT.

Depending on how your program uses the default FCB and DMA (command line) area, you may have to manually set that up. The DDT "i" command will setup one file name at 005CH, but will not setup the second at 006CH nor setup the commandline string at 0080H. If your command takes only one filename on the command line, and does not parse any of that itself, then the DDT "i" command should be enough.

March 25th, 2018, 03:15 PM
CORRECTION: it seems that DDT does not gracefully handle a program that returns to CCP, at least not in CP/M 2.2. So, if your program being debugged does return directly to CCP then it will be a little more difficult. You might have to setup a "wrapper" program to catch that. Here's an example:

First, for the example, I have a small program at 0100H that prints a message and returns:

005D 46 4F 4F FOO
0060 42 41 52 0D 0A 24 BAR..$
0100 LXI D,005D
0103 MVI C,09
0105 CALL 0005
0108 RET

Then, to run that safely, I find the start of DDT and patch a wrapper program before that. In this case, DDT was relocated to 0C800H so I used 0C700H:

0005 JMP C800
C700 lxi sp,c780
C703 call 100
C706 rst 7

The wrapper program just sets up a "safe" stack and calls the TPA, then does a RST 7 to return to DDT. If your program actually uses all of the TPA (sounds like this one does not), it becomes more complicated as you have to change the BDOS JMP at location 0005H, too.

March 25th, 2018, 05:06 PM
I did figure out that I can enter text to the program, even when doing a 't'. I just learned to recognize the BDOS jump to the input routine.

What is the difference between G and G100?

When I run with a G my program does a RET and I get back to ddt. Although the memory areas I expect to be modified don't get changed. When I run the program with "t"'s the memory gets set.


March 25th, 2018, 05:45 PM
"G" runs the program using the current PC, which will be set initially to 0100H. "G100" explicitly runs the program at the TPA start address 0100H. If you are resuming a program, you'd want to use "G". But when you are starting a program from the beginning then "G100" would be unambiguous.

When you say your program does a RET and you get back to DDT, do you mean that your program executes a RET instruction when it is finished? as opposed to doing JMP 0? In my experiments, programs that end with a RET would often end up (eventually) back in DDT, but they were not getting there cleanly. That's why I advised a wrapper program. Do you know exactly which method this program uses? If it is simply doing RET, you'll want to make sure you have saved the CCP stack pointer and set a local stack, and restore before RET. The CCP stack area is small and a classic bug is to overrun the CCP stack and corrupt the CCP.

March 25th, 2018, 06:14 PM
Yes, just a return.

I am getting close! This is fun.

Does cpm know the actual file size of a file? When I call the bdos funcion to write a block it copies 128 bytes. If you only had 5 bytes in the dma buffer can you tell cpm the actual file size even though the entire block is written.

Also for some reason The first character of my buffer does not make it in the file. I will read up a bit more tomight and start again tomorrow.

March 25th, 2018, 06:28 PM
No--the granularity of the CP/M file system is 128 bytes.

ASCII files will use a ctrl-Z (hex 1a) to indicate the end of the file, but it's up to the application to know that (e.g. TYPE or ED). Some early MS-DOS utilities also honored the 1A ASCII file end.

The way CP/M tells the file size is to search the directory for the highest extent and compute the file size from the record count and extent number. You can, in fact, have a file larger than the disk in CP/M (called a "sparse file").

March 25th, 2018, 06:33 PM
CP/M uses the entire 128 bytes. Files don't have a byte count - at least not in CP/M 2.2. Text files have a Ctrl-Z character to indicate EOF at a byte boundary, but I think you are talking about binary files. Some programs will fill the last buffer to 128 bytes, to ensure it is "clean" (otherwise, it contains the remnants of the previous operation).

I'm guessing you are using a "put character" sort of routine to fill the buffer byte-by-byte, writing when 128 bytes have been put. Depending on how you've done that, it could be that the initial values of the variables used to control that buffering have resulted in dropping the first byte that is "put".

March 25th, 2018, 07:46 PM
Thanks guys, that is really helpful. I think tomorrow I should be able to get this all working.


March 26th, 2018, 10:19 AM
Got it!!!

I added a routine to fill the DMA area with 0x1A.

I had to add delays to the serial stream on the PC side. I run my port at 9600 baud. A 2Mhz 8080 struggles. With the delays on the chars everything is good.

If anyone is interested I can post the code. It's a port of Grants DOWNLOAD.COM from Z80 to 8080 so I can upload SW over a serial port easily. Grant wrote a utility that runs on windows that you drop files onto to convert them to HEX. Then you paste the resulting HEX into your serial terminal. The DOWNLOAD program recreates the file on your CPM machine. Adding the <cntl> z makes it work well with text files.

Thanks for all your help!!!


March 26th, 2018, 11:44 AM
Yeah, 2MHz Z80/8080 is going to have trouble with a sustained stream at 9600 baud. You might be able to bypass BDOS/BIOS and directly access the UART in DOWNLOAD.COM, but even then you have to eventually write to disk which blows it all out of the water. You must have added some serious delays. The surest way would be to use handshaking. But if your host PC doesn't honor handshake signals/characters then you're stuck.