; ; NUBYE v1.01 ; 07/30/86 ; ; - REMOTE CONSOLE PROGRAM FOR CP/M AND MODEM - ; For use with CP/M 2 or CP/M 3 ; ; NUBYE will operate stand-alone (i.e. without running a BBS system), but ; also supports most of the popular CP/M BBS software (i.e. MBBS, METAL, ; Oxgate, PBBS, RBBS, ZBBS, etc). You should find NUBYE easy to install -- ; if not, contact me or one of the test sites for additional help as needed. ; There are currently three fully commented, public domain utilities of this ; type -- NUBYE, MBYE and BYE3 -- while each is unique in its own way, I ; hope NUBYE will attract your attention enough to make you switch (or, at ; least, give it a try). ; ; A complete NUBYExxx.LBR will contain the following files: ; ; NUBYE1xx.ASM This file ; NUBYE.HLP General installation help ; NUBYE.HIS All current and past revisions documented ; NUBDOS.DOC Extended BDOS functions documented ; NUBYE-IM.DOC General modem equates discussed ; NUBY-INS.LST List of currently available computer specific inserts ; NU-CLOCK.LST List of currently available clock inserts ; ; ===== ; ; NUBYE enables a remote caller to access and operate your system just as ; if he/she was sitting in front of the keyboard locally. It may be set ; up to operate as a stand-alone monitoring system (i.e. allows employees ; access to company computers during off hours - internal password entry ; option) or as part of a message base system, as normally found on ; RCP/M's. Read NUBYE.HLP for help in setting this program up for your ; particular needs. ; ; NUBYE is the result of no one authorship, but many hundreds of people ; across the country. The source code is fully commented to aid those of ; you who wish to customize/modify it or who are trying to understand how ; programs such as this work. ; ; If you have changes that you feel should be included in future releases, ; please forward them me, or to one of the contact boards listed below. ; All submitted modifications/suggestions will be considered, whether they ; are from an individual or a group. FULL credit will also be given for ; such code as is incorporated into future versions (i.e. system name and ; number, as well as the person's name). ; ; - Tom Brady ; Decibel RCP/M (PBBS) ; 300/1200/2400 ; *(404) 288-6858 ; P.O. Box 1773 ; Decatur, GA 30031 ; ; Other Contact/Beta-Test Systems: ; ; Joubert Berger Terry Carroll Jim Gooch ; Atlanta Kaypro MBBS Poor Man's Z-NODE (#56) Norcross MBBS RCP/M ; 300/1200 300/1200/2400 300/1200/2400 ; *(404) 923-2580 (817) 283-9167 *(404) 921-1116 ; ; Ernest Hintz Gary Inman Barry Miller ; KAY+FUN RCP/M West Los Angeles RCP/M Long Island Epson RCP/M ; 300/1200 300/1200/2400 300/1200/2400 ; *(415) 572-8219 (213) 838-9229 (516) 536-1546 ; ; Murray Simsolo ; LIKUG MBBS/RCPM ; 300/1200/2400 ; (516) 825-8465 ; ; (*PC Pursuit area) ; ; NOTE: All CP/M Plus (CP/M 3) code courtesy of George Peace -- enough ; credit has not been given to him for making NUBYE (and other ; BYE versions) available for CP/M 3 systems. If any of your ; modifications/questions concern CP/M Plus, please contact him ; on his system: ; George Peace ; FOG System #10 ; 300/1200/2400 ; (717) 657-8699 ; or ; (717) 657-0285 -- voice ; ; ===== ; ; Remove the ';' from before the ASEG if you use Microsoft's MACRO-80 ; macroassembler (referred to as M80). Nobody else needs this command. ; ; ASEG ; Needed by M80 assembler, only ; ; For conditional assembly ; NO EQU 0 YES EQU NOT NO ; ; **************************** ; OPTION CONFIGURATION SECTION ; **************************** ; ; NUBYE configuration ; CCPL EQU 8 ; Number of sectors for CCP size (norm=8) ; -Apples with Micrsoft CP/M v2.23+ use 9. ; -CP/M-3 does not use this. ; -Trantor WL bios, see TRANWL.INS CCPOFF EQU 128 ; Many systems need an offset, while others ; may get along with none. Leave at 128 ; unless you wish to experiment. ; ----- ; Modem Equates ; IMODEM EQU no ; Yes, using Hayes-type intelligent modem IMAT EQU no ; Yes, modem supports Hayes' "AT" protocol ; ; Set one (and only one) of the following HS equates to YES ; HS2400 EQU no ; Yes, modem's highest speed is 2400 bps HS1200 EQU no ; Yes, modem's highest speed is 1200 bps HS300 EQU no ; Yes, modem's highest speed is 300 bps ; ; The next 7 equates are only used if IMAT is YES ; ECHO EQU no ; Yes for Hayes, ProModem, Courier, RV-Maxwell ; NO for all others (Anchor, etc.) ANCHOR EQU NO ; Yes, if you have a Mark XII NODTR EQU NO ; Yes, modem or computer does not support DTR ; (i.e. Anchor Mark XII or some Osborne ; 1's). NOTE Ozzie-1 users: NUBYE can ; handle your NODTR problem, but will NOT ; work unless you install the hardware mod ; to fix the carrier detect logic. The ; same mod also fixes the DTR problem, so ; you should install the whole mother board ; modification kit and set this NO. NOATA EQU NO ; Yes, if you have an older Password, 212A or ; S-100 that will not execute the ATA ; command after ring is detected. Newer ; firmware works. SHORTB EQU NO ; Yes, for modems that that can't accept a 30 ; char cmd string, like the MultiModem. DOATZ EQU no ; Yes, send modem an ATZ command between ; calls. Set to NO if your modem reverts ; to auto-answer following an ATZ command. ; Many modems require it be set YES in ; order for the OFFHK function to work ; properly. (You will definately have to ; experiment with this one.) When set NO, ; it speeds up the turnaround b/t calls ; AND prevents modem from answering while ; NUBYE is trying to reinitialize for the ; next call. OFFHK EQU yes ; Yes, take phone off hook when running ; locally (E) or exiting to CP/M (instead ; of using ATS0=0). ; ----- ; ; BBS type, etc... ; ; If you operate a BBS system not shown and wish NUBYE to load/run your ; .COM file upon carrier detect (COMFILE = YES) and an exit .COM file ; upon carrier loss or user saying BYE (EXFILE = YES), you should set ; NOSHOW to YES and enter your entry/exit .COM filenames at the labels ; COMFCB: and EXITFCB: respectively (near end of this file). ; NOSHOW EQU NO ; Yes, none of the BBS systems shown below ; MBBS EQU no ; Yes, running MBBS BBS system (v4.1 & up) METAL EQU no ; Yes, running METAL BBS system. MINICK EQU no ; Yes, running MINICBBS OXGATE EQU no ; Yes, running OxGate BBS system PBBS EQU no ; Yes, running PBBS system RBBS EQU no ; Yes, running RBBS, sets/resets 'WRTLOC' flag ; IOVAL EQU 0 ; Initial value for IOBYTE (if MINICK YES) ; ; Set LMBELL to YES if you run a BBS system which uses the low memory flag ; location for the console bell status. To have NUBYE initialize with this ; flag turned off, set BELOFF to YES -- NUBYE will retain the last toggle ; state of this flag until you return to CP/M locally. ; (MBBS & RBBS = YES) ; LMBELL EQU no BELOFF EQU yes KILBEL EQU 3BH ; Low memory bell toggle flag location ; ; Set RTOK to YES if your BBS system requires a reentry flag to be reset ; between calls for proper BBS entry/reentry. Set RTOKFG to the low memory ; byte your system inspects when a user reenters it from CP/M. NUBYE will ; reset this byte to 0 between calls. ; (PBBS & RBBS = YES) (PBBS = 20H) (RBBS = 5BH) ; RTOK EQU no RTOKFG EQU 20H ; ; ----- ; General Equates ; WBDRIV EQU 'A' ; Drive to log to on first warmboot to CP/M HARDLOG EQU no ; Yes, echo remote input to printer PRINTER EQU no ; Yes, if your bbs uses printer for error ; msgs or if you use ^P remotely. ; ----- ; The next four equates are used if you wish for NUBYE to run a .COM file ; upon carrier detect. The COMDRV/COMUSR equates are used to tell NUBYE ; where your BBS .COM files are to be found. (MBBS NOTE: These are used ; for locating your LOGIN.COM and MFMSG.COM files.) Some systems perform ; unecessary double loads of the .COM file -- if this is so, set REENTER ; to YES (i.e. MBBS) ; COMFILE EQU no ; Yes, run a .COM file on carrier detect REENTER EQU no ; Yes, prevent double .COM file loading COMDRV EQU 'A' ; Drive where .COM file(s) located COMUSR EQU 0 ; User# where .COM file(s) located ; ; NOTE - MBBS systems: ; MBBSDRV/MBBSUSR are set ONLY for locating MBBS.COM ; MBBSDRV EQU 'A' ; Drive where MBBS.COM located MBBSUSR EQU 0 ; User# where MBBS.COM located ; ; ----- ; Set EXFILE to YES if NUBYE needs to chain to an exit file when a caller ; types BYE or carrier is lost. Your exit file must preserve the stack ; and do a RET (not warmboot) to reenter NUBYE. ; (MBBS = NO) (PBBS & CP/M 3 = YES) ; EXFILE EQU no EXDRV EQU 'C' ; Drive where exit .COM file located EXUSR EQU 15 ; User# where exit .COM file located ; ; Set EXRET to YES if your exit file can't preserve the stack and perform a ; normal RET (i.e. MBASIC, C and some Pascal programs). If your exit file ; has this problem, poke 5DH (FCB+1) with a lowercase 'r' and do a warm- ; boot -- NUBYE will handle the return properly.1 ; EXRET EQU NO ; ; Set BYHANG to YES to have NUBYE hang up the phone *before* calling your ; exit file. (METAL & OXGATE = YES) (PBBS = NO) ; BYHANG EQU NO ; ; ----- ; Set MSGDSC to YES if you run a system which supports NUKMD's upload ; description entry (v1.01+) or its special message file transfers. ; Your message file handling utility must preserve the stack and do a ; RET (not warmboot) to reenter NUBYE. The default name for this utility ; is MFMSG.COM defined at the label MSGFCB:. ; (MBBS = YES) ; MSGDSC EQU no ; ; ----- ; Set READLC to YES if you need NUBYE to read your LASTCALR file -- leave ; set to NO, if your BBS pokes this information into NUBYE. ; (MBBS & PBBS = NO) ; READLC EQU no LCDRV EQU 'C' ; Drive where LASTCALR.??? is located LCUSR EQU 15 ; User# where LASTCALR.??? is located ; DSPLC EQU no ; Yes, display last caller data via ^W. LCBUF EQU 78 ; Size (bytes) of last caller buffer ; (MBBS = 65 / METAL, PBBS, OXGATE = 78) ; ; Set DSPHED to YES if you wish to have a special descriptive header ; displayed whenever you hit the ^W (WHOKEY) function key. Modify this ; header at the label LCHEAD: (near the end of this file) to suit your ; system. Default header is formatted for MBBS systems. ; DSPHED EQU no ; ; ----- ; Set WELFILE to YES if you wish to have NUBYE display a special text file ; upon login. If running a BBS system, you would normally leave this NO. ; WELFILE EQU NO WELDRV EQU 'A' ; Drive where WELCOME??? file located WELUSR EQU 14 ; User# where WELCOME??? file located ; ; ----- ; Set CLRSCR to YES if you wish to have NUBYE perform automatic clear screen ; sequences during system resets. Until you have everything working ok, ; you should leave this set to NO. When set NO, you may manually clear your ; screen with the ^Z function. Define your clear screen code (up to ; 6 bytes max) below at CLRCH1-CLRCH6. ; CLRSCR EQU yes CLRCH1 EQU 1AH ; All unused bytes must remain 0 CLRCH2 EQU 0 CLRCH3 EQU 0 CLRCH4 EQU 0 CLRCH5 EQU 0 CLRCH6 EQU 0 ; ; ----- ; Set SHOSYS to YES if you wish NUBYE to display your system name, or other ; special entry line upon carrier detect (this is regardless of the equate ; settings below -- HITCR/ASKNUL). ; SHOSYS EQU no ; ; Set HITCR to YES if you have ASKNUL set NO and feel you must have your ; system wait until the user hits a key before continuing (rather defeats ; the purpose of bypassing the nulls question...) ; HITCR EQU no ; ; Set ASKNUL to YES if you aren't running a BBS system, or if your BBS ; system does not allow user setting as a menu option (NUBYE will default ; to th caller's MSPEED upon carrier detect, regardless -- v1.00). If you ; leave this set to NO and are not running PBBS, NUBYE will display the ; default nulls setting to the user upon carrier detect. ; (MBBS & PBBS = NO) ; ASKNUL EQU no ; ; ----- ; Set PRGRSS to NO once your system is operating smoothly. While set to ; YES, NUBYE will display your modem result codes and other useful error ; message (should anything unusual occur). ; PRGRSS EQU yes ; Set NO when running smoothly ; ; ----- ; Set PRNTGB to YES if you wish NUBYE to say "Goodbye..." (PBBS = NO) ; PRNTGB EQU no ; ; Set PRNTWB to YES if you wish NUBYE to say "Warm Boot" each time one ; occurs (normally leave set to NO). ; PRNTWB EQU NO ; ; ; ----- ; If you aren't running a BBS system, set PWRQD to YES and NUBYE will ; require the user to answer a question expecting your predefined answer ; (as defined at the label PASSWD: -- defaults to DDT). If you run a BBS ; and set this to YES, NUBYE will ask for this entry password, before it ; runs your BBS program (normally set to NO). ; PWRQD EQU no ; ; ----- ; System and Hardware dependent options ; MHZ EQU 4 ; Processor clock in MHz ; ; Set ECBFIX to YES if your computer requires that you reset the ; modem/ports after a cold boot, before doing a "BYE E" -- normally, ; set this NO. Note: Kaypro 10 with TurboROM and Epson QX's should ; probably set this to YES. ; ECBFIX EQU no ; ; CLOSS is set to the number of seconds to wait before hanging up, after ; the carrier drops (normally leave set to 1). ; CLOSS EQU 1 ; ; ^C's are mapped to this character -- some systems can't handle them. ; (MBBS = 'C') (PBBS = 'K') ; CTRLC EQU 'C' ; ; Set DOWNMIN to the number of minutes before a user is kicked off, once ; you hit ^O -- tells user "System going down in xx minutes". ; DOWNMIN EQU 2 ; ; Set LOSER to YES if a warm boot overwrites part of the BIOS ; LOSER EQU NO ; ; Set MOTOR to YES if you wish to have your drives turned on/off by NUBYE ; between calls. ; MOTOR EQU no ; ; ----- ; The next three equates are only for CP/M 3 users (see NU-CPM3.DOC). ; CPM3 EQU no ; Yes, operating under CP/M 3 POSPRO EQU no ; Yes, execute MDPOSP routine in computer insert PREPRO EQU no ; Yes, execute MDPREP routine in computer insert ; ; End of CP/M 3 ; ------------- ; ; Function Keys -- If you are pressed for TPA, set FUNKEY to NO. This ; leave you with the TWITKEY and BELLKEY, but turn off all other sysop ; functions. ; FUNKEY EQU yes ; Yes, to use all special function keys ; ; Set LEADIN to YES, if you wish to specify your own lead-in key for ; operation of sysop functions. If NO, all functions respond to CTRL keys. ; LEADIN EQU NO ; Yes, to specify lead-in key ; IF LEADIN LEADKY EQU '\' ; Choose a seldom used key ENDIF ; LEADIN ; ; If LEADIN is YES, you must type the LEADKY first, then a character inside ; the ' ' below. If LEADIN is NO, the following characters are used as ; single control keys. You may define your own characters inside the ; quotes (NOTE: These are CONTROL characters -- i.e. ^C, ^G, etc...). ; BELLKEY EQU 'G' ; Toggle bell on console TWITKEY EQU 'N' ; Hangup modem manually ; IF FUNKEY ANSKEY EQU 'A' ; Force modem to answer the phone BLNKKEY EQU 'B' ; Toggle remote terminal on/off WHLKEY EQU 'E' ; Toggle WHEEL on/off LCKEY EQU 'L' ; Force local return to CP/M, after ; current user logs off system. SYSDKEY EQU 'O' ; Print "System going down in n min.." MSGKEY EQU 'Q' ; Print "Message from SYSOP: " TIMEKEY EQU 'T' ; Display time (if TIMEON) -- will also ; set the auto-startup time (over-rides ; the ONHR setting) to current hour. ULTKEY EQU 'U' ; Set current caller to unlimited time. WHOKEY EQU 'W' ; Display LASTCALR if DSPLC is YES. XITKEY EQU 'X' ; Exit from "Message from sysop" loop ENDIF ; IF (NOT CLRSCR) AND FUNKEY CLSKEY EQU 'Z' ; Clear crt manually when CLRSCR is NO ENDIF ; ; ----- ; Normally, your BBS program will set the access bits for each user (as ; with MBBS). SETFLG will allow you the option of having any/all of the ; access bits turned on when you use the ^B/^U functions. The ^B function ; is an ON/OFF Sysop toggle (i.e. normal user settings returned), but the ; ^U function is an ON only toggle, so not acceptable in all situations. ; Choose which bits you wish to toggle ON at USRFLG. ; ; NOTE: MBBS systems do NOT need this function, so leave SETFLG NO and use ; your special MBBSUSRC.COM utility, instead. ; IF FUNKEY ; SETFLG EQU no ; Yes, if you wish ^B/^U to turn on the ; access bits chosen below (USRFLG). ; (NO for MBBS, normally) ; ; * Of the 8 flag bits available below, only bits 3, 5, 6 and 7 are checked by ; NUKMD through NUBYE's extended BDOS call 85. ; USRFLG EQU 11111111B ; ^B will toggle these bits ON ; |||||||| ; |||||||| BIT Used For ; ||||||||_____ 0 System Access (0 = no admittance) ; |||||||______ 1 BBS Access (0 = CP/M only) ; ||||||_______ 2 Read Access (0 = no message reading) ; |||||________ 3* Write Access (0 = no message writing) ; ||||_________ 4 CP/M Access (0 = no CP/M access) ; |||__________ 5* Download Access (0 = no downloads) ; ||___________ 6* Upload Access (0 = no uploads) ; |____________ 7* Priviledged User (0 = normal user) ; ; ----- ; Set SHOWTLN to YES if you want the ^T function to display: ; "Minutes left: xx" (normal users) ; "Minutes on: xx" (special users) ; Otherwise, the default display for all will be: "Minutes on: xx" ; (Note: In either case, displayed on local console only) ; SHOWTLN EQU no ; Yes, special display ; ENDIF ; FUNKEY ; ; ----- ; Clock/Time Equates ; ; Set CLOCK to YES if you have inserted your reader code (found within ; the NU-CLOCK.LBR) at the label TIME: -- store binary values in CCHOUR ; and CCMIN. ; CLOCK EQU no BCD2BIN EQU no ; Yes, your clock routine calls BCDBIN BIN2BCD EQU no ; Yes, your clock routine calls BINBCD ; ; Set UPDOUT to NO if your system is slow during clock reads (as evidenced ; by "hitches" during DIR's and other screen output) and wish the clock reads ; to occur only on incoming CR's. Normally, set to YES and NUBYE will update ; the clock after LF's sent by your system. ; UPDOUT EQU yes ; ; (NOTE: The previous caller's timeon is preserved for your exit or login ; program to access in NUBYE's fixed lookup table at label LCPTR. You may ; choose to store that data in low memory, or elsewhere by changing the ; LCTON equate to a new address. LCTON is found just after NUBYE's fixed ; lookup table.) ; ; Set TIMEON to YES if you want NUBYE to monitor the user's time-on-system ; and log them off after MAXMIN. Even if you don't have a clock, setting ; this to YES here and in NUKMD will allow NUKMD to restrict file transfers ; according to MAXMIN and the computed transfer time. ; TIMEON EQU yes ; MAXMIN EQU 45 ; Minutes for maximum time allowed on system. ; (255 max -- 0 = No restrictions) ; (Suggest 45 if CLOCK NO and TIMEON YES) TOVALUE EQU 4 ; Minutes of no-activity allowed (255 max) ; before NUBYE kicks them off. PRNTOS EQU no ; Yes, show "Time on: xx" on warm boots ; ; ; Set AUTOSYS to YES if you are running your system only during certain ; hours (i.e. 7 p.m. to 8 a.m.) and wish for NUBYE automatically run ; your system. It will make your system start answering at ONHR and stop ; answering at OFFHR. This routine will also automatically determine if a ; user is on DOWNMIN before OFFHR, warn the user (i.e. "System going down ; in xx minutes") and begin the automatic countdown just as if you had been ; there and used the ^O function key. ; ; While NUBYE is waiting for ONHR, you may force it to override ONHR by ; hitting your ^T -- it will then start answering on the next ring and ; continue until OFFHR (it then reverts back to waiting for the next ONHR). ; You must have a clock reader routine (set CLOCK to YES) in order to take ; advantage of this feature. ; ; With this one function, you can have NUBYE run your board from 7 p.m. to ; 8 a.m. every day of the week automatically, fully unattended and go on ; vacation or whatever. ; AUTOSYS EQU no ; Yes, automatic unattended system operation ; during specific hours. ONHR EQU 19 ; Time for auto system startup (19 = 7 pm) OFFHR EQU 08 ; Time for auto system shutdown (08 = 8 am) ; 24 hr clock (00 = midnight 23 = 11 pm) DROPLOC EQU no ; Yes, drops into local mode at OFFHR, else ; will continue to wait for ONHR to again ; begin answering the phone. ; ; If you wish to restrict a certain baud rate access to your system during ; specific hours, you may do so by setting the next four equates accordingly. ; Must have a clock reader routine (set CLOCK to YES) in order to take ; advantage of this feature. ; RSPEED EQU NO ; 24 hr clock (00 = midnight 23 = 11 pm) BEGHR EQU 19 ; Time to begin restriction (19 = 7 pm) ENDHR EQU 22 ; Time to end restriction (22 = 10 pm) SPEED EQU 5 ; Lowest baud rate allowed access between ; BEGHR and ENDHR. Change the message ; located at OFFMSG: to match your times, ; baud rate and time zone. ; (1=300, 5=1200, 6=2400) ; ----- ; CCP Options ; ZCPR2 EQU no ; Yes, if running ZCPR/ZCMD/NZCPR (1 or 2) ; or if using CP/M 3 with the WHEEL byte ; ; NOTE: Requires MAC.COM to assemble if ZCPR3 is set YES. ; ZCPR3 EQU no ; Yes, if running ZCPR3 ; IF ZCPR3 MACLIB Z3BASE ; Requires MAC to assemble, else enter ; constants directly. See label DOZ3 ; for required EQU's. ENDIF ; ; NZCPR/ZCMD/ZCPR all use bytes (at 3DH/3EH/3FH) to store the maximum ; drive, wheel status, and maximum user area. Some BBS systems poke ; these low memory bytes to reset these (for Sysop, etc.) Other BBS ; systems the bytes in NUBYE that do the same thing. The equate USEZCPR ; allows you to select where you want to poke things. ; (MBBS/PBBS/OXGATE = NO) (RBBS = YES) ; (if ZCPR2 and ZCPR3 are set NO, then set USEZCPR to NO) ; USEZCPR EQU NO CHEKDU EQU NO ; Yes, if NUBYE will monitor MAXDRIV/USER. ; If using ZCPR/ZCMD/NZCPR, set this NO, ; since they already do it (saves alot of ; code, too). In either case, NUBYE will ; have the correct values in MAXDRIV/USER. MAXDRIV EQU 3DH ; ZCPR lolcation of MAXDRIV byte WHEEL EQU 3EH ; Location of ZCPR's wheel flag MAXUSER EQU 3FH ; ZCPR location of MAXUSR byte ; ; (NOTE: Ignore MAXDRV/USR if running MBBS.) ; MAXDRV EQU 'H' ; Highest drive supported MAXUSR EQU 5 ; Highest user area ; ; In all cases, set SYSDRV/USR, since the ^B function gives you these ; d/u areas when used to toggle off the user temporarily. ; SYSDRV EQU 'M' ; Highest local drive supported SYSUSR EQU 15 ; Highest local user area (0-15) ; ; When CHGPATH is set YES (and you're not using internal preconfigured paths, ; as with ZCMD23 and up), NUBYE will automatically change the .COM search ; path depending upon current system mode. Example: For normal users, the ; path should be to search the default d/u area, THEN search drive A, user ; area 0. When you toggle remote off (^B function), your sysop path takes ; effect (i.e. default d/u area, drv1/usr1, drv2/usr2, drv3/usr3) -- when ; remote is toggled back on, normal user paths are restored. This allows ; you to safely keep your special utilities (i.e. NSWEEP, DDT, etc) out of ; harm's way... ; ; NOTE: ZCMD23 (and later) users who have their systems set for proper ; internal paths, should leave CHGPATH set to NO. ; CHGPATH EQU no ; Yes, if changing ZCPR's external path EXTPATH EQU 40H ; ZCPR external path default location ; ; The following equates determine the command paths you require NUBYE ; to take. PATHDR1/PATHUS1 is command path available to both sysop and ; user, while PATHDR2/3 and PATHUS2/3 are strictly for sysop. ; (NOTE: Drive A = 1, B = 2, C = 3, etc...) ; Z3OPT EQU no ; Yes, if you wish NUBYE to search ; additonal paths (i.e. ZCPR3) ; PATHDR1 EQU 1 ; 1st command path drive (A:) PATHUS1 EQU 0 ; 1st command path user area (0) PATHDR2 EQU 1 ; 2nd command path drive (A:) PATHUS2 EQU 15 ; 2nd command path user area (15) PATHDR3 EQU 2 ; 3rd command path drive (B: -- optional) PATHUS3 EQU 15 ; 3rd command path user area (15 -- optional) PATHDR4 EQU 0 ; 4th (optional) PATHUS4 EQU 0 PATHDR5 EQU 0 ; 5th (optional) PATHUS5 EQU 0 ; ; ----- ; Motor controlled drives ; ; These values suit a Compupro/Viasyn Disk1A with YE-DATA 180 20cm drives ; Alter to suit your needs if your disk controller supports motor control. ; IF MOTOR DISK EQU 0C3H ; Disk control port DISKON EQU 80H ; Motors on DISKOFF EQU 0 ; Motors off ENDIF ; ; ----- ; If LOSER is set YES ; ; There are some cases where warm boot overwrites the initial BIOS jump ; table. This problem was solved for the Superbrain 3.0 bios by find- ; ing a warmboot call to HIGH in the BIOS. This call is then patched by ; NUBYE. The form of the call is: WBCALL CALL WMSTRT ; IF LOSER WBCALL EQU 0E260H ; Check this in your BIOS ; ; The following location is called ; WMSTRT EQU 0E566H ; Check this in your BIOS ENDIF ; ; --------------------------- ; END OF OPTION CONFIGURATION ; --------------------------- ; IF NOT CPM3 ORG 100H ; ; ------- Special Loader Routine ------- ; 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 ; ; HL now contains the destination address of NUBYE ; 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) ; ; The following code determines whether or not an address is within the ; NUBYE prgm and sets it to the new address if it is - otherwise it will ; not disturb the code. ; 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. ; ; Here is where we test for the 3-byte opcodes ; 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 ; ; Skip all the 2-byte opcodes - this keeps the transfer program from ; trying to figure out what the second byte is. ; 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 ; ; See if the address is above the range ; 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 ; ; See if the address is below the range ; 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 ; ; Update the value of this address by adding the offset to it ; 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 ; ; Small subroutine to negate the contents of HL ; 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 ; ; Prepare to branch to the NUBYE program ; 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 ; ; The following table is the listing of the 2-byte opcodes used in the ; 8080 instruction code set. ; 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 ; ; The following table defines the 3-byte load instructions used in the ; 8080 instruction set. ; 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 ; ; ---------- CP/M 3 ---------- ; ;This is a Resident System Extension (RSX) intended to run under CP/M Plus. ;The RSX is called via BDOS function 60 (in register C) with register pair DE ;pointing to the RSX parameter block. The parameter block has the following ;format: ; ; RSXPB: DB subfnc ;the subfunction number (0-127) for this RSX ; DB 2 ;exactly two parameters follow ; DW rsxnam ;pointer to the name of the RSX being called ; ; (must be exactly 8 bytes) ; DW arglst ;pointer to the arguments for the subfunction ; ; (may vary in length depending on subfunction) ; ; ------ RSX Prefix Structure ------ ; 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 ; ; ------ BDOS function 60 sub-functions used by this RSX ------ ; RSXTEST EQU 100 ; Test for RSX in memory RSXINIT EQU 101 ; RSX initialize function code RSXTERM EQU 102 ; RSX terminate function code ; ; ------ RSX-specific Constants used to support this RSX ------ ; NPARAM EQU 2 ; Number of parameters required in caller's ; RSX parameter block ; ; ------ Define the RSX subfunction lookup table format ------ ; TABENT MACRO M1,M2 ; TABENT subfunction,address DB M1 ; RSX subfunction number DW M2 ; RSX routine address ENDM ; ; ------ RSX subfunction lookup table ------ ; 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 ; ; ------ Miscellaneous data/constants ------ ; 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 ; ; ------ Stack storage ------ ; DS 48 ; LOCSTK: ; Internal stack STKSAV: DW 0 ; Caller's stack pointer ; ; ------------------------------------ ; ------ Entry point to the RSX ------ ; ------------------------------------ ; FTEST: MOV A,C ; Get BDOS function CPI 60 ; Could this one be for us? JNZ NEXT ; Nope - ignore this function ; ;This is the RSX-specific BDOS call. First, we'll make sure that the call ;is actually for this RSX by testing the name of the RSX passed as the first ;parameter. ; 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 ; ;Ok, the name matches so this must be the right RSX. ; POP D ; Recall passed parameter XCHG SHLD RSXPB ; Save caller's RSX parameter block address ; ;But wait ... what if there is already another copy of this RSX in memory? ;We'll check ... ;Call the next RSX or BDOS to see if this is the only copy of the RSX ;currently in memory with our name. ; 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 ; ;Ok, this is it. All the external validity checks are complete. Now make sure ;the RSX has been properly initialized. If not, only an initialize operation ;is allowed. ; ;Get the RSX subfunction from the first byte of the caller's parameter block ; 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 ; ;Check the initialize indicator ; 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 ; ; ----------------------------------- ; ------ RSX control functions ------ ; ----------------------------------- ; ; ------ Provide an indication that the RSX is in memory and available ------ ; ; ARGLST: DB nn ;dummy parameter ; DB nn ;status 1 of the request (0CCh=ok, other=error) ; DB nn ;status 2 of the request (0CCh=ok, other=error) ; ;(both bytes must be equal) ; 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 ; ; ------ Initialize the RSX environment for subsequent calls ------ ; ; This routine establishes the environment for all other calls to the RSX. ; The operation is completed only if the RSX is not in an initialized state. ; If the RSX is already initialized, the function is bypassed and an error ; status is passed back to the caller. ; ; ARGLST: DB nn ;dummy parameter ; DB nn ;status of the request (0=ok, other=error) ; 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 ; ; ------ Disengage this RSX ------ ; ; ARGLST: DB nn ;dummy parameter ; DB nn ;status of the request (0=ok, other=error) ; 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 this RSX and pass control on to the next one ------ ; 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 ; ; ------ Processing is complete - return to caller ------ ; 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 ; ; Set aside space for the stack region ; DS 40 ISTACK: DW 0 ; Top of stack ; ; ----- ; The following code gets moved to high RAM by the loader or RSX program, ; where it is then executed. ; 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 ; ; ----- ; Here is a quickie handy reference table to use so we do not get mixed ; up. Please do not change the order of it in any future changes. ; ; |mxusr|mxdrv|toval|nulls|ulcsw|lfeeds|wrtloc|hardon|lostflg|covect| ; |1 byt|1 byt|1 byt|1 byt|1 byt|1 byte|1 byte|1 byte|1 byte |2 byes| ; ; |BYE |comflg|stat |lcbuf|mxtme|rtcbuf|bell |twit | ; |3 byt|1 byte|1 byt|2 byt|1 byt|2 byte|1 byt|1 byt| ; |bell | ; ; ----- ; Variables follow in a predefined order that can be manipulated by a ; passworded or other program to give special users different capabili- ; ties. ; 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 ; ; End of NUBYE's fixed lookup table ; --------------------------------- ; 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 ++++ ; *************************************************** ; ; --------------- ; START IMAT CODE ; --------------- ; 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 ; ; Get next character if first was a '1' ; 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 ; ; Initialize the modem for normal unattended operation. ; 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 ; ; Delay about one second to let modem get stabilized before or after a ; command string. ; 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 ; ; De-initiaize the modem. When the operator uses CTL-C followed by any- ; thing but "R", this call will return the modem to default settings. ; 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 ; ; Send a command string to the modem. (If ECHO) Verify, reset the modem ; and resend string if echo fails. ; 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 ; ; Pre-shutdown warning and auto logoff countdown. ; 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 ; ; END OF IMAT CODE ; ---------------- ; ; 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 ; ; ************************************** ; ; If the carrier is lost - hang up, await ring. Otherwise, say goodbye, ; and hang-up. ; 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 ; ; Patch in NUBYE's BDOS interceptor ; 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 ; ; Set MINICK to 'YES' if you use MINICBBS and want to take advantage of ; its feature which can prevent the modem from hanging up if the caller ; should happen to disconnect during a file update. MINICBBS sets the ; high-order bit of IOBYTE (address 3H) to indicate a file update is ; in progress. ; 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 ; ; Get the System Parameter Block address, common memory base page, ; BDOS base page for use in main program or overlays. ; 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 ; ; Nobody there, or we are done. ; 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 ; ; Give summary and initialize for next call ; 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 ; ; ZCPR3 command line buffer, shell stack, TCAP stuff (this section was ; DOZ3: routine). ; 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 ; ; This CALL to MDQUIT caused some modems trouble when OFFHK was YES. While ; many computer inserts just call IMQUIT from MDQUIT without doing anything, ; some would drop DTR, then call IMQUIT -- this is what caused those folks' ; modems to ignore the ATH1 command... Hence, the above CALL directly to ; IMQUIT solves everyone's problems. ; 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 ; ; Await ring - check local keyboard for ^C exit request - also check ; for startup and shutdown times (if AUTOSYS is set YES). NOTE: Must ; do input via BDOS because CBIOS patches are not done until the call ; comes in. ; 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 ; ; ----- ; answer routine ; 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 ; ; Now test input for baud rate - FIRST, check for 300 baud ; 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 ; ; Now check for 1200 bps ; 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 ; ; Now check for 2400 bps ; 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 ; ; end of answer routine ; --------------------- ; 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 ; ; ----- ; Common routine to check for carrier lost - called from console out ; 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 ; ; Carrier is lost, inform sysop ; LXI H,CLMSG ; Carrier lost message CALL PRINTL ; Send this Message ; ; Come here to log off a user ; 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 ; ; ----- ; Function key routines ; 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 ; ; Message from Sysop ; 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 ; ; System Going down ; 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 ; ; Last call after current user logoff ; 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 ; ; ; Toggle bell ; 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 ; ; Set user for unlimited time and special access flag settings ; 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 ; ; Toggle blankout ; 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 ; ; WHEEL toggle ; 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 ; ; Who's there ; 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 ; ; Display time-on or time-left-on ; 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 ; ; end of function key routines ; ---------------------------- ; ; Routine to zero memory blocks ; IF ZCPR3 ZERO128:MVI B,128 ENDIF ; ZEROM: MVI M,0 INX H DCR B JNZ ZEROM RET ; ; ---- ; NUBYE's BDOS interceptor (See NUBYE.DOC for more information) ; 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 ; ; Ok, it's one of our commands, let's handle it. ; 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 ; ; NUBYE existance test ; 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 - if A=0..254 then poke value with A ; - if A=255 then return with current value ; 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 - if A=0 then poke value with 0 ; - if A=1 then poke value with 255 ; - if A=255 then return with current value ; 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 ; ; ----- ; .1 sec delay routine ; DELAY: PUSH B LXI B,4167*MHZ ; Timing constant * clock MHz ; DELAY1: DCX B MOV A,B ORA C JNZ DELAY1 POP B RET ; ; 1 millisecond delay routine ; KDELAY: PUSH B LXI B,42*MHZ ; Timing constant * clock MHz JMP DELAY1 ; ; ----- ; Here to exit to CP/M (first reset the Port/Modem to default status). ; BEXCPM: LXI H,BELMSG ; Bells CALL PRINTL ; Tell sysop ; EXCPM: IF IMAT CALL IMQUIT ; Send ATH1 or ATS0=0 depending on OFFHK ENDIF ; ; This CALL to MDQUIT caused some modems trouble when OFFHK was YES. While ; many computer inserts just call IMQUIT from MDQUIT without doing anything, ; some would drop DTR, then call IMQUIT -- this is what caused those folks' ; modems to ignore the ATH1 command... Hence, the above CALL directly to ; IMQUIT solves everyone's problems. ; 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 ; ; ----- ; Loss of carrier test and drive/user validation ; 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 ; ; Now test drives and (if CP/M 2.x) user #'s to insure that maximums ; are not exceeded. ; 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 ; ; ----- ; print routines ; ; The following code has been modified to accomodate the automatic ; loader. (The loader may modify a constant, so all messages have been ; placed at the end of the program and just moved to high memory.) ; ; Print on both consoles ** USE ONLY IF IN PATCHED MODE ** ; 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 ; ; ; Print locally only ; 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 ; ; ----- ; .COM file routine ; ; Routine to load the .COM file ; 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 ; ; Now load the file ; 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 ; ; Check for load past top-of-memory ; 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 ; ; end of .COM file routine ; ------------------------ ; ; Boot trap - becomes disconnect if JMP at 0 doesn't match any tests. ; 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 ; ; ----- ; Modem input function, checks local console first ; 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 ; ; Local console wasn't ready, so read modem ; CALL MDINP ; ; Clean up line noise (for us, anyway) here. ; 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 ; ; ----- ; Modem output function ; MOUTPUT: IF NOT CPM3 CALL BDCHEK ENDIF ; ; If we already know carrier is lost, do not check for it again or loop ; trying to output. ; 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 ; ; ; Check for nulls ; CPI LF ; Time for nulls? RNZ ; No ; ; ; Send nulls if requested ; 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 (HL) to (DE), length in (B) ; 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 ; ; ----- ; Keyboard/modem status test routine ; 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 ; ; Can't wait around forever, so tell user... ; 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 ; ; ----- ; This is the jmp table which is copied on top of the one pointed to by ; location 1 in CP/M. ; NEWJTBL:JMP MCBOOT ; Cold boot JMP MBOOT ; Warm boot JMP MSTAT ; Modem status test JMP MINPUT ; Modem input routine JMP MOUTPUT ; Modem output routine ; ; This prevents everything going double, if sysop types ^P accidentally (and ; HARDLOG/PRINTER options are off). ; 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 in the new JMP table (saving the old) ; 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 ; ; ; Now move the new JMP table to CP/M ; 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 is called to print out the NUBYE version # and USRLOG info. It ; can be called from outside the program, using the vector after MCBOOT. ; 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) ; ; ----- ; Readbyte routine - used to read the welcome file ; IF WELFILE RDBYTE: MOV A,H ; Time to read? ORA A ; If at 100H, no read required JZ NORD ; Have to read a sector ; 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 ; ; ----- ; Calculate HL=CP/M's jump table, B=length ; 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 ; ; ----- ; TSTBAUD attempts to read a CR, LF or CTL-C and returns with zero flag ; if the character read is one of these three. ; 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 routine (note no extent used) ; ; Welcome to the system ; 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 ; ; The # of nulls will default to the value of MSPEED, which should ; keep most sysops happy, whether ASKNUL is set YES or NO. ; 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 ; ; Print the welcome file ; 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 ; ; Open the WELCOME file ; MVI C,SELDSK ; Select default drive for WELCOME file MVI E,WELDRV-'A' CALL BDOS LXI D,FCB CALL OPENFIL ; ; Did it exist? ; JZ PASSINT ; No WELCOME file ; ; Got a file, type it ; XRA A ; A=0 STA FCBRNO ; Zero record number LXI H,100H ; Get initial buffer pointer ; ; Type the WELCOME file ; 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 ; ; Get the password ; 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 ; ; Password did not match ; 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 ; ; Character matched in password ; PWMAT: INX H ; To next character CPI CR ; End? JNZ PWMLP ; No, loop ; ; End of password, any missed characters? ; 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 ; ; Either run the COM file or warmboot to cp/m ; 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 ; ; Delete any RSX's the last caller might have caused to be loaded. If we ; find NUBYE RSX, assume all other RSX's have been removed. ; 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 ; ; Compare two strings DE, HL, length in B ; 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 ; ; end of WELCOME routine ; ---------------------- ; ; calculate user's elapsed time ; ; Calculate time on system. Log him off if =>MAXMIN unless WHEEL is on, ; or MXTIME=0. If MXTIME = 255, user not logged in. ; 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 ; ; TCX60 will multiply (A)x60, results in (HL). ; 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 will subtract (DE) from (HL), results in (HL). ; (HL) normally has the larger number ; 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 ; ; BCD to Binary converter ; ----------------------- ; This routine will convert an 8 bit BCD number (0-99) to binary. ; ; To use: ; LDA BCDNUMBER ; CALL BCDBIN ; ; The routine returns with the binary number in the A register. ; 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 ; ; BINBCD will convert a 0-99 binary number to 0-99 BCD number. ; Call with (A)=binary number 0-99. (A)=0-99 BCD on exit ; 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 will convert an 8 bit binary number in A to 3 ASCII bytes. ; HL points to the MSB location where the ASCII bytes will be stored. ; Leading zeros are suppressed and spaces are stored in unused bytes. ; 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 ; ; ----- ; SCB manipulation for CP/M Plus ; ; Set SCB byte at offset in [B] to value in [A] ; IF CPM3 SETSCB: LHLD SCBBASE ; Get SCB pointer MOV L,B ; Set the offset MOV M,A ; Save the byte RET ; ; Get SCB byte at offset in [B] to [A] ; GETSCB: LHLD SCBBASE ; Get SCB pointer MOV L,B ; Set the offset MOV A,M ; Get the byte RET ; ; Set user number to value in [A] ; 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 ; ; Set drive to value in [B] ; 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 ; ; End of SCB manipulation routines ; -------------------------------- ; IF NOT CPM3 ENDOBJ EQU $ ENDIF ; ; end of main body of program ; ; -------------------------------------------------- ; START IMAT COMMANDS (Set IMAT EQU YES to use this) ; -------------------------------------------------- ; 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 ; ; End of IMAT command strings ; --------------------------- ; ; messages ; 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 ; ; Program version number message ; 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 ; ; Real-Time clock buffer. ; Store BCD values into this area if TIMEON is YES and your clock is read ; as BCD values for time and date. Use BCDBIN to convert HH and MM to binary ; and store those values in CCHOUR and CCMIN. ; ; (NOTE: 99:99:99 INDICATES CLOCK IS NOT RUNNING OR NOT AVAILABLE) ; 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 ; ; Access password (ends in carriage return) - keep password here ; IF PWRQD PASSWD: DB 'DDT' ; The password itself DB CR ; End of password, CR-only to erase it ; ; (Allow room for larger password to be entered, up to 10 characters) ; 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 ; ; ----- ; ZCPR external paths -- the current d/u area will always be searched first. ; ; Command path available for SYSOP ; 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 ; ; ; Command path available for remote user ; REMPATH:DB PATHDR1,PATHUS1 ; Do A0: (don't do A15:) DB 0 ; End of path ; LREMP EQU $-REMPATH ENDIF ; ; end of ZCPR external paths ; -------------------------- ; ; These areas are not initialized ; IF COMFILE OR EXFILE CURRFCB:DS 2 ENDIF ; TOCNTM: DS 1 TOCNT: DS 2 ; ; Save the CP/M jump table here ; VCOLDBT: DS 3 VWARMBT: DS 3 VCONSTAT: DS 3 VCONIN: DS 3 VCONOUT: DS 3 VLISTOUT: DS 3 VPUNCH: DS 3 VREADER: DS 3 ; ; System addresses initialized at program startup ; 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 ; ; ----- ; ASCII Equates ; 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 31 YEAR EQU 86 ; LOCMD EQU 61 ; NUBYE's lowest extended BDOS call HICMD EQU 85 ; NUBYE's highest extended BDOS call ; ; MSPEED values ; 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 ; ; CP/M Plus system variable offsets ; 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 equates ; 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)