home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpminfo / cpm.doc < prev    next >
Text File  |  1994-07-13  |  44KB  |  888 lines

  1.         --------- CP/M Documentation as of 1-July-80 ---------
  2.  
  3.  
  4.   Although CP/M is  not the  ultimate in  operating systems  it is  an
  5. extremely useful software development tool which is implementable on a
  6. wide variety of  8080 and  Z80 based computers.   As a  result it  has
  7. become, by default if  not by design, the  standard of users of  these
  8. microprocessors.
  9.  
  10.      CP/M's implementability lays mainly  in the portability  afforded
  11. to it through the use of the BIOS (Basic I/O System).  This concept is
  12. a simple  but  very effective  solution  to the  problem  of  hardware
  13. variability inherent  in microprocessor-based  computer systems.   The
  14. S-100 bus  not  withstanding,  there  seems to  be  as  many  hardware
  15. configurations as there are designers (as an example, my own  computer
  16. has  a  56-pin  backplane  and  uses  memory-mapped  I/O).   It  is  a
  17. surprising and welcome gesture for  a software manufacturer to  supply
  18. the exact procedures necessary to bring  up its software.  By the  way
  19. of contrast, I know a very compulsive programmer who spent the  better
  20. part of a  month bringing up  ISIS on a  non-MDS hardware, whereas  it
  21. took less than a week for this same programmer to bring up CP/M on the
  22. same hardware.
  23.  
  24.      The  other  major   factor  in   CP/M's  success   must  be   the
  25. configurability.  How many microcomputer  users start with 64K  (which
  26. ISIS requires to do anything useful)?  With the ability to turn a  16K
  27. toy into a "real" computer, CP/M has ensured a very large following of
  28. devotees.
  29.      As mentioned  in the  users  manual, CP/M  is divided  into  four
  30. logically distinct but interacting parts:
  31.  
  32.   BDOS (Basic Disk Operating System)
  33.   BIOS (Basic Input/Output System)
  34.   CCP (Console Command Processor)
  35.   TPA (Transient Program Area)
  36.  
  37.      As there isn't a whole lot to  say about the TPA other than  that
  38. is where the transient programs are executed, and the users manual  is
  39. very explicit on the use of the CCP, I will deal mainly with the  BDOS
  40. and BIOS, which I consider to be the heart of the system.
  41.  
  42.  
  43.      THE BASIC DISK OPERATING SYSTEM (BDOS)
  44.  
  45.      The BDOS is the file manager  of CP/M.  Though it an  application
  46. program can:
  47.  
  48.      o Open a file
  49.      o Close a file
  50.      o Search for the first and subsequent entries in the
  51.        directory for a file
  52.      o Erase a file from the directory
  53.      o Read and Write logical records
  54.      o Create entries in the directory
  55.      o Rename entries in the directory
  56.  
  57.  
  58. as well as various  support functions.  These  functions mainly add  a
  59. level of abstraction to the disk  hardware as implemented in the  BIOS
  60. allowing the application  program(mer) to  deal with  the stored  data
  61. without having to know where it is physically located.  The BDOS  also
  62. acts as a conduit  between the application  program and the  character
  63. I/O entry points in the BIOS as well as providing some macro-functions
  64. for string I/O to and from the  console.  Thus through the use of  the
  65. BDOS, CP/M is able to transform a computer and its peripherals into  a
  66. generalized system with no particular hardware characteristics machine
  67. language itself.  (and through the use  of one of the many  "hi-level"
  68. languages which have been implemented to run under CP/M, it is possibe
  69. to solve problems with out even knowing or caring what kind of CPU  is
  70. running  the  show).   To  my  way  of  thinking,  this  is  a  rather
  71. significant development in micro-computing.
  72.  
  73.      The BDOS functions  are listed and  explained quite throughly  in
  74. the programmer's guide so I will not bore the reader (and make  myself
  75. type anyone than  I have  to) by  reiterating them  here.  However,  I
  76. would like  to  address  myself  to  a  couple  of  points  which  are
  77. associated with the BDOS.   First, while monkeying  around one day,  I
  78. discovered that there are some  strange locations at the beginning  of
  79. the BDOS which  can be  (and most  definitely are)  used by  transient
  80. programs even though they are  supposedly secure inside the  operating
  81. system.  Here is an outline of these locations:
  82.  
  83.          ORG     BDOS
  84. SERNUM:  DS      6
  85. ENTRY:   JMP     COMMAND
  86.          DW      BADIO
  87.          DW      SELERROR
  88.          DW      ROERROR
  89. MAP:     LXI     H,MAPTABLE     ; POINT HL AT LOG TO PHYS MAPPING
  90.          MVI     B,0            ; SET BC TO LOGICAL SECTOR (IN C)
  91.          DAD     B              ; INDEX INTO MAPPING TABLE
  92.          MOV     C,M            ; CONVERT LOGICAL TO PHYSICAL SECTOR
  93.          JMP     SETSECTOR      ; DO BIOS SET SECTOR FUNCTION
  94.          NOP
  95.  
  96. MAPTABLE:
  97.      DB     1,7,13,19
  98.      DB     25,5,11,7           ; THIS TABLE MAPS LOGICAL ONTO
  99.      DB     23,3,9,15           ; PHYSICAL DISK SECTORS
  100.      DB     21,2,8,14
  101.      DB     20,26,6,12
  102.      DB     18,24,4,10
  103.      DB     16,22,0,0
  104.      DB     0,0,0,0             ; THE NULLS ARE ADDRESS ALINGMENT
  105. CONFIG:
  106.      DB     SECPT               ; SECTORS PER TRACK(26 ON IBM DISKS)
  107.      DB     LASTDIR             ; NUMBERS OF LAST DIRECTORY ENTRY (63)
  108.      DB     RPB                 ; 2**RPB=RECORDS PER BLOCK (3)
  109.      DB     LASTSEC             ; LAST SECTOR IN BLOCK (7)
  110.      DB     LASTBLOCK           ; LAST BLOCK ON THE DISK (242)
  111.      DB     DIRALLOC            ; DIRECTORY ALLOCATION MASK (C0 HEX)
  112.      DB     DIRTACK             ; TRACK THAT DIRECTORY BEGINS ON (2)
  113.  
  114.      The first 6 bytes  of the BDOS contain  the serial numbers  which
  115. supposedly prevent  copyright infringments.   The next  3 contain  the
  116. actual entry point to the BDOS command decoder.  The next 9 bytes  are
  117. the addresses of routines inside the BDOS which are executed when  one
  118. of the three fatal errors occur.  Following the error addresses is the
  119. routine which the BDOS uses to stagger its data on a track followed by
  120. the mapping table.  After the  mapping table are some constants  which
  121. define the particular implementation of CP/M on specific size disks.
  122.  
  123.      The error address can  be modified by  an application program  to
  124. recover somewhat more gracefully from a fatal error (I ran across this
  125. in a  screen-oriented text  editor  which made  possible to  save  the
  126. memory image of the  program after an  I/O error).  The  configuration
  127. table data can be used  (and is used by STAT  in version 1.4) to  find
  128. out what's going on with disk space.
  129.  
  130.      As far  as  I  can  tell, it  is  not  serendipitious  that  this
  131. information exists and  is located  where it  is.  It  seems that  the
  132. people at Digital  Research left room  for certain easy  modifications
  133. and enhancements to CP/M.
  134.  
  135.      The second point I  would like to address  is the technique  CP/M
  136. uses to do  housekeeping on  its files.   All of  the file  management
  137. functions in the BDOS use the  address of an FCB (file control  block)
  138. as their parameter.  Like  the concept of  the I/O byte  the FCB is  a
  139. rather  elegant  solution  to  the  housekeeping  problem  in  a  file
  140. management system.   It accomplishes  two  things for  the  operations
  141. system.  First  it  "decentralizes"  the process  of  maintaining  and
  142. second, it gives application programs  access to the same  information
  143. about a file that the BDOS uses.  These two factors together allow  an
  144. application to get  as close  as it  requires to  the file  management
  145. process.  There are some problems with the FCB, as implemented.  First
  146. there is the question of whether  or not an application OUGHT to  have
  147. access to housekeeping information as  it is possible to louse  things
  148. up pretty badly if things are not  done correctly.  This I think is  a
  149. matter of taste.  Since CP/M, as implemented, is a single-user  system
  150. there isn't the problem of messing other people's files up and it is a
  151. foolish programmer  indeed  who  messes with  something  that  is  not
  152. understood.
  153.  
  154.      The other  problem is  a  bit more  serious  (rumor has  it  that
  155. Digital Research is dealing with  it).  Since the largest number  that
  156. an allocated block  can have (see  below) is 255(D)  and with a  block
  157. size of an even 2k,  the maximum number of  bytes of bytes of  storage
  158. CP/M can address  on one  disk is approximately  510 kilobytes.   This
  159. presents serious  impediments  (due  to  program  standardization)  to
  160. implementing the operating system on the larger disk systems  becoming
  161. available.
  162.  
  163.      Be that as it may, within the scope of single (or perhaps double)
  164. density floppy disks, the  FCB is in my  opinion, a stroke of  genius.
  165. Because  an  application,  through  the  use  of  the  FCB,  has  such
  166. flexibility it behooves the assembly language programmer to understand
  167. as best he or she can how to use it.  The programmer's guide describes
  168. the format and I'll elaborate a bit on it.
  169.  
  170.      The FCB consists  of seven  fields of information  each having  a
  171. mnemonic associated with it.  They are:
  172.  
  173.          FIELD           FCB POSITIONS
  174.          ET              0
  175.          FN              1-8
  176.          FT              9-11
  177.          EX              12
  178.          NOT USED        13-14
  179.          RC              15
  180.          DM              16-31
  181.          NR              32
  182.  
  183.      The FN and FT fields are only logically distinct.  The BDOS  uses
  184. all eleven bytes as  a fundamental unit  of information when  opening,
  185. closing, creating,  erasing, searching  and renaming  files.  (The  EX
  186. field is included during OPEN,  CLOSE, CREATE and SEARCH  operations).
  187. It is the CCP and transient programs which make a distinction  between
  188. file name and file type.
  189.  
  190.      Since a file can be of any length up to the capacity of the  disk
  191. and since a single FCB describes only 16k bytes of a file, there  must
  192. be a way to link multiple sections of a file each described by its own
  193. FCB.  This is done via the EX  field.  The first extent of a file  has
  194. an EX value of 0, the second a value of 1 and so on.
  195.  
  196.      The  ET  field  is  an  interesting  mixture  of  usefulness  and
  197. ambiguity.  When the FCB is stored  in the directory the ET filed  may
  198. contain a  0,  indicating  that  the  entry  is  used,  or,  an  E5(H)
  199. indicating that the entry has been deleted (or never used).   However,
  200. when the FCB  is used as  a parmeter  for one of  the file  management
  201. functions, the ET field serves an entirely different purpose.  If ET=0
  202. then then  the BDOS  will  assume that  the  command pertains  to  the
  203. currently selected disk.  If  the ET field is  not zero then the  BDOS
  204. assumes that  it contains  the  disk number+1  to which  the  function
  205. pertains.  In this case the  BDOS will temporarily select disk  number
  206. ET-1 and then clear  ET to zero before  proceeding with the  requested
  207. function.  When the file operation is complete, the BDOS will  restore
  208. ET to its  original value.   Thus, an application  program need  never
  209. concern itself with remembering or  selecting specific disks as  these
  210. values are retained throughout processing  from the time that the  CCP
  211. sets them up in the default FCBs.
  212.  
  213.     The RC field  is essentially an  "end of extent"  pointer.  It  is
  214. "pushed" along by the NR  field when writing is  used to limit the  NR
  215. field during reading to prevent the reading unwritten data.
  216.  
  217.      The DM field is an array of 16 bytes, each representing a logical
  218. block of data  within the extent.   The value of  each of these  bytes
  219. represents the physical area  of disk space  allocated to the  logical
  220. block unless the  value is 0,  in which  case the block  has not  been
  221. allocated any disk space.
  222.  
  223.      Together, the RC and  DM fields form  a "current" description  of
  224. the locations on the disk used by the data contained with the  extent.
  225.  
  226.      The NR field  is used to  specify which record,  relative to  the
  227. beginning of the  extent, is  to be read  or written.   The BDOS  will
  228. automatically increment this number during read and write  operations,
  229. making sequential file access virtually automatic.
  230.  
  231.  
  232.                      The BASIC I/O SYSTEM (BIOS)
  233.                      ===========================
  234.  
  235.      This section  of the  system concerns  itself with  the  hardware
  236. dependent aspects of I/O.  There are two types:
  237.  
  238.          1) Disk I/O, which is block oriented
  239.          2) Character I/O, which is byte oriented
  240.  
  241. It is convenient to consider these  two aspects seperately as they  do
  242. not interact directly.
  243.  
  244.      Looking at  the  BIOS  jump  table  (as  described  in  the  CP/M
  245. documentation),  the   first  two   entries   are  paths   to   system
  246. initialization routines.   The  next  six  are  entry  points  to  the
  247. character I/O routines and the rest  are entry points to disk I/O  and
  248. disk support routines.  The section called "BIOS Entry Points" in  the
  249. System Alteration Guide  describes the  function of each  of these  15
  250. entry points better than I could.  However, what the guide does not do
  251. (as it is only a manual) is point out the importance of the I/O  byte.
  252. I consider this to be deserving of special attention.
  253.  
  254.      Experience (mostly  my  own)  has  shown  that  until  one  makes
  255. concrete use of the  I/O byte concept, it  is difficult to  appreciate
  256. the elegance of this  technique.  It does have  its limits, but it  is
  257. very simple  and effective  solution to  CP/M's character  I/O  device
  258. standardization problems.
  259.  
  260.      I first ran  across this  concept on the  MDS MOD-80  development
  261. system which did  not have  disk drives and  used paper  tape for  its
  262. off-line storage.  The Intel Monitor used a standard jump table  which
  263. allowed programs to  do character  I/O without  necessarily having  to
  264. worry about the actual hardware devices.  Perhaps you've seen it,  but
  265. in case you haven't here it is.
  266.  
  267.         ORG     MOINTOR
  268.         JMP     MAINLINE
  269.         JMP     CI              ; CONSOLE INPUT
  270.         JMP     RI              ; READER INPUT
  271.         JMP     CO              ; CONSOLE OUTPUT
  272.         JMP     PO              ; PUNCH OUTPUT
  273.         JMP     LO              ; LIST OUTPUT
  274.         JMP     CSTS            ; RETURN CONSOLE STATUS
  275.         JMP     IOCHK           ; RETURN I/O BYTE
  276.         JMP     IOSET           ; CHANGE I/O BYTE (NEW VALUE IN C)
  277.         JMP     MEMCK           ; RETURN TOP OF USER MEMORY IN A AND B
  278.  
  279.      As you can see, this does  basically the same that the BIOS  jump
  280. table does.  IOCHK, IOSET and MEMCHK are not needed in the BIOS  since
  281. the information returned  by these  routines are located  in the  zero
  282. page of CP/M's memory.
  283.  
  284.      As the  alteration  guide  is  not explicit  on  the  subject  of
  285. implementing an I/O byte, I'll  outline in assembly language code  the
  286. techniques I've found useful for a generalized implementation.
  287.  
  288.      But first notice that each of the six character I/O routines must
  289. decode out the path to  the specific I/O device "currently  assigned".
  290. The way  this  is done  (in  English) is  as  follows.  The  I/O  byte
  291. contains four fields, each as consisting  of low bits.  Each field  is
  292. associated with one of  the four "logical"  I/O devices (List,  Punch,
  293. Reader and  Console) and  may take  on the  value of  (in binary)  00,
  294. 01,10, or 11.  Thus, up to four different physical I/O devices may  be
  295. associated with each of the four "logical" devices.  For example,  the
  296. logical device  "List."  By  manipulating the  value of  these 2  bits
  297. (presumably) without affecting the rest of the byte) one may  "assign"
  298. a specific hardware driver (and the device itself) to the list device.
  299. In PASCALese this is the decoder:
  300.  
  301.          VAR IOBYTE(4): PACKED ARRAY OF (0..3)
  302.          DO CASE IOBYTE(4)
  303.          0:  TTYOUT;
  304.          1:  LPTOUT;
  305.          2:  CRTOUT;
  306.          3:  USERLIST;
  307.          END
  308.  
  309.      The alteration guide and documentation  on PIP and STAT  describe
  310. what these physical device might be.  The command:
  311.  
  312.        STAT VAL:
  313.  
  314. produces essentially a menu of the nominal physical devices assignable
  315. in the CP/M system.
  316.  
  317.      Here is some software:
  318.  
  319.         IOBYTE  EQU     3       ; LOCATION OF IOBYTE
  320.         CMASK   EQU     03H     ; CONSOLE MASK
  321.         RMASK   EQU     0CH     ; READER MASK
  322.         PMASK   EQU     30H     ; PUNCH MASK
  323.         LMASK   EQU     0C0H    ; LIST MASK
  324.         RD2     EQU     08H     ; READER DEVICE 2 MASK
  325.         PD2     EQU     40H     ; PUNCH DEVICE 2 MASK
  326. CONTIN:
  327.         LDA     IOBYTE          ; GET IOBYTE
  328.         ANI     CMASK           ; LOOK AT CONSOLE FIELD
  329.         JZ      TTYIN           ; CONSOLE 0:
  330.         JPE     UCIN1           ; CONSOLE 3:
  331.         RAR                     ; CONSOLE1:
  332.         JC      KBDIN           ; CONSOLE 1:
  333.         JMP     BACHIN          ; CONSOLE 2
  334. ONOUT:
  335.         LDA     IOBYTE          ; GET IO BYTE
  336.         ANI     CMASK           ; LOOK AT CONSOLE FIELD
  337.         JZ      TTYOUT          ; CONSOLE 0:
  338.         JPE     UCOUT1          ; CONSOLE 3:
  339.         RAR                     ; LOOK FOR CONSOLE 1
  340.         JC      CRTOUT          ; CONSOLE 1:
  341.         JMP     BACHOUT         ; CONSOLE 2
  342. CONSTAT:
  343.         LDA     IOBYTE
  344.         ANI     CMASK
  345.         JZ      TTYSTAT         ; CONSOLE STATUS 0
  346.         JPE     UCSTAT1         ; CONSOLE STATUS 3
  347.         RAR
  348.         JC      KBDSTAT         ; CONSOLE STATUS 3
  349.         RET                     ; DON'T KNOW WHAT STATUS 2 IS
  350. READER:
  351.         LDA     IOBYTE
  352.         ANI     RMASK           ; LOOK AT READER BITS ONLY
  353.         JZ      TTYIN           ; READER 0
  354.         JPE     UR2             ; READER 3:
  355.         ANI     RD2             ; SEE IF EITHER READER 1 OR 2
  356.         JZ      PT              ; READER 1 IF ZERO
  357.         JMP     UR1             ; READER 2 IF NOT
  358. PUNCH:
  359.         LDA     IOBYTE
  360.         ANI     PMASK
  361.         JZ      TTYOUT          ; PUNCH 0
  362.         JPE     UP2             ; PUNCH 3
  363.         ANI     PD2             ; SEE IF EITHER READER 1 OR 2
  364.         JZ      PTP             ; PUNCH 1 IF ZERO
  365.         JMP     UP1             ; PUNCH 2 IF NOT
  366. LIST:
  367.         LDA     IOBYTE
  368.         ANI     LMASK
  369.         JZ      TTYOUT          ; LIST 0:
  370.         JPE     UL1             ; LIST 3:
  371.         JM      LPTOUT          ; LIST 2:
  372.         JMP     CRT             ; LIST 1:
  373.  
  374.      The logical device "BATCH" is a hangover from the Intel I/O  byte
  375. definition.  It was originally intended  to allow console input to  be
  376. some continuous input device, such as papertape, tape, and the console
  377. output be a hard copy device such as a line printer.  This was to make
  378. it possible to create "jobs" offline and then run them unattended.
  379.      CP/M's SUBMIT  command is  the counterpart  of this  function  in
  380. floppy  disk  land,  though  it  seems  to  have  lost  a  little   in
  381. translation.
  382.  
  383.      What all this buys us  as CP/M users is  this: If you can  figure
  384. out how to make a piece of  hardware accept or produce data, one  byte
  385. at a time, then  it can be  assigned to one or  more of these  logical
  386. devices.  You are then able to treat it in application software as you
  387. would any  of the  standard character  I/O devices  such as  a CRT  or
  388. keyboard.
  389.  
  390.      As for the  disk I/O,  things are not  quite as  elegant but  are
  391. standarized so as not  to ruin CP/M's  implementability.  It looks  as
  392. though Digital Research asked themselves,  "What does all floppy  disk
  393. hardware need  from the  processor to  perform disk  I/O?"  Again  the
  394. alteration guide is a better place than here to answer that  question.
  395. However, I'd like to discuss something that was discovered during  the
  396. implementation of CP/M on my hardware.
  397.  
  398.      By the way  of background, the  disk hardware on  my computer  is
  399. totally bizarre.  It uses a 256 byte,  hard sectors.  16 of them on  a
  400. track, and  writes  the  bytes  bitwise,  backwards  with  respect  to
  401. literally everyone else.  I did everything short of making a pact with
  402. Methistopheles (the devil's agent.  Ed.) in order to make my equipment
  403. look like  IBM compatible  hardware.   In experimenting  with  various
  404. techniques, both  my own  and from  suggestions of  others, I  finally
  405. implemented a  full-track (26  IBM  sectors) buffering  concept  which
  406. works very well  for me.   I can provide  listings of  this if  anyone
  407. cares to see it.
  408.  
  409.      The  important   thing   that   I  discovered   was   that   most
  410. implementations of  the BIOS  do not  have provisions  for read  after
  411. write, or similar way of verifing that the data was written correctly.
  412. Obviously, with  the  low  error rate  floppy  disks  supposedly  have
  413. (something 1 in 6 million bytes) the overhead of doing a verify  after
  414. write may not be justified.  Since I was writing software which, if  a
  415. bit were  dropped, could  unpleasantly affect  all future  development
  416. (including modifications  to CP/M  itself)  I felt  that I  could  not
  417. afford even that small chance of  error or at least not knowing  about
  418. the error  if it  should occur.   But here's  the catch:  In order  to
  419. verify one sector after writing you  have absolutely no choice but  to
  420. wait for the disk to bring it around again.  This means that in  order
  421. to write one whole track this way, it will take 26 revolutions of  the
  422. disk!  At a sixth of a second per revolution (on 8 inch floppy drives)
  423. that amounts  to 4  1/3 seconds  which is  obviously unacceptable  and
  424. totally unjustifiable.  Using a full track buffer, I was able to solve
  425. that problem very neatly.   It took one revolution  to write the  data
  426. and one revolution to verify that  it was written correctly.  This  is
  427. approximately 300 milliseconds or a savings of over 400 percent.
  428.  
  429.      The following is a detailed explanation of disk space  allocation
  430. and what the BDOS does with the FCB during file I/O.
  431.  
  432.                         Disk Space Allocation
  433.                         =====================
  434.  
  435.      First number  all of  the sectors  on  the disk  from 1  to  2002
  436. starting at sector  1 track zero.   I'll refer to  this number as  the
  437. "INTEGER SECTOR  NUMBER"  or ISECTOR.   CP/M  reserves the  first  two
  438. tracks (ISECTOR 1-52) for holding image  of the system as well as  the
  439. bootstrap program  (see  the  Alteration Guide  section  of  the  CP/M
  440. documentation).  Thus there  are 1950  sectors available  on a  single
  441. density dsk starting at ISECTOR 53.
  442.  
  443.      For reasons which should become apparent, space is not  allocated
  444. on a sector-by-sector basis.  Instead, space is allocated in 8  sector
  445. blocks (I'm  told that  the  double density  version allocates  in  16
  446. sector blocks).  That means there are  1950/8 or 243 (remainder of  6)
  447. allocatable blocks of disk space.  Blocks zero and 1 are pre-allocated
  448. (when a disk is logged on) for the directory and the 6 sectors in  the
  449. partial block  are not  used.   Thus there  are 241  blocks  (241*1024
  450. bytes) of disk space available on the disk for data, starting at block
  451. 2.
  452.  
  453.      In order to keep  track of which blocks  have been allocated  and
  454. which have not,  the BDOS  maintains an "allocation  VECTOR" for  each
  455. logged-on disk.  This data structure is  an array of 256 bits  (packed
  456. into 32 bytes),  where each  bit is  associated by  position with  the
  457. corresponding block of disk space.  For example the first two bits  in
  458. the array are always  turned on indicating that  the first two  blocks
  459. are allocated to the directory.  The allocation vector is created when
  460. a disk is  selected for the  first time (logged  on) after  re-booting
  461. (booting), by  examining the  DM  (Disk Map)  field of  all  non-empty
  462. (ET=0) FCBs  (File Control  Blocks) in  the directory.   Since the  DM
  463. field of an extent  contains the numbers of  all the blocks  belonging
  464. (allocated) to the  extent, the  collection of  all DM  fields in  the
  465. directory describe all space that is  currently used on the disk.   It
  466. is a relatively simple task to look at  a byte in a DM field and  turn
  467. on its associated bit in the allocation vector, which is exactly  what
  468. the BDOS does.
  469.  
  470.      After the allocation vector is created, it is updated whenever  a
  471. new block is allocated, as are the DM byes for the extent.  It  should
  472. be noted, I think that this  technique for space management does  away
  473. with specific  file  and free  space  linking  as well  as  having  to
  474. explicitly store a bit map on the disk.
  475.  
  476.      It is  an  easy  task  to  translate  block  numbers  into  their
  477. corresponding physical  track  and sector  numbers  on the  disk.   By
  478. including in the process, a relative record number (from the beginning
  479. of an extent) it is not much more work to map the logical records of a
  480. file onto  exact physical  disk locations.   This is,  I believe,  the
  481. virtue of CP/M's file structure.
  482.  
  483.  
  484. 11.5  Disk Mapping Process
  485.  
  486.      There are three steps to the mapping process:  let:
  487.  
  488.      ISECTOR = integer sector number
  489.      TRACK   = track containing sector
  490.      LSECTOR = logical sector (before staggering)
  491.      PSECTOR = physical sector (after staggering)
  492.      BLOCK   = block containing logical record
  493.  
  494.      1.  The first  step is  to calculate the  integer sector  number.
  495.          This is done using:
  496.  
  497.               ISECTOR = (BLOCK*8) + (NR MOD 8) +52
  498.  
  499.               We multiply BLOCK by 8  because there are 8 sectors  per
  500.          block.  NR MOD  8 produces the  displacement into the  block,
  501.          and 52 must be added because block zero begins at ISECTOR  52
  502.          (remember that the  System image  is stored in  the first  52
  503.          disk sectors).   Note: if  you're  unfamiliar with  the  term
  504.          "MOD", it means "Use the remainder produced by the division".
  505.          E.G.:
  506.  
  507.               25 MOD 5  =  0 (5*5=25 remainder 0)
  508.               25 MOD 7  =  4 (3*7=21 remainder 4)
  509.               25 MOD 27 = 25 (27*0=0 remainder 25)
  510.  
  511.      2.  Next we calculate the physical track and logical sector.
  512.  
  513.               TRACK = ISECTOR/26
  514.               LSECTOR = ISECTOR MOD 26
  515.  
  516.          (26 because there are 26 sectors on a track.)
  517.  
  518.      3.  Finally we map the logical sector using the routine "MAP"  at
  519.          the beginning of the BDOS:
  520.  
  521.               PSECTOR = MAPTABLE(LSECTOR)
  522.  
  523.          There is an algorithm which will produce "MAPTABLE":
  524.  
  525.               DIMENSION MAPTABLE(26)
  526.               MAP(1)=1
  527.               FOR N=2 TO 26
  528.               IF N > 13 THEN J=1
  529.                    ELSE J=0
  530.               MAP(N)=((MAP(N-1)+6 MOD 26) +J
  531.               NEXT N
  532.               END
  533.  
  534.          What this really does is  stagger the logical sectors  around
  535.          the track so that they are actually 5 physical sectors apart.
  536.          This allows CP/M  five sectors  or about  35 milliseconds  of
  537.          processing time between  disk accesses  in order  to keep  up
  538.          with disk latency.
  539.  
  540.  
  541. 11.6  FILE CONTROL BLOCK USAGE DURING I/O
  542.  
  543.      In general the  following explains  what happens when  a read  or
  544. write operation is requested by an application.  There are a series of
  545. exceptions which the BDOS  checks for, including  "READ PAST EOF"  and
  546. "DISK FULL"..  Foor the sake of  this discussion let us assume that  a
  547. write operation is being requested.  Reading is very similar.
  548.  
  549.      The BDOS will first increment the NR field and determine  whether
  550. or not the next record is within a previously allocated block.
  551.  
  552.           NR := NR+1
  553.           IF DM(NR/8) = 0
  554.             THEN BLOCK NOT ALLOCATED
  555.             ELSE BLOCK IS ALLOCATED
  556.  
  557. "NR/8" determines which of the DM bytes (logical blocks) contains  the
  558. logical  record  in  question.   If  a  physical  block  needs  to  be
  559. allocated, the BDOS will search through the allocation vector to  find
  560. an unallocated block  (which can be  anywhere on the  disk).  If  none
  561. exists (i.e.  no  zero bits in  map) then a  "DISK FULL" condition  is
  562. returned to the application  (calling program).  If  a block is  found
  563. its bit is turned on and its number is deposited at location DM(NR/8).
  564.  
  565.      After this, the  absolute sector is  determined and is  converted
  566. into the physical track and sector numbers.  Then the data is written.
  567.  
  568.      The read operation is similar except that no space allocation  is
  569. performed.  Instead,  if  the record  being  requested to  read  falls
  570. within an unallocated block a "READ PAST EOF" condition is returned to
  571. the application (Calling program).
  572.  
  573.      For both the read and write  operations, if the NR field goes  to
  574. 128 decimal when incremented, the BDOS will close the current  extent,
  575. increment the EX  field and  attempt to open  the next  extent of  the
  576. file.  If the operation was a write and the next extent doesn't exist,
  577. the BDOS will create it and then open it.  If the operation was a read
  578. and the next  extent doesn't  exist, a  "READ PAST  EOF" condition  is
  579. generated as it will in the case that either NR is greater that 128 or
  580. NR is greater than RC is true.
  581.  
  582.      Here is an example:
  583.  
  584.           LET:
  585.           NR = 12
  586.           RC = 17
  587.           DM(0) = 50
  588.           DM(1) = 51
  589.           DM(2)-DM(15) = ANYTHING.
  590.  
  591.           1.     NR := NR+1      (NR=13)
  592.  
  593.           2.     LBLOCK  = NR/8
  594.                          = 12/8
  595.                          = 1
  596.  
  597.           3.     BLOCK   = DM(LBLOCK)
  598.                          = DM(1)
  599.                          = 51
  600.  
  601.           4.     ISECTOR = (BLOCK*8)+(NR MOD 8) + 52
  602.                          = (51*8)+(13 MOD 8) + 52
  603.                          = 408+4+52
  604.                          = 464
  605.  
  606.           5.     TRACK   = ISECTOR/26
  607.                          = 464/26
  608.                          = 17
  609.  
  610.           6.     LSECTOR = ISECTOR MOD 26
  611.                          = 464 MOD 26
  612.                          = 22
  613.  
  614.      7.     PSECTOR = MAPTABLE(LSECTOR)
  615.                          = MAPTABLE(22)
  616.                          = 24
  617.  
  618. Thus the sector being accessed is 24 on track 17.
  619.  
  620.  
  621.                          Miscellaneous Stuff
  622.  
  623.      To conclude, there are some interesting tid-bits which the reader
  624. may (or may not for that matter) find useful.
  625.  
  626.      The SUBMIT processor in the CCP (Console Command Processor)  uses
  627. a very  interesting programming  technique  which I  found  worthwhile
  628. understanding.  Recall that the transient program "SUBMIT" uses as its
  629. input a text file of CP/M commands and produces a file called  $$$.SUB
  630. which the CCP will  use as a command  file.  For example consider  the
  631. following submit file:
  632.  
  633.      A:PIP B:PROG.ASM=B:PO.SRC,B:P1.SRC(CR)(LF)
  634.      A:ASM PROG.BBB(CR)(LF)
  635.      A:LOAD B:PROG(CR)(LF)
  636.      A:PIP LST:=B:PROG,PRN(T8P50)(CR)(LF)
  637.  
  638.      The Submit  program turns  the file  into a  series of  128  byte
  639. records arranged such that the first line of the original file is  the
  640. last record of the new  file, the second line  the second to the  last
  641. record and so on.  Each record has the following form:
  642.  
  643.      byte 1:             length
  644.      bytes 2-length+1:   command string
  645.      bytes length+2-128: undefined
  646.  
  647.      The above file  would look  like this when  converted to  $$$.SUB
  648. (the numbers  in  decimal and  brackets  are included  here  just  for
  649. clarity):
  650.  
  651.      record 1:  (28)A:PIP LST:=B:PROG,PRN(T8P50)
  652.      record 2:  (13)A:LOAD B:PROG
  653.      record 3:  (14)A:ASM PROG.BBB
  654.      record 4:  (33)A:PIP B:PROG.ASM=B:PO.SRC,B:P1.SRC
  655.  
  656.      Why, you  ask, is  $$$.SUB backwards?   Well that's  part of  the
  657. trick.  Remember that  the RC field  in the  FCB is an  end of  extent
  658. pointer.  If the $$$.SUB file is on the disk the CCP will open it  and
  659. set the NR field of the FCB to RC-1 and read the file.  What this does
  660. is read  in the  last record  of the  file (as  determined by  the  RC
  661. field).  After the read the BDOS will decrement the RC field and close
  662. the file, which will cause the FCB and specifically the RC field to be
  663. updated.  The CCP will use  the data just read as  a command as if  it
  664. had been typed  in from the  console.  The next  time around, the  CCP
  665. will do the same thing except that the RC field is now pointing at the
  666. record whose number is one less  than that of the previous  operation.
  667. In other words, the  RC field is used  as an implicit record  pointer.
  668. Very neat and it works too!
  669.  
  670.      Sometimes it is  desirable to  bypass the  BDOS and  communicated
  671. directly with certain BIOS  functions.  For example MICROSOFT's  BASIC
  672. interpreter does not use the BDOS  character I/O functions as it  does
  673. its own line editing  and the USCD  Pascal system completely  overlays
  674. the BDOS.  There is a technique for accessing the BIOS that is general
  675. enough so as not to be considered a kludge.  An application can always
  676. find the page boundary on which the BIOS begins by examining the  high
  677. order address byte of  the warm boot entry  point.  Using that as  the
  678. high order byte of the address the low order byte is set to an  offset
  679. into that pags determined by:
  680.  
  681.      OFFSET=FUNCTION * 3
  682.  
  683. as each entry  is three bytes.   For example here  is a short  routine
  684. which causes data to be written to the list device:
  685.  
  686. LOFF     EQU     0FH            ; BIOS+LOFF=LIST ENTRY
  687.  
  688. LIST:
  689.         PUSH    H               ; SAVE HL
  690.         LHLD    1               ; GET ADDRESS OF WARM BOOT
  691.         MVI     L,LOFF          ; SET LOW ORDER BYTE TO LIST OFFSET
  692.         XTHL                    ; RESTORE HL, LIST ADDRESS ON STACK
  693.         RET                     ; EXECUTE LIST ROUTINE IN BIOS
  694.  
  695. This technique works as  long as the BIOS  begins on a page  boundary.
  696. The more general technique would be:
  697.  
  698. LOFF     EQU     0FH-3          ; OFFSET FROM WARM BOOT ENTRY
  699.  
  700. LIST:
  701.         PUSH    H               ; SAVE HL
  702.         LHLD    1               ; GET ADDRESS OF WARM BOOT
  703.         PUSH    D               ; SAVE DE
  704.         LXI     D,LOFF
  705.         DAD     D               ; GET TO LIST ENTRY POINT
  706.         POP     D               ; RESTORE DE
  707.         XTHL                    ; RESTORE HL
  708.         RET                     ; EXECUTE LIST ROUTINE
  709.  
  710.      There is one more thing and then I'll quit.  If you remember from
  711. above, I mentioned that while  poking around inside a  screen-oriented
  712. text editor, I found that it modified the error address fields at  the
  713. beginning of the  BDOS.  It also  does another clever  thing.  In  the
  714. editor there is  a command  to save  the rest  of the  file, exit  the
  715. editor  and  automatically  process  it  with  an  entirely  different
  716. program, such  as  an  assembler  or  text  formatter.   There  is  an
  717. interesting technique  here which  could  be generally  useful.   What
  718. happens is  this:  First, modify  the  address of  the  console  input
  719. routine in  the  BIOS  jump  table  to  cause  a  routine  inside  the
  720. application program  to supply  data  to the  CCP.   This is  done  as
  721. follows:
  722.  
  723.         LHLD    1               ; GET THE BIOS PAGE ADDRESS IN H
  724.         MVI     L,CI+1          ; HL IS THE ADDRESS OF THE ADDRESS
  725.                                 ; OF CONSOLE INPUT ROUTINE
  726.         MOV     E,M             ; GET THE DEVICE ADDRESS IN DE
  727.         INX     H
  728.         MOV     D,M
  729.         XCHG
  730.         SHLD    SAVE            ; SAVE IT OR LATER
  731.         LXI     D,ALT           ; DE IS ADDRESS OF ALTERNATE ROUTINE
  732.         MOV     M,D             ; POKE JUMP ADDRESS IN BIOS JUMP TABLE
  733.         DCX     H
  734.         MOV     M,E
  735.         JMP     0               ; AND RE-BOOT
  736. SAVE:   DS      2               ; LOCATION USED TO SAVE
  737.                                 ; CONSOLE INPUT DEVICE ADDRESS
  738.  
  739.      After doing this,  everytime the  CCP requests  a character  from
  740. what it  thinks is  the console  input  device, it  will be  handed  a
  741. character from inside the  original application program.  Just  before
  742. handing the CCP a  carriage return, the  application will restore  the
  743. original address of the console input routine:
  744.  
  745.         LHLD    SAVE            ; GET ORIGINAL DEVICE ADDRESS
  746.         XCHG                    ; PUT IN DE
  747.         LHLD    1               ; GET ADDRESS OF ADDRESS FIELD OF
  748.                                 ; CONSOLE INPUT ENTRY
  749.         MVI     L,CI+1
  750.         MOV     M,D             ; RESTORE ORIGINAL ADDRESS
  751.         INX     H
  752.         MOV     M,E
  753.                                 ; ... AND CONTINUE
  754.  
  755. The routine that does character handling is essentially this:
  756.  
  757.         LHLD    POINTER         ; GET ADDRESS OF NEXT CHARACTER
  758.         MOV     A,M             ; GET NEXT CHARACTER
  759.         INX     H               ; ADVANCE POINTER
  760.         SHLD    POINTER         ; SAVE POINTER
  761.         CPI     CR              ; END OF DATA?
  762.         RNZ                     ; IF NOT THEN JUST RETURN IT
  763.                                 ; ELSE RESTORE CONSOLE
  764.                                 ; INPUT ADDRESSES
  765.  
  766.      This works for two reasons: obviously the BIOS jump table can  be
  767. considered data as well as code  (hail to John Von Neumann) and  since
  768. the original  program will  remain intact  until the  next program  is
  769. actually loaded on top of it, the routine simulating the console  will
  770. function normally.   This  technique  suggests  a  viable  method  for
  771. chaining a series of programs together without having to  specifically
  772. build a submit file for each chain.
  773.  
  774.  
  775.  
  776.         CP/M QUICK REFERENCE; compiled by Steve Stolen
  777. ===========================================================================
  778. I Func. I  Func. I  Function          I Entry Parameter  I Return Value    I 
  779. I Code  I  Code  I        (C)         I (DE)  (E)        I (A)             I 
  780. I (Dec) I  (Hex) I                    I                  I                 I 
  781. ==========================================================================
  782. I    1  I     1  I  Read Console      I       -          I ASCII Char.     I 
  783. ---------------------------------------------------------------------
  784. I    2  I     2  I  Write Console     I  ASCII Char.     I        -        I 
  785. ----------------------------------------------------------------------------
  786. I    3  I     3  I  Read Console      I       -          I ASCII Char.     I 
  787. ----------------------------------------------------------------------------
  788. I    4  I     4  I  Write Punch       I  ASCII Char.     I        -        I 
  789. ----------------------------------------------------------------------------
  790. I    5  I     5  I  Write List        I  ASCII Char.     I        -        I 
  791. ---------------------------------------------------------------------------
  792. I    6  I     6  I       -            I       -          I        -        I 
  793. ----------------------------------------------------------------------------
  794. I    7  I     7  I  Get I/O Status    I       -          I I/O Status Byte I 
  795. ----------------------------------------------------------------------------
  796. I    8  I     8  I  Set I/O Status    I  I/O Status Byte I        -        I 
  797. ----------------------------------------------------------------------------
  798. I    9  I     9  I  Print Buffer      I  Buffer Address  I        -        I 
  799. ----------------------------------------------------------------------------
  800. I   10  I     A  I  Read Buffer       I  Buffer Address  I        -        I 
  801. ----------------------------------------------------------------------------
  802. I   11  I     B  I  Check Console Rdy I       -          I        -        I 
  803. ----------------------------------------------------------------------------
  804. I   12  I     C  I  Lift Head         I       -          I        -        I 
  805. ----------------------------------------------------------------------------
  806. I   13  I     D  I  Initialize BDOS   I       -          I        -        I 
  807. ----------------------------------------------------------------------------
  808. I   14  I     E  I  Log in Drive      I  0...N           I        -        I 
  809. ----------------------------------------------------------------------------
  810. I   15  I     F  I  Open File         I  FCB             I 255=not present I 
  811. ----------------------------------------------------------------------------
  812. I   16  I    10  I  Close File        I  FCB             I 255=not present I 
  813. --------------------------------------------------------------------------
  814. I   17  I    11  I  File Search       I  FCB             I 255=no match    I 
  815. ----------------------------------------------------------------------------
  816. I   18  I    12  I  File Search Next  I  FCB             I Adr.next entry  I 
  817. ----------------------------------------------------------------------------
  818. I   19  I    13  I  Delete File       I  FCB             I       -         I 
  819. ----------------------------------------------------------------------------
  820. I   20  I    14  I  Read Next Record  I  FCB +           I 0=OK 1=EOF 2=ND I 
  821. ----------------------------------------------------------------------------
  822. I   21  I    15  I  Write Next Record I  FCB +           I See Notes       I 
  823. ----------------------------------------------------------------------------
  824. I   22  I    16  I  Make File         I  FCB             I 255=DIR Full    I 
  825. ----------------------------------------------------------------------------
  826. I   25  I    19  I  Drive Number ?    I        -         I  Drive Number   I 
  827. ----------------------------------------------------------------------------
  828. I   26  I    1A  I  Set DMA Address   I  DMA Address     I        -        I 
  829. ----------------------------------------------------------------------------
  830. ============================================================================
  831.  
  832.  
  833.                HOW TO PATCH CP/M TO BACKSPACE ON RUBOUT
  834.                ----------------------------------------
  835.  
  836. The patches listed below will allow  CP/M to echo the delete  function
  837. as a backspace.  Since one of the patches takes advantage of the  jump
  838. relative capability of the Z80, THE PATCHES WILL NOT WORK AS DESCRIBED
  839. ON AN 8080 SYSTEM.  However with the info given it will be simple  for
  840. an 8080 user to make the appropriate patch that will function.
  841.  
  842.       CAUTION: I am  currently using  this patch  and to  date it  has
  843. proven to work well.  It does not work within the 'Insert' mode of the
  844. Editor, but it does work at  command level.  I'll leave it to  someone
  845. else to figure out why.  Anyway use it at your own risk.
  846.  
  847. PATCH ONE: The console  input routine (12F3H) gets  a char and,  after
  848. testing for carriage return, tests for 'delete'.  If the test for  7FH
  849. is successful, CP/M gets the counter from Reg B and tests it for zero.
  850. If true (i.e. we're at start of buffer) a jump is executed back to the
  851. console input routine.
  852.  
  853. If we are not at the start of the buffer, CP/M loads the previous char
  854. (pointed to by HL) into the Acc  and jumps to the CONOUT rout.   Since
  855. we want to echo a backspace instead of the previous char, replace  the
  856. MOV A,M with MVI A,08 (or whichever character it is that your terminal
  857. treats as a backspace).  The extra memory location required by the MVI
  858. instruction is recovered by replacing the JZ XX73 with JR Z,E9.   More
  859. precisely:
  860.  
  861. LOC IN TPA AFTER        ORIGINAL MACHINE CODE      REPLACED BY
  862.      SYSGEN
  863. ----------------        ---------------------      -----------
  864.     1308                    CA XX 73 7E            28 E9 3E 08
  865.  
  866. PATCH TWO: CP/M  will convert any  character less than  20 hex to  its
  867. Ascii equivalent preceeded by  '^'.  All except CR,  LF, and that  is.
  868. To enable our backspace character to be echoed unchanged, we insert  a
  869. patch at the start of the conversion routine to test for '08' and exit
  870. if true.  Again in the TPA:
  871.  
  872. LOC IN TPA AFTER        ORIGINAL MACHINE CODE       REPLACED BY
  873.      SYSGEN
  874. ----------------        ---------------------       -----------
  875.     12B0                     F5 3E 5E               C3 <PATCH>
  876. AND:
  877.   <PATCH, LOC                  N/A                  FE 08 CA 3A
  878.     YOUR CHOICE>                                    XX F5 3E 5E
  879.                                                     C3 XX 33
  880.  
  881. NOTE: The  'XX's  above  must  be replaced  by  the  appropriate  page
  882. boundaries if  your operating  CP/M system.   Also since  we are  only
  883. echoing one backspace, the control characters which CP/M echoed as two
  884. characters will not be completely removed (we leave the '^').  Again I
  885. will leave it to you to come up with a more glamorous fix.
  886.  
  887. ---  EOF ---
  888.