static void Main(string[] args) { System.IO.FileStream f = new System.IO.FileStream("test.s", System.IO.FileMode.Open); Parser p = new Parser(new Scanner(f)); bool res = p.Parse(); if (res == false) throw new Exception("Parse error"); // first loop to convert all operands to lowercase, and // expand all complex instructions List<Statement> pass1 = new List<Statement>(); foreach (Statement s in ((StatementList)p.output).list) pass1.Add(ExpandComplex(s)); // next, add offset information to statements and extract label offsets int cur_offset = 0; string cur_label = ""; Dictionary<string, int> label_offsets = new Dictionary<string, int>(); foreach(Statement s in pass1) { s.offset = cur_offset; cur_offset = s.OffsetAfter(cur_offset); if(s is LineLabel) { LineLabel ll = s as LineLabel; // append local labels to their base name if (ll.name.StartsWith(".")) ll.abs_name = cur_label + ll.name; else { cur_label = ll.name; ll.abs_name = ll.name; } label_offsets[ll.abs_name] = ll.offset; } } // now do the actual encoding List<byte> oput = new List<byte>(); cur_label = ""; foreach(Statement s in pass1) { if(s is Instruction) { Instruction i = s as Instruction; int srca = 0; int srcb = 0; int dest = 0; byte srca_b, srcb_b, dest_b; bool srca_fits, srcb_fits, dest_fits; // Extract data types srca = ExtractData(i.srca, label_offsets, out srca_b, out srca_fits, cur_label); srcb = ExtractData(i.srcb, label_offsets, out srcb_b, out srcb_fits, cur_label); dest = ExtractData(i.dest, label_offsets, out dest_b, out dest_fits, cur_label); if (i.op == "lit") { var bs = BitConverter.GetBytes(srca); for(int idx = 0; idx < 4; idx++) { byte b; if (idx < bs.Length) b = bs[idx]; else b = 0; if (idx == 3) b |= 0x80; oput.Add(b); } } else { if (opcodes.ContainsKey(i.op)) { // special case jump opcodes to always use pc as dest and subtract 4 from srca if(i.op == "jmp" || i.op == "j" || i.op == "jz" || i.op == "jnz" || i.op == "jpos" || i.op == "jneg") { dest_b = (byte)regs["PC"]; if ((srca_b & 0x80) == 0x80) { if (srca_b >= 0xc0 && srca_b <= 0xc3) srca_fits = false; srca_b = (byte)(srca_b - 0x4U); srca_b |= 0x80; } } // special case load/store to use 4 byte length unless already specified if(i.op == "load" || i.op == "store") { if (i.srcb == null) srcb_b = 0x84; } // ensure all byte operands fit if (!srca_fits) throw new Exception("First operand in \'" + i.ToString() + "\' is too large"); if (!srcb_fits) throw new Exception("Second operand in " + i.ToString() + " is too large"); if ((dest_b & 0x80) == 0x80) throw new Exception("Destination in " + i.ToString() + " is not a register"); oput.Add(srca_b); oput.Add(srcb_b); oput.Add(dest_b); oput.Add(opcodes[i.op]); } else throw new Exception("Unknown opcode " + i.ToString()); } } else if(s is DataDirective) { DataDirective dd = s as DataDirective; // Compress string and label members to integers List<int> data = new List<int>(); foreach(var ddi in dd.data) { if (ddi is StringDataItem) { foreach (char c in ((StringDataItem)ddi).val) data.Add((int)c); } else if (ddi is IntegerDataItem) data.Add(((IntegerDataItem)ddi).val); else if(ddi is LabelDataItem) { string label = ((LabelDataItem)ddi).val; if (label_offsets.ContainsKey(label)) data.Add(label_offsets[label]); else throw new Exception("Label " + label + " + not found"); } } // Then output depending on ddtype int b_count = 1; switch (dd.directive) { case DataDirective.DDType.Byte: b_count = 1; break; case DataDirective.DDType.Word: b_count = 2; break; case DataDirective.DDType.DWord: b_count = 4; break; } foreach (int data_i in data) { byte[] data_b = BitConverter.GetBytes(data_i); for (int data_bi = 0; data_bi < b_count; data_bi++) { if (data_bi < data_b.Length) oput.Add(data_b[data_bi]); else oput.Add(0); } } } else if(s is LineLabel) { LineLabel ll = s as LineLabel; if (!ll.name.StartsWith(".")) cur_label = ll.name; } } string oput_bin = "test.bin"; string oput_hex = "C:\\Users\\jncro\\Documents\\fpga\\cpu\\fware.hex"; System.IO.FileStream fs_bin = new System.IO.FileStream(oput_bin, System.IO.FileMode.Create, System.IO.FileAccess.Write); System.IO.BinaryWriter bw_bin = new System.IO.BinaryWriter(fs_bin); foreach (byte b in oput) bw_bin.Write(b); bw_bin.Close(); System.IO.FileStream fs_hex = new System.IO.FileStream(oput_hex, System.IO.FileMode.Create, System.IO.FileAccess.Write); System.IO.StreamWriter sw_hex = new System.IO.StreamWriter(fs_hex, Encoding.ASCII); uint addr = 0; foreach(byte b in oput) { StringBuilder sb = new StringBuilder(); sb.Append(":01"); sb.Append(addr.ToString("X4")); sb.Append("00"); sb.Append(b.ToString("X2")); uint csum = 01 + addr + (addr >> 8) + b; csum &= 0xffU; csum = 0x100U - csum; csum &= 0xffU; sb.Append(csum.ToString("X2")); addr++; sw_hex.WriteLine(sb.ToString()); } sw_hex.WriteLine(":00000001FF"); sw_hex.Close(); }
static void Main(string[] args) { if (ParseArgs(args) == false) { DispUsage(); return; } if(output_file == null) { if (input_file != null) { FileInfo fi = new FileInfo(input_file); output_file = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length) + ".o"; } else output_file = "a.out"; } Stream istream = null; if (input_file == null) { // TODO - read from stdin DispUsage(); return; } else istream = new FileStream(input_file, FileMode.Open); Parser p = new Parser(new Scanner(istream)); bool res = p.Parse(); if (res == false) throw new Exception("Parse error"); // first loop to convert all operands to lowercase, and // expand all complex instructions List<Statement> pass1 = new List<Statement>(); foreach (Statement s in ((StatementList)p.output).list) ExpandComplex(s, pass1); Dictionary<string, LabelOffset> label_offsets; bool changes = false; do { // Reset all section offsets to 0 foreach (var s in sections.Values) { s.cur_offset = 0; s.cur_label = ""; } changes = false; // next, add offset information to statements and extract label offsets cur_section = sections[".text"]; label_offsets = new Dictionary<string, LabelOffset>(); los = label_offsets; foreach (Statement s in pass1) { if (s == null) continue; if (s is SectionHeader) { var sh = s as SectionHeader; cur_section = sections[sh.name]; } s.offset = cur_section.cur_offset; s.section = cur_section; cur_section.cur_offset = s.OffsetAfter(cur_section.cur_offset); if (s is LineLabel) { LineLabel ll = s as LineLabel; ll.abs_name = ll.name; label_offsets[ll.abs_name] = new LabelOffset { Section = cur_section, Offset = ll.offset }; if (global_objs.ContainsKey(ll.name)) global_objs[ll.name] = cur_section; } } // do a pass to evaluate all expressions cur_section = sections[".text"]; foreach (Statement s in pass1) { if (s == null) continue; if (s is SectionHeader) { var sh = s as SectionHeader; cur_section = sections[sh.name]; } if (s is Instruction) { Instruction i = s as Instruction; i.srca = EvaluateOperand(i.srca, label_offsets, cur_section); i.srcb = EvaluateOperand(i.srcb, label_offsets, cur_section); i.dest = EvaluateOperand(i.dest, label_offsets, cur_section); } } /* Identify those instructions of the form jrel(cc) where we can see for definite that the relocation won't fit */ List<Statement> pass2 = new List<Statement>(); foreach (Statement s in pass1) { Instruction i = s as Instruction; if (i != null) { if (i.op == "mov" && (i.dest is RegisterOperand) && (((RegisterOperand)i.dest).val.ToLower() == "pc" || ((RegisterOperand)i.dest).val.ToLower() == "r0") && i.cond != null && i.cond.ctype != Condition.CType.Always) { Relocation r = i.srca as Relocation; if (r != null) { // Do we target a label in this section ? if (r.TargetSection != null && r.TargetSection == i.section) { // Relocation type will eventually be SRCABREL: S + A - P // Calculate its value long r_val = los[r.TargetName].Offset + r.Addend - i.offset; // SRCABREL can fit values from -1024 to + 1023 if (r_val < -1024 || r_val > 1023) { // Replace with LIT val -> R1; mov(cc) R1 -> PC; Instruction lit = new Instruction(); lit.op = "lit"; lit.cond = new Condition { ctype = Condition.CType.Always }; lit.dest = new RegisterOperand { val = "R1" }; lit.srca = r; pass2.Add(lit); Instruction mov = new Instruction(); mov.op = "mov"; mov.cond = i.cond; mov.srca = new RegisterOperand { val = "R1" }; mov.dest = new RegisterOperand { val = "PC" }; pass2.Add(mov); changes = true; continue; } } } } } if (s != null) pass2.Add(s); } pass1 = pass2; } while (changes == true); // now do the actual encoding List<byte> oput; cur_section = sections[".text"]; oput = cur_section.oput; string cur_label = ""; foreach(Statement s in pass1) { if (s is SectionHeader) { var sh = s as SectionHeader; cur_section = sections[sh.name]; oput = cur_section.oput; } if (s is Instruction) { Instruction i = s as Instruction; // ensure dest is either null or a valid register uint dest_idx = 0; bool valid = false; if (i.dest != null) { if (i.dest is RegisterOperand) { RegisterOperand r_dest = i.dest as RegisterOperand; string r_dest_str = r_dest.val.ToUpper(); if (regs.ContainsKey(r_dest_str)) { dest_idx = (uint)regs[r_dest_str]; valid = true; } } } else valid = true; if (dest_idx >= 32) valid = false; if (!valid) throw new Exception("Invalid destination register: " + i.ToString()); uint srca, srcb, srcab, srcbcond, srcabcond, lit, litr1; bool srca_fits, srcb_fits, srcab_fits, srcbcond_fits, srcabcond_fits, lit_fits, litr1_fits; // Extract data types srca = ExtractData(i.srca, label_offsets, out srca_fits, cur_label, i.offset, 6, false); srcb = ExtractData(i.srcb, label_offsets, out srcb_fits, cur_label, i.offset, 6, false); srcab = ExtractData(i.srca, label_offsets, out srcab_fits, cur_label, i.offset, 12, false); srcbcond = ExtractData(i.srcb, label_offsets, out srcbcond_fits, cur_label, i.offset, 11, false); srcabcond = ExtractData(i.srca, label_offsets, out srcabcond_fits, cur_label, i.offset, 17, false); lit = ExtractData(i.srca, label_offsets, out lit_fits, cur_label, i.offset, 25, true); litr1 = ExtractData(i.srca, label_offsets, out litr1_fits, cur_label, i.offset, 31, true); // Extract relocations Relocation srcar = i.srca as Relocation; Relocation srcbr = i.srcb as Relocation; uint oput_val = 0; // encode literals if (i.op == "lit") { if (i.cond.ctype != Condition.CType.Always) throw new Exception("Cannot use conditions on lit: " + i.ToString()); // if dest not specified, assume r1 if (i.dest == null) dest_idx = 1; if (dest_idx != 1 && !lit_fits || !litr1_fits) { throw new Exception("Literal value too large: " + i.ToString()); } if (dest_idx == 1) { oput_val = 0x80000000U | litr1; if (srcar != null) srcar.Type = binary_library.elf.ElfFile.R_JCA_LITR1; } else { oput_val = 0x40000000U | (dest_idx << 25) | lit; if (srcar != null) srcar.Type = binary_library.elf.ElfFile.R_JCA_LIT; } } else { if (opcodes.ContainsKey(i.op)) { // special case jump opcodes to always use pc as dest if (i.op == "jmp" || i.op == "j") { dest_idx = (uint)regs["PC"]; } // special case load/store to use 4 byte length unless already specified if (i.op == "load" || i.op == "store") { if (i.srcb == null) { srcb = 0x24; srcbcond = 0x404; srcb_fits = true; srcbcond_fits = true; } } uint opcode_val = opcodes[i.op]; uint cond_val = Condition.cond_vals[i.cond.ctype]; uint cond_reg_val = (uint)i.cond.reg_no; // if condition is always, srcab and srcb extend // into srcabcond and srcbcond respectively bool has_cond_reg = true; if (i.cond.ctype == Condition.CType.Always) { srcab = srcabcond; srcb = srcbcond; srcab_fits = srcabcond_fits; srcb_fits = srcbcond_fits; has_cond_reg = false; } // special case mov - it uses srcab instead of // srca bool has_srcb = true; if (opcode_val == opcodes["mov"]) { srca = srcab; srca_fits = srcab_fits; has_srcb = false; } // ensure all operands fit if (!srca_fits) throw new Exception("First operand in \'" + i.ToString() + "\' is too large"); if (has_srcb && !srcb_fits) throw new Exception("Second operand in \'" + i.ToString() + "\' is too large"); oput_val = opcode_val << 26 | cond_val << 22 | dest_idx << 17 | srca; if (has_cond_reg) oput_val |= cond_reg_val << 12; if (has_srcb) oput_val |= srcb << 6; if(srcar != null) { if (has_srcb) { srcar.Type = binary_library.elf.ElfFile.R_JCA_SRCA; oput_val |= (1U << 5); } else if (has_cond_reg) { srcar.Type = binary_library.elf.ElfFile.R_JCA_SRCAB; oput_val |= (1U << 11); } else { srcar.Type = binary_library.elf.ElfFile.R_JCA_SRCABCOND; oput_val |= (1U << 16); } if (srcar.IsPCRel) srcar.Type = srcar.Type - binary_library.elf.ElfFile.R_JCA_SRCA + binary_library.elf.ElfFile.R_JCA_SRCAREL; } if(srcbr != null) { if (has_cond_reg) { srcbr.Type = binary_library.elf.ElfFile.R_JCA_SRCB; oput_val |= (1U << 11); } else { srcbr.Type = binary_library.elf.ElfFile.R_JCA_SRCBCOND; oput_val |= (1U << 16); } if (srcbr.IsPCRel) srcbr.Type = srcbr.Type - binary_library.elf.ElfFile.R_JCA_SRCA + binary_library.elf.ElfFile.R_JCA_SRCAREL; } } else throw new Exception("Unknown opcode " + i.ToString()); } var bs = BitConverter.GetBytes(oput_val); foreach (byte b in bs) oput.Add(b); if(srcar != null) { srcar.SourceOffset = i.offset; relocs.Add(srcar); } if(srcbr != null) { srcbr.SourceOffset = i.offset; relocs.Add(srcbr); } } else if(s is DataDirective) { DataDirective dd = s as DataDirective; // Compress string and label members to integers List<int> data = new List<int>(); foreach(var ddi in dd.data) { var e = ddi.Evaluate(new MakeState(label_offsets, cur_section)); switch(e.Type) { case Expression.EvalResult.ResultType.String: foreach (char c in e.strval) data.Add((int)c); break; case Expression.EvalResult.ResultType.Int: data.Add(e.intval); break; default: throw new Exception("Unsupported type in data directive at " + s.ToString()); } } // Then output depending on ddtype int b_count = 1; switch (dd.directive) { case DataDirective.DDType.Byte: b_count = 1; break; case DataDirective.DDType.Word: b_count = 2; break; case DataDirective.DDType.DWord: b_count = 4; break; } foreach (int data_i in data) { byte[] data_b = BitConverter.GetBytes(data_i); for (int data_bi = 0; data_bi < b_count; data_bi++) { if (data_bi < data_b.Length) oput.Add(data_b[data_bi]); else oput.Add(0); } } } else if(s is LineLabel) { LineLabel ll = s as LineLabel; cur_label = ll.name; } } // Generate ELF output var fs_obj = new System.IO.FileStream(output_file, System.IO.FileMode.Create, System.IO.FileAccess.Write); var bw_obj = new System.IO.BinaryWriter(fs_obj); Elf.GenerateELF(bw_obj); bw_obj.Close(); // Generate binary output /* string oput_bin = "test.bin"; string oput_hex = "C:\\Users\\jncro\\Documents\\fpga\\cpu\\fware2.hex"; System.IO.FileStream fs_bin = new System.IO.FileStream(oput_bin, System.IO.FileMode.Create, System.IO.FileAccess.Write); System.IO.BinaryWriter bw_bin = new System.IO.BinaryWriter(fs_bin); foreach (byte b in oput) bw_bin.Write(b); bw_bin.Close();*/ // Generate HEX output /* System.IO.FileStream fs_hex = new System.IO.FileStream(oput_hex, System.IO.FileMode.Create, System.IO.FileAccess.Write); System.IO.StreamWriter sw_hex = new System.IO.StreamWriter(fs_hex, Encoding.ASCII); uint addr = 0; foreach(byte b in oput) { StringBuilder sb = new StringBuilder(); sb.Append(":01"); sb.Append(addr.ToString("X4")); sb.Append("00"); sb.Append(b.ToString("X2")); uint csum = 01 + addr + (addr >> 8) + b; csum &= 0xffU; csum = 0x100U - csum; csum &= 0xffU; sb.Append(csum.ToString("X2")); addr++; sw_hex.WriteLine(sb.ToString()); } sw_hex.WriteLine(":00000001FF"); sw_hex.Close(); */ }