Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A Simulator for Knuth's MIX Computer

, 2 Feb 2011 CPOL
Assembler and Simulator for Don Knuth's MIX Computer from The Art of Computer Programming.
MIXWare.zip
Examples
easter.mixal
maximum.mixal
perms.mixal
perms-input.deck
primes.mixal
MIX
MIX.csproj.user
Properties
MIXAsm
MIXAsm.csproj.user
Properties
MIXLib
Devices.cd
Machine.cd
MIXLib.csproj.user
Parser
Properties
MIXWare.pdf
using System;
using System.IO;
using System.Linq;
using System.Threading;

namespace MIXLib
{
    #region Abstract Device

    public abstract class MIXDevice
    {
        /// <summary>
        /// The descriptive name of this device
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// The device's backing store
        /// </summary>
        protected Stream Store 
        {
            get { return store; }
            set 
            {
                lock (this)
                {
                    Ready = false;
                    store = value;
                    StoreChanged();
                    Ready = true;
                }
            }
        }

        public bool Ready { get; protected set; }

        private Stream store;

        protected virtual void StoreChanged() { }

        /// <summary>
        /// The device's block size in MIX words
        /// </summary>
        protected byte BlockSize { get; private set; }
        /// <summary>
        /// The MIX machine this device is attached to
        /// </summary>
        protected MIXMachine Machine { get; private set; }
        
        protected MIXDevice(MIXMachine machine, string name, Stream store, byte blockSize)
        {
            Name = name;
            Store = store;
            BlockSize = blockSize;
            Machine = machine;
        }

        public void Redirect(Stream newStore)
        {
            if (Store != null) Store.Close();
            Store = newStore;
        }

        public void Flush()
        {
            if (Store != null && Store.CanWrite) Store.Flush();
        }

        protected 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;
                result += ch.DefaultIfEmpty('█').First().ToString();
            }

            return result;
        }

        public override string ToString()
        {
            string result = "";
            if (Store == null)
                result = string.Format("NAME: {0}; BLOCK SIZE: {1}; BACKING STORE: N/A", Name, BlockSize);
            else if (Store is FileStream)
                result = string.Format("NAME: {0}, BLOCK SIZE: {1}, BACKING STORE: {2}", Name, BlockSize, (Store as FileStream).Name);
            else if (Store is MemoryStream)
                result = string.Format("NAME: {0}, BLOCK SIZE: {1}, BACKING STORE: MEMORY", Name, BlockSize);
            else
                result = string.Format("NAME: {0}, BLOCK SIZE: {1}, BACKING STORE: CONSOLE", Name, BlockSize);

            return string.Format(result);
        }

        public void Out(int M)
        {
            Thread t = new Thread(OutProc);
            t.Start(M);
        }

        public void In(int M)
        {
            Thread t = new Thread(InProc);
            t.Start(M);
        }

        public void IOC(int M)
        {
            Thread t = new Thread(IOCProc);
            t.Start(M);
        }

        protected abstract void OutProc(object M);
        protected abstract void InProc(object M);
        protected abstract void IOCProc(object M);
    }

    #endregion

    #region Tapes

    public class Tape : MIXDevice
    {
        public Tape(MIXMachine machine, Stream store) : base(machine, "TAPE", store, 100) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;

                    for (int i = 0; i < BlockSize; i++)
                    {
                        byte[] buffer = Machine.Memory[M + i].ToByteArray();
                        foreach (var b in buffer)
                            Store.WriteByte(b);
                    }
                }

                Ready = true;
            }
        }

        protected override void InProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;

                    for (int i = 0; i < BlockSize; i++)
                    {
                        byte[] buffer = new byte[6];
                        using (MemoryStream ms = new MemoryStream())
                        {
                            int read;
                            read = Store.Read(buffer, 0, 6);
                            while (read > 0)
                            {
                                ms.Write(buffer, 0, read);
                                read = Store.Read(buffer, 0, 6);
                            }
                            MIXWord w = MIXWord.FromByteArray(ms.ToArray());
                            Machine.Memory[M + i].Value = w.Value;
                        }
                    }
                }

                Ready = true;
            }
        }

        protected override void IOCProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    if (M == 0)
                        Store.Seek(0, SeekOrigin.Begin);
                    else if (M < 0)
                        Store.Seek(-(M * BlockSize * 6), SeekOrigin.Current);
                    else
                        Store.Seek(M * BlockSize * 6, SeekOrigin.Current);
                }

                Ready = true;
            }
        }
    }

    #endregion

    #region Disks

    public class Disk : MIXDevice
    {
        public Disk(MIXMachine machine, Stream store) : base(machine, "DISK", store, 100) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    Store.Seek(Machine.X * BlockSize, SeekOrigin.Begin);

                    for (int i = 0; i < BlockSize; i++)
                    {
                        byte[] buffer = Machine.Memory[M + i].ToByteArray();
                        foreach (var b in buffer)
                            Store.WriteByte(b);
                    }
                }

                Ready = true;
            }
        }

        protected override void InProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    Store.Seek(Machine.X * BlockSize, SeekOrigin.Begin);

                    for (int i = 0; i < BlockSize; i++)
                    {
                        byte[] buffer = new byte[6];
                        using (MemoryStream ms = new MemoryStream())
                        {
                            int read;
                            read = Store.Read(buffer, 0, 6);
                            while (read > 0)
                            {
                                ms.Write(buffer, 0, read);
                                read = Store.Read(buffer, 0, 6);
                            }
                            MIXWord w = MIXWord.FromByteArray(ms.ToArray());
                            Machine.Memory[M + i].Value = w.Value;
                        }
                    }
                }

                Ready = true;
            }
        }

        protected override void IOCProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    if (M == 0)
                        Store.Seek(Machine.X * BlockSize, SeekOrigin.Begin);
                }

                Ready = true;
            }
        }
    }

    #endregion

    #region Card Reader

    public class CardReader : MIXDevice
    {
        public CardReader(MIXMachine machine, Stream store) : base(machine, "CARD_READER", store, 16) { }

        protected override void OutProc(object data)
        {
        }

        protected StreamReader storeReader;
        protected override void StoreChanged()
        {
            if (storeReader != null) storeReader.Close();
            if (Store != null)
                storeReader = new StreamReader(Store, true);
            else
                storeReader = null;
        }

        protected override void InProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;

                    if (storeReader.Peek() != -1)
                    {
                        string inp = storeReader.ReadLine().PadRight(BlockSize * 5);

                        // Break the input string into chunks of 5.
                        // Should be BlockSize chunks.
                        var chunks = inp
                            .Select((x, i) => new { Index = i, Value = x })
                            .GroupBy(x => x.Index / 5)
                            .Select(x => x.Select(v => v.Value).ToList())
                            .ToList();

                        int j = 0;
                        foreach (var curr in chunks)
                        {
                            MIXWord w = new MIXWord();
                            w[1] = MIXMachine.CHAR_TABLE[curr[0]];
                            w[2] = MIXMachine.CHAR_TABLE[curr[1]];
                            w[3] = MIXMachine.CHAR_TABLE[curr[2]];
                            w[4] = MIXMachine.CHAR_TABLE[curr[3]];
                            w[5] = MIXMachine.CHAR_TABLE[curr[4]];

                            Machine.Memory[M + j] = w;
                            j++;
                        }
                    }
                }

                Ready = true;
            }
        }

        protected override void IOCProc(object data)
        {
        }
    }

    #endregion

    #region Card Punch

    public class CardPunch : MIXDevice
    {
        public CardPunch(MIXMachine machine, Stream store) : base(machine, "CARD_PUNCH", store, 16) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    Store.Seek(0, SeekOrigin.End);

                    for (int j = 0; j < BlockSize; j++)
                    {
                        string outp = MIXWordToString(Machine.Memory[M + j]);
                        storeWriter.Write(outp);
                    }
                    storeWriter.WriteLine();
                }

                Ready = true;
            }
        }

        protected StreamWriter storeWriter;
        protected override void StoreChanged()
        {
            if (storeWriter != null) storeWriter.Close();
            if (Store != null)
            {
                storeWriter = new StreamWriter(Store);
                storeWriter.AutoFlush = true;
            }
            else
                storeWriter = null;
        }

        protected override void InProc(object data)
        {
        }

        protected override void IOCProc(object data)
        {
        }
    }

    #endregion

    #region Line Printer

    public class LinePrinter : MIXDevice
    {
        public LinePrinter(MIXMachine machine, Stream store) : base(machine, "LINE_PRINTER", store, 24) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;

                    StreamWriter writer = new StreamWriter(Store);
                    for (int j = 0; j < BlockSize; j++)
                    {
                        string outp = MIXWordToString(Machine.Memory[M + j]);
                        writer.Write(outp);
                    }
                    writer.WriteLine();
                    writer.Flush();
                }

                Ready = true;
            }
        }

        protected override void InProc(object M)
        {
        }

        protected override void IOCProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    if (M == 0)
                    {
                        StreamWriter writer = new StreamWriter(Store);
                        writer.WriteLine("============ PAGE BREAK ============");
                        writer.Flush();
                    }
                }

                Ready = true;
            }
        }
    }

    #endregion

    #region Terminal

    public class Terminal
        : MIXDevice
    {
        public Terminal(MIXMachine machine, Stream store) : base(machine, "TERMINAL", store, 14) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                int M = (int)data;

                StreamWriter writer = new StreamWriter(Console.OpenStandardOutput());
                for (int j = 0; j < BlockSize; j++)
                {
                    string outp = MIXWordToString(Machine.Memory[M + j]);
                    writer.Write(outp);
                }
                writer.WriteLine();
                writer.Flush();

                Ready = true;
            }
        }

        protected override void InProc(object data)
        {
            lock (this)
            {
                Ready = false;

                int M = (int)data;

                StreamReader storeReader = new StreamReader(Console.OpenStandardInput());
                if (storeReader.Peek() != -1)
                {
                    string inp = storeReader.ReadLine().PadRight(BlockSize * 5).Substring(0, BlockSize * 5);

                    // Break the input string into chunks of 5.
                    // Should be BlockSize chunks.
                    var chunks = inp
                        .Select((x, i) => new { Index = i, Value = x })
                        .GroupBy(x => x.Index / 5)
                        .Select(x => x.Select(v => v.Value).ToList())
                        .ToList();

                    int j = 0;
                    foreach (var curr in chunks)
                    {
                        MIXWord w = new MIXWord();
                        w[1] = MIXMachine.CHAR_TABLE[curr[0]];
                        w[2] = MIXMachine.CHAR_TABLE[curr[1]];
                        w[3] = MIXMachine.CHAR_TABLE[curr[2]];
                        w[4] = MIXMachine.CHAR_TABLE[curr[3]];
                        w[5] = MIXMachine.CHAR_TABLE[curr[4]];

                        Machine.Memory[M + j] = w;
                        j++;
                    }
                }

                Ready = true;
            }
        }

        protected override void IOCProc(object data)
        {
        }
    }

    #endregion

    #region Paper Tape

    public class PaperTape
        : MIXDevice
    {
        public PaperTape(MIXMachine machine, Stream store) : base(machine, "PAPER_TAPE", store, 14) { }

        protected override void OutProc(object data)
        {
            lock (this)
            {
                Ready = false;

                int M = (int)data;

                StreamWriter writer = new StreamWriter(Store);
                for (int j = 0; j < BlockSize; j++)
                {
                    string outp = MIXWordToString(Machine.Memory[M + j]);
                    writer.Write(outp);
                }
                writer.WriteLine();
                writer.Flush();

                Ready = true;
            }
        }

        protected override void InProc(object data)
        {
            lock (this)
            {
                Ready = false;

                int M = (int)data;

                StreamReader storeReader = new StreamReader(Store);
                if (storeReader.Peek() != -1)
                {
                    string inp = storeReader.ReadLine().PadRight(BlockSize * 5).Substring(0, BlockSize * 5);

                    // Break the input string into chunks of 5.
                    // Should be BlockSize chunks.
                    var chunks = inp
                        .Select((x, i) => new { Index = i, Value = x })
                        .GroupBy(x => x.Index / 5)
                        .Select(x => x.Select(v => v.Value).ToList())
                        .ToList();

                    int j = 0;
                    foreach (var curr in chunks)
                    {
                        MIXWord w = new MIXWord();
                        w[1] = MIXMachine.CHAR_TABLE[curr[0]];
                        w[2] = MIXMachine.CHAR_TABLE[curr[1]];
                        w[3] = MIXMachine.CHAR_TABLE[curr[2]];
                        w[4] = MIXMachine.CHAR_TABLE[curr[3]];
                        w[5] = MIXMachine.CHAR_TABLE[curr[4]];

                        Machine.Memory[M + j] = w;
                        j++;
                    }
                }

                Ready = true;
            }
        }

        protected override void IOCProc(object data)
        {
            lock (this)
            {
                Ready = false;

                if (Store != null)
                {
                    int M = (int)data;
                    if (M == 0)
                        Store.Seek(0, SeekOrigin.Begin);
                }

                Ready = true;
            }
        }
    }

    #endregion
}

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)

Share

About the Author

George Tryfonas
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

| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 2 Feb 2011
Article Copyright 2011 by George Tryfonas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid