Click here to Skip to main content
15,893,668 members
Articles / General Programming / Algorithms

A Simulator for Knuth's MIX Computer

Rate me:
Please Sign up or sign in to vote.
4.91/5 (26 votes)
2 Feb 2011CPOL7 min read 55.9K   668   34  
Assembler and Simulator for Don Knuth's MIX Computer from The Art of Computer Programming.
using System;
using System.IO;
using MIXLib;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections.Generic;
using System.Linq;

namespace MIX
{
    public class MIXController
    {
        MIXMachine machine;
        Dictionary<string, MIXWord> symbolTable;

        public MIXController()
        {
            machine = new MIXMachine();
            verbose = false;
            symbolTable = new Dictionary<string, MIXWord>();
        }

        public void Interface()
        {
            string input = "";

            while (true)
            {
                // Get next command
                Console.Write("> ");
                input = Console.ReadLine();
                var commands = input.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                if (commands.Length > 0)
                {
                    switch (commands[0].Trim().ToLower())
                    {
                        case "?":
                        case "help":
                            Help(commands.Skip(1).ToArray());
                            break;
                        case "reboot":
                            Console.WriteLine("Rebooting...");
                            machine = new MIXMachine();
                            symbolTable = new Dictionary<string, MIXWord>();
                            Console.WriteLine("Done.");
                            break;
                        case "quit":
                        case "exit":
                            Console.WriteLine("Bye.");
                            Environment.Exit(0);
                            break;
                        case "load":
                            int w = LoadImage(commands[1]);
                            Console.WriteLine(string.Format("Loaded {0} word(s). PC set to {1}", w, machine.PC));
                            break;
                        case "loaddeck":
                            LoadDeck(commands[1]);
                            break;
                        case "verbose":
                            verbose = !verbose;
                            break;
                        case "show":
                            Show(commands.Skip(1).ToArray());
                            break;
                        case "set":
                            Set(commands.Skip(1).ToArray());
                            break;
                        case "clear":
                            Clear(commands.Skip(1).ToArray());
                            break;
                        case "step":
                            Console.WriteLine(string.Format("PC: {1} - Executing: {0}", GetDisassembly(machine.Memory[machine.PC]), machine.PC));
                            machine.Step();
                            if (verbose)
                                ShowAllState();
                            Console.WriteLine("Done.");
                            break;
                        case "go":
                        case "run":
                            Console.WriteLine("Executing...");
                            machine.Run();
                            if (verbose)
                                ShowAllState();
                            Console.WriteLine("Done.");
                            break;
                        case "redir":
                        case "redirect":
                            Redirect(commands.Skip(1).ToArray());
                            break;
                        default:
                            Console.Error.WriteLine("Unknown command.");
                            break;
                    }
                }
            }
        }

        private void Help(string[] what)
        {
            if (what.Length == 0)
            {
                Console.WriteLine("Commands are:\n");

                Console.WriteLine("CLEAR (BP|BREAKPOINT) [n|ALL]\n\tClear breakpoint n, or all breakpoints.");
                Console.WriteLine("EXIT|QUIT\n\tExit the console.");
                Console.WriteLine("GO|RUN\n\tStart execution.");
                Console.WriteLine("LOAD {filename}\n\tLoad a memory dump from file.");
                Console.WriteLine("REBOOT\n\tRestart MIX.");
                Console.WriteLine("REDIR[ECT] {UnitId} {filename|console}\n\tRedirect I/O device UnitId to the console or to a disk file.");
                Console.WriteLine("SET {args}\n\tSet various parameters. Type ? SET for more info.");
                Console.WriteLine("SHOW {args}\n\tShow the current values of various parameters. Type ? SHOW for more info.");
                Console.WriteLine("STEP\n\tExecute only the current instruction.");
                Console.WriteLine("VERBOSE\n\tToggle verbosity on or off.");
            }
            else
            {
                switch (what[0])
                {
                    case "show":
                        Console.WriteLine("Valid options are:\n");
                        Console.WriteLine("TIME\n\t Show execution time in time units.");
                        Console.WriteLine("BREAKPOINT|BP [n]\n\tShow breakpoint n, if specified, otherwise show all breakpoints.");
                        Console.WriteLine("MEM|MEMORY [start end [WITH (DASM|DISASSEMBLY)]]\n\tShow contents of memory from 'start' to 'end'\n\twith optional disassembly.\n\n\tNote: 'start' or 'end' may be a symbol.");
                        Console.WriteLine("r(A|X|J|I1-I6)\n\tShow contents of specified register.");
                        Console.WriteLine("OF|OVERFLOW\n\tShow status of the overflow flag.");
                        Console.WriteLine("CI\n\tShow status of the comparison indicator.");
                        Console.WriteLine("PC\n\tShow contents of program counter.");
                        Console.WriteLine("STATE\n\tShow contents of all registers, flags and indicators.");
                        Console.WriteLine("VERBOSE\n\tShow status of the verbose flag.");
                        Console.WriteLine("DEVICE [n]\n\tShow information about device n, if specified,\n\totherwise show information for all devices.");
                        Console.WriteLine("SYMBOL {symb}\n\tShow the value of symbol 'symb'.");
                        Console.WriteLine("SYMBOLS\n\tShow all the contents of the symbol table.");
                        break;
                    case "set":
                        Console.WriteLine("Valid options are:\n");
                        Console.WriteLine("MEM|MEMORY {loc} {data}\n\tSet contents of memory at location 'loc' to 'data'.");
                        Console.WriteLine("r(A|X|J|I1-I6|PC) {data}\n\tSet contents of specified register to 'data'.");
                        Console.WriteLine("BP|BREAKPOINT {loc}\n\tSet a new breakpoint at location 'loc'.\n\n\tNote: 'loc' can be a symbol.");
                        break;
                    default:
                        Console.Error.WriteLine("Unknown command.");
                        break;
                }
            }
        }

        private void Redirect(string[] what)
        {
            int unitId = int.Parse(what[0]);
            if (what[1] == "console" || what[1] == "stdout")
            {
                machine.RedirectDevice(unitId, Console.OpenStandardOutput());
            }
            else if (what[1] == "stdout")
            {
                machine.RedirectDevice(unitId, Console.OpenStandardInput());
            }
            else
            {
                machine.RedirectDevice(unitId, null);
                FileStream fs;
                if (unitId == MIXMachine.CARD_READER)
                    fs = new FileStream(what[1], FileMode.Open);
                else
                    fs = new FileStream(what[1], FileMode.Append);
                machine.RedirectDevice(unitId, fs);
            }
        }

        private void LoadDeck(string deck)
        {
            FileStream deckStream = new FileStream(deck, FileMode.Open);
            machine.RedirectDevice(MIXMachine.CARD_READER, deckStream);
            machine.LoadDeck();
            deckStream.Close();
            machine.RedirectDevice(MIXMachine.CARD_READER, null);
        }

        private int LoadImage(string dump)
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(dump, FileMode.Open, FileAccess.Read, FileShare.Read);
            MIXWord startLoc = (MIXWord)formatter.Deserialize(stream);
            List<MemoryCell> data = (List<MemoryCell>)formatter.Deserialize(stream);
            symbolTable = (Dictionary<string, MIXWord>)formatter.Deserialize(stream);
            stream.Close();

            machine.LoadImage(data);
            machine.PC = startLoc;

            return data.Count;
        }

        private void Clear(string[] what)
        {
            switch (what[0])
            {
                case "bp":
                case "breakpoint":
                    if (what.Length == 1 || what[1] == "all")
                        machine.ClearAllBreakpoints();
                    else
                        machine.ClearBreakpoint(int.Parse(what[1]));
                    break;

                default:
                    Console.Error.WriteLine("Unknown parameter.");
                    break;
            }
        }

        private void Show(string[] what)
        {
            switch (what[0])
            {
                case "time":
                    Console.WriteLine(string.Format("Execution time: {0}u", machine.ExecutionTime));
                    break;
                case "bp":
                case "breakpoint":
                    if (what.Length == 1)
                    {
                        for (int i = 0; i < machine.BreakpointCount; i++)
                            Console.WriteLine(string.Format("{0} @ {1}", i, machine.GetBreakpoint(i)));
                    }
                    else
                    {
                        int bp = int.Parse(what[1]);
                        Console.WriteLine(string.Format("{0} @ {1}", bp, machine.GetBreakpoint(bp)));
                    }
                    break;
                case "mem":
                case "memory":
                    int start = 0, end = 3999;
                    if (what.Length > 1)
                    {
                        if (symbolTable.ContainsKey(what[1].ToUpper()))
                            start = symbolTable[what[1].ToUpper()].Value;
                        else
                            start = int.Parse(what[1]);
                        if (what.Length > 2)
                        {
                            if (symbolTable.ContainsKey(what[2].ToUpper()))
                                end = symbolTable[what[2].ToUpper()].Value;
                            else
                                end = int.Parse(what[2]);
                        }
                    }

                    bool dasm = false;
                    if (what.Length > 4 && what[3] == "with" && (what[4] == "disassembly" || what[4] == "dasm"))
                        dasm = true;

                    ShowMemory(start, end, dasm);
                    break;
                case "state":
                    ShowAllState();
                    break;
                case "ra":
                    ShowRegister(7);
                    break;
                case "rx":
                    ShowRegister(8);
                    break;
                case "rj":
                    ShowRegister(9);
                    break;
                case "ri1":
                    ShowRegister(1);
                    break;
                case "ri2":
                    ShowRegister(2);
                    break;
                case "ri3":
                    ShowRegister(3);
                    break;
                case "ri4":
                    ShowRegister(4);
                    break;
                case "ri5":
                    ShowRegister(5);
                    break;
                case "ri6":
                    ShowRegister(6);
                    break;
                case "overflow":
                case "of":
                    ShowRegister(10);
                    break;
                case "ci":
                    ShowRegister(11);
                    break;
                case "pc":
                    ShowRegister(12);
                    break;
                case "verbose":
                    Console.WriteLine(string.Format("Verbose mode: {0}", verbose));
                    break;
                case "devices":
                    ShowAllDevices();
                    break;
                case "device":
                    if (what.Length > 1)
                        ShowDevice(int.Parse(what[1]));
                    else
                        ShowAllDevices();
                    break;
                case "symbols":
                    foreach (var s in symbolTable)
                    {
                        Console.WriteLine(string.Format("{0} = {1} = {2} = '{3}'", s.Key, s.Value, s.Value.Value, MIXWordToString(s.Value)));
                    }
                    break;
                case "symbol":
                    if (symbolTable.ContainsKey(what[1].ToUpper()))
                    {
                        MIXWord w = symbolTable[what[1].ToUpper()];
                        Console.WriteLine(string.Format("{0} = {1} = {2} = '{3}'", what[1].ToUpper(), w, w.Value, MIXWordToString(w)));
                    }
                    else
                    {
                        Console.Error.WriteLine(string.Format("Symbol '{0}' not found.", what[1].ToUpper()));
                    }
                    break;
                default:
                    Console.Error.WriteLine(string.Format("Unknown parameter: '{0}'", what));
                    break;
            }
        }

        private void ShowDevice(int unitId)
        {
            Console.WriteLine(string.Format("UNIT #{0}: {1}", unitId, machine.GetDeviceInfo(unitId)));
        }

        private void ShowAllDevices()
        {
            for (int i = 0; i < 21; i++)
                ShowDevice(i);
        }

        private void ShowMemory(int start, int end, bool dasm)
        {
            Console.WriteLine(string.Format("MEMORY CONTENTS ({0:0000} TO {1:0000})", start, end));
            Console.WriteLine();
            for (int i = start; i <= end; i++)
            {
                MIXWord w = machine.Memory[i];
                if (!dasm)
                    Console.WriteLine(string.Format("@{0:0000}: {1} = {2} = '{3}'", i, w, Convert.ToString(w.Value).PadLeft(10), MIXWordToString(w)));
                else
                {
                    Console.WriteLine(string.Format("@{0:0000}: {1} = {2} = '{3}'\t{4}", i, w, Convert.ToString(w.Value).PadLeft(10), MIXWordToString(w), GetDisassembly(w)));
                }
            }
        }

        private string GetDisassembly(MIXWord w)
        {
            string disassembly = "";
            InstructionInfo info = machine.Disassemble(w);
            if (info == null)
                disassembly = "???";
            else
            {
                disassembly = machine.Disassemble(w).Name + " " + w[0, 2];
                disassembly += w[3] != 0 ? "," + w[3] : "";
                if (w[4] != machine.Disassemble(w).DefaultField)
                    disassembly += "(" + (w[4] / 8) + ":" + (w[4] % 8) + ")";
            }

            return disassembly;
        }

        private void ShowAllState()
        {
            ShowRegister(7);
            ShowRegister(8);
            ShowRegister(9);

            ShowRegister(1);
            ShowRegister(2);
            ShowRegister(3);
            ShowRegister(4);
            ShowRegister(5);
            ShowRegister(6);

            ShowRegister(10);
            ShowRegister(11);
            ShowRegister(12);
        }

        private void ShowRegister(byte which)
        {
            switch (which)
            {
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                    Console.WriteLine(string.Format("I{0}: {1} = {2} = '{3}'", which, machine.I[which - 1], machine.I[which - 1].Value, MIXWordToString(machine.I[which - 1])));
                    break;

                case 7: Console.WriteLine(string.Format(" A: {0} = {1} = '{2}'", machine.A, machine.A.Value, MIXWordToString(machine.A)));
                    break;
                case 8: Console.WriteLine(string.Format(" X: {0} = {1} = '{2}'", machine.X, machine.X.Value, MIXWordToString(machine.X)));
                    break;
                case 9: Console.WriteLine(string.Format(" J: {0} = {1} = '{2}'", machine.J, machine.J.Value, MIXWordToString(machine.J)));
                    break;
                case 10: Console.WriteLine(string.Format("Overflow: {0}", machine.Overflow));
                    break;
                case 11: Console.WriteLine(string.Format("CI: {0}", machine.CI));
                    break;
                case 12: Console.WriteLine(string.Format("PC: {0}", machine.PC));
                    break;

                default:
                    Console.Error.WriteLine("Unknown register.");
                    break;
            }
        }

        private void Set(string[] what)
        {
            switch (what[0])
            {
                case "mem":
                case "memory":
                    machine.Memory[int.Parse(what[1])] = new MIXWord(int.Parse(what[2]));
                    break;
                case "ra":
                    SetRegister(7, int.Parse(what[1]));
                    break;
                case "rx":
                    SetRegister(8, int.Parse(what[1]));
                    break;
                case "rj":
                    SetRegister(9, int.Parse(what[1]));
                    break;
                case "ri1":
                    SetRegister(1, int.Parse(what[1]));
                    break;
                case "ri2":
                    SetRegister(2, int.Parse(what[1]));
                    break;
                case "ri3":
                    SetRegister(3, int.Parse(what[1]));
                    break;
                case "ri4":
                    SetRegister(4, int.Parse(what[1]));
                    break;
                case "ri5":
                    SetRegister(5, int.Parse(what[1]));
                    break;
                case "ri6":
                    SetRegister(6, int.Parse(what[1]));
                    break;
                case "pc":
                    SetRegister(12, int.Parse(what[1]));
                    break;
                case "bp":
                case "breakpoint":
                    if (symbolTable.ContainsKey(what[1].ToUpper()))
                        machine.AddBreakpoint(symbolTable[what[1].ToUpper()]);
                    else
                        machine.AddBreakpoint(int.Parse(what[1]));
                    break;
                default:
                    Console.Error.WriteLine(string.Format("Unknown parameter: '{0}'", what));
                    break;
            }
        }

        private void SetRegister(int which, int value)
        {
            switch (which)
            {
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                    machine.I[which - 1].Value = value;
                    break;

                case 7: machine.A.Value = value;
                    break;
                case 8: machine.X.Value = value;
                    break;
                case 9: machine.J.Value = value;
                    break;
                case 12: machine.PC = value;
                    break;

                default:
                    Console.Error.WriteLine("Unknown register.");
                    return;
            }
            Console.WriteLine("Done.");
        }

        private bool verbose;

        private string MIXWordToString(MIXWord w)
        {
            string result = "";

            for (byte i = 1; i < 6; i++)
            {
                var ch = from c in MIXMachine.CHAR_TABLE
                         where w[i] == c.Value
                         select c.Key;
                if (ch.Count() > 0)
                    result += ch.ElementAt(0).ToString();
                else
                    result += "█";
            }

            return result;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
Greece Greece
I am a software developer (mainly in C# and T-SQL) for a project management company in Athens, Greece. I have been working with computers since early 1987. I am adept at Pascal, C, C++, Java (my MSc was sponsored by Sun Microsystems), Lisp, Scheme, F#, C# VB.Net, Perl and some others that are too obscure to mention. When I want a quick and dirty solution to a programming problem I use a functional language, such as Haskell, Scheme or, more recently, F#.

I also play the keyboards and compose music.

---------------------------------------------------------

MSc Distributed Systems and Networks - University of Kent at Canterbury
BEng Computer Systems Engineering - University of Kent at Canterbury

Comments and Discussions