Category : Files from Magazines
Archive   : VOL11N02.ZIP
Filename : PCREMOT2.ASM

 
Output of file : PCREMOT2.ASM contained in archive : VOL11N02.ZIP
TITLE PCREMOT2 - Unattended Computer Access - 1991 by Terry Lahman
PAGE 78,132
;======================================================================
;
; PCREMOT2 - An unattended computer access utility. Allows access to an
; unattended computer from a manned computer. The unattended computer
; executes the TSR portion of the program and waits for the manned
; computer to call. For use with text only programs. Like using the
; phone lines and modems as a long extension cord for your keyboard
; and monitor.
;
; Usage: PCREMOT2 [ /M ] [ /# ] [ /B# ] [ /D ] [ /S ] [ /N ] [ /U ] [ /? ]
;
; [ /M ] = "Manned" mode (used on remote system)
; [ /# ] = 1 - 4, Communication port used
; [ /B# ] = Baud rate 1=1200, 2=2400, 4=4800, 9=9600
; 19=19200, 3=38400
; [ /D ] = "Desnow" flag (used on CGA monitors)
; [ /S ] = Smiley face displayed (used on host system)
; [ /N ] = Null modem cable connecting 2 systems
; [ /U ] = Uninstall (used on host computer to remove from memory) "
; [ /? ] = Display help screen
;
;======================================================================
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING

BS EQU 8
CR EQU 13
LF EQU 10
SPACE EQU 32
ESC_KEY EQU 1BH

;----------------------------------------------------------------------
; Start of code
;----------------------------------------------------------------------
ORG 100H ;Starting offset for .com
START: JMP INITIALIZE ;Jump over resident code
DW OFFSET CONFIG - $ ;Offset to config parameters

;----------------------------------------------------------------------
; Data storage
;----------------------------------------------------------------------

COPYRIGHT DB "PCREMOT2 Version 1.0 (c) 1989, 1991 Ziff Communications Co."
DB CR,LF," PC Magazine ",254," by Terry Lahman & Kevin Sims"
DB CR,LF,"$",26
CONNECT_FLAG DB 0 ;0=not connected
;1=connected to manned system
ACT_FLAG DB 0 ;0=unattended prog not active
;1=unattended program is active
; do not run again
OLDINT_8 DW 0,0 ;Old timer vector
OLDINT_21 DW 0,0 ;Old DOS vector
OLDINT_COMM DW 0,0 ;Old communications vector
INSTALLED_SEG DW 0 ;Segment of resident program
SET_VEC DW 2500H ;DOS set vector call
SLEEP_FLAG DB 0 ;0=pcremote is awake
;1=pcremote is asleep
;needs to sleep during init.
CURRENT_SPEED DB 0 ;Currently selected speed
SPEED_COUNT DB 0 ;Change speed once a second
COMM_PORT DW 0 ;Comm port address
COMM_INT_STA DB 0 ;Comm port interrupt status
;0=not transmitting data
;1=transmitting data
VIDEO_COPY DW 0 ;Address of video ram copy
VIDEO_SEGMENT DW 0 ;Segment register for vid ram
SHIFT_STATUS DB 0 ;Current status of shift byte
STACK_TOP DW INITIALIZE+256D ;Top of stack for unattended
OLD_SS DW 0 ;Old stack segment
OLD_SP DW 0 ;Old stack pointer
TEMP_REG DW 0 ;Temp storage for register
CURSOR_POSITION DW 0 ;Old cursor position
VID_RAM_OFFSET DW 0 ;Current compare offset
ASCII_FLAG DB 0 ;0=no data is pending
;1=received a FE
;2=received ASCII char
;4=received scan code
SHIFT_FLAG DB 0 ;0=no data is pending
;1=received a FD
KEY_ONE DB 0 ;ASCII char received
KEY_TWO DB 0 ;Scan code received
BAUD_RATE DB 0,60H ;1200 BAUD divisor MSB LSB
DB 0,30H ;2400 BAUD divisor MSB LSB
DB 0,18H ;4800 BAUD divisor MSB LSB
DB 0,0CH ;9600 BAUD divisor MSB LSB
DB 0,06H ;19200 BAUD divisor MSB LSB
DB 0,03H ;38400 BAUD divisor MSB LSB
IN_BUFF_SIZE EQU 256D ;Size for input buffer
OUT_BUFF_SIZE EQU 256D ;Size for output buffer
IN_BUFF_HEAD DW ? ;Pointer to input buffer head
IN_BUFF_TAIL DW ? ;Pointer to input buffer tail
IN_BUFF_BEGIN DW ? ;Pointer to input buffer begin
IN_BUFF_END DW ? ;Pointer to input buffer end
OUT_BUFF_HEAD DW ? ;Pointer to output buffer head
OUT_BUFF_TAIL DW ? ;Pointer to output buffer tail
OUT_BUFF_BEGIN DW ? ;Pointer to output buffer begin
OUT_BUFF_END DW ? ;Pointer to output buffer end
BLOCK_SIZE EQU 16D ;Block transfer size in words
BLOCK_SIZEX2 EQU BLOCK_SIZE*2 ;Block transfer size in bytes
BLOCK_COUNT DB ? ;Block number being processed
BLOCK_POINTER DW 0 ;Points to current video block
TEMP_VIDEO_PTR DW ? ;Pointer to temp video storage
CR_COUNT DB ? ;Number of CRs for speed sync
MODEM_ATTENTION DB CR,"AT",CR,0
MODEM_SETUP1 DB "ATE0 S12=40 Q0 V0 X1 S0=0",CR,0 ;Manned
MODEM_SETUP2 DB "ATE0 S12=40 Q1S0=2",CR,0 ;Unattended
MODEM_SETUP3 DB "AT&C1",CR,0 ;Enable DCD on 2400 baud modem
MODEM_SETUP4 DB "ATW0X4&Q5&K3L0",CR,0 ;for 9600 baud modems
MODEM_HANGUP DB "ATH0",CR,0
MODEM_ESCAPE DB "+++",0
PASS_MESSAGE DB "Enter password:",0 ;Enter password message
PASSWORD_BUFFER DB 20 DUP(?)
BAD_PASSWORD DB ? ;0=No, don't send exit code
;1=Yes, 3 bad, so send code
CONFIG: ;The following parameters must remain in order to support
; the setup program.
MANNED_FLAG DB 0 ;0=Operate in unattended mode
;1=Operate in manned mode
COMM_FLAG DB 0 ;0=Use comm1
;1=Use comm2
;2=Use comm3
;3=Use comm4
COMM_PORT3 DW 03E8H ;Port address for COMM 3
COMM_PORT4 DW 02E8H ;Port address for COMM 4
COMM3_INT DB 4 ;Interrupt for com1 and 3
COMM4_INT DB 3 ;Interrupt for com2 and 4
SPEED_FLAG DB 0 ;0=Use 1200 baud
;1=Use 2400 baud
;2=Use 4800 baud
;3=Use 9600 baud
;4=Use 19200 baud
;5=Use 38400 baud
DESNOW_FLAG DB 0 ;0=Do not use desnow code
;1=Use desnow code
NULL_MODEM DB 0 ;0=Using a modem
;1=Using a null modem cable
SMILE_FLAG DB 0 ;0=Disable corner smile face
;1=Enable corner smile face
CLEAR_CODE DB 0,2EH ; alt-c, clear code
EXIT_CODE DB 0,45D ; alt-x, code to exit program
SHELL_CODE DB 0,1FH ;alt-s, shell to DOS code
TRANSFER_CODE DB 0,20D ;alt-t, file transfer code
PASSWORD_SIZE DW EXTRA_PW_SPACE-PASSWORD
PASSWORD DB "PC MAGAZINE"
EXTRA_PW_SPACE DB 20-(EXTRA_PW_SPACE-PASSWORD) DUP(?)
MODEM_SETUP5 DB 30 DUP(0); ;Extra modem setup string
TONE_DIAL DB "ATDT",0 ;Tone dial command

;======================================================================
; Interrupt handlers. Interrupt 8 is used in unattended mode only.
; The communications interrupt is used in both modes.
;======================================================================
;----------------------------------------------------------------------
; Interrupt 8 handling routine. If program is already active do not run.
; Run connect unattended if not connected, otherwise run unattended.
;----------------------------------------------------------------------
INT8 PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH DS ;Save data segment
PUSH CS ;Set data segment to code seg
POP DS
PUSHF ;Call old int 8
CALL DWORD PTR OLDINT_8
I8_10:
CLI ;Disable interrupts
CMP BYTE PTR SLEEP_FLAG,1 ;If another program has control
JZ I8_EXIT ;then exit
CMP BYTE PTR ACT_FLAG,0 ;Check for program active
JNZ I8_EXIT ;Exit if progam is active
INC BYTE PTR ACT_FLAG ;Set program active flag
STI ;Enable interrupts
CALL SET_STACK ;Create stack & save registers
CMP BYTE PTR CONNECT_FLAG,0 ;Connected to manned?
JNZ I8_20 ;Yes, then run unattended
CALL CONNECT_UNATTENDED ;No, check for connection
JMP I8_30
I8_20:
CALL UNATTENDED ;Run unattended
I8_30:
CALL RESET_STACK ;Restore registers & stack
DEC BYTE PTR ACT_FLAG ;Clear program active flag
I8_EXIT:
STI ;Enable interrupts
POP DS ;Restore data segment
IRET
INT8 ENDP

;----------------------------------------------------------------------
; Interrupt 21 handling routine. If another program, zcopy, changes
; the comm interrupt, go to sleep by setting the sleep flag. When the
; comm interrupt vector returns, WAKE UP!
;----------------------------------------------------------------------
INT21 PROC FAR
PUSHF ;save registers
PUSH AX
PUSH BX
CMP AX,WORD PTR CS:SET_VEC ;setting comm int. vector?
JZ I21_20 ;yes
I21_10:
POP BX ;no, restore registers
POP AX
POPF
JMP DWORD PTR CS:OLDINT_21 ;execute original DOS int
I21_20:
MOV AX,CS ;is new code segment
MOV BX,DS ; the same as pcremote
CMP AX,BX
JNZ I21_30 ;no, then go to sleep
CMP DX,OFFSET CS:INT_COMM ;is it my comm interrupt vec?
JZ I21_40 ;yes, wake up
I21_30:
MOV BYTE PTR CS:SLEEP_FLAG,1 ;I'm getting sleeeeepy
JMP I21_10
I21_40:
POP BX ;restore registers
POP AX
STI ;enable interrupts
CALL DWORD PTR CS:OLDINT_21 ;execute original DOS int vec
CLI ;disable interrupts
CALL CLEAR_VIDEO ;clear the screen
MOV BYTE PTR CS:SLEEP_FLAG,00 ;WAKE UP!
PUSH DX
PUSH AX
PUSH DS ;Save data segment
PUSH CS ;Set data segment to code seg.
POP DS
CALL INIT_SERIAL ;Re-initialize serial port
CALL CLEAR_OUTBUFF ;Clear buffers because ZCOPY
CALL CLEAR_INBUFF ; sent hex 02's to sync systems
POP DS ;Restore the data segment
POP AX ;Restore registers
POP DX
STI ;Enable interrupts
RET 2 ;Return to calling program
INT21 ENDP

;---------------------------------------------------------------------
; Clear the video copy to force a new screen to be tranferred to
; remote system
;---------------------------------------------------------------------
CLEAR_VIDEO:
PUSH CX ;Store necessary registers
PUSH DI
PUSH ES
PUSHF
CLD ;Direction = forward
PUSH CS ;Push CS so ES can now
POP ES ; point at it .. ES->CS
MOV DI,WORD PTR VIDEO_COPY ;ES:DI->Video copy
MOV AX,720H ;AL=SPACE,AH=Normal attribute
MOV CX,2000D ;Clear entire video copy
REP STOSW ;Store char and attribute
MOV CURSOR_POSITION,0FFFFH ;Reset cursor to be updated
POPF ;Restore registers
POP ES
POP DI
POP CX
RET

;----------------------------------------------------------------------
; Interrupt handling routine for communications interrupt. Provides
; interrupt driven I/O. Transmit or receive a character.
;----------------------------------------------------------------------
INT_COMM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH DS ;Save data segment
PUSH DX ;Save registers
PUSH BX
PUSH AX
PUSH CS ;Set data segment to code seg.
POP DS
MOV DX,COMM_PORT ;Get port base address
INC DX ;Point to int. id reg.
INC DX
IN AL,DX ;Get the interrupt id byte
IC_10:
CMP AL,2 ;Transmit empty int?
JZ IC_40 ;Yes transmit a byte
;
; received data, get it and store in buffer
;
DEC DX ;Port base address
DEC DX
IN AL,DX ;Get data from receive register
MOV BX,IN_BUFF_TAIL ;Get the buffer tail pointer
MOV [BX],AL ;Store the data in buffer
INC BX ;Point to next data storage
CMP IN_BUFF_END,BX ;Beyond end of buffer area?
JNE IC_20 ;No, then don't reset
MOV BX,IN_BUFF_BEGIN ;Yes, reset to buffer begin
IC_20:
CMP BX,IN_BUFF_HEAD ;Test for buffer full
JE IC_30 ;If so,don't change ptr ,sorry
MOV IN_BUFF_TAIL,BX ;Save new tail pointer
IC_30:
JMP IC_70
;
; transmit buffer empty, send a byte
;
IC_40:
DEC DX ;Port base address
DEC DX
MOV BX,OUT_BUFF_HEAD ;Get the buffer head pointer
CMP BX,OUT_BUFF_TAIL ;Test for data in buffer
JE IC_60 ;If the same, no data so exit
MOV AL,[BX] ;Get the data
INC BX ;Point to next data in buffer
CMP OUT_BUFF_END,BX ;Beyond end of buffer area?
JNE IC_50 ;No, then don't reset
MOV BX,OUT_BUFF_BEGIN ;Yes, reset to buffer begin
IC_50:
MOV OUT_BUFF_HEAD,BX ;Save new head pointer
OUT DX,AL ;Send the data out the port
JMP IC_70 ;Check for request pending
IC_60:
MOV BYTE PTR COMM_INT_STA,0 ;Reset transmitting data flag
IC_70:
INC DX ;Point to int. id reg.
INC DX
IN AL,DX ;Get the interrupt id byte
TEST AL,1 ;Request pending?
JZ IC_10 ;Yes, then process
IC_EXIT:
MOV AL,20H ;Reset 8259
OUT 20H,AL
STI ;Enable interrupts
POP AX ;Restore registers
POP BX
POP DX
POP DS ;Restore data segment
IRET
INT_COMM ENDP

;----------------------------------------------------------------------
; Create stack area and save all registers.
;----------------------------------------------------------------------
SET_STACK PROC NEAR
ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
MOV TEMP_REG,BX ;Save BX
POP BX ;Save the return address
PUSH AX ;Save AX
;
; make my own stack
;
CLI ;Disable interrupts
MOV AX,SS ;Put old stack segment in AX
MOV OLD_SS,AX ;And save it
MOV AX,SP ;Put old stack pointer in AX
MOV OLD_SP,AX ;And save it
MOV AX,CS ;Get current segment
MOV SS,AX ;And put into stack segment
MOV AX,STACK_TOP ;Get top of stack address
MOV SP,AX ;And put into stack pointer
STI ;Enable interrupts
;
; save all the registers on the stack
;
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
MOV AX,CS ;Get code segment
MOV DS,AX ;Set data segment to code seg
MOV ES,AX ;Set extra seg to code seg
PUSH BX ;Restore return address
RET
SET_STACK ENDP

;----------------------------------------------------------------------
; Restore all registers and reset stack
;----------------------------------------------------------------------
RESET_STACK PROC NEAR
ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
POP BX ;Save return address
;
; restore the registers
;
POP BP ;Restore the registers
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
;
; restore the original stack
;
CLI ;Disable interrupts
MOV AX,OLD_SP ;Get old stack pointer
MOV SP,AX ;And restore it
MOV AX,OLD_SS ;Get old stack segment
MOV SS,AX ;And restore it
STI ;Enable interrupts
POP AX ;Restore AX
PUSH BX ;Put return add back on stack
MOV BX,TEMP_REG ;Restore BX
RET
RESET_STACK ENDP

;======================================================================
; The unattended routine will execute the connect routine to establish
; a connection with the manned system. Once connected it will execute
; the unattended routine to process incoming data and send any changed
; video data to the manned system.
;======================================================================

;----------------------------------------------------------------------
; CONNECT_UNATTENDED - Check for ring codes, answer the phone and check
; the password. If correct, set connected flag.
;----------------------------------------------------------------------
CONNECT_UNATTENDED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
MOV BP,SP ;Save stack pointer for exit
XOR AH,AH ;Use input buffer
MOV CX,10D ;Number of char to check for CR
MOV BYTE PTR CR_COUNT,5 ;Number of matches required
CU_10:
CALL GET_BUFF_DATA ;Try to get data from buffer
JC CU_20 ;If data, check for CR code
JMP CU_40 ;else exit, try another speed
CU_20:
CMP AL,CR ;Check for CR code
JNZ CU_30 ;No so skip over
MOV BYTE PTR SPEED_COUNT,19 ;it's a CR, so speed is correct
DEC BYTE PTR CR_COUNT ;Found a match dec count
JZ CU_70 ;5 out of ten then null connect
CU_30:
LOOP CU_10 ;Keep trying
CU_40:
DEC BYTE PTR SPEED_COUNT
JZ CU_50
JMP CU_EXIT
CU_50:
DEC BYTE PTR CURRENT_SPEED ;Try a slower speed
CMP BYTE PTR CURRENT_SPEED,0FFH ;Out of speeds?
JNZ CU_60 ;No
MOV AL,BYTE PTR SPEED_FLAG ;Ran out of speeds, reset
MOV BYTE PTR CURRENT_SPEED,AL ; to starting speed
CU_60:
MOV BYTE PTR SPEED_COUNT,19 ;reset tick counter
MOV AL,BYTE PTR CURRENT_SPEED ;set the slower speed
CALL SET_BAUD_RATE
CALL CLEAR_INBUFF ;Empty the input buffer
JMP CU_EXIT ;No 5 out of 10, exit
CU_70:
MOV BYTE PTR CONNECT_FLAG,1 ;Set connect flag
MOV CX,3 ;3 tries to enter password
CU_80:
MOV SI,OFFSET PASS_MESSAGE ;Point to enter password mess.
CALL LOAD_ZSTRING ;Load it into output buffer
CALL GET_PASSWORD ;Get the password
CALL CHECK_PASSWORD ;Check the password sent
JZ CU_90 ;Jump if correct
LOOP CU_80 ;Keep trying
MOV AL,1 ;Use set up string 2
MOV BYTE PTR BAD_PASSWORD,1D ;Send exit code
CALL RESET_MODEM ;Hangup and reset modem
JMP CU_EXIT ;Done, so exit
CU_90:
MOV AH,1 ;Use output buffer
XOR AL,AL ;Sync byte to send
MOV CX,5 ;Send 5 of them
CU_100:
CALL PUT_BUFF_DATA ;Send them
LOOP CU_100
;
; Notify remote computer the value of host's COMM_FLAG switch. This allows
; the remote computer to properly send the ZCOPY command line to the host.
;
MOV AH,1 ;Use output buffer
MOV AL,BYTE PTR COMM_FLAG ;Send host's comm port
INC AL ;Bump AL so sent byte will be
; in range from 1 thru 4.
MOV CX,10D ;Send 10 of them
CU_110:
CALL PUT_BUFF_DATA ;Send Host Comm Port
LOOP CU_110 ;Sent 10 ?
XOR AL,AL ;Sync byte to send
MOV CX,5D ;Send 5 of them
CU_120:
CALL PUT_BUFF_DATA ;Send sync byte
LOOP CU_120 ;Sent 5 ?
CU_EXIT:
RET
CONNECT_UNATTENDED ENDP

;----------------------------------------------------------------------
; Get the password from the manned system
; Input - Nothing
; Output - Password buffer contains password from manned system
; Changes - DI, AX
;----------------------------------------------------------------------
GET_PASSWORD PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
PUSH CX ;Save register
MOV DI,OFFSET PASSWORD_BUFFER ;Point to password buffer
MOV CX,20D ;Zero buffer to clear previous
XOR AL,AL ; password
CLD
REP STOSB
MOV DI,OFFSET PASSWORD_BUFFER ;Pointer to buffer
MOV CX,20D ;Maximum password size
GP_10:
CALL CHECK_CARRIER ;Check for carrier loss
JNZ GP_20
JMP IP_90 ;Carrier loss, reset and exit
GP_20:
CALL INPUT_PROCESSING ;Get data & put in keybd buffer
CALL GET_KEYSTROKE ;Check for keystroke
JZ GP_10 ;None, so wait
CMP AL,60H ;Check for lower case
JL GP_30 ;No then leave it alone
AND AL,5FH ;Convert to upper case
GP_30:
CMP AL,CR ;If it is a CR then exit
JZ GP_EXIT
CMP AL,BS ;Is it a back space?
JNZ GP_40 ;No, so save it
INC CX ;Resetcounter for BS
CMP DI,OFFSET PASSWORD_BUFFER ;Already at start of buffer?
JZ GP_60 ;Yes, then don't backspace
DEC DI ;Backspace buffer pointer
MOV BYTE PTR [DI],0 ; and null the data
JMP GP_50
GP_40:
CLD ;Forward
STOSB ;Save the character
MOV AL,'*' ;Echo character
GP_50:
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA ; and store the character
GP_60:
LOOP GP_10 ;Receive up to CX characters
GP_EXIT:
POP CX ;Restore register
RET
GET_PASSWORD ENDP

;----------------------------------------------------------------------
; Check the password in the buffer with correct password
; Input - Nothing
; Output - Zero set - correct password
; Zero reset - Wrong password
; Changes - SI, DI
;----------------------------------------------------------------------
CHECK_PASSWORD PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
PUSH CX ;Save register
MOV SI,OFFSET PASSWORD_BUFFER ;Pointer to buffer
MOV DI,OFFSET PASSWORD ;Pointer to password
MOV CX,PASSWORD_SIZE ;Number of compares to make
CLD ;Compare forward
REPZ CMPSB ;Repeat while passwords match
POP CX ;Restore register
RET
CHECK_PASSWORD ENDP

;----------------------------------------------------------------------
; UNATTENDED - Process incoming data and check for changes in video
; data, format and send to manned system. Maintains 18 or less characters
; in output buffer, ensures maximum throughput.
;----------------------------------------------------------------------
UNATTENDED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
MOV BP,SP ;Save stack pointer for exit
CALL INPUT_PROCESSING ;Process any buffered data
;
; Check for a change in the cursor position
;
PUSH ES
MOV AX,40H ;Set ES to BIOS data segment
MOV ES,AX
MOV BX,ES:[50H] ;Get current cursor position
CMP BX,CURSOR_POSITION ;Compare with copy
JZ UA_10 ;No change, skip
MOV CURSOR_POSITION,BX ;Save new cursor position
MOV AL,0D2H ;Sync bits, set position
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA ;Send sync byte
MOV AL,BL ;Low byte
CALL PUT_BUFF_DATA
MOV AL,BH ;High byte
CALL PUT_BUFF_DATA
UA_10:
POP ES ;Restore ES
CMP BYTE PTR SMILE_FLAG,0 ;Smile face enabled ?
JE UA_20 ;No, skip smile face coding
;
; Redisplay the smile face in upper right corner
;
PUSH ES ;Store ES
MOV AX,VIDEO_SEGMENT ;AX = Video Segment
MOV ES,AX ;ES = Video Segment
MOV DI,158D ;DI-> Upper right corner
MOV AL,01H ;Smiley Face code
CALL PUT_VIDEO_DATA ;Write Video RAM
POP ES ;Restore ES
UA_20:
MOV VID_RAM_OFFSET,0 ;Start at address 0
MOV BYTE PTR BLOCK_COUNT,0 ;Reset block counter
;
; Check the output buffer and keep it full enough so it's busy till
; next timer tick.
;
UA_30:
MOV AX,7 ;1200 baud, at least 7 bytes
MOV CL,BYTE PTR CURRENT_SPEED ;the speed decides the size
SHL AX,CL ;larger for faster baud
MOV CX,AX ;at least this many bytes
CLI
MOV AX,OUT_BUFF_HEAD ;Get the head pointer
MOV BX,OUT_BUFF_TAIL ;Get the tail pointer
STI
UA_40:
CMP AX,BX ;Less than bytes in buffer
JZ UA_60 ;Yes, check the video data
INC AX ;Increment head pointer
CMP OUT_BUFF_END,AX ;Beyond end of buffer area?
JNE UA_50 ;No, then don't reset
MOV AX,OUT_BUFF_BEGIN ;Yes, reset to buffer begin
UA_50:
LOOP UA_40 ;Check all the bytes
JMP UA_EXIT ;More than allowed, exit
;
; compare the video copy with the video ram
;
UA_60:
PUSH DS ;Save data segment
MOV SI,VID_RAM_OFFSET ;Get the current offset
MOV DI,TEMP_VIDEO_PTR ;Dest. ES:DI temp video buffer
MOV AX,VIDEO_SEGMENT ;Get video Segment register
MOV DS,AX ;Source DS:SI video RAM
;
; transfer block_size words of data from video RAM to the temp buffer
;
MOV CX,BLOCK_SIZE ;Get count of words to transfer
CLD
CMP BYTE PTR CS:DESNOW_FLAG,0 ;Check desnow flag
JZ UA_90
SAL CX,1 ;Convert words to bytes
MOV DX,3DAH ;CGA status port
UA_70:
IN AL,DX ;Get status byte
TEST AL,1 ;Test display enable
JNZ UA_70 ;If in Hor. sync then wait
CLI ;Disable interrupts
UA_80:
IN AL,DX ;Get status byte
TEST AL,1 ;Test display enable
JZ UA_80 ;Wait for Hor. sync
MOVSB ;Transfer one byte
STI ;Enable interrupts
LOOP UA_70 ;Transfer block size words
JMP UA_100
UA_90:
REP MOVSW
UA_100:
POP DS ;Restore data segment
;
; compare the block from video RAM with the video copy
;
MOV CX,BLOCK_SIZEX2 ;Number of words to compare
MOV SI,TEMP_VIDEO_PTR ;Point to block of video data
MOV DI,VIDEO_COPY ;Point to video copy
ADD DI,VID_RAM_OFFSET ;Adjust for current block
REPE CMPSB ;Compare while equal
JNE UA_110 ;No match, format & send block
MOV AX,VID_RAM_OFFSET ;Get current block pointer
ADD AX,BLOCK_SIZEX2 ;Point to next block
MOV VID_RAM_OFFSET,AX ;And save the pointer
INC BYTE PTR BLOCK_COUNT ;Increment current block count
CMP AX,4000D ;Check for end of video RAM
JNZ UA_60 ;No, keep checking the RAM
JMP UA_EXIT ;then exit
;
; data doesn't match, format and send to manned system
;
UA_110:
INC CX ;Adjust count
AND CX,1 ;LSB indicates char or attr.
CALL TRANSFER_BLOCK ;Prepare to send block of data
JMP UA_30 ;Again, till buffer is full
UA_EXIT:
RET
UNATTENDED ENDP

;----------------------------------------------------------------------
; Get data from the unattended input buffer and process
; Input - Nothing
; Output - Keyboard buffer or shift status is updated
; Changes - Nothing
;----------------------------------------------------------------------
INPUT_PROCESSING PROC NEAR
PUSH AX ;Save register
PUSH DX
PUSH ES
MOV AX,40H ;Set ES to BIOS data area
MOV ES,AX
IP_10:
CMP BYTE PTR CONNECT_FLAG,0 ;Are we connected
JZ IP_20 ;No, don't check carrier
CALL CHECK_CARRIER ;Yes check for carrier loss

JZ IP_90 ;Carrier loss, reset and exit
;
; make sure there is room in the keyboard buffer for a keystroke
;
IP_20:
CLI ;Don't allow interrupts
MOV AX,ES:[1CH] ;Get the buffer tail pointer
INC AX ;Point to next data storage
INC AX
CMP AX,3EH ;Beyond end of buffer area?
JNE IP_30 ;No, then don't reset
MOV AX,1EH ;Yes, reset to buffer begin
IP_30:
CMP AX,ES:[1AH] ;Test for buffer full
JE IP_40 ;If the same, don't process key
STI
JMP IP_50 ;Process incoming keystrokes
IP_40:
STI ;Enable interrupts
JMP IP_EXIT ;Exit the routine
IP_50:
MOV AH,0 ;Use input buffer
CALL GET_BUFF_DATA ;Get a byte of data
JC IP_60 ;If data then process
JMP IP_EXIT ;Otherwise exit
;
; Check to see if expecting ASCII data
;
IP_60:
CMP ASCII_FLAG,0 ;Check ASCII flag
JNZ IP_70 ;
JMP IP_120 ; 0=not expecting data here
IP_70:
CMP ASCII_FLAG,1 ;Check for received 1st byte
JNZ IP_80 ;Jump if second byte
MOV KEY_ONE,AL ;Save scan code, byte one
INC BYTE PTR ASCII_FLAG ;Indicate receiving one byte
JMP IP_10 ;Process next byte
IP_80:
MOV KEY_TWO,AL ;Save ASCII code, byte two
MOV AL,KEY_ONE ;Get ASCII code
MOV AH,KEY_TWO ;Get scan code
CMP AX,WORD PTR EXIT_CODE ;Check for exit code
JNZ IP_100 ;No,then continue processing
IP_90: ;Otherwise reset connect & exit
CMP BYTE PTR SMILE_FLAG,0 ;Smile flag set ?
JE IP_95 ;No, skip resetting smile face
PUSH AX ;Store AX
PUSH ES ;Store ES
PUSH DI ;Store DI
MOV AX,VIDEO_SEGMENT ;AX = VIDEO_SEGMENT
MOV ES,AX ;ES = VIDEO_SEGMENT
MOV DI,158D ;Bump DI 79 chars
MOV AL,SPACE ;Blank space
CALL PUT_VIDEO_DATA ;Write Video RAM
POP DI ;Restore DI
POP ES ;Restore ES
POP AX ;Restore AX
IP_95:
CALL CLEAR_INBUFF ;Clear input buffer
CALL CLEAR_OUTBUFF ;Clear output buffer
MOV BYTE PTR COMM_INT_STA,0 ;Reset transmitting data flag
CALL IU_10 ;reinit and reset modem
CLI ;Disable interrupts
MOV BYTE PTR CONNECT_FLAG,0 ;Reset the connect flag
MOV BYTE PTR ASCII_FLAG,0 ;Reset ASCII flag
MOV BYTE PTR SHIFT_FLAG,0 ;Reset shift flag
MOV BYTE PTR ES:[17H],0 ;Reset any shift status
MOV SP,BP ;Clean the stack
RET ;And exit unattended routine
IP_100:
CMP AX,WORD PTR CLEAR_CODE ;Clear code received
JNZ IP_110 ;No... skip clear code coding
CALL CLEAR_VIDEO ;Rewrite screen
MOV BYTE PTR ASCII_FLAG,0 ;Reset ASCII flag
JMP IP_10 ;Get next character
IP_110: ;
CALL PUT_KEY_DATA ;And stuff in keyboard buffer
MOV BYTE PTR ASCII_FLAG,0 ;Reset ASCII flag for next data
JMP IP_10 ;Process next byte
;
; Check to see if expecting shift data
;
IP_120:
CMP BYTE PTR SHIFT_FLAG,0 ;Check shift flag
JZ IP_130 ;0=not expecting data here
MOV ES:[17H],AL ;And save in shift status
MOV BYTE PTR SHIFT_FLAG,0 ;Reset shift flag for next data
JMP IP_10 ;Process next byte
;
; Check to see if it's a sync byte
;
IP_130:
CMP AL,0FEH ;Check for ASCII sync byte
JNZ IP_140 ;If not then check for shift
INC BYTE PTR ASCII_FLAG ;Indicate received FEh
JMP IP_10 ;Process next byte
IP_140:
CMP AL,0FDH ;Check for shift sync byte
JNZ IP_150 ;If not then throw away
INC BYTE PTR SHIFT_FLAG ;Indicate received FDh
IP_150:
JMP IP_10 ;Process till buffer empty
IP_EXIT:
POP ES ;Restore registers
POP DX
POP AX
RET
INPUT_PROCESSING ENDP

;----------------------------------------------------------------------
; Formats the data in temporary video buffer and puts it into the
; output buffer
; Input - CX=0 Transfer character data to output buffer
; CX=1 Transfer attribute data to output buffer
; Output - Nothing
; Changes - AX, BX, CX, SI, DI
;----------------------------------------------------------------------
TRANSFER_BLOCK PROC NEAR
PUSH CX ;Save the data type
;
; If the data is all the same only send it once, and set the repeat flag
;
MOV SI,TEMP_VIDEO_PTR ;Point
ADD SI,CX ;Adjust for char. or attr.
XOR BL,BL ;Initialize for non-repeating
MOV CX,BLOCK_SIZE ;Number of bytes to compare
MOV AH,[SI] ;Get the first byte in block
TB_10:
LODSB ;Get next byte from the block
INC SI ;Adjust for word
CMP AH,AL ;Verify all the same
LOOPZ TB_10 ;For the entire block
JNZ TB_20 ;Not the same, send block
MOV BL,2 ;set repeat flag
TB_20:
POP CX ;Restore data type
MOV AL,0FCH ;Start with base sync byte
OR AL,CL ;Include data type bit
OR AL,BL ;Include repeat bit
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA ;Put sync byte in output buff.
MOV AL,BLOCK_COUNT
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA ;Send the block number
MOV SI,TEMP_VIDEO_PTR ;Point to block of video data
ADD SI,CX ;Adjust for char. or attr.
MOV DI,VIDEO_COPY ;Point to video copy
ADD DI,VID_RAM_OFFSET ;Adjust for block offset
ADD DI,CX ; and character or attribute
MOV CX,BLOCK_SIZE ;Number of bytes to send
CLD ;Forward direction
TB_30:
LODSW ;Get the unmatched data
OR BL,BL ;Check the repeat flag
JNZ TB_40 ;Don't send repeat data
MOV AH,1 ;Use the output buffer
CALL PUT_BUFF_DATA ;Put the video data in out buff
TB_40:
STOSB ;Save video data in copy
INC DI ;Adjust for word offset
LOOP TB_30 ;Send block size bytes of data
OR BL,BL ;If repeat data then send one
JZ TB_50
MOV AH,1
CALL PUT_BUFF_DATA
TB_50:
RET
TRANSFER_BLOCK ENDP

;----------------------------------------------------------------------
; Put a byte of data into the unattended keyboard buffer.
; Input - AL contains ASCII to be put into buffer.
; AH contains scan code to be put into buffer.
; Output - Carry Set - Byte placed in buffer successfully
; Carry Reset - Buffer full, byte not stored in buffer
; Changes - Nothing
;----------------------------------------------------------------------
PUT_KEY_DATA PROC NEAR
PUSH BX ;Save registers
PUSH SI
PUSH DI
MOV SI,1AH ;Point to keyboard head pointer
CLI ;Don't allow interrupts
MOV BX,ES:[1CH] ;Get the buffer tail pointer
MOV DI,BX ;Save the tail pointer
INC BX ;Point to next data storage
INC BX
CMP BX,3EH ;Beyond end of buffer area?
JNE PK_10 ;No, then don't reset
MOV BX,1EH ;Yes, reset to buffer begin
PK_10:
CMP BX,ES:[1AH] ;Test for buffer full
JE PK_EXIT ;If the same, don't save it
; exit, carry is already reset
MOV ES:[DI],AX ;Store the data in buffer
MOV ES:[1CH],BX ;Save new tail pointer
STC ;Indicate data stored OK
PK_EXIT:
POP DI
POP SI
POP BX
STI ;Enable interrupts
RET
PUT_KEY_DATA ENDP

;======================================================================
; COMMON ROUTINES - These routines are common to both the unattended
; processing portion of the program and the manned processing portion.
;======================================================================

;----------------------------------------------------------------------
; Store the video data in AL in the memory location pointed to by DI
; INPUT - AL video data, ES:DI video RAM destination
; OUTPUT - Nothing
; Changes - DI is incremented
;----------------------------------------------------------------------
PUT_VIDEO_DATA PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
;
; Check desnow flag, if set wait for horizontal sync to put in video RAM
;
PUSH BX ; Save Registers
PUSH DX
CMP BYTE PTR DESNOW_FLAG,0 ; Check Desnow flag
JZ PV_30 ; No, skip over
CLD ; Forward
XCHG AX,BX ; Save video data
MOV DX,3DAH ; CGA status port
PV_10:
IN AL,DX ; Get status byte
TEST AL,1 ; Test display enable
JNZ PV_10 ; If in Hor. sync, wait
PV_20:
IN AL,DX ; Get status byte
TEST AL,1 ; Test display enable
JZ PV_20 ; Wait for Hor. sync
XCHG AX,BX ; Get video data
PV_30:
STOSB ; Put into video RAM
POP DX ; Restore registers
POP BX
RET
PUT_VIDEO_DATA ENDP

;----------------------------------------------------------------------
; Get a byte of data from a buffer. Byte pointed to by head pointer is
; is next data byte. If head=tail, no data in buffer.
; Input - AH - Buffer to use 0=Input buffer, 1=Output buffer.
; Output - Carry Set - Byte from buffer is in AL
; Carry Reset - No data in buffer
; Changes - AL
;----------------------------------------------------------------------
GET_BUFF_DATA PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH BX ;Save registers
PUSH SI
CMP AH,0 ;Check which buffer to use
JNZ GD_10 ;Jump for output buffer
MOV SI,OFFSET IN_BUFF_HEAD ;Point to input buffer
JMP GD_20 ;Skip over out buffer
GD_10:
MOV SI,OFFSET OUT_BUFF_HEAD ;Point to output buffer
GD_20:
CLI ;Don't allow interrupts
MOV BX,[SI] ;Get the buffer head pointer
CMP BX,2[SI] ;Test for data in buffer
JE GD_EXIT ;If the same, no data so
; exit, carry is already reset
MOV AL,[BX] ;Get the data
INC BX ;Point to data in buffer
CMP 6[SI],BX ;Beyond end of buffer area?
JNE GD_30 ;No, then don't reset
MOV BX,4[SI] ;Yes, reset to buffer begin
GD_30:
MOV [SI],BX ;Save new head pointer
STC ;Indicate data is in AL
GD_EXIT:
POP SI ;Restore registers
POP BX
STI ;Enable interrupts
RET
GET_BUFF_DATA ENDP

;----------------------------------------------------------------------
; Put a byte of data into a buffer. Byte is stored at location
; pointed to by tail pointer.
; Input - AL contains data to be put into buffer.
; AH - Buffer to use 0=Input buffer, 1=Output buffer
; Output - Carry Set - byte placed in buffer successfully
; Carry Reset - Buffer full, byte not stored in buffer
; Changes - Nothing
;----------------------------------------------------------------------
PUT_BUFF_DATA PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save registers
PUSH BX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH CS ;Set data segment to CS
POP DS
CMP AH,0 ;Check which buffer to use
JNZ PD_10 ;Jump for output buffer
MOV SI,OFFSET IN_BUFF_HEAD ;Point to input buffer
JMP PD_20 ;Skip over out buffer
PD_10:
MOV SI,OFFSET OUT_BUFF_HEAD ;Point to output buffer
PD_20:
CLI ;Don't allow interrupts
MOV BX,2[SI] ;Get the buffer tail pointer
MOV DI,BX ;Save the tail pointer
INC BX ;Point to next data storage
CMP 6[SI],BX ;Beyond end of buffer area?
JNE PD_30 ;No, then don't reset
MOV BX,4[SI] ;Yes, reset to buffer begin
PD_30:
CMP BX,[SI] ;Test for buffer full
JE PD_40 ;If so, exit carry is reset

MOV [DI],AL ;Store the data in buffer
MOV 2[SI],BX ;Save new tail pointer
STC ;Indicate data stored ok
PD_40:
PUSHF ;Save the flags
CMP BYTE PTR COMM_INT_STA,0 ;Transmit int. running?
JNZ PD_60 ;Yes, so exit
MOV AX,OUT_BUFF_HEAD ;Is data in output buffer
CMP AX,OUT_BUFF_TAIL
JZ PD_60 ;No, so exit
MOV BYTE PTR COMM_INT_STA,1 ;Set transmitting data flag
MOV DX,COMM_PORT ;Get port base address
ADD DX,5 ;Line status register
PD_50:
IN AL,DX ;Make sure holding reg. empty
TEST AL,00100000B ;Test the hold reg empty flag
JZ PD_50 ;Loop if not empty
SUB DX,5 ;Port base register
MOV AH,1 ;Use the output buffer
CALL GET_BUFF_DATA ;Get data from output buffer
OUT DX,AL ;Send the data out the port
PD_60:
STI ;Enable interrupts
POPF ;Restore flags
POP DS ;Restore registers
POP DI
POP SI
POP DX
POP BX
POP AX
RET
PUT_BUFF_DATA ENDP

;----------------------------------------------------------------------
; Clear the input buffer
; Input - Nothing
; Output - Nothing
; Changes - Nothing
;----------------------------------------------------------------------
CLEAR_INBUFF PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save register
CLI ;Disable interrupts
MOV AX,IN_BUFF_TAIL ;Get buffer tail pointer
MOV IN_BUFF_HEAD,AX ;Make head equal tail
STI ;Enable interrupts
POP AX
RET
CLEAR_INBUFF ENDP

;----------------------------------------------------------------------
; Clear the output buffer
; Input - Nothing
; Output - Nothing
; Changes - Nothing
;----------------------------------------------------------------------
CLEAR_OUTBUFF PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save register
CLI ;Disable interrupts
MOV AX,OUT_BUFF_TAIL ;Get buffer tail pointer
MOV OUT_BUFF_HEAD,AX ;Make head equal tail
STI ;Enable interrupts
POP AX
RET
CLEAR_OUTBUFF ENDP

;----------------------------------------------------------------------
; Reset the modem and send the setup string to initialize
; Input - AL - 0 use setup string 1, 1 use setup string 2
; Output - Nothing
; Changes - Nothing
;----------------------------------------------------------------------
RESET_MODEM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
CMP BYTE PTR NULL_MODEM,0 ;If null modem don't reset
JZ RM_10
MOV BYTE PTR CONNECT_FLAG,0 ;Reset connect flag
;
; After resetting connect flag, see if unattended and three bad passwords
; received, if so send an exit code to the remote computer so that the
; remote computer can exit to DOS.
;
CMP BYTE PTR MANNED_FLAG,0 ;If host, then send exit code
JNE RM_5 ; to remote system to exit
CMP BYTE PTR BAD_PASSWORD,0 ;Three bad passwords ?
JE RM_5 ;No, skip sending exit code
CALL CLEAR_OUTBUFF ;Clear out buff to insure room
MOV AX,WORD PTR EXIT_CODE ;Set up AL and AH to send
MOV AL,AH ; the exit code thru out buff.
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA ;Send char (exit_code)
RM_5: ;
JMP RM_EXIT ;and return to caller
RM_10:
PUSH SI ;Save registers
PUSH DX
PUSH AX
CMP BYTE PTR CONNECT_FLAG,0 ;Is modem connected?
JZ RM_20 ;No then send setup only
CALL CLEAR_OUTBUFF ;Empty the output buffer
MOV BYTE PTR CONNECT_FLAG,0 ;Reset connect flag
MOV AL,1 ;Wait a second for guard time
CALL DELAY
MOV SI,OFFSET MODEM_ESCAPE ;Send modem escape code
CALL LOAD_ZSTRING
MOV AL,2 ;Wait
CALL DELAY
MOV SI,OFFSET MODEM_HANGUP ;Send modem hangup code
CALL LOAD_ZSTRING
MOV AL,1 ;Wait for a second
CALL DELAY
RM_20:
MOV AL,SPEED_FLAG ;Get speed flag
MOV BYTE PTR CURRENT_SPEED,AL ;Save as next starting speed
CALL SET_BAUD_RATE
MOV BYTE PTR SPEED_COUNT,19
MOV SI,OFFSET MODEM_ATTENTION ;Point to modem attention
CALL LOAD_ZSTRING ;Put it into output buffer
MOV AL,1 ;Wait for a second
CALL DELAY
POP AX ;Get setup string to use
CMP AL,0 ;Test for string 1
JNZ RM_30 ;No, then use string 2
MOV SI,OFFSET MODEM_SETUP1 ;Point to modem setup string 1
JMP RM_40
RM_30:
MOV SI,OFFSET MODEM_SETUP2 ;Point to modem setup string 2
RM_40:
CALL LOAD_ZSTRING ;Load setup string to modem
MOV AL,1 ;Wait a second
CALL DELAY
MOV SI,OFFSET MODEM_SETUP3 ;Point to modem setup string 3
CALL LOAD_ZSTRING ;Load setup string to modem
CMP BYTE PTR SPEED_FLAG,2 ;If speed is 1200 or 2400
JL RM_50 ; skip setup string 4
MOV AL,1 ;Wait a second
CALL DELAY
MOV SI,OFFSET MODEM_SETUP4 ;Point to modem setup string 4
CALL LOAD_ZSTRING ;Load setup string to modem
RM_50:
MOV AL,1 ;Wait a second
CALL DELAY
MOV SI,OFFSET MODEM_SETUP5 ;Point to custom setup string 5
CALL LOAD_ZSTRING ;Load setup string to modem
CALL CLEAR_INBUFF ;Clear the input buffer
POP DX ;Restore registers
POP SI
RM_EXIT:
RET
RESET_MODEM ENDP

;----------------------------------------------------------------------
; Check carrier reads the carrier status signal and sets Z flag to
; indicate status
; Input - Nothing
; Output - Zero flag 0 - Carrier
; Zero flag 1 - No Carrier detected
; Changes - Nothing
;----------------------------------------------------------------------
CHECK_CARRIER PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save registers
CMP BYTE PTR NULL_MODEM,0 ;If null modem don't reset
JZ CC_10
CMP BYTE PTR CONNECT_FLAG,0 ;
JZ CC_EXIT ;not connected, send no carrier
MOV AH,1 ;reset zero flag
CMP AH,0
JMP CC_EXIT ;and return to caller
CC_10:
PUSH DX
MOV DX,COMM_PORT ;Get the comm base address
ADD DX,6 ;Modem status register
IN AL,DX ;Get the current status
TEST AL,10000000B ;Data carrier detect
POP DX ;Restore registers
CC_EXIT:
POP AX
RET
CHECK_CARRIER ENDP

;----------------------------------------------------------------------
; DELAY - delay approximate number of seconds in AL
; Input - AL
; Output - Nothing (just waits till AL is zero)
; Changes - Nothing
;----------------------------------------------------------------------
DELAY PROC NEAR
PUSH CX ;Save registers
PUSH DX
PUSH DI
PUSH AX
XOR AH,AH ;Read system time
INT 1AH
STI ;Enable interrupts Ver 1.1
MOV DI,DX ;Save low tick count
MOV SI,CX ;Save high tick count
POP AX ;Get number of seconds to delay
PUSH AX
XOR CX,CX ;Zero CX
MOV CL,AL ;Put seconds into loop counter
D_10:
ADD DI,19D ;Approximate counts in a second
ADC SI,0 ;Add carry to SI
LOOP D_10
D_20:
XOR AH,AH ;Read system time
INT 1AH
STI ;Enable interrupts Ver 1.1
CMP SI,CX
JNE D_20
CMP DI,DX ;End of delay time
JGE D_20 ;No, keep checking
POP AX ;Restore registers
POP DI
POP DX
POP CX
RET
DELAY ENDP

;----------------------------------------------------------------------
; String at SI is placed in output buffer to be sent out serial port
; Input - SI points to zero terminated string
; Output - Nothing
; Changes - SI
;----------------------------------------------------------------------
LOAD_ZSTRING PROC NEAR
PUSH AX ;Save register
MOV AH,1 ;Use output buffer
CLD ;Forward
LZ_10:
LODSB ;Get a byte of data
CMP AL,0 ;Check for zero
JZ LZ_EXIT ;Yes, then exit
CALL PUT_BUFF_DATA ;No, put in output buffer
JMP LZ_10 ;Process next data
LZ_EXIT:
POP AX ;Restore register
RET
LOAD_ZSTRING ENDP

;----------------------------------------------------------------------
; Check for a key in the keyboard buffer, if one is there, get it
; Input - Nothing
; Output - Zero flag = 1 no key in buffer
; Zero flag = 0 key is in AX
; Changes - AX
;----------------------------------------------------------------------
GET_KEYSTROKE PROC NEAR
ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING
MOV AH,1 ;Check for keystroke
INT 16H ;Keyboard BIOS
JZ GK_EXIT ;No key so exit
PUSHF ;Save the zero flag
XOR AH,AH ;Get the keystroke
INT 16H
POPF ;Restore the zero flag
GK_EXIT:
RET
GET_KEYSTROKE ENDP

;----------------------------------------------------------------------
; Initialize the buffer pointers for the input and output buffers.
; Input - CX points to starting buffer location
; Output - Input and output buffer points are initialized
; Changes - BX, CX
;----------------------------------------------------------------------
INIT_BUFFERS PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV BX,OFFSET IN_BUFF_HEAD ;In buffer will be here
MOV WORD PTR [BX],CX ; Set head pointer to buffer
MOV WORD PTR 2[BX],CX ;Set tail pointer to buffer
MOV WORD PTR 4[BX],CX ;Set begin of buffer
ADD CX,IN_BUFF_SIZE ;CX Points to end of in buffer
IB_10:
MOV WORD PTR 6[BX],CX ;Set end of buffer
MOV BX,OFFSET OUT_BUFF_HEAD ;Out buffer after in buffer
MOV WORD PTR [BX],CX ; Set head pointer to buffer
MOV WORD PTR 2[BX],CX ;Set tail pointer to buffer
MOV WORD PTR 4[BX],CX ;Set begin of buffer
ADD CX,OUT_BUFF_SIZE ;CX Points to end of out buffer
MOV WORD PTR 6[BX],CX ;Set end of buffer
RET
INIT_BUFFERS ENDP

;----------------------------------------------------------------------
; Change the interrupt 8 vector to the interrupt service routine of
; PCREMOTE
; Input - Nothing
; Output - Interrupt vector 8 points to INT8
; Changes - AX, DX, BX, ES
;----------------------------------------------------------------------
MODIFY_INT8 PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
;
; Change interrupt 8 vector
;
MOV AX,3508H ;Get interrupt 8h vector
INT 21H
MOV OLDINT_8,BX ;And save it
MOV OLDINT_8[2],ES
MOV AX,2508H ;Set interrupt 8h vector
MOV DX,OFFSET INT8 ; to point to new routine
INT 21H
RET
MODIFY_INT8 ENDP

;----------------------------------------------------------------------
; Change the interrupt 21 vector to the interrupt service routine of
; PCREMOTE
; Input - Nothing
; Output - Interrupt vector 21 points to INT21
; Changes - AX, DX, BX, ES
;----------------------------------------------------------------------
MODIFY_INT21 PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,3521H ;Get interrupt 21H vector
INT 21H
MOV WORD PTR OLDINT_21,BX ;And save it
MOV WORD PTR OLDINT_21[2],ES
MOV AX,2521H ;Set interrupt 21H vector
MOV DX,OFFSET INT21 ; to point to new routine
INT 21H
RET
MODIFY_INT21 ENDP

;----------------------------------------------------------------------
; Change the communication interrupt vector to the interrupt service
; routine of PCREMOTE
; Input - SET_VEC contains interrupt vector number
; Output - communications interrupt vector points to INT_COMM
; Changes - AX, DX, BX, ES
;----------------------------------------------------------------------
MODIFY_INTCOMM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH ES
MOV AL,BYTE PTR SET_VEC ;Get the interrupt number
MOV AH,35H ;Get the old vector
INT 21H
MOV WORD PTR OLDINT_COMM,BX ;And save it
MOV WORD PTR OLDINT_COMM[2],ES
POP ES
MOV AL,BYTE PTR SET_VEC ;Get the interrupt number
MOV AH,25H ;Set interrupt for comm vector
MOV DX,OFFSET INT_COMM ; to point to new routine
INT 21H
RET
MODIFY_INTCOMM ENDP

;----------------------------------------------------------------------
; Set baud rate
; Input - AL -0 1200 baud, 1 2400 baud, 2 4800 baud, 3 9600 baud
; 4 19200 baud, 5 38400 baud
; Output - Nothing
; Changes - Nothing
;----------------------------------------------------------------------
SET_BAUD_RATE PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH DX ;Save registers
PUSH BX
PUSH SI
PUSH AX
MOV DX,COMM_PORT ;Get port address
ADD DX,3 ;Line control register
MOV AL,83H ;Toggle port address to
OUT DX,AL ; prepare to set baud rate
SUB DX,2 ;Baud rate divisor MSB port
POP AX ;Restore baud rate
PUSH AX
XOR AH,AH ;Zero AH
MOV SI,AX ;Save in index register
SHL SI,1 ;Multiply by 2, word address
MOV BX,OFFSET BAUD_RATE ;Point to baud rates
MOV AL,[BX+SI] ;Get baud rate MSB
OUT DX,AL ; and set it
DEC DX ;Baud rate divisor LSB port
MOV AL,1[BX+SI] ;Get baud rate LSB
OUT DX,AL ; and set it
ADD DX,3 ;Line control register
MOV AL,3 ;8 data bits,1 stop,no parity
OUT DX,AL ;Set data bit pattern
; and toggle port address
POP AX ;Restore registers
POP SI
POP BX
POP DX
RET
SET_BAUD_RATE ENDP

;----------------------------------------------------------------------
; Initialize the serial port
; Input - COMM_PORT contains port address,
; COMM_FLAG 0-comm1 1-comm2 2-comm3 3-comm4
; Output - Serial port initialize for interrupt driven I/O
; Changes - Nothing
;----------------------------------------------------------------------
INIT_SERIAL PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH DX ;Save registers
PUSH CX
PUSH BX
PUSH AX
;
; disable the interrupts on the 8250 and initialize DTR and RTS
;
CLI ;Disable interrupts
MOV DX,COMM_PORT ;Get UART base address
ADD DX,4 ;Modem control register
MOV AL,00001011B ;Set DTR, RTS, and OUT2
OUT DX,AL
;
; set the baud rate of the UART and initialize line control register
;
CMP BYTE PTR CONNECT_FLAG,0 ;If connect then use current
JZ IS_10
MOV AL,BYTE PTR CURRENT_SPEED
JMP IS_20
IS_10:
MOV AL,SPEED_FLAG ;Get speed flag
IS_20:
MOV BYTE PTR CURRENT_SPEED,AL ;Save the current speed
MOV BYTE PTR SPEED_COUNT,19
CALL SET_BAUD_RATE
;
; set 8259 mask to enable the comm port interrupt
;
MOV CL,BYTE PTR SET_VEC ;Get the interrupt number
SUB CL,8 ; and adjust
MOV AH,1 ;Mask for the interrupt
SHL AH,CL
NOT AH ;Invert
IN AL,21H ;Get current 8259 int mask
AND AL,AH ;Mask appropriate int bit
OUT 21H,AL ;And set new 8259 mask
;
; enable the data received interrupt and reset the 8250
;
SUB DX,3 ;Point to int enable reg
MOV AL,3 ;Enable data received
OUT DX,AL ; and transmit empty int
DEC DX ;Point to base address
MOV CX,7 ;Reset the serial port
IS_30:
IN AL,DX ;Read registers to reset
INC DX
LOOP IS_30

CMP BYTE PTR COMM_INT_STA,0 ;Transmit int. running?
JZ IS_50 ; no, so exit
MOV AX,OUT_BUFF_HEAD ;Is data in output buffer
CMP AX,OUT_BUFF_TAIL
JZ IS_50 ;No, so exit
MOV DX,COMM_PORT ;Get port base address
ADD DX,5 ;Line status register
IS_40:
IN AL,DX ;Make sure holding reg. empty
TEST AL,00100000B ;Test the hold reg empty flag
JZ IS_40 ;Loop if not empty
SUB DX,5 ;Port base register
MOV AH,1 ;Use the output buffer
CALL GET_BUFF_DATA ;Get data from output buffer
OUT DX,AL ;Send the data out the port
IS_50:
STI ;Enable interrupts
POP AX ;Restore registers
POP BX
POP CX
POP DX
RET
INIT_SERIAL ENDP

;======================================================================
; Initialize routines. The initialize routine for unattended is first
; because it must remain resident.
;======================================================================

;-----------------------------------------------------------------------------
; Initialize the unattended program.
;-----------------------------------------------------------------------------
INIT_UNATTENDED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
CALL IU_10 ;Init pointers,clear video copy
CALL MODIFY_INT8 ;Change INT 8 vector
CALL MODIFY_INT21 ;Change INT 21 vector
;
; Terminate-Stay-Resident
;
MOV DX,VIDEO_COPY ;Start of video copy
ADD DX,4000D ;Allow room for video copy
INT 27H ;Terminate-Stay-Resident
;
; Initialize buffer pointers and video copy pointer
;
IU_10:
PUSH ES ;Save extra segment
MOV CX,OFFSET INITIALIZE ;CX points to begin of buffer
ADD CX,256D ;Leave room for TSR stack
CALL INIT_BUFFERS ;Initalize the buffer pointers
INC CX ;Point to copy of video ram
MOV TEMP_VIDEO_PTR,CX ;Save address of temp video buf
ADD CX,BLOCK_SIZEX2 ;Save room for temp video data
MOV VIDEO_COPY,CX ;Save address of video copy
;
; fill video RAM image with with space code since screen
; of manned system is blanked when connected
;
PUSH CS ;Video copy is destination
POP ES ;ES:DI points to video copy
MOV DI,WORD PTR VIDEO_COPY
MOV AX,0720H ;Data to fill buffer
CLD ;Move upward
MOV CX,2000D ;Move 2000 words
REP STOSW ;Fill to force screen dump
MOV CURSOR_POSITION,0FFFFH ;Force cursor position update
MOV AL,1 ;Use setup string 2
MOV BYTE PTR BAD_PASSWORD,0D ;Do not send exit code
CALL RESET_MODEM ;Reset the modem
POP ES ;Restore extra segment
RET
INIT_UNATTENDED ENDP

;-----------------------------------------------------------------------------
; INITIALIZE - Initialize the program. Determine whether it is manned
; or unattended by processing the command line. Initialize the serial
; port. This area is overwritten by PCREMOTE host mode to conserve memory.
;-----------------------------------------------------------------------------
INITIALIZE PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
;
; Display copyright notice.
;
MOV DX,OFFSET COPYRIGHT ;Display copyright notice
MOV AH,9 ;Display string
INT 21H
;
; check to see if the program is already in memory
;
MOV BYTE PTR START,0 ;Zero word to avoid false match
XOR BX,BX ;Initialize search segment
MOV AX,CS ;Save current segment in AX
CLD ;Clear direction flag
I_10:
INC BX ;Increment search segment
CMP AX,BX ;Reached current segment?
JE I_20 ;Yes, PCREMOTE not resident
MOV ES,BX ;Point ES to search segment
MOV SI,OFFSET START ;Start of compare area
MOV DI,SI ;Make offsets equal
MOV CX,16D ;Check 16 characters
REPE CMPSB ;Compare the strings
JNE I_10 ;Compare failed
MOV INSTALLED_SEG,BX ;Save the segment of resident

; MOV AH,09H ;Print string
; MOV DX,OFFSET PROG_RES ;Display program resident mess.
; INT 21H
; INT 20H ;Terminate

;
; Process command line for switches.
;
I_20:
PUSH CS ;Restore ES
POP ES
MOV BX,80H ;Point to command line length
MOV AH,[BX] ;Get command line length
I_30:
OR AH,AH ;Check for commands
JNZ I_40
JMP I_210 ;None, so don't look
I_40:
INC BX ;Point to next data
MOV AL,[BX] ;Get data
DEC AH ;Decrement counter
I_50:
CMP AL,"?" ;Check for help menu (usage)
JNE I_60 ;No, check for slash
JMP HELP ;Yes, help is requested !
I_60:
CMP AL,"/" ;Check for slash
JNE I_30 ;No, jump to check next data
OR AH,AH ;Check for data after slash
JNZ I_70
JMP I_210 ;No, so don't process
I_70:
INC BX ;Point to next data
MOV AL,[BX] ;Get data
DEC AH ;Decrement counter
CMP AL,"?" ;Check for help after a slash
JNE I_80 ;No, check for comm2
JMP HELP ;Yes, help is requested !
I_80:
CMP AL,"1" ;Check for comm1
JNE I_90 ;No, check for another switch
MOV BYTE PTR COMM_FLAG,0 ;Set comm1 flag
I_90:
CMP AL,"2" ;Check for comm2
JNE I_100 ;No, check for another switch
MOV BYTE PTR COMM_FLAG,1 ;Set comm2 flag
I_100:
CMP AL,"3" ;Check for comm port 3
JNE I_110 ;No, jump to check next data
MOV BYTE PTR COMM_FLAG,2 ;Set comm3 flag
I_110:
CMP AL,"4" ;Check for comm port 4
JNE I_120 ;No, jump to check next data
MOV BYTE PTR COMM_FLAG,3 ;Set comm4 flag
JMP I_30 ;Process next switch
I_120:
OR AL,20H ;Force data to lower case
CMP AL,"m" ;Check for manned switch
JNE I_130 ;No, check for another switch
INC BYTE PTR MANNED_FLAG ;Set manned flag
JMP I_30 ;Process next switch
I_130:
CMP AL,"b" ;Check for baud rate
JNE I_170 ;No, check for another switch
OR AH,AH ;Check for data after slash
JZ I_210 ;No, so don't process
INC BX ;Point to next data
MOV AL,[BX] ;Get data
DEC AH ;Decrement counter
CMP AL,"1" ;Check for 1200 baud
JNE I_140
MOV BYTE PTR SPEED_FLAG,0 ;1200 baud
MOV AL,[BX+1] ;Check for 19200 baud
CMP AL,"9"
JNE I_140 ;No so continue
INC BX ;Skip this data now
DEC AH ;Decrement counter
MOV BYTE PTR SPEED_FLAG,4 ;19200 baud
JMP I_30
I_140:
CMP AL,"2" ;Check for 2400
JNE I_150
MOV BYTE PTR SPEED_FLAG,1 ;2400 baud
I_150:
CMP AL,"4" ;Check for 4800
JNE I_160
MOV BYTE PTR SPEED_FLAG,2 ;4800 baud
I_160:
CMP AL,"9" ;Check for 9600
JNE I_165 ;Check next speed
MOV BYTE PTR SPEED_FLAG,3 ;9600
JMP I_30 ;Process next switch
I_165:
CMP AL,"3" ;Check for 38400
JE I_166
JMP I_50 ;maybe char is next slash
I_166:
MOV BYTE PTR SPEED_FLAG,5 ;38400
JMP I_30 ;Process next switch
I_170:
CMP AL,"d" ;Check for desnow
JNE I_180 ;No, process next switch
INC BYTE PTR DESNOW_FLAG ;Set desnow flag
JMP I_30 ;Process next switch
I_180:
CMP AL,"n" ;Check for null modem
JNE I_190 ;No, process next switch
INC BYTE PTR NULL_MODEM ;Set null modem flag
JMP I_30 ;Process next switch
I_190:
CMP AL,"s" ;Check for smile face flag
JNE I_200 ;No, process next switch
INC BYTE PTR SMILE_FLAG ;Yes, set smile flag
JMP I_30 ;No, check for more switches
I_200:
CMP AL,"u" ;Check for uninstall flag
JNE I_205 ;No, process next switch
JMP UNINSTALL ;If pcremote resident
I_205:
JMP I_30 ;Process next switch

I_210:
;
; If PCREMOTE is resident and this is host mode then terminate
;
CMP INSTALLED_SEG,0 ;Check for resident segment
JZ I_215 ;none, then ok to continue
CMP MANNED_FLAG,1 ;Check for manned mode
JZ I_215 ;yes, then continue
MOV AH,09H ;Print string
MOV DX,OFFSET PROG_RES ;Display program resident mess.
INT 21H
INT 20H ;Terminate
;----------------------------------------------------------------------
; Initialize the serial port
;----------------------------------------------------------------------
I_215:
;
; get the comm port base address using 2* comm port flag as offset
;
PUSH DS ;Save data segment
XOR BX,BX ;Zero BX
MOV BL,COMM_FLAG ;Get comm port flag
MOV SI,BX ;Save in index register
SHL SI,1 ;Multiply by 2, word address
MOV AX,40H ;Point DS to BIOS data area
MOV DS,AX
XOR BX,BX ;Point to comm port address
MOV AX,[BX+SI] ;Get comm port address
POP DS ;Restore data segment
CMP AX,0 ;Make sure there is a entry
JNZ I_230
CMP BYTE PTR COMM_FLAG,2 ;Is this comm 3?
JNZ I_220 ;No, must be comm4
MOV AX,WORD PTR COMM_PORT3 ;Yes, get the comm port 3 add
JMP I_230
I_220:
MOV AX,WORD PTR COMM_PORT4
I_230:
MOV WORD PTR COMM_PORT,AX ;Save comm port address
;
; modify the interrupt vector for the comm port
;
CMP BYTE PTR COMM_FLAG,0 ;Determine INT vector to change
JNZ I_240
MOV AL,4 ;Comm 1 interrupt 4
I_240:
CMP BYTE PTR COMM_FLAG,1
JNZ I_250
MOV AL,3 ;Comm 2 interrupt 3
I_250:
CMP BYTE PTR COMM_FLAG,2
JNZ I_260
MOV AL,BYTE PTR COMM3_INT ;Comm 3 interrupt
I_260:
CMP BYTE PTR COMM_FLAG,3
JNZ I_270
MOV AL,BYTE PTR COMM4_INT ;Comm 4 interrupt
I_270:
ADD AL,8 ;Interrupt number
CLI
MOV BYTE PTR SET_VEC,AL ;
CALL MODIFY_INTCOMM ;Change comm interrupt vector
CALL INIT_SERIAL ;Initialize the serial port
;
; determine color or monochrome and save appropriate video segment
;
MOV AH,0FH ;Determine video mode
INT 10H ;By using BIOS int 10
CMP AL,7 ;Check for monochrome
JZ MONOCHROME ;Jump if it is
MOV WORD PTR VIDEO_SEGMENT,0B800H ;Nope, it's CGA or EGA
JMP I_280 ;Skip over
MONOCHROME:
MOV WORD PTR VIDEO_SEGMENT,0B000H ;It's a monochrome
I_280:
;
; If manned then execute manned initialization, otherwise execute
; unattended initialization.
;
CMP BYTE PTR MANNED_FLAG,0 ;Manned or unattended
JZ I_290
JMP INIT_MANNED ;Init the manned routines
I_290:
JMP INIT_UNATTENDED ;Init the unattended routines
INITIALIZE ENDP

;----------------------------------------------------------------------
; Uninstall the resident (host) portion of PCREMOTE. Make sure another
; program did not change any of the interrupt vectors, if so, then the
; vectors cannot be restored properly and the program cannot be uninstalled.
; Tom - if this looks like some of your code, it is, I borrowed from
; PRN2FILE.
;----------------------------------------------------------------------
UNINSTALL PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AL,08H ;Check the timer interrupt
CALL CHECK_SEG
JNE CANT_UNINSTALL ;If changed, can't uninstall
MOV AL,21H ;Check DOS interrupt vector
CALL CHECK_SEG
JNE CANT_UNINSTALL ;If changed, can't uninstall
MOV ES,INSTALLED_SEG ;Get the segment of resident
MOV AL,BYTE PTR ES:SET_VEC ;Check comm interrupt vector
CALL CHECK_SEG
JNE CANT_UNINSTALL ;If changed, can't uninstall
MOV ES,INSTALLED_SEG
ASSUME DS:NOTHING, ES:NOTHING
LDS DX,DWORD PTR ES:OLDINT_8;Get original timer vector
MOV AX,2508H ;and change it back
INT 21H
LDS DX,DWORD PTR ES:OLDINT_21;Get original DOS vector
MOV AX,2521H ;and change it back
INT 21H
; comm vector changed last so PCREMOTE doesn't go to sleep
CALL RESET_COMM ;Reset the comm port 8250
LDS DX,DWORD PTR ES:OLDINT_COMM ;Get original comm vector
MOV AX,ES:SET_VEC ;and change it back
INT 21H
; release the memory allocated to the resident portion of PCREMOTE
MOV ES,WORD PTR ES:2CH ;Get the environment segment
MOV AH,49H ;Free allocated memory
INT 21H
JC RELEASE_ERR
MOV ES,INSTALLED_SEG ;Resident program segment
NOT WORD PTR ES:START ;Modify so can't find again
MOV AH,49H ;Free allocated memory
INT 21H
JC RELEASE_ERR ;Error releasing the memory
MOV DX,OFFSET YES_UNINSTALL ;Display uninstalled mess
JMP UNI_10
CANT_UNINSTALL:
MOV DX,OFFSET NO_UNINSTALL ;Display can't uninstall mess
JMP UNI_10
RELEASE_ERR:
MOV DX,OFFSET CANT_RELEASE ;Display can't release mess
JMP UNI_10
UNI_10:
PUSH CS ;Restore the data segment
POP DS
MOV AH,09H ;Print string
INT 21H
MOV AX,4C00H
INT 21H ;Terminate
UNINSTALL ENDP

;----------------------------------------------------------------------
; Check to see if an interrupt vector points to the installed program
; segment.
; Input: AL contains interrupt to check.
; Output: Zero flag = 1 if yes, else Zero flag = 0
; Destroys: AH, ES
;----------------------------------------------------------------------
CHECK_SEG PROC NEAR
MOV AH,35H ;Get vector
INT 21H
MOV AX,ES ;Save returned segment
CMP AX,INSTALLED_SEG ;Is it installed segment
RET
CHECK_SEG ENDP

;----------------------------------------------------------------------
; Initialize the manned program. Initialize buffers, and change INT 8
; vector.
;----------------------------------------------------------------------
INIT_MANNED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
CALL GET_COMSPEC
MOV CX,OFFSET LAST_BYTE ;CX points to begin of buffer
CALL INIT_BUFFERS ;Initalize the buffer pointers
MOV AX,VIDEO_SEGMENT ;Set ES to point to video RAM
MOV ES,AX
JMP CONNECT_MANNED ;Run manned portion of program
INIT_MANNED ENDP

;======================================================================
; Data used by manned portion of program only. Not required to be
; resident for unattended mode.
;======================================================================
HOST_PORT EQU BYTE PTR CONNECT_UNATTENDED ;Host port number (1-4) DB
OLD_CURSOR EQU WORD PTR HOST_PORT+1 ;Cursor position save field DW
UP_LENGTH EQU WORD PTR OLD_CURSOR+2 ;Upload string length DW
DN_LENGTH EQU WORD PTR UP_LENGTH+2 ;Download string length DW
PATH_LENGTH EQU WORD PTR DN_LENGTH+2 ;Pathname string length DW
LAST_KEY EQU BYTE PTR PATH_LENGTH+2 ;Last key pressed save field DB
EXEC_VAL EQU BYTE PTR LAST_KEY+1 ;Shell Variable (1-3) DB
; 1 = Remote shell to DOS
; 2 = Remote Download
; 3 = Remote Upload
UPDN_STRING EQU BYTE PTR EXEC_VAL+1 ;Upload string (filename)
PATH_STRING EQU BYTE PTR UPDN_STRING+44 ;Path string (drive & path)
ORGDSK EQU BYTE PTR PATH_STRING+44 ;1 byte
ORGPTH EQU BYTE PTR ORGDSK+1 ;256 bytes
BLOCK_DATA_COUNT DB 0 ;Number of bytes left to rec.
TYPE_TRANSFER DB 0FFH ;FF - No data transfer in prog.
;00 - Received char. data
;01 - Received attr. data
RPT_STATUS DB 0 ;00 - No data repeat
;02 - Repeat the char in block
CUR_STATUS DB 0 ;0=No cursor data being rec.
; otherwise, byte count
CUR_LOW DB 0 ;Low byte of cursor data
CUR_HIGH DB 0 ;High byte of cursor data
ENTER_NUMBER DB CR,LF,CR,LF,"Enter phone number:$"
NO_CARRIER DB CR,LF,"No carrier.$"
MODEM_ERROR DB CR,LF,"Error.$"
TERMINATE_MESS DB CR,LF,"Returning to DOS.$"
PROG_RES DB CR,LF,"PCREMOT2 is already resident.$"
NO_UNINSTALL DB CR,LF,"Cannot uninstall PCREMOT2.$"
YES_UNINSTALL DB CR,LF,"PCREMOT2 uninstalled successfully.$"
CANT_RELEASE DB CR,LF,"Error release PCREMOT2 resident memory.$"
SHELL_PROMPT DB " Shell to DOS initiated. Type 'EXIT' at any DOS "
DB "prompt to return to PCREMOT2. ",0
TRANSFER_PROMPT DB " File transfer initiated ... ",0
PARM_$_1200 DB "/a /8 /r",0 ;ZCOPY parm 1200 baud
PARM_$_2400 DB "/a /7 /r",0 ;ZCOPY parm 2400 baud
PARM_$_4800 DB "/a /6 /r",0 ;ZCOPY parm 4800 baud
PARM_$_9600 DB "/a /5 /r",0 ;ZCOPY parm 9600 baud
PARM_$_19200 DB "/a /4 /r",0 ;ZCOPY parm 19200 baud
PARM_$_38400 DB "/a /3 /r",0 ;ZCOPY parm 38400 baud
PARM_STRING DB " ",0 ;Actual ZCOPY parm
ASCII_CODE DB "~`!1@2#3$4%5^6&7*8(9)0_-+|\=QWERTYUIOP{[}]ASDFGHJKL:;"
DB 34D,39D,CR,"ZXCVBNM<,>.?/ "
SCAN_CODE DB 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12
DB 13,13,14,14,17,18,19,20,21,22,23,24,25,26,27,27,28,28
DB 31,32,33,34,35,36,37,38,39,40,40,41,41,43,46,47,48,49
DB 50,51,52,53,53,54,54,55,55,61
ZCOPY DB "ZCOPY ",0 ;ZCOPY command line
REMOTE_COM DB "COM ",0 ;Remote Comm value
HOST_COM DB "COM ",0 ;Host Comm value
UPDNLOAD_COM DB " /c zcopy ", 90 DUP(?)
HELP_SCREEN DB CR,LF
DB " Usage : PCREMOT2 [ /# ] [ /B# ] [ /M ] [ /D ] [ /S ] [ /N ]"
DB CR,LF,CR,LF
DB " [ /# ] - # = Communication port number ( 1 - 4 ) "
DB CR,LF
DB " [ /B# ] - # = Baud rate 1=1200, 2=2400, 4=4800, 9=9600 "
DB CR,LF
DB " null modem only 19=19200, 3=38400 "
DB CR,LF
DB " [ /M ] - Manned mode (used on remote computer) "
DB CR,LF
DB " [ /D ] - Desnow flag (used on CGA monitors) "
DB CR,LF
DB " [ /S ] - Smile face enable (used on host computer) "
DB CR,LF
DB " [ /N ] - Null modem connects the two computers "
DB CR,LF
DB " [ /U ] - Uninstall (used on host computer to remove from memory) "
DB CR,LF,CR,LF,"$",26
EXIT_MESSAGE DB " "
DB " ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ "
DB " ³ OK to EXIT? (Y/N)³ "
DB " ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ "
DB " "
BOX_CHAR DW 164 ;location for character
DB 'É' ;Character for box
DW 256
DB '»'
DW 324
DB 'º'
DW 416
DB 'º'
DW 484
DB 'Ì'
DW 576
DB '¹'
DW 644
DB 'º'
DW 804
DB 'º'
DW 964
DB 'º'
DW 1124
DB 'º'
DW 736
DB 'º'
DW 896
DB 'º'
DW 1056
DB 'º'
DW 1216
DB 'º'
DW 1284
DB 'Ì'
DW 1376
DB '¹'
DW 1444
DB 'º'
DW 1536
DB 'º'
DW 1604
DB 'È'
DW 1696
DB '¼'
TRANSFER_MESS DB "FILE TRANSFER REQUESTED",0
DB " ",0
DB " (U)pload - Send file(s) to Host System.",0
DB " or",0
DB " (D)ownload - Receive file(s) from Host.",0
UPLOAD_MESS DB " UPLOAD REQUESTED",0
DB "Enter the full pathname of the file(s) on ",0
DB "the remote system which you want to upload",0
DB "to the host system.",0
DB ":",0
DOWNLOAD_MESS DB " DOWNLOAD REQUESTED",0
DB "Enter the full pathname of the file(s) on ",0
DB "the host system which you want to download",0
DB "to the remote system.",0
DB ":",0
UP_PATH_MESS DB " UPLOAD REQUESTED",0
DB "Enter the path on the host system to place ",0
DB "the uploaded file(s). No target, other",0
DB "than directory path, may be specified.",0
DB ":",0
DN_PATH_MESS DB " DOWNLOAD REQUESTED",0
DB "Enter the path on the remote system to place",0
DB "the downloaded file(s). No target, other",0
DB "than directory path, may be specified.",0
DB ":",0
PRESS_ESC DB "Press to return to PCREMOT2",0
;
;-----------------------------------------------------------------------
;
COMSPC LABEL DWORD
COM_OFS DW 0000
COM_SEG DW 0000

PAR_BLK DW 0000
DW 0000 ; OFFSET NUL_TAIL
DW 0000
DW 0000
DD 0FFFFH
NOEXEC DB 00
;--------------------------------------------------------------------
;
COM_VAR DB 'COMSPEC='
NUL_TAIL DB 00
;
; DON'T REMOVE OR PHYSICALLY REARRANGE THIS
;
GODPMT DB '$P$G',00
PMTSTR DB 'PROMPT=',00
PMTLOC DW 0000
DFTPMT DB 'PROMPT='
PCRSTR DB 'DOS SHELL- '
PMTLGT DW $-OFFSET PCRSTR

;======================================================================
; The manned routine will execute the connect manned routine to call the
; unattended system. These routines are only used in the manned mode.
;======================================================================

;----------------------------------------------------------------------
; User requested a help screen by including a question mark in the
; command line. Therefore, display the usage for PCREMOTE and end
; the program.
;----------------------------------------------------------------------
HELP PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH CS ;
POP DS ;DS->CS
MOV AH,09H ;String print INT21 (09)
MOV DX,OFFSET HELP_SCREEN ;DS:DX->Help screen variable
INT 21H ;Display the help screen
INT 20H ;Terminate the program
HELP ENDP

;----------------------------------------------------------------------
; Connect manned asks for the phone number to call, dials the number
; and waits for connect. Once connected it sends bursts of 20 CRs at
; one second intervals. When it receives alpha data it processes video
; data and waits for sync byte (00), then transfers control to the
; manned routine.
;----------------------------------------------------------------------
CONNECT_MANNED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH ES ;
PUSH CS ;
POP ES ;
MOV BX,1000H ;
MOV AH,4AH ;
INT 21H ;
POP ES ;
CM_10:
CMP BYTE PTR NULL_MODEM,0 ;If null modem skip number
JNZ CM_50
XOR AL,AL ;Use setup string 1
CALL RESET_MODEM ;Reset the modem first
MOV DX,OFFSET ENTER_NUMBER ;Point to enter number mess.
MOV AH,9 ;Display message
INT 21H
MOV DX,OFFSET UPDN_STRING ;Unused area as keyboard buffer
MOV BX,DX ;Enter max characters for input
MOV BYTE PTR [BX],40D
MOV AH,0AH ;Buffered keyboard input
INT 21H ;Get the phone number
MOV AL,1 ;Wait a second
CALL DELAY
MOV SI,OFFSET UPDN_STRING ;Point to phone number
INC SI ;Amount of data in key buffer
XOR AX,AX ;Zero AX
CLD ;Forward
LODSB ;Get count
OR AL,AL ;Check for no input
JNZ CM_20 ;If there is then continue
JMP EXIT_MANNED ;No, then exit program
CM_20:
PUSH SI ;Save pointer
MOV SI,OFFSET TONE_DIAL ;Send the tone dial command
CALL LOAD_ZSTRING
POP SI ;Restore pointer
ADD SI,AX ;Point to end of buffer
INC SI ;Include CR code
MOV BYTE PTR [SI],0 ;And put a zero on end
DEC SI ;Adjust for previous INC
SUB SI,AX ;Back to begin of buffer
CALL LOAD_ZSTRING ;Dial the phone number
MOV AL,1 ;Wait a second
CALL DELAY
CALL CLEAR_INBUFF ;Clear input buffer
CM_30:
CALL GET_KEYSTROKE ;Check for a keystroke
JZ CM_40 ;None, so wait
CMP AL,ESC_KEY ;Check for ESCAPE
JNZ CM_40 ;No
JMP CM_10 ;Yes, then enter new number
CM_40:
CALL CHECK_CARRIER ;Check for a connect signal
JZ CM_60 ;No, then check for err codes
CM_50:
CALL CLEAR_SCREEN ;Yes, blank the screen
MOV BYTE PTR CONNECT_FLAG,1 ;Set the connect flag
JMP CM_90 ;Execute manned
CM_60:
MOV AH,0 ;Use input buffer
CALL GET_BUFF_DATA ;Get data from buffer
JNC CM_30 ;No data, try again
CMP AL,"3" ;No carrier code?
JNZ CM_80 ;No, check for other code
CM_70:
MOV DX,OFFSET NO_CARRIER ;Send no carrier message
MOV AH,09H
INT 21H
JMP CM_10 ;Yes, reset modem and try again
CM_80:
CMP AL,"8" ;2400 no answer code
JZ CM_70 ;Yes, display no carrier mess
CMP AL,"4" ;Error code?
JNZ CM_30 ;No, check for other code
MOV DX,OFFSET MODEM_ERROR ;Send modem command error mess
MOV AH,09H
INT 21H
JMP CM_10 ;Yes, reset modem and try again
CM_90:
MOV AL,1 ;Wait for a second
CALL DELAY
MOV BYTE PTR CR_COUNT,30D ;Send 30 CRs at a time
CM_100:
CALL CHECK_CARRIER ;Check for carrier loss
JNZ CM_110 ;No, then continue
JMP EXIT_MANNED ;Yes, then exit
CM_110:
CALL GET_KEYSTROKE ;Check for a keystroke
JZ CM_120 ;None, so wait
CMP AL,ESC_KEY ;Check for ESCAPE
JNZ CM_120 ;No
JMP EXIT_MANNED ;Yes, give up
CM_120:
MOV AX,OUT_BUFF_TAIL ;Is output buffer empty?
CMP AX,OUT_BUFF_HEAD
JNZ CM_130 ;No, skip CR
MOV AL,CR ;Send a CR for speed sync
MOV AH,1 ;Use output buffer
CALL PUT_BUFF_DATA
DEC BYTE PTR CR_COUNT ;Decrement CR counter
JZ CM_90
CM_130:
MOV AH,0 ;Use input buffer
CALL GET_BUFF_DATA ; and check for data
JNC CM_100 ;No data so keep checking
CMP AL,'@' ;Alpha message?
JL CM_100 ;No, then wait for it
CM_140:
CMP BYTE PTR NULL_MODEM,0 ;Using a null modem ?
JZ CM_150 ;No... no need for exit check
PUSH AX ;Store AX just in case
MOV AH,AL ;Set up AX for exit compare
XOR AL,AL ;Reset AL to zero
CMP WORD PTR EXIT_CODE,AX ;Did host send exit_code ?
POP AX ;Restore AX just in case no
JNZ CM_150 ;No check for sync byte
JMP EXIT_MANNED ;Yes, exit back to DOS
CM_150:
CMP AL,0 ;Is data a sync byte
JNE CM_270 ;No, then put data on screen
CM_160: ;
MOV AH,0 ;Input buffer
CALL GET_BUFF_DATA ;Receive a character
JNC CM_160 ;Did we receive one yet ?
CMP AL,0 ;Sync byte ?
JE CM_160 ;Yes
;
; A character was received that is not a sync byte. This is the host trying
; to tell the remote computer what Comm port the host is currently running
; through. Because garbage (line noise) is always a threat, remote must
; accurately determine which Comm port was sent.
;
PUSH CX ;Store register
PUSH DX ;Store register
XOR CX,CX ;Reset counters to zero
XOR DX,DX ;Reset counters to zero
JMP CM_180 ;Process AL data
CM_170:
MOV AH,0 ;Use input buffer
CALL GET_BUFF_DATA ;Receive a character
JNC CM_170 ;Did we get one yet ?
CMP AL,0 ;Sync byte ?
JE CM_220 ;Yes, determine host comm
CM_180:
CMP AL,1D ;One received ?
JNE CM_190 ;No
INC CL ;Yes, bump Comm 1 counter
CM_190:
CMP AL,2D ;Two received ?
JNE CM_200 ;No
INC CH ;Yes, bump Comm 2 counter
CM_200:
CMP AL,3D ;Three received ?
JNE CM_210 ;No
INC DL ;Yes, bump Comm 3 counter
CM_210:
CMP AL,4D ;Four received ?
JNE CM_170 ;No, get another character
INC DH ;Yes, bump Comm 4 counter
JMP CM_170 ;Get another character
CM_220:
MOV AL,'1' ;Assume Comm 1 for now
CMP CH,CL ;Comm2 count > Comm1 count ?
JLE CM_230 ;No
MOV AL,'2' ;Yes, assume Comm2 for now
MOV CL,CH ;Save Comm2 counter in CL
CM_230:
CMP DL,CL ;Comm3 count > CL (current)
JLE CM_240 ;No
MOV AL,'3' ;Yes, assume Comm3 for now
MOV CL,DL ;Save Comm3 counter in CL
CM_240:
CMP DH,CL ;Comm4 count > CL (current)
JLE CM_250 ;No
MOV AL,'4' ;Yes, Host port = Comm4
CM_250:
MOV HOST_PORT,AL ;Save largest port counter
POP DX ;Restore register
POP CX ;Restore register
CALL MAKE_PARM_STRING ;Make ZCOPY parm w/ baud & comm
CM_260:
CALL GET_BUFF_DATA ;Receive a character
JNC CM_260 ;Did we receive one yet ?
CMP AL,0 ;Sync Byte ?
JNE CM_270 ;Yes
CALL CLEAR_SCREEN ;Blank the screen
JMP MANNED ;We're in, let's run program
CM_270:
MOV DL,AL ;Prepare to display character
MOV AH,2 ; using DOS
INT 21H ;Display it
CM_280:
CALL CHECK_CARRIER ;Check for carrier loss
JNZ CM_290 ;No, then continue
JMP EXIT_MANNED ;Yes, then exit
CM_290:
CALL GET_KEYSTROKE ;Check for a keystroke
JZ CM_310 ;If none, skip next routine
;
; Check for exit code, if it is then exit
;
CMP AX,WORD PTR EXIT_CODE ;Check for exit code
JNZ CM_300 ;No, so continue
CALL CONFIRM_EXIT ;Yes, confirm exit
JNZ CM_310 ;No, so continue, don't send
JMP EXIT_MANNED ;Otherwise reset and exit
CM_300:
CALL SEND_KEYSTROKE ;Send to unattended system
CM_310:
MOV AH,0 ;Use input buffer
CALL GET_BUFF_DATA ; and check for data
JNC CM_280 ;No data, check for keys
JMP CM_140 ;Check for sync byte
CONNECT_MANNED ENDP

;----------------------------------------------------------------------
; MAKE_PARM_STRING creates the ZCOPY parameters using the current baud
; rate of the remote system. This PROC also creates REMOTE_COM using
; the COMM_FLAG. REMOTE_COM is used in MAKE_UP_COM & MAKE_DN_COM.
;----------------------------------------------------------------------
MAKE_PARM_STRING PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Store registers & flags
PUSH CX
PUSH DS
PUSH ES
PUSH DI
PUSH SI
PUSHF
CLD ;Direction flag -> forward
PUSH CS
PUSH CS
POP DS ;DS->CS
POP ES ;ES->CS
MOV DI,OFFSET PARM_STRING ;DI->Parm string's first char
MOV SI,OFFSET PARM_$_1200 ;SI->1st char of 1200 var.
CMP BYTE PTR SPEED_FLAG,1D ;2400 Baud ?
JNE MPS_10 ;Yes, use 2400 baud variable
MOV SI,OFFSET PARM_$_2400 ;SI->1st char of 2400 var.
MPS_10:
CMP BYTE PTR SPEED_FLAG,2D ;4800 Baud ?
JNE MPS_20 ;Yes, use 4800 baud variable
MOV SI,OFFSET PARM_$_4800 ;SI->1st char of 4800 var.
MPS_20:
CMP BYTE PTR SPEED_FLAG,3D ;9600 Baud ?
JNE MPS_30
MOV SI,OFFSET PARM_$_9600 ;SI->1st char of 9600 var.
MPS_30:
CMP BYTE PTR SPEED_FLAG,4D ;19200 Baud ?
JNE MPS_32
MOV SI,OFFSET PARM_$_19200 ;SI->1st char of 19200 var.
MPS_32:
CMP BYTE PTR SPEED_FLAG,5D ;38400 Baud ?
JNE MPS_34
MOV SI,OFFSET PARM_$_38400 ;SI->1st char of 38400 var.
MPS_34:
MOV CX,8D ;Move 8 letters
MPS_40:
LODSB ;Pick off char. and place it
STOSB ; in the real ZCOPY parm string
LOOP MPS_40 ;11 characters done ?
;
; Remote comm must be set up using COMM_FLAG
;
MOV DI,OFFSET REMOTE_COM ;ES:DI-> (C)om var. 1st char
ADD DI,3D ;ES:DI-> COM( ) var. 4th char
MOV AL,'1' ;Assume remote using com1
CMP BYTE PTR COMM_FLAG,1D ;Using com2 ?
JNE MPS_50 ;No
MOV AL,'2' ;Remote using com2
MPS_50:
CMP BYTE PTR COMM_FLAG,2D ;Using com3 ?
JNE MPS_60 ;No
MOV AL,'3' ;Remote using com3
MPS_60:
CMP BYTE PTR COMM_FLAG,3D ;using com4?
JNE MPS_70 ;No
MOV AL,'4' ;Remote using com4
MPS_70:
STOSB ;Store com (1-4) variable
;
; HOST_COM must be set up using the HOST_PORT variable.
;
MOV AL,HOST_PORT ;Ready AL for STOSB
MOV DI,OFFSET HOST_COM ;ES:DI->1st char of HOST_COM
ADD DI,3D ;ES:DI->4th char of HOST_COM
STOSB ;Store HOST_COM into variable
POPF ;Restore flags & registers
POP SI
POP DI
POP ES
POP DS
POP CX
POP AX
RET
MAKE_PARM_STRING ENDP

;----------------------------------------------------------------------
; Manned portion of the program. Not RAM resident. Sends keystrokes
; to the unattended computer. Data from unattended computer is
; decoded and processed.
;----------------------------------------------------------------------
MANNED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
;
; Check for loss of carrier
;
CALL CHECK_CARRIER ;No, check for carrier loss
JNZ M_10 ;No loss so continue
JMP EXIT_MANNED ;Otherwise reset and exit
;
; Check to see if shift status has changed, if so, put new status
; in the output buffer
;
M_10:
MOV AH,2 ; Prepare shift status
INT 16H ; Get shift status
CMP SHIFT_STATUS,AL ; See if the same
JE M_20 ; If so, next routine
MOV SHIFT_STATUS,AL ; Save new status
MOV AL,0FDH ; Sync, expect shift
MOV AH,1 ; Put into out buff
CALL PUT_BUFF_DATA ;
MOV AL,SHIFT_STATUS ; Get shift status
CALL PUT_BUFF_DATA ; Send to host system
;
; Check for keystoke, if so, put the ASCII code and scan code
; into the output buffer, check it for exit code
;
M_20:
CALL GET_KEYSTROKE ;Check for keystroke
JNZ M_30
JMP M_90 ;If none skip this routine
;
; Check for shell to DOS, if it is then shell & return
;
M_30:
CMP AX,WORD PTR SHELL_CODE ; Shell key pressed ?
JNE M_40 ; No....
MOV EXEC_VAL,1D ; Tell EXEC to shell
CALL EXEC_ZCOPY ; EXEC to shell
CALL INIT_SERIAL ; Re-initialize serial port
CALL CLEAR_SCREEN ; Clear the remote screen
MOV AX,WORD PTR CLEAR_CODE ; Send clear_code to
CALL SEND_KEYSTROKE ; rewrite entire screen
JMP M_90 ; Process next char
;
; Check for exit code, if it is then exit
;
M_40:
CMP AX,WORD PTR EXIT_CODE ;Check for exit code
JNZ M_50 ;No, so continue
PUSH DX ; Store DX
CALL GET_CURSOR ; Get cursor pos.
MOV OLD_CURSOR,DX ; Save Cursor pos.
MOV DH,25D ; Place cursor
MOV DL,79D ; off screen
CALL PUT_CURSOR ; Set cursor pos.
POP DX ; Restore DX
CALL CONFIRM_EXIT ;Yes, confirm exit
PUSH DX ; Store DX
MOV DX,OLD_CURSOR ; Restore cursor pos.
CALL PUT_CURSOR ; Set cursor pos.
POP DX ; Restore DX
JNZ M_90 ;No, so continue, don't send
JMP EXIT_MANNED ;Otherwise reset and exit
;
; Check for transfer code, if it is then ask to upload or download
;
M_50: ;
CMP AX,WORD PTR TRANSFER_CODE ; Check for transfer
JNZ M_70 ; No, so continue
PUSH DX ; Store register
CALL GET_CURSOR ; Get cursor position
MOV OLD_CURSOR,DX ; Save cursor in old cursor
MOV DH,25D ; Cursor Row = 25
MOV DL,79D ; Cursor Col = 79
CALL PUT_CURSOR ; Position Cursor
POP DX ; Restore DX register
CALL TRANSFER_TYPE ; Transfer file
CMP AX,WORD PTR CLEAR_CODE ; Transferred files ?
JNE M_60 ; Yes-reset remote scrn
CALL INIT_SERIAL ; Re-initialize serial port
CALL CLEAR_INBUFF
CALL CLEAR_OUTBUFF
JMP M_70 ; Send clear screen code
M_60:
PUSH DX ; Push DX register
MOV DX,OLD_CURSOR ; Put cursor back
CALL PUT_CURSOR ; where it was
POP DX ; Restore DX register
JMP M_90 ; Transfer complete
;
; Check for clear and resend entire screen code.
;
M_70: ;
CMP AX,WORD PTR CLEAR_CODE ; Clear screen pressed?
JNZ M_80 ; No...
PUSH AX ; Yes.... save key
CALL CLEAR_SCREEN ; Clear remote screen
POP AX ; Restore key
M_80: ;
CALL SEND_KEYSTROKE ; Send key to host
;
; Check for receive data, if available, get data and decode
;
M_90:
MOV AH,0 ;Get data from input buffer
CALL GET_BUFF_DATA
JC M_100 ;Data is there, so process
JMP M_EXIT ;No data,so loop again
M_100:
CMP BYTE PTR TYPE_TRANSFER,0FFH ;Check transfer type
JZ M_160 ;If ff, check for sync byte
CMP VID_RAM_OFFSET,0FFFFH ;Received block no. yet?
JNZ M_120 ;Yes, then process as data
MOV BYTE PTR BLOCK_DATA_COUNT,BLOCK_SIZE ;Data counter
MOV BX,BLOCK_SIZEX2 ;Get block size in bytes
MUL BX ;Block number X block size
ADD AL,TYPE_TRANSFER ;Adjust for char. or attr.
MOV VID_RAM_OFFSET,AX ;Save pointer to video RAM
JMP M_EXIT
M_120:
MOV DI,VID_RAM_OFFSET ;Point to video RAM
CMP BYTE PTR RPT_STATUS,0 ;Check the repeat flag
JZ M_140 ;Normal data transfer
MOV CL,BYTE PTR BLOCK_DATA_COUNT ;Number to repeat
XOR CH,CH
M_130:
CALL PUT_VIDEO_DATA ;Store the data in video RAM
INC DI ;Point to next video RAM loc.
LOOP M_130
JMP M_150 ;End of data, reset variables
M_140:
CALL PUT_VIDEO_DATA ;Store the data in video RAM
INC DI ;Point to next video RAM loc.
MOV VID_RAM_OFFSET,DI ;And save pointer
DEC BYTE PTR BLOCK_DATA_COUNT ;Dec. data counter
JZ M_150 ;End of data, reset variables
JMP M_EXIT ;Otherwise continue
M_150:
MOV BYTE PTR TYPE_TRANSFER,0FFH ;Reset type transfer flag
JMP M_EXIT
M_160:
CMP BYTE PTR CUR_STATUS,0 ;Check cursor status
JZ M_170 ;If 0, check for sync byte
XOR BX,BX ;Zero BX for index
MOV BL,BYTE PTR CUR_STATUS ;Use as index for cursor data

MOV DI,OFFSET CUR_STATUS ;Base address for cursor data
MOV [DI+BX],AL ;Save the register data
INC BL ;Increment count
MOV BYTE PTR CUR_STATUS,BL ;And save it
CMP BL,3 ;Check to see if we have 3 byte
JNZ M_EXIT ;No, so wait till enough data
MOV BH,0 ;Always use page one
MOV DX,WORD PTR CUR_LOW ;Get cursor position
MOV AH,2 ;Set cursor function
INT 10H
MOV BYTE PTR CUR_STATUS,0 ;Reset cursor status
JMP M_EXIT
M_170:
PUSH AX ;Save the data
AND AL,0FCH ;Mask out data type and repeat
CMP AL,0FCH ;Video data sync?
JNZ M_180 ;No, check for cursor sync
POP AX ;Restore data
PUSH AX
AND AL,1 ;Save data type
MOV TYPE_TRANSFER,AL ;Set transfer type flag
POP AX ;Restore data
AND AL,2 ;Save repeat flag
MOV RPT_STATUS,AL ;Set repeat flag
MOV VID_RAM_OFFSET,0FFFFH ;Prepare to receive block no.
JMP M_EXIT
M_180:
POP AX ;Restore data
CMP AL,0D2H ;Check for cursor data sync
JNZ M_EXIT ;If not, then throw away

MOV BYTE PTR CUR_STATUS,1 ;Set cursor status byte
M_EXIT:
JMP MANNED ;Do it again
MANNED ENDP

;----------------------------------------------------------------------
; Exit manned. The exit code is sent, interrupt vector reset, and
; the modem is hung up and reset
; Input - Nothing
; Output - DOS
; Changes - everything
;----------------------------------------------------------------------
EXIT_MANNED PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,WORD PTR EXIT_CODE ;Get the exit code
CALL SEND_KEYSTROKE ;Send the exit code
CALL CLEAR_SCREEN ;Blank the screen
MOV DX,OFFSET TERMINATE_MESS ;Display terminate call mess.
MOV AH,09H
INT 21H
XOR AL,AL ;Reset modem and exit
CALL RESET_MODEM
MOV AL,1 ;Wait a second
CALL DELAY
CALL RESET_COMM
LDS DX,DWORD PTR OLDINT_COMM ;Get original comm vector
MOV AX,SET_VEC ;and change it back
INT 21H
INT 20H ;Terminate program
EXIT_MANNED ENDP

;----------------------------------------------------------------------
; Reset comm port. Resets the comm port 8250 and disables interrupt
; driven I/O. Reset the 8259 mask.
;----------------------------------------------------------------------
RESET_COMM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH ES
PUSH DS
PUSH CS
POP DS
CMP INSTALLED_SEG,0 ;Maybe this is uninstall
JZ EM_10
MOV ES,INSTALLED_SEG ;Get location of resident prog
MOV DX,ES:COMM_PORT ;Get the comm port it used
MOV CL,BYTE PTR ES:SET_VEC ;Determine mask bit to change
JMP EM_20
EM_10:
MOV DX,CS:COMM_PORT ;Get port address
MOV CL,BYTE PTR SET_VEC ;Determine mask bit to change
EM_20:
INC DX ;Interrupt enable reg.
XOR AL,AL
OUT DX,AL ;Disable all interrupts
SUB CL,8 ; and adjust
MOV AH,1
SHL AH,CL ;Select the mask bit to set
IN AL,21H ;Get current 8259 int mask
OR AL,AH ;Set appropriate int bit
OUT 21H,AL ;And set new 8259 mask
POP DS
POP ES ;Restore segment
RET
RESET_COMM ENDP

;----------------------------------------------------------------------
; Clear screen. This routine will blank the video display
; Input - Nothing
; Output - Screen is cleared
; Changes - AX, BX, CX, DX
;----------------------------------------------------------------------
CLEAR_SCREEN PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV BX,0 ;Use video page one
MOV DX,0 ;Position cursor at row 0 col 0
MOV AH,2 ;Set cursor function
INT 10H
MOV AX,0700H ;Scroll down, clear screen
MOV BH,07H ;White on black
MOV CX,0 ;Upper left corner
MOV DH,24D ;Lower right corner
MOV DL,79D
INT 10H ;Video BIOS call
RET
CLEAR_SCREEN ENDP

;----------------------------------------------------------------------
; Display OK to exit message and wait for response
; Input - Nothing
; Output - Zero flag set - exit, zero flag reset - do not exit
; Changes - CX, SI, DI
;----------------------------------------------------------------------
CONFIRM_EXIT PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save the exit code
CE_10:
MOV AX,OUT_BUFF_HEAD ;Wait until the out buffer
CMP AX,OUT_BUFF_TAIL ; is empty
JNZ CE_10
;
; Save the existing video data in the output buffer
;
CALL SWITCH_DS_ES
MOV DI,ES:OUT_BUFF_END ;Use output buffer as temp stor.
MOV SI,0 ;Upper left corner of video
MOV CX,5 ;Transfer 5 lines
CE_20:
PUSH CX ;Save line counter
MOV CX,24D ;Transfer 24 char. per line
REPNZ MOVSW
ADD SI,112D ;Next line
POP CX ;Restore line counter
LOOP CE_20
;
; Display the ok to exit message
;
CALL SWITCH_DS_ES
MOV DI,0 ;Upper left corner of video
MOV SI,OFFSET EXIT_MESSAGE ;Exit message
MOV CX,5 ;Display 5 lines
CE_30:
PUSH CX ;Save line counter
MOV CX,24D ;Display 20 characters per line
CE_40:
LODSB
CALL PUT_VIDEO_DATA ;Display the character
MOV AL,70H ;White on black attribute
CALL PUT_VIDEO_DATA ;Store attribute
LOOP CE_40
ADD DI,112D ;Next line
POP CX ;Restore line counter
LOOP CE_30
;
; Wait for keystroke and convert to lower case
;
CE_50:
CALL GET_KEYSTROKE ;Check for keystroke
JZ CE_50 ;None there, try again
OR AL,20H ;Convert to lower case
;
; Restore the saved video data
;
MOV SI,OUT_BUFF_END ;Old video is in output buffer
MOV DI,0 ;Upper left corner of video
MOV CX,5 ;Transfer 5 lines
CE_60:
PUSH CX ;Save line counter
MOV CX,24D ;Transfer 24 char. per line
REPNZ MOVSW
ADD DI,112D ;Next line
POP CX ;Restore line counter
LOOP CE_60
CMP AL,'y' ;Check for yes, all others no
POP AX ;Restore exit code
RET
CONFIRM_EXIT ENDP

;---------------------------------------------------------------
; Draw the box and display the text in it.
; Input - SI points to the text strings
; Output - Current video stored in output buffer, text displayed
; in box.
; Changes - nothing
;---------------------------------------------------------------
DISPLAY_BOX PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save registers
PUSH BX
PUSH CX
PUSH DI
PUSH SI
;
; Save the current video area in the output buffer
;
CALL SWITCH_DS_ES
MOV DI,ES:OUT_BUFF_END ;Use output buffer as temp stor.
XOR SI,SI ;Upper left of video
MOV CX,12 ;Save 12 lines
DB_10:
PUSH CX ;Save the line counter
MOV CX,51 ;Save 51 columns
REPNZ MOVSW
ADD SI,58 ;Adjust to start of next line
POP CX ;Restore line counter
LOOP DB_10
CALL SWITCH_DS_ES
;
; Blank that area of the screen
;
XOR DI,DI ;Upper left of video
MOV CX,12 ;Blank 12 lines
DB_20:
PUSH CX ;Save line counter
MOV CX,51 ;Blank 51 characters
DB_30:
MOV AL,SPACE ;Space character
CALL PUT_VIDEO_DATA ;Display the character
MOV AL,70H ;Attribute
CALL PUT_VIDEO_DATA
LOOP DB_30
ADD DI,58 ;Adjust to start of next line
POP CX ;Restore line counter
LOOP DB_20
;
; Draw the box
;
MOV DI,166 ;First line of box
CALL DRAW_LINE ;Display the horizontal line
MOV DI,486 ;Second line of box
CALL DRAW_LINE ;Display the horizontal line
MOV DI,1286 ;Third line of box
CALL DRAW_LINE ;Display the horizontal line
MOV DI,1606 ;Fourth line of box
CALL DRAW_LINE ;Display the horizontal line
MOV BX,OFFSET BOX_CHAR ;Get a pointer to box corners
MOV CX,20 ;Corners and vertical lines
DB_40:
MOV DI,[BX] ;Get offset to char location
ADD BX,2 ;Point to character
MOV AL,[BX] ;Get the character
INC BX ;Next corner
CALL PUT_VIDEO_DATA
LOOP DB_40
;
; Draw the text in the box
;
POP SI ;Get pointer to text
PUSH SI
MOV DI,348 ;Location for title line
CALL DRAW_TEXT ;Display the line of text
MOV DI,648 ;Next line of text
CALL DRAW_TEXT ;Display the line of text
MOV DI,808 ;Next line of text
CALL DRAW_TEXT ;Display the line of text
MOV DI,968 ;Next line of text
CALL DRAW_TEXT ;Display the line of text
MOV DI,1128 ;Next line of text
CALL DRAW_TEXT ;Display the line of text
MOV DI,1458 ;Next line of text
MOV SI,OFFSET PRESS_ESC ;Display the press ESC to exit
CALL DRAW_TEXT ;Display the line of text
POP SI ;Restore registers
POP DI
POP CX
POP BX
POP AX
RET
DISPLAY_BOX ENDP

;---------------------------------------------------------------
; Draw horizontal line
; Input ES:DI points to location to start drawing the line
; Output - nothing
; Changes - AL, CX, DI
;---------------------------------------------------------------
DRAW_LINE PROC NEAR
MOV CX,45 ;45 Columns in line
MOV AL,'Í' ;Line draw character
DL_10:
CALL PUT_VIDEO_DATA ;Display the character
INC DI ;Adjust past attribute
LOOP DL_10
RET
DRAW_LINE ENDP

;---------------------------------------------------------------
; Display line of text
; Input - SI points to text DI points to video location
; Output - line of text is displayed on the screen
; Changes - AL, SI, DI
;---------------------------------------------------------------
DRAW_TEXT PROC NEAR
LODSB ;Get the text
CMP AL,0 ;Is this the end of the string
JZ DT_10 ;Yes
CALL PUT_VIDEO_DATA ;Display the character
INC DI
JMP DRAW_TEXT
DT_10:
RET
DRAW_TEXT ENDP

;---------------------------------------------------------------
; Restore the video saved to display a box
; Input - nothing
; Output - nothing
; Changes - nothing
;---------------------------------------------------------------
RESTORE_BOX PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save registers
PUSH CX
PUSH DI
PUSH SI
MOV SI,OUT_BUFF_END ;Old video in output buffer
XOR DI,DI ;Upper left corner of screen
MOV CX,12 ;Line counter
RB_10:
PUSH CX ;Save line counter
MOV CX,51 ;51 characters per line
REPNZ MOVSW
ADD DI,58 ;Next line
POP CX ;Restore line counter
LOOP RB_10
POP SI ;Restore registers
POP DI
POP CX
POP AX
RET
RESTORE_BOX ENDP


;---------------------------------------------------------------
; Display (U)pload or (D)ownload message and wait for response
; Input - Nothing
; Output -
; Changes - CX, SI, DI
;---------------------------------------------------------------
TRANSFER_TYPE PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,OUT_BUFF_HEAD ;Wait until the out
CMP AX,OUT_BUFF_TAIL ;buffer is empty
JNZ TRANSFER_TYPE
;
; Display the (U)pload or (D)ownload message
;
MOV SI,OFFSET TRANSFER_MESS ;Transfer message
CALL DISPLAY_BOX
;
; Wait for a keystroke and convert to lower case
;
TT_10:
CALL GET_KEYSTROKE ;Check for a keystroke
JZ TT_10 ;None there, try again
OR AL,20H
CALL RESTORE_BOX
CMP AL,'u' ;Upload Requested ?
JZ TT_20 ;Yes
JMP TT_90 ;No, so continue
TT_20: ;UPLOAD CODING HERE
MOV LAST_KEY,0 ;Clear last key pressed
CALL UPLOAD ;Ask for upload file
CMP LAST_KEY,ESC_KEY ;ESC pressed ?
JNZ TT_30 ;No
JMP TT_180 ;Yes - return to PCREMOTE
TT_30:
MOV LAST_KEY,0 ;Clear last key pressed
MOV SI,OFFSET UP_PATH_MESS ; Path message for upload
CALL PATH ;Get destination Path
CMP LAST_KEY,ESC_KEY ;ESC pressed ?
JNZ TT_40 ;No
JMP TT_180 ;Yes - return to PCREMOTE
TT_40:
MOV LAST_KEY,0 ;Clear last key pressed
CALL MAKE_UP_COM ;Create ZCOPY EXEC com
CLD ;Forward direction ptr.
MOV SI,OFFSET ZCOPY ;DS:SI -- ZCOPY variable
CALL SEND_STRING ;Send ZCOPY command line
MOV SI,OFFSET HOST_COM ;DS:SI -- COM variable
CALL SEND_STRING ;Send COM to host
MOV AL,SPACE ;AL = SPACE
CALL GET_SEND_CODE ;Get & send scan-code
MOV SI,OFFSET PATH_STRING ;DS:SI -- Path variable
MOV CX,PATH_LENGTH ;CX = Length of PATH
CMP CX,0 ;Default Path ?
JE TT_60 ;Yes...
TT_50: ;No...
LODSB ;AL = next character
CALL GET_SEND_CODE ;Get & send scan-code
LOOP TT_50 ;All characters sent ?
MOV AL,SPACE ;AL = SPACE
CALL GET_SEND_CODE ;Get & send scan-code
TT_60:
MOV SI,OFFSET PARM_STRING ;DS:SI -- PARM variable
MOV CX,9D
TT_70:
LODSB ;AL = Next character
CALL GET_SEND_CODE ;Get & send scan-code
LOOP TT_70 ;All characters sent ?
MOV AL,CR ;AL = carriage return
CALL GET_SEND_CODE ;Get & send scan-code
TT_80:
MOV AX,OUT_BUFF_HEAD ;Get the buff head ptr
CMP AX,OUT_BUFF_TAIL ;Test for data
JNZ TT_80 ;Wait until buffer empty
MOV EXEC_VAL,3D ;Tell EXEC to upload
CALL EXEC_ZCOPY ;Shell to DOS & EXEC ZCOPY
JMP TT_170 ;Transfer complete, exit
TT_90:
CMP AL,'d' ;Download Requested ?
JZ TT_100 ;Yes
JMP TT_180 ;No so return to PCREMOTE
TT_100: ;DOWNLOAD CODING HERE
MOV LAST_KEY,0 ;Clear last key pressed
CALL DOWNLOAD ;Ask for filename
CMP LAST_KEY,ESC_KEY ;ESC pressed ?
JNZ TT_110 ;No
JMP TT_180 ;Yes, return to PC_REMOTE
TT_110:
MOV LAST_KEY,0 ;Clear last key pressed
MOV SI,OFFSET DN_PATH_MESS ; Path message for download
CALL PATH ;Get destination Path
CMP LAST_KEY,ESC_KEY ;ESC pressed ?
JNZ TT_120 ;No
JMP TT_180 ;Yes, return to PC_REMOTE
TT_120:
MOV LAST_KEY,0 ;Clear last key pressed
CALL MAKE_DN_COM ;Create ZCOPY EXEC com
CLD ;Forward direction flag
MOV SI,OFFSET ZCOPY ;DS:SI -- ZCOPY variable
CALL SEND_STRING ;Send ZCOPY command
MOV SI,OFFSET UPDN_STRING ;DS:SI -- file variable
MOV CX,DN_LENGTH ;CX = length of filename
CMP CX,0 ;Default Filename
JE TT_140 ;Yes...
TT_130: ;No...
LODSB ;AL = next character
CALL GET_SEND_CODE ;Get & send scan-code
LOOP TT_130 ;All characters sent ?
MOV AL,SPACE ;AL = SPACE
CALL GET_SEND_CODE ;Get & send scan-code
TT_140:
MOV SI,OFFSET HOST_COM ;DS:SI -- Com variable
CALL SEND_STRING ;Send COM variable
MOV AL,SPACE ;AL = SPACE
CALL GET_SEND_CODE ;Get & send scan-code
MOV SI,OFFSET PARM_STRING ;DS:SI -- PARM variable
MOV CX,9D
TT_150:
LODSB ;AL = next character
CALL GET_SEND_CODE ;Get & send scan-code
LOOP TT_150 ;All characters sent ?
MOV AL,CR ;AL = carriage return
CALL GET_SEND_CODE ;Get & send scan-code
TT_160:
MOV AX,OUT_BUFF_HEAD ;Get buffer head ptr
CMP AX,OUT_BUFF_TAIL ;Test for data in buff
JNZ TT_160 ;Wait for buffer empty
MOV EXEC_VAL,2D ;Tell EXEC to download
CALL EXEC_ZCOPY ;Shell to DOS - EXEC ZCOPY
JMP TT_170
TT_170:
MOV AX,WORD PTR CLEAR_CODE ;Tell the caller to clear
JMP TT_190 ;the remote's screen
TT_180:
MOV AX,WORD PTR TRANSFER_CODE ;Tell the caller that
TT_190: ;screen doesn't need to
RET ;be rewritten.
TRANSFER_TYPE ENDP

;------------------------------------------------------------
; Display upload message then wait for filename(s).
; Input - Nothing
; Output -
; Changes - CX,SI,DI
;
;------------------------------------------------------------
UPLOAD PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,OUT_BUFF_HEAD ;Wait until the out
CMP AX,OUT_BUFF_TAIL ;buffer is empty
JNZ UPLOAD
MOV SI,OFFSET UPLOAD_MESS ;Upload message
CALL DISPLAY_BOX
PUSH DX ;Save register
MOV DH,7 ;Put cursor in row 8
MOV DL,5 ;Put cursor in column 6
CALL PUT_CURSOR ;Set Cursor
POP DX ;Restore register
PUSH ES ;Store ES
PUSH DS ;Store DS
POP ES ;ES = DS
MOV DI,OFFSET UPDN_STRING ;ES:DI -> UP_string
MOV CX,42D ;MAX length = 42
CALL GETSTRING ;Receive file
MOV LAST_KEY,AL ;Save last key
MOV UP_LENGTH,CX ;Save length
CMP UP_LENGTH,0 ;Any file selected ?
JNE UP_10 ;Yes
MOV LAST_KEY,ESC_KEY ;No...Return (esc)
UP_10:
POP ES ;Restore ES
CALL RESTORE_BOX
RET
UPLOAD ENDP

;------------------------------------------------------------
; Display download message then wait for response.
; Input - Nothing
; Output -
; Changes - CX,SI,DI
;------------------------------------------------------------
DOWNLOAD PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,OUT_BUFF_HEAD ;Wait until the
CMP AX,OUT_BUFF_TAIL ;out buffer is empty
JNZ DOWNLOAD
MOV SI,OFFSET DOWNLOAD_MESS ;Download message
CALL DISPLAY_BOX
PUSH DX ;Save DX register
MOV DH,7 ;Put cursor in row 8
MOV DL,5 ;Put cursor in column 6
CALL PUT_CURSOR ;Put cursor there
POP DX ;Restore register
PUSH ES ;Store ES
PUSH DS ;Store DS
POP ES ;ES = DS
MOV DI,OFFSET UPDN_STRING ;ES:DI -> DN_string
MOV CX,42D ;MAX length = 42
CALL GETSTRING ;Get filename
MOV LAST_KEY,AL ;Save last key
MOV DN_LENGTH,CX ;Save length
CMP DN_LENGTH,0 ;File selected ?
JNE DN_10 ;Yes
MOV LAST_KEY,ESC_KEY ;No... return (esc)
DN_10:
POP ES ;Restore ES
CALL RESTORE_BOX
RET
DOWNLOAD ENDP

;----------------------------------------------------------------------
; Display the path request screen & prompt user to input path name.
; Input - SI points to display message for transfer
; Output - Path Message Displayed on screen.
; Changes - CX,SI,DI
;----------------------------------------------------------------------
PATH PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
MOV AX,OUT_BUFF_HEAD ; Wait until the
CMP AX,OUT_BUFF_TAIL ; out buffer is empty
JNZ PATH
CALL DISPLAY_BOX
PUSH DX ; Save DX Register
MOV DH,7 ; Put cursor in row 8
MOV DL,5 ; Put cursor in col 6
CALL PUT_CURSOR ; Put cursor there
POP DX ; Restore DX
PUSH ES ; Store ES
PUSH DS ; Store DS
POP ES ; ES = DS
MOV DI,OFFSET PATH_STRING ; ES:DI -> Path_string
MOV CX,42D ; Max length = 42
CALL GETSTRING ; Get path
MOV LAST_KEY,AL ; Save last key
MOV PATH_LENGTH,CX ; Save length
;
; Validate and correct (modify) pathname if ZCOPY will not accept the
; string entered for the pathname.
; i.e.- C:\ is acceptable whereas C: is unacceptable
; C:\DOS is acceptable whereas C:\DOS\ is unacceptable.
;
CMP LAST_KEY,ESC_KEY ; ESC key pressed ?
JE PATH_20 ; Yes, so skip pathname edit
MOV SI,OFFSET PATH_STRING ; SI->1st char of path name
ADD SI,PATH_LENGTH ; SI->1 char after pathname
DEC SI ; SI->Last char of pathname
LODSB ; Pick off last char
CMP AL,':' ; Is last char a colon
JNE PATH_10 ; No, check other chars.
PUSH SI ; Save SI into DI
POP DI ; DI = SI
MOV AL,'\' ; Add backslash to pathname
STOSB ; Store the backslash
INC PATH_LENGTH ; Add one to length of path
JMP PATH_20 ; Editting is complete
PATH_10:
CMP AL,'\' ; Is last char a backslash
JNE PATH_20 ; No, skip pathname edit
DEC SI ; SI -> Last char again
DEC SI ; SI -> 2nd last char
LODSB ; Pick off 2nd last char
CMP AL,':' ; 2nd last char a colon ?
JE PATH_20 ; Yes, so skip edit.
PUSH SI ; Store SI for DI
POP DI ; DI-> Last char of pathname
MOV AL,SPACE ; Clear last char of path
STOSB ; Store blank
DEC PATH_LENGTH ; Subtract 1 from path length
PATH_20:
POP ES
CALL RESTORE_BOX
RET
PATH ENDP

;----------------------------------------------------------------------
; Create the Upload command string that the REMOTE computer EXECS
;----------------------------------------------------------------------
MAKE_UP_COM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Store registers
PUSH CX
PUSH DS
PUSH ES
PUSH DI
PUSH SI
PUSHF
PUSH CS
PUSH CS
POP DS ;DS -> CS
POP ES ;ES -> CS
CLD ;Forward Direction Flag
MOV DI,OFFSET UPDNLOAD_COM ;DI -> 1st char of upload com
ADD DI,10D ;DI -> 10th char of upl. com
MOV AL,SPACE ;Reset com string w/ 89
MOV CX,89D ; blanks
MUC_10:
STOSB ;Fill sting with AL
LOOP MUC_10 ;89 chars complete ?
SUB DI,89D ;DI -> 10th char of upl. com
MOV SI,OFFSET UPDN_STRING ;SI -> 1st char of upl. file
MOV CX,UP_LENGTH ;CX = length of string
MUC_20:
LODSB ;AL = char from filename
STOSB ;Upl. com gets char
LOOP MUC_20 ;All chars copied ?
MOV AL,SPACE ;Place a blank char at the
STOSB ; end of filename
MOV SI,OFFSET REMOTE_COM ;SI -> 1st char of comm var.
MOV CX,4D ;Pick off 4 characters
MUC_30:
LODSB ;Pick off 1 char
STOSB ;Place in upl. com string
LOOP MUC_30 ;Entire string done ?
MOV AL,SPACE ;Place a blank at end of comm
STOSB ; string in upl. com string
MOV SI,OFFSET PARM_STRING ;SI -> 1st char of parm
ADD SI,3D ;SI -> 4th char of parm
MOV CX,5D ;Pick off 5 chars from parm
MUC_40:
LODSB ;Pick off 1 char
STOSB ;Place char in upl. com
LOOP MUC_40 ;6 characters done yet ?
MUC_50:
MOV AL,CR ;Carriage return
STOSB
MOV AL,0 ;0 to end string
STOSB
;
; Length of UPDNLOAD_COM must be inserted in the first character of the
; string ("?/c zcopy..."), therefore the length must be determined.
;
MOV SI,OFFSET UPDNLOAD_COM ;SI-> 1st char of string
CALL GET_STRING_LEN ;CX = Length of string
MOV AL,CL ;Ready AL for STOSB instruct.
MOV DI,OFFSET UPDNLOAD_COM ;DI-> 1st char of string
STOSB ;Store AL in string
MUC_RET:
POPF ;Restore registers & flags
POP SI
POP DI
POP ES
POP DS
POP CX
POP AX
RET
MAKE_UP_COM ENDP

;----------------------------------------------------------------------
; Creates the download command string that the REMOTE computer EXECS
;----------------------------------------------------------------------
MAKE_DN_COM PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Store registers
PUSH CX
PUSH DS
PUSH ES
PUSH DI
PUSH SI
PUSHF
PUSH CS
PUSH CS
POP DS ;DS -> CS
POP ES ;ES -> CS
CLD ;Forward direction flag
MOV DI,OFFSET UPDNLOAD_COM ;DI -> Download command string
ADD DI,10D ;DI -> 11th char of command
MOV AL,SPACE ;Reset command w/ blanks
MOV CX,89D ; 89 of them...
MDC_10:
STOSB ;Store the blank in command
LOOP MDC_10 ;56 chars. complete ?
SUB DI,89D ;Reset DI to 11th char.
MOV SI,OFFSET REMOTE_COM ;SI -> (C)OMx
MOV CX,4D ;Transfer COMx to com. line
MDC_20:
LODSB ;Pick off a char into AL
STOSB ;Move AL into command
LOOP MDC_20 ;All 4 chars copied ?
MOV AL,SPACE ;Move a blank in after COMx
STOSB ;Store blank
CMP PATH_LENGTH,0 ;Does target directory exist?
JE MDC_40 ;No, skip target dir. copy
MOV SI,OFFSET PATH_STRING ;Yes, SI -> 1st char of dir.
MOV CX,PATH_LENGTH ;CX = length of path string
MDC_30:
LODSB ;Pick off one char
STOSB ;Store char in command line
LOOP MDC_30 ;All chars. copied ?
MOV AL,SPACE ;Move in a blank after dir.
STOSB ;Store blank in line
MDC_40:
MOV SI,OFFSET PARM_STRING ;SI -> 1st char of parm
ADD SI,3D ;SI -> 4th char of parm
MOV CX,5D ;Move in last 5 characters
MDC_50:
LODSB ;Pick off one char.
STOSB ;Copy char in command
LOOP MDC_50 ;All 6 chars copied ?
MDC_60:
MOV AL,CR ;Carriage return
STOSB
MOV AL,0 ;End string with a 0
STOSB
;
; Length of download_com must be inserted in the first character of the
; string ("?/c zcopy..."), therefore the length must be determined.
;
MOV SI,OFFSET UPDNLOAD_COM ;SI-> 1st char of string
CALL GET_STRING_LEN ;CX = Length of string
MOV AL,CL ;Ready AL for STOSB instruct.
MOV DI,OFFSET UPDNLOAD_COM ;DI-> 1st char of string
STOSB ;Store al in string
MDC_RET:
POPF ;Restore registers
POP SI
POP DI
POP ES
POP DS
POP CX
POP AX
RET
MAKE_DN_COM ENDP

;----------------------------------------------------------------------
; Get string length determines the length of the string that DS:SI is
; pointing to. The length is returned in the CX register.
; Input - DS:SI points to the first character of the associated string.
; Output - CX = Length of the string.
; Changes - CX
;----------------------------------------------------------------------
GET_STRING_LEN PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Store registers
PUSH SI
PUSHF
CLD ;Go forward in variables
XOR CX,CX ;Reset CX (counter) to zero
GSL_10:
LODSB ;Pick off one character
OR AL,AL ;Is the character a zero ?
JZ GSL_20 ;Yes, return w/ length = CX
INC CX ;No... bump character counter
JMP GSL_10 ;Get another character
GSL_20:
POPF ;Restore registers
POP SI
POP AX
RET
GET_STRING_LEN ENDP

;----------------------------------------------------------------------
; Get current cursor position
; Input - Nothing
; Output - DX contains current cursor position
; Changes - DX
;----------------------------------------------------------------------
GET_CURSOR PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Push registers
PUSH BX
MOV BH,0 ;First page
MOV AH,3
INT 10H ;DOS call
POP BX ;Restore registers
POP AX
RET
GET_CURSOR ENDP

;--------------------------------------------------------------------
; Places cursor in the DX position.
; Input - DX
; Output - Cursor placement
; Changes - Nothing
;--------------------------------------------------------------------
PUT_CURSOR PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Push registers
PUSH BX
MOV AH,2
MOV BH,0 ;First page
INT 10H ;DOS call
POP BX ;Restore registers
POP AX
RET
PUT_CURSOR ENDP

;--------------------------------------------------------------------
; Inputs a character string from the keyboard. Characters will be
; accepted until the user presses the return, ESC or CX chars. have
; been received from keyboard.
; Input - ES:DI Points to the buffer that is to receive the string.
; CX Contains the length of the input buffer.
; Output -CX Contains the actual length of the character string.
; AX Contains the last keystroke
; The specified buffer contains the character string that was
; entered.
;---------------------------------------------------------------------
GETSTRING PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH BX
PUSH DI ;Store Registers
PUSH DX
PUSHF
CLD ;Direction Flag Forward
XOR BH,BH ;Screen page 0
XOR DX,DX ;Reset character counter
GS_10:
XOR AH,AH ;Function 0 -INT16H- Read key
INT 16H ;Read character from keys
CMP AL,CR ;Carriage Return ?
JE GS_30 ;Yes
CMP AL,ESC_KEY ;Escape key ?
JE GS_30 ;Yes
CMP AL,BS ;Back space ?
JNE GS_14 ;No
CMP DX,0 ;Yes, but 1st char. ?
JE GS_10 ;Yes, so skip BS & get a key
GS_14:
STOSB ;No... Store Character
INC DX ;Bump character counter
MOV AH,14 ;Function 14 -INT10H-
INT 10H ;Display character
CMP AL,BS ;Was last char a backspace ?
JNE GS_20 ;No
GS_15:
SUB DX,2 ;Decrement char count twice
SUB DI,2 ;Move PTR back 2 spaces
ADD CX,2 ;Move in two spaces
MOV AH,14 ;Display character interrupt
MOV AL,SPACE ;Space
INT 10H ;Display character
MOV AH,14 ;Display character interrupt
MOV AL,BS ;Backspace
INT 10H ;Display character
GS_20:
LOOP GS_10 ;Size = # of characters
GS_30:
MOV CX,DX ;Save # of chars. in CX
POPF ;Restore Registers
POP DX
POP DI
POP BX
RET
GETSTRING ENDP

;------------------------------------------------------------
; Input - DS:SI must point to the 1st char of the string to be
; sent to the host computer.
; Output - The entire string is sent to the host computer until
; a ZERO (end of string) is found.
; Changes - SI,AL
;------------------------------------------------------------
SEND_STRING PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSHF ;Store Direction Flag
CLD
SZ_10:
LODSB ;Get AL
CMP AL,0 ;Done with string ?
JE SZ_EXIT ;Yes...
CALL GET_SEND_CODE ;No...get & send scan
JMP SZ_10 ;Repeat until done
SZ_EXIT:
POPF ;Restore Direction Flag
RET
SEND_STRING ENDP

;------------------------------------------------------------
; Input - AL contains the ASCII code of the character to be
; sent to the host computer.
; Output - AX is sent to the host computer where
; AL is ASCII code
; AH is the associated scan code for the ASCII code
; Changes - Nothing
;------------------------------------------------------------
GET_SEND_CODE PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Store Registers
PUSH BX
PUSH CX
PUSH SI
PUSHF
CMP AL,'a' ;Small Case letter ?
JL GSC_10 ;No ....
CMP AL,'z'
JG GSC_10 ;No ....
SUB AL,32D ;Yes ....
GSC_10:
MOV BL,AL ;Save ASCII code
MOV CX,0 ;Reset counter into var.
MOV SI,OFFSET ASCII_CODE ;DS:SI -> ASCII variable
CLD ;Forward
GSC_20:
CMP CX,70D ;Invalid ASCII ?
JE GSC_EXIT ;Yes...
INC CX ;Bump counter
LODSB ;AL = next char in var.
CMP AL,BL ;Find correct offset ?
JNE GSC_20 ;No...
GSC_30: ;Yes... so bump that far
MOV SI,OFFSET SCAN_CODE ;into scan_code var and
CLD ;pick off that scan_code.
GSC_40:
LODSB
LOOP GSC_40
GSC_50:
MOV AH,AL ;AH = scan-code
MOV AL,BL ;AL = ASCII code
CALL SEND_KEYSTROKE ;Send AX
GSC_EXIT:
POPF ;Restore registers
POP SI
POP CX
POP BX
POP AX
RET
GET_SEND_CODE ENDP

;-----------------------------------------------------------------
; Execute ZCOPY (upload/download) or shell to DOS and return to
; the calling program.
;-----------------------------------------------------------------
EXEC_ZCOPY PROC NEAR
CMP BYTE PTR NOEXEC,0FFH
JNZ EZ_10
JMP EZ_EXIT
EZ_10:
PUSH AX ;Store all registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
PUSH CS
PUSH CS
POP DS
POP ES
CMP EXEC_VAL,1D ;Shelling to DOS ?
JE EZ_20 ;Yes, display shell prompt
CMP EXEC_VAL,2D ;Downloading from host ?
JE EZ_30 ;Yes, display transfer prompt
CMP EXEC_VAL,3D ;Uploading from host ?
JE EZ_30 ;Yes, display transfer prompt
JMP EZ_60 ;EXEC_VAL has wrong value !
EZ_20:
MOV AX,OFFSET NUL_TAIL ;Shell to DOS
MOV SI,OFFSET SHELL_PROMPT ;Notify user of shell
JMP EZ_40
EZ_30:
MOV AX,OFFSET UPDNLOAD_COM ;Uploading
MOV SI,OFFSET TRANSFER_PROMPT ;Notify transferring
EZ_40:
MOV PAR_BLK+2,AX ;EXEC
CALL CLEAR_SCREEN ;Clear remote screen
CALL GET_STRING_LEN ;Get string length of DS:SI
PUSH ES ;Store registers & flags
PUSH DI
PUSHF ;Store flag
MOV AX,VIDEO_SEGMENT ;AX->1st char of video segment
MOV ES,AX ;ES->1st char of video segment
XOR DI,DI ;ES:DI-> 1st char of video seg
CLD ;Forward in video segment
EZ_50:
LODSB ;Pick off char in string
CALL PUT_VIDEO_DATA ; and place in video segment
MOV AL,70H ;Place reverse video
CALL PUT_VIDEO_DATA ; in video segment
LOOP EZ_50 ;All characters displayed ?
POPF ;Restore flags
POP DI ;Restore registers
POP ES
MOV DH,2D ;Row 2
XOR DL,DL ;Col 0
CALL PUT_CURSOR ;Place cursor on screen
CALL SET_ENV
CMP BYTE PTR NOEXEC,0FFH
JZ EZ_60
PUSH DS
PUSH ES
MOV OLD_SS,SS
MOV OLD_SP,SP
MOV BX,OFFSET PAR_BLK
LDS DX,CS:COMSPC
MOV AX,4B00H
INT 21H
;
; return from DOS exec
;
MOV SS,CS:OLD_SS
MOV SP,CS:OLD_SP
MOV ES,CS:PAR_BLK
MOV AH,49H
INT 21H
POP ES
POP DS
EZ_60:
POP BP
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
EZ_EXIT:
RET
EXEC_ZCOPY ENDP

;-----------------------------------------------------------------------
; Find the string pointed to by DS:SI in the segment pointed to by ES
; ES:0000 is assumed to contain null terminated stings.
; Input - ES points to environment segment
; SI points to search string
; Output - Zero set - could not locate string
; Zero reset - found the string, ES:DI points to end of string
; found. DI is also count of characters up to
; end of string.
; Changes -
;-----------------------------------------------------------------------
FIND_SI PROC NEAR
XOR DI,DI ;Zero environment pointer
FNDSI1: MOV BX,SI ;Save the source string
CMP BYTE PTR ES:[DI],00 ;End of environment space?
JNZ FNDSI2 ; no
RET ; yes, return with zero set
FNDSI2:
MOV AL,[BX] ;Get source string character
OR AL,AL ;End of string?
JZ FNDSI4 ; yes
CMP AL,ES:[DI] ; no, Same as environment char?
JNZ FNDSI3 ; no, string doesn't match
INC BX ; yes, check the next char
INC DI ; in both strings
JMP FNDSI2
FNDSI3:
XOR AL,AL ;Null string terminator
MOV CX,-0001 ;Make sure to find string end
CLD
REPNZ SCASB ;Find the end of this string
JMP FNDSI1 ;Test the next string
FNDSI4:
XOR AX,AX ;Located the string
INC AX ;Reset the zero flag
RET
FIND_SI ENDP

;-----------------------------------------------------------------
; SET NEW ENVIRONMENT
;
;-----------------------------------------------------------------

SET_ENV PROC NEAR
PUSH DS
PUSH ES
MOV ES,DS:[002CH] ;Environment segment pointer
MOV SI,OFFSET PMTSTR ;Pointer to "PROMPT="
CALL FIND_SI ;Is "PROMPT=" in environment?
JNZ GOTPMT ; yes
XOR DI,DI ; no, set length to 0
;
; DI = number of characters in environment to end of "PROMPT=" string.
;
GOTPMT:
MOV BX,DI ;Save the count
XOR DI,DI
MOV AX,DI
MOV CX,0FFFFH
CLD
REPNZ SCASW ;Locate the end of the string
MOV AX,PMTLGT
ADD AX,DI
CMP BX,0000
JNZ PMTHER
ADD AX,(OFFSET PCRSTR) - (OFFSET DFTPMT)
PMTHER:
MOV CL,04
SHR AX,CL
INC AX
SUB DI,BX
PUSH DI

PUSH BX
MOV BX,AX ;number of paragraphs request
MOV AH,48H ;malloc
INT 21H
JNC NOERR
MOV BYTE PTR NOEXEC,0FFH

NOERR: MOV PAR_BLK,AX ;segment to malloc
MOV ES,AX ;segment to malloc
XOR DI,DI
CLD
POP CX
CMP CX,0000
JNZ DOITAL
MOV CX,PMTLGT
MOV WORD PTR PMTLGT,0000
ADD CX,(OFFSET PCRSTR) - (OFFSET DFTPMT)
MOV SI,OFFSET DFTPMT
REP MOVSB
MOV SI,OFFSET GODPMT
MOV CX,(OFFSET PMTSTR) - (OFFSET GODPMT)
REP MOVSB
;
; FROM ADD TO USERS PROMPT
;
DOITAL:
MOV AX,DS:[002CH]
MOV DS,AX
XOR SI,SI
JCXZ PMTDON
REP MOVSB
;
; IF NO PROMPT=', NEW PROMPT STORED AND WHOLE ENV STORED NEXT
;
PMTDON:
PUSH DS
PUSH SI
PUSH CS
POP DS
MOV SI,OFFSET PCRSTR
MOV CX,PMTLGT
JCXZ NOPMT
REP MOVSB
;
; ANY PROMPT STORED?
;
NOPMT:
POP SI
POP DS
POP CX
REP MOVSB
POP ES
POP DS
RET
SET_ENV ENDP

;----------------------------------------------------------------------
GET_COMSPEC PROC NEAR
PUSH ES
MOV PAR_BLK+04,DS
MOV AX,DS:[002CH]
MOV PAR_BLK,AX
MOV ES,AX
MOV SI,OFFSET COM_VAR
CALL FIND_SI
MOV CS:COM_SEG,ES
MOV CS:COM_OFS,DI
JNZ NO_FND
MOV BYTE PTR NOEXEC,0FFH
NO_FND:
POP ES
RET
GET_COMSPEC ENDP

;----------------------------------------------------------------------
; Switch ES and DS
; Input - Nothing
; Output - ES is in DS and DS is in ES
; Changes - ES, DS
;----------------------------------------------------------------------
SWITCH_DS_ES PROC NEAR
PUSH ES ;Push ES
PUSH DS ;Push DS
POP ES ;Switch ES
POP DS ;with DS
RET
SWITCH_DS_ES ENDP

;----------------------------------------------------------------------
; Put the keystroke data into the output buffer to be sent to unattended
; system
; Input - AX - keystroke data
; Output - Nothing
; Changes - Nothing
;----------------------------------------------------------------------
SEND_KEYSTROKE PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:NOTHING,SS:NOTHING
PUSH AX ;Save it
MOV AH,1 ;Use the output buffer
MOV AL,0FEH ;Sync byte, to expect key data
CALL PUT_BUFF_DATA ;Put AL into the output buffer
POP AX ;Get AL back
PUSH AX ;Save AH
MOV AH,1 ;Use the output buffer
CALL PUT_BUFF_DATA ;Put ASCII code in out buffer
POP AX ;Get AH back
PUSH AX ;Save keystroke
MOV AL,AH ;Move it to AL
MOV AH,1 ;Use the output buffer
CALL PUT_BUFF_DATA ;Put scan code in out buffer
POP AX ;Restore keystroke
RET
SEND_KEYSTROKE ENDP

;======================================================================
LAST_BYTE EQU $
CSEG ENDS
END START



  3 Responses to “Category : Files from Magazines
Archive   : VOL11N02.ZIP
Filename : PCREMOT2.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/