instr.c 29.8 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Andreas Mohr's avatar
Andreas Mohr committed
2
 * Emulation of privileged instructions
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1995 Alexandre Julliard
5 6
 * Copyright 2005 Ivan Leo Puoti
 * Copyright 2005 Laurent Pinchart
7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
23 24 25
#include "config.h"
#include "wine/port.h"

26 27 28
#include <stdarg.h>

#include "ntstatus.h"
29
#include "windef.h"
30
#include "winbase.h"
31
#include "wingdi.h"
32
#include "wine/winuser16.h"
33
#include "excpt.h"
34
#include "thread.h"
35
#include "wine/debug.h"
36
#include "kernel_private.h"
37
#include "kernel16_private.h"
38
#include "wine/exception.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
39

40 41
WINE_DEFAULT_DEBUG_CHANNEL(int);
WINE_DECLARE_DEBUG_CHANNEL(io);
42

43 44 45 46
/* macros to set parts of a DWORD */
#define SET_LOWORD(dw,val)  ((dw) = ((dw) & 0xffff0000) | LOWORD(val))
#define SET_LOBYTE(dw,val)  ((dw) = ((dw) & 0xffffff00) | LOBYTE(val))
#define ADD_LOWORD(dw,val)  ((dw) = ((dw) & 0xffff0000) | LOWORD((DWORD)(dw)+(val)))
47
#define ISV86(context)      ((context)->EFlags & 0x00020000)
48

Alexandre Julliard's avatar
Alexandre Julliard committed
49 50 51 52 53 54 55
inline static void add_stack( CONTEXT86 *context, int offset )
{
    if (ISV86(context) || !IS_SELECTOR_32BIT(context->SegSs))
        ADD_LOWORD( context->Esp, offset );
    else
        context->Esp += offset;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
56

Alexandre Julliard's avatar
Alexandre Julliard committed
57 58
inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long_addr )
{
59
    if (ISV86(context)) return (void *)((seg << 4) + LOWORD(off));
60
    if (wine_ldt_is_system(seg)) return (void *)off;
Alexandre Julliard's avatar
Alexandre Julliard committed
61
    if (!long_addr) off = LOWORD(off);
62
    return (char *) MapSL( MAKESEGPTR( seg, 0 ) ) + off;
Alexandre Julliard's avatar
Alexandre Julliard committed
63
}
64

Alexandre Julliard's avatar
Alexandre Julliard committed
65 66
inline static void *get_stack( CONTEXT86 *context )
{
67
    if (ISV86(context)) return (void *)((context->SegSs << 4) + LOWORD(context->Esp));
68
    return wine_ldt_get_ptr( context->SegSs, context->Esp );
Alexandre Julliard's avatar
Alexandre Julliard committed
69
}
Alexandre Julliard's avatar
Alexandre Julliard committed
70

Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
#include "pshpack1.h"
struct idtr
{
    WORD  limit;
    BYTE *base;
};
#include "poppack.h"

static LDT_ENTRY idt[256];

inline static struct idtr get_idtr(void)
{
    struct idtr ret;
#if defined(__i386__) && defined(__GNUC__)
    __asm__( "sidtl %0" : "=m" (ret) );
#else
    ret.base = (BYTE *)idt;
    ret.limit = sizeof(idt) - 1;
#endif
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
94 95 96 97
/***********************************************************************
 *           INSTR_ReplaceSelector
 *
 * Try to replace an invalid selector by a valid one.
Alexandre Julliard's avatar
Alexandre Julliard committed
98 99
 * The only selector where it is allowed to do "mov ax,40;mov es,ax"
 * is the so called 'bimodal' selector 0x40, which points to the BIOS
100
 * data segment. Used by (at least) Borland products (and programs compiled
Alexandre Julliard's avatar
Alexandre Julliard committed
101 102 103
 * using Borland products).
 *
 * See Undocumented Windows, Chapter 5, __0040.
Alexandre Julliard's avatar
Alexandre Julliard committed
104
 */
105
static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel )
Alexandre Julliard's avatar
Alexandre Julliard committed
106
{
Alexandre Julliard's avatar
Alexandre Julliard committed
107
    if (*sel == 0x40)
Alexandre Julliard's avatar
Alexandre Julliard committed
108
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
109 110
        static WORD sys_timer = 0;
        if (!sys_timer)
111 112 113 114 115
        {
            if (!winedos.BiosTick) load_winedos();
            if (winedos.BiosTick)
                sys_timer = CreateSystemTimer( 55, winedos.BiosTick );
        }
116
        *sel = DOSMEM_BiosDataSeg;
Alexandre Julliard's avatar
Alexandre Julliard committed
117
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
118
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
119
    return FALSE;  /* Can't replace selector, crashdump */
Alexandre Julliard's avatar
Alexandre Julliard committed
120 121 122
}


Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
/* store an operand into a register */
static void store_reg( CONTEXT86 *context, BYTE regmodrm, const BYTE *addr, int long_op )
{
    switch((regmodrm >> 3) & 7)
    {
    case 0:
        if (long_op) context->Eax = *(DWORD *)addr;
        else SET_LOWORD(context->Eax,*(WORD *)addr);
        break;
    case 1:
        if (long_op) context->Ecx = *(DWORD *)addr;
        else SET_LOWORD(context->Ecx,*(WORD *)addr);
        break;
    case 2:
        if (long_op) context->Edx = *(DWORD *)addr;
        else SET_LOWORD(context->Edx,*(WORD *)addr);
        break;
    case 3:
        if (long_op) context->Ebx = *(DWORD *)addr;
        else SET_LOWORD(context->Ebx,*(WORD *)addr);
        break;
    case 4:
        if (long_op) context->Esp = *(DWORD *)addr;
        else SET_LOWORD(context->Esp,*(WORD *)addr);
        break;
    case 5:
        if (long_op) context->Ebp = *(DWORD *)addr;
        else SET_LOWORD(context->Ebp,*(WORD *)addr);
        break;
    case 6:
        if (long_op) context->Esi = *(DWORD *)addr;
        else SET_LOWORD(context->Esi,*(WORD *)addr);
        break;
    case 7:
        if (long_op) context->Edi = *(DWORD *)addr;
        else SET_LOWORD(context->Edi,*(WORD *)addr);
        break;
    }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
163 164 165 166 167
/***********************************************************************
 *           INSTR_GetOperandAddr
 *
 * Return the address of an instruction operand (from the mod/rm byte).
 */
168
static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr,
Alexandre Julliard's avatar
Alexandre Julliard committed
169
                                   int long_addr, int segprefix, int *len )
Alexandre Julliard's avatar
Alexandre Julliard committed
170
{
171
    int mod, rm, base = 0, index = 0, ss = 0, seg = 0, off;
172
    LDT_ENTRY entry;
Alexandre Julliard's avatar
Alexandre Julliard committed
173 174 175 176 177 178 179 180 181 182 183 184 185

#define GET_VAL(val,type) \
    { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }

    *len = 0;
    GET_VAL( &mod, BYTE );
    rm = mod & 7;
    mod >>= 6;

    if (mod == 3)
    {
        switch(rm)
        {
186 187 188 189 190 191 192 193
        case 0: return (BYTE *)&context->Eax;
        case 1: return (BYTE *)&context->Ecx;
        case 2: return (BYTE *)&context->Edx;
        case 3: return (BYTE *)&context->Ebx;
        case 4: return (BYTE *)&context->Esp;
        case 5: return (BYTE *)&context->Ebp;
        case 6: return (BYTE *)&context->Esi;
        case 7: return (BYTE *)&context->Edi;
Alexandre Julliard's avatar
Alexandre Julliard committed
194 195 196 197 198 199 200 201 202 203 204 205 206
        }
    }

    if (long_addr)
    {
        if (rm == 4)
        {
            BYTE sib;
            GET_VAL( &sib, BYTE );
            rm = sib & 7;
            ss = sib >> 6;
            switch(sib >> 3)
            {
207 208 209 210
            case 0: index = context->Eax; break;
            case 1: index = context->Ecx; break;
            case 2: index = context->Edx; break;
            case 3: index = context->Ebx; break;
Alexandre Julliard's avatar
Alexandre Julliard committed
211
            case 4: index = 0; break;
212 213 214
            case 5: index = context->Ebp; break;
            case 6: index = context->Esi; break;
            case 7: index = context->Edi; break;
Alexandre Julliard's avatar
Alexandre Julliard committed
215 216 217 218 219
            }
        }

        switch(rm)
        {
220 221 222 223 224 225 226 227
        case 0: base = context->Eax; seg = context->SegDs; break;
        case 1: base = context->Ecx; seg = context->SegDs; break;
        case 2: base = context->Edx; seg = context->SegDs; break;
        case 3: base = context->Ebx; seg = context->SegDs; break;
        case 4: base = context->Esp; seg = context->SegSs; break;
        case 5: base = context->Ebp; seg = context->SegSs; break;
        case 6: base = context->Esi; seg = context->SegDs; break;
        case 7: base = context->Edi; seg = context->SegDs; break;
Alexandre Julliard's avatar
Alexandre Julliard committed
228 229 230 231 232 233 234
        }
        switch (mod)
        {
        case 0:
            if (rm == 5)  /* special case: ds:(disp32) */
            {
                GET_VAL( &base, DWORD );
235
                seg = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
            }
            break;

        case 1:  /* 8-bit disp */
            GET_VAL( &off, BYTE );
            base += (signed char)off;
            break;

        case 2:  /* 32-bit disp */
            GET_VAL( &off, DWORD );
            base += (signed long)off;
            break;
        }
    }
    else  /* short address */
    {
        switch(rm)
        {
        case 0:  /* ds:(bx,si) */
255 256
            base = LOWORD(context->Ebx) + LOWORD(context->Esi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
257 258
            break;
        case 1:  /* ds:(bx,di) */
259 260
            base = LOWORD(context->Ebx) + LOWORD(context->Edi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
261 262
            break;
        case 2:  /* ss:(bp,si) */
263 264
            base = LOWORD(context->Ebp) + LOWORD(context->Esi);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
265 266
            break;
        case 3:  /* ss:(bp,di) */
267 268
            base = LOWORD(context->Ebp) + LOWORD(context->Edi);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
269 270
            break;
        case 4:  /* ds:(si) */
271 272
            base = LOWORD(context->Esi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
273 274
            break;
        case 5:  /* ds:(di) */
275 276
            base = LOWORD(context->Edi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
277 278
            break;
        case 6:  /* ss:(bp) */
279 280
            base = LOWORD(context->Ebp);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
281 282
            break;
        case 7:  /* ds:(bx) */
283 284
            base = LOWORD(context->Ebx);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
285 286 287 288 289 290 291 292 293
            break;
        }

        switch(mod)
        {
        case 0:
            if (rm == 6)  /* special case: ds:(disp16) */
            {
                GET_VAL( &base, WORD );
294
                seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
295 296 297 298 299 300 301 302 303 304 305 306 307
            }
            break;

        case 1:  /* 8-bit disp */
            GET_VAL( &off, BYTE );
            base += (signed char)off;
            break;

        case 2:  /* 16-bit disp */
            GET_VAL( &off, WORD );
            base += (signed short)off;
            break;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
308
        base &= 0xffff;
Alexandre Julliard's avatar
Alexandre Julliard committed
309 310 311
    }
    if (segprefix != -1) seg = segprefix;

Alexandre Julliard's avatar
Alexandre Julliard committed
312
    /* Make sure the segment and offset are valid */
313
    if (wine_ldt_is_system(seg)) return (BYTE *)(base + (index << ss));
314 315 316 317
    if ((seg & 7) != 7) return NULL;
    wine_ldt_get_entry( seg, &entry );
    if (wine_ldt_is_empty( &entry )) return NULL;
    if (wine_ldt_get_limit(&entry) < (base + (index << ss))) return NULL;
318
    return (BYTE *)wine_ldt_get_base(&entry) + base + (index << ss);
Alexandre Julliard's avatar
Alexandre Julliard committed
319
#undef GET_VAL
Alexandre Julliard's avatar
Alexandre Julliard committed
320 321 322 323 324 325 326 327
}


/***********************************************************************
 *           INSTR_EmulateLDS
 *
 * Emulate the LDS (and LES,LFS,etc.) instruction.
 */
328 329
static BOOL INSTR_EmulateLDS( CONTEXT86 *context, BYTE *instr, int long_op,
                              int long_addr, int segprefix, int *len )
Alexandre Julliard's avatar
Alexandre Julliard committed
330
{
Alexandre Julliard's avatar
Alexandre Julliard committed
331
    WORD seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
332 333 334
    BYTE *regmodrm = instr + 1 + (*instr == 0x0f);
    BYTE *addr = INSTR_GetOperandAddr( context, regmodrm,
                                       long_addr, segprefix, len );
Alexandre Julliard's avatar
Alexandre Julliard committed
335 336 337
    if (!addr)
        return FALSE;  /* Unable to emulate it */
    seg = *(WORD *)(addr + (long_op ? 4 : 2));
Alexandre Julliard's avatar
Alexandre Julliard committed
338

Alexandre Julliard's avatar
Alexandre Julliard committed
339
    if (!INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
340 341 342 343
        return FALSE;  /* Unable to emulate it */

    /* Now store the offset in the correct register */

Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
344
    store_reg( context, *regmodrm, addr, long_op );
Alexandre Julliard's avatar
Alexandre Julliard committed
345 346 347 348 349

    /* Store the correct segment in the segment register */

    switch(*instr)
    {
350 351
    case 0xc4: context->SegEs = seg; break;  /* les */
    case 0xc5: context->SegDs = seg; break;  /* lds */
Alexandre Julliard's avatar
Alexandre Julliard committed
352 353
    case 0x0f: switch(instr[1])
               {
354 355 356
               case 0xb2: context->SegSs = seg; break;  /* lss */
               case 0xb4: context->SegFs = seg; break;  /* lfs */
               case 0xb5: context->SegGs = seg; break;  /* lgs */
Alexandre Julliard's avatar
Alexandre Julliard committed
357 358 359 360 361 362 363 364 365 366
               }
               break;
    }

    /* Add the opcode size to the total length */

    *len += 1 + (*instr == 0x0f);
    return TRUE;
}

367 368 369
/***********************************************************************
 *           INSTR_inport
 *
370
 * input on an I/O port
371 372 373
 */
static DWORD INSTR_inport( WORD port, int size, CONTEXT86 *context )
{
374 375
    DWORD res = ~0U;

376 377
    if (!winedos.inport) load_winedos();
    if (winedos.inport) res = winedos.inport( port, size );
378

379 380 381 382 383 384
    if (TRACE_ON(io))
    {
        switch(size)
        {
        case 1:
            DPRINTF( "0x%x < %02x @ %04x:%04x\n", port, LOBYTE(res),
385
                     (WORD)context->SegCs, LOWORD(context->Eip));
386 387 388
            break;
        case 2:
            DPRINTF( "0x%x < %04x @ %04x:%04x\n", port, LOWORD(res),
389
                     (WORD)context->SegCs, LOWORD(context->Eip));
390 391 392
            break;
        case 4:
            DPRINTF( "0x%x < %08lx @ %04x:%04x\n", port, res,
393
                     (WORD)context->SegCs, LOWORD(context->Eip));
394 395 396 397 398 399 400 401 402 403
            break;
        }
    }
    return res;
}


/***********************************************************************
 *           INSTR_outport
 *
404
 * output on an I/O port
405 406 407
 */
static void INSTR_outport( WORD port, int size, DWORD val, CONTEXT86 *context )
{
408 409
    if (!winedos.outport) load_winedos();
    if (winedos.outport) winedos.outport( port, size, val );
410

411 412 413 414 415 416
    if (TRACE_ON(io))
    {
        switch(size)
        {
        case 1:
            DPRINTF("0x%x > %02x @ %04x:%04x\n", port, LOBYTE(val),
417
                    (WORD)context->SegCs, LOWORD(context->Eip));
418 419 420
            break;
        case 2:
            DPRINTF("0x%x > %04x @ %04x:%04x\n", port, LOWORD(val),
421
                    (WORD)context->SegCs, LOWORD(context->Eip));
422 423 424
            break;
        case 4:
            DPRINTF("0x%x > %08lx @ %04x:%04x\n", port, val,
425
                    (WORD)context->SegCs, LOWORD(context->Eip));
426 427 428 429 430
            break;
        }
    }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
431

Alexandre Julliard's avatar
Alexandre Julliard committed
432 433 434
/***********************************************************************
 *           INSTR_EmulateInstruction
 *
435
 * Emulate a privileged instruction.
436
 * Returns exception continuation status.
Alexandre Julliard's avatar
Alexandre Julliard committed
437
 */
438
DWORD INSTR_EmulateInstruction( EXCEPTION_RECORD *rec, CONTEXT86 *context )
Alexandre Julliard's avatar
Alexandre Julliard committed
439
{
Alexandre Julliard's avatar
Alexandre Julliard committed
440
    int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
Alexandre Julliard's avatar
Alexandre Julliard committed
441 442
    BYTE *instr;

Alexandre Julliard's avatar
Alexandre Julliard committed
443 444
    long_op = long_addr = (!ISV86(context) && IS_SELECTOR_32BIT(context->SegCs));
    instr = make_ptr( context, context->SegCs, context->Eip, TRUE );
445
    if (!instr) return ExceptionContinueSearch;
Alexandre Julliard's avatar
Alexandre Julliard committed
446 447 448

    /* First handle any possible prefix */

Alexandre Julliard's avatar
Alexandre Julliard committed
449
    segprefix = -1;  /* no prefix */
Alexandre Julliard's avatar
Alexandre Julliard committed
450
    prefix = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
451
    repX = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
452
    prefixlen = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
453 454 455 456 457
    while(prefix)
    {
        switch(*instr)
        {
        case 0x2e:
458
            segprefix = context->SegCs;
Alexandre Julliard's avatar
Alexandre Julliard committed
459 460
            break;
        case 0x36:
461
            segprefix = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
462 463
            break;
        case 0x3e:
464
            segprefix = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
465 466
            break;
        case 0x26:
467
            segprefix = context->SegEs;
Alexandre Julliard's avatar
Alexandre Julliard committed
468 469
            break;
        case 0x64:
470
            segprefix = context->SegFs;
Alexandre Julliard's avatar
Alexandre Julliard committed
471 472
            break;
        case 0x65:
473
            segprefix = context->SegGs;
Alexandre Julliard's avatar
Alexandre Julliard committed
474 475 476 477 478 479 480
            break;
        case 0x66:
            long_op = !long_op;  /* opcode size prefix */
            break;
        case 0x67:
            long_addr = !long_addr;  /* addr size prefix */
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
481
        case 0xf0:  /* lock */
Alexandre Julliard's avatar
Alexandre Julliard committed
482
	    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
483
        case 0xf2:  /* repne */
Alexandre Julliard's avatar
Alexandre Julliard committed
484 485
	    repX = 1;
	    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
486
        case 0xf3:  /* repe */
Alexandre Julliard's avatar
Alexandre Julliard committed
487
	    repX = 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
488
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
489 490 491 492 493 494 495
        default:
            prefix = 0;  /* no more prefixes */
            break;
        }
        if (prefix)
        {
            instr++;
Alexandre Julliard's avatar
Alexandre Julliard committed
496
            prefixlen++;
Alexandre Julliard's avatar
Alexandre Julliard committed
497 498 499 500 501 502 503
        }
    }

    /* Now look at the actual instruction */

    switch(*instr)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
504 505 506
        case 0x07: /* pop es */
        case 0x17: /* pop ss */
        case 0x1f: /* pop ds */
Alexandre Julliard's avatar
Alexandre Julliard committed
507
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
508
                WORD seg = *(WORD *)get_stack( context );
Alexandre Julliard's avatar
Alexandre Julliard committed
509 510 511
                if (INSTR_ReplaceSelector( context, &seg ))
                {
                    switch(*instr)
Alexandre Julliard's avatar
Alexandre Julliard committed
512
                    {
513 514 515
                    case 0x07: context->SegEs = seg; break;
                    case 0x17: context->SegSs = seg; break;
                    case 0x1f: context->SegDs = seg; break;
Alexandre Julliard's avatar
Alexandre Julliard committed
516
                    }
Alexandre Julliard's avatar
Alexandre Julliard committed
517
                    add_stack(context, long_op ? 4 : 2);
518
                    context->Eip += prefixlen + 1;
519
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
520
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
521
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
522
            break;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
523

Alexandre Julliard's avatar
Alexandre Julliard committed
524 525
        case 0x0f: /* extended instruction */
            switch(instr[1])
Alexandre Julliard's avatar
Alexandre Julliard committed
526
            {
527
	    case 0x22: /* mov eax, crX */
528 529
                switch (instr[2])
                {
530
		case 0xc0:
531
			ERR("mov eax,cr0 at 0x%08lx, EAX=0x%08lx\n",
532 533
                            context->Eip,context->Eax );
                        context->Eip += prefixlen+3;
534
			return ExceptionContinueExecution;
535 536 537 538 539
		default:
			break; /*fallthrough to bad instruction handling */
		}
		break; /*fallthrough to bad instruction handling */
	    case 0x20: /* mov crX, eax */
540 541
                switch (instr[2])
                {
542 543 544 545 546 547 548 549 550 551 552 553
		case 0xe0: /* mov cr4, eax */
		    /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
		     * bit 0: VME	Virtual Mode Exception ?
		     * bit 1: PVI	Protected mode Virtual Interrupt
		     * bit 2: TSD	Timestamp disable
		     * bit 3: DE	Debugging extensions
		     * bit 4: PSE	Page size extensions
		     * bit 5: PAE   Physical address extension
		     * bit 6: MCE	Machine check enable
		     * bit 7: PGE   Enable global pages
		     * bit 8: PCE	Enable performance counters at IPL3
		     */
554 555 556
                    ERR("mov cr4,eax at 0x%08lx\n",context->Eip);
                    context->Eax = 0;
                    context->Eip += prefixlen+3;
557
		    return ExceptionContinueExecution;
558
		case 0xc0: /* mov cr0, eax */
559 560 561
                    ERR("mov cr0,eax at 0x%08lx\n",context->Eip);
                    context->Eax = 0x10; /* FIXME: set more bits ? */
                    context->Eip += prefixlen+3;
562
		    return ExceptionContinueExecution;
563 564 565 566 567
		default: /* fallthrough to illegal instruction */
		    break;
		}
		/* fallthrough to illegal instruction */
		break;
568 569 570
            case 0x21: /* mov drX, eax */
                switch (instr[2])
                {
571 572 573 574 575 576 577
                case 0xc8: /* mov dr1, eax */
                    context->ContextFlags = CONTEXT_DEBUG_REGISTERS;
                    NtGetContextThread( GetCurrentThread(), context );
                    TRACE("mov dr1,eax at 0x%08lx\n",context->Eip);
                    context->Eax = context->Dr1;
                    context->Eip += prefixlen+3;
                    return ExceptionContinueExecution;
578 579 580 581 582 583
                case 0xf8: /* mov dr7, eax */
                    TRACE("mov dr7,eax at 0x%08lx\n",context->Eip);
                    context->Eax = 0x400;
                    context->Eip += prefixlen+3;
                    return ExceptionContinueExecution;
                }
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
                ERR("Unsupported DR register, eip+2 is %02x\n", instr[2]);
                /* fallthrough to illegal instruction */
                break;
            case 0x23: /* mov eax drX */
                switch (instr[2])
                {
                case 0xc8: /* mov eax, dr1 */
                    context->ContextFlags = CONTEXT_DEBUG_REGISTERS;
                    NtGetContextThread( GetCurrentThread(), context );
                    context->Dr1 = context->Eax;
                    context->Eip += prefixlen+3;
                    context->ContextFlags = CONTEXT_DEBUG_REGISTERS;
                    NtSetContextThread( GetCurrentThread(), context );
                    return ExceptionContinueExecution;
                }
                ERR("Unsupported DR register, eip+2 is %02x\n", instr[2]);
600 601
                /* fallthrough to illegal instruction */
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
602
            case 0xa1: /* pop fs */
Alexandre Julliard's avatar
Alexandre Julliard committed
603
                {
Alexandre Julliard's avatar
Alexandre Julliard committed
604
                    WORD seg = *(WORD *)get_stack( context );
Alexandre Julliard's avatar
Alexandre Julliard committed
605
                    if (INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
606
                    {
607
                        context->SegFs = seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
608
                        add_stack(context, long_op ? 4 : 2);
609
                        context->Eip += prefixlen + 2;
610
                        return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
611 612 613
                    }
                }
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
614 615
            case 0xa9: /* pop gs */
                {
Alexandre Julliard's avatar
Alexandre Julliard committed
616
                    WORD seg = *(WORD *)get_stack( context );
Alexandre Julliard's avatar
Alexandre Julliard committed
617
                    if (INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
618
                    {
619
                        context->SegGs = seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
620
                        add_stack(context, long_op ? 4 : 2);
621
                        context->Eip += prefixlen + 2;
622
                        return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
623 624 625 626 627 628 629 630 631
                    }
                }
                break;
            case 0xb2: /* lss addr,reg */
            case 0xb4: /* lfs addr,reg */
            case 0xb5: /* lgs addr,reg */
                if (INSTR_EmulateLDS( context, instr, long_op,
                                      long_addr, segprefix, &len ))
                {
632
                    context->Eip += prefixlen + len;
633
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
634 635 636 637
                }
                break;
            }
            break;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
638

Alexandre Julliard's avatar
Alexandre Julliard committed
639 640 641 642 643 644 645
        case 0x6c: /* insb     */
        case 0x6d: /* insw/d   */
        case 0x6e: /* outsb    */
        case 0x6f: /* outsw/d  */
	    {
	      int typ = *instr;  /* Just in case it's overwritten.  */
	      int outp = (typ >= 0x6e);
Alexandre Julliard's avatar
Alexandre Julliard committed
646
	      unsigned long count = repX ?
647
                          (long_addr ? context->Ecx : LOWORD(context->Ecx)) : 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
648
	      int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
649 650
	      int step = (context->EFlags & 0x400) ? -opsize : +opsize;
	      int seg = outp ? context->SegDs : context->SegEs;  /* FIXME: is this right? */
Alexandre Julliard's avatar
Alexandre Julliard committed
651 652 653

	      if (outp)
		/* FIXME: Check segment readable.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
654
		(void)0;
Alexandre Julliard's avatar
Alexandre Julliard committed
655 656
	      else
		/* FIXME: Check segment writeable.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
657
		(void)0;
Alexandre Julliard's avatar
Alexandre Julliard committed
658 659

	      if (repX)
660
              {
661 662
		if (long_addr) context->Ecx = 0;
		else SET_LOWORD(context->Ecx,0);
663
              }
Alexandre Julliard's avatar
Alexandre Julliard committed
664 665 666 667

	      while (count-- > 0)
		{
		  void *data;
668
                  WORD dx = LOWORD(context->Edx);
Alexandre Julliard's avatar
Alexandre Julliard committed
669
		  if (outp)
Alexandre Julliard's avatar
Alexandre Julliard committed
670
                  {
671 672 673
                      data = make_ptr( context, seg, context->Esi, long_addr );
                      if (long_addr) context->Esi += step;
                      else ADD_LOWORD(context->Esi,step);
Alexandre Julliard's avatar
Alexandre Julliard committed
674
                  }
Alexandre Julliard's avatar
Alexandre Julliard committed
675
		  else
Alexandre Julliard's avatar
Alexandre Julliard committed
676
                  {
677 678 679
                      data = make_ptr( context, seg, context->Edi, long_addr );
                      if (long_addr) context->Edi += step;
                      else ADD_LOWORD(context->Edi,step);
Alexandre Julliard's avatar
Alexandre Julliard committed
680
                  }
681

Alexandre Julliard's avatar
Alexandre Julliard committed
682
		  switch (typ)
Alexandre Julliard's avatar
Alexandre Julliard committed
683
                  {
Alexandre Julliard's avatar
Alexandre Julliard committed
684
		    case 0x6c:
685
		      *(BYTE *)data = INSTR_inport( dx, 1, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
686 687 688
		      break;
		    case 0x6d:
		      if (long_op)
689
                          *(DWORD *)data = INSTR_inport( dx, 4, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
690
		      else
691
                          *(WORD *)data = INSTR_inport( dx, 2, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
692 693
		      break;
		    case 0x6e:
694
                        INSTR_outport( dx, 1, *(BYTE *)data, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
695
                        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
696
		    case 0x6f:
Alexandre Julliard's avatar
Alexandre Julliard committed
697
                        if (long_op)
698
                            INSTR_outport( dx, 4, *(DWORD *)data, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
699
                        else
700
                            INSTR_outport( dx, 2, *(WORD *)data, context );
Alexandre Julliard's avatar
Alexandre Julliard committed
701
                        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
702 703
		    }
		}
704
              context->Eip += prefixlen + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
705
	    }
706
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
707

Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
        case 0x8b: /* mov Ev, Gv */
            {
                BYTE *addr = INSTR_GetOperandAddr(context, instr + 1, long_addr,
                                                  segprefix, &len);
                struct idtr idtr = get_idtr();
                unsigned int offset = addr - idtr.base;

                if (offset <= idtr.limit + 1 - (long_op ? 4 : 2))
                {
                    idt[1].LimitLow = 0x100; /* FIXME */
                    idt[3].LimitLow = 0x500; /* FIXME */
                    store_reg( context, instr[1], (BYTE *)&idt + offset, long_op );
                    context->Eip += prefixlen + len + 1;
                    return ExceptionContinueExecution;
                }
            }
            break;  /* Unable to emulate it */

Alexandre Julliard's avatar
Alexandre Julliard committed
726
        case 0x8e: /* mov XX,segment_reg */
Alexandre Julliard's avatar
Alexandre Julliard committed
727
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
728 729
                WORD seg;
                BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
730
                                                  long_addr, segprefix, &len );
Alexandre Julliard's avatar
Alexandre Julliard committed
731 732 733
                if (!addr)
                    break;  /* Unable to emulate it */
                seg = *(WORD *)addr;
Alexandre Julliard's avatar
Alexandre Julliard committed
734
                if (!INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
735 736 737 738 739
                    break;  /* Unable to emulate it */

                switch((instr[1] >> 3) & 7)
                {
                case 0:
740 741
                    context->SegEs = seg;
                    context->Eip += prefixlen + len + 1;
742
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
743 744 745
                case 1:  /* cs */
                    break;
                case 2:
746 747
                    context->SegSs = seg;
                    context->Eip += prefixlen + len + 1;
748
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
749
                case 3:
750 751
                    context->SegDs = seg;
                    context->Eip += prefixlen + len + 1;
752
                    return ExceptionContinueExecution;
753
                case 4:
754 755
                    context->SegFs = seg;
                    context->Eip += prefixlen + len + 1;
756
                    return ExceptionContinueExecution;
757
                case 5:
758 759
                    context->SegGs = seg;
                    context->Eip += prefixlen + len + 1;
760
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
761 762 763 764 765 766 767 768 769 770 771 772
                case 6:  /* unused */
                case 7:  /* unused */
                    break;
                }
            }
            break;  /* Unable to emulate it */

        case 0xc4: /* les addr,reg */
        case 0xc5: /* lds addr,reg */
            if (INSTR_EmulateLDS( context, instr, long_op,
                                  long_addr, segprefix, &len ))
            {
773
                context->Eip += prefixlen + len;
774
                return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
775 776
            }
            break;  /* Unable to emulate it */
777

Alexandre Julliard's avatar
Alexandre Julliard committed
778
        case 0xcd: /* int <XX> */
779
            if (wine_ldt_is_system(context->SegCs)) break;  /* don't emulate it in 32-bit code */
780 781
            if (!winedos.EmulateInterruptPM) load_winedos();
            if (winedos.EmulateInterruptPM)
782 783
            {
                context->Eip += prefixlen + 2;
784
                winedos.EmulateInterruptPM( context, instr[1] );
785
                return ExceptionContinueExecution;
786 787
            }
            break;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
788

Alexandre Julliard's avatar
Alexandre Julliard committed
789
        case 0xcf: /* iret */
790
            if (wine_ldt_is_system(context->SegCs)) break;  /* don't emulate it in 32-bit code */
Alexandre Julliard's avatar
Alexandre Julliard committed
791 792
            if (long_op)
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
793
                DWORD *stack = get_stack( context );
794 795 796
                context->Eip = *stack++;
                context->SegCs  = *stack++;
                context->EFlags = *stack;
Alexandre Julliard's avatar
Alexandre Julliard committed
797
                add_stack(context, 3*sizeof(DWORD));  /* Pop the return address and flags */
Alexandre Julliard's avatar
Alexandre Julliard committed
798 799 800
            }
            else
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
801
                WORD *stack = get_stack( context );
802 803 804
                context->Eip = *stack++;
                context->SegCs  = *stack++;
                SET_LOWORD(context->EFlags,*stack);
Alexandre Julliard's avatar
Alexandre Julliard committed
805
                add_stack(context, 3*sizeof(WORD));  /* Pop the return address and flags */
Alexandre Julliard's avatar
Alexandre Julliard committed
806
            }
807
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
808 809

        case 0xe4: /* inb al,XX */
810 811
            SET_LOBYTE(context->Eax,INSTR_inport( instr[1], 1, context ));
            context->Eip += prefixlen + 2;
812
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
813 814

        case 0xe5: /* in (e)ax,XX */
815
            if (long_op)
816
                context->Eax = INSTR_inport( instr[1], 4, context );
817
            else
818 819
                SET_LOWORD(context->Eax, INSTR_inport( instr[1], 2, context ));
            context->Eip += prefixlen + 2;
820
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
821 822

        case 0xe6: /* outb XX,al */
823 824
            INSTR_outport( instr[1], 1, LOBYTE(context->Eax), context );
            context->Eip += prefixlen + 2;
825
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
826 827

        case 0xe7: /* out XX,(e)ax */
828
            if (long_op)
829
                INSTR_outport( instr[1], 4, context->Eax, context );
830
            else
831 832
                INSTR_outport( instr[1], 2, LOWORD(context->Eax), context );
            context->Eip += prefixlen + 2;
833
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
834 835

        case 0xec: /* inb al,dx */
836 837
            SET_LOBYTE(context->Eax, INSTR_inport( LOWORD(context->Edx), 1, context ) );
            context->Eip += prefixlen + 1;
838
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
839 840

        case 0xed: /* in (e)ax,dx */
841
            if (long_op)
842
                context->Eax = INSTR_inport( LOWORD(context->Edx), 4, context );
843
            else
844 845
                SET_LOWORD(context->Eax, INSTR_inport( LOWORD(context->Edx), 2, context ));
            context->Eip += prefixlen + 1;
846
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
847 848

        case 0xee: /* outb dx,al */
849 850
            INSTR_outport( LOWORD(context->Edx), 1, LOBYTE(context->Eax), context );
            context->Eip += prefixlen + 1;
851
            return ExceptionContinueExecution;
852

Alexandre Julliard's avatar
Alexandre Julliard committed
853
        case 0xef: /* out dx,(e)ax */
854
            if (long_op)
855
                INSTR_outport( LOWORD(context->Edx), 4, context->Eax, context );
856
            else
857 858
                INSTR_outport( LOWORD(context->Edx), 2, LOWORD(context->Eax), context );
            context->Eip += prefixlen + 1;
859
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
860

861 862
        case 0xfa: /* cli */
            NtCurrentTeb()->dpmi_vif = 0;
863
            context->Eip += prefixlen + 1;
864
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
865

866 867
        case 0xfb: /* sti */
            NtCurrentTeb()->dpmi_vif = 1;
868
            context->Eip += prefixlen + 1;
869 870
            if (NtCurrentTeb()->vm86_pending)
            {
871
                NtCurrentTeb()->vm86_pending = 0;
872 873 874
                rec->ExceptionCode = EXCEPTION_VM86_STI;
                break; /* Handle the pending event. */
            }
875
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
876
    }
877
    return ExceptionContinueSearch;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
878
}
879

880 881 882 883 884 885

/***********************************************************************
 *           INSTR_CallBuiltinHandler
 */
void INSTR_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
{
886 887
    if (!winedos.CallBuiltinHandler) load_winedos();
    if (winedos.CallBuiltinHandler) winedos.CallBuiltinHandler( context, intnum );
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
}


/***********************************************************************
 *           DOS3Call         (KERNEL.102)
 */
void WINAPI DOS3Call( CONTEXT86 *context )
{
    INSTR_CallBuiltinHandler( context, 0x21 );
}


/***********************************************************************
 *           NetBIOSCall      (KERNEL.103)
 */
void WINAPI NetBIOSCall16( CONTEXT86 *context )
{
    INSTR_CallBuiltinHandler( context, 0x5c );
}


/***********************************************************************
 *		GetSetKernelDOSProc (KERNEL.311)
 */
FARPROC16 WINAPI GetSetKernelDOSProc16( FARPROC16 DosProc )
{
    FIXME("(DosProc=0x%08x): stub\n", (UINT)DosProc);
    return NULL;
}