Ejemplo n.º 1
0
        public static List<BFOpcode> ParseCodeblock(uint[] data, out bool isExtendedOpcodePresent)
        {
            isExtendedOpcodePresent = false;
            List<BFOpcode> opcodes = new List<BFOpcode>(2 << 12);

            for (int i = 0; i < data.Length; i++)
            {
                BFInstruction instruction = (BFInstruction)(data[i] & 0x0000FFFF);
                BFOpcode op = null;

                switch (instruction)
                {
                    case BFInstruction.PushUInt32:
                        {
                            if (!isExtendedOpcodePresent) // optimization, keep track of whether or not there are extended opcodes for sorting later
                                isExtendedOpcodePresent = true;

                            Debug.Assert((data[i] & 0xFFFF0000) == 0); // debug: check if the high bits are indeed zeroed out

                            op = new BFOpcode(instruction, i, data[++i]); // consume next int for op
                        }
                        break;
                    case BFInstruction.PushFloat:
                        {
                            if (!isExtendedOpcodePresent) // optimization, keep track of whether or not there are extended opcodes for sorting later
                                isExtendedOpcodePresent = true;

                            Debug.Assert((data[i] & 0xFFFF0000) == 0); // debug: check if the high bits are indeed zeroed out

                            op = new BFOpcode(instruction, i, BitConverter.ToSingle(BitConverter.GetBytes(data[++i]), 0)); // consume next int as float for op
                        }
                        break;
                    case BFInstruction.Add:
                    case BFInstruction.NotEqual:
                    case BFInstruction.PushResult:
                    case BFInstruction.Return:
                    case BFInstruction.Subtract:
                    case BFInstruction.Equal:
                        Debug.Assert((data[i] & 0xFFFF0000) == 0); // debug: check if the high bits are indeed zeroed out
                        op = new BFOpcode(instruction, i, null); // uses no operands, high bits are zeroed out
                        break;
                    default:
                        op = new BFOpcode(instruction, i, ((data[i] & 0xFFFF0000) >> 16));
                        break;     
                }

                opcodes.Add(op);
            }

            return opcodes;
        }
Ejemplo n.º 2
0
        public static BFFile AssembleFromASMText(string path)
        {
            using (StreamReader reader = new StreamReader(path))
            {
                // keep track of various indices
                int lineIndex = -1;
                int procIndex = 0;
                int jumpIndex = 0;
                int opIndex = 0;

                // list for fixing up jump references later
                List<Tuple<string, int>> unresolvedJumps = new List<Tuple<string, int>>();
                List<Tuple<string, int>> unresolvedProcs = new List<Tuple<string, int>>();

                // code label and opcode lists
                List<BFCodeLabel> procedures = new List<BFCodeLabel>();
                List<BFCodeLabel> jumps = new List<BFCodeLabel>();
                List<BFOpcode> opcodes = new List<BFOpcode>();

                while (!reader.EndOfStream)
                {
                    lineIndex++;

                    // trim, clean, split line
                    string[] tokens = reader.ReadLine()
                        .Trim()
                        .Replace("\t", string.Empty)
                        .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    // skip if line contains no tokens or starts with a comment
                    if (tokens.Length == 0 || tokens[0] == ";")
                        continue;

                    if (tokens[0].EndsWith(":")) // we got a label
                    {
                        if (tokens[0].StartsWith("@")) // and the label is a jump label identifier
                        {
                            jumps.Add(ParseASMLabel(lineIndex, tokens[0], jumpIndex, opIndex, '@', ':'));
                            jumpIndex++;
                        }
                        else // the label is a procedure identifier
                        {
                            procedures.Add(ParseASMLabel(lineIndex, tokens[0], procIndex, opIndex, ':'));
                            procIndex++;
                        }
                    }
                    else // we got an instruction
                    {
                        // instruction without any procedures, throw an error
                        if (procedures.Count == 0)
                            throw new BFASMParserException("Instruction defined at line {0} before any procedures", lineIndex + 1);

                        BFInstruction instr;
                        BFOpcode op = null;
                        bool isDefined = ASMKeywordToBFInstruction.TryGetValue(tokens[0].ToLowerInvariant(), out instr);

                        if (!isDefined) // 'unk_XX' instruction expected
                        {
                            string opNumStr = tokens[0].Split(new string[] { "unk_" }, StringSplitOptions.RemoveEmptyEntries)[0];
                            int opNumInt;
                            long operand;

                            if (opNumStr.Length < 2 || !int.TryParse(opNumStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out opNumInt) ||
                                !long.TryParse(tokens[1], out operand))
                            {
                                throw new BFASMParserException("unk_XX", lineIndex + 1);
                            }

                            op = new BFOpcode((ushort)opNumInt, 0, operand);
                        }
                        else // regular instruction expected
                        {
                            // verify the number of arguments
                            VerifyArgCount(instr, tokens, lineIndex + 1);

                            switch (instr)
                            {
                                case BFInstruction.BeginProcedure:
                                case BFInstruction.CallNative:
                                case BFInstruction.CallProcedure:
                                case BFInstruction.Jump:
                                case BFInstruction.JumpIfFalse:
                                    string strArg = tokens[1];
                                    int intArg;

                                    if (int.TryParse(strArg, out intArg)) // reference by id
                                    {
                                        op = new BFOpcode(instr, intArg);
                                    }
                                    else // reference by name 
                                    {
                                        op = ParseInstrWithStringLiteralArgs(instr, strArg, lineIndex, opIndex, unresolvedProcs, unresolvedJumps);
                                    }
                                    break;

                                case BFInstruction.PushFloat:
                                    op = ParseInstrWithDecimalLiteralArgs(instr, tokens, lineIndex);
                                    break;

                                case BFInstruction.PushUInt16:
                                case BFInstruction.PushVariable:
                                case BFInstruction.SetVariable:
                                case BFInstruction.PushUInt32:
                                    op = ParseInstrWithNumberLiteralArgs(instr, tokens, lineIndex);
                                    break;

                                default:
                                    op = new BFOpcode(instr, null); // opcode takes no operands, eg add, subtract, return
                                    break;
                            }
                        }

                        // this shouldn't be possible
                        // but they said the same about the world being round
                        if (op == null)
                            throw new BFASMParserException(instr, lineIndex + 1);

                        opcodes.Add(op);
                        opIndex++;
                    }
                }

                // fix up unresolved jump/procedure references
                ResolveLabelReferences(unresolvedJumps, jumps, opcodes);
                ResolveLabelReferences(unresolvedProcs, procedures, opcodes);

                return new BFFile(procedures.ToArray(), jumps.ToArray(), opcodes);
            }
        }