home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / jsage / znode3 / tcj / tcj38bmm.wz / TCJ38BMM.WS
Encoding:
Text File  |  1993-06-07  |  24.8 KB  |  569 lines

  1. TCJ38BMM  2/28/89
  2.  
  3. ART: PLEASE print BOTH titles!
  4.  
  5.                 Advanced CP/M
  6.  
  7.             Batch Processing and a New ZEX
  8.  
  9.                Bridger Mitchell
  10. [usual sidebar on Bridger]
  11.  
  12.  
  13. .h1 More Environmental Programming
  14.  
  15. Two columns ago I went out on a limb and suggested a number of
  16. guidelines for environmentally-sensitive programming -- how to write
  17. programs that were aware of their host computer's environment, took
  18. care to avoid damaging the system, and allowed you to exploit advanced
  19. features if they were supported.  A number of you have continued the
  20. discussion by mail and on the Z-Nodes.  There are several areas for
  21. further fruitful exploration.  I'll touch on one or two this time,
  22. and I imagine we can look forward to further exchanges.
  23.  
  24. Lee Hart asks if there are ways to detect other CPU's (in addition to
  25. the HD64180/Z180 and Z280) that support the Z80 instruction set,
  26. including the Ferranti, SGS and NEC chips.  It would be useful for some
  27. programs to know, for example, that they are running on a pc.  If you
  28. can shed some light on this, please do!
  29.  
  30. .h2 Preserving the Z80 Registers
  31.  
  32. I (and others) have argued that an environmentally-conscious BIOS will
  33. preserve any non-8080 registers that it uses, and restore their values
  34. before returning from any BIOS call.  Al Hawley and other CP/M
  35. veterans recalled that Zilog's early data sheets for the Z80 suggested
  36. using the alternate registers to switch contexts in servicing an
  37. interrupt.
  38.  
  39. In an embedded application, using the alternate registers in a service
  40. routine is entirely appropriate and efficient, because the
  41. designer knows exactly what tasks will use which registers.  But it's
  42. another matter altogether to use the registers (without preserving
  43. them) in an operating system, which is intended to run an _arbitrary_
  44. task that may very well itself actively use the same registers.
  45.  
  46. Unfortunately, several BIOSes were written along the Zilog guidelines,
  47. and some authors used the register-swap instructions to save a few
  48. bytes.  As a result, erratic bugs continue to pop up.  Recently, a few
  49. users have encountered them when installing ZSDOS, which itself
  50. preserves and uses the index registers.
  51.  
  52. Cam Cotrill has come up with a portable "fix" for part of this
  53. defect.  It takes the form of a special NZ-COM BIOS segment that saves
  54. all of the Z80 registers before every BIOS call and then restores them
  55. just before returning.  Because NZ-COM allows the user to load a
  56. customized BIOS module -- in additon to command-processor,
  57. named-directory, and other segments -- and to adjust its size, it is
  58. possible to provide such a band-aid without knowing anything about the
  59. hardware features of the BIOS itself!  You can find this file in
  60. ZSNZBI12.LBR on one of the major Z-Nodes.
  61.  
  62. As ingenious as this solution is, it would be better still if it were
  63. unneeded.  And while it handles the BIOS that consumes registers in
  64. normal services, it cannot rectify the BIOS service routine that
  65. consumes a register for handling interrupts.  If you're writing a new
  66. BIOS, or have the source to your existing system, take care to
  67. preserve the index and alternate registers!
  68.  
  69. .h2 Interrupts
  70.  
  71. How should the environmentally-conscious programmer deal with
  72. interrupts?  First, a portable program can't use Z80 or 8080/8085
  73. interrupts, because it can't readily determine the availability of
  74. interrupt vectors for its own use, and the possible conflicts that
  75. could exist with other interrupts used by the system.  Therefore,
  76. programming interrupt-service routines falls in the province of
  77. writing hardware-specific BIOS extensions.
  78.  
  79. The relevant general-purpose guidelines must be limited to
  80. procedures for disabling and enabling interrupts.  It's rarely
  81. necessary to turn off interrupts, and the rule is: keep it short!
  82. Interrupts must be disabled whenever your code leaves the system in a
  83. state in which an arbitrary interrupt-service routine cannot execute
  84. correctly.  Keep the stack pointer and system addresses clearly in
  85. mind.
  86.  
  87. Why would you ever use SP for anything but a stack, anyway?  Well,
  88. it's sometimes a handy way to load a table of word into registers.
  89. Repeatedly pushing a constant can be the fastest method of
  90. initializing a segment of memory.  And several issues ago I described
  91. a code-relocation algorithm that used a similar trick to fetch,
  92. relocate, and store successive words of code in PRL format.
  93.  
  94. Interrupts should be disabled (with a DI instruction) just before
  95. changing the stack pointer to use it for data operations, and
  96. re-enabled (EI) as the instruction that immediately follows restoring
  97. the stack pointer to a valid stack.  If you don't turn off interrupts,
  98. and an interrupt occurs, then when the cpu catches the interrupt it
  99. will push the current program counter value onto your "stack",
  100. clobbering part of your data area.
  101.  
  102. In some applications it's necessary to change the BIOS or page 0
  103. vectors.  It's remotely possible that an interrupt service routine
  104. would use one of these vectors (but only if the BIOS is re-entrant).
  105. So, a fastidious guideline would use a DI before any multi-instruction
  106. code that changes a vector.
  107.  
  108. This code:
  109.  
  110.     ld (vector_address),de
  111.  
  112. is _atomic_ -- it changes everything necessary in a single executable
  113. instruction, one that cannot itself be interrupted.  However, using
  114. several instructions to storing the low and high bytes of the address,
  115. for example:
  116.  
  117.     ld    hl,vector_address
  118.     di
  119.     ld    (hl),e
  120.     inc    hl
  121.     ld    (hl),d
  122.     ei
  123.  
  124. is not atomic. While that sequence of instructions is executing, the
  125. state of the system BIOS vector is not well defined.  Without the DI
  126. instruction, if an interrupt occurs, its service routine could get an
  127. invalid address.
  128.  
  129. I have used the DI/EI instructions without apparent problems.  But
  130. when I wrote BackGrounder ii I wanted to ensure wide portability, and
  131. I worried about a BIOS that did _not_ use interrupts and might
  132. behave strangely if they were suddenly enabled.  This might seem
  133. paranoid, and it's probably the case that a number of other programs
  134. would not run on such a system.  But I was recalling an early
  135. experience of trying to boot an 8085-based S-100 Compupro system in
  136. which the interrupt lines had been left floating.  When the first EI
  137. was executed in the cold-boot code, one of the devices triggered an
  138. interrupt, before the BIOS's service routine had been installed.
  139.  
  140. The routines in Figure 1 can be called in place of inline DI and EI
  141. instructions to disable interrupts and conditionally re-enable them.
  142. As far as I have been able to determine, this test of the Z80's
  143. interrupt status works correctly.  However, I have heard reports that
  144. some "Z80" cpu's do not report this status correctly.  I would welcome
  145. any reliable information on this point.
  146.  
  147.           Figure 1. Disable and Re-enable Interrupts
  148. ;
  149. ; Save interrupt status and disable interrupts
  150. ;
  151. disable_int:
  152.     push    af        ; save registers
  153.     push    bc
  154.     ld    a,i        ; get interrupt status to A
  155.     push    af
  156.     pop    bc        ; and into C
  157.     ld    a,c        ; and save it
  158.     ld    (intflag),a
  159.     pop    bc
  160.     pop    af
  161.     di            ; disable non-maskable interrupts
  162.     ret
  163. ;
  164. ; If interrupts were previously enabled, 
  165. ; re-enable them.
  166. ;
  167. enable_int:
  168.     push    af        ; save register
  169.     ld    a,(intflag)    ; if interrupts
  170.     bit    2,a        ; .. were previously enabled
  171.     jr    z,1$
  172.     ei            ; ..re-enable them
  173. 1$:    pop    af
  174.     ret
  175.  
  176.     
  177.  
  178. .h1 Batch Processing
  179.  
  180. Batch processing is running a sequence of commands by submitting a
  181. single command to the operating system.  In the good old days, the
  182. computer operator submitted programs, on 80-column punched cards, to a
  183. desk-sized card reader.  Programs were batched together by stacking
  184. the card decks in a long metal tray.  You (or the operator) lugged the
  185. tray across the room, crossing your fingers that you didn't trip and
  186. spill everything on the floor.  Eventually, your job ran and after a
  187. seemingly endless wait, the printer disgorged interminible pages of
  188. digits, and you went back to debugging yet another core dump.  Then
  189. the cycle repeated....
  190.  
  191. CP/M's standard batch processor is the SUBMIT utility.  It takes a
  192. file of command lines, stored in a file of type SUB, and writes them
  193. to a temporary file.  The command processor detects this file and gets
  194. its commands from it, a line at a time, until it has completed the
  195. batch.  Then it once again gets its commands from the keyboard.
  196.  
  197. A submit file, or script, called TEST.SUB might look like this:
  198.  
  199.     cmd1 command_tail1
  200.     cmd2
  201.     cmd3 command_tail3
  202.  
  203. Your command
  204.  
  205.     SUBMIT TEST
  206.  
  207. would then cause the three commands to run in sequence.
  208.  
  209. This basic system works well for programs that require only
  210. command-line parameters for their input.  But when a program, say
  211. CMD1.COM, needs console input, the process stops in its tracks and
  212. waits for the user to type in the input.  Many times this is just what
  213. you want to occur -- the user needs to make a real-time decision, and
  214. enter data or a response.  Often, however, we want the _program input_
  215. to also be automated, so that it can be provided from the same script,
  216. and the entire batch of jobs will run to completition unattended.
  217.  
  218. Digital Research, the authors of CP/M, attempted to provide this
  219. capability with the XSUB utility.  But it was an early attempt to
  220. write an RSX (resident system extension), it was buggy, and it proved
  221. incompatible with any other RSX.
  222.  
  223. A major step forward was the development of utilities that combined
  224. SUBMIT and XSUB processing, kept the script in memory inside the RSX
  225. for faster performance, and supplied a line editor so that short
  226. scripts could be typed in on-the-fly when needed.  EX.COM was one.
  227. Another was the In Memory Submit capability included in Morrow
  228. computers stored the script in banked memory on their CP/M computers.
  229.  
  230. .h2 ZEX
  231.  
  232. For the Z-System the batch processor has been ZEX -- the Z-System
  233. EXecutive input processor.  It evolved from EX, and has grown like
  234. topsy, with significant contributions from Rick Conn, Joe Wright, Jay
  235. Sage and others.  These increasingly elaborate versions provided for
  236. greater control over input, the ability to print messages while the
  237. script was running, simple looping, testing of command flow control,
  238. etc.
  239.  
  240. Yet ZEX never quite seemed housebroken, and the tireless Rick Charnes
  241. was always coming up with some new batch process that he couldn't
  242. quite get ZEX to perform.  Moreover, there was no ZEX for Z3PLUS
  243. systems.  And the hieroglyphics required to write a ZEX script always
  244. required relearning just when you needed a quick, automated process.
  245.  
  246. These warts, and conversations with Joe Wright and Jay Sage, the most
  247. recent revisors of ZEX, finally led me to take a fundamental look at
  248. this utility.  Although the code contained many notable advances, this
  249. was truly a "topsy" program, something that had been bandaged and
  250. remodeled many times.  So, in discussions with Jay, I decided that we
  251. need to rethink our objectives and design the program from the outside
  252. in.  This issue's column focuses on that design, leaving its
  253. implementation for another time.
  254.  
  255. .h2 What Should ZEX Be?
  256.  
  257. The easy part was how it should run.  The new ZEX should run on both
  258. CP/M 2.2 and CP/M Plus systems.  It should be compatible with existing
  259. RSX's.  It should be able to load and use RSX's as part of a script.
  260. And, perhaps, it should be able to invoke a second ZEX script.
  261.  
  262. These requirements would give us a single batch processor for all
  263. Z-Systems, and scripts that could be used on both CP/M 2.2 and CP/M
  264. Plus machines without change.  A script could be executed while an
  265. RSX, such as BackGrounder ii or DosDisk, is already in memory.  If
  266. needed, the script could load an RSX, for example one to filter
  267. printer output.
  268.  
  269. Preliminary goals for the script language seemed straightforward.  The
  270. language should allow a standard SUBMIT script to run identically.  It
  271. should use English-like directives, provide convenient, easily
  272. readable comments, and clearly distinguish between input for the
  273. command processor, input for programs, and messages and directives.
  274. Programs should run identically when the same commands appear in a
  275. script, or are typed in at the console.
  276.  
  277. This is starting to sound like the textbook-prescribed top-down design
  278. exercise.  As any real programmer knows, that would be a fairy tale,
  279. because it seems that all of us just _have to_ write some code, if
  280. only to check out an idea.
  281.  
  282. Well, writing code before the design is completed can indeed be
  283. productive -- the key thing is to avoid getting emeshed in the thicket
  284. of small details before the major skeleton of the project, and
  285. possible alternatives, have been sketched and evaluated.  So, while
  286. drafting and redrafting these preliminary specifications, I also found
  287. myself experimenting experimenting with the parsing code, rewriting,
  288. modularizing and consolidating several existing ZEX versions, and
  289. developing and testing the CP/M Plus interface.
  290.  
  291. What follows, then, is a still-in-process description of the evolving
  292. new ZEX, version 4.0.  Your comments and suggestions will be welcome
  293. and will surely improve it.  I expect ZEX to continue to evolve -- it
  294. will be easier to add features to the code now that it is more
  295. modular.  What will require effort is the systematic thought and
  296. testing of extensions to the language, to avoid unintended side
  297. effects and anomalous cases.
  298.  
  299.  
  300. .h1 The ZEX Script
  301.  
  302. ZEX is the Z-System batch-processing language.  ZEX.COM is the
  303. system tool that implements it.  Its purpose is to automate complex
  304. and repetitive tasks that require running a series of programs or
  305. entering keyboard input.
  306.  
  307. A ZEX _script_ is a text (ascii) file, or series of text lines entered
  308. interactively when ZEX is run.  The script file is conventionally
  309. given the filetype .ZEX, or sometimes .SUB, for convenience in
  310. identifying scripts in a directory.
  311.  
  312. A script typically consists of a series of _commands_ and their
  313. _command-tails_ that form the input to the ZCPR command processor.  In
  314. this form the script is equivalent to a CP/M SUBMIT script.  In
  315. addition, the script may contain _data_ for programs that would
  316. otherwise be entered from the console keyboard.  This feature is
  317. similar to, but more advanced than, the CP/M XSUB.
  318.  
  319. In addition, the ZEX script may contain a number of ZEX _directives_
  320. that provide for console messages, waiting for a keypress, ringing
  321. the bell, testing command flow control, and so forth.
  322.  
  323. ZEX explicitly distinguishes between _command-processor_ input
  324. and _program_ input.  Normally, ZEX gets all command-line input from
  325. the _script_ and all program input from the _console_.  (This is
  326. exactly what SUBMIT does; a SUBMIT script will run identically under
  327. ZEX.)  But the input sources can be switched by directives.  For
  328. example, all program input can also be obtained from the script, so
  329. that the complete script will run unattended from start to finish.
  330.  
  331. In reading this, keep clearly in mind the difference between a script
  332. file, typed input, and console output.  A file is a stream of bytes,
  333. broken into lines by a _pair_ of bytes: <CR> followed by <LF>.
  334. Similarly, when a line of text is output to the screen, it ends with a
  335. <CR> (which moves the cursor to the first column of the current line),
  336. and a <LF> (which moves it down one line).  However, when a line is
  337. entered from the keyboard it is terminated by a <CR> only.  Thus,
  338. in a script you should designate the end of a line of program _input_
  339. with a |CR|.  For a multi-line message to the screen, terminate each
  340. message line with |CRLF|.
  341.  
  342. .h1 The ZEX Language
  343.  
  344. The ZEX script consists of lines of ascii text, each terminated by a
  345. <CR><LF> pair.  (Create the script with a text editor in ascii
  346. (non-document) mode, or just type it into ZEX when prompted.)  A
  347. number of reserved words, called _directives_, control the various
  348. features.  Each directive begins and ends with the verticule character
  349. '|'.  The directives may be entered in upper, lower, or mixed case; we
  350. use uppercase here to make them stand out.  All script input that is
  351. to be sent to a program begins with a '<' character in the first
  352. column; all other lines are sent to the command processor or, when
  353. specifically directed, are messages sent directly to the console
  354. output.
  355.  
  356. .h2 Command-processor input:
  357.  
  358.  - is any line of the script that doesn't begin with '<'
  359.  - is case-independent.
  360.  - spaces and tabs at the beginning of a line are ignored
  361.  - is <CR><LF> sensitive.  The end of a script line is the end of
  362.    one command line.  Use the |JOIN| directive at the end of a script
  363.    line to continue the same command line on a second script line.
  364.    (The <LF> is always discarded).
  365.  - use "|NUL| " or |SPACE| to insert a space preceeding a command, or
  366.    after a command and before a comment.
  367.  - begin each command (or set of multiple commands, separated by
  368.    semicolons) on a new script line, optionally preceeded or followed by
  369.    whitespace.
  370.  - all whitespace immediately preceding a |JOIN|, and all characters
  371.    on the line following |JOIN| are discarded.
  372.  
  373. .h2 Program input:
  374.  
  375.  - is normally obtained from the console.
  376.  - begin each line of program input with a '<' in the first column.
  377.  - input is case-sensitive.
  378.  - data from the script ignores the <CR><LF> at the end of
  379.    a script line.  A single line of program input may spread over
  380.    several script lines.
  381.  - use |CR| to supply a carriage-return.
  382.  - use |LF| for linefeed and |CRLF| for carriage-return-linefeed.
  383.  - if the program requests more input than is supplied in the script,
  384.    the remaining input is obtained from the console
  385.  - use |WATCHFOR string| to take further input from the console, until
  386.    the program sends "string" to the console output, then resume
  387.    input from the script
  388.  
  389.  
  390. .h2 Both:
  391.  
  392.  - use |SAY| to begin text to be printed on the console.
  393.  - use |END SAY| to terminate that text
  394.  - use |UNTIL ~| to take further input from the console,
  395.    until a keyboard ~ is entered.  The '~' character may be any
  396.    character; pick one that won't be needed in entering console input.
  397.  - use |UNTIL| to take further input from the console,
  398.    until a keyboard <CR> is entered.
  399.  
  400. .h2 Comments
  401.  
  402. A double semicolon ";;" designates the beginning of a comment.  The
  403. two semicolons, any immediately-preceding whitespace, and all text up to
  404. the end of that line of script are ignored.
  405.  
  406. A left brace '{ in the first column designates the beginning of a
  407. comment field; all text, on any number of lines, is ignored up to the
  408. first right brace '}'.
  409.  
  410.  
  411. .h2 Other Directives
  412.  
  413. Within a directive, a SPACE character is optional.  Thus, |IF TRUE|
  414. and |IFTRUE| have the identical effect.
  415.  
  416. |IF TRUE|    begin conditional script; do if command flow state is true
  417. |END IF|    end conditional script
  418. |IF FALSE|    begin conditional script; do if command flow state is false
  419. |RING|        ring console bell
  420. |WAIT|        wait until a <CR> is pressed
  421. |AGAIN|        repeat the entire ZEX script
  422. |ABORT|        terminate the script if the flow state is true
  423. |QUIET ON|    turn on the ZCPR quiet flag
  424. |QUIET OFF|    turn off the ZCPR quiet flag
  425. |CCPCMD ON|    turn on ZCPR (CCP) command prompt
  426. |CCPCMD OFF|    turn off ZCPR (CCP) command prompt
  427. |ZEXCMD ON|    turn on ZEX command prompt
  428. |ZEXCMD OFF|    turn off ZEX command prompt
  429. |NUL|        use to make following whitespace significant
  430. ||        same as |NUL|
  431. |SPACE|        one space character
  432.  
  433.  
  434. .h2 Parameters
  435.  
  436. ZEX (like SUBMIT) provides for formal parameters designated $0 $1 ...
  437. $9.  When ZEX is started with a command line such as:
  438.  
  439.     A> ZEX SCRIPT1 ARG1 ARG2 ARG3
  440.  
  441. then ZEX reads and compiles the SCRIPT1.ZEX file.  In the script,
  442. any "$0" will be replaced by "SCRIPT1", any "$1" is replaced by
  443. the "first" argument "ARG1", etc.
  444.  
  445. The script may define "default parameters" for the values $1 ... $9.
  446. To do so, enter the three characters "^$n" followed (with no space) by
  447. the nth default parameter.  When ZEX encounters a formal parameter in
  448. the script, it substitutes the command-line parameter, if there was one
  449. on the command line, and otherwise the corresponding default
  450. parameter, if it was defined.
  451.  
  452. Alternatively, you can define default parameters by entering
  453. "|n=param|", where 'n' is '1' to '9' and "param" is the default string
  454. (containing no whitespace).
  455.  
  456. .h2 Control characters
  457.  
  458. You enter a control character into the script by entering a caret '^'
  459. followed by the control-character letter/symbol.  For example, "^A"
  460. will enter a Control-A (01 hex).  Control-characters may be entered in
  461. upper or lower case.
  462.  
  463. .h2 Quotation
  464.  
  465. ZEX uses a number of characters in special ways: dollar-sign, caret,
  466. verticule, left and right curley braces, less-than sign, semicolon,
  467. (space, and carriage-return).  Sometimes we might want to include
  468. these characters as ordinary input, or as output in a screen message.
  469. For this, ZEX uses '$' as the _quotation character_.  (This is also
  470. called the _escape_ character, because it allows one to escape from
  471. the meaning reserved for a special character.)  "Quotation" means that
  472. the next character is to be taken literally; I use this term to avoid
  473. confusion with the control code 1B hex generated by the _escape key_.
  474.  
  475. If '$' is followed by any character other than the digits from '0' to
  476. '9', that character is taken literally.  Thus, if we want a caret in
  477. the text and not a control character, we use '$^'.  If we want a '<'
  478. in the first column of a line that is for the command processor and
  479. not for program input, then we use '$<' there instead.  And don't
  480. forget that if we want a '$' in our script, we must use '$$'.  There
  481. are some cases, like '$a', where the '$' is not necessary, but it can
  482. always be used.
  483.  
  484. To pass a ZEX directive to a program, or the command processor, use
  485. the quotation character with the verticule.  For example, to echo the
  486. string "|RING|", the zex script should be:
  487.  
  488.         echo $|RING$|
  489.  
  490. .h2 Some examples
  491.  
  492. Figure 2 provides several examples of how the new script language
  493. should work.  You will note a number of differences from the current
  494. dialect used, for example, in Rick Charnes' article in this issue.
  495. And, no doubt, further improvements will emerge from your suggestions
  496. and the actual implementation of the new batch processor.
  497.  
  498.  
  499.             Figure 2.  ZEX Script Examples
  500.  
  501.  
  502. ZEX SCRIPT        INPUT SOURCE/EXECUTION SEQUENCE
  503.  
  504. cmd1    ;;comment    The CCP receives "cmd1<cr>".  The spaces before
  505.                 the comment are stripped, and the <cr> at the
  506.             end of the line is passed to the CCP.
  507.             The cmd1 program gets its input from the console.
  508.  
  509. cmd2 |UNTIL|        The CCP receives "cmd2 " and then gets additional
  510.             input from the console, including a <cr>.
  511.             The cmd2 program gets its input from the console.
  512. |SAY|ccp msg|ENDSAY|cmd3
  513.             When the CCP prompts for the next command,
  514.             "ccp msg" is printed on the console.  The CCP
  515.             then receives "cmd3<cr>"
  516. <text            The cmd3 program gets "textmore text<cr>new
  517. <more text|CR|        line of text"
  518. <new line of text    If the program requests more input, it comes from
  519.             the console.
  520.  
  521. cmd4 cmd4tail        The CCP receives "cmd4 cmd4tail<cr>"
  522. <|UNTIL~|        The cmd4 program receives console input until
  523. <text            the user types a '~'.  Then the program receives
  524.             "text"
  525.             If the program requests more input, it comes
  526.             from the console.  If the program doesn't use all
  527.             of the input, it is discarded.
  528.  
  529. cmd5 |UNTIL~| tail    The CCP receives "cmd5 " and then gets additional
  530.             input from the console, until the user types '~'.
  531.             The CCP then receives " tail<cr>".
  532.             The program receives input from the console.
  533.  
  534. |UNTIL|            The CCP receives a command line of input from the console.
  535.             The program receives input from the console.
  536.  
  537. |UNTIL|            The CCP receives a command line of input from the console.
  538. <|SAY|message|ENDSAY|    When the program first calls for console input,
  539. <text            "message" is printed on the console.  Then the
  540.             program receives "text".
  541.             Additional program input is received from the console.
  542.  
  543.  
  544. cmd6            The CCP gets "cmd6<cr>"
  545. <|WATCHFORstring|    The cmd6 program gets input from the console, until
  546. <|SAY|message|ENDSAY|    the characters "string" appear at the console output.
  547. <text            Then "message" appears on the console output, and
  548.             the program gets "text".  Further input comes
  549.             from the console.
  550.             If "string" never appears, all of this is
  551.             discarded.
  552.  
  553. alias1            The CCP gets "alias1<cr>".  That program, a Z-System
  554.             alias, puts "cmd1;cmd2" into the multiple
  555.             command line buffer.  The CCP then obtains "cmd1" from mcl
  556. <|UNTIL~|        The cmd1 program gets any input from the
  557. <cmd2text        console.  If a '~' is typed, it gets "cmd2text".
  558.             If cmd1 does not request console input, or if
  559.             no '~' is typed, cmd1 finishes and the CCP then
  560.             obtains "cmd2" from mcl.  Assume this case.
  561.             The cmd2 program obtains input from the
  562.             console, until a '~' is typed.  Then it gets
  563.             "cmd2text".  Further input comes from the console
  564. cmd3            The CCP gets "cmd3<cr>".
  565. <text            The cmd3 program gets "text".  Further input
  566.             comes from the console.
  567.  
  568.  
  569.