         name      talk
         page      55,132
         .lfcond             ;list false conditionals too
         title     'TALK --- dumb terminal emulator'
;
; TALK --- a dump terminal emulator for the
; IBM PC, to illustrate use of the ROM BIOS
; asynchronous communications card support.
;
; Copyright (c) 1983 Ray Duncan
; May be freely reproduced for non-commercial use.
;
cr       equ       0dh       ;ASCII carriage return
lf       equ       0ah       ;ASCII line feed
esc      equ       1bh       ;ASCII escape code
;
echo     equ       0         ;leave this zero to run
                             ;communications full-duplex,
                             ;change to -1 if half-duplex
;
comm_port  equ     0         ;set to 0 or 1 depending
                             ;on which comm port is
                             ;hooked to your modem
;
;
cseg     segment   para public 'CODE'
;
         assume    cs:cseg,ds:dseg,ss:stack
;
talk     proc      far       ;entry point from PC-DOS
                             ;
         push      ds        ;save DS:0000 on stack
         xor       ax,ax     ;for final exit to PC-DOS.
         push      ax
                             ;make data area addressable
         mov       ax,seg dseg
         mov       ds,ax

                             ;make sure the modem is on-line
         call      com_stat  ;by checking the "Data Set
                             ; Ready" bit in the status word.
         test      al,20h
         jnz       talk1     ;if bit is on, ok to proceed.
                             ;
                             ;bit is not on, print warning
                             ;message and exit.
         mov       dx,offset msg1
         jmp       talk6
                             ;
talk1:                       ;initialize the display to
                             ;reverse video, so the user
                             ;knows he's talking to the modem.
                             ;
         mov       ah,15     ;first use "get mode" function
                             ;of the ROM BIOS video driver to
         int       10h       ;find the number of columns on
         dec       ah        ;the display, save it for use
                             ;by the screen clear routine.
         mov       columns,ah
         cmp       al,7      ;make sure display is text mode.
         je        talk2     ;mode 7 ok, proceed
         cmp       al,3
         jbe       talk2     ;modes 0-3 ok,proceed
         mov       dx,offset msg2
         jmp       talk6     ;print error message and exit
                             ;
talk2:
         mov       bh,70h    ;now clear screen and set to
         call      clear     ;reverse video attribute 70h.
                             ;also put the cursor in the
         call      home      ;upper left corner of screen.
                             ;
                             ;****************************
                             ;this is the main loop of
                             ;the communications program.
                             ;****************************
talk3:   call      pc_stat   ;check character waiting
                             ;from the IBM PC keyboard
         jz        talk4     ;no
         call      pc_in     ;read char. from PC keyboard
         cmp       al,esc    ;is it the ESCape key?
         je        talk5     ;yes, exit the terminal emulator

                             ;if running half-duplex, echo
                             ;the character to the PC display
         if        echo
         push      ax        ;save copy of the character
         call      pc_out    ;send it to the PC display
         pop       ax        ;now restore the character
         endif

         call      com_out   ;write char. to the comm port
                             ;
talk4:   call      com_stat  ;check if character waiting
                             ;from the comm port
         jz        talk3     ;no, loop
         call      com_in    ;read char. from comm port
         call      pc_out    ;write it to the PC display
                             ;
                             ;now do it all again
         jmp       talk3
                             ;
talk5:                       ;ESC key detected, prepare
                             ;to exit the terminal emulator.
                             ;
                             ;first set the display back
                             ;to normal video, so the user
                             ;will know he's talking to
                             ;the PC and not the modem.

         mov       bh,07h    ;7 is the "attribute" for
         call      clear     ;normal video display.
         call      home      ;also put cursor in upper
                             ;left hand corner of screen.
                             ;
                             ;print farewell message
         mov       dx,offset msg3
talk6:   mov       ah,9      ;use PC-DOS function 9 to
         int       21h       ;print the string whose address
                             ;is in register DX.
         ret                 ;now return to PC-DOS.
                             ;
talk     endp
;
                             ;this routine reads status
                             ;from the COM port, returns
                             ;Z=false if character ready
                             ;Z=true if nothing waiting
                             ;AH=line status,AL=modem status
                             ;register DX destroyed.
com_stat proc      near
         mov       dx,comm_port
         mov       ah,3      ;use ROM BIOS driver's function
         int       14h       ;3 to get status, also
         test      ah,098h   ;check comm port error flags
                             ;for timeout,break,framing error.
         jnz       com_err   ;error was detected, beep.

com_stat1:                   ;test the data ready bit,
         test      ah,1      ;returning Z flag=false if
         ret                 ;character waiting.

com_err: push      ax        ;communications error detected
         mov       al,7
         call      pc_out    ;send a bell code to the PC
         pop       ax        ;then go return comm status
         jmp       com_stat1

com_stat endp
;
                             ;read a character from the
                             ;COM port, return it in AL.
                             ;register DX destroyed.
com_in   proc      near
         mov       dx,comm_port
         mov       ah,2      ;use ROM BIOS driver's
         int       14h       ;function 2 to get char.
         ret
com_in   endp
;
                             ;write the character in AL
                             ;to the COM port.
                             ;register DX destroyed.
com_out  proc      near
         mov       dx,comm_port
         mov       ah,1      ;use ROM BIOS driver's
         int       14h       ;function 1 to send char.
         ret
com_out  endp
;
                             ;read status for the IBM
                             ;PC's keyboard, returns
                             ;Z=false if character ready
                             ;Z=true if nothing waiting.
                             ;register DX destroyed.
pc_stat  proc      near
                             ;if a character is already
                             ;waiting,just return status
         mov       al,in_char
         or        al,al
         jnz       pc_stat1
         mov       ah,6      ;otherwise call PC-DOS to
         mov       dl,0ffh   ;determine status
         int       21h
         jz        pc_stat1  ;jump,nothing ready
                             ;got a char, save it for
                             ;"pc_in" routine.
         mov       in_char,al
pc_stat1:                    ;return to caller with
         ret                 ;Z flag set appropriately
pc_stat  endp
;
                             ;read a character from the
                             ;IBM PC's keyboard, return
                             ;it in AL.  DX may be destroyed.
pc_in    proc      near
         mov       al,in_char
         or        al,al     ;any character waiting?
         jnz       pc_in1    ;yes,return it to caller
         call      pc_stat   ;try and read a character
         jmp       pc_in
pc_in1:  xor       ah,ah     ;clear the character
                             ;waiting flag
         mov       in_char,ah
         ret                 ;exit with AL=char

pc_in    endp
;
                             ;write the character in AL
                             ;to the PC's display.
                             ;register DX destroyed.
pc_out   proc      near
         mov       dl,al     ;we use PC-DOS function 6 to
         mov       ah,6      ;send the character, it ignores
         int       21h       ;control characters so they
         ret                 ;can be passed on to the
pc_out   endp                ;remote system.
;
clear    proc      near      ;clear the display and set
                             ;it to the attribute in BH.
                             ;registers AX, CX, DX destroyed.
         mov       dl,columns
         mov       dh,24     ;DL,DH=X,Y of lower right
                             ;corner of "window".
         mov       cx,0      ;CL,CH=X,Y of upper left
                             ;corner of "window".
         mov       ax,600h   ;AH=6 for "scroll or initialize
                             ;window" function, AL=0 for
                             ;number of lines to scroll.
         int       10h       ;call ROM BIOS video driver.
         ret
clear    endp
;
                             ;home the cursor, i.e.
                             ;put it in the upper left
                             ;corner of the screen.
                             ;registers AX,BX,DX destroyed.
home     proc      near
         mov       dx,0      ;(DL,DH)=(X,Y) for new
                             ;cursor position, both are 0.
         mov       bh,0      ;BH=video page
         mov       ah,2      ;function 2=set cursor
         int       10h       ;call ROM BIOS video driver.
         ret
home     endp
;
cseg     ends
;
;
dseg     segment   para 'DATA'
                             ;
in_char  db        0         ;PC keyboard input char.
                             ;
columns  db        0         ;highest numbered column in
                             ;current display mode (39 or 79)
                             ;
msg1     db        cr,lf,'Check your modem.'
         db        cr,lf,'$'
msg2     db        cr,lf,'Display must be text mode.'
         db        cr,lf,'$'
msg3     db        cr,lf,lf,'Goodbye.'
         db        cr,lf,'$'
dseg     ends
;
;
stack    segment   para stack 'STACK'
                             ;allow 64 bytes in this case
         db        64 dup (?)
stack    ends
;
         end       talk
