#include "psuedo.h" #include "eval.h" #define CLASS TPsuedoOp CLASS::CLASS() { } CLASS::~CLASS() { } int CLASS::doDO(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); TEvaluator eval(a); int64_t eval_result = 0; uint8_t shift; uint32_t result32; int res = 0; int err = 0; std::string op = Poco::toUpper(line.opcode); std::string oper = line.operand_expr; result32 = 0xFFFFFFFF; if (op == "IF") { if (oper == "") { err = errIllegalCharOperand; } goto out; } if (op == "DO") { a.DOstack.push(a.curDO); if (oper == "") { err = errIllegalCharOperand; a.curDO.doskip = false; goto out; } shift = 0; eval_result = 0; int x = eval.evaluate(line.operand_expr, eval_result, shift); if (x < 0) { a.curDO.doskip = false; err = errBadLabel; if (a.pass == 0) { err = errForwardRef; } goto out; } result32 = eval_result & 0xFFFFFFFF; a.curDO.doskip = (result32 != 0) ? false : true; goto out; } if (op == "ELSE") { if (a.DOstack.size() > 0) { //line.flags |= FLAG_NOLINEPRINT; a.curDO.doskip = !a.curDO.doskip; } else { err = errUnexpectedOp; } goto out; } if (op == "FIN") { //line.flags |= FLAG_NOLINEPRINT; if (a.DOstack.size() > 0) { a.curDO = a.DOstack.top(); a.DOstack.pop(); } else { // kind of a silent error here, just make sure we reinitialize err = errUnexpectedOp; a.curDO.doskip = false; } goto out; } out: //printf("DO eval: %08X %s\n", result32, a.curDO.doskip ? "true" : "false"); if (err > 0) { line.setError(err); } return (res); } int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); TEvaluator eval(a); int64_t eval_result = 0; uint8_t shift; int lidx, len; int res = 0; int err = 0; std::string op = Poco::toUpper(line.opcode); if (op == "LUP") { line.flags |= FLAG_NOLINEPRINT; len = line.lineno - 1; // MerlinLine line numbers are +1 from actual array idx if (len >= 0) { shift = 0; eval_result = 0; int x = eval.evaluate(line.operand_expr, eval_result, shift); a.LUPstack.push(a.curLUP); a.curLUP.lupoffset = len; a.curLUP.lupct = eval_result & 0xFFFF; // evaluate here a.curLUP.luprunning++; if ((x < 0) || (eval_result <= 0) || (eval_result > 0x8000)) { // merlin just ignores LUP if the value is out of range a.curLUP.lupct = 0; a.curLUP.lupskip = true; } } else { err = errUnexpectedOp; } } if (op == "--^") { line.flags |= FLAG_NOLINEPRINT; if (a.curLUP.luprunning > 0) { lidx = line.lineno - 1; len = lidx - a.curLUP.lupoffset - 1; if (a.curLUP.lupct > 0) { a.curLUP.lupct--; if (a.curLUP.lupct != 0) { a.lineno = a.curLUP.lupoffset; goto out; } } // kind of a silent error here, just make sure we reinitialize a.curLUP.luprunning = 0; a.curLUP.lupct = 0; a.curLUP.lupskip = false; //printf("start=%d end=%d len=%d\n", a.curLUP.lupoffset, lidx, len); if (a.LUPstack.size() > 0) { a.curLUP = a.LUPstack.top(); a.LUPstack.pop(); } else { err = errUnexpectedOp; } } else { a.curLUP.lupskip = false; // SGQ - found a '--^' without a LUP, should we just ignore? //err = errUnexpectedOp; } } out: if (err > 0) { line.setError(err); } return (res); } constexpr unsigned int strhash(const char *str, int h = 0) { return !str[h] ? 5381 : (strhash(str, h + 1) * 33) ^ str[h]; } int CLASS::doDATA(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); TEvaluator eval(a); int i; int outct = 0; int wordsize = 2; int endian = 0; std::string oper = line.operand_expr; std::string op = Poco::toUpper(Poco::trim(line.opcode)); Poco::StringTokenizer tok(oper, ",", Poco::StringTokenizer::TOK_TRIM | Poco::StringTokenizer::TOK_IGNORE_EMPTY); const char *ptr = (const char *)op.c_str(); switch (strhash(ptr) ) { case strhash((const char *)"DA"): case strhash((const char *)"DW"): wordsize = 2; break; case strhash((const char *)"DDB"): wordsize = 2; endian = 1; break; case strhash((const char *)"DFB"): case strhash((const char *)"DB"): wordsize = 1; break; case strhash((const char *)"ADR"): wordsize = 3; break; case strhash((const char *)"ADRL"): wordsize = 4; break; default: wordsize = 0; break; } for (auto itr = tok.begin(); itr != tok.end(); ++itr) { //printf("%s\n",(*itr).c_str()); //evaluate each of these strings, check for errors on pass 2 std::string expr = *itr; int64_t eval_result = 0; uint8_t shift; int r; uint8_t b; shift = 0; r = eval.evaluate(expr, eval_result, shift); if (r < 0) { //printf("eval error %d |%s|\n", r,expr.c_str()); if (a.pass > 0) { line.setError(errBadEvaluation); } } if (shift == '>') { eval_result = (eval_result) & 0xFF; } if (shift == '<') { eval_result = (eval_result >> 8) & 0xFF; } else if ((shift == '^') || (shift == '|')) { eval_result = (eval_result >> 16) & 0xFF; } outct += wordsize; if (a.pass > 0) { if (!endian) // little endian { for (i = 0; i < wordsize; i++) { b = (eval_result >> (8 * i)) & 0xFF; line.outbytes.push_back(b); //printf("%02X\n",b); } } else { // big endian for (i = 0; i < wordsize; i++) { b = (eval_result >> ((wordsize - 1 - i) * 8)) & 0xFF; line.outbytes.push_back(b); //printf("%02X\n",b); } } } } line.outbytect = outct; return (outct); } int CLASS::doDS(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); int res = 0; int32_t v = line.expr_value; if (line.eval_result != 0) { line.setError(errForwardRef); } else if ((v < 0) || ((a.PC.currentpc + v) >= 0x10000)) // no neg, or crossing bank bound { line.setError(errOverflow); } else { res = v; line.datafillbyte = line.eval_result & 0xFF; line.datafillct = v; } return (res); } int CLASS::doDUM(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); int res = 0; bool isdend = ((opinfo.opcode == P_DEND) ? true : false); if (!isdend) { a.dumstart = 1; a.dumstartaddr = line.expr_value; } else { a.dumstart = -1; if (a.PCstack.size() == 0) { line.setError(errBadDUMop); a.dumstart = 0; } } return (res); } int CLASS::doLST(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); std::string s; if (a.pass > 0) { s = Poco::toUpper(Poco::trim(line.operand_expr)); if (s=="") { a.listing=true; a.skiplist=true; } else if (s == "RTN") { if (a.LSTstack.size()) { a.listing = a.LSTstack.top(); a.LSTstack.pop(); } } else if ((s == "ON") || (line.expr_value > 0)) { //printf("ON\n"); a.skiplist = true; a.listing = true; } else if ((s == "OFF") || (line.expr_value == 0)) { //printf("OFF\n"); a.skiplist = true; a.listing = false; } } return (0); } int CLASS::doTR(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); std::string s; if (a.pass > 0) { s = Poco::toUpper(Poco::trim(line.operand_expr)); if (s == "ADR") { a.truncdata |= 0x03; } else if ((s == "ON") || (line.expr_value > 0)) { a.truncdata |= 0x01;; } else if ((s == "OFF") || (line.expr_value == 0)) { a.truncdata = 0x00; } } return (0); } int CLASS::doHEX(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { UNUSED(opinfo); std::string os = Poco::toUpper(Poco::trim(line.operand_expr)); uint32_t bytect = 0; uint8_t b = 0; uint8_t ct = 0; if (os.length() == 0) { // case where HEX has no operand, Merlin does not flag as error //line.setError(errIllegalCharOperand); bytect = 0; goto out; } for ( uint32_t i = 0; i < os.length(); ++i ) { char c = os[i]; if ((c >= '0') && (c <= '9')) { c = c - '0'; } else if ((c >= 'a') && (c <= 'f')) { c = c - 'a' + 10; } else if ((c >= 'A') && (c <= 'F')) { c = c - 'A' + 10; } else if (c == ',') { continue; } else { line.setError(errIllegalCharOperand); bytect = 0; goto out; } // Got a good char, append to hex string and see if we've got a byte switch (ct) { case 0: b = (c << 4); break; case 1: b |= c; break; } ct = (ct + 1) & 0x01; if (!ct) { if (a.pass > 0) { line.outbytes.push_back(b); } b = 0; bytect++; } } if (ct & 0x01) // we got an odd number of nibbles { line.setError(errBadOperand); bytect = 0; } out: line.outbytect = bytect; return bytect; } int CLASS::ProcessOpcode(T65816Asm &a, MerlinLine &line, TSymbol &opinfo) { int res = 0; switch (opinfo.opcode) { default: res = -1; // undefined p-op line.setError(errUnimplemented); break; case P_DS: res = doDS(a, line, opinfo); break; case P_PUT: case P_USE: // both of these are handled by the input file processor, just allow them to be // processed with no errors here break; case P_DUM: case P_DEND: res = doDUM(a, line, opinfo); line.flags |= FLAG_FORCEADDRPRINT; break; case P_ORG: if (line.operand_expr.length() > 0) { a.PC.orgsave = a.PC.currentpc; a.PC.currentpc = line.expr_value; line.startpc = line.expr_value; } else { a.PC.currentpc = a.PC.orgsave; line.startpc = a.PC.orgsave; } line.flags |= FLAG_FORCEADDRPRINT; break; case P_SAV: a.savepath = a.processFilename(line.operand, Poco::Path::current(), 0); break; case P_LST: res = doLST(a, line, opinfo); break; case P_HEX: res = doHEX(a, line, opinfo); break; case P_DATA: res = doDATA(a, line, opinfo); break; case P_LUP: res = doLUP(a, line, opinfo); break; case P_DO: res = doDO(a, line, opinfo); break; case P_TR: res=doTR(a,line,opinfo); break; } return (res); }