Category : Files from Magazines
Archive   : VOL10N19.ZIP
Filename : TALK.ASM
page 55,132
.lfcond ;list false conditionals too
; TALK.ASM --- Simple terminal emulator program
; that demonstrates use of the DOS 5 Task Switcher API.
; Copyright (c) 1991 Ray Duncan
;
; Set the equates COM1 and ECHO to TRUE or FALSE as appropriate
; for your machine. This program does not set baud rates or
; other communications parameters; use the MODE command instead.
; Exit from TALK.EXE by entering Alt-X.
stdout equ 1 ; standard output handle
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
AltX equ 02dh ; keycode for Alt-X
bufsiz equ 4096 ; size of serial port buffer
true equ -1
false equ 0
com1 equ false ; Use COM1 if nonzero
com2 equ not com1 ; Use COM2 if nonzero
echo equ false ; FALSE=full-duplex,
; TRUE=half-duplex
pic_mask equ 21h ; 8259 int. mask port
pic_eoi equ 20h ; 8259 EOI port
if com1
com_data equ 03f8h ; port assignments for COM1
com_ier equ 03f9h
com_mcr equ 03fch
com_sts equ 03fdh
com_int equ 0ch ; COM1 interrupt number
int_mask equ 10h ; IRQ4 mask for 8259
endif
if com2
com_data equ 02f8h ; port assignments for COM2
com_ier equ 02f9h
com_mcr equ 02fch
com_sts equ 02fdh
com_int equ 0bh ; COM2 interrupt number
int_mask equ 08h ; IRQ3 mask for 8259
endif
_DATA segment word public 'DATA'
in_char db 0 ; PC keyboard input char.
in_flag db 0 ; <>0 if char waiting
msg1 db cr,lf,'Terminal emulator running...',cr,lf
msg1_len equ $-msg1
msg2 db cr,lf,'Exit from terminal emulator.',cr,lf
msg2_len equ $-msg2
oldcomm dd 0 ; original contents of serial
; port interrupt vector
switcher dd 0 ; address of task switcher's
; service entry point
swvers dd 0 ; pointer to SWVERSION structure
; returned by task switcher
cbinfo label word ; switcher callback info structure
cbnext dd 0 ; addr of next structure in chain
cbentry dd callback ; entry point for notify function
dd 0 ; reserved
cbapi dd apiinfo ; addr of list of api info strucs
apiinfo dw 0 ; empty list of api info structures
hooked dw 0 ; nonzero if we are hooked into
; switcher notification chain
asc_in dw 0 ; input pointer to ring buffer
asc_out dw 0 ; output pointer to ring buffer
asc_buf db bufsiz dup (?) ; communications buffer
_DATA ends
_TEXT segment word public 'CODE'
assume cs:_TEXT,ds:_DATA,ss:_STK
talk proc far
mov ax,_DATA ; make our data addressable
mov ds,ax
mov es,ax
call hook ; try and hook switcher
jc talk1 ; jump, function failed
mov hooked,-1 ; set flag that we are chained
talk1: call asc_enb ; enable communications port
mov dx,offset msg1 ; display message
mov cx,msg1_len ; 'Terminal emulator running'
mov bx,stdout ; BX = standard output handle
mov ah,40h ; Fxn 40H = write file or device
int 21h ; transfer to MS-DOS
talk2: call pc_stat ; keyboard character waiting?
jz talk4 ; nothing waiting, jump
call pc_in ; read keyboard character
cmp al,0 ; is it a function key?
jne talk3 ; not function key, jump
call pc_in ; function key, get 2nd part
cmp al,AltX ; was it Alt-X?
je talk5 ; yes, terminate program
push ax ; no, send to comm port
xor al,al ; send lead byte (0)
call com_out
pop ax ; send function key code
call com_out
jmp talk2 ; get another key
talk3: if echo ; keyboard character received
push ax ; if half-duplex, echo
call pc_out ; character to PC display
pop ax
endif
call com_out ; send char to serial port
talk4: call com_stat ; serial port input waiting?
jz talk2 ; nothing waiting, jump
call com_in ; read char from serial port
call pc_out ; send it to PC display
jmp talk4 ; see if any more waiting
talk5: call asc_dsb ; release comm hardware
cmp hooked,0 ; did we hook task switcher?
je talk6 ; no, jump
call unhook ; release switcher hooks
talk6: mov dx,offset msg2 ; display farewell message
mov cx,msg2_len ; DX=addr, CX=length
mov bx,stdout ; BX = standard output handle
mov ah,40h ; Fxn 40H = write device
int 21h ; transfer to MS-DOS
mov ax,4c00h ; terminate program with
int 21h ; return code = 0
talk endp
; The variable OLDMUX holds the previous contents of the Int 2FH
; vector. The variable is in the code segment so that it is
; addressable at interrupt time without changing DS.
oldmux dd 0 ; prev owner of Int 2FH
;
; HOOK - Hook switcher notification chain and multiplex interrupt.
;
; Call with: Nothing
; Returns: Carry = clear if switcher was hooked
; Carry = set if switcher not present or could
; not be hooked.
; Destroys: AX, BX, CX, DX, DI, ES
;
hook proc near
xor bx,bx ; check if switcher present
mov di,bx ; BX, ES:DI must be zero
mov es,bx
mov ax,4b02h ; multiplex function 4b02h
int 2fh ; is detect switcher call
mov ax,es ; check for ES:DI=0
or ax,di
jz hook2 ; jump if no switcher
mov word ptr switcher,di ; save address of switcher
mov word ptr switcher+2,es ; service entry point
xor ax,ax ; get pointer to switcher's
call [switcher] ; version data structure
jc hook2 ; jump, function failed
mov word ptr swvers,bx ; otherwise pointer was
mov word ptr swvers+2,es ; returned in ES:BX
cmp word ptr es:[bx],1 ; check switcher protocol version
jne hook2 ; we insist on version 1.0 or we
cmp word ptr es:[bx+2],0 ; don't hook notification chain
jne hook2 ; jump, not 1.0
mov ax,4 ; now hook notification chain
mov di,ds ; switcher function = 4
mov es,di ; ES:DI = addr of switcher
mov di,offset _DATA:cbinfo ; callback info structure
call [switcher] ; call switcher entry point
jc hook2 ; jump, function failed
mov ax,352fh ; get current vector for
int 21h ; multiplex interrupt 2FH
mov word ptr oldmux+2,es ; and save it
mov word ptr oldmux,bx
push ds ; save our data segment
mov ax,cs ; set DS:DX = address
mov ds,ax ; of our multiplex handler
mov dx,offset muxisr ; to hook Int 2FH
mov ax,252fh ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
clc ; return carry = clear
ret ; if switcher was hooked
hook2: stc ; return carry = set
ret ; if switcher not hooked
hook endp
;
; UNHOOK - Release switcher notification chain and multiplex interrupt.
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, DX, DI, ES
;
unhook proc near
push ds ; save our data segment
lds dx,oldmux ; load address of previous
; multiplex interrupt owner
mov ax,252fh ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
mov ax,5 ; unhook from notify chain
mov di,ds ; switcher function = 5
mov es,di ; ES:DI = address of
mov di,offset _DATA:cbinfo ; callback info structure
call [switcher] ; call switcher entry point
ret
unhook endp
;
; MUXISR - Handler for multiplex interrupt.
;
; Call with: AH = multiplex number, other registers vary.
; Returns: ES:BX = address of callback info structure
; if multiplex function 4BH subfunction 01H detected,
; otherwise changes nothing, chains to previous owner.
; Destroys: Nothing
;
muxisr proc far
cmp ax,4b01h ; is this Build Notify Chain?
je mux2 ; yes, process it
jmp [oldmux] ; no, chain to old handler
mux2: push ds ; save updated switcher
push ax ; entry point address
mov ax,_DATA
mov ds,ax
mov word ptr switcher+2,cx
mov word ptr switcher,dx
pop ax
pop ds
pushf ; call down through the
call [oldmux] ; multiplex handler chain
push ds ; now save address of next
push ax ; guy's callback info structure
mov ax,_DATA ; in our callback info structure
mov ds,ax
mov word ptr cbnext,bx
mov word ptr cbnext+2,es
mov es,ax ; let ES:BX = address of
mov bx,offset _DATA:cbinfo ; our callback info structure
pop ax ; and pass it back to switcher
pop ds
iret ; return from multiplex interrupt
muxisr endp
;
; CALLBACK - Switcher callback function installed by HOOK and MUXISR.
; Looks for session switch notifications and refuses same.
;
; Call with: AX = notification type, other registers vary.
; Returns: AX <> 0 if Query Suspend Session or Suspend Session
; notifications, otherwise AX = 0.
; Destroys: Nothing
;
callback proc far
cmp ax,1 ; is it Query Suspend callback?
je callb2 ; yes, return 1 to block suspend
cmp ax,2 ; is it Suspend Session callback?
je callb2 ; yes, return 1 to block suspend
xor ax,ax ; else return success
callb2: retf
callback endp
;
; COM_STAT - Check communications port status
;
; Call with: Nothing
; Returns: Zero flag = false if character ready
; Zero flag = true if no character waiting
; Destroys: Nothing
;
com_stat proc near
push ax ; save AX
mov ax,asc_in ; compare ring buffer pointers
cmp ax,asc_out ; to set Zero flag
pop ax ; restore AX
ret ; return to caller
com_stat endp
;
; COM_IN - Get character from serial port
;
; Call with: Nothing
; Returns: AL = character
; Destroys: Nothing
;
com_in proc near
push bx ; save register BX
com_in1: ; if no char waiting, wait
mov bx,asc_out ; until one is received
cmp bx,asc_in
je com_in1 ; jump, nothing waiting
mov al,[bx+asc_buf] ; extract char from buffer
inc bx ; update buffer pointer
cmp bx,bufsiz ; time to wrap pointer?
jne com_in2 ; no, jump
xor bx,bx ; yes, reset pointer
com_in2:
mov asc_out,bx ; store updated pointer
pop bx ; restore register BX
ret ; and return to caller
com_in endp
;
; COM_OUT - Transmit character to serial port
;
; Call with: AL = character
; Returns: Nothing
; Destroys: Nothing
;
com_out proc near
push dx ; save register DX
push ax ; save character to send
mov dx,com_sts ; DX = status port address
com_out1: ; check if transmit buffer
in al,dx ; is empty (TBE bit = set)
and al,20h
jz com_out1 ; no, must wait
pop ax ; retrieve character
mov dx,com_data ; DX = data port address
out dx,al ; transmit the character
pop dx ; restore register DX
ret ; and return to caller
com_out endp
;
; PC_STAT - Get keyboard status
;
; Call with: Nothing
; Returns: Zero flag = false if character ready
; Zero flag = true if no character waiting
; Destroys: DX
;
pc_stat proc near
mov al,in_flag ; if character already
or al,al ; waiting, return status
jnz pc_stat1 ; in Zero flag
mov ah,6 ; otherwise call DOS to
mov dl,0ffh ; determine keyboard status
int 21h
jz pc_stat1 ; jump if no key ready
mov in_char,al ; got key, save it for
mov in_flag,0ffh ; "pc_in" routine
pc_stat1: ; return to caller with
ret ; status in Zero flag
pc_stat endp
;
; PC_IN - Get character from keyboard
;
; Call with: Nothing
; Returns: AL = character
; Destroys: DX
;
pc_in proc near
mov al,in_flag ; key already waiting?
or al,al
jnz pc_in1 ; yes,return it to caller
call pc_stat ; no, try and read char.
jmp pc_in
pc_in1: mov in_flag,0 ; clear char. waiting flag
mov al,in_char ; and return AL = char.
ret
pc_in endp
;
; PC_OUT - Send character to PC screen
;
; Call with: AL = character
; Returns: Nothing
; Destroys: AX, DX
;
pc_out proc near
mov dl,al ; DL = char to output
mov ah,6 ; function 6 = direct I/O
int 21h ; transfer to MS-DOS
ret ; return to caller
pc_out endp
;
; ASC_ENB - Enable comm port, capture communications interrupt
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, BX, DX, ES
;
asc_enb proc near
mov ax,3500h+com_int ; save address of previous
int 21h ; owner of comm interrupt
mov word ptr oldcomm+2,es ; function 35H returns
mov word ptr oldcomm,bx ; vector in ES:BX
; install our comm handler
push ds ; save our data segment
mov ax,cs ; DS:DX = address of
mov ds,ax ; comm interrupt handler
mov dx,offset asc_isr
mov ax,2500h+com_int ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
mov dx,com_mcr ; set modem control reg.
mov al,0bh ; DTR and OUT2 bits
out dx,al ; to enable interrupts
mov dx,com_ier ; set interrupt enable
mov al,1 ; register on serial
out dx,al ; port controller
in al,pic_mask ; read 8259 interrupt mask
and al,not int_mask ; set mask for COM port
out pic_mask,al ; write new 8259 mask
ret ; back to caller
asc_enb endp
;
; ASC_DSB - Release communications interrupt, disable comm hardware
;
; Call with: Nothing
; Returns: Nothing
; Destroys: AX, DX
;
asc_dsb proc near
in al,pic_mask ; read 8259 interrupt mask
or al,int_mask ; reset mask for COM port
out pic_mask,al ; write new 8259 mask
push ds ; save our data segment
lds dx,oldcomm ; DX:DX = address of prev
; comm interrupt handler
mov ax,2500h+com_int ; Fxn 25H = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
ret ; back to caller
asc_dsb endp
;
; ASC_ISR - Communications interrupt service routine
; Call with: Nothing
; Returns: Nothing
; Destroys: Nothing
;
asc_isr proc far
sti ; turn interrupts back on
push ax ; save registers
push bx
push dx
push ds
mov ax,_DATA ; make our data segment
mov ds,ax ; addressable
mov dx,com_data ; DX = data port address
in al,dx ; read this character
mov bx,asc_in ; get buffer pointer
mov [asc_buf+bx],al ; store this character
inc bx ; bump pointer
cmp bx,bufsiz ; time for wrap?
jne asc_isr1 ; no, jump
xor bx,bx ; yes, reset pointer
asc_isr1: ; store updated pointer
mov asc_in,bx
mov al,20h ; send EOI to 8259
out pic_eoi,al
pop ds ; restore all registers
pop dx
pop bx
pop ax
iret ; return from interrupt
asc_isr endp
_TEXT ends
_STK segment para stack 'STACK'
db 128 dup (?)
_STK ends
end talk
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
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/