478 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include "memory.h"
 | 
						|
#include "cpu.h"
 | 
						|
#include "assembler.h"
 | 
						|
#include "compiler.h"
 | 
						|
#include "validater.h"
 | 
						|
 | 
						|
 | 
						|
namespace Validater
 | 
						|
{
 | 
						|
    bool initialise(void)
 | 
						|
    {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void adjustLabelAddresses(uint16_t address, int offset)
 | 
						|
    {
 | 
						|
        // Adjust addresses for any non page jump labels with addresses higher than start label, (labels can be stored out of order)
 | 
						|
        for(int i=0; i<int(Compiler::getLabels().size()); i++)
 | 
						|
        {
 | 
						|
            if(!Compiler::getLabels()[i]._pageJump  &&  Compiler::getLabels()[i]._address >= address)
 | 
						|
            {
 | 
						|
                Compiler::getLabels()[i]._address += int16_t(offset);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        for(int i=0; i<int(Compiler::getInternalLabels().size()); i++)
 | 
						|
        {
 | 
						|
            if(Compiler::getInternalLabels()[i]._address >= address)
 | 
						|
            {
 | 
						|
                Compiler::getInternalLabels()[i]._address += int16_t(offset);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void adjustVasmAddresses(int codeLineIndex, uint16_t address, int offset)
 | 
						|
    {
 | 
						|
        for(int i=codeLineIndex; i<int(Compiler::getCodeLines().size()); i++)
 | 
						|
        {
 | 
						|
            for(int j=0; j<int(Compiler::getCodeLines()[i]._vasm.size()); j++)
 | 
						|
            {
 | 
						|
                // Don't adjust page jump's
 | 
						|
                if(!Compiler::getCodeLines()[i]._vasm[j]._pageJump  &&  Compiler::getCodeLines()[i]._vasm[j]._address >= address)
 | 
						|
                {
 | 
						|
                    Compiler::getCodeLines()[i]._vasm[j]._address += int16_t(offset);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    auto insertPageJumpInstruction(const std::vector<Compiler::CodeLine>::iterator& itCode, const std::vector<Compiler::VasmLine>::iterator& itVasm,
 | 
						|
                                   const std::string& opcode, const std::string& operand, const std::string& code, uint16_t address, int vasmSize)
 | 
						|
    {
 | 
						|
        if(itVasm >= itCode->_vasm.end())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Validater::insertPageJumpInstruction() : Trying to insert a PAGE JUMP into lala land, in '%s'", itCode->_code.c_str());
 | 
						|
            _EXIT_(EXIT_FAILURE);
 | 
						|
        }
 | 
						|
 | 
						|
        Memory::takeFreeRAM(address, vasmSize, true);
 | 
						|
 | 
						|
        return itCode->_vasm.insert(itVasm, {address, opcode, operand, code, "", true, vasmSize});
 | 
						|
    }
 | 
						|
 | 
						|
    // TODO: make this more flexible, (e.g. sound channels off etc)
 | 
						|
    bool checkForRelocation(const std::string& opcode, uint16_t vPC, uint16_t& nextPC, uint16_t& numThunks, uint16_t& totalThunkSize, bool& print)
 | 
						|
    {
 | 
						|
#define CALL_PAGE_JUMP_SIZE    7
 | 
						|
#define CALLI_PAGE_JUMP_SIZE   3
 | 
						|
#define CALL_PAGE_JUMP_OFFSET  2
 | 
						|
#define CALLI_PAGE_JUMP_OFFSET 0
 | 
						|
 | 
						|
        int opcodeSize = 0;
 | 
						|
        if(opcode.size())
 | 
						|
        {
 | 
						|
            // Macro
 | 
						|
            if(opcode[0] == '%')
 | 
						|
            {
 | 
						|
                std::string macro = opcode;
 | 
						|
                macro.erase(0, 1);
 | 
						|
 | 
						|
                if(Compiler::getMacroIndexEntries().find(macro) != Compiler::getMacroIndexEntries().end())
 | 
						|
                {
 | 
						|
                    opcodeSize = Compiler::getMacroIndexEntries()[macro]._byteSize;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // VASM
 | 
						|
            else
 | 
						|
            {
 | 
						|
                opcodeSize = Assembler::getAsmOpcodeSize(opcode);
 | 
						|
            }
 | 
						|
 | 
						|
            if(opcodeSize)
 | 
						|
            {
 | 
						|
                // Increase opcodeSize by size of page jump prologue
 | 
						|
                int opSize = (Compiler::getCodeRomType() >= Cpu::ROMv5a) ? opcodeSize + CALLI_PAGE_JUMP_SIZE : opcodeSize + CALL_PAGE_JUMP_SIZE;
 | 
						|
 | 
						|
                // Code can't straddle page boundaries
 | 
						|
                if(HI_BYTE(vPC) == HI_BYTE(vPC + opSize)  &&  Memory::isFreeRAM(vPC, opSize))
 | 
						|
                {
 | 
						|
                    // Code relocation is not required if requested RAM address is free
 | 
						|
                    Memory::takeFreeRAM(vPC, opcodeSize, true);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                // Get next free code address after page jump prologue and relocate code, (return true)
 | 
						|
                if(!Memory::getNextCodeAddress(Memory::FitAscending, vPC, opSize, nextPC))
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "Validater::checkForRelocation(): Memory alloc at 0x%0x4 of size %d failed\n", vPC, opSize);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                if(!print)
 | 
						|
                {
 | 
						|
                    print = true;
 | 
						|
                    fprintf(stderr, "\n*******************************************************\n");
 | 
						|
                    fprintf(stderr, "*                      Relocating                      \n");
 | 
						|
                    fprintf(stderr, "*******************************************************\n");
 | 
						|
                    fprintf(stderr, "*       Opcode         : Address :    Size     :  New  \n");
 | 
						|
                    fprintf(stderr, "*******************************************************\n");
 | 
						|
                }
 | 
						|
 | 
						|
                numThunks++;
 | 
						|
 | 
						|
                uint16_t newPC = nextPC;
 | 
						|
                if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
 | 
						|
                {
 | 
						|
                    newPC += CALLI_PAGE_JUMP_OFFSET;
 | 
						|
                    totalThunkSize += CALLI_PAGE_JUMP_SIZE + CALLI_PAGE_JUMP_OFFSET;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    newPC += CALL_PAGE_JUMP_OFFSET;
 | 
						|
                    totalThunkSize += CALL_PAGE_JUMP_SIZE + CALL_PAGE_JUMP_OFFSET;
 | 
						|
                }
 | 
						|
                fprintf(stderr, "* %-20s : 0x%04x  :    %2d bytes : 0x%04x\n", opcode.c_str(), vPC, opcodeSize, newPC);
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkForRelocations(void)
 | 
						|
    {
 | 
						|
        std::string line;
 | 
						|
        bool print = false;
 | 
						|
        uint16_t numThunks = 0, totalThunkSize = 0;
 | 
						|
        for(auto itCode=Compiler::getCodeLines().begin(); itCode!=Compiler::getCodeLines().end();)
 | 
						|
        {
 | 
						|
            if(itCode->_vasm.size() == 0)
 | 
						|
            {
 | 
						|
                itCode++;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            int codeLineIndex = int(itCode - Compiler::getCodeLines().begin());
 | 
						|
 | 
						|
            for(auto itVasm=itCode->_vasm.begin(); itVasm!=itCode->_vasm.end();)
 | 
						|
            {
 | 
						|
                uint16_t nextPC;
 | 
						|
                bool excluded = checkForRelocation(itVasm->_opcode, itVasm->_address, nextPC, numThunks, totalThunkSize, print);
 | 
						|
 | 
						|
                if(!itVasm->_pageJump  &&  excluded)
 | 
						|
                {
 | 
						|
                    std::vector<std::string> tokens;
 | 
						|
                    uint16_t currPC = itVasm->_address;
 | 
						|
 | 
						|
                    // Insert PAGE JUMP
 | 
						|
                    int restoreOffset = 0;
 | 
						|
                    std::string nextPClabel = "_page_" + Expression::wordToHexString(nextPC);
 | 
						|
                    if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
 | 
						|
                    {
 | 
						|
                        // CALLI PAGE JUMP
 | 
						|
                        std::string codeCALLI;
 | 
						|
                        int sizeCALLI = Compiler::createVcpuAsm("CALLI", nextPClabel, codeLineIndex, codeCALLI);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm, "CALLI", nextPClabel, codeCALLI,  uint16_t(currPC), sizeCALLI);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // ROMS that don't have CALLI save and restore vAC
 | 
						|
                        std::string codeSTW, codeLDWI, codeCALL, codeLDW;
 | 
						|
#define VAC_SAVE_STACK
 | 
						|
#ifdef VAC_SAVE_STACK
 | 
						|
                        int sizeSTW  = Compiler::createVcpuAsm("STLW", "0xFE", codeLineIndex, codeSTW);
 | 
						|
                        int sizeLDWI = Compiler::createVcpuAsm("LDWI", nextPClabel, codeLineIndex, codeLDWI);
 | 
						|
                        int sizeCALL = Compiler::createVcpuAsm("CALL", "giga_vAC", codeLineIndex, codeCALL);
 | 
						|
                        int sizeLDW  = Compiler::createVcpuAsm("LDLW", "0xFE", codeLineIndex, codeLDW);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 0, "STLW", "0xFE", codeSTW,  uint16_t(currPC), sizeSTW);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "LDWI", nextPClabel, codeLDWI, uint16_t(currPC + sizeSTW), sizeLDWI);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "CALL", "giga_vAC", codeCALL, uint16_t(currPC + sizeSTW + sizeLDWI), sizeCALL);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "LDLW", "0xFE", codeLDW,  uint16_t(nextPC), sizeLDW);
 | 
						|
#else
 | 
						|
#define VAC_SAVE_START  0x00D6
 | 
						|
                        int sizeSTW  = Compiler::createVcpuAsm("STW", Expression::byteToHexString(VAC_SAVE_START), codeLineIndex, codeSTW);
 | 
						|
                        int sizeLDWI = Compiler::createVcpuAsm("LDWI", nextPClabel, codeLineIndex, codeLDWI);
 | 
						|
                        int sizeCALL = Compiler::createVcpuAsm("CALL", "giga_vAC", codeLineIndex, codeCALL);
 | 
						|
                        int sizeLDW  = Compiler::createVcpuAsm("LDW", Expression::byteToHexString(VAC_SAVE_START), codeLineIndex, codeLDW);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 0, "STW",  Expression::byteToHexString(VAC_SAVE_START), codeSTW,  uint16_t(currPC), sizeSTW);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "LDWI", nextPClabel, codeLDWI, uint16_t(currPC + sizeSTW), sizeLDWI);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "CALL", "giga_vAC", codeCALL, uint16_t(currPC + sizeSTW + sizeLDWI), sizeCALL);
 | 
						|
                        itVasm = insertPageJumpInstruction(itCode, itVasm + 1, "LDW",  Expression::byteToHexString(VAC_SAVE_START), codeLDW,  uint16_t(nextPC), sizeLDW);
 | 
						|
#endif
 | 
						|
                        // New page address is offset by size of vAC restore
 | 
						|
                        restoreOffset = sizeLDW;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Fix labels and addresses
 | 
						|
                    int offset = nextPC + restoreOffset - currPC;
 | 
						|
                    adjustLabelAddresses(currPC, offset);
 | 
						|
                    adjustVasmAddresses(codeLineIndex, currPC, offset);
 | 
						|
 | 
						|
                    // Check for existing label, (after label adjustments)
 | 
						|
                    int labelIndex = -1;
 | 
						|
                    std::string labelName;
 | 
						|
                    Compiler::VasmLine* vasmCurr = &itCode->_vasm[itVasm - itCode->_vasm.begin()]; // points to CALLI and LDW
 | 
						|
                    Compiler::VasmLine* vasmNext = &itCode->_vasm[itVasm + 1 - itCode->_vasm.begin()]; // points to instruction after CALLI and after LDW
 | 
						|
                    if(Compiler::findLabel(nextPC) >= 0)
 | 
						|
                    {
 | 
						|
                        labelIndex = Compiler::findLabel(nextPC);
 | 
						|
                        labelName = Compiler::getLabels()[labelIndex]._name;
 | 
						|
                    }
 | 
						|
                    if(labelIndex == -1)
 | 
						|
                    {
 | 
						|
                        // Create CALLI page jump label, (created later in outputCode())
 | 
						|
                        if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
 | 
						|
                        {
 | 
						|
                            // Code referencing these labels must be fixed later in outputLabels, (discarded label addresses must be updated if they match page jump address)
 | 
						|
                            if(vasmNext->_internalLabel.size())
 | 
						|
                            {
 | 
						|
                                Compiler::getDiscardedLabels().push_back({vasmNext->_address, vasmNext->_internalLabel});
 | 
						|
                                Compiler::adjustDiscardedLabels(vasmNext->_internalLabel, vasmNext->_address);
 | 
						|
                            }
 | 
						|
            
 | 
						|
                            vasmNext->_internalLabel = nextPClabel;
 | 
						|
                        }
 | 
						|
                        // Create pre-CALLI page jump label, (created later in outputCode())
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            // Code referencing these labels must be fixed later in outputLabels, (discarded label addresses must be updated if they match page jump address)
 | 
						|
                            if(vasmCurr->_internalLabel.size())
 | 
						|
                            {
 | 
						|
                                Compiler::getDiscardedLabels().push_back({vasmCurr->_address, vasmCurr->_internalLabel});
 | 
						|
                                Compiler::adjustDiscardedLabels(vasmCurr->_internalLabel, vasmCurr->_address);
 | 
						|
                            }
 | 
						|
 | 
						|
                            vasmCurr->_internalLabel = nextPClabel;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    // Existing label at the PAGE JUMP address, so use it
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // Update CALLI page jump label
 | 
						|
                        if(Compiler::getCodeRomType() >= Cpu::ROMv5a)
 | 
						|
                        {
 | 
						|
                            // Macro labels are underscored by default
 | 
						|
                            vasmCurr->_code = (labelName[0] == '_') ? "CALLI" + std::string(OPCODE_TRUNC_SIZE - 4, ' ') + labelName : "CALLI" + std::string(OPCODE_TRUNC_SIZE - 4, ' ') + "_" + labelName;
 | 
						|
                        }
 | 
						|
                        // Update pre-CALLI page jump label
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            // Macro labels are underscored by default
 | 
						|
                            Compiler::VasmLine* vasm = &itCode->_vasm[itVasm - 2 - itCode->_vasm.begin()]; // points to LDWI
 | 
						|
                            vasm->_code = (labelName[0] == '_') ? "LDWI" + std::string(OPCODE_TRUNC_SIZE - 4, ' ') + labelName : "LDWI" + std::string(OPCODE_TRUNC_SIZE - 4, ' ') + "_" + labelName;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                itVasm++;
 | 
						|
            }
 | 
						|
 | 
						|
            itCode++;
 | 
						|
        }
 | 
						|
 | 
						|
        if(print)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "*******************************************************\n");
 | 
						|
            fprintf(stderr, "* Number of page jumps = %4d, RAM used = %5d bytes *\n", numThunks, totalThunkSize);
 | 
						|
            fprintf(stderr, "*******************************************************\n");
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool opcodeHasBranch(const std::string& opcode)
 | 
						|
    {
 | 
						|
        if(opcode == "BRA")                return true;
 | 
						|
        if(opcode == "BEQ")                return true;
 | 
						|
        if(opcode == "BNE")                return true;
 | 
						|
        if(opcode == "BGE")                return true;
 | 
						|
        if(opcode == "BLE")                return true;
 | 
						|
        if(opcode == "BGT")                return true;
 | 
						|
        if(opcode == "BLT")                return true;
 | 
						|
        if(opcode == "%ForNextInc")        return true;
 | 
						|
        if(opcode == "%ForNextDec")        return true;
 | 
						|
        if(opcode == "%ForNextDecZero")    return true;
 | 
						|
        if(opcode == "%ForNextAdd")        return true;
 | 
						|
        if(opcode == "%ForNextSub")        return true;
 | 
						|
        if(opcode == "%ForNextVarAdd")     return true;
 | 
						|
        if(opcode == "%ForNextVarSub")     return true;
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkBranchLabels(void)
 | 
						|
    {
 | 
						|
        for(int i=0; i<int(Compiler::getCodeLines().size()); i++)
 | 
						|
        {
 | 
						|
            // Line number taking into account modules
 | 
						|
            int codeLineStart = Compiler::getCodeLineStart(i);
 | 
						|
 | 
						|
            for(int j=0; j<int(Compiler::getCodeLines()[i]._vasm.size()); j++)
 | 
						|
            {
 | 
						|
                uint16_t opcAddr = Compiler::getCodeLines()[i]._vasm[j]._address;
 | 
						|
                std::string opcode = Compiler::getCodeLines()[i]._vasm[j]._opcode;
 | 
						|
                const std::string& code = Compiler::getCodeLines()[i]._vasm[j]._code;
 | 
						|
                const std::string& basic = Compiler::getCodeLines()[i]._text;
 | 
						|
 | 
						|
                Expression::stripWhitespace(opcode);
 | 
						|
                if(opcodeHasBranch(opcode))
 | 
						|
                {
 | 
						|
                    std::vector<std::string> tokens = Expression::tokenise(code, ' ', false);
 | 
						|
                    if(tokens.size() < 2) continue;
 | 
						|
 | 
						|
                    // Normal branch
 | 
						|
                    std::string operand;
 | 
						|
                    if(tokens.size() == 2)
 | 
						|
                    {
 | 
						|
                        Expression::stripWhitespace(tokens[1]);
 | 
						|
                        operand = tokens[1];
 | 
						|
                    }
 | 
						|
                    // Branch embedded in a FOR NEXT macro
 | 
						|
                    else if(tokens.size() > 2)
 | 
						|
                    {
 | 
						|
                        Expression::stripWhitespace(tokens[2]);
 | 
						|
                        operand = tokens[2];
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Remove underscores from BASIC labels for matching
 | 
						|
                    if(operand.size() > 1  &&  operand[0] == '_') operand.erase(0, 1);
 | 
						|
 | 
						|
                    // Is operand a label?
 | 
						|
                    int labelIndex = Compiler::findLabel(operand);
 | 
						|
                    if(labelIndex >= 0)
 | 
						|
                    {
 | 
						|
                        uint16_t labAddr = Compiler::getLabels()[labelIndex]._address;
 | 
						|
                        if(HI_MASK(opcAddr) != HI_MASK(labAddr))
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "\nValidater::checkBranchLabels() : *** Error ***, %s is branching from 0x%04x to 0x%04x, for '%s' on line %d\n\n", opcode.c_str(), opcAddr, labAddr, basic.c_str(), codeLineStart);
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    // Check internal label
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // Internal labels always have underscores, so put it back
 | 
						|
                        operand.insert(0, 1, '_');
 | 
						|
 | 
						|
                        labelIndex = Compiler::findInternalLabel(operand);
 | 
						|
                        if(labelIndex >= 0)
 | 
						|
                        {
 | 
						|
                            uint16_t labAddr = Compiler::getInternalLabels()[labelIndex]._address;
 | 
						|
                            if(HI_MASK(opcAddr) != HI_MASK(labAddr))
 | 
						|
                            {
 | 
						|
                                fprintf(stderr, "\nValidater::checkBranchLabels() : *** Error ***, %s is branching from 0x%04x to 0x%04x, for '%s' on line %d\n\n", opcode.c_str(), opcAddr, labAddr, basic.c_str(), codeLineStart);
 | 
						|
                                return false;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkStatementBlocks(void)
 | 
						|
    {
 | 
						|
        bool success = true;
 | 
						|
 | 
						|
        // Check FOR NEXT blocks
 | 
						|
        while(!Compiler::getForNextDataStack().empty())
 | 
						|
        {
 | 
						|
            success = false;
 | 
						|
            Compiler::ForNextData forNextData = Compiler::getForNextDataStack().top();
 | 
						|
            int codeLineIndex = forNextData._codeLineIndex;
 | 
						|
            const std::string& code = Compiler::getCodeLines()[codeLineIndex]._code;
 | 
						|
            fprintf(stderr, "Validater::checkStatementBlocks() : Syntax error, missing NEXT statement, for '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
            Compiler::getForNextDataStack().pop();
 | 
						|
        }
 | 
						|
 | 
						|
        // Check ELSE ELSEIF blocks
 | 
						|
        while(!Compiler::getElseIfDataStack().empty())
 | 
						|
        {
 | 
						|
            success = false;
 | 
						|
            Compiler::ElseIfData elseIfData = Compiler::getElseIfDataStack().top();
 | 
						|
            int codeLineIndex = elseIfData._codeLineIndex;
 | 
						|
            const std::string& code = Compiler::getCodeLines()[codeLineIndex]._code;
 | 
						|
            fprintf(stderr, "Validater::checkStatementBlocks() : Syntax error, missing ELSE or ELSEIF statement, for '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
            Compiler::getElseIfDataStack().pop();
 | 
						|
        }
 | 
						|
 | 
						|
        // Check WHILE WEND blocks
 | 
						|
        while(!Compiler::getWhileWendDataStack().empty())
 | 
						|
        {
 | 
						|
            success = false;
 | 
						|
            Compiler::WhileWendData whileWendData = Compiler::getWhileWendDataStack().top();
 | 
						|
            int codeLineIndex = whileWendData._codeLineIndex;
 | 
						|
            const std::string& code = Compiler::getCodeLines()[codeLineIndex]._code;
 | 
						|
            fprintf(stderr, "Validater::checkStatementBlocks() : Syntax error, missing WEND statement, for '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
            Compiler::getWhileWendDataStack().pop();
 | 
						|
        }
 | 
						|
 | 
						|
        // Check DO UNTIL blocks
 | 
						|
        while(!Compiler::getRepeatUntilDataStack().empty())
 | 
						|
        {
 | 
						|
            success = false;
 | 
						|
            Compiler::RepeatUntilData repeatUntilData = Compiler::getRepeatUntilDataStack().top();
 | 
						|
            int codeLineIndex = repeatUntilData._codeLineIndex;
 | 
						|
            const std::string& code = Compiler::getCodeLines()[codeLineIndex]._code;
 | 
						|
            fprintf(stderr, "Validater::checkStatementBlocks() : Syntax error, missing UNTIL statement, for '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
            Compiler::getRepeatUntilDataStack().pop();
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkCallProcFuncData(void)
 | 
						|
    {
 | 
						|
        bool success = true;
 | 
						|
 | 
						|
        // Check PROC's corresponding CALL's
 | 
						|
        for(auto it=Compiler::getCallDataMap().begin(); it!=Compiler::getCallDataMap().end(); ++it)
 | 
						|
        {
 | 
						|
            int numParams = it->second._numParams;
 | 
						|
            int codeLineIndex = it->second._codeLineIndex;
 | 
						|
            const std::string& procName = it->second._name;
 | 
						|
            const std::string& code = Compiler::getCodeLines()[codeLineIndex]._code;
 | 
						|
 | 
						|
            if(Compiler::getProcDataMap().find(procName) == Compiler::getProcDataMap().end())
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Validator::checkCallProcFuncData() : Syntax error, 'CALL <NAME>' cannot find a corresponding 'PROC <NAME>', in '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
                success = false;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            if(Compiler::getProcDataMap()[procName]._numParams != numParams)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Validator::checkCallProcFuncData() : Syntax error, 'CALL <NAME>' has incorrect number of parameters compared to 'PROC <NAME>', in '%s' on line %d\n", code.c_str(), codeLineIndex);
 | 
						|
                success = false;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkRuntimeVersion(void)
 | 
						|
    {
 | 
						|
        int16_t runtimeVersion = Assembler::getRuntimeVersion();
 | 
						|
        if(runtimeVersion != RUNTIME_VERSION)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "\n*************************************************************************************************\n");
 | 
						|
            fprintf(stderr, "* Expected runtime version %04d : Found runtime version %04d\n", RUNTIME_VERSION, runtimeVersion);
 | 
						|
            fprintf(stderr, "*************************************************************************************************\n\n");
 | 
						|
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
} |