diff --git a/README.md b/README.md index 0fc4f9b..fc73494 100644 --- a/README.md +++ b/README.md @@ -13,19 +13,24 @@ The resulting compiler is a frankenstein constructed from a converted javascript The performance of interpreted code is clearly not as good as native machine code but the penalty for 16bit code is around 40-50% and less than 10% for floating point. Code that can use 8bit my suffer up to a factor of 10 to 20. +The goal is to implement the actual C standard and not some subset for performance reasons. So the compiler must support: + +* Floating point +* Recursion +* Multi dimensional arrays +* Pointer to structs + + ## Limits and Errors -The first release of the compiler is severely limited considering it is only two weeks old, so there is quite a lot missing or broken. I hope to cross out most of the problems in the coming weeks. +After four weeks, the compiler has now matured significantly. There are still several open areas. ### Language -* No union type * No long integer * No struct function return * Missing const checks for structs and enums -* No static variables in functions * Missing warnings for all kind of abuses -* no #if in preprocessor ### Linker @@ -34,7 +39,6 @@ The first release of the compiler is severely limited considering it is only two ### Standard Libraries -* Limited formatting in printf * No file functions ### Runtime @@ -46,12 +50,11 @@ The first release of the compiler is severely limited considering it is only two ### Optimizing * All global variables are considered volatile -* No loop opmtimization +* Simple loop opmtimization * Poor bookeeping of callee saved registers -* Missing livetime reduction of intermediates -* No block domination analysis +* Partial block domination analysis * No register use for arguments -* Auto variables places on fixed stack for known call sequence +* Auto variables placed on fixed stack for known call sequence ### Intermediate code generation @@ -60,8 +63,23 @@ The first release of the compiler is severely limited considering it is only two ### Native code generation -* Calling non native functions missing * More byte operation optimisation required +* Simple loop detection and optimisation not complete + +## Compiler arguments + +The compiler is command line driven, and creates an executable .prg file. + + oscar64 {-i=includePath} [-o=output.prg] [-cr=runtime.c] [-e] [-n] [-dSYMBOL[=value]] {source.c} + +* -i : additional include paths +* -o : optional output file name +* -cr : alternative runtime library, replaces the crt.c +* -e : execute the result in the integrated emulator +* -n : create pure native code for all functions +* -d : define a symbol (e.g. NOFLOAT to avoid float code in printf) + +A list of source files can be provided. ## Implementation Details @@ -69,6 +87,9 @@ The compiler does a full program compile, the linker step is part of the compila #pragma compile("stdio.c") +The character map for string and char constants can be changed with a pragma to match a custon character set or PETSCII. + + #pragma charmap(char, code [,count]) The byte code interpreter is compiled by the compiler itself and placed in the source file "crt.c". Functions implementing byte codes are marked with a pragma: @@ -123,4 +144,3 @@ Routines can be marked to be compiled to 6502 machine code with the native pragm - diff --git a/oscar64/Errors.h b/oscar64/Errors.h index 86e7216..7a4a69a 100644 --- a/oscar64/Errors.h +++ b/oscar64/Errors.h @@ -49,6 +49,7 @@ enum ErrorID EERR_ASM_INVALID_INSTRUCTION, EERR_ASM_INVALID_MODE, EERR_PRAGMA_PARAMETER, + ERRR_PREPROCESSOR, EERR_INVALID_PREPROCESSOR, }; diff --git a/oscar64/Scanner.cpp b/oscar64/Scanner.cpp index f98a08c..bbb065a 100644 --- a/oscar64/Scanner.cpp +++ b/oscar64/Scanner.cpp @@ -252,9 +252,11 @@ Scanner::Scanner(Errors* errors, Preprocessor* preprocessor) { mOffset = 0; mLine = mPreprocessor->mLine; - mPrepCondition = 0; - mPrepPending = 0; + mPrepCondFalse = 0; + mPrepCondDepth = 0; + mPrepCondExit = 0; mAssemblerMode = false; + mPreprocessorMode = false; mMacroExpansion = nullptr; mDefines = new MacroDict(); @@ -358,13 +360,71 @@ void Scanner::NextToken(void) NextRawToken(); if (mToken == TK_PREP_ENDIF) { - if (mPrepCondition > 0) - mPrepCondition--; - else if (mPrepPending > 0) - mPrepPending--; + if (mPrepCondFalse > 0) + mPrepCondFalse--; + else if (mPrepCondExit) + mPrepCondExit = false; + else if (mPrepCondDepth > 0) + mPrepCondDepth--; else mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #endif"); } + else if (mToken == TK_PREP_ELSE) + { + if (mPrepCondExit) + { + + } + else if (mPrepCondFalse == 1) + { + mPrepCondFalse = 0; + mPrepCondDepth++; + } + else if (mPrepCondFalse > 1) + { + } + else if (mPrepCondDepth > 0) + { + mPrepCondExit = true; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else"); + } + else if (mToken == TK_PREP_ELIF) + { + if (mPrepCondExit) + { + + } + else if (mPrepCondFalse == 1) + { + mPreprocessorMode = true; + mPrepCondFalse = 0; + + NextToken(); + int v = PrepParseConditional(); + if (v) + { + mPrepCondFalse = 0; + mPrepCondDepth++; + } + else + mPrepCondFalse++; + + mPreprocessorMode = false; + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + } + else if (mPrepCondFalse > 1) + { + } + else if (mPrepCondDepth > 0) + { + mPrepCondExit = true; + } + else + mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else"); + } else if (mToken == TK_EOF) { if (!mPreprocessor->CloseSource()) @@ -372,10 +432,10 @@ void Scanner::NextToken(void) mToken = TK_NONE; mOffset = 0; } - else if (mPrepCondition > 0) + else if (mPrepCondFalse > 0 || mPrepCondExit) { - if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF) - mPrepCondition++; + if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF || mToken == TK_PREP_IF) + mPrepCondFalse++; } else if (mToken == TK_PREP_INCLUDE) { @@ -439,9 +499,9 @@ void Scanner::NextToken(void) { Macro * def = mDefines->Lookup(mTokenIdent); if (def) - mPrepPending++; + mPrepCondDepth++; else - mPrepCondition++; + mPrepCondFalse++; } } else if (mToken == TK_PREP_IFNDEF) @@ -451,11 +511,24 @@ void Scanner::NextToken(void) { Macro * def = mDefines->Lookup(mTokenIdent); if (!def) - mPrepPending++; + mPrepCondDepth++; else - mPrepCondition++; + mPrepCondFalse++; } } + else if (mToken == TK_PREP_IF) + { + mPreprocessorMode = true; + NextToken(); + int v = PrepParseConditional(); + if (v) + mPrepCondDepth++; + else + mPrepCondFalse++; + mPreprocessorMode = false; + if (mToken != TK_EOL) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected"); + } else if (mToken == TK_IDENT) { Macro* def = nullptr; @@ -551,7 +624,7 @@ void Scanner::NextRawToken(void) while (IsWhitespace(mTokenChar)) { - if (mAssemblerMode && mTokenChar == '\n') + if ((mAssemblerMode || mPreprocessorMode) && mTokenChar == '\n') { mToken = TK_EOL; NextChar(); @@ -1321,3 +1394,234 @@ void Scanner::ParseNumberToken(void) } } } + + +int64 Scanner::PrepParseSimple(void) +{ + int64 v = 0; + + switch (mToken) + { + case TK_INTEGER: + v = mTokenInteger; + NextToken(); + break; + case TK_SUB: + NextToken(); + v = -PrepParseSimple(); + break; + case TK_LOGICAL_NOT: + NextToken(); + v = !PrepParseSimple(); + break; + case TK_BINARY_NOT: + NextToken(); + v = ~PrepParseSimple(); + break; + case TK_OPEN_PARENTHESIS: + NextToken(); + v = PrepParseConditional(); + if (mToken == TK_CLOSE_PARENTHESIS) + NextToken(); + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "')' expected"); + break; + default: + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Invalid preprocessor token", TokenName(mToken)); + if (mToken != TK_EOL) + NextToken(); + } + + return v; +} + +int64 Scanner::PrepParseMul(void) +{ + int64 v = PrepParseSimple(); + int64 u; + for (;;) + { + switch (mToken) + { + case TK_MUL: + NextToken(); + v *= PrepParseSimple(); + break; + case TK_DIV: + NextToken(); + u = PrepParseSimple(); + if (u == 0) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero"); + else + v /= u; + break; + case TK_MOD: + u = PrepParseSimple(); + if (u == 0) + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero"); + else + v %= u; + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseAdd(void) +{ + int64 v = PrepParseMul(); + for (;;) + { + switch (mToken) + { + case TK_ADD: + NextToken(); + v += PrepParseMul(); + break; + case TK_SUB: + NextToken(); + v -= PrepParseMul(); + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseShift(void) +{ + int64 v = PrepParseAdd(); + for (;;) + { + switch (mToken) + { + case TK_LEFT_SHIFT: + NextToken(); + v <<= PrepParseAdd(); + break; + case TK_RIGHT_SHIFT: + NextToken(); + v >>= PrepParseAdd(); + break; + default: + return v; + } + } +} + +int64 Scanner::PrepParseRel(void) +{ + int64 v = PrepParseShift(); + for (;;) + { + switch (mToken) + { + case TK_LESS_THAN: + NextToken(); + v = v < PrepParseShift(); + break; + case TK_GREATER_THAN: + NextToken(); + v = v > PrepParseShift(); + break; + case TK_LESS_EQUAL: + NextToken(); + v = v <= PrepParseShift(); + break; + case TK_GREATER_EQUAL: + NextToken(); + v = v >= PrepParseShift(); + break; + case TK_EQUAL: + NextToken(); + v = v == PrepParseShift(); + break; + case TK_NOT_EQUAL: + NextToken(); + v = v != PrepParseShift(); + break; + default: + return v; + } + } + +} + +int64 Scanner::PrepParseBinaryAnd(void) +{ + int64 v = PrepParseRel(); + while (mToken == TK_BINARY_AND) + { + NextToken(); + v &= PrepParseRel(); + } + return v; +} + +int64 Scanner::PrepParseBinaryXor(void) +{ + int64 v = PrepParseBinaryAnd(); + while (mToken == TK_BINARY_XOR) + { + NextToken(); + v ^= PrepParseBinaryAnd(); + } + return v; +} + +int64 Scanner::PrepParseBinaryOr(void) +{ + int64 v = PrepParseBinaryXor(); + while (mToken == TK_BINARY_OR) + { + NextToken(); + v |= PrepParseBinaryXor(); + } + return v; +} + +int64 Scanner::PrepParseLogicalAnd(void) +{ + int64 v = PrepParseBinaryOr(); + while (mToken == TK_LOGICAL_AND) + { + NextToken(); + if (!PrepParseBinaryOr()) + v = 0; + } + return v; +} + +int64 Scanner::PrepParseLogicalOr(void) +{ + int64 v = PrepParseLogicalAnd(); + while (mToken == TK_LOGICAL_OR) + { + NextToken(); + if (PrepParseLogicalAnd()) + v = 1; + } + return v; +} + +int64 Scanner::PrepParseConditional(void) +{ + int64 v = PrepParseLogicalOr(); + if (mToken == TK_QUESTIONMARK) + { + NextToken(); + int64 vt = PrepParseConditional(); + if (mToken == TK_COLON) + NextToken(); + else + mErrors->Error(mLocation, ERRR_PREPROCESSOR, "':' expected"); + int64 vf = PrepParseConditional(); + if (v) + v = vt; + else + v = vf; + } + + return v; +} diff --git a/oscar64/Scanner.h b/oscar64/Scanner.h index ef531a9..82c233b 100644 --- a/oscar64/Scanner.h +++ b/oscar64/Scanner.h @@ -175,7 +175,8 @@ public: Errors* mErrors; Preprocessor * mPreprocessor; - int mPrepCondition, mPrepPending; + int mPrepCondFalse, mPrepCondDepth; + bool mPrepCondExit; int mOffset; const char * mLine; @@ -194,6 +195,7 @@ public: void SetAssemblerMode(bool mode); bool mAssemblerMode; + bool mPreprocessorMode; void AddMacro(const Ident* ident, const char* value); protected: @@ -215,4 +217,16 @@ protected: bool NextChar(void); void ParseNumberToken(void); + int64 PrepParseSimple(void); + int64 PrepParseMul(void); + int64 PrepParseAdd(void); + int64 PrepParseShift(void); + int64 PrepParseRel(void); + int64 PrepParseBinaryAnd(void); + int64 PrepParseBinaryXor(void); + int64 PrepParseBinaryOr(void); + int64 PrepParseLogicalAnd(void); + int64 PrepParseLogicalOr(void); + int64 PrepParseConditional(void); + };