home *** CD-ROM | disk | FTP | other *** search
- advcpm.b34 Advanced CP/M for TCJ #34
-
- .h1 = main heading
- .h2 = secondary heading
- @I{...} = set in italics
-
- Advanced CP/M
- Extending the Operating System
-
-
- Bridger Mitchell
-
- [ the usual sidebar on Bridger]
-
- Sooner or later, the CP/M user bumps up hard against the limitations
- of the operating system and wonders -- can something be done? Yes,
- CP/M can be enhanced at several levels.
-
- .h2 Command Processor Extensions
-
- A great deal of effort has been directed to improving the external,
- command-processing part of CP/M. The "command shell" is readily
- replaced; it is the most immediately noticed component; and it
- is can be extended by placing added code in files that
- can be manipulated by the existing system. Z3PLUS, NZ-COM, and ZCPR34
- are the latest achievements at this level; they both replace the
- command processor and provide well-defined resident buffers for
- communication between successive tasks. There have been a number of
- other replacement command processors, such as CNIX, CONIX, and QPM.
-
- .h2 BDOS and BIOS extensions
-
- The next level of extension is adding new operating system functions
- -- extra BDOS calls. CP/M Plus provides a particularly convenient
- method, called a Resident System Extension (RSX), for adding such
- capabilities. Under CP/M Plus, an RSX can be attached to a program
- that need extended services and loaded automatically along with the
- original program. It is rather easy to modify existing BDOS
- functions, for example, to keep statistics on the frequency with which
- key files are accessed.
-
- More elaborate RSXs have been developed for CP/M Plus to
- emulate the CP/M 2.2 BIOS file functions so that programs that make
- direct BIOS calls can run under CP/M Plus by attaching the necessary
- RSX but leaving the program itself unchanged. The most complex CP/M
- Plus RSX is the brand-new Z3PLUS system. Within the single RSX are
- the ZCPR34 command processor and program and RSX loader, the Z-System
- buffers for named directories, resident commands, messages, command
- line, file control block, and environment descriptor.
-
- However, more basic changes to CP/M 2.2, such as adding time and
- datestamping or redirection of console and printer services, are much
- more difficult. They are necessarily inner extensions to CP/M and
- require intimate knowledge of the BDOS and BIOS.
-
- Another area that frequently needs extension is an interface to the
- hardware. The original BIOS may provide for only a limited number of
- disk formats. Function keys that transmit escape sequences may
- need to be timed and mapped into defined character strings. Printers
- could need character translation tables to generate control sequences
- or foreign characters. These system extensions are primarily BIOS
- modifications.
-
- The most extensive extension of CP/M 2.2, and the most complex piece
- of software I have ever written, is BackGrounder ii. It is installed
- as an RSX that makes extensive modifications to selected BIOS and BDOS
- functions, replaces the command processor, and adds virtual swapping
- memory to the basic system. The result is effectively @I(two) virtual
- CP/M computers on one machine, with user-controlled ability to switch
- between two running programs at any time, and to preserve the exact
- screen display of each on most terminals.
-
- .h2 RSXs for CP/M 2.2
-
- An RSX is not necessarily the first, or the best, choice for adding
- capability to a computer system. The operating system can be extended
- by rewriting the BIOS (provided the source code is still available!).
- This is the most fundamental approach. Specific to one computer, it
- is permanent and totally non-portable.
-
- The system can be extended for only a single application, as when a
- program is run by first loading a debugger. Quick and dirty,
- this approach is sometimes useful for temporarily modifying
- system behavior or pre-testing a more permanent approach.
-
- A resident system extension is a versatile, intermediate
- approach. An RSX can be installed and left in place to extend
- the system for as long as it is needed, then removed to restore
- the original system and full memory. If well designed, it can be
- quite portable. It can be used to add both BDOS and BIOS
- features to the vanilla CP/M 2.2 system. And several RSXs can be used
- together to combine system power.
-
- In this issue's column I discuss System Extensions that modify the
- behavior of the BDOS and/or BIOS. Because these services must
- essentially be available at all times (whenever a BDOS or BIOS call is
- made), the extension code must be _resident_. This is unlike
- extensions of the command processor, which can be reloaded from the
- system tracks (or from a file) when a new command is ready for
- processing.
-
- I will limit the examples to extensions for the CP/M 2.2 operating
- system (and its clones). CP/M Plus already includes RSX capability for
- BDOS (but not BIOS) extensions and it also includes provision for
- character-device redirection (so that, for example, the printer output
- can be diverted into a file). The basic ideas, however, carry over to
- CP/M Plus. Indeed, I have used them effectively in implementing both
- Z3PLUS and DosDisk on CP/M Plus computers.
-
- .h2 No Free Lunch
-
- Although smart coding and clever uses of the RSX concept can greatly
- extend the vanilla CP/M system, there are limits imposed by a 64K
- address space Z-80 operating system. As I was preparing this column a
- potential DosDisk customer called, hoping that DosDisk would let him
- run Lotus 1-2-3 on his Z-80 computer with 8 inch disks! I trust that
- anyone who has read this far has somewhat more modest expectations.
-
-
- .h1 The Conventional CP/M 2.2 System
-
- In the remainder of this column I will describe how an RSX modifies
- the conventional memory map and flow of control, using a standard RSX
- header data structure. We will then turn to a complete example.
-
- In order to see how RSXs fit into the CP/M 2.2 system, let's first
- review a few key points about a "vanilla" system.
-
- The end of the TPA (transient program area), the memory available to
- application programs, is specified by the address stored on page-zero
- at location 0006. Initially, that value is the address of the entry
- to the BDOS, and a "call 5" instruction will jump directly to the
- BDOS. Beginning at the address whose value is stored at 0006, memory
- is said to be "protected" -- it is not available to applications, and
- data and code in that area should remain "resident" from one
- application to the next.
-
- The command processor is assembled or linked to be loaded in the top
- 2K of the TPA memory. The command processor is not resident, and that
- 2K of memory is available to applications. Therefore, on each
- warm-boot -- a jump or call to 0000 -- the BIOS reads a fresh copy of
- the command processor into high memory. (It may also read in a fresh
- copy of the BDOS, depending on the implementation in the BIOS.)
-
- Figure 1 shows the essential flow of control. The address at 0006 is
- "bdos+6", the entry to the BDOS, and the TPA extends from 100h to
- bdos+5. A jump to 0000 vectors to "bios+3", which jumps to "bioswb",
- the BIOS warm-boot routine. That routine reloads the command
- processor, installs fresh values of "bios+3" and "bdos+6" at 0001 and
- 0006, and then jumps to the command processor entry "ccp+3" to process
- the next command.
-
- .h1 The Standard RSX Header
-
- In order to insert a resident extension into this scheme we must
- deal with memory management, BDOS and BIOS calls, provide for
- removal of the RSX when it is no longer needed, and anticipate
- the loading of other RSXs while this one is active.
-
- In addition to being intrinsically complex, implementing all of this
- is tedious and error-prone. Several years ago, to provide a standard
- approach, I established a Plu*Perfect Systems RSX header -- a uniform
- data structure at the beginning of every RSX -- to permit several RSXs
- to coexist peacefully. And for this column I extended JetLDR to
- recognize a relocatable RSX module in named-common ZRL relocatable
- format and load it automatically.
-
- The header (Figure 2) begins with three jumps to routines in the RSX
- -- to the BDOS intercept routine "rsxstart", to the warm-boot
- intercept routine "rsxwb", and to the removal routine "rsxremove".
-
- Next come three addresses. First, "rsxwba" is the address of the
- (original) BIOS warmboot vector which is used to, hopefully,
- correct the problems created by programs that erroneously change
- the contents of 0001. The second word is the "protect" address,
- the lowest byte in the RSX that must be in protected memory.
- This is followed by the address of the nul-terminated ascii
- name of the RSX.
-
- Two jumps and a final word complete the header. The chain of a
- warm-boot (jump 0000) flows through "rsxnext", which points to
- either the next-higher RSX, or to the command processor entry.
- Similarly, the call BDOS chain flows through "next", which points
- to either the next-higher RSX, or to the BDOS entry. The final
- item, "nextwb" holds the address from the BIOS warm-boot vector
- at the time the RSX is loaded, so that it can be restored by the
- remove routine.
-
-
- ---------------------------------------------------------------------
- Figure 2. Standard RSX Header
-
- rsx: jp rsxstart ; BDOS intercept ; 00
- jp rsxwb ; warm-boot intercept ; +03
- jp rsxremove ; remove-rsx entry ; +06
- rsxwba: dw $-$ ; original 0001 value ; +09
- rsxprot:dw rsx ; lowest rsx address ; +0B
- dw rsxname ; -> rsx name ; +0D
- rsxnext:jp $-$ ; -> next wb or ccp entry ; +0F
- ;
- next: jp $-$ ; -> next rsx or bdos ; +12
- nextwb: dw $-$ ; original bios+4 addr ; +15
-
- ---------------------------------------------------------------------
-
- .h1 Adding an RSX to Memory
-
- In order to add an RSX to a running system, we must arrange to
-
- - allocate sufficient memory for the RSX
- - deduct that memory from the available TPA, so that
- an application will not attempt to use the memory occupied by the RSX
- - prevent the BIOS from undoing the memory allocation at the next warm-boot
-
- We will locate the RSX in high memory, immediately below the command
- processor -- see Figure 3. To do this, we change the value at 0006 to
- point to the first byte of the RSX, and arrange to have a jump
- instruction there that (eventually) causes control to enter the BDOS
- (this is the initial jump in the standard RSX header). The TPA is now
- reduced to the area 100h to "rsx1-1".
-
- To keep the BIOS routine "bioswb" from undoing our handiwork by
- resetting the value of 0006, we must either modify the "bioswb"
- routine, get control back @I{after} that routine has completed, or
- prevent it from executing. Prevention is the only portable solution;
- and it must be done intelligently.
-
- .h2 The System Loadstone
-
- The "obvious" method of bypassing the warm-boot routine is to change
- the address at 0001 to point to a copy of the (modified) BIOS jump
- table located in the RSX. However, this would be fundamentally wrong,
- because this is the @I{one} address in the CP/M system that must
- remain fixed. Why? Consider what will happen when a second RSX is
- being loaded. It will be unable to locate the BIOS and therefore
- cannot correctly intercept BIOS functions!
-
- @I{The warm-boot address at 0001 should never be altered!} It is
- the one fixed point, the lodestone, of the entire CP/M system.
- Instead, the warm-boot chain should be intercepted at the BIOS
- vector (at bios+4) and redirected to the RSX from that point.
-
- Unfortunately, this absolutely essential role of 0001 has apparently
- not been widely understood. Both Digital Research (in XSUB) -- the
- designers of CP/M! -- and MicroPro (in the WordStar "R" command)
- occasionally commit the error of changing 0001, and their mistakes
- have been perpetuated by several public-domain programs as well.
-
- Figure 3 shows a correctly modified warm-boot chain with the RSX
- installed. At 0000, control continues to jump to the BIOS, where it is
- @I(then) redirected to "rsx1+3" and then to the RSX's warm-boot
- routine. Eventually, control flows to the copy of the command
- processor that is already in memory, bypassing the bios warmboot
- routine and without reloading the command processor. We will cover the
- details of the RSX warmboot routine in the example below.
-
-
- .h2 A Second RSX
-
- Adding a second RSX is similar to loading the first one (see
- Figure 4) and involves splicing the new RSX into the BDOS and
- warmboot chains. Memory is allocated below the first RSX, 0006 is
- redirected to the head of rsx2 where BDOS calls are intercepted
- and eventually vectored to rsx1. The warm-boot intercept at
- bios+4 is redirected to rsx2+3, which eventually vectors to
- rsx1's warmboot routine.
-
- .h2 Removing an RSX
-
- The standard RSX header includes an entry that is called to
- remove the routine. It deallocates the RSX's memory and removes
- itself from the BDOS and warm-boot chains. It also removes
- its BIOS intercepts, if any. If there is more than one RSX in
- memory, the lowest one must be removed first, to ensure that
- the addresses (or data) that are restored are correct. Each
- RSX's remove routine first determines that it is, in fact, lowest
- before executing the rest of the removal code.
-
- The standard header anticipates that most RSXs will be self-removing,
- provided that they are the bottom RSX when the remove entrypoint is
- called. However, some RSXs, such as DateStamper and DosDisk, make
- extensive modifications to the BIOS and BDOS. A good deal of
- additional code and data would have to be resident in the RSX for
- these RSX's to be self removing. Therefore, these RSXs are removed
- by their own customized loaders. If the remove entrypoint is called
- by any other program it will do nothing except to return the carry
- flag clear, indicating that the RSX has not been removed.
-
-
- .h1 OKDRIVES, or, Who's On Line?
-
- This is the third Advanced CP/M column, and by now some readers
- will have anticipated my penchant for turning to an actual
- application to illustrate the technical subject at hand. This
- example grew out of a conversation with Ben Grey, who was seeking
- a general-purpose method of limiting access to certain drives on
- a remote system.
-
- Many users have computers with a mix of actual and "missing"
- logical drives. For example, A: and B: might be floppies and M:
- a ram disk.
-
- How can an application program determine whether it should
- attempt to use a particular drive? Simply trying to select the
- drive with a BDOS call is playing Russian roulette. If the drive
- is invalid, or doesn't exist, the BDOS will complain with an
- error message and terminate the application with extreme
- prejudice -- an abrupt warm-boot.
-
- ZCPR34 allows the user to specify a vector of valid drives,
- stored in the Z-System extended external environment at offset
- 34h. The ZCPR34 command processor tests this vector before
- attempting to log in a drive, and the same test can be made by an
- application once it determines that it is indeed running in a
- system with an extended external environment. The extensions
- are listed in my previous Advanced CP/M column.
-
- Frequently, however, the programmer wants his application to run
- smoothly on other CP/M systems that aren't (yet) running ZCPR34.
- And users occasionally need a method of taking an erratic drive
- "off line" for maintenance. Is there a general, portable method
- of (1) determining which drives are currently available? and (2)
- making individual drives inaccessible?
-
- OKDRIVES in Figure 5 is one method of dealing with this
- challenge. It is a small RSX that maintains a vector of valid
- drives and monitors all BIOS disk-select calls. If a drive is
- selected that is not in the vector, it returns a select error.
- Otherwise, it allows the BIOS to select the drive. In order to
- set and change the list of valid drives, the RSX adds one BDOS
- function to the system. That function call serves the dual
- purpose of setting the valid (and invalid) drives, and reporting
- what the current setting is.
-
- .h2 Structure of the RSX
-
- OKDRIVES is written to be assembled into a ZRL (Z-ReLocatable)
- file and loaded with JetLDR, which I also described in the
- previous column.
-
- The RSX is made up of the standard RSX header, custom code to perform
- the operations on the valid-drives vector, and an initialization
- section. I have written the RSX in a quasi-modular fashion, so that
- almost all of the tedious code to install and remove the RSX can be
- copied in a few blocks and reused in any other RSX.
-
- This RSX uses one extended BDOS function number -- 241. When the
- RSX has been installed and the BDOS is called with this function
- number, it will set a new vector of valid drives (if DE is
- non-zero), or report the current vector (when DE is zero). For
- example, to make drives A:, B: and D: valid, call the BDOS with C
- = 241, DE = 11 = 1011b.
-
- The RSX must have a module name of the form "RSX...", so that
- JetLDR can identify it. Following that, the standard header
- begins the code segment at label "rsx:".
-
- Next come the unique data and code for this RSX, beginning with
- its nul-terminated ascii name and the vector of valid drives.
- The static value of the vector is assembled with all drives
- enabled.
-
- The action begins at "rsxstart". Every BDOS call is intercepted here
- and tested for this RSX's function number. Most of the time, it will
- be some other function, and so control jumps to "next" -- and then on
- to either an RSX immediately above this one, or the BDOS. However,
- when the function number is for this RSX, the routine proceeds to test
- DE for zero and either load the valid-drives vector into HL, or set
- the vector to the value in DE. In either case, the RSX returns to the
- caller.
-
- That's all there is to the added BDOS function. But to make it work,
- the RSX must also intercept the BIOS select-disk routine, at
- "rsxseldsk". The code checks the requested drive number against the
- valid-drives vector, using a simple loop that does a 16-bit left shift
- into the carry flag. If the drive is not active, it returns the BIOS
- select error (HL = 0). Otherwise, it continues to the BIOS
- select-disk routine.
-
- .h2 Housekeeping Code
-
- These two routines -- "rsxstart" and "rsxseldsk" -- do all of the
- work; the rest is necessary housekeeping. The initialization
- code is needed only to verify conditions and set up the RSX, so
- it is assembled, beginning at label "init" in the _INIT_ named-common
- address space. JetLDR will relocate this code into a working buffer
- in low memory and execute it there; it takes up no space in the
- resident system extension. JetLDR relocates the code segment of the
- RSX, allocating space for it immediately below the command processor
- or the lowest RSX already in memory.
-
- Initialization involves verifying that the RSX can be correctly
- loaded, linking the rsx into both the warm-boot and BDOS call
- chains, and installing any additional BIOS intercepts needed for
- this particular RSX.
-
- The "initlp" code checks each RSX in memory, beginning with the
- lowest, to see if an RSX with the same name is already in memory.
- If so, it (indirectly) calls the routine "custom_twin". This routine
- can accept, or reject a duplicate RSX; for OKDRIVES a duplicate RSX
- would be an error.
-
- Provided no duplicate RSX has caused termination, the search eventually
- reaches the end of the RSX warmboot chain. If there is no RSX currently
- in memory, then the "rsxnext" address is set to the ccp entry.
- However, if one or more RSXs are already resident, the address is
- set to the warmboot address in the header of the currently lowest RSX.
-
- Next, the RSX's warmboot routine is linked into the BIOS's
- warmboot chain, and the RSX's BDOS entry is linked into the
- chain that begins on page-0 with the jump instruction at 0005.
-
- The final initialization step is to call "custom_init". For this
- particular RSX, that code links the select-disk intercept
- routine into the BIOS jump table.
-
- Now, look back at the "rsxremove" routine. It is in the code
- segment, because the remove function must be resident within the
- RSX. It first calls "custom_remove", to take care of unlinking
- the BIOS select-disk intercept. Then it unlinks the warmboot
- intercept and exits with the carry flag set to signal successful
- removal of the RSX.
-
- There is one more step to unlinking the RSX. When the next
- warm boot occurs, it will be processed by the RSX immediately
- above this one, or, if there is none, by the BIOS. In either
- case, that routine will set a new "protect" address on page-zero at
- 0006. Of course, this RSX must have an essentially similar
- routine; it is at "rsxwb". It first calls "fix0001", a
- precaution that ensures the correctness of the pointer at 0001 to
- the actual BIOS jump vector.
-
- Next, it determines whether this RSX is, in fact, the lowest RSX
- in memory; it does this by checking the BIOS jump's address
- against the RSX address. Only if it is indeed the lowest does it
- set the protect address on page-zero. Finally, it jumps to the next
- higher RSX, taking care to first load the current drive/user byte
- into C, in case the next "rsx" is in fact the command processor.
-
- The details of managing the two linked lists -- one pointing
- upward to higher RSXs, the other pointing initially downward from
- the BIOS and then to successively higher RSX's -- @I{are}
- tedious, but necessary. But now, with JetLDR, most of the
- work can be avoided, and only the particular, custom elements
- of an RSX need to be specially coded.
-
- .h1 Extending System Extensions
-
- As the wag said, "I like standards, because there are so many to
- choose from!" The Plu*Perfect Systems RSX header is just one way
- to add resident extensions to CP/M 2.2. But it is well tested,
- provides for BIOS as well as BDOS system extensions, and takes
- care to be compatible with other RSXs.
-
- I have used these RSX structures in BackGrounder ii -- as well as
- its spooler, printer redefinition module, and secure memory
- allocator -- in DateStamper, and in DosDisk, achieving a high
- degree of compatibility between them. More recently, Carson
- Wilson, Joe Wright and others have adopted the Plu*Perfect
- Systems header structure to add such extensions as quad-density
- disk drivers to a BIOS and resident conditional-execution
- processing (IF.COM) to ZCPR34.
-
- This experience shows that a uniform RSX header can, indeed,
- allow diverse programs and applications to work together.
- Several older programs, including versions of BYE for remote
- operation of a CP/M system, and ZEX for in-memory submit
- processing could be revised to use this interface, so that
- other RSXs could be run while those extensions are active.
-
- In a CP/M 2.2 system that includes additional memory, it is
- possible to make system extensions "resident" without subtracting
- from scarce memory for applications. Malcom Kemp has pioneered this
- approach, called Banked System Extensions, by defining a similar BSX
- header and providing loading/removal service in the XBIOS system
- for the SB180 and SB180/FX computers. I developed both a
- banked-memory DateStamper and a DosDisk for this system. In fact,
- I am completing this column on an XBIOS system with banked
- DateStamping and a DOS disk in drive C:, with no loss of TPA!
-
- The next time you wish your computer had some missing capability,
- consider whether it might be added as an RSX. Those features may
- already be available in products such as DosDisk, BackGrounder ii, or
- Z3PLUS. If not, you may be able to code the routines yourself and bring
- your system to new levels of performance and versatility.
-
-
-
-
- Fig. 1 CONVENTIONAL MEMORY MAP
-
- 0000: jp bios+3 --->----------------------->----------------------------*
- 0005: jp bdos+6 --->* ccp+3: *-> bios+3: jp bioswb
- * --------------------->---------------> bdos+6: ...
- bioswb: ...
- jp ccp+3
- 0100: tpa, to bdos+5
-
-
- Fig 3. MEMORY MAP WITH ONE RSX
-
- *-->------------------------>----------------------->---*
- 0000: jp bios+3-* *-----------------<-----------------------<---+------------------<---*
- 0005: jp rsx1 ----------+> rsx1: jp entry1 *-> ccp+3: *--> bios+3: jp rsx1+3-*
- *--> +3: jp rsx1wb / *-> bdos+6:
- 0100: tpa, to rsx1-1 ... / /
- +F: jp ccp+3-* /
- next1: jp bdos+6 ---------*
- rsx1wb: ...
- jp rsx+F
- entry1: ...
- jp next1
-
-
-
-
-
- Fig. 4 MEMORY MAP WITH TWO RSXs
-
- *-->------------------------->---------------------------------------->---*
- 0000: jp bios+3 * *------------------<----------------------------------------<---+------------------<---*
- 0005: jp rsx2 ----------+> rsx2: jp entry2 rsx1: jp entry1 *-> ccp+3: *--> bios+3: jp rsx2+3-*
- *--> +3: jp rsx2wb *->+3: jp rsx1wb / *-> bdos+6:
- 0100: tpa, to rsx2-1 ... / ... / /
- +F: jp rsx1+3--* +F: jp ccp+3-* /
- next2: jp rsx1 next1: jp bdos+6 ---------*
- rsx2wb: ... rsx1wb: ...
- jp rsx2+F jp rsx1+F
- entry2: ... entry1: ...
- jp next2 jp next1
-
-
-
-
- Here, the labels "ccp", "bios", and "bdos" refer to the base address of the corresponding CP/M operating system
- segment. The entry to the bdos is at bdos+6; don't confuse it with the common equate for the page-zero
- vector: "bdos equ 0005".
-
-
- Figure 5
-
- title okdrives.asm 6/25/88 (c) 1988 Bridger Mitchell
- ;
- ;
- ; This rsx sets the vector of valid drives allowed by the bios.
- ; If called with de == 0, it returns the current valid-drives vector.
- ;
- ; usage to set valid drives:
- ;
- ; ld c,DRIVEFN
- ; ld de,<vector> ; bit 0 = A:, ..., bit 15 = P:
- ; call 5
- ;
- ; usage to determine currently-valid drives:
- ;
- ; ld c,DRIVEFN
- ; ld de,0000
- ; call 5
- ;
- ;
- ; We need an extended bdos function number.
- ;
- DRIVEFN equ 241 ; 0F1h
- ABORT equ 0ffh
- ;
- ;
- ; Name the REL image with "RSX" plus 0-3 characters of identification.
- ; In this case, we've used the rsx's bdos function number (241).
-
- name RSX241 ;"RSX" required
-
- ;
- ; All of the code within the bracketed regions is the same for any rsx
- ; loaded by JetLDR. It can be copied intact when creating a different rsx.
-
- ;
- ; *---------- Plu*Perfect Systems RSX Extended Header----------------*
- ;/ \
- ;
- ; The rsx code goes in the CSEG (code segment).
- ;
- CSEG
-
- rsx: jp rsxstart ; 00
- jp rsxwb ; +03
- jp rsxremove ; +06
- rsxwba: dw $-$ ; +09
- rsxprot:dw rsx ; +0B
- dw rsxname ; +0D
- rsxnext:jp $-$ ; -> next wb or ccp entry ; +0F
- ;
- next: jp $-$ ; -> next rsx or bdos ; +12
- nextwb: dw $-$ ; +15
- ;\ /
- ; *-----------------------------------------------------------------*
- ;
- ;
- ; The custom code for this rsx begins here.
- ;
- ;
- rsxname:db 'OKDRIVES',0 ; nul-terminated name of rsx.
- ;
- vector: dw 1111111111111111b ; <-- set bits for valid drives
- ; PONMLKJIHGFEDCBA ; <-- must be terminated by 'B' char.
- ;
- ;
- ; This RSX's bdos function.
- ;
- ; enter: c = DRIVEFN
- ; de == 0 to get current ok-drives vector
- ; de != 0 to set the current vector to de
- ; return:
- ; hl = vector of ok drives
- ;
- rsxstart:
- ld a,c
- cp DRIVEFN ; if not our function
- jr nz,next ; .. on to the next rsx/bdos
- ld a,e ; set vector?
- or d
- jr nz,set ; ..yes
- get: ld hl,(vector) ; no, return the drive vector in hl
- ld a,l ; return a != 0
- ret
- ;
- set: ex de,hl
- set 0,l ; ensure drive A: always valid
- ld (vector),hl ; save the new drive vector
- ld a,l ; and return it in hl
- ret
-
- ;
- ; The bios seldsk intercept
- ;
- ; enter: c = requested drive
- ; exit: hl == 0 if drive not allowed
- ; else continue to bios seldsk
- ;
- rsxseldsk:
- ld hl,(vector) ; shift ok-drives vector left
- ld a,16
- sub c
- rsxs1: add hl,hl
- dec a
- jr nz,rsxs1
- ld hl,0000 ; prime error return
- ret nc ; NC if bit wasn't set
- jseldsk:jp $-$ ; jmp to bios seldsk routine
- ;
- ;
- ; Restore this rsx's particular patches.
- ;
- custom_remove:
- ld hl,(jseldsk+1) ; restore bseldsk address
- ld (bios+1ch),hl ; to bios jmp vector
- ret
-
- ;
- ; *---------------- Standard RSX Code -----------------------------*
- ;/ \
- ;
- ; The warm-boot intercept.
- ;
- rsxwb: .new
- call fix0001 ; ensure correct page 0
- ld hl,(bios+4) ; does bios wb addr
- ld de,rsx+3 ; point at us?
- or a
- sbc hl,de
- jr nz,rsxwb1 ; no, we're not the bottom rsx
- ld hl,(rsxprot) ; we are, set our protect address
- ld (0006),hl
- rsxwb1: ld bc,(0004h) ; get c = logged du for ccp
- jp rsxnext ; in case we're top rsx
- ;
- ;
- ; The removal routine.
- ;
- rsxremove:
- call custom_remove ; do extra restoration for this rsx
- ;
- ld hl,(nextwb) ; get saved original warmboot addr
- ld (bios+4),hl ; and restore it to bios jmp vector
- ;
- ; When the caller terminates to a warmboot,
- ; the next module (or bios, if none), will correct 0006.
- ;
- ; Set CY flag to inform the removal tool that this routine
- ; has indeed taken action. (Some RSX's are not self-removing).
- ;
- fix0001:ld hl,(rsxwba) ; restore (0001) in case an errant
- ld (0001h),hl ; application has tampered with it
- scf ; set CY to signal success
- ret
- ;
- ;
- ; Before loading an RSX, JetLDR will first check for protected memory.
- ; If it detects that memory is protected by a non-RSX header (e.g. a debugger)
- ; it will cancel the load. Otherwise, JetLDR will call any
- ; code in the _INIT_ named common, after the rsx module has been
- ; loaded and relocated. This code will be located in non-protected
- ; memory, and takes no space in the RSX.
- ;
- ; Return parameter: A = 0 indicates a good installation
- ; A = ABORT = 0FFh = not installed
- ;
- common /_INIT_/
- ;
- ; Install the rsx. This code is standard for all rsx's,
- ; except for:
- ; custom_init
- ; custom_twin
- ;
- init: ld hl,(0006) ; hl = possible rsx, or bdos
- ld c,0 ; initialize count of rsx's
- ;
- initlp: push hl ; stack (possible) rsx base address
- ld de,09 ; if candidate is an rsx
- add hl,de ; ..the wbaddr will be here
- ld e,(hl) ; get address
- inc hl
- ld d,(hl)
- ld hl,(0001) ; and compare
- or a
- sbc hl,de
- pop hl
- jr nz,inittop ; warmboot addr not there, stop looking
- ;
- ; we have an rsx in memory, is it our twin?
- ;
- inc c ; count an rsx found
- push hl
- call ckname
- pop hl
- jr z,twin
- ;
- ld de,0Fh+1 ; that rsx was't a twin, check for more
- add hl,de ; get addr of next rsx's wboot jmp
- ld a,(hl)
- inc hl
- ld h,(hl)
- ld l,a
- dec hl ; back up to head of that next rsx
- dec hl
- dec hl
- jr initlp ; now check that rsx
- ;
- ; we're at the top of the (possibly empty) rsx chain
- ;
- inittop:
- inc c ; any rsx's found?
- dec c
- ld hl,ccp+3 ; prepare to use ccp entry address
- jr z,setnext ; ..no
- ;
- ld hl,(0006) ; yes, use bottom rsx's address
- ;
- setnext:
- ld (rsxnext+1),hl ; save the next addr
- ; in the rsx chain to bdos/ccp
- ;
- ; install the rsx into the running system
- ;
- ld hl,(bios+4) ; save the bios's wb addr
- ld (nextwb),hl ; in the header
-
- ld hl,rsx+3 ; point the bios wb jump
- ld (bios+4),hl ; at the rsx wb vector
-
- ld hl,bios+3 ; store wb addr
- ld (rsx+09),hl ; in rsx header word
-
- ld hl,(0006) ; get addr of next rsx or bdos
- ld (next+1),hl ; and install it
-
- ld hl,rsx ; finally, protect the rsx
- ld (0006),hl
- ;
- call custom_init ; take care of extras
- ret
- ;
- ckname: ld de,0dh ; offset to candidate rsx name pointer
- add hl,de
- ld a,(hl) ; get address
- inc hl
- ld h,(hl)
- ld l,a
- ld de,rsxname ; compare to our name
- ckname1:ld a,(de)
- cp (hl)
- ret nz
- inc (hl) ; candidate must be nul-terminated
- dec (hl)
- jr nz,ckname2
- or a ; ..at our same byte
- ret
- ckname2:inc hl
- inc de
- jr ckname1
-
- ;
- ; Handle the case of a previously-loaded copy of this RSX.
- ;
- twin: call custom_twin
- ret
- ;\ /
- ; *-----------------------------------------------------------------*
- ;
- ; Custom initialization code goes here.
- ;
- ;
- ; Do the particular patches for this RSX.
- ; Note: this code is in the _INIT_ segment.
-
- custom_init:
- ld hl,(bios+1ch) ; get bseldsk address
- ld (jseldsk+1),hl ; install it in rsx
- ;
- ld hl,rsxseldsk ; divert bios jump
- ld (bios+1ch),hl ; to the rsx
- ret
- ;
- ; This particular rsx should not be duplicated.
- ; A different rsx might wish to query the user here,
- ; print a warning, or whatever.
- ;
- custom_twin:
- ld a,ABORT
- ret
- ;
-
- ; Include identification info in the REL image.
- ; JetLDR will display the bytes up to the first NUL byte
- ; when the RSX is loaded.
- ;
- ;
- common /_ID_/
- ;
- db 'OKDRIVES: RSX prevents bios logins'
- db 13,10
- db 'Use BDOS function 241 (0F1h) to set de = drive vector',0
-
- ;
- ; Include whatever other named-commons are needed for this RSX.
- ; JetLDR will resolve these labels for us.
- ;
- common /_BIOS_/
- bios equ $
-
- common /_CCP_/
- ccp equ $
-
-
- end ;okdrives.asm
-
-
-