home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / image / drwatson / disasm.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  43KB  |  1,218 lines

  1. /*++
  2.  
  3. Copyright (c) 1993-1997 Microsoft Corporation
  4.  
  5. Module Name:
  6.  
  7.     disasm.c
  8.  
  9. Abstract:
  10.  
  11.     This file provides support disassembly ( x86 ).
  12.  
  13. Author:
  14.  
  15.     Gerd Immeyer       19-Oct-1989
  16.     Wesley Witt (wesw) 1-May-1993    ( ported from ntsd to drwatson)
  17.  
  18. Environment:
  19.  
  20.     User Mode
  21.  
  22. --*/
  23.  
  24. #include <windows.h>
  25. #include <stddef.h>
  26. #include <string.h>
  27. #include "regs.h"
  28. #include "disasm.h"
  29. #include "drwatson.h"
  30. #include "proto.h"
  31.  
  32. /*****                     macros and defines                          *****/
  33.  
  34. #define BIT20(b) (b & 0x07)
  35. #define BIT53(b) (b >> 3 & 0x07)
  36. #define BIT76(b) (b >> 6 & 0x03)
  37. #define MAXL     16
  38. #define MAXOPLEN 10
  39.  
  40. #define OBOFFSET 26
  41. #define OBOPERAND 34
  42. #define OBLINEEND 77
  43.  
  44. /*****                     static tables and variables                 *****/
  45.  
  46. static char regtab[] = "alcldlblahchdhbhaxcxdxbxspbpsidi";  /* reg table */
  47. static char *mrmtb16[] = { "bx+si",  /* modRM string table (16-bit) */
  48.                            "bx+di",
  49.                            "bp+si",
  50.                            "bp+di",
  51.                            "si",
  52.                            "di",
  53.                            "bp",
  54.                            "bx"
  55.                          };
  56.  
  57. static char *mrmtb32[] = { "eax",       /* modRM string table (32-bit) */
  58.                            "ecx",
  59.                            "edx",
  60.                            "ebx",
  61.                            "esp",
  62.                            "ebp",
  63.                            "esi",
  64.                            "edi"
  65.                          };
  66.  
  67. static char seg16[8]   = { REGDS,  REGDS,  REGSS,  REGSS,
  68.                            REGDS,  REGDS,  REGSS,  REGDS };
  69. static char reg16[8]   = { REGEBX, REGEBX, REGEBP, REGEBP,
  70.                            REGESI, REGEDI, REGEBP, REGEBX };
  71. static char reg16_2[4] = { REGESI, REGEDI, REGESI, REGEDI };
  72.  
  73. static char seg32[8]   = { REGDS,  REGDS,  REGDS,  REGDS,
  74.                            REGSS,  REGSS,  REGDS,  REGDS };
  75. static char reg32[8]   = { REGEAX, REGECX, REGEDX, REGEBX,
  76.                            REGESP, REGEBP, REGESI, REGEDI };
  77.  
  78. static char sregtab[] = "ecsdfg";  // first letter of ES, CS, SS, DS, FS, GS
  79.  
  80. char    hexdigit[] = { '0', '1', '2', '3', '4', '5', '6', '7',
  81.                        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
  82.  
  83. static int              mod;            /* mod of mod/rm byte */
  84. static int              rm;             /* rm of mod/rm byte */
  85. static int              ttt;            /* return reg value (of mod/rm) */
  86. static unsigned char    *pMem;          /* current position in instruction */
  87. static int              mode_32;        /* local addressing mode indicator */
  88. static int              opsize_32;      /* operand size flag */
  89.  
  90. ULONG                   EAaddr[2];      //  offset of effective address
  91. static int              EAsize[2];      //  size of effective address item
  92. static char             *pchEAseg[2];   //  normal segment for operand
  93.  
  94. int                     G_mode_32 = 1;  /* global address mode indicator */
  95.  
  96. static BOOLEAN          fMovX;          // indicates a MOVSX or MOVZX
  97.  
  98. //      internal function definitions
  99.  
  100. BOOLEAN disasm(PDEBUGPACKET, PULONG, PUCHAR, BOOLEAN);
  101. void DIdoModrm(PDEBUGPACKET dp,char **, int, BOOLEAN);
  102.  
  103. void OutputHexString(char **, char *, int);
  104. void OutputHexValue(char **, char *, int, int);
  105. void OutputHexCode(char **, char *, int);
  106. void OutputString(char **, char *);
  107. void OutputSymbol(PDEBUGPACKET, char **, char *, int, int);
  108.  
  109. void GetNextOffset(PDEBUGPACKET, PULONG, BOOLEAN);
  110. void OutputHexAddr(PUCHAR *, ULONG);
  111. USHORT GetSegRegValue(PDEBUGPACKET,int);
  112.  
  113. /**** disasm - disassemble an 80x86/80x87 instruction
  114. *
  115. *  Input:
  116. *       pOffset = pointer to offset to start disassembly
  117. *       fEAout = if set, include EA (effective address)
  118. *
  119. *  Output:
  120. *       pOffset = pointer to offset of next instruction
  121. *       pchDst = pointer to result string
  122. *
  123. ***************************************************************************/
  124.  
  125. BOOLEAN
  126. disasm( PDEBUGPACKET dp, PULONG pOffset, PUCHAR pchDst, BOOLEAN fEAout )
  127. {
  128.     int     opcode;                     /* current opcode */
  129.     int     olen = 2;                   /* operand length */
  130.     int     alen = 2;                   /* address length */
  131.     int     end = FALSE;                /* end of instruction flag */
  132.     int     mrm = FALSE;                /* indicator that modrm is generated*/
  133.     unsigned char *action;              /* action for operand interpretation*/
  134.     long    tmp;                        /* temporary storage field */
  135.     int     indx;                       /* temporary index */
  136.     int     action2;                    /* secondary action */
  137.     int     instlen;                    /* instruction length */
  138.     int     cBytes;                     //  bytes read into instr buffer
  139.     int     segOvr = 0;                 /* segment override opcode */
  140.     char    membuf[MAXL];               /* current instruction buffer */
  141.     char    *pEAlabel = "";             //  optional label for operand
  142.  
  143.     char    *pchResultBuf = pchDst;     //  working copy of pchDst pointer
  144.     char    RepPrefixBuffer[32];        //  rep prefix buffer
  145.     char    *pchRepPrefixBuf = RepPrefixBuffer; //  pointer to prefix buffer
  146.     char    OpcodeBuffer[8];            //  opcode buffer
  147.     char    *pchOpcodeBuf = OpcodeBuffer; //  pointer to opcode buffer
  148.     char    OperandBuffer[80];          //  operand buffer
  149.     char    *pchOperandBuf = OperandBuffer; //  pointer to operand buffer
  150.     char    ModrmBuffer[80];            //  modRM buffer
  151.     char    *pchModrmBuf = ModrmBuffer; //  pointer to modRM buffer
  152.     char    EABuffer[42];               //  effective address buffer
  153.     char    *pchEABuf = EABuffer;       //  pointer to EA buffer
  154.  
  155.     int     obOpcode = OBOFFSET;
  156.     int     obOpcodeMin;
  157.     int     obOpcodeMax;
  158.  
  159.     int     obOperand = OBOPERAND;
  160.     int     obOperandMin;
  161.     int     obOperandMax;
  162.  
  163.     int     cbOpcode;
  164.     int     cbOperand;
  165.     int     cbOffset;
  166.     int     cbEAddr;
  167.  
  168.     int     fTwoLines = FALSE;
  169.  
  170.     fMovX = FALSE;
  171.     EAsize[0] = EAsize[1] = 0;          //  no effective address
  172.     pchEAseg[0] = dszDS_;
  173.     pchEAseg[1] = dszES_;
  174.  
  175.     mode_32 = opsize_32 = (G_mode_32 == 1); /* local addressing mode */
  176.     olen = alen = (1 + mode_32) << 1;   //  set operand/address lengths
  177.                                         //  2 for 16-bit and 4 for 32-bit
  178.  
  179.     OutputHexAddr(&pchResultBuf, *pOffset);
  180.  
  181.     *pchResultBuf++ = ' ';
  182.  
  183.     DoMemoryRead( dp,
  184.                   (LPVOID)*pOffset,
  185.                   (LPVOID)membuf,
  186.                   MAXL,
  187.                   &cBytes
  188.                 );
  189.  
  190.                                         /* move full inst to local buffer */
  191.     pMem = membuf;                      /* point to begin of instruction */
  192.     opcode = *pMem++;                   /* get opcode */
  193.     OutputString(&pchOpcodeBuf, distbl[opcode].instruct);
  194.     action = actiontbl + distbl[opcode].opr; /* get operand action */
  195.  
  196. /*****          loop through all operand actions               *****/
  197.  
  198.     do {
  199.         action2 = (*action) & 0xc0;
  200.         switch((*action++) & 0x3f) {
  201.             case ALT:                   /* alter the opcode if 32-bit */
  202.                 if (opsize_32) {
  203.                     indx = *action++;
  204.                     pchOpcodeBuf = &OpcodeBuffer[indx];
  205.                     if (indx == 0)
  206.                         OutputString(&pchOpcodeBuf, dszCWDE);
  207.                     else {
  208.                         *pchOpcodeBuf++ = 'd';
  209.                         if (indx == 1)
  210.                             *pchOpcodeBuf++ = 'q';
  211.                         }
  212.                     }
  213.                 break;
  214.  
  215.             case STROP:
  216.                 //  compute size of operands in indx
  217.                 //  also if dword operands, change fifth
  218.                 //  opcode letter from 'w' to 'd'.
  219.  
  220.                 if (opcode & 1) {
  221.                     if (opsize_32) {
  222.                         indx = 4;
  223.                         OpcodeBuffer[4] = 'd';
  224.                         }
  225.                     else
  226.                         indx = 2;
  227.                     }
  228.                 else
  229.                     indx = 1;
  230.  
  231.                 if (*action & 1) {
  232.                     if (fEAout) {
  233.                         EAaddr[0] = (DWORD)GetRegValue(dp, REGESI);
  234.                         EAsize[0] = indx;
  235.                         }
  236.                     }
  237.                 if (*action++ & 2) {
  238.                     if (fEAout) {
  239.                         EAaddr[1] = (DWORD)GetRegValue(dp, REGEDI);
  240.                         EAsize[1] = indx;
  241.                         }
  242.                     }
  243.                 break;
  244.  
  245.             case CHR:                   /* insert a character */
  246.                 *pchOperandBuf++ = *action++;
  247.                 break;
  248.  
  249.             case CREG:                  /* set debug, test or control reg */
  250.                 if ((opcode - 231) & 0x04)      //  remove bias from opcode
  251.                     *pchOperandBuf++ = 't';
  252.                 else if ((opcode - 231) & 0x01)
  253.                     *pchOperandBuf++ = 'd';
  254.                 else
  255.                     *pchOperandBuf++ = 'c';
  256.                 *pchOperandBuf++ = 'r';
  257.                 *pchOperandBuf++ = (char)('0' + ttt);
  258.                 break;
  259.  
  260.             case SREG2:                 /* segment register */
  261.                 ttt = BIT53(opcode);    //  set value to fall through
  262.  
  263.             case SREG3:                 /* segment register */
  264.                 *pchOperandBuf++ = sregtab[ttt];  // reg is part of modrm
  265.                 *pchOperandBuf++ = 's';
  266.                 break;
  267.  
  268.             case BRSTR:                 /* get index to register string */
  269.                 ttt = *action++;        /*    from action table */
  270.                 goto BREGlabel;
  271.  
  272.             case BOREG:                 /* byte register (in opcode) */
  273.                 ttt = BIT20(opcode);    /* register is part of opcode */
  274.                 goto BREGlabel;
  275.  
  276.             case ALSTR:
  277.                 ttt = 0;                /* point to AL register */
  278. BREGlabel:
  279.             case BREG:                  /* general register */
  280.                 *pchOperandBuf++ = regtab[ttt * 2];
  281.                 *pchOperandBuf++ = regtab[ttt * 2 + 1];
  282.                 break;
  283.  
  284.             case WRSTR:                 /* get index to register string */
  285.                 ttt = *action++;        /*    from action table */
  286.                 goto WREGlabel;
  287.  
  288.             case VOREG:                 /* register is part of opcode */
  289.                 ttt = BIT20(opcode);
  290.                 goto VREGlabel;
  291.  
  292.             case AXSTR:
  293.                 ttt = 0;                /* point to eAX register */
  294. VREGlabel:
  295.             case VREG:                  /* general register */
  296.                 if (opsize_32)          /* test for 32bit mode */
  297.                     *pchOperandBuf++ = 'e';
  298. WREGlabel:
  299.             case WREG:                  /* register is word size */
  300.                 *pchOperandBuf++ = regtab[ttt * 2 + 16];
  301.                 *pchOperandBuf++ = regtab[ttt * 2 + 17];
  302.                 break;
  303.  
  304.             case IST_ST:
  305.                 OutputString(&pchOperandBuf, "st(0),st");
  306.                 *(pchOperandBuf - 5) += rm;
  307.                 break;
  308.  
  309.             case ST_IST:
  310.                 OutputString(&pchOperandBuf, "st,");
  311.             case IST:
  312.                 OutputString(&pchOperandBuf, "st(0)");
  313.                 *(pchOperandBuf - 2) += rm;
  314.                 break;
  315.  
  316.             case xBYTE:                 /* set instruction to byte only */
  317.                 EAsize[0] = 1;
  318.                 pEAlabel = "byte ptr ";
  319.                 break;
  320.  
  321.             case VAR:
  322.                 if (opsize_32)
  323.                     goto DWORDlabel;
  324.  
  325.             case xWORD:
  326.                 EAsize[0] = 2;
  327.                 pEAlabel = "word ptr ";
  328.                 break;
  329.  
  330.             case EDWORD:
  331.                 opsize_32 = 1;    //  for control reg move, use eRegs
  332.             case xDWORD:
  333. DWORDlabel:
  334.                 EAsize[0] = 4;
  335.                 pEAlabel = "dword ptr ";
  336.                 break;
  337.  
  338.             case QWORD:
  339.                 EAsize[0] = 8;
  340.                 pEAlabel = "qword ptr ";
  341.                 break;
  342.  
  343.             case TTBYTE:
  344.                 EAsize[0] = 10;
  345.                 pEAlabel = "tbyte ptr ";
  346.                 break;
  347.  
  348.             case FARPTR:
  349.                 if (opsize_32) {
  350.                     EAsize[0] = 6;
  351.                     pEAlabel = "fword ptr ";
  352.                     }
  353.                 else {
  354.                     EAsize[0] = 4;
  355.                     pEAlabel = "dword ptr ";
  356.                     }
  357.                 break;
  358.  
  359.             case LMODRM:                //  output modRM data type
  360.                 if (mod != 3)
  361.                     OutputString(&pchOperandBuf, pEAlabel);
  362.                 else
  363.                     EAsize[0] = 0;
  364.  
  365.             case MODRM:                 /* output modrm string */
  366.                 if (segOvr)             /* in case of segment override */
  367.                     OutputString(&pchOperandBuf, distbl[segOvr].instruct);
  368.                 *pchModrmBuf = '\0';
  369.                 OutputString(&pchOperandBuf, ModrmBuffer);
  370.                 break;
  371.  
  372.             case ADDRP:                 /* address pointer */
  373.                 OutputHexString(&pchOperandBuf, pMem + olen, 2); // segment
  374.                 *pchOperandBuf++ = ':';
  375.                 OutputSymbol(dp, &pchOperandBuf, pMem, olen, segOvr);
  376.                 pMem += olen + 2;
  377.                 break;
  378.  
  379.             case REL8:                  /* relative address 8-bit */
  380.                 if (opcode == 0xe3 && mode_32) {
  381.                     pchOpcodeBuf = OpcodeBuffer;
  382.                     OutputString(&pchOpcodeBuf, dszJECXZ);
  383.                     }
  384.                 tmp = (long)*(char *)pMem++; /* get the 8-bit rel offset */
  385.                 goto DoRelDispl;
  386.  
  387.             case REL16:                 /* relative address 16-/32-bit */
  388.                 tmp = 0;
  389.                 memmove(&tmp,pMem,sizeof(long));
  390.                 pMem += alen;           /* skip over offset */
  391. DoRelDispl:
  392.                 tmp += *pOffset + (pMem - membuf); /* calculate address */
  393.                 OutputSymbol(dp, &pchOperandBuf, (char *) &tmp, alen, segOvr);
  394.                                                    // address
  395.                 break;
  396.  
  397.             case UBYTE:                 //  unsigned byte for int/in/out
  398.                 OutputHexString(&pchOperandBuf, pMem, 1);  //  ubyte
  399.                 pMem++;
  400.                 break;
  401.  
  402.             case IB:                    /* operand is immediate byte */
  403.                 if ((opcode & ~1) == 0xd4) {  // postop for AAD/AAM is 0x0a
  404.                     if (*pMem++ != 0x0a) // test post-opcode byte
  405.                         OutputString(&pchOperandBuf, dszRESERVED);
  406.                     break;
  407.                     }
  408.                 olen = 1;               /* set operand length */
  409.                 goto DoImmed;
  410.  
  411.             case IW:                    /* operand is immediate word */
  412.                 olen = 2;               /* set operand length */
  413.  
  414.             case IV:                    /* operand is word or dword */
  415. DoImmed:
  416.                 OutputHexValue(&pchOperandBuf, pMem, olen, FALSE);
  417.                 pMem += olen;
  418.                 break;
  419.  
  420.             case OFFS:                  /* operand is offset */
  421.                 EAsize[0] = (opcode & 1) ? olen : 1;
  422.  
  423.                 if (segOvr)             /* in case of segment override */
  424.                     OutputString(&pchOperandBuf, distbl[segOvr].instruct);
  425.  
  426.                 *pchOperandBuf++ = '[';
  427.                 OutputSymbol(dp,&pchOperandBuf, pMem, alen, segOvr);  //  offset
  428.                 pMem += alen;
  429.                 *pchOperandBuf++ = ']';
  430.                 break;
  431.  
  432.             case GROUPP:                 /* operand is of group 1,2,4,6 or 8 */
  433.                                         /* output opcode symbol */
  434.                 OutputString(&pchOpcodeBuf, group[*action++][ttt]);
  435.                 break;
  436.  
  437.             case GROUPT:                /* operand is of group 3,5 or 7 */
  438.                 indx = *action;         /* get indx into group from action */
  439.                 goto doGroupT;
  440.  
  441.             case EGROUPT:               /* x87 ESC (D8-DF) group index */
  442.                 indx = BIT20(opcode) * 2; /* get group index from opcode */
  443.                 if (mod == 3) {         /* some operand variations exists */
  444.                                         /*   for x87 and mod == 3 */
  445.                     ++indx;             /* take the next group table entry */
  446.                     if (indx == 3) {    /* for x87 ESC==D9 and mod==3 */
  447.                         if (ttt > 3) {  /* for those D9 instructions */
  448.                             indx = 12 + ttt; /* offset index to table by 12 */
  449.                             ttt = rm;   /* set secondary index to rm */
  450.                             }
  451.                         }
  452.                     else if (indx == 7) { /* for x87 ESC==DB and mod==3 */
  453.                         if (ttt == 4)   /* only valid if ttt==4 */
  454.                             ttt = rm;   /* set secondary group table index */
  455.                         else
  456.                             ttt = 7;    /* no an x87 instruction */
  457.                         }
  458.                     }
  459. doGroupT:
  460.                 /* handle group with different types of operands */
  461.  
  462.                 OutputString(&pchOpcodeBuf, groupt[indx][ttt].instruct);
  463.                 action = actiontbl + groupt[indx][ttt].opr;
  464.                                                         /* get new action */
  465.                 break;
  466.  
  467.             case OPC0F:                 /* secondary opcode table (opcode 0F) */
  468.                 opcode = *pMem++;       /* get real opcode */
  469.                 fMovX  = (BOOLEAN)(opcode == 0xBF || opcode == 0xB7);
  470.                 if (opcode < 7) /* for the first 7 opcodes */
  471.                     opcode += 256;      /* point begin of secondary opcode tab. */
  472.                 else if (opcode > 0x1f && opcode < 0x27)
  473.                     opcode += 231;      /* adjust for non-existing opcodes */
  474.                 else if (opcode > 0x2f && opcode < 0x33)
  475.                     opcode += 222;      /* adjust for non-existing opcodes */
  476.                 else if (opcode > 0x7e && opcode < 0xd0)
  477.                     opcode += 148;      /* adjust for non-existing opcodes */
  478.                 else
  479.                     opcode = 260;       /* all non-existing opcodes */
  480.                 goto getNxtByte1;
  481.  
  482.             case ADR_OVR:               /* address override */
  483.                 mode_32 = !G_mode_32;   /* override addressing mode */
  484.                 alen = (mode_32 + 1) << 1; /* toggle address length */
  485.                 goto getNxtByte;
  486.  
  487.             case OPR_OVR:               /* operand size override */
  488.                 opsize_32 = !G_mode_32; /* override operand size */
  489.                 olen = (opsize_32 + 1) << 1; /* toggle operand length */
  490.                 goto getNxtByte;
  491.  
  492.             case SEG_OVR:               /* handle segment override */
  493.                 segOvr = opcode;        /* save segment override opcode */
  494.                 pchOpcodeBuf = OpcodeBuffer;  // restart the opcode string
  495.                 goto getNxtByte;
  496.  
  497.             case REP:                   /* handle rep/lock prefixes */
  498.                 *pchOpcodeBuf = '\0';
  499.                 if (pchRepPrefixBuf != RepPrefixBuffer)
  500.                     *pchRepPrefixBuf++ = ' ';
  501.                 OutputString(&pchRepPrefixBuf, OpcodeBuffer);
  502.                 pchOpcodeBuf = OpcodeBuffer;
  503. getNxtByte:
  504.                 opcode = *pMem++;        /* next byte is opcode */
  505. getNxtByte1:
  506.                 action = actiontbl + distbl[opcode].opr;
  507.                 OutputString(&pchOpcodeBuf, distbl[opcode].instruct);
  508.  
  509.             default:                    /* opcode has no operand */
  510.                 break;
  511.             }
  512.         switch (action2) {              /* secondary action */
  513.             case MRM:                   /* generate modrm for later use */
  514.                 if (!mrm) {             /* ignore if it has been generated */
  515.                     DIdoModrm(dp, &pchModrmBuf, segOvr, fEAout);
  516.                                         /* generate modrm */
  517.                     mrm = TRUE;         /* remember its generation */
  518.                     }
  519.                 break;
  520.  
  521.             case COM:                   /* insert a comma after operand */
  522.                 *pchOperandBuf++ = ',';
  523.                 break;
  524.  
  525.             case END:                   /* end of instruction */
  526.                 end = TRUE;
  527.                 break;
  528.             }
  529.  } while (!end);                        /* loop til end of instruction */
  530.  
  531. /*****       prepare disassembled instruction for output              *****/
  532.  
  533.     instlen = pMem - membuf;
  534.  
  535.     if (instlen < cBytes)
  536.         cBytes = instlen;
  537.  
  538.     OutputHexCode(&pchResultBuf, membuf, cBytes);
  539.  
  540.     if (instlen > cBytes) {
  541.         *pchResultBuf++ = '?';
  542.         *pchResultBuf++ = '?';
  543.         (*pOffset)++;                   //  point past unread byte
  544.         }
  545.  
  546.     *pOffset += instlen;                /* set instruction length */
  547.  
  548.     if (instlen > cBytes) {
  549.         do
  550.             *pchResultBuf++ = ' ';
  551.         while (pchResultBuf < pchDst + OBOFFSET);
  552.         OutputString(&pchResultBuf, "???");
  553.         *pchResultBuf++ = '\0';
  554.         return FALSE;
  555.         }
  556.  
  557.     //  if fEAout is set, build each EA with trailing space in EABuf
  558.     //  point back over final trailing space if buffer nonnull
  559.  
  560.     if (fEAout) {
  561.  
  562.         for (indx = 0; indx < 2; indx++)
  563.             if (EAsize[indx]) {
  564.                 OutputString(&pchEABuf, segOvr ? distbl[segOvr].instruct
  565.                                                : pchEAseg[indx]);
  566.                 OutputHexAddr(&pchEABuf, EAaddr[indx]);
  567.                 *pchEABuf++ = '=';
  568.  
  569.                 DoMemoryRead( dp,
  570.                               (LPVOID)EAaddr[indx],
  571.                               (LPVOID)membuf,
  572.                               EAsize[indx],
  573.                               &tmp
  574.                             );
  575.  
  576.                 if (tmp == EAsize[indx])
  577.                     OutputHexString(&pchEABuf, (char *)membuf,
  578.                                                EAsize[indx]);
  579.                 else
  580.                     while (EAsize[indx]--) {
  581.                         *pchEABuf++ = '?';
  582.                         *pchEABuf++ = '?';
  583.                         }
  584.                 *pchEABuf++ = ' ';
  585.                 }
  586.         if (pchEABuf != EABuffer)
  587.             pchEABuf--;
  588.         }
  589.  
  590.     //  compute lengths of component strings.
  591.     //  if the rep string is nonnull,
  592.     //      add the opcode string length to the operand
  593.     //      make the rep string the opcode string
  594.  
  595.     cbOffset = pchResultBuf - pchDst;
  596.     cbOperand = pchOperandBuf - OperandBuffer;
  597.     cbOpcode = pchOpcodeBuf - OpcodeBuffer;
  598.     if (pchRepPrefixBuf != RepPrefixBuffer) {
  599.         cbOperand += cbOpcode + (cbOperand != 0);
  600.         cbOpcode = pchRepPrefixBuf - RepPrefixBuffer;
  601.         }
  602.     cbEAddr = pchEABuf - EABuffer;
  603.  
  604.     //  for really long strings, where the opcode and operand
  605.     //      will not fit on a 77-character line, make two lines
  606.     //      with the opcode on offset 0 on the second line with
  607.     //      the operand following after one space
  608.  
  609.     if (cbOpcode + cbOperand > OBLINEEND - 1) {
  610.         fTwoLines = TRUE;
  611.         obOpcode = 0;
  612.         obOperand = cbOpcode + 1;
  613.         }
  614.     else {
  615.  
  616.         //  compute the minimum and maximum offset values for
  617.         //      opcode and operand strings.
  618.         //  if strings are nonnull, add extra for separating space
  619.  
  620.         obOpcodeMin = cbOffset + 1;
  621.         obOperandMin = obOpcodeMin + cbOpcode + 1;
  622.         obOperandMax = OBLINEEND - cbEAddr - (cbEAddr != 0) - cbOperand;
  623.         obOpcodeMax = obOperandMax - (cbOperand != 0) - cbOpcode;
  624.  
  625.         //  if minimum offset is more than the maximum, the strings
  626.         //      will not fit on one line.  recompute the min/max
  627.         //      values with no offset and EA strings.
  628.  
  629.         if (obOpcodeMin > obOpcodeMax) {
  630.             fTwoLines = TRUE;
  631.             obOpcodeMin = 0;
  632.             obOperandMin = cbOpcode + 1;
  633.             obOperandMax = OBLINEEND - cbOperand;
  634.             obOpcodeMax = obOperandMax - (cbOperand != 0) - cbOpcode;
  635.             }
  636.  
  637.         //  compute the opcode and operand offsets.  set offset as
  638.         //      close to the default values as possible.
  639.  
  640.         if (obOpcodeMin > OBOFFSET)
  641.             obOpcode = obOpcodeMin;
  642.         else if (obOpcodeMax < OBOFFSET)
  643.             obOpcode = obOpcodeMax;
  644.  
  645.         obOperandMin = obOpcode + cbOpcode + 1;
  646.  
  647.         if (obOperandMin > OBOPERAND)
  648.             obOperand = obOperandMin;
  649.         else if (obOperandMax < OBOPERAND)
  650.             obOperand = obOperandMax;
  651.         }
  652.  
  653.     //  build the resultant string with the offsets computed
  654.  
  655.     //  if two lines are to be output,
  656.     //      append the EAddr string
  657.     //      output a new line and reset the pointer
  658.  
  659.     if (fTwoLines) {
  660.         if (pchEABuf != EABuffer) {
  661.             do
  662.                 *pchResultBuf++ = ' ';
  663.             while (pchResultBuf < pchDst + OBLINEEND - cbEAddr);
  664.             *pchEABuf = '\0';
  665.             OutputString(&pchResultBuf, EABuffer);
  666.             OutputString(&pchResultBuf, "\n        ");
  667.             }
  668.         pchDst = pchResultBuf;
  669.         }
  670.  
  671.     //  output rep, opcode, and operand strings
  672.  
  673.     do
  674.         *pchResultBuf++ = ' ';
  675.     while (pchResultBuf < pchDst + obOpcode);
  676.  
  677.     if (pchRepPrefixBuf != RepPrefixBuffer) {
  678.         *pchRepPrefixBuf = '\0';
  679.         OutputString(&pchResultBuf, RepPrefixBuffer);
  680.         do
  681.             *pchResultBuf++ = ' ';
  682.         while (pchResultBuf < pchDst + obOperand);
  683.         }
  684.  
  685.     *pchOpcodeBuf = '\0';
  686.     OutputString(&pchResultBuf, OpcodeBuffer);
  687.  
  688.     if (pchOperandBuf != OperandBuffer) {
  689.         do
  690.             *pchResultBuf++ = ' ';
  691.         while (pchResultBuf < pchDst + obOperand);
  692.         *pchOperandBuf = '\0';
  693.         OutputString(&pchResultBuf, OperandBuffer);
  694.         }
  695.  
  696.     //  if one line is to be output, append the EAddr string
  697.  
  698.     if (!fTwoLines && pchEABuf != EABuffer) {
  699.         *pchEABuf = '\0';
  700.         do
  701.             *pchResultBuf++ = ' ';
  702.         while (pchResultBuf < pchDst + OBLINEEND - cbEAddr);
  703.         OutputString(&pchResultBuf, EABuffer);
  704.         }
  705.  
  706.     *pchResultBuf = '\0';
  707.     return TRUE;
  708. }
  709.  
  710. /*...........................internal function..............................*/
  711. /*                                                                          */
  712. /*                       generate a mod/rm string                           */
  713. /*                                                                          */
  714.  
  715. void
  716. DIdoModrm (PDEBUGPACKET dp, char **ppchBuf, int segOvr, BOOLEAN fEAout)
  717. {
  718.     int     mrm;                        /* modrm byte */
  719.     char    *src;                       /* source string */
  720.     int     sib;
  721.     int     ss;
  722.     int     ind;
  723.     int     oldrm;
  724.  
  725.     mrm = *pMem++;                      /* get the mrm byte from instruction */
  726.     mod = BIT76(mrm);                   /* get mod */
  727.     ttt = BIT53(mrm);                   /* get reg - used outside routine */
  728.     rm  = BIT20(mrm);                   /* get rm */
  729.  
  730.     if (mod == 3) {                     /* register only mode */
  731.         src = ®tab[rm * 2];          /* point to 16-bit register */
  732.         if (EAsize[0] > 1) {
  733.             src += 16;                  /* point to 16-bit register */
  734.             if (opsize_32 && !fMovX)
  735.                 *(*ppchBuf)++ = 'e';    /* make it a 32-bit register */
  736.             }
  737.         *(*ppchBuf)++ = *src++;         /* copy register name */
  738.         *(*ppchBuf)++ = *src;
  739.         EAsize[0] = 0;                  //  no EA value to output
  740.         return;
  741.         }
  742.  
  743.     if (mode_32) {                      /* 32-bit addressing mode */
  744.         oldrm = rm;
  745.         if (rm == 4) {                  /* rm == 4 implies sib byte */
  746.             sib = *pMem++;              /* get s_i_b byte */
  747.             rm = BIT20(sib);            /* return base */
  748.             }
  749.  
  750.         *(*ppchBuf)++ = '[';
  751.         if (mod == 0 && rm == 5) {
  752.             OutputSymbol(dp,ppchBuf, pMem, 4, segOvr); // offset
  753.             pMem += 4;
  754.             }
  755.         else {
  756.             if (fEAout) {
  757.                 if (segOvr) {
  758.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]);
  759.                     pchEAseg[0] = distbl[segOvr].instruct;
  760.                     }
  761.                 else if (reg32[rm] == REGEBP || reg32[rm] == REGESP) {
  762.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]);
  763.                     pchEAseg[0] = dszSS_;
  764.                     }
  765.                 else
  766.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]);
  767.                 }
  768.             OutputString(ppchBuf, mrmtb32[rm]);
  769.             }
  770.  
  771.         if (oldrm == 4) {               //  finish processing sib
  772.             ind = BIT53(sib);
  773.             if (ind != 4) {
  774.                 *(*ppchBuf)++ = '+';
  775.                 OutputString(ppchBuf, mrmtb32[ind]);
  776.                 ss = 1 << BIT76(sib);
  777.                 if (ss != 1) {
  778.                     *(*ppchBuf)++ = '*';
  779.                     *(*ppchBuf)++ = (char)(ss + '0');
  780.                     }
  781.                 if (fEAout)
  782.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg32[ind]);
  783.                 }
  784.             }
  785.         }
  786.     else {                              //  16-bit addressing mode
  787.         *(*ppchBuf)++ = '[';
  788.         if (mod == 0 && rm == 6) {
  789.             OutputSymbol(dp,ppchBuf, pMem, 2, segOvr);   // 16-bit offset
  790.             pMem += 2;
  791.             }
  792.         else {
  793.             if (fEAout) {
  794.                 if (segOvr) {
  795.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]);
  796.                     pchEAseg[0] = distbl[segOvr].instruct;
  797.                     }
  798.                 else if (reg16[rm] == REGEBP) {
  799.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]);
  800.                     pchEAseg[0] = dszSS_;
  801.                     }
  802.                 else
  803.                     EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]);
  804.                 if (rm < 4)
  805.                     EAaddr[0] += (DWORD)GetRegValue(dp, reg16_2[rm]);
  806.             }
  807.             OutputString(ppchBuf, mrmtb16[rm]);
  808.             }
  809.         }
  810.  
  811.     //  output any displacement
  812.  
  813.     if (mod == 1) {
  814.         if (fEAout)
  815.             EAaddr[0] += (ULONG)pMem;
  816.         OutputHexValue(ppchBuf, pMem, 1, TRUE);
  817.         pMem++;
  818.         }
  819.     else if (mod == 2) {
  820.         long tmp = 0;
  821.         if (mode_32) {
  822.             memmove(&tmp,pMem,sizeof(long));
  823.             if (fEAout)
  824.                 EAaddr[0] += (ULONG)tmp;
  825.             OutputHexValue(ppchBuf, pMem, 4, TRUE);
  826.             pMem += 4;
  827.             }
  828.         else {
  829.             memmove(&tmp,pMem,sizeof(short));
  830.             if (fEAout)
  831.                 EAaddr[0] += tmp;
  832.             OutputHexValue(ppchBuf, pMem, 2, TRUE);
  833.             pMem += 2;
  834.             }
  835.         }
  836.  
  837.     if (!mode_32 && fEAout) {
  838.         EAaddr[0] &= 0xffff;
  839.         EAaddr[1] &= 0xffff;
  840.     }
  841.  
  842.     *(*ppchBuf)++ = ']';
  843. }
  844.  
  845. /*** OutputHexValue - output hex value
  846. *
  847. *   Purpose:
  848. *       Output the value pointed by *ppchBuf of the specified
  849. *       length.  The value is treated as signed and leading
  850. *       zeroes are not printed.  The string is prefaced by a
  851. *       '+' or '-' sign as appropriate.
  852. *
  853. *   Input:
  854. *       *ppchBuf - pointer to text buffer to fill
  855. *       *pchMemBuf - pointer to memory buffer to extract value
  856. *       length - length in bytes of value (1, 2, and 4 supported)
  857. *       fDisp - set if displacement to output '+'
  858. *
  859. *   Output:
  860. *       *ppchBuf - pointer updated to next text character
  861. *
  862. *************************************************************************/
  863.  
  864. void OutputHexValue (char **ppchBuf, char *pchMemBuf, int length, int fDisp)
  865. {
  866.     long    value;
  867.     int     index;
  868.     char    digit[8];
  869.  
  870.     value = 0;
  871.     if (length == 1)
  872.         value = (long)(*(char *)pchMemBuf);
  873.     else if (length == 2)
  874.         memmove(&value,pchMemBuf,2);
  875.     else
  876.         memmove(&value,pchMemBuf,sizeof(long));
  877.  
  878.     length <<= 1;               //  shift once to get hex length
  879.  
  880.     if (value != 0 || !fDisp) {
  881.         if (fDisp)
  882.             if (value < 0 && length == 2) {   //  use neg value for byte
  883.                 value = -value;               //    displacement
  884.                 *(*ppchBuf)++ = '-';
  885.                 }
  886.             else
  887.                 *(*ppchBuf)++ = '+';
  888.  
  889.         *(*ppchBuf)++ = '0';
  890.         *(*ppchBuf)++ = 'x';
  891.         for (index = length - 1; index != -1; index--) {
  892.             digit[index] = (char)(value & 0xf);
  893.             value >>= 4;
  894.             }
  895.         index = 0;
  896.         while (digit[index] == 0 && index < length - 1)
  897.             index++;
  898.         while (index < length)
  899.             *(*ppchBuf)++ = hexdigit[digit[index++]];
  900.         }
  901. }
  902.  
  903. /*** OutputHexString - output hex string
  904. *
  905. *   Purpose:
  906. *       Output the value pointed by *ppchMemBuf of the specified
  907. *       length.  The value is treated as unsigned and leading
  908. *       zeroes are printed.
  909. *
  910. *   Input:
  911. *       *ppchBuf - pointer to text buffer to fill
  912. *       *pchValue - pointer to memory buffer to extract value
  913. *       length - length in bytes of value
  914. *
  915. *   Output:
  916. *       *ppchBuf - pointer updated to next text character
  917. *       *ppchMemBuf - pointer update to next memory byte
  918. *
  919. *************************************************************************/
  920.  
  921. void
  922. OutputHexString (char **ppchBuf, char *pchValue, int length)
  923. {
  924.     unsigned char    chMem;
  925.  
  926.     pchValue += length;
  927.     while (length--) {
  928.         chMem = *--pchValue;
  929.         *(*ppchBuf)++ = hexdigit[chMem >> 4];
  930.         *(*ppchBuf)++ = hexdigit[chMem & 0x0f];
  931.         }
  932. }
  933.  
  934. /*** OutputHexCode - output hex code
  935. *
  936. *   Purpose:
  937. *       Output the code pointed by pchMemBuf of the specified
  938. *       length.  The value is treated as unsigned and leading
  939. *       zeroes are printed.  This differs from OutputHexString
  940. *       in that bytes are printed from low to high addresses.
  941. *
  942. *   Input:
  943. *       *ppchBuf - pointer to text buffer to fill
  944. *       pchMemBuf - pointer to memory buffer to extract value
  945. *       length - length in bytes of value
  946. *
  947. *   Output:
  948. *       *ppchBuf - pointer updated to next text character
  949. *
  950. *************************************************************************/
  951.  
  952. void
  953. OutputHexCode (char **ppchBuf, char *pchMemBuf, int length)
  954. {
  955.     unsigned char    chMem;
  956.  
  957.     while (length--) {
  958.         chMem = *pchMemBuf++;
  959.         *(*ppchBuf)++ = hexdigit[chMem >> 4];
  960.         *(*ppchBuf)++ = hexdigit[chMem & 0x0f];
  961.         }
  962. }
  963.  
  964. /*** OutputString - output string
  965. *
  966. *   Purpose:
  967. *       Copy the string into the buffer pointed by *ppBuf.
  968. *
  969. *   Input:
  970. *       *pStr - pointer to string
  971. *
  972. *   Output:
  973. *       *ppBuf points to next character in buffer.
  974. *
  975. *************************************************************************/
  976.  
  977. void
  978. OutputString (char **ppBuf, char *pStr)
  979. {
  980.     while (*pStr)
  981.         *(*ppBuf)++ = *pStr++;
  982. }
  983.  
  984. /*** OutputSymbol - output symbolic value
  985. *
  986. *   Purpose:
  987. *       Output the value in outvalue into the buffer
  988. *       pointed by *pBuf.  Express the value as a
  989. *       symbol plus displacment, if possible.
  990. *
  991. *   Input:
  992. *       *ppBuf - pointer to text buffer to fill
  993. *       *pValue - pointer to memory buffer to extract value
  994. *       length - length in bytes of value
  995. *
  996. *   Output:
  997. *       *ppBuf - pointer updated to next text character
  998. *
  999. *************************************************************************/
  1000.  
  1001. void
  1002. OutputSymbol (PDEBUGPACKET dp, char **ppBuf, char *pValue, int length, int segOvr)
  1003. {
  1004.     ULONG               displacement;
  1005.     ULONG               value;
  1006.     char                *szSymName;
  1007.  
  1008.     value = 0;
  1009.     if (length == 1)
  1010.         value = (long)(*(char *)pValue);
  1011.     else if (length == 2)
  1012.         memmove(&value,pValue,sizeof(short));
  1013.     else
  1014.         memmove(&value,pValue,sizeof(long));
  1015.  
  1016.     EAaddr[0] = value;
  1017.  
  1018.     if (SymGetSymFromAddr( dp->hProcess, value, &displacement, sym )) {
  1019.         szSymName = sym->Name;
  1020.         OutputString(ppBuf, szSymName);
  1021.         OutputHexValue(ppBuf, (char *)&displacement, length, TRUE);
  1022.         *(*ppBuf)++ = ' ';
  1023.         *(*ppBuf)++ = '(';
  1024.         OutputHexString(ppBuf, pValue, length);
  1025.         *(*ppBuf)++ = ')';
  1026.         }
  1027.     else
  1028.         OutputHexString(ppBuf, pValue, length);
  1029. }
  1030.  
  1031. /*** X86GetNextOffset - compute offset for trace or step
  1032. *
  1033. *   Purpose:
  1034. *       From a limited disassembly of the instruction pointed
  1035. *       by the FIR register, compute the offset of the next
  1036. *       instruction for either a trace or step operation.
  1037. *
  1038. *   Input:
  1039. *       fStep - TRUE if step offset returned - FALSE for trace offset
  1040. *
  1041. *   Returns:
  1042. *       step or trace offset if input is TRUE or FALSE, respectively
  1043. *       -1 returned for trace flag to be used
  1044. *
  1045. *************************************************************************/
  1046.  
  1047. void
  1048. GetNextOffset (PDEBUGPACKET dp, PULONG pcaddr, BOOLEAN fStep)
  1049. {
  1050.     int     mode_32;
  1051.     int     opsize_32;
  1052.     int     cBytes;
  1053.     char    membuf[MAXL];               //  current instruction buffer
  1054.     ULONG   addrReturn;
  1055.     USHORT  retAddr[3];                 //  return address buffer
  1056.     char    *pMem;
  1057.     UCHAR   opcode;
  1058.     int     fPrefix = TRUE;
  1059.     int     fRepPrefix = FALSE;
  1060.     int     ttt;
  1061.     int     rm;
  1062.     ULONG   instroffset;
  1063.  
  1064.     //  read instruction stream bytes into membuf and set mode and
  1065.     //      opcode size flags
  1066.  
  1067.     *pcaddr = (DWORD)GetRegValue(dp,REGEIP);
  1068.     instroffset = *pcaddr;
  1069.     G_mode_32 = TRUE;
  1070.     mode_32 = opsize_32 = (G_mode_32 == 1); /* local addressing mode */
  1071.     DoMemoryRead( dp,
  1072.                   (LPVOID)*pcaddr,
  1073.                   (LPVOID)membuf,
  1074.                   MAXL,
  1075.                   &cBytes
  1076.                 );
  1077.                                         /* move full inst to local buffer */
  1078.     pMem = membuf;                      /* point to begin of instruction */
  1079.  
  1080.     //  read and process any prefixes first
  1081.  
  1082.     do {
  1083.         opcode = (UCHAR)*pMem++;        /* get opcode */
  1084.         if (opcode == 0x66)
  1085.             opsize_32 = !G_mode_32;
  1086.         else if (opcode == 0x67)
  1087.             mode_32 = !G_mode_32;
  1088.         else if ((opcode & ~1) == 0xf2)
  1089.             fRepPrefix = TRUE;
  1090.         else if (opcode != 0xf0 && (opcode & ~0x18) != 0x26
  1091.                                 && (opcode & ~1) != 0x64)
  1092.             fPrefix = FALSE;
  1093.         }
  1094.     while (fPrefix);
  1095.  
  1096.     //  for instructions that alter the TF (trace flag), return the
  1097.     //      offset of the next instruction despite the flag of fStep
  1098.  
  1099.     if (((opcode & ~0x3) == 0x9c))
  1100.         //  9c-9f, pushf, popf, sahf, lahf
  1101.         ;
  1102.  
  1103.     else if (opcode == 0xcf) {          //  cf - iret - get RA from stack
  1104.  
  1105.         addrReturn = (DWORD)GetRegValue(dp, REGESP);
  1106.         DoMemoryRead( dp,
  1107.                       (LPVOID)addrReturn,
  1108.                       (LPVOID)retAddr,
  1109.                       sizeof(retAddr),
  1110.                       NULL
  1111.                     );
  1112.  
  1113.         *pcaddr = retAddr[2];
  1114.         return;
  1115.         }
  1116.  
  1117.     //  repeated string/port instructions
  1118.  
  1119.     if (opcode == 0xe8)            //  near direct jump
  1120.         pMem += (1 + opsize_32) * 2;
  1121.  
  1122.     else if (opcode == 0x9a)            //  far direct jump
  1123.         pMem += (2 + opsize_32) * 2;
  1124.  
  1125.     else if (opcode == 0xcd ||
  1126.              (opcode >= 0xe0 && opcode <= 0xe2)) //  loop / int nn instrs
  1127.         pMem++;
  1128.  
  1129.     else if (opcode == 0xff) {          //  indirect call - compute length
  1130.         opcode = *pMem++;               //  get modRM
  1131.         ttt = BIT53(opcode);
  1132.         if ((ttt & ~1) == 2) {
  1133.             mod = BIT76(opcode);
  1134.             if (mod != 3) {                     //  nonregister operand
  1135.                 rm = BIT20(opcode);
  1136.                 if (mode_32) {
  1137.                     if (rm == 4)
  1138.                         rm = BIT20(*pMem++);    //  get base from SIB
  1139.                     if (mod == 0) {
  1140.                         if (rm == 5)
  1141.                             pMem += 4;          //  long direct address
  1142.                         }                       //  else register
  1143.                     else if (mod == 1)
  1144.                         pMem++;                 //  register with byte offset
  1145.                     else
  1146.                         pMem += 4;              //  register with long offset
  1147.                     }
  1148.                 else {                          //  16-bit mode
  1149.                     if (mod == 0) {
  1150.                         if (rm == 6)
  1151.                             pMem += 2;          //  short direct address
  1152.                         }
  1153.                     else
  1154.                         pMem += mod;            //  reg, byte, word offset
  1155.                     }
  1156.                 }
  1157.             }
  1158.         else
  1159.             instroffset = (ULONG)-1;            //  0xff, but not call
  1160.         }
  1161.  
  1162.     else if (!((fRepPrefix && ((opcode & ~3) == 0x6c ||
  1163.                                (opcode & ~3) == 0xa4 ||
  1164.                                (opcode & ~1) == 0xaa ||
  1165.                                (opcode & ~3) == 0xac)) ||
  1166.                                opcode == 0xcc || opcode == 0xce))
  1167.         instroffset = (ULONG)-1;                //  not repeated string op
  1168.                                                 //  or int 3 / into
  1169.  
  1170.     //  if not enough bytes were read for instruction parse,
  1171.     //      just give up and trace the instruction
  1172.  
  1173.     if (cBytes < pMem - membuf)
  1174.         instroffset = (ULONG)-1;
  1175.  
  1176.     //  if not tracing, compute the new instruction offset
  1177.  
  1178.     if (instroffset != (ULONG)-1)
  1179.         instroffset += pMem - membuf;
  1180.  
  1181.     *pcaddr = instroffset;
  1182. }
  1183.  
  1184. void
  1185. OutputHexAddr (PUCHAR *ppBuffer, ULONG offset)
  1186. {
  1187.     OutputHexString(ppBuffer, (char *)&offset, sizeof(ULONG));
  1188. }
  1189.  
  1190. USHORT
  1191. GetSegRegValue (PDEBUGPACKET dp, int segOpcode)
  1192. {
  1193.     ULONG    regnum;
  1194.  
  1195.     switch (segOpcode) {
  1196.         case 0x26:
  1197.             regnum = REGES;
  1198.             break;
  1199.         case 0x2e:
  1200.             regnum = REGCS;
  1201.             break;
  1202.         case 0x36:
  1203.             regnum = REGSS;
  1204.             break;
  1205.         case 0x64:
  1206.             regnum = REGFS;
  1207.             break;
  1208.         case 0x65:
  1209.             regnum = REGGS;
  1210.             break;
  1211.         case 0x3e:
  1212.         default:
  1213.             regnum = REGDS;
  1214.         }
  1215.  
  1216.     return (USHORT)GetRegValue(dp,regnum);
  1217. }
  1218.