Иконка ресурса

FREE Microcontroller : 0.0.0 0.0.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using UnityEngine;

using System.Runtime.InteropServices;

namespace Oxide.Plugins
{
    [Info("Microcontroller", "Andrew", "0.0.0")]
    public class Microcontroller : RustPlugin {
        static ConfigData config;
        static Microcontroller plugin = null;
        static Assembler assembler = null;
        string shortName = "electrical.random.switch.deployed";
        string prefab = "assets/prefabs/deployable/playerioents/gates/randswitch/electrical.random.switch.deployed.prefab";
        static int numChannels = 4;
        bool ignoreSpawn = false;
        ulong McuId = 0;
        Dictionary<ulong, McuComponent> McuComponents = new Dictionary<ulong, McuComponent>();
        Dictionary<uint, IOEntityMapper> ioEnts = new Dictionary<uint, IOEntityMapper>();
        McuManager manager = null;
        const int maxAvgCharsPerLine = 32;

        class Assembler {
            List<Statement> statements = new List<Statement>();
            public Dictionary<string, Symbol> symbols = new Dictionary<string, Symbol>();
            public List<string> errors = new List<string>();
            public List<byte> programData = new List<byte>();
            public List<Instruction> instructions = new List<Instruction>();
            public List<uint> code = new List<uint>();
            public byte[] memory = null;
            public int numInstructions;
            List<KeyValuePair<Symbol, Symbol>> isrs = new List<KeyValuePair<Symbol, Symbol>>();

            public void Reset() {
                statements.Clear();
                symbols.Clear();
                errors.Clear();
                programData.Clear();
                instructions.Clear();
                code.Clear();
                memory = null;
                isrs.Clear();
            }

            struct Statement {
                public int lineNum;
                public string[] line;
                public Token[] tokens;
            }

            public struct Variable {
                public CPU.Value32 val32;
                public Type type;
                public int size;

                public enum Type {
                    NONE, UNKNOWN, INT, UINT, BYTE, FLOAT
                }
            }

            struct Token {
                public enum Type {
                    NONE,
                    DIRECTIVE,
                    LABEL,
                    INSTRUCTION,
                    IDENTIFIER,
                    INTEGER,
                    FLOAT
                }

                public int offset;
                public Type type;
                public string stringValue;
                public Variable var;
                public string cond;
            }

            public struct Symbol {
                public string name;
                public Variable var;
                public int labelInstructionIndex;
                public Type type;

                public enum Type {
                    NONE,
                    LABEL,
                    LITERAL,
                    CONSTANT,
                    REGISTER
                }
            }

            public void LoadProgramToCPU(CPU cpu) {
                cpu.Reset();
                cpu.instructions = new uint[numInstructions];
                cpu.memory = new byte[memory.Length];
                memory.CopyTo(cpu.memory, 0);
                cpu.registers[(int)CPU.Register.SP] = (uint)programData.Count;
                cpu.pc = symbols["main"].var.val32.Uint;
                int instructionIndex = 0;

                for(int i = 0; i < instructions.Count; i++) {
                    var instruction = instructions[i];
                    cpu.instructions[instructionIndex++] = instruction.Create();

                    if(instruction.additionalInstructions == null) {
                        continue;
                    }

                    for(int j = 0; j < instruction.additionalInstructions.Length; j++) {
                        cpu.instructions[instructionIndex++] = instruction.additionalInstructions[j];
                    }
                }

                cpu.flags |= (uint)CPU.Flag.READY;
            }

            public struct Instruction {
                public CPU.Opcode opcode;
                public CPU.Cond cond;
                public List<CPU.Register> operands;
                public Symbol immediate;
                public int address;
                public uint[] additionalInstructions;

                public int AdditionalInstructions(bool ignoreLabelAddresses) {
                    if(immediate.type == Symbol.Type.NONE || (immediate.type == Symbol.Type.LABEL && ignoreLabelAddresses)) {
                        return 0;
                    }

                    if(immediate.var.type == Variable.Type.FLOAT) {
                        return 1;
                    } else if(immediate.var.type == Variable.Type.UNKNOWN) {
                        return immediate.var.size / sizeof(uint);
                    } else if(immediate.var.type == Variable.Type.INT) {
                        if(immediate.var.val32.Uint >= GetMaxImmediateValue(operands.Count + 1)) {
                            return 1;
                        }
                    }

                    return 0;
                }

                public uint Create() {
                    uint instruction = (uint)cond << (int)CPU.Instruction.COND_SHIFT;
                    instruction |= (uint)opcode << (int)CPU.Instruction.OPCODE_SHIFT;

                    for(int i = 0; i < operands.Count; i++) {
                        switch(i) {
                            case 0:
                                instruction |= (uint)((uint)operands[i] << (int)CPU.Instruction.OP1_SHIFT);
                                instruction |= (uint)CPU.Instruction.OP1_FLAG_MASK;
                                break;
                            case 1:
                                instruction |= (uint)((uint)operands[i] << (int)CPU.Instruction.OP2_SHIFT);
                                instruction |= (uint)CPU.Instruction.OP2_FLAG_MASK;
                                break;
                            case 2:
                                instruction |= (uint)((uint)operands[i] << (int)CPU.Instruction.OP3_SHIFT);
                                instruction |= (uint)CPU.Instruction.OP3_FLAG_MASK;
                                break;
                        }
                    }

                    instruction |= immediate.var.val32.Uint;

                    return instruction;
                }
            }

            public bool Compile(string code, int memorySize) {
                Reset();

                if(!Preprocess(code)) {
                    return false;
                }

                if(!Tokenize()) {
                    return false;
                }

                if(!Parse()) {
                    return false;
                }

                if(!symbols.ContainsKey("main")) {
                    errors.Add($"program must define \"main:\" entry point");
                    return false;
                }

                if(!GenerateCode()) {
                    return false;
                }

                if(memorySize < programData.Count) {
                    errors.Add($"program data is larger than desired memory size ({memorySize} < {programData.Count})");
                    return false;
                }

                memory = new byte[memorySize];
                programData.CopyTo(memory, 0);

                return true;
            }

            static bool TryStringToOpcode(string str, out CPU.Opcode opcode) {
                var names = Enum.GetNames(typeof(CPU.Opcode));
                var values = Enum.GetValues(typeof(CPU.Opcode));
                str = str.ToUpperInvariant();
            
                for(int i = 0; i < names.Length; i++) {
                    if(names[i] == str) {
                        opcode = (CPU.Opcode)values.GetValue(i);
                        return true;
                    }
                }

                opcode = 0;
                return false;
            }

            static bool TryStringToCond(string str, out CPU.Cond cond) {
                var names = Enum.GetNames(typeof(CPU.Cond));
                var values = Enum.GetValues(typeof(CPU.Cond));
                str = str.ToUpperInvariant();
            
                for(int i = 0; i < names.Length; i++) {
                    if(names[i] == str) {
                        cond = (CPU.Cond)values.GetValue(i);
                        return true;
                    }
                }

                cond = 0;
                return false;
            }

            void AllocateRegisters() {
                var names = Enum.GetNames(typeof(CPU.Register));

                for(int i = 0; i < names.Length; i++) {
                    var name = names[i].ToLowerInvariant();

                    symbols.Add(name, new Symbol {
                        name = name,
                        var = new Variable{ val32 = new CPU.Value32{ Int = i } },
                        type = Symbol.Type.REGISTER
                    });
                }
            }

            void AddData(Variable var, int size) {
                //todo: don't ignore size
                programData.Add(var.val32.byte0);
                programData.Add(var.val32.byte1);
                programData.Add(var.val32.byte2);
                programData.Add(var.val32.byte3);
            }

            static uint GetMaxImmediateValue(int argNum) {
                switch(argNum) {
                    case 1:
                        return (uint)CPU.Instruction.IMM1_MASK;
                    case 2:
                        return (uint)CPU.Instruction.IMM2_MASK;
                    case 3:
                        return (uint)CPU.Instruction.IMM3_MASK;
                }

                return 0;
            }

            void AddError(int line, string error) {
                errors.Add($"[error] line {line}: {error}");
            }

            bool GenerateCode() {
                numInstructions = 0;

                // first pass, can't update label addresses yet
                for(int i = 0; i < instructions.Count; i++) {
                    Instruction instruction = instructions[i];
                    int additionalInstructions = instruction.AdditionalInstructions(true);

                    if(additionalInstructions != 0) {
                        instruction.additionalInstructions = new uint[additionalInstructions];

                        //todo: support more than int, uint, and float
                        instruction.additionalInstructions[0] = instruction.immediate.var.val32.Uint;
                        instruction.immediate.var.val32.Uint = GetMaxImmediateValue(instruction.operands.Count + 1);
                    }

                    instruction.address = numInstructions;
                    numInstructions += 1 + additionalInstructions;

                    instructions[i] = instruction;
                }

                int growth = 0;

                for(int i = 0; i < instructions.Count; i++) {
                    Instruction instruction = instructions[i];
                
                    if(instruction.immediate.type == Symbol.Type.LABEL) {
                        uint maxValue = GetMaxImmediateValue(instruction.operands.Count + 1);
                        var immediate = instruction.immediate;
                        Instruction target = instructions[instruction.immediate.labelInstructionIndex];

                        if(target.address + growth >= maxValue) {
                            growth++;
                            numInstructions++;
                            instruction.additionalInstructions = new uint[1];
                            instruction.additionalInstructions[0] = (uint)(target.address + growth);
                            instruction.immediate.var.val32.Uint = maxValue;
                        } else {
                            instruction.immediate.var.val32.Int = target.address + growth;
                        }

                        if(!symbols.ContainsKey(instruction.immediate.name)) {
                            errors.Add($"missing symbol \"{instruction.immediate.name}\" (this should never happen)");
                            return false;
                        }

                        var symbol = symbols[instruction.immediate.name];
                        symbol.var.val32.Int = target.address + growth;
                        symbols[instruction.immediate.name] = symbol;
                    }

                    instructions[i] = instruction;

                    //Print($"{instruction.opcode} [{String.Join(", ", instruction.operands)}] {instruction.immediate.word.Uint} [{(instruction.additionalInstructions == null ? "" : String.Join(", ", instruction.additionalInstructions))}]");
                }

                List<KeyValuePair<string, Symbol>> changes = new List<KeyValuePair<string, Symbol>>();

                foreach(var pair in symbols) {
                    Symbol symbol = pair.Value;

                    if(symbol.type == Symbol.Type.LABEL) {
                        int addr = instructions[symbol.labelInstructionIndex].address;

                        if(symbol.var.val32.Int != addr) {
                            symbol.var.val32.Int = addr;
                            changes.Add(new KeyValuePair<string, Symbol>(pair.Key, symbol));
                        }
                    }
                }

                foreach(var pair in changes) {
                    symbols[pair.Key] = pair.Value;
                }

                foreach(var pair in isrs) {
                    Symbol target = pair.Key;
                    Symbol replacement = pair.Value;
                    Instruction targetInstruction = instructions[target.labelInstructionIndex];
                    Instruction replacementInstruction = instructions[replacement.labelInstructionIndex];

                    if(targetInstruction.additionalInstructions != null) {
                        errors.Add($"isr \"{target.name}\" is broken, stub address is too large");
                        return false;
                    } else if(replacementInstruction.address >= GetMaxImmediateValue(1)) {
                        errors.Add($"isr \"{replacement.name}\" address is too large");
                        return false;
                    }

                    targetInstruction.immediate.var.val32.Int = replacementInstruction.address;
                    instructions[target.labelInstructionIndex] = targetInstruction;
                }

                return true;
            }

            bool Parse() {
                AllocateRegisters();
                numInstructions = 0;

                for(int i = 0; i < statements.Count; i++) {
                    var statement = statements[i];

                    if(statement.tokens[0].type == Token.Type.LABEL) {
                        if(symbols.ContainsKey(statement.tokens[0].stringValue)) {
                            AddError(statement.lineNum, $"redefinition of identifier \"{statement.tokens[0].stringValue}\"");
                            return false;
                        } else {
                            symbols.Add(statement.tokens[0].stringValue, new Symbol {
                                name = statement.tokens[0].stringValue,
                                var = new Variable {
                                    type = Variable.Type.NONE
                                },
                                labelInstructionIndex = numInstructions,
                                type = Symbol.Type.LABEL
                            });
                        }
                    } else if(statement.tokens[0].type == Token.Type.INSTRUCTION) {
                        numInstructions++;
                    }
                }

                for(int i = 0; i < statements.Count; i++) {
                    var statement = statements[i];

                    if(statement.tokens[0].type == Token.Type.DIRECTIVE) {
                        if(statement.tokens[0].stringValue == "const" || statement.tokens[0].stringValue == "word") {
                            if(statement.tokens.Length != 3 || statement.tokens[1].type != Token.Type.IDENTIFIER || (statement.tokens[2].type != Token.Type.INTEGER && statement.tokens[2].type != Token.Type.FLOAT)) {
                                AddError(statement.lineNum, $"invalid directive");
                                return false;
                            }

                            if(symbols.ContainsKey(statement.tokens[1].stringValue)) {
                                AddError(statement.lineNum, $"redefinition of identifier \"{statement.tokens[1].stringValue}\"");
                                return false;
                            } else {
                                if(statement.tokens[0].stringValue == "const") {
                                    symbols.Add(statement.tokens[1].stringValue, new Symbol {
                                        name = statement.tokens[1].stringValue,
                                        var = statement.tokens[2].var,
                                        type = Symbol.Type.CONSTANT
                                    });
                                } else {
                                    int addr = programData.Count;
                                    AddData(statement.tokens[2].var, 4);

                                    symbols.Add(statement.tokens[1].stringValue, new Symbol {
                                        name = statement.tokens[1].stringValue,
                                        var = new Variable{ val32 = new CPU.Value32{ Int = addr }},
                                        type = Symbol.Type.CONSTANT
                                    });
                                }
                            }
                        } else if(statement.tokens[0].stringValue == "isr") {
                            if(statement.tokens.Length != 3 || statement.tokens[1].type != Token.Type.IDENTIFIER ||statement.tokens[2].type != Token.Type.IDENTIFIER) {
                                AddError(statement.lineNum, $"invalid directive");
                                return false;
                            }

                            Symbol target;
                            Symbol replacement;

                            if(!symbols.TryGetValue(statement.tokens[1].stringValue, out target)) {
                                AddError(statement.lineNum, $"invalid isr directive, no symbol \"{statement.tokens[1].stringValue}\"");
                                return false;
                            } else if(target.type != Symbol.Type.LABEL) {
                                AddError(statement.lineNum, $"invalid isr directive, symbol \"{statement.tokens[1].stringValue}\" is not a label");
                                return false;
                            }

                            if(!symbols.TryGetValue(statement.tokens[2].stringValue, out replacement)) {
                                AddError(statement.lineNum, $"invalid isr directive, no symbol \"{statement.tokens[2].stringValue}\"");
                                return false;
                            } else if(replacement.type != Symbol.Type.LABEL) {
                                AddError(statement.lineNum, $"invalid isr directive, symbol \"{statement.tokens[2].stringValue}\" is not a label");
                                return false;
                            }

                            isrs.Add(new KeyValuePair<Symbol, Symbol>(target, replacement));
                        } else {
                            AddError(statement.lineNum, $"unknown directive \"{statement.tokens[0].stringValue}\"");
                            return false;
                        }
                    } else if(statement.tokens[0].type == Token.Type.INSTRUCTION) {
                        var instruction = new Instruction {
                            opcode = 0,
                            cond = 0,
                            operands = new List<CPU.Register>(),
                            address = 0,
                            additionalInstructions = null,
                            immediate = new Symbol {
                                var = new Variable{ type = Variable.Type.NONE },
                                type = Symbol.Type.NONE
                            }
                        };

                        if(!TryStringToOpcode(statement.tokens[0].stringValue, out instruction.opcode)) {
                            AddError(statement.lineNum, $"unknown opcode \"{statement.tokens[0].stringValue}\"");
                            return false;
                        }

                        if(!TryStringToCond(statement.tokens[0].cond, out instruction.cond)) {
                            AddError(statement.lineNum, $"unknown condition \"{statement.tokens[0].cond}\"");
                            return false;
                        }

                        for(int j = 1; j < statement.tokens.Length; j++) {
                            if(statement.tokens[j].type == Token.Type.IDENTIFIER) {
                                Symbol symbol;

                                if(!symbols.TryGetValue(statement.tokens[j].stringValue, out symbol)) {
                                    AddError(statement.lineNum, $"unknown identifier \"{statement.tokens[j].stringValue}\"");
                                    return false;
                                }

                                if(symbol.type == Symbol.Type.REGISTER) {
                                    instruction.operands.Add((CPU.Register)symbol.var.val32.Uint);
                                } else {
                                    instruction.immediate = symbol;
                                }
                            } else if(statement.tokens[j].type == Token.Type.INTEGER || statement.tokens[j].type == Token.Type.FLOAT) {
                                instruction.immediate = new Symbol {
                                    var = statement.tokens[j].var,
                                    type = Symbol.Type.LITERAL
                                };
                            } else {
                                AddError(statement.lineNum, $"invalid instruction argument \"{statement.tokens[j].stringValue}\"");
                                return false;
                            }
                        }

                        instructions.Add(instruction);
                    }
                }

                return true;
            }

            static bool TryParseIntegerLiteral(string str, out int value) {
                Match decimalMatch = Regex.Match(str, @"^[+-]?[0-9]+$");
                Match hexMatch = Regex.Match(str, @"^([+-])?0x([0-9a-zA-Z]+)$");
                Match binMatch = Regex.Match(str, @"^([+-])?0b([01]+)$");

                if(decimalMatch.Success) {
                    value = Convert.ToInt32(decimalMatch.Groups[0].ToString(), 10);
                    return true;
                } else if(hexMatch.Success) {
                    str = hexMatch.Groups[0].ToString();
                    value = Convert.ToInt32(hexMatch.Groups[2].ToString(), 16);

                    if(hexMatch.Groups[1].ToString() == "-") {
                        value *= -1;
                    }

                    return true;
                } else if(binMatch.Success) {
                    str = binMatch.Groups[0].ToString();
                    value = Convert.ToInt32(binMatch.Groups[2].ToString(), 2);

                    if(binMatch.Groups[1].ToString() == "-") {
                        value *= -1;
                    }

                    return true;
                }

                value = 0;
                return false;
            }

            bool Tokenize() {       
                for(int i = 0; i < statements.Count; i++) {
                    var statement = statements[i];

                    for(int j = 0; j < statement.line.Length; j++) {
                        string arg = statement.line[j];

                        statement.tokens[j].offset = 0;
                        statement.tokens[j].type = Token.Type.NONE;

                        Match directiveMatch = Regex.Match(arg, @"^\.([a-zA-Z_][a-zA-Z0-9_]*)$");
                        Match labelMatch = Regex.Match(arg, @"^([a-zA-Z_][a-zA-Z0-9_]*):$");
                        Match identifierMatch = Regex.Match(arg, @"^[a-zA-Z_][a-zA-Z0-9_]*$");
                        Match instructionMatch = Regex.Match(arg, @"^([a-zA-Z_][a-zA-Z0-9_]*)\.(al|eq|ne|gt|ge|lt|le)$");
                        Match floatMatch = Regex.Match(arg, @"^[+-]?[0-9]?[\.][0-9]*$");

                        if(directiveMatch.Success) {
                            statement.tokens[j].type = Token.Type.DIRECTIVE;
                            statement.tokens[j].stringValue = directiveMatch.Groups[1].ToString();
                        } else if(labelMatch.Success) {
                            statement.tokens[j].type = Token.Type.LABEL;
                            statement.tokens[j].stringValue = labelMatch.Groups[1].ToString();
                        } else if(identifierMatch.Success) {
                            statement.tokens[j].type = (j == 0) ? Token.Type.INSTRUCTION : Token.Type.IDENTIFIER;
                            statement.tokens[j].stringValue = identifierMatch.Groups[0].ToString();

                            if(statement.tokens[j].type == Token.Type.INSTRUCTION) {
                                statement.tokens[j].cond = "al";
                            }
                        } else if(instructionMatch.Success) {
                            statement.tokens[j].type = Token.Type.INSTRUCTION;
                            statement.tokens[j].stringValue = instructionMatch.Groups[1].ToString();
                            statement.tokens[j].cond = instructionMatch.Groups[2].ToString();
                        } else if(floatMatch.Success && floatMatch.Groups[0].ToString() != ".") {
                            statement.tokens[j].type = Token.Type.FLOAT;
                            statement.tokens[j].stringValue = floatMatch.Groups[0].ToString();
                            float.TryParse(statement.tokens[j].stringValue, out statement.tokens[j].var.val32.Float);
                            statement.tokens[j].var.type = Variable.Type.FLOAT;
                        } else if(TryParseIntegerLiteral(arg, out statement.tokens[j].var.val32.Int)) {
                            statement.tokens[j].type = Token.Type.INTEGER;
                            statement.tokens[j].stringValue = arg;
                            statement.tokens[j].var.type = Variable.Type.INT;
                        }
                    }
                }

                return true;
            }

            bool Preprocess(string code) {
                var lines = code.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None);
            
                for(int i = 0; i < lines.Length; i++) {
                    lines[i] = code = Regex.Replace(lines[i].Trim() ,@"\s+"," ");

                    if(lines[i].Length > 0) {
                        if(lines[i][0] == '#') {
                            continue;
                        }

                        var args = lines[i].Split(' ');

                        if(args.Length > 0) {
                            statements.Add(new Statement {
                                lineNum = i,
                                line = args,
                                tokens = new Token[args.Length]
                            });
                        }
                    }
                }

                return true;
            }
        }

        class CPU {
            public uint[] registers = new uint[64];
            public byte[] memory = null;
            public uint[] instructions = null;
            public uint flags = (uint)Flag.INTERRUPTS_ENABLED;
            Queue<uint> pendingInterrupts = new Queue<uint>();
            public IPeripheral peripheral = null;
            static int maxPendingInterrupts = 32;
            System.Random random = new System.Random();
            public uint pc = 0;
            public uint peripheralBase = 0;
            Status savedStatus;

            public CPU(IPeripheral peripheral, uint peripheralBase) {
                this.peripheral = peripheral;
                this.peripheralBase = peripheralBase;
            }

            public void Reset() {
                registers = new uint[64];
                memory = null;
                instructions = null;
                flags = (uint)Flag.INTERRUPTS_ENABLED;
                pendingInterrupts.Clear();
            }

            public interface IPeripheral {
                Value32 Read(uint addr);
                void Write(uint addr, Value32 value);
            }

            [Flags]
            public enum Flag : uint {
                INTERRUPTS_ENABLED = 1 << 0,
                EQUAL = 1 << 1,
                GREATER_THAN = 1 << 2,
                LESS_THAN = 1 << 3,
                READY = 1 << 4
            }

            [StructLayout(LayoutKind.Explicit)]
            public struct Value32 {
                [FieldOffset(0)]
                public int Int;
                [FieldOffset(0)]
                public uint Uint;
                [FieldOffset(0)]
                public float Float;
                [FieldOffset(0)]
                public byte byte0;
                [FieldOffset(1)]
                public byte byte1;
                [FieldOffset(2)]
                public byte byte2;
                [FieldOffset(3)]
                public byte byte3;
            }

            public enum Instruction {
                COND_SHIFT = 29,
                COND_MASK = 7 << (int)COND_SHIFT,
                OPCODE_SHIFT = 23,
                OPCODE_MASK = 0x3F << (int)OPCODE_SHIFT,
                OP1_FLAG_SHIFT = 22,
                OP1_FLAG_MASK = 1 << (int)OP1_FLAG_SHIFT,
                OP1_SHIFT = 16,
                OP1_MASK = 0x3F << (int)OP1_SHIFT,
                OP2_FLAG_SHIFT = 15,
                OP2_FLAG_MASK = 1 << (int)OP2_FLAG_SHIFT,
                OP2_SHIFT = 9,
                OP2_MASK = 0x3F << (int)OP2_SHIFT,
                OP3_FLAG_SHIFT = 8,
                OP3_FLAG_MASK = 1 << (int)OP3_FLAG_SHIFT,
                OP3_SHIFT = 2,
                OP3_MASK = 0x3F << (int)OP3_SHIFT,
                IMM1_SHIFT = 0,
                IMM1_MASK = 0x3FFFFF << (int)IMM1_SHIFT,
                IMM2_SHIFT = 0,
                IMM2_MASK = 0x7FFF << (int)IMM2_SHIFT,
                IMM3_SHIFT = 0,
                IMM3_MASK = 0xFF << (int)IMM3_SHIFT,
                IMM4_SHIFT = 0,
                IMM4_MASK = 0x3 << (int)IMM4_SHIFT
            }
        
            public enum Cond {
                AL, EQ, NE, GT, GE, LT, LE, RESERVED
            }

            public enum Register {
                R0, R1, R2,  R3,  R4,  R5,  R6,  R7,
                R8, R9, R10, R11, R12, R13, R14, R15,
                SP, BP
            }

            public enum Opcode {
                RET, CLI, SEI,
                JMP, JNE, CALL, PUSH, POP,
                MOV, LDR, LDRB, STR, STRB, CMPI, CMPU,
                SHRS, SHRU, SHL, AND, OR, XOR, NOT, ADD, SUB, MUL, DIV, MOD,
                ITOF, FTOI, CMPF, ADDF, SUBF, MULF, DIVF, MODF,
                RESERVED = 0x3F
            }

            public enum Status {
                SUCCESS,
                OUT_OF_INSTRUCTIONS,
                MISSING_INSTRUCTION,
                BAD_INSTRUCTION,
                SEGFAULT,
                DIVISION_BY_ZERO,
                UNDEFINED
            }

            public bool Interrupt(uint addr) {
                if((flags & (uint)Flag.READY) == 0) {
                    return false;
                }

                if(pendingInterrupts.Count >= maxPendingInterrupts) {
                    return false;
                }

                pendingInterrupts.Enqueue(addr);
                return true;
            }

            void AssignMemory(uint addr, Value32 val) {
                //Print($"AssignMemory(uint addr: {addr}, uint value: {value})");

                if(peripheral != null && addr >= peripheralBase) {
                    peripheral.Write(addr, val);
                    return;
                } else if(addr >= memory.Length) {
                    savedStatus = Status.SEGFAULT;
                    return;
                }

                memory[addr + 0] = val.byte0;
                memory[addr + 1] = val.byte1;
                memory[addr + 2] = val.byte2;
                memory[addr + 3] = val.byte3;
            }

            void AssignMemory(uint addr, uint val) {
                AssignMemory(addr, new Value32 { Uint = val });
            }

            Value32 ReadMemory(uint addr) {
                if(peripheral != null && addr >= peripheralBase) {
                    return peripheral.Read(addr);
                } else if(addr >= memory.Length) {
                    savedStatus = Status.SEGFAULT;
                    return new Value32 { Uint = 0 };
                }

                Value32 val = new Value32 {
                    byte0 = memory[addr + 0],
                    byte1 = memory[addr + 1],
                    byte2 = memory[addr + 2],
                    byte3 = memory[addr + 3]
                };

                //Print($"ReadMemory(uint addr: {addr}) -> uint: {val.Uint}");
                return val;
            }

            public bool Cycle(out Status status, int numCycles = 1) {
                savedStatus = Status.UNDEFINED;

                for(int i = 0; i < numCycles; i++) {
                    if(savedStatus != Status.UNDEFINED) {
                        status = savedStatus;
                        return false;
                    }

                    if((flags & (uint)Flag.INTERRUPTS_ENABLED) != 0 && pendingInterrupts.Count != 0) {
                        uint addr = pendingInterrupts.Dequeue();

                        AssignMemory(registers[(int)Register.SP], pc);
                        registers[(int)Register.SP] += 4;
                        pc = addr;

                        if(savedStatus != Status.UNDEFINED) {
                            status = savedStatus;
                            return false;
                        }
                    }

                    if(pc >= instructions.Length) {
                        status = Status.OUT_OF_INSTRUCTIONS;
                        return false;
                    }

                    uint inst = instructions[pc++];
                    Opcode opcode = (Opcode)((inst & (uint)Instruction.OPCODE_MASK) >> (int)Instruction.OPCODE_SHIFT);
                    Cond cond = (Cond)((inst & (int)Instruction.COND_MASK) >> (int)Instruction.COND_SHIFT);

                    /*PrintVar(nameof(pc), pc);
                    Print($"instruction: {opcode}.{cond}");
                    Print($"instruction bits: {Convert.ToString(inst, 2).PadLeft(32, '0')}");
                    Print($"flags: {Convert.ToString(flags & (uint)Flag.EQUAL, 2).PadLeft(32, '0')}");*/

                    switch(cond) {
                        case Cond.EQ:
                            if((flags & (uint)Flag.EQUAL) != 0) {
                                break;
                            }

                            continue;
                        case Cond.NE:
                            if((flags & (uint)Flag.EQUAL) == 0) {
                                break;
                            }

                            continue;
                        case Cond.GT:
                            if((flags & (uint)Flag.GREATER_THAN) != 0) {
                                break;
                            }

                            continue;
                        case Cond.LT:
                            if((flags & (uint)Flag.LESS_THAN) != 0) {
                                break;
                            }

                            continue;
                        case Cond.GE:
                            if((flags & (uint)(Flag.GREATER_THAN | Flag.EQUAL)) != 0) {
                                break;
                            }

                            continue;
                        case Cond.LE:
                            if((flags & (uint)(Flag.LESS_THAN | Flag.EQUAL)) != 0) {
                                break;
                            }

                            continue;
                    }

                    bool handledHere = true;

                    // zero arg instructions
                    switch(opcode) {
                        case Opcode.RET:
                            registers[(int)Register.SP] -= 4;
                            pc = ReadMemory(registers[(int)Register.SP]).Uint;
                            break;
                        case Opcode.CLI:
                            flags &= ~(uint)Flag.INTERRUPTS_ENABLED;
                            break;
                        case Opcode.SEI:
                            flags |= (uint)Flag.INTERRUPTS_ENABLED;
                            break;
                        default:
                            handledHere = false;
                            break;
                    }

                    if(handledHere) {
                        if(savedStatus == Status.UNDEFINED) {
                            status = Status.SUCCESS;
                            continue;
                        } else {
                            status = savedStatus;
                            return false;
                        }
                    }

                    uint op1 = (inst & (uint)Instruction.OP1_MASK) >> (int)Instruction.OP1_SHIFT;
                    uint op1Flag = inst & (uint)Instruction.OP1_FLAG_MASK;
                    uint imm1 = (inst & (uint)Instruction.IMM1_MASK) >> (int)Instruction.IMM1_SHIFT;

                    if(op1Flag == 0 && imm1 == (uint)Instruction.IMM1_MASK) {
                        if(pc >= instructions.Length) {
                            status = Status.OUT_OF_INSTRUCTIONS;
                            return false;
                        }

                        imm1 = instructions[pc++];
                    }

                    uint arg1 = (op1Flag != 0) ? registers[op1] : imm1;
                    // just testing this, if it's not slower, arg1 will just be a Value32
                    Value32 arg1v = new Value32 { Uint = arg1 };
                    handledHere = true;

                    /*PrintVar(nameof(op1), op1);
                    PrintVar(nameof(op1Flag), op1Flag);
                    PrintVar(nameof(imm1), imm1);
                    PrintVar(nameof(arg1), arg1);*/

                    // one arg instructions
                    switch(opcode) {
                        case Opcode.JMP:
                            pc = arg1;
                            break;
                        case Opcode.CALL:
                            AssignMemory(registers[(int)Register.SP], pc);
                            registers[(int)Register.SP] += 4;
                            pc = arg1;
                            break;
                        case Opcode.PUSH:
                            AssignMemory(registers[(int)Register.SP], arg1);
                            registers[(int)Register.SP] += 4;
                            break;
                        case Opcode.POP:
                            registers[(int)Register.SP] -= 4;
                            registers[op1] = ReadMemory(registers[(int)Register.SP]).Uint;
                            break;
                        case Opcode.ITOF:
                            var itof = new Value32 { Uint = registers[op1] };
                            itof.Float = (float)itof.Int;
                            registers[op1] = itof.Uint;
                            break;
                        case Opcode.FTOI:
                            var ftoi = new Value32 { Uint = registers[op1] };
                            ftoi.Int = (int)ftoi.Float;
                            registers[op1] = ftoi.Uint;
                            break;
                        default:
                            handledHere = false;
                            break;
                    }

                    if(handledHere) {
                        continue;
                    }

                    uint op2 = (inst & (uint)Instruction.OP2_MASK) >> (int)Instruction.OP2_SHIFT;
                    uint op2Flag = inst & (uint)Instruction.OP2_FLAG_MASK;
                    uint imm2 = (inst & (uint)Instruction.IMM2_MASK) >> (int)Instruction.IMM2_SHIFT;

                    if(op2Flag == 0 && imm2 == (uint)Instruction.IMM2_MASK) {
                        if(pc >= instructions.Length) {
                            status = Status.OUT_OF_INSTRUCTIONS;
                            return false;
                        }

                        imm2 = instructions[pc++];
                    }

                    uint arg2 = (op2Flag != 0) ? registers[op2] : imm2;
                    Value32 arg2v = new Value32 { Uint = arg2 };
                    handledHere = true;

                    /*PrintVar(nameof(op2), op2);
                    PrintVar(nameof(op2Flag), op2Flag);
                    PrintVar(nameof(imm2), imm2);
                    PrintVar(nameof(arg2), arg2);*/

                    // two arg instructions
                    switch(opcode) {
                        case Opcode.MOV:
                            registers[op1] = arg2;
                            break;
                        case Opcode.LDR:
                            registers[op1] = ReadMemory(arg2).Uint;
                            break;
                        case Opcode.STR:
                            AssignMemory(arg2, arg1);
                            break;
                        case Opcode.CMPI:
                            flags = ((int)arg1 == (int)arg2) ? flags | (uint)Flag.EQUAL : flags & ~(uint)Flag.EQUAL;
                            flags = ((int)arg1 > (int)arg2) ? flags | (uint)Flag.GREATER_THAN : flags & ~(uint)Flag.GREATER_THAN;
                            flags = ((int)arg1 < (int)arg2) ? flags | (uint)Flag.LESS_THAN : flags & ~(uint)Flag.LESS_THAN;
                            break;
                        case Opcode.CMPU:
                            flags = (arg1 == arg2) ? flags | (uint)Flag.EQUAL : flags & ~(uint)Flag.EQUAL;
                            flags = (arg1 > arg2) ? flags | (uint)Flag.GREATER_THAN : flags & ~(uint)Flag.GREATER_THAN;
                            flags = (arg1 < arg2) ? flags | (uint)Flag.LESS_THAN : flags & ~(uint)Flag.LESS_THAN;
                            break;
                        case Opcode.CMPF:
                            flags = (arg1v.Float == arg2v.Float) ? flags | (uint)Flag.EQUAL : flags & ~(uint)Flag.EQUAL;
                            flags = (arg1v.Float > arg2v.Float) ? flags | (uint)Flag.GREATER_THAN : flags & ~(uint)Flag.GREATER_THAN;
                            flags = (arg1v.Float < arg2v.Float) ? flags | (uint)Flag.LESS_THAN : flags & ~(uint)Flag.LESS_THAN;
                            break;
                        default:
                            handledHere = false;
                            break;
                    }

                    if(handledHere) {
                        continue;
                    }

                    uint op3 = (inst & (uint)Instruction.OP3_MASK) >> (int)Instruction.OP3_SHIFT; 
                    uint op3Flag = inst & (uint)Instruction.OP3_FLAG_MASK;
                    uint imm3 = (inst & (uint)Instruction.IMM3_MASK) >> (int)Instruction.IMM3_SHIFT;

                    if(op3Flag == 0 && imm3 == (uint)Instruction.IMM3_MASK) {
                        if(pc >= instructions.Length) {
                            status = Status.OUT_OF_INSTRUCTIONS;
                            return false;
                        }

                        imm3 = instructions[pc++];
                    }

                    uint arg3 = (op3Flag != 0) ? registers[op3] : imm3;
                    Value32 arg3v = new Value32 { Uint = arg3 };
                    handledHere = true;

                    /*PrintVar(nameof(op3), op3);
                    PrintVar(nameof(op3Flag), op3Flag);
                    PrintVar(nameof(imm3), imm3);
                    PrintVar(nameof(arg3), arg3);*/

                    // three arg instructions
                    switch(opcode) {
                        case Opcode.SHRS:
                            registers[op1] = (uint)((int)arg2 >> (int)arg3);
                            break;
                        case Opcode.SHRU:
                            registers[op1] = arg2 >> (int)arg3;
                            break;
                        case Opcode.SHL:
                            registers[op1] = arg2 << (int)arg3;
                            break;
                        case Opcode.AND:
                            registers[op1] = arg2 & arg3;
                            break;
                        case Opcode.OR:
                            registers[op1] = arg2 | arg3;
                            break;
                        case Opcode.XOR:
                            registers[op1] = arg2 ^ arg3;
                            break;
                        case Opcode.NOT:
                            registers[op1] = ~arg2;
                            break;
                        case Opcode.ADD:
                            registers[op1] = arg2 + arg3;
                            break;
                        case Opcode.SUB:
                            registers[op1] = arg2 - arg3;
                            break;
                        case Opcode.MUL:
                            registers[op1] = arg2 * arg3;
                            break;
                        case Opcode.DIV:
                            if(arg2 == 0) {
                                status = Status.DIVISION_BY_ZERO;
                                return false;
                            }

                            registers[op1] = arg2 / arg3;
                            break;
                        case Opcode.MOD:
                            if(arg2 == 0) {
                                status = Status.DIVISION_BY_ZERO;
                                return false;
                            }

                            registers[op1] = arg2 % arg3;
                            break;
                        case Opcode.ADDF:
                            registers[op1] = new Value32 { Float = arg2v.Float + arg3v.Float }.Uint;
                            break;
                        case Opcode.SUBF:
                            registers[op1] = new Value32 { Float = arg2v.Float - arg3v.Float }.Uint;
                            break;
                        case Opcode.MULF:
                            registers[op1] = new Value32 { Float = arg2v.Float * arg3v.Float }.Uint;
                            break;
                        case Opcode.DIVF:
                            if(arg2v.Float == 0) {
                                status = Status.DIVISION_BY_ZERO;
                                return false;
                            }

                           
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху