MockbaTheBorg / RunCPM

RunCPM is a multi-platform, portable, Z80 CP/M 2.2 emulator.
MIT License
414 stars 75 forks source link

BDOS 35 - Compute file size return value #133

Closed MiguelVis closed 3 years ago

MiguelVis commented 3 years ago

The implementation of the BDOS function 35 - Compute file size returns a success / error value in the A/L registers, but this does not follow the CP/M BDOS v2 rules.

According to the CP/M v2 manual, this function does not return anything in any register after performing the operation.

I have tested it in an official CP/M BDOS v2 from Digital Research and in fact it always returns FFh in the A register.

On the other side, CP/M BDOS v3 returns the values implemented in RunCPM:

So, a program that tests the A register without having in mind the CP/M version will run ok in RunCPM and CP/M v3 but will fail in a real CP/M v2 due to a false failure (remember: CP/M v2 always return FFh in register A).

I kown because I did it!

RunCPM code:

// Returns the size of a CP/M file
uint8 _GetFileSize(uint16 fcbaddr) {
    CPM_FCB* F = (CPM_FCB*)_RamSysAddr(fcbaddr);
    uint8 result = 0xff;
    int32 count = _FileSize(DE) >> 7;

    if (count != -1) {
        F->r0 = count & 0xff;
        F->r1 = (count >> 8) & 0xff;
        F->r2 = (count >> 16) & 0xff;
        result = 0x00;
    }
    return(result);
}

CP/M v2 manual:

FUNCTION 35: COMPUTE FILE SIZE

Entry Parameters:
    Register C: 23H
    Registers DE: FCB Address

Returned Value:
    Random Record Field Set

When computing the size of a file, the DE register pair addresses an FCB in random mode format
(bytes r0, r1, and r2 are present). The FCB contains an unambiguous filename that is used in the
directory scan. Upon return, the random record bytes contain the virtual file size, which is, in
effect, the record address of the record following the end of the file. Following a call to Function
35, if the high record byte r2 is 01, the file contains the maximum record count 65536.

Otherwise, bytes r0 and r1 constitute a 16-bit value as before (r0 is the least significant byte),
which is the file size.

Data can be appended to the end of an existing file by simply calling Function 35 to set the
random record position to the end-of-file and then performing a sequence of random writes
starting at the preset record address.

The virtual size of a file corresponds to the physical size when the file is written sequentially. If
the file was created in random mode and holes exist in the allocation, the file might contain fewer
records than the size indicates. For example, if only the last record of an 8-megabyte file is
written in random mode (that is, record number 65535), the virtual size is 65536 records,
although only one block of data is actually allocated.

CP/M v3 manual:

BDOS FUNCTION 35: COMPUTE FILE SIZE

Entry Parameters:
    Registers C: 23H
    DE: FCB Address

Returned Value:
    Registers A: Error Flag
    H: Physical or Extended error
    Random Record Field Set

The Compute File Size function determines the virtual file size, which is, in effect,
the address of the record immediately following the end of the file. The virtual size
of a file corresponds to the physical size if the file is written sequentially. If the file is
written in random mode, gaps might exist in the allocation, and the file might contain
fewer records than the indicated size. For example, if a single record with record
number 262,143, the CP/M 3 maximum is written to a file using the Write Random
function, then the virtual size of the file is 262,144 records even though only 1 data
block is actually allocated.

To compute file size, the calling program passes in register pair DE the address of
an FCB in random mode format, bytes rO, r1 and r2 present. Note that the FCB
must contain an unambiguous filename and filetype. Function 35 sets the random
record field of the FCB to the random record number + 1 of the last record in the
file. If the r2 byte is set to 04, then the file contains the maximum record count
262,144.

A program can append data to the end of an existing file by calling Function 35 to
set the random record position to the end of file, and then performing a sequence of
random writes starting at the preset record address.

Note: the BDOS does not require that the file be open to use Function 35. However,
if the file has been written to, it must be dosed before calling Function 35. Otherwise,
an incorrect file size might be returned.

Upon return, Function 35 returns a zero in register A if the file specified by the
referenced FCB is found, or an OFFH in register A if the file is not found. Register H
is set to zero in both of these cases. If a physical error is encountered, Function 35
performs different actions depending on the BDOS error mode (see Function 45).

If the BDOS error mode is the default mode, a message identifying the error is
displayed at the console and the program is terminated. Otherwise, Function 35
returns to the calling program with register A set to OFFH, and register H set to one
of the following physical errors:

01 : Disk 110 error
04 : Invalid drive error
MockbaTheBorg commented 3 years ago

This is a good catch. I did most of the research on the BDOS function over seasip.info, and they they say: "Returns A=0FFh if error (file not found, or CP/M 3 hardware error); otherwise A=0.".

I just removed the line: "result = 0x00" above for now. This should do it I guess. I should upload v5.7 soon.

MiguelVis commented 3 years ago

I sent an email to John Elliot regarding the info. shown in his website about this BDOS fn., no reply yet, but the CP/M 2 manuals and the official BDOS from DR are the best source.

Thanks!