;RUN.ASM ; ;Charles E. Horn,PE ;Horn Engineering Associates ;Garland, TX ;28 April 1984 ;(No rights reserved) ; ;====================================================================== ;REVISION HISTORY ; ;06/11/85 Corrected error in computation of load address in ; code as noted. [CEH 06/11/85] ;====================================================================== ;RUN is a variation of the original RUNFILE program by Phil Cary. That ;program was designed for use on an RBBS and permits one to execute ;a selected program from a selected high user area, even though the ;file is not normally accessible to the user or caller. With this ;technique, the file can be executed without alteration of the wheel ;byte. ; ;RUN is somewhat different. RUNFILE loads the program loader just ;below the BDOS and over a portion of the CCP. This is no problem if ;the executed program (such as RBBS) exits with a warmboot, which ;reloads the CCP. However, some programs exit directly to CP/M without ;a warmboot (such as SD.COM). If it is allowed to do this when called ;from RUNFILE, the CCP will remain corrupt. RUN solves this problem ;by locating the program loader just below the CCP. ; ;RUN requires specification of a file name in the command line. This can ;be at least as secure as an 8-character password because the file in ;the high user area can be named anything and will not be accessible for ;view by directory if the BBS does not normally permit a user to access ;these high areas. This is the purpose of MAXUSR and MAXDRV in the ;various RBBS utilities, such as NZCPR. ; ;The command syntax is: A>RUN ; ;The program will also support the usual command line extensions if the ;target program normally extracts them from the CP/M default DMA area. ;For example, RUN SD $A would work. RUN SD B: $A would not work because ;the extra $A entry would clobber the CP/M secondary FCB. However, the ;variation RUN B:SD $A would cause the target file, SD in user 14, for ;example, to go to drive B: and display all files in all user areas. ;This program also offers an option to build the target file name into ;the program so that it will operate in the same mode as the RUNFILE ;program. In this case the command would simply be RUN. ; ; WRCON: EQU 2 ;Console output PRINTF: EQU 9 ;Print string BDOS: EQU 5 ;Bdos Call SELDRV: EQU 14 ;Select Default Drive OPEN: EQU 15 ;Open file READ: EQU 20 ;Read sequential SETDMA: EQU 26 ;Set DMA Address USER: EQU 32 ;Set user area RDS: EQU 13 ;Reset disk system ; CPMFCB: EQU 5CH ;CP/M primary FCB address CP2FCB: EQU 6CH ;CP/M secondary FCB address ; ;MISC EQUATES ; CR: EQU 0DH LF: EQU 0AH BLANK: EQU 20H ; ; ORG 100H ; ; JMP START ;Skip following data ; ; ;CHANGE THE FOLLOWING TO MEET YOUR REQUIREMENTS ; RETDRV: EQU 0 ;Exit to Drive A RETUSR: EQU 0 ;..and User 0 DEFDRV: EQU 'A'-'@' ;Drive containing the COM file you want DEFUSR: EQU 14 ;User area " " " " " ; RUNFIL: DB 'XFILE ' ;Filename you are CALLING ;Eight Chars-->> ^^^^^^^^<< ; ; ;NOTE: If it desired to build the target file name into this program ; without using the command line technique, change the code in ; the program as noted below. In this case, the user can not ; use RUN to execute any arbitrary file, but is stuck with the ; one that is built in above. ; ERRMSG: DB CR,LF DB 'You must be Kidding.....',CR,LF,'$' ;Print Error Message ; ;NOTE: The above message gives the user no idea of what RUN does. ; START: MVI C,8 ;Move the filename into fcb LXI H,CPMFCB+1 ;location of filename ; ;NOTE: Replace the operand "CPMFCB+1" in the above instruction with ; "RUNFIL" if the internal name at the label RUNFIL is going to ; be the target file. ; LXI D,FCB+1 ;destination CALL MOVE ;8 bytes ; MVI C,11 ;Clear the primary CP/M FCB LXI H,CPMFCB+1 ;Destination CALL BLANKS ;Put in blanks MVI C,11 ;Clear the secondary CP/M FCB LXI D,CP2FCB+1 ;Destination CALL BLANKS ;Put in blanks ; ; If you need to reset the default drive back to 'A' then ; include the next three lines of code. ; MVI C,SELDRV ;If necessary to locate BRUN, for example MVI E,00H ;Set Default Drive back to A CALL BDOS ;Set it ; MVI C,USER ;set up area of the target file MVI E,DEFUSR ;Desired user area CALL BDOS ;do it ; LHLD BDOS+1 ;get BDOS entry address [ver201 fixes] lxi b,-6-2048 ;offset to start of ccp dad b ;pointer to ccp in HL LXI B,-CODELN ;Subtract length of code to be moved DAD B ;To make room at top of TPA SHLD JMPR+1 ;Fill in jump address below PUSH H ;Save relocated loader address ; ; Code to cause loader to return to drive and user designated ; by RETDRV and RETUSR per EQUates at top, in case loaded program ; returns to CP/M without a warmboot. ; XCHG LXI H,SETRET-LOADER ;Distance between start of loader ;..and RET jump address DAD D ;Compute relocated address SHLD JMPRET+1 ;..and plant it in loader POP H ;Recover relocated loader address ; PUSH H ;Save code address for RET XCHG ;And use to calculate final location of FCB LXI H,FCB-LOADER ;Distance between start of loader and FCB DAD D ;Add address computed above SHLD FCBR+1 ;And put in LXI below for eventual file read PUSH H ;Save FCB destination address LXI H,LOADER ;Point to start of loader MVI C,CODELN ;Length of loader CALL MOVE ;Destination still in DE from above POP D ;Recover FCB address (saved as push H above) MVI C,OPEN ; CALL BDOS ;Open file INR A ; JZ ERROR ;signal if error LXI H,100H ;Point to start of TPA for set DMA below RET ;To address on stack which is start of loader ; LOADER: PUSH H ;DMA address at start of TPA XCHG ;Put in DE for DMA set MVI C,SETDMA CALL BDOS ;Set DMA address ; FCBR: LXI D,$-$ ;Address of moved FCB filled in earlier MVI C,READ ; CALL BDOS ;Read next record ORA A ;Check for end of file POP H JMPRET: JNZ $-$ ;EOF -> JMP to SETRET (address patched above) LXI D,128 ;....and bump DMA address DAD D ; JMPR: JMP $-$ ;jump to loader address filled in earlier ; ; If the loaded program exits without a warmboot, the following ; code will assure that we are not left in the user area that ; the loaded program normally resides. ; SETRET: MVI C,RDS ;Reset disk system and restore default DMA CALL BDOS MVI C,SELDRV ;Set default drive before return MVI E,RETDRV CALL BDOS MVI C,USER ;Set default user before return MVI E,RETUSR CALL BDOS JMP 100H ;Go run the program in memory ; FCB: DB DEFDRV ;drive code DS 8 ;room for filename DB 'COM' ;File type DB 0,0,0,0,0,0 ;Zero out remaining 24 bytes of FCB DB 0,0,0,0,0,0 DB 0,0,0,0,0,0 DB 0,0,0,0,0,0 ; CODELN: EQU $-LOADER ;computes size of loader code for move ; MOVE: ; C= # Bytes, HL= Source, DE= Destination ; MOV A,M STAX D INX H INX D DCR C JNZ MOVE RET ; BLANKS: ; C= # blanks, HL = Destination ; MVI A,BLANK MOV M,A INX H DCR C JNZ BLANKS RET ; ERROR: LXI D,ERRMSG CALL PRNMSG JMP 0 ; ;Write a string of characters to the CRT ; PRNMSG: MVI C,PRINTF CALL BDOS RET ; END ;