home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / advdos.doc / s1c07 < prev    next >
Encoding:
Text File  |  1992-04-21  |  38.2 KB  |  954 lines

  1. ────────────────────────────────────────────────────────────────────────────
  2. Chapter 7  Printer and Serial Port
  3.  
  4.   MS-DOS supports printers, plotters, modems, and other hard-copy output or
  5.   communication devices with device drivers for parallel ports and serial
  6.   ports. Parallel ports are so named because they transfer a byte──8 bits──
  7.   in parallel to the destination device over eight separate physical paths
  8.   (plus additional status and handshaking signals). The serial port, on the
  9.   other hand, communicates with the CPU with bytes but sends data to or
  10.   receives data from its destination device serially──a bit at a time──over
  11.   a single physical connection.
  12.  
  13.   Parallel ports are typically used for high-speed output devices, such as
  14.   line printers, over relatively short distances (less than 50 feet). They
  15.   are rarely used for devices that require two-way communication with the
  16.   computer. Serial ports are used for lower-speed devices, such as modems
  17.   and terminals, that require two-way communication (although some printers
  18.   also have serial interfaces). A serial port can drive its device reliably
  19.   over much greater distances (up to 1000 feet) over as few as three wires──
  20.   transmit, receive, and ground.
  21.  
  22.   The most commonly used type of serial interface follows a standard called
  23.   RS-232. This standard specifies a 25-wire interface with certain
  24.   electrical characteristics, the use of various handshaking signals, and a
  25.   standard DB-25 connector. Other serial-interface standards exist──for
  26.   example, the RS-422, which is capable of considerably higher speeds than
  27.   the RS-232── but these are rarely used in personal computers (except for
  28.   the Apple Macintosh) at this time.
  29.  
  30.   MS-DOS has built-in device drivers for three parallel adapters, and for
  31.   two serial adapters on the PC or PC/AT and three serial adapters on the
  32.   PS/2. The logical names for these devices are LPT1, LPT2, LPT3, COM1,
  33.   COM2, and COM3. The standard printer (PRN) and standard auxiliary (AUX)
  34.   devices are normally aliased to LPT1 and COM1, but you can redirect PRN to
  35.   one of the serial ports with the MS-DOS MODE command.
  36.  
  37.   As with keyboard and video display I/O, you can manage printer and
  38.   serial-port I/O at several levels that offer different degrees of
  39.   flexibility and hardware independence:
  40.  
  41.   ■  MS-DOS handle-oriented functions
  42.  
  43.   ■  MS-DOS traditional character functions
  44.  
  45.   ■  IBM ROM BIOS driver functions
  46.  
  47.   In the case of the serial port, direct control of the hardware by
  48.   application programs is also common. I will discuss each of these I/O
  49.   methods briefly, with examples, in the following pages.
  50.  
  51.  
  52. Printer Output
  53.  
  54.   The preferred method of printer output is to use the handle write function
  55.   (Int 21H Function 40H) with the predefined standard printer handle (4).
  56.   For example, you could write the string hello to the printer as follows:
  57.  
  58.   ──────────────────────────────────────────────────────────────────────────
  59.   msg     db      'hello'     ; message for printer
  60.   msg_len equ     $-msg       ; length of message
  61.           .
  62.           .
  63.           .
  64.           mov     ah,40h      ; function 40h = write file or device
  65.           mov     bx,4        ; BX = standard printer handle
  66.           mov     cx,msg_len  ; CX = length of string
  67.           mov     dx,seg msg  ; DS:DX = string address
  68.           mov     ds,dx
  69.           mov     dx,offset msg
  70.           int     21h         ; transfer to MS-DOS
  71.           jc      error       ; jump if error
  72.           .
  73.           .
  74.           .
  75.   ──────────────────────────────────────────────────────────────────────────
  76.  
  77.   If there is no error, the function returns the carry flag cleared and the
  78.   number of characters actually transferred to the list device in register
  79.   AX. Under normal circumstances, this number should always be the same as
  80.   the length requested and the carry flag indicating an error should never
  81.   be set. However, the output will terminate early if your data contains an
  82.   end-of-file mark (Ctrl-Z).
  83.  
  84.   You can write independently to several list devices (for example, LPT1,
  85.   LPT2) by issuing a specific open request (Int 21H Function 3DH) for each
  86.   device and using the handles returned to access the printers individually
  87.   with Int 21H Function 40H. You have already seen this general approach in
  88.   Chapters 5 and 6.
  89.  
  90.   An alternative method of printer output is to use the traditional Int 21H
  91.   Function 05H, which transfers the character in the DL register to the
  92.   printer. (This function is sensitive to Ctrl-C interrupts.) For example,
  93.   the assembly-language code sequence at the top of the following page would
  94.   write the the string hello to the line printer.
  95.  
  96.   ──────────────────────────────────────────────────────────────────────────
  97.   msg     db      'hello'     ; message for printer
  98.   msg_len equ     $-msg       ; length of message
  99.           .
  100.           .
  101.           .
  102.           mov     bx,seg msg  ; DS:BX = string address
  103.           mov     ds,bx
  104.           mov     bx,offset msg
  105.           mov     cx,msg_len  ; CX = string length
  106.  
  107.   next:   mov     dl,[bx]     ; get next character
  108.           mov     ah,5        ; function 05h = printer output
  109.           int     21h         ; transfer to MS-DOS
  110.           inc     bx          ; bump string pointer
  111.           loop    next        ; loop until string done
  112.           .
  113.           .
  114.           .
  115.   ──────────────────────────────────────────────────────────────────────────
  116.  
  117.   Programs that run on IBM PC─compatible machines can obtain improved
  118.   printer throughput by bypassing MS-DOS and calling the ROM BIOS printer
  119.   driver directly by means of Int 17H. Section III of this book, "IBM ROM
  120.   BIOS and Mouse Functions Reference," documents the Int 17H functions in
  121.   detail. Use of the ROM BIOS functions also allows your program to test
  122.   whether the printer is off line or out of paper, a capability that MS-DOS
  123.   does not offer.
  124.  
  125.   For example, the following sequence of instructions calls the ROM BIOS
  126.   printer driver to send the string hello to the line printer:
  127.  
  128.   ──────────────────────────────────────────────────────────────────────────
  129.   msg     db      'hello'     ; message for printer
  130.   msg_len equ     $-msg       ; length of message
  131.           .
  132.           .
  133.           .
  134.           mov     bx,seg msg  ; DS:BX = string address
  135.           mov     ds,bx
  136.           mov     bx,offset msg
  137.           mov     cx,msg_len  ; CX = string length
  138.           mov     dx,0        ; DX = printer number
  139.  
  140.   next:   mov     al,[bx]     ; AL = character to print
  141.           mov     ah,0        ; function 00h = printer output
  142.           int     17h         ; transfer to ROM BIOS
  143.           inc     bx          ; bump string pointer
  144.           loop    next        ; loop until string done
  145.           .
  146.           .
  147.           .
  148.   ──────────────────────────────────────────────────────────────────────────
  149.  
  150.   Note that the printer numbers used by the ROM BIOS are zero-based, whereas
  151.   the printer numbers in MS-DOS logical-device names are one-based. For
  152.   example, ROM BIOS printer 0 corresponds to LPT1.
  153.  
  154.   Finally, the most hardware-dependent technique of printer output is to
  155.   access the printer controller directly. Considering the functionality
  156.   already provided in MS-DOS and the IBM ROM BIOS, as well as the speeds of
  157.   the devices involved, I cannot see any justification for using direct
  158.   hardware control in this case. The disadvantage of introducing such
  159.   extreme hardware dependence for such a low-speed device would far outweigh
  160.   any small performance gains that might be obtained.
  161.  
  162.  
  163. The Serial Port
  164.  
  165.   MS-DOS support for serial ports (often referred to as the auxiliary device
  166.   in MS-DOS manuals) is weak compared with its keyboard, video-display, and
  167.   printer support. This is one area where the application programmer is
  168.   justified in making programs hardware dependent to extract adequate
  169.   performance.
  170.  
  171.   Programs that restrict themselves to MS-DOS functions to ensure
  172.   portability can use the handle read and write functions (Int 21H Functions
  173.   3FH and 40H), with the predefined standard auxiliary handle (3) to
  174.   access the serial port. For example, the following code writes the string
  175.   hello to the serial port that is currently defined as the AUX device:
  176.  
  177.   ──────────────────────────────────────────────────────────────────────────
  178.   msg     db      'hello'     ; message for serial port
  179.   msg_len equ     $-msg       ; length of message
  180.           .
  181.           .
  182.           .
  183.           mov     ah,40h      ; function 40h = write file or device
  184.           mov     bx,3        ; BX = standard aux handle
  185.           mov     cx,msg_len  ; CX = string length
  186.           mov     dx,seg msg  ; DS:DX = string address
  187.           mov     ds,dx
  188.           mov     dx,offset msg
  189.           int     21h         ; transfer to MS-DOS
  190.           jc      error       ; jump if error
  191.           .
  192.           .
  193.           .
  194.   ──────────────────────────────────────────────────────────────────────────
  195.  
  196.   The standard auxiliary handle gives access to only the first serial port
  197.   (COM1). If you want to read or write COM2 and COM3 using the handle calls,
  198.   you must issue an open request (Int 21H Function 3DH) for the desired
  199.   serial port and use the handle returned by that function with Int 21H
  200.   Functions 3FH and 40H.
  201.  
  202.   Some versions of MS-DOS have a bug in character-device handling that
  203.   manifests itself as follows: If you issue a read request with Int 21H
  204.   Function 3FH for the exact number of characters that are waiting in the
  205.   driver's buffer, the length returned in the AX register is the number of
  206.   characters transferred minus one. You can circumvent this problem by
  207.   always requesting more characters than you expect to receive or by placing
  208.   the device handle into binary mode using Int 21H Function 44H.
  209.  
  210.   MS-DOS also supports two traditional functions for serial-port I/O. Int
  211.   21H Function 03H inputs a character from COM1 and returns it in the AL
  212.   register; Int 21H Function 04H transmits the character in the DL register
  213.   to COM1. Like the other traditional calls, these two are direct
  214.   descendants of the CP/M auxiliary-device functions.
  215.  
  216.   For example, the following code sends the string hello to COM1 using the
  217.   traditional Int 21H Function 04H:
  218.  
  219.   ──────────────────────────────────────────────────────────────────────────
  220.   msg     db      'hello'     ; message for serial port
  221.   msg_len equ     $-msg       ; length of message
  222.           .
  223.           .
  224.           .
  225.           mov     bx,seg msg  ; DS:BX = string address
  226.           mov     ds,bx
  227.           mov     bx,offset msg
  228.           mov     cx,msg_len  ; CX = length of string
  229.     mov     dl,[bx]     ; get next character
  230.           mov     ah,4        ; function 04h = aux output
  231.           int     21h         ; transfer to MS-DOS
  232.           inc     bx          ; bump pointer to string
  233.           loop    next        ; loop until string done
  234.           .
  235.           .
  236.           .
  237.   ──────────────────────────────────────────────────────────────────────────
  238.  
  239.   MS-DOS translates the traditional auxiliary-device functions into calls on
  240.   the same device driver used by the handle calls. Therefore, it is
  241.   generally preferable to use the handle functions in the first place,
  242.   because they allow very long strings to be read or written in one
  243.   operation, they give access to serial ports other than COM1, and they are
  244.   symmetrical with the handle video-display, keyboard, printer, and file I/O
  245.   methods described elsewhere in this book.
  246.  
  247.   Although the handle or traditional serial-port functions allow you to
  248.   write programs that are portable to any machine running MS-DOS, they have
  249.   a number of disadvantages:
  250.  
  251.   ■  The built-in MS-DOS serial-port driver is slow and is not interrupt
  252.      driven.
  253.  
  254.   ■  MS-DOS serial-port I/O is not buffered.
  255.  
  256.   ■  Determining the status of the auxiliary device requires a separate call
  257.      to the IOCTL function (Int 21H Function 44H)──if you request input and
  258.      no characters are ready, your program will simply hang.
  259.  
  260.   ■  MS-DOS offers no standardized function to configure the serial port
  261.      from within a program.
  262.  
  263.   For programs that are going to run on the IBM PC or compatibles, a more
  264.   flexible technique for serial-port I/O is to call the IBM ROM BIOS
  265.   serial-port driver by means of Int 14H. You can use this driver to
  266.   initialize the serial port to a desired configuration and baud rate,
  267.   examine the status of the controller, and read or write characters.
  268.   Section III of this book, "IBM ROM BIOS and Mouse Functions Reference,"
  269.   documents the functions available from the ROM BIOS serial-port driver.
  270.  
  271.   For example, the following sequence sends the character X to the first
  272.   serial port (COM1):
  273.  
  274.   ──────────────────────────────────────────────────────────────────────────
  275.           .
  276.           .
  277.           .
  278.           mov     ah,1        ; function 01h = send character
  279.           mov     al,'X'      ; AL = character to transmit
  280.           mov     dx,0        ; DX = serial-port number
  281.           int     14h         ; transfer to ROM BIOS
  282.           and     ah,80h      ; did transmit fail?
  283.           jnz     error       ; jump if transmit error
  284.           .
  285.           .
  286.           .
  287.   ──────────────────────────────────────────────────────────────────────────
  288.  
  289.   As with the ROM BIOS printer driver, the serial-port numbers used by the
  290.   ROM BIOS are zero-based, whereas the serial-port numbers in MS-DOS
  291.   logical-device names are one-based. In this example, serial port 0
  292.   corresponds to COM1.
  293.  
  294.   Unfortunately, like the MS-DOS auxiliary-device driver, the ROM BIOS
  295.   serial-port driver is not interrupt driven. Although it will support
  296.   higher transfer speeds than the MS-DOS functions, at rates greater than
  297.   2400 baud it may still lose characters. Consequently, most programmers
  298.   writing high-performance applications that use a serial port (such as
  299.   telecommunications programs) take complete control of the serial-port
  300.   controller and provide their own interrupt driver. The built-in functions
  301.   provided by MS-DOS, and by the ROM BIOS in the case of the IBM PC, are
  302.   simply not adequate.
  303.  
  304.   Writing such programs requires a good understanding of the hardware. In
  305.   the case of the IBM PC, the chips to study are the INS8250 Asynchronous
  306.   Communications Controller and the Intel 8259A Programmable Interrupt
  307.   Controller. The IBM technical reference documentation for these chips is a
  308.   bit disorganized, but most of the necessary information is there if you
  309.   look for it.
  310.  
  311.  
  312. The TALK Program
  313.  
  314.   The simple terminal-emulator program TALK.ASM (Figure 7-1) is an example
  315.   of a useful program that performs screen, keyboard, and serial-port I/O.
  316.   This program recapitulates all of the topics discussed in Chapters 5
  317.   through 7. TALK uses the IBM PC's ROM BIOS video driver to put characters
  318.   on the screen, to clear the display, and to position the cursor; it uses
  319.   the MS-DOS character-input calls to read the keyboard; and it contains its
  320.   own interrupt driver for the serial-port controller.
  321.  
  322.   ──────────────────────────────────────────────────────────────────────────
  323.           name      talk
  324.           page      55,132
  325.           .lfcond             ; List false conditionals too
  326.           title     TALK--Simple terminal emulator
  327.  
  328.   ;
  329.   ; TALK.ASM--Simple IBM PC terminal emulator
  330.   ;
  331.   ; Copyright (c) 1988 Ray Duncan
  332.   ;
  333.   ; To assemble and link this program into TALK.EXE:
  334.   ;
  335.   ;       C>MASM TALK;
  336.   ;       C>LINK TALK;
  337.   ;
  338.  
  339.   stdin   equ     0               ; standard input handle
  340.   stdout  equ     1               ; standard output handle
  341.   stderr  equ     2               ; standard error handle
  342.  
  343.   cr      equ     0dh             ; ASCII carriage return
  344.   lf      equ     0ah             ; ASCII linefeed
  345.   bsp     equ     08h             ; ASCII backspace
  346.   escape  equ     1bh             ; ASCII escape code
  347.  
  348.   dattr   equ     07h             ; display attribute to use
  349.                                   ; while in emulation mode
  350.  
  351.   bufsiz  equ     4096            ; size of serial-port buffer
  352.  
  353.   echo    equ     0               ; 0 = full-duplex, -1 = half-duplex
  354.      equ     -1
  355.   false   equ     0
  356.  
  357.   com1    equ     true            ; use COM1 if nonzero
  358.   com2    equ     not com1        ; use COM2 if nonzero
  359.  
  360.   pic_mask  equ   21h             ; 8259 interrupt mask port
  361.   pic_eoi   equ   20h             ; 8259 EOI port
  362.  
  363.           if      com1
  364.   com_data equ    03f8h           ; port assignments for COM1
  365.   com_ier  equ    03f9h
  366.   com_mcr  equ    03fch
  367.   com_sts  equ    03fdh
  368.   com_int  equ    0ch             ; COM1 interrupt number
  369.   int_mask equ    10h             ; IRQ4 mask for 8259
  370.           endif
  371.  
  372.           if      com2
  373.   com_data equ    02f8h           ; port assignments for COM2
  374.   com_ier  equ    02f9h
  375.   com_mcr  equ    02fch
  376.   com_sts  equ    02fdh
  377.   com_int  equ    0bh             ; COM2 interrupt number
  378.   int_mask equ    08h             ; IRQ3 mask for 8259
  379.           endif
  380.  
  381.   _TEXT   segment word public 'CODE'
  382.  
  383.           assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  384.  
  385.   talk    proc    far             ; entry point from MS-DOS
  386.  
  387.           mov     ax,_DATA        ; make data segment addressable
  388.           mov     ds,ax
  389.           mov     es,ax
  390.                                   ; initialize display for
  391.                                   ; terminal emulator mode...
  392.  
  393.           mov     ah,15           ; get display width and
  394.           int     10h             ; current display mode
  395.           dec     ah              ; save display width for use
  396.           mov     columns,ah      ; by the screen-clear routine
  397.  
  398.           cmp     al,7            ; enforce text display mode
  399.           je      talk2           ; mode 7 ok, proceed
  400.          cmp     al,3
  401.           jbe     talk2           ; modes 0-3 ok, proceed
  402.  
  403.           mov     dx,offset msg1
  404.           mov     cx,msg1_len
  405.           jmp     talk6           ; print error message and exit
  406.  
  407.   talk2:  mov     bh,dattr        ; clear screen and home cursor
  408.           call    cls
  409.  
  410.           call    asc_enb         ; capture serial-port interrupt
  411.                                   ; vector and enable interrupts
  412.  
  413.           mov     dx,offset msg2  ; display message
  414.           mov     cx,msg2_len     ; 'terminal emulator running'
  415.           mov     bx,stdout       ; BX = standard output handle
  416.           mov     ah,40h          ; function 40h = write file or device
  417.           int     21h             ; transfer to MS-DOS
  418.  
  419.   talk3:  call    pc_stat         ; keyboard character waiting?
  420.           jz      talk4           ; nothing waiting, jump
  421.  
  422.           call    pc_in           ; read keyboard character
  423.  
  424.           cmp     al,0            ; is it a function key?
  425.           jne     talk32          ; not function key, jump
  426.  
  427.           call    pc_in           ; function key, discard 2nd
  428.                                   ; character of sequence
  429.           jmp     talk5           ; then terminate program
  430.  
  431.   talk32:                         ; keyboard character received
  432.           if      echo
  433.           push    ax              ; if half-duplex, echo
  434.           call    pc_out          ; character to PC display
  435.           pop     ax
  436.           endif
  437.  
  438.           call    com_out         ; write char to serial port
  439.  
  440.   talk4:  call    com_stat        ; serial-port character waiting?
  441.           jz      talk3           ; nothing waiting, jump
  442.  
  443.           call    com_in          ; read serial-port character
  444.  
  445.           cmp     al,20h          ; is it control code?
  446.           jae     talk45          ; jump if not
  447.           call    ctrl_code       ; control code, process it
  448.  
  449.           jmp     talk3           ; check keyboard again
  450.  
  451.   talk45:                         ; noncontrol char received,
  452.           call    pc_out          ; write it to PC display
  453.  
  454.           jmp     talk4           ; see if any more waiting
  455.  
  456.   talk5:                          ; function key detected,
  457.                                   ; prepare to terminate...
  458.  
  459.           mov     bh,07h          ; clear screen and home cursor
  460.           call    cls
  461.  
  462.           mov     dx,offset msg3  ; display farewell message
  463.           mov     cx,msg3_len
  464.  
  465.   talk6:  push    dx              ; save message address
  466.           push    cx              ; and message length
  467.  
  468.           call    asc_dsb         ; disable serial-port interrupts
  469.                                   ; and release interrupt vector
  470.  
  471.           pop     cx              ; restore message length
  472.           pop     dx              ; and address
  473.  
  474.           mov     bx,stdout       ; handle for standard output
  475.           mov     ah,40h          ; function 40h = write device
  476.           int     21h             ; transfer to MS-DOS
  477.  
  478.           mov     ax,4c00h        ; terminate program with
  479.           int     21h             ; return code = 0
  480.  
  481.   talk    endp
  482.  
  483.   com_stat proc   near            ; check asynch status; returns
  484.                                   ; Z = false if character ready
  485.                                   ; Z = true if nothing waiting
  486.           push    ax
  487.           mov     ax,asc_in       ; compare ring buffer pointers
  488.           cmp     ax,asc_out
  489.           pop     ax
  490.           ret                     ; return to caller
  491.   stat endp
  492.  
  493.   com_in  proc    near            ; get character from serial-
  494.                                   ; port buffer; returns
  495.                                   ; new character in AL
  496.  
  497.           push    bx              ; save register BX
  498.  
  499.   com_in1:                        ; if no char waiting, wait
  500.           mov     bx,asc_out      ; until one is received
  501.           cmp     bx,asc_in
  502.           je      com_in1         ; jump, nothing waiting
  503.  
  504.           mov     al,[bx+asc_buf] ; character is ready,
  505.                                   ; extract it from buffer
  506.  
  507.           inc     bx              ; update buffer pointer
  508.           cmp     bx,bufsiz
  509.           jne     com_in2
  510.           xor     bx,bx           ; reset pointer if wrapped
  511.   com_in2:
  512.           mov     asc_out,bx      ; store updated pointer
  513.           pop     bx              ; restore register BX
  514.           ret                     ; and return to caller
  515.  
  516.   com_in  endp
  517.  
  518.   com_out proc    near            ; write character in AL
  519.                                   ; to serial port
  520.  
  521.           push    dx              ; save register DX
  522.           push    ax              ; save character to send
  523.           mov     dx,com_sts      ; DX = status port address
  524.  
  525.   com_out1:                       ; check if transmit buffer
  526.           in      al,dx           ; is empty (TBE bit = set)
  527.           and     al,20h
  528.           jz      com_out1        ; no, must wait
  529.  
  530.           pop     ax              ; get character to send
  531.           mov     dx,com_data     ; DX = data port address
  532.           out     dx,al           ; transmit the character
  533.           pop     dx              ; restore register DX
  534.           ret                     ; and return to caller
  535.  
  536.   com_out endp
  537.   pc_stat proc    near            ; read keyboard status; returns
  538.                                   ; Z = false if character ready
  539.                                   ; Z = true if nothing waiting
  540.                                   ; register DX destroyed
  541.  
  542.           mov     al,in_flag      ; if character already
  543.           or      al,al           ; waiting, return status
  544.           jnz     pc_stat1
  545.  
  546.           mov     ah,6            ; otherwise call MS-DOS to
  547.           mov     dl,0ffh         ; determine keyboard status
  548.           int     21h
  549.  
  550.           jz      pc_stat1        ; jump if no key ready
  551.  
  552.           mov     in_char,al      ; got key, save it for
  553.           mov     in_flag,0ffh    ; "pc_in" routine
  554.  
  555.   pc_stat1:                       ; return to caller with
  556.           ret                     ; Z flag set appropriately
  557.  
  558.   pc_stat endp
  559.  
  560.   pc_in   proc    near            ; read keyboard character,
  561.                                   ; return it in AL
  562.                                   ; DX may be destroyed
  563.  
  564.           mov     al,in_flag      ; key already waiting?
  565.           or      al,al
  566.           jnz     pc_in1          ; yes, return it to caller
  567.  
  568.           call    pc_stat         ; try to read a character
  569.           jmp     pc_in
  570.  
  571.   pc_in1: mov     in_flag,0       ; clear char-waiting flag
  572.           mov     al,in_char      ; and return AL = character
  573.           ret
  574.  
  575.   pc_in   endp
  576.  
  577.   pc_out  proc    near            ; write character in AL
  578.                                   ; to the PC's display
  579.  
  580.           mov     ah,0eh          ; ROM BIOS function 0eh =
  581.                                   ; "teletype output"
  582.           push    bx              ; save register BX
  583.           xor     bx,bx           ; assume page 0
  584.           int     10h             ; transfer to ROM BIOS
  585.           pop     bx              ; restore register BX
  586.           ret                     ; and return to caller
  587.  
  588.   pc_out  endp
  589.  
  590.  
  591.   cls     proc    near            ; clear display using
  592.                                   ; char attribute in BH
  593.                                   ; registers AX, CX,
  594.                                   ; and DX destroyed
  595.  
  596.           mov     dl,columns      ; set DL,DH = X,Y of
  597.           mov     dh,24           ; lower right corner
  598.           mov     cx,0            ; set CL,CH = X,Y of
  599.                                   ; upper left corner
  600.           mov     ax,600h         ; ROM BIOS function 06h =
  601.                                   ; "scroll or initialize
  602.                                   ; window"
  603.           int     10h             ; transfer to ROM BIOS
  604.           call    home            ; set cursor at (0,0)
  605.           ret                     ; and return to caller
  606.  
  607.   cls     endp
  608.  
  609.   clreol  proc    near            ; clear from cursor to end
  610.                                   ; of line using attribute
  611.                                   ; in BH, registers AX, CX,
  612.                                   ; and DX destroyed
  613.  
  614.           call    getxy           ; get current cursor position
  615.           mov     cx,dx           ; current position = "upper
  616.                                   ; left corner" of window;
  617.           mov     dl,columns      ; "lower right corner" X is
  618.                                   ; max columns, Y is same
  619.                                   ; as upper left corner
  620.           mov     ax,600h         ; ROM BIOS function 06h =
  621.                                   ; "scroll or initialize
  622.                                   ; window"
  623.           int     10h             ; transfer to ROM BIOS
  624.           ret                     ; return to caller
  625.  
  626.   clreol  endp
  627.   home    proc    near            ; put cursor at home position
  628.  
  629.           mov     dx,0            ; set (X,Y) = (0,0)
  630.           call    gotoxy          ; position the cursor
  631.           ret                     ; return to caller
  632.  
  633.   home    endp
  634.  
  635.   gotoxy  proc    near            ; position the cursor
  636.                                   ; call with DL = X, DH = Y
  637.  
  638.           push    bx              ; save registers
  639.           push    ax
  640.  
  641.           mov     bh,0            ; assume page 0
  642.           mov     ah,2            ; ROM BIOS function 02h =
  643.                                   ; set cursor position
  644.           int     10h             ; transfer to ROM BIOS
  645.  
  646.           pop     ax              ; restore registers
  647.           pop     bx
  648.           ret                     ; and return to caller
  649.  
  650.   gotoxy  endp
  651.  
  652.  
  653.   getxy   proc    near            ; get cursor position,
  654.                                   ; returns DL = X, DH = Y
  655.  
  656.           push    ax              ; save registers
  657.           push    bx
  658.           push    cx
  659.  
  660.           mov     ah,3            ; ROM BIOS function 03h =
  661.                                   ; get cursor position
  662.           mov     bh,0            ; assume page 0
  663.           int     10h             ; transfer to ROM BIOS
  664.  
  665.           pop     cx              ; restore registers
  666.           pop     bx
  667.           pop     ax
  668.           ret                     ; and return to caller
  669.  
  670.   getxy   endp
  671.   ctrl_code proc  near            ; process control code
  672.                                   ; call with AL = char
  673.  
  674.           cmp     al,cr           ; if carriage return
  675.           je      ctrl8           ; just send it
  676.  
  677.           cmp     al,lf           ; if linefeed
  678.           je      ctrl8           ; just send it
  679.  
  680.           cmp     al,bsp          ; if backspace
  681.           je      ctrl8           ; just send it
  682.  
  683.           cmp     al,26           ; is it cls control code?
  684.           jne     ctrl7           ; no, jump
  685.  
  686.           mov     bh,dattr        ; cls control code, clear
  687.           call    cls             ; screen and home cursor
  688.  
  689.           jmp     ctrl9
  690.  
  691.   ctrl7:
  692.           cmp     al,escape       ; is it Escape character?
  693.           jne     ctrl9           ; no, throw it away
  694.  
  695.           call    esc_seq         ; yes, emulate CRT terminal
  696.           jmp     ctrl9
  697.  
  698.   ctrl8:  call    pc_out          ; send CR, LF, or backspace
  699.                                   ; to the display
  700.  
  701.   ctrl9:  ret                     ; return to caller
  702.  
  703.   ctrl_code endp
  704.  
  705.  
  706.   esc_seq proc    near            ; decode Televideo 950 escape
  707.                                   ; sequence for screen control
  708.  
  709.           call    com_in          ; get next character
  710.           cmp     al,84           ; is it clear to end of line?
  711.           jne     esc_seq1        ; no, jump
  712.  
  713.           mov     bh,dattr        ; yes, clear to end of line
  714.           call    clreol
  715.           jmp     esc_seq2        ; then exit
  716.   esc_seq1:
  717.           cmp     al,61           ; is it cursor positioning?
  718.           jne     esc_seq2        ; no jump
  719.  
  720.           call    com_in          ; yes, get Y parameter
  721.           sub     al,33           ; and remove offset
  722.           mov     dh,al
  723.  
  724.           call    com_in          ; get X parameter
  725.           sub     al,33           ; and remove offset
  726.           mov     dl,al
  727.           call    gotoxy          ; position the cursor
  728.  
  729.   esc_seq2:                       ; return to caller
  730.           ret
  731.  
  732.   esc_seq endp
  733.  
  734.  
  735.   asc_enb proc    near            ; capture serial-port interrupt
  736.                                   ; vector and enable interrupt
  737.  
  738.                                   ; save address of previous
  739.                                   ; interrupt handler...
  740.           mov     ax,3500h+com_int ; function 35h = get vector
  741.           int     21h             ; transfer to MS-DOS
  742.           mov     word ptr oldvec+2,es
  743.           mov     word ptr oldvec,bx
  744.  
  745.                                   ; now install our handler...
  746.           push    ds              ; save our data segment
  747.           mov     ax,cs           ; set DS:DX = address
  748.           mov     ds,ax           ; of our interrupt handler
  749.           mov     dx,offset asc_int
  750.           mov     ax,2500h+com_int ; function 25h = set vector
  751.           int     21h             ; transfer to MS-DOS
  752.           pop     ds              ; restore data segment
  753.  
  754.           mov     dx,com_mcr      ; set modem-control register
  755.           mov     al,0bh          ; DTR and OUT2 bits
  756.           out     dx,al
  757.  
  758.           mov     dx,com_ier      ; set interrupt-enable
  759.           mov     al,1            ; register on serial-
  760.           out     dx,al           ; port controller
  761.           in      al,pic_mask     ; read current 8259 mask
  762.           and     al,not int_mask ; set mask for COM port
  763.           out     pic_mask,al     ; write new 8259 mask
  764.  
  765.           ret                     ; back to caller
  766.  
  767.   asc_enb endp
  768.  
  769.  
  770.   asc_dsb proc    near            ; disable interrupt and
  771.                                   ; release interrupt vector
  772.  
  773.           in      al,pic_mask     ; read current 8259 mask
  774.           or      al,int_mask     ; reset mask for COM port
  775.           out     pic_mask,al     ; write new 8259 mask
  776.  
  777.           push    ds              ; save our data segment
  778.           lds     dx,oldvec       ; load address of
  779.                                   ; previous interrupt handler
  780.           mov     ax,2500h+com_int ; function 25h = set vector
  781.           int     21h             ; transfer to MS-DOS
  782.           pop     ds              ; restore data segment
  783.  
  784.           ret                     ; back to caller
  785.  
  786.   asc_dsb endp
  787.  
  788.  
  789.   asc_int proc    far             ; interrupt service routine
  790.                                   ; for serial port
  791.  
  792.           sti                     ; turn interrupts back on
  793.  
  794.           push    ax              ; save registers
  795.           push    bx
  796.           push    dx
  797.           push    ds
  798.  
  799.           mov     ax,_DATA        ; make our data segment
  800.           mov     ds,ax           ; addressable
  801.  
  802.           cli                     ; clear interrupts for
  803.                                   ; pointer manipulation
  804.  
  805.           mov     dx,com_data     ; DX = data port address
  806.           in      al,dx           ; read this character
  807.           mov     bx,asc_in       ; get buffer pointer
  808.           mov     [asc_buf+bx],al ; store this character
  809.           inc     bx              ; bump pointer
  810.           cmp     bx,bufsiz       ; time for wrap?
  811.           jne     asc_int1        ; no, jump
  812.           xor     bx,bx           ; yes, reset pointer
  813.  
  814.   asc_int1:                       ; store updated pointer
  815.           mov     asc_in,bx
  816.  
  817.           sti                     ; turn interrupts back on
  818.  
  819.           mov     al,20h          ; send EOI to 8259
  820.           out     pic_eoi,al
  821.  
  822.           pop     ds              ; restore all registers
  823.           pop     dx
  824.           pop     bx
  825.           pop     ax
  826.  
  827.           iret                    ; return from interrupt
  828.  
  829.   asc_int endp
  830.  
  831.   _TEXT   ends
  832.  
  833.  
  834.   _DATA   segment word public 'DATA'
  835.  
  836.   in_char db      0               ; PC keyboard input char
  837.   in_flag db      0               ; <>0 if char waiting
  838.  
  839.   columns db      0               ; highest numbered column in
  840.                                   ; current display mode (39 or 79)
  841.  
  842.   msg1    db      cr,lf
  843.           db      'Display must be text mode.'
  844.           db      cr,lf
  845.   msg1_len equ $-msg1
  846.  
  847.   msg2    db      'Terminal emulator running...'
  848.           db      cr,lf
  849.   msg2_len equ $-msg2
  850.  
  851.   msg3    db      'Exit from terminal emulator.'
  852.           db      cr,lf
  853.   msg3_len equ $-msg3
  854.   oldvec  dd      0               ; original contents of serial-
  855.                                   ; port interrupt vector
  856.  
  857.   asc_in  dw      0               ; input pointer to ring buffer
  858.   asc_out dw      0               ; output pointer to ring buffer
  859.  
  860.   asc_buf db      bufsiz dup (?)  ; communications buffer
  861.  
  862.   _DATA   ends
  863.  
  864.  
  865.   STACK   segment para stack 'STACK'
  866.  
  867.           db      128 dup (?)
  868.  
  869.   STACK   ends
  870.  
  871.           end     talk            ;  defines entry point
  872.   ──────────────────────────────────────────────────────────────────────────
  873.  
  874.   Figure 7-1.  TALK.ASM: A simple terminal-emulator program for IBM
  875.   PC─compatible computers. This program demonstrates use of the MS-DOS and
  876.   ROM BIOS video and keyboard functions and direct control of the
  877.   serial-communications adapter.
  878.  
  879.   The TALK program illustrates the methods that an application should use to
  880.   take over and service interrupts from the serial port without running
  881.   afoul of MS-DOS conventions.
  882.  
  883.   The program begins with some equates and conditional assembly statements
  884.   that configure the program for half- or full-duplex and for the desired
  885.   serial port (COM1 or COM2). At entry from MS-DOS, the main routine of the
  886.   program──the procedure named talk──checks the status of the serial port,
  887.   initializes the display, and calls the asc_enb routine to take over the
  888.   serial-port interrupt vector and enable interrupts. The talk procedure
  889.   then enters a loop that reads the keyboard and sends the characters out
  890.   the serial port and then reads the serial port and puts the characters on
  891.   the display──in other words, it causes the PC to emulate a simple CRT
  892.   terminal.
  893.  
  894.   The TALK program intercepts and handles control codes (carriage return,
  895.   linefeed, and so forth) appropriately. It detects escape sequences and
  896.   handles them as a subset of the Televideo 950 terminal capabilities. (You
  897.   can easily modify the program to emulate any other cursor-addressable
  898.   terminal.) When one of the PC's special function keys is pressed, the
  899.   program disables serial-port interrupts, releases the serial-port
  900.   interrupt vector, and exits back to MS-DOS.
  901.  
  902.   There are several TALK program procedures that are worth your attention
  903.   because they can easily be incorporated into other programs. These are
  904.   listed in the table on the following page.
  905.  
  906.  
  907.   Procedure          Action
  908.   ──────────────────────────────────────────────────────────────────────────
  909.   asc_enb            Takes over the serial-port interrupt vector and enables
  910.                      interrupts by writing to the modem-control register of
  911.                      the INS8250 and the interrupt-mask register of the
  912.                      8259A.
  913.  
  914.   asc_dsb            Restores the original state of the serial-port
  915.                      interrupt vector and disables interrupts by writing to
  916.                      the interrupt-mask register of the 8259A.
  917.  
  918.   asc_int            Services serial-port interrupts, placing received
  919.                      characters into a ring buffer.
  920.  
  921.   com_stat           Tests whether characters from the serial port are
  922.                      waiting in the ring buffer.
  923.  
  924.   com_in             Removes characters from the interrupt handler's ring
  925.                      buffer and increments the buffer pointers
  926.                      appropriately.
  927.  
  928.   com_out            Sends one character to the serial port.
  929.  
  930.   cls                Calls the ROM BIOS video driver to clear the screen.
  931.  
  932.   clreol             Calls the ROM BIOS video driver to clear from the
  933.                      current cursor position to the end of the line.
  934.  
  935.   home               Places the cursor in the upper left corner of the
  936.                      screen.
  937.  
  938.   gotoxy             Positions the cursor at the desired position on the
  939.                      display.
  940.  
  941.   getxy              Obtains the current cursor position.
  942.  
  943.   pc_out             Sends one character to the PC's display.
  944.  
  945.   pc_stat            Gets status for the PC's keyboard.
  946.  
  947.   pc_in              Returns a character from the PC's keyboard.
  948.   ──────────────────────────────────────────────────────────────────────────
  949.  
  950.  
  951.  
  952.  
  953.  
  954.