public ProgramInfo(string[] lines, CheatSheet cheat_sheet) { RegisterOwnerNode[] reg_to_inst = new RegisterOwnerNode[128]; Regex opcode_operand_comment_splitter = new Regex(@"([A-Za-z]+)\s+([^;]*)(;*)"); Regex label_rest_of_line_splitter = new Regex(@"^([a-zA-Z_\.][a-zA-Z0-9_]*):(.*)"); // matches ".macro_begin SomeMacro var1, var2 ,var3 , var4" //Regex macro_arg_splitter = new Regex //( // @"^\b*\.macro_begin\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*))*" //); Regex register_extractor = new Regex(@"\$(\d+)"); m_log = ""; int lines_of_actual_code = 0; // todo: stop overusing trim for (int line_num = 0; line_num < lines.Length; ++line_num) { string current_line = lines[line_num].Trim(); // early out to skip whole line comments if (current_line == "" || current_line[0] == ';') { continue; } Match match; // macro processing string macro_begin_token = ".macro_begin"; if (current_line.StartsWith(macro_begin_token)) { current_line = current_line.Substring(macro_begin_token.Length); string[] args = current_line.Split(new char[] {',',' '}, StringSplitOptions.RemoveEmptyEntries); int num_args = args.Length; if (num_args <= 2) { ErrorMacroWrongNumArgs(line_num); return; } int macro_start_line = line_num; string macro_name = args[0]; while (current_line.StartsWith(".macro_end") == false && line_num < lines.Length) { current_line = lines[line_num++].Trim(); } // either we found the macro end or we have an unterminated macro if (current_line.StartsWith(".macro_end")) { --line_num; continue; } else { ErrorUnterminatedMacro(macro_start_line, macro_name); return; } } // deal with labels alone on a line and labels with code after them match = label_rest_of_line_splitter.Match(current_line); if (match.Success) { string lab_to_add = match.Groups[1].Value; // disallow duplicate labels if (m_label_table.ContainsKey(lab_to_add)) { ErrorLabelDuplicate(line_num, lab_to_add); return; } // add label to table, and see if there was anything after the label m_label_table.Add(lab_to_add, lines_of_actual_code); // set the current line to everything after the label and continue current_line = match.Groups[2].Value.Trim(); // if this is a label by itself, continue. if (current_line == "" || current_line[0] == ';') { continue; } } match = opcode_operand_comment_splitter.Match(current_line); if (match.Success) { string opcode = match.Groups[1].Value; // make sure the opcode exists Program.Instruction inst; if (!cheat_sheet.LookupInstruction(opcode, out inst)) { ErrorInvalidOpcode(line_num, opcode); return; } InstructionNode dependency_node = new InstructionNode(opcode, inst); dependency_node.m_original_inst_num = m_original_program.Count; m_original_program.Add(dependency_node); ++m_instruction_cnt[inst.m_pipeline == Program.Pipe.EVEN? 0 : 1]; // dont allow jumps in the middle of the code. Todo: check branch target as well if (dependency_node.IsBranch(cheat_sheet) && line_num < lines.Length - 1) { ErrorInvalidBranchSlot(line_num); return; } else if (dependency_node.IsBranch(cheat_sheet) == false && line_num == lines.Length - 1) { ErrorLastInstructionNotBranch(line_num); return; } // don't do anything for opcodes that dont require operands int num_expected_operands = inst.GetNumExpectedOperands(); if (num_expected_operands > 0) { // group 2 must be opcodes. If it starts with a semicolon, we are missing the proper operands if (match.Groups[2].Value.Length < 1 || match.Groups[2].Value[0] == ';' || match.Groups[2].Value == "") { ErrorNoOperands(line_num, opcode, num_expected_operands); return; } // now the real work begins. Verify the operands are what they should be string operands_match = match.Groups[2].Value; string[] operands = operands_match.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // wrong number of operands if (operands.Length != num_expected_operands) { ErrorWrongNumOperands(line_num, opcode, num_expected_operands, operands.Length); return; } // verify operand types are what they should be for (int o = 0; o < num_expected_operands; ++o) { string trimmed_operand = operands[o].Trim(); // if the operand is a label to jump to, replace it with the label address if (m_label_table.ContainsKey( trimmed_operand )) { trimmed_operand = m_label_table[trimmed_operand].ToString(); } // original register number int original_reg_num = -1; int operand_offset = -1; bool operand_type_valid = Program.OperandInfo.GetOperandValues(inst.m_operand_types[o], trimmed_operand, out original_reg_num, out operand_offset); if (!operand_type_valid) { ErrorInvalidOperandType(line_num, opcode, o, inst.m_operand_types[o]); return; } if (original_reg_num > 127) { ErrorRegisterGt127(line_num, o); return; } // add this to the dependency tree RegisterDependencyNode operand_dependency_node = new RegisterDependencyNode(inst.m_operand_types[o]); dependency_node.m_reg_info.Add(operand_dependency_node); operand_dependency_node.m_allocated_reg = original_reg_num; operand_dependency_node.m_immediate_arg = operand_offset; //check for input dependencies on other registers (as opposed to literals) if (inst.m_operand_types[o] == Program.OperandInfo.OperandType.INPUT_REG || inst.m_operand_types[o] == Program.OperandInfo.OperandType.BASE_REG_AND_SIGNED_LITERAL_OFFSET || inst.m_operand_types[o] == Program.OperandInfo.OperandType.BASE_REG_AND_SIGNED_LITERAL_OFFSET) { if (original_reg_num >= 0 && reg_to_inst[original_reg_num] != null) { // if this input register was written to by a previous instruction, then we depend on that instruction //is_root_node = false; reg_to_inst[original_reg_num].m_times_used++; operand_dependency_node.m_dependency = new DepPair(reg_to_inst[original_reg_num].m_owner, 0); m_original_program[reg_to_inst[original_reg_num].m_owner].m_reg_info[0].m_dependents.Add(new DepPair(lines_of_actual_code, o)); } } } // register ownership and removal of unused instructions if (inst.m_operand_types[0] == Program.OperandInfo.OperandType.OUTPUT_REG) { string trimmed_operand = operands[0].Trim(); int reg_num = -1; match = register_extractor.Match(trimmed_operand); if (match.Success && int.TryParse(match.Groups[1].Value, out reg_num)) { // look at the previous register owner. If between when it wrote its value and now the register is // never used, this is probably an unused instruction and should be removed! if (reg_to_inst[reg_num] != null && reg_to_inst[reg_num].m_times_used == 0) { // mark this and remove it later, making sure to fix up the odd/even inst count, // labels, indices, pointers, and who knows what else needs to be fixed :) int unused_inst_line = reg_to_inst[reg_num].m_owner; EliminateUnusedInstructionChain(unused_inst_line, reg_to_inst); } // assign register ownership reg_to_inst[reg_num] = new RegisterOwnerNode(lines_of_actual_code); } } } ++lines_of_actual_code; } else { m_program_status.m_error_line = line_num; m_program_status.m_error_msg = "Parse error. Line: " + line_num.ToString(); m_program_status.m_status = ProgramStatus.Status.STATUS_PARSE_ERROR; return; } } // lock the final owners of all regs for (int r = 0; r < 128; ++r) { if (reg_to_inst[r] != null && reg_to_inst[r].m_owner != -1 && m_original_program[reg_to_inst[r].m_owner].m_include_in_schedule == true) { m_original_program[reg_to_inst[r].m_owner].m_reg_info[0].m_is_locked = true; } } }