; ; C-128 CP/M REU Quick Patches -- Beyond-512k+ and 1764 REU Filesystem Fix v1.0 BETA -- XmikeX ; ; ; CP/M REU Patches? What is this for? ; ; Ram Expansion Unit filesystem modifications for C-128 CP/M, involving 1764 REU and modded REUs. Simple as that. ; ; ; I don't want to read this further, just gimmie the patches. ; ; The relevant hex string on an unmodified CP/M System Disk with REU Support is: ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB ; ('Modified' system builds that have left the 512k REU DPB untouched should naturally retain the string above). ; ; Find the string above with a Hex Editor, on your disk image or actual disk, and replace with one of the following: ; 0200030700FF007F00F000008000000101 - 256k REU hex patch ; 0200040F00FF017F00C000008000000101 - 1M REU hex patch ; 0200040F00FF037F00C000008000000101 - 2M REU hex patch ; 0200040F00FF077F00C000008000000101 - 4M REU hex patch ; 0200051F01FF077F008000008000000101 - 8M REU hex patch ; 0200063F03FF077F008000008000000101 - 16M REU hex patch ; ; The remainder of this text will explain why there are some limitations with these patches (e.g., 128 Dir Entry Max).; ; ; ; Why did this come about? ; ; 1) I have been irritated for years at the lack of decent drivespace under C-128 CP/M. ; 2) I never got around to creating a real REU to 1 or 2M, or buying an equivalent product from CMD, etc. ; 3) I 'noticed' VICE x128 could boot/run CP/M. ; 4) I 'remembered' VICE provided "up to" 16M REU. ; 5) I did not find any 'Greater-Than-512k-REU' CP/M builds or patches on the net. (Do builds/patches exist elsewhere?) ; 6) VICE x128 z80 ( @ ALT-WARP!! ) is comfortably faster than the real thing. ; 7) Data from the CP/M 3 system guide is available on the net (i.e., better than flipping through that heavy thing in real life). ; 8) Praising VICE 1000x times a day and chatting endlessly about CP/M will ensure that IRCNET #c-64 goes into severe spasms. ; ; ; This seems like a lengthy piece of documentation. How far along is this project? ; ; 1) Quick Patches are done for now. There are limitations to patching, and further modifications (for the sake of sanity) are ; likely better done with a rebuild of the CP/M operating system. The information provided in this text is relevant for that as well. ; 2) This text is my attempt at trying to get everything I've learned relevant to this project into one location, before my memory fades ; on the subject. ; ; ; Obviously, you've tried these mods in VICE. Have you tried them on real C-128/REU hardware or 16M "REU" provided by the 1541u I/II device? ; ; 1) No. ; 2) Not Yet. ; 3) Do 1541u's even work in C-128 / 128 mode at all? ; ; ; Do these patches check REU Size and set up the filesystem accordingly? ; ; No. You apply the patches yourself to match your particular hardware. ; The only exception here is the original check for the 1700 REU which we've left alone and should work as always. ; ; ; Where did you get this information from? ; ; This file contains information sourced from the CPM 3.0 System Guide, The Computer Journal, the C-128 CPM3 source code, the manual ; page in cpmtools_2.7-1_i386, random-ish musings from the net, trial/error, and my interpretations in general. ; ; ; Errors? Is there a warranty or guarantee? ; ; There is no warranty or guarantee of any kind associated with any and all bits of information presented in this text file. ; You assume liability/responsibility for any real or imagined: ; 1) inconvenience(s), ; 2) annoyance(s), ; 3) data loss, and ; 4) all imaginable and unimaginable consequences arising from the use or misuse of information presented here. ; ; -- XmikeX, November 27, 2010. ; ;-- ; ; ; Our story begins with VON ERTWINE, the fellow that ported CP/M over to the C-128. Among many other things, he created the CXRAMDSK.ASM ; source file which (for our purposes) illustrates the layout of the Ramdisk filesystem and its functions. The CXRAMDSK.ASM file is where the ; DPBs (Disk Parameter Blocks) for the REU are located. These DPBs will be the focus of this text, as they will allow us to change certain filesystem ; parameters for our benefit. ; ; The Good News -- 1700 and 1750 REUs are accounted for in C-128 CP/M. ; ; The Bad News -- 1764 REU (256k) and REU mods beyond 512k (1,2M ..16M -1541u) are not handled properly by CP/M on the C-128. ; These "other" REUs came out after VON ERTWINE created the DPBs in CXRAMDSK.ASM and it appears as if Commodore never got around to ; contracting him to produce an update (at least for 1764 REU). ; ; ANY REU that is NOT a 1700 is handled as a 512k REU and is assigned the DPB for 512k REU. For all "other" REUs EXCEPT the 1764, ; this has no bearing on data integrity. 1764 REUs (256k) are handled as 512k REUs and they are given a 512k drivespace which ; they obviously cannot handle. 1700 REUs are detected as such by code in CXRAMDSK.ASM and are given a separate DPB with 128k drivespace. ; ; --> Our patches will therefore apply only to the Original 512k REU DPB. ; ; Please Note: The REU I/O code in C-128 CP/M appears to be competent enough to parse through ; the three-byte REU BASE ADDRESS ($DF04..$DF06) up to 16 Megs. Therefore, we will only focus ; on modifying the DPBs while allowing the I/O code to perform as it normally does. ; ; With the information and examples provided in this text, you will hopefully be able to patch your own systems accordingly for your own needs. ; ; ;-- ;A Very Incomplete Summary of data allocation. ; ; The basic logical unit of data in CP/M is the 128-Byte Record. The DPB describes how these Records are laid out on disk, usually ; grouped into sectors and tracks for representation on physical media. Logically, CP/M essentially allocates disk space in a unit called ; a Block (AKA Allocation units / Clusters). In practice, a block is the smallest allocatable storage unit. ; (Further details in the System Guide). ; ; Do not confuse Records and Sectors with (Blocks / Allocation Units / Clusters) as they may not be equivalent. ; Additionally, the number of Tracks in a DPB may not be necessarily dependent on the number of Blocks allocated. ;-- ; ; ; The following are the original 1700 and 1750 DPBs from CXRAMDSK.ASM . ; VON ERTWINE set up the .ASM code so that it checks for 128k REU only, while anything else is treated as a 512k REU. ; Because of this, we only need to patch the DPB for the 512k REU. This has the added benefit of preserving the 128k REU ; DPB/filesystem in case we ever need it. ; ;dpb$RM$128: ; dpb 256,1,512,1024,64,0 ; ;--> ORIGINAL 128k REU <-- ; ; ; ;256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 512 Tracks per Disk, 1024-Byte allocation units, 64 Dir Entries-Max ; ; ; DW 0002 ; 128 BYTE RECORDS PER TRACK ; DB 03,07 ; BLOCK SHIFT AND MASK ; DB 00 ; EXTENT MASK ; DW 007Fh ; MAXIMUM BLOCK NUMBER ; DW 003Fh ; MAXIMUM DIRECTORY ENTRY NUMBER ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY ; DW 8000h ; CHECKSUM SIZE ; DW 0 ; OFFSET FOR SYSTEM TRACKS ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT ; ; ;dpb$RM$512: ; dpb 256,1,2048,2048,128,0 ; ;--> ORIGINAL 512k REU <-- ; ; ; ;256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 2048 Tracks per Disk, 2048-Byte allocation units, 128 Dir Entries-Max ; ; ; DW 0002 ; 128 BYTE RECORDS PER TRACK ; DB 04,0Fh ; BLOCK SHIFT AND MASK ; DB 01 ; EXTENT MASK ; DW 00FFh ; MAXIMUM BLOCK NUMBER ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY ; DW 8000h ; CHECKSUM SIZE ; DW 0 ; OFFSET FOR SYSTEM TRACKS ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT ; ; ;-- ;Breakdown of DPB, examples and comments, follows below: ; ; ; Typical DPB action in CP/M is done with macros found in CPM3.LIB, as noted : ; ; ; dpb physical$sector$size, - disk parameter block ; ; physical$sectors$per$track, ; ; number$tracks, ; ; block$size, ; ; number$dir$entries, ; ; track$offset, ; ; checksum$vec$size (optional) ; ; This is why we see comments like "dpb 256,1,2048,2048,128,0" in CXRAMDSK.ASM and elsewhere. ; Fortunately, with respect to REU, VON ERTWINE dispensed with macros and performed this task directly. ; ; ; Original DPB for 512k REU (extensively commented). ; ;dpb$RM$512: ; dpb 256,1,2048,2048,128,0 ; ;--> ORIGINAL 512k REU <-- ; ; ; ;256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 2048 Tracks per Disk, 2048-Byte allocation units, 128 Dir Entries-Max ; ; ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; --> Idealized in labels as 'Sectors Per Track'. This value actually represents the number ; of 128-Byte "records" per track, which need to correlate with the physical Sector Size on ; the disk. Physical Sector Sizes are assigned by PSH and PHM settings (see below). ; ; In PSH and PHM below, our current sectors are set to 256-Bytes each. Since we've set the value ; here to Two 128-Byte Records per Track, and our Physical Sector Size is 256-Bytes, the filesystem ; will assign a 'Sectors Per Track' value of One. ; ; At it's heart, CP/M manages data on storage devices using 128-byte logical 'records', with sectors ; being primarily purposed for description of disk-format layout on physical media. ; Several physical sectors, indirectly as logical records, are linked into "Allocation Blocks". ; The Allocation Blocks are then linked to "Logical Extents" for the representation in the directory. ; ; DB 04,0Fh ; BLOCK SHIFT (BSH) AND BLOCK MASK (BLM) ; --> These two bytes imply the data/block allocation size (BLS). ; ; The values of BSH and BLM implicitly determine the ; Block Allocation Size (BLS) which is NOT an entry in ; the DPB. Given that the designer has selected a value ; for BLS, the values of BSH and BLM are shown in the ; following table: (Values below are decimal.) ; BLS BSH BLM ; 1,024 3 7 ; 2,048 4 15 <-- 2,048-Byte BLS is set for 512k REU by DB 04,0Fh ; 4,096 5 31 ; 8,192 6 63 ; 16,384 7 127 ; ; As noted above, BLS is the number of bytes in a Block/Cluster/Allocation Unit. ; The BLS can be 1024, 2048, 4096, 8192, or 16384 (decimal) bytes. ; A large block size decreases the size of the allocation vectors but can result in wasted disk space. ; A smaller block size increases the size of the allocation vectors because there are more blocks on ; the same size disk. ; ; There is a restriction on the block size. If the block size is 1024, there cannot be more than 255 ; blocks present on a logical drive. In other words, if the disk is larger than 256K, it is necessary ; to use at least 2048 byte blocks. ; ; DB 01 ; EXTENT MASK (EXM) - Pertaining to 'Logical Extents'. ; --> Logical Extents under CP/M are not the same as 'Extents' as described by other operating systems. ; As noted eariler, One Record = 128 Bytes. One Logical Extent = 128 Records = 16384 Bytes (16K). ; ; Since CP/M Files can have more than one directory entry, a CP/M extent is merely the section/range ; of a file managed by a single directory entry. Multiple directory entries for a file are distinguished ; by their EX and S2 descriptor bytes. The formula is: Entry number = ((32*S2)+EX) / (EXM+1) where EXM ; is the extent mask value listed here. (See APPENDIX 1 and 2 for more complete information). ; ; --> Values below are decimal. The value of EXM depends ; upon the BLS and whether the DSM value is less than ; 256 or greater than 255. (DSM = MAXIMUM BLOCK NUMBER) ; ; For DSM less than 256, the value of EXM is given by: ; BLS EXM ; 1,024 0 ; 2,048 1 ; 4,096 3 ; 8,192 7 ; 16,384 15 ; ; For DSM greater than 255, the value of EXM is given by: ; BLS EXM ; 1,024 N/A ; 2,048 0 ; 4,096 1 ; 8,192 3 ; 16,384 7 ; ; As noted in CP/M 3 System Guide, for reference: ; The value of EXM is one less than the maximum number of 16K extents per FCB (File Control Block). ; Set EXM to zero if you want media compatibility with an extended CP/M 1.4 system. ; This only applies to double-density CP/M 1.4 systems, with disk sizes greater than 256K bytes. ; It is preferable to copy double-density 1.4 disks to single-density, then reformat them and ; recreate them with the CP/M 3 system, because CP/M 3 uses directory entries more effectively ; than CP/M 1.4. ; ; Please note - The phrase "physical extents" is sometimes used in place of "directory entries". ; Do not confuse these with Logical Extents, as described in this section. ; ; DW 00FFh ; MAXIMUM BLOCK NUMBER (DSM) ; --> The value of DSM is the maximum data block number ; measured in BLS units supported by the drive. ; ; The product BLS * ( DSM + 1 ) is the total number of ; bytes held by the drive and, of course, must be within ; the capacity of the physical disk, not counting the ; reserved operating system tracks. ; ; e.g., DSM of 00FFh = 512k REU diskspace if BLS are set as 2k blocks. (256 * 2,048 = 524,288) ; ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) ; --> Number of the last directory entry (Number of entries ; per disk -1). Total number of directory entries that can ; be stored on this drive. (AL0, AL1 determine reserved ; directory blocks). The DRM entry is one less than the ; total number of directory entries that can take on a 16-bit ; value. (The DRM entry is one less than the total number of ; 32-byte directory entries, and is a 16-bit value.) ; ; DRM must be less than or equal to (BLS/32 * 16) - 1. ; If you submit a DRM value that is greater than (BLS/32 * 16) - 1, ; the DRM will be too large to encode a proper Alloc Vector (see below). ; ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) ; --> Starting value of the first two bytes of the allocation table. ; (Determined by DRM; bit meaning see below.) ; ; The values of AL0 and AL1 are determined by DRM. ; AL0 and AL1 values together can be considered a string of 16-bits, ; as shown below: ; ; AL0 -- 00 01 02 03 04 05 06 07 ; AL1 -- 08 09 10 11 12 13 14 15 ; ; Position 00 corresponds to the high-order bit of the byte AL0, and ; position 15 to the low-order bit of the byte AL1. Each bit position ; reserves a data block for a number of directory entries, thus allowing ; a total of 16 data blocks to be assigned for directory entries (bits are ; assigned starting at 00 and filled to the right through position 15). ; ; Each directory entry occupies 32 bytes, resulting in the following table: ; ; BLS Directory Entries ; 1,024 32 times # bits ; 2,048 64 times # bits ; 4,096 128 times # bits ; 8,192 256 times # bits ; 16,384 512 times # bits ; ; e.g., Assume BLS is 2048, and DRM is 127. ; 2048 BLS (Block Allocation Size) can hold sixty-four 32-byte entries. ; IF DRM = 127 (128 directory entries), then TWO blocks of 2048 BLS are ; sufficent to hold all 128 dir entries, and these two blocks correspond ; to the first two bits being set in the following AL0/AL1 two-byte string, ; in binary : 1100 0000 + 0000 0000 ; AL0 (11000000) + AL1 (00000000) ; in hex : C0 + 00 ; AL0 (C0) + AL1 (00) ; ; ; e.g., Assume BLS is 2048, and DRM is 255. ; 2048 BLS (Block Allocation Size) can hold sixty-four 32-byte entries. ; IF DRM = 255 (256 directory entries), then FOUR blocks of 2048 BLS are ; sufficent to hold all 256 dir entries, and these four blocks correspond ; to the first four bits being set in the following AL0/AL1 two-byte string, ; in binary : 1111 0000 + 0000 0000 ; AL0 (11110000) + AL1 (00000000) ; in hex : F0 + 00 ; AL0 (F0) + AL1 (00) ; ; ; e.g., Assume BLS is 4096, and DRM is 255. ; 4096 BLS (Block Allocation Size) can hold one-hundred-twenty-eight 32-byte entries. ; IF DRM = 255 (256 directory entries), then TWO blocks of 4096 BLS are ; sufficent to hold all 256 dir entries, and these four blocks correspond ; to the first four bits being set in the following AL0/AL1 two-byte string, ; in binary : 1100 0000 + 0000 0000 ; AL0 (11000000) + AL1 (00000000) ; in hex : C0 + 00 ; AL0 (F0) + AL1 (00) ; ; ; e.g., Assume BLS is 4096, and DRM is 511. ; 4096 BLS (Block Allocation Size) can hold one-hundred-twenty-eight 32-byte entries. ; IF DRM = 511 (512 directory entries), then FOUR blocks of 4096 BLS are ; sufficent to hold all 512 dir entries, and these four blocks correspond ; to the first four bits being set in the following AL0/AL1 two-byte string, ; in binary : 1111 0000 + 0000 0000 ; AL0 (11110000) + AL1 (00000000) ; in hex : F0 + 00 ; AL0 (F0) + AL1 (00) ; ; Example listed in CP/M 3 System Guide, for reference: If DRM = 127 (128 directory entries) and BLS = 1,024, there ; are 32 directory entries per block, requiring four reserved blocks. ; In this case, the four high-order bits of AL0 are set, resulting in ; the values AL0 = $F0 and AL1 = $00. ; ; DW 8000h ; CHECKSUM SIZE (CKS) - 0 or 8000h for a fixed disc (hard-drive, ram-drive). ; --> The CKS value is determined as follows: ; ; 1. If the disk drive media is removable, then CKS = ( DRM + 1 ) / 4, ; where DRM is the last directory number. ; ; 2. If the media is fixed then CKS = 0 or 8000, and no directory records are checked. ; ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; --> The OFF field determines the number of tracks that are skipped at the ; beginning of the physical disk. This value is automatically added whenever ; SETTRK is called. It can be used as a mechanism for skipping reserved ; operating system tracks or for partitioning a large disk into smaller ; segmented sections. ; ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; --> PSH and PHM determine the physical sector size of the disk. ; All disk I/O is in terms of the physical sector size. Set PSH and PSM ; to zero if the BIOS is blocking and deblocking instead of the BDOS. ; ; PSH specifies the physical record shift factor, ranging from 0 to 5, ; corresponding to physical record sizes of 128, 256, 512, 1K, 2K, or 4K bytes. ; It is equal to the logarithm base two of the physical record size divided by ; 128, or LOG2(sector-size/128). See Table 3-7 for PSH values. ; ; PHM specifies the physical record mask, ranging from 0 to 31, corresponding to ; physical record sizes of 128, 256, 512, 1K, 2K, or 4K bytes. It is equal to one ; less than the sector size divided by 128, or, (sector-size/128)-l. ; ; PSH and PHN Values ; Sector Size PSH PHM ; 128 0 0 ; 256 1 1 ; 512 2 3 ; 1,024 3 7 ; 2,048 4 15 ; 4,096 5 31 ; ; To complete the discussion of the DPB (Disk Parameter Block), several DPHs (Disk Parameter Headers) ; can address the same DPB if their drive characteristics are identical. Furthermore, the DPB can be ; dynamically changed when a new drive is addressed. Since the BDOS copies the DPB values to a local ; area whenever the SELDSK function is called, simply change the pointer in the DPH (Disk Parameter Header). ; ; ;-- ;Random-ish Notes for Future Reference (probably unnecessary for REU mods) ; CSV is the address of a scratchpad area used to detect changed disks. ; This address must be different for each removable media Disk Parameter Header. ; There must be one byte for every 4 directory entries (or 128 bytes of directory). ; In other words, length(CSV) = (DRM/4)+l. ; The size of the data addressed by CSV is CKS bytes, which is sufficient to hold the directory check ; information for this particular drive. If CKS = ( DRM +1 ) / 4, you must reserve ( DRM + 1 ) / 4 bytes ; for directory check use. If CKS = 0 or 8000, no storage is reserved. ; If the drive is permanently mounted (as the CP/M REU is), set the CKS variable in the DPB to 8000H and set ; CSV to 0000H. This way, no storage is reserved for a checksum vector. ; The checksum vector may be located in common memory or in Bank 0. Set CSV to 0FFFEH for GENCPM to set up the checksum vector. ; ; ALV is the address of the scratchpad area called the allocation vector, which the BDOS uses to keep disk storage allocation information. ; This area must be unique for each drive. ; Returning back to the DPH for a particular drive, the two address values, CSV and ALV, these reference areas ; of uninitialized memory in the BIOS data segment. Again, these areas must be unique for each drive, and the size ; of each area is determined by the values in the DPB. ; ; The size of the area addressed by ALV is determined by the maximum number of data blocks allowed for ; this particular disk, and is equal to 2 * (DSM / 8 + 1). Two copies of the allocation map for the disk ; are kept in this area: the first vector stores temporarily allocated blocks resulting from write operations, ; the second stores permanently allocated blocks resulting from CLOSE FILE operations. ;-- ; ; ; :::: PATCHES :::: :::: PATCHES :::: :::: PATCHES :::: :::: PATCHES :::: :::: PATCHES :::: :::: PATCHES :::: ; ; HEX STRINGS and ASSOCIATED DPB DESCRIPTIONS. ; ; Examples 0 through 1d, listed below, can be easily patched into the system. ; Example 2, listed below, cannot be easily patched and likely needs GENCPM rebuild of CP/M. ; ; The relevant hex string on your CP/M System Disk is : ; 0200040F01FF007F00C000008000000101 ; This string corresponds to the DPB entry for the original 512k REU as seen in CXRAMDSK.ASM . ; ; All dpb$RM$512 labels have been retained for rebuild compatibility. ; ; ; EXAMPLE 0 - 256k REU Filesystem Fix ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in 1764 REU. ; 0200030700FF007F00F000008000000101 - 256k REU hex patch (1764 is handled properly, instead of being assigned a 512k REU filesystem). ; ; ;dpb$RM$512: ; dpb 256,1,2048,1024,128,0 ; ;--> 256k REU PATCH <-- ; ; ; ;256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 1024 Tracks per Disk (256 Bytes x 1024 = 256k), ; ; 1024-Byte allocation units, 128 Dir Entries-Max ; ; ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 03,07h ; BLOCK SHIFT AND MASK (BSH, BLM) - Set 1024-Byte Blocks ; DB 00 ; EXTENT MASK (EXM) ; DW 00FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 256 Total Blocks (1024-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 0F0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - F0 00 here allocates Four 1024-Byte Blocks ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ; EXAMPLE 1 - 1 Meg Ramdisk with 2K blocks, 128 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in >512k mods. ; 0200040F00FF017F00C000008000000101 - 1M REU hex patch representing DPB as follows: ; ;dpb$RM$512: ; dpb 256,1,4096,2048,128,0 -- 1 Meg Ramdisk with 2K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 4096 Tracks per Disk (256 Bytes x 4096 = 1M), ; ; 2048-Byte allocation units, 128 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 04,0Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 2048-Byte Blocks ; DB 00 ; EXTENT MASK (EXM) ; DW 01FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 512 Total Blocks (2048-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - C0 00 here allocates Two 2048-Byte Blocks ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ; EXAMPLE 1a - 2 Meg Ramdisk with 2K blocks, 128 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in >512k mods. ; 0200040F00FF037F00C000008000000101 - 2M REU hex patch representing DPB as follows: ; ;dpb$RM$512: ; dpb 256,1,8192,2048,128,0 -- 2 Meg Ramdisk with 2K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 8192 Tracks per Disk (256 Bytes x 8192 = 2M), ; ; 2048-Byte allocation units, 128 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 04,0Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 2048-Byte Blocks ; DB 00 ; EXTENT MASK (EXM) ; DW 03FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 1024 Total Blocks (2048-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - C0 00 here allocates Two 2048-Byte Blocks ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ; EXAMPLE 1b - 4 Meg Ramdisk with 2K blocks, 128 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in >512k mods. ; 0200040F00FF077F00C000008000000101 - 4M REU hex patch representing DPB as follows: ; ;dpb$RM$512: ; dpb 256,1,16384,2048,128,0 -- 4 Meg Ramdisk with 2K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 16384 Tracks per Disk (256 Bytes x 16384 = 4M), ; ; 2048-Byte allocation units, 128 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 04,0Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 2048-Byte Blocks ; DB 00 ; EXTENT MASK (EXM) ; DW 07FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 2048 Total Blocks (2048-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 0C0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - C0 00 here allocates Two 2048-Byte Blocks ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ; EXAMPLE 1c - 8 Meg Ramdisk with 4K blocks, 128 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in >512k mods. ; 0200051F01FF077F008000008000000101 - 8M REU hex patch representing DPB as follows: ; ;dpb$RM$512: ; dpb 256,1,32768,2048,128,0 -- 8 Meg Ramdisk with 4K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 32768 Tracks per Disk (256 Bytes x 32768 = 8M), ; ; 4096-Byte allocation units, 128 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 05,1Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 4096-Byte Blocks ; DB 01 ; EXTENT MASK (EXM) ; DW 07FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 2048 Total Blocks (4096-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 080h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - 80 00 here allocates One 4096-Byte Block ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ; EXAMPLE 1d - 16 Meg Ramdisk with 8K blocks, 128 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB as a hex string. We must use this DPB area to patch in >512k mods. ; 0200063F03FF077F008000008000000101 - 16M REU hex patch representing DPB as follows: ; ;dpb$RM$512: ; dpb 256,1,65536,2048,128,0 -- 16 Meg Ramdisk with 8K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 65536 Tracks per Disk (256 Bytes x 65536 = 16M), ; ; 8096-Byte allocation units, 128 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 06,3Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 8192-Byte Blocks ; DB 03 ; EXTENT MASK (EXM) ; DW 07FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 2048 Total Blocks (8192-Bytes each) ; DW 007Fh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 128 entries require 4096-Byte Allocation in AL0, AL1 ; DB 080h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - 80 00 here allocates One 8192-Byte Block ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ; ;-- ; In the previous EXAMPLES, the DPB has been extended successfully with simple patching from 1 to 16M REU size but there are a few ; limitations to be noted: ; DSM Values greater-than 07FF and/or DRM values greater-than 007F may not be functional without a complete system rebuild, ; resulting in the following conclusions: ; 1) The Total Number of Blocks on a disk cannot be patched easily above 2048 without risk to the filesystem. ; 2) The Maximum Number of Directory Entries is limited to 128 with these simple patches. ; ; ; EXAMPLE 2 below sports a DPB that is nominally dialed-in properly, at least as far as several CP/M tools are concerned. ; Unfortunately, at this point, simple substitution of this string of bytes does not render a functional patch. ; ; The filesystem disagrees this patched DPB due to greater-than 127 (7F) DRM entry in the DPB below. ; We've not accounted for this DRM expansion anywhere else in CP/M and CP/M is therefore refusing to comply. ; In this case, the filesystem will flag 7F+1 (128) dir entries as "Free" and then flag the remaining entries as "Used". ; ; Rebuilding the entire CP/M system from source with new DPB data and using build tools and GENCPM appears to be the solution, ; but I've not tested this yet. However, CP/M 'Recompile Magic' (proper setting of other associated parameters elsewhere) ; 'should' hopefully result in a functional system that complies with new DPB data in source. ;-- ; ; ; EXAMPLE 2 - 8 Meg Ramdisk with 4K Blocks, 512 dir entries max ; 0200040F01FF007F00C000008000000101 - Original 512k REU DPB which we must use to patch in >512k mods ; 0200051F01FF07FF01F000008000000101 - 8M REU patch -with 512 Dir Entries- (NON-FUNCTIONAL) ; ;dpb$RM$512: ; dpb 256,1,32768,4096,512,0 -- 8 Meg Ramdisk with 4K blocks ; ;--> 256-Byte Sectors, 1 Sector (Two 128-Byte Records) per Track, ; ; 32768 Tracks per Disk (256 Bytes x 32768 = 8M), ; ; 4096-Byte allocation units, 512 Dir Entries-Max ; DW 0002 ; 128 BYTE RECORDS PER TRACK (SPT) ; DB 05,1Fh ; BLOCK SHIFT AND MASK (BSM, BLM) - Set 4096-Byte Blocks ; DB 01 ; EXTENT MASK (EXM) ; DW 07FFh ; MAXIMUM BLOCK NUMBER (DSM) - Set 2048 Total Blocks (4096-Bytes each) ; DW 01FFh ; MAXIMUM DIRECTORY ENTRY NUMBER (DRM) - 512 entries require 16384-Byte Allocation in AL0, AL1 ; DB 0F0h,00h ; ALLOC VECTOR FOR DIRECTORY (AL0, AL1) - F0 00 here allocates Four 4096-Byte Blocks ; DW 8000h ; CHECKSUM SIZE (CKS) ; DW 0 ; OFFSET FOR SYSTEM TRACKS (OFF) ; DB 1,1 ; PHYSICAL SECTOR SIZE SHIFT (PSH, PHM) ; ;-- ; APPENDIX 1 ; ; ; -DIRECTORY ENTRIES ; ; As noted by Bridger Mitchell in The Computer Journal, Issue 35: ; ; The CP/M directory structure is like a tree house that grew as the ; kids got bigger. First it was a simple platform (for CP/M 1.4 files). ; Rooms got rebuilt to handle larger files and larger disks, and the ; file control block got extended to provide random access (CP/M 2.2). ; And small passageways were crammed with filesize, datestamps, and ; passwords (CP/M 3). ; ; ; (The information that follows is from the manual page in cpmtools_2.7-1_i386 for *nix) ; ; The directory is a sequence of directory entries (also called physical extents), ; which contain 32 bytes of the following structure: ; ; St F0 F1 F2 F3 F4 F5 F6 F7 E0 E1 E2 Xl Bc Xh Rc ; Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al Al ; ; St is the status; possible values are: ; ; 0-15: used for file, status is the user number ; 16-31: used for file, status is the user number (P2DOS) or used ; for password extent (CP/M 3 or higher) ; 32: disc label ; 33: time stamp (P2DOS) ; 0xE5: unused ; ; F0-E2 are the file name and its extension. They may consist of any ; printable 7 bit ASCII character but: < > . , ; : = ? * [ ]. The file ; name must not be empty, the extension may be empty. Both are padded ; with blanks. The highest bit of each character of the file name and ; extension is used as attribute. The attributes have the following ; meaning: ; ; F0: requires set wheel byte (Backgrounder II) ; F1: public file (P2DOS, ZSDOS), foreground-only command ; (Backgrounder II) ; F2: date stamp (ZSDOS), background-only commands (Backgrounder ; II) ; F7: wheel protect (ZSDOS) ; E0: read-only ; E1: system file ; E2: archived ; ; Public files (visible under each user number) are not supported by CP/M ; 2.2, but there is a patch and some free CP/M clones support them ; without any patches. ; ; The wheel byte is (by default) the memory location at 0x4b. If it is ; zero, only non-privileged commands may be executed. ; ; Xl and Xh store the extent number. A file may use more than one ; directory entry, if it contains more blocks than an extent can hold. ; In this case, more extents are allocated and each of them is numbered ; sequentially with an extent number. If a physical extent stores more ; than 16k, it is considered to contain multiple logical extents, each ; pointing to 16k data, and the extent number of the last used logical ; extent is stored. Note: Some formats decided to always store only one ; logical extent in a physical extent, thus wasting extent space. CP/M ; 2.2 allows 512 extents per file, CP/M 3 and higher allow up to 2048. ; Bit 5-7 of Xl are 0, bit 0-4 store the lower bits of the extent number. ; Bit 6 and 7 of Xh are 0, bit 0-5 store the higher bits of the extent ; number. ; ; Rc and Bc determine the length of the data used by this extent. The ; physical extent is divided into logical extents, each of them being 16k ; in size (a physical extent must hold at least one logical extent, e.g. ; a blocksize of 1024 byte with two-byte block pointers is not allowed). ; Rc stores the number of 128 byte records of the last used logical ; extent. Bc stores the number of bytes in the last used record. The ; value 0 means 128 for backward compatibility with CP/M 2.2, which did ; not support Bc. ; ; Al stores block pointers. If the disk capacity is less than 256 ; blocks, Al is interpreted as 16 byte-values, otherwise as 8 double- ; byte-values. A block pointer of 0 marks a hole in the file. If a hole ; covers the range of a full extent, the extent will not be allocated. ; In particular, the first extent of a file does not necessarily have ; extent number 0. A file may not share blocks with other files, as its ; blocks would be freed if the other files is erased without a following ; disk system reset. CP/M returns EOF when it reaches a hole, whereas ; UNIX returns zero-value bytes, which makes holes invisible. ; ; Time stamps ; P2DOS and CP/M Plus support time stamps, which are stored in each ; fourth directory entry. This entry contains the time stamps for the ; extents using the previous three directory entries. Note that you ; really have time stamps for each extent, no matter if it is the first ; extent of a file or not. The structure of time stamp entries is: ; ; 1 byte status 0x21 ; 8 bytes time stamp for third-last directory entry ; 2 bytes unused ; 8 bytes time stamp for second-last directory entry ; 2 bytes unused ; 8 bytes time stamp for last directory entry ; ; A time stamp consists of two dates: Creation and modification date (the ; latter being recorded when the file is closed). CP/M Plus further ; allows optionally to record the access instead of creation date as ; first time stamp. ; ; 2 bytes (little-endian) days starting with 1 at 01-01-1978 ; 1 byte hour in BCD format ; 1 byte minute in BCD format ; ; Disc labels ; CP/M Plus support disc labels, which are stored in an arbitrary ; directory entry. The structure of disc labels is: ; ; 1 byte status 0x20 ; F0-E2 are the disc label ; 1 byte mode: bit 7 activates password protection, bit 6 causes ; time stamps on access, but 5 causes time stamps on ; modifications, bit 4 causes time stamps on creation and bit 0 is ; set when a label exists. Bit 4 and 6 are exclusively set. ; 1 byte password decode byte: To decode the password, xor this ; byte with the password bytes in reverse order. To encode a ; password, add its characters to get the decode byte. ; 2 reserved bytes ; 8 password bytes ; 4 bytes label creation time stamp ; 4 bytes label modification time stamp ; ; Passwords ; CP/M Plus supports passwords, which are stored in an arbitrary ; directory entry. The structure of these entries is: ; ; 1 byte status (user number plus 16) ; F0-E2 are the file name and its extension. ; 1 byte password mode: bit 7 means password required for reading, ; bit 6 for writing and bit 5 for deleting. ; 1 byte password decode byte: To decode the password, xor this ; byte with the password bytes in reverse order. To encode a ; password, add its characters to get the decode byte. ; 2 reserved bytes ; 8 password bytes ;-- ; ; APPENDIX 2 ; ; ; The following is by Bridger Mitchell as sourced from The Computer Journal, Issue 35 ; Please note : 'Tree House', in Mitchell's text, whimsically refers to the directory structure. ; ; ; The CP/M file system has two fundamental units of measurement: ; 1 record =128 bytes ; 1 logical extent =128 records = 16K bytes ; ; Records and logical extents are numbered sequentially, beginning with 0. ; ; Now consider a 17K file, with copies on several types of disks. Things might look like this. ; ; On Disk #1, 16K of data blocks fill up one directory entry. Then one entry corresponds to one logical extent. ; The 17K file will have 2 logical extents, and 2 directory entries. ; ; On Disk #2, 32K of data blocks fill up one directory entry. (How might this occur? Suppose a block is 4K, and block numbers are 2-byte ; values. 8*4K = 32K.) Now, one entry can hold two logical extents. The 17K file will have 2 logical extents, but only one directory ; entry. ; ; CP/M keeps track of logical extents with the EXtent byte, which can hold 0 to 31 (0 to 1F hex). After 31, it must again be 0. ; ; Why, you may well ask, does CP/M not allow more than 32 extent values in this field? Well, the tree house architect wasn't that farsighted. ; In the directory search functions, the BDOS uses a '?' character to indicate a "wild-card" search. When a '?' appears in the EXtent byte ; of an FCB, the BDOS will match any extent number. And since the '?' byte is 3F hex = 00111111 binary, only 5 bits are available to number ; logical extents! ; ; If five bits were indeed all that is available, CP/M files would be restricted to a maximum size of 32*block size. To allow larger files, ; the tree house added the S2 byte. It holds the "overflow" from the EXtent byte. Each unit of S2 thus represents 32 logical extents, and ; then the S2 byte can take a value from 0 to 3F hex. ; ; The full logical extent number is, therefore obtained by combining the EXtent byte and the S2 byte as follows: ; ; log_ext = (EXT & 1Fh) + ((S2 & 3Fh) << 5) ; ; (I use the c language operators: '&' is bitwise and, '<<' is shift-left). ; ; Note well that the high-order bits must really be masked; while the directory entry is active in the fcb, the BDOS uses the higher bits of ; the EXTent and S2 bytes for internal BDOS flags. ; ; Now, what is the directory entry number (the "physical extent")? It is the logical extent number, divided by the number of logical extents ; per directory entry. And that depends on the format, information that is _not_ in the directory, but in the BIOS's data structure for the ; drive -- the disk parameter block (dpb). ; ; entry_no = log_ext / extents_per_entry ; ; The _extent mask_ byte in the dpb encodes the number of logical extents per directory entry. Its value is ; ; extent_mask = 2 ** extents_per_entry - 1 ; ; A strange, but handy, representation, because it gives the number of times to right-shift the log_ext value to calculate the directory ; entry number. And, simultaneously, it is a bitmask that, applied to the EXTent byte, yields the number of logical extents within ; the current directory entry that are in use. ; ; entry_no = log_ext >> extent_mask ; ; = ((EXT & 1fh) >> extmask) + ((s2 & 1fh) << (5 - extmask)) ; ; How big is a file? What is its size in records, or equivalently, what is the record number of the file? It is the record count in the last ; directory entry (the number of records in the final logical extent), plus the size, in records, of all prior extents. Since the RC byte ; may be 80 hex, we must mask it. The formula is: ; ; recno = log_ext << 7 + (RC & 7Fh) ; ; Before considering practical answers to that question, let's consider how large a record number can ever be. The record count is 7 bits, ; the EXtent byte is 5 bits, and the S2 byte can be 6 bits, a total of 18 bits. The largest possible record number is therefore 2**18. ; Since there are 8 = 2*3 records in 1 kilobyte, the maximum filesize is 2**15 K = 32 MB, a large file indeed! ; ; This is the limit under CP/M Plus and ZSDOS. Regular CP/M 2.2, however, limited the record number to a 16-bit quantity (with the largest ; S2 value being 0F hex), and thus a maximum filesize of 4 MB. And I'm afraid most CP/M application programs expect that limit not to be exceeded. ;--