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 */
- ;************************************************************************/
- ;
- ; TAIL.ASM - This is 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 mixing
- ; real and protected mode code in a single EXP
- ; file (in this instance, in a single source code
- ; file). The real mode code and data is linked at
- ; the beginning of the program, and the -REALBREAK
- ; switch is used at link time to specify how much
- ; of the program must be loaded in conventional
- ; MS-DOS memory. The program has three routines
- ; that run in real mode - a setup routine that calls
- ; the mouse driver to specify the initial mouse
- ; interrupt handler, and two mouse interrupt
- ; which correspond to the two program states,
- ; character input mode and toggle mode. The
- ; program demonstrates how to make intermode
- ; procedure calls passing data both on the stack
- ; and in registers, and how to issue real mode
- ; interrupts from protected mode with arbitrary
- ; real mode segment register values.
- ;
-
- ;
- ; Segment ordering and attributes. We make sure the
- ; real mode code and data comes first. The real and
- ; prot mode data are together so they can be grouped.
- ;
- rmcode segment byte public use16
- rmcode ends
- rmdata segment dword public use16
- rmdata ends
- pmdata segment dword public use32
- pmdata ends
- pmcode segment byte public use32
- pmcode ends
- stack segment dword stack use32
- stack ends
- dgroup group rmdata,pmdata
-
- ;
- ; 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,rm_setup,ch_hndlr
- public tog_hndlr,print_ch
- public donef,call_prot,vid_mode
-
- ;
- ; Give program a 4K stack
- ;
- stack segment
- db 4096 dup(?)
- stack ends
-
- ;
- ; Data that needs to be accessed in both real
- ; mode and protected mode
- ;
- rmdata segment
-
- donef dd ? ; flags when done
- outp_ch db ? ; current output char
- ; ('*' or ' ')
- call_prot dd ? ; addr of routine to
- ; call thru to prot mode
- code_sel dw ? ; prot mode code selector for this pgm
-
- chndlrmsg db 'Output char at cursor error'
- db 0Dh,0Ah,'$'
-
- ;
- ; Symbol marking end of real mode code & data,
- ; used at link time to specify the real mode
- ; code & data size
- ;
- public end_real
- end_real label byte
-
- rmdata ends
-
- ;
- ; Data that is only accessed in prot mode
- ;
- pmdata segment
-
- rm_seg dw ? ; real mode segment addr
- ; of start of
- ; pgm segment
- vid_mode db ? ; saved video mode
-
- drivmsg db 'Mouse driver not present'
- db 0Dh,0Ah,'$'
- b2msg db 'Mouse must have 2 buttons'
- db 0Dh,0Ah,'$'
- realmsg db 'Real mode code not addressable'
- db 0Dh,0Ah,'$'
- callmsg db 'Call to real mode failed'
- db 0Dh,0Ah,'$'
- setupmsg db 'Real mode setup routine error'
- db 0Dh,0Ah,'$'
-
- pmdata ends
-
- ;
- ; Because of data grouping, this ASSUME will
- ; allow the assembler to correctly reference
- ; data in both real mode and prot mode code
- ;
- assume ds:dgroup
- page
- ;
- ; Protected mode code
- ;
- assume cs:pmcode
- pmcode segment
-
- ;***************************************************
- ; main - sets up the mouse handler, then just loops,
- ; processing mouse button hits until the left
- ; one is pressed twice.
- ;***************************************************
- main proc near ; program entry point
-
- call init ; init mouse
- cmp eax,TRUE ; branch if error
- je short #err ;
-
- mov donef,FALSE ; init done flag
- mov outp_ch,'*' ; init output char
- #wait:
- cmp donef,FALSE ; loop until handler
- je #wait ; sets flag
-
- call cleanup ; clean up mouse
- mov al,0 ; return success
-
- #exit:
- mov ah,4Ch ; exit to DOS
- int 21h ;
-
- #err:
- 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 and sets up the real mode
- ; handler to get control when a mouse button
- ; is pressed. It also sets up an appropriate
- ; video mode, and gets the address of the
- ; real mode DOS-Extender routine which is
- ; used to call through to protected mode.
- ;
- ; Returns: TRUE if error
- ; FALSE if success
- ;***************************************************
- init proc near
-
- push es ; save regs
-
- ;
- ; Get code segment selector for this program
- ;
- mov code_sel,cs
-
- ;
- ; 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 address of DOS-Extender routine to use to
- ; call through from real mode to protected mode
- ;
- mov ax,250Dh ; get real mode link
- int 21h ; info
- mov call_prot,eax ; save proc address
-
- ;
- ; 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
-
- ;
- ; Call through to real mode to set up interrupt
- ; handler for mouse driver. This call demonstrates
- ; passing data both on the stack and in registers,
- ; and getting back a value in a register.
- ;
- mov ax,ds ; get real mode para
- mov es,ax ; address of
- xor ebx,ebx ; program
- lea ecx,end_real ; segment
- mov ax,250Fh ;
- int 21h ;
- jc short #not_real ; branch if error
- test ecx,0FFFFh ; branch if not on
- jnz short #not_real ; para boundary
- mov ebx,ecx ; EBX = real mode addr
- lea bx,rm_setup ; of rm_setup
- shr ecx,16 ; pass hndlr seg addr
- push cx ; on stack
- mov rm_seg,cx ; save seg addr
- lea dx,ch_hndlr ; pass offset in DX
- mov ecx,1 ; 1 WORD on stack
- mov ax,250Eh ; call rm_setup
- int 21h ;
- jnc short #ok ; branch if success
- add esp,2 ; pop args and take
- jmp short #bad_call ; error exit
- #ok:
- add esp,2 ; pop args off stack
- cmp ax,FALSE ; branch if error
- jne short #setup_err ;
-
- ;
- ; 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
- #not_real:
- lea edx,realmsg
- jmp short #err
- #setup_err:
- lea edx,setupmsg
- jmp short #err
- #bad_call:
- lea edx,callmsg
- #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.
- ;***************************************************
- cleanup proc near
- mov ax,0 ; reset mouse
- int 33h ;
- mov ah,0 ; restore video mode
- mov al,vid_mode ;
- int 10h ;
- #exit:
- ret
- cleanup 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 setting the
- ; mouse handler routine to be tog_hndlr.
- ;
- ; If the right button was pressed, this proc
- ; outputs the current output character at
- ; the specified cursor position.
- ;
- ; This routine illustrates how to issue a
- ; real mode interrupt with arbitrary segment
- ; register values.
- ;
- ; Inputs:
- ; 1st stack param = mouse condition mask
- ; CX = horizontal cursor coord
- ; DX = vertical cursor coord
- ;
- ; Outputs:
- ; AX = TRUE if error, FALSE if success
- ; BX-DX = destroyed
- ; High word of all 32-bit registers preserved
- ;***************************************************
- ;
- ; Data structure passed to system call 2511h, issue
- ; real mode interrupt with arbitrary seg reg values
- ;
- RMINT struc
- RMI_INUM dw ? ; interrupt number
- RMI_DS dw ? ; real mode DS
- RMI_ES dw ? ; real mode ES
- RMI_FS dw ? ; real mode FS
- RMI_GS dw ? ; real mode GS
- RMI_EAX dd ? ; EAX value
- RMI_EDX dd ? ; EDX value
- RMINT ends
-
- print_ch proc far
- ;
- ; Stack frame
- ;
- #COND_MSK equ (word ptr 12[ebp])
- #RMI equ (dword ptr [ebp - (size RMINT)])
-
- push ebp ; Set up stack frame
- mov ebp,esp ;
- sub esp,size RMINT ; allocate local vars
-
- test #COND_MSK,2 ; branch if left button
- jz short #right ; not pressed
-
- ;
- ; Left mouse button was pressed. Issue a real mode
- ; interrupt to set up the mouse handler routine to
- ; be tog_hndlr. The interrupt takes a function number
- ; in AX, an interrupt mask in CX, and the real mode
- ; addr of the handler in ES:DX. We don't bother to
- ; initialize the values in the struct for DS,FS, or
- ; GS, and just let them get loaded with garbage.
- ;
- mov ax,rm_seg ; ES:DX = addr of
- mov #RMI.RMI_ES,ax ; handler
- lea eax,tog_hndlr ;
- mov #RMI.RMI_EDX,eax ;
- mov cx,0Ah ; interrupt mask
- mov #RMI.RMI_EAX,12 ; EAX = function #
- mov #RMI.RMI_INUM,33h ; interrupt number
- push ds ; issue real mode
- mov ax,ss ; interrupt
- mov ds,ax ;
- lea edx,#RMI ;
- mov ax,2511h ;
- int 21h ;
- pop ds ;
- mov ax,FALSE ; return success
- jmp short #exit ;
-
- #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
- mov al,outp_ch ; character
- mov word ptr es:[bx],ax ; output it
-
- mov ax,FALSE ; return success
-
- #exit:
- add esp,size RMINT ; pop local vars
- pop ebp ; restore regs &
- ret ; exit
- print_ch endp
-
- pmcode ends
-
- page
- ;
- ; Real mode code
- ;
- rmcode segment
-
- ;***************************************************
- ; rm_setup(hndlr_seg)
- ; WORD hndlr_seg;
- ;
- ; This routine is called from protected
- ; mode. It calls the mouse driver to set up
- ; the specified address as the mouse interrupt
- ; handler when either mouse button is pressed.
- ; This call is made in real mode because
- ; it passes a value in ES.
- ;
- ; Inputs:
- ; 1st stack param = segment addr of handler
- ; DX = offset of handler
- ;
- ; Outputs:
- ; AX = TRUE if error, FALSE if success
- ; ES,BX-DX = destroyed
- ; all other registers unchanged
- ;***************************************************
- assume cs:rmcode,ds:dgroup
- rm_setup proc far
- ;
- ; Stack frame
- ;
- #HNDLR_SEG equ (word ptr 6[bp])
-
- push bp ; set up stack frame
- mov bp,sp ;
-
- mov ax,#HNDLR_SEG ; ES:DX = addr of
- mov es,ax ; handler
- mov cx,0Ah ; interrupt mask
- mov ax,12 ; set up handler
- int 33h ;
-
- mov ax,FALSE ; return success
-
- pop bp ; restore regs &
- ret ; exit
- rm_setup endp
-
- ;***************************************************
- ; ch_hndlr - This routine is invoked by the mouse
- ; driver when either mouse button is pressed,
- ; and when we are in character output mode.
- ; This handler just calls a protected mode
- ; routine to process the interrupt. Since
- ; this is a hardware interrupt handler, it is
- ; obligated to preserve all 32-bit registers.
- ;
- ; This routine demonstrates passing values
- ; both on the stack and in registers to a
- ; protected mode subroutine.
- ;***************************************************
- assume nothing,cs:rmcode
- ch_hndlr proc far
- push ds ; save regs
- push eax ;
-
- ;
- ; To call a prot mode routine, all we do is push
- ; any params to the routine onto the stack, then
- ; push the FAR addr of the desired prot mode
- ; routine, then call the 386|DOS-Extender routine
- ; to switch modes and transfer control to the prot
- ; mode routine.
- ;
- push cs ; set up data segment
- pop ds ;
- assume ds:dgroup ;
-
- push ax ; param = cond. mask
- push dword ptr 0 ; no seg reg param blk
- push code_sel ; prot mode addr of
- lea eax,print_ch ; display
- push eax ; routine
- call call_prot ; call it
- add sp,12 ; pop arguments
- cmp ax,TRUE ; branch if error
- je short #err ;
-
- #exit:
- pop eax ; restore regs &
- pop ds ; exit
- ret ;
-
- #err:
- lea dx,chndlrmsg ; output error msg
- mov ah,9 ;
- int 21h ;
- jmp #exit
- ch_hndlr endp
-
- ;***************************************************
- ; tog_hndlr - This routine is invoked by the mouse
- ; driver when either mouse button is pressed,
- ; and we are in toggle mode (the left mouse
- ; button was the last one pressed).
- ; If the left button has been pressed, we
- ; set the done flag. If the right button has
- ; been pressed, we toggle the character that
- ; gets output between '*' and ' ', and then
- ; restore ch_hndlr as the mouse interrupt
- ; handler. Since this is a hardware
- ; interrupt handler, it is obligated to
- ; preserve all 32-bit registers.
- ;***************************************************
- assume nothing,cs:rmcode
- tog_hndlr proc far
- push ds ; save regs
- push es ;
-
- push cs ; set up data segment
- pop ds ;
- assume ds:dgroup ;
-
- test ax,2 ; branch if left button
- jz short #right ; not pressed
- mov donef,TRUE ; set done flag & exit
- jmp short #exit ;
-
- #right:
- mov al,' ' ; assume char = ' '
- cmp outp_ch,'*' ; toggle output char
- je short #space ;
- mov al,'*' ;
- #space: ;
- mov outp_ch,al ;
- mov ax,cs ; Restore ch_hndlr
- mov es,ax ; as mouse
- lea dx,ch_hndlr ; handler
- mov cx,0Ah ;
- mov ax,12 ;
- int 33h ;
-
- #exit:
- pop es ; restore regs &
- pop ds ; exit
- ret ;
- tog_hndlr endp
-
- rmcode ends
-
- end main ; program entry point
-