summaryrefslogtreecommitdiff
path: root/src/sim86/sim86.cpp
diff options
context:
space:
mode:
authorRaymaekers Luca <luca@spacehb.net>2025-11-12 18:52:38 +0100
committerRaymaekers Luca <luca@spacehb.net>2025-11-12 18:52:38 +0100
commitd4f6774c172ac1e7c193fc4e89230c873d179c2b (patch)
tree049b855ac7b68482dc9e1e35b339f5b4d18d675b /src/sim86/sim86.cpp
parente20d69ffb1f5676bb7960ac4d71c1013e4582149 (diff)
checkpoint
Diffstat (limited to 'src/sim86/sim86.cpp')
-rw-r--r--src/sim86/sim86.cpp535
1 files changed, 535 insertions, 0 deletions
diff --git a/src/sim86/sim86.cpp b/src/sim86/sim86.cpp
new file mode 100644
index 0000000..7c444bd
--- /dev/null
+++ b/src/sim86/sim86.cpp
@@ -0,0 +1,535 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "./libs/reference_decoder/sim86_lib.cpp"
+
+#include "sim86.h"
+#include "clocks_table.inl"
+#include "generated/generated.cpp"
+
+global_variable u8 GlobalMemory[1*1024*1024] = {};
+
+internal void
+SetOrUnsetFlag(u32 *FlagsRegister, b32 Condition, flags_8086 Flag)
+{
+ if(Condition)
+ {
+ *FlagsRegister |= Flag;
+ }
+ else
+ {
+ *FlagsRegister &= (~Flag);
+ }
+}
+
+internal u32
+FlagsToString(char *Buffer, u32 Flags)
+{
+ u32 Length = 0;
+
+ for(s32 MappingIndex = 0;
+ MappingIndex < flags_8086_strings_count;
+ MappingIndex++)
+ {
+ u8 Char = flags_8086_strings[MappingIndex][0];
+ b32 IsBitSet = (Flags & 1);
+ Flags >>= 1;
+ if(IsBitSet)
+ {
+ Buffer[Length++] = Char;
+ }
+ }
+
+ return Length;
+}
+
+// Sets flags by checking the destination value of the register.
+internal void
+FlagsFromValue(u32 *FlagsRegister, u32 InstructionFlags, s32 Value)
+{
+ u32 OldFlagsRegister = *FlagsRegister;
+
+ u32 SignMask = (1 << ((InstructionFlags & Inst_Wide) ? 15 : 7));
+ SetOrUnsetFlag(FlagsRegister, (Value & SignMask), Flag_Sign);
+ SetOrUnsetFlag(FlagsRegister, (Value == 0), Flag_Zero);
+
+ // NOTE(luca): Parity flag is set when in lower 8 bits have an even number of set bits.
+ u32 OneBitsCount = 0;
+ for(u32 BitsIndex = 0;
+ BitsIndex < 8;
+ BitsIndex++)
+ {
+ OneBitsCount += (Value & 1);
+ Value >>= 1;
+ }
+ SetOrUnsetFlag(FlagsRegister, (!(OneBitsCount & 1)), Flag_Parity);
+
+ // TODO(luca): Overflow flag
+ // Were we adding positive numbers but produced a negative number?
+
+ // TODO(luca): Carry flag
+ // When twot numbers are added and the 17th bit would be set?
+
+ // TODO(luca): Auxiliary carry flag
+ // Carry for bottom 8 bits
+ if(*FlagsRegister != OldFlagsRegister)
+ {
+ char OldFlagsString[ArrayCount(flags_8086_strings)] = {};
+ char FlagsString[ArrayCount(flags_8086_strings)] = {};
+ FlagsToString(OldFlagsString, OldFlagsRegister);
+ FlagsToString(FlagsString, *FlagsRegister);
+
+ printf(" flags:%s->%s", OldFlagsString, FlagsString);
+ }
+}
+
+s32 GetCompleteDisplacement(s32 *Registers, instruction_operand *Operand)
+{
+ s32 CompleteDisplacement = Operand->Address.Displacement;
+
+ u32 Count = Operand->Address.Terms[0].Register.Count;
+ u32 Mask = ((u32)((-1)) >> (16 + (16 - Count*8)));
+ CompleteDisplacement +=
+ (Registers[Operand->Address.Terms[0].Register.Index] & Mask) +
+ (Registers[Operand->Address.Terms[1].Register.Index] & Mask);
+
+ return CompleteDisplacement;
+}
+
+internal s32 *
+OperandToValue(s32 *Registers, u8 *Memory, instruction_operand *Operand)
+{
+ s32 *Result = 0;
+
+ if(0) {}
+ else if(Operand->Type == Operand_Register)
+ {
+ Result = Registers + Operand->Register.Index;
+ }
+ else if(Operand->Type == Operand_Memory)
+ {
+ s32 CompleteDisplacement = GetCompleteDisplacement(Registers, Operand);
+ Result = (s32 *)((u8 *)Memory + CompleteDisplacement);
+ }
+ else if(Operand->Type == Operand_Immediate)
+ {
+ Result = &Operand->Immediate.Value;
+ }
+ else if(Operand->Type != Operand_None)
+ {
+ Assert(0);
+ }
+
+ return Result;
+}
+
+b32 IsAccumulator(instruction_operand *Operand)
+{
+ b32 Result = ((Operand->Type == Operand_Register) &&
+ (Operand->Register.Index == Register_a));
+ return Result;
+}
+
+b32 IsMatchingOp(instruction_operand *Operand, instruction_clocks_operand_type Type)
+{
+ b32 Matching = false;
+
+ Matching = Matching || (Type == InstructionClocksOperand_Memory && Operand->Type == Operand_Memory);
+ Matching = Matching || (Type == InstructionClocksOperand_Register && Operand->Type == Operand_Register);
+ Matching = Matching || (Type == InstructionClocksOperand_Accumulator && IsAccumulator(Operand));
+ Matching = Matching || (Type == InstructionClocksOperand_Immediate && Operand->Type == Operand_Immediate);
+
+ return Matching;
+}
+
+internal void
+Run8086(psize MemorySize, u8 *Memory)
+{
+ s32 Registers[Register_count] = {};
+ u32 FlagsRegister = 0;
+ u32 IPRegister = 0;
+ u32 ElapsedClocks = 0;
+
+ while(IPRegister < MemorySize)
+ {
+ instruction Decoded;
+ Sim86_Decode8086Instruction(MemorySize - IPRegister, Memory + IPRegister, &Decoded);
+ if(Decoded.Op)
+ {
+ u32 OldIPRegister = IPRegister;
+
+#if SIM86_INTERNAL
+ printf("%s ;", Sim86_MnemonicFromOperationType(Decoded.Op));
+#endif
+
+ instruction_operand *DestinationOperand = Decoded.Operands + 0;
+ instruction_operand *SourceOperand = Decoded.Operands + 1;
+
+ u32 AddedClocks = 0;
+ for(u32 ClocksIndex = 0;
+ ClocksIndex < ArrayCount(ClocksTable);
+ ClocksIndex++)
+ {
+ instruction_clocks *Clocks = ClocksTable + ClocksIndex;
+
+ b32 Matching = Decoded.Op == Clocks->Op;
+ Matching = Matching && IsMatchingOp(DestinationOperand, Clocks->Operands[0]);
+ Matching = Matching && IsMatchingOp(SourceOperand, Clocks->Operands[1]);
+ if(Matching)
+ {
+ AddedClocks += Clocks->Clocks;
+
+ if(Clocks->EffectiveAddress)
+ {
+ instruction_operand *MemoryOperand = ((DestinationOperand->Type == Operand_Memory) ? DestinationOperand : SourceOperand);
+ u32 FirstIndex = MemoryOperand->Address.Terms[0].Register.Index;
+ u32 SecondIndex = MemoryOperand->Address.Terms[1].Register.Index;
+
+ // Only displacement
+ if(FirstIndex == 0 && SecondIndex == 0)
+ {
+ AddedClocks += 6;
+ }
+ // Base or index
+ else if(FirstIndex == 0 || SecondIndex == 0)
+ {
+ // Base or index only
+ if(MemoryOperand->Address.Displacement == 0)
+ {
+ AddedClocks += 5;
+ }
+ // Base or index + displacement
+ else
+ {
+ AddedClocks += 9;
+ }
+ }
+ // Base + index
+ else if(MemoryOperand->Address.Displacement == 0)
+ {
+ /*
+ bp+di bx+si 7
+ bp+si bx+di 8
+ */
+ if((FirstIndex == Register_bp && SecondIndex == Register_di) ||
+ (FirstIndex == Register_b && SecondIndex == Register_si))
+ {
+ AddedClocks += 7;
+ }
+ else
+ {
+ AddedClocks += 8;
+ }
+ }
+ // Base + index + displacement
+ else
+ {
+ /*
+ bp+di bx+si 11
+ bp+si bx+di 12
+ */
+ if((FirstIndex == Register_bp && SecondIndex == Register_di) ||
+ (FirstIndex == Register_b && SecondIndex == Register_si))
+ {
+ AddedClocks += 11;
+ }
+ else
+ {
+ AddedClocks += 12;
+ }
+ }
+
+ }
+
+ // Add transfer penalty
+ if(Clocks->Transfers && (Decoded.Flags & Inst_Wide))
+ {
+ instruction_operand *MemoryOperand = ((DestinationOperand->Type == Operand_Memory) ? DestinationOperand : SourceOperand);
+ s32 Displacement = GetCompleteDisplacement(Registers, MemoryOperand);
+ if(Displacement & 1)
+ {
+ AddedClocks += 4*Clocks->Transfers;
+ }
+ }
+
+ break;
+ }
+ }
+
+#if 0
+ Assert(AddedClocks);
+#endif
+
+ ElapsedClocks += AddedClocks;
+ printf(" clocks: +%d = %d", AddedClocks, ElapsedClocks);
+
+ s32 *Destination = OperandToValue(Registers, Memory, DestinationOperand);
+ s32 *Source = OperandToValue(Registers, Memory, SourceOperand);
+
+ if(0) {}
+ else if(Decoded.Op == Op_int3)
+ {
+ Assert(0);
+ }
+ else if(Decoded.Op == Op_mov)
+ {
+ s32 Old = *Destination;
+ if(Decoded.Flags & Inst_Wide)
+ {
+ *(u16 *)Destination = *(u16 *)Source;
+ }
+ else
+ {
+ Assert((SourceOperand->Type == Operand_Immediate ||
+ SourceOperand->Type == Operand_Memory) &&
+ (SourceOperand->Register.Offset == 0));
+ Assert((DestinationOperand->Type == Operand_Immediate || DestinationOperand->Type == Operand_Memory) && (DestinationOperand->Register.Offset == 0));
+
+ u8 *SourceByte = (u8 *)Source + SourceOperand->Register.Offset;
+ u8 *DestByte = (u8 *)Destination + DestinationOperand->Register.Offset;
+
+ *DestByte = *SourceByte;
+ }
+
+#if SIM86_INTERNAL
+ if(DestinationOperand->Type == Operand_Register)
+ {
+ printf(" %s:0x%x->0x%x", Sim86_RegisterNameFromOperand(&DestinationOperand->Register),
+ Old, *Destination);
+ }
+#endif
+ }
+ else if(Decoded.Op == Op_ret)
+ {
+ printf("\n");
+ printf("STOPONRET: Return encountered at address %d.\n", IPRegister);
+
+ break;
+ }
+ else if(Decoded.Op == Op_inc)
+ {
+ Assert(DestinationOperand->Type == Operand_Register);
+ Assert(SourceOperand->Type == Operand_None);
+ *Destination += 1;
+ }
+ else if(Decoded.Op == Op_test)
+ {
+
+ Assert(DestinationOperand->Type == Operand_Register);
+ Assert(SourceOperand->Type == Operand_Register || SourceOperand->Type == Operand_Immediate);
+
+ s32 Value =((Decoded.Flags & Inst_Wide) ?
+ (u16)((u16)*Destination & ((u16)*Source)) :
+ (u8)((u8)*Destination & ((u8)*Source)));
+ FlagsFromValue(&FlagsRegister, Decoded.Flags, Value);
+ }
+ else if(Decoded.Op == Op_xor)
+ {
+
+ Assert(DestinationOperand->Type == Operand_Register);
+ Assert(SourceOperand->Type == Operand_Register || SourceOperand->Type == Operand_Immediate);
+
+ s32 Value =((Decoded.Flags & Inst_Wide) ?
+ (u16)((u16)*Destination ^ ((u16)*Source)) :
+ (u8)((u8)*Destination ^ ((u8)*Source)));
+ FlagsFromValue(&FlagsRegister, Decoded.Flags, Value);
+ *Destination = Value;
+ }
+ else if(Decoded.Op == Op_cmp)
+ {
+ Assert(DestinationOperand->Type == Operand_Register);
+ Assert(SourceOperand->Type == Operand_Register || SourceOperand->Type == Operand_Immediate);
+
+ s32 Value = ((Decoded.Flags & Inst_Wide) ?
+ (u16)((u16)*Destination - ((u16)*Source)) :
+ (u8)((u8)*Destination - ((u8)*Source)));
+
+ FlagsFromValue(&FlagsRegister, Decoded.Flags, Value);
+ }
+ else if(Decoded.Op == Op_sub)
+ {
+ Assert(DestinationOperand->Type == Operand_Register);
+ Assert(SourceOperand->Type == Operand_Register || SourceOperand->Type == Operand_Immediate);
+
+ s32 Old = *Destination;
+ *Destination = ((Decoded.Flags & Inst_Wide) ?
+ (u16)((u16)*Destination - ((u16)*Source)) :
+ (u8)((u8)*Destination - ((u8)*Source)));
+
+ printf(" %s:0x%x->0x%x",
+ Sim86_RegisterNameFromOperand(&DestinationOperand->Register),
+ Old, *Destination);
+
+ FlagsFromValue(&FlagsRegister, Decoded.Flags, *Destination);
+
+ }
+ else if(Decoded.Op == Op_add)
+ {
+ s32 Old = *Destination;
+
+ *Destination = ((Decoded.Flags & Inst_Wide) ?
+ (u16)((u16)*Destination + ((u16)*Source)) :
+ (u8)((u8)*Destination + ((u8)*Source)));
+
+ printf(" %s:0x%x->0x%x",
+ Sim86_RegisterNameFromOperand(&DestinationOperand->Register),
+ Old, *Destination);
+
+ FlagsFromValue(&FlagsRegister, Decoded.Flags, *Destination);
+ }
+ else if(Decoded.Op == Op_jne)
+ {
+ if(!(FlagsRegister & Flag_Zero))
+ {
+ IPRegister += *Destination;
+ }
+ }
+ else if(Decoded.Op == Op_je)
+ {
+ if((FlagsRegister & Flag_Zero))
+ {
+ IPRegister += *Destination;
+ }
+ }
+ else
+ {
+ Assert(0 && "Op not implemented yet.");
+ }
+
+ IPRegister += Decoded.Size;
+
+#if SIM86_INTERNAL
+ printf(" ip:0x%x->0x%x", OldIPRegister, IPRegister);
+#endif
+
+ }
+ else
+ {
+ printf("Unrecognized instruction\n");
+ break;
+ }
+
+#if SIM86_INTERNAL
+ printf("\n");
+#endif
+
+ }
+
+ printf("\nFinal registers:\n");
+ for(u32 RegisterIndex = Register_a;
+ RegisterIndex < Register_ds + 1;
+ RegisterIndex++)
+ {
+ register_access Register = {};
+ Register.Index = RegisterIndex;
+ Register.Offset = 0;
+ Register.Count = 2;
+
+ u32 Value = Registers[RegisterIndex];
+ if(Value > 0)
+ {
+ printf(" %s: 0x%0x (%d)\n",
+ Sim86_RegisterNameFromOperand(&Register),
+ Value, Value);
+ }
+ }
+ printf(" ip: 0x%04x (%d)\n", IPRegister, IPRegister);
+
+ if(FlagsRegister)
+ {
+ char FlagsString[ArrayCount(flags_8086_strings)] = {};
+ FlagsToString(FlagsString, FlagsRegister);
+ printf(" flags: %s\n", FlagsString);
+ }
+}
+
+void PrintUsage(char *ExePath)
+{
+ printf("usage: %s [-exec] <assembly>\n", ExePath);
+}
+
+int main(int ArgsCount, char *Args[])
+{
+ u32 Version = Sim86_GetVersion();
+
+#if SIM86_INTERNAL
+ printf("Sim86 Version: %u (expected %u)\n", Version, SIM86_VERSION);
+#endif
+
+ if(Version != SIM86_VERSION)
+ {
+ printf("ERROR: Header file version doesn't match DLL.\n");
+ return -1;
+ }
+
+ instruction_table Table;
+ Sim86_Get8086InstructionTable(&Table);
+
+#if SIM86_INTERNAL
+ printf("8086 Instruction Instruction Encoding Count: %u\n", Table.EncodingCount);
+#endif
+
+ // print (default)
+ // -exec (also execute the disassembled instructinos)
+
+ b32 Execute = false;
+ b32 Dump = false;
+ char *FileName = 0;
+ for(s32 ArgsIndex = 1;
+ ArgsIndex < ArgsCount;
+ ArgsIndex++)
+ {
+ char *Command = Args[ArgsIndex];
+ if(0) {}
+ else if(!strcmp(Command, "-exec"))
+ {
+ Execute = true;
+ }
+ else if(!strcmp(Command, "-dump"))
+ {
+ Dump = true;
+ }
+ else
+ {
+ FileName = Command;
+ }
+ }
+
+ if(FileName)
+ {
+ FILE *File = fopen(FileName, "rb");
+ if(File)
+ {
+ psize BytesWritten = fread(GlobalMemory, 1, sizeof(GlobalMemory), File);
+ fclose(File);
+
+ if(Execute)
+ {
+ printf("--- %s execution ---\n", FileName);
+
+ Run8086(BytesWritten, GlobalMemory);
+
+ if(Dump)
+ {
+ // NOTE(luca): We have to add ".data" or Gimp will throw an error.
+ FILE *DumpFile = fopen("sim86_memory_0.data", "wb");
+ fwrite(GlobalMemory, 1, sizeof(GlobalMemory), DumpFile);
+ fclose(DumpFile);
+ }
+
+ }
+ else
+ {
+ printf("ERROR: Disassembling not implemented yet.\n");
+ }
+
+ }
+ else
+ {
+ printf("ERROR: Unable to open %s.\n", FileName);
+ PrintUsage(Args[0]);
+ }
+ }
+
+ return 0;
+}