Started implementing the 8086 simulation

This commit is contained in:
2023-03-29 01:28:37 +01:00
parent 732aa2803f
commit 96d1381303
20 changed files with 832 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
#ifndef ALIASES_H
#define ALIASES_H
#include <stdint.h>
#ifndef u8
#define u8 uint8_t
#endif // !u8
#ifndef u16
#define u16 uint16_t
#endif // !u16
#ifndef u32
#define u32 uint32_t
#endif // !u32
#ifndef u64
#define u64 uint64_t
#endif // !u64
#ifndef i8
#define i8 int8_t
#endif // !i8
#ifndef i16
#define i16 int16_t
#endif // !i16
#ifndef i32
#define i32 int32_t
#endif // !i32
#ifndef i64
#define i64 int64_t
#endif // !i64
#ifndef f32
#define f32 float
#endif // !f32
#ifndef f64
#define f64 double
#endif // !f64
#endif // !ALIASES_H

View File

@@ -0,0 +1,11 @@
#ifndef REG_ACCESS_H
#define REG_ACCESS_H
#include "aliases.h"
#include "sim86_instruction.h"
void set_register(register_access reg, u16 new_value);
u16 get_register(register_access reg);
const char *get_register_name(register_access reg);
#endif // !REG_ACCESS_H

54
8086_sim/include/sim86.h Normal file
View File

@@ -0,0 +1,54 @@
/* ========================================================================
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Please see https://computerenhance.com for more information
======================================================================== */
#ifndef SIM86_H
#define SIM86_H
#ifndef u8
typedef char unsigned u8;
#endif // !u8
#ifndef u16
typedef short unsigned u16;
#endif // !u16
#ifndef u32
typedef int unsigned u32;
#endif // !u32
#ifndef u64
typedef long long unsigned u64;
#endif // u64
#ifndef s8
typedef char s8;
#endif // !s8
#ifndef s16
typedef short s16;
#endif // !s16
#ifndef s32
typedef int s32;
#endif // !s32
#ifndef s64
typedef long long s64;
#endif // !s64
typedef s32 b32;
#define ArrayCount(Array) (sizeof(Array) / sizeof((Array)[0]))
static u32 const SIM86_VERSION = 3;
#endif // !SIM86_H

View File

@@ -0,0 +1,92 @@
/* ========================================================================
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Please see https://computerenhance.com for more information
======================================================================== */
#ifndef SIM86_INST_H
#define SIM86_INST_H
#include "sim86.h"
enum operation_type : u32 {
Op_None,
#define INST(Mnemonic, ...) Op_##Mnemonic,
#define INSTALT(...)
#include "sim86_instruction_table.inl"
Op_Count,
};
enum instruction_flag : u32 {
Inst_Lock = 0x1,
Inst_Rep = 0x2,
Inst_Segment = 0x4,
Inst_Wide = 0x8,
Inst_Far = 0x10,
};
struct register_access {
u32 Index; // Index in the register table
u32 Offset; // High vs Low bits
u32 Count; // How many bytes are accessed
};
struct effective_address_term {
register_access Register;
s32 Scale;
};
enum effective_address_flag : u32 {
Address_ExplicitSegment = 0x1,
};
struct effective_address_expression {
effective_address_term Terms[2];
u32 ExplicitSegment;
s32 Displacement;
u32 Flags;
};
enum immediate_flag : u32 {
Immediate_RelativeJumpDisplacement = 0x1,
};
struct immediate {
s32 Value;
u32 Flags;
};
enum operand_type : u32 {
Operand_None,
Operand_Register,
Operand_Memory,
Operand_Immediate,
};
struct instruction_operand {
operand_type Type;
union {
effective_address_expression Address;
register_access Register;
immediate Immediate;
};
};
struct instruction {
u32 Address;
u32 Size;
operation_type Op;
u32 Flags;
instruction_operand Operands[2];
u32 SegmentOverride;
};
#endif // !SIM86_INST_H

View File

@@ -0,0 +1,65 @@
/* ========================================================================
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Please see https://computerenhance.com for more information
======================================================================== */
#include "sim86.h"
#include "sim86_instruction.h"
enum instruction_bits_usage : u8 {
Bits_End, // NOTE(casey): The 0 value, indicating the end of the instruction
// encoding array
Bits_Literal, // NOTE(casey): These are opcode bits that identify instructions
// NOTE(casey): These bits correspond directly to the 8086 instruction manual
Bits_D,
Bits_S,
Bits_W,
Bits_V,
Bits_Z,
Bits_MOD,
Bits_REG,
Bits_RM,
Bits_SR,
Bits_Disp,
Bits_Data,
Bits_DispAlwaysW, // NOTE(casey): Tag for instructions where the displacement
// is always 16 bits
Bits_WMakesDataW, // NOTE(casey): Tag for instructions where SW=01 makes the
// data field become 16 bits
Bits_RMRegAlwaysW, // NOTE(casey): Tag for instructions where the register
// encoded in RM is always 16-bit width
Bits_RelJMPDisp, // NOTE(casey): Tag for instructions that require address
// adjustment to go through NASM properly
Bits_Far, // NOTE(casey): Tag for instructions that require a "far" keyword in
// their ASM to select the right opcode
Bits_Count,
};
struct instruction_bits {
instruction_bits_usage Usage;
u8 BitCount;
u8 Shift;
u8 Value;
};
struct instruction_encoding {
operation_type Op;
instruction_bits Bits[16];
};
struct instruction_table {
instruction_encoding *Encodings;
u32 EncodingCount;
u32 MaxInstructionByteCount;
};

View File

@@ -0,0 +1,250 @@
/* ========================================================================
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Please see https://computerenhance.com for more information
======================================================================== */
/*
NOTE(casey): This instruction table is a direct translation of table 4-12 in the Intel 8086 manual.
The macros are designed to allow direct transcription, without changing the order or manner
of specification in the table in any way. Additional "implicit" versions of the macros are provided
so that hard-coded fields can be supplied uniformly.
The table is also designed to allow you to include it multiple times to "pull out" other things
from the table, such as opcode mnemonics as strings or enums, etc.
*/
#ifndef INST
#define INST(Mnemonic, Encoding, ...) {Op_##Mnemonic, Encoding, __VA_ARGS__},
#endif
#ifndef INSTALT
#define INSTALT INST
#endif
#define B(Bits) {Bits_Literal, sizeof(#Bits)-1, 0, 0b##Bits}
#define D {Bits_D, 1}
#define S {Bits_S, 1}
#define W {Bits_W, 1}
#define V {Bits_V, 1}
#define Z {Bits_Z, 1}
#define XXX {Bits_Data, 3, 0}
#define YYY {Bits_Data, 3, 3}
#define RM {Bits_RM, 3}
#define MOD {Bits_MOD, 2}
#define REG {Bits_REG, 3}
#define SR {Bits_SR, 2}
#define ImpW(Value) {Bits_W, 0, 0, Value}
#define ImpREG(Value) {Bits_REG, 0, 0, Value}
#define ImpMOD(Value) {Bits_MOD, 0, 0, Value}
#define ImpRM(Value) {Bits_RM, 0, 0, Value}
#define ImpD(Value) {Bits_D, 0, 0, Value}
#define ImpS(Value) {Bits_S, 0, 0, Value}
#define DISP {Bits_Disp, 0, 0, 0}
#define ADDR {Bits_Disp, 0, 0, 0}, {Bits_DispAlwaysW, 0, 0, 1}
#define DATA {Bits_Data, 0, 0, 0}
#define DATA_IF_W {Bits_WMakesDataW, 0, 0, 1}
#define Flags(F) {F, 0, 0, 1}
INST(mov, {B(100010), D, W, MOD, REG, RM})
INSTALT(mov, {B(1100011), W, MOD, B(000), RM, DATA, DATA_IF_W, ImpD(0)})
INSTALT(mov, {B(1011), W, REG, DATA, DATA_IF_W, ImpD(1)})
INSTALT(mov, {B(1010000), W, ADDR, ImpREG(0), ImpMOD(0), ImpRM(0b110), ImpD(1)})
INSTALT(mov, {B(1010001), W, ADDR, ImpREG(0), ImpMOD(0), ImpRM(0b110), ImpD(0)})
INSTALT(mov, {B(100011), D, B(0), MOD, B(0), SR, RM, ImpW(1)}) // NOTE(casey): This collapses 2 entries in the 8086 table by adding an explicit D bit
INST(push, {B(11111111), MOD, B(110), RM, ImpW(1)})
INSTALT(push, {B(01010), REG, ImpW(1)})
INSTALT(push, {B(000), SR, B(110), ImpW(1)})
INST(pop, {B(10001111), MOD, B(000), RM, ImpW(1)})
INSTALT(pop, {B(01011), REG, ImpW(1)})
INSTALT(pop, {B(000), SR, B(111), ImpW(1)})
INST(xchg, {B(1000011), W, MOD, REG, RM, ImpD(1)})
INSTALT(xchg, {B(10010), REG, ImpMOD(0b11), ImpW(1), ImpRM(0)})
INST(in, {B(1110010), W, DATA, ImpREG(0), ImpD(1)})
INSTALT(in, {B(1110110), W, ImpREG(0), ImpD(1), ImpMOD(0b11), ImpRM(2), Flags(Bits_RMRegAlwaysW)})
INST(out, {B(1110011), W, DATA, ImpREG(0), ImpD(0)})
INSTALT(out, {B(1110111), W, ImpREG(0), ImpD(0), ImpMOD(0b11), ImpRM(2), Flags(Bits_RMRegAlwaysW)})
INST(xlat, {B(11010111)})
INST(lea, {B(10001101), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(lds, {B(11000101), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(les, {B(11000100), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(lahf, {B(10011111)})
INST(sahf, {B(10011110)})
INST(pushf, {B(10011100)})
INST(popf, {B(10011101)})
INST(add, {B(000000), D, W, MOD, REG, RM})
INSTALT(add, {B(100000), S, W, MOD, B(000), RM, DATA, DATA_IF_W})
INSTALT(add, {B(0000010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(adc, {B(000100), D, W, MOD, REG, RM})
INSTALT(adc, {B(100000), S, W, MOD, B(010), RM, DATA, DATA_IF_W})
INSTALT(adc, {B(0001010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(inc, {B(1111111), W, MOD, B(000), RM})
INSTALT(inc, {B(01000), REG, ImpW(1)})
INST(aaa, {B(00110111)})
INST(daa, {B(00100111)})
INST(sub, {B(001010), D, W, MOD, REG, RM})
INSTALT(sub, {B(100000), S, W, MOD, B(101), RM, DATA, DATA_IF_W})
INSTALT(sub, {B(0010110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(sbb, {B(000110), D, W, MOD, REG, RM})
INSTALT(sbb, {B(100000), S, W, MOD, B(011), RM, DATA, DATA_IF_W})
INSTALT(sbb, {B(0001110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(dec, {B(1111111), W, MOD, B(001), RM})
INSTALT(dec, {B(01001), REG, ImpW(1)})
INST(neg, {B(1111011), W, MOD, B(011), RM})
INST(cmp, {B(001110), D, W, MOD, REG, RM})
INSTALT(cmp, {B(100000), S, W, MOD, B(111), RM, DATA, DATA_IF_W})
INSTALT(cmp, {B(0011110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)}) // NOTE(casey): The manual table suggests this data is only 8-bit, but wouldn't it be 16 as well?
INST(aas, {B(00111111)})
INST(das, {B(00101111)})
INST(mul, {B(1111011), W, MOD, B(100), RM, ImpS(0)})
INST(imul, {B(1111011), W, MOD, B(101), RM, ImpS(1)})
INST(aam, {B(11010100), B(00001010)}) // NOTE(casey): The manual says this has a DISP... but how could it? What for??
INST(div, {B(1111011), W, MOD, B(110), RM, ImpS(0)})
INST(idiv, {B(1111011), W, MOD, B(111), RM, ImpS(1)})
INST(aad, {B(11010101), B(00001010)})
INST(cbw, {B(10011000)})
INST(cwd, {B(10011001)})
INST(not, {B(1111011), W, MOD, B(010), RM})
INST(shl, {B(110100), V, W, MOD, B(100), RM})
INST(shr, {B(110100), V, W, MOD, B(101), RM})
INST(sar, {B(110100), V, W, MOD, B(111), RM})
INST(rol, {B(110100), V, W, MOD, B(000), RM})
INST(ror, {B(110100), V, W, MOD, B(001), RM})
INST(rcl, {B(110100), V, W, MOD, B(010), RM})
INST(rcr, {B(110100), V, W, MOD, B(011), RM})
INST(and, {B(001000), D, W, MOD, REG, RM})
INSTALT(and, {B(1000000), W, MOD, B(100), RM, DATA, DATA_IF_W})
INSTALT(and, {B(0010010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(test, {B(1000010), W, MOD, REG, RM}) // NOTE(casey): The manual suggests there is a D flag here, but it doesn't appear to be true (it would conflict with xchg if it did)
INSTALT(test, {B(1111011), W, MOD, B(000), RM, DATA, DATA_IF_W})
INSTALT(test, {B(1010100), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)}) // NOTE(casey): The manual table suggests this data is only 8-bit, but it seems like it could be 16 too?
INST(or, {B(000010), D, W, MOD, REG, RM})
INSTALT(or, {B(1000000), W, MOD, B(001), RM, DATA, DATA_IF_W})
INSTALT(or, {B(0000110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(xor, {B(001100), D, W, MOD, REG, RM})
INSTALT(xor, {B(1000000), W, MOD, B(110), RM, DATA, DATA_IF_W}) // NOTE(casey): The manual has conflicting information about this encoding, but I believe this is the correct binary pattern.
INSTALT(xor, {B(0011010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})
INST(rep, {B(1111001), Z})
INST(movs, {B(1010010), W})
INST(cmps, {B(1010011), W})
INST(scas, {B(1010111), W})
INST(lods, {B(1010110), W})
INST(stos, {B(1010101), W})
INST(call, {B(11101000), ADDR, Flags(Bits_RelJMPDisp)})
INSTALT(call, {B(11111111), MOD, B(010), RM, ImpW(1)})
INSTALT(call, {B(10011010), ADDR, DATA, DATA_IF_W, ImpW(1)})
INSTALT(call, {B(11111111), MOD, B(011), RM, ImpW(1), Flags(Bits_Far)})
INST(jmp, {B(11101001), ADDR, Flags(Bits_RelJMPDisp)})
INSTALT(jmp, {B(11101011), DISP, Flags(Bits_RelJMPDisp)})
INSTALT(jmp, {B(11111111), MOD, B(100), RM, ImpW(1)})
INSTALT(jmp, {B(11101010), ADDR, DATA, DATA_IF_W, ImpW(1)})
INSTALT(jmp, {B(11111111), MOD, B(101), RM, ImpW(1), Flags(Bits_Far)})
// NOTE(casey): The actual Intel manual does not distinguish mnemonics RET and RETF,
// but NASM needs this to reassemble properly, so we do.
INST(ret, {B(11000011)})
INSTALT(ret, {B(11000010), DATA, DATA_IF_W, ImpW(1)})
INST(retf, {B(11001011)})
INSTALT(retf, {B(11001010), DATA, DATA_IF_W, ImpW(1)})
INST(je, {B(01110100), DISP, Flags(Bits_RelJMPDisp)})
INST(jl, {B(01111100), DISP, Flags(Bits_RelJMPDisp)})
INST(jle, {B(01111110), DISP, Flags(Bits_RelJMPDisp)})
INST(jb, {B(01110010), DISP, Flags(Bits_RelJMPDisp)})
INST(jbe, {B(01110110), DISP, Flags(Bits_RelJMPDisp)})
INST(jp, {B(01111010), DISP, Flags(Bits_RelJMPDisp)})
INST(jo, {B(01110000), DISP, Flags(Bits_RelJMPDisp)})
INST(js, {B(01111000), DISP, Flags(Bits_RelJMPDisp)})
INST(jne, {B(01110101), DISP, Flags(Bits_RelJMPDisp)})
INST(jnl, {B(01111101), DISP, Flags(Bits_RelJMPDisp)})
INST(jg, {B(01111111), DISP, Flags(Bits_RelJMPDisp)})
INST(jnb, {B(01110011), DISP, Flags(Bits_RelJMPDisp)})
INST(ja, {B(01110111), DISP, Flags(Bits_RelJMPDisp)})
INST(jnp, {B(01111011), DISP, Flags(Bits_RelJMPDisp)})
INST(jno, {B(01110001), DISP, Flags(Bits_RelJMPDisp)})
INST(jns, {B(01111001), DISP, Flags(Bits_RelJMPDisp)})
INST(loop, {B(11100010), DISP, Flags(Bits_RelJMPDisp)})
INST(loopz, {B(11100001), DISP, Flags(Bits_RelJMPDisp)})
INST(loopnz, {B(11100000), DISP, Flags(Bits_RelJMPDisp)})
INST(jcxz, {B(11100011), DISP, Flags(Bits_RelJMPDisp)})
INST(int, {B(11001101), DATA})
INST(int3, {B(11001100)}) // TODO(casey): The manual does not suggest that this intrinsic has an "int3" mnemonic, but NASM thinks so
INST(into, {B(11001110)})
INST(iret, {B(11001111)})
INST(clc, {B(11111000)})
INST(cmc, {B(11110101)})
INST(stc, {B(11111001)})
INST(cld, {B(11111100)})
INST(std, {B(11111101)})
INST(cli, {B(11111010)})
INST(sti, {B(11111011)})
INST(hlt, {B(11110100)})
INST(wait, {B(10011011)})
INST(esc, {B(11011), XXX, MOD, YYY, RM})
INST(lock, {B(11110000)})
INST(segment, {B(001), SR, B(110)})
#undef INST
#undef INSTALT
#undef B
#undef D
#undef S
#undef W
#undef V
#undef Z
#undef XXX
#undef YYY
#undef RM
#undef MOD
#undef REG
#undef SR
#undef ImpW
#undef ImpREG
#undef ImpMOD
#undef ImpRM
#undef ImpD
#undef ImpS
#undef DISP
#undef ADDR
#undef DATA
#undef DATA_IF_W
#undef Flags

View File

@@ -0,0 +1,21 @@
/* ========================================================================
(C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Please see https://computerenhance.com for more information
======================================================================== */
#include "sim86.h"
#include "sim86_instruction.h"
#include "sim86_instruction_table.h"
extern "C" u32 Sim86_GetVersion(void);
extern "C" void Sim86_Decode8086Instruction(u32 SourceSize, u8 *Source, instruction *Dest);
extern "C" char const *Sim86_RegisterNameFromOperand(register_access *RegAccess);
extern "C" char const *Sim86_MnemonicFromOperationType(operation_type Type);
extern "C" void Sim86_Get8086InstructionTable(instruction_table *Dest);