title Library Run Tool For TURBO CHN files - LRUNTP name ('LRUNTP') include LRUN.LIB ; This version bases upon LRUN20.ASM by Gary P. Novosielski, 1982 ; It now requires CP/M 3.x as well as a Z80 CPU ; Modified by W.Cirsovius ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Especially used for Turbo Pasxal Chain files. ; Default library is TURBO.LBR ; Each library must hold the Turbo Run Time Library. ; It must be named TURBORTL.COM. ; Each executable must be end with type .CHN ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++ $$PRG macro db 'LRUNTP' endm Version equ 21 ; 92-09-07 Added CP/M+ RSX capability ; From the original source: ;--------------------------NOTICE------------------------------ ; ; (!!!!! FROM ORIGINAL LRUN20.ASM !!!!!) ; ; (c) Copyright 1982 Gary P. Novosielski ; All rights reserved. ; ; The following features courtesy of Ron Fowler: ; 1) command line reparsing and repacking (this allows ; the former load-only program to become a load & run ; utility). ; 2) code necessary to actually execute the loaded file ; 3) the HELP facility (LRUN with no arguments) ; 4) modified error routines to avoid warm-boot delay ; (return to CCP directly instead) ; ; Permission to distribute this program in source or ; object form without prior written aproval is granted ; only under the following conditions. ; ; 1. No charge is imposed for the program. ; 2. Charges for incidental costs including ; but not limited to media, postage, tele- ; communications, and data storage do not ; exceed those costs actually incurred. ; 3. This Notice and any copright notices in ; the object code remain intact ; ; (signed) Gary P. Novosielski ; ;-------------------------------------------------------------- ; ; LRUN is intended to be used in conjunction with libraries ; created with LU.COM, a library utility based upon the ; groundwork laid by Michael Rubenstein, with some additional ; inspiration from Leor Zolman's CLIB librarian for .CRL files. ; ; The user can place the less frequently used command (.COM) ; files in a library to save space, and still be able to run ; them when required, by typing: ; LRUN . ; The name of the library can be specified, but the greatest ; utility will be achieved by placing all commands in one ; library called TURBO.LBR, or some locally defined name, ; and always letting LRUN use that name as the default. ; ;Syntax: ; LRUNTP [-] [] ; ;where: ; is the optional library name. In the ; distrubution version, this defaults to ; TURBO.LBR. If the user wishes to use a ; different name for the default, the 8-byte ; literal at DFLTNAM below may be changed to ; suit local requirements. The current drive ; is searched for the .LBR file, and if not ; found there, the A: drive is searched. ; **Note that the leading minus sign (not a part ; of the name) is required to indicate an ; override library name is being entered. ; ; is the name of the .CHN file in the library ; ; is the (possibly empty) set of parameters ; which are to be passed to , as in ; normal CP/M syntax. Notice that if the ; library name is defaulted, the syntax is ; simply: ; LRUN ; which is just the normal command line with ; LRUN prefixed to it. ; ;-------------------------------------------------------------- ; USER MODIFIABLE EQUATES ; ; Define a secondary search drive and user if .LBR is ; not found after initial search of current area: ; SSDRV equ 'A' ; Valid values are 'A' through 'P'. SSUSR equ 0 ; Valid values are 0 through 15. ; ;Default library may also be modified. See label DFLTNAM. ;-------------------------------------------------------------- ; jp intro ; signon: $$PRG db ' Ver ',Version/10+'0','.',Version mod 10+'0' db cr,lf db ' Copyright (c) 1982 Gary P. Novosielski ' db cr,lf db ' Modified for CP/M+ 1992 Werner Cirsovius ' db eot,eof ; ; Enter LRUNTP ; intro: sub a ; Verify Z80 CPU jp pe,BadCPU ; Print message if not ld sp,LStack ; Load local stack ld c,Ver call BDOS ; Test version of CP/M in use cp CPMv ; 3.0 or better? jp c,BadVer ; No, bitch and quit. ld de,signon ld c,Msg call BDOS ; Display signon ld c,RSX ld de,IniPar call BDOS ; Get RSX data. ld a,h ; Test RSX installed or l jp nz,NoRSX ; Nope call setup ; Initialize. ld c,RSX ld de,GoPar call BDOS ; Start and never come back jp NoFit ; Error if return here ; ; Move BC bytes from ^HL to ^DE ; BlkMov: ldir ; Real simple one on Z80 CPU ret ; ; Any one-shot initialization code goes here. ; setup: ld hl,NoLoad ld (TPA+1),hl ; Prevent reentry ld e,Query ld c,Usr ; Get current user call BDOS ld (EntUsr),a ; Save for later. call Repars ; Re-parse command line ld de,Member+.drv+.nam ld a,(de) ; Check member filetype cp ' ' ; If blank, ld hl,CHNlit ld bc,.ext call z,BlkMov ; default to CHN. ld de,LBRfil+.drv+.nam ld a,(de) ; Check library filetype cp ' ' ; If blank, ld hl,LBRlit ld bc,.ext call z,BlkMov ; default to LBR ld de,LBRfil+.drv ld a,(de) ; Check name cp ' ' ; If blank, ld hl,DfltNam ld bc,.nam call z,BlkMov ; use default name. DirOpn: ld de,LBRfil ; Open for directory read. ld c,Opn call BDOS inc a ; Was it found? jr nz,DirOk ; yes, ok ld hl,LBRfil ; No, test drive spec ld a,(hl) ; to see if it's or a ; explicit jp nz,NoDir ; It is explicit. Out of luck ld (hl),SSDRV-'@' ; Look on secondary drive, ld e,SSUSR ld c,Usr call BDOS ; in secondary user. jr DirOpn ; before giving up. DirOk: ld hl,Fishy push hl ; Set error routine ld de,TBUFF ld c,DMA call BDOS ld de,LBRfil ld c,FRd call BDOS ; Read the directory or a ret nz ; Empty file, Give up. ld hl,TBUFF ld a,(hl) or a ret nz ; Directory not active?? ld b,.nam+.ext ; Check for blanks ld a,' ' ValidLoop: inc hl cp (hl) ret nz djnz ValidLoop ld hl,(TBUFF+L.IDX); Index must be 0000 ld a,h or l ret nz pop hl ; Clean stack ld hl,RTLLBR ; Point to .COM file call LdLBRdata ; Load data ld (MIndex),bc ; Save it ld (MLenx),de ld hl,Member ; Point to .CHN file call LdLBRdata ; Load data ld (Index),bc ; Save it ld (Lenx),de ld hl,(MLenx) add hl,de ; Calculate entire length ld (CLenx),hl call PackUp ; Repack command line arguments ld e,cr ld c,Con call BDOS ; do only (look like CCP) ret ; ; Load data from main RTL file or chain file ; LdLBRdata: ld (curFN),hl ; Save file pointer xor a ld (LBRfil+_EX),a ; Init extent ld (LBRfil+_CR),a ; Init current record ld de,LBRfil ; Re-open for directory read. ld c,Opn call BDOS ld de,LBRfil ld c,FRd call BDOS ; Read the directory ld hl,(TBUFF+L.LEN); Get directory size dec hl ; We already read one. push hl ; Save on stack jr FindMbrn ; Jump into loop FindMbrl: pop hl ; Read sector count from TOS ld a,h or l ; 0 ? jp z,NoMemb ; Member not found in library dec hl ; Count down push hl ; and put it back. ld de,LBRfil ld c,FRd call BDOS ; Get next directory sector or a jp nz,Fishy FindMbrn: ld hl,TBUFF ; Point to buffer. ld c,TBUFF / L.ENT ; Number of directory entries FindMbr1: call Compare ; Check if found yet. jr z,GetLoc ; Found member in .DIR dec c jr z,FindMbrl ld de,L.ENT ; No match, point to next one. add hl,de jr FindMbr1 ; ;The name was found now get index and length ; GetLoc: pop bc ; Clear stack garbage ex de,hl ; Pointer to sector address. ld c,(hl) ; Get First inc hl ld b,(hl) inc hl ld e,(hl) ; Get Size to DE inc hl ld d,(hl) ret ; ; REPARSE re-parses the fcbs from the command line, ; to allow the "-" character to prefix the library name ; Repars: ld de,Member call NitF ; first reinitialize both fcbs ld de,LBRfil call NitF ld hl,TBUFF ; store a null at the end of ld e,(hl) ; the command line (this is ld d,0 ; done by CP/M usually, except ex de,hl ; in the case of a full com- add hl,de ; mand line inc hl ld (hl),null ex de,hl ; tbuff pointer back in hl ScanBk: inc hl ; bump to next char position ld a,(hl) ; fetch next char or a ; reached a null? (no arguments) jr z,Help ; interpret as a call for help cp ' ' ; not null, skip blanks jr z,ScanBk cp '-' ; library name specifier? jr nz,NotLBR ; skip if not inc hl ; it is, skip over flag character ld de,LBRfil ; parse library name into FCB call GetFN NotLBR: ld de,Member ; now parse the command name call GetFN ld de,Hold+1 ; pnt to temp storage for rest of cmd line ld b,-1 ; init a counter ClSave: inc b ; bump up counter ld a,(hl) ; fetch a char ld (de),a ; move it to hold area inc hl ; bump pointers inc de or a ; test whether char was a terminator jr nz,ClSave ; continue moving line if not ld a,b ; it was, get count ld (Hold),a ; save it in hold area ret ; ; PACKUP retrieves the command line stored at ; HOLD and moves it back to tbuff, then reparses ; the default file control blocks so the command ; will never know it was run from a library ; PackUp: ld hl,Hold ; point to length byte of HOLD ld c,(hl) ; get length in BC ld b,0 inc bc ; bump up to because length byte doesn't inc bc ; include itself or null terminator ld de,TBUFF call BlkMov ; moving everybody to Tbuff ld hl,TBUFF+1 ; point to the command tail ld de,FCB call GetFN ; first parse out tfcb1 ld de,FCB+_DIR call GetFN ; then tfcb2 ret ; ; Here when HELP is requested (indicated ; by LRUN with no arguments) ; Help: ld de,HlpMsg ld c,Msg call BDOS ; Give help jp BOOT ; Exit ; ; the HELP message ; HlpMsg: db cr,lf db 'TURBO Pascal Chain File Executor' db cr,lf,lf db 'Correct syntax is:' db cr,lf,lf db tab $$PRG db ' [-] ' db cr,lf,lf db 'Where is the optional library name' db cr,lf db '(Note the preceding "-". ) If omitted,' db cr,lf db 'the default command library is used.' db lf,cr,lf db ' is the name and parameters' db cr,lf db 'of the command being run from the library,' db cr,lf db 'just as if a separate .COM file were being run.' NewLin: db cr,lf,eot ; ; Test status, name and type of ; a directory entry. ; Compare: push hl ld b,.drv+.nam+.ext ex de,hl ld hl,(curFN) ; Point to current file Compar1: ld a,(de) cp (hl) ; Compare jr nz,CompExit ; No match inc de inc hl djnz Compar1 CompExit: ; Return with DE pointing to pop hl ; last match + 1, and HL still ret ; pointing to beginning. ; curFN: ds 2 ; ; File name parsing subroutines ; ; getfn gets a file name from text pointed to by reg hl into ; an fcb pointed to by reg de. leading delimeters are ; ignored. ; ; entry hl first character to be scanned ; de first byte of fcb ; exit hl character following file name ; GetFN: call NitF ; init 1st half of fcb call GStart ; scan to first character of name ret z ; end of line was found - leave fcb blank call GetDRv ; get drive spec. if present call GetPS ; get primary and secondary name ret ; ; nitf fills the fcb with dflt info - 0 in drive field ; all-blank in name field, and 0 in ex,s1,s2 and rc flds ; NitF: push de ; save fcb loc ex de,hl ; move it to hl ld (hl),0 ; zap dr field inc hl ; bump to name field ld b,.nam+.ext ; zap all of name fld NitLp1: ld (hl),' ' inc hl djnz NitLp1 ld b,_DIR-_EX ; zero others NitLp2: ld (hl),0 inc hl djnz NitLp2 ex de,hl ; restore hl pop de ; restore fcb pointer ret ; ; gstart advances the text pointer (reg hl) to the first ; non delimiter character (i.e. ignores blanks). returns a ; flag if end of line (00h or ';') is found while scaning. ; exit hl pointing to first non delimiter ; a clobbered ; zero set if end of line was found ; GStart: call GetCh ; see if pointing to delim? ret nz ; nope - return cp ';' ; end of line? ret z ; yup - return w/flag or a ret z ; yup - return w/flag inc hl ; nope - move over it jr GStart ; and try next char ; ; getdrv checks for the presence of a drive spec at the text ; pointer, and if present formats it into the fcb and ; advances the text pointer over it. ; entry hl text pointer ; de pointer to first byte of fcb ; exit hl possibly updated text pointer ; de pointer to second (primary name) byte of fcb ; GetDRv: inc de ; point to name if spec not found inc hl ; look ahead to see if ':' present ld a,(hl) dec hl ; put back in case not present cp ':' ; is a drive spec present? ret nz ; nope - return ld a,(hl) ; yup - get the ascii drive name sub 'A'-1 ; convert to fcb drive spec dec de ; point back to drive spec byte ld (de),a ; store spec into fcb inc de ; point back to name inc hl ; skip over drive name inc hl ; and over ':' ret ; ; getps gets the primary and secondary names into the fcb. ; entry hl text pointer ; exit hl character following secondary name (if present) ; GetPS: ld c,.nam ; max length of primary name call GetNam ; pack primary name into fcb ld a,(hl) ; see if terminated by a period cp '.' ret nz ; nope - secondary name not given ; return default (blanks) inc hl ; yup - move text pointer over period FTpoint: ld a,c ; yup - update fcb pointer to secondary or a jr z,GetFT inc de dec c jr FTpoint GetFT: ld c,.ext ; max length of secondary name call GetNam ; pack secondary name into fcb ret ; ; getnam copies a name from the text pointer into the fcb for ; a given maximum length or until a delimiter is found, which ; ever occurs first. if more than the maximum number of ; characters is present, characters are ignored until a ; a delimiter is found. ; entry hl first character of name to be scaned ; de pointer into fcb name field ; c maximum length ; exit hl pointing to terminating delimiter ; de next empty byte in fcb name field ; c max length - number of characters transfered ; GetNam: call GetCh ; are we pointing to a delimiter yet? ret z ; if so, name is transfered inc hl ; if not, move over character cp '*' ; ambigious file reference? jr z,Ambig ; if so, fill the rest of field with '?' ld (de),a ; if not, just copy into name field inc de ; increment name field pointer dec c ; if name field full? jr nz,GetNam ; nope - keep filling jr GetDel ; yup - ignore until delimiter Ambig: ld a,'?' ; fill character for wild card match QFill: ld (de),a ; fill until field is full inc de dec c jr nz,QFill ; fall thru to ingore rest of name GetDel: call GetCh ; pointing to a delimiter? ret z ; yup - all done inc hl ; nope - ignore another one jr GetDel ; ; getch gets the character pointed to by the text pointer ; and sets the zero flag if it is a delimiter. ; entry hl text pointer ; exit hl preserved ; a character at text pointer ; z set if a delimiter ; GetCh: ld a,(hl) ; get the character push bc push hl ld hl,DelTab ld bc,DelTabL cpir ; filter delimiter pop hl pop bc ret ; DelTab: db '.,; :=<>',null DelTabL equ $-DelTab ; ; Error routines ; BadCPU: call AbNoUsr db 'Requires Z80 CPU',eot NoRSX: call AbNoUsr db 'Missing RSX',eot BadVer: call AbNoUsr db 'Requires CP/M 3.x',eot NoDir: call Abend db 'Library not found',eot Fishy: call Abend db 'Name after "-" isn''t a library',eot NoMemb: call Abend db 'Command not in directory',eot NoLoad: call Abend db 'No program in memory',eot NoFit: call Abend db 'Program too large to load',eot ; DfltNam: db 'TURBO ' ; <---change this if you like--- CHNlit: db 'CHN' LBRlit: db 'LBR' ; ; Process error ; Abend: ld a,(EntUsr) ld e,a ld c,Usr call BDOS ; Reset to entry user. AbNoUsr: ld de,NewLin ld c,Msg call BDOS pop de ld c,Msg call BDOS ld de,SUBfile ld c,Del call BDOS ld de,AbtMsg ld c,Msg call BDOS jp BOOT ; AbtMsg: db '...ABORTED.',eot SUBfile: db 'A'-'@','$$$ SUB' ds 4 IniPar: db MyRSX db 0 ; Initial call RTLLBR: db 0,'TURBORTLCOM' ; ; ------------------------------------------------------\ ; | GoPar: ; | db MyRSX ; | db 1 ; Real call | ; | ; >>>>>>>>> Following data moved to RSX <<<<<<<<< | ; | RSXpar equ $ MIndex: ; | dw 0 ; Index of RTL | MLenx: ; | dw 0 ; Length of RTL | Index: ; | dw 0 ; Index of member | Lenx: ; | dw 0 ; Length of member | CLenx: ; | dw 0 ; Entire length | EntUsr: ; | db 0 ; Current user | ; | ; Library FCB | ; | LBRfil: ; | ds FCBlen ; | RSXdata equ $-RSXpar ; | ; | ; >>>>>>>>>>>>>>>>>>>>>>>-<<<<<<<<<<<<<<<<<<<<<<< | ; | ; ------------------------------------------------------/ ; ; Local stack ; ds 2*16 LStack: ; ; Copy of command line ; Hold equ $ ; ; .CHN FCB ; Member equ Hold+CCPlen end