Thanks to Thijs Kroesbergen for the pointer to this week's source. He turned me on to a tiny Operating System written in C# called "Cosmos (C# Open Source Managed Operating System)". As the project says, why? Because it's fun! I wrote a Tiny Virtual OS in C# for an Operating Systems class I took while I was going to school at night.
And so, Dear Reader, I present to you fifteenth in a infinite number of posts of "The Weekly Source Code." Here's some source I was reading this week.
First, I went back and looked at some of my source code from the Tiny OS project. It was written, largely at night, in .NET 1.1. It was for a 10 week (one term) project, and I wrote it all in a weekend so as to have the rest of the term free.
It's pretty horrific to read old source. I would propose that if you can look at old code you wrote and feel good about it that you're either insane, deranged or a really good programmer. One of those three.
Do remember that this project was a tiny Virtual OS. We were given an Instruction Set for our little processor and a format for programs and the goal was to write an OS to run them.
Here's the constructor for my MemoryManager. I'm sure doing a lot of work in the constructor! One of the fun little things I did for this Tiny OS was to swap memory pages as XML Files. It was a little joke because XML was such overkill and overhead for something so nuanced as a memory manager. I figured since I was simulating an OS's behavior using something as high-level as .NET, then why shouldn't I swap fake memory to disk as XML?
I wanted the source for this Tiny Virtual OS to ready like all the psuedo-code we'd been looking at in the Operating Systems books.
Looking back, I think it'd have been cool if I'd made a WinForms host and did a graphical view of memory that would allow folks to see things like memory fragmentation. Maybe I'll do one in WPF or as an XBAP, it'd be a cool learning tool for some of the students I talk to.
public MemoryManager(uint virtualMemSizeIn)
{
// Find a size for addressableMemory that is on a page boundary
virtualMemSize = CPU.UtilRoundToBoundary(virtualMemSizeIn, CPU.pageSize);
// Size of memory must be a factor of CPU.pageSize
// This was asserted when the CPU initialized memory
uint physicalpages = (uint)(CPU.physicalMemory.Length/CPU.pageSize);
uint addressablepages = (uint)(virtualMemSize/CPU.pageSize);
_pageTable = new ArrayList((int)addressablepages);
// Delete all our Swap Files
foreach (string f in Directory.GetFiles(".","*.xml"))
File.Delete(f);
// For all off addressable memory...
// Make the pages in physical and the pages that aren't in physical
for (uint i = 0;i < virtualMemSize; i+=CPU.pageSize)
{
// Mark the Pages that are in physical memory as "false" or "not free"
MemoryPage p;
if (i < CPU.physicalMemory.Length)
{
p = new MemoryPage(i, true);
freePhysicalPages[(int)(i/CPU.pageSize)] = false;
}
else p = new MemoryPage(i, false);
_pageTable.Add(p);
}
}
Now my OS a trinket, to be clear. Cosmos, on the other hands, is darned interesting. How could you create a REAL OS (meaning an actual bootable OS off of hardware, be it virtual or physical, using IL? You translate the IL to ASM, of course. Very cool and darned clever.
Cosmos includes a compiler (IL2CPU, which is part of Cosmos) that reads the input file (usually the shell) and Cosmos libraries and compiles the resulting IL to x86 code. IL2CPU has a layer for cross platform and we plan to support other processors and platforms, including x64. IL2CPU also supports certain extension methods which allow C# code to interact directly with the CPU, registers, and ports in the kernel. IL2CPU contains some inline assembler, but there are no ASM files that need to be linked in.
Currently IL2CPU first outputs raw asm files (with IL comments) and then processes them through nasm (a free assembler). Later we plan to emit directly to binary.
The scenarios that Cosmos could be used in are very interesting. Because it's easy to write to and easy to build, you could create little mini-OSes with just the features you want. You could make an OS that just does DNS, or just does some REST service. Who knows. (Yes, I know you could also do a stripped down Linux). There is also talk about getting Cosmos to work on the Wii.
The example below is from Indy.IL2CPU.Assembler.X86.Native. As you can see, IL2CPU writes out ASM.
protected override void EmitDataSectionHeader(string aGroup, StreamWriter aOutputWriter) {
base.EmitDataSectionHeader(aGroup, aOutputWriter);
if (aGroup == MainGroup) {
aOutputWriter.WriteLine("section .data");
aOutputWriter.WriteLine("_start: ");
aOutputWriter.WriteLine("; multiboot header ");
aOutputWriter.WriteLine("MBFLAGS equ 0x03 ; 4KB aligned modules etc., full memory info, ");
aOutputWriter.WriteLine(" ; use special header (see below) ");
aOutputWriter.WriteLine("dd 0x1BADB002 ; multiboot signature ");
aOutputWriter.WriteLine("dd MBFLAGS ; 4kb page aligment for modules, supply memory info ");
aOutputWriter.WriteLine("dd -0x1BADB002-MBFLAGS ; checksum=-(FLAGS+0x1BADB002) ");
aOutputWriter.WriteLine("; other data - that is the additional (optional) header which helps to load ");
aOutputWriter.WriteLine("; the kernel. ");
aOutputWriter.WriteLine("; end of header ");
aOutputWriter.WriteLine("MultiBootInfo_Memory_High dd 0");
aOutputWriter.WriteLine("MultiBootInfo_Memory_Low dd 0");
}
There's lots of methods like this that do the hard work. The orchestration, however, is up in Engine.cs, where assemblies are taken appear via reflection, and their methods are taken apart using the obvious (my psuedo-code):
foreach Type in Assembly
foreach Method in Type
ProcessMethod into ASM (via ProcessAllMethods)
Using an interesting ILReader, a useful class in and of itself. Here's a trimmed chunk from ProcessAllMethods that uses the ILReader.
At this point, we've loaded an assembly, got a type, and we're sitting on xCurrentMethod. They take the method, check for some exception handling (I've trimmed that, it's tedious) and they get an OpCode, and then using the current Assembler, they assemble that operation. Lather, rinse, repeat as necessary.
ILReader xReader = new ILReader(xCurrentMethod);
while (xReader.Read()) {
if (mInstructionsToSkip > 0) {
mInstructionsToSkip--;
continue;
}
ExceptionHandlingClause xCurrentHandler = null;
...snip...
xMethodInfo.CurrentHandler = xCurrentHandler;
xOp = GetOpFromType(mMap.GetOpForOpCode(xReader.OpCode), xReader, xMethodInfo);
if ((!xOp.SupportsMetalMode) && mAssembler.InMetalMode) {
throw new Exception("OpCode '" + xReader.OpCode + "' not supported in Metal mode!");
}
xOp.Assembler = mAssembler;
new Comment("StackItems = " +
mAssembler.StackContents.Count + ",
Top item = " +
(mAssembler.StackContents.Count > 0 ? mAssembler.StackContents.Peek().ToString() : "(empty)"));
xOp.Assemble();
}
There's not a lot of comments in the Cosmos Project, but once you get your head around what they are doing, it's a pretty amazing piece of work, and I can see why they are having so much fun.
From their site at http://www.gocosmos.org:
If you just want to play with Cosmos:
- Install the user kit.
- Join the discussion list
Other resources:
- Read the FAQ.
- Subscribe to the Cosmos Blog
- Documentation - Most developer documentation right now.
If you are interested in kernel development:
- Get source from CodePlex
- Read the full requirements. They are pretty basic though, and everything you need except for Windows (For development) is free.
- Read Getting Started
Enjoy!
Hosting By