home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / advmsdos / chap07 / talk.asm next >
Encoding:
Assembly Source File  |  1988-10-01  |  18.8 KB  |  571 lines

  1.     title    TALK --- Simple terminal emulator
  2.         page    55,132
  3.         .lfcond             ;list false conditionals too
  4.  
  5. ;
  6. ; TALK.ASM  --- Simple IBM PC terminal emulator
  7. ; Copyright (c) 1988 Ray Duncan
  8. ;
  9. ; Build:  MASM TALK;
  10. ;         LINK TALK;
  11. ;
  12. ; Usage:  TALK
  13. ;
  14.  
  15. stdin   equ     0               ; standard input handle
  16. stdout  equ     1               ; standard output handle
  17. stderr  equ     2               ; standard error handle
  18.  
  19. cr      equ     0dh             ; ASCII carriage return
  20. lf      equ     0ah             ; ASCII line feed
  21. bsp     equ     08h             ; ASCII backspace
  22. escape  equ     1bh             ; ASCII escape code
  23.  
  24. dattr   equ     07h             ; display attribute to use
  25.                                 ; while in emulation mode
  26.  
  27. bufsiz  equ     4096            ; size of serial port buffer
  28.  
  29. echo    equ     0               ; 0=full-duplex, -1=half-duplex
  30.  
  31. true    equ     -1
  32. false   equ     0
  33.  
  34. com1    equ     true            ; Use COM1 if nonzero
  35. com2    equ     not com1        ; Use COM2 if nonzero
  36.  
  37. pic_mask  equ   21h             ; 8259 int. mask port
  38. pic_eoi   equ   20h             ; 8259 EOI port
  39.         
  40.         if      com1
  41. com_data equ    03f8h           ; port assignments for COM1 
  42. com_ier  equ    03f9h
  43. com_mcr  equ    03fch
  44. com_sts  equ    03fdh
  45. com_int  equ    0ch             ; COM1 interrupt number
  46. int_mask equ    10h             ; IRQ4 mask for 8259
  47.         endif
  48.  
  49.         if      com2
  50. com_data equ    02f8h           ; port assignments for COM2
  51. com_ier  equ    02f9h
  52. com_mcr  equ    02fch
  53. com_sts  equ    02fdh
  54. com_int  equ    0bh             ; COM2 interrupt number 
  55. int_mask equ    08h             ; IRQ3 mask for 8259
  56.         endif
  57.  
  58. _TEXT   segment word public 'CODE'
  59.  
  60.         assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  61.  
  62. talk    proc    far             ; entry point from MS-DOS
  63.  
  64.         mov     ax,_DATA        ; make data segment addressable 
  65.         mov     ds,ax
  66.         mov     es,ax
  67.                                 ; initialize display for 
  68.                                 ; terminal emulator mode...
  69.  
  70.         mov     ah,15           ; get display width and 
  71.         int     10h             ; current display mode
  72.         dec     ah              ; save display width for use
  73.         mov     columns,ah      ; by the screen clear routine
  74.  
  75.         cmp     al,7            ; enforce text display mode
  76.         je      talk2           ; mode 7 ok, proceed
  77.  
  78.         cmp     al,3
  79.         jbe     talk2           ; modes 0-3 ok,proceed
  80.  
  81.         mov     dx,offset msg1
  82.         mov     cx,msg1_len
  83.         jmp     talk6           ; print error message and exit
  84.  
  85. talk2:  mov     bh,dattr        ; clear screen and home cursor
  86.         call    cls
  87.  
  88.         call    asc_enb         ; capture serial port interrupt
  89.                                 ; vector and enable interrupts
  90.  
  91.         mov     dx,offset msg2  ; display message
  92.         mov     cx,msg2_len     ; 'Terminal emulator running'
  93.         mov     bx,stdout       ; BX = standard output handle
  94.         mov     ah,40h          ; Fxn 40H = write file or device
  95.         int     21h             ; transfer to MS-DOS
  96.  
  97. talk3:  call    pc_stat         ; keyboard character waiting?
  98.         jz      talk4           ; nothing waiting, jump
  99.  
  100.         call    pc_in           ; read keyboard character
  101.  
  102.         cmp     al,0            ; is it a function key?
  103.         jne     talk32          ; not function key, jump
  104.  
  105.         call    pc_in           ; function key, discard 2nd
  106.                                 ; character of sequence
  107.         jmp     talk5           ; then terminate program
  108.  
  109. talk32:                         ; keyboard character received
  110.         if      echo
  111.         push    ax              ; if half-duplex, echo
  112.         call    pc_out          ; character to PC display
  113.         pop     ax              
  114.         endif
  115.  
  116.         call    com_out         ; write char. to serial port
  117.  
  118. talk4:  call    com_stat        ; serial port character waiting?
  119.         jz      talk3           ; nothing waiting, jump
  120.  
  121.         call    com_in          ; read serial port character
  122.  
  123.         cmp     al,20h          ; is it control code?
  124.         jae     talk45          ; jump if not
  125.  
  126.         call    ctrl_code       ; control code, process it
  127.  
  128.         jmp     talk3           ; check keyboard again
  129.  
  130. talk45:                         ; non-control char. received,
  131.         call    pc_out          ; write it to PC display
  132.  
  133.         jmp     talk4           ; see if any more waiting
  134.  
  135. talk5:                          ; function key detected, 
  136.                                 ; prepare to terminate...
  137.  
  138.         mov     bh,07h          ; clear screen and home cursor
  139.         call    cls
  140.  
  141.         mov     dx,offset msg3  ; display farewell message
  142.         mov     cx,msg3_len
  143.  
  144. talk6:  push    dx              ; save message address
  145.         push    cx              ; and message length
  146.  
  147.         call    asc_dsb         ; disable serial port interrupts
  148.                                 ; and release interrupt vector
  149.  
  150.         pop     cx              ; restore message length
  151.         pop     dx              ; and address
  152.  
  153.         mov     bx,stdout       ; handle for standard output
  154.         mov     ah,40h          ; Fxn 40H = write device
  155.         int     21h             ; transfer to MS-DOS    
  156.  
  157.         mov     ax,4c00h        ; terminate program with
  158.         int     21h             ; return code = 0
  159.  
  160. talk    endp
  161.  
  162.  
  163. com_stat proc   near            ; Check asynch status, returns  
  164.                                 ; Z=false if character ready
  165.                                 ; Z=true if nothing waiting
  166.         push    ax
  167.         mov     ax,asc_in       ; compare ring buffer pointers
  168.         cmp     ax,asc_out
  169.         pop     ax
  170.         ret                     ; return to caller      
  171.  
  172. com_stat endp
  173.  
  174.  
  175. com_in  proc    near            ; get character from serial
  176.                                 ; port buffer, returns
  177.                                 ; new character in AL
  178.  
  179.         push    bx              ; save register BX
  180.  
  181. com_in1:                        ; if no char waiting, wait
  182.         mov     bx,asc_out      ; until one is received
  183.         cmp     bx,asc_in
  184.         je      com_in1         ; jump, nothing waiting
  185.  
  186.         mov     al,[bx+asc_buf] ; character is ready,
  187.                                 ; extract it from buffer
  188.  
  189.         inc     bx              ; update buffer pointer 
  190.         cmp     bx,bufsiz
  191.         jne     com_in2
  192.         xor     bx,bx           ; reset pointer if wrapped
  193. com_in2:                        
  194.         mov     asc_out,bx      ; store updated pointer
  195.         pop     bx              ; restore register BX
  196.         ret                     ; and return to caller
  197.  
  198. com_in  endp
  199.  
  200.  
  201. com_out proc    near            ; write character in AL 
  202.                                 ; to serial port   
  203.  
  204.         push    dx              ; save register DX
  205.         push    ax              ; save character to send
  206.         mov     dx,com_sts      ; DX = status port address
  207.  
  208. com_out1:                       ; check if transmit buffer
  209.         in      al,dx           ; is empty (TBE bit = set)
  210.         and     al,20h
  211.         jz      com_out1        ; no, must wait
  212.  
  213.         pop     ax              ; get character to send
  214.         mov     dx,com_data     ; DX = data port address
  215.         out     dx,al           ; transmit the character
  216.         pop     dx              ; restore register DX
  217.         ret                     ; and return to caller
  218.  
  219. com_out endp
  220.  
  221.  
  222. pc_stat proc    near            ; read keyboard status, returns
  223.                                 ; Z=false if character ready
  224.                                 ; Z=true if nothing waiting.
  225.                                 ; register DX destroyed.
  226.  
  227.         mov     al,in_flag      ; if character already    
  228.         or      al,al           ; waiting, return status   
  229.         jnz     pc_stat1
  230.  
  231.         mov     ah,6            ; otherwise call MS-DOS to
  232.         mov     dl,0ffh         ; determine keyboard status
  233.         int     21h
  234.  
  235.         jz      pc_stat1        ; jump if no key ready
  236.  
  237.         mov     in_char,al      ; got key, save it for
  238.         mov     in_flag,0ffh    ; "pc_in" routine
  239.  
  240. pc_stat1:                       ; return to caller with
  241.         ret                     ; Z flag set appropriately
  242.  
  243. pc_stat endp
  244.  
  245.  
  246. pc_in   proc    near            ; read keyboard character,
  247.                                 ; return it in AL
  248.                                 ; DX may be destroyed.
  249.  
  250.         mov     al,in_flag      ; key already waiting?  
  251.         or      al,al
  252.         jnz     pc_in1          ; yes,return it to caller
  253.  
  254.         call    pc_stat         ; try and read a character
  255.         jmp     pc_in
  256.  
  257. pc_in1: mov     in_flag,0       ; clear char. waiting flag
  258.         mov     al,in_char      ; and return AL = character
  259.         ret 
  260.  
  261. pc_in   endp
  262.  
  263.  
  264. pc_out  proc    near            ; write character in AL
  265.                                 ; to the PC's display.
  266.  
  267.         mov     ah,0eh          ; ROM BIOS Fxn 0EH = 
  268.                                 ; "teletype output"
  269.         push    bx              ; save register BX
  270.         xor     bx,bx           ; assume page 0
  271.         int     10h             ; transfer to ROM BIOS
  272.         pop     bx              ; restore register BX
  273.         ret                     ; and return to caller
  274.  
  275. pc_out  endp
  276.  
  277.  
  278. cls     proc    near            ; clear display using 
  279.                                 ; char. attribute in BH
  280.                                 ; registers AX, CX, 
  281.                                 ; and DX destroyed.
  282.  
  283.         mov     dl,columns      ; set DL,DH = X,Y of
  284.         mov     dh,24           ; lower right corner
  285.         mov     cx,0            ; set CL,CH=X,Y of
  286.                                 ; upper left corner
  287.         mov     ax,600h         ; ROM BIOS Fxn 06H =
  288.                                 ; "scroll or initialize
  289.                                 ; window" 
  290.         int     10h             ; transfer to ROM BIOS
  291.         call    home            ; set cursor at (0,0)
  292.         ret                     ; and return to caller
  293.  
  294. cls     endp
  295.  
  296.  
  297. clreol  proc    near            ; clear from cursor to end
  298.                                 ; of line using attribute
  299.                                 ; in BH.  Register AX, CX,
  300.                                 ; and DX destroyed.
  301.  
  302.         call    getxy           ; get current cursor pos'n
  303.         mov     cx,dx           ; current position = "upper 
  304.                                 ; left corner" of window
  305.         mov     dl,columns      ; "lower right corner" X is 
  306.                                 ; max columns, Y is same
  307.                                 ; as upper left corner
  308.         mov     ax,600h         ; ROM BIOS Fxn 06H = 
  309.                                 ; "scroll or initialize
  310.                                 ; window"
  311.         int     10h             ; transfer to ROM BIOS
  312.         ret                     ; return to caller
  313.  
  314. clreol  endp
  315.  
  316.  
  317. home    proc    near            ; put cursor at home position
  318.  
  319.         mov     dx,0            ; set (X,Y) = (0,0)
  320.         call    gotoxy          ; position the cursor
  321.         ret                     ; return to caller
  322.  
  323. home    endp
  324.  
  325.  
  326. gotoxy  proc    near            ; position the cursor
  327.                                 ; call with DL=X, DH=Y
  328.  
  329.         push    bx              ; save registers
  330.         push    ax
  331.  
  332.         mov     bh,0            ; assume page 0
  333.         mov     ah,2            ; ROM BIOS Fxn 02H = 
  334.                                 ; set cursor position
  335.         int     10h             ; transfer to ROM BIOS
  336.  
  337.         pop     ax              ; restore registers
  338.         pop     bx
  339.         ret                     ; and return to caller
  340.  
  341. gotoxy  endp
  342.  
  343.  
  344. getxy   proc    near            ; get cursor position,
  345.                                 ; Returns DL=X, DH=Y
  346.  
  347.         push    ax              ; save registers
  348.         push    bx
  349.         push    cx
  350.  
  351.         mov     ah,3            ; ROM BIOS Fxn 03H =
  352.                                 ; get cursor position
  353.         mov     bh,0            ; assume page 0
  354.         int     10h             ; transfer to ROM BIOS
  355.  
  356.         pop     cx              ; restore registers
  357.         pop     bx
  358.         pop     ax
  359.         ret                     ; and return to caller
  360.  
  361. getxy   endp
  362.  
  363.  
  364. ctrl_code proc  near            ; process control code
  365.                                 ; call with AL=char
  366.  
  367.         cmp     al,cr           ; if carriage return
  368.         je      ctrl8           ; just send it  
  369.  
  370.         cmp     al,lf           ; if line feed
  371.         je      ctrl8           ; just send it
  372.  
  373.         cmp     al,bsp          ; if backspace
  374.         je      ctrl8           ; just send it
  375.  
  376.         cmp     al,26           ; is it cls control code?
  377.         jne     ctrl7           ; no, jump
  378.  
  379.         mov     bh,dattr        ; cls control code, clear
  380.         call    cls             ; screen and home cursor
  381.  
  382.         jmp     ctrl9
  383.  
  384. ctrl7:
  385.         cmp     al,escape       ; is it Escape character?
  386.         jne     ctrl9           ; no, throw it away
  387.  
  388.         call    esc_seq         ; yes, emulate CRT terminal
  389.         jmp     ctrl9
  390.  
  391. ctrl8:  call    pc_out          ; send CR, LF, or backspace
  392.                                 ; to the display
  393.  
  394. ctrl9:  ret                     ; return to caller
  395.  
  396. ctrl_code endp
  397.  
  398.  
  399. esc_seq proc    near            ; decode Televideo 950 escape
  400.                                 ; sequence for screen control
  401.  
  402.         call    com_in          ; get next character
  403.         cmp     al,84           ; is it clear to end of line?
  404.         jne     esc_seq1        ; no, jump
  405.  
  406.         mov     bh,dattr        ; yes, clear to end of line
  407.         call    clreol
  408.         jmp     esc_seq2        ; then exit     
  409.  
  410. esc_seq1:
  411.         cmp     al,61           ; is it cursor positioning?
  412.         jne     esc_seq2        ; no jump
  413.  
  414.         call    com_in          ; yes, get Y parameter
  415.         sub     al,33           ; and remove offset
  416.         mov     dh,al
  417.  
  418.         call    com_in          ; get X parameter
  419.         sub     al,33           ; and remove offset
  420.         mov     dl,al
  421.         call    gotoxy          ; position the cursor 
  422.  
  423. esc_seq2:                       ; return to caller
  424.         ret
  425.  
  426. esc_seq endp
  427.  
  428.  
  429. asc_enb proc    near            ; capture serial port interrupt
  430.                                 ; vector and enable interrupt
  431.  
  432.                                 ; save address of previous
  433.                                 ; interrupt handler...
  434.         mov     ax,3500h+com_int ; Fxn 35H = get vector
  435.         int     21h             ; transfer to MS-DOS
  436.         mov     word ptr oldvec+2,es
  437.         mov     word ptr oldvec,bx
  438.  
  439.                                 ; now install our handler...
  440.         push    ds              ; save our data segment
  441.         mov     ax,cs           ; set DS:DX = address
  442.         mov     ds,ax           ; of our interrupt handler
  443.         mov     dx,offset asc_int
  444.         mov     ax,2500h+com_int ; Fxn 25H = set vector
  445.         int     21h             ; transfer to MS-DOS
  446.         pop     ds              ; restore data segment
  447.         
  448.         mov     dx,com_mcr      ; set modem control register
  449.         mov     al,0bh          ; DTR and OUT2 bits
  450.         out     dx,al
  451.  
  452.         mov     dx,com_ier      ; set interrupt enable 
  453.         mov     al,1            ; register on serial
  454.         out     dx,al           ; port controller
  455.  
  456.         in      al,pic_mask     ; read current 8259 mask
  457.         and     al,not int_mask ; set mask for COM port
  458.         out     pic_mask,al     ; write new 8259 mask
  459.  
  460.         ret                     ; back to caller
  461.  
  462. asc_enb endp
  463.          
  464.  
  465. asc_dsb proc    near            ; disable interrupt and 
  466.                                 ; release interrupt vector
  467.         
  468.         in      al,pic_mask     ; read current 8259 mask
  469.         or      al,int_mask     ; reset mask for COM port
  470.         out     pic_mask,al     ; write new 8259 mask
  471.  
  472.         push    ds              ; save our data segment
  473.         lds     dx,oldvec       ; load address of
  474.                                 ; previous interrupt handler    
  475.         mov     ax,2500h+com_int ; Fxn 25H = set vector
  476.         int     21h             ; transfer to MS-DOS    
  477.         pop     ds              ; restore data segment
  478.  
  479.         ret                     ; back to caller
  480.  
  481. asc_dsb endp
  482.  
  483.  
  484. asc_int proc    far             ; interrupt service routine
  485.                                 ; for serial port
  486.  
  487.         sti                     ; turn interrupts back on
  488.  
  489.         push    ax              ; save registers
  490.         push    bx
  491.         push    dx
  492.         push    ds
  493.  
  494.         mov     ax,_DATA        ; make our data segment 
  495.         mov     ds,ax           ; addressable
  496.  
  497.         cli                     ; clear interrupts for
  498.                                 ; pointer manipulation
  499.  
  500.         mov     dx,com_data     ; DX = data port address
  501.         in      al,dx           ; read this character
  502.  
  503.         mov     bx,asc_in       ; get buffer pointer
  504.         mov     [asc_buf+bx],al ; store this character
  505.         inc     bx              ; bump pointer
  506.         cmp     bx,bufsiz       ; time for wrap?
  507.         jne     asc_int1        ; no, jump
  508.         xor     bx,bx           ; yes,reset pointer
  509.  
  510. asc_int1:                       ; store updated pointer
  511.         mov     asc_in,bx
  512.  
  513.         sti                     ; turn interrupts back on
  514.  
  515.         mov     al,20h          ; send EOI to 8259
  516.         out     pic_eoi,al
  517.  
  518.         pop     ds              ; restore all registers
  519.         pop     dx
  520.         pop     bx
  521.         pop     ax
  522.  
  523.         iret                    ; return from interrupt
  524.  
  525. asc_int endp
  526.  
  527. _TEXT   ends
  528.  
  529.  
  530. _DATA   segment word public 'DATA'
  531.  
  532. in_char db      0               ; PC keyboard input char.
  533. in_flag db      0               ; <>0 if char waiting
  534.  
  535. columns db      0               ; highest numbered column in
  536.                                 ; current display mode (39 or 79)
  537.  
  538. msg1    db      cr,lf
  539.         db      'Display must be text mode.'
  540.         db      cr,lf
  541. msg1_len equ $-msg1
  542.  
  543. msg2    db      'Terminal emulator running...'
  544.         db      cr,lf
  545. msg2_len equ $-msg2
  546.  
  547. msg3    db      'Exit from terminal emulator.'
  548.         db      cr,lf
  549. msg3_len equ $-msg3
  550.  
  551. oldvec  dd      0               ; original contents of serial 
  552.                                 ; port interrupt vector
  553.  
  554. asc_in  dw      0               ; input pointer to ring buffer
  555. asc_out dw      0               ; output pointer to ring buffer
  556.  
  557. asc_buf db      bufsiz dup (?)  ; communications buffer
  558.  
  559. _DATA   ends
  560.  
  561.  
  562. STACK   segment para stack 'STACK'
  563.  
  564.         db      128 dup (?)
  565.  
  566. STACK   ends
  567.  
  568.         end     talk            ;  defines entry point  
  569.  
  570.