/// <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; } }
/// <summary> /// Initialize the CPU definition /// </summary> /// <param name="output"></param> public void Initialize(Output output) { Opcodes6800.MakeOpcodes(output); }