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

  1. .h1 = main heading
  2. .h2 = subheading
  3. italics are delimited by matching underscores
  4.  
  5.                 Advanced CP/M
  6.  
  7.               Environmental Programming
  8.  
  9.                Bridger Mitchell
  10. ------------
  11. sidebar on Bridger
  12. PLEASE CHANGE TELEPHONE NUMBER TO:    "213-393-6105 (evenings)"
  13.  
  14. ------------
  15.  
  16. .h1 BackGrounder ii Update
  17.  
  18. BackGrounder ii is like no other CP/M program -- it simply feels
  19. different.  A touch of the "suspend" key and you pop into the
  20. background command processor, a touch of a user-defined macro key and
  21. you can switch to a second program, literally in mid-sentence.  The
  22. built-in calculator, notepad, screen-dump, and cut-and-paste function
  23. turn out to be extremely handy desk accessories, especially because
  24. results on one screen can be exported to another task.  But the magic
  25. of it all -- black magic, perhaps -- is the feeling that comes over
  26. you when you first experience the screen flashing back, cursor in
  27. place, with _no_ trace of having been away!
  28.  
  29. BGii and the Z-System stand as twin pinnacles of advanced CP/M
  30. operating systems.  At a conceptual level, they are orthogonal.  By
  31. providing memory buffers for the command processor and applications
  32. and supporting conditional execution, ZCPR 3.4 allows tasks to
  33. _communicate sequentially_.  By making the BDOS and command-processor
  34. recursive, BackGrounder ii creates two-way _communication between
  35. simultaneous tasks_, under user control.  When combined -- BGii
  36. running in a ZCPR 3.4 system -- they elevate 8-bit computing into another
  37. dimension.  The results are awesome.
  38.  
  39. Bringing BGii fully up-to-date to support the latest ZCPR version 3.4
  40. has been a largely enjoyable task.  I had put it off more than once,
  41. wanting to finish up DosDisk and then Z3PLUS.
  42.  
  43. When I finally returned to the BGii code I was pleasantly surprised to
  44. uncover several new coding shortcuts.  They enabled me to squeeze in
  45. almost all of the "Z34" features and add some new conveniences,
  46. including enabling the user to rename the built-in BGii commands.
  47. Expert testing by Cam Cotrill and Jay Sage greatly firmed up several
  48. soft spots.  It's now the production version and licensed users can
  49. order an update at low cost.
  50.  
  51.  
  52. .h1            Environmental Programming
  53.  
  54. A customer of long-standing called the other night, as I was drafting this
  55. column.  He enthused about BackGrounder ii, but then noted that "it
  56. sometimes finds bugs in _other_ programs!"
  57.  
  58. Alas, bugs are always with us, even when we think we've got our own
  59. code pretty solid!  This column is going to be about writing code that
  60. is respectful of the environment in which it is running.
  61. The sage (Sage?) advice collected here, and culled from the
  62. programming experience of many old hands, surely won't eliminate bugs.
  63. But it will greatly increase the chances of your programs living more
  64. harmoniously with a wide variety of CP/M systems.
  65.  
  66.  
  67. .h2  Make a good start
  68.  
  69. The command processor starts your program by _calling_ it. This means
  70. that you can speed up the flow of jobs by _returning_ to the CCP when
  71. your program terminates, instead of causing a warm boot that reloads
  72. the CCP.  To do this, you must _save the stack pointer_ and stay clear
  73. of the CCP in the 2K of memory just below the BDOS.
  74.  
  75. _Use a local stack_ for all but the simplest programs.  The command
  76. processor's stack may not be deep enough for your functions, BIOS
  77. calls, and interrupts.  And that stack could be in the TPA, part of
  78. the CCP that may be overwritten by your program or data.
  79.  
  80. .h2 Know the Territory
  81.  
  82. A shockingly large number of programs assume that they will always be
  83. run only in the environment for which they were written.  Drop them
  84. into a different world and they almost always injure their host.
  85. So, please, join the environmentalists and take the responsible
  86. programmer's oath: _Do No Harm!_ Survey the territory before plunging
  87. ahead, and pose these questions:
  88.  
  89. Is our host a Z80?  An HD64180?  A Z280?  That determines which opcodes can
  90. we safely use.
  91.  
  92. Is our host running CP/M Plus? or ZSDOS?  What system calls are available?
  93. Is DateStamper running?
  94.  
  95. Is one of the drives set to MS-DOS format under DosDisk?  If so, we
  96. must not make assumptions about internal data in the file control
  97. blocks on that drive or about the structure of the disk directory.
  98.  
  99. Is the host running a Z-System?  With an extended environment?  If so,
  100. we should allow for possible non-standard sized BDOS and CCP modules
  101. and get their addresses from the environment.  If it is _not_ a
  102. Z-System, we must avoid any references to Z-environment parameters;
  103. if we are a Z-tool, put out a short message of requirements and quit.
  104.  
  105. Finally, if we should need to know, can we determine what BIOS and type of
  106. machine our host is?
  107.  
  108. Figure 1 is a routine called TERRITORY that does these checks.
  109. It should be called at the very beginning of a program.  If the host
  110. system has a Z-System command processor (ZCPR 3.3 or later, or
  111. BackGrounder ii) the program will begin with the HL register
  112. containing the address of the Z-System external environment.
  113.  
  114. TERRITORY first checks for a Z80-compatible processor, and then uses
  115. obscure differences in register operations to identify HD64180 and
  116. Z280 processors.  The system addresses (BIOS, BDOS, and CCP) are
  117. determined from a Z-System extended external environment, if there is
  118. one, so that non-standard BDOS and CCP modules can be used correctly.
  119.  
  120. The BIOS check demonstrates how to detect an NZ-COM system and find
  121. the address of the original CBIOS.  Several systems have bios-specific
  122. references to such things as function-key tables, foreign disk
  123. parameter blocks, and extended BIOS functions that cannot be located
  124. from the address at 0001h when NZ-COM is running.
  125.  
  126. You can use the flags and addresses established by TERRITORY for your
  127. own requirements.  You might also want to make a version of it into a
  128. simple diagnostic tool that prints out messages identifying exactly
  129. what the host system consists of.
  130.  
  131.  
  132. .h2 Identify yourself
  133.  
  134. Unless yours should be a silent program, announce yourself to the
  135. user with an appropriate message that includes a version number.
  136. All programs change, get updated, and gain features.  You and other
  137. users need to be able to identify which model they're driving.
  138. Most Z-System tools use a standard format, which is well worth
  139. adopting for other programs:
  140.  
  141.    A>PROGNAME Vers. 1.5 -- terse functional description
  142.  
  143. If you are the silent type, include sufficient version identification
  144. in the data area so that a debugger can be used to inspect the
  145. program.  Alternatively, include the information in a help screen.
  146. Z-System tools use the standard "double-slash" command line to request
  147. help, another worthwhile convention:
  148.  
  149.   A>PROGNAME //
  150.  
  151.  
  152.  
  153. .h2 Protect the Environment
  154.  
  155. _Save the current drive and user number_, so you can restore them on exit.
  156.  
  157. Explicitly _allocate memory_ and check to prevent overflowing the
  158. available transient program area.  The TPA is always the memory from
  159. 100h to the value that is stored at 0006, less 1 byte.
  160.  
  161. If there are no RSX's in memory, that value is the entry address of
  162. the BDOS and is the target of the jump instruction at 0005.  This is
  163. the most common case; in CP/M 2.2 the CCP will occupy 6 + 800 hex
  164. bytes below that.  But if an RSX has been loaded, its address
  165. will be at 0006. In that case the CCP will already be protected, and
  166. you can use all of the TPA and still return to the CCP. 
  167.  
  168. So, if no RSX is loaded, if you intend to return to the CCP, and if you
  169. are not running under CP/M Plus, allow 2K of space below the BDOS.
  170. Figure 2 gives a routine that makes this calculation.  It calculates
  171. the largest usable memory that will still preserve the CCP and returns
  172. the address of the first byte beyond that.
  173.  
  174. Applications have the right to have their register values treated
  175. systematically when they call on the operating system for services.
  176. CP/M is an 8080-based operating system, and from the beginning it put
  177. programmers on notice that it would not preserve the user's registers.
  178. That was logical, as the OS needed most of them for returning values.
  179.  
  180. The introduction of the Z80 and subsequent 8080-compatible CPUs led to
  181. more compact and more efficient BIOSes.  Unfortunately, more than one
  182. BIOS writer began using the additional _Z80 registers_ without
  183. preserving their values for the user.  The consequences have been
  184. erratic havoc -- programs test out flawlessly on a variety of systems,
  185. then fail to start, or die mysteriously on another machine.
  186.  
  187. The environmentally-conscious rule here is: if your code will become
  188. part of the operating system -- the BDOS, BIOS or an RSX extension --
  189. save and restore all Z80 registers you use.  Why?  Simply because it's
  190. a far greater burden on an application to preserve IX, IY, AF', BC',
  191. DE', and HL' in order to run on an arbitrary system, than it is for
  192. the system programmer to protect exactly those registers he needs to
  193. use.
  194.  
  195. The extreme case of environmental wantonness is a rom-based-based BIOS
  196. for the early Osborne Executive, which used alternate Z80 registers
  197. for an interrupt service routine, without preserving them!  A moment's
  198. thought should persuade you that there is _no way_ an application
  199. could _ever_ use those registers and run on that machine.  Was the
  200. system designer so naive as to think that only 8080-code would ever be
  201. run on an Executive?
  202.  
  203.  
  204. .h2  Expect the Worst
  205.  
  206. _Test for all disk errors_.  Proceeding after one error (a full disk,
  207. a non-existent file, ...) can wreak disaster.
  208.  
  209. _Have a recovery strategy_.  Tell the user enough about the problem
  210. that he can alter the environment and try again.  Give him a
  211. choice (insert a disk, restart the program, exit, ...).
  212.  
  213.  
  214. .h2 Limit the Damage
  215.  
  216. Before writing a file, check the disk space remaining.  If there isn't
  217. enough room, give the user the chance to delete something, or to insert a
  218. new disk.
  219.  
  220. If a write error occurs, try to close the file to save as much data as
  221. possible.  Offer the user a second chance, by changing disks or
  222. drives.  Clean up file fragments after recovering.
  223.  
  224. Before printing, test to see if the printer is ready.  If it isn't,
  225. ask the user to make it ready before retesting.  Otherwise, if you
  226. start sending characters, the computer will hang and you can't inform
  227. the user what's wrong.  A short routine to do this, derived from
  228. BackGrounder ii, is shown in Figure 3.  It's important to send a test
  229. character (a carriage return) in order to actually test the _printer_
  230. itself, because a serial printer channel will normally have a UART
  231. with a one-character buffer.  If the UART's buffer is empty, it will
  232. report that _it_ is ready to receive a character, even if the printer
  233. isn't.
  234.  
  235.  
  236. .h2 Don't take shortcuts
  237.  
  238. The TERRITORY routine doesn't check for Z-Systems earlier than ZCPR
  239. version 3.3 (which supplies the external environment address in HL
  240. when it calls each program).  It can be extended to do so by searching
  241. memory for the "Z3ENV" string and verifying that the self-reference
  242. address indeed points to the environment area being examined.
  243.  
  244. Note that if the string is found, but the address test fails, the
  245. search must be continued; it's quite possible to have more than one
  246. "Z3ENV" string in memory.  Jay Sage tells me there is a group of
  247. programs that, regrettably, take a shortcut and stop searching on the
  248. first match.  They fail to run on his system because it includes,
  249. quite appropriately, a directory named "Z3ENV".
  250.  
  251. The new Z3PLUS and NZ-COM systems have exposed shoddy programming
  252. practices, bugs that have been hibernating in widely used utilities.
  253. A common one results from the faulty assumption that the Z-System
  254. external environment address began on a memory page (xx00 hex).
  255. When this happens to be the case, then:
  256.  
  257.     ld    l,offset
  258.  
  259. is a shorter route pointing to an environment parameter than:
  260.  
  261.     ld    de,offset
  262.     add    hl,de
  263.  
  264. But when it's not, the path leads over the cliff.
  265.  
  266.  
  267.  
  268. .h2 Clean up
  269.  
  270. _Use a common exit_ point.  This makes it easier to ensure that
  271. nothing is overlooked as your program grows to include new branches.
  272.  
  273. _Close open files_ and _delete temporaries_. 
  274.  
  275. _Restore the default drive and user_.
  276.  
  277. If you've used full-screen terminal features, _leave a clean screen
  278. below the cursor_.  For some applications you may want to clear the
  279. entire screen.  In other cases, having the final lines of data remain
  280. allows the user to make use of the information when she or he enters
  281. the next command; in that case, put the cursor on the bottom line and
  282. send a newline.
  283.  
  284. Finally, if you have not overwritten the CCP, restore the stack and
  285. return.  Otherwise warmboot.
  286.  
  287.  
  288. I certainly don't follow these guidelines slavishly, though I do
  289. pay them regard before releasing any software for wide testing. 
  290. My own temporary programs, run in a known test environment, are
  291. often rude, slap-dash pasteups to get the job of the moment done
  292. rapidly. But I guard against giving them out to others.  It's no
  293. favor to pass on code that may explode a friend's system at some
  294. unsuspecting moment.  Which leads me to recall ...
  295.  
  296.  
  297. .h1           A Tale of Too-hasty Design
  298.  
  299. Some months ago a well-known programmer, author of CP/M several
  300. BIOSes, gave me a copy of the BIOS to a particular new computer. 
  301. I was developing customized operating-system code for this
  302. box, and had a similar machine for the debugging and testing
  303. cycle.  I had re-assembled the BIOS, made several rounds of
  304. modifications that were converging to a stable new system running
  305. on my box when, poof, I exited from a program and the system
  306. went to lunch.  It wouldn't reboot, even from power up.
  307.  
  308. A systems programmer learns (from bitter experiences!) to sit
  309. quietly, write down everything he can remember, and think long
  310. and hard before he touchs anything besides a pencil. Some clues
  311. to the crash may remain behind, on disk or in memory (although in
  312. this case memory was fully reset).  In this case, the A: drive
  313. had been a ram disk with uninterruptible power, so I assumed that
  314. its system tracks had somehow been damaged.
  315.  
  316. I decided to make a systematic tour of the A: disk drive by booting up
  317. from a floppy system, and looking at different tracks with DU.  (Yes,
  318. I try always to have a couple of bootable systems stored away on
  319. floppies for that black day that a hard-disk or ramdisk goes bad.  You
  320. should too.)  Sure enough, I found what appeared to be file data in
  321. the directory tracks.  More investigation disclosed that the following
  322. tracks, which should have been the directory, had also been corrupted.
  323. I took a deep breath, made a mental note to write down what I could
  324. still remember of what I had typed in during the last two hours of
  325. iterations since backing up the experimental system code.  I continued
  326. checking the disk.  Suddenly, several tracks later, the disk appeared normal.
  327.  
  328. What could cause such systematic damage?  Hypotheses poured forth,
  329. only to be discarded.  Finally, I saw the glimmer of a pattern.
  330. The start of the ram disk -- the start of banked memory --contained
  331. data related to the data at the very end of the disk.  Wraparound!
  332. The BIOS had literally gone off the deep end of the ram disk and
  333. started writing on the front, clobbering the system, the
  334. directory, and the first files after that.  As soon as a warmboot
  335. was attempted, the system loaded the corrupted system track and
  336. was dead.
  337.  
  338. A hot theory.  But where was the bug?  I consulted with the
  339. author, and then he remembered --  he'd given me the _large_
  340. ramdisk version of the BIOS.  With less ram installed on my
  341. box the addressing had wrapped around.
  342.  
  343. I wasn't pleased to learn he'd overlooked putting in a _runtime_
  344. check for the amount of memory the system possessed.  A simple
  345. test to make, yet because it was omitted, I now had several hours
  346. of reconstructing and testing files ahead of me.
  347.  
  348. This bug was in hibernation, waiting to byte just when a large
  349. disk filled up.  Only extreme testing is likely to have disclosed
  350. it before it zapped a directory on a customer's machine.  We were
  351. lucky it happened when it did.  Yet it could have been prevented,
  352. by systematic design.  Environmentally conscious programming
  353. would have done it.
  354.  
  355.  
  356. .h1            Identifying the BIOS
  357.  
  358. How can a program determine who its host is?  What hardware is
  359. available?  Digital Research (DRI) had no BDOS function to return a
  360. version number in CP/M 1.4 and never did introduce one for identifying
  361. the BIOS.  I have no idea why DRI failed to anticipate this question;
  362. a BIOS call to return version information seems now the obvious way to
  363. do things.  And MSDOS is no better.
  364.  
  365. Anyway, we are stuck with CP/M's warts; there is no _portable_ BIOS
  366. call that will identify an Ampro from a Kaypro from an S-100 box.
  367. Even if _a particular_ manufacturer had the vision to include this
  368. entry point, it's catch-22.  We can't safely use the call until we know
  369. that it's available!
  370.  
  371. The best approach is to identify the BIOS by reading memory bytes in
  372. the BIOS.  Every BIOS should include a unique signature and version
  373. information.  The signature can be an ascii string, such as "PPS" (in
  374. the Advent/Plu*Perfect TurboRom BIOS) or "XBIOS" (in the XBIOS for
  375. SB180's), at a known offset from the start of the BIOS.  Once the
  376. program has identified the type of BIOS, it knows it can make an
  377. extended BIOS call, if one has been provided, to obtain additional
  378. information.  The XBIOS system, for example, provides information on
  379. the available hardware devices and their corresponding ascii names in
  380. the system.
  381.  
  382. The TERRITORY routine includes a ck_bios routine to identify Kaypro
  383. systems with a TurboRom BIOS and systems running NZ-COM.  Other checks
  384. can be added to identify other particular systems.
  385.  
  386. Unfortunately, a number of BIOS's were written by people who
  387. apparently never thought others might write software for their very
  388. computer!  In these cases, the safest approach is to tell the user in
  389. a message that the type of BIOS cannot be determined, and ask him to
  390. confirm or enter the model manually before proceeding.
  391.  
  392.  
  393. ;   Figure 1.  Determine characteristics of CP/M host's environment
  394. ;   _______________________________________________________________
  395.  
  396.  
  397. bdos    equ    5
  398.  
  399. ; Call this routine immediately.
  400. ; Enter with HL = value from command processor.
  401. ;
  402. TERRITORY:
  403. ;
  404. ; Test for z80-compatible cpu.
  405. ;
  406.     sub    a        ; sets even parity in 8080
  407.     jp    po,ck_180
  408.     ld    c,9        ; announce Z80 requirement
  409.     ld    de,notz80msg
  410.     call    bdos
  411.     rst    0        ; ..and exit to warmboot
  412. ;
  413. ; Test for HD64180/Z180
  414. ;
  415. ck_180:
  416.     ld    bc,101h        ; prepare to multipy B=1 x C=1
  417.     db    0EDh,04Ch    ; MLT BC opcode
  418.     dec    b        ; if Z80, B will be unchanged 
  419.     jr    z,ck_280    ;
  420.     ld    a,c        ; 180 leaves 16-bit result (1) in BC
  421.     ld    (z180flag),a
  422. ;
  423. ; Test for Z280
  424.  
  425. ck_280:    ld    a,10        ; Z280 doesn't use refresh register
  426.     ld    r,a        ; load it
  427.     ld    c,a        ; save it
  428.     ld    b,a        ; cause some refreshes
  429. loop:    djnz    loop
  430.     ld    a,r        ; if value hasn't changed
  431.     cp    c
  432.     jr    nz,ck_rest
  433.     ld    (z280flag),a    ; ..it's a 280
  434. ;
  435. ck_rest:
  436.     push    hl        ; save possible env address from ccp
  437.     call    ck_dos        ; check type of BDOS
  438.     pop    hl
  439.     call    ck_z3        ; check for Z-System
  440.     call    ck_dosdisk    ; check for DosDisk
  441.     call    ck_bios        ; check type of BIOS
  442.     call    ck_bg        ; check for BackGrounder ii
  443.     ret
  444.  
  445. ;
  446. ;
  447. ; Check for BDOS version 
  448. ;
  449. ck_dos:    ld    c,12        ; get CP/M version number
  450.     ld    e,'D'        ; with DateStamper id request
  451.     call    bdos
  452.     cp    30h
  453.     jr    c,ck_ds
  454.     ld    (cpm3flag),a    ; set flag if CP/M 3
  455.     ret
  456. ;
  457. ; Check for DateStamper
  458. ;
  459. ck_ds:
  460.     cp    22h
  461.     jr    nz,ck_xdos    ; .. not CP/M 2.2
  462.     ld    a,h
  463.     cp    'D'
  464.     jr    nz,ck_xdos    ; ..no DateStamper
  465.     ld    (dsflag),a    ; set flag
  466.     ld    (dsclock),de    ; and save clock pointer
  467. ;
  468. ; Check extended dos version
  469. ;
  470. ck_xdos:ld    c,48        ; use extended version number call
  471.     call    bdos
  472.     ld    (dosvers),hl    ; save version number and type
  473.     ret
  474.  
  475. ;
  476. ; Check for Z-System.
  477. ; Enter with HL = value from command processor.  If ZCPR 3.3
  478. ; or BackGrounder ii, HL -> external environment
  479. ;
  480. ;
  481. ck_z3:    push    hl        ; save possible ENV address
  482.     inc    hl        ; Offset to 'Z3ENV'
  483.     inc    hl
  484.     inc    hl
  485.     ld    b,Z3ENVLEN
  486.     ld    de,z3envsig
  487.     call    match
  488.     pop    de        ; recover de = ENV address
  489.     jr    nz,set_std
  490.     ld    hl,1Bh        ; Offset to self-reference address
  491.     add    hl,de
  492.     ld    a,(hl)        ; Check low byte
  493.     cp    e
  494.     jr    nz,set_std
  495.     inc    hl
  496.     ld    a,(hl)        ; Check high byte
  497.     cp    d
  498.     jr    nz,set_std
  499. ;
  500.     ld    (zsysflag),a    ; set flag
  501.     ld    (z3env),de    ; save environment address
  502.     ld    hl,08h        ; get env. type
  503.     add    hl,de
  504.     ld    a,(hl)
  505.     and    80h        ; test for extended type (>= 80h)
  506.     ld    (extenvflag),a    ; and save result
  507.     jr    z,set_std
  508. ;
  509. ; Set system addresses for a Z-System with an extended environment
  510.  
  511.     ld    hl,45h        ; -> bios address in environment
  512.     call    addderef
  513.     ld    (biosbase),hl
  514.     ld    hl,42h
  515.     call    addderef
  516.     ld    (bdosbase),hl
  517.     ld    hl,3Fh
  518.     call    addderef
  519.     ld    (ccpbase),hl
  520.     ret
  521.  
  522. ;
  523. ; Set system addresses for a standard system.
  524. ;
  525. set_std:
  526.     ld    hl,(0001h)
  527.     ld    l,0
  528.     ld    (biosbase),hl
  529.     ld    de,-0E00h
  530.     add    hl,de
  531.     ld    (bdosbase),hl
  532.     ld    de,-800h
  533.     add    hl,de
  534.     ld    (ccpbase),hl
  535.     ld    a,(cpm3flag)    ; if not CP/M Plus
  536.     or    a,a        ; ..all done
  537.     ret    z
  538. ;
  539.     ld    c,49        ; for CP/M Plus, w/o extended environment...
  540.     ld    de,getscbpb    ; get system control block address
  541.     call    bdos
  542.     ld    l,98h        ; offset to address of resident bdos
  543.     call    deref        ; dereference pointer
  544.     ld    l,0
  545.     ld    (bdosbase),hl
  546.     ld    hl,100h
  547.     ld    (ccpbase),hl
  548.     ret
  549. ;
  550. addderef:
  551.     add    hl,de        ; offset pointer by DE
  552. deref:    ld    a,(hl)        ; dereference HL pointer
  553.     inc    hl
  554.     ld    h,(hl)
  555.     ld    l,a
  556.     ret
  557. ;
  558. match:    ld    a,(de)        ; match B bytes at DE, HL
  559.     cp    (hl)
  560.     inc    hl
  561.     inc    de
  562.     ret    nz
  563.     djnz    match
  564.     ret
  565.  
  566. ck_dosdisk:
  567.     ld    c,113        ; get DosDisk id
  568.     call    bdos
  569.     cp    0FDh
  570.     ret    nz        ; ..if not DosDisk, quit
  571.     ld    (dosdiskflag),hl; set flag (L) and drive with MS-DOS format (H)
  572.     ret
  573.  
  574.  
  575. ck_bios:
  576.     ld    hl,(0001h)    ; -> normal CBIOS warmboot address
  577.     ld    l,90        ; -> NZ-COM id in NZ-COM pseudobios
  578.     ld    de,nzname    ; if NZ-COM id is exactly there
  579.     ld    b,NZNAMELEN
  580.     call    match
  581.     jr    nz,ck_turbo
  582.     ld    hl,(z3env)    ; ..get CBIOS (page) addr from
  583.     inc    hl        ; ..z3env+2
  584.     inc    hl
  585.     ld    h,(hl)        ; ..get page
  586.     ld    l,0
  587.     ld    (biosbase),hl    ; ..and save correct ptr to CBIOS
  588. ;
  589. ; Check for Kaypro TurboRom
  590. ;
  591. ck_turbo:
  592.     ld    hl,0FFF8h    ; -> location of TurboRom signature
  593.     ld    b,TURBOSIGLEN
  594.     ld    de,turbosig
  595.     call    match
  596.     ret    nz
  597.     ld    (turboromflag),a; set flag
  598.     ret
  599.  
  600. ;
  601. ; Check for BackGrounder ii
  602. ;
  603. ck_bg::    ld    hl,(bdosbase)    ; -> location of BGii signature
  604.     push    hl
  605.     ld    de,-800h+5Bh    ; in BGii CCP
  606.     add    hl,de
  607.     ld    b,BGSIGLEN
  608.     ld    de,bgsig
  609.     call    match
  610.     pop    hl
  611.     ret    nz
  612.     ld    (bgflag),a    ; set flag
  613.     ld    de,-800h+1    ; -> BGii ccp entry +1
  614.     call    addderef    ; get that address
  615.     dec    hl        ; BGii task flag is 1 byte lower
  616.     ld    (bgtaskptr),hl    ; save ptr to task flag
  617.     ret
  618. ;
  619. notz80msg:
  620.     db    'Not Z80.$'
  621. getscbpb:
  622.     db    3Ah        ; return address of scb
  623.     db    0        ; "get" code
  624. ;
  625. ; Z-System environment signature
  626. ;
  627. z3envsig:
  628.     db    'Z3ENV'
  629. z3envlen equ $-z3envsig
  630. ;
  631. ; NZ-COM's  signature in the pseudo-bios, at offset 90.
  632. ;
  633. nzname:    db    'NZ-COM'
  634. nznamelen equ $ - nzname
  635. ;
  636. ; BackGrounder ii's signature
  637.  
  638. bgsig:    db    'BGii'
  639. bgsiglen equ    $ - bgsig
  640. ;
  641. ; Kaypro TurboRom signature, at FFF8h
  642. ;
  643. turbosig:db    'PPS'
  644. turbosiglen equ $ - turbosig
  645. ;
  646. ;
  647. ; System flags (any NZ value means TRUE)
  648. ;
  649. z180flag:    db    0    ; HD64180/Z180 cpu
  650. z280flag:    db    0    ; Z280 cpu
  651. cpm3flag:    db    0    ; CP/M Plus
  652. dsflag:        db    0    ; DateStamper
  653. zsysflag:    db    0    ; Z-System (ZCPR 3.3 or later)
  654. extenvflag:    db    0    ; extended Z-System environment
  655. bgflag:        db    0    ; BackGrounder ii
  656. turboromflag:    db    0    ; Advent/Plu*Perfect Turborom
  657. ;
  658. dosvers:    db    0    ; } a pair  version number (hex)
  659. dostype:    db    0    ; }         'S' = ZSDOS, 'D' = ZDDOS, 0 = ZRDOS
  660. dosdiskflag:    db    0    ; } a pair  DosDisk
  661. dosdrive:    db    0    ; }        MS-DOS format drive
  662. bgtaskptr:    dw    0    ; BGii internal flag pointer: bit 1 set = upper task
  663. ;
  664. ; System Addresses
  665. ;
  666. z3env:        dw    0    ; Z-System external environment
  667. biosbase:    dw    0    ; BIOS
  668. bdosbase:    dw    0    ; BDOS
  669. ccpbase:    dw    0    ; CCP
  670. dsclock:    dw    0    ; DateStamper clock
  671.  
  672.  
  673.  
  674.  
  675. ;     Figure 2.  Find Top of Usable Memory that Preserves the CCP
  676. ;     ____________________________________________________________
  677.  
  678.  
  679. ; Return HL = top of usable memory + 1
  680. ;
  681. top_of_mem:
  682.     ld    hl,(0006)    ; load protect address
  683.     ld    a,(cpm3flag)    ; if CPM Plus
  684.     or    a        ; 
  685.     ret    nz        ; ..return it
  686.     push    hl        ; for CP/M 2.2
  687.     ld    de,(ccpbase)    ; if protect address is below CCP
  688.     sbc    hl,de
  689.     pop    hl
  690.     ret    c        ; .. return it
  691.     ex    de,hl        ; else return CCP address
  692.     ret
  693.  
  694.  
  695.  
  696. ;           Figure 3.  A Printer-Ready Test
  697. ;                  _______________________________
  698.  
  699. ; Return: NZ if printer is ready
  700. ;
  701. test_list:
  702.     call    b_lstat        ; if printer is busy
  703.     ret    z        ; ..return
  704.     ld    c,0dh        ; send carriage-return
  705.     call    b_list        ; ..to flush UART buffer
  706.     call    wait        ; allow printer to process it
  707.                 ; and re-test status
  708. b_lstat:ld    de,2dh-3    ; call BIOS list-status entry
  709.     call    bcall
  710.     or    a,a
  711.     ret
  712. ;
  713. b_list:    ld    de,0fh-3    ; call BIOS list entry
  714. bcall:    ld    hl,(0001)
  715.     add    hl,de
  716.     jp    (hl)
  717.  
  718.  
  719. wait:
  720. ;    ...            ; any 10-20 mS routine
  721.     ret
  722.  
  723.  
  724.