;***** MSCAN V1.0 Source Code
;***** Quick message scan door for MAXsBBS V1.52+
;***** by Keith Bennett and Glen Martin 1996
;***** Last updated: 22-JAN-1996

;get some library offsets and constants

           include "Assempro:Includes/exec.offsets"
           include "Assempro:Includes/dos.offsets"
mode_old   equ     1005

;set up some macros for library calls

callsys:   macro   $\1                   ;macro for exec lib calls
           jsr     _LVO\1(a6)
           endm
calldos:   macro   $\1                   ;macro for dos lib calls
           move.l  doslibbase(pc),a6
           jsr     _LVO\1(a6)
           move.w  #4,a6
           move.l  (a6),a6
           endm

           bra     begin                 ;vanity embedding
           dc.b    "MSCAN V1.0 for MAXsBBS "
           dc.b    "by Keith Bennett and Glen Martin 1996."
           align

;***** START OF PROGRAM *****

begin:     nop                           ;do nothing!
           lea     InitialSP(PC),a5      ;store the current stack pointer
           move.l  sp,(a5)               ;in case we exit from a subroutine

           move.w  #4,a6                 ;get the exec library base in a6
           move.l  (a6),a6

;***** READ SELECTED SECTION NUMBERS

           subq.l  #1,d0                 ;did we get anything from the launch?
           beq     exit1                 ;if not then lets get out of here
           move.b  #255,d7               ;set section number error flag
           subq.l  #1,d0                 ;are there any parameters
           beq     getnoden              ;nope, then skip 
           clr.l   d7                    ;reset section number error flag
           move.l  a0,a1                 ;start of paramenters to a1

           lea     sections(PC),a2       ;point to section selection list
           clr.l   d3
paraloop:  bsr     readnum               ;read next section number
           tst.b   d7                    ;any error?
           bmi     getnoden              ;yes
           move.b  #1,(a2,d1)            ;set section as selected
backsep:   move.b  (a1)+,d2              ;read next character
           beq     getnoden              ;end of parameters
           cmp.b   #" ",d2               ;is it a space?
           beq     getnoden              ;yes, end of parameters
           cmp.b   #",",d2               ;is it a comma?
           beq     paraloop              ;yes, read next section
           move.b  #255,d7               ;set error flag
           cmp.b   #"-",d2               ;is it a dash? (range)
           bne     getnoden              ;no, (error)
           clr.l   d7                    ;reset error flag
           move.b  d1,d3                 ;remember previous number
           bsr     readnum               ;read next section number
           tst.b   d7                    ;any error?
           bmi     getnoden              ;yes
sloop:     cmp.b   d1,d3                 ;compare the values
           bhs     backsep               ;end of range?
           move.b  #1,(a2,d3)            ;set section as selected
           addq    #1,d3                 ;increment section number
           bra     sloop                 ;loop until range is selected

;***** SET UP MESSAGE PORTS TO/FROM MAXS BBS

getnoden:  add.l   d0,a0                 ;point a0 to node number
           lea     msgportn1+11(PC),a5   ;load the node number into the
           move.b  (a0),(a5)             ;message control port name
           lea     msgportn2+9(PC),a5    ;and the reply port name
           move.b  (a0),(a5)

           move.l  #140,d0               ;get some ram for the message port
           move.l  #$10001,d1
           callsys allocmem
           lea     msgport(PC),a5
           move.l  d0,(a5)
           beq     exit1                 ;cant get it? then exit

           sub.l   a1,a1                 ;find out who am I
           callsys findtask
           move.l  (a5),a0

           move.l  d0,16(a0)             ;get ourself a free signal
           moveq   #-1,d0
           callsys allocsignal
           move.l  (a5),a1

           move.b  #4,8(a1)              ;set up the message port
           lea     msgportn2(PC),a5
           move.l  a5,10(a1)
           move.b  d0,15(a1)
           bmi     exit2                 ;if no free signals then exit
           move.b  #5,42(a1)
           move.l  a1,48(a1)
           move.w  #106,52(a1)
           callsys addport               ;add our message port

           callsys forbid                ;find max's control port
           lea     msgportn1(PC),a1
           callsys findport
           lea     contport(PC),a5
           move.l  d0,(a5)
           callsys  permit
           tst.l   (a5)
           beq     exit3                 ;if we cant find it then exit

;***** INITIALISE EVERYTHING

           lea     doslibname(PC),a1     ;open dos library
           sub.l   d0,d0
           callsys openlibrary
           lea     doslibbase(PC),a5
           move.l  d0,(a5)
           beq     cleanup               ;if we cant open it then exit

           move.l  bufflen(PC),d0        ;get some ram for the file buffer
           move.l  #$10000,d1
           callsys allocmem
           lea     filebuff(PC),a5
           lea     txt07(PC),a0
           move.l  d0,(a5)
           beq     endit                 ;cant get it? then error and exit

           moveq   #8,d0                 ;ask max for the BBS path
           lea     textbuff(PC),a3
           bsr     getstring
           subq    #1,a2                 ;add on max's config filename
           lea     confname(PC),a0
loop02:    move.b  (a0)+,(a2)+
           bne     loop02
           bsr     openf                 ;open max's config file
           lea     txt03(PC),a0
           tst.l   d0                    ;was the open successful?
           beq     endit                 ;no, show error and exit
           move.l  d0,d1                 ;seek through max's config file to
           move.l  #144,d2               ;the message base path
           moveq   #-1,d3
           calldos seek
           move.l  filehand(PC),d1       ;read the message base path
           lea     textbuff(PC),a0
           move.l  a0,d2
           moveq   #41,d3
           calldos read
           lea     txt03(PC),a0
           tst.l   d0                    ;did it read ok?
           bmi     endit                 ;no, show error and exit
           move.l  filehand(PC),d1       ;close max's config file
           calldos close
           lea     filehand(PC),a0       ;clear filehandle
           clr.l   (a0)
           lea     textbuff(PC),a0       ;point to end of message base path
loop03:    move.b  (a0)+,d0
           bne     loop03
           subq    #1,a0
           lea     msgname(PC),a1        ;add on message.data filename
loop04:    move.b  (a1)+,(a0)+
           bne     loop04
           bsr     openf                 ;open message.data file
           lea     txt04(PC),a0
           tst.l   d0                    ;did it open ok?
           beq     endit                 ;no, show error and exit
           move.l  d0,d1                 ;seek though the message.data file to
           move.l  #470,d2               ;the message records
           move.l  #-1,d3
           calldos seek

           lea     username(PC),a3       ;get user's username from max
           moveq   #1,d0
           bsr     getstring

           lea     accessl(PC),a3        ;get user's access level from max
           moveq   #1,d0
           bsr     getint

           lea     pagelen(PC),a3        ;get user's page length from max
           moveq   #9,d0
           bsr     getint

           lea     txt01(PC),a0          ;point to error text
           cmp.b   #255,d7               ;is d7 error staus? (255)
           beq     endit                 ;yes, display error and exit

;***** THE MAIN BIT

           lea     txt05(PC),a0          ;print heading
           bsr     print
           lea     txt11(PC),a0          ;underline the heading
           bsr     print
           lea     txt11(PC),a0
           bsr     print
           lea     txt02(PC),a0          ;and print a CR
           bsr     print
           clr.l   d6                    ;clear message counter
           moveq   #6,d4                 ;set initial number of lines displayed

mainloop:  move.l  filehand(PC),d1       ;read next block of message data
           move.l  filebuff(PC),d2
           move.l  bufflen(PC),d3
           calldos read
           lea     txt04(PC),a0
           move.l  d0,d5                 ;get number of bytes read
           bmi     endit                 ;error reading
           beq     eof                   ;end of file
           move.l  filebuff(PC),a4       ;point to start of message data

secdloop:  btst    #0,27(a4)             ;deleted message?
           bne     next                  ;yes, next message
           lea     sections(PC),a0       ;point to selections
           move.b  28(a4),d7             ;read section number from message
           tst.b   (a0,d7)               ;it it selected?
           beq     next                  ;no, then next message
           tst.b   d7                    ;private section?
           bne     display               ;nope
           move.l  accessl(PC),d0        ;check access level
           cmp.w   #4999,d0              ;sysops see all private messages
           bhi     display
           lea     70(a4),a0             ;is it to user?             
           bsr     checkusrn
           tst.l   d0
           bne     display               ;yes, display it
           lea     29(a4),a0             ;is it from user?
           bsr     checkusrn
           tst.l   d0
           beq     next                  ;no, next message

display:   move.l  (a4),d0               ;read message number
           bsr     hxtoa                 ;convert to ASCII
           move.l  a1,a0                 ;point to ASCII number
           lea     textbuff(PC),a1       ;point to text buffer
           lea     color1(PC),a2         ;point to ANSI color code
           moveq   #8,d0                 ;set column width
           bsr     makeseg               ;make first column
           lea     183(a4),a0            ;point to from alias
           move.b  (a0),d0               ;is there an alias?
           bne     fromalias             ;yes, skip
           lea     29(a4),a0             ;no, point to to username
fromalias: lea     color2(PC),a2         ;point to from ANSI color code
           moveq   #20,d0                ;set column width
           bsr     makeseg               ;make the from column
           move.b  #0,(a1)               ;terminate line
           lea     textbuff(PC),a0       ;print 1st half of line
           bsr     print                 ;(we need to print this line in 2 parts
           lea     textbuff(PC),a1       ; so we don't overrun the message port)
           lea     224(a4),a0            ;point to to alias
           move.b  (a0),d0               ;is there a to alias?
           bne     toalias               ;yes, skip
           lea     70(a4),a0             ;no, point to to username
toalias:   lea     color3(PC),a2         ;point to ANSI color code
           moveq   #20,d0                ;set column width
           bsr     makeseg               ;make the to column
           lea     111(a4),a0            ;point to subject
           lea     color4(PC),a2         ;point to ANSI color code
           moveq   #31,d0                ;set column width
           bsr     makeseg               ;make the last column
           move.b  #13,(a1)+             ;add CR to line
           move.b  #0,(a1)               ;terminate line
           lea     textbuff(PC),a0       ;print 2nd half of line
           bsr     print

           addq    #1,d6                 ;increment message counter
           addq    #1,d4                 ;increment line count
           cmp.l   pagelen(PC),d4        ;end of page?
           blo     next                  ;no, next message
           lea     txt09(PC),a0          ;display hit return to continue...
           bsr     hotkey                ;wait for user to press key
           cmp.b   #'S',d0               ;did user hit 'S' to stop?
           beq     eof                   ;yes, jump to end bit
           lea     txt08(PC),a0          ;backspace over message
           bsr     print
           moveq   #4,d4                 ;reset line count

next:      add.w   #274,a4               ;point to next record
           sub.l   #274,d5               ;end of buffer?
           bhi     secdloop              ;no, next record
           bra     mainloop              ;yes, read next block

eof:       lea     txt02(PC),a0          ;print CR
           bsr     print
           move.l  d6,d0                 ;convert message count to ascii
           bsr     hxtoa
           move.l  a1,a0                 ;print message count
           bsr     print
           lea     txt10(PC),a0          ;point to message count text

endit:     bsr     print                 ;print message
           lea     txt06(PC),a0          ;display hit any key to end
           bsr     hotkey                ;and wait for key

;***** CLEAN UP AND EXIT

cleanup:   move.l  filehand(PC),d1       ;get file handle
           beq     nof                   ;skip if file is not open
           calldos close                 ;close file
nof:       move.l  filebuff(PC),a1       ;free file buffer ram
           move.l  a1,d0
           beq     nor                   ;but only if it's allocated
           move.l  bufflen(PC),d0
           callsys freemem
nor:       move.l  doslibbase(PC),a1     ;get dos lib base
           move.l  a1,d0                 ;test for zero
           beq.s   \skip                 ;is it open?
           callsys closelibrary          ;close if open
\skip:     bsr     getmsgport            ;get message port
           move.w  #20,(a2)              ;command #20 (exit door)
           move.l  contport(PC),a0       ;tell MAX's that i'm finished
           callsys putmsg
\wait:     move.l  msgport(PC),a0        ;wait for reply
           callsys waitport
           move.l  msgport(PC),a0        ;get reply
           callsys getmsg
           tst.l   d0
           beq     \wait
exit3:     move.l  msgport(PC),a1        ;remove our message port
           callsys remport
           sub.l   d0,d0                 ;free our signal
           move.l  msgport(PC),a0
           move.b  15(a0),d0
           callsys freesignal
exit2:     move.l  msgport(PC),a1        ;free message port ram
           move.l  #140,d0
           callsys freemem
exit1:     move.l  InitialSP(PC),sp      ;recover the stack pointer
           sub.l   d0,d0
           rts                           ;return
       
;***** READ A SECTION NUMBER (a1) to d1

readnum:   clr.l   d1                    ;clear results
           clr.l   d2
           move.b  1(a1),d1              ;read second character
           cmp.b   #'0',d1               ;is it a number?
           blo     digit1                ;no, only 1 digit
           cmp.b   #'9',d1               ;is it a number?
           bhi     digit1                ;no, only 1 digit
           move.b  (a1)+,d2              ;read first digit (10s)
           cmp.b   #'0',d2               ;is it a number?
           blo     numerr                ;no, error
           cmp.b   #'9',d2               ;is it a number?
           bhi     numerr                ;no error
           sub.b   #$30,d2               ;convert decimal to binary
           mulu    #10,d2                ;multiply by 10
digit1:    move.b  (a1)+,d1              ;read next digit
           cmp.b   #'0',d1               ;is it a number?
           blo     numerr                ;no, error
           cmp.b   #'9',d1               ;is it a number?
           bhi     numerr                ;no error
           sub.b   #$30,d1               ;convert decimal to binary
           add.b   d2,d1                 ;add the 10s
           rts                           ;return
numerr:    move.b  #255,d7               ;set error flag
           clr.l   d1                    ;clear result
           rts                           ;return

;***** MAKE A COLUMN OF THE LINE, (a0)=source, (a1)=dest, (a2)=ansi, d0=width

makeseg:   move.b  (a2)+,(a1)+           ;copy in ANSI color info
           bne     makeseg
           subq    #1,a1
           subq    #1,d0
\loop1:    move.b  (a0)+,d1              ;copy in info
           beq     \loop2                ;end of info
           move.b  d1,(a1)+
           dbra    d0,\loop1             ;loop up to width
           tst.b   (a1)                  ;are we at the end anyway?
           beq     \loop2                ;yes, no '+'
           move.b  #' ',-1(a1)           ;info is bigger than width 
           move.b  #'+',-2(a1)           ;so put in a '+' indicator
           rts                           ;return from subroutine
\loop2:    move.b  #" ",(a1)+            ;info is smaller that width
           dbra    d0,\loop2             ;so space fill the rest
           rts                           ;return from subroutine

;***** CHECK USERNAME WITH (a0), d0=0 for no match

checkusrn: moveq   #1,d0                 ;set match flag
           lea     username(PC),a1       ;point to user's username
loopu:     move.b  (a0)+,d1              ;read character from message username
           cmp.b   #" ",d1               ;is it a space?
           beq     compskip1             ;yes, dont convert
           and.b   #$df,d1               ;convert to uppercase
compskip1: move.b  (a1)+,d2              ;read character from username
           cmp.b   #" ",d2               ;is it a space?
           beq     compskip2             ;yes, dont convert
           and.b   #$df,d2               ;convert to uppercase
compskip2: cmp.b   d1,d2                 ;are they the same?
           bne     nomatch               ;no, reset match flag and return
           or.b    d1,d2                 ;both nulls? (end of username?)
           bne     loopu                 ;no, check next character
           rts                           ;yes, return (match)
nomatch:   clr.l   d0                    ;clear match flag
           rts                           ;return from subroutine

;***** PRINT (a0)

print:     bsr     getmsgport            ;get message port
           move.w  #1,(a2)+              ;command #1
           clr.w   (a2)+                 ;no data
ploop:     move.b  (a0)+,(a2)+           ;put print text into message
           bne     ploop
           bra     putwmsg               ;send the message then return

;***** DISPLAY (a0) AND HOTKEY TO d0

hotkey:    bsr     getmsgport            ;get message port
           move.w  #8,(a2)+              ;command #8
           clr.w   (a2)+                 ;no data
\loop:     move.b  (a0)+,(a2)+           ;copy print text into message
           bne     \loop
           bsr     putwmsg               ;send message to max
           move.b  (a0),d0               ;read keypress
           and.b   #$df,d0               ;convert to uppercase
           rts                           ;return from subroutine

;***** GET STRING INFO FROM MAX, d0=string type, result to (a3) 

getstring: bsr     getmsgport            ;get message port
           move.w  #14,(a2)+             ;command #14
           move.w  d0,(a2)               ;data=string type
           bsr     putwmsg               ;send request to max
           move.l  a3,a2                 ;store reply
\loop      move.b  (a0)+,(a2)+
           bne     \loop
           rts                           ;return from subroutine

;***** GET INTEGER INFO FROM MAX, d0=int type, result(.l) to (a3)

getint:    bsr     getmsgport            ;get message port
           move.w  #13,(a2)+             ;command #13
           move.w  d0,(a2)               ;data=int type
           bsr     putwmsg               ;send request to max
           sub.l   d0,d0                 ;read result
           move.w  -(a0),d0
           move.l  d0,(a3)               ;store result
           rts                           ;return from subroutine

;***** GET MESSAGE PORT

getmsgport:move.l  msgport(PC),a1        ;point to message port structure
           move.l  a1,a2
           adda.w  #34,a1                ;point a1 to message structure
           adda.w  #54,a2                ;point a2 to the message data
           clr.l   2(a2)                 ;clear the data field
           rts                           ;return

;***** SEND MESSAGE TO MAX's, WAIT FOR AND GET REPLY

putwmsg:   move.l  contport(PC),a0       ;point to message port structure
           callsys putmsg                ;send the message to MAX's
\wait:     move.l  msgport(PC),a0
           callsys waitport              ;wait for reply
           move.l  msgport(PC),a0
           callsys getmsg                ;get reply message
           tst.l   d0                    ;did we get a reply
           beq     \wait                 ;no, keep waiting
           move.l  d0,a0                 ;check for carrier loss
           adda.w  #24,a0
           tst.w   80(a0)
           bne     cleanup               ;carrier loss? if so then exit
           rts                           ;return

;***** OPEN A FILE

openf:     moveq   #4,d5                 ;number of times to attempt open
openloop:  lea     textbuff(PC),a0       ;point to file name
           move.l  a0,d1
           move.l  #mode_old,d2          ;open in read mode 
           calldos open                  ;open file
           tst.l   d0                    ;did it open?
           bne     openok                ;yes, store handle and return
           calldos ioerr                 ;no, get error code
           cmp.l   #202,d0               ;is file in use?
           bne     openerror             ;no, then it's a real error
           move.l  #64,d1                ;wait a few secs
           calldos delay
           dbra    d5,openloop           ;next attempt
openerror: clr.l   d0                    ;clear filehandle (error)
openok:    lea     filehand(PC),a0       ;store filehandle
           move.l  d0,(a0)
           rts                           ;return

;CONVERT HEX (BINARY) d0 TO ASCII (a1)

hxtoa:     tst.l   d0                    ;compliments Anthony Barrett's
           bne     skp04                 ;door starter pack
           lea     cbuf3(PC),a1 
           move.b  #'0',(a1)
           clr.b   1(a1)
           rts
skp04:     lea     cbuf3(PC),a1
           move.l  #$30303030,d1
           move.l  d1,(a1)+
           move.l  d1,(a1)+
           move.w  d1,(a1)
           suba.w  #28,a1
           move.l  d1,(a1)+
           move.l  d1,(a1)+
           move.w  #$3031,(a1)
nxbit:     btst    #0,d0
           bne     goada
retur:     lsr.l   #1,d0
           beq     endbit
           lea     cbuf2(PC),a1
           lea     cbuf1(PC),a2
           move.l  (a2)+,(a1)+
           move.l  (a2)+,(a1)+
           move.w  (a2),(a1)
           addq.l  #1,a1
           addq.l  #1,a2
           bsr     adasc
           bra     nxbit
endbit     lea     cbuf3(PC),a1
lop03:     cmp.b   #$30,(a1)+
           beq.s   lop03
           subq.l  #1,a1
           rts
goada:     lea     cbuf1+9(PC),a1
           lea     cbuf3+9(PC),a2
           bsr     adasc
           bra     retur
adasc:     moveq   #9,d1
next1:     move.b  (a1),d2
           sub.b   #$30,d2       
           add.b   d2,(a2)
check:     cmp.b   #$39,(a2)
           bhi     overf
           subq.l  #1,a2
retr2:     subq.l  #1,a1
           dbf     d1,next1
           rts
overf:     sub.b   #10,(a2)
           addq.b  #1,-(a2)
           movem.l a1/a2/d1,-(sp)
           sub.l   d1,d1
           bsr     check
           movem.l (sp)+,d1/a2/a1
           bra     retr2

;***** DATA
;file and port names

msgportn1: dc.b    "DoorControl#",0      ;message port name
msgportn2: dc.b    "DoorReply#",0        ;message port name
doslibname:dc.b    "dos.library",0       ;dos library name
msgname:   dc.b    "Message.data",0      ;message base file name

;error messages

txt01:     dc.b    13,"Illegal message section selection format",13,0
txt03:     dc.b    13,"Error accessing MAXsBBS config file",13,0
txt04:     dc.b    13,"Error accessing Message base",13,0
txt07:     dc.b    13,"Error allocating RAM",13,0

;text

txt02:     dc.b    $1b,"[0m",13,0
txt06:     dc.b    13,"Hit any key to continue",0
txt08:     dc.b    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
           dc.b    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0
txt09:     dc.b    $1b,"[0;1;33mHit <RETURN> to continue (S - stop)... ",0
txt10:     dc.b    " messages were listed.",13,0
txt11:     dc.b    $1b,"[1;37m---------------------------------------",0
           align.l

;storage

msgport:   dc.l    0                     ;message port
contport:  dc.l    0                     ;control port pointer
doslibbase:dc.l    0                     ;dosbase store
InitialSP: dc.l    0                     ;stack pointer store
filehand:  dc.l    0                     ;file handle
accessl:   dc.l    0                     ;access level
pagelen:   dc.l    0                     ;page length
filebuff:  dc.l    0                     ;file buffer pointer
username:  blk.b   44,0                  ;username
textbuff:  blk.b   128,0                 ;text buffer
cbuf1:     blk.b   10,0                  ;hex to acsii conversion ram
cbuf2:     blk.b   10,0
cbuf3:     blk.b   12,0
sections:  blk.b   100,0                 ;section selection list
           align

;file buffer length

           dc.b    " File buffer length, must be a multiple of 274 (binary):"
bufflen:   dc.l    274*100               ;read 100 messages at a time
           
;hex editable text           

           dc.b    "You can hex edit these strings, all must be 0 terminated",13
           dc.b    "MAXs CONFIG FILE NAME:"
confname:  dc.b    "MAXsBBS.Config",0,0,0,0,0,0,0,0,0,0,0,0,0
           dc.b    "HEADING TEXT:"
txt05:     dc.b    $1b,"[H",$1b,"[2J",$1b,"[0;1;37mMSG No. FROM:               "
           dc.b    "TO:                 SUBJECT:",13,0,0,0,0,0,0,0
           dc.b    "MSG No. COLOR:"
color1:    dc.b    $1b,"[0;32m",0,0,0,0,0
           dc.b    "TO COLOR:"
color2:    dc.b    $1b,"[0;37m",0,0,0,0,0
           dc.b    "FROM COLOR:"
color3:    dc.b    $1b,"[0;37m",0,0,0,0,0
           dc.b    "SUBJECT COLOR:"
color4:    dc.b    $1b,"[0;36m",0,0,0,0,0
           
           end

