; COMSEC v1.3 ; This program is similar in nature as the 'message service' for a ; SYSOP on a BBS. The main point is, that it can be used in the ; command line in CP/M. I was tired of having to re-enter the BBS ; just to leave a message because the SYSOP didn't answer on ; CHAT. ; Some of these routines were borrowed from CHAT. The origional ; author of CHAT was: Roderick Hart... ; The caller simply enters the program name or uses the option to ; immediately start without instructions. Using a ' D' after the ; name will directly enter it. ; It should also be noted that this program has NO provisions for ; anything less than CP/M 2.x... ;############################################### ; Written by: ;(except where noted) ; Version 1.0 ; R. Kester ; JAN 05 84 ; Version 1.1 ; Minor changes, renumbered for me ; Version 1.2 ; Some more minor changes... ; Version 1.3 ; Re-did code so compatible with NUCHAT's.. ;################################################ NO EQU 0 YES EQU 0FFH STDCPM EQU YES ;Yes for 'standard' CP/M ALTCPM EQU NO ;Yes for other type CP/M (TRS-80,etc) IF STDCPM BASE EQU 0 ENDIF IF ALTCPM BASE EQU 4200H ENDIF ORG BASE+100H ;Version 1.3 VER EQU 13 ;* Current version number BDOS EQU BASE+5 FCB EQU 5CH OPEN EQU 0FH MAKE EQU 16H READ EQU 14H WRITE EQU 15H CLOSE EQU 10H SETDMA EQU 1AH USR EQU 20H DEFBUF EQU 80H CR EQU 0DH LF EQU 0AH BELL EQU 07H SPACE EQU 20H SECT EQU 80H DEL EQU 7FH ABORT EQU 'A'-40H ;Abort program in message mode FINIS EQU 'C'-40H ;Quit and save file (message) EOF EQU 'Z'-40H ;End Of File BACKUP EQU 'H'-40H ;Baskspace JMP START ;Bypass ; NOTE: When specifying the drive code, enter the number ; corrosponding to the drive. ; i.e. 0=current drive ; 1=dirve 'A' ; 2=drive 'B'.....etc. ; 'MEMLIM' = This allows that number (MEMLIM) of bytes to be added ; starting at BUFF. BUFF is the area directly following this pro- ; gram where all received characters are stored, INCLUDING already ; existing messages (if any). i.e. If the value of MEMLIM were 50, ; then this program would only allow 50 bytes to be placed in mem- ; ory. It would then issue an error telling the user it is running ; low on memory and automatically 'close up shop'. It should be ; noted that, even if it does enter the error condition, it still ; includes the LASTCALR information. So this number should be used ; as a reference only. ; I.E. 20,000 = 20,000 BYTE MESSAGE FILE. ;* * * * * * USER MOD AREA * * * * * * * ;* Message limit (see note above) * MEMLIM EQU 20000 ;* Set YES for an RBBS system * ;* (use the LASTCALR file) * RBBS EQU YES ;* # of characters per line * LIMIT EQU 72 ;* How many repeatative characters * ;* before tagging an error? * TOMANY EQU LIMIT-8 ;* User area you want messages in * USER EQU 10 ;* Drive for messages, put number here * DFDRV EQU 1 ;* Drive with LASTCALR on it * CALLDR EQU 1 ;* User area of LASTCALR * CALLU EQU 0 ;* File name created for messages * ;* spaces ||||||||||| =11 * FNAME DB DFDRV,'MESSAGE CPM' ;* spaces ||||||||||| =11 * ; End of option selections * ;**************************************** ; From here on, you shouldn't need to modify anything else... IF RBBS DBUF EQU 80H BSIZE EQU 80H CALLERFCB: DB CALLDR,'LASTCALR ',0 DS 23 DB 0FFH CALLERADR:DW DBUF CALLERSIZ:EQU BSIZE CALLERLEN:DW BSIZE CALLERPTR:DS 2 ENDIF ;RBBS START: ; Do the usual routine for the SP LXI H,0 DAD SP SHLD STACK LXI SP,STACK ; Initialize direct CBIOS calls LHLD 1 LXI D,3 DAD D SHLD CSTAT+1 ;Con stat DAD D SHLD CIN+1 ;Con in DAD D SHLD COUT+1 ;Con out ; Get current user area and save it MVI E,0FFH ;Code for GET MVI C,USR CALL BDOS ;Do it STA OLDUSR ;Save it for return ;Get any potential options next LDA DEFBUF+1 ORA A JZ NNOP LDA DEFBUF+2 STA OPT NNOP: IF RBBS XRA A ;Zero A STA CALLERFCB+12 STA CALLERFCB+32 LXI H,CALLERSIZ ;Get value SHLD CALLERLEN SHLD CALLERPTR MVI E,CALLU ;Set area for LASTCALR MVI C,USR CALL BDOS LXI D,CALLERFCB ;Point to filename MVI C,OPEN CALL BDOS CPI YES ;Was it successful? JNZ OPENOK ;Zero = No CALL ILPRT DB BELL,CR,LF,LF DB 'ERROR --> LASTCALR file not found!...ABORTING' DB CR,LF,LF,0 JMP LEAVE OPENOK: LXI D,DEFBUF ;Point to default buffer MVI C,SETDMA ;Make new DMA addr CALL BDOS MVI C,READ ;Read in file @DMA LXI D,CALLERFCB CALL BDOS ORI 0FFH ;Read OK? JNZ ROK CALL ILPRT DB BELL,CR,LF,LF DB 'ERROR -> Can''t read LASTCALR file!' DB CR,LF,LF,0 JMP LEAVE ROK: CALL VEIW ;Set up name MVI M,'$' ;Mark end ENDIF ;RBBS ; Do sign-on CALL ILPRT DB CR,LF,LF DB ' Computer Secretary v' DB VER/10+'0','.',VER MOD 10+'0' DB CR,LF,LF,0 ; See if any requests are there LDA OPT CPI NO ;Any options? JZ NONE ;No... CPI 'D' ;Direct entry? JZ DIRECT ;We saw a 'D' ; Otherwise give brief instructions NONE: CALL ILPRT DB CR,LF DB 'When the -: prompt appears, you may start entering' DB CR,LF DB 'your message. Hitting the RETURN key is not necessary' DB CR,LF DB 'for terminating lines. You may ABORT the process by' DB CR,LF DB 'entering a ^A. Use ^C for saving message.' DB CR,LF DB 'You may also make your life easier next time by:' DB CR,LF,LF DB 'A>progname D <-- use a ''D'' for direct entry' DB CR,LF,LF DB 0 ; First, move the FNAME into the FCB DIRECT: MVI B,12 ;Number of bytes to move LXI H,FCB ;The 'to' place LXI D,FNAME ;The 'what to move' name LOOP: LDAX D ;Get the byte MOV M,A ;Get the 'what' byte INX H ;Bump the pointer INX D ;Bump the 'getter' DCR B ;Decrement the counter JNZ LOOP ;If B<>0 then keep chuggin' CALL CLRFCB ;Clear certain extensions ; And set the area for the messages... MVI E,USER ;Get ready to set the MVI C,USR ; user are desired CALL BDOS ;Do it. LXI D,FCB ;Point to the filename MVI C,OPEN ;Get ready to open CALL BDOS ;the file pointed by DE CPI YES ;Was it successful? JZ MAKEIT ;Zero = make it the first time ; Now read in the current contents... LXI D,BUFF ;Point to buffer RLOOP: MVI C,SETDMA PUSH D ;Save previous DMA addr. CALL BDOS LXI D,FCB ;Point to filename MVI C,READ ;Read it in CALL BDOS POP D ORA A ;Find out DIR code JNZ FINISHED ;Zero = not finished LXI H,80H ;Value of 1 sector DAD D ;HL has new DMA addr. XCHG ;Now DE has JMP RLOOP CLRFCB: XRA A ;Zero A STA FCB+12 STA FCB+32 RET ; We finished reading the file in to buffer FINISHED: XCHG ;Get the last DMA for a double check SHLD POINTR CALL CLRFCB ;Clear the record info for writing CALL SEARCH ;Find the EOF mark and cancel it. ; and then reset the POINTR. BEGIN: IF RBBS CALL FIRSTNM ;Get & print callers name ENDIF ;RBBS CALL ILPRT DB BELL,CR,LF DB ' - ^A aborts - ^C saves message' DB CR,LF,LF DB '-: ' DB 0 READIT: CALL TESTMEM ;Check memory limit CALL CIN ;Get a byte typed by the user CPI FINIS ;A ^C? JZ QUIT ;Yes?, then tidy up CPI ABORT ;Change their mind? JZ STOP ;Yes?, then don't tidy up CPI CR ;A return? JZ CRLF ;Yes?, do the dirty work CPI BACKUP ;A backspace? JZ BACK ;Do what it requires CPI DEL JZ BACK CPI ' ' ;A space? JC READIT ;If it equals a value below, then loop CALL PUTNMEM ;Slip it in memory PUSH PSW ;Save 'A' MOV C,A ;Swap it for output CALL COUT ;Send it to them POP B ;Get 'A' into 'B' now LDA COUNT ;How far we gone on the screen? INR A ;Bump it STA COUNT ;Save it CPI LIMIT ;Too many characters yet? JZ CRLF ;Yep CPI LIMIT-8 ;Near the limit? JC READIT ;Nope MOV A,B ;Find out if we can CPI ' ' ; help'm out and do a JNZ READIT ; return for them... CRLF: CALL ILPRT ;...we could! DB CR,LF DB '-: ' DB 0 XRA A ;Reset the counter STA COUNT MVI A,CR ;Load a RETURN CALL PUTNMEM MVI A,LF ;Load a LINE FEED CALL PUTNMEM JMP READIT ;Do it all again BACK: LDA COUNT ;Get the counter DCR A ;Sub one for a backspace JM READIT ;Already at 0? STA COUNT ;Then save it CALL ILPRT DB BACKUP,' ',BACKUP,0 LHLD POINTR ;Get pointer value MVI A,L ;If it is already ORA H ; a zero then JZ READIT ; skip the rest DCX H ;Sub one for backup SHLD POINTR ;Save it JMP READIT ;Go back and do some more ; Inline print routine using direct I/O ILPRT: XTHL ;Swap SP/HL ILPLP: MOV C,M ;'C' = ->HL PUSH H CALL COUT ;Send it to the console POP H INX H ;Bump the char. pointer MOV A,M ;'A' = ->(HL) ORA A ;Is it a null? JNZ ILPLP ;Nope, do some more XTHL ;Yep, swap HL/SP RET IF RBBS ;Enter here to display callers name to CRT... FIRSTNM: CALL ILPRT DB 'Sorry I wasn''t around ',0 LXI H,DEFBUF ;Point to area HAGA: MOV A,M ;Get byte CPI '$' ;See if end JZ ALM ;Yes... PUSH H ;Else, save HL MOV C,A ;Get byte to send CALL COUT ;Send it to CRT POP H ;Get HL back INX H ;Bump it JMP HAGA ;Loop.. ALM: CALL ILPRT DB '....',CR,LF,LF,0 ;Send this for looks RET ;Enter this routine to set-up the name to be printed ;in the file, Replaces the comma with a space. Puts ;it the default buffer... VEIW: LXI H,DEFBUF ;Point to defualt buffer DLOP: MOV A,M ;Get a byte CPI EOF ;End of file RZ ;Yes..or CPI CR ; found a CR? RZ ;Yes... ALOOP: CPI ',' ;Then check for this JNZ BLOP ;No... MVI A,' ' ;Then make it a space BLOOP: MOV M,A ;Put it in memory BLOP: INX H ;Bump pointer JMP DLOP ;Loop... ENDIF ;RBBS ;Message for SYSOP if too many chars. in arow. TOMSG: DB CR,LF,LF,'This person possibly tried to fool you!',CR,LF,'$' ;Enter here when we got too many of the same character in a row. TOERR: CALL ILPRT DB BELL,CR,LF,LF DB 'ERROR -> Too many similar characters, ABORTING!' DB CR,LF,LF,0 LHLD ORNPTR ;Get value before anything was entered SHLD POINTR ;Make that the current value LXI D,TOMSG ;Enter a msg. so SYSOP nows why CALL PLOOP ; nothing was entered QUIT: MVI A,CR ;Put some area in for readibility CALL PUTNMEM MVI A,LF CALL PUTNMEM CALL PUTNMEM IF NOT RBBS JMP ALMOST ENDIF ;NOT RBBS IF RBBS CALL CALLGET ;Put name into file JMP ALMOST ;Enter here to place callers name into file.. CALLGET: LXI D,DEFBUF HLOOP: LDAX D ;Get byte CPI '$' ;End? RZ ;Yes.. PUSH D ;Then save DE CALL PUTNMEM ;Get byte=>DE put in file by (HL) POP D ;Get DE back INX D ;Bump it JMP HLOOP ;Loop... ENDIF ;RBBS ; Call this routine each time we enter a byte into the buffer ; and keep track of twits... PUTNMEM: STA TEMP ;Save A for the following LHLD POINTR ;Get current value MOV B,A ;Save it MOV M,A ;Slip in byte INX H ;Bump the pointer SHLD POINTR ;Save it LHLD POINTR ;Get it back DCX H ;Decrement it DCX H ; again MOV A,M ;Get byte CMP B ;The same as B? JZ SETNOT ;Yep.. CPI CR ; ? JZ SETNOT ;Yep.. CPI LF ; ? JZ SETNOT ;Yes?, do something about it XRA A ;No?, then STA MNYCNT ; reset count LDA TEMP ;Get A back RET ;Enter here when we find the same character typed twice in a row. ;And exit if too many of them, and keep the caller's name. SETNOT: LDA MNYCNT ;Get count INR A ;Bump it STA MNYCNT ;Save new count CPI TOMANY ;Too many of them? JZ TOERR ;Yes?, then error exit LDA TEMP ;Get A back RET ; Test memory limit... if we are there, then quit TESTMEM: LHLD MEMS ;The number not to exceed XCHG ;Swap LHLD POINTR ;The number to compare to MOV A,H ;Put MS part in A CMP D RC ;Ok, if carry MOV A,L ;Else do the same CMP E RC ;Ok, if carry ;No carry so we are over exteneded... CALL ILPRT ;Then print error message DB BELL,CR,LF,LF DB 'SORRY -> Ending things, running low on memory!' DB CR,LF DB 'Please try again another time...' DB CR,LF,LF,0 JMP QUIT ;Close up shop MEMS: DW BUFF+MEMLIM ;Max. value not to exceed ; Put some sort of marking for the next message ; when being typed out. ; End of message delimmiter. ENDING: DB CR,LF,LF,'+ + + + + + + + + + + + + + + +',CR,LF,LF,'$' ALMOST: LXI D,ENDING ;Put the above line in the file CALL PLOOP ; for readibility JMP GONE ;Used elsewhere... PLOOP: LDAX D CPI '$' RZ CALL PUTNMEM INX D JMP PLOOP GONE: MVI A,EOF ;Get EOF mark CALL PUTNMEM ; Change the user area for the message file MVI E,USER MVI C,USR CALL BDOS LXI D,BUFF ;Beginning of DMA PUSH D ;Save it WLOOP: POP D ;Get previous push into DE PUSH D ;Save on the stack MVI C,SETDMA ;Set the DMA to CALL BDOS ;the addr. in DE LXI D,FCB ;Point to filename MVI C,WRITE ;Write to it CALL BDOS CPI NO ;Successful? JNZ WEXIT ;Zero = yes POP H ;Get the past DMA addr. LXI D,SECT ;One more sector DAD D ; is added to the value PUSH H ;Save the next DMA addr. MOV A,H ;Get the high byte CMA ;1's compliment MOV D,A ;Save that in D MOV A,L ;Get the low byte CMA ;1's compliment MOV E,A ;Save that in E INX D ;= inverted current DMA addr.+1 LHLD POINTR ;Get # of bytes that were typed DAD D ;Effectively -> NEW - CURRENT = ; # of bytes left to write in HL MOV A,H ;Get the MS value in A INR A ;Bump it ANA A ;Set any flags? (a -1?) JNZ WLOOP ;No, then we have more to write. POP H ;Clean the stack JMP EXIT WEXIT: CALL ILPRT DB CR,LF,LF,BELL DB 'ERROR --> Can''t write file, ABORTING!' DB CR,LF,LF,0 JMP LEAVE ;Leave and do nothing EXIT: LXI D,FCB ;Point to filename MVI C,CLOSE ;And close it CALL BDOS CPI YES ;Successful? JNZ LEAVE ;Zero = No CALL ILPRT DB CR,LF,LF,BELL DB 'ERROR --> Can''t close file, ABORTING!' DB CR,LF,LF,0 LEAVE: MVI C,SETDMA ;Re-set the DMA LXI D,DEFBUF ; so we don't CALL BDOS ; mess up. LDA OLDUSR ;Get origional MOV E,A ; user area and MVI C,USR ; return us to CALL BDOS ; there. LHLD STACK ;Get origional SP SPHL ; for 'soft' return RET STOP: CALL ILPRT DB CR,LF,LF DB '* * * ABORTED! - Nothing saved * * *' DB CR,LF,LF,0 JMP LEAVE ; Create the file MAKEIT: CALL ILPRT DB CR,LF DB 'Creating file...' DB CR,LF,LF,0 LXI D,FCB ;We had to create it new MVI C,MAKE CALL BDOS CPI YES ;successful? LXI H,BUFF ;If we goto BEGIN.... SHLD POINTR JNZ BEGIN ;Zero = No CALL ILPRT DB CR,LF,LF,BELL DB 'ERROR --> No directory space or trouble opening.' DB CR,LF,LF DB 'Please try again another time....' DB CR,LF,LF,0 JMP EXIT ;Search the current file and blank out the EOF mark... SEARCH: LXI D,BUFF ;Point to beginning LHLD POINTR ;Get current position SLOOP: LDAX D ;Move byte into A CPI EOF ;Was it the EOF? JZ NULLIT ;Yep?, the zero it INX D ;No?, then keep searching DCX H ;Decrement the pointer MOV A,H ;Find out if we have no ORA L ; more positions JZ NULLERR ;Just used for a double check JMP SLOOP ;Else, check some more NULLIT: XRA A ;Zero A XCHG ;Get position in HL MOV M,A ;Put a '0' there SHLD POINTR ;Save the area where our new DCX H ;Save for later if we SHLD ORNPTR ; need it... RET ; buffer starts ; Enter here if we did not find an EOF mark in the available ; number of positions (double check) NULLERR: CALL ILPRT DB BELL,CR,LF,LF DB 'The validity of the file might be questioned' DB CR,LF DB 'Did NOT find the EOF, and should have!' DB CR,LF,LF,0 RET CSTAT: JMP $-$ ;Set upon entry CIN: JMP $-$ ; " " " COUT: JMP $-$ ; " " " COUNT: DB 0 OPT: DB NO TEMP: DS 1 MNYCNT: DS 1 OLDUSR: DS 1 POINTR: DS 2 ORNPTR: DS 2 DS 64 ;32 level stack STACK: DS 2 ;Storge for incoming stack BUFF EQU $ ;Message buffer starts here END