private void AddLabelsToSymbolTable(IEnumerable <string> code)
        {
            logger.log("PHASE: FILL SYMBOL TABLE");
            logger.log($"INPUT CODE:---{string.Join(Environment.NewLine, code)}---");

            var outputLineCounter = 0;
            var parser            = new AssemblyParser(null, code, this.logger);

            while (parser.HasMoreCommands() && parser.Advance())
            {
                //for all non label, non define commands increment the counter
                if (parser.CommandType() != CommandType.ASSEM_LABEL && parser.CommandType() != CommandType.ASSEM_DEFINE)
                {
                    var increment = parser.commandTypeToNumberOfLines[parser.CommandType()];
                    outputLineCounter = outputLineCounter + increment;

                    //variables will be handled in the next pass.
                }
                //if the current command is a label - don't increment our counter.
                //if we see a label, add a symbol for the address it points to.
                else if (parser.CommandType() == CommandType.ASSEM_LABEL)
                {
                    var memoryAddressInUserCodeSpace = outputLineCounter + MemoryMap[MemoryMapKeys.user_code].AbsoluteStart;

                    if (this.symbolTable.ContainsKey(parser.LabelText()))
                    {
                        Console.WriteLine($"WARNING: Symboltable already contained this label {parser.LabelText()} at line {this.symbolTable[parser.LabelText()]}, redefining it to equal:  {memoryAddressInUserCodeSpace}");
                        // throw new Exception($"Symboltable already contained this label {parser.LabelText()} at line {memoryAddressInUserCodeSpace}");
                    }

                    this.symbolTable[parser.LabelText()] = memoryAddressInUserCodeSpace;

                    logger.log($"adding symbol {parser.LabelText() } at line { memoryAddressInUserCodeSpace }");
                }
                else if (parser.CommandType() == CommandType.ASSEM_DEFINE)
                {
                    var define  = parser.Operands().FirstOrDefault();
                    var address = parser.Operands().Skip(1).FirstOrDefault();
                    this.symbolTable[define] = int.Parse(address);

                    logger.log($"adding symbol {define } at line { address}");
                }
            }
        }
        private IEnumerable <string> ExpandMacros(string assemblyFilePath)
        {
            logger.log("PHASE: EXPAND MACROS");

            var parser = new AssemblyParser(assemblyFilePath, null, this.logger);

            while (parser.HasMoreCommands() && parser.Advance())
            {
                //if the current command is a storage macro, we should do a conversion like:

                //symbol = 100
                //is transformed to:
                ////////////////
                //LOADAIMMEDIATE
                //100
                //STOREA
                //symbol
                ////////////////

                if (parser.CommandType() == CommandType.ASSEM_STORE_MACRO)
                {
                    parser.output.Add(nameof(CommandType.LOADAIMMEDIATE));
                    parser.output.Add(parser.Operands()[1]);
                    parser.output.Add(nameof(CommandType.STOREA));
                    parser.output.Add(parser.Operands()[0]);

                    logger.log("JUST EXPANDED A STORAGE MACRO");
                    logger.log(string.Join(",", parser.output.TakeLast(4)));
                }
                //all other commands are unchanged
                else
                {
                    parser.output.Add(parser.currentLine);
                    if (parser.HasOperands())
                    {
                        parser.output = parser.output.Concat(parser.Operands()).ToList();
                    }

                    logger.log("JUST EXPANDED NOTHING");
                    logger.log(string.Join(Environment.NewLine, parser.output.Last()));
                }
            }
            return(parser.output);
        }
        private IEnumerable <string> ConvertOpCodes(IEnumerable <string> code)
        {
            logger.log("PHASE: CONVERT OPCODES TO OUTPUT FORMAT");

            var parser    = new AssemblyParser(null, code, this.logger);
            var converter = new CodeConverter();

            while (parser.HasMoreCommands() && parser.Advance())
            {
                //dont do anything for labels or defines - we already made symbols for them
                //in the first pass.
                if (parser.CommandType() != CommandType.ASSEM_LABEL && parser.CommandType() != CommandType.ASSEM_DEFINE)
                {
                    //first convert the opcode.
                    parser.output.Add(converter.InstructionAsHexString(parser.CommandType()));

                    //then check if this opcode has symbols
                    if (parser.HasSymbols())
                    {
                        //get the symbol and lookup the memory address it points to -
                        //store this as the next line in the output string array.

                        // always assume a default offset of addding 0.
                        int symbolOffset = 0;
                        var symbol       = parser.Operands()[0];
                        if (parser.SymbolHasOffset())
                        {
                            symbolOffset = parser.symbolOffsetExpressionInfo().Item2;
                            symbol       = parser.symbolOffsetExpressionInfo().Item1;
                        }

                        if (this.symbolTable.ContainsKey(symbol))
                        {
                            parser.output.Add(converter.NumberAsHexString(this.symbolTable[symbol] + symbolOffset));
                        }

                        //new symbol store it.
                        //increment the symbolTable offset so variables are stored at next free space at offset 255+500 ( so first one is at 755)
                        //this means programs have a max length currently of 500 lines - and can store 1000 - 755 symbols -
                        //to increase this we just need to modify the bootloader - we have 64k address space to play with in the cpu.
                        //transfer time will just increase.
                        else
                        {
                            var symbolTableCurrentLocation = MemoryMap[MemoryMapKeys.symbols].AbsoluteStart + this.currentSymbolTableOffset;
                            if (symbolTableCurrentLocation > MemoryMap[MemoryMapKeys.symbols].AbsoluteEnd)
                            {
                                throw new Exception(" symbol table has more variables than allocated memory space for symbols");
                            }
                            logger.log($"adding symbol, {symbol} at line: {symbolTableCurrentLocation}");
                            this.symbolTable[symbol] = symbolTableCurrentLocation;
                            //increment the offset.
                            this.currentSymbolTableOffset = this.currentSymbolTableOffset + 1;
                            parser.output.Add(converter.NumberAsHexString(this.symbolTable[symbol]));
                        }
                    }
                    //if no symbols check if it has any operands
                    else if (parser.HasOperands())
                    {
                        var convertedOperandsToHex = parser.Operands().Select(x => converter.NumberAsHexString(int.Parse(x))).ToArray();
                        parser.output = parser.output.Concat(convertedOperandsToHex).ToList();
                    }
                }
            }
            return(parser.output);
        }