A machine emulator that visualizes how each instruction is processed
See the code
A machine emulator that visualizes how each instruction is processed
MAD SCIENCE
Table of Contents generated with DocToc
Used for byte sized operations on registers or a register pair.
In case a register pair is used the names of both registers are provided. In case only one register is used, the same code is used except we only use the first register of the pair.
Used for any operation that operates on a register pair mov, add, etc.
Same code used no matter of the pair size dword, word. For byte size general puropose regs we use @see _byteRegPair instead.
Operations for smaller pairs just have a different opcode than dword operations prefixing the pair.
Certain operations like add/sub only use first reg of the pair, addressing it via the pair code. In that case the operation may also be encoded in the reg pair code, i.e.
add ecx, ... ; 83 c1 ... uses c1 to indicate ecx
sub ecx, ... ; 83 e9 ... uses e9 to indicate ecx
cmp ecx, ... ; 83 f9 ... uses f9 to indicate ecx
Index of each flag in the eflags register.
Flags representation for each case of ONE flag set at a time. Used to isolate each flag for flag operations
Flag's Meanings
see: wiki flags register
Determnies if a carry or borrow has been generated out of the least significant four bits when adding src to dst wiki
| Name | Type | Description |
|---|---|---|
dst | Number | destination register |
src | Number | source register |
true if a half-carry occurs when adding src to dst, otherwise false
Decrement a register
| Name | Type | Description |
|---|---|---|
opcode | ||
asm | ||
srcbytes | ||
dstbytes |
Inccrement a register
| Name | Type | Description |
|---|---|---|
opcode | ||
asm | ||
srcbytes | ||
dstbytes |
Moves one register into another. In order to execute this instruction we read the next code byte. It tells us which register pairs are affected (i.e. which register to move into which).
We look these up via a table.
| Name | Type | Description |
|---|---|---|
opcode | Number | |
asm | String | |
srcbytes | Number | the size of the (sub)register to move |
Fetches, decodes and executes next instruction and stores result.
This implementation totally ignores a few things about modern processors and instead uses a much simpler algorithm to fetch and execute instructions and store the results.
Here are some concepts that make modern processors faster, but are not employed here, followed by the simplified algorightm we actually use here.
Pipelining
Caches
Branch Prediction
Translation to RISC like micro-instructions
Simplified Algorithm
5) goto 1
and 2. basically become one step since we just call a function named after the opcode of the mnemonic.
We then fetch more bytes from the code in order to complete the instruction from memory (something that is inefficient and not done in the real world, where multiple instructions are pre-fetched instead).
The decoder is authored using this information.
Push 32 bit register onto stack. x50
50 push eax
51 push ecx
53 push ebx
52 push edx
54 push esp
55 push ebp
56 push esi
57 push edi
| Name | Type | Description |
|---|---|---|
opcode |
Converts given number to a two digit hex str
| Name | Type | Description |
|---|---|---|
x | Number | number between 0x00 and 0xff |
two digit string representation
Antidote to leVal. Converts a value into a buffer of n bytes ordered little endian.
| Name | Type | Argument | Description |
|---|---|---|---|
val | Number | value 8, 16 or 32 bits | |
nbytes | Number |
<optional> | number of bytes of the value to include (default: 4) |
byte representation of the given @see val
Calculates value of little endian ordered bytes.
leVal([ 0x00, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 0)
leVal([ 0x01, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 1)
leVal([ 0xff, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 255)
leVal([ 0x00, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 00 ( 256)
leVal([ 0x01, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 01 ( 257)
leVal([ 0xff, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 ff ( 511)
leVal([ 0xff, 0xff, 0x00, 0x00 ]) // => 0x00 00 ff ff ( 65,535)
leVal([ 0x00, 0x00, 0xff, 0x00 ]) // => 0x00 ff 00 00 ( 16,711,680)
leVal([ 0xff, 0xff, 0xff, 0x00 ]) // => 0x00 ff ff ff ( 16,777,215 )
leVal([ 0x00, 0x00, 0x00, 0x0f ]) // => 0x0f 00 00 00 ( 251,658,240 )
leVal([ 0x00, 0x00, 0x00, 0xf0 ]) // => 0xf0 00 00 00 (4,026,531,840)
leVal([ 0x00, 0x00, 0x00, 0xff ]) // => 0xff 00 00 00 (4,278,190,080)
leVal([ 0xff, 0xff, 0xff, 0xff ]) // => 0xff ff ff ff (4,294,967,295)
| Name | Type | Argument | Description |
|---|---|---|---|
bytes | Array.<Number> | bytes that contain number representation | |
nbytes | Number |
<optional> | number of bytes, if not given it is deduced |
number contained in bytes
Calculates if an overflow occurred due to the last arithmetic operation.
The overflow flag is set when the most significant bit (sign bit) is changed by adding two numbers with the same sign or subtracting two numbers with opposite signs.
A negative result out of positive operands (or vice versa) is an overflow.
| Name | Type | Description |
|---|---|---|
op1 | Number | first operand of the arithmetic operation |
op2 | Number | second operand of the arithmetic operation |
res | Number | result of the arithmetic operation |
nbytes | Number | byte sizes of the operands and the result |
true if an overflow occurred, otherwise false
Calculates parity of a given number and returns value to set parity flag to.
Mostly used to check for serial data communications correctness checking:
parity bit, or check bit is a bit added to the end of a string of binary code that indicates whether the number of bits in the string with the value one is even or odd. Parity bits are used as the simplest form of error detecting code. To determine odd parity if the sum of bits with a value of 1 is odd, the parity bit's value is set to zero.
Summary
- parity flag is set to 0 if the number of set bits is odd
- parity flag is set to 1 if the number of set bits is even
This method takes around 9 operations, and works for 32-bit words. It first shifts and XORs the eight nibbles of the 32-bit value together, leaving the result in the lowest nibble of v. Next, the binary number 0110 1001 1001 0110 (0x6996 in hex) is shifted to the right by the value represented in the lowest nibble of v. This number is like a miniature 16-bit parity-table indexed by the low four bits in v. The result has the parity of v in bit 1, which is masked and returned.
x86 parity only applies to the low 8 bits x86 caveat
| Name | Type | Description |
|---|---|---|
v | Number | 32-bit number to get parity for |
0 if odd, otherwise 1
Registers are stored as a 4 byte array in order to allow accessing sub registers like ax, ah and al easily.
The byte order is little endian to be consistent with how things are stored in memory and thus be able to use the same store/load functions we use for the latter.
As an example eax is stored as follows:
this._eax = [
0x0 // al
, 0x0 // ah
, 0x0 // lower byte of upper word
, 0x0 // upper byte of upper word
]
Each register part can be accessed via a property, i.e. regs.ah, regs.ax.
| Name | Type | Description |
|---|---|---|
k | String | the name of the register |
Assigns given registers with the supplied values. Leaves all other flags alone.
| Name | Type | Description |
|---|---|---|
regs |
Clears a given flag
First we invert the mask for the flag to clear.
Then we and the flags with that mask which clears
our flag since that's the only bit in the mask that's 0.
| Name | Type | Description |
|---|---|---|
flag |
Returns a given flag
First masks out the bit of the flag we are interested in and then shifts our flag bit into lowest bit.
| Name | Type | Description |
|---|---|---|
flag |
1 if flag is set, otherwise 0
Sets a given flag
ors flags with mask that will preserve all other flags and set
our flag since that bit is set in the mask.
| Name | Type | Description |
|---|---|---|
flag |
Determines if a number is signed, i.e. the most significant bit is set
| Name | Type | Description |
|---|---|---|
v | Number | to check for signedness |
nbytes | Number | size of the value in bytes |
true if number is signed, otherwise false
generated with docme
GPL3
109 commits
JavaScript
87.1%
CSS
6.4%
Handlebars
3.1%
HTML
2.5%
A machine emulator that visualizes how each instruction is processed
See the code
A machine emulator that visualizes how each instruction is processed
MAD SCIENCE
Table of Contents generated with DocToc
Used for byte sized operations on registers or a register pair.
In case a register pair is used the names of both registers are provided. In case only one register is used, the same code is used except we only use the first register of the pair.
Used for any operation that operates on a register pair mov, add, etc.
Same code used no matter of the pair size dword, word. For byte size general puropose regs we use @see _byteRegPair instead.
Operations for smaller pairs just have a different opcode than dword operations prefixing the pair.
Certain operations like add/sub only use first reg of the pair, addressing it via the pair code. In that case the operation may also be encoded in the reg pair code, i.e.
add ecx, ... ; 83 c1 ... uses c1 to indicate ecx
sub ecx, ... ; 83 e9 ... uses e9 to indicate ecx
cmp ecx, ... ; 83 f9 ... uses f9 to indicate ecx
Index of each flag in the eflags register.
Flags representation for each case of ONE flag set at a time. Used to isolate each flag for flag operations
Flag's Meanings
see: wiki flags register
Determnies if a carry or borrow has been generated out of the least significant four bits when adding src to dst wiki
| Name | Type | Description |
|---|---|---|
dst | Number | destination register |
src | Number | source register |
true if a half-carry occurs when adding src to dst, otherwise false
Decrement a register
| Name | Type | Description |
|---|---|---|
opcode | ||
asm | ||
srcbytes | ||
dstbytes |
Inccrement a register
| Name | Type | Description |
|---|---|---|
opcode | ||
asm | ||
srcbytes | ||
dstbytes |
Moves one register into another. In order to execute this instruction we read the next code byte. It tells us which register pairs are affected (i.e. which register to move into which).
We look these up via a table.
| Name | Type | Description |
|---|---|---|
opcode | Number | |
asm | String | |
srcbytes | Number | the size of the (sub)register to move |
Fetches, decodes and executes next instruction and stores result.
This implementation totally ignores a few things about modern processors and instead uses a much simpler algorithm to fetch and execute instructions and store the results.
Here are some concepts that make modern processors faster, but are not employed here, followed by the simplified algorightm we actually use here.
Pipelining
Caches
Branch Prediction
Translation to RISC like micro-instructions
Simplified Algorithm
5) goto 1
and 2. basically become one step since we just call a function named after the opcode of the mnemonic.
We then fetch more bytes from the code in order to complete the instruction from memory (something that is inefficient and not done in the real world, where multiple instructions are pre-fetched instead).
The decoder is authored using this information.
Push 32 bit register onto stack. x50
50 push eax
51 push ecx
53 push ebx
52 push edx
54 push esp
55 push ebp
56 push esi
57 push edi
| Name | Type | Description |
|---|---|---|
opcode |
Converts given number to a two digit hex str
| Name | Type | Description |
|---|---|---|
x | Number | number between 0x00 and 0xff |
two digit string representation
Antidote to leVal. Converts a value into a buffer of n bytes ordered little endian.
| Name | Type | Argument | Description |
|---|---|---|---|
val | Number | value 8, 16 or 32 bits | |
nbytes | Number |
<optional> | number of bytes of the value to include (default: 4) |
byte representation of the given @see val
Calculates value of little endian ordered bytes.
leVal([ 0x00, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 0)
leVal([ 0x01, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 1)
leVal([ 0xff, 0x00, 0x00, 0x00 ]) // => 0x00 00 00 ff ( 255)
leVal([ 0x00, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 00 ( 256)
leVal([ 0x01, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 01 ( 257)
leVal([ 0xff, 0x01, 0x00, 0x00 ]) // => 0x00 00 01 ff ( 511)
leVal([ 0xff, 0xff, 0x00, 0x00 ]) // => 0x00 00 ff ff ( 65,535)
leVal([ 0x00, 0x00, 0xff, 0x00 ]) // => 0x00 ff 00 00 ( 16,711,680)
leVal([ 0xff, 0xff, 0xff, 0x00 ]) // => 0x00 ff ff ff ( 16,777,215 )
leVal([ 0x00, 0x00, 0x00, 0x0f ]) // => 0x0f 00 00 00 ( 251,658,240 )
leVal([ 0x00, 0x00, 0x00, 0xf0 ]) // => 0xf0 00 00 00 (4,026,531,840)
leVal([ 0x00, 0x00, 0x00, 0xff ]) // => 0xff 00 00 00 (4,278,190,080)
leVal([ 0xff, 0xff, 0xff, 0xff ]) // => 0xff ff ff ff (4,294,967,295)
| Name | Type | Argument | Description |
|---|---|---|---|
bytes | Array.<Number> | bytes that contain number representation | |
nbytes | Number |
<optional> | number of bytes, if not given it is deduced |
number contained in bytes
Calculates if an overflow occurred due to the last arithmetic operation.
The overflow flag is set when the most significant bit (sign bit) is changed by adding two numbers with the same sign or subtracting two numbers with opposite signs.
A negative result out of positive operands (or vice versa) is an overflow.
| Name | Type | Description |
|---|---|---|
op1 | Number | first operand of the arithmetic operation |
op2 | Number | second operand of the arithmetic operation |
res | Number | result of the arithmetic operation |
nbytes | Number | byte sizes of the operands and the result |
true if an overflow occurred, otherwise false
Calculates parity of a given number and returns value to set parity flag to.
Mostly used to check for serial data communications correctness checking:
parity bit, or check bit is a bit added to the end of a string of binary code that indicates whether the number of bits in the string with the value one is even or odd. Parity bits are used as the simplest form of error detecting code. To determine odd parity if the sum of bits with a value of 1 is odd, the parity bit's value is set to zero.
Summary
- parity flag is set to 0 if the number of set bits is odd
- parity flag is set to 1 if the number of set bits is even
This method takes around 9 operations, and works for 32-bit words. It first shifts and XORs the eight nibbles of the 32-bit value together, leaving the result in the lowest nibble of v. Next, the binary number 0110 1001 1001 0110 (0x6996 in hex) is shifted to the right by the value represented in the lowest nibble of v. This number is like a miniature 16-bit parity-table indexed by the low four bits in v. The result has the parity of v in bit 1, which is masked and returned.
x86 parity only applies to the low 8 bits x86 caveat
| Name | Type | Description |
|---|---|---|
v | Number | 32-bit number to get parity for |
0 if odd, otherwise 1
Registers are stored as a 4 byte array in order to allow accessing sub registers like ax, ah and al easily.
The byte order is little endian to be consistent with how things are stored in memory and thus be able to use the same store/load functions we use for the latter.
As an example eax is stored as follows:
this._eax = [
0x0 // al
, 0x0 // ah
, 0x0 // lower byte of upper word
, 0x0 // upper byte of upper word
]
Each register part can be accessed via a property, i.e. regs.ah, regs.ax.
| Name | Type | Description |
|---|---|---|
k | String | the name of the register |
Assigns given registers with the supplied values. Leaves all other flags alone.
| Name | Type | Description |
|---|---|---|
regs |
Clears a given flag
First we invert the mask for the flag to clear.
Then we and the flags with that mask which clears
our flag since that's the only bit in the mask that's 0.
| Name | Type | Description |
|---|---|---|
flag |
Returns a given flag
First masks out the bit of the flag we are interested in and then shifts our flag bit into lowest bit.
| Name | Type | Description |
|---|---|---|
flag |
1 if flag is set, otherwise 0
Sets a given flag
ors flags with mask that will preserve all other flags and set
our flag since that bit is set in the mask.
| Name | Type | Description |
|---|---|---|
flag |
Determines if a number is signed, i.e. the most significant bit is set
| Name | Type | Description |
|---|---|---|
v | Number | to check for signedness |
nbytes | Number | size of the value in bytes |
true if number is signed, otherwise false
generated with docme
GPL3
109 commits
JavaScript
87.1%
CSS
6.4%
Handlebars
3.1%
HTML
2.5%