← index
ats/system/code_func.cpp
Source: ats/system/code_func.cpp
#include "code.hpp"

namespace interp {

    std::ostringstream comp_err;
    
    bool saveLineSource(const std::string &text) {
        std::fstream file;
        file.open(text, std::ios::out);
        if(!file.is_open()) return false;
        for(unsigned int i = 0; i < lines.size(); ++i) {
            file << lines[i].index << " " << lines[i].text << "\n";
        }
        file.close();
        return true;
    }
    
    std::string eatComment(std::string src) {
        std::string text;
        for(unsigned int i = 0; i < src.length(); ++i) {
            if(src[i] == ';') return text;
            else
                text += src[i];
        }
        return text;
    }
    
    bool openLineSource(const std::string &text) {
        comp_err.str("");
        std::fstream file;
        file.open(text, std::ios::in);
        if(!file.is_open()) return false;
        
        if(!lines.empty()) {
            lines.clear();
        }
        
        std::string orig;
        while(std::getline(file, orig)) {  
            std::string in = eatComment(orig);
            if(in.length() == 0) continue;
            
            std::vector<lex::Token> tokens;
            std::istringstream stream(in);
            lex::Scanner scan(stream);
            while(scan.valid()) {
                lex::Token token;
                scan >> token;
                if(token.getTokenType() != lex::TOKEN_EOF)
                    tokens.push_back(token);
            }
            
            if(!inputText(tokens, in))
                return false;
        }
        file.close();
        return true;
    }

    bool openLineString(const std::string &text) {
        comp_err.str("");
        std::istringstream file(text);
        if(!lines.empty()) {
            lines.erase(lines.begin(), lines.end());
        }
        std::string orig;
        while(std::getline(file, orig)) {
            std::string in;
            in = eatComment(orig);
            if(in.length()==0) continue;
            std::vector<lex::Token> tokens;
            std::istringstream stream(in);
            lex::Scanner scan(stream);
            while(scan.valid()) {
                lex::Token token;
                scan >> token;
                if(token.getTokenType() != lex::TOKEN_EOF)
                	tokens.push_back(token);
            }   
            if(!inputText(tokens, in))
                return false;
        }
        return true;
    }
    
    void insertText(std::vector<lex::Token> &tokens, const TextLine &in) {
        for(unsigned int i = 0; i < lines.size(); ++i) {
            if(lines[i].index == in.index) {
                lines[i].text = in.text;
                std::sort(lines.begin(), lines.end());
                return;
            }
        }
        lines.push_back(in);
        std::sort(lines.begin(), lines.end());
    }

    bool inputText(std::vector<lex::Token> &tokens, std::string input_line) {
        if(!comp_err.str().empty()) return false;
        if (input_line.empty()) {
            comp_err << "Error: Empty input line.\n";
            return false;
        }
         
        bool hasLineNumber = (tokens.size() >= 1 && tokens[0].getTokenType() == lex::TOKEN_DIGIT);
        
        if (hasLineNumber && tokens.size() >= 2) {
            int value = atoi(tokens[0].getToken().c_str());
            std::string codetext = input_line.substr(input_line.find(tokens[0].getToken()) + tokens[0].getToken().length());
            codetext.erase(0, codetext.find_first_not_of(" \t"));    
            TextLine in(value, codetext);
           
            if (checkInstruction(tokens, in)) {
                insertText(tokens, in);
                return true;
            }
        } else if (!hasLineNumber && tokens.size() >= 1) {
            int generatedLineNumber = 10;
            if (!lines.empty()) {
                int maxLine = 0;
                for (const auto& line : lines) {
                    if (line.index > maxLine) {
                        maxLine = line.index;
                    }
                }
                generatedLineNumber = maxLine + 10;
            }
            
            TextLine in(generatedLineNumber, input_line);
            std::cout << "Creating auto-numbered line: " << generatedLineNumber << " -> '" << input_line << "'\n";
            
            std::vector<lex::Token> tempTokens;
            tempTokens.push_back(lex::Token(std::to_string(generatedLineNumber), lex::TOKEN_DIGIT));
            tempTokens.insert(tempTokens.end(), tokens.begin(), tokens.end());
            
            if (checkInstruction(tempTokens, in)) {
                insertText(tempTokens, in);
                return true;
            }
        } else if (tokens.size() == 1 && hasLineNumber) {
              int index_n = atoi(tokens[0].getToken().c_str());
            for (int i = 0; i < lines.size(); ++i) {
                if (lines[i].index == index_n) {
                    lines.erase(lines.begin() + i);
                    std::cout << "Deleted line " << index_n << std::endl;
                    return true;
                }
            }
            comp_err << "Error: Line " << index_n << " not found.\n";
            return false;
        } else {
            std::cout << "Line rejected - doesn't match any pattern\n";
            comp_err << "Error: Invalid input format for line: " << input_line << "\n";
            return false;
        }
        
        return false;
    }

    bool isBranchInstruction(icode::opc op) {
        return op == icode::opc::BNE || op == icode::opc::BEQ ||
            op == icode::opc::BCC || op == icode::opc::BCS ||
            op == icode::opc::BMI || op == icode::opc::BPL ||
            op == icode::opc::BVC || op == icode::opc::BVS;      
    }


    bool checkInstruction(std::vector<lex::Token> &tokens, const TextLine &text) {
        if (tokens.size() <= 1) {
            comp_err << "Error: Statement requires instruction.\n";
            return false;
                }
       
        static unsigned int inc_offset = 0;
        inc_offset = 0; 
        
        if (tokens.size() >= 2) {
            icode::opc op = icode::strtoInc(tokens[1].getToken());
            
            if (op == icode::opc::NOTINC) {
                if (tokens[1].getTokenType() != lex::TOKEN_CHAR) {
                    comp_err << "Syntax Error: After line number requires either Label or instruction.\n";
                    return false;
                }
                
                inc_offset = 1;
                if (tokens.size() <= 2) {
                    comp_err << "Syntax Error: Label '" << tokens[1].getToken() << "' must be followed by an instruction.\n";
                    return false;
                }
                
                op = icode::strtoInc(tokens[1 + inc_offset].getToken());
                if (op == icode::opc::NOTINC) {
                    comp_err << "Syntax Error: '" << tokens[1 + inc_offset].getToken() << "' is not a valid 6502 instruction.\n";
                    return false;
                }
            }
            
            return validateAddressingMode(tokens, op, inc_offset);
        }
        
        return true;
    }

    bool validateAddressingMode(std::vector<lex::Token> &tokens, icode::opc instruction, unsigned int offset) {
        int instrIndex = 1 + offset; 

        
        if (instruction == icode::opc::JSR) {
            if (tokens.size() <= instrIndex + 1) {
                comp_err << "Syntax Error: JSR instruction requires a target address or label.\n";
                return false;
            }
            
            const lex::Token& targetToken = tokens[instrIndex + 1];
            if (targetToken.getTokenType() == lex::TOKEN_CHAR) {
                return validateInstructionAddressingMode(instruction, "ABSOLUTE");
            } else if (targetToken.getTokenType() == lex::TOKEN_HEX || 
                       targetToken.getTokenType() == lex::TOKEN_DIGIT) {
                return validateInstructionAddressingMode(instruction, "ABSOLUTE");
            } else {
                comp_err << "Syntax Error: JSR requires a valid address or label.\n";
                return false;
            }
        }
        
        
        if (tokens.size() <= instrIndex + 1) {
            
            if(instruction == icode::opc::END) {
                std::cout << "Found END instruction, terminating validation.\n";
                return true; 
            }


            if (instruction == icode::opc::CLC || instruction == icode::opc::CLD || 
                instruction == icode::opc::CLI || instruction == icode::opc::CLV ||
                instruction == icode::opc::SEC || instruction == icode::opc::SED ||
                instruction == icode::opc::SEI || instruction == icode::opc::TXA ||
                instruction == icode::opc::TAX || instruction == icode::opc::TAY ||
                instruction == icode::opc::TSX || instruction == icode::opc::TXS ||
                instruction == icode::opc::TYA || instruction == icode::opc::INX ||
                instruction == icode::opc::INY || instruction == icode::opc::DEX ||
                instruction == icode::opc::DEY || instruction == icode::opc::NOP ||
                instruction == icode::opc::PHA || instruction == icode::opc::PHP ||
                instruction == icode::opc::PLA || instruction == icode::opc::PLP ||
                instruction == icode::opc::RTS || instruction == icode::opc::RTI ||
                instruction == icode::opc::BRK) {
                

                bool result = validateInstructionAddressingMode(instruction, "IMPLIED");
                
                return result;
            }
            
            else if (instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                     instruction == icode::opc::ROL || instruction == icode::opc::ROR) {
                
                
                bool result = validateInstructionAddressingMode(instruction, "ACCUMULATOR");
                
                return result;
            }
            std::cout << "Instruction not recognized as implied or accumulator\n";
            comp_err << "Syntax Error: " << tokens[instrIndex].getToken() << " instruction requires an operand.\n";
            return false;
        }
    
        int operandIndex = instrIndex + 1;
      
        
        if (isBranchInstruction(instruction)) {
            if (tokens[operandIndex].getTokenType() == lex::TOKEN_DIGIT) {
                return validateInstructionAddressingMode(instruction, "RELATIVE");
            } else if (tokens[operandIndex].getTokenType() == lex::TOKEN_CHAR) {        
                return validateInstructionAddressingMode(instruction, "RELATIVE");
            } else {
                comp_err << "Syntax Error: Branch instruction expects line number or label.\n";
                return false;
            }
        }

        if (tokens[operandIndex].getToken() == "#") {
            if (tokens.size() <= operandIndex + 1) {
                comp_err << "Syntax Error: Missing value after #\n";
                return false;
            }
            
            
            if (tokens[operandIndex + 1].getTokenType() != lex::TOKEN_DIGIT && 
                tokens[operandIndex + 1].getTokenType() != lex::TOKEN_HEX) {
                comp_err << "Syntax Error: # must be followed by a number or hex value ($XX).\n";
                return false;
            }
         
            if (tokens.size() > operandIndex + 2) {
                comp_err << "Syntax Error: Immediate addressing (#) cannot be indexed with ,X or ,Y\n";
                return false;
            }
            
            return validateInstructionAddressingMode(instruction, "IMMEDIATE");
        }
        else if (tokens[operandIndex].getToken() == "(" ) {
            return parseIndirectAddressing(tokens, instruction, operandIndex);
        }
        else if (tokens[operandIndex].getTokenType() == lex::TOKEN_HEX || 
                tokens[operandIndex].getTokenType() == lex::TOKEN_DIGIT ||
                tokens[operandIndex].getTokenType() == lex::TOKEN_CHAR) {
            
            std::string addressingMode = "ABSOLUTE";
            
            if (tokens[operandIndex].getTokenType() == lex::TOKEN_HEX) {
                std::string hexValue = tokens[operandIndex].getToken();
                if (hexValue.length() <= 3) { 
                    addressingMode = "ZEROPAGE";
                }
            } else if (tokens[operandIndex].getTokenType() == lex::TOKEN_DIGIT) {
                int value = atoi(tokens[operandIndex].getToken().c_str());
                if (value < 256) {
                    addressingMode = "ZEROPAGE";
                }
            }
            
            if (tokens.size() > operandIndex + 1) {
                if (tokens[operandIndex + 1].getToken() == ",") {
                    if (tokens.size() <= operandIndex + 2) {
                        comp_err << "Syntax Error: Comma must be followed by X or Y register.\n";
                        return false;
                    }
                    
                    std::string reg = tokens[operandIndex + 2].getToken();
                    if (reg == "X" || reg == "x") {
                        addressingMode += "_X";
                    } else if (reg == "Y" || reg == "y") {
                        addressingMode += "_Y";
                    } else {
                        comp_err << "Syntax Error: Only X and Y registers are valid for indexing.\n";
                        return false;
                    }
                } else {
                    comp_err << "Syntax Error: Expected comma before register, found: " << tokens[operandIndex + 1].getToken() << "\n";
                    return false;
                }
            }
            
            return validateInstructionAddressingMode(instruction, addressingMode);
        }
        
        if (tokens[operandIndex].getToken() == "A" || tokens[operandIndex].getToken() == "a") {
            if (instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                instruction == icode::opc::ROL || instruction == icode::opc::ROR) {
                return validateInstructionAddressingMode(instruction, "ACCUMULATOR");
            }
        }
        
        comp_err << "Syntax Error: Invalid operand format.\n";
        return false;
    }

    bool validateInstructionIndirectMode(icode::opc instruction, const std::string& mode) {
        
        if (mode == "RELATIVE") {
               return isBranchInstruction(instruction);
        }
        if (mode == "INDIRECT") {
            return instruction == icode::opc::JMP;
        }
        
        if (mode == "INDEXED_INDIRECT" || mode == "INDEXED_I") {
        return (instruction == icode::opc::LDA ||
                instruction == icode::opc::STA ||
                instruction == icode::opc::CMP ||
                instruction == icode::opc::ADC ||
                instruction == icode::opc::SBC ||
                instruction == icode::opc::AND ||
                instruction == icode::opc::ORA ||
                instruction == icode::opc::EOR);
    }
        if (mode == "INDIRECT_INDEXED" || mode == "INDIRECT_I") {
            return (instruction == icode::opc::LDA ||
                    instruction == icode::opc::STA ||
                    instruction == icode::opc::CMP ||
                    instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC ||
                    instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA ||
                    instruction == icode::opc::EOR);
        }
        return false;
    }

    bool parseIndirectAddressing(std::vector<lex::Token> &tokens, icode::opc instruction, int startIndex) {
        if (tokens.size() <= startIndex + 2) {
            comp_err << "Syntax Error: Incomplete indirect addressing mode.\n";
            return false;
        }

        if (tokens[startIndex + 1].getTokenType() != lex::TOKEN_HEX &&
            tokens[startIndex + 1].getTokenType() != lex::TOKEN_DIGIT) {
            comp_err << "Syntax Error: Expected address inside parentheses.\n";
            return false;
        }

        std::string addressingMode;

        // Handle JMP ($nnnn)
        if (tokens[startIndex + 2].getToken() == ")" && instruction == icode::opc::JMP) {
            addressingMode = "INDIRECT";
            return validateInstructionIndirectMode(instruction, addressingMode);
        }

        
        if (
        
            (tokens.size() > startIndex + 4 &&
            tokens[startIndex + 2].getToken() == "," &&
            (tokens[startIndex + 3].getToken() == "X" || tokens[startIndex + 3].getToken() == "x") &&
            tokens[startIndex + 4].getToken() == ")")
        ) {
            addressingMode = "INDEXED_INDIRECT";
            return validateInstructionIndirectMode(instruction, addressingMode);
        }

        
        if (tokens.size() > startIndex + 4 &&
            tokens[startIndex + 2].getToken() == ")" &&
            tokens[startIndex + 3].getToken() == "," &&
            (tokens[startIndex + 4].getToken() == "Y" || tokens[startIndex + 4].getToken() == "y")) {
            addressingMode = "INDIRECT_INDEXED";
            return validateInstructionIndirectMode(instruction, addressingMode);
        }

        
        if (tokens[startIndex + 2].getToken() == ")") {
            if (instruction != icode::opc::JMP) {
                comp_err << "Syntax Error: Only JMP supports (addr) indirect mode.\n";
                return false;
            }
            addressingMode = "INDIRECT";
            return validateInstructionIndirectMode(instruction, addressingMode);
        }

        comp_err << "Syntax Error: Invalid indirect addressing syntax.\n";
        return false;
    }
    bool validateInstructionAddressingMode(icode::opc instruction, const std::string& mode) {        
                
        if (mode == "RELATIVE") {
            
            bool isRelative = (instruction == icode::opc::BPL ||  // Branch if Plus
                              instruction == icode::opc::BMI ||  // Branch if Minus
                              instruction == icode::opc::BVC ||  // Branch if Overflow Clear
                              instruction == icode::opc::BVS ||  // Branch if Overflow Set
                              instruction == icode::opc::BCC ||  // Branch if Carry Clear
                              instruction == icode::opc::BCS ||  // Branch if Carry Set
                              instruction == icode::opc::BNE ||  // Branch if Not Equal
                              instruction == icode::opc::BEQ);   // Branch if Equal
            
            return isRelative;
        }
              
        if (mode == "IMPLIED") {
            bool isImplied = (instruction == icode::opc::CLC || instruction == icode::opc::CLD || 
                             instruction == icode::opc::CLI || instruction == icode::opc::CLV ||
                             instruction == icode::opc::SEC || instruction == icode::opc::SED ||
                             instruction == icode::opc::SEI || instruction == icode::opc::TXA ||
                             instruction == icode::opc::TAX || instruction == icode::opc::TAY ||
                             instruction == icode::opc::TSX || instruction == icode::opc::TXS ||
                             instruction == icode::opc::TYA || instruction == icode::opc::INX ||
                             instruction == icode::opc::INY || instruction == icode::opc::DEX ||
                             instruction == icode::opc::DEY || instruction == icode::opc::NOP ||
                             instruction == icode::opc::PHA || instruction == icode::opc::PHP ||
                             instruction == icode::opc::PLA || instruction == icode::opc::PLP ||
                             instruction == icode::opc::RTS || instruction == icode::opc::RTI);
    
            return isImplied;
        }
        
        if (mode == "IMMEDIATE") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDX || 
                    instruction == icode::opc::LDY || instruction == icode::opc::CMP ||
                    instruction == icode::opc::CPX || instruction == icode::opc::CPY ||
                    instruction == icode::opc::ADC || instruction == icode::opc::SBC ||
                    instruction == icode::opc::AND || instruction == icode::opc::ORA ||
                    instruction == icode::opc::EOR);
        }
        
        if (mode == "ABSOLUTE_X") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDY ||
                    instruction == icode::opc::STA || instruction == icode::opc::STY ||
                    instruction == icode::opc::CMP || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR ||
                    instruction == icode::opc::INC || instruction == icode::opc::DEC ||
                    instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                    instruction == icode::opc::ROL || instruction == icode::opc::ROR);
        }
        
        if (mode == "ZEROPAGE") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDX || 
                    instruction == icode::opc::LDY || instruction == icode::opc::STA ||
                    instruction == icode::opc::STX || instruction == icode::opc::STY ||
                    instruction == icode::opc::CMP || instruction == icode::opc::CPX ||
                    instruction == icode::opc::CPY || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR ||
                    instruction == icode::opc::INC || instruction == icode::opc::DEC ||
                    instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                    instruction == icode::opc::ROL || instruction == icode::opc::ROR ||
                    instruction == icode::opc::BIT);
        }
        
        if (mode == "ABSOLUTE") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDX || 
                    instruction == icode::opc::LDY || instruction == icode::opc::STA ||
                    instruction == icode::opc::STX || instruction == icode::opc::STY ||
                    instruction == icode::opc::CMP || instruction == icode::opc::CPX ||
                    instruction == icode::opc::CPY || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR ||
                    instruction == icode::opc::INC || instruction == icode::opc::DEC ||
                    instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                    instruction == icode::opc::ROL || instruction == icode::opc::ROR ||
                    instruction == icode::opc::BIT || instruction == icode::opc::JMP ||
                    instruction == icode::opc::JSR);
        }
        
        if (mode == "ZEROPAGE_X") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDY ||
                    instruction == icode::opc::STA || instruction == icode::opc::STY ||
                    instruction == icode::opc::CMP || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR ||
                    instruction == icode::opc::INC || instruction == icode::opc::DEC ||
                    instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                    instruction == icode::opc::ROL || instruction == icode::opc::ROR);
        }
        
        if (mode == "ABSOLUTE_X") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDY ||
                    instruction == icode::opc::STA || instruction == icode::opc::STY ||
                    instruction == icode::opc::CMP || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR ||
                    instruction == icode::opc::INC || instruction == icode::opc::DEC ||
                    instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                    instruction == icode::opc::ROL || instruction == icode::opc::ROR);
        }
        
        if (mode == "ZEROPAGE_Y") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDX ||
                    instruction == icode::opc::STA || instruction == icode::opc::STX ||
                    instruction == icode::opc::CMP || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR);
        }
        
        if (mode == "ABSOLUTE_Y") {
            return (instruction == icode::opc::LDA || instruction == icode::opc::LDX ||
                    instruction == icode::opc::STA || instruction == icode::opc::STX ||
                    instruction == icode::opc::CMP || instruction == icode::opc::ADC ||
                    instruction == icode::opc::SBC || instruction == icode::opc::AND ||
                    instruction == icode::opc::ORA || instruction == icode::opc::EOR);
        }
        if (mode == "ACCUMULATOR") {
            bool isAccumulator = (instruction == icode::opc::ASL || instruction == icode::opc::LSR ||
                                instruction == icode::opc::ROL || instruction == icode::opc::ROR);
    
            return isAccumulator;
      }
        
    
        return false;
    }
    
    bool procLine(const TextLine &text, Code &code) {
        std::istringstream input(text.text);
        lex::Scanner scan(input);
        std::vector<lex::Token> tokens;
        while(scan.valid()) {
            lex::Token token;
            scan >> token;
            if(token.getTokenType() != lex::TOKEN_EOF)
            	tokens.push_back(token);
            
        }
        // for now output each token
        std::cout << "Code Line: ["<<text.index <<"] - [" << text.text << "] = {\n";
        for(unsigned int i = 0; i < tokens.size(); ++i) {
            std::cout << tokens[i] << "\n";
        }
        std::cout << "}\n\n";
        return true;
    }
   
}