using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Cosmos.Assembler;
using Cosmos.Assembler.x86;
using Cosmos.Build.Common;
using Cosmos.Debug.DebugStub;
using XSharp.Common;
namespace Cosmos.IL2CPU
{
public class CosmosAssembler: Assembler.Assembler
{
public CosmosAssembler(int comPort)
{
mComPort = comPort;
}
protected int mComPort = 0;
///
/// Setting this field to false means the .xs files for the debug stub are read from the DebugStub assembly.
/// This allows the automated kernel tester to use the live ones, instead of the installed ones.
///
public static bool ReadDebugStubFromDisk = true;
public virtual void WriteDebugVideo(string aText)
{
// This method emits a lot of ASM, but thats what we want becuase
// at this point we need ASM as simple as possible and completely transparent.
// No stack changes, no register mods, etc.
// TODO: Add an option on the debug project properties to turn this off.
// Also see TokenPatterns.cs Checkpoint in X#
var xPreBootLogging = true;
if (xPreBootLogging)
{
new Comment("DebugVideo '" + aText + "'");
UInt32 xVideo = 0xB8000;
for (UInt32 i = xVideo; i < xVideo + 80 * 2; i = i + 2)
{
new LiteralAssemblerCode("mov byte [0x" + i.ToString("X") + "], 0");
new LiteralAssemblerCode("mov byte [0x" + (i + 1).ToString("X") + "], 0x02");
}
foreach (var xChar in aText)
{
new LiteralAssemblerCode("mov byte [0x" + xVideo.ToString("X") + "], " + (byte)xChar);
xVideo = xVideo + 2;
}
}
}
public void CreateGDT()
{
new Comment(this, "BEGIN - Create GDT");
var xGDT = new List();
// Null Segment - Selector 0x00
// Not used, but required by many emulators.
xGDT.AddRange(new byte[8]
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
});
// Code Segment
mGdCode = (byte)xGDT.Count;
xGDT.AddRange(GdtDescriptor(0x00000000, 0xFFFFFFFF, true));
// Data Segment - Selector
mGdData = (byte)xGDT.Count;
xGDT.AddRange(GdtDescriptor(0x00000000, 0xFFFFFFFF, false));
DataMembers.Add(new DataMember("_NATIVE_GDT_Contents", xGDT.ToArray()));
XS.Comment("Tell CPU about GDT");
var xGdtPtr = new UInt16[3];
// Size of GDT Table - 1
xGdtPtr[0] = (UInt16)(xGDT.Count - 1);
DataMembers.Add(new DataMember("_NATIVE_GDT_Pointer", xGdtPtr));
new Mov
{
DestinationRef = ElementReference.New("_NATIVE_GDT_Pointer"),
DestinationIsIndirect = true,
DestinationDisplacement = 2,
SourceRef = ElementReference.New("_NATIVE_GDT_Contents")
};
XS.Set(XSRegisters.EAX, "_NATIVE_GDT_Pointer");
XS.LoadGdt(XSRegisters.EAX, isIndirect: true);
XS.Comment("Set data segments");
XS.Set(XSRegisters.EAX, mGdData);
XS.Set(XSRegisters.DS, XSRegisters.AX);
XS.Set(XSRegisters.ES, XSRegisters.AX);
XS.Set(XSRegisters.FS, XSRegisters.AX);
XS.Set(XSRegisters.GS, XSRegisters.AX);
XS.Set(XSRegisters.SS, XSRegisters.AX);
XS.Comment("Force reload of code segment");
new JumpToSegment
{
Segment = mGdCode, DestinationLabel = "Boot_FlushCsGDT"
};
XS.Label("Boot_FlushCsGDT");
new Comment(this, "END - Create GDT");
}
protected void SetIdtDescriptor(int aNo, string aLabel, bool aDisableInts)
{
int xOffset = aNo * 8;
XS.Set(XSRegisters.EAX, aLabel);
var xIDT = ElementReference.New("_NATIVE_IDT_Contents");
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset, SourceReg = RegistersEnum.AL
};
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 1, SourceReg = RegistersEnum.AH
};
XS.ShiftRight(XSRegisters.EAX, 16);
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 6, SourceReg = RegistersEnum.AL
};
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 7, SourceReg = RegistersEnum.AH
};
// Code Segment
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 2, SourceValue = mGdCode, Size = 16
};
// Reserved
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 4, SourceValue = 0x00, Size = 8
};
// Type
new Mov
{
DestinationRef = xIDT, DestinationIsIndirect = true, DestinationDisplacement = xOffset + 5, SourceValue = (byte)(aDisableInts ? 0x8E : 0x8F), Size = 8
};
}
public void CreateIDT()
{
new Comment(this, "BEGIN - Create IDT");
// Create IDT
UInt16 xIdtSize = 8 * 256;
DataMembers.Add(new DataMember("_NATIVE_IDT_Contents", new byte[xIdtSize]));
//
if (mComPort > 0)
{
SetIdtDescriptor(1, "DebugStub_TracerEntry", false);
SetIdtDescriptor(3, "DebugStub_TracerEntry", false);
//for (int i = 0; i < 256; i++)
//{
// if (i == 1 || i == 3)
// {
// continue;
// }
// SetIdtDescriptor(i, "DebugStub_Interrupt_" + i.ToString(), true);
//}
}
//SetIdtDescriptor(1, "DebugStub_INT0"); - Change to GPF
// Set IDT
DataMembers.Add(new DataMember("_NATIVE_IDT_Pointer", new UInt16[]
{
xIdtSize, 0, 0
}));
new Mov
{
DestinationRef = ElementReference.New("_NATIVE_IDT_Pointer"),
DestinationIsIndirect = true,
DestinationDisplacement = 2,
SourceRef = ElementReference.New("_NATIVE_IDT_Contents")
};
XS.Set(XSRegisters.EAX, "_NATIVE_IDT_Pointer");
if (mComPort > 0)
{
XS.Set("static_field__Cosmos_Core_Common_CPU_mInterruptsEnabled", 1, destinationIsIndirect: true);
XS.LoadIdt(XSRegisters.EAX, isIndirect: true);
}
XS.Label("AfterCreateIDT");
new Comment(this, "END - Create IDT");
}
public void Initialize()
{
uint xSig = 0x1BADB002;
DataMembers.Add(new DataIfNotDefined("ELF_COMPILATION"));
DataMembers.Add(new DataMember("MultibootSignature", new uint[]
{
xSig
}));
uint xFlags = 0x10003;
DataMembers.Add(new DataMember("MultibootFlags", xFlags));
DataMembers.Add(new DataMember("MultibootChecksum", (int)(0 - (xFlags + xSig))));
DataMembers.Add(new DataMember("MultibootHeaderAddr", ElementReference.New("MultibootSignature")));
DataMembers.Add(new DataMember("MultibootLoadAddr", ElementReference.New("MultibootSignature")));
DataMembers.Add(new DataMember("MultibootLoadEndAddr", ElementReference.New("_end_code")));
DataMembers.Add(new DataMember("MultibootBSSEndAddr", ElementReference.New("_end_code")));
DataMembers.Add(new DataMember("MultibootEntryAddr", ElementReference.New("Kernel_Start")));
DataMembers.Add(new DataEndIfDefined());
DataMembers.Add(new DataIfDefined("ELF_COMPILATION"));
xFlags = 0x00003;
DataMembers.Add(new DataMember("MultibootSignature", new uint[]
{
xSig
}));
DataMembers.Add(new DataMember("MultibootFlags", xFlags));
DataMembers.Add(new DataMember("MultibootChecksum", (int)(0 - (xFlags + xSig))));
DataMembers.Add(new DataEndIfDefined());
// graphics info fields
DataMembers.Add(new DataMember("MultibootGraphicsRuntime_VbeModeInfoAddr", Int32.MaxValue));
DataMembers.Add(new DataMember("MultibootGraphicsRuntime_VbeControlInfoAddr", Int32.MaxValue));
DataMembers.Add(new DataMember("MultibootGraphicsRuntime_VbeMode", Int32.MaxValue));
// memory
DataMembers.Add(new DataMember("MultiBootInfo_Memory_High", 0));
DataMembers.Add(new DataMember("MultiBootInfo_Memory_Low", 0));
DataMembers.Add(new DataMember("Before_Kernel_Stack", new byte[0x50000]));
DataMembers.Add(new DataMember("Kernel_Stack", new byte[0]));
DataMembers.Add(new DataMember("MultiBootInfo_Structure", new uint[1]));
// constants
DataMembers.Add(new DataMember(@"__uint2double_const", new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x41 }));
DataMembers.Add(new DataMember(@"__ulong2double_const", 0x5F800000));
DataMembers.Add(new DataMember(@"__doublesignbit", 0x8000000000000000));
DataMembers.Add(new DataMember(@"__floatsignbit", 0x80000000));
if (mComPort > 0)
{
new Define("DEBUGSTUB");
}
// This is our first entry point. Multiboot uses this as Cosmos entry point.
new Label("Kernel_Start", isGlobal: true);
XS.Set(XSRegisters.ESP, "Kernel_Stack");
// Displays "Cosmos" in top left. Used to make sure Cosmos is booted in case of hang.
// ie bootloader debugging. This must be the FIRST code, even before setup so we know
// we are being called properly by the bootloader and that if there are problems its
// somwhere in our code, not the bootloader.
WriteDebugVideo("Cosmos pre boot");
// For when using Bochs, causes a break ASAP on entry after initial Cosmos display.
//new LiteralAssemblerCode("xchg bx, bx");
// CLI ASAP
WriteDebugVideo("Clearing interrupts.");
XS.ClearInterruptFlag();
WriteDebugVideo("Begin multiboot info.");
new LiteralAssemblerCode("%ifndef EXCLUDE_MULTIBOOT_MAGIC");
new Comment(this, "MultiBoot compliant loader provides info in registers: ");
new Comment(this, "EBX=multiboot_info ");
new Comment(this, "EAX=0x2BADB002 - check if it's really Multiboot-compliant loader ");
new Comment(this, " ;- copy mb info - some stuff for you ");
new Comment(this, "BEGIN - Multiboot Info");
new Mov
{
DestinationRef = ElementReference.New("MultiBootInfo_Structure"), DestinationIsIndirect = true, SourceReg = RegistersEnum.EBX
};
XS.Add(XSRegisters.EBX, 4);
XS.Set(XSRegisters.EAX, XSRegisters.EBX, sourceIsIndirect: true);
new Mov
{
DestinationRef = ElementReference.New("MultiBootInfo_Memory_Low"), DestinationIsIndirect = true, SourceReg = RegistersEnum.EAX
};
XS.Add(XSRegisters.EBX, 4);
XS.Set(XSRegisters.EAX, XSRegisters.EBX, sourceIsIndirect: true);
new Mov
{
DestinationRef = ElementReference.New("MultiBootInfo_Memory_High"), DestinationIsIndirect = true, SourceReg = RegistersEnum.EAX
};
new Comment(this, "END - Multiboot Info");
new LiteralAssemblerCode("%endif");
WriteDebugVideo("Creating GDT.");
CreateGDT();
WriteDebugVideo("Configuring PIC");
ConfigurePIC();
WriteDebugVideo("Creating IDT.");
CreateIDT();
#if LFB_1024_8
new Comment("Set graphics fields");
XS.Mov(XSRegisters.EBX, Cosmos.Assembler.ElementReference.New("MultiBootInfo_Structure"), sourceIsIndirect: true);
XS.Mov(XSRegisters.EAX, XSRegisters.EBX, sourceDisplacement: 72);
new Move { DestinationRef = Cosmos.Assembler.ElementReference.New("MultibootGraphicsRuntime_VbeControlInfoAddr"), DestinationIsIndirect = true, SourceReg = Registers.EAX };
XS.Mov(XSRegisters.EAX, XSRegisters.EBX, sourceDisplacement: 76);
new Move { DestinationRef = Cosmos.Assembler.ElementReference.New("MultibootGraphicsRuntime_VbeModeInfoAddr"), DestinationIsIndirect = true, SourceReg = Registers.EAX };
XS.Mov(XSRegisters.EAX, XSRegisters.EBX, sourceDisplacement: 80);
new Move { DestinationRef = Cosmos.Assembler.ElementReference.New("MultibootGraphicsRuntime_VbeMode"), DestinationIsIndirect = true, SourceReg = Registers.EAX };
#endif
//WriteDebugVideo("Initializing SSE.");
//new Comment(this, "BEGIN - SSE Init");
//// CR4[bit 9]=1, CR4[bit 10]=1, CR0[bit 2]=0, CR0[bit 1]=1
//XS.Mov(XSRegisters.EAX, XSRegisters.Registers.CR4);
//XS.Or(XSRegisters.EAX, 0x100);
//XS.Mov(XSRegisters.CR4, XSRegisters.Registers.EAX);
//XS.Mov(XSRegisters.EAX, XSRegisters.Registers.CR4);
//XS.Or(XSRegisters.EAX, 0x200);
//XS.Mov(XSRegisters.CR4, XSRegisters.Registers.EAX);
//XS.Mov(XSRegisters.EAX, XSRegisters.Registers.CR0);
//XS.And(XSRegisters.EAX, 0xfffffffd);
//XS.Mov(XSRegisters.CR0, XSRegisters.Registers.EAX);
//XS.Mov(XSRegisters.EAX, XSRegisters.Registers.CR0);
//XS.And(XSRegisters.EAX, 1);
//XS.Mov(XSRegisters.CR0, XSRegisters.Registers.EAX);
//new Comment(this, "END - SSE Init");
if (mComPort > 0)
{
WriteDebugVideo("Initializing DebugStub.");
XS.Call("DebugStub_Init");
}
// Jump to Kernel entry point
WriteDebugVideo("Jumping to kernel.");
XS.Call(EntryPointName);
new Comment(this, "Kernel done - loop till next IRQ");
XS.Label(".loop");
XS.ClearInterruptFlag();
XS.Halt();
XS.Jump(".loop");
if (mComPort > 0)
{
var xGen = new AsmGenerator();
var xGenerateAssembler =
new Action