using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lomont.Utils;
using System.Diagnostics;

namespace Lomont.Binary.X86
    {
    /// <summary>
    /// Class to decode a single instruction. 
    /// </summary>
    public class X86InstructionDecoder
        {
        // Note - make this class usable across many threads, to prevent having 
        // to make one for each decoded instruction. This requires passing state 
        // around internally.

        // TODO - test by throwing random byte sequences at this and see how crashes.


        /// <summary>
        /// Return the instruction if its valid, else return null. 
        /// </summary>
        /// <returns>The instruction if valid, else null.</returns>
        public X86Instruction DecodeInstruction(X86DecodeContext context)
            {
            // todo - wrap in try block, catch out of bounds exceptions, among others?
            // todo - clean up messaging - needs to be simpler and cleaner to use

            context.Instruction = new X86Instruction();

            var instruction = context.Instruction;
            instruction.StartAddress = context.Data.DataIndex;

            // to detect opcode, try, and if fail, see if prefix can be eaten, then try again
            X86InstructionDefinition def = null;
            do
                {
                // get anything matching these bytes
                def = context.tables.MatchOpcode(context);
                if (def == null)
                    { // did not get one, try to eat a prefix
                    if (!DecodePrefixNew(context))
                        {// cannot process this
                        context.OnError("Cannot match an opcode at {0}", instruction.StartAddress);
                        return null;
                        }
                    }
                } while (def == null);

            context.OnInfo("Matched {0} {1} {2} {3}", def.Mnenomic, def.Dst, def.Src, def.Op3);
            instruction.Definition = def;

            // decode opcode
            if (!DecodeOpcode(context))
                return null;

            // todo - check any prefixes are allowed on the current instruction before returning

            return instruction; // success!
            }


        /// <summary>
        /// Decode the prefixes for the instruction, updating index as needed.
        /// Some prefixes are also used as mandatory prefixes for opcodes.
        /// </summary>
        /// <param name="operandText">The operandText to use</param>
        /// <returns>true on prefix eaten, else false</returns>
        bool DecodePrefixNew(X86DecodeContext context)
            {
            if (context.Data.BytesLeft <= 0)
                return false; // nothing more to do

            // decode prefix, at most one per group. TODO - what if multiple ones? legal?
            int group1 = 0, group2 = 0, group3 = 0, group4 = 0; // todo - check prefixes already eaten when adding?
            ulong start = context.Data.DataIndex; // save instruction start location for error disasm
            bool notPrefix = false;  // assume prefix
            var instruction = context.Instruction;
            Action<X86Register> Group2 = (reg) =>
            {
                instruction.SegmentOverride = reg;
                if (group2++ > 0)
                    context.OnWarning("Too many group 2 prefixes at {0}", start);
            };

            var possiblePrefix = (X86Instruction.Prefix)context.Data.DataByte(0);
            switch (possiblePrefix)
                {
                // group 1
                case X86Instruction.Prefix.LOCK:
                case X86Instruction.Prefix.REPNZ:
                case X86Instruction.Prefix.REPZ:
                    if (group1++ > 0)
                        context.OnWarning("Too many group 1 prefixes at {0}", start);
                    break;

                // group 2
                case X86Instruction.Prefix.OverrideCS: // same as branch no hint on jump
                    Group2(X86Register.CS);
                    break;
                case X86Instruction.Prefix.OverrideSS:
                    Group2(X86Register.SS);
                    break;
                case X86Instruction.Prefix.OverrideDS: // same as branch yes hint on jump
                    Group2(X86Register.DS);
                    break;
                case X86Instruction.Prefix.OverrideES:
                    Group2(X86Register.ES);
                    break;
                case X86Instruction.Prefix.OverrideFS:
                    Group2(X86Register.FS);
                    break;
                case X86Instruction.Prefix.OverrideGS:
                    Group2(X86Register.GS);
                    break;

                // group 3
                case X86Instruction.Prefix.OperandSizeOverride:
                    if (group3++ > 0)
                        context.OnWarning("Too many group 3 prefixes at {0}", start);
                    context.Instruction.OperandSizeOverridden = true;
                    break;

                // group 4
                case X86Instruction.Prefix.AddressSizeOverride:
                    if (group4++ > 0)
                        context.OnWarning("Too many group 4 prefixes at {0}", start);
                    context.Instruction.AddressSizeOverriden = true;
                    break;
                default:
                    notPrefix = true; // not a prefix
                    break;
                }
            if (!notPrefix)
                { // was a prefix - add it. Note these are saved in the order seen in memory
                byte val = context.Data.NextByte();
                context.Instruction.Prefixes.Add((X86Instruction.Prefix)val);
                instruction.Data.Add(val);
                return true; // saw prefix
                }
            return false; // did not see prefix
            } // DecodePrefix


        /// <summary>
        /// Decode all prefixes for the instruction, updating index as needed.
        /// Some prefixes are also used as mandatory prefixes for opcodes.
        /// </summary>
        /// <param name="operandText">The operandText to use</param>
        /// <returns>true on success, else false on error</returns>
        bool DecodePrefixesOld(X86DecodeContext context)
            {
            // decode prefixes, at most one per group. TODO - what if multiple ones? legal?
            int group1 = 0, group2 = 0, group3 = 0, group4 = 0;
            ulong start = context.Data.DataIndex; // save instruction start location for error disasm
            bool done = false; // loop until no more prefixes seen
            var instruction = context.Instruction;
            Action<X86Register> Group2 = (reg) =>
                {
                    instruction.SegmentOverride = reg;
                    if (group2++ > 0)
                        context.OnWarning("Too many group 2 prefixes at {0}", start);
                };

            do
                {
                var possiblePrefix = (X86Instruction.Prefix)context.Data.DataByte(0);
                switch (possiblePrefix)
                    {
                    // group 1
                    case X86Instruction.Prefix.LOCK:
                    case X86Instruction.Prefix.REPNZ:
                    case X86Instruction.Prefix.REPZ:
                        if (group1++ > 0)
                            context.OnWarning("Too many group 1 prefixes at {0}", start);
                        break;

                    // group 2
                    case X86Instruction.Prefix.OverrideCS: // same as branch no hint on jump
                        Group2(X86Register.CS);
                        break;
                    case X86Instruction.Prefix.OverrideSS:
                        Group2(X86Register.SS);
                        break;
                    case X86Instruction.Prefix.OverrideDS: // same as branch yes hint on jump
                        Group2(X86Register.DS);
                        break;
                    case X86Instruction.Prefix.OverrideES:
                        Group2(X86Register.ES);
                        break;
                    case X86Instruction.Prefix.OverrideFS:
                        Group2(X86Register.FS);
                        break;
                    case X86Instruction.Prefix.OverrideGS:
                        Group2(X86Register.GS);
                        break;

                    // group 3
                    case X86Instruction.Prefix.OperandSizeOverride:
                        if (group3++ > 0)
                            context.OnWarning("Too many group 3 prefixes at {0}", start);
                        context.Instruction.OperandSizeOverridden = true;
                        break;

                    // group 4
                    case X86Instruction.Prefix.AddressSizeOverride:
                        if (group4++ > 0)
                            context.OnWarning("Too many group 4 prefixes at {0}", start);
                        context.Instruction.AddressSizeOverriden = true;
                        break;
                    default:
                        done = true; // not a prefix
                        break;
                    }
                if (!done)
                    { // was a prefix - add it. Note these are saved in the order seen in memory
                    byte val = context.Data.NextByte();
                    context.Instruction.Prefixes.Add((X86Instruction.Prefix)val);
                    instruction.Data.Add(val);

                    }
                } while (!done);
            return true;
            } // DecodePrefixes

        /// <summary>
        /// Decode the opcode, including any mandatory prefixes and REX flags
        /// updating index as needed. Also parses any needed displacement and immediate values.
        /// </summary>
        /// <param name="context">The decoding context</param>
        /// <returns>true on success, else false on error</returns>
        bool DecodeOpcode(X86DecodeContext context)
            {

            var instruction = context.Instruction;

            var def = instruction.Definition;

            // see if the Mod R/M byte (and possible SIB) is needed. Todo- make entry in opcode table for this.
            bool hasModRM = HasModRM(context, def.Src);
            if (!hasModRM)
                hasModRM |= HasModRM(context, def.Dst);
            if (!hasModRM)
                hasModRM |= HasModRM(context, def.Op3);
            if ((instruction.Definition.Extension.HasValue) || (instruction.Definition.Mod3.HasValue) || (instruction.Definition.RM.HasValue))
                hasModRM = true;

            // read the ModR/M byte (and possible SIB bytes read if needed)
            if (hasModRM)
                ReadModRM(context);

            // parse operand fields to determine what else needs done. This fills in some 
            // operand fields, but does not read displacement and immediate values yet.
            if (!ParseOperand(context, def.Src, context.Instruction.SourceOperand))
                return false;
            if (!ParseOperand(context, def.Dst, context.Instruction.DestinationOperand))
                return false;
            if (!ParseOperand(context, def.Op3, context.Instruction.ThirdOperand))
                return false;

            // decode displacement if needed. There can be at most one operand requesting a displacement.
            int dCount = 0;
            if (instruction.SourceOperand.HasDisplacement)
                ++dCount;
            if (instruction.DestinationOperand.HasDisplacement)
                ++dCount;
            if (instruction.ThirdOperand.HasDisplacement)
                ++dCount;
            if (dCount > 1)
                {
                context.OnError("Too many displacements needed");
                return false;
                }

            if ((instruction.SourceOperand.HasDisplacement) && !DecodeDisplacement(context, instruction.SourceOperand))
                return false; // failed
            if ((instruction.DestinationOperand.HasDisplacement) && !DecodeDisplacement(context, instruction.DestinationOperand))
                return false; // failed
            if ((instruction.ThirdOperand.HasDisplacement) && !DecodeDisplacement(context, instruction.ThirdOperand))
                return false; // failed

            // decode immediate if needed. There can be at most one operand requesting an immediate value.
            int iCount = 0;
            if (instruction.SourceOperand.HasImmediate)
                ++iCount;
            if (instruction.DestinationOperand.HasImmediate)
                ++iCount;
            if (instruction.ThirdOperand.HasImmediate)
                ++iCount;
            if (iCount > 1)
                {	// NOTE: ENTER Iw,Ib has two immediate operands - allow this. Do other ones have this also?
                if ((instruction.Mnemonic != X86Mnemonic.ENTER) || (iCount != 2))
                    {
                    context.OnError("Too many immediate operands needed");
                    return false;
                    }
                }

            if ((instruction.SourceOperand.HasImmediate) && !DecodeImmediate(context, instruction.SourceOperand))
                return false; // failed
            if ((instruction.DestinationOperand.HasImmediate) && !DecodeImmediate(context, instruction.DestinationOperand))
                return false; // failed
            if ((instruction.ThirdOperand.HasImmediate) && !DecodeImmediate(context, instruction.ThirdOperand))
                return false; // failed

            return true;
            }

        /// <summary>
        /// Read the ModRM byte (which determines if optional SIB byte also needed) 
        /// Set fields in the context as needed.
        /// </summary>
        /// <param name="context"></param>
        void ReadModRM(X86DecodeContext context)
            {
            var modrm = context.Data.NextByte();
            var instruction = context.Instruction;
            instruction.SetModRM(modrm);
            context.OnInfo("Mod/reg/rm {0:X2}: {1},{2},{3}", modrm, instruction.ModRM.Mod, instruction.ModRM.Reg, instruction.ModRM.RM);
            instruction.Data.Add(modrm);

            // check for SIB byte, and read it if needed.
            if ((context.ProcessorMode.AddressingMode == X86ProcessorMode.BitMode.Bit32) && (instruction.ModRM.Mod != 3) && (instruction.ModRM.RM == 4))
                { // has SIB byte. Load it into the instruction.
                byte sib = context.Data.NextByte();
                instruction.SetSIB(sib);
                context.OnInfo("SIB Scale/Index/Base {0},{1},{2}", instruction.SIB.Scale, instruction.SIB.Index, instruction.SIB.Base);
                instruction.Data.Add(sib);
                }
            }


        /// <summary>
        /// Given an operand definition, see if the ModR/M byte 
        /// (and possible SIB also) is needed.
        /// </summary>
        /// <param name="operandDefinitionText"></param>
        /// <returns></returns>
        bool HasModRM(X86DecodeContext context, string operandDefinitionText)
            {
            if (String.IsNullOrEmpty(operandDefinitionText))
                return false;
            if (RegisterOperand(context, operandDefinitionText, null))
                return false; // is a direct register, no ModRM needed
            // codes for addressing mode - todo - make table indexed? in table creation
            switch (operandDefinitionText[0])
                {
                case 'C': // reg field of ModRM selects a control register
                    return true;
                case 'D': // reg field of ModRM selects a debug register
                    return true;
                case 'E': // has ModRM. 
                    return true;
                case 'G': // reg field of ModRM selects a general register
                    return true;
                case 'M': // the ModRM may only refer to memory - todo
                    return true;
                case 'N': // The R/M field of ModRM byte selects a packed-quadword, MMX register
                    return true;
                case 'O': // no ModRM. The offset of the operand is a word or doubleword
                    return false;
                case 'P': // the reg field of ModRM selects a packed quadword MMX register
                    return true;
                case 'Q': // a ModRM byte follows the opcode and specifies operand.
                    return true;
                case 'R': // the R/M field of ModRM byte may refer only to a general register
                    return true;
                case 'S': // the reg field of ModRM byte selects a segment register
                    return true;
                case 'U': // the reg R/M of ModRM byte selects a 128 bit XMM register
                    return true;
                case 'V': // the reg field of ModRM byte selects a segment register
                    return true;
                case 'W': // a ModRM byte follows, and specifies the operand, which is either a 128 bit XMM or a memory address
                    return true;
                default:
                    // todo - replace those with a direct false answer, such as A, I, etc.
                    //context.disasm.OnError("Unknown operand string {0}", operandDefinitionText); 
                    break;
                }
            return false;
            }

        /// <summary>
        /// Parse the operand according to table A.2.1 Vol2. A missing or blank operandDefinitionText is legal.
        /// Also parses the ModRM and SIB if needed. Upon return index points to immediate and address if any.
        /// </summary>
        /// <returns>true on success, else false</returns>
        bool ParseOperand(X86DecodeContext context, string operandDefinitionText, X86Operand operand)
            {
            if (String.IsNullOrEmpty(operandDefinitionText))
                return true; // nothing to do, assume this is ok (called with empty fields)

            // default to this
            operand.OperandFormat = X86Operand.Format.Empty;

            if (!RegisterOperand(context, operandDefinitionText, operand))
                { // was not a register (todo - tag these in operand tables for rapid lookup)
                var instruction = context.Instruction;

                // operand size to read. This is the size of the action, move, etc., and is usually 1,2, or 4 bytes
                // each section sets an operand size. 
                switch (operandDefinitionText.Substring(1))
                    {
                    case "a": // two one word operands in memory or two double word operands, used only by BOUND
                        context.OnWarning("Unfinished operand type 'a'");
                        break;
                    case "b": // always a byte, no matter what
                        operand.Size = X86Instruction.DataSize.Byte;
                        break;
                    case "bcd": // read a 80 bit BCD value
                        // added since missing in Intel docs, which uses m80bcd sometimes.
                        operand.Size = X86Instruction.DataSize.ExtendedReal;
                        break;
                    case "c": // byte or word, depending on operand-size attribute
                        if (instruction.OperandSizeOverridden)
                            operand.Size = X86Instruction.DataSize.Byte;
                        else
                            operand.Size = X86Instruction.DataSize.Word;
                        context.OnWarning("Unfinished operand type 'c'");
                        break;
                    case "d": // always doubleword
                        operand.Size = X86Instruction.DataSize.Dword;
                        break;
                    case "dr": // double real - 64 bit floating point value
                        // added since missing in Intel docs, needed for floating, see sr,dr,er
                        operand.Size = X86Instruction.DataSize.Qword;
                        break;
                    case "dq": // always double quadword
                        operand.Size = X86Instruction.DataSize.DoubleQword;
                        break;
                    case "er": // extended real - 80 bit floating point value
                        // added since missing in Intel docs, needed for floating, see sr,dr,er
                        operand.Size = X86Instruction.DataSize.ExtendedReal;
                        break;
                    case "p": // 32, 48, or 80 bit pointer, depending on operand attribute
                        instruction.DisplacementSize = X86Instruction.DataSize.Segm16m32;
                        context.OnWarning("Unfinished operand type 'p'");
                        break;
                    case "pd": // 128 bit packed single precision floating point data
                        operand.Size = X86Instruction.DataSize.DoubleQword;
                        break;
                    case "pi": // quadword MMX
                        operand.Size = X86Instruction.DataSize.Qword;
                        break;
                    case "ps": // 128 bit packed single precision
                        operand.Size = X86Instruction.DataSize.DoubleQword;
                        break;
                    case "q": // always quadword
                        operand.Size = X86Instruction.DataSize.Qword;
                        break;
                    case "s": // 6 or 10 byte pseudo-descriptor
                        operand.Size = X86Instruction.DataSize.ExtendedReal;
                        context.OnWarning("Unfinished operand type 's'");
                        break;
                    case "sd": // scalar element of a 128 bit double precision floating data
                        operand.Size = X86Instruction.DataSize.DoubleQword;
                        break;
                    case "sr": // single real - 32 bit floating point value
                        // added since missing in Intel docs, needed for floating, see sr,dr,er
                        operand.Size = X86Instruction.DataSize.Dword;
                        break;
                    case "si": // doubleword integer register, e.g., EAX
                        operand.Size = X86Instruction.DataSize.Dword;
                        context.OnWarning("Unfinished operand type 'si'");
                        break;
                    case "ss": // scalar element 128 bit packed single precision
                        operand.Size = X86Instruction.DataSize.DoubleQword;
                        context.OnWarning("Unfinished operand type 'ss'");
                        break;
                    case "v": // word, doubleword, or (in 64 bit mode) quadword
                        operand.Size = context.ProcessorMode.OperandSize(instruction.OperandSizeOverridden);
                        break;
                    case "w": // always word
                        operand.Size = X86Instruction.DataSize.Word;
                        break;
                    case "y": // doubleword or quadword (in 64 bit mode), depending on operand size attribute
                        operand.Size = X86Instruction.DataSize.Dword;
                        context.OnWarning("Unfinished operand type 'y'");
                        break;
                    case "z": // word for 16 bit operand bitSize or doubleword for 32 or 64 bit bitSize
                        if (instruction.OperandSizeOverridden) // todo - based on context? mode 32 bit, 16 bit, etc?
                            operand.Size = X86Instruction.DataSize.Word;
                        else
                            operand.Size = X86Instruction.DataSize.Dword;
                        context.OnWarning("Unfinished operand type 'z'");
                        break;
                    default:
                        if (operandDefinitionText.Length > 1)
                            context.OnError("Unknown operand string {0}", operandDefinitionText);
                        break;
                    }

                // codes for addresssing mode
                switch (operandDefinitionText[0])
                    {
                    case 'A': // Direct Address, address encoded in instruction
                        operand.OperandFormat = X86Operand.Format.Direct;
                        break;
                    case 'C': // reg field of ModR/M selects a control register
                        context.OnWarning("operand field 'C' unfinished");
                        break;
                    case 'D': // reg field of ModR/M selects a debug register
                        context.OnWarning("operand field 'D' unfinished");
                        break;
                    case 'E': // has ModR/M. Either GPR or register group 
                        context.OnWarning("operand field 'E' unfinished");
                        ProcessModRM(context, operand);
                        //operand.BaseRegister = baseRegisters32[instruction.ModRM.RM];
                        //operand.OperandFormat = X86Operand.Format.Register;
                        break;
                    case 'F': // EFLAGS or RFLAGS register
                        if (context.ProcessorMode.OperationMode == X86ProcessorMode.OperationModes.SixtyFourBit)
                            operand.BaseRegister = X86Register.RFLAGS;
                        else
                            operand.BaseRegister = X86Register.EFLAGS; // todo - 16 bit?
                        break;
                    case 'G': // reg field of ModR/M selects a general register
                        operand.OperandFormat = X86Operand.Format.Register;
                        operand.BaseRegister = ObtainRegister(operand.Size, instruction.ModRM.Reg);
                        break;
                    case 'I': // Immediate data
                        operand.OperandFormat = X86Operand.Format.Immediate;
                        instruction.ImmediateSize = operand.Size;
                        break;
                    case 'J': // instruction has a relative offset to be added to the instruction pointer register
                        // this is a jump, branch, or call instruction
                        operand.OperandFormat = X86Operand.Format.Immediate; // read an immediate value
                        instruction.ImmediateSize = operand.Size;
                        break;
                    case 'M': // the ModRM may only refer to memory - todo
                        context.OnWarning("operand field 'M' unfinished");
                        return ProcessModRM(context, operand);
                    case 'N': // The R/M field of ModRM byte selects a packed-quadword, MMX register
                        break;
                    case 'O': // no ModRM. The offset of the operand is a word or doubleword
                        operand.OperandFormat = X86Operand.Format.Direct;
                        context.OnWarning("operand field 'O' unfinished");
                        instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                        break;
                    case 'P': // the reg field of ModRM selects a packed quadword MMX register
                        operand.OperandFormat = X86Operand.Format.Register;
                        operand.BaseRegister = ObtainRegister(operand.Size, instruction.ModRM.Reg);
                        break;
                    case 'Q': // a ModRM byte follows the opcode and specifies operand.
                        ProcessModRM(context, operand);
                        break;
                    case 'R': // the R/M field of ModRM byte may refer only to a general register
                        break;
                    case 'S': // the reg field of ModRM byte selects a segment register
                        break;
                    case 'U': // the reg R/M of ModRM byte selects a 128 bit XMM register
                        break;
                    case 'V': // the reg field of ModRM byte selects a segment register
                        operand.OperandFormat = X86Operand.Format.Register;
                        operand.BaseRegister = ObtainRegister(operand.Size, instruction.ModRM.Reg);
                        break;
                    case 'W': // a ModRM byte follows, and specifies the operand, which is either a 128 bit XMM or a memory address
                        ProcessModRM(context, operand);
                        break;
                    case 'X': // memory addressed by the DS:rSI pair (MOVS, CMPS, OUTS, LODS)
                        break;
                    case 'Y': // memory addressed by the ES:rDI pair (MOVS, CMPS, INS, STOS, SCAS)
                        break;
                    default:
                        context.OnError("Unknown operand string {0}", operandDefinitionText);
                        break;
                    }
                }

            return true; // success!
            } // ParseOperand


        /// <summary>
        /// Parse the operand string if it is a register.
        /// Return true if was a register, else false.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="operandDefinitionText"></param>
        /// <param name="operand"></param>
        /// <returns></returns>
        bool RegisterOperand(X86DecodeContext context, string operandDefinitionText, X86Operand operand)
            {
            // check for register rules, else send error message
            var instruction = context.Instruction;
            string reg = operandDefinitionText;
            if (reg.Contains('/'))
                reg = reg.Substring(0, reg.IndexOf('/')); // todo - remove cases like rAX/R9 for now, fix later
            if (reg.Length == 3)
                { // see if general register, if so, lookup size
                if ((reg[0] == 'r') || (reg[0] == 'e'))
                    { // 16,32, or 64 (allowed if r) size
                    switch (context.ProcessorMode.OperandSize(instruction.OperandSizeOverridden))
                        {
                        case X86Instruction.DataSize.Word:
                            reg = reg.Substring(1);
                            break;
                        case X86Instruction.DataSize.Dword:
                            reg = "E" + reg.Substring(1);
                            break;
                        case X86Instruction.DataSize.Qword:
                            if (reg[0] == 'r')
                                reg = "R" + reg.Substring(1);
                            else
                                context.OnError("Illegal length general register {0}. Needs 64 bit operand", reg);
                            break;
                        default:
                            context.OnError("Unknown register size {0}", operand.Size);
                            break;
                        }
                    }
                }
            X86Register tempReg;
            bool isReg = X86Register.TryParse(reg, out tempReg);
            if ((isReg) && (operand != null))
                {
                operand.BaseRegister = tempReg;
                operand.OperandFormat = X86Operand.Format.Register;
                }
            return isReg;
            }

        /// <summary>
        /// Given the size and the reg field from the ModR/M byte, 
        /// return the register.
        /// </summary>
        /// <returns></returns>
        X86Register ObtainRegister(X86Instruction.DataSize size, byte regField)
            {
            return table2_1[size][regField];
            }

        /// <summary>
        /// Table 2.1 from manual, showing reg field for ModR/M byte
        /// Each row is reg values 0-7, the rows are indexed by size and style of register
        /// </summary>
        readonly static Dictionary<X86Instruction.DataSize, X86Register[]> table2_1 = new Dictionary<X86Instruction.DataSize, X86Register[]>()
        {
            {X86Instruction.DataSize.Byte,new X86Register[]{X86Register.AL, X86Register.CL, X86Register.DL, X86Register.BL, X86Register.AH, X86Register.CH, X86Register.DH, X86Register.BH}},
            {X86Instruction.DataSize.Word,new X86Register[]{X86Register.AX, X86Register.CX, X86Register.DX, X86Register.BX, X86Register.SP, X86Register.BP, X86Register.SI, X86Register.DI}},
            {X86Instruction.DataSize.Dword,new X86Register[]{X86Register.EAX, X86Register.ECX, X86Register.EDX, X86Register.EBX, X86Register.ESP, X86Register.EBP, X86Register.ESI, X86Register.EDI}},
            {X86Instruction.DataSize.Qword,new X86Register[]{X86Register.MMX0, X86Register.MMX1, X86Register.MMX2, X86Register.MMX3, X86Register.MMX4, X86Register.MMX5, X86Register.MMX6, X86Register.MMX7}},
            {X86Instruction.DataSize.DoubleQword,new X86Register[]{X86Register.XMM0, X86Register.XMM1, X86Register.XMM2, X86Register.XMM3, X86Register.XMM4, X86Register.XMM5, X86Register.XMM6, X86Register.XMM7}}
        };


        /// <summary>
        /// 8 base registers from table 2-2, volume 2. Note that Mod=00 is slightly different from this
        /// </summary>
        readonly static X86Register[] baseRegisters32 = { X86Register.EAX, X86Register.ECX, X86Register.EDX, X86Register.EBX, X86Register.ESP, X86Register.EBP, X86Register.ESI, X86Register.EDI };

        /// <summary>
        /// Process the ModRM byte. ModR/M is an addressing form modifier byte, but sometimes
        /// contains special bits for specific opcodes. Some combinations require a following 
        /// Scale Index Base (SIB) byte.
        /// Bits 
        ///    7-6 : Mod - combines with r/m field to form 32 values: 8 registers and 24 addressing modes.
        ///    5-3 : Reg/Opcode - either a register number or three more bits of opcode.
        ///    2-0 : R/M - register/memory - either a register or combine with mod.
        ///    
        /// This function implements Intel tables 2-1 through 2-3 of Volume 2. 
        /// context already holds the read ModR/M byte and optional SIB if needed.
        /// </summary>
        /// <returns>true on success, else false.</returns>
        bool ProcessModRM(X86DecodeContext context, X86Operand operand)
            {

            var instruction = context.Instruction;


            var mod = instruction.ModRM.Mod;
            var reg = instruction.ModRM.Reg;
            var rm = instruction.ModRM.RM;

            // if there is a SIB value instead, do that
            if (instruction.HasSIB)
                {
                operand.OperandFormat = X86Operand.Format.Indirect;
                if (mod == 1) // these SIB mod types have a displacement
                    {
                    instruction.DisplacementSize = X86Instruction.DataSize.Byte;
                    operand.OperandFormat = X86Operand.Format.IndirectOffset;
                    }
                else if (mod == 2)
                    {
                    instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                    operand.OperandFormat = X86Operand.Format.IndirectOffset;
                    }
                return ProcessSIB(context, operand, mod);
                }

            // read most common effective address for 32 bit addressing
            if (context.ProcessorMode.AddressingMode == X86ProcessorMode.BitMode.Bit32)
                {
                if (mod != 3)
                    {
                    // No SIB, use table 2-2 volume 2A
                    operand.BaseRegister = baseRegisters32[rm];
                    if ((mod == 0) && (rm == 5))
                        { // special case
                        operand.BaseRegister = null;
                        instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                        operand.OperandFormat = X86Operand.Format.Direct; // needs displacement
                        }
                    if (mod == 1) // todo - these depend on prefixes? - make accessors in instruction
                        {
                        instruction.DisplacementSize = X86Instruction.DataSize.Byte;
                        operand.OperandFormat = X86Operand.Format.IndirectOffset;
                        }
                    else if (mod == 2)
                        {
                        instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                        operand.OperandFormat = X86Operand.Format.IndirectOffset;
                        }
                    }
                else
                    { // mod == 3, register case, depends on data size
                    if (operand.Size == X86Instruction.DataSize.None)
                        return false; // fails
                    operand.BaseRegister = table2_1[operand.Size][rm];
                    operand.OperandFormat = X86Operand.Format.Register;
                    }
                }
            else if (context.ProcessorMode.AddressingMode == X86ProcessorMode.BitMode.Bit16)
                {
                // todo - very similar to the 32 bit version, make simple modifications?
                context.OnError("16 bit addressing not implemented");
                return false; // todo - implement
                }
            else
                {
                context.OnError("Invalid addressing mode. Only 16 and 32 bit allowed");
                }
            return true;
            }

        /// <summary>
        ///  Parse the SIB byte. Return true on success
        /// </summary>
        /// <param name="sibByte"></param>
        /// <returns></returns>
        bool ProcessSIB(X86DecodeContext context, X86Operand operand, byte mod)
            {
            Debug.Assert(context.Instruction.HasSIB);

            var instruction = context.Instruction;
            var scale = instruction.SIB.Scale;
            var index = instruction.SIB.Index;
            var Base = instruction.SIB.Base;

            operand.IndexRegister = baseRegisters32[index];
            operand.Scale = 1 << scale;
            operand.BaseRegister = baseRegisters32[Base];

            // special case changes
            if (index == 4)
                {
                operand.IndexRegister = null; // only disp32 base
                //instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                // todo? operand.OperandFormat = X86Operand.Format.
                }
            if (Base == 5)
                {
                operand.OperandFormat = X86Operand.Format.IndirectOffset;
                if (mod == 0)
                    {
                    operand.BaseRegister = null;
                    instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                    }
                else if (mod == 1)
                    instruction.DisplacementSize = X86Instruction.DataSize.Byte;
                else if (mod == 2)
                    instruction.DisplacementSize = X86Instruction.DataSize.Dword;
                }

            return true;
            }

        /// <summary>
        /// Decode the Displacement field, updating index as needed.
        /// </summary>
        /// <param name="index">offset into image</param>
        /// <param name="instruction">The instruction to decode items into</param>
        /// <returns>true on success, else false on error</returns>
        bool DecodeDisplacement(X86DecodeContext context, X86Operand operand)
            {
            int offset, size = 0;
            var instruction = context.Instruction;
            switch (instruction.DisplacementSize)
                {
                case X86Instruction.DataSize.Byte:
                    operand.Value = context.Data.DataByte(0);
                    size = 1;
                    break;
                case X86Instruction.DataSize.Word:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read2(context.Data.Data, ref offset);
                    size = 2;
                    break;
                case X86Instruction.DataSize.Dword:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read4(context.Data.Data, ref offset);
                    size = 4;
                    break;
                case X86Instruction.DataSize.Segm16m32:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read2(context.Data.Data, ref offset) << 32;
                    operand.Value = BitUtils.Read4(context.Data.Data, ref offset);
                    size = 6;
                    break;
                default:
                    context.OnError("Illegal displacement size {0}", instruction.DisplacementSize);
                    return false;
                }
            // copy bytes into instruction
            while (size-- > 0)
                {
                instruction.Data.Add(context.Data.NextByte());
                }
            return true;
            }

        /// <summary>
        /// Decode the Immediate field if needed updating index as needed.
        /// </summary>
        /// <param name="index">offset into image</param>
        /// <param name="instruction">The instruction to decode items into</param>
        /// <returns>true on success, else false on error</returns>
        bool DecodeImmediate(X86DecodeContext context, X86Operand operand)
            {
            int offset;
            int size = 0;
            var instruction = context.Instruction;
            //            switch (instruction.ImmediateSize) // TODO: Note - instruction cannot have Immediate size due to ENTER has two immediate values
            switch (operand.Size)
                {
                case X86Instruction.DataSize.Byte:
                    operand.Value = context.Data.DataByte(0);
                    size = 1;
                    break;
                case X86Instruction.DataSize.Word:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read2(context.Data.Data, ref offset);
                    size = 2;
                    break;
                case X86Instruction.DataSize.Dword:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read4(context.Data.Data, ref offset);
                    size = 4;
                    break;
                case X86Instruction.DataSize.Segm16m32:
                    offset = (int)context.Data.DataIndex;
                    operand.Value = BitUtils.Read2(context.Data.Data, ref offset) << 32;
                    operand.Value = BitUtils.Read4(context.Data.Data, ref offset);
                    size = 6;
                    break;
                default:
                    context.OnError("Illegal immediate size {0}", operand.Size);
                    return false;
                }
            // copy bytes into instruction
            while (size-- > 0)
                {
                instruction.Data.Add(context.Data.NextByte());
                }
            return true; // success
            }
        }
    }
