Source file: /~heha/basteln/PC/USB2LPT/usb2lpt.zip/src/sys/Vlpt.c

#include "usb2lpt.h"
#ifndef NTDDK
# define WORD USHORT
# define DWORD ULONG
# include <vmm.h>	// Install_IO_Handler
#endif

/***********************
 ** Globale Variablen **
 ***********************/

#define Usage_Len 32		// >=4, die ersten vier (auch) via DR0..DR3
USHORT Usage[Usage_Len];	// Überwachte Portadressen in Viererblöcken (Bits 1:0 = 0)
// Der Wert 0 bedeutet: freies Debugregister
// Der Wert 1 weist beim Treiber-Start besetzte Debugregister aus
// (diese werden jedoch bei UCB_ForceDebReg trotzdem verwendet)
// Ungerade Zahlen (Bit 0 gesetzt) für temporär ausgeschaltete Portadressen(?)
// Die Indizes 0..3 gelten für DR0..DR3, alle anderen: ausschließlich [RW]_PORT_U[CSL]
// Dieses Array kann mit "rep scasw" schnell nach dem passenden Index durchsucht werden.

ULONG UsageBits;		// 0-Bits für temporär ausgeschaltete Portadressen
#ifdef _X86_
static BOOLEAN HaveDebugExt;	// Wenn der Prozessor I/O-Breakpoints unterstützt (1 oder 0)
static BOOLEAN HaveSysEnter;	// Wenn der Prozessor den SysEnter-Befehl unterstützt (1 oder 0)
#else
# define HaveDebugExt 1
# define HaveSysEnter 1
#endif

static PDEVICE_EXTENSION X4DR[Usage_Len]; // zugehörige USB2LPT-Geräte

static struct {
 void* Int1;		/* bleibt Null ohne HaveDebugExt */
#ifdef NTDDK
 void* Int2E;		/* bleibt NULL ohne Pentium */
 void* SysEnter;
#endif
}OldInt;

static USHORT SaveDS;		// Für ISR zum Restaurieren
static USHORT SaveFS;		// FS ist numerisch gleich auf verschiedenen Prozessoren
#ifndef NTDDK
/*static*/ ULONG CurThreadPtr;	// Get_Cur_Thread_Handle-Ersatz
#endif
// Strukturzeiger für 2. Kernel-Stack (Win9x) C0013E78
// Unter W2K/WXP ist der aktuelle Thread durch FS: adressiert
// ...dann ist diese Variable = NULL.
void NewInt1(void);
#if defined(NTDDK) && defined(_X86_)
void NewInt2E(void);
void NewSysEnter(void);
#endif

static MYTIMER debset;	// Debugregister-Setz-Zeitgeber (periodisch)
// Der Timer dient zur Feststellung "geklauter" Debugregister.
ULONG DebRegStolen[3];	// globaler Zähler für "geklaute" Debugregister (nicht in X->ac)
// Index 0: Timer
// Index 1: Int2E
// Index 2: SysEnter / SysCall

/*****************************
 ** Debug-Register und Int1 **
 *****************************/
/* Zu tun: Auf Mehrprozessormaschinen müssen in _allen_ Prozessoren
 * die Debugregister gesetzt/gelesen/geprüft werden.
 * Ein NTDDK-Treiber kann dazu KeSetTargetProcessorDpc benutzen?
 * Auf Dual-Core-Maschinen (zumindest beim FSC Pentium4HT
 * "Mauersberger.mb.tu-chemnitz.de") haben die beiden Prozessoren
 * _verschiedene_ IDT-Einträge! Da stürzt der Debugregister-Trap zz. ab!
 * Problem: Für WDM-Treiber stehen die prozessorspezifischen Funktionen
 *          nicht zur Verfügung. Daher Aufspaltung Oktober 2009
 */

#ifdef NTDDK

#ifdef _X86_
// IRQL >= DISPATCH_LEVEL
// Der KDPC (1. Argument) wird nicht mehr benötigt.
static __declspec(naked) void EachProcessorDpc(KDPC*Dpc, PVOID Context, PVOID Arg1, PVOID Arg2) { _asm{
	mov	ecx,[esp+12]		// ECX = Arg1
	call	[esp+8]			// Context = Prozedurzeiger
	movzx	eax,_PCR KPCR.Number	// = KeGetCurrentProcessorNumber()
	mov	ecx,[esp+16]		// ECX = Zeiger auf Affinitätsmaske (Arg2)
	lock btr dword ptr[ecx],eax	// Für diesen Prozessor als erledigt markieren
	ret	16
}}
#else
void EachProcessorDpc(KDPC*,PVOID,PVOID,PVOID);
#endif

// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
// Ruft die angegebene, in Assembler geschriebene Callback-Routine
// für alle Prozessoren auf mit ECX = Argument.
// Blockiert (per Eigenbau-Spinlock) den aktuellen Prozessor (Thread)
// bis alle Prozessoren den Kode ausgeführt haben.
static void __fastcall EachProcessor(void(__fastcall*Callback)(int),int Arg) {
 ULONG i;
 volatile KAFFINITY a;
 a=KeQueryActiveProcessors();
 if (a==1) Callback(Arg);			// Einzelprozessorsystem (Abk.)
 else{
  KAFFINITY m;
  KDPC *Dpc,*pDpc;
  Dpc=ExAllocatePoolWithTag(NonPagedPool,sizeof(KDPC)*MAXIMUM_PROCESSORS,'tplE');
  if (!Dpc) return;
  for (i=0,m=1,pDpc=Dpc; a>=m && i<MAXIMUM_PROCESSORS; i++,m<<=1,pDpc++) if (a&m) {
// Für den aktiven Prozessor direkt aufrufen, sonst unnötiges Kuddelmuddel mit IRQL
   if (i==KeGetCurrentProcessorNumber()) EachProcessorDpc(NULL,Callback,(PVOID)Arg,(PVOID)&a);
   else{
    KeInitializeDpc(pDpc,EachProcessorDpc,Callback);
    KeSetTargetProcessorDpc(pDpc,(char)i);
    KeInsertQueueDpc(pDpc,(PVOID)Arg,(PVOID)&a);
   }
  }
  while (a);	// warten bis alle fertig sind! (Aua! Aber besser geht's wohl nicht.)
  ExFreePoolWithTag(Dpc,'tplE');
 }
}
#else
# define EachProcessor(Callback,Arg) Callback(Arg)
#endif

#ifndef NTDDK
// Basisadresse der Interruptdeskriptortabelle (IDT) beschaffen
// PA: ECX=IDT-Basisadresse
static __declspec(naked) void GetIdtBase(void) { _asm{
	push	ecx
	push	ecx		/* Platz auf dem Stack */
	 sidt	[esp+2]
	pop	ecx
	pop	ecx		/* ECX = Basisadresse der IDT */
	ret
}}
#endif

#ifdef _X86_
// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
// PE: CL = Interrupt-Nummer
// PA: EAX = Adresse aus IDT (des aktuellen Prozessors)
// VR: EAX,ECX
static __declspec(naked) void* __fastcall GetIdtAddr(UCHAR nr) { _asm{
	movzx	eax,cl
#ifdef NTDDK
	mov	ecx,_PCR KPCR.IDT
#else
	call	GetIdtBase
#endif
	lea	ecx,[ecx+eax*8]
	mov	ax,[ecx+6]		/* High-Teil Offset */
	shl	eax,16
	mov	ax,[ecx+0]		/* Low-Teil Offset */
	ret
}}

// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
static __declspec(naked) void __fastcall SetIdtAddr(UCHAR nr) { _asm{
/* PE: EAX=neue Adresse für INT
 *     CL: Interrupt-Nummer
 * PA: -
 * VR: ECX,EDX,EAX */
	movzx	edx,cl
	mov	ecx,cr0
	push	ecx
	 btr	ecx,16		/* Supervisor Mode Write Protect */
	 mov	cr0,ecx
#ifdef NTDDK
	 mov	ecx,_PCR KPCR.IDT
#else
	 call	GetIdtBase
#endif
	 lea	ecx,[ecx+edx*8]
	 mov	[ecx+0],ax	/* Low-Teil Offset */
	 rol	eax,16
	 mov	[ecx+6],ax	/* High-Teil Offset */
	pop	ecx
	mov	cr0,ecx
	ret
}}

// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
static __declspec(naked) void GetInts(void) { _asm{
#ifdef NTDDK
	push	edi
	 mov	edx,offset GetIdtAddr
	 mov	cl,1
	 call	edx
	 cmp	eax,offset NewInt1	// schon gehookt?
	 je	nosave			// nichts tun! (Schleifen vermeiden)
	 mov	edi,offset OldInt
	 stosd
	 mov	cl,0x2E
	 call	edx
	 stosd
	 cmp	[HaveSysEnter],0
	 jz	nosave
	 mov	ecx,0x176		// IA32_SYSENTER_EIP
	 rdmsr
	 stosd
nosave:	pop	edi
#else
	mov	cl,1
	call	GetIdtAddr
	cmp	eax,offset NewInt1	// schon gehookt?
	je	nosave			// nichts tun! (Schleifen vermeiden)
	mov	[OldInt.Int1],eax
nosave:
#endif
	ret
}}


static __declspec(naked) void _fastcall SetSysEnter(void) { _asm{
//PE: EAX = zu setzende Adresse für SysEnter-Befehl
	cmp	[HaveSysEnter],0	// SysEnter vorhanden?
	jz	e			// sonst GPF!
	mov	ecx,0x176		// IA32_SYSENTER_EIP
	xor	edx,edx			// sonst GPF!
	wrmsr
e:	ret
}}


// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
static __declspec(naked) void _fastcall HookInts(int unused) { _asm{
// Faule Annahme: bei INT1 befindet sich bereits ein gültiger Gate-Deskriptor
// VR: EAX,EDX,ECX; EFlags unverändert
// Bei Hyperthreading hat jeder Prozessor eine eigene IDT.
	pushfd
	 cli
	 cmp	OldInt.Int1,0
	 jz	no_586
	 mov	eax,offset NewInt1	// ISR-Anfangsadresse + Prozessornummer*12
	 mov	cl,1
	 call	SetIdtAddr
#ifdef NTDDK
//	 cmp	[CurThreadPtr],0
//	 jnz	no_586		// kein NT
	 mov	eax,offset NewInt2E
	 mov	cl,0x2E
	 call	SetIdtAddr
	 mov	eax,offset NewSysEnter
	 call	SetSysEnter
#endif
no_586:
	popfd
	ret
}}

// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
static __declspec(naked) void _fastcall UnhookInts(int unused) { _asm{
// VR: EAX,EDX,ECX; EFlags unverändert
	pushfd
	 cli
#ifdef NTDDK
	 push	esi
	  mov	esi,offset OldInt
	  lodsd
	  or	eax,eax
	  jz	no_586
	  mov	cl,1
	  call	SetIdtAddr
//	 cmp	[CurThreadPtr],0
//	 jnz	no_586		// kein NT
	  lodsd
	  mov	cl,0x2E
	  call	SetIdtAddr
	  lodsd
	  call	SetSysEnter
no_586:
	 pop	esi
#else
	 mov	eax,[OldInt.Int1]
	 or	eax,eax
	 jz	no_586
	 mov	cl,1
	 call	SetIdtAddr
no_586:
#endif
	popfd
	ret
}}

// Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
static __declspec(naked) void cyLoadDR(void) { _asm{
/* Debugregister des aktuellen Prozessors laden/wiederherstellen, VR: EAX,EBX,ECX,EDX,ESI,Flags */
// Liefert ECX!=0 wenn sich die Debugregister dabei verändern
	xor	ecx,ecx		// Bit-Sammler
	mov	esi,offset Usage// im Ring 0 immer CLD
	_emit	0x0F		// mov eax,cr4
	_emit	0x20
	_emit	0xE0
	bts	eax,3		/* PENTIUM Debug Extension (DE) aktivieren */
	_emit	0x0F		// mov cr4,eax
	_emit	0x22
	_emit	0xE0
	setnc	cl		// CL=1 wenn Debug Extension ausgeschaltet war
	mov	ebx,DR7
	xor	eax,eax
	lods	word ptr cs:[esi]
	or	ah,ah		// mit (gültiger) Adresse gefüllt?
	jz	no0
	mov	edx,DR0
	mov	DR0,eax
	xor	edx,eax
	or	ecx,edx		// ECX!=0 wenn DR0 verändert wurde
	or	ebx,0x000E0202
	btr	ebx,16		/* DR7=xxxxxxxx xxxx1110 xxxxxx1x xxxxxx1x */
no0:	lods	word ptr cs:[esi]
	or	ah,ah
	jz	no1
	mov	edx,DR1
	mov	DR1,eax
	xor	edx,eax
	or	ecx,edx		// ECX!=0 wenn DR1 verändert wurde
	or	ebx,0x00E00208
	btr	ebx,20		/* DR7=xxxxxxxx 1110xxxx xxxxxx1x xxxx1xxx */
no1:	lods	word ptr cs:[esi]
	or	ah,ah
	jz	no2
	mov	edx,DR2
	mov	DR2,eax
	xor	edx,eax
	or	ecx,edx		// ECX!=0 wenn DR2 verändert wurde
	or	ebx,0x0E000220
	btr	ebx,24		/* DR7=xxxx1110 xxxxxxxx xxxxxx1x xx1xxxxx */
no2:	lods	word ptr cs:[esi]
	or	ah,ah
	jz	no3
	mov	edx,DR3
	mov	DR3,eax
	xor	edx,eax
	or	ecx,edx		// ECX!=0 wenn DR3 verändert wurde
	or	ebx,0xE0000280
	btr	ebx,28		/* DR7=1110xxxx xxxxxxxx xxxxxx1x 1xxxxxxx */
no3:	mov	edx,DR7
	mov	DR7,ebx
	xor	edx,ebx
	or	ecx,edx		// ECX!=0 wenn DR7 verändert wurde
	ret
}}

// Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
static __declspec(naked) BOOLEAN _fastcall LoadDR(int unused) { _asm{
	push	esi
	push	ebx
	 call	cyLoadDR
	pop	ebx
	pop	esi
	add	ecx,-1		// Returnwert nach CY (gesetzt wenn ECX!=0)
	setc	al		// für sog. Hochsprachen...
	ret
}}
#else
void HookInts(int);	//amd64.asm
void UnhookInts(int);
BOOLEAN LoadDR(int);
#endif

// Alle DPCs laufen mit IRQL == DISPATCH_LEVEL
static VOID SetDebDpc(IN PKDPC dpc,PVOID x,PVOID a,PVOID b) {
 if (LoadDR(0)) {
  DebRegStolen[0]++;
  Vlpt_KdPrint2(("Debugregister gemaust!\n"));
 }
}

/*********************************
 * Prozessorfeatures detektieren *
 *********************************/
#ifdef _X86_
static __declspec(naked) void SwapID(void) { _asm{	/* Pentium-Test */
	pushfd
	pop	eax
	btc	eax,21
	push	eax
	popfd
	ret
}}

// Liefert TRUE wenn der Prozessor das Umschalten des CPUID-Flagbits zulässt
__declspec(naked) BOOLEAN IsPentium(void) { _asm{
 	call	SwapID
	xchg	ecx,eax
	call	SwapID
	sub	eax,ecx
	je	no
	xor	eax,eax
	push	ebx
	inc	eax
	cpuid
	bt	edx,2			// DE = Debugging Extensions?
	pop	ebx
	setc	[HaveDebugExt]
	bt	edx,11			// SEP = SysEnter Present?
	jnc	yes			// kein SYSENTER!
// Sonderfall (Family==6 && Model<3 && Stepping<3) prüfen
	mov	edx,eax
	and	edx,0xFF0		// DH = Family / DL = Model
	and	eax,0x0F		// AL = Stepping
	cmp	dh,6
	jne	k1
	cmp	dl,0x30
	jae	k1
	cmp	al,3
	jb	yes			// kein SysEnter
k1:	mov	[HaveSysEnter],1
yes:	mov	al,1
no:	ret
}}

// Nur aufrufen wenn IsPentium() TRUE lieferte
void PrepareDR(void) {
/* PA: [OldInt.Int1]=Adresse von INT1, aber nur beim Pentium */
// Weiterhin SaveDS, SaveFS, CurThreadPtr
 _asm{
#ifndef NTDDK 
	push	edi
 	 mov	cl,0		// Int0 (bei Int1 stört SoftICE)
	 call	GetIdtAddr
	 xchg	edi,eax
// Sucht ab Int0 den Befehl "mov edi,[xxxxxxxx]"; der Wert ist der
// globale Zeiger für den aktuellen Thread (Stack-Tausch) (Win98)
	 mov	eax,0xC766006A	// So geht's bei W2K los (PUSH 0, ...)
	 scasd
	 stc
	 je	f		// keine Bytefolge suchen, NT-Betriebssystem
	 mov	ecx,100h
	 mov	al,0x8B		// Bytefolge 8B 3D suchen
b:	 repne	scasb
	 stc
	 jne	f
	 cmp	byte ptr [edi],0x3D
	 jne	b
	 mov	eax,[edi+1]
	 mov	[CurThreadPtr],eax	// gleichzeitig Win9x-Kennung
f:	pop	edi
	jc	exi
#endif
// holt den OldInt1-Zeiger, aber bitte nur, wenn's ein Pentium ist
	call	GetInts
	mov	[SaveDS],ds	// Win9x: 30h	WinNT: 23h
	mov	[SaveFS],fs	// Win9x: 78h	WinNT: 30h
// markiert die bereits vor dem Treiber-Start verwendeten Debugregister
// als "nicht verwendungsfähig für uns".  Aufzurufen beim Treiber-Start
 	mov	edx,offset Usage
	mov	eax,DR7
	push	4
	pop	ecx
l:	test	al,3		// Lx oder Gx gesetzt? (Also in Benutzung?)
	setnz	[edx]		// wenn ja, auf 1 setzen, sonst 0 lassen
	shr	eax,2		// nächstes Debugregister
	add	edx,2		// nächstes Usage-Word
	loop	l
 }
// Debugregister-Diebstahl zyklisch rückgängig machen (WinXP)
// Besser wäre es, die Ursache auszumachen...
 KeInitializeTimer(&debset.tmr);
 KeInitializeDpc(&debset.dpc,SetDebDpc,NULL);
#ifndef NTDDK 
exi:;
#endif
}
#else
void iPrepareDR(void);	//amd64.asm

BOOLEAN IsPentium(void) { return TRUE;}	// vorläufig, liefert künftig stets TRUE

void PrepareDR(void) {
 iPrepareDR();
 KeInitializeTimer(&debset.tmr);
 KeInitializeDpc(&debset.dpc,SetDebDpc,NULL);
}
#endif

static void __fastcall SetTimerX() {
 KeSetTimerEx(&debset.tmr,RtlConvertLongToLargeInteger(100*-10000),
   100,&debset.dpc);
}

#ifdef _X86_
/**********************************
 * IOPM-basierter Trap (Win98/Me) *
 **********************************/

static __declspec(naked) void FindAddr(void) { _asm{
//PE: DX=Portadresse
//PA: NZ wenn nicht gefunden
//    EDI=X4DR-Eintrag, ESI=Index, EDX unverändert
//VR: ECX,EAX,ESI,EDI
//Stille Voraussetzung:
// Adressen beim Word- und DWord-Zugriff kreuzen keine 4-Byte-Grenze!
	cmp	dh,1		// Niemals <100h trappen!
	jc	exi		// NZ!
	mov	eax,edx
	and	al,0xFC		// Vier aufeinanderfolgende Adressen
	push	Usage_Len
	pop	ecx
	mov	edi,offset Usage
	repne	scasw		// Adresse suchen
	jne	exi
	push	Usage_Len-1
	pop	esi		// Index ermitteln
	sub	esi,ecx
	mov	edi,X4DR[esi*4]	// pDevExt laden
	test	byte ptr[edi]DEVICE_EXTENSION.f,No_Function	// (temporär) gesperrt?
exi:	ret
}}

#ifndef NTDDK

#ifdef IOPMDEBUG
// Irgendwie geht mein SoftICE nicht mehr!
static char debugstr[]= "USB2LPT:IOPM:INB(###)=##\r\n";
static char debugstr1[]="USB2LPT:IOPM:OUTB(###,##)\r\n";
static char debugstr2[]="USB2LPT:IOPM:Simulate_IO\r\n";

static __declspec(naked) void __fastcall outhex() { _asm{
	movzx	ecx,cl
	std
l1:	push	eax
	 and	al,0x0F
	 add	al,0x90
	 daa
	 adc	al,0x40
	 daa
	 stosb
	pop	eax
	shr	eax,4
	loop	l1
	cld
	ret
}}
#endif

// Versuch für/wegen GhaiRacer 091101
static __declspec(naked) void __fastcall IOCallback() { _asm{
//PE:	EBX = VM-Handle
//	ECX = IoType
//	EDX = Portadresse
//	EBP = Zeiger auf Client_Reg_Struc
//	EAX = Datenbyte/wort/doppelwort (nur bei einfachem OUT-Befehl)
//PA:	EAX = Datenbyte/wort/doppelwort (nur bei einfachem IN-Befehl)
//VR:	alle
	test	cl,0x38		// komplexer I/O-Befehl (Word, DWord, [rep] ins/outs)
	jnz	l2		// zerlegen lassen
	test	cl,BYTE_OUTPUT	// OUT-Befehl?
	jnz	l1
	call	FindAddr
	inc	[edi]DEVICE_EXTENSION.ac.rpu
#ifdef IOPMDEBUG
	 push	edx
#endif
	push	-1		// Platz auf Stack mit 0xFFFFFFFF
	mov	eax,esp
	push	1		// stets 1 Byte
	push	eax		// Adresse der Rückgabedaten
	push	edx		// Portadresse
	push	edi		// DEVICE_EXTENSION
	call	HandleIn	// blockiert Thread
	pop	eax		// Rückgabedaten
#ifdef IOPMDEBUG
	 pop	edx
	 pushad
	  mov	esi,offset debugstr
	  lea	edi,[esi+23]
	  mov	cl,2
	  call	outhex
	  lea	edi,[esi+19]
	  mov	eax,edx
	  mov	cl,3
	  call	outhex
	  VMMCall(Out_Debug_String)
	 popad
#endif
	ret
l1:
	push	eax
	 call	FindAddr
	pop	eax
	inc	[edi]DEVICE_EXTENSION.ac.wpu
#ifdef IOPMDEBUG
	 pushad
	  mov	esi,offset debugstr1
	  lea	edi,[esi+23]
	  mov	cl,2
	  call	outhex
	  dec	edi
	  mov	eax,edx
	  mov	cl,3
	  call	outhex
	  VMMCall(Out_Debug_String)
	 popad
#endif
	push	1
	push	eax
	push	edx
	push	edi
	call	HandleOut
	ret
l2:
#ifdef IOPMDEBUG
	pushad
	 mov	esi,offset debugstr2
	 VMMCall(Out_Debug_String)
	popad
#endif
	VMMJmp(Simulate_IO)	// von Windows 98/Me aufsplitten lassen
}}

static UCHAR HasTrap[Usage_Len];	// Bits 0..3 = Belegungsbits für Portadresse+0 bis +3

// 4 aufeinanderfolgende Adressen per IOPM belegen
static __declspec(naked) void __fastcall InstallIoHandler(int i/*ECX*/) { _asm{
	push	esi
	 xor	eax,eax
	 inc	eax		// AH = Belegung, AL = Bitmaske
	 movzx	edx,Usage[ecx*2]
	 mov	esi,offset IOCallback
l1:	 VMMCall(Install_IO_Handler)
	 jc	l2
	 or	ah,al		// Trap installiert - vermerken
l2:	 shl	al,1
	 inc	edx
	 cmp	al,16
	 jb	l1
	 mov	HasTrap[ecx],ah	// Belegung abspeichern
	pop	esi
	ret
}}

// 4 aufeinanderfolgende Adressen per IOPM freigeben, aber jeweils nur bei vorhergehender Belegung
static __declspec(naked) void __fastcall RemoveIoHandler(int i/*ECX*/) { _asm{
	xor	eax,eax
	xchg	HasTrap[ecx],ah
	inc	eax		// AH = Belegung, AL = Bitmaske
	movzx	edx,Usage[ecx*2]
l1:	test	ah,al
	jz	l2		// Nicht belegt worden
	VMMCall(Remove_IO_Handler)
l2:	shl	al,1
	inc	edx
	cmp	al,16
	jb	l1
	ret
}}
#endif


/********************************
 * API (öffentliche Funktionen) *
 ********************************/

// Scans the USHORT array for given USHORT and returns a pointer to entry; returns NULL if not found
static __declspec(naked) USHORT* __fastcall ScanMemW(SIZE_T l /*ECX*/, const USHORT*p /*EDX*/, USHORT a /*stack*/){ _asm{
	xchg	edx,edi
	mov	eax,[esp+4]
	repne	scasw
	lea	eax,[edi-2]
	je	found
	xor	eax,eax
found:	xchg	edi,edx
	ret	4
}}
#else
USHORT* ScanMemW(SIZE_T,const USHORT*,USHORT);	//amd64.asm
#endif

static USHORT* MakeSpace(BOOLEAN DebReg) {
// Macht Platz in Usage[], entsprechend X4DR[] und UsageBits, für einen neuen, erzwungenen Eintrag.
// Entweder auf den ersten 4 Plätzen (DebReg!=0) oder auf den hinteren 28 Plätzen (DebReg==0).
// Voraussetzung: Die gewünschten Plätze sind bereits besetzt!
// Zurzeit keine Implementierung eines MRU-Mechanismus'.
 int i;
 ULONG m=0;				// Bitmaske für stehen bleibende Bits; 0-Bitpositionen werden geschoben
 USHORT*p=Usage;
 PDEVICE_EXTENSION*x=X4DR;
 if (DebReg) {
  USHORT*q=ScanMemW(4,p,1);		// Suche Win98-vorbelegtes Debugregister (markiert mit 1) zum Überschreiben
  if (q) {
   p=q;					// diese Stelle nehmen, nicht schieben
   DebReg=3;				// Für Debugausgabe markieren ("3" ausgeben)
   goto w98;
  }
  q=ScanMemW(Usage_Len-4,p+4,0);	// Suche freie Stelle auf den hinteren 28 Plätzen zum Zusammenschieben
  if (q) {
   i=(int)(q-p);			// Anzahl zu verschiebender Plätze (4..31)
   if (i<31) m=0xFFFFFFFFUL<<(i+1);	// oben stehen bleibende Bits ermitteln
  }else{
   i=Usage_Len-1;			// Wenn nichts frei, purzelt letzte Stelle heraus
   DebReg=2;				// Für Debugausgabe markieren ("2" ausgeben)
  }
 }else{
  p+=4;
  x+=4;
  i=Usage_Len-5;			// hier purzelt immer etwas heraus (wegen Voraussetzung, s.o.)
  m=0x0F;
 }
 RtlMoveMemory(p+1,p,i<<1);		// Platz machen bei Adressliste Usage[]
 RtlMoveMemory(x+1,x,i<<2);		// Platz machen bei Device-Extension-Liste X4DR[]
 UsageBits=(UsageBits<<1)&~m | UsageBits&m;
w98:
 Vlpt_KdPrint(("MakeSpace:%u\n",DebReg));
 return p;
}

char _stdcall GetIndexDR(USHORT adr) {
// Liefert Momentanbelegung der E/A-Adresse
// PA: 0..3: Debugregister
//     >=4: nur Anzapfung {READ|WRITE}_PORT_U{CHAR|SHORT|LONG}
//     -4: nicht gefunden (bspw. inzwischen herausgeworfen)
// Nebenfunktion: Findet auch freie Indizes mit adr==0 und Win98-vorbelegte mit adr==1
// Muss für verwertbares Ergebnis mit DPC_LEVEL (und angehaltenen anderen Prozessoren?) gerufen werden!
 USHORT*p=Usage;
 USHORT*q=ScanMemW(Usage_Len,p,adr);
 if (!q) return -4;
 return (char)(q-p);
}

char _stdcall AllocDR(USHORT adr,PDEVICE_EXTENSION X,UCHAR flags) {
// Belegung eines E/A-Debugregisters und andere Arbeiten
// Start der Umleitung von 4 aufeinander folgenden Portadressen
// via Debugregister (wenn in flags UC_Debugreg gesetzt ist)
// und via {READ|WRITE}_PORT_U{CHAR|SHORT|LONG}
// sowie (Win98/Me) via IOPM
// Problem: Kein vernünftiger MRU-Mechanismus für Debugregister-Belegung!
// Ist aufwändig und schwer kalkulierbar, es müssten die Traps geloggt werden.
// PA: 0..3: Debugregister wurde belegt, Umleitung zusätzlich per {READ|WRITE}_PORT_U{CHAR|SHORT|LONG}
//     >=4:  kein Debugregister belegt, Umleitung nur per {READ|WRITE}_PORT_U{CHAR|SHORT|LONG}
//     -1: Parameterfehler
//     -2: Adresse wird bereits benutzt (eine Adresse kann nicht mehrere Traps haben)
//     -3: Kein Platz in Tabelle
// Hinweis: Der rückgegebene (nichtnegative) Index zeigt die Momentanbelegung an und kann sich ändern,
// wenn andere Allokationen eine Verschiebung erwirken!
 int i=Usage_Len;
 USHORT*p=Usage;
 BOOLEAN DebReg=flags&HaveDebugExt;	// UC_Debugreg ist Bit 0
 if (adr&0x3) return -1;	// Fehlerkode: falsche Adresse
 if (!(adr&0xFF00)) return -1;	// Board-Register (<100h) sind ebenfalls unzulässig: viel zu gefährlich!
 if (ScanMemW(i,p,adr)) return -2; // Schon benutzt
 if (!DebReg) p+=4, i-=4;	// nur hintere 28 Plätze
 else if (flags&UC_ForceAlloc) i=4;	// nur vordere 4 Plätze
 p=ScanMemW(i,p,0);
 if (!p && flags&UC_ForceAlloc) p=MakeSpace(DebReg);
 if (p) {
#ifdef NTDDK
  KIRQL irql=KeRaiseIrqlToDpcLevel();
#endif
  i=(int)(p-Usage);
  X4DR[i]=X;
  *p=adr;
  if (!(UsageBits&0xF) && i<4) {
//#if defined(NTDDK) && !defined(_X86_)
//   InstallExceptionHandler();
//#else
   EachProcessor(HookInts,0);
//#endif
   SetTimerX();
  }
  UsageBits|=1<<i;
  if (i<4) EachProcessor(LoadDR,i);	// Rückgabewert egal
#ifdef NTDDK
  KeLowerIrql(irql);
#else
  InstallIoHandler(i);
#endif
  return i;
 }
 return -3;			// Kein Register frei
}

#ifdef _X86_
// Diese Routine wird mit ECX = Debugregister-Nummer aufgerufen
// Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
static __declspec(naked) void _fastcall UnloadDR(int debregnumber) { _asm{
	mov	edx,DR7
	mov	eax,0xFFFCFFF0	// Maske für DR7, HiWord/LoWord vertauscht
	shl	cl,1		// Nr. mal zwei
	shl	eax,cl		// HiWord (künftiges LoWord) richtig
	shl	ax,cl		// LoWord (künftiges HiWord) richtig
	ror	eax,16		// Vertauschen HiWord/LoWord
	and	eax,edx		// Bits weg, Debugregister frei
	mov	DR7,eax
	xor	eax,eax
	shr	cl,1		// wieder zurück
	jnz	no0
	mov	DR0,eax		// hübsch machen (nicht erforderlich, aber macht das Debuggen übersichtlicher)
no0:	loop	no1
	mov	DR1,eax
no1:	loop	no2
	mov	DR2,eax
no2:	loop	no3
	mov	DR3,eax
no3:	ret
}}
#else
void UnloadDR(int);	//amd64.asm
#endif


char _stdcall FreeDR(USHORT adr) {	// Freigabe eines Debugregisters / einer Anzapfung
 int dr;
 if (adr&3 || !(adr&0xFF00)) return -1;	// Parameterfehler
 dr=GetIndexDR(adr);
 if (dr<0) return dr;		// Nicht gefunden (-4)
#ifdef NTDDK
 {KIRQL irql=KeRaiseIrqlToDpcLevel();
#else
 RemoveIoHandler(dr);
#endif
 Usage[dr]=0;			// Freigabe (unsauber, <dr> könnte ungültig sein! cmpxchg oder Mutex!)
 X4DR[dr]=NULL;
 UsageBits&=~(1<<dr);
 if (dr<4) {
  EachProcessor(UnloadDR,dr);
  if (!(UsageBits&0xF)) {	// Kein Debugregister beteiligt?
   KeCancelTimer(&debset.tmr);	// Ganz zurückziehen
//#if defined(NTDDK) && !defined(_X86_)
//   UninstallExceptionHandler();
//#else
   EachProcessor(UnhookInts,0);
//#endif
  }
 }
#ifdef NTDDK
 KeLowerIrql(irql);}
#endif
 return dr;			// okay (>=0)
}

/************************************************************
 ** Abfangen von READ_PORT_UCHAR und WRITE_PORT_UCHAR (NT) **
 ************************************************************/
// Für LabVIEW muss extra ...ULONG abgefangen werden!
#ifdef _X86_

static __declspec(naked) ULONG MyWritePort(void) { _asm{
// Gleiches "Ende" für MyWritePortUchar/Ushort/Ulong
	 inc	[edi]DEVICE_EXTENSION.ac.wpu
	 push	[esp+1Ch+4]	// EAX
	 push	edx
	 push	edi
	 call	HandleOut	// blockiert Thread gelegentlich
	popad
	popfd
	ret	8
}}

static __declspec(naked) ULONG MyReadPort(void) { _asm{
// Gleiches "Ende" für MyReadPortUchar/Ushort/Ulong
	 inc	[edi]DEVICE_EXTENSION.ac.rpu
	 lea	eax,[esp+1Ch+4]	// PUSHAD-Struktur modifizieren lassen
	 push	eax
	 push	edx
	 push	edi
	 call	HandleIn	// blockiert Thread immer für ca. 1 ms
	popad
	popfd
	ret	4
}}

static __declspec(naked) ULONG MyWritePortBuffer(void) { _asm{
// Gleiches "Ende" für MyWritePortBufferUchar/Ushort/Ulong
	 inc	[edi]DEVICE_EXTENSION.ac.wpu
	 inc	[edi]DEVICE_EXTENSION.ac.fail
	 pop	eax		// Längenangabe
	 dec	eax
	 jz	l1
	 inc	[edi]DEVICE_EXTENSION.ac.wdw
l1:	popad
	popfd
	ret	12
}}

static __declspec(naked) ULONG MyReadPortBuffer(void) { _asm{
// Gleiches "Ende" für MyReadPortBufferUchar/Ushort/Ulong
	 inc	[edi]DEVICE_EXTENSION.ac.rpu
	 inc	[edi]DEVICE_EXTENSION.ac.fail
	 pop	eax		// Längenangabe
	 dec	eax
	 jz	l1
	 inc	[edi]DEVICE_EXTENSION.ac.wdw
l1:	popad
	popfd
	ret	12
}}

static __declspec(naked) void MyWritePortUchar(PUCHAR a,UCHAR b) { _asm{
// Gleiche Signatur wie WRITE_PORT_UCHAR()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// b
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	1
	 jmp	MyWritePort
l1:
	popad
	popfd
	out	dx,al
	ret	8
}}

static __declspec(naked) UCHAR MyReadPortUchar(PUCHAR a) { _asm{
// Gleiche Signatur wie READ_PORT_UCHAR()
	mov	edx,[esp+4]	// a
	xor	eax,eax
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	1
	 jmp	MyReadPort
l1:
	popad
	popfd
	in	al,dx
	ret	4
}}

static __declspec(naked) void MyWritePortUshort(PUCHAR a,USHORT b) { _asm{
// Gleiche Signatur wie WRITE_PORT_USHORT()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// b
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	2
	 jmp	MyWritePort
l1:
	popad
	popfd
	out	dx,ax
	ret	8
}}

static __declspec(naked) USHORT MyReadPortUshort(PUCHAR a) { _asm{
// Gleiche Signatur wie READ_PORT_USHORT()
	mov	edx,[esp+4]	// a
	xor	eax,eax
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	2
	 jmp	MyReadPort
l1:
	popad
	popfd
	in	ax,dx
	ret	4
}}

static __declspec(naked) void MyWritePortUlong(PUCHAR a,ULONG b) { _asm{
// Gleiche Signatur wie WRITE_PORT_ULONG()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// b
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	4
	 jmp	MyWritePort
l1:
	popad
	popfd
	out	dx,eax
	ret	8
}}

static __declspec(naked) ULONG MyReadPortUlong(PUCHAR a) { _asm{
// Gleiche Signatur wie READ_PORT_ULONG()
	mov	edx,[esp+4]	// a
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	4
	 jmp	MyReadPort
l1:
	popad
	popfd
	in	eax,dx
	ret	4
}}

static __declspec(naked) void MyWritePortBufferUchar(PUCHAR a,PUCHAR m,ULONG l) { _asm{
// Gleiche Signatur wie WRITE_PORT_BUFFER_UCHAR()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	1
	 jmp	MyWritePortBuffer
l1:
	popad
	popfd
	xchg	esi,eax
	rep outsb
	xchg	esi,eax
	ret	12
}}

static __declspec(naked) void MyReadPortBufferUchar(PUCHAR a,PUCHAR m,ULONG l) { _asm{
// Gleiche Signatur wie READ_PORT_BUFFER_UCHAR()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	1
	 jmp	MyReadPortBuffer
l1:
	popad
	popfd
	xchg	edi,eax
	rep insb
	xchg	edi,eax
	ret	12
}}

static __declspec(naked) void MyWritePortBufferUshort(PUSHORT a,PUSHORT m,ULONG l) { _asm{
// Gleiche Signatur wie WRITE_PORT_BUFFER_USHORT()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	2
	 jmp	MyWritePortBuffer
l1:
	popad
	popfd
	xchg	esi,eax
	rep outsw
	xchg	esi,eax
	ret	12
}}

static __declspec(naked) void MyReadPortBufferUshort(PUSHORT a,PUSHORT m,ULONG l) { _asm{
// Gleiche Signatur wie READ_PORT_BUFFER_USHORT()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	2
	 jmp	MyReadPortBuffer
l1:
	popad
	popfd
	xchg	edi,eax
	rep insw
	xchg	edi,eax
	ret	12
}}

static __declspec(naked) void MyWritePortBufferUlong(PULONG a,PULONG m,ULONG l) { _asm{
// Gleiche Signatur wie WRITE_PORT_BUFFER_ULONG()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
// künftig kürzer:
//	push	4|PT_WRITE|PT_BUFFER
//	call	Redirect	// kümmert sich um den Rest
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	4
	 jmp	MyWritePortBuffer
l1:
	popad
	popfd
	xchg	esi,eax
	rep outsd
	xchg	esi,eax
	ret	12
}}

static __declspec(naked) void MyReadPortBufferUlong(PULONG a,PULONG m,ULONG l) { _asm{
// Gleiche Signatur wie READ_PORT_BUFFER_ULONG()
	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// m
	mov	ecx,[esp+12]	// l
	pushfd
	pushad
	 call	FindAddr
	 jnz	l1
	 push	4
	 jmp	MyReadPortBuffer
l1:
	popad
	popfd
	xchg	edi,eax
	rep insd
	xchg	edi,eax
	ret	12
}}
#endif

/***************************
 * Tabelle der Anzapfungen *
 ***************************/

#ifdef _X86_
// Funktionszeiger in Importtabelle (Keine Thunks generieren lassen!) - Nicht aufrufen!
extern void _imp__WRITE_PORT_UCHAR(int,int);
extern void _imp__READ_PORT_UCHAR(int);
extern void _imp__WRITE_PORT_USHORT(int,int);
extern void _imp__READ_PORT_USHORT(int);
extern void _imp__WRITE_PORT_ULONG(int,int);
extern void _imp__READ_PORT_ULONG(int);
extern void _imp__WRITE_PORT_BUFFER_UCHAR(int,int,int);
extern void _imp__READ_PORT_BUFFER_UCHAR(int,int,int);
extern void _imp__WRITE_PORT_BUFFER_USHORT(int,int,int);
extern void _imp__READ_PORT_BUFFER_USHORT(int,int,int);
extern void _imp__WRITE_PORT_BUFFER_ULONG(int,int,int);
extern void _imp__READ_PORT_BUFFER_ULONG(int,int,int);

// Alte und neue Funktionszeiger
void* func[24]={
 _imp__WRITE_PORT_UCHAR,	MyWritePortUchar,	_imp__READ_PORT_UCHAR,		MyReadPortUchar,
 _imp__WRITE_PORT_USHORT,	MyWritePortUshort,	_imp__READ_PORT_USHORT,		MyReadPortUshort,
 _imp__WRITE_PORT_ULONG,	MyWritePortUlong,	_imp__READ_PORT_ULONG,		MyReadPortUlong,
 _imp__WRITE_PORT_BUFFER_UCHAR,	MyWritePortBufferUchar,	_imp__READ_PORT_BUFFER_UCHAR,	MyReadPortBufferUchar,
 _imp__WRITE_PORT_BUFFER_USHORT,MyWritePortBufferUshort,_imp__READ_PORT_BUFFER_USHORT,	MyReadPortBufferUshort,
 _imp__WRITE_PORT_BUFFER_ULONG,	MyWritePortBufferUlong,	_imp__READ_PORT_BUFFER_ULONG,	MyReadPortBufferUlong};
#endif

/**********************
 * Utility-Funktionen *
 **********************/
#ifdef _X86_
#ifdef NTDDK
static __declspec(naked) void BlockProcessorDpc(KDPC *Dpc, PVOID Context, PVOID Arg1, PVOID Arg2) { _asm{
	movzx	eax,_PCR KPCR.Number
	mov	ecx,[esp+12]		// ECX = Zeiger auf Affinitätsmaske (Arg1)
	lock btr dword ptr[ecx],eax	// Für diesen Prozessor als aufgerufen markieren
	mov	ecx,[esp+8]		// ECX = Zeiger auf Blocking (BOOLEAN)
	pushfd
	 cli				// Nicht einmal Interrupts dürfen dazwischenfunken!
block:	 cmp	BOOLEAN[ecx],0
	 jnz	block
	popfd
	mov	ecx,[esp+16]		// ECX = Zeiger auf Affinitätsmaske (Arg2)
	lock btr dword ptr[ecx],eax	// Für diesen Prozessor als erledigt markieren
	ret	16
}}

static void BlockSiblingsAndCall(void(*Callback)(void)) {
 ULONG i;
 volatile BOOLEAN Blocking;
 volatile KAFFINITY a,b;
 KIRQL irql=KeRaiseIrqlToDpcLevel();
 a=KeQueryActiveProcessors();
 i=KeGetCurrentProcessorNumber();
 a&=~(1<<i);		// aktuellen Prozessor aus Bitmaske herausnehmen - ob der Compiler "btr" einsetzt?
 b=a;
 if (a) {	 	// Bei Einzelprozessorsystem: Keine weitere Arbeit
  KAFFINITY m;
  KDPC *Dpc,*pDpc;
  Dpc=ExAllocatePoolWithTag(NonPagedPool,sizeof(KDPC)*MAXIMUM_PROCESSORS,'tplB');
  if (Dpc) {
   Blocking=TRUE;
   for (i=0,m=1,pDpc=Dpc; a>=m && i<MAXIMUM_PROCESSORS; i++,m<<=1,pDpc++) if (a&m) {
    KeInitializeDpc(pDpc,BlockProcessorDpc,(PVOID)&Blocking);
    KeSetTargetProcessorDpc(pDpc,(char)i);
    KeInsertQueueDpc(pDpc,(PVOID)&a,(PVOID)&b);
   }
   while (a);		// warten bis alle blockieren
   Callback();
   Blocking=FALSE;	// Andere Prozessoren erlösen sich
   while (b);		// warten bis alle fertig sind (ist vermutlich unnötig)
   ExFreePoolWithTag(Dpc,'tplB');
  }
 }else Callback();
 KeLowerIrql(irql);
}
#endif//NTDDK

static UCHAR save[12][5];	// Platz für die an 12 Stellen überschriebenen 5 Code-Bytes für JMP

#ifdef NTDDK
static __declspec(naked) void iHookSyscalls(void) { _asm{
#else
extern __declspec(naked) void HookSyscalls(void) { _asm{
#endif
	mov	edx,cr0
	pushad
	pushfd
	 btr	edx,16			// Supervisor Mode Write Protect
	 cli				// höchstes IRQL, keine ISR darf funken!
	 mov	cr0,edx
	 mov	edi,offset save
	 mov	esi,offset func
	 push	12
	 pop	ecx
l:	 lodsd				// Zeiger in Importtabelle
	 mov	ebx,[eax]		// Originaladresse
 	 mov	al,0E9h			// JMP rel.
	 xchg	[ebx],al
	 stosb
	 lodsd				// Umleitungsadresse
	 sub	eax,5			// Opcode-Länge JMP rel.
	 sub	eax,ebx
	 xchg	[ebx+1],eax
	 stosd
	 loop	l
	popfd
	popad
	mov	cr0,edx
	ret
}}

#ifdef NTDDK
static __declspec(naked) void iUnhookSyscalls(void) { _asm{
#else
extern __declspec(naked) void UnhookSyscalls(void) { _asm{
#endif
	mov	edx,cr0
	pushad
	pushfd
	 btr	edx,16			// Supervisor Mode Write Protect
	 cli				// höchstes IRQL, keine ISR darf funken!
	 mov	cr0,edx
	 xor	eax,eax
	 mov	esi,offset save
l:	 mov	edi,func[eax*8]
	 mov	edi,[edi]
	 inc	eax
	 movsb
	 cmp	al,12
	 movsd
	 jb	l
	popfd
	popad
	mov	cr0,edx
	ret
}}

#ifdef NTDDK
extern void HookSyscalls(void) { BlockSiblingsAndCall(iHookSyscalls); }
extern void UnhookSyscalls(void) { BlockSiblingsAndCall(iUnhookSyscalls); }
#endif

/***************************
 ** Portzugriffe und Trap **
 ***************************/
//#define CurXX 0xC0013E78
//#define CurVM 0xC0013EEC
#ifdef NTDDK
static __declspec(naked) void NewInt2E(void) { _asm{
	pushfd
	pushad
	 call	cyLoadDR
	 lock adc ss:[DebRegStolen+4],0		// Datensegmentregister ist nicht gesetzt, deshalb SS nehmen
	popad
	popfd
	jmp	cs:OldInt.Int2E
}}

static __declspec(naked) void NewSysEnter(void) { _asm{
	pushfd
	pushad
	 call	cyLoadDR
	 lock adc ss:[DebRegStolen+8],0
	popad
	popfd
	jmp	cs:OldInt.SysEnter
}}
#endif
static _declspec(naked) void HandleOutIn(void) { _asm{
// Hilfsroutine für Debugregister-Anzapfung
//  insb/outsb-Unterstützung würde tiefgreifende Erweiterungen erfordern,
//  um die verschiedenen Adressierungsarten (VM, PM16, PM32; Segmentpräfix) zu verarbeiten
// PE:	ECX Bit 22 = USE32-Bit (aus LAR-Befehl)
//	AH=Opcode EC(inb), ED(inw), EE(outb), EF(outw)
//	   Opcode 6C(insb), 6D(insw), 6E(outsb), 6F(outsw) nicht unterstützt!
//	   Opcode E4, E5, E6, E7 nicht unterstützt! (Wäre in AL)
//	AL=Präfix (66)
//	DX=Portadresse
//	ESI=Client-EAX-Zeiger
//	EDI=DevExt-Zeiger
//	Interrupts gesperrt
	mov	cl,2		// USE16
	bt	ecx,22
	jnc	use16
	mov	cl,4		// USE32
//jetzt:CL=4 oder 2 (USE32 oder USE16 je nach Attribut des unterbrochenen Kodesegmentes)
use16: 	cmp	al,0x66		// Präfixbyte? (Schätzung!!)
	jne	no_swap
	xor	cl,6		// aus 4 mach 2 und aus 2 mach 4
no_swap:xchg	ah,al
	cmp	al,0xEF		//OUT dx,ax oder OUT dx,eax
	je	out_cl
	cmp	al,0xEE		//OUT dx,al
	jne	no_outb
	mov	cl,1		//Präfix gilt nicht!
out_cl:	inc	[edi]DEVICE_EXTENSION.ac.out
	sti
	push	ecx
	push	[esi]		//Client_EAX
	push	edx
	push	edi
	call	HandleOut
	jmp	supported	//mit AL/AX/EAX=geschriebenes Byte
no_outb:
	cmp	al,0xED		//IN ax,dx oder IN eax,dx
	je	in_cl
	cmp	al,0xEC		//IN al,dx
	jne	no_inb
	mov	cl,1
in_cl:	inc	[edi]DEVICE_EXTENSION.ac.in
	sti
	push	ecx
	push	esi
	push	edx
	push	edi
	call	HandleIn	//Thread blockieren...
	jmp	supported
no_inb:
	inc	[edi]DEVICE_EXTENSION.ac.fail
#if DBG
 	int 3
#endif
supported:
	cli
	ret
}}

/* Stackaufbau 80386+, Win9x	WinNT
EBP+	Register-9x	EBP+	Register-NT
44	 GS		88		(nur V86)
40	 FS		84		(nur V86)
3C	 DS		80		(nur V86)
38	 ES		7C		(nur V86)
34	 SS		78		(nur Ring3)
30	 ESP		74		(nur Ring3)
2C	 EFlags		70		(Bit17 = V86)
28	 CS		6C		(kein Selektor wenn V86!)
24	 EIP		68
20	 Fehlerkode	64
1C	EAX		60	EBP
18	ECX		5C	EBX
14	EDX		58	ESI
10	EBX		54	EDI
0C	(ESP)		50	FS
08	EBP		4C	Geheimnisvolle Speicherzelle
04	ESI		48	0
00	EDI		44	EAX
			40	ECX
			3C	EDX
			38	DS
			34	ES
			30	GS
EFlags-Aufbau
21 20  19  18 17 16 15 14 13-12 11 10  9  8  7  6 5  4 3  2 1  0
ID VIP VIF AC VM RF  0 NT IOPL  OF DF IF TF SF ZF 0 AF 0 PF 1 CF
*/

static __declspec(naked) void NewInt1(void) { _asm{

#ifdef NTDDK
// NT-Version mit angepassten Registern ohne Stack-Umstapeln:
	push	eax		// Fehlerkode (später)
	push	ebp		// Mit EBP beginnt der Stack bei NT32, nicht mit PUSHAD
	 mov	eax,cs:[UsageBits]
	 mov	ebp,DR6
	 and	eax,0Fh
	 test	eax,ebp
	 jnz	access03
	pop	ebp
	pop	eax
	jmp	cs:OldInt.Int1
access03:
	 not	eax
	 and	eax,ebp		// "Unsere" Bits in DR6 löschen
	 mov	DR6,eax
	 bsf	ebp,ebp		// Merken, welches Debugregister es war (0..3) -> EBP
	 xor	eax,eax
	 xchg	[esp+4],eax	// Fehlerkode Null setzen, EAX restaurieren
	 cld
	 push	ebx		// Restlicher NT-Stack
	 push	esi
	 push	edi
	 push	fs
	 mov	fs,cs:[SaveFS]
	 push	fs:dword ptr[0]
	//mov	fs:dword ptr[0],-1
	 push	0
	 push	eax
	 push	ecx
	 push	edx
	 push	ds		// Bei V86-Unterbrechung sind da nur Nullen drin
	 push	es
	 push	gs
	  mov	di,cs:[SaveDS]
	  sub	esp,0x30
	  mov	ds,di
	  mov	edx,ebp		// Debugregister-Nummer (0..3)
	  mov	ebp,esp
	  test	byte ptr[ebp+0x72],2	// V86?
	  jz	protmode
	  lea	esi,[ebp+0x7C]	// Segmentregister umkopieren
	  lodsd
	  mov	[ebp+0x34],eax
	  lodsd
	  mov	[ebp+0x38],eax
	  lodsd
	  mov	[ebp+0x50],eax
	  lodsd
	  mov	[ebp+0x30],eax
	  mov	es,di
	  mov	ebx,[ebp+0x6C]	// Client_CS (V86-Modus, <64K)
	  shl	ebx,4
	  add	ebx,[ebp+0x68]	// Client_EIP (V86-Modus, <64K)
	  xor	ecx,ecx		// niemals USE32
	  jmp	fromvm
protmode:	// Protected Mode, entweder Ring 0 oder Ring 3
	  les	ebx,[ebp+0x68]	// Client_CS_EIP
	  lar	ecx,[ebp+0x6C]	// Client_CS -> USE32-Bit
fromvm:
	  mov	esi,fs:[0x124]	//TEB
	  add	esi,0x128	// lange Opcodes vermeiden
	  push	dword ptr[esi]	// alten Rahmenzeiger retten
	  mov	[esi],ebp	// neuen Rahmenzeiger setzen
	  mov	al,[ebp+0x6C]	// Client_CS
	  and	al,1
	  mov	[esi+0x134-0x128],al	// Privileg setzen
	  push	esi
//-------------------------
	   mov	ax,es:[ebx-2]	//Opcode (ggf. mit Präfix) - UNSAUBER, erwischt Mehrfachpräfixe sowie REP nicht
	   mov	es,di
	   mov	gs,di
//DevExt-Zeiger beschaffen für Rückruf
	   mov	edi,X4DR[edx*4]
//Richtung des Portzugriffs (Lesen oder Schreiben) ermitteln
	   lea	esi,[ebp+0x44]	// Client_EAX
	   mov	edx,[ebp+0x3C]	// Client_EDX
	   call	HandleOutIn
//-------------------------
	  pop	esi
	  pop	dword ptr[esi]		// Rahmenzeiger wiederherstellen
	  add	esp,0x30
	 test	byte ptr[ebp+0x72],2	// V86?
	 jz	novm
	add	esp,12
	jmp	nopop
novm:	pop	gs
	pop	es
	pop	ds
nopop:	pop	edx
	pop	ecx
	pop	eax
	pop	ebx		// Dummy-Lesen
	pop	fs:dword ptr[0]
	pop	fs
	pop	edi
	pop	esi
	pop	ebx
	pop	ebp
	add	esp,4		// "Fehlercode" übergehen
	iretd

#else	// Win98/Me

	push	0
 	pushad
	 mov	ecx,cs:[UsageBits]
	 mov	eax,DR6
	 and	ecx,0Fh
	 test	ecx,eax		// Zugriff auf eine "unserer" Adressen?
	 jnz	access03	// ja
	popad			// nein, andere Software ist dran
	lea	esp,[esp+4]	// Fehlerkode wegnehmen, verändert Flags nicht!
	jmp	cs:OldInt.Int1	// weiter (zu SoftICE o.ä.)
access03:			// Hier: Returnadresse wird zur Berechnung der Prozessornummer benötigt!
//bei jedem Portzugriff (LPT) steht in DX die Portadresse
//In CLIENT_EAX steht das Datenbyte (nur bei OUT)
	 not	ecx
	 and	ecx,eax		// "Unsere" Bits in DR6 löschen
	 mov	DR6,ecx
	 bsf	esi,eax		// Merken, welches Debugregister es war (0..3)
/* Kontext-Retten Start */
	 cld
	 mov	eax,cs:[CurThreadPtr]
	 mov	di,cs:[SaveDS]
	 mov	eax,cs:[eax]	// Cur_Thread_Handle
	 mov	ebp,esp		// CRS adressierbar machen (ab EIP -4!)
//Opcode beschaffen; Adreßrechnung je nach V86- oder Protmode
	 test	byte ptr [ebp+0x2E],2	// Client_EFlags:17: V86-Mode?
	 jz	protmode
// V86: Segmentregister sind schon auf dem Stack gerettet
	 mov	es,di		// Lineares Segment
	 mov	ebx,[ebp+0x28]	// Client_CS (V86-Mode, stets <64K)
	 shl	ebx,4
	 add	ebx,[ebp+0x24]	// Client_EIP (V86-Mode, stets <64K)
	 xor	ecx,ecx		// immer USE16
	 jmp	fromvm
protmode:
//Faule Annahme: Zugriff im Protected Mode mit lesbarem Code-Segment
//	mov	ebx,cs:[eax+0x14]	// Momentane Virtuelle Maschine CurVM
//	cmp	ebp,cs:[ebx+8]	// VM_ClientRegs
// Etwas sauberer ist der Test des Kodesegment-Registers auf dem Stack...
	 lar	ecx,[ebp+0x28]	// Client_CS: USE32 und DPL beschaffen
	 test	ch,0x60		// Privileg-Level
	 jz	pm0		// Intra-Ring-Aufruf wenn Null
// PM3: Bei Aufruf aus Ring3 heraus die V86-Segmentregister nachbilden
	 mov	[ebp+0x38],es
	 mov	[ebp+0x3C],ds
	 mov	[ebp+0x40],fs
	 mov	[ebp+0x44],gs	// V86-kompatibel ablegen
	 les	ebx,[ebp+0x24]	// Code-Zeiger (FAR48)
// Win98-typische Stack-Umschaltung bei Ring-Übergang...
fromvm:
	 mov	ds,di
	 add	eax,0x4C
	 xchg	esp,[eax]	// Stack (nicht aber EBP!) umschalten
	 push	eax
	 jmp	saved
pm0:
// PM0: Keine Ring- und Stackumschaltung! Aber trotzdem Register retten!
	 inc	dword ptr [ebp+0x20]	// Fehlerkode als Kennung missbrauchen
	 push	ds
	 push	es
	 push	fs
	 push	gs
	 les	ebx,[ebp+0x24]	// Code-Zeiger (FAR48)
	 mov	ds,di
saved:
	 mov	fs,[SaveFS]
	 mov	ax,es:[ebx-2]		//Opcode (AH, ggf. mit Präfix 66h) - UNSAUBER!!
	 mov	es,di
	 mov	gs,di
/* Kontext-Retten Ende */
//DevExt-Zeiger beschaffen für Rückruf
	 mov	edi,X4DR[esi*4]
//Richtung des Portzugriffs (Lesen oder Schreiben) ermitteln
	 lea	esi,[ebp+0x1C]	// Client_EAX
	 call	HandleOutIn
/* Kontext-Wiederherstellen (Win9x) Start */
	 bt	dword ptr [ebp+0x20],0	//Ohne Ringübergang?
	 jnc	trans
	 pop	gs
	 pop	fs
	 pop	es
	 pop	ds
	 jmp	nopop
trans:
	 pop	eax
	 xchg	esp,[eax]	// Win98-Stack rückschalten
	 test	byte ptr [ebp+0x2E],2	//V86-Mode?
	 jnz	nopop
	 mov	gs,[ebp+0x44]
	 mov	fs,[ebp+0x40]
	 mov	ds,[ebp+0x3C]
	 mov	es,[ebp+0x38]
nopop:
/* Kontext-Wiederherstellen (Win9x) Ende */
	popad
	add	esp,4		// "Fehlercode" übergehen
	iretd
#endif
}}

#else//_X86_

// Übergangsfunktion, wird vom 64-Bit-Assemblerkode (Int1-ISR) aufgerufen
// flags Bit 1:0 = Operandengröße, damit Typ von <data>: PBYTE,PWORD,PDWORD,PQWORD
// flags Bit 2 = Direction-Down-Bit (immer post-inkrement/dekrement)
// flags Bit 4 = IN (sonst OUT, damit <data> = const)
// flags Bit 5 = Nur Fehler zählen
void DispatchHook(char idx/*cl*/, USHORT adr/*dx*/, char flags/*r8*/, void*data/*r9*/, size_t reps/*stack*/) {
 PDEVICE_EXTENSION X=X4DR[idx];
 int opsize=1<<(flags&3);
 if (flags&3) X->ac.wdw++;	// kurioser Befehl für Parallelport
// int incval=opsize;
// if (flags&4) incval=-incval;
 if (flags<16) {	// OUTPUT BYTE/WORD/DWORD/QWORD
  X->ac.out++;
//  for(;reps;--reps} {
   HandleOut(X,adr,*(ULONG*)data,opsize);
//   (BYTE*)data+=incval;
//  }
 }else if (flags<32) {	// INPUT BYTE/WORD/DWORD/QWORD
  X->ac.in++;
//  for(;reps;--reps} {
   HandleIn(X,adr,(ULONG*)data,opsize);
//   (BYTE*)data+=incval;
//  }
 }else{
  X->ac.fail++;
 }
}

#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)
#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];

CASSERT(FIELD_OFFSET(KPCR,IdtBase)==56,vlpt);

#endif//_X86_
Detected encoding: UTF-80