home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / jsage / znode3 / tcj / tcj54.ws < prev    next >
Encoding:
Text File  |  1994-09-02  |  26.0 KB  |  557 lines

  1.  
  2.                 Z-System Corner -- Issue 54
  3.  
  4.                  Tenth Anniversary of ZCPR
  5.  
  6.                           Jay Sage
  7.  
  8.  
  9.    On February 2, 1992, just about the time you are reading
  10. this column, it will be exactly ten years since the first
  11. version of ZCPR was released.  I have been involved with it
  12. one way or another most of that time, and I think it is
  13. amazing how vibrant the activity in the field still is.
  14.  
  15.    Our editor, Chris McEwen, had hoped that we could make
  16. this issue a special celebration of ZCPR with contributions
  17. from some of the original developers, most notably Richard
  18. Conn.  I exchanged email with Richard several times about
  19. this, but he never picked up on it.  Since of all those
  20. still active in the Z community I may be the one who goes
  21. back the farthest, it is perhaps fitting that I take on the
  22. task.
  23.  
  24.  
  25. Announcement
  26.  
  27.    Before I start on that, I do have one important
  28. announcement to make.  I would like to call your attention
  29. to the new Sage Microsystems East ad in this issue.  You
  30. will notice that there have been quite a number of
  31. significant price reductions.  We hope that by lowering the
  32. entry price to Z-System from $70 to $49 we will encourage
  33. more people who still do not use Z-System to try it out.
  34.  
  35.  
  36. The History of ZCPR
  37.  
  38.    Much of the material in this column comes from the
  39. introductory chapter of my book, the "ZCPR33 User Guide." 
  40. When Echelon made my revision of ZCPR3 the official product
  41. release in 1987, naturally they wanted a manual to go with
  42. it.  Besides including all the necessary technical
  43. information, such as what the new command processor did and
  44. how it should be installed, I also included two other items
  45. that were of great importance to me: a statement of what I
  46. was trying to achieve with ZCPR33 and the history that led
  47. up to it.  Here in TCJ I often talk about the goals of Z-
  48. System; this time I will review some of the history.
  49.  
  50.  
  51.                            ZCPR1
  52.  
  53.    "Don't you know about ZCPR!"  I remember very well being
  54. greeted with that exclamation from one of the veteran club
  55. members when, as a neophyte computer user, I attended a CP/M
  56. computer club meeting.  He could not believe that someone
  57. would still be using standard CP/M.  I soon felt the same
  58. way and still do today!
  59.  
  60.    The ZCPR he was referring to was what we would now call
  61. ZCPR1.  ZCPR, which stood for "Z80 Command Processor
  62. Replacement," was the work of a group of computer hobbyists
  63. who called themselves "The CCP Group."  They were Frank
  64. Wancho, Keith Petersen, Ron Fowler, Charlie Strom, Bob
  65. Mathias, and Richard Conn.  Richard, as we will see, was the
  66. main force behind the effort.
  67.  
  68.    Ron Fowler is well known as the author of the MEX
  69. telecommunications program, which I still use and enjoy
  70. immensely.  Keith Petersen wrote a simplified version of
  71. Ward Christensen's CBBS, the original computerized bulletin
  72. board system.  Keith's program was called MINICBBS; my own
  73. customized version of it runs to this day on my Z-Node.  It
  74. is, to be sure, outmoded, but it gives me a sense of
  75. connection with history that I still treasure.
  76.  
  77.    Keith Petersen was for a long time a sysop of one of the
  78. finest BBS systems in the country, Royal Oak.  Though it
  79. branched out to MS-DOS software, it never neglected CP/M. 
  80. It was, perhaps, unique in that callers to Royal Oak found
  81. themselves immediately at the operating system prompt. 
  82. There was no request for a name or password.  If you wanted
  83. to use MINICBBS, you had to invoke it yourself.
  84.  
  85.    Frank Wancho is involved in the administration of the
  86. SIMTEL20 computer at the White Sands Missile Range.  This
  87. machine houses a huge archive of CP/M programs (and many
  88. others).  Keith is on contract to SIMTEL20 to help maintain
  89. the collections.  I continue to see both their names. 
  90. Frank, via email, gave me some of the information on the
  91. birth of ZCPR.
  92.  
  93.    Sometime around 1981 Richard Conn sparked the group's
  94. enthusiasm over rewriting the CP/M console command
  95. processor, or CCP, to take advantage of the more efficient
  96. and elegant opcodes of the new Zilog Z80 microprocessor. 
  97. The people in the CCP Group were not in physical proximity. 
  98. I believe that they maintained contact, as we do today, via
  99. electronic mail.  Frank Wancho provided the computer access
  100. that made that contact possible.
  101.  
  102.    With some space opened up in the CCP, the programmers
  103. were able to add a number of convenient new features.  The
  104. most important new concept was that of a search path for COM
  105. files.  With CP/M version 2, Digital Research had introduced
  106. user numbers, but the way they were implemented made them
  107. virtually worthless, because there was no way from one user
  108. area to run or access files in another user area.  ZCPR,
  109. with its ability to automatically search drive A/user 0,
  110. overcame this problem and opened up the possibility of
  111. putting the new user areas to effective use.
  112.  
  113.    Also introduced with ZCPR was the GO command, which
  114. permitted the most recently executed transient program to be
  115. run again without having to reload it from disk.  That was a
  116. real a boon in those days of slow floppy drives.  Many small
  117. -- but very useful and helpful -- improvements were made in
  118. the resident commands.  For example, in CP/M, when a REN or
  119. SAVE command specified a destination file that already
  120. existed, the command would simply abort.  The user would
  121. then have to erase the old file manually and start over
  122. again.  With ZCPR, the REN and SAVE commands made life
  123. easier by asking the user if the old file should be
  124. overwritten.
  125.  
  126.    The original ZCPR was released to the public on a disk
  127. published by SIG/M (Special Interest Group/Microcomputers),
  128. the public-domain software distribution arm of the Amateur
  129. Computer Club of New Jersey.  The disk was volume 54, dated
  130. February 2, 1982.  Interestingly enough, this is volume 54
  131. of The Computer Journal!
  132.  
  133.    Several additional refinements were made to ZCPR by other
  134. programmers, leading to a train of development known as
  135. NZCPR (New ZCPR).  Version 1.6 of NZCPR was released on
  136. SIG/M volume 77 at the end of October, 1982.  This branch
  137. eventually reached version NZCPR21, a version never
  138. published in disk form but distributed over the remote
  139. access computer system network.
  140.  
  141.    Jim Byram, of the Boston Computer Society CP/M Group,
  142. produced a privately distributed version of NZCPR using only
  143. Intel 8080 code, which showed that efficient coding, and not
  144. simply the use of the new Z80 opcodes, was a major factor in
  145. improving the command processor.  Jim, by the way, may be
  146. the one who made the remark to me that I quoted earlier.  I
  147. eventually became the leader of that group, which merged
  148. with several others and ultimately became the Zi/Tel Group,
  149. of which I am now the CP/M director and the bulletin board
  150. sysop.  That group supports CP/M, Z-System, and MS-DOS.
  151.  
  152.  
  153.                            ZCPR2
  154.  
  155.    While ZCPR1 was a significant improvement over CP/M, it
  156. was not a revolutionary advance.  Richard Conn, however, had
  157. a vision of a truly advanced operating system, and he
  158. continued the development.  On February 14, 1983, almost
  159. exactly one year after ZCPR1 appeared, ZCPR2 was released in
  160. a set of ten SIG/M volumes (98-107), an unprecedented and
  161. monumental contribution of public-domain software.
  162.  
  163.    ZCPR2 made a very significant conceptual advance: it used
  164. memory buffers in protected memory above the BIOS to hold
  165. new operating system modules.  The command line, which had
  166. always resided in the command processor, was put in one of
  167. these buffers so that it would not be destroyed by warm
  168. boots, during which a fresh copy of the command processor is
  169. loaded from disk.  In that way multiple commands on a line
  170. could be implemented.
  171.  
  172.    The command search path was also placed in one of these
  173. buffers instead of hard-coding it into the command
  174. processor.  In this way the search path could be changed by
  175. the user at any time.  The concept of named directories was
  176. also introduced, using still another memory buffer to store
  177. the index of names.
  178.  
  179.    Many of the utilities that we are familiar with in ZCPR3
  180. first appeared with ZCPR2.  These include ZEX, WHEEL, HELP,
  181. PATH, PWD, MKDIR, and MENU.  A rudimentary shell concept was
  182. used in MENU.  When this program placed a command into the
  183. multiple command line buffer, it would always add its own
  184. name at the end of the command sequence so that control
  185. would eventually return to MENU.  This worked fine for
  186. single levels of shells.  Extended command processing was
  187. also introduced with ZCPR2.
  188.  
  189.    The ZCPR2 documentation, alone, ran to more than half a
  190. megabyte!  It included a concepts manual, an installation
  191. manual, a users guide, and a rationale manual (I guess Rick
  192. felt he had to prove he wasn't crazy in doing all this
  193. wonderful stuff).
  194.  
  195.    Shortly after the initial ZCPR2 SIG/M release, an upgrade
  196. to version 2.3 was published in volume 108.  Up to this
  197. point ZCPR2 still followed in the tradition of ZCPR1 and
  198. used Zilog opcodes.  The features of ZCPR2 were now so
  199. exciting, however, that owners of computers based on Intel's
  200. 8080 and 8085 microprocessors wanted to have them, too. 
  201. Charlie Strom, a member of the original CCP Group and well-
  202. known later as the sysop of the Compuserve CP/M Special
  203. Interest Group, converted the command processor code and
  204. some of the key utilities to Intel-compatible code and
  205. released the result in SIG/M volume 122.  At the time,
  206. believe it or not, I was using at work an Intel MDS-800
  207. microprocessor development system, the computer for which
  208. Gary Kildall, then at Intel, invented CP/M, and I remember
  209. very well bringing up this 8080 version of ZCPR2.  It was
  210. marvelous!
  211.  
  212.  
  213.                            ZCPR3
  214.  
  215.    But ZCPR2 was by no means the end of the evolution.  On
  216. Bastille day, July 14, 1984, not quite a year and a half
  217. after ZCPR2, Richard Conn offered ZCPR version 3 in the form
  218. of another nine volumes of SIG/M disks (184 to 192).  At
  219. this point more than 10% of all the software ever released
  220. by SIG/M had come from one contributor -- Richard Conn!
  221.  
  222.    One time when I was talking with Richard, I must have
  223. expressed my amazement at the incredible amount of software
  224. he had written and released to the public.  I was equally
  225. impressed by Richard's response.  He said that the code that
  226. others had offered to the public had taught him and helped
  227. him so much that he felt a tremendous obligation to
  228. contribute what he could to the community.  He certainly did
  229. that!  And that same spirit still pervades the 8-bit
  230. community.
  231.  
  232.    ZCPR3 brought both significant new concepts and major
  233. refinements.  Three of the innovations were flow control,
  234. error handling, and the message buffer.
  235.  
  236.    Flow control made it possible to achieve a vastly higher
  237. degree of automated operation, since the command processor
  238. was no longer dependent on the user for all command
  239. decisions but could now make wide-ranging decisions on its
  240. own.  The message buffer made possible communication between
  241. the command processor and programs and between successively
  242. run programs.
  243.  
  244.    Error handlers made it possible for improperly entered
  245. commands to be corrected, an important facility to have in
  246. connection with multiple commands on a line.  Having to
  247. retype a single command after a mistake had been bad enough;
  248. having to retype a whole, long string of commands because of
  249. a single mistake seriously discouraged one from making use
  250. of the multiple command facility.
  251.  
  252.    ZCPR3, by the way, unlike it predecessors, was written so
  253. that it could be assembled to either Intel or Zilog opcodes. 
  254. In the former case, the code was considerably longer and
  255. fewer features could be included, but it would work on an
  256. 8080 or 8085 computer.
  257.  
  258.  
  259.                            ZCPR31
  260.  
  261.    The chain of refinements to ZCPR3 that led to version 3.3
  262. started in March, 1985, when I produced a private,
  263. experimental version of ZCPR3 called ZCPR31 for use on my Z-
  264. Node.  It was modified so that the command processor would
  265. get the values for maximum drive and user from the
  266. environment descriptor (more on this later).
  267.  
  268.    This was my first close look at operating system code,
  269. something that had always frightened me, as I am sure it has
  270. many others.  There is a mystique about those words,
  271. "operating system," that makes one think that only the most
  272. advanced programmers could possibly understand the code.  In
  273. fact, I discovered that the code did not look much different
  274. from that in ordinary utility programs.  To my amazement, I
  275. was able to make changes that worked and improved the CCP. 
  276. The most significant advances occurred in August, 1985, when
  277. three further major enhancements were introduced.
  278.  
  279.    First, the code was changed to prevent the infinite loop
  280. that Z30 experienced when the specified error handler could
  281. not be found (perhaps because the path was changed or the
  282. error handler renamed).  In that situation, a command error
  283. would invoke the error handler.  When the error handler
  284. could not be found, that constituted another error that
  285. caused the error handler to be invoked, and so on until one
  286. pressed the reset button or turned off the power.
  287.  
  288.    Second, the code was modified so that it could determine
  289. the addresses of the RCP, FCP, and NDR modules from the
  290. environment and respond to dynamic changes in these
  291. addresses.
  292.  
  293.    Finally, additions were made to the code that allowed an
  294. extended command processor to return control to the command
  295. processor if it also could not resolve the command.  The
  296. command processor would then invoke the error handler.  Now
  297. the extended command processor really was a full-fledged
  298. extension of the CCP, and a ZCPR3 system could take
  299. advantage of both extended command processing and error
  300. handling.  The same mechanism also made it possible for
  301. ordinary programs to initiate error handling.
  302.  
  303.    In January, 1986, the first steps were taken to fix
  304. serious bugs in the way the minimum path and root path were
  305. computed.  The fix, however, had errors of its own, and it
  306. was not until June, 1986, that Howard Goldstein finally
  307. implemented a complete and proper solution.
  308.  
  309.    The next major set of advances came in March, 1986, when
  310. Al Hawley, sysop of Z-Node #2 and now a familiar TCJ author,
  311. introduced several new concepts.  One was a new way to
  312. implement wheel-protected commands (commands that can be
  313. executed only by specially authorized users).  In Z30 wheel
  314. protection had to be hard coded into the command processor
  315. (and RCP), and when one of the restricted commands was
  316. invoked with the wheel off, an error message resulted.  Al
  317. introduced the idea of setting the high bit of the first
  318. character of a command to signal that the command was off-
  319. limits to non-wheel users.
  320.  
  321.    This concept had several important advantages.  First,
  322. the code was shorter.  Second, the new code automatically
  323. made the same technique apply to commands in other modules
  324. (RCP and FCP), so that wheel-checking code could be
  325. eliminated from those modules.  Third, when the wheel byte
  326. was off, wheel-protected commands instead of displaying an
  327. error message simply vanished as far as the command
  328. processor was concerned.  In this way, transient programs or
  329. aliases with the same name as the resident command could
  330. automatically step in and provide whatever action the system
  331. implementer desired.
  332.  
  333.    Al Hawley also introduced two concepts that made dealing
  334. with secure systems easier.  He made it possible for the
  335. command processor to determine dynamically whether or not to
  336. recognize the DU form of directory reference in response to
  337. the setting of the DUOK flag in the environment, and he
  338. allowed the option of bypassing password checking when the
  339. wheel byte was set.  These features made it possible for a
  340. sysop or system implementer to live comfortably with a
  341. secure system (though they did not make life any easier for
  342. the restricted user).
  343.  
  344.    The last major advance that occurred in the development
  345. of ZCPR31 resulted from a conversation I had with Bruce
  346. Morgen in July, 1986.  We were discussing the annoying way
  347. that ZEX functioned under shells, with the shell program
  348. being reloaded for each command line, only to realize that
  349. ZEX was running.  It would then feed the next command line
  350. from ZEX to the multiple command line buffer.  I conceived a
  351. small change in the code that made this problem vanish in a
  352. flash.
  353.  
  354.  
  355.                            ZCPR33
  356.  
  357.    At the very end of January, 1987, I got a call from
  358. Echelon.  Richard Conn had decided to discontinue his
  359. involvement with ZCPR3, and Echelon asked if I would be
  360. willing to write the official ZCPR version 3.3 release based
  361. on the experimental ZCPR31.  I agreed.  During the months of
  362. February, March, and April of 1987 an enormous amount of
  363. additional development took place, the results of which are
  364. described in detail in the "ZCPR33 User Guide."  Only some
  365. key concepts will be mentioned here.
  366.  
  367.    Once again, the decision was made no longer to make any
  368. attempt to support 8080/8085 computers.  The code was
  369. written using Zilog mnemonics, and extensive use was made of
  370. Z80-specific instructions, including relative jumps, block
  371. moves, block searches, direct word transfers to register
  372. pairs other than HL, 16-bit subtractions, and the alternate
  373. register set.  This approach has continued to the current
  374. ZCPR34.  To my knowledge, no one has even tried to make an
  375. 8080 version; there just are not many of those machines
  376. still in operation.
  377.  
  378.    One of the nicest features introduced with ZCPR33 was the
  379. automatic installation of programs.  Until this point,
  380. before a ZCPR-aware program could be used, it had to be "installed" for the specific system configuration.  If one
  381. forgot to do this, the program would likely behave in
  382. bizarre ways, and this was a very common source of
  383. difficulty for new and experienced users alike.
  384.  
  385.    In ZCPR2 installation was a very elaborate procedure in
  386. which a large block of code had to be patched using the
  387. special GENINS utility.  With ZCPR3 the information about
  388. the system configuration was placed in a memory buffer
  389. (called the environment or ENV) where all programs could
  390. access it.  More importantly, the system configuration could
  391. be changed without reinstalling all the programs.  Now
  392. installation amounted only to patching the ENV address into
  393. the program.
  394.  
  395.    As soon as I heard that Richard Conn had figured out a
  396. way to eliminate this annoying installation step, the
  397. solution became obvious to me as well.  Since the command
  398. processor already loads a program from disk, and since it
  399. already knows the ENV address, why couldn't it install the
  400. address directly into the memory image of the program? 
  401. That's just what it does.
  402.  
  403.    One truly revolutionary concept was introduced with
  404. ZCPR33.  Until that time, all CP/M transient programs were
  405. loaded to and ran at a standard address, 100H.  With CP/M
  406. there was no reason to do otherwise, but with ZCPR3 there
  407. was.  From the time of ZCPR1, I had become quite accustomed
  408. to using the GO command to rerun the previous program.  To
  409. my puzzlement, GO sometimes produced bizarre results under
  410. ZCPR3.
  411.  
  412.    Under CP/M, programs get loaded only when the user
  413. instructs the system to run them.  Under ZCPR3, however,
  414. there are quite a few programs that are loaded and executed
  415. automatically by the command processor.  These include
  416. extended command processors, error handlers, shells, and
  417. transient (COM) versions of otherwise resident commands,
  418. such as ERA or REN.  Sometimes, using the GO command
  419. resulted in rerunning these programs instead of the last
  420. program the user specified.
  421.  
  422.    One day as I was working on the ZCPR33 code, I noticed
  423. that a trivial change would allow the command processor to
  424. load a file to an address other than 100H.  This, I
  425. realized, could overcome the problems with the GO command. 
  426. User programs could be loaded, as usual, to 100H, but
  427. programs invoked automatically by the command processor
  428. could be loaded to a higher address, such as 8000H.  User
  429. programs in low memory would not be overwritten, and the GO
  430. command would still be able to rerun them.
  431.  
  432.    One more group of major innovations was introduced with
  433. ZCPR33.  ZCPR30 provided a number of security features that
  434. made it particularly suitable for use on a remote access
  435. system (BBS).  The so-called wheel byte could be used to
  436. control access to both resident and transient programs. 
  437. Dynamically changeable limits on the range of drives and
  438. user numbers and named directories with passwords could keep
  439. callers out of certain disk areas.
  440.  
  441.    This security made it possible to allow remote users to
  442. run a system directly from the command line prompt, in sharp
  443. contrast to MS-DOS remote systems, where a user who gets to
  444. the command prompt has free reign to access or destroy any
  445. part of the system.
  446.  
  447.    The security system under ZCPR30, while fully effective,
  448. however, could be an unnecessary nuisance.  For example,
  449. there could be situations where a user could access a
  450. directory area by name, because it had no password, but not
  451. by drive/user value, because they exceeded the allowed
  452. range.  Under ZCPR33, if a directory is accessible by name,
  453. then it could also be accessed by drive/user.
  454.  
  455.  
  456.                           ZCPR34
  457.  
  458.    The current state of the art of the ZCPR command
  459. processor is version 3.4.  It was first released some time
  460. around March of 1988 along with NZCOM and Z3PLUS and was
  461. described in issue 32 of TCJ.  Relative to Z33 it was an
  462. evolutionary advance, a refinement; there were no radical
  463. new ideas, as there had been in Z33.  Nevertheless, the
  464. changes were significant and useful.  There have been
  465. several minor revisions since the original release.
  466.  
  467.    One change introduced with ZCPR34 was an extended
  468. environment descriptor.  We removed some information that
  469. had proved to be of little use and added new information. 
  470. The most important addition was a drive vector word.  The
  471. ENV always had a max-drive byte that specified the highest
  472. letter drive available on the system.  However, this was not
  473. adequate for systems that had drives that were not
  474. contiguous, such a A:, B:, and E:.  The new drive vector
  475. tells exactly which drives are available for use at any
  476. time.
  477.  
  478.    The new ENV also contains the addresses of the CCP, BDOS,
  479. and BIOS and the sizes of the first two.  This is to prepare
  480. us for some future enhancements in which we will not
  481. necessarily adhere to the standard component sizes that were
  482. specified in the original CP/M.  Hal Bower and Cam Cotrill,
  483. as part of the development of a new banked version of ZSDOS
  484. (which is nearing release), have been experimenting with a
  485. CCP that is larger than the usual 2K and a banked DOS that
  486. will be significantly smaller than the standard 3.5K.
  487.  
  488.    I mentioned earlier that ZCPR33 had rationalized the
  489. implementation of directory security so that any area
  490. accessible by name would also be accessible by drive/user,
  491. even if the drive or user exceeded the limit set in the
  492. environment.  With ZCPR34 the symmetry was completed.  Now
  493. if there is a password-protected directory that could be
  494. accessed freely using the DU: format, then its password will
  495. be ignored.  Now there will never be directory areas that
  496. can be accessed in one way but not the other.
  497.  
  498.    The extended command processor interface was liberalized
  499. so that commands that would formerly have been considered
  500. illegal and processed immediately as errors, such as those
  501. with wildcard characters ('?' or '*') or with an explicit
  502. file type, can be passed to the ECP.  For example, the
  503. ALIAS.CMD file that defines aliases for the ARUNZ extended
  504. command processor can now have an entry for the command '?'
  505. that invokes the program HELP.
  506.  
  507.    The most significant advance in ZCPR34 was support for
  508. what we now call a type-4 program.  Type-3 programs, as we
  509. described earlier, are loaded and run at an address other
  510. than 100H, but the address is still fixed at the time the
  511. program is compiled.  It was clear to me at the time I wrote
  512. Z33 that it would be ideal if the load/run address of a
  513. program could be determined dynamically (that is, at the
  514. time it is loaded by the CCP).  However, I opted for the
  515. very simple code that sufficed for handling the type-3
  516. program.
  517.  
  518.    Joe Wright was not satisfied with that compromise and
  519. soon wrote an initial implementation of a type-4 program,
  520. which would relocate the code automatically to the top of
  521. free memory.  With a lot of cooperation between us, we honed
  522. the approach to the point where it functioned very nicely
  523. and did not add much code to the command processor.
  524.  
  525.    The secret to this lay in Joe's use of what is called a
  526. PRL (page relocatable) program for the executable file.  The
  527. details of this are described in TCJ issue 32.  The standard
  528. PRL file begins with two 128-byte header records, and I
  529. suggested placing the code required to calculate the proper
  530. load address and the code to perform the address relocation
  531. in these header records rather than in the command processor
  532. itself.  Joe found a brilliant way to implement this.
  533.  
  534.    Not only did this approach keep the CCP code shorter, it
  535. also made the whole type-4 program more flexible by making
  536. it independent of the command processor.  My next TCJ column
  537. will introduce the first examples of alternative versions of
  538. the type-4 loader routines that are placed in the PRL
  539. header.  These new headers can be installed by the user in
  540. any existing type-4 program to change the way the program
  541. relocates itself.  This is another example of the beauty of
  542. the modular approach that has been one of the hallmarks of
  543. ZCPR.
  544.  
  545.    My next column will also introduce the latest revision of
  546. ZCPR34, version 3.4E.  Howard Goldstein prepared this
  547. version by integrating a number of ideas, most notably a
  548. small change in the type-4 loader code to make it even more
  549. flexible than it was originally.
  550.  
  551.    So, after ten years, an eternity in the computer
  552. industry, ZCPR -- the concept Richard Conn initiated -- is
  553. still developing and still challenging the creativity of
  554. users and programmers alike.  As always, these developments
  555. arise from the cooperation of a large community of people
  556. willing and eager to share ideas.
  557.