instr.c 30.3 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
 *
 * 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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23 24
#include <stdarg.h>

25
#include "windef.h"
26
#include "winbase.h"
27
#include "winternl.h"
28
#include "wine/winuser16.h"
29
#include "excpt.h"
30
#include "wine/debug.h"
31
#include "kernel16_private.h"
32
#include "dosexe.h"
33
#include "wine/exception.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
34

35 36
WINE_DEFAULT_DEBUG_CHANNEL(int);
WINE_DECLARE_DEBUG_CHANNEL(io);
37

38 39 40 41 42
/* 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)))

43
static inline void add_stack( CONTEXT *context, int offset )
Alexandre Julliard's avatar
Alexandre Julliard committed
44
{
45
    if (!IS_SELECTOR_32BIT(context->SegSs))
Alexandre Julliard's avatar
Alexandre Julliard committed
46 47 48 49
        ADD_LOWORD( context->Esp, offset );
    else
        context->Esp += offset;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
50

51
static inline void *make_ptr( CONTEXT *context, DWORD seg, DWORD off, int long_addr )
Alexandre Julliard's avatar
Alexandre Julliard committed
52
{
53
    if (ldt_is_system(seg)) return (void *)off;
Alexandre Julliard's avatar
Alexandre Julliard committed
54
    if (!long_addr) off = LOWORD(off);
55
    return (char *) MapSL( MAKESEGPTR( seg, 0 ) ) + off;
Alexandre Julliard's avatar
Alexandre Julliard committed
56
}
57

58
static inline void *get_stack( CONTEXT *context )
Alexandre Julliard's avatar
Alexandre Julliard committed
59
{
60
    return ldt_get_ptr( context->SegSs, context->Esp );
Alexandre Julliard's avatar
Alexandre Julliard committed
61
}
Alexandre Julliard's avatar
Alexandre Julliard committed
62

Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
63 64 65 66 67 68 69 70 71 72
#include "pshpack1.h"
struct idtr
{
    WORD  limit;
    BYTE *base;
};
#include "poppack.h"

static LDT_ENTRY idt[256];

73
static inline struct idtr get_idtr(void)
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
74 75 76 77 78 79 80 81 82 83 84 85
{
    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
86 87 88 89
/***********************************************************************
 *           INSTR_ReplaceSelector
 *
 * Try to replace an invalid selector by a valid one.
Alexandre Julliard's avatar
Alexandre Julliard committed
90 91
 * 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
92
 * data segment. Used by (at least) Borland products (and programs compiled
Alexandre Julliard's avatar
Alexandre Julliard committed
93 94 95
 * using Borland products).
 *
 * See Undocumented Windows, Chapter 5, __0040.
Alexandre Julliard's avatar
Alexandre Julliard committed
96
 */
97
static BOOL INSTR_ReplaceSelector( CONTEXT *context, WORD *sel )
Alexandre Julliard's avatar
Alexandre Julliard committed
98
{
Alexandre Julliard's avatar
Alexandre Julliard committed
99
    if (*sel == 0x40)
Alexandre Julliard's avatar
Alexandre Julliard committed
100
    {
101
        DOSVM_start_bios_timer();
102
        *sel = DOSMEM_BiosDataSeg;
Alexandre Julliard's avatar
Alexandre Julliard committed
103
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
104
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
105
    return FALSE;  /* Can't replace selector, crashdump */
Alexandre Julliard's avatar
Alexandre Julliard committed
106 107 108
}


Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
109
/* store an operand into a register */
110
static void store_reg_word( CONTEXT *context, BYTE regmodrm, const BYTE *addr, int long_op )
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
111 112 113 114
{
    switch((regmodrm >> 3) & 7)
    {
    case 0:
115 116
        if (long_op) context->Eax = *(const DWORD *)addr;
        else SET_LOWORD(context->Eax, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
117 118
        break;
    case 1:
119 120
        if (long_op) context->Ecx = *(const DWORD *)addr;
        else SET_LOWORD(context->Ecx, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
121 122
        break;
    case 2:
123 124
        if (long_op) context->Edx = *(const DWORD *)addr;
        else SET_LOWORD(context->Edx, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
125 126
        break;
    case 3:
127 128
        if (long_op) context->Ebx = *(const DWORD *)addr;
        else SET_LOWORD(context->Ebx, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
129 130
        break;
    case 4:
131 132
        if (long_op) context->Esp = *(const DWORD *)addr;
        else SET_LOWORD(context->Esp, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
133 134
        break;
    case 5:
135 136
        if (long_op) context->Ebp = *(const DWORD *)addr;
        else SET_LOWORD(context->Ebp, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
137 138
        break;
    case 6:
139 140
        if (long_op) context->Esi = *(const DWORD *)addr;
        else SET_LOWORD(context->Esi, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
141 142
        break;
    case 7:
143 144
        if (long_op) context->Edi = *(const DWORD *)addr;
        else SET_LOWORD(context->Edi, *(const WORD *)addr);
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
145 146 147 148
        break;
    }
}

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
/* store an operand into a byte register */
static void store_reg_byte( CONTEXT *context, BYTE regmodrm, const BYTE *addr )
{
    switch((regmodrm >> 3) & 7)
    {
    case 0: context->Eax = (context->Eax & 0xffffff00) | *addr; break;
    case 1: context->Ecx = (context->Ecx & 0xffffff00) | *addr; break;
    case 2: context->Edx = (context->Edx & 0xffffff00) | *addr; break;
    case 3: context->Ebx = (context->Ebx & 0xffffff00) | *addr; break;
    case 4: context->Eax = (context->Eax & 0xffff00ff) | (*addr << 8); break;
    case 5: context->Ecx = (context->Ecx & 0xffff00ff) | (*addr << 8); break;
    case 6: context->Edx = (context->Edx & 0xffff00ff) | (*addr << 8); break;
    case 7: context->Ebx = (context->Ebx & 0xffff00ff) | (*addr << 8); break;
    }
}

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

#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)
        {
187 188 189 190 191 192 193 194
        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
195 196 197 198 199 200 201 202 203 204 205
        }
    }

    if (long_addr)
    {
        if (rm == 4)
        {
            BYTE sib;
            GET_VAL( &sib, BYTE );
            rm = sib & 7;
            ss = sib >> 6;
206
            switch((sib >> 3) & 7)
Alexandre Julliard's avatar
Alexandre Julliard committed
207
            {
208 209 210 211
            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
212
            case 4: index = 0; break;
213 214 215
            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
216 217 218 219 220
            }
        }

        switch(rm)
        {
221 222 223 224 225 226 227 228
        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
229 230 231 232 233 234 235
        }
        switch (mod)
        {
        case 0:
            if (rm == 5)  /* special case: ds:(disp32) */
            {
                GET_VAL( &base, DWORD );
236
                seg = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
            }
            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) */
256 257
            base = LOWORD(context->Ebx) + LOWORD(context->Esi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
258 259
            break;
        case 1:  /* ds:(bx,di) */
260 261
            base = LOWORD(context->Ebx) + LOWORD(context->Edi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263
            break;
        case 2:  /* ss:(bp,si) */
264 265
            base = LOWORD(context->Ebp) + LOWORD(context->Esi);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
266 267
            break;
        case 3:  /* ss:(bp,di) */
268 269
            base = LOWORD(context->Ebp) + LOWORD(context->Edi);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
270 271
            break;
        case 4:  /* ds:(si) */
272 273
            base = LOWORD(context->Esi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
274 275
            break;
        case 5:  /* ds:(di) */
276 277
            base = LOWORD(context->Edi);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
278 279
            break;
        case 6:  /* ss:(bp) */
280 281
            base = LOWORD(context->Ebp);
            seg  = context->SegSs;
Alexandre Julliard's avatar
Alexandre Julliard committed
282 283
            break;
        case 7:  /* ds:(bx) */
284 285
            base = LOWORD(context->Ebx);
            seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
286 287 288 289 290 291 292 293 294
            break;
        }

        switch(mod)
        {
        case 0:
            if (rm == 6)  /* special case: ds:(disp16) */
            {
                GET_VAL( &base, WORD );
295
                seg  = context->SegDs;
Alexandre Julliard's avatar
Alexandre Julliard committed
296 297 298 299 300 301 302 303 304 305 306 307 308
            }
            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
309
        base &= 0xffff;
Alexandre Julliard's avatar
Alexandre Julliard committed
310 311 312
    }
    if (segprefix != -1) seg = segprefix;

Alexandre Julliard's avatar
Alexandre Julliard committed
313
    /* Make sure the segment and offset are valid */
314
    if (ldt_is_system(seg)) return (BYTE *)(base + (index << ss));
315
    if ((seg & 7) != 7) return NULL;
316 317 318
    if (!ldt_is_valid( seg )) return NULL;
    if (ldt_get_limit( seg ) < (base + (index << ss))) return NULL;
    return (BYTE *)ldt_get_base( seg ) + 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
static BOOL INSTR_EmulateLDS( CONTEXT *context, BYTE *instr, int long_op,
329
                              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 */

344
    store_reg_word( 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
static DWORD INSTR_inport( WORD port, int size, CONTEXT *context )
373
{
374
    DWORD res = DOSVM_inport( port, size );
375

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


/***********************************************************************
 *           INSTR_outport
 *
401
 * output on an I/O port
402
 */
403
static void INSTR_outport( WORD port, int size, DWORD val, CONTEXT *context )
404
{
405
    DOSVM_outport( port, size, val );
406

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

Alexandre Julliard's avatar
Alexandre Julliard committed
427

Alexandre Julliard's avatar
Alexandre Julliard committed
428
/***********************************************************************
429
 *           __wine_emulate_instruction
Alexandre Julliard's avatar
Alexandre Julliard committed
430
 *
431
 * Emulate a privileged instruction.
432
 * Returns exception continuation status.
Alexandre Julliard's avatar
Alexandre Julliard committed
433
 */
434
DWORD __wine_emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT *context )
Alexandre Julliard's avatar
Alexandre Julliard committed
435
{
Alexandre Julliard's avatar
Alexandre Julliard committed
436
    int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
Alexandre Julliard's avatar
Alexandre Julliard committed
437 438
    BYTE *instr;

439
    long_op = long_addr = IS_SELECTOR_32BIT(context->SegCs);
Alexandre Julliard's avatar
Alexandre Julliard committed
440
    instr = make_ptr( context, context->SegCs, context->Eip, TRUE );
441
    if (!instr) return ExceptionContinueSearch;
Alexandre Julliard's avatar
Alexandre Julliard committed
442 443 444

    /* First handle any possible prefix */

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

    /* Now look at the actual instruction */

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

Alexandre Julliard's avatar
Alexandre Julliard committed
520 521
        case 0x0f: /* extended instruction */
            switch(instr[1])
Alexandre Julliard's avatar
Alexandre Julliard committed
522
            {
523
            case 0x22: /* mov %eax, %crX */
524 525
                switch (instr[2])
                {
526
                case 0xc0:
527 528 529
                    FIXME("mov %%eax, %%cr0 at 0x%08x, EAX=0x%08x\n",
                          context->Eip,context->Eax );
                          context->Eip += prefixlen+3;
530 531 532 533 534 535
                    return ExceptionContinueExecution;
                default:
                    break; /* Fallthrough to bad instruction handling */
                }
                break; /* Fallthrough to bad instruction handling */
            case 0x20: /* mov %crX, %eax */
536 537
                switch (instr[2])
                {
538 539 540 541 542 543 544 545 546 547 548 549
                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
                     */
550
                    FIXME("mov %%cr4, %%eax at 0x%08x\n",context->Eip);
551 552
                    context->Eax = 0;
                    context->Eip += prefixlen+3;
553 554
                    return ExceptionContinueExecution;
                case 0xc0: /* mov %cr0, %eax */
555
                    FIXME("mov %%cr0, %%eax at 0x%08x\n",context->Eip);
556 557
                    context->Eax = 0x10; /* FIXME: set more bits ? */
                    context->Eip += prefixlen+3;
558 559 560 561 562 563 564
                    return ExceptionContinueExecution;
                default: /* Fallthrough to illegal instruction */
                    break;
                }
                /* Fallthrough to illegal instruction */
                break;
            case 0x21: /* mov %drX, %eax */
565 566
                switch (instr[2])
                {
567 568
                case 0xc8: /* mov %dr1, %eax */
                    TRACE("mov %%dr1, %%eax at 0x%08x\n",context->Eip);
569 570 571
                    context->Eax = context->Dr1;
                    context->Eip += prefixlen+3;
                    return ExceptionContinueExecution;
572 573
                case 0xf8: /* mov %dr7, %eax */
                    TRACE("mov %%dr7, %%eax at 0x%08x\n",context->Eip);
574 575 576 577
                    context->Eax = 0x400;
                    context->Eip += prefixlen+3;
                    return ExceptionContinueExecution;
                }
578
                FIXME("Unsupported DR register, eip+2 is %02x\n", instr[2]);
579 580
                /* fallthrough to illegal instruction */
                break;
581
            case 0x23: /* mov %eax, %drX */
582 583
                switch (instr[2])
                {
584
                case 0xc8: /* mov %eax, %dr1 */
585 586 587 588
                    context->Dr1 = context->Eax;
                    context->Eip += prefixlen+3;
                    return ExceptionContinueExecution;
                }
589
                FIXME("Unsupported DR register, eip+2 is %02x\n", instr[2]);
590 591
                /* fallthrough to illegal instruction */
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
592
            case 0xa1: /* pop fs */
Alexandre Julliard's avatar
Alexandre Julliard committed
593
                {
Alexandre Julliard's avatar
Alexandre Julliard committed
594
                    WORD seg = *(WORD *)get_stack( context );
Alexandre Julliard's avatar
Alexandre Julliard committed
595
                    if (INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
596
                    {
597
                        context->SegFs = seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
598
                        add_stack(context, long_op ? 4 : 2);
599
                        context->Eip += prefixlen + 2;
600
                        return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
601 602 603
                    }
                }
                break;
Alexandre Julliard's avatar
Alexandre Julliard committed
604 605
            case 0xa9: /* pop gs */
                {
Alexandre Julliard's avatar
Alexandre Julliard committed
606
                    WORD seg = *(WORD *)get_stack( context );
Alexandre Julliard's avatar
Alexandre Julliard committed
607
                    if (INSTR_ReplaceSelector( context, &seg ))
Alexandre Julliard's avatar
Alexandre Julliard committed
608
                    {
609
                        context->SegGs = seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
610
                        add_stack(context, long_op ? 4 : 2);
611
                        context->Eip += prefixlen + 2;
612
                        return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
613 614 615 616 617 618 619 620 621
                    }
                }
                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 ))
                {
622
                    context->Eip += prefixlen + len;
623
                    return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
624 625 626 627
                }
                break;
            }
            break;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
628

Alexandre Julliard's avatar
Alexandre Julliard committed
629 630 631 632 633 634 635
        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
636
	      unsigned long count = repX ?
637
                          (long_addr ? context->Ecx : LOWORD(context->Ecx)) : 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
638
	      int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
639
	      int step = (context->EFlags & 0x400) ? -opsize : +opsize;
640
	      int seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
641 642

	      if (outp)
643
              {
644 645
		/* Check if there is a segment prefix override and honour it */
		seg = segprefix == -1 ? context->SegDs : segprefix;
646
		/* FIXME: Check segment is readable.  */
647
              }
Alexandre Julliard's avatar
Alexandre Julliard committed
648
	      else
649
              {
650
		seg = context->SegEs;
651
		/* FIXME: Check segment is writable.  */
652
              }
Alexandre Julliard's avatar
Alexandre Julliard committed
653 654

	      if (repX)
655
              {
656 657
		if (long_addr) context->Ecx = 0;
		else SET_LOWORD(context->Ecx,0);
658
              }
Alexandre Julliard's avatar
Alexandre Julliard committed
659 660 661 662

	      while (count-- > 0)
		{
		  void *data;
663
                  WORD dx = LOWORD(context->Edx);
Alexandre Julliard's avatar
Alexandre Julliard committed
664
		  if (outp)
Alexandre Julliard's avatar
Alexandre Julliard committed
665
                  {
666 667 668
                      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
669
                  }
Alexandre Julliard's avatar
Alexandre Julliard committed
670
		  else
Alexandre Julliard's avatar
Alexandre Julliard committed
671
                  {
672 673 674
                      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
675
                  }
676

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

703
        case 0x8a: /* mov Eb, Gb */
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
704 705
        case 0x8b: /* mov Ev, Gv */
            {
706
                BYTE *data = INSTR_GetOperandAddr(context, instr + 1, long_addr,
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
707
                                                  segprefix, &len);
708
                unsigned int data_size = (*instr == 0x8b) ? (long_op ? 4 : 2) : 1;
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
709
                struct idtr idtr = get_idtr();
710
                unsigned int offset = data - idtr.base;
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
711

712
                if (offset <= idtr.limit + 1 - data_size)
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
713 714
                {
                    idt[1].LimitLow = 0x100; /* FIXME */
715
                    idt[2].LimitLow = 0x11E; /* FIXME */
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
716
                    idt[3].LimitLow = 0x500; /* FIXME */
717 718 719 720 721 722

                    switch (*instr)
                    {
                    case 0x8a: store_reg_byte( context, instr[1], (BYTE *)idt + offset ); break;
                    case 0x8b: store_reg_word( context, instr[1], (BYTE *)idt + offset, long_op ); break;
                    }
Ivan Leo Puoti's avatar
Ivan Leo Puoti committed
723 724 725 726 727 728
                    context->Eip += prefixlen + len + 1;
                    return ExceptionContinueExecution;
                }
            }
            break;  /* Unable to emulate it */

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
781
        case 0xcd: /* int <XX> */
782 783 784
            context->Eip += prefixlen + 2;
            if (DOSVM_EmulateInterruptPM( context, instr[1] )) return ExceptionContinueExecution;
            context->Eip -= prefixlen + 2;  /* restore eip */
785
            break;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
786

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

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

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

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

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

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

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

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

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

859
        case 0xfa: /* cli */
860
            context->Eip += prefixlen + 1;
861
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
862

863
        case 0xfb: /* sti */
864
            context->Eip += prefixlen + 1;
865
            return ExceptionContinueExecution;
Alexandre Julliard's avatar
Alexandre Julliard committed
866
    }
867
    return ExceptionContinueSearch;  /* Unable to emulate it */
Alexandre Julliard's avatar
Alexandre Julliard committed
868
}
869

870

871 872 873 874 875 876 877 878 879
/***********************************************************************
 *           INSTR_vectored_handler
 *
 * Vectored exception handler used to emulate protected instructions
 * from 32-bit code.
 */
LONG CALLBACK INSTR_vectored_handler( EXCEPTION_POINTERS *ptrs )
{
    EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
880
    CONTEXT *context = ptrs->ContextRecord;
881

882
    if (ldt_is_system(context->SegCs) &&
883 884 885
        (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
         record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION))
    {
886
        if (__wine_emulate_instruction( record, context ) == ExceptionContinueExecution)
887 888 889 890 891 892
            return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}


893 894 895
/***********************************************************************
 *           DOS3Call         (KERNEL.102)
 */
896
void WINAPI DOS3Call( CONTEXT *context )
897
{
898
    __wine_call_int_handler( context, 0x21 );
899 900 901 902 903 904
}


/***********************************************************************
 *           NetBIOSCall      (KERNEL.103)
 */
905
void WINAPI NetBIOSCall16( CONTEXT *context )
906
{
907
    __wine_call_int_handler( context, 0x5c );
908 909 910 911 912 913 914 915
}


/***********************************************************************
 *		GetSetKernelDOSProc (KERNEL.311)
 */
FARPROC16 WINAPI GetSetKernelDOSProc16( FARPROC16 DosProc )
{
916
    FIXME("(DosProc=%p): stub\n", DosProc);
917 918
    return NULL;
}