All pastes #831410 Raw Edit

Untitled

public text v1 · immutable
#831410 ·published 2007-12-24 22:26 UTC
rendered paste body
using System;
using System.Runtime.InteropServices;
using D = System.Diagnostics.Debug;

namespace NXSNES {
    public class Ricoh5A22 : ICyclic {
        private IMemory mem;

        [Flags]
        public enum Status : int {
            C   = 0x001,
            Z   = 0x002,
            I   = 0x004,
            D   = 0x008,
            X   = 0x010,
            M   = 0x020,
            V   = 0x040,
            N   = 0x080,
            E   = 0x100
        };

        /* FIXME: not portable to big-endian systems */
        [StructLayout(LayoutKind.Explicit)]
        public struct Registers {
            [FieldOffset(0)]    public ushort A;
            [FieldOffset(0)]    public byte AL;
            [FieldOffset(1)]    public byte AH;

            [FieldOffset(2)]    public byte DB;     /* Data Bank Register */

            [FieldOffset(4)]    public ushort X;
            [FieldOffset(4)]    public byte XL;
            [FieldOffset(5)]    public byte XH;

            [FieldOffset(6)]    public ushort Y;
            [FieldOffset(6)]    public byte YL;
            [FieldOffset(7)]    public byte YH;

            [FieldOffset(8)]    public ushort D;    /* Direct Page Pointer */
            [FieldOffset(8)]    public byte DL;
            [FieldOffset(9)]    public byte DH;
            
            [FieldOffset(10)]   public ushort SP;   /* Stack Pointer */

            [FieldOffset(12)]   public int PL;      /* Program Counter Long */
            [FieldOffset(12)]   public ushort PC;   /* Program Counter */
            [FieldOffset(14)]   public byte PB;     /* Program Bank */

            [FieldOffset(16)]   public Status S;    /* Status */
        }

        public Registers R;

        private const long OneCycle = 6;

        private delegate Microcode Microcode(out long time);

        private int operand;

        public static class EmulationVectors {
            public const int IRQBRK  = 0xfffe;
            public const int Reset   = 0xfffc;
            public const int NMI     = 0xfffa;
            public const int Abort   = 0xfff8;
            public const int COP     = 0xfff4;
        }
        public static class NativeVectors {
            public const int IRQ     = 0xffee;
            public const int NMI     = 0xffea;
            public const int Abort   = 0xffe8;
            public const int BRK     = 0xffe6;
            public const int COP     = 0xffe5;
        }

/* ----------------------------------------------------------------------------
 *   Processor subroutines 
 * ------------------------------------------------------------------------- */
     
        private void SetZN(byte x)
        {
            if (x == 0)
                R.S |= Status.Z;
            else
                R.S &= Status.Z;

            if ((x & 0x80) == 0x80)
                R.S |= Status.N;
            else
                R.S &= ~Status.N;
        }

        private void SetZN(ushort x)
        {
            if (x == 0)
                R.S |= Status.Z;
            else
                R.S &= ~Status.Z;

            if ((x & 0x8000) == 0x8000)
                R.S |= Status.N;
            else
                R.S &= ~Status.N;
        }

/* ----------------------------------------------------------------------------
 *   Archetypes 
 * ------------------------------------------------------------------------- */

        /* Archetypes are generally calculations, such as ADC, LDA, and so
         * forth. In fact, every operation is at least a combination of an
         * addressing mode and an archetype on the 65816. Archetypes are
         * distinguished from microcode by the fact that microcode takes time
         * to execute, while archetypes are simply operations that the real
         * 65816 would perform as loading takes place. They are therefore best
         * viewed as "extensions" or "plugins" to an addressing mode instead of
         * actual microcode steps. */

        private delegate void Archetype();

        private void ADC()
        {
            ushort d = (ushort)operand;

            /* TODO: 8-bit version */
            if ((R.S & Status.M) == 0) {
                d += (ushort)(R.S & Status.C);
                uint r = (uint)(R.A + d);

                if ((r & 0xffff0000) != 0)
                    R.S |= Status.C;
                else
                    R.S &= ~Status.C;

                if (((R.A & d & 0x8000) != 0 && (r & 0x8000) == 0) ||
                        ((R.A & 0x8000) == 0 && (d & 0x8000) == 0 &&
                         (r & 0x8000) != 0))
                    R.S |= Status.V;
                else
                    R.S &= ~Status.V;

                R.A = (ushort)r;
                SetZN(R.A);
            }
        }

        private void LDA()
        {
            if ((R.S & Status.M) == 0) {
                R.A = (ushort)operand;
                SetZN(R.A);
            } else {
                R.AL = (byte)operand;
                SetZN(R.AL);
            }
        }

/* ----------------------------------------------------------------------------
 *   Addressing modes 
 * ------------------------------------------------------------------------- */

        private int offset;

        private Microcode Immediate(Archetype arch)
        {
            Microcode cycle2 = null, cycle2a = null;
           
            cycle2 =
                (out long time) => {    /* IDL */
                    operand = mem.GetByte(R.PC++, out time);
                    if ((R.S & Status.M) == 0)
                        return cycle2a;
                    else {
                        arch(); return null;
                    }
                };
            cycle2a =
                (out long time) => {    /* IDH */
                    operand |= ((int)(mem.GetByte(R.PC++, out time)) << 8);
                    arch(); return null;
                };

            return cycle2;
        }

        private Microcode Direct(Archetype arch, bool write)
        {
            if (!write) {
                Microcode cycle2 = null, cycle2a = null;
                Microcode cycle3 = null, cycle3a = null;

                cycle2 =
                    (out long time) => {    /* DO */
                        offset = mem.GetByte(R.PC, out time);
                        if (R.DL != 0)
                            return cycle2a; /* one extra cycle */
                        else {
                            R.PC++;
                            return cycle3;
                        }
                    };
                cycle2a =
                    (out long time) => {    /* IO */
                        R.PC++;
                        time = OneCycle;
                        return cycle3;
                    };
                cycle3 =
                    (out long time) => {    /* Data Low */
                        operand = mem.GetByte(R.D + offset, out time);
                        if ((R.S & Status.M) == 0)
                            return cycle3a;
                        else {
                            arch(); return null;
                        }
                    };
                cycle3a =
                    (out long time) => {    /* Data High */
                        operand |= ((int)(mem.GetByte(R.D + offset + 1, out
                                        time)) << 8);
                        arch(); return null;
                    };

                return cycle2;
            } else
                throw new Exception();
        }

/* ----------------------------------------------------------------------------
 *   Operation implementations
 * ------------------------------------------------------------------------- */

        Microcode[] ops = new Microcode[256];

        private void InitOps()
        {
            ops[0x69] = Immediate   (ADC);
            ops[0xa5] = Direct      (LDA, false);
            ops[0xa9] = Immediate   (LDA);
        }

/* ----------------------------------------------------------------------------
 *   Initialization and interrupts
 * ------------------------------------------------------------------------- */

        public Ricoh5A22(IMemory m)
        {
            mem = m;
            InitOps();
        }

        public void Reset()
        {
            long time;

            R.S  = Status.E;
            R.PC = (ushort)(mem.GetByte(EmulationVectors.Reset, out time) |
                    (mem.GetByte(EmulationVectors.Reset + 1, out time) << 8));
            R.PB = 0;
        }

/* ----------------------------------------------------------------------------
 *   The main run loop
 * ------------------------------------------------------------------------- */

        private Microcode currentMicroOp = null;
        private long nextClock = 0;

        public long Step(long clock)
        {
            long time;

            if (clock < nextClock)
                return nextClock;

            if (currentMicroOp == null)
                /* Null means do an opcode fetch-and-decode, which always takes
                 * (mem speed) cycles. */
                currentMicroOp = ops[mem.GetByte(R.PC++, out time)];
            else
                currentMicroOp = currentMicroOp(out time);

            nextClock = clock + time;
            return nextClock;
        }

    }
}