public static void Split(Assembler.AsmState state, string outputPath) { var output = state.Output; output.Info($"Splitting ROM into {state.RomDefinitions.Count} output files."); try { foreach (var romDef in state.RomDefinitions) { var filename = Path.Combine(outputPath, romDef.Filename + OutputSuffix); var rom = new byte[romDef.Size]; Array.Copy(state.RomImage, romDef.Offset, rom, 0, romDef.Size); File.WriteAllBytes(filename, rom); output.Info($"File {filename} created"); } } catch (Exception ex) { output.Error($"Exception: {ex}"); } }
// evaluate expression // return true if evaluated to a value, else false // if false, marks line as needing a fixup public static bool Evaluate(Assembler.AsmState state, Line line, string expression, out int value) { var retval = true; // assume works value = 0; try { var state2 = new State(expression); var t = ParseToTree(state2); retval = EvaluateTree(state, t, out value); } catch (Exception) { // todo - errors handled elsewhere retval = false; } if (!retval) { line.NeedsFixup = true; } return(retval); }
/// <summary> /// Handle the opcode parsing /// </summary> /// <param name="state"></param> /// <param name="line"></param> /// <param name="op"></param> public void ParseOpcodeAndOperand(Assembler.AsmState state, Line line, Opcode op) { /* rules: * 1. empty operand => Inherent mode * 2. starts with # => Immediate * 3. relative => Direct (branching, etc) * 4. ,R or n,R => Indexed (last Indexed Indirect, PC relative) * 5. expr => Direct (if fits) or Extended (if does not) */ var addrMode = Opcodes6800.AddressingMode.Direct; var operand = line.Operand; var operandText = operand?.Text ?? ""; var mnemonic = Assembler.GetMnemonic(line); var isBranch = Opcodes6800.IsBranch(mnemonic); var indexed = operandText.Contains(","); if (String.IsNullOrEmpty(operand?.Text)) { addrMode = Opcodes6800.AddressingMode.Inherent; // rule 1 } else if (operandText.StartsWith("#")) { addrMode = Opcodes6800.AddressingMode.Immediate; // rule 2 } else if (isBranch) { addrMode = Opcodes6800.AddressingMode.Direct; // rule 3 } else if (indexed) { addrMode = Opcodes6800.AddressingMode.Indexed; // rule 4 } else if (operandText.StartsWith(">")) { addrMode = Opcodes6800.AddressingMode.Extended; } else // rule 5 { if (!Evaluator.Evaluate(state, line, operandText, out int value1)) { if (state.Pass == 2) { state.Output.Error(line, line.Operand, "Cannot evaluate operand."); } } if (Assembler.BitsRequired(value1) <= 8) { addrMode = Opcodes6800.AddressingMode.Direct; } else { addrMode = Opcodes6800.AddressingMode.Extended; } } // now have addrMode, isBranch, requiresRegisterList, isRegisterList, indexed to decide var missingKey = !op.Forms.ContainsKey((int)addrMode); if (addrMode == Opcodes6800.AddressingMode.Direct && missingKey && op.Forms.ContainsKey((int)Opcodes6800.AddressingMode.Extended)) { addrMode = Opcodes6800.AddressingMode.Extended; missingKey = false; } // encode instruction and operand bytes if (missingKey) { state.Output.Error(line, line.Operand, "Illegal operand mode"); return; } var opMode = op.Forms[(int)addrMode]; line.AddressingMode = (int)addrMode; line.Data.Clear(); // in case double called line.Data.AddRange(opMode.Bytes); line.Length = Math.Abs(opMode.Length); int value; switch (addrMode) { case Opcodes6800.AddressingMode.Inherent: // no bytes to add break; case Opcodes6800.AddressingMode.Direct: if (Evaluator.Evaluate(state, line, operandText, out value)) { if (isBranch) { if (Evaluator.Evaluate(state, line, operandText, out value)) { var relative = -(state.Address - value + line.Length); var bitsRequired = Assembler.BitsRequired(relative); if (bitsRequired <= 8) { line.AddValue(relative, 1); } else { // loop may not exist yet. track pass, and error on second one line.NeedsFixup = true; if (state.Pass == 2) { state.Output.Error(line, operand, $"Operand {relative} out of 8 bit target range"); } return; } } } else { line.AddValue(value, 1); } } break; case Opcodes6800.AddressingMode.Immediate: if (!operandText.StartsWith("#")) { state.Output.Error(line, line.Operand, "Missing '#' on immediate addressing mode."); return; } var len = opMode.Length - opMode.Bytes.Count; if (Evaluator.Evaluate(state, line, operandText.Substring(1), out value)) { line.AddValue(value, len); } break; case Opcodes6800.AddressingMode.Extended: var clearOp = operandText; if (operandText.StartsWith(">")) { clearOp = clearOp.Substring(1); } if (Evaluator.Evaluate(state, line, clearOp, out value)) { line.AddValue(value, 2); } break; case Opcodes6800.AddressingMode.Indexed: var words = operandText.Split(',').Select(t => t.Trim()).ToList(); if (words.Count != 2 || words[1].ToLower() != "x") { state.Output.Error(line, line.Operand, "Indexed addressing mode not of form [n],x"); return; } if (Evaluator.Evaluate(state, line, words[0], out value)) { if (value < 0 || 255 < value) { state.Output.Error(line, line.Operand, "Indexed addressing mode operand not in range 0-255."); } line.AddValue(value, 1); } else if (state.Pass == 2) { state.Output.Error(line, line.Operand, "Cannot evaluate operand."); } break; default: state.Output.Error(line, line.Operand, $"Unknown addressing mode {addrMode}"); break; } }
static bool EvaluateTree(Assembler.AsmState state, Tree tree, out int value) { value = 0; int left, right; if (tree.op != null) { switch (tree.op) { case "+": if (!EvaluateTree(state, tree.left, out left)) { return(false); } if (tree.right == null) { value = left; return(true); } if (!EvaluateTree(state, tree.right, out right)) { return(false); } value = left + right; return(true); case "-": if (!EvaluateTree(state, tree.left, out left)) { return(false); } if (tree.right == null) { value = -left; return(true); } if (!EvaluateTree(state, tree.right, out right)) { return(false); } value = left - right; return(true); case "*": if (!EvaluateTree(state, tree.left, out left) || !EvaluateTree(state, tree.right, out right)) { return(false); } value = left * right; return(true); case "/": if (!EvaluateTree(state, tree.left, out left) || !EvaluateTree(state, tree.right, out right)) { return(false); } if (right == 0) { throw new Exception("Division by zero"); } value = left / right; return(true); case "%": if (!EvaluateTree(state, tree.left, out left) || !EvaluateTree(state, tree.right, out right)) { return(false); } if (right == 0) { throw new Exception("Mod division by zero"); } value = left % right; return(true); default: throw new Exception($"unknown op {tree.op}"); } } else { var text = tree.value; if (text.StartsWith("$")) { value = Convert.ToInt32(text.Substring(1), 16); return(true); } if (text.All(t => Char.IsDigit(t))) { value = Int32.Parse(text); return(true); } // must be symbol - look up value or throw if (state.Symbols.GetValue(text, out value, state.Address)) { return(true); } return(false); // Symbol {text} not yet evaluated } }