private bool ParsePragma(int lineIndex, string line, string opcode, List <string> tokens, ParserState state) { string pragma = opcode.ToLowerInvariant(); if (!LineSearch.MatchPragma(pragma)) { return(false); } switch (pragma) { case ".dat8": ParseData8(line.Replace(".dat8", string.Empty).Trim(), state); return(true); case ".dat16": ParseData16(line.Replace(".dat16", string.Empty).Trim(), state); return(true); case ".advance": break; case ".alias": { int value; if (tokens.Count != 3) { throw new Exception("alias pragma takes two parameters"); } string alias = tokens[1]; if (!char.IsLetter(alias[0])) { throw new Exception("alias must begin with a letter"); } if (alias.Any(t => !char.IsLetterOrDigit(t))) { throw new Exception("alias must be composed of letters and digits"); } tokens[2] = tokens[2].Replace("$", "0x"); object convertFromString = new Int32Converter().ConvertFromString(tokens[2]); if (convertFromString != null) { value = (int)convertFromString; } else { throw new Exception("alias pragma - ushort parameter must be an unsigned 16-bit integer"); } Scopes.Scope scope = state.Scopes.GetLastOpenScope(); scope.AddAlias(alias, (ushort)value); return(true); } case ".alignglobals": { int value; if (tokens.Count != 2) { throw new Exception("alignglobals pragma takes a single parameter"); } if (!int.TryParse(tokens[1], out value)) { throw new Exception("alignglobals pragma parameter must be an integer"); } if (value < 1 || value > 4) { throw new Exception("alignglobals pragma parameter must be an integer between 1 and 4"); } m_Alignment = value; return(true); } case ".checkpc": break; case ".org": break; case ".incbin": return(IncludeBinary(tokens, state)); case ".include": return(IncludeAsm(tokens, state)); case ".macro": break; case ".macend": break; case ".require": break; case ".reserve": break; case ".scope": case "{": state.Scopes.ScopeOpen(state.Code.Count, lineIndex); return(true); case ".scend": case "}": return(state.Scopes.ScopeClose(state.Code.Count)); } throw new Exception($"Unimplemented pragma in line {line}"); }
private void AssembleLine(int lineIndex, string line, ParserState state) { line = line.Trim(); if (LineSearch.MatchLabel(line)) { if (!state.Scopes.IsScopeOpen) // global scope { while ((state.Code.Count % m_Alignment) != 0) { state.Code.Add(0x00); } } // parse label and determine if there is anything else to parse on this line. int remaiderLineContentIndex = ParseLabel(line, state); if (remaiderLineContentIndex <= 0) { return; } // if there is something left to parse, trim it and then interpret it as its own line. line = line.Remove(0, remaiderLineContentIndex).Trim(); if (line.Length == 0) { return; } } List <string> tokens = Tokenize(line); string opcode = tokens[0]; opcode = opcode.Trim(); if (ParsePragma(lineIndex, line, opcode, tokens, state)) { // Successfully parsed a pragma, no need to continue with this line. return; } OpcodeFlag opcodeFlag = OpcodeFlag.BitWidth16; // default to operating on 16 bits // Look for flags on operands (xxx.yyy, where y is the flag). if (opcode.IndexOf('.') != -1 && (opcode.IndexOf('.') > 1) && (opcode.Length - opcode.IndexOf('.') - 1 > 0)) { // opcode has a flag string flag = opcode.Substring(opcode.IndexOf('.') + 1); if (!ParseOpcodeFlag(flag, ref opcodeFlag)) { throw new Exception($"Unknown bit width flag '{flag}' for instruction '{line}'"); } opcode = opcode.Substring(0, opcode.IndexOf('.')); } // get the assembler for this opcode. If no assembler exists, throw error. Func <List <string>, OpcodeFlag, ParserState, List <ushort> > assembler; if (m_Opcodes.ContainsKey(opcode.ToLowerInvariant())) { assembler = m_Opcodes[opcode.ToLowerInvariant()]; } else { throw new Exception($"Undefined instruction in line \"{line}\""); } // get the parameters List <string> param = new List <string>(); for (int i = 1; i < tokens.Count; i++) { param.Add(tokens[i].Trim()); } // pass the params to the opcode's assembler. If no output, throw error. List <ushort> code = assembler(param, opcodeFlag, state); if (code == null) { throw new Exception($"Error assembling line {line}"); } // add the output of the assembler to the machine code output. for (int i = 0; i < code.Count; i++) { ushort this_opcode = code[i]; state.Code.Add((byte)(this_opcode & 0x00ff)); state.Code.Add((byte)((this_opcode & 0xff00) >> 8)); } }