;****************************************************************************** ; ; Subroutines for NBYE.MAC ; ; INCLOG: ;Increments a 16-bit BCD counter at a specified address. ; ; Ptr to Counter ==> HL ==> Unchanged ; ; Byte at (HL) Byte at (HL) + 1 ; ------------------------------------ ; Counter Format: | tens units | thousands hundreds | ; ------------------------------------ ; ;Exit Status: None ; ; XRA A ;ENSURE NO RESIDUAL FLAGS FOR 'DAA' MOV A,M ;GET CURRENT COUNTER VALUE LOW BYTE INR A ;INCREMENT IN BCD DAA MOV M,A RNC ;DONE IF NO CARRY TO NEXT DIGIT INX H ;ELSE BUMP THE HIGH BYTE TOO XRA A ;ENSURE NO RESIDUAL FLAGS FOR 'DAA' MOV A,M INR A DAA MOV M,A DCX H ;KEEP REGISTERS AS ADVERTISED RET ;DONE ; ; ; ILPRNT: ;In-Line Print Routine. Prints the 0FFH-terminated message ;immediately following the call to this routine. ; ; e.g. CALL ILPRT ; DB 'HELLO',0FFH ; ;Exit Status: None ; ; XTHL ;SAVE HL, GET MESSAGE ADDRESS MOV A,M ;GET NEXT CHARACTER INX H ;KEEP POINTER CURRENT XTHL ;FLIP STACK IN PLACE IN CASE DONE CPI 0FFH ;DONE? RZ ;...YES CALL MOUTPUT ;...NO, SEND THE CHARACTER AND CONTINUE JMP ILPRNT ; ; ; LDFILE: ;File Load Routine. Loads the file named immediately after ;the call into the TPA. The filename must be terminated with ;a 0FFH. If the drive is not specified, or the filename is ;not exactly 11 characters, or the file doesn't exist on the ;specified drive in user FILEUSR (see comments at top of NBYE), ;or any problems are encountered in loading the file, the ;routine exits with Not Loaded status. The drive specifier ;is per the CP/M FCB conventions (0 ==> default, 1 ==> A:). ;Also, the user number specified in FILEUSR is ignored if using ;CP/M 1.4. A CP/M end-of-file (1AH) is written after the last ;record of the file to guard against text files that are exact ;multiples of 128 bytes. If the file is successfully loaded, ;a carriage return/line feed pair are sent to the remote user ;(and local console if DUAL$IO). ; ; Call Example: CALL LDFILE ; DB 1,'MYFILE COM',0FFH ; ; ==> Load MYFILE.COM from Drive A: ; into the TPA. ; ;Exit Status: C ==> File NOT Loaded ; NC ==> File Loaded Okay ; ; XTHL ;SAVE HL, GET POINTER TO FILE SPEC PUSH B ;SAVE OTHER REGISTERS PUSH D LXI D,FCB ;MOVE FILESPEC TO FCB MVI B,12 SETFCB: MOV A,M ;GET CHARACTER FROM FILESPEC INX H ;KEEP POINTER CURRENT CPI 0FFH ;CONFIRM WE HAVEN'T TERMINATED PREMATURELY JZ NOFNAM ;AND EXIT IF SO STAX D ;ELSE KEEP MOVING THE FILESPEC INX D DCR B JNZ SETFCB MOV A,M ;FILESPEC MOVED -- CONFIRM WE'RE AT THE END INX H CPI 0FFH JZ OK2LD ;LOAD THE FILE IF OKAY FNDRTN: MOV A,M ;ELSE FIND THE TRUE RETURN ADDRESS AND EXIT INX H CPI 0FFH JNZ FNDRTN NOFNAM: POP D ;RESTORE REGISTERS AND EXIT WITH NO LOAD POP B XTHL STC RET ; OK2LD: POP D ;PUT TRUE RETURN ON STACK POP B XTHL PUSH B ;AND SAVE ALL REGISTERS PUSH D PUSH H LDA CPMVER ;GET CP/M VERSION NUMBER CPI 020H MVI C,32 ;AND SET FILE USER AREA IF VERSION 2.0 OR LATER MVI E,FILEUSR CNC BDOS LXI H,FCB+12 ;FINISH SETTING UP THE FCB MVI B,21 RDYFCB: MVI M,0 INX H DCR B JNZ RDYFCB MVI C,15 ;NOW OPEN THE FILE LXI D,FCB CALL BDOS INR A ;SUCCESSFUL? JZ NOFILE ;...NO, ABORT LXI H,BASE+100H-128 ;...YES, INITIALIZE THE DMA ADDRESS SHLD BYEDMA NXTREC: LHLD BYEDMA ;BUMP DMA ADDRESS TO NEXT RECORD LXI D,128 DAD D SHLD BYEDMA DAD D ;NOW CONFIRM WE WON'T OVERWRITE THE BDOS MOV A,H CMA MOV D,A MOV A,L CMA MOV E,A INX D LHLD BDOS+1 DAD D JNC NOFILE ;AND ABORT IF WE WOULD HAVE MVI C,26 ;NOW SET DMA ADDRESS THROUGH CP/M BYEDMA EQU $+1 LXI D,0 CALL BDOS MVI C,20 ;READ NEXT RECORD LXI D,FCB CALL BDOS ORA A ;SUCCESSFUL? JZ NXTREC ;...YES, CONTINUE WITH NEXT RECORD LHLD BYEDMA ;...NO, WE'VE REACHED THE END OF THE FILE MVI M,1AH ; SO ENSURE AT LEAST ONE EOF CHARACTER LXI H,0 ;ENSURE DEFAULT BUFFER IS CLEAR SHLD BASE+080H POP H ;RESTORE REGISTERS POP D POP B JMP CRLF ;SEND CR/LF PAIR AND EXIT WITH CARRY CLEAR ; NOFILE: STC ;DIDN'T COMPLETE THE LOAD, SO EXIT WITH CARRY POP H POP D POP B RET ; ; ; RESET: ;Routine to initialize the counters and set the flag so we ;know they've been set. ; ;Exit Status: None ; ; PUSH H ;DON'T DESTROY ANY REGISTERS LXI H,055AAH ;SET FLAG SO WE KNOW COUNTERS ARE OKAY SHLD USRFLG LXI H,0 ;NOW RESET THE COUNTERS SHLD VOICE SHLD ANSCAL SHLD LOGDON POP H ;RESTORE REGISTERS AND EXIT RET ; ; ; PRNLOG: ;Routine to print the current contents of the counters on ;the local console. ; ;Exit Status: None ; ; PUSH H ;SAVE REGISTERS IFT CALLBAK CALL LCLMSG DB CR,LF,' Voice Calls: ',0FFH LHLD VOICE CALL KNTDSP ENDIF ;CALLBAK CALL LCLMSG DB CR,LF,'Calls Answered: ',0FFH LHLD ANSCAL CALL KNTDSP CALL LCLMSG DB CR,LF,' Logons: ',0FFH LHLD LOGDON CALL KNTDSP CALL LCLMSG DB CR,LF,0FFH POP H ;RESTORE REGISTERS AND EXIT RET ; ; ; KNTDSP: ;Routine to display the hex contents of HL on the local ;console. ; ; Value to display ==> HL ==> Unchanged ; ;Exit Status: None ; ; MOV A,H ;DISPLAY HIGH-ORDER BYTE CALL SHOBYT MOV A,L ;AND FOLLOW WITH LOW-ORDER SHOBYT: PUSH PSW ;SAVE BYTE VALUE ANI 11110000B ;ISOLATE HIGH-ORDER HEX DIGIT RRC ;MOVE TO LOW-ORDER NYBBLE RRC RRC RRC CALL HEXSHO ;SEND HEX VALUE TO DISPLAY POP PSW ;RESTORE BYTE ANI 00001111B ;ISOLATE LOW-ORDER HEX DIGIT HEXSHO: ADI '0' ;MAP DIGIT TO ASCII CHARACTER CPI '9'+1 ;IN 0-9 RANGE? JC HDOK ;...YES ADI 'A'-'9'-1 ;...NO, MAP TO A-F FOR VALID HEX DIGIT HDOK: PUSH B ;SEND RESULTING DIGIT TO DISPLAY AND EXIT MOV C,A CALL LCOUT POP B RET ; ; ; PATCH: ;Routine to patch the BIOS jump vectors to vector through ;NBYE. ; ;Exit Status: None ; ; PUSH B ;SAVE REGISTERS PUSH D PUSH H MVI A,0FFH ;SET PATCHED STATUS STA PATCHD LHLD BIOSVECT+13 ;STORE ORIGINAL CONSOLE OUTPUT ROUTINE FOR SHLD COVECT ;XMODEM AND FRIENDS CALL BJADR ;SET BIOS JUMP TABLE ADDRESS AND COUNT XCHG ;AS DESTINATION LXI H,BYEJT ;SOURCE IS THE LOCAL JUMP TABLE FOR 'BYE' CALL MOVE ;MOVE IN PLACE POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; UNPATCH: ;Routine to undo what PATCH did -- i.e. to restore the ;original BIOS jump vectors. ; ;Exit Status: None ; ; PUSH B ;SAVE REGISTERS PUSH D PUSH H XRA A ;SET UNPATCHED STATUS STA PATCHD CALL BJADR ;GET BIOS JUMP TABLE ADDRESS AND COUNT XCHG ;AND RESTORE THE ORIGINAL JUMP TABLE LXI H,BIOSVECT CALL MOVE POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; BJADR: ;Routine to get BIOS jump table address and move count for ;PATCH and UNPATCH. ; ; ??? ==> BC ==> Move Count ; ??? ==> HL ==> BIOS Jump Table Address ; ;Exit Status: None ; ; LHLD WBOOT+1 ;GET BIOS WARM BOOT ADDRESS DCX H ;BACK UP TO START OF THE JUMP TABLE DCX H DCX H LXI B,15 ;SET MOVE COUNT AND EXIT RET ; ; ; MOVE: ;Routine to move memory. ; ; Nbr Bytes to Move ==> BC ==> 0 ; Destination Address ==> DE ==> Dest Adrs + Count ; Source Address ==> HL ==> Source Adrs + Count ; ;Exit Status: None ; ; MOV A,B ;MORE BYTES TO MOVE? ORA C RZ ;...NO MOV A,M ;...YES, MOVE A BYTE STAX D INX D ;UPDATE POINTERS AND COUNTER INX H DCX B JMP MOVE ;AND CONTINUE ; ; ; MSTAT: ;'Modem' Status Routine. Actually gets status from either ;the local console (if DUAL$IO) or the modem. ; ;Exit Status: Z ==> No Character Ready ; NZ ==> Character is Ready ; ; CALL LOWCHK ;KEEP REVECTORING BDOS JUMP IF BELOW BDOS CALL CHECK ;CHECK CARRIER IFT DUAL$IO CALL LCSTAT ;CHECK LOCAL CONSOLE STATUS RNZ ;AND EXIT IF A CHARACTER IS READY ENDIF ;DUAL$IO JMP MISTAT ;ELSE RETURN TRUE MODEM STATUS ; ; ; MINPUTU: ;Same as MINPUT, but converts lower case to upper case ;before exit. ; ;Exit Status: None ; ; CALL MINPUT ;GET NEXT CHARACTER UPPER: CPI 'a' ;AND CONVERT TO UPPER CASE IF NEEDED RC CPI 'z'+1 RNC SUI 020H RET ; ; ; MINPUT: ;'Modem' Input Routine. ; ; ??? ==> Acc ==> Next Input Character ; ; NOTE: If no input in MAXIDLE minutes, the caller is ; disconnected and BYE reentered at RESTART. If ; carrier is lost BYE will be reentered at CONXIT. ; ;Exit Status: None ; ; IFT MAXIDLE NE 0 PUSH B ;SAVE REGISTERS PUSH D MVI B,MAXIDLE ;SET MAXIMUM TIME WE'LL WAIT FOR INPUT MIN1: LXI D,IDLEKNT ;SET TIMEOUT VALUE FOR APPROX ONE MINUTE ENDIF ;MAXIDLE NE 0 MIN2: CALL MSTAT ;ANYTHING AVAILABLE? IFT MAXIDLE EQ 0 JZ MIN2 ;...NO, KEEP WAITING ELSE JNZ MIN3 ;...YES, PROCESS IT CALL KDELAY ;...NO, KEEP WAITING BUT UPDATE WAIT TIME FIRST DCX D MOV A,D ORA E JNZ MIN2 DCR B JNZ MIN1 POP D POP B CALL ILPRNT ;TIMED OUT -- TELL CALLER AND KICK HIM OFF DB CR,LF DB ' [Input Timed Out] ',BELL,0FFH JMP RESTART MIN3 EQU $ POP D ;RESTORE REGISTERS POP B ENDIF ;MAXIDLE EQ 0 ; IFT DUAL$IO CALL LCSTAT ;GET LOCAL CONSOLE STATUS JZ NOLCL CALL LCIN ;AND IF READY GET THE CHARACTER AND EXIT JZ MINPUT ;UNLESS A NULL RET NOLCL EQU $ ENDIF ;DUAL$IO CALL MINP ;GET MODEM INPUT ANI 01111111B ;STRIP PARITY BIT JZ MINPUT ;IGNORE NULLS PUSH B ;CHECK FOR HARDCOPY LOG MOV B,A ;SAVING CHARACTER FOR LATER USE MOV C,A ; AND FOR OUTPUT LDA HARDON ORA A JZ HCPY2 ;NO HARDCOPY LOG NEEDED MOV A,B ;WANT HARDCOPY LOG, SO CHECK FOR SPECIAL CHARS CPI CR ;ALLOW CARRIAGE RETURN JZ HCPY1 CPI ' ' ;...BUT NO OTHER CONTROL CHARACTERS JC HCPY2 HCPY1: CALL LISTOUT MOV A,B CPI CR ;FOLLOW CARRIAGE RETURNS WITH LINE FEEDS MVI C,LF CZ LISTOUT HCPY2: MOV A,B ;RESTORE CHARACTER POP B ;RESTORE REGISTERS AND EXIT CPI 'C'-040H ;...UNLESS A CONTROL-C RNZ LDA WBOOT ;NEED TO CHECK CONTROL-C'S CPI JMP ;AND IF NOT ENABLED MAP TO CONTROL-K MVI A,'C'-040H RZ MVI A,'K'-040H RET ; ; ; MOUTPUTC: ;Same as MOUTPUT, but get input in C. ; ; Char to Send ==> C ==> Unchanged ; ;Exit Status: None ; ; MOV A,C ;PUT CHARACTER IN PLACE FOR 'MOUTPUT' ;...NOW FALL INTO IT ; ; ; MOUTPUT: ;'Modem' Output Routine. ; ; Char to Send ==> Acc ==> ??? ; ;Exit Status: None ; ; PUSH B ;SAVE REGISTERS ANI 01111111B ;FORCE PARITY BIT TO 0 CPI 'a' ;CHECK FOR LOWER CASE CHARACTER JC MO1 CPI 'z'+1 JNC MO1 MOV C,A ;SAVE IF SO LDA ULCSW ;SEE IF LOWER CASE IS ALLOWED ORA A MOV A,C ;(RESTORE REGARDLESS) JZ MO1 ;...YES IT IS SUI 020H ;...NO IT'S NOT -- CHANGE TO UPPER CASE MO1: CPI LF ;LINE FEED? JNZ MO2 ;...NO LDA LFEEDS ;...YES, SEE IF THEY'RE ALLOWED ORA A MVI A,LF ;(IN CASE THEY ARE) JZ MO2 ;...LINE FEEDS OKAY MVI A,NULL ;...LINE FEEDS NOT OKAY -- REPLACE WITH NULL MO2: MOV C,A ;SAVE CHARACTER IN C CALL LOWCHK ;KEEP REVECTORING BDOS JUMP IF BELOW BDOS LDA PATCHD ;SKIP MODEM OUTPUT IF NOT PATCHED ORA A JZ SKPMDM CALL CHECK ;ELSE CHECK THE CARRIER JC SKPMDM ;AND DON'T SEND TO MODEM IF KNOWN CARRIER LOSS MOV A,C ;SEND CHARACTER TO MODEM CALL MOUT SKPMDM EQU $ IFT DUAL$IO CALL LCOUT ;SEND TO LOCAL CONSOLE MOV A,C ENDIF ;DUAL$IO POP B ;(RESTORE REGISTERS IN CASE NO NULLS) CPI CR ;TIME FOR NULLS? JZ MO3 ;...YES CPI LF RNZ ;...NO MO3: LDA NULLS ;GET NULL COUNT ORA A RZ ;SKIP IF NONE PUSH B ;ELSE SEND THE NULLS MOV B,A MONULS: MVI A,NULL CALL MOUTPUT DCR B JNZ MONULS POP B RET ; ; ; LCOUT: ;Local console output routine. Uses original BIOS vector ;so output is only to local console. ; ; Char to Send ==> C ==> Unchanged ; ;Exit Status: None ; ; PUSH B ;SAVE REGISTERS IN CASE BIOS ROUTINE KILLS THEM PUSH D PUSH H MOV A,C ;CHECK FOR BELL CPI BELL JNZ LCOUT1 LDA BELLSW ;CHECK LOCAL BELL TRAP BEFORE SENDING A BELL ORA A JNZ LCOUT2 ;AND DON'T ALLOW IF THE TRAP IS ON LCOUT1: CALL BIOSVECT+12 ;DO OUTPUT THROUGH THE BIOS LCOUT2: POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; LCIN: ;Local console input routine. Uses original BIOS vector ;so input is from local console only. ; ; ??? ==> Acc ==> Input Character ; ;Exit Status: Z ==> Input character was a Null ; NZ ==> Input was not a Null ; ; PUSH B ;SAVE REGISTERS IN CASE BIOS ROUTINE KILLS THEM PUSH D PUSH H CALL BIOSVECT+9 ;DO INPUT THROUGH THE BIOS CALL FKEYCHK ;CHECK FOR SPECIAL FUNCTION KEYS ORA A ;SET STATUS TO TRAP NULLS POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; LCSTAT: ;Local console status routine. Uses original BIOS vector ;so status is from local console only. ; ;Exit Status: Z ==> No Character Ready ; NZ ==> Character is Ready ; ; PUSH B ;SAVE REGISTERS IN CASE BIOS ROUTINE KILLS THEM PUSH D PUSH H CALL BIOSVECT+6 ;GET STATUS FROM THE BIOS ORA A ;SET FLAGS POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; LCLMSG: ;In-Line Print Routine for local console only. Prints the ;0FFH-terminated message immediately following the call to ;this routine. ; ; e.g. CALL LCLMSG ; DB 'HELLO',0FFH ; ;Exit Status: None ; ; XTHL ;SAVE HL, GET MESSAGE ADDRESS CALL HLPRNT ;PRINT THE MESSAGE XTHL ;RESTORE HL, RETURN ADDRESS TO STACK RET ;DONE ; ; ; HLPRNT: ;Prints the 0FFH-terminated message pointed to by HL to the ;local console. ; ; Ptr to Msg ==> HL ==> Ptr to Byte After Terminator ; ;Exit Status: None ; ; MOV A,M ;GET NEXT CHARACTER INX H ;KEEP POINTER CURRENT CPI 0FFH ;DONE? RZ ;...YES PUSH B ;...NO, SEND THE CHARACTER AND CONTINUE MOV C,A CALL LCOUT POP B JMP HLPRNT ; ; ; LISTOUT: ;List device output routine. Uses original BIOS vector to ;send characters to the list device. ; ; Char to Send ==> C ==> Unchanged ; ; PUSH B ;SAVE REGISTERS IN CASE BIOS ROUTINE KILLS THEM PUSH D PUSH H LSTADR EQU $+1 CALL $-$ ;SEND CHARACTER THROUGH THE BIOS POP H ;RESTORE REGISTERS AND EXIT POP D POP B RET ; ; ; LOWCHK: ;Checks for BYE running below BDOS, and keeps revectoring ;if so to protect against anyone who may have revectored the ;jump elsewhere. ; ;Exit Status: None ; ; PUSH H LHLD BYE+1 ;RUNNING BELOW BDOS? MOV A,H ORA L POP H ;(IN CASE NOT) RZ ;...NO PUSH H ;...YES, REVECTOR THE BDOS JUMP, THEN EXIT LXI H,BYE SHLD BDOS+1 POP H RET ; ; ; LCRQST: ;Routine to service a local console request while BYE is ;waiting for a call. ; ;Exit Status: None ; ; PUSH H CALL CLRCRT ;CLEAR THE SCREEN LXI H,BVMSG ;SEND VERSION MESSAGE CALL HLPRNT POP H CALL PRNLOG ;AND SHOW CURRENT CALL STATISTICS CALL LCLMSG ;GET SYSOP'S OPTION DB CR,LF,LF DB 'Options: A)nswer the phone, do not execute .COM file',CR,LF DB ' C)omfile -- Answer phone and execute .COM file' DB CR,LF DB ' E)xit BYE to CP/M',CR,LF DB ' R)esume waiting for a call',CR,LF DB ' Z)ero the call counters',CR,LF,0FFH LCOPT: CALL LCLMSG DB CR,LF,'Option? ',BELL,0FFH CALL LCIN CALL UPPER ;FORCE TO UPPER CASE STA OPTION ;SAVE IN CASE 'A' OR 'C' PUSH B MOV C,A ;ECHO TO CONSOLE CALL LCOUT MOV A,C ;RESTORE OPTION POP B CPI 'A' ;PROCESS THE REQUEST JZ ANSWER ;...ANSWER THE PHONE, SKIP .COM FILE CPI 'C' JZ ANSWER ;...ANSWER THE PHONE, EXECUTE THE .COM FILE CPI 'E' JZ EXITBYE ;...EXIT BYE TO CP/M CPI 'Z' JNZ NOTZRO CALL LCLMSG ;...ZERO THE CALL COUNTERS DB 'eroing the Counters...',CR,LF,' R',0FFH CALL RESET JMP RSMWT NOTZRO: CPI 'R' JNZ LCOPT ;...INVALID OPTION -- WAIT FOR ANOTHER RSMWT: CALL LCLMSG ;...RESUME WAITING FOR A CALL DB 'esuming...',CR,LF,0FFH RET ; EXITBYE EQU $ CALL LCLMSG DB 'xiting from BYE...',0FFH CALL MEXIT ;SHUT DOWN THE MODEM CALL USRFINI ;DO ANY SPECIAL USER FUNCTIONS XRA A ;FORCE LOAD ON NEXT 'BYE' COMMAND STA BLDFLG LHLD BYE+1 ;REVECTOR TO TRUE BDOS IF WE'RE BELOW IT MOV A,H ORA L JZ NOTLOW SHLD BDOS+1 NOTLOW: CALL UNPATCH ;ENSURE BIOS VECTORS ARE UNALTERED MVI A,JMP ;AND NO TRICKS AT THE WARM BOOT JUMP STA WBOOT JMP WBOOT ;NOW OFF TO CP/M ; ; ; CHKCAR: ;Routine to check for carrier. If none present, waits ;2 seconds before giving up, so features like call waiting ;won't kill us. If carrier is present a check is made for ;valid drive/user selection, and A0: is automatically logged ;in if not a valid selection. ; ;Exit Status: C ==> No Carrier ; NC ==> Carrier Still There ; ; PUSH B ;SAVE REGISTERS MVI B,20 ;SET MAX WAIT TIME FOR CARRIER CC1: CALL MCARRIER ;HAVE CARRIER? JNZ CC2 ;...YES CALL DELAY ;...NO, WAIT A BIT DCR B JNZ CC1 STC ;TIMED OUT -- RETURN BAD STATUS POP B RET ; CC2 EQU $ IFT HAVCLK CALL TCHECK ;ABORT IF CALLER HAS EXCEEDED MAX TIME ENDIF ;HAVCLK PUSH D ;SAVE ADDITIONAL REGISTERS PUSH H LXI D,BASE+4 ;POINT TO LOGIN BYTE LDAX D ;GET DRIVE ANI 00001111B LXI H,MXDRV ;OKAY? CMP M JC CC3 ;...YES CPI EXCDRV-'A' JZ CC3 ;...YES LDAX D ;...NO, FORCE CALLER TO DRIVE A: ANI 11110000B ; BUT LEAVE USER NUMBER INTACT STAX D CALL ILPRNT ;TELL CALLER WHAT WE DID, THEN WARM BOOT DB CR,LF,'*** Invalid Drive ***',0FFH JMP WBOOT ; CC3: LDA CPMVER ;CHECK CP/M VERSION NUMBER CPI 020H JC CC4 ;SKIP USER NUMBER CHECK IF PRIOR TO 2.0 LDAX D ;ELSE GET USER ANI 11110000B RRC ;TO LOW NYBBLE FOR CHECKS RRC RRC RRC LXI H,MXUSR ;OKAY? CMP M JC CC4 ;...YES LDAX D ;...NO, FORCE CALLER TO USER 0 ANI 00001111B ; BUT LEAVE DRIVE NUMBER INTACT STAX D CALL ILPRNT ;TELL CALLER WHAT WE DID, THEN WARM BOOT DB CR,LF,'*** Invalid User Number ***',0FFH JMP WBOOT ; CC4: POP H ;ALL CHECKS OKAY -- RESTORE REGISTERS, SET POP D ; GOOD STATUS, AND EXIT POP B ORA A RET ; ; ; IFT HAVCLK ; TCHECK: ;Routine to check the user's elapsed time with the maximum ;allowable logon time, and automatically log him out if he ;has exceeded the maximum time. ; ; LDA TFLAG ;BE SURE WE'VE SET THIS GUY'S LOG ON TIME ORA A RZ ;AND DON'T DO ANY CHECKS IF NOT CALL TIME ;UPDATE ALL TIME AREAS RC ;SKIP TIME CHECKS IF TIME NOT AVAILABLE PUSH D ;ELSE COMPUTE TIME ON THE SYSTEM PUSH H LXI H,LHOUR LDA CHOUR SUB M JNC HRSSET ADI 24 HRSSET: MOV L,A MVI H,0 DAD H ;2 x HRS DAD H ;4 x HRS MOV D,H MOV E,L DAD H ;8 x HRS PUSH H DAD D ;12 x HRS XCHG POP H DAD H ;16 x HRS PUSH H DAD D ;28 x HRS XCHG POP H DAD H ;32 x HRS DAD D ;60 x HRS ==> NUMBER OF MINUTES FOR HOURS XCHG LXI H,LMIN LDA CMIN SUB M MVI H,0 JNC MINSET DCR H MINSET: MOV L,A DAD D ;NUMBER OF MINUTES ON THE SYSTEM LXI D,-MAXMIN ;SEE IF WE'VE EXCEEDED THE MAXIMUM DAD D POP H ;(RESTORE REGISTERS IN CASE NOT) POP D RNC ;STILL OKAY LDA WRTLOC ;DON'T KILL CALLER IF FILE UPDATE IN PROGRESS ORA A RNZ LDA STATUS ;IS THIS GUY PRIVELEGED? ORA A RNZ ;...YES, DON'T KICK HIM OFF STA TFLAG ;...NO, KICK HIM OFF AND RESTART BYE CALL ILPRNT ; (ZERO TFLAG TO PREVENT RECURSION) DB CR,LF DB 'Your Time is Up!' DB BELL,BELL,0FFH JMP RESTART ENDIF ;HAVCLK ; ; ; FKEYCHK: ;Routine to implement local function keys. ;The functions implemented are: ; ; ~ ==> Toggle Function Key State ; (Function Keys Initially Enabled) ; Ctrl-B ==> Toggle Remote Console Blankout ; Ctrl-D ==> Print 'System Going Down...' msg ; Ctrl-G ==> Toggle local console bell trap ; Ctrl-N ==> Immediate Disconnect to Kill 'Nurds' ; Ctrl-O ==> Send 'Message from SYSOP:' and stay in ; message state until Ctrl-C (so caller ; can't interrupt the message). ; Ctrl-P ==> Toggle HardCopy Log (Printer) ; Ctrl-U ==> Implement User Special Function ; (see NBYEUSER.DOC) ; Ctrl-W ==> Toggle Priveleged User Status (Wheel) ; Ctrl-Z ==> Clear the local console. ; ;The function keys only operate in the 'patched' state except ;the clear screen function (Ctrl-Z), which also operates in ;the 'unpatched' state. ; ;This routine may destroy all registers except the character ;in Acc, which must be unchanged if not a function key, and ;changed to a null if it is. E.G. ; ; Input Char ==> Acc ==> Unchanged if Not Function Key ; 0 otherwise ; ;Exit Status: None ; ; PUSH PSW ;SAVE THE CHARACTER WE JUST RECEIVED LDA PATCHD ;ARE BIOS VECTORS PATCHED? ORA A JNZ FKEYSOK ;...YES POP PSW ;...NO, SKIP CHECKS EXCEPT FOR CLEAR SCREEN CPI 'Z'-040H RNZ CLRCRT: CALL LCLMSG ;CLEAR THE LOCAL TERMINAL CLRMSG DB 0FFH XRA A ;AND RETURN WITH A NULL RET ; FKEYSOK EQU $ ; POP PSW ;RESTORE CHARACTER TO CHECK PUSH PSW CPI '~' ;TOGGLE FUNCTION KEY STATE? JNZ FKEYS1 ;...NO POP PSW ;...YES, BALANCE STACK AND TOGGLE THE STATE FKEYSW EQU $+1 MVI A,0FFH XRI 11111111B STA FKEYSW PUSH PSW ;SAVE CURRENT STATE CALL LCLMSG ;AND EXIT WITH STATUS MESSAGE DB CR,LF,'Function Keys',0FFH POP PSW JZ NOWOFF CALL NOWON ;SEND CURRENT FUNCTION KEY STATUS CALL LCLMSG ;FOLLOW WITH A MENU OF THE FUNCTIONS DEFB CR,LF DEFB " ~ ==> Toggle Function Key State",CR,LF DEFB " Ctrl-B ==> Toggle Remote Console Blankout",CR,LF DEFB " Ctrl-D ==> Print 'System Going Down...' Message",CR,LF DEFB " Ctrl-G ==> Toggle local console bell trap",CR,LF DEFB " Ctrl-N ==> Disconnect Immediately to kill a 'Nerd'",CR,LF DEFB " Ctrl-O ==> Send 'Message from SYSOP:' until Ctrl-C",CR,LF DEFB " Ctrl-P ==> Toggle Hardcopy Log",CR,LF DEFB " Ctrl-U ==> Do User Special Function",CR,LF DEFB " Ctrl-W ==> Toggle Priveleged User Status",CR,LF DEFB " Ctrl-Z ==> Clear local console",CR,LF,LF,0FFH XRA A ;EXIT WITH A NULL RET ; NOWOFF: CALL LCLMSG DB ' Now OFF',CR,LF,0FFH XRA A RET ; FKEYS1: LDA FKEYSW ;FUNCTION KEYS ALLOWED? ORA A JNZ CKFKEY ;...YES, CHECK THE KEYS NOW POP PSW ;...NO, RESTORE THE CHARACTER AND EXIT RET ; CKFKEY: POP PSW ;RESTORE KEY TO CHECK CPI 'B'-040H ;TOGGLE REMOTE CONSOLE BLANKOUT? JNZ FKEYS2 ;...NO LDA LOSTFLG ;...YES, TOGGLE IT ORA A JZ MAKEFF XRA A JMP SLF MAKEFF: MVI A,0FFH SLF: STA LOSTFLG ;STORE NEW FLAG VALUE ORA A ;SET STATUS PUSH PSW ;SAVE IT CALL LCLMSG ;AND EXIT WITH STATUS MESSAGE DB CR,LF,'Remote Console Blanking',0FFH DSTATE: POP PSW JZ NOWOFF NOWON: CALL LCLMSG DB ' Now ON',CR,LF,0FFH XRA A RET ; FKEYS2: CPI 'D'-040H ;PRINT 'System Going Down...' MSG? JNZ FKEYS3 ;...NO CALL ILPRNT ;...YES, SEND THE MSG DB CR,LF DB "System Going Down, Please Type 'BYE' to Log Off",CR,LF,0FFH XRA A ;EXIT WITH A NULL RET ; FKEYS3: CPI 'G'-040H ;TOGGLE LOCAL CONSOLE BELL TRAP? JNZ FKEYS4 ;...NO BELLSW EQU $+1 MVI A,0 ;...YES, TOGGLE IT XRI 11111111B STA BELLSW PUSH PSW ;SAVE CURRENT STATE CALL LCLMSG ;AND EXIT WITH STATUS MESSAGE DB CR,LF,'Local Bell Trap',0FFH JMP DSTATE ; FKEYS4: CPI 'N'-040H ;KILL A NURD? JNZ FKEYS5 ;...NO JMP CONXIT ;...YES, KILL THE CALLER ; FKEYS5: CPI 'O'-040H ;SEND 'Message From SYSOP...' ? JNZ FKEYS6 ;...NO CALL ILPRNT ;...YES, SEND HEADER MESSAGE DB CR,LF,'Message From SYSOP: ',0FFH SOPMSG: CALL BIOSVECT+9 ;GET THE MESSAGE, ONE CHARACTER AT A TIME CPI 'C'-040H ;END OF MESSAGE? JNZ SOPCHR ;...NO, ECHO THE CHARACTER CRLF: CALL ILPRNT ;...YES, SEND CR/LF AND EXIT WITH NULL DB CR,LF,0FFH XRA A STA SOPKNT ;ALSO ZERO CHARACTER COUNT FOR SYSOP MESSAGES RET ; SOPCHR: CPI RUBOUT ;CHECK FOR DELETE CHARACTER JZ SOPDEL CPI BKSPC JZ SOPDEL CPI CR ;CHECK FOR CARRIAGE RETURN (END OF LINE) JZ SOPEOL CALL MOUTPUT ;WASN'T DELETE OR END OF LINE, SO SEND THE CHAR LXI H,SOPKNT ;AND UPDATE THE COUNT INR M JMP SOPMSG ;AND CONTINUE WITH THE MESSAGE ; SOPKNT EQU $+1 SOPDEL: MVI A,0 ;ANYTHING TO DELETE? ORA A JZ SOPMSG ;...NO, JUST CONTINUE DCR A ;...YES, UPDATE COUNT AND SEND BKSPC,SPACE, STA SOPKNT ; BKSPC TO DELETE THE CHARACTER CALL ILPRNT DB BKSPC,' ',BKSPC,0FFH JMP SOPMSG ;AND CONTINUE WITH THE MESSAGE ; SOPEOL: CALL CRLF ;SEND CR/LF TO TERMINATE THE LINE JMP SOPMSG ;AND CONTINUE WITH THE MESSAGE ; FKEYS6: CPI 'P'-040H ;TOGGLE HARDCOPY LOG? JNZ FKEYS7 ;...NO LDA HARDON ;...YES, DO IT XRI 11111111B STA HARDON PUSH PSW ;SAVE RESULTING STATUS CALL LCLMSG ;TELL LOCAL USER CURRENT STATUS AND EXIT DB CR,LF,'HardCopy Log',0FFH JMP DSTATE ; FKEYS7: CPI 'U'-040H ;SPECIAL USER FUNCTION? JZ USRFUNC ;IF SO GO IMPLEMENT IT CPI 'W'-040H ;TOGGLE PRIVELEGED USER STATUS? JNZ FKEYS8 ;...NO LDA STATUS ;...YES, DO IT XRI 11111111B STA STATUS PUSH PSW ;SAVE RESULTING STATUS CALL LCLMSG ;TELL LOCAL USER CURRENT STATUS AND EXIT DB CR,LF,'Priveleged User Flag',0FFH JMP DSTATE ; FKEYS8: CPI 'Z'-040H ;CLEAR LOCAL CONSOLE? JZ CLRCRT ;...YES RET ;...NO, THIS WASN'T A FUNCTION KEY