; ; NUBYE v1.01 ; 07/30/86 ; ; - REMOTE CONSOLE PROGRAM FOR CP/M AND MODEM - ; For use with CP/M 2 or CP/M 3 ; ; This is the "Body" of NUBYE -- append this to the "Head" (after setting up ; the EQUates for your system's needs), using PIP (or similar) and assemble ; with your favorite assembler. To cut down on space requirements, I've ; removed many of the section descriptions, leaving the line comments intact. ; ; - Tom Brady ; IF NOT CPM3 ORG 100H ; START: LXI SP,ISTACK ; Set stack for initialization routine MVI C,32 MVI E,241 CALL BDOS CPI 77 ; NUBYE running? JNZ STARTA ; No, relocate code LHLD BDOS+1 ; Load BDOS vector INX H ; Compute start of NUBYE INX H INX H PCHL ; Execute already loaded code ENDIF ; STARTA: IF LMBELL AND (NOT CPM3) MVI A,BELOFF MOD 256 ; Get initial bell status STA KILBEL ; Store it ENDIF ; IF COMFILE AND REENTER AND (NOT CPM3) XRA A STA COMFLG ; Clear .COM load status ENDIF ; IF COMFILE AND (NOT CPM3) LXI H,COMFCB+1 ; BBS .COM file name LXI D,ENTMSG ; Display buffer MVI B,8 ; Length of filename ; STAR1: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR2 STAX D INX D INX H DCR B JNZ STAR1 ; Loop until space or 8 chars found ; STAR2: XRA A STAX D ; Store 0 as print terminator ENDIF ; IF EXFILE AND (NOT CPM3) LXI H,EXITFCB+1 ; Exit com filename LXI D,EXTMSG ; Display buffer MVI B,8 ; Length of filename ; STAR3: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR4 STAX D INX D INX H DCR B JNZ STAR3 ; Loop until space or 8 chars found ; STAR4: XRA A STAX D ; Store 0 as print terminator ENDIF ; IF MSGDSC AND (NOT CPM3) LXI H,MSGFCB+1 ; Message com filename LXI D,MSGMSG ; Display buffer MVI B,8 ; Length of filename ; STAR5: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR6 STAX D INX D INX H DCR B JNZ STAR5 ; Loop until space or 8 chars found ; STAR6: XRA A STAX D ; Store 0 as print terminator ENDIF ; IF NOT CPM3 LHLD BDOS+1 ; Load BDOS vector LXI D,-((CCPL*256)+CCPOFF) ; 2k btyes in CCP plus offset DAD D ; Make room for the CCP LXI D,OBJEND-1 ; Set up the source pointer LXI B,OBJEND-BEGOBJ ; Set up byte counter ; BLOCK: LDAX D ; Get program byte MOV M,A ; Move program byte MOV A,B ; Get byte count ORA C ; Finished block transfer? JZ UPDATE ; Yes, check on the opcode values DCX D ; No, set source pointer DCX H ; Set destination pointer DCX B ; Set byte counter JMP BLOCK ; Continue block transfer until finished ; UPDATE: XCHG ; Move the source addrress into 'HL' CALL NEGHL ; Prepare value for subtraction DAD D ; Form the program offset SHLD OFFSET ; Save the program offset XCHG ; Set up the offset register LXI H,ENDOBJ ; Get the ending addr of the prgm code DAD D ; Form new ending addr (new location) SHLD ENDRNG ; Save the ending addr of the prgm code LXI H,BEGOBJ ; Get the start address of program code DAD D ; Form new beginning addr (new location) DCX H ; Set up the source pointer for the ; modification routine entry MODIFY: INX H ; Point to the next instruction (hopefully) DB LXID ; Get the address of the end of NUBYE ; ENDRNG: DW 0 MOV A,E SUB L MOV A,D SBB H ; Have we finished moving this block? JC BEGIN ; Yes, we can begin now. MVI B,INST3E-INST3 ; Get number of elements in the table LXI D,INST3 ; Set up the 3-byte opcodes table ptr ; THRBYT: LDAX D ; Get opcode byte from table CMP M ; Is this byte a 3-byte opcode? JZ CHANGE ; Change the 2nd and 3rd bytes if needed INX D ; No, advance table pointer DCR B ; End of 3-byte table? JNZ THRBYT ; No, keep looking MVI B,INST2E-INST2 ; Get the number of table elements LXI D,INST2 ; Set up the 2-byte-opcodes-table ptr ; TWOBYT: LDAX D ; Get opcode byte from table CMP M ; Is this byte a 2-byte opcode? JZ SKIP ; Yes, skip it and continue DCR B ; No, end of 2-byte table? INX D ; Advance table pointer JNZ TWOBYT ; No, keep looking JMP MODIFY ; Yes, a one-byte opcode, keep going ; SKIP: INX H ; Advance object code pointera JMP MODIFY ; Continue search ; CHANGE: LXI D,OBJEND ; Set up end of range pointer LXI B,BEGOBJ ; Set up beginning of range pointer INX H ; Advance pointer to the LSB of the addr MOV A,E SUB M INX H ; Advance pointer to the MSB of the addr MOV A,D SBB M JC MODIFY DCX H ; Set ptr back to the LSB of the addr MOV A,M SUB C INX H ; Advance pointer to the MSB of the addr MOV A,M SBB B JC MODIFY DCX H ; Set ptra back to LSB of the address DB LXID ; Load DE with the offset value ; OFFSET: DW 0 MOV A,M ; Get base address ADD E ; Change LSB to new address MOV M,A ; Update memory INX H ; Advance pointer to the MSB of the addr MOV A,M ; Get the MSB of the base addr ADC D ; Change LSB to new address MOV M,A ; Update memory JMP MODIFY ; Take care of the next instruction ; NEGHL: MOV A,H CMA MOV H,A ; Get the complement of the MSB MOV A,L CMA MOV L,A ; Get the complement of the LSBb INX H ; Make 'HL' totally negative RET ; BEGIN: LHLD BDOS+1 PUSH H LXI D,BEGOBJ LHLD OFFSET ; Get prgram offset DAD D ; Form address of new BDOS address SHLD BDOS+1 ; Update BDOS vector INX H POP D MOV M,E INX H MOV M,D INX H PCHL ; Jump to relocated NUBYE program ; INST2: DB 6,0EH,16H,1EH,26H,2EH,36H,3EH,0C6H DB 0CEH,0D3H,0D6H,0DBH,0DEH,0E6H,0EEH,0F6H,0FEH ; INST2E EQU $ ; End of 2 byte op codes ; INST3: DB 1,11H,21H,22H,2AH,31H,32H,3AH,0C2H DB 0C3H,0C4H,0CAH,0CCH,0CDH,0D2H,0D4H,0DAH,0DCH DB 0E2H,0E4H,0EAH,0ECH,0F2H,0F4H,0FAH,0FCH ; INST3E EQU $ ; End of 3 byte op codes ; ENDIF ; NOT CPM3 ; IF CPM3 ; Loader for cp/m plus SERIAL: DB 0,0,0,0,0,0 ; Room to insert serial number STARTX: JMP FTEST ; Beginning of program NEXT: DB 0C3H ; Jump instruction op-code DW 0 ; Next module in line (or BDOS) PREV: DW 0 ; Previous module REMOV: DB 0FFH ; Remove flag initially set NONBNK: DB 0 ; >0 to load only in non-banked CP/M RSXNAM: DB 'BYE ' ; The name of this RSX LOADER: DB 0 ; Loader flag DB 0,0 ; Reserved RSXTEST EQU 100 ; Test for RSX in memory RSXINIT EQU 101 ; RSX initialize function code RSXTERM EQU 102 ; RSX terminate function code NPARAM EQU 2 ; Number of parameters required in caller's ; RSX parameter block TABENT MACRO M1,M2 ; TABENT subfunction,address DB M1 ; RSX subfunction number DW M2 ; RSX routine address ENDM ; FTABLE: TABENT RSXTEST,RSXTST ; Test for RSX in memory TABENT RSXINIT,RSXINT ; RSX initialize TABENT RSXTERM,RSXTRM ; RSX terminate and deactivate DB 0FFH ; End of table RSXPB: DW 0 ; Caller's RSX parameter block address SPSAVE: DW 0 ; Pointer storage INIFLG: DB NOTINIT ; If not reset, all operations rejected INIT EQU 0 ; Initialized NOTINIT EQU 0FFH ; Not initialized DS 48 LOCSTK: ; Internal stack STKSAV: DW 0 ; Caller's stack pointer ; FTEST: MOV A,C ; Get BDOS function CPI 60 ; Could this one be for us? JNZ NEXT ; Nope - ignore this function LXI H,0 ; Start out with a zero DAD SP ; Now HL contains the stack pointer SHLD STKSAV ; Save caller's stack pointer LXI SP,LOCSTK ; Load stack pointer with our stack address PUSH B ; Save caller's environment (except AF) PUSH D PUSH H PUSH D ; Save caller's DE XCHG ; Get caller's parameter block address in HL INX H ; Move past function code MOV A,M ; Get number of parameters passed CPI NPARAM ; Exactly right number of parameters? JNZ BYPASS ; If not, pass the caller on to the next RSX INX H ; Move pointer past number of parameters MOV E,M ; Get first parameter address INX H MOV D,M INX H ; HL points to second parameter PUSH D ; Save first parameter address MOV E,M ; Get second parameter address INX H MOV D,M XCHG SHLD SPSAVE ; Save address of actual caller parameters POP D ; Restore first parameter address LXI H,RSXNAM ; Get location of the RSX name field MVI B,8 ; Set loop counter to 8 (characters) ; CKNAME: LDAX D ; Get next character of caller's parameter INX D ; Update pointer to caller's parameter CMP M ; Compare to this RSX's name INX H ; Update pointer to my RSX name JNZ BYPASS ; No match - not for us DCR B ; Decrement loop counter JNZ CKNAME ; Go back for more if not done POP D ; Recall passed parameter XCHG SHLD RSXPB ; Save caller's RSX parameter block address CALL NEXT ; Check if this the only one copy in memory CPI 0FFH ; Did the call reach BDOS? JZ REALTHING ; Yes - this RSX is unique MVI A,0FFH ; No - this RSX duplicates one already resident STA REMOV ; Mark this RSX to be unloaded JMP DONE ; And return to caller ; REALTHING: LHLD RSXPB ; Retrieve original parameter pointer XCHG ; Move the address to DE LDAX D ; Get RSX function number from parameter block MOV C,A ; Save RSX function LDA INIFLG ; Get the initialize flag CPI INIT ; Is the RSX initialized? JZ ALLFOK ; Yes - all functions are ok MOV A,C ; Retrieve the function code CPI RSXINIT ; Is it an initialize request? JNZ ERFUN ; Nope, treat the request as if it is undefined ; ALLFOK: LXI H,FTABLE ; Get function table address ; FLOOK: MOV A,M ; Pick up function value INX H ; Increment pointer to routine address CPI 0FFH ; Reached end of table yet? JZ ERFUN ; Yes - bad function passed CMP C ; Is this our function? JZ FMATCH ; Yes - go jump to our function INX H ; Increment HL past INX H ; Address field JMP FLOOK ; And try again ; FMATCH: MOV E,M ; Get address of routine INX H ; In register pair DE MOV D,M XCHG ; Set up HL for the jump LXI D,DONE ; Get routine return address PUSH D ; Put it on the stack for RET instruction PCHL ; And jump to the requested routine ; ERFUN: JMP BYPAS1 ; No match - continue with next RSX ; RSXTST: LHLD SPSAVE ; Retrieve second parameter address MVI A,0CCH ; Get a special status code INX H ; Increment to first status byte MOV M,A ; And update the caller's status byte INX H ; Increment to second status byte MOV M,A ; And update the caller's status byte RET ; Complete - return to caller ; RSXINT: LDA INIFLG ; Get the initialize indicator CPI NOTINIT ; Is this a clean copy? JNZ RSXIN2 ; No - can't reinitialize, must use RESET LHLD SPSAVE ; Retrieve second parameter address INX H ; Increment to status byte XRA A ; Get a zero STA REMOV ; Mark the RSX resident MOV M,A ; Set status normal STA INIFLG ; And mark this RSX as open for business ENDIF ; CPM3 ; IF COMFILE AND REENTER AND CPM3 XRA A STA COMFLG ; Clear .COM load status ENDIF ; IF COMFILE AND CPM3 LXI H,COMFCB+1 ; BBS com filename LXI D,ENTMSG ; Display buffer MVI B,8 ; Length of filename ; STAR1: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR2 STAX D INX D INX H DCR B JNZ STAR1 ; Loop until space or 8 chars found ; STAR2: XRA A STAX D ; Store 0 as print terminator ENDIF ; IF EXFILE AND CPM3 LXI H,EXITFCB+1 ; Exit com filename LXI D,EXTMSG ; Display buffer MVI B,8 ; Length of filename ; STAR3: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR4 STAX D INX D INX H DCR B JNZ STAR3 ; Loop until space or 8 chars found ; STAR4: XRA A STAX D ; Store 0 as print terminator ENDIF ; EXFILE AND CPM3 ; IF MSGDSC AND CPM3 LXI H,MSGFCB+1 ; Message com filename LXI D,MSGMSG ; Display buffer MVI B,8 ; Length of filename ; STAR5: MOV A,M ; Move it to display buffer CPI ' ' ; Stop on first space JZ STAR6 STAX D INX D INX H DCR B JNZ STAR5 ; Loop until space or 8 chars found ; STAR6: XRA A STAX D ; Store 0 as print terminator ENDIF ; IF LMBELL AND CPM3 MVI A,BELOFF MOD 256 ; Get initial bell status STA KILBEL ; Store it ENDIF ; IF CPM3 JMP BGOBJ2 ; Go fire it up ; RSXIN2: LHLD SPSAVE ; Retrieve second parameter address INX D ; Increment to status byte MVI A,0FFH ; Get an error code MOV M,A ; And let caller know we failed JMP RSXIN9 ; Complete - exit ; RSXIN9: RET ; All done - return to caller ; RSXTRM: CALL MDCARCK ; Check for caller still logged on JNZ START0 ; Have carrier - log caller off and reset CALL UNPATCH ; Remove BIOS overrides JMP BGOBJ2 ; Clean up and restart this program ; RSXCLR: MVI A,0FFH STA INIFLG ; Reset the init flag to prevent further access STA REMOV ; Flag to drop this RSX on next warm boot LHLD SPSAVE ; Retrieve second parameter address INX H ; Increment to status byte XRA A ; Get a zero MOV M,A ; Set status normal RET ; BYPASS: POP D ; Restore stack condition ; BYPAS1: POP H POP D POP B LHLD STKSAV ; Restore caller's stack pointer SPHL JMP NEXT ; Move on to next RSX ; DONE: XRA A ; Indicate to caller that we were here POP H POP D POP B LHLD STKSAV ; Restore caller's stack pointer SPHL RET ; Return to caller ENDIF ; CPM3 ; DS 40 ISTACK: DW 0 ; Top of stack ; IF NOT CPM3 BEGOBJ: JMP 0 ; Filled by BEGIN ENDIF ; BGOBJ2: JMP START0 ; Hop over fixed vectors ; MCBOOT: JMP MBOOT ; Off to warm boot JMP PRNLOG ; Go print out items of interest ; MXUSR: DB MAXUSR ; Runtime maximum user area available MXDRV: DB MAXDRV-'@' ; Runtime maximum drive available TOVAL: DB TOVALUE ; Number of mins. to wait before timeout NULLS: DB 0 ; Number of nulls after ULCSW: DB 0 ; Upper case only switch (32=upper case) LFEEDS: DB 0 ; Line feed mask (0=don't mask) WRTLOC: DB 0 ; Location RBBS pokes so NUBYE won't hang HARDON: DB 0FFH ; If 0, hardlog is deactivated MDMOFF: DB 0 ; If 0FFH, do not output to modem COVECT: DW 0 ; Console output vector for XMODEM HDROFF EQU $-MCBOOT ; Offset to 'BYE' that follows tells us DB 'BYE' ; (NUKMD/XMDM/KMD) that NUBYE is being used ; IF LMBELL COMFLG: DB 0 ; Status of .COM file load ENDIF ; IF NOT LMBELL BELLON: DB 0 ; 0FFH = ok to Chat (local console bell on) ENDIF ; LCPTR: JMP LCDATA ; First byte is user access restrictions/flags ; while user is logged on, used for his ; total time-on after log off. LCDATA is ; address of buffer for DSPLC data, LCBUF ; in length. Next 2 bytes point to lastcalr ; data buffer. MXTIME: JMP RTCBUF ; First bye holds maximum time allowed ; IF LMBELL BELLON: DB 0 ; 0FFH = ok to Chat (local console bell on) ENDIF ; TWIT: DB 0 ; Twit = 0FFH if sysop uses ^N function ; LCTON EQU LCPTR ; Byte to store lastcallers time-on-system ; value (in binary). You may equate this ; to a low-memory byte or use this byte in ; the NUBYE fixed variable table...can then ; be used by your entry/exit program to ; determine how long the previous caller ; was on. ; =================================== ; OFFICIAL START OF THE NUBYE PROGRAM ; =================================== ; ; ************************************************** ; ; ++++ Install your I/O port insert here ++++ ; (get it from the NUBYEINS.LBR) ; ; ************************************************** ; ; ++++ Install your Modem dependent insert here ++++ ; (only if IMAT is set NO) ; ; ************************************************** ; ; ; TIME: <--- Clock reader code ; ; Install your clock reader code here, if you have a clock. Find your ; special insert in the NU-CLOCK library. If you write your own insert, ; make sure you store binary (not BCD) values in CCHOUR and CCMIN (use ; RTCBUF to store BCD clock data, then call the BCDBIN routine to convert ; it to binary). See NUC-QXnn.INS as an example of handling a BCD clock. ; The registers are saved before TIME is called, so you don't have to ; save them. ; ; IF CLOCK ; ; ************************************************** ; ++++ Install your TIME routine here ++++ ; (between the IF and ENDIF) ; ************************************************** ; ENDIF ; ; ; NOTE: BDOS CALL 84 ; This is currently defined for METAL systems and not coded into NUBYE ; due to it being a proprietary message base. You may install a subroutine ; here (at the label SRUDEF:) that can be called from a transient program ; like your entry/exit .com file. To access it, MVI C,84 CALL BDOS from ; your program. Make sure this subroutine has a RET at the end. The ; transient program, making a BDOS 84 call may pass data to this subroutine ; in any register(s) except A and C. This routine can return data to the ; calling program in any register(s). ; ; *************************************************** ; ++++ Install your user defined subroutine here ++++ SRUDEF: ; SubRoutine U DEFine for BDOS CALL 84 RET ; ; ++++ end of user defined subroutine ++++ ; *************************************************** ; IF IMAT IMRING: CALL MDINST ; Character ready from modem? RZ ; No CALL MDINP ; Get the modem response code ANI 7FH ; Strip parity ENDIF ; IF IMAT AND PRGRSS CALL RCDISP ; Display RC for local sysop PUSH PSW LXI H,LFMSG CALL PRINTL ; Turn up a line on crt POP PSW ENDIF ; IF IMAT CPI CR RZ CPI LF RZ CPI '2' ; Ring? JNZ REDOIT ; No, something wrong, start over ENDIF ; IMRIN1: IF IMAT AND (NOT NOATA) CALL EATALL ; Swallow c/r or lf from result code ; IMRIN2: LXI H,IMATA CALL IMSEND ; Send ATA to modem ENDIF ; IF IMAT LXI B,30000 ; We will check for RC every 1 ms for 30 secs ; MDR1: CALL MDINST JZ RCHEK ; And wait for response CALL MDINP ; Then fetch it ANI 7FH ; And strip parity ENDIF ; IF IMAT AND PRGRSS CALL RCDISP ; And show results code (RC) ENDIF ; IF IMAT CPI CR JZ MDR1 CPI LF JZ MDR1 CPI '1' ; 300 baud or 2400 bps? JZ MDR2 ; Yes, so check which CPI '2' ; Missed ring indicator? JZ IMRIN1 ; Answer again CPI '3' ; Timeout? JZ REDOIT ; Yes CPI '4' ; Error? JZ REDOIT ; Yes ENDIF ; IF IMAT AND (NOT HS300) CPI '5' ; 1200 bps? JZ SET12 ; Yes ENDIF ; IF IMAT AND (NOT HS300) AND ANCHOR JMP SET3 ; Not 1, 3, 4 or 5 means Anchor connected ; at 300 bps but sent wrong result code ENDIF ; IF IMAT AND (NOT ANCHOR) JMP MDR1 ; Wait 30 secs for valid response ENDIF ; MDR2: IF IMAT AND HS2400 CALL CHECK1 ; Let's see if it's a 1 or 10 ENDIF ; IF IMAT AND HS2400 AND PRGRSS CALL RCDISP ; Show RC to local terminal ENDIF ; IF IMAT AND HS2400 CPI '0' JZ SET24 ; For Vadic and Hayes, 10 means 2400 bps CPI '2' ; Voice? (12 for USR Courier and others) JZ REDOIT ; Yes CPI '9' ; 2400 bps? (some send this...) JZ SET24 ; Yes ENDIF ; IF IMAT JMP SET3 ; Was a 1 (300 baud) ENDIF ; IF IMAT RCHEK: CALL KDELAY ; Wait 1 millisecond DCX B MOV A,C ORA B ; Time up? JNZ MDR1 ; No, Keep trying ; REDOIT: POP H ; Go reset if none of these responses LXI H,VCNUM ; Update the voice call INR M ; Counter LXI H,LFMSG CALL PRINTL ; Turn up a line on crt CALL MDSTOP ; Turn dtr off while we reset JMP HANGUP1 ENDIF ; IF IMAT AND PRGRSS RCDISP: PUSH B PUSH H PUSH PSW ; Save A STA RCSHOW LXI H,RCSHOW ; And show results code (RC) CALL PRINTL POP PSW PUSH PSW CPI CR JNZ RCDIS1 MVI A,LF STA RCSHOW ; Force a LF after a CR LXI H,RCSHOW CALL PRINTL RCDIS1: POP PSW POP H POP B RET ENDIF ; IMAT AND PRGRSS ; IF IMAT CHECK1: LXI B,400 ; Try for 400 ms CHECK2: CALL KDELAY DCX B MOV A,B ORA C JZ CHECK3 ; 400 ms is up CALL MDINST ; Character ready? JZ CHECK2 ; No, keep waiting CALL MDINP ; Yes, fetch it ANI 7FH ; And strip parity RET ; And return ; CHECK3: MVI A,0FFH ; Set error code RET ; And return ENDIF ; IF IMAT SET3: CALL DLP CALL SET300 ; Set port to 300 baud MVI A,BP300 STA MSPEED ; Set speed indicator JMP FINISH ENDIF ; IF IMAT AND (NOT HS300) SET12: CALL DLP CALL SET1200 ; Set port to 1200 bps MVI A,BP1200 STA MSPEED ; And speed indicator JMP FINISH ENDIF ; IF IMAT AND HS2400 SET24: CALL DLP ; 1 sec delay CALL SET2400 ; Set port to 2400 bps MVI A,BP2400 STA MSPEED ; Set speed indicator ENDIF ; IF IMAT FINISH: CALL MDCARCK ; Still have carrier? JZ REDOIT ; No, reset modem POP H ; Reset CALL on the stack CALL PATCH ; Patch the jump table MVI B,6 ; .6 sec more with 1 sec already used= 1.6 sec CALL DLP1 ; Wait to enter terminal mode CALL EATALL ; Clear input port (takes .4 sec more) and JMP ANSW ; Greet the user at his speed ; IMINIT: CALL DLP CALL EATALL ENDIF ; IF IMAT AND DOATZ LXI H,IMATZ ; Reset the modem CALL IMSEND CALL DLP CALL EATALL ; Swallow the response ENDIF ; IF IMAT LXI H,IMINT CALL IMSEND ; Go initialize the modem ENDIF ; IF IMAT AND SHORTB CALL DLP CALL EATALL LXI H,IMINT1 CALL IMSEND ; For the short buffered modems, use 2 strings ENDIF ; IF IMAT AND PRGRSS IMINT1: CALL CHECK1 CPI 0FFH ; No result code? JZ IMINT2 ; Yes, inform sysop and retry CPI '0' ; Executed ok? CZ RCDISP ; Display result code JZ IMINT3 ; And continue CPI '4' ; Modem error? CZ RCDISP JNZ IMINT1 ; Wait for a zero or four or 0FFH IMINT2: LXI H,CMDERR ; We have a 4 or 0FFH CALL PRINTL ; Inform sysop of problem JMP IMINIT ; Try it again IMINT3: LXI H,LFMSG CALL PRINTL ; Turn up a line on crt ENDIF ; IMAT AND PRGRSS AND NOT ANCHOR ; IF IMAT CALL DLP JMP EATALL ; Get any garble from the modem ; DLP: MVI B,10 ; DLP1: CALL DELAY DCR B JNZ DLP1 RET ; EATALL: CALL CHECK1 CPI 0FFH ; All characters eaten? JNZ EATALL ; No, keep eating RET ; IMQUIT: LXI H,LFMSG CALL PRINTL CALL DLP ; Some need this delay CALL EATALL ; ...and this garbage collection ENDIF ; IMAT ; IF (IMAT AND DOATZ) AND (NOT OFFHK) LXI H,IMATZ CALL IMSEND ; Send ATZ message to modem CALL DLP CALL EATALL ENDIF ; IF IMAT AND (DOATZ OR NOATA) AND (NOT OFFHK) LXI H,IMUSR CALL IMSEND ; Send ATS0=0 to modem JMP EATALL ENDIF ; IF IMAT AND OFFHK LXI H,IMATH1 CALL IMSEND ; Send ATH1 to the modem JMP EATALL ; Some need this ENDIF ; IF IMAT IMSEND: PUSH B ; Save 'BC' registers SHLD ADDSTR ; Save start of command string ; IMSEN1: CALL MDOUTST ; Modem ready for character? JZ IMSEN1 ; No, go check again MOV A,M ; Get the character PUSH PSW CALL MDOUTP ; Send the character POP PSW ENDIF ; IF IMAT AND PRGRSS CALL RCDISP ; Display the command string ENDIF ; IF IMAT AND ECHO ; Hayes needs echo checking CALL CHECK1 ; Get the echo character CMP M ; Same? JNZ NOECHO ; No, let's resend entire command string ENDIF ; IF IMAT INX H ; Point to next MOV A,M ; Get next character ORA A ; Has all been sent JNZ IMSEN1 ; No, go send another character POP B ; Restore the BC registers RET ; Return past end of message ENDIF ; NOECHO: IF IMAT AND ECHO AND PRGRSS LXI H,NOEMSG CALL PRINTL ; Inform sysop of echo error ENDIF ; IF IMAT AND ECHO CALL MDOUTST ; Wait for modem ready JZ NOECHO MVI A,CR CALL MDOUTP ; Force a c/r to end command string CALL DLP ; Let modem settle CALL EATALL ; Make sure input is clear LHLD ADDSTR ; Restore address of command string JMP IMSEN1 ; And send it again ENDIF ; IF IMAT AND NODTR IMHANG: CALL EATALL MVI B,30 ; Some modems need 3 sec, doesn't hurt others CALL DLP1 ; This routine will hang up the phone LXI H,IMESC ; Using +++ATH CALL IMSEND CALL EATALL MVI B,15 CALL DLP1 LXI H,IMATH CALL IMSEND ; For modems without DTR support JMP DLP ENDIF ; IF IMAT AND CLOCK AND AUTOSYS CHKDWN: LDA SYSDWN ORA A ; Going down flag set? RNZ ; Yes MVI A,OFFHR ORA A ; Midnight (0)? JZ CHKDN1 ; Yes SUI 1 JMP CHKDN2 ; CHKDN1: ADI 23 ; Set to 11 pm ; CHKDN2: LXI H,CCHOUR ; Current hour CMP M ; Equal? RNZ ; No LDA CCMIN ; Current minute ADI DOWNMIN ; Auto logoff at OFFHR CPI 59 ; Ready? RC ; No JMP SYSDOWN ; Set the countdown ENDIF ; CLOCK AND AUTOSYS ; IMDONE: IF NOT NODTR CALL MDSTOP ; Drop DTR ENDIF ; CALL MDCARCK ; Carrier? RZ ; Carrier gone, return ; IF IMAT AND NODTR CALL IMHANG ; Send +++ATH ENDIF ; JMP IMDONE ; Keep looping ; START0: IF TIMEON AND (NOT MBBS) LDA TON STA LCTON ; Save last callers time-on for entry pgm ENDIF ; IF NOT CPM3 LHLD BDOS+1 ; Get beginning address of NUBYE program SHLD BDADDR ; Save address of NUBYE start ENDIF ; IF CPM3 LHLD STARTX+1 ENDIF ; IF NOT CPM3 LHLD BEGOBJ+1 ; Get real bdos call ENDIF ; MOV A,H ; Get high address byte LXI D,BYERSX ; Have to do it this way to fool relocator CMP D ; Already pointed to BYERSX? JZ NORPTC ; Then don't patch again SHLD REALBD+1 ; Save it in the interceptor routine LXI H,BYERSX ; Get address of interceptor routine ; IF CPM3 SHLD STARTX+1 ; So we intercept BDOS calls ENDIF ; IF NOT CPM3 SHLD BEGOBJ+1 ; Save it so it goes thru our extended BDOS ENDIF ; NORPTC: XRA A ; A=0 STA LFEEDS ; And line feed flag STA MDMOFF ; Show no carrier lost STA SYSDWN ; Reset system going down flag STA TWIT ; And twit status flag STA ULCSW ; And upper/lower case flag STA WRTLOC ; And write-in-progress flag ; IF CLOCK AND AUTOSYS STA SYSON ; Show system off ENDIF ; IF TIMEON AND CLOCK CALL MDCARCK ENDIF ; IF TIMEON AND CLOCK AND (NOT PBBS) CNZ RMTOS ; Report final time-on-system ENDIF ; IF MINICK MVI A,IOVAL ; Get proper initial value STA IOBYTE ; Set it in IOBYTE ENDIF ; IF MBBS LXI SP,STACK CALL MDCARCK JZ START1 ; No carrier, skip this LDA LCDATA CPI ' ' ; User logged in? JZ GOODBY ; No, carry on XRA A STA 0 ; Prep mbbs MVI A,0FFH STA WRTLOC ; To prevent hangup LDA FCB+1 CPI 'C' ; Comments requested? JNZ MBBS00 ; No, else... PUSH PSW MVI C,85 MVI E,255 CALL BDOS ; Get access flag ANI 2 ; BBS access? JNZ MBBSC ; Yes, do comments POP PSW ENDIF ; MBBS00: IF MBBS AND PRNTGB LXI H,GBMSG CALL PRINTB ; Say goodbye to user ENDIF ; IF MBBS MBBS01: CALL IMDONE ; Drop carrier and fix so phone won't answer JMP MBBSNC ; And tell sysop ; MBBSC: POP PSW STA OPTION ; So we know to load mbbs or login LXI H,MBBS1 CALL PRINTB ; Wait for mbbs to load CALL LODCOM ; Load mbbs for comments ENDIF ; IF MBBS AND REENTER XRA A STA COMFLG ENDIF ; IF MBBS CALL MDCARCK ; Still have carrier? JZ MBBSNC ; No MVI A,0CDH STA 0 ; So mbbs will ask for comments CALL 100H ; Now do it ; MBBSNC: MVI A,0FFH STA MDMOFF ; So bye will handle rest of this MVI A,'E' STA OPTION ; So bye will trap mbbs return CALL LODCOM ; Load login CALL 100H ; Let login finish user stats ENDIF ; MBBS ; CALL MDCARCK ; Call modem carrier check routine JNZ GOODBY ; We have carrier, so say bye bye... ; START1: IF COMFILE LDA FCB+1 STA OPTION ; So remote cannot type BYE E MVI A,' ' STA FCB+1 ENDIF ; IF CPM3 MVI C,GTSCB ; Return base page of SCB LXI D,SCBPB CALL BDOS SHLD SCBBASE ; Save SCB address MVI L,SCBCOM ; 0FAH = common memory base page MOV A,M STA MEMBASE ; Save common memory base page MVI L,SCBBDOS ; 99H = base page of BDOS MOV A,M STA BDOSBASE ; Save base page of BDOS system ENDIF ; CALL PATCH ; Copies vectors for PRINTL CALL UNPATCH JMP HANGUP ; Prepare for first caller ; GOODBY: IF EXFILE AND (NOT MBBS) JMP LOGOFF ; Run the exit file ENDIF ; IF PRNTGB LXI H,GBMSG ; Goodbye message CALL PRINTB ; Print this message ENDIF ; CALL IMDONE ; Hang up the phone before doing this CALL UNPATCH ; Undo BIOS patches ; HANGUP: LXI SP,STACK ; Set up local stack ; IF COMFILE AND PRGRSS LXI H,ENTMSG CALL PRINTL ; Show it's the entry file ENDIF ; IF COMFILE CALL LODCOM ; Load the .COM file ENDIF ; HANGUP1:MVI A,WBDRIV-'A' ; Force next warmboot to user 0 STA 4 ; And drive wbdriv ; IF CPM3 CALL SETDRIVE ; Set to warmboot drive XRA A CALL SETUSER ; Set to User 0 ENDIF ; XRA A STA MDMOFF ; Clear modem status STA WRTLOC ; And wrtloc STA TON ; Reset time-on-system ; IF RTOK STA RTOKFG ; So BBS can be re-entered ENDIF ; IF ZCPR3 LHLD Z3CL ; Z3CL is the address of the MCLB MVI M,0 LXI H,SHSTK ; SHSTK is the addr of the Shell Stack CALL ZERO128 LXI H,Z3ENV+128 ; Z3ENV is the address of the Environ- CALL ZERO128 ; ment Descriptor...the TCAP is the ; Second 128 bytes LXI H,Z3MSG ; Z3MSG is the addr of the msg buffers MVI B,64 CALL ZEROM ENDIF ; IF COMFILE LDA OPTION CPI 'E' ; Run local? JZ HANGUP2 ; Yes ENDIF ; IF CLRSCR LXI H,CLRSEQ CALL PRINTL ; Clear local crt screen ENDIF ; LXI H,VMSG ; Load program info CALL PRINTL ; Display it to caller ; IF DSPLC OR MBBS OR PBBS LXI H,LFMSG CALL PRINTL LXI H,LCDATA CALL PRINTL ; Show sysop who was just on ENDIF ; IF DSPLC OR MBBS OR PBBS LXI H,LCDATA MVI B,LCBUF CALL ZEROM ; Fill with zeros for printl LXI H,LCFILL LXI D,LCDATA MVI B,15 CALL MOVE ; Put filler msg into lastcalr for now ENDIF ; IF TIMEON LXI H,TONMSG CALL PRINTL ; Show him how long he was on XRA A LXI H,TONMSD CALL DEC8 ; So next message will be 0 ENDIF ; IF TIMEON OR CLOCK MVI A,255 STA TCHKFG ; Show clock not read ENDIF ; CALL CALSUM ; Give sysop call summary ; IF ECBFIX HANGUP2: ENDIF ; IF IMAT AND HS300 CALL SET300 ENDIF ; IF IMAT AND HS1200 CALL SET1200 ENDIF ; IF IMAT AND HS2400 CALL SET2400 ENDIF ; CALL MDINIT ; Call modem initialization routine MVI A,0C3H ; Clear any traps left from .COM file STA 0 ; IF FUNKEY LDA LCDFLG ; Get Last Call status flag ORA A ; Sysop wants local? JNZ BEXCPM ; Yes ENDIF ; IF NOT ECBFIX HANGUP2: ENDIF ; IF CLRSCR LXI H,CLRSEQ CALL PRINTL ; Clear local crt screen ENDIF ; IF COMFILE LDA OPTION CPI 'E' ; Execute comfile locally? JNZ DRVOFF ; No, continue ENDIF ; RUNE: IF COMFILE AND IMAT CALL IMQUIT ; Send ATH1 or ATS0=0 depending on OFFHK ENDIF ; IF COMFILE AND (NOT IMAT) CALL MDQUIT ; Drop DTR and send ATH1 or ATS0=0 to modem ENDIF ; IF COMFILE MVI A,0FFH STA MDMOFF ; Turn modem off STA WRTLOC ; And write flag MVI A,'L'-30H STA MSPEED ; MBBS needs this, others don't care JMP WELCOME ; Strictly for time stuff ENDIF ; DRVOFF: IF MOTOR CALL DSKOFF ; Turn off drives ENDIF ;1 ; RINGWT: CALL CONSTAT ORA A JZ RINGW1 CALL VCONIN ; Character typed ANI 7FH ; Strip parity bit CPI 'C'-40H ; CTL-C? CZ USRCHK ; Check for exit CPI 'E' ; Sysop wants local run? JZ USRUN ; Yes, else... CALL CKFUNC ; Check for function keys RINGW1: IF CLOCK AND AUTOSYS CALL TIME ; Get current time LXI H,CCHOUR ; Point at current hour LDA SYSON ; Get system on flag ORA A ; Has it already been turned on? JNZ RINGW2 ; Yes, so skip startup check MVI A,ONHR ; Startup hour CMP M ; Equal? JNZ RINGWT ; No, so wait MVI A,1 STA SYSON ; System is now on RINGW2: MVI A,OFFHR ; Shutdown hour CMP M ; Equal? ENDIF ; IF CLOCK AND AUTOSYS AND DROPLOC JZ EXCPM ; Yes, time to close shop ENDIF ; IF CLOCK AND AUTOSYS AND (NOT DROPLOC) JNZ RINGW3 ; No, so ok to check for ring XRA A STA SYSON ; System now off JMP RINGWT ; Loop back and wait for ONHR ENDIF ; RINGW3: IF IMAT CALL IMRING ; This routine does it all JZ RINGWT ENDIF ; IF NOT IMAT CALL MDCARCK ; Carrier? JZ RINGWT ; No, keep looping ENDIF ; ANSW: IF NOT CPM3 CALL BDCHEK ENDIF ; IF MOTOR CALL DSKON ; Turn on drives ENDIF ; IF (NOT USEZCPR) AND (ZCPR2 OR ZCPR3) MVI A,MAXUSR ; Reset maximum user area STA MXUSR ; Set it in bye INR A ; Bump it STA MAXUSER ; Set it in ZCPR MVI A,MAXDRV-'@' ; Reset maximum drive STA MXDRV DCR A STA MAXDRIV ENDIF ; IF CHGPATH CALL REMPAT ; Set up the secure path for caller ENDIF ; XRA A ; Make sure line feeds are on again STA LFEEDS STA CDOFF ; Limit for waiting for c/r STA FKFLAG ; F-key lead-in flag ; IF ZCPR2 OR ZCPR3 STA WHEEL ; Initiate with WHEEL off, always ENDIF ; MVI A,TOVALUE ; Reset timeout count STA TOVAL LXI H,CWCAR ; Get # of attempts INR M ; And add one ; IF IMAT JMP WELCOME ; Skip the old fashion CR detect method ENDIF ; IF NOT IMAT ANSWA: CALL SET300 MVI A,BP300 ; Poke in MSPEED value STA MSPEED CALL MDINP ; Clear garbage characters CALL MDINP ; ANSWB: CALL PATCH ; Patch jump table ENDIF ; IF PRGRSS AND (NOT IMAT) LXI H,MSG30 CALL PRINTL ; Print locally ENDIF ; IF NOT IMAT CALL TSTBAUD ; See if 300 baud JZ WELCOME ; Yes, exit ENDIF ; IF PRGRSS AND (NOT IMAT) AND (NOT HS300) LXI H,MSG12 CALL PRINTL ; Print locally ENDIF ; IF (NOT IMAT) AND (NOT HS300) CALL SET1200 ; Now check 1200 bps JNZ ANS0 MVI A,BP1200 ; Set the MSPEED pointer STA MSPEED CALL MDINP ; Clear garbage CALL TSTBAUD ; Check baud rate JZ WELCOME ENDIF ; ANS0: IF PRGRSS AND (NOT IMAT) AND HS2400 LXI H,MSG24 CALL PRINTL ; Print locally ENDIF ; IF (NOT IMAT) AND HS2400 CALL SET2400 ; Check for 2400 baud JNZ BADDO ; Start over MVI A,BP2400 ; Set speed indicator STA MSPEED CALL MDINP ; Clear garbage CALL TSTBAUD ; Check it JZ WELCOME ENDIF ; IF NOT IMAT BADDO: CALL UNPATCH ; Restore original jump table JMP ANSWA ; Test more - invalid baud rate ENDIF ; IF NOT CPM3 BDCHEK: PUSH H ; To make truly universal, (???) this DB LXIH ; Program always re-stores the BDOS ; BDADDR: DW 0000H ; Pointer at 6 and 7 set up location for SHLD 6 ; Beginning address of CONSOLX CTL POP H ; At every chance. This replaces the RET ; WMLOCK & OLDBD as in BYE2 AND BYE3 ENDIF ; CHECK: IF MINICK LDA IOBYTE ; Get IOBYTE ANI 80H ; Test for disk update RNZ ; Busy, wait until done ENDIF ; IF RBBS OR MBBS OR PBBS LDA WRTLOC ; Get write in progress flag ORA A RNZ ; Busy, wait until done ENDIF ; LDA MDMOFF ORA A ; Know modem off? RNZ ; Yes, skip this CALL CARCK ; See if carrier still on RNC ; All ok LXI H,CLMSG ; Carrier lost message CALL PRINTL ; Send this Message ; LOGOFF: LXI SP,STACK ; Ensure valid stack ; IF NOT CPM3 CALL BDCHEK ; In case carrier lost in LUX ENDIF ; CALL PATCH ; We need this so bye is in the loop ; IF TIMEON AND (NOT MBBS) LDA TON STA LCTON ; Preserve callers time-on for entry/exit pgm ENDIF ; CALL MDCARCK JZ LOGOF1 ; Skip over goodby data ; IF PBBS LDA TWIT ORA A ; Been this way? JZ $+9 ; No, so continue, else... CALL IMDONE ; Go ahead and drop carrier JMP LOGOF1 ; Skip data output MVI A,0FFH STA TWIT ; And show we've been this way ENDIF ; IF EXFILE AND (NOT BYHANG) JMP LOGOF2 ; Bypass close-out routines ENDIF ; IF TIMEON AND CLOCK AND (NOT PBBS) CALL RMTOS ; Report users final timeon ENDIF ; IF PRNTGB AND (NOT PBBS) LXI H,GBMSG CALL PRINTB ; Say goodbye ENDIF ; LOGOF1: MVI A,255 STA MDMOFF ; Show known loss of carrier STA TCHKFG ; Reset time flag LDA TWIT ; Get twit status ORA A ; Well? JNZ $+6 ; Yes, so skip next line (already done) CALL IMDONE ; Make sure phone is disconnected ; IF MBBS XRA A STA FCB+1 ; Prepare to exit thru MBBS STA 0 STA MXTIME LDA LCDATA ; If user was logged in CPI ' ' JNZ MBBS01 ; User logged in so exit thru MBBS ENDIF ; LOGOF2: IF CPM3 AND EXFILE XRA A ; Get a zero CALL SETUSER ; Set user to zero XRA A ; Get a zero CALL SETDRIVE ; Set drive to zero CALL UNPATCH ; CPM3 will exit thru another driver ENDIF ; IF EXFILE LXI H,EXTMSG CALL PRINTL ; So sysop knows exit file is executing MVI C,SETUSR ; Select user area for EXITFILE MVI E,EXUSR CALL BDOS MVI C,SELDSK ; Select default drive for EXITFILE MVI E,EXDRV-'A' CALL BDOS CALL LODEX CALL 100H ; EXITFIL was loaded ok, so run it ENDIF ; JMP PREOFF ; Prepare for next caller ; CKFUNC: IF LEADIN PUSH PSW ; Save character CPI LEADKY ; Lead-in key? JNZ FKEY1 ; No MVI A,0FFH ; Yes STA FKFLAG ; Set lead-in flag POP PSW ; Reset stack MVI A,7FH ; DEL for buffer RET ; FKEY1: LDA FKFLAG ORA A ; Lead-in already typed? JZ FKEXIT ; No XRA A ; Yes STA FKFLAG ; Reset the flag POP PSW ; Get character back ANI 1FH ; Make it a ctrl character ENDIF ; IF IMAT AND (NOT NOATA) MOV B,A PUSH B ; Save the character CALL MDCARCK ; Carrier? POP B MOV A,B JNZ ANSKNO ; Can't do this with carrier ENDIF ; IF IMAT AND (NOT NOATA) AND FUNKEY CPI ANSKEY-40H ; Sysop wants NUBYE to answer the phone CZ IMRIN2 ; So do it, IMRIN2 will restore the call ENDIF ; ANSKNO: IF (DSPLC OR MBBS OR PBBS) AND FUNKEY CPI WHOKEY-40H ; Display last caller data? JZ WHOSIT ENDIF ; IF TIMEON AND FUNKEY CPI TIMEKEY-40H ; Check user's time-on? JZ DTIME ENDIF ; IF (NOT CLRSCR) AND FUNKEY CPI CLSKEY-40H ; Clear screen? JZ CLEARIT ENDIF ; CPI BELLKEY-40H ; Toggle bell on/off? JZ BELLTOG MOV B,A PUSH B CALL MDCARCK ; Still have carrier? POP B MOV A,B RZ ; No, so skip the rest ; IF FUNKEY CPI LCKEY-40H ; Sysop wants on next? JZ LCDOIT CPI ULTKEY-40H ; Give user unlimited time? JZ ULTIME CPI BLNKKEY-40H ; Turn off caller's output for a moment? JZ BLNKTOG CPI WHLKEY-40H ; Toggle WHEEL? JZ WHLTOG CPI SYSDKEY-40H ; Going down? JZ SYSDOWN CPI TWITKEY-40H ; Hang up on user? JNZ ANSNO1 ; No, else... MVI A,0FFH STA TWIT ; Save status CALL IMDONE ; Drop carrier now JMP LOGOFF ; ANSNO1: CPI MSGKEY-40H ; Message from sysop? RNZ ; No, else fall through LXI H,MFSMSG ; SYSOP message CALL PRINTB ; Tell caller you want to say something ; SYSML: CALL VCONIN ; Get key from sysop CPI XITKEY-40H ; If exit key, exit loop JZ SYSMX MOV C,A ; Else echo to console and modem PUSH PSW CALL MOUTPUT POP PSW CPI 'H'-'@' ; If BS, do BS/SP/BS JZ SYSMBS CPI CR ; If CR, do CRLF JZ SYSMCR JMP SYSML ; SYSMCR: MVI C,LF ; Do linefeed after CR JMP SYSECH ; SYSMBS: MVI C,' ' CALL MOUTPUT MVI C,'H'-'@' ; SYSECH: CALL MOUTPUT JMP SYSML ; SYSMX: MVI C,CR CALL MOUTPUT ; Send CR MVI C,LF CALL MOUTPUT ; Send LF CALL MDINP CALL MDINP ; Clear caller junk first MVI A,CR ; Return with CR for buffer RET ; SYSDOWN:LDA SYSDWN ORA A ; Going down flag set? RNZ ; Yes LXI H,SGDMSG ; System going down message CALL PRINTB ; Send this message ENDIF ; FUNKEY ; IF (CLOCK OR TIMEON) AND FUNKEY CALL TCHECK ; Calculate current time-on-system LDA TON ; Fetch it ADI DOWNMIN ; Give him this much longer STA MXTIME ; And NUBYE will log him off ENDIF ; IF FUNKEY MVI A,CR ; Return with CR for buffer STA SYSDWN ; Set system going down flag RET ; LCDOIT: LDA LCDFLG ; Get last call status flag ORA A ; Set? RNZ ; Yes LXI H,LCDMSG ; Last call message CALL PRINTL ; Display it local MVI A,7FH STA LCDFLG ; Set it RET ENDIF ; FUNKEY ; BELLTOG: IF NOT LMBELL LDA BELLON ; Get NUBYE's bell status ENDIF ; IF LMBELL LDA KILBEL ; Get BBS bell status ENDIF ; ORA A MVI A,0FFH ; Prepare for on/off toggle ; IF NOT LMBELL LXI H,BELMON ENDIF ; IF LMBELL LXI H,BELMOFF ENDIF ; JZ BELLT1 ; Go turn bell on XRA A ; Else turn bell off ; IF NOT LMBELL LXI H,BELMOFF ENDIF ; IF LMBELL LXI H,BELMON ENDIF ; BELLT1: IF NOT LMBELL STA BELLON ENDIF ; IF LMBELL STA KILBEL ENDIF ; CALL PRINTL ; Print status message locally MVI A,7FH ; DEL for buffer RET ; IF FUNKEY ULTIME: XRA A STA MXTIME ; Give unlimited timeon to current caller ENDIF ; IF SETFLG AND FUNKEY MVI A,USRFLG ; Get predefined access flag bits STA LCPTR ; Set them ENDIF ; IF FUNKEY LXI H,ULTMSG CALL PRINTL ; So sysop will know MVI A,CR ; C/R for buffer RET ; BLNKTOG:LDA MDMOFF ORA A ; If zero, make 0FFH ENDIF ; FUNKEY ; IF (ZCPR2 OR ZCPR3) AND FUNKEY JZ DUSET ; Store sysops d/u values CALL RETDU ; Restore normal d/u values ENDIF ; IF (NOT ZCPR2) AND (NOT ZCPR3) AND FUNKEY MVI A,0FFH ; (we do not use CMA, because MDMOFF LXI H,SCRMOFF JZ BLNKT1 ; Could equal a different value like 1) XRA A ; If not zero, make it zero LXI H,SCRMON ENDIF ; BLNKT1: IF FUNKEY STA MDMOFF ENDIF ; IF (ZCPR2 OR ZCPR3) AND FUNKEY STA WHEEL ; Set wheel for sysop ENDIF ; IF FUNKEY CALL PRINTL MVI A,CR ; Return with cr for buffer RET ENDIF ; DUSET: IF (ZCPR2 OR ZCPR3) AND FUNKEY MVI A,SYSUSR+1 ; Sysops maxuser STA MAXUSER ; To low memory MVI A,(SYSDRV-'@')-1 ; Sysop's maxdriv STA MAXDRIV ; To low memory ENDIF ; IF SETFLG AND FUNKEY LDA LCPTR ; Get current user access flags STA CDOFF ; Temporary storage ENDIF ; IF CHGPATH AND FUNKEY CALL SYSPAT ; Setup sysops path ENDIF ; IF SETFLG AND FUNKEY MVI A,USRFLG ; Get predefined access flag bits STA LCPTR ; Set them ENDIF ; IF (ZCPR2 OR ZCPR3) AND FUNKEY MVI A,0FFH ; Wheel on, modem off LXI H,SCRMOFF ; Correct message JMP BLNKT1 ; RETDU: LDA MXUSR ; Callers allowed maxuser INR A STA MAXUSER ; To low memory LDA MXDRV ; Callers allowed maxdriv DCR A STA MAXDRIV ; To low memory ENDIF ; IF SETFLG AND FUNKEY LDA CDOFF ; Get original access bits back STA LCPTR ; set them ENDIF ; IF CHGPATH AND FUNKEY CALL REMPAT ; Setup remote users path ENDIF ; IF (ZCPR2 OR ZCPR3) AND FUNKEY XRA A ; Wheel off, modem on LXI H,SCRMON ; Correct message RET ENDIF ; WHLTOG: IF (ZCPR2 OR ZCPR3) AND FUNKEY LDA WHEEL ; Get WHEEL status ORA A ; Off? JNZ WHLTG1 ; No MVI A,0FFH ; Set it up STA WHEEL ; And turn it off LXI H,WHLON ; Show it's ON JMP WHLTG2 ; WHLTG1: XRA A ; Set it up STA WHEEL ; And turn it off LXI H,WHLOFF ; Show it's off WHLTG2: CALL PRINTL ; Display status MVI A,7FH ; DEL for buffer ENDIF ; RET ; IF (DSPLC OR MBBS) AND FUNKEY WHOSIT: LXI H,CRMSG ; Turn up a fresh line CALL PRINTL ENDIF ; IF (DSPLC OR MBBS) AND DSPHED AND FUNKEY LXI H,LCHEAD ; Print customized header (if any) CALL PRINTL ENDIF ; IF (DSPLC OR MBBS) AND FUNKEY LXI H,LCDATA CALL PRINTL ; Show sysop lastcaller data LXI H,CRMSG CALL PRINTL ; New line for neatness MVI A,7FH ; DEL for buffer RET ENDIF ; DTIME: IF CLOCK AND AUTOSYS AND FUNKEY LDA SYSON ORA A ; Already up? JNZ DTIMA ; Yes MVI A,1 STA SYSON ; No, so start up now ENDIF ; DTIMA: IF TIMEON AND FUNKEY CALL TCHECK ; Read time LXI H,LFMSG CALL PRINTL ENDIF ; IF TIMEON AND SHOWTLN AND FUNKEY LDA MXTIME ; Get user's max time allowed ORA A ; Unlimited? LXI H,TLNMSG ; Time-left-on system JZ DTIME1 ; Yes ENDIF ; IF TIMEON AND (ZCPR2 OR ZCPR3) AND SHOWTLN AND FUNKEY LDA WHEEL ORA A ; Sysop? JNZ DTIME1 ; Yes ENDIF ; IF TIMEON AND SHOWTLN AND FUNKEY JMP DTIME2 ; No, so show time-left-on ENDIF ; IF TIMEON AND FUNKEY DTIME1: LXI H,TONMSG ; Time-on-system msg DTIME2: CALL PRINTL ; Print time-on LXI H,LFMSG CALL PRINTL MVI A,7FH ; DEL for buffer RET ENDIF ; IF (NOT CLRSCR) AND FUNKEY CLEARIT:LXI H,CLRSEQ CALL PRINTL ; Send clear screen sequence MVI A,LF ; Return with a LF RET ENDIF ; IF LEADIN FKEXIT: POP PSW RET ENDIF ; IF ZCPR3 ZERO128:MVI B,128 ENDIF ; ZEROM: MVI M,0 INX H DCR B JNZ ZEROM RET ; REALBD: JMP 0 ; Will be filled in to point to REAL bdos ; BYERSX: MOV A,C ; BDOS doesn't care if we use A CPI 32 ; Is it USER command? JZ TSTUSR CPI LOCMD ; Is it less than lowest NUBYE command? JC REALBD CPI HICMD+1 ; Is it higher than the highest NUBYE command? JNC REALBD SUI LOCMD ; Commands now range from 0 to HICMD PUSH D MOV E,A ; Save copy of command in A ADD A ; A=2*A ADD E ; A=3*A 3x offset for each vector MOV E,A ; Make command offset 16-bits MVI D,0 ; DE = offset into table LXI H,RSXTBL DAD D ; HL points to entry in RSXTBL now POP D MOV A,E ; Generalized movement of input data PCHL ; Jump to entry in rsx table ; RSXTBL: JMP MDINST ; Get modem input status 61 JMP MDOUTST ; Get modem output status 62 JMP MDOUTP ; Output character to modem 63 JMP MDINP ; Input character from modem 64 JMP MDCARCK ; Get modem carrier status 65 JMP CONSTAT ; Get console input status 66 JMP CONIN ; Get console input character 67 JMP RCONOT ; Send character to console 68 JMP RMXDRV ; Set/get maximum drive 69 JMP RMXUSR ; Set/get maximum user area 70 JMP RMTOUT ; Set/get timeout value 71 JMP RMNULL ; Set/get nulls 72 JMP RMULC ; Set upper/lower case flag 73 JMP RMLFM ; Set line feed mask 74 JMP RMWRT ; Set/get wrtloc flag 75 JMP RMHDR ; Set/get hardon flag 76 JMP RMOFF ; Set/get mdmoff flag 77 JMP RMBELL ; Set/get console bell flag 78 JMP RMRTC ; Call TCHECK & return TON & RTC address 79 JMP RMLCBF ; Return LC buffer address 80 JMP RMMXT ; Set/get maximum time on system 81 JMP RMLTIM ; Set login time 82 JMP RMTOS ; Print TOS message to caller/sysop 83 JMP SRUDEF ; SubRoutine U, the user DEFines 84 ; (used by METAL systems) JMP RMXLCP ; Set/get LCPTR. When a user is logged 85 ; In, LCPTR is a bit mapped status ; Register. If no user is logged in, ; LCPTR contains previous callers Timeon ; TSTUSR: MOV A,E ; Get E register value CPI 241 ; Special call for extended BDOS? JNZ REALBD ; No, was for normal BDOS MVI A,77 ; Was for us, say we're here RET ; RCONOT: MOV C,E ; Get byte to send JMP VCONOUT ; RMXDRV: LXI H,MXDRV ; Set/get maximum drive JMP SETGET1 ; RMXUSR: LXI H,MXUSR ; Set/get maximum user area JMP SETGET1 ; RMNULL: LXI H,NULLS ; Set/get nulls JMP SETGET1 ; RMTOUT: LXI H,TOVAL ; Set/get timeout value JMP SETGET1 ; RMULC: LXI H,ULCSW ; Set/get upper-lowercase flag JMP SETGET1 ; RMLFM: LXI H,LFEEDS ; Set/get line-feed mask JMP SETGET2 ; RMWRT: LXI H,WRTLOC ; Set/get RBBS WRTLOC flag JMP SETGET2 ; RMHDR: LXI H,HARDON ; Set/get hard-log JMP SETGET2 ; RMOFF: LXI H,MDMOFF ; Set/get modem-off flag JMP SETGET2 ; RMBELL: LXI H,BELLON ; Set/get console bell flag JMP SETGET2 ; RMRTC: IF TIMEON CALL TCHECK ; TCHECK returns TON with/without clock ENDIF ; IF CLOCK AND (NOT TIMEON) CALL TIME ; In case caller just wants time ENDIF ; LDA TON LXI H,RTCBUF ; Return address of RTC buffer RET ; RMLCBF: LXI H,LCDATA ; Return address of LC data buffer RET ; RMMXT: LXI H,MXTIME ; Set/get maximum time allowed on system JMP SETGET1 ; RMLTIM: STA LMIN ; Set login time MOV A,D STA LHOUR RET ; RMTOS: IF TIMEON CALL TCHECK ; Get time-on LDA TWIT ; Get twit status ORA A ; Well? RNZ ; Yes, so don't report anything LXI H,TONMSG CALL PRINTB ENDIF ; RET ; RMXLCP: LXI H,LCPTR ; Address of LCPTR INR A ; If (255), return current value JZ SGET1 ; So do it MOV M,D ; If (A)<>255, store (D) in LCPTR (0-255) RET ; SETGET1:INR A ; If A was 255, Z flag will now be set JZ SGET1 ; We want to get current value DCR A MOV M,A ; No, set current value RET ; SGET1: MOV A,M ; Return with current value in A RET ; SETGET2:INR A ; If A was 255, Z flag will now be set JZ SGET1 ; We want to get current value DCR A ; If it's zero, then poke a zero JZ SGET2W MVI A,255 ; Else poke a 255 SGET2W: MOV M,A RET ; CONIN: PUSH B PUSH D PUSH H CALL VCONIN ; IF COMFILE MOV B,A ; Set aside for now LDA OPTION CPI 'E' ; Local? MOV A,B ; Return to A JZ $+6 ; Skip function keys ENDIF ; CALL CKFUNC POP H POP D POP B RET ; CONOUT: PUSH B PUSH D PUSH H MOV A,C ANI 7FH CNZ VCONOUT ; Don't send nulls to local console POP H POP D POP B RET ; CONSTAT:PUSH B PUSH D PUSH H CALL VCONSTAT POP H POP D POP B RET ; DELAY: PUSH B LXI B,4167*MHZ ; Timing constant * clock MHz ; DELAY1: DCX B MOV A,B ORA C JNZ DELAY1 POP B RET ; KDELAY: PUSH B LXI B,42*MHZ ; Timing constant * clock MHz JMP DELAY1 ; BEXCPM: LXI H,BELMSG ; Bells CALL PRINTL ; Tell sysop ; EXCPM: IF IMAT CALL IMQUIT ; Send ATH1 or ATS0=0 depending on OFFHK ENDIF ; IF NOT IMAT CALL MDQUIT ; Drop DTR and send ATH1 or ATS0=0 to modem ENDIF ; IF MOTOR CALL DSKON ; Turn on drives ENDIF ; LHLD REALBD+1 ; Get real bdos vector ; IF CPM3 SHLD STARTX+1 ; Restore for cpm3 exit ENDIF ; IF NOT CPM3 SHLD BDOS+1 ; Save it so CP/M doesn't crash ENDIF ; IF ZCPR2 OR ZCPR3 MVI A,0FFH STA WHEEL ; Restore wheel byte for SYSOP MVI A,SYSUSR+1 STA MAXUSER ; And MAXUSR MVI A,(SYSDRV-'@')-1 STA MAXDRIV ; And MAXDRIV ENDIF ; IF CHGPATH ; If external zcpr path CALL SYSPAT ; Setup sysop's path ENDIF ; IF POSPRO AND CPM3 CALL MDPOSP ; Execute post-processing routine ENDIF ; IF CPM3 CALL RSXCLR ; Unload this RSX ENDIF ; MVI A,0C3H STA 0 ; Reset in case running local CALL UNPATCH ; And then... JMP 0 ; Warm boot CP/M ; CARCK: LDA MDMOFF ORA A ; Known loss? JNZ CARCK2 ; Yes, allow d/u check locally CALL MDCARCK ; Carrier there? JNZ CARCK2 ; Yep, go onto other checks... PUSH B ; Preserve so we can use it MVI B,CLOSS*10 ; Set for 'CLOSS' seconds ; CARLP: CALL DELAY ; Wait .1 seconds CALL MDCARCK ; Check for carrier MOV A,B ; Get count back in a POP B ; Fix stack in case all is ok JNZ CARCK2 ; Got carrier, continue on DCR A ; Count time down STC ; In case this is the end of 'time' RZ ; Return if timed out PUSH B ; Preserve 'BC' MOV B,A ; Get counter value in 'B' JMP CARLP ; Keep checking ; CARCK2: IF USEZCPR AND (ZCPR2 OR ZCPR3) LDA MAXDRIV INR A STA MXDRV LDA MAXUSER ; Get it from ZCPR/ZCMD DCR A ; Drop it one STA MXUSR ; Save it in bye ENDIF ; IF (NOT USEZCPR) AND (ZCPR2 OR ZCPR3) LDA MXDRV ; Older versions did not do this if DCR A ; Wheel was set -- BAD KARMA STA MAXDRIV LDA MXUSR ; Get it from NUBYE INR A ; Bump it STA MAXUSER ; Save it in ZCPR ENDIF ; IF CHEKDU LDA 4 ; Check disk/user # ANI 0FH ; Isolate drive PUSH H ; Save 'HL' LXI H,MXDRV ; Point to allowed # of drives CMP M ; Valid drive? JC CARCK3 ; Yes, skip this junk LDA 4 ; Get whole login byte ANI 0F0H ; Retain user # ORI WBDRIV-'A' ; And force drive to wbdriv STA 4 ; Update login byte ENDIF ; IF CPM3 AND CHEKDU MVI A,WBDRIV-'A' ; Zero drive message CALL SETDRIVE ENDIF ; IF CHEKDU LXI H,IDMSG ; Incorrect Drive Message CALL PRINTB ; Tell user what he did JMP 0 ; Warm boot ; CARCK3: LDA 4 ; Get login byte ANI 0F0H ; Isolate user # RRC ; Move to low bits RRC RRC RRC LXI H,MXUSR ; Point to maximum user number CMP M ; Valid user #? JC CARCK4 ; Yes, don't change JZ CARCK4 LDA 4 ; Get login byte again ANI 0FH ; Keep drive, zero user area STA 4 ; Update login byte ENDIF ; IF CPM3 AND CHEKDU XRA A ; Zero user request CALL SETUSER ENDIF ; IF CHEKDU LXI H,IUMSG ; Invalid User message CALL PRINTB ; Tell him what happened JMP 0 ; Warm boot ; CARCK4: POP H ; Restore 'HL' ENDIF ; ORA A RET ; PRINTB: PUSH B ; Save BC PUSH PSW ; And status regs ; PRBL: MOV C,M ; Get character CALL MOUTPUT ; Output it INX H ; Point to next character MOV A,M ; Test for end of message ORA A JNZ PRBL POP PSW ; Restore status regs POP B ; Restore BC RET ; PRINTL: PUSH B ; Save BC PUSH PSW ; And status regs ; PRLL: MOV C,M ; Get character CALL CONOUT ; Output it INX H ; Point to next character MOV A,M ; Test for end of message ORA A JNZ PRLL POP PSW ; Restore status regs POP B ; Restore BC RET ; LISTOUT:PUSH B PUSH D PUSH H PUSH PSW ; IF NOT CPM3 CALL BDCHEK ENDIF ; MOV C,A CALL VLISTOUT POP PSW POP H POP D POP B RET ; LODCOM: IF COMFILE AND REENTER LDA COMFLG ; Get status of .COM load ORA A ; Been this way yet? RNZ ; Yes MVI A,0FFH STA COMFLG ; Show it's loaded, now ENDIF ; IF COMFILE MVI C,SELDSK MVI E,COMDRV-'A' ; Select drive with .COM file on CALL BDOS MVI C,SETUSR ; Set CP/M user area function MVI E,COMUSR ; Location of our COMFILE CALL BDOS LDA 0 ENDIF ; IF MBBS CPI 0CAH ; Utility requesting COMMENT (JZ)? JZ COMENT ; Yes ENDIF ; IF MSGDSC ; Routine to load message/desc file handler CPI 0C2H ; Is NUKMD requesting RM or description (JNZ)? JNZ NOMSGF ; No XRA A STA MSGFCB ; Prep the fcb LXI H,MSGFCB SHLD CURRFCB LXI H,MSGFCB+12 MVI B,21 CALL ZEROM LXI D,MSGFCB ; Ready to load it now JMP LODCM2 ; So do it ENDIF ; NOMSGF: IF MBBS LDA OPTION CPI 'C' ; Comments requested (BYE C)? JNZ LODCM1 ; No ; COMENT: MVI C,SELDSK MVI E,MBBSDRV-'A' ; Select drive with MBBS.COM CALL BDOS MVI C,SETUSR MVI E,MBBSUSR ; Select user # of MBBS.COM CALL BDOS XRA A STA OPTION ; Clear option flag STA FCB+1 ; And FCB to avoid loop STA CM2FCB LXI H,CM2FCB ; Login fcb SHLD CURRFCB LXI H,CM2FCB+12 MVI B,21 CALL ZEROM ; Clear rest of fcb LXI D,CM2FCB JMP LODCM2 ; Load MBBS.COM ENDIF ; IF COMFILE LODCM1: LXI H,COMFCB SHLD CURRFCB XRA A ; Initialize FCB STA COMFCB LXI H,COMFCB+12 MVI B,21 CALL ZEROM LXI D,COMFCB ; LODCM2: CALL OPENFIL JZ ABORT JMP LOADFIL ENDIF ; LODEX: IF EXFILE LXI H,EXITFCB SHLD CURRFCB LXI H,EXITFCB+12 MVI B,21 CALL ZEROM LXI D,EXITFCB CALL OPENFIL JZ ABORT ; Cannot open file, finish NUBYE hangup ENDIF ; IF COMFILE OR EXFILE LOADFIL:LHLD 6 ; Get top of memory LXI D,-80H DAD D PUSH H ; Save on stack LXI D,80H ; TPA-80H LXI B,0 ; Keep a record counter PUSH B ; Save counter PUSH D ; And load address ; GLOOP: POP D ; Get TPA address LXI H,80H ; Point to next address to read to DAD D ; HL has the address POP B ; Increment the counter POP D ; Get (top-of-memory) PUSH D ; Resave for next time MOV A,E ; Subtract: (top) - (address) SUB L MOV A,D ; Only the carry needed SBB H JNC SIZEOK ; CY=better MOVCPM ENDIF ; COMFILE OR EXFILE ; IF PRGRSS AND (COMFILE OR EXFILE) LXI H,PTSMSG JMP ERRXIT ENDIF ; IF (NOT PRGRSS) AND (COMFILE OR EXFILE) JMP EXCPM ENDIF ; IF COMFILE OR EXFILE SIZEOK: INX B PUSH B PUSH H ; Save TPA address XCHG ; Align registers MVI C,STDMA ; Tell BDOS where to put record CALL BDOS LHLD CURRFCB ; Point to aprropriate FCB XCHG MVI C,READ CALL BDOS ORA A JZ GLOOP ; A=0 if more to read POP B ; Unjunk stack POP B ; This is our counter POP H ; More junk on stack MOV A,B ; Check for zero ORA C JZ ABORT ; We should have read something LXI D,80H ; We did, reset DMA to 80H MVI C,STDMA CALL BDOS LXI H,CFLMSG JMP PRINTL ENDIF ; COMFILE OR EXFILE ; ABORT: IF PRGRSS AND (COMFILE OR EXFILE) LXI H,CNFMSG ; ERRXIT: CALL PRINTL ENDIF ; IF COMFILE OR EXFILE JMP EXCPM ; Warm boot ENDIF ; OPENFIL: IF CPM3 PUSH D ; Save FCB address LXI D,80H MVI C,STDMA CALL BDOS ; Set DMA to 80H POP D ; Get back pointer to FCB PUSH D ; Save FCB pointer again MVI C,SEARCH ; Search for first match CALL BDOS INR A ; Did a file match? POP D RZ ; No, return PUSH D DCR A ; A=directory code (0-3) ADD A ; *2 ADD A ; *4 ADD A ; *8 ADD A ; *16 ADD A ; *32 MOV E,A MVI D,0 LXI H,80H ; Add (32*dir code) to default DMA DAD D ; to find first match filename POP D ; DE=FCB PUSH D ; Save DE again INX H ; Move HL past user # byte in buffer INX D ; Move DE past drive # in FCB MVI B,11 CALL MOVE ; Move name found to FCB POP D ; And continue with the open ENDIF ; CPM3 ; MVI C,OPEN ; Open file pointed to by 'DE' CALL BDOS INR A RET ; MBOOT: LXI SP,STACK ; Ensure good stack ; IF EXRET LDA FCB+1 ; Check if the exit file is doing it right CPI 'r' ; ok? JZ PREOFF ; Yes ENDIF ;1 ; IF MSGDSC ; See if message/description file uploaded LDA 0 CPI 0C2H ; Is NUKMD requesting RM or description (JNZ)? JNZ MBOOT1 ; No LXI H,MSGMSG ; Let sysop know what file is sought CALL PRINTL CALL LODCOM ; Try to load it MVI A,0C3H ; Reset the flag STA 0 CALL 100H ; Now run the message file handler LXI SP,STACK ; Restore the stack ENDIF ; MBOOT1: IF CPM3 MVI B,0B3H ; Offset to chain program flag in SCB CALL GETSCB ; Get byte from SCB ANI 80H ; In the middle of a chain program? JNZ VWARMBT ; Yes, skip checks ENDIF ; IF (COMFILE OR EXFILE) AND (NOT PBBS) LDA OPTION CPI 'E' ; Return from local test? JNZ MBOOT2 ; No, continue MVI A,' ' STA FCB+1 ; Fool system STA OPTION CALL UNPATCH ; Yes, restore ENDIF ; IF EXFILE AND (NOT MBBS) AND (NOT PBBS) JMP LOGOFF ; Run exit file locally ENDIF ; IF COMFILE AND (NOT PBBS) JMP START0 ; And start fresh ENDIF ; MBOOT2: LDA 0 ; Look at opcode ; IF MBBS CPI 0CAH ; Utility requesting COMMENT (JZ)? JNZ MBOOT3 ; No CALL LODCOM CALL 100H ENDIF ; MBOOT3: CPI 0C3H ; Is it still a JMP? ; IF MBBS JNZ NORPTC ; Let login take over ENDIF ; IF NOT MBBS JNZ LOGOFF ; Check for exit file ENDIF ; IF NOT CPM3 CALL BDCHEK ENDIF ; IF (DSPLC OR MBBS) AND READLC LDA LCDATA ; See if lastcalr has been read CPI ' ' ; Or data poked by a BBS, like MBBS JNZ NO25EX ; Yes, skip this XRA A ; Else, let's pick up the lastcalr file STA FCBRNO ; Prepare FCB for lastcalr LXI H,LCNAME LXI D,FCB MVI B,13 CALL MOVE ; Move rest of FCB MVI C,0DH ; Reset disk and CALL BDOS ; Set for default buffer MVI C,SELDSK MVI E,LCDRV-'A' CALL BDOS ; Set drive MVI C,SETUSR MVI E,LCUSR CALL BDOS ; And user LXI D,FCB CALL OPENFIL ; Open it ENDIF ; IF PRGRSS AND (DSPLC OR MBBS) AND READLC LXI H,LCMSG1 CZ PRINTL ; Error msg ENDIF ; IF (DSPLC OR MBBS) AND READLC JZ NO25EX ; No file available, exit LXI D,FCB MVI C,READ CALL BDOS ; Read 1 record max ENDIF ; IF PRGRSS AND (DSPLC OR MBBS) AND READLC LXI H,LCMSG1 ORA A CNZ PRINTL ; Say error ENDIF ; IF (DSPLC OR MBBS) AND READLC LXI H,80H LXI D,LCDATA MVI B,LCBUF ; Size of buffer CALL MOVE ; Move data into bye's internal buffer MVI B,LCBUF ; Max we will display LXI H,LCDATA ; NO25RD: MOV A,M CPI 'Z'-'@' ; EOF? JZ NO25ZD CPI ',' CZ NO25SP ; Convert commas and semicolons to spaces CPI ';' CZ NO25SP CPI CR CZ NO25SP ; Convert cr's and lf's to spaces CPI LF CZ NO25SP DCR B JZ NO25ZD INX H ; Get next byte JMP NO25RD ; NO25ZD: XRA A MOV M,A ; Set terminator for print routine STA 4 ENDIF ; READLC ; NO25EX: IF COMFILE AND REENTER XRA A STA COMFLG ; Clear .COM load status ENDIF ; IF PRNTWB LXI H,WBMSG CALL PRINTB ; Print the following message: ENDIF ; IF TIMEON AND CLOCK AND PRNTOS CALL RMTOS ; Display timeon ENDIF ; JMP VWARMBT ; Go do a warm boot ; IF (DSPLC OR MBBS) AND READLC NO25SP: MVI A,' ' ; Space MOV M,A ; To lcdata RET ENDIF ; MINPUT: IF NOT CPM3 CALL BDCHEK ENDIF ; LDA TOVAL ; Get # of minutes before timeout ; MINP1: STA TOCNTM ; Set minutes counter PUSH H LXI H,42000 ; Initialize one minute timeout counter SHLD TOCNT POP H ; MINP2: CALL MSTAT ; Check for input and timeout status ORA A ; Anything? JNZ MINP3 JMP MINP2 ; Loop until character or timeout ; MINP3: CALL CONSTAT ; Check local console ORA A ; Character? JNZ CONIN ; Yes, read it & return CALL MDINP ANI 7FH ; Strip off any high bits ORA A ; Ignore nulls JZ MINP2 CPI 7BH ; Ignore left braces JZ MINP2 CPI 7FH ; Was it a delete? JZ MINP2 ; Filter this too CPI 60H ; "`" causes problems for some JZ MINP2 ; So filter it too ; IF ZCPR2 OR ZCPR3 MOV B,A ; Save byte in B LDA WHEEL ORA A MOV A,B ; Get character back JNZ MINP4 ; It's a wheel, don't trap ^P ENDIF ; CPI 'P'-'@' ; Is it a ^P? JZ MINP2 ; Ignore CTL-P unless wheel is set ; MINP4: IF HARDLOG PUSH B MOV B,A ; Put a copy of the character in B LDA HARDON ; If HARDON=0 then turn HARDLOG off (so ORA A ; sysop does not waste paper while MOV A,B ; playing ZORK from work.) POP B JZ NOLOG CPI 20H JNC MINP5 ENDIF ; CPI CR JNZ NOLOG ; IF (CLOCK OR TIMEON) AND NOT UPDOUT CALL TCHECK ; Update/check clock and timeon MVI A,CR ; Return CR to A ENDIF ; IF HARDLOG MINP5: CALL LISTOUT ; Echo on printer CPI CR JNZ NOLOG ; Return needs linefeed MVI A,LF CALL LISTOUT ; So send it MVI A,CR ; Get back CR ENDIF ; NOLOG: CPI 'C'-'@' ; Is it ^C? RNZ ; No, pass it through LDA 0 ; See if warm boot disabled CPI 0C3H ; Jump means warm boot ok MVI A,3 ; So return with a ^C RZ MVI A,CTRLC-'@' ; Else convert to different character RET ; MOUTPUT: IF NOT CPM3 CALL BDCHEK ENDIF ; IF COMFILE LDA OPTION CPI 'E' ; Running locally? JNZ MONOTE ; No, continue ENDIF ; IF COMFILE AND CLOCK MOV A,C ; Get character CPI LF ; Update clock local? CZ TCHECK ; Yes ENDIF ; MONOTE: LDA MDMOFF ; Known loss of carrier? ORA A PUSH PSW CNZ CONOUT ; Output to local only POP PSW RNZ ; Then exit ; MOUTA: CALL CHECK ; Carrier still on? CALL MDOUTST ; Check modem output status JZ MOUTA MOV A,C ; Get character ANI 7FH ; Strip parity bit RZ ; Don't output DEL to remote ; CPI 60H ; Check for lower case JC MOUTP2 ; Skip if not lower case CPI 7FH ; Check for rubout JZ MOUTP2 PUSH H LXI H,ULCSW ; Subtract either 20H or nothing SUB M POP H MOV C,A ; Force on local as well as remote ; MOUTP2: CPI LF ; Toggle for line feeds? JNZ MOUTP3 ; No ; IF (CLOCK OR TIMEON) AND UPDOUT CALL TCHECK ; Update/check clock and timeon ENDIF ; IF CLOCK AND AUTOSYS PUSH H CALL CHKDWN ; Shutdown check and warning POP H ENDIF ; LDA LFEEDS ; Yes, see if we can send it... ORA A ; Set flags MVI A,0 ; Prepare with a null JNZ MOUTP3 ; Nope, don't (instead, send a null) MVI A,LF ; Yes, it's ok to send the line feed ; MOUTP3: CALL MDOUTP ; Output character to modem ; PUSH PSW ; Save character CPI 'G'-'@' ; Is it a bell? JNZ NOTBEL ; IF NOT LMBELL LDA BELLON ; Get flag ORA A JZ ISBELL ENDIF ; IF LMBELL LDA KILBEL ORA A JNZ ISBELL ENDIF ; NOTBEL: CALL CONOUT ; Send to regular BIOS ; ISBELL: POP PSW ; Get character again CPI LF ; Time for nulls? RNZ ; No LDA NULLS ; Get count ORA A ; Any? RZ ; No PUSH B MOV B,A ; Save count ; NULLP: CALL MDOUTST ; Modem ready? JZ NULLP XRA A ; 0 is a null CALL MDOUTP ; Type a null DCR B ; Another? JNZ NULLP ; Yes, loop POP B RET ; MOVE: MOV A,M ; Get a byte STAX D ; Put at new home INX D ; Bump pointers INX H DCR B ; Decrement byte count JNZ MOVE ; If more, do it RET ; If not, return ; MSTAT: IF NOT CPM3 CALL BDCHEK ; Set 6 to safety ENDIF ; CALL CHECK ; Check for carrier lost CALL CONSTAT ; Get local status ORA A RNZ ; Return if local character LDA MDMOFF ORA A MVI A,0 RNZ ; Don't let remote input while modem is muted ; IF ZCPR2 OR ZCPR3 LDA WHEEL ; Get WHEEL status ORA A ; Set? JNZ MDINST ; Yes, so don't bother with time-out ENDIF ; CALL KDELAY PUSH H LHLD TOCNT ; Knock down timeout counter DCX H SHLD TOCNT MOV A,H ORA L POP H JNZ MDINST ; Still time left, keep trying LDA TOCNTM ; Count off last minute DCR A STA TOCNTM ; Save new PUSH H LXI H,42000 SHLD TOCNT ; Reset one minute counter POP H JNZ MDINST ; Get modem input status LXI H,ITOMSG CALL PRINTB CALL PATCH ; In case LUX was running ; IF MBBS JMP NORPTC ; Let mbbs/login log him off ENDIF ; IF NOT MBBS JMP LOGOFF ; Check for exit file ENDIF ; NEWJTBL:JMP MCBOOT ; Cold boot JMP MBOOT ; Warm boot JMP MSTAT ; Modem status test JMP MINPUT ; Modem input routine JMP MOUTPUT ; Modem output routine ; IF (NOT HARDLOG) AND (NOT PRINTER) RET ; Modem list device NOP NOP RET ; Modem punch device NOP NOP RET ; Modem reader device NOP NOP ENDIF ; IF MOTOR DSKON: PUSH PSW ; Save 'A' and flags MVI A,DISKON ; Turn on motors OUT DISK PUSH H ; Now, wait a long time LXI H,0 ; DSKLP: XTHL XTHL DCX H ; Count loop MOV A,H ; Check if done? ORA L JNZ DSKLP POP H ; Restore HL POP PSW ; And 'A' and flags RET ; DSKOFF: PUSH PSW ; Save 'A' and flags MVI A,DISKOFF ; Turn motors off OUT DISK POP PSW RET ENDIF ; MOTOR ; NWBCALL: IF LOSER CALL WMSTRT ; Warm boot disk read CALL PATCH ; Fix BIOS again after WMSTRT ENDIF ; IF NOT CPM3 CALL BDCHEK ENDIF ; RET ; PATCH: LDA PTFLAG ORA A JNZ SKPTH ; Save the original jump table only once ; IF CPM3 AND PREPRO CALL MDPREP ; Do system dependant initialization ENDIF ; CALL TBLADDR ; HL= CP/M BIOS jump table LXI D,VCOLDBT ; Point to save location MVI B,24 ; Save all vectors CALL MOVE ; Move it LHLD VCONOUT+1 ; Get the original CONOUT address SHLD COVECT ; Store for use with XMODEM programs ; SKPTH: CALL TBLADDR ; HL= CP/M BIOS jump table XCHG ; Move it to 'DE' LXI H,NEWJTBL ; Point to new jump table CALL MOVE ; Move it MVI A,0FFH STA PTFLAG ; IF LOSER LXI H,NWBCALL ; Set new warm boot call SHLD WBCALL+1 ; Store the new address ENDIF ; RET ; PRNLOG: LXI H,VMSG ; Load program info CALL PRINTL ; Display it ; CALSUM: LDA CWCAR ; Get 8 bit number LXI H,ATMSN ; Address to store ascii CALL DEC8 ; And convert to ascii LXI H,ATMSG ; Print Callers with carrier detected CALL PRINTL ; To local crt ; IF IMAT ; Print # of voice calls LDA VCNUM LXI H,VCMSN ; Put ascii here CALL DEC8 LXI H,VCMSG CALL PRINTL ENDIF ; IF PWRQD LDA NWPWD ; 8 bit counter LXI H,NWMSD ; Put ascii here CALL DEC8 LXI H,NWMSG ; Print callers who knew password CALL PRINTL ENDIF ; LXI H,LFMSG JMP PRINTL ; Turn up a line (end of call summary) ; IF WELFILE RDBYTE: MOV A,H ; Time to read? ORA A ; If at 100H, no read required JZ NORD LXI D,FCB MVI C,READ CALL BDOS ORA A ; Ok? MVI A,1AH ; Fake up EOF RNZ ; Return EOF if bad LXI H,80H ; NORD: MOV A,M ; Get character INX H ; Point to next byte RET ENDIF ; WELFILE ; TBLADDR:LHLD 1 ; Get BIOS pointer DCX H ; Skip to cold boot DCX H DCX H ; IF HARDLOG OR PRINTER MVI B,15 ; Don't move lister jump ENDIF ; IF (NOT HARDLOG) AND (NOT PRINTER) MVI B,24 ; Move all jumps ENDIF ; RET ; IF NOT IMAT TSTBAUD:CALL MDCARCK ; Check carrier first JZ PREOFF ; Carrier is gone, start over LDA CDOFF INR A STA CDOFF CPI 60 ; Allow 1 minute for c/r detect JZ PREOFF ; Hangup and start over MVI D,20 ; Check for 2 sec for current baud rate ; TSTB1: MVI B,1 ; At .1 sec intervals PUSH D CALL DELAY CALL MDINST ; See if character available ORA A JZ TSTB2 ; None yet CALL MDINP ; Yes, read the character ANI 7FH POP D ; Restore the stack JMP TSTB3 ; And see if it is right character ; TSTB2: POP D ; Restore DE DCR D ; And decrement the count MOV A,D ORA A ; 2 sec's up?? JNZ TSTB1 ; No, try again MVI A,0FFH ; Dummy character ; TSTB3: CPI CR ; CR? RZ ; Yes CPI LF ; LF? RZ ; Yes CPI 'C'-40H ; CTRL-C? RET ; Return with proper flag set ENDIF ; NOT IMAT ; UNPATCH:CALL TBLADDR ; HL= CP/M BIOS jump table XCHG ; Move to DE LXI H,VCOLDBT ; Get saved table CALL MOVE ; Move original table back ; IF LOSER LXI H,WMSTRT ; Load old call location SHLD WBCALL+1 ; Restore old call ENDIF ; RET ; USRCHK: CALL PRNLOG ; Give info LXI H,RS1MSG CALL PRINTL ; Prompt to resume waiting for ring ; PRNREL: CALL CONSTAT ORA A ; Reply? JZ PRNREL ; No, so loop until answered CALL VCONIN CPI 60H ; Lower case? JC $+5 ; Skip conversion SBI 20H ; Else make upper case CPI 'E' ; Execute .COM file? JZ USRUN ; Yes CPI 'Z' ; Zero counter flags and resume NUBYE? JZ RESFLG ; Yes CPI 'R' ; Resume NUBYE without counter reset? JZ RESBYE ; Yes LXI H,CRMSG ; None of the above, so... CALL PRINTL ; Turn up a fresh line JMP EXCPM ; And warm boot to CP/M ; RESFLG: XRA A STA CWCAR ; Clear carrier calls flag STA VCNUM ; And voice call flag ; RESBYE: IF NOT CLRSCR LXI H,RS2MSG ENDIF ; IF CLRSCR LXI H,CLRSEQ ENDIF ; JMP PRINTL ; USRUN: IF CLRSCR LXI H,CLRSEQ CALL PRINTL ; Clear local crt screen ENDIF ; IF NOT CLRSCR LXI H,CRMSG ; CR/LF for neatness CALL PRINTL ENDIF ; MVI A,'E' STA OPTION ; Show local run JMP RUNE ; Run local for sysop ; WELCOME: IF CLOCK CALL TIME ; Read current time LDA CCHOUR ; And set STA LHOUR ; The users LDA CCMIN ; Login STA LMIN ; Time for later use ENDIF ; IF TIMEON OR CLOCK MVI A,MAXMIN ; Set number of minutes STA MXTIME ; Into mxtime XRA A STA TCHKFG ; Show it's ok to check time ENDIF ; IF NOT PRGRSS LDA OPTION CPI 'E' ; Running local? JZ LOCJMP ; Yes, so skip nulls and other garbage ENDIF ; LDA MSPEED ; Get callers baud rate STA NULLS ; Default nulls = MSPEED LXI H,VMSG ; Load program info CALL PRINTB ; Display it to caller ; IF SHOSYS LXI H,URMSG CALL PRINTB ; Display your intro message ENDIF ; IF IMAT AND HITCR AND (NOT ASKNUL) CRONLY: LXI H,PRSCR CALL PRINTB ; Nulls prompt CALL MINPUT CPI CR ; CR? JNZ CRONLY ; No, so loop until CR ENDIF ; LXI H,LFMSG CALL PRINTB ; CR/LF ; IF ASKNUL LXI H,NULHDR CALL PRINTB ; Give info ; ASKNL0: LDA NULTRY CPI 12 ; Retry limit? (allow for noisy lines) JZ PREOFF ; Yes, else continue INR A STA NULTRY ; Increment counter and save it LXI H,NULMSG ; Nulls message CALL PRINTB ; Send it CALL MINPUT ; Get value MVI C,'0' ; Default 0 nulls CPI CR ; Default ok? JZ $+14 ; Yes, so skip next 5 lines MOV C,A CPI '0' JC ASKNL0 ; Bad, retry CPI '9'+1 JNC ASKNL0 ; Bad, retry CALL MOUTPUT ; Echo character MOV A,C ; Restore value SUI '0' ; Make binary STA NULLS ; Save new nulls LXI H,CRMSG ; CR/LF/LF CALL PRINTB ; Send this message ENDIF ; IF IMAT AND (NOT ASKNUL) AND (NOT PBBS) LDA NULLS ; Get default nulls LXI H,NULMSD CALL DEC8 ; Convert to ASCII LXI H,CRMSG1 ; Show default nulls message CALL PRINTB ; Send this message ENDIF ; IF PRGRSS AND CLOCK AND RSPEED LDA OPTION CPI 'E' ; Running local? JZ SPDOK ; Yes, so skip next ENDIF ; IF CLOCK AND RSPEED XRA A ; Clear status LDA MSPEED ; See what speed the user is at SUI SPEED ; Is it acceptable? JNC SPDOK ; Yes, continue CALL TIME ; Get current time MVI A,BEGHR ; Begin restriction time ; SPD01: LXI H,CCHOUR ; Point at current hour CMP M ; Equal? JZ SPDOFF ; Yes, dump him INR A CPI ENDHR ; End restriction time? JZ SPDOK ; Yes CPI 24 ; Past midnight? JNZ SPD01 ; No, continue XRA A ; Yes set to 00 hour JMP SPD01 ; And continue ; SPDOFF: LXI H,OFFMSG ; Point at logoff message CALL PRINTB ; And tell him why JMP PREOFF ; Then log him off ENDIF ; CLOCK AND RSPEED ; SPDOK: IF WELFILE LXI H,WELFILN ; Source LXI D,FCB ; Destination MVI B,13 ; Length CALL MOVE ; Move the name LXI D,80H ; Set DMA address to 80H MVI C,STDMA CALL BDOS MVI C,SETUSR ; Set user number for WELCOME file MVI E,WELUSR CALL BDOS MVI C,SELDSK ; Select default drive for WELCOME file MVI E,WELDRV-'A' CALL BDOS LXI D,FCB CALL OPENFIL JZ PASSINT ; No WELCOME file XRA A ; A=0 STA FCBRNO ; Zero record number LXI H,100H ; Get initial buffer pointer ; WELTYPE:CALL RDBYTE ; Get a byte CPI 'Z'-'@' ; EOF? JZ PASSINT ; Yes, done MOV C,A ; Setup for type CALL MOUTPUT ; Type the character CALL MSTAT ; Check for character typed ORA A JZ WELTYPE ; No, loop CALL MINPUT CPI 'C'-'@' ; CTRL-C? JZ PASSINT ; Yes CPI 'K'-'@' ; CTRL-K? JZ PASSINT ; Yes CPI 'S'-'@' ; CTRL-S? JZ WAIT ; Yes ANI 5FH ; Convert lower case to upper case CPI 'K' ; K? JNZ WELTYPE ; No, loop until done JMP PASSINT ; Yes ; WAIT: CALL MSTAT ORA A ; Has another char been typed? JZ WAIT ; No, wait CALL MINPUT ; Yes, check character CPI 'C'-'@' ; CTRL-C? JZ PASSINT ; Yes CPI 'K'-'@' ; CTRL-K? JZ PASSINT ; Yes ANI 5FH ; Convert lower case to upper case CPI 'K' ; K? JZ PASSINT ; Yes JMP WELTYPE ; No, loop until done ENDIF ; WELFILE ; PASSINT: IF PWRQD MVI D,3 ; 3 tries at password ; PASSINP:LXI H,PWMSG ; Password message CALL PRINTB ; Send this message LXI H,PASSWD ; Point to password MVI E,0 ; No missed letters ; PWMLP: CALL MINPUT ; Get a character CPI 60H ; Lower case? JC NOTLC ; No, ANI 5FH ; Make upper case alpha ; NOTLC: PUSH PSW ; Save character input CPI 20H ; Is character a control code? JNC PWDIS ; Pass if displayable MVI C,'^' ; If control map to up arrow then display CALL CONOUT POP PSW PUSH PSW ADI 40H ; PWDIS: MOV C,A CALL CONOUT ; Output character locally POP PSW ; Restore 'A' reg. CPI 'U'-40H ; CTL-U? JZ PASSINP ; Yes, abort, and retry CMP M ; Match password? JZ PWMAT MVI E,1 ; No, show miss CPI CR ; CR? JNZ PWMLP ; No, wait for CR ; PWNMAT: LXI H,WRGMSG ; Wrong password message CALL PRINTB ; Send this message DCR D ; More tries? JNZ PASSINP ; Yes JMP PREOFF ; No, go hang up ; PWMAT: INX H ; To next character CPI CR ; End? JNZ PWMLP ; No, loop MOV A,E ; Get flag ORA A JNZ PWNMAT ; Not right LXI H,NWPWD ; Get last value INR M ; And add one ENDIF ; PWRQD ; LOCJMP: IF COMFILE MVI C,SETUSR MVI E,COMUSR ; Switch to .COM file user area CALL BDOS MVI C,SELDSK ; In case WELCOME file MVI E,COMDRV-'A' ; is on a different drive CALL BDOS ; than the .COM file. MVI A,' ' ; Fool the system so that the .COM file STA FCB+1 ; will see a space at FCB+1 for default. ENDIF ; IF COMFILE CALL PATCH ; So we run under bye if running "E" option CALL 100H ; Execute com file ENDIF ; JMP 0 ; Warm boot now for "normal" CP/M use ; PREOFF: CALL IMDONE ; Hangup the phone CALL UNPATCH XRA A STA OPTION ; Reset the local "E" option ; IF CPM3 LHLD BDOS+1 ; Get address of first RSX ; PLOOP: PUSH H ; Save memory address MVI L,10H ; Bump RSX name in header LXI D,RSXNAM ; Get address of string MVI B,8 CALL COMP ; Compare it POP H ; Restore memory address JZ HANGUP ; 0? - all done, else... MVI L,0EH ; Get new RSX header offset MVI A,0FFH ; Get remove flag MOV M,A ; Update header MVI L,0BH ; Point to start page of next RSX MOV H,M ; Update address JMP PLOOP ; Loop until NUBYE RSX match ; COMP: PUSH D ; Save the registers PUSH H INR B ; COMP1: DCR B JZ COMP2 LDAX D ANI 7FH MOV C,A MOV A,M ANI 7FH CMP C INX H INX D JZ COMP1 ; COMP2: POP H ; Restore the registers POP D RET ENDIF ; CPM3 1 ; IF NOT CPM3 JMP HANGUP ; And prep for next call ENDIF ; IF TIMEON OR CLOCK TCHECK: LDA TCHKFG INR A RZ ; If mxtime was 255, user not logged in PUSH B PUSH D PUSH H ENDIF ; IF TIMEON AND (NOT CLOCK) LDA TON JMP TCNCLK ENDIF ; IF CLOCK CALL TIME ; Get current time ENDIF ; IF CLOCK AND TIMEON LDA LHOUR ; (LHOUR)=log-in hour 0-23 CALL TCX60 ; Convert to minutes, results in (HL) LDA LMIN ; (LMIN)=log-in minutes, 0-59 LXI D,0 ; Clear (DE) MOV E,A ; (LMIN) to (DE) DAD D ; (HL)+(DE)=(HL) PUSH H ; Save log-in minutes on stack LDA CCHOUR ; (CCHOUR)=current hour, 0-23 LXI H,LHOUR ; Let's see if we crossed midnight SUB M JNC TCOK ; No, we're ok LDA CCHOUR ; Yes, let's add 24 ADI 24 STA CCHOUR ; Now we're ok TCOK: LDA CCHOUR ; (CCHOUR) is now => than (LHOUR) CALL TCX60 ; Convert it to minutes LDA CCMIN ; Current minutes, 0-59 LXI D,0 ; Clear (DE) MOV E,A ; CCMIN to (DE) DAD D ; (HL) now has current time in minutes POP D ; (DE) now has log-in time CALL TCDIF ; Get the difference, (HL)-(DE)=(HL) MOV A,L ; Only save LSB, 0-255 is plenty STA TON ; And save it ENDIF ; TIMEON AND CLOCK ; TCNCLK: IF TIMEON LXI H,TONMSD ; (A)=TON, (HL)=address to store ascii CALL DEC8 ; Convert timeon to ascii ENDIF ; IF TIMEON AND SHOWTLN LDA TON ; Get time-on MOV B,A ; Store it LDA MXTIME ; Get max time-on SUB B ; Subtract current time-on LXI H,TLNMSD CALL DEC8 ; Convert to ASCII ENDIF ; IF TIMEON POP H POP D POP B ENDIF ; IF TIMEON AND (ZCPR2 OR ZCPR3) LDA WHEEL ORA A ; Maybe he typed his password for user RNZ ENDIF ; IF TIMEON LDA MXTIME ORA A ; Special user? RZ ; Yes, exit PUSH B ; Else, check his timeon MOV B,A ; Move it to 'B' LDA TON SUB B ; Subtract max time allowed POP B RC ; Still time left MVI A,255 STA TCHKFG ; Reset time flag LXI H,TIMEUP ; Time is up, inform user CALL PRINTB CALL PATCH ; In case LUX was running ENDIF ; IF TIMEON AND MBBS XRA A STA MXTIME JMP NORPTC ; Let login do it ENDIF ; IF TIMEON AND (NOT MBBS) JMP LOGOFF ; Check for exit file ENDIF ; IF CLOCK TCX60: LXI H,0 ; (HL)=0 ORA A RZ ; If (A)=0, no x60 needed LXI D,60 ; Multiplier TCX61: DAD D ; X60 DCR A ; Done? JNZ TCX61 ; No RET ; TCDIF: MOV A,L ; LSB of (HL) SUB E ; LSB of (DE) MOV L,A ; Back to L MOV A,H ; MSB of (HL) SBB D ; MSB of (DE) and carry flag MOV H,A ; (HL) now has difference RET ENDIF ; IF BCD2BIN BCDBIN: PUSH D MOV E,A ; Save original byte ANI 15 MOV D,A ; Save low nibble MOV A,E ANI 240 ; Mask LSN RRC ; x2 MOV E,A RRC ; x4 RRC ; x8 ADD E ; x10 ADD D ; low nibble POP D RET ENDIF ; IF BIN2BCD BINBCD: PUSH D MVI E,255 ; -1 BLP: INR E ; Increment tens counter SUI 10 ; Subtract 10 each pass JNC BLP ADI 10 ; Get back number MOV D,A MOV A,E RLC ; Shift over to MSN RLC RLC RLC ADD D ; Add in ones position POP D RET ENDIF ; DEC8: PUSH PSW PUSH H MVI A,' ' MOV M,A ; Clear destination with spaces INX H MOV M,A INX H MOV M,A POP H POP PSW PUSH B PUSH D MVI E,0 ; Leading zero flag MVI D,100 DEC81: MVI C,'0'-1 DEC82: INR C SUB D ; 100 or 10 JNC DEC82 ; Still + ADD D ; Now add it back MOV B,A ; Remainder MOV A,C ; Get 100/10 CPI '1' ; Zero? JNC DEC84 ; Yes MOV A,E ; Check flag ORA A ; Reset? MOV A,C ; Restore byte JZ DEC85 ; Leading zeros are skipped DEC84: MOV M,A ; Store it in buffer pointed at by HL INX H ; Increment storage location MVI E,0FFH ; Set zero flag DEC85: MOV A,D SUI 90 ; 100 to 10 MOV D,A MOV A,B ; Remainder JNC DEC81 ; Do it again ADI '0' ; Make ascii MOV M,A ; And store it POP D POP B RET ; IF CHGPATH ; To alter path REMPAT: LXI H,REMPATH ; Source=remote path LXI D,EXTPATH ; Dest=external path at 40H MVI B,LREMP ; Length of remote path CALL MOVE RET ; SYSPAT: LXI H,SYSPATH ; Source=SYSOP's path LXI D,EXTPATH ; Dest=external path at 40H MVI B,LSYSP ; Length of new path JMP MOVE ENDIF ; IF CPM3 SETSCB: LHLD SCBBASE ; Get SCB pointer MOV L,B ; Set the offset MOV M,A ; Save the byte RET ; GETSCB: LHLD SCBBASE ; Get SCB pointer MOV L,B ; Set the offset MOV A,M ; Get the byte RET ; SETUSER:MVI B,0B0H ; B0 = CCP user number CALL SETSCB ; Set CCP user number MVI B,0E0H ; E0 = BDOS user number JMP SETSCB ; Set BDOS user number ; SETDRIVE: MVI B,0AFH ; AF = CCP drive CALL SETSCB ; Set CCP drive MVI B,0DAH ; DA = BDOS drive JMP SETSCB ; Set BDOS drive ENDIF ; CPM3 ; IF NOT CPM3 ENDOBJ EQU $ ENDIF ; IF IMAT AND DOATZ IMATZ: DB 'ATZ',CR,0 ; Reset ENDIF ; IF IMAT AND NODTR IMESC: DB '+++',0 ; Escape sequence IMATH: DB 'ATH',CR,0 ; Hangup phone ENDIF ; IF IMAT AND OFFHK IMATH1: DB 'ATH1' ; Take phone off-hook ENDIF ; IF IMAT AND OFFHK AND (NOT ANCHOR) DB 'M0' ; Speaker off ENDIF ; IF IMAT AND OFFHK DB CR,0 ; End the command line ENDIF ; IF IMAT IMATA: DB 'ATA',CR,0 ; Answer the phone ADDSTR: DW 0 ; Address of command string VCMSG: DB CR,LF,'Voice calls: ' VCMSN: DB ' ',0 VCNUM: DB 0 ; Counter for voice calls ENDIF ; IF IMAT AND PRGRSS RCSHOW: DB '_',0 ; Will be stored by PRGRSS NOEMSG: DB CR,LF,'Echo error',CR,LF,0 CMDERR: DB 'Modem error',CR,LF,0 ENDIF ; IF IMAT IMINT: DB 'AT' ; Attention ENDIF ; IF IMAT AND OFFHK DB 'H0' ; Hangup phone ENDIF ; IF IMAT AND ECHO DB 'E1' ; Echo back characters sent to modem ENDIF ; IF IMAT AND (NOT ECHO) DB 'E0' ; No echo ENDIF ; IF IMAT DB 'Q0' ; Send result codes DB 'V0' ; Numeric mode ENDIF ; IF IMAT AND (NOT NOATA) DB 'S0=0' ; No auto-answer (we will use 'ATA') ENDIF ; IF IMAT AND NOATA DB 'S0=1' ; Will answer on first ring ENDIF ; IF IMAT AND SHORTB DB CR,0 IMINT1: DB 'AT' ENDIF ; IF IMAT AND (NOT ANCHOR) DB 'M0' ; Speaker off DB 'S10=25' ; 2 1/2 sec wait after carrier loss to hang up ENDIF ; IF IMAT AND (NOT HS300) AND (NOT ANCHOR) DB 'X1' ; Extended response codes beyond '4' ENDIF ; IF IMAT DB CR,0 ; CR finishes command string ENDIF ; IF IMAT AND (DOATZ OR NOATA) IMUSR: DB 'ATS0=0',CR,0 ; This helps the Password and S-100 ENDIF ; IF PRGRSS AND (NOT IMAT) MSG30: DB '300 baud test',CR,LF,0 MSG12: DB '1200 baud test',CR,LF,0 MSG24: DB '2400 baud test',CR,LF,0 ENDIF ; VMSG: DB CR,LF,'NUBYE v',MAIN+'0','.' DB VERS/10+'0',VERS MOD 10+'0',' - ' DB MONTH/10+'0',MONTH MOD 10+'0','/' DB DAY/10+'0',DAY MOD 10+'0','/' DB YEAR/10+'0',YEAR MOD 10+'0',CR,LF,0 ; CLRSEQ: DB CLRCH1,CLRCH2,CLRCH3,CLRCH4,CLRCH5,CLRCH6,0 ; IF FUNKEY BELMSG: DB 7,7,0 ENDIF ; CDOFF: DB 0 OPTION: DB 0 ; IF CLOCK AND RSPEED OFFMSG: DB CR,LF,'Sorry! 1200 or 2400 baud only allowed 7PM-11PM' DB CR,LF,LF,' Call back before/after 7-11PM EST' DB CR,LF,0 ENDIF ; IF TIMEON TIMEUP: DB 7,CR,LF,LF DB ' Your time is up - wait 24 hours to call back',CR,LF,0 TONMSG: DB CR,LF,'Minutes on : ' TONMSD: DB '0 ',CR,LF,0 ENDIF ; IF TIMEON AND SHOWTLN TLNMSG: DB CR,LF,'Minutes left: ' TLNMSD: DB '0 ',CR,LF,0 ENDIF ; RTCBUF: DB 99H,99H,99H ; HH:MM:SS (BCD 24hr time) 00:00:00-23:59:59 DB 19H,85H,02H,18H ; YYYY/MM/DD (BCD ISO date) ; TON: DB 0,0 ; (Total time on system-binary integer) CCHOUR: DB 0 ; Current clock hour in binary CCMIN: DB 0 ; Current clock minute in binary LHOUR: DB 0 ; Login hour in binary LMIN: DB 0 ; Login minutes in binary SYSDWN: DB 0 ; System going down flag (0 = off, 1 = on) SYSON: DB 0 ; System on flag (0 = off, 1 = on) TCHKFG: DB 0 ; Tcheck flag (0 = OK, 255 = not OK to check) CRMSG: DB CR,LF,LF,0 ; IF SHOSYS URMSG: DB CR,LF,'Connected to: Your BBS System Name/Msg Here' DB CR,LF,0 ENDIF ; IF IMAT AND (NOT ASKNUL) AND (NOT PBBS) CRMSG1: DB CR,LF,'Default nulls = ' NULMSD: DB '0 ' DB CR,LF,'(you may change this at the main menu)' ENDIF ; IF IMAT AND (NOT ASKNUL) DB CR,LF,LF,0 ENDIF ; IF ASKNUL NULHDR: DB 'Enter new value (0-9) or accept default nulls (CR)',CR,LF,0 NULMSG: DB '0 <-- Default nulls',CR,0 NULTRY: DB 0 ; Counter for times asked ENDIF ; FKFLAG: DB 0 PTFLAG: DB 0 ; IF PRNTGB GBMSG: DB CR,LF,' Goodbye, call again...',CR,LF,LF,0 ENDIF ; RS1MSG: IF COMFILE DB CR,LF,'xecute .COM file' ENDIF ; DB CR,LF,'esume NUBYE -- no counter reset' DB CR,LF,'ero counters and resume NUBYE' DB CR,LF,'<*> Any other key aborts to CP/M' DB CR,LF,LF DB 'Enter your choice: ',0 ; IF NOT CLRSCR RS2MSG: DB ' Resuming...',CR,LF,0 ENDIF ; ATMSG: DB CR,LF,'Modem calls: ' ATMSN: DB ' ',0 CWCAR: DB 0 ; Counter for carrier detected calls ; IF PWRQD PASSWD: DB 'DDT' ; The password itself DB CR ; End of password, CR-only to erase it DB 0,0,0,0,0,0,0 PWMSG: DB CR,LF,'Enter Password: ',0 WRGMSG: DB 'Incorrect password',CR,LF,0 NWMSG: DB CR,LF,'Passwords: ' NWMSD: DB ' ',0 NWPWD: DB 0 ; Counter for correct password callers ENDIF ; IF CHEKDU IDMSG: DB '> [Invalid drive]',0 IUMSG: DB '> [Invalid user#]',0 ENDIF ; CLMSG: DB CR,LF,LF,'[Carrier lost]',CR,LF,0 ITOMSG: DB CR,LF,LF,'[Input timed out]',7,CR,LF,0 BELMON: DB CR,LF,LF,'[Bell on]',CR,LF,LF,0 BELMOFF:DB CR,LF,LF,'[Bell off]',CR,LF,LF,0 ; IF FUNKEY LCDMSG: DB CR,LF,LF,'[Last Call]',CR,LF,LF,0 LCDFLG: DB 0 MFSMSG: DB CR,LF,LF,'Message from Sysop: ',CR,LF,LF,0 SCRMON: DB CR,LF,LF,'[Remote on]',CR,LF,0 SCRMOFF:DB CR,LF,LF,'[Remote off]',CR,LF,0 SGDMSG: DB CR,LF,LF,'Going down in ',DOWNMIN+'0' DB ' minutes.',CR,LF,0 ULTMSG: DB CR,LF,LF,'[Unlimited Time]',CR,LF,0 ENDIF ; IF (ZCPR2 OR ZCPR3) AND FUNKEY WHLON: DB CR,LF,LF,'[WHEEL is ON]',CR,LF,0 WHLOFF: DB CR,LF,LF,'[WHEEL is OFF]',CR,LF,0 ENDIF ; LFMSG: DB CR,LF,0 ; IF PRNTWB WBMSG: DB CR,LF,'Booting CP/M...',CR,LF DB 0 ENDIF ; IF CPM3 SCBPB: DB 3AH,0 ; Parameter block for extracting SCB start adr ENDIF ; IF (COMFILE OR EXFILE) AND PRGRSS PTSMSG: DB '++ TPA too small ++',CR,LF,0 CNFMSG: DB '++ .COM not found ++',CR,LF,0 ENDIF ; IF COMFILE OR EXFILE ENTMSG: DB 'Entry ',0 ; Filled by loader CFLMSG: DB '.COM file loaded',CR,LF,LF,0 ENDIF ; IF EXFILE EXTMSG: DB 'Exit ',0 ; Filled by loader ENDIF ; IF MSGDSC MSGFCB: DB 0,'MFMSG COM' ; Put the name of your message handler here DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 ; Rest of FCB MSGMSG: DB 'MSGNAME ',0 ; Filled by loader ENDIF ; IF WELFILE WELFILN:DB 0,'WELCOME ???' ; WELCOME file name, must be 11 chars. DB 0 ; WELCOME or WELCOME.TXT will work ENDIF ; CM2FCB: IF MBBS DB 0,'MBBS COM' ; MBBS module DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 ; Rest of FCB ENDIF ; COMFCB: IF COMFILE AND NOSHOW DB 0,'YOURBBS COM' ; .COM filename (MUST be 11 chrs total) ENDIF ; IF COMFILE AND MBBS DB 0,'LOGIN COM' ; MBBS entry file ENDIF ; IF COMFILE AND METAL DB 0,'MENTR COM' ; Metal entry file ENDIF ; IF COMFILE AND OXGATE DB 0,'OXENTR COM' ; OxGate entry module ENDIF ; IF COMFILE AND RBBS DB 0,'RBBS COM' ; RBBS entry file ENDIF ; IF COMFILE AND PBBS DB 0,'PBBS COM' ; PBBS entry module ENDIF ; IF COMFILE DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 ; Rest of FCB ENDIF ; EXITFCB: IF EXFILE AND (NOT OXGATE) AND (NOT METAL) AND (NOT PBBS) DB 0,'EXIT COM' ; Exit filename, must be 11 characters ENDIF ; IF EXFILE AND METAL DB 0,'MEXIT COM' ; Exit filename ENDIF ; IF EXFILE AND OXGATE DB 0,'OXEXIT COM' ; Exit filename ENDIF ; IF EXFILE AND PBBS DB 0,'PBYE COM' ; Exit filename ENDIF ; IF EXFILE DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0 ; Rest of FCB ENDIF ; LCNAME: IF DSPLC OR MBBS DB 0,'LASTCALR???' ; LASTCALR OR LASTCALR.DAT will be used DB 0 ENDIF ; LCDATA: IF DSPLC OR MBBS DB ' ',0 DS LCBUF-2 ; Size of buffer LCFILL: DB ' NO DATA',0 ENDIF ; ; Customize this Last Caller Header to suit your system ; IF (DSPLC OR MBBS) AND DSPHED LCHEAD: DB CR,LF,LF DB 'B NAME FROM REC# DATE LOGS HMSG' DB CR,LF,0 ENDIF ; IF (DSPLC OR MBBS) AND READLC AND PRGRSS LCMSG1: DB 'No L/C file',CR,LF,0 ENDIF ; IF MBBS MBBS1: DB CR,LF,'Loading for COMMENT before exiting...',CR,LF,LF,0 ENDIF ; IF CHGPATH SYSPATH:DB PATHDR1,PATHUS1 ; Do A0: DB PATHDR2,PATHUS2 ; Then do A15: ENDIF ; IF CHGPATH AND Z3OPT DB PATHDR3,PATHUS3 ; Then do B15: DB PATHDR4,PATHUS4 DB PATHDR5,PATHUS5 ENDIF ; IF CHGPATH DB 0 ; End of path ; LSYSP EQU $-SYSPATH ; REMPATH:DB PATHDR1,PATHUS1 ; Do A0: (don't do A15:) DB 0 ; End of path ; LREMP EQU $-REMPATH ENDIF ; IF COMFILE OR EXFILE CURRFCB:DS 2 ENDIF ; TOCNTM: DS 1 TOCNT: DS 2 VCOLDBT: DS 3 VWARMBT: DS 3 VCONSTAT: DS 3 VCONIN: DS 3 VCONOUT: DS 3 VLISTOUT: DS 3 VPUNCH: DS 3 VREADER: DS 3 ; IF CPM3 SCBBASE: DW 0 ; Base address of the CP/M Plus SCB MEMBASE: DB 0 ; Base page of common memory BDOSBASE: DB 0 ; Base page of BDOS ENDIF ; CR EQU 0DH ; Carriage return LF EQU 0AH ; Line feed LXID EQU 11H ; Define byte of LIX D,nnnn to fake loader LXIH EQU 21H ; Define byte of LXI H,nnnn to fake loader IOBYTE EQU 3 ; Location of CP/M IOBYTE FCB EQU 5CH FCBRNO EQU FCB+32 MAIN EQU 1 VERS EQU 01 MONTH EQU 07 DAY EQU 28 YEAR EQU 86 LOCMD EQU 61 ; NUBYE's lowest extended BDOS call HICMD EQU 85 ; NUBYE's highest extended BDOS call MSPEED EQU 3CH ; Byte where baud rate stored BP110 EQU 0 ; 110 baud BP300 EQU 1 ; 300 baud BP450 EQU 2 ; 450 baud BP600 EQU 3 ; 600 baud BP710 EQU 4 ; 710 baud BP1200 EQU 5 ; 1200 baud BP2400 EQU 6 ; 2400 baud ; IF CPM3 SCBDAT EQU 0F4H ; Offset of date into SCB SCBTIM EQU 0F6H ; Offset of time into SCB SCBCOM EQU 0FAH ; Offset of common memory base page in SCB SCBBDOS EQU 99H ; Offset of base page of BDOS in SCB ENDIF ; BDOS EQU 5 CI EQU 1 WRCON EQU 2 DRECTIO EQU 6 PRINTF EQU 9 CSTS EQU 11 SELDSK EQU 14 OPEN EQU 15 SEARCH EQU 17 READ EQU 20 STDMA EQU 26 SETUSR EQU 32 CHAINP EQU 47 GTSCB EQU 49 ; DS 40 STACK EQU $ ; Local stack ; IF NOT CPM3 OBJEND EQU $ ENDIF ; END ; ; 1) Wayne Masters et al, "BYE507.ASM" (1986)