home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / JSAGE / ZSUS / TCJ / TCJ36BMM.WS < prev    next >
Text File  |  2000-06-30  |  26KB  |  712 lines

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