TITLE 'PBBS.MAC, version 5.00 October 09/90' ; Filename PBBS50.MAC ; Author Ian Cottrell ; Language Z-80 Assembler ; Last Update Oct 09/90 ; By Ian Cottrell ; The Public Bulletin Board System ; Author: Ian Cottrell ; 44 Lindhurst Cres ; Ottawa, ON, Canada ; K2G 0T7 ; The Information Centre RCP/M ; (613) 952-2289 (300/1200/2400) ;------------------------------------------------------------------ ; PBBS represents many hours of design, coding and debugging. ; However, I do not believe in SHAREWARE. So, if you find ; PBBS of value to you and wish to pay for it in some way, ; please consider a small contribution ($50 suggested) to your ; local Cancer Society in both of our names (thus creating a ; new class of software - charityware!). Then send a little ; note telling me what you did and we will both feel good! In ; any case, please share this program with others, it is free ; and no form of remuneration may be accepted by anyone except ; its author. ; Version 1.00 - March 1st 1986 - initial public release ; Version 2.00 - April 6th 1986 ; Version 3.00 - June 15th 1986 ; Version 4.00 - November 1st 1987 ; Version 4.10 - April 15th 1988 ; Version 4.50 - April 17th 1989 ; Version 5.00 - October 9th 1990 ;------------------------------------------------------------------------------ ; This is the signon/mail module for the PBBS (RCP/M) programs. ;------------------------------------------------------------------------------ INCLUDE PBBSEQU.HDR ; PBBS configuration equates IF M80 .Z80 ; Needed for M80 IF LINK80 CSEG ELSE ASEG ORG 100H ENDIF ; LINK80 ENDIF ; M80 JP START ; Jump around header and data Z3IDEN: DB 'Z3ENV' ; Z3 identifier Z3TYPE: DB 1 ; Z3 environment (external) Z3EADR: DW 0000 ; Z3 environment (address) Z3LOAD: DW 0000 ; Z3 load address ; Name, date, version and level VNAME: DB 'PBBS ' ; Name DB 'Release v ',0 ; Status VER: DB 5 ; Version VERR: DB 00 ; Revision VERDAT: DB 90 ; Year DB 10 ; Month DB 09 ; Day ; My credit! (Turn it on or off but please leave it here!) AUTHOR: DB CR,LF,' by: Ian Cottrell',CR,LF,0 INCLUDE PBBSDB.HDR ; PBBS configuration strings, etc INCLUDE BDOSHDR.MAC ; Time and date conversions ;----------------------------------------------------------------------------- ; Special optional OXGATE BBS password decoder and updater. Only used ; if OXGATE = YES in PBBSEQU.HDR. ;----------------------------------------------------------------------------- IF OXGATE OXPWD: LD HL,0 LD IX,INBUF LD D,0 LD E,(IX) LD B,4 CALL ADDOX LD D,0 LD E,(IX+1) LD B,8 CALL ADDOX LD D,0 LD E,(IX+2) LD B,12 CALL ADDOX LD D,0 LD E,(IX+3) LD B,16 CALL ADDOX PUSH HL LD HL,PSWRDF LD A,(HL) LD C,A INC HL LD A,(HL) LD B,A POP HL SBC HL,BC RET NZ LD HL,INBUF LD DE,PSWRDF LD BC,10 LDIR XOR A RET ADDOX: ADD HL,DE DJNZ ADDOX RET ENDIF ; OXGATE ;------------------------------------------------------------------------------ ; Start of program ;------------------------------------------------------------------------------ START: LD (CCPSTK),SP ; Save old stack LD SP,STACK ; and set up local one LD E,241 ; Now make sure that BYE is present LD C,BEXIST ; using one of the BYE CALL SPBDOS ; extended BDOS calls CP 77 ; Is it there? JR Z,CNTINU ; Yup, carry on CALL PRINT ; Else, tell user, then exit DB 'BYE not present. Please use the "E" option to test PBBS.' DB CR,LF,0 LD SP,(CCPSTK) RET CNTINU: LD A,0CDH ; 'call' opcode LD (0),A ; Disable ^C for BYE5xx ; Call to GETTIM at this time does nothing. It is here so that the user ; may use GETTIM to perform custom routines and any patching that is ; required by his specific system. CALL GETTIM ; Run of subroutine to get logon time CALL ENDPBS ; Returns HL = first byte after program LD (HL),-1 ; Flag byte for message routines INC HL ; This is start of the message storage buffer LD (MSG),HL ; Message entry buffer pointer LD DE,MSGBUF+128 ; Messages must be multiples of 64, so ADD HL,DE ; this become the start of general buffer LD (HL),0 ; Terminator for buffer INC HL LD (MSGARR),HL ; Save message array address LD E,SYSDRV ; Log onto the drive LD C,LOGDRV ; that holds the system files CALL SPBDOS LD E,SYSUSR ; Now the user area LD C,LOGUSR CALL SPBDOS ; Get index info from disk and store it in memory for later. CALLER: CALL IOPEN ; Open index file CALL GET ; Get record ; Get the day and move it to SYDAYF and re-write from RNDBUF LD A,(BDATE+1) ; Logged on day LD (SYDAYF),A LD HL,0 ; Record #1 CALL PUT ; Update index file CALL CLOSE ; Index file LD HL,IDATEF ; Move from buffer LD DE,IDATE ; to storage LD BC,NDXLEN ; This many bytes LDIR ; Move record ; Clear some flags to insure proper state. XOR A LD (NUSR),A ; Clear new user flag LD A,TRIES ; Set number of tries INC A ; for GETNME LD (TRIED),A ; to maximum allowed ; Check if we are to get caller's info or go to the mail section. ; Mail is entered by simply loading signon and checking the re-enter ; byte (REENTR), so BYE must reset REENTR to 0 when the caller leaves. ; (This can also be done by a 'shadow' file called BYE that updates and ; then runs the real BYE). LD A,(REENTR) ; See if signed on already OR A ; 1=yes, 0=no (0 set by exit prog) JR Z,SSTATE ; If not, set the state of the prog. for logon LD A,1 ; Else, set ALTERNATE file flag LD (NOFILE),A ; Now run mail JP PBBS ; Clear some flags to insure proper state. SSTATE: LD (UPLOADS),A ; Clear upload, LD (DNLOADS),A ; download LD (TWIT),A ; and twit flags IF ZCPR3 LD HL,(ZWHL) ; Wheel byte address LD (HL),A ; Reset wheel byte ELSE LD (WHEEL),A ENDIF ; ZCPR3 LD A,'2' LD (ASCACC),A ; Set ASCII access to level 2 ; Check whether hard-copy wanted (function handled by BYE). LD A,(HRDLOG) ; See if hard-copy wanted (in index file) LD E,A LD C,HARDON ; Set BYE hardlog status flag CALL SPBDOS ; 1=yes, 0=no (store in BYE) ; Get today's date and place it in index area. CALL GETTIM ; Get date and time from BYE LD HL,TIME LD DE,LOGSTR LD BC,8 LDIR LD IY,BDATE ; Get clock date LD IX,IDATE ; Get last known date CALL DATDIF ; Check for clock accuracy RL H ; Move flag from [H] to carry SBC A,A ; Extend flag through [A] LD (BADCLK),A ; If non-zero, the clock is screwy JR NZ,WELC ; so keep the old date in index MVDATE: LD HL,BDATE ; Point to binary date LD DE,IDATE ; And date storage area LD BC,3 ; 3 bytes LDIR ; Move 'em ; Welcome wanted, so type it (if present). The type routine used does not ; use the random file buffer(s), but has it's own sequential read routine ; and uses the default DMA buffer at 080H. WELC: IF WELON LD HL,WELCOM CALL TYPE ; Display the text file ENDIF ; WELON ; Check for version number and credits print. VCSTRT: IF VERSION CALL PVER ; Go print program name, date and version ENDIF ; VERSION START0: IF CREDITS LD HL,AUTHOR ; Point to message CALL PRINTM ENDIF ; CREDITS XOR A ; Clear the TWIT flag in case he LD (TWIT),A ; made a mistake and is here to fix it ; Get caller's name. Name must be in form: first last or first;last. ; Initials are not permitted. There is a check done for extra spaces or ; any other nonsense that makes it more difficult to deal with later. GETNME: LD A,(TRIED) ; Get attempts DEC A ; Less one JP Z,PASOUT ; Too many attempts LD (TRIED),A ; Still some left so store new value CALL PRINT DB CR,LF,'Enter your FIRST & LAST names: ' DB CR,LF,'_____________________________<<--',CR,0 LD B,29 ; Input length (insure 1 null) LD C,20H ; Force caps (C=0 for no caps) XOR A ; Echo on LD (COUNT+1),A ; Clear MSB of character count LD D,A ; No word-wrap CALL INPUT JR Z,GETNME LD (COUNT),A ; The following section allows the Sysop to sign on as "Sysop". It then ; goes to the PBBSHDR.MAC file and finds his name under the SYSOP label ; and uses that name as though he had typed it in the first place. It ; still requires his normal password. If however you feel this weakens ; the security, go back to PBBSEQU.HDR and set SYSOK to NO. IF SYSOK PUSH HL ; See if it's the Sysop LD B,5 LD DE,SYSSTR CALL MATCH JR NZ,ZSYS3 ; No, continue LD A,(HL) ; Get character CP ';' ; Did sysop supply password LD (CNTTMP),A ; Set semi-colon flag JR NZ,NOPD2 ; If no semi-colon, no password entered INC HL ; Point to password in input buffer LD DE,PWDBUF ; Point to password buffer LD BC,10 ; Move password and trailing nulls LDIR ; Move password to temp buffer NOPD2: LD B,0 ; Setup character counter LD DE,SYSOP ; Point at Sysop's name ZSYS1: LD A,(DE) OR A JR Z,ZSYS2 ; Until first null INC B INC DE JR ZSYS1 ; Keep counting characters in name ZSYS2: LD A,B LD (COUNT),A ; Save character count POP DE ; Get input buffer address in DE PUSH DE LD HL,SYSOP ; And Sysop's name address LD A,(COUNT) ; Get length of sysop name LD C,A ; Place in BC LD B,0 LDIR ; And move it LD A,(CNTTMP) ; Get semi-colon flag OR A ; Is it set? JR Z,ZSYS3 ; If not set, skip password move LD (DE),A ; Else, place semi-colon at end of name INC DE ; Advance pointer LD HL,PWDBUF ; Point to password buffer LD BC,10 ; Set for 10 character move LDIR ; Move password to input buffer ZSYS3: LD A,0 ; Zero semi-colon flag LD (CNTTMP),A ; and save POP HL ENDIF ; SYSOK LD C,1 ; Assume one character LOOP: LD A,(HL) ; Get character OR A JP Z,NMERR CP ';' JR Z,GOTSEP CP ' ' JR Z,GOTSEP CP 027H ; It's a "'" for O'BRIAN JR Z,LOOP1 ; Let it by CP '-' ; For Mary JONES-SMITH JR Z,LOOP1 ; It's her fault she's a feminist... CALL CHKAZ ; Make sure that character is alpha LOOP1: INC HL INC C ; Bump character count JR LOOP GOTSEP: LD A,C ; A=count CP 2 ; Ensure at least two char before delimiter JP C,NMERR LD A,',' LD (HL),A ; Make separator a comma LD (SPACE),HL ; Save address for later INC HL ; Reset pointer to character after delimiter FNDREC: LD A,(HL) ; Get first char of last name CALL CHKAZ ; Check that first char is alpha PUSH HL CALL HASH ; Calc starting pos'n in users file LD (HSHREC),HL ; Set the hashed record number POP HL LD C,1 ; Set counter LOOP00: LD A,(HL) ; Loop again for second name OR A ; At end? JR Z,GOTEND ; If so, go look for name CP '.' ; Else, is period for St.Amant (or St. Amant)? JR Z,LOOP02 ; If so, go set period flag and continue CP ' ' ; Else, is space (after . in St. Amant)? JR Z,LOOP03 ; If so, go check it out CP 027H ; Else, is a "'" for O'BRIAN? JR Z,LOOP01 ; If so, let it by CP '-' ; Else, is hyphon for Mary JONES-SMITH? JR Z,LOOP01 ; It's her fault she's a feminist... CP ';' ; Else, is semi-colon (password to follow)? JR Z,SEP2 ; If yes, go find name and check password CALL CHKAZ ; Else, check if alpha character LOOP01: XOR A ; Clear the period flag LOOP02: LD (CNTTMP),A ; Save period status INC HL ; Point next INC C ; and bump count JR LOOP00 LOOP03: LD A,(CNTTMP) ; Get period flag OR A ; Was last character a period? JR Z,NMERR ; Nope, so error PUSH HL ; Else, make sure it wasn't a single initial DEC HL DEC HL DEC HL LD A,(HL) ; This had better be an alpha character CALL CHKAZ ; Check if alpha character POP HL ; Ok, restore pointer JR LOOP01 ; Clear period flag and continue SEP2: LD (CNTTMP),A ; Set semi-colon flag PUSH BC ; Save counter PUSH HL ; and pointer INC HL ; Point to start of password LD DE,PWDBUF ; Move password LD BC,10 ; to temporary LDIR ; buffer POP HL ; Restore pointer LD (HL),0 ; Set PUSH HL ; rest POP DE ; input INC DE ; buffer LD BC,15 ; to LDIR ; nulls POP BC ; Restore counter GOTEND: LD A,C ; Get last name counter CP 2 ; Last name needs more than 1 charaacter JR C,NMERR ; Name entered incorrectly ; Got a valid name (first;last or first last), so store it with ; a "," instead of the space or ; in LASTCALR file buffer. LD DE,INBUF ; Point to start of input buffer PUSH DE ; Save for later PUSH DE ; and later still OR A ; Clear carry flag SBC HL,DE ; Calculate length of name in [HL] PUSH HL ; Save counter on stack for later LD C,LCDATA ; Get BYE's last caller buffer pointer CALL SPBDOS POP BC ; Restore length as counter EX DE,HL ; Make last caller buffer the destination POP HL ; Restore buffer pointer (INBUF) LDIR ; Move name to last caller buffer LD HL,ENDSTR ; Point to CR/LF/00/1AH string LD BC,4 ; and move to LDIR ; last caller buffer LD HL,(SPACE) ; Get delimiter position in buffer LD (HL),' ' ; Make it a space POP HL ; Restore buffer pointer (INBUF) LD DE,UNAME LD BC,30 ; Move name to storage LDIR JR SEEK ; Now we see if we can find caller in file ; Check to see that the character in A is alpha (A-Z). CHKAZ: CP 'A' ; Check if less than 'A' JR C,CKAZ1 ; If so, it's a bad character CP 'Z'+1 ; Check if greater than 'Z' RET C ; Return if good CKAZ1: POP HL ; Else, clean return address off stack ; and fall through to NMERR ; If neither space nor ; found, then probably didn't enter two names ; or entered initial, period or none A-Z character. NMERR: CALL PRINT DB CR,LF,'FIRST & LAST name required.',CR,LF DB 'No numbers, initials or middle names.',0 JP GETNME ; Open users file for search, initialize arguments. SEEK: LD HL,-1 LD (TMPREC),HL ; Clear flag for possible entry (deleted user) LD HL,WATMSG CALL PRINTM CALL UOPEN ; Open users file LD HL,(HSHREC) ; Start at calculated hash point ; Loop to read users file and attempt match. RDUSRS: CALL GET ; Users file LD B,30 LD HL,UNAMEF ; Compare the user name to file record LD DE,INBUF CALL MATCH JR Z,FNDUSR ; If match, process LD A,(AVAILF) ; Else, see if free (unused) record OR A JP Z,NUOK ; If so, no match but we've got room LD A,(ACESSF) ; Else, get access level OR A ; Zero (deleted user)? JR NZ,NXTREC ; If no, keep looking LD HL,(TMPREC) ; See if we've already found a deleted record LD DE,1 ADD HL,DE ; If HL=FFFF, will set carry JR NC,NXTREC ; Yup, so just keep scanning LD HL,(RRNO1) ; Nope, save this one in case we LD (TMPREC),HL ; need it for a new user NXTREC: LD HL,(RRNO1) ; Get record number INC HL ; Test next one EX DE,HL XOR A ; Clear the carry flag LD HL,MAXU-1 ; See if we are at the SBC HL,DE ; end of the users file JR NC,NREC1 ; Nope, so continue LD DE,0 ; Else, load up first record in file NREC1: LD HL,(HSHREC) ; See if we have XOR A ; have checked SBC HL,DE ; the whole users EX DE,HL ; file with no match JR Z,NOSPC ; Yup, so see if we have room for new users JR RDUSRS ; Else, keep going ; Found user, so process info. FNDUSR: LD A,(ACESSF) ; Access level DEC A ; Banned? JR Z,BAN ; If user is deleted, this is caught later LD HL,(RRNO1) LD (TMPREC),HL ; Set record number JP PASS ; User access=1 means banned from access, so dump. BAN: LD HL,TWITMSG CALL PRINTM LD IY,TWITIN ; Point to twit log message. JP LOGOUT ; Log him out but record his try. ; Couldn't find name in file, and there are no free entries. ; Make sure he spelled it right, then dump him if so. NOSPC: LD HL,(TMPREC) ; See if we had LD DE,1 ; a deleted record ADD HL,DE ; to fill JR C,NOSPC1 ; If not, check if new user and dump if so LD HL,(TMPREC) ; Else, get record to use LD (RRNO1),HL JR NUOK NOSPC1: CALL CLOSE LD HL,NONME ; No, see if spelled okay CALL PRINTM ; Ask question TYPOW: CALL YESNO ; Get the answer JP Z,VCSTRT ; If not new user, get his name again DEC A JR NZ,TYPOW ; When the first user signs off after midnight, the users file will be ; purged, possibly freeing up new user space. FULL: CALL PRINT DB CR,LF,'Sorry, Users file is FULL. ' DB 'Please try again tomorrow.',0 JP 0000 ; <== LET BYE HANDLE IT ; Space available, so get his information for storage. NUOK: LD HL,(RRNO1) LD (TMPREC),HL ; Set record number, saved from scan LD HL,NONME CALL PRINTM TYPOW1: CALL YESNO ; Get an answer JP Z,VCSTRT ; If not new user, get his name again DEC A JR NZ,TYPOW1 NERD: IF PRVATE LD A,PLOGUSR ; Are we allowing them to register OR A JR NZ,CP ; Yup, we'll catch 'em later ENDIF PREXIT: IF PRVTXT AND PRVATE LD HL,PRVTFIL ; Get the text file explaining CALL TYPE JP 0000 ; Let BYE handle it ENDIF IF (NOT PRVTXT) AND PRVATE LD DE,PRVTCM ; Load the exit file JP CPM ENDIF ; NOT PRVTXT AND PRVATE ; Find from where user is calling (new users only). CP: CALL PRINT DB CR,LF DB 'From which City and Province/State are you calling?' DB CR,LF DB 'Please enter the full name of your city followed' DB CR,LF DB 'by a 2 character province or state abbreviation.' DB CR,LF DB 'For example: Ottawa ON or Port Moody BC.' DB CR,LF,'____________________<<--',CR,0 CALL EATCHR CALL EATCHR LD B,20 ; Length LD C,20H ; Caps XOR A ; Echo on LD D,A ; No word-wrap CALL INPUT ; Get location JP Z,CP LD B,A ; Length of input string to B as counter PUSH HL ; Save pointer PUSH BC ; and counter LD DE,CITST+1 ; Clear buffer to nulls LD HL,CITST LD BC,20 ; Buffer is 20 bytes long LD (HL),0 LDIR POP BC ; Restore counter POP HL ; and pointer LD DE,CITST ; Move to correct location while we're at it ISCOMA: LD A,(HL) CP ',' ; No commas in users file JR NZ,ISSLSH LD A,' ' ; Make it a space for PRINTN routine ISSLSH: CP '/' ; Some people use '/' as separator JR NZ,ISPERID LD A,' ' ; This also becomes a space ISPERID:CP '.' ; Is a period like St. John's? CALL Z,PERFIX ; If so, make sure it's followed by a space NOCOMA: CP ' ' JR NZ,CLRSPC LD A,(TSPACE) OR A LD A,' ' JR NZ,NOSAVE LD (TSPACE),A JR OKSAVE CLRSPC: PUSH AF XOR A LD (TSPACE),A POP AF OKSAVE: LD (DE),A INC DE NOSAVE: INC HL DJNZ ISCOMA DEC HL ; If user entered DEC HL ; a 2 character DEC HL ; province/state LD A,(HL) ; then this should CP ' ' ; be a space JR Z,DOPHN ; If so, continue CP ',' ; A comma will also do JR Z,DOPHN CITERR: CALL PRINT ; Else, tell him and start again DB CR,LF DB 'You must enter both a city and a 2 character province/state!' DB CR,LF,0 JP CP ; Do it all over again PERFIX: LD (DE),A ; Store the period INC DE ; Point to the next location LD A,' ' ; Next location MUST be a space RET ; Regular routine will handle from here DOPHN: CALL PHN ; Get and check his phone number CALL PRINT DB CR,LF,LF,'You are: ',0 LD B,30 LD HL,UNAME CALL PRINTN CALL PRINT DB CR,LF,'Phone #: ',0 LD HL,PHONE LD B,12 CALL PRINTL CALL PRINT DB CR,LF,' From: ',0 LD B,21 LD HL,CITST CALL PRINTN CALL PRINT DB CR,LF,'Is this correct (Y/N)? ',0 WAIT: CALL YESNO ; Get an answer JP Z,VCSTRT ; If no, go get it all again DEC A ; Else, was yes? JP Z,NULLQ ; If so, continue JR WAIT ; Else, go get another answer PHN: CALL PRINT DB CR,LF DB 'Enter the Area Code and Number at which the Sysop',CR,LF DB ' may call you for validation, if necessary:' DB CR,LF,0 PHN2: CALL PRINT DB CR,'___-___-____<<--',CR,0 LD B,12 ; Length LD HL,PHONE ; Point to input buffer PHN1: PUSH BC ; Save counter CALL GETCH ; Get input POP BC ; Restore counter CP BS ; Backspace? JR Z,BACKUP ; If so, go do it CP '0' ; Else, ASCII zero? JR C,PHN1 ; If less, get another CP '9'+1 ; Else, ASCII nine? JR NC,PHN1 ; If more, get another LD (HL),A ; Else, put character in buffer CALL ECHO ; and print it on screen INC HL ; Advance pointer DEC B ; Decrement counter LD A,B ; Counter to [A] CP 9 ; Counter at 9? JR Z,PDASH ; If so, do dash CP 5 ; Else, counter at 5? JR Z,PDASH ; If so, do dash CP 0 ; Else, all done? JR NZ,PHN1 ; If not, loop for another character JR GOTCR ; Else, go check if number is valid BACKUP: LD A,12 ; Is counter CP B ; already at 12? JR Z, PHN1 ; If so, cannot backspace LD A,BS ; Else, get a backspace CALL ECHO ; Print it LD A,B ; Counter to [A] DEC HL ; Adjust the buffer pointer INC B ; and the counter CP 8 ; Counter at 8? JR Z,BCKUP1 ; If so, accomodate the dash CP 4 ; Else, counter a 4? JR NZ,PHN1 ; If not, loop for another character BCKUP1: DEC HL ; Adjust the buffer pointer INC B ; and the counter again LD A,BS ; Get a backspace CALL ECHO ; Print it JR PHN1 ; Loop for another character PDASH: LD A,'-' ; Get a dash CALL ECHO ; Print it on screen LD (HL),A ; Insert the dash into the buffer INC HL ; Adjust buffer pointer DEC B ; and counter JR PHN1 ; Keep going GOTCR: LD A,(PHONE+1) ; Get middle digit of area code CP '2' ; Must be '0' or '1' JR NC,TWITY1 ; If not, set user access level to 'TWIT' LD DE,CHKTBL ; Else, check table of phone # as area codes LD B,CTABLN ; Get number of entries in table CHKLP: LD HL,PHONE ; Point to input area code PUSH BC ; Save counter PUSH DE ; Save starting point for pointer LD B,3 ; Number of characters to check CALL MATCH ; Check this entry JR Z,TWITY ; If match, set him to TWIT POP HL ; Restore starting point for pointer LD DE,3 ; Next area code is 3 bytes further ADD HL,DE ; Point to right area code PUSH HL ; Transfer pointer POP DE ; to [DE] POP BC ; Restore counter DJNZ CHKLP ; Else, loop until done LD DE,CHKTBL ; Now check the same numbers as exchanges LD B,CTABLN ; Get number of entries in table CHKLP1: LD HL,PHONE+4 ; Point to input exchange PUSH BC ; Save counter PUSH DE ; Save starting point for pointer LD B,3 ; Number of characters to check CALL MATCH ; Check this entry JR Z,TWITY ; If match, set him to TWIT POP HL ; Restore starting point for pointer LD DE,3 ; Next area code is 3 bytes further ADD HL,DE ; Point to right area code PUSH HL ; Transfer pointer POP DE ; to [DE] POP BC ; Restore counter DJNZ CHKLP1 ; Else, loop until done PHNRET: RET ; User entered an invalid phone number, so let the system know. TWITY: POP DE ; Stack POP BC ; housekeeping TWITY1: LD A,1 ; Set LD (TWIT),A ; Twit flag RET DASH: CALL PRINT DB CR,LF,'Please use numbers only.',0 JP PHN1 PSL6: CALL PRINT DB CR,LF,'Must be at least 4 characters.',0 JP PS PSL7: CALL PRINT DB CR,LF,'Password cannot start with a space.',0 JP PS NULLQ: CALL PRINT DB CR,LF,'How many nulls do you need (0-9, usually 0)? ',0 LD B,1 XOR A LD D,A LD C,A CALL INPUT JR NZ,NULL1 LD A,'5' ; entered, so default to 5 LD (HL),A ; Put it in buffer LD A,1 ; Number of characters for convert routine NULL1: CALL CNULL IF NOT MDOS LD HL,DRV0 ; Point to default DU: RRD ; Get drive DEC A ; Normalize INC HL ; Point to user area RLD ; Rotate drive into place LD A,(HL) ; Now get DU ELSE DUSET: LD A,(DUSET3) OR A JR NZ,DUSET1 CALL PCRLF1 DUSET1: CALL PRINT DB CR,LF,LF DB 'The following MENU gives a list of operating systems.' DB CR,LF DB 'Please select your main area of interest' DB CR,LF,LF,0 LD B,0 ; Initialize drive counter LD HL,DU0 ; Default CP/M drive CALL DRMENU ; Display menu selection IF (NMDOS GT 0) ; Show other selections if MDOS LD HL,DU1 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 1) LD HL,DU2 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 2) LD HL,DU3 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 3) LD HL,DU4 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 4) LD HL,DU5 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 5) LD HL,DU6 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 6) LD HL,DU7 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 7) LD HL,DU8 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 8) LD HL,DU9 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 9) LD HL,DU10 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 10) LD HL,DU11 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 11) LD HL,DU12 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 12) LD HL,DU13 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 13) LD HL,DU14 ; Drive identification CALL DRMENU ; Display menu selection ENDIF IF (NMDOS GT 14) LD HL,DU15 ; Drive identificattion CALL DRMENU ; Display menu selection ENDIF CALL PCRLF1 CALL PRINT DB CR,'Enter Selection: ',0 ; The following JP was placed here to allow the insertion of the ; following subroutine. JR DUSETA DRMENU: PUSH HL ; Save pointer to drive id LD A,B UTEN: CALL PS2ASC ; Display drive counter INC B ; Advance counter CALL PRINT DB '. ',0 POP HL ; Restore pointer to drive id CALL PRINTM ; Display drive identification CALL PCRLF RET DUSET2: CALL DOBS ; Erase 2 invalid CALL DOBS ; characters DUSETA: CALL GET0F ; Get selection CP NMDOS+1 ; Is drive available? JR C,DUSETC ; If yes, process it JR DUSET2 ; Else, clear one char & start over DUSETC: ADD A,A ; Double number LD C,A ; Put it LD B,0 ; into BC LD HL,DRV0 ; Point to start of table ADD HL,BC ; Point to correct entry GDCHR: RRD ; Get drive DEC A ; Normalize INC HL ; Point to user area RLD ; Rotate drive into place LD A,(HL) ; Now get DU ENDIF ; MDOS LD (INTAR),A ; Save drive/user DUSET3: NOP ; Can be filled in with a "RETURN" ; Set the number of terminal lines and the desired line length. LD HL,PS PUSH HL CALL LENCHG ; Get number of terminal lines CALL LINCHG ; Get length of each line LD A,TRIES INC A LD (TRIED),A ; Allow selection of password. PS: LD A,(TRIED) DEC A JP Z,PASOUT LD (TRIED),A CALL PRINT DB CR,LF,'Select a password (4-10 characters):' DB CR,LF,'__________<<--',CR,0 LD B,10 ; Length LD C,20H ; Caps XOR A ; Echo on LD D,A ; No word-wrap CALL INPUT ; Get password CP 4 ; Must be a minimum of 4 characters long JP C,PSL6 ; Too short, tell him and ask again LD A,(HL) ; Check for a space CP ' ' ; as first character JP Z,PSL7 ; Oops, tell him and ask again LD DE,PSWRD ; Is ok, save it in buffer LD BC,10 LDIR CALL PRINT DB CR,'Please remember it.',CR,LF,0 ; Set values for new user into place. LD HL,0 ; Clear LD (TMSON),HL ; times on LD (DNLDS),HL ; number of downloads LD (UPLDS),HL ; number of uploads XOR A ; Reset LD (MFLAG),A ; mail waiting flag LD (MFTMP),A ; new mail waiting counter LD (TCODE),A ; terminal code (none selected yet) LD (TOTME),A ; time used so far today INC A ; Set LD (ACESS),A ; access level (gets bumped later) LD (NUSR),A ; new user LD (AVAIL),A ; record used LD HL,(IUSER) ; Update INC HL ; system LD (IUSER),HL ; user count LD HL,WATMSG CALL PRINTM IF TCAP CALL PRINT DB CR,LF,'Please select a Terminal from the following menu(s)' DB CR,LF,0 XOR A ; Request CALL TCSET ; terminal select LD (TCODE),A ; Store terminal code ENDIF ; TCAP LD HL,(IUSRC) ; Get start record number ; No free space, so set this guy into old deleted user's space. FNDFRE: JP UPDATE ; Update index and user files ; If existing user, we end up here to get password (3 tries). PASS: LD A,(ACESSF) ; Found name but first check if OR A ; this guy has been deleted once JR NZ,PASS1 INC A ; Be nice and let him back in LD (ACESSF),A ; Gets bumped later LD (NUSR),A ; But make him a new user PASS1: LD A,(CNTTMP) ; Get semi-colon flag OR A ; Set? JR Z,PASS2 ; If not, go get password input LD HL,PWDBUF ; Else, point to temporary buffer LD DE,PSWRDF ; Match to password on file LD B,10 CALL MATCH JR Z,PS3A ; If match, let him in PASS2: LD A,TRIES ; Else, get tries allowed INC A LD (TRIED),A ; Set countdown ; Now get the password. PS2: LD IY,BREKIN ; Point to the message LD A,(TRIED) DEC A JP Z,LOGOUT ; Too many tries LD (TRIED),A CALL PRINT DB CR,' ',CR,'Password: ',0 LD B,10 ; Length LD C,20H ; Caps XOR A ; Set echo off LD D,A ; No word-wrap CALL SPINP OR A JR Z,PS2 LD DE,PSWRDF ; Match to password on file LD B,10 CALL MATCH IF OXGATE CALL NZ,OXPWD ; Go try to decode the password if OXGATE ENDIF ; OXGATE JP NZ,PASERR CALL PRINT DB ' Thank You.',0 PS3: LD HL,WATMSG CALL PRINTM PS3A: CALL UMOV ; Move user info to memory LCHK: LD A,(LENG) ; Get terminal line length OR A ; Already set? JP NZ,TCHK ; If so, go chec terminal lines CALL LINCHG ; Else, go set the screen width JR TCHK LINCHG: CALL PRINT DB CR,LF,LF DB 'Enter your preferred screen width (39-78 cols): ',0 CALL GETS2 ; Get two numbers & convert to binary CP 39 ; See if less than minimum width JR NC,LNCH00 ; If so, go check maximum LD A,39 ; Else, set to minimum LNCH00: CP 78 ; See if less than 78 JR C,LINCH1 ; If yes, handle normally LINCH0: LD A,78 ; Maximum CRT width (and buffer size) LINCH1: LD (LENG),A LD (LENGF),A RET TCHK: LD A,(TLIN) ; Get number of terminal lines OR A ; Already set? JR NZ,PCHK ; If so, go check phone number CALL LENCHG ; Else, go get # of lines on terminal JR PCHK ; Now check phone number LENCHG: CALL PRINT DB CR,LF,LF DB 'Enter your preferred screen length (16-66 lines): ',0 CALL GETS2 ; Get two numbers & convert to binary JR Z,TRMCH1 ; entered, default to 24 CP 16 ; See if less than minimum length JR NC,TRMCH0 ; If not, go check maximum LD A,16 ; Else, set to minimum TRMCH0: CP 67 ; See if less than 66 JR C,TRMCH2 ; If so, handle normally TRMCH1: LD A,24 ; Else, default to 24 lines TRMCH2: LD (TLIN),A ; Update LD (TLINF),A ; user record RET ; Get 2 ASCII digits input by the user and convert them to binary. Binary ; value returned in [A]. GETS2: LD B,2 ; Two characters to be input XOR A ; Clear LD C,A ; case flag LD D,A ; word wrap flag CALL INPUT ; Go get characters RET Z ; If nothing entered, all done LD (CNVRT0+1),A ; Else, set number of chars to convert PUSH HL ; Move pointer POP IX ; to [IX] CALL CNVRT0 ; Go convert ASCII to binary LD A,L ; Get the converted value OR A ; Force to return non-zero RET ; Now check for phone number in user profile and ask for number ; if area code is 000. This will occur because of conversion from ; MBBS or RBBS where phone numbers are not used or a user entered ; by the Sysop using PMAINT. PCHK: LD HL,PHONE ; Point to user's phone number LD DE,CHKTBL ; Point to zero entry in phone table LD B,3 CALL MATCH ; Is area code all zeroes? JR NZ,SETDRV ; If not, continue CALL PHN ; Else, get a phone number from user ; Checks to see if drive/user is default A0: If yes, puts a "RET" in ; SETDRU: routine, jumps there to set the drive/user. While there, also ; checks to see if a special area for MS-DOS. If yes, then asks for the ; user's preference and stores that in the "INTAR" location. Once this ; has been requested, doesn't need to ever ask again. SETDRV: IF MDOS LD A,(INTAR) ; Present drive/user default A0: ? OR A JR NZ,UPDATE ; If not, already set, exit LD A,RET ; Make DUSET: a subroutine LD (DUSET3),A CALL DUSET ; Set requested entry drive/user area ENDIF ; MDOS ; Valid user (new or existing), so update all elements of files. UPDATE: LD A,(BADCLK) ; See if we have a bad clock OR A JR NZ,NOCLK ; Yup, don't change user date LD IX,LASTON LD IY,IDATE ; See if same day as last day on-line CALL DATDIF ; and if not, then 0 out total time today LD A,H ; See if same day (0 difference) OR L JR Z,NOREST ; No time-total reset NOCLK: XOR A LD (TOTME),A ; Reset byte to 0 for a new day ; Now we have to run through code to check for auto-bump. ; Code to bump user access level if today is not the same as last ; day on. Bumps a 2 to a 3, a 3 to a 4, etc until the value of BUMPHI: ; is reached. Levels above BUMPHI must be set by Sysop, using maintenance ; routines. No bump done if DOBUMP flag is not set. NOREST: IF DOBUMP LD A,(ACESS) CP 2 JR C,NEWU ; If access < 2, then check for new user CP BUMPHI JR NC,BMPDON ; If access >= BUMPHI, then no auto-bump ; Potential is here for a bump. LD IY,IDATE ; Today's date LD IX,LASTON ; Get last date on here CALL DATDIF ; See if this is same day as last call LD A,H OR L ; 0? JR Z,BMPDON ; Same day so skip next JR BLEV ; Go bump the level ENDIF ; DOBUMP NEWU: LD A,(NUSR) ; Is he a DEC A ; new user? JR NZ,BMPDON ; If not, TWITS stay TWITS (ain't it true!) BLEV: LD HL,ACESS ; Else, point to access level INC (HL) ; Bump 2 -> BUMPHI LD A,(HL) ; Get access level in [A] LD HL,USMP2 ; Point to start of map table SUB 2 ; Normalize access level, zero relative RLCA ; Multiply level x 2 RLCA ; x 4 LD E,A ; Move to 'E' LD D,0 ADD HL,DE ; HL now points to correct user map LD DE,USRMP ; Point to user map in user record LD BC,4 ; Bytes to move LDIR ; Move 'em BMPDON: LD A,1 ; Set flag, indicate signed-on LD (REENTR),A ; Set the bit maps in low memory (not necessarily consecutive) LD HL,(USRMP) ; Move user area bit map from user LD (USRMAP),HL ; record to low memory LD HL,(DRVMP) ; Move drive bit map from user LD (DRVMAP),HL ; record to low memory ; Now update the rest of the info before we write the record. LD HL,(TMSON) ; Bump the number of INC HL ; times on the system LD (TMSON),HL ; by one LD HL,(TMPREC) ; Get user record number LD (USREC),HL ; and move it to 'permanent' storage LD A,(BSPEED) ; Read baudrate this time on and LD (BDCDE),A ; store it in user record LD A,(NNULL) ; Read number nulls needed LD E,A LD C,NULLS ; Set number of nulls CALL SPBDOS ; within BYE IF TCAP LD A,(TCODE) ; Get terminal code LD (TRMCD),A ; and store in low memory ENDIF ; TCAP LD HL,AVAIL LD DE,AVAILF LD BC,USRLEN LDIR ; Move info to buffer LD A,(BADCLK) ; Check for bad clock OR A JR NZ,BCONT LD HL,IDATE ; Set today as last date on in the LD DE,LSTONF ; buffer ONLY, since we need laston LD BC,3 ; for stats printout that comes next LDIR ; Now check to see if he was a twit. BCONT: LD A,(TWIT) ; If he is, this is a 1 OR A JR Z,GOOD ; He's okay, on with the program LD (ACESSF),A ; Make him a TWIT before we write the record GOOD: CALL UPUT ; Update users file LD A,(ACESS) ; Get access value CP PSYSP ; Only real Sysop not logged JR Z,GOOD1 ; If Sysop, don't bump calls ; And then write the index. LD HL,(ICALL) INC HL ; Bump # callers LD (ICALL),HL LD HL,IDATE LD DE,RNDBUF LD BC,NDXLEN LDIR ; Move index info to buffer CALL IOPEN CALL PUT CALL CLOSE ; Now set time and date into LASTCALR file and buffer for other ; programs to access and then we're almost done. GOOD1: LD C,LCDATA ; Get address of BYE's last caller buffer CALL SPBDOS LD DE,RNDBUF LD BC,LSTLEN LDIR ; Move info to random buffer LD HL,LASTCAL ; Point to FCB CALL OPEN ; LASTCALR LD HL,LSTLEN LD (RRSZ1),HL ; Set length of record LD HL,0 CALL PUT ; Write to record 0 CALL CLOSE ; LASTCALR LD E,0 LD C,WRTLOC ; Clear the write lock CALL SPBDOS ; within BYE LD C,LCDATA ; Get address of BYE's last caller buffer CALL SPBDOS EX DE,HL ; Save it LD A,(BSPEED) ; Get baud rate ADD A,'0' ; Will show 'L' if running local LD (DE),A ; Show callers speed INC DE LD A,' ' LD (DE),A INC DE LD HL,TIME ; Point to the time LD BC,5 ; Just show HH:MM LDIR ; Move it into place LD A,' ' LD (DE),A INC DE LD HL,UNAME LD B,30 ; Maximum of 30 characters (stop on 0) NAME: LD A,(HL) OR A JR Z,DNAME LD (DE),A INC HL INC DE DJNZ NAME DNAME: LD A,' ' LD (DE),A INC DE LD HL,CITST ; Now the city and province/state LD B,20 ; Maximum of 20 characters (stop on 0) CITY: LD A,(HL) OR A JR Z,DCITY LD (DE),A INC HL INC DE DJNZ CITY DCITY: LD A,' ' LD (DE),A INC DE LD HL,PHONE PHNE: LD BC,12 LDIR DPHNE: LD A,' ' LD (DE),A INC DE LD A,(LASTON) ; Show last log date as MM/DD CALL BINASC ; Convert binary to ASCII LD (DE),A ; MSN is in [A], LSN in [B] INC DE LD A,B LD (DE),A INC DE LD A,'/' LD (DE),A INC DE LD A,(LASTON+1) CALL BINASC LD (DE),A INC DE LD A,B LD (DE),A INC DE ; Add /YY for WHATSNEW feature LD A,'/' LD (DE),A INC DE LD A,(LASTON+2) CALL BINASC LD (DE),A INC DE LD A,B LD (DE),A INC DE LD A,' ' LD (DE),A INC DE LD A,(ACESS) CALL MKASCI ; Convert to ASCII (1-9 or A-F) LD (DE),A INC DE LD HL,ENDSTR LD BC,3 LDIR ; Now set the 25th status line IF SET25 LD HL,IN25 ; Select the 25th line mode CALL PRLC ; and send it to local console only LD C,LCDATA ; Get address of the LASTCALR buffer CALL SPBDOS ; from BYE (returned in [HL]) CALL PRLC ; Print local console only LD HL,OUT25 CALL PRLC ; De-select 25th line mode ENDIF ; Okay, if he's a twit, get rid of him, never to return. ; Let him leave a comment the first time, good evidence for ; future prosecution. NOSET: LD A,(TWIT) ; Is this a twit? OR A JP NZ,BAN ; If so, get rid of him ; Okay, check if we're a private system allowing registration. If so jump ; to the exit routine, and tell him about it. IF PRVATE LD A,(ACESS) ; See if he's a new user CP 2 JP Z,PREXIT ; Yup, dump him ENDIF ; PRVATE ; Okay, show what's goin' on and enter mail section if ; there is any waiting, else display the main menu. HELLO: IF DSKLOG XOR A LD (DSKFLG),A ; Make sure flag is zero at start LD E,0FFH ; Ask for status LD C,SDSKLOG ; BYE5 DISKLOG status request CALL SPBDOS CP 77 ; 77 says "NO is set in BYE5" JR Z,HELLO0 ; Exit with no change to the flag OR A ; Says DISKLOG is there but turned off JR Z,HELLO0 ; Exit with no change to the flag LD (DSKFLG),A ; DISLOG is in use, so set flag first LD E,0 ; Now turn off the DISKLOG in BYE5 LD C,SDSKLOG ; BYE5 DISKLOG status request CALL SPBDOS ENDIF ; DSKLOG HELLO0: IF WELCUM LD A,(ACESS) ; Don't bother with CP COSYS ; Sysop or co-Sysop JR NC,HELLOA CALL MKASCI ; Make it ASCII LD (WELCOM+6),A ; Rename the welcome file pointer LD HL,WELCOM ; Point to FCB CALL TYPEIF ; Open and display a text file if exists ENDIF ; WELCUM HELLOA: IF BULTIN LD A,(ACESS) ; Don't bother with CP COSYS ; Sysop or co-Sysop JR NC,HELLOB LD HL,BULLETIN ; Point to FCB CALL TYPEIF ; Open and display a text file if exists ENDIF ; BULTIN HELLOB: CALL PCRLF1 ; Scroll 2 lines CALL GETTIM ; Get time and convert to binary LD A,(BTIME) CP 12 ; Is it morning? JR NC,HELLO1 LD HL,GMORN ; Yes, say good morning JR HELLO3 HELLO1: CP 18 JR NC,HELLO2 LD HL,GAFT ; Say good afternoon JR HELLO3 HELLO2: LD HL,GEVE ; Say good evening HELLO3: CALL PRINTM CALL PRINT DB 'it''s ',0 LD HL,TIME CALL PRINTM CALL PRINT DB ' hours ',0 LD HL,TZONE ; Point to time zone CALL PRINTM ; and print it CALL GETTIM ; Get the current date and convert it LD IX,BDATE ; Point to the date (binary) CALL FULDAT ; Print it as dd mmm 19yy CALL PRINT DB '.',CR,LF,LF,'You are caller .....: ',0 LD HL,(ICALL) CALL PB2ASC CALL PRINT DB CR,LF,'System users .......: ',0 LD HL,(IUSER) CALL PB2ASC CALL PRINT DB CR,LF,'Your signons .......: ',0 LD HL,(TMSON) CALL PB2ASC LD A,(NUSR) ; If new user, skip next message OR A JP NZ,HELLO4 CALL PRINT DB CR,LF,'Last signon was ....: ',0 LD IX,LASTON ; Point to date (binary) CALL FULDAT ; Print it as dd mmm 19yy LD DE,XLSTON ; Grab LASTON LD HL,LASTON ; while still LD BC,3 ; valid LDIR HELLO4: IF TCAP LD A,-1 ; Ask TCSET to load the appropriate CALL TCSET ; TCAP record into memory LD (TRMCD),A ; Store terminal number (in case it changed) CALL PRINT DB CR,LF,'Current terminal ...: ',0 IF ZCPR3 LD HL,ZTCAP ; Point to ZCPR3 TCAP terminal name ELSE LD HL,SYSENV+80H ; Point to ZCPR3 TCAP terminal name ENDIF ; ZCPR3 LD B,16 CALL PRINTL ENDIF ; TCAP IF (MSTATS and DSTATS); Start system usage display CALL PRINT DB CR,LF,LF,'Daily Call Count ...........: ',0 LD A,(SYDCC) ; Total daily calls CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB CR,LF,'Average Call Duration ......: ',0 LD A,(SYDCC) ; Total daily calls OR A ; Set flags LD L,A LD H,0 JR Z,NODIV ; Don't bother dividing by zero callers EX DE,HL LD HL,(SYACU) ; Total Daily Minutes CALL DIV16 ; HL / DE = average in HL NODIV: CALL PB2ASC CALL PRINT DB ' min' DB CR,LF,'Current Day System Usage ..: ',0 LD A,(SYCRP) OKPRN: CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB ' % [',0 LD HL,(SYACU) ; Total daily minutes LD DE,60 ; 60 min/hr CALL DIV16 ; HL = total hours, DE = remainder (minutes) PUSH DE ; Hang onto this LD A,L ; Get hours into A CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB ':',0 ; Customary separator POP HL ; Restore minutes to HL LD A,L CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB ']' DB CR,LF,'Previous Day System Usage ..: ',0 LD A,(SYPVP) OKPRN1: CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB ' %',0 ENDIF CALL PRINT DB CR,LF,LF,'Highest SYS. MSG ...: ',0 LD HL,(IMNXT) DEC HL CALL PB2ASC CALL PRINT DB CR,LF,'Highest MSG. read ..: ',0 LD HL,(HIMSG) CALL PB2ASC NSKP0: LD A,(ACESS) ; Access level SUB 2 ; Normalize access level, zero relative LD H,0 ; (levels 0 & 1 never get here) LD L,A ; [HL]=access level LD DE,ACTBLEN ; Length of each entry in table CALL MLDL ; [HL]=offset LD DE,ACC2 ; Start of table ADD HL,DE ; [HL]=address for this access level ; Set ZCPR and BYE values based on access table and user's file. PUSH HL ; Save access table pointer LD A,(HL) ; Get maximum drive IF ZCPR3 LD (ZDRV),A ; Set maximum drive LD HL,ZTYP ; Point to environment type byte BIT 7,(HL) ; Test of extended environment JR NZ,NOTNZ ; If not, skip LD HL,(DRVMP) ; Load the caller's drive map LD (ZDRVM),HL ; Poke it into environment ELSE DEC A ; Normalize LD (MXDRV),A ; Set maximum drive ENDIF ; ZCPR3 NOTNZ: LD E,A ; Get maximum drive INC E ; Normalize for BYE LD C,BMXDRV ; Let BYE set it CALL BDOS POP HL ; Update INC HL ; access table PUSH HL ; pointer LD A,(HL) ; Get maximum user value DEC A ; Normalize IF ZCPR3 LD (ZUSR),A ; Set maximum user ELSE LD (MXUSR),A ; Set maximum user ENDIF ; ZCPR3 LD E,A ; Get maximum user area LD C,BMXUSR CALL BDOS ; Let BYE set it POP HL ; Restore access table pointer INC HL ; Point to minutes allowed on-line LD A,(HL) ; Get value OR A ; If A = 0, unlimited time JR Z,STOTME ; Skip time check ; This code checks to see if the user has any time left today. ; NOTE: the update of the time used so far on a day is done by ; PBYE.MAC. If you are not using BYE, be sure to set ; TOT in PBBSHDR to 0 to skip this code. IF TOT LD A,(TOTME) ; Get total time on-line today LD B,A LD A,(HL) ; Get max time allowed per day DEC A ; Adjust to make compare easier SUB B ; See if total used exceeds max allowed JR C,OVRLIM ; If over then drop INC A ; Re-adjust time allowed JR STOTME ; and go set it OVRLIM: CALL PRINT DB CR,LF,LF,'You have exceeded the daily maximum time allowed.' DB CR,LF,'Please call back tommorrow.....',CR,LF,0 JP 0000 ; Let BYE handle it ENDIF ; TOTal time check NOTOT: LD A,(HL) ; Get minutes allowed on-line STOTME: PUSH HL ; Save access table pointer LD E,A LD C,MXTIME CALL BDOS ; Set BYE's max time allowed POP HL ; Restore access table pointer INC HL ; Days to deletion INC HL ; Upload/download ratio IF XMACC LD A,(HL) LD (XMRTIO),A ENDIF ; XMACC LD A,(ACESS) ; Access level LD E,A ; Set sleepy caller time out = ACESS LD C,TOVAL CALL BDOS ; Set BYE's sleepy caller time out LD (LOCK),A ; and load it for restricted .COM files etc. ; PBBS doesn't use this but you might ADD A,ASCII ; Add in ASCII offset (make it an ASCII char.) LD (ASCACC),A ; and place in ASCII access loc'n in low mem CALL PRINT DB CR,LF,'Time allowed .......: ',0 LD E,255 ; Get maximum time LD C,MXTIME ; allowed on sytem CALL BDOS ; from BYE OR A ; Set flags JR NZ,STOT2 ; If non-zero, display time allowed CALL PRINT ; Else, display unlimited DB 'unlimited',0 JR XM STOT2: CALL PA2ASC CALL PRINT DB ' minutes.',0 ; The following code is executed if XMACC is set to 1. This allows the Sysop ; to restrict the use of XMODEM (or KMD) for various levels of user. The ; restrictions are no file transfers, uploads only, downloads only, or full ; usage. Set the proper values in the PBBSHDR file to use this facility. ; And be sure to set your XMODEM (or KMD) to check these values before ; allowing operation. XM: IF XMACC LD A,(ACESS) ; Get access level CP XMLVL ; File transfer allowed? JP C,XMBLK ; If not, go block access to file transfer LD A,(XMRTIO) ; Else, see if we're using U/D ratio OR A JP Z,XMDOK ; If not, go allow file transfer LD B,A LD DE,(UPLDS) LD L,A ; Give him one freeby LD H,0 XRATAD: ADD HL,DE ; Calc the number of d/l he's allowed DJNZ XRATAD LD DE,(DNLDS) OR A ; Clear carry SBC HL,DE ; (UPLOADS * RATIO) - DOWNLOADS JP NC,XMDOK ; Still good.. CALL PRINT DB CR,LF,LF,BEL,'You have exceeded the ',0 LD A,(XMRTIO) CALL PA2ASC ; Print, with leading zero if necessary CALL PRINT DB ':1 Download to Upload ratio allowed.' DB CR,LF DB 'You are restricted to Upload ONLY until your ratio improves.' DB CR,LF,LF DB 'NOTE: This calculation is only done when you login,' DB CR,LF DB ' so you must call back after your Upload(s) to reset it!' DB CR,LF,0 JR XMBLK ; Go block transfers XMDOK: LD A,XMOK LD (STATUS),A JR XMSET XMBLK: LD A,XMNO LD (STATUS),A XMSET: LD A,(STATUS) LD B,A LD A,(ACESS) ; Is this CP COSYS ; a sysop? JR C,XMSET2 ; If not, continue SET 7,B ; Else, set 'priviledged user' XMSET2: LD D,B ; Use BYE's LD E,0 ; extended BDOS LD C,LCPTR ; call to set CALL SPBDOS ; ACCESS byte ENDIF ; XMACC ; Check to see if any mail waiting. CMAIL: XOR A ; Clear version and credits flags LD (VER),A ; since we printed them at signon LD (AUTHOR),A LD A,(MFLAG) ; Any mail? OR A JP Z,NOMAIL ; Nope, so skip ; Got some mail, so jump to mail section. GMAIL: CALL MIOPEN ; Open message index file LD HL,(IMNDX) ; Put number of records LD (RRNO1),HL ; into record # NCHK1: LD HL,(RRNO1) CALL GET CALL ISTO ; See if to user JR NZ,NCHK2 ; If no, skip it LD A,(MREAD) ; Else, get read/deleted flag OR A ; Has he read it? JR NZ,NCHK2 ; If yes (or it's deleted), don't update LD HL,MFTMP ; Else, bump the new INC (HL) ; mail waiting counter NCHK2: LD HL,(RRNO1) ; Get record number LD A,L ; Check for OR H ; record zero JR Z,NCHK3 ; If record zero, bail out DEC HL ; Else, point to previous record LD (RRNO1),HL ; Store it LD HL,(MNUMF) ; Get current message number LD DE,(HIMSG) ; and user's high message number XOR A ; Clear carry flag SBC HL,DE ; All done? JR NC,NCHK1 ; If no, go check next message NCHK3: CALL PRINT ; Else, display message queue count DB CR,LF,LF,'==>> You have ',0 LD A,(MFLAG) ; Get mail count PUSH AF ; Save it for later CALL PA2ASC CALL PRINT DB ' message',0 POP AF ; Restore mail count CP 1 JR Z,NCHK4 CALL PRINT DB 's',0 NCHK4: CALL PRINT DB ' in your queue <<==',CR,LF,0 LD A,(MFTMP) ; Get new mail count OR A ; Any new? JP Z,NOMAIL ; If not, skip PUSH AF ; Else, save new count CALL PRINT DB '==>> of which [ ',0 POP AF ; Restore new count PUSH AF ; and save it again CALL PA2ASC POP AF ; Restore new count PUSH AF ; and save it again CP 1 JR Z,GMAIL1 CALL PRINT DB ' ] are',0 JR GMAILA GMAIL1: CALL PRINT DB ' ] is',0 GMAILA: CALL PRINT DB ' NEW! <<==',CR,LF,'==>> Read your NEW message',0 POP AF ; Restore count of new mail CP 1 JR Z,GMAIL2 CALL PRINT DB 's',0 GMAIL2: CALL PRINT DB ' now (Y/n)? ',0 CALL EATCHR CALL DEFYES ; Go get an answer (default yes) JP Z,PBBS ; If NO, proceed directly to PBBS LD A,1 ; Else, set new message read flag LD (PNEW),A JP PBBS ; No mail waiting, so we ship him off to the MAIN MENU NOMAIL: LD A,(NUSR) ; Is this a OR A ; new user? JR NZ,MSGWT ; If yes, then can't have mail waiting CALL PRINT ; Else, tell old user that nobody loves him! DB CR,LF,'==>> Sorry, no NEW messages waiting for you <<==',0 JP PBBS MSGWT: CALL PRINT DB CR,LF,'Enter to proceed ',0 CALL GETCH ; Get input JP PBBS PASERR: CALL PRINT DB ' Incorrect!',CR,LF,0 JP PS2 ; Log an incorrect login attempt. LOGOUT: LD A,2 ; Bump # lines by one for 'from' info LD (LINES),A LD E,1 ; Set BYE's LD C,WRTLOC ; write lock flag CALL SPBDOS LD HL,COMMENTS ; Point to FCB CALL SPOPEN ; Open or create comments file LD HL,64 LD (RRSZ1),HL ; Set record size LD HL,0 ; Get record number CALL GET ; Get record from comments file LD HL,(RNDBUF) ; First 2 bytes of record 0 PUSH HL ; are this comment's record number LD A,(LINES) LD D,0 LD E,A ADD HL,DE ; New number for next comment LD (RNDBUF),HL ; Store it LD HL,0 ; Record number CALL PUT64 ; Update comments file ; And finally, write each 64 byte line to sequential records ; showing the type of access. PASWRT: POP HL ; Get starting record number back LD (RRNO1),HL LD A,(LINES) DEC A ; Less one because of 'from' line LD B,A ; Set up counter CALL WRTLP CALL CLRBUF ; Clear random buffer to zeros LD HL,UNAME LD DE,RNDBUF LD B,30 ; Maximum length for caller's name CALL MOVNM ; Move in mixed case LD A,' ' ; Then a space LD (DE),A CALL GETTIM ; Get date and time LD HL,TIME INC DE LD BC,8 LDIR ; Move time string into place LD A,' ' ; Another space LD (DE),A LD HL,DATE INC DE LD BC,8 LDIR ; And date LD HL,MSGBRK ; Point to EOM message (includes CR,LF) LD BC,15 LDIR LD HL,(RRNO1) CALL PUT64 ; Write last line CALL CLOSE ; All done LD E,0 ; Allow BYE to LD C,WRTLOC ; hang-up again by CALL SPBDOS ; clearing the write lock PASOUT: CALL PRINT DB CR,LF,LF,'Too many tries...',0 JP 0000 ; Let BYE handle it ; Program ends up in this section if no mail is waiting for a user. ; If PBBS is called from the CCP, then this is the entry point. ; This is determined by reading byte labelled REENTR somewhere ; in memory. If REENTR=0, then the signon routines are entered. ; If REENTR=1, then program jumps here after setting the stack. ;------------------------------------------------------------------------------ ;PBBS.COM by Russ Pencin ; All rights reserved. ;------------------------------------------------------------------------------ PBBS: IF DSKLOG LD A,(DSKFLG) ; See if DISKLOG is in use OR A ; If zero, DISKLOG is there but turned off JR Z,PBBSA ; Exit with no change to the flag LD E,0 ; Now turn off the DISKLOG in BYE5 LD C,SDSKLOG ; BYE5 DISKLOG status request CALL SPBDOS ENDIF ; DSKLOG PBBSA: IF VERSION LD A,(VER) ; Get version flag OR A JR Z,PBBS0 ; If already been printed, skip CALL PVER ; Else, print version.revision ENDIF PBBS0: IF CREDITS LD HL,AUTHOR CALL PRINTM ; Show who wrote this ENDIF ; CREDITS PBBS1: CALL UGET ; Get record from users file CALL CLOSE ; Users file ; Set the mail folder access levels. IF NMFLDRS GT 1 LD HL,ACCTBL ; Get address of access table into [HL] LD (HL),0 ; Clear the first byte of the table PUSH HL ; Set up and clear entire PUSH HL ; table to zeros INC HL EX DE,HL POP HL LD BC,9 LDIR POP HL ; Restore start of table LD (HL),1 ; Set first INC HL ; two entries LD (HL),1 ; to 1 INC HL LD IX,MAXFLD ; Point to maximum folder for this user LD (IX+00H),1 ; Set it to 1 LD IY,MAILTBL ; Now point to access levels for each folder LD A,(UFACC) ; Store access blocked LD B,A ; flags to B LD A,(UFACC+1) ; and access flags LD C,A ; to C LD A,(ACESS) ; Store user's access LD E,A ; level to E LD A,040H ; Second byte of 'BIT 0,B' instruction LD D,NMFLDRS-1 ; Total number of folders-1 in D PUSH AF AFTEST: POP AF PUSH AF LD (AFT1+1),A ; Set 'BIT' instruction to right bit INC A ; And the LD (AFT3+1),A ; next one ADD A,080H ; Now the 'SET' instruction LD (AFT2+1),A AFT1: BIT 0,B ; Test one bit in 'blocked' byte JR NZ,AFT4 ; Blocked, so skip next LD A,(IY+00H) ; Get minimum level required CP E ; Compare to user's access level JR Z,AFT2 ; OK, he gets access to this folder JR NC,AFT3 ; Nope, not this one AFT2: SET 0,C ; Set the correct flag to access AFT3: BIT 0,C ; Does he have access here? JR Z,AFT4 ; Nope, skip LD (HL),1 ; Set ACCTBL for access INC (IX+00H) ; Bump his maximum folder AFT4: INC HL ; Point to next ACCTBL INC IY ; and MAILTBL POP AF ; Restore second byte of 'BIT' instruction ADD A,08H ; Adjust for next bit PUSH AF ; Save it again DEC D ; Decrement counter JR NZ,AFTEST ; and loop til done AFT5: POP AF ; Stack housekeeping LD A,C ; Restore access flags for this user LD (UFACC+1),A ; and put it back in buffer ENDIF ; NMFLDRS GT 1 XOR A ; Reset LD (OLDFLD),A ; previous folder to 0 (GLOBAL) LD (MFLDR),A ; current folder to 0 (GLOBAL) LD (UMFLDR),A ; user's folder to 0 (GLOBAL) LD A,(MFLAG) ; Does he have mail waiting (read or unread)? OR A IF NOT ONEMNU JP Z,PMENU ; If no, off to the PBBS menu ELSE JR Z,OMENU ; If no, off to the menu ENDIF ; NOT ONEMNU LD A,(PNEW) ; Else, does he have new mail OR A ; and want to read it? JP NZ,READMP ; If so, go read it IF NOT ONEMNU JP MMENU ; Else, just send him to the MAIL menu ENDIF ; NOT ONEMNU IF ONEMNU OMENU: CALL EATCHR ; Clear garbage XOR A ; Reset LD (SYSFLG),A ; sysop private read flag LD (LFLAG),A ; left messages flag LD (PFLAG),A ; public messages flag LD (PMSND),A ; private mail send flag LD (ALLFLG),A ; message sent to 'ALL' flag LD (RNEW),A ; read new messages flag LD (PNEW),A ; read private new flag LD A,(OLDFLD) ; Get selected folder (previous) LD (MFLDR),A ; and set it into the current (active) folder LD A,(FBYTE) ; Get the user's flag byte BIT XPTBIT,A ; and test the expert flag JR NZ,OCMD ; Expert mode on, so skip menu OMENUD: LD HL,ONEMENU ; Give him a menu CALL TYPE OCMD: CALL PCRLF IF NOT SET25 ; Not needed if 25th line available LD HL,UNAME CALL PRLC ; Print users name locally ENDIF ; NOT SET25 LD C,RMTOS ; BYE's 'Time on system...' function CALL SPBDOS IF NMFLDRS GT 1 LD A,(MFLDR) CALL SFLDR ; Display working folder CALL PCRLF LD HL,UNAME ; Make him feel at home LD B,30 CALL PRINTS CALL PRINT DB ', the PBBS commands are:',CR,LF DB 'B,C,D,E,F,G,H,I,J,L,N,P,R,S,T,U,V,W,X,Y',0 LD A,(MFLDR) ; Get current folder OR A ; Is it 0 (Global)? JR Z,OMENUA ; If yes, that's all the commands CALL PRINT ; Else, show last 2 commands DB ',>,<',0 OMENUA: CALL PRINT ; Now finish the command prompt DB ' or ?: ',0 ELSE LD HL,UNAME ; Make him feel at home LD B,30 CALL PRINTS CALL PRINT DB ', the PBBS commands are:',CR,LF DB 'B,C,D,E,F,G,H,I,J,L,N,P,R,S,T,U,V,W,X,Y or ?: ',0 ENDIF ; NMFLDRS GT 1 OMENU1: CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? JR Z, OMENU1 ; Yes, do nothing CP ' ' ; Printable character? JR C,OMENU1 ; If not, get another LD HL,JPTAB ; Start of jump decode table LD BC,DBEND-JPTAB ; Number of entries in BC CALL FINDER ; Get jump address, if valid command JR NZ,OMENU2 ; Invalid character PUSH DE ; Stuff action address onto stack RET ; and go do it OMENU2: CALL DOBS ; Wrong character, blank it out JR OMENU1 ; and return to command prompt JPTAB: DB '?@!' IF NMFLDRS GT 1 DB '>] DW GETHI ; Change to next higher mail folder [<] ENDIF ; NMFLDRS GT 1 DW SYSRED ; Sysop read all [!] DW YAKSOP ; Sysop initiated chat [@] DW OMENUD ; Display the menu [?] ELSE PMENU: CALL EATCHR ; Clear garbage LD A,(FBYTE) ; Get the user's flag byte BIT XPTBIT,A ; and test the expert flag JR NZ,PCMD ; Expert flag set, so skip menu PMENUD: LD HL,PBBSMENU ; Give him a menu CALL TYPE PCMD: CALL PCRLF IF NOT SET25 LD HL,UNAME CALL PRLC ENDIF ; NOT SET25 LD C,RMTOS ; BYE's 'Time on system...' function CALL SPBDOS LD HL,UNAME ; Make him feel at home LD B,30 CALL PRINTS CALL PRINT DB ', the PBBS commands are: ' DB 'B,C,D,G,I,J,L,M,N,T,U,W,X,Y or ?: ',0 CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? JR Z,PMENU1 ; Yes, get another one CP ' ' ; Printable character? JR C,PMENU1 ; If not, get another LD HL,JPTB1 ; Start of jump decode table LD BC,DBEN1-JPTB1 ; Number of entries in BC CALL FINDER ; Get jump address, if valid command JR NZ,PMENU2 ; Invalid character PUSH DE ; Stuff action address onto stack RET ; and go do it PMENU2: CALL DOBS ; Wrong character, blank it out JR PMENU1 ; and return to command prompt JPTB1: DB '?@BCDGIJLMNTUWXY' DBEN1: DW YAK ; Chat to the Sysop (Y) DW SETXPT ; Expert mode toggle (X) DW FDBACK ; Leave a comment for Sysop (W) DW STATS ; Show user stats (U) DW PTOG ; Toggle [more] pauses (T) DW XMDM ; Display new uploads (N) DW MMENU ; Change to the Mail Menu (M) DW PUSERS ; View the user list (L) DW JMPCPM ; Jump to CP/M (J) DW PNEWS ; Display information/news articles (I) DW QUIT ; Goodbye (G) DW WFOR ; Display descriptions of new files (D) DW PCLRS ; Display last callers list (C) DW PBLTN ; Print the bulletin (B) DW YAKSOP ; Sysop initiated chat (@) DW PMENUD ; Display the menu (?) ENDIF FDBACK: LD A,RET ; Make comment routine a subroutine LD (EXIT2),A CALL CMNT ; Go get comments from user XOR A ; Restore subroutine to normal routine LD (EXIT2),A IF NOT ONEMNU JP PMENU ; Now back to the proper menu ELSE JP OMENU ENDIF ; NOT ONEMNU PTOG: CALL MORTOG IF NOT ONEMNU JP PMENU ; Now back to the proper menu ELSE JP OMENU ENDIF ; Display the bulletin file for the user PBLTN: CALL EATCHR LD HL,BULLETIN ; Load the bulletin file name CALL TYPE LD HL,EOFMSG CALL PRINTM CALL EATCHR PBLTWT: CALL GTCHAR ; Wait for him to read it IF NOT ONEMNU JP PMENU ; Now back to the proper menu ELSE JP OMENU ENDIF ; NOT ONEMNU PNEWS: LD HL,NEWS ; NEWS must question line with no 'CR' CALL TYPE CALL PRINT DB ' to exit: ',0 CALL EATCHR PNASK: CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? IF NOT ONEMNU JP Z,PMENU ; If so, back to the menu ELSE JP Z,OMENU ; If so, back to the menu ENDIF ; NOT ONEMNU CALL CAPS CP 'A' JR C,PNASK ; PBBS allows up to 26 news files: A-Z CP 'Z'+1 JR NC,PNASK LD L,A ; Save input character in [L] LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag LD A,L ; Restore input character JR NZ,PNASK1 ; Hotkeys not on, so no need to echo CALL ECHO PNASK1: LD (NEWSEXT),A LD HL,NEWS ; Point to FCB CALL TYPE ; Display NEWSx.PBS XOR A ; Reset LD (NEWSEXT),A ; the file extension LD HL,EOFMSG CALL PRINTM CALL EATCHR PNEWWT: CALL GTCHAR ; Let him read it JP PNEWS SETXPT: CALL TOGXPT ; Requesting change of XPERT mode IF NOT ONEMNU JP PMENU ; Now back to the proper menu ELSE JP OMENU ENDIF ; NOT ONEMNU TOGXPT: LD HL,FBYTE ; Point to user's flag byte XOR A ; Set to toggle SET XPTBIT,A ; expert mode XOR (HL) ; Do it LD (HL),A ; and put it back TXPT1: LD HL,AVAIL LD DE,AVAILF LD BC,USRLEN LDIR ; Move user record to random buffer JP UPUT ; Update user record MORTOG: LD HL,FBYTE ; Point to the user's flag byte XOR A ; Set to toggle SET MORBIT,A ; [more] bit XOR (HL) ; Do it LD (HL),A ; and put it back PUSH AF ; Save it CALL PRINT DB CR,LF,LF,'[more] pauses now ',0 POP AF ; Restore flag byte BIT MORBIT,A ; and test it JR Z,MORON ; It's on, so tell us CALL PRINT ; Else, say it's off DB 'OFF',CR,LF,0 JR MORON1 MORON: CALL PRINT DB 'ON',CR,LF,0 MORON1: JP TXPT1 ; Go and write the modified user record ; Start of user profile display STATS: XOR A LD (TWIT),A ; Clear TWIT/PNEW flag LD HL,LOGSTR ; Hl -> logon time LD DE,ONTIM ; Point to where to put it LD BC,8 ; No. of bytes LDIR ; Move em LD HL,TZONE ; Point to time zone string LD DE,TIMEZ1 ; Put it here LD BC,3 ; Only 3 bytes LDIR LD HL,TZONE ; Do it for the second location LD DE,TIMEZ2 LD BC,3 LDIR CALL GETTIM ; Get the current time LD HL,TIME ; Point to the time string LD DE,CURTIM ; Where to put it LD BC,8 LDIR ; Move it CALL CALTIM ; Calculate the elapsed time ; Now print the results. CALL PRINT DB CR,LF,LF,LF,LF,'Today is .........: ',0 LD IX,BDATE CALL FULDAT CALL PRINT DB CR,LF,'Current time .....: ' CURTIM: DB '00:00:00 ' ; Current time goes here TIMEZ1: DB ' ' ; Time zone goes here DB CR,LF,'User Name ........: ',0 LD HL,UNAME LD B,30 CALL PRINTN CALL PRINT DB CR,LF,'User Phone .......: ',0 LD HL,PHONE LD B,12 CALL PRINTL CALL PRINT DB CR,LF,'Logged on at .....: ' ONTIM: DB '00:00:00 ' ; Login time goes here TIMEZ2: DB ' ' ; Time zone goes here DB CR,LF,'Connect time .....: ' ELPTIM: DB '00:00:00' DB CR,LF,'Time left ........: ',0 LD C,RTCCHK CALL SPBDOS ; Get time on LD H,0 LD L,A PUSH HL ; Save it LD E,255 LD C,MXTIME CALL SPBDOS ; Get time allowed OR A JR NZ,ONTIM1 CALL PRINT ; 0 means unlimited time allowed DB 'unlimited',0 POP HL ; Clean up stack JR MOR ONTIM1: LD H,0 LD L,A POP DE ; Was HL... AND A ; Clear carry flag SBC HL,DE CALL PB2ASC CALL PRINT DB ' mins.',0 MOR: CALL PRINT DB CR,LF,LF,'[more] Toggle ....: ',0 LD A,(FBYTE) ; Get flag byte BIT MORBIT,A ; Test the [more] toggle JR Z,MOR1 ; If on, skip CALL PRINT ; Else, tell him DB 'Off',0 ; it's off JR EXPT ; and go test expert mode MOR1: CALL PRINT ; Tell him DB 'On ',0 ; it's on EXPT: CALL PRINT ; Expert mode DB ' Expert Mode ......: ',0 LD A,(FBYTE) ; Get flag byte BIT XPTBIT,A ; Test the expert bit JR Z,EXPT1 ; If off, skip CALL PRINT ; Else, tell him DB 'On',0 ; it's on JR HOTKY ; and go do hotkey flag EXPT1: CALL PRINT ; Tell him DB 'Off',0 ; it's off HOTKY: CALL PRINT ; Expert mode DB CR,LF,'Hotkey Input .....: ',0 LD A,(FBYTE) ; Get flag byte BIT HOTBIT,A ; Test the hotkey bit JR NZ,HOTK1 ; If off, skip CALL PRINT ; Else, tell him DB 'On',0 ; it's on JR ONTIM2 ; and go do uploads HOTK1: CALL PRINT ; Tell him DB 'Off',0 ; it's off ONTIM2: CALL PRINT DB CR,LF,LF,'Current Nulls ....: ',0 LD E,255 LD C,NULLS ; Get current number of nulls from BYE CALL SPBDOS CALL PA2ASC CALL PRINT DB CR,LF,'Terminal Width ...: ',0 LD A,(LENG) CALL PA2ASC CALL PRINT DB CR,LF,'Terminal Length ..: ',0 LD A,(TLIN) CALL PA2ASC IF TCAP CALL PRINT DB CR,LF,'Current Terminal .: ',0 IF ZCPR3 LD HL,ZTCAP ; Point to TCAP terminal name ELSE LD HL,SYSENV+80H ; Point to TCAP terminal name ENDIF ; ZCPR3 LD B,16 CALL PRINTL ENDIF ; TCAP CALL PRINT DB CR,LF,LF,'Total Up/downloads: ',0 LD A,(UPLOADS) LD H,0 LD L,A LD BC,(UPLDS) ADD HL,BC CALL PB2ASC LD A,'/' CALL ECHO LD A,(DNLOADS) LD H,0 LD L,A LD BC,(DNLDS) ADD HL,BC CALL PB2ASC CALL PRINT DB CR,LF,'Highest SYS. MSG .: ',0 LD HL,(IMNXT) DEC HL CALL PB2ASC CALL PRINT DB CR,LF,'Highest MSG. read : ',0 LD HL,(HIMSG) CALL PB2ASC CALL PRINT DB CR,LF,'Messages waiting .: ',0 LD A,(MFLAG) CALL PA2ASC MSTAT: CALL PRINT DB CR,LF,LF,LF DB 'You may change: ' DB 'F)one #, N)ulls, P)assword, ' IF TCAP DB 'T)erminal,' ENDIF ; TCAP DB CR,LF DB 'L)ength, W)idth, H)otkeys or to exit: ',0 CALL EATCHR ; Clear garbage MSTAT1: CALL GTCHAR ; Get a character from the console CP CR IF NOT ONEMNU JP Z,PMENU ; Now back to the proper menu ELSE JP Z,OMENU ENDIF ; NOT ONEMNU CP ' ' ; Printable character? JR C,MSTAT1 ; If not, get another LD HL,JPTBS ; Start of jump decode table LD BC,DBENS-JPTBS ; Number of entries in BC CALL FINDER ; Get jump address, if valid command JR NZ,MSTAT2 ; Invalid character PUSH DE ; Stuff action address onto stack RET ; and go do it MSTAT2: CALL DOBS ; Wrong character, blank it out JR MSTAT1 JPTBS: DB 'FNP' IF TCAP DB 'T' ENDIF ; TCAP DB 'LWH' DBENS: DW HOTCHG ; Hotkey mode [H] DW CHLIN ; Screen width [W] DW CHLEN ; Terminal length [L] IF TCAP DW TRMCHG ; Terminal choice [T] ENDIF ; TCAP DW PASCHG ; Password [P] DW NULCHG ; Number of nulls [N] DW PHNCHG ; Phone number [F] CHLIN: CALL PRINT DB 'idth' DB CR,LF,LF,'Your current Screen Width is ',0 LD A,(LENG) CALL PA2ASC CALL LINCHG JR GPCH1 CHLEN: CALL PRINT DB 'ength' DB CR,LF,LF,'Your current Screen Length is ',0 LD A,(TLIN) CALL PA2ASC CALL LENCHG GPCH1: JR PASCH1 PASCHG: CALL PRINT DB 'assword' DB CR,LF,LF,'Password now: ',0 LD HL,PSWRD LD B,10 CALL PRINTL PASCH0: CALL PRINT DB CR,LF,'New password (return to exit)? ',0 XOR A LD D,A LD C,20H ; Caps on LD B,10 CALL INPUT JP Z,MSTAT ; He's just playing CP 4 ; Must be at least 4 characters JR C,ERR ; Nope, tell him and ask again LD A,(HL) ; Else, check for a space CP ' ' ; as first character JR Z,ERR ; Oops, tell him and ask again LD DE,PSWRD LD BC,10 LDIR PASCH1: CALL TXPT1 ; Go and update the modified user record JP MSTAT ERR: CALL PRINT DB CR,LF,'Must be at least 4 characters with a non-space' DB 'as the first character.',0 JP PASCH0 NULCHG: CALL PRINT DB 'ulls' DB CR,LF,LF,'You are currently set to ',0 LD E,255 LD C,NULLS ; Get current number of nulls from BYE CALL SPBDOS CALL PA2ASC CALL PRINT DB ' nulls.',0 LD HL,NULLQ+3 ; Save a few bytes here CALL PRINTM LD B,1 XOR A LD D,A LD C,A CALL INPUT JP Z,MSTAT CALL CNULL CALL TXPT1 ; Go and update the modified user record JP STATS CNULL: LD (CNVRT0+1),A ; Set number characters to convert PUSH HL POP IX ; Pointer to IX CALL CNVRT0 ; Make string binary number LD A,L AND A ; Clear carry LD DE,10 PUSH HL SBC HL,DE POP HL RET NC ; Over 9, so no good LD A,L NULDN: LD (NNULL),A LD E,A LD C,NULLS ; Set new nulls CALL SPBDOS ; Let BYE know RET PHNCHG: CALL PRINT DB 'one Number' DB CR,LF,LF DB 'Enter new number or re-enter old number if no change.' DB CR,LF,LF,'Phone number now: ',0 LD HL,PHONE LD BC,12 LD DE,PHONEF LDIR ; Save the old one LD HL,PHONE LD B,12 CALL PRINTL ; Display it CALL PCRLF CALL PHN ; Go get a new one LD A,(TWIT) OR A ; See if he faked it JP Z,STATS ; Nope, so return LD HL,PHONEF ; Else, he did fake it LD BC,12 ; so restore old one LD DE,PHONE LDIR JP STATS ; Then return HOTCHG: LD HL,FBYTE ; Point to the user's flag byte XOR A ; Set to toggle SET HOTBIT,A ; hotkey bit XOR (HL) ; Do it LD (HL),A ; and put it back PUSH AF ; Save it CALL PRINT DB CR,LF,LF,'Hotkey mode now ',0 POP AF ; Restore flag byte BIT HOTBIT,A ; and test it JR Z,HOTON ; It's on, so tell us CALL PRINT ; Else, say it's off DB 'OFF',CR,LF,0 JR HOTON1 HOTON: CALL PRINT DB 'ON',CR,LF,0 HOTON1: CALL TXPT1 ; Go and write the modified user record JP STATS ; and return to prompt IF TCAP TRMCHG: CALL PRINT DB 'erminal',CR,LF,LF,0 XOR A CALL TCSET OR A JP Z,MSTAT LD (TCODE),A LD (TRMCD),A CALL TXPT1 ; Go and update the modified user record JP MSTAT ENDIF ; TCAP IF NOT ONEMNU MXPRT: LD HL,FBYTE ; Point to the user's flag byte XOR A ; Set to toggle SET XPTBIT,A ; the expert mode XOR (HL) ; Do it LD (HL),A ; and put it back CALL TXPT1 ; Go and write the modified user record MMENU: CALL EATCHR ; Clear some garbage XOR A ; Reset LD (SYSFLG),A ; sysop private read flag LD (LFLAG),A ; left messages flag LD (PFLAG),A ; public messages flag LD (PMSND),A ; private mail send flag LD (ALLFLG),A ; message sent to 'ALL' flag LD (RNEW),A ; read new messages flag LD (PNEW),A ; private new flag LD A,(OLDFLD) ; Get selected folder (previous) LD (MFLDR),A ; and set it into the current (active) folder LD A,(FBYTE) ; Get user's flag byte BIT XPTBIT,A ; and test it JR NZ,MCMD ; User is expert, skip menu MMENUD: LD HL,MAILMENU ; Load up the menu file CALL TYPE MCMD: CALL PCRLF1 IF NOT SET25 ; Not necessary if 25th line available LD HL,UNAME CALL PRLC ; Print users name locally ENDIF LD C,RMTOS ; BYE's 'Time on system...' function CALL SPBDOS IF NMFLDRS GT 1 LD A,(MFLDR) CALL SFLDR ; Display working folder ENDIF ; NMFLDRS GT 1 CALL PCRLF LD HL,UNAME ; Make him feel at home LD B,30 CALL PRINTS CALL PRINT DB ', the MAIL commands are: ' DB 'E,' IF NMFLDRS GT 1 DB 'F,' ENDIF DB 'G,H,J,M,P,R,S,T,V,X',0 IF NMFLDRS GT 1 LD A,(MFLDR) ; Get current folder OR A ; Zero (Global)? JR Z,MMENU0 ; If yes, all done printing commands CALL PRINT ; Else, print the last 2 commands DB ',>,<',0 MMENU0: ENDIF CALL PRINT DB ' or ?: ',0 MMENU1: CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? JR Z,MMENU1 ; Yes, get another character CP ' ' ; Printable character? JR C,MMENU1 ; If not, get another LD HL,JPTB2 ; Start of jump decode table LD BC,DBEN2-JPTB2 ; Number of entries in BC CALL FINDER ; Get jump address, if valid command JR NZ,MMENU2 ; Invalid character PUSH DE ; Stuff action address onto stack RET ; and go do it MMENU2: CALL DOBS ; Wrong character, blank it out JR MMENU1 ; and return to command prompt JPTB2: DB '?!@' IF NMFLDRS GT 1 DB '<>F' ENDIF DB 'EGHJMPRSTVX' DBEN2: DW MXPRT ; Expert mode toggle [X] DW LREADM ; Read messages you wrote [V] DW MTOG ; Toggle [more] pauses [T] DW KWSRCH ; Search message subjects [S] DW PMAIL ; Read public (and your private) mail [R] DW READM ; Read private mail [P] DW PMENU ; Change to PBBS Menu [M] DW JMPCPM ; Jump to CP/M [J] DW SCAN ; Scan message headers [H] DW QUIT ; Wants to quit [G] DW POSTM ; Enter a message [E] IF NMFLDRS GT 1 DW GFLD ; Change mail folders [F] DW GETHI ; Change to next higher mail folder [<] DW GTLOW ; Change to next lower mail folder [>] ENDIF ; NMFLDRS GT 1 DW YAKSOP ; Sysop initiated chat [@] DW SYSRED ; Sysop read all [!] DW MMENUD ; Display menu [?] MTOG: CALL MORTOG JP MMENU ENDIF ; NOT ONEMNU ; Read private mail. READM: LD A,(MFLDR) ; Get current folder LD (OLDFLD),A ; and store it as old folder XOR A ; Reset LD (MFLDR),A ; GLOBAL folder (zero) for now LD (RNEW),A ; read new messages flag LD (CNTN),A ; continuous flag LD (LFLAG),A ; left flag LD (PFLAG),A ; public flag LD (MFTMP),A ; temp (new) mail count LD A,(MFLAG) ; Does he have OR A ; any mail? JP NZ,READMP ; If yes, go process mail CALL PRINT DB CR,LF,LF DB 'Sorry, there are no messages addressed to you.' DB CR,LF,0 IF NOT ONEMNU JP MMENU ; Poor baby, no one left him any mail ELSE JP OMENU ; Poor baby, no one left him any mail ENDIF ; NOT ONEMNU ; Set the continous read function SETCON: LD A,(CNVRT0+1) ; Eliminate the + DEC A LD (CNVRT0+1),A ; Save the result XOR A ; Reset LD (RNEW),A ; new flag, no [Next] message INC A ; Set LD (CNTN),A ; continuous flag, no pause between messages RET ; Check for messages left by this user LREADM: LD A,(MFLDR) LD (OLDFLD),A XOR A ; Reset LD (MFLDR),A ; folder 1 (zero) for now LD (CNTN),A ; continous read flag LD (PFLAG),A ; public flag INC A ; Set LD (LFLAG),A ; read left messages flag JP READMP ; Special Sysop function to read all messages in database ; to allow him to delete offensive ones. SYSRED: LD A,(ACESS) ; Better be a Sysop.. CP COSYS IF NOT ONEMNU JR C,MMENU2 ; Send him back empty ELSE JP C,OMENU2 ; Send him back empty ENDIF ; NOT ONEMNU LD A,(MFLDR) ; Get current fldr # LD (OLDFLD),A ; Save it for later XOR A ; Set current folder to LD (MFLDR),A ; GLOBAL (zero) for now INC A ; Set LD (SYSFLG),A ; sysop flag JR READMP ; Reading all public mail, so set/clear the appropriate flags. PMAIL: CALL SETFLG ; Go set up the flags ; Common entry point for all reads (public, private or left by user). READMP: LD HL,RD1 ; Move word 'read' LD DE,SMSG ; into introductory LD BC,4 ; message for GETNUM LDIR ; subroutine LD DE,(MSGARR) ; Point to messages table PUSH DE ; Save it for later LD A,(PNEW) OR A JR NZ,RPCNT CALL PRINT DB CR,LF,'Stand by...',0 RPCNT: CALL MIOPEN ; Open message index file LD (RRNO1),HL ; and start with first message ; Read message index loop and make decisions about whether to display ; based on user's name and current function (public, private). MRASK: LD A,(SYSFLG) ; See if Sysop is reading all OR A JR NZ,SYSASK ; YUP...ask him where to start.. LD A,(PNEW) ; Reading new private mail? OR A JR Z,MRASK0 ; Nope, go get all private LD HL,(HIMSG) ; Start with his last high-message INC HL JR MDFND ; Get actual record MRASK0: LD A,(PFLAG) ; Are we reading OR A ; public mail? JR Z,MRDLP ; If not, don't tell him about continuous SYSASK: CALL PRINT ; Else, can add a '+' for continuous read DB CR,LF,LF,'Add a + after msg # for non-stop' DB ' read for disk capture.',0 CALL GETNUM ; Get the starting message number MDFND: CALL LOCMSG ; Print message then CALL GETRC ; go find actual record (or closest) MRDLP: LD HL,(RRNO1) CALL GET CALL TDONTD ; See if we should display it JR NZ,MBUMP ; No, go get another one CALL ISTO ; See if to this user JR NZ,MRDLP4 ; If not, don't bump new mail count LD HL,MFTMP ; Else, incrment the INC (HL) ; new mail count by one MRDLP4: LD HL,MRECF ; Starting record number for message POP DE ; Get current pointer LD BC,2 LDIR ; Save message info PUSH DE ; New pointer LD A,(007H) ; Get top of TPA DEC A ; Give us some head room SUB D JR NZ,MBUMP ; We still have room CALL PRINT DB 'Unable to display all messages',BEL,CR,LF,0 JR TPAEND ; Bail out MBUMP: LD DE,(RRNO1) ; Get record INC DE ; and up it by one LD (RRNO1),DE LD HL,(IMNDX) ; See if any XOR A ; more messages SBC HL,DE ; to test JP NC,MRDLP ; If yes, loop TPAEND: LD A,(LFLAG) » Readinç lefô msgs? OR A JR NZ,MBUMP1 ; If yes, skip mail count update LD A,(PFLAG) ; Else, reading public messages? OR A JR NZ,MBUMP1 ; If yes, skip mail count update LD A,(SYSFLG) ; Else, is sysop reading all? OR A JR NZ,MBUMP1 ; If yes, skip update LD A,(PNEW) ; Don't update count on 'new private' read OR A JR NZ,MBUMP1 LD A,(MFTMP) ; Get new mail count LD (MFLAG),A ; This keeps the mail count accurate MBUMP1: POP IY ; Get pointer to end of array (was DE) LD (IY),-1 LD (IY+1),-1 CALL PCRLF LD A,1 ; Set first read flag LD (FIRST),A ; (no pause after header) PMSGS: LD IY,(MSGARR) ; Point to table PMLOP1: LD A,(IY) ; See if at end of array CP -1 JR NZ,PM01 LD A,(IY+1) CP -1 JP Z,NOMOR ; If at end, quit ; Display header of message about to be processed PM01: CALL MIOPEN ; Open or reopen message index file LD L,(IY) ; Set message index file record LD H,(IY+1) LD (RRNO1),HL ; Prepare to display CALL GET ; Get record from message index file XOR A ; Clear LD (DELFLG),A ; delete flag ;------------------------------ ; Display Message Header ;------------------------------ RDMS3: CALL PRINT ; Display message number DB CR,LF,LF DB 'Mesg: ',0 LD HL,(MNUMF) ; Message number LD (LSTMSG),HL ; Update last message read CALL PB2ASC ; Display message number LD B,A ; Number of digits printed to B LD A,6 ; Max number allowed SUB B ; Calculate pad spaces LD B,A ; .. in B CALL PAD ; Pad to next field ; Display message read status R0: LD A,(MREAD) ; Message read/deleted flag OR A ; Deleted? LD A,' ' ; Assume not read or deleted JR Z,R2 ; If unread, char in A is correct JP M,R1 ; Else, if it's deleted, go put 'D' in A LD A,'R' ; Else, must be read JR R2 R1: LD A,'D' ; Message is deleted R2: LD (R3+1),A ; Message status character CALL PRINT ; Display message status R3: DB '( )',0 ; Display message security status LD A,(LENG) ; Screen width CP 52 ; Max header width LD B,8 ; Assume it's ok, so pad 8 spaces JR NC,R3B ; If width ok, proceed LD B,1 ; Else, pad 1 space R3B: CALL PAD ; Pad to next field LD A,(MPUBF) ; Message public/private flag OR A ; Public? JR Z,R3C ; If yes, display public CALL PRINT ; Else, display private DB 'Private',0 JR R4 R3C: LD B,7 ; Pad 7 spaces (for public message) CALL PAD ; Pad to next field ; Display date R4: LD A,(LENG) ; User's screen width CP 52 ; Max header width LD B,7 ; Assume it's ok, so pad 7 spaces JR NC,R4B ; If room, continue LD B,1 ; Else, pad 1 space R4B: CALL PAD ; Print spaces CALL PRINT ; Display date DB 'Date: ',0 LD IX,MDATF ; Message date CALL FULDAT ; Display message date CALL PCRLF ; New line IF NMFLDRS GT 1 ; Display folder name CALL PRINT ; Display folder DB 'Fldr: ',0 LD A,(MFNUMF) ; Message folder number LD (RMFLD),A ; Save for reply message folder CALL SFLDR2 ; Go display folder name CALL PAD ; Print rest of 15 chars set in SFLDR ELSE LD B,21 ; Pad 21 spaces (over folder title and name) CALL PAD ; Pad to next field ENDIF ; NMFLDRS GT 1 LD A,(LENG) ; Screen width CP 52 ; Max header width LD B,16 ; Assume there is room, pad 16 spaces JR NC,R5A ; If there is room, continue LD B,03 ; Else, pad 3 spaces R5A: CALL PAD ; Pad to next field ; Display time CALL PRINT ; Display time DB 'Time: ',0 LD IX,MTIMEF ; Message time CALL PTIME ; Display message time ; Display subject CALL PRINT ; Display subject DB CR,LF DB 'Subj: ',0 LD HL,MSUBF ; Point to message subject LD B,26 ; Maximum length CALL PRINTL IF MSGTHD ; Display original message in thread LD A,(LENG) ; Get screen width CP 61 ; Max width for this header line JR NC,R5B ; If width ok, thread info on this line CALL PCRLF ; Else, start a new line JR R5C ; Now go print thread info R5B: LD A,5 ; Add extra spaces to reach Rply field ADD A,B ; Adjust LD B,A ; counter CALL PAD ; Space over to Rply field R5C: CALL PRINT DB 'Rply: ',0 LD HL,(MREPLY) ; Get message replied to LD A,L ; Is MREPLY=0 OR H JR NZ,PREPLY CALL PRINT DB 'None',0 JR NREPLY PREPLY: CALL PB2ASC ; Print reply number NREPLY: CALL PRINT DB ' (',0 LD HL,(MRLNK) ; Get and display reverse link LD A,L ; Is MRLNK=0 OR H JR NZ,PRLNK CALL PRINT DB 'BOT',0 ; Print BOT (Beginning Of Thread) JR NPRLNK PRLNK: CALL PB2ASC NPRLNK: CALL PRINT DB '-',0 LD HL,(MFLNK) ; Get and display forward link LD A,L ; Is MFLNK=0 OR H JR NZ,PFLNK CALL PRINT DB 'EOT',0 ; Print EOT (End Of Thread) JR NPFLNK PFLNK: CALL PB2ASC NPFLNK: CALL PRINT DB ')',0 LD HL,(MREPLY) ; Get MREPLY LD A,L ; Is MREPLY=0? OR H ; If yes, we must change it JR NZ,MREP ; Otherwise, save for later LD HL,(MNUMF) ; Get current message number MREP: LD (OLDMSG),HL ; Save for later LD HL,(MFLNK) ; Get MFLNK (forward link) LD (FWDMSG),HL ; Save for later LD HL,(MRLNK) ; Get MRLNK (reverse link) LD (REVMSG),HL ; Save for later ENDIF ; MSGTHD ; Display message sender. CALL PRINT ; Display receiver DB CR,LF DB 'From: ',0 LD HL,MFROMF ; Point to sender's name LD B,30 ; Max length CALL PRINTN ; Print in mixed case LD A,(LENG) ; Terminal width CP 73 ; Max header width for this line JR NC,R6 ; If room, go display message sender CALL PCRLF ; Else, new line, then sender JR R6A ; Skip the padding ; Display message receiver. R6: INC B ; Need at least one space between CALL PAD ; sender and receiver R6A: CALL PRINT ; Display sender DB 'To: ',0 LD HL,MTOF ; Point to receiver's name LD B,30 ; Max length CALL PRINTN ; Print in mixed case RBUMP: LD HL,(MNUMF) LD (LSTMSG),HL LD A,(MBLKF) ; Get line count LD (LCOUNT),A ; and save it for later LD HL,(MSTRF) LD (MSGREC),HL ; Set the message start record LD HL,(MRECF) ; Get this index record LD (CURREC),HL ; Save it LD HL,MFROMF ; Save sender name LD DE,MTOTMP ; as receiver LD BC,30 ; for possible LDIR ; answer LD A,(ACESS) ; Is this CP COSYS ; a Sysop? JR NC,SETDF ; If so, go set delete flag IF USRDEL CALL ISFROM ; Else, see if from user JR Z,SETDF ; If so, go set delete flag CALL ISTO ; Else, see if to user JR NZ,SETDF1 ; If not, skip setting the delete flag ELSE JR SETDF1 ; Else, skip setting the delete flag ENDIF ; USRDEL SETDF: LD A,-1 ; Else, set LD (DELFLG),A ; the delete flag SETDF1: IF SKPHDR JP PMGO ; Exit if not stopping after each header ELSE LD A,(CNTN) OR A JP NZ,PMGO ; If we're running continous, go display LD A,(FIRST) ; Else, see if this is a first read OR A JP NZ,PMGO ; If yes, skip questions CALL PRINT DB CR,LF,' R)ead, N)ext, P)revious',0 IF MSGTHD LD HL,(MFLNK) ; Get forward link LD DE,(MRLNK) ; Get reverse link ADD HL,DE ; Add links together LD A,L ; Is result 0 OR H JR Z,PSKP2 ; If yes, don't give thread prompt PSKP1: CALL PRINT ; Show thread option DB ', T)hread, (>/<)',0 PSKP2: ENDIF ; MSGTHD LD A,(LFLAG) ; Is he reading OR A ; his own messages? JR NZ,NOSCAN ; If yes, don't allow 'scan' CALL PRINT DB ', S)can',0 NOSCAN: CALL EATCHR ; Clear garbage LD A,(DELFLG) ; Is he allowed OR A ; to delete this one? JR Z,PSKP ; If not, skip delete prompt PDEL: CALL PRINT ; Else, show delete option DB ', D)elete',0 PSKP: CALL PRINT ; Everyone can exit DB ', E)xit? ',0 PMWT: CALL GTCHAR ; Get a character from the console CP CR JR NZ,PMWT0 ; Not , so check other options LD A,'R' ; entered, so need CALL ECHO ; to print 'R' for 'Read' JP PMGOA ; Now go display the message PMWT0: CP ' ' ; Printable character? JR C,PMWT ; If not, get another on LD HL,JPPMW ; Else, set up LD BC,DBPMW-JPPMW ; the pointers and CALL FINDER ; see if this is a valid character JR NZ,PMWTB ; If not, go delete it and get another PUSH DE ; Else, proceed with the command RET PMWTB: CALL DOBS JR PMWT IF MSGTHD JPPMW: DB '.>,] DW FTHD ; Thread Fwd [,] ENDIF PMXIT0: CALL PRINT DB 'xit',0 PMXIT: CALL HMUPD ; Go update high msg pointer (if necessary) IF NOT ONEMNU PMXIT1: JP MMENU ELSE PMXIT1: JP OMENU ENDIF ; [NOT] ONEMNU ENDIF ; [NOT] SKIP HEADERS ; Display message. PMGOA: CALL PRINT DB 'ead',0 PMGO: XOR A LD (FIRST),A ; Not first any more. LD HL,MSUBF ; Save subject LD DE,MSBTMP ; of message LD BC,26 ; in case he LDIR ; wants to reply CALL ISTO ; See if to user JR NZ,PMGO1 ; If not, go display it LD A,(MREAD) ; Get message status OR A ; Read? JP NZ,PMGO1 ; If yes, don't change it LD A,1 ; Else, mark LD (MREAD),A ; it read LD HL,(RRNO1) ; Update the CALL PUT ; message index record PMGO1: CALL CLOSE ; Close message index file CALL MSGOPN ; and open messages file LD HL,(MSGREC) ; Get message record back LD (RRNO1),HL LD A,(LCOUNT) ; Recover line count LD B,A ; and stuff into [B] for use LD DE,(MSG) ; Point to start of buffer PMLOP2: PUSH BC ; Save counter LD HL,(RRNO1) PUSH DE ; Save buffer address CALL GET64 POP DE ; Restore buffer address LD HL,RNDBUF LD BC,64 ; Move to message buffer LDIR ; DE=next buffer address LD HL,(RRNO1) INC HL ; Next line (record) LD (RRNO1),HL POP BC ; Get counter DJNZ PMLOP2 ; If not done, loop CALL CLOSE ; Else, close the message file CALL LIST ; List the message to screen JR NZ,CL2 ; If he aborted, go prepare for it LD A,(CNTN) ; Else, are we in OR A ; continous read mode? IF NOT ONEMNU JP NZ,MMENU ; If yes, abort without updating hi msg pointer ELSE JP NZ,OMENU ; If yes, abort without updating hi msg pointer ENDIF ; NOT ONEMNU CL2: CALL HMUPD ; Go update high msg pointer (if necessary) PKILLA: CALL ABORT ; Check for pause or abort JR NZ,PKL1 ; If not, carry on CALL PCRLF ; Else, print CR,LF IF NOT ONEMNU JP MMENU ; and return to menu ELSE JP OMENU ; and return to menu ENDIF ; NOT ONEMNU PKL1: LD A,(CNTN) ; Are we in continuous (+) mode ? OR A JP NZ,PMSKP ; If so, go get another msg QUICK MSGEND: CALL EATCHR ; Else, clear any characters from modem CALL PRINT ; and begin printing prompt line DB CR,LF DB ' N)ext' ; Everyone gets next DB ', S)can',0 ; and scan IF MSGTHD LD HL,(MFLNK) ; Get forward link LD DE,(MRLNK) ; Get reverse link ADD HL,DE ; Add links together LD A,L ; Is result 0? OR H JR Z,MSEND1 ; If MRLNK=0 and MFLNK=0 then skip CALL PRINT DB ', T)hread, (>/<)',0 ENDIF ; MSGTHD MSEND1: XOR A ; Clear LD (LFLAG),A ; the 'reading left by me' flag CALL ISFROM ; See if from this user JR Z,MSEND3 ; If so, can delete but cannot reply LD A,(ACESS) ; Else, get his access level CP POSTLV ; Message posting allowed? JR C,MSEND5 ; If not, cannot reply MSEND2: CALL PRINT DB ', R)eply',0 MSEND3: LD A,(DELFLG) ; Is he allowed OR A ; to delete this one? JR Z,MSEND5 ; If not, skip delete prompt MSEND4: CALL PRINT DB ', D)elete',0 MSEND5: CALL PRINT ; Everyone can exit DB ', E)xit: ',0 PMGT: CALL GTCHAR ; Get a character from the console CP CR ; ? JR NZ,PKF1 ; Not , so check other options LD A,'N' ; entered, so need CALL ECHO ; to print 'N' for 'Next' JP PMSKP0 ; Now skip to next PKF1: CP ' ' ; Printable character? JR C,PMGT ; If not, go get another one LD HL,JPPKF ; Else, set up LD BC,DBPKF-JPPKF ; the pointers and CALL FINDER ; go see if this is a valid character JR NZ,PKF1A ; If not, go delete it and get another PUSH DE ; Else, proceed with the command RET PKF1A: CALL DOBS JR PMGT ; And return for another one IF MSGTHD JPPKF: DB '.>,] DW FTHD ; Forward Thread [.] ENDIF PSCAN: LD BC,PMWTB ; Return to PMTWB on error JR PSCNX ; Go see if he can 'scan' PSCN1: LD BC,PKF1A ; Return to PKF1A on error PSCNX: LD A,(LFLAG) ; Is he reading OR A ; his own messages? PUSH BC ; Put error address on stack RET NZ ; and bop back if not POP BC ; Else, just clean up stack CALL PRINT ; and go to scan mode DB 'can',0 PSCN2: CALL SCNRDY ; Set flags, etc for scan CALL CLOSE ; Close messages file CALL MIOPEN ; Open message index file LD HL,(LSTMSG) ; Get last message read INC HL ; Starting message for scan LD DE,(IMNXT) ; Get highest system DEC DE ; message number XOR A ; Clear carry flag PUSH HL ; Save message number SBC HL,DE ; Valid message number? POP HL ; Restore desired number JP C,SCNFND ; If so, go scan from there LD HL,TOOBIG ; Else, go tell him JP PNOMOR ; then abort ; Reply to message PMREP: LD A,(LFLAG) ; Message left by this user? OR A JP NZ,PMSKP0 ; If so, can't reply to himself, so skip LD A,0FFH ; Else, reply requested LD (RFLAG),A ; so set reply flag CALL PRINT DB 'eply',0 PUSH IY ; Save the msg array pointer LD A,RET ; RET op-code LD (EXIT2),A ; Make ENTER: a subroutine LD (GETTO),A ; Make TO: lookup a subroutine IF NMFLDRS GT 1 LD A,(MFLDR) LD (OLDFLD),A LD A,(RMFLD) ; Set the auto reply folder number LD (MFLDR),A ENDIF ; NMFLDRS GT 1 CALL PMW01 ; Ask for name or Sysop for FROM: XOR A LD (GETTO),A LD (ALLFLG),A ; Clear the 'message to ALL' flag LD HL,MTOTMP ; Point to receiver name CALL OTOREP XOR A ; ENTER: is no LD (EXIT2),A ; longer a subroutine POP IY ; Restore message array pointer IF NMFLDRS GT 1 LD A,(OLDFLD) LD (MFLDR),A ENDIF ; NMFLDRS GT 1 LD A,(DELFLG) ; Can he OR A ; delete this message? JR Z,NKILL ; If not, don't ask @PKIL0: CALL PRINT ; Else, ask if he wants to delete DB CR,LF,'Delete answered message (y/N)? ',0 CALL EATCHR PKILL: CALL DEFNO ; Get an answer IF ASKDEL JP NZ,MKILLC ; If yes, go confirm delete ELSE JP NZ,MKILL ; If yes, go delete message ENDIF ; ASKDEL NKILL: JP PMSKP ; Get next message, if any ; No more mail, so quit. NOMOR: CALL CLOSE LD A,(PNEW) ; See if it's new private read OR A JR Z,NOMOR1 ; Nope...say no more XOR A ; Else, clear LD (PNEW),A ; new private flag LD HL,NEWMOR ; Tell him there are others JR PNOMOR ; Go print it NOMOR1: LD HL,(IMNXT) ; Else, set actual end of messages count DEC HL LD (HIMSG),HL ; Store it LD HL,NOMORE ; Tell him there are no more PNOMOR: CALL PRINTM CALL PCRLF XOR A ; Clear LD (CNTN),A ; continuous flag LD (RNEW),A ; new mail flag IF NOT ONEMNU JP MMENU ; Return to menu/prompt ELSE JP OMENU ; Return to menu/prompt ENDIF ; NOT ONEMNU ; Kill routine MKILL0: LD BC,PMWTB ; Load error return address JR MKILLX ; Go see if allowed to delete MKILLA: LD BC,PKF1A ; Load error return address MKILLX: LD A,(DELFLG) ; Delete flag set? OR A PUSH BC ; Push error return address onto stack RET Z ; and return if error POP BC ; Else, just clean up stack CALL PRINT ; and proceed to delete the message DB 'elete',0 IF ASKDEL MKILLC: CALL PRINT DB CR,LF DB 'Are you sure (y/N)? ',0 CALL DEFNO ; Get an answer JP Z,PMSKP ; If no, go get next message ENDIF ; ASKDEL MKILL: CALL CLOSE ; Close messages file while we kill CALL MIOPEN ; Open message index file LD HL,(CURREC) ; Restore this msg's record # CALL GET ; Read it LD A,(DELFLG) ; Can he delete OR A ; this message? JR NZ,MKILL1 ; If so, proceed NOKILL: CALL PRINT ; Else, tell him he can't delete DB CR,LF,'Message not killed...invalid user ID...',0 CALL CLOSE JP MF8 MKILL1: LD E,1 ; Prevent BYE from LD C,WRTLOC ; hanging up by CALL SPBDOS ; setting the write lock LD A,-1 ; Set the message status LD (MREAD),A ; to 'deleted' IF MSGTHD LD HL,(MFLNK) ; Get forward link LD (FWDMSG),HL ; Save for later LD HL,(MRLNK) ; Get reverse link LD (REVMSG),hl ; Save for later ENDIF ; MSGTHD LD HL,(RRNO1) ; Restore record number CALL PUT ; and write record back IF MSGTHD CALL UPDEL ENDIF ; MSGTHD LD HL,(CURREC) ; Restore this msg #'s record CALL GET ; Read it CALL CLOSE ; Message index file CALL ISTO ; See if to this user JR NZ,MF1 ; If not, go update receiver's mail flag LD A,(MFLAG) ; Else, get number of messages waiting OR A ; Is it already zero? JR Z,MF7 ; If yes, don't decrement it any more DEC A ; Else, show one less message LD (MFLAG),A ; Store number of messages waiting JR MF7 ; Go update receiver's mail flag MF1: LD HL,MTOF ; Save the receiver's name LD DE,MTOTMP LD BC,30 LDIR CALL USRGET ; Get his record number JR NZ,MF7 LD (RRNO1),HL ; Save that record number CALL UOPEN ; Open users file and get record MF6: LD A,(MAILF) ; Get number of messages waiting OR A ; Is it already zero? JR Z,MF7 ; If yes, don't decrement it any more DEC A ; Else, show one less message LD (MAILF),A ; Store number of messages waiting LD HL,(RRNO1) ; Get record number CALL PUT ; Write record MF7: CALL CLOSE ; Users file CALL PRINT DB CR,LF,'Message Killed...',0 MF8: LD E,0 LD C,WRTLOC CALL SPBDOS ; Clear write lock CALL MSGOPN ; Msg killed, so re-open messages file JR PMSKP ; and go to next message ; Skip to next message PMSKP0: CALL PRINT DB 'ext',0 PMSKP: LD DE,2 ; Move pointer ahead ADD IY,DE ; by 2 bytes JP PMLOP1 ; Go back for more PMBCK: CALL PRINT DB 'revious',0 PUSH IY ; Get current index POP HL ; into HL LD DE,(MSGARR) ; Get start of index XOR A ; Clear carry SBC HL,DE ; Find out where we are JP Z,PMLOP1 ; We're at the start DEC IY ; Back up one-half DEC IY ; Back up the other half JP PMLOP1 ; Go show it IF MSGTHD RTHD: LD HL,(MRLNK) ; Get reverse link JR THD FTHD: LD HL,(MFLNK) ; Get forward link THD: LD A,L ; Is the link=0? OR H JR NZ,NTHD ; If not, go display the header LD HL,(LSTMSG) ; Else, restore last message displayed number NTHD: PUSH HL ; Save message number on stack CALL MIOPEN ; Open message index file POP HL ; Restore message number TNEW: CALL GETRC ; Convert msg # to message index rec # JR C,CORTHD ; If exact match not found, tell us LD HL,(RRNO1) ; Else, get message index rec # CALL GET ; Get message index record LD A,(ACESS) ; Get user's access level CP COSYS ; Is user a SYSOP? JP NC,RDMS3 ; If yes, they can read anything CALL TDONTD ; Else, go see if user can read this one JP Z,RDMS3 ; If so, go read message LD HL,(MFLNK) ; Else, get forward link LD A,L ; Is MFLNK=0? OR H JR NZ,TNEW ; If not, go get message header LD HL,(MRLNK) ; Else, get reverse link JR TNEW ; Loop for message CORTHD: CALL CORMSG ; Print corrupt thread message JR TNEW ; and go display last message CORMSG: CALL PRINT DB CR,LF,LF,'An error has occurred! This thread is corrupt.' DB CR,LF,'Please notify the Sysop.',CR,LF,0 LD HL,(LSTMSG) ; Restore last message displayed number RET THREAD: CALL MIOPEN ; Open message index file CALL PRINT ; Display signon for function DB 'hread',0 CALL PCRLF1 CALL PRINT ; Sigon message DB '-> Messages in Current Thread <-',0 CALL PCRLF LD HL,(MREPLY) ; Start with first message in thread LD A,L ; Is MREPLY=0? OR H JR NZ,NOREP ; If not, go get message header LD HL,(LSTMSG) ; Else, restore last message displayed number NOREP: CALL GETRC ; Convert msg # to message index rec # JP C,NOTHD ; If error, tell us about it LD HL,(RRNO1) ; Get message index rec # CALL GET ; Get message header THMOR: LD A,(ACESS) ; Get user access flag CP COSYS ; Is user the SYSOP or COSYSOP JR NC,TGO ; If so, display everything LD A,(MPUBF) ; Get public/private flag OR A ; Test flag JR NZ,TCONT ; If private, skip message TGO: CALL PCRLF ; Display new line LD HL,(MNUMF) ; Get current message number CALL PB2ASC ; Display message number LD B,A ; Calculate number of spaces needed LD A,6 SUB B LD B,A CALL PAD ; Pad with spaces TA0: LD A,(MREAD) ; Get read/delete flag OR A ; Test flag LD A,' ' ; Assume its a blank JR Z,TA2 ; If not read or deleted, go print it JP M,TA1 ; If deleted, go get a 'D' LD A,'R' ; Else, get an 'R' JR TA2 TA1: LD A,'D' ; Deleted record TA2: LD (TA3+1),A ; Poke the character and print it CALL PRINT ; Display message subject TA3: DB '( ) Sub: ',0 LD HL,MSUBF ; Point to subject LD B,22 ; Only room for first 22 characters CALL PRINTL CALL PAD ; Pad out display LD A,(LENG) ; Get user's terminal width CP 55 ; If less than 55, need CR,LF JR NC,TNSMS ; Ok, continue CALL PCRLF ; Else, print CR,LF TNSMS: CALL PRINT ; Display sender DB ' Fr: ',0 LD B,16 ; Maximum number of characters to print LD HL,MFROMF ; Point to sender name CALL PRINTN INC B CALL PAD ; Pad out display LD A,(LENG) ; Get user's terminal width SUB 56 ; Already tested for this JR C,TB0 ; Ok, even 40 chars is enough for line CP 14 ; Any width over 70 should be ok JR NC,TB0 CALL PCRLF TB0: CALL PRINT ; Display Receiver DB ' To: ',0 LD B,16 ; Maximum number of characters to print LD HL,MTOF ; Point to receiver name CALL PRINTN TCONT: LD HL,(MFLNK) ; Get forward link LD A,L ; Is MFLNK=0 OR H JR Z,THEX ; If yes, exit JP NOREP ; Continue until all links displayed NOTHD: CALL CORTHD ; Print corrupt thread message JP NOREP ; and display last valid message THEX: LD HL,(LSTMSG) ; Restore last message displayed number CALL GETRC ; Convert msg # to message index rec # LD HL,(RRNO1) ; Get message index rec # CALL GET ; Get last message record JP RDMS3 ; Go display header ; Deleting a record in a threaded message chain requires some attention. ; If you remove a record from the thread, you must update the forward and ; reverse links of that message to reflect the new links. ; MSG 100 MSG 140 MSG 180 ; flink 140 flink 180 flink 220 ; rlink 80 rlink 100 rlink 140 ; MSG 100 MSG 140 (Deleted) MSG 180 ; flink 180 flink 220 ; rlink 80 rlink 100 ; As you can see, the links in messages 100 and 180 have been changed to ; reflect the deletion of message 140. ; This section of code is responsible for UPdating forward and reverse ; links for a DELeted message UPDEL: PUSH HL ; Save all registers PUSH DE PUSH BC PUSH AF LD HL,(MREPLY) ; Get first message in thread LD DE,(MNUMF) ; Get current message number CALL SUBHL ; Are we deleting first msg in thread? JR NZ,UPFL ; If not, start updating fwd & rev links ; We are deleting the first message in thread (BOT), so all messages in ; the thread must be updated with the new BOT (which is the first message ; forward from the [now deleted] old BOT). REPLP: CALL GETRC ; Find message index rec # for fwd msg JR C,UPFL ; If no match, abandon update LD HL,(RRNO1) ; Else, get message index rec # PUSH HL ; Save record number CALL GET ; Get record LD HL,(FWDMSG) ; Get new BOT LD (MREPLY),HL ; Save in buffer POP HL ; Restore record number CALL PUT ; Write record LD HL,(MFLNK) ; Get next forward link LD A,L ; Is MFLNK=0 OR H ; If yes, stop update JR NZ,REPLP ; Else, continue through links ; Now correct the reverse link of the next message forward in the thread. ; This should now point to the message previous (in the thread) to the ; deleted message. UPFL: LD HL,(FWDMSG) ; Get forward link LD A,L ; Are we deleting OR H ; the last msg in thread? JR Z,UPRL ; If so, nothing to update, go do rev links CALL GETRC ; Else, convert msg # to message index rec # JR C,UPRL ; If no match, go try forward link LD HL,(RRNO1) ; Else, get message index rec # PUSH HL ; Save record number CALL GET ; Get record LD HL,(REVMSG) ; Get new reverse link LD (MRLNK),HL ; Replace reverse link POP HL ; Restore record number CALL PUT ; Save record ; Now correct the forward link of the next message back in the thread. ; This should now point to the message next (in the thread) to the ; deleted message. UPRL: LD HL,(REVMSG) ; Get reverse link message number LD A,L ; Are we deleting OR H ; the first msg in thread? JR Z,DELEX ; If so, nothing to update, done CALL GETRC ; Else, convert msg # to message index rec # JR C,DELEX ; If no match, abandon update LD HL,(RRNO1) ; Else, get message index rec # PUSH HL ; Save record number CALL GET ; Get record LD HL,(FWDMSG) ; Get new forward link LD (MFLNK),HL ; Replace forward link POP HL ; Restore record number CALL PUT ; Save record DELEX: POP AF ; Restore registers POP BC POP DE POP HL RET ENDIF ; MSGTHD ; Search titles for user specified word(s) or SENDER/RECEIVER names for ; user specified names. Display messages that match the criteria in the ; same manner as a [R]ead command. KWSRCH: CALL SETFLG ; Set up the flags CALL PRINT ; Else, see if to, from or title DB CR,LF,LF DB 'Do you wish to search for S)ender, R)eceiver or T)itle? ',0 SRCH01: CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? JR Z, SRCH01 ; Yes, do nothing CP ' ' ; Printable character? JR C,SRCH01 ; If not, get another LD HL,SRCTAB ; Start of jump decode table LD BC,SRCEND-SRCTAB; Number of entries in BC CALL FINDER ; Get jump address, if valid command JR NZ,SRCH02 ; Invalid character PUSH DE ; Stuff action address onto stack RET ; and go do it SRCH02: CALL DOBS ; Wrong character, blank it out JR SRCH01 ; and go get another one SRCTAB: DB 'SRT' SRCEND: DW KWSR1 ; Title (T) DW RSRCH ; Receiver (R) DW SSRCH ; Sender (S) KWSR1: CALL PRINT DB CR,LF,LF DB 'Enter the string for which you wish to search: ',0 LD C,20H ; Force upper case XOR A ; Echo input LD D,A ; No word wrap LD B,26 ; Maximum length CALL INPUT IF ONEMNU JP Z,OMENU ; Skip search if just hit return ELSE JP Z,MMENU ; Skip search if just hit return ENDIF ; ONEMENU LD HL,MSUBF ; Point to subject string in msg header LD (SRCHST),HL ; Save it for search routine LD A,27 ; Max length of subject string LD (SRCHLN),A ; Save it for search routine JP SNAM2 ; Go find messages RSRCH: LD A,'R' ; We are searching LD (STYPE),A ; for receivers JR SRCHNM ; Go get name SSRCH: LD A,'S' ; We are searching LD (STYPE),A ; for senders SRCHNM: CALL PRINT DB CR,LF,LF DB 'Enter the name for which you wish to search: ',0 LD C,20H ; Force upper case XOR A ; Echo input LD D,A ; No word wrap LD B,30 ; Maximum length CALL INPUT IF ONEMNU JP Z,OMENU ; Skip search if just hit return ELSE JP Z,MMENU ; Skip search if just hit return ENDIF ; ONEMENU LD A,(STYPE) ; Get sender/receiver flag CP 'S' ; Sender? LD HL,MFROMF ; Point to sender name in msg header JR Z,SNAM1 ; If so, go search for sender LD HL,MTOF ; Else, point to receiver name in msg header SNAM1: LD (SRCHST),HL ; Save it for search routine LD A,31 ; Max length of receiver string LD (SRCHLN),A ; Save it for search routine SNAM2: LD DE,(MSGARR) ; Point to messages table PUSH DE ; Popped later as IX CALL PRINT DB CR,LF,'Stand by...',0 CALL MIOPEN ; Open message index file LD (RRNO1),HL ; Read message index loop and build message array based on search string ; and user's access to each message. KWSLP: LD HL,(RRNO1) CALL GET LD DE,INBUF ; Point to search string LD HL,(SRCHST) ; and string to search LD A,(SRCHLN) ; Get max length of string to search LD B,A ; and save in in [B] as a counter PUSH HL ; Save both pointers PUSH DE SRCHLP: LD A,(DE) ; Get byte from search string OR A ; End of string? JR Z,FOUND ; Yup, so found LD C,A ; Else, save character in C LD A,(HL) ; Get byte from string being scanned OR A ; End of string? JR Z,NFOUND ; Yup, so not found DEC B ; String may not be zero terminated JR Z,NFOUND ; so test for length CALL CAPS ; Make upper case CP C ; and compare string elements JR NZ,NEXT INC HL ; Point to next byte INC DE JR SRCHLP ; Continue scan ; No match, so try rest of scanned string. NEXT: INC HL ; Point next LD DE,INBUF ; Back to begin of search string JR SRCHLP ; and scan again ; No match, so restore pointers and go get next message NFOUND: POP DE ; Restore pointers POP HL JR KWBUMP ; Go get another message ; Match, so restore pointers and go decide if this user can see it. FOUND: POP DE ; Get pointers POP HL ; and continue LD A,(ACESS) ; Is this a sysop? CP COSYS JR NC,KWSLP2 ; Show him everything CALL TDONTD ; See if we should display it JR NZ,KWBUMP ; No, go get another one KWSLP2: LD HL,MRECF ; Starting record number for message POP DE ; Get current pointer LD BC,2 LDIR ; Save message info PUSH DE ; Save pointer LD HL,MFTMP ; Count messages found INC (HL) LD C,D LD A,(007H) ; Get top of TPA DEC A ; Give us some head room SUB C JR NZ,KWBUMP ; We still have room CALL PRINT DB 'Unable to display all matching messages',BEL,CR,LF,0 JR KWSEND ; Bail out KWBUMP: LD DE,(RRNO1) ; Get record INC DE ; and up it by one LD (RRNO1),DE ; then put it back LD HL,(IMNDX) ; See if any more XOR A ; Clear carry flag SBC HL,DE JP NC,KWSLP ; More to test KWSEND: CALL CLOSE CALL PCRLF LD A,(MFTMP) ; Get number of messages found CALL PA2ASC ; and tell us about it CALL PRINT DB ' matching messages found.',0 JP MBUMP1 ; SCAN message base and display message headers (one header/line). SCAN: CALL SCNRDY ; Set flags, etc for scan CALL PCRLF ; New line CALL GETNUM ; Get starting number from user SCNFND: CALL LOCMSG ; Print message then get record # CALL GETRC ; from message index for this message SCNDLP: CALL ABORT ; Check for pause or abort JP Z,SCNEND LD HL,(RRNO1) CALL GET ; Get message header (from message index file) SCNLP0: CALL TDONTD ; See if we want to display this one JP NZ,SBUMP ; If no, go get another one SCNM3B: CALL PCRLF LD HL,(MNUMF) ; Get msg # LD (LSTMSG),HL ; Store it for HIMSG update CALL PB2ASC ; Print it LD B,A ; Calculate number of spaces needed LD A,6 SUB B LD B,A CALL PAD ; Pad with spaces A0: LD A,(MREAD) ; Has it been read? OR A LD A,' ' ; Assume its a blank JR Z,A2 ; If not read or deleted, go print it JP M,A1 ; If deleted, go get a 'D' LD A,'R' ; Else, get an 'R' JR A2 A1: LD A,'D' ; Here if deleted JR A2 A2: LD (A3+1),A ; Poke the correct letter into next CALL PRINT ; message, then print it A3: DB '( ) Sub: ',0 LD HL,MSUBF ; Point to message subject LD B,22 ; Only room for 22 chars CALL PRINTL ; Print subject CALL PAD ; Pad with spaces LD A,(LENG) ; Get user's terminal width CP 55 ; If less than 55, need CR,LF JR NC,NSCMSG ; Ok, carry on CALL PCRLF ; Else, print CR,LF LD HL,TLINES ; Don't forget the DEC (HL) ; screen line counter NSCMSG: CALL PRINT DB ' Fr: ',0 LD HL,MFROMF ; Point to sender's name LD B,16 ; Only room for 16 chars CALL PRINTN CALL PAD ; Pad with spaces LD A,(LENG) ; Get user's terminal width SUB 56 ; Already tested for this JR C,B0 ; Ok, even 40 chars is enough for line CP 14 ; Any width over 70 should be ok JR NC,B0 ; Yup, don't need new line CALL PCRLF LD HL,TLINES ; Don't forget the DEC (HL) ; screen line counter B0: CALL PRINT DB ' To: ',0 LD HL,MTOF ; Point to receiver's name LD B,16 ; Only room for 16 chars CALL PRINTN LD A,(FBYTE) ; Get user's flag byte BIT MORBIT,A ; Test the [more] toggle JR NZ,SBUMP ; If [more] toggle is off, skip LD A,(TLINES) ; Else, update screen line counter DEC A LD (TLINES),A JR NZ,SBUMP ; If screen not full, continue CALL PRINT ; Else, pause and wait for input DB CR,LF,' R)ead, E)xit, to continue ==>',0 CALL GTCHAR ; Get a character from the console CALL CAPS CP 'E' JR NZ,SCNNXT LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR NZ,SCNNX0 ; Hotkeys not on, so 'E' already printed CALL PRINT ; Else, print an 'E' DB 'E',0 SCNNX0: CALL PRINT DB 'xit',0 JR SCNEND SCNNXT: CP 'R' ; Read? JR Z,QSNEND ; Go do it LD A,(TLIN) ; Get user console length DEC A ; Leave an overlap DEC A ; of 2 lines LD (TLINES),A ; Set line counter LD A,CR ; Clear prompt from screen CALL ECHO LD B,29 ; Print the CALL PAD ; first 29 chars LD B,10 ; Now 10 CALL PAD ; more SBUMP: LD DE,(RRNO1) ; Get record INC DE ; and add one LD (RRNO1),DE LD HL,(IMNDX) ; Past the end? AND A ; Clear carry flag SBC HL,DE JP NC,SCNDLP ; Nope, so loop SCNEND: CALL CLOSE LD HL,EOFMSG » Poinô tï enä oæ filå message CALL PRINTM » Prinô it CALL EATCHR » Cleaò anù garbagå froí modeí port CALL GTCHAR » Waiô foò useò tï reaä messagå IF NOT ONEMNU JP MMENU ELSE JP OMENU ENDIF ; NOT ONEMNU QSNEND: CALL CLOSE LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR NZ,QSNEN0 ; Hotkeys not on, so 'R' already printed CALL PRINT ; Else, print an 'R' DB 'R',0 QSNEN0: CALL PRINT DB 'ead',0 LD A,(ACESS) ; Is it a sysop? CP COSYS JP NC,SYSRED ; If so, he has access to all msgs JP PMAIL ; Else, a normal [R]ead ; Post mail to another user. POSTM: LD A,(ACESS) ; Get access level CP POSTLV ; Posting messages allowed? JR NC,POSTM1 ; If yes, continue LD HL,MSGWRN ; Else, go explain CALL PRINTM CALL PRINT DB 'Enter to proceed ',0 CALL GTCHAR ; Get input IF NOT ONEMNU JP MMENU ; Back to MAIL menu ELSE JP OMENU ; Back to menu ENDIF ; NOT ONEMNU POSTM1: LD A,(ACESS) ; Get access level CP ALLLV ; Public message allowed? JR NC,PMWAIT ; If yes, skip next LD HL,ALLWRN ; Else, explain why not CALL PRINTM JP GETTO ; Go get receiver name PMWAIT: CALL PCRLF PMW01: LD A,(ACESS) ; See if this is the Sysop CP PSYSP JR NZ,GETTO ; No, so skip this bit CALL PRINT DB CR,LF,'Use your N)ame or S)ysop? ',0 CALL GTCHAR ; Get a character from the console CALL CAPS ; Make sure it's upper case CP 'N' JR Z,PMWDON LD A,'S' PMWDON: LD (SYSBYT),A ; Store answer LD L,A ; Save input character LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag LD A,L ; Restore input character JR NZ,PMWD0 ; If hotkeys not on, char already printed CALL ECHO ; Else, show it PMWD0: CALL PCRLF GETTO: NOP ; Inline modification for return CALL PRINT DB CR,'Message to? ',0 OR A ; Clear carry flag LD A,(ACESS) ; Get access level CP ALLLV ; Public messages allowed? JR C,GETTO1 ; If not, skip next CALL PRINT ; Else, offer option for 'ALL' DB ' for ALL ',0 GETTO1: CALL PRINT DB CR,LF,'______________________________<<--',CR,0 LD C,20H LD B,30 XOR A ; Echo on LD D,A ; Clear wrap flag LD (ALLFLG),A ; and the 'ALL' flag CALL INPUT JP Z,PUB ; Jump to ALL area LD HL,INBUF+29 LD DE,MTOTMP+29 LD BC,30 LDDR ; Save message TO INC HL ; Point to input ; If message was to 'SYSOP' then substitute name at label 'SYSOP' ; as message 'To' name. OTOREP: LD DE,SYSSTR ; See if to Sysop LD B,6 CALL MATCH JR NZ,POSTM2 ; If not Sysop, go search for entry LD HL,SYSOP ; Else, move name at label LD DE,MTOTMP ; SYSOP to MTO stuff LD BC,30 LDIR POSTM2: XOR A LD (PMSND),A CALL PRINT DB CR,LF,'Checking user file...',0 CALL USRGET JR Z,GOTIT CALL PRINT DB CR,'User not found, check your spelling',CR,LF,0 JP GETTO GOTIT: LD (MTOREC),HL ; Save record number for later CALL PRINT DB ' Found',CR,LF,0 LD A,(ACESS) ; Get access level CP ALLLV ; Public messages allowed? JR NC,ENTER ; If yes, skip next LD A,1 ; Else, force LD (PMSND),A ; private JR ENTER ; and then continue PUB: LD A,(ACESS) ; Get access level CP ALLLV ; Public messages allowed? IF NOT ONEMNU JP C,MMENU ; Access too low, quit ELSE JP C,OMENU ; Access too low, quit ENDIF ; NOT ONEMNU LD HL,ALLMSG CALL PRINTM CALL PCRLF LD HL,MTOTMP+29 ; Clear LD DE,MTOTMP+28 ; the message LD BC,29 ; receiver LD (HL),0 ; field LDDR ; to nulls LD HL,ALLMSG ; Move ALL INC DE ; message to LD BC,10 ; message receiver LDIR ; field CALL PCRLF PUB1: LD A,1 ; Set message LD (ALLFLG),A ; to ALL flag ENTER: LD A,(RFLAG) ; Get reply flag OR A ; Set? JR Z,ENTER1 ; If not, go get a subject CALL PRINT ; Else, show the old one DB CR,LF,'Subj: ',0 LD HL,MSBTMP ; Point to source message subject LD B,26 ; Maximum length CALL PRINTL CALL PRINT DB CR,LF,LF,'Is the above subject acceptable (Y/n)? ',0 CALL DEFYES ; Get an answer (default = yes) JR NZ,CMNTE ; If yes, go get message ENTER1: CALL PRINT ; Else, ask for new subject DB CR,LF,'Subject? to exit',CR,LF DB '__________________________<<--',CR,0 LD B,26 XOR A ; Set input flags LD D,A LD C,A CALL INPUT JP Z,ABXIT1 ; If only, abort LD HL,INBUF ; Else, move LD DE,MSBTMP ; new subject LD BC,26 ; into place LDIR CMNTE: CALL PCRLF1 LD HL,UNAME LD B,30 CALL PRINTS CALL PRINT DB ', you are allowed ',0 LD HL,MSGBUF CALL PB2ASC ; Show max message length CALL PRINT DB ' characters in this message.',CR,LF DB 'Use a space + in column 1 to add a blank line.' DB CR,LF,'Use 2 carriage returns to exit.',0 LD C,RMTOS ; BYE's 'Time on system...' function CALL SPBDOS CALL PRINT DB 'Enter text...',CR,LF,LF,0 XOR A ; Clear LD (CRS),A ; the CR counter LD (COLUMN),A ; current column number LD (LSTLN),A ; last line routine LD (PNTCHR),A ; printing character flag LD (SPCSTR),A ; the 'no space yet' flag LD HL,0 ; Ready to zero buffers LD IX,(MSG) ; Point to start of buffer LD BC,MSGBUF ; Get length of buffer ; Add any special delete characters here DEL EQU 7FH ; 'normal' delete key DELOS EQU 1FH ; ^- on the OS-1 MSLOP: PUSH BC CALL GETCH ; Get input POP BC AND 7FH CP BS ; Normal backspace? JP Z,BCKSPC ; If yes, exit CP DEL ; Delete key used for backspace? JP Z,BCKSPC ; If yes, exit CP DELOS ; Special backspace key of some sort? JP Z,BCKSPC ; If yes, exit CP TAB ; Tab key to jump ahead on line? JP Z,CHKTAB ; If yes, exit CP ' ' ; Space character? JP Z,CHKSPC ; If yes, exit, show we got one CP CR ; New line requested? CALL Z,CRLF ; If yes, display CR-LF both CP ' '+1 JR C,MSLOP ; Must be a printable character CP '~'+1 JR NC,MSLOP LD (PNTCHR),A ; If here, we have a printing character MSLOP1:CALL ENTCHR JR MSLOP ; Got printable character, add to buffer, then show it. First, see if ; if any room is left in the buffer, to continue the message. ENTCHR: PUSH AF ; Save the character DEC BC ; Buffer has a '0' to terminate LD A,B ; Check MSP of buffer length OR A ; Less than 255 characters available? JR NZ,ENTCH0 ; Nope, continue OR C ; Check LSP of buffer length JP Z,ENDBUF ; If buffer is empty now, exit LD A,C ; See how far under 255 chars. we are CP 150 ; Less than 150 characters left to go? CALL C,LSTLN ; If yes, warn user one time only ; Now check to see if line length would be exceeded. ENTCH0: LD A,(LENG) ; Get user's maximum line length DEC A ; Adjust LD D,A ; Save for test LD A,(COLUMN) ; Now get current column CP D ; Check maximum line length JP C,ENTCH7 ; If less, can continue normally LD A,(SPCSTR) ; Else, had any space characters yet? OR A JR NZ,ENTCH1 ; If yes, exit POP AF ; Else, correct the stack JR ENTCH6 ; No space char. yet, beep and exit ; Line will be too long, so remove characters from buffer back to last ; printing character in previous word, store temporarily, insert CR-LF ; then place the characters on next line and continue. ENTCH1: LD HL,TBUFF ; Get temporary buffer address LD (HL),0 ; Zero 1st character in TBUFF ; Next store current typed character into temporary buffer. If a space ; handle as CRLF unless no printing characters are on the line yet. POP AF ; Get typed character back CP ' ' ; See if it was a space JR NZ,ENTCH2 ; If not, exit LD A,(PNTCHR) ; See if printing character yet OR A JP Z,ENTCH6 ; No, beep and ignore LD A,' ' ; Restore the character CALL ENTCH8 ; Enter the space into the message JP TURNUP ; Now turn up a new line ENTCH2: INC HL ; Next TBUFF position LD (HL),A ; Store typed character temporarily ; Now get into the message buffer and pick off characters back to end of ; previous word. Store into temporary buffer, quit if a space character. ENTCH3: INC HL ; Next TBUFF position DEC IX ; Previous message buffer address LD A,(IX) ; Get the character from message buffer CP ' ' ; See if a space character, yet JR Z,ENTCH4 ; If yes, exit CP '-' ; Accomodate hyphenated words JR Z,ENTCH4 ; Exit and turn up a new line LD (HL),A ; Store the character temporarily PUSH BC PUSH HL PUSH IX CALL DOBS ; Remove it from the display POP IX POP HL POP BC JR ENTCH3 ; Keep looping until space char. found ; Found a space character or hyphen ENTCH4: INC IX ; Position to first printing character CALL TURNUP ; Display a new line on CRT ; Now start replacing characters from temporary buffer to next line ENTCH5: DEC HL ; Get previous temp. buffer position LD A,(HL) ; Get the character there OR A RET Z CALL ENTCH8 ; Display and enter the character JR ENTCH5 ; Continue until done ; Line will be too long with this character and no spaces have been sent ; yet, so can't use auto-wrap at this point. Beep and wait for a ; or backspace to some other character. ENTCH6: PUSH BC PUSH HL CALL PRINT ; Sound off as can't continue at present DB BEL,0 POP HL POP BC RET ; Return for next character ENTCH7: POP AF ; Get the character back ENTCH8: LD (IX),A ; Add to buffer CALL ECHO ; Show character INC IX ; Increment it for next character LD A,(COLUMN) ; Get current position on line INC A LD (COLUMN),A XOR A ; Meanwhile, zero it just in case LD (IX),A ; Float a 0 at end of message LD (CRS),A ; Reset the consecutive CR count RET BCKSPC: LD A,(COLUMN) ; Get position on the line OR A JP Z,MSLOP ; If zero, can't backspace DEC A LD (COLUMN),A ; This backspace go to left margin? JR NZ,BCKSP1 ; If not, exit LD (PNTCHR),A ; Else, reset flag for printing character LD (SPCSTR),A ; and flag for 'got a space character' INC A ; Set LD (CRS),A ; the consecutive CR count BCKSP1: DEC IX ; Move the buffer pointer back one LD (IX),0 ; Float a 0 at end of message INC BC PUSH BC PUSH IX CALL DOBS ; Backspace one POP IX POP BC JP MSLOP ; Check for a tab, expand up to 8 spaces if yes, quit prior to end of ; line. CHKTAB: LD A,(LENG) ; Get user's maximum line length DEC A ; Adjust LD D,A ; Save for test LD A,(COLUMN) ; Now get current column CP D ; Check maximum line length JP NC,MSLOP ; If less, can continue normally PUSH AF ; Save current column LD A,D ; Retrieve user's line length SUB 7 ; Adjust for tab LD D,A ; Put back POP AF ; Restore current column CP D ; Check for partial tab at end of line JR C,CHKT1 ; If not close to end, handle normally AND 7 ; Else, stop at normal tab before line end JP Z,MSLOP ; If at tab stop, quit CHKT1: LD A,' ' CALL ENTCHR LD A,(COLUMN) AND 7 JR NZ,CHKTAB JP MSLOP CHKSPC: LD (SPCSTR),A ; Keep track of last space received JP MSLOP1 ; Enter and display a CR-LF CRLF: CALL CRCOM ; Call common section LD A,(CRS) ; Bump # of consecutive CRs INC A ; and if over 3, quit LD (CRS),A CP 2 JP NC,EXITCR ; Two consecutive CRs, so quit LD A,CR LD (IX),A ; Store in the buffer CALL ECHO ; Show the CR on the CRT INC IX ; Increment to next position LD A,LF CALL ECHO ; Display a line feed on CRT XOR A ; Reset LD (COLUMN),A ; column counter (now in column 0) LD (PNTCHR),A ; flag for printing character LD (SPCSTR),A ; flag for 'got a space character' LD (IX),A ; Terminate message with a 0 RET CRCOM: DEC BC ; Add back one count for this CR LD A,B ; More that 256 characters left? OR A RET NZ ; If yes, everything is ok OR C ; See if out of space entirely JR Z,CRCOM1 ; If yes, reset stack and exit LD A,C ; See if only two lines left CP 150 ; Last 150 characters? JR C,LSTLN ; If yes, tell user only 2 lines left RET ; Else, return CRCOM1: POP HL ; Remove CALL CRCOM from stack ENDBUF: POP HL ; Remove CALL Z,CRLF, or CALL ENTCHR POP AF ; Clear stack INC BC ; Re-align counter DEC IX PUSH BC ; Save count PUSH IX ; Save position XOR A LD (IX),A ; Set end of message CALL PRINT DB CR,LF,LF,'++ Buffer Full ++',0 JP EXIT0 LSTLN: NOP ; Can get changed to a RET PUSH BC PUSH IX CALL PRINT DB CR,LF,'++ Only 2 lines left ++',CR,LF,0 LD A,RET ; Disable 'last line' message so it can LD (LSTLN),A ; only be shown one time POP IX POP BC RET TURNUP: CALL CRCOM ; See if any room left, etc. PUSH BC PUSH HL PUSH IX CALL PCRLF POP IX POP HL POP BC XOR A ; Reset LD (COLUMN),A ; column counter LD (PNTCHR),A ; printing character flag LD (SPCSTR),A ; 'got a space character' flag INC A LD (CRS),A RET EXITCR: POP HL ; Remove 'CALL CRLF' from stack LD HL,(MSG) LD A,(HL) CP CR ; Starts with a CR JR NZ,XITCR1 ; Nope, exit normally INC HL LD A,(HL) OR A ; Insurance.. JR NZ,XITCR1 ; Nope, exit normally LD BC,MSGBUF PUSH BC ; Reset to zero PUSH HL ; Reset to zero JP ABXIT0 XITCR1: PUSH BC ; Save buffer length for later handling PUSH IX ; Save address for later handling EXIT0: CALL PCRLF LD A,(ALLFLG) ; See if this is OR A ; a message to 'ALL' JR NZ,EXIT0B ; If so, do not offer 'Private Save' LD A,(ACESS) ; Else, get access level CP ALLLV ; Private messages allowed? JR C,EXIT0B ; If not, only offer public save CALL PRINT ; Else, offer private save DB 'P)rivate, ',0 EXIT0B: CALL PRINT DB 'S)ave, A)bort, C)ontinue, E)dit, L)ist: ',0 CALL EATCHR ; Kill any stray characters... EXIT1: CALL GTCHAR ; Get a character from the console CP CR JR Z,EXIT1 CP ' ' ; Printable character? JR C,EXIT1 ; If not, get another one LD HL,JPEXT LD BC,DBEXT-JPEXT CALL FINDER JR NZ,EXITN PUSH DE RET EXITN: CALL DOBS JR EXIT1 JPEXT: DB 'ACELPS' DBEXT: DW SAVE ; Save (S) DW PRVTA ; Private save (P) DW LISTE ; List (L) DW EDIT ; Edit (E) DW CONT ; Continue (C) DW ABXIT ; Abort (A) PRVTA: LD A,(ALLFLG) ; Ok to store as private? OR A JR NZ,EXIT1 ; If not, get another character JP PRVT ; Else, go do it ABXIT0: LD A,'A' ; For those sections CALL ECHO ; that don't print the 'A' ABXIT: CALL PRINT DB 'bort',CR,LF,'Abort this message (y/N)? ',0 CALL DEFNO ; Get an answer (default = no) JP Z,EXIT0 ; If no, show options again POP IX ; Else, abort message POP BC ; Stack housekeeping ABXIT1: XOR A ; Clear the LD (RFLAG),A ; reply flag EXIT2: NOP ; Inline mod to force return IF NOT ONEMNU JP MMENU ELSE JP OMENU ENDIF ; NOT ONEMNU EDIT: CALL PRINT DB 'dit',0 LD HL,EBUF ; Clear LD DE,EBUF+1 ; edit buffers LD BC,129 ; including ELEN LD (HL),0 ; and RLEN LDIR ; to null CALL PRINT DB CR,LF,LF DB 'You may now edit individual characters, words, or lines...' DB CR,LF,'Use a ^ to represent a carriage return...' DB CR,LF,'Enter characters you wish to change: ',0 XOR A LD C,A LD D,A LD B,64 CALL INPUT JP Z,EXIT0 LD (ELEN),A ; Save length LD C,A LD B,0 LD HL,INBUF LD DE,EBUF LDIR ; Move search edit string to buffer CALL PRINT DB CR,LF,'Enter the replacement characters: ',0 XOR A LD C,A LD D,A LD B,64 CALL INPUT LD (RLEN),A ; Save length JR Z,CHKSTR ; If no replacement then skip move LD C,A LD B,0 LD HL,INBUF LD DE,RBUF LDIR ; Move new string into place ; Replace any ^ characters with . CHKSTR: LD HL,EBUF LD B,128 CRLOP: LD A,(HL) CP '^' JR NZ,NCR LD A,CR LD (HL),A NCR: INC HL DJNZ CRLOP ; Do til all characters checked LD HL,EBUF ; Point to search string LD DE,(MSG) ; and message start SELOP: LD A,(DE) CP (HL) JR Z,SELOP0 INC DE ; Next in buffer LD A,(DE) OR A ; At end? JR NZ,SELOP ; Not yet, so try again JP NOSTR ; String not found SELOP0: PUSH DE PUSH HL ; Save current positions SELOP1: INC HL INC DE LD A,(HL) ; At end of edit string yet? OR A JR Z,SELOP2 ; Got a match LD A,(DE) CP (HL) JR Z,SELOP1 POP HL POP DE INC DE ; Point to next in buffer JR SELOP ; Go look some more SELOP2: POP HL ; Get rid of junk PUSH DE ; Save end of found string LD A,(ELEN) LD B,A LD A,(RLEN) CP B ; Compare lengths of strings JP Z,REQU JP C,RLESS ; __________________________________________ ; | While all the message routines use the | ; | stack, this section in particular is | ; | EXTREMELY stack intensive! Use a LOT | ; | of care if you change ANY of this. | ; ------------------------------------------ ; These routines enter with stack as: ; TOP - end of found string ; NEXT - start of found string ; NEXT - current pos in buffer ; NEXT - characters remaining in buffer RGTR: SUB B LD E,A LD D,0 ; DE=diff LD (DIFF),DE ; Need later POP IY ; Get end, POP BC ; start, POP IX ; current POP HL ; and chars left PUSH HL ; Now re-save chars, PUSH IX ; current, PUSH BC ; start, PUSH IY ; and end AND A ; Clear carry SBC HL,DE ; HL must be > DE JP C,LNGSTR ; Too long, so abort LD (NEWFRE),HL ; Save characters free for later PUSH IX ; End of buffer POP HL ; in HL PUSH HL ADD HL,DE ; New end in HL EX DE,HL ; DE=move to POP HL ; HL=move from PUSH IY POP BC ; BC=end of found string PUSH HL ; Save move from AND A ; Clear carry SBC HL,BC ; HL=# characters to move INC HL ; Need one more moved PUSH HL POP BC ; BC=count POP HL ; HL=move from LDDR ; Move back to open space up POP HL ; Don't need end anymore POP DE ; Move to LD HL,RBUF ; From here LD A,(RLEN) LD C,A LD B,0 ; This many LDIR POP IX POP BC LD HL,(NEWFRE) PUSH HL ; Save new characters remaining PUSH IX ; and current pos in buffer POP HL ; to HL LD DE,(DIFF) ADD HL,DE PUSH HL ; Save new position on stack JP EXIT0 RLESS: LD A,(RLEN) OR A JR Z,RL0 ; If 0 then deleting, so skip first move LD C,A LD B,0 POP HL ; Get end of found string POP DE ; Get start of found string PUSH HL ; Need end later LD HL,RBUF LDIR ; Move new string to old position JR RL1 ; Skip next unless no replacement string RL0: POP HL ; End of found string POP DE PUSH HL ; For next pop RL1: POP HL ; Move from here (DE set from LDIR or RL0) POP BC ; Get current buffer position PUSH BC ; Save again PUSH DE ; Save end of old string (in buffer) PUSH HL ; Save start of string to move up (in buffer) AND A ; Clear carry PUSH HL PUSH BC ; Reverse BC and HL POP HL POP BC SBC HL,BC ; HL=characters to move less one INC HL ; Re-align PUSH HL POP BC ; Count POP HL ; From POP DE ; To LDIR ; Move rest of string up LD A,(RLEN) LD B,A LD A,(ELEN) SUB B LD E,A LD D,0 ; DE=diff POP IX POP HL ; Get characters left ADD HL,DE ; Update count PUSH HL ; Save it PUSH IX POP HL ; Get current position AND A SBC HL,DE ; Update PUSH HL JP EXIT0 REQU: LD A,(RLEN) LD C,A LD B,0 POP DE ; Don't need end of found string address POP DE ; Get start of found string LD HL,RBUF LDIR ; Move new string into buffer JP EXIT0 LNGSTR: POP HL POP HL ; Clear stack CALL PRINT DB CR,LF,LF,'Replacement string too long.',0 JP EXIT0 NOSTR: CALL PRINT DB CR,LF,LF,'Old string not found...',0 JP EXIT0 CONT: CALL PRINT DB 'ontinue',CR,LF,LF,0 POP IX POP BC DEC BC ; Buffer full when 1 byte left (!) LD A,B OR C ; See if at end of buffer INC BC ; Re-align counter JR Z,NOCONT ; Nope, so go PUSH BC PUSH IX CALL PRINT DB 'Continue entering text...',CR,LF,LF,0 POP IX POP BC JP MSLOP ; Go for it... NOCONT: PUSH BC PUSH IX CALL PRINT DB 'Can''t continue, at end of buffer.',CR,LF DB 'Use E)dit to delete from and change message...',0 JP EXIT0 ; Display message. LISTE: CALL PRINT DB 'ist',0 CALL LIST JP EXIT0 LIST: LD IX,(MSG) ; Point to buffer LD HL,(MSG) ; Independent check for line length LD A,(TLIN) ; Get user terminal length IF SKPHDR SUB 8 ; Extra line in header if stopping ELSE SUB 7 ; Lets [more] show entire header ENDIF ; SKPHDR LD (TLINES),A ; If [more] is shown PUSH HL CALL PCRLF1 ; Start a new line POP HL XOR A ; Reset LD (COLUMN),A ; current column number LD (SPCSTR),A ; flag to show a space has been received LD (PNTCHR),A ; printing character flag ; Count the text one character at a time. If too many for the desired ; maximum line length, do a word wrap on the local display. LSTLP: LD A,(HL) ; Get the character OR A JR Z,LSTLP1 ; End of message, exit CP CR ; End of line character? JP Z,LSTNEW ; See if CR should be retained CP ' ' ; See if 01 or other non-printing JR C,LSTLP4 ; Skip any non-printing characters LSTLP1: LD A,(COLUMN) ; Increment for this character INC A LD (COLUMN),A LD A,(HL) ; Get the character back again CP CR+1 ; See if a JP C,SHOLIN ; If yes, send the new line CP ' ' JR NZ,LSTLP2 LD (SPCSTR),A JR LSTLP3 LSTLP2: LD (PNTCHR),A ; Show a printing character on the line LSTLP3: LD A,(LENG) ; Get user's maximum line length LD D,A ; Save for test LD A,(COLUMN) ; Now get current column CP D ; Check maximum line length JR NC,LSTWRP ; If less, can continue normally LSTLP4: INC HL JR LSTLP ; Get the next character ; Line was too long, see why and fix the count accordingly. LSTWRP: LD A,(HL) ; Get the character back CP ' ' ; Space at end of full line? JR Z,SHOLIN ; If yes, send the line LSTWR1: DEC HL LD A,(COLUMN) DEC A LD (COLUMN),A LD A,(SPCSTR) ; Any spaces yet? OR A JR Z,SHOLIN ; If not show what we have so far LSTWR2: LD A,(HL) CP '-' JR Z,SHOLIN CP ' ' JR NZ,LSTWR1 ; Neither of these, keep backtracking ; Ok all finished, now, display the line. SHOLIN: LD A,(IX) OR A ; Message all finished? JR Z,SHOLN1 ; Yes, go reset zero flag and return CP CR JR Z,LSTCR ; Exit if at end of this line CP ' ' JR C,LSTWR4 ; Ignore 01's and non-printing CALL ECHO ; Display the character LD A,(COLUMN) DEC A LD (COLUMN),A JR Z,LSTCR ; If zero, no so add one LSTWR4: INC IX JR SHOLIN ; Do the next character SHOLN1: XOR A ; Reset INC A ; zero flag RET ; and return ; Displays CR, resets the column counter, then checks to see if time for ; the [more] pause, returns with CR in A, not displayed yet. LSTCR: CALL LSTUP ; Display a CR and check for [more] CALL ECHO ; Display the LF CALL TWAIT ; Check for screen full, pause or abort RET Z INC HL ; Increment for this character PUSH HL ; Transfer HL address to IX POP IX JP LSTLP ; Back to work for the next line LSTUP: XOR A ; Reset LD (COLUMN),A ; column counter LD (PNTCHR),A ; printing character flag LD (SPCSTR),A ; space character flag LD A,CR CALL ECHO LD A,(CNTN) ; In non-stop continuous mode? OR A LD A,LF RET ; Into the main part of the message now, check next character after CR ; to see if a space for a blank line or new paragraph, or end of msg. LSTNEW: INC HL ; Check next character LD A,(HL) OR A ; See if end of message JR Z,LSTN3 ; If yes, handle this CR normally CP ' ' ; Is it a space for blank line? JR Z,LSTN3 ; If yes, handle normally DEC HL ; Back to normal position LD A,(PNTCHR) ; Any printing chars. on line yet? OR A JR NZ,LSTN4 ; If yes, call this a 'soft return' DEC HL ; Else check ahead of this for a space LD A,(HL) CP ' ' ; Space for a blank line, etc. JR Z,LSTN2 ; If yes, keep the 'hard return' LSTN1: INC HL ; Back to current location LD A,' ' ; Call it a space then LD (HL),A ; Put into the message for now JP LSTLP1 ; Back to work LSTN2: INC HL ; Handle normally JP LSTLP1 LSTN3: DEC HL JP LSTLP1 ; Handle normally ; Line had some printing characters, check for a trailing space. LSTN4: DEC HL LD A,(HL) CP ' ' ; Was it a trailing space? JR NZ,LSTN1 ; If not, call this a 'soft return' INC HL LD (HL),1 JP LSTLP ; Get next character, ignore this return ; End of display messages. Save the message. PRVT: CALL PRINT DB 'rivate S',0 LD A,1 LD (PMSND),A ; Make it private SAVE: CALL PRINT DB 'ave',0 SAVEP: POP IX POP BC ; Must clear stack first LD HL,MSGBUF ; Get max characters allowed AND A ; Clear carry SBC HL,BC ; HL=number characters in message INC HL ; plus one LD DE,64 ; Calculate number of 64 character CALL DIV16 ; lines in message LD A,L ; Store the number of lines LD (LINES),A ; for later LD A,D OR E ; Is there any remainder? JR Z,NOREM ; If not, skip next LD HL,LINES ; Else, add an INC (HL) ; extra line NOREM: LD A,(MFLDR) ; Get current folder IF NMFLDRS GT 1 OR A ; Is it zero (GLOBAL)? JR NZ,NOREM1 ; If not, then skip LD A,(MAXFLD) ; Else, see if user has CP 1 ; access to multiple folders JR Z,NOREM1 ; If not, then message goes in folder 1 XOR A ; Else, get folder for this msg DEC A LD (MLSEL),A CALL PRINT DB CR,LF DB 'Select message category from the following list: ' DB CR,LF,0 CALL GFLD1 ; Get the folder number from the user PUSH AF CALL PCRLF POP AF ENDIF NOREM1: LD (TRIED),A ; Save folder number for later CALL PRINT DB CR,LF,'Updating: ',0 LD E,1 ; Prevent BYE from LD C,WRTLOC ; hanging up by CALL SPBDOS ; setting the write lock LD A,(ALLFLG) ; Else, see if this is to ALL OR A JR NZ,ALLSKP ; If yes, don't flag user record ; Set flag in TO user's record so he's bumped to mail next signon. USET: CALL PRINT DB 'Users file...',0 CALL UOPEN ; Open users file LD HL,(MTOREC) ; Record # saved when we searched for TO user CALL GET ; Read TO user into buffer LD HL,MAILF ; Point to mail waiting flag INC (HL) ; and increase it by one LD HL,(RRNO1) ; Get record number CALL PUT ; and write record back CALL CLOSE CALL PRINT DB BS,BS,BS,', ',0 ; Message is to ALL, so we don't have to flag a user's record. ALLSKP: CALL PRINT DB 'Message Index file...',0 CALL MIOPEN ; Open message index file CALL CLRBUF ; Zero the buffer LD HL,(IMRNM) LD (MSTRF),HL ; Set message record number LD A,(LINES) LD (MBLKF),A ; And number lines (records) LD D,0 LD E,A ADD HL,DE ; Calc new record start, LD (IMRNM),HL ; store in index LD HL,(IMNDX) ; and bump INC HL ; number of records LD (IMNDX),HL ; in index LD (MRECF),HL ; Store rec # in message index LD (RRNO1),HL ; Set record number in buffer ; Now write the message index record # determined above. WRITE: LD HL,(IMNXT) ; Next message number LD (MNUMF),HL ; Make it this message LD HL,MTOTMP LD DE,MTOF LD BC,30 LDIR ; Set message receiver field CALL GETTIM ; Get the date and time LD HL,BDATE ; Move the date (binary) LD DE,MDATF ; into the buffer LD BC,3 LDIR LD HL,BTIME ; And the time (binary) LD DE,MTIMEF LD BC,3 LDIR LD A,(PMSND) ; Private flag LD (MPUBF),A LD DE,MSUBF LD HL,MSBTMP LD BC,26 LDIR ; Set subject field PUSH DE ; Save next buffer address ; See if this is Sysop and if he wants his name or ; 'SYSOP' used as FROM string LD A,(ACESS) ; Access level CP PSYSP ; Sysop? JR NZ,NOSYS ; No, so store user's name SYSBYT EQU $+1 ; In-code modification LD A,0 ; Else, does he want to use his name? CP 'N' JR Z,NOSYS ; Wanted to use his name LD HL,SYSSTR ; Wants to store SYSOP as name LD BC,6 JR ISYS NOSYS: LD HL,UNAME ; Get user's name LD BC,30 ISYS: POP DE LDIR LD A,(TRIED) ; Get previously saved folder number LD (MFNUMF),A ; and put in place in the record LD HL,(RRNO1) ; Get record number CALL PUT ; and write out the message index record CALL CLOSE ; Message index file ; Now update index info. LD HL,(MSTRF) ; Get record number for first line LD (STRTMP),HL ; Save it for later LD HL,(IMNXT) ; Next message number INC HL LD (IMNXT),HL ; Update next message number LD HL,IDATE ; Update index info LD DE,IDATEF LD BC,NDXLEN LDIR CALL IOPEN ; Open index file CALL PUT ; Update record CALL CLOSE ; Index closed ; And finally, write each 64 byte line to sequential records. CALL PRINT DB BS,BS,BS,', Messages file...',0 ; CALL MSGOPN LD HL,(STRTMP) ; Set record number for first line LD (RRNO1),HL LD IY,(MSG) ; Point to message LD A,(LINES) LD B,A ; Set up counter CALL WRTLP CALL CLOSE IF MSGTHD ; Update threads... First we need to scan the msgindex to find the logical ; EOT (End Of Thread). This is found when the forward link (MFLNK) = 0. ; Entry - OLDMSG = msg number of BOT (Beginning Of Thread) ; Exit - REVMSG = msg number of EOT (End Of Thread) ; - NEWMSG = msg number of newest message on system (just added by reply) UPTHD: LD A,(RFLAG) ; Get reply flag OR A ; Is flag set? JP Z,THDEND ; Only process on reply CALL MIOPEN ; Else, open message index file LD HL,(OLDMSG) ; Get BOT message number LD (REVMSG),HL ; Save it as potential EOT CALL GETRC ; Calc message record # in message index JP C,THDCOR ; If thread is corrupt, start new one! LD HL,(RRNO1) ; Else, get record number CALL GET ; Get first message in thread LD HL,(OLDMSG) ; Save as LD (MREPLY),HL ; first message in thread CHLNK: LD HL,(MFLNK) ; Get forward link of this message LD A,L ; Is it zero? OR H JR Z,GOTLNK ; If yes, skip next LD (REVMSG),HL ; Else, save for later CALL GETRC ; Convert FLNK msg # to message index rec # JP C,THDCOR ; If thread is corrupt, start new one! LD HL,(RRNO1) ; Else, get message index rec # CALL GET ; Get next message in chain JR CHLNK ; Loop until MFLNK=0 ; Record containing the EOT must now be updated to reflect the new ; forward link (message just added to system), then write the record. GOTLNK: LD HL,(IMNXT) ; Get next available message number DEC HL ; Adjust to current message number LD (MFLNK),HL ; Save in msg index for this message LD HL,(RRNO1) ; Get record number CALL PUT ; and write record ; Now get the newest message added to the system and update it. The ; forward link of the current message will be used to locate record. ; Entry - REVMSG = EOT ; - OLDMSG = BOT UPMSG: LD HL,(MFLNK) ; Get forward link CALL GETRC ; Convert msg # to message index rec # LD HL,(RRNO1) ; Get message index rec # PUSH HL ; Save on stack CALL GET ; Get record LD HL,(OLDMSG) ; Get original message in thread LD (MREPLY),HL ; Save in buffer LD HL,(REVMSG) ; Get reverse link LD (MRLNK),HL ; Save in buffer POP HL ; Restore record number CALL PUT ; Save record CALL CLOSE ; Message index file ENDIF ; MSGTHD THDEND: XOR A ; Reset LD (RFLAG),A ; reply flag (even if it wasn't set) LD E,0 LD C,WRTLOC CALL SPBDOS ; Clear write lock CALL PCRLF JP EXIT2 ; Thread is somehow corrupt. Set new message up as the start of a new ; thread. While this is a poor solution, it at least means that further ; additions to the new thread will at least be correct. This entire problem ; of corrupt threads will have to be addressed in the next release! IF MSGTHD THDCOR: LD HL,0 ; Set the LD (OLDMSG),HL ; first message in thread LD (REVMSG),HL ; and the reverse link to zero LD HL,(IMNXT) ; Get next available message number DEC HL ; Adjust to current message number LD (MFLNK),HL ; Save in msg index for later use JR UPMSG ; Now go update the current msg index ENDIF ; MSGTHD ; Clear buffer for next. CLRBUF: LD HL,RNDBUF ; Point to first byte of random buffer LD (HL),0 ; Put a null in it CLRBUF1:LD DE,RNDBUF+1 ; Now point to second byte of buffer LD BC,SYSLEN-1 ; Longest used length LDIR ; Propagate null through the buffer RET CMNT: XOR A ; Reset LD (ALLFLG),A ; 'message to ALL' flag LD A,(ACESS) ; Get access level CP ALLLV ; Public messages allowed? JR NC,CMNTA ; Access >ALLLV so skip next CALL PRINT DB CR,LF,LF,'Comments entered here are Private to the Sysop: ',0 LD A,1 LD (PMSND),A ; Force private JP CMNTB CMNTA: CALL PRINT DB CR,LF,LF DB 'Comments are Public unless Private Save is selected: ',0 XOR A LD (PMSND),A CMNTB: LD A,'N' LD (SYSBYT),A ; Clear Sysop personal message byte LD HL,SYSOP ; To SYSOP, so move name at label LD DE,MTOTMP ; SYSOP to receiver buffer LD BC,30 LDIR CALL USRGET JR NZ,CMNTC ; SYSOP not found LD (MTOREC),HL ; Else, store record # for msg counter update JP ENTER ; and go get message CMNTC: CALL PCRLF1 CALL PRINT DB 'SYSOP not found',0 IF NOT ONEMNU JP PMENU ELSE JP OMENU ENDIF ; NOT ONEMNU ; Print the last 20 callers from the CALLERS file. PCLRS: CALL PCRLF1 LD HL,CALLRS ; Point to FCB CALL OPEN ; CALLERS CP 0FFH ; Did it exist? JR NZ,PCLRS1 CALL PRINT DB CR,LF,'CALLERS file not found.',CR,LF,0 IF NOT ONEMNU JP PMENU ELSE JP OMENU ENDIF ; NOT ONEMNU PCLRS1: LD HL,PASMSG CALL PRINTM LD HL,64 LD (RRSZ1),HL ; Set record size LD HL,0 ; Get record number CALL GET64 LD HL,(RNDBUF) ; First 2 bytes of record 0 LD (RRNO1),HL ; is our starting record # LD IX,LINES LD (IX),20 ; We look at 20 or less PCLRS2: LD HL,(RRNO1) DEC HL LD A,H OR A ; Record zero? JR NZ,PCALR ; Nope.. LD A,L ; Maybe.. OR A JR Z,PCLRS3 ; Yup...get out PCALR: DEC (IX) ; Decrement the count JR Z,PCLRS3 ; Done with 20? CALL GET64 ; Nope... LD HL,RNDBUF CALL PRINTM ; Print it to the null CALL ABORT ; Check for pause or abort JR Z,PCLRS3 ; Aborted, so wrap it up JR PCLRS2 ; Else, loop til done PCLRS3: CALL CLOSE ; Callers file LD HL,EOFMSG ; Point to end of file message CALL PRINTM ; Print it CALL EATCHR ; Clear any garbage from modem port CALL GTCHAR ; Wait for user to read message IF NOT ONEMNU JP PMENU ELSE JP OMENU ENDIF ; NOT ONEMNU PUSERS: LD A,(TLIN) ; Get screen length DEC A ; Allow one line LD (TLINES),A ; Set counter CALL EATCHR ; Clear garbage CALL PRINT DB CR,LF,'Enter first character of last name or User Level (02-',0 LD A,(ACESS) ; Get user's access level CALL PA2ASC ; and print it CALL PRINT DB '): ',0 CALL EATCHR USERS1: CALL GTCHAR ; Get a character from the console CP CR ; Carriage return? JP Z,USDONE ; Yup, so abort CALL CAPS ; Make sure it's upper case LD (COUNT),A ; Save it for later LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag LD A,(COUNT) ; First, restore character JR NZ,USERS2 ; If hotkeys not on, char already printed CALL ECHO ; Else, print it USERS2: CP 'A' ; Check for A-Z entry JP C,PLEVEL ; If not, might be a numeral CP 'Z'+1 JP NC,PLEVEL CALL HASH LD (HSHREC),HL LD HL,PASMSG CALL PRINTM CALL UOPEN ; Open users file LD HL,(HSHREC) ; We now have the beginning hashed record for this user probe. USLP: CALL GET LD A,(AVAILF) ; See if active OR A JR Z,USDONE ; Not an active record, so no more users LD A,(ACESSF) ; See if active (0=deleted, 1=twit, >=2 okay) CP 2 JR C,USLP0 LD HL,UNAMEF ; See if it is the right character USNXT: LD A,(HL) ; Get a character INC HL ; Next character CP ' ' ; See if it's a space JR NZ,USNXT LD A,(HL) ; Get first char of last name LD HL,COUNT ; Get the character back CP (HL) PUSH AF ; Save status CALL Z,GOTUS POP AF ; Restore status CALL Z,TWAIT ; If valid user, go check for screen full, etc JR Z,USDONE USLP0: XOR A ; Clear the carry flag LD HL,(RRNO1) INC HL ; Next record number EX DE,HL USLP1: LD HL,(HSHREC) ; Have we checked them all? XOR A ; Clear carry flag SBC HL,DE JR Z,USDONE ; Yes, so all done LD HL,MAXU-1 ; At highest user? XOR A SBC HL,DE EX DE,HL JR NC,USLP ; Nope, so continue LD DE,0 ; Else, start at first record in file JR USLP1 ; and test again USDONE: CALL CLOSE ; Users file LD HL,EOFMSG ; Point to end of file message CALL PRINTM ; Print it CALL EATCHR ; Clear any garbage from modem port CALL GTCHAR ; Wait for user to read message IF NOT ONEMNU JP PMENU ELSE JP OMENU ENDIF ; NOT ONEMNU GOTUS: LD A,(ACESS) ; Is this a sysop? CP COSYS JR C,GOTUS0 ; Nope, skip next LD A,(ACESSF) ; Else, display user's level CALL PA2ASC CALL PRINT DB ' ',0 GOTUS0: LD B,30 LD HL,UNAMEF CALL PRINTN CALL PAD ; Pad with spaces LD BC,20 ; Move city & province/state to work buffer LD HL,CITSTF LD DE,INBUF LDIR EX DE,HL ; HL point to work buffer DEC HL ; Point to last character in buffer GOTLP: LD A,(HL) ; Get char from work buffer CP ' ' ; Find space between DEC HL ; city and province/state JR NZ,GOTLP INC HL ; Point to space LD (HL),0 ; Make it a null LD HL,INBUF ; Now point to start of buffer LD B,20 ; Maximum number of chars allowed CALL PRINTN ; Print it with first letters capitalized PUSH HL ; Save pointer CALL PAD ; Pad with spaces POP HL ; Restore pointer LD B,2 ; Print 2 characters only CALL PRINTL ; (in upper case) GOTUS1: CALL PRINT DB ' last on: ',0 LD IX,LSTONF CALL FULDAT CALL PCRLF RET PLEVEL: CP '1' ; Is it a '1' JR NZ,PLEV0 ; If not, go see if a valid number CALL GTCHAR ; Else, might want >9, so get another char CP CR ; Then, was a character entered? JR Z,PSORRY ; If not, 1 is not a valid number CP '0' ; Ensure that JR C,PSORRY ; the entered character CP '5'+1 ; is within the JR NC,PSORRY ; correct range LD (COUNT),A ; Save it for later LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR NZ,PLEVA ; If hotkeys not on, char already printed LD A,(COUNT) ; Else, restore character CALL ECHO ; and print it PLEVA: ADD A,0AH ; Make it 3AH to 3FH PLEV0: CP '2' JR C,PSORRY PUSH AF LD A,(ACESS) INC A LD B,A POP AF SUB '0' CP B JR NC,PSORRY PLEV1: LD (COUNT),A ; Save it for later CALL PCRLF1 CALL UOPEN USLVP: CALL GET LD A,(AVAILF) ; See if active OR A JR Z,USLVP0 ; Not an active record, so next record LD A,(ACESSF) ; See if right level LD HL,COUNT CP (HL) PUSH AF ; Save status CALL Z,GOTUS ; If right level, go print info POP AF ; Restore status CALL Z,TWAIT ; If right level, go check for screen full, etc JP Z,USDONE ; If abort, all done USLVP0: XOR A ; Else, clear the carry flag LD HL,(RRNO1) INC HL EX DE,HL LD HL,MAXU-1 ; At the end? XOR A SBC HL,DE EX DE,HL JR NC,USLVP ; Nope, continue JP USDONE ; Else, quit PSORRY: CALL PCRLF1 CALL PRINT DB 'Only access levels from 2 to your level allowed.',CR,LF,0 JP PUSERS ; Either Sysop or user wants to chat with the other. YAK: LD A,(ACESS) ; See if the Sysop CP PSYSP JP Z,YAKOK ; If yes, he can Yak regardless of hour IF COYAK CP COSYS ; Is it a Co-Sysop? JR Z,OKYAK ; If yes, he can page at any time ENDIF ; COYAK IF CHTBYE LD E,-1 ; See if the BYE bell is on LD C,BELLON CALL SPBDOS OR A JP Z,NOYAK ; Nope, sorry Charlie ENDIF ; CHTBYE CALL PCRLF CALL GETTIM ; Get time and convert to binary LD A,CHAT0 ; Available 'from' hour LD B,CHAT1 ; Available 'to' hour CP B ; See which is larger LD A,(BTIME) ; Get current time JR Z,OKYAK ; If same times, ok to yak JR C,YAK1 ; Asking for up to 11 pm or less, exit ; This area handles times past midnight, such as 1800 to 0100, etc. CP CHAT1 ; Get stopping time JR C,OKYAK ; If less, ok to ask for Sysop CP CHAT0 ; Get starting time JR NC,OKYAK ; If more, ok to ask for Sysop JP NOYAK ; Else not within his posted hours ; This area handles times up to 2300 (11 pm). YAK1: CP CHAT0 ; Get starting time JP C,NOYAK ; If less, do not ask for Sysop CP CHAT1 ; Get stopping time JP NC,NOYAK ; If more, do not ask for Sysop ; Ok to proceed OKYAK: CALL PRINT DB CR,LF,LF,'You have requested chat mode. ' DB 'Should I page the Sysop (y/N)? ',0 CALL EATCHR CALL DEFNO ; Get an answer IF NOT ONEMNU JP Z,PMENU ; If not, return to menu ELSE JP Z,OMENU ; If not, return to menu ENDIF ; NOT ONEMNU CALL PRINT ; Else, page the sysop DB CR,LF,'Hold on, ',0 LD B,30 LD HL,UNAME CALL PRINTN CALL PRINT DB '. You may use ^C or ^X to abort.',CR,LF,LF DB ' |',0 LD A,ALERT ; Get number of rings (from PBBSDB.HDR) LD (LINES),A ; Use lines as temporary counter LD IY,LINES ; Point to counter BARS1: CALL PRINT ; Print bars (1 bar/ring) DB '-',0 DEC (IY) ; Done with bars? JR NZ,BARS1 ; Not done, do it again CALL PRINT DB '|',CR,LF,0 LD A,ALERT ; Restore ring count LD (LINES),A ; and put it in counter CALL PRINT DB 'Ringing: ',0 ; Attempt to alert operator. RNGBEL: CALL DELAY ; Exit if operator answers CALL PRINT ; Ring the bell DB BEL,'.',0 ; and print a period DEC (IY) ; Done with alert attempts? JR NZ,RNGBEL ; No, keep trying JP NOYAK ; Else, give up ; Delay routine to wait between beeps. DELAY: CALL GETTIM ; Get date and time LD A,(BTIME+2) ; Get the current second LD (OLDSEC),A ; and save it PUSH HL ; Save registers, consume some time PUSH DE PUSH BC PUSH AF DELAY1: LD E,-1 ; Request LD C,DIRCON ; direct console input/status CALL SPBDOS ; Get console status OR A ; Character ready? JR NZ,DELAY3 ; If so, go see if it's an abort char LD C,CONSTAT ; Else, check local console CALL SPBDOS ; for character JR Z,DELAY2 ; If none, go check time LD C,CONIN ; Else, get character CALL SPBDOS ; from local console CP ESC ; Does sysop want to chat? JR Z,YAKOK ; If so, ok to yak DELAY2: CALL GETTIM ; Else, check new time LD A,(OLDSEC) ; Compare old LD HL,BTIME+2 ; seconds with current CP (HL) ; seconds JR Z,DELAY1 ; If the same, continue waiting POP AF ; Restore the registers POP BC POP DE POP HL RET ; Finished this time DELAY3: AND 31 ; Convert to equivalent CTL char CP 'K'-'@' ; Is K, k or ^K to abort? JR Z,DELAY4 ; Yes, go back to PBBS CP 'X'-'@' ; Else, is X, x or ^X to abort? JR Z,DELAY4 ; Yes, go back to PBBS CP 'C'-'@' ; Else, what about C, c or ^C? JR NZ,DELAY2 ; No, keep timing DELAY4: LD SP,STACK ; Else, reset the stack IF NOT ONEMNU JP PCMD ; Back to the menu, Sysop not available ELSE JP OCMD ; Back to the menu, Sysop not available ENDIF ; NOT ONEMNU YAKSOP: CALL PRINT ; Sysop has typed '@' to get here DB CR,LF,LF,'Sysop wants to Chat',0 ; Operator is present. YAKOK: IF DSKLOG LD E,-1 ; Ask for status LD C,SDSKLOG ; BYE DISKLOG status request CALL SPBDOS CP 77 ; 77 says "NO is set in BYE5" JR Z,YAKOK1 ; Exit with no change to the flag LD A,(DSKFLG) ; See if DISKLOG is in use OR A JR Z,YAKOK1 ; Exit if not LD E,0 ; Else turn it off during CHAT LD C,SDSKLOG CALL SPBDOS ENDIF ; DSKLOG YAKOK1: LD SP,STACK ; Fix stack LD E,-1 LD C,MXTIME ; Get max time allowed (from BYE) CALL SPBDOS LD (OLDMXT),A ; Get old max time OR A JR Z,YSPC ; Yes, special user - no limit ADD A,60 ; Else, give him an hour to chat LD E,A LD C,MXTIME ; Set max time allowed (in BYE) CALL SPBDOS YSPC: LD C,RTCCHK ; Get time on system CALL SPBDOS LD (OLDTON),A ; Save the current time on CALL PRINT DB CR,LF,LF,'Operator is available, please go ahead...',CR,LF DB 'Use three to exit.',CR,LF,0 YAKOK2: LD IY,LINES ; Consecutive counter LD (IY),3 ; Set it to 3 YAKOK3: CALL PCRLF ; New line LD B,70+15 ; Input length + 15 for simple linewrap XOR A ; Allow echo LD C,A ; No caps forced LD D,1 ; Turns linewrap on CALL INPUT ; Get line JR NZ,YAKOK2 ; If input, reset counter & continue DEC (IY) ; Else, count one JR NZ,YAKOK3 ; If not third one, continue LD A,(OLDMXT) ; Else, all done chat, so check old max time OR A ; Unlimited? JR NZ,YAKOK4 ; No, go add chat time to max time LD E,A ; Else, re-set user's max time LD C,MXTIME CALL SPBDOS IF DSKLOG LD A,(DSKFLG) ; See if DISKLOG is in use OR A IF NOT ONEMNU JP Z,PMENU ; Exit if not ELSE JP Z,OMENU ; Exit if not ENDIF ; NOT ONEMNU LD E,1 LD C,SDSKLOG ; Else turn it back ON after CHAT CALL SPBDOS ENDIF IF NOT ONEMNU JP PMENU ELSE JP OMENU ENDIF ; NOT ONEMNU ; Add time to maximum time. YAKOK4: PUSH AF ; Save old max time LD C,RTCCHK ; Get current time on system CALL SPBDOS PUSH AF ; Save it LD A,(OLDTON) ; Get old time on system LD B,A ; Save in B POP AF ; Restore old TON SUB B ; Difference equals LD B,A ; chat duration POP AF ; Restore old max time ADD A,B ; Old max + chat duration LD E,A ; equals new max time LD C,MXTIME ; Set new max time allowed CALL SPBDOS IF DSKLOG LD A,(DSKFLG) ; See if DISKLOG is in use OR A IF NOT ONEMNU JP Z,PMENU ; Exit if not ELSE JP Z,OMENU ; Exit if not ENDIF ; NOT ONEMNU LD E,1 ; Else, turn it back ON after CHAT LD C,SDSKLOG CALL SPBDOS ENDIF IF NOT ONEMNU JP PMENU ; Then return to menu ELSE JP OMENU ; Then return to menu ENDIF ; NOT ONEMNU NOYAK: LD HL,NOCHAT ; Point to message in PBBSDB.HDR CALL PRINTM ; and print it LD HL,TZONE ; Now the time zone CALL PRINTM LD HL,NCHAT1 ; And the rest of the 'no chat' message CALL PRINTM IF NOT ONEMNU JP PMENU ; Then return to menu ELSE JP OMENU ; Then return to menu ENDIF ; NOT ONEMNU ; End of YAK routine ;********************************************************************* WFOR: IF MDOS and KMD ; KMD supports 2 separate FOR files LD A,(INTAR) LD B,A LD A,(DRV1) ; Get MSDOS drive SUB 'A' ; Normalize it CP B JR NZ,WFOR1 LD E,A LD C,LOGDRV ; Set drive for FOR file CALL SPBDOS ENDIF ; MDOS and KMD WFOR1: LD HL,WHATSFOR ; Point to FOR file CALL TYPE ; and display it IF FOR8 JR Z,WFOR2 ; If aborted, skip next LD HL,WHATSF8 ; Else, point to second FOR file CALL TYPE ; and display it ENDIF ; FOR8 WFOR2: LD HL,EOFMSG CALL PRINTM CALL EATCHR WNEWWT: CALL GTCHAR ; Get input JP TDONE6 XMDM: IF MDOS and KMD ; KMD supports 2 separate upload logs LD A,(INTAR) LD B,A LD A,(DRV1) ; Get MSDOS drive SUB 'A' ; Normalize it CP B JR NZ,XMDM1 LD E,A LD C,LOGDRV ; Set drive for FOR file CALL SPBDOS ENDIF ; MDOS and KMD XMDM1: CALL ENDPBS ; Find end of PBBS + SUBS LD L,80 ; Take to next page boundary INC H ; plus 80 INC H LD (BUFEND),HL ; Starting location for work buffer LD A,(TLIN) ; Get user terminal length SUB 5 ; Initial lines/screen LD (TLINES),A ; Set initial lines/screen LD (XLFLAG),A ; Make sure XLFLAG is on XOR A LD (FPASS),A ; Set first pass flag LD HL,XMODEM ; Open the appropriate .LOG file CALL OPEN ; (name in PBBSHDR.MAC) LD HL,PASMSG CALL PRINTM LD HL,WATMSG CALL PRINTM LD DE,FCB ; Point to FCB LD C,35 ; BDOS Compute File Size function CALL SPBDOS LD DE,(BUFEND) ; Point to work buffer INC DE ; Terminate buffer with null XOR A ; NOTE: buffer starts at (BUFEND) and LD (DE),A ; is filled backwards DEC DE ; Back to start of buffer PUSH DE ; Save it CALL PRINT ; Print header DB CR,'D/U Filename Size Speed ' DB ' Date Time Uploaded by',CR,LF,0 POP DE ; Restore buffer pointer READX: LD HL,(FCB+33) ; Get random record pointer DEC HL ; Back up 1 record LD (FCB+33),HL ; Put it back INC HL ; Increment by 1 LD A,L ; Now test for zero (actually zero minus 1) OR H JP Z,TDONE ; If zero, all done DEC HL ; Else, restore it and carry on ; Read sector from source file READLP: PUSH DE ; Save work buffer pointer LD DE,DMA ; Set the DMA for our read LD C,SETDMA ; BDOS Set File Buffer Address CALL SPBDOS LD DE,FCB ; Now read the record LD C,33 ; BDOS Random Read function CALL SPBDOS POP DE ; Restore work buffer OR A ; Read ok? JR Z,GOAHED ; Yes, procede PUSH AF ; Save error number CALL PRINT DB CR,LF,'++ SOURCE FILE READ ERROR ++',CR,LF,' Error = ',0 POP AF ; Restore error number CALL PA2ASC ; Display the error in ASCII JP TDONE4 ; Then, close file and get outa here GOAHED: LD HL,DMA+127 ; Set up end of buffer LD B,128 ; The buffer will be filled up backwards LD A,(FPASS) ; Is this the first record read? OR A JR NZ,ONEMOR ; Skip next if not GOLOOP: LD A,(HL) ; Find EOF marker (1AH) DEC B ; Decrement counter CP 'Z'-40H DEC HL ; Point next JR NZ,GOLOOP ; Loop til we find it LD (FPASS),A ; Turn off first pass flag DEC HL ; Adjust pointer DEC B ; and counter ONEMOR: LD A,(HL) ; Get character AND 7FH ; Strip any high bits CP LF ; Check for end of line - LF is the marker JR Z,CHCK ; that we will use to determine each line CP 7FH JR Z,NXTONE CP 'Z'-40H ; Check for CTRL-Z, end-of-file marker JR Z,NXTONE LD (DE),A ; Store character in our working buffer DEC E ; Decrement pointers (DE = destination buffer) NXTONE: DEC L ; (HL = source buffer) DJNZ ONEMOR ; Loop til source buffer is empty JP READX ; Then, go read another record ; One line now in work buffer. Check to see if it's an upload. CHCK: DEC B ; Decrement our counter DEC L ; and source pointer PUSH HL ; Save it PUSH BC ; Save counter EX DE,HL ; HL will now be our working buffer INC L ; Point to 'direction' char (R,S,A,L or P) LD A,(HL) CP 'R' ; Was this a "Receive" file? JR Z,PRRCV ; Yes, go print it on screen CP 'P' ; Was it a Private upload? JP NZ,NOPRNT ; Nope, on to next one LD A,(ACESS) ; See if it's a Sysop CP COSYS JP C,NOPRNT ; Nope, on to next one LD (PRIVT),A ; Flag a Private u/l for later ; Print one line on screen. Also print 'Your Last Log On...' message ; if appropriate. PRRCV: INC L ; Point to speed LD A,(HL) ; Get speed LD (SPDSAV),A ; Save it for later LD A,(XLFLAG) ; Has logon msg been printed? OR A JP Z,PRRCV1 ; Yup, don't even bother testing LD A,(NUSR) ; Is this a new user OR A JP NZ,PRRCV1 ; Yup, don't even bother testing PUSH HL ; Else, save pointer LD DE,41 ; Point to upload ADD HL,DE ; date in work buffer LD DE,DATE ; Move u/l date LD BC,8 ; from buffer to LDIR ; DATE: (8 bytes) CALL CDATE ; Convert to binary at BDATE: IF EDATE ; International date format LD DE,(BDATE) ; Get month into D and day into E LD B,E ; Put day into B LD C,D ; and month into C LD (BDATE),BC ; Now put 'em back in reverse order ENDIF ; EDATE PUSH IX ; Save index regs PUSH IY LD IX,XLSTON ; Binary version of last on date LD IY,BDATE ; Binary version of u/l date from log file CALL DATDIF ; (IY) - (IX) => HL POP IY ; Restore index regs POP IX LD A,07FH ; Limit for positive is 32767 days (7FFFh) CP H POP HL ; Restore pointer first JR NC,PRRCV1 ; H < 127, positive, so don't print yet PUSH HL ; Save pointer again CALL PRINT ; Dis must be de place! DB CR,' ' DB '***>>> Your Last Log On Was HERE!!! <<<***',CR,LF,0 POP HL ; Pointer back XOR A ; Clear the LD (XLFLAG),A ; message flag CALL TWAIT ; Check for screen full, pause or abort JP Z,TDONE4 PRRCV1: LD DE,8 ; Point to DU> ADD HL,DE LD B,3 ; Print 3 characters CALL PRINTL LD A,(PRIVT) ; Was a Private u/l? OR A LD A,0 ; Zero the flag (needed or not) LD (PRIVT),A LD A,'*' ; If Private, use an asterisk JR NZ,PRRCV2 ; Yup, so skip next LD A,':' ; Else, use a colon PRRCV2: CALL ECHO LD A,' ' ; And a space CALL ECHO INC L ; Point to filename LD B,8 ; 8 characters this time CALL PRINTL LD A,'.' ; Print period between name & extent CALL ECHO LD B,3 ; 3 more characters CALL PRINTL LD DE,12 ; Now the size ADD HL,DE LD B,6 ; Space, 4 characters for size, space CALL PRINTL LD A,' ' ; Another space CALL ECHO LD A,(SPDSAV) ; Get speed SUB '0' ; Strip ASCII bias LD C,A ADD A,A ; X2 LD B,A ADD A,A ; X4 ADD A,A ; X8 ADD A,B ; X10 ADD A,C ; X11 PUSH HL ; Save pointer LD HL,BPSTBL ; Point to speed table LD E,A LD D,0 ADD HL,DE LD B,20 ; Make sure routine doesn't terminate early CALL PRINTL ; Print speed string + 2 spaces POP HL ; Restore pointer LD B,15 ; 13 characters for time/date + 2 spaces CALL PRINTL LD A,' ' ; And another space CALL ECHO LD B,20 ; Name is max of 20 characters CALL PRINTN ; Print name in mixed case CALL PCRLF ; Print CR,LF CALL TWAIT ; Check for screen full, pause or abort JP Z,TDONE4 NOPRNT: LD DE,(BUFEND) ; Else, go try next one POP BC ; Restore counter POP HL ; and source pointer LD A,B ; Make sure that source buffer OR A ; isn't empty JP Z,READX ; If so, go read another sector JP ONEMOR ; Else, go find the next entry ; Clears any garbage characters that may have been entered. EATCHR: LD E,0FFH LD C,6 CALL SPBDOS OR A JR NZ,EATCHR RET ; Check for a request to pause or abort the display. First check for screen ; full. If [more] pauses are on and screen is full, then print '[more]' ; message, wait for next character and then take appropriate action (abort, ; advance 1 line or advance a full screen). If [more] pauses are off or the ; screen is not full, the check for pause or abort. If pause requested, ; then wait for next character and take appropriate action (abort as below ; or continue). If abort requested, then print appropriate message and ; return with zero flag set. ABORT: maybe entered directly if screen full ; check is not wanted. ; Returns with Z flag set if abort requested. TWAIT: LD A,(CNTN) ; Doing continuous read? OR A JR NZ,ABORT ; Yes, so skip screen full test LD A,(FBYTE) ; Get user's flag byte BIT MORBIT,A ; Are [more] pauses on? JR NZ,ABORT ; If no, go check for abort LD A,(TLINES) ; Else, get number of lines to go DEC A ; Decrease by one LD (TLINES),A ; Put it back JR NZ,ABORT ; Screen not full, so go check for abort PUSH HL ; Save regs PUSH DE PUSH BC LD HL,MORE ; Print [more] message CALL PRINTM CALL ABRTCH ; Go get character and check it JR Z,ABORT2 ; Got an abort char, so go print abort message CP ' ' ; Else, space prints 1 line and stops again LD A,1 ; Just in case it is a space JR Z,TWAIT1 LD A,(TLIN) ; Nope, so print another full screen DEC A ; Always allow one line TWAIT1: LD (TLINES),A ; Update line counter LD HL,MORE1 ; Erase [more] message CALL PRINTM TWAIT2: POP BC ; Restore regs POP DE POP HL XOR A ; Reset INC A ; zero flag RET ABORT: PUSH HL ; Save regs PUSH DE PUSH BC LD E,0FFH ; Get a character LD C,6 ; using BDOS Direct CALL SPBDOS ; console I/O OR A ; Anything there? JR Z,TWAIT2 ; Nope, so go return with zero flag reset LD E,A ; Save the character in E AND 31 ; Convert to equivalent CTL character CP 'S'-'@' ; Else, is S,s or ^S to pause? JR Z,ABRTWT ; Yup, go wait for next character CALL ABRTC1 ; Else, go check if it's an abort character JR ABORT1 ; Now test for abort ABRTWT: CALL ABRTCH ; Get a char and check it ABORT1: JR NZ,TWAIT2 ; Nothing yet, return with zero flag reset ABORT2: CALL PRINT DB CR,LF,'++ ABORTED ++',CR,LF,0 POP BC ; Restore regs POP DE POP HL XOR A ; Set zero flag RET ; and return to caller ABRTCH: CALL GETCH ; Get input LD E,A ; Save it in E AND 31 ; Convert to equivalent CTL character ABRTC1: CP 'K'-'@' ; Is K, k or ^K to abort? RET Z ; Yup, return with zero flag set CP 'C'-'@' ; Else, is C, c or ^C to abort? RET Z ; Yup, return with zero flag set CP 'X'-'@' ; Else, what about X, x or ^X? LD A,E ; Restore the original character RET ; and return ; File has been displayed - close up and return to menu TDONE: CALL PRINT DB CR,LF,LF,'[End of listing]',CR,LF,LF,' to proceed ',0 CALL GTCHAR ; Get input TDONE4: LD DE,FCB CALL CLOSE TDONE6: IF MDOS and KMD LD E,SYSDRV ; Log to drive where system files are LD C,LOGDRV ; Restore system drive CALL SPBDOS ENDIF ; MDOS and KMD IF NOT ONEMNU JP PMENU ; Back to the start ELSE JP OMENU ENDIF ; NOT ONEMNU ; 88/05/10 ; Exit mail, asking to kill all messages to current user first. QUIT: CALL PRINT DB CR,LF,LF,'End this session (y/N)? ',0 CALL EATCHR QUITW: CALL DEFNO ; Go get an answer (default = no) JR NZ,YQUIT ; If yes, end session IF NOT ONEMNU JP PMENU ; Else, back to the command prompt ELSE JP OMENU ; Else, back to the command prompt ENDIF ; NOT ONEMNU YQUIT: CALL PCRLF1 ; Else, end the session LD A,1 LD (QFLAG),A JP KILLM JMPCPM: XOR A LD (QFLAG),A ; Not quitting. IF RSTKON LD A,(ACESS) ; Get access level CP RSTKLV ; Can he enter CP/M? JP NC,KILLM ; If so, go do it LD HL,RSTMSG ; Else, tell him about it CALL PRINTM JP NOJMP ENDIF KNASK: IF (KNOWON AND NOT RSTKON) LD A,(ACESS) ; Get access level CP KNOKLV ; Ask knowledge question? JP NC,KILLM ; If not, carry on NERD0: LD HL,NRDQUS ; Else, point to question CALL PRINTM LD B,10 ; Length LD C,20H ; Force caps XOR A ; Echo on LD D,A ; No auto-return CALL INPUT ; Get location IF NOT ONEMNU JP Z,PMENU ; Not so easy! back to the menu... ELSE JP Z,OMENU ENDIF ; NOT ONEMNU LD DE,NRDANS LD B,10 ; Length to match CALL MATCH JR Z,KILLM ; Got it LD HL,NRDRSP ; Give him the sorry msg... CALL PRINTM ENDIF ; KNOWON AND NOT RSTKON IF KNOWON OR RSTKON NOJMP: CALL PRINT DB CR,LF,'Any key to continue...',0 CALL EATCHR NOJMP1: CALL GTCHAR ; Get input IF NOT ONEMNU JP PMENU ; Back to the farm... ELSE JP OMENU ENDIF ; NOT ONEMNU ENDIF ; KNOWON OR RSTKON KILLM: IF NOT NODEL LD A,(MFLAG) ; See if mail was waiting OR A JP Z,MEXIT ; No, so skip killing old msgs PUSH AF ; Save mail count for later SKILL: LD A,(ACESS) CP PSYSP ; Is this the Sysop? JP Z,SKN ; Yes, jump past 'Delete?' question CALL PRINT DB CR,LF,'Kill the ',0 POP AF ; Restore mail count PUSH AF ; and save it again CALL PA2ASC CALL PRINT DB ' message',0 POP AF ; Restore mail count CP 1 JR Z,SNP CALL PRINT DB 's',0 SNP: CALL PRINT DB ' in your queue (y/N)? ',0 CALL EATCHR SKILLW: CALL DEFNO ; Go get an answer (default = no) JR NZ,SKY ; If yes, go kill messages ENDIF ; NOT NODEL SKN: LD A,1 ; Else, set flag LD (SYSDEL),A ; and skip kill routine JP MEXIT SKY: XOR A ; Yes, kill old messages, so clear flag LD (SYSDEL),A ; and kill KILLM1: LD E,1 LD C,WRTLOC CALL SPBDOS ; Set write lock LD IY,MREAD ; Point to message number field CALL PRINT DB CR,LF,'Killing old messages...',0 CALL MIOPEN ; Open message index file LD HL,(IMNDX) ; Number of records KILMLP: CALL GET CALL ISTO ; See if to this user JR NZ,KBPREC ; No, don't delete LD (IY),-1 ; Else, set the delete flag LD HL,(RRNO1) CALL PUT KBPREC: LD HL,(RRNO1) ; Get record DEC HL ; and drop it by one LD A,H CP 0FFH ; See if any more JR NZ,KILMLP ; Yes CALL CLOSE ; Killing done, if required, so exit mail, rewriting index with new ; message counts etc. and clearing mail waiting flag in user's record. MEXIT: LD E,1 LD C,WRTLOC CALL SPBDOS ; Set write lock LD HL,WATMSG CALL PRINTM LD HL,IDATE LD DE,RNDBUF LD BC,NDXLEN ; Move index info to buffer and LDIR ; We're ready to write CALL IOPEN CALL PUT ; Write it CALL CLOSE LD A,(SYSDEL) ; Avoid reset of mail flag OR A ; See if Sysop and no msg-kill JR NZ,SDEL ; 1=no delete wanted XOR A ; Clear the mail LD (MFLAG),A ; waiting count SDEL: CALL TXPT1 ; Go write the modified user record CALL CLOSE LD E,0 LD C,WRTLOC CALL SPBDOS ; Clear write lock CALL PCRLF LD A,(QFLAG) OR A JP NZ,0000H ; Let BYE handle it IF DSKLOG LD A,(ACESS) ; See if a Sysop CP COSYS JR NC,SDONE2 ; If yes, ignore turning DISKLOG on LD A,(DSKFLG) ; See if DISKLOG was turned off OR A JR Z,SDONE2 ; If zero, was never in use LD E,1 ; Else, turn DISKLOG back on LD C,SDSKLOG CALL SPBDOS ENDIF ; DSKLOG SDONE2: LD C,11 ; Eliminate spurious character by CALL SPBDOS ; first checking console status OR A ; Anything there? JR Z,SDONE2A ; If not, carry on LD C,1 ; Else, read the character CALL SPBDOS ; (unfortunately, with echo!) SDONE2A:LD A,(ACESS) CP COSYS ; Is this a Sysop JR C,SDONE3 ; If not, continue LD A,0FFH ; Else, for systems using a 'WHEEL' byte IF ZCPR3 LD HL,(ZWHL) ; Wheel byte address LD (HL),A ; Set wheel on ELSE LD (WHEEL),A ; Give him privleges ENDIF ; ZCPR3 IF SETPATH ; Are we setting Sysop path? LD HL,SYSPATH ; New path IF ZCPR3 LD DE,(ZPTH) ; System path address ELSE LD DE,PATH ; Destination ENDIF ; ZCPR3 LD BC,PATHLEN ; Length LDIR ; Move it.. ENDIF LD DE,FAKE ; Skip any entry JP JMP ; files for Sysop SDONE3: LD A,(NOFILE) ; Has he been here once? OR A JP NZ,JUSTJMP ; Yup, just drop to CP/M IF NOT ENTY LD A,(NUSR) ; This a new user? OR A JR Z,NOTNEW LD DE,NENTRY ; New users get file named at nentry JP CPM ENDIF ; NOT ENTY NOTNEW: IF SPON AND NOT ENTY LD A,(ACESS) ; See if this user is =>7 CP 7 ; If not then = 0 JR C,NOTSP ; So not special LD DE,SPENTRY ; Get special file JP CPM ENDIF ; SPON AND NOT ENTY NOTSP: IF ENTY LD A,(ACESS) ; Get user access level CP COSYS ; Is user a SYSOP? JR NC,JUSTJMP ; If yes, do not run entry file CALL MKASCI ; Else, make access level ASCII LD (ENTRY+5),A ; Place access level in filename ENDIF ; ENTY LD DE,ENTRY JP CPM JUSTJMP: IF ALTON LD DE,ALTFILE JP CPM ENDIF JMP: LD DE,FAKE ; He's seen the file once JP CPM ; so don't show it again ; Now we write the user's record. UPUT: LD E,1 LD C,WRTLOC CALL SPBDOS ; Set write lock CALL UOPEN LD HL,(USREC) ; Get record number CALL PUT CALL CLOSE LD E,0 LD C,WRTLOC CALL SPBDOS ; Clear write lock RET UGET: CALL UOPEN ; Open users file LD HL,(USREC) ; Get this user's record number CALL GET ; Get user's info and move it to memory UMOV: LD HL,AVAILF LD DE,AVAIL LD BC,USRLEN LDIR RET WRTLP: PUSH BC ; Save counter PUSH IY ; Move message pointer POP HL ; to HL LD DE,RNDBUF ; Move one LD BC,MSGLEN ; line to the LDIR ; random buffer PUSH HL ; Put message pointer POP IY ; back into IY LD HL,(RRNO1) ; Get random record number CALL PUT64 ; Go write record to messages file LD HL,(RRNO1) ; Increment INC HL ; the random LD (RRNO1),HL ; record number POP BC ; Restore counter DJNZ WRTLP ; Loop until entire message is written out RET ;*********************************************************************** ; SUBROUTINE: GETRC ; PURPOSE: Take a message number and return the appropriate ; message index file record number for that message. ; INPUT: HL=message number to find ; OUTPUT: (RRNO1)=message index file record number ; Z flag set if exact match ; C flag set if closest available message ; USES: A, DE, HL ; CALLS: GET ;*********************************************************************** GETRC: PUSH HL ; User request - popped as DE later OR A ; Clear carry LD DE,1 ; See if user SBC HL,DE ; requested msg #1 JR NZ,GETNXT ; Nope LD (RRNO1),HL ; It's zero, go get record zero JP GETDON GETNXT: LD HL,(IMNDX) ; Number records INC HL LD (RRNO1),HL ; To record # counter GETRC1: LD HL,(RRNO1) DEC HL LD A,H CP 0FFH ; Done with records? JR Z,GETDON ; Start with record 0 LD (RRNO1),HL ; Set new record CALL GET LD A,(ACESS) CP COSYS ; Is this a Sysop JR NC,GETRC2 ; Get 'em all LD A,(MREAD) INC A ; Deleted? JR Z,GETRC1 ; Yes, next please GETRC2: POP DE ; Restore requested msg # PUSH DE ; and save it again AND A ; Clear carry flag LD HL,(MNUMF) ; Is this the SBC HL,DE ; requested message? JR Z,GETDON ; If so, all done JR C,GETRC3 ; If we passed it, go set to closed msg JR GETRC1 ; Else, try next message GETRC3: LD HL,(RRNO1) ; No exact match, so INC HL ; point to closest message LD (RRNO1),HL ; that is less than requested one GETDON: POP DE ; Clear the stack RET ; and return with either Z or C set ; Print a message to keep the user happy during long message file searches. LOCMSG: PUSH HL ; Save message number CALL PRINT DB CR,LF,'Stand by...locating messages...',CR,LF,LF,0 POP HL ; Restore message number RET ; GET SELECTION 0-F ; This routine will allow the user to select from a menu of fifteen ; selections inputing up to two decimal characters and returning ; the input in binary (00H-0FH) in register A. ; Two entry points are provided. A CALL to GET0F will accept entries ; of numbers from 0-15 and a CALL to GET1F will accept entries from ; 1-15, not allowing the entry of a 0. In either case, only accepted ; numbers will be echoed to the display. ; INPUT - None ; OUTPUT - Binary selection (00H-0FH) in register A ; Registers used: AF,BC ; Other subroutines used: GETCH, ECHO, DOBS GET1F: CALL GTCHAR ; Go get a character from the console CP '0' JR Z,GET1F JR GOTCH GET0F: CALL GETCH ; Go get a character from the console GOTCH: CP CR ; Carriage return? JR Z,GET0F ; Start over CP '1' ; Is it a '1' JR NZ,VALID ; If not, go see if a valid number CALL ECHO ; Valid character so show it CALL GTCHAR ; Else, might want >9, get another char CP CR ; Then, was a character entered? LD B,A ; Save it before jumping LD A,1 ; then prepare for a 1 RET Z ; Yup, he wants a 1 LD A,B ; Else, restore entered character CP '0' ; Ensure that JR C,GET0F ; the entered character CP '5'+1 ; is within the JR NC,NORNG ; correct range CALL ECHO ; Valid character so show it SUB 26H ; Make it 0AH to 0FH (10 to 15 decimal) RET ; Exit with binary selection in A NORNG: CALL DOBS JR GET0F VALID: CP '0' ; Is it > 0? JR C,GET0F ; If not, start again CP '9'+1 ; Else, is it < 10? JR NC,GET0F ; If not, start again CALL ECHO ; Else, show character SUB '0' ; and make it binary RET ; Exit with binary selection in A ;*********************************************************************** ; SUBROUTINE: GTCHAR ; PURPOSE: Find out if the user is using 'hotkeys' and then get a ; character from the console using the approprate input ; routine. ; INPUT: none ; OUTPUT: A = character from console ; USES: A,DE,BC ; CALLS: GETCH, GTCHCR ;*********************************************************************** GTCHAR: LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR NZ,GTCHR1 ; Hotkeys not on, so get input + CR CALL GETCH ; Else, get hotkey input RET ; and return GTCHR1: CALL GTCHCR ; Get input + CR RET ;*********************************************************************** ; SUBROUTINE: HMUPD ; PURPOSE: Determine if a user's high message pointer (HIMSG) should ; be updated and, if so, update it with number stored in ; LSTMSG. ; INPUT: none ; OUTPUT: none ; USES: A,DE,HL ; CALLS: nothing ;*********************************************************************** HMUPD: LD A,(PFLAG) ; Are we doing OR A ; a public read? RET Z ; If not, return with nothing changed IF NMFLDRS GT 1 LD A,(MFLDR) ; Only update hi msg from global folder OR A RET NZ ENDIF ; NMFLDRS GT 1 LD HL,(HIMSG) ; Get current high message pointer LD DE,(LSTMSG) ; and last message read AND A ; Clear carry flag SBC HL,DE ; and see which is larger RET NC ; If high message larger, return LD (HIMSG),DE ; Else, update high message pointer RET ;*********************************************************************** ; SUBROUTINE: DEFYES and DEFNO ; PURPOSE: Get a character from the keyboard, determine if it is ; a 'Y' (for yes), an 'N' (for no) or some other characer. ; If Y or N, print the appropriate word, if not, depending ; on whether YES or NO is the default, print the default. ; INPUT: none ; OUTPUT: Zero flag set if answer is NO or default = NO (A = 0) ; Sign flag reset if answer is YES or default = YES (A = 1) ; USES: A,HL ; CALLS: YESNO ;*********************************************************************** DEFYES: CALL YESNO JP M,PRYES RET DEFNO: CALL YESNO JP M,PRNO RET ;*********************************************************************** ; SUBROUTINE: YESNO ; PURPOSE: Get a character from the keyboard, determine if it is ; a 'Y' (for yes), an 'N' (for no) or some other characer. ; If Y or N, print the appropriate word ; INPUT: none ; OUTPUT: Zero flag set if answer is NO (A = 0) ; Sign flag reset if answer is YES (A = 1) ; Sign flag set if some other character was entered (A = -1) ; USES: A,HL ; CALLS: GTCHAR ; PRINTM ;*********************************************************************** YESNO: CALL GTCHAR ; Go get a character from the console CALL CAPS ; Make sure it's upper case CP 'Y' JR Z,PRYES CP 'N' JR Z,PRNO XOR A ; Clear zero DEC A ; Set minus RET PRYES: LD HL,YESMSG LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR Z,PRYES1 ; If hotkeys on, must print 'Y' INC HL ; Else, 'Y' already printed PRYES1: CALL PRINTM XOR A ; Clear zero INC A ; Set plus RET PRNO: LD HL,NOMSG LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag JR Z,PRNO1 ; If hotkeys on, must print 'N' INC HL ; Else, 'N' already printed PRNO1: CALL PRINTM XOR A ; Set zero flag RET ;*********************************************************************** ; SUBROUTINE: ISFROM ; PURPOSE: determine if the current loaded message index record is ; for a message from the current user ; INPUT: none ; OUTPUT: Z flag set if message from user, reset otherwise ; USES: A,BC,DE,HL ;*********************************************************************** ISFROM: LD HL,MFROMF ; Point to sender of this message CALL ISTO1 ; Use common code to test RET Z ; If from user, return with flags set LD A,(ACESS) ; Else, is this CP PSYSP ; the sysop? RET NZ ; If not, return (with no match) LD HL,MFROMF ; Else, point to sender again LD DE,SYSSTR ; and to 'SYSOP' LD B,5 ; Maximum length (of 'SYSOP') CALL MATCH ; Test 'em RET ; and return with the flags set ;*********************************************************************** ; SUBROUTINE: ISTO ; PURPOSE: determine if the current loaded message index record is ; for a message addressed to the current user ; INPUT: none ; OUTPUT: Z flag set if message from user, reset otherwise ; USES: A,BC,DE,HL ;*********************************************************************** ISTO: LD HL,MTOF ; Point to receiver of this message ISTO1: LD DE,UNAME ; and to current user LD B,30 ; Maximum length CALL MATCH ; Test 'em RET ; and return with flags set ;*********************************************************************** ; SUBROUTINE: GETNUM ; PURPOSE: get user input to start scan or read start search ; INPUT: none ; OUTPUT: random record number in messge index file to begin search ; USES: A,BC,DE,HL,IX ;*********************************************************************** GETNUM: CALL MIOPEN ; Open message index file CALL PRINT DB CR,LF,'Message number to begin ' SMSG: DB 'scan (1-',0 LD HL,(IMNXT) ; Next message number DEC HL CALL PB2ASC CALL PRINT DB ') or "NEW" for new messages: ',0 LD B,6 ; Maximum chars allowed (10000+) XOR A ; Set echo LD C,A LD D,A CALL INPUT ; Returns 0 if nothing entered JP Z,ENDNUM ; Nothing entered so quit LD (CNVRT0+1),A ; # of characters entered LD A,(HL) CALL CAPS CP 'N' ; New? JR Z,GN2 ; Yup, go do it CP '1' ; Less than 1? JR C,GN1 ; Yup, go tell him about it CP '9'+1 ; Greater than 9? JR C,GN3 ; Nope, go process a valid number GN1: CALL PRINT ; Else, tell him about it DB CR,LF,'Enter message number or "NEW" only.',CR,LF,0 POP HL ; Fix the stack IF NOT ONEMNU JP MMENU ELSE JP OMENU ENDIF ; NOT ONEMNU GN2: LD A,(SMSG) CP 's' JR Z,GN2A INC HL LD A,(HL) CP '+' ; Wants continous? CALL Z,SETCON ; If so, go set continous read OR A ; Guess not JR NZ,GN2 GN2A: LD HL,(HIMSG) INC HL ; Make it message +1 LD A,1 LD (RNEW),A JR GN6 GN3: PUSH HL GN4: LD A,(SMSG) CP 's' JR Z,GN5 INC HL LD A,(HL) CP '+' ; Wants continous? CALL Z,SETCON ; Set continous read OR A ; Guess not JP NZ,GN4 GN5: POP IX CALL CNVRT0 XOR A LD (RNEW),A ; Reset to check out of range GN6: PUSH HL POP DE LD HL,(IMNXT) ; Next message number DEC HL AND A ; Clear carry flag SBC HL,DE JR C,NANY ; Tell him no NEW or out of range PUSH DE POP HL ; Get user request back LD A,L OR A RET NZ LD A,H OR A RET NZ ENDNUM: POP HL ; Clear 'CALL GETNUM' from stack POP HL ; And message array pointer CALL CLOSE IF NOT ONEMNU JP MMENU ELSE JP OMENU ENDIF ; NOT ONEMNU NANY: POP HL ; Clean up stack LD A,(RNEW) ; Out of range "NEW" read? OR A LD HL,TOOBIG ; Tell him it's too big JP Z,PNOMOR ; Nope... LD HL,NOTANY JP PNOMOR ;*********************************************************************** ; SUBROUTINE: USRGET ; PURPOSE: See if user name at MTOTMP is in the users file ; INPUT: name must be in MTOTMP ; OUTPUT: HL=user record number, Z flag set if match, clear if not ; USES: A,DE,HL ;*********************************************************************** USRGET: CALL UOPEN ; Open users file LD HL,MTOTMP ; Get to: name LD BC,30 ; Get max number of characters LD A,' ' ; Want to find a space CPIR ; Now find it JR NZ,PBNONE ; Nope, go get a real name LD A,(HL) ; Get first char of last name CP 'A' ; Check for valid char. JR C,PBNONE ; Go get a real name CP 'Z'+1 JR NC,PBNONE ; Go get a real name CALL HASH LD (HSHREC),HL ; We now have the beginning hashed record for this user probe PMLP: CALL GET LD A,(AVAILF) ; See if active OR A JR Z,PBNONE ; Not an active record, so no user LD A,(ACESSF) ; See if active (0=deleted, 1=twit, >=2 okay) CP 2 JR C,PMLP0 LD HL,UNAMEF ; See if to user LD DE,MTOTMP LD B,30 CALL MATCH JR Z,USRGOT PMLP0: LD HL,(RRNO1) INC HL ; Next record number EX DE,HL PMLP1: LD HL,(HSHREC) ; Have we checked them all? XOR A ; Clear the carry flag SBC HL,DE JR Z,PBNONE ; Yup, so there's no user here LD HL,MAXU-1 ; Are we at highest possible user? XOR A SBC HL,DE EX DE,HL JR NC,PMLP ; Nope, continue LD DE,0 ; Else, start at first record in file JR PMLP1 ; and test again PBNONE: CALL CLOSE ; Users file XOR A ; Reset DEC A ; Set no user found flag RET USRGOT: CALL CLOSE LD HL,(RRNO1) ; Return the record number XOR A ; Set user found flag RET ;*********************************************************************** ; SUBROUTINE: TDONTD ; PURPOSE: Decide if a particular message should be shown to this user ; INPUT: Message index file record loaded ; OUTPUT: If Z flag set, then user has access, else no access ; USES: All regs ;*********************************************************************** TDONTD: LD A,(SYSFLG) ; Does Sysop want all, OR A ; including deleted? JP NZ,YEXIT ; If yes, go display it LD A,(MREAD) ; Else, is this message INC A ; deleted? JP Z,NEXIT ; If so, don't display it CALL ISFROM ; Else, see if from this user JR Z,TDNTD2 ; If yes, go see if reading 'TO ME' CALL ISTO ; Else, see if to this user JR Z,TDNTD3 ; If yes, go see if reading 'LEFT BY ME' LD A,(PFLAG) ; Else, reading PUBLIC ([R] command)? OR A JP Z,NEXIT ; If not, no display LD A,(MPUBF) ; Else, is it OR A ; a public message? JP NZ,NEXIT ; If not, no display IF NMFLDRS GT 1 TDNTD0: LD A,(MFLDR) ; Else, see if user is OR A ; reading the 'GLOBAL' folder JR Z,TDNTD1 ; If yes, see if can view this folder LD HL,MFNUMF ; Else, point to folder number ; If here, we aren't reading folder 0 (GLOBAL) read, so check to see if ; active folder value matches this message's folder number. If it does, ; continue, else go back for more. LD A,(MFLDR) ; Get the folder we are reading into 'A' CP (HL) ; Comp to this message's folder number JR NZ,NEXIT ; No, return and get another one JR YEXIT ; Else, return and proceed ; If here we are doing a GLOBAL read (all folders), but we still need ; to check if the user has access to this folder. Note that this is not ; required for a read of a specific folder, for if the user doesn't have ; access to a folder he would not be able to select the folder in the ; first place. TDNTD1: LD A,(MFNUMF) ; Get this message's folder number into [A] LD C,A ; Place into [C] LD B,0 ; Then clear [B] so [BC] has offset into table LD HL,ACCTBL ; Get address of table into [HL] ADD HL,BC ; Add bias LD A,(HL) ; Table value into [A] OR A ; Does user have access? JR Z,NEXIT ; If not, return and get another one JR YEXIT ; Else, return and proceed ENDIF ; If here, message it FROM this user. If we are reading PUBLIC ([R] command), ; then we must make sure that it is in the proper folder before we display ; it. If we are reading 'LEFT BY ME' ([V] command), then we can display it ; (folders don't matter in this command). Otherwise, we must be reading ; PRIVATE ([P] command) or PRIVATE NEW (from signin module), so don't display ; it. TDNTD2: LD A,(PFLAG) ; Reading PUBLIC? OR A JP NZ,TDNTD0 ; If so, go check folder LD A,(LFLAG) ; Else, reading 'LEFT BY ME'? OR A JR NZ,YEXIT ; If so, ok to display JR NEXIT ; Else, reading PRIVATE, so don't display ; If here, message it TO this user. If we are reading PUBLIC ([R] command), ; then we must make sure that it is in the proper folder before we display ; it. If we are reading 'LEFT BY ME' ([V] command), don't display it. If ; we are reading PRIVATE , then see if we have come from the [P] command or ; from the signin module (PRIVATE NEW). If from [P], go display it. If from ; signin, see it it has already been read. If it has, don't display it. TDNTD3: LD A,(LFLAG) ; Reading 'LEFT BY ME' ([V] command)? OR A JR NZ,NEXIT ; If so, do not display LD A,(PFLAG) ; Else, reading PUBLIC ([R] command)? OR A JP NZ,TDNTD0 ; If so, go check folder LD A,(PNEW) ; Else, reading 'PRIVATE NEW'? OR A JR Z,YEXIT ; If not, go display it LD A,(MREAD) ; Else, is it unread? OR A JP NZ,NEXIT ; If no, return and get another one ; If here, is ok to display this message. Return with the appropriate flags. YEXIT: XOR A ; Set the zero flag RET ; and return ; If here, is NOT ok to display this message. Return with the appropriate ; flags. NEXIT: XOR A ; Reset INC A ; zero flag RET ; And return ;*********************************************************************** ; SUBROUTINE: HASH ; PURPOSE: Calc pos'n in users file to start search ; INPUT: A =first character of last name ; OUTPUT: HL=pos'n in users file to begin search ; USES: A,DE,HL ;*********************************************************************** HASH: SUB 41H ; Make it 0 thru 25 ADD A,A ; Double it LD HL,HSHTBL ; Point to start of table LD E,A ; Calc pos'n in table LD D,0 ADD HL,DE LD A,(HL) ; Get starting pos'n in HL INC HL LD H,(HL) LD L,A RET ;*********************************************************************** ; SUBROUTINE: SCNRDY ; PURPOSE: Prepare to 'scan' messages ; INPUT: none ; OUTPUT: none ; USES: A,BC,DE,HL ;*********************************************************************** SCNRDY: LD HL,SN1 ; Move word 'scan' LD DE,SMSG ; into introductory LD BC,4 ; message for GETNUM LDIR ; subroutine LD A,(TLIN) ; Get lines/page DEC A ; Allow a 2 DEC A ; line overlap LD (TLINES),A ; Set line counter CALL SETFLG ; Set up flags (subrtn so others can use it) LD A,(ACESS) ; Is this CP COSYS ; a sysop? RET C ; Nope, all done LD (SYSFLG),A ; Else, set his flag (for TDONTD subrtn) RET SETFLG: XOR A ; Clear some flags (for TDONTD subrtn) LD (RNEW),A ; new public flag LD (PNEW),A ; new private flag LD (CNTN),A ; continuous read flag LD (MFTMP),A ; temporary mail count LD (LFLAG),A ; 'left by me' flag INC A ; Now set some flags LD (PFLAG),A ; public mail flag RET ;*********************************************************************** ; SUBROUTINE: FINDER ; PURPOSE: See if character is a valid command and return the ; address of that command if so ; INPUT: A = command character ; HL = start of jump command table ; BC = length of table ; OUTPUT: DE = address of command routine if valid character ; or Z flag cleared if invalid ; USES: A,BC,DE,HL ;*********************************************************************** FINDER: LD E,A ; Save input character LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag LD A,E ; Restore input character JR NZ,FINDR1 ; If hotkeys not on, don't need ECHO CALL CAPS ; Else, make sure character is upper case CALL ECHO ; and show it on screen FINDR1: CPIR ; Search table for match RET NZ ; If entry not found, return ADD HL,BC ; Else, multiply pointer by 3 ADD HL,BC ADD HL,BC LD E,(HL) ; Move address into DE INC HL LD D,(HL) RET ; Pad the display with spaces from 'PADSTR' (in PBBSDB.HDR). Number of ; spaces to print in B. PAD: LD A,B ; If B=0 OR A ; then return RET Z ; immediately LD HL,PADSTR ; Else, point to string of spaces CALL PRINTL ; Print 'B' of 'em RET ; Convert a number from 0-0FH in 'A' to an ASCII digit from 1-9 or from A-F ; in 'A'. MKASCI: ADD A,90H ; Convert to DAA ; ASCII 1 to 9 ADC A,40H ; or A to F DAA RET ;*********************************************************************** ; ; Subroutine: TCSET ; Purpose: Select and/or load a ZCPR3 TCAP (terminal definition file) ; Input: [A] = 0 - select a (new) terminal and load its TCAP record ; [A] = 1 - load user's selected TCAP record ; Output: [A] = number of selected terminal ; Uses: A, BC, DE, HL ; Calls: CAPS, CLOSE, DEFNO, ECHO, ENDPBS, GET, GTCHR, OPEN, PAD, ; PCRLF, PRINT, PRINTL, PRINTM, SPBDOS, TC0, ; ;*********************************************************************** IF TCAP TCSET: PUSH AF LD E,TCPDRV LD C,LOGDRV ; Log drive CALL SPBDOS LD E,TCPUSR LD C,LOGUSR ; Log user area CALL SPBDOS POP AF CALL TC0 PUSH AF LD E,SYSDRV LD C,LOGDRV CALL SPBDOS LD E,SYSUSR LD C,LOGUSR CALL SPBDOS POP AF RET TCMNT EQU 26 ; Number of entries per screen TC0: LD (TCVAR1),A OR A JR Z,TC2 LD A,(TCODE) LD L,A LD H,0 TC2: LD (TCVAR2),HL LD HL,TCPNAM CALL OPEN ; ZCPR3 TCAP file LD HL,128 ; Get record size LD (RRSZ1),HL ; and store it CALL ENDPBS ; Find start of available buffer LD (TCVAR9),HL ; and store that LD BC,0 LD HL,-1 PUSH HL ; Load Z3TCAP index. TC3: POP HL INC HL PUSH HL INC C PUSH BC LD (RRNO1),HL CALL GET LD HL,RNDBUF LD DE,(TCVAR9) PUSH DE LD BC,128 LDIR POP HL LD DE,16 LD B,8 ; Check for end of index. TC4: LD A,(HL) CP ' ' JR Z,TC5 ADD HL,DE LD (TCVAR9),HL DJNZ TC4 POP BC JR TC3 ; Reached end of index. TC5: ADD HL,DE ; Compute address of next block after last DJNZ TC5 POP BC ; Get record number of next block LD A,C LD (TCVAR6),A ; Save count CALL CLOSE ; Z3TCAP POP HL LD A,(TCVAR2) OR A JR Z,TC6 LD HL,(TCVAR2) DEC HL LD (TCVAR2),HL JP TC10 ; Print menu of terminals. TC6: LD A,1 ; Set LD (TCVAR7),A ; menu number CALL ENDPBS ; Find first free byte after program LD (TCVAR8),HL ; Save pointer TC7: CALL TC18 ; Print menu pointed to by HL CALL PRINT DB CR,LF,'Select',0 CALL TC29 ; First menu? LD HL,TCMSG2 ; Point to last message CALL Z,PRINTM CALL TC30 ; Last menu? LD HL,TCMSG1 ; Point to next message CALL Z,PRINTM CALL PRINT DB ' (^K to exit with NO terminal): ',0 TC8: CALL GTCHAR ; Go get a character from the console CALL CAPS ; and make sure it's upper case PUSH AF CALL PCRLF POP AF CP CR ; Carriage return? JP Z,TC14 ; If yes, go print next menu CP 'K'-'@' ; Else, abort? JR Z,TC10 ; If yes, assume 'None Requested' CP '+' ; Else, next? JP Z,TC14 ; If yes, go print next menu CP '-' ; Else, previous? JP Z,TC16 ; If yes, go print previous menu SUB 'A' ; Else, convert to digit JP C,TC13 ; Print error message LD C,A ; Result in C LD A,(TCV10) ; Number of menu items DEC A CP C ; Range error? JP C,TC13 ; Set pointer to menu entry. ; On input, C = offset in 20-terminal menu and TCVAR7 is menu (1..). LD A,(TCVAR7) ; Get menu number DEC A ; Adjust to 0 offset LD D,0 ; [DE] = number LD E,A LD HL,0 ; Initial sum LD B,TCMNT ; Multiply by number of entries TC9: ADD HL,DE ; + menu number DJNZ TC9 ; Count down ADD HL,BC ; Compute offset from record 1 for entry LD (TCVAR2),HL ; HL now contains terminal number (zero relative). TC10: LD HL,(TCVAR2) LD A,(TCVAR6) ; Get location of terminal data record 1 LD C,A ; Move it to BC LD B,0 ADD HL,BC ; HL contains random record number of terminal ; HL now contains random record number for terminal in file (zero relative). ; Re-open Z3TCAP.TCP. PUSH HL LD HL,TCPNAM CALL OPEN ; ZCPR3 TCAP file LD HL,128 LD (RRSZ1),HL POP HL ; Position to correct record and read it in. LD (RRNO1),HL CALL GET CALL CLOSE ; ZCPR3 TCAP file ; Confirm selection. LD A,(TCVAR1) ; Are we selecting OR A ; or loading a TCAP? JR NZ,TC12 ; If loading, go put TCAP in environment CALL PRINT ; Else, confirm selection DB CR,LF,' Selected Terminal is: ',0 LD HL,RNDBUF LD B,16 CALL PRINTL CALL PRINT DB ' -- Confirm (y/N)? ',0 CALL DEFNO ; Get an answer JR NZ,TC12 ; If yes, go put TCAP in environment LD HL,0 ; Else, LD (TCVAR2),HL ; reset variables JP TC6 ; and return to TCAP menu ; Place TCAP entry into ZCPR environment descriptor. TC12: LD HL,RNDBUF IF ZCPR3 LD DE,ZTCAP ; Point to TCAP in environment ELSE LD DE,SYSENV+128 ; Point to environment descriptor ENDIF LD BC,128 ; Copy 128 bytes LDIR ; Move new TCAP to environment descriptor LD A,(TCVAR2) ; Get the selected terminal number INC A ; Increment for PBBS RET ; Invalid selection. TC13: CALL PRINT DB ' -- Error: Invalid selection.',0 JP TC7 ; Advance to next menu. TC14: CALL TC30 ; At end? JR Z,TC15 CALL PRINT DB ' -- Error: Already at last menu.',0 JP TC7 TC15: LD HL,(TCVAR8) ; Point to current table LD DE,16*TCMNT ; Advance to next ADD HL,DE LD (TCVAR8),HL LD A,(TCVAR7) ; Increment menu number INC A LD (TCVAR7),A JP TC7 ; Backup to last menu. TC16: CALL TC29 ; At beginning? JR Z,TC17 CALL PRINT DB ' -- Error: Already at first menu.',0 JP TC7 TC17: LD HL,(TCVAR8) ; Point to current table LD DE,-16*TCMNT ; Backup ADD HL,DE LD (TCVAR8),HL LD A,(TCVAR7) ; Decrement menu number DEC A LD (TCVAR7),A JP TC7 ; Set flag if at 1st menu, set flag if at last menu and print menu ; in 2 columns. TC18: XOR A LD (TCV10),A ; Save the menu items LD (TCVAR3),A ; Set not at 1st menu LD (TCVAR4),A ; Set not at nth menu CALL PRINT DB CR,LF,'** Terminal Menu **',CR,LF,LF,0 ; Determine if at 1st menu. CALL ENDPBS ; Point to terminal table EX DE,HL ; in DE LD HL,(TCVAR8) ; Set 1st menu flag PUSH HL XOR A SBC HL,DE POP HL JR NZ,TC19 LD A,0FFH ; Set LD (TCVAR3),A ; first menu flag ; Determine if at nth menu. TC19: PUSH HL ; Save pointer to current table LD DE,16 ; Size of table entry LD B,TCMNT ; Entries per screen TC20: LD A,(HL) ; End? CP ' ' ; No entry? JR Z,TC21 ADD HL,DE ; Advance DJNZ TC20 JR TC22 TC21: LD A,0FFH ; Set LD (TCVAR4),A ; nth menu flag ; Determine menu bounds. TC22: LD HL,0 ; Clear pointer to col2 LD (TCVAR5),HL POP HL ; Get pointer to current table LD B,TCMNT/2 ; Try to advance entcnt/2 entries TC23: LD A,(HL) ; No next entry? CP ' ' JR Z,TC24 ADD HL,DE ; Advance to next DJNZ TC23 LD (TCVAR5),HL ; Save pointer to column 2 ; Print menu. TC24: LD HL,(TCVAR5) ; Get pointer to column 2 EX DE,HL ; in DE LD HL,(TCVAR8) ; Get pointer to column 1 LD B,TCMNT/2 ; Entcnt/2 lines max LD C,'A' ; Current letter TC25: LD A,(HL) ; Get first char? CP ' ' ; Done? RET Z LD A,C ; Output letter CALL TC27 ; Print entry EX DE,HL ; HL points to col 2 LD A,H ; Done? OR L JR Z,TC26 LD A,(HL) ; Empty? CP ' ' JR Z,TC26 LD A,C ; Get char ADD A,13 ; Add offset CALL TC27 TC26: INC C ; Increment menu letter EX DE,HL ; Restore HL/DE PUSH HL PUSH BC PUSH DE CALL PCRLF POP DE POP BC POP HL DJNZ TC25 RET ; Print entry whose letter is in A and whose text is pointed to by HL. ; Then advance HL. TC27: PUSH DE PUSH HL PUSH BC CALL ECHO ; Output char CALL PRINT DB '. ',0 POP BC POP HL PUSH BC ; Save regs LD B,16 ; 16 chars TC28: LD A,(HL) ; Get char INC HL ; Point next CALL ECHO ; Print char DJNZ TC28 PUSH HL LD B,8 ; Print 8 spaces CALL PAD ; as a separator LD HL,TCV10 INC (HL) POP HL POP BC POP DE RET ; Check to see if this is the first menu. TC29: LD A,(TCVAR3) ; Get flag OR A RET ; Check to see if this is the last menu. TC30: LD A,(TCVAR4) ; Get flag OR A RET ENDIF ; TCAP ; Print a menu of the available mail folders including folder 0 (GLOBAL) ; then get and validate input. Set UMFLDR: to the input value, but set ; the contents of MFLDR: and OLDFLD: to the real (decoded) folder value. ; After the user's input is validated, place the value in UMFLDR: and ; jump to UFLDR: to set the actual values. Unless this routine was ; called from the 'E'nter function, don't show folder 0, just return ; the selected value in 'A'. IF NMFLDRS GT 1 GFLD: XOR A LD (MLSEL),A CALL PRINT DB CR,LF,'Select a folder from the following list:',CR,LF,0 LD HL,FNAMES LD B,35 ; Maximum length to print CALL PRINTL GFLD1: CALL GFLD2 ; Display names 1 thru MAXFLDR CALL GFLD3 ; Get and validate input PUSH AF ; Save the selected value LD A,(MLSEL) ; Get flag as to type of call OR A ; Is this select from mail 'E'nter? JR Z,GFLD4 ; If non-zero, it is not, so jump POP AF ; Else, restore the requested folder CALL UFLDR2 ; Map to the real folder RET ; and return GFLD4: POP AF OR A ; Is it 0 (GLOBAL)? JP Z,UFLDR1 ; If yes no table mapping required JP UFLDR ; Else, go map the value ; Print the available folder names (1 thru MAXFLDR). GFLD2: PUSH IY LD HL,FNAME1-16 LD IY,ACCTBL ; Acccess table LD D,NMFLDRS ; Loop count LD E,0 ; For display PUSH DE PUSH HL GFLD2A: POP HL POP DE INC IY LD BC,16 ADD HL,BC LD A,(IY+00H) CP 1 JR NZ,GFLD2B PUSH DE PUSH HL CALL PCRLF POP HL POP DE INC E PUSH DE PUSH HL LD L,E LD H,0 CALL PB2ASC CALL PRINT DB '. ',0 POP HL PUSH HL LD B,16 CALL PRINTL POP HL POP DE GFLD2B: DEC D PUSH DE PUSH HL JR NZ,GFLD2A GFLD2C: POP HL POP DE POP IY RET ; Get and validate the user's input here. If MLSEL is non-zero, ; then FOLDER 0 is an invalid selection. GFLD3: CALL PRINT DB CR,LF,'Enter folder #: ',0 GFLD3A: CALL GTCHAR ; Go get a character CALL CAPS ; and make sure it's upper case LD C,A CP '0' JR C,GFLD3A LD A,(MAXFLD) ADD A,030H CP C JR C,GFLD3A LD A,(MLSEL) OR A JR Z,GFLD3B LD A,C CP '0' JR Z,GFLD3A GFLD3B: LD A,(FBYTE) ; Get the user's flag byte BIT HOTBIT,A ; and test the hotkey flag LD A,C ; Get character back JR NZ,GFLD3C ; Hotkeys not on, so get input + CR CALL ECHO ; Else, echo char to screen GFLD3C: SUB 030H LD (UMFLDR),A RET ; Select the next available folder number (higher if available or folder 1 ; if the current folder is the highest), then save the value in ; UMFLDR: and jump back to above code that actually sets the values for ; MFLDR: and OLDFLD:. GETHI: LD A,(UMFLDR) ; Get the current folder value OR A IF NOT ONEMNU JP Z,MMENU ELSE JP Z,OMENU ENDIF ; NOT ONEMNU INC A ; Increment it LD (UMFLDR),A ; and save it for now LD C,A ; Also save in 'C' LD A,(MAXFLD) ; Get maximum allowed CP C ; See if higher than max allowed LD A,C ; Restore the value JR NC,UFLDR ; If not, it is ok so go set the folder LD A,1 ; Else, set the folder to 1 LD (UMFLDR),A ; Save the value JR UFLDR ; and go set the folder for the user ; Select the next available folder number (lower if available or highest ; folder if the current folder is the lowest), then save the value in ; UMFLDR: and jump back to above code that actually sets the values for ; MFLDR: and OLDFLD:. GTLOW: LD A,(UMFLDR) ; Get the current folder value OR A ; See if zero IF NOT ONEMNU JP Z,MMENU ELSE JP Z,OMENU ENDIF ; NOT ONEMNU GTLOW2: DEC A ; Else, decrement it LD (UMFLDR),A ; And save it for now JR NZ,UFLDR ; If non zero ok, go set the folder LD A,(MAXFLD) ; Else, select the maximum folder LD (UMFLDR),A ; Save the value ; The number of the folder the user wanted is in 'A'. UFLDR: CALL UFLDR2 ; It's changed, so go map new value UFLDR1: CALL UFLDR4 ; Set the value into MFLDR and OLDFLD IF NOT ONEMNU JP MMENU ELSE JP OMENU ENDIF ; NOT ONEMNU ; Using the value in UMFLDR, scan the ACCTBL for the UMFLDRth ; folder to which the user has access, place the table position value ; in 'A' and then return. The number of the folder the user wanted ; is in 'A'. UFLDR2: LD IX,ACCTBL ; Set IX to first table entry LD C,A ; Folder user wanted into C LD D,0 ; Clear D and E for counters LD E,D UFLDR3: INC IX ; Point to next table entry INC E ; Increment table loop counter LD A,(IX+00H) ; Get access code OR A ; Is it zero? JR Z,UFLDR3 ; Yes, go check next entry INC D ; No, increment valid counter LD A,C ; Get desired folder back CP D ; Equal to valid counter? JR NZ,UFLDR3 ; No, try next entry LD A,E ; Yes, table index into A RET ; Update MFLDR: and OLDFLDR: with the value in 'A' which ; is the decoded value for the user's selected folder (UMFLDR:). UFLDR4: LD (MFLDR),A LD (OLDFLD),A RET ; Display the folder number and name of the currently active folder ; or folder 0 if global. SFLDR: CALL PRINT DB 'Current Folder is: ',0 LD A,(MFLDR) SFLDR2: LD HL,FNAMES+4 ; Point to "GLOBAL" folder OR A LD B,25 ; If "GLOBAL", need 25 places CALL NZ,CNAME ; If not zero, go set HL and B CALL PRINTL ; Then, print the name RET ; Get the folder name of the active folder and display it after ; the user's folder number (may not be the actual folder number). CNAME: LD HL,FNAME1 ; Point to folder names DEC A ; Decrement for base 0 ADD A,A ; Mulitiply by 16 ADD A,A ADD A,A ADD A,A LD B,0 LD C,A ; BC now has offset ADD HL,BC ; Add to base to form true address LD B,15 ; Not printing "GLOBAL", only 15 needed RET ENDIF ; This routine outputs the string pointed at by HL to the local console ; only, without going out the modem. String must be terminated with a ; null (0). PRLC: LD A,(HL) ; Get character OR A ; Null? RET Z ; Yes, all done LD E,A ; Setup for BDOS PUSH HL LD C,VCONOUT ; BYE's console only call CALL SPBDOS ; Send it POP HL INC HL ; Next char JR PRLC ; Loop till null found ; This routine will convert a binary number 0-99 in [A], to two ascii ; digits. ASCII is returned in [A] = most significant nibble, [B] = LSN. BINASC: CALL BINBCD ; First convert it to packed bcd PUSH HL LD (EBUF),A ; Save BCD in temp storage LD HL,EBUF ; Point to BCD number LD A,'0' ; Prepare to convert to ASCII RRD ; Convert LSN LD B,A ; Save LSN in B RRD ; Convert MSN POP HL RET ; This routine will convert a binary 0-99 number to packed BCD. ; Enter with [A]=binary number, exit with [A]=BCD number. BINBCD: PUSH HL LD HL,EBUF ; Point to temporary storage LD (HL),255 ; -1 BLP: INC (HL) ; Increment 10's counter SUB 10 ; Sub 10 each pass JR NC,BLP ADD A,10 ; Get the number back RLD ; Combine the two in temp storage LD A,(HL) ; Get BCD into [A] POP HL RET ; This type routine does not use the random file buffer(s). It has ; it's own sequential read routine and uses the default DMA buffer ; at 80h. Point to the file name with HL and call TYPE. File not ; found will cause a default file to be displayed, then return to ; the caller. TYPE: CALL OPEN ; Open text file INC A ; File found? (requires PBBSUBS.REL) JP Z,TYPERR ; If not, display default file TYPEA: PUSH HL ; Save the argument LD HL,PASMSG ; Print pause message CALL PRINTM LD A,(TLIN) ; Get user screen length OR A ; Zero? JR NZ,TYPEB ; If not, use it as counter LD A,24 ; Else, default to 24 lines TYPEB: DEC A ; Allow for [more] message LD (TLINES),A POP HL ; Restore argument LD DE,DMA ; Set DMA LD C,SETDMA ; address CALL SPBDOS ; using BDOS call TYPE1: LD BC,80H ; Set character position TYPE2: LD A,C CP 80H ; End of buffer yet? JR C,TYPE3 ; No, so skip next PUSH BC ; Else, refill buffer LD DE,FCB LD C,FREAD ; Read next block CALL SPBDOS POP BC ; Restore counters OR A ; Error? JR NZ,TYPEND ; Yup, so close file and end LD C,0 ; Else, reset char count LD HL,TBUFF ; and buffer pointer TYPE3: LD A,(HL) ; Get character AND 7FH ; Strip high bit CP EOF ; End of file? JR Z,TYPEOF ; Yup, go wrap it up CP LF ; Else, line feed? JR Z,TYPE4 ; Yup, special attention needed CP TAB ; Else, tab? JR Z,TYPTAB ; Yup, go expand it CALL TYPE5 ; None of the above, so just print it INC B ; Increment tab counter, TYPE6: INC C ; character counter INC HL ; and pointer JR TYPE2 ; Loop for next character TYPE4: CALL TYPE5 ; Print CR CALL TWAIT ; Go check for screen full, pause or abort JR Z,TYPEND ; Aborted, so go do it LD B,0 ; Else, reset tab count JR TYPE6 TYPTAB: LD A,' ' ; Tab becomes series of spaces CALL TYPE5 ; Print one INC B ; Increase count LD A,B ; At tab point? AND 7 JR NZ,TYPTAB ; Nope, so loop again JR TYPE6 ; Else, return to main loop TYPE5: PUSH HL ; Save registers PUSH BC CALL ECHO ; Print the character in A POP BC ; Restore regs POP HL RET TYPEOF: OR A ; Reset zero flag TYPEND: PUSH AF ; Save flags CALL CLOSE POP AF ; Now restore flags RET ; Done TYPERR: IF MDOS and KMD LD E,SYSDRV LD C,LOGDRV CALL SPBDOS ENDIF ; MDOS and KMD LD HL,NEWSERR ; Now the error message JP TYPE ; This routine is identical to the TYPE routine above, except that ; a file not found will cause a return to the caller, with no further ; action. TYPEIF: CALL OPENA ; Open a file w/o error checking INC A ; Check if file was found RET Z ; Not found, so just return CALL TYPEA ; Display file for user LD HL,MSGWT+3 ; Point to wait message CALL PRINTM ; and print it CALL EATCHR ; Clear garbage from input JP GTCHAR ; then go wait for a character ;*********************************************************************** ; ; Subroutine: CALTIM ; Purpose: Calculate the difference between two ASCII time strings ; returning the ASCII difference in a third string. ; Input: None ; Output: String containing the ASCII difference ; Uses: A, BC, DE, HL, IX ; ;*********************************************************************** CALTIM: LD HL,BCD$ON ; Destination for packed BCD LD DE,ONTIM ; ASCII time string CALL ASCBCD ; Convert ASCII to packed BCD LD DE,CURTIM ; HL points to destination already CALL ASCBCD ; convert again LD HL,BCDCUR+2 ; Start with seconds LD DE,BCD$ON+2 XOR A ; Clear carry PUSH AF ; and save on stack CALL BCDSUB ; Do seconds CALL BCDSUB ; and minutes EX DE,HL LD A,(DE) ; Now do the hours CP (HL) ; Check if Off < On JR NC,CALTM0 ; NC says not special case ADD A,24H ; Else, bump time off CALTM0: LD B,A ; Carry/borrow passed on stack POP AF ; so recover it now LD C,(HL) ; Get time on hours LD A,B SBC A,C DAA ; Convert result back to packed BCD LD (DE),A ; and save it for later EX DE,HL ; Now setup registers for BCD to ASCII LD DE,ELPTIM ; Point to destination for ASCII LD B,3 ; 3 groups of two digits to process LD A,30H ; Load upper nibble of A with 3 CALTLP: RLD ; Move lower nibble into place LD (DE),A ; and save it in the ASCII string INC DE ; Point to next digit RLD ; and process it LD (DE),A INC HL ; Point to next byte of BCD string INC DE ; Skip over colon separator, INC DE ; point to next ASCII character DJNZ CALTLP ; and loop until all done RET ASCBCD: LD B,3 ; Convert 3 ASCII pairs to packed BCD DBCDLP: LD A,(DE) ; ASCII xx:xx:xx location in DE RLD ; Packed BCD yyy location in HL INC DE ; Bump to next ASCII character LD A,(DE) ; and process it RLD INC DE ; Skip over colon INC DE ; and point to next ASCII pair INC HL ; Bump to next BCD byte DJNZ DBCDLP ; Loop until all done RET BCDSUB: POP IX ; Save the return address POP AF ; and restore the old Carry LD A,(DE) ; Get first packed BCD byte LD B,A ; Save it for now LD A,(HL) ; Fetch the second byte SBC A,B ; Find their difference DAA ; and convert the result back to BCD PUSH AF ; Save the carry for later JR NC,BSUB1 ; Check for underflow SUB 40H ; and correct to modulo 60 BSUB1: LD (HL),A ; Save the result DEC HL ; Bump the pointers DEC DE JP (IX) ; And return to saved address ;-------------------------------------------------------------------------- ; Data area, double labels used in different routines to avoid confusion. GMORN: DB 'Good morning, ',0 GAFT: DB 'Good afternoon, ',0 GEVE: DB 'Good evening, ',0 BREKIN: DB 'User password failure after three tries, ' DB 'possible break-in. ',CR,LF TWITIN: DB 'Well, this TWIT decided to try to call us' DB ' again, no access. ',CR,LF MSGBRK: DB CR,LF,'=== EOM ===',CR,LF EOFMSG: DB CR,LF,'End of file... to return to menu.',0 WATMSG: DB CR,LF,'Please wait...',0 PASMSG: DB CR,LF,'Type S to pause, C, K or X to abort' ENDSTR: DB CR,LF,0,1AH NEWMOR: DB CR,LF,'No more NEW private messages',0 TOOBIG: DB CR,LF,'No message that high',0 SYSSTR: DB 'SYSOP',0 ; Used as FROM when name not wanted BPSTBL: DB ' 110 bps ',0 ; BPSEED=0 DB ' 300 bps ',0 ; BPSEED=1 DB ' 450 bps ',0 ; BPSEED=2 DB ' 600 bps ',0 ; BPSEED=3 DB ' 710 bps ',0 ; BPSEED=4 DB '1200 bps ',0 ; BPSEED=5 DB '2400 bps ',0 ; BPSEED=6 DB '4800 bps ',0 ; BPSEED=7 DB '9600 bps ',0 ; BPSEED=8 DB '19200 bps ',0 ; BPSEED=9 RD1: DB 'read' SN1: DB 'scan' NOTANY: DB CR,LF,'No New messages',0 NOMORE: DB CR,LF,'No more messages',0 NONME: DB CR,' ' DB CR,'Name not found.',CR,LF DB 'Are you a New User? ',0 MORE: DB CR,' [Type to continue; C, K or X to abort]',CR,0 MORE1: DB ' ',CR,0 ; Stack area. DATA: DB 'STACKSTACKSTACKSTACKSTACKSTACKSTACKSTACKSTACKSTACKSTACK' STACK: DW 0 CCPSTK: DW 0 ; Stack storage CHRCNT: DB 0 ; Character count for word wrap transfer PNTCHR: DB 0 ; Got a printing char. for auto-wrap SPCSTR: DB 0 ; Flag to show a space has been received OLDSEC: DB 0 OLDMXT: DB 0 OLDTON: DB 0 FAKE: DW 0 SPACE: TSPACE: DW 0 ; Temporary storage COUNT: DW 0 ; Temporary storage NOFILE: DB 0 ; Re-entered PBBS, so no exit file CNTTMP: DB 0 ; Temporary storage CNTN: DB 0 ; Continous read flag (1=ON) RNEW: DB 0 ; Read all 'NEW' mail flag (1=ON) SPDSAV: DB 0 ; XMODEM speed storage location STATUS: DB 0 ; Temporary storage of user flag TMPREC: DW 0 ; Storage for user record number HSHREC: DW 0 ; Starting hash record FIRST: DB 0 ; Flag for first read FPASS: ; WHATSNEW first pass flag TRIED: DB 0 ; Tries at password NUSR: DB 0 ; New user flag (1=ON) PRIVT: ; WHATSNEW private upload flag PNEW: ; Reading 'new personal mail' flag (1=ON) TWIT: DB 0 ; Twit flag set in phone # routine QFLAG: DB 0 ; Quit flag PFLAG: DB 0 ; Reading public messages flag (1=ON) PMSND: DB 0 ; Private mail flag (for send mail) LFLAG: DB 0 ; Reading 'messages left by me' flag (1=ON) TMPFLG: DB 0 ; Temporary flag, clear before using MFTMP: DB 0 ; Temporary message waiting count ALLFLG: DB 0 ; 'Message to ALL' flag SYSFLG: DB 0 ; Sysop read-all flag (1=ON) LINES: DB 0 ; Line count for message entry TLINES: DB 0 ; TWAIT lines for [more] MSGREC: DW 0 ; Message starting record number MTOREC: DW 0 ; Record # to which user message is being sent STRTMP: DW 0 ; Starting record number for message MTOTMP: DS 30 ; Name of user to which a message is being sent MSBTMP: DS 26 ; Subject of message being sent SYSDEL: DB 0 ; Flag no-kill by Sysop BADCLK: DB 0 ; Flag to signal a bad clock return WHMSG: DW 0 ; Storage for himsg if multiple used LSTMSG: DW 0 ; Last message pointer CURREC: DW 0 ; Current msgindex record, used in kill routine DELFLG: DB 0 ; Can user delete this message (1=yes) LCOUNT: DB 0 ; Save line count for message read IF TCAP TCMSG1: DB ', or + for Next',0 TCMSG2: DB ', - for Last',0 TCVAR1: DS 1 ; Flag for auto load - if '1', no initial menu TCVAR2: DS 2 ; Hold area for terminal rec # in Z3TCAP file TCVAR3: DS 1 ; 1st menu flag TCVAR4: DS 1 ; Nth menu flag TCVAR5: DS 2 ; Pointer to column 2 entries TCVAR6: DS 1 ; Number of 1st data record TCVAR7: DS 1 ; Number of current menu TCVAR8: DS 2 ; Current table pointer TCVAR9: DS 2 ; Table load pointer TCV10: DS 1 ; Menu max counter ENDIF ; TCAP MFLDR: DS 1 ; Number of current folder being worked with OLDFLD: DS 1 ; Number of previously selected folder RMFLD: DS 1 ; Auto-reply folder number UMFLDR: DS 1 ; User's selected folder (external value) ; function - when equal to number of folders ; implemented, function is done. ACCTBL: DS 10 ; Folder access table. If the byte value is ; 0, the user doesn't have access to a given ; folder because his System level is too low. ; If the value is -1, the Sysop has blocked ; his access via PBBSMNT. MAXFLD: DS 1 ; The maximum folder to which this user has ; access. This is an external number for ; the user interface. Thus, user may have ; access to say (real) folders 1, 2, 3, 5 ; and 6, but his maximum folder would ; be 5 and his folder numbers for ; selection would be 0 (GLOBAL), 1, 2, ; 3, 4 and 5. If the user entered a ; 5, the value in UMFLDR would be 5, ; while the value in MFLDR would be ; 6 (real folder). MLSEL: DS 1 ; Flag for the folder selection routines, if ; value is zero, call to routines is from ; the mail 'W', '-', or '+' option. If ; value is -1, call to routines is from ; the mail 'E' option when the user is ; in the 'GLOBAL' mode. You can't enter ; mail into the 'GLOBAL' folder, so must ; select a valid folder. BUFEND: DW 0 ; WHATSNEW work buffer location XLSTON: DB 0,0,0 ; Storage for user's laston date for WHATSNEW XLFLAG: DB 1 ; WHATSNEW storage flag BCD$ON: DB 0,0,0 ; Storage for start time (BCD) (CALTIM:) BCDCUR: DB 0,0,0 ; Current/elapsed times go here PWDBUF: ; Temp password and EBUF: DS 64 ; edit string buffer RBUF: DS 64 ; Replacement string buffer ELEN: DB 0 ; Length of old string RLEN: DB 0 ; Length of new string NEWFRE: DW 0 ; Temp storage DIFF: DW 0 ; Ditto COLUMN: DB 0 ; Current column counter CRS: DB 0 ; Carriage return counter RFLAG: DB 0 ; Reply flag STYPE: DB 0 ; Sender/receiver flag (for msg search) SRCHLN: DB 0 ; Max length of search string SRCHST: DW 0 ; Pointer to search string (sub, to, from) IF MSGTHD FWDMSG: DW 0 ; Forward message link temp storage REVMSG: DW 0 ; Reverse message link temp storage NEWMSG: DW 0 ; New message number temp storage OLDMSG: DW 0 ; Old message number temp storage ENDIF ; MSGTHD MSG: DW 0 ; Message entry storage starts after ; linked PBBSUBS.REL file MSGARR: DW 0 ; Storage for message pointers ;*** END ;***