home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c220 / 7.ddi / EXAMPLES / INTHNDLR / SERIO.ASM < prev    next >
Encoding:
Assembly Source File  |  1990-11-30  |  14.1 KB  |  497 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',0Dh,0Ah,'$'
  120.     align    4
  121.  
  122. _data    ends
  123.  
  124. ;****************************************************************************
  125. ; Program entry point
  126. ;****************************************************************************
  127.  
  128.     assume    cs:_codeseg,ds:_data
  129. _codeseg    segment    
  130.  
  131.     public    main
  132. main proc    near
  133.  
  134. ;
  135. ; Copy the real mode code and data to a conventional memory block, and init
  136. ; globals for conv mem block
  137. ;
  138.     call    copy_real        ; set up real mode code/data
  139.     cmp    eax,TRUE        ; exit if error
  140.     je    #exit                ;
  141.  
  142. ;
  143. ; Save current real and prot mode IRQ4 vectors
  144. ;
  145.     mov    ax,250Ch        ; get IRQ4 interrupt number
  146.     int    21h                ;
  147.     add    al,4                ;
  148.     mov    irq4_int,al            ;
  149.     mov    cl, irq4_int        ; save real mode IRQ4 vector
  150.     mov    ax, 2503h            ; 
  151.     int    21h                ;
  152.     mov    rm_irq4_vec, ebx        ; 
  153.     mov    ax, 2502h        ; save prot mode IRQ4 vector
  154.     mov    cl,irq4_int            ;
  155.     int    21h                ; 
  156.     mov    pm_irq4_sel, es            ;
  157.     mov    pm_irq4_off, ebx        ; 
  158.  
  159. ;
  160. ; Install our real-mode and protected-mode IRQ4 (COM1) handlers.
  161. ;
  162.     push    ds            ; set real and prot mode IRQ4 vectors
  163.     mov    cl,irq4_int            ;
  164.     mov    bx,rm_csds            ;
  165.     shl    ebx,16                ;
  166.     lea    bx,rm_irq4_hnd            ;
  167.     lea    edx,pm_irq4_hnd            ;
  168.     mov    ax,cs                ;
  169.     mov    ds,ax                ;
  170.     mov    ax,2507h            ;
  171.     int    21h                ;
  172.     pop    ds                ;
  173.  
  174. ;
  175. ; Init MCR and IER in the 8250 serial chip. 
  176. ; Make sure IRQ4 is unmasked in the 8259 interrupt controller.
  177. ;
  178.     mov    dx, COM_MCR        ; set RTS, DTR, and OUT2 bits in the
  179.     mov    al, 0Bh                ; Modem Control Register
  180.     out    dx, al                ;
  181.     mov    dx, COM_IER        ; enable interrupts on receive data
  182.     mov    al, 01h                ; on the serial chip
  183.     out    dx, al                ;
  184.     in    al, PIC_MSK        ; unmask IRQ4 in the 8259 mask
  185.     and    al, NOT IRQ4_MSK        ; 
  186.     out    PIC_MSK, al            ;
  187.  
  188. ;
  189. ; Prompt user to send serial data
  190. ;
  191.     mov    ah, 09h            ; print prompt message
  192.     lea    edx, prompt_msg            ;
  193.     int    21h                ;
  194.  
  195. ;
  196. ; Loop, reading data from the buffer as it gets filled and printing
  197. ; it to the screen.  When we see a carriage return, exit the loop.
  198. ;
  199.     mov    ax,SS_DOSMEM        ; set ES:EBX to I/O data block in
  200.     mov    es,ax                ; conventional memory
  201.     mov    ebx,iodat_offs            ;
  202. #loop:
  203.     mov    ecx,es:[ebx].RD_IDX    ; keep looping if buffer empty
  204.     cmp    ecx,es:[ebx].WR_IDX        ;
  205.     je    #loop                ;
  206.     pushfd                ; get character and update read index,
  207.     cli                    ; disabling interrupts while
  208.     mov    dl,es:[ebx][ecx].IO_BUF        ; we update the I/O globals
  209.     inc    ecx                ;
  210.     cmp    ecx,IOBUF_SIZE            ;
  211.     jb    short #in_buf            ;
  212.     mov    ecx,0                ;
  213. #in_buf:                    ;
  214.     mov    es:[ebx].RD_IDX,ecx        ;
  215.     popfd                    ;
  216.     mov    ah, 02h            ; print the character in DL on the
  217.     int 21h                    ; display
  218.     cmp    dl,CR            ; continue loop unless it was CR
  219.     jne    #loop                ; 
  220.     mov    dl,LF            ; print a line feed
  221.     mov    ah,2                ;
  222.     int    21h                ;
  223.  
  224. ;
  225. ; Restore serial port to previous state, restore IRQ4 handlers, and
  226. ; free up the conventional memory block we allocated.
  227. ;
  228.     mov    dx, COM_MCR        ; clear RTS, DTR, and OUT2 in the
  229.     in    al, dx                ; Modem Control Register
  230.     and    al, 04h                ; 
  231.     out    dx, al                ;
  232.     mov    dx, COM_IER        ; disable serial chip interrupts
  233.     mov    al, 00h                ; 
  234.     out    dx, al                ;
  235.     in    al, PIC_MSK        ; mask off IRQ4 in the 8259
  236.     or    al, IRQ4_MSK            ; 
  237.     out    PIC_MSK, al            ;
  238.     push    ds            ; restore original real and prot mode 
  239.     mov    cl,irq4_int            ; IRQ4 vectors
  240.     mov    ebx, rm_irq4_vec        ;
  241.     mov    edx, pm_irq4_off        ;
  242.     mov    ax, pm_irq4_sel            ; 
  243.     mov    ds, ax                ;
  244.     mov    ax,2507h            ;
  245.     int    21h                ;
  246.     pop    ds                ;
  247.     mov    cx,cbuf_seg        ; free up conventional memory block
  248.     mov    ax,25C1h            ;
  249.     int    21h                ;
  250.  
  251. #exit:
  252.     mov    ax, 4C00h        ; exit to DOS
  253.     int    21h                ;
  254. main endp
  255.  
  256. ;****************************************************************************
  257. ; COPY_REAL -- copy real mode code & data to a conventional memory block,
  258. ;    and initialize global data used to manage the memory block
  259. ;
  260. ; Returns:  TRUE    if error
  261. ;        FALSE    if success
  262. ;****************************************************************************
  263.     public    copy_real
  264. copy_real proc    near
  265. ;
  266. ; Stack frame
  267. ;
  268. #REAL_NBYTES equ (dword ptr 12[ebp])    ; number of bytes RM code & data
  269. #REAL_START equ    (dword ptr 8[ebp])    ; start offset of RM code & data
  270.  
  271.     push    ebp            ; set up stack frame
  272.     mov    ebp,esp                ;
  273.     sub    esp,8            ; allocate local data
  274.     push    es            ; save regs
  275.     push    esi                ;
  276.     push    edi                ;
  277.  
  278. ;
  279. ; Allocate DOS segment in conventional memory the size of our real mode
  280. ; code and data.
  281. ;
  282. ; NOTE we are just assuming there is free conventional memory (the program
  283. ;    should have been linked with the -MINREAL switch), rather than
  284. ;    attempting to make sure memory is available with the 2525h and/or
  285. ;    2530h system calls as we would in a more elaborate program.
  286. ;
  287.     lea    ebx, end_real        ; end of RM code and data
  288.     test    ebx,0FFFF0000h        ; branch if not within 1st 64K of
  289.     jnz    #bad_segorder            ; of program segment
  290.     lea    ecx, start_real        ; start of RM code and data, rounded
  291.     and    ecx, not 0Fh            ; down to paragraph boundary
  292.     mov    #REAL_START, ecx        ; 
  293.     sub    ebx, ecx        ; RM code and data size in EBX
  294.     mov    #REAL_NBYTES, ebx        ; 
  295.     add    ebx, 15            ; round size up to paragraph count
  296.     shr    ebx, 4                ; 
  297.     mov    ax, 25C0h        ; alloc DOS memory block
  298.     int    21h                ; 
  299.     jc    #no_mem            ; branch if failure
  300.     mov    cbuf_seg, ax        ; save addr of conv mem block
  301.  
  302. ;
  303. ; Calculate a real mode segment address for real mode CS and DS that will
  304. ; allow the real mode code to use all its link-time offsets to code and data.
  305. ; Copy the real mode code and data to conventional memory.
  306. ;
  307.     mov    ax,cbuf_seg        ; calculate real-mode CS and DS
  308.     mov    ebx,#REAL_START            ;
  309.     shr    ebx,4                ;
  310.     sub    ax,bx                ;
  311.     mov    rm_csds,ax            ;
  312.     push    ds            ; copy code & data to conv mem block
  313.     mov    ecx, #REAL_NBYTES        ;
  314.     mov    ax, SS_DOSMEM            ;
  315.     mov    es, ax                ;
  316.     movzx    edi, cbuf_seg            ;
  317.     shl    edi, 4                ; 
  318.     mov    esi, #REAL_START        ;
  319.     mov    ax, cs                ;
  320.     mov    ds, ax                ; 
  321.     cld                    ;
  322.     rep    movsb                ; 
  323.     pop    ds                ;
  324.  
  325. ;
  326. ; Calculate the protected mode offset in segment 0034h to the I/O data
  327. ; block we keep in conventional memory.  Also initialize the data block.
  328. ;
  329.     lea    eax,io_data        ; get prot mode offset to I/O data
  330.     movzx    ebx,rm_csds            ; in conv memory block
  331.     shl    ebx,4                ;
  332.     add    eax,ebx                ;
  333.     mov    iodat_offs,eax            ;
  334.     mov    ax,SS_DOSMEM        ; init to empty buffer
  335.     mov    es,ax                ;
  336.     mov    eax,iodat_offs            ;
  337.     mov    es:[eax].RD_IDX,0        ;
  338.     mov    es:[eax].WR_IDX,0        ;
  339.  
  340.     mov    eax,FALSE        ; return success
  341. #exit:
  342.     pop    edi            ; restore regs
  343.     pop    esi                ;
  344.     pop    es                ;
  345.     mov    esp,ebp            ; restore stack frame & exit
  346.     pop    ebp                ;
  347.     ret                    ;
  348.  
  349. #no_mem:
  350. ;
  351. ; Couldn't allocate conventional memory for real mode code & data
  352. ;
  353. _data    segment
  354. nocmem_msg db    "Can't allocate conventional memory buffer",0Dh,0Ah,'$'
  355. _data    ends
  356.     mov    ah, 09h            ; print err message
  357.     lea    edx, nocmem_msg            ;
  358.     int    21h                ;
  359.     mov    eax,TRUE        ; return error
  360.     jmp    #exit                ;
  361.  
  362. #bad_segorder:
  363. ;
  364. ; real mode code & data not within 1st 64K of program segment
  365. ;
  366. _data    segment
  367. boffs_msg db    'Real mode code/data > 64K into program segment',0Dh,0Ah,'$'
  368. _data    ends
  369.     mov    ah, 09h            ; print err message
  370.     lea    edx, boffs_msg            ;
  371.     int    21h                ;
  372.     mov    eax,TRUE        ; return error
  373.     jmp    #exit                ;
  374. copy_real endp
  375.  
  376. ;****************************************************************************
  377. ; PM_IRQ4_HND - Protected Mode IRQ4 Interrupt Handler
  378. ;    Just reads the character from the serial port and stuffs it into
  379. ;    the circular buffer.
  380. ;****************************************************************************
  381.     public    pm_irq4_hnd
  382. pm_irq4_hnd    proc    near
  383.  
  384.     push    eax            ; save regs we use
  385.     push    ebx                ;
  386.     push    ecx                ;
  387.     push    edx                ;
  388.     push    es                ;
  389.     push    ds                ;
  390.     mov    ax,SS_DATA        ; set DS to our data segment
  391.     mov    ds, ax                ; 
  392.     mov    ax,SS_DOSMEM        ; set ES:EBX to I/O data in 
  393.     mov    es, ax                ; conventional memory
  394.     mov    ebx,iodat_offs            ;
  395.  
  396. ;
  397. ; Get the character and write it to the buffer, with interrupts still
  398. ; disabled so we can't get reentered.
  399. ;
  400. ; If the circular buffer is full, just drop this character on the floor.
  401. ;
  402.     mov    dx, PORTNO        ; read character from serial chip
  403.     in    al, dx                ;
  404.     mov    ecx,es:[ebx].WR_IDX    ; calculate new write index after
  405.     inc    ecx                ; we stuff this char into
  406.     cmp    ecx,IOBUF_SIZE            ; buffer
  407.     jb    short #in_buf            ;
  408.     mov    ecx,0                ;
  409. #in_buf:                    ;
  410.     cmp    ecx,es:[ebx].RD_IDX    ; drop this char if buffer full
  411.     je    short #done_ch            ;
  412.     mov    edx,es:[ebx].WR_IDX    ; write this char to buffer
  413.     mov    es:[ebx][edx].IO_BUF,al        ;
  414.     mov    es:[ebx].WR_IDX,ecx    ; update write index
  415. #done_ch:
  416.  
  417. ;
  418. ; It's now OK to get reentered, so we can re-enable interrupts and 
  419. ; acknowledge the interrupt to the 8259
  420. ;
  421.     sti                ; re-enable interrupts
  422.     mov    al, 20h            ; send EOI to 8259
  423.     out    PIC_EOI, al            ;
  424.  
  425.     pop    ds            ; restore registers
  426.     pop    es                ;
  427.     pop    edx                ;
  428.     pop    ecx                ;
  429.     pop    ebx                ;
  430.     pop    eax                ;
  431.     iretd                ; return to interrupted code
  432.  
  433. pm_irq4_hnd    endp
  434.  
  435. _codeseg    ends
  436.  
  437. ;****************************************************************************
  438. ; RM_IRQ4_HND - Real Mode IRQ4 Interrupt Handler
  439. ;    Just reads the character from the serial port and stuffs it into
  440. ;    the circular buffer.
  441. ;****************************************************************************
  442.     assume    cs:_rcodeseg, ds:dgroup
  443. _rcodeseg segment 
  444.  
  445.     public    rm_irq4_hnd
  446. rm_irq4_hnd    proc    near
  447.  
  448.     push    eax            ; save regs we use
  449.     push    ebx                ;
  450.     push    ecx                ;
  451.     push    edx                ;
  452.     push    ds                ;
  453.     mov    ax,cs            ; set DS to our data segment
  454.     mov    ds,ax                ;
  455.  
  456. ;
  457. ; Get the character and write it to the buffer, with interrupts still
  458. ; disabled so we can't get reentered.
  459. ;
  460. ; If the circular buffer is full, just drop this character on the floor.
  461. ;
  462.     mov    dx, PORTNO        ; read character from serial chip
  463.     in    al, dx                ;
  464.     mov    ecx,io_data.WR_IDX    ; calculate new write index after
  465.     inc    ecx                ; we stuff this char into
  466.     cmp    ecx,IOBUF_SIZE            ; buffer
  467.     jb    short #in_buf            ;
  468.     mov    ecx,0                ;
  469. #in_buf:                    ;
  470.     cmp    ecx,io_data.RD_IDX    ; drop this char if buffer full
  471.     je    short #done_ch            ;
  472.     mov    edx,io_data.WR_IDX    ; write this char to buffer
  473.     mov    io_data[edx].IO_BUF,al        ;
  474.     mov    io_data.WR_IDX,ecx    ; update write index
  475. #done_ch:
  476.  
  477. ;
  478. ; It's now OK to get reentered, so we can re-enable interrupts and 
  479. ; acknowledge the interrupt to the 8259
  480. ;
  481.     sti                ; re-enable interrupts
  482.     mov    al, 20h            ; send EOI to 8259
  483.     out    PIC_EOI, al            ;
  484.  
  485.     pop    ds            ; restore registers
  486.     pop    edx                ;
  487.     pop    ecx                ;
  488.     pop    ebx                ;
  489.     pop    eax                ;
  490.     iret                ; return to interrupted code
  491.  
  492. rm_irq4_hnd    endp
  493.  
  494. _rcodeseg ends
  495.  
  496.     end main
  497.