; ; CP/M CONSOLE COMMAND PROCESSOR (CCP) for CP/M REV. 2.2 ; ORIGINAL CCP DISASSEMBLED BY ???? ; ORIGINAL CCP DISASSEMBLED FURTHER BY RLC ; ORIGINAL CCP COMMENTED BY RLC ; ;**** CUSTOMIZATION PROCEDURE ***** ; To customize this CCP for CP/M, do the following: ; 1. Run SYSGEN and obtain a sysgen image of CP/M ; 3. Assemble this CCP (with Mods); MAC or M80 MUST be ; used because of MACROs ; 4. If end address exceeds BDOS addr, CCP is too big; ; correct ; 5. Load sysgen image with "DDT CPM.COM" ; 6. Init FCB with ICCP22.HEX (from assembly) ; 7. Read in CCP ; 9. Place system on disk with "SYSGEN" ; ;**** NON-STANDARD FEATURES ***** ; The non-standard features incorporated into this CCP are: ; A. The Command-Search Hierarchy, as follows -- ; 1. Scan for a CCP-resident command and execute it if ; found ; 2. If not CCP-resident, look for a .COM file on disk ; 3. If the .COM file is not found in the current user ; area and the current user area is not USER 0, ; USER 0 is selected and scanned for the file ; 4. If the .COM file is not found on the current ; logged-in disk drive, drive A: is selected ; and scanned for the file ; B. The DIR Command no longer prints the current drive spec ; at the beginning of each line ; C. The TYPE Command pages its output ; D. A LIST Command now exists which is like TYPE but does not ; page and sends its output to the LST: device ; F. The user number is printed as part of the command prompt; ; the prompt is now du>, such as A0> and A15> ; H. The input line buffer has been reduced in size to 100 bytes ; I. The ERA Command displays the names of the files it is to ; erase ; J. The DIR Command has an additional special form of "DIR @" ; which displays all files (both non-system and system), ; while "DIR" displays just the non-system files ; K. The Directory Display no longer displays the disk name at ; the beginning of each line and it now includes a '.' between ; the file name and file type (FILENAME.TYP) ; L. The SUBMIT File Facility now expects the $$$.SUB file to be ; on the currently logged-in disk (as opposed to always A:) ; M. The Command Line Prompt is now '$' if the command comes from ; a $$$.SUB file and '>' if the command comes from the user; ; also, the '>' is not printed until all preprocessing is ; completed ; N. The TYPE and LIST Commands mask the MSB of each byte, so that ; files created by editors such as EDIT80 are "printable" ; ; MSIZE EQU 62 ;Memory size CCPLOC EQU (MSIZE-20)*1024+3400H ;Where CCP resides for TT DOSSER EQU (MSIZE-20)*1024+3C00H ;Where BDOS serial number resides NLINES EQU 16 ; NUMBER OF LINES ON CRT SCREEN CR EQU 0DH LF EQU 0AH TAB EQU 09H WBOOT EQU 0000H ; CP/M WARM BOOT ADDRESS UDFLAG EQU 0004H ; USER NUMBER IS IN HIGH NYBBLE, DISK IN LOW BDOS EQU 0005H ; BDOS FUNCTION CALL ENTRY PT TBUFF EQU 0080H ; DEFAULT DISK I/O BUFFER TFCB EQU 005CH ; DEFAULT FCB BUFFER TPA EQU 0100H ; BASE OF TPA ; ; MACROS TO PROVIDE Z80 EXTENSIONS ; MACROS INCLUDE: ; JR - JUMP RELATIVE ; JRC - JUMP RELATIVE IF CARRY ; JRNC - JUMP RELATIVE IF NO CARRY ; JRZ - JUMP RELATIVE IF ZERO ; JRNZ - JUMP RELATIVE IF NO ZERO ; DJNZ - DECREMENT B AND JUMP RELATIVE IF NO ZERO ; ; ; @CHK MACRO USED FOR CHECKING 8 BIT DISPLACMENTS ; @CHK MACRO ?DD ;; USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS IF (?DD GT 7FH) AND (?DD LT 0FF80H) 'DISPLACEMENT RANGE ERROR - Z80 LIB' ENDIF ENDM ; ; Z80 MACRO EXTENSIONS ; JR MACRO ?N JMP ?N ENDM JRC MACRO ?N JC ?N ENDM JRNC MACRO ?N JNC ?N ENDM JRZ MACRO ?N JZ ?N ENDM JRNZ MACRO ?N JNZ ?N ENDM DJNZ MACRO ?N DCR B JNZ ?N ENDM ; ; END OF Z80 MACRO EXTENSIONS ; ORG CCPLOC ENTRY: JMP CCP JMP CCP1 ; INPUT COMMAND LINE AND DEFAULT COMMAND BUFLEN EQU 100 ; 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 DB ' ' DB ' ' DB ' ' CIBUF: DS 85 ; TOTAL IS 100 BYTES DS 20 ; STACK AREA STACK EQU $ ; TOP OF STACK CIBPTR: DW CIBUFF ;POINTER TO CMD INPUT BUFF CIPTR: DW CIBUF ;CURRENT PNTR ; ; I/O UTILITIES ; ; OUTPUT SPACER: MVI A,' ' ; FALL THRU TO CONOUT ; OUTPUT CHAR IN REG A TO CONSOLE AND DON'T CHANGE BC CONOUT: PUSH B PUSH H MVI C,02H OUTPUT: MOV E,A CALL BDOS POP H POP B RET ; CALL BDOS AND SAVE BC BDOSB: PUSH B CALL BDOS POP B RET ; OUTPUT CHAR IN REG A TO LIST DEVICE LSTOUT: PUSH B PUSH H MVI C,05H JR OUTPUT ; OUTPUT CRLF: MVI A,CR CALL CONOUT MVI A,LF JR CONOUT ; PRINT STRING (ENDING IN 0) PTED TO BY RET ADR; START WITH PRINT: XTHL ; GET PTR TO STRING PUSH PSW ; SAVE FLAGS CALL CRLF ; NEW LINE CALL PRIN1 POP PSW ; GET FLAGS XTHL ; RESTORE HL AND RET ADR RET ; PRINT STRING (ENDING IN 0) PTED TO BY HL PRIN1: MOV A,M ; GET NEXT BYTE INX H ; PT TO NEXT BYTE ORA A ; DONE IF 0 RZ CALL CONOUT ; PRINT CHAR JR PRIN1 ; ; BDOS FUNCTION ROUTINES ; RESET: MVI C,0DH JMP BDOS ; LOGIN: MOV E,A MVI C,0EH JMP BDOS ; OPENF: XRA A STA FCBCR LXI D,FCBDN ; FALL THRU TO OPEN ; OPEN: MVI C,0FH ; FALL THRU TO GRBDOS ; GRBDOS: CALL BDOS INR A ; SET ZERO FLAG FOR ERROR RETURN RET ; CLOSE: MVI C,10H JR GRBDOS ; SEARF: LXI D,FCBDN ; SPECIFY FCB SEAR1: MVI C,11H JR GRBDOS ; SEARN: MVI C,12H JR GRBDOS ; DELETE: MVI C,13H JMP BDOS ; READF: LXI D,FCBDN ; FALL THRU TO READ ; READ: MVI C,14H ; FALL THRU TO GOBDOS ; GOBDOS: CALL BDOSB ; PRESERVE B ORA A RET ; WRITE: MVI C,15H JR GOBDOS ; CREATE: MVI C,16H JR GRBDOS ; GETUSR: MVI E,0FFH ;GET CURRENT USER NUMBER SETUSR: MVI C,20H ;SET USER NUMBER TO VALUE IN E (GET IF E=FFH) JMP BDOS ; ; END OF BDOS FUNCTIONS ; ; ; CCP UTILITIES ; ; SET USER/DISK FLAG TO CURRENT USER AND DEFAULT DISK SETUD: CALL GETUSR ; GET NUMBER OF CURRENT USER ADD A ; PLACE IT IN HIGH NYBBLE ADD A ADD A ADD A LXI H,TDRIVE ; MASK IN DEFAULT DRIVE NUMBER (LOW NYBBLE) ORA M ; MASK IN STA UDFLAG ; SET USER/DISK NUMBER RET ; SET USER/DISK FLAG TO USER 0 AND DEFAULT DISK SETU0D: LDA TDRIVE ; SET USER 0/DEFAULT DISK STA UDFLAG ; SET USER/DISK NUMBER RET ; CONVERT CHAR IN A TO UPPER CASE UCASE: CPI 61H ; LOWER-CASE A RC CPI 7BH ; GREATER THAN LOWER-CASE Z? RNC ANI 5FH ; CAPITALIZE RET ; INPUT NEXT COMMAND TO CCP REDBUF: LDA RNGSUB ; SUBMIT FILE CURRENTLY IN EXECUTION? ORA A ; 0=NO JRZ RB1 ; GET LINE FROM CONSOLE IF NOT LXI D,SUBFCB ; OPEN $$$.SUB CALL OPEN JRZ RB1 ; ERASE $$$.SUB IF END OF FILE AND GET CMND LDA SUBFRC ; GET VALUE OF LAST RECORD IN FILE DCR A ; PT TO NEXT TO LAST RECORD STA SUBFCR ; SAVE NEW VALUE OF LAST RECORD IN $$$.SUB LXI D,SUBFCB ; READ LAST RECORD OF SUBMIT FILE CALL READ JRNZ RB1 ; ABORT $$$.SUB IF ERROR IN READING LAST REC LXI D,CBUFF ; COPY LAST RECORD (NEXT SUBMIT CMND) TO CBUFF LXI H,TBUFF ; FROM TBUFF MVI B,BUFLEN ; NUMBER OF BYTES CALL MOVEHD LXI H,SUBFS2 ; PT TO S2 OF $$$.SUB FCB MVI M,0 ; SET S2 TO ZERO INX H ; PT TO RECORD COUNT DCR M ; DECREMENT RECORD COUNT OF $$$.SUB LXI D,SUBFCB ; CLOSE $$$.SUB CALL CLOSE JRZ RB1 ; ABORT $$$.SUB IF ERROR MVI A,'$' ; PRINT SUBMIT PROMPT CALL CONOUT LXI H,CIBUFF ; PRINT COMMAND LINE FROM $$$.SUB CALL PRIN1 CALL BREAK ; CHECK FOR ABORT (ANY CHAR) JRZ CNVBUF ; IF (NO ABORT), CAP COMMAND AND RUN CALL SUBKIL ; KILL $$$.SUB IF ABORT JMP RESTRT ; RESTART CCP ; INPUT COMMAND LINE FROM USER CONSOLE RB1: CALL SUBKIL ; ERASE $$$.SUB IF PRESENT CALL SETUD ; SET USER AND DISK MVI A,'>' ; PRINT PROMPT CALL CONOUT MVI C,0AH ; READ COMMAND LINE FROM USER LXI D,MBUFF CALL BDOS CALL SETU0D ; SET CURRENT DISK NUMBER IN LOWER PARAMS ; CAPITALIZE STRING (ENDING IN 0) IN CBUFF CNVBUF: LXI H,CBUFF ; PT TO USER'S COMMAND MOV B,M ; CHAR COUNT IN B CB1: INX H ; PT TO 1ST VALID CHAR MOV A,B ; DONE WHEN ENCOUNTERED ORA A JRZ CB2 MOV A,M ; CAPITALIZE COMMAND CHAR CALL UCASE MOV M,A DCR B ; CONTINUE UNTIL END OF COMMAND LINE JR CB1 CB2: MOV M,A ; STORE ENDING LXI H,CIBUFF ; SET COMMAND LINE PTR TO 1ST CHAR SHLD CIBPTR RET ; CHECK FOR ANY CHAR FROM USER CONSOLE; RET W/ZERO SET IF NONE BREAK: PUSH D ; SAVE DE MVI E,0FFH ; GET STATUS MVI C,6 ; DIRECT CONSOLE I/O CALL BDOSB POP D ANI 7FH ; MASK MSB AND SET ZERO FLAG RET ; RETURN NUMBER OF CURRENT DISK IN A GETDRV: MVI C,19H JMP BDOS ; SET 80H AS DMA ADDRESS DEFDMA: LXI D,TBUFF ; 80H=TBUFF DMASET: MVI C,1AH JMP BDOS ; CHECK FOR SUBMIT FILE IN EXECUTION AND ABORT IT IF SO SUBKIL: LXI H,RNGSUB ; CHECK FOR SUBMIT FILE IN EXECUTION MOV A,M ORA A ; 0=NO RZ MVI M,0 ; ABORT SUBMIT FILE LXI D,SUBFCB ; DELETE $$$.SUB JMP DELETE ; INVALID COMMAND -- PRINT IT ERROR: CALL CRLF ; NEW LINE LHLD CIPTR ; PT TO BEGINNING OF COMMAND LINE ERR2: MOV A,M ; GET CHAR CPI ' ' ; SIMPLE '?' IF JRZ ERR1 ORA A ; SIMPLE '?' IF JRZ ERR1 PUSH H ; SAVE PTR TO ERROR COMMAND CHAR CALL CONOUT ; PRINT COMMAND CHAR POP H ; GET PTR INX H ; PT TO NEXT JR ERR2 ; CONTINUE ERR1: MVI A,'?' ; PRINT '?' CALL CONOUT CALL SUBKIL ; TERMINATE ACTIVE $$$.SUB IF ANY JMP RESTRT ; RESTART CCP ; CHECK TO SEE IF DE PTS TO DELIMITER; IF SO, RET W/ZERO FLAG SET SDELM: LDAX D ORA A ; 0=DELIMITER RZ CPI ' ' ; ERROR IF < JC ERROR RZ ; =DELIMITER CPI '=' ; '='=DELIMITER RZ CPI 5FH ; UNDERSCORE=DELIMITER RZ CPI '.' ; '.'=DELIMITER RZ CPI ':' ; ':'=DELIMITER RZ CPI ';' ; ';'=DELIMITER RZ CPI '<' ; '<'=DELIMITER RZ CPI '>' ; '>'=DELIMITER RET ; SKIP STRING PTED TO BY DE (STRING ENDS IN 0) UNTIL END OF STRING ; OR NON-BLANK ENCOUNTERED (BEGINNING OF TOKEN) SBLANK: LDAX D ORA A RZ CPI ' ' RNZ INX D JR SBLANK ; ADD A TO HL (HL=HL+A) ADDAH: ADD L MOV L,A RNC INR H RET ; EXTRACT TOKEN FROM COMMAND LINE AND PLACE IT INTO FCBDN; FORMAT FCBDN ; 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 SET ; IF '?' IS IN TOKEN SCANER: MVI A,0 ; START AT DRIVE SPECIFICATION BYTE SCAN1: LXI H,FCBDN ; POINT TO FCBDN CALL ADDAH ; OFFSET INTO FCB PUSH H PUSH H XRA A ; SET TEMPORARY DRIVE NUMBER TO DEFAULT STA TEMPDR LHLD CIBPTR ; GET PTR TO NEXT CHAR IN COMMAND LINE XCHG ; PTR IN DE CALL SBLANK ; SKIP TO NON-BLANK OR END OF LINE XCHG SHLD CIPTR ; SET PTR TO NON-BLANK OR END OF LINE XCHG ; DE PTS TO NEXT NON-BLANK OR END OF LINE CHAR POP H ; GET PTR TO NEXT BYTE IN FCBDN LDAX D ; END OF LINE? ORA A ; 0=YES JRZ SCAN2 SBI 'A'-1 ; CONVERT POSSIBLE DRIVE SPEC TO NUMBER MOV B,A ; STORE NUMBER (A:=0, B:=1, ETC) IN B INX D ; PT TO NEXT CHAR LDAX D ; SEE IF IT IS A COLON (:) CPI ':' JRZ SCAN3 ; YES^ WE HAVE A DRIVE SPEC DCX D ; NO^ BACK UP PTR TO FIRST NON-BLANK CHAR SCAN2: LDA TDRIVE ; SET 1ST BYTE OF FCBDN AS DEFAULT DRIVE MOV M,A JR SCAN4 SCAN3: MOV A,B ; WE HAVE A DRIVE SPEC^ STA TEMPDR ; SET TEMPORARY DRIVE MOV M,B ; SET 1ST BYTE OF FCBDN AS SPECIFIED DRIVE INX D ; PT TO BYTE AFTER ':' ; EXTRACT FILENAME FROM POSSIBLE FILENAME.TYP SCAN4: MVI B,08H ; MAX OF 8 CHARS IN FILE NAME SCAN5: CALL SDELM ; DONE IF DELIMITER ENCOUNTERED - FILL JRZ SCAN9 INX H ; PT TO NEXT BYTE IN FCBDN CPI '*' ; IS (DE) A WILD CARD? JRNZ SCAN6 ; CONTINUE IF NOT MVI M,'?' ; PLACE '?' IN FCBDN AND DON'T ADVANCE DE IF SO JR SCAN7 SCAN6: MOV M,A ; STORE FILENAME CHAR IN FCBDN INX D ; 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 JRZ SCAN10 ; ZERO FLAG SET IF DELIMITER FOUND INX D ; PT TO NEXT CHAR IN COMMAND LINE JR SCAN8 SCAN9: INX H ; PT TO NEXT BYTE IN FCBDN MVI M,' ' ; FILL FILENAME PART WITH DJNZ SCAN9 ; EXTRACT FILE TYPE FROM POSSIBLE FILENAME.TYP SCAN10: MVI B,03H ; PREPARE TO EXTRACT TYPE CPI '.' ; IF (DE) DELIMITER IS A '.', WE HAVE A TYPE JRNZ SCAN15 ; FILL FILE TYPE BYTES WITH INX D ; PT TO CHAR IN COMMAND LINE AFTER '.' SCAN11: CALL SDELM ; CHECK FOR DELIMITER JRZ SCAN15 ; FILL REST OF TYPE IF IT IS A DELIMITER INX H ; PT TO NEXT BYTE IN FCBDN CPI '*' ; WILD? JRNZ SCAN12 ; STORE CHAR IF NOT WILD MVI M,'?' ; STORE '?' AND DON'T ADVANCE COMMAND LINE PTR JR SCAN13 SCAN12: MOV M,A ; STORE CHAR IN FCBDN INX D ; 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 JRZ SCAN16 ; DELIMITER INX D JR SCAN14 SCAN15: INX H ; FILL IN REST OF TYP WITH MVI M,' ' DJNZ SCAN15 ; FILL IN EX, S1, S2, AND RC WITH ZEROES SCAN16: MVI B,4 ; 4 BYTES SCAN17: INX H ; PT TO NEXT BYTE IN FCBDN MVI M,0 DJNZ SCAN17 ; SCAN COMPLETE -- DE PTS TO DELIMITER BYTE AFTER TOKEN XCHG ; STORE PTR TO NEXT BYTE IN COMMAND LINE SHLD CIBPTR ; SET ZERO FLAG TO INDICATE PRESENCE OF '?' IN FILENAME.TYP POP H ; GET PTR TO FCBDN IN HL LXI B,11 ; SCAN FOR '?' IN FILENAME.TYP (C=11 BYTES) SCAN18: INX H ; PT TO NEXT BYTE IN FCBDN MOV A,M CPI '?' JRNZ SCAN19 INR B ; B<>0 TO INDICATE '?' ENCOUNTERED SCAN19: DCR C ; COUNT DOWN JRNZ SCAN18 MOV A,B ; A=B=NUMBER OF '?' IN FILENAME.TYP ORA A ; SET ZERO FLAG TO INDICATE ANY '?' RET ; ; CCP BUILT-IN COMMAND TABLE AND COMMAND PROCESSOR ; NCMNDS EQU 7 ; NUMBER OF CCP COMMANDS NCHARS EQU 4 ; NUMBER OF CHARS/COMMAND ; CCP COMMAND NAME TABLE CMDTBL: DB 'D' ; 'DIR ' DW DIR DB 'Q' ; 'ERA ' DW ERA DB 'L' ; 'LIST' DW LIST DB 'T' ; 'TYPE' DW TYPE DB 'S' ; 'SAVE' DW SAVE DB 'R' ; 'REN ' DW REN DB 'U' ; 'USER' DW USER ; ; CCP STARTING POINTS ; ; START CCP AND DON'T PROCESS DEFAULT COMMAND STORED CCP1: XRA A ; SET NO DEFAULT COMMAND STA CBUFF ; START CCP AND POSSIBLY PROCESS DEFAULT COMMAND CCP: LXI SP,STACK ; RESET STACK PUSH B MOV A,C ; C=USER/DISK NUMBER (SEE LOC 4) RAR ; EXTRACT USER NUMBER RAR RAR RAR ANI 0FH MOV E,A ; SET USER NUMBER CALL SETUSR CALL RESET ; RESET DISK SYSTEM POP B MOV A,C ; C=USER/DISK NUMBER (SEE LOC 4) ANI 0FH ; EXTRACT DEFAULT DISK DRIVE STA TDRIVE ; SET IT CALL LOGIN ; LOG IN DEFAULT DISK LXI D,SUBFCB ; CHECK FOR $$$.SUB ON CURRENT DISK CALL SEAR1 CMA ; 0FFH IS RETURNED IF NO $$$.SUB, SO COMPLEMENT STA RNGSUB ; SET FLAG (0=NO $$$.SUB) LDA CBUFF ; EXECUTE DEFAULT COMMAND? ORA A ; 0=NO JRNZ RS1 ; PROMPT USER AND INPUT COMMAND LINE FROM HIM RESTRT: LXI SP,STACK ; RESET STACK ; PRINT PROMPT (DU>) CALL CRLF ; PRINT PROMPT CALL GETDRV ; CURRENT DRIVE IS PART OF PROMPT ADI 'A' ; CONVERT TO ASCII A-P CALL CONOUT CALL GETUSR ; GET USER NUMBER CPI 10 ; USER < 10? JRC RS00 ADI 7 ; DISPLAY IN HEX RS00: ADI '0' ; OUTPUT 1'S DIGIT (CONVERT TO ASCII) CALL CONOUT ; READ INPUT LINE FROM USER OR $$$.SUB CALL REDBUF ; INPUT COMMAND LINE FROM USER (OR $$$.SUB) ; PROCESS INPUT LINE RS1: LXI D,TBUFF ; PT TO INPUT COMMAND LINE (IN TBUFF) CALL DMASET ; SET TBUFF TO DMA ADDRESS CALL GETDRV ; GET DEFAULT DRIVE NUMBER STA TDRIVE ; SET IT CALL SCANER ; PARSE COMMAND NAME FROM COMMAND LINE CNZ ERROR ; ERROR IF COMMAND NAME CONTAINS A '?' LDA TEMPDR ; IS COMMAND OF FORM 'D:COMMAND'? ORA A ; NZ=YES JNZ COM ; PROCESS AS COM FILE IMMEDIATELY ; SCAN FOR CCP-RESIDENT COMMAND CMDSER: LXI D,FCBFN+1 ; POINT TO WHAT OUGHT TO BE ' ' LDAX D CPI ' ' ; IS IT? JNZ COM ; NO, PROCESS AS COM FILE MVI C,NCMNDS ; COUNT LDA FCBFN ; COMMAND LETTER (MAYBE) LXI H,CMDTBL ; COMMAND TABLE CMS1: CMP M INX H JZ CMS2 ; MATCHES COMMAND IN TABLE INX H ; NOT A MATCH, SKIP ADDRESS INX H DCR C ; COUNT JNZ CMS1 ; MORE TABLE ENTRIES TO TEST JMP COM ; NO MATCH IN TABLE, COM FILE CMS2: MOV A,M ; ADDR LOW INX H MOV H,M ; ADDR HIGH MOV L,A PCHL ; GO DO IT ; ; ERROR MESSAGES ; PRNNF: CALL PRINT ; NO FILE MESSAGE DB 'Not found',0 RET ; ; MORE CCP UTILITIES ; ; EXTRACT NUMBER FROM COMMAND LINE NUMBER: CALL SCANER ; PARSE NUMBER AND PLACE IN FCBFN LDA TEMPDR ; TOKEN BEGIN WITH DRIVE SPEC (D:)? ORA A ; ERROR IF SO JNZ ERROR LXI H,FCBFN ; PT TO TOKEN FOR CONVERSION LXI B,11 ; B=ACCUMULATED VALUE, C=CHAR COUNT NUM1: MOV A,M ; GET CHAR CPI ' ' ; DONE IF JRZ NUM2 INX H ; PT TO NEXT CHAR SUI '0' ; CONVERT TO BINARY (ASCII 0-9 TO BINARY) CPI 10 ; ERROR IF >= 10 JNC ERROR MOV D,A ; DIGIT IN D MOV A,B ; GET ACCUMULATED VALUE ANI 0E0H ; CHECK FOR RANGE ERROR (>255) JNZ ERROR MOV A,B ; NEW VALUE = OLD VALUE * 10 RLC RLC RLC ADD B ; CHECK FOR RANGE ERROR JC ERROR ADD B ; CHECK FOR RANGE ERROR JC ERROR ADD D ; NEW VALUE = OLD VALUE * 10 + DIGIT JC ERROR ; CHECK FOR RANGE ERROR MOV B,A ; SET NEW VALUE DCR C ; COUNT DOWN JRNZ NUM1 RET ; REST OF TOKEN BUFFER MUST BE NUM2: MOV A,M ; CHECK FOR CPI ' ' JNZ ERROR INX H ; PT TO NEXT DCR C ; COUNT DOWN CHARS JRNZ NUM2 MOV A,B ; GET ACCUMULATED VALUE RET ; MOVE 3 BYTES FROM HL TO DE MOVHD3: MVI B,3 ; MOVE 3 CHARS MOVEHD: MOV A,M ; GET IT STAX D ; PUT IT INX H ; PT TO NEXT INX D DJNZ MOVEHD RET ; PT TO DIRECTORY ENTRY IN TBUFF WHOSE OFFSET IS SPECIFIED BY A AND C DIRPTR: LXI H,TBUFF ; PT TO TEMP BUFFER ADD C ; PT TO 1ST BYTE OF DIR ENTRY CALL ADDAH ; PT TO DESIRED BYTE IN DIR ENTRY MOV A,M ; GET DESIRED BYTE RET ; CHECK FOR SPECIFIED DRIVE AND LOG IT IN IF NOT DEFAULT SLOGIN: XRA A ; SET FCBDN FOR DEFAULT DRIVE STA FCBDN CALL COMLOG ; CHECK DRIVE RZ JMP LOGIN ; DO LOGIN OTHERWISE ; CHECK FOR SPECIFIED DRIVE AND LOG IN DEFAULT DRIVE IF SPECIFIED<>DEFAULT DLOGIN: CALL COMLOG ; CHECK DRIVE RZ ; ABORT IF SAME LDA TDRIVE ; LOG IN DEFAULT DRIVE JMP LOGIN ; ROUTINE COMMON TO BOTH LOGIN ROUTINES; ON EXIT, Z SET MEANS ABORT COMLOG: LDA TEMPDR ; DRIVE SPECIFIED? ORA A ; 0=NO RZ DCR A ; COMPARE IT AGAINST DEFAULT LXI H,TDRIVE CMP M RET ; ABORT IF SAME ; ; CCP DIRECTORY DISPLAY FUNCTION (DIR) ; DIR: MVI A,80H ; SET SYSTEM BIT EXAMINATION PUSH PSW CALL SCANER ; EXTRACT POSSIBLE D:FILENAME.TYP TOKEN CALL SLOGIN ; LOG IN DRIVE IF NECESSARY LXI H,FCBFN ; MAKE FCB WILD (ALL '?') IF NO FILENAME.TYP MOV A,M ; GET FIRST CHAR OF FILENAME.TYP CPI ' ' ; IF , ALL WILD JNZ DIR2 DIR0: MVI B,11 ; NUMBER OF CHARS IN FN & FT DIR1: MVI M,'?' ; STORE '?' INX H DJNZ DIR1 DIR2: POP PSW ; GET FLAG CALL DIRPR ; PRINT DIRECTORY JMP RSTCCP ; RESTART CCP ; DIRECTORY PRINT ROUTINE DIRPR: MVI D,80H ; EXCLUDE SYSTEM FILES MVI E,0 ; SET COLUMN COUNTER TO ZERO PUSH D ; SAVE COLUMN COUNTER (E) AND SYSTEM FLAG (D) CALL SEARF ; SEARCH FOR SPECIFIED FILE (FIRST OCCURRANCE) CZ PRNNF ; PRINT NO FILE MSG; REG A NOT CHANGED ; ENTRY SELECTION LOOP; ON ENTRY, A=OFFSET FROM SEARF OR SEARN DIR3: JRZ DIR11 ; DONE IF ZERO FLAG SET DCR A ; ADJUST TO RETURNED VALUE RRC ; CONVERT NUMBER TO OFFSET INTO TBUFF RRC RRC ANI 60H MOV C,A ; OFFSET INTO TBUFF IN C (C=OFFSET TO ENTRY) MVI A,10 ; ADD 10 TO PT TO SYSTEM FILE ATTRIBUTE BIT CALL DIRPTR POP D ; GET SYSTEM BIT MASK FROM D PUSH D ANA D ; MASK FOR SYSTEM BIT JRNZ DIR10 ; SKIP ENTRY IF BIT IS SET POP D ; GET ENTRY COUNT (= COUNTER) MOV A,E ; ADD 1 TO IT INR E PUSH D ; SAVE IT ANI 03H ; OUTPUT IF 4 ENTRIES PRINTED IN LINE PUSH PSW JRNZ DIR4 CALL CRLF ; NEW LINE JR DIR5 DIR4: CALL SPACER ; PRINT : BETWEEN ENTRIES MVI A,'|' CALL CONOUT CALL SPACER DIR5: MVI B,01H ; PT TO 1ST BYTE OF FILE NAME DIR6: MOV A,B ; A=OFFSET CALL DIRPTR ; HL NOW PTS TO 1ST BYTE OF FILE NAME ANI 7FH ; MASK OUT MSB CPI ' ' ; NO FILE NAME? JRNZ DIR8 ; PRINT FILE NAME IF PRESENT POP PSW PUSH PSW CPI 03H JRNZ DIR7 MVI A,09H ; PT TO 1ST BYTE OF FILE TYPE CALL DIRPTR ; HL NOW PTS TO 1ST BYTE OF FILE TYPE ANI 7FH ; MASK OUT MSB CPI ' ' ; NO FILE TYPE? JRZ DIR9 ; CONTINUE IF SO DIR7: MVI A,' ' ; OUTPUT DIR8: CALL CONOUT ; PRINT CHAR INR B ; INCR CHAR COUNT MOV A,B CPI 12 ; END OF FILENAME.TYP? JRNC DIR9 ; CONTINUE IF SO CPI 09H ; END IF FILENAME ONLY? JRNZ DIR6 ; PRINT TYP IF SO MVI A,'.' ; PRINT DOT BETWEEN FILE NAME AND TYPE CALL CONOUT JR DIR6 DIR9: POP PSW DIR10: CALL BREAK ; CHECK FOR ABORT JRNZ DIR11 CALL SEARN ; SEARCH FOR NEXT FILE JR DIR3 ; CONTINUE DIR11: POP D ; RESTORE STACK RET ; ; CCP FILE ERASE FUNCTION (ERA) ; ERA: CALL SCANER ; PARSE FILE SPECIFICATION CPI 0BH ; ALL WILD (ALL FILES = 11 '?')? JRNZ ERA1 ; IF NOT, THEN DO ERASES CALL PRINT DB 'All?',0 CALL REDBUF ; GET REPLY LXI H,CBUFF ; CHECK FOR DCR M JNZ RESTRT ; RESTART CCP IF JUST INX H ; PT TO RESPONSE BYTE MOV A,M ; GET IT CPI 'Y' ; YES? JNZ RESTRT ; RESTART CCP IF NOT INX H ; PT TO CHAR AFTER 'Y' SHLD CIBPTR ; SET PTR TO IT ERA1: CALL SLOGIN ; LOG IN SELECTED DISK IF ANY MVI A,80H ; SKIP SYSTEM FILES (EXAMINE SYSTEM BIT) CALL DIRPR ; PRINT DIRECTORY OF ERASED FILES LXI D,FCBDN ; DELETE FILE SPECIFIED CALL DELETE JMP RSTCCP ; REENTER CCP ; ; CCP LIST FUNCTION (LIST) ; LIST: MVI A,0FFH ; TURN ON PRINTER FLAG JR TYPE0 ; ; CCP TYPE FUNCTION (TYPE) ; TYPE: XRA A ; TURN OFF PRINTER FLAG ; ENTRY POINT FOR CCP LIST FUNCTION (LIST) TYPE0: STA PRFLG ; SET FLAG CALL SCANER ; EXTRACT FILENAME.TYP TOKEN JNZ ERROR ; ERROR IF ANY QUESTION MARKS CALL SLOGIN ; LOG IN SELECTED DISK IF ANY CALL OPENF ; OPEN SELECTED FILE JZ TYPE4 ; ABORT IF ERROR CALL CRLF ; NEW LINE CALL PAGSET ; SET LINE COUNT LXI H,CHRCNT ; SET CHAR POSITION/COUNT MVI M,0FFH ; EMPTY LINE MVI B,0 ; SET TAB CHAR COUNTER TYPE1: LXI H,CHRCNT ; PT TO CHAR POSITION/COUNT MOV A,M ; END OF BUFFER? CPI 80H JRC TYPE2 PUSH H ; READ NEXT BLOCK CALL READF POP H JRNZ TYPE3 ; ERROR? XRA A ; RESET COUNT MOV M,A TYPE2: INR M ; INCREMENT CHAR COUNT LXI H,TBUFF ; PT TO BUFFER CALL ADDAH ; COMPUTE ADDRESS OF NEXT CHAR FROM OFFSET MOV A,M ; GET NEXT CHAR ANI 7FH ; MASK OUT MSB CPI 1AH ; END OF FILE (^Z)? JZ RSTCCP ; RESTART CCP IF SO PUSH PSW ; SAVE CHAR LDA PRFLG ; TYPE OR LIST? ORA A ; 0=TYPE JRZ TYPE2T ; OUTPUT CHAR TO LST: DEVICE WITH TABULATION POP PSW ; GET CHAR CPI CR ; RESET TAB COUNT? JRZ TABRST CPI LF ; RESET TAB COUNT? JRZ TABRST CPI TAB ; TAB? JRZ LTAB CALL LSTOUT ; LIST CHAR INR B ; INCREMENT CHAR COUNT JR TYPE2L TABRST: CALL LSTOUT ; OUTPUT MVI B,0 ; RESET TAB COUNTER JR TYPE2L LTAB: MVI A,' ' ; CALL LSTOUT INR B ; INCR POS COUNT MOV A,B ANI 7 JRNZ LTAB JR TYPE2L ; OUTPUT CHAR TO CON: WITH TABULATION TYPE2T: POP PSW ; GET CHAR PUSH PSW ; SAVE CHAR CALL CONOUT ; TYPE CHAR POP PSW CPI LF ; PAGE ON CZ PAGER ; COUNT LINES AND PAGE ; CONTINUE PROCESSING TYPE2L: CALL BREAK ; CHECK FOR ABORT JRZ TYPE1 ; CONTINUE IF NO CHAR CPI 'C'-'@' ; ^C? JZ RSTCCP ; RESTART IF SO JR TYPE1 TYPE3: DCR A ; NO ERROR? JZ RSTCCP ; RESTART CCP TYPE4: CALL DLOGIN ; LOG IN DEFAULT DRIVE JMP ERROR ; ; PAGING ROUTINES ; PAGER COUNTS DOWN LINES AND PAUSES FOR INPUT (DIRECT) IF COUNT EXPIRES ; PAGSET SETS LINES/PAGE COUNT ; PAGER: LDA PAGCNT ; COUNT DOWN DCR A STA PAGCNT RNZ PUSH H ; SAVE HL PAGER1: MVI C,6 ; DIRECT CONSOLE I/O MVI E,0FFH ; INPUT CALL BDOSB ORA A ; CHAR READY? JRZ PAGER1 ; WAIT FOR CHAR CPI 'C'-'@' ; ^C JZ RSTCCP ; RESTART CCP POP H ; RESTORE HL PAGSET: MVI A,NLINES-2 ; GET LINE COUNT STA PAGCNT RET ; ; CCP SAVE FUNCTION (SAVE) ; SAVE: CALL NUMBER ; EXTRACT NUMBER FROM COMMAND LINE PUSH PSW ; SAVE IT CALL SCANER ; EXTRACT FILENAME.TYPE JNZ ERROR ; MUST BE NO '?' IN IT CALL SLOGIN ; LOG IN SELECTED DISK LXI D,FCBDN ; DELETE FILE IN CASE IT ALREADY EXISTS PUSH D CALL DELETE POP D CALL CREATE ; MAKE NEW FILE JRZ SAVE3 ; ERROR? XRA A ; SET RECORD COUNT FIELD OF NEW FILE'S FCB STA FCBCR POP PSW ; GET PAGE COUNT MOV L,A ; HL=PAGE COUNT MVI H,0 DAD H ; DOUBLE IT FOR HL=SECTOR (128 BYTES) COUNT LXI D,TPA ; PT TO START OF SAVE AREA (TPA) SAVE1: MOV A,H ; DONE WITH SAVE? ORA L ; HL=0 IF SO JRZ SAVE2 DCX H ; COUNT DOWN ON SECTORS PUSH H ; SAVE PTR TO BLOCK TO SAVE LXI H,128 ; 128 BYTES PER SECTOR DAD D ; PT TO NEXT SECTOR PUSH H ; SAVE ON STACK CALL DMASET ; SET DMA ADDRESS FOR WRITE (ADDRESS IN DE) LXI D,FCBDN ; WRITE SECTOR CALL WRITE POP D ; GET PTR TO NEXT SECTOR IN DE POP H ; GET SECTOR COUNT JRNZ SAVE3 ; WRITE ERROR? JR SAVE1 ; CONTINUE SAVE2: LXI D,FCBDN ; CLOSE SAVED FILE CALL CLOSE INR A ; ERROR? JRNZ SAVE4 SAVE3: CALL PRINT DB 'No Room',0 SAVE4: CALL DEFDMA ; SET DMA TO 0080 JMP RSTCCP ; RESTART CCP ; ; CCP RENAME FILE FUNCTION (REN) ; REN: CALL SCANER ; EXTRACT FILE NAME JNZ ERROR ; ERROR IF ANY '?' IN IT LDA TEMPDR ; SAVE CURRENT DEFAULT DISK PUSH PSW CALL SLOGIN ; LOG IN SELECTED DISK CALL SEARF ; LOOK FOR SPECIFIED FILE JRZ REN0 ; CONTINUE IF NOT FOUND CALL PRINT DB 'Exists',0 JMP RENRET REN0: LXI H,FCBDN ; SAVE NEW FILE NAME LXI D,FCBDM MVI B,16 ; 16 BYTES CALL MOVEHD LHLD CIBPTR ; GET PTR TO NEXT CHAR IN COMMAND LINE XCHG ; ... IN DE CALL SBLANK ; SKIP TO NON-BLANK CPI '=' ; '=' OR UNDERSCORE OK JRZ REN1 CPI 5FH JRNZ REN4 REN1: XCHG ; PT TO CHAR AFTER '=' OR UNDERSCORE IN HL INX H SHLD CIBPTR ; SAVE PTR TO OLD FILE NAME CALL SCANER ; EXTRACT FILENAME.TYP TOKEN JRNZ REN4 ; ERROR IF ANY '?' POP PSW ; GET OLD DEFAULT DRIVE MOV B,A ; SAVE IT LXI H,TEMPDR ; COMPARE IT AGAINST CURRENT DEFAULT DRIVE MOV A,M ; MATCH? ORA A JRZ REN2 CMP B ; CHECK FOR DRIVE ERROR MOV M,B JRNZ REN4 REN2: MOV M,B XRA A STA FCBDN ; SET DEFAULT DRIVE LXI D,FCBDN ; RENAME FILE MVI C,17H ; BDOS RENAME FCT CALL BDOS INR A ; ERROR? -- FILE NOT FOUND IF SO JRNZ RENRET REN3: CALL PRNNF ; PRINT NO FILE MSG RENRET: JMP RSTCCP ; RESTART CCP REN4: CALL DLOGIN ; LOG IN DEFAULT DRIVE JMP ERROR ; ; CCP SET USER NUMBER FUNCTION ; MAXUSR EQU 15 ; MAXIMUM USER AREA ACCESSABLE USER: CALL NUMBER ; EXTRACT USER NUMBER FROM COMMAND LINE CPI MAXUSR+1 ; ERROR IF >= MAXUSR JNC ERROR MOV E,A ; PLACE USER NUMBER IN E LDA FCBFN ; CHECK FOR PARSE ERROR CPI ' ' ; =ERROR JZ ERROR CALL SETUSR ; SET SPECIFIED USER JMP RCCPNL ; RESTART CCP (NO DEFAULT LOGIN) ; ; NOT CCP-RESIDENT COMMAND -- PROCESS AS TRANSIENT ; COM: CALL GETUSR ; GET CURRENT USER NUMBER STA TMPUSR ; SAVE IT FOR LATER STA TSELUSR ; TEMP USER TO SELECT LDA FCBFN ; ANY COMMAND? CPI ' ' ; ' ' MEANS COMMAND WAS 'D:' TO SWITCH JRNZ COM1 ; NOT , SO MUST BE TRANSCIENT OR ERROR LDA TEMPDR ; LOOK FOR DRIVE SPEC ORA A ; IF ZERO, JUST BLANK JZ RCCPNL DCR A ; ADJUST FOR LOG IN STA TDRIVE ; SET DEFAULT DRIVE CALL SETU0D ; SET DRIVE WITH USER 0 CALL LOGIN ; LOG IN DRIVE JMP RCCPNL ; RESTART CCP COM1: LDA FCBFT ; CHECK FOR ERROR IN FCB CPI ' ' ; ERROR IF SO JNZ ERROR ; ; COMA is a reentry point for a non-standard CP/M Modification ; This is the return point for when the .COM file is not found the ; first time, Drive A: is selected for a second attempt ; COMA: CALL SLOGIN ; LOG IN SPECIFIED DRIVE IF ANY LXI H,COMMSG ; PLACE 'COM' IN FCB LXI D,FCBFT ; PT TO FILE TYPE CALL MOVHD3 ; MOVE 3 CHARS CALL OPENF ; OPEN COMMAND.COM FILE JRNZ COMA1 ; ERROR? ; ERROR ROUTINE TO SELECT USER 0 IF ALL ELSE FAILS LDA TSELUSR ; GET USER FLAG ORA A ; SET FLAGS JRZ COMA0 ; TRY DISK A: IF ALREADY USER 0 XRA A ; SELECT USER 0 MOV E,A STA TSELUSR ; RESET TEMPORARY USER NUMBER CALL SETUSR JR COMA ; TRY AGAIN ; ERROR ROUTINE TO SELECT DRIVE A: IF DEFAULT WAS ORIGINALLY SELECTED COMA0: LXI H,TEMPDR ; GET DRIVE FROM CURRENT COMMAND XRA A ; A=0 ORA M JNZ COM8 ; ERROR IF ALREADY DISK A: MVI M,1 ; SELECT DRIVE A: JR COMA ; FILE FOUND -- PROCEED WITH LOAD COMA1: LXI H,TPA ; SET START ADDRESS OF MEMORY LOAD COM2: PUSH H ; SAVE ADDRESS OF NEXT SECTOR XCHG ; ... IN DE CALL DMASET ; SET DMA ADDRESS FOR LOAD LXI D,FCBDN ; READ NEXT SECTOR CALL READ JRNZ COM3 ; READ ERROR OR EOF? POP H ; GET ADDRESS OF NEXT SECTOR LXI D,128 ; MOVE 128 BYTES PER SECTOR DAD D ; PT TO NEXT SECTOR IN HL LXI D,ENTRY-128 ; ARE WE GOING TO WRITE OVER CCP? MOV A,L ; COMPARE ADDRESS OF NEXT SECTOR (HL) SUB E ; TO START OF CCP (DE) MOV A,H SBB D JRNC PRNLE ; ERROR IF SAME JR COM2 ; OTHERWISE CONTINUE ; LOAD ERROR PRNLE: CALL PRINT DB 'Too Big',0 JMP RSTCCP COM3: POP H ; LOAD COMPLETE! DCR A JRNZ PRNLE CALL RESETUSR ; RESET CURRENT USER NUMBER ; USER MUST BE SET BEFORE LOGIN IS DONE CALL DLOGIN ; LOG IN DEFAULT DRIVE CALL SCANER ; SEARCH COMMAND LINE FOR NEXT TOKEN LXI H,TEMPDR ; SAVE PTR TO DRIVE SPEC PUSH H MOV A,M ; SET DRIVE SPEC STA FCBDN MVI A,10H ; OFFSET FOR 2ND FILE SPEC CALL SCAN1 ; SCAN FOR IT AND LOAD IT INTO FCBDN+16 POP H ; SET UP DRIVE SPECS MOV A,M STA FCBDM XRA A STA FCBCR LXI D,TFCB ; COPY TO DEFAULT FCB LXI H,FCBDN ; FROM FCBDN MVI B,33 ; SET UP DEFAULT FCB CALL MOVEHD LXI H,CIBUFF COM4: MOV A,M ; SKIP TO END OF 2ND FILE NAME ORA A ; END OF LINE? JRZ COM5 CPI ' ' ; END OF TOKEN? JRZ COM5 INX H JR COM4 ; LOAD COMMAND LINE INTO TBUFF COM5: MVI B,0 ; SET CHAR COUNT LXI D,TBUFF+1 ; PT TO CHAR POS COM6: MOV A,M ; COPY COMMAND LINE TO TBUFF STAX D ORA A ; DONE IF ZERO JRZ COM7 INR B ; INCR CHAR COUNT INX H ; PT TO NEXT INX D JR COM6 ; RUN LOADED TRANSIENT PROGRAM COM7: MOV A,B ; SAVE CHAR COUNT STA TBUFF CALL CRLF ; NEW LINE CALL DEFDMA ; SET DMA TO 0080 CALL SETUD ; SET USER/DISK CALL TPA ; RUN TRANSIENT CALL SETU0D ; SET USER 0/DISK CALL LOGIN ; LOGIN DISK JMP RESTRT ; RESTART CCP ; TRANSIENT LOAD ERROR COM8: CALL RESETUSR ; RESET CURRENT USER NUMBER ; RESET MUST BE DONE BEFORE LOGIN CALL DLOGIN ; LOG IN DEFAULT DISK JMP ERROR ; RESET SELECTED USER NUMBER IF CHANGED RESETUSR: LDA TMPUSR ; GET OLD USER NUMBER MOV E,A ; PLACE IN E JMP SETUSR ; RESET ; FILE TYPE FOR COMMAND COMMSG: DB 'COM' ; 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 LDA FCBFN ; GET FIRST CHAR OF TOKEN SUI ' ' ; ANY CHAR? LXI H,TEMPDR ORA M JNZ ERROR JMP RESTRT RNGSUB: DB 0 ;0=$$$.SUB NOT PRESENT, ELSE $$$.SUB PRESENT ; ; FILE CONTROL BLOCK (FCB), ONE ; SUBFCB: DB 0 ;DISK NAME 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 ; ; 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 PRFLG: DB 0 ;PRINTER FLAG (0=NO, 0FFH=YES) PAGCNT: DB NLINES-2 ;LINES LEFT ON PAGE IORESL: DB 0 ;I/O RESULTS TDRIVE: DB 1 ;TEMP DRIVE NUMBER TEMPDR: DB 0 CHRCNT: DB 0 ;CHAR COUNT FOR TYPE TMPUSR: DB 0 ;TEMPORARY USER NUMBER FOR COM TSELUSR: DB 0 ;TEMPORARY SELECTED USER NUMBER END