; ; CHN.MAC -- Version 3.1 -- For CP/M Plus (3.0) Only ; ; Creates a command file (alias) for a group of chained ; commands. ; ; Note: This program requires CP/M 3.0 and a Z80 CPU. ; ; Usage: CHN {{d:}fn {[command{\command{...}}}} ; ; The filename is name of the output (command) file for CHN to ; create or edit. If a filename is not given, it will be requested: ; ; Name of Command File: {d:}fn ; ; Enter the name. If a drive specification is included, the new ; file will be created on that drive, otherwise the default drive ; will be used. The file created will be a .COM file, so it is ; not necessary to enter the filetype (any filetype entered will ; be ignored). ; ; If an output filename is included in the command tail then a ; command line for inclusion in the command file may also be ; included in the tail following a left bracket ([). You have ; slightly less command line space for your command file this way ; because the CCP will not accept more than 128 characters from ; the system prompt, including "CHN", the output filename, and the ; necessary delimiting spaces. In addition, exclamation points ; cannot be included in a CP/M Plus command tail, so a backslash ; (\) should be used to separate multiple commands. These will ; be converted by CHN to exclamation points. Backslashes are ; seldom used in commands, but if you need to enter one then omit ; the command line from the command tail. ; ; CHN will then request the command line from you: ; ; COMMAND LINE: ; ; > ; ; Enter the command line. It may include parameters like SUBMIT ; files of the form $n where n is a single digit 1-9. Parameter ; 0 cannot be used (it becomes the command name). Entering two ; $$ in the command line enters a real $ (like SUBMIT). Parameters ; may be up to 14 characters each. The command line is limited ; to 128 characters total, including space for entering parameters. ;*Multiple chained commands separated by ! are permitted, as are ; conditional commands using a colon (:). ; ; CHN's .COM files do not support program input. (SUBMIT allows ; that using the "<" marker.) ; ; If a .COM with the same name exists, you will be asked if you want ; to modify it. If you answer "Y" and you enter a command line in ; the command tail, the old file will immediately be overwritten. If ; you didn't enter a command tail, CHN will attempt to read the file. ; (If it is not a CHN file, CHN will complain and abort.) ; ; After the old file is read, the old command line will be displayed ; for editing. You may use all the CP/M Plus line editing facilities, ; some of which will be displayed on the screen to remind you. Once ; you press , a new file containing the edited line will be ; created. ; ; VERSIONS: ; 0.1 Gordon Wilk -- original version. ; 1.0 Gordon Wilk -- first public domain release. ; 2.0 Gordon Wilk, 9/86 -- FOG release -- Osborne ; Executive specific; program named SYN for ; SYNonym. ; 3.0 Gene Pizzetta, 5/20/87 -- Machine specific code ; (which was unnecessary) changed; command ; line filename and drive specification added ; for output file; changed parameter marker ; to '$' (was '&') to be consistent with SUBMIT ; (nothing new to remember); changed program ; name to CHN from SYN; now sends "failed" ; code to BDOS if aborted (to work with ; conditional CHN and SUBMIT files); code ; cleaned up; Z80 macros removed, Z80.LIB now ; required. Should now run on all CP/M Plus ; machines. ; 3.1 Gene Pizzetta, 6/15/87 -- Added ability to read ; command line contents from command tail; ; fixed bug that caused program not to abort ; after command line editing mode if ^X was ; used to erase the entire command line (an ; empty command line was written to disk). ; ; COPYRIGHT: Original code copyright 1986 by Gordon Wilk ; 6707 Springpark Ave. ; Los Angeles CA 90056 ; ; Released for private non-commercial uses without fee. ; ; Please report any bugs or suggestions concerning this version to: ; Gene Pizzetta CompuServe 72060,505 ; 481 Revere Street FOG #29 (617) 288-4667 ; Revere, MA 02151 Voice (617) 284-0891 ; ; Developed with SLRMAC. May be assembled with MAC by changing the ; filetype to .ASM. Z80.LIB is required. Note: the line above ; marked with an * will generate an error, but it is harmless. ; The code is not affected. ; ; WBoot equ 00h ; warm boot Bdos equ 05h ; BDOS entry MemTop equ Bdos+1 ; top of memory Fail equ 0FF00h ; failure code CpmFcb equ 05Ch ; default FCB FcbExt equ CpmFcb+9 ; default filetype FcbCr equ CpmFcb+32 DBuff equ 080h ; default DMA buffer ; ; BDOS functions ; ConIn equ 01h FOpen equ 0Fh FClose equ 10h FRead equ 14h FWrite equ 15h FMake equ 16h MultSec equ 2Ch Chain equ 2Fh BdosRet equ 6Ch FParse equ 98h ; ; character codes ; LF equ 0Ah ; linefeed CR equ 0Dh ; carriage return BELL equ 07h ; BEL ESC equ 1Bh ; escape ; ; The following equates may be changed and the program re-assembled ; ArgMark equ '$' ; parameter argument marking character CmdDelm equ '\' ; command tail multiple command delimiter ; MACLIB Z80 ; PRINT MACRO MSG ; print the string at MSG lxi d,MSG call PSTR ENDM ; REPLY MACRO BUFFR,LENGTH ; get string from console lxi h,BUFFR mvi m,LENGTH xchg call GETS ENDM ; FILE MACRO FUNC,POINTER ; BDOS file related calls IF NOT NUL POINTER lxi d,POINTER ENDIF mvi c,FUNC call Bdos ENDM ; SETSEC MACRO NUMBER ; set multi-sector count push b push d push h mvi c,MultSec mvi e,NUMBER call Bdos pop h pop d pop b ENDM ; BLKMOVE MACRO FROM,TO,LENGTH ; move a block of memory lxi h,FROM lxi d,TO lxi b,LENGTH LDIR ENDM ; org 100h jmp MAIN ; ; strings for dim and normal video must end with $ ; DIM: db '$',0,0,0,0,0,0,0 ; string for dim video NORMAL: db '$',0,0,0,0,0,0,0 ; string for normal video ; ; messages ... ; SIGNON: db CR,LF,'CHN for CP/M Plus Version 3.1$' ASKFIL: db 'Name of Command File: $' EDHLP: db '<-- --> DEL Del/BS BOL/EOL Redsply DONE ',CR,LF db '^A ^F ^G ^H ^B ^R $' SHWOLD: db 'EDITING COMMAND LINE:$' CRLF: db CR,LF,LF,'$' ASKCOM: db 'COMMAND LINE:$' PROMPT: db '>$' ASKMOD: db 'This Command File Exists. Modify it? (Y/N): $' NOTCHN: db ' * * Not a Command File * *$' ABORT: db 'ABORTED',BELL,'$' DONE: db 'DONE$' QCOM: db '.COM',0 QCHN: db 'CHN',0 PFCB: dw DBuff+2 ; parse block: name starts at 2nd byte dw CpmFcb ; MAIN: lhld MemTop ; stack at top of memory dcx H sphl PRINT SIGNON ; sign on lda CpmFcb+1 ; check for command tail cpi '#' cc GETFN ; (no tail, ask) BLKMOVE QCOM+1,FcbExt,3 ; move .COM to FCB ; lxi h,DBuff+1 lxi d,COMNC call BRACKET ; check for command line in tail ; ISFILE: BLKMOVE CpmFcb+1,PARMS,8 ; and place in parameter 0 FILE FOpen,CpmFcb ; see if file already exists inr a ; if it does then ask JRNZ FILEX ; ..about modification lda COMNC ; see if command line already exists cpi 0 JRNZ FLMAKE ; (yes, don't ask for one) PRINT CRLF PRINT ASKCOM ; otherwise, ask for command line and PRINT CRLF PRINT PROMPT REPLY COMMX,128 ; ..put into buffer space of new program jz EREXIT ; (no command line: abort) FLMAKE: FILE FMake,CpmFcb ; ..create it and jmp DO$WR ; ..write the new file ; ; FILEX -- the command file exists; do you want to modify it? ; FILEX: PRINT CRLF PRINT ASKMOD ; Modify it? call YESNO jrz YESMOD call GETFN ; (no, get new filename) jr ISFILE ; YESMOD: lda COMNC ; see if a new cmd line already exists cpi 0 jnz DO$WR ; if so just write it call RD$COM ; get command line from file, sub a ; ..set cr to first sta FcbCr ; ..record for re-write ; ; show the old line and accept edit to COMLIN ; PRINT CRLF PRINT DIM ; dim video PRINT EDHLP ; editing help message PRINT NORMAL ; normal video PRINT CRLF PRINT SHWOLD ; editing message PRINT CRLF PRINT PROMPT lxi D,COMMX call GETEDIT ; edit command line ; ; DO$WR -- write program code and command line data to new file ; (512 bytes, 4 logical sectors) ; DO$WR: lda COMNC ; is command line null? cpi 0 JRZ EREXIT ; (yes, abort) lxi d,NEWPRO ; set buffer to new program area call SETBUFF SETSEC 4 ; set multi-sector count FILE FWrite,CpmFcb ; ..write FILE FClose,CpmFcb ; ..close ; ; FINISH -- normal end of program ; FINISH: PRINT CRLF PRINT DONE ; say we're through jmp WBoot ; ..and terminate ; ; EREXIT -- aborted program exit ; EREXIT: PRINT CRLF PRINT ABORT ; let us know lxi d,Fail ; kill any conditional mvi c,BdosRet ; ..batch file call Bdos jmp WBoot ; ..and terminate ; ;================================================================ ; SUBROUTINES ;================================================================ ; BRACKET: mvi b,'[' ; look for [ in command tail mvi c,0 ; ..or a null call SCAN cmp b ; was it a [ rnz ; (no, return) mvi a,0 ; yes, move it sta COMNC ; initialize buffer to 0 LOOP: inx d inx h ; increment pointers mov a,m ; get byte from command tail cpi CmdDelm ; is it a backslash? JRNZ NOSLSH ; (no, skip conversion) mvi a,'!' ; make it an exclamation NOSLSH: stax d ; store in command line cpi 0 ; was it a null? rz ; (yes, end of tail) lda COMNC ; no, increment command length inr a sta COMNC jr LOOP ; SCAN: mov a,m ; get byte cmp b ; is it a [ rz ; (yes, return) cmp c ; a null then? rz ; (yes, return) inx h ; increment pointer jr SCAN ; ..and keep looking ; GETFN: PRINT CRLF PRINT ASKFIL ; ask for filename REPLY DBuff,10 ; get reply, put in DMA buffer jz EREXIT ; (no entry: abort) lxi b,5 ; move 5 chars from ".COM" xchg ; ..to 1st byte after name lxi h,QCOM LDIR FILE FParse,PFCB ; parse filename into CpmFcb ret ; ; RD$COM -- Read an existing file and verify it is a CHN file ; RD$COM: lxi d,NEWPRO ; overlay the NEWPRO code call SETBUFF ; with a file read SETSEC 4 ; set multi-sector count FILE FRead,CpmFcb ; ..read it ; ; parmeter 0 must be filename, but only the first char is checked ; lda PARMS ; 1st char of parm0 lxi h,CpmFcb+1 ; --> 1st char of filename cmp m rz ; if it matches, return PRINT CRLF PRINT NOTCHN ; otherwise, complain jmp EREXIT ; ..and abort ; ; GETEDIT -- edits string at (DE) ; return: DE = A = length and flags set ; HL -> EOS null termination ; GETEDIT: push d ; save buffer address call SETBUFF ; set DMA address there mov d,a ; set DE=0 for edit mov e,a jmp GETS$2 ; ; GETS -- gets string to (DE) ; GETS: push d ; save buffer address GETS$2: mvi c,10 ; get string (DE) call Bdos pop h ; HL --> buffer inx h ; HL --> count mov a,m ; A = length mov e,a mvi d,0 ; DE = length dad d ; HL --> last char in string inx h ; HL --> EOS mov m,d ; NULL terminate ora a ; and set flags ret ; ; YESNO -- return with Z flag set if reply = Y ; reset otherwise ; YESNO: mvi C,ConIn call Bdos ani 05Fh ;upper case cpi 'Y' ret ; ; * * The routines below return directly from BDOS * * ; ; PSTR -- Console display string ; PSTR: mvi c,09 ; display string pointed jmp Bdos ; ..to by DE ; ; SETBUFF -- set buffer address ; SETBUFF: mvi c,26 ; set DMA buffer jmp Bdos ; ..to (DE) ; ; ; * * below is the new command file to be created * * ; ; Move the command line data from COMLIN to the default DMA buffer ; and chain to it. OFFSET adjusts addresses to their runtime ; locations because MAC does not have .phase/.dephase ; NEWPRO equ $ OFFSET equ $-100H lhld MemTop ; set up stack dcx h sphl ; call GETARG ; get args into parm list lxi h,COMLIN-OFFSET ; move the command line lxi d,DBuff ; ..to DMA buffer call MOVE stax d ; terminate with 0 mvi c,Chain ; chain to command line jmp Bdos ; ; * * SUBROUTINES * * ; ; GETARG -- moves arguments from the command line to the ; appropriate PARM slots ; GETARG equ $-OFFSET lxi d,DBuff+1 ; source lxi h,PARMS-OFFSET+16 ; destination step over parm0 lxi b,16 ; parm increment FNDARG equ $-OFFSET ; find an argument ldax d inx d cpi ' ' ; if it's a space jz FNDARG ; ..keep looking rc ; (if < space, quit; otherwise ; ..we've found an argument push h ; save address of parm slot ARGLOOP equ $-OFFSET ; move chars until end of argument mov m,a ; move char to parms inx h ; increment destination ldax d ; get next char inx d ; increment source cpi ' '+1 ; (if it's > space then jnc ARGLOOP ; ..there's more to copy) pop h ; else restore HL --> parm slot ora a ; (if it's null rz ; ..then quit) dad b ; otherwise HL = HL+16, next slot jmp FNDARG ; ; MOVE -- a recursive macro processor ; HL --> source string ; DE --> destination in the command line buffer ; Characters are moved (HL) to (DE). If an ArgMark is encountered, ; HL is saved, the address of the stored argument placed in HL ; and MOVE is called recursively to move from the stored argument. ; Stops at a NULL byte in source. ; Return: A = 0 ; DE --> EOS ; MOVE equ $-OFFSET mov a,m ; A = char inx h ; point to next char ora a ; if 0 then rz ; ..end of string cpi ArgMark ; if not an argument marker then jnz MOVE2 ; ..move the character mov a,m ; otherwise get next char inx h ; ..and point to one after that cpi '0' ; if it's < '0' it's not an arg number jc MOVE2 ; ..so move it cpi '9'+1 ; if it's > '9' it's not an arg number jnc MOVE2 ; ..so move it ; otherwise its an ASCII digit sui '0' ; ..so make it binary ; ; A = argument number which, multiplied by 16, is list index. ; Save HL and replace it with the argument address. ; add a add a add a add a ; times 16 for length of arg push h ; save address of next char mvi b,0 mov c,a ; BC = address of list offset lxi h,PARMS-OFFSET ; HL = base of arg list dad b ; HL = address in arg list call MOVE ; move recursively from arg list pop h ; restore source address jmp MOVE ; ..and start over ; MOVE2 equ $-OFFSET stax d ; move to destination inx d ; point to next char in dest jmp MOVE ; get next char ; ; data for NEWPRO ; PARMS: ds 10*16 ; parameter space COMMX: db 128 ; maximum chars accepted COMNC: db 0 ; number of chars entered COMLIN: ds 128 ; command line buffer ; end 100h