You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

686 lines
19 KiB

//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileUtilities.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
bool SkipBlock(int column)
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
if (!CompileBlock(column))
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool SkipRange()
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
bool bRange;
if (!CompileRange(bRange))
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool SkipExpression()
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
if (!CompileExpression())
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool CheckIndex(bool& bIndex, int& expSourcePtr)
{
bIndex = false;
if (g_pElementizer->CheckElement(type_leftb))
{
expSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_rightb))
{
return false;
}
bIndex = true;
}
return true;
}
bool CheckIndexRange(bool& bIndex, int& expSourcePtr)
{
bIndex = false;
if (g_pElementizer->CheckElement(type_leftb))
{
expSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (g_pElementizer->CheckElement(type_dotdot))
{
if (!SkipExpression())
{
return false;
}
}
if (!g_pElementizer->GetElement(type_rightb))
{
return false;
}
bIndex = true;
}
return true;
}
bool CheckVariable_AddressExpression(int& expSourcePtr)
{
bool bIndex = false;
if (!CheckIndex(bIndex, expSourcePtr))
{
return false;
}
if (!bIndex)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eleftb];
return false;
}
return true;
}
bool CheckVariable(bool& bVariable, unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr)
{
address = g_pElementizer->GetValue();
indexSourcePtr = 0;
unsigned char varType = (unsigned char)(g_pElementizer->GetType() & 0xFF);
if (varType >= type_var_byte && varType <= type_var_long)
{
type = type_var_byte;
// adjust address base on the var size
if (varType < type_var_long)
{
address += g_pCompilerData->var_long;
}
if (varType == type_var_byte)
{
address += g_pCompilerData->var_word;
}
}
else if (varType >= type_dat_byte && varType <= type_dat_long)
{
type = type_dat_byte;
}
else if (varType >= type_loc_byte && varType <= type_loc_long)
{
type = type_loc_byte;
}
else
{
type = varType;
if (varType == type_size)
{
size = (unsigned char)(g_pElementizer->GetValue() & 0xFF);
if (!CheckVariable_AddressExpression(address))
{
return false;
}
bool bIndex = false;
if (!CheckIndex(bIndex, indexSourcePtr))
{
return false;
}
bVariable = true;
return true;
}
else
{
size = 2;
if (varType == type_spr)
{
if (!CheckVariable_AddressExpression(address))
{
return false;
}
bVariable = true;
return true;
}
else if (varType == type_reg)
{
bool bIndex = false;
if (!CheckIndexRange(bIndex, indexSourcePtr))
{
return false;
}
if (bIndex)
{
size = 3;
}
bVariable = true;
return true;
}
else
{
bVariable = false;
return true;
}
}
}
// if we got here then it's a var/dat/loc type
// set size
size = varType;
size -= type;
bool bIndex = false;
if (!CheckIndex(bIndex, indexSourcePtr))
{
return false;
}
if (!bIndex)
{
// check for .byte/word/long{[index]}
if (g_pElementizer->CheckElement(type_dot))
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof)) // get byte/word/long
{
return false;
}
if (g_pElementizer->GetType() != type_size)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_ebwol];
return false;
}
if (size < (g_pElementizer->GetValue() & 0xFF)) // new size must be same or smaller
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_sombs];
return false;
}
size = (g_pElementizer->GetValue() & 0xFF); // update size
bool bIndexCheck = false;
if (!CheckIndex(bIndexCheck, indexSourcePtr))
{
return false;
}
}
}
bVariable = true;
return true;
}
bool GetVariable(unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr)
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
bool bVariable = false;
if (!CheckVariable(bVariable, type, size, address, indexSourcePtr))
{
return false;
}
if (!bVariable)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eav];
return false;
}
return true;
}
bool CompileVariable(unsigned char vOperation, unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
// compile and index(s)
if (type != type_reg)
{
if (type == type_spr || type == type_size)
{
if (!CompileOutOfSequenceExpression(address))
{
return false;
}
}
if (type != type_spr)
{
if (indexSourcePtr != 0)
{
if (!CompileOutOfSequenceExpression(indexSourcePtr))
{
return false;
}
}
}
}
unsigned char byteCode = 0;
if (type == type_spr)
{
byteCode = 0x24 | vOperation;
}
else if (type == type_reg)
{
byteCode = 0x3F;
if (size != 2)
{
bool bRange = false;
if (!CompileOutOfSequenceRange(indexSourcePtr, bRange))
{
return false;
}
if (bRange)
{
byteCode = 0x3E;
}
else
{
byteCode = 0x3D;
}
}
if (!EnterObj(byteCode))
{
return false;
}
// byteCode = 1 in high bit, bottom 2 bits of vOperation in next two bits, then bottom 5 bits of address
byteCode = 0x80 | ((vOperation & 3) << 5) | (address & 0x1F);
}
else
{
if ((type != type_var_byte && type != type_loc_byte) || size != 2 || address >= 8*4 || indexSourcePtr != 0)
{
// not compact
byteCode = 0x80 | (size << 5);
if (indexSourcePtr != 0)
{
byteCode |= 0x10;
}
byteCode |= vOperation;
if (type != type_size)
{
if (type == type_dat_byte)
{
byteCode += 4;
}
else if (type == type_var_byte)
{
byteCode += 8;
}
else if (type == type_loc_byte)
{
byteCode += 12;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_internal];
return false;
}
if (!EnterObj(byteCode))
{
return false;
}
if (address > 0x7F)
{
// two byte address
byteCode = (unsigned char)(address >> 8) | 0x80;
if (!EnterObj(byteCode))
{
return false;
}
}
byteCode = (unsigned char)address;
}
}
else
{
// compact
byteCode = (type == type_var_byte) ? 0x40 : 0x60;
byteCode |= (unsigned char)address;
byteCode |= vOperation;
}
}
if (!EnterObj(byteCode))
{
return false;
}
if (vOperation == 2) // if assign
{
if (!EnterObj(vOperator))
{
return false;
}
}
return true;
}
bool CompileVariable_Assign(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
return CompileVariable(2, vOperator, type, size, address, indexSourcePtr);
}
bool CompileVariable_Expression(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
if (!CompileExpression())
{
return false;
}
return CompileVariable(2, vOperator, type, size, address, indexSourcePtr);
}
bool CompileVariable_PreSignExtendOrRandom(unsigned char vOperator)
{
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
return CompileVariable_Assign(vOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
bool CompileVariable_IncOrDec(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
return CompileVariable(2, vOperator | (((size + 1) & 3) << 1), type, size, address, indexSourcePtr);
}
bool CompileVariable_PreIncOrDec(unsigned char vOperator)
{
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
return CompileVariable_IncOrDec(vOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
bool CompileParameters(int numParameters)
{
if (numParameters > 0)
{
if (!g_pElementizer->GetElement(type_left)) // (
{
return false;
}
for (int i = 0; i < numParameters; i++)
{
if (!CompileExpression())
{
return false;
}
if (i < (numParameters - 1))
{
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
}
}
if (!g_pElementizer->GetElement(type_right)) // )
{
return false;
}
}
return true;
}
bool CompileConstant(int value)
{
if (value >= -1 && value <= 1)
{
// constant is -1, 0, or 1, so compiles to a single bytecode
unsigned char byteCode = (unsigned char)(value+1) | 0x34;
if (!EnterObj(byteCode))
{
return false;
}
return true;
}
// see if it's a mask
// masks can be: only one bit on (e.g. 0x00008000),
// all bits on except one (e.g. 0xFFFF7FFF),
// all bits on up to a bit then all zeros (e.g. 0x0000FFFF),
// or all bits off up to a bit then all ones (e.g. 0xFFFF0000)
for (unsigned char i = 0; i < 128; i++)
{
int testVal = 2;
testVal <<= (i & 0x1F); // mask i, so that we only actually shift 0 to 31
if (i & 0x20) // i in range 32 to 63 or 96 to 127
{
testVal--;
}
if (i& 0x40) // i in range 64 to 127
{
testVal = ~testVal;
}
if (testVal == value)
{
if (!EnterObj(0x37)) // (constant mask)
{
return false;
}
if (!EnterObj(i))
{
return false;
}
return true;
}
}
// handle constants with upper 2 or 3 bytes being 0xFFs, using 'not'
if ((value & 0xFFFFFF00) == 0xFFFFFF00)
{
// one byte constant using 'not'
if (!EnterObj(0x38))
{
return false;
}
unsigned char byteCode = (unsigned char)(value & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
if (!EnterObj(0xE7)) // (bitwise bot)
{
return false;
}
return true;
}
else if ((value & 0xFFFF0000) == 0xFFFF0000)
{
// two byte constant using 'not'
if (!EnterObj(0x39))
{
return false;
}
unsigned char byteCode = (unsigned char)((value >> 8) & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
byteCode = (unsigned char)(value & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
if (!EnterObj(0xE7)) // (bitwise bot)
{
return false;
}
return true;
}
// 1 to 4 byte constant
unsigned char size = 1;
if (value & 0xFF000000)
{
size = 4;
}
else if (value & 0x00FF0000)
{
size = 3;
}
else if (value & 0x0000FF00)
{
size = 2;
}
unsigned char byteCode = 0x37 + size; // (constant 1..4 bytes)
if (!EnterObj(byteCode))
{
return false;
}
for (unsigned char i = size; i > 0; i--)
{
byteCode = (unsigned char)((value >> ((i - 1) * 8)) & 0xFF);
if (!EnterObj(byteCode))
{
return false;
}
}
return true;
}
bool CompileOutOfSequenceExpression(int sourcePtr)
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pElementizer->SetSourcePtr(sourcePtr);
if (!CompileExpression())
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
return true;
}
bool CompileOutOfSequenceRange(int sourcePtr, bool& bRange)
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pElementizer->SetSourcePtr(sourcePtr);
if (!CompileRange(bRange))
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
return true;
}
// compiles either a value or a range and sets the bRange flag accordingly
bool CompileRange(bool& bRange)
{
if (!CompileExpression())
{
return false;
}
if (g_pElementizer->CheckElement(type_dotdot))
{
if (!CompileExpression())
{
return false;
}
bRange = true;
}
else
{
bRange = false;
}
return true;
}
// Compile relative address
bool CompileAddress(int address)
{
address -= g_pCompilerData->obj_ptr; // make relative address
address--; // compensate for single-byte
if ((address < 0 && abs(address) <= 64) || (address >= 0 && address < 64))
{
// single byte, enter
address &= 0x007F;
}
else
{
// double byte, compensate and enter
address--;
if (!EnterObj((unsigned char)((address >> 8) | 0x80)))
{
return false;
}
address &= 0x00FF;
}
return EnterObj((unsigned char)address);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////