mirror of https://github.com/wwarthen/RomWBW.git
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.
2129 lines
69 KiB
2129 lines
69 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. //
|
|
// //
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// PropellerCompiler.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 "UnusedMethodUtils.h"
|
|
|
|
//////////////////////////////////////////
|
|
// declarations for internal functions
|
|
// some of these are defined in other files (where noted)
|
|
//
|
|
|
|
bool CompileDevBlocks();
|
|
bool CompileConBlocks(int pass);
|
|
bool CompileSubBlocksId();
|
|
bool CompileObjBlocksId();
|
|
bool CompileDatBlocksFileNames();
|
|
|
|
bool CompileObjSymbols();
|
|
bool CompileVarBlocks();
|
|
extern bool CompileDatBlocks(); // in CompileDatBlocks.cpp
|
|
bool CompileSubBlocks();
|
|
bool CompileObjBlocks();
|
|
bool DistillObjBlocks();
|
|
bool CompileFinal();
|
|
bool PointToFirstCon();
|
|
bool DetermineStack();
|
|
bool DetermineClock();
|
|
bool DetermineDebug();
|
|
bool CompileDoc();
|
|
|
|
extern bool DistillObjects(); // in DistillObjects.cpp
|
|
extern bool CompileTopBlock(); // in InstructionBlockCompiler.cpp
|
|
|
|
// globals used by the compiler
|
|
CompilerDataInternal* g_pCompilerData = 0;
|
|
SymbolEngine* g_pSymbolEngine = 0;
|
|
Elementizer* g_pElementizer = 0;
|
|
|
|
//////////////////////////////////////////
|
|
// exported functions
|
|
//
|
|
|
|
// Call this before using Compile1() & Compile2()
|
|
// the CompilerData pointer it returns is what Compile1() and Compile2() use/fill.
|
|
CompilerData* InitStruct()
|
|
{
|
|
g_pCompilerData = new CompilerDataInternal;
|
|
// wipe the compiler data struct with 0's
|
|
memset(g_pCompilerData, 0, sizeof(CompilerDataInternal));
|
|
|
|
g_pSymbolEngine = new SymbolEngine;
|
|
g_pElementizer = new Elementizer(g_pCompilerData, g_pSymbolEngine);
|
|
|
|
return g_pCompilerData;
|
|
}
|
|
|
|
void Cleanup()
|
|
{
|
|
delete g_pElementizer;
|
|
g_pElementizer = 0;
|
|
delete g_pSymbolEngine;
|
|
g_pSymbolEngine = 0;
|
|
delete g_pCompilerData;
|
|
g_pCompilerData = 0;
|
|
}
|
|
|
|
// Usage:
|
|
//
|
|
// Call Compile1
|
|
// Load any obj files
|
|
// Call Compile2
|
|
// Save new obj file
|
|
//
|
|
// OBJ structure:
|
|
//
|
|
// word varsize, pgmsize ;variable and program sizes
|
|
//
|
|
// 0: word objsize ;object size (w/o sub-objects)
|
|
// byte objindex>>2, objcount ;sub-object start index and count
|
|
// 4: word PUBn offset, PUBn locals ;index to PUBs (multiple)
|
|
// word PRIn offset, PRIn locals ;index to PRIs (multiple)
|
|
// objindex: word OBJn offset, OBJn var offset ;index to OBJs (multiple)
|
|
// byte DAT data... ;DAT data
|
|
// byte PUB data... ;PUB data
|
|
// byte PRI data... ;PRI data
|
|
// objsize:
|
|
// long OBJ data... ;OBJ data (sub-objects)
|
|
// pgmsize:
|
|
// byte checksum ;checksum reveals language_version
|
|
// byte 'PUBn', parameters ;PUB names and parameters (0..15)
|
|
// byte 'CONn', 16, values ;CON names and values
|
|
//
|
|
|
|
const char* Compile1()
|
|
{
|
|
g_pElementizer->Reset();
|
|
g_pSymbolEngine->Reset();
|
|
g_pCompilerData->pubcon_list_size = 0;
|
|
g_pCompilerData->list_length = 0;
|
|
g_pCompilerData->doc_length = 0;
|
|
g_pCompilerData->doc_mode = false;
|
|
g_pCompilerData->info_count = 0;
|
|
|
|
// reset obj pointer based on compile_mode
|
|
if (g_pCompilerData->compile_mode == 0)
|
|
{
|
|
g_pCompilerData->obj_ptr = 4;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->obj_ptr = 0;
|
|
}
|
|
|
|
SetPrint(g_pCompilerData->list, g_pCompilerData->list_limit);
|
|
|
|
if (!CompileDevBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileConBlocks(0))
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileSubBlocksId())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileObjBlocksId())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileDatBlocksFileNames())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
g_pCompilerData->source_start = 0;
|
|
g_pCompilerData->source_finish = 0;
|
|
return 0;
|
|
}
|
|
|
|
const char* Compile2()
|
|
{
|
|
if (!CompileObjSymbols())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileConBlocks(1))
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileVarBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!CompileDatBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
if (!g_pCompilerData->bDATonly)
|
|
{
|
|
if (!CompileSubBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
}
|
|
|
|
if (!CompileObjBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
if (!g_pCompilerData->bDATonly)
|
|
{
|
|
if (!DistillObjBlocks())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
}
|
|
|
|
if (!CompileFinal())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
if (!g_pCompilerData->bDATonly)
|
|
{
|
|
if (!PointToFirstCon())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!DetermineStack())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!DetermineClock())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
if (!DetermineDebug())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
if (!PrintObj())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
|
|
g_pCompilerData->list_length = g_pCompilerData->print_length;
|
|
|
|
if (g_pCompilerData->doc_limit > 0)
|
|
{
|
|
SetPrint(g_pCompilerData->doc, g_pCompilerData->doc_limit);
|
|
|
|
if (!CompileDoc())
|
|
{
|
|
return g_pCompilerData->error_msg;
|
|
}
|
|
g_pCompilerData->doc_length = g_pCompilerData->print_length;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->doc_length = 0;
|
|
}
|
|
}
|
|
|
|
g_pCompilerData->source_start = 0;
|
|
g_pCompilerData->source_finish = 0;
|
|
return 0;
|
|
}
|
|
|
|
bool GetErrorInfo(int& lineNumber, int& column, int& offsetToStartOfLine, int& offsetToEndOfLine, int& offendingItemStart, int& offendingItemEnd)
|
|
{
|
|
if (g_pCompilerData && g_pCompilerData->error)
|
|
{
|
|
lineNumber = g_pElementizer->GetCurrentLineNumber(offsetToStartOfLine, offsetToEndOfLine);
|
|
column = g_pElementizer->GetColumn();
|
|
offendingItemStart = g_pCompilerData->source_start;
|
|
offendingItemEnd = g_pCompilerData->source_finish;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
// internal function definitions
|
|
//
|
|
|
|
bool CompileDevBlocks()
|
|
{
|
|
g_pCompilerData->pre_files = 0;
|
|
g_pCompilerData->arc_files = 0;
|
|
int index = 0;
|
|
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(block_dev, bEof))
|
|
{
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end)
|
|
{
|
|
continue;
|
|
}
|
|
if (g_pElementizer->GetType() == type_precompile)
|
|
{
|
|
if (!AddFileName(g_pCompilerData->pre_files,
|
|
index,
|
|
g_pCompilerData->pre_filenames,
|
|
g_pCompilerData->pre_name_start,
|
|
g_pCompilerData->pre_name_finish,
|
|
error_loxupfe))
|
|
{
|
|
return false;
|
|
}
|
|
if (!g_pElementizer->GetElement(type_end))
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
else if (g_pElementizer->GetType() == type_archive)
|
|
{
|
|
if (!AddFileName(g_pCompilerData->arc_files,
|
|
index,
|
|
g_pCompilerData->arc_filenames,
|
|
g_pCompilerData->arc_name_start,
|
|
g_pCompilerData->arc_name_finish,
|
|
error_loxuafe))
|
|
{
|
|
return false;
|
|
}
|
|
if (!g_pElementizer->GetElement(type_end))
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
else if (g_pElementizer->GetType() != type_block)
|
|
{
|
|
// we got an element that wasn't a precompile or archive or the next block
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_epoa];
|
|
return false;
|
|
}
|
|
|
|
// if we get here, then the element we got was of type_block
|
|
|
|
// finished with this block, backup off the next block
|
|
g_pElementizer->Backup();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileConBlocks(int pass)
|
|
{
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
g_pCompilerData->enum_valid = 1;
|
|
g_pCompilerData->enum_value = 0;
|
|
|
|
bool bFindNextConBlock = false;
|
|
while (!bEof && !bFindNextConBlock)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
while(!bEof)
|
|
{
|
|
g_pCompilerData->assign_flag = 1;
|
|
|
|
if ((g_pElementizer->GetType() == type_con) ||
|
|
(g_pElementizer->GetType() == type_con_float))
|
|
{
|
|
// constant
|
|
if (pass == 0)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaucnop];
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->assign_flag = 0;
|
|
g_pCompilerData->assign_type = g_pElementizer->GetType();
|
|
g_pCompilerData->assign_value = g_pElementizer->GetValue();
|
|
}
|
|
|
|
if (!HandleConSymbol(pass))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
if (!HandleConSymbol(pass))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (g_pElementizer->GetType() == type_pound)
|
|
{
|
|
// pound
|
|
if (!GetTryValue(pass == 1 ? true : false, true))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pCompilerData->bUndefined == false)
|
|
{
|
|
g_pCompilerData->enum_valid = 1;
|
|
g_pCompilerData->enum_value = GetResult();
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->enum_valid = 0;
|
|
}
|
|
}
|
|
else if (g_pElementizer->GetType() == type_block)
|
|
{
|
|
// hit next block, so backup and search for next con block
|
|
g_pElementizer->Backup();
|
|
bFindNextConBlock = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// we got an element that isn't valid in a con block
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaucnop];
|
|
return false;
|
|
}
|
|
|
|
bool bComma = false;
|
|
if (!GetCommaOrEnd(bComma))
|
|
{
|
|
return false;
|
|
}
|
|
if (bComma == false)
|
|
{
|
|
break;
|
|
}
|
|
if(!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!bEof)
|
|
{
|
|
if(!g_pElementizer->GetNextBlock(block_con, bEof))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CompileSubBlocksId_Compile(int blockType, bool &bFirst, int &nMethodIndex)
|
|
{
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(blockType, bEof))
|
|
{
|
|
if (!bEof)
|
|
{
|
|
char params = 0;
|
|
int locals = 0;
|
|
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end || g_pElementizer->GetType() != type_undefined)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eausn];
|
|
return false;
|
|
}
|
|
|
|
// save a copy of the symbol
|
|
g_pElementizer->BackupSymbol();
|
|
|
|
if (g_pCompilerData->obj_ptr < 256*4)
|
|
{
|
|
params = 0;
|
|
|
|
// are there parameters?
|
|
if (g_pElementizer->CheckElement(type_left))
|
|
{
|
|
// if so, then count them
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
if (params < 15)
|
|
{
|
|
params++;
|
|
bool bComma = false;
|
|
if (!GetCommaOrRight(bComma))
|
|
{
|
|
// error was set inside GetCommaOrRight()
|
|
return false;
|
|
}
|
|
if (!bComma)
|
|
{
|
|
// we got the ')' so fall out of counting parameters
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// too many parameters
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_loxpe];
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a parameter used an already defined symbol name
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaupn];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// is there a result defined
|
|
if (g_pElementizer->CheckElement(type_colon))
|
|
{
|
|
// yes, so read the name
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() != type_undefined &&
|
|
g_pElementizer->GetType() != type_loc_long) // this allows for 'RESULT' (ignores it)
|
|
{
|
|
// result name was not unique
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaurn];
|
|
return false;
|
|
}
|
|
}
|
|
// check for locals
|
|
locals = 0;
|
|
bool bPipe = false;
|
|
if(!GetPipeOrEnd(bPipe))
|
|
{
|
|
// error was set inside GetPipeOrEnd()
|
|
return false;
|
|
}
|
|
if (bPipe)
|
|
{
|
|
// count locals (handling arrays)
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
// is it an array?
|
|
if (g_pElementizer->CheckElement(type_leftb))
|
|
{
|
|
// it is, so read the index
|
|
if (!GetTryValue(true, true))
|
|
{
|
|
return false;
|
|
}
|
|
int value = GetResult();
|
|
value <<= 2;
|
|
if (value > loc_limit)
|
|
{
|
|
// too many locals
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_loxlve];
|
|
return false;
|
|
}
|
|
locals += value;
|
|
if (locals <= loc_limit)
|
|
{
|
|
if (!g_pElementizer->GetElement(type_rightb))
|
|
{
|
|
// error was set inside GetElement()
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
locals += 4;
|
|
}
|
|
if (locals > loc_limit)
|
|
{
|
|
// too many locals
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_loxlve];
|
|
return false;
|
|
}
|
|
bool bComma = false;
|
|
if (!GetCommaOrEnd(bComma))
|
|
{
|
|
// error was set inside GetCommaOrEnd()
|
|
return false;
|
|
}
|
|
if (!bComma)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a local used an already defined symbol name
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eauvn];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!g_pCompilerData->bFinalCompile || IsMethodUsed(g_pCompilerData->current_filename, nMethodIndex))
|
|
{
|
|
// enter sub symbol
|
|
int value = params;
|
|
value <<= 8;
|
|
value |= (g_pCompilerData->obj_ptr >> 2) & 0xFF;
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_sub, value, blockType);
|
|
#ifdef RPE_DEBUG
|
|
printf("Pub/Pri %s %d (%d, %d)\n", g_pCompilerData->symbolBackup, value, params, g_pCompilerData->obj_ptr);
|
|
#endif
|
|
if (!g_pCompilerData->bDATonly)
|
|
{
|
|
// enter locals count into index (shifted up 16 to leave space for the sub offset which will be fixed up later)
|
|
EnterObjLong(locals<<16);
|
|
}
|
|
|
|
if (blockType == block_pub)
|
|
{
|
|
if (!AddSymbolToPubConList())
|
|
{
|
|
return false;
|
|
}
|
|
if (!AddPubConListByte(params))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (bFirst == false)
|
|
{
|
|
g_pCompilerData->first_pub_parameters = params;
|
|
bFirst = true;
|
|
}
|
|
}
|
|
nMethodIndex++;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_loxspoe];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileSubBlocksId()
|
|
{
|
|
bool bFirst = false;
|
|
int nMethodIndex = 0;
|
|
if (!CompileSubBlocksId_Compile(block_pub, bFirst, nMethodIndex))
|
|
{
|
|
return false;
|
|
}
|
|
if (bFirst == false && g_pCompilerData->compile_mode == 0)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_nprf];
|
|
g_pCompilerData->source_start = g_pCompilerData->source_finish;
|
|
return false;
|
|
}
|
|
if (!CompileSubBlocksId_Compile(block_pri, bFirst, nMethodIndex))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileObjBlocksId()
|
|
{
|
|
g_pCompilerData->obj_start = g_pCompilerData->obj_ptr;
|
|
g_pCompilerData->obj_count = 0;
|
|
g_pCompilerData->obj_files = 0;
|
|
g_pCompilerData->unused_obj_files = 0;
|
|
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(block_obj, bEof))
|
|
{
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end)
|
|
{
|
|
continue;
|
|
}
|
|
else if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
// save a copy of the symbol
|
|
g_pElementizer->BackupSymbol();
|
|
|
|
int instanceCount = 1;
|
|
|
|
// see if there is a count
|
|
if (g_pElementizer->CheckElement(type_leftb))
|
|
{
|
|
// get the count value and validate it
|
|
if (!GetTryValue(true, true))
|
|
{
|
|
return false;
|
|
}
|
|
instanceCount = GetResult();
|
|
if (instanceCount < 1 || instanceCount > 255)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_ocmbf1tx];
|
|
return false;
|
|
}
|
|
// get the closing bracket
|
|
if (!g_pElementizer->GetElement(type_rightb))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// must have the colon
|
|
if (!g_pElementizer->GetElement(type_colon))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int objFileIndex = 0;
|
|
// now get the filename
|
|
if (!AddFileName(g_pCompilerData->obj_files,
|
|
objFileIndex,
|
|
g_pCompilerData->obj_filenames,
|
|
g_pCompilerData->obj_name_start,
|
|
g_pCompilerData->obj_name_finish,
|
|
error_loxuoe))
|
|
{
|
|
return false;
|
|
}
|
|
if (!g_pCompilerData->bFinalCompile || IsObjectUsed(&g_pCompilerData->obj_filenames[objFileIndex<<8]))
|
|
{
|
|
// is it a new obj?
|
|
if (objFileIndex <= (g_pCompilerData->obj_files - 1))
|
|
{
|
|
// reset instances
|
|
g_pCompilerData->obj_instances[objFileIndex] = 0;
|
|
}
|
|
|
|
// enter obj symbol
|
|
int value = objFileIndex;
|
|
value <<= 8;
|
|
value |= (g_pCompilerData->obj_ptr >> 2) & 0xFF;
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_obj, value);
|
|
#ifdef RPE_DEBUG
|
|
printf("Obj %s %d (%d, %d)\n", g_pCompilerData->symbolBackup, value, instanceCount, g_pCompilerData->obj_ptr);
|
|
#endif
|
|
|
|
for (int i=0; i < instanceCount; i++)
|
|
{
|
|
if (g_pCompilerData->obj_ptr < 256*4)
|
|
{
|
|
// enter object index into table
|
|
EnterObjLong(objFileIndex);
|
|
g_pCompilerData->obj_count++;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_loxspoe];
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// accumulate instances
|
|
g_pCompilerData->obj_instances[objFileIndex] += instanceCount;
|
|
}
|
|
else
|
|
{
|
|
strcpy(&(g_pCompilerData->obj_unused[g_pCompilerData->unused_obj_files<<8]), &(g_pCompilerData->obj_filenames[objFileIndex<<8]));
|
|
int value = g_pCompilerData->unused_obj_files | 0x40;
|
|
value <<= 8;
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_obj, value);
|
|
g_pCompilerData->unused_obj_files++;
|
|
g_pCompilerData->obj_files--;
|
|
}
|
|
|
|
if (!g_pElementizer->GetElement(type_end))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (g_pElementizer->GetType() == type_block)
|
|
{
|
|
g_pElementizer->Backup();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eauon];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CompileDatBlocksFileNames()
|
|
{
|
|
g_pCompilerData->dat_files = 0;
|
|
int index = 0;
|
|
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(block_dat, bEof))
|
|
{
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end)
|
|
{
|
|
continue;
|
|
}
|
|
if (g_pElementizer->GetType() == type_file)
|
|
{
|
|
if (!AddFileName(g_pCompilerData->dat_files,
|
|
index,
|
|
g_pCompilerData->dat_filenames,
|
|
g_pCompilerData->dat_name_start,
|
|
g_pCompilerData->dat_name_finish,
|
|
error_loxudfe))
|
|
{
|
|
return false;
|
|
}
|
|
if (!g_pElementizer->GetElement(type_end))
|
|
{
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
else if (g_pElementizer->GetType() != type_block)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// if we get here, then the element we got was of type_block
|
|
|
|
// finished with this block, backup off the next block
|
|
g_pElementizer->Backup();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CompileObjSymbol_BadObj(int nFile)
|
|
{
|
|
g_pCompilerData->print_length = 0;
|
|
PrintString("Invalid object file ");
|
|
char* pFilename = &(g_pCompilerData->obj_filenames[nFile]);
|
|
PrintString(pFilename);
|
|
PrintString(".OBJ");
|
|
PrintChr(0);
|
|
g_pCompilerData->error_msg = g_pCompilerData->list;
|
|
}
|
|
|
|
bool CompileObjSymbols()
|
|
{
|
|
int nFile;
|
|
for (nFile = 0; nFile < g_pCompilerData->obj_files; nFile++)
|
|
{
|
|
unsigned char* pData = &(g_pCompilerData->obj_data[g_pCompilerData->obj_offsets[nFile]]);
|
|
|
|
// do checksum of obj
|
|
unsigned char uChecksum = 0;
|
|
for (int i = 0; i < g_pCompilerData->obj_lengths[nFile]; i++)
|
|
{
|
|
uChecksum += pData[i];
|
|
}
|
|
|
|
unsigned char* pDataEnd = pData + g_pCompilerData->obj_lengths[nFile];
|
|
|
|
short vsize = pData[0] | ((short)pData[1] << 8);// *((short*)(&pData[0]));
|
|
short psize = pData[2] | ((short)pData[3] << 8);// *((short*)(&pData[2]));
|
|
pData += 4; // move past vsize/psize
|
|
|
|
// validate checksum and that vsize/psize are valid long addresses
|
|
if ((!g_pCompilerData->bDATonly && uChecksum != language_version) || vsize & 0x03 || psize & 0x03)
|
|
{
|
|
CompileObjSymbol_BadObj(nFile);
|
|
return false;
|
|
}
|
|
|
|
// skip obj bytes and checksum
|
|
pData += psize;
|
|
pData++;
|
|
|
|
// go thru symbols validating them and adding them to the symbol table
|
|
int nPub = 1;
|
|
while (pData < pDataEnd)
|
|
{
|
|
for (int i = 0; i < symbol_limit+1; i++)
|
|
{
|
|
if (!CheckWordChar((char)(*pData)))
|
|
{
|
|
CompileObjSymbol_BadObj(nFile);
|
|
return false;
|
|
}
|
|
g_pCompilerData->symbolBackup[i] = (char)(*pData);
|
|
pData++;
|
|
if (pData[0] < 18) // 0 to 15 = pub param count, 16 and 17 are constants
|
|
{
|
|
g_pCompilerData->symbolBackup[i+1] = (char)(nFile+1);
|
|
g_pCompilerData->symbolBackup[i+2] = 0;
|
|
if (pData[0] < 16) // handle objpub symbol
|
|
{
|
|
int value = nPub | ((int)pData[0] << 8);
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_objpub, value);
|
|
#ifdef RPE_DEBUG
|
|
printf("objpub: %s %d \n", g_pCompilerData->symbolBackup, value);
|
|
#endif
|
|
nPub++;
|
|
pData++; // adjust pointer to after param count
|
|
break;
|
|
}
|
|
else // handle objcon or objcon_float symbol
|
|
{
|
|
int value = (int)pData[1] | ((int)pData[2] << 8) | ((int)pData[3] << 16) | ((int)pData[4] << 24);// *((int*)(&pData[1]));
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, (pData[0] == 16) ? type_objcon : type_objcon_float, value);
|
|
#ifdef RPE_DEBUG
|
|
float fValue = *((float*)(&value));
|
|
printf("objcon: %s %d %f \n", g_pCompilerData->symbolBackup, value, fValue);
|
|
#endif
|
|
pData+=5; // adjust pointer to after value
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pData > pDataEnd)
|
|
{
|
|
CompileObjSymbol_BadObj(nFile);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now add any CON symbols from unused objects
|
|
for (int nUnusedFile = 0; nUnusedFile < g_pCompilerData->unused_obj_files; nUnusedFile++)
|
|
{
|
|
unsigned char* pData = 0;
|
|
int nDataSize = 0;
|
|
if (GetObjectPubConList(&(g_pCompilerData->obj_unused[nUnusedFile<<8]), &pData, &nDataSize))
|
|
{
|
|
unsigned char *pDataEnd = pData + nDataSize;
|
|
// go thru symbols validating them and adding them to the symbol table
|
|
while (pData < pDataEnd)
|
|
{
|
|
for (int i = 0; i < symbol_limit+1; i++)
|
|
{
|
|
if (!CheckWordChar((char)(*pData)))
|
|
{
|
|
CompileObjSymbol_BadObj(nFile);
|
|
return false;
|
|
}
|
|
g_pCompilerData->symbolBackup[i] = (char)(*pData);
|
|
pData++;
|
|
if (pData[0] < 18) // 0 to 15 = pub param count, 16 and 17 are constants
|
|
{
|
|
g_pCompilerData->symbolBackup[i+1] = (char)(0x40 | (nUnusedFile + 1));
|
|
g_pCompilerData->symbolBackup[i+2] = 0;
|
|
if (pData[0] < 16) // handle objpub symbol
|
|
{
|
|
// we don't add pubs in this case
|
|
pData++; // adjust pointer to after param count
|
|
break;
|
|
}
|
|
else // handle objcon or objcon_float symbol
|
|
{
|
|
int value = (int)pData[1] | ((int)pData[2] << 8) | ((int)pData[3] << 16) | ((int)pData[4] << 24);
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, (pData[0] == 16) ? type_objcon : type_objcon_float, value);
|
|
#ifdef RPE_DEBUG
|
|
float fValue = *((float*)(&value));
|
|
printf("objcon: %s %d %f *\n", g_pCompilerData->symbolBackup, value, fValue);
|
|
#endif
|
|
pData+=5; // adjust pointer to after value
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CompileVarBlocks()
|
|
{
|
|
g_pCompilerData->var_byte = 0;
|
|
g_pCompilerData->var_word = 0;
|
|
g_pCompilerData->var_long = 0;
|
|
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(block_var, bEof))
|
|
{
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_end)
|
|
{
|
|
continue;
|
|
}
|
|
if (g_pElementizer->GetType() == type_size)
|
|
{
|
|
int nSize = g_pElementizer->GetValue();
|
|
|
|
while(!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() != type_undefined)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eauvn];
|
|
return false;
|
|
}
|
|
|
|
// save a copy of the symbol
|
|
g_pElementizer->BackupSymbol();
|
|
|
|
int nCount = 1;
|
|
|
|
// see if there is a count
|
|
if (g_pElementizer->CheckElement(type_leftb))
|
|
{
|
|
// get the count value and validate it
|
|
if (!GetTryValue(true, true))
|
|
{
|
|
return false;
|
|
}
|
|
nCount = GetResult();
|
|
if (nCount > var_limit)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_tmvsid];
|
|
return false;
|
|
}
|
|
// get the closing bracket
|
|
if (!g_pElementizer->GetElement(type_rightb))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int nValue = 0;
|
|
switch(nSize)
|
|
{
|
|
case 0:
|
|
nValue = g_pCompilerData->var_byte;
|
|
g_pCompilerData->var_byte += nCount;
|
|
break;
|
|
case 1:
|
|
nValue = g_pCompilerData->var_word;
|
|
g_pCompilerData->var_word += nCount<<1;
|
|
break;
|
|
case 2:
|
|
nValue = g_pCompilerData->var_long;
|
|
g_pCompilerData->var_long += nCount<<2;
|
|
break;
|
|
}
|
|
if ((nValue + (nCount << nSize)) > var_limit)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_tmvsid];
|
|
return false;
|
|
}
|
|
|
|
// add the symbol
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, (nSize == 0) ? type_var_byte : ((nSize == 1) ? type_var_word : type_var_long), nValue);
|
|
#ifdef RPE_DEBUG
|
|
printf("var: %s %d (%d, %d)\n", g_pCompilerData->symbolBackup, nValue, nSize, nCount);
|
|
#endif
|
|
|
|
bool bComma = false;
|
|
if (!GetCommaOrEnd(bComma))
|
|
{
|
|
// error was set inside GetCommaOrEnd()
|
|
return false;
|
|
}
|
|
if (!bComma)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
else if (g_pElementizer->GetType() != type_block)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_ebwol];
|
|
return false;
|
|
}
|
|
|
|
// if we get here, then the element we got was of type_block
|
|
|
|
// finished with this block, backup off the next block
|
|
g_pElementizer->Backup();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileSubBlocks_Compile(int blockType, int &subCount, int &nMethodIndex)
|
|
{
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
while (!bEof)
|
|
{
|
|
if(g_pElementizer->GetNextBlock(blockType, bEof))
|
|
{
|
|
if (!bEof)
|
|
{
|
|
int saved_inf_start = g_pCompilerData->source_start;
|
|
int saved_inf_data0 = g_pCompilerData->obj_ptr;
|
|
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int saved_inf_data2 = g_pCompilerData->source_start;
|
|
int saved_inf_data3 = g_pCompilerData->source_finish;
|
|
|
|
// locals is tracking the number of bytes, so 4 per long
|
|
// we start at 4 because every sub has a result local
|
|
int locals = 4;
|
|
int paramCount = 0;
|
|
|
|
// are there parameters?
|
|
if (g_pElementizer->CheckElement(type_left))
|
|
{
|
|
// if so, then count them
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
g_pElementizer->BackupSymbol();
|
|
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_loc_long, locals, 0, true); // add to temp symbols
|
|
|
|
g_pCompilerData->inf_start = g_pCompilerData->source_start;
|
|
g_pCompilerData->inf_finish = g_pCompilerData->source_finish;
|
|
g_pCompilerData->inf_data0 = subCount;
|
|
g_pCompilerData->inf_data1 = paramCount;
|
|
g_pCompilerData->inf_data2 = saved_inf_data2;
|
|
g_pCompilerData->inf_data3 = saved_inf_data3;
|
|
g_pCompilerData->inf_data4 = 0;
|
|
if (blockType == block_pub)
|
|
{
|
|
g_pCompilerData->inf_type = info_pub_param;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->inf_type = info_pri_param;
|
|
}
|
|
EnterInfo();
|
|
|
|
paramCount++;
|
|
#ifdef RPE_DEBUG
|
|
printf("temp loc: %s %d\n", g_pCompilerData->symbolBackup, locals);
|
|
#endif
|
|
|
|
locals += 4;
|
|
|
|
bool bComma = false;
|
|
if (!GetCommaOrRight(bComma))
|
|
{
|
|
// error was set inside GetCommaOrRight()
|
|
return false;
|
|
}
|
|
if (!bComma)
|
|
{
|
|
// we got the ')' so fall out of counting parameters
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a parameter used an already defined symbol name
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaupn];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// is there a result defined
|
|
if (g_pElementizer->CheckElement(type_colon))
|
|
{
|
|
// yes, so read the name
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if ((g_pElementizer->GetType() != type_undefined && g_pElementizer->GetType() != type_loc_long) || // this allows for 'RESULT'
|
|
(g_pElementizer->GetType() == type_loc_long && g_pElementizer->GetValue() != 0)) // ''
|
|
{
|
|
// result name was not unique
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eaurn];
|
|
return false;
|
|
}
|
|
|
|
if (g_pElementizer->GetType() != type_loc_long)
|
|
{
|
|
// if we result symbol then add it to temp symbols
|
|
// we don't increment locals, because result local is already accounted for
|
|
g_pElementizer->BackupSymbol();
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_loc_long, 0, 0, true);
|
|
#ifdef RPE_DEBUG
|
|
printf("result: %s %d\n", g_pCompilerData->symbolBackup, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// check for locals
|
|
bool bPipe = false;
|
|
if(!GetPipeOrEnd(bPipe))
|
|
{
|
|
// error was set inside GetPipeOrEnd()
|
|
return false;
|
|
}
|
|
if (bPipe)
|
|
{
|
|
// count locals (handling arrays)
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() == type_undefined)
|
|
{
|
|
g_pElementizer->BackupSymbol();
|
|
|
|
int sizeOfThisLocal = 4;
|
|
|
|
// is it an array?
|
|
if (g_pElementizer->CheckElement(type_leftb))
|
|
{
|
|
// it is, so read the index (size of array)
|
|
if (!GetTryValue(true, true))
|
|
{
|
|
return false;
|
|
}
|
|
int value = GetResult();
|
|
sizeOfThisLocal = (value * 4);
|
|
|
|
// get passed ]
|
|
if (!g_pElementizer->GetElement(type_rightb))
|
|
{
|
|
// error was set inside GetElement()
|
|
return false;
|
|
}
|
|
}
|
|
|
|
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, type_loc_long, locals, 0, true); // add to temp symbols
|
|
#ifdef RPE_DEBUG
|
|
if (sizeOfThisLocal > 4)
|
|
{
|
|
printf("temp loc: %s[%d] %d\n", g_pCompilerData->symbolBackup, sizeOfThisLocal/4, locals);
|
|
}
|
|
else
|
|
{
|
|
printf("temp loc: %s %d\n", g_pCompilerData->symbolBackup, locals);
|
|
}
|
|
#endif
|
|
locals += sizeOfThisLocal;
|
|
|
|
bool bComma = false;
|
|
if (!GetCommaOrEnd(bComma))
|
|
{
|
|
// error was set inside GetCommaOrEnd()
|
|
return false;
|
|
}
|
|
if (!bComma)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a local used an already defined symbol name
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_eauvn];
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!g_pCompilerData->bFinalCompile || IsMethodUsed(g_pCompilerData->current_filename, nMethodIndex))
|
|
{
|
|
// enter sub offset into index
|
|
*((short*)&(g_pCompilerData->obj[4 + (subCount * 4)])) = (short)g_pCompilerData->obj_ptr;
|
|
|
|
if (!CompileTopBlock()) // instruction block compiler
|
|
{
|
|
return false;
|
|
}
|
|
|
|
g_pCompilerData->inf_start = saved_inf_start;
|
|
g_pCompilerData->inf_finish = g_pElementizer->GetSourcePtr();
|
|
g_pCompilerData->inf_data0 = saved_inf_data0;
|
|
g_pCompilerData->inf_data1 = g_pCompilerData->obj_ptr;
|
|
g_pCompilerData->inf_data2 = saved_inf_data2;
|
|
g_pCompilerData->inf_data3 = saved_inf_data3;
|
|
g_pCompilerData->inf_data4 = (paramCount << 16) | subCount;
|
|
if (blockType == block_pub)
|
|
{
|
|
g_pCompilerData->inf_type = info_pub;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->inf_type = info_pri;
|
|
}
|
|
EnterInfo();
|
|
|
|
subCount++;
|
|
}
|
|
else
|
|
{
|
|
// just simple tracking of unused methods, maximum tracked amount works out to 1024 entries
|
|
if (g_pCompilerData->unused_methods < (32 * file_limit))
|
|
{
|
|
char szMethodName[symbol_limit + 1];
|
|
int nLength = saved_inf_data3 - saved_inf_data2;
|
|
strncpy(szMethodName, &g_pCompilerData->source[saved_inf_data2], nLength);
|
|
szMethodName[nLength] = 0;
|
|
sprintf(&(g_pCompilerData->method_unused[symbol_limit * g_pCompilerData->unused_methods]), "%s.%s", g_pCompilerData->current_filename, szMethodName);
|
|
g_pCompilerData->unused_methods++;
|
|
}
|
|
}
|
|
nMethodIndex++;
|
|
g_pSymbolEngine->Reset(true); // cancel local symbols
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileSubBlocks()
|
|
{
|
|
int subCount = 0;
|
|
int nMethodIndex = 0;
|
|
if (!CompileSubBlocks_Compile(block_pub, subCount, nMethodIndex))
|
|
{
|
|
return false;
|
|
}
|
|
if (!CompileSubBlocks_Compile(block_pri, subCount, nMethodIndex))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileObjBlocks()
|
|
{
|
|
// calculate var_ptr and align to long
|
|
g_pCompilerData->var_ptr = g_pCompilerData->var_byte + g_pCompilerData->var_word + g_pCompilerData->var_long;
|
|
if ((g_pCompilerData->var_ptr & 0x00000003) != 0)
|
|
{
|
|
g_pCompilerData->var_ptr = (g_pCompilerData->var_ptr | 0x00000003) + 1;
|
|
}
|
|
if (g_pCompilerData->var_ptr > var_limit)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_tmvsid];
|
|
return false;
|
|
}
|
|
|
|
// align obj_ptr to long
|
|
if (!g_pCompilerData->bDATonly)
|
|
{
|
|
while ((g_pCompilerData->obj_ptr & 0x00000003) != 0)
|
|
{
|
|
if (!EnterObj(0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_pCompilerData->compile_mode == 0)
|
|
{
|
|
// set obj size word at offset 0
|
|
*((short*)(&g_pCompilerData->obj[0])) = (short)g_pCompilerData->obj_ptr;
|
|
// set obj index byte at offset 2
|
|
g_pCompilerData->obj[2] = (unsigned char)(g_pCompilerData->obj_start >> 2);
|
|
// set obj count byte at offset 3
|
|
g_pCompilerData->obj[3] = (unsigned char)(g_pCompilerData->obj_count);
|
|
}
|
|
|
|
// and any objects (OBJ sections)
|
|
int objptr[file_limit];
|
|
int objvar[file_limit];
|
|
|
|
for (int i = 0; i < g_pCompilerData->obj_files; i++)
|
|
{
|
|
objptr[i] = g_pCompilerData->obj_ptr;
|
|
|
|
unsigned char* pObj = &(g_pCompilerData->obj_data[g_pCompilerData->obj_offsets[i]]);
|
|
|
|
// get vsize and save in objvar[i]
|
|
//objvar[i] = (int)(*((unsigned short*)(pObj)));
|
|
objvar[i] = (int)pObj[0] | ((int)pObj[1] << 8);
|
|
pObj += 2;
|
|
|
|
// get psize
|
|
//unsigned short psize = *((unsigned short*)(pObj));
|
|
unsigned short psize = (unsigned short)pObj[0] | ((unsigned short)pObj[1] << 8);
|
|
pObj += 2;
|
|
|
|
for (unsigned short j = 0; j < psize; j++)
|
|
{
|
|
if (!EnterObj(pObj[j]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get start of object index
|
|
unsigned short* pIndex = (unsigned short*)&(g_pCompilerData->obj[g_pCompilerData->obj_start]);
|
|
|
|
for (int i = 0; i < g_pCompilerData->obj_count; i++)
|
|
{
|
|
// get file number from index
|
|
int index = *((int*)pIndex);
|
|
|
|
// write objptr back to index
|
|
*pIndex = (unsigned short)(objptr[index]);
|
|
pIndex++;
|
|
|
|
// write var ptr back to index
|
|
*pIndex = (unsigned short)(g_pCompilerData->var_ptr);
|
|
pIndex++;
|
|
|
|
// update var ptr and check limit
|
|
g_pCompilerData->var_ptr += objvar[index];
|
|
if (g_pCompilerData->var_ptr > var_limit)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_tmvsid];
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DistillObjBlocks()
|
|
{
|
|
if (g_pCompilerData->compile_mode == 0)
|
|
{
|
|
// Cannot "distill" large objects (eeprom_size set to greater than min_obj_limit(64k))
|
|
if (g_pCompilerData->obj_ptr <= min_obj_limit)
|
|
{
|
|
return DistillObjects();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CompileFinal()
|
|
{
|
|
if (g_pCompilerData->compile_mode == 0)
|
|
{
|
|
int vsize = g_pCompilerData->var_ptr;
|
|
int psize = g_pCompilerData->obj_ptr;
|
|
int vsize_psize = (psize << 16) | vsize;
|
|
int checksum_offset = g_pCompilerData->obj_ptr;
|
|
if (!EnterObj(0)) //placeholder for checksum;
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!g_pCompilerData->bFinalCompile && g_pCompilerData->bUnusedMethodElimination)
|
|
{
|
|
AddObjectPubConList(g_pCompilerData->current_filename, g_pCompilerData->pubcon_list, g_pCompilerData->pubcon_list_size);
|
|
}
|
|
|
|
// copy pubcon_list into obj (RPE: this could be optimized)
|
|
for (int i = 0; i < g_pCompilerData->pubcon_list_size; i++)
|
|
{
|
|
if (!EnterObj(g_pCompilerData->pubcon_list[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!EnterObjLong(0)) // allocate space for vsize/psize long
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// shift contents of obj up 4 bytes (to insert vsize/psize at front)
|
|
memmove(&(g_pCompilerData->obj[4]), &(g_pCompilerData->obj[0]), g_pCompilerData->obj_limit - 4);
|
|
// insert vsize_psize at beginning on obj
|
|
*((int*)(&g_pCompilerData->obj[0])) = vsize_psize;
|
|
// also store them separately in case they are larger than 65536
|
|
g_pCompilerData->vsize = vsize;
|
|
g_pCompilerData->psize = psize;
|
|
|
|
// calculate the checksum
|
|
unsigned char checksum = 0;
|
|
for (int i = 0; i < g_pCompilerData->obj_ptr; i++)
|
|
{
|
|
checksum += g_pCompilerData->obj[i];
|
|
}
|
|
g_pCompilerData->obj[checksum_offset + 4] = language_version - checksum; // + 4 because we shifted obj by 4 above
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PointToFirstCon()
|
|
{
|
|
bool bEof = false;
|
|
g_pElementizer->Reset();
|
|
|
|
if(g_pElementizer->GetNextBlock(block_con, bEof))
|
|
{
|
|
if (!bEof)
|
|
{
|
|
g_pCompilerData->source_finish = g_pCompilerData->source_start;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Determine_GetSymbol(const char* pSymbol, int errorCode, bool& bFound)
|
|
{
|
|
bFound = false;
|
|
if (g_pElementizer->FindSymbol(pSymbol))
|
|
{
|
|
if (g_pElementizer->GetType() == type_con)
|
|
{
|
|
bFound = true;
|
|
}
|
|
else if (g_pElementizer->GetType() != type_undefined)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[errorCode];
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DetermineStack()
|
|
{
|
|
int stackRequired = 16;
|
|
|
|
bool bFound;
|
|
if (!Determine_GetSymbol("_STACK", error_ssaf, bFound))
|
|
{
|
|
return false;
|
|
}
|
|
if (bFound)
|
|
{
|
|
stackRequired = g_pElementizer->GetValue();
|
|
}
|
|
if (!Determine_GetSymbol("_FREE", error_ssaf, bFound))
|
|
{
|
|
return false;
|
|
}
|
|
if (bFound)
|
|
{
|
|
stackRequired += g_pElementizer->GetValue();
|
|
}
|
|
|
|
if (stackRequired > 0x2000)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_safms];
|
|
return false;
|
|
}
|
|
|
|
g_pCompilerData->stack_requirement = stackRequired;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Determine_GetBitPos(int value, int& bitPos)
|
|
{
|
|
int bitCount = 0;
|
|
for (int i = 32; i > 0; i--)
|
|
{
|
|
if (value & 0x01)
|
|
{
|
|
bitPos = 32 - i;
|
|
bitCount++;
|
|
}
|
|
value >>= 1;
|
|
}
|
|
if (bitCount != 1)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_icms];
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DetermineClock()
|
|
{
|
|
// set to RCFAST for default
|
|
g_pCompilerData->clkmode = 0;
|
|
g_pCompilerData->clkfreq = 12000000;
|
|
|
|
int mode = 0;
|
|
int freq = 0;
|
|
int xin = 0;
|
|
int freqShift = 0;
|
|
|
|
// try to find the values in the symbols
|
|
bool bHaveClkMode = false;
|
|
bool bHaveClkFreq = false;
|
|
bool bHaveXinFreq = false;
|
|
|
|
if (!Determine_GetSymbol("_CLKMODE", error_sccx, bHaveClkMode))
|
|
{
|
|
return false;
|
|
}
|
|
if (bHaveClkMode)
|
|
{
|
|
mode = g_pElementizer->GetValue();
|
|
}
|
|
if (!Determine_GetSymbol("_CLKFREQ", error_sccx, bHaveClkFreq))
|
|
{
|
|
return false;
|
|
}
|
|
if (bHaveClkFreq)
|
|
{
|
|
freq = g_pElementizer->GetValue();
|
|
}
|
|
if (!Determine_GetSymbol("_XINFREQ", error_sccx, bHaveXinFreq))
|
|
{
|
|
return false;
|
|
}
|
|
if (bHaveXinFreq)
|
|
{
|
|
xin = g_pElementizer->GetValue();
|
|
}
|
|
|
|
if (bHaveClkMode == false && bHaveClkFreq == false && bHaveXinFreq == false)
|
|
{
|
|
// just use default (already set above)
|
|
return true;
|
|
}
|
|
|
|
// can't have either freq without clkmode
|
|
if (bHaveClkMode == false && (bHaveClkFreq == true || bHaveXinFreq == true))
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_cxswcm];
|
|
return false;
|
|
}
|
|
|
|
// can't have both clkfreq and xinfreq
|
|
if (bHaveClkFreq == true && bHaveXinFreq == true)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_ecoxmbs];
|
|
return false;
|
|
}
|
|
|
|
// validate the mode
|
|
if (mode == 0 || (mode & 0xFFFFF800) != 0 || (((mode & 0x03) != 0) && ((mode & 0x7FC) != 0)))
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_icms];
|
|
return false;
|
|
}
|
|
|
|
if (mode & 0x03) // RCFAST or RCSLOW
|
|
{
|
|
// can't have clkfreq or xinfreq in RC mode
|
|
if (bHaveClkFreq == true || bHaveXinFreq == true)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_cxnawrc];
|
|
return false;
|
|
}
|
|
|
|
if (mode == 2)
|
|
{
|
|
// RCSLOW
|
|
g_pCompilerData->clkmode = 1;
|
|
g_pCompilerData->clkfreq = 20000;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// RCFAST (which is already set as default)
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get xinput/xtal1/xtal2/xtal3
|
|
int bitPos = 0;
|
|
if (!Determine_GetBitPos((mode >> 2) & 0x0F, bitPos))
|
|
{
|
|
return false;
|
|
}
|
|
g_pCompilerData->clkmode = (unsigned char)((bitPos << 3) | 0x22); // 0x22 = 0100010b
|
|
|
|
if (mode & 0x7C0)
|
|
{
|
|
// get xmul
|
|
if (!Determine_GetBitPos(mode >> 6, bitPos))
|
|
{
|
|
return false;
|
|
}
|
|
freqShift = bitPos;
|
|
g_pCompilerData->clkmode += (unsigned char)(bitPos + 0x41); // 0x41 = 1000001b
|
|
}
|
|
}
|
|
|
|
// get clkfreq
|
|
|
|
// must have xinfreq or clkfreq
|
|
if (bHaveClkFreq == false && bHaveXinFreq == false)
|
|
{
|
|
g_pCompilerData->error = true;
|
|
g_pCompilerData->error_msg = g_pErrorStrings[error_coxmbs];
|
|
return false;
|
|
}
|
|
|
|
if (bHaveClkFreq)
|
|
{
|
|
g_pCompilerData->clkfreq = freq;
|
|
}
|
|
else
|
|
{
|
|
g_pCompilerData->clkfreq = (xin << freqShift);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DetermineDebug()
|
|
{
|
|
bool bFound = false;
|
|
if (!Determine_GetSymbol("_DEBUG", error_sdcobu, bFound))
|
|
{
|
|
return false;
|
|
}
|
|
if (bFound)
|
|
{
|
|
g_pCompilerData->debugbaud = g_pElementizer->GetValue();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char CompileDoc_ScanSkip(int& scanPtr)
|
|
{
|
|
while (g_pCompilerData->source[scanPtr] == ' ' || g_pCompilerData->source[scanPtr] == 9)
|
|
{
|
|
scanPtr++;
|
|
}
|
|
|
|
return g_pCompilerData->source[scanPtr];
|
|
}
|
|
|
|
bool CompileDoc_ScanInterface(bool bPrint, int& nCount)
|
|
{
|
|
int savedSourcePtr = g_pElementizer->GetSourcePtr();
|
|
|
|
bool bEof = false;
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// start off count with the length of the pub name
|
|
nCount = g_pCompilerData->source_finish - g_pCompilerData->source_start;
|
|
|
|
if (bPrint)
|
|
{
|
|
// print the pub name
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
if (!PrintChr(g_pCompilerData->source[g_pCompilerData->source_start + i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// start right after name
|
|
int scanPtr = g_pCompilerData->source_start + nCount;
|
|
|
|
// scan/print any parameters
|
|
char currentChar = CompileDoc_ScanSkip(scanPtr);
|
|
if (currentChar == '(')
|
|
{
|
|
for (;;)
|
|
{
|
|
currentChar = g_pCompilerData->source[scanPtr++];
|
|
nCount++;
|
|
if (bPrint)
|
|
{
|
|
if (!PrintChr(currentChar))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (currentChar == ')')
|
|
{
|
|
break;
|
|
}
|
|
else if (currentChar == ',')
|
|
{
|
|
// add a space after the comma
|
|
nCount++;
|
|
if (bPrint)
|
|
{
|
|
if (!PrintChr(' '))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// scan for first char of next param
|
|
CompileDoc_ScanSkip(scanPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// scan/print any result
|
|
currentChar = CompileDoc_ScanSkip(scanPtr);
|
|
if (currentChar == ':')
|
|
{
|
|
nCount+=3;
|
|
if (bPrint)
|
|
{
|
|
if (!PrintString(" : "))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// scan/print chars until we get a non-word char (end of the result name)
|
|
currentChar = CompileDoc_ScanSkip(scanPtr);
|
|
while (CheckWordChar(currentChar))
|
|
{
|
|
nCount++;
|
|
if (bPrint)
|
|
{
|
|
if (!PrintChr(' '))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
currentChar = g_pCompilerData->source[scanPtr++];
|
|
}
|
|
}
|
|
|
|
// done with this interface
|
|
g_pElementizer->SetSourcePtr(savedSourcePtr);
|
|
nCount += 5; // account for 'PUB ' & the following cr
|
|
if (bPrint)
|
|
{
|
|
if (!PrintChr(13))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileDoc_PrintAll(int sourcePtr)
|
|
{
|
|
g_pElementizer->Reset();
|
|
g_pElementizer->SetSourcePtr(sourcePtr);
|
|
|
|
bool bEof = false;
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNextBlock(block_pub, bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (bEof)
|
|
{
|
|
break;
|
|
}
|
|
if (g_pCompilerData->doc_mode)
|
|
{
|
|
// print extra cr and underline
|
|
if (!PrintChr(13))
|
|
{
|
|
return false;
|
|
}
|
|
int nCount = 0;
|
|
if (!CompileDoc_ScanInterface(false, nCount))
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < nCount; i++)
|
|
{
|
|
if (!PrintChr('_'))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if (!PrintChr(13))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// print pub name and interface
|
|
if (!PrintString("PUB "))
|
|
{
|
|
return false;
|
|
}
|
|
int nCount = 0;
|
|
if (!CompileDoc_ScanInterface(true, nCount))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pCompilerData->doc_mode)
|
|
{
|
|
// print extra cr
|
|
if (!PrintChr(13))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CompileDoc()
|
|
{
|
|
g_pElementizer->Reset();
|
|
g_pCompilerData->doc_flag = false;
|
|
g_pCompilerData->doc_mode = true;
|
|
|
|
bool bEof = false;
|
|
|
|
// in doc mode, this will print out any doc comments at the top of the obj
|
|
// GetNext() does it, and also sets the doc_flag if it did
|
|
// it'll return type_end until it gets to the first non-comment line (start of code)
|
|
while (!bEof)
|
|
{
|
|
if (!g_pElementizer->GetNext(bEof))
|
|
{
|
|
return false;
|
|
}
|
|
if (g_pElementizer->GetType() != type_end)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if something was printed above then add a CR
|
|
if (g_pCompilerData->doc_flag)
|
|
{
|
|
if (!PrintChr(13))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// clear doc_mode flag so we can print the interface stuff without doc comments in it
|
|
g_pCompilerData->doc_mode = false;
|
|
int savedSourceStart = g_pCompilerData->source_start;
|
|
|
|
char tempStr[512];
|
|
sprintf(tempStr, "Object \"%s", g_pCompilerData->obj_title);
|
|
if (!PrintString(tempStr))
|
|
{
|
|
return false;
|
|
}
|
|
if (!PrintString("\" Interface:\r\r"))
|
|
{
|
|
return false;
|
|
}
|
|
if (!CompileDoc_PrintAll(savedSourceStart))
|
|
{
|
|
return false;
|
|
}
|
|
short variables = *((short*)&(g_pCompilerData->obj[0])) >> 2;
|
|
short program = *((short*)&(g_pCompilerData->obj[2])) >> 2;
|
|
sprintf(tempStr, "\rProgram: %d Longs\rVariable: %d Longs\r", program, variables);
|
|
if (!PrintString(tempStr))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// doc_flag will get set when printing the interfaces above
|
|
if (g_pCompilerData->doc_flag)
|
|
{
|
|
// set doc mode to true, in order to print the interfaces (again) with doc comments in it
|
|
g_pCompilerData->doc_mode = true;
|
|
|
|
// doc comments in pubs print interface again, this time with doc comments
|
|
if (!CompileDoc_PrintAll(savedSourceStart))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 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. //
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|