/*
* gencode.c - Code generator
*
* Copyright (C) 1997-2003 Gero Kuhlmann <gero@gkminix.han.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: gencode.c,v 1.4 2003/01/25 23:29:44 gkminix Exp $
*/
#define NEED_BINARY 1
#include "mknbi.h"
#include "mgl.h"
#include "gencode.h"
#include "opcodes.h"
/*
* Global variables
*/
addr_t dataptr = 0; /* Current data pointer */
addr_t constptr = 0; /* Current constant pointer */
addr_t codeptr = 0; /* Current code pointer */
addr_t startadr = 0; /* Start address */
/*
* The following structure is used to hold debugging information
*/
struct debuginfo {
addr_t addr;
int lineno;
};
/*
* Local variables
*/
static __u8 codebuf[CODESIZE]; /* Buffer for code */
static __u8 constbuf[DATASIZE]; /* Buffer for constant data */
static addr_t relocbuf[RELOCSIZE]; /* Buffer for reloc entries */
static unsigned int relocnum = 0; /* Current reloc entry */
static struct debuginfo debugbuf[DEBUGSIZE]; /* Buffer for debugging info */
static unsigned int debugnum = 0; /* Number of debug entries */
static addr_t stackofs = 0; /* Size of local stack area */
/*
************************************************************************
*
* Basic code generation routines
*
************************************************************************
*/
/*
* Put one byte into the code segment
*/
void putcode(c)
unsigned int c;
{
if (codeptr >= CODESIZE) {
prnerr0("code segment too large");
exit(EXIT_MGL_CODESIZE);
}
codebuf[codeptr++] = (__u8)(c & 0xff);
}
/*
* Put one byte into the data segment
*/
void putconst(c)
unsigned int c;
{
if (constptr >= CONSTSIZE) {
prnerr0("constant data segment too large");
exit(EXIT_MGL_CONSTSIZE);
}
constbuf[constptr++] = (__u8)(c & 0xff);
}
/*
* Put one string into the data segment
*/
addr_t putstring(str)
char *str;
{
char *cp;
addr_t i, j, end;
/* Let's see if we have the string in the constant buffer already */
end = constptr - strlen(str) + 1;
for (i = 0; i < end; i++) {
for (cp = str, j = i; *cp && j < constptr; cp++, j++)
if ((__u8)(*cp & 0xff) != constbuf[j])
break;
if (j < constptr && !*cp &&
(__u8)(*cp & 0xff) == constbuf[j])
return(i);
}
/* No, it's not in yet, create a new string in the buffer */
i = constptr;
for (cp = str; *cp; cp++)
putconst(*cp & 0xff);
putconst(0);
return(i);
}
/*
* Put one integer value into the code segment
*/
void putint(val)
long val;
{
putcode((unsigned int)(val & 0x00ff));
putcode((unsigned int)((val >> 8) & 0x00ff));
}
/*
* Set a new relocation entry at present code position
*/
void setreloc()
{
if (relocnum >= RELOCSIZE) {
prnerr0("relocation buffer overflow");
exit(EXIT_MGL_RELOCOVRFLOW);
}
relocbuf[relocnum++] = codeptr;
}
/*
* Put a jump opcode into the code segment
*/
void putjmp(dest, jmpcode)
addr_t dest;
unsigned int jmpcode;
{
addr_t distance;
distance = dest - codeptr - 2;
if (jmpcode == JMP_UNCOND) {
if (distance > -128 && distance < 128) {
putcode(OP_JMP_SHORT);
putcode((unsigned int)(distance & 0xff));
} else {
putcode(OP_JMP_NEAR);
putint((dest - codeptr - 2) & 0xffff);
}
} else {
#ifdef PARANOID
if (distance <= -128 || distance >= 128)
interror(40, "conditional jump outside of range");
#endif
putcode(OP_JMP_COND | jmpcode);
putcode((unsigned int)(distance & 0xff));
}
}
/*
* Put any displacement according to mod and r/m fields into the code segment
*/
static void putdisp(modrm, disp, usereloc)
unsigned int modrm;
addr_t disp;
int usereloc;
{
switch (modrm & OP_MOD_MASK) {
case OP_MOD_DIRECT:
/* Direct adressing */
if ((modrm & OP_RM_MASK) == OP_RM_BP) {
if (usereloc)
setreloc();
putint((long)disp);
}
break;
case OP_MOD_8BIT:
/* 8 bit displacement */
#ifdef PARANOID
if (usereloc)
interror(66, "cannot use 8 bit displacement with reloc");
#endif
putcode((unsigned int)(disp & 0xff));
break;
case OP_MOD_16BIT:
/* 16 bit displacement */
if (usereloc)
setreloc();
putint((long)disp);
break;
case OP_MOD_REG:
/* Register instruction, no displacement */
break;
}
}
/*
* Put an opcode into the code segment which is either a reg/reg, reg/mem
* or mem/reg operation.
*/
void putregop(op, srcreg, destreg, disp)
unsigned int op;
unsigned int srcreg;
unsigned int destreg;
addr_t disp;
{
unsigned char c1 = op & ~((unsigned int)(OP_WORD_MASK | OP_DIR_MASK));
unsigned char c2;
int usereloc;
/* Determine opcodes to use */
if ((destreg & REG_8BIT_FLAG) == 0 || (srcreg & REG_8BIT_FLAG) == 0)
c1 |= OP_WORD_MASK;
if ((srcreg & REG_MODRM_FLAG) == REG_MODRM_FLAG) {
/* operation is mem->reg */
c1 |= OP_DIR_MASK;
c2 = rmmid(destreg) | (srcreg & ~(unsigned int)REG_16BIT_MASK);
usereloc = ((srcreg & REG_RELOC_FLAG) == REG_RELOC_FLAG);
} else if ((destreg & REG_MODRM_FLAG) == REG_MODRM_FLAG) {
/* operation is reg->mem */
c2 = rmmid(srcreg) | (destreg & ~(unsigned int)REG_16BIT_MASK);
usereloc = ((destreg & REG_RELOC_FLAG) == REG_RELOC_FLAG);
} else {
/* operation is reg->reg */
#ifdef PARANOID
if ((destreg & REG_8BIT_FLAG) != (srcreg & REG_8BIT_FLAG))
interror(81, "register sizes don't match");
#endif
/* Optimize 'mov' away if possible */
if (op == OP_MOV && srcreg == destreg)
return;
c1 |= OP_DIR_MASK;
c2 = rmmid(destreg) | rmlow(srcreg) | OP_MOD_REG;
usereloc = FALSE;
}
/* Move into or out of AX is special */
if (op == OP_MOV &&
((srcreg == REG_AX && destreg == REG_16DISP) ||
(destreg == REG_AX && srcreg == REG_16DISP))) {
c1 &= OP_WORD_MASK | OP_DIR_MASK; /* Preserve word and dir bits */
c1 |= OP_MOV_AX;
putcode(c1);
if (usereloc)
setreloc();
putint((long)disp);
} else {
putcode(c1);
putcode(c2);
putdisp(c2, disp, usereloc);
}
}
/*
* Put an opcode with an immediate value into the code segment
*/
void putimmed(op, reg, val, disp)
unsigned int op;
unsigned int reg;
long val;
addr_t disp;
{
unsigned char c1 = OP_IMMED;
unsigned char c2 = op & OP_IMMED_MASK;
/* Do some optimization */
if ((op == OP_IMMED_ADD || op == OP_IMMED_SUB) && val == 0)
/* Don't optimize for arithmetic with the carry flag! */
return;
/* Immediate move into a register is special */
if (op == OP_MOV) {
if (val == 0 && (reg & REG_MODRM_FLAG) != REG_MODRM_FLAG) {
putregop(OP_XOR, reg, reg, 0);
} else if ((reg & REG_MODRM_FLAG) == REG_MODRM_FLAG) {
if ((reg & REG_8BIT_FLAG) == REG_8BIT_FLAG)
putcode(OP_MOV_MEMIM);
else
putcode(OP_MOV_MEMIM | OP_WORD_MASK);
c2 = (reg & (OP_MOD_MASK | OP_RM_MASK));
putcode(c2);
putdisp(c2, disp, (reg & REG_RELOC_FLAG) == REG_RELOC_FLAG);
if ((reg & REG_8BIT_FLAG) == REG_8BIT_FLAG)
putcode((unsigned int)(val & 0x00ff));
else
putint(val & 0xffff);
} else if ((reg & REG_8BIT_FLAG) == REG_8BIT_FLAG) {
putcode(OP_MOV_BREGIM | rmlow(reg));
putcode((unsigned int)(val & 0x00ff));
} else {
putcode(OP_MOV_WREGIM | rmlow(reg));
putint(val & 0xffff);
}
return;
}
/* Compute first byte of opcode */
if ((reg & REG_8BIT_FLAG) == 0) {
c1 |= OP_WORD_MASK;
if ((val & 0xff80) == 0 || (val & 0xff80) == 0xff80)
c1 |= OP_SIGN_MASK;
}
putcode(c1);
/* Compute second byte of opcode */
if ((reg & REG_MODRM_FLAG) == REG_MODRM_FLAG)
c2 |= reg & ~(unsigned int)OP_IMMED_MASK;
else
c2 |= OP_MOD_REG | rmlow(reg);
putcode(c2);
/* Output any displacement and the immediate value */
putdisp(c2, disp, (reg & REG_RELOC_FLAG) == REG_RELOC_FLAG);
if ((c1 & OP_WORD_MASK) == 0 || (c1 & OP_SIGN_MASK) == OP_SIGN_MASK)
putcode((unsigned int)(val & 0x00ff));
else
putint(val);
}
/*
* Determine the modrm value necessary to access data relative to a register
*/
unsigned int getmodrm(basereg, disp)
unsigned int basereg;
addr_t disp;
{
unsigned int modrm;
if ((disp & 0xff80) == 0 || (disp & 0xff80) == 0xff80)
modrm = OP_MOD_8BIT | REG_MODRM_FLAG;
else
modrm = OP_MOD_16BIT | REG_MODRM_FLAG;
switch (basereg) {
case REG_BX:
modrm |= OP_RM_BX;
break;
case REG_BP:
modrm |= OP_RM_BP;
break;
case REG_SI:
modrm |= OP_RM_IND | OP_RM_SI;
break;
case REG_DI:
modrm |= OP_RM_IND | OP_RM_DI;
break;
default:
modrm = REG_NONE;
break;
}
return(modrm);
}
/*
* Put an LEA or equivalent instruction into the output code segment
*/
void putlea(destreg, basereg, disp)
unsigned int destreg;
unsigned int basereg;
addr_t disp;
{
unsigned int modrm;
/* If we have a relocatable address, we must use an ADD instruction */
if ((basereg & REG_RELOC_FLAG) == REG_RELOC_FLAG) {
putregop(OP_MOV, (basereg & ~(unsigned int)REG_RELOC_FLAG), destreg, 0);
putimmed(OP_IMMED_ADD, (destreg | REG_RELOC_FLAG), (long)disp, 0);
return;
}
/* Otherwise we can use LEA */
if (disp == 0) {
putregop(OP_MOV, basereg, destreg, 0); /* mov destreg,basereg */
} else {
modrm = getmodrm(basereg, disp);
if (modrm != REG_NONE)
putregop(OP_LEA, destreg, modrm, disp);
else {
putregop(OP_MOV, basereg, destreg, 0);
putimmed(OP_IMMED_ADD, destreg, (long)disp, 0);
}
}
}
/*
* Put a push register instruction into the output code segment
*/
void putstackreg(reg, push, disp)
unsigned int reg;
int push;
addr_t disp;
{
unsigned int modrm;
if ((reg & REG_MODRM_FLAG) == 0) {
if ((reg & REG_8BIT_FLAG) == REG_8BIT_FLAG)
reg &= REG_8BIT_MASK;
if (push)
putcode(OP_PUSH_REG | rmlow(reg));
else
putcode(OP_POP_REG | rmlow(reg));
} else {
modrm = (push ? OP_MEM_PUSH : 0) |
(reg & ~((unsigned int)(OP_MOD_MASK | OP_RM_MASK)));
if (push)
putcode(OP_PUSH_MEM);
else
putcode(OP_POP_MEM);
putdisp(modrm, disp, FALSE);
}
}
/*
* Put a call to the runtime module into the output code segment
*/
void putfunc(func, stack)
int func;
addr_t stack;
{
if (func >= 0) {
putimmed(OP_MOV, REG_AH, func, 0);
putcode(OP_CALL);
putint((long)(ENTRYOFS - codeptr - 2));
}
if (stack > 0)
/* add sp,#stack */
putimmed(OP_IMMED_ADD, REG_SP, (long)stack, 0);
}
/*
************************************************************************
*
* Code to support different processors
*
************************************************************************
*/
/*
* Put a BOUND instruction into the output code segment. This will set
* a bounds check against the limits setup by the type given.
*/
void putbound(srcreg, tp)
unsigned int srcreg;
struct typesdef *tp;
{
int ax_pushed = FALSE;
/* No type given, cannot check */
if (tp == NULL || !isscalar(tp))
return;
/* Check if we have the bounds in the constant segment already */
if (tp->def.s.boundaddr < 0) {
tp->def.s.boundaddr = constptr;
putconst((unsigned char)(tp->def.s.min & 0x00ff));
putconst((unsigned char)((tp->def.s.min >> 8) & 0x00ff));
putconst((unsigned char)(tp->def.s.max & 0x00ff));
putconst((unsigned char)((tp->def.s.max >> 8) & 0x00ff));
}
/* If we have a 186+ processor, we can now just use the BOUND instruction */
if ((srcreg & REG_8BIT_FLAG) == REG_8BIT_FLAG) {
if (srcreg == REG_AH) {
putpush(REG_AX);
ax_pushed = TRUE;
}
putregop(OP_MOV, srcreg, REG_AL, 0);
putcode(OP_CBW);
srcreg = REG_AX;
}
if (is186) {
putcode(OP_BOUND);
putcode(OP_MOD_DIRECT | (srcreg & REG_16BIT_MASK) | OP_RM_BP);
putint(tp->def.s.boundaddr & 0xffff); /* const seg --> no reloc */
} else {
putimmed(OP_IMMED_CMP, srcreg, tp->def.s.min, 0);
putjmp(codeptr + 6, JMP_JC);
putimmed(OP_IMMED_CMP, srcreg, tp->def.s.max, 0);
putjmp(codeptr + 4, JMP_JBE);
putcode(OP_INT);
putcode(5);
}
if (ax_pushed)
putpop(REG_AX);
}
/*
* Put an enter instruction into the output code segment
*/
static void putenter(stack, level)
addr_t stack;
int level;
{
int i;
stackofs = stack & 0xffff;
if (is186) {
level = (level > 1 ? level - 1 : 0);
putcode(OP_ENTER);
putint((long)(stack & 0xffff));
putcode((unsigned int)(level & 0xff));
} else {
putpush(REG_BP); /* push bp */
if (level > 1) {
putregop(OP_MOV, REG_SP, REG_AX, 0); /* mov ax,sp */
for (i = 0; i < (level - 2); i++) {
/* sub bp,2 */
putimmed(OP_IMMED_SUB, REG_BP, 2, 0);
/* push [bp] */
putstackreg(REG_BP_8DISP, TRUE, 0);
}
putpush(REG_AX); /* push ax */
putregop(OP_MOV, REG_AX, REG_BP, 0); /* mov bp,ax */
} else
putregop(OP_MOV, REG_SP, REG_BP, 0); /* mov bp,sp */
if (stackofs > 0)
/* sub sp, stackofs */
putimmed(OP_IMMED_SUB, REG_SP, (long)stackofs, 0);
}
putpush(REG_DI);
putpush(REG_SI);
}
/*
* Put a leave instruction into the output code segment
*/
static void putleave()
{
putpop(REG_SI);
putpop(REG_DI);
if (is186)
putcode(OP_LEAVE);
else {
if (stackofs > 0)
/*
* Don't reset stackofs here - CODE_PROC_END might
* be called more than once in a procedure!
*/
putregop(OP_MOV, REG_BP, REG_SP, 0); /* mov sp,bp */
putcode(OP_POP_REG | rmlow(REG_BP)); /* pop bp */
}
}
/*
* Put a set-bit instruction into the output code segment
*/
void putsetbit(reg, cond)
unsigned int reg;
unsigned int cond;
{
if (!is386) {
putimmed(OP_MOV, reg, 1, 0);
putjmp(codeptr + 4, cond);
putregop(OP_XOR, reg, reg, 0);
} else {
putcode(OP_386);
putcode(OP_SET_COND | cond);
putcode(OP_MOD_REG | rmlow(reg));
}
}
/*
************************************************************************
*
* Conditional and nested commands
*
************************************************************************
*/
/*
* Structure used to keep track of nested commands
*/
#define MAX_JUMP 32 /* Maximum number of jumps */
typedef enum {
NEST_IF, /* Type for if command */
NEST_WHILE, /* Type for while command */
NEST_REPEAT, /* Type for repeat command */
NEST_SELECT /* Type for select command */
} nesttype;
struct nest {
nesttype type; /* Type of nesting statement */
int selecterr; /* Error in select, skip rest */
int jmpnum; /* Number of addresses to jumps */
addr_t jmptab[MAX_JUMP]; /* Pointers to jump instructions */
addr_t jmpdest[MAX_JUMP]; /* Addresses where jumps are going to */
addr_t loopaddr; /* Return address for loops */
int breaknum; /* Number of break statements */
int breaktabsize; /* Size of break address table */
addr_t *breaktab; /* Pointers to jumps for break */
struct nest *next;
};
static struct nest *nestlist = NULL; /* List of nested commands */
/*
* Put the condition handling code into the output buffer
*/
static void putcond(ep)
struct expr *ep;
{
struct meminfo di;
unsigned char jmpcode;
#ifdef PARANOID
if (exprtype(ep) != EXPR_BOOL)
interror(59, "invalid expression in condition");
#endif
/*
* We simply leave comparison to the boolean expression routine, but
* don't let it return a value. This doesn't work if we have a boolean
* leaf node. In that case we have to load the value into AL.
*/
di.t = NULL;
if (!isleaf(ep)) {
di.memtype = MEM_NOADDR | MEM_NOSIZE;
putboolexpr(ep, &di);
} else {
di.memtype = MEM_REGISTER | MEM_NOSIZE;
di.addr.r = REG_AL;
putboolexpr(ep, &di); /* mov al,val */
putregop(OP_OR, REG_AL, REG_AL, 0); /* or al,al */
}
/* Determine type of jump required */
switch (ep->opcode) {
case CMD_EQ: jmpcode = JMP_JZ; break;
case CMD_GT: jmpcode = JMP_JG; break;
case CMD_GE: jmpcode = JMP_JGE; break;
case CMD_LT: jmpcode = JMP_JL; break;
case CMD_LE: jmpcode = JMP_JLE; break;
case CMD_NE: jmpcode = JMP_JNZ; break;
default: jmpcode = JMP_JNZ; break; /* for AND, OR, XOR */
}
putjmp(codeptr + 5, jmpcode); /* jcond $+3 */
nestlist->jmptab[nestlist->jmpnum++] = codeptr;
codeptr += 3; /* provide space for jmp near */
}
/*
* Create a new nest structure
*/
static void newnest(type)
nesttype type;
{
struct nest *np;
np = (struct nest *)nbmalloc(sizeof(struct nest));
np->next = nestlist;
np->type = type;
np->loopaddr = codeptr;
nestlist = np;
}
/*
* Put out the code for the 'else' branch of an 'if' statement
*/
static void putelse()
{
#ifdef PARANOID
if (nestlist == NULL || nestlist->type != NEST_IF || nestlist->jmpnum != 1)
interror(62, "missing if statement for else");
#endif
nestlist->jmpdest[nestlist->jmpnum - 1] = codeptr + 3;/* where if jumps to */
nestlist->jmptab[nestlist->jmpnum++] = codeptr; /* mark new jump */
codeptr += 3; /* provide space for jmp near */
}
/*
* Put an item branch of a select command into the output code segment
*/
static void putitem(ep)
struct expr *ep;
{
int itemchar;
#ifdef PARANOID
if (nestlist == NULL || nestlist->type != NEST_SELECT)
interror(63, "no select statement for item");
if (ep == NULL || exprtype(ep) != EXPR_NUM || !isconst(ep) ||
ep->spec.cval.val.i > 9)
interror(64, "invalid item number");
#endif
/* Check that we don't have too many items in select statement */
if (nestlist->jmpnum >= MAX_JUMP) {
error("too many items in select statement");
nestlist->selecterr++;
}
if (nestlist->selecterr > 0)
return;
/* Let the last item terminate and point to this new one */
if (nestlist->jmpnum > 0) {
putjmp(nestlist->loopaddr, JMP_UNCOND);
nestlist->jmpdest[nestlist->jmpnum - 1] = codeptr;
}
/* Now put out a new compare instruction */
if (ep->spec.cval.val.i < 0)
itemchar = 0xff;
else
itemchar = (ep->spec.cval.val.i & 0x0f) + '0';
putimmed(OP_IMMED_CMP, REG_AL, itemchar, 0); /* cmp ax,itemchar */
putjmp(codeptr + 5, JMP_JZ); /* jz $+3 */
nestlist->jmptab[nestlist->jmpnum++] = codeptr;
codeptr += 3; /* provide space for jmp near */
}
/*
* Break command to jump to end of innermost loop command
*/
static void putbreak()
{
struct nest *np;
addr_t *newbuf;
int newsize;
/* Search innermost loop command */
for (np = nestlist; np != NULL; np = np->next)
if (np->type == NEST_SELECT ||
np->type == NEST_WHILE ||
np->type == NEST_REPEAT)
break;
if (np == NULL) {
error("invalid break command");
return;
}
/* Lets see, if our break pointer buffer is large enough */
if (np->breaktab == NULL || np->breaknum >= np->breaktabsize) {
newsize = np->breaktabsize + 32;
newbuf = (addr_t *)nbmalloc(sizeof(addr_t) * newsize);
if (np->breaktab != NULL) {
memcpy(newbuf, np->breaktab, sizeof(addr_t) * np->breaknum);
free(np->breaktab);
}
np->breaktabsize = newsize;
np->breaktab = newbuf;
}
/* Add current code pointer into the break table, and put out new jump */
np->breaktab[np->breaknum++] = codeptr;
codeptr += 3; /* provide space for jmp near */
}
/*
* End nested command
*/
static void putendnest(ep)
struct expr *ep;
{
struct nest *temp;
addr_t tmpadr;
int i;
#ifdef PARANOID
if (nestlist == NULL)
interror(65, "invalid endnest, nestlist empty");
#endif
/* Terminate current command */
switch (nestlist->type) {
case NEST_IF:
nestlist->jmpdest[nestlist->jmpnum - 1] = codeptr;
break;
case NEST_WHILE:
putjmp(nestlist->loopaddr, JMP_UNCOND);
nestlist->jmpdest[nestlist->jmpnum - 1] = codeptr;
break;
case NEST_REPEAT:
putcond(ep);
nestlist->jmpdest[nestlist->jmpnum - 1] = nestlist->loopaddr;
break;
case NEST_SELECT:
nestlist->jmpdest[nestlist->jmpnum - 1] = codeptr;
putjmp(nestlist->loopaddr, JMP_UNCOND);
break;
}
/* Resolve jump list */
tmpadr = codeptr;
for (i = 0; i < nestlist->jmpnum; i++) {
codeptr = nestlist->jmptab[i];
putjmp(nestlist->jmpdest[i], JMP_UNCOND);
}
/* Resolve break list */
if (nestlist->breaktab != NULL) {
for (i = 0; i < nestlist->breaknum; i++) {
codeptr = nestlist->breaktab[i];
putjmp(tmpadr, JMP_UNCOND);
}
free(nestlist->breaktab);
}
codeptr = tmpadr;
/* Remove current nested command from list */
temp = nestlist;
nestlist = nestlist->next;
free(temp);
}
/*
************************************************************************
*
* Determine the address of a variable
*
************************************************************************
*/
/*
* Return a pointer to the inuse flag corresponding to a register
*/
static int *getinuse(reg)
unsigned int reg;
{
switch (reg) {
case REG_BX:
return(&bx_inuse);
case REG_CX:
return(&cx_inuse);
case REG_DX:
return(&dx_inuse);
case REG_SI:
return(&si_inuse);
case REG_DI:
return(&di_inuse);
default:
return(NULL);
}
}
/*
* Set the meminfo record for a terminal symbol (i.e. a symbol which
* appears last in a record reference).
*/
static void setfirstinfo(sp, dp, basereg, sizereg)
struct sym *sp;
struct meminfo *dp;
unsigned int basereg;
unsigned int sizereg;
{
unsigned int basedisp = REG_BP_16DISP;
if (isfuncsym(sp)) {
#ifdef PARANOID
if (sp->def.f.ret == NULL || sp != curproc)
interror(72, "invalid function assignment");
#endif
/*
* Assigning a return value to a function means putting the value
* into some space provided by the caller. If the return value is
* non-scalar, there is a pointer to the return value space on the
* stack. With scalar return types, the space for the return value
* is directly located on the stack and accessible through BP.
*/
if (isnonscalar(sp->def.f.ret)) {
putregop(OP_MOV, REG_BP_16DISP, basereg, sp->def.f.retaddr);
dp->memtype = MEM_RELATIVE;
dp->addr.i = 0;
dp->addr.r = basereg;
if (sp->def.f.ret->type == EXPR_STRING && sizereg != REG_NONE) {
putregop(OP_MOV, REG_BP_16DISP, sizereg,
sp->def.f.retaddr + 2);
dp->memtype |= MEM_SIZEREG;
dp->size.i = 0;
dp->size.r = sizereg;
} else {
dp->memtype |= MEM_IMMEDIATE;
dp->size.i = sp->def.f.ret->size;
}
} else if (isscalar(sp->def.f.ret)) {
dp->memtype = MEM_RELATIVE;
dp->addr.i = sp->def.f.retaddr;
dp->addr.r = REG_BP;
}
#ifdef PARANOID
else
interror(50, "invalid function return type");
#endif
} else if (isvarsym(sp)) {
#ifdef PARANOID
if (sp->level > curlevel)
interror(73, "invalid symbol level");
#endif
/*
* Handle a variable as the destination. We have to consider the
* following cases:
*
* 1.) Variable is scalar and can be directly accessed because:
* - it's defined within the current context
* - it's passed to a function by value
* 2.) Variable is scalar and has been passed by reference. In this
* case we only have a pointer on the stack.
* 3.) Variable is non-scalar and can be directly accessed because it
* has been defined within the current context.
* 4.) Variable is non-scalar and has been passed by value. In this
* case we also only have a pointer to the variable value on the
* stack. However, the variable value has to be copied into a
* temporary memory first (usually located on the stack as well),
* so that the original variable remains untouched even when the
* called function modifies the variable.
* 5.) Variable is non-scalar and has been passed by reference. We
* also have a pointer on the stack like in case 4.) but without
* a temporary copy.
*
* Therefore, cases 1.) and 3.) can be handled together, like cases
* 2.), 4.) and 5.) are also handled the same.
*/
if ((isscalar(sp->def.v.t) && sp->def.v.attr != ATTR_REF) ||
sp->addr < 0) {
/*
* If we have a variable, which is defined within the current
* context, or a variable which has NOT been passed by re-
* ference (non-scalars are always passed with pointers, not
* by reference, which is not the same but very similar), we
* can directly access the variable without having to calculate
* the address at runtime.
*/
if (sp->level == curlevel) {
dp->memtype = MEM_RELATIVE;
dp->addr.i = sp->addr;
dp->addr.r = REG_BP;
} else if (sp->level <= 0) {
/* This catches static variables and record elements */
dp->memtype = MEM_ABSOLUTE;
dp->addr.i = sp->addr;
} else {
putregop(OP_MOV, REG_BP_16DISP, basereg,
-((sp->level - 1) * 2));
dp->memtype = MEM_RELATIVE;
dp->addr.i = sp->addr;
dp->addr.r = basereg;
}
} else {
/*
* It's somewhat more complicated with non-scalars: they
* can either be declared within the current context, or
* passed as an argument. In the latter case, we only have
* a pointer to the variable space on the stack. The first
* case is handled above (with sp->addr < 0).
* Also with all variables passed by reference we have a
* pointer on the stack instead of the actual value, so we
* have to load the pointer in order to be able to load
* or save a value.
*/
if (sp->level == curlevel) {
putregop(OP_MOV, REG_BP_16DISP, basereg, sp->addr);
dp->memtype = MEM_RELATIVE;
dp->addr.i = 0;
dp->addr.r = basereg;
} else if (sp->level <= 0) {
/* This catches record elements, should never happen */
dp->memtype = MEM_ABSOLUTE;
dp->addr.i = sp->addr;
} else {
switch (basereg) {
case REG_BX:
basedisp = REG_BX_16DISP;
break;
case REG_BP:
basedisp = REG_BP_16DISP;
break;
case REG_SI:
basedisp = REG_SI_16DISP;
break;
case REG_DI:
basedisp = REG_DI_16DISP;
break;
#ifdef PARANOID
default:
interror(110, "invalid base register");
#endif
}
putregop(OP_MOV, REG_BP_16DISP, basereg,
-((sp->level - 1) * 2));
putregop(OP_MOV, basedisp, basereg, sp->addr);
dp->memtype = MEM_RELATIVE;
dp->addr.i = 0;
dp->addr.r = basereg;
}
}
/*
* Determine the size of a non-scalar. If it's not a strings, a static
* variable, not an argument or an argument passed by value, we know
* the size from the variable type. Only for strings passed by refe-
* rence the destination size has been pushed onto the stack together
* with the address.
*/
if (isnonscalar(sp->def.v.t)) {
if (sp->def.v.t->type == EXPR_STRING &&
sp->def.v.attr == ATTR_REF &&
sp->addr >= 0 && sp->level > 0 &&
sizereg != REG_NONE) {
if (sp->level == curlevel) {
putregop(OP_MOV, REG_BP_16DISP, sizereg,
sp->addr + 2);
} else {
if (si_inuse || basereg == REG_SI)
putpush(REG_SI);
putregop(OP_MOV, REG_BP_16DISP, REG_SI,
-((sp->level - 1) * 2));
putregop(OP_MOV, REG_SI_16DISP, sizereg, sp->addr);
if (si_inuse || basereg == REG_SI)
putpop(REG_SI);
}
dp->memtype |= MEM_SIZEREG;
dp->size.r = sizereg;
} else {
dp->memtype |= MEM_IMMEDIATE;
dp->size.i = sp->def.v.t->size;
}
}
}
}
/*
* Set meminfo record for an array. The record should already contain
* the data for the base element (i.e. the array itself).
*/
static void setarrayinfo(vp, dp, basereg)
struct varinfo *vp;
struct meminfo *dp;
unsigned int basereg;
{
struct typesdef *elemtype = vp->type->def.a.basetype;
struct typesdef *indextype = vp->type->def.a.indextype;
struct meminfo di;
int *addrinuse, *sizeinuse;
long indexval;
#ifdef PARANOID
if (!isscalar(indextype))
interror(105, "invalid index type");
#endif
/* If the index is constant, we can compute it at compile time */
if (isconst(vp->index)) {
indexval = getord(vp->index);
if (indexval < indextype->def.s.min ||
indexval > indextype->def.s.max ||
(indexval - indextype->def.s.min) >= vp->type->def.a.elementnum) {
error("array subscript out of range");
return;
}
indexval -= indextype->def.s.min;
dp->addr.i += indexval * elemtype->size;
return;
}
/*
* Otherwise we have to compute it at runtime. For this we have to mark
* the base and size registers as used.
*/
addrinuse = NULL;
if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE)
addrinuse = getinuse(dp->addr.r);
if (addrinuse != NULL)
(*addrinuse)++;
sizeinuse = NULL;
if ((dp->memtype & MEM_SIZE_MASK) == MEM_SIZEREG)
sizeinuse = getinuse(dp->size.r);
if (sizeinuse != NULL)
(*sizeinuse)++;
/*
* We can now setup a new meminfo for the subexpression and let put
* out the code to compute the index.
*/
di.memtype = MEM_REGISTER | MEM_NOSIZE;
di.t = NULL;
switch (indextype->type) {
case EXPR_ENUM:
case EXPR_NUM:
di.addr.r = REG_AX;
putintexpr(vp->index, &di);
break;
case EXPR_BOOL:
di.addr.r = REG_AL;
putboolexpr(vp->index, &di);
putcode(OP_CBW);
break;
case EXPR_CHAR:
di.addr.r = REG_AL;
putcharexpr(vp->index, &di);
putcode(OP_CBW);
break;
default:
/* Can't happen, already checked above */
break;
}
/* Check that the index is within bounds */
putbound(REG_AX, indextype);
/* If the min of the index is not zero, we have to subtract the min */
if (indextype->def.s.min != 0)
putimmed(OP_SUB, REG_AX, indextype->def.s.min, 0);
/* The index is now in AX, so that we can compute the offset */
if (elemtype->size == 2) {
putcode(OP_SHIFT_1);
putcode(OP_MOD_REG | OP_SHIFT_SHL | rmlow(REG_AX));
} else if (elemtype->size == 4) {
putcode(OP_SHIFT_1);
putcode(OP_MOD_REG | OP_SHIFT_SHL | rmlow(REG_AX));
putcode(OP_SHIFT_1);
putcode(OP_MOD_REG | OP_SHIFT_SHL | rmlow(REG_AX));
} else if (elemtype->size > 1) {
if (dx_inuse)
putpush(REG_DX);
putimmed(OP_MOV, REG_DX, elemtype->size, 0);
putcode(OP_NONIM | OP_WORD_MASK);
putcode(OP_NONIM_MUL | OP_MOD_REG | rmlow(REG_DX));
if (dx_inuse)
putpop(REG_DX);
}
/* We have the offset in AX now, so we can finally set the meminfo */
if ((dp->memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
putimmed(OP_MOV, basereg, dp->addr.i, 0);
putregop(OP_ADD, REG_AX, basereg, 0);
dp->memtype = (dp->memtype & ~MEM_ADR_MASK) | MEM_RELATIVE;
dp->addr.i = 0;
dp->addr.r = basereg;
} else if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE) {
if (dp->addr.r == REG_BP) {
putlea(basereg, dp->addr.r, dp->addr.i);
putregop(OP_ADD, REG_AX, basereg, 0);
dp->addr.r = basereg;
} else {
putlea(dp->addr.r, dp->addr.r, dp->addr.i);
putregop(OP_ADD, REG_AX, dp->addr.r, 0);
}
dp->addr.i = 0;
}
/* Restore the inuse indicators */
if (addrinuse)
(*addrinuse)--;
if (sizeinuse)
(*sizeinuse)--;
}
/*
* Set meminfo record for a record variable. The meminfo should already contain
* the data for the base element (i.e. the record itself). This is very similar
* but not the same as 'setfirstinfo'.
*/
static void setrecordinfo(vp, dp, basereg)
struct varinfo *vp;
struct meminfo *dp;
unsigned int basereg;
{
struct sym *sp = vp->symbol;
#ifdef PARANOID
if (sp == NULL || (!isvarsym(sp) && !isfuncsym(sp)))
interror(107, "invalid type for record element");
if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE && dp->addr.r == REG_BP)
interror(108, "BP register not allowed for record elements");
#endif
/* Handle function return value. This always has to be a terminal. */
if (isfuncsym(sp)) {
#ifdef PARANOID
if (sp->def.f.ret == NULL ||
sp->def.f.ret->type != EXPR_RECORD || sp != curproc)
interror(106, "invalid function assignment");
#endif
if ((dp->memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
putregop(OP_MOV, REG_BP_16DISP, basereg, sp->def.f.retaddr);
dp->memtype = (dp->memtype & ~MEM_ADR_MASK) | MEM_RELATIVE;
dp->addr.r = basereg;
} else if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE)
putregop(OP_ADD, REG_BP_16DISP, dp->addr.r, sp->def.f.retaddr);
return;
}
/* Handle variable record elements */
#ifdef PARANOID
if (sp->def.v.t->type != EXPR_RECORD)
interror(109, "invalid record");
#endif
if (sp->level == curlevel) {
if ((dp->memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
dp->memtype = (dp->memtype & ~MEM_ADR_MASK) | MEM_RELATIVE;
dp->addr.r = REG_BP;
} else if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE) {
putregop(OP_ADD, REG_BP, dp->addr.r, 0);
}
} else if (sp->level > 0) {
if ((dp->memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
putregop(OP_MOV, REG_BP_16DISP, basereg,
-((sp->level - 1) * 2));
dp->memtype = (dp->memtype & ~MEM_ADR_MASK) | MEM_RELATIVE;
dp->addr.r = basereg;
} else {
putregop(OP_ADD, REG_BP_16DISP, dp->addr.r,
-((sp->level - 1) * 2));
}
}
dp->addr.i += sp->addr;
}
/*
* Set a meminfo record according to the variable pointed to in the
* expression.
*/
void setmeminfo(ep, dp, basereg, sizereg)
struct expr *ep;
struct meminfo *dp;
unsigned int basereg;
unsigned int sizereg;
{
struct varinfo *vp, *prev;
#ifdef PARANOID
if (!isvariable(ep))
interror(57, "expression is not a variable");
#endif
/*
* Find out which register we can use in case we need to calculate an
* address.
*/
if (basereg == REG_NONE) {
if (!di_inuse)
basereg = REG_DI;
else if (!si_inuse)
basereg = REG_SI;
else if (!bx_inuse)
basereg = REG_BX;
else if (!cx_inuse)
basereg = REG_CX;
else if (!dx_inuse)
basereg = REG_DX;
else
basereg = REG_AX;
}
/*
* Handle the first element in the varinfo record (which is the
* last element in a record reference).
*/
#ifdef PARANOID
if (ep->spec.var.symbol == NULL ||
(!isvarsym(ep->spec.var.symbol) && !isfuncsym(ep->spec.var.symbol)))
interror(103, "missing symbol in variable specification");
#endif
setfirstinfo(ep->spec.var.symbol, dp, basereg, sizereg);
if (ep->spec.var.index != NULL)
setarrayinfo(&(ep->spec.var), dp, basereg);
/*
* Now scan through the variable info list and handle all record references.
* At this point, if we have a record element, the meminfo record contains
* the offset to the element from the start of the record.
*/
prev = &(ep->spec.var);
for (vp = ep->spec.var.next; vp != NULL; vp = vp->next) {
#ifdef PARANOID
if (vp->symbol == NULL || !isvarsym(vp->symbol))
interror(104, "missing symbol in variable specification");
#endif
prev = vp;
setrecordinfo(vp, dp, basereg);
if (vp->index != NULL)
setarrayinfo(vp, dp, basereg);
}
/*
* Finally we have to find out if the variable is in the static area, so
* that we have to set the relocation flag. Also save the variable type
* into the meminfo record for later use by the bound checker.
*/
if (prev->symbol->level <= 0)
dp->memtype |= MEM_RELOC_FLAG;
dp->t = ep->type;
}
/*
************************************************************************
*
* Interface to parser
*
************************************************************************
*/
/*
* Save debug info for current statement
*/
static void setdebug()
{
if (debug && (debugnum == 0 || debugbuf[debugnum - 1].lineno != lineno)) {
debugbuf[debugnum].addr = codeptr;
debugbuf[debugnum].lineno = lineno;
if (++debugnum >= DEBUGSIZE) {
prnerr0("debugging info buffer overflow");
exit(EXIT_MGL_DEBUGOVRFLOW);
}
}
}
/*
* Compile a timeout value and put it into DX.
*/
static void puttimeout(ep)
struct expr *ep;
{
struct meminfo di;
if (ep != NULL) {
di.memtype = MEM_REGISTER;
di.addr.r = REG_DX;
di.t = NULL;
putintexpr(ep, &di);
} else
putregop(OP_XOR, REG_DX, REG_DX, 0);
}
/*
* With an assignment to a complex variable we have a problem: the
* expression might involve the variable itself, like: a := b + a.
* In this case we cannot allow the expression calculation routines
* to put the result directly into the destination, but have to
* use a temporary area as the initial destination. After doing the
* expression the data has to be copied out of the temp area into
* the final variable.
*/
static void handlecomplex(ep, dp)
struct expr *ep;
struct meminfo *dp;
{
struct meminfo di;
unsigned int tempreg = REG_DI;
unsigned int modrm;
int *addrinuse = NULL;
int *sizeinuse = NULL;
int needcopy;
addr_t size;
/*
* We don't need to copy if the expression is a leaf node, or it's
* a function of which we know that it doesn't use any complex
* variables:
* 1.) it has to be an internal function
* 2.) it has to have no string arguments
* Presently, there is only one such function: getbootp.
*/
needcopy = (!isleaf(ep) && ep->opcode != CMD_GETBOOTP);
di = *dp;
if (needcopy) {
/*
* If the expression is a leaf node, there is no need to use a
* temporary buffer. Otherwise we have to provide temp space on
* the stack. We don't have to care much about used registers,
* because this routine is only called from 'docmd'.
*/
addrinuse = NULL;
if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE)
addrinuse = getinuse(dp->addr.r);
if (addrinuse != NULL)
/* Prevent base pointer to get used by expression */
(*addrinuse)++;
sizeinuse = NULL;
if ((dp->memtype & MEM_SIZE_MASK) == MEM_SIZEREG)
sizeinuse = getinuse(dp->size.r);
if (sizeinuse != NULL)
/* Prevent size register to get used by expression */
(*sizeinuse)++;
if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE)
tempreg = (dp->addr.r == REG_DI ? REG_SI :
dp->addr.r == REG_BX ? REG_DI : REG_BX);
else
tempreg = REG_DI;
if ((dp->memtype & MEM_SIZE_MASK) == MEM_SIZEREG)
putregop(OP_SUB, dp->size.r, REG_SP, 0);
else {
if (exprtype(ep) == EXPR_STRING)
size = (dp->size.i + 2) & 0xfffe;
else
size = (dp->size.i + 1) & 0xfffe;
putimmed(OP_SUB, REG_SP, size, 0);
}
putregop(OP_MOV, REG_SP, tempreg, 0);
di.memtype = (di.memtype & ~(MEM_ADR_MASK | MEM_RELOC_FLAG)) |
MEM_RELATIVE;
di.addr.i = 0;
di.addr.r = tempreg;
}
/* Process the expression */
switch (exprtype(ep)) {
case EXPR_STRING:
putstrexpr(ep, &di, FALSE);
break;
case EXPR_ARRAY:
case EXPR_RECORD:
putcomplexexpr(ep, &di);
break;
default:
break;
}
/*
* If we used a temporary data area, copy the result into the variable.
* Here we can safely use any register we want, because this routine
* returns back to the parser.
*/
if (needcopy) {
if (addrinuse != NULL)
(*addrinuse)--;
if (sizeinuse != NULL)
(*sizeinuse)--;
if (tempreg == REG_DI) {
putregop(OP_MOV, REG_DI, REG_AX, 0);
tempreg = REG_AX;
}
/* Set destination address into DI */
if ((dp->memtype & MEM_ADR_MASK) == MEM_RELATIVE) {
modrm = 0;
if ((dp->memtype & MEM_RELOC_FLAG) == MEM_RELOC_FLAG)
modrm = REG_RELOC_FLAG;
putlea(REG_DI, dp->addr.r | modrm, dp->addr.i);
} else if ((dp->memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
putcode(OP_MOV_WREGIM | rmlow(REG_DI));
if ((dp->memtype & MEM_RELOC_FLAG) == MEM_RELOC_FLAG)
setreloc();
putint(dp->addr.i);
}
/*
* Set source register to our temporary area, the expression is
* not allowed to touch the base register in 'di', e.g. our tempreg.
*/
if (tempreg == REG_SI) {
putregop(OP_MOV, REG_SI, REG_AX, 0); /* Save it for later */
tempreg = REG_AX;
} else
putregop(OP_MOV, tempreg, REG_SI, 0);
/* Set copy size */
if ((dp->memtype & MEM_SIZE_MASK) == MEM_SIZEREG)
putregop(OP_MOV, dp->size.r, REG_CX, 0);
else {
if (exprtype(ep) == EXPR_STRING)
size = (dp->size.i + 2) & 0xfffe;
else
size = (dp->size.i + 1) & 0xfffe;
putimmed(OP_MOV, REG_CX, size, 0);
}
/* Now do the copy */
putcode(OP_REP);
putcode(OP_MOVSB);
/* Restore the stack */
putregop(OP_MOV, REG_SI, REG_SP, 0);
}
}
/*
* Handle high level commands and break them down into smaller pieces.
* This is basically one big switch statement.
*/
void docmd(cmd, sp, ep1, ep2)
codes cmd;
struct sym *sp;
struct expr *ep1;
struct expr *ep2;
{
struct meminfo di;
setdebug();
switch (cmd) {
case CODE_PROC_START:
/* Start a new procedure or function */
putenter(sp->addr, sp->level);
break;
case CODE_PROC_END:
/* Quit current procedure and return to previous procedure */
if (isfuncsym(curproc) && curproc->def.f.ret != NULL) {
if (isscalar(curproc->def.f.ret) &&
curproc->def.f.ret->size < 2)
putregop(OP_MOV, REG_BP_16DISP | REG_8BIT_FLAG,
REG_AL, curproc->def.f.retaddr);
else
putregop(OP_MOV, REG_BP_16DISP,
REG_AX, curproc->def.f.retaddr);
}
putleave();
putcode(OP_RET);
break;
case CODE_RESTART:
/* Restart is simply a jump to the beginning of current menu */
#ifdef PARANOID
if (curproc == NULL || !isfuncsym(curproc))
interror(60, "invalid curproc");
#endif
putjmp(curproc->def.f.restartaddr, JMP_UNCOND);
break;
case CODE_CALL_PROC:
/* Call a procedure */
#ifdef PARANOID
if (!isproc(ep1))
interror(71, "invalid procedure call");
#endif
di.memtype = MEM_NOADDR | MEM_NOSIZE;
di.t = NULL;
putproc(ep1, &di);
break;
case CODE_ASSIGN:
setmeminfo(ep1, &di, REG_DI, REG_CX);
switch (exprtype(ep2)) {
case EXPR_NUM:
case EXPR_ENUM:
/* Integer assignment */
putintexpr(ep2, &di);
break;
case EXPR_CHAR:
/* Character assignment */
putcharexpr(ep2, &di);
break;
case EXPR_BOOL:
/* Boolean assignment */
putboolexpr(ep2, &di);
break;
case EXPR_STRING:
case EXPR_ARRAY:
case EXPR_RECORD:
/* Non-scalar assignment */
handlecomplex(ep2, &di);
break;
#ifdef PARANOID
default:
interror(69, "invalid symbol type in assignment");
break;
#endif
}
break;
case CODE_INT_PRINT:
/* Print integer number */
di.memtype = MEM_REGISTER;
di.addr.r = REG_CX;
di.t = NULL;
putintexpr(ep1, &di);
putfunc(FUNC_iprint, 0);
break;
case CODE_STR_PRINT:
/* Print string */
di.memtype = MEM_REGISTER;
di.addr.r = REG_SI;
di.t = NULL;
putstrexpr(ep1, &di, FALSE);
putfunc(FUNC_sprint, 0);
break;
case CODE_CHAR_PRINT:
/* Print character */
di.memtype = MEM_REGISTER;
di.addr.r = REG_CL;
di.t = NULL;
putcharexpr(ep1, &di);
putfunc(FUNC_cprint, 0);
break;
case CODE_INT_GET:
/* Read an integer number */
puttimeout(ep2);
setmeminfo(ep1, &di, REG_NONE, REG_NONE);
putfunc(FUNC_iget, 0);
putsaveintreg(REG_AX, &di);
break;
case CODE_CHAR_GET:
/* Read a character */
puttimeout(ep2);
setmeminfo(ep1, &di, REG_NONE, REG_NONE);
putfunc(FUNC_cget, 0);
putsaveintreg(REG_AL, &di);
break;
case CODE_STR_GET:
/* Read a string */
puttimeout(ep2);
setmeminfo(ep1, &di, REG_NONE, REG_CX);
if ((di.memtype & MEM_ADR_MASK) == MEM_ABSOLUTE) {
putcode(OP_MOV_WREGIM | rmlow(REG_DI));
if ((di.memtype & MEM_RELOC_FLAG) == MEM_RELOC_FLAG)
setreloc();
putint((long)(di.addr.i & 0xffff));
#ifdef PARANOID
} else if ((di.memtype & MEM_ADR_MASK) != MEM_RELATIVE) {
interror(48, "invalid meminfo in get string");
#endif
} else {
putlea(REG_DI, di.addr.r, di.addr.i);
}
if ((di.memtype & MEM_SIZE_MASK) == MEM_IMMEDIATE)
putimmed(OP_MOV, REG_CX, (long)di.size.i, 0);
else
putregop(OP_MOV, di.size.r, REG_CX, 0);
putfunc(FUNC_sget, 0);
break;
case CODE_PUSH_IPADDR:
/* Push an IP address onto the stack */
if (sp == NULL) {
putregop(OP_XOR, REG_CX, REG_CX, 0); /* xor cx,cx */
putpush(REG_CX); /* push cx */
putpush(REG_CX); /* push cx */
} else {
putcode(OP_MOV_WREGIM | rmlow(REG_CX)); /* mov cx,val */
putint((*(__u16 *)&(sp->name[2])) & 0xffff);
putpush(REG_CX); /* push cx */
putcode(OP_MOV_WREGIM | rmlow(REG_CX)); /* mov cx,val */
putint((*(__u16 *)&(sp->name[0])) & 0xffff);
putpush(REG_CX); /* push cx */
}
break;
case CODE_LOAD:
/* Load new file. Two IP addresses are on the stack already */
di.memtype = MEM_REGISTER;
di.addr.r = REG_AX;
di.t = NULL;
putstrexpr(ep1, &di, FALSE);
putpush(REG_AX);
putfunc(FUNC_load, 10);
break;
case CODE_IF:
/* Start IF command */
newnest(NEST_IF);
putcond(ep1);
break;
case CODE_ELSE:
/* Start ELSE command */
putelse();
break;
case CODE_WHILE:
/* Start WHILE command */
newnest(NEST_WHILE);
putcond(ep1);
break;
case CODE_REPEAT:
/* Start REPEAT command */
newnest(NEST_REPEAT);
break;
case CODE_SELECT:
/* Start select command */
newnest(NEST_SELECT);
puttimeout(ep1);
putfunc(FUNC_getsel, 0);
break;
case CODE_ITEM:
/* Start new ITEM selection */
putitem(ep1);
break;
case CODE_BREAK:
/* Terminate current nested command */
putbreak();
break;
case CODE_ENDNEST:
/* Terminate current nesting command */
putendnest(ep1);
break;
}
}
/*
************************************************************************
*
*
*
************************************************************************
*/
/*
* Main routine of the code generator. It parses the input file and
* assembles the output binary.
*/
int gencode(infile)
char *infile;
{
char *fname;
long codesize;
long relocval;
long debugptr;
int outfile;
unsigned int j;
/* Clear code and data buffers and write runtime module into code segment */
memset(codebuf, OP_NOP, sizeof(codebuf));
memset(constbuf, 0, sizeof(constbuf));
memcpy(codebuf, runtime_data, runtime_data_size);
/* Setup global variables */
codeptr = runtime_data_size;
dataptr = (MAX_STR_LEN + 2) & 0xfffe;
constptr = 0;
/* Parse the input file */
yylexinit(infile);
yyparse();
yylexterm();
if (errors > 0) {
prnerr1("%d error(s) encountered, aborting", errors);
exit(EXIT_MGL_COMPERRS);
}
if (warnings > 0)
prnerr1("%d warning(s)", warnings);
/*
* Write the debugging info into the constant segment
*/
debugptr = 0;
if (debug) {
debugptr = constptr;
for (j = 0; j < debugnum; j++) {
putconst((unsigned int)(debugbuf[j].addr & 0x00ff));
putconst((unsigned int)((debugbuf[j].addr >> 8) & 0x00ff));
putconst((unsigned int)(debugbuf[j].lineno & 0x00ff));
putconst((unsigned int)((debugbuf[j].lineno >> 8) & 0x00ff));
}
putconst(0); /* end marker */
putconst(0);
}
/*
* Scan through the relocation table and add the value at the relocation
* to the size of the constant segment
*/
for (j = 0; j < relocnum; j++) {
relocval = codebuf[relocbuf[j]] & 0x00ff;
relocval += ((codebuf[relocbuf[j]+1]) << 8) & 0xff00;
relocval += constptr;
codebuf[relocbuf[j]] = (relocval & 0x00ff);
codebuf[relocbuf[j]+1] = ((relocval >> 8) & 0x00ff);
}
/* Write the start address into the code module */
if (startadr == 0) {
prnerr0("no start screen defined");
exit(EXIT_MGL_NOSTART);
}
assign(*((__u16 *)&(codebuf[STARTOFS])), htot(startadr & 0xffff));
/* Align the data segment to a segment boundary and write code size */
codesize = (codeptr + 0x000f) & 0xfff0;
assign(*((__u16 *)&(codebuf[CODESIZEOFS])), htot(codesize & 0xffff));
assign(*((__u16 *)&(codebuf[CONSTSIZEOFS])), htot(constptr & 0xffff));
assign(*((__u16 *)&(codebuf[DATASIZEOFS])), htot(dataptr & 0xffff));
assign(*((__u16 *)&(codebuf[DEBUGOFS])), htot(debugptr & 0xffff));
/* Open temporary output file */
if ((fname = tempnam(NULL, "mgl")) == NULL) {
prnerr0("unable to generate name of temporary file");
exit(EXIT_TEMPNAME);
}
if ((outfile = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
0644)) < 0) {
prnerr1("unable to open temporary file %s", fname);
exit(EXIT_OPEN);
}
if (verbose < 2)
unlink(fname);
free(fname);
/* Write all buffers into output file */
(void)nbwrite(codebuf, (unsigned int)codesize, outfile);
(void)nbwrite(constbuf, (unsigned int)constptr, outfile);
if (lseek(outfile, 0L, 0) < 0) {
prnerr0("unable to seek in temporary file");
exit(EXIT_SEEK);
}
return(outfile);
}
syntax highlighted by Code2HTML, v. 0.9.1