home *** CD-ROM | disk | FTP | other *** search
- ;************************************************************************/
- ;* Copyright (C) 1986-1988 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 */
- ;************************************************************************/
- ;
- ; PTAIL2.ASM - This is the protected mode portion
- ; of an example program that uses
- ; the Microsoft mouse driver, and also demonstrates
- ; several techniques for mixing real and protected
- ; mode code in the same program.
- ;
- ; FUNCTIONALITY - The program is used to draw rough
- ; pictures on the screen using the mouse. The
- ; program is always in one of two modes - character
- ; output mode, or toggle mode. When in character
- ; output mode pressing the right mouse button will
- ; output a character at the current cursor position.
- ; Pressing the left mouse button causes the program
- ; to enter toggle mode. When in toggle mode,
- ; pressing the right mouse button will cause the
- ; character that gets output to be toggled between
- ; a '*' and a ' ' (space). Pressing the left mouse
- ; button in toggle mode causes the program to exit.
- ; The program starts up in character output mode,
- ; with the output character set to '*'. The program
- ; is used to draw rudimentary pictures with '*'s,
- ; toggling the output character to a space to
- ; erase mistakes.
- ;
- ; IMPLEMENTATION - The program demonstrates having
- ; the protected mode code in a protected mode EXP
- ; file, and the real mode code in a separate EXE
- ; file. The protected mode program is linked (or
- ; run) with the -CALLBUFS switch to allocate
- ; a data buffer in conventional memory for
- ; passing data on intermode procedure calls, and
- ; the -MINREAL switch to make sure enough
- ; conventional memory is left free to load the
- ; real mode program. The protected mode program
- ; is run first, and it makes a Phar Lap EXEC
- ; system call to load the real mode program into
- ; memory without remapping the hardware interrupts
- ; back to their standard real mode settings.
- ; Once the real mode program is running,
- ; the two programs transfer control to each other
- ; with software interrupts.
- ;
-
- ;
- ; Segment ordering and attributes.
- ;
- pmdata segment dword public use32
- pmdata ends
- pmcode segment byte public use32
- pmcode ends
- stack segment dword stack use32
- stack ends
-
- ;
- ; Constants
- ;
- TRUE equ 1
- FALSE equ 0
-
- ;
- ; Make entry points and data global so they
- ; can be accessed by the debugger
- ;
- public main,init,cleanup,print_ch,pmi_hndlr
- public vid_mode,pm_cbuf,rm_cseg
-
- ;
- ; Give program a 4K stack
- ;
- stack segment
- db 4096 dup(?)
- stack ends
-
- ;
- ; Data structure for global data that is
- ; stored in the intermode call buffer, where
- ; both the real mode and the protected mode
- ; program can access it. This data
- ; is initialized by the protected mode program,
- ; and modified by the real mode program.
- ;
- SHARE struc
- DONEF dd ? ; flags when pgm done
- OUTP_CH db ? ; current output char
- SHARE ends
-
- ;
- ; Global data.
- ;
- pmdata segment
-
- vid_mode db ? ; saved video mode
-
- pm_cbuf label pword ; prot mode addr of
- pm_coffs dd ? ; intermode call
- pm_cseg dw ? ; buffer
- rm_cbuf dd ? ; real mode addr of buf
- rm_cseg dw ? ; segment addr of real
- ; mode pgm's code
- ; segment
-
- PROTINT equ 0F0h ; interrupt used to call
- ; from real to
- ; prot mode
- pmi_sel dw ? ; original prot mode
- pmi_offs dd ? ; interrupt vec
- rmi_adr dd ? ; orig real mode vector
-
- ;
- ; Data used to perform EXEC call.
- ;
- EXE_PBLK struc
- ENVOFFS dd ?
- ENVSEG dw ?
- CMDOFFS dd ?
- CMDSEG dw ?
- EXE_PBLK ends
- epblk EXE_PBLK <> ; exec param block
- rmpname db 'RTAIL2.EXE',0 ; real mode pgm name
- cmdtail db 1,PROTINT,0Dh ; cmd tail gives
- ; interrupt vec
- ;
- ; Error messages
- ;
- drivmsg db 'Mouse driver not present'
- db 0Dh,0Ah,'$'
- b2msg db 'Mouse must have 2 buttons'
- db 0Dh,0Ah,'$'
- bufmsg db 'Intermode call buffer too small'
- db 0Dh,0Ah,'$'
- loadmsg db 'Error loading real mode pgm'
- db 0Dh,0Ah,'$'
-
- pmdata ends
-
- page
- assume cs:pmcode,ds:pmdata
- pmcode segment
-
- ;***************************************************
- ; main - loads the real mode program and sets up the
- ; mouse handler, then execs to the real mode
- ; program, which will not terminate until the
- ; left mouse button is pressed twice.
- ;***************************************************
- main proc near ; program entry point
-
- call init ; init mouse
- cmp eax,TRUE ; branch if error
- je short #initerr ;
-
- les ebx,pm_cbuf ; set ES:EBX to point
- ; to call buf
- mov es:[ebx].DONEF,FALSE ; init done flag
- mov es:[ebx].OUTP_CH,'*' ; init output char
-
- ;
- ; Start the real mode program running. The first thing
- ; the real mode program will do is call the prot mode
- ; program (with the software interrupt identified in
- ; the command line from the exec) to get the address of
- ; the intermode call buffer. Then it will set up its
- ; own handler to get control when a mouse button is
- ; pressed, and just loop until the left button is pressed
- ; twice. At that point it terminates and the prot mode
- ; program will get control again at the instruction
- ; following the exec system call.
- ;
- mov epblk.ENVSEG,0 ; param blk = inherit
- mov ax,ds ; environment,
- mov epblk.CMDSEG,ax ; hardwired cmd
- mov epblk.CMDOFFS,offset cmdtail ; tail
- mov ax,ds ; load program
- mov es,ax ;
- lea ebx,epblk ;
- lea edx,rmpname ;
- mov ax,25C3h ;
- int 21h ;
- jc short #lderr ; branch if error
-
- ;
- ; OK, real mode pgm terminated so we are all done.
- ;
- call cleanup ; clean up mouse
- mov al,0 ; return success
-
- #exit:
- mov ah,4Ch ; exit to DOS
- int 21h ;
-
- #initerr:
- mov al,1 ; return error
- jmp #exit ;
- #lderr:
- lea edx,loadmsg
- #err:
- mov ah,9 ; output error msg
- int 21h ;
- call cleanup ; clean up mouse
- mov al,1 ; return error
- jmp #exit ;
-
- main endp
-
- ;***************************************************
- ; init - This routine checks for the presence
- ; of the mouse driver, and if it is present,
- ; initializes it. It also gets the address
- ; of the intermode call buffer which is used
- ; for passing data between the real and
- ; protected mode programs, and takes over
- ; the software interrupt which is used by
- ; the real mode program to call the prot
- ; mode program.
- ;
- ; Returns: TRUE if error
- ; FALSE if success
- ;***************************************************
- init proc near
-
- push es ; save regs
-
- ;
- ; Save the current video mode, and set the video mode
- ; to 80x25 B&W (8x8 cell size).
- ;
- mov ah,0Fh ; get video state
- int 10h ;
- mov vid_mode,al ;
- mov ax,0002h ; set video state
- int 10h ;
-
- ;
- ; Get real and prot mode addresses of intermode call
- ; buffer, and check to make sure the call buffer
- ; is sufficiently large.
- ;
- mov ax,250Dh ; get real mode link
- int 21h ; info
- cmp ecx,size SHARE ; branch if call buf
- jb #buferr ; too small
- mov rm_cbuf,ebx ; save RM buf addr
- mov pm_coffs,edx ; save PM buf addr
- mov ax,es ;
- mov pm_cseg,ax ;
-
- ;
- ; Check for presence of mouse driver, take error
- ; exit if not present, initialize it if present
- ;
- mov cl,33h ; get mouse real mode
- mov ax,2503h ; int vector
- int 21h ;
- cmp ebx,0 ; branch if null
- je #no_driver ;
- xor eax,eax ; branch if vector
- mov ax,bx ; just points
- and ebx,0FFFF0000h ; to an IRET
- shr ebx,12 ;
- add ebx,eax ;
- mov ax,0034h ;
- mov es,ax ;
- cmp byte ptr es:[ebx],0CFh ;
- je short #no_driver ;
- mov ax,0 ; Initialize mouse
- int 33h ; driver
- cmp ax,0 ; branch if not
- je short #no_driver ; installed
- cmp bx,2 ; pgm requires 2
- jne short #buttons ; buttons
-
- ;
- ; Save the current protected AND real mode interrupt
- ; vectors for the interrupt used to call from real
- ; mode to protected mode, and then take over the
- ; interrupt so that we always gain control in our
- ; protected mode handler (taking over the interrupt
- ; in this fashion changes both the real and prot
- ; mode interrupt vectors).
- ;
- mov ax,2502h ; save original
- mov cl,PROTINT ; prot mode
- int 21h ; vector
- mov pmi_sel,es ;
- mov pmi_offs,ebx ;
- mov ax,2503h ; save original
- mov cl,PROTINT ; real mode
- int 21h ; vector
- mov rmi_adr,ebx ;
- push ds ; take over int
- mov ax,cs ; to always
- mov ds,ax ; get control
- lea edx,pmi_hndlr ; in prot
- mov cl,PROTINT ; mode
- mov ax,2506h ;
- int 21h ;
- pop ds ;
-
- ;
- ; OK, all initialized - display mouse cursor and exit
- ;
- mov ax,1 ; display mouse cursor
- int 33h ;
- mov eax,FALSE ; return success
-
- #exit:
- pop es ; restore regs
- ret ; & exit
-
- #buttons:
- lea edx,b2msg
- jmp short #err
- #no_driver:
- lea edx,drivmsg
- jmp short #err
- #buferr:
- lea edx,bufmsg
- #err:
- mov ah,0 ; restore video mode
- mov al,vid_mode ;
- int 10h ;
- mov ah,9 ; output error msg
- int 21h ;
- mov eax,TRUE ; return error
- jmp #exit ;
-
- init endp
-
- ;***************************************************
- ; cleanup - This routine resets the mouse driver to
- ; set the interrupt call mask to 0 and hide
- ; the cursor, and restores the original video
- ; mode, and restores the original interrupt
- ; vectors for the interrupt we took over.
- ;***************************************************
- cleanup proc near
- push es ; save regs we modify
-
- mov ax,0 ; reset mouse
- int 33h ;
- mov ah,0 ; restore video mode
- mov al,vid_mode ;
- int 10h ;
-
- push ds ; restore original
- mov edx,pmi_offs ; prot mode
- mov ax,pmi_sel ; vector
- mov ds,ax ;
- mov cl,PROTINT ;
- mov ax,2504h ;
- int 21h ;
- pop ds ;
- mov ebx,rmi_adr ; restore original
- mov cl,PROTINT ; real mode
- mov ax,2505h ; vector
- int 21h ;
-
- #exit:
- pop es ; restore regs &
- ret ; exit
- cleanup endp
-
- ;***************************************************
- ; pmi_hndlr - This is the interrupt handler that gets
- ; control when the real mode program issues
- ; the software interrupt we have taken over.
- ; Since we know we always get control from
- ; real mode, we know 386|DOS-Extender will
- ; always have set up the segment registers
- ; as they were at startup time, so we don't
- ; have to set them up here.
- ;
- ; This routine takes a code specifying the
- ; desired function in EAX. The following
- ; functions are supported:
- ;
- ; EAX=1 Initialize interface
- ; IN: BL = interrupt number to call thru
- ; to real mode pgm
- ; CX = segment addr of code segment
- ; in real mode program (used
- ; only for debugging)
- ; OUT: EAX = FALSE
- ; EBX = real mode addr of call buffer
- ;
- ; EAX=2 Process mouse button hit when in
- ; character output mode
- ; IN: CX = horizontal cursor coordinate
- ; DX = vertical cursor coordinate
- ; WORD on top of original stack = condition mask
- ; OUT: EAX = TRUE if error, FALSE if success
- ; EBX-EDX = destroyed
- ;
- ; EAX = anything else
- ; OUT: EAX = TRUE (error)
- ;***************************************************
- pmi_hndlr proc near
- ;
- ; Stack frame
- ;
- #ORIG_SS equ (dword ptr 32[ebp])
- #ORIG_ESP equ (dword ptr 28[ebp])
-
- push ebp ; set up stack frame
- mov ebp,esp ;
-
- cmp eax,1 ; branch if init func
- je short #init ;
- cmp eax,2 ; branch if mouse
- je short #button ; button
- jmp short #err ; bad function number
-
- #init:
- ;
- ; We modify the code for the INT instruction that calls
- ; thru to real mode in the print_ch routine, now that we
- ; know which interrupt the real mode program took over.
- ; Since we know that CS and DS map the same program
- ; segment, there is no need to set up an alias for the
- ; code segment to do this.
- ;
- assume ds:pmcode ; Modify INT instruction
- mov byte ptr int_inst+1,bl ;
- assume ds:pmdata ;
- mov rm_cseg,cx ; save code segment addr
- ; for real mode pgm
- mov ebx,rm_cbuf ; return real mode addr
- ; of call buffer
- mov eax,FALSE ; return success
- jmp short #exit ;
-
- #button:
- push es ; get condition mask off orig stack
- mov ax,34h ;
- mov es,ax ;
- movzx eax,word ptr #ORIG_SS ;
- shl eax,4 ;
- add eax,#ORIG_ESP ;
- mov ax,es:[eax] ;
- pop es ;
- push ax ; process button hit
- call print_ch ; (ret's status
- add esp,2 ; in EAX)
-
- #exit:
- pop ebp ; restore registers
- iretd ; return to caller
-
- #err:
- mov eax,TRUE ; return error
- jmp #exit ;
-
- pmi_hndlr endp
-
- ;***************************************************
- ; print_ch(cond_msk)
- ; WORD cond_msk;
- ;
- ; This routine is called from real mode
- ; when either mouse button is pressed and we
- ; are in character input mode.
- ;
- ; If the left button was pressed, this proc
- ; puts us in toggle mode by calling (via a
- ; software interrupt) the real mode routine
- ; that sets up the mouse handler to the
- ; toggle mode handler in the real mode pgm.
- ;
- ; If the right button was pressed, this proc
- ; outputs the current output character at
- ; the specified cursor position.
- ;
- ; Inputs:
- ; 1st stack param = mouse condition mask
- ; CX = horizontal cursor coord
- ; DX = vertical cursor coord
- ;
- ; Outputs:
- ; AX = TRUE if error, FALSE if success
- ; EBX-EDX = destroyed
- ;***************************************************
- print_ch proc near
- ;
- ; Stack frame
- ;
- #COND_MSK equ (word ptr 8[ebp])
-
- push ebp ; Set up stack frame
- mov ebp,esp ;
- push es ; save regs
- push fs ;
-
- test #COND_MSK,2 ; branch if left button
- jz short #right ; not pressed
-
- ;
- ; Left mouse button was pressed. All we do is
- ; call a routine in the real mode program (with a
- ; software interrupt), which changes to toggle mode
- ; by setting up the toggle mode handler in the real
- ; mode program to get control when a mouse button
- ; is pressed. The interrupt to call is set up
- ; by modifying the code during initialization.
- ;
- int_inst:
- int 0 ; toggle modes
- jmp short #exit ; all done
-
- #right:
- ;
- ; Right mouse button was pressed. Set up ES to point
- ; to screen segment.
- ;
- mov ax,001Ch
- mov es,ax
-
- ;
- ; Get offset in screen segment of 1st character on
- ; the line the cursor is on
- ;
- shr dx,3 ; convert pixel to line
- imul dx,160 ; 160 bytes/line
-
- ;
- ; Add in the column number * 2 (2 bytes/char)
- ;
- shr cx,2
- add cx,dx
- mov bx,cx
-
- ;
- ; Output the character at the cursor.
- ;
- mov ah,7 ; normal attribute
- lfs edx,pm_cbuf ; get character
- mov al,fs:[edx].OUTP_CH ;
- mov word ptr es:[bx],ax ; output it
-
- mov ax,FALSE ; return success
-
- #exit:
- pop fs ; restore regs &
- pop es ; exit
- pop ebp ;
- ret ;
- print_ch endp
-
- pmcode ends
-
- end main ; program entry point
-