[NTVDM]
[reactos.git] / subsystems / ntvdm / vddsup.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: vddsup.c
5 * PURPOSE: Virtual DOS Drivers (VDD) Support
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "vddsup.h"
15
16 #include "bop.h"
17 #include "registers.h"
18
19 typedef VOID (WINAPI *VDD_PROC)(VOID);
20
21 typedef struct _VDD_MODULE
22 {
23 HMODULE hDll;
24 VDD_PROC DispatchRoutine;
25 } VDD_MODULE, *PVDD_MODULE;
26
27 /* BOP Identifiers */
28 #define BOP_3RDPARTY 0x58 // 3rd-party VDD BOP
29
30 /* PRIVATE VARIABLES **********************************************************/
31
32 #define MAX_VDD_MODULES 0xFF + 1
33 VDD_MODULE VDDList[MAX_VDD_MODULES] = {{NULL}}; // TODO: Maybe use a linked list.
34 // But the number of elements must be <= MAXUSHORT
35
36 /* PRIVATE FUNCTIONS **********************************************************/
37
38 USHORT GetNextFreeVDDEntry(VOID)
39 {
40 USHORT Entry = MAX_VDD_MODULES;
41 for (Entry = 0; Entry < sizeof(VDDList)/sizeof(VDDList[0]); ++Entry)
42 {
43 if (VDDList[Entry].hDll == NULL) break;
44 }
45 return Entry;
46 }
47
48 VOID WINAPI ThirdPartyVDDBop(LPWORD Stack)
49 {
50 /* Get the Function Number and skip it */
51 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
52 setIP(getIP() + 1);
53
54 switch (FuncNum)
55 {
56 /* RegisterModule */
57 case 0:
58 {
59 BOOL Success = TRUE;
60 WORD RetVal = 0;
61 WORD Entry = 0;
62 LPCSTR DllName = NULL,
63 InitRoutineName = NULL,
64 DispatchRoutineName = NULL;
65 HMODULE hDll = NULL;
66 VDD_PROC InitRoutine = NULL,
67 DispatchRoutine = NULL;
68
69 DPRINT1("RegisterModule() called\n");
70
71 /* Clear the Carry Flag (no error happened so far) */
72 setCF(0);
73
74 /* Retrieve the VDD name in DS:SI */
75 DllName = SEG_OFF_TO_PTR(getDS(), getSI());
76
77 /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */
78 if (getES() != 0 || getDI() != 0)
79 InitRoutineName = SEG_OFF_TO_PTR(getES(), getDI());
80
81 /* Retrieve the dispatch routine API name in DS:BX */
82 DispatchRoutineName = SEG_OFF_TO_PTR(getDS(), getBX());
83
84 DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n",
85 (DllName ? DllName : "n/a"),
86 (InitRoutineName ? InitRoutineName : "n/a"),
87 (DispatchRoutineName ? DispatchRoutineName : "n/a"));
88
89 /* Load the VDD DLL */
90 hDll = LoadLibraryA(DllName);
91 if (hDll == NULL)
92 {
93 DWORD LastError = GetLastError();
94 Success = FALSE;
95
96 if (LastError == ERROR_NOT_ENOUGH_MEMORY)
97 {
98 DPRINT1("Not enough memory to load DLL '%s'\n", DllName);
99 RetVal = 4;
100 goto Quit;
101 }
102 else
103 {
104 DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError);
105 RetVal = 1;
106 goto Quit;
107 }
108 }
109
110 /* Load the initialization routine if needed */
111 if (InitRoutineName)
112 {
113 InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName);
114 if (InitRoutine == NULL)
115 {
116 DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName);
117 Success = FALSE;
118 RetVal = 3;
119 goto Quit;
120 }
121 }
122
123 /* Load the dispatch routine */
124 DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName);
125 if (DispatchRoutine == NULL)
126 {
127 DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName);
128 Success = FALSE;
129 RetVal = 2;
130 goto Quit;
131 }
132
133 /* If we arrived there, that means everything is OK */
134
135 /* Register the VDD DLL */
136 Entry = GetNextFreeVDDEntry();
137 if (Entry == MAX_VDD_MODULES)
138 {
139 DPRINT1("Failed to create a new VDD module entry\n");
140 Success = FALSE;
141 RetVal = 4;
142 goto Quit;
143 }
144 VDDList[Entry].hDll = hDll;
145 VDDList[Entry].DispatchRoutine = DispatchRoutine;
146
147 /* Call the initialization routine if needed */
148 if (InitRoutine) InitRoutine();
149
150 /* We succeeded */
151 Success = TRUE;
152 RetVal = Entry;
153
154 Quit:
155 if (!Success)
156 {
157 /* Unload the VDD DLL */
158 if (hDll) FreeLibrary(hDll);
159
160 /* Set the Carry Flag to indicate that an error happened */
161 setCF(1);
162 }
163 // else
164 // {
165 // /* Clear the Carry Flag (success) */
166 // setCF(0);
167 // }
168 setAX(RetVal);
169 break;
170 }
171
172 /* UnRegisterModule */
173 case 1:
174 {
175 WORD Entry = getAX();
176
177 DPRINT1("UnRegisterModule() called\n");
178
179 /* Sanity checks */
180 if (Entry > MAX_VDD_MODULES || VDDList[Entry].hDll == NULL)
181 {
182 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
183 /* Stop the VDM */
184 VdmRunning = FALSE;
185 return;
186 }
187
188 /* Unregister the VDD DLL */
189 FreeLibrary(VDDList[Entry].hDll);
190 VDDList[Entry].hDll = NULL;
191 VDDList[Entry].DispatchRoutine = NULL;
192 break;
193 }
194
195 /* DispatchCall */
196 case 2:
197 {
198 WORD Entry = getAX();
199
200 DPRINT1("DispatchCall() called\n");
201
202 /* Sanity checks */
203 if (Entry > MAX_VDD_MODULES ||
204 VDDList[Entry].hDll == NULL ||
205 VDDList[Entry].DispatchRoutine == NULL)
206 {
207 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
208 /* Stop the VDM */
209 VdmRunning = FALSE;
210 return;
211 }
212
213 /* Call the dispatch routine */
214 VDDList[Entry].DispatchRoutine();
215 break;
216 }
217
218 default:
219 {
220 DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum);
221 setCF(1);
222 break;
223 }
224 }
225 }
226
227 /* PUBLIC FUNCTIONS ***********************************************************/
228
229 VOID VDDSupInitialize(VOID)
230 {
231 /* Register the 3rd-party VDD BOP Handler */
232 RegisterBop(BOP_3RDPARTY, ThirdPartyVDDBop);
233 }
234
235 /* EOF */