oscar64/oscar64/oscar64.cpp

640 lines
17 KiB
C++

#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#include "Compiler.h"
#include "DiskImage.h"
#include <time.h>
#ifdef _WIN32
bool GetProductAndVersion(char* strProductName, char* strProductVersion)
{
// get the filename of the executable containing the version resource
TCHAR szFilename[MAX_PATH + 1] = { 0 };
if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
{
return false;
}
// allocate a block of memory for the version info
DWORD dummy;
DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
if (dwSize == 0)
{
return false;
}
BYTE* data = new BYTE[dwSize];
// load the version info
if (!GetFileVersionInfo(szFilename, NULL, dwSize, data))
{
return false;
}
// get the name and version strings
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
LPVOID pvProductVersion = NULL;
unsigned int iProductVersionLen = 0;
// replace "040904e4" with the language ID of your resources
if (!VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductName", &pvProductName, &iProductNameLen) ||
!VerQueryValueA(&data[0], "\\StringFileInfo\\000904b0\\ProductVersion", &pvProductVersion, &iProductVersionLen))
{
return false;
}
strcpy_s(strProductName, 100, (LPCSTR)pvProductName);
strcpy_s(strProductVersion, 100, (LPCSTR)pvProductVersion);
return true;
}
#endif
int main2(int argc, const char** argv)
{
InitDeclarations();
InitAssembler();
if (argc > 1)
{
char basePath[200], crtPath[200], includePath[200], targetPath[200], diskPath[200];
char strProductName[100], strProductVersion[200];
int dataFileInterleave = 10;
#ifdef _WIN32
GetProductAndVersion(strProductName, strProductVersion);
DWORD length = ::GetModuleFileNameA(NULL, basePath, sizeof(basePath));
#else
strcpy(strProductName, "oscar64");
strcpy(strProductVersion, "1.30.250");
#ifdef __APPLE__
uint32_t length = sizeof(basePath);
_NSGetExecutablePath(basePath, &length);
length = strlen(basePath);
#else
int length = readlink("/proc/self/exe", basePath, sizeof(basePath));
// strcpy(basePath, argv[0]);
// int length = strlen(basePath);
#endif
#endif
while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\')
length--;
if (length > 0)
{
length--;
while (length > 0 && basePath[length - 1] != '/' && basePath[length - 1] != '\\')
length--;
}
basePath[length] = 0;
Compiler* compiler = new Compiler();
compiler->mCompilerOptions |= COPT_NATIVE;
Location loc;
GrowingArray<const char*> dataFiles(nullptr);
GrowingArray<bool> dataFileCompressed(false);
compiler->mPreprocessor->AddPath(basePath);
strcpy_s(includePath, basePath);
{
FILE* crtFile;
char crtFileNamePath[FILENAME_MAX];
crtFileNamePath[FILENAME_MAX - 1] = '\0';
strcpy_s(crtFileNamePath, basePath);
strcat_s(crtFileNamePath, "include/crt.h");
if (!fopen_s(&crtFile, crtFileNamePath, "r"))
strcat_s(includePath, "include/");
else
{
strcpy_s(crtFileNamePath, basePath);
strcat_s(crtFileNamePath, "include/oscar64/crt.h");
if (!fopen_s(&crtFile, crtFileNamePath, "r"))
strcat_s(includePath, "include/oscar64/");
else
{
printf("Could not locate Oscar64 includes under %s\n", basePath);
return 20;
}
}
fclose(crtFile);
}
compiler->mPreprocessor->AddPath(includePath);
strcpy_s(crtPath, includePath);
strcat_s(crtPath, "crt.c");
bool emulate = false, profile = false;
int trace = 0;
targetPath[0] = 0;
diskPath[0] = 0;
char targetFormat[20];
strcpy_s(targetFormat, "prg");
char targetMachine[20];
strcpy_s(targetMachine, "c64");
compiler->AddDefine(Ident::Unique("__OSCAR64C__"), "1");
compiler->AddDefine(Ident::Unique("__STDC__"), "1");
compiler->AddDefine(Ident::Unique("__STDC_VERSION__"), "199901L");
bool defining = false;
for (int i = 1; i < argc; i++)
{
const char* arg = argv[i];
if (defining)
{
defining = false;
char def[100];
int i = 0;
while (arg[i] && arg[i] != '=')
{
def[i] = arg[i];
i++;
}
def[i] = 0;
if (arg[i] == '=')
compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1));
else
compiler->AddDefine(Ident::Unique(def), "");
}
else if (arg[0] == '-')
{
if (arg[1] == 'i' && arg[2] == '=')
{
compiler->mPreprocessor->AddPath(arg + 3);
}
else if (arg[1] == 'f' && arg[2] == '=')
{
dataFiles.Push(arg + 3);
dataFileCompressed.Push(false);
}
else if (arg[1] == 'f' && arg[2] == 'z' && arg[3] == '=')
{
dataFiles.Push(arg + 4);
dataFileCompressed.Push(true);
}
else if (arg[1] == 'f' && arg[2] == 'i' && arg[3] == '=')
{
dataFileInterleave = atoi(arg + 4);
}
else if (arg[1] == 'o' && arg[2] == '=')
{
strcpy_s(targetPath, arg + 3);
}
else if (arg[1] == 'r' && arg[2] == 't' && arg[3] == '=')
{
strcpy_s(crtPath, arg + 4);
}
else if (arg[1] == 'd' && arg[2] == '6' && arg[3] == '4' && arg[4] == '=')
{
strcpy_s(diskPath, arg + 5);
}
else if (arg[1] == 't' && arg[2] == 'f' && arg[3] == '=')
{
strcpy_s(targetFormat, arg + 4);
}
else if (arg[1] == 't' && arg[2] == 'm' && arg[3] == '=')
{
strcpy_s(targetMachine, arg + 4);
}
else if (arg[1] == 'c' && arg[2] == 'i' && arg[3] == 'd' && arg[4] == '=')
{
char cid[10];
strcpy_s(cid, arg + 5);
compiler->mCartridgeID = atoi(cid);
}
else if (arg[1] == 'n')
{
compiler->mCompilerOptions |= COPT_NATIVE;
}
else if (arg[1] == 'b' && arg[2] == 'c')
{
compiler->mCompilerOptions &= ~COPT_NATIVE;
}
else if (arg[1] == 'p' && arg[2] == 's' && arg[3] == 'c' && arg[4] == 'i')
{
compiler->mCompilerOptions |= COPT_PETSCII;
}
else if (arg[1] == 'O')
{
if (arg[2] == '0')
compiler->mCompilerOptions &= ~(COPT_OPTIMIZE_ALL);
else if (arg[1] == '1' || arg[1] == 0)
compiler->mCompilerOptions |= COPT_OPTIMIZE_DEFAULT;
else if (arg[2] == '2')
compiler->mCompilerOptions |= COPT_OPTIMIZE_SPEED;
else if (arg[2] == '3')
compiler->mCompilerOptions |= COPT_OPTIMIZE_ALL;
else if (arg[2] == 's')
compiler->mCompilerOptions |= COPT_OPTIMIZE_SIZE;
else if (arg[2] == 'a')
compiler->mCompilerOptions |= COPT_OPTIMIZE_ASSEMBLER;
else if (arg[2] == 'i')
compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_INLINE;
else if (arg[2] == 'z')
compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_ZEROPAGE;
else if (arg[2] == 'p')
compiler->mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS;
else if (arg[2] == 'g')
compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL;
else if (arg[2] == 'm')
compiler->mCompilerOptions |= COPT_OPTIMIZE_MERGE_CALLS;
}
else if (arg[1] == 'e')
{
emulate = true;
if (arg[2] == 'p')
profile = true;
else if (arg[2] == 't')
trace = 2;
else if (arg[2] == 'b')
trace = 1;
}
else if (arg[1] == 'D' && !arg[2])
{
defining = true;
}
else if (arg[1] == 'd' || arg[1] == 'D')
{
char def[100];
int i = 2;
while (arg[i] && arg[i] != '=')
{
def[i - 2] = arg[i];
i++;
}
def[i - 2] = 0;
if (arg[i] == '=')
compiler->AddDefine(Ident::Unique(def), _strdup(arg + i + 1));
else
compiler->AddDefine(Ident::Unique(def), "");
}
else if (arg[1] == 'g')
{
compiler->mCompilerOptions |= COPT_DEBUGINFO;
}
else if (arg[1] == 'v')
{
compiler->mCompilerOptions |= COPT_VERBOSE;
if (arg[2] == '2')
compiler->mCompilerOptions |= COPT_VERBOSE2;
else if (arg[2] == '3')
compiler->mCompilerOptions |= COPT_VERBOSE2 | COPT_VERBOSE3;
}
else if (arg[1] == 'x' && arg[2] == 'z')
{
compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE;
}
else if (arg[1] == 'p' && arg[2] == 'p')
{
compiler->mCompilerOptions |= COPT_CPLUSPLUS;
compiler->AddDefine(Ident::Unique("__cplusplus"), "1");
}
else
compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg);
}
else
{
if (!targetPath[0])
strcpy_s(targetPath, argv[i]);
ptrdiff_t n = strlen(argv[i]);
if (n > 4 && argv[i][n - 4] == '.' && argv[i][n - 3] == 'c' && argv[i][n - 2] == 'p' && argv[i][n - 1] == 'p')
{
compiler->mCompilerOptions |= COPT_CPLUSPLUS;
compiler->AddDefine(Ident::Unique("__cplusplus"), "1");
}
compiler->mCompilationUnits->AddUnit(loc, argv[i], nullptr);
}
}
if (compiler->mCompilerOptions & COPT_NATIVE)
{
compiler->AddDefine(Ident::Unique("OSCAR_NATIVE_ALL"), "1");
}
// REMOVE ME
// compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL;
// REMOVE ME
char basicStart[10];
strcpy_s(basicStart, "0x0801");
if (!strcmp(targetMachine, "c64"))
{
compiler->mTargetMachine = TMACH_C64;
compiler->AddDefine(Ident::Unique("__C64__"), "1");
}
else if (!strcmp(targetMachine, "c128"))
{
strcpy_s(basicStart, "0x1c01");
compiler->mTargetMachine = TMACH_C128;
compiler->AddDefine(Ident::Unique("__C128__"), "1");
}
else if (!strcmp(targetMachine, "c128b"))
{
strcpy_s(basicStart, "0x1c01");
compiler->mTargetMachine = TMACH_C128B;
compiler->AddDefine(Ident::Unique("__C128B__"), "1");
}
else if (!strcmp(targetMachine, "c128e"))
{
strcpy_s(basicStart, "0x1c01");
compiler->mTargetMachine = TMACH_C128E;
compiler->AddDefine(Ident::Unique("__C128E__"), "1");
}
else if (!strcmp(targetMachine, "vic20"))
{
strcpy_s(basicStart, "0x1001");
compiler->mTargetMachine = TMACH_VIC20;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "vic20+3"))
{
strcpy_s(basicStart, "0x0401");
compiler->mTargetMachine = TMACH_VIC20_3K;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "vic20+8"))
{
strcpy_s(basicStart, "0x1201");
compiler->mTargetMachine = TMACH_VIC20_8K;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "vic20+16"))
{
strcpy_s(basicStart, "0x1201");
compiler->mTargetMachine = TMACH_VIC20_16K;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "vic20+24"))
{
strcpy_s(basicStart, "0x1201");
compiler->mTargetMachine = TMACH_VIC20_24K;
compiler->AddDefine(Ident::Unique("__VIC20__"), "1");
}
else if (!strcmp(targetMachine, "pet"))
{
strcpy_s(basicStart, "0x0401");
compiler->mTargetMachine = TMACH_PET_8K;
compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
}
else if (!strcmp(targetMachine, "pet16"))
{
strcpy_s(basicStart, "0x0401");
compiler->mTargetMachine = TMACH_PET_16K;
compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
}
else if (!strcmp(targetMachine, "pet32"))
{
strcpy_s(basicStart, "0x0401");
compiler->mTargetMachine = TMACH_PET_32K;
compiler->AddDefine(Ident::Unique("__CBMPET__"), "1");
}
else if (!strcmp(targetMachine, "plus4"))
{
strcpy_s(basicStart, "0x1001");
compiler->mTargetMachine = TMACH_PLUS4;
compiler->AddDefine(Ident::Unique("__PLUS4__"), "1");
}
else if (!strcmp(targetMachine, "x16"))
{
strcpy_s(basicStart, "0x0801");
compiler->mTargetMachine = TMACH_X16;
compiler->AddDefine(Ident::Unique("__X16__"), "1");
}
else if (!strcmp(targetMachine, "nes"))
{
compiler->mTargetMachine = TMACH_NES;
}
else if (!strcmp(targetMachine, "nes_nrom_h"))
{
compiler->mTargetMachine = TMACH_NES_NROM_H;
}
else if (!strcmp(targetMachine, "nes_nrom_v"))
{
compiler->mTargetMachine = TMACH_NES_NROM_V;
}
else if (!strcmp(targetMachine, "nes_mmc1"))
{
compiler->mTargetMachine = TMACH_NES_MMC1;
}
else if (!strcmp(targetMachine, "nes_mmc3"))
{
compiler->mTargetMachine = TMACH_NES_MMC3;
}
else if (!strcmp(targetMachine, "atari"))
{
compiler->mTargetMachine = TMACH_ATARI;
compiler->AddDefine(Ident::Unique("__ATARI__"), "1");
}
else
compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target machine option", targetMachine);
if (compiler->mTargetMachine >= TMACH_NES && compiler->mTargetMachine <= TMACH_NES_MMC3)
{
compiler->mCompilerOptions |= COPT_TARGET_NES;
compiler->mCompilerOptions |= COPT_EXTENDED_ZERO_PAGE;
compiler->mCompilerOptions |= COPT_NATIVE;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_NES"), "1");
switch (compiler->mTargetMachine)
{
default:
case TMACH_NES:
case TMACH_NES_NROM_H:
case TMACH_NES_NROM_V:
break;
case TMACH_NES_MMC1:
compiler->AddDefine(Ident::Unique("__NES_MMC1__"), "1");
break;
case TMACH_NES_MMC3:
compiler->AddDefine(Ident::Unique("__NES_MMC3__"), "1");
break;
}
compiler->AddDefine(Ident::Unique("__NES__"), "1");
}
else if (!strcmp(targetFormat, "prg"))
{
compiler->mCompilerOptions |= COPT_TARGET_PRG;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_PRG"), "1");
compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart);
}
else if (!strcmp(targetFormat, "crt"))
{
compiler->mCompilerOptions |= COPT_TARGET_CRT_EASYFLASH;
compiler->mCartridgeID = 0x0020;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT_EASYFLASH"), "1");
}
else if (!strcmp(targetFormat, "crt16"))
{
compiler->mCompilerOptions |= COPT_TARGET_CRT16;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT16"), "1");
}
else if (!strcmp(targetFormat, "crt8"))
{
compiler->mCompilerOptions |= COPT_TARGET_CRT8;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_CRT8"), "1");
}
else if (!strcmp(targetFormat, "bin"))
{
compiler->mCompilerOptions |= COPT_TARGET_BIN;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_BIN"), "1");
}
else if (!strcmp(targetFormat, "lzo"))
{
compiler->mCompilerOptions |= COPT_TARGET_LZO;
compiler->AddDefine(Ident::Unique("OSCAR_TARGET_LZO"), "1");
compiler->AddDefine(Ident::Unique("OSCAR_BASIC_START"), basicStart);
}
else
compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid target format option", targetFormat);
if (compiler->mErrors->mErrorCount == 0)
{
strcpy_s(compiler->mVersion, strProductVersion);
if (compiler->mCompilerOptions & COPT_VERBOSE)
{
printf("Starting %s %s\n", strProductName, strProductVersion);
}
{
char dstring[100], tstring[100];
time_t now = time(NULL);
struct tm t;
#ifdef _WIN32
localtime_s(&t, &now);
#else
localtime_r(&now, &t);
#endif
strftime(dstring, sizeof(tstring) - 1, "\"%b %d %Y\"", &t);
strftime(tstring, sizeof(dstring) - 1, "\"%H:%M:%S\"", &t);
compiler->AddDefine(Ident::Unique("__DATE__"), dstring);
compiler->AddDefine(Ident::Unique("__TIME__"), tstring);
}
// Add runtime module
if (crtPath[0])
compiler->mCompilationUnits->AddUnit(loc, crtPath, nullptr);
if (compiler->mCompilerOptions & COPT_TARGET_LZO)
{
compiler->BuildLZO(targetPath);
}
else if (compiler->ParseSource() && compiler->GenerateCode())
{
DiskImage* d64 = nullptr;
if (diskPath[0])
d64 = new DiskImage(diskPath);
compiler->WriteOutputFile(targetPath, d64);
if (d64)
{
for (int i = 0; i < dataFiles.Size(); i++)
{
if (!d64->WriteFile(dataFiles[i], dataFileCompressed[i], dataFileInterleave))
{
printf("Could not embed disk file %s\n", dataFiles[i]);
return 20;
}
}
if (!d64->WriteImage(diskPath))
{
printf("Could not write disk image %s\n", diskPath);
return 20;
}
}
if (emulate)
compiler->ExecuteCode(profile, trace);
}
}
if (compiler->mErrors->mErrorCount != 0)
return 20;
}
else
{
printf("oscar64 {-i=includePath} [-o=output.prg] [-rt=runtime.c] [-tf=target] [-tm=machine] [-e] [-n] [-g] [-O(0|1|2|3)] [-pp] {-dSYMBOL[=value]} [-v] [-d64=diskname] {-f[z]=file.xxx} {source.c}\n");
return 0;
}
return 0;
}
#ifdef WIN32
#ifndef _DEBUG
int seh_filter(unsigned int code, struct _EXCEPTION_POINTERS* info)
{
#ifdef _WIN64
printf("oscar64 crashed. %08x %08llx", info->ExceptionRecord->ExceptionCode, (uint64)(info->ExceptionRecord->ExceptionAddress));
#else
printf("oscar64 crashed. %08x %08x", info->ExceptionRecord->ExceptionCode, (uint32)(info->ExceptionRecord->ExceptionAddress));
#endif
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#endif
int main(int argc, const char** argv)
{
#if 1
#ifdef _WIN32
#ifndef __GNUC__
#ifndef _DEBUG
__try
{
#endif
#endif
#endif
#endif
return main2(argc, argv);
#if 1
#ifdef _WIN32
#ifndef __GNUC__
#ifndef _DEBUG
}
__except (seh_filter(GetExceptionCode(), GetExceptionInformation()))
{
return 30;
}
#endif
#endif
#endif
#endif
}