diff --git a/Makefile b/Makefile index 75353fe..992c873 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,9 @@ debug: distclean: - rm -rf ./build - -rm -rf ./testout + -rm -rf ./build + -rm -rf ./qasmout + -rm -rf ./m32out clean: -rm -rf ./build @@ -60,13 +61,13 @@ compare: asm: test1: - -qasm src/main.s + -qasm testdata/3001-lroathe.S test2: - -qasm src/testfile.s + -qasm testdata/3002-testfile.S test3: - -qasm src/var.s + -qasm testdata/3003-var.S diff --git a/README b/README index 5e4a78c..0d6b762 100644 --- a/README +++ b/README @@ -1,29 +1,38 @@ Merlin Compatible assembler/linker (and more) for linux This project is FAR from complete...as I have just gotten it to compile all 256 65816 opcodes. -The program will read/compile linux based Merlin syntax files (.s), as well as original Merlin 8/16/16+ files in Merlin format -(high bit set), compressed spaces, and only a to end lines. I plan on adding a mode that will process and convert to linux style so -files can be converted for easy editing under linux. -Merlin32 is great, but there are aspects of it that I don't like (always outputting assembly status and creating .txt file listings, etc) +Update (11/19/2019) - All code generating opcodes/psuedo-ops complete. -Along time ago, in the mid 1980's I wrote a Merlin compatible assembler and programming shell (with the help of Lane Roathe) for the Apple //GS -Recently, Lane sent me the source code to that assembler. Unfortunately, because of some of the features we added to the assembler, it will not -compile under Merlin or Merlin32. (We actually used qasm to build qasm). +The program will read/compile linux based Merlin syntax files (.s), as well as original Merlin +8/16/16+ files in Merlin format +(high bit set), compressed spaces, and only a to end lines. I plan on adding a mode that +will process and convert to linux style so files can be converted for easy editing under linux. -This project will be a linux compatible version, that will compile/link the original source. However, due to speed and everything 'linux' -I doubt the original will be used much. +Merlin32 is great, but there are aspects of it that I don't like (always outputting assembly +status and creating .txt file listings, etc) -I also would like to interface with the CiderPress library to allow output from the assembler/linker to write files directly to Prodos -based .2MG (or other) images. That way, after a compile, the code under test can be immediately run from a GS emulator. +Along time ago, in the mid 1980's I wrote a Merlin compatible assembler and programming shell +(with the help of Lane Roathe) for the Apple //GS +Recently, Lane sent me the source code to that assembler. Unfortunately, because of some of the +features we added to the assembler, it will not compile under Merlin or Merlin32. (We actually used +qasm to build qasm). + +This project will be a linux compatible version, that will compile/link the original source. +However, due to speed and everything 'linux' I doubt the original will be used much. + +I also would like to interface with the CiderPress library to allow output from the assembler/linker +to write files directly to Prodos based .2MG (or other) images. That way, after a compile, the code +under test can be immediately run from a GS emulator. This early version relies on the Poco Foundation libraries. You must have these installed. -Additionally, this repo relies on another repo here called 'libpal'. Both repositories must be cloned at the same level. +Additionally, this repo relies on another repo here called 'libpal'. Both repositories must +be cloned at the same level. libpal does not have to be built. The 'qasm' compile will build all of those source files. -'cmake' is used to build the Makefiles. There is a generic Makefile in the base directory that does all of the 'cmake' stuff for you. -(in a 'build' directory) +'cmake' is used to build the Makefiles. There is a generic Makefile in the base directory that does +all of the 'cmake' stuff for you. (in a 'build' directory) For example: mkdir workdir diff --git a/asm.cpp b/asm.cpp index b480bcb..6dc0de5 100644 --- a/asm.cpp +++ b/asm.cpp @@ -2,6 +2,9 @@ #include "asm.h" #include "eval.h" #include "psuedo.h" +#include +#include + #define CLASS MerlinLine @@ -99,7 +102,7 @@ void CLASS::print(uint32_t lineno) } } bool empty = false; - if ((printlable == "") && (opcode == "") && (operand == "")) + if ((printlable == "") && (opcode == "") && (printoperand == "")) { empty = true; } @@ -174,7 +177,7 @@ void CLASS::print(uint32_t lineno) { pcol += printf("%c", comment[cc]); comct++; - if ((comment[cc] <= ' ') && (pcol >= (commentcol + savpcol + 10))) + if ((comment[cc] <= ' ') && (pcol >= (commentcol + savpcol + 20))) { printf("\n"); pcol = 0; @@ -202,7 +205,21 @@ void CLASS::print(uint32_t lineno) { pcol += printf(" "); } - pcol += printf("%s ", operand.c_str()); + if (isDebug() > 1) + { + pcol += printf("%s ", operand.c_str()); + } + else + { + if (printoperand.length() > 0) + { + pcol += printf("%s ", printoperand.c_str()); + } + else + { + pcol += printf("%s ", operand.c_str()); + } + } //pcol += printf("%-12s %-8s %-10s ", printlable.c_str(), opcode.c_str(), operand.c_str()); } if ((errorcode > 0) && (!merlinerrors)) @@ -282,11 +299,13 @@ void CLASS::print(uint32_t lineno) void CLASS::clear() { syntax = SYNTAX_MERLIN; + wholetext = ""; lable = ""; printlable = ""; opcode = ""; opcodelower = ""; operand = ""; + printoperand = ""; comment = ""; operand_expr = ""; operand_expr2 = ""; @@ -311,27 +330,47 @@ void CLASS::clear() outbytes.clear(); } +std::string operEx[] = +{ + "^(\\S*)(#?)([<>\\^|]?)([\"\'])(.*)(\\4)([\\S]*)", // catches the normal delims + "^(\\s*)([!-~])([!-~]*?)([^;]*)\\2(\\S*)", // catches the unusual delims + "^(\\s*)(\\S+)", // captures everything else + "" +}; + +std::string commentEx = "^(\\s*)((;|\\/{2}))+(.*)"; + void CLASS::set(std::string line) { int state = 0; - int l = line.length(); + int l = (int)line.length(); int i = 0; int x; char c, delim; bool isascii; - std::string opupper; - + std::string opupper, s; + std::string restofline; + std::string tline = line; clear(); + wholetext = line; isascii = false; delim = 0; - //printf("line: |%s|\n", line.c_str()); while (i < l) { - c = line[i++]; - //printf("state: %d\n",state); + c = tline[i++]; switch (state) { + case 7: + if (c >= ' ') + { + comment += c; + } + else + { + i = l; + } + break; case 0: // start of line state if ((c == ';') || (c == '*') || (c == '/')) { @@ -381,156 +420,109 @@ void CLASS::set(std::string line) { opcode += c; } -#if 1 else { - // SGQ - // this is bad, but the only way I currently know how to do this. - // the problem is, is that the ASCII generating psuedo-ops in Merlin - // use any char > space and less than apostrophe, and > apostrophe - // as delimiters. - // however, those characters also contain valid opcode expression characters - // so we see a character here, it looks like a delim, and we keep reading to EOL - // which might include a comment. All of that, then goes into the operand, and - // comments cause errors on evaluation. - // So, at this point in the code, we must determine if the opcode is one of our - // ascii psuedo-ops and treat the first char as a delim. - // otherwise, we must parse the operand as an express. - // this parser should know NOTHING about what the code does...but it needs to in - // this case. - - opupper = Poco::toUpper(opcode); - if (opupper.length() > 0) - { - if ( - (opupper == "STRL") - || (opupper == "STR") - || (opupper == "ASC") - || (opupper == "DCI") - || (opupper == "INV") - || (opupper == "FLS") - || (opupper == "REV") - ) - { - isascii = true; - } - - } - + i--; state = 4; } -#else - else - { - // SGQ - // this is bad, but the only way I currently know how to do this. - // the problem is, is that the ASCII generating psuedo-ops in Merlin - // use any char > space and less than apostrophe, and > apostrophe - // as delimiters. - // however, those characters also contain valid opcode expression characters - // so we see a character here, it looks like a delim, and we keep reading to EOL - // which might include a comment. All of that, then goes into the operand, and - // comments cause errors on evaluation. - // So, at this point in the code, we must determine if the opcode is one of our - // ascii psuedo-ops and treat the first char as a delim. - // otherwise, we must parse the operand as an express. - // this parser should know NOTHING about what the code does...but it needs to in - // this case. - - opupper = Poco::toUpper(opcode); - - auto itr = a.opcodes.find(op); - if (itr != a.opcodes.end()) - { - TSymbol s = itr->second; - if (1) - { - isascii = true; - } - } - - state = 4; - } -#endif } break; case 4: // read whitespace between opcode and operand - if (c == ';') + { + std::vector strs; + std::string s; + + Poco::RegularExpression comEx(commentEx, 0, true); + restofline = Poco::trim(tline.substr(i, tline.length())) + " "; + //printf("ROL: |%s|\n",restofline.c_str()); + + if (restofline == "") { - comment += c; - state = 7; + i = l; + break; } - else if (c > ' ') + strs.clear(); + x = 0; + try { - operand += c; - if ((c <= '/') && (isascii)) + x = comEx.split(restofline, strs, 0); + } + catch (Poco::Exception &e) + { + x = 0; + if (isDebug() > 3) { - delim = c; - state = 8; - } - else - { - state = 5; + cout << e.displayText() << endl; } } - break; - case 5: - if (c > ' ') + if (x > 0) { - if ((c == '\'') || (c == '"')) + // if the comment detector above is true, then the rest of line is comment; + operand = ""; + comment = strs[0]; + //printf("comment=%s\n", comment.c_str()); + i = l; + break; + } + + int ct = 0; + int x = 0; + bool match = false; + s = operEx[ct]; + while (s != "") + { + RegularExpression regex(s, 0, true); + strs.clear(); + x = 0; + try { - delim = c; - operand += c; - state = 8; + x = regex.split(restofline, strs, 0); } - else + catch (Poco::Exception &e) { - operand += c; + x = 0; + if (isDebug() > 3) + { + cout << e.displayText() << endl; + } } + if (x > 0) + { + //printf("%d regex %d match |%s|\n", ct, x, restofline.c_str()); + operand = strs[0]; + //printf("which=%d operand=|%s|\n",ct,operand.c_str()); + i = (int)operand.length(); + restofline = restofline.substr(i, restofline.length()); + comment = Poco::trim(restofline); + match = true; + break; + } + ct++; + s = operEx[ct]; } - else + i = l; + if (!match) { - state = 6; + // if you are here, there probably isn't an operand and/or comment after opcode } - break; - case 6: - if (c > ' ') - { - comment += c; - state = 7; - } - break; - case 7: - comment += c; - break; - case 9: - break; - case 8: - if (c < ' ') - { - } - else if (c == delim) - { - operand += c; - state = 5; - } - else - { - operand += c; - } - break; + } + break; } } printlable = lable; - x = lable.length(); + x = (int)lable.length(); if (x > 1) { - while ((x > 1) && (lable[x - 1] == ':')) + // M32 syntax allows a colon after lable, and it is not part of the lable + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) { - lable = lable.substr(0, x - 1); - x--; + while ((x > 1) && (lable[x - 1] == ':')) + { + lable = lable.substr(0, x - 1); + x--; + } + //printf("linelable: |%s|\n", lable.c_str()); } - //printf("linelable: |%s|\n", lable.c_str()); } opcodelower = Poco::toLower(opcode); @@ -541,7 +533,20 @@ void CLASS::set(std::string line) CLASS::CLASS() { + int x; errorct = 0; + + win_columns = -1; + win_rows = -1; + struct winsize w; + x = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + if (x == 0) + { + win_columns = w.ws_col; + win_rows = w.ws_row; + } + //printf("cols=%d rows=%d\n",win_columns,win_rows); + } CLASS::~CLASS() @@ -561,9 +566,24 @@ void CLASS::init(void) filenames.clear(); starttime = GetTickCount(); initialdir = Poco::Path::current(); - syntax = 0; + syntax = SYNTAX_MERLIN; filecount = 0; + s = getConfig("option.syntax", "merlin16"); + s = Poco::toUpper(Poco::trim(s)); + if ((s == "MERLIN") || (s == "MERLIN16")) + { + syntax = SYNTAX_MERLIN; + } + else if (s == "MERLIN32") + { + syntax = SYNTAX_MERLIN32; + } + else if (s == "QASM") + { + syntax = SYNTAX_QASM; + } + std::string tabstr = getConfig("reformat.tabs", "8,16,32"); tabstr = Poco::trim(tabstr); @@ -718,6 +738,7 @@ int CLASS::processfile(std::string p, std::string &newfilename) linect = 0; done = false; + p = Poco::trim(p); currentdir = Poco::Path::current(); if (filecount == 0) @@ -788,10 +809,14 @@ int CLASS::processfile(std::string p, std::string &newfilename) } if ((fn.isDirectory()) || (!fn.canRead())) { - //LOG_DEBUG << "File is a directory: " << p1 << endl; + LOG_DEBUG << "File is a directory: " << p1 << endl; valid = false; } } + else + { + printf("file does not exist |%s|\n", p1.c_str()); + } newfilename = p1; if (!valid) @@ -915,9 +940,10 @@ int CLASS::doline(int lineno, std::string line) void CLASS::process(void) { - uint32_t len, t, pos; - uint32_t ct = lines.size(); + uint32_t ct = (uint32_t)lines.size(); + + uint32_t len, t, pos; for (uint32_t lineno = 0; lineno < ct; lineno++) { @@ -1022,7 +1048,7 @@ void CLASS::pushopcode(std::string op, uint8_t opcode, uint16_t flags, TOpCallba opcodes.insert(p); } -TSymbol *CLASS::addSymbol(std::string symname, uint32_t val, bool replace) +TSymbol * CLASS::addSymbol(std::string symname, uint32_t val, bool replace) { TSymbol *res = NULL; TSymbol *fnd = NULL; @@ -1100,7 +1126,29 @@ out: return (res); } -TSymbol *CLASS::findSymbol(std::string symname) + +TMacro * CLASS::findMacro(std::string symname) +{ + TMacro *res = NULL; + + std::string sym = symname; + if (!casesen) + { + sym = Poco::toUpper(sym); + } + if (symname.length() > 0) + { + //printf("finding: %s\n",symname.c_str()); + auto itr = macros.find(sym); + if (itr != macros.end()) + { + res = &itr->second; + } + } + return (res); +} + +TSymbol * CLASS::findSymbol(std::string symname) { TSymbol *res = NULL; @@ -1143,7 +1191,7 @@ out: return (res); } -TSymbol *CLASS::addVariable(std::string symname, std::string val, bool replace) +TSymbol * CLASS::addVariable(std::string symname, std::string val, TVariable &vars, bool replace) { TSymbol *res = NULL; TSymbol *fnd = NULL; @@ -1155,7 +1203,7 @@ TSymbol *CLASS::addVariable(std::string symname, std::string val, bool replace) } //printf("addvariable\n"); - fnd = findVariable(sym); + fnd = findVariable(sym, vars); if ((fnd != NULL) && (!replace)) { @@ -1165,7 +1213,7 @@ TSymbol *CLASS::addVariable(std::string symname, std::string val, bool replace) if (fnd != NULL) { //printf("replacing symbol: %s %08X\n",sym.c_str(),val); - fnd->text = val; + fnd->var_text = val; return (fnd); } @@ -1175,25 +1223,39 @@ TSymbol *CLASS::addVariable(std::string symname, std::string val, bool replace) s.namelc = Poco::toLower(sym); s.stype = 0; s.value = 0; - s.text = val; + s.var_text = val; s.used = false; s.cb = NULL; //printf("addvariable: %s %s\n", s.name.c_str(), s.text.c_str()); std::pair p(sym, s); - variables.insert(p); - res = findVariable(sym); + vars.vars.insert(p); + res = findVariable(sym, vars); return (res); } -TSymbol *CLASS::findVariable(std::string symname) +TSymbol * CLASS::findVariable(std::string symname, TVariable &vars) { TSymbol *res = NULL; + if (!casesen) + { + symname = Poco::toUpper(symname); + } + + if ((expand_macrostack.size() > 0) && (vars.id != expand_macro.variables.id)) + { + res = findVariable(symname, expand_macro.variables); + if (res != NULL) + { + return (res); + } + } + //printf("finding: %s\n",symname.c_str()); - auto itr = variables.find(Poco::toUpper(symname)); - if (itr != variables.end()) + auto itr = vars.vars.find(symname); + if (itr != vars.vars.end()) { //printf("Found: %s 0x%08X\n",itr->second.name.c_str(),itr->second.value); res = &itr->second; @@ -1203,15 +1265,15 @@ TSymbol *CLASS::findVariable(std::string symname) return (res); } -void CLASS::showVariables(void) +void CLASS::showVariables(TVariable &vars) { - if (variables.size() > 0) + if (vars.vars.size() > 0) { printf("\nVariables:\n"); - for (auto itr = variables.begin(); itr != variables.end(); ++itr) + for (auto itr = vars.vars.begin(); itr != vars.vars.end(); ++itr) { - printf("%-16s %s\n", itr->first.c_str(), itr->second.text.c_str()); + printf("%-16s %s\n", itr->first.c_str(), itr->second.var_text.c_str()); } printf("\n"); } @@ -1270,12 +1332,62 @@ void CLASS::showSymbolTable(bool alpha) } } +// set alpha to true to print table sorted by name or +// false to print by value; +void CLASS::showMacros(bool alpha) +{ + if (macros.size() > 0) + { + std::map alphamap; + + int columns = getInt("asm.symcolumns", 3); + int column = columns; + + for (auto itr = macros.begin(); itr != macros.end(); itr++) + { + TMacro ptr = itr->second; + alphamap.insert(pair(ptr.name, 0)); + } + + if (alpha) + { + printf("\n\nmacros sorted alphabetically:\n\n"); + + for (auto itr = alphamap.begin(); itr != alphamap.end(); ++itr) + { + printf("%-16s 0x%08X ", itr->first.c_str(), itr->second); + if ( !--column ) + { + printf("\n"); + column = columns; + } + } + } + if (column > 0) + { + printf("\n"); + } + } +} + int CLASS::callOpCode(std::string op, MerlinLine &line) { int res = -1; char c; std::string s; + // 'op' is always lowercase here + +// during MACRO definition no opcodes are called (except for MAC, EOM, <<) + if (macrostack.size() > 0) + { + // if something on the macro stack, then a macro is being defined + if (!((op == "mac") || (op == "eom") || (op == "<<<"))) + { + return 0; + } + } + if (op.length() == 4) // check for 4 digit 'L' opcodes { c = op[3] & 0x7F; @@ -1308,29 +1420,64 @@ int CLASS::callOpCode(std::string op, MerlinLine &line) } } - switch (line.expr_shift) + if (line.addressmode == syn_imm) { - case '<': - line.expr_value &= 0xFF; - line.flags |= FLAG_DP; - break; - case '>': - line.expr_value >>= 8; - line.expr_value &= 0xFFFF; - break; - case '^': - line.expr_value = (line.expr_value >> 16) & 0xFFFF; - break; - case '|': - line.flags |= FLAG_FORCELONG; - break; + //printf("immediate mode\n"); + switch (line.expr_shift) + { + case '<': + //line.expr_value &= 0xFF; + break; + case '>': + line.expr_value >>= 8; + //line.expr_value &= 0xFFFF; + break; + case '^': + line.expr_value = (line.expr_value >> 16); + //line.expr_value = (line.expr_value >> 16) & 0xFFFF; + break; + case '|': + if (syntax == SYNTAX_MERLIN) + { + line.setError(errBadLabel); + line.expr_value = 0; + } + break; + } + } + else + { + switch (line.expr_shift) + { + case '<': + line.flags |= FLAG_DP; + break; + case '>': +#if 0 + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + // bug in M32 or not, do what it does + line.flags |= FLAG_FORCEABS; + } + else +#endif + { + line.flags |= FLAG_FORCELONG; + } + break; + case '|': + line.flags |= FLAG_FORCEABS; + break; + case '^': + //line.flags |= FLAG_FORCELONG; + break; + } } if (line.expr_value >= 0x100) { line.flags |= FLAG_FORCEABS; } - auto itr = opcodes.find(Poco::toUpper(op)); if (itr != opcodes.end()) { @@ -1362,13 +1509,6 @@ typedef struct // these are the regular expressions that determine the addressing mode // and extract the 'expr' part of the addr-mode -// ^([_,a-z,A-Z,0-9:\]].+)\,[s,S]{1}$ // might be a better syn_s - -// "^([:-~][0-Z_-~]*)$" // this is a valid identifier -// "^([$][0-9A-Fa-f]+)$" // hex digit -// "^([%][0-1][0-1_]+[0-1])$" - binary numbera -// "^([0-9]+)$" - decimal number -// "^([:-~][^\],()]*)$" - valid expression const TaddrMode addrRegEx[] = { { "^(?'expr'.+)\\,[s,S]{1}$", syn_s, "e,s"}, // expr,s @@ -1386,9 +1526,6 @@ const TaddrMode addrRegEx[] = {"", 0, ""} }; -// keep this next line for awhile -// {"^#{1}(?'shift'[<,>,^,|]?)(.+)$", syn_imm, "immediate"}, //#expr,#^expr,#|expr,#expr - // one or more of any character except ][,(); const std::string valExpression = "^([^\\]\\[,();]+)$"; @@ -1445,7 +1582,22 @@ void CLASS::initpass(void) merlinerrors = getBool("asm.merlinerrors", true); trackrep = getBool("asm.trackrep", false); - merlincompat = getBool("asm.merlincompatible", true); + if (syntax == SYNTAX_MERLIN32) + { + trackrep = true; // can't turn this off in M32 + } + else if (syntax == SYNTAX_MERLIN) + { + trackrep = false; // can't turn this ON in M16 + } + else if (syntax == SYNTAX_QASM) + { + // we will allow this to be settable default off + trackrep = false; + trackrep = getBool("asm.trackrep", trackrep); + + } + //merlincompat = getBool("asm.merlincompatible", true); allowdup = getBool("asm.allowduplicate", true); skiplist = false; @@ -1487,7 +1639,12 @@ void CLASS::initpass(void) lastcarry = false; relocatable = false; - currentsym = &topSymbol; // this is the default symbol for :locals without a global above; + currentsym = NULL; + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + // M32 allows locals that don't have a global above. this is the catchall for that + currentsym = &topSymbol; // this is the default symbol for :locals without a global above; + } currentsymstr = ""; lineno = 0; errorct = 0; @@ -1495,11 +1652,15 @@ void CLASS::initpass(void) dumstartaddr = 0; dumstart = 0; truncdata = 0; - variables.clear(); // clear the variables for each pass + variables.vars.clear(); // clear the variables for each pass - while (!PCstack.empty()) + while (!macrostack.empty()) { - PCstack.pop(); + macrostack.pop(); + } + while (!expand_macrostack.empty()) + { + expand_macrostack.pop(); } while (!LUPstack.empty()) { @@ -1513,6 +1674,12 @@ void CLASS::initpass(void) { LSTstack.pop(); } + while (!PCstack.empty()) + { + PCstack.pop(); + } + currentmacro.clear(); + expand_macro.clear(); curLUP.clear(); curDO.clear(); } @@ -1531,7 +1698,7 @@ void CLASS::complete(void) std::ofstream f(savepath); uint32_t lineno = 0; - uint32_t l = lines.size(); + uint32_t l = (uint32_t)lines.size(); while (lineno < l) { MerlinLine &line = lines.at(lineno++); @@ -1566,8 +1733,10 @@ void CLASS::complete(void) { showSymbolTable(true); showSymbolTable(false); - showVariables(); + showVariables(variables); + showMacros(true); } + } int CLASS::evaluate(MerlinLine &line, std::string expr, int64_t &value) @@ -1691,18 +1860,17 @@ int CLASS::getAddrMode(MerlinLine & line) // symbol is defined later, we will generate different // bytes on the next pass - if (Poco::toUpper(oper) == "A") // check the whole operand, not just the expression + if ((line.syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) { - // SGQ - // Merlin32 supports the 'A" operand for immediate - // mode for opcodes like "ROR A". Problem is, Merlin16 - // does not, and 'A' could be a lable. - TSymbol *sym = findSymbol("A"); - if (sym == NULL) + if (Poco::toUpper(oper) == "A") // check the whole operand, not just the expression { - line.flags |= FLAG_FORCEIMPLIED; - mode = syn_implied; // if the label hasn't been defined yet, assume Immediate addressing - goto out; + TSymbol *sym = findSymbol("A"); + if (sym == NULL) + { + line.flags |= FLAG_FORCEIMPLIED; + mode = syn_implied; // if the label hasn't been defined yet, assume Immediate addressing + goto out; + } } } } @@ -1768,64 +1936,129 @@ int CLASS::parseOperand(MerlinLine & line) return (res); } -int CLASS::substituteVariables(MerlinLine & line) +int CLASS::substituteVariables(MerlinLine & line, std::string &outop) { - int res = -1; + int res = 0; int x; std::string::size_type offset, slen; std::string oper = line.operand; std::string s; + std::string operin; TSymbol *sym; uint32_t len, off, ct; - slen = oper.length(); - if (slen > 0) + bool done = false; + operin = oper; + ct = 0; +restart: + while (!done) { - std::vector groups; - offset = 0; - RegularExpression varEx(varExpression, Poco::RegularExpression::RE_EXTRA, true); - Poco::RegularExpression::MatchVec mVec; - - //printf("|%s|%s|\n", varExpression.c_str(), oper.c_str()); - groups.clear(); - ct = 0; - while (offset < slen) + slen = oper.length(); + if (slen > 0) { - try - { - varEx.match(oper, offset, mVec, 0); - } - catch (...) - { - offset = slen; - } + std::vector groups; - x = mVec.size(); - if (x > 0) + offset = 0; + RegularExpression varEx(varExpression, 0, true); + Poco::RegularExpression::MatchVec mVec; + + //printf("|%s|%s|\n", varExpression.c_str(), oper.c_str()); + groups.clear(); + while (offset < slen) { - res = 0; - off = mVec[0].offset; - len = mVec[0].length; - s = oper.substr(off, len); - sym = findVariable(s); - if (sym != NULL) + try { - ct++; - if (pass > 0) - { - //printf("%d |%s|\n", ct, s.c_str()); - } + varEx.match(oper, offset, mVec, 0); + } + catch (...) + { + offset = slen; } - offset += len; - } - else - { - offset = slen; - } - } + x = (int)mVec.size(); + if (x > 0) + { + res = 0; + off = (uint32_t)mVec[0].offset; + len = (uint32_t)mVec[0].length; + s = oper.substr(off, len); + slen = s.length(); + sym = NULL; + if (expand_macrostack.size() > 0) + { + sym = findVariable(s, expand_macro.variables); + } + if (sym == NULL) + { + sym = findVariable(s, variables); + } + if (sym != NULL) + { + //printf("match |%s|\n",sym->var_text.c_str()); + + if (sym->var_text != "") + { + oper = oper.replace(off, len, sym->var_text); + slen = oper.length(); + ct++; + if (pass > 0) + { + //printf("%d |%s|\n", ct, s.c_str()); + } + goto restart; + } + } + else + { + done = true; + } + offset += len; + } + else + { + offset = slen; + done = true; + } + } + + } + else + { + done = true; + } } +//printf("inoper=|%s| outoper=|%s|\n",operin.c_str(),oper.c_str()); + if (ct > 0) + { + outop = oper; + res = ct; + } + return (res); +} + +bool CLASS::doOFF(void) +{ + bool res = curDO.doskip; + std::stack tmpstack; + TDOstruct doitem; + + uint32_t ct = (uint32_t)DOstack.size(); + if (ct > 0) + { + tmpstack = DOstack; + } + while (ct > 0) + { + doitem = tmpstack.top(); + tmpstack.pop(); + if (doitem.doskip) + { + res = true; + } + ct--; + } + //printf("DOOFF: %d\n",res); return (res); } @@ -1835,7 +2068,8 @@ bool CLASS::codeSkipped(void) bool res = false; res = (curLUP.lupskip) ? true : res; - res = (curDO.doskip) ? true : res; + res = doOFF() ? true : res; + res = currentmacro.running ? true : res; //printf("codeskip: %d\n",res); @@ -1844,22 +2078,77 @@ bool CLASS::codeSkipped(void) void CLASS::process(void) { + +#if 0 + uint32_t ct = lines.size(); + for (uint32_t lineno = 0; lineno < ct; lineno++) + { + //MerlinLine &line = lines.at(lineno); + //printf("|%s| |%s| |%s| |%s|\n", line.lable.c_str() + // , line.opcode.c_str(), line.operand.c_str(), line.comment.c_str()); + } +#else + uint32_t l; int x;; char c; char buff[256]; MerlinLine errLine; - std::string op, operand, ls; + std::string op, realop, operand, ls; pass = 0; while (pass < 2) { initpass(); - l = lines.size(); - while ((lineno < l) && (!passcomplete)) + l = (uint32_t)lines.size(); + bool passdone = false; + while ((!passdone) && (!passcomplete)) { - MerlinLine &line = lines[lineno]; + + MerlinLine *ml = NULL; + bool srcline = true; + if (expand_macro.running) + { + srcline = false; + if (expand_macro.currentline >= expand_macro.len) + { + // macro is complete + lineno = expand_macro.sourceline + 1; + if (expand_macrostack.size() > 0) + { + expand_macro = expand_macrostack.top(); + expand_macrostack.pop(); + } + else + { + expand_macro.clear(); + } + srcline = true; + } + else + { + ml = &expand_macro.lines[expand_macro.currentline]; + lineno = expand_macro.sourceline; + expand_macro.currentline++; + } + } + if (srcline) + { + if (lineno >= l) + { + passdone = true; + goto passout; + } + else + { + ml = &lines[lineno]; + } + } + + MerlinLine &line = *ml; + + //printf("lineno=%u %s\n", lineno, line.wholetext.c_str()); line.eval_result = 0; line.lineno = lineno + 1; @@ -1868,14 +2157,16 @@ void CLASS::process(void) //printf("lineno: %d %d |%s|\n",lineno,l,line.operand.c_str()); op = Poco::toLower(line.opcode); + realop = line.opcode; operand = Poco::toLower(line.operand); line.startpc = PC.currentpc; line.linemx = mx; line.bytect = 0; line.showmx = showmx; + line.syntax = syntax; line.merlinerrors = merlinerrors; - if ((line.lable != "")) + if ((line.lable != "") && (op != "mac")) { std::string lable = Poco::trim(line.lable); TSymbol *sym = NULL; @@ -1886,8 +2177,8 @@ void CLASS::process(void) case ']': sprintf(buff, "$%X", PC.currentpc); ls = buff; - sym = addVariable(line.lable, ls, true); - if (sym == NULL) { dupsym = true; } + sym = addVariable(line.lable, ls, variables, true); + //if (sym == NULL) { dupsym = true; } break; case ':': @@ -1913,7 +2204,19 @@ void CLASS::process(void) line.setError(errDupSymbol); } } - x = substituteVariables(line); + std::string outop; + line.printoperand = line.operand; + + x = 0; + if (macrostack.size() == 0) + { + x = substituteVariables(line, outop); + } + if (x > 0) + { + line.printoperand = outop; + line.operand = outop; + } x = parseOperand(line); if (x >= 0) { @@ -1937,7 +2240,88 @@ void CLASS::process(void) x = 0; if (op.length() > 0) { - x = callOpCode(op, line); + bool skipop = false; + if (doOFF()) + { + skipop = true; + if ((op == "fin") || (op == "else") || (op == "do") || (op == "if")) + { + skipop = false; + } + } + if (!skipop) + { + TMacro *mac = NULL; + bool inoperand = false; + if (macrostack.size() == 0) + { + mac = findMacro(realop); + if (mac == NULL) + { + if (op == ">>>") // specal merlin way of calling a macro + { + Poco::StringTokenizer tok(operand, ", ", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); + std::string s = ""; + if (tok.count() > 0) + { + s = tok[0]; + } + mac = findMacro(s); + inoperand = true; + } + } + } + if (mac == NULL) + { + x = callOpCode(op, line); + } + if (mac != NULL) + { + expand_macrostack.push(expand_macro); + expand_macro = *mac; + + expand_macro.lines.clear(); + //printf("mac start=%u end=%u\n", expand_macro.start, expand_macro.end); + for (uint32_t lc = expand_macro.start; lc < expand_macro.end; lc++) + { + //printf("pushing %s\n", lines[lc].wholetext.c_str()); + MerlinLine nl(lines[lc].wholetext); // create a new clean line (without errors,data) + expand_macro.lines.push_back(nl); + } + expand_macro.running = true; + expand_macro.sourceline = lineno; + expand_macro.variables.vars.clear(); + // set the variables for the macro here SGQ + + std::string parms = line.operand; + if (inoperand) + { + Poco::StringTokenizer tok(parms, ", ", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); + parms = ""; + if (tok.count() > 1) + { + parms = tok[1]; + } + } + Poco::StringTokenizer tok(parms, ",;", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); + + uint32_t ct = 0; + for (auto itr = tok.begin(); itr != tok.end(); ++itr) + { + //evaluate each of these strings, check for errors on pass 2 + std::string expr = *itr; + std::string v = "]" + Poco::NumberFormatter::format(ct + 1); + //printf("var: %s %s\n", v.c_str(), expr.c_str()); + addVariable(v, expr, expand_macro.variables, true); + ct++; + } + x = 0; + expand_macro.currentline = 0; + } + } } if ((x > 0) && (codeSkipped())) // has a psuedo-op turned off code generation? (LUP, IF, etc) @@ -1986,7 +2370,10 @@ void CLASS::process(void) { if ((line.pass0bytect != line.bytect) && (line.errorcode == 0)) { - line.setError(errBadByteCount); + if (expand_macrostack.size() == 0) // if macro expanding, you can't make this check + { + line.setError(errBadByteCount); + } } if (line.errorcode != 0) @@ -2001,7 +2388,7 @@ void CLASS::process(void) } lineno++; } - +passout: // end of file reached here, do some final checks #if 0 @@ -2015,7 +2402,7 @@ void CLASS::process(void) #endif pass++; } - +#endif } int CLASS::doline(int lineno, std::string line) @@ -2044,6 +2431,7 @@ int CLASS::doline(int lineno, std::string line) { std::string fn; x = processfile(l.operand, fn); + //printf("processfile : %d\n",x); if (x < 0) { switch (x) diff --git a/asm.h b/asm.h index cae8a1e..9f47d2e 100644 --- a/asm.h +++ b/asm.h @@ -8,8 +8,18 @@ #define MODE_65816 2 #define SYNTAX_MERLIN 0 -#define SYNTAX_APW 1 -#define SYNTAX_ORCA 2 +#define SYNTAX_MERLIN32 0x01 +#define SYNTAX_APW 0x02 +#define SYNTAX_ORCA 0x04 +#define SYNTAX_QASM (0x08 | SYNTAX_MERLIN32) +#define OPTION_ALLOW_A_OPERAND 0x0100 +#define OPTION_ALLOW_LOCAL 0x0200 +#define OPTION_ALLOW_COLON 0x0400 +#define OPTION_FORCE_REPSEP 0x0800 +#define OPTION_NO_REPSEP 0x1000 +#define OPTION_CFG_REPSEP 0x2000 +#define OPTION_M32_VARS 0x4000 + #define FLAG_FORCELONG 0x01 #define FLAG_FORCEABS 0x02 @@ -68,6 +78,7 @@ enum asmErrors errBadLUPOperand, errBadLabel, errBadOperand, + errErrOpcode, errMAX }; @@ -102,6 +113,7 @@ const std::string errStrings[errMAX + 1] = "LUP value must be 0 < VAL <= $8000", "Unknown label", "Bad operand", + "Break", "" }; @@ -132,6 +144,7 @@ enum syn_MAX }; + class TOriginSection { // SGQ - if you do something unusual here, be aware of copy constructor @@ -177,9 +190,11 @@ class MerlinLine { public: - uint8_t syntax; + uint32_t syntax; + std::string wholetext; std::string lable; std::string printlable; + std::string printoperand; std::string opcode; std::string opcodelower; std::string operand; @@ -222,9 +237,11 @@ public: class TFileProcessor { protected: + int win_columns; + int win_rows; std::string initialdir; std::vector filenames; - uint8_t syntax; + uint32_t syntax; uint64_t starttime; uint8_t tabs[16]; @@ -265,11 +282,12 @@ public: { clear(); } - void clear(void) { - lupct=0; - lupoffset=0; - luprunning=0; - lupskip=false; + void clear(void) + { + lupct = 0; + lupoffset = 0; + luprunning = 0; + lupskip = false; } uint16_t lupct; bool lupskip; @@ -284,9 +302,10 @@ public: { clear(); } - void clear(void) { - doskip=false; - value=0; + void clear(void) + { + doskip = false; + value = 0; } uint32_t value; bool doskip; @@ -301,7 +320,8 @@ class TSymbol public: std::string namelc; std::string name; - std::string text; + //std::string text; + std::string var_text; uint32_t value; uint16_t stype; uint8_t opcode; @@ -317,7 +337,8 @@ public: { value = 0; used = false; - text = ""; + //text = ""; + var_text = ""; name = ""; namelc = ""; stype = 0; @@ -326,6 +347,50 @@ public: } }; +//typedef Poco::HashMap variable_t; + +class TVariable +{ +public: + uint32_t id; + Poco::HashMap vars; + TVariable() + { + // SGQ - must fix this so it is guaranteed unique for each one + id=rand(); + } +}; + +class TMacro +{ +public: + std::string name; + std::string lcname; + TVariable variables; + std::vector lines; + uint32_t start, end, currentline, len; + uint32_t sourceline; + bool running; + + TMacro() + { + clear(); + } + void clear(void) + { + name = ""; + lcname = ""; + variables.vars.clear(); + lines.clear(); + sourceline = 0; + currentline = 0; + len = 0; + start = 0; + end = 0; + running = false; + } +}; + class TPsuedoOp; class T65816Asm : public TFileProcessor @@ -335,7 +400,6 @@ public: bool casesen; bool showmx; bool trackrep; - bool merlincompat; bool merlinerrors; bool allowdup; uint8_t mx; @@ -355,14 +419,16 @@ public: std::string currentsymstr; std::vector lines; - Poco::HashMapopcodes; - Poco::HashMap macros; + Poco::HashMap macros; + Poco::HashMap opcodes; Poco::HashMap symbols; - Poco::HashMap variables; + TVariable variables; TOriginSection PC; TLUPstruct curLUP; TDOstruct curDO; + TMacro currentmacro; + TMacro expand_macro; bool listing; uint8_t truncdata; // for the TR opcode @@ -370,6 +436,8 @@ public: std::stack LUPstack; std::stack DOstack; std::stack LSTstack; + std::stack macrostack; + std::stack expand_macrostack; TPsuedoOp *psuedoops; @@ -387,19 +455,26 @@ public: void pushopcode(std::string op, uint8_t opcode, uint16_t flags, TOpCallback cb); int callOpCode(std::string op, MerlinLine &line); + TMacro *findMacro(std::string sym); + TSymbol *findSymbol(std::string sym); TSymbol *addSymbol(std::string sym, uint32_t val, bool replace); - TSymbol *findVariable(std::string sym); - TSymbol *addVariable(std::string sym, std::string val, bool replace); + TSymbol *findVariable(std::string sym, TVariable &vars); + TSymbol *addVariable(std::string sym, std::string val, TVariable &vars, bool replace); void initpass(void); void showSymbolTable(bool alpha); - void showVariables(void); + void showMacros(bool alpha); + + void showVariables(TVariable &vars); int evaluate(MerlinLine &line, std::string expr, int64_t &value); - int substituteVariables(MerlinLine & line); + int substituteVariables(MerlinLine & line, std::string &outop); + bool codeSkipped(void); + bool doOFF(void); + int parseOperand(MerlinLine &line); int getAddrMode(MerlinLine &line); diff --git a/config.h b/config.h index f1f9a01..7d8623b 100644 --- a/config.h +++ b/config.h @@ -14,6 +14,7 @@ //#define USE_JSON //#define USE_XML +#define NO_TTY_SETUP // help text #define HELP_USAGE " " #define HELP_PURPOSE "\nMerlin 16+ Compatible 65816 Development Tool" diff --git a/eval.cpp b/eval.cpp index ff7be25..086e379 100644 --- a/eval.cpp +++ b/eval.cpp @@ -15,6 +15,7 @@ std::ostream& operator<<(std::ostream& os, const Token& token) CLASS::CLASS(T65816Asm &_asm) : assembler(_asm) { + allowMX = false; } CLASS::~CLASS() @@ -232,20 +233,30 @@ std::deque CLASS::shuntingYard(const std::deque& tokens) } else { - sym = assembler.findSymbol(token.str); - //printf("symbol find |%s| %p\n",token.str.c_str(),sym); - - if (sym != NULL) + std::string tok = Poco::toUpper(token.str); + if ((tok == "MX") && (allowMX)) { - sym->used = true; - sprintf(buff, "$%X", sym->value); - token.str = buff; + //printf("MX EVAL\n"); + sprintf(buff,"$%02X",assembler.mx&0x03); + token.str=buff;; } else { - setError(Token::unknownSymbolErr); - badsymbol = token.str; - token.str = "0"; + sym = assembler.findSymbol(token.str); + //printf("symbol find |%s| %p\n",token.str.c_str(),sym); + + if (sym != NULL) + { + sym->used = true; + sprintf(buff, "$%X", sym->value); + token.str = buff; + } + else + { + setError(Token::unknownSymbolErr); + badsymbol = token.str; + token.str = "0"; + } } } queue.push_back(token); @@ -380,7 +391,7 @@ int CLASS::parseAscii(std::string n, int64_t &val) bool high = false; uint8_t c; - uint32_t l = n.length(); + uint32_t l = (uint32_t)n.length(); for (uint32_t i = 0; i < l - 1; i++) { c = n[i]; @@ -431,7 +442,7 @@ int CLASS::parseNumber(std::string n, int64_t &val) //printf("parseNumber |%s|\n",n.c_str()); i = 0; - l = n.length(); + l = (uint32_t)n.length(); s = ""; for (i = 0; i < l; i++) { @@ -665,7 +676,7 @@ int CLASS::evaluate(std::string & e, int64_t &res, uint8_t &_shiftmode) } else if (token.str == "<") { - //rhs = (rhs << 8 ) & 0xFFFF; + //rhs = (rhs) & 0xFFFF; } else if (token.str == ">") { diff --git a/eval.h b/eval.h index 73b6c54..f0ed9a2 100644 --- a/eval.h +++ b/eval.h @@ -68,6 +68,7 @@ protected: public: CLASS(T65816Asm &_asm); ~CLASS(); + bool allowMX; std::string badsymbol; std::deque shuntingYard(const std::deque& tokens); std::deque exprToTokens(const std::string& expr); diff --git a/merlintests.sh b/merlintests.sh new file mode 100755 index 0000000..ad97e50 --- /dev/null +++ b/merlintests.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +X= +if [ "$1""L" != "L" ] ; then + X=_$1 +fi + +OUTDIR=./m32out + +rm -rf $OUTDIR +mkdir -p $OUTDIR + +SRC=`ls ./testdata | grep -E '^([0-9]+)(.*)\.[Ss]'` + +#SRC=`ls ./testdata | grep -E '^([0-9]+)(.*)\.[Ss]' | grep -i 2007` + +for S in $SRC ; do + + S1=$S + S1=${S1/.S/} + S1=${S1/.s/} + + BASE=${S/.S/} + BASE=${BASE/.s/} + cd ./testdata + merlin32$X . $S 2>/dev/null >/dev/null + #merlin32 . $S 2>/dev/null + + R=?$ + cd - >/dev/null + cp ./testdata/$S1 $OUTDIR/$S1.bin 2>/dev/null + rm -f ./testdata/*.txt 2>/dev/null + rm -f ./testdata/$S1 2>/dev/null + R=?$ + +done +ls $OUTDIR + + + diff --git a/opcodes.cpp b/opcodes.cpp index 58afdbd..3ec3c6a 100644 --- a/opcodes.cpp +++ b/opcodes.cpp @@ -94,7 +94,16 @@ int CLASS::doEQU(MerlinLine &line, TSymbol &sym) else if (isvar) { res = -1; - s = addVariable(line.lable, line.operand, true); + +#if 1 + char buff[32]; + sprintf(buff, "$%08X", line.expr_value); + std::string s1 = buff; + s = addVariable(line.lable, s1, variables,true); +#else + // do this if you want to do this more as a #define + s = addVariable(line.lable, line.operand, variables,true); +#endif if (s != NULL) { res = 0; @@ -185,9 +194,14 @@ int CLASS::doMVN(MerlinLine &line, TSymbol &sym) //line.errorText = line.operand_expr2; } + uint32_t v=(value & 0xFFFFFFFF); + //printf("val1 %08X\n",v); + //printf("val1 %08X\n",line.expr_value); + setOpcode(line, op); - line.outbytes.push_back(value & 0xFF); - line.outbytes.push_back(line.expr_value & 0xFF); + // these bytes are the two bank registers + line.outbytes.push_back((v>>16) & 0xFF); + line.outbytes.push_back((line.expr_value>>16) & 0xFF); line.outbytect = res; } @@ -284,6 +298,19 @@ int CLASS::doAddress(MerlinLine &line, TSymbol &sym) res = 1 + sym.stype; if (pass > 0) { + switch(line.expr_shift) + { + case '^': + line.expr_value=(line.expr_value>>16)&0xFFFF; + break; + case '<': + line.expr_value=(line.expr_value)&0xFF; + break; + case '>': + line.expr_value=(line.expr_value>>8)&0xFFFF; + break; + } + //line.setError(errIncomplete); setOpcode(line, sym.opcode); for (i = 0; i < (res - 1); i++) @@ -759,13 +786,13 @@ int CLASS::doBYTE(MerlinLine & line, TSymbol & sym) { lastcarry = false; } - else if (sym.opcode==0xFB) // XCE + else if (sym.opcode == 0xFB) // XCE { if (trackrep) { if (lastcarry) { - mx=0x03; + mx = 0x03; } } } @@ -819,6 +846,7 @@ void CLASS::insertOpcodes(void) pushopcode("DUM", P_DUM, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("DEND", P_DEND, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("AST", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode("CAS", P_CAS, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("CYC", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("DAT", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("EXP", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); @@ -849,7 +877,7 @@ void CLASS::insertOpcodes(void) pushopcode("IF", P_DO, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("FIN", P_DO, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("CHK", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); - pushopcode("ERR", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode("ERR", P_ERR, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("KBD", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("LUP", P_LUP, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("--^", P_LUP, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); @@ -858,9 +886,10 @@ void CLASS::insertOpcodes(void) pushopcode("SW", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("USR", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("XC", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doXC)); - pushopcode("MAC", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); - pushopcode("EOM", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); - pushopcode("<<<", 0x00, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode("MAC", P_MAC, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode("EOM", P_MAC, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode("<<<", P_MAC, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); + pushopcode(">>>", P_MAC, OP_PSUEDO, OPHANDLER(&CLASS::doPSEUDO)); pushopcode("ADC", 0x03, OP_STD | OP_A, OPHANDLER(&CLASS::doBase6502)); diff --git a/psuedo.cpp b/psuedo.cpp index 6d9f163..36b512e 100644 --- a/psuedo.cpp +++ b/psuedo.cpp @@ -18,15 +18,15 @@ uint32_t CLASS::doShift(uint32_t value, uint8_t shift) { if (shift == '<') { - value = (value) & 0xFF; + value = (value) & 0xFFFFFF; } if (shift == '>') { - value = (value >> 8) & 0xFF; + value = (value >> 8) & 0xFFFFFF; } else if ((shift == '^') || (shift == '|')) { - value = (value >> 16) & 0xFF; + value = (value >> 16) & 0xFFFFFF; } return (value); } @@ -36,6 +36,7 @@ int CLASS::doDO(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) UNUSED(opinfo); TEvaluator eval(a); + eval.allowMX = true; // allow the built in MX symbol int64_t eval_value = 0; uint8_t shift; @@ -130,6 +131,88 @@ out: return (res); } +int CLASS::doMAC(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) +{ + UNUSED(opinfo); + + int res = 0; + int err = 0; + + std::string op = Poco::toUpper(line.opcode); + if (op == "MAC") + { + if (a.expand_macrostack.size() > 0) + { + line.flags |= FLAG_NOLINEPRINT; + goto out; + } + if (line.lable.length() == 0) + { + err = errBadLabel; + goto out; + } + a.macrostack.push(a.currentmacro); + a.currentmacro.clear(); + + a.currentmacro.name = line.lable; + a.currentmacro.lcname = Poco::toLower(line.lable); + a.currentmacro.start = line.lineno; + a.currentmacro.running = true; + + if (!a.casesen) + { + a.currentmacro.name = Poco::toUpper(a.currentmacro.name); + } + + if (a.pass == 0) + { + } + else + { + // don't need to do anything on pass > 0 + } + //printf("macro stack size=%zu\n",a.macrostack.size()); + } + else if (op == ">>>") + { + // don't do anything here, let the macro call handler stuff do ths (asm.cpp) + } + else // it is EOM or <<< + { + while (a.macrostack.size() > 0) + { + a.currentmacro.end = line.lineno - 1; + a.currentmacro.len = 0; + if (a.currentmacro.end >= a.currentmacro.start) + { + a.currentmacro.len = a.currentmacro.end - a.currentmacro.start; + //printf("macro len=%d\n",a.currentmacro.len); + } + a.currentmacro.running = false; + + std::pair p(a.currentmacro.name, a.currentmacro); + //printf("macro insert %s\n",a.currentmacro.name.c_str()); + a.macros.insert(p); + + a.currentmacro = a.macrostack.top(); + a.macrostack.pop(); + } +#if 0 + else + { + err = errUnexpectedOp; + goto out; + } +#endif + } +out: + if (err) + { + line.setError(err); + } + return (res); +} + int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); @@ -157,7 +240,14 @@ int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) a.LUPstack.push(a.curLUP); - a.curLUP.lupoffset = len; + if (a.expand_macrostack.size() > 0) + { + a.curLUP.lupoffset = a.expand_macro.currentline; + } + else + { + a.curLUP.lupoffset = len; + } a.curLUP.lupct = eval_value & 0xFFFF; // evaluate here a.curLUP.luprunning++; @@ -180,6 +270,8 @@ int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) if (a.curLUP.luprunning > 0) { + + lidx = line.lineno - 1; len = lidx - a.curLUP.lupoffset - 1; @@ -188,7 +280,14 @@ int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) a.curLUP.lupct--; if (a.curLUP.lupct != 0) { - a.lineno = a.curLUP.lupoffset; + if (a.expand_macrostack.size() > 0) + { + a.expand_macro.currentline = a.curLUP.lupoffset; + } + else + { + a.lineno = a.curLUP.lupoffset; + } goto out; } } @@ -215,7 +314,6 @@ int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) //err = errUnexpectedOp; } } - out: if (err > 0) { @@ -340,6 +438,8 @@ int CLASS::doDATA(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) return (outct); } + + int CLASS::doDS(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); @@ -352,7 +452,7 @@ int CLASS::doDS(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) uint8_t shift; line.eval_result = 0; // since this is an data p-op, clear the global 'bad operand' flag - line.flags|=FLAG_FORCEADDRPRINT; + line.flags |= FLAG_FORCEADDRPRINT; std::string s; Poco::StringTokenizer tok(line.operand, ",", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY); @@ -418,11 +518,11 @@ int CLASS::doDS(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) v = datact; if (pagefill) { - v=line.startpc&0xFF; - v=0x100-v; + v = line.startpc & 0xFF; + v = 0x100 - v; } line.datafillct = (uint16_t)v & 0xFFFF; - res=line.datafillct; + res = line.datafillct; out: //printf("res=%d %04X\n",res,res); @@ -810,6 +910,7 @@ int CLASS::doASC(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) int CLASS::ProcessOpcode(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { int res = 0; + std::string s; switch (opinfo.opcode) { @@ -843,11 +944,49 @@ int CLASS::ProcessOpcode(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) a.PC.currentpc = a.PC.orgsave; line.startpc = a.PC.orgsave; } + +#if 0 + // Merlin32 seems to have a bug where ORG seems like it can only be 16 bits + if ((line.syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + // so clear the bank word in all variables + a.PC.orgsave &= 0xFFFF; + a.PC.currentpc &= 0xFFFF; + line.startpc &= 0xFFFF; + } +#endif + line.flags |= FLAG_FORCEADDRPRINT; break; case P_SAV: a.savepath = a.processFilename(line.operand, Poco::Path::current(), 0); break; + case P_CAS: + s = Poco::toUpper(line.operand); + if (s == "SE") + { + a.casesen = true; + } + if (s=="IN") + { + a.casesen=false; + } + res = 0; + break; + case P_MAC: + res = doMAC(a, line, opinfo); + break; + case P_ERR: + if (a.pass > 0) + { + if ((line.expr_value != 0) || (line.eval_result < 0)) + { + line.setError(errErrOpcode); + //a.passcomplete=true; // terminate assembly + } + } + res = 0; + break; case P_LST: res = doLST(a, line, opinfo); break; diff --git a/psuedo.h b/psuedo.h index 1270e00..809cb54 100644 --- a/psuedo.h +++ b/psuedo.h @@ -19,6 +19,9 @@ enum P_DO, P_TR, P_ASC, + P_ERR, + P_MAC, + P_CAS, P_MAX }; @@ -40,6 +43,8 @@ public: int doDO(T65816Asm &a, MerlinLine &line, TSymbol &opinfo); int doTR(T65816Asm &a, MerlinLine &line, TSymbol &opinfo); int doASC(T65816Asm &a, MerlinLine &line, TSymbol &opinfo); + int doMAC(T65816Asm &a, MerlinLine &line, TSymbol &opinfo); + }; #undef CLASS diff --git a/qasm.cpp b/qasm.cpp index 8c06b3f..c065ecd 100644 --- a/qasm.cpp +++ b/qasm.cpp @@ -18,9 +18,11 @@ programOption PAL::appOptions[] = #ifdef DEBUG { "debug", "d", "enable debug info (repeat for more verbosity)", "", false, true}, #endif - //{ "config", "f", "load configuration data from a ", " ", false, false}, - { "exec", "x", "execute a command [asm, link, reformat] default=asm", " ", false, false}, - { "objfile", "o", "write output to file", " ", false, false}, + //{ "config", "f", "load configuration data from a ", "", false, false}, + { "exec", "x", "execute a command [asm, link, reformat] default=asm", "", false, false}, + { "objfile", "o", "write output to file", "", false, false}, + { "syntax", "s", "enforce syntax of other assembler [merlin16, merlin32]", "", false, false}, + { "", "", "", "", false, false} }; diff --git a/qasm.ini b/qasm.ini index 2aad0ca..5cd67fa 100644 --- a/qasm.ini +++ b/qasm.ini @@ -4,9 +4,10 @@ logdir=/var/log/mylog logfile=mylog.log [option] -debug=1 +;==debug must be an integer. Code can use this as a level +;debug=0 nocolor=false -;debug must be an integer. Code can use this as a level +;syntax=merlin32 [application] timezone=America/Los_Angeles @@ -28,13 +29,11 @@ lst=true cpu=M65816 trackrep=true allowduplicate=true -merlinerrors=false -merlincompatible=true +merlinerrors=true symcolumns=3 [reformat] -tabs=12; 18; 30 -;tabs=0;0;0 +tabs=12;18;30 diff --git a/runtests.sh b/runtests.sh index 0c22973..fdc5cd0 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,6 +1,6 @@ #!/bin/bash -OUTDIR=./testout +OUTDIR=./qasmout TMPFILE=/tmp/qasm_out.txt rm -f $TMPFILE @@ -27,7 +27,9 @@ for S in $SRC ; do BASE=${S/.S/} BASE=${BASE/.s/} - ./qasm -o 0/$OUTDIR/$S1 ./testdata/$S >> $TMPFILE + #./qasm -o 0/$OUTDIR/$S1 ./testdata/$S + + ./qasm --syntax merlin32 -o 0/$OUTDIR/$S1 ./testdata/$S >> $TMPFILE R=?$ #echo $S " " $S1 @@ -38,14 +40,15 @@ for S in $SRC ; do MSHA="Q" QSHA="M" - if [ -f ./testdata/M32_expected/$BASE ] ; then - MSHA=`sha256sum ./testdata/M32_expected/$BASE | awk '{ print $1;}'` 2>/dev/null >/dev/null + + if [ -f ./m32out/$BASE.bin ] ; then + MSHA=`sha256sum ./m32out/$BASE.bin | awk '{ print $1;}'` 2>/dev/null >/dev/null fi if [ -f $OUTDIR/$BASE.bin ] ; then QSHA=`sha256sum $OUTDIR/$BASE.bin |awk '{print $1;}'` 2>/dev/null >/dev/null fi - #echo "$MSHA $QSHA" + #echo "MSHA=$MSHA QSHA=$QSHA" shapass=0; CX=" " diff --git a/testdata/1000-allops-value-65816.S b/testdata/1000-allops-value-65816.S index 2439293..dc67504 100644 --- a/testdata/1000-allops-value-65816.S +++ b/testdata/1000-allops-value-65816.S @@ -5,7 +5,7 @@ ZP EQU $FF ABS EQU $FEFF LONG EQU $FDFEFF -MV0 EQU ZP ;Merlin 32 bug -- must use 8-bit constant, or -MV1 EQU ZP-1 ; '^' modifier is implicitly applied +MV0 EQU LONG ;Merlin 32 bug -- must use 8-bit constant, or +MV1 EQU LONG-$10000 ; '^' modifier is implicitly applied PUT allops-common-65816.S diff --git a/testdata/2007-labels-and-symbols.S b/testdata/2007-labels-and-symbols.S index e5400cf..aca00e4 100644 --- a/testdata/2007-labels-and-symbols.S +++ b/testdata/2007-labels-and-symbols.S @@ -219,13 +219,13 @@ next :skiphex ; extract bytes from 32-bit value with short regs - lda #thirty2 + 768 + lda #thirty2+768 lda #^thirty2 rep #$30 mx %00 - lda #thirty2 + 1024 + lda #thirty2+1024 lda #^thirty2 rts diff --git a/testdata/3000-addresses.S b/testdata/3000-addresses.S new file mode 100644 index 0000000..d99592b --- /dev/null +++ b/testdata/3000-addresses.S @@ -0,0 +1,28 @@ + lst + xc + xc + org $018200 + +bank02 equ $020000 +bank03 equ $030000 +dp equ $A5 +long equ $020304 + + mx %00 +start nop + pea ^start + pea start + mvn bank02,bank03 + mvp bank03,bank02 + lda dp + lda dp + lda ^dp + lda |dp + lda #long + lda #long + lda #^long + lda #long + + lst off diff --git a/src/main.s b/testdata/3001-lroathe.S similarity index 98% rename from src/main.s rename to testdata/3001-lroathe.S index fe7a468..bfd0932 100644 --- a/src/main.s +++ b/testdata/3001-lroathe.S @@ -114,7 +114,7 @@ START adc _num1+dum1 sbc _num1+dum1 bit _num1+dum0 - sta _num1+dum0 ;(FIXED): can't use sta _num1+dum0 + sta _num1+dum0 ;(FIXED): can't use sta _num1+dum0 ' stz _num1+dum0 lda _num1+dum0,x @@ -180,7 +180,7 @@ myQuit ldaz $FFFF ; forced DP - lda: $FFFF ; forced ABS (any char but 'L', 'D', and 'Z" + lda: $FFFF ; forced ABS (any char but 'L', 'D', and 'Z' ldal $FFFF ; forced long abs (3 byte address) ldaz $05 @@ -307,7 +307,7 @@ myQuit sbc ^$012345 sbc |$012345 - asll $1234 + ;asll $1234 lda <$fff0+24 ;zp lda >$fff0+24 ;ABS (lo word) @@ -338,8 +338,6 @@ myQuit lda #^$A51234 ;bank - mx MX - lda $0008 ;ZP lda $08 ;ZP lda $ffff-$fff7 ;ZP @@ -362,8 +360,8 @@ L00BC bit L00BC hex ;no error hex 11,22,33,44,55,66,77,88,99 - hex 112233445566778899F - hex 112233445I566778899FF + ;hex 112233445566778899F + ;hex 112233445I566778899FF hex aabb,CC,0123456789abcdefABCDEF,ff @@ -411,8 +409,8 @@ L00BC bit L00BC asc 02,15,?123456? asc 02,15,>123456> asc 02,15,<123456< - asc 02,15,5,"123456" - asc 02,15,"123456 + asc 02,15,05,"123456" + asc 02,15,"123456" asc 0215"1234"1502 dci 8D,'Hello',8D,'there',8D @@ -425,7 +423,7 @@ lup_start: --^ - lst off + ;lst off //]XCODEEND ; Keep this at the end and put your code above this ;lst off diff --git a/src/testfile.s b/testdata/3002-testfile.S similarity index 99% rename from src/testfile.s rename to testdata/3002-testfile.S index c120a8d..725dbbf 100644 --- a/src/testfile.s +++ b/testdata/3002-testfile.S @@ -14,7 +14,7 @@ lexpr = $010203 immed = $123456 neg equ -16 -]var1 = v1234 +*]var1 = v1234 ;lst off start00 @@ -308,5 +308,5 @@ startF0 inc expr,x sbcl lexpr,x lst off - sav ./test.bin + ;sav ./test.bin diff --git a/src/var.s b/testdata/3003-var.S similarity index 76% rename from src/var.s rename to testdata/3003-var.S index b54c676..7702c95 100644 --- a/src/var.s +++ b/testdata/3003-var.S @@ -13,13 +13,13 @@ start nop ldy #$00 ]loop sta $800,y dey - bne ]loop;]loop2 - bne ]myvar; + ;dw ]loop;]loop2 + ;bne ]myvar; bcs ]loop bpl ]loop rts - use var + ;use var lst on diff --git a/testdata/M32_expected/1000-allops-value-65816 b/testdata/M32_expected/1000-allops-value-65816 deleted file mode 100644 index 204a57b..0000000 Binary files a/testdata/M32_expected/1000-allops-value-65816 and /dev/null differ diff --git a/testdata/M32_expected/1001-allops-zero-65816 b/testdata/M32_expected/1001-allops-zero-65816 deleted file mode 100644 index a885f04..0000000 Binary files a/testdata/M32_expected/1001-allops-zero-65816 and /dev/null differ diff --git a/testdata/M32_expected/1002-embedded-instructions b/testdata/M32_expected/1002-embedded-instructions deleted file mode 100644 index cffff3c..0000000 Binary files a/testdata/M32_expected/1002-embedded-instructions and /dev/null differ diff --git a/testdata/M32_expected/1003-flags-and-branches b/testdata/M32_expected/1003-flags-and-branches deleted file mode 100644 index 34e3a42..0000000 Binary files a/testdata/M32_expected/1003-flags-and-branches and /dev/null differ diff --git a/testdata/M32_expected/1004-data-recognition b/testdata/M32_expected/1004-data-recognition deleted file mode 100644 index 7687e62..0000000 Binary files a/testdata/M32_expected/1004-data-recognition and /dev/null differ diff --git a/testdata/M32_expected/2000-allops-value-6502 b/testdata/M32_expected/2000-allops-value-6502 deleted file mode 100644 index 52f4f7a..0000000 Binary files a/testdata/M32_expected/2000-allops-value-6502 and /dev/null differ diff --git a/testdata/M32_expected/2001-allops-zero-6502 b/testdata/M32_expected/2001-allops-zero-6502 deleted file mode 100644 index 4e99391..0000000 Binary files a/testdata/M32_expected/2001-allops-zero-6502 and /dev/null differ diff --git a/testdata/M32_expected/2002-allops-value-65C02 b/testdata/M32_expected/2002-allops-value-65C02 deleted file mode 100644 index 264d1f6..0000000 Binary files a/testdata/M32_expected/2002-allops-value-65C02 and /dev/null differ diff --git a/testdata/M32_expected/2003-allops-zero-65C02 b/testdata/M32_expected/2003-allops-zero-65C02 deleted file mode 100644 index 094abe6..0000000 Binary files a/testdata/M32_expected/2003-allops-zero-65C02 and /dev/null differ diff --git a/testdata/M32_expected/2004-numeric-types b/testdata/M32_expected/2004-numeric-types deleted file mode 100644 index 47e2524..0000000 Binary files a/testdata/M32_expected/2004-numeric-types and /dev/null differ diff --git a/testdata/M32_expected/2005-string-types b/testdata/M32_expected/2005-string-types deleted file mode 100644 index df57058..0000000 Binary files a/testdata/M32_expected/2005-string-types and /dev/null differ diff --git a/testdata/M32_expected/2006-operand-formats b/testdata/M32_expected/2006-operand-formats deleted file mode 100644 index ff2319e..0000000 Binary files a/testdata/M32_expected/2006-operand-formats and /dev/null differ diff --git a/testdata/M32_expected/2007-labels-and-symbols b/testdata/M32_expected/2007-labels-and-symbols deleted file mode 100644 index 55ff1f3..0000000 Binary files a/testdata/M32_expected/2007-labels-and-symbols and /dev/null differ diff --git a/testdata/M32_expected/2008-address-changes b/testdata/M32_expected/2008-address-changes deleted file mode 100644 index cfec9c0..0000000 Binary files a/testdata/M32_expected/2008-address-changes and /dev/null differ diff --git a/testdata/M32_expected/2010-target-adjustment b/testdata/M32_expected/2010-target-adjustment deleted file mode 100644 index 5b3cfa6..0000000 Binary files a/testdata/M32_expected/2010-target-adjustment and /dev/null differ diff --git a/testdata/M32_expected/2011-hinting b/testdata/M32_expected/2011-hinting deleted file mode 100644 index 07cb179..0000000 --- a/testdata/M32_expected/2011-hinting +++ /dev/null @@ -1,2 +0,0 @@ -,,©ê,¢ÿê -ê,¢ÿê  V$©¢"  ( V$©3¢D : =ê­V$ E` V$©U¢f`‚ƒ©™` \ No newline at end of file diff --git a/testdata/M32_expected/2012-label-localizer b/testdata/M32_expected/2012-label-localizer deleted file mode 100644 index 7a2358e..0000000 Binary files a/testdata/M32_expected/2012-label-localizer and /dev/null differ diff --git a/testdata/M32_expected/2013-notes-and-comments b/testdata/M32_expected/2013-notes-and-comments deleted file mode 100644 index 233ba05..0000000 Binary files a/testdata/M32_expected/2013-notes-and-comments and /dev/null differ diff --git a/testdata/M32_expected/2014-label-dp b/testdata/M32_expected/2014-label-dp deleted file mode 100644 index 5c8e85e..0000000 Binary files a/testdata/M32_expected/2014-label-dp and /dev/null differ diff --git a/testdata/M32_expected/2019-local-variables b/testdata/M32_expected/2019-local-variables deleted file mode 100644 index 64d9d5b..0000000 Binary files a/testdata/M32_expected/2019-local-variables and /dev/null differ diff --git a/testdata/M32_expected/2020-cycle-counts-65816 b/testdata/M32_expected/2020-cycle-counts-65816 deleted file mode 100644 index a35ec67..0000000 Binary files a/testdata/M32_expected/2020-cycle-counts-65816 and /dev/null differ diff --git a/testdata/M32_expected/2021-external-symbols b/testdata/M32_expected/2021-external-symbols deleted file mode 100644 index b1d9202..0000000 Binary files a/testdata/M32_expected/2021-external-symbols and /dev/null differ diff --git a/testdata/M32_expected/2022-extension-scripts b/testdata/M32_expected/2022-extension-scripts deleted file mode 100644 index f110a86..0000000 Binary files a/testdata/M32_expected/2022-extension-scripts and /dev/null differ diff --git a/todo.txt b/todo.txt index a5106a3..7752725 100644 --- a/todo.txt +++ b/todo.txt @@ -1,11 +1,12 @@ (0) 2019-11-17 - BRK does not detect a lable that can't be evaluated (0) 2019-11-17 - ASCII parsing in both eval and for ASC type commands (0) 2019-11-17 - IF processing for character compare mode - (0) 2019-11-17 - - (0) 2019-11-17 - - (0) 2019-11-17 - - (0) 2019-11-17 - - (0) 2019-11-17 - + (0) 2019-11-17 - Note (syntax): Merlin doesn't allow local labels without a previous global + (0) 2019-11-17 - Note (syntax): Merlin doesn't allow local colons after label which is ignored + (0) 2019-11-17 - Note (syntax): Merlin doesn't allow for 'rep/sep/xce' tracking + (0) 2019-11-17 - Bug (evaluation routine puts ascii bytes in reverse order (#"AB") + (0) 2019-11-17 - want to wrap comments based on current terminal width, and don't if printing to non-terminal + (0) 2019-11-17 - syntax options - check OPTION bits, not syntax type (OPTION_ALLOW_LOCAL) (0) 2019-11-17 - (0) 2019-11-17 - (0) 2019-11-17 -