qasm/psuedo.cpp

1161 lines
21 KiB
C++

#include "psuedo.h"
#include "eval.h"
#define CLASS TPsuedoOp
CLASS::CLASS()
{
}
CLASS::~CLASS()
{
}
uint32_t CLASS::doShift(uint32_t value, uint8_t shift)
{
if (shift == '<')
{
value = (value) & 0xFFFFFF;
}
if (shift == '>')
{
value = (value >> 8) & 0xFFFFFF;
}
else if ((shift == '^') || (shift == '|'))
{
value = (value >> 16) & 0xFFFFFF;
}
return (value);
}
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;
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_value = 0;
int x = eval.evaluate(line.operand_expr, eval_value, shift);
if (x < 0)
{
a.curDO.doskip = false;
err = errBadLabel;
if (a.pass == 0)
{
err = errForwardRef;
}
goto out;
}
result32 = eval_value & 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::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 <<<
{
if (a.macrostack.size()>0)
{
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<std::string, TMacro> 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();
}
}
else
{
err = errUnexpectedOp;
goto out;
}
}
out:
if (err)
{
line.setError(err);
}
return (res);
}
int CLASS::doLUP(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
UNUSED(opinfo);
TEvaluator eval(a);
int64_t eval_value = 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_value = 0;
int x = eval.evaluate(line.operand_expr, eval_value, shift);
a.LUPstack.push(a.curLUP);
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++;
if ((x < 0) || (eval_value <= 0) || (eval_value > 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)
{
if (a.expand_macrostack.size() > 0)
{
a.expand_macro.currentline = a.curLUP.lupoffset;
}
else
{
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;
std::string op = Poco::toUpper(Poco::trim(line.opcode));
//printf("DFB TOK1 : |%s|\n", oper.c_str());
line.eval_result = 0; // since this is an data p-op, clear the global 'bad operand' flag
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)
{
//evaluate each of these strings, check for errors on pass 2
std::string expr = *itr;
//printf("DFB TOK : |%s|\n", expr.c_str());
int64_t eval_value = 0;
uint8_t shift;
int r;
uint8_t b;
if (expr.length() > 0)
{
if (expr[0] == '#')
{
expr[0] = ' ';
expr = Poco::trim(expr);
}
shift = 0;
eval_value = 0;
//printf("DFB EVAL: |%s|\n", expr.c_str());
r = eval.evaluate(expr, eval_value, shift);
if (r < 0)
{
//printf("error %d\n",r);
if (a.pass > 0)
{
line.setError(errBadEvaluation);
}
}
eval_value = (uint64_t)doShift((uint32_t)eval_value, shift);
}
outct += wordsize;
if (a.pass > 0)
{
if (!endian) // little endian
{
for (i = 0; i < wordsize; i++)
{
b = (eval_value >> (8 * i)) & 0xFF;
line.outbytes.push_back(b);
//printf("%02X\n",b);
}
}
else
{
// big endian
for (i = 0; i < wordsize; i++)
{
b = (eval_value >> ((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;
TEvaluator eval(a);
int64_t eval_value = 0;
uint8_t shift;
line.eval_result = 0; // since this is an data p-op, clear the global 'bad operand' flag
line.flags |= FLAG_FORCEADDRPRINT;
std::string s;
Poco::StringTokenizer tok(line.operand, ",", Poco::StringTokenizer::TOK_TRIM |
Poco::StringTokenizer::TOK_IGNORE_EMPTY);
int32_t datact = 0;
uint8_t fill = 0x0;
bool pagefill = false;
int32_t v = 0;
int ct = 0;
for (auto itr = tok.begin(); itr != tok.end(); ++itr)
{
s = *itr;
if (ct == 0)
{
if (s == "\\")
{
pagefill = true;
}
else
{
shift = 0;
eval_value = 0;
int x = eval.evaluate(s, eval_value, shift);
if (x < 0)
{
line.setError(errBadOperand);
goto out;
}
eval_value = (uint64_t)doShift((uint32_t)eval_value, shift);
datact = eval_value & 0xFFFF;
if (datact < 0)
{
line.setError(errBadOperand);
goto out;
}
}
}
else if (ct == 1)
{
shift = 0;
eval_value = 0;
int x = eval.evaluate(s, eval_value, shift);
if (x < 0)
{
line.setError(errBadOperand);
goto out;
}
eval_value = (uint64_t)doShift((uint32_t)eval_value, shift);
fill = eval_value & 0xFF;
}
else if (ct > 1)
{
line.setError(errBadOperand);
}
ct++;
}
line.datafillbyte = fill;
v = datact;
if (pagefill)
{
v = line.startpc & 0xFF;
v = 0x100 - v;
}
line.datafillct = (uint16_t)v & 0xFFFF;
res = line.datafillct;
out:
//printf("res=%d %04X\n",res,res);
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);
}
char hexVal( char c )
{
char v = -1;
if ((c >= '0') && (c <= '9'))
{
v = c - '0';
}
else if ((c >= 'a') && (c <= 'f'))
{
v = c - 'a' + 10;
}
else if ((c >= 'A') && (c <= 'F'))
{
v = c - 'A' + 10;
}
return v;
}
int CLASS::doHEX(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
UNUSED(opinfo);
std::string os = Poco::trim(line.operand);
line.eval_result = 0; // since this is an data p-op, clear the global 'bad operand' flag
uint32_t bytect = 0;
uint8_t b = 0;
uint8_t ct = 0;
for ( uint32_t i = 0; i < os.length(); ++i )
{
char c = os[i];
if (c == ',')
{
continue;
}
char hv = hexVal(c);
if ( hv < 0 )
{
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 = (hv << 4);
break;
case 1:
b |= hv;
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;
}
static int usr_hash(std::string os)
{
int hash = 0;
os.resize(4, ' ');
os = Poco::toUpper(os);
hash = (os[0] & 0x1f) << 11;
hash |= (os[1] & 0x1f) << 6;
hash |= (os[2] & 0x1f) << 1;
if (os[3] == 'L')
{
hash |= 0x01;
}
return hash;
}
/*
handler for USR. generates a 2-byte opcode hash
USR $00 - equivalent to DW $0 (qasm)
USR 'ADC ' - hashes the 4-byte string (qasm)
USR ADC - hashes the 1-4 byte symbol (merlin)
*/
int CLASS::doUSR(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
UNUSED(opinfo);
line.eval_result = 0; // since this is an data p-op, clear the global 'bad operand' flag
std::string os = Poco::trim(line.operand);
uint32_t bytect = 2;
uint32_t hash = 0;
char c = os.empty() ? 0 : os.front();
if (c == '$' && os.length() > 1)
{
for (uint32_t i = 1; i < os.length(); ++i)
{
char hv = hexVal(os[i]);
if (hv < 0)
{
line.setError(errIllegalCharOperand);
bytect = 0;
goto out;
}
hash <<= 4;
hash |= hv;
}
}
else if ((c == '\'' || c == '\"') && os.front() == os.back() && os.length() == 6)
{
hash = usr_hash(os.substr(1, 4));
}
else if (os.length() > 0 && os.length() <= 4)
{
hash = usr_hash(os);
}
else
{
printf("line.setError(errBadOperand);\n");
line.setError(errBadOperand);
bytect = 0;
goto out;
}
if (a.pass > 0)
{
line.outbytes.push_back(hash & 0xff);
line.outbytes.push_back(hash >> 8);
}
out:
line.outbytect = bytect;
return bytect;
}
int CLASS::doPAU(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
UNUSED(opinfo);
return 0;
}
int CLASS::doCHK(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
int res=1;
UNUSED(opinfo);
if (a.pass>0)
{
uint8_t val=0;
for (uint64_t i=0;i<a.outputbytes.size();i++)
{
val^=a.outputbytes[i];
//printf("%02X ",val);
}
line.outbytes.push_back(val);
}
line.outbytect=res;
return (res);
}
// the handler for STR,STRL,REV,FLS,INV,DCI,ASC
int CLASS::doASC(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
UNUSED(opinfo);
std::string os = line.operand;
std::string op = Poco::toUpper(line.opcode);
uint8_t firstdelim = 0;
uint32_t bytect = 0;
uint8_t b = 0;
uint8_t b1;
uint8_t ct = 0;
uint8_t delimiter = 0;
uint32_t ss = 0;
uint32_t lastdelimidx = 0;
bool dci = false;
std::vector<uint8_t> bytes;
line.eval_result = 0; // since this is an ASCII p-op, clear the global 'bad operand' flag
for ( uint32_t i = 0; i < os.length(); ++i )
{
uint8_t c = os[i];
// are we inside a delimited string?
if ( delimiter )
{
if ( c == delimiter )
{
bytect += (i - ss);
if ( a.pass > 0 )
{
for ( ; ss < i; ++ss )
{
c = os[ss];
if ( delimiter >= '\'' )
{
c &= 0x7F;
}
else
{
c |= 0x80;
}
bytes.push_back(c);
lastdelimidx = (uint32_t)(bytes.size() - 1);
}
}
delimiter = 0;
ss = 0;
continue;
}
}
else
{
// No, check for seperator characters
if ( c == ',' || c == ' ' )
{
continue;
}
// Is this a hex char?
char hv = hexVal(c);
if ( hv < 0 )
{
// if not a hex value, then consider the character to be the string delimiter
delimiter = c;
if( ! firstdelim )
{
firstdelim = c;
}
else if (delimiter != firstdelim)
{
line.setError(errIllegalCharOperand);
}
ss = i + 1;
continue;
}
// Got a hex char, append to hex string and see if we've got a byte
switch (ct)
{
case 0:
b = (hv << 4);
break;
case 1:
b |= hv;
break;
}
ct = (ct + 1) & 0x01;
if (!ct)
{
if (a.pass > 0)
{
bytes.push_back(b);
}
b = 0;
bytect++;
}
}
}
if ( delimiter || (ct & 0x01) ) // error w/unterminated string or we got an odd number of nibbles in hex value
{
line.setError(errBadOperand);
bytect = 0;
}
else // now figure out what psuedo op we are and transfer the data to outbytect
{
uint32_t i;
bool reverse = false;
uint8_t andval = 0xFF;
uint8_t orval = 0x00;
uint8_t addlen = 0;
uint32_t truebytect = (uint32_t)bytes.size();
const char *ptr = (const char *)op.c_str();
//printf("bytect=%d bytes.size()=%zu\n",bytect,bytes.size());
switch (strhash(ptr) )
{
case strhash((const char *)"STRL"):
addlen = 2;
break;
case strhash((const char *)"STR"):
addlen = 1;
break;
case strhash((const char *)"REV"):
reverse = true;
break;
case strhash((const char *)"FLS"):
andval = (uint8_t)~0xC0;
orval = (uint8_t)0x40;
break;
case strhash((const char *)"INV"):
andval = (uint8_t)~0xC0;
orval = 0x00;
break;
case strhash((const char *)"DCI"):
dci = true;
break;
case strhash((const char *)"ASC"):
break;
default:
line.setError(errBadOpcode);
bytect = 0;
addlen = 0;
break;
}
if (a.pass > 0)
{
for (i = 0; i < addlen; i++) // if a string, push length
{
line.outbytes.push_back((truebytect >> (i * 8)) & 0xFF);
}
for (i = 0; i < truebytect; i++)
{
if (reverse)
{
b = bytes[bytect - i - 1];
}
else
{
b = bytes[i];
}
b1 = b & 0x7F;
if ((andval != 0xFF) || (orval != 0x00))
{
b = b1;
}
if ((b1 < 0x60))
{
b &= andval; // strip whatever bits needed to flash or invert
b |= orval;
}
if ((line.options->isMerlin32()) || (line.options->isMerlin16plus()))
{
// invert last byte of total bytes out
if ((dci) && ((i+1)>=truebytect))
{
b^=0x80;
}
}
else
{
//printf("bug\n"); // SGQ BUG - still not working right
// invert last byte of last string (old merlin way (pre-merlin16+))
if (dci && (i == lastdelimidx))
{
// SGQ BUG - Merlin16+ does it like Merlin32 and now does the last
// byte not the way merlin816 and earlier do it documented below.
// use OPTION_M16_PLUS when implemented.
//lr - Merlin only toggles the high bit of string chars, not hex values
// 8D,'Hello',8D,'there',8D becomes 8D 48 65 6C 6C 6F 8D 74 68 65 72 E5
//
// The DCI instruction is documented to work like this on page 108
// (regardless of how this effects the desired lda, (bpl/bmi) functionality)
//
// I am now checking the delimiter character to determine hi/lo toggle (reversed)
// and am tracking the index to the last delimited character put into 'bytes'.
// This produces the same results as Merlin 16+ in my testing.
if ( firstdelim >= '\'' )
{
b |= 0x80;
}
else
{
b &= 0x7F;
}
}
}
line.outbytes.push_back(b);
}
}
bytect = bytect + addlen;
}
//printf("XXX bytect=%d bytes.size()=%zu\n",bytect,bytes.size());
line.outbytect = bytect;
return bytect;
}
int CLASS::ProcessOpcode(T65816Asm &a, MerlinLine &line, TSymbol &opinfo)
{
int res = 0;
std::string s,sp;
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;
}
#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)
if (options.isMerlin32())
{
// 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:
sp = getConfig("option.objfile", "");
if (sp=="")
{
//a.savepath = a.processFilename(line.operand, Poco::Path::current(), 0);
a.savepath=line.operand;
}
else // if they specified an output name on the command line, use it.
{
a.savepath = sp;
}
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;
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;
case P_ASC:
res = doASC(a, line, opinfo);
break;
case P_USR:
res = doUSR(a, line, opinfo);
break;
case P_PAU:
res = doPAU(a, line, opinfo);
break;
case P_CHK:
res = doCHK(a, line, opinfo);
break;
}
return (res);
}