TITLE 'CCPZ Version 4.1' .Z80 ; org 0e400h ; For RunCPM 60k org 0f400h ; For RunCPM 64k ; ; CP/M CONSOLE COMMAND PROCESSOR (CCP) Revision 4.1 ; FOR Z80-BASED CP/M 2.X SYSTEMS ; ; ORIGINAL CCP DISASSEMBLED BY ???? ; ORIGINAL CCP DISASSEMBLED FURTHER BY RLC ; ORIGINAL CCP COMMENTED BY RLC ; CUSTOMIZED FOR ARIES-II BY RLC ; FURTHER MODIFIED BY RGF AS V2.0 ; FURTHER MODIFIED BY RLC AS V2.1 ; FURTHER MODIFIED BY KBP AS V2.2 ; FURTHER MODIFIED BY RLC AS V2.4 (V2.3 skipped) ; FURTHER MODIFIED BY RLC AS V2.5 ; FURTHER MODIFIED BY RLC AS V2.6 ; FURTHUR MODIFIED BY SBB AS V2.7 ; FURTHER MODIFIED BY RLC AS V2.8 ; FURTHER MODIFIED BY RLC AS V2.9 ; FURTHER MODIFIED BY RLC AS V3.0 ; FURTHER MODIFIED BY RLC AS V3.1 ; FURTHER MODIFIED BY RLC AS V4.0 ; SBB V4.1 11/27/81 ; ;******** Refer to CCPZ-Vxx.NOT File for Revision History ******** ; ;******** Structure Notes ******** ; ; This CCP is divided into a number of major sections. The following ; is an outline of these sections and the names of the major routines ; located therein. ; ; Section Function/Routines ; ------- ----------------- ; ; -- Opening Comments, Equates, and Macro Definitions ; ; 0 JMP Table into CCP ; ; 1 Buffers ; ; 2 CCP Starting Modules ; CCP1 CCP RESTRT RSTCCP RCCPNL ; PRNNF ; ; 3 Utilities ; CRLF CONOUT CONIN LCOUT LSTOUT ; READF READ BDOSB PRINTC PRINT ; GETDRV DEFDMA DMASET RESET BDOSJP ; LOGIN OPENF OPEN GRBDOS CLOSE ; SEARF SEAR1 SEARN SUBKIL DELETE ; WRITE CREATE RESETUSR GETUSR SETUSR ; ; 4 CCP Utilities ; SETUD SETU0D UCASE REDBUF CNVBUF ; BREAK USRNUM ERROR SDELM ADVAN ; SBLANK ADDAH NUMBER NUMERR HEXNUM ; DIRPTR SLOGIN DLOGIN COMLOG SCANER ; CMDSER ; ; 5 CCP-Resident Commands and Functions ; 5A DIR DIRPR FILLQ ; 5B ERA ; 5C LIST ; 5D TYPE PAGER ; 5E SAVE ; 5F REN ; 5G USER ; 5H DFU ; 5I JUMP ; 5J GO ; 5K COM CALLPROG ERRLOG ERRJMP ; 5L GET MEMLOAD PRNLE ; ; FALSE EQU 0 TRUE EQU NOT FALSE ; ; CUSTOMIZATION EQUATES ; ; The following equates may be used to customize this CCP for the user's ; system and integration technique. The following constants are provided: ; ; REL - TRUE if integration is to be done via MOVCPM ; - FALSE if integration is to be done via DDT and SYSGEN ; ; BASE - Base Address of user's CP/M system (normally 0 for DR version) ; This equate allows easy modification by non-standard CP/M (eg,H89) ; ; CCPLOC - Base Page Address of CCP; this value can be obtained by running ; the BDOSLOC program on your system, or by setting the ; MSIZE and BIOSEX equates to the system memory size in ; K-bytes and the "extra" memory required by your BIOS ; in K-bytes. BIOSEX is zero if your BIOS is normal size, ; and can be negative if your BIOS is in PROM or in ; non-contiguous memory. ; ; RAS - Remote-Access System; setting this equate to TRUE disables ; certain CCP commands that are considered harmful in a Remote- ; Access environment; use under Remote-Access Systems (RBBS) for ; security purposes ; REL EQU FALSE ;SET TO TRUE FOR MOVCPM INTEGRATION ; BASE EQU 0 ;BASE OF CP/M SYSTEM (SET FOR STANDARD CP/M) ; IF REL CCPLOC EQU 0 ;MOVCPM IMAGE ELSE ; ; If REL is FALSE, the value of CCPLOC may be set in one ; of two ways. The first way is to set MSIZE and BIOSEX ; as described above using the following three lines: ; ;MSIZE EQU 64 ;SIZE OF MEM IN K-BYTES ;BIOSEX EQU 2 ;EXTRA # K-BYTES IN BIOS ;CCPLOC EQU 3400H+(MSIZE-20-BIOSEX)*1024;CCP ORIGIN ; ; The second way is to obtain the origin of your current ; CCP using BDSLOC or its equivalent, then merely set CCPLOC ; to that value as as in the following line: ; ;CCPLOC EQU 0BD00H ;FILL IN WITH BDOSLOC SUPPLIED VALUE ; ; Note that you should only use one method or the other. ; Do NOT define CCPLOC twice! ; ; The following gives the required offset to load the CCP into the ; CP/M SYSGEN Image through DDT (the Roffset command); Note that this ; value conforms with the standard value presented in the CP/M reference ; manuals, but it may not necessarily conform with the location of the ; CCP in YOUR CP/M system; several systems (Morrow Designs, P&T, Heath ; Org-0 to name a few) have the CCP located at a non-standard address in ; the SYSGEN Image. ; ;CCPR EQU 0980H-CCPLOC ;DDT LOAD OFFSET ;CCPR EQU 1100H-CCPLOC ;DDT LOAD OFFSET FOR MORROW DESIGNS ;CCPR EQU 0E00H-CCPLOC ;DDT LOAD OFFSET FOR APPLE SOFTCARD 56K ENDIF ; RAS EQU FALSE ;SET TO TRUE IF CCP IS FOR A REMOTE-ACCESS SYSTEM ; ; *** Note to Apple Softcard Users *** ; ; In their infinite (?) wisdom (???), Microsoft decided that the way to ; get a two-column directory display instead of four-column (narrow 40-col ; screen, remember) was to have their BIOS poke CCP every time it was ; loaded. Naturally, that will turn into a random poke on any non-standard ; CCP, like this one. The best way to get this CCP up on the Apple is to ; load it into CPM56.COM, at location 0E00H in the image. The BIOS code ; that pokes the CCP can also be modified at that time. The poke is done ; by "STA 0C8B2H", found at 24FEH in the CPM56 image. To eliminate the ; poke forever, change the "STA" to "LDA" by changing the contents of ; location 24FEH from 32H to 3AH. If you want a two-column display, set ; the TWOCOL switch below to a value of TRUE. ; TWOCOL EQU FALSE ;TRUE IF TWO COL DIR INSTEAD OF FOUR ; ; The following is presented as an option, but is not generally user-customiz- ; able. A basic design choice had to be made in the design of CCPZ concerning ; the execution of SUBMIT files. The original CCP had a problem in this sense ; in that it ALWAYS looked for the SUBMIT file from drive A: and the SUBMIT ; program itself (SUBMIT.COM) would place the $$$.SUB file on the currently ; logged-in drive, so when the user was logged into B: and he issued a SUBMIT ; command, the $$$.SUB was placed on B: and did not execute because the CCP ; looked for it on A: and never found it. ; ; After much debate it was decided to have CCPZ perform the same type of ; function as CCP (look for the $$$.SUB file on A:), but the problem with ; SUBMIT.COM still exists. Hence, RGF designed SuperSUB and RLC took his ; SuperSUB and designed SUB from it; both programs are set up to allow the ; selection at assembly time of creating the $$$.SUB on the logged-in drive ; or on drive A:. ; ; A final definition of the Indirect Command File ($$$.SUB or SUBMIT ; File) is presented as follows: ; ; "An Indirect Command File is one which contains ; a series of commands exactly as they would be ; entered from a CP/M Console. The SUBMIT Command ; (or SUB Command) reads this files and transforms ; it for processing by the CCPZ (the $$$.SUB File). ; CCPZ will then execute the commands indicated ; EXACTLY as if they were typed at the Console." ; ; Hence, to permit this to happen, the $$$.SUB file must always ; be present on a specific drive, and A: is the choice for said drive. ; With this facility engaged as such, Indirect Command Files like: ; ; DIR ; A: ; DIR ; ; can be executed, even though the currently logged-in drive is changed ; during execution. If the $$$.SUB file was present on the currently ; logged-in drive, the above series of commands would not work since the ; CCPZ would be looking for $$$.SUB on the logged-in drive, and switching ; logged-in drives without moving the $$$.SUB file as well would cause ; processing to abort. ; SUBA EQU TRUE ; Set to TRUE to have $$$.SUB always on A: ; Set to FALSE to have $$$.SUB on the logged-in drive ; ; The following flag enables extended processing for user-program supplied ; command lines. This is for Command Level 3 of CCPZ. Under the CCPZ Version ; 4.0 philosophy, three command levels exist: ; ; (1) that command issued by the user from his console at the '>' prompt ; (2) that command issued by a $$$.SUB file at the '$' prompt ; (3) that command issued by a user program by placing the command into ; CIBUFF and setting the character count in CBUFF ; ; Setting CLEVEL3 to TRUE enables extended processing of the third level of ; CCPZ command. All the user program need do is to store the command line and ; set the character count; CCPZ will initialize the pointers properly, store ; the ending zero properly, and capitalize the command line for processing. ; Once the command line is properly stored, the user executes the command line ; by reentering the CCPZ through CCPLOC [NOTE: The C register MUST contain ; a valid User/Disk Flag (see location 4) at this time.] ; CLEVEL3 EQU TRUE ;ENABLE COMMAND LEVEL 3 PROCESSING ; ; ;*** TERMINAL AND 'TYPE' CUSTOMIZATION EQUATES ; NLINES EQU 24 ;NUMBER OF LINES ON CRT SCREEN WIDE EQU TRUE ;TRUE IF WIDE DIR DISPLAY FENCE EQU '|' ;SEP CHAR BETWEEN DIR FILES ; PGDFLT EQU TRUE ;SET TO FALSE TO DISABLE PAGING BY DEFAULT PGDFLG EQU 'P' ;FOR TYPE COMMAND: PAGE OR NOT (DEP ON PGDFLT) ; THIS FLAG REVERSES THE DEFAULT EFFECT ; MAXUSR EQU 15 ;MAXIMUM USER NUMBER ACCESSABLE ; SYSFLG EQU 'A' ;FOR DIR COMMAND: LIST $SYS AND $DIR ; SOFLG EQU 'S' ;FOR DIR COMMAND: LIST $SYS FILES ONLY ; SUPRES EQU TRUE ;SUPRESSES USER # REPORT FOR USER 0 ; DEFUSR EQU 0 ;DEFAULT USER NUMBER FOR COM FILES ; SPRMPT EQU '$' ;CCP PROMPT INDICATING SUBMIT COMMAND CPRMPT EQU '>' ;CCP PROMPT INDICATING USER COMMAND ; NUMBASE EQU 'H' ;CHARACTER USED TO SWITCH FROM DEFAULT ; NUMBER BASE ; SECTFLG EQU 'S' ;OPTION CHAR FOR SAVE COMMAND TO SAVE SECTORS ; ; END OF CUSTOMIZATION SECTION ; CR EQU 0DH LF EQU 0AH TAB EQU 09H ; WBOOT EQU BASE+0000H ;CP/M WARM BOOT ADDRESS UDFLAG EQU BASE+0004H ;USER NUM IN HIGH NYBBLE, DISK IN LOW BDOS EQU BASE+0005H ;BDOS FUNCTION CALL ENTRY PT TFCB EQU BASE+005CH ;DEFAULT FCB BUFFER TBUFF EQU BASE+0080H ;DEFAULT DISK I/O BUFFER TPA EQU BASE+0100H ;BASE OF TPA ;**** Section 0 **** ; ; ORG CCPLOC ; ; ENTRY POINTS INTO CCPZ ; ; If the CCPZ is entered at location CCPLOC (at the JMP to CCP), then ; the default command in CIBUFF will be processed. If the CCPZ is entered ; at location CCPLOC+3 (at the JMP to CCP1), then the default command in ; CIBUFF will NOT be processed. ; ; NOTE: Entry into CCPZ in this way is permitted under CCPZ Version 4.0, ; but in order for this to work, CIBUFF and CBUFF MUST be initialized properly ; AND the C register MUST contain a valid User/Disk Flag (see Location 4: the ; most significant nybble contains the User Number and the least significant ; nybble contains the Disk Number). ; ; Some user programs (such as SYNONYM3) attempt to use the default ; command facility. Under the original CCP, it was necessary to initialize ; the pointer after the reserved space for the command buffer to point to ; the first byte of the command buffer. Under Version 4.x of CCPZ, this is ; no longer the case. The CIBPTR (Command Input Buffer PoinTeR) is located ; to be compatible with such programs (provided they determine the buffer ; length from the byte at MBUFF [CCPLOC + 6]), but under Version 4.x of CCPZ ; this is no longer necessary. CCPZ Version 4.x automatically initializes ; this buffer pointer in all cases. ; ENTRY: JP CCP ; Process potential default command JP CCP1 ; Do NOT process potential default command ; ;**** Section 1 **** ; ; BUFFERS ET AL ; ; INPUT COMMAND LINE AND DEFAULT COMMAND ; ; The command line to be executed is stored here. This command line ; is generated in one of three ways: ; ; (1) by the user entering it through the BDOS READLN function at ; the du> prompt [user input from keyboard] ; (2) by the SUBMIT File Facility placing it there from a $$$.SUB ; file ; (3) by an external program or user placing the required command ; into this buffer ; ; In all cases, the command line is placed into the buffer starting at ; CIBUFF. This command line is terminated by the last character (NOT Carriage ; Return), and a character count of all characters in the command line ; up to and including the last character is placed into location CBUFF ; (immediately before the command line at CIBUFF). The placed command line ; is then parsed, interpreted, and the indicated command is executed. ; If CLEVEL3 is permitted, a terminating zero is placed after the command ; (otherwise the user program has to place this zero) and the CIBPTR is ; properly initialized (otherwise the user program has to init this ptr). ; If the command is placed by a user program, entering at CCPLOC is enough ; to have the command processed. Again, under CCPZ Version 4.x, it is not ; necessary to store the pointer to CIBUFF in CIBPTR; CCPZ will do this for ; the calling program if CLEVEL3 is made TRUE. ; ; WARNING: The command line must NOT exceed BUFLEN characters in length. ; For user programs which load this command, the value of BUFLEN can be ; obtained by examining the byte at MBUFF (CCPLOC + 6). ; BUFLEN EQU 80 ;MAXIMUM BUFFER LENGTH MBUFF: DB BUFLEN ;MAXIMUM BUFFER LENGTH CBUFF: DB 0 ;NUMBER OF VALID CHARS IN COMMAND LINE CIBUFF: DB ' ';DEFAULT (COLD BOOT) COMMAND CIBUF: DB 0 ;COMMAND STRING TERMINATOR DB ' CCPZ-V4.1 of 11/27/81 ';FOR DUMP IDENTIFICATION DS BUFLEN-($-CIBUFF)+1;TOTAL IS 'BUFLEN' BYTES ; CIBPTR: DW CIBUFF ;POINTER TO COMMAND INPUT BUFFER CIPTR: DW CIBUF ;PTR TO CURR CMD FOR ERROR REPORTING ; DS 26 ;STACK AREA STACK EQU $ ;TOP OF STACK ; ; FILE TYPE FOR COMMAND ; COMMSG: DB 'COM' ; ; SUBMIT FILE CONTROL BLOCK ; SUBFCB: IF SUBA ;IF $$$.SUB ON A: DB 1 ;DISK NAME SET TO DEFAULT TO DRIVE A: ENDIF ; IF NOT SUBA ;IF $$$.SUB ON CURRENT DRIVE DB 0 ;DISK NAME SET TO DEFAULT TO CURRENT DRIVE ENDIF ; DB '$$$' ;FILE NAME DB ' ' DB 'SUB' ;FILE TYPE DB 0 ;EXTENT NUMBER DB 0 ;S1 SUBFS2: DS 1 ;S2 SUBFRC: DS 1 ;RECORD COUNT DS 16 ;DISK GROUP MAP SUBFCR: DS 1 ;CURRENT RECORD NUMBER ; ; COMMAND FILE CONTROL BLOCK ; FCBDN: DS 1 ;DISK NAME FCBFN: DS 8 ;FILE NAME FCBFT: DS 3 ;FILE TYPE DS 1 ;EXTENT NUMBER DS 2 ;S1 AND S2 DS 1 ;RECORD COUNT FCBDM: DS 16 ;DISK GROUP MAP FCBCR: DS 1 ;CURRENT RECORD NUMBER ; ; OTHER BUFFERS ; PAGCNT: DB NLINES-2 ;LINES LEFT ON PAGE CHRCNT: DB 0 ;CHAR COUNT FOR TYPE ; ; CCP BUILT-IN COMMAND TABLE ; NCHARS EQU 4 ;NUMBER OF CHARS/COMMAND ; ; CCP COMMAND NAME TABLE ; EACH TABLE ENTRY IS COMPOSED OF THE 4-BYTE COMMAND AND 2-BYTE ADDRESS ; CMDTBL: DB 'DIR ' DW DIR DB 'LIST' DW LIST DB 'TYPE' DW TYPE DB 'USER' DW USER DB 'DFU ' DW DFU ; IF NOT RAS ;FOR NON-RAS DB 'GO ' DW GO DB 'ERA ' DW ERA DB 'SAVE' DW SAVE DB 'REN ' DW REN DB 'GET ' DW GET DB 'JUMP' DW JUMP ENDIF ; NCMNDS EQU ($-CMDTBL)/(NCHARS+2) ; ; ;**** Section 2 **** ; CCP STARTING POINTS ; ; START CCP AND DON'T PROCESS DEFAULT COMMAND STORED ; CCP1: XOR A ;SET NO DEFAULT COMMAND LD (CBUFF),A ; ; START CCP AND POSSIBLY PROCESS DEFAULT COMMAND ; ; NOTE ON MODIFICATION BY RGF: BDOS RETURNS 0FFH IN ; ACCUMULATOR WHENEVER IT LOGS IN A DIRECTORY, IF ANY ; FILE NAME CONTAINS A '$' IN IT. THIS IS NOW USED AS ; A CLUE TO DETERMINE WHETHER OR NOT TO DO A SEARCH ; FOR SUBMIT FILE, IN ORDER TO ELIMINATE WASTEFUL SEARCHES. ; CCP: LD SP,STACK ;RESET STACK PUSH BC LD A,C ;C=USER/DISK NUMBER (SEE LOC 4) RRA ;EXTRACT USER NUMBER RRA RRA RRA AND 0FH LD E,A ;SET USER NUMBER CALL SETUSR CALL RESET ;RESET DISK SYSTEM LD (RNGSUB),A ;SAVE SUBMIT CLUE FROM DRIVE A: POP BC LD A,C ;C=USER/DISK NUMBER (SEE LOC 4) AND 0FH ;EXTRACT DEFAULT DISK DRIVE LD (TDRIVE),A ;SET IT JR Z,NOLOG ;SKIP IF 0...ALREADY LOGGED CALL LOGIN ;LOG IN DEFAULT DISK ; IF NOT SUBA ;IF $$$.SUB IS ON CURRENT DRIVE LD (RNGSUB),A ;BDOS '$' CLUE ENDIF ; NOLOG: LD DE,SUBFCB ;CHECK FOR $$$.SUB ON CURRENT DISK RNGSUB EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION LD A,0 ;2ND BYTE (IMMEDIATE ARG) IS THE RNGSUB FLAG OR A ;SET FLAGS ON CLUE CPL ;PREPARE FOR COMING 'CMA' CALL NZ,SEAR1 CPL ;0FFH IS RETURNED IF NO $$$.SUB, SO COMPLEMENT LD (RNGSUB),A ;SET FLAG (0=NO $$$.SUB) LD A,(CBUFF) ;EXECUTE DEFAULT COMMAND? OR A ;0=NO JR NZ,RS1 ; ; PROMPT USER AND INPUT COMMAND LINE FROM HIM ; RESTRT: LD SP,STACK ;RESET STACK ; ; PRINT PROMPT (DU>) ; CALL CRLF ;PRINT PROMPT CALL GETDRV ;CURRENT DRIVE IS PART OF PROMPT ADD A,'A' ;CONVERT TO ASCII A-P CALL CONOUT CALL GETUSR ;GET USER NUMBER ; IF SUPRES ;IF SUPPRESSING USR # REPORT FOR USR 0 OR A JR Z,RS000 ENDIF ; CP 10 ;USER < 10? JR C,RS00 SUB 10 ;SUBTRACT 10 FROM IT PUSH AF ;SAVE IT LD A,'1' ;OUTPUT 10'S DIGIT CALL CONOUT POP AF RS00: ADD A,'0' ;OUTPUT 1'S DIGIT (CONVERT TO ASCII) CALL CONOUT ; ; READ INPUT LINE FROM USER OR $$$.SUB ; RS000: CALL REDBUF ;INPUT COMMAND LINE FROM USER (OR $$$.SUB) ; ; PROCESS INPUT LINE ; RS1: ; IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED CALL CNVBUF ;CAPITALIZE COMMAND LINE, PLACE ENDING 0, ; AND SET CIBPTR VALUE ENDIF ; CALL DEFDMA ;SET TBUFF TO DMA ADDRESS CALL GETDRV ;GET DEFAULT DRIVE NUMBER LD (TDRIVE),A ;SET IT CALL SCANER ;PARSE COMMAND NAME FROM COMMAND LINE CALL NZ,ERROR ;ERROR IF COMMAND NAME CONTAINS A '?' LD DE,RSTCCP ;PUT RETURN ADDRESS OF COMMAND PUSH DE ;ON THE STACK LD A,(TEMPDR) ;IS COMMAND OF FORM 'D:COMMAND'? OR A ;NZ=YES JP NZ,COM ; IMMEDIATELY CALL CMDSER ;SCAN FOR CCP-RESIDENT COMMAND JP NZ,COM ;NOT CCP-RESIDENT LD A,(HL) ;FOUND IT: GET LOW-ORDER PART INC HL ;GET HIGH-ORDER PART LD H,(HL) ;STORE HIGH LD L,A ;STORE LOW JP (HL) ;EXECUTE CCP ROUTINE ; ; ENTRY POINT FOR RESTARTING CCP AND LOGGING IN DEFAULT DRIVE ; RSTCCP: CALL DLOGIN ;LOG IN DEFAULT DRIVE ; ; ENTRY POINT FOR RESTARTING CCP WITHOUT LOGGING IN DEFAULT DRIVE ; RCCPNL: CALL SCANER ;EXTRACT NEXT TOKEN FROM COMMAND LINE LD A,(FCBFN) ;GET FIRST CHAR OF TOKEN SUB ' ' ;ANY CHAR? LD HL,TEMPDR OR (HL) JP NZ,ERROR JR RESTRT ; ; No File Error Message ; PRNNF: CALL PRINTC ;NO FILE MESSAGE DB 'No Fil','e'+80H RET ; ;**** Section 3 **** ; I/O UTILITIES ; ; OUTPUT CHAR IN REG A TO CONSOLE AND DON'T CHANGE BC ; ; ; OUTPUT ; CRLF: LD A,CR CALL CONOUT LD A,LF ;FALL THRU TO CONOUT ; CONOUT: PUSH BC LD C,02H OUTPUT: LD E,A PUSH HL CALL BDOS POP HL POP BC RET ; CONIN: LD C,01H ;GET CHAR FROM CON: WITH ECHO JR BDOSB ; LCOUT: PUSH AF ;OUTPUT CHAR TO CON: OR LST: DEP ON PRFLG PRFLG EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION LD A,0 ;2ND BYTE (IMMEDIATE ARG) IS THE PRINT FLAG OR A ;0=TYPE JR Z,LC1 POP AF ;GET CHAR ; ; OUTPUT CHAR IN REG A TO LIST DEVICE ; LSTOUT: PUSH BC LD C,05H JR OUTPUT LC1: POP AF ;GET CHAR PUSH AF CALL CONOUT ;OUTPUT TO CON: POP AF CP LF ;CHECK FOR PAGING JP Z,PAGER RET ; READF: LD DE,FCBDN ;FALL THRU TO READ READ: LD C,14H ;FALL THRU TO BDOSB ; ; CALL BDOS AND SAVE BC ; BDOSB: PUSH BC CALL BDOS POP BC OR A RET ; ; PRINT STRING (ENDING IN 0) PTED TO BY RET ADR;START WITH ; PRINTC: PUSH AF ;SAVE FLAGS CALL CRLF ;NEW LINE POP AF ; PRINT: EX (SP),HL ;GET PTR TO STRING PUSH AF ;SAVE FLAGS CALL PRIN1 ;PRINT STRING POP AF ;GET FLAGS EX (SP),HL ;RESTORE HL AND RET ADR RET ; ; PRINT STRING (ENDING IN 0) PTED TO BY HL ; PRIN1: LD A,(HL) ;GET NEXT BYTE CALL CONOUT ;PRINT CHAR LD A,(HL) ;GET NEXT BYTE AGAIN FOR TEST INC HL ;PT TO NEXT BYTE OR A ;SET FLAGS RET Z ;DONE IF ZERO RET M ;DONE IF MSB SET JR PRIN1 ; ; BDOS FUNCTION ROUTINES ; ; ; RETURN NUMBER OF CURRENT DISK IN A ; GETDRV: LD C,19H JR BDOSJP ; ; SET 80H AS DMA ADDRESS ; DEFDMA: LD DE,TBUFF ;80H=TBUFF DMASET: LD C,1AH JR BDOSJP ; RESET: LD C,0DH BDOSJP: JP BDOS ; LOGIN: LD E,A LD C,0EH JR BDOSJP ;SAVE SOME CODE SPACE ; OPENF: XOR A LD (FCBCR),A LD DE,FCBDN ;FALL THRU TO OPEN ; OPEN: LD C,0FH ;FALL THRU TO GRBDOS ; GRBDOS: CALL BDOS INC A ;SET ZERO FLAG FOR ERROR RETURN RET ; CLOSE: LD C,10H JR GRBDOS ; SEARF: LD DE,FCBDN ;SPECIFY FCB SEAR1: LD C,11H JR GRBDOS ; SEARN: LD C,12H JR GRBDOS ; ; CHECK FOR SUBMIT FILE IN EXECUTION AND ABORT IT IF SO ; SUBKIL: LD HL,RNGSUB ;CHECK FOR SUBMIT FILE IN EXECUTION LD A,(HL) OR A ;0=NO RET Z LD (HL),0 ;ABORT SUBMIT FILE LD DE,SUBFCB ;DELETE $$$.SUB ; DELETE: LD C,13H JR BDOSJP ;SAVE MORE SPACE ; WRITE: LD C,15H JP BDOSB ; CREATE: LD C,16H JR GRBDOS ; ; RESET USER NUMBER IF CHANGED ; RESETUSR: TMPUSR EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION LD A,0 ;2ND BYTE (IMMEDIATE ARG) IS TMPUSR LD E,A ;PLACE IN E JR SETUSR ;THEN GO SET USER GETUSR: LD E,0FFH ;GET CURRENT USER NUMBER SETUSR: LD C,20H ;SET USER NUMBER TO VALUE IN E (GET IF E=FFH) JR BDOSJP ;MORE SPACE SAVING ; ; END OF BDOS FUNCTIONS ; ; ;**** Section 4 **** ; CCP UTILITIES ; ; SET USER/DISK FLAG TO CURRENT USER AND DEFAULT DISK ; SETUD: CALL GETUSR ;GET NUMBER OF CURRENT USER ADD A,A ;PLACE IT IN HIGH NYBBLE ADD A,A ADD A,A ADD A,A LD HL,TDRIVE ;MASK IN DEFAULT DRIVE NUMBER (LOW NYBBLE) OR (HL) ;MASK IN LD (UDFLAG),A ;SET USER/DISK NUMBER RET ; ; SET USER/DISK FLAG TO USER 0 AND DEFAULT DISK ; SETU0D: TDRIVE EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION LD A,0 ;2ND BYTE (IMMEDIATE ARG) IS TDRIVE LD (UDFLAG),A ;SET USER/DISK NUMBER RET ; ; CONVERT CHAR IN A TO UPPER CASE ; UCASE: CP 61H ;LOWER-CASE A RET C CP 7BH ;GREATER THAN LOWER-CASE Z? RET NC AND 5FH ;CAPITALIZE RET ; ; INPUT NEXT COMMAND TO CCP ; This routine determines if a SUBMIT file is being processed ; and extracts the command line from it if so or from the user's console ; REDBUF: LD A,(RNGSUB) ;SUBMIT FILE CURRENTLY IN EXECUTION? OR A ;0=NO JR Z,RB1 ;GET LINE FROM CONSOLE IF NOT LD DE,SUBFCB ;OPEN $$$.SUB PUSH DE ;SAVE DE CALL OPEN POP DE ;RESTORE DE JR Z,RB1 ;ERASE $$$.SUB IF END OF FILE AND GET CMND LD A,(SUBFRC) ;GET VALUE OF LAST RECORD IN FILE DEC A ;PT TO NEXT TO LAST RECORD LD (SUBFCR),A ;SAVE NEW VALUE OF LAST RECORD IN $$$.SUB CALL READ ;DE=SUBFCB JR NZ,RB1 ;ABORT $$$.SUB IF ERROR IN READING LAST REC LD DE,CBUFF ;COPY LAST RECORD (NEXT SUBMIT CMND) TO CBUFF LD HL,TBUFF ; FROM TBUFF LD BC,BUFLEN ;NUMBER OF BYTES LDIR LD HL,SUBFS2 ;PT TO S2 OF $$$.SUB FCB LD (HL),0 ;SET S2 TO ZERO INC HL ;PT TO RECORD COUNT DEC (HL) ;DECREMENT RECORD COUNT OF $$$.SUB LD DE,SUBFCB ;CLOSE $$$.SUB CALL CLOSE JR Z,RB1 ;ABORT $$$.SUB IF ERROR LD A,SPRMPT ;PRINT SUBMIT PROMPT CALL CONOUT LD HL,CIBUFF ;PRINT COMMAND LINE FROM $$$.SUB CALL PRIN1 CALL BREAK ;CHECK FOR ABORT (ANY CHAR) ; IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED RET Z ;IF (NO ABORT), RETURN TO CALLER AND RUN ENDIF ; IF NOT CLEVEL3 ;IF THIRD COMMAND LEVEL IS NOT PERMITTED JR Z,CNVBUF ;IF (NO ABORT), CAPITALIZE COMMAND ENDIF ; CALL SUBKIL ;KILL $$$.SUB IF ABORT JP RESTRT ;RESTART CCP ; ; INPUT COMMAND LINE FROM USER CONSOLE ; RB1: CALL SUBKIL ;ERASE $$$.SUB IF PRESENT CALL SETUD ;SET USER AND DISK LD A,CPRMPT ;PRINT PROMPT CALL CONOUT LD C,0AH ;READ COMMAND LINE FROM USER LD DE,MBUFF CALL BDOS ; IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED JP SETU0D ;SET CURRENT DISK NUMBER IN LOWER PARAMS ENDIF ; IF NOT CLEVEL3 ;IF THIRD COMMAND LEVEL IS NOT PERMITTED CALL SETU0D ;SET CURRENT DISK NUMBER IF LOWER PARAMS ; AND FALL THRU TO CNVBUF ENDIF ; ; CAPITALIZE STRING (ENDING IN 0) IN CBUFF AND SET PTR FOR PARSING ; CNVBUF: LD HL,CBUFF ;PT TO USER'S COMMAND LD B,(HL) ;CHAR COUNT IN B INC B ;ADD 1 IN CASE OF ZERO CB1: INC HL ;PT TO 1ST VALID CHAR LD A,(HL) ;CAPITALIZE COMMAND CHAR CALL UCASE LD (HL),A DJNZ CB1 ;CONTINUE TO END OF COMMAND LINE CB2: LD (HL),0 ;STORE ENDING LD HL,CIBUFF ;SET COMMAND LINE PTR TO 1ST CHAR LD (CIBPTR),HL RET ; ; CHECK FOR ANY CHAR FROM USER CONSOLE;RET W/ZERO SET IF NONE ; BREAK: PUSH DE ;SAVE DE LD C,11 ;CSTS CHECK CALL BDOSB CALL NZ,CONIN ;GET INPUT CHAR BRKBK: POP DE RET ; ; GET THE REQUESTED USER NUMBER FROM THE COMMAND LINE AND VALIDATE IT. ; USRNUM: CALL NUMBER CP MAXUSR+1 RET C ; ; INVALID COMMAND -- PRINT IT ; ERROR: CALL CRLF ;NEW LINE LD HL,(CIPTR) ;PT TO BEGINNING OF COMMAND LINE ERR2: LD A,(HL) ;GET CHAR CP ' '+1 ;SIMPLE '?' IF OR LESS JR C,ERR1 PUSH HL ;SAVE PTR TO ERROR COMMAND CHAR CALL CONOUT ;PRINT COMMAND CHAR POP HL ;GET PTR INC HL ;PT TO NEXT JR ERR2 ;CONTINUE ERR1: CALL PRINT ;PRINT '?' DB '?'+80H CALL SUBKIL ;TERMINATE ACTIVE $$$.SUB IF ANY JP RESTRT ;RESTART CCP ; ; CHECK TO SEE IF DE PTS TO DELIMITER; IF SO, RET W/ZERO FLAG SET ; SDELM: LD A,(DE) OR A ;0=DELIMITER RET Z CP ' ' ;ERROR IF < JR C,ERROR RET Z ;=DELIMITER CP '=' ;'='=DELIMITER RET Z CP 5FH ;UNDERSCORE=DELIMITER RET Z CP '.' ;'.'=DELIMITER RET Z CP ':' ;':'=DELIMITER RET Z CP ';' ;';'=DELIMITER RET Z CP '<' ;'<'=DELIMITER RET Z CP '>' ;'>'=DELIMITER RET ; ; ADVANCE INPUT PTR TO FIRST NON-BLANK AND FALL THROUGH TO SBLANK ; ADVAN: LD DE,(CIBPTR) ; ; SKIP STRING PTED TO BY DE (STRING ENDS IN 0) UNTIL END OF STRING ; OR NON-BLANK ENCOUNTERED (BEGINNING OF TOKEN) ; SBLANK: LD A,(DE) OR A RET Z CP ' ' RET NZ INC DE JR SBLANK ; ; ADD A TO HL (HL=HL+A) ; ADDAH: ADD A,L LD L,A RET NC INC H RET ; ; EXTRACT DECIMAL NUMBER FROM COMMAND LINE ; RETURN WITH VALUE IN REG A;ALL REGISTERS MAY BE AFFECTED ; NUMBER: CALL SCANER ;PARSE NUMBER AND PLACE IN FCBFN LD HL,FCBFN+10 ;PT TO END OF TOKEN FOR CONVERSION LD B,11 ;11 CHARS MAX ; ; CHECK FOR SUFFIX FOR HEXADECIMAL NUMBER ; NUMS: LD A,(HL) ;GET CHARS FROM END, SEARCHING FOR SUFFIX DEC HL ;BACK UP CP ' ' ;SPACE? JR NZ,NUMS1 ;CHECK FOR SUFFIX DJNZ NUMS ;COUNT DOWN JR NUM0 ;BY DEFAULT, PROCESS NUMS1: CP NUMBASE ;CHECK AGAINST BASE SWITCH FLAG JR Z,HNUM0 ; ; PROCESS DECIMAL NUMBER ; NUM0: LD HL,FCBFN ;PT TO BEGINNING OF TOKEN LD BC,1100H ;C=ACCUMULATED VALUE, B=CHAR COUNT ; (C=0, B=11) NUM1: LD A,(HL) ;GET CHAR CP ' ' ;DONE IF JR Z,NUM2 INC HL ;PT TO NEXT CHAR SUB '0' ;CONVERT TO BINARY (ASCII 0-9 TO BINARY) CP 10 ;ERROR IF >= 10 JR NC,NUMERR LD D,A ;DIGIT IN D LD A,C ;NEW VALUE = OLD VALUE * 10 RLCA RLCA RLCA ADD A,C ;CHECK FOR RANGE ERROR JR C,NUMERR ADD A,C ;CHECK FOR RANGE ERROR JR C,NUMERR ADD A,D ;NEW VALUE = OLD VALUE * 10 + DIGIT JR C,NUMERR ;CHECK FOR RANGE ERROR LD C,A ;SET NEW VALUE DJNZ NUM1 ;COUNT DOWN ; ; RETURN FROM NUMBER ; NUM2: LD A,C ;GET ACCUMULATED VALUE RET ; ; NUMBER ERROR ROUTINE FOR SPACE CONSERVATION ; NUMERR: JP ERROR ;USE ERROR ROUTINE - THIS IS RELATIVE PT ; ; EXTRACT HEXADECIMAL NUMBER FROM COMMAND LINE ; RETURN WITH VALUE IN REG A; ALL REGISTERS MAY BE AFFECTED ; HEXNUM: CALL SCANER ;PARSE NUMBER AND PLACE IN FCBFN HNUM0: LD HL,FCBFN ;PT TO TOKEN FOR CONVERSION LD DE,0 ;DE=ACCUMULATED VALUE LD B,11 ;B=CHAR COUNT HNUM1: LD A,(HL) ;GET CHAR CP ' ' ;DONE? JR Z,HNUM3 ;RETURN IF SO CP 'H' ;DONE IF H SUFFIX JR Z,HNUM3 SUB '0' ;CONVERT TO BINARY JR C,NUMERR ;RETURN AND DONE IF ERROR CP 10 ;0-9? JR C,HNUM2 SUB 7 ;A-F? CP 10H ;ERROR? JR NC,NUMERR HNUM2: INC HL ;PT TO NEXT CHAR LD C,A ;DIGIT IN C LD A,D ;GET ACCUMULATED VALUE RLCA ;EXCHANGE NYBBLES RLCA RLCA RLCA AND 0F0H ;MASK OUT LOW NYBBLE LD D,A LD A,E ;SWITCH LOW-ORDER NYBBLES RLCA RLCA RLCA RLCA LD E,A ;HIGH NYBBLE OF E=NEW HIGH OF E, ; LOW NYBBLE OF E=NEW LOW OF D AND 0FH ;GET NEW LOW OF D OR D ;MASK IN HIGH OF D LD D,A ;NEW HIGH BYTE IN D LD A,E AND 0F0H ;MASK OUT LOW OF E OR C ;MASK IN NEW LOW LD E,A ;NEW LOW BYTE IN E DJNZ HNUM1 ;COUNT DOWN ; ; RETURN FROM HEXNUM ; HNUM3: EX DE,HL ;RETURNED VALUE IN HL LD A,L ;LOW-ORDER BYTE IN A RET ; ; PT TO DIRECTORY ENTRY IN TBUFF WHOSE OFFSET IS SPECIFIED BY A AND C ; DIRPTR: LD HL,TBUFF ;PT TO TEMP BUFFER ADD A,C ;PT TO 1ST BYTE OF DIR ENTRY CALL ADDAH ;PT TO DESIRED BYTE IN DIR ENTRY LD A,(HL) ;GET DESIRED BYTE RET ; ; CHECK FOR SPECIFIED DRIVE AND LOG IT IN IF NOT DEFAULT ; SLOGIN: XOR A ;SET FCBDN FOR DEFAULT DRIVE LD (FCBDN),A CALL COMLOG ;CHECK DRIVE RET Z JR DLOG5 ;DO LOGIN OTHERWISE ; ; CHECK FOR SPECIFIED DRIVE AND LOG IN DEFAULT DRIVE IF SPECIFIED<>DEFAULT ; DLOGIN: CALL COMLOG ;CHECK DRIVE RET Z ;ABORT IF SAME LD A,(TDRIVE) ;LOG IN DEFAULT DRIVE ; DLOG5: JP LOGIN ; ; ROUTINE COMMON TO BOTH LOGIN ROUTINES; ON EXIT, Z SET MEANS ABORT ; COMLOG: TEMPDR EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION LD A,0 ;2ND BYTE (IMMEDIATE ARG) IS TEMPDR OR A ;0=NO RET Z DEC A ;COMPARE IT AGAINST DEFAULT LD HL,TDRIVE CP (HL) RET ;ABORT IF SAME ; ; EXTRACT TOKEN FROM COMMAND LINE AND PLACE IT INTO FCBDN; ; FORMAT FCBDN FCB IF TOKEN RESEMBLES FILE NAME AND TYPE (FILENAME.TYP); ; ON INPUT, CIBPTR PTS TO CHAR AT WHICH TO START SCAN; ; ON OUTPUT, CIBPTR PTS TO CHAR AT WHICH TO CONTINUE AND ZERO FLAG IS RESET ; IF '?' IS IN TOKEN ; SCANER: XOR A ;A=0 TO START AT DRIVE SPECIFICATION BYTE SCAN1: LD HL,FCBDN ;POINT TO FCBDN CALL ADDAH ;OFFSET INTO FCB PUSH HL PUSH HL XOR A ;SET TEMPORARY DRIVE NUMBER TO DEFAULT LD (TEMPDR),A CALL ADVAN ;SKIP TO NON-BLANK OR END OF LINE LD (CIPTR),DE ;SET PTR TO NON-BLANK OR END OF LINE POP HL ;GET PTR TO NEXT BYTE IN FCBDN LD A,(DE) ;END OF LINE? OR A ;0=YES JR Z,SCAN2 SBC A,'A'-1 ;CONVERT POSSIBLE DRIVE SPEC TO NUMBER LD B,A ;STORE NUMBER (A:=0, B:=1, ETC) IN B INC DE ;PT TO NEXT CHAR LD A,(DE) ;SEE IF IT IS A COLON (:) CP ':' JR Z,SCAN3 ;YES, WE HAVE A DRIVE SPEC DEC DE ;NO, BACK UP PTR TO FIRST NON-BLANK CHAR SCAN2: LD A,(TDRIVE) ;SET 1ST BYTE OF FCBDN AS DEFAULT DRIVE LD (HL),A JR SCAN4 SCAN3: LD A,B ;WE HAVE A DRIVE SPEC LD (TEMPDR),A ;SET TEMPORARY DRIVE LD (HL),B ;SET 1ST BYTE OF FCBDN AS SPECIFIED DRIVE INC DE ;PT TO BYTE AFTER ':' ; ; EXTRACT FILENAME FROM POSSIBLE FILENAME.TYP ; SCAN4: LD B,8 ;MAX OF 8 CHARS IN FILE NAME SCAN5: CALL SDELM ;DONE IF DELIMITER ENCOUNTERED - FILL JR Z,SCAN9 INC HL ;PT TO NEXT BYTE IN FCBDN CP '*' ;IS (DE) A WILD CARD? JR NZ,SCAN6 ;CONTINUE IF NOT LD (HL),'?' ;PLACE '?' IN FCBDN AND DON'T ADVANCE DE IF SO JR SCAN7 SCAN6: LD (HL),A ;STORE FILENAME CHAR IN FCBDN INC DE ;PT TO NEXT CHAR IN COMMAND LINE SCAN7: DJNZ SCAN5 ;DECREMENT CHAR COUNT UNTIL 8 ELAPSED SCAN8: CALL SDELM ;8 CHARS OR MORE - SKIP UNTIL DELIMITER JR Z,SCAN10 ;ZERO FLAG SET IF DELIMITER FOUND INC DE ;PT TO NEXT CHAR IN COMMAND LINE JR SCAN8 SCAN9: INC HL ;PT TO NEXT BYTE IN FCBDN LD (HL),' ' ;FILL FILENAME PART WITH DJNZ SCAN9 ; ; EXTRACT FILE TYPE FROM POSSIBLE FILENAME.TYP ; SCAN10: LD B,3 ;PREPARE TO EXTRACT TYPE CP '.' ;IF (DE) DELIMITER IS A '.', WE HAVE A TYPE JR NZ,SCAN15 ;FILL FILE TYPE BYTES WITH INC DE ;PT TO CHAR IN COMMAND LINE AFTER '.' SCAN11: CALL SDELM ;CHECK FOR DELIMITER JR Z,SCAN15 ;FILL REST OF TYPE IF IT IS A DELIMITER INC HL ;PT TO NEXT BYTE IN FCBDN CP '*' ;WILD? JR NZ,SCAN12 ;STORE CHAR IF NOT WILD LD (HL),'?' ;STORE '?' AND DON'T ADVANCE COMMAND LINE PTR JR SCAN13 SCAN12: LD (HL),A ;STORE CHAR IN FCBDN INC DE ;PT TO NEXT CHAR IN COMMAND LINE SCAN13: DJNZ SCAN11 ;COUNT DOWN CHARS IN FILE TYPE (3 MAX) SCAN14: CALL SDELM ;SKIP REST OF CHARS AFTER 3-CHAR TYPE TO JR Z,SCAN16 ; DELIMITER INC DE JR SCAN14 SCAN15: INC HL ;FILL IN REST OF TYP WITH LD (HL),' ' DJNZ SCAN15 ; ; FILL IN EX, S1, S2, AND RC WITH ZEROES ; SCAN16: LD B,4 ;4 BYTES SCAN17: INC HL ;PT TO NEXT BYTE IN FCBDN LD (HL),0 DJNZ SCAN17 ; ; SCAN COMPLETE -- DE PTS TO DELIMITER BYTE AFTER TOKEN ; LD (CIBPTR),DE ; ; SET ZERO FLAG TO INDICATE PRESENCE OF '?' IN FILENAME.TYP ; POP HL ;GET PTR TO FCBDN IN HL LD BC,11 ;SCAN FOR '?' IN FILENAME.TYP (C=11 BYTES) SCAN18: INC HL ;PT TO NEXT BYTE IN FCBDN LD A,(HL) CP '?' JR NZ,SCAN19 INC B ;B<>0 TO INDICATE '?' ENCOUNTERED SCAN19: DEC C ;COUNT DOWN JR NZ,SCAN18 LD A,B ;A=B=NUMBER OF '?' IN FILENAME.TYP OR A ;SET ZERO FLAG TO INDICATE ANY '?' RET ; ; CMDTBL (COMMAND TABLE) SCANNER ; ON RETURN, HL PTS TO ADDRESS OF COMMAND IF CCP-RESIDENT ; ON RETURN, ZERO FLAG SET MEANS CCP-RESIDENT COMMAND ; CMDSER: LD HL,CMDTBL ;PT TO COMMAND TABLE LD C,NCMNDS ;SET COMMAND COUNTER CMS1: LD DE,FCBFN ;PT TO STORED COMMAND NAME LD B,NCHARS ;NUMBER OF CHARS/COMMAND (8 MAX) CMS2: LD A,(DE) ;COMPARE AGAINST TABLE ENTRY CP (HL) JR NZ,CMS3 ;NO MATCH INC DE ;PT TO NEXT CHAR INC HL DJNZ CMS2 ;COUNT DOWN LD A,(DE) ;NEXT CHAR IN INPUT COMMAND MUST BE CP ' ' JR NZ,CMS4 RET ;COMMAND IS CCP-RESIDENT (ZERO FLAG SET) CMS3: INC HL ;SKIP TO NEXT COMMAND TABLE ENTRY DJNZ CMS3 CMS4: INC HL ;SKIP ADDRESS INC HL DEC C ;DECREMENT TABLE ENTRY NUMBER JR NZ,CMS1 INC C ;CLEAR ZERO FLAG RET ;COMMAND IS DISK-RESIDENT (ZERO FLAG CLEAR) ; ;**** Section 5 **** ; CCP-Resident Commands ; ; ;Section 5A ;Command: DIR ;Function: To display a directory of the files on disk ;Forms: ; DIR Displays the DIR files ; DIR S Displays the SYS files ; DIR A Display both DIR and SYS files ; DIR: LD A,80H ;SET SYSTEM BIT EXAMINATION PUSH AF CALL SCANER ;EXTRACT POSSIBLE D:FILENAME.TYP TOKEN CALL SLOGIN ;LOG IN DRIVE IF NECESSARY LD HL,FCBFN ;MAKE FCB WILD (ALL '?') IF NO FILENAME.TYP LD A,(HL) ;GET FIRST CHAR OF FILENAME.TYP CP ' ' ;IF , ALL WILD CALL Z,FILLQ CALL ADVAN ;LOOK AT NEXT INPUT CHAR LD B,0 ;SYS TOKEN DEFAULT JR Z,DIR2 ;JUMP; THERE ISN'T ONE CP SYSFLG ;SYSTEM FLAG SPECIFIER? JR Z,GOTSYS ;GOT SYSTEM SPECIFIER CP SOFLG ;SYS ONLY? JR NZ,DIR2 LD B,80H ;FLAG SYS ONLY GOTSYS: INC DE LD (CIBPTR),DE CP SOFLG ;SYS ONLY SPEC? JR Z,DIR2 ;THEN LEAVE BIT SPEC UNCHAGNED POP AF ;GET FLAG XOR A ;SET NO SYSTEM BIT EXAMINATION PUSH AF DIR2: POP AF ;GET FLAG DIR2A: ;DROP INTO DIRPR TO PRINT DIRECTORY ; THEN RESTART CCP ; ; DIRECTORY PRINT ROUTINE; ON ENTRY, MSB OF A IS 1 (80H) IF SYSTEM FILES EXCL ; DIRPR: LD D,A ;STORE SYSTEM FLAG IN D LD E,0 ;SET COLUMN COUNTER TO ZERO PUSH DE ;SAVE COLUMN COUNTER (E) AND SYSTEM FLAG (D) LD A,B ;SYS ONLY SPECIFIER LD (SYSTST),A CALL SEARF ;SEARCH FOR SPECIFIED FILE (FIRST OCCURRANCE) CALL Z,PRNNF ;PRINT NO FILE MSG;REG A NOT CHANGED ; ; ENTRY SELECTION LOOP; ON ENTRY, A=OFFSET FROM SEARF OR SEARN ; DIR3: JR Z,DIR11 ;DONE IF ZERO FLAG SET DEC A ;ADJUST TO RETURNED VALUE RRCA ;CONVERT NUMBER TO OFFSET INTO TBUFF RRCA RRCA AND 60H LD C,A ;OFFSET INTO TBUFF IN C (C=OFFSET TO ENTRY) LD A,10 ;ADD 10 TO PT TO SYSTEM FILE ATTRIBUTE BIT CALL DIRPTR POP DE ;GET SYSTEM BIT MASK FROM D PUSH DE AND D ;MASK FOR SYSTEM BIT SYSTST EQU $+1 ;POINTER TO IN-THE-CODE BUFFER SYSTST CP 0 JR NZ,DIR10 POP DE ;GET ENTRY COUNT (= COUNTER) LD A,E ;ADD 1 TO IT INC E PUSH DE ;SAVE IT ; IF TWOCOL AND 01H ;OUTPUT IF 2 ENTRIES PRINTED IN LINE ENDIF ; IF NOT TWOCOL AND 03H ;OUTPUT IF 4 ENTRIES PRINTED IN LINE ENDIF ; PUSH AF JR NZ,DIR4 CALL CRLF ;NEW LINE JR DIR5 DIR4: CALL PRINT ; IF WIDE DB ' ' ;2 SPACES DB FENCE ;THEN FENCE CHAR DB ' ',' '+80H ;THEN 2 MORE SPACES ENDIF ; IF NOT WIDE DB ' ' ;SPACE DB FENCE ;THEN FENCE CHAR DB ' '+80H ;THEN SPACE ENDIF ; DIR5: LD B,01H ;PT TO 1ST BYTE OF FILE NAME DIR6: LD A,B ;A=OFFSET CALL DIRPTR ;HL NOW PTS TO 1ST BYTE OF FILE NAME AND 7FH ;MASK OUT MSB CP ' ' ;NO FILE NAME? JR NZ,DIR8 ;PRINT FILE NAME IF PRESENT POP AF PUSH AF CP 03H JR NZ,DIR7 LD A,09H ;PT TO 1ST BYTE OF FILE TYPE CALL DIRPTR ;HL NOW PTS TO 1ST BYTE OF FILE TYPE AND 7FH ;MASK OUT MSB CP ' ' ;NO FILE TYPE? JR Z,DIR9 ;CONTINUE IF SO DIR7: LD A,' ' ;OUTPUT DIR8: CALL CONOUT ;PRINT CHAR INC B ;INCR CHAR COUNT LD A,B CP 12 ;END OF FILENAME.TYP? JR NC,DIR9 ;CONTINUE IF SO CP 09H ;END IF FILENAME ONLY? JR NZ,DIR6 ;PRINT TYP IF SO LD A,'.' ;PRINT DOT BETWEEN FILE NAME AND TYPE CALL CONOUT JR DIR6 DIR9: POP AF DIR10: CALL BREAK ;CHECK FOR ABORT JR NZ,DIR11 CALL SEARN ;SEARCH FOR NEXT FILE JR DIR3 ;CONTINUE DIR11: POP DE ;RESTORE STACK RET ; ; FILL FCB @HL WITH '?' ; FILLQ: LD B,11 ;NUMBER OF CHARS IN FN & FT FQLP: LD (HL),'?' ;STORE '?' INC HL DJNZ FQLP RET ; ;Section 5B ;Command: ERA ;Function: Erase files ;Forms: ; ERA Erase Specified files and print their names ; IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM ; ERA: CALL SCANER ;PARSE FILE SPECIFICATION CP 11 ;ALL WILD (ALL FILES = 11 '?')? JR NZ,ERA1 ;IF NOT, THEN DO ERASES CALL PRINTC DB 'All','?'+80H CALL CONIN ;GET REPLY CALL UCASE ;CAPITALIZE CP 'Y' ;YES? JP NZ,RESTRT ;RESTART CCP IF NOT CALL CRLF ;NEW LINE ERA1: CALL SLOGIN ;LOG IN SELECTED DISK IF ANY XOR A ;PRINT ALL FILES (EXAMINE SYSTEM BIT) LD B,A ;NO SYS-ONLY OPT TO DIRPR CALL DIRPR ;PRINT DIRECTORY OF ERASED FILES LD DE,FCBDN ;DELETE FILE SPECIFIED CALL DELETE RET ;REENTER CCP ; ENDIF ;RAS ; ;Section 5C ;Command: LIST ;Function: Print out specified file on the LST: Device ;Forms: ; LIST Print file (NO Paging) ; LIST: LD A,0FFH ;TURN ON PRINTER FLAG JR TYPE0 ; ;Section 5D ;Command: TYPE ;Function: Print out specified file on the CON: Device ;Forms: ; TYPE Print file ; TYPE P Print file with paging flag ; TYPE: XOR A ;TURN OFF PRINTER FLAG ; ; ENTRY POINT FOR CCP LIST FUNCTION (LIST) ; TYPE0: LD (PRFLG),A ;SET FLAG CALL SCANER ;EXTRACT FILENAME.TYP TOKEN JP NZ,ERROR ;ERROR IF ANY QUESTION MARKS CALL ADVAN ;GET PGDFLG IF IT'S THERE LD (PGFLG),A ;SAVE IT AS A FLAG JR Z,NOSLAS ;JUMP IF INPUT ENDED INC DE ;PUT NEW BUF POINTER EX DE,HL LD (CIBPTR),HL NOSLAS: CALL SLOGIN ;LOG IN SELECTED DISK IF ANY CALL OPENF ;OPEN SELECTED FILE JP Z,TYPE4 ;ABORT IF ERROR CALL CRLF ;NEW LINE LD A,NLINES-1 ;SET LINE COUNT LD (PAGCNT),A LD HL,CHRCNT ;SET CHAR POSITION/COUNT LD (HL),0FFH ;EMPTY LINE LD B,0 ;SET TAB CHAR COUNTER TYPE1: LD HL,CHRCNT ;PT TO CHAR POSITION/COUNT LD A,(HL) ;END OF BUFFER? CP 80H JR C,TYPE2 PUSH HL ;READ NEXT BLOCK CALL READF POP HL JR NZ,TYPE3 ;ERROR? XOR A ;RESET COUNT LD (HL),A TYPE2: INC (HL) ;INCREMENT CHAR COUNT LD HL,TBUFF ;PT TO BUFFER CALL ADDAH ;COMPUTE ADDRESS OF NEXT CHAR FROM OFFSET LD A,(HL) ;GET NEXT CHAR AND 7FH ;MASK OUT MSB CP 1AH ;END OF FILE (^Z)? RET Z ;RESTART CCP IF SO ; ; OUTPUT CHAR TO CON: OR LST: DEVICE WITH TABULATION ; CP CR ;RESET TAB COUNT? JR Z,TABRST CP LF ;RESET TAB COUNT? JR Z,TABRST CP TAB ;TAB? JR Z,LTAB CALL LCOUT ;OUTPUT CHAR INC B ;INCREMENT CHAR COUNT JR TYPE2L TABRST: CALL LCOUT ;OUTPUT OR LD B,0 ;RESET TAB COUNTER JR TYPE2L LTAB: LD A,' ' ; CALL LCOUT INC B ;INCR POS COUNT LD A,B AND 7 JR NZ,LTAB ; ; CONTINUE PROCESSING ; TYPE2L: CALL BREAK ;CHECK FOR ABORT JR Z,TYPE1 ;CONTINUE IF NO CHAR CP 'C'-'@' ;^C? RET Z ;RESTART IF SO JR TYPE1 TYPE3: DEC A ;NO ERROR? RET Z ;RESTART CCP TYPE4: JP ERRLOG ; ; PAGING ROUTINES ; PAGER COUNTS DOWN LINES AND PAUSES FOR INPUT (DIRECT) IF COUNT EXPIRES ; PAGSET SETS LINES/PAGE COUNT ; PAGER: PUSH HL LD HL,PAGCNT ;COUNT DOWN DEC (HL) JR NZ,PGBAK ;JUMP IF NOT END OF PAGE LD (HL),NLINES-2 ;REFILL COUNTER ; PGFLG EQU $+1 ;POINTER TO IN-THE-CODE BUFFER PGFLG LD A,0 ;0 MAY BE CHANGED BY PGFLG EQUATE CP PGDFLG ;PAGE DEFAULT OVERRIDE OPTION WANTED? ; IF PGDFLT ;IF PAGING IS DEFAULT JR Z,PGBAK ; PGDFLG MEANS NO PAGING, PLEASE ELSE ;IF PAGING NOT DEFAULT JR NZ,PGBAK ; PGDFLG MEANS PLEASE PAGINATE ENDIF ; CALL CONIN ;GET CHAR TO CONTINUE CP 'C'-'@' ;^C JP Z,RSTCCP ;RESTART CCP PGBAK: POP HL ;RESTORE HL RET ; ;Section 5E ;Command: SAVE ;Function: To save the contents of the TPA onto disk as a file ;Forms: ; SAVE ; Save specified number of pages (start at 100H) ; from TPA into specified file; is in DEC ; SAVE S ; Like SAVE above, but numeric argument specifies ; number of sectors rather than pages ; IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM ; SAVE: CALL NUMBER ;EXTRACT NUMBER FROM COMMAND LINE PUSH AF ;SAVE IT CALL SCANER ;EXTRACT FILENAME.TYPE JP NZ,ERROR ;MUST BE NO '?' IN IT CALL SLOGIN ;LOG IN SELECTED DISK LD DE,FCBDN ;DELETE FILE IN CASE IT ALREADY EXISTS PUSH DE CALL DELETE POP DE CALL CREATE ;MAKE NEW FILE JR Z,SAVE3 ;ERROR? XOR A ;SET RECORD COUNT FIELD OF NEW FILE'S FCB LD (FCBCR),A POP AF ;GET PAGE COUNT LD L,A ;HL=PAGE COUNT LD H,0 PUSH HL CALL ADVAN ;LOOK FOR 'S' FOR SECTOR OPTION INC DE ;PT TO AFTER 'S' TOKEN POP HL CP SECTFLG JR Z,SAVE0 DEC DE ;NO 'S' TOKEN, SO BACK UP ADD HL,HL ;DOUBLE IT FOR HL=SECTOR (128 BYTES) COUNT SAVE0: LD (CIBPTR),DE ;SET PTR TO BAD TOKEN OR AFTER GOOD TOKEN LD DE,TPA ;PT TO START OF SAVE AREA (TPA) SAVE1: LD A,H ;DONE WITH SAVE? OR L ;HL=0 IF SO JR Z,SAVE2 DEC HL ;COUNT DOWN ON SECTORS PUSH HL ;SAVE PTR TO BLOCK TO SAVE LD HL,128 ;128 BYTES PER SECTOR ADD HL,DE ;PT TO NEXT SECTOR PUSH HL ;SAVE ON STACK CALL DMASET ;SET DMA ADDRESS FOR WRITE (ADDRESS IN DE) LD DE,FCBDN ;WRITE SECTOR CALL WRITE POP DE ;GET PTR TO NEXT SECTOR IN DE POP HL ;GET SECTOR COUNT JR NZ,SAVE3 ;WRITE ERROR? JR SAVE1 ;CONTINUE SAVE2: LD DE,FCBDN ;CLOSE SAVED FILE CALL CLOSE INC A ;ERROR? JR NZ,SAVE4 SAVE3: CALL PRNLE ;PRINT 'NO SPACE' ERROR SAVE4: CALL DEFDMA ;SET DMA TO 0080 RET ;RESTART CCP ; ENDIF ;RAS ; ;Section 5F ;Command: REN ;Function: To change the name of an existing file ;Forms: ; REN = Perform function ; IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM ; REN: CALL SCANER ;EXTRACT FILE NAME JP NZ,ERROR ;ERROR IF ANY '?' IN IT LD A,(TEMPDR) ;SAVE CURRENT DEFAULT DISK PUSH AF CALL SLOGIN ;LOG IN SELECTED DISK CALL SEARF ;LOOK FOR SPECIFIED FILE JR Z,REN0 ;CONTINUE IF NOT FOUND CALL PRINTC DB 'File Exist','s'+80H POP AF ;CLEAR STACK RET ;RESTART CCP REN0: LD HL,FCBDN ;SAVE NEW FILE NAME LD DE,FCBDM LD BC,16 ;16 BYTES LDIR CALL ADVAN ;ADVANCE CIBPTR CP '=' ;'=' OK JR NZ,REN4 REN1: EX DE,HL ;PT TO CHAR AFTER '=' IN HL INC HL LD (CIBPTR),HL ;SAVE PTR TO OLD FILE NAME CALL SCANER ;EXTRACT FILENAME.TYP TOKEN JR NZ,REN4 ;ERROR IF ANY '?' POP AF ;GET OLD DEFAULT DRIVE LD B,A ;SAVE IT LD HL,TEMPDR ;COMPARE IT AGAINST CURRENT DEFAULT DRIVE LD A,(HL) ;MATCH? OR A JR Z,REN2 CP B ;CHECK FOR DRIVE ERROR LD (HL),B JR NZ,REN4 REN2: LD (HL),B XOR A LD (FCBDN),A ;SET DEFAULT DRIVE LD DE,FCBDN ;RENAME FILE LD C,17H ;BDOS RENAME FCT CALL GRBDOS RET NZ REN3: CALL PRNNF ;PRINT NO FILE MSG REN4: JP ERRLOG ; ENDIF ;RAS ; ;Section 5G ;Command: USER ;Function: Change current USER number ;Forms: ; USER Select specified user number; is in DEC ; USER: CALL USRNUM ;EXTRACT USER NUMBER FROM COMMAND LINE LD E,A ;PLACE USER NUMBER IN E CALL SETUSR ;SET SPECIFIED USER RSTJMP: JP RCCPNL ;RESTART CCP ; ;Section 5H ;Command: DFU ;Function: Set the Default User Number for the command/file scanner ; (MEMLOAD) ;Forms: ; DFU Select Default User Number; is in DEC ; DFU: CALL USRNUM ;GET USER NUMBER LD (DFUSR),A ;PUT IT AWAY JR RSTJMP ;RESTART CCP (NO DEFAULT LOGIN) ; ;Section 5I ;Command: JUMP ;Function: To Call the program (subroutine) at the specified address ; without loading from disk ;Forms: ; JUMP Call at ; is in HEX ; IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM ; JUMP: CALL HEXNUM ;GET LOAD ADDRESS IN HL JR CALLPROG ;PERFORM CALL ; ENDIF ;RAS ; ;Section 5J ;Command: GO ;Function: To Call the program in the TPA without loading ; loading from disk. Same as JUMP 100H, but much ; more convenient, especially when used with ; parameters for programs like STAT. Also can be ; allowed on remote-access systems with no problems. ; ;Form: ; GO ; IF NOT RAS ;ONLY IF RAS ; GO: LD HL,TPA ;Always to TPA JR CALLPROG ;Perform call ; ENDIF ;END OF GO FOR RAS ; ;Section 5K ;Command: COM file processing ;Function: To load the specified COM file from disk and execute it ;Forms: ; ; COM: LD A,(FCBFN) ;ANY COMMAND? CP ' ' ;' ' MEANS COMMAND WAS 'D:' TO SWITCH JR NZ,COM1 ;NOT , SO MUST BE TRANSIENT OR ERROR LD A,(TEMPDR) ;LOOK FOR DRIVE SPEC OR A ;IF ZERO, JUST BLANK JP Z,RCCPNL DEC A ;ADJUST FOR LOG IN LD (TDRIVE),A ;SET DEFAULT DRIVE CALL SETU0D ;SET DRIVE WITH USER 0 CALL LOGIN ;LOG IN DRIVE JP RCCPNL ;RESTART CCP COM1: LD A,(FCBFT) ;FILE TYPE MUST BE BLANK CP ' ' JP NZ,ERROR LD HL,COMMSG ;PLACE DEFAULT FILE TYPE (COM) INTO FCB LD DE,FCBFT ;COPY INTO FILE TYPE LD BC,3 ;3 BYTES LDIR LD HL,TPA ;SET EXECUTION/LOAD ADDRESS PUSH HL ;SAVE FOR EXECUTION CALL MEMLOAD ;LOAD MEMORY WITH FILE SPECIFIED IN CMD LINE ;(NO RETURN IF ERROR OR TOO BIG) POP HL ;GET EXECUTION ADDRESS ; ; CALLPROG IS THE ENTRY POINT FOR THE EXECUTION OF THE LOADED ; PROGRAM;ON ENTRY TO THIS ROUTINE, HL MUST CONTAIN THE EXECUTION ; ADDRESS OF THE PROGRAM (SUBROUTINE) TO EXECUTE ; CALLPROG: LD (EXECADR),HL ;PERFORM IN-LINE CODE MODIFICATION CALL DLOGIN ;LOG IN DEFAULT DRIVE CALL SCANER ;SEARCH COMMAND LINE FOR NEXT TOKEN LD HL,TEMPDR ;SAVE PTR TO DRIVE SPEC PUSH HL LD A,(HL) ;SET DRIVE SPEC LD (FCBDN),A LD A,10H ;OFFSET FOR 2ND FILE SPEC CALL SCAN1 ;SCAN FOR IT AND LOAD IT INTO FCBDN+16 POP HL ;SET UP DRIVE SPECS LD A,(HL) LD (FCBDM),A XOR A LD (FCBCR),A LD DE,TFCB ;COPY TO DEFAULT FCB LD HL,FCBDN ;FROM FCBDN LD BC,33 ;SET UP DEFAULT FCB LDIR LD HL,CIBUFF COM4: LD A,(HL) ;SKIP TO END OF 2ND FILE NAME OR A ;END OF LINE? JR Z,COM5 CP ' ' ;END OF TOKEN? JR Z,COM5 INC HL JR COM4 ; ; LOAD COMMAND LINE INTO TBUFF ; COM5: LD B,0 ;SET CHAR COUNT LD DE,TBUFF+1 ;PT TO CHAR POS COM6: LD A,(HL) ;COPY COMMAND LINE TO TBUFF LD (DE),A OR A ;DONE IF ZERO JR Z,COM7 INC B ;INCR CHAR COUNT INC HL ;PT TO NEXT INC DE JR COM6 ; ; RUN LOADED TRANSIENT PROGRAM ; COM7: LD A,B ;SAVE CHAR COUNT LD (TBUFF),A CALL CRLF ;NEW LINE CALL DEFDMA ;SET DMA TO 0080 CALL SETUD ;SET USER/DISK ; ; EXECUTION (CALL) OF PROGRAM (SUBROUTINE) OCCURS HERE ; EXECADR EQU $+1 ;CHANGE ADDRESS FOR IN-LINE CODE MODIFICATION CALL TPA ;CALL TRANSIENT CALL DEFDMA ;SET DMA TO 0080, IN CASE ;PROG CHANGED IT ON US CALL SETU0D ;SET USER 0/DISK CALL LOGIN ;LOGIN DISK JP RESTRT ;RESTART CCP ; ;Section 5L ;Command: GET ;Function: To load the specified file from disk to the specified address ;Forms: ; GET Load the specified file at the specified page; ; is in HEX ; IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM ; GET: CALL HEXNUM ;GET LOAD ADDRESS IN HL PUSH HL ;SAVE ADDRESS CALL SCANER ;GET FILE NAME POP HL ;RESTORE ADDRESS JR NZ,ERRJMP ;MUST BE UNAMBIGUOUS ; ; FALL THRU TO MEMLOAD ; ENDIF ;RAS ; ; LOAD MEMORY WITH THE FILE WHOSE NAME IS SPECIFIED IN THE COMMAND LINE ; ON INPUT, HL CONTAINS STARTING ADDRESS TO LOAD ; ; EXIT BACK TO CALLER IF NO ERROR. IF COM FILE TOO BIG OR MEMORY ; FULL, EXIT TO MLERR. ; MEMLOAD: LD (LOADADR),HL ;SET LOAD ADDRESS CALL GETUSR ;GET CURRENT USER NUMBER LD (TMPUSR),A ;SAVE IT FOR LATER LD (TSELUSR),A ;TEMP USER TO SELECT ; ; MLA is a reentry point for a non-standard CP/M Modification ; This is the return point for when the .COM (or GET) file is not found the ; first time, Drive A: is selected for a second attempt. ; MLA: CALL SLOGIN ;LOG IN SPECIFIED DRIVE IF ANY CALL OPENF ;OPEN COMMAND.COM FILE JR NZ,MLA1 ;FILE FOUND - LOAD IT ; ; ERROR ROUTINE TO SELECT USER 0 IF ALL ELSE FAILS ; DFUSR EQU $+1 ;MARK IN-THE-CODE VARIABLE LD A,DEFUSR ;GET DEFAULT USER TSELUSR EQU $+1 ;MARK IN-THE-CODE VARIABLE CP DEFUSR ;SAME? JR Z,MLA0 ;JUMP IF SO LD (TSELUSR),A ;ELSE PUT DOWN NEW ONE LD E,A CALL SETUSR ;GO SET NEW USER NUMBER JR MLA ;AND TRY AGAIN ; ; ERROR ROUTINE TO SELECT DRIVE A: IF DEFAULT WAS ORIGINALLY SELECTED ; MLA0: LD HL,TEMPDR ;GET DRIVE FROM CURRENT COMMAND XOR A ;A=0 OR (HL) JR NZ,MLERR ;ERROR IF ALREADY DISK A: LD (HL),1 ;SELECT DRIVE A: JR MLA ; ; FILE FOUND -- PROCEED WITH LOAD ; MLA1: LOADADR EQU $+1 ;MEMORY LOAD ADDRESS (IN-LINE CODE MOD) LD HL,TPA ;SET START ADDRESS OF MEMORY LOAD ML2: LD A,ENTRY/256-1 ;GET HIGH-ORDER ADR OF JUST BELOW CCP CP H ;ARE WE GOING TO OVERWRITE THE CCP? JR C,PRNLE ;ERROR IF SO PUSH HL ;SAVE ADDRESS OF NEXT SECTOR EX DE,HL ;... IN DE CALL DMASET ;SET DMA ADDRESS FOR LOAD LD DE,FCBDN ;READ NEXT SECTOR CALL READ POP HL ;GET ADDRESS OF NEXT SECTOR JR NZ,ML3 ;READ ERROR OR EOF? LD DE,128 ;MOVE 128 BYTES PER SECTOR ADD HL,DE ;PT TO NEXT SECTOR IN HL JR ML2 ; ML3: DEC A ;LOAD COMPLETE JP Z,RESETUSR ;IF ZERO, OK, GO RESET CORRECT USER ;# ON WAY OUT, ELSE FALL THROUGH TO PRNLE ; ; LOAD ERROR ; PRNLE: CALL PRINTC DB 'Ful','l'+80H ; ; TRANSIENT LOAD ERROR ; MLERR: CALL RESETUSR ;RESET CURRENT USER NUMBER ; RESET MUST BE DONE BEFORE LOGIN ERRLOG: CALL DLOGIN ;LOG IN DEFAULT DISK ERRJMP: JP ERROR ; END