2968 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2968 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <cmath>
 | 
						|
#include <vector>
 | 
						|
#include <stack>
 | 
						|
#include <fstream>
 | 
						|
#include <sstream>
 | 
						|
#include <iomanip>
 | 
						|
#include <iterator>
 | 
						|
#include <algorithm>
 | 
						|
#include <cstdarg>
 | 
						|
 | 
						|
#include "memory.h"
 | 
						|
#include "cpu.h"
 | 
						|
#include "audio.h"
 | 
						|
#include "editor.h"
 | 
						|
#include "loader.h"
 | 
						|
#include "assembler.h"
 | 
						|
#include "expression.h"
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
#include "cpu.h"
 | 
						|
#include "editor.h"
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#define BRANCH_ADJUSTMENT 2
 | 
						|
#define MAX_DASM_LINES    30
 | 
						|
 | 
						|
 | 
						|
namespace Assembler
 | 
						|
{
 | 
						|
    enum ParseType {PreProcessPass=0, MnemonicPass, CodePass, NumParseTypes};
 | 
						|
    enum EvaluateResult {Failed=-1, NotFound, Reserved, Duplicate, Skipped, Success};
 | 
						|
    enum AddressMode {D_AC=0b00000000, X_AC=0b00000100, YD_AC=0b00001000, YX_AC=0b00001100, D_X=0b00010000, D_Y=0b00010100, D_OUT=0b00011000, YXpp_OUT=0b00011100};
 | 
						|
    enum BusMode {D=0b00000000, RAM=0b00000001, AC=0b00000010, IN=0b00000011};
 | 
						|
    enum ReservedWords {CallTable=0, StartAddress, SingleStepWatch, DisableUpload, CpuUsageAddressA, CpuUsageAddressB, INCLUDE, MACRO, ENDM, GPRINTF, NumReservedWords};
 | 
						|
 | 
						|
 | 
						|
    struct Label
 | 
						|
    {
 | 
						|
        uint16_t _address;
 | 
						|
        std::string _name;
 | 
						|
    };
 | 
						|
 | 
						|
    struct Equate
 | 
						|
    {
 | 
						|
        bool _isCustomAddress;
 | 
						|
        uint16_t _operand;
 | 
						|
        std::string _name;
 | 
						|
    };
 | 
						|
 | 
						|
    struct Instruction
 | 
						|
    {
 | 
						|
        bool _isRomAddress;
 | 
						|
        bool _isCustomAddress;
 | 
						|
        ByteSize _byteSize;
 | 
						|
        uint8_t _opcode;
 | 
						|
        uint8_t _operand0;
 | 
						|
        uint8_t _operand1;
 | 
						|
        uint16_t _address;
 | 
						|
        OpcodeType _opcodeType;
 | 
						|
    };
 | 
						|
 | 
						|
    struct CallTableEntry
 | 
						|
    {
 | 
						|
        uint8_t _operand;
 | 
						|
        uint16_t _address;
 | 
						|
    };
 | 
						|
 | 
						|
    struct Macro
 | 
						|
    {
 | 
						|
        bool _complete = false;
 | 
						|
        bool _fromInclude = false;
 | 
						|
        int _fileStartLine;
 | 
						|
        std::string _name;
 | 
						|
        std::string _filename;
 | 
						|
        std::vector<std::string> _params;
 | 
						|
        std::vector<std::string> _lines;
 | 
						|
    };
 | 
						|
 | 
						|
 | 
						|
    int _lineNumber;
 | 
						|
 | 
						|
    uint8_t _vSpMin = 0x00;
 | 
						|
 | 
						|
    uint16_t _byteCount = 0;
 | 
						|
    uint16_t _callTablePtr = 0x0000;
 | 
						|
    uint16_t _startAddress = DEFAULT_EXEC_ADDRESS;
 | 
						|
    uint16_t _currentAddress = _startAddress;
 | 
						|
    uint16_t _currDasmByteCount = 1, _prevDasmByteCount = 1;
 | 
						|
    uint16_t _currDasmPageByteCount = 0, _prevDasmPageByteCount = 0;
 | 
						|
 | 
						|
    std::string _includePath = ".";
 | 
						|
 | 
						|
    std::vector<Label> _labels;
 | 
						|
    std::vector<Equate> _equates;
 | 
						|
    std::vector<Instruction> _instructions;
 | 
						|
    std::vector<ByteCode> _byteCode;
 | 
						|
    std::vector<CallTableEntry> _callTableEntries;
 | 
						|
    std::vector<std::string> _reservedWords;
 | 
						|
    std::vector<DasmCode> _disassembledCode;
 | 
						|
 | 
						|
    std::map<std::string, InstructionType> _asmOpcodes;
 | 
						|
    std::map<uint8_t, InstructionDasm> _vcpuOpcodes;
 | 
						|
    std::map<uint8_t, InstructionDasm> _nativeOpcodes;
 | 
						|
    std::map<uint16_t, Gprintf> _gprintfs;
 | 
						|
    std::map<std::string, Define> _defines;
 | 
						|
    std::stack<std::string> _currentDefine;
 | 
						|
 | 
						|
 | 
						|
    uint8_t getvSpMin(void) {return _vSpMin;}
 | 
						|
    uint16_t getStartAddress(void) {return _startAddress;}
 | 
						|
    int getPrevDasmByteCount(void) {return _prevDasmByteCount;}
 | 
						|
    int getCurrDasmByteCount(void) {return _currDasmByteCount;}
 | 
						|
    int getPrevDasmPageByteCount(void) {return _prevDasmPageByteCount;}
 | 
						|
    int getCurrDasmPageByteCount(void) {return _currDasmPageByteCount;}
 | 
						|
    int getDisassembledCodeSize(void) {return int(_disassembledCode.size());}
 | 
						|
    const std::string& getIncludePath(void) {return _includePath;}
 | 
						|
    DasmCode* getDisassembledCode(int index) {return &_disassembledCode[index % _disassembledCode.size()];}
 | 
						|
 | 
						|
    void setvSpMin(uint8_t vSpMin) {_vSpMin = vSpMin;}
 | 
						|
    void setIncludePath(const std::string& includePath) {_includePath = includePath;}
 | 
						|
 | 
						|
 | 
						|
    int getAsmOpcodeSize(const std::string& opcodeStr)
 | 
						|
    {
 | 
						|
        if(opcodeStr[0] == ';') return 0;
 | 
						|
 | 
						|
        if(_asmOpcodes.find(opcodeStr) != _asmOpcodes.end())
 | 
						|
        {
 | 
						|
            return _asmOpcodes[opcodeStr]._byteSize;
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    int getAsmOpcodeSizeText(const std::string& textStr)
 | 
						|
    {
 | 
						|
        for(auto it=_asmOpcodes.begin(); it!=_asmOpcodes.end(); ++it)
 | 
						|
        {
 | 
						|
            if(textStr.find(it->first) != std::string::npos  &&  _asmOpcodes.find(it->first) != _asmOpcodes.end())
 | 
						|
            {
 | 
						|
                return _asmOpcodes[it->first]._byteSize;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    void initialiseOpcodes(void)
 | 
						|
    {
 | 
						|
        // Gigatron vCPU instructions
 | 
						|
        _asmOpcodes["ST"   ] = {0x5E, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["STW"  ] = {0x2B, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["STLW" ] = {0xEC, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LD"   ] = {0x1A, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LDI"  ] = {0x59, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LDWI" ] = {0x11, 0x00, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["LDW"  ] = {0x21, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LDLW" ] = {0xEE, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ADDW" ] = {0x99, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["SUBW" ] = {0xB8, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ADDI" ] = {0xE3, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["SUBI" ] = {0xE6, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LSLW" ] = {0xE9, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["INC"  ] = {0x93, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ANDI" ] = {0x82, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ANDW" ] = {0xF8, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ORI"  ] = {0x88, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["ORW"  ] = {0xFA, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["XORI" ] = {0x8C, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["XORW" ] = {0xFC, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["PEEK" ] = {0xAD, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["DEEK" ] = {0xF6, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["POKE" ] = {0xF0, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["DOKE" ] = {0xF3, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["LUP"  ] = {0x7F, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["BRA"  ] = {0x90, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["CALL" ] = {0xCF, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["RET"  ] = {0xFF, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["PUSH" ] = {0x75, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["POP"  ] = {0x63, 0x00, OneByte,    vCpu};
 | 
						|
        _asmOpcodes["ALLOC"] = {0xDF, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["SYS"  ] = {0xB4, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["HALT" ] = {0xB4, 0x80, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["DEF"  ] = {0xCD, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["CALLI"] = {0x85, 0x00, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["CMPHS"] = {0x1F, 0x00, TwoBytes,   vCpu};
 | 
						|
        _asmOpcodes["CMPHU"] = {0x97, 0x00, TwoBytes,   vCpu};
 | 
						|
 | 
						|
        // Gigatron vCPU branch instructions
 | 
						|
        _asmOpcodes["BEQ"] = {0x35, 0x3F, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["BNE"] = {0x35, 0x72, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["BLT"] = {0x35, 0x50, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["BGT"] = {0x35, 0x4D, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["BLE"] = {0x35, 0x56, ThreeBytes, vCpu};
 | 
						|
        _asmOpcodes["BGE"] = {0x35, 0x53, ThreeBytes, vCpu};
 | 
						|
 | 
						|
        // Reserved assembler opcodes
 | 
						|
        _asmOpcodes["DB" ] = {0x00, 0x00, TwoBytes,   ReservedDB };
 | 
						|
        _asmOpcodes["DW" ] = {0x00, 0x00, ThreeBytes, ReservedDW };
 | 
						|
        _asmOpcodes["DBR"] = {0x00, 0x00, TwoBytes,   ReservedDBR};
 | 
						|
        _asmOpcodes["DWR"] = {0x00, 0x00, ThreeBytes, ReservedDWR};
 | 
						|
                                                                           
 | 
						|
        // Gigatron native instructions                                    
 | 
						|
        _asmOpcodes[".LD"  ] = {0x00, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".NOP" ] = {0x02, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".ANDA"] = {0x20, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".ORA" ] = {0x40, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".XORA"] = {0x60, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".ADDA"] = {0x80, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".SUBA"] = {0xA0, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".ST"  ] = {0xC0, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".JMP" ] = {0xE0, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BGT" ] = {0xE4, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BLT" ] = {0xE8, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BNE" ] = {0xEC, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BEQ" ] = {0xF0, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BGE" ] = {0xF4, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BLE" ] = {0xF8, 0x00, TwoBytes, Native};
 | 
						|
        _asmOpcodes[".BRA" ] = {0xFC, 0x00, TwoBytes, Native};
 | 
						|
 | 
						|
        // Gigatron vCPU instructions
 | 
						|
        _vcpuOpcodes[0x5E] = {0x5E, 0x00, TwoBytes,   vCpu, "ST"   };
 | 
						|
        _vcpuOpcodes[0x2B] = {0x2B, 0x00, TwoBytes,   vCpu, "STW"  };
 | 
						|
        _vcpuOpcodes[0xEC] = {0xEC, 0x00, TwoBytes,   vCpu, "STLW" };
 | 
						|
        _vcpuOpcodes[0x1A] = {0x1A, 0x00, TwoBytes,   vCpu, "LD"   };
 | 
						|
        _vcpuOpcodes[0x59] = {0x59, 0x00, TwoBytes,   vCpu, "LDI"  };
 | 
						|
        _vcpuOpcodes[0x11] = {0x11, 0x00, ThreeBytes, vCpu, "LDWI" };
 | 
						|
        _vcpuOpcodes[0x21] = {0x21, 0x00, TwoBytes,   vCpu, "LDW"  };
 | 
						|
        _vcpuOpcodes[0xEE] = {0xEE, 0x00, TwoBytes,   vCpu, "LDLW" };
 | 
						|
        _vcpuOpcodes[0x99] = {0x99, 0x00, TwoBytes,   vCpu, "ADDW" };
 | 
						|
        _vcpuOpcodes[0xB8] = {0xB8, 0x00, TwoBytes,   vCpu, "SUBW" };
 | 
						|
        _vcpuOpcodes[0xE3] = {0xE3, 0x00, TwoBytes,   vCpu, "ADDI" };
 | 
						|
        _vcpuOpcodes[0xE6] = {0xE6, 0x00, TwoBytes,   vCpu, "SUBI" };
 | 
						|
        _vcpuOpcodes[0xE9] = {0xE9, 0x00, OneByte,    vCpu, "LSLW" };
 | 
						|
        _vcpuOpcodes[0x93] = {0x93, 0x00, TwoBytes,   vCpu, "INC"  };
 | 
						|
        _vcpuOpcodes[0x82] = {0x82, 0x00, TwoBytes,   vCpu, "ANDI" };
 | 
						|
        _vcpuOpcodes[0xF8] = {0xF8, 0x00, TwoBytes,   vCpu, "ANDW" };
 | 
						|
        _vcpuOpcodes[0x88] = {0x88, 0x00, TwoBytes,   vCpu, "ORI"  };
 | 
						|
        _vcpuOpcodes[0xFA] = {0xFA, 0x00, TwoBytes,   vCpu, "ORW"  };
 | 
						|
        _vcpuOpcodes[0x8C] = {0x8C, 0x00, TwoBytes,   vCpu, "XORI" };
 | 
						|
        _vcpuOpcodes[0xFC] = {0xFC, 0x00, TwoBytes,   vCpu, "XORW" };
 | 
						|
        _vcpuOpcodes[0xAD] = {0xAD, 0x00, OneByte,    vCpu, "PEEK" };
 | 
						|
        _vcpuOpcodes[0xF6] = {0xF6, 0x00, OneByte,    vCpu, "DEEK" };
 | 
						|
        _vcpuOpcodes[0xF0] = {0xF0, 0x00, TwoBytes,   vCpu, "POKE" };
 | 
						|
        _vcpuOpcodes[0xF3] = {0xF3, 0x00, TwoBytes,   vCpu, "DOKE" };
 | 
						|
        _vcpuOpcodes[0x7F] = {0x7F, 0x00, TwoBytes,   vCpu, "LUP"  };
 | 
						|
        _vcpuOpcodes[0x90] = {0x90, 0x00, TwoBytes,   vCpu, "BRA"  };
 | 
						|
        _vcpuOpcodes[0xCF] = {0xCF, 0x00, TwoBytes,   vCpu, "CALL" };
 | 
						|
        _vcpuOpcodes[0xFF] = {0xFF, 0x00, OneByte,    vCpu, "RET"  };
 | 
						|
        _vcpuOpcodes[0x75] = {0x75, 0x00, OneByte,    vCpu, "PUSH" };
 | 
						|
        _vcpuOpcodes[0x63] = {0x63, 0x00, OneByte,    vCpu, "POP"  };
 | 
						|
        _vcpuOpcodes[0xDF] = {0xDF, 0x00, TwoBytes,   vCpu, "ALLOC"};
 | 
						|
        _vcpuOpcodes[0xB4] = {0xB4, 0x00, TwoBytes,   vCpu, "SYS"  };
 | 
						|
        _vcpuOpcodes[0xCD] = {0xCD, 0x00, TwoBytes,   vCpu, "DEF"  };
 | 
						|
        _vcpuOpcodes[0x85] = {0x85, 0x00, ThreeBytes, vCpu, "CALLI"};
 | 
						|
        _vcpuOpcodes[0x1F] = {0x1F, 0x00, TwoBytes,   vCpu, "CMPHS"};
 | 
						|
        _vcpuOpcodes[0x97] = {0x97, 0x00, TwoBytes,   vCpu, "CMPHU"};
 | 
						|
 | 
						|
        // Gigatron vCPU branch instructions, (this works because condition code is still unique compared to opcodes)
 | 
						|
        _vcpuOpcodes[0x3F] = {OPCODE_V_BCC, 0x3F, ThreeBytes, vCpu, "BEQ"};
 | 
						|
        _vcpuOpcodes[0x72] = {OPCODE_V_BCC, 0x72, ThreeBytes, vCpu, "BNE"};
 | 
						|
        _vcpuOpcodes[0x50] = {OPCODE_V_BCC, 0x50, ThreeBytes, vCpu, "BLT"};
 | 
						|
        _vcpuOpcodes[0x4D] = {OPCODE_V_BCC, 0x4D, ThreeBytes, vCpu, "BGT"};
 | 
						|
        _vcpuOpcodes[0x56] = {OPCODE_V_BCC, 0x56, ThreeBytes, vCpu, "BLE"};
 | 
						|
        _vcpuOpcodes[0x53] = {OPCODE_V_BCC, 0x53, ThreeBytes, vCpu, "BGE"};
 | 
						|
 | 
						|
        // Gigatron native instructions
 | 
						|
        _nativeOpcodes[0x00] = {0x00, 0x00, TwoBytes, Native, "LD"  };
 | 
						|
        _nativeOpcodes[0x02] = {0x02, 0x00, TwoBytes, Native, "NOP" };
 | 
						|
        _nativeOpcodes[0x20] = {0x20, 0x00, TwoBytes, Native, "ANDA"};
 | 
						|
        _nativeOpcodes[0x40] = {0x40, 0x00, TwoBytes, Native, "ORA" };
 | 
						|
        _nativeOpcodes[0x60] = {0x60, 0x00, TwoBytes, Native, "XORA"};
 | 
						|
        _nativeOpcodes[0x80] = {0x80, 0x00, TwoBytes, Native, "ADDA"};
 | 
						|
        _nativeOpcodes[0xA0] = {0xA0, 0x00, TwoBytes, Native, "SUBA"};
 | 
						|
        _nativeOpcodes[0xC0] = {0xC0, 0x00, TwoBytes, Native, "ST"  };
 | 
						|
        _nativeOpcodes[0xE0] = {0xE0, 0x00, TwoBytes, Native, "JMP" };
 | 
						|
        _nativeOpcodes[0xE4] = {0xE4, 0x00, TwoBytes, Native, "BGT" };
 | 
						|
        _nativeOpcodes[0xE8] = {0xE8, 0x00, TwoBytes, Native, "BLT" };
 | 
						|
        _nativeOpcodes[0xEC] = {0xEC, 0x00, TwoBytes, Native, "BNE" };
 | 
						|
        _nativeOpcodes[0xF0] = {0xF0, 0x00, TwoBytes, Native, "BEQ" };
 | 
						|
        _nativeOpcodes[0xF4] = {0xF4, 0x00, TwoBytes, Native, "BGE" };
 | 
						|
        _nativeOpcodes[0xF8] = {0xF8, 0x00, TwoBytes, Native, "BLE" };
 | 
						|
        _nativeOpcodes[0xFC] = {0xFC, 0x00, TwoBytes, Native, "BRA" };
 | 
						|
    }
 | 
						|
 | 
						|
    void initialise(void)
 | 
						|
    {
 | 
						|
        _reservedWords.push_back("_CALLTABLE_");
 | 
						|
        _reservedWords.push_back("_BREAKPOINT_");
 | 
						|
        _reservedWords.push_back("_STARTADDRESS_");
 | 
						|
        _reservedWords.push_back("_SINGLESTEPWATCH_");
 | 
						|
        _reservedWords.push_back("_DISABLEUPLOAD_");
 | 
						|
        _reservedWords.push_back("_CPUUSAGEADDRESSA_");
 | 
						|
        _reservedWords.push_back("_CPUUSAGEADDRESSB_");
 | 
						|
        _reservedWords.push_back("%INCLUDE");
 | 
						|
        _reservedWords.push_back("%DEFINE");
 | 
						|
        _reservedWords.push_back("%IF");
 | 
						|
        _reservedWords.push_back("%ELSE");
 | 
						|
        _reservedWords.push_back("%ELSEIF");
 | 
						|
        _reservedWords.push_back("%ENDIF");
 | 
						|
        _reservedWords.push_back("%MACRO");
 | 
						|
        _reservedWords.push_back("%ENDM");
 | 
						|
        _reservedWords.push_back("%SUB");
 | 
						|
        _reservedWords.push_back("%ENDS");
 | 
						|
        _reservedWords.push_back("GPRINTF");
 | 
						|
 | 
						|
        initialiseOpcodes();
 | 
						|
    }
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
    void getDasmCurrAndPrevByteSize(uint16_t address, ByteSize byteSize)
 | 
						|
    {
 | 
						|
        // Save current and previous instruction lengths
 | 
						|
        if(_disassembledCode.size() == 0)
 | 
						|
        {
 | 
						|
            _currDasmByteCount = uint16_t(byteSize);
 | 
						|
 | 
						|
            // Attempt to get bytesize of previous instruction
 | 
						|
            for(uint16_t addr=address-1; addr>=address-3; --addr)
 | 
						|
            {
 | 
						|
                uint8_t size = uint8_t(address - addr); // no instruction is longer than 3 bytes
 | 
						|
                uint8_t inst = Cpu::getRAM(addr);
 | 
						|
                if(inst == OPCODE_V_BCC) inst = Cpu::getRAM(addr + 1);
 | 
						|
                if(_vcpuOpcodes.find(inst) != _vcpuOpcodes.end()  &&  _vcpuOpcodes[inst]._opcodeType == vCpu  &&  _vcpuOpcodes[inst]._byteSize == size)
 | 
						|
                {
 | 
						|
                    _prevDasmByteCount = size;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void getDasmCurrAndPrevPageByteSize(int pageSize)
 | 
						|
    {
 | 
						|
        // Current page size
 | 
						|
        _currDasmPageByteCount = 0;
 | 
						|
        for(int i=0; i<pageSize; i++) _currDasmPageByteCount += _disassembledCode[i]._byteSize;
 | 
						|
 | 
						|
        // Previous page size
 | 
						|
        _prevDasmPageByteCount = 0;
 | 
						|
        uint16_t address = _disassembledCode[0]._address;
 | 
						|
        for(int i=0; i<pageSize; i++)
 | 
						|
        {
 | 
						|
            // Get bytesize of previous page worth of instructions
 | 
						|
            bool foundInstruction = false;
 | 
						|
            
 | 
						|
            if(address < 4) continue;
 | 
						|
 | 
						|
            for(uint16_t addr=address-1; addr>=address-3; --addr)
 | 
						|
            {
 | 
						|
                uint8_t size = uint8_t(address - addr); // no instruction is longer than 3 bytes
 | 
						|
                uint8_t inst = Cpu::getRAM(addr);
 | 
						|
                if(inst == OPCODE_V_BCC) inst = Cpu::getRAM(addr + 1);
 | 
						|
                if(_vcpuOpcodes.find(inst) != _vcpuOpcodes.end()  &&  _vcpuOpcodes[inst]._opcodeType == vCpu  &&  _vcpuOpcodes[inst]._byteSize == size)
 | 
						|
                {
 | 
						|
                    foundInstruction = true;
 | 
						|
                    _prevDasmPageByteCount += size;
 | 
						|
                    address -= size;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if(!foundInstruction)
 | 
						|
            {
 | 
						|
                _prevDasmPageByteCount++;
 | 
						|
                address--;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    std::string removeBrackets(const char* str)
 | 
						|
    {
 | 
						|
        std::string string = str;
 | 
						|
        Expression::stripChars(string, "[]");
 | 
						|
        return string;
 | 
						|
    }
 | 
						|
 | 
						|
    // Adapted from disassemble() in Core\asm.py
 | 
						|
    bool getNativeMnemonic(uint8_t instruction, uint8_t data, char* mnemonic)
 | 
						|
    {
 | 
						|
        uint8_t inst, addr, bus;
 | 
						|
 | 
						|
        // Special case NOP
 | 
						|
        if(instruction == 0x02  &&  data == 0x00)
 | 
						|
        {
 | 
						|
            strcpy(mnemonic, _nativeOpcodes[instruction]._mnemonic.c_str());
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        inst = instruction & 0xE0;
 | 
						|
        addr = instruction & 0x1C;
 | 
						|
        bus  = instruction & 0x03;
 | 
						|
 | 
						|
        bool store = (inst == 0xC0);
 | 
						|
        bool jump = (inst == 0xE0);
 | 
						|
 | 
						|
        if(_nativeOpcodes.find(inst) == _nativeOpcodes.end()) return false;
 | 
						|
 | 
						|
        // Instruction mnemonic, jump = 0xE0 + (condition codes)
 | 
						|
        char instStr[8];
 | 
						|
        (!jump) ? strcpy(instStr, _nativeOpcodes[inst]._mnemonic.c_str()) : strcpy(instStr, _nativeOpcodes[0xE0 + addr]._mnemonic.c_str());
 | 
						|
 | 
						|
        // Effective address string
 | 
						|
        char addrStr[12];
 | 
						|
        char regStr[4];
 | 
						|
        if(!jump)
 | 
						|
        {
 | 
						|
            switch(addr)
 | 
						|
            {
 | 
						|
                case EA_0D_AC:    sprintf(addrStr, "[$%02x]",   data); sprintf(regStr, "AC");  break;
 | 
						|
                case EA_0X_AC:    sprintf(addrStr, "[X]");             sprintf(regStr, "AC");  break;
 | 
						|
                case EA_YD_AC:    sprintf(addrStr, "[Y,$%02x]", data); sprintf(regStr, "AC");  break;
 | 
						|
                case EA_YX_AC:    sprintf(addrStr, "[Y,X]");           sprintf(regStr, "AC");  break;
 | 
						|
                case EA_0D_X:     sprintf(addrStr, "[$%02x]",   data); sprintf(regStr, "X");   break;
 | 
						|
                case EA_0D_Y:     sprintf(addrStr, "[$%02x]",   data); sprintf(regStr, "Y");   break;
 | 
						|
                case EA_0D_OUT:   sprintf(addrStr, "[$%02x]",   data); sprintf(regStr, "OUT"); break;
 | 
						|
                case EA_YX_OUTIX: sprintf(addrStr, "[Y,X++]");         sprintf(regStr, "OUT"); break;
 | 
						|
 | 
						|
                default: break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            sprintf(addrStr, "[$%02x]", data);
 | 
						|
        }
 | 
						|
 | 
						|
        // Bus string
 | 
						|
        char busStr[8];
 | 
						|
        switch(bus)
 | 
						|
        {
 | 
						|
            case BUS_D:   sprintf(busStr, "$%02x", data);                          break;
 | 
						|
            case BUS_RAM: (!store) ? strcpy(busStr, addrStr) : strcpy(busStr, ""); break;
 | 
						|
            case BUS_AC:  strcpy(busStr, "AC");                                    break;
 | 
						|
            case BUS_IN:  strcpy(busStr, "IN");                                    break;
 | 
						|
 | 
						|
            default: break;
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Compose instruction string
 | 
						|
        if(!jump)
 | 
						|
        {
 | 
						|
            if(store)
 | 
						|
            {
 | 
						|
                char storeStr[32];
 | 
						|
                (bus == BUS_AC) ? sprintf(storeStr, "%-4s %s", instStr, addrStr) : sprintf(storeStr, "%-4s %s,%s", instStr, busStr, addrStr);
 | 
						|
                if(bus == BUS_RAM) sprintf(storeStr, "CTRL %s", removeBrackets(addrStr).c_str());
 | 
						|
                if(addr == EA_0D_X  ||  addr == EA_0D_Y) sprintf(mnemonic, "%s,%s", storeStr, regStr);
 | 
						|
                else strcpy(mnemonic, storeStr);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // if reg == AC
 | 
						|
                (addr <= EA_YX_AC) ? sprintf(mnemonic, "%-4s %s", instStr, busStr) : sprintf(mnemonic, "%-4s %s,%s", instStr, busStr, regStr);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Compose jump string
 | 
						|
        else
 | 
						|
        {
 | 
						|
            char jumpStr[32];
 | 
						|
            switch(addr)
 | 
						|
            {
 | 
						|
                case BRA_CC_FAR: sprintf(jumpStr, "%-4s Y,", instStr); break;
 | 
						|
                default:         sprintf(jumpStr, "%-4s ",   instStr); break;
 | 
						|
            }
 | 
						|
 | 
						|
            sprintf(mnemonic, "%-4s%s", jumpStr, busStr);
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    int disassemble(uint16_t address)
 | 
						|
    {
 | 
						|
        _disassembledCode.clear();
 | 
						|
 | 
						|
        _currDasmByteCount = 1;
 | 
						|
        _prevDasmByteCount = 1;
 | 
						|
 | 
						|
        while(_disassembledCode.size() < MAX_DASM_LINES)
 | 
						|
        {
 | 
						|
            char dasmText[32];
 | 
						|
            DasmCode dasmCode;
 | 
						|
            ByteSize byteSize = OneByte;
 | 
						|
            uint8_t instruction = 0, data0 = 0, data1 = 0;
 | 
						|
 | 
						|
            Editor::MemoryMode memoryMode = Editor::getMemoryMode();
 | 
						|
            switch(memoryMode)
 | 
						|
            {
 | 
						|
                // Native instructions
 | 
						|
                case Editor::ROM0: 
 | 
						|
                case Editor::ROM1: 
 | 
						|
                {
 | 
						|
                    instruction = Cpu::getROM(address, 0);
 | 
						|
                    data0 = Cpu::getROM(address, 1);
 | 
						|
                    data1 = 0;
 | 
						|
 | 
						|
                    char mnemonic[24];
 | 
						|
                    if(!getNativeMnemonic(instruction, data0, mnemonic))
 | 
						|
                    {
 | 
						|
                        sprintf(dasmText, "%04x  $%02x $%02x", address, instruction, data0);
 | 
						|
                        dasmCode._address = address;
 | 
						|
                        address++;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    sprintf(dasmText, "%04x  %s", address, mnemonic);
 | 
						|
                    dasmCode._address = address;
 | 
						|
                    address++;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                // vCPU instructions
 | 
						|
                case Editor::RAM:
 | 
						|
                {
 | 
						|
                    instruction = Cpu::getRAM(address);
 | 
						|
                    data0 = Cpu::getRAM(address + 1);
 | 
						|
                    data1 = Cpu::getRAM(address + 2);
 | 
						|
 | 
						|
                    // Invalid instruction or invalid address space
 | 
						|
                    if((_vcpuOpcodes.find(instruction) == _vcpuOpcodes.end()  &&  instruction != OPCODE_V_BCC)  ||
 | 
						|
                       (address >= GIGA_CH0_WAV_A  &&  address <= GIGA_CH0_OSC_H) ||  (address >= GIGA_CH1_WAV_A  &&  address <= GIGA_CH1_OSC_H) ||
 | 
						|
                       (address >= GIGA_CH2_WAV_A  &&  address <= GIGA_CH2_OSC_H) ||  (address >= GIGA_CH3_WAV_A  &&  address <= GIGA_CH3_OSC_H))
 | 
						|
                    {
 | 
						|
                        sprintf(dasmText, "%04x  $%02x", address, instruction);
 | 
						|
                        dasmCode._address = address;
 | 
						|
                        address = (address + 1) & (Memory::getSizeRAM() - 1);
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Branch instructions
 | 
						|
                    bool foundBranch = false;
 | 
						|
                    if(instruction == OPCODE_V_BCC)
 | 
						|
                    {
 | 
						|
                        instruction = data0;
 | 
						|
                        if(_vcpuOpcodes.find(instruction) == _vcpuOpcodes.end())
 | 
						|
                        {
 | 
						|
                            sprintf(dasmText, "%04x  $%02x", address, instruction);
 | 
						|
                            dasmCode._address = address;
 | 
						|
                            address = (address + 1) & (Memory::getSizeRAM() - 1);
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                        foundBranch = true;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Halt instruction
 | 
						|
                    if(instruction == OPCODE_V_HALT  &&  data0 == OPERAND_V_HALT)
 | 
						|
                    {
 | 
						|
                        byteSize = TwoBytes;
 | 
						|
                        sprintf(dasmText, "%04x  %-5s", address, "HALT");
 | 
						|
                    }
 | 
						|
                    // Valid instruction
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        byteSize = _vcpuOpcodes[instruction]._byteSize;
 | 
						|
                        switch(byteSize)
 | 
						|
                        {
 | 
						|
                            case OneByte:  sprintf(dasmText, "%04x  %-5s", address, _vcpuOpcodes[instruction]._mnemonic.c_str());              break;
 | 
						|
                            case TwoBytes: sprintf(dasmText, "%04x  %-5s $%02x", address, _vcpuOpcodes[instruction]._mnemonic.c_str(), data0); break;
 | 
						|
                            case ThreeBytes: (foundBranch) ? sprintf(dasmText, "%04x  %-5s $%02x", address, _vcpuOpcodes[instruction]._mnemonic.c_str(), data1) : sprintf(dasmText, "%04x  %-5s $%02x%02x", address, _vcpuOpcodes[instruction]._mnemonic.c_str(), data1, data0); break;
 | 
						|
 | 
						|
                            default: break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    dasmCode._address = address;
 | 
						|
                    address = uint16_t((address + byteSize) & (Memory::getSizeRAM() - 1));
 | 
						|
 | 
						|
                    // Save current and previous instruction sizes to allow scrolling
 | 
						|
                    getDasmCurrAndPrevByteSize(dasmCode._address, byteSize);
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                default: break;
 | 
						|
            }
 | 
						|
 | 
						|
            std::string dasmCodeText = std::string(dasmText);
 | 
						|
            dasmCode._instruction = instruction;
 | 
						|
            dasmCode._byteSize = uint8_t(byteSize);
 | 
						|
            dasmCode._data0 = data0;
 | 
						|
            dasmCode._data1 = data1;
 | 
						|
            dasmCode._text = (memoryMode == Editor::RAM) ? Expression::strToUpper(dasmCodeText) : Expression::strToLower(dasmCodeText);
 | 
						|
 | 
						|
            _disassembledCode.push_back(dasmCode);
 | 
						|
        }
 | 
						|
 | 
						|
        // Save current and previous page instruction sizes to allow page scrolling
 | 
						|
        if(Editor::getMemoryMode() == Editor::RAM)
 | 
						|
        {
 | 
						|
            getDasmCurrAndPrevPageByteSize(MAX_DASM_LINES);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            _currDasmPageByteCount = MAX_DASM_LINES;
 | 
						|
            _prevDasmPageByteCount = MAX_DASM_LINES;
 | 
						|
        }
 | 
						|
 | 
						|
        return int(_disassembledCode.size());
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    // Returns true when finished
 | 
						|
    bool getNextAssembledByte(ByteCode& byteCode, bool debug)
 | 
						|
    {
 | 
						|
        static bool isUserCode = false;
 | 
						|
 | 
						|
        if(_byteCount >= _byteCode.size())
 | 
						|
        {
 | 
						|
            _byteCount = 0;
 | 
						|
            if(debug  &&  isUserCode) fprintf(stderr, "\n");
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        static uint16_t address = 0x0000;
 | 
						|
        static uint16_t customAddress = 0x0000;
 | 
						|
 | 
						|
        // Get next byte
 | 
						|
        if(_byteCount == 0) address = _startAddress;
 | 
						|
        byteCode = _byteCode[_byteCount++];
 | 
						|
 | 
						|
        // New section
 | 
						|
        if(byteCode._isCustomAddress)
 | 
						|
        {
 | 
						|
            address = byteCode._address;
 | 
						|
            customAddress = byteCode._address;
 | 
						|
        }
 | 
						|
 | 
						|
        // User code is RAM code or ROM code in user ROM space
 | 
						|
        isUserCode = !byteCode._isRomAddress  ||  (byteCode._isRomAddress  &&  customAddress >= USER_ROMv1_ADDRESS);
 | 
						|
 | 
						|
        // Seperate sections
 | 
						|
        if(debug  &&  byteCode._isCustomAddress  &&  isUserCode) fprintf(stderr, "\n");
 | 
						|
 | 
						|
        // 16bit for ROM, 8bit for RAM
 | 
						|
        if(debug  &&  isUserCode)
 | 
						|
        {
 | 
						|
            if(byteCode._isRomAddress)
 | 
						|
            {
 | 
						|
                if((address & 0x0001) == 0x0000)
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "Assembler::getNextAssembledByte() : ROM : %04X  %02X", customAddress + (LO_BYTE(address)>>1), byteCode._data);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "%02X\n", byteCode._data);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::getNextAssembledByte() : RAM : %04X  %02X\n", address, byteCode._data);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        address++;
 | 
						|
 | 
						|
        return false;
 | 
						|
    }    
 | 
						|
 | 
						|
    bool parseDefineOffset(const std::string& token, uint16_t& offset, size_t& lbra)
 | 
						|
    {
 | 
						|
        size_t rbra;
 | 
						|
        if(Expression::findMatchingBrackets(token, 0, lbra, rbra))
 | 
						|
        {
 | 
						|
            Expression::Numeric value;
 | 
						|
            if(Expression::parse(token.substr(lbra + 1, rbra - (lbra + 1)), _lineNumber, value))
 | 
						|
            {
 | 
						|
                offset = uint16_t(std::lround(value._value));
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    InstructionType getOpcode(const std::string& opcodeStr, uint16_t& offset)
 | 
						|
    {
 | 
						|
        offset = 0;
 | 
						|
        std::string opcode = opcodeStr;
 | 
						|
        Expression::strToUpper(opcode); 
 | 
						|
        if(_asmOpcodes.find(opcode) != _asmOpcodes.end())
 | 
						|
        {
 | 
						|
            return _asmOpcodes[opcode];
 | 
						|
        }
 | 
						|
 | 
						|
        size_t lbra;
 | 
						|
        if(parseDefineOffset(opcode, offset, lbra))
 | 
						|
        {
 | 
						|
            if(_asmOpcodes.find(opcode.substr(0, lbra)) != _asmOpcodes.end())
 | 
						|
            {
 | 
						|
                return _asmOpcodes[opcode.substr(0, lbra)];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return {0x00, 0x00, BadSize, vCpu};
 | 
						|
    }
 | 
						|
 | 
						|
    void preProcessExpression(const std::vector<std::string>& tokens, int tokenIndex, std::string& input, bool stripWhiteSpace)
 | 
						|
    {
 | 
						|
        input.clear();
 | 
						|
 | 
						|
        // Pre-process
 | 
						|
        for(int j=tokenIndex; j<int(tokens.size()); j++)
 | 
						|
        {
 | 
						|
            // Strip comments
 | 
						|
            if(tokens[j].find_first_of(";#") != std::string::npos) break;
 | 
						|
 | 
						|
            // Concatenate
 | 
						|
            input += tokens[j];
 | 
						|
        }
 | 
						|
 | 
						|
        // Strip white space
 | 
						|
        if(stripWhiteSpace) Expression::stripWhitespace(input);
 | 
						|
    }
 | 
						|
 | 
						|
    size_t findSymbol(const std::string& input, const std::string& symbol, size_t pos=0)
 | 
						|
    {
 | 
						|
        const size_t len = input.length();
 | 
						|
        if(pos >= len) return std::string::npos;
 | 
						|
 | 
						|
        const std::string separators = "+-*/().,!?;#'[]<> \t\n\r";
 | 
						|
        const std::vector<std::string> operators = {"**", ">>", "<<", "==", "!=", "<=", ">="};
 | 
						|
 | 
						|
        for(;;)
 | 
						|
        {
 | 
						|
            size_t sep = input.find_first_of(separators, pos);
 | 
						|
            bool eos = (sep == std::string::npos);
 | 
						|
            if(eos)
 | 
						|
            {
 | 
						|
                for(int i=0; i<int(operators.size()); i++)
 | 
						|
                {
 | 
						|
                    sep = input.find(operators[i]);
 | 
						|
                    eos = (sep == std::string::npos);
 | 
						|
                    if(eos) break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            size_t end = eos ? len : sep;
 | 
						|
            if(input.substr(pos, end-pos) == symbol)
 | 
						|
            {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            else if(eos)
 | 
						|
            {
 | 
						|
                pos = std::string::npos;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            pos = sep + 1;
 | 
						|
        }
 | 
						|
 | 
						|
        return pos;
 | 
						|
    }
 | 
						|
 | 
						|
    bool applyEquatesToExpression(std::string& expression, const std::vector<Equate>& equates)
 | 
						|
    {
 | 
						|
        bool modified = false;
 | 
						|
        for(int i=0; i<int(equates.size()); i++)
 | 
						|
        {
 | 
						|
            for(;;)
 | 
						|
            {
 | 
						|
                size_t pos = findSymbol(expression, equates[i]._name);
 | 
						|
                if(pos == std::string::npos) break;  // not found
 | 
						|
                modified = true;
 | 
						|
                expression.replace(pos, equates[i]._name.size(), std::to_string(equates[i]._operand));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return modified;
 | 
						|
    }
 | 
						|
 | 
						|
    bool applyLabelsToExpression(std::string& expression, const std::vector<Label>& labels, bool nativeCode)
 | 
						|
    {
 | 
						|
        bool modified = false;
 | 
						|
        for(int i=0; i<int(labels.size()); i++)
 | 
						|
        {
 | 
						|
            for(;;)
 | 
						|
            {
 | 
						|
                size_t pos = findSymbol(expression, labels[i]._name);
 | 
						|
                if (pos == std::string::npos) break;  // not found
 | 
						|
                modified = true;
 | 
						|
                uint16_t address = (nativeCode) ? labels[i]._address >>1 : labels[i]._address;
 | 
						|
                expression.replace(pos, labels[i]._name.size(), std::to_string(address));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return modified;
 | 
						|
    }
 | 
						|
 | 
						|
    bool evaluateExpression(std::string input, bool nativeCode, int16_t& result)
 | 
						|
    { 
 | 
						|
        // Replace equates
 | 
						|
        applyEquatesToExpression(input, _equates);
 | 
						|
 | 
						|
        // Replace labels
 | 
						|
        applyLabelsToExpression(input, _labels, nativeCode);
 | 
						|
 | 
						|
        // Strip white space
 | 
						|
        input.erase(remove_if(input.begin(), input.end(), isspace), input.end());
 | 
						|
 | 
						|
        // Parse expression and return with a result
 | 
						|
        Expression::Numeric numeric;
 | 
						|
        bool valid = Expression::parse(input, _lineNumber, numeric);
 | 
						|
        result = int16_t(std::lround(numeric._value));
 | 
						|
        return valid;
 | 
						|
    }
 | 
						|
 | 
						|
    bool searchEquate(const std::string& token, Equate& equate)
 | 
						|
    {
 | 
						|
        bool success = false;
 | 
						|
        for(int i=0; i<int(_equates.size()); i++)
 | 
						|
        {
 | 
						|
            if(_equates[i]._name == token)
 | 
						|
            {
 | 
						|
                equate = _equates[i];
 | 
						|
                success = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool evaluateEquateOperand(const std::string& token, Equate& equate)
 | 
						|
    {
 | 
						|
        // Expression equates
 | 
						|
        Expression::ExpressionType expressionType = Expression::isExpression(token);
 | 
						|
        if(expressionType == Expression::IsInvalid) return false;
 | 
						|
        if(expressionType == Expression::HasOperators)
 | 
						|
        {
 | 
						|
            int16_t value;
 | 
						|
            if(!evaluateExpression(token, false, value)) return false;
 | 
						|
            equate._operand = value;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        // Check for existing equate
 | 
						|
        return searchEquate(token, equate);
 | 
						|
    }
 | 
						|
 | 
						|
    bool evaluateEquateOperand(const std::vector<std::string>& tokens, int tokenIndex, Equate& equate, bool compoundInstruction)
 | 
						|
    {
 | 
						|
        if(tokenIndex >= int(tokens.size())) return false;
 | 
						|
 | 
						|
        // Expression equates
 | 
						|
        std::string token;
 | 
						|
        if(compoundInstruction)
 | 
						|
        {
 | 
						|
            token = tokens[tokenIndex];
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            preProcessExpression(tokens, tokenIndex, token, false);
 | 
						|
        }
 | 
						|
 | 
						|
        return evaluateEquateOperand(token, equate);
 | 
						|
    }
 | 
						|
 | 
						|
    EvaluateResult evaluateEquates(const std::vector<std::string>& tokens, ParseType parse)
 | 
						|
    {
 | 
						|
        std::string token0 = Expression::strUpper(tokens[0]);
 | 
						|
        std::string token1 = Expression::strUpper(tokens[1]);
 | 
						|
        if(token1 == "EQU")
 | 
						|
        {
 | 
						|
            if(parse == MnemonicPass)
 | 
						|
            {
 | 
						|
                Equate equate = {false, 0x0000, tokens[0]};
 | 
						|
                if(!Expression::stringToU16(tokens[2], equate._operand))
 | 
						|
                {
 | 
						|
                    if(!evaluateEquateOperand(tokens, 2, equate, false)) return NotFound;
 | 
						|
                }
 | 
						|
 | 
						|
                // Reserved word, (equate), _callTable_
 | 
						|
                if(token0 == "_CALLTABLE_")
 | 
						|
                {
 | 
						|
                    _callTablePtr = equate._operand;
 | 
						|
                }
 | 
						|
                // Reserved word, (equate), _startAddress_
 | 
						|
                else if(token0 == "_STARTADDRESS_")
 | 
						|
                {
 | 
						|
                    _startAddress = equate._operand;
 | 
						|
                    _currentAddress = _startAddress;
 | 
						|
                }
 | 
						|
#ifndef STAND_ALONE
 | 
						|
                // Disable upload of the current assembler module
 | 
						|
                else if(token0 == "_DISABLEUPLOAD_")
 | 
						|
                {
 | 
						|
                    Loader::disableUploads(equate._operand != 0);
 | 
						|
                }
 | 
						|
                // Reserved word, (equate), _singleStepWatch_
 | 
						|
                else if(token0 == "_SINGLESTEPWATCH_")
 | 
						|
                {
 | 
						|
                    Editor::setSingleStepAddress(equate._operand);
 | 
						|
                }
 | 
						|
                // Start address of vCPU exclusion zone
 | 
						|
                else if(token0 == "_CPUUSAGEADDRESSA_")
 | 
						|
                {
 | 
						|
                    Editor::setCpuUsageAddressA(equate._operand);
 | 
						|
                }
 | 
						|
                // End address of vCPU exclusion zone
 | 
						|
                else if(token0 == "_CPUUSAGEADDRESSB_")
 | 
						|
                {
 | 
						|
                    Editor::setCpuUsageAddressB(equate._operand);
 | 
						|
                }
 | 
						|
#endif
 | 
						|
                // Standard equates
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    // Check for duplicate
 | 
						|
                    equate._name = tokens[0];
 | 
						|
                    if(searchEquate(tokens[0], equate)) return Duplicate;
 | 
						|
 | 
						|
                    _equates.push_back(equate);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if(parse == CodePass)
 | 
						|
            {
 | 
						|
            }
 | 
						|
 | 
						|
            return Success;
 | 
						|
        }
 | 
						|
 | 
						|
        return Failed;
 | 
						|
    }
 | 
						|
 | 
						|
    bool searchLabel(const std::string& token, Label& label)
 | 
						|
    {
 | 
						|
        bool success = false;
 | 
						|
        for(int i=0; i<int(_labels.size()); i++)
 | 
						|
        {
 | 
						|
            if(token == _labels[i]._name)
 | 
						|
            {
 | 
						|
                success = true;
 | 
						|
                label = _labels[i];
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool evaluateLabelOperand(const std::string& token, Label& label)
 | 
						|
    {
 | 
						|
        // Expression labels
 | 
						|
        Expression::ExpressionType expressionType = Expression::isExpression(token);
 | 
						|
        if(expressionType == Expression::IsInvalid) return false;
 | 
						|
        if(expressionType == Expression::HasOperators)
 | 
						|
        {
 | 
						|
            int16_t value;
 | 
						|
            if(!evaluateExpression(token, false, value)) return false;
 | 
						|
            label._address = value;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        // Check for existing label
 | 
						|
        return searchLabel(token, label);
 | 
						|
    }
 | 
						|
 | 
						|
    bool evaluateLabelOperand(const std::vector<std::string>& tokens, int tokenIndex, Label& label, bool compoundInstruction)
 | 
						|
    {
 | 
						|
        if(tokenIndex >= int(tokens.size())) return false;
 | 
						|
 | 
						|
        // Expression labels
 | 
						|
        std::string token;
 | 
						|
        if(compoundInstruction)
 | 
						|
        {
 | 
						|
            token = tokens[tokenIndex];
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            preProcessExpression(tokens, tokenIndex, token, false);
 | 
						|
        }
 | 
						|
 | 
						|
        return evaluateLabelOperand(token, label);
 | 
						|
    }
 | 
						|
 | 
						|
    EvaluateResult EvaluateLabels(const std::vector<std::string>& tokens, ParseType parse, int tokenIndex)
 | 
						|
    {
 | 
						|
        if(parse == MnemonicPass) 
 | 
						|
        {
 | 
						|
            // Check reserved words
 | 
						|
            for(int i=0; i<int(_reservedWords.size()); i++)
 | 
						|
            {
 | 
						|
                if(tokens[tokenIndex] == _reservedWords[i]) return Reserved;
 | 
						|
            }
 | 
						|
            
 | 
						|
            Label label;
 | 
						|
            if(searchLabel(tokens[tokenIndex], label)) return Duplicate;
 | 
						|
 | 
						|
            // Check equates for a custom start address
 | 
						|
            for(int i=0; i<int(_equates.size()); i++)
 | 
						|
            {
 | 
						|
                if(_equates[i]._name == tokens[tokenIndex])
 | 
						|
                {
 | 
						|
                    _equates[i]._isCustomAddress = true;
 | 
						|
                    _currentAddress = _equates[i]._operand;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Normal labels
 | 
						|
            label = {_currentAddress, tokens[tokenIndex]};
 | 
						|
            _labels.push_back(label);
 | 
						|
        }
 | 
						|
        else if(parse == CodePass)
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        return Success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleDefineByte(std::vector<std::string>& tokens, int tokenIndex, const Instruction& instruction, bool createInstruction, uint16_t defineOffset, int& dbSize)
 | 
						|
    {
 | 
						|
        bool success = false;
 | 
						|
 | 
						|
        // Handle case where first operand is a string
 | 
						|
        size_t quote1 = tokens[tokenIndex].find_first_of("'");
 | 
						|
        size_t quote2 = tokens[tokenIndex].find_last_of("'");
 | 
						|
        bool quotes = (quote1 != std::string::npos  &&  quote2 != std::string::npos  &&  (quote2 - quote1 > 1));
 | 
						|
        if(quotes)
 | 
						|
        {
 | 
						|
            // Remove escape sequence
 | 
						|
            std::string token = tokens[tokenIndex].substr(quote1+1, quote2 - (quote1+1));
 | 
						|
            Expression::stripChars(token, "\\");
 | 
						|
            if(createInstruction)
 | 
						|
            {
 | 
						|
                for(int j=1; j<int(token.size()); j++) // First instruction was created by callee
 | 
						|
                {
 | 
						|
                    Instruction inst = {instruction._isRomAddress, false, OneByte, uint8_t(token[j]), 0x00, 0x00, 0x0000, instruction._opcodeType};
 | 
						|
                    _instructions.push_back(inst);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            dbSize += int(token.size()) - 1; // First instruction was created by callee
 | 
						|
            success = true;
 | 
						|
        }
 | 
						|
       
 | 
						|
        for(int i=tokenIndex+1; i<int(tokens.size()); i++)
 | 
						|
        {
 | 
						|
            // Handle all other variations of strings
 | 
						|
            quote1 = tokens[i].find_first_of("'");
 | 
						|
            quote2 = tokens[i].find_last_of("'");
 | 
						|
            quotes = (quote1 != std::string::npos  &&  quote2 != std::string::npos);
 | 
						|
            if(quotes)
 | 
						|
            {
 | 
						|
                // Remove escape sequence
 | 
						|
                std::string token = tokens[i].substr(quote1+1, quote2 - (quote1+1));
 | 
						|
                Expression::stripChars(token, "\\");
 | 
						|
                if(createInstruction)
 | 
						|
                {
 | 
						|
                    for(int j=0; j<int(token.size()); j++)
 | 
						|
                    {
 | 
						|
                        Instruction inst = {instruction._isRomAddress, false, OneByte, uint8_t(token[j]), 0x00, 0x00, 0x0000, instruction._opcodeType};
 | 
						|
                        _instructions.push_back(inst);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                dbSize += int(token.size());
 | 
						|
                success = true;
 | 
						|
            }
 | 
						|
            // Non string tokens
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Strip comments
 | 
						|
                if(tokens[i].find_first_of(";#") != std::string::npos) break;
 | 
						|
 | 
						|
                uint8_t operand;
 | 
						|
                success = Expression::stringToU8(tokens[i], operand);
 | 
						|
                if(!success)
 | 
						|
                {
 | 
						|
                    // Search equates
 | 
						|
                    Equate equate;
 | 
						|
                    Label label;
 | 
						|
                    if((success = evaluateEquateOperand(tokens[i], equate)) == true)
 | 
						|
                    {
 | 
						|
                        operand = uint8_t(equate._operand);
 | 
						|
                    }
 | 
						|
                    // Search labels
 | 
						|
                    else if((success = evaluateLabelOperand(tokens[i], label)) == true)
 | 
						|
                    {
 | 
						|
                        operand = uint8_t(label._address);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        // Normal expression
 | 
						|
                        if(Expression::isExpression(tokens[i]) == Expression::HasOperators)
 | 
						|
                        {
 | 
						|
                            Expression::Numeric value;
 | 
						|
                            if(Expression::parse(tokens[i], _lineNumber, value))
 | 
						|
                            {
 | 
						|
                                operand = uint8_t(std::lround(value._value));
 | 
						|
                                success = true;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        else
 | 
						|
                        {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if(createInstruction)
 | 
						|
                {
 | 
						|
                    bool hasDefineOffset = (defineOffset != 0);
 | 
						|
                    uint16_t address = (hasDefineOffset) ? _currentAddress : 0x0000;
 | 
						|
                    Instruction inst = {instruction._isRomAddress, hasDefineOffset, OneByte, operand, 0x00, 0x00, address, instruction._opcodeType};
 | 
						|
                    _currentAddress += defineOffset;
 | 
						|
                    _instructions.push_back(inst);
 | 
						|
                }
 | 
						|
                dbSize++;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    bool handleDefineWord(const std::vector<std::string>& tokens, int tokenIndex, const Instruction& instruction, bool createInstruction, uint16_t defineOffset, int& dwSize)
 | 
						|
    {
 | 
						|
        bool success = false;
 | 
						|
 | 
						|
        for(int i=tokenIndex+1; i<int(tokens.size()); i++)
 | 
						|
        {
 | 
						|
            // Strip comments
 | 
						|
            if(tokens[i].find_first_of(";#") != std::string::npos)
 | 
						|
            {
 | 
						|
                success = true;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            uint16_t operand;
 | 
						|
            success = Expression::stringToU16(tokens[i], operand);
 | 
						|
            if(!success)
 | 
						|
            {
 | 
						|
                // Search equates
 | 
						|
                Equate equate;
 | 
						|
                Label label;
 | 
						|
                if((success = evaluateEquateOperand(tokens[i], equate)) == true)
 | 
						|
                {
 | 
						|
                    operand = equate._operand;
 | 
						|
                }
 | 
						|
                // Search labels
 | 
						|
                else if((success = evaluateLabelOperand(tokens[i], label)) == true)
 | 
						|
                {
 | 
						|
                    operand = label._address;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    // Normal expression
 | 
						|
                    if(Expression::isExpression(tokens[i]) == Expression::HasOperators)
 | 
						|
                    {
 | 
						|
                        Expression::Numeric value;
 | 
						|
                        if(Expression::parse(tokens[i], _lineNumber, value))
 | 
						|
                        {
 | 
						|
                            operand = int16_t(std::lround(value._value));
 | 
						|
                            success = true;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if(createInstruction)
 | 
						|
            {
 | 
						|
                bool hasDefineOffset = (defineOffset != 0);
 | 
						|
                uint16_t address = (hasDefineOffset) ? _currentAddress : 0x0000;
 | 
						|
                Instruction inst = {instruction._isRomAddress, hasDefineOffset, TwoBytes, uint8_t(LO_BYTE(operand)), uint8_t(HI_BYTE(operand)), 0x00, address, instruction._opcodeType};
 | 
						|
                _currentAddress += defineOffset * 2;
 | 
						|
                _instructions.push_back(inst);
 | 
						|
            }
 | 
						|
            dwSize += 2;
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleNativeOperand(const std::string& token, uint8_t& operand)
 | 
						|
    {
 | 
						|
        Expression::ExpressionType expressionType = Expression::isExpression(token);
 | 
						|
        if(expressionType == Expression::IsInvalid) return false;
 | 
						|
        if(expressionType == Expression::HasOperators)
 | 
						|
        {
 | 
						|
            // Parse expression and return with a result
 | 
						|
            int16_t value;
 | 
						|
            if(!evaluateExpression(token, true, value)) return false;
 | 
						|
            operand = uint8_t(value);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        Label label;
 | 
						|
        if(searchLabel(token, label))
 | 
						|
        {
 | 
						|
            operand = uint8_t(LO_BYTE(label._address >>1));
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        Equate equate;
 | 
						|
        if(searchEquate(token, equate))
 | 
						|
        {
 | 
						|
            operand = uint8_t(equate._operand);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        return Expression::stringToU8(token, operand);
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleNativeInstruction(const std::vector<std::string>& tokens, int tokenIndex, uint8_t& opcode, uint8_t& operand)
 | 
						|
    {
 | 
						|
        std::string input, token;
 | 
						|
 | 
						|
        preProcessExpression(tokens, tokenIndex, input, true);
 | 
						|
        size_t openBracket = input.find_first_of("[");
 | 
						|
        size_t closeBracket = input.find_first_of("]");
 | 
						|
        bool noBrackets = (openBracket == std::string::npos  &&  closeBracket == std::string::npos);
 | 
						|
        bool validBrackets = (openBracket != std::string::npos  &&  closeBracket != std::string::npos  &&  closeBracket > openBracket);
 | 
						|
 | 
						|
        size_t comma1 = input.find_first_of(",");
 | 
						|
        size_t comma2 = input.find_first_of(",", comma1+1);
 | 
						|
        bool noCommas = (comma1 == std::string::npos  &&  comma2 == std::string::npos);
 | 
						|
        bool oneComma = (comma1 != std::string::npos  &&  comma2 == std::string::npos);
 | 
						|
        bool twoCommas = (comma1 != std::string::npos  &&  comma2 != std::string::npos);
 | 
						|
 | 
						|
        operand = 0x00;
 | 
						|
 | 
						|
        // NOP
 | 
						|
        if(opcode == 0x02) return true;
 | 
						|
 | 
						|
        // Accumulator
 | 
						|
        if(input == "AC"  ||  input == "ac")
 | 
						|
        {
 | 
						|
            opcode |= BusMode::AC;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        // Jump
 | 
						|
        if(opcode == 0xE0)
 | 
						|
        {
 | 
						|
            // y,[D]
 | 
						|
            if(validBrackets  &&  oneComma  &&  comma1 < openBracket)
 | 
						|
            {
 | 
						|
                opcode |= BusMode::RAM;
 | 
						|
                token = input.substr(openBracket+1, closeBracket - (openBracket+1));
 | 
						|
                return handleNativeOperand(token, operand);
 | 
						|
            }
 | 
						|
 | 
						|
            // y,D
 | 
						|
            if(noBrackets  &&  oneComma)
 | 
						|
            {
 | 
						|
                token = input.substr(comma1+1, input.size() - (comma1+1));
 | 
						|
                return handleNativeOperand(token, operand);
 | 
						|
            }
 | 
						|
        
 | 
						|
            return false;                    
 | 
						|
        }
 | 
						|
 | 
						|
        // Branch
 | 
						|
        if(opcode >= 0xE4)
 | 
						|
        {
 | 
						|
            token = input;
 | 
						|
            if(validBrackets) {opcode |= BusMode::RAM; token = input.substr(openBracket+1, closeBracket - (openBracket+1));}
 | 
						|
            if(Expression::stringToU8(token, operand)) return true;
 | 
						|
            return handleNativeOperand(token, operand);
 | 
						|
        }
 | 
						|
 | 
						|
        // IN or IN,[D]
 | 
						|
        if(input.find("IN") != std::string::npos  ||  input.find("in") != std::string::npos)
 | 
						|
        {
 | 
						|
            opcode |= BusMode::IN;
 | 
						|
 | 
						|
            // IN,[D]
 | 
						|
            if(validBrackets &&  oneComma  &&  comma1 < openBracket)
 | 
						|
            {
 | 
						|
                token = input.substr(openBracket+1, closeBracket - (openBracket+1));
 | 
						|
                return handleNativeOperand(token, operand);
 | 
						|
            }
 | 
						|
            
 | 
						|
            // IN
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        // D
 | 
						|
        if(noBrackets && noCommas) return handleNativeOperand(input, operand);
 | 
						|
 | 
						|
        // Read or write
 | 
						|
        (opcode != 0xC0) ? opcode |= BusMode::RAM : opcode |= BusMode::AC;
 | 
						|
 | 
						|
        // [D] or [X]
 | 
						|
        if(validBrackets  &&  noCommas)
 | 
						|
        {
 | 
						|
            token = input.substr(openBracket+1, closeBracket - (openBracket+1));
 | 
						|
            if(token == "X"  ||  token == "x") {opcode |= AddressMode::X_AC; return true;}
 | 
						|
            return handleNativeOperand(token, operand);
 | 
						|
        }
 | 
						|
 | 
						|
        // AC,X or AC,Y or AC,OUT or D,X or D,Y or D,OUT or [D],X or [D],Y or [D],OUT or D,[D] or D,[X] or D,[Y] or [Y,D] or [Y,X] or [Y,X++]
 | 
						|
        if(oneComma)
 | 
						|
        {
 | 
						|
            token = input.substr(comma1+1, input.size() - (comma1+1));
 | 
						|
            if(token == "X"    ||  token == "x")   opcode |= AddressMode::D_X;
 | 
						|
            if(token == "Y"    ||  token == "y")   opcode |= AddressMode::D_Y;
 | 
						|
            if(token == "OUT"  ||  token == "out") opcode |= AddressMode::D_OUT;
 | 
						|
 | 
						|
            token = input.substr(0, comma1);
 | 
						|
 | 
						|
            // AC,X or AC,Y or AC,OUT
 | 
						|
            if(token == "AC"  ||  token == "ac") {opcode &= 0xFC; opcode |= BusMode::AC; return true;}
 | 
						|
 | 
						|
            // D,X or D,Y or D,OUT
 | 
						|
            if(noBrackets)
 | 
						|
            {
 | 
						|
                opcode &= 0xFC; return handleNativeOperand(token, operand);
 | 
						|
            }
 | 
						|
 | 
						|
            // [D],X or [D],Y or [D],OUT or D,[D] or D,[X] or D,[Y] or [Y,D] or [Y,X] or [Y,X++]
 | 
						|
            if(validBrackets)
 | 
						|
            {
 | 
						|
                if(comma1 > closeBracket) token = input.substr(openBracket+1, closeBracket - (openBracket+1));
 | 
						|
                else if(comma1 < openBracket) {opcode &= 0xFC; token = input.substr(0, comma1);}
 | 
						|
                else if(comma1 > openBracket  &&  comma1 < closeBracket)
 | 
						|
                {
 | 
						|
                    token = input.substr(openBracket+1, comma1 - (openBracket+1));
 | 
						|
                    if(token != "Y"  &&  token != "y") return false;
 | 
						|
 | 
						|
                    token = input.substr(comma1+1, closeBracket - (comma1+1));
 | 
						|
                    if(token == "X"    ||  token == "x")   {opcode |= AddressMode::YX_AC;    return true;}
 | 
						|
                    if(token == "X++"  ||  token == "x++") {opcode |= AddressMode::YXpp_OUT; return true;}
 | 
						|
 | 
						|
                    opcode |= AddressMode::YD_AC;                
 | 
						|
                }
 | 
						|
                return handleNativeOperand(token, operand);
 | 
						|
            }
 | 
						|
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // D,[Y,X] or D,[Y,X++]
 | 
						|
        if(validBrackets  &&  twoCommas  &&  comma1 < openBracket  &&  comma2 > openBracket  &&  comma2 < closeBracket)
 | 
						|
        {
 | 
						|
            token = input.substr(0, comma1);
 | 
						|
            if(!handleNativeOperand(token, operand)) return false;
 | 
						|
 | 
						|
            token = input.substr(openBracket+1, comma2 - (openBracket+1));
 | 
						|
            if(token != "Y"  &&  token != "y") return false;
 | 
						|
            opcode &= 0xFC; // reset bus bits to D
 | 
						|
 | 
						|
            token = input.substr(comma2+1, closeBracket - (comma2+1));
 | 
						|
            if(token == "X"    ||  token == "x")   {opcode |= YX_AC;    return true;}
 | 
						|
            if(token == "X++"  ||  token == "x++") {opcode |= YXpp_OUT; return true;}
 | 
						|
                
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // [Y,X++],out
 | 
						|
        if(validBrackets  &&  twoCommas  &&  comma1 > openBracket  &&  comma2 > closeBracket)
 | 
						|
        {
 | 
						|
            token = input.substr(openBracket+1, comma1 - (openBracket+1));
 | 
						|
            if(token != "Y"  &&  token != "y") return false;
 | 
						|
 | 
						|
            token = input.substr(comma1+1, closeBracket - (comma1+1));
 | 
						|
            if(token == "X"    ||  token == "x")   {opcode |= YX_AC;    return true;}
 | 
						|
            if(token == "X++"  ||  token == "x++") {opcode |= YXpp_OUT; return true;}
 | 
						|
                
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    void packByteCode(Instruction& instruction, ByteCode& byteCode)
 | 
						|
    {
 | 
						|
        switch(instruction._byteSize)
 | 
						|
        {
 | 
						|
            case OneByte:
 | 
						|
            {
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = instruction._isCustomAddress;
 | 
						|
                byteCode._data = instruction._opcode;
 | 
						|
                byteCode._address = instruction._address;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            case TwoBytes:
 | 
						|
            {
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = instruction._isCustomAddress;
 | 
						|
                byteCode._data = instruction._opcode;
 | 
						|
                byteCode._address = instruction._address;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = false;
 | 
						|
                byteCode._data = instruction._operand0;
 | 
						|
                byteCode._address = 0x0000;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            case ThreeBytes:
 | 
						|
            {
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = instruction._isCustomAddress;
 | 
						|
                byteCode._data = instruction._opcode;
 | 
						|
                byteCode._address = instruction._address;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = false;
 | 
						|
                byteCode._data = instruction._operand0;
 | 
						|
                byteCode._address = 0x0000;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
 | 
						|
                byteCode._isRomAddress = instruction._isRomAddress;
 | 
						|
                byteCode._isCustomAddress = false;
 | 
						|
                byteCode._data = instruction._operand1;
 | 
						|
                byteCode._address = 0x0000;
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
            default: break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void packByteCodeBuffer(void)
 | 
						|
    {
 | 
						|
        // Pack instructions
 | 
						|
        ByteCode byteCode;
 | 
						|
        uint16_t segmentOffset = 0x0000;
 | 
						|
        uint16_t segmentAddress = 0x0000;
 | 
						|
        for(int i=0; i<int(_instructions.size()); i++)
 | 
						|
        {
 | 
						|
            // Segment RAM instructions into 256 byte pages for .gt1 file format
 | 
						|
            if(!_instructions[i]._isRomAddress)
 | 
						|
            {
 | 
						|
                // Save start of segment
 | 
						|
                if(_instructions[i]._isCustomAddress)
 | 
						|
                {
 | 
						|
                    segmentOffset = 0x0000;
 | 
						|
                    segmentAddress = _instructions[i]._address;
 | 
						|
                }
 | 
						|
 | 
						|
                // Force a new segment, (this could fail if an instruction straddles a page boundary, but
 | 
						|
                // the page boundary crossing detection logic will stop the assembler before we get here)
 | 
						|
                if(!_instructions[i]._isCustomAddress  &&  (HI_BYTE(segmentAddress + segmentOffset) != HI_BYTE(segmentAddress)))
 | 
						|
                {
 | 
						|
                    segmentAddress += segmentOffset;
 | 
						|
                    segmentOffset = 0x0000;
 | 
						|
 | 
						|
                    _instructions[i]._isCustomAddress = true;
 | 
						|
                    _instructions[i]._address = segmentAddress;
 | 
						|
                }
 | 
						|
 | 
						|
                segmentOffset += uint16_t(_instructions[i]._byteSize);
 | 
						|
            }
 | 
						|
 | 
						|
            packByteCode(_instructions[i], byteCode);
 | 
						|
        }
 | 
						|
 | 
						|
        // Append call table
 | 
						|
        if(_callTablePtr  &&  _callTableEntries.size())
 | 
						|
        {
 | 
						|
            // _callTable grows downwards, pointer is 2 bytes below the bottom of the table by the time we get here
 | 
						|
            for(int i=int(_callTableEntries.size())-1; i>=0; i--)
 | 
						|
            {
 | 
						|
                byteCode._isRomAddress = false;
 | 
						|
                byteCode._isCustomAddress = true;  // calltable entries can be non-sequential because of 0x80, (ONE_CONST_ADDRESS)
 | 
						|
                byteCode._data = LO_BYTE(_callTableEntries[i]._address);
 | 
						|
                byteCode._address = LO_BYTE(_callTableEntries[i]._operand);
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
 | 
						|
                byteCode._isRomAddress = false;
 | 
						|
                byteCode._isCustomAddress = false;
 | 
						|
                byteCode._data = HI_BYTE(_callTableEntries[i]._address);
 | 
						|
                byteCode._address = LO_BYTE(_callTableEntries[i]._operand + 1);
 | 
						|
                _byteCode.push_back(byteCode);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    bool checkInvalidAddress(ParseType parse, uint16_t currentAddress, uint16_t instructionSize, const Instruction& instruction, const LineToken& lineToken, const std::string& filename, int lineNumber)
 | 
						|
    {
 | 
						|
        // Check for audio channel stomping
 | 
						|
        if(parse == CodePass  &&  !instruction._isRomAddress)
 | 
						|
        {
 | 
						|
            uint16_t start = currentAddress;
 | 
						|
            uint16_t end = currentAddress + instructionSize - 1;
 | 
						|
            if((start >= GIGA_CH0_WAV_A  &&  start <= GIGA_CH0_OSC_H)  ||  (end >= GIGA_CH0_WAV_A  &&  end <= GIGA_CH0_OSC_H)  ||
 | 
						|
               (start >= GIGA_CH1_WAV_A  &&  start <= GIGA_CH1_OSC_H)  ||  (end >= GIGA_CH1_WAV_A  &&  end <= GIGA_CH1_OSC_H)  ||
 | 
						|
               (start >= GIGA_CH2_WAV_A  &&  start <= GIGA_CH2_OSC_H)  ||  (end >= GIGA_CH2_WAV_A  &&  end <= GIGA_CH2_OSC_H)  ||
 | 
						|
               (start >= GIGA_CH3_WAV_A  &&  start <= GIGA_CH3_OSC_H)  ||  (end >= GIGA_CH3_WAV_A  &&  end <= GIGA_CH3_OSC_H))
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::checkInvalidAddress() : Warning, audio channel boundary compromised, (if you've disabled the audio channels, then ignore this warning) ");
 | 
						|
                fprintf(stderr, "Assembler::checkInvalidAddress() : '%s:%d' : 0x%04X <-> 0x%04X\nAssembler::checkInvalidAddress() : '%s'\nAssembler::checkInvalidAddress()\n",
 | 
						|
                        filename.c_str(), lineNumber+1, start, end, lineToken._text.c_str());
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Check for page boundary crossings
 | 
						|
        if(parse == CodePass  &&  (instruction._opcodeType == vCpu || instruction._opcodeType == Native))
 | 
						|
        {
 | 
						|
            static uint16_t customAddress = 0x0000;
 | 
						|
            if(instruction._isCustomAddress) customAddress = instruction._address;
 | 
						|
 | 
						|
            uint16_t oldAddress = (instruction._isRomAddress) ? customAddress + (LO_BYTE(currentAddress)>>1) : currentAddress;
 | 
						|
            currentAddress += instructionSize - 1;
 | 
						|
            uint16_t newAddress = (instruction._isRomAddress) ? customAddress + (LO_BYTE(currentAddress)>>1) : currentAddress;
 | 
						|
            if((oldAddress >>8) != (newAddress >>8))
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::checkInvalidAddress() : '%s:%d' : page boundary compromised : %04X : %04X : '%s'\n", filename.c_str(), lineNumber+1, oldAddress, newAddress, lineToken._text.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    bool handleInclude(const std::vector<std::string>& tokens, const std::string& lineToken, int lineIndex, std::vector<LineToken>& includeLineTokens)
 | 
						|
    {
 | 
						|
        // Check include syntax
 | 
						|
        if(tokens.size() != 2)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleInclude() : '%s:%d' : bad %%include statement\n", lineToken.c_str(), lineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        std::string filepath = _includePath + "/" + tokens[1];
 | 
						|
        std::replace(filepath.begin(), filepath.end(), '\\', '/');
 | 
						|
        std::ifstream infile(filepath);
 | 
						|
        if(!infile.is_open())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleInclude() : failed to open file '%s'\n", filepath.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Collect lines from include file
 | 
						|
        int lineNumber = lineIndex;
 | 
						|
        while(!infile.eof())
 | 
						|
        {
 | 
						|
            LineToken includeLineToken = {true, lineNumber++ - lineIndex, "", filepath};
 | 
						|
            std::getline(infile, includeLineToken._text);
 | 
						|
            includeLineTokens.push_back(includeLineToken);
 | 
						|
 | 
						|
            if(!infile.good() && !infile.eof())
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::handleInclude() : '%s:%d' : bad lineToken '%s'\n", filepath.c_str(), lineNumber - lineIndex, includeLineToken._text.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleMacros(const std::vector<Macro>& macros, std::vector<LineToken>& lineTokens)
 | 
						|
    {
 | 
						|
        // Incomplete macros
 | 
						|
        for(int i=0; i<int(macros.size()); i++)
 | 
						|
        {
 | 
						|
            if(!macros[i]._complete)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::handleMacros() : '%s:%d' : bad macro, missing 'ENDM'\n", macros[i]._filename.c_str(), macros[i]._fileStartLine);
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Delete original macros
 | 
						|
        auto filter = [](LineToken& lineToken)
 | 
						|
        {
 | 
						|
            static bool foundMacro = false;
 | 
						|
            if(lineToken._text.find("%MACRO") != std::string::npos)
 | 
						|
            {
 | 
						|
                foundMacro = true;
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            if(foundMacro)
 | 
						|
            {
 | 
						|
                if(lineToken._text.find("%ENDM") != std::string::npos) foundMacro = false;
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
 | 
						|
            return false;
 | 
						|
        };
 | 
						|
        lineTokens.erase(std::remove_if(lineTokens.begin(), lineTokens.end(), filter), lineTokens.end());
 | 
						|
 | 
						|
        // Find and expand macro
 | 
						|
        int macroInstanceId = 0;
 | 
						|
        for(int m=0; m<int(macros.size()); m++)
 | 
						|
        {
 | 
						|
            bool macroMissing = true;
 | 
						|
            bool macroMissingParams = true;
 | 
						|
            Macro macro = macros[m];
 | 
						|
 | 
						|
            for(auto itLine=lineTokens.begin(); itLine!=lineTokens.end();)
 | 
						|
            {
 | 
						|
                // Lines containing only white space are skipped
 | 
						|
                LineToken lineToken = *itLine;
 | 
						|
                size_t nonWhiteSpace = lineToken._text.find_first_not_of("  \n\r\f\t\v");
 | 
						|
                if(nonWhiteSpace == std::string::npos)
 | 
						|
                {
 | 
						|
                    ++itLine;
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                // Tokenise current line
 | 
						|
                std::vector<std::string> tokens = Expression::tokeniseLine(lineToken._text);
 | 
						|
 | 
						|
                // Find macro
 | 
						|
                bool macroSuccess = false;
 | 
						|
                for(int t=0; t<int(tokens.size()); t++)
 | 
						|
                {
 | 
						|
                    if(tokens[t] == macro._name)
 | 
						|
                    {
 | 
						|
                        macroMissing = false;
 | 
						|
                        if(tokens.size() - t > macro._params.size())
 | 
						|
                        {
 | 
						|
                            macroMissingParams = false;
 | 
						|
                            std::vector<std::string> labels;
 | 
						|
                            std::vector<LineToken> macroLines;
 | 
						|
 | 
						|
                            // Create substitute lines
 | 
						|
                            for(int ml=0; ml<int(macro._lines.size()); ml++)
 | 
						|
                            {
 | 
						|
                                // Tokenise macro line
 | 
						|
                                std::vector<std::string> mtokens =  Expression::tokeniseLine(macro._lines[ml]);
 | 
						|
 | 
						|
                                // Save labels
 | 
						|
                                nonWhiteSpace = macro._lines[ml].find_first_not_of("  \n\r\f\t\v");
 | 
						|
                                if(nonWhiteSpace == 0) labels.push_back(mtokens[0]);
 | 
						|
 | 
						|
                                // Replace parameters
 | 
						|
                                for(int mt=0; mt<int(mtokens.size()); mt++)
 | 
						|
                                {
 | 
						|
                                    for(int p=0; p<int(macro._params.size()); p++)
 | 
						|
                                    {
 | 
						|
                                        //if(mtokens[mt] == macro._params[p]) mtokens[mt] = tokens[t + 1 + p];
 | 
						|
                                        size_t param = mtokens[mt].find(macro._params[p]);
 | 
						|
                                        if(param != std::string::npos)
 | 
						|
                                        {
 | 
						|
                                            mtokens[mt].erase(param, macro._params[p].size());
 | 
						|
                                            mtokens[mt].insert(param, tokens[t + 1 + p]);
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
 | 
						|
                                // New macro line using any existing label
 | 
						|
                                LineToken macroLine = {false, 0, "", ""};
 | 
						|
                                macroLine._text = (t > 0  &&  ml == 0) ? tokens[0] : "";
 | 
						|
 | 
						|
                                // Append to macro line
 | 
						|
                                for(int mt=0; mt<int(mtokens.size()); mt++)
 | 
						|
                                {
 | 
						|
                                    // Don't prefix macro labels with a space
 | 
						|
                                    if(nonWhiteSpace != 0  ||  mt != 0) macroLine._text += " ";
 | 
						|
 | 
						|
                                    macroLine._text += mtokens[mt];
 | 
						|
                                }
 | 
						|
 | 
						|
                                macroLines.push_back(macroLine);
 | 
						|
                            }
 | 
						|
 | 
						|
                            // Insert substitute lines
 | 
						|
                            for(int ml=0; ml<int(macro._lines.size()); ml++)
 | 
						|
                            {
 | 
						|
                                // Delete macro caller
 | 
						|
                                if(ml == 0) itLine = lineTokens.erase(itLine);
 | 
						|
 | 
						|
                                // Each instance of a macro's labels are made unique
 | 
						|
                                for(int i=0; i<int(labels.size()); i++)
 | 
						|
                                {
 | 
						|
                                    size_t labelFoundPos = macroLines[ml]._text.find(labels[i]);
 | 
						|
                                    if(labelFoundPos != std::string::npos) macroLines[ml]._text.insert(labelFoundPos + labels[i].size(), std::to_string(macroInstanceId));
 | 
						|
                                }
 | 
						|
 | 
						|
                                // Insert macro lines
 | 
						|
                                itLine = lineTokens.insert(itLine, macroLines[ml]);
 | 
						|
                                ++itLine;
 | 
						|
                            }
 | 
						|
 | 
						|
                            macroInstanceId++;
 | 
						|
                            macroSuccess = true;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if(!macroSuccess) ++itLine;
 | 
						|
            }
 | 
						|
 | 
						|
            if(macroMissing)
 | 
						|
            {
 | 
						|
                //fprintf(stderr, "Assembler::handleMacros() : '%s:%d' : warning, macro '%s' is never called\n", macro._filename.c_str(), macro._fileStartLine, macro._name.c_str());
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            if(macroMissingParams)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::handleMacros() : '%s:%d' : missing macro parameters for '%s'\n", macro._filename.c_str(), macro._fileStartLine, macro._name.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleMacroStart(const std::string& filename, const LineToken& lineToken, const std::vector<std::string>& tokens, Macro& macro, int adjustedLineIndex)
 | 
						|
    {
 | 
						|
        int lineNumber = (lineToken._fromInclude) ? lineToken._includeLineNumber + 1 : adjustedLineIndex + 1;
 | 
						|
        std::string macroFileName = (lineToken._fromInclude) ? lineToken._includeName : filename;
 | 
						|
 | 
						|
        // Check macro syntax
 | 
						|
        if(tokens.size() < 2)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleMacroStart() : '%s:%d' : bad macro, missing name\n", macroFileName.c_str(), lineNumber);
 | 
						|
            return false;
 | 
						|
        }                    
 | 
						|
 | 
						|
        macro._name = tokens[1];
 | 
						|
        macro._fromInclude = lineToken._fromInclude;
 | 
						|
        macro._fileStartLine = lineNumber;
 | 
						|
        macro._filename = macroFileName;
 | 
						|
 | 
						|
        // Save params
 | 
						|
        for(int i=2; i<int(tokens.size()); i++) macro._params.push_back(tokens[i]);
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleMacroEnd(std::vector<Macro>& macros, Macro& macro)
 | 
						|
    {
 | 
						|
        // Check for duplicates
 | 
						|
        for(int i=0; i<int(macros.size()); i++)
 | 
						|
        {
 | 
						|
            if(macro._name == macros[i]._name)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::handleMacroEnd() : '%s:%d' : bad macro, duplicate name '%s'\n", macro._filename.c_str(), macro._fileStartLine, macro._name.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        macro._complete = true;
 | 
						|
        macros.push_back(macro);
 | 
						|
 | 
						|
        macro._name = "";
 | 
						|
        macro._lines.clear();
 | 
						|
        macro._params.clear();
 | 
						|
        macro._complete = false;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void clearDefines(void)
 | 
						|
    {
 | 
						|
        _defines.clear();
 | 
						|
        while(!_currentDefine.empty()) _currentDefine.pop();
 | 
						|
    }
 | 
						|
 | 
						|
    bool createDefine(const std::string& filename, const std::vector<std::string>& tokens, int adjustedLineIndex)
 | 
						|
    {
 | 
						|
        if(tokens.size() < 2  ||  tokens.size() > 3)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::createDefine() : '%s:%d' : %%define requires one or two params\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Define name
 | 
						|
        std::string defineName = tokens[1];
 | 
						|
        if(_defines.find(defineName) != _defines.end())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::createDefine() : '%s:%d' : found duplicate define '%s'\n", filename.c_str(), adjustedLineIndex, defineName.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Define value
 | 
						|
        int16_t defineValue = 0;
 | 
						|
        if(tokens.size() == 3)
 | 
						|
        {
 | 
						|
            if(!evaluateExpression(tokens[2], false, defineValue))
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::createDefine() : '%s:%d' : found invalid define value '%s'\n", filename.c_str(), adjustedLineIndex, tokens[2].c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        _defines[defineName] = {true, false, defineValue, defineName};
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleIfDefine(const std::string& filename, const std::vector<std::string>& tokens, int adjustedLineIndex)
 | 
						|
    {
 | 
						|
        if(tokens.size() != 2)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleIfDefine() : '%s:%d' : %%if requires one param\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        std::string define = tokens[1];
 | 
						|
        if(_defines.find(define) == _defines.end())
 | 
						|
        {
 | 
						|
            _defines[define] = {false, false, 0, define};
 | 
						|
        }
 | 
						|
 | 
						|
        // Push current define to stack
 | 
						|
        _currentDefine.push(define);
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleEndIfDefine(const std::string& filename, const std::vector<std::string>& tokens, int adjustedLineIndex)
 | 
						|
    {
 | 
						|
        if(tokens.size() != 1)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleEndIfDefine() : '%s:%d' : %%endif requires no params\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        if(_currentDefine.empty())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleEndIfDefine() : '%s:%d' : syntax error, no valid define\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // If enabled was toggled by an else then reset it
 | 
						|
        std::string define = _currentDefine.top();
 | 
						|
        if(_defines[define]._toggle == true)
 | 
						|
        {
 | 
						|
            _defines[define]._enabled = !_defines[define]._enabled;
 | 
						|
            _defines[define]._toggle = false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Pop current define from stack
 | 
						|
        _currentDefine.pop();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleElseDefine(const std::string& filename, const std::vector<std::string>& tokens, int adjustedLineIndex)
 | 
						|
    {
 | 
						|
        if(tokens.size() != 1)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleElseDefine() : '%s:%d' : %%else requires no params\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        if(_currentDefine.empty())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::handleElseDefine() : '%s:%d' : syntax error, no valid define\n", filename.c_str(), adjustedLineIndex);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Toggle current define
 | 
						|
        std::string define = _currentDefine.top();
 | 
						|
        _defines[define]._enabled = !_defines[define]._enabled;
 | 
						|
        _defines[define]._toggle = true;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool isCurrentDefineDisabled(void)
 | 
						|
    {
 | 
						|
        // Check top of define stack
 | 
						|
        if(!_currentDefine.empty())
 | 
						|
        {
 | 
						|
            std::string define = _currentDefine.top();
 | 
						|
            if(_defines.find(define) != _defines.end()  &&  !_defines[define]._enabled)
 | 
						|
            {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    int16_t getRuntimeVersion(void)
 | 
						|
    {
 | 
						|
        int16_t runtimeVersion = 0;
 | 
						|
        if(_defines.find("RUNTIME_VERSION") != _defines.end())
 | 
						|
        {
 | 
						|
            runtimeVersion = _defines["RUNTIME_VERSION"]._value;
 | 
						|
        }
 | 
						|
 | 
						|
        return runtimeVersion;
 | 
						|
    }
 | 
						|
 | 
						|
    bool preProcess(const std::string& filename, std::vector<LineToken>& lineTokens, bool doMacros)
 | 
						|
    {
 | 
						|
        Macro macro;
 | 
						|
        std::vector<Macro> macros;
 | 
						|
        bool buildingMacro = false;
 | 
						|
 | 
						|
        int adjustedLineIndex = 0;
 | 
						|
        for(auto itLine=lineTokens.begin(); itLine != lineTokens.end();)
 | 
						|
        {
 | 
						|
            // Lines containing only white space are skipped
 | 
						|
            LineToken lineToken = *itLine;
 | 
						|
            size_t nonWhiteSpace = lineToken._text.find_first_not_of("  \n\r\f\t\v");
 | 
						|
            if(nonWhiteSpace == std::string::npos)
 | 
						|
            {
 | 
						|
                ++itLine;
 | 
						|
                ++adjustedLineIndex;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            bool eraseLine = false;
 | 
						|
            int lineIndex = int(itLine - lineTokens.begin()) + 1;
 | 
						|
 | 
						|
            // Strip comments
 | 
						|
            size_t commentPos = lineToken._text.find_first_of(";#");
 | 
						|
            if(commentPos != std::string::npos)
 | 
						|
            {
 | 
						|
                lineToken._text = lineToken._text.substr(0, commentPos);
 | 
						|
            }
 | 
						|
 | 
						|
            // Tokenise current line
 | 
						|
            std::vector<std::string> tokens = Expression::tokeniseLine(lineToken._text);
 | 
						|
 | 
						|
            // Valid pre-processor commands
 | 
						|
            if(tokens.size() > 0)
 | 
						|
            {
 | 
						|
                Expression::strToUpper(tokens[0]);
 | 
						|
 | 
						|
                // Remove subroutine header and footer
 | 
						|
                if(tokens[0] == "%SUB"  ||  tokens[0] == "%ENDS")
 | 
						|
                {
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                // Include
 | 
						|
                if(tokens[0] == "%INCLUDE")
 | 
						|
                {
 | 
						|
                    std::vector<LineToken> includeLineTokens;
 | 
						|
                    if(!handleInclude(tokens, lineToken._text, lineIndex, includeLineTokens)) return false;
 | 
						|
 | 
						|
                    // Recursively include everything in order
 | 
						|
                    if(!preProcess(filename, includeLineTokens, false))
 | 
						|
                    {
 | 
						|
                        fprintf(stderr, "Assembler::preProcess() : '%s:%d' : bad include file : '%s'\n", filename.c_str(), adjustedLineIndex, tokens[1].c_str());
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Remove original include line and replace with include text
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    itLine = lineTokens.insert(itLine, includeLineTokens.begin(), includeLineTokens.end());
 | 
						|
                    ++adjustedLineIndex -= int(includeLineTokens.end() - includeLineTokens.begin());
 | 
						|
                    eraseLine = true;
 | 
						|
                }
 | 
						|
                // Include path
 | 
						|
                else if(tokens[0] == "%INCLUDEPATH"  &&  tokens.size() > 1)
 | 
						|
                {
 | 
						|
                    if(Expression::isStringValid(tokens[1]))
 | 
						|
                    {
 | 
						|
                        // Strip quotes
 | 
						|
                        std::string includePath = tokens[1];
 | 
						|
                        includePath.erase(0, 1);
 | 
						|
                        includePath.erase(includePath.size()-1, 1);
 | 
						|
 | 
						|
                        // Prepend current file path to relative paths
 | 
						|
                        if(includePath.find(":") == std::string::npos  &&  includePath[0] != '/')
 | 
						|
                        {
 | 
						|
                            std::string filepath = Loader::getFilePath();
 | 
						|
                            size_t slash = filepath.find_last_of("\\/");
 | 
						|
                            filepath = (slash != std::string::npos) ? filepath.substr(0, slash) : ".";
 | 
						|
                            includePath = filepath + "/" + includePath;
 | 
						|
                        }
 | 
						|
 | 
						|
                        _includePath = includePath;
 | 
						|
                        itLine = lineTokens.erase(itLine);
 | 
						|
                        eraseLine = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // Define
 | 
						|
                else if(tokens[0] == "%DEFINE")
 | 
						|
                {
 | 
						|
                    if(!createDefine(filename, tokens, 3)) return false;
 | 
						|
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    eraseLine = true;
 | 
						|
                }
 | 
						|
                // If
 | 
						|
                else if(tokens[0] == "%IF")
 | 
						|
                {
 | 
						|
                    if(!handleIfDefine(filename, tokens, adjustedLineIndex)) return false;
 | 
						|
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    eraseLine = true;
 | 
						|
                }
 | 
						|
                // EndIf
 | 
						|
                else if(tokens[0] == "%ENDIF")
 | 
						|
                {
 | 
						|
                    if(!handleEndIfDefine(filename, tokens, adjustedLineIndex)) return false;
 | 
						|
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    eraseLine = true;
 | 
						|
                }
 | 
						|
                // Else
 | 
						|
                else if(tokens[0] == "%ELSE")
 | 
						|
                {
 | 
						|
                    if(!handleElseDefine(filename, tokens, adjustedLineIndex)) return false;
 | 
						|
 | 
						|
                    itLine = lineTokens.erase(itLine);
 | 
						|
                    eraseLine = true;
 | 
						|
                }
 | 
						|
                // Check if there is a current define and delete code if define is disabled
 | 
						|
                else if(!_currentDefine.empty())
 | 
						|
                {
 | 
						|
                    std::string define = _currentDefine.top();
 | 
						|
                    if(_defines.find(define) == _defines.end())
 | 
						|
                    {
 | 
						|
                        fprintf(stderr, "Assembler::preProcess() : '%s:%d' : define '%s' does not exist\n", filename.c_str(), adjustedLineIndex, define.c_str());
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if(!_defines[define]._enabled)
 | 
						|
                    {                    
 | 
						|
                        itLine = lineTokens.erase(itLine);
 | 
						|
                        eraseLine = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // Build macro
 | 
						|
                else if(doMacros)
 | 
						|
                {
 | 
						|
                    if(tokens[0] == "%MACRO"  &&  !buildingMacro)
 | 
						|
                    {
 | 
						|
                        if(!handleMacroStart(filename, lineToken, tokens, macro, adjustedLineIndex)) return false;
 | 
						|
 | 
						|
                        buildingMacro = true;
 | 
						|
                    }
 | 
						|
                    else if(buildingMacro  &&  tokens[0] == "%ENDM")
 | 
						|
                    {
 | 
						|
                        if(!handleMacroEnd(macros, macro)) return false;
 | 
						|
                        buildingMacro = false;
 | 
						|
                    }
 | 
						|
                    else if(buildingMacro)
 | 
						|
                    {
 | 
						|
                        macro._lines.push_back(lineToken._text);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if(!eraseLine)
 | 
						|
            {
 | 
						|
                ++itLine;
 | 
						|
                ++adjustedLineIndex;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Check matching %if %endif
 | 
						|
        if(!_currentDefine.empty())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::preProcess() : '%s' : missing '%%endif'\n", filename.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Handle complete macros
 | 
						|
        if(doMacros  &&  !handleMacros(macros, lineTokens)) return false;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    int getAsmOpcodeSize(const std::string& filename, std::vector<LineToken>& lineTokens)
 | 
						|
    {
 | 
						|
        // Pre-processor
 | 
						|
        if(!preProcess(filename, lineTokens, true)) return -1;
 | 
						|
 | 
						|
        // Tokenise lines
 | 
						|
        int vasmSize = 0;
 | 
						|
        for(int i=0; i<int(lineTokens.size()); i++)
 | 
						|
        {
 | 
						|
            std::vector<std::string> tokens = Expression::tokeniseLine(lineTokens[i]._text);
 | 
						|
            for(int j=0; j<int(tokens.size()); j++)
 | 
						|
            {
 | 
						|
                std::string token = Expression::strToUpper(tokens[j]);
 | 
						|
                vasmSize += getAsmOpcodeSize(token);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return vasmSize;
 | 
						|
    }
 | 
						|
 | 
						|
    int getAsmOpcodeSizeFile(const std::string& filename)
 | 
						|
    {
 | 
						|
        std::ifstream infile(filename);
 | 
						|
        if(!infile.is_open())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::getAsmOpcodeSizeFile() : failed to open file : '%s'\n", filename.c_str());
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        // Get file
 | 
						|
        int numLines = 0;
 | 
						|
        LineToken lineToken;
 | 
						|
        std::vector<LineToken> lineTokens;
 | 
						|
        while(!infile.eof())
 | 
						|
        {
 | 
						|
            std::getline(infile, lineToken._text);
 | 
						|
            lineTokens.push_back(lineToken);
 | 
						|
 | 
						|
            if(!infile.good() && !infile.eof())
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::getAsmOpcodeSizeFile() : '%s:%d' : bad lineToken '%s'\n", filename.c_str(), numLines+1, lineToken._text.c_str());
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
 | 
						|
            numLines++;
 | 
						|
        }
 | 
						|
        
 | 
						|
        return getAsmOpcodeSize(filename, lineTokens);
 | 
						|
    }
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
    bool handleBreakPoints(ParseType parse, const std::string& lineToken, int lineNumber)
 | 
						|
    {
 | 
						|
        UNREFERENCED_PARAM(lineNumber);
 | 
						|
 | 
						|
        std::string input = lineToken;
 | 
						|
        Expression::strToUpper(input);
 | 
						|
 | 
						|
        if(input.find("_BREAKPOINT_") != std::string::npos)
 | 
						|
        {
 | 
						|
            if(parse == MnemonicPass) Editor::addVpcBreakPoint(_currentAddress);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool addGprintf(const Gprintf& gprintf, uint16_t address)
 | 
						|
    {
 | 
						|
        if(_gprintfs.find(address) != _gprintfs.end()) return false;
 | 
						|
 | 
						|
        _gprintfs[address] = gprintf;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool parseGprintfFormat(const std::string& fmtstr, const std::vector<std::string>& variables, std::vector<Gprintf::Var>& vars, std::vector<std::string>& subs)
 | 
						|
    {
 | 
						|
        const char* fmt = fmtstr.c_str();
 | 
						|
        std::string sub;
 | 
						|
        char chr;
 | 
						|
 | 
						|
        int width = 0, index = 0;
 | 
						|
        bool foundToken = false;
 | 
						|
 | 
						|
        while((chr = *fmt++) != 0)
 | 
						|
        {
 | 
						|
            if(index + 1 > int(variables.size())) return false;
 | 
						|
 | 
						|
            if(chr == '%'  ||  foundToken)
 | 
						|
            {
 | 
						|
                foundToken = true;
 | 
						|
                Gprintf::Format format = Gprintf::Int;
 | 
						|
                sub += chr;
 | 
						|
 | 
						|
                switch(chr)
 | 
						|
                {
 | 
						|
                    case '0':
 | 
						|
                    {
 | 
						|
                        // Maximum field width of 16 digits
 | 
						|
                        width = strtol(fmt, nullptr, 10) % 17;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                    case 'c': format = Gprintf::Chr; break;
 | 
						|
                    case 'd': format = Gprintf::Int; break;
 | 
						|
                    case 'b': format = Gprintf::Bin; break;
 | 
						|
                    case 'q':
 | 
						|
                    case 'o': format = Gprintf::Oct; break;
 | 
						|
                    case 'x': format = Gprintf::Hex; break;
 | 
						|
                    case 's': format = Gprintf::Str; break;
 | 
						|
 | 
						|
                    default: break;
 | 
						|
                }
 | 
						|
 | 
						|
                if(chr == 'c' || chr == 'd' || chr == 'b' || chr == 'q' || chr == 'o' || chr == 'x' || chr == 's')
 | 
						|
                {
 | 
						|
                    foundToken = false;
 | 
						|
                    Gprintf::Var var = {0, format, width, 0x0000, variables[index++]};
 | 
						|
                    vars.push_back(var);
 | 
						|
                    subs.push_back(sub);
 | 
						|
                    sub.clear();
 | 
						|
                    width = 0;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool handleGprintf(ParseType parse, const std::string& lineToken, int lineNumber)
 | 
						|
    {
 | 
						|
        std::string input = lineToken;
 | 
						|
        Expression::strToUpper(input);
 | 
						|
 | 
						|
        // Handle non commented GPRINTF
 | 
						|
        size_t gprintfPos = input.find("GPRINTF");
 | 
						|
        if(gprintfPos != std::string::npos  &&  input.find_last_of(';', gprintfPos) == std::string::npos)
 | 
						|
        {
 | 
						|
            size_t lbra, rbra;
 | 
						|
            if(Expression::findMatchingBrackets(lineToken, 0, lbra, rbra))
 | 
						|
            {
 | 
						|
                size_t quote1 = lineToken.find_first_of("\"", lbra+1);
 | 
						|
                size_t quote2 = lineToken.find_last_of("\"", rbra-1);
 | 
						|
                bool quotes = (quote1 != std::string::npos  &&  quote2 != std::string::npos  &&  (quote2 - quote1 > 0));
 | 
						|
                if(quotes)
 | 
						|
                {
 | 
						|
                    if(parse == MnemonicPass)
 | 
						|
                    {
 | 
						|
                        std::string formatText = lineToken.substr(quote1+1, quote2 - (quote1+1));
 | 
						|
                        std::string variableText = lineToken.substr(quote2+1, rbra - (quote2+1));
 | 
						|
 | 
						|
                        std::vector<Gprintf::Var> vars;
 | 
						|
                        std::vector<std::string> subs;
 | 
						|
                        std::vector<std::string> variables = Expression::tokenise(variableText, ',');
 | 
						|
                        parseGprintfFormat(formatText, variables, vars, subs);
 | 
						|
 | 
						|
                        Gprintf gprintf = {_currentAddress, lineNumber, lineToken, formatText, vars, subs};
 | 
						|
                        if(_gprintfs.find(_currentAddress) != _gprintfs.end())
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "Assembler::handleGprintf() : '%s:%d' : gprintf at address 0x%0x4 already exists\n", lineToken.c_str(), lineNumber, _currentAddress);
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
 | 
						|
                        _gprintfs[_currentAddress] = gprintf;
 | 
						|
                    }
 | 
						|
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            fprintf(stderr, "Assembler::handleGprintf() : '%s:%d' : bad gprintf format\n", lineToken.c_str(), lineNumber);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool parseGprintfVar(const std::string& param, uint16_t& data)
 | 
						|
    {
 | 
						|
        bool success = Expression::stringToU16(param, data);
 | 
						|
        if(!success)
 | 
						|
        {
 | 
						|
            std::vector<std::string> tokens;
 | 
						|
            tokens.push_back(param);
 | 
						|
 | 
						|
            // Search equates
 | 
						|
            Equate equate;
 | 
						|
            Label label;
 | 
						|
            if((success = evaluateEquateOperand(tokens, 0, equate, false)) == true)
 | 
						|
            {
 | 
						|
                data = equate._operand;
 | 
						|
            }
 | 
						|
            // Search labels
 | 
						|
            else if((success = evaluateLabelOperand(tokens, 0, label, false)) == true)
 | 
						|
            {
 | 
						|
                data = label._address;
 | 
						|
            }
 | 
						|
            // Normal expression
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if(Expression::isExpression(param) == Expression::HasOperators)
 | 
						|
                {
 | 
						|
                    Expression::Numeric value;
 | 
						|
                    if(Expression::parse(param, _lineNumber, value))
 | 
						|
                    {
 | 
						|
                        data = int16_t(std::lround(value._value));
 | 
						|
                        success = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return success;
 | 
						|
    }
 | 
						|
 | 
						|
    bool parseGprintfs(void)
 | 
						|
    {
 | 
						|
        for(auto it=_gprintfs.begin(); it!=_gprintfs.end(); it++ )
 | 
						|
        {
 | 
						|
            Gprintf& gprintf = it->second;
 | 
						|
 | 
						|
            for(int j=0; j<int(gprintf._vars.size()); j++)
 | 
						|
            {
 | 
						|
                uint16_t addr = 0x0000;
 | 
						|
                std::string token = gprintf._vars[j]._text;
 | 
						|
        
 | 
						|
                // Strip white space
 | 
						|
                token.erase(remove_if(token.begin(), token.end(), isspace), token.end());
 | 
						|
                gprintf._vars[j]._text = token;
 | 
						|
 | 
						|
                // Check for indirection
 | 
						|
                size_t asterisk = token.find_first_of("*");
 | 
						|
                if(asterisk != std::string::npos)
 | 
						|
                {
 | 
						|
                    // Don't overwrites gtBASIC vars
 | 
						|
                    if(gprintf._vars[j]._indirection == 0) gprintf._vars[j]._indirection = 1;
 | 
						|
                    token = token.substr(asterisk + 1);
 | 
						|
                }
 | 
						|
 | 
						|
                if(!parseGprintfVar(token, addr))
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "Assembler::parseGprintfs() : '%s:%d' : error in gprintf(), missing label or equate '%s'\n", gprintf._lineToken.c_str(), gprintf._lineNumber, token.c_str());
 | 
						|
                    _gprintfs.erase(it);
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                gprintf._vars[j]._data = addr;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    bool getGprintfString(const Gprintf& gprintf, std::string& gstring)
 | 
						|
    {
 | 
						|
        gstring = gprintf._format;
 | 
						|
   
 | 
						|
        size_t subIndex = 0;
 | 
						|
        for(int i=0; i<int(gprintf._vars.size()); i++)
 | 
						|
        {
 | 
						|
            char token[256];
 | 
						|
 | 
						|
            // Use indirection if required
 | 
						|
            uint16_t data = (gprintf._vars[i]._indirection) ? Cpu::getRAM16(gprintf._vars[i]._data) : gprintf._vars[i]._data;
 | 
						|
 | 
						|
            // Maximum field width of 16 digits
 | 
						|
            uint8_t width = gprintf._vars[i]._width % 17;
 | 
						|
            std::string fieldWidth = "%";
 | 
						|
            if(width) fieldWidth = std::string("%0" + std::to_string(width));
 | 
						|
 | 
						|
            switch(gprintf._vars[i]._format)
 | 
						|
            {
 | 
						|
                case Gprintf::Chr: fieldWidth += "c"; sprintf(token, fieldWidth.c_str(), data); break;
 | 
						|
                case Gprintf::Int: fieldWidth += "d"; sprintf(token, fieldWidth.c_str(), data); break;
 | 
						|
                case Gprintf::Oct: fieldWidth += "o"; sprintf(token, fieldWidth.c_str(), data); break;
 | 
						|
                case Gprintf::Hex: fieldWidth += "x"; sprintf(token, fieldWidth.c_str(), data); break;
 | 
						|
 | 
						|
                // Strings are always indirect and assume that length is first byte
 | 
						|
                case Gprintf::Str:
 | 
						|
                {
 | 
						|
                    // gtBASIC strings are dereferenced by 2 levels of indirection
 | 
						|
                    uint16_t addr = (gprintf._vars[i]._indirection == 2) ? Cpu::getRAM16(gprintf._vars[i]._data) : gprintf._vars[i]._data;
 | 
						|
 | 
						|
                    // Maximum length of 254, first byte is length, last byte is '0' trailing delimiter
 | 
						|
                    uint8_t length = Cpu::getRAM(addr) % 254;
 | 
						|
                    for(uint16_t j=0; j<length; j++) token[j] = Cpu::getRAM(addr + j + 1);
 | 
						|
                    token[length] = 0;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                case Gprintf::Bin:
 | 
						|
                {
 | 
						|
                    for(int j=width-1; j>=0; j--)
 | 
						|
                    {
 | 
						|
                        token[width-1 - j] = '0' + ((data >> j) & 1);
 | 
						|
                    }
 | 
						|
                    token[width] = 0;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
 | 
						|
                default: return false;
 | 
						|
            }
 | 
						|
 | 
						|
            // Replace substrings
 | 
						|
            subIndex = gstring.find(gprintf._subs[i], subIndex);
 | 
						|
            if(subIndex != std::string::npos)
 | 
						|
            {
 | 
						|
                gstring.erase(subIndex, gprintf._subs[i].size());
 | 
						|
                gstring.insert(subIndex, token);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void handleGprintfs(void)
 | 
						|
    {
 | 
						|
        if(_gprintfs.size() == 0) return;
 | 
						|
 | 
						|
        if(_gprintfs.find(Cpu::getVPC()) != _gprintfs.end())
 | 
						|
        {
 | 
						|
            std::string gstring;
 | 
						|
            const Gprintf& gprintf = _gprintfs[Cpu::getVPC()];
 | 
						|
 | 
						|
            getGprintfString(gprintf, gstring);
 | 
						|
            fprintf(stderr, "gprintf() : 0x%04X : %s\n", gprintf._address, gstring.c_str());
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    uint8_t sysHelper(const std::string& opcodeStr, uint16_t operand, const std::string& filename, int lineNumber)
 | 
						|
    {
 | 
						|
        std::string opcode = opcodeStr;
 | 
						|
        Expression::strToUpper(opcode); 
 | 
						|
        
 | 
						|
        if(opcode != "SYS") return uint8_t(operand);
 | 
						|
 | 
						|
        if((operand & 0x0001) || operand < 28 || operand > 284)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::sysHelper() : '%s:%d' : SYS operand '%d' must be an even constant in [28, 284]\n", filename.c_str(), lineNumber, operand);
 | 
						|
            return uint8_t(operand);
 | 
						|
        }
 | 
						|
 | 
						|
        return uint8_t((270 - operand / 2) & 0x00FF);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    void clearAssembler(bool dontClearGprintfs)
 | 
						|
    {
 | 
						|
        _byteCode.clear();
 | 
						|
        _labels.clear();
 | 
						|
        _equates.clear();
 | 
						|
        _instructions.clear();
 | 
						|
        _callTableEntries.clear();
 | 
						|
        if(!dontClearGprintfs) _gprintfs.clear();
 | 
						|
 | 
						|
        clearDefines();        
 | 
						|
 | 
						|
        _vSpMin = 0x00;
 | 
						|
        _callTablePtr = 0x0000;
 | 
						|
 | 
						|
        Expression::setExprFunc(Expression::expression);
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
        Editor::clearVpcBreakPoints();
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    bool assemble(const std::string& filename, uint16_t startAddress, bool dontClearGprintfs)
 | 
						|
    {
 | 
						|
        std::ifstream infile(filename);
 | 
						|
        if(!infile.is_open())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Assembler::assemble() : failed to open file '%s'\n", filename.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        clearAssembler(dontClearGprintfs);
 | 
						|
 | 
						|
        _startAddress = startAddress;
 | 
						|
        _currentAddress = _startAddress;
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
        Loader::disableUploads(false);
 | 
						|
#endif
 | 
						|
 | 
						|
        // Get file
 | 
						|
        int numLines = 0;
 | 
						|
        LineToken lineToken;
 | 
						|
        std::vector<LineToken> lineTokens;
 | 
						|
        while(!infile.eof())
 | 
						|
        {
 | 
						|
            std::getline(infile, lineToken._text);
 | 
						|
            lineTokens.push_back(lineToken);
 | 
						|
 | 
						|
            if(!infile.good() && !infile.eof())
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Assembler::assemble() : '%s:%d' : bad lineToken '%s'\n", filename.c_str(), numLines+1, lineToken._text.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            numLines++;
 | 
						|
        }
 | 
						|
 | 
						|
        // Pre-processor
 | 
						|
        if(!preProcess(filename, lineTokens, true)) return false;
 | 
						|
 | 
						|
        fprintf(stderr, "\n*******************************************************\n");
 | 
						|
        fprintf(stderr, "* Assembling file : '%s'\n", filename.c_str());
 | 
						|
        fprintf(stderr, "*******************************************************\n");
 | 
						|
 | 
						|
        numLines = int(lineTokens.size());
 | 
						|
 | 
						|
        // The mnemonic pass we evaluate all the equates and labels, the code pass is for the opcodes and operands
 | 
						|
        for(int parse=MnemonicPass; parse<NumParseTypes; parse++)
 | 
						|
        {
 | 
						|
            for(_lineNumber=0; _lineNumber<numLines; _lineNumber++)
 | 
						|
            {
 | 
						|
                lineToken = lineTokens[_lineNumber];
 | 
						|
 | 
						|
                // Lines containing only white space are skipped
 | 
						|
                size_t nonWhiteSpace = lineToken._text.find_first_not_of("  \n\r\f\t\v");
 | 
						|
                if(nonWhiteSpace == std::string::npos) continue;
 | 
						|
 | 
						|
                int tokenIndex = 0;
 | 
						|
 | 
						|
                // Tokenise current line
 | 
						|
                std::vector<std::string> tokens = Expression::tokeniseLine(lineToken._text);
 | 
						|
                if(tokens.size() == 0)
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "Assembler::assemble() : '%s:%d' : unknown error '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                // Comments
 | 
						|
                if(tokens.size() > 0  &&  tokens[0].find_first_of(";#") != std::string::npos) continue;
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
                // Gprintf lines are skipped
 | 
						|
                if(handleGprintf(ParseType(parse), lineToken._text, _lineNumber+1)) continue;
 | 
						|
 | 
						|
                // _breakPoint_ lines are skipped
 | 
						|
                if(handleBreakPoints(ParseType(parse), lineToken._text, _lineNumber+1)) continue;
 | 
						|
#endif
 | 
						|
 | 
						|
                // Starting address, labels and equates
 | 
						|
                if(nonWhiteSpace == 0)
 | 
						|
                {
 | 
						|
                    if(tokens.size() >= 2)
 | 
						|
                    {
 | 
						|
                        EvaluateResult result = evaluateEquates(tokens, (ParseType)parse);
 | 
						|
                        if(result == NotFound)
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "Assembler::assemble() : '%s:%d' : missing equate '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        else if(result == Duplicate)
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "Assembler::assemble() : '%s:%d' : duplicate equate '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        // Skip equate lines
 | 
						|
                        else if(result == Success) 
 | 
						|
                        {
 | 
						|
                            continue;
 | 
						|
                        }
 | 
						|
                            
 | 
						|
                        // Labels
 | 
						|
                        result = EvaluateLabels(tokens, (ParseType)parse, tokenIndex);
 | 
						|
                        if(result == Reserved)
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "Assembler::assemble() : '%s:%d' : can't use a reserved word in label '%s'\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                        else if(result == Duplicate)
 | 
						|
                        {
 | 
						|
                            fprintf(stderr, "Assembler::assemble() : '%s:%d' : duplicate label '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                            return false;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    // On to the next token even if we failed with this one
 | 
						|
                    if(tokens.size() > 1) tokenIndex++;
 | 
						|
                }
 | 
						|
 | 
						|
                // Opcode
 | 
						|
                bool operandValid = false;
 | 
						|
                uint16_t defineOffset;
 | 
						|
                std::string opcodeStr = tokens[tokenIndex++];
 | 
						|
                InstructionType instructionType = getOpcode(opcodeStr, defineOffset);
 | 
						|
                uint8_t opcode0 = instructionType._opcode0;
 | 
						|
                uint8_t opcode1 = instructionType._opcode1;
 | 
						|
                int outputSize = instructionType._byteSize;
 | 
						|
                OpcodeType opcodeType = instructionType._opcodeType;
 | 
						|
                Instruction instruction = {false, false, ByteSize(outputSize), opcode0, 0x00, 0x00, _currentAddress, opcodeType};
 | 
						|
 | 
						|
                if(outputSize == BadSize)
 | 
						|
                {
 | 
						|
                    fprintf(stderr, "Assembler::assemble() : '%s:%d' : bad Opcode '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
 | 
						|
                // Compound instructions that require a Mnemonic pass
 | 
						|
                bool compoundInstruction = false;
 | 
						|
                if(opcodeType == ReservedDB  ||  opcodeType == ReservedDBR)
 | 
						|
                {
 | 
						|
                    compoundInstruction = true;
 | 
						|
                    if(parse == MnemonicPass)
 | 
						|
                    {
 | 
						|
                        outputSize = OneByte; // first instruction has already been parsed
 | 
						|
                        if(tokenIndex + 1 < int(tokens.size()))
 | 
						|
                        {
 | 
						|
                            if(!handleDefineByte(tokens, tokenIndex, instruction, false, defineOffset, outputSize))
 | 
						|
                            {
 | 
						|
                                fprintf(stderr, "Assembler::assemble() : '%s:%d' : bad DB data '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                                return false;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else if(opcodeType == ReservedDW  ||  opcodeType == ReservedDWR)
 | 
						|
                {
 | 
						|
                    compoundInstruction = true;
 | 
						|
                    if(parse == MnemonicPass)
 | 
						|
                    {
 | 
						|
                        outputSize = TwoBytes; // first instruction has already been parsed
 | 
						|
                        if(tokenIndex + 1 < int(tokens.size()))
 | 
						|
                        {
 | 
						|
                            if(!handleDefineWord(tokens, tokenIndex, instruction, false, defineOffset, outputSize))
 | 
						|
                            {
 | 
						|
                                fprintf(stderr, "Assembler::assemble() : '%s:%d' : bad DW data '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                                return false;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                if(parse == CodePass)
 | 
						|
                {
 | 
						|
                    // Native NOP
 | 
						|
                    if(opcodeType == Native  &&  opcode0 == 0x02)
 | 
						|
                    {
 | 
						|
                        operandValid = true;
 | 
						|
                    }
 | 
						|
                    // Missing operand
 | 
						|
                    else if((outputSize == TwoBytes  ||  outputSize == ThreeBytes)  &&  int(tokens.size()) <= tokenIndex)
 | 
						|
                    {
 | 
						|
                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : missing operand/s '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                        return false;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // First instruction inherits start address
 | 
						|
                    if(_instructions.size() == 0)
 | 
						|
                    {
 | 
						|
                        instruction._address = _startAddress;
 | 
						|
                        instruction._isCustomAddress = true;
 | 
						|
                        _currentAddress = _startAddress;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Custom address
 | 
						|
                    for(int i=0; i<int(_equates.size()); i++)
 | 
						|
                    {
 | 
						|
                        if(_equates[i]._name == tokens[0]  &&  _equates[i]._isCustomAddress)
 | 
						|
                        {
 | 
						|
                            instruction._address = _equates[i]._operand;
 | 
						|
                            instruction._isCustomAddress = true;
 | 
						|
                            _currentAddress = _equates[i]._operand;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Operand
 | 
						|
                    switch(outputSize)
 | 
						|
                    {
 | 
						|
                        case OneByte:
 | 
						|
                        {
 | 
						|
                            _instructions.push_back(instruction);
 | 
						|
                            if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                        }
 | 
						|
                        break;
 | 
						|
 | 
						|
                        case TwoBytes:
 | 
						|
                        {
 | 
						|
                            uint8_t operand = 0x00;
 | 
						|
 | 
						|
                            // HALT
 | 
						|
                            if(opcodeType == vCpu  &&  opcode0 == OPCODE_V_HALT  &&  opcode1 == OPERAND_V_HALT)
 | 
						|
                            {
 | 
						|
                                operand = opcode1;
 | 
						|
                                operandValid = true;
 | 
						|
                            }
 | 
						|
                            // BRA
 | 
						|
                            else if(opcodeType == vCpu  &&  opcode0 == OPCODE_V_BRA)
 | 
						|
                            {
 | 
						|
                                // Search for branch label
 | 
						|
                                Label label;
 | 
						|
                                if(evaluateLabelOperand(tokens, tokenIndex, label, false))
 | 
						|
                                {
 | 
						|
                                    operandValid = true;
 | 
						|
                                    operand = uint8_t(label._address) - BRANCH_ADJUSTMENT;
 | 
						|
                                }
 | 
						|
                                // Allow branches to raw hex values, lets hope the user knows what he is doing
 | 
						|
                                else if(Expression::stringToU8(tokens[tokenIndex], operand))
 | 
						|
                                {
 | 
						|
                                    operandValid = true;
 | 
						|
                                    operand -= BRANCH_ADJUSTMENT;
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    fprintf(stderr, "Assembler::assemble() : '%s:%d' : label '%s' missing\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                                    return false;
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                            // CALL+
 | 
						|
                            else if(opcodeType == vCpu  &&  opcode0 == OPCODE_V_CALL)
 | 
						|
                            {
 | 
						|
                                // Search for call label
 | 
						|
                                if(_callTablePtr  &&  operand != GIGA_V_AC)
 | 
						|
                                {
 | 
						|
                                    Label label;
 | 
						|
                                    if(evaluateLabelOperand(tokens, tokenIndex, label, false))
 | 
						|
                                    {
 | 
						|
                                        // Search for address
 | 
						|
                                        bool newLabel = true;
 | 
						|
                                        uint16_t address = uint16_t(label._address);
 | 
						|
                                        for(int i=0; i<int(_callTableEntries.size()); i++)
 | 
						|
                                        {
 | 
						|
                                            if(_callTableEntries[i]._address == address)
 | 
						|
                                            {
 | 
						|
                                                operandValid = true;
 | 
						|
                                                operand = _callTableEntries[i]._operand;
 | 
						|
                                                newLabel = false;
 | 
						|
                                                break;
 | 
						|
                                            }
 | 
						|
                                        }
 | 
						|
 | 
						|
                                        // Found a new call address label, put it's address into the call table and point the call instruction to the call table
 | 
						|
                                        if(newLabel)
 | 
						|
                                        {
 | 
						|
                                            operandValid = true;
 | 
						|
                                            operand = uint8_t(LO_BYTE(_callTablePtr));
 | 
						|
                                            CallTableEntry entry = {operand, address};
 | 
						|
                                            _callTableEntries.push_back(entry);
 | 
						|
                                            _callTablePtr -= 0x0002;
 | 
						|
 | 
						|
                                            // Avoid ONE_CONST_ADDRESS
 | 
						|
                                            if(_callTablePtr == ONE_CONST_ADDRESS)
 | 
						|
                                            {
 | 
						|
                                                fprintf(stderr, "Assembler::assemble() : warning, (safe to ignore), Calltable : 0x%02x : collided with : 0x%02x : on line %d\n", _callTablePtr, ONE_CONST_ADDRESS, _lineNumber+1);
 | 
						|
                                                _callTablePtr -= 0x0002;
 | 
						|
                                            }
 | 
						|
                                            else if(_callTablePtr+1 == ONE_CONST_ADDRESS)
 | 
						|
                                            {
 | 
						|
                                                fprintf(stderr, "Assembler::assemble() : warning, (safe to ignore), Calltable : 0x%02x : collided with : 0x%02x : on line %d\n", _callTablePtr+1, ONE_CONST_ADDRESS, _lineNumber+1);
 | 
						|
                                                _callTablePtr -= 0x0001;
 | 
						|
                                            }
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                // CALL that doesn't use the call table, ('CALL GIGA_V_AC', usually to save zero page memory at the expense of code size and code speed).
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    Equate equate;
 | 
						|
                                    if((operandValid = evaluateEquateOperand(tokens, tokenIndex, equate, false)) == true)
 | 
						|
                                    {
 | 
						|
                                        operand = uint8_t(equate._operand);
 | 
						|
                                    }
 | 
						|
                                    else 
 | 
						|
                                    {
 | 
						|
                                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : label '%s' missing\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                                        return false;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                                
 | 
						|
                            // All other non native 2 byte instructions
 | 
						|
                            if(opcodeType != Native  &&  !operandValid)
 | 
						|
                            {
 | 
						|
                                Label label;
 | 
						|
                                Equate equate;
 | 
						|
 | 
						|
                                // String
 | 
						|
                                size_t quote1 = tokens[tokenIndex].find_first_of("'");
 | 
						|
                                size_t quote2 = tokens[tokenIndex].find_last_of("'");
 | 
						|
                                bool quotes = (quote1 != std::string::npos  &&  quote2 != std::string::npos  &&  (quote2 - quote1 > 1));
 | 
						|
                                if(quotes)
 | 
						|
                                {
 | 
						|
                                    operand = sysHelper(opcodeStr, uint16_t(tokens[tokenIndex][quote1+1]), filename, _lineNumber+1);
 | 
						|
                                }
 | 
						|
                                // Search equates
 | 
						|
                                else if((operandValid = evaluateEquateOperand(tokens, tokenIndex, equate, compoundInstruction)) == true)
 | 
						|
                                {
 | 
						|
                                    operand = sysHelper(opcodeStr, equate._operand, filename, _lineNumber+1);
 | 
						|
                                }
 | 
						|
                                // Search labels
 | 
						|
                                else if((operandValid = evaluateLabelOperand(tokens, tokenIndex, label, compoundInstruction)) == true)
 | 
						|
                                {
 | 
						|
                                    operand = sysHelper(opcodeStr, label._address, filename, _lineNumber+1);
 | 
						|
                                }
 | 
						|
                                else if(Expression::isExpression(tokens[tokenIndex]) == Expression::HasOperators)
 | 
						|
                                {
 | 
						|
                                    Expression::Numeric value;
 | 
						|
                                    std::string input;
 | 
						|
                                    preProcessExpression(tokens, tokenIndex, input, true);
 | 
						|
                                    if(!Expression::parse(input, _lineNumber, value)) return false;
 | 
						|
                                    operand = sysHelper(opcodeStr, uint16_t(std::lround(value._value)), filename, _lineNumber+1);
 | 
						|
                                    operandValid = true;
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    uint16_t operandU16 = 0;
 | 
						|
                                    operandValid = Expression::stringToU16(tokens[tokenIndex], operandU16);
 | 
						|
                                    if(!operandValid)
 | 
						|
                                    {
 | 
						|
                                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : invalid label/Equate '%s'\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                                        return false;
 | 
						|
                                    }
 | 
						|
 | 
						|
                                    operand = sysHelper(opcodeStr, operandU16, filename, _lineNumber+1);
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
 | 
						|
                            // Native instructions
 | 
						|
                            if(opcodeType == Native)
 | 
						|
                            {
 | 
						|
                                if(!operandValid)
 | 
						|
                                {
 | 
						|
                                    if(!handleNativeInstruction(tokens, tokenIndex, opcode0, operand))
 | 
						|
                                    {
 | 
						|
                                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : native instruction '%s' is malformed\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                                        return false;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
 | 
						|
                                instruction._isRomAddress = true;
 | 
						|
                                instruction._opcode = opcode0;
 | 
						|
                                instruction._operand0 = uint8_t(LO_BYTE(operand));
 | 
						|
                                _instructions.push_back(instruction);
 | 
						|
                                if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
                                uint16_t add = instruction._address>>1;
 | 
						|
                                uint8_t opc = Cpu::getROM(add, 0);
 | 
						|
                                uint8_t ope = Cpu::getROM(add, 1);
 | 
						|
                                if(instruction._opcode != opc  ||  instruction._operand0 != ope)
 | 
						|
                                {
 | 
						|
                                    fprintf(stderr, "Assembler::assemble() : '%s:%d' : ROM Native instruction mismatch : 0x%04X : ASM=0x%02X%02X : ROM=0x%02X%02X\n", filename.c_str(), _lineNumber+1, add, 
 | 
						|
                                                                                                                                                                      instruction._opcode, instruction._operand0,
 | 
						|
                                                                                                                                                                      opc, ope);
 | 
						|
 | 
						|
                                    // Fix mismatched instruction?
 | 
						|
                                    //instruction._opcode = opc;
 | 
						|
                                    //instruction._operand0 = ope;
 | 
						|
                                    //_instructions.back() = instruction;
 | 
						|
                                }
 | 
						|
#endif
 | 
						|
                            }
 | 
						|
                            // Reserved assembler opcode DB, (define byte)
 | 
						|
                            else if(opcodeType == ReservedDB  ||  opcodeType == ReservedDBR)
 | 
						|
                            {
 | 
						|
                                // Push first operand
 | 
						|
                                outputSize = OneByte;
 | 
						|
                                instruction._isRomAddress = (opcodeType == ReservedDBR) ? true : false;
 | 
						|
                                instruction._byteSize = ByteSize(outputSize);
 | 
						|
                                instruction._opcode = uint8_t(LO_BYTE(operand));
 | 
						|
                                if(defineOffset != 0)
 | 
						|
                                {
 | 
						|
                                    instruction._address = _currentAddress;
 | 
						|
                                    instruction._isCustomAddress = true;
 | 
						|
                                    _currentAddress += defineOffset;
 | 
						|
                                }
 | 
						|
                                _instructions.push_back(instruction);
 | 
						|
    
 | 
						|
                                // Push any remaining operands
 | 
						|
                                if(tokenIndex + 1 < int(tokens.size()))
 | 
						|
                                {
 | 
						|
                                    if(!handleDefineByte(tokens, tokenIndex, instruction, true, defineOffset, outputSize))
 | 
						|
                                    {
 | 
						|
                                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : bad DB data '%s'\n", filename.c_str(), _lineNumber+1, lineToken._text.c_str());
 | 
						|
                                        return false;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
 | 
						|
                                if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                            }
 | 
						|
                            // Normal instructions
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                instruction._operand0 = operand;
 | 
						|
                                _instructions.push_back(instruction);
 | 
						|
                                if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        break;
 | 
						|
                            
 | 
						|
                        case ThreeBytes:
 | 
						|
                        {
 | 
						|
                            // BCC
 | 
						|
                            if(opcode1)
 | 
						|
                            {
 | 
						|
                                // Search for branch label
 | 
						|
                                Label label;
 | 
						|
                                uint8_t operand = 0x00;
 | 
						|
                                if(evaluateLabelOperand(tokens, tokenIndex, label, false))
 | 
						|
                                {
 | 
						|
                                    operand = uint8_t(label._address) - BRANCH_ADJUSTMENT;
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    fprintf(stderr, "Assembler::assemble() : '%s:%d' : label '%s' missing\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                                    return false;
 | 
						|
                                }
 | 
						|
 | 
						|
                                instruction._operand0 = opcode1;
 | 
						|
                                instruction._operand1 = LO_BYTE(operand);
 | 
						|
                                _instructions.push_back(instruction);
 | 
						|
                                if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                            }
 | 
						|
                            // All other 3 byte instructions
 | 
						|
                            else
 | 
						|
                            {
 | 
						|
                                uint16_t operand = 0x0000;
 | 
						|
                                Label label;
 | 
						|
                                Equate equate;
 | 
						|
 | 
						|
                                // Search equates
 | 
						|
                                if((operandValid = evaluateEquateOperand(tokens, tokenIndex, equate, compoundInstruction)) == true)
 | 
						|
                                {
 | 
						|
                                    operand = equate._operand;
 | 
						|
                                }
 | 
						|
                                // Search labels
 | 
						|
                                else if((operandValid = evaluateLabelOperand(tokens, tokenIndex, label, compoundInstruction)) == true)
 | 
						|
                                {
 | 
						|
                                    operand = label._address;
 | 
						|
                                }
 | 
						|
                                else if(Expression::isExpression(tokens[tokenIndex]) == Expression::HasOperators)
 | 
						|
                                {
 | 
						|
                                    Expression::Numeric value;
 | 
						|
                                    std::string input;
 | 
						|
                                    preProcessExpression(tokens, tokenIndex, input, true);
 | 
						|
                                    if(!Expression::parse((char*)input.c_str(), _lineNumber, value)) return false;
 | 
						|
                                    operand = int16_t(std::lround(value._value));
 | 
						|
                                    operandValid = true;
 | 
						|
                                }
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    operandValid = Expression::stringToU16(tokens[tokenIndex], operand);
 | 
						|
                                    if(!operandValid)
 | 
						|
                                    {
 | 
						|
                                        fprintf(stderr, "Assembler::assemble() : '%s:%d' : invalid label/equate '%s'\n", filename.c_str(), _lineNumber+1, tokens[tokenIndex].c_str());
 | 
						|
                                        return false;
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                
 | 
						|
                                // Reserved assembler opcode DW, (define word)
 | 
						|
                                if(opcodeType == ReservedDW  ||  opcodeType == ReservedDWR)
 | 
						|
                                {
 | 
						|
                                    // Push first operand
 | 
						|
                                    outputSize = TwoBytes;
 | 
						|
                                    instruction._isRomAddress = (opcodeType == ReservedDWR) ? true : false;
 | 
						|
                                    instruction._byteSize = ByteSize(outputSize);
 | 
						|
                                    instruction._opcode   = uint8_t(LO_BYTE(operand));
 | 
						|
                                    instruction._operand0 = uint8_t(HI_BYTE(operand));
 | 
						|
                                    if(defineOffset != 0)
 | 
						|
                                    {
 | 
						|
                                        instruction._address = _currentAddress;
 | 
						|
                                        instruction._isCustomAddress = true;
 | 
						|
                                        _currentAddress += defineOffset * 2;
 | 
						|
                                    }
 | 
						|
                                    _instructions.push_back(instruction);
 | 
						|
 | 
						|
                                    // Push any remaining operands
 | 
						|
                                    if(tokenIndex + 1 < int(tokens.size())) handleDefineWord(tokens, tokenIndex, instruction, true, defineOffset, outputSize);
 | 
						|
                                    if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(outputSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                                }
 | 
						|
                                // Normal instructions
 | 
						|
                                else
 | 
						|
                                {
 | 
						|
                                    instruction._operand0 = uint8_t(LO_BYTE(operand));
 | 
						|
                                    instruction._operand1 = uint8_t(HI_BYTE(operand));
 | 
						|
                                    _instructions.push_back(instruction);
 | 
						|
                                    if(!checkInvalidAddress(ParseType(parse), _currentAddress, uint16_t(instruction._byteSize), instruction, lineToken, filename, _lineNumber)) return false;
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        break;
 | 
						|
 | 
						|
                        default: break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                _currentAddress += uint16_t(outputSize);
 | 
						|
            }              
 | 
						|
        }
 | 
						|
 | 
						|
        // Pack byte code buffer from instruction buffer
 | 
						|
        packByteCodeBuffer();
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
        // Parse gprintf labels, equates and expressions
 | 
						|
        if(!parseGprintfs()) return false;
 | 
						|
#endif
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
}
 |