Category : Files from Magazines
Archive   : VOL10N19.ZIP
Filename : TALK.ASM

 
Output of file : TALK.ASM contained in archive : VOL10N19.ZIP
title TALK --- Simple terminal emulator
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



  3 Responses to “Category : Files from Magazines
Archive   : VOL10N19.ZIP
Filename : TALK.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/