bool OpcodeHandled(AsmState state, Line line) { var mnemonic = GetMnemonic(line); var op = State.Cpu.FindOpcode(mnemonic); if (op != null) { line.Address = state.Address; State.Cpu.ParseOpcodeAndOperand(state, line, op); return(true); } return(false); }
bool StructHandled(AsmState state, Line line) { var structName = line.Opcode.Text; var str = state.Symbols.GetStructs().FirstOrDefault(s => s.Text == structName); if (str != null) { line.Address = state.Address; var operands = line.Operand?.Text ?? ""; line.Length = str.ByteLength; if (String.IsNullOrEmpty(operands)) { Error(line, line.Opcode, "Empty struct operand"); return(false); } // odd special case of zeroed fields var cleaned = operands.Replace("<", "").Replace(">", "").Replace("\t", "").Replace(" ", ""); var zeroOut = cleaned == "0"; // special case - if this, zero out structure cleaned = ConvertStringsToNumbers(cleaned); var items = cleaned.Split(','); if (str.ByteLengths.Count != items.Length && !zeroOut) { Error(line, line.Operand, $"Struct def has {str.ByteLengths.Count} fields, operand has {items.Length}"); return(false); } var bytesAlready = line.Data.Count; var value = 0; for (var i = 0; i < str.ByteLengths.Count; ++i) { if (zeroOut || Evaluator.Evaluate(state, line, items[i], out value)) { line.AddValue(value, str.ByteLengths[i]); } else { line.NeedsFixup = true; var len = line.Data.Count; if (len > bytesAlready) // restore state { line.Data.RemoveRange(bytesAlready, len - bytesAlready); } break; // will return to here and try again } } return(true); } return(false); }
bool PseudoOpHandled(AsmState state, Line line) { switch (line.Opcode.Text.ToLower()) { case "fcc": // string data line.Address = state.Address; var num = ConvertStringsToNumbers(line.Operand.Text); WriteData(state, line, num, 1); break; case "rmb": // reserve bytes line.Address = state.Address; var szText = line?.Operand?.Text ?? ""; if (ParseHex(szText, out var size)) { line.Data.AddRange(new byte[size]); line.Length = size; line.Address += size; } else { state.Output.Error($"Cannot parse 'rmb' directive size {szText}"); } break; case "fcb": // byte data line.Address = state.Address; WriteData(state, line, line.Operand.Text, 1); break; case "fdb": // double byte data line.Address = state.Address; WriteData(state, line, line.Operand.Text, 2); break; case "end": // end of assembly line.Address = state.Address; line.Length = 0; break; default: return(false); } return(true); }
// write fdb or fcb data void WriteData(AsmState state, Line line, string text, int itemLength) { var items = text.Split(',').Select(t => t.Trim()).ToList(); line.Length = itemLength * items.Count; foreach (var item in items) { if (Evaluator.Evaluate(state, line, item, out int value)) { line.AddValue(value, itemLength); } else { line.Data.Clear(); line.NeedsFixup = true; break; } } }
bool DirectiveHandled(AsmState state, Line line) { int value; switch (line.Opcode.Text.ToLower()) { case ".setdp": // set direct page line.Address = state.Address; line.Length = 0; if (Evaluator.Evaluate(state, line, line.Operand.Text, out value)) { state.dpRegister = value; // todo - move to CPU class } else { Error(line, line.Operand, "Cannot evaluate DP"); } break; case ".rom": // add rom definition line.Address = state.Address; line.Length = 0; var fields = line.Operand.Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) .Select(r => r.Trim().ToLower()) .ToList(); if (fields.Count == 4 && Int32.TryParse(fields[1], out var size) && Int32.TryParse(fields[3], NumberStyles.AllowHexSpecifier | NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var offset)) { var filename = fields[0]; var sha1 = fields[2]; state.RomDefinitions.Add(new AsmState.RomDef(filename, size, offset, sha1)); } else { Error(line, line.Operand, "Cannot parse .ROM directive."); } break; case ".cpu": // set CPU type todo case ".meta": // simple comment line.Address = state.Address; line.Length = 0; break; case "org": case ".org": // set address line.Address = state.Address; line.Length = 0; if (Evaluator.Evaluate(state, line, line.Operand.Text, out value)) { state.Address = value; } else { Error(line, line.Operand, "Cannot evaluate address"); } break; default: return(false); } return(true); }
/// <summary> /// Assemble the code. /// Return true on no errors, else false /// Inspect the State property to see returned items /// </summary> /// <param name="filename"></param> /// <param name="textOut"></param> /// <returns></returns> public bool Assemble(string filename, TextWriter textOut) { State = new AsmState(); State.Output = new Output(textOut); State.Output.Info(""); State.Output.Info(""); State.Output.Info($"Assembling file {filename}"); if (!File.Exists(filename)) { State.Output.Error($"File {filename} does not exist."); return(false); } State.Lines = Tokenizer.GetLines(File.ReadLines(filename), State.Output); // determine CPU foreach (var line in State.Lines) { if (line?.Opcode?.Text.ToLower() == ".cpu") { State.Cpu = MakeCpu(line?.Operand?.Text, State.Output); } } if (State.Cpu == null) { State.Output.Info("CPU not detected, assuming 6809. Use '.cpu' directive to set."); State.Cpu = new Cpu6809(false); State.Cpu.Initialize(State.Output); } // clean all opcodes foreach (var line in State.Lines) { if (line.Opcode != null) { line.Opcode.Text = State.Cpu.FixupMnemonic(line); } } if (State.Lines == null) { return(false); } State.Output.Info($"{State.Lines.Count} lines tokenized."); // handle #ifdef, #undef, etc Preprocess(); //foreach (var line in lines) // WriteLine(line.ToString()); // get structs, fill in sizes, validate if (!MakeStructs()) { return(false); } State.Output.Info($"{State.Symbols.GetStructs().Count} structs degrizzled."); // foreach (var s in structs) // WriteLine($"Struct {s}"); var labelLines = State.Lines.Where(d => d.Label != null); foreach (var labelLine in labelLines) { State.Symbols.AddSymbol(new Label(labelLine), State.Output); } State.Output.Info($"{State.Symbols.Count} labels cryoambulated."); if (!Assemble()) { return(false); } return(CreateRom()); }