⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 keyint3.asm

📁 dos 1.0 其中包含quick basic源代码、内存管理himem emm386 发展历史
💻 ASM
📖 第 1 页 / 共 2 页
字号:
endif	; KANJI

	cmp	[di].rstCur, RST_REPEATING
	jne	@F
	call	KillQueue
	jmp	short ExitInt09
@@:
	call	CopyQueue

ExitInt09:
	mov	[si].fPollKeyboardInkb,1    ;; poll to see if anything happened

	pop	es
	pop	ds
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	iret

;----------------------------------------------------------------------------
;
; Input:     AL = the scan code (this is all-important)
;            DS:SI = ptr to keyboard data.
;            DS:DI = ptr to Data in data segment.
;            ES:BX = ptr to keyboard data in low RAM.
;
; Crunches:  AX,CX
;
; Preserves: BX,DX,SI,DI,DS,ES

cProc	ScanCodeStuff, <NEAR>
cBegin	nogen	; ScanCodeStuff

	cld

	mov	ah,al
	and	ah,080H			; make/break in ah

;*	* In order to detect repeating key sequences, the "fKeyIsUp" global
;*	*  variable is clear whenever a key is down (set with any keyup).
;*	* The "fKeyWasUp" variable is set on each key up event.
;*	* (i.e. fKeyIsUp is the state, fKeyWasUp is the transition).
;*	* NOTE : this is not fool-proof but handles most cases well, see the
;*	*  API document for example usage of these two flags


	cmp	al,0E0H
	je	dont_touch_fkey		;* skip for extended key
	mov	[si].fKeyIsUpInkb,ah
	cmp	al,0F0H
	je	dont_touch_fkey		;* skip for break
	or	[si].fKeyWasUpInkb,ah
dont_touch_fkey:

	and	al,07fH			; scan code in al

IFDEF	DEBUG

;  From windows: (minor alterations)
;
;  The SYS REQ key is used to simulate an NMI.
;  This is done by clearing up the stack and doing a far jump to the NMI
;  interrupt vector location.  If the NMI interrupt points into the ROM,
;  we don't jump, since the ROM treats NMIs as parity errors.
;
;  On the RT keyboard SYS REQ is generated by hitting Alt-PrintScreen,
;  badly this combination also has a special meaning in Windows (transfert
;  screen contents to the clipboard). To survive we'll keep this 2nd
;  behaviour and the NMI feature will be reached by hitting Crt-Alt-PrintScreen.
;

SYSREQ  EQU     054h    ; PC-AT SYS REQ key
cPrint  EQU     055h

OFF_nmi	EQU	2*4
SEG_nmi	EQU	2*4+2

	or	ah,ah		    ; sysreq on key down
	jnz	notsys
        cmp     al,SYSREQ           ; SYSREQ key?
        jne     notsys

	push	es		; check for RT keyboard
	xor	cx,cx
	mov	es,cx
	test	byte ptr es:[KbType],10H
	pop	es
	jz	not_ronco_int09
	test	[di].ssCur,SS_CONTROL	; else, is Ctrl down ?
	mov	al, cPrint		; new scan code (only used if no nmi)
	jz	notsys			; if not, skip (code = Print)
not_ronco_int09:

	push	es		; get NMI vector segment
	mov	es,cx			; cx is still 0
        mov     ax,word ptr es:[SEG_nmi]
	pop	es
        cmp     ax,0F000H         	; see if it points to ROM
        jne     do_nmi
        jmp     ExitScanCodeStuff

do_nmi:

ack_port equ    20h         ; 8259 acknowledge port
eoi      equ    20h         ; 8259 end of interrupt
kb_ctl   equ    61h

	in	al,kb_ctl
	mov	ah,al
	or	al,80h
	out	kb_ctl,al
	xchg	al,ah
	out	kb_ctl,al
	mov	al,eoi			; don't pass this on, eat it
	out	ack_port,al

	int	2
;;;	jmp	ExitInt09
	jmp	ExitScanCodeStuff

notsys:	; end of sysreq test

ENDIF	;DEBUG

;
; Software autorepeat control
;	This is all bypassed unless 0 < wRateKeyRepeat <= 10.
;	There are three states: IDLE, WAITING, and REPEATING.
;	IDLE is the default state, when no keydowns have been
;	received yet.  If a keydown is received in the IDLE state,
;	the state is changed to WAITING, indicating that we are
;	waiting for another copy of the same key code (when the
;	keyboard indicates enough time has passed to begin actually
;	repeating).  When we receive the same key again in the
;	WAITING state, we move on to the REPEATING state and init
;	the repeat delay counter.  This cues the timer-interrupt
;	to dump in wRateKeyRepeat extra keys every 1/18 second.  If
;	a keydown of a different key is received in the WAITING or
;	REPEATING states, the WAITING state is restarted as above.
;	A keyup with the same scan code as the last keydown changes 
;	the state to IDLE.
;
	or	ah,ah			;* key break => maybe stop repeat
	jns	a_key_down
	cmp	al, [di].scRepeatWhich	;* ignore if not same key
	jne	repeat_done
	mov	[di].rstCur, RST_IDLE
	jmp	short kill_pending	;* kill pending repeats
a_key_down:
	cmp	[di].rstCur, RST_IDLE
	jne	not_first
	cmp	[si].wRateKeyRepeatInkb, 0  ;* skip if rate = default
	jle	repeat_done
start_waiting:
	mov	[di].rstCur, RST_WAITING  ;* waiting to start repeating
	mov	[di].scRepeatWhich, al
kill_pending:
	mov	[di].ckeyRepeat, 0	;* kill pending repeats
	jmp	short repeat_done
not_first:
	cmp	al, [di].scRepeatWhich
	jne	start_waiting
	cmp	[di].rstCur, RST_REPEATING
	je	repeat_done		;* and later ignore char
	mov	cx, [si].wRateKeyRepeatInkb  ;* get CW key repeat rate
	mov	[di].ckeyRepeat, cx
	mov	[di].fRepeatToggle, 0
	mov	[di].rstCur, RST_REPEATING
repeat_done:

;
; Look for space bar down/up, and maintain its state in ssCur
;
	cmp	al,SC_SPACE
	jne	not_space_key
;*	* update the extra shift state for the spacebar
	or	[di].ssCur,SS_SPACE		;* assume down
	test	ah,080H
	jz	shift_is_down
	and	[di].ssCur,NOT SS_SPACE		;* shift is off
shift_is_down:

not_space_key:
	or	ah, ah
	jnz	ExitScanCodeStuff

;*	* if ALT or Extended key then pass on
	cmp	al,SC_ALT
	je	ExitScanCodeStuff
	cmp	al,SC_EXTENDED
	je	ExitScanCodeStuff
;*	* otherwise we have a non-alt key
	or	[si].fNonAltKeyHitInkb,ah	;* set if key down

;
; The BIOS eats a lot of CTRL- and ALT- keys that we need to preserve.
; So if CTRL or ALT is held down, we might have to fool the BIOS into
; passing the key, which we do by turning off the CTRL or ALT shift 
; state, chaining to the BIOS, then resetting the shift state.  So now
; we look up our key in a bit-packed table to see if it requires diddling...
; We also do some weird diddling (temporarily turn on the Shift key)
; for numpad 5, cause the BIOS eats that, too.
;
	mov	ah, es:[bx]		; current shift state

IFDEF	STD_NUMPAD
	cmp	al, 76			; numpad 5 scan code
	jne	CheckAltCtrl
	test	ah, SS_ALT
	jnz	ExitScanCodeStuff	; don't diddle Alt-numpad
	test	ah, SS_NUMLOCK
	jnz	NumLock5
	or	ah, SS_LSHIFT		; NumLock off ==> turn on shift
	jmp	short Num5Diddle
NumLock5:
	test	ah, SS_SHIFT
	jz	Num5Diddle		; NL, no shift ==> don't diddle
	and	ah, not SS_NUMLOCK	; NL, shift ==> turn off NL
Num5Diddle:
	and	ah, not SS_CONTROL	; turn off Ctrl
	mov	es:[bx], ah
	jmp	short SetDiddle
ENDIF	; STD_NUMPAD

IFNDEF TANDY_1000	; Tandy 1000 doesn't require any diddling
CheckAltCtrl:	
	and	ah, SS_ALT or SS_CONTROL
	jz	ExitScanCodeStuff
	push	bx
	mov	bl, al
	mov	cl, al
	and	cl, 7
	mov	al, 1
	shl	al, cl			; bit mask
	mov	cl, 3
	shr	bl, cl			; byte address
	xor	bh, bh
	cmp	ah, SS_ALT
	je	CheckAlt
	cmp	ah, SS_CONTROL
	je	CheckCtrl
	add	bx, offset mpscffAltCtrlDiddle
	jmp	short CheckDiddle
CheckCtrl:
	add	bx, offset mpscffCtrlDiddle
	jmp	short CheckDiddle
CheckAlt:
	add	bx, offset mpscffAltDiddle
CheckDiddle:
	test	cs:[bx], al
	pop	bx
	jz	ExitScanCodeStuff
DoDiddle:
	mov	byte ptr es:[bx], 0
SetDiddle:
	mov	[di].fShiftStateDiddled,ah
ENDIF ;TANDY_1000

ExitScanCodeStuff:

	ret

cEnd	nogen	; ScanCodeStuff


;----------------------------------------------------------------------------

;*	* * * QUEUE CONTROL * * *

;---------------------------------------
;
; LockInsertQueue
; LockRemoveQueue
;
;   Access the driver's keyboard queue for Insertion and Removal
;
;   entry : DS:DI => driver data
;
;   exit  : DS:SI => queue structure
;           ZR => queue available and now locked
;           NZ => queue already locked, unavailable

cProc	LockInsertQueue, <PUBLIC, NEAR, ATOMIC>
cBegin	LockInsertQueue

	AssertEQ di,OFF_lpwDataKbd

	lea	si,[di].queueKb
	inc	[si].semInsertQueue
	jz	@F
	cCall	ReleaseInsertQueue
@@:

cEnd	LockInsertQueue

cProc	LockRemoveQueue, <PUBLIC, NEAR, ATOMIC>
cBegin	LockRemoveQueue

	AssertEQ di,OFF_lpwDataKbd

	lea	si,[di].queueKb
	inc	[si].semRemoveQueue
	jz	@F
	cCall	ReleaseRemoveQueue
@@:

cEnd	LockRemoveQueue

;---------------------------------------
;
; ReleaseInsertQueue
; ReleaseRemoveQueue
;
;   Release the queue (make it available to other threads)
;
;   entry : DS:SI => queue structure
;
;   exit  : all registers (including flags) preserved

cProc	ReleaseInsertQueue, <PUBLIC, NEAR, ATOMIC>
cBegin	ReleaseInsertQueue

	pushf
	dec	[si].semInsertQueue
	popf

cEnd	ReleaseInsertQueue

cProc	ReleaseRemoveQueue, <PUBLIC, NEAR, ATOMIC>
cBegin	ReleaseRemoveQueue

	pushf
	dec	[si].semRemoveQueue
	popf

cEnd	ReleaseQueue

;;********** IncQueuePtr **********
;*	entry : BX = current queue pointer
;*		SI => QUEUE structure
;*	* bump pointer
;*	exit : BX = new pointer

cProc IncQueuePtr, <NEAR, ATOMIC>
cBegin	IncQueuePtr

	add	bx,4				;* bump long
	cmp	bx,[si].pEndQueue
	jne	inc_ok
	mov	bx,[si].pStartQueue		;* wrap around
inc_ok:
cEnd	IncQueuePtr



;********** CopyQueue **********
;*	entry:	DS:DI => driver data
;*	* copy item from bios Q to our buffer QUEUE (ignore if no room)
;*	exit : n/a

cProc	CopyQueue,<NEAR>, <si>
cBegin	CopyQueue

	AssertEQ di,OFF_lpwDataKbd

	cCall	LockInsertQueue
	jnz	cq_exit
	mov	bx,[SI].pTailQueue		;* add after tail

CopyNextKey:
	push	bx
	cCall	IncQueuePtr
	cmp	bx,[SI].pHeadQueue		;* is there room ?
	pop	bx
	jz	DoneQueueCopy			;* no room

	cCall	FnzGetBiosKey
	jnz	EnqueueKey

;*	* test to see if shift states have changed
	mov	ax,[di].ssCur
	mov	dx,ax
	xchg	ax,[di].ssLastInt		;* set new, get old
	cmp	ax,dx
	je	DoneQueueCopy			;* shift states the same
	xor	ax,ax				;* no char, ss changed though

EnqueueKey:
	;* DX:AX = key
	mov	[bx+0],ax
	mov	[bx+2],dx
	cCall	IncQueuePtr
	jmp	short CopyNextKey

DoneQueueCopy:
	mov	[SI].pTailQueue,bx
	cCall	ReleaseInsertQueue

cq_exit:

cEnd	CopyQueue

;----------------------------------------------------------------------------
;
;  KillQueue
;
;	entry: DS:DI => driver data
;	* empty the bios key queue
;	exit: n/a

cProc	KillQueue,<NEAR>, <si>
cBegin	KillQueue

	AssertEQ di, OFF_lpwDataKbd
KillKeys:
	cCall	FnzGetBiosKey
	jnz	KillKeys

cEnd	KillQueue

;----------------------------------------------------------------------------
;
; Int08Handler()
;
; INT 08 is invoked 18.2 times per second by the timer chip.  We hook
; this to get a regular interval for software auto-repeat.

Int08Handler:
	push	ds
	push	di
	push	ax
	mov	ax, SEG_lpwDataKbd
	mov	ds, ax
	mov	di, OFF_lpwDataKbd
	pushf					;* first chain to Dos
	call	[di].pfnOldInt08
	cmp	[di].rstCur, RST_REPEATING	;* skip if not repeating
	jne	int08_done
	push	si
	mov	si, [di].pinkbCur
	mov	ax, [si].wRateKeyRepeatInkb
	add	[di].ckeyRepeat, ax
	mov	[si].fPollKeyboardInkb, 1	;* tell CW about it
	pop	si
int08_done:
	pop	ax
	pop	di
	pop	ds
	iret

;----------------------------------------------------------------------------
;
; Int16Handler()
;
; BIOS int 16h (keyboard services) emulator.
; Traps AH = 0, 1, 2, 10h, 11h, or 12h; the rest are passed
;  to the default handler.
;
Int16Handler proc far
	push	ax	;(cs)	; sp +	16	;* this is for chaining
	push	ax	;(ip)	; 	14	;* (see below)
	push	ds		; 	12
	push	di		; 	10
	push	si		; 	8
	push	dx		; 	6
	push	cx		; 	4
	push	bx		; 	2
	push	ax		; 	0

	mov	di, SEG_lpwDataKbd
	mov	ds, di
	mov	di, OFF_lpwDataKbd
	cmp	[di].fUnhook16, 0
	jnz	int16_chain
	or	ah, ah
	je	int16_0
	cmp	ah, 1
	je	int16_1
	cmp	ah, 2
	je	int16_2
	cmp	ah, 10h
	je	int16_0
	cmp	ah, 11h
	je	int16_1
	cmp	ah, 12h
	je	int16_2

; Since we need to chain to the previous int 16 handler, but can't
; afford to mess up any registers to do it (e.g., via an indirect
; call or jmp using DS and DI) we poke the handler's address into a 
; convenient blank space in the stack frame, pop all registers, 
; and "return" to the old int 16 handler.  (I.e., pop+jmp an address
; on the stack.)
;
int16_chain:
	mov	bx, sp
	mov	ax, word ptr [di].pfnOldInt16	;* segment
	mov	ss:[bx+14], ax
	mov	ax, word ptr [di].pfnOldInt16+2	;* offset
	mov	ss:[bx+16], ax
	pop	ax
	pop	bx
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	ds
	ret	;far				;* to [pfnOldInt16]

int16_0:
	cCall	InKey				;* copy queue, get key
	or	ax, ax
	jnz	int16_0done			;* go return the key
	or	dx, dx				;* make sure we've got 
	jnz	int16_0				;*  all the SS events too
	cmp	[di].ckeyRepeat, 0		;* check for repeating keys
	jz	int16_0				;* keep polling if none
	dec	[di].ckeyRepeat
	mov	ax, word ptr [di].chPrev	;* use the previous key
int16_0done:
	mov	word ptr [di].chPrev, ax	;* save for repeats
	jmp	short int16_done

int16_1:
	cmp	[di].ckeyRepeat, 0		;* check artificial repeats
	jnz	int16_1ret
	lea	si, [di].queueKb
	mov	bx, [si].pHeadQueue
int16_1loop:
	cmp	bx, [si].pTailQueue
	jz	int16_1nomo			;* no mo', go check BIOS
	cmp	word ptr [bx], 0
	jnz	int16_1ret			;* true if non-nil key
	cCall	IncQueuePtr			;* skip SS-only event
	jmp	short int16_1loop		;* otherwise keep looking
int16_1nomo:
	pop	ax				;* get int 16 function #
	push	ax				;* don't mess up stack frame
	pushf
	call	[di].pfnOldInt16		;* check the BIOS buffer too
int16_1ret:
	lahf					;* ha ha ha
	and	ah, 40h				;* ZF mask in cpu flags
	mov	bx, sp
	add	bx, 22				;* offset to caller's flags
	and	byte ptr ss:[bx], not 40h
	or	byte ptr ss:[bx], ah		;* poke in ZF
	jmp	short int16_done

int16_2:
	lds	si, lpsslBios
	lodsb

int16_done:
	add	sp, 2				;* incoming ax
	pop	bx
	pop	cx
	pop	dx
	pop	si
	pop	di
	pop	ds
	add	sp, 4				;* empty space
	iret
Int16Handler endp

;************************************************************************

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -