• Please review our updated Terms and Rules here

Using BDOS calls to read & write to disk

MykeLawson

Veteran Member
Joined
Mar 20, 2014
Messages
576
I am in the process of trying to write a program that will need to have CP/M write files to the disk and read files from the disk, all while never leaving my program. Enter BDOS calls. I have been, and still are, reading up as much as I can on how to do this, but I am open to any real like knowledge and experience. Here is an example of what I envision happening is the 'write' portion of this:
1. my program reads the incoming data from a serial port and starts storing it locally at address 0100h.
2, once all the data has been retrieved, my program will internally issue the correct BDOS calls, in the correct order, so CP/M can perform the actual disk I/O.
3. once CP/M has written the file to disk, my program continues to operate where it left off.

The opposite would occur when reading a file off the disk and sending out the serial port.

BTW, my program will operate from as high up in memory as I can stick it without banging into the CCP. The system is a Z-80, 64K, running CP/M 2.2. So, if anyone has any first hand knowledge or experience in doing this, I'd really be grateful.
 
First, you should not receive serial data into address 0100h onwards. That is where your program lives. The serial data will overwrite the program while it is receiving the serial data, which is most likely not what you want.

edit: I just saw that you relocate the program. Then that approach works, although it is probably easier to keep your program at 0100h and have the receive buffer follow it. That way you maximize the receive buffer size, and you can clobber the CCP only if necessary.

In CP/M you prepare an FCB with the drive and filename to use. Then you OPEN the file using the FCB. Then you SEQUENTIAL WRITE your data using the FCB. Then you CLOSE the file using the FCB. Look at the CP/M or BDOS documentation on how to prepare the registers and call these functions.
 
First, you should not receive serial data into address 0100h onwards. That is where your program lives. The serial data will overwrite the program while it is receiving the serial data, which is most likely not what you want.

edit: I just saw that you relocate the program. Then that approach works, although it is probably easier to keep your program at 0100h and have the receive buffer follow it. That way you maximize the receive buffer size, and you can clobber the CCP only if necessary.

In CP/M you prepare an FCB with the drive and filename to use. Then you OPEN the file using the FCB. Then you SEQUENTIAL WRITE your data using the FCB. Then you CLOSE the file using the FCB. Look at the CP/M or BDOS documentation on how to prepare the registers and call these functions.
Guess I need to figure out how to 'prepare an FCB' and then what I need to do to 'OPEN the file' with that FCB. Looks like more reading is in order... Figuring out the FCB is now top of the list. Thanks
 
If you are receiving all data from the serial port before writing the file, you should be fine. If you are writing the data as it comes in, you may have timing problems since the file writes may take enough time to cause loss of serial data.
 
If you are receiving all data from the serial port before writing the file, you should be fine. If you are writing the data as it comes in, you may have timing problems since the file writes may take enough time to cause loss of serial data.
Yes, the program flow will be to test if there is incoming data, and if so receive it and begin storing it at 0100h. Once completed, calculate the number of blocks needing to be written to disk. Then pass the filename, extension, and number of blocks to CP/M so it can placed on the disk. Once it has been written then control is returned to my program.
 
I don't know if user programs were "allowed" to hook into interrupts in CP/M in general or not, but if that is allowed then doing the receiving (and perhaps also any sending) in interrupt code will solve the problem of dropping characters while doing disk I/O. Or it will unless the disk I/O gets higher priority (DMA perhaps or so).
 
I don't know if user programs were "allowed" to hook into interrupts in CP/M in general or not, but if that is allowed then doing the receiving (and perhaps also any sending) in interrupt code will solve the problem of dropping characters while doing disk I/O. Or it will unless the disk I/O gets higher priority (DMA perhaps or so).
Interrupts for receiving any data over the serial port into the machine is not really a concern. I just need to figure out how to get the info of what I placed into memory and provided to CP/M without having to exit my program.
 
The CP/M 3 Programmers Guide gives some code examples.
Thanks, the file-to-file and dump-file routines could easily be tweaked to do what I need. The dump-file looks the best for a first test. Instead of dump to the screen, I can wrap it in a header, and send it out the serial port and not the screen. Once that works, then I'll have a better idea of the parts and pieces I will need, and in what order, so I can do the serial to memory, to disk routine. Thanks
 
Hi @myke,

What part of the task are you looking for ideas to help with?

I'm guessing you have the serial port reads down OK, since you most likely built that hardware.

Is it just to writing to disk you have an issue with?

The process goes like this - you can have the FCB (File Control Block) anywhere, but generally you can use the one at $0080 for your application to save memory.

1) Fill in the FCB with the filename and drive ( or 0 for default... 1 is for A: and so on ).
2) Call BDOS for Open File. Check for errors. Handle errors.
3) Save the start of memory you want to save in memory.
4) Calculate the file size in records. Store in a 16 bit counter ( since it can be up to 9 bits long )
5) Loop here;
Retrieve memory to write. Set this as the DMA location via BDOS call.
Call Write Sequential BDOS call. Reference the FCB so it knows it's the same file.
Increase DMA location in memory by 128 bytes with a 16 bit add.
Decreate record counter
Repeat until all records are written.
6) exit.
 
Usually, CP/M programs don't need to parse a file name into an FCB. The CCP does that for you, for the first two arguments on the commandline. You have the first at 005cH, where it can be used directly in BDOS calls, and the optional second at 006cH (where it needs to be copied off if using 005cH as the FCB). If you typed "MYPROG B:SOMEFILE.TYP" on the commandline, then 005cH will be filled in and ready to call the BDOS to OPEN or MAKE it.
 
I don't know if user programs were "allowed" to hook into interrupts in CP/M in general or not, but if that is allowed then doing the receiving (and perhaps also any sending) in interrupt code will solve the problem of dropping characters while doing disk I/O.
They can, but not all CP/M machines use interrupts. Also, this is machine-specific. I call it a Bad Idea for a general application. Especially to someone asking the kinds of question as Myke.
 
Usually, CP/M programs don't need to parse a file name into an FCB. The CCP does that for you, for the first two arguments on the commandline. You have the first at 005cH, where it can be used directly in BDOS calls, and the optional second at 006cH (where it needs to be copied off if using 005cH as the FCB). If you typed "MYPROG B:SOMEFILE.TYP" on the commandline, then 005cH will be filled in and ready to call the BDOS to OPEN or MAKE it.

Assuming he's typing in the file name at the command line - I'm not sure if it's coming from the serial communications or somewhere else.

But it is a good point you added that if you have a single filename after the .COM file is called from the CLI, you can just use the FCB at $005C by default. That would definitely save some code and space also, and the file could be opened prior to the serial port reads to ensure that it's OK to open and that would allow the command to fail early if there's no space left, or the file can't be opened.
 
They can, but not all CP/M machines use interrupts. Also, this is machine-specific. I call it a Bad Idea for a general application. Especially to someone asking the kinds of question as Myke.

If you have a 128 byte FIFO serial chip and a slow enough baud rate, you could write in real time without losing anything while just polling :)

Handshaking with CTS/RTS is also an option in between writes.
 
so, I guess I should peel back another layer on this. This is sort of a 'client-server' arrangement. This topic specifically centers around the server side for now.
1. The server will monitor the serial port to determine if there is data to receive.
2. The data will have a header that will have some code value for the type of function (get a file from disk, store a file from disk, print a file, etc.), followed by the filename and extension, and the byte count (in hex), finally followed by the data.
3. The server will determine the type of function, then temporarily store the filename & extension, and load a counter with the byte count.
4. Using the byte count, the server will start inputting the data and store it at 0100h; increment the address, decrement the counter; rinse & repeat until all data retrieved.
5. Parse the filename & extension and put it into the FCB at 0080h (from cj7)
6. Calculate the number of blocks and load that into the FCB
7. Same with the disk the data is to be stored in
8. Continue with Cj7's routine until complete.

As for the worry of dropped bits & bytes; I'm going to start out slow, with something like 9600 baud. Once things are functioning, then i can start speeding things up. Given I have used the physical data transfer routines on three systems already for other things, I'm not concerned at that speed. And, I can always integrate an X-modem protocol at some point anyway. Since this function is basically two sections; retrieve the data and store the data, integrating some error protocol should not affect the CP/M BDOS side of things.

So now I have to start the actual code design with more detail and then start writing actual source code to assemble. Hey, it's a hobby, and taking baby steps works just fine. Thanks everyone and stay tuned.... I'm also looking to make this kinda portable in case anyone else might be interested.
 
As for the worry of dropped bits & bytes; I'm going to start out slow, with something like 9600 baud.
Early machinery considers 9600 bps fast. Slow would be 1200 bps or even 300 bps. Depends on your target.

You might have issues with "monitor the serial port to determine if there is data to receive" in a portable fashion. CP/M 2.2 offers no non-blocking function to check whether serial (aux) data is available; it will always wait until a byte is available and return it. Direct BIOS access does not help, either. In CP/M 3.0, non-blocking serial status checks are available.

Have you looked at CP/NET? Sounds like you are trying to write something similar.

If you have a 128 byte FIFO serial chip and a slow enough baud rate, you could write in real time without losing anything while just polling :)
Modern emulators implement huge FIFOs. When I published my AVR emulator, I even got called out for not having it. With a 132-byte (or larger) FIFO, XMODEM transfers at line speed will always succeed.
 
Yep, I have considered CP/NET, but I'm looking for something to develop on my own, and that offers a level on complexity I'm not ready to tackle right now. 9600 baud has never failed me so far, so that is my starting point.
 
CP/NET can use a serial port as the physical layer, but it is an extra level of configuration. But, there's always the question "how do I get CP/NET on to my computer the first time?" where things like this can help.
 
Yep Doug, who knows, this thing may end up with CP/NET at some point in the future; but for now, I'm taking baby steps
 
I wrote a CP/M client program to fetch files from a (PC) server over serial. It takes the filename from the command line, opens a new file, sends the name over serial, then receives the file using plain xmodem or a variant with 256-byte blocks and 16-bit checksum. Each block is written to disk as it is received.

It uses the Z280 serial port and contains some Z280 instructions, but might still be a useful reference for your project. It also contains my notes about how BDOS calls work, since I sometimes like to have documentation handy within my source file when I'm writing code. http://www.hyakushiki.net/misc/zget.no
 
Back
Top