home *** CD-ROM | disk | FTP | other *** search
- title TALK --- Simple terminal emulator
- page 55,132
- .lfcond ;list false conditionals too
-
- ;
- ; TALK.ASM --- Simple IBM PC terminal emulator
- ;
- ; Copyright (c) 1988 Ray Duncan
- ;
- ; Build: MASM TALK;
- ; LINK TALK;
- ;
- ; Usage: TALK
- ;
-
- stdin equ 0 ; standard input handle
- stdout equ 1 ; standard output handle
- stderr equ 2 ; standard error handle
-
- cr equ 0dh ; ASCII carriage return
- lf equ 0ah ; ASCII line feed
- bsp equ 08h ; ASCII backspace
- escape equ 1bh ; ASCII escape code
-
- dattr equ 07h ; display attribute to use
- ; while in emulation mode
-
- bufsiz equ 4096 ; size of serial port buffer
-
- echo equ 0 ; 0=full-duplex, -1=half-duplex
-
- true equ -1
- false equ 0
-
- com1 equ true ; Use COM1 if nonzero
- com2 equ not com1 ; Use COM2 if nonzero
-
- 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
-
- _TEXT segment word public 'CODE'
-
- assume cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
-
- talk proc far ; entry point from MS-DOS
-
- mov ax,_DATA ; make data segment addressable
- mov ds,ax
- mov es,ax
- ; initialize display for
- ; terminal emulator mode...
-
- mov ah,15 ; get display width and
- int 10h ; current display mode
- dec ah ; save display width for use
- mov columns,ah ; by the screen clear routine
-
- cmp al,7 ; enforce text display mode
- je talk2 ; mode 7 ok, proceed
-
- cmp al,3
- jbe talk2 ; modes 0-3 ok,proceed
-
- mov dx,offset msg1
- mov cx,msg1_len
- jmp talk6 ; print error message and exit
-
- talk2: mov bh,dattr ; clear screen and home cursor
- call cls
-
- call asc_enb ; capture serial port interrupt
- ; vector and enable interrupts
-
- mov dx,offset msg2 ; display message
- mov cx,msg2_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
-
- talk3: 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 talk32 ; not function key, jump
-
- call pc_in ; function key, discard 2nd
- ; character of sequence
- jmp talk5 ; then terminate program
-
- talk32: ; keyboard character received
- if echo
- push ax ; if half-duplex, echo
- call pc_out ; character to PC display
- pop ax
- endif
-
- call com_out ; write char. to serial port
-
- talk4: call com_stat ; serial port character waiting?
- jz talk3 ; nothing waiting, jump
-
- call com_in ; read serial port character
-
- cmp al,20h ; is it control code?
- jae talk45 ; jump if not
-
- call ctrl_code ; control code, process it
-
- jmp talk3 ; check keyboard again
-
- talk45: ; non-control char. received,
- call pc_out ; write it to PC display
-
- jmp talk4 ; see if any more waiting
-
- talk5: ; function key detected,
- ; prepare to terminate...
-
- mov bh,07h ; clear screen and home cursor
- call cls
-
- mov dx,offset msg3 ; display farewell message
- mov cx,msg3_len
-
- talk6: push dx ; save message address
- push cx ; and message length
-
- call asc_dsb ; disable serial port interrupts
- ; and release interrupt vector
-
- pop cx ; restore message length
- pop dx ; and address
-
- mov bx,stdout ; handle for standard output
- 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
-
-
- com_stat proc near ; Check asynch status, returns
- ; Z=false if character ready
- ; Z=true if nothing waiting
- push ax
- mov ax,asc_in ; compare ring buffer pointers
- cmp ax,asc_out
- pop ax
- ret ; return to caller
-
- com_stat endp
-
-
- com_in proc near ; get character from serial
- ; port buffer, returns
- ; new character in AL
-
- 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] ; character is ready,
- ; extract it from buffer
-
- inc bx ; update buffer pointer
- cmp bx,bufsiz
- jne com_in2
- xor bx,bx ; reset pointer if wrapped
- com_in2:
- mov asc_out,bx ; store updated pointer
- pop bx ; restore register BX
- ret ; and return to caller
-
- com_in endp
-
-
- com_out proc near ; write character in AL
- ; to serial port
-
- 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 ; get character to send
- 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 proc near ; read keyboard status, returns
- ; Z=false if character ready
- ; Z=true if nothing waiting.
- ; register DX destroyed.
-
- mov al,in_flag ; if character already
- or al,al ; waiting, return status
- jnz pc_stat1
-
- mov ah,6 ; otherwise call MS-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 ; Z flag set appropriately
-
- pc_stat endp
-
-
- pc_in proc near ; read keyboard character,
- ; return it in AL
- ; DX may be destroyed.
-
- mov al,in_flag ; key already waiting?
- or al,al
- jnz pc_in1 ; yes,return it to caller
-
- call pc_stat ; try and read a character
- jmp pc_in
-
- pc_in1: mov in_flag,0 ; clear char. waiting flag
- mov al,in_char ; and return AL = character
- ret
-
- pc_in endp
-
-
- pc_out proc near ; write character in AL
- ; to the PC's display.
-
- mov ah,0eh ; ROM BIOS Fxn 0EH =
- ; "teletype output"
- push bx ; save register BX
- xor bx,bx ; assume page 0
- int 10h ; transfer to ROM BIOS
- pop bx ; restore register BX
- ret ; and return to caller
-
- pc_out endp
-
-
- cls proc near ; clear display using
- ; char. attribute in BH
- ; registers AX, CX,
- ; and DX destroyed.
-
- mov dl,columns ; set DL,DH = X,Y of
- mov dh,24 ; lower right corner
- mov cx,0 ; set CL,CH=X,Y of
- ; upper left corner
- mov ax,600h ; ROM BIOS Fxn 06H =
- ; "scroll or initialize
- ; window"
- int 10h ; transfer to ROM BIOS
- call home ; set cursor at (0,0)
- ret ; and return to caller
-
- cls endp
-
-
- clreol proc near ; clear from cursor to end
- ; of line using attribute
- ; in BH. Register AX, CX,
- ; and DX destroyed.
-
- call getxy ; get current cursor pos'n
- mov cx,dx ; current position = "upper
- ; left corner" of window
- mov dl,columns ; "lower right corner" X is
- ; max columns, Y is same
- ; as upper left corner
- mov ax,600h ; ROM BIOS Fxn 06H =
- ; "scroll or initialize
- ; window"
- int 10h ; transfer to ROM BIOS
- ret ; return to caller
-
- clreol endp
-
-
- home proc near ; put cursor at home position
-
- mov dx,0 ; set (X,Y) = (0,0)
- call gotoxy ; position the cursor
- ret ; return to caller
-
- home endp
-
-
- gotoxy proc near ; position the cursor
- ; call with DL=X, DH=Y
-
- push bx ; save registers
- push ax
-
- mov bh,0 ; assume page 0
- mov ah,2 ; ROM BIOS Fxn 02H =
- ; set cursor position
- int 10h ; transfer to ROM BIOS
-
- pop ax ; restore registers
- pop bx
- ret ; and return to caller
-
- gotoxy endp
-
-
- getxy proc near ; get cursor position,
- ; Returns DL=X, DH=Y
-
- push ax ; save registers
- push bx
- push cx
-
- mov ah,3 ; ROM BIOS Fxn 03H =
- ; get cursor position
- mov bh,0 ; assume page 0
- int 10h ; transfer to ROM BIOS
-
- pop cx ; restore registers
- pop bx
- pop ax
- ret ; and return to caller
-
- getxy endp
-
-
- ctrl_code proc near ; process control code
- ; call with AL=char
-
- cmp al,cr ; if carriage return
- je ctrl8 ; just send it
-
- cmp al,lf ; if line feed
- je ctrl8 ; just send it
-
- cmp al,bsp ; if backspace
- je ctrl8 ; just send it
-
- cmp al,26 ; is it cls control code?
- jne ctrl7 ; no, jump
-
- mov bh,dattr ; cls control code, clear
- call cls ; screen and home cursor
-
- jmp ctrl9
-
- ctrl7:
- cmp al,escape ; is it Escape character?
- jne ctrl9 ; no, throw it away
-
- call esc_seq ; yes, emulate CRT terminal
- jmp ctrl9
-
- ctrl8: call pc_out ; send CR, LF, or backspace
- ; to the display
-
- ctrl9: ret ; return to caller
-
- ctrl_code endp
-
-
- esc_seq proc near ; decode Televideo 950 escape
- ; sequence for screen control
-
- call com_in ; get next character
- cmp al,84 ; is it clear to end of line?
- jne esc_seq1 ; no, jump
-
- mov bh,dattr ; yes, clear to end of line
- call clreol
- jmp esc_seq2 ; then exit
-
- esc_seq1:
- cmp al,61 ; is it cursor positioning?
- jne esc_seq2 ; no jump
-
- call com_in ; yes, get Y parameter
- sub al,33 ; and remove offset
- mov dh,al
-
- call com_in ; get X parameter
- sub al,33 ; and remove offset
- mov dl,al
- call gotoxy ; position the cursor
-
- esc_seq2: ; return to caller
- ret
-
- esc_seq endp
-
-
- asc_enb proc near ; capture serial port interrupt
- ; vector and enable interrupt
-
- ; save address of previous
- ; interrupt handler...
- mov ax,3500h+com_int ; Fxn 35H = get vector
- int 21h ; transfer to MS-DOS
- mov word ptr oldvec+2,es
- mov word ptr oldvec,bx
-
- ; now install our handler...
- push ds ; save our data segment
- mov ax,cs ; set DS:DX = address
- mov ds,ax ; of our interrupt handler
- mov dx,offset asc_int
- 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 register
- mov al,0bh ; DTR and OUT2 bits
- out dx,al
-
- mov dx,com_ier ; set interrupt enable
- mov al,1 ; register on serial
- out dx,al ; port controller
-
- in al,pic_mask ; read current 8259 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 proc near ; disable interrupt and
- ; release interrupt vector
-
- in al,pic_mask ; read current 8259 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,oldvec ; load address of
- ; previous 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_int proc far ; interrupt service routine
- ; for serial port
-
- 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
-
- cli ; clear interrupts for
- ; pointer manipulation
-
- 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_int1 ; no, jump
- xor bx,bx ; yes,reset pointer
-
- asc_int1: ; store updated pointer
- mov asc_in,bx
-
- sti ; turn interrupts back on
-
- 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_int endp
-
- _TEXT ends
-
-
- _DATA segment word public 'DATA'
-
- in_char db 0 ; PC keyboard input char.
- in_flag db 0 ; <>0 if char waiting
-
- columns db 0 ; highest numbered column in
- ; current display mode (39 or 79)
-
- msg1 db cr,lf
- db 'Display must be text mode.'
- db cr,lf
- msg1_len equ $-msg1
-
- msg2 db 'Terminal emulator running...'
- db cr,lf
- msg2_len equ $-msg2
-
- msg3 db 'Exit from terminal emulator.'
- db cr,lf
- msg3_len equ $-msg3
-
- oldvec dd 0 ; original contents of serial
- ; port interrupt vector
-
- 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
-
-
- STACK segment para stack 'STACK'
-
- db 128 dup (?)
-
- STACK ends
-
- end talk ; defines entry point
-
-