home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
modem
/
byepc300.arc
/
BYE300.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-11-05
|
100KB
|
3,123 lines
PAGE 62,132
TITLE BYE-PC Version 3.00 - Resident Modem Driver
;-----------------------------------------------------------------------
; * * * BYE-PC Resident Modem Driver * * *
;
; (c) Copyright 1986, 1987 MCODE Software
; by
; R. E. Starr, Jr.
; 3444 Dresden Dr.
; Montgomery Al. 36111
; (205)244-7230
;-----------------------------------------------------------------------
;
; Many thanks to the following people for
; making this project possible:
;
; - William C. Bryan -
;
; Special thanks to Bill for the hardware timing functions
; and guidance during initial project development.
;
; - Greg Gross -
;
; Special thanks to Greg for the endless hours of online de-
; bugging. Almost all of the online debugging was performed by
; Greg.
;
;-----------------------------------------------------------------------
;-------------------- General Equates Section --------------------------
;-----------------------------------------------------------------------
;
PGM_VER equ 3 ;BYE-PC version number
PGM_REV equ 0 ;BYE-PC revision level
;
; -- general purpose equates --
;
YES equ 1 ;boolean defines
NO equ 0
ON equ 1
OFF equ 0
EOF equ -1 ;end of file
NULL equ 0 ;zero value
CR equ 0Dh ;carriage return
LF equ 0Ah ;line feed
ESK equ 1Bh ;escape (ESC is an instruction)
BKSPC equ 08h
BELL equ 7 ;ring bell through tty print
CTRL_C equ 03h ;remote break control key
CTRL_S equ 13h ;remote pause key
;
; -- keyboard scan codes --
;
ALT_Q equ 16 ;top row keys
ALT_W equ 17
ALT_E equ 18
ALT_R equ 19
ALT_T equ 20
ALT_Y equ 21
ALT_U equ 22
ALT_I equ 23
ALT_O equ 24
ALT_P equ 25
ALT_A equ 30 ;middle row keys
ALT_S equ 31
ALT_D equ 32
ALT_F equ 33
ALT_G equ 34
ALT_H equ 35
ALT_J equ 36
ALT_K equ 37
ALT_L equ 38
ALT_Z equ 44 ;bottom row keys
ALT_X equ 45
ALT_C equ 46
ALT_V equ 47
ALT_B equ 48
ALT_N equ 49
ALT_M equ 50
;
; -- baud rate initialization for 8255 --
;
BPS300 equ 0180h ;port initialize (300 bps)
BPS600 equ 00C0h ;port initialize (600 bps)
BPS1200 equ 0060h ;port initialize (1200 bps)
BPS2400 equ 0030h ;port initialize (2400 bps)
BPS3600 equ 0020h ;port initialize (3600 bps)
BPS4800 equ 0018h ;port initialize (4800 bps)
BPS7200 equ 0010h ;port initialize (7200 bps)
BPS9600 equ 000Ch ;port initialize (9600 bps)
INT_RX equ 0 ;Set the Rx-Interrupt mode
INT_TX equ 1 ;Set the Tx-Interrupt mode
INT_STAT equ 2 ;Set the Status Interrupt mode
;
; -- system hardware addresses --
;
EOI equ 20h ;end of interrupt command
IO_8259 equ 20h ;8259 PIC interrupt controller port
PORT_TM equ 40h ;8253 timer i/o port
PORT_PA equ 60h ;8255 PPI keyboard input port
PORT_PB equ PORT_PA+1 ;8255 PPI in/out keyboard & cassette
PORT_PC equ PORT_PA+2 ;8255 PPI input from planer switches
; (COM1=03F8h, COM2=02F8h)
WBOOT_OFF equ 0E05Bh ;IBM-PC Warm boot offset
WBOOT_SEG equ 0F000h ;IBM-PC Warm boot segment
;
;***********************************************************************
;******************** PROGRAM CONFIGURATION SECTION ********************
;***********************************************************************
;
; COMM Port selection equates, all three of these must be changed in
; order to change the COMM port used for the modem.
;
;
RS232_INT equ 0Ch ;rs232 vector (0Ch=COM1, 0Bh=COM2)
IRQ_BASE equ 10h ;(IRQ4=10h:COM1, IRQ3=08h:COM2)
PORT_BASE equ 03F8h ;8255 rs232 i/o base port address
;03F8h=COM1, 02F8h=COM2
;
; Receive buffer queue size. This is the actual size of the ring
; buffer in bytes used by the rs-232 interrupt routines.
;
RX_BUFF equ 256 ;rx-data interrupt queue size
;
; YES if you are using a Hayes compatable command set modem. If NO, then
; use the CD line to wait for carrier and sync to baud by checking for
; a carriage return to appear at the port (remote must send CR's). This
; is for dumb modems, such as the Universal Data Systems 212b with auto-
; answer capability only.
;
SMART_MDM equ YES ;yes, if you are using a smart modem
;
; Select ONLY ONE of these baud rates for your modems highest baud rate!
;
BPS_300 equ NO ;max baud rate 300bps?
BPS_1200 equ YES ;max baud rate 1200bps?
BPS_2400 equ NO ;max baud rate 2400bps?
TELEBIT equ NO ;Telebit Trailblazer 9600 only!
;
; Set to YES for the Telebit Trailblazer, If the rx-queue becomes full,
; the RTS line is asserted false to tell the modem to halt sending data.
;
USE_RTS equ NO ;yes, ask nulls question
;
; Yes for nulls, you may need to ask for nulls if you have 2400ps mdm.
;
USE_NULLS equ YES ;yes, ask nulls question
;
; Yes to run a COM/EXE file upon receiveing a valid carrier.
;
LOGON_EXE equ YES ;yes, execute XBBS during logon
;
; If YES, re-boot after running 'COM/EXE' file locally.
;
REBOOT equ NO ;no, exit to dos after 'run local'
;
; Initial time allowed on system. This can be read and set via INT66
; function numbers 17 & 18. Normally set/reset by XBBS after log on.
;
TIME equ 60 ;initial time on system limit
;
; Ctrl-S timeout, after timeout is reached Ctrl-S pause if reset.
;
TIMEOUT equ 30 ;30 seconds during Ctrl-S pause
;
; This sets the limit for remote type ahead during 'stdin' operations.
; If the limit is exceeded (noise), the rx queue is flushed and reset.
;
KEYLIM equ 32 ;up to 32 chars at once
;
; Allows optional call to old interrupt 1C service routine. This will
; be an 'iret' instruction if no other resident programs loaded.
;
CHAIN_INT1C equ YES ;call old interrupt 1C
;
; Local keyboard control key mapping. These keys become active
; when the caller makes connection.
;
K_END equ 79 ;[End] key to exit msg from sys/op
K_REMOTE equ ALT_B ;toggle remote on off key
K_LOGOFF equ ALT_O ;send log off message
K_HANGUP equ ALT_H ;hangup on caller and re-boot
K_CHAT equ ALT_Q ;message from sysop key
K_BELL equ ALT_G ;toggle bell on/off
K_CLS equ ALT_Z ;clear the screen locally
K_TIME equ ALT_U ;unlimited time on key
K_SYSOP equ ALT_S ;sys/op status level
K_CALLER equ ALT_C ;display caller on line
;
;***********************************************************************
;****************** END PROGRAM CONFIGURATION SECTION ******************
;***********************************************************************
;
rom_bios_data SEGMENT at 40h ;ROM Bios data area SEGMENT
ORG 72h
reset_flag dw ? ;ALT-CTRL-DEL pressed flag
rom_bios_data ENDS
;
; -- set up the segment and begin exection --
;
code SEGMENT para public 'code'
ASSUME cs:code, ds:code, es:code, ss:code
ORG 100h
start: jmp initialize ;goto loader routine.
;
PAGE
;-----------------------------------------------------------------------
;----------------------- PROGRAM DATA AREA -----------------------------
;-----------------------------------------------------------------------
;
; NOTE: Do not remove copyright as "byexface.c" uses this to
; determine if BYE-PC is resident in memory.
;
;-----------------------------------------------------------------------
;
copyrite db "BYE-PC (c) Copyright 1986, 1987 MCODE Software"
db 01Ah ;Do not alter this string!!!
bye_sign dw 1234h ;BYE-PC signiture when installed
;
; -- Terminal Configuration Data --
; (Televideo 910/912/920/925/950)
;
cls_cnt db 2 ;#bytes to send cls
cls_bytes db ESK,':',0,0,0,0 ;clear screen data
loc_cnt db 2 ;#bytes to locate cursor
loc_bytes db ESK,'=',0,0,0,0 ;locate cursor prefix data
namebuf db 64 dup(0) ;caller name buffer.
;
; -- System Messages and Strings --
;
lost_msg db 0Dh,0Ah,0Dh,0Ah
db "[Carrier Lost]",0Dh,0Ah,"$"
timeup_msg db 0Dh,0Ah,0Dh,0Ah
db "[Time Limit Expired]",0Dh,0Ah,0Dh,0Ah,"$"
off_msg db ": Off]$"
on_msg db ": On]$"
remote_msg db "[Remote$"
bell_msg db "[Bell$"
utime_msg db "[Unlimited Time]$"
logoff_msg db "[System Going down in 2 Mins!]$"
chat_msg db "Message from Sys/Op: $"
sysop_msg db "[Sys/Op Privledges]$"
caller_msg db "[Caller on line: $"
;
;-----------------------------------------------------------------------
;------------------- Program Flags & Varibles Area----------------------
;-----------------------------------------------------------------------
;
baud db 0 ;call baud rate (0=300,1=1200,3=2400)
nulls db 0 ;# of nulls to send
time_on db TIME ;minutes allowed online
cd_lost db NO ;carrier lost detect flag
bell_flg db ON ;local bell state
brk_flg db ON ;Ctrl-Break enable from remote on
trap_flg db OFF ;^S & ^C trap enable flag
tout_flg db ON ;timeout during pause flag on
cstat dw 0FFFFh ;caller status byte (0-255)
;
; --Interrupt Data Storage Areas --
;
old_int09 dd ? ;storage for old keybd service int
old_int10 dd ? ;storage for old video int
old_int13 dd ? ;storage for old disk interrupt
old_int16 dd ? ;storage for old keybd access int
old_int1C dd ? ;storage for old timer interrupt
;
; -- Program status and system flags --
;
timer_tick dw 0 ;timer tick counter
timer_min db 0 ;timer tick minutes counter
int10_merge db ON ;merge characters 'to' screen & mdm
int16_merge db ON ;merge characters 'from' screen & mdm
cd_check db ON ;checking carrier detect status
scan_key db EOF ;scan code key storage.
local_flg db 0 ;program already active flag.
disk_flg db 0 ;disk int 13h busy flag
pause_req db 0 ;Ctrl-S pressed flag
pause_flg db 0 ;Ctrl-S pressed flag
recur_flg db 0 ;int10 re-entrance flag
IF USE_RTS
rts_flg db ON ;rts on/off state flag
ENDIF
;
; -- Receive interrupt data storage --
;
rx_head dw 0 ;queue starting ptr
rx_tail dw 0 ;queue ending ptr
rx_cnt dw 0 ;# of chars in rx-queue
rx_buf db RX_BUFF+1 dup(0) ;rx-queue buffer
;
;
PAGE
SUBTTL *** Keyboard Service Interrupt ***
;---INT-09--------------------------------------------------------------
;Keyboard service interrupt to load all characters from the keyboard.
;This routine traps all keyboard interrupts from the BIOS and checks
;for the 'hot keys' to enable local control options.
;-----------------------------------------------------------------------
;
int09_main PROC far
sti ;re-enable interrupts
push ds ;save registers
push ax
push cs ;DS=CS
pop ds
in al,PORT_PA ;get scan code.
mov scan_key,al ;save key pressed.
call lkey_ndx ;key a local ctrl function?
cmp al,EOF ;no, exit
je kb0
mov ah,2 ;check shift key status
int 16h
test al,8 ;is the Alt key pressed?
jne kb2 ;no, then exit
kb0: pop ax ;restore registers
pop ds
jmp cs:[old_int09] ;jump to Bios routine
;
; Reset the keyboard controler and issue an EOI to 8259 controller.
;
kb2: in al,PORT_PB ;get current control value
mov ah,al ;save it in AH
or al,80h ;set the high bit
out PORT_PB,al ;send it to the control port
mov al,ah ;recover original value
out PORT_PB,al ;send it out
cli ;suspend interrupts
mov al,EOI ;load EOI value
out IO_8259,al ;send it to the 8259
sti ;restore interrupts
cmp local_flg,NULL ;local routine already active?
jne kb1 ; yes, then exit
cmp disk_flg,NULL ;any disk i/o pending?
jne kb1 ; yes, then exit
call local_ctrl ;call local key routine
kb1: pop ax
pop ds ;end interrupt routine
iret
int09_main ENDP
;
;
;-----------------------------------------------------------------------
; LOCAL CONTROL -- This sets up for a call to the local control key
; functions. After one of the correct hot keys are pressed, control is
; transferred here to perform the actual function call.
;-----------------------------------------------------------------------
;
local_ctrl PROC near
mov local_flg,YES ;set program active flag
push ax ;save all registers
push bx
push cx
push dx
push si
push di
mov al,scan_key ;get scan code stored
call lkey_ndx ;key a local ctrl function?
cmp al,EOF ;no, exit
je alt_key_end
xor bx,bx ;clear the word
mov bl,al ;load the function number
shl bx,1 ;table offset x 2
call cs:alt_jtbl[bx] ;call subroutine function#
alt_key_end:
mov local_flg,NULL ;reset status flag
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;
; --- Call table of hot key functions to call ---
;
alt_jtbl dw OFFSET loc_remote ;0 = toggle remote
dw OFFSET loc_logoff ;1 = tell user to logoff
dw OFFSET boot_sys ;2 = hangup on caller
dw OFFSET loc_chat ;3 = enter chat
dw OFFSET loc_bell ;4 = toggle bell on/off
dw OFFSET loc_scrn ;5 = clear screen
dw OFFSET loc_utime ;6 = unlimited time
dw OFFSET loc_sysop ;7 = sys/op status
dw OFFSET loc_caller ;8 = caller on line
local_ctrl ENDP
;
;
;-----------------------------------------------------------------------
; LOCAL KEY INDEX -- This function returns the index of the local keybd
; Alt control keys. If an EOF is returned, the key does not exsists.
;
; Entry AL = Scan Code of Alt key to check
;
; Return: AL = EOF if not a local control key option
;
;-----------------------------------------------------------------------
;
lkey_ndx PROC near
push bx
xor bl,bl
cmp al,K_REMOTE ;toggle remote on off key
je lkey_exit ;BL = 0
inc bl
cmp al,K_LOGOFF ;send log off message
je lkey_exit ;BL = 1
inc bl
cmp al,K_HANGUP ;hangup on caller and re-boot
je lkey_exit ;BL = 2
inc bl
cmp al,K_CHAT ;message from sysop
je lkey_exit ;BL = 3
inc bl
cmp al,K_BELL ;toggle bell on/off
je lkey_exit ;BL = 4
inc bl
cmp al,K_CLS ;clear the screen locally
je lkey_exit ;BL = 5
inc bl
cmp al,K_TIME ;unlimited time on key
je lkey_exit ;BL = 6
inc bl
cmp al,K_SYSOP ;sys/op status
je lkey_exit ;BL = 7
inc bl
cmp al,K_CALLER ;display caller on line
je lkey_exit ;BL = 8
mov bl,EOF ;else, invalid key
lkey_exit: mov al,bl
pop bx
ret
lkey_ndx ENDP
;
;
PAGE
SUBTTL *** Video BIOS Interrupt ***
;---INT-10--------------------------------------------------------------
; All video writes to the BIOS are translated here and passed to the
; modem and the screen. This routine directs all the character video
; writes to the modem and then to the screen. Presently this function
; traps TTY and write character/attribute calls.
;-----------------------------------------------------------------------
;
int10_main PROC far
sti ;re-enable interrupts
push ds ;save DS on the stack
push cs ;DS=CS
pop ds
cmp int10_merge,NULL ;exit if ignoring mdm
je int10_end
cmp recur_flg,0 ;exit if recursive call
je int10_entry ; from old int10 routine
int10_end: pop ds
jmp cs:[old_int10] ;go back to old routine
;
; Redirect BIOS video writes to go to the modem first.
;
int10_entry: push ax ;save function register
cmp ah,14 ;write tty?
je vid_tty ; yes, send it out
cmp ah,10 ;write char at pstn?
je vid_con ; yes, send it out
cmp ah,9 ;write char/attr at pstn?
je vid_con ; yes, send it out
cmp ah,7 ;scroll down?
je vid_cls ; yes, process it
cmp ah,6 ;scroll up?
je vid_cls ; yes, process it
cmp ah,2 ;position cursor?
jne int10_exit ; no, exit
vid_loc: push cx ;send locate csr prefix
push si
mov cl,loc_cnt
mov si,OFFSET loc_bytes
call tx_puts
pop si
pop cx
mov al,dh ;send the cursor row#.
add al,020h ;make it ascii
call tx_putc
mov al,dl ;send the cursor col#.
add al,020h ;make it ascii
call tx_putc
jmp SHORT int10_exit ;exit to old int10
vid_cls: cmp al,0 ;is it a clear window?
jne int10_exit ;no, skip sending cls
cmp cx,0 ;upper left corner 0,0
jne int10_exit
cmp dl,79 ;column 79 or greater?
jl int10_exit
cmp dh,24 ;row 24 or greater?
jl int10_exit
push cx ;send clear screen data
push si
mov cl,cls_cnt
mov si,OFFSET cls_bytes
call tx_puts
pop si
pop cx
jmp SHORT int10_exit ;exit to old int10
vid_tty: call tx_putc ;char to tx in AL now
cmp al,BELL ;was it a bell?
jne int10_exit ; no, then send it out
cmp bell_flg,NULL ;ok to ring locally?
jne int10_exit ; yes, the ring it.
pop ax ;get char off stack
jmp SHORT int10_exit1 ;exit without bell
vid_con: call tx_char ;print char with nulls
;
; Restore registers, call the old int10 to service the video display,
; and then reset the recursion flag before exiting.
;
int10_exit: pop ax ;restore function register
mov recur_flg,1
pushf ;save the flags
call old_int10 ;call old bios routine
mov recur_flg,0 ;clear recursion flag
int10_exit1: pop ds ;one last pop for the DS
iret
int10_main ENDP
;
;
PAGE
SUBTTL *** Disk BIOS Interrupt ***
;---INT-13--------------------------------------------------------------
; Disk i/o interrupt. This routine sets a flag any time disk i/o is
; pending. This is used to prevent any local console hot keys from
; doing anything if any disk i/o is being performed.
;------------------------------------------------------------------------------
;
int13_main PROC far
mov cs:disk_flg,1 ;set disk 'busy' flag
pushf ;save the flags
call cs:[old_int13] ;call old disk int
mov cs:disk_flg,0 ;reset disk 'busy' flag
ret 2 ;exit with new flags
int13_main ENDP
;
;
PAGE
SUBTTL *** Keyboard BIOS Interrupt ***
;---INT-16--------------------------------------------------------------
; All keyboard requests to the BIOS are intercepted here. Keyboard BIOS
; functions #0-(keyboard get character) and #1-(keyboard data availble)
; are interrcepted here. If the modem rx-queue has data, it is merged
; before the local keyboard data received. Otherwise, the original BIOS
; routines are called to service the local keyboard. Note that we could
; simply stuff the characters in the queue within the rx-data interrupt,
; but this would prevent us from having more than 15 character type
; ahead. This method allows the int16 to keep getting chars up to the
; the maximum rx-queue size.
;-----------------------------------------------------------------------
;
int16_main PROC far
sti ;re-enable interrupts
push ds
push ax
push cs ;DS=CS
pop ds
cmp cd_check,NULL ;checking cd status?
je int16_main1 ;no, skip time check
call chk_timeon ;time online is up?
int16_main1: cmp int16_merge,NULL ;ignoring modem input?
je skip_int16 ; yes, then jump
;
; -- Check for input overflow. If buffer is full ring bell & flush --
;
push ax ;save ax
flush_all: call rx_size ;check remote input limit
cmp ax,KEYLIM
jl int16_main2 ;if limit reached, reset it
call rx_flush ;reset the rx buffer
cmp bell_flg,NULL ;ok to ring bell?
je flush_all ; no, skip it.
push bx
mov bl,2 ; yes, beep on overflow
call beep
pop bx
jmp SHORT flush_all ;flush it out again
int16_main2: pop ax ;restore ax
or ah,ah ;0 = get-a-key
jz getkey
dec ah ;1 = get-keybd-status
jz kbhit
skip_int16: pop ax ;recover function#
pop ds ;recover DS
jmp cs:[old_int16] ;skip shift status, jmp to bios
;
; --- check for char waiting at modem, else goto BIOS keybd routine ---
;
kbhit: call rx_ready ;character in the queue?
jz skip_int16 ; no, exit through BIOS
pop ax ;recover function call#
pop ds ;recover DS
or al,1 ;char ready at keybd bit.
ret 2 ;return from interrupt with
; flags popped off stack.
;
; --- get a character from modem or keyboard ---
;
getkey: call rx_ready ;character in queue?
jz skip_int16 ;jump if no character rdy
pop ax ;discard int function#
gotkey: call rx_getc ;get character from mdm
and al,7Fh ;strip off parity bit 7
xor ah,ah ;clear scan code return
pop ds ;recover DS
iret
int16_main ENDP
;
;
;-----------------------------------------------------------------------
; CHECK TIME ONLINE -- Check to see if timeon has expired, if not exit
; else tell the caller time is up, hangup, and re-boot. This function
; is called before each keyboard get data/status interrupt is processed.
;-----------------------------------------------------------------------
;
chk_timeon PROC near
push ax
mov ah,time_on ;get time limit
or ah,0
jz check_end ; no, exit
cmp timer_min,ah ;compare to time passed
jg time_up ;time on < time expired?
check_end: pop ax
ret
time_up: pop ax
mov si,OFFSET timeup_msg ;tell caller time is up
call con_puts
mov ax,2 ;wait 2 seconds
call delay ;fall through and wboot
jmp NEAR PTR boot_sys ;reboot system
chk_timeon ENDP
;
PAGE
SUBTTL *** Timer Tick Service Interrupt ***
;---INT-1C--------------------------------------------------------------
; This routine is called approximatly 18.2 times/second to service the
; system timer that counts minutes passed since logon. Also, if the
; keyboard 'request_flg' is set, the alt_key routine is called to serv-
; ice local keyboard control functions.
;-----------------------------------------------------------------------
;
int1C_main PROC far
sti
push ds ;save DS
push cs ;DS=CS
pop ds
IF CHAIN_INT1C
pushf ;save the flags
call old_int1C ;call old timer rtn
ENDIF
;
; Check to see if the RTS is off, and if we can turn it on yet.
; The RTS line is used to control data flows from high speed modems.
;
IF USE_RTS
push ax
cmp rts_flg,NULL ;exit if RTS is on
jne skip_rts
call rx_size ;get queue size
cmp ax,(RX_BUFF/4) ;3/4 empty yet?
jg skip_rts ;no, leave it off
mov rts_flg,ON
mov ax,ON ;turn RTS back on now
call port_rts
skip_rts: pop ax
ENDIF
;
; Increment the time on system clock ticker used to check time-on limit.
;
inc timer_tick
cmp timer_tick,1092 ;ticked 1092 times yet?
jl int1C_end ;jmp if not > 1080
inc timer_min ;else, inc minutes...
mov timer_tick,0 ;reset the ticker
int1C_end: pop ds
iret
int1C_main ENDP
;
PAGE
SUBTTL *** Applications Program Interface (API) ***
;-----------------------------------------------------------------------
;INT66 APPLICATIONS PROGRAM INTERFACE (API) -- This interrupt provides
; modem i/o for applications which need direct control of the modem.
;
; Altered registers : AX, BX, CX, DX
; ES (only when funct 21 called)
;
; Preserved registers: CS, DS, SS, DI, SI, SP, BP
;
;-----------------------------------------------------------------------
; Functions Provided:
;
; AH=0 Get character from modem in AL.
; AL=character returned
; AH=0 null (no keybd scan codes)
; AX = EOF if no data.
;
; AH=1 Put character in AL to modem.
; AL=character to send.
;
; AH=2 Hang up the modem (DTR off):
; AL=0 turn off dtr
; AL=1 turn on dtr
;
; AH=3 Get carrier detect status in AX:
; AX=0 no carrier detect found
; AX=1 carrier detect found
;
; AH=4 Set carrier detect check status:
; AX=0 ignore carrier detect status
; AX=1 check carrier detect status
;
; AH=5 Flush the rx queue of all data.
;
; AH=6 Get # of chars in rx queue:
; AX=0 rx queue size
;
; AH=7 Get Baud rate of this call in AX:
; (0=300bps, 1=1200bps, 3=2400bps)
;
; AH=8 Set Control Break/Pause (CTRL-C/CTRL-S) flag state:
; AL=0 CTRL_NOBRK - disable remote ^C & ^S breaks
; AL=1 CTRL_BRK - enable remote breaks
; AL=2 CTRL_NOTOUT - disable ^S pause timeout
; AL=3 CTRL_TOUT - enable ^S timeout
; AL=4 CTRL_NOTRAP - dont trap ^C & ^S characters
; AL=5 CTRL_TRAP - allow ^C & ^S to be trapped out
;
; AH=9 Set <stdout> merged with modem:
; AL=0 disable output modem
; AL=1 enable output to modem
;
; AH=10 Set <stdin> merged with modem:
; AL=0 disable input from modem (rx-queue active)
; AL=1 enable input from modem (rx-queue active)
;
; AH=11 Get BYE-PC version/revison level in AX:
; AL=Current Revision level
; AH=Current Version level
;
; AH=12 Get number of nulls requested in AX:
; AX=Number of nulls requested
;
; AH=13 Set number of nulls to AX:
; AL=Number of nulls to send
;
; AH=14 Warm Boot the system through bios Ctrl-Alt-Del.
;
; AH=15 Get the user caller status
; AX=Status level (0-FFFFh).
;
; AH=16 Set the user caller status
; CX=Status level (0-FFFFh).
;
; AH=17 Set the time limit caller is allowed on line.
; AL=number of minutes (0-255, 0=Unlimited).
;
; AH=18 Get the time caller has been on line
; AX=number of minutes online (0-255).
;
; AH=19 Set the receive data interrupt mode
; AL=Mode (0=Off, 1=On)
;
; AH=20 Get the time limit caller is allowed on line.
; AX=number of minutes allowed online (0-255).
;
; AH=21 Get far pointer to caller name buffer.
; ES:BX -> far ptr to name buffer
;
; AH=22 Get far pointer to terminal configuration buffer
; ES:BX -> far ptr to terminal config buffer
;
;-----------------------------------------------------------------------
;
int66_main PROC far
sti ;re-enable interrupts
push si
push ds ;save DS
push cs
pop ds ;DS=CS for data items
cmp ah,22 ;check function range
jg int66_end
xor bh,bh ;clear the msb of word
mov bl,ah ;load function# in lsb
shl bx,1 ;table OFFSET x 2
call cs:int66_jtbl[bx] ;call subroutine function#
int66_end: pop ds ;restore DS
pop si
iret
;
; --- Call table for function # to call ---
;
int66_jtbl dw OFFSET rx_getc ; 0 = mdm get character
dw OFFSET iputc ; 1 = mdm put character
dw OFFSET dtr_state ; 2 = dtr on/off
dw OFFSET cd_status ; 3 = CD status
dw OFFSET cd_flag ; 4 = ignore CD state
dw OFFSET rx_flush ; 5 = flush Rx queue
dw OFFSET rx_size ; 6 = get Rx-size
dw OFFSET get_bps ; 7 = get baud rate
dw OFFSET set_break ; 8 = set break
dw OFFSET halt_send ; 9 = halt loc sending
dw OFFSET halt_get ; 10 = halt loc receiving
dw OFFSET get_vers ; 11 = get rev level
dw OFFSET get_nulls ; 12 = get # of nulls
dw OFFSET set_nulls ; 13 = set # of nulls
dw OFFSET boot_sys ; 14 = re-boot system
dw OFFSET get_stat ; 15 = set status
dw OFFSET set_stat ; 16 = get status
dw OFFSET set_timer ; 17 = set max time online
dw OFFSET get_timeon ; 18 = get time online
dw OFFSET set_rxint ; 19 = set rx interrupt
dw OFFSET get_timer ; 20 = get max time online
dw OFFSET get_nameptr ; 21 = ptr to name buffer
dw OFFSET get_termptr ; 22 = ptr to term buffer
int66_main ENDP
;
PAGE
;-----------------------------------------------------------------------
; INT66 subroutine functions. These are called via software interrupts.
;-----------------------------------------------------------------------
;
; Put character to modem in AL.
;
iputc PROC near
call tx_putc ;char in AL
xor ax,ax ;signal ok
iputc1: ret
iputc ENDP
;
; Return the current status of the carrier detect line.
;
cd_status PROC near
call port_cd ;check CD line
mov ax,YES
jc cd_stat1 ;jump if CD found
mov ax,NULL ;flag no carrier
cd_stat1: ret
cd_status ENDP
;
; Set/Reset the flag that ignores carrier detect status.
;
cd_flag PROC near
or al,al ;carrier chk on or off?
jz cd_flag1
mov cd_check,ON ;carrier checking on
cmp cd_lost,NULL ;has carrier been lost?
je cd_flag2 ; jump if not lost
jmp NEAR PTR boot_sys ; else, re-boot system.
cd_flag1: mov cd_check,NULL ;carrier checking off
cd_flag2: ret
cd_flag ENDP
;
; Toggle the dtr/cts line to the modem to force a hangup.
;
dtr_state PROC near
or al,al
jz dtr_state1
mov ax,ON
jmp SHORT dtr_state2
dtr_state1: mov ax,NULL ;turn OFF the dtr/rts
dtr_state2: call port_dtr
ret
dtr_state ENDP
;
; Get the baud rate of this call in AX.
;
get_bps PROC near
xor ah,ah ;clear the msb
mov al,baud ;return baud rate
ret
get_bps ENDP
;
; Set the ctrl-break flag on/off.
;
set_break PROC near
or al,al
jz set_brk_off ;AH=0 - Break/Pause Off
dec al
jz set_brk_on ;AH=1 - Break/Pause On
dec al
jz set_tim_off ;AH=2 - Pause Timeout Off
dec al
jz set_tim_on ;AH=3 - Pause Timeout On
dec al
jz filter_off ;AH=4 - Filter/Trap off
dec al
jz filter_on ;AH=5 - Filter/Trap On
jmp SHORT set_brk_end
set_brk_off: mov brk_flg,OFF ;^C break off
jmp SHORT set_brk_end
set_brk_on: mov brk_flg,ON ;^C break on
jmp SHORT set_brk_end
set_tim_off: mov tout_flg,OFF ;^S time-out off
jmp SHORT set_brk_end
set_tim_on: mov tout_flg,ON ;^S time-out on
jmp SHORT set_brk_end
filter_off: mov trap_flg,OFF ;^C & ^S trap off
mov pause_req,OFF ;clear ^S pause flag
jmp SHORT set_brk_end
filter_on: mov trap_flg,ON ;^C & ^S trap mdm on
set_brk_end: ret
set_break ENDP
;
; Set/Reset the flag that ignores chars sent to modem.
;
halt_send PROC near
or al,al
jz halt_send1
mov int10_merge,ON ;chars 'to' mdm on
jmp SHORT halts_exit
halt_send1: mov int10_merge,NULL ;chars 'to' mdm off
halts_exit: ret
halt_send ENDP
;
; Set/Reset the flag that ignores chars received from modem.
;
halt_get PROC near
or al,al
jz halt_get1
mov int16_merge,ON ;chars 'from' mdm on
jmp SHORT haltg_exit
halt_get1: mov int16_merge,NULL ;chars 'from' mdm off
haltg_exit: ret
halt_get ENDP
;
; Return the version/revision level in AX.
;
get_vers PROC near
mov al,PGM_REV ;get rev level
mov ah,PGM_VER ;get vers number
ret
get_vers ENDP
;
; Get the number of nulls currently used.
;
get_nulls PROC near
xor ah,ah ;clear msb
mov al,nulls ;return nulls
ret
get_nulls ENDP
;
; Set the number of nulls currently used.
;
set_nulls PROC near
cmp al,9
jle set_nulls1
mov al,0
set_nulls1: mov nulls,al
ret
set_nulls ENDP
;
; Re-Boot the system function.
;
boot_sys PROC near
mov ax,OFF
call port_dtr ;hang up modem
ASSUME ds:rom_bios_data
mov ax,rom_bios_data ;set DS to rom bios data
mov ds,ax ; area.
mov reset_flag, 1234h ;set the alt-ctrl-del flag.
db 0EAH ;jmp to ROM warm boot at:
dw WBOOT_OFF ; 0E05B OFFSET
dw WBOOT_SEG ; 0F000 segment
ASSUME ds:code
boot_sys ENDP
;
; Get the caller status level.
;
get_stat PROC near
mov ax,cstat ;get status level
ret
get_stat ENDP
;
; Set the caller status level.
;
set_stat PROC near
mov cstat,cx ;set status level
ret
set_stat ENDP
;
; Set the callers time limit online allowed.
;
set_timer PROC near
mov time_on,al ;save time on allowed
mov timer_tick,0 ;reset timer counter
mov timer_min,0
ret
set_timer ENDP
;
; Get the callers time online.
;
get_timeon PROC near
xor ah,ah
mov al,timer_min ;return it in AL
ret
get_timeon ENDP
;
; Set the receive interrupt mode
;
set_rxint PROC near
and al,ON ;toggle for int state
mov ah,INT_RX ;set rx-interrupt mode
call port_imode ;change interrupt mode
call rx_flush ;clear rx queue
ret
set_rxint ENDP
;
; Get the callers time limit online allowed.
;
get_timer PROC near
xor ah,ah
mov al,time_on ;save time on allowed
ret
get_timer ENDP
;
; Get far ptr to caller name buffer within BYE-PC.
;
get_nameptr PROC near
mov ax,cs ;ES=CS
mov es,ax
lea bx,namebuf ;ES:BX points to name buff
ret
get_nameptr ENDP
;
; Get far ptr to terminal type buffer within BYE-PC.
;
get_termptr PROC near
mov ax,cs ;ES=CS
mov es,ax
lea bx,cls_cnt ;ES:BX points to term'buff
ret
get_termptr ENDP
;
;-----------------------------------------------------------------------
; Local keyboard control key subroutines called from INT09 interrupts.
;-----------------------------------------------------------------------
;
; Toggle the remote on/off from 'stdin' and 'stdout'
;
loc_remote PROC near
mov bl,int10_merge ;save int10 trap state
push bx ;save bx reg
mov int10_merge,NULL ;dont send chars to mdm
mov si,OFFSET remote_msg ;show header
call con_puts
pop bx ;restore bx
mov al,bl
xor al,ON ;now toggle int10 state
or al,al ;is it off?
mov si,OFFSET off_msg
jz loc_remote1 ;show 'on'/'off' msg
mov si,OFFSET on_msg
loc_remote1: call con_puts
call rx_flush ;reset rx-queue
mov int10_merge,al ;set chars 'to' mdm
mov int16_merge,al ;set chars 'from' mdm
;--> mov ah,INT_RX ;set rx-interrupt mode
;--> call port_imode ;change interrupt mode
ret
loc_remote ENDP
;
; Teller caller to logoff.
;
loc_logoff PROC near
mov si,OFFSET logoff_msg
call con_puts
ret
loc_logoff ENDP
;
; Give caller unlimted time.
;
loc_utime PROC near
mov si,OFFSET utime_msg
call con_puts
mov time_on,0 ;disable online timer
ret
loc_utime ENDP
;
; Perform local sys/op message
;
loc_chat PROC near
mov si,OFFSET chat_msg ;tell'em were breaking in
call con_puts
mov al,OFF ;cut off rx-interrupts
mov ah,INT_RX ;set rx-interrupt
call port_imode ;change interrupt mode
call rx_flush
loc_chat1: mov ah,1 ;see if local keyboard
pushf ; has char ready.
call old_int16
jnz loc_chat1 ;jmp if no character ready
mov ah,0 ;check key press state.
pushf
call old_int16 ;read the key pressed.
cmp al,0 ;was it a scan code key?
jne loc_chat4 ; no, display the key
cmp ah,K_END ;[End] key exits chat
je loc_chat_end
jp SHORT loc_chat1 ;continue, wrong scan key
loc_chat4: cmp al,ESK ;<ESK> key exits chat
je loc_chat_end
call con_putc ;display key received
cmp al,CR
jne loc_chat1 ;if carriage return,
mov al,LF ; send a line feed also.
call con_putc
jp SHORT loc_chat1
loc_chat_end: mov al,ON ;toggle for int state
mov ah,INT_RX ;set rx-interrupt mode
call port_imode ;change interrupt mode
call rx_flush
mov al,CR ;send a new line on exit.
call con_putc
mov al,LF
call con_putc
ret
loc_chat ENDP
;
; Toggle the system bell enable flag
;
loc_bell PROC near
mov bl,int10_merge
push bx
mov int10_merge,NULL
mov si,OFFSET bell_msg
call con_puts
mov al,bell_flg ;get current bell state
xor al,YES ;now toggle it.
cmp al,NULL ;is it on?
mov si,OFFSET on_msg
jne loc_bell1
mov si,OFFSET off_msg
loc_bell1: call con_puts
mov bell_flg,al ;set new bell state
pop bx
mov int10_merge,bl
ret
loc_bell ENDP
;
; Perform local clear screen function.
;
loc_scrn PROC near
mov cx,0
mov ax,0700h ;clear the screen
mov dx,184fh
mov bx,0700h
pushf
call old_int10
mov ah,2 ;home the cursor
mov dx,0
mov bx,0
pushf
call old_int10
ret
loc_scrn ENDP
;
; Give caller online sys/op privledges.
;
loc_sysop PROC near
mov si,OFFSET sysop_msg
call con_puts
mov cstat,0FFFFh ;caller status byte
ret
loc_sysop ENDP
;
; Display caller name buffer locally.
;
loc_caller PROC near
mov bl,int10_merge
mov int10_merge,NULL
mov si,OFFSET caller_msg
call con_puts
mov si,OFFSET namebuf
call con_puts
mov al,']'
call con_putc
mov int10_merge,bl
ret
loc_caller ENDP
;
;
PAGE
SUBTTL *** RS232 Service Routines ***
;---RS232-INTERRUPT-SERVICE-ROUTINES------------------------------------
; This is the interrupt main service routine for the serial port.
; This routine is called due to one of the following occurances:
;
; Priority: (1) Received Data register has character.
; (2) Transmitter Holding register empty (Not Used Yet!).
; (3) Change in modem CD status (Carrier Detect).
;
; When the interrupt occurs the interrupt identification register is
; read to check for which class of interrupt occured and call the
; appropriate function to handle the request. Note that more than one
; interrupt may have occured at once and the interrupt id register
; must be checked for any pending interrupts before exiting.
;-----------------------------------------------------------------------
;
rs232_serv PROC far
sti ;restart interrupts first!
push ax ;save registers
push bx
push cx
push dx
push di
push si
push ds
mov ax,cs ;DS=CS
mov ds,ax
;
; Check the 8259 interrupt controller for any other interrupts that
; may be pending. More than one may have occured at same time!
;
rs232_serv0: mov dx,PORT_BASE+2 ;interrupt id register
in al,dx ;read interrupt id code
test al,1 ;test for interrupts pending
jnz rs232_exit ;no more to service, exit!
mov bl,al ;set table index in lsb
xor bh,bh ;clear the msb
call cs:rs232_jmp[bx] ;call appropriate service rtn
jmp SHORT rs232_serv0 ;check for other pending ints
;
; Signal the 8259 interrupt controller that we are done.
;
rs232_exit: cli ;halt interrupts temporarily
mov al,EOI ;send EOI command to the
out IO_8259,al ; 8259 interrupt controller.
sti ;now re-enable interrupts
cmp pause_req,NULL ;Ctrl-S pause requested?
je rs232_exit1 ; no, exit from interrupt.
cmp pause_flg,NULL ;Is pause already in effect?
jne rs232_exit1 ; yes, exit from interrupt.
call pause_rtn ;fall into pause loop until
;another rx interrupt occurs.
rs232_exit1: pop ds
pop si
pop di
pop dx
pop cx
pop bx
pop ax
iret
rs232_serv ENDP
;
; Jump table of routines to call on interrupt request
;
rs232_jmp dw OFFSET stat_serv ;service modem status change
dw OFFSET tx_serv ;service tx-routine
dw OFFSET rx_serv ;service rx-routine
;
;
;---PAUSE-ROUTINE-------------------------------------------------------
; This routine handles the pause routines, generated by a Ctrl-S being
; received at the serial port, if control key trapping is enabled.
;-----------------------------------------------------------------------
;
pause_rtn PROC near
mov pause_flg,ON ;signal pause in effect
mov ah,15
int 10h ;get video mode
cmp al,2 ;if CGA is found make sure
je vmode2 ; its enabled before pause
cmp al,3
je vmode3
jmp SHORT pause0 ;else, no re-enable needed.
vmode2: mov al,2Dh ;re-enable value mode 2
jmp SHORT pause0
vmode3: mov al,29h ;re-enable value mode 3
mov dx,3D8h ;cga control register
out dx,al ;send it to the cga
;
; Now wait in a loop for another receive interrupt to occur or
; for the timer to timeout and return control.
;
pause0: mov cx,TIMEOUT*8 ;set up time out delay
pause1: cmp pause_req,NULL ;has another interrupt occured
je pause_end ; to cut off pause?
pause2: cmp tout_flg,NULL ;any timeout?
je pause1 ; no,loop back
mov ax,2 ;else
call timer ; wait 1/9th sec
loop pause1 ;loop if no timeout yet
pause_end: mov pause_req,NULL ;reset pause request
mov pause_flg,NULL ;pause no longer active
ret
pause_rtn ENDP
;
;
;---RX-SERVICE-ROUTINE--------------------------------------------------
; This routine is called every time a character arrives at the serial
; port. The data is placed in the circular rx-queue and the buffer
; pointers are incremented.
;-----------------------------------------------------------------------
;
rx_serv PROC near
mov dx,PORT_BASE ;rx-data register
in al,dx ;get data from port
mov dx,PORT_BASE+5 ;line status register
mov bl,al ;save character in BL
in al,dx ;read line status register
test ah,00011110b ;receive error?
jnz rx_nerr ;jmp if no rx-error
mov bl,0FBh ;force error to 'check' char
rx_nerr: mov al,bl ;restore char from BL
;
; The character is in the AL reg now, check for special characters.
;
cmp pause_req,NULL ;if pause is off, skip
je rx_serva ; pause reset,
mov pause_req,NULL ;else, reset pause and
ret ; exit.
rx_serva: cmp trap_flg,NULL ;dont trap any ctrl-chars if
je rx_serv0 ; trap is off.
rx_servb: cmp al,CTRL_C ;check for ^C break.
jne rx_servc
cmp brk_flg,NULL ;skip if break off
je rx_servd
int 1Bh ;issue a break program interrupt
rx_reset: mov rx_head,0 ;reset the rx queue
mov rx_tail,0
mov rx_cnt,0
ret ; and exit
rx_servc: cmp al,CTRL_S ;check for ^S pause.
jne rx_serv0
mov pause_req,ON ;set the pause request flag on
rx_servd: ret ; and exit
;
; Put the character in the rx-queue and adjust the queue pointers.
;
rx_serv0: mov bx,rx_tail ;get the end pointer
mov [rx_buf+bx],al ;put char in the queue
inc rx_cnt ;inc cnt for char added
inc bx ;advance the pointer
cmp bx,RX_BUFF ;wrapped around yet?
jl rx_serv1
mov bx,0 ;yes, wrap around
rx_serv1: cmp bx,rx_head ;is the buffer full?
je rx_serv3 ;yes, next char overwrites
mov rx_tail,bx ;new end of queue
ret
rx_serv3:
IF USE_RTS
mov rts_flg,OFF ;set flg signaling RTS is off
mov ax,OFF
call port_rts ;now drop the RTS line
ELSE
jmp SHORT rx_reset
ENDIF
rx_serv_end: ret
rx_serv ENDP
;
;
;---TX-SERVICE-ROUTINE--------------------------------------------------
; DUMMY FUNCTION -- Tx-interrupts are not implemented yet!
;-----------------------------------------------------------------------
;
tx_serv PROC near
ret
tx_serv ENDP
;
;
;---STATUS-SERVICE-ROUTINE----------------------------------------------
; This routine is called every time a change in modem status is found.
; This routine checks for loss of carrier and resets if carrier is lost.
;-----------------------------------------------------------------------
;
stat_serv PROC near
call port_cd ;CD pin pulled high?
jc stat_exit ;jmp if CD found
mov cd_lost,YES ;set carrier lost flg
cmp cd_check,NULL ;checking CD status?
je stat_exit ;no, exit
mov int10_merge,NULL ;skip the modem
mov int16_merge,NULL ;skip the keyboard
mov si,OFFSET lost_msg ;show carrier lost message
call con_puts
mov ax,OFF ;turn off dtr/rts, hang up
call port_dtr
mov ax,2 ;wait a second
call delay ;fall through and wboot
call boot_sys ;re-boot the system!
stat_exit: ret
stat_serv ENDP
;
;-----------------------------------------------------------------------
; *** END OF INTERRUPT SERVICE ROUTINES ***
;-----------------------------------------------------------------------
;
;
PAGE
SUBTTL *** Receive Data Routines ***
;-RX-READY--------------------------------------------------------------
; Set the zero flag if the rx-queue is empty, else clear the zero
; flag to indicate that data is available in the queue.
;
; Return: Zero Flag set = NO data in queue.
; Zero Flag reset = data waiting in queue.
;
;-----------------------------------------------------------------------
;
rx_ready PROC near
cli ;halt all interrupts
push bx
or bl,bl ;clear zero flag
mov bx,rx_head ;fetch the pointers
cmp bx,rx_tail ;if head=tail, no data!
pop bx
sti ;restart interrupts
ret
rx_ready ENDP
;
;
;-RX-GET-CHARACTER------------------------------------------------------
; Get a character from the rx-queue. If the character is not available
; return an EOF to signal an error. (All other registers preserved)
;
; Return: AX = EOF if error.
; AL = Character received
; AH = NULL (scan codes not supported)
;
;-----------------------------------------------------------------------
;
rx_getc PROC near
cli ;halt all interrupts
push bx
mov ax,EOF ;assume error
mov bx,rx_head ;fetch the pointers
cmp bx,rx_tail ;if head=tail, no data!
je rx_getc3 ;no, exit with eof
mov al,[rx_buf+bx] ;get data from head ptr
xor ah,ah ;clear the msb scan code
dec rx_cnt ;dec cnt after removal
inc bx ;advance head ptr
cmp bx,RX_BUFF ;wrapped around?
jl rx_getc2 ; no, update head ptr
mov bx,0 ; yes, wrap ptr around
rx_getc2: mov rx_head,bx ;update the ptr
rx_getc3: pop bx ;restart interrupts
sti
ret
rx_getc ENDP
;
;
;-----------------------------------------------------------------------
; RX FLUSH -- Empty all characters from the mdm receive port
;-----------------------------------------------------------------------
;
rx_flush PROC near
cli
mov rx_head,0 ;reset head pointer
mov rx_tail,0 ;reset tail pointer
mov rx_cnt,0 ;reset counter
sti
ret
rx_flush ENDP
;
;
;-----------------------------------------------------------------------
; RX SIZE -- Return the number of characters in the rx-queue.
;
; Return: AX = Characters in rx-queue.
;
;-----------------------------------------------------------------------
;
rx_size PROC near
cli
mov ax,rx_cnt ;get the count
sti
ret
rx_size ENDP
;
;
PAGE
SUBTTL *** Transmit Data Routines ***
;-----------------------------------------------------------------------
; TX PUTS - This function transmits the buffer pointed to by the AX
; register for CL bytes. AL & CL register destroyed, all others are
; preserved. This function does not use nulls translation.
;
; Entry: DS:SI -- buffer to transmit
; CL -- count of bytes to transmit
;
;-----------------------------------------------------------------------
;
tx_puts PROC near
xor ch,ch ;clear the msb
cld ;clear direction flag
tx_puts1: lodsb ;get a byte from string
call tx_putc ;send the data
loop tx_puts1
ret
tx_puts ENDP
;
;
;-----------------------------------------------------------------------
; MODEM TX CHARACTER -- Send a character to modem through com port.
; If the character sent was a line feed (LF) and 'nulls' greater that 0,
; then nulls are sent after the line feed. (All registers preserved)
;
; Entry: AL - Character to send
;
;-----------------------------------------------------------------------
;
tx_char PROC near
call tx_putc ;now send the character
IF USE_NULLS
push cx
mov cl,nulls ;get # of nulls to send
xor ch,ch ;clear the msb
or cx,cx ;check if for any nulls to tx
jz tx_char2 ;exit if nulls not in effect
cmp al,LF ;send nulls if a line feed
jne tx_char2 ;no, skip over if not a LF
push ax ;save the data
tx_char1: mov al,0
call tx_putc ;send a null out
loop tx_char1 ;loop until CX=0
pop ax
tx_char2: pop cx
ENDIF
ret
tx_char ENDP
;
;
;-----------------------------------------------------------------------
; TRANSMIT CHARACTER -- Send a character to modem through com port.
; All registers used are saved before sending the char. Note that in
; order to transmit a byte, that DSR and CTS must be active.
;
; Entry: AL - Character to send
;
;-----------------------------------------------------------------------
;
tx_putc PROC near
push dx
push ax
mov dx,PORT_BASE+6 ;modem status register
tx_putc1: in al,dx ;read port status
and al,030h ;check DSR & CTS
jz tx_putc1 ;loop until DSR & CTS
dec dx ;line status register
tx_putc2: in al,dx ;read port status
and al,20h ;check tx hold reg empty
jz tx_putc2 ;loop until tx complete
sub dx,5 ;data port register
pop ax ;recover data to send
out dx,al ;send the character
pop dx ;restore dx
ret
tx_putc ENDP
;
;
PAGE
SUBTTL *** RS232 Port Control Routines ***
;-----------------------------------------------------------------------
; PORT CARRIER DETECT -- Read the modem CD pin from COM status port
;
; Exit: CARRY FLAG SET = Carrier Detect (CD) found
;
;-----------------------------------------------------------------------
;
port_cd PROC near
push dx
push ax
cli
mov dx,PORT_BASE+6 ;mdm status register
or al,al ;clear the carry flag
in al,dx ;get the port status
rcl al,1 ;rotate left through carry
sti
pop ax
pop dx
ret
port_cd ENDP
;
;
;-----------------------------------------------------------------------
; PORT DTR -- Toggle the DTR/RTS line to the modem
;
; Entry: AX == 0 DTR/RTS off
; AX <> 0 DTR/RTS on
;
;-----------------------------------------------------------------------
;
port_dtr PROC near
push bx
push dx
cli
mov bx,ax ;save state flag
mov dx,PORT_BASE+4 ;modem control register
in al,dx
or bx,0 ;turn on or off?
jz port_dtr0
or al,00000011b ;DTR/RTS bits on
jmp SHORT port_dtr1
port_dtr0: and al,11111100b ;DTR/RTS bits off
port_dtr1: mov dx,PORT_BASE+4 ;modem control register
out dx,al
sti
pop dx
pop bx
ret
port_dtr ENDP
;
;
;-----------------------------------------------------------------------
; PORT RTS -- Toggle only the RTS line to the modem
;
; Entry: AX == 0 RTS off
; AX <> RTS on
;
;-----------------------------------------------------------------------
;
IF USE_RTS
port_rts PROC near
push bx
push dx
cli
mov bx,ax ;save state flag
mov dx,PORT_BASE+4 ;modem control register
in al,dx
or bx,0 ;turn on or off?
jz port_rts0
or al,00000010b ;RTS bits on
jmp SHORT port_rts1
port_rts0: and al,11111101b ;RTS bits off
port_rts1: mov dx,PORT_BASE+4 ;modem control register
out dx,al
sti
pop dx
pop bx
ret
port_rts ENDP
ENDIF
;
;
;-----------------------------------------------------------------------
; ASYNC PORT BAUD RATE SET -- Set the baud rate of the 8250.
;
; Entry: AX = Baud rate baud rate initialization value.
;
;-----------------------------------------------------------------------
;
port_baud PROC near
push dx
cli
push ax
mov dx,PORT_BASE+3 ;line control register
in al,dx
or al,10000000b ;set DLAB = 1
out dx,al
pop ax
mov dx,PORT_BASE
out dx,al ;send the lsb
inc dx
mov al,ah
out dx,al ;send the msb
mov dx,PORT_BASE+3 ;line control register
in al,dx
and al,not 10000000b ;set DLAB = 0
out dx,al
mov al,00000011b ;no parity, 8 bits, 1 stop bit
out dx,al
sti
pop dx
ret
port_baud ENDP
;
;
;-----------------------------------------------------------------------
; ASYNC PORT INTERRUPT START -- Starts the interrupts from the 8250.
; This routine sets up all the interrupt registers on the 8250 and
; on the 8259 interrupt controller for interrupts. The default start
; mode is for Rx/Tx interrupts only, modem status is enabled after
; a caller is online to check for carrier detect lost.
;-----------------------------------------------------------------------
;
port_start PROC near
mov dx,PORT_BASE+3 ;line control register
in al,dx
and al,not 10000000b ;set DLAB = 0
out dx,al
mov dx,PORT_BASE ;data holding register
in al,dx ;flush any Rx-data out
;
; 1) Set the correct bit in the 8259 interrupt controller to enable
; the IRQ4 interrupts from the serial port.
;
pushf ;save interrupt status
cli ;stop interrupts temporarily
mov cl,not IRQ_BASE ;set the IRQ4 bit ON to 8259
in al,21h ;read 8259 status
and al,cl ;set bit to zero to enable
out 21h,al ;update new interrupt status
;
; 2) At base+3 cut off the DLAB bit to allow access to the
; interrupt enable register (EIR).
;
mov dx,PORT_BASE+3
in al,dx ;read current status
and al,not 10000000b
out dx,al ;turn off DLAB
;
; 3) At base base+1 (IER), enable interrupts to the 8250
;
mov dx,PORT_BASE+1 ;8250 interrupt enable register
mov al,00000000b ;enable rx interrupts to start
out dx,al
;
; 4) At base base+4 (mdm control), enable OUT2 signal
;
mov dx,PORT_BASE+4 ;modem control register
in al,dx
or al,00001000b ;set the out2 bit
out dx,al ;send update
popf ;restore interrupt status
;
; Interrupts are enabled and will load the first character ready
;
call clear_pints ;clear any pending interrupts
ret
port_start ENDP
;
;
;-----------------------------------------------------------------------
; ASYNC PORT INTERRUPT STOP -- Stops the interrupts from the 8250.
; This routine resets all the interrupt registers on the 8250 and
; on the 8259 interrupt controller.
;-----------------------------------------------------------------------
;
port_stop PROC near
;
; 1) Set the correct bit in the 8259 interrupt controller to disable
; the IRQ4 interrupts from the serial port.
;
pushf ;save interrupt status
cli ;stop interrupts temporarily
mov cl,00010000b ;set the IRQ4 bit OFF to 8259
in al,21h ;read 8259 status
or al,cl ;set bit to 'one' to disable
out 21h,al ;update new interrupt status
;
; 2) At base+3 cut off the DLAB bit to allow access to the
; interrupt enable register (EIR).
;
mov dx,PORT_BASE+3
in al,dx ;read current status
and al,not 10000000b
out dx,al ;turn off DLAB
;
; 3) At base base+1 (IER), disable interrupts to the 8250
;
mov dx,PORT_BASE+1 ;8250 interrupt enable register
mov al,00000000b ;disable all interrupts
out dx,al
;
; 4) At base base+4 (mdm control), disable OUT2 signal
;
mov dx,PORT_BASE+4 ;modem control register
in al,dx
and al,not IRQ_BASE ;reset the out2 bit
out dx,al ;send update
popf ;restore interrupt status
ret
port_stop ENDP
;
;
;-----------------------------------------------------------------------
; ASYNC INTERRUPT MODE -- This routine sets the interrupts modes for
; the comm port either on or off.
;
; Entry:
;
; AL = Mode (0=OFF, 1=ON)
;
; AH = 0 -- Set the Rx-Interrupt mode
; AH = 1 -- Set the Tx-Interrupt mode
; AH = 2 -- Set the Modem Status Interrupt mode
;
;-----------------------------------------------------------------------
;
port_imode PROC near
push ax
push bx
push cx
pushf ;save interrupt status
cli ;stop interrupts temporarily
mov bl,al
or ah,ah
mov bh,00000001b
jz imode0 ;rx-interrupts?
dec ah
mov bh,00000010b
jz imode0 ;tx-interrupt?
dec ah
mov bh,00001000b
jz imode0 ;modem status interrupts?
jmp SHORT imode_end
;
; 1) At base+3 cut off the DLAB bit to allow access to the
; interrupt enable register (EIR).
;
imode0: mov dx,PORT_BASE+3
in al,dx ;read current status
and al,not 10000000b
out dx,al ;turn off DLAB
;
; 2) At base base+1 (IER), set interrupts to the 8250
;
mov dx,PORT_BASE+1 ;8250 interrupt enable register
in al,dx ;read current interrupt state
or bl,bl ;test for on/off flag
jnz imode1
not bh ;set the bit off
and al,bh
jmp SHORT imode2
imode1: or al,bh ;set the bit on
imode2: out dx,al ;send new interrupt mode
imode_end: popf ;restart interrupts
pop cx
pop bx
pop ax
ret
port_imode ENDP
;
;
;-----------------------------------------------------------------------
; CLEAR PENDING INTERRUPTS -- Clear any pending interrupts from the
; 8250 and the 8259 interrupt controller.
;-----------------------------------------------------------------------
;
clear_pints PROC near
mov dx,PORT_BASE+2 ;(IIR) interrupt id register
in al,dx
test al,1
jnz clear_exit ;exit if none pending
xor bh,bh
mov bl,al
call cs:clr_jmp[bx] ;clear the interrupt
jmp SHORT clear_pints
clear_exit: ret
;
; Jump table of routines to call clear interrupts
;
clr_jmp dw OFFSET clr_stat ;clear modem status
dw OFFSET clr_tx ;clear tx-routine
dw OFFSET clr_rx ;clear rx-routine
;
; Clear the interrupt based on the interrupt id code
;
clr_stat: mov dx,PORT_BASE+6 ;cleared by reading
in al,dx ; modem status reg.
ret
clr_tx: ret ;cleared from reading IIR
clr_rx: mov dx,PORT_BASE ;cleared by reading rx-data
in al,dx ; register.
ret
clear_pints ENDP
;
;
PAGE
SUBTTL *** Utility Routines ***
;----------------------------------------------------------------------
; *** These routines compliments of William C. Bryan ***
;
; This routine has two entry points. One entry point allows timing
; in 18ths of seconds, and the other entry point allows in seconds.
;----------------------------------------------------------------------
;
; TIMER -- provides a hardware delay timing in 18ths of a second
;
; Entry: AX = # of 1/18th seconds to delay (65535 Max)
;
;----------------------------------------------------------------------
;
; DELAY -- provides a hardware delay timing in seconds
;
; Entry: AX = # of seconds to delay. (60 Minutes Max)
;
;----------------------------------------------------------------------
;
timer PROC near
push ax ;save all registers changed
push bx
push cx
push dx
begin: call getticks ;read present time into DX
add dx,ax
adc cx,0
cmp cx,018h ;check for 24 hour rollover
jl timer0
cmp dx,0B0h
jl timer0
sub dx,0B0h ;correct for rollover
sbb cx,018h
timer0: mov bx,dx ;AX:BX = Match time
mov ax,cx
timer1: call getticks
cmp dx,bx ;compare match time to current time
jne timer1 ; as a long value
cmp cx,ax
jne timer1
pop dx ;restore altered registers
pop cx
pop bx
pop ax
ret
delay LABEL near
push ax ;save all registers changed
push bx
push cx
push dx
mov cx,ax ;provide correction for 18.2 ms timing
mov ax,182 ;TICKRATE = 18.2 times per second / 10
mul cx ;multiply CX * TICKRATE
mov cx,10
div cx ;now divide by ten, final value in AX
jmp SHORT begin
timer ENDP
;
;
;----------------------------------------------------------------------
; GETTICKS -- get the current BIOS clock ticks value and return
; long value in the DX:CX register.
;----------------------------------------------------------------------
;
getticks PROC near
push ax ;save ax reg
xor ah,ah
int 1Ah ;read time of day
pop ax ;restore ax
ret
getticks ENDP
;
;
;----------------------------------------------------------------------
; BEEP -- Emit a tone for 'BL' durations of ~500ms each.
;----------------------------------------------------------------------
;
beep PROC near
push ax
push cx
mov al,10110110B ;timer mode2, lsb,msb,bin
out PORT_TM+3,al ;write to timer mode reg
mov ax,533h ;divisor for 1000hz
out PORT_TM+2,al ;write timer 2 cnt, lsb.
mov al,ah
out PORT_TM+2,al ;write timer 2 cnt, msb.
in al,PORT_PB ;read port setting
mov ah,al ;save port register
or al,3 ;turn on the speaker
out PORT_PB,al
sub cx,cx
beep1:
loop beep1 ;delay loop
dec bl
jnz beep1 ;loop until BL = 0
mov al,ah ;recover port state
out PORT_PB,al ;restore port state
pop cx
pop ax
ret
beep ENDP
;
;
;-----------------------------------------------------------------------
; CONSOLE PUT CHARACTER -- Print a character on the console and remote.
;
; Entry: AL - Character to Print locally and remote
;
;-----------------------------------------------------------------------
;
con_putc PROC near
mov ah,0Eh ;write tty function
int 10h ;video bios int
ret
con_putc ENDP
;
;
;-----------------------------------------------------------------------
; CONSOLE PUT STRING -- Send the console a string at DS:DI. The string
; must contain a "$" or null character to signal the end of the string.
; If 'int10_skip' is non-zero, characters will not be sent to the modem.
;
; Entry: DS:SI - Points to the string of chars to send
;
;-----------------------------------------------------------------------
;
con_puts PROC near
push ax
cld
con_puts1: lodsb ;get a byte from string
cmp al,'$' ;if '$', end of string
je con_puts2
cmp al,0 ;if null, end of string
je con_puts2
mov ah,0Eh ;write tty function
int 10h ;video bios int
jmp SHORT con_puts1 ;continue looping
con_puts2: pop ax
ret
con_puts ENDP
;
;
; -- All code after this point is trashed after loading --
;
end_resident:
;
PAGE
SUBTTL *** Initialization and Loader Routines ***
;
;-----------------------------------------------------------------------
;--------------------- Start of Loader Code ----------------------------
;-----------------------------------------------------------------------
;
; NOTE: All code after this point is trashed after the connection
; to remote station is established.
;
;-----------------------------------------------------------------------
;
init_msg db 0Dh,0Ah
db "BYE-PC for the IBM Personal Computer - Version "
db PGM_VER+'0',".0",PGM_REV+'0',0Dh,0Ah
db "Copyright (C) MCODE Software 1986, 1987. All rights reserved", 0Dh,0Ah
db "[Local Control use Ctrl-C]"
IFE SMART_MDM
db 0Dh,0Ah,0Dh,0Ah,"Waiting for call...$"
ELSE
db 0Dh,0Ah,0Dh,0Ah,"$"
ENDIF
halt_msg db 0Dh,0Ah
IFE SMART_MDM
db 0Dh,0Ah
ENDIF
IF LOGON_EXE
db "PRESS: <E>xecute locally",0Dh,0Ah
db " <R>esume Call Wait",0Dh,0Ah
ELSE
db "PRESS: <R>esume Call Wait",0Dh,0Ah
ENDIF
db " Return",17,196,217," exit to DOS$"
exit_msg db 0Dh,0Ah,0Dh,0Ah
db "Exiting BYE-PC...."
db 0Dh,0Ah, "$"
bye_msg db 0Dh,0Ah
db " Goodbye, call again...."
db 0Dh,0Ah,0Dh,0Ah, "$"
crlf db 0Dh,0Ah,"$"
mdm_nulls db 0Dh,0Ah
db "Nulls, if needed, (0-9)? ","$"
mdm_flunk db 0Dh,0Ah,0Dh,0Ah
db "You flunked the (0-9) IQ test...Bye!"
db 0Dh,0Ah,0Dh,0Ah, "$"
IF SMART_MDM
error_msg1 db 0Dh,0Ah
db "[ERROR: Modem Initialization!]",0Dh,0Ah
db "$"
ENDIF
IF LOGON_EXE
free_err_msg db 0Dh,0Ah
db "[ERROR: Releasing Memory!]",0Dh,0Ah
db 0Dh,0Ah,0Dh,0Ah, "$"
exec_err_msg db 0Dh,0Ah
db "[ERROR: Executing .COM or .EXE file!]",0Dh,0Ah
db 0Dh,0Ah,0Dh,0Ah, "$"
ENDIF
IF SMART_MDM
mdm_init db "ATE0F1Q0V0M0"
IF BPS_2400
db "X2"
ELSE
db "X1"
ENDIF
db "S0=0S2=3S10=25"
ENDIF
IF TELEBIT
db "S50=0S51=4S52=1S53=1S55=2S58=2S66=1S68=2"
ENDIF
db 0Dh,0Ah,"$"
IF SMART_MDM
mdm_reset db "ATZ",0Dh,0Ah,"$"
mdm_atten db "AT",0Dh,0Ah,"$"
mdm_answer db "ATA",0Dh,0Ah,"$"
mdm_noanswer db "ATS0=0",0Dh,0Ah,"$"
ENDIF
msg_nocd db 0Dh,0Ah
db "[No Carrier]"
db 0Dh,0Ah,"$"
msg_300 db "[300 bps]",0Dh,"$"
IF BPS_1200 OR BPS_2400 OR TELEBIT
msg_1200 db "[1200bps]",0Dh,"$"
ENDIF
IF BPS_2400 OR TELEBIT
msg_2400 db "[2400bps]",0Dh,"$"
ENDIF
IF TELEBIT
msg_tbit db "[Highspeed PEP]",0Dh,"$"
ENDIF
;
locl_flg db NO ;local control flag
res_buf db 16 dup(0) ;modem results code buffer
;
;
PAGE
;-----------------------------------------------------------------------
; Load and Initialize the Program
;-----------------------------------------------------------------------
; INITIALIZE check to see if BYE-PC is already installed. If it is,
; hangup the modem and re-boot for the next call. Otherwise, store all
; of the interrupts present locations, setup rs232 interrupt vector and
; intialize the modem and wait for a call.
;
; NOTE: All subroutines after this point may not be called from
; above this point, as they are discarded after loading.
;
;-----------------------------------------------------------------------
;
initialize PROC near
mov ax,cs ;DS = CS
mov ds,ax
;
; check for BYE already active.
;
mov ax,3516h ;get vector
int 21h
cmp es:bye_sign,1234h ;bye signiture
jne load_bye ;bye not present, load it
;
; Bye is already loaded, say goodby, hangup, and re-boot system
;
mov ah,INT_STAT ;mdm status interrupts.
mov al,OFF ;turn off status ints
call port_imode ; for loss of carrier.
mov si,OFFSET bye_msg ;send goodbye message
call con_puts ; to console and mdm.
mov ax,OFF ;hang up the modem.
call port_dtr
mov ax,2 ;wait a sec or two
call delay
mov ax,ON ;turn dtr back on
call port_dtr
IF SMART_MDM
mov si,OFFSET mdm_noanswer ;tell the mdm to not
call mdm_puts ; answer any calls
ENDIF
mov ax,2 ;wait a sec or two
call delay
ASSUME ds:rom_bios_data ;boot the system
wboot_sys: mov ax,rom_bios_data ;set DS to rom bios data
mov ds,ax ; area.
mov reset_flag, 1234h ;set the alt-ctrl-del flag.
db 0EAH ;jmp to the specified:
dw WBOOT_OFF ; OFFSET
dw WBOOT_SEG ; segment
ASSUME ds:code
;
; Wait for call re-vector interrupts to BYE intercept routines
;
load_bye: mov ah,25h ;set rs232 vector
mov al,RS232_INT
mov dx, OFFSET rs232_serv ;set new rs232 int
int 21h ;make dos call
mov ax,3509h ;dos get vector function
int 21h ;make dos call
mov WORD PTR [old_int09],bx ;set asside old vector to
mov WORD PTR [old_int09 + 2],es ; allow pass to Bios L
mov ax,3510h ;dos get vector function
int 21h ;make dos call
mov WORD PTR [old_int10],bx ;set asside old vector to
mov WORD PTR [old_int10 + 2],es ; allow pass to Bios
mov ax,3513h ;dos get vector function
int 21h ;make dos call
mov WORD PTR [old_int13],bx ;set asside old vector to
mov WORD PTR [old_int13 + 2],es ; allow pass to Bios
mov ax,3516h ;dos get vector function
int 21h ;make dos call
mov WORD PTR [old_int16],bx ;set asside old vector to
mov WORD PTR [old_int16 + 2],es ; allow pass to Bios
mov ax,351Ch ;dos get vector function
int 21h ;make dos call
mov WORD PTR [old_int1C],bx ;set asside old vector to
mov WORD PTR [old_int1C + 2],es ; allow pass to Bios
;
; Wait for a call, then replace the interrupt vectors with the new ones.
;
call call_wait ;wait for a call
mov ax,2509h ;dos set vector function
mov dx, OFFSET int09_main ;set new keybd int
int 21h ;make dos call
mov ax,2510h ;dos set vector function
mov dx, OFFSET int10_main ;set new video int
int 21h ;make dos call
mov ax,2513h ;dos set vector function
mov dx, OFFSET int13_main ;set new disk int
int 21h ;make dos call
mov ax,2516h ;dos set vector function
mov dx, OFFSET int16_main ;set new keyboard int
int 21h ;make dos call
mov ax,251Ch ;dos set vector function
mov dx, OFFSET int1C_main ;set up timer tick int
int 21h ;make dos call
mov ax,2566h ;dos set vector function
mov dx, OFFSET int66_main ;set BYE control int
int 21h ;make dos call
;
; Execute .EXE/.COM file upon after modem is logged on
;
mov trap_flg,ON ;enable ^S & ^C trapping
IF LOGON_EXE
cmp locl_flg,NULL ;if local, cut of mdm
je run_local ; to screen & keybd.
mov int10_merge,NULL ;data 'to' mdm off
mov int16_merge,NULL ;data 'from' mdm off
run_local: mov brk_flg,NULL ;disable remote Ctrl-C
call free_mem ;free memory after loader
call exec_pgm ;run external program
ENDIF
;
; Calculate the size of the program and load resident portion.
;
IF REBOOT
cmp locl_flg,NULL ;if local re-boot
je run_local1
mov ax,2 ;wait a couple secs
call delay
jmp wboot_sys
ENDIF
run_local1: mov dx,OFFSET end_resident ;get end of program area.
mov cl,4 ;resident cant do with an
shr dx,cl ; expression because assembler
inc dx ; can't calc w/relocatable label.
mov ax,3100h ;dos make resident function.
int 21h ;terminate be stay resident.
initialize ENDP
;
;
PAGE
;-----------------------------------------------------------------------
; CALL WAIT -- clears the screen, re-initializes the modem and watches
; the modem for any calls. When a call is detected, the modem answers
; and the program determines the baud rate of the call. If nulls are
; enabled, the caller is prompted for the number of nulls before comp-
; leting the connection.
;-----------------------------------------------------------------------
;
call_wait PROC near
;
; Initialize baud rate, turn on DTR/RTS, and start interrupts
;
IF BPS_300
mov ax,BPS300 ;start at 300 bps
ENDIF
IF BPS_1200
mov ax,BPS1200 ;start at 1200 bps
ENDIF
IF BPS_2400
mov ax,BPS2400 ;start at 2400 bps
ENDIF
IF TELEBIT
mov ax,BPS9600 ;start at 9600 bps
ENDIF
call port_baud ;initialize com port
mov ax,ON ;turn on dtr/rts
call port_dtr ; and IRQ line.
call port_start ;enable interrupts
mov al,ON
mov ah,INT_RX ;start Rx-interrupts
call port_imode
call_restart: mov cx,0
mov ax,0700h ;clear the screen
mov dx,184fh
mov bx,0700h
int 10h
mov ah,2 ;home the cursor
mov dx,0
mov bx,0
int 10h
mov si,OFFSET init_msg ;show sign on message
call con_puts
;
; Reset and initialize the modem for a phone call.
;
IF SMART_MDM
mov si,OFFSET mdm_reset ;reset the modem
call mcon_puts ;send the 'ATZ'
mov ax,2 ;wait for 2 seconds
call delay ;wait for delay time
call rx_flush ;empty trash from Rx buf
mov si,OFFSET mdm_init ;initialization string
call mcon_puts
mov ax,1 ;wait for 1 second
call delay
call rx_flush ;empty trash from Rx buf
mov si,OFFSET mdm_atten ;try to get mdm's attention
call mdm_puts
reset0: call rx_ready ;any data ready?
jz reset0 ;no char waiting
call rx_getc ;is the modem setup ok
cmp al,'0'
je reset1
mov si,OFFSET error_msg1 ;did'nt get back an Ok
call con_puts ; result code of 0.
mov al,BELL ;ring the bell
call con_putc
mov ax,2 ;wait 2 seconds
call delay
jmp call_restart ;try again...
reset1: call con_putc ;show init complete ok
mov si,OFFSET crlf
call con_puts
call rx_flush ;empty trash from Rx buf
ENDIF
;
; Wait for modem to ring or a Ctrl-C at the local console.
;
wloop: mov ah,1 ;check for console keys
int 16h
jz wloop1 ;jmp if no key
mov ah,0
int 16h ;get the key pressed
cmp al,CTRL_C
jne wloop1 ;ctrl-c pressed?
mov si,OFFSET halt_msg ;ask user what to do
call con_puts
;
; Ctrl-C was pressed ask the user what he wants to do.
;
wloop0: mov ah,1 ;check for con key press
int 16h
jz wloop0 ;jmp if no key
mov ah,0
int 16h ;get the key pressed
cmp al,61h ;check for upper case
jl wloop2
cmp al,7Ah
jg wloop2
and al,0DFh ;make upper case
wloop2: cmp al,'R' ;'R' to resume call wait
jne loc_ctrl
jmp call_restart
loc_ctrl:
IF LOGON_EXE
cmp al,'E' ;'E' execute BYE locally
jne loc_ctrl1
mov locl_flg,YES
jmp getnulls
ENDIF
loc_ctrl1: cmp al,CR ;CR to exit BYE to DOS
jne wloop0
loc_ctrl3: mov si,OFFSET exit_msg
call con_puts
mov ax,OFF ;turn off dtr.
call port_dtr
call port_stop ;turn off interrupts
int 20h ;exit to dos
;
; Check the modem to see if its ringing. If it is not a ring ignore
;the data and jump to see if local keyboard has anything.
;
IF SMART_MDM
wloop1: call rx_ready
jz wloop ;no char waiting
call rx_getc ;get char from mdm.
cmp al,'2' ;mdm sent ringing code?
jne wloop1 ; no, try again!
call con_putc ;show character rx-ed
ELSE
wloop1: call port_cd ;cd pin active?
jnc wloop ;jmp if cd not found
ENDIF
ring: mov si,OFFSET crlf ;send a crlf
call con_puts
mov ax,2 ;wait a sec for modem to
call delay ; settle.
IF SMART_MDM
call rx_flush
mov si,OFFSET mdm_answer ;answer the phone call
call mcon_puts ;send the 'ATA' to modem
ENDIF
;
; Get the status of the modem answer request. If no carrier was found
; hang up the modem and recycle for the next call.
;
IF SMART_MDM
mov ax,2 ;wait about 1/2 sec
call delay
mov cx,4 ;up to 4 chars at max
mov di,OFFSET res_buf ;ptr to input buffer
call mdm_gets ;get a string from mdm
mov si,OFFSET res_buf
call con_puts ;display result code
mov ax, WORD PTR res_buf ;read first 2 bytes
mov baud,0
mov bl, '1' ;code "1" = 300bps
mov bh,0 ;Bell 103 compatable
cmp bx,ax
je answer1
IF BPS_1200 OR BPS_2400 OR TELEBIT
mov baud,1
mov bl, '5' ;code "5" = 1200bps
mov bh,0 ;Bell 212A or V.22
cmp bx,ax
je answer1
ENDIF
IF BPS_2400 OR TELEBIT
mov baud,2
mov bl, '1' ;code "10" = 2400bps
mov bh, '0' ;V.22 compatable
cmp bx,ax
je answer1
ENDIF
IF TELEBIT
mov baud,3
mov bl, '5' ;code "50" = 9600bps
mov bh, '0' ;PEP Protocol
cmp bx,ax
je answer1
ENDIF
no_answer: mov si,OFFSET msg_nocd ;show no carrier msg
call con_puts
mov ax,1 ;now wait and recycle
call delay
jmp abort_call
ENDIF
;
; The modem is now locked on to the remote carrier. We must now make
; sure the port is set to the correct baud if not at 1200 buad.
;
answer1:
mov si,OFFSET crlf ;send a cr/lf to console
call con_puts
IF SMART_MDM
mov ax,2 ;let the modem settle
ELSE
mov ax,1 ;let the modem settle
ENDIF ; after carrier lock!
call delay
call rx_flush ;emtpy out rx-buffer
;
; Check for a CR to sync the baud rate on and to determine buad rate.
;
IF SMART_MDM
call set_baud ;set from mdm result code
ELSE
call sync_baud ;set by sync'ing to cr
jnc abort_call ;was baud rate set ok?
ENDIF
;
; Caller is on-line, baud rate is set; start mdm status interrupts
; that check for loss of carrier. Program will re-boot if lost now.
;
mov al,ON ;turn on modem status
mov ah,INT_STAT ; interrupts to test
call port_imode ; for loss of carrier.
;
; Ask the user how many nulls. May need nulls at 2400bps.
;
getnulls:
IF USE_NULLS
call ask_nulls ;ask caller for nulls
jc call_online ;go on if ok
ELSE
jmp call_online
ENDIF
;
; Caller flunked the nulls test or rx-trash on baud sync. Start all over
;
abort_call: mov ah,INT_STAT ;mdm status interrupts off
mov al,OFF ;status int's testing
call port_imode ; for carrier loss.
mov ax,OFF ;hang up the modem.
call port_dtr
mov ax,2 ;let mdm settle after
call delay ; hanging up.
IF BPS_300
mov ax,BPS300 ;start at 300 bps
ENDIF
IF BPS_1200
mov ax,BPS1200 ;start at 1200 bps
ENDIF
IF BPS_2400
mov ax,BPS2400 ;start at 2400 bps
ENDIF
IF TELEBIT
mov ax,BPS9600 ;start at 9600 bps
ENDIF
call port_baud ;initialize com port
call rx_flush ;clear out any trash
mov ax,ON ;DTR/RTS back on
call port_dtr
jmp call_restart ;start all over,
call_online: mov si,OFFSET crlf ;send crlf
call con_puts
ret ;call online rtn completed...
call_wait ENDP
;
;
;-----------------------------------------------------------------------
; ASK NULLS --- Ask the caller for how many nulls to needed.
;
; Exit: Carry Set - Error getting Nulls Response from caller
; Carry Clr - Caller Answered Nulls Question ok
;
;-----------------------------------------------------------------------
;
IF USE_NULLS
ask_nulls PROC near
mov si,OFFSET crlf
call mcon_puts
mov bx,9 ;only 8 tries allowed
getnulls1: dec bx
cmp bx,0
je flunked ;screwed up the iq test?
mov si,OFFSET mdm_nulls
call mcon_puts ;ask nulls question.
getnulls2: call rx_ready
jnz getnulls3
mov ah,1 ;check for console keys
int 16h
jz getnulls2 ;jmp if no key
mov ah,0
int 16h ;get the key pressed
jmp SHORT getnulls4
getnulls3: call rx_getc ;get a response.
getnulls4: call mcon_putc ;show character rx-ed.
cmp al,'0'
jl getnulls1
cmp al,'9'
jg getnulls1
sub al,30h ;convert to 0-9 base
mov nulls,al ;store # of nulls to send
jmp SHORT ask_ok
flunked: mov si,OFFSET mdm_flunk
call mcon_puts ;fall through to abort
ask_err: clc ;clear the carry flag
jmp SHORT ask_ok1
ask_ok: mov si,OFFSET crlf
call mcon_puts
stc ;set carry flag
ask_ok1: ret
ask_nulls ENDP
;
ENDIF
;
;-----------------------------------------------------------------------
; SET BAUD RATE -- Sets the baud rate of caller according to the baud
; rate flag that was set by the modem result code sent on answer.
;
; baud = 0 300bps
; = 1 1200bps
; = 2 2400bps
; = 3 9600bps (Telebit Modem already set to 9600)
;
;-----------------------------------------------------------------------
;
IF SMART_MDM
;
set_baud PROC near
mov al,baud
or al,al
jz set_300 ;0 = 300bps
IF BPS_1200 OR BPS_2400 OR TELEBIT
dec al
jz set_1200 ;1 = 1200bps
ENDIF
IF BPS_2400 OR TELEBIT
dec al
jz set_2400 ;2 = 2400bps
ENDIF
IF TELEBIT
dec al
jz set_9600 ;3 = 9600bps
ENDIF
jmp SHORT set_1200 ;error!, reset to 1200bps
set_300: mov ax,BPS300 ;reset port to 300 bps
mov si,OFFSET msg_300
jmp SHORT set_end
IF BPS_1200 OR BPS_2400 OR TELEBIT
set_1200: mov ax,BPS1200 ;reset port to 1200 bps
mov si,OFFSET msg_1200
jmp SHORT set_end
ENDIF
IF BPS_2400 OR TELEBIT
set_2400: mov ax,BPS2400 ;reset port to 2400 bps
mov si,OFFSET msg_2400
jmp SHORT set_end
ENDIF
IF TELEBIT
set_9600: mov ax,BPS9600 ;reset port to 9600 bps
mov si,OFFSET msg_tbit
ENDIF
set_end:
IFE TELEBIT
call port_baud ;set the baud rate
ENDIF
call con_puts ;show baud rate setting
mov si,OFFSET crlf ; and send a new line
call con_puts
mov ax,2 ;wait a second...
call delay
call rx_flush ;clear any trash
ret
set_baud ENDP
;
ELSE ; Its not a smart modem....
;
;-----------------------------------------------------------------------
; SYNC BAUD RATE -- Look for a CR to determine what baud rate the call
; is at. If unable to lock on to the baud rate then return with the
; carry flag set, else clear the carry flag.
;
; Exit: Carry Set - Baud rate determined and set
; Carry Clr - Error during baud rate sync
;
;-----------------------------------------------------------------------
;
sync_baud PROC near
mov cx, 15 ;try 15 times
sync_baud1: mov ax,BPS300 ;try 300 bps
call port_baud
call rx_flush
mov baud,0
mov si,OFFSET msg_300
call con_puts
call sync_cr
jc sync_ok
IF BPS_1200 OR BPS_2400
mov ax,BPS1200 ;try 1200 bps
call port_baud
call rx_flush
mov baud,1
mov si,OFFSET msg_1200
call con_puts
call sync_cr
jc sync_ok
ENDIF
IF BPS_2400
mov ax,BPS2400 ;try 2400bps
call port_baud
call rx_flush
mov baud,2
mov si,OFFSET msg_2400
call con_puts
call sync_cr
jc sync_ok
ENDIF
loop sync_baud1 ;jmp not enough trash yet.
sync_err: clc ;clear the carry flag
jmp SHORT sync_ok1
sync_ok: stc ;set the carry flag
sync_ok1: ret
sync_baud ENDP
;
;
;-----------------------------------------------------------------------
; SYNC TO <CR> -- Look for a CR/Ctrl-C to determine what baud rate.
;
; Exit: Carry Set - Found a CR or Ctrl-C ok
; Carry Clr - Error getting CR or Ctrl-C
;
;-----------------------------------------------------------------------
;
sync_cr PROC near
push cx
mov cx,20 ;try 20 times
sync_cr0: call rx_getc ;get a character
cmp al,CR
je sync_cr1 ;jump if CR found
cmp al,CTRL_C
je sync_cr1 ;jump if ^C, else
mov ax,1 ; delay for a sec
call timer ; and try again...
loop sync_cr0
clc ;clear carry, error
jmp SHORT sync_cr2
sync_cr1: stc ;set carry, its sync'ed
sync_cr2: pop cx
ret
sync_cr ENDP
;
ENDIF
;
;
;-----------------------------------------------------------------------
; MODEM GET STRING -- Get a string from the modem and place the results
; in the buffer pointed to by DS:SI. This function will input up to 'n'
; characters in the CX register or until a CR or LF is received. Any
; CR or LF received is replaced by a null terminating character.
;
;
; Entry: DS:DI - Buffer to place data in
; CX - Maximum characters to input
;
;-----------------------------------------------------------------------
;
mdm_gets PROC near
cld ;set 'fwd' direction
gets1: call rx_getc ;get the character
cmp ax,EOF ;skip error.
je gets1 ;error reading char?
cmp al, CR ;break on CR
je gets_end
cmp al, LF ;break on LF
je gets_end
mov [di],al ;store it in buffer
inc di
loop gets1
gets_end: mov al,0 ;null terminate string
mov [di],al ;store it in buffer
ret
mdm_gets ENDP
;
;
;-----------------------------------------------------------------------
; MODEM PUT STRING -- Send the modem a string at DS:DI. The string must
; contain a "$" character to signal the end of the string.
;
; Entry: DS:SI - Points to the string of chars to send
;
;-----------------------------------------------------------------------
;
mdm_puts PROC near
push ax
cld
mdmputs1: lodsb ;get a byte from string
cmp al,'$' ;if '$', end of string
je mdmputs2
cmp al,0 ;if null, end of string
je mdmputs2
call tx_putc ;send char to modem
jmp SHORT mdmputs1 ;continue looping
mdmputs2: pop ax
ret
mdm_puts ENDP
;
;
;-----------------------------------------------------------------------
; MODEM & CONSOLE PUT STRING -- Send the modem a string at DS:DI.
; The string must contain a "$" character to signal the end of string.
;
; Entry: DS:SI - Points to the string of chars to send
;
;-----------------------------------------------------------------------
;
mcon_puts PROC near
push ax
cld
cputs1: lodsb ;get a byte from string
cmp al,'$' ;if '$', end of string
je cputs2
cmp al,0 ;if null, end of string
je cputs2
call mcon_putc ;send char to modem & screen
jmp SHORT cputs1 ;continue looping
cputs2: pop ax
ret
mcon_puts ENDP
;
;
;-----------------------------------------------------------------------
; MODEM & CONSOLE PUT CHAR -- Send a character to the local console
; and the modem.
;-----------------------------------------------------------------------
;
mcon_putc PROC near
cmp locl_flg,0 ;local mode?
jne mcon_putc1 ;yes, skip modem
call tx_putc ;send char to modem
mcon_putc1: mov ah,0Eh ;write tty function
int 10h ;video bios call
ret
mcon_putc ENDP
;
;
;-----------------------------------------------------------------------
; FREE MEMORY -- This routine releases the memory used after BYE is
; loaded so that other programs will have memory to execute.
;-----------------------------------------------------------------------
;
IF LOGON_EXE
free_mem PROC near
mov ax,cs ;ES=CS
mov es,ax
mov bx,OFFSET eom ;get end of memory
mov cl,4
shr bx,cl ;BX divided by 16
inc bx ;case not multiple of 16
mov ah,4Ah ;code for memory release
int 21h
jnc free_ok ;memory free ok?
mov al,BELL ;ring the bell
call con_putc
mov si,OFFSET free_err_msg ; no, print error msg
call con_puts ; and abort execute.
free_ok: ret
free_mem ENDP
;
;
;-----------------------------------------------------------------------
; EXECUTE A CHILD PROCESS -- This function will execute either a .COM
; or an .EXE file and return control to the caller.
;-----------------------------------------------------------------------
;
exec_pgm PROC near
mov ax,cs ;ES=CS
mov es,ax
mov dx,OFFSET child_name ;file to execute
mov bx,OFFSET child_parms ;es:bx -> paramater block
mov cx,cs:[2Ch] ;set SEGMENT addrs of
mov environ_seg,cx ; environment blk passed.
mov command_off,80h
mov command_seg,cs
mov fcb1_off,5Ch
mov fcb1_seg,cs
mov fcb2_off,6Ch
mov fcb2_seg,cs
cli
mov stk_ptr,sp ;get stack pointer
mov stk_seg,ss
sti
;
; -- now execute the child process and return control
;
mov ax,4B00h ;execute a child process
int 21h
jnc exec_ok ;exec ok?
mov al,BELL ;ring the bell
call con_putc
mov si,OFFSET exec_err_msg ;no, print error msg
call con_puts ;and abort execute.
exec_ok: mov ax,cs ;set SEGMENTs back up
mov es,ax
mov ds,ax
cli ;halt int's temoporarily
mov ss,stk_seg ;restore stack segment
mov sp,stk_ptr ;restore stack ptr
sti
ret
exec_pgm ENDP
;
;
;-----------------------------------------------------------------------
; --- Data area for executable programs ---
;-----------------------------------------------------------------------
;
child_name db "C:\SYSTEM\XBBS\XBBS.EXE"
db 0
child_parms equ $ ;passed to calling module
environ_seg dw ?
command_off dw ?
command_seg dw ?
fcb1_off dw ?
fcb1_seg dw ?
fcb2_off dw ?
fcb2_seg dw ?
stk_ptr dw ?
stk_seg dw ?
ENDIF
eom equ $ ;end of pgm memory marker
;
;-----------------------------------------------------------------------
; End of Program Code
;-----------------------------------------------------------------------
;
code ENDS ;end of segment
END start ;end of program from start