home *** CD-ROM | disk | FTP | other *** search
- .386p
- ;************************************************************************/
- ;* Copyright (C) 1986-1990 Phar Lap Software, Inc. */
- ;* Unpublished - rights reserved under the Copyright Laws of the */
- ;* United States. Use, duplication, or disclosure by the */
- ;* Government is subject to restrictions as set forth in */
- ;* subparagraph (c)(1)(ii) of the Rights in Technical Data and */
- ;* Computer Software clause at 252.227-7013. */
- ;* Phar Lap Software, Inc., 60 Aberdeen Ave., Cambridge, MA 02138 */
- ;************************************************************************/
-
- ;
- ; This program installs bimodal (separate real-mode and protected-mode)
- ; handlers for the IRQ4 (COM1 serial port) hardware interrupt. The
- ; interrupt handlers just stuff received characters into a circular
- ; buffer. The main program loop retrieves characters out of the buffer
- ; and prints them on the screen until it sees a carriage return, when
- ; it exits.
- ;
- ; This program does NOT set up the serial port parameters; use the DOS
- ; MODE command to program the port appropriately before running this
- ; program. For example, to use 9600 baud, no parity, 8 data bits, one
- ; stop bit, use the command:
- ; mode com1 9600,n,8,1
- ;
- ; Rather than use the -REALBREAK switch (which is not DPMI-compatible)
- ; to get the real mode code and data into conventional memory, we
- ; allocate a conventional memory block at startup time and copy the
- ; real mode code and data into it.
- ;
-
- ;
- ; Constants and data structures
- ;
- include dosx.ah
-
- IOBUF_SIZE equ 32 ; Size of circular buffer
- CR equ 0Dh ; ASCII carriage return
- LF equ 0Ah ; ASCII linefeed
- PIC_MSK equ 21h ; PIC Mask Register port #
- PIC_EOI equ 20h ; PIC EOI Register port #
- PORTNO equ 3F8h ; COM1 base address (port #)
- COM_IER equ PORTNO+1 ; Serial port INT Enable Reg
- COM_MCR equ PORTNO+4 ; Serial port Modem Ctrl Reg
- IRQ4_MSK equ 10h ; PIC enable IRQ4 INTs mask
-
- ;
- ; Struct for data kept in conventional memory that also needs to be accessed
- ; from protected mode
- ;
- REALDATA struc
- RD_IDX dd ? ; current read index in IO_BUF
- WR_IDX dd ? ; current write index in IO_BUF
- IO_BUF db IOBUF_SIZE dup (?) ; 32 byte circular I/O buffer
- REALDATA ends
-
- ;
- ; Segment definitions and ordering
- ;
- _rcodeseg segment byte public use16 'rm_code'
- public start_real
- start_real label byte
- _rcodeseg ends
- _rdata segment dword public use16 'rm_data'
- _rdata ends
- _data segment dword public use32 'data'
- _data ends
- dgroup group _rdata, _data
- _codeseg segment byte public use32 'code'
- _codeseg ends
- _stack segment dword stack use32 'stack'
- db 2048 dup (?) ; 2K stack
- _stack ends
-
- ;
- ; Global data that needs to be accessed in both real mode and protected mode
- ; This data gets copied to conventional memory; it is not used in its
- ; original link-time location.
- ;
- _rdata segment
-
- io_data REALDATA <> ; I/O buffer and circular buffer pointers
-
- public end_real
- end_real label byte
-
- _rdata ends
-
- ;
- ; Global protected mode data
- ;
- _data segment
-
- ;
- ; Original RM and PM IRQ4 vectors
- ;
- public rm_irq4_vec,pm_irq4_off,pm_irq4_sel,irq4_int
- rm_irq4_vec dd ? ; original real mode vector
- pm_irq4_off dd ? ; original prot mode vector
- pm_irq4_sel dw ? ;
- irq4_int db ? ; interrupt number for IRQ4
- align 4
-
- ;
- ; Conventional memory block control. Note that the real mode CS and DS
- ; values for our real mode IRQ4 handler are not necessarily the same as
- ; the address of the memory block. This is because if necessary we back
- ; them off so the link-time offsets to code and data will still be correct
- ; at run time. It is necessary to do this if the real mode code and data
- ; don't begin at the start of the protected mode segment (eg, if the
- ; -OFFSET 1000h link-time switch is used).
- ;
- cbuf_seg dw ? ; real mode paragraph addr of memory block
- rm_csds dw ? ; real mode CS and DS values
- align 4
- iodat_offs dd ? ; offset in segment 0034h (the DOS memory
- ; seg) of the io_data structure
-
- prompt_msg db 'Send CR-terminated data over serial port 1, '
- db 'or type CTRL-C to exit',0Dh,0Ah,'$'
- align 4
-
- _data ends
-
- ;****************************************************************************
- ; Program entry point
- ;****************************************************************************
-
- assume cs:_codeseg,ds:_data
- _codeseg segment
-
- public main
- main proc near
-
- ;
- ; Copy the real mode code and data to a conventional memory block, and init
- ; globals for conv mem block
- ;
- call copy_real ; set up real mode code/data
- cmp eax,TRUE ; exit if error
- je #exit ;
-
- ;
- ; Save current real and prot mode IRQ4 vectors
- ;
- mov ax,250Ch ; get IRQ4 interrupt number
- int 21h ;
- add al,4 ;
- mov irq4_int,al ;
- mov cl, irq4_int ; save real mode IRQ4 vector
- mov ax, 2503h ;
- int 21h ;
- mov rm_irq4_vec, ebx ;
- mov ax, 2502h ; save prot mode IRQ4 vector
- mov cl,irq4_int ;
- int 21h ;
- mov pm_irq4_sel, es ;
- mov pm_irq4_off, ebx ;
-
- ;
- ; Install our real-mode and protected-mode IRQ4 (COM1) handlers.
- ;
- push ds ; set real and prot mode IRQ4 vectors
- mov cl,irq4_int ;
- mov bx,rm_csds ;
- shl ebx,16 ;
- lea bx,rm_irq4_hnd ;
- lea edx,pm_irq4_hnd ;
- mov ax,cs ;
- mov ds,ax ;
- mov ax,2507h ;
- int 21h ;
- pop ds ;
-
- ;
- ; Init MCR and IER in the 8250 serial chip.
- ; Make sure IRQ4 is unmasked in the 8259 interrupt controller.
- ;
- mov dx, COM_MCR ; set RTS, DTR, and OUT2 bits in the
- mov al, 0Bh ; Modem Control Register
- out dx, al ;
- mov dx, COM_IER ; enable interrupts on receive data
- mov al, 01h ; on the serial chip
- out dx, al ;
- in al, PIC_MSK ; unmask IRQ4 in the 8259 mask
- and al, NOT IRQ4_MSK ;
- out PIC_MSK, al ;
-
- ;
- ; Prompt user to send serial data
- ;
- mov ah, 09h ; print prompt message
- lea edx, prompt_msg ;
- int 21h ;
-
- ;
- ; Loop, reading data from the buffer as it gets filled and printing
- ; it to the screen. When we see a carriage return, exit the loop.
- ;
- ; Make a do-nothing DOS call inside the loop (get default disk) to
- ; allow DOS to generate a CTRL-C interrupt so the user can kill the
- ; program if they can't get serial hookup to work.
- ;
- mov ax,SS_DOSMEM ; set ES:EBX to I/O data block in
- mov es,ax ; conventional memory
- mov ebx,iodat_offs ;
- #loop:
- mov ah,19h ; DOS call to allow CTRL-C
- int 21h ;
- mov ecx,es:[ebx].RD_IDX ; keep looping if buffer empty
- cmp ecx,es:[ebx].WR_IDX ;
- je #loop ;
- pushfd ; get character and update read index,
- cli ; disabling interrupts while
- mov dl,es:[ebx][ecx].IO_BUF ; we update the I/O globals
- inc ecx ;
- cmp ecx,IOBUF_SIZE ;
- jb short #in_buf ;
- mov ecx,0 ;
- #in_buf: ;
- mov es:[ebx].RD_IDX,ecx ;
- popfd ;
- mov ah, 02h ; print the character in DL on the
- int 21h ; display
- cmp dl,CR ; continue loop unless it was CR
- jne #loop ;
- mov dl,LF ; print a line feed
- mov ah,2 ;
- int 21h ;
-
- ;
- ; Restore serial port to previous state, restore IRQ4 handlers, and
- ; free up the conventional memory block we allocated.
- ;
- mov dx, COM_MCR ; clear RTS, DTR, and OUT2 in the
- in al, dx ; Modem Control Register
- and al, 04h ;
- out dx, al ;
- mov dx, COM_IER ; disable serial chip interrupts
- mov al, 00h ;
- out dx, al ;
- in al, PIC_MSK ; mask off IRQ4 in the 8259
- or al, IRQ4_MSK ;
- out PIC_MSK, al ;
- push ds ; restore original real and prot mode
- mov cl,irq4_int ; IRQ4 vectors
- mov ebx, rm_irq4_vec ;
- mov edx, pm_irq4_off ;
- mov ax, pm_irq4_sel ;
- mov ds, ax ;
- mov ax,2507h ;
- int 21h ;
- pop ds ;
- mov cx,cbuf_seg ; free up conventional memory block
- mov ax,25C1h ;
- int 21h ;
-
- #exit:
- mov ax, 4C00h ; exit to DOS
- int 21h ;
- main endp
-
- ;****************************************************************************
- ; COPY_REAL -- copy real mode code & data to a conventional memory block,
- ; and initialize global data used to manage the memory block
- ;
- ; Returns: TRUE if error
- ; FALSE if success
- ;****************************************************************************
- public copy_real
- copy_real proc near
- ;
- ; Stack frame
- ;
- #REAL_NBYTES equ (dword ptr 12[ebp]) ; number of bytes RM code & data
- #REAL_START equ (dword ptr 8[ebp]) ; start offset of RM code & data
-
- push ebp ; set up stack frame
- mov ebp,esp ;
- sub esp,8 ; allocate local data
- push es ; save regs
- push esi ;
- push edi ;
-
- ;
- ; Allocate DOS segment in conventional memory the size of our real mode
- ; code and data.
- ;
- ; NOTE we are just assuming there is free conventional memory (the program
- ; should have been linked with the -MINREAL switch), rather than
- ; attempting to make sure memory is available with the 2525h and/or
- ; 2530h system calls as we would in a more elaborate program.
- ;
- lea ebx, end_real ; end of RM code and data
- test ebx,0FFFF0000h ; branch if not within 1st 64K of
- jnz #bad_segorder ; of program segment
- lea ecx, start_real ; start of RM code and data, rounded
- and ecx, not 0Fh ; down to paragraph boundary
- mov #REAL_START, ecx ;
- sub ebx, ecx ; RM code and data size in EBX
- mov #REAL_NBYTES, ebx ;
- add ebx, 15 ; round size up to paragraph count
- shr ebx, 4 ;
- mov ax, 25C0h ; alloc DOS memory block
- int 21h ;
- jc #no_mem ; branch if failure
- mov cbuf_seg, ax ; save addr of conv mem block
-
- ;
- ; Calculate a real mode segment address for real mode CS and DS that will
- ; allow the real mode code to use all its link-time offsets to code and data.
- ; Copy the real mode code and data to conventional memory.
- ;
- mov ax,cbuf_seg ; calculate real-mode CS and DS
- mov ebx,#REAL_START ;
- shr ebx,4 ;
- sub ax,bx ;
- mov rm_csds,ax ;
- push ds ; copy code & data to conv mem block
- mov ecx, #REAL_NBYTES ;
- mov ax, SS_DOSMEM ;
- mov es, ax ;
- movzx edi, cbuf_seg ;
- shl edi, 4 ;
- mov esi, #REAL_START ;
- mov ax, cs ;
- mov ds, ax ;
- cld ;
- rep movsb ;
- pop ds ;
-
- ;
- ; Calculate the protected mode offset in segment 0034h to the I/O data
- ; block we keep in conventional memory. Also initialize the data block.
- ;
- lea eax,io_data ; get prot mode offset to I/O data
- movzx ebx,rm_csds ; in conv memory block
- shl ebx,4 ;
- add eax,ebx ;
- mov iodat_offs,eax ;
- mov ax,SS_DOSMEM ; init to empty buffer
- mov es,ax ;
- mov eax,iodat_offs ;
- mov es:[eax].RD_IDX,0 ;
- mov es:[eax].WR_IDX,0 ;
-
- mov eax,FALSE ; return success
- #exit:
- pop edi ; restore regs
- pop esi ;
- pop es ;
- mov esp,ebp ; restore stack frame & exit
- pop ebp ;
- ret ;
-
- #no_mem:
- ;
- ; Couldn't allocate conventional memory for real mode code & data
- ;
- _data segment
- nocmem_msg db "Can't allocate conventional memory buffer",0Dh,0Ah,'$'
- _data ends
- mov ah, 09h ; print err message
- lea edx, nocmem_msg ;
- int 21h ;
- mov eax,TRUE ; return error
- jmp #exit ;
-
- #bad_segorder:
- ;
- ; real mode code & data not within 1st 64K of program segment
- ;
- _data segment
- boffs_msg db 'Real mode code/data > 64K into program segment',0Dh,0Ah,'$'
- _data ends
- mov ah, 09h ; print err message
- lea edx, boffs_msg ;
- int 21h ;
- mov eax,TRUE ; return error
- jmp #exit ;
- copy_real endp
-
- ;****************************************************************************
- ; PM_IRQ4_HND - Protected Mode IRQ4 Interrupt Handler
- ; Just reads the character from the serial port and stuffs it into
- ; the circular buffer.
- ;****************************************************************************
- public pm_irq4_hnd
- pm_irq4_hnd proc near
-
- push eax ; save regs we use
- push ebx ;
- push ecx ;
- push edx ;
- push es ;
- push ds ;
- mov ax,SS_DATA ; set DS to our data segment
- mov ds, ax ;
- mov ax,SS_DOSMEM ; set ES:EBX to I/O data in
- mov es, ax ; conventional memory
- mov ebx,iodat_offs ;
-
- ;
- ; Get the character and write it to the buffer, with interrupts still
- ; disabled so we can't get reentered.
- ;
- ; If the circular buffer is full, just drop this character on the floor.
- ;
- mov dx, PORTNO ; read character from serial chip
- in al, dx ;
- mov ecx,es:[ebx].WR_IDX ; calculate new write index after
- inc ecx ; we stuff this char into
- cmp ecx,IOBUF_SIZE ; buffer
- jb short #in_buf ;
- mov ecx,0 ;
- #in_buf: ;
- cmp ecx,es:[ebx].RD_IDX ; drop this char if buffer full
- je short #done_ch ;
- mov edx,es:[ebx].WR_IDX ; write this char to buffer
- mov es:[ebx][edx].IO_BUF,al ;
- mov es:[ebx].WR_IDX,ecx ; update write index
- #done_ch:
-
- ;
- ; It's now OK to get reentered, so we can re-enable interrupts and
- ; acknowledge the interrupt to the 8259
- ;
- sti ; re-enable interrupts
- mov al, 20h ; send EOI to 8259
- out PIC_EOI, al ;
-
- pop ds ; restore registers
- pop es ;
- pop edx ;
- pop ecx ;
- pop ebx ;
- pop eax ;
- iretd ; return to interrupted code
-
- pm_irq4_hnd endp
-
- _codeseg ends
-
- ;****************************************************************************
- ; RM_IRQ4_HND - Real Mode IRQ4 Interrupt Handler
- ; Just reads the character from the serial port and stuffs it into
- ; the circular buffer.
- ;****************************************************************************
- assume cs:_rcodeseg, ds:dgroup
- _rcodeseg segment
-
- public rm_irq4_hnd
- rm_irq4_hnd proc near
-
- push eax ; save regs we use
- push ebx ;
- push ecx ;
- push edx ;
- push ds ;
- mov ax,cs ; set DS to our data segment
- mov ds,ax ;
-
- ;
- ; Get the character and write it to the buffer, with interrupts still
- ; disabled so we can't get reentered.
- ;
- ; If the circular buffer is full, just drop this character on the floor.
- ;
- mov dx, PORTNO ; read character from serial chip
- in al, dx ;
- mov ecx,io_data.WR_IDX ; calculate new write index after
- inc ecx ; we stuff this char into
- cmp ecx,IOBUF_SIZE ; buffer
- jb short #in_buf ;
- mov ecx,0 ;
- #in_buf: ;
- cmp ecx,io_data.RD_IDX ; drop this char if buffer full
- je short #done_ch ;
- mov edx,io_data.WR_IDX ; write this char to buffer
- mov io_data[edx].IO_BUF,al ;
- mov io_data.WR_IDX,ecx ; update write index
- #done_ch:
-
- ;
- ; It's now OK to get reentered, so we can re-enable interrupts and
- ; acknowledge the interrupt to the 8259
- ;
- sti ; re-enable interrupts
- mov al, 20h ; send EOI to 8259
- out PIC_EOI, al ;
-
- pop ds ; restore registers
- pop edx ;
- pop ecx ;
- pop ebx ;
- pop eax ;
- iret ; return to interrupted code
-
- rm_irq4_hnd endp
-
- _rcodeseg ends
-
- end main
-