; HNOTE v1 17 Apr 88 ; ; Modified by Irv Hoff, for HBBS. Written by Wayne Masters for use with ; Russ Pencin's PBBS, based on Simon Ewin's EMX. ; ; Lets the user write a note to the Sysop via theHBBS message system, ; while in CP/M. May be public or private, his choice. ; ; ASEG ; Needed by M80 - else ignore any error .Z80 ORG 0100H ; JP START ; VER: DEFB 1 ; Version ; ; Include header storage area, strings and equates ; INCLUDE HBBSHDR.INC ; Options and header file INCLUDE HBBSUBS.INC ; Subroutine file INCLUDE HDOSHDR.INC ; Clock read routine ; START: LD HL,0 ADD HL,SP LD (CCPSTK),HL ; Save the return address LD SP,STACK ; Install our stack ; LD C,SETUSR LD E,0FFH CALL SPBDOS ; Get current user LD (OLDUSR),A ; And save it LD C,CURDRV CALL SPBDOS ; Get current drive LD (OLDDRV),A ; And save it LD C,SETUSR LD E,241 CALL SPBDOS ; See if BYE5 is active CP 77 JP Z,BEGIN ; Yes, proceed CALL PRINT ; Else abort DEFB CR,LF,'BYE5 not available, aborting...',CR,LF,0 JP REXIT ; Restore D/U and exit ; BEGIN: CALL PRINT ; Print all the neat stuff DEFB CR,LF,'HNOTE v',0 LD A,(VER) ; Get the version number LD L,A LD H,0 CALL PB2ASC CALL ENDBBS ; Returns HL-> last byte of file LD A,0FFH ; Flag byte for message routines LD (HL),A INC HL ; Start of the message storage buffer LD (MSG),HL LD DE,MSGBUF+128 ; Messages are multiples of 64 ADD HL,DE ; Start of general buffer XOR A LD (HL),A INC HL LD (MSGARR),HL LD A,SYSDRV ; System files are here LD E,A LD C,SELDSK CALL SPBDOS ; Log to the drive LD A,SYSUSR LD E,A LD C,SETUSR ; Log to the user area CALL SPBDOS ; ; Get index information from the disk and store it im memory for later ; CALL IOPEN CALL GET ; Get record CALL CLOSE ; Close file LD HL,IDATEF ; Move from buffer LD DE,IDATE ; To storage LD BC,NDXLEN ; This many bytes LDIR ; Move record ; ;----------------------------------------------------------------------- ; ; User is on system, and has typed NOTE. Allow the user to leave a ; private note and then exit back to CP/M. ; MAINT: LD C,LOGST ; BYE DISKLOG status request LD E,0FFH ; Ask for status CALL SPBDOS CP 77 ; 77 says "NO is set in BYE5" JR Z,MAINT1 ; Exit with no change to the flag LD A,(DSKFLG) ; See if DISK LOG is on now OR A JR Z,MAINT1 ; If not, exit LD C,LOGST ; BYE5 DISLOG status request LD E,0 ; Now turn off the DISKLOG in BYE5 CALL SPBDOS ; MAINT1: CALL GETTIM LD HL,USERS ; Address the filename CALL OPEN ; Open the file LD HL,USRLEN ; Set the record size LD (RRSZ),HL ; LD HL,(USREC) ; Get the user record number in HL CALL GET ; Get record into buffer CALL CLOSE ; And close the file LD HL,AVAILF ; Save it for caller's file update LD DE,AVAIL LD BC,USRLEN LDIR ; ;----------------------------------------------------------------------- ; CMNT: LD B,ALLLV ; See if access allows public messages LD A,(ACESS) CP B JP NC,CMNT1 ; Access >ALLLV so skip next LD A,1 LD (PFLAG),A ; Force private JP CMNT2 ; CMNT1: XOR A LD (PFLAG),A ; CMNT2: CALL SETLIN ; Set line length to user's request LD HL,SYSOP ; Was 'Sysop' so move name at label LD DE,MTOTMP ; 'Sysop' to MTO stuff LD BC,NAMLEN LDIR CALL UOPEN LD HL,USRLEN ; Record length LD (RRSZ),HL LD HL,MTOTMP ; Get To: name ; @PMHSH: LD A,(HL) ; Get a character INC HL ; Next character CP ' ' ; See if it's a space JR NZ,@PMHSH LD A,(HL) ; Get first character of last name CALL HASH LD (HSHREC),HL LD HL,(HSHREC) ; @PMLP: CALL GET LD A,(ACESSF) ; Active 0=deleted, 1=twit, >=2 okay CP 2 JR C,@PMLP0 LD HL,UNAMEF ; See if To: user LD DE,MTOTMP LD B,NAMLEN CALL MATCH OR A JR Z,@GOTIT ; @PMLP0: XOR A ; Clear the carry flag LD HL,(RRNO) INC HL ; Next record number EX DE,HL LD HL,(HSHREC) ; Have we checked all of them SBC HL,DE JR Z,PBNONE ; So there's no user here LD HL,MAXU XOR A SBC HL,DE EX DE,HL JR NC,@PMLP LD HL,0 ; load up first record in file JR @PMLP ; No, keep going ; PBNONE: CALL PRINT DEFB 'Unable to locate SYSOP user record',CR,LF,0 JP REXIT ; @GOTIT: CALL CLOSE LD HL,(RRNO) LD (MTOREC),HL ; Save record number for later ; CMNTE: CALL PRINT DEFB CR,LF,CR,LF DEFB 'You can use up to ',0 LD H,0 LD L,MSGLIN CALL PB2ASC ; Show maximum message length CALL PRINT DEFB ' lines for this message' DEFB CR,LF DEFB 'Use a space in 1st column to add a blank line' DEFB CR,LF DEFB 'Use 2 to exit',CR,LF,CR,LF DEFB 'Enter text...',CR,LF,CR,LF,0 XOR A LD (CRS),A LD (COLUMN),A ; Current column number LD (LINCNT),A LD (LSTLN),A ; Set last line routine LD (PNTCHR),A ; Zero printing character flag LD (SPCSTR),A ; Zero the 'no space yet' flag CALL LINNUM ; Show the current line number LD HL,0 ; Ready to 0 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 ; MSGLOP: PUSH BC ; Save the character counter registers CALL GETCH POP BC ; Get the character count back AND 7FH ; Remove any parity 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,MSGLOP ; Must be a printable character CP '~'+1 JR NC,MSGLOP LD (PNTCHR),A ; Have a printing character this line ; MSGLOP1:CALL ENTCHR JP MSGLOP ;..... ; CHRCNT: DEFB 0 ; Character count for word wrap transfer LINCNT: DEFB 0 ; Line count for messages PNTCHR: DEFB 0 ; Got a printing char. for auto-wrap SPCSTR: DEFB 0 ; Flag to show a space has been received ; ; 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 ; Under 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,(COLUMN) ; See what column we are in now ; LIN1 EQU $+1 ; CP 72 ; Check maximum line length JP C,ENTCH7 ; If less, can continue normally LD A,(SPCSTR) ; Had any space characters yet? OR A JR NZ,ENTCH1 ; If yes, exit POP AF ; Set the stack correctly 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: XOR A ; Zero first character in temp. buffer LD HL,TBUFF LD (HL),A ; 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, exit 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 PRINT DEFB BS,' ',BS,0 ; 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 JP 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 CALL PRINT ; Sound off as can't continue at present DEFB BEL,0 POP BC RET ; Return for next character ; ENTCH7: POP AF ; Get the character back ; ENTCH8: LD (IX),A ; Add to buffer PUSH IX ; In case BIOS uses IX CALL ECHO ; Show character POP IX ; Restore the IX value INC IX ; Increment it for next character LD A,(COLUMN) ; Get current position on line INC A LD (COLUMN),A XOR A ; In 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,MSGLOP ; 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 ; Reset flag for printing character LD (SPCSTR),A ; Reset flag for got a space character LD A,1 LD (CRS),A ; Set the consecutive CR count ; BCKSP1: DEC IX ; Move the buffer pointer back one XOR A ; Float a 0 at end of message LD (IX),A INC BC PUSH BC PUSH IX CALL PRINT DEFB BS,' ',BS,0 ; Backspace one POP IX POP BC JP MSGLOP ;..... ; ; Check for a tab, expand up to 8 spaces if yes, exit prior to end of ; line. ; CHKTAB: LD A,(COLUMN) ; Get position on line ; LIN2 EQU $+1 ; CP 72 ; At end now? JP NC,MSGLOP ; If yes, exit ; LIN3 EQU $+1 ; CP 72-7 ; Prevent a partial tab at end of line JR C,CHKT1 ; No close to end, handle normally AND 7 ; Stop at normal tab before line end JP Z,MSGLOP ; If at tab stop, exit ; CHKT1: LD A,' ' CALL ENTCHR LD A,(COLUMN) AND 7 JR NZ,CHKTAB JP MSGLOP ; CHKSPC: LD (SPCSTR),A ; Keep track of last space received JP MSGLOP1 ; ; Enter and display a CR-LF ; CRLF: CALL CRCOM ; Call common section LD A,(CRS) ; Bump # cr in a row INC A ; And if over 3 then exit LD (CRS),A CP 2 JP NC,EXITCR ; Two consecutive CR, so exit LD A,CR LD (IX),A ; Store in the buffer PUSH IX ; Save buffer address CALL ECHO ; Show the CR on the CRT POP IX ; Get the buffer address back INC IX ; Increment to next position LD A,LF CALL ECHO ; Display a line feed on CRT XOR A ; Clear the A register LD (COLUMN),A ; Show in Column 0, now LD (PNTCHR),A ; Reset flag for printing character LD (SPCSTR),A ; Reset flag for got a space character LD (IX),A ; Terminate message with a 0 CALL LINNUM ; Show next line number 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? JP C,LSTLN ; If yes, tell user only 2 lines left RET ;... ; CRCOM1: POP HL ; Remove CALL CRCOM from stack ; ENDBUF: POP AF ; Clear stack INC BC ; Re-align counter PUSH BC ; Save it DEC IX ; Back to end of block PUSH IX ; Save again XOR A LD (IX),A ; Set end of message CALL PRINT DEFB CR,LF,CR,LF DEFB '++ Buffer Full ++',0 JP EXITM ;,,,,, ; ; Puts a line number while writing a message ; LINNUM EQU $ ; IF NOT LINNOS RET ENDIF ; NOT LINNOS ;... ; IF LINNOS PUSH BC PUSH HL LD A,(LINCNT) INC A LD (LINCNT),A CP 10 JR NC,LINNUM1 CALL PRINT DEFB ' ',0 ; LINNUM1:LD A,(LINCNT) LD H,0 LD L,A CALL PB2ASC CALL PRINT DEFB ': ',0 POP HL POP BC RET ENDIF ; LINNOS ;..... ; LSTLN: NOP ; Get's changed to a RET PUSH BC PUSH IX CALL PRINT DEFB CR,LF,'++ Only 2 lines left ++',CR,LF,0 LD A,0C9H ; Disable last line message so it can LD (LSTLN),A ; only be shown the one time POP IX POP BC RET ;..... ; TURNUP: CALL CRCOM ; See if any room left, etc. PUSH BC PUSH HL PUSH IX CALL PRINT DEFB CR,LF,0 POP IX POP HL POP BC LD A,1 LD (CRS),A XOR A LD (COLUMN),A LD (PNTCHR),A LD (SPCSTR),A CALL LINNUM ; Show next line RET ;..... ; EXITCR: POP HL ; Remove 'CALL CRLF' from stack PUSH BC ; Save buffer length for later handling PUSH IX ; Save address for later handling LD IX,(MSG) INC IX LD A,(IX) OR A JR NZ,EXITM1 POP IX POP BC JP REXIT ;..... ; EXITM: CALL PRINT DEFB CR,LF,0 EXITM1: CALL PRINT DEFB CR,' ',CR,LF DEFB 'Save, Abort, Continue, Edit, List',0 LD A,(ACESS) ; From Sysop? CP 8 JR NC,EXIT2 ; Can be private ; ;;;; IF PRVSYS JR EXIT2 ; Can be private ;;;; ENDIF ; PRVSYS ; JR EXITM3 ; Exit, not allowing private ; EXIT2: CALL PRINT DEFB ', Private',0 ; EXITM3: CALL PRINT DEFB ': ',0 CALL EATCHR ; Kill any stray characters... ; EXITM4: CALL GETCH CP CR JP Z,CONT CALL CAPS CP 'A' JP Z,ABXIT CP 'C' JP Z,CONT CP 'E' JP Z,EDIT CP 'L' JP Z,LISTE ; Actual LIST is called by LISTE CP 'S' ; Want to store the message? JP Z,SAVE CP 'P' ; Requesting private? JR NZ,EXITM4 ; If not, ask again LD A,(ACESS) ; See if Sysop or assistant CP 8 JP NC,PRVT ; Yes, private is ok ; ;;;; IF PRVSYS JP PRVT ; Ok, can be private ;;;; ENDIF ; PRVSYS ; JR EXITM4 ; Else ask again ;..... ; ABXIT: CALL PRINT DEFB 'Abort',CR,LF,CR,LF DEFB 'Abort this message ? ',0 CALL GETCH CALL CAPS CP 'Y' JP Z,ABXIT1 CALL PRINT DEFB 'No',CR,LF,0 JP EXITM ;... ; ABXIT1: CALL PRINT DEFB 'Yes',CR,LF,0 POP IX POP BC JP REXIT ;,,,,, ; EDIT: XOR A ; Clear edit buffers LD HL,EBUF LD (HL),A LD DE,EBUF+1 LD BC,129 ; Including ELEN and RLEN LDIR CALL PRINT DEFB 'Edit',CR,LF,CR,LF DEFB 'Characters to change: ',0 XOR A LD C,A LD D,A LD B,64 CALL INPUT OR A JP Z,EXITM 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 DEFB CR,LF DEFB 'Change them to these: ',0 XOR A LD C,A LD D,A LD B,64 CALL INPUT LD (RLEN),A ; Save length OR A 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 ; CHKSTR: 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 ; Get start POP IX ; Get current POP HL ; Get chars left PUSH HL ; And restore them PUSH IX ; And current PUSH BC ; And 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 EXITM ;...., ; RLESS: LD A,(RLEN) OR A JR Z,RL0 ; If 0 then we are deleteing so skip LD C,A ; first move 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 ; Realign 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=difference 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 EXITM ;..... ; 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 CALL PRINT DEFB '...Done.',0 JP EXITM ;..... ; LNGSTR: POP HL POP HL ; Clear stack CALL PRINT DEFB CR,LF,CR,LF DEFB 'Replacement string too long.',0 JP EXITM ;..... ; NOSTR: CALL PRINT DEFB CR,LF,CR,LF DEFB 'Old string not found...',0 JP EXITM ;..... ; CONT: CALL PRINT DEFB 'Continue',CR,LF,CR,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 ; Realign counter JR Z,NOCONT ; Nope, so go PUSH BC PUSH IX CALL PRINT DEFB 'Continue entering text...' DEFB CR,LF,CR,LF,0 LD A,(LINCNT) DEC A LD (LINCNT),A CALL LINNUM POP IX POP BC JP MSGLOP ; Go for it... ;..... ; NOCONT: PUSH BC PUSH IX CALL PRINT DEFB 'Cant''t continue, at end of buffer.',CR,LF DEFB 'Use E)dit to delete from and change message...',0 JP EXITM ;..... ; ;----------------------------------------------------------------------- ; displays messages ; LISTE: CALL PRINT DEFB 'List',CR,LF,0 CALL LIST JP EXITM1 ;... ; LIST: CALL PRINT ; Start a new line DEFB CR,LF,0 XOR A LD (COLUMN),A ; Current column number LD (PNTCHR),A ; Reset printing character flag LD (SPCSTR),A ; Flag to show a space has been received LD HL,(MSG) ; Independent check for line length LD IX,(MSG) ; Point to buffer ; ; 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 JP 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 or end of message 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,(COLUMN) ; See what column we are in now ; LIN4 EQU $+1 ; CP 72 JP NC,LSTWRP ; Exit if line is now too long ; LSTLP4: INC HL JP 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 NZ,LSTWR3 ; Not a space, exit PUSH HL ; Save current location CALL LSTSP1 ; Eliminate any trailing spaces POP HL ; Get the original location again PUSH HL ; Save once more ; LSTWR1: INC HL ; Increment next position in line LD A,(HL) ; Get that character CP ' ' ; Double space after a period? JR NZ,LSTWR2 ; If not exit, done LD (HL),3 ; Else eliminate the extra space JP LSTWR1 ; Check/eliminate any more spaces ;... ; LSTWR2: POP HL ; Restore the buffer location JR SHOLIN ; Go show the line ;... ; LSTWR3: 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 LD A,(HL) CP '-' JR Z,SHOLIN CP ' ' JR NZ,LSTWR3 ; Neither of these, keep backtracking PUSH HL CALL LSTSP ; Check for any trailing spaces POP HL JR SHOLIN ; Then display the line ; LSTSP: DEC HL ; Don't check current location ; LSTSP1: LD A,(HL) ; Get the character CP ' ' ; A space character? RET NZ ; Exit if not ; LD (HL),3 ; Don't keep the space JR LSTSP ; See if any more spaces ;... ; ; Ok all finished, now, display the line ; SHOLIN: LD A,(IX) OR A ; Message all finished? RET Z ; Yes, exit CP CR JR Z,LSTCR ; Exit if at end of this line CP 3 JR NZ,SHOLN1 LD (IX),' ' ; Change the "Ignore char" back to space ; SHOLN1: CP ' ' JR C,SHOLN2 ; Ignore non-printing CALL ECHO ; Display the character ; SHOLN2: LD A,(COLUMN) DEC A LD (COLUMN),A JR Z,LSTCR ; If zero, no so add one ; LSTWR5: INC IX JR SHOLIN ; Do the next character ;..... ; ; Displays CR, resets the column counter, then checks to see if time for ; the [more] pause, returns with LF in 'A', not displayed yet. ; LSTCR: CALL LSTUP ; Display a CR and check for [more] 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 LD (COLUMN),A ; Reset column counter LD (PNTCHR),A ; Reset printing character flag LD (SPCSTR),A ; Reset space character flag LD A,CR ; Turn up a new line CALL ECHO LD A,LF CALL ECHO PUSH HL LD E,0FFH LD C,DIRCON CALL SPBDOS ; See if a pause is requested OR A JR Z,LSTUP1 ; If not, keep on going CALL GETCH ; Else wait for another character ; LSTUP1: POP HL ; Restore the stack RET ;..... ; ; Found a CR, check following character to see if: ; ; 1) end of message - keep CR ; 2) space - keep CR, as intentional new line ; 3) printing character - means this was a short line so remove CR ; LSTNEW: INC HL ; Check next character LD A,(HL) OR A ; See if end of message JR Z,LSTN1 ; If yes, handle this CR normally CP ' ' ; Is it a space for blank line? JR Z,LSTCK ; If yes, handle normally DEC HL ; Back to normal position LD A,(PNTCHR) ; Current line have any printing chars? OR A JP Z,LSTCK1 ; If not, keep the CR and check spaces LD (HL),' ' ; Change the CR to a space character JP LSTCK1 ; Now check for any trailing spaces ;... ; LSTN1: DEC HL JP LSTLP1 ; Keep the CR, handle normally ;..... ; ; Keeping the CR, so check for any trailing spaces ; LSTCK: DEC HL ; Back to the CR location ; LSTCK1: PUSH HL ; Save this location for now LD A,(SPCSTR) ; Current line have any spaces? OR A JR Z,LSTCK3 ; If not, can't have any trailing spaces ; LSTCK2: DEC HL ; Previous location on line LD A,(HL) ; Get the previous character CP ' ' ; Was it a trailing space character? JR NZ,LSTCK3 ; If not, ext LD (HL),3 ; Eliminates the trailing space JR LSTCK2 ; Check for additional trailing spaces ; LSTCK3: POP HL ; Back to the original CR location JP LSTLP1 ; Handle normally ;..... ; end of display messages ;----------------------------------------------------------------------- ; save the message PRVT: CALL PRINT DEFB 'Private ',0 LD A,1 LD (PFLAG),A ; Make it private JP SAVEP ; SAVE: CALL PRINT DEFB 'Save',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 LD C,0FFH ; Set up quotient ; DLOP: INC C AND A ; Clear carry SBC HL,DE ; Divide by 64 to get number of lines JR NC,DLOP ADD HL,DE ; Remainder back in HL LD A,C ; Get number of lines INC A ; Bump # lines by one for 'from' info INC A ; And one for 'to' and time/date LD (LINES),A LD A,H OR L ; If any remainder then need extra line JR Z,NOREM LD A,(LINES) INC A LD (LINES),A ; Save new value ; NOREM: CALL PRINT DEFB CR,LF DEFB 'Updating index, message and user files...',0 LD DE,1 LD C,RMWRT CALL SPBDOS ; ; Set flag in 'TO' user's record so he's bumped to mail next signon ; USET: CALL UOPEN ; Read 'to' user into buffer LD HL,USRLEN LD (RRSZ),HL LD HL,(MTOREC) ; Record # saved when we searched for 'to' user CALL GET LD A,(MAILF) ; Flag mail waiting INC A ; Show one more LD (MAILF),A ; And set it LD HL,(RRNO) ; Get record number CALL PUT ; And write record back CALL CLOSE ; ; Just see if space is free in the message index and, if so, use it, ; else we will extend messages file with a new record. ; ; Have to open new record for message in MSGINDEX so set up for it ; ALLSKP: CALL MOPEN LD HL,(IMRNM) LD (MSTRF),HL ; Set message record number LD A,(LINES) LD (MBLKF),A ; & number lines (records) LD D,0 LD E,A ADD HL,DE ; Calc new record start LD (IMRNM),HL ; And store in index LD HL,(IMNDX) ; And bump number records in index INC HL LD (MRECF),HL ; Store rec# in msgindex LD (IMNDX),HL LD (RRNO),HL ; Set record number in buffer ; ; Now write the MSGINDEX record # determined above ; WRITE: CALL GETTIM ; Get date/time string LD HL,(IMNXT) ; Next message number LD (MNUMF),HL ; Make it this message LD HL,MTOTMP LD DE,MTOF LD BC,NAMLEN LDIR ; Set message 'to' field LD HL,TIME ; Get the current time LD DE,MTIMF ; Put it in MSGINDEX.BBS LD BC,5 ; Just the hour and minutes is ample LDIR LD IY,IDATE LD IX,MDATF ; And date LD A,(IY) LD (IX),A LD A,(IY+1) LD (IX+1),A LD A,(IY+2) LD (IX+2),A LD IY,PFLAG ; Private flag LD A,(IY) LD (MPUBF),A LD DE,MSUBF LD HL,MSUBTMP LD BC,26 LDIR ; Set subject field LD HL,UNAME ; Get user's name LD BC,NAMLEN LDIR ; Move users name XOR A ; Clear 'A' INC A ; And increment so it's a 1 LD (MANUMF),A ; And then store in area number LD HL,(RRNO) CALL PUT CALL CLOSE ; MSGINDEX closed ; ; Now update index info ; LD HL,(MSTRF) ; Get record number for first line LD (STRTMP),HL ; Save it for later ; ; Set the area number to 1. All comments must go to area 1 ; LD HL,(IMNXT) INC HL LD (IMNXT),HL ; Update next message number LD HL,IDATE ; Update INDEX info LD DE,IDATEF LD BC,NDXLEN LDIR CALL IOPEN CALL PUT CALL CLOSE ; INDEX closed ; ; And finally, write each 64 byte line to sequential records ; LD HL,MESSAGES CALL OPEN LD HL,MSGLEN LD (RRSZ),HL LD HL,(STRTMP) ; Set record number for first line LD (RRNO),HL ; ; Now we create the first two records which are the message header with ; time/date, to/from, subject stuff ; CALL CLRBUF ; Nice clean buffer LD HL,MSG1 ; Move first of header to buffer LD DE,RNDBUF LD BC,6 LDIR LD HL,DATE ; And date string LD BC,8 LDIR LD A,' ' LD (DE),A INC DE LD HL,TIME ; And time string LD BC,8 LDIR LD A,' ' LD (DE),A INC DE LD A,CR LD (DE),A INC DE LD HL,MSG2 ; Next header bit LD BC,6 LDIR LD HL,MTOTMP ; Who to LD BC,NAMLEN LDIR LD A,CR ; End of line LD (DE),A INC DE XOR A ; Insure clean buffer LD (DE),A CALL STRIP ; Waste the nulls LD HL,(RRNO) ; Get record # CALL PUT ; And write it CALL CLRBUF ; Clear the buffer for next stuff ; ; Now get 'from' name and move into place ; LD HL,MSG3 LD DE,RNDBUF LD BC,6 LDIR ; Move characters before 'from' name LD HL,UNAME ; Get user's name LD BC,NAMLEN LDIR ; Move users name LD A,CR LD (DE),A ; Last carriage return INC DE LD (DE),A ; Twice CALL STRIP ; Get rid of nulls LD HL,(RRNO) INC HL CALL PUT ; Write second record LD HL,(RRNO) INC HL LD (RRNO),HL ; Save updated record number LD IY,(MSG) ; Point to message LD A,(LINES) DEC A ; Less 2 for time/date stuff ... DEC A ; That has already been written LD B,A ; Set up counter CALL WRTLP CALL CLOSE LD DE,0 LD C,RMWRT CALL SPBDOS ; CLEAR WRITE LOCK CALL PRINT DEFB CR,LF,0 ; REXIT: LD C,LOGST ; BYE DISKLOG status request LD E,0FFH ; Ask for status CALL SPBDOS CP 77 ; 77 says "NO is set in BYE5" JR Z,REXITM4 ; Exit with no change to the flag LD A,(DSKFLG) ; See if DISKLOG was originally on OR A JR Z,REXITM4 ; If not, all done LD C,LOGST ; BYE5 DISKLOG status byte LD E,1 ; Else turn the DISKLOG back on CALL SPBDOS ; to catch Sysop's log off time ; REXITM4:LD A,(OLDDRV) LD E,A LD C,SELDSK CALL SPBDOS ; Restore original drive LD A,(OLDUSR) LD E,A LD C,SETUSR CALL SPBDOS ; Restore original user area LD HL,(CCPSTK) LD SP,HL ; Restore original stack RET ; And exit ;..... ; ; Set the user's preferred terminal with for writing, reading messages ; SETLIN: LD A,(LENG) ; Get user's terminal line width LD (LIN1),A LD (LIN2),A ADD A,1 LD (LIN4),A SUB 8 LD (LIN3),A RET ;..... ; ; Take the nulls out of the record being written to MESSAGES.BBS. This ; only needed on the first 2 records which contain data moved from other ; areas that use nulls to pad the fields... ; STRIP: LD HL,RNDBUF ; Start of buffer LD B,MSGLEN ; Length of each message record ; STRIP0: LD A,(HL) ; Get character OR A ; Is it a null? JR NZ,STRIP1 ; Not 0 INC A ; Yes, so change 0 to 1 ; STRIP1: LD (HL),A ; Save character INC HL ; Next character DJNZ STRIP0 ; Do entire buffer RET ; Done ;..... ; ; Clear buffer for next ; CLRBUF: XOR A LD HL,RNDBUF LD (HL),A LD DE,RNDBUF+1 LD BC,USRLEN ; Longest used length LDIR RET ;..... ; ; Header data strings... ; MSG1: DEFB 'Left ' ; 6 characters before time/date MSG2: DEFB 'To ' ; 6 characters before who to MSG3: DEFB 'By ' ; 6 characters before who from ; ; Subroutines to handle user, index, and message files ; UOPEN: LD HL,USERS JP OPEN ; Get user info to memory ; IOPEN: LD HL,INDEX ; Point to index name CALL OPEN ; Open file LD HL,NDXLEN ; Length of record LD (RRSZ),HL ; Set into buffer LD HL,0 ; Set record number RET ;..... ; MOPEN: LD HL,MSGINDEX ; Point to index name CALL OPEN ; Open file LD HL,MNDXLEN ; Length of record LD (RRSZ),HL ; Set into buffer RET ;..... ; WRTLP: PUSH BC PUSH IY POP HL LD DE,RNDBUF LD BC,MSGLEN LDIR PUSH HL POP IY LD HL,(RRNO) CALL PUT LD HL,(RRNO) INC HL LD (RRNO),HL POP BC DJNZ WRTLP RET ;..... ; EATCHR: LD C,CONST ; Check console status CALL SPBDOS OR A RET Z ; No characters, exit ; LD C,RDCON ; Get the character CALL SPBDOS CP ' ' ; Non-printing character? JR C,EATCH1 ; If yes, ignore and exit CALL PRINT ; Else remove from the display DEFB BS,' ',BS,0 ; EATCH1: XOR A ; Ignore this character JR EATCHR ; See if any more characters ;..... ; ;*********************************************************************** ; ; SUBROUTINE: HASH ; PURPOSE: Calc pos'n in USERS.BBS to start search ; INPUT: A =first character of last name ; OUTPUT: HL=pos'n in USERS.BBS 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 ;..... ; EBUF: DEFS 64 ; Edit string buffer RBUF: DEFS 64 ; Replacement string buffer ELEN: DEFB 0 ; Length of old string RLEN: DEFB 0 ; Length of new string NEWFRE: DEFW 0 ; Temp storage DIFF: DEFW 0 ; Ditto COLUMN: DEFB 0 ; Current column counter CRS: DEFB 0 ; Carriage return counter MTOTMP: DEFS 30 MSUBTMP:DEFB 'CP/M Note' ; MSUBLEN EQU $-MSUBTMP DEFS 26-MSUBLEN ; MTOREC: DEFW 0 HSHREC: DEFW 0 STRTMP: DEFW 0 PFLAG: DEFB 0 TLINES: DEFB 0 OLDUSR: DEFB 0 OLDDRV: DEFB 0 LINES: DEFB 0 CCPSTK: DEFW 0 DELLEV: DEFB 0 TMPSTK: DEFW 0 ; DEFS 128 ; Stack area ; STACK EQU $ ; MSG: DEFW 0 ; Message entry storage MSGARR: DEFW 0 ; Storage for message pointers HBSEND: DEFS 0 ; END