home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / pcmag / vol8n19.zip / 1STCLASS.ASM next >
Assembly Source File  |  1989-10-03  |  41KB  |  1,698 lines

  1.     PAGE    60,132
  2.     TITLE    '1stClass:  MCI Mail Agent'
  3.  
  4. CINT    =    14H        ; interrupt for Couriers
  5. FIFOSIZE=    512        ; size for COM-port input FIFO
  6. FNSIZE    =    65        ; maximum filename size
  7. ATTESC    =    '!'        ; attachment escape character
  8. CLOCK    =    46CH        ; low-memory timer word
  9. SBS    =    128        ; small-buffer size
  10. LBS    =    512        ; large buffer size:  must be at least (SBS+1)*3
  11. BELL    =    07H        ; ASCII bell
  12. BS    =    08H        ; ASCII backspace
  13. CR    =    0DH        ; ASCII carriage return
  14. ESCAPE    =    1BH        ; ASCII escape
  15. LF    =    0AH        ; ASCII linefeed
  16. TAB    =    09H        ; ASCII tab
  17.  
  18. ; Mail Link message types (internal enumeration only)
  19.  
  20. MT_COMMENT    =    0
  21. MT_CREATE    =    1
  22. MT_END        =    2
  23. MT_ENV        =    3
  24. MT_INIT        =    4
  25. MT_REPLY    =    5
  26. MT_RESET    =    6
  27. MT_SEND        =    7
  28. MT_TERM        =    8
  29. MT_TEXT        =    9
  30. MT_TURN        =    10
  31.  
  32. PSEG    SEGMENT
  33.     ASSUME    CS:PSEG, DS:PSEG, ES:PSEG
  34.  
  35. ; Entry point
  36.  
  37.     ORG    100H
  38. ENTRY    PROC    NEAR
  39.     XOR    AX,AX            ; jump to CS:0000 to exit
  40.     PUSH    AX
  41.     CLD
  42.     CALL    TTY$            ; announce 1stClass
  43.     DW    OFFSET INITMSG
  44.     MOV    AH,80H            ; check if Couriers is loaded
  45.     INT    CINT
  46.     CMP    AH,232
  47.     JNE    .EN5            ; if Couriers is not there
  48.     MOV    AH,3CH            ; create log file for session transcript
  49.     MOV    DX,OFFSET LOGFN        ; DX -> "1STCLASS.LOG"
  50.     XOR    CX,CX
  51.     INT    21H
  52.     MOV    LOGFH,AX        ; save handle
  53.     JC    .EN3            ; if create failed
  54.     MOV    AX,3D01H        ; open file for incoming mail
  55.     MOV    DX,OFFSET INFN        ; DX -> "MAIL.IN"
  56.     INT    21H
  57.     JNC    .EN2            ; if opened successfully
  58.     MOV    AH,3CH            ; else try a create
  59.     XOR    CX,CX
  60.     INT    21H
  61.     JC    .EN3            ; if we cannot get the file at all
  62.  
  63. .EN2:    MOV    MAILIN,AX
  64.     MOV    BX,AX            ; BX = handle
  65.     MOV    AX,4202H        ; move file pointer to end
  66.     XOR    CX,CX
  67.     XOR    DX,DX
  68.     INT    21H    
  69.     MOV    DX,OFFSET CSFN        ; DX -> "1STCLASS.CSF"
  70.     CALL    SCRIPT            ; process the script
  71.     JMP    CLEAN            ; go clean up and exit
  72.  
  73. .EN3:    JMP    OPEN_ERROR
  74.  
  75. .EN5:    CALL    TTY$            ; complain that Couriers is not loaded
  76.     DW    OFFSET LOADMSG
  77.     RET
  78. ENTRY    ENDP
  79.  
  80. ; Append a carriage return and linefeed to a string
  81.  
  82. APPEND_CRLF    PROC NEAR    ; DI -> string
  83.     MOV    AX,(LF SHL 8) OR CR
  84.     STOSW
  85.     RET
  86. APPEND_CRLF ENDP    ; returns DI = updated string pointer
  87.  
  88. ; Process an !ATTACH instruction
  89.  
  90. ATTACH_FILE    PROC    NEAR    ; SI -> Attach command
  91.     INC    SI            ; push SI past escape character
  92.     CALL    FOLDUP            ; fold line to uppercase
  93.     MOV    BX,OFFSET INST_KEYS    ; match an instruction key
  94.     CALL    MATCH
  95.     JNZ    .AF4            ; if not "ATTACH"
  96.     CALL    SKIP_WHITESPACE
  97.     MOV    DI,OFFSET FILENAME
  98.     MOV    DX,DI            ; DX, DI -> filename area
  99.  
  100. .AF1:    LODSB                ; peel off filename
  101.     CMP    AL,' '            ; note that length is not checked!
  102.     JLE    .AF2
  103.     STOSB
  104.     JMP    SHORT .AF1
  105.  
  106. .AF2:    XOR    AX,AX
  107.     STOSB
  108.  
  109.     CALL    TTY$            ; tell user about attachment
  110.     DW    OFFSET ATMSG
  111.     MOV    BX,DX            ; BX -> filename
  112.     CALL    TTYZ
  113.     MOV    AX,3D00H        ; open for read, DX -> filename
  114.     INT    21H
  115.     MOV    BX,AX            ; BX = handle
  116.     JC    .AF3            ; if error
  117.     PUSH    BX
  118.     MOV    SI,DX            ; SI -> filename
  119.  
  120. .AF24:    LODSB                ; scan past path
  121.     TEST    AL,AL
  122.     JZ    .AF28
  123.     CMP    AL,':'
  124.     JE    .AF26
  125.     CMP    AL,'\'
  126.     JNE    .AF24
  127.  
  128. .AF26:    MOV    DX,SI
  129.     JMP    SHORT .AF24
  130.  
  131. .AF28:    MOV    SI,DX            ; send FILENAME.EXT with attachment
  132.     CALL    SEND_FILE        ; send the file
  133.     MOV    AH,3EH            ; and close the file
  134.     POP    BX            ; BX = handle
  135.     INT    21H
  136.     RET
  137.  
  138. .AF3:    JMP    OPEN_ERROR        ; complain that file cannot be opened
  139.  
  140. .AF4:    CALL    TTY$            ; complain about invalid !command
  141.     DW    OFFSET INSTMSG
  142.  
  143. .AF5:    MOV    BX,OFFSET LINE_BUFFER    ; and echo the command
  144.     CALL    TTYZ
  145.     RET
  146. ATTACH_FILE    ENDP
  147.  
  148. ; Calculate a checksum
  149.  
  150. CALCULATE_CHECKSUM    PROC    NEAR        ; SI -> message to be summed
  151.     PUSH    SI
  152.     MOV    BX,CHECKSUM        ; accumulate sum in BX
  153.     XOR    AX,AX
  154.  
  155. .CC1:    LODSB                ; load each byte
  156.     ADD    BX,AX            ; and add to the checksum
  157.     TEST    AL,AL            ; until a null is encountered
  158.     JNZ    .CC1
  159.  
  160.     MOV    AX,BX
  161.     MOV    CHECKSUM,AX        ; store checksum
  162.     POP    SI
  163.     RET        ; returns AX = checksum, kills BX
  164. CALCULATE_CHECKSUM    ENDP
  165.  
  166. ; Clean up and exit
  167.  
  168. CLEAN    PROC    NEAR
  169.     MOV    AX,8D00H        ; deconfigure COM port
  170.     OR    AL,COM_PORT
  171.     JZ    .CL1            ; unless we never configured one
  172.     INT    CINT
  173.  
  174. .CL1:    XOR    BX,BX
  175.     JMP    BX
  176. CLEAN    ENDP
  177.  
  178. ; Process a received COMMENT or RESET command
  179.  
  180. COMMENTARY    PROC    NEAR
  181.     INC    ECHO_LINE        ; display this reply
  182.     CALL    RECEIVE_MESSAGE        ; wait for rest of commentary
  183.     DEC    ECHO_LINE
  184.     XOR    BX,BX            ; no text in reply
  185.     RET
  186. COMMENTARY    ENDP
  187.  
  188. ; Copy ASCII characters to a Mail Link message, mapping as required
  189.  
  190. COPY_ASCII    PROC    NEAR    ; SI -> source (null terminated), DI -> destination
  191.     PUSH    DI
  192.  
  193. .CA1:    LODSB                ; AL = next character
  194.     TEST    AL,AL
  195.     JZ    .CA3            ; if at end of message
  196.     CMP    AL,1AH            ; check for end-of-file
  197.     JE    .CA4
  198.     CMP    AL,'*'            ; translate asterisk, slash and percent
  199.     JE    .CA2
  200.     CMP    AL,'/'
  201.     JE    .CA2
  202.     CMP    AL,'%'
  203.     JE    .CA2
  204.     STOSB
  205.     JMP    SHORT .CA1
  206.  
  207. .CA2:    MOV    AH,AL            ; AH = ASCII code for PUTHEX
  208.     MOV    AL,'%'            ; preface hex by a percent sign
  209.     STOSB
  210.     MOV    CH,2            ; encode a 2-character hex
  211.     CALL    PUTHEX
  212.     JMP    SHORT .CA1
  213.  
  214. .CA3:    POP    SI
  215.     CLC
  216.     RET
  217.  
  218. .CA4:    POP    SI
  219.     STC
  220.     RET        ; returns SI -> destination, updated pointer in DI
  221. COPY_ASCII ENDP        ; CF = 1 if Ctrl-Z end-of-file detected
  222.  
  223. ; Copy a string of bytes, mapping codes to ASCII as required
  224. ;
  225. ; Basic algorithm:
  226. ;    Fill buffer up to CR/LF then flush but if buffer reaches 197
  227. ;    characters then append a %CR/LF and flush.  If data does not end
  228. ;    with a CR/LF then append a %CRLF.
  229.  
  230. COPY_BINARY    PROC    NEAR    ; BX = handle to read from
  231.     MOV    DI,OFFSET XM_BUFFER    ; SI, DI -> transmit buffer
  232.     MOV    SI,DI
  233.  
  234. .CB1:    CALL    READ_BYTE
  235.     JC    .CB8            ; on EOF
  236.  
  237. .CB2:    CMP    AL,CR            ; special considerations for CRs
  238.     JE    .CB5
  239.     CMP    AL,TAB            ; tabs go through as is
  240.     JE    .CB3
  241.     CMP    AL,' '            ; all other control chars get mapped
  242.     JB    .CB4
  243.     CMP    AL,'*'            ; so do asterisk, slash and percent
  244.     JE    .CB4
  245.     CMP    AL,'/'
  246.     JE    .CB4
  247.     CMP    AL,'%'
  248.     JE    .CB4
  249.     CMP    AL,7EH            ; DEL and all extended chars
  250.     JAE    .CB4
  251.  
  252. .CB3:    STOSB                ; stuff the byte into the buffer
  253.     CALL    FLUSH_CHECK        ; flush buffer if it's full
  254.     JMP    SHORT .CB1
  255.  
  256. .CB4:    CALL    MAP_BYTE        ; map the byte and check the buffer
  257.     JMP    SHORT .CB1
  258.  
  259. .CB5:    CALL    READ_BYTE        ; got a CR, see what's next
  260.     JNC    .CB6            ; if anything but EOF
  261.     MOV    AL,CR            ; and send the CR mapped
  262.     CALL    MAP_BYTE
  263.     JMP    SHORT .CB8        ; then tie things up
  264.  
  265. .CB6:    CMP    AL,LF            ; do we have a nice CR/LF?
  266.     JNE    .CB7            ; oh dear
  267.     CALL    APPEND_CRLF        ; buffer CR/LF
  268.     CALL    TRANSMIT        ; flush buffer and reset pointers
  269.     CALL    DOTTY            ; drive user dotty
  270.     JMP    .CB1
  271.  
  272. .CB7:    PUSH    AX            ; save second character
  273.     MOV    AL,CR            ; and send the CR mapped
  274.     CALL    MAP_BYTE
  275.     POP    AX            ; retrieve next character
  276.     JMP    .CB2            ; and handle normally
  277.  
  278. .CB8:    CMP    SI,DI            ; anything in the buffer?
  279.     JE    .CB9            ; if not we are done
  280.     MOV    AL,'%'            ; else append a delimited CR/LF
  281.     STOSB
  282.     CALL    APPEND_CRLF
  283.     CALL    TRANSMIT        ; and flush the buffer
  284.  
  285. .CB9:    RET
  286. COPY_BINARY ENDP
  287.  
  288. ; Copy a null-terminated string
  289.  
  290. COPY_STRING    PROC    NEAR    ; SI -> string, DI -> destination
  291.  
  292. .CS1:    LODSB
  293.     STOSB
  294.     TEST    AL,AL
  295.     JNZ    .CS1
  296.     DEC    DI
  297.     RET        ; returns SI -> end of string, DI -> next position
  298. COPY_STRING    ENDP
  299.  
  300. ; Handle a received CREATE command -- which means doing nothing
  301.  
  302. CREATE    PROC    NEAR
  303.     XOR    BX,BX            ; no text in reply
  304.     RET
  305. CREATE    ENDP
  306.  
  307. ; Delay for a certain number of 18.2-to-a-second clock ticks.  The method used
  308. ; is guaranteed never to be shorter than the specified period but it may be
  309. ; longer.
  310.  
  311. DELAY    PROC    NEAR    ; TIMEOUT set for number of ticks to delay
  312.     MOV    CX,TIMEOUT        ; CX = ticks to delay
  313.     INC    CX            ; adjust for method
  314.     PUSH    DS
  315.     XOR    AX,AX
  316.     MOV    DS,AX            ; DS = 0
  317.  
  318. OS1:    CMP    AX,DS:[CLOCK]        ; read the system clock
  319.     JE    OS1            ; until it gets updated
  320.     MOV    AX,DS:[CLOCK]
  321.     LOOP    OS1            ; then count down one tick
  322.  
  323.     POP    DS
  324.     RET
  325. DELAY    ENDP
  326.  
  327. ; Display a dot on the screen to let the user know that things are progressing
  328.  
  329. DOTTY    PROC    NEAR
  330.     MOV    AH,02H            ; use DOS service 2
  331.     MOV    DL,'.'
  332.     INT    21H
  333.     RET
  334. DOTTY    ENDP
  335.  
  336. ; End a Mail Link command being prepared for transmission
  337.  
  338. END_COMMAND    PROC NEAR    ; AH = command type
  339.     MOV    DI,OFFSET XM_BUFFER    ; prepare message in transmit buffer
  340.     PUSH    DI
  341.     MOV    SI,OFFSET ENDER        ; SI -> '/END '
  342.     MOV    CX,L_ENDER
  343.     REP    MOVSB
  344.     CALL    KEY            ; get SI -> key name
  345.     CALL    COPY_STRING        ; copy key up to null
  346.     POP    SI            ; SI -> XM_BUFFER
  347.     JMP    SENDER            ; append checksum and transmit
  348. END_COMMAND ENDP
  349.  
  350. ; Handle a received ENV command
  351.  
  352. ENVELOPE    PROC    NEAR
  353.     MOV    AX,MAILIN        ; set up incoming mail file
  354.     MOV    WRITE_HANDLE,AX
  355.     INC    ECHO_LINE        ; echo envelope to screen
  356.     CALL    RECEIVE_MESSAGE
  357.     DEC    ECHO_LINE
  358.     MOV    SI,OFFSET EOE        ; append a blank line
  359.     CALL    WRITELINE
  360.     XOR    BX,BX            ; no text in reply
  361.     RET
  362. ENVELOPE    ENDP
  363.  
  364. ERROR    PROC    NEAR    ; DX -> diagnostic message
  365.     POP    AX            ; AX = return address
  366.     MOV    ERROR_AT,AX
  367.     MOV    AH,9            ; display the message
  368.     INT    21H
  369.     JMP    CLEAN            ; disconnect
  370. ERROR    ENDP
  371.  
  372. ; Check if buffer needs to be flushed during "binary" transmission
  373.  
  374. FLUSH_CHECK    PROC    NEAR    ; SI -> start of buffer, DI -> next byte
  375.     CMP    DI,OFFSET XM_BUFFER+195
  376.     JB    .FC1            ; if < 195 chars in buffer
  377.     MOV    AL,'%'            ; else append a delimited CR/LF
  378.     STOSB
  379.     CALL    APPEND_CRLF
  380.     CALL    TRANSMIT        ; flush the buffer and reset SI and DI
  381.     CALL    DOTTY
  382.  
  383. .FC1:    RET
  384. FLUSH_CHECK    ENDP
  385.  
  386. ; Fold a line of text to uppercase
  387.  
  388. FOLDUP    PROC    NEAR    ; SI -> null-terminated text
  389.     PUSH    SI
  390.     MOV    DI,SI
  391.  
  392. .FU1:    LODSB                ; AL = next character
  393.     CMP    AL,'a'            ; skip if not lowercase alphabetic
  394.     JL    .FU2
  395.     CMP    AL,'z'
  396.     JG    .FU2
  397.     SUB    AL,'a' - 'A'
  398.  
  399. .FU2:    STOSB                ; re-store letter even if not changed
  400.     TEST    AL,AL            ; test for end of line
  401.     JNZ    .FU1
  402.     POP    SI
  403.     RET
  404. FOLDUP    ENDP        ; returns SI -> text
  405.  
  406. ; Decode a decimal integer
  407.  
  408. GETDEC    PROC    NEAR    ; SI -> string to be decoded,  AX = default value
  409.     PUSH    AX            ; save default
  410.     CALL    SKIP_WHITESPACE        ; skip any white space before the no.
  411.     XOR    AX,AX
  412.     MOV    BX,AX            ; BX used to accumulate the result
  413.     MOV    CX,AX            ; CX used as character counter
  414.  
  415. GD1:    LODSB                ; AL = next character
  416.     CMP    AL,'0'            ; stop when any non-digit is seen
  417.     JB    GD2
  418.     CMP    AL,'9'
  419.     JA    GD2
  420.     SUB    AL,'0'            ; convert digit to binary
  421.     CBW
  422.     XCHG    AX,BX
  423.     ADD    AX,AX            ; multiply partial result by 10
  424.     MOV    DX,AX
  425.     ADD    AX,AX
  426.     ADD    AX,AX
  427.     ADD    AX,DX
  428.     ADD    BX,AX            ; then add in the new digit
  429.     INC    CX            ; count characters
  430.     JMP    SHORT GD1
  431.  
  432. GD2:    XCHG    AX,BX
  433.     TEST    CX,CX
  434.     POP    CX            ; CX = default value
  435.     JNZ    GD3            ; if length was non-zero
  436.     MOV    AX,CX            ; else return default value
  437.  
  438. GD3:    DEC    SI            ; point SI to next character
  439.     RET        ; returns AX = decoded value, BL = terminator
  440. GETDEC    ENDP
  441.  
  442. ; Decode a hexadecimal number of a fixed number of characters
  443.  
  444. GETHEX    PROC    NEAR    ; SI -> hex string to be decoded, CH = number of hexits
  445.     XOR    AX,AX
  446.     MOV    BX,AX        ; BX will be used to accumulate the result
  447.     MOV    CL,04H        ; CH = char counter, CL = shift count
  448.  
  449. .GH1:    LODSB                ; we assume, do not check, that each
  450.     SUB    AL,'0'            ;   character is a valid hexit
  451.     CMP    AL,9
  452.     JBE    .GH2            ; if 0-9
  453.     SUB    AL,7            ; if A-F
  454.  
  455. .GH2:    SHL    BX,CL            ; shift partial result by 4 bits
  456.     ADD    BX,AX
  457.     DEC    CH
  458.     JNZ    .GH1
  459.     MOV    AX,BX
  460.     RET        ; returns AX = decoded value, SI -> next character
  461. GETHEX    ENDP
  462.  
  463. ; Get a keypress without echo
  464.  
  465. GETKEY    PROC
  466.     MOV    AH,7            ; use DOS service 7
  467.     INT    21H
  468.     TEST    AL,AL            ; ASCII code?
  469.     JNZ    GET1
  470.     MOV    AH,7            ; no, get the rest of it
  471.     INT    21H
  472.     XOR    AH,AH            ; set Z bit
  473. GET1:    RET        ; returns ZR = 0 if ASCII, ZR = 1 otherwise
  474. GETKEY    ENDP
  475.  
  476. ; Wait for, and process, the Mail Link reply to a command just sent
  477.  
  478. GET_REPLY    PROC    NEAR    ; AL = reply type expected (ignored at present)
  479.     CALL    RECEIVE_MESSAGE
  480.     CMP    AL,MT_REPLY        ; should be a reply
  481.     JNE    .GR_PF            ; if not...
  482.     MOV    BX,OFFSET MESSAGE_KEYS    ; determine reply type
  483.     CALL    MATCH
  484.     JNZ    .GR1            ; if not recognizable type
  485.     MOV    AX,100            ; assume good reply
  486.     CALL    GETDEC            ; get reply code
  487.     PUSH    AX
  488.     CMP    AX,100            ; echo reply if not type 100
  489.     JE    .GR2
  490.  
  491. .GR1:    INC    ECHO_LINE
  492.  
  493. .GR2:    CALL    RECEIVE_MESSAGE        ; wait for END
  494.     CMP    AL,MT_END
  495.     JNE    .GR_PF
  496.     XOR    AX,AX
  497.     MOV    ECHO_LINE,AL
  498.     POP    AX
  499.     CMP    AX,100
  500.     RET        ; returns AX = numeric code in reply, ZR = AX==100
  501.  
  502. .GR_PF:    MOV    DX,OFFSET PFMSG        ; protocol failure:  invalid message received
  503.     CALL    ERROR
  504. GET_REPLY    ENDP
  505.  
  506. ; Interpret a received Mail Link message
  507.  
  508. INTERPRET_PROTOCOL_LINE    PROC    NEAR    ; SI -> line, DI -> end of line
  509.     SUB    DI,2            ; assume line ends with CR/LF
  510.     XOR    AX,AX
  511.     STOSB                ; overwrite CR with null
  512.     INC    SI            ; point SI to first char past the /
  513.     MOV    BX,OFFSET MESSAGE_KEYS
  514.     CALL    MATCH            ; try to match message key
  515.     JNZ    .IPL3            ; if not recognized
  516.     PUSH    AX
  517.     CMP    AL,MT_END        ; an END?
  518.     JNE    .IPL1
  519.     CALL    SKIP_WHITESPACE        ; yes, skip past command type
  520.     MOV    BX,OFFSET MESSAGE_KEYS
  521.     CALL    MATCH            ; by matching message key
  522.  
  523. .IPL1:    CALL    SKIP_WHITESPACE        ; skip to checksum marker
  524.     CMP    AL,'*'
  525.     JNE    .IPL2
  526.     INC    SI
  527.     MOV    CH,4            ; decode a four-character hex number
  528.     CALL    GETHEX
  529.     SUB    STARSUM,AX
  530. .IPL2:    POP    AX
  531.     RET
  532.  
  533. .IPL3:    MOV    DX,OFFSET PFMSG        ; tell user we got a bad message
  534.     CALL    ERROR
  535. INTERPRET_PROTOCOL_LINE    ENDP
  536.  
  537. ; Look up a message name from the table given an id
  538.  
  539. KEY    PROC    NEAR    ; AH = key id
  540.     PUSH    DI
  541.     MOV    DI,OFFSET MESSAGE_KEYS    ; DI -> message name table
  542.     XOR    AL,AL
  543.     
  544. .KEY1:    TEST    AH,AH            ; look up message type
  545.     JZ    .KEY2
  546.     DEC    AH
  547.     MOV    CX,0FFFFH
  548.     REPNZ    SCASB            ; scan to next message
  549.     JMP    SHORT .KEY1
  550.  
  551. .KEY2:    MOV    SI,DI            ; SI -> key
  552.     POP    DI
  553.     RET        ; returns SI -> key name
  554. KEY    ENDP
  555.  
  556. ; Submit the piece of mail from the active file
  557.  
  558. MAILER    PROC    NEAR    ; DX -> filename
  559.     CALL    TTY$            ; tell user what's up[loading]
  560.     DW    OFFSET UPMSG
  561.     MOV    BX,DX            ; BX -> filename
  562.     CALL    TTYZ
  563.     MOV    AH,MT_CREATE        ; start by sending a CREATE command
  564.     CALL    MAKE_COMMAND
  565.     CALL    SENDER
  566.     CALL    GET_REPLY
  567.     JNE    .MR25            ; if bad reply came ack (.MR7 is far)
  568.     MOV    AH,MT_ENV        ; then comes the envelope
  569.     CALL    MAKE_COMMAND
  570.     CALL    APPEND_CRLF
  571.     CALL    TRANSMIT
  572.  
  573. .MR1:    MOV    BX,READ_HANDLE
  574.     CALL    READLINE        ; read next line from the file
  575.     JC    .MR2            ; if read error
  576.     CALL    SKIP_WHITESPACE
  577.     TEST    AL,AL
  578.     JZ    .MR2            ; zero-length line => end of envelope
  579.     MOV    DI,OFFSET XM_BUFFER    ; DI-> transmit buffer
  580.     CALL    COPY_ASCII        ; map to approved ASCII
  581.     CALL    APPEND_CRLF
  582.     CALL    TRANSMIT
  583.     CALL    DOTTY            ; drive user dotty
  584.     JMP    SHORT .MR1
  585.  
  586. .MR2:    MOV    AH,MT_ENV        ; send the END ENV
  587.     CALL    END_COMMAND
  588.     CALL    GET_REPLY
  589.  
  590. .MR25:    JNE    .MR7            ; if bad reply came back
  591.     MOV    AH,MT_TEXT        ; send a TEXT ASCII command
  592.     CALL    MAKE_COMMAND
  593.     MOV    AL,' '
  594.     STOSB
  595.     MOV    SI,OFFSET K_ASCII
  596.     CALL    COPY_STRING
  597.     CALL    APPEND_CRLF
  598.     MOV    SI,OFFSET XM_BUFFER
  599.     CALL    TRANSMIT
  600.  
  601. .MR3:    MOV    BX,READ_HANDLE        ; read each line of the message text
  602.     CALL    READLINE
  603.     JC    .MR4            ; if EOF
  604.     CMP    BYTE PTR [SI],ATTESC    ; attachment escape?
  605.     JE    .MR4
  606.     MOV    DI,OFFSET XM_BUFFER
  607.     CALL    COPY_ASCII        ; map characters
  608.     CALL    APPEND_CRLF        ; end the line
  609.     CALL    TRANSMIT        ; and send it off
  610.     CALL    DOTTY            ; display one dot per line
  611.     JMP    SHORT .MR3
  612.  
  613. .MR4:    PUSH    SI
  614.     MOV    AH,MT_TEXT        ; send an END TEXT
  615.     CALL    END_COMMAND
  616.     CALL    GET_REPLY
  617.     JNE    .MR7            ; if bad reply came back
  618.     CALL    TTY$            ; end the dot stream neatly
  619.     DW    OFFSET CRLF
  620.     POP    SI
  621.  
  622. .MR45:    CMP    BYTE PTR [SI],ATTESC    ; attachment escape?
  623.     JNE    .MR6
  624.  
  625. .MR5:    CALL    ATTACH_FILE        ; attach a file to the message
  626.     MOV    BX,READ_HANDLE
  627.     CALL    READLINE        ; then read next line
  628.     JNC    .MR45            ; if not EOF
  629.  
  630. .MR6:    MOV    AH,MT_SEND        ; send a SEND command
  631.     CALL    MAKE_COMMAND
  632.     CALL    SENDER
  633.     CALL    GET_REPLY
  634.  
  635. .MR7:    RET    ; returns ZR = 1 if mail was successfully sent
  636. MAILER    ENDP
  637.  
  638. ; Prepare a Mail Link command header for transmission
  639.  
  640. MAKE_COMMAND    PROC    NEAR    ; AH = command type
  641.     CALL    KEY            ; get SI -> key name
  642.     MOV    DI,OFFSET XM_BUFFER    ; prepare message in transmit buffer
  643.     PUSH    DI
  644.     MOV    BYTE PTR ES:[DI],'/'    ; start with a '/'
  645.     INC    DI
  646.     CALL    COPY_STRING        ; copy key up to null
  647.     XOR    AX,AX            ; reset checksum
  648.     MOV    CHECKSUM,AX
  649.     POP    SI            ; SI -> xm_buffer
  650.     RET        ; returns SI -> xm_buffer, DI -> next character slot
  651. MAKE_COMMAND ENDP
  652.  
  653. ; Map a byte for binary transmission.  (Called by COPY_BINARY)
  654.  
  655. MAP_BYTE    PROC    NEAR    ; AL = byte, DI -> buffer
  656.     MOV    AH,AL            ; AH = ASCII code for puthex
  657.     MOV    AL,'%'
  658.     STOSB
  659.     MOV    CH,2
  660.     CALL    PUTHEX
  661.     JMP    FLUSH_CHECK
  662. MAP_BYTE    ENDP
  663.  
  664. ; Play master role in Mail Link exchange
  665.  
  666. MASTER    PROC    NEAR
  667.     INC    MASTERED        ; record that we have played this role
  668.  
  669.     MOV    AL,SLAVED        ; are we doing this first or second?
  670.     TEST    AL,AL
  671.     JNZ    .MA1            ; if we slaved already
  672.  
  673.     MOV    ECHO_LINE,AL        ; turn off echoing to start
  674.     CALL    RECEIVE_MESSAGE        ; assume we get a REPLY INIT
  675.     CALL    RECEIVE_LINE        ; Should be "Request performed..."
  676.     INC    ECHO_LINE        ; echo rest of reply
  677.     CALL    RECEIVE_MESSAGE
  678.     DEC    ECHO_LINE
  679.  
  680.     CALL    OUTGOING        ; send any outgoing mail
  681.  
  682.     MOV    AH,MT_TURN        ; then send a TURN command
  683.     CALL    MAKE_COMMAND        ;   to switch roles
  684.     CALL    SENDER
  685.     CALL    GET_REPLY
  686.     JMP    SLAVE            ; time to do our slaving
  687.  
  688. .MA1:    CALL    OUTGOING        ; send any outgoing messages
  689.  
  690.     MOV    AH,MT_TERM        ; then terminate
  691.     CALL    MAKE_COMMAND
  692.     CALL    SENDER
  693.     CALL    GET_REPLY
  694.     RET
  695. MASTER    ENDP
  696.  
  697. ; Match a string to a set of keys
  698.  
  699. MATCH    PROC    NEAR    ; BX -> list of keys, SI -> string to be matched
  700.     CALL    SKIP_WHITESPACE        ; skip any leading blanks
  701.     MOV    DI,SI            ; SI, DI -> first non-white char
  702.     XOR    CX,CX            ; count keys in CX
  703.  
  704. .MAT1:    MOV    SI,DI            ; SI -> target of match
  705.  
  706. .MAT2:    CMP    BYTE PTR [BX],0        ; check for end of table
  707.     JE    .MAT5
  708.     LODSB                ; AL = next character of string
  709.     CMP    AL,' '            ; match up to blank or control
  710.     JBE    .MAT4
  711.     CMP    AL,[BX]
  712.     PUSHF
  713.     INC    BX
  714.     POPF
  715.     JE    .MAT2
  716.  
  717. .MAT3:    CMP    BYTE PTR [BX],0
  718.     PUSHF
  719.     INC    BX
  720.     POPF
  721.     JNZ    .MAT3
  722.     INC    CX
  723.     CMP    BYTE PTR [BX],0
  724.     JNZ    .MAT1
  725.     INC    CX
  726.     RET                ; return with ZR = 0
  727.  
  728. .MAT4:    DEC    SI
  729.     CMP    BYTE PTR [BX],0
  730.  
  731. .MAT5:    MOV    AX,CX
  732.     RET        ; returns ZR = 1 if match, AX = key number
  733.             ;        and SI -> character past key
  734. MATCH    ENDP
  735.  
  736. ; Normalize translates a null-terminated string containing control characters
  737. ; in the form '^X'
  738.  
  739. NORMALIZE    PROC    NEAR    ; SI -> null-terminated string
  740.     PUSH    SI
  741.     MOV    DI,SI
  742.  
  743. .NOR1:    LODSB                ; AL = next character
  744.     MOV    ES:[DI],AL        ; re-store in string
  745.     OR    AL,AL
  746.     JZ    .NOR3            ; if end of string
  747.     CMP    AL,' '
  748.     JB    .NOR1            ; ignore "real" control characters
  749.     CMP    AL,'^'
  750.     JNE    .NOR2
  751.     LODSB
  752.     CMP    AL,'^'            ; ^^ means ^
  753.     JE    .NOR2
  754.     AND    AL,1FH            ; make a control
  755.  
  756. .NOR2:    STOSB                ; and store into string
  757.     JMP    SHORT .NOR1
  758.  
  759. .NOR3:    POP    SI
  760.     MOV    BX,SI
  761.     MOV    CX,DI            ; calculate new length
  762.     SUB    CX,BX
  763.     RET        ; returns BX = SI -> normalized string, CX = length
  764.             ;    DI -> end of string
  765. NORMALIZE ENDP
  766.  
  767. ; Display a message about a file-open error and quit
  768.  
  769. OPEN_ERROR    PROC    NEAR    ; DX -> filename
  770.     CALL    TTY$            ; say "Cannot open file: "
  771.     DW    OFFSET OPENMSG
  772.     MOV    BX,DX            ; and announce the filename
  773.     CALL    TTYZ
  774.     JMP    CLEAN            ; then quit
  775. OPEN_ERROR    ENDP
  776.  
  777. ; Send any outgoing mail
  778.  
  779. OUTGOING    PROC    NEAR
  780.     MOV    AH,4EH            ; DOS find first matching file function
  781.     MOV    DX,OFFSET OUTFN        ; look for "*.OUT"
  782.  
  783. .OG1:    INT    21H            ; find first/next matching dir entry
  784.     JC    .OG3            ; if error or no more files
  785.     MOV    AH,2FH            ; DOS get disk transfer address
  786.     INT    21H            ; returns BX -> DTA
  787.     MOV    DX,BX
  788.     ADD    DX,30            ; DX -> filename
  789.     MOV    AX,3D00H        ; open read-only
  790.     INT    21H
  791.     MOV    READ_HANDLE,AX
  792.     JC    .OG2            ; if open failed
  793.  
  794.     PUSH    DX            ; save pointer to name
  795.     CALL    MAILER            ; mail the file
  796.     PUSHF                ; save result flags
  797.     MOV    AH,3EH            ; DOS close function
  798.     MOV    BX,READ_HANDLE
  799.     INT    21H
  800.     POPF                ; did mailer succeed?
  801.     POP    SI            ; SI -> filename
  802.     JNZ    .OG2            ; no...
  803.     PUSH    SI
  804.     MOV    DI,OFFSET FILENAME
  805.     PUSH    DI
  806.  
  807. .OG15:    LODSB                ; rename the file with extension "MLD"
  808.     STOSB
  809.     CMP    AL,'.'
  810.     JNE    .OG15
  811.  
  812.     MOV    SI,OFFSET RENAME
  813.     MOV    CX,4
  814.     REP    MOVSB
  815.  
  816.     MOV    AH,41H            ; delete any already renamed file
  817.     POP    DX
  818.     INT    21H            ; ignore errors
  819.  
  820.     MOV    AH,56H            ; DOS rename file service
  821.     MOV    DI,DX            ; DI -> new name
  822.     POP    DX            ; DX -> old name
  823.     INT    21H
  824.  
  825. .OG2:    MOV    AH,4FH            ; DOS find next matching file
  826.     JMP    SHORT .OG1
  827.  
  828. .OG3:    RET
  829. OUTGOING    ENDP
  830.  
  831. ; Encode a number into a 3-digit decimal ASCII string
  832.  
  833. PUTDEC    PROC    NEAR    ; AX = number to be encoded, DI -> destination
  834.     IDIV    BYTE PTR HUNDRED    ; divide by 100
  835.     ADD    AL,'0'
  836.     STOSB
  837.     MOV    AL,AH
  838.     XOR    AH,AH
  839.     IDIV    BYTE PTR TEN
  840.     ADD    AX,'00'
  841.     STOSW
  842.     RET
  843. PUTDEC    ENDP
  844.  
  845. ; Encode a number into a hex string of a given number of characters
  846.  
  847. PUTHEX    PROC    NEAR    ; AX = number, DI -> destination for encoded hex number
  848.             ; CH = number of hexits required (1 - 4)
  849.     MOV    BX,AX            ; keep target in BX
  850.     MOV    CL,04H            ; CH = char counter, CL = shift count
  851.  
  852. .PH1:    ROL    BX,CL
  853.     MOV    AX,BX
  854.     AND    AL,0FH            ; mask off a nybble
  855.     ADD    AL,'0'
  856.     CMP    AL,'9'
  857.     JBE    .PH2            ; if 0 - 9
  858.     ADD    AL,7            ; if A - F
  859.  
  860. .PH2:    STOSB                ; store into the string
  861.     DEC    CH            ; decrement the character counter
  862.     JNZ    .PH1            ; if more to go
  863.     RET        ; returns DI = updated pointer
  864. PUTHEX    ENDP
  865.  
  866. ; Read a byte from the send file
  867.  
  868. READ_BYTE    PROC    NEAR
  869.     MOV    AH,3FH            ; DOS read file function
  870.     MOV    BX,SEND_HANDLE
  871.     MOV    CX,1            ; read one byte
  872.     MOV    DX,OFFSET READ_POT    ; into read_pot
  873.     INT    21H
  874.     JC    .RB1            ; if error
  875.     TEST    AX,AX
  876.     JZ    .RB1            ; zero count means end-of-file
  877.     MOV    AL,READ_POT
  878.     RET                ; TEST sets CF = 0
  879.  
  880. .RB1:    STC
  881.     RET    ; returns CF set if error or EOF else AL = character
  882. READ_BYTE    ENDP
  883.  
  884. ; Read line from file to line_buffer
  885.  
  886. READLINE    PROC    NEAR    ; BX = file handle
  887.     MOV    SI,OFFSET LINE_BUFFER    ; SI -> line_buffer
  888.     MOV    DI,SI            ; DI too
  889.     MOV    CX,1            ; read one byte at a time
  890.  
  891. .RE1:    MOV    AH,3FH            ; DOS read function
  892.     MOV    DX,SI            ; DS:DX -> buffer
  893.     INT    21H
  894.     JC    .RE5            ; if read error
  895.     TEST    AX,AX
  896.     JZ    .RE4            ; if EOF
  897.     AND    BYTE PTR [SI],07FH    ; mask the character just read
  898.     MOV    AL,[SI]            ; AL = byte just read
  899.     CMP    AL,' '            ; control character?
  900.     JB    .RE2            ; if so
  901.     INC    SI            ; else bump buffer pointer
  902.     CMP    SI,OFFSET LINE_BUFFER+79; and check for overflow
  903.     JB    .RE1            ; handle over-long lines ungracefully!
  904.  
  905. .RE3:    XOR    AX,AX            ; null terminate the line
  906.     MOV    [SI],AL
  907.     MOV    CX,SI            ; calculate its length
  908.     MOV    SI,DI
  909.     SUB    CX,SI            ; CX = line length
  910.     CLC
  911.     RET        ; return with CF zero and SI -> input, CX = length
  912.  
  913. .RE2:    CMP    AL,CR            ; check for CR
  914.     JNE    .RE1            ; and discard other control characters
  915.     JMP    SHORT .RE3        ; end the line on CR
  916.  
  917. .RE4:    CMP    SI,DI            ; accept a last line with no CR
  918.     JNE    .RE3
  919.  
  920. .RE5:    STC
  921.     RET        ; EOF or read error, return with CF set
  922. READLINE    ENDP
  923.  
  924. ; Receive a char, mask to ASCII, echo to screen
  925.  
  926. RECEIVE_ASCII    PROC    NEAR
  927.     CALL    RECEIVE_CHARACTER
  928.     AND    AL,07FH
  929.     PUSH    AX
  930.     MOV    AH,0EH            ; echo to screen
  931.     INT    10H            ; using BIOS tty write
  932.     POP    AX
  933.     RET
  934. RECEIVE_ASCII    ENDP
  935.  
  936. ; Receive a character from the line
  937.  
  938. RECEIVE_CHARACTER    PROC    NEAR
  939.  
  940. .RC1:    MOV    AH,84H            ; read next input
  941.     MOV    AL,COM_PORT
  942.     INT    CINT
  943.     JNZ    .RC2            ; if input returned...
  944.  
  945.     CALL    TIMER
  946.     JMP    SHORT .RC1        ; if not expired
  947.  
  948. .RC2:    TEST    AL,AL            ; ignore NULs (TYMNET sends a few)
  949.     JZ    .RC1
  950.     PUSH    AX
  951.     MOV    READ_POT,AL        ; save in memory
  952.     MOV    DX,OFFSET READ_POT    ; write to log file
  953.     MOV    BX,LOGFH
  954.     MOV    AH,40H
  955.     MOV    CX,1
  956.     INT    21H
  957.     POP    AX
  958.     XOR    AH,AH            ; add into checksum
  959.     ADD    CHECKSUM,AX
  960.     RET        ; returns AL = character
  961. RECEIVE_CHARACTER    ENDP
  962.  
  963. ; Receive a message from Mail Link partner
  964.  
  965. RECEIVE_LINE    PROC    NEAR
  966.     MOV    DI,OFFSET RM_BUFFER    ; read through this buffer
  967.     PUSH    DI
  968.     MOV    AX,364            ; wait 20 seconds per line
  969.     MOV    TIMEOUT,AX        ; set timeout
  970.     MOV    AX,LBS-2        ; AX = buffer size - 2
  971.     MOV    BUFFER_COUNTER,AX
  972.     CALL    RECEIVE_CHARACTER
  973.     PUSH    AX            ; save initial character
  974.     JMP    SHORT .RL2
  975.  
  976. .RL1:    CALL    RECEIVE_CHARACTER
  977.  
  978. .RL2:    STOSB                ; store into buffer
  979.     CMP    AL,'*'            ; checksum delimiter?
  980.     JNE    .RL3
  981.     MOV    BX,CHECKSUM
  982.     MOV    STARSUM,BX
  983.  
  984. .RL3:    CMP    AL,'%'            ; expanded code?
  985.     JNE    .RL4
  986.     MOV    SI,DI            ; SI -> hex code
  987.     CALL    RECEIVE_CHARACTER
  988.     STOSB
  989.     CALL    RECEIVE_CHARACTER
  990.     STOSB
  991.     MOV    DI,SI
  992.     DEC    DI
  993.     CMP    AL,LF            ; check for %/CR/LF
  994.     JE    .RL5
  995.     MOV    CH,2            ; decode 2-character hex number
  996.     CALL    GETHEX
  997.     STOSB
  998.  
  999. .RL4:    CMP    AL,LF
  1000.     JE    .RL5
  1001.     MOV    AX,1
  1002.     SUB    BUFFER_COUNTER,AX
  1003.     JG    .RL1
  1004.  
  1005. .RL5:    XOR    AX,AX            ; terminate with a NUL
  1006.     STOSB
  1007.     DEC    DI            ; DI -> null terminator
  1008.     POP    AX
  1009.     POP    SI
  1010.     RET    ; returns SI -> receive buffer, DI -> end of message
  1011.         ;         AL = initial character
  1012. RECEIVE_LINE    ENDP
  1013.  
  1014. ; Receive a Mail Link message
  1015.  
  1016. RECEIVE_MESSAGE    PROC    NEAR
  1017.  
  1018. .RM1:    CALL    RECEIVE_LINE
  1019.     CMP    AL,'/'            ; is it a protocol line?
  1020.     JE    .RM2            ; if so...
  1021.     CALL    WRITER
  1022.     TEST    BYTE PTR ECHO_LINE,0FFH
  1023.     JZ    .RM3
  1024.     MOV    BX,SI            ; BX -> line
  1025.     CALL    TTYZ
  1026.     JMP    SHORT .RM1
  1027.  
  1028. .RM3:    TEST    BYTE PTR DOTTER,0FFH
  1029.     JZ    .RM1
  1030.     CALL    DOTTY
  1031.     JMP    SHORT .RM1
  1032.  
  1033. .RM2:    JMP    INTERPRET_PROTOCOL_LINE
  1034. RECEIVE_MESSAGE    ENDP    ; returns AL = type number
  1035.  
  1036. ; Read and interpret a script
  1037.  
  1038. SCRIPT    PROC    NEAR    ; DX -> script file name
  1039.     MOV    AX,3D00H        ; open script file for read
  1040.     INT    21H
  1041.     MOV    SCRIPT_HANDLE,AX
  1042.     JNC    .SC1
  1043.     JMP    OPEN_ERROR        ; if open error reported
  1044.  
  1045. .SC1:    MOV    BX,SCRIPT_HANDLE    ; read first/next line of the script
  1046.     CALL    READLINE
  1047.     JNC    .SC2
  1048.     RET                ; return on end-of-file
  1049.  
  1050. .SC2:    LODSB                ; AL = first character
  1051.     PUSH    AX            ; save while decoding numeric argument
  1052.     XOR    AX,AX            ; default is always 0
  1053.     CALL    GETDEC
  1054.     MOV    CX,AX            ; CX = result
  1055.     MOV    BL,18            ; convert ticks to seconds
  1056.     MUL    BL
  1057.     MOV    TIMEOUT,AX        ; save for anyone that wants to use it
  1058.     CALL    SKIP_WHITESPACE
  1059.     CMP    AL,'"'            ; quoted argument?
  1060.     JNE    .SC3            ; if not assume okay
  1061.     INC    SI            ; else push pointer past it
  1062.     PUSH    SI            ; and look for closing quote
  1063.  
  1064. .SC25:    LODSB
  1065.     TEST    AL,AL            ; no worry if there is none
  1066.     JZ    .SC28
  1067.     CMP    AL,'"'
  1068.     JNE    .SC25
  1069.     MOV    BYTE PTR [SI-1],0    ; replace a closing quote with a NUL
  1070.  
  1071. .SC28:    POP    SI
  1072.  
  1073. .SC3:    POP    AX            ; retrieve command key
  1074.     MOV    BX,OFFSET .SC1        ; stack return address
  1075.     PUSH    BX            ; make like a CALL
  1076.     CALL    SWITCHER        ; with CX = numeric arg, SI -> next arg
  1077.  
  1078. SCRIPT_SWITCH    LABEL    BYTE
  1079.     DB    'B'            ; bps rate
  1080.     DB    SCRIPT_BPS - SCRIPT_SWITCH
  1081.     DB    'C'            ; comment
  1082.     DB    SCRIPT_COMMENT - SCRIPT_SWITCH
  1083.     DB    'D'            ; delay
  1084.     DB    SCRIPT_DELAY - SCRIPT_SWITCH
  1085.     DB    'E'            ; echo mode
  1086.     DB    SCRIPT_ECHO - SCRIPT_SWITCH
  1087.     DB    'M'            ; master mode
  1088.     DB    SCRIPT_MASTER - SCRIPT_SWITCH
  1089.     DB    'P'            ; set port
  1090.     DB    SCRIPT_PORT - SCRIPT_SWITCH
  1091.     DB    'S'            ; slave mode
  1092.     DB    SCRIPT_SLAVE - SCRIPT_SWITCH
  1093.     DB    'T'            ; transmit string
  1094.     DB    SCRIPT_TX - SCRIPT_SWITCH
  1095.     DB    'R'            ; receive
  1096.     DB    SCRIPT_RX - SCRIPT_SWITCH
  1097.     DB    0
  1098.     DB    SCRIPT_ERROR - SCRIPT_SWITCH
  1099.  
  1100. SCRIPT_ERROR:
  1101.     CALL    TTY$
  1102.     DW    OFFSET CSERM        ; "Error in connect script"
  1103.     POP    AX            ; clean local return address
  1104.     RET
  1105.  
  1106. SCRIPT_BPS:                ; reset line speed
  1107.     MOV    DI,OFFSET RM_BUFFER    ; receive characters into rm_buffer
  1108.     MOV    SI,DI
  1109.  
  1110. .SB1:    CALL    RECEIVE_ASCII
  1111.     STOSB
  1112.     CMP    AL,CR            ; until a CR is seen
  1113.     JNE    .SB1            ; note there is no length check!
  1114.  
  1115.     MOV    AX,COM_SPEED        ; AX = default speed
  1116.     CALL    GETDEC            ; decode the new speed
  1117.     CMP    AX,COM_SPEED        ; was it changed
  1118.     JE    .SB2            ; skip if not
  1119.     MOV    COM_SPEED,AX        ; record the new speed
  1120.     MOV    BX,AX            ; BX = speed
  1121.  
  1122.     MOV    AH,8CH            ; change speed on COM port
  1123.     MOV    AL,COM_PORT
  1124.     INT    CINT
  1125.  
  1126. .SB2:    RET
  1127.  
  1128. SCRIPT_COMMENT:                ; display a comment on the screen
  1129.     CALL    NORMALIZE        ; normalize the string
  1130.     JMP    TTYZ            ; and display it
  1131.  
  1132. SCRIPT_PORT:                ; set up COM-port parameters
  1133.     MOV    COM_PORT,CL        ; port number already decoded into CX
  1134.     CALL    GETDEC            ; decode the port speed
  1135.     MOV    COM_SPEED,AX        ; record it
  1136.     MOV    BX,AX            ; BX = speed for Couriers
  1137.     MOV    AH,82H            ; configure COM port
  1138.     MOV    AL,COM_PORT
  1139.     MOV    CX,COM_OPTIONS
  1140.     INT    CINT
  1141.  
  1142.     MOV    AH,83H            ; start input on port
  1143.     MOV    AL,COM_PORT
  1144.     MOV    BX,OFFSET FIFO
  1145.     MOV    CX,FIFOSIZE
  1146.     INT    CINT
  1147.  
  1148. ; for unclear reasons a delay is required here so wait a sec or 4
  1149.     ; fall through
  1150. SCRIPT_DELAY:                ; delay N seconds
  1151.     CALL    DELAY
  1152.     RET
  1153.  
  1154. SCRIPT_ECHO:                ; change mode for echoing received chars
  1155.     MOV    MODEM_ECHO,CL
  1156. .SEC1:    RET
  1157.  
  1158. SCRIPT_TX:                ; transmit a string
  1159.     CALL    NORMALIZE        ; normalize it
  1160.     TEST    BYTE PTR MODEM_ECHO,0FFH; echo mode on?
  1161.     JZ    .STX1            ; skip if not
  1162.     MOV    DX,BX            ; DX -> message
  1163.     MOV    BX,LOGFH        ; write to log file
  1164.     MOV    AH,40H
  1165.     INT    21H
  1166.  
  1167. .STX1:    JMP    TRANSMIT        ; shove it out the port
  1168.  
  1169. SCRIPT_RX:                ; wait to receive a string
  1170.     CALL    NORMALIZE        ; normalize the string expected
  1171.     JMP    WAITST            ; and wait for it
  1172.  
  1173. SCRIPT_MASTER:                ; start Mail Link processing as master
  1174.     JMP    MASTER
  1175.  
  1176. SCRIPT_SLAVE:                ; start Mail Link processing as slave
  1177.     JMP    SLAVE
  1178. SCRIPT    ENDP
  1179.  
  1180. ; Process a received SEND command
  1181.  
  1182. SEND    PROC    NEAR
  1183.     MOV    SI,OFFSET EOL        ; end letter with some blank lines
  1184.     CALL    WRITELINE
  1185.     XOR    BX,BX            ; no text in reply
  1186.     MOV    WRITE_HANDLE,BX        ; write no more to file
  1187.     RET
  1188. SEND    ENDP
  1189.  
  1190. ; Terminate and transmit a Mail Link message
  1191.  
  1192. SENDER    PROC    NEAR    ; SI -> buffer, DI -> tail
  1193.  
  1194. ; Append '*', calculate and append checksum, and terminate with CR/LF
  1195.  
  1196.     MOV    AL,'*'
  1197.     STOSB
  1198.     CALL    TRANSMIT        ; transmit up to '*'
  1199.     MOV    SI,OFFSET XM_BUFFER    ; encode checksum as a 4-char hex
  1200.     MOV    DI,SI
  1201.     MOV    AX,CHECKSUM
  1202.     MOV    CH,4
  1203.     CALL    PUTHEX
  1204.     CALL    APPEND_CRLF        ; append CR/LF
  1205.     JMP    TRANSMIT        ; and transmit that
  1206. SENDER    ENDP
  1207.  
  1208. ; Send a file as a binary attachment to a mail message
  1209.  
  1210. SEND_FILE    PROC    NEAR    ; BX = file handle, SI -> attachment note
  1211.     MOV    SEND_HANDLE,BX
  1212.     PUSH    SI
  1213.     MOV    AH,MT_TEXT        ; make a TEXT command
  1214.     CALL    MAKE_COMMAND
  1215.     MOV    AL,' '            ; append a blank
  1216.     STOSB
  1217.     MOV    SI,OFFSET K_BINARY    ; say it's BINARY
  1218.     CALL    COPY_STRING
  1219.     MOV    AL,':'
  1220.     STOSB
  1221.     POP    SI
  1222.     CALL    COPY_STRING        ; and copy the note
  1223.     CALL    APPEND_CRLF
  1224.     MOV    SI,OFFSET XM_BUFFER
  1225.     CALL    TRANSMIT        ; send the first line
  1226.     CALL    COPY_BINARY        ; copy the file
  1227.     MOV    AH,MT_TEXT        ; send END TEXT
  1228.     CALL    END_COMMAND
  1229.     CALL    TTY$            ; terminate dot stream on screen
  1230.     DW    OFFSET CRLF
  1231.     JMP    GET_REPLY        ; get TEXT reply
  1232. SEND_FILE ENDP
  1233.  
  1234. ; Send a Mail Link reply
  1235.  
  1236. SEND_REPLY    PROC    NEAR    ; AX = code (normally 100), BX -> text
  1237.     PUSH    AX
  1238.     MOV    AH,COMMAND_TYPE        ; AH = command id
  1239.     CALL    KEY            ; get SI -> key name
  1240.     PUSH    SI            ; save -> message key
  1241.     MOV    DI,OFFSET REPLY_BUFFER    ; prepare message in reply buffer
  1242.     MOV    SI,OFFSET REPLY_HEAD    ; copy in "/REPLY "
  1243.     MOV    CX,L_REPLY_HEAD
  1244.     REP    MOVSB
  1245.  
  1246.     POP    SI
  1247.     CALL    COPY_STRING        ; copy key up to null
  1248.     MOV    AL,' '            ; need a space after this
  1249.     STOSB
  1250.     POP    AX            ; retrieve code
  1251.     CALL    PUTDEC            ; encode it
  1252.     CALL    APPEND_CRLF        ; that does the first line
  1253.     TEST    BX,BX            ; any text?
  1254.     JZ    .SR6            ; if not
  1255.     MOV    SI,BX
  1256.     CALL    COPY_ASCII
  1257.  
  1258. .SR6:    CALL    APPEND_CRLF        ; that makes the second line
  1259.     MOV    SI,OFFSET REPLY_TAIL    ; third line is "/END REPLY..."
  1260.     MOV    CX,L_REPLY_TAIL
  1261.     REP    MOVSB
  1262.     XOR    AX,AX            ; append temporary null end marker
  1263.     MOV    BYTE PTR ES:[DI],AL
  1264.     MOV    CHECKSUM,AX        ; and reset checksum
  1265.     MOV    SI,OFFSET REPLY_BUFFER
  1266.     JMP    SENDER            ; join common message-send code
  1267. SEND_REPLY ENDP
  1268.  
  1269. ; Skip over blanks and tabs in a string
  1270.  
  1271. SKIP_WHITESPACE    PROC    NEAR    ; SI -> string
  1272.  
  1273. .SW1:    LODSB                ; AL = next character
  1274.     CMP    AL,' '
  1275.     JE    .SW1            ; if it's a space
  1276.     CMP    AL,09H
  1277.     JE    .SW1            ; if it's a tab
  1278.     DEC    SI            ; else SI -> first non-white
  1279.     RET        ; returns SI -> first non-white char, AL = said char
  1280. SKIP_WHITESPACE    ENDP
  1281.  
  1282. ; Play slave role in Mail Link exchange
  1283.  
  1284. SLAVE    PROC    NEAR
  1285.     INC    SLAVED            ; record that we have slaved
  1286.  
  1287. .SL1:    XOR    AX,AX            ; reset checksum
  1288.     MOV    CHECKSUM,AX
  1289.     CALL    RECEIVE_MESSAGE        ; receive a Mail Link command
  1290.  
  1291.     MOV    COMMAND_TYPE,AL        ; save the command id
  1292.     MOV    BX,OFFSET SLAVE_SWITCH    ; switch to command handler
  1293.     ADD    BX,AX
  1294.     ADD    BX,AX
  1295.     MOV    BX,[BX]
  1296.     TEST    BX,BX
  1297.     JZ    .SL1            ; entry can be noop
  1298.     CALL    BX            ; returns BX -> reply text
  1299.     MOV    AX,STARSUM        ; was checksum correct?
  1300.     TEST    AX,AX
  1301.     PUSHF
  1302.     MOV    AX,100            ; assume normal reply
  1303.     JZ    .SL2            ; if checksum okay
  1304.     MOV    AX,403            ; else send a REPLY 403 "Checksum error"
  1305.     MOV    BX,OFFSET CKERROR
  1306.  
  1307. .SL2:    CALL    SEND_REPLY        ; reply to command
  1308.     POPF
  1309.     JZ    .SL1            ; if checksum was okay
  1310.     MOV    DX,OFFSET BADMSG    ; protocol message with bad checksum received
  1311.     CALL    ERROR
  1312. SLAVE    ENDP
  1313.  
  1314. ; Perform a computer switch through a jump table
  1315.  
  1316. SWITCHER    PROC    NEAR    ; AL = code, [SP] -> switch table
  1317.     CLD
  1318.     POP    BX            ; BX -> switch table
  1319.     PUSH    SI
  1320.     MOV    SI,BX            ; SI -> switch table
  1321.     MOV    AH,AL            ; AH = switch code
  1322.     DEC    SI
  1323.  
  1324. SW1:    INC    SI
  1325.     LODSB                ; AL = code to match
  1326.     TEST    AL,AL
  1327.     JZ    SW2            ; if end of list
  1328.     CMP    AL,AH
  1329.     JNE    SW1
  1330.  
  1331. SW2:    XOR    AX,AX            ; prepare AX for byte offset
  1332.     LODSB
  1333.     ADD    BX,AX            ; from base of switch
  1334.     POP    SI
  1335.     JMP    BX
  1336. SWITCHER    ENDP
  1337.  
  1338. ; Process a TEXT command
  1339.  
  1340. TEXT    PROC    NEAR
  1341.     MOV    AX,MAILIN        ; set up incoming mail file
  1342.     MOV    WRITE_HANDLE,AX
  1343.     INC    DOTTER            ; show user we're busy
  1344.     CALL    SKIP_WHITESPACE        ; skip to text-type field
  1345.     TEST    AL,AL            ; but there may not be one
  1346.     JZ    .TE4            ; in which case it is treated as ASCII
  1347.     MOV    BX,OFFSET TEXT_KEYS    ; is it ASCII or BINARY?
  1348.     CALL    MATCH
  1349.     JNZ    .TE4            ; if unknown, treat as ASCII
  1350.     TEST    AX,AX
  1351.     JZ    .TE4            ; if ASCII
  1352.                     ; process a BINARY attachment
  1353.     INC    SI            ; normally a colon follows
  1354.     PUSH    SI            ; save -> attachment name
  1355.     CALL    TTY$            ; say we're downloading an attachment
  1356.     DW    OFFSET DOWNAT
  1357.     MOV    SI,OFFSET ATTACHMENT    ; note attachment in the mail file
  1358.     CALL    WRITELINE
  1359.     POP    SI
  1360.     CALL    WRITELINE
  1361.     MOV    DX,SI            ; DX -> filename from message
  1362.     MOV    AX,4300H        ; see if it exists
  1363.     INT    21H
  1364.     JC    .TE3            ; if it does not exist use this name
  1365.  
  1366. .TE2:    MOV    AH,2CH            ; else get time of day from DOS
  1367.     INT    21H            ;  to use in generating a filename
  1368.     MOV    AX,CX
  1369.     ADD    AX,DX
  1370.     MOV    CH,3
  1371.     MOV    DI,OFFSET ATTACH_EXT
  1372.     CALL    PUTHEX
  1373.  
  1374.     MOV    DX,OFFSET ATTACH_FN    ; DX -> filename
  1375.  
  1376. .TE3:    MOV    AH,3CH            ; DOS create file function
  1377.     XOR    CX,CX            ; zero attributes
  1378.     INT    21H
  1379.     JC    .TE2            ; if open failed
  1380.     MOV    WRITE_HANDLE,AX
  1381.     MOV    SI,DX            ; SI -> filename
  1382.     MOV    DI,OFFSET FILENAME    ; copy for later use
  1383.     PUSH    DI
  1384.     CALL    COPY_STRING
  1385.     CALL    RECEIVE_MESSAGE
  1386.     MOV    AH,3EH            ; DOS close function
  1387.     MOV    BX,WRITE_HANDLE
  1388.     INT    21H
  1389.     MOV    AX,MAILIN        ; note attachment within the mail
  1390.     MOV    WRITE_HANDLE,AX
  1391.     MOV    SI,OFFSET TOFILE
  1392.     CALL    WRITELINE
  1393.     POP    SI            ; SI -> filename
  1394.     CALL    WRITELINE
  1395.     JMP    SHORT .TE5
  1396.  
  1397. .TE4:    CALL    TTY$            ; say we're downloading a message
  1398.     DW    OFFSET DOWNER
  1399.     CALL    RECEIVE_MESSAGE
  1400.  
  1401. .TE5:    DEC    DOTTER
  1402.     CALL    TTY$
  1403.     DW    OFFSET CRLF
  1404.     XOR    BX,BX            ; no text in reply
  1405.     RET
  1406.  
  1407. .TE6:    MOV    DX,OFFSET PFMSG        ; note an unrecognized command
  1408.     CALL    ERROR
  1409. TEXT    ENDP
  1410.  
  1411. ; Count down ticks in TIMEOUT
  1412.  
  1413. TIMER    PROC    NEAR
  1414.     XOR    AX,AX
  1415.     CMP    AX,TIMEOUT        ; zero period
  1416.     JZ    .TIM1            ; means wait forever
  1417.  
  1418.     PUSH    DS
  1419.     MOV    DS,AX
  1420.     MOV    AX,DS:[CLOCK]
  1421.     POP    DS
  1422.     CMP    AX,TIME
  1423.     JE    .TIM1
  1424.     MOV    TIME,AX
  1425.     DEC    TIMEOUT
  1426.     JZ    .TIM3            ; if timer expired
  1427.  
  1428. .TIM1:    MOV    AH,1            ; check for keyboard input
  1429.     INT    16H            ; using the BIOS
  1430.     JZ    .TIM2            ; if no keyboard action
  1431.     CALL    GETKEY            ; else read the character
  1432.     CMP    AL,ESCAPE        ; is it an an Escape?
  1433.     JNE    .TIM2            ; ignore anything else
  1434.     MOV    DX,OFFSET INMSG        ; interrupted message
  1435.     CALL    ERROR
  1436.  
  1437. .TIM2:    RET
  1438.  
  1439. .TIM3:    MOV    DX,OFFSET TOMSG        ; tell user we timed out
  1440.     CALL    ERROR
  1441. TIMER    ENDP        ; returns only if timer not expired
  1442.  
  1443. ; Transmit some data
  1444.  
  1445. TRANSMIT    PROC    NEAR    ; SI -> start of buffer, DI -> end of buffer
  1446.     MOV    AX,1092            ; wait a minute for transmit to complete
  1447.     MOV    TIMEOUT,AX
  1448.     MOV    BYTE PTR [DI],0        ; ensure data is zero terminated
  1449.     MOV    BX,SI            ; BX -> buffer
  1450.     MOV    CX,DI            ; calculate length of buffer
  1451.     SUB    CX,SI
  1452.     MOV    AH,86H            ; start output
  1453.     MOV    AL,COM_PORT
  1454.     INT    CINT
  1455.  
  1456.     MOV    DX,BX            ; DX -> message
  1457.     MOV    BX,LOGFH        ; write to log file
  1458.     MOV    AH,40H
  1459.     INT    21H
  1460.  
  1461.     CALL    CALCULATE_CHECKSUM    ; gather checksum while transmitting
  1462.  
  1463. .TX1:    MOV    AH,87H            ; wait until output is done
  1464.     MOV    AL,COM_PORT
  1465.     INT    CINT
  1466.     JZ    .TX2
  1467.     CALL    TIMER
  1468.     JMP    SHORT .TX1
  1469.  
  1470. .TX2:    MOV    DI,SI
  1471.     RET        ; returns SI = DI -> start of buffer
  1472. TRANSMIT ENDP
  1473.  
  1474. ; Display a null-terminated string
  1475.  
  1476. TTYZ    PROC    NEAR    ; BX -> string
  1477.     PUSH    BX
  1478.  
  1479. .TZ1:    MOV    AL,[BX]            ; do it one character at a time
  1480.     INC    BX
  1481.     TEST    AL,AL
  1482.     JZ    .TZ2
  1483.     MOV    AH,0EH            ; suing the BIOS
  1484.     INT    10H
  1485.     JMP    SHORT .TZ1
  1486.  
  1487. .TZ2:    POP    BX
  1488.     RET
  1489. TTYZ    ENDP
  1490.  
  1491. ; Display a '$'-terminated message
  1492.  
  1493. TTY$    PROC    NEAR    ; (SP) -> address of '$'-terminated message
  1494.     POP    SI
  1495.     PUSH    AX
  1496.     PUSH    DX
  1497.     LODSW                ; AX -> message
  1498.     MOV    DX,AX
  1499.     MOV    AH,09H            ; use DOS service 9
  1500.     INT    21H
  1501.     POP    DX
  1502.     POP    AX
  1503.     JMP    SI
  1504. TTY$    ENDP
  1505.  
  1506. ; Process a received TURN command
  1507.  
  1508. TURN    PROC    NEAR
  1509.     XOR    BX,BX            ; reply
  1510.     MOV    AX,100
  1511.     CALL    SEND_REPLY
  1512.     POP    AX            ; discard return address
  1513.     JMP    MASTER
  1514. TURN    ENDP
  1515.  
  1516. ; Wait for a given string to be received or for a timeout
  1517.  
  1518. WAITST    PROC    NEAR    ; BX -> string to wait for (null terminated), timeout set
  1519.     MOV    BP,BX            ; BP -> target string
  1520.  
  1521. .WST1:    MOV    SI,BP            ; SI -> string
  1522.  
  1523. .WST2:    CALL    RECEIVE_ASCII
  1524.  
  1525.     MOV    CL,AL            ; CL = character
  1526.     TEST    BYTE PTR MODEM_ECHO,0FFH; don't echo if echo is OFF
  1527.     JZ    .WST3
  1528.  
  1529.     MOV    AH,89H            ; output a single character (in CL)
  1530.     MOV    AL,COM_PORT
  1531.     INT    CINT
  1532.  
  1533. .WST3:    LODSB
  1534.     CMP    CL,AL            ; is it character we want?
  1535.     JNE    .WST1            ; no
  1536.     TEST    BYTE PTR [SI],0FFH
  1537.     JNZ    .WST2            ; more to go...
  1538.     RET
  1539. WAITST    ENDP
  1540.  
  1541. ; Write a line to the current file
  1542.  
  1543. WRITELINE    PROC    NEAR    ; SI -> null-terminated string
  1544.     MOV    DI,SI
  1545.     XOR    AX,AX
  1546.     MOV    CX,0FFFFH
  1547.     REPNE    SCASB
  1548.     DEC    DI
  1549. ;    fall through to WRITER
  1550. WRITELINE    ENDP
  1551. ; Write received-message contents to current file
  1552. WRITER    PROC    NEAR    ; SI -> line buffer, DI -> last character stored + 1
  1553.     MOV    AH,40H            ; DOS write function
  1554.     MOV    DX,SI            ; DX -> line
  1555.     MOV    CX,DI            ; calculate CX = length
  1556.     SUB    CX,DX
  1557.     JZ    .WR1
  1558.     MOV    BX,WRITE_HANDLE
  1559.     TEST    BX,BX
  1560.     JZ    .WR1            ; zero handle => no file open
  1561.     INT    21H
  1562.  
  1563. .WR1:    RET
  1564. WRITER    ENDP
  1565.  
  1566. ; User parameters for COM port settings
  1567.  
  1568. COM_PORT    DB    0    ; COM-port number (1, 2, 3, or 4)
  1569. COM_SPEED    DW    0    ; speed in bps
  1570. COM_OPTIONS    DW    3H    ; use both input and output flow control
  1571.  
  1572. ; Filenames and messages
  1573.  
  1574. CSFN        DB    '1STCLASS.CSF',0
  1575. INFN        DB    'MAIL.IN',0
  1576. OUTFN        DB    '*.OUT',0
  1577. RENAME        DB    'MLD',0
  1578. LOGFN        DB    '1STCLASS.LOG',0
  1579. ATTACH_FN    DB    'ATTACHED.'
  1580. ATTACH_EXT    DB    'XYZ',0
  1581.  
  1582. ; Messages
  1583.  
  1584. INITMSG    DB    '1stClass 1.0 (c) 1989 Ziff Communications Co.',CR,LF
  1585.     DB    'PC Magazine * Pete Maclean',CR,LF,'$'
  1586.  
  1587. NOTE    DB    'NOTICE: This software was developed, in part, using, with' 
  1588.     DB    'permission, proprietary, trade secret information of MCI'
  1589.     DB    'Telecommunications Corporation.  The user agrees to use this'
  1590.     DB    'program only for the purpose of communicating with MCI Mail.$'
  1591.  
  1592. LOADMSG    DB    'Please load Couriers.$'
  1593. TOMSG    DB    BELL,CR,LF,'***TIMED OUT***$'
  1594. INMSG    DB    BELL,CR,LF,'***Interrupted by user***$'
  1595. PFMSG    DB    BELL,CR,LF,'***Protocol failure: unrecognized message received***$'
  1596. BADMSG    DB    BELL,CR,LF,'***Message received with bad checksum***$'
  1597. INSTMSG    DB    BELL,'Ignoring unrecognized !COMMAND:',CR,LF,'$'
  1598. OPENMSG    DB    BELL,CR,LF,'Cannot open file: $'
  1599. CSERM    DB    BELL,CR,LF,'Unknown script command$'
  1600. CRLF    DB    CR,LF,'$'
  1601. UPMSG    DB    'Uploading $'
  1602. ATMSG    DB    'Attaching $'
  1603. DOWNAT    DB    'Downloading attachment$'
  1604. DOWNER    DB    'Downloading message$'
  1605.  
  1606. ; Odd strings
  1607.  
  1608. ATTACHMENT    DB    CR,LF,'ATTACHMENT:  ',0
  1609. EOL        DB    CR,LF,CR,LF            ; end of letter...
  1610. EOE        DB    CR,LF,0                ; end of envelope
  1611. TOFILE        DB    CR,LF,'STORED ON FILE:  ',0
  1612.  
  1613. ; Message keys
  1614.  
  1615. MESSAGE_KEYS    LABEL    BYTE
  1616.     DB    'COMMENT',0,'CREATE',0,'END',0,'ENV',0,'INIT',0,'REPLY',0
  1617.     DB    'RESET',0,'SEND',0,'TERM',0,'TEXT',0,'TURN',0,0
  1618.  
  1619. ; Text types
  1620.  
  1621. TEXT_KEYS    LABEL    BYTE
  1622. K_ASCII        DB    'ASCII',0
  1623. K_BINARY    DB    'BINARY',0,0
  1624.  
  1625. ; Escape instructions
  1626.  
  1627. INST_KEYS    LABEL    BYTE
  1628. I_ATTACH    DB    'ATTACH',0,0
  1629.  
  1630. ; Code switch for slave processing
  1631.  
  1632. SLAVE_SWITCH    LABEL    WORD
  1633.     DW    OFFSET COMMENTARY    ; COMMENT
  1634.     DW    OFFSET CREATE        ; CREATE
  1635.     DW    0            ; END
  1636.     DW    OFFSET ENVELOPE        ; ENV
  1637.     DW    0            ; INIT
  1638.     DW    0            ; REPLY
  1639.     DW    OFFSET COMMENTARY    ; RESET
  1640.     DW    OFFSET SEND        ; SEND
  1641.     DW    0            ; TERM
  1642.     DW    OFFSET TEXT        ; TEXT
  1643.     DW    OFFSET TURN        ; TURN
  1644.  
  1645. ; Mail Link reply text
  1646.  
  1647. CKERROR        DB    'Checksum error',0
  1648.  
  1649. ENDER        DB    '/END '
  1650. L_ENDER        =    $ - ENDER
  1651.  
  1652. REPLY_HEAD    DB    '/REPLY '
  1653. L_REPLY_HEAD    =    $ - REPLY_HEAD
  1654.  
  1655. REPLY_TAIL    DB    '/END REPLY'
  1656. L_REPLY_TAIL    =    $ - REPLY_TAIL
  1657.  
  1658. ; Numeric constants
  1659.  
  1660. HUNDRED        DB    100
  1661. TEN        DB    10
  1662.  
  1663. ; Miscellaneous variables
  1664.  
  1665. BUFFER_COUNTER    DW    0    ; for counting characters in READLINE
  1666. CHECKSUM    DW    0    ; for checksums
  1667. COMMAND_TYPE    DB    0    ; Mail Link command type, MT_XXXX
  1668. DOTTER        DB    0    ; ? show download progress with dots
  1669. MODEM_ECHO    DB    0    ; ? echo received characters (like a modem does)
  1670.                 ; (applies only in script handler)
  1671. ECHO_LINE    DB    0    ; ? echo received lines to screen
  1672. ERROR_AT    DW    0    ; error address (for debugging)
  1673. FILENAME    DB    65 DUP (?)
  1674. LOGFH        DW    0    ; file handle for log
  1675. MAILIN        DW    0    ; handle for incoming mail file
  1676. MASTERED    DB    0    ; ? played Mail Link master role
  1677. MESSAGE_LENGTH    DW    0    ; length of a received message
  1678. SCRIPT_HANDLE    DW    0    ; handle for script file
  1679. SEND_HANDLE    DW    0    ; handle for a send file
  1680. READ_HANDLE    DW    0    ; handle for reading mail and attachment files
  1681. READ_POT    DB    0    ; copy of last character received
  1682. SLAVED        DB    0    ; ? played Mail Link slave role
  1683. STARSUM        DW    0    ; received checksum saved at '*'
  1684. TIMEOUT        DW    0    ; timeout counter
  1685. TIME        DW    0    ; last time read from system clock
  1686. WRITE_HANDLE    DW    0    ; handle for file being written
  1687.  
  1688. ; Buffers
  1689.  
  1690. FIFO        LABEL    BYTE
  1691. LINE_BUFFER    =    FIFO + FIFOSIZE        ; text-line buffer
  1692. RM_BUFFER    =    LINE_BUFFER + SBS    ; receive message buffer
  1693. XM_BUFFER    =    RM_BUFFER + LBS        ; transmit message buffer
  1694. REPLY_BUFFER    =    XM_BUFFER + LBS        ; Mail Link reply buffer (LBS bytes)
  1695.  
  1696. PSEG    ENDS
  1697.     END    ENTRY
  1698.