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

  1. THE  FOLLOWING  IS  A TRANSCRIPT OF AN  "APPLICATION  NOTE"  FROM 
  2. DIGITAL  RESEARCH,  THOUGH THE ORIGINAL BEARS NO NOTICE OF SOURCE 
  3. OR COPYRIGHT:
  4.  
  5.  
  6.                  CHAINING PROGRAMS UNDER CPM 2.2
  7.                                by
  8.                            DOUG HUSKEY
  9.  
  10.  
  11. I  have  often been asked how to write menu  driven  applications 
  12. which  will  run  under CP/M.   If  the  applications  are  being 
  13. developed using PL/I-80,  this can be accomplished by writing the 
  14. programs  as  a set of overlays.  Often,  however,  some  of  the 
  15. programs may be written in assembly language, or require too much 
  16. memory  to  make  the  use  of the  overlay  feature  of  PL/I-80 
  17. appropriate.    Without  using  overlays,   there  are  only  two 
  18. effective ways of chaining under CP/M 2.2.   First,  you can  use 
  19. the  CP/M  submit facility.   The trick is to have the main  menu 
  20. program  create  a submit file with the programs  to  be  chained 
  21. listed in it.  The file must be written to drive A,  and have the 
  22. name "$$$.SUB".
  23.  
  24. The  submit file  consists of command lines exactly  as  would be
  25. typed at the console following the system prompt.   The  commands 
  26. are  placed in reverse order so that the last command in the file 
  27. is  the first to be executed.   Each command is placed in  a  128 
  28. byte record with the following format:
  29.  
  30.               |---|----|----|-----|----|---|-----|
  31.               | n | c1 | c2 | ... | cn | 0 | ... |
  32.               |---|----|----|-----|----|---|-----|
  33.  
  34. The first byte of the record contains the number of characters in 
  35. the  command  (n),   followed  by  the  characters  (c1-cn),  and 
  36. terminated with a zero.   The number of characters in the command 
  37. is  written as a binary number and each character is  represented 
  38. in its normal ASCII format.   It does not matter what follows the 
  39. terminating zero in the record.   For example, if the command was 
  40. "STAT *.*",  the first byte would be a binary 8,  followed by the 
  41. letters "STAT *.*" and terminated with a zero.
  42.  
  43. The  second  approach to program chaining is  simpler.   In  this 
  44. approach,  you  simply  include a procedure in the  menu  program 
  45. which  will load the next program and chain to it.   Each program 
  46. that  might chain to another program must include a copy  of  the 
  47. procedure.   The trick here is that the procedure must first move 
  48. itself  out  of  the  way so that it is not  overwritten  by  the 
  49. program it is loading.
  50.  
  51. The  assembly language program listed at the end of this  article 
  52. accomplishes  this.   It  was written to be linked  with  PL/I-80 
  53. modules as an external procedure.   Of course,  it could also  be 
  54. used  in an assembly language menu program.   If you wish to link 
  55. it  to a PL/I-80 program the following entry declaration must  be 
  56. included in the PL/I-80 program doing the chaining:
  57.  
  58.           dcl chain entry (char(12));
  59.  
  60. The  character  12 variable consists of the  standard  CP/M  file 
  61. control  block (FCB) format.   This can be created in the PL/I-80 
  62. program as a structure.  A char(12) variable can then be based at 
  63. the same address as the structure for the purpose of  interfacing 
  64. to  the chain procedure.   The PL/I-80 program below  illustrates 
  65. this.  Note that the drive is not an ASCII character but a binary 
  66. number between 0 and 16, where 0 is the current default drive and 
  67. 1 through 16 represent the CP/M drives A through P, respectively.
  68.  
  69.  
  70. chainl: proc options(main);   /* chain subroutine tester */
  71.  
  72.      dcl 1 fcb static,
  73.                2 drive fixed(7) init(0),
  74.                2 name char(8) init('CHAIN2'),
  75.                2 type char(3) init('COM'),
  76.  
  77.           dummy char(12) based(dp),
  78.           dp pointer,
  79.      chain entry(char(12));
  80.  
  81.      put skip list ('Chain Test program 1');
  82.      dp = addr(fcb);
  83.      call chain(dummy);
  84.      put skip(2) list('Shouldn''t be here!!');
  85. end chainl;
  86.  
  87. This  program will print the message "Chain Test program 1",  and 
  88. chain to the program CHAIN2.COM on the default drive.   CHAIN2 is 
  89. a  program identical to CHAIN1 except that it prints "Chain  Test 
  90. program  2"  and chains to CHAIN1.COM.   Thus chain1  and  chain2 
  91. continue to chain back and forth to each other,  not real  useful 
  92. but  an  interesting  demonstration.   Note that  any  statements 
  93. following the call to the chain procedure will never be  executed 
  94. as the chain procedure never returns, it chains.
  95.  
  96. The  chain procedure consists of two routines,  an initialization 
  97. routine  and  the loader  routine.   The  initialization  routine 
  98. initializes  the  FCB  for  the program to  be  loaded  and  then 
  99. relocates  the  loader and FCB to the very top of  the  transient 
  100. program area (TPA),  immediately below the BDOS, so that it won't 
  101. be  overwritten by the loaded program.   The loader begins at the 
  102. label  "code:"  and ends at the end of the FCB at  the  statement 
  103. "codelen equ $-code".
  104.  
  105. The initialization portion first copies the drive,  file name and 
  106. type  into the FCB using the "move" routine.   It then fills  the 
  107. rest  of the FCB with zeros using the "fill" routine.   Now comes 
  108. the tricky part.  It picks up the BDOS base address from the jump 
  109. at  location 5 in low memory.   Next the routine  subtracts  from 
  110. this  the length of the loader and fcb and fills in the jump back 
  111. to "code:" in the loader.   It calculates  the address of the FCB 
  112. after  it is moved and fills in the lxi instruction at the  label 
  113. "fcbr:".   Then  another call to the move routine moves the  code 
  114. and  fcb into the proper location below the BDOS.   It opens  the 
  115. file  to be loaded,  tests the A register to see it the open  was 
  116. successful,  and  signals an error ("Bad Chain Attempt")  if  the 
  117. file was not found.  Finally, it pops the address of the start of  
  118. the  loader routine,  sets the stack to grow down from below  the 
  119. loader  and pushes the address back onto the stack in preparation 
  120. for  a  return.   Only one thing left to do,  initialize  the  HL 
  121. register to the beginning of the TPA at 100H,  where the  program 
  122. will be loaded.  The return fires off the loader.
  123.  
  124. The  loader routine simply sets the DMA address,  reads a sector, 
  125. checks  for  an end of file,  increments the DMA address  by  128 
  126. bytes,  and  repeats  the  process.   When the  end  of  file  is 
  127. detected, it jumps to the chained program.
  128.  
  129. This  routine provides an effective and relatively simple  method 
  130. of chaining programs under CP/M, MP/M II and CP-NET.  In addition 
  131. to being compatible with all of these systems,  it is also faster 
  132. than  the  submit file method described at the beginning of  this 
  133. article.
  134.  
  135.           public chain ; (char(12))          
  136.           extrn    ?signal
  137.           ; /* loads another COM file and executes it */
  138. bdos      equ      5
  139. openf     equ      15
  140. readf     equ      20
  141. dmaf      equ      26
  142.  
  143.           cseg
  144. chain:    move e,m ! inx h ! mov d,m ! xchg  ;get first arg address
  145.           lxi d,fcb ! mvi c,12 ! call move   ;move string to fcb
  146.           lxi d,fcb+12 ! mvi a,0 ! mvi c,21 ! call fill
  147.                                              ;zero rest of fcb
  148.           lhld bdos+1 ! lxi b,-code$len ! dad b
  149.                                              ;make space at top of TPA
  150.           shld jmpr+1                        ;jump address
  151.           push h                             ;save code address for RET
  152.           xchg ! lxi h,fcb-code ! dad d      ;make address of FCB
  153.           shld fcbr+1                        ;and fix LXI
  154.           push h                             ;save FCB destination add.
  155.           lxi h,code ! mvi c,code$len ! call move
  156.                                              ;destination in DE
  157.           pop d                              ;recover FCB address
  158.           mvi c,openf ! call bdos            ;open file
  159.           inr a ! jz sig                     ;signal if error
  160.           pop h ! sphl ! push h              ;point stack to top of
  161.                                              ;TPA and save address
  162.           lxi h,100h                         ;point to start of TPA
  163.           ret
  164.  
  165. code:     push h ! xchg ! mvi c,dmaf ! call bdos
  166.                                              ;set DMA address
  167. fcbr:     lxi d,$-$ ! mvi c,readf ! call bdos
  168.                                              ;read next record
  169.           ora a ! jnz 100h                   ;EOF -> start TPA
  170.           pop h ! lxi d,128 ! dad d          ;recover and bump DMA
  171.                                              ;address
  172. jmpr:     jmp $-$                            ;jump to code
  173.  
  174. fcb:      ds       1                         ;drive code
  175.           ds       8                         ;file name
  176.           ds       3                         ;file type
  177.           ds       4                         ;control info
  178.           ds       16                        ;disk map
  179.           ds       1                         ;rrec
  180. codelen   equ      $-code
  181.  
  182. move:     ; c = # bytes, hl = source, de = destination
  183.           mov a,m ! stax d
  184.           inx  h ! inx d ! dcr c
  185.           jnz move
  186.           ret
  187.  
  188. fill:     ; a =byte to fill, c = # bytes, de = start address
  189.           stax d ! inx d
  190.           dcr c ! jnz fill 
  191.           ret
  192.  
  193. sig:      lxi h,siglist ! call ?signal ! jmp 0
  194.                                              ;signal error
  195. siglist:  dw       sigcode,sigsub,sigfil,message
  196.                                              ;(fixed(6),bit(8),ptr,p
  197. sigcode   db       6                         ;undefined file error
  198. sigsub    db       2                         ;arbitrary subcode
  199. sigfil    dw       fpb                       ;ptr to file parameter
  200. message   dw       quack                     ;auxiliary oper. msg.
  201.  
  202. fpb:                                         ;PL/I file parameter blk
  203. fcbptr    dw       fcb-1                     ;.fcb-1
  204. fpblst    dw       0                         ;(unused)ptr
  205. column    dw       0                         ;current col fixed (15)
  206. curline   dw       0                         ;current line  "
  207. curpage   dw       0                         ;current page  "
  208. currec    dw       0                         ;(unused) 
  209. lookchr   db       0                         ;lookahead char (1)
  210. ioend     dw       0                         ;i/o end address
  211. iostk     dw       0                         ;user stack upon sio entry
  212. spacer    ds       4                         ;spacer
  213. linesz    dw       0                         ;line size fixed (15)
  214. pagesz    dw       0                         ;page size   "
  215. fixedsz   dw       0                         ;fixed size  "
  216. blocksz   dw       0                         ;block size  "
  217. filedes   dw       0                         ;file descriptor
  218. dtitle    db       0,''                      ;default title
  219.                                              ;  char(14) varying
  220. quack     db       17,'Bad Chain Attempt',0  ;error message
  221.