735 lines
12 KiB
C
735 lines
12 KiB
C
|
/* asmexpr.c -- microsoft 80x86 assembler
|
||
|
**
|
||
|
** microsoft (r) macro assembler
|
||
|
** copyright (c) microsoft corp 1986. all rights reserved
|
||
|
**
|
||
|
** randy nevin
|
||
|
**
|
||
|
** 10/90 - Quick conversion to 32 bit by Jeff Spencer
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <ctype.h>
|
||
|
#include "asm86.h"
|
||
|
#include "asmfcn.h"
|
||
|
#include "asmctype.h"
|
||
|
#include "asmexpr.h"
|
||
|
#include "asmmsg.h"
|
||
|
|
||
|
extern UCHAR opprec [];
|
||
|
extern char fValidSym, addplusflagCur;
|
||
|
|
||
|
|
||
|
|
||
|
/*** endstring - check for end of string
|
||
|
*
|
||
|
* flag = endstring ();
|
||
|
*
|
||
|
* Entry delim = string delimiter character
|
||
|
* Exit none
|
||
|
* Returns TRUE if at end of string
|
||
|
* FALSE if not at end of string
|
||
|
* Calls error
|
||
|
* Note Double occurances of delimiter character are returned as a
|
||
|
* single occurance of the delimiter character.
|
||
|
*/
|
||
|
|
||
|
UCHAR PASCAL CODESIZE
|
||
|
endstring ()
|
||
|
{
|
||
|
register UCHAR cc;
|
||
|
|
||
|
if ((cc = PEEKC ()) == 0) {
|
||
|
/* End of line before delim */
|
||
|
errorc (E_UEL);
|
||
|
return (TRUE);
|
||
|
}
|
||
|
else if (cc == delim) {
|
||
|
/* check for escaped quote character */
|
||
|
SKIPC ();
|
||
|
if ((cc = PEEKC ()) != delim) {
|
||
|
BACKC ();
|
||
|
return (TRUE);
|
||
|
}
|
||
|
}
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** oblititem - release parse stack record
|
||
|
*
|
||
|
* oblititem (arg);
|
||
|
*
|
||
|
* Entry *arg = parse stack record
|
||
|
* Exit parse stack record released
|
||
|
* Returns none
|
||
|
* Calls free
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
oblititem (
|
||
|
register DSCREC *arg
|
||
|
){
|
||
|
register char c;
|
||
|
|
||
|
if ((c = arg->itype) == ENDEXPR || c == OPERATOR || c == OPERAND)
|
||
|
dfree( (UCHAR *)arg );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** flteval - Look at ST | ST(i) and create entry
|
||
|
*
|
||
|
* flteval ();
|
||
|
*
|
||
|
* Entry *ptr = parse stack entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
flteval ()
|
||
|
{
|
||
|
*itemptr = emptydsc;
|
||
|
/* ST means ST(0) */
|
||
|
/* We are 8087 stack */
|
||
|
itemptr->dsckind.opnd.dtype = M_RCONST | M_FLTSTACK;
|
||
|
/* Need + if ST(i) */
|
||
|
addplusflagCur = (PEEKC () == '(');
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** createitem - make item entry
|
||
|
*
|
||
|
* createitem (itemkind, itemsub, p);
|
||
|
*
|
||
|
* Entry itemkind = kind of item
|
||
|
* itemsub =
|
||
|
* *p = activation record
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
* Note If symbol, look further to see if EQU, record name
|
||
|
* and do appropriate thing.
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
createitem (
|
||
|
UCHAR itemkind,
|
||
|
UCHAR itemsub
|
||
|
){
|
||
|
register struct psop *pso; /* parse stack operand structure */
|
||
|
|
||
|
switch (itemkind) {
|
||
|
case OPERAND:
|
||
|
/* Create default record */
|
||
|
itemptr = defaultdsc ();
|
||
|
pso = &(itemptr->dsckind.opnd);
|
||
|
switch (itemsub) {
|
||
|
case ICONST:
|
||
|
pso->doffset = val;
|
||
|
break;
|
||
|
case ISIZE:
|
||
|
#ifdef V386
|
||
|
pso->doffset = (long) (SHORT) varsize;
|
||
|
#else
|
||
|
pso->doffset = varsize;
|
||
|
#endif
|
||
|
pso->s++; /* note for expr evaluator */
|
||
|
break;
|
||
|
case IUNKNOWN:
|
||
|
pso->dflag = INDETER;
|
||
|
break;
|
||
|
case ISYM:
|
||
|
createsym ();
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case OPERATOR:
|
||
|
itemptr = dalloc();
|
||
|
itemptr->dsckind.opr.oidx = opertype;
|
||
|
break;
|
||
|
}
|
||
|
/* Set type of entry */
|
||
|
itemptr->itype = itemkind;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** numeric - evaluate numeric string
|
||
|
*
|
||
|
* numeric (count, base, p);
|
||
|
*
|
||
|
* Entry count = number of characters in string
|
||
|
* base = conversion base
|
||
|
* *p = activation record
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
numeric (
|
||
|
SHORT cnt,
|
||
|
SHORT base
|
||
|
){
|
||
|
register UCHAR t;
|
||
|
register long temp = 0;
|
||
|
OFFSET maxInt;
|
||
|
|
||
|
maxInt = (fArth32)? OFFSETMAX: 0xffff;
|
||
|
|
||
|
if (base > 10)
|
||
|
for (; cnt; cnt--) {
|
||
|
if ((t = MAP (NEXTC ()) - '0') > 9)
|
||
|
t -= 'A' - '9' - 1;
|
||
|
if (t >= base)
|
||
|
errorc (E_NDN);
|
||
|
if ((OFFSET)(temp = temp * base + t) > maxInt)
|
||
|
errorc (E_DVZ);
|
||
|
}
|
||
|
else
|
||
|
for (; cnt; cnt--) {
|
||
|
if ((t = NEXTC () - '0') >= base)
|
||
|
errorc (E_NDN);
|
||
|
|
||
|
if ((OFFSET)(temp = temp * base + t) > maxInt)
|
||
|
errorc (E_DVZ);
|
||
|
}
|
||
|
val = temp;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** evalconst - evaluate constant
|
||
|
*
|
||
|
* type = evalconst (p);
|
||
|
*
|
||
|
* Entry *p = parser activation record
|
||
|
* Exit numeric item added to parse stack entry
|
||
|
* Returns type of item added to parse stack
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
void PASCAL CODESIZE
|
||
|
evalconst ()
|
||
|
{
|
||
|
register char cc;
|
||
|
register SHORT i = 0;
|
||
|
char *endscan, *begscan;
|
||
|
SHORT rbase;
|
||
|
|
||
|
begscan = lbufp;
|
||
|
while (isxdigit (cc = PEEKC ())) {
|
||
|
SKIPC ();
|
||
|
i++;
|
||
|
}
|
||
|
switch (MAP (cc)) {
|
||
|
case 'H':
|
||
|
rbase = 16;
|
||
|
SKIPC ();
|
||
|
break;
|
||
|
case 'O':
|
||
|
case 'Q':
|
||
|
rbase = 8;
|
||
|
SKIPC ();
|
||
|
break;
|
||
|
default:
|
||
|
BACKC ();
|
||
|
switch (MAP (NEXTC ())) {
|
||
|
case 'B':
|
||
|
rbase = 2;
|
||
|
i--;
|
||
|
break;
|
||
|
case 'D':
|
||
|
rbase = 10;
|
||
|
i--;
|
||
|
break;
|
||
|
default:
|
||
|
if (cc == '.')
|
||
|
errorcSYN ();
|
||
|
if (radixescape)
|
||
|
rbase = 10;
|
||
|
else
|
||
|
rbase = radix;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
endscan = lbufp;
|
||
|
lbufp = begscan;
|
||
|
numeric (i, rbase);
|
||
|
lbufp = endscan;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** evalstring - evaluate quoted string
|
||
|
*
|
||
|
* type = evalstring ();
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit new item added to parse stack
|
||
|
* Returns type of item added to stack
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
char PASCAL CODESIZE
|
||
|
evalstring ()
|
||
|
{
|
||
|
register USHORT i, max;
|
||
|
|
||
|
max = 2;
|
||
|
if (cputype & P386)
|
||
|
max += 2;
|
||
|
|
||
|
delim = NEXTC (); /* Set delim for string */
|
||
|
i = 0;
|
||
|
val = 0;
|
||
|
while (!endstring () && i <= max) {
|
||
|
|
||
|
val = (val << 8) + ((UCHAR)NEXTC ());
|
||
|
i++;
|
||
|
}
|
||
|
if (i == 0)
|
||
|
errorc (E_EMS);
|
||
|
|
||
|
else if (i > max) { /* Too long */
|
||
|
while (!endstring ())
|
||
|
SKIPC ();
|
||
|
errorcSYN ();
|
||
|
}
|
||
|
if (PEEKC () == delim)
|
||
|
SKIPC ();
|
||
|
|
||
|
createitem (OPERAND, ICONST);
|
||
|
return (OPERAND);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** getitem - get next item on line
|
||
|
*
|
||
|
* getitem (p);
|
||
|
*
|
||
|
* Entry *p = activation record
|
||
|
* Exit *itemptr = description of item
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
char PASCAL CODESIZE
|
||
|
getitem (
|
||
|
struct ar *p
|
||
|
){
|
||
|
register char cc;
|
||
|
#ifdef FIXCOMPILERBUG
|
||
|
char cc1;
|
||
|
#endif
|
||
|
|
||
|
if (fValidSym)
|
||
|
return (evalalpha (p));
|
||
|
|
||
|
/* The compiler bug looses the correct value for cc when optimization is
|
||
|
turned on. This in turn caused an exception to occure near getitem+1C0.
|
||
|
The bogus code below sidesteps the problem. */
|
||
|
#ifdef FIXCOMPILERBUG // This was put in to get around a MIPS compiler bug(12/3/90)
|
||
|
cc1 = skipblanks();
|
||
|
if (ISTERM (cc1))
|
||
|
return (ENDEXPR);
|
||
|
cc = cc1;
|
||
|
#else
|
||
|
if (ISTERM (cc = skipblanks()))
|
||
|
return (ENDEXPR);
|
||
|
#endif
|
||
|
if (LEGAL1ST (cc))
|
||
|
return (evalalpha (p));
|
||
|
|
||
|
/* token is not alpha string or .string (.TYPE) operator */
|
||
|
|
||
|
if (ISOPER (cc)) {
|
||
|
SKIPC ();
|
||
|
switch (cc) {
|
||
|
case '(':
|
||
|
opertype = OPLPAR;
|
||
|
break;
|
||
|
case '+':
|
||
|
opertype = OPPLUS;
|
||
|
break;
|
||
|
case '-':
|
||
|
opertype = OPMINUS;
|
||
|
break;
|
||
|
case '*':
|
||
|
opertype = OPMULT;
|
||
|
break;
|
||
|
case '/':
|
||
|
opertype = OPDIV;
|
||
|
break;
|
||
|
case ')':
|
||
|
opertype = OPRPAR;
|
||
|
break;
|
||
|
case '.':
|
||
|
errorcSYN ();
|
||
|
opertype = OPDOT;
|
||
|
break;
|
||
|
case ',': /* should never get here, for density */
|
||
|
break;
|
||
|
default:
|
||
|
if (cc == '[')
|
||
|
opertype = OPLBRK;
|
||
|
else if (cc == ']')
|
||
|
opertype = OPRBRK;
|
||
|
else if (cc == ':')
|
||
|
opertype = OPCOLON;
|
||
|
break;
|
||
|
}
|
||
|
operprec = opprec [opertype];
|
||
|
createitem (OPERATOR, ISYM);
|
||
|
return (OPERATOR);
|
||
|
}
|
||
|
else if (isdigit (cc)){
|
||
|
|
||
|
evalconst ();
|
||
|
createitem (OPERAND, ICONST);
|
||
|
return (OPERAND);
|
||
|
}
|
||
|
|
||
|
else if ((cc == '"') || (cc == '\''))
|
||
|
/* String may be made into constant if <=2 */
|
||
|
return (evalstring ());
|
||
|
else
|
||
|
return (ENDEXPR);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** defaultdsc - create a default parse stack entry
|
||
|
*
|
||
|
* ptr = defaultdsc ();
|
||
|
*
|
||
|
* Entry none
|
||
|
* Exit none
|
||
|
* Returns *ptr = default parse stack entry
|
||
|
* Calls malloc
|
||
|
*/
|
||
|
|
||
|
DSCREC * PASCAL CODESIZE
|
||
|
defaultdsc ()
|
||
|
{
|
||
|
register DSCREC *valu;
|
||
|
|
||
|
valu = dalloc();
|
||
|
*valu = emptydsc;
|
||
|
return (valu);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID PASCAL
|
||
|
makedefaultdsc ()
|
||
|
{
|
||
|
register struct psop *p; /* parse stack operand structure */
|
||
|
|
||
|
emptydsc.itype = OPERAND;
|
||
|
p = &emptydsc.dsckind.opnd;
|
||
|
p->dtype = xltsymtoresult[REC];
|
||
|
p->dflag = KNOWN;
|
||
|
p->fixtype = FCONSTANT;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** checksegment - see if sreg is correct segment register for variable
|
||
|
*
|
||
|
* routine ();
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
char PASCAL CODESIZE
|
||
|
checksegment (
|
||
|
UCHAR sreg,
|
||
|
register struct ar *p
|
||
|
){
|
||
|
register SYMBOL FARSYM *segctx;
|
||
|
register SYMBOL FARSYM *segptr;
|
||
|
|
||
|
if (sreg != NOSEG) { /* NOseg never found */
|
||
|
|
||
|
/* Current Sreg assume */
|
||
|
segctx = regsegment[sreg];
|
||
|
|
||
|
/* Assume looking for left arg to : */
|
||
|
segptr = p->curresult->dsckind.opnd.dcontext;
|
||
|
|
||
|
if (!segptr) /* If no :, use segment */
|
||
|
segptr = p->curresult->dsckind.opnd.dsegment;
|
||
|
|
||
|
if (segptr && segctx) {
|
||
|
|
||
|
#ifndef FEATURE
|
||
|
if (segctx == pFlatGroup) /* flat space matchs all */
|
||
|
goto found;
|
||
|
#endif
|
||
|
|
||
|
/* if same segorg or ptr is segment ... and Same group */
|
||
|
|
||
|
if (segctx == segptr ||
|
||
|
|
||
|
(segptr->symkind == SEGMENT &&
|
||
|
segctx == segptr->symu.segmnt.grouptr)) {
|
||
|
found:
|
||
|
p->segovr = sreg;
|
||
|
p->curresult->dsckind.opnd.dcontext = segctx;
|
||
|
|
||
|
return (TRUE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** findsegment - find segment for variable
|
||
|
*
|
||
|
* routine ();
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
findsegment (
|
||
|
UCHAR dseg,
|
||
|
register struct ar *p
|
||
|
){
|
||
|
register struct psop *pso; /* parse stack operand structure */
|
||
|
|
||
|
pso = &(p->curresult->dsckind.opnd);
|
||
|
if ((M_DATA & p->rstype) &&
|
||
|
(pso->dsegment || pso->dcontext) &&
|
||
|
p->linktype != FCONSTANT && pso->fixtype != FOFFSET && emittext) {
|
||
|
/* Should find segment */
|
||
|
if (!checksegment (dseg, p)) {
|
||
|
/* If not in default */
|
||
|
checksegment (CSSEG, p);
|
||
|
checksegment (ESSEG, p);
|
||
|
checksegment (SSSEG, p);
|
||
|
checksegment (DSSEG, p);
|
||
|
#ifdef V386
|
||
|
if (cputype&P386)
|
||
|
{
|
||
|
checksegment (FSSEG, p);
|
||
|
checksegment (GSSEG, p);
|
||
|
}
|
||
|
#endif
|
||
|
if (p->segovr == NOSEG)
|
||
|
/* If not found,UNKNOWN */
|
||
|
p->segovr = NOSEG+1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** exprop - process expression operator
|
||
|
*
|
||
|
* exprop (p);
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
exprop (
|
||
|
register struct ar *p
|
||
|
){
|
||
|
register struct dscrec *pTop = itemptr;
|
||
|
|
||
|
p->curprec = (unsigned char)operprec; /* Get prec of new operator */
|
||
|
|
||
|
if (!p->lastitem) /* start */
|
||
|
pTop->prec = 0;
|
||
|
else
|
||
|
pTop->prec = p->lastitem->prec;
|
||
|
|
||
|
switch (pTop->dsckind.opr.oidx) {
|
||
|
|
||
|
case OPRPAR:
|
||
|
|
||
|
if (--p->parenlevel >= 0)
|
||
|
break;
|
||
|
|
||
|
/* Unmatched right paren is from count dup (xx) */
|
||
|
|
||
|
p->parenlevel = 0;
|
||
|
BACKC ();
|
||
|
dfree((char *)pTop);
|
||
|
p->exprdone = TRUE;
|
||
|
return;
|
||
|
|
||
|
case OPRBRK:
|
||
|
if (--p->bracklevel >= 0)
|
||
|
break;
|
||
|
|
||
|
p->exprdone = TRUE;
|
||
|
return;
|
||
|
|
||
|
case OPLPAR:
|
||
|
p->parenlevel++;
|
||
|
goto leftComm;
|
||
|
|
||
|
case OPLBRK:
|
||
|
|
||
|
p->bracklevel++;
|
||
|
leftComm:
|
||
|
/* See if could have no oper in which case kludge + */
|
||
|
|
||
|
if ((p->lastitem || p->addplusflag) &&
|
||
|
p->lastitem->itype != OPERATOR) {
|
||
|
|
||
|
/* make + OPERATOR */
|
||
|
opertype = OPPLUS;
|
||
|
createitem (OPERATOR, ISYM);
|
||
|
|
||
|
p->bracklevel--;
|
||
|
exprop(p);
|
||
|
p->bracklevel++;
|
||
|
p->lastprec = 6;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
pTop->prec = p->curprec;
|
||
|
break;
|
||
|
}
|
||
|
p->unaryflag = FALSE;
|
||
|
|
||
|
if (pTop->dsckind.opr.oidx == OPPLUS ||
|
||
|
pTop->dsckind.opr.oidx == OPMINUS) {
|
||
|
|
||
|
if (!p->lastitem)
|
||
|
p->unaryflag = TRUE;
|
||
|
|
||
|
else if (p->lastitem->itype == OPERATOR)
|
||
|
|
||
|
p->unaryflag = !(p->lastitem->dsckind.opr.oidx == OPRPAR ||
|
||
|
p->lastitem->dsckind.opr.oidx == OPRBRK);
|
||
|
}
|
||
|
|
||
|
if (p->unaryflag ||
|
||
|
(p->curprec > p->lastprec &&
|
||
|
!(pTop->dsckind.opr.oidx == OPRPAR ||
|
||
|
pTop->dsckind.opr.oidx == OPRBRK))) {
|
||
|
|
||
|
/* Push OPERATOR */
|
||
|
|
||
|
pTop->previtem = p->lastitem;
|
||
|
p->lastitem = pTop;
|
||
|
|
||
|
if (p->unaryflag) {
|
||
|
|
||
|
if (pTop->dsckind.opr.oidx == OPPLUS)
|
||
|
|
||
|
pTop->dsckind.opr.oidx = OPUNPLUS;
|
||
|
else
|
||
|
pTop->dsckind.opr.oidx = OPUNMINUS;
|
||
|
|
||
|
pTop->prec = p->lastprec;
|
||
|
p->lastprec = 10;
|
||
|
}
|
||
|
else
|
||
|
p->lastprec = p->curprec;
|
||
|
|
||
|
if (pTop->dsckind.opr.oidx == OPLPAR ||
|
||
|
pTop->dsckind.opr.oidx == OPLBRK)
|
||
|
|
||
|
p->lastprec = 0;
|
||
|
}
|
||
|
else /* Evaluate top OPERATOR */
|
||
|
|
||
|
evaluate (p);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** forceimmed - generate error if value is not immediate
|
||
|
*
|
||
|
* routine ();
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
VOID PASCAL CODESIZE
|
||
|
forceimmed (
|
||
|
register DSCREC *dsc
|
||
|
){
|
||
|
if (dsc->dsckind.opnd.mode != 4)
|
||
|
/* Must be constant */
|
||
|
errorc (E_CXP);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** exprconst - check for constant expression
|
||
|
*
|
||
|
* routine ();
|
||
|
*
|
||
|
* Entry
|
||
|
* Exit
|
||
|
* Returns
|
||
|
* Calls
|
||
|
*/
|
||
|
|
||
|
OFFSET PASCAL CODESIZE
|
||
|
exprconst ()
|
||
|
{
|
||
|
char sign;
|
||
|
register OFFSET ret;
|
||
|
|
||
|
ret = exprsmag(&sign);
|
||
|
|
||
|
if (sign) {
|
||
|
|
||
|
/* change to simple unary minus
|
||
|
* pso->doffset = 65535 - ret + 1; */
|
||
|
|
||
|
ret = -(long)ret;
|
||
|
|
||
|
if (!fArth32)
|
||
|
ret &= 0xffff;
|
||
|
}
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*** exprsmag - evaluate constant expression and return sign/magnitude
|
||
|
*
|
||
|
* ushort = exprsmag (sign, magnitude);
|
||
|
*
|
||
|
* Entry none
|
||
|
* Exit sign = TRUE if sign of result is set
|
||
|
* magnitude = magnitude of result
|
||
|
* Returns 16 bit integer result
|
||
|
* Calls expreval
|
||
|
*/
|
||
|
|
||
|
OFFSET PASCAL CODESIZE
|
||
|
exprsmag (
|
||
|
char *sign
|
||
|
){
|
||
|
register struct psop *pso; /* parse stack operand structure */
|
||
|
register OFFSET ret;
|
||
|
DSCREC *dsc;
|
||
|
|
||
|
dsc = expreval (&nilseg);
|
||
|
forceimmed (dsc);
|
||
|
pso = &(dsc->dsckind.opnd);
|
||
|
*sign = pso->dsign;
|
||
|
ret = pso->doffset;
|
||
|
|
||
|
dfree ((char *)dsc );
|
||
|
return (ret);
|
||
|
}
|