[SOFT386]
[reactos.git] / lib / soft386 / common.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: 386/486 CPU Emulation Library
4 * FILE: common.c
5 * PURPOSE: Common functions used internally by Soft386.
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "common.h"
12
13 /* PRIVATE FUNCTIONS **********************************************************/
14
15 static
16 inline
17 INT
18 Soft386GetCurrentPrivLevel(PSOFT386_STATE State)
19 {
20 return GET_SEGMENT_RPL(State->SegmentRegs[SOFT386_REG_CS].Selector);
21 }
22
23 /* PUBLIC FUNCTIONS ***********************************************************/
24
25 inline
26 BOOLEAN
27 Soft386ReadMemory(PSOFT386_STATE State,
28 INT SegmentReg,
29 ULONG Offset,
30 BOOLEAN InstFetch,
31 PVOID Buffer,
32 ULONG Size)
33 {
34 ULONG LinearAddress;
35 PSOFT386_SEG_REG CachedDescriptor;
36
37 ASSERT(SegmentReg < SOFT386_NUM_SEG_REGS);
38
39 /* Get the cached descriptor */
40 CachedDescriptor = &State->SegmentRegs[SegmentReg];
41
42 if ((Offset + Size) >= CachedDescriptor->Limit)
43 {
44 /* Read beyond limit */
45 // TODO: Generate exception #GP
46
47 return FALSE;
48 }
49
50 /* Check for protected mode */
51 if (State->ControlRegisters[0] & SOFT386_CR0_PE)
52 {
53 /* Privilege checks */
54
55 if (!CachedDescriptor->Present)
56 {
57 // TODO: Generate exception #NP
58 return FALSE;
59 }
60
61 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
62 {
63 // TODO: Generate exception #GP
64 return FALSE;
65 }
66
67 if (InstFetch)
68 {
69 if (!CachedDescriptor->Executable)
70 {
71 /* Data segment not executable */
72
73 // TODO: Generate exception #GP
74 return FALSE;
75 }
76 }
77 else
78 {
79 if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
80 {
81 /* Code segment not readable */
82
83 // TODO: Generate exception #GP
84 return FALSE;
85 }
86 }
87 }
88
89 /* Find the linear address */
90 LinearAddress = CachedDescriptor->Base + Offset;
91
92 // TODO: Paging support!
93
94 /* Did the host provide a memory hook? */
95 if (State->MemReadCallback)
96 {
97 /* Yes, call the host */
98 State->MemReadCallback(State, LinearAddress, Buffer, Size);
99 }
100 else
101 {
102 /* Read the memory directly */
103 RtlMoveMemory(Buffer, (LPVOID)LinearAddress, Size);
104 }
105
106 return TRUE;
107 }
108
109 inline
110 BOOLEAN
111 Soft386WriteMemory(PSOFT386_STATE State,
112 INT SegmentReg,
113 ULONG Offset,
114 PVOID Buffer,
115 ULONG Size)
116 {
117 ULONG LinearAddress;
118 PSOFT386_SEG_REG CachedDescriptor;
119
120 ASSERT(SegmentReg < SOFT386_NUM_SEG_REGS);
121
122 /* Get the cached descriptor */
123 CachedDescriptor = &State->SegmentRegs[SegmentReg];
124
125 if ((Offset + Size) >= CachedDescriptor->Limit)
126 {
127 /* Write beyond limit */
128 // TODO: Generate exception #GP
129
130 return FALSE;
131 }
132
133 /* Check for protected mode */
134 if (State->ControlRegisters[0] & SOFT386_CR0_PE)
135 {
136 /* Privilege checks */
137
138 if (!CachedDescriptor->Present)
139 {
140 // TODO: Generate exception #NP
141 return FALSE;
142 }
143
144 if (GET_SEGMENT_RPL(CachedDescriptor->Selector) > CachedDescriptor->Dpl)
145 {
146 // TODO: Generate exception #GP
147 return FALSE;
148 }
149
150 if (CachedDescriptor->Executable)
151 {
152 /* Code segment not writable */
153
154 // TODO: Generate exception #GP
155 return FALSE;
156 }
157 else if (!CachedDescriptor->ReadWrite)
158 {
159 /* Data segment not writeable */
160
161 // TODO: Generate exception #GP
162 return FALSE;
163 }
164 }
165
166 /* Find the linear address */
167 LinearAddress = CachedDescriptor->Base + Offset;
168
169 // TODO: Paging support!
170
171 /* Did the host provide a memory hook? */
172 if (State->MemWriteCallback)
173 {
174 /* Yes, call the host */
175 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
176 }
177 else
178 {
179 /* Write the memory directly */
180 RtlMoveMemory((LPVOID)LinearAddress, Buffer, Size);
181 }
182
183 return TRUE;
184 }
185
186 inline
187 BOOLEAN
188 Soft386StackPush(PSOFT386_STATE State, ULONG Value)
189 {
190 BOOLEAN Size = State->SegmentRegs[SOFT386_REG_SS].Size;
191
192 // TODO: Handle OPSIZE prefix.
193
194 if (Size)
195 {
196 /* 32-bit size */
197
198 /* Check if ESP is between 1 and 3 */
199 if (State->GeneralRegs[SOFT386_REG_ESP].Long >= 1
200 && State->GeneralRegs[SOFT386_REG_ESP].Long <= 3)
201 {
202 // TODO: Exception #SS
203 return FALSE;
204 }
205
206 /* Subtract ESP by 4 */
207 State->GeneralRegs[SOFT386_REG_ESP].Long -= 4;
208
209 /* Store the value in SS:ESP */
210 return Soft386WriteMemory(State,
211 SOFT386_REG_SS,
212 State->GeneralRegs[SOFT386_REG_ESP].Long,
213 &Value,
214 sizeof(ULONG));
215 }
216 else
217 {
218 /* 16-bit size */
219 USHORT ShortValue = LOWORD(Value);
220
221 /* Check if SP is 1 */
222 if (State->GeneralRegs[SOFT386_REG_ESP].Long == 1)
223 {
224 // TODO: Exception #SS
225 return FALSE;
226 }
227
228 /* Subtract SP by 2 */
229 State->GeneralRegs[SOFT386_REG_ESP].LowWord -= 2;
230
231 /* Store the value in SS:SP */
232 return Soft386WriteMemory(State,
233 SOFT386_REG_SS,
234 State->GeneralRegs[SOFT386_REG_ESP].LowWord,
235 &ShortValue,
236 sizeof(USHORT));
237 }
238 }
239
240 inline
241 BOOLEAN
242 Soft386StackPop(PSOFT386_STATE State, PULONG Value)
243 {
244 ULONG LongValue;
245 USHORT ShortValue;
246 BOOLEAN Size = State->SegmentRegs[SOFT386_REG_SS].Size;
247
248 // TODO: Handle OPSIZE prefix.
249
250 if (Size)
251 {
252 /* 32-bit size */
253
254 /* Check if ESP is 0xFFFFFFFF */
255 if (State->GeneralRegs[SOFT386_REG_ESP].Long == 0xFFFFFFFF)
256 {
257 // TODO: Exception #SS
258 return FALSE;
259 }
260
261 /* Read the value from SS:ESP */
262 if (!Soft386ReadMemory(State,
263 SOFT386_REG_SS,
264 State->GeneralRegs[SOFT386_REG_ESP].Long,
265 FALSE,
266 &LongValue,
267 sizeof(LongValue)))
268 {
269 /* An exception occurred */
270 return FALSE;
271 }
272
273 /* Increment ESP by 4 */
274 State->GeneralRegs[SOFT386_REG_ESP].Long += 4;
275
276 /* Store the value in the result */
277 *Value = LongValue;
278 }
279 else
280 {
281 /* 16-bit size */
282
283 /* Check if SP is 0xFFFF */
284 if (State->GeneralRegs[SOFT386_REG_ESP].LowWord == 0xFFFF)
285 {
286 // TODO: Exception #SS
287 return FALSE;
288 }
289
290 /* Read the value from SS:SP */
291 if (!Soft386ReadMemory(State,
292 SOFT386_REG_SS,
293 State->GeneralRegs[SOFT386_REG_ESP].LowWord,
294 FALSE,
295 &ShortValue,
296 sizeof(ShortValue)))
297 {
298 /* An exception occurred */
299 return FALSE;
300 }
301
302 /* Increment SP by 2 */
303 State->GeneralRegs[SOFT386_REG_ESP].Long += 2;
304
305 /* Store the value in the result */
306 *Value = ShortValue;
307 }
308
309 return TRUE;
310 }
311
312 inline
313 BOOLEAN
314 Soft386LoadSegment(PSOFT386_STATE State, INT Segment, WORD Selector)
315 {
316 PSOFT386_SEG_REG CachedDescriptor;
317 SOFT386_GDT_ENTRY GdtEntry;
318
319 ASSERT(Segment < SOFT386_NUM_SEG_REGS);
320
321 /* Get the cached descriptor */
322 CachedDescriptor = &State->SegmentRegs[Segment];
323
324 /* Check for protected mode */
325 if (State->ControlRegisters[SOFT386_REG_CR0] & SOFT386_CR0_PE)
326 {
327 /* Make sure the GDT contains the entry */
328 if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1))
329 {
330 // TODO: Exception #GP
331 return FALSE;
332 }
333
334 /* Read the GDT */
335 // FIXME: This code is only correct when paging is disabled!!!
336 if (State->MemReadCallback)
337 {
338 State->MemReadCallback(State,
339 State->Gdtr.Address
340 + GET_SEGMENT_INDEX(Selector),
341 &GdtEntry,
342 sizeof(GdtEntry));
343 }
344 else
345 {
346 RtlMoveMemory(&GdtEntry,
347 (LPVOID)(State->Gdtr.Address
348 + GET_SEGMENT_INDEX(Selector)),
349 sizeof(GdtEntry));
350 }
351
352 /* Check if we are loading SS */
353 if (Segment == SOFT386_REG_SS)
354 {
355 if (GET_SEGMENT_INDEX(Selector) == 0)
356 {
357 // TODO: Exception #GP
358 return FALSE;
359 }
360
361 if (GdtEntry.Executable || !GdtEntry.ReadWrite)
362 {
363 // TODO: Exception #GP
364 return FALSE;
365 }
366
367 if ((GET_SEGMENT_RPL(Selector) != Soft386GetCurrentPrivLevel(State))
368 || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
369 {
370 // TODO: Exception #GP
371 return FALSE;
372 }
373
374 if (!GdtEntry.Present)
375 {
376 // TODO: Exception #SS
377 return FALSE;
378 }
379 }
380 else
381 {
382 if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
383 && (Soft386GetCurrentPrivLevel(State) > GdtEntry.Dpl))
384 {
385 // TODO: Exception #GP
386 return FALSE;
387 }
388
389 if (!GdtEntry.Present)
390 {
391 // TODO: Exception #NP
392 return FALSE;
393 }
394 }
395
396 /* Update the cache entry */
397 CachedDescriptor->Selector = Selector;
398 CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseHigh << 24);
399 CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
400 CachedDescriptor->Accessed = GdtEntry.Accessed;
401 CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
402 CachedDescriptor->DirConf = GdtEntry.DirConf;
403 CachedDescriptor->Executable = GdtEntry.Executable;
404 CachedDescriptor->SystemType = GdtEntry.SystemType;
405 CachedDescriptor->Dpl = GdtEntry.Dpl;
406 CachedDescriptor->Present = GdtEntry.Present;
407 CachedDescriptor->Size = GdtEntry.Size;
408
409 /* Check for page granularity */
410 if (GdtEntry.Granularity) CachedDescriptor->Limit <<= 12;
411 }
412 else
413 {
414 /* Update the selector and base */
415 CachedDescriptor->Selector = Selector;
416 CachedDescriptor->Base = Selector << 4;
417 }
418
419 return TRUE;
420 }
421
422 /* EOF */