;*; Updated on 31-Aug-89 at 1:41 PM by Steve Archuleta; edit time: 0:03:38
;*******************************
;*       IntraNetwork CB       *
;*                             *
;*      by Dave Heyliger       *
;*         AMUS Staff          *
;*                             *
;* NOTE: you need              *
;*                             *
;*           CB.M68            *
;*          CBCLR.M68          *
;*          CBSYS.M68          *
;*******************************

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; "Go ahead... make me rich!"
;
;    translated: THIS BABY IS FREE FOR ANYONE AND EVERYONE. The ONLY way
;                this program will generate money is if someone decides
;                to cheat, sell it, and get caught by me or other members
;                etc. Then in come the lawyers and bingo: I'm rich!
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

;##########################################################################
; How it works, General Instructions, What You Need:
;
; CB works by storing pointers to user partitions of everyone who is on CB.
; When a user on CB modifies their "message" to other users, it changes a
; "hash" (the count of the number of characters). When there is a hash change
; OR a user quits OR a new user comes on, the messages are updated. CB offers
; unique "handles" for everyone who is on (SPECIAL TSASS VERSION uses
; INITIALS as the handle automatically). CB also offers dynamic screen editing
; that adds great effects! You will find CB addicting and fun - guaranteed.
;
; FOR CB TO WORK, you MUST modify this file. There are TWO versions contained
; in this source code file. 1) "PUBLIC" version and 2) "TSASS" version. For
; those who don't run TSASS (Dravac), you MUST comment out the "TSASS"
; portion and visa versa. It is clearly marked in the code, and it involves
; placing ";"s before an entire paragraph of code. After reading the code you
; will see where to place the series of ";"s.
; You will also need a funny file called CB.SYS. This is just a one block file
; that contains absoulely NOTHING! You MUST place CB.SYS in System Memory (it
; only takes up 512 bytes). You do this by storing CB.SYS on DSK0:[1,4] and
; from within your AMOSL.INI file you have a SYSTEM CB.SYS line before the
; final SYSTEM line. Reboot your system and you are ready to go. If you have
; the space, place CB.LIT (the assembled version of this file) on DSK0:[1,4]
; too so you can call up CB from anywhere. The file CB.SYS can be generated
; from a very small file called CBSYS.M68. Be sure to RENAME it to CB.SYS!
; Finally, a file called CBCLR.M68 creates CBCLR.LIT (CB Clear). If for any
; reason CB.SYS gets "mucked", type CBCLR and it will reset CBSYS to all "0"s.
;############################################################################

       OBJNAM  CB.LIT                  ;final product

;--- SEARCH appropriate files
;
       SEARCH SYS
       SEARCH SYSSYM
       SEARCH TRM

;--- define a version number
;
       VMAJOR=1.
       VMINOR=0.
       VEDIT=100.                      ;original version by Dave Heyliger
       VEDIT=101.                      ;loosing chars @ 1200 bps, 1 liner cor

;--- variables
;
       .OFINI
       .OFDEF  GLOBAL,120              ;global transmitter that all will see
       .OFDEF  FLAG,2                  ;indication of change in CB.SYS
       .OFDEF  PRMCNT,2                ;"perminent" user count
       .OFDEF  CBSYS,4                 ;System Module CB.SYS
       .OFDEF  USRNUM,2                ;"variable" user count
       .OFDEF  HASHES,24               ;the hash code array
       .OFDEF  USRLST,140              ;24 user pointers allowed
       .OFSIZ  IMPSIZ

       PHDR    -1,0,PH$REE!PH$REU      ;define a program header

;--- Macro Definitions
;
DEFINE  PRTTAB  AA,BB           ;PRINT TAB (#,#)
       MOVB    #AA,D1
       LSLW    D1,#10
       MOVB    #BB,D1
       TCRT
ENDM

DEFINE  OVER    AA,BB           ;TAB OVER special tab function
       MOVB    AA,D1
       LSLW    D1,#10
       MOVB    BB,D1
       TCRT
ENDM

DEFINE  PRTONE  AA              ;PRINT TAB ((#+5),1)
       ADD     #5,AA
       MOVB    AA,D1
       LSLW    D1,#10
       MOVB    #1,D1
       TCRT
ENDM

DEFINE  HOME    AA              ;moves cursor "HOME" on your input line
       MOVB    #3,D1
       LSLW    D1,#10
       MOVB    AA,D1
       TCRT
ENDM


;--- Image mode, no echo, no control-C, and get .OFDEF pointer (A3)
;
       JOBIDX  A6                      ;A6 points to JCB
       LEA     A4,JOBTYP(A6)           ;A4 points to processing word
       ANDW    #^B1111111101111111,@A4 ;set no ctrlc
       MOV     JOBTRM(A6),A4           ;A4 points to the terminal def. tbl.
       ORW     #T$IMI!T$ECS,@A4        ;Force image mode, suppress echo
       GETIMP  IMPSIZ,A3               ;A3 points to variables

;--- See if user wants to get on...
;
CBINI:  PRTTAB  377,0                   ;clear the screen
       PRTTAB  2,1                     ;tab over
       TYPE    < There are >           ;start of msg
       CALL    SERCH                   ;fill CBSYS variable - find CB.SYS
       CLR     D1                      ;data registers are funny, so clear
       MOVB    20(A1),D1               ;D1 contains count of current users
       PUSH    D1                      ;save the count
       DCVT    0,OT$TRM                ;blast out current number
       TYPE    < user(s) currently on.>
       POP     D1                      ;retrieve the count
       CMPB    D1,#30                  ;all full??
       BNE     ROOM                    ;nope, still some room
       TYPECR  < Sorry, but the airways are full!>
       JMP     QOFF                    ;type above msg and quit
ROOM:   TYPE    < Would you like to connect?  y or n >
QON:    SLEEP   #2500.                  ;sleep a bit to not burn CPU
       TCKI                            ;any input???
       BNE     QON                     ;waiting for reply still....
       KBD                             ;get the reply
       UCS                             ;create upper case for comparison
       CMPB    D1,#131                 ;"Y"????
       JNE     QOFF                    ;nope, so quit

;--- TSASS LOGON ROUTINE
;
;       Place semi-colons in front of this entire paragraph if you do NOT
;       use TSASS!
;
;       USRBAS  A1                      ;get the pseudo base of memory
;       SUB     #572,A1                 ;A1 points to the initials
;       LEA     A5,GLOBAL(A3)           ;time to .INI global message space
;       MOVB    #377,(A5)+              ;"ON" byte set (checked by others)
;       MOVB    #0,(A5)+                ;"HASH" set to zero (no input yet)
;       MOVB    #'[,(A5)+               ;move in the HANDLE information
;       MOV     #4,D4                   ;four times we will do this:
;MOVHDL:        MOVB    (A1)+,(A5)+             ;       move in the char
;       DEC     D4                      ;       one less char to move in
;       BNE     MOVHDL                  ;       and get next char
;STLMOR:        MOVB    #'],(A5)+               ;move in more HANDLE info
;       MOVB    #40,(A5)+               ;a space
;       MOVB    #'-,(A5)+               ;a dash
;       MOVB    #40,(A5)+               ;a space
;       CLRB    @A5                     ;a null


;--- PUBLIC LOGON ROUTINE
;
;       Place semi-colons in front of this entire paragraph if you do
;       use TSASS! (original version has this paragraph commented out)
;
       LEA     A5,GLOBAL(A3)           ;time to .INI global message space
       MOVB    #377,(A5)+              ;"ON" byte set (checked by others)
       MOVB    #0,(A5)+                ;"HASH" set to zero (no input yet)
       MOVB    #'[,(A5)+               ;move in the HANDLE information
       PRTTAB  4,30                    ;tab to here
       TYPE    < Enter in a 4-letter handle ____>
       PRTTAB  4,64                    ;return tab to here
       CLR     D3                      ;D3 will be a counter
LOGON:  CMP     D3,#4                   ;all full?
       JEQ     APPEND                  ;yup, time to append some characters
       KBD                             ;wait for ONE character
L1:     CMPB    D1,#177                 ;rubout?
       BNE     L3                      ;nope, don't do code below
       CMPB    D3,#0                   ;rubout, but room?
       BNE     L2                      ;if not "0" then room to go back
SCOLD:  MOV     #7,D1                   ;get a bell
       TTY                             ;beep
       BR      LOGON                   ;and try again
L2:     PRTTAB  377,5                   ;cursor left one column
       MOVB    #137,D1                 ;put a "_" in D1
       TTY                             ;blast it out
       PRTTAB  377,5                   ;move left back over the "_"
       DECB    D3                      ;adjust count
       DEC     A5                      ;adjust pointer
       CLRB    @A5                     ;delete the character from the var.
       JMP     LOGON                   ;and ready for next key entry
L3:     CMPB    D1,#172                 ;is it a z or more?
       BGT     SCOLD                   ;then ding
       CMPB    D1,#40                  ;is it a space?
       BEQ     L4                      ;then AOK
       CMPB    D1,#101                 ;A or less?
       BLT     SCOLD                   ;then ding
L4:     UCS                             ;convert to upper case
       TTY                             ;type it out
       MOVB    D1,(A5)+                ;ok character
       INC     D3                      ;count one more
       JMP     LOGON                   ;and do it again
APPEND:MOVB     #'],(A5)+               ;move in more HANDLE info
       MOVB    #40,(A5)+               ;a space
       MOVB    #'-,(A5)+               ;a dash
       MOVB    #40,(A5)+               ;a space
       CLRB    @A5                     ;a null

;--- fill in information about CB:
;       1) get the "permanent user count" from CB.SYS
;       2) get everyone else's global message location
;       3) place YOUR global message location in CB.SYS
;
CBINFO: CALL    SERCH                   ;find CB.SYS
       LEA     A4,GLOBAL(A3)           ;A4 gets global msg buffer
       LEA     A5,USRLST(A3)           ;A5 points to user pointer array
       LEA     A2,PRMCNT(A3)           ;A3 points to "perm" count
CBIN:   JLOCK   ;--------------------------------------------------------
       INCB    20(A1)                  ;increment total user count in CBSYS
       MOVB    20(A1),@A2              ;get the count into user memory
       LEA     A1,24(A1)               ;point A1 to user pointers
NXTUSR: CMP     @A1,#0                  ;is there a user pointer?
       BEQ     INYOU                   ;if not, put yourself in
       MOV     (A1)+,(A5)+             ;else move in the user pointer
       BR      NXTUSR                  ;and get next user
INYOU:  MOV     A4,@A1                  ;blast in your global ^ to CBSYS
       JUNLOK  ;--------------------------------------------------------
       LEA     A5,USRNUM(A3)           ;record "variable" user number too
       MOVB    @A2,@A5                 ;by saving the "permanent" count

;--- set the "flag" for all other users currently on CB.SYS modification
;
       MOVB    @A2,D3                  ;move in a counter
       DECB    D3                      ;don't count yourself!
       CMPB    D3,#0                   ;only one on?
       BEQ     CBSCRN                  ;yup, so skip rest of this portion
       LEA     A5,USRLST(A3)           ;else repoint A5 to pointer arrays
FLAGON: MOV     (A5)+,A4                ;get a pointer
       ADD     #120,A4                 ;bypass GLOBAL area
       MOVB    #1,@A4                  ;turn on the FLAG
       DECB    D3                      ;one less guy to do
       BNE     FLAGON                  ;if more, do it again
       LEA     A5,FLAG(A3)             ;get YOUR flag
       MOVB    #1,@A5                  ;and turn it on

;--- Generate the transmission screen - give directions
;
CBSCRN: PRTTAB  377,44                  ;screen off
       PRTTAB  377,0                   ;clear screen
       PRTTAB  377,13                  ;dim on
       PRTTAB  377,40                  ;reverse on
       TYPE    < Hit [CR] to clear line >
       PRTTAB  377,14                  ;dim off
       TYPE    <    IntraNetwork CB (1.0)    >
       PRTTAB  377,13                  ;dim on
       TYPE    <       ESCape to quit     >
       PRTTAB  377,41                  ;reverse off
       PRTTAB  377,14                  ;dim off
       PRTTAB  3,2                     ;move to here
       TYPE    < YOU: >                ;your prompt
       PRTTAB  377,35                  ;cursor off
       PRTTAB  377,45                  ;screen on
       MOVB    #10,D5                  ;D5 is column of where YOU are editing

TOP:
;--- Main loop!
;       if input to your terminal, process it
;       if only one on, hang out
;       if others on (and "hash" of msg has changed), type out messages
;       if "new" user on or "old" user off, redo pointers and count
;           and update individuals screen
;
       SLEEP   #500                    ;hang out a bit so CPU don't burn
       TCKI                            ;any input lately?
       BNE     X                       ;nope
       CALL    OWNINP                  ;yup, so get the input and return here
X:      LEA     A4,FLAG(A3)             ;get the flag (modification of users)
       CMPB    @A4,#1                  ;is it on??
       BNE     P                       ;nope, so dont adjust pointer array
       MOV     #7,D1                   ;get a bell
       TTY                             ;and inform that a new user is on
       CALL    UPDATE                  ;update the pointers and count
MORUSR: LEA     A2,PRMCNT(A3)           ;get the perm count
       CMPB    (A4)+,(A2)+             ;if usernumber < perminent count
       BGE     EUPDAT                  ;   then don't clear bottom line
ONLESS: DEC     A2                      ;repoint A2 to "old" count
       MOVB    @A2,D3                  ;setup D3 to bottom row number
       PRTONE  D3                      ;move to the bottom row
       LEA     A2,SPACES               ;get spaces
       TTYL    @A2                     ;clear bottom row (1 less user on CB)
EUPDAT: LEA     A2,PRMCNT(A3)           ;always move the variable user count
       LEA     A1,USRNUM(A3)           ;to the "permanent" non-variable count
       MOVB    @A1,(A2)+               ;move it on in
       CMPB    @A1,#1                  ;only one on?
       BNE     CHKMSG                  ;nope, so check the messages
       LEA     A4,FLAG(A3)             ;yup, so we need to clear the FLAG
       CLRB    @A4                     ;like this!
       BR      TOP                     ;and return to top
CHKMSG: CALL    GETMSG                  ;else get the message(s)
       BR      TOP                     ;and return to TOP
P:      LEA     A4,PRMCNT(A3)           ;get the user number
       CMPB    @A4,#1                  ;only one on?
       BEQ     TOP                     ;then be boring (sleep)
       BR      CHKMSG                  ;else examine others msgs


;--- UPDATE subroutine: updates user pointers and number of users on CB
;
UPDATE: LEA     A4,CBSYS(A3)            ;get the pointer to CB.SYS
       MOV     @A4,A1                  ;and place the pointer into A1
       LEA     A4,USRNUM(A3)           ;A4 points to user number
       LEA     A2,GLOBAL(A3)           ;get YOUR user pointer
       LEA     A5,USRLST(A3)           ;array of user pointers
       JLOCK   ;---------------------------------------------------
       MOVB    20(A1),@A4              ;A4 gets user count
       LEA     A1,24(A1)               ;point A1 to user pointers
NEXT1:  CMP     @A1,#0                  ;is there a user pointer?
       BEQ     NOMORE                  ;nope, all done
       CMP     A2,@A1                  ;YOUR user pointer???
       BNE     GRBUSR                  ;nope, so branch to grab user
       ADD     #4,A1                   ;else bypass it
       BR      NEXT1                   ;and try again
GRBUSR: MOV     (A1)+,(A5)+             ;move in the user pointer
       BR      NEXT1                   ;and try again
NOMORE: JUNLOK  ;---------------------------------------------------
       RTN

;--- DING subroutine - if past your CB "buffer", BEEP and instruct
;
DING:   MOV     #7,D1                   ;get a bell
       TTY                             ;BEEP!
       PRTTAB  4,5                     ;place cursor here
       LEA     A5,ERRMSG               ;get the error message
       TTYL    @A5                     ;blast out error message
TICKI:  TCKI                            ;any input yet?
       BEQ     INPCHK                  ;yup, see if an OK key...([CR] or DEL)
       SLEEP   #2500                   ;else don't burn CPU time
       BR      TICKI                   ;and try again
INPCHK: KBD                             ;get the key (only DEL and CR allowed)
       CMPB    D1,#177                 ;rubout??
       BEQ     OKKEY                   ;then ok key
       CMPB    D1,#15                  ;return??
       BEQ     OKKEY                   ;then ok key
       MOV     #7,D1                   ;else get a bell
       TTY                             ;and scold them again!
       BR      TICKI                   ;and give them another chance
OKKEY:  PUSH    D1                      ;save the key
       PRTTAB  4,5                     ;back to the row
       LEA     A5,SPACES               ;ready for clear
       TTYL    @A5                     ;clear the ERROR message
       POP     D1                      ;retrieve the key
       CMPB    D1,#15                  ;[CR]??
       JEQ     HITCR                   ;yup, so clear out the message
       CALL    BCKONE                  ;nope, must be DEL, so process
       RTN                             ;and return (from "OWNINP")

;--- if an ESC. key was hit, modify CB.SYS, return to AMOS, and set "flag".
;
EXIT:   LEA     A4,CBSYS(A3)            ;get the pointer to CB.SYS
       MOV     @A4,A1                  ;and place the pointer into A1
       LEA     A2,GLOBAL(A3)           ;get YOUR user pointer
       LEA     A5,USRLST(A3)           ;array of user pointers
       JLOCK   ;---------------------------------------------------
       LEA     A1,20(A1)               ;point A1 to user count
       DECB    @A1                     ;one less user on CB
       ADD     #4,A1                   ;point to Global msg list
       MOV     A1,A4                   ;A4 points there too
NEXT2:  CMP     @A1,#0                  ;is there a user pointer?
       BEQ     FLGSET                  ;nope, time to set everones flags
       CMP     A2,@A1                  ;YOUR user pointer? (we want this)
       BNE     NOTYOU                  ;nope, so leave it alone
BUMPEM: ADD     #4,A4                   ;bypass YOUR pointer (with A4 only)
       MOV     @A4,(A1)+               ;and bump this guy over YOUR pointer
       CMP     @A4,#0                  ;more to bump?
       BNE     BUMPEM                  ;yup
       BR      FLGSET                  ;nope
NOTYOU: ADD     #4,A1                   ;get next location
       ADD     #4,A4                   ;get next location
       BR      NEXT2                   ;and get next user (maybe YOU now)
FLGSET: LEA     A2,PRMCNT(A3)           ;get the number of users
       MOVB    @A2,D3                  ;D3 is our counter
       CMPB    D3,#1                   ;only one on?
       BEQ     OFF                     ;yup, so no flags to set
       LEA     A5,USRLST(A3)           ;A5 to start (old) pointer array
NXTFLG: MOV     (A5)+,A4                ;get a pointer
       ADD     #120,A4                 ;bypass GLOBAL area
       MOVB    #1,@A4                  ;turn on the FLAG
       DECB    D3                      ;one less guy to do
       BNE     NXTFLG                  ;if more, do it again
OFF:    JUNLOK  ;---------------------------------------------------

QOFF:   JOBIDX  A6                      ;get JCB
       LEA     A4,JOBTYP(A6)           ;get processing word
       ORW     #^B10000000,@A4         ;set controlc
       MOV     JOBTRM(A6),A4           ;get terminal definition stuff
       ANDW    #17774,@A4              ;return screen to normal
       PRTTAB  377,0                   ;clear the screen
       PRTTAB  377,34                  ;cursor on
       EXIT                            ;return to AMOS

SERCH:
;--- Setup pointer to get find CB.SYS
;
       LEA     A2,BUFFER               ;point A2 to ascii CB.SYS
       LEA     A4,CBSYS(A3)            ;point A4 to variable CBSYS
       FILNAM  @A4                     ;convert ascii CB.SYS ---> RAD50
       SRCH    @A4,A1                  ;A1 points to CB.SYS
       LEA     A4,CBSYS(A3)            ;A4 points to variable to hold pointer
       MOV     A1,@A4                  ;move in the pointer to CBSYS
       RTN

OWNINP:
;--- own user input to get
;
       MOVB    #10,D5                  ;HOME location less data
       LEA     A5,GLOBAL(A3)           ;message line
       ADD     #13,A5                  ;bypass perminent info
FNDNUL: CMPB    @A5,#0                  ;at a null yet?
       BEQ     INKY                    ;yup
       INC     A5                      ;else try next location
       INCB    D5                      ;and move cursor position (hash+10)
       BR      FNDNUL                  ;and try again
INKY:   HOME    D5                      ;move cursor to correct spot
       SUBB    #10,D5                  ;make D5 be the hash value (sneaky!)
       PUSHB   D5                      ;save this baby (it goes everywhere)
       KBD                             ;get the input
       CMPB    D1,#40                  ;space or less?
       BLT     SPECL                   ;yup, so special processing
       CMPB    D1,#177                 ;chech for rubout
       JLT     BYPASS                  ;if NOT, bypass the following
YESRUB: CALL    BCKONE                  ;RUBOUT, so erase, and
       LEA     A5,GLOBAL(A3)           ;get the message space junk
       INC     A5                      ;point past ON/OFF byte
       POPB    @A5                     ;move in "new" hash value (from D5)
       DECB    @A5                     ;one less in hash
       RTN                             ;and return
SPECL:  POPB    D5                      ;restore stack (from D5)
       CMPB    D1,#33                  ;escape?
       JEQ     EXIT                    ;yup, time to EXIT
       CMPB    D1,#15                  ;return?
       BEQ     HITCR                   ;yup, time to clear message
       JMP     NXTCHR                  ;else disregard bogus input
HITCR:  LEA     A5,GLOBAL(A3)           ;get the message junk
       INC     A5                      ;bypass ON/OFF byte
       CLRB    @A5
               ;hash now "0"
       ADD     #12,A5                  ;bypass handle et.al.
       MOVB    #77,D3                  ;number of nulls to move in
MORNUL: CLRB    (A5)+                   ;place in a null
       DECB    D3                      ;one less null to blast
       BNE     MORNUL                  ;still more to clear
       MOVB    #10,D5                  ;get ready to go home
       HOME    D5                      ;home we are (cursor wise)
       MOVB    #120,D3                 ;counter
       MOVB    #40,D1                  ;space character
CLRYOU: TTY                             ;type out a space
       DECB    D3                      ;one less space to type
       BNE     CLRYOU                  ;still more to type
       RTN                             ;end of clearing transmission

BYPASS: POPB    D5                      ;restore stack (from D5)
       CMP     D5,#77                  ;hash too big? (there is a limit!)
       JGE     DING                    ;yup, so inform user too many chrs
       TTY                             ;else blast out new input character
       MOVB    D1,(A5)+                ;and store the character in edit
       LEA     A5,GLOBAL(A3)           ;get the message space junk
       INC     A5                      ;point past ON/OFF byte
       MOVB    D5,@A5                  ;move in "new" hash value
       INCB    @A5                     ;one more in hash (so new msg shows)
NXTCHR: TCKI                            ;any input waiting?
       JEQ     OWNINP                  ;yup, so repeat this section
       RTN                             ;else return to main loop

;--- BCKONE subroutine - erases a character on transmission input
;
BCKONE: CMPB    D5,#0                   ;room to go back?
       BLE     NOROOM                  ;nope
       DEC     A5                      ;move A5 back one
       CLRB    @A5                     ;and null it
       PRTTAB  377,5                   ;backspace cursor
       MOVB    #40,D1                  ;move in a space
       TTY                             ;type it out
       PRTTAB  377,5                   ;and back up
       DECB    D5                      ;column is one less
NOROOM: RTN                             ;done erasing

;--- GETMSG subroutine - analyse other users activities and act accordingly
;
GETMSG: LEA     A1,FLAG(A3)             ;get the flag
       CMPB    @A1,#1                  ;major update on CB?
       BNE     NMAJOR                  ;nope, so don't type all msgs out
       CALL    ALLOUT                  ;yup - type all msgs no matter what
       RTN                             ;and return to TOP
NMAJOR: CALL    HSHCMP                  ;else just compare hashes (maybe type)
       RTN                             ;and return to TOP

;--- ALLOUT subroutine: type out all msgs no matter what
;
ALLOUT: LEA     A6,USRNUM(A3)           ;get the number of user msgs to type
       LEA     A4,USRLST(A3)           ;A4 points to user pointers
AO:     CLR     D4                      ;D4 is our "space" counter register
       MOV     (A4)+,A5                ;A5 points to OTHERS global msg
       CLR     D3                      ;D3 will be our row number
       MOVB    @A6,D3                  ;first get the count
       PRTONE  D3                      ;and tab to this row
       JLOCK   ;------------------------------------------------------
       CMPB    (A5)+,#377              ;CB ON??
       BNE     AOUL                    ;nope - unlock and continue
       INC     A5                      ;bypass the hash code total
AOSTAR: CMPB    @A5,#0                  ;at a NULL yet??
       BEQ     AOUL                    ;yup, no more typing needed
       INC     D4                      ;else one less space to type
       MOVB    (A5)+,D1                ;and get the character of the msg
       TTY                             ;type it out
       BR      AOSTAR                  ;and see if we are at the NULL yet
AOUL:   JUNLOK  ;------------------------------------------------------
       MOV     #40,D1                  ;get a space
SPCOUT: TTY                             ;type it out
       INC     D4                      ;one less space to type
       CMP     D4,#107                 ;last space out?
       BLE     SPCOUT                  ;nope
       LEA     A6,USRNUM(A3)           ;get the user count
       DECB    @A6                     ;one less user to mess with
       CMPB    @A6,#1                  ;only you left?
       BNE     AO                      ;nope, do above all over again
       CALL    NXTGET                  ;update user count since it's mucked
       RTN                             ;and return to TOP

;--- HSHCMP subroutine: compare hash codes, type msg out if different
;
HSHCMP: LEA     A6,USRNUM(A3)           ;get the user count
       LEA     A4,USRLST(A3)           ;A4 points to user pointers
       LEA     A2,HASHES(A3)           ;A3 points to hash codes
HC:     MOV     (A4)+,A5                ;A5 points to OTHERS global msg
       PUSH    A5                      ;save the pointer
       INC     A5                      ;point to the hash code
       CMMB    @A5,(A2)+               ;same hash as last time?
       BNE     NEWHSH                  ;no, different hash, so output
       POP     A5                      ;else restore stack
       LEA     A6,USRNUM(A3)           ;get the user count
       DECB    @A6                     ;one less user to test
       CMPB    @A6,#1                  ;only you left?
       BNE     HC                      ;nope, still more users on
       CALL    NXTGET                  ;else update user count
       RTN                             ;and return to TOP
NEWHSH: DEC     A2                      ;different hash, so repoint A2
       MOVB    @A5,(A2)+               ;move in different hash for next time
       MOV     #11,D4                  ;12 spaces we KNOW don't need typed
       ADDB    @A5,D4                  ;plus hash (although a bit much)
       POP     A5                      ;point to msg
       CLR     D3                      ;D3 will be the row number
       MOVB    @A6,D3                  ;add user count to the row number
       ADD     #5,D3                   ;move it down by 5
       JLOCK   ;------------------------------------------------------
       CMPB    (A5)+,#377              ;CB ON??
       BNE     HCUL                    ;nope
       CMPB    (A5)+,#0                ;no message to type (due to [cr]?)
       BNE     STLSTF                  ;still stuff to type so branch
       OVER    D3,D4                   ;tab over to here
       CLR     D4                      ;D4 is our space counter
       MOV     #40,D1                  ;D1 contains a space
CLRMSG: TTY                             ;start clearing the msg spc by spc
       INC     D4                      ;one less space to type
       CMP     D4,#105                 ;done clearing???
       BLE     CLRMSG                  ;nope
       BR      HCUL                    ;yup, blow by rest of JLOCK
STLSTF: SUB     #7,D4                   ;don't lose any characters.
       OVER    D3,D4                   ;tab to here
       DEC     D4
       ADD     D4,A5                   ;make A5 point to "end" of prev. msg
       TTYL    @A5                     ;and then type out new additions
HCUL:   JUNLOK  ;------------------------------------------------------
       MOV     #40,D1                  ;get a space
       TTY                             ;type out a few spaces
       TTY                             ;just incase of backrubs
       LEA     A6,USRNUM(A3)           ;get the user count
       DECB    @A6                     ;one less user
       CMPB    @A6,#1                  ;only you left?
       JNE     HC                      ;nope, do this mess again
       CALL    NXTGET                  ;else update user count
       RTN                             ;and return to TOP

;--- NXTGET subroutine: updates user count for TOP
;
NXTGET: LEA     A1,USRNUM(A3)           ;get user count (variable)
       LEA     A4,PRMCNT(A3)           ;and the "permanent count"
       MOVB    @A4,(A1)+               ;reset variable user number
       LEA     A1,FLAG(A3)             ;get the flag
       CLRB    @A1                     ;clear it even if it is already clear
       RTN                             ;and return to caller

BUFFER: ASCII   /CB.SYS/                ;the file that is in SYSTEM memory
       EVEN

SPACES: ASCII   /                                                                           /
       BYTE    0
       EVEN
ERRMSG: ASCII   /       End of buffer... hit [CR] to clear or RUBOUT to edit/
       BYTE    0
       EVEN

       END