/****************************  disasm2.cpp   ********************************
* Author:        Agner Fog
* Date created:  2007-02-25
* Last modified: 2011-08-01
* Project:       objconv
* Module:        disasm2.cpp
* Description:
* Module for disassembler containing file output functions
*
* Changes that relate to assembly language syntax should be done in this file only.
*
* Copyright 2007-2011 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#include "stdafx.h"

/**********************  Warning and error texts   ***************************
These texts are inserted in disassembled code in case of warnings or errors.

The occurrence of an error makes the disassembler mark the code block between
the nearest known code labels as dubious. This means that the byte sequence
might be data in the code segment or the disassembler might be out of phase
with instruction boundaries. Dubious code will be shown both as code and as
data.

A warning will be shown as 'Note:' before the instruction it applies to.
This might indicate suboptimal coding or a possible cause for concern.

The criteria for distinguishing between warnings and errors is not the 
severity of consequences, but whether the condition is likely to be caused
by common programming errors or by data in the code segment.

Some of the warning messages are quite benign, e.g. an unnecessary prefix.
Other warning messages can have severe consequences, e.g. a function missing
a return statement.

Still other warnings are no case for concern, but a condition requiring 
attention. For example the message: "Multi-byte NOP. Replace with ALIGN",
might actually indicate a well optimized code. But it requires attention
because the assembler cannot re-create the multi-byte NOP if the code
is assembled again. The programmer needs to decide what level of alignment
is optimal and replace the NOP with an align statement.

*****************************************************************************/

// Define error texts. 
SIntTxt AsmErrorTexts[] = {
   {1,         "Instruction longer than 15 bytes"},
   {2,         "Lock prefix not allowed for this opcode"},
   {4,         "Illegal opcode"},
   {8,         "Illegal operands for this opcode"},
   {0x10,      "Instruction extends beyond end of code block"},
   {0x20,      "Prefix after REX prefix not allowed"},
   {0x40,      "This instruction is not allowed in 64 bit mode"},
   {0x80,      "Instruction out of phase with next label"},
   {0x100,     "Attempt to use R13 as base register without displacement"},
   {0x200,     "Register 8 - 15 only allowed in 64 bit mode (Ignored)."},
   {0x400,     "REX prefix not allowed on instruction with DREX byte"},
   {0x800,     "VEX has X bit but no SIB byte (Probably ignored)"},
   {0x1000,    "Relocation source does not match address or operand field"},
   {0x2000,    "Overlapping relocations"},
   {0x4000,    "This is unlikely to be code"}, // Consecutive bytes of 0 found
   {0x8000,    "VEX.L bit not allowed here"},
   {0x10000,   "VEX.mmmm bits out of range"},
   {0x80000,   "Internal error in opcode table in opcodes.cpp"}
};

// Warning texts 1: Warnings about conditions that could be intentional and suboptimal code
SIntTxt AsmWarningTexts1[] = {
   {1,          "Immediate operand could be made smaller by sign extension"},
   {2,          "Immediate operand could be made smaller by zero extension"},
   {4,          "Zero displacement could be omitted"},
   {8,          "Displacement could be made smaller by sign extension"},
   {0x10,       "SIB byte unnecessary here"},
   {0x20,       "A shorter instruction exists for register operand"},
   {0x40,       "Length-changing prefix causes delay on Intel processors"},
   {0x80,       "Address size prefix should be avoided"},
   {0x100,      "Same prefix occurs more than once"},
   {0x200,      "Prefix valid but unnecessary"},
   {0x400,      "Prefix bit or byte has no meaning in this context"},
   {0x800,      "Contradicting prefixes"},
   {0x1000,     "Required prefix missing"},
   {0x2000,     "Address has scale factor but no index register"},
   {0x4000,     "Address is not rip-relative"},
   {0x8000,     "Absolute memory address without relocation"},
   {0x10000,    "Unusual relocation type for this operand"},
   {0x20000,    "Instruction pointer truncated by operand size prefix"},
   {0x40000,    "Stack pointer truncated by address size prefix"},
   {0x80000,    "Jump or call to data segment not allowed"},
   {0x100000,   "Undocumented opcode"},
   {0x200000,   "Unknown opcode reserved for future extensions"},
   {0x400000,   "Memory operand is misaligned. Performance penalty"},
   {0x800000,   "Alignment fault. Memory operand must be aligned"},
   {0x1000000,  "Multi-byte NOP. Replace with ALIGN"},
   {0x2000000,  "Bogus length-changing prefix causes delay on Intel processors here"},
   {0x4000000,  "Non-default size for stack operation"},
   {0x8000000,  "Function does not end with ret or jmp"},
   {0x10000000, "Inaccessible code"},
   {0x20000000, "Full 64-bit address"},
   {0x40000000, "VEX prefix bits not allowed here"}
};

// Warning texts 2: Warnings about possible misinterpretation; serious warnings
SIntTxt AsmWarningTexts2[] = {
   {1,          "Label out of phase with instruction. Possibly spurious"},
   {2,          "Planned future instruction, according to preliminary specification"},
   {4,          "This instruction has been planned but never implemented because plans were changed. Will not work"},
};


// Indication of relocation types in comments:
SIntTxt RelocationTypeNames[] = {
   {0x001,  "(d)" },                   // Direct address in flat address space
   {0x002,  "(rel)" },                 // Self-relative
   {0x004,  "(imgrel)" },              // Image-relative
   {0x008,  "(segrel)" },              // Segment-relative
   {0x010,  "(refpoint)" },            // Relative to arbitrary point (position-independent code in Mach-O)
   {0x021,  "(d)" },                   // Direct (adjust by image base)
   {0x041,  "(d)" },                   // Direct (make procecure linkage table entry)
   {0x081,  "(indirect)" },            // Gnu indirect function dispatcher (make procecure linkage table entry?)
   {0x100,  "(seg)" },                 // Segment address or descriptor
   {0x200,  "(sseg)" },                // Segment of symbol
   {0x400,  "(far)" },                 // Far segment:offset address
   {0x1001, "(GOT)" },                 // GOT entry
   {0x1002, "(GOT r)" },               // self-relative to GOT entry
   {0x2002, "(PLT r)" }                // self-relative to PLT entry
};

// Instruction set names
const char * InstructionSetNames[] = {
   "8086", "80186", "80286", "80386",               // 0 - 3
   "80486", "Pentium", "Pentium Pro", "MMX",        // 4 - 7
   "Pentium II", "", "", "",                        // 8 - B
   "", "", "", "",                                  // C - F
   "", "SSE", "SSE2", "SSE3",                       // 10 - 13
   "Supplementary SSE3", "SSE4.1", "SSE4.2", "AES", // 14 - 17
   "CLMUL", "AVX", "FMA3", "??"                     // 18 - 1B
   };

const int InstructionSetNamesLen = TableSize(InstructionSetNames);


/**************************  class CDisassembler  *****************************
Most member functions of CDisassembler are defined in disasm1.cpp

Only the functions that produce output are defined here:
******************************************************************************/

void CDisassembler::WriteShortRegOperand(uint32 Type) {
   // Write register operand from lower 3 bits of opcode byte to OutFile
   uint32 rnum = Get<uint8>(s.OpcodeStart2) & 7;
   // Check REX.B prefix
   if (s.Prefixes[7] & 1) rnum |= 8;             // Add 8 if REX.B prefix
   // Write register name
   WriteRegisterName(rnum, Type);
}

void CDisassembler::WriteRegOperand(uint32 Type) {
   // Write register operand from reg bits
   uint32 Num = s.Reg;                           // Register number

   // Write register name
   WriteRegisterName(Num, Type);
}

void CDisassembler::WriteRMOperand(uint32 Type) {
   // Write memory or register operand from mod/rm bits of mod/reg/rm byte 
   // and possibly SIB byte or direct memory operand to OutFile.
   // Also used for writing direct memory operand

   if ((Type & 0xFF) == 0) {
      // No explicit operand
      return;
   }

   uint32 Components = 0;                        // Count number of addends inside []
   int64  Addend = 0;                            // Inline displacement or addend
   int AddressingMode = 0;                       // 0: 16- or 32 bit addressing mode
                                                 // 1: 64-bit pointer
                                                 // 2: 32-bit absolute in 64-bit mode
                                                 // 4: 64-bit rip-relative
                                                 // 8: 64-bit absolute
   // Check if register or memory
   if (s.Mod == 3) {
      // Register operand
      WriteRegisterName(s.RM, Type);
      return;
   }

   // Find addend, if any
   switch (s.AddressFieldSize) {
   case 1:  // 1 byte displacement
      Addend = Get<int8>(s.AddressField);
      break;
   case 2:  // 2 bytes displacement
      Addend = Get<int16>(s.AddressField);
      break;
   case 4:  // 4 bytes displacement
      Addend = Get<int32>(s.AddressField);
      if ((s.MFlags & 0x100) && !s.AddressRelocation) {
         // rip-relative
         Addend += ImageBase + uint64(SectionAddress + IEnd);
      }
      break;
   case 8:  // 8 bytes address
      Addend = Get<int64>(s.AddressField);
      break;
   }
   // Get AddressingMode
   if (s.AddressSize > 32) {
      if (s.MFlags & 0x100) {
         AddressingMode = 4;                     // 64-bit rip-relative
      }
      else if (s.AddressFieldSize == 8) {
         AddressingMode = 8;                     // 64-bit absolute
      }
      else if (s.AddressRelocation || (s.BaseReg==0 && s.IndexReg==0)) {
         AddressingMode = 2;                     // 32-bit absolute in 64-bit mode
      }
      else {
         AddressingMode = 1;                     // 64-bit pointer
      }
   }

   // Make exception for LEA with no type
   if (Opcodei == 0x8D) {
      Type = 0;
   }
   // Write type override
   WriteOperandType(Type);

   if (Syntax != SUBTYPE_MASM) {
      // Write "[" around memory operands, before segment
      OutFile.Put("[");
   }

   // Write segment prefix, if any
   if (s.Prefixes[0]) {
      OutFile.Put(RegisterNamesSeg[GetSegmentRegisterFromPrefix()]);
      OutFile.Put(":");
   }
   else if (!s.BaseReg && !s.IndexReg && (!s.AddressRelocation || (s.Warnings1 & 0x10000)) && Syntax != SUBTYPE_YASM) { 
      // No pointer register and no memory reference or wrong type of memory reference.
      // Write segment register to indicate that we have a memory operand
      OutFile.Put("DS:");
   }

   if (Syntax == SUBTYPE_MASM) {
      // Write "[" around memory operands, after segment
      OutFile.Put("[");
   }

   if (Syntax == SUBTYPE_YASM && (AddressingMode & 0x0E)) {
      // Specify absolute or relative addressing mode
      switch (AddressingMode) {
      case 2: OutFile.Put("abs ");  break;
      case 4: OutFile.Put("rel ");  break;
      case 8: OutFile.Put("abs qword ");  break;
      }
   }

   // Write relocation target, if any
   if (s.AddressRelocation) {
      // Write cross reference
      WriteRelocationTarget(s.AddressRelocation, 4 | (s.MFlags & 0x100), Addend);
      // Addend has been written, don't write it again
      Addend = 0;
      // Remember that something has been written
      Components++;
   }

   // Check address size for pointer registers
   const char * * PointerRegisterNames;
   switch (s.AddressSize) {
      case 16:
         PointerRegisterNames = RegisterNames16;  break;
      case 32:
         PointerRegisterNames = RegisterNames32;  break;
      case 64:
         PointerRegisterNames = RegisterNames64;  break;
   }

   // Write base register, if any
   if (s.BaseReg) {
      if (Components++) OutFile.Put("+");      // Put "+" if anything before
      OutFile.Put(PointerRegisterNames[s.BaseReg - 1]);
   }

   // Write index register, if any
   if (s.IndexReg) {
      if (Components++) OutFile.Put("+");        // Put "+" if anything before
      if ((s.OpcodeDef->InstructionFormat & 0x1F) != 0x1E) {
         // normal index register
         OutFile.Put(PointerRegisterNames[s.IndexReg - 1]);
      }
      else {
         // VSIB byte specifies vector index register
         uint8 L = s.Prefixes[6] & 0x20;         // L bit = 256 bit destination vector size 
         uint8 W = s.Prefixes[7] & 0x08;         // W bit = destination word size is 64 bit
         int I = Type & 4;                       // index word size is 64 bit
         int IR = L && (!W || I);                // index register is ymm if dest is ymm, unless index word size < operand word size
         OutFile.Put(IR ? "ymm" : "xmm");
         OutFile.PutDecimal(s.IndexReg - 1);
      }
      // Write scale factor, if any
      if (s.Scale) {
         OutFile.Put("*");
         OutFile.PutDecimal(1 << s.Scale);
      }
   }

   // Write +/- before addend
   if (Components && Addend) {
      // Displacement comes after base/index registers
      if (Addend >= 0 || s.AddressFieldSize == 8) {
         // Positive. Write +
         OutFile.Put("+");
      }
      else {
         // Negative. Write -
         OutFile.Put("-");
         Addend = -Addend;
      }
   }

   if (Addend || Components == 0) {
      // Find minimum number of digits needed
      uint32 AddendSize = s.AddressFieldSize;
      if ((uint64)Addend < 0x100 && AddendSize > 1) AddendSize = 1;
      else if ((uint64)Addend < 0x10000 && AddendSize > 2) AddendSize = 2;

      // Write address or addend as hexadecimal
      OutFile.PutHex((uint64)Addend, 2);
   }

   if (Syntax == SUBTYPE_GASM && (AddressingMode == 4)) {
      // Need to specify rip-relative address
      OutFile.Put("+rip");
   }

   // End with "]"
   OutFile.Put("]");
}


void CDisassembler::WriteOperandType(uint32 type) {
   switch (Syntax) {
   case SUBTYPE_MASM:
      WriteOperandTypeMASM(type);  break;
   case SUBTYPE_YASM:
      WriteOperandTypeYASM(type);  break;
   case SUBTYPE_GASM:
      WriteOperandTypeGASM(type);  break;
   }
}

void CDisassembler::WriteOperandTypeMASM(uint32 type) {
   // Write type override before operand, e.g. "dword ptr", MASM syntax
   if (type & 0xF00) {
      type &= 0xF00;                             // Ignore element type for vectors
   }
   else {
      type &= 0xFF;                              // Use operand type only
   }

   switch (type) {
   case 1:  // 8 bits
      OutFile.Put("byte ptr ");  break;
   case 2:  // 16 bits
      OutFile.Put("word ptr ");  break;
   case 3:  // 32 bits
      OutFile.Put("dword ptr ");  break;
   case 4:  // 64 bits
      OutFile.Put("qword ptr ");  break;
   case 5:  // 80 bits
      if ((s.OpcodeDef->Destination & 0xFF) == 0xD) {
         // 64+16 bit far pointer. Not supported by MASM
         OutFile.Put("fword ptr ");
         s.OpComment = "64+16 bit. Need REX.W prefix";
      }
      else { 
         OutFile.Put("tbyte ptr ");}
      break;
   case 6: case 0x40: case 0x48: case 0:
      // Other size. Write nothing
      break;
   case 7: case 0x0D: // 48 bits or far
      OutFile.Put("fword ptr ");  
      if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) {
         // All assemblers I have tried forget the REX.W prefix here. Make a notice
         s.OpComment = "32+16 bit. Possibly forgot REX.W prefix"; 
      }      
      break;
   case 0x43: // 32 bits float (x87)
   case 0x4B: // 32 bits float (SSE2)
      OutFile.Put("dword ptr ");  break;
   case 0x44: // 64 bits float
   case 0x4C: // 64 bits float (SSE2)
      OutFile.Put("qword ptr ");  break;
   case 0x45: // 80 bits float
      OutFile.Put("tbyte ptr ");  break;
   case 0x84: case 0x85: // far call
      OutFile.Put("far ptr ");  break;
   case 0x300:  // MMX
      OutFile.Put("qword ptr ");  break;
   case 0x400:  // XMM
      OutFile.Put("xmmword ptr ");  break;
   case 0x500:  // YMM
      OutFile.Put("ymmword ptr ");  break;
   }
}

void CDisassembler::WriteOperandTypeYASM(uint32 type) {
   // Write type override before operand, e.g. "dword", YASM syntax
   if (type & 0xF00) {
      type &= 0xF00;                             // Ignore element type for vectors
   }
   else {
      type &= 0xFF;                              // Use operand type only
   }
   uint32 Dest = s.OpcodeDef->Destination & 0xFF;// Destination operand
   if (Dest >= 0xB && Dest < 0x10) {
      // This is a pointer
      if (Dest < 0x0D) {
         OutFile.Put("near ");                   // Near indirect jump/call
      }
      else {
         // Far pointer
         if ((WordSize == 16 && type == 3) || (WordSize == 32 && type == 7)) {
            OutFile.Put("far ");
         }
         else {
            // Size currently not supported by YASM
            switch (type) {
            case 3: OutFile.Put("far ");
               s.OpComment = "16+16 bit. Needs 66H prefix";
               break;
            case 7: OutFile.Put("far ");  
               s.OpComment = "32+16 bit. Possibly forgot REX.W prefix";
               break;
            case 5: OutFile.Put("far ");  
               s.OpComment = "64+16 bit. Needs REX.W prefix";
               break;
            }
         }
      }
      return;
   }
   switch (type) {
   case 1:  // 8 bits
      OutFile.Put("byte ");  break;
   case 2:  // 16 bits
      OutFile.Put("word ");  break;
   case 3:  // 32 bits
      OutFile.Put("dword ");  break;
   case 4:  // 64 bits
      OutFile.Put("qword ");  break;
   case 5:  // 80 bits
      OutFile.Put("tbyte ");  break;
   case 7:  // 48 bits
      OutFile.Put("fword ");  break;
   case 0x43: // 32 bits float (x87)
   case 0x4B: // 32 bits float (SSE2)
      OutFile.Put("dword ");  break;
   case 0x44: // 64 bits float
   case 0x4C: // 64 bits float (SSE2)
      OutFile.Put("qword ");  break;
   case 0x45: // 80 bits float
      OutFile.Put("tbyte ");  break;
   case 0x84: case 0x85: // far call
      OutFile.Put("far ");  break;
   case 0x300:  // MMX
      OutFile.Put("qword ");  break;
   case 0x400:  // XMM
      OutFile.Put("oword ");  break;
   case 0x500:  // YMM
      OutFile.Put("yword ");  break;
   default:; // Anything else: write nothing
   }
}

void CDisassembler::WriteOperandTypeGASM(uint32 type) {
   // Write type override before operand, e.g. "dword ptr", GAS syntax
   if (type & 0xF00) {
      type &= 0xF00;                             // Ignore element type for vectors
   }
   else {
      type &= 0xFF;                              // Use operand type only
   }

   switch (type & 0xFF) {
   case 1:  // 8 bits
      OutFile.Put("byte ptr ");  break;
   case 2:  // 16 bits
      OutFile.Put("word ptr ");  break;
   case 3:  // 32 bits
      OutFile.Put("dword ptr ");  break;
   case 4:  // 64 bits
      OutFile.Put("qword ptr ");  break;
   case 5:  // 80 bits
      if ((s.OpcodeDef->Destination & 0xFF) == 0xD) {
         // 64+16 bit far pointer. Not supported by Gas
         OutFile.Put("fword ptr ");
         s.OpComment = "64+16 bit. Needs REX.W prefix";
      }
      else { 
         OutFile.Put("tbyte ptr ");}
      break;
   case 6: case 0x40: case 0x48: case 0:
      // Other size. Write nothing
      break;
   case 7:    // 48 bits
      OutFile.Put("fword ptr ");  
      if ((s.OpcodeDef->Destination & 0xFF) == 0xD && WordSize == 64) {
         // All assemblers I have tried forget the REX.W prefix here. Make a notice
         s.OpComment = "32+16 bit. Possibly forgot REX.W prefix"; 
      }      
      break;
   case 0x43: // 32 bits float (x87)
   case 0x4B: // 32 bits float (SSE2)
      OutFile.Put("dword ptr ");  break;
   case 0x44: // 64 bits float
   case 0x4C: // 64 bits float (SSE2)
      OutFile.Put("qword ptr ");  break;
   case 0x45: // 80 bits float
      OutFile.Put("tbyte ptr ");  break;
   case 0x84: case 0x85: // far call
      OutFile.Put("far ptr ");  break;
   case 0x300:  // MMX
      OutFile.Put("qword ptr ");  break;
   case 0x400:  // XMM
      OutFile.Put("xmmword ptr ");  break;
   case 0x500:  // YMM
      OutFile.Put("ymmword ptr ");  break;
   }
}


void CDisassembler::WriteDREXOperand(uint32 Type) {
   // Write register operand from dest bits of DREX byte (AMD only)
   uint32 Num = s.DVREX >> 4;                    // Register number
   // Write register name
   WriteRegisterName(Num, Type);
}

void CDisassembler::WriteVEXOperand(uint32 Type, int i) {
   // Write register operand from VEX.vvvv bits or immediate bits
   uint32 Num;                                   // Register number
   switch (i) {
   case 0:  // Use VEX.vvvv bits
      Num = s.DVREX & 0x0F;  break;
   case 1:  // Use immediate bits 4-7
      Num = Get<uint8>(s.ImmediateField) >> 4;  break;
   case 2:  // Use immediate bits 0-3 (Unused. For possible future use)
      Num = Get<uint8>(s.ImmediateField) & 0x0F;  break;
   }
   // Write register name
   WriteRegisterName(Num, Type);
}


void CDisassembler::WriteRegisterName(uint32 Value, uint32 Type) {
   // Write name of register to OutFile
   if (Type & 0xF00) {
      // vector register
      Type &= 0xF00;
   }
   else {
      // Other register
      Type &= 0xFF;                              // Remove irrelevant bits
   }

   // Check fixed registers (do not depend on Value)
   switch (Type) {
   case 0xA1:  // al
      Type = 1;  Value = 0;
      break;

   case 0xA2:  // ax
      Type = 2;  Value = 0;
      break;

   case 0xA3:  // eax
      Type = 3;  Value = 0;
      break;

   case 0xA4:  // rax
      Type = 4;  Value = 0;
      break;

   case 0xAE:  // xmm0
      Type = 0x400;  Value = 0;
      break;

   case 0xAF:  // st(0)
      Type = 0x40;  Value = 0;
      break;

   case 0xB2:  // dx
      Type = 2;  Value = 2;
      break;

   case 0xB3:  // cl
      Type = 1;  Value = 1;
      break;
   }

   // Get value within limits
   Value &= 0x0F;

   // Now write register name depending on type
   switch (Type) {
   case 1:  // 8 bit register. Depends on any REX prefix
      OutFile.Put(s.Prefixes[7] ? RegisterNames8x[Value] : RegisterNames8[Value & 7]);
      break;

   case 2:  // 16 bit register
      OutFile.Put(RegisterNames16[Value]);
      break;

   case 3:  // 32 bit register
      OutFile.Put(RegisterNames32[Value]);
      break;

   case 4:  // 64 bit register
      OutFile.Put(RegisterNames64[Value]);
      break;

   case 0x300:  // mmx register
      OutFile.Put("mm");
      OutFile.PutDecimal(Value & 7);
      break;

   case 0x400:  // xmm register (packed integer or float)
   case 0x48: case 0x4B: case 0x4C: // xmm register (scalar float)
      OutFile.Put("xmm");
      OutFile.PutDecimal(Value);
      break;

   case 0x500:  // ymm register (packed)
      OutFile.Put("ymm");
      OutFile.PutDecimal(Value);
      break;

   case 0x40:  // st register
      if (Syntax == SUBTYPE_YASM) {
         // NASM, YASM and GAS-AT&T use st0
         OutFile.Put("st");
         OutFile.PutDecimal(Value & 7);
      }
      else {
         // MASM and GAS-Intel use st(0), 
         OutFile.Put("st(");
         OutFile.PutDecimal(Value & 7);
         OutFile.Put(")");
      }
      break;

   case 0x91:  // Segment register
      OutFile.Put(RegisterNamesSeg[Value & 7]);
      break;

   case 0x92:  // Control register
      OutFile.Put(RegisterNamesCR[Value]);
      break;

   case 0x93:  // Debug register
      OutFile.Put("dr");
      OutFile.PutDecimal(Value);
      break;

   case 0x94:  // Test register (obsolete)
      OutFile.Put("tr");
      OutFile.PutDecimal(Value);
      break;

   case 0xB1:  // 1
      OutFile.Put("1");
      break;

   default:    // Unexpected
      OutFile.Put("UNKNOWN REGISTER TYPE ");
      OutFile.PutDecimal(Value);
      break;
   }
}


void CDisassembler::WriteImmediateOperand(uint32 Type) {
   // Write immediate operand or direct jump/call address
   int    WriteFormat;                 // 0: unsigned, 1: signed, 2: hexadecimal
   int    Components = 0;              // Number of components in immediate operand       
   uint32 OSize;                       // Operand size
   uint32 FieldPointer;                // Pointer to field containing value
   uint32 FieldSize;                   // Size of field containing value
   int64  Value = 0;                   // Value of immediate operand

   // Check if far
   if ((Type & 0xFE) == 0x84) {
      // Write far ptr
      WriteOperandType(Type);
   }

   // Check if type override needed
   if ((s.OpcodeDef->AllowedPrefixes & 2) && s.Prefixes[4] == 0x66
   && (Opcodei == 0x68 || Opcodei == 0x6A)) {
      // Push immediate with non-default operand size needs type override
      WriteOperandType(s.OperandSize == 16 ? 2 : 3);
   }

   FieldPointer = s.ImmediateField;
   FieldSize    = s.ImmediateFieldSize;

   if (Syntax == SUBTYPE_YASM && (Type & 0x0F) == 4 && FieldSize == 8) {
      // Write type override to make sure we get 8 bytes address in case there is a relocation here
      WriteOperandType(4);
   }

   if (Type & 0x200000) {
      if (FieldSize > 1) {
         // Uses second part of field. Single byte only
         FieldPointer += FieldSize-1;
         FieldSize = 1;
      }
      else {
         // Uses half a byte
         FieldSize = 0;
      }
   }

   // Get inline value
   switch (FieldSize) {
   case 0:  // 4 bits
      Value = Get<uint8>(FieldPointer) & 0x0F;
      break;

   case 1:  // 8 bits
      Value = Get<int8>(FieldPointer);  
      break;

   case 2:  // 16 bits
      Value = Get<int16>(FieldPointer);  break;

   case 6:  // 48 bits
      Value  = Get<int32>(FieldPointer);  
      Value += (uint64)Get<uint16>(FieldPointer + 4) << 32;  
      break;

   case 4:  // 32 bits
      Value = Get<int32>(FieldPointer);  break;

   case 8:  // 64 bits
      Value = Get<int64>(FieldPointer);  break;

   case 3:  // 16+8 bits ("Enter" instruction)
      if ((Type & 0xFF) == 0x12) {
         // First 16 bits
         FieldSize = 2; Value = Get<int16>(FieldPointer);  break;
      }
      // else continue in default case to get error message

   default:  // Other sizes should not occur
      err.submit(3000);  Value = -1;
   }

   // Check if relocation
   if (s.ImmediateRelocation) {
      // Write relocation target name
      uint32 Context = 2;
      if ((Type & 0xFC) == 0x80) Context = 8;     // Near jump/call destination
      if ((Type & 0xFC) == 0x84) Context = 0x10;  // Far jump/call destination

      // Write cross reference
      WriteRelocationTarget(s.ImmediateRelocation, Context, Value);

      // Remember that Value has been written
      Value = 0;
      Components++;
   }
   // Check if AAM or AAD
   if (Value == 10 && (Opcodei & 0xFE) == 0xD4) {
      // Don't write operand for AAM or AAD if = 10
      return;
   }

   // Write as unsigned, signed or hexadecimal:
   if ((Type & 0xF0) == 0x30 || (Type & 0xF0) == 0x80) {
      // Hexadecimal
      WriteFormat = 2;
   }
   else if (s.ImmediateFieldSize == 8) {
      // 64 bit constant
      if (Value == (int32)Value) {
         // Signed
         WriteFormat = 1;
      }
      else {
         // Hexadecimal
         WriteFormat = 2;
      }
   }
   else if ((Type & 0xF0) == 0x20) {
      // Signed
      WriteFormat = 1;
   }
   else {
      // Unsigned
      WriteFormat = 0;
   }

   if ((Type & 0xFC) == 0x80 && !s.ImmediateRelocation) {
      // Self-relative jump or call without relocation. Adjust immediate value
      Value += IEnd;                             // Get absolute address of target

      // Look for symbol at target address
      uint32 ISymbol = Symbols.FindByAddress(Section, (uint32)Value);
      if (ISymbol && (Symbols[ISymbol].Name || CodeMode == 1)) {
         // Symbol found. Write its name
         OutFile.Put(Symbols.GetName(ISymbol));
         // No offset to write
         return;
      }
      // Target address has no name
      Type |= 0x4000;                            // Write target as hexadecimal
   }

   // Operand size
   if ((s.Operands[0] & 0xFFF) <= 0xA || (s.Operands[0] & 0xF0) == 0xA0) {
      // Destination is general purpose register
      OSize = s.OperandSize;
   }
   else {
      // Constant probably unrelated to destination size
      OSize = 8;
   }
   // Check if destination is 8 bit operand
   //if ((s.Operands[0] & 0xFF) == 1 || (s.Operands[0] & 0xFF) == 0xA1) OSize = 8;

   // Check if sign extended
   if (OSize > s.ImmediateFieldSize * 8) {
      if (WriteFormat == 2 && Value >= 0) {
         // Hexadecimal sign extended, not negative:
         // Does not need full length
         OSize = s.ImmediateFieldSize * 8;
      }
      else if (WriteFormat == 0) {
         // Unsigned and sign extended, change to signed
         WriteFormat = 1;
      }
   }

   if (Components) {
      // There was a relocated name
      if (Value) {
         // Addend to relocation is not zero
         if (Value > 0 || WriteFormat != 1) {
            OutFile.Put("+");                  // Put "+" between name and addend
         }
         else {
            OutFile.Put("-");                  // Put "-" between name and addend
            Value = - Value;                  // Change sign to avoid another "-"
         }
      }
      else {
         // No addend to relocated name
         return;
      }
   }
   // Write value
   if (WriteFormat == 2) {
      // Write with hexadecimal number appropriate size
      switch (OSize) {
         case 8:  // 8 bits
            OutFile.PutHex((uint8)Value, 1);  break;
         case 16:  // 16 bits
            if ((Type & 0xFC) == 0x84) {
               // Segment of far call
               OutFile.PutHex((uint16)(Value >> 16), 1);
               OutFile.Put(':');
            }
            OutFile.PutHex((uint16)Value, 2);  break;
         case 32:  // 32 bits
         default:  // Should not occur
            if ((Type & 0xFC) == 0x84) {
               // Segment of far call
               OutFile.PutHex((uint16)(Value >> 32), 1);
               OutFile.Put(':');
            }
            OutFile.PutHex((uint32)Value, 2);  break;
         case 64:  // 64 bits
            OutFile.PutHex((uint64)Value, 2);  break;
      }
   }
   else {
      // Write as signed or unsigned decimal
      if (WriteFormat == 0) { // unsigned
         switch (OSize) {
         case 8:  // 8 bits
            Value &= 0x00FF;  break;
         case 16:  // 16 bits
            Value &= 0xFFFF;  break;
         }
      }
      OutFile.PutDecimal((int32)Value, WriteFormat);  // Write value. Signed or usigned decimal
   }
}


void CDisassembler::WriteOtherOperand(uint32 Type) {
   // Write other type of operand
   const char * * OpRegisterNames;               // Pointer to list of register names
   uint32 RegI = 0;                              // Index into list of register names

   switch (Type & 0x8FF) {
   case 0xA1:  // AL
      OpRegisterNames = RegisterNames8;
      break;
   case 0xA2:  // AX
      OpRegisterNames = RegisterNames16;
      break;
   case 0xA3:  // EAX
      OpRegisterNames = RegisterNames32;
      break;
   case 0xA4:  // RAX
      OpRegisterNames = RegisterNames64;
      break;
   case 0xAE:  // xmm0
      OutFile.Put("xmm0");
      return;
   case 0xAF:  // ST(0)
      OutFile.Put("st(0)");
      return;
   case 0xB1:  // 1
      OutFile.Put("1");
      return;
   case 0xB2:  // DX
      OpRegisterNames = RegisterNames16;
      RegI = 2;
      break;
   case 0xB3: // CL
      OpRegisterNames = RegisterNames8;
      RegI = 1;
      break;
   default:
      OutFile.Put("unknown operand");
      err.submit(3000);
      return;
   }
   // Write register name
   OutFile.Put(OpRegisterNames[RegI]);
}


void CDisassembler::WriteErrorsAndWarnings() {
   // Write errors, warnings and comments, if any
   uint32 n;                                     // Error bit
   if (s.Errors) {
      // There are errors
      // Loop through all bits in s.Errors
      for (n = 1; n; n <<= 1) {
         if (s.Errors & n) {
            if (OutFile.GetColumn()) OutFile.NewLine(); 
            OutFile.Put(CommentSeparator);       // Write "\n; "
            OutFile.Put("Error: ");              // Write "Error: "
            OutFile.Put(Lookup(AsmErrorTexts,n));// Write error text
            OutFile.NewLine(); 
         }
      }
   }

   if (s.Warnings1) {
      // There are warnings 1
      // Loop through all bits in s.Warnings1
      for (n = 1; n; n <<= 1) {
         if (s.Warnings1 & n) {
            if (OutFile.GetColumn()) OutFile.NewLine(); 
            OutFile.Put(CommentSeparator);       // Write "; "
            OutFile.Put("Note: ");               // Write "Note: "
            OutFile.Put(Lookup(AsmWarningTexts1, n));// Write warning text
            OutFile.NewLine(); 
         }
      }
   }
   if (s.Warnings2) {
      // There are warnings 2
      // Loop through all bits in s.Warnings2
      for (n = 1; n; n <<= 1) {
         if (s.Warnings2 & n) {
            if (OutFile.GetColumn()) OutFile.NewLine(); 
            OutFile.Put(CommentSeparator);            // Write "; "
            OutFile.Put("Warning: ");                 // Write "Warning: "
            OutFile.Put(Lookup(AsmWarningTexts2, n)); // Write warning text
            OutFile.NewLine(); 
         }
      }
      if (s.Warnings2 & 1) {
         // Write spurious label
         uint32 sym1 = Symbols.FindByAddress(Section, LabelEnd);
         if (sym1) {
            const char * name = Symbols.GetName(sym1);
            OutFile.Put(CommentSeparator);
            OutFile.Put(name);
            OutFile.Put("; Misplaced symbol at address ");
            OutFile.PutHex(Symbols[sym1].Offset);
            OutFile.NewLine();
         }
      }
   }

   if (!s.Warnings1 && s.Prefixes[0] && (s.OpcodeDef->AllowedPrefixes & 8)) {
      // Branch hint prefix. Write comment
      OutFile.Put(CommentSeparator);             // Write "; "
      switch (s.Prefixes[0]) {
      case 0x2E:
         OutFile.Put("Branch hint prefix for Pentium 4: Predict no jump");   
         break;
      case 0x3E:
         OutFile.Put("Branch hint prefix for Pentium 4: Predict jump");   
         break;
      case 0x64:
         OutFile.Put("Branch hint prefix for Pentium 4: Predict alternate");   
         break;
      default:
         OutFile.Put("Note: Unrecognized branch hint prefix");   
      }
      OutFile.NewLine(); 
   }
}

void CDisassembler::WriteSymbolName(uint32 symi) {
   // Write symbol name. symi = new symbol index
   OutFile.Put(Symbols.GetName(symi));
}

void CDisassembler::WriteSectionName(int32 SegIndex) {
   // Write name of section, segment or group from section index
   const char * Name = 0;
   // Check for special index values
   switch (SegIndex) {
   case ASM_SEGMENT_UNKNOWN:   // Unknown segment. Typical for external symbols
      Name = "Unknown";  break;
   case ASM_SEGMENT_ABSOLUTE:  // No segment. Used for absolute symbols
      Name = "Absolute";  break;
   case ASM_SEGMENT_FLAT:      // Flat segment group
      Name = "flat";  break;
   case ASM_SEGMENT_NOTHING:   // No segment
      Name = "Nothing";  break;
   case ASM_SEGMENT_ERROR:     // Segment register assumed to error
      Name = "Error";  break;
   case ASM_SEGMENT_IMGREL:    // Segment unknown. Offset relative to image base or file base
      Name = "ImageBased";  break;
   default:                    // > 0 means normal segment index
      if ((uint32)SegIndex >= Sections.GetNumEntries()) {
         // Out of range
         Name = "IndexOutOfRange";
      }
      else {
         // Get index into NameBuffer
         uint32 NameIndex = Sections[SegIndex].Name;
         // Check if valid
         if (NameIndex == 0 || NameIndex >= NameBuffer.GetDataSize()) {
            Name = "ErrorNameMissing";
         }
         else {
            // Normal valid name of segment, section or group
            Name = NameBuffer.Buf() + NameIndex;
         }
      }
      break;
   }
   if (Syntax == SUBTYPE_YASM && Name[0] == '_') {
      // Change leading underscore to dot
      OutFile.Put('.');
      OutFile.Put(Name+1); // Write rest of name
   }
   else {
      // Write name
      OutFile.Put(Name);
   }
}

void CDisassembler::WriteDataItems() {
   // Write data items to output file

   int LineState;  // 0: Start of new line, write label
                   // 1: Label written if any, write data directive
                   // 2: Data directive written, write data
                   // 3: First data item written, write comma and more data
                   // 4: Last data item written, write comment
                   // 5: Comment written if any, start new line
   uint32 Pos = IBegin;                          // Current position
   uint32 LinePos = IBegin;                      // Position for beginning of output line
   uint32 BytesPerLine;                          // Number of bytes to write per line
   uint32 LineEnd;                               // Data position for end of line
   uint32 DataEnd;                               // End of data
   uint32 ElementSize, OldElementSize;           // Size of each data element
   uint32 RelOffset;                             // Offset of relocation
   uint32 irel, Oldirel;                         // Relocation index
   int64  Value;                                 // Inline value or addend
   const char * Symname;                         // Symbol name
   int    SeparateLine;                          // Label is on separate line

   SARelocation Rel;                             // Dummy relocation record

   // Check if size is valid
   if (DataSize == 0) DataSize = 1;
   if (DataSize > 32) DataSize = 32;  

   // Expected end position
   if (CodeMode & 3) {
      // Writing data for dubious code. Make same length as code instruction
      DataEnd = IEnd;
   }
   else {
      // Regular data. End at next label
      DataEnd = LabelEnd;
      if (DataEnd > FunctionEnd) DataEnd = FunctionEnd;
      if (DataEnd <= Pos) DataEnd = Pos + DataSize;
      if (DataEnd > Sections[Section].InitSize && Pos < Sections[Section].InitSize) {
         DataEnd = Sections[Section].InitSize;
      }
   }

   // Size of each data element
   ElementSize = DataSize;

   // Check if packed type
   if (DataType & 0xF00) {
      // This is a packed vector type. Get element size
      ElementSize = GetDataElementSize(DataType);
   }

   // Avoid sizes that are not powers of 2
   if (ElementSize == 6 || ElementSize == 10) ElementSize = 2;

   // Set maximum element size to 8
   if (ElementSize > 8)  ElementSize = 8;

   // Set minimum element size to 1
   if (ElementSize < 1)  ElementSize = 1;
               
   if (Pos + ElementSize > DataEnd) {
      // Make sure we end at DataEnd
      ElementSize = 1;  BytesPerLine = 8;
      LineEnd = DataEnd;
   }

   // Set number of bytes per line
   BytesPerLine = (DataSize == 10) ? 10 : 8;

   if (!(CodeMode & 3)) {
      // Begin new line for each data item (except in code segment)
      OutFile.NewLine();
   }
   LineState = 0; irel = 0;

   // Check if alignment required
   if (DataSize >= 16 && (DataType & 0xC00) && (DataType & 0xFF) != 0x51 
   && (FlagPrevious & 0x100) < (DataSize << 4) && !(IBegin & (DataSize-1))) {
      // Write align directive
      WriteAlign(DataSize);
      // Remember that data is aligned
      FlagPrevious |= (DataSize << 4);
   }

   // Get symbol name for label
   uint32 sym;                                   // Current symbol index
   uint32 sym1, sym2 = 0;                        // First and last symbol at current address

   sym1 = Symbols.FindByAddress(Section, Pos, &sym2);

   // Loop for one or more symbols at this address
   for (sym = sym1; sym <= sym2; sym++) {

      if (sym && Symbols[sym].Scope && !(Symbols[sym].Scope & 0x100) && !(Symbols[sym].Type & 0x80000000)) {

         // Prepare for writing symbol label
         Symname = Symbols.GetName(sym);         // Symbol name
         // Check if label needs a separate line
         SeparateLine = (ElementSize != DataSize 
            || Symbols[sym].Size != DataSize 
            || strlen(Symname) > AsmTab1
            || sym < sym2 
            // || (Sections[Section].Type & 0xFF) == 3
            || (Symbols[sym].Type+1 & 0xFE) == 0x0C);

         // Write symbol label
         switch (Syntax) {
         case SUBTYPE_MASM:
            WriteDataLabelMASM(Symname, sym, SeparateLine);  break;
         case SUBTYPE_YASM:
            WriteDataLabelYASM(Symname, sym, SeparateLine);  break;
         case SUBTYPE_GASM:
            WriteDataLabelGASM(Symname, sym, SeparateLine);  break;
         }
         LineState = 1;                          // Label written
         if (SeparateLine) {
            LineState = 0;
         }
      }
   }

   if ((Sections[Section].Type & 0xFF) == 3 || Pos >= Sections[Section].InitSize) {
      // This is an unitialized data (BSS) section
      // Data repeat count
      uint32 DataCount = (DataEnd - Pos) / ElementSize;
      if (DataCount) {
         OutFile.Tabulate(AsmTab1);
         // Write data directives
         switch (Syntax) {
         case SUBTYPE_MASM:
            WriteUninitDataItemsMASM(ElementSize, DataCount);  break;
         case SUBTYPE_YASM:
            WriteUninitDataItemsYASM(ElementSize, DataCount);  break;
         case SUBTYPE_GASM:
            WriteUninitDataItemsGASM(ElementSize, DataCount);  break;
         }
         // Write comment
         WriteDataComment(ElementSize, Pos, Pos, 0);
         OutFile.NewLine();
         LineState = 0;
      }
      // Update data position
      Pos += DataCount * ElementSize;

      if (Pos < DataEnd) {
         // Some odd data remain. Write as bytes
         DataCount = DataEnd - Pos;
         ElementSize = 1;
         OutFile.Tabulate(AsmTab1);
         switch (Syntax) {
         case SUBTYPE_MASM:
            WriteUninitDataItemsMASM(ElementSize, DataCount);  break;
         case SUBTYPE_YASM:
            WriteUninitDataItemsYASM(ElementSize, DataCount);  break;
         case SUBTYPE_GASM:
            WriteUninitDataItemsGASM(ElementSize, DataCount);  break;
         }
         // Write comment
         WriteDataComment(ElementSize, Pos, Pos, 0);
         OutFile.NewLine();
         Pos = DataEnd;
         LineState = 0;
      }
   }
   else {
      // Not a BSS section
      // Label has been written, write data

      // Loop for one or more elements
      LinePos = Pos;
      while (Pos < DataEnd) {

         // Find end of line position
         LineEnd = LinePos + BytesPerLine;

         // Remember element size and relocation
         OldElementSize = ElementSize;
         Oldirel = irel;

         // Check if relocation
         Rel.Section = Section;
         Rel.Offset  = Pos;
         uint32 irel = Relocations.FindFirst(Rel);
         if (irel >= Relocations.GetNumEntries() || Relocations[irel].Section != Section) {
            // No relevant relocation
            irel = 0;
         }
         if (irel) {
            // A relocation is found
            // Check relocation source
            RelOffset = Relocations[irel].Offset;
            if (RelOffset == Pos) {
               // Relocation source is here
               // Make sure the size fits and begin new line
               ElementSize = Relocations[irel].Size;  BytesPerLine = 8;
               if (ElementSize < 1) ElementSize = WordSize / 8;
               if (ElementSize < 1) ElementSize = 4;
               LineEnd = Pos + ElementSize;
               if (LineState > 2) LineState = 4; // Make sure we begin at new line
            }
            else if (RelOffset < Pos + ElementSize) {
               // Relocation source begins before end of element with current ElementSize
               // Change ElementSize to make sure a new element begins at relocation source
               ElementSize = 1;  BytesPerLine = 8;
               LineEnd = RelOffset;
               if (LineState > 2) LineState = 4; // Make sure we begin at new line
               irel = 0;
            }
            else {
               // Relocation is after this element
               irel = 0;
            }
            // Check for overlapping relocations
            if (irel && irel+1 < Relocations.GetNumEntries() 
               && Relocations[irel+1].Section == Section
               && Relocations[irel+1].Offset < RelOffset + ElementSize) {
                  // Overlapping relocations
                  s.Errors |= 0x2000;
                  WriteErrorsAndWarnings();
                  LineEnd = Relocations[irel+1].Offset;
                  if (LineState > 2) LineState = 4; // Make sure we begin at new line
               }
               // Drop alignment
               FlagPrevious &= ~0xF00;
         }
         if (irel == 0) {
            // No relocation here
            // Check if DataEnd would be exceeded
            if (Pos + ElementSize > DataEnd) {
               // Make sure we end at DataEnd unless there is a relocation source here
               ElementSize = 1;  BytesPerLine = 8;
               LineEnd = DataEnd;
               if (LineState > 2) LineState = 4; // Make sure we begin at new line
               FlagPrevious &= ~0xF00;           // Drop alignment
            }
         }
         // Check if new line needed
         if (LineState == 4) {
            // Finish this line
            if (!(CodeMode & 3)) {
               WriteDataComment(OldElementSize, LinePos, Pos, Oldirel);
            }
            // Start new line
            OutFile.NewLine();
            LineState = 0;
            LinePos = Pos;
            continue;
         }

         // Tabulate
         OutFile.Tabulate(AsmTab1);

         if (LineState < 2) {
            // Write data definition directive for appropriate size
            switch (Syntax) {
            case SUBTYPE_MASM:
               WriteDataDirectiveMASM(ElementSize);  break;
            case SUBTYPE_YASM:
               WriteDataDirectiveYASM(ElementSize);  break;
            case SUBTYPE_GASM:
               WriteDataDirectiveGASM(ElementSize);  break;
            }
            LineState = 2;
         }
         else if (LineState == 3) {
            // Not the first element, write comma
            OutFile.Put(", ");
         }
         // Get inline value
         switch (ElementSize) {
         case 1:  Value = Get<int8>(Pos);  break;
         case 2:  Value = Get<int16>(Pos);  break;
         case 4:  Value = Get<int32>(Pos);  break;
         case 6:  Value = Get<uint32>(Pos) + ((uint64)Get<uint16>(Pos+4) << 32); break;
         case 8:  Value = Get<int64>(Pos);  break;
         case 10: Value = Get<int64>(Pos); break;
         }
         if (irel) {
            // There is a relocation here. Write the name etc.
            WriteRelocationTarget(irel, 1, Value);
         }
         else {
            // Write value
            switch (ElementSize) {
            case 1:
               OutFile.PutHex((uint8)Value, 1);  
               break;
            case 2:
               OutFile.PutHex((uint16)Value, 1);  
               break;
            case 4:
               OutFile.PutHex((uint32)Value, 1);  
               break;
            case 6:
               OutFile.PutHex((uint16)(Value >> 32), 1);  
               OutFile.Put(":");
               OutFile.PutHex((uint32)Value, 1);  
               break;
            case 8:
               OutFile.PutHex((uint64)Value, 1);  
               break;
            case 10:
               OutFile.Put("??");
               break;
            }
         }
         LineState = 3;
         // Increment position
         Pos += ElementSize;

         // Check if end of line
         if (Pos >= LineEnd || Pos >= DataEnd) LineState = 4;

         if (LineState == 4) {
            // End of line
            if (!(CodeMode & 3)) {
               // Write comment
               WriteDataComment(ElementSize, LinePos, Pos, irel);
            }
            OutFile.NewLine();
            LinePos = Pos;
            LineState = 0;
         }
      }
   }

   // Indicate end
   if (IEnd < Pos) IEnd = Pos;
   if (IEnd > LabelEnd) IEnd = LabelEnd;
   if (IEnd > FunctionEnd && FunctionEnd) IEnd = FunctionEnd;

   // Reset FlagPrevious if not aligned
   if (DataSize < 16 || (DataType & 0xFF) == 0x28) FlagPrevious = 0;
}


void CDisassembler::WriteDataLabelMASM(const char * name, uint32 sym, int line) {
   // Write label before data item, MASM syntax
   // name = name of data item(s)
   // sym  = symbol index
   // line = 1 if label is on separate line, 0 if data follows on same line
   // Write name
   OutFile.Put(name);
   // At least one space
   OutFile.Put(" ");
   // Tabulate
   OutFile.Tabulate(AsmTab1);

   if (line) {
      // Write label and type on seperate line
      // Get size
      uint32 Symsize = Symbols[sym].Size;
      if (Symsize == 0) Symsize = DataSize;
      OutFile.Put("label ");
      // Write type
      switch(Symsize) {
      case 1: default:
         OutFile.Put("byte");  break;
      case 2:
         OutFile.Put("word");  break;
      case 4:
         OutFile.Put("dword");  break;
      case 6:
         OutFile.Put("fword");  break;
      case 8:
         OutFile.Put("qword");  break;
      case 10:
         OutFile.Put("tbyte");  break;
      case 16:
         OutFile.Put("xmmword");  break;
      case 32:
         OutFile.Put("ymmword");  break;
      }
      // Check if jump table or call table
      if ((Symbols[sym].Type+1 & 0xFE) == 0x0C) {
         OutFile.Tabulate(AsmTab3);
         OutFile.Put(CommentSeparator);
         if (Symbols[sym].DLLName) {
            // DLL import
            OutFile.Put("import from ");
            OutFile.Put(Symbols.GetDLLName(sym));
         }
         else if (Symbols[sym].Type & 1) {
            OutFile.Put("switch/case jump table");
         }
         else {
            OutFile.Put("virtual table or function pointer");
         }
      }
      // New line
      OutFile.NewLine();
   }
}

void CDisassembler::WriteDataLabelYASM(const char * name, uint32 sym, int line) {
   // Write label before data item, YASM syntax
   // name = name of data item(s)
   // sym  = symbol index
   // line = 1 if label is on separate line, 0 if data follows on same line
   // Write name and colon
   OutFile.Put(name);
   OutFile.Put(": ");
   // Tabulate
   OutFile.Tabulate(AsmTab1);

   if (line) {
      // Write label on seperate line
      // Write comment
      OutFile.Tabulate(AsmTab3);
      OutFile.Put(CommentSeparator);
      // Check if jump table or call table
      if ((Symbols[sym].Type+1 & 0xFE) == 0x0C) {
         if (Symbols[sym].DLLName) {
            // DLL import
            OutFile.Put("import from ");
            OutFile.Put(Symbols.GetDLLName(sym));
         }
         else if (Symbols[sym].Type & 1) {
            OutFile.Put("switch/case jump table");
         }
         else {
            OutFile.Put("virtual table or function pointer");
         }
      }
      else {
         // Write size
         uint32 Symsize = Symbols[sym].Size;
         if (Symsize == 0) Symsize = DataSize;
         switch(Symsize) {
         case 1: default:
            OutFile.Put("byte");  break;
         case 2:
            OutFile.Put("word");  break;
         case 4:
            OutFile.Put("dword");  break;
         case 6:
            OutFile.Put("fword");  break;
         case 8:
            OutFile.Put("qword");  break;
         case 10:
            OutFile.Put("tbyte");  break;
         case 16:
            OutFile.Put("oword");  break;
         case 32:
            OutFile.Put("yword");  break;
         }
      }
      // New line
      OutFile.NewLine();
   }
}

void CDisassembler::WriteDataLabelGASM(const char * name, uint32 sym, int line) {
   // Write label before data item, GAS syntax
   // name = name of data item(s)
   // sym  = symbol index
   // line = 1 if label is on separate line, 0 if data follows on same line
   // Write name and colon
   OutFile.Put(name);
   OutFile.Put(": ");
   // Tabulate
   OutFile.Tabulate(AsmTab1);

   if (line) {
      // Write label on seperate line
      // Write comment
      OutFile.Tabulate(AsmTab3);
      OutFile.Put(CommentSeparator);
      // Check if jump table or call table
      if ((Symbols[sym].Type+1 & 0xFE) == 0x0C) {
         if (Symbols[sym].DLLName) {
            // DLL import
            OutFile.Put("import from ");
            OutFile.Put(Symbols.GetDLLName(sym));
         }
         else if (Symbols[sym].Type & 1) {
            OutFile.Put("switch/case jump table");
         }
         else {
            OutFile.Put("virtual table or function pointer");
         }
      }
      else {
         // Write size
         uint32 Symsize = Symbols[sym].Size;
         if (Symsize == 0) Symsize = DataSize;
         switch(Symsize) {
         case 1: default:
            OutFile.Put("byte");  break;
         case 2:
            OutFile.Put("word");  break;
         case 4:
            OutFile.Put("int");  break;
         case 6:
            OutFile.Put("farword");  break;
         case 8:
            OutFile.Put("qword");  break;
         case 10:
            OutFile.Put("tfloat");  break;
         case 16:
            OutFile.Put("xmmword");  break;
         case 32:
            OutFile.Put("ymmword");  break;
         }
      }
      // New line
      OutFile.NewLine();
   }
}

void CDisassembler::WriteUninitDataItemsMASM(uint32 size, uint32 count) {
   // Write uninitialized (BSS) data, MASM syntax
   // size = size of each data element
   // count = number of data elements on each line
         
   // Write data definition directive for appropriate size
   switch (size) {
   case 1:
      OutFile.Put("db ");  break;
   case 2:
      OutFile.Put("dw ");  break;
   case 4:
      OutFile.Put("dd ");  break;
   case 6:
      OutFile.Put("df ");  break;
   case 8:
      OutFile.Put("dq ");  break;
   case 10:
      OutFile.Put("dt ");  break;
   }
   OutFile.Tabulate(AsmTab2);
   if (count > 1) {
      // Write duplication operator
      OutFile.PutDecimal(count);
      OutFile.Put(" dup (?)");
   }
   else {
      // DataCount == 1
      OutFile.Put("?");
   }
}

void CDisassembler::WriteUninitDataItemsYASM(uint32 size, uint32 count) {
   // Write uninitialized (BSS) data, YASM syntax
   // Write data definition directive for appropriate size
   switch (size) {
   case 1:
      OutFile.Put("resb ");  break;
   case 2:
      OutFile.Put("resw ");  break;
   case 4:
      OutFile.Put("resd ");  break;
   case 6:
      OutFile.Put("resw ");  count *= 3;  break;
   case 8:
      OutFile.Put("resq ");  break;
   case 10:
      OutFile.Put("rest ");  break;
   }
   OutFile.Tabulate(AsmTab2);
   OutFile.PutDecimal(count);
}

void CDisassembler::WriteUninitDataItemsGASM(uint32 size, uint32 count) {
   // Write uninitialized (BSS) data, GAS  syntax
   OutFile.Put(".zero");
   OutFile.Tabulate(AsmTab2);
   if (count != 1) {
      OutFile.PutDecimal(count);  OutFile.Put(" * ");
   }
   OutFile.PutDecimal(size);
}

void CDisassembler::WriteDataDirectiveMASM(uint32 size) {
   // Write DB, etc., MASM syntax
   // Write data definition directive for appropriate size
   switch (size) {
   case 1:  OutFile.Put("db ");  break;
   case 2:  OutFile.Put("dw ");  break;
   case 4:  OutFile.Put("dd ");  break;
   case 6:  OutFile.Put("df ");  break;
   case 8:  OutFile.Put("dq ");  break;
   case 10: OutFile.Put("dt ");  break;
   case 16: OutFile.Put("xmmword ");  break;
   case 32: OutFile.Put("ymmword ");  break;
   default: OutFile.Put("Error ");  break;
   }
}

void CDisassembler::WriteDataDirectiveYASM(uint32 size) {
   // Write DB, etc., YASM syntax
   // Write data definition directive for appropriate size
   switch (size) {
   case 1:  OutFile.Put("db ");  break;
   case 2:  OutFile.Put("dw ");  break;
   case 4:  OutFile.Put("dd ");  break;
   case 6:  OutFile.Put("df ");  break;
   case 8:  OutFile.Put("dq ");  break;
   case 10: OutFile.Put("dt ");  break;
   case 16: OutFile.Put("ddq ");  break;
   default: OutFile.Put("Error ");  break;
   }
}

void CDisassembler::WriteDataDirectiveGASM(uint32 size) {
   // Write DB, etc., GAS syntax
   // Write data definition directive for appropriate size
   switch (size) {
   case 1:  OutFile.Put(".byte  ");  break;
   case 2:  OutFile.Put(".short ");  break;
   case 4:  OutFile.Put(".int   ");  break;
   case 8:  OutFile.Put(".quad  ");  break;
   case 10: OutFile.Put(".tfloat ");  break;
   default: OutFile.Put("Error ");  break;
   }
}


void CDisassembler::WriteDataComment(uint32 ElementSize, uint32 LinePos, uint32 Pos, uint32 irel) {
   // Write comment after data item
   uint32 pos1;                            // Position of data for comment
   uint32 RelType = 0;                     // Relocation type
   char TextBuffer[64];                    // Buffer for writing floating point number

   OutFile.Tabulate(AsmTab3);              // Tabulate to comment field
   OutFile.Put(CommentSeparator);          // Start comment

   // Write address
   if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) {
      // Write 32 bit address
      OutFile.PutHex(LinePos + SectionAddress + (uint32)ImageBase);
   }
   else {
      // Write 16 bit address
      OutFile.PutHex((uint16)(LinePos + SectionAddress));
   }

   if ((Sections[Section].Type & 0xFF) == 3 || Pos > Sections[Section].InitSize) {
      // Unitialized data. Write no data
      return;
   }

   if (irel && irel < Relocations.GetNumEntries() && Relocations[irel].Offset == LinePos) {
      // Value is relocated, get relocation type
      RelType = Relocations[irel].Type;
   }

   // Space after address
   OutFile.Put(" _ ");

   // Comment type depends on ElementSize and DataType
   switch (ElementSize) {
   case 1:
      // Bytes. Write ASCII characters
      for (pos1 = LinePos; pos1 < Pos; pos1++) {
         // Get character
         int8 c = Get<int8>(pos1);
         // Avoid non-printable characters
         if (c < ' ' || c == 0x7F) c = '.';
         // Print ASCII character
         OutFile.Put(c);
      }
      break;
   case 2:
      // Words. Write as decimal
      for (pos1 = LinePos; pos1 < Pos; pos1 += 2) {
         if (RelType) {
            OutFile.PutHex(Get<uint16>(pos1), 1); // Write as hexadecimal
         }
         else {
            OutFile.PutDecimal(Get<int16>(pos1), 1);// Write as signed decimal
         }
         OutFile.Put(' ');
      }
      break;
   case 4:
      // Dwords      
      for (pos1 = LinePos; pos1 < Pos; pos1 += 4) {
         if ((DataType & 0x47) == 0x43) {
            // Write as float
            sprintf(TextBuffer, "%.8G", Get<float>(pos1));
            OutFile.Put(TextBuffer);
            // Make sure the number has a . or E to indicate a floating point number
            if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0");
         }
         else if (((DataType + 1) & 0xFF) == 0x0C || RelType) {
            // jump/call address or offset. Write as hexadecimal
            OutFile.PutHex(Get<uint32>(pos1));
         }
         else {
            // Other. Write as decimal
            OutFile.PutDecimal(Get<int32>(pos1), 1);
         }
         OutFile.Put(' ');
      }
      break;                 
   case 8:
      // Qwords
      for (pos1 = LinePos; pos1 < Pos; pos1 += 8) {
         if ((DataType & 0x47) == 0x44) {
            // Write as double
            sprintf(TextBuffer, "%.16G", Get<double>(pos1));
            OutFile.Put(TextBuffer);
            // Make sure the number has a . or E to indicate a floating point number
            if (!strchr(TextBuffer,'.') && !strchr(TextBuffer,'E')) OutFile.Put(".0");
         }
         else {
            // Write as hexadecimal
            OutFile.PutHex(Get<uint64>(pos1));
         }
         OutFile.Put(' ');
      }
      break;
   case 10:
      // tbyte. Many compilers do not support long doubles in sprintf. Write as bytes
      for (pos1 = LinePos; pos1 < Pos; pos1++) {
         OutFile.PutHex(Get<uint8>(pos1), 1);
      }
      break;
   }
   if (RelType) {
      // Indicate relocation type
      OutFile.Put(Lookup(RelocationTypeNames, RelType));
   }
}


void CDisassembler::WriteRelocationTarget(uint32 irel, uint32 Context, int64 Addend) {
   // Write cross reference, including addend, but not including segment override and []
   // irel = index into Relocations
   // Context:
   // 1      = Data definition
   // 2      = Immediate data field in instruction
   // 4      = Data address in instruction
   // 8      = Near jump/call destination
   // 0x10   = Far  jump/call destination
   // 0x100  = Self-relative address expected
   // Addend:  inline addend
   // Implicit parameters:
   // IBegin:  value of '$' operator
   // IEnd:    reference point for self-relative addressing
   // BaseReg, IndexReg

   uint32 RefFrame;                    // Target segment
   int32  Addend2 = 0;                 // Difference between '$' and reference point

   // Get relocation type
   uint32 RelType = Relocations[irel].Type;

   if (RelType & 0x60) {
      // Inline addend is already relocated. 
      // Ignore addend and treat as direct relocation
      RelType = 1;
      Addend = 0;
   }

   // Get relocation size
   uint32 RelSize = Relocations[irel].Size;

   // Get relocation addend
   Addend += Relocations[irel].Addend;

   // Get relocation target
   uint32 Target = Relocations[irel].TargetOldIndex;

   // Is offset operand needed?
   if (Syntax != SUBTYPE_YASM && (
      ((RelType & 0xB) && (Context & 2)) 
      || ((RelType & 8) && (Context & 0x108)))) {
      // offset operator needed to convert memory operand to immediate address
      OutFile.Put("offset ");
   }

   // Is seg operand needed?
   if (RelType & 0x200) {
      // seg operator needed to convert memory operand to its segment
      OutFile.Put("seg ");
   }

   // Is explicit segment or frame needed?
   if ((RelType & 0x408) && (Context & 0x11B)) {
      // Write name of segment/group frame
      RefFrame = Relocations[irel].RefOldIndex;
      if (!RefFrame) {
         // No frame. Use segment of symbol
         RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section;
      }
      if (RefFrame && RefFrame < Sections.GetNumEntries()) {
         // Write segment or group name
         const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name;
         OutFile.Put(SecName);
         OutFile.Put(":");
      }
   }

   // Is imagerel operator needed?
   if (RelType & 4) {
      // imagerel operator needed to get image-relative address
      OutFile.Put("imagerel(");
   }

   // Adjust addend
   // Adjust offset if self-relative relocation expected and found
   if ((RelType & 2) && (Context & 0x108)) {
      // Self-relative relocation expected and found
      // Adjust by size of address field and immediate field
      Addend += IEnd - Relocations[irel].Offset;
   }
   // Subtract self-reference if unexpected self-relative relocation
   if ((RelType & 2) && !(Context & 0x108)) {
      // Self-relative relocation found but not expected
      // Fix difference between '$' and reference point
      Addend2 = Relocations[irel].Offset - IBegin;
      Addend -= Addend2;
   }
   // Add self-reference if self-relative relocation expected but not found
   if (!(RelType & 2) && (Context & 0x108)) {
      // Self-relative relocation expected but not found
      // Fix difference between '$' and reference point
      Addend += IEnd - IBegin;
   }

   if (RelType & 0x100) {
      // Target is a segment
      RefFrame = Symbols[Symbols.Old2NewIndex(Target)].Section;
      if (RefFrame && RefFrame < Sections.GetNumEntries()) {
         const char * SecName = NameBuffer.Buf()+Sections[RefFrame].Name;
         OutFile.Put(SecName);
      }
      else {
         OutFile.Put("undefined segment");
      }
   }
   else {
      // Target is a symbol

      // Find target symbol
      uint32 TargetSym = Symbols.Old2NewIndex(Target);

      // Check if Target is appropriate
      if (((Symbols[TargetSym].Type & 0x80000000) || (int32)Addend)
      && !(CodeMode == 1 && s.BaseReg)) {
         // Symbol is a start-of-section entry in symbol table, or has an addend
         // Look for a more appropriate symbol, except if code with base register
         uint32 sym, sym1, sym2 = 0;
         sym1 = Symbols.FindByAddress(Symbols[TargetSym].Section, Symbols[TargetSym].Offset + (int32)Addend, &sym2);
         for (sym = sym1; sym && sym <= sym2; sym++) {
            if (Symbols[sym].Scope && !(Symbols[sym].Type & 0x80000000)) {
               // Found a better symbol name for target address
               TargetSym = sym;
               Addend = Addend2;
            }
         }
      }
      // Write name of target symbol
      OutFile.Put(Symbols.GetName(TargetSym));

      if (Syntax == SUBTYPE_GASM && (
      RelType == 0x41 || RelType == 0x81 || RelType == 0x2002)) {
         // make PLT entry
         OutFile.Put("@PLT");
      }
   }

   // End parenthesis if we started one
   if (RelType & 4) {
      OutFile.Put(")");
   }

   // Subtract reference point, if any
   if (RelType & 0x10) {
      OutFile.Put("-");
      // Write name of segment/group frame
      uint32 RefPoint = Relocations[irel].RefOldIndex;
      if (RefPoint) {
         // Reference point name valid
         OutFile.Put(Symbols.GetNameO(RefPoint));
      }
      else {
         OutFile.Put("Reference_Point_Missing");
      }
   }

   // Subtract self-reference if unexpected self-relative relocation
   if ((RelType & 2) && !(Context & 0x108)) {
      // Self-relative relocation found but not expected
      OutFile.Put("-"); OutFile.Put(HereOperator);
   }

   // Add self-reference if self-relative relocation expected but not found
   if (!(RelType & 2) && (Context & 0x108)) {
      // Self-relative relocation expected but not found
      OutFile.Put("+"); OutFile.Put(HereOperator);
   }

   // Write addend, if not zero
   if (Addend) {
      if (Addend < 0) {
         // Negative, write "-"
         OutFile.Put("-");
         Addend = -Addend;
      }
      else {
         // Positive, write "+"
         OutFile.Put("+");
      }

      // Write value as hexadecimal
      switch (RelSize) {
      case 1:
         OutFile.PutHex((uint8)Addend, 1);
         break;
      case 2:
         OutFile.PutHex((uint16)Addend, 2);
         break;
      case 4:
         OutFile.PutHex((uint32)Addend, 2);
         break;
      case 6:
         OutFile.PutHex((uint16)(Addend >> 32), 1);
         OutFile.Put(":");
         OutFile.PutHex((uint32)Addend, 1);
         break;
      case 8:
         OutFile.PutHex((uint64)Addend, 2);
         break;
      default:
         OutFile.Put("??"); // Should not occur
         break;
      }
   }
}


int CDisassembler::WriteFillers() {
   // Check if code is a series of NOPs or other fillers. 
   // If so then write it as filler and return 1.
   // If not, then return 0.

   // Check if code is filler
   if (!(OpcodeOptions & 0x40)) {
      // This instruction can not be used as filler
      return 0;
   }
   uint32 FillerType;                            // Type of filler
   const char * FillerName = s.OpcodeDef->Name;  // Name of filler
   uint32 IFillerBegin = IBegin;                 // Start of filling space
   uint32 IFillerEnd;                            // End of filling space

   // check for CC = int 3 breakpoint, 3C00 = 90 NOP, 11F = multibyte NOP
   if (Opcodei == 0xCC || (Opcodei & 0xFFFE) == 0x3C00 || Opcodei == 0x11F) {
      // Instruction is a NOP or int 3 breakpoint
      FillerType = Opcodei;
   }
   else if (s.Warnings1 & 0x1000000) {
      // Instruction is a LEA, MOV, etc. with same source and destination
      // used as a multi-byte NOP
      FillerType = 0xFFFFFFFF;
   }
   else {
      // This instruction does something. Not a filler
      return 0;
   }
   // Save beginning position
   IFillerEnd = IEnd = IBegin;

   // Loop through instructions to find all consecutive fillers
   while (NextInstruction2()) {

      // Parse instruction
      ParseInstruction();

      // Check if code is filler
      if (!(OpcodeOptions & 0x40)) {
         // This instruction can not be a filler
         // Save position of this instruction
         IFillerEnd = IBegin;
         break;
      }
      if (Opcodei != 0xCC && (Opcodei & 0xFFFE) != 0x3C00 && Opcodei != 0x11F
      && !(s.Warnings1 & 0x1000000)) {
         // Not a filler
         // Save position of this instruction
         IFillerEnd = IBegin;
         break;
      }
      // If loop exits here then fillers end at end of this instruction
      IFillerEnd = IEnd;
   }
   // Safety check
   if (IFillerEnd <= IFillerBegin) return 0;

   // Size of fillers
   uint32 FillerSize = IFillerEnd - IFillerBegin;

   // Write size of filling space
   OutFile.Put(CommentSeparator);
   OutFile.Put("Filling space: ");
   OutFile.PutHex(FillerSize, 2);
   OutFile.NewLine();
   // Write filler type
   OutFile.Put(CommentSeparator);
   OutFile.Put("Filler type: ");
   switch (FillerType) {
   case 0xCC:
      FillerName = "INT 3 Debug breakpoint"; break;
   case 0x3C00:
      FillerName = "NOP"; break;
   case 0x3C01:
      FillerName = "NOP with prefixes"; break;
   case 0x011F:
      FillerName = "Multi-byte NOP";break;
   }
   OutFile.Put(FillerName);
   if (FillerType == 0xFFFFFFFF) {
      OutFile.Put(" with same source and destination");
   }

   // Write as bytes
   uint32 Pos;
   for (Pos = IFillerBegin; Pos < IFillerEnd; Pos++) {
      if ((Pos - IFillerBegin & 7) == 0) {
         // Start new line
         OutFile.NewLine();
         OutFile.Put(CommentSeparator);
         OutFile.Tabulate(AsmTab1);
         OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "db ");
      }
      else {
         // Continue on same line
         OutFile.Put(", ");
      }
      // Write byte value
      OutFile.PutHex(Get<uint8>(Pos), 1);
   }
   // Blank line
   OutFile.NewLine(); OutFile.NewLine();

   // Find alignment
   uint32 Alignment = 4;                         // Limit to 2^4 = 16

   // Check if first non-filler is aligned by this value
   while (Alignment && (IFillerEnd & (1 << Alignment) - 1)) {
      // Not aligned by 2^Alignment
      Alignment--;
   }
   if (Alignment) {

      // Check if smaller alignment would do
      if (Alignment > 3 && FillerSize < 1u << (Alignment-1)) {
         // End is aligned by 16, but there are less than 8 filler bytes.
         // Change to align 8
         Alignment--;
      }
      // Write align directive
      WriteAlign(1 << Alignment);
      // Prevent writing ALIGN again
      FlagPrevious &= ~1;
   }

   // Restore IBegin and IEnd to beginning of first non-filler instruction
   IBegin = IEnd = IFillerEnd;

   if (LabelInaccessible == IFillerBegin && IFillerEnd < LabelEnd) {
      // Mark first instruction after filler as inaccessible
      LabelInaccessible = IFillerEnd;
   }

   // Return success. Fillers have been written. Don't write as normal instructions
   return 1;
}

void CDisassembler::WriteAlign(uint32 a) {
   // Write alignment directive
   OutFile.Put(Syntax == SUBTYPE_GASM ? ".ALIGN" : "ALIGN");
   OutFile.Tabulate(AsmTab1);
   OutFile.PutDecimal(a);
   OutFile.NewLine();
}

void CDisassembler::WriteFileBegin() {
   // Write begin of file

   // Initial comment
   OutFile.Put(CommentSeparator);
   OutFile.Put("Disassembly of file: ");
   OutFile.Put(cmd.InputFile);
   OutFile.NewLine();
   // Date and time. 
   // Note: will fail after year 2038 on computers that use 32-bit time_t
   time_t time1 = time(0);
   char * timestring = ctime(&time1);
   if (timestring) {
      // Remove terminating '\n' in timestring
      for (char *c = timestring; *c; c++) {
         if (*c < ' ') *c = 0;
      }
      // Write date and time as comment
      OutFile.Put(CommentSeparator);
      OutFile.Put(timestring);
      OutFile.NewLine();
   }

   // Write mode
   OutFile.Put(CommentSeparator);
   OutFile.Put("Mode: ");
   OutFile.PutDecimal(WordSize);
   OutFile.Put(" bits");
   OutFile.NewLine();

   // Write syntax dialect
   OutFile.Put(CommentSeparator);
   OutFile.Put("Syntax: ");
   switch (Syntax) {
   case SUBTYPE_MASM:
      OutFile.Put(WordSize < 64 ? "MASM/ML" : "MASM/ML64");  break;
   case SUBTYPE_YASM:
      OutFile.Put("YASM/NASM");  break;
   case SUBTYPE_GASM:
      OutFile.Put("GAS(Intel)");  break;
   }
   OutFile.NewLine();

   // Write instruction set as comment
   // Instruction set is at least .386 if 32 bit mode
   if (InstructionSetMax < 3 && (MasmOptions & 0x200)) InstructionSetMax = 3;

   // Get name of basic instruction set
   const char * set0 = "";
   if (InstructionSetMax < InstructionSetNamesLen) {
      set0 = InstructionSetNames[InstructionSetMax];
   }

   // Write as comment
   OutFile.Put(CommentSeparator);
   OutFile.Put("Instruction set: ");
   OutFile.Put(set0);

   if (InstructionSetAMDMAX) {
      // Get name of any AMD-specific instruction set
      const char * setA = "";
      switch (InstructionSetAMDMAX) {
      case 1:  setA = "AMD 3DNow";   break;
      case 2:  setA = "AMD 3DNowE";  break;
      case 4:  setA = "AMD SSE4a";   break;
      case 5:  setA = "AMD XOP";     break;
      case 6:  setA = "AMD FMA4";    break;
      case 7:  setA = "AMD CVT16";   break;
      }
      if (*setA) {
         OutFile.Put(", ");
         OutFile.Put(setA);
      }
   }
   // VIA instruction set:
   if (InstructionSetOR & 0x2000) OutFile.Put(", VIA");

   // Additional instruction sets:
   if (WordSize > 32) OutFile.Put(", x64");
   if (InstructionSetOR & 0x100) OutFile.Put(", 80x87");
   if (InstructionSetOR & 0x800) OutFile.Put(", privileged instructions");
   OutFile.NewLine();

   if (NamesChanged) {
      // Tell that symbol names have been changed
      OutFile.NewLine();
      OutFile.Put(CommentSeparator);
      OutFile.Put("Error: symbol names contain illegal characters,");
      OutFile.NewLine(); OutFile.Put(CommentSeparator);
      OutFile.PutDecimal(NamesChanged);
#if ReplaceIllegalChars
      OutFile.Put(" Symbol names changed");
#else
      OutFile.Put(" Symbol names not changed");
#endif
      OutFile.NewLine();
   }

   // Write syntax-specific initializations
   switch (Syntax) {
   case SUBTYPE_MASM:
      WriteFileBeginMASM();  
      WritePublicsAndExternalsMASM();
      break;
   case SUBTYPE_YASM:
      WriteFileBeginYASM();  
      WritePublicsAndExternalsYASMGASM();
      break;
   case SUBTYPE_GASM:
      WriteFileBeginGASM();
      WritePublicsAndExternalsYASMGASM();
      break;
   }
}


void CDisassembler::WriteFileBeginMASM() {
   // Write MASM-specific file init
   if (WordSize < 64) {
      // Write instruction set directive, except for 64 bit assembler
      const char * set1 = "";
      switch (InstructionSetMax) {
      case 0:  set1 = ".8086";  break;
      case 1:  set1 = ".186";  break;
      case 2:  set1 = ".286";  break;
      case 3:  set1 = ".386";  break;
      case 4:  set1 = ".486";  break;
      case 5:  set1 = ".586";  break;
      case 6: default:
         set1 = ".686";  break;
      }
      // Write basic instruction set
      OutFile.NewLine();
      OutFile.Put(set1);
      if (InstructionSetOR & 0x800) {
         // Privileged. Add "p"
         OutFile.Put("p");
      }
      OutFile.NewLine();
      // Write extended instruction set
      if (InstructionSetOR & 0x100) {
         // Floating point
         if (InstructionSetMax < 3) {
            OutFile.Put(".8087");  OutFile.NewLine();
         }
         else if (InstructionSetMax < 5) {
            OutFile.Put(".387");  OutFile.NewLine();
         }
      }
      if (InstructionSetMax >= 0x11) {
         // .xmm directive. Not differentiated between SSE, SSE2, etc.
         OutFile.Put(".xmm");  OutFile.NewLine();
      }
      else if (InstructionSetMax >= 7) {
         // .mmx directive
         OutFile.Put(".mmx");  OutFile.NewLine();
      }
   }
   if (MasmOptions & 1) {
      // Need dotname option
      OutFile.Put("option dotname");  OutFile.NewLine();
   }
   if (WordSize == 32) {
      // Write .model flat if 32 bit mode
      OutFile.Put(".model flat");  OutFile.NewLine();
   }
   // Initialize Assumes for segment registers
   if (!(MasmOptions & 0x100)) {
      // No 16-bit segments. Assume CS=DS=ES=SS=flat
      Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_FLAT;
   }
   else {
      // 16-bit segmented model. Segment register values unknown
      Assumes[0]=Assumes[1]=Assumes[2]=Assumes[3] = ASM_SEGMENT_UNKNOWN;
   }
   // FS and GS assumed to ERROR
   Assumes[4] = Assumes[5] = ASM_SEGMENT_ERROR;

   // Write assume if FS or GS used
   // This is superfluous because an assume directive will be written at first use of FS/GS
   if (MasmOptions & 2) {
      OutFile.Put("assume fs:nothing");  OutFile.NewLine();
   }
   if (MasmOptions & 4) {
      OutFile.Put("assume gs:nothing");  OutFile.NewLine();
   }
   OutFile.NewLine();                            // Blank line
}

void CDisassembler::WriteFileBeginYASM() {
   // Write YASM-specific file init
   OutFile.NewLine();
   if (WordSize == 64) {
      OutFile.Put("default rel"); OutFile.NewLine();
   }
   //if (InstructionSetMax >= 0x11) {OutFile.Put("%define xmmword  oword");  OutFile.NewLine();}
   //if (InstructionSetMax >= 0x19) {OutFile.Put("%define ymmword");  OutFile.NewLine();}
   OutFile.NewLine();
}

void CDisassembler::WriteFileBeginGASM() {
   // Write  GAS-specific file init
   OutFile.NewLine();
   OutFile.Put(CommentSeparator);
   OutFile.Put("Note: Uses Intel syntax with destination operand first. Remember to");
   OutFile.NewLine();
   OutFile.Put(CommentSeparator);
   OutFile.Put("put syntax directives in the beginning and end of inline assembly:");
   OutFile.NewLine();
   OutFile.Put(".intel_syntax noprefix ");
   OutFile.NewLine(); OutFile.NewLine();
}

void CDisassembler::WritePublicsAndExternalsMASM() {
   // Write public and external symbol definitions
   uint32 i;                                     // Loop counter
   uint32 LinesWritten = 0;                      // Count lines written
   const char * XName;                           // Name of external symbols

   // Loop through public symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {
      if (Symbols[i].Scope & 0x1C) {
         // Symbol is public
         OutFile.Put("public ");
         // Write name
         OutFile.Put(Symbols.GetName(i));
         // Check if weak or communal
         if (Symbols[i].Scope & 0x18) {
            // Scope is weak or communal
            OutFile.Tabulate(AsmTab3);
            OutFile.Put(CommentSeparator);
            if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak. Not supported by MASM ");
            if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal. Not supported by MASM");
         }
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
   // Loop through external symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {

      if (Symbols[i].Scope & 0x20) {
         // Symbol is external
         OutFile.Put("extern ");
         // Get name
         XName = Symbols.GetName(i);
         // Check for dynamic import
         if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) {
            // Remove "_imp" prefix from name
            XName += (uint32)strlen(Symbols.ImportTablePrefix);
         }

         // Write name
         OutFile.Put(XName);
         OutFile.Put(": ");

         // Write type
         if ((Symbols[i].Type & 0xFE) == 0x84) {
            // Far
            OutFile.Put("far");
         }
         else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) {
            // Near
            OutFile.Put("near");
         }
         else {
            // Data. Write size
            switch (GetDataItemSize(Symbols[i].Type)) {
            case 1: default: OutFile.Put("byte");  break;
            case 2: OutFile.Put("word");  break;
            case 4: OutFile.Put("dword");  break;
            case 6: OutFile.Put("fword");  break;
            case 8: OutFile.Put("qword");  break;
            case 10: OutFile.Put("tbyte");  break;
            case 16: OutFile.Put("xmmword");  break;
            case 32: OutFile.Put("ymmword");  break;
            }
         }
         // Add comment if DLL import
         if (Symbols[i].DLLName) {
            OutFile.Tabulate(AsmTab3);
            OutFile.Put(CommentSeparator);
            OutFile.Put(Symbols.GetDLLName(i));
         }
         // Finished line
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
   // Write the value of any constants
   // Loop through symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {
      // Local symbols included because there might be a rip-relative address to a named constant = 0
      if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) {
         // Symbol is constant
         // Write name
         OutFile.Put(Symbols.GetName(i));
         OutFile.Put(" equ ");
         // Write value as hexadecimal
         OutFile.PutHex(Symbols[i].Offset, 1);
         // Write decimal value as comment
         OutFile.Tabulate(AsmTab3);
         OutFile.Put(CommentSeparator);
         OutFile.PutDecimal(Symbols[i].Offset, 1);
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
   // Write any group definitions
   int32 GroupId, SegmentId;
   // Loop through sections to search for group definitions
   for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) {

      // Get section type
      uint32 SectionType = Sections[GroupId].Type;
      if (SectionType & 0x800) {
         // This is a segment group definition
         // Count number of members
         uint32 NumMembers = 0;
         // Write group name
         WriteSectionName(GroupId);
         // Write "group"
         OutFile.Put(" ");  OutFile.Tabulate(AsmTab1);  OutFile.Put("GROUP ");
         // Search for group members
         for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) {
            if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) {
               // is this first member?
               if (NumMembers++) {
                  // Not first member. Write comma
                  OutFile.Put(", ");
               }
               // Write group member
               WriteSectionName(SegmentId);
            }
         }
         // End line
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
}


void CDisassembler::WritePublicsAndExternalsYASMGASM() {
   // Write public and external symbol definitions, YASM and GAS syntax
   uint32 i;                                     // Loop counter
   uint32 LinesWritten = 0;                      // Count lines written
   const char * XName;                           // Name of external symbols

   // Loop through public symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {
      if (Symbols[i].Scope & 0x1C) {
         // Symbol is public
         if (Syntax == SUBTYPE_GASM) OutFile.Put(".");
         OutFile.Put("global ");
         // Write name
         OutFile.Put(Symbols.GetName(i));

         // Write type
         if ((Symbols[i].Type & 0xF0) == 0x80) {         
            // Symbol is a function
            if (Syntax == SUBTYPE_YASM) { 
               OutFile.Put(": function");
            }
            else if (Syntax == SUBTYPE_GASM) {
               OutFile.NewLine();
               OutFile.Put(".type ");
               OutFile.Put(Symbols.GetName(i));
               OutFile.Put(", @function");
            }
         }

         // Check if weak or communal
         if (Symbols[i].Scope & 0x18) {
            // Scope is weak or communal
            OutFile.Tabulate(AsmTab3);
            OutFile.Put(CommentSeparator);
            if (Symbols[i].Scope & 8) OutFile.Put("Note: Weak.");
            if (Symbols[i].Scope & 0x10) OutFile.Put("Note: Communal.");
         }
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
   // Loop through external symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {

      if (Symbols[i].Scope & 0x20) {
         // Symbol is external
         if (Syntax == SUBTYPE_GASM) OutFile.Put(".");
         OutFile.Put("extern ");
         // Get name
         XName = Symbols.GetName(i);
         // Check for dynamic import
         if (Symbols[i].DLLName && strncmp(XName, Symbols.ImportTablePrefix, (uint32)strlen(Symbols.ImportTablePrefix)) == 0) {
            // Remove "_imp" prefix from name
            XName += (uint32)strlen(Symbols.ImportTablePrefix);
         }
         // Write name
         OutFile.Put(XName);
         OutFile.Put(" ");
         OutFile.Tabulate(AsmTab3);
         OutFile.Put(CommentSeparator);

         // Write type
         if ((Symbols[i].Type & 0xFE) == 0x84) {
            // Far
            OutFile.Put("far");
         }
         else if ((Symbols[i].Type & 0xF0) == 0x80 || Symbols[i].DLLName) {
            // Near
            OutFile.Put("near");
         }
         else {
            // Data. Write size
            switch (GetDataItemSize(Symbols[i].Type)) {
            case 1: default: OutFile.Put("byte");  break;
            case 2: OutFile.Put("word");  break;
            case 4: OutFile.Put("dword");  break;
            case 6: OutFile.Put("fword");  break;
            case 8: OutFile.Put("qword");  break;
            case 10: OutFile.Put("tbyte");  break;
            case 16: OutFile.Put("xmmword");  break;
            case 32: OutFile.Put("ymmword");  break;
            }
         }
         // Add comment if DLL import
         if (Symbols[i].DLLName) {
            OutFile.Tabulate(AsmTab3);
            OutFile.Put(CommentSeparator);
            OutFile.Put(Symbols.GetDLLName(i));
         }
         // Finished line
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();  LinesWritten = 0;
   }
   // Write the value of any constants
   // Loop through symbols
   for (i = 0; i < Symbols.GetNumEntries(); i++) {
      if (Symbols[i].Section == ASM_SEGMENT_ABSOLUTE /*&& (Symbols[i].Scope & 0x1C)*/) {
         // Symbol is constant
         if (Syntax == SUBTYPE_YASM) {
            // Write name equ value
            OutFile.Put(Symbols.GetName(i));
            OutFile.Put(" equ ");
         }
         else {
            // Gas: write .equ name, value
            OutFile.Put(".equ ");
            OutFile.Tabulate(AsmTab1);
            OutFile.Put(Symbols.GetName(i));
            OutFile.Put(", ");
         }
         // Write value as hexadecimal
         OutFile.PutHex(Symbols[i].Offset, 1);
         // Write decimal value as comment
         OutFile.Tabulate(AsmTab3);
         OutFile.Put(CommentSeparator);
         OutFile.PutDecimal(Symbols[i].Offset, 1);
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
   // Write any group definitions
   int32 GroupId, SegmentId;
   // Loop through sections to search for group definitions
   for (GroupId = 1; GroupId < (int32)Sections.GetNumEntries(); GroupId++) {
      // Get section type
      uint32 SectionType = Sections[GroupId].Type;
      if (SectionType & 0x800) {
         // This is a segment group definition
         // Count number of members
         uint32 NumMembers = 0;
         // Write group name
         WriteSectionName(GroupId);
         // Write "group"
         OutFile.Put(" ");  OutFile.Tabulate(AsmTab1);  OutFile.Put("GROUP ");
         // Search for group members
         for (SegmentId = 1; SegmentId < (int32)Sections.GetNumEntries(); SegmentId++) {
            if (Sections[SegmentId].Group == GroupId && !(Sections[SegmentId].Type & 0x800)) {
               // is this first member?
               if (NumMembers++) {
                  // Not first member. Write comma
                  OutFile.Put(", ");
               }
               // Write group member
               WriteSectionName(SegmentId);
            }
         }
         // End line
         OutFile.NewLine();  LinesWritten++;
      }
   }
   // Blank line if anything written
   if (LinesWritten) {
      OutFile.NewLine();
      LinesWritten = 0;
   }
}


void CDisassembler::WriteFileEnd() {
   // Write end of file
   OutFile.NewLine();
   switch(Syntax) {
   case SUBTYPE_MASM:
      OutFile.Put("END");  break;
   case SUBTYPE_GASM:
      OutFile.Put(CommentSeparator);
      OutFile.Put("Return to AT&T syntax with destination operand last:");
      OutFile.NewLine();
      OutFile.Put(".att_syntax prefix ");
      OutFile.NewLine();
      break;
   case SUBTYPE_YASM:
      break;
   }
}


void CDisassembler::WriteSegmentBegin() {
   // Write begin of segment
   // Choose dialect
   switch (Syntax) {
   case SUBTYPE_MASM:
      WriteSegmentBeginMASM();  break;
   case SUBTYPE_YASM:
      WriteSegmentBeginYASM();  break;
   case SUBTYPE_GASM:
      WriteSegmentBeginGASM();  break;
   }
}


void CDisassembler::WriteSegmentBeginMASM() {
   // Write begin of segment
   OutFile.NewLine();                            // Blank line

   // Check if Section is valid
   if (Section == 0 || Section >= Sections.GetNumEntries()) {
      // Illegal segment entry
      OutFile.Put("UNKNOWN SEGMENT");  OutFile.NewLine(); 
      return;
   }

   // Write segment name
   WriteSectionName(Section);
   // Tabulate
   OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
   // Write "segment"
   OutFile.Put("SEGMENT ");

   // Write alignment
   switch (Sections[Section].Align) {
   case 0:  // 1
      OutFile.Put("BYTE ");  break;
   case 1:  // 2
      OutFile.Put("WORD ");  break;
   case 2:  // 4
      OutFile.Put("DWORD ");  break;
   case 4:  // 16
      OutFile.Put("PARA ");  break;
   //case 8:  // 256 or 4096. Definition is ambiguous!
   //   OutFile.Put("PAGE ");  break;
   default:
      // Non-standard alignment
      OutFile.Put("ALIGN("); 
      OutFile.PutDecimal(1 << Sections[Section].Align); 
      OutFile.Put(") "); 
      break;
   }
   if (WordSize != 64) {
      // "PUBLIC" not supported by ml64 assembler
      OutFile.Put("PUBLIC ");
      // Write segment word size if necessary
      if (MasmOptions & 0x100) {
         // There is at least one 16-bit segment. Write segment word size
         OutFile.Put("USE"); 
         OutFile.PutDecimal(Sections[Section].WordSize);
         OutFile.Put(" "); 
      }
   }
   // Write segment class
   switch (Sections[Section].Type & 0xFF) {
   case 1:
       OutFile.Put("'CODE'");   break;
   case 2:
       OutFile.Put("'DATA'");   break;
   case 3:
       OutFile.Put("'BSS'");    break;
   case 4:
       OutFile.Put("'CONST'");  break;
   default:;
      // Unknown class. Write nothing
   }

   // Tabulate to comment
   OutFile.Put(" ");  OutFile.Tabulate(AsmTab3);
   OutFile.Put(CommentSeparator);
   // Write section number
   OutFile.Put("section number ");  
   OutFile.PutDecimal(Section);

   // New line
   OutFile.NewLine();

   if (Sections[Section].Type & 0x1000) {
      // Communal
      OutFile.Put(CommentSeparator);
      OutFile.Put(" Communal section not supported by MASM");
      OutFile.NewLine();
   }

   if (WordSize == 16 && Sections[Section].Type == 1) {
      // 16 bit code segment. Write ASSUME CS: SEGMENTNAME
      OutFile.Put("ASSUME ");
      OutFile.Tabulate(AsmTab1);
      OutFile.Put("CS:");
      if (Sections[Section].Group) {
         // Group name takes precedence over segment name
         WriteSectionName(Sections[Section].Group);
      }
      else {
         WriteSectionName(Section);
      }
      OutFile.NewLine();
      Assumes[1] = Section;
   }
}

void CDisassembler::WriteSegmentBeginYASM() {
   // Write begin of segment
   OutFile.NewLine();                            // Blank line

   // Check if Section is valid
   if (Section == 0 || Section >= Sections.GetNumEntries()) {
      // Illegal segment entry
      OutFile.Put("UNKNOWN SEGMENT");  OutFile.NewLine(); 
      return;
   }

   // Write SECTION directive
   OutFile.Put("SECTION ");   
   // Write segment name
   WriteSectionName(Section);
   // Tabulate
   OutFile.Put(" ");  OutFile.Tabulate(AsmTab2);
   OutFile.Put("align=");
   OutFile.PutDecimal(1 << Sections[Section].Align);
   if (Sections[Section].WordSize != WordSize) {
      OutFile.Put(" use");
      OutFile.PutDecimal(Sections[Section].WordSize);
   }
   if ((Sections[Section].Type & 0xFF) == 1) {
      OutFile.Put(" execute");
   }
   else {
      OutFile.Put(" noexecute");
   }

   // Tabulate to comment
   OutFile.Put(" ");  OutFile.Tabulate(AsmTab3);
   OutFile.Put(CommentSeparator);
   // Write section number
   OutFile.Put("section number ");  
   OutFile.PutDecimal(Section);
   // Write type
   OutFile.Put(", ");
   switch (Sections[Section].Type & 0xFF) {
   case 1: OutFile.Put("code");  break;
   case 2: OutFile.Put("data");  break;
   case 3: OutFile.Put("bss");  break;
   case 4: OutFile.Put("const");  break;
   default: OutFile.Put("unknown");  break;
   }

   // New line
   OutFile.NewLine();

   if (Sections[Section].Type & 0x1000) {
      // Communal
      OutFile.Put(CommentSeparator);
      OutFile.Put(" Communal section not supported by YASM");
      OutFile.NewLine();
   }
}

void CDisassembler::WriteSegmentBeginGASM() {
   // Write begin of segment
   uint32 Type;                                  // Section type

   OutFile.NewLine();                            // Blank line

   // Check if Section is valid
   if (Section == 0 || Section >= Sections.GetNumEntries()) {
      // Illegal segment entry
      OutFile.Put("UNKNOWN SEGMENT");  OutFile.NewLine(); 
      return;
   }

   // Write SECTION directive
   OutFile.Put(".SECTION ");
   OutFile.Tabulate(AsmTab1);
   // Write segment name
   WriteSectionName(Section);
   // Tabulate
   OutFile.Put(" ");  OutFile.Tabulate(AsmTab2);
   // Flags not supported by all versions of Gas. Put as comment:
   OutFile.Put(CommentSeparator);
   // Write flags
   OutFile.Put('"');
   Type = Sections[Section].Type & 0xFF;
   if (Type) OutFile.Put('a');                   // Allocatable
   if (Type != 1 && Type != 4) OutFile.Put('w'); // Writeable
   if (Type == 1) OutFile.Put('x');              // Executable
   OutFile.Put('"');
   if (Type) OutFile.Put(", @progbits");         // Allocatable

   // Tabulate to comment
   OutFile.Put(" ");  OutFile.Tabulate(AsmTab3);
   OutFile.Put(CommentSeparator);
   // Write section number
   OutFile.Put("section number ");  
   OutFile.PutDecimal(Section);
   // Write type
   OutFile.Put(", ");
   switch (Sections[Section].Type & 0xFF) {
   case 1: OutFile.Put("code");  break;
   case 2: OutFile.Put("data");  break;
   case 3: OutFile.Put("bss");  break;
   case 4: OutFile.Put("const");  break;
   default: OutFile.Put("unknown");  break;
   }
   OutFile.NewLine();                            // Blank line
   if (Sections[Section].Type & 0x1000) {
      // Communal
      OutFile.Put(CommentSeparator);
      OutFile.Put(" Communal section ");
      OutFile.NewLine();
   }

   // Write alignment
   OutFile.Tabulate(AsmTab1);
   OutFile.Put(".ALIGN");
   OutFile.Tabulate(AsmTab2);
   OutFile.PutDecimal(1 << Sections[Section].Align);

   // New line
   OutFile.NewLine();
}


void CDisassembler::WriteSegmentEnd() {
   // Write end of segment
   OutFile.NewLine();

   if (Syntax != SUBTYPE_MASM) {
      // Not MASM syntax, write only blank line
      return;
   }

   // Check if Section is valid
   if (Section == 0 || Section >= Sections.GetNumEntries()) {
      // Illegal segment entry
      OutFile.Put("UNKNOWN ENDS");  OutFile.NewLine(); 
      return;
   }

   // Write segment name
   const char * segname = NameBuffer.Buf() + Sections[Section].Name;
   OutFile.Put(segname);

   // Tabulate
   OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
   // Write "segment"
   OutFile.Put("ENDS");
   // New line
   OutFile.NewLine();
}



void CDisassembler::WriteFunctionBegin() {
   // Write begin of function IFunction

   // Check if IFunction is valid
   if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) {
      // Should not occur
      OutFile.Put(CommentSeparator);
      OutFile.Put("Internal error: undefined function begin");
      return;
   }

   // Get symbol old index
   uint32 symi = FunctionList[IFunction].OldSymbolIndex;

   // Get symbol record
   uint32 SymI = Symbols.Old2NewIndex(symi);

   OutFile.NewLine();                        // Blank line

   // Remember that symbol has been written
   Symbols[SymI].Scope |= 0x100;

   // Check alignment if preceded by NOP
   if ((FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) {
      WriteAlign(16);
   }

   if (Symbols[SymI].Name == 0) {
      // Has no name. Probably only NOP fillers
      return;
   }

   // Write function name etc.
   switch (Syntax) {
   case SUBTYPE_MASM:
      WriteFunctionBeginMASM(SymI, Symbols[SymI].Scope);  break;
   case SUBTYPE_YASM:
      WriteFunctionBeginYASM(SymI, Symbols[SymI].Scope);  break;
   case SUBTYPE_GASM:
      WriteFunctionBeginGASM(SymI, Symbols[SymI].Scope);  break;
   }
}

void CDisassembler::WriteFunctionBeginMASM(uint32 symi, uint32 scope) {
   // Write begin of function, MASM syntax
   // Write name
   WriteSymbolName(symi);
   // Space
   OutFile.Put(" "); OutFile.Tabulate(AsmTab1);

   if (scope & 0x1C) {
      // Scope is public
      // Write "PROC"
      OutFile.Put("PROC");
      // Write "NEAR" unless 64 bit mode
      if (WordSize < 64) OutFile.Put(" NEAR");
      // Check if weak
      if (scope & 8) {
         OutFile.NewLine();
         OutFile.Put(CommentSeparator);
         OutFile.Put(" WEAK ");
         WriteSymbolName(symi);
      }
      // Check if communal
      if (scope & 0x10) {
         OutFile.NewLine();
         OutFile.Put(CommentSeparator);
         OutFile.Put(" COMDEF ");
         WriteSymbolName(symi);
      }
   }
   else {
      // Scope is local
      OutFile.Put("LABEL NEAR");
   }
   // Check if Gnu indirect
   if (Symbols[symi].Type & 0x40000000) {
      OutFile.Put(CommentSeparator);
      OutFile.Put("Gnu indirect function"); // Cannot be represented in Masm syntax
   }
   // End line
   OutFile.NewLine();
}

void CDisassembler::WriteFunctionBeginYASM(uint32 symi, uint32 scope) {
   // Write begin of function, YASM syntax
   // Write name
   WriteSymbolName(symi);
   // Colon
   OutFile.Put(":"); OutFile.Tabulate(AsmTab1);

   if (scope & 0x1C) {
      // Scope is public
      // Write comment
      OutFile.Put(CommentSeparator);
      OutFile.Put("Function begin");
      // Check if weak
      if (scope & 8) {
         OutFile.Put(", weak");
      }
      // Check if communal
      if (scope & 0x10) {
         OutFile.Put(", communal");
      }
   }
   else {
      // Scope is local. Write comment
      OutFile.Put(CommentSeparator);
      OutFile.Put("Local function");
   }
   // Check if Gnu indirect
   if (Symbols[symi].Type & 0x40000000) {
      OutFile.Put(CommentSeparator);
      OutFile.Put("Gnu indirect function"); // Cannot be represented in NASM/YASM syntax
   }
   // End line
   OutFile.NewLine();
}

void CDisassembler::WriteFunctionBeginGASM(uint32 symi, uint32 scope) {
   // Write begin of function, GAS syntax
   WriteSymbolName(symi);                        // Write name
   OutFile.Put(":"); 
   OutFile.Tabulate(AsmTab3);  OutFile.Put(CommentSeparator);
   if (scope & 3) OutFile.Put("Local ");
   if (scope & 8) OutFile.Put("weak ");
   if (scope & 0x10) OutFile.Put("communal ");
   OutFile.Put("Function");
   OutFile.NewLine();
   OutFile.Tabulate(AsmTab1);
   OutFile.Put(".type ");
   OutFile.Tabulate(AsmTab2);
   WriteSymbolName(symi); // Write name
   if (Symbols[symi].Type & 0x40000000) {
      OutFile.Put(", @gnu_indirect_function");
   }
   else {
      OutFile.Put(", @function");
   }
   OutFile.NewLine();
}


void CDisassembler::WriteFunctionEnd() {
   // Write end of function

   // Check if IFunction is valid
   if (IFunction == 0 || IFunction >= FunctionList.GetNumEntries()) {
      // Should not occur
      OutFile.Put(CommentSeparator);
      OutFile.Put("Internal error: undefined function end");
      return;
   }

   // Get symbol index
   uint32 SymOldI = FunctionList[IFunction].OldSymbolIndex;
   uint32 SymNewI = Symbols.Old2NewIndex(SymOldI);

   // check scope
   if (Symbols[SymNewI].Scope & 0x1C) {
      // Has public scope. Write end of function
      switch (Syntax) {
      case SUBTYPE_MASM:
         WriteFunctionEndMASM(SymNewI);  break;
      case SUBTYPE_YASM:
         WriteFunctionEndYASM(SymNewI);  break;
      case SUBTYPE_GASM:
         WriteFunctionEndGASM(SymNewI);  break;
      }
   }
}

void CDisassembler::WriteFunctionEndMASM(uint32 symi) {
   // Write end of function, MASM syntax
   // Write name
   WriteSymbolName(symi);

   // Space
   OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
   // Write "ENDP"
   OutFile.Put("ENDP");
   OutFile.NewLine();
}

void CDisassembler::WriteFunctionEndYASM(uint32 symi) {
   // Write end of function, YASM syntax
   // Write comment
   OutFile.Put(CommentSeparator);
   // Write name
   WriteSymbolName(symi);
   OutFile.Put(" End of function");
   OutFile.NewLine();
}

void CDisassembler::WriteFunctionEndGASM(uint32 symi){
   // Write end of function, GAS syntax
   // Write .size directive
   OutFile.Tabulate(AsmTab1);
   OutFile.Put(".size ");
   OutFile.Tabulate(AsmTab2);
   WriteSymbolName(symi);                        // Name of function
   OutFile.Put(", . - ");
   WriteSymbolName(symi);                        // Name of function
   OutFile.Tabulate(AsmTab3);
   OutFile.Put(CommentSeparator);
   OutFile.Put("End of function is probably here");
   OutFile.NewLine();
}


void CDisassembler::WriteCodeLabel(uint32 symi) {
   // Write private or public code label. symi is new symbol index

   // Get scope
   uint32 Scope = Symbols[symi].Scope;

   // Check scope
   if (Scope & 0x100) return;                    // Has been written as function begin

   if (Scope == 0) {
      // Inaccessible. No name. Make blank line
      OutFile.NewLine();
      // Remember position for warning check
      LabelInaccessible = IBegin;
      return;
   }

   // Begin on new line if preceded by another symbol
   if (OutFile.GetColumn()) OutFile.NewLine(); 

   // Check alignment if preceded by NOP
   if ((Scope & 0xFF) > 1 && (FlagPrevious & 1) && (IBegin & 0x0F) == 0 && Sections[Section].Align >= 4) {
      WriteAlign(16);
   }

   switch (Syntax) {
   case SUBTYPE_MASM:
      WriteCodeLabelMASM(symi, Symbols[symi].Scope);  break;
   case SUBTYPE_YASM:
      WriteCodeLabelYASM(symi, Symbols[symi].Scope);  break;
   case SUBTYPE_GASM:
      WriteCodeLabelGASM(symi, Symbols[symi].Scope);  break;
   }

   // Remember this has been written
   Symbols[symi].Scope |= 0x100;
}


void CDisassembler::WriteCodeLabelMASM(uint32 symi, uint32 scope) {
   // Write private or public code label, MASM syntax
   if ((scope & 0xFF) > 1) {
      // Scope > function local. Write as label near
      // Check if extra linefeed needed
      // if (!(IFunction && FunctionList[IFunction].Start == IBegin)) 
      // New line
      OutFile.NewLine();

      // Write name
      WriteSymbolName(symi);
      // Space
      OutFile.Put(" "); OutFile.Tabulate(AsmTab1);
      // Write "LABEL"
      OutFile.Put("LABEL");
      // Write "NEAR" even 64 bit mode
      OutFile.Put(" NEAR");
      // New line
      OutFile.NewLine();

      // Check if weak
      if (scope & 8) {
         OutFile.Put(CommentSeparator);
         OutFile.Put(" WEAK ");
         WriteSymbolName(symi);
         OutFile.NewLine();
      }
      // Check if communal
      if (scope & 0x10) {
         OutFile.Put(CommentSeparator);
         OutFile.Put(" COMDEF ");
         WriteSymbolName(symi);
         OutFile.NewLine();
      }
   }
   else {
      // Symbol is local to current function. Write name with colon
      if (FlagPrevious & 2) {
         // Insert blank line if previous instruction was unconditional jump or return
         OutFile.NewLine();
      }
      // Write name
      WriteSymbolName(symi);
      // Write ":"
      OutFile.Put(":");
      if (OutFile.GetColumn() > AsmTab1) {
         // Past tabstop. Go to next line
         OutFile.NewLine();                   // New line
      }
   }
}

void CDisassembler::WriteCodeLabelYASM(uint32 symi, uint32 scope) {
   // Write private or public code label, YASM syntax
   if ((scope & 0xFF) > 2) {
      // Scope is public
      OutFile.NewLine();
      // Write name
      WriteSymbolName(symi);
      OutFile.Put(":");

      // Check if weak
      if (scope & 8) {
         OutFile.Put(CommentSeparator);
         OutFile.Put(" weak ");
         WriteSymbolName(symi);
      }
      // Check if communal
      if (scope & 0x10) {
         OutFile.Put(CommentSeparator);
         OutFile.Put(" communal ");
         WriteSymbolName(symi);
      }
      OutFile.NewLine();
   }
   else {
      // Symbol is local to current function. Write name with colon
      if (FlagPrevious & 2) {
         // Insert blank line if previous instruction was unconditional jump or return
         OutFile.NewLine();
      }
      // Write name
      WriteSymbolName(symi);
      // Write ":"
      OutFile.Put(":");
      if (OutFile.GetColumn() > AsmTab1) {
         // Past tabstop. Go to next line
         OutFile.NewLine();                   // New line
      }
   }
}

void CDisassembler::WriteCodeLabelGASM(uint32 symi, uint32 scope) {
   // Write private or public code label, GAS syntax same as YASM syntax
   WriteCodeLabelYASM(symi, scope);
}

void CDisassembler::WriteAssume() {
   // Write assume directive for segment register if MASM syntax
   if (Syntax != SUBTYPE_MASM) return;
   if (!s.AddressField) return;

   int32 SegReg, PrefixSeg;                      // Segment register used
   uint32 symo;                                  // Target symbol old index
   uint32 symi;                                  // Target symbol new index
   int32 TargetSegment;                          // Target segment/section
   int32 TargetGroup;                            // Group containing target segment

   // Find which segment register is used for addressing memory operand
   SegReg = 3;  // DS is default
   if (s.BaseReg == 4+1 || s.BaseReg == 5+1) {
      // Base register is (E)BP or ESP
      SegReg = 2;     // SS register used unless there is a prefix
   }
   if (s.Prefixes[0]) {
      // There is a segment prefix
      PrefixSeg = GetSegmentRegisterFromPrefix();
      if (PrefixSeg >= 0 && PrefixSeg <= 5) {
         // Segment prefix is valid. Segment determined by segment prefix
         SegReg = PrefixSeg;
      }
   }
   // Default target segment is none
   TargetSegment = TargetGroup = 0;

   // Find symbol referenced by next instruction
   if (s.AddressRelocation && s.AddressRelocation < Relocations.GetNumEntries()) {
      symo = Relocations[s.AddressRelocation].TargetOldIndex; // Target symbol old index
      if (symo) {
         symi = Symbols.Old2NewIndex(symo);                   // Target symbol new index
         if (symi) {
            TargetSegment = Symbols[symi].Section;            // Target segment
            if (TargetSegment < 0 || TargetSegment >= (int32)Sections.GetNumEntries()) {
               TargetSegment = 0;
            }
            else {
               TargetGroup = Sections[TargetSegment].Group;   // Group containing target segment
               if (TargetGroup <= ASM_SEGMENT_ERROR || TargetGroup >= (int32)Sections.GetNumEntries()) {
                  TargetGroup = 0;
               }
            }
         }
      }
   }
   if (TargetSegment) {
      // Target has a segment. Check if it is different from currently assumed segment
      if (TargetSegment != Assumes[SegReg] && TargetGroup != Assumes[SegReg]) {
         // Assume directive needed
         // If segment belongs to a group then the group takes precedence
         if (TargetGroup) TargetSegment = TargetGroup;
         // Write assume directive
         OutFile.Put("ASSUME ");
         OutFile.Tabulate(AsmTab1);
         OutFile.Put(RegisterNamesSeg[SegReg]);  // Name of segment register used
         OutFile.Put(":");
         WriteSectionName(TargetSegment);        // Name of segment or group referenced
         OutFile.NewLine();
         Assumes[SegReg] = TargetSegment;
      }
   }
   else {
      // Target segment not specified. Assumed value may be anyting but 'error'
      if (Assumes[SegReg] <= ASM_SEGMENT_ERROR) {
         // Segment register is assumed to 'error'. Change assume to 'nothing'
         OutFile.Put("ASSUME ");
         OutFile.Tabulate(AsmTab1);
         OutFile.Put(RegisterNamesSeg[SegReg]);  // Name of segment register used
         OutFile.Put(":NOTHING");
         OutFile.NewLine();
         Assumes[SegReg] = ASM_SEGMENT_NOTHING;
      }
   }
}


void CDisassembler::WriteInstruction() {
   // Write instruction and operands
   uint32 NumOperands = 0;                       // Number of operands written
   uint32 i;                                     // Loop index
   const char * OpName;                          // Opcode name

   if (s.AddressFieldSize && Syntax == SUBTYPE_MASM) {
      // There is a memory operand. Check if ASSUME directive needed
      WriteAssume();
   }

   if (CodeMode & 6) {
      // Code is dubious. Show as comment only
      OutFile.Put(CommentSeparator);             // Start comment
   }
   else if ((s.OpcodeDef->Options & 0x20) && s.OpcodeStart1 > IBegin) {
      // Write prefixes explicitly. 
      // This is used for rare cases where the assembler cannot generate the prefix
      OutFile.Tabulate(AsmTab1);                 // Tabulate
      OutFile.Put(Syntax == SUBTYPE_GASM ? ".byte " : "DB ");
      OutFile.Tabulate(AsmTab2);                 // Tabulate
      for (i = IBegin; i < s.OpcodeStart1; i++) {
         if (i > IBegin) OutFile.Put(", ");
         OutFile.PutHex(Get<uint8>(i), 1);
      }
      OutFile.Tabulate(AsmTab3);                 // Tabulate
      OutFile.Put(CommentSeparator);
      OutFile.Put("Prefix coded explicitly");    // Comment
      OutFile.NewLine();
   }

   if ((s.Operands[0] & 0xF0) == 0xC0 || (s.Operands[1] & 0xF0) == 0xC0) {
      // String instruction or xlat instruction
      WriteStringInstruction();
      return;
   }

   OutFile.Tabulate(AsmTab1);                    // Tabulate

   if (s.Prefixes[2]) {
      OutFile.Put("lock ");                      // Lock prefix
   }

   // Get opcode name
   if (s.OpcodeDef->Name) {
      // Opcode name
      OpName = s.OpcodeDef->Name;
      // Search for opcode comment
      s.OpComment = strchr(OpName, ';');
      if (s.OpComment) s.OpComment++;            // Point to after ';'
   }
   else {
      OpName = "UNDEFINED";                      // Undefined code with no name
      s.OpComment = 0;
   }

   // Check prefix option
   if ((s.OpcodeDef->Options & 2) && (s.Prefixes[7] & 0x30)) {
      // Put prefix 'v' for VEX-prefixed instruction
      OutFile.Put('v');
   }

   // Write opcode name
   if (s.OpComment) {
      // OpName string contains opcode name and comment, separated by ';'
      while (*OpName != ';' && *OpName != 0) {   // Write opcode name until comment
         OutFile.Put(*(OpName++));
      }
   }
   else {
      OutFile.Put(OpName);                       // Write normal opcode name
   }

   // Check suffix option
   if (s.OpcodeDef->Options & 1) {
      // Append suffix for operand size or type to name
      if ((s.OpcodeDef->AllowedPrefixes & 0x3000) == 0x1000) {
         // F.P. operand size defined by W prefix bit
         i = s.Prefixes[7] & 8;  // W prefix bit
         OutFile.Put(i ? 'd' : 's');
      }
      else if ((s.OpcodeDef->AllowedPrefixes & 0x3000) == 0x3000) {
         // Integer operand size defined by W prefix bit
         i = s.Prefixes[7] & 8;  // W prefix bit
         OutFile.Put(i ? 'q' : 'd');
      }
      else if (s.OpcodeDef->AllowedPrefixes & 0xE00) {
         // F.P. operand type and size defined by prefixes
         switch (s.Prefixes[5]) {
         case 0:     // No prefix = ps
            OutFile.Put("ps");  break;
         case 0x66:  // 66 prefix = pd
            OutFile.Put("pd");  break;
         case 0xF3:  // F3 prefix = ss
            OutFile.Put("ss");  break;
         case 0xF2:  // F2 prefix = sd
            OutFile.Put("sd");  break;
         default:
            err.submit(9000); // Should not occur
         }
      }
      else if (s.OpcodeDef->AllowedPrefixes & 0x100){
         // Integer operand size defined by prefixes
         // Suffix for operand size
         i = s.OperandSize / 8;
         if (i <= 8) {
            static const char SizeSuffixes[] = " bw d f q"; // Table of suffixes
            OutFile.Put(SizeSuffixes[i]);
         }
      }
   }
   // More suffix option
   if ((s.OpcodeDef->Options & 0x400) && s.ImmediateFieldSize == 8) {
      // 64 bit immediate mov
      if (Syntax == SUBTYPE_GASM) OutFile.Put("abs");
   }   

   // Space between opcode name and operands
   OutFile.Put(" "); OutFile.Tabulate(AsmTab2);  // Tabulate. At least one space

   // Loop for all operands to write
   for (i = 0; i < 5; i++) {
      if (s.Operands[i]) {

         // Write operand i
         if (NumOperands++) {
            // At least one operand before this one. Separate by ", "
            OutFile.Put(", ");
         }

         // Write constant and jump operands
         switch (s.Operands[i] & 0xF0) {
         case 0x10: case 0x20: case 0x30: case 0x80:
            WriteImmediateOperand(s.Operands[i]);
            continue;
         }

         // Write register and memory operands
         switch (s.Operands[i] & 0xF0000) {
         case 0:        // Other type of operand
            WriteOtherOperand(s.Operands[i]);  break;

         case 0x10000:  // Direct memory operand
            WriteRMOperand(s.Operands[i]);  break;

         case 0x20000:  // Register operand indicated by last bits of opcode
            WriteShortRegOperand(s.Operands[i]);  break;

         case 0x30000:  // Register or memory operand indicated by mod/rm bits
            WriteRMOperand(s.Operands[i]);  break;

         case 0x40000:  // Register operand indicated by reg bits
            WriteRegOperand(s.Operands[i]);  break;

         case 0x50000:  // Register operand indicated by dest bits of DREX byte
            WriteDREXOperand(s.Operands[i]);  break;

         case 0x60000:  // Register operand indicated by VEX.vvvv bits
            WriteVEXOperand(s.Operands[i], 0);  break;

         case 0x70000:  // Register operand indicated by bits 4-7 of immediate operand
            WriteVEXOperand(s.Operands[i], 1);  break;

         case 0x80000:  // Register operand indicated by bits 0-3 of immediate operand
            WriteVEXOperand(s.Operands[i], 2);  break; // Unused. For future use
         }
      }
   }

   if (s.OpComment) {
      // Write opcode comment
      OutFile.Put(' ');
      OutFile.Put(CommentSeparator);
      OutFile.Put(s.OpComment);
   }
}


void CDisassembler::WriteStringInstruction() {
   // Write string instruction or xlat instruction
   uint32 NumOperands = 0;                       // Number of operands written
   uint32 i;                                     // Loop index
   uint32 Segment;                               // Possible segment prefix

   if (!(s.OpcodeDef->AllowedPrefixes & 0x1100)) {
      // Operand size is 8 if operand size prefixes not allowed
      s.OperandSize = 8;
   }

   OutFile.Tabulate(AsmTab1);                    // Tabulate

   if (Syntax != SUBTYPE_MASM && s.Prefixes[0] && (s.OpcodeDef->AllowedPrefixes & 4)) {
      // Get segment prefix
      Segment = GetSegmentRegisterFromPrefix();  // Interpret segment prefix
      // Write segment override
      OutFile.Put(RegisterNamesSeg[Segment]);
      OutFile.Put(" ");
   }     

   // Check repeat prefix
   if (s.OpcodeDef->AllowedPrefixes & 0x20) {
      if (s.Prefixes[3]) {
         // Repeat prefix
         OutFile.Put("rep ");
      }
   }
   else if (s.OpcodeDef->AllowedPrefixes & 0x40) {
      if (s.Prefixes[3] == 0xF2) {
         // repne prefix
         OutFile.Put("repne ");
      }
      else if (s.Prefixes[3] == 0xF3) {
         // repe prefix
         OutFile.Put("repe ");
      }
   }

   // Write opcode name
   OutFile.Put(s.OpcodeDef->Name);               // Opcode name

   if (Syntax == SUBTYPE_MASM
   && (((s.OpcodeDef->AllowedPrefixes & 4) && s.Prefixes[0]) 
   || ((s.OpcodeDef->AllowedPrefixes & 1) && s.Prefixes[1]))) {
      // Has segment or address size prefix. Must write operands explicitly
      OutFile.Put(" ");                          // Space before operands

      // Check address size for pointer registers
      const char * * PointerRegisterNames;
      switch (s.AddressSize) {
      case 16:
         PointerRegisterNames = RegisterNames16;  break;
      case 32:
         PointerRegisterNames = RegisterNames32;  break;
      case 64:
         PointerRegisterNames = RegisterNames64;  break;
      }

      // Loop for possibly two operands
      for (i = 0; i < 2; i++) {
         if (s.Operands[i]) {
            // Operand i defined
            if (NumOperands++) {
               // An operand before this one. Separate by ", "
               OutFile.Put(", ");
            }

            if (NumOperands == 1) {
               // Write operand size for first operand
               switch (s.OperandSize) {
               case 8:
                  OutFile.Put("byte ptr ");  break;
               case 16:
                  OutFile.Put("word ptr ");  break;
               case 32:
                  OutFile.Put("dword ptr ");  break;
               case 64:
                  OutFile.Put("qword ptr ");  break;
               }
            }

            // Get segment
            Segment = 1;                        // Default segment is DS
            if (s.Prefixes[0]) {
               Segment = GetSegmentRegisterFromPrefix(); // Interpret segment prefix
            }
            if ((s.Operands[i] & 0xCF) == 0xC2) {
               Segment = 0;                      // Segment is ES regardless of prefix for [edi] operand
            }

            // Write segment override
            OutFile.Put(RegisterNamesSeg[Segment]);
            OutFile.Put(":");

            // Opening "["
            OutFile.Put("[");

            // Write pointer register
            switch (s.Operands[i] & 0xCF) {
            case 0xC0:  // [bx], [ebx] or [rbx]
               OutFile.Put(PointerRegisterNames[3]);  
               break;
            case 0xC1:  // [si], [esi] or [rsi]
               OutFile.Put(PointerRegisterNames[6]);  
               break;
            case 0xC2:  // [di], [edi] or [rdi]
               OutFile.Put(PointerRegisterNames[7]);  
               break;
            }

            // Closing "]"
            OutFile.Put("]");
         }
      }
   }
   else {
      // We don't have to write the operands
      // Append suffix for operand size, except for xlat
      if ((s.Operands[1] & 0xCF) != 0xC0) {

         // Suffix for operand size
         uint32 i = s.OperandSize / 8;
         if (i <= 8) {
            static const char SizeSuffixes[] = " bw d   q"; // Table of suffixes
            OutFile.Put(SizeSuffixes[i]);
         }
      }
   }
}


void CDisassembler::WriteCodeComment() {
   // Write hex listing of instruction as comment after instruction
   uint32 i;                                     // Index to current byte
   uint32 FieldSize;                             // Number of bytes in field
   const char * Spacer;                          // Space between fields

   OutFile.Tabulate(AsmTab3);                    // Tabulate to comment field
   OutFile.Put(CommentSeparator);                // Start comment

   // Write address
   if (SectionEnd + SectionAddress + (uint32)ImageBase > 0xFFFF) {
      // Write 32 bit address
      OutFile.PutHex(IBegin + SectionAddress + (uint32)ImageBase);
   }
   else {
      // Write 16 bit address
      OutFile.PutHex((uint16)(IBegin + SectionAddress));
   }

   // Space after address
   OutFile.Put(" _");

   // Start of instruction
   i = IBegin;

   // Write bytes
   while (i < IEnd) {
      FieldSize = 1;                             // Size of field to write
      Spacer = " ";                              // Space between fields

      // Spacer and FieldSize depends on fields
      if (i == s.OpcodeStart1 && i > IBegin) {
         Spacer = ": ";                          // Space between prefixes and opcode
      }
      if (i == s.OpcodeStart2 + 1) {
         Spacer = ". ";                          // Space between opcode and mod/reg/rm bytes
      }
      if (i == s.AddressField && s.AddressFieldSize) {
         Spacer = ", ";                          // Space before address field
         FieldSize = s.AddressFieldSize;
      }
      if (i == s.ImmediateField && s.ImmediateFieldSize) {
         Spacer = ", ";                          // Space before immediate operand field
         FieldSize = s.ImmediateFieldSize;
      }
      // Write space
      OutFile.Put(Spacer);

      // Write byte or bytes
      switch (FieldSize) {
      case 1:  // Write single byte
         OutFile.PutHex(Get<uint8>(i));
         break;
      case 2:  // Write two bytes
         OutFile.PutHex(Get<uint16>(i));
         break;
      case 3:  // Write three bytes (operands for "enter" instruction)
         OutFile.PutHex(Get<uint16>(i));
         OutFile.Put(", ");
         OutFile.PutHex(Get<uint8>(i+2));
         break;
      case 4:  // Write four bytes
         if ((s.Operands[0] & 0xFE) == 0x84) {
            // Far jump/call address
            OutFile.PutHex(Get<uint16>(i));
            OutFile.Put(" ");
            OutFile.PutHex(Get<uint16>(i+2));
         }
         else {
            // Any other 32 bit operand
            OutFile.PutHex(Get<uint32>(i));
         }
         break;
      case 6:  // Write six bytes (far jump address)
         OutFile.PutHex(Get<uint32>(i));
         OutFile.Put(" ");
         OutFile.PutHex(Get<uint16>(i+4));
         break;
      case 8:  // Write eight bytes
         OutFile.PutHex(Get<uint64>(i));
         break;
      }
      // Search for relocation
      SARelocation rel1;                            // Make relocation records for searching
      rel1.Section = Section;
      rel1.Offset  = i;                             // rel1 marks current field in instruction

      // Is there a relocation source exactly here?
      int32 irel = Relocations.Exists(rel1);        // Finds relocation with source = i

      if (irel > 0) {
         // This field has a relocation. Indicate relocation type
         // 0 = unknown, 1 = direct, 2 = self-relative, 3 = image-relative, 
         // 4 = segment relative, 5 = relative to arbitrary ref. point, 8 = segment address/descriptor
         uint32 RelType = Relocations[irel].Type;
         if (RelType) {
            OutFile.Put(Lookup(RelocationTypeNames, RelType));
         }
         if (Relocations[irel].Size > FieldSize) {
            // Relocation has wrong size
            OutFile.Put(" Misplaced relocation.");
         }
      }

      // Point to next byte
      i += FieldSize;
   }
   // New line
   OutFile.NewLine();
}


void CDisassembler::CountInstructions() {
   // Count total number of instructions defined in opcodes.cpp
   // Two instructions are regarded as the same and counted as one if they 
   // have the same name and differ only in the bits that define register 
   // name, operand size, etc.

   uint32 map;                                   // Map number
   uint32 index;                                 // Index into map
   uint32 n;                                     // Number of instructions with same code
   uint32 iset;                                  // Instruction set
   uint32 instructions = 0;                      // Total number of instructions
   uint32 mmxinstr = 0;                          // Number of MMX instructions
   uint32 sseinstr = 0;                          // Number of SSE instructions
   uint32 sse2instr = 0;                         // Number of SSE2 instructions
   uint32 sse3instr = 0;                         // Number of SSE3 instructions
   uint32 ssse3instr = 0;                        // Number of SSSE3 instructions
   uint32 sse41instr = 0;                        // Number of SSE4.1 instructions
   uint32 sse42instr = 0;                        // Number of SSE4.2 instructions
   uint32 AVXinstr  = 0;                         // Number of AVX instructions
   uint32 FMAinstr  = 0;                         // Number of FMA3 and later instructions
   uint32 AVX2instr  = 0;                        // Number of AVX2 instructions
   uint32 AMDinstr = 0;                          // Number of AMD instructions
   uint32 VIAinstr = 0;                          // Number of AMD instructions
   uint32 privilinstr = 0;                       // Number of privileged instructions
   uint32 undocinstr = 0;                        // Number of undocumented instructions
   uint32 droppedinstr = 0;                      // Number of opcodes planned but never implemented
   uint32 VEXdouble = 0;                         // Number of instructions that have both VEX and non-VEX version
   SOpcodeDef const * opcode;                    // Pointer to map entry

   // Loop through all maps
   for (map = 0; map < NumOpcodeTables1; map++) {
      // Loop through each map
      for (index = 0; index < OpcodeTableLength[map]; index++) {
         opcode = OpcodeTables[map] + index;
         if (opcode->InstructionFormat && opcode->Name 
         && !opcode->TableLink && !(opcode->InstructionFormat & 0x8000)) {
            // instruction is defined
            if ((opcode->InstructionFormat & 0xFFF) == 3
            && index > 0 && (opcode-1)->Name 
            && strcmp(opcode->Name, (opcode-1)->Name) == 0) {
               // Same as previous instruction, just with another register
               continue;                         // Don't count this
            }
            n = 1;                               // Default = one instruction per map entry
            // Check if we have multiple instructions with different prefixes
            if (opcode->Options & 1) {
               if (opcode->AllowedPrefixes & 0x3000) {
                  n++;                           // Extra instruction with W prefix bit
               }
               else if (opcode->AllowedPrefixes & 0xE00) {               
                  if (opcode->AllowedPrefixes & 0x200) n++; // Extra instruction with 66 prefix
                  if (opcode->AllowedPrefixes & 0x400) n++; // Extra instruction with F3 prefix
                  if (opcode->AllowedPrefixes & 0x800) n++; // Extra instruction with F2 prefix
               }
               else if (opcode->AllowedPrefixes & 0x100) {
                  n++;                                      // Extra instruction with 66 prefix
                  if (opcode->AllowedPrefixes & 0x1000) n++;// Extra instruction with L prefix bit
               }
            }
            if (opcode->Options & 2) VEXdouble += n; // Instructions that have both VEX and non-VEX version
            instructions += n;                   // Count total instructions

            iset = opcode->InstructionSet;       // Instruction set
            if (iset & 0x20000) {
               droppedinstr += n; iset = 0;      // Opcodes planned but never implemented
            }
            if (iset & 0x800) privilinstr += n;  // Privileged instruction
            if (opcode->InstructionFormat & 0x4000) undocinstr += n; // Undocumented instruction
            
            switch (iset & 0x37FF) {
            case 7:  // MMX
               mmxinstr += n;  break;
            case 0x11:  // SSE
               sseinstr += n;  break;
            case 0x12:  // SSE2
               sse2instr += n;  break;
            case 0x13: // SSE3
               sse3instr += n;  break;
            case 0x14: // SSSE3
               ssse3instr += n;  break;
            case 0x15: // SSE4.1
               sse41instr += n;  break;
            case 0x16: // SSE4.2
               sse42instr += n;  break;
            case 0x17: case 0x18: case 0x19: // VEX etc.
               AVXinstr += n;  break;
            case 0x1A: case 0x1B:            // FMA and later instructions
               FMAinstr += n;  break;
            case 0x1C: case 0x1D: case 0x1E: // AVX2 and later instructions
               AVX2instr += n; break;
            case 0x1001: case 0x1002: case 0x1004: case 0x1005: case 0x1006:  // AMD
               AMDinstr += n;  break;
            case 0x2001: // VIA
               VIAinstr += n;  break;               
            }
         }
      }
   }

   // output result
   printf("\n\nNumber of instruction opcodes supported by disassembler:\n%5i Total, including:", 
      instructions);
   printf("\n%5i Privileged instructions", privilinstr);
   printf("\n%5i MMX  instructions", mmxinstr);
   printf("\n%5i SSE  instructions", sseinstr);
   printf("\n%5i SSE2 instructions", sse2instr);
   printf("\n%5i SSE3 instructions", sse3instr);
   printf("\n%5i SSSE3 instructions", ssse3instr);
   printf("\n%5i SSE4.1 instructions", sse41instr);
   printf("\n%5i SSE4.2 instructions", sse42instr);
   printf("\n%5i AVX instructions etc.", AVXinstr);
   printf("\n%5i AVX2 and later instructions", AVX2instr);
   printf("\n%5i FMA3 instructions", FMAinstr);
   printf("\n%5i AMD  instructions", AMDinstr);
   printf("\n%5i VIA  instructions", VIAinstr);   
   printf("\n%5i instructions planned but never implemented in any CPU", droppedinstr);
   printf("\n%5i undocumented or illegal instructions", undocinstr);
   printf("\n%5i instructions have both VEX and non-VEX versions", VEXdouble);
   printf("\n");   
}
