Beispiel #1
0
        /// <summary>
        /// Effectue le décodage des instructions
        /// </summary>
        /// <param name="bin"></param>
        /// <param name="offset"></param>
        /// <param name="context">Contexte de décodage. Par défaut, prend la valeur du mode actuel du CPU</param>
        /// <returns></returns>
        public InstructionsBin Decode(MemoryBin bin, int offset, InstructionDecodeContext context)
        {
            context.Source = bin;

            var decodeResult = new List<InstructionReference>();
            var length = bin.Length;

            int originalOffset = 0;

            while (offset < length)
            {
                var code = bin.ReadInt1(originalOffset = offset);
                offset++;

                var match = knownInstructions[code];

                var instructionReference = new InstructionReference();
                instructionReference.instruction = match;
                instructionReference.offset = originalOffset;

                match.DecodeArguments(bin, ref context, ref offset, ref instructionReference);

                decodeResult.Add(instructionReference);
            }

            var result = new InstructionsBin() { DecodeContext = context, Instructions = decodeResult.ToArray() };

            return result;
        }
Beispiel #2
0
        public CPU(SnesPlatform platform)
        {
            if (platform == null)
                throw new ArgumentNullException("platform");

            Platform = platform;

            // TODO vérifier les plages
            DirectPage = new MemoryBin(platform.Memory, 0, 256);

            RAM = new SnesMemoryMappingBin(platform.Memory);

            NativeStackBin = new MemoryBin(platform.Memory, 0x7E0000, 0x00FFFF + 1);
            EmulationStackBin = new MemoryBin(platform.Memory, NativeStackBin.Start + 0x100, 0xFF + 1);

            DecodeTable = new InstructionsDecodeTable(this);

            Reset();
        }
Beispiel #3
0
        /// <summary>
        /// Decode une unique instruction à partir de l'offset spécifié dans le bin spécifié à l'aide du contexte courrant, et la stoque dans decodedInstruction
        /// </summary>
        /// <param name="bin"></param>
        /// <param name="offset"></param>
        /// <param name="context"></param>
        /// <param name="decodedInstruction"></param>
        public void DecodeOnce(MemoryBin bin, ref int offset, ref InstructionDecodeContext context, ref InstructionReference decodedInstruction)
        {
            var code = bin.ReadInt1(offset);
            offset++;

            var match = knownInstructions[code];
            decodedInstruction.instruction = match;
            decodedInstruction.offset = offset - 1;

            match.DecodeArguments(bin, ref context, ref offset, ref decodedInstruction);
        }
Beispiel #4
0
 public InstructionsBin Decode(MemoryBin bin, int offset)
 {
     return Decode(bin, offset, CPU.BuildCurrentContext());
 }
Beispiel #5
0
        /// <summary>
        /// Exécute une seule instruction en partant de l'addresse spécifiée
        /// </summary>
        /// <param name="bin">Mémoire à utiliser pour la référence</param>
        /// <param name="startAddress">Adresse, ou rien pour utiliser le PC actuel</param>
        public void RunOnce(MemoryBin bin, int startAddress = -1)
        {
            if (bin == null)
                throw new ArgumentNullException("bin");

            if (startAddress >= 0)
                platform.CPU.PC = startAddress;

            var refInstruction = new InstructionReference();
            var context = platform.CPU.BuildCurrentContext();

            platform.Decoder.DecodeOnce(bin, ref platform.CPU.PC, ref context, ref refInstruction);

            refInstruction.instruction.Run(refInstruction.param1, refInstruction.param2);
        }
Beispiel #6
0
        private static void InitTestContext(out SnesPlatform snes, out MemoryBin romBin, out int writeOffset)
        {
            snes = new SnesPlatform();
            romBin = new MemoryBin(snes.Memory, 0, snes.Memory.Length);

            snes.Interpreter.RethrowExecutionExceptions = true;
            snes.Interpreter.Trace = true;

            snes.Encoder.EnableInstructionValidation = true;

            writeOffset = 0;
        }
Beispiel #7
0
        /// <summary>
        /// Lance un thread d'exécution qui process toutes les instructions à partir de startAddress
        /// </summary>
        /// <param name="bin">Mémoire à utiliser pour la référence</param>
        /// <param name="startAddress">Adresse, ou rien pour utiliser le PC actuel</param>
        /// <param name="separateThread">true pour lancer dans un thread séparé, false sinon</param>
        public void Interpret(MemoryBin bin, int startAddress = -1, bool separateThread = true)
        {
            if (bin == null)
                throw new ArgumentNullException("bin");
            else if (interpreting)
                throw new InvalidOperationException("Une seule interprétation possible à la fois");
            else
            {
                // Il y a clairement une RunCondition ici mais bon ...
                interpreting = true;
                stop = false;

                if (startAddress >= 0)
                    platform.CPU.PC = startAddress;

                interpreterMemory = bin;

                if (separateThread)
                {
                    startedInSeparateThread = true;

                    interpretThread = new Thread(new ThreadStart(InterpretThreadFunction));
                    interpretThread.Name = String.Format("SNES Interpreter - {0}", DateTime.Now.ToLongTimeString());

                    // Si on kill le programme, ça va killer aussi le thread ... c'est bien ça
                    interpretThread.IsBackground = true;

                    interpretThread.Start();
                }
                else
                {
                    startedInSeparateThread = false;

                    InterpretThreadFunction();
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// Ecrit une instruction. C'est un peut chiant car il faut spécifier à la fois l'instruction, l'addrMode, le type des args et les args mais bon,
        /// pour l'instant ça devrait faire l'affaire
        /// </summary>
        /// <param name="bin"></param>
        /// <param name="offset"></param>
        /// <param name="opCode"></param>
        /// <param name="addrMode"></param>
        /// <param name="param1Type"></param>
        /// <param name="param1"></param>
        /// <param name="param2"></param>
        public void Write(MemoryBin bin, ref int offset, OpCodes opCode, AddressingModes addrMode = AddressingModes.Direct, ArgumentType param1Type = ArgumentType.I1, int param1 = 0, int param2 = 0)
        {
            var originalOffset = offset;

            /* d'abord, on cherche l'instruction */
            Instruction match_instruction = null;

            var match_code = from instruction in cpu.DecodeTable.KnownInstructions where instruction.Code == opCode select instruction;
            switch(match_code.Count())
            {
                case 0: throw new InvalidOperationException(String.Format("Instruction not found : {0}", opCode));
                case 1: {
                    /* on se prend pas la tête sur le mode d'addr */
                    match_instruction = match_code.First();
                    break;
                }
                default:
                    {
                        /* on cherche le bon mode */
                        match_instruction = (from code_item in match_code where code_item.AddrMode == addrMode select code_item).FirstOrDefault();

                        if(match_instruction== null)
                            throw new InvalidOperationException(String.Format("Instruction {0} was found, but the addrMode {1} has no binding", opCode, addrMode));

                        break;
                    }
            }

            /* on écrit le code */
            bin.WriteInt1(offset, match_instruction.AssociatedHexCode);
            offset++;

            /* s'il y a des arguments, on les écrit */
            if (match_instruction.HaveArgs)
            {
                WriteParameter(bin, ref offset, param1Type, param1);

                if (addrMode == AddressingModes.BlockMove)
                {
                    /* c'est le seul mode qui a deux arguments, et il a la même
                     * taille que le premier argument
                     * */
                    WriteParameter(bin, ref offset, param1Type, param2);
                }
            }

            if (enableInstructionValidation)
            {
                /* on vérifie l'instruction écrite */
                var instructionDecode = new InstructionReference();
                var context = cpu.BuildCurrentContext();

                cpu.Platform.Decoder.DecodeOnce(bin, ref originalOffset, ref context, ref instructionDecode);

                if (instructionDecode.instruction.Code != opCode ||
                    instructionDecode.param1 != param1 ||
                    instructionDecode.param2 != param2)
                {
                    throw new InvalidProgramException(String.Format("Instruction decode mismatch.\r\nEncoded:{0}({1},{2}) [{3}]\r\nDecoded:{4}({5},{6}) [{7}]",
                        opCode,
                        param1, param2,
                        addrMode,

                        instructionDecode.instruction.Code,
                        instructionDecode.param1, instructionDecode.param2,
                        instructionDecode.instruction.AddrMode));
                }
                else if (originalOffset != offset)
                {
                    throw new InvalidProgramException(String.Format("Instruction decode mismatch, Invalid offset. Difference : {0}", offset - originalOffset));
                }
            }
        }
Beispiel #9
0
 private void WriteParameter(MemoryBin bin, ref int offset, ArgumentType paramType, int param)
 {
     switch (paramType)
     {
         default:
         case ArgumentType.None:
             break;
         case ArgumentType.I1:
             bin.WriteInt1(offset, (byte)param);
             offset++;
             break;
         case ArgumentType.I2:
             bin.WriteInt1(offset, (byte)param);
             bin.WriteInt1(offset + 1, (byte)(param >> 8));
             offset += 2;
             break;
         case ArgumentType.I3:
             bin.WriteInt1(offset, (byte)param);
             bin.WriteInt1(offset + 1, (byte)(param >> 8));
             bin.WriteInt1(offset + 2, (byte)(param >> 16));
             offset += 3;
             break;
     }
 }
Beispiel #10
0
        public void WriteNativeToEmulationMode(MemoryBin romBin, ref int writeOffset)
        {
            Write(romBin, ref writeOffset, OpCodes.SEC);
            Write(romBin, ref writeOffset, OpCodes.XCE);

            /* Histoire de préserver les changements de contexte */
            if (enableInstructionValidation)
                cpu.SwitchFromNativeToEmulationMode();
        }
Beispiel #11
0
        /// <summary>
        /// Utilise une instruction spéciale qui va appeler un code à un moment clef de l'exécution
        /// </summary>
        /// <remarks>ATTENTION : Il n'y a que 254 slots disponibles en l'état. Pour effacer les associations, utiliser ClearCallbackInvokes</remarks>
        /// <param name="bin"></param>
        /// <param name="offset"></param>
        /// <param name="callback"></param>
        public void WriteCallbackInvoke(MemoryBin bin, ref int offset, Action<Instruction> callback)
        {
            if (callback == null)
                return;

            int slot = 0;

            for(slot = 0; slot <= 254;slot++)
            {
                if (cpu.InstrumentationCallBacks[slot] == null)
                    break;
            }

            if (slot > 254)
                throw new InvalidOperationException("Tous les slots de callback sont utilisés. Impossible d'encoder cette instruction");
            else
            {
                cpu.InstrumentationCallBacks[slot] = callback;
                Write(bin, ref offset, OpCodes.WDM, param1Type: ArgumentType.I1, param1: slot + 1);
            }
        }