                            Machine Code Opcodes
                                on the x86       
                                  -TI #1-        
                            Compiled by: Cyclone 


[Disclaimer]
     I am NOT responsible for whatever you decide to do with this
     information.  I am also not responsible for the correctness of
     the information provided, although I did my best to see to it
     that it is correct (the book I used had a ton of errors, so
     one or two might have survived.  If that's the case sorry).

[Introduction]

	This text assumes you already have a working knowledge of the workings
of the x86 assembly language.  I have, therefore, decided against typing up the
function of each of the instructions, as I didn't feel like doing that much
typing, and the x86 instruction set is fairly well documented anyway.  For full
use of this text, I recommend you have a list of the various instructions and
their function.  So without further adieu...


The structure of a typical assembly instruction is as follows:
----------      ----------      ----------      ------------    ------------
 Prefix          Opcode          Address        Displacement     Data
 1-byte          1/2-byte        1/2-byte        1/2/4-byte      1/2/4-byte
(optional)                      (optional)       (optional)      (optional)
----------      ----------      ----------      ------------    ------------



[Prefixes]

There are 5 types of prefixes:
	Operand Size
	Address Size
	Segment Override        - cs,ds,es,ss,fs,gs
	Bus LOCK                - lock
	Repeat                  - repz, repnz/rep

	More than one prefix can be uses for one instruction.  The order of the
prefixes does not matter as long as the prefixes are of different types.  If
the prefixes are of the same type than the last one takes effect.

For example:
	cs:
	es:
	mov     [si],5
is equivalent to:
	es:
	mov     [si],5

Note:
	use32           \
	repnz             = rep movsd (in real mode)
	movsw           /
is equivalent to:
	repnz           \
	use32             = rep movsd (in real mode)
	movsw           /

It is worth while to point out that the REP/REPNZ instruction can only precede:
LODS, STOS, MOVS, INS, OUTS, CMPS, SCAS.  The REPZ instruction can only
precede CMPS and SCAS.  Putting these repeat instructions with other
instructions will have no effect.

Also, The LOCK instruction can only precede certain instructions.  These are:
XCHG    mem,
ADD     mem,
ADC     mem,
SUB     mem,
SBB     mem,
NEG     mem
OR      mem,
AND     mem,
XOR     mem,
NOT     mem
BTC     mem
BTR     mem
BTS     mem
if a lock precedes an instruction other than one listed above the CPU will not
be a happy camper and issue an exception 6 (invalid opcode)

Other prefixes like the address, operand, and segment prefixes are much more
forgiving.  When they are paired with something silly (like NOP), they are
just ignored.

One final note about prefixes:
	There is an upper limit to the size of an instruction.  For example,
the 386's largest instruction is 15 bytes.  This can only be achieved ny excess
prefixing.  If the instruction becomes too large for the processor to handle,
the CPU screams (issues an exception 13).



[Opcodes]

	Opcodes tell the computer which instructions are to be executed.  These
can be one to two bytes long.  Originally, way back on the 8086, all the
opcode bytes were a single byte.  Well, eventually, this proved to be too
little for our friends at Intel, and the size was expanded to up to two bytes.
To do this one opcode was scrapped from the "POP segreg" family.  The
Instruction "POP CS" was removed and now its bit pattern basically means:
"more opcodes coming".

Pop Seg. Reg.   -       000xx111
Pop CS          -       00001111

Thus POP CS doesn't work on an 286 or higher.

The Opcode does contain some useful info.  Let's look at the SUB opcode.
The byte to subtract a register from another register is:
-----------------
|0|0|1|0|1|0|d|w|
-----------------
What the heck is a "w" and a "d" bit you might ask?  Fair enough.

A "w" bit decides whether the operand is a byte size, or bigger.  How big?
Well, that depends on the mode of the processor.  If the CPU is in protected
mode or 32-bit mode, then it's 32-bits.  If, on the other hand, the CPU is
running real or virtual mode, then it's 16-bit.

The "d" bit decides which operand comes first.  A value of '0' means the
"mod R/M" is the first operand while "reg" is operand 2 (for more info on what
the "mod R/M" and "reg" fields are, see the next section).  If the bit was '1',
then the "reg" field is operand 1, while "mod R/M" is operand 2.
For example:
  (001010dw = sub,  "mod R/M"= [bx],  "reg"= AX )
00101000 ...    - sub   [BX], AL
The opcode can also sometimes have a reg field, as in the case of "inc reg",
"dec reg", "xchg ax,reg", "push", "pop".

Also, sometimes the opcode byte alone, does not determine the entire
instruction.  This happens mainly in cases where an immediate value is involved.
In these cases, the rest of the operation is usually stored in the "reg" field.



[Addresses]

	It's time we talk about one of the ugliest things in the x86 language
- the Address byte(s).  It's not hard, but it is a wee bit messy.  Lets start
in Real mode.

	In real mode the Address byte - is just that (1 byte).  It contains a
"Mod" field, a "R/M" field, and a "Reg" field as follows:
	-----------------
	|mod| reg | R/M |
	-----------------

The "Mod" & "R/M" fields make up the first operand while the "reg" field makes
up the second.  The full table of values is in the accompanying text.  Notice
that the combination of the mod & R/M fields is 5-bits.  This allows many more
options for the first operand than the second.  If mod = 0 then the thing
specified by R/M is a pointer (ex.  mod=00, R/M=100 => [SI]).  When mod = 1,
an 8-bit displacement is added (ex. mod=01, R/M=100 => [SI+d8]).  When mod is
2, a full 16-bit displacement is added (ex mod=10, R/M=100 => [SI+d16]).
When mod = 3, the second field is a register - not a memory location.  I think
it's time for an example.
  (001010dw = sub,  reg: AX=000 CX=001 DX= 010 BX=011)
00101001 00000000       - sub   [BX+SI], AX     ; mod=00 R/M=000 => [BX+SI]
00101001 01001100       - sub   [SI+d8], CX     ; mod=01 R/M=100 => [SI+d8]
00101011 00000100       - sub   AX, [SI]        ; mod=00 R/M=100 => [SI]
00101011 11001010       - sub   CX, DX          ; mod=11 R/M=010 => DX

Hopefully that's clear enough, so I can mess with your mind some more in
protected mode.  If you don't care about Protected Mode, feel free to skip to
the next section.

Protected mode isn't so easy - what else is new?  IN Protected mode can have
upto 2 Address bytes of the following structure:
	-----------------   [ ----------------- ]
	|mod| reg | R/M |   [ |ss |index| base| ]
	-----------------   [ ----------------- ]
The second byte only appears if the R/M field is 100.

If the R/M field is not 100, then the structure is almost the same as in real
mode with one exception.  The values mean different things.  For example:
mod=00 RM=000
in real mode            = [BX+SI]
in protected mode       = [EAX]

When the R/M field does equal 000 then the second byte is added.  It contains:
	ss      - A scale factor of the index (times 1,2,4,8)
	index   - index (like in an array)
	base    - holds the info the R/M couldn't
For example:
	mov     [EAX+EBX*8],EDX         ; (mod=00)
		  |   |  |   |--- reg field
		  |   |  |------- ss (scale factor)
		  |   |---------- index
		  |-------------- base


[Displacements]

	In the last section you might have noticed I mention displacements.
They were labelled as d8 and d16 in my examples.  There's noting hard about
displacements.  Displacements are a constant number added to a memory location.
Example:
	mov     [SI],ax         ; just [si] - no displacement
	mov     [SI+2],ax       ; 2 - usually a byte (8-bit) displacement
	mov     [SI+1000],ax    ; 1000 - 16-bit displacement.
Displacements are signed numbers.  That means they do represent negative
numbers (FFh = -1, FEh= -2, etc).  This allows things like:
	mov     [SI-2],ax
The size (if any) of the displacement field is determined by the Address byte.
And that's really all there is to know about that.


[Data]

	Some Opcodes require immediate data.  This is where that data is put
(at the end of the instruction).  Examples:
	mov     ax,1234h        ; data = 1234h
	int     21h             ; data = 21h
	shl     ax,4            ; data = 4
Some opcodes require more than one piece of data (like direct far jumps and
calls).  These are well labeled in the other text though.


[So Example time (again)]

So let's say you want to create:        sub     ax, es:[BX+6]
You look up SUB and you find:

-SUB-------Integer Subtract
		-----------------  -----------------
Register/Memory |0|0|1|0|1|0|d|w|  |mod| reg | R/M |
		-----------------  -----------------
Immediate to    -----------------  -----------------
Register/Memory |1|0|0|0|0|0|s|w|  |mod|1|0|1| R/M |  -Data-
		-----------------  -----------------
Immediate to    -----------------
AL/AX/EAX Reg.  |0|0|1|0|1|1|0|w|  -Data-
		-----------------

Well, we have no immediate data so we're concerned only with the first one.
-----------------  -----------------
|0|0|1|0|1|0|d|w|  |mod| reg | R/M |
-----------------  -----------------

The first thing the instruction needs is a prefix.  The prefix is es:.  So
we look up ES: and find:
-----------------
|0|0|1|0|0|1|1|0|
-----------------

This is the first byte of the instruction.  (26h)

The instruction "sub ax,es:[bx+6]" is word sized (we're dealing with ax).
Thus the "w" bit is set to 1.
The more "complex" instruction which can be represented only by the mod R/M
part of the address is operand 2, thus the "d" bit is set.  Thus the opcode
looks like:
00101011b       = 2Bh

Now we need reg= 000 => AX,
and mod=01 & reg=110 => [BX+d8]
Thus slapping these values into the address byte we get:
	01 000 110      = 46h

The displacement is 6 and it's a byte => 06h
And we have no immediate data.

Thus sub ax,es:[bx+6] =
26h,    2Bh,    46h,    06h
prefix  opcode  addr.   disp.


[Final Example]

	cs:0100h	jb	1009h	(numbers are memory locations)

No prefixes to worry about.
The instruction we need has Full 16-displacement.  It follows:
		-----------------  -----------------
Full Displ. -   |0|0|0|0|1|1|1|1|  |1|0|0|0| cond. |  Full displacement
		-----------------  -----------------
the condition we need (b = below) = 0010.
Thus our opcode is:     0Fh, 82h

Displacements are calculated by, finding out the ending address of the
current instruction and subtracting it from the destination instruction.
Thus we have:
1009h [dest] - (0100h (start of instruction) + 4 (length of instruction))
= 1005h
Thus our displacement is: 1005h
However intel stores it's numbers backward, so in memory 1005h looks like:
05h, 01h

Our instruction thus is:
	0Fh, 82h, 05h, 01h

Please note:  only numbers are flipped.  Opcode are not.


Well, I hope that this was at least a little informative and that you enjoyed
this doc at least as much as I hate typing :)
Let me know what you think and good luck with your endeavours, whatever they
may be.

Cyclone

