home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l350 / 3.ddi / EXAMPLES / INTHNDLR / SERIO.ASM < prev    next >
Encoding:
Assembly Source File  |  1991-02-01  |  14.4 KB  |  504 lines

  1.     .386p
  2. ;************************************************************************/
  3. ;*    Copyright (C) 1986-1990 Phar Lap Software, Inc.            */
  4. ;*    Unpublished - rights reserved under the Copyright Laws of the    */
  5. ;*    United States.  Use, duplication, or disclosure by the         */
  6. ;*    Government is subject to restrictions as set forth in         */
  7. ;*    subparagraph (c)(1)(ii) of the Rights in Technical Data and     */
  8. ;*    Computer Software clause at 252.227-7013.            */
  9. ;*    Phar Lap Software, Inc., 60 Aberdeen Ave., Cambridge, MA 02138    */
  10. ;************************************************************************/
  11.  
  12. ;
  13. ; This program installs bimodal (separate real-mode and protected-mode)
  14. ; handlers for the IRQ4 (COM1 serial port) hardware interrupt.  The 
  15. ; interrupt handlers just stuff received characters into a circular
  16. ; buffer.  The main program loop retrieves characters out of the buffer
  17. ; and prints them on the screen until it sees a carriage return, when
  18. ; it exits.
  19. ;
  20. ; This program does NOT set up the serial port parameters;  use the DOS
  21. ; MODE command to program the port appropriately before running this
  22. ; program.  For example, to use 9600 baud, no parity, 8 data bits, one
  23. ; stop bit, use the command:
  24. ;    mode com1 9600,n,8,1
  25. ;
  26. ; Rather than use the -REALBREAK switch (which is not DPMI-compatible)
  27. ; to get the real mode code and data into conventional memory, we
  28. ; allocate a conventional memory block at startup time and copy the
  29. ; real mode code and data into it.
  30. ;
  31.  
  32. ;
  33. ; Constants and data structures
  34. ;
  35. include    dosx.ah
  36.  
  37. IOBUF_SIZE    equ    32            ; Size of circular buffer
  38. CR        equ    0Dh            ; ASCII carriage return 
  39. LF        equ    0Ah            ; ASCII linefeed
  40. PIC_MSK        equ    21h            ; PIC Mask Register port #
  41. PIC_EOI        equ    20h            ; PIC EOI Register port #
  42. PORTNO        equ    3F8h            ; COM1 base address (port #)
  43. COM_IER        equ    PORTNO+1        ; Serial port INT Enable Reg
  44. COM_MCR        equ    PORTNO+4        ; Serial port Modem Ctrl Reg
  45. IRQ4_MSK    equ    10h            ; PIC enable IRQ4 INTs mask
  46.  
  47. ;
  48. ; Struct for data kept in conventional memory that also needs to be accessed
  49. ; from protected mode
  50. ;
  51. REALDATA struc
  52.     RD_IDX        dd    ?    ; current read index in IO_BUF
  53.     WR_IDX        dd    ?    ; current write index in IO_BUF
  54.     IO_BUF    db IOBUF_SIZE dup (?)    ; 32 byte circular I/O buffer
  55. REALDATA ends
  56.  
  57. ;
  58. ; Segment definitions and ordering
  59. ;
  60. _rcodeseg     segment byte public use16 'rm_code'
  61.     public    start_real
  62. start_real    label    byte
  63. _rcodeseg    ends
  64. _rdata        segment    dword public use16 'rm_data'
  65. _rdata        ends
  66. _data        segment    dword public use32 'data'
  67. _data        ends
  68. dgroup    group    _rdata, _data
  69. _codeseg    segment    byte public use32 'code'
  70. _codeseg    ends
  71. _stack        segment dword stack use32 'stack'
  72.     db    2048 dup (?)    ; 2K stack
  73. _stack        ends
  74.  
  75. ;
  76. ; Global data that needs to be accessed in both real mode and protected mode
  77. ; This data gets copied to conventional memory;  it is not used in its
  78. ; original link-time location.
  79. ;
  80. _rdata    segment    
  81.  
  82. io_data    REALDATA    <>    ; I/O buffer and circular buffer pointers
  83.  
  84.     public    end_real
  85. end_real    label    byte
  86.  
  87. _rdata    ends
  88.  
  89. ;
  90. ; Global protected mode data
  91. ;
  92. _data    segment    
  93.  
  94. ;
  95. ; Original RM and PM IRQ4 vectors
  96. ;
  97.     public    rm_irq4_vec,pm_irq4_off,pm_irq4_sel,irq4_int
  98. rm_irq4_vec    dd    ?    ; original real mode vector
  99. pm_irq4_off    dd    ?    ; original prot mode vector
  100. pm_irq4_sel    dw    ?        ;
  101. irq4_int    db    ?    ; interrupt number for IRQ4
  102.     align    4
  103.  
  104. ;
  105. ; Conventional memory block control.  Note that the real mode CS and DS
  106. ; values for our real mode IRQ4 handler are not necessarily the same as
  107. ; the address of the memory block.  This is because if necessary we back
  108. ; them off so the link-time offsets to code and data will still be correct
  109. ; at run time.  It is necessary to do this if the real mode code and data
  110. ; don't begin at the start of the protected mode segment (eg, if the
  111. ; -OFFSET 1000h link-time switch is used).
  112. ;
  113. cbuf_seg    dw    ?    ; real mode paragraph addr of memory block
  114. rm_csds        dw    ?    ; real mode CS and DS values
  115.     align    4
  116. iodat_offs    dd    ?    ; offset in segment 0034h (the DOS memory
  117.                     ; seg) of the io_data structure
  118.  
  119. prompt_msg db    'Send CR-terminated data over serial port 1, '
  120.     db    'or type CTRL-C to exit',0Dh,0Ah,'$'
  121.     align    4
  122.  
  123. _data    ends
  124.  
  125. ;****************************************************************************
  126. ; Program entry point
  127. ;****************************************************************************
  128.  
  129.     assume    cs:_codeseg,ds:_data
  130. _codeseg    segment    
  131.  
  132.     public    main
  133. main proc    near
  134.  
  135. ;
  136. ; Copy the real mode code and data to a conventional memory block, and init
  137. ; globals for conv mem block
  138. ;
  139.     call    copy_real        ; set up real mode code/data
  140.     cmp    eax,TRUE        ; exit if error
  141.     je    #exit                ;
  142.  
  143. ;
  144. ; Save current real and prot mode IRQ4 vectors
  145. ;
  146.     mov    ax,250Ch        ; get IRQ4 interrupt number
  147.     int    21h                ;
  148.     add    al,4                ;
  149.     mov    irq4_int,al            ;
  150.     mov    cl, irq4_int        ; save real mode IRQ4 vector
  151.     mov    ax, 2503h            ; 
  152.     int    21h                ;
  153.     mov    rm_irq4_vec, ebx        ; 
  154.     mov    ax, 2502h        ; save prot mode IRQ4 vector
  155.     mov    cl,irq4_int            ;
  156.     int    21h                ; 
  157.     mov    pm_irq4_sel, es            ;
  158.     mov    pm_irq4_off, ebx        ; 
  159.  
  160. ;
  161. ; Install our real-mode and protected-mode IRQ4 (COM1) handlers.
  162. ;
  163.     push    ds            ; set real and prot mode IRQ4 vectors
  164.     mov    cl,irq4_int            ;
  165.     mov    bx,rm_csds            ;
  166.     shl    ebx,16                ;
  167.     lea    bx,rm_irq4_hnd            ;
  168.     lea    edx,pm_irq4_hnd            ;
  169.     mov    ax,cs                ;
  170.     mov    ds,ax                ;
  171.     mov    ax,2507h            ;
  172.     int    21h                ;
  173.     pop    ds                ;
  174.  
  175. ;
  176. ; Init MCR and IER in the 8250 serial chip. 
  177. ; Make sure IRQ4 is unmasked in the 8259 interrupt controller.
  178. ;
  179.     mov    dx, COM_MCR        ; set RTS, DTR, and OUT2 bits in the
  180.     mov    al, 0Bh                ; Modem Control Register
  181.     out    dx, al                ;
  182.     mov    dx, COM_IER        ; enable interrupts on receive data
  183.     mov    al, 01h                ; on the serial chip
  184.     out    dx, al                ;
  185.     in    al, PIC_MSK        ; unmask IRQ4 in the 8259 mask
  186.     and    al, NOT IRQ4_MSK        ; 
  187.     out    PIC_MSK, al            ;
  188.  
  189. ;
  190. ; Prompt user to send serial data
  191. ;
  192.     mov    ah, 09h            ; print prompt message
  193.     lea    edx, prompt_msg            ;
  194.     int    21h                ;
  195.  
  196. ;
  197. ; Loop, reading data from the buffer as it gets filled and printing
  198. ; it to the screen.  When we see a carriage return, exit the loop.
  199. ;
  200. ; Make a do-nothing DOS call inside the loop (get default disk) to
  201. ; allow DOS to generate a CTRL-C interrupt so the user can kill the
  202. ; program if they can't get serial hookup to work.
  203. ;
  204.     mov    ax,SS_DOSMEM        ; set ES:EBX to I/O data block in
  205.     mov    es,ax                ; conventional memory
  206.     mov    ebx,iodat_offs            ;
  207. #loop:
  208.     mov    ah,19h            ; DOS call to allow CTRL-C
  209.     int    21h                ;
  210.     mov    ecx,es:[ebx].RD_IDX    ; keep looping if buffer empty
  211.     cmp    ecx,es:[ebx].WR_IDX        ;
  212.     je    #loop                ;
  213.     pushfd                ; get character and update read index,
  214.     cli                    ; disabling interrupts while
  215.     mov    dl,es:[ebx][ecx].IO_BUF        ; we update the I/O globals
  216.     inc    ecx                ;
  217.     cmp    ecx,IOBUF_SIZE            ;
  218.     jb    short #in_buf            ;
  219.     mov    ecx,0                ;
  220. #in_buf:                    ;
  221.     mov    es:[ebx].RD_IDX,ecx        ;
  222.     popfd                    ;
  223.     mov    ah, 02h            ; print the character in DL on the
  224.     int 21h                    ; display
  225.     cmp    dl,CR            ; continue loop unless it was CR
  226.     jne    #loop                ; 
  227.     mov    dl,LF            ; print a line feed
  228.     mov    ah,2                ;
  229.     int    21h                ;
  230.  
  231. ;
  232. ; Restore serial port to previous state, restore IRQ4 handlers, and
  233. ; free up the conventional memory block we allocated.
  234. ;
  235.     mov    dx, COM_MCR        ; clear RTS, DTR, and OUT2 in the
  236.     in    al, dx                ; Modem Control Register
  237.     and    al, 04h                ; 
  238.     out    dx, al                ;
  239.     mov    dx, COM_IER        ; disable serial chip interrupts
  240.     mov    al, 00h                ; 
  241.     out    dx, al                ;
  242.     in    al, PIC_MSK        ; mask off IRQ4 in the 8259
  243.     or    al, IRQ4_MSK            ; 
  244.     out    PIC_MSK, al            ;
  245.     push    ds            ; restore original real and prot mode 
  246.     mov    cl,irq4_int            ; IRQ4 vectors
  247.     mov    ebx, rm_irq4_vec        ;
  248.     mov    edx, pm_irq4_off        ;
  249.     mov    ax, pm_irq4_sel            ; 
  250.     mov    ds, ax                ;
  251.     mov    ax,2507h            ;
  252.     int    21h                ;
  253.     pop    ds                ;
  254.     mov    cx,cbuf_seg        ; free up conventional memory block
  255.     mov    ax,25C1h            ;
  256.     int    21h                ;
  257.  
  258. #exit:
  259.     mov    ax, 4C00h        ; exit to DOS
  260.     int    21h                ;
  261. main endp
  262.  
  263. ;****************************************************************************
  264. ; COPY_REAL -- copy real mode code & data to a conventional memory block,
  265. ;    and initialize global data used to manage the memory block
  266. ;
  267. ; Returns:  TRUE    if error
  268. ;        FALSE    if success
  269. ;****************************************************************************
  270.     public    copy_real
  271. copy_real proc    near
  272. ;
  273. ; Stack frame
  274. ;
  275. #REAL_NBYTES equ (dword ptr 12[ebp])    ; number of bytes RM code & data
  276. #REAL_START equ    (dword ptr 8[ebp])    ; start offset of RM code & data
  277.  
  278.     push    ebp            ; set up stack frame
  279.     mov    ebp,esp                ;
  280.     sub    esp,8            ; allocate local data
  281.     push    es            ; save regs
  282.     push    esi                ;
  283.     push    edi                ;
  284.  
  285. ;
  286. ; Allocate DOS segment in conventional memory the size of our real mode
  287. ; code and data.
  288. ;
  289. ; NOTE we are just assuming there is free conventional memory (the program
  290. ;    should have been linked with the -MINREAL switch), rather than
  291. ;    attempting to make sure memory is available with the 2525h and/or
  292. ;    2530h system calls as we would in a more elaborate program.
  293. ;
  294.     lea    ebx, end_real        ; end of RM code and data
  295.     test    ebx,0FFFF0000h        ; branch if not within 1st 64K of
  296.     jnz    #bad_segorder            ; of program segment
  297.     lea    ecx, start_real        ; start of RM code and data, rounded
  298.     and    ecx, not 0Fh            ; down to paragraph boundary
  299.     mov    #REAL_START, ecx        ; 
  300.     sub    ebx, ecx        ; RM code and data size in EBX
  301.     mov    #REAL_NBYTES, ebx        ; 
  302.     add    ebx, 15            ; round size up to paragraph count
  303.     shr    ebx, 4                ; 
  304.     mov    ax, 25C0h        ; alloc DOS memory block
  305.     int    21h                ; 
  306.     jc    #no_mem            ; branch if failure
  307.     mov    cbuf_seg, ax        ; save addr of conv mem block
  308.  
  309. ;
  310. ; Calculate a real mode segment address for real mode CS and DS that will
  311. ; allow the real mode code to use all its link-time offsets to code and data.
  312. ; Copy the real mode code and data to conventional memory.
  313. ;
  314.     mov    ax,cbuf_seg        ; calculate real-mode CS and DS
  315.     mov    ebx,#REAL_START            ;
  316.     shr    ebx,4                ;
  317.     sub    ax,bx                ;
  318.     mov    rm_csds,ax            ;
  319.     push    ds            ; copy code & data to conv mem block
  320.     mov    ecx, #REAL_NBYTES        ;
  321.     mov    ax, SS_DOSMEM            ;
  322.     mov    es, ax                ;
  323.     movzx    edi, cbuf_seg            ;
  324.     shl    edi, 4                ; 
  325.     mov    esi, #REAL_START        ;
  326.     mov    ax, cs                ;
  327.     mov    ds, ax                ; 
  328.     cld                    ;
  329.     rep    movsb                ; 
  330.     pop    ds                ;
  331.  
  332. ;
  333. ; Calculate the protected mode offset in segment 0034h to the I/O data
  334. ; block we keep in conventional memory.  Also initialize the data block.
  335. ;
  336.     lea    eax,io_data        ; get prot mode offset to I/O data
  337.     movzx    ebx,rm_csds            ; in conv memory block
  338.     shl    ebx,4                ;
  339.     add    eax,ebx                ;
  340.     mov    iodat_offs,eax            ;
  341.     mov    ax,SS_DOSMEM        ; init to empty buffer
  342.     mov    es,ax                ;
  343.     mov    eax,iodat_offs            ;
  344.     mov    es:[eax].RD_IDX,0        ;
  345.     mov    es:[eax].WR_IDX,0        ;
  346.  
  347.     mov    eax,FALSE        ; return success
  348. #exit:
  349.     pop    edi            ; restore regs
  350.     pop    esi                ;
  351.     pop    es                ;
  352.     mov    esp,ebp            ; restore stack frame & exit
  353.     pop    ebp                ;
  354.     ret                    ;
  355.  
  356. #no_mem:
  357. ;
  358. ; Couldn't allocate conventional memory for real mode code & data
  359. ;
  360. _data    segment
  361. nocmem_msg db    "Can't allocate conventional memory buffer",0Dh,0Ah,'$'
  362. _data    ends
  363.     mov    ah, 09h            ; print err message
  364.     lea    edx, nocmem_msg            ;
  365.     int    21h                ;
  366.     mov    eax,TRUE        ; return error
  367.     jmp    #exit                ;
  368.  
  369. #bad_segorder:
  370. ;
  371. ; real mode code & data not within 1st 64K of program segment
  372. ;
  373. _data    segment
  374. boffs_msg db    'Real mode code/data > 64K into program segment',0Dh,0Ah,'$'
  375. _data    ends
  376.     mov    ah, 09h            ; print err message
  377.     lea    edx, boffs_msg            ;
  378.     int    21h                ;
  379.     mov    eax,TRUE        ; return error
  380.     jmp    #exit                ;
  381. copy_real endp
  382.  
  383. ;****************************************************************************
  384. ; PM_IRQ4_HND - Protected Mode IRQ4 Interrupt Handler
  385. ;    Just reads the character from the serial port and stuffs it into
  386. ;    the circular buffer.
  387. ;****************************************************************************
  388.     public    pm_irq4_hnd
  389. pm_irq4_hnd    proc    near
  390.  
  391.     push    eax            ; save regs we use
  392.     push    ebx                ;
  393.     push    ecx                ;
  394.     push    edx                ;
  395.     push    es                ;
  396.     push    ds                ;
  397.     mov    ax,SS_DATA        ; set DS to our data segment
  398.     mov    ds, ax                ; 
  399.     mov    ax,SS_DOSMEM        ; set ES:EBX to I/O data in 
  400.     mov    es, ax                ; conventional memory
  401.     mov    ebx,iodat_offs            ;
  402.  
  403. ;
  404. ; Get the character and write it to the buffer, with interrupts still
  405. ; disabled so we can't get reentered.
  406. ;
  407. ; If the circular buffer is full, just drop this character on the floor.
  408. ;
  409.     mov    dx, PORTNO        ; read character from serial chip
  410.     in    al, dx                ;
  411.     mov    ecx,es:[ebx].WR_IDX    ; calculate new write index after
  412.     inc    ecx                ; we stuff this char into
  413.     cmp    ecx,IOBUF_SIZE            ; buffer
  414.     jb    short #in_buf            ;
  415.     mov    ecx,0                ;
  416. #in_buf:                    ;
  417.     cmp    ecx,es:[ebx].RD_IDX    ; drop this char if buffer full
  418.     je    short #done_ch            ;
  419.     mov    edx,es:[ebx].WR_IDX    ; write this char to buffer
  420.     mov    es:[ebx][edx].IO_BUF,al        ;
  421.     mov    es:[ebx].WR_IDX,ecx    ; update write index
  422. #done_ch:
  423.  
  424. ;
  425. ; It's now OK to get reentered, so we can re-enable interrupts and 
  426. ; acknowledge the interrupt to the 8259
  427. ;
  428.     sti                ; re-enable interrupts
  429.     mov    al, 20h            ; send EOI to 8259
  430.     out    PIC_EOI, al            ;
  431.  
  432.     pop    ds            ; restore registers
  433.     pop    es                ;
  434.     pop    edx                ;
  435.     pop    ecx                ;
  436.     pop    ebx                ;
  437.     pop    eax                ;
  438.     iretd                ; return to interrupted code
  439.  
  440. pm_irq4_hnd    endp
  441.  
  442. _codeseg    ends
  443.  
  444. ;****************************************************************************
  445. ; RM_IRQ4_HND - Real Mode IRQ4 Interrupt Handler
  446. ;    Just reads the character from the serial port and stuffs it into
  447. ;    the circular buffer.
  448. ;****************************************************************************
  449.     assume    cs:_rcodeseg, ds:dgroup
  450. _rcodeseg segment 
  451.  
  452.     public    rm_irq4_hnd
  453. rm_irq4_hnd    proc    near
  454.  
  455.     push    eax            ; save regs we use
  456.     push    ebx                ;
  457.     push    ecx                ;
  458.     push    edx                ;
  459.     push    ds                ;
  460.     mov    ax,cs            ; set DS to our data segment
  461.     mov    ds,ax                ;
  462.  
  463. ;
  464. ; Get the character and write it to the buffer, with interrupts still
  465. ; disabled so we can't get reentered.
  466. ;
  467. ; If the circular buffer is full, just drop this character on the floor.
  468. ;
  469.     mov    dx, PORTNO        ; read character from serial chip
  470.     in    al, dx                ;
  471.     mov    ecx,io_data.WR_IDX    ; calculate new write index after
  472.     inc    ecx                ; we stuff this char into
  473.     cmp    ecx,IOBUF_SIZE            ; buffer
  474.     jb    short #in_buf            ;
  475.     mov    ecx,0                ;
  476. #in_buf:                    ;
  477.     cmp    ecx,io_data.RD_IDX    ; drop this char if buffer full
  478.     je    short #done_ch            ;
  479.     mov    edx,io_data.WR_IDX    ; write this char to buffer
  480.     mov    io_data[edx].IO_BUF,al        ;
  481.     mov    io_data.WR_IDX,ecx    ; update write index
  482. #done_ch:
  483.  
  484. ;
  485. ; It's now OK to get reentered, so we can re-enable interrupts and 
  486. ; acknowledge the interrupt to the 8259
  487. ;
  488.     sti                ; re-enable interrupts
  489.     mov    al, 20h            ; send EOI to 8259
  490.     out    PIC_EOI, al            ;
  491.  
  492.     pop    ds            ; restore registers
  493.     pop    edx                ;
  494.     pop    ecx                ;
  495.     pop    ebx                ;
  496.     pop    eax                ;
  497.     iret                ; return to interrupted code
  498.  
  499. rm_irq4_hnd    endp
  500.  
  501. _rcodeseg ends
  502.  
  503.     end main
  504.