Source file: /~heha/secret/dknack.zip/SRC/VDONGLE.ASM

	.386p
	.XLIST
	include	tvmm2.inc
	include	debug2.inc
	.LIST
	locals

VDONGLE_Major_Ver	equ	1
VDONGLE_Minor_Ver	equ	06
VDONGLE_Ver		equ	VDONGLE_Major_Ver*256+VDONGLE_Minor_Ver
VDONGLE_Device_ID	equ     3B48h		;ab Version 1.02
;>You have been assigned a device ID of 3B48h for your VELGNOD.386 virtual
;>device.  This number is uniquely assigned to this device.

Declare_Virtual_Device  VDONGLE, VDONGLE_Major_Ver, VDONGLE_Minor_Ver,\
			VDONGLE_Control_Proc, VDONGLE_Device_ID,\
			3B000000h, , VDONGLE_API

;3C000000 ist die VPD_Init_Order des konkurrierenden VPD.VXD unter Windows95

;Int2F-API-Funktionen (Nur für System-VM):
;AH=0	Versionsnummer besorgen (AH=Hauptversion, AL=Nebenversion)
;AH=1	Callback-Adresse festlegen (ES:BX=Adresse)
;	Callback wird mit AL=Datenbyte und AH=Port-Festlegung gerufen
;	AH Bits 0..1 geben Portadresse an, AH Bit 2 ist beim Lesen gesetzt
;AH=2	Callback und Puffer ausschalten (beim Beenden der .EXE bzw. in der
;	WEP der .DLL aufrufen)
;AH=3	Emulationsmodus einschalten (AL=1) oder ausschalten (AL=0)
;	Bei inaktivem Callback bezieht sich die "Emulation" auf das
;	Vorgaukeln eines fehlenden Dongles - Vermeidung von Portzugriffen
;	z.B. bei schlecht gecrackten Programmen in Verbindung mit LPTDAC
;	AL=FF (altes API: AL=2) fragt aktuelle Stellung ab; Ergebnis in AL
;	Standard ist AUSgeschaltet.
;AH=4	Zugriff-Knack einschalten (AL=1) oder ausschalten (AL=0)
;	AL=FF (altes API: AL=2) fragt aktuelle Stellung ab; Ergebnis in AL
;	Standard ist EINgeschaltet, beeinflußbar über SYSTEM.INI-Eintrag
;AH=5	V.1.01: Puffer aktivieren (ES:BX=Adresse, CX=Länge in WORDs)
;	Der Puffer ist in Words organisiert, ähnlich einem Pascal-String
;	mit Unicode-Zeichen, und mit vorangestelltem Längen-Word.
;	Abschluß der Kette bildet ein IN-Befehl, der ebenfalls mit ab-
;	gespeichert wird (jedoch nicht in die Längen-Zählung eingeht).
;	Ist der Puffer zu kurz, enthält das Längen-Word CX-1 und nur
;	OUT-Befehle (kein IN am Ende). Deshalb: Puffer ausreichend
;	dimensionieren, z.B. 2048 Words groß.
;	V.1.03: Ist ES=0, dann reserviert VDONGLE.386 einen Puffer und
;	einen GDT-Eintrag; der Selektor darauf wird in AX zurückgeliefert
;AH=6	V.1.04: Int21/AH=2C (Uhrzeit abholen, hh:mm:ss:hh = CH:CL:DH:DL)
;	abfangen und an Callback mit AX=2C01 und EDX=Uhrzeit weiterreichen.
;	AL=1: einschalten, AL=0: ausschalten, AL=FF: Stellung abfragen
;	Zweck ist das Anhalten bzw. definierte Starten von
;	Zufallsgeneratoren. Der Callback kann mit AL=0 verhindern, daß die
;	originale Routine gerufen wird. Mit AX=2C23h kann der Callback die
;	wirkliche Uhrzeit abholen, ohne daß reentrant der Callback gerufen
;	wird (Hintertür) [V.1.06: Nur System_VM trappen]
;AH=7	V.1.05: Trapping aktivieren (AL=1) oder deaktivieren (AL=0)
;AH=8	V.1.06: OUT-Zusammenfassung aktivieren (AL=1), deaktivieren (AL=0)
;	bzw. abfragen (AL=FFh)

;*******************************************
;*** Residente Daten und Code (pageable) ***
;*******************************************
VxD_DATA_SEG

PortBase	dd	378h	;Portadresse der Emulation
;Die Portadresse wird durch "VDonglePort=LPTx" oder "...=xxx" unter [386enh]
;festgelegt. (Gilt nicht für Sys_Dynamic_Init)
; Standard ist LPT1, falls vorhanden, sonst 378h
; (bei Sys_Dynamic_Init =37C, wegen VPD.VXD bzw. VLPT.SYS)
PhysBase	dd	378h	;Physikalische Portadresse
;Die Portadresse wird durch "VDonglePhys=LPTx" oder "...=xxx" unter [386enh]
;festgelegt. (Gilt nicht für Sys_Dynamic_Init)
; Standard ist LPT1, falls vorhanden, sonst 378h
; (auch bei Sys_Dynamic_Init)

CallbackAdr	dd	0	;16:16-Rückruf-Funktion in System-VM
BufferAdr	dd	0	;FLAT-Pufferadresse
BufferLen	dd	0	;Puffer-Länge in WORDs
EmuMode		db	0	;Emulation ein/aus
TickSound	db	1	;Knackgeräusch über Speaker ein/aus
;Das Knackgeräusch kann mit "VDongleTick=off" unter [386enh] per defaut
;ausgeschaltet werden. (Gilt nicht für Sys_Dynamic_Init)

;Hier folgen die Daten für die Emulation (EmuMode<>0)
DataMirror	db	0
CtlIMirror	db	11000111b	;"Read-Only"!
CtlOMirror	db	11101100b

RetVal		db	0
HeapSel		dw	0	;Heap-Selektor
Semaphore	dd	0	;Handle zum Einschläfern von DOS-Boxen...
ChangedPort	dw	0	;zur Rücknahme des Patches beim Beenden
WasAddr		dw	0
DosTime		dd	?	;Übergabezelle für Longints
HookSwitch	db	0
ConcatEquals	db	0	;Identische Zugriffe im Puffer zusammenfassen

	align	4

uptab	dd	OFFSET32 GetVer
	dd	OFFSET32 SetCallback
	dd	OFFSET32 RemoveCallback
	dd	OFFSET32 SetEmu
	dd	OFFSET32 SetTick
	dd	OFFSET32 SetBuffer
	dd	OFFSET32 Hook212C
	dd	OFFSET32 TrapControl
	dd	OFFSET32 SetConcat

VxD_DATA_ENDS

VxD_CODE_SEG

;VxD-obligatorische Steuer-Prozedur
BeginProc VDONGLE_Control_Proc
	Debug_Out "VDONGLE: ControlProc"
	Control_Dispatch Device_Init, VDONGLE_Init
	Control_Dispatch System_Exit, VDONGLE_Exit
	Control_Dispatch Init_Complete, VDONGLE_Init2
	Control_Dispatch 1bh, VDONGLE_Dynamic_Init
	Control_Dispatch 1ch, VDONGLE_Dynamic_Exit
	clc
	ret
EndProc VDONGLE_Control_Proc

BeginProc VDONGLE_Exit
	mov	eax,[Semaphore]
	VMMCall	Destroy_Semaphore
	call	UnEditLpt	;Edition zurücknehmen
	stc
	ret
EndProc VDONGLE_Exit

BeginProc VDONGLE_Dynamic_Exit
	call	ClearHeap
	call	VDONGLE_Exit	;Semaphore verwerfen
	mov	edx,[PortBase]
	LD	ecx,3
@@l:	VMMCall	00010116h	;Remove_IO_Handler
	inc	edx
	loopd	@@l
	LD	eax,21h
	lea	esi,NewInt21
	VMMjmp	00010118h	;Unhook_V86_Int_Chain
EndProc VDONGLE_Dynamic_Exit

EnableTrappings proc
	mov	edx,[PortBase]
	LD	ecx,3
@@l:	VMMCall	Enable_Global_Trapping
	inc	edx
	loop	@@l
	ret
EnableTrappings endp

;Ein Tick aus dem Lautsprecher ausgeben...
MakeTick proc
	cmp	[TickSound],0	;eingeschaltet?
	jz	@@e		;nein, schweigen!
	push	eax
	pushf
	 BTST	[TickSound],bit 1 ;LAUT eingeschaltet?
	 cli
	 jz	@@1		;nein, schweigen!
	 mov	al,10100000b	;Zähler 2 (Speaker) auf Modus 0 (Intr am Ende)
	 out	43h,al
	 mov	al,2		;512, rund 1/2 Millisekunde
	 out	42h,al		;Puls definierter Länge ausgeben
@@1:	 in	al,61h
	 or	al,3
	 out	61h,al		;und hörbar machen!
	popf
	pop	eax
@@e:	ret
MakeTick endp

EndTick proc
	cmp	[TickSound],1	;eingeschaltet (nicht LAUT?)
	jne	@@e		;nein, nichts tun!
	push	eax
	pushf
	 cli
	 in	al,61h
	 and	al,not 3
	 out	61h,al		;und AUS!
	popf
	pop	eax
@@e:	ret
EndTick endp

;Callback rufen; die richtige VM ist bereits aktiv
;AX wird ins Callback hinein gegeben; AL wird als Ergebnis durchgereicht
CallCallback proc
	cmp	[CallbackAdr],0
	jz	@@e			;hier: Notbremse!
	Push_Client_State Uses_EDI
	VMMCall	Begin_Nest_Exec
	mov	[ebp.Client_AX],ax	;AX zum Aufruf setzen!
	mov	eax,[DosTime]
	mov	[ebp.Client_EDX],eax
	movzx	edx,word ptr [CallbackAdr]
	mov	cx,word ptr [CallbackAdr+2]
	VMMCall	Simulate_Far_Call
	VMMCall	Resume_Exec
	mov	eax,[ebp.Client_EDX]
	mov	[DosTime],eax
	mov	al,[ebp.Client_AL]	;AL als Ergebnis liefern
	VMMCall	End_Nest_Exec
	Pop_Client_State Uses_ESI
@@e:	ret
CallCallback endp

;Callback rufen und wartenden Prozeß aufwecken
Call3back proc
	mov	eax,edx
	call	CallCallback
	mov	[RetVal],al		;Ergebnis sichern
	mov	eax,[Semaphore]
	VMMCall	Signal_Semaphore
	ret
Call3back endp

;Direktaufruf oder via Priority_VM_Event...
CallWindows proc
	cmp	[CallbackAdr],0
	jz	@@e			;Ohne Callback kein Windows-Ruf
	VMMCall	Test_Sys_VM_Handle	;Aktuelle VM ist System-VM?
	jz	CallCallback		;Ja - Direkt-Aufruf!
;Callback vermitteln und einschlafen
	mov	edx,eax
	mov	eax,Time_Critical_Boost
	VMMCall	Get_Sys_VM_Handle
	xor	ecx,ecx
	lea	esi,Call3back
	VMMCall	Call_Priority_VM_Event
	mov	eax,[Semaphore]
	LD	ecx,Block_Svc_Ints or Block_Enable_Ints
	VMMCall	Wait_Semaphore
	mov	al,[RetVal]
@@e:	ret
CallWindows endp

PutInList proc
	mov	esi,[BufferAdr]
	cmp	esi,1
	jc	@@e		;Kein Puffer - raus!
	movzx	edx,word ptr [esi]	;Aktuelle String-Länge
	cmp	[ConcatEquals],0
	jz	@@1		;Immer eintragen!
	or	edx,edx		;Puffer leer?
	jz	@@1		;Dann auch immer eintragen!
	cmp	[esi+edx*2],ax	;Schon vorhanden?
	jz	@@e		;ja, ignorieren - und CY=0
@@1:	inc	edx
	mov	[esi+edx*2],ax	;Befehl eintragen
	bt	eax,10		;IN-Befehl?
	jc	@@e		;Windows rufen
	mov	[esi],dx	;String ist länger geworden...
	cmp	edx,[BufferLen]	;Puffer voll - CY=0 ?
	cmc			;CY umdrehen
@@e:	ret
PutInList endp

;------------------------------------------------------------------------------
;   ENTRY:
;       EBX = VM Handle.
;       ECX = Type of I/O
;       EDX = Port number
;       EBP = Pointer to client register structure
;   EXIT:
;       EAX = data input or output depending on type of I/O
;------------------------------------------------------------------------------

BeginProc IOCallback High_Freq
	push	esi edi
	call	MakeTick
	Dispatch_Byte_IO Fall_Through, @@O
	sub	edx,[PortBase]
	mov	al,[DataMirror+edx]
	cmp	[EmuMode],0
	jnz	@@1		;Emulation EIN, vom Spiegel lesen!
	push	edx
	 add	edx,[PhysBase]
	 in	al,dx		;Emu AUS, vom Dongle lesen!
	pop	edx
@@1:	mov	ah,dl
	or	ah,4		;Bit 2 als IN-Kennung setzen
	jmp	@@5
@@O:
	sub	edx,[PortBase]
	cmp	dl,1		;Ausnahme: Eingabeport?
	je	@@4		;Nicht beschreiben
	mov	[DataMirror+edx],al	;Mirror beschreiben
@@4:	cmp	[EmuMode],0
	jnz	@@3		;Emulation EIN, zum Spiegel schreiben!
	push	edx
	 add	edx,[PhysBase]
	 out	dx,al		;Emu AUS, zum Dongle schreiben!
	pop	edx
@@3:	mov	ah,dl
@@5:	call	PutInList	;liefert CY, wenn CallWindows erforderlich
	jnc	@@e
	call	CallWindows	;Callback rufen (falls vorhanden)
	call	ClearBuffer
@@e:	call	EndTick
	clc
	pop	edi esi
	ret
EndProc IOCallback

BeginProc NewInt21, High_Freq
	VMMCall	Test_Sys_VM_Handle
	jnz	@@cont		;Weiter, wenn Ruf aus DOS-Box
	mov	eax,[ebp.Client_EAX]
	cmp	ah,2Ch
	jnz	@@cont
	cmp	al,'#'		;Spezielle Kennung
	jz	@@cont
	cmp	[HookSwitch],1
	jnz	@@cont		;Ausgeschaltet!
	call	MakeTick
	push	[ebp.Client_CX]	;hh:mm
	push	[ebp.Client_DX]	;ss:hs
	pop	[DosTime]	;im LongInt zusammenfassen
	mov	al,1
	BSET	[HookSwitch],bit 7	;Reenter-Flag
	call	CallWindows
	BRES	[HookSwitch],bit 7	;Reenter-Flag
	call	EndTick
	or	al,al
	jnz	@@cont
	push	[DosTime]
	pop	[ebp.Client_DX]	;ss:hs
	pop	[ebp.Client_CX]	;hh:mm
	ret			;CY=0
@@cont:
	stc
	ret
EndProc NewInt21

GetVer proc
	mov	[ebp.Client_AX],VDONGLE_Ver
	ret
GetVer endp

SetCallback proc
	push	[ebp.Client_ES]
	push	[ebp.Client_BX]
rcb1:	pop	[CallbackAdr]
	ret
SetCallback endp

RemoveCallback proc
	call	ClearHeap
	xor	eax,eax		;Ausnullen
	mov	[BufferAdr],eax
	mov	[BufferLen],eax
	push	0
	jmp	rcb1
RemoveCallback endp

SetEmu:
	lea	esi,EmuMode
	jmp	GetSetSwitch
SetTick:
	lea	esi,TickSound
	jmp	GetSetSwitch
Hook212C:
	lea	esi,HookSwitch
	jmp	GetSetSwitch
SetConcat:
	lea	esi,ConcatEquals
GetSetSwitch proc
	mov	al,[ebp.Client_AL]
	cmp	al,0FFh
	je	@@1
	xchg	[esi],al	;Alte Schalterstellung liefern!
	jmp	@@2
@@1:	mov	al,[esi]
@@2:	mov	[ebp.Client_AL],al
	ret
GetSetSwitch endp

TrapControl proc
	mov	al,[ebp.Client_AL]
	or	al,al
	jnz	EnableTrappings
DisableTrappings:
	mov	edx,[PortBase]
	LD	ecx,3
@@l:	VMMCall	Disable_Global_Trapping
	inc	edx
	loop	@@l
	ret
TrapControl endp

BeginProc SetBuffer
	movzx	ecx,[ebp.Client_CX]
	cmp	ecx,2		;Mindestens 2 WORDs?
	jc	@@z
	cmp	[ebp.Client_ES],0	;Selektor Null?
	jnz	@@2
	push	ecx
	 add	ecx,ecx		;in Bytes!
	 call	ReAllocHeap
	 mov	[ebp.Client_AX],ax	;Selektor in AX zurückliefern
	pop	ecx
	jmp	@@3
@@2:	push	ecx
	 call	ClearHeap
	pop	ecx
	Client_Ptr_Flat eax,ES,BX
	cmp	eax,-1
	jnz	@@1
@@z:	xor	eax,eax
	xor	ecx,ecx
@@1:	mov	[BufferAdr],eax
@@3:	dec	ecx		;maximale Pascal-String-Länge
	mov	[BufferLen],ecx	;Adresse und Länge eintragen
EndProc SetBuffer

BeginProc ClearBuffer
	mov	esi,[BufferAdr]
	or	esi,esi
	jz	@@e
	mov	word ptr [esi],0
@@e:	ret
EndProc ClearBuffer

ClearHeap:
	xor	ecx,ecx
ReAllocHeap proc
;PE: ECX=neue Heap-Größe (0=Heap aufräumen, falls vorhanden)
;    [BufferAdr]=Heap-Adresse (nur wenn HeapSel<>0, sonst Annahme: kein Heap)
;    [HeapSel]=alter Heap-Selektor
;PA: [BufferAdr]=neue Heap-Adresse
;    [HeapSel]=AX=neuer Heap-Selektor
	cmp	[HeapSel],0
	jnz	@@realloc
	jecxz	@@e2		;Nichts tun! (Dreifachsprung wegen Codegröße)
	push	ecx		;_HeapAllocate zerstört ECX!!
	 VMMCall _HeapAllocate, <ecx,0>
	pop	ecx
	or	eax,eax
@@e2:	jz	@@e1		;Fehler!
	mov	[BufferAdr],eax
	dec	ecx		;Größe-->Limit
	VMMCall _BuildDescriptorDWORDS, <eax,ecx,RW_Data_Type,0,0>
;Selektor besorgen
	VMMCall	_Allocate_GDT_Selector, <edx,eax,0>
	mov	[HeapSel],ax
	or	eax,eax
	jnz	@@e1		;Okay!
	jmp	@@freemem
@@realloc:
	jecxz	@@free
	push	ecx		;_HeapReAllocate vermutlich auch!
	 VMMCall _HeapReAllocate, <[BufferAdr],ecx,HeapNoCopy>
	pop	ecx
	mov	[BufferAdr],eax	;Neue lineare Adresse!
	or	eax,eax
	jz	@@free
	dec	ecx
	VMMCall _BuildDescriptorDWORDS, <eax,ecx,RW_Data_Type,0,0>
	movzx	ecx,[HeapSel]
	VMMCall	_SetDescriptor, <ecx,ebx,edx,eax,0>
@@e1:	jmp	@@e		;Okay!
@@free:
	xor	eax,eax
	xchg	[HeapSel],ax
	VMMCall	_Free_GDT_Selector, <eax,0>
@@freemem:
	xor	eax,eax
	xchg	[BufferAdr],eax
	or	eax,eax
	jz	@@e
	VMMCall	_HeapFree, <eax,0>
@@e:	mov	ax,[HeapSel]
	ret
ReAllocHeap endp

BeginProc VDONGLE_API
	Debug_Out "VDONGLE: API Call"
	VMMCall	Test_Sys_VM_Handle
	jnz	@@e		;Anforderungen aus DPMI-Programmen verweigern
	movzx	eax,[ebp.Client_AH]
	cmp	eax,8
	ja	@@err
	call	[uptab+eax*4]
@@ok:	BRES	[ebp.Client_Flags],CF_Mask
	jmp	@@e
@@err:	BSET	[ebp.Client_Flags],CF_Mask
@@e:	ret
EndProc VDONGLE_API

UnEditLpt proc	;Änderungen in der BIOS-Tabelle zurücknehmen
	movzx	esi,[ChangedPort]
	or	esi,esi
	jz	@@e
	mov	dx,[WasAddr]
	mov	[esi],dx
@@e:	ret
UnEditLpt endp

VxD_CODE_ENDS

;****************************************
;*** Initialisierungs-Daten und -Code ***
;****************************************
VxD_IDATA_SEG
Port$	db	"VDonglePort",0
Phys$	db	"VDonglePhys",0
Tick$	db	"VDongleTick",0
Trap$	db	"VDongleTrap",0
VxD_IDATA_ENDS

VxD_ICODE_SEG
GetEntry proc
;PE: EDI=String, ESI=0 (!!)
;PA: EAX=Portadresse
;    CY=1: kein oder ungültiger Eintrag
	VMMCall	Get_Profile_String
	jc	@@e		;Nicht gefunden? - Nicht auswerten!
	mov	eax,[edx]
	and	eax,00DFDFDFh	;Großbuchstaben!
	cmp	eax,'TPL'	;"LPT"?
	je	@@2		;ja
	VMMCall	Convert_Hex_String
	cmp	byte ptr [edx],1	;korrekt null-terminiert?
	cmc
	jmp	@@e
@@2:
	mov	al,[edx+3]	;Nummer...
	sub	al,'1'
	cmp	al,4
	cmc
	jc	@@e
	movzx	eax,al
	movzx	eax,word ptr [408h+eax*2]	;LPT-Adresse...
	cmp	eax,1		;Null? (Wäre ein Fehler!)
@@e:	ret
GetEntry endp

EditLpt proc
;PE: [PortBase]=Portadresse
;PA: diese Portadresse wird global (??) in 408h eingetragen.
;    Falls keine Adresse frei ist (OS/2-Szenario), wird LPT3 überbügelt.
;    Falls die angegebene Adresse bereits vorhanden ist, wird nichts getan.
;    Die Änderungen werden für die Rücknahme beim Beenden von Windows gemerkt.
	mov	esi,408h
	xor	eax,eax
	mov	edx,[PortBase]
	LD	ecx,3
@@l:	lodsw			;1. freie LPT suchen oder letzte überbügeln
	cmp	eax,edx
	je	@@e		;kein Patch erforderlich!
	cmp	eax,[PhysBase]
	je	@@1		;Portadresse ersetzen!
	or	eax,eax
	loopnz	@@l
@@1:	dec	esi
	dec	esi
	xchg	[esi],dx	;eintragen
	mov	[ChangedPort],si	;merken
	mov	[WasAddr],dx
@@e:	ret
EditLpt endp

InitPhysBase proc
	movzx	eax,word ptr ds:[408h]	;LPT1-Portadresse
	or	eax,eax
	jz	@@e
	mov	[PhysBase],eax
	mov	[PortBase],eax
@@e:	ret
InitPhysBase endp

;I/O-Handler installieren (3fache Ausführung)
Try_Install_EDX_IO:
	mov	[PortBase],edx
Try_Install_IO_Handlers proc
	lea	esi,IOCallback
	mov	edx,[PortBase]
	LD	ecx,3		;mit 3 Bytes eine 3 in ECX!
@@l:	VMMcall Install_IO_Handler
	jc	@@e		;Schwerer Fehler
	inc	edx		;Nächste Portadresse
	loopd	@@l
@@e:	ret
Try_Install_IO_Handlers endp

BeginProc VDONGLE_Init
	Trace_Out "VDONGLE: Device_Init"
	call	InitPhysBase
;Knackgeräusch ein/aus?
	xor	esi,esi
	lea	edi,Tick$
	VMMCall	Get_Profile_Boolean
	jbe	@@i4		;erschlägt CY und Z gleichermaßen
	MAX	al,2
	mov	[TickSound],al	;Bool-Wert ablegen
@@i4:	;Portadresse(n) besorgen
	lea	edi,Port$
	call	GetEntry
	jc	@@i1		;Nicht gefunden? - Nicht auswerten!
	mov	[PortBase],eax
	mov	[PhysBase],eax
@@i1:	lea	edi,Phys$
	call	GetEntry
	jc	@@i2		;Nicht gefunden? - Nicht auswerten!
	mov	[PhysBase],eax
@@i2:	jmp	@@i3
VDONGLE_Dynamic_Init:		;Einsprung Dynamische Initialisierung
	VMMCall	Get_VMM_Version
	cmp	ah,4		;Windows95 erforderlich!!
	jc	@@e
	call	InitPhysBase
@@i3:	call	Try_install_IO_Handlers
	jnc	@@i5		;Okay!
	mov	dx,37Ch		;Versuch auf üblicherweise freier Adresse
	call	Try_Install_EDX_IO
	jnc	@@i6		;Okay!
	mov	dx,27Ch		;Noch ein letzter Versuch...
	call	Try_Install_EDX_IO
	jnc	@@e		;Fehler (Achselzucken)
@@i6:	call	EditLpt
@@i5:	call	DisableTrappings
;Semaphor installieren
	xor	ecx,ecx
	VMMCall	Create_Semaphore;ECX ist bereits 0!
	jc	@@e		;Fehler - raus!
	mov	[Semaphore],eax	;Handle speichern
	LD	eax,21h
	lea	esi,NewInt21
	VMMCall	Hook_V86_Int_Chain
	jc	VDONGLE_Exit
@@e:	ret
EndProc VDONGLE_Init

VDongle_Init2 proc
	pushad
	xor	esi,esi
	lea	edi,Trap$
	VMMCall	Get_Profile_Boolean
	jbe	@@e
	or	al,al
	jz	@@e
	call	EnableTrappings
@@e:	popad
	clc
	ret
VDongle_Init2 endp

VxD_ICODE_ENDS
	END

Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded