title RSXMAST outline RSX for CPM2.2 (85/11/13) ; ; Copyright (c) 1985 by C.B Falconer (203) 281-1438 ; 680 Hartford Tpk, Hamden, Ct 06517. All rights reserved. ; ; This program may be copied/used/modified etc, but it may NOT ; be sold without express written permission from C.B. Falconer ; ; This system was assembled with SLR's SLRMAC. M80 can also be ; used, but care must be taken with macro parameter names. ; rsxver equ 12; Version number to be filled in. <10 debug ; ; ==================== I M P O R T A N T ======================= ; AN ACTUAL SYSTEM IS CONSTRUCTED WITH INCLUDE FILES. THE FILES ; REQUIRED ARE: ; RSXCUST.INC, RSXINIT.INC, BYERSX.INC and RSXMAIN.INC ; in addition to this file and BDOS.DEF ; If your assembler cannot handle include files, then you must ; make the insertions manually with your editor. ; ============================================================== ; false equ 0 true equ NOT false no equ false; synonyms for convenience yes equ true ; ; The system implements a generalized CPM 2.2 RSX system. The ; RSX is installed by running the system, and removed by running ; it again. The program uses 8080 opcodes only in the RSX ; section, although the included files required may contain ; Z80 code. This should ease porting to 8080 only systems. ; ; NOTE: This source file requires SLRs SLRMAC or M80 for assembly, ; and RELOCCP.SYS for run-time load and relocation. ; ; Revisions: (in LIFO order) ; 1.2 85/11/13. FINDOS/CHKSYS are now application init callable ; procs. BDOSLGH, BDOSARG, CCPLGH are now equates, to allow ; for non-CPM systems. Increased min. stack to 48 bytes. ; Added boothk. Some name changes for M80 compatibility. ; Modified the CCP re-entry mechanism for ZCPR compatibility. ; 1.1 85/11/09. Allowances for range of RSX's. Drivers may now ; be installed after a non-driver RSX has been mounted. These ; changes allow implementation of a BYE replacement system. ; Separate RSXCUST.INC file. ; 1.0 85/09/23. Initial release. ; 0.3 85/01/27. First working disk drive. ; 0.2 85/01/24. Provisions for BDOS driver installation, ; checks etc. ; 0.1 85/01/05. Inactive causes reversion to original BDOS call in ; case this was a substitute for it. debug allows testing. ; Keep track of current DMA address. ; 0.0 Original version, by C.B. Falconer (85/Jan/04). ; ; This system is the outline of a generalized RSX system, with ; initialization and termination code. It is expected to ; respond to BDOS calls with the value @RSX (normally larger ; than any usual CPM call value), and receive an argument of ; some form in the (de) register. Two special values of (de) ; are reserved (0 and -1). In usual operation the (de) value ; is normally a pointer, which cannot take on either of these ; special values. For character output RSXs (e.g. list output ; replacement) this prevents sending a nul or a rubout with all ; bits set. Remember this is a full word parameter. ; ; 0 implies a residence inquiry, and should return a zero ; value if the system is not resident, (as will CPM 2.2 for any ; invalid calls), and non-zero if resident and activated. This ; allows the initialization code to detect that the system is ; already mounted, and avoid multiple loading. Similarly ; application programs can check that the RSX is available. A ; zero value returned in (a) signifies the RSX is not available. ; ; The second special value is 0ffffh (i.e. -1), which ; signals the system to become inactive, so that the next ; warm boot will remove it entirely. ; ; A third special argument is 0001h. This is optional, and is ; used to return a data address. For DSKDRIVE in particular ; this is used to return a pointer to the memory resident ; drive configuration table (whose structure thus should not ; be altered in order to maintain compatibility with auxiliary ; software). (DSKDRIVE actually returns this pointer for all ; DE arguments other than 0 or -1, but this should not be ; counted on.) ; ; This file implements an example RSX, and indicates where the ; code should be installed, and the calling conventions for other ; RSXs. See the label "chkparms" to allow initialization ; parameters, and the label "rsx" to install the actual code. ; Note especially than any code located ahead of the label ; "@keep" is available only during initialization. However ; the initialization section can call routines in the retained ; portion. The "bthook" connector allows re-initialization on ; each "warm boot" or disk reset. "boothk" is different, and is ; called on each warm boot, but not at disk reset. ; ; The system, when combined with "RELOCCP", loads itself just ; below the CCP, and keeps the CCP resident. (See RELOCCP.OVR, ; which is an assembly time option for RELOCCP, for a method ; of minimizing memory wastage for multiple RSX's). If RSX.COM ; is executed while resident, it attempts to remove itself and ; reclaim memory. If other systems have been loaded below RSX ; it will not be able to reclaim memory until those systems ; (probably further RSX's) have been removed. The customizable ; routine "killhk" allows manipulation of a resident RSX. ; ; To create a custom RSX modify the files RSXINIT, RSXMAIN, RSXCUST ; and possibly BYERSX.xxx, rename them with the extensions ".INC" ; and .DEF as required, and re-assemble RSXMAST. Give the resultant ; relocatable file the appropriate name, and create the MYRSX.COM ; with RELOCCP (See RELOCCP documentation). When executed you now ; have available a new BDOS function, which executes whatever you put ; in. IF YOUR BDOS/CCP is non-standard see BDOS.DEF file. ; ; Acknowledgement: Some of the mechanisms used in this system ; have been taken from various public domain systems, especially ; those by Gary Novasielski and Bruce Ratoff. ; ; This system was created to ease installation of two operations: ; a generalized foreign disc driver, and a "CHAIN" mechanism ; whereby programs can load and execute arbitrary commands. The ; pre-existing KEYS program can probably be converted to execute ; under this system, as can UNSPOOL40 (enhancement of Br. Ratoffs ; UNSPOL30). ; ; While some code economies could easily be made for any particular ; application, I feel that using one standard environment is much ; more important. Thus the provisions for bios driver modification ; have been installed, with the complete check/restore system. The ; disadvantage remains that custom code must be included in this ; source and assembled, rather than standing by itself. ; ; Definitions of BDOS functions. include BDOS.DEF ; ; Put any customization equates here include RSXCUST.DEF ; ; Macro Definitions ; ; Force location counter zero modulo val ; with respect to "wrto" by filling with "fill" bytes align macro val,wrto,fill local here here equ $ - wrto if (here+val)/val*val-here-val if nul fill ds (here+val)/val*val-here else;; SLRMAC specific coding. ds (here+val)/val*val-here,fill endif endif endm ; ; ************************************************************ ; * This is my standard page relocatable system. See RELOCCP * ; ************************************************************ ; cseg; system requires org 0 and org 100h modules ; You may replace this with two "orgs" in two ; different assemblies to get the overall system ; for RELOCCP to prepare and relocate at run-time. ; ; This defines the first location of the relocated image. ; The portion from here to "@keep" is not retained after ; initialization. The data here is used for relocation @base: jmp intro; Following is std relocation data @size: dw segsiz; size of segment to relocate @memsz: db pages; total memory use in pages ; ; Patch/alter this value to non-zero if the RSX may only be ; loaded in a virgin system. This should be done if the system ; installs bios modifications that the BDOS can see. chkflg: db driver; Set non-zero for virgin system only ; ; This code gets everything started intro: lhld bdos+1; First so other calls work shld gobdos+1; Save the BDOS entry point lxi d,signon call tstr call findos jnz badsys; Invalid system configuration ; " " ; Set up the connector to return to CCP on boots lxi d,-ccplgh-7+3; Form pointer to CCP re-entry dad d shld ccpret+1; and save for future use ; " " ; Do application specific parameter checking call chkparms; Do any parameter checking needed jc exeunt; ..with help message on carry=fault ; " " ; Check for initial load, or re-entry lxi d,0 mvi a,@RSX; call DOS; enquire whether loaded ora a; If loaded, then jnz intro1; bring it down call rsxset; else initialize. ; " " ; Now do the user customizable initialization call init; Customized portion jmp bootrq; ..bootrq, which sets connectors ; ; already loaded, bring it down intro1: call killhk; for systems that modify running RSX lxi d,-1 mvi a,@RSX call DOS; tell it to come down on next boot jmp boot; which should kill it ; ; Setup the system. First, check environment to see if ; BIOS vectors are accessible and reasonable. ; a,f,c,d,e,h,l rsxset: lhld boot+1 shld boot0+1; Save the BOOT vector if nodrive inx h mov e,m; save warm boot connector inx h mov d,m xchg shld bsave lxi h,bootrq; and reconnect to our system xchg mov m,d dcx h mov m,e ret else; drivers to be available lda boot; Location BOOT should cpi 0c3h; have a JMP instruction jnz vecterr lhld boot+1; Location one points to xchg; the table of bios jumps lhld bdos+1; This should point to BDOS lda chkflg sta kill+1; Save for use at exit time. Unclean ora a; but want it user patchable (1 place) if multidrv call findos else; NOT multidrv cnz chksys; Hook, prevent loading invalid system endif xchg; bios pointer to hl mvi c,nvects; preserve z flag in here lxi d,biosv push d; by default init our vector jnz vecterr; after push, to correct stack lxi d,bsave; which we move into xchg; the code. ; " " ; Now copy the original vectors into our tables, while verifying ; that they are reasonable (i.e. start with CALL or JMP). The ; CALLs allow for warmboots that call a ROM routine, with the ; stack identifying the actual system location. rsxset1: mvi a,nvects-n22vec cmp c jnc rsxset2; stop checking after 2.2 types ldax d xra m; another JMP? ani 0f1h; allow Call or Jmp jnz vecterr rsxset2: ldax d mov m,a; bsave[n,0] inx d inx h xthl inx h ldax d mov m,a; biosv[n,1] inx h xthl mov m,a; bsave[n,1] inx d inx h ldax d mov m,a; bsave[n,2] xthl mov m,a; biosv[n,2] inx h; biosv[n+1,0] xthl inx h; biosv[n+1,0] inx d dcr c; n := n+1; jnz rsxset1 pop h; purge 2nd transfer address ; This was satisfactory, now if "driver" is TRUE, patch the ; BIOS drivers to point to the new copy (only the std CPM 2.2 ; entries) so that new entries can be made locally. The final ; exit mechanism will restore everything. lda chkflg ora a rz; no bios patches needed ; " " ; Patch the original bios drivers, which have been checked ; for validity and a reasonable location, to point to the ; new bios table. Only called when "driver" is TRUE. DO NOT ; PATCH the original warm/cold boot entries. bpatch: lhld boot+1 inx h inx h lxi d,biosv mvi c,n22vec-1; count of 2.2 bios entries only bpat1: inx h inx d inx d inx d inx h mov m,e inx h mov m,d dcr c jnz bpat1; more ret ; ; Check system is in usable state. (de) holds the bios pointer, ; and (hl) holds the bdos pointer. If the values are not ; reasonable, return non-zero flag. If successful returns hl ; a pointer to location 7 on the BDOS entry page (CPM 2.2). ; Modifications here can allow installation of multiple ; drivers. This is safer as it stands. ; a,f,h,l chksys: call findos; worked before, must work now mov a,e cpi 3 rnz; boot pointer should end in 3 mov a,d sub h sui BDOSLGH; Z flag if correct page, ret; else non-virgin BIOS pointer ; vecterr: pop d; remove the extra pointer endif; NOT nodrive ; " " ; Exit with invalid system message badsys: lxi d,vcterrmsg ; " " ; exit with message de^ exeunt: call tstr jmp boot; try re-booting. ; vcterrmsg: db CR,LF,'Invalid system$' ; ; Find the actual location of BDOS by following the trail from ; the pointer in hl. Return z flag and hl = pointer to location ; 7 of the base BDOS page if found, else nz flag. ; For this to function connectors must be of the form used in ; intercept below. Scan for label "intercept:" ; Note that ENTRY IS IN THE MIDDLE, not the next instruction ; a,f,h,l fndos1: cpi 03eh; (mvi a). Check for intercept rnz; not a connector, not found inx h; ignore operand for MVI A,-- inx h mov a,m cpi 0b9h; (cmp c) rnz; not a connector inx h mov a,m ani 0e7h; may be an intercept cpi 0c2h; jz, jnz, jc, jnc are acceptable rnz; not a connector inx h fndos2: mov a,m fndos3: inx h; load the JMP operand mov h,m mov l,a; and continue up the chain ; " " ; Main entry point here findos: mov a,m; find the actual BDOS. *** ENTRY <<< cpi 0c3h; (jmp). Code for CPM 2.2 only jnz fndos1; Not the real thing fndos4: inx h mov a,m cpi BDOSARG MOD 256; known value in CPM 2.2 jnz fndos3; not end, continue up the chain inx h mov a,m adi BDOSARG / 256; allow for non-CPM systems cmp h; at end JMP is on same page (CPM2.2) dcx h; point back to start of arg jnz fndos2; else continue up the chain ret; (z) FOUND it. ; ; ============================================================= ; **** Custom portion of initialization code here **** ; ============================================================= ; ; The following code is used on initialization and then discarded. ; See "RSXINIT.xxx" for details. It may call on all routines in ; the system. include RSXINIT.INC ; ; ------------------ End custom initialization area ---------------- ; align 256,@base,0; ensure page aligned ; ; ******************************************************* ; * The code from here up is retained in memory after * ; * initialization. It may be used by the initializer * ; * This code MUST start on a page boundary. * ; ******************************************************* @keep: ; ; During operation, this location will point to intercept and will ; be jumped to by BDOS calls from location 5. This organization ; depends on the fact that BDOS calls the bios directly (ignoring ; the pointer at location 1), except when needing a warm boot ; (e.g. after a disk error), when it uses the pointer at 1. ; ; This must be at the lowest location in the protected code segment. bdosv: jmp intercept; the bdos link ; ; This will normally contain no code, but can be used to create a ; system compatible with the existing BYE programs. include BYERSX.INC ; if nodrive bsave: dw $-$; save original warm boot connector else; NOT nodrive ; This area replaces the bios jump table, allowing intercepts. ; Any intercepts (usually console commands) are set up by the ; initializing code, which is discarded after execution. See ; comments on "nvects" above. Normally these bios vectors will be ; accessed only by executing programs, and not by the BDOS unit. ; To insert drivers in the bios it is necessary to save the original ; bios pointers (for restoration when the RSX is removed) and ; install jumps to this revised table in the original table. Note ; that the original table may contain a CALL for the warmboot vector, ; so that ROM based code can tell the CPM system size when called. biosv: REPT nvects;; biosv is the new warmboot connector jmp $-$ endm ; ; This table has two purposes - it allows connection to the original ; bios vectors destinations, and it saves all the values for ; restoration upon RSX exit. Thus the custom initialization section ; can patch the real bios table to install new disk drivers, etc. ; Note that this can only work when this is the FIRST rsx installed. ; Further ones can only intercept user bios calls and BDOS calls. ; Provided that the bios table in effect on entry to this system ; does not hold any data areas, the restoration on RSX removal will ; be harmless. bsave: REPT nvects;; bsave is the old warmboot connector jmp $-$ endm ; ; An example showing how to refer to the "old" entries needed. ; Index 0 here is warmboot, not cold. Similarly for "new" entries ;@home equ bsave+(7*3) ;@seldk equ bsave+(8*3) ;@setrk equ bsave+(9*3) ;@setsec equ bsave+(10*3) ;@setdma equ bsave+(11*3) ;@read equ bsave+(12*3) ;@write equ bsave+(13*3) ;@sectran equ bsave+(15*3) endif; NOT nodrive ; ; This routine intercepts all BDOS calls. ; Note that the initial code sequence allows applications to ; find the actual BDOS, if necessary. See SD88F6 for one use. intercept: mvi a,0ffh cmp c jc gobdos; Never taken, but SD trackable mvi a,@SYS; Get function cmp c jz sysreq; a reboot request mvi a,@DMA cmp c jz dodma mvi a,@LOG cmp c jz drvreset; reset this drive also mvi a,@RSX; defined for this system cmp c jz inrange mvi a,@RSXX; check for a special extra cmp c; intercept (e.g. setuser for jz inrange; BYE replacement) mvi a,@RSXY; check for another special extra cmp c; intercept (e.g. chain for jz inrange; BYE replacement) mvi a,@RSXLO-1 cmp c jnc gobdos; not in range mvi a,@RSXHI cmp c jc gobdos; not for us ; DO NOT optimize the above sequence. It is intended to be ; trackable by utilities to list the RSX's active. The series ; stops when the instruction is not "ld a,bytevalue". The ; initial "ld a,0ffh" will not be counted by tracking utilities ; and the checks on @SYS and @DMA are always expected. This ; organization allows a range of BDOS calls to be intercepted. ; " " inrange: lxi h,0; intercepting, switch stacks dad sp; because application may not lxi sp,lclstk; allow sufficient room push h; save entry stack pointer call doit; This is an extension call xchg; save return value pop h; restore entry stack sphl xchg; return its result mov a,l; copy result to (a) mov b,h; so (ba)=(hl), like BDOS ret; to the caller. ; ; Reset this drive also when application wants a drive reset ; Added to RSX system for disk drive application. ; This is also called on any warm-boots drvreset: push b call bthook; user customizable. pop b jmp gobdos ; ; Keep track of the current DMA address on general principles dodma: xchg shld dmadr; BDOS' view, not BIOS xchg ; " " ; Connects to the "real" BDOS routine gobdos: jmp $-$; Patched on entry ; ; The RSX (resident system extension). Argument is de as usual, ; and the extension specified is (c). Return value is put in hl. ; The stack is already set to the local stack, with the old ; stack pointer under the return from doit. ; a,f,b,c,d,e,h,l (allowed) doit: lhld active; set "enquiry" return value in (l) mov a,l ora a jz gobdos; inactive, use bdos call mov a,c cpi @RSX jnz rsx; not the master call, ignore parm mvi h,0 mov a,e; check for "loaded enquiry" ora d rz; signal active on enquiry mov a,e ana d inr a jnz rsx; not "pulldown" request. ; " " ; Inactivate the RSX. Next boot will try to recover the memory. kill: mvi a,$-$; patched with "chkflg" at init ora a cnz unpch; Remove any bios alterations xra a sta active; mark inactive mov l,a; return 0, pulldown accepted mov h,a; and we are now inactive. ret ; unpch: if nodrive ret else; NOT nodrive ; Remove any bios patches made on initialization, in case this is a ; driver being inactivated, and further RSX's are loaded beyond it. ; Thus a "kill" request will be logically executed, even though the ; memory is not reclaimed. However bios alterations via the pointer ; at 1 cannot be removed until a warm boot occurs (that pointer may ; be pointing to an RSX installed later). Thus the table at biosv ; must be updated to point to the original bios entries lxi d,bsave+3 lhld boot0+1 inx h inx h inx h; start at the constat entry push h lxi h,biosv+3 mvi c,n22vec-1; dont alter the warm boot entries unpch1: mov b,l; offset, depends on page alignment inx d; past opcode inx h; Ignore the opcode (dont change) mov m,b; biosv[n,1] --> oldbios[n,0] inx h xthl mov b,h; oldbios page inx h ldax d; lsb mov m,a; oldbios[n,1] inx d; ^msb inx h ldax d; msb mov m,a; oldbios[n,2] inx h; oldbios[n+1,0] xthl mov m,b; biosv[n,2] --> oldbios[n,0] inx h; biosv[n+1,0] inx d; next opcode dcr c; n := n+1 jnz unpch1 pop h ret endif; NOT nodrive ; ; Note that the following "boot" entries will never be reached if a ; further RSX has been installed, since that RSX will intercept the ; boot and do a direct return to the CCP (unless it is inactive, and ; removes itself, when a whole chain of RSX removals can be started) ; ; The application process has requested a warm-boot by invoking BDOS ; function 0. If the system is inactive remove it, otherwise return ; to the CCP via the stored (on initialization) CCP return value. sysreq: jmp bootrq; This allows separation if needed ; ; The application process has requested a reboot by jumping to ; location 0. If we are no longer active, we will honor the request ; by executing the address found in the BOOT vector at entry. ; Otherwise return to CCP without rebooting. bootrq: lxi sp,lclstk; set up a valid stack lda active ora a jz done; Not active, all done. Remove self call boothk; For applications to detect reboot mvi c,@LOG call drvreset; Reset drives, like any other reboot lxi d,actmsg; does. Calls bthook routine call tstr lxi d,altid; added message portion for call tstr; DSKDRIVE use ; " " ; Reset the system pointers to use this system. lxi h,bdosv shld bdos+1 if NOT nodrive; else leave connectors alone lxi h,bootrq shld biosv+1 lxi h,biosv shld boot+1 endif; NOT nodrive lxi h,tbuff shld dmadr; Will be set by CCP lda drvusr mov c,a; drive/user under which to re-enter ccpret: jmp $-$; Patched on startup ; ; Done with the system done: lxi d,donemsg; Message and jump to old boot addr. call tstr; as originally read from memory wd 1 if nodrive; Then we have to reset the wboot lhld bsave xchg lhld boot0+1; The original destination inx h mov m,e inx h mov m,d; restore it all endif boot0: jmp $-$; Reboot. Patched on initialization ; This value points to old bios table ; and the old reboot resets connectors ; ; ================================================================ ; Utility routine area ; ================================================================ ; ; Put string (de) to console, '$' terminated ; a,f tstr: mvi a,@MSG ; " " ; dos call (a) without disturbing registers. Does not use the RSX ; a,f DOS: push b push d push h mov c,a call gobdos pop h pop d pop b ret ; ; ================================================================== ; **** To keep as much as possible constant, put real RSX here **** ; ================================================================== ; ; The following allows for machine dependant code for i/o drivers ; the file may consist of comments only if not needed. include RSXIO.INC ; ; The following is the actual application code. It may only call on ; the following routines: TSTR, DOS, GOBDOS. (See above) include RSXMAIN.INC ; ; This organization allows RSXMAIN to assign storage contiguous ; to the local stack, but not past it. ds STKSZ; Local stack of at least STKSZ/2 words ; plus any remainder on page ds 2; allow for dmadr below, forced to page end ; align to next page boundary align 256,bdosv ; lclstk equ $-2; Reserve space for following dmadr equ lclstk; Keeps track of current value known to DOS. ; Note that this may not be that known to BIOS ;************************************************* ; End of segment to be relocated. pages equ ($-@base)/256; So loader knows memory needs. ; end