• Please review our updated Terms and Rules here

Anomolies around STAT - and the meaning of certain displays.

cj7hawk

Veteran Member
Joined
Jan 25, 2022
Messages
1,058
Location
Perth, Western Australia.
Hi All,

So I'm playing with some of the CP/M routines to complete some final debugging of my code, and working with STAT, I really like what it does, and wanted to be able to use it natively. ( For those who remember my early questions around what CP/M calls I needed to support, STAT was one of the programs expected to use the AV tables and other vectors, which I wasn't originally going to support, then realized I couldn't actually find a better solution. )

So I run STAT on LokiOS ( based on CP/M 2.2 ) and I get the following output ( Standard DR STAT.COM ) - Note- I find it doesn't recognized stat dsk: since it doesn't handle lower case.

M>STAT DSK:

A: Drive Characteristics
5056: 128 Byte Record Capacity
632: Kilobyte Drive Capacity
128: 32 Byte Directory Entries
32: Checked Directory Entries
256: Records/ Extent
16: Records/ Block
32: Sectors/ Track
1: Reserved Tracks

L: Drive Characteristics
464: 128 Byte Record Capacity
58: Kilobyte Drive Capacity
64: 32 Byte Directory Entries
0: Checked Directory Entries
128: Records/ Extent
8: Records/ Block
32: Sectors/ Track
241: Reserved Tracks

M: Drive Characteristics
8192: 128 Byte Record Capacity
1024: Kilobyte Drive Capacity
128: 32 Byte Directory Entries
0: Checked Directory Entries
512: Records/ Extent
32: Records/ Block
32: Sectors/ Track
0: Reserved Tracks

Bad Delimiter

M>

So reading this, and looking at Disk M, where I have 4096 byte sized blocks, hence only use 4 bytes of the allocation vector in the extent, since I'm using single byte descriptors in each allocation.

STAT returns 512 Records/Extent, which technically I guess there are, since 16 x 32 = 512, but of course CP/M doesn't recognize that and only uses the first 4 bytes...

So what is the situation with larger extents? The CR and RC don't support more than 128 records, but STAT reports otherwise, so what is STAT thinking? Was this an aberration caused by simplistic maths within the STAT program, or was there some kind of plan to support super-extents with >128 records?

L: and M: are the same drive, BTW, but L: is a subdirectory of M: and so the number of reserved tracks is correct and not abnormal... And aside from the records/extent all the other data reported by STAT is correct.

Also, just to remind, it's LokiOS - so it's a rewritten CP/M underlying FDOS and it might be caused by my implementation of the BIOS and BDOS, but I am thinking a normal DR written BIOS/BDOS should return the same result, since it's based primarily on the DPB, which is returned as a simple vector to a conforming table in the BIOS.

Thanks
David.

p.s. What are "checked directory entries" also?
p.p.s What is a "bad delimiter" in STAT?
 
The "Bad Delimiter" message is probably to do with the way your are creating the commandline buffer. Perhaps not setting the length correctly, or otherwise stray characters after the "DSK:". Most CP/M programs expect the commandline to have been converted to upper case, since that's what the CCP does.

As far as the odd output from STAT, we'd need to see the actual DPBs your are returning to be sure. Note that, in spite of using the word "Extent", the "Records/ Extent" line is actually reporting the number of records per directory entry. i.e. ((extmsk+1) * 128).
 
1. Don't confuse the FCB with directory entries. They're not the same.
2. Checked entries aren't necessary for hard disks, so your DPB should reflect CKS 0. Basically, it's how the BDOS determines that a floppy has been changed; i.e. it checks a pre-specified number of directory entries, starting at the beginning, when opening an extent for writing. If the checksum doesn't match, then you get the dreaded "BDOS ERR ON x: R/O" meaning that it has detected a change and has set the volume to read-only to avoid corruption.
It's not perfect, but it works, after a fashion.
3. STAT's "BAD DELIMITER" message pops up only on a badly formed I/O device assignment, such as STAT RDR:=UR2:. Perhaps you've run into another case-related issue? Or does your command end with a return right after the DSK:, and not a space?
 
...
So reading this, and looking at Disk M, where I have 4096 byte sized blocks, hence only use 4 bytes of the allocation vector in the extent, since I'm using single byte descriptors in each allocation.

STAT returns 512 Records/Extent, which technically I guess there are, since 16 x 32 = 512, but of course CP/M doesn't recognize that and only uses the first 4 bytes...
...
Not sure what you're trying to say here. CP/M directories may have multiple (16K) "extents" in a given "directory entry". That is where DPB.EXM (extmsk) comes in. If you're implementation does not use the whole directory entry, or if it is faking directory entries and only makes fake allocation entries for the first 16K, then the DPB should reflect that. As I recall, you are not using the on-disk filesystem format of CP/M, so things like SEARCH FIRST/NEXT will have to reconstruct "fake" directory entries, right? The DPB returned for that drive must match the way you create those directory entries.

And it should not be necessary to type a blank space after "DSK:" on the command. STAT does not require that, but if the command buffer you create at 0080H has the size byte computed wrong, then programs may be seeing another, invalid, character after "DSK:".
 
Thanks,
DPBs as per below. Comments might be wrong, but the data is as listed.



A:
DW $20 ; 00,01 ; L drive is 32 sectors per track - Disk 0! 128 byte "sectors" = records
DB 4 ; 02 ; 3 and 7(next ) = 512K sectors, 2048K allocations.
DB 15 ; 03 ; See above.
DB 1 ; 04 ; Extent mask. Blocks are 1K, so should be 0.
DW 315 ; 05,06 ; This has 316 2K blocks. ( Allocation = 1 K ). Would be a 720k disk.
DW 127 ; 07,08 ; 4 x 2k directory allocation = 128 file extents max. (128 total ) I think my format might be a little off though.
DB %11110000 ;09 ; Only set 4 bits so we know 4 allocation is directory
DB %00000000 ;0A
DW 8 ; 0B,0C ; Have set this as removable. Might be necessary to mark as 0 for fixed.
DW 1 ; 0D,0E ; 1 track offset. This gives 2K for boot of BDOS and to load the CCP and other files. Enough.

L:
DW $20 ; 00,01 ; L drive is 32 sectors per track - DISK 11! 128 byte "sectors" = records
DB 3 ; 02 ; 3 and 7(next ) = 512K sectors, 2048K allocations.
DB 7 ; 03 ; See above.
DB 0 ; 04 ; Extent mask. Blocks are 1K, so should be 0.
DW 57 ; 05,06 ; This has 58 1K blocks. ( Allocation = 1 K ). Would be a 64k disk. ie, 27C512 ROMDISK.
DW 63 ; 07,08 ; 1 x 1k directory allocation = 64 file extents max. (64 total )
DB %11000000 ;09 ; Only set 2 bits so we know 2 allocations allocations are directories
DB %00000000 ;0A
DW 0 ; 0B,0C ; Have set this as removable. Might be necessary to make as 0 for fixed.
DW $F1 ; 0D,0E ; 241 tracks offset, should put the directory at F1000 - This gives 4K for boot of BDOS/BIOS and Bootstrap from F0000 to F1000

M:
DW $20 ; 00,01 ; M drive is 32 sectors per track - DISK 12! is 128 byte "sectors" = records
DB 5 ; 02 ; 5 bits and 0-31 sectors per 4096k allocation.
DB 31 ; 03 ; See above.
DB 3 ; 04 ; Extent mask. Blocks are 4K, so should be 1.
DW 255 ; 05,06 ; This has 256 4K blocks. ( Allocation = 4 K ). Would be a 1024k disk. Lower 1Mb of RAM can be accessed as DISK.
DW 127 ; 07,08 ; 4 x 2k directory allocation = 128 file extents max. (128 total ) I think my format might be a little off though.
DB %10000000 ;09 ; Only set 1 bits so we know 1 allocation is directory
DB %00000000 ;0A
DW 0 ; 0B,0C ; Have set this as removable. Might be necessary to make as 0 for fixed.
DW 0 ; 0D,0E ; 0 track offset. First 4K is Directory Allocation ONLY. Allocation 1 is first memory space.
 
1. Don't confuse the FCB with directory entries. They're not the same.
2. Checked entries aren't necessary for hard disks, so your DPB should reflect CKS 0. Basically, it's how the BDOS determines that a floppy has been changed; i.e. it checks a pre-specified number of directory entries, starting at the beginning, when opening an extent for writing. If the checksum doesn't match, then you get the dreaded "BDOS ERR ON x: R/O" meaning that it has detected a change and has set the volume to read-only to avoid corruption.
It's not perfect, but it works, after a fashion.
3. STAT's "BAD DELIMITER" message pops up only on a badly formed I/O device assignment, such as STAT RDR:=UR2:. Perhaps you've run into another case-related issue? Or does your command end with a return right after the DSK:, and not a space?

Thanks Chuck - I'm not entirely sure I understand point 1) - I don't think I'm confusing them- I assume you're just reminding me that directory entries are the extents in the directory for each file, but I could be incorrect on that.

2) - That helps. Now I understand. A: is removable - so now I know what I need to make work there. I probably need to find a better way of doing it.

3) That helps too - yes, it's a CR right after the entry - but now I know what it means, I can look. That's probably where something I did conflicts slightly with something that is normal in CP/M so I'll look there.

Thanks
David
 
Here's where the "Bad Delimiter" message comes in. Note the two commands (run under 22NICE running under DOSEMU running under Debian Bullseye x64):
Code:
D:\Development\22nice>stat dsk:

    D: Drive Characteristics
65536: 128 Byte Record Capacity
    0: Kilobyte Drive  Capacity
    0: 32  Byte Directory Entries
    0: Checked  Directory Entries
 2048: Records/ Extent
  256: Records/ Block
   36: Sectors/ Track
    0: Reserved Tracks
D:\Development\22nice>stat dsk:/

    D: Drive Characteristics
65536: 128 Byte Record Capacity
    0: Kilobyte Drive  Capacity
    0: 32  Byte Directory Entries
    0: Checked  Directory Entries
 2048: Records/ Extent
  256: Records/ Block
   36: Sectors/ Track
    0: Reserved Tracks

Bad Delimiter
D:\Development\22nice>

Granted, 22NICE isn't exactly CP/M, but I think I did a pretty good job of emulating it.
 
Here's where the "Bad Delimiter" message comes in. ....

Granted, 22NICE isn't exactly CP/M, but I think I did a pretty good job of emulating it.
22NICE must not be constructing the commandline buffer correctly.
Code:
STAT DSK:(cr)
should not produce any error. The CP/M emulator I use does not print that error, nor does any real CP/M system.
 
Last edited:
Thanks,
DPBs as per below. Comments might be wrong, but the data is as listed.
...
So, drive M: is saying that each directory entry returned by SEARCH FIRST/NEXT will contain 4 16K extents - i.e. all 16 bytes of the map area are used (although a given file may not use them all depending on its size). If you are emulating the results of SEARCH FIRST/NEXT, you will want to ensure that your DPB is telling STAT the right way to interpret those results. Note that files larger than 64K (in the case of drive M: ) will need to return multiple directory entries on successive calls to SEARCH NEXT.
 
So, drive M: is saying that each directory entry returned by SEARCH FIRST/NEXT will contain 4 16K extents - i.e. all 16 bytes of the map area are used (although a given file may not use them all depending on its size). If you are emulating the results of SEARCH FIRST/NEXT, you will want to ensure that your DPB is telling STAT the right way to interpret those results. Note that files larger than 64K (in the case of drive M: ) will need to return multiple directory entries on successive calls to SEARCH NEXT.
Hi Doug, I'm only emulating the z80 - the code is following CP/M's bios calls ( or should be ) - I took the same approach that MSDOS took with CP/M 2.2 except I wrote for z80.

Here's the extent entries in the M: directory -
0000 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 00 00 00 80 01 02 03 04 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0020 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 01 00 00 80 05 06 07 08 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0040 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 02 00 00 80 09 0A 0B 0C 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0060 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 03 00 00 80 0D 0E 0F 10 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0080 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 00 00 00 80 F0 F1 F2 F3 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00A0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 01 00 00 80 F4 F5 F6 F7 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00C0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 02 00 00 80 F8 F9 FA FB 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00E0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 03 00 00 80 FC FD FE FF 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
0100 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 00 00 00 80 80 81 82 83 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0120 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 01 00 00 80 84 85 86 87 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0140 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 02 00 00 80 88 89 8A 8B 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0160 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 03 00 00 80 8C 8D 8E 8F 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0180 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 04 00 00 80 90 91 92 93 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01A0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 05 00 00 80 94 95 96 97 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01C0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 06 00 00 80 98 99 9A 9B 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01E0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 07 00 00 80 9C 9D 9E 9F 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
 
22NICE must not be constructing the commandline buffer correctly.
Take a good look at my second example in #7. If your copy of CP/M doesn't behave the same way, there's something wrong with it. Note the slash after the "DSK:" in the second exampe.
That is "STAT DSK:/" will produce a delimiter error, where "STAT DSK:" will not.
 
Hi Chuck,

I get that - though from what I can see, the string looks the same both in my emulator and in Doug's, which doesn't have the issue. That is, there is no additional delimiter in the string passed to the app.

So I'm still trying to work out where the difference is hiding so as to improve my version of CP/M.

Do you know if there's any STAT source code available?

Thanks
David
 
Urk.... Well, it's no worse than my knowledge of every other language combined and sorted in order from most difficult and least understood.

But it looks readable to at least give me an idea of what it might be doing.

Interestingly, STAT DSK: causes an error, but STAT A: DSK: gives the correct result - In fact, STAT <ANYTHING>: DSK: seems to give the right result.

It's going to be interesting figuring out what is causing this.
 
Hi Doug, I'm only emulating the z80 - the code is following CP/M's bios calls ( or should be ) - I took the same approach that MSDOS took with CP/M 2.2 except I wrote for z80.

Here's the extent entries in the M: directory -
0000 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 00 00 00 80 01 02 03 04 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0020 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 01 00 00 80 05 06 07 08 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0040 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 02 00 00 80 09 0A 0B 0C 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0060 - 00 52 4F 4F 54 5F 43 50 4D 49 4D 47 03 00 00 80 0D 0E 0F 10 00 00 00 00 00 00 00 00 00 00 00 00 - +ROOT_CPMIMG++++++++++++++++++++
0080 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 00 00 00 80 F0 F1 F2 F3 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00A0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 01 00 00 80 F4 F5 F6 F7 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00C0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 02 00 00 80 F8 F9 FA FB 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
00E0 - 00 4C 3A 5F 44 52 49 56 45 44 53 4B 03 00 00 80 FC FD FE FF 00 00 00 00 00 00 00 00 00 00 00 00 - +L:_DRIVEDSK++++++++++++++++++++
0100 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 00 00 00 80 80 81 82 83 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0120 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 01 00 00 80 84 85 86 87 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0140 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 02 00 00 80 88 89 8A 8B 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0160 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 03 00 00 80 8C 8D 8E 8F 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
0180 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 04 00 00 80 90 91 92 93 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01A0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 05 00 00 80 94 95 96 97 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01C0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 06 00 00 80 98 99 9A 9B 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
01E0 - 00 53 63 72 65 65 6E 5F 31 49 4D 47 07 00 00 80 9C 9D 9E 9F 00 00 00 00 00 00 00 00 00 00 00 00 - +Screen_1IMG++++++++++++++++++++
So, if you are running a real CP/M BDOS and using the DPB you showed, then the directory entries should be using all 16 bytes of the map area, not just the first 4. If you really have an actual DRI BDOS, then maybe something in your BIOS seldsk routine is not returning the proper DPH (->DPB) for drive M:.
 
Regarding the error message from STAT, if you are running a true DRI CCP then it should have properly setup the commandline buffer and it should have worked. If you are running a custom CCP, check that it is computing the length byte correctly. Remember that the commandline buffer contains the length byte in 0080H and the text of the command after the command name begins at 0081H - starting with the blank space after the command name. If the length byte were being computed as N+1 it would produce the exact error message you see from STAT.
 
So, if you are running a real CP/M BDOS and using the DPB you showed, then the directory entries should be using all 16 bytes of the map area, not just the first 4. If you really have an actual DRI BDOS, then maybe something in your BIOS seldsk routine is not returning the proper DPH (->DPB) for drive M:.

Hi Doug - It's not DR BDOS/CCP - I wrote both from scratch in z80 assembly, though STAT is DR and is a binary COM file. I'm trying to get my BDOS/CCP working with a greater range of software.

So hitting issues is not surprising. Knowing what the error means gets me some way to solving it.

If I just do a single drive - eg, STAT A: DSK: I don't get the error. If I let it recurse through all, I get the error, so it doesn't look like a parsing issue.

Do you have an example of what an extent looks like with 512 records? That would help me figure out what is going wrong. From prior discussions, I thought that when the masks started to get very large, that it used less of the entries to maintain 16K per extent, but it's very possible I'm mistaken about that.

I should probably adapt the DR BDOS/CCP to see how it behaves... But the source I have isn't perfectly compatible with my assembler so I need to modify it. It seems like it might be the next logical step though so I can find out why it's not working and what a normal BDOS/CCP does under the same circumstances. I had wanted to avoid that, but in fine tuning it might be necessary.

Thanks
David.
 
I did some searches on this, but everything I found said that CP/M only uses part of the 16 bytes of the map area when using 4K allocations, with 1 byte allocation groups - I don't have any other examples to go from with real CP/Ms so I can't tell where my code is wrong in generating the extents it does. If you have some further information you can point me to, or an actual example from a CP/M system that uses 4K single-byte allocations, that would be immensely helpful.
 
So, in other words, BSH 5 with DSM < 256? I can give you a bunch--or you can have a look through the 22disk definitions. It's very common.

For example, the 96 tpi Zorba:
Code:
BEGIN ZOR2  Zorba - DSDD 96 tpi 5.25"
DENSITY MFM, LOW
CYLINDERS 80 SIDES 2 SECTORS 10,512 SKEW 2
SIDE1 0 1,2,3,4,5,6,7,8,9,10
SIDE2 0 11,12,13,14,15,16,17,18,19,20
ORDER SIDES
BSH 5 BLM 31 EXM 3 DSM 194 DRM 127 AL0 080H AL1 0 OFS 2
END
 
I did some searches on this, but everything I found said that CP/M only uses part of the 16 bytes of the map area when using 4K allocations, with 1 byte allocation groups - I don't have any other examples to go from with real CP/Ms so I can't tell where my code is wrong in generating the extents it does. If you have some further information you can point me to, or an actual example from a CP/M system that uses 4K single-byte allocations, that would be immensely helpful.
Each "extent" is 16K, so that is 4 bytes of map in your case of drive M:. However, CP/M uses the full directory entry if DPB.EXM is set correctly. The DPB data you provided says that DPB.EXM is telling a true BDOS to use the whole directory entry. What documentation did you see that suggests CP/M only uses the 4 bytes?
 
Back
Top