home *** CD-ROM | disk | FTP | other *** search
- page 60,132
- ;+
- ;********************************************************************
- ; (c) Copyright 1989-1992 Magic Box Designs, David J. Crone
- ; all rights reserved
- ;
- ; Filename: kbstuff.asm
- ;
- ; Function: Assembly language routines for accessing PC keyboard
- ;
- ; Author: David J. Crone
- ; Version: 2.0
- ; Last Edit: 04-Jun-91 [014] DJC fix to newfudge
- ; 10-Aug-89 DJC Creation
- ;
- ;********************************************************************
- ;-
-
- INCLUDE kb.inc
-
- NEWFUDGE EQU 1 ;0=use code tromping version
- ;1=use controller overwrite version
-
-
- ; .CODE
- _TEXT SEGMENT WORD PUBLIC 'CODE'
- ASSUME cs:_TEXT, ds:_TEXT, es:_TEXT
-
-
- ; --- data ---
- if NEWFUDGE EQ 0
- _fudge_count:WORD
- _fudge_factor:WORD
- _fudge_addr:WORD
- _fudge_isr_addr:DWORD
- endif
-
-
-
- ;+
- ;**********************************************************
- ; name: set_vector(interrupt_number, far *routine);
- ; return type: void
- ; entry type: int interrupt_number;
- ; far *routine;
- ; function:
- ; set an interrupt vector to use a specific routine
- ;
- ;**********************************************************
- ;-
-
- int_num = 4 ;interrupt number
- r_off = 6 ;routine offset
- r_seg = 8 ;routine segment
-
- PUBLIC _set_vector
- _set_vector PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- push es
- push ds
- push si
-
- mov ax,[bp].r_seg ;get segment of routine
- mov ds,ax
- mov dx,[bp].r_off ;get offset of routine
-
- mov ax,WORD PTR [bp].int_num ;get interrupt number
- mov ah,25h ;"set int vector" DOS function
- int 21h ;passed as DS:DX
-
- pop si
- pop ds
- pop es
- pop bp
- ret
-
- _set_vector ENDP
-
-
- ;+
- ;**********************************************************
- ; name: get_vector(interrupt_number);
- ; return type: void far *
- ; entry type: int interrupt_number;
- ;
- ; function:
- ; returns the contents of an interrupt vector
- ; exit:
- ; dx segment
- ; ax offset
- ;**********************************************************
- ;-
-
- int_num = 4 ;interrupt number
-
- PUBLIC _get_vector
- _get_vector PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- push es
- push ds
- push si
-
- mov ax,WORD PTR [bp].int_num ;get interrupt number
- mov ah,35h ;"get int vector" DOS function
- int 21h ;returns ES:BX
-
- mov dx,es ;we want to return in DX:AX
- mov ax,bx
-
- pop si
- pop ds
- pop es
- pop bp
- ret
-
- _get_vector ENDP
-
-
- ;+
- ;**********************************************************
- ; name: read_vector(interrupt_number);
- ; return type: void far *
- ; entry type: int interrupt_number;
- ;
- ; function: This routine manually reads the interrupt
- ; vector address. It should not normally be used, but
- ; is the only way to get a vector from within an
- ; interrupt service routine. DOS is not nice about
- ; executing function 35h from within an interrupt
- ; routine
- ; exit:
- ; dx segment
- ; ax offset
- ;**********************************************************
- ;-
-
- int_num = 4 ;interrupt number
-
- PUBLIC _read_vector
- _read_vector PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- push ds
- push si
-
- mov si,WORD PTR [bp].int_num ;get interrupt number
- shl si,2 ; * 4 for offset
- mov ax,0
- mov ds,ax ;segment 0
-
- mov ax,[si] ;read offset of vector
- mov dx,[si+2] ;read segment of vector
-
- pop si
- pop ds
- pop bp
- ret
-
- _read_vector ENDP
-
-
-
- ;+
- ;********************************************************
- ; name: signal_eoi()
- ; return type: void
- ; entry type: void
- ;
- ; function: Signal end of interrupt to 8259A
- ;********************************************************/
- ;-
-
- PUBLIC _signal_eoi
- _signal_eoi PROC NEAR
- mov al,EOI ;signal end-of-interrupt
- out EOI_MASTER,al ;first signal master
- cmp _delta_int,070h ;is IRQ on slave ?
- jb sig_10 ;no, skip
-
- out EOI_SLAVE,al ;then signal slave 8259
- sig_10:
- ret
- _signal_eoi ENDP
-
-
-
- ;+
- ;**********************************************************
- ;* name: get_bios_buffer()
- ;* return type: int
- ;* entry type: void
- ;*
- ;* function: returns next char from BIOS buffer if one available
- ;**********************************************************
- ;-
-
- PUBLIC _get_bios_buffer
- _get_bios_buffer PROC NEAR
-
- push bx
- push es
-
- mov ax,BIOS_SEG ;set up BIOS data seg
- mov es,ax
- mov bx,es:bios_head ;get head ptr
- cmp bx,es:bios_tail ;if anything in buffer
- je get_bios_20
-
- mov ax,es:[bx] ;get char
- call inc_bios ;then inc ptr
- mov es:bios_head,bx ;store it
- get_bios_20:
- pop es
- pop bx
- ret
-
- _get_bios_buffer ENDP
-
-
-
-
- ;+
- ;**********************************************************
- ;* name: put_bios_buffer(scancode)
- ;* return type: int
- ;* entry type: unsigned scancode
- ;*
- ;* function: generate interrupt 0x15
- ;**********************************************************
- ;-
-
- scancode = 4
-
- PUBLIC _put_bios_buffer
- _put_bios_buffer PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- push es
- push bx
- push dx
-
- mov ax,BIOS_SEG ;set up BIOS data seg
- mov es,ax
- mov bx,es:bios_tail ;get tail ptr
- mov si,bx
- call inc_bios ;increment it
- cmp bx,es:bios_head ;wrapped around ?
- je put_bios_20 ;yes, skip the put
-
- mov ax,[bp].scancode ;get scan code
- mov es:[si],ax ;put it in BIOS buffer
- mov es:bios_tail,bx ;update tail pointer
-
- put_bios_20:
- pop dx
- pop bx
- pop es
- pop bp
- ret
-
- _put_bios_buffer ENDP
-
-
- ;+
- ;**********************************************************
- ;* name: clear_bios_buffer()
- ;* return type: void
- ;* entry type: void
- ;*
- ;* function: reset BIOS head & tail ptrs
- ;**********************************************************
- ;-
-
- PUBLIC _clear_bios_buffer
- _clear_bios_buffer PROC NEAR
-
- push es
-
- mov ax,BIOS_SEG ;set up BIOS data seg
- mov es,ax
- mov ax,es:bios_start ;reset buffer ptrs
- mov es:bios_head,ax
- mov es:bios_tail,ax
-
- pop es
- ret
-
- _clear_bios_buffer ENDP
-
-
- ;*****
- ; Increment bios buffer pointer -- strictly local routine
- ;*****
- inc_bios:
- inc bx
- inc bx
- cmp bx,es:bios_end
- jb inc_bios_20
-
- mov bx,es:bios_start
- inc_bios_20:
- ret
-
-
- ;+
- ;**********************************************************
- ;* name: update_bios_flags(bf, bf1, bf2)
- ;* return type: void
- ;* entry type: int bf flag
- ;* int bf1 flag_1
- ;* int bf2 flag_2
- ;*
- ;* function: copy flags to BIOS data area
- ;**********************************************************
- ;-
-
- PUBLIC _update_bios_flags
- _update_bios_flags PROC NEAR
-
- bf = 4
- bf1 = 6
- bf2 = 8
-
- push bp
- mov bp,sp ;establish stack frame
- push es
-
- mov ax,BIOS_SEG ;set up BIOS data seg
- mov es,ax
- mov ax,[bp].bf ;copy LSByte of flags
- mov es:bios_flag,al
- mov ax,[bp].bf1
- mov es:bios_flag_1,al
- mov ax,[bp].bf2
- mov es:bios_flag_2,al
-
- pop es
- pop bp
- ret
-
- _update_bios_flags ENDP
-
-
- IF NEWFUDGE
- ;+
- ;***************************************************************
- ; Name: newfudge(scan_code)
- ; Entry: unsigned char scan_code last scan code from delta kb
- ; Exit: void
- ;
- ; Function: Send sequence to PC keyboard controller chip (8042) that
- ; will cause it to place 'scan_code' in its data
- ; register at 060h. This will allow us to vector to
- ; the current Int 9 handler and let it handle the scan
- ; code as it sees fit. If this works, we can eliminate
- ; the need to walk through the current Int 9 handler
- ; and tromp on its code.
- ; This will be accomplished by writing the scan code to the
- ; PC keyboard controller's command byte. Then we will issue
- ; the command to read the controller's command byte. This
- ; will cause the byte we just wrote (our scan code) to be
- ; presented as data on the keyboard controller. Any subsequent
- ; reads of the keyboard controller's data port will result
- ; in reading the scan code we put there.
- ; ***014 A problem was encountered when the data was EVEN
- ; (i.e. bit 0 = 0) This caused OutputBufferFull interrupts to
- ; be disabled on the 8042, and thus no IRQ was registered on the
- ; 8259 int. controller. Special handling was added within
- ; "***014" markers to correct for this by doing a software
- ; Int 9h instruction for EVEN data only.
- ;***************************************************************
- ;-
-
- scan = 4 ;index to scan code
-
- PUBLIC _newfudge
- _newfudge PROC NEAR ; ***012 Begin
- push bp
- mov bp,sp ;establish stack frame
- pushf
- cli ;disable interrupts for this
- ;Don't want Int 9h handler to wake
- ; up until we get data set up first
-
- in al,pc_k_data ;clear any random data
-
- ;-- send Write Command Byte command to controller port (64h)
- push KC_WRITE
- call _send_pckb_controller
- pop ax
-
- ;-- send SCANCODE to data port (60h)
- mov ax,[bp].scan ;get scan code
- push ax
- call _send_pckb_data
- pop ax
-
- ;-- send Read Command Byte command to controller port
- push KC_READ
- call _send_pckb_controller
- pop ax
-
- ;------------------------
- ; keyboard controller now has our scancode in data port
- ;------------------------
-
- ;-- send Write Command Byte command to controller port (64h)
- push KC_WRITE
- call _send_pckb_controller
- pop ax
-
- ;-- send standard enable to data port (60h)
- push 049h ;enable kb interrupts
- call _send_pckb_data
- pop ax
-
- ; The keyboard controller should generate an interrupt
- ; as soon as the command byte is available.
- ; (ODD scan codes only)
-
- call _read_pckb_data ;clear pending interrupt ****
-
- popf ;restore interrupt status
- ; ***014 Begin
- ; test WORD PTR [bp].scan,01h ;is scan code EVEN ?
- ; jne newf_50 ; no, exit
-
- int 9h ; yes, must manually cause int 9h
- newf_50: ; ***014 End
- pop bp
- ret
- _newfudge ENDP ; ***012 End
-
-
- ;+
- ;**********************************************************
- ; name: send_pckb_controller(cmnd)
- ; return type: void
- ; entry type: unsigned char cmnd
- ;
- ; function: send a command code to the PC's 8042 keyboard
- ; controller
- ;**********************************************************
- ;-
-
- PUBLIC _send_pckb_controller
- cmnd = 4
-
- _send_pckb_controller PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- pushf
- cli ;;disable interrupts
-
- call _wait_pckb_send ;;wait till O.K. to send
- mov al,BYTE PTR [bp].cmnd
-
- mov dx,pc_k_control
- out dx,al ;;output command
-
- popf ;restore interrupt status
- pop bp
- ret
- _send_pckb_controller ENDP
-
-
- ;+
- ;********************************************************
- ; name: send_pckb_data(cmnd)
- ; return type: void
- ; entry type: unsigned char cmnd
- ;
- ; function: send the keyboard command code through the PC's 8042
- ; to the PC's keyboard
- ;********************************************************/
- ;-
- PUBLIC _send_pckb_data
- cmnd = 4
-
- _send_pckb_data PROC NEAR
- push bp
- mov bp,sp ;establish stack frame
- pushf
- cli ;;disable interrupts
-
- spkbd_10:
- call _wait_pckb_send ;;wait till O.K. to send
-
- mov al,BYTE PTR [bp].cmnd
- mov dx,pc_k_data
- out dx,al ;;output the command
-
- popf
- pop bp
- ret
-
- _send_pckb_data ENDP
-
-
- ;+
- ;**********************************************************
- ; name: wait_pckb_send
- ; return type: int 0 = ready for data
- ; entry type: void
- ;
- ; funtion: Wait until PC's 8042 ready for new data
- ;**********************************************************
- ;-
-
- PUBLIC _wait_pckb_send
- _wait_pckb_send PROC NEAR
- sub cx,cx ;max. wait time
- wpkbs_10:
- mov dx,pc_k_status
- in al,dx ;wait for input buffer empty
- and ax,KS_IBF ;ibf=0 means buffer empty
- loopnz wpkbs_10
-
- ret
-
- _wait_pckb_send ENDP
-
-
- ;+
- ;**********************************************************
- ; name: wait_pckb_receive
- ; return type: int non-zero = data available
- ; entry type: void
- ;
- ; funtion: Wait until new data ready from PC's 8042
- ;**********************************************************
- ;-
- PUBLIC _wait_pckb_receive
- _wait_pckb_receive PROC NEAR
- sub cx,cx ;max. wait time
- wpkbr_10:
- mov dx,pc_k_status
- in al,dx ;wait for input buffer empty
- and ax,KS_OBF ;obf=1 means buffer full
- loopz wpkbr_10
-
- ret
-
- _wait_pckb_receive ENDP
-
-
- ;+
- ;********************************************************
- ; name: read_pckb_data()
- ; return type: unsigned char
- ; entry type: void
- ;
- ; function: return the PC's keyboard scan code
- ;********************************************************/
- ;-
- PUBLIC _read_pckb_data
- _read_pckb_data PROC NEAR
-
- mov dx,pc_k_data
- in al,dx ;read data port of 8042
-
- ret
-
- _read_pckb_data ENDP
-
-
-
- ;+
- ;********************************************************
- ; name: read_pckb_status()
- ; return type: unsigned char
- ; entry type: void
- ;
- ; function: return the status register of the PC's 8042 keyboard
- ; controller
- ;********************************************************/
- ;-
- PUBLIC _read_pckb_status
- _read_pckb_status PROC NEAR
-
- mov dx,pc_k_status
- in al,dx ;read 8042 status port
-
- ret
-
- _read_pckb_status ENDP
-
-
- ELSE ;using old fudge method
-
- ;+
- ;***************************************************************
- ; Name: fudge(scan_code)
- ; Entry: unsigned char scan_code last scan code from delta kb
- ; Exit: int 0 = no fudging, don't call int9
- ; 1 = current handler is fudged
- ; int9 needs to be called
- ;
- ; Function: Search through the currently installed Int 9 handler
- ; (standard keyboard IRQ) and find the following instruction:
- ; IN AL,60h
- ; Replace all occurrences of this statement within the first
- ; 100h bytes with an
- ; INT 60h
- ; so that our special handler can substitute the scan code
- ; we want to use to fake out the Int 9 handler.
- ;***************************************************************
- ;-
-
- scan = 4 ;index to scan code
-
- PUBLIC _fudge
- _fudge PROC NEAR
-
- push bp
- mov bp,sp ;establish stack frame
- push es
- push di
- cld ;make sure forward direction set
-
- mov al,[bp].scan
- mov BYTE PTR CS:scode,al ;save scan code
-
- mov ax,9
- push ax
- call _read_vector ;get int9 handler addr
- add sp,2 ; DX:AX = handler
- mov es,dx
- mov di,ax ;now ES:DI = handler
-
- ;* If int9 handler is our dummy handler, then no need
- ;* to fudge.
- ;;;; cmp dx,SEG _dummy_pckb_isr ;is int9 = our dummy handler ?
- cmp dx,_codeseg
- jne fudge_5
- cmp di,OFFSET _dummy_pckb_isr
- jne fudge_5 ;no
-
- mov _fudge_factor,0
- mov WORD PTR _fudge_isr_addr,di ;yes, remove old app's
- mov WORD PTR _fudge_isr_addr+2,dx ; handler
- jmp fudge_50 ;go exit
-
- ;* If int9 handler is same as last time, then no need
- ;* to fudge.
-
- fudge_5:
- mov _fudge_factor,1 ;set flag so int9 gets called
- cmp dx,WORD PTR _fudge_isr_addr+2 ;is int9 = same as last time?
- jne fudge_10
- cmp di,WORD PTR _fudge_isr_addr
- je fudge_50 ;yes, skip fudge
- ;no, fudge it
- fudge_10:
- mov WORD PTR _fudge_isr_addr,di ;save fudge addr so we only
- mov WORD PTR _fudge_isr_addr+2,dx ; do it once
- call _clear_bios_buffer
-
- mov cx,100h ;limit our search
- fudge_15:
- mov al,BYTE PTR cs:in60 ;IN AL instruction
-
- REPNZ scasb ;find 'IN AL'
- jnz fudge_50 ;no good, just exit
-
- mov al,BYTE PTR cs:in60+1 ;rest of IN AL,60 instruction
- scasb
- jnz fudge_30 ;this wasn't it
-
- fudge_20: ;found it, now replace it
- dec di ;backup pointer to instruction
- dec di
-
- mov _fudge_addr,di ;DIAGNOSTICS
- inc _fudge_count ;DIAGNOSTICS
-
- mov al,BYTE PTR cs:int60 ;Int 60h instruction
- stosb ;substitute it
- mov al,BYTE PTR cs:int60+1
- stosb
- fudge_30:
- or cx,cx ;hit our limit?
- jnz fudge_15 ;not yet, try again
-
- fudge_50: ;exit
- mov ax,_fudge_factor
- pop di
- pop es
- pop bp
- ret
-
- ;These don't execute, they are data
- in60: in al,60h ;the instruction we're looking for
- int60: int 60h ;the substitute instruction
- scode: db 0 ;scan code gets stored here
-
- _fudge ENDP
-
-
-
- ;+
- ;***************************************************************
- ; Name: int60_isr()
- ; Entry: void
- ; Exit: unsigned
- ;
- ; Function: Int 60h handler
- ; Returns the scan code stored in scode
- ; in AL to fake the Int 9 handler into thinking it came
- ; from the PC keyboard.
- ;***************************************************************
- ;-
- PUBLIC _int60_isr
- _int60_isr PROC FAR
-
- in al,pc_k_data ;check for keystroke on real keyboard
- test al,KS_OBF
- jz i6_20 ;no key, skip read
-
- ;Must have gotten here through hardware
- ; keyboard interrupt (IRQ1 = int9)
- in al,pc_k_data ;clear standard keyboard
-
- i6_20:
- mov al,BYTE PTR CS:scode ;get scan code
-
- iret ;return to int 9 handler
-
- _int60_isr ENDP
-
-
- ;+
- ;**********************************************************
- ; name: dummy_pckb_isr();
- ; return type: void
- ; entry type: void
- ;
- ; function: dummy interrupt service routine for the standard
- ; PC keyboard
- ;**********************************************************
- ;-
-
- PUBLIC _dummy_pckb_isr
- _dummy_pckb_isr PROC FAR
-
- push ax
- in al,pc_k_data ;clear key from keyboard
- mov al,EOI
- out EOI_MASTER,al ;clear irq
- pop ax
- iret ;return from interrupt
-
- _dummy_pckb_isr ENDP
-
-
- ;+
- ;**********************************************************
- ;* name: int9()
- ;* return type: int
- ;* entry type: void
- ;*
- ;* function: generate interrupt 0x09 - Standard Keyboard
- ;* hardware interupt service routine
- ;**********************************************************
- ;-
-
- PUBLIC _int9
- _int9 PROC NEAR
- int 9h ;do the interrupt
- ret
-
- _int9 ENDP
-
-
- ENDIF ;end conditional fudge routines
-
-
- _TEXT ENDS
- END
-
- ;--- end of kbstuff.asm ---
-