466 lines
8.9 KiB
C
466 lines
8.9 KiB
C
/* $Id$ */
|
|
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
/* I N T E R M E D I A T E C O D E
|
|
*
|
|
* I C _ A U X . C
|
|
*/
|
|
|
|
|
|
|
|
#include <em_pseu.h>
|
|
#include <em_spec.h>
|
|
#include <em_mnem.h>
|
|
#include "../share/types.h"
|
|
#include "../share/global.h"
|
|
#include "../share/debug.h"
|
|
#include "../share/def.h"
|
|
#include "../share/aux.h"
|
|
#include "ic.h"
|
|
#include "ic_io.h"
|
|
#include "ic_lookup.h"
|
|
#include "../share/alloc.h"
|
|
#include "ic_aux.h"
|
|
|
|
|
|
|
|
/* opr_size */
|
|
|
|
offset opr_size(instr)
|
|
short instr;
|
|
{
|
|
switch(instr) {
|
|
case op_loe:
|
|
case op_ste:
|
|
case op_ine:
|
|
case op_dee:
|
|
case op_zre:
|
|
return (offset) ws;
|
|
case op_lde:
|
|
case op_sde:
|
|
return (offset) 2*ws;
|
|
case op_lae:
|
|
case op_fil:
|
|
case op_gto:
|
|
return (offset) UNKNOWN_SIZE;
|
|
default:
|
|
error("illegal operand of opr_size: %d", instr);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
|
|
/* dblockdef */
|
|
|
|
STATIC offset argsize(arg)
|
|
arg_p arg;
|
|
{
|
|
/* Compute the size (in bytes) that the given initializer
|
|
* will occupy.
|
|
*/
|
|
|
|
offset s;
|
|
argb_p argb;
|
|
|
|
switch(arg->a_type) {
|
|
case ARGOFF:
|
|
/* See if value fits in a short */
|
|
if ((short) arg->a_a.a_offset == arg->a_a.a_offset) {
|
|
return ws;
|
|
} else {
|
|
return 2*ws;
|
|
}
|
|
case ARGINSTRLAB:
|
|
case ARGOBJECT:
|
|
case ARGPROC:
|
|
return ps; /* pointer size */
|
|
case ARGSTRING:
|
|
/* strings are partitioned into pieces */
|
|
s = 0;
|
|
for (argb = &arg->a_a.a_string; argb != (argb_p) 0;
|
|
argb = argb->ab_next) {
|
|
s += argb->ab_index;
|
|
}
|
|
return s;
|
|
case ARGICN:
|
|
case ARGUCN:
|
|
case ARGFCN:
|
|
return arg->a_a.a_con.ac_length;
|
|
default:
|
|
assert(FALSE);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
STATIC offset blocksize(pseudo,args)
|
|
byte pseudo;
|
|
arg_p args;
|
|
{
|
|
/* Determine the number of bytes of a datablock */
|
|
|
|
arg_p arg;
|
|
offset sum;
|
|
|
|
switch(pseudo) {
|
|
case DHOL:
|
|
case DBSS:
|
|
if (args->a_type != ARGOFF) {
|
|
error("offset expected");
|
|
}
|
|
return args->a_a.a_offset;
|
|
case DCON:
|
|
case DROM:
|
|
sum = 0;
|
|
for (arg = args; arg != (arg_p) 0; arg = arg->a_next) {
|
|
/* Add the sizes of all initializers */
|
|
sum += argsize(arg);
|
|
}
|
|
return sum;
|
|
default:
|
|
assert(FALSE);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
STATIC arg_p copy_arg(arg)
|
|
arg_p arg;
|
|
{
|
|
/* Copy one argument */
|
|
|
|
arg_p new;
|
|
|
|
assert(arg->a_type == ARGOFF);
|
|
new = newarg(ARGOFF);
|
|
new->a_a.a_offset = arg->a_a.a_offset;
|
|
return new;
|
|
}
|
|
|
|
|
|
|
|
STATIC arg_p copy_rom(args)
|
|
arg_p args;
|
|
{
|
|
/* Make a copy of the values of a rom,
|
|
* provided that the rom contains only integer values,
|
|
*/
|
|
|
|
arg_p arg, arg2, argh;
|
|
|
|
for (arg = args; arg != (arg_p) 0; arg = arg->a_next) {
|
|
if (arg->a_type != ARGOFF) {
|
|
return (arg_p) 0;
|
|
}
|
|
}
|
|
/* Now make the copy */
|
|
arg2 = argh = copy_arg(args);
|
|
for (arg = args->a_next; arg != (arg_p) 0; arg = arg->a_next) {
|
|
arg2->a_next = copy_arg(arg);
|
|
arg2 = arg2->a_next;
|
|
}
|
|
return argh;
|
|
}
|
|
|
|
|
|
|
|
dblockdef(db,n,lnp)
|
|
dblock_p db;
|
|
int n;
|
|
line_p lnp;
|
|
{
|
|
/* Process a data block defining occurrence */
|
|
|
|
byte m;
|
|
|
|
switch(n) {
|
|
case ps_hol:
|
|
m = DHOL;
|
|
break;
|
|
case ps_bss:
|
|
m = DBSS;
|
|
break;
|
|
case ps_con:
|
|
m = DCON;
|
|
break;
|
|
case ps_rom:
|
|
m = DROM;
|
|
break;
|
|
default:
|
|
assert(FALSE);
|
|
}
|
|
db->d_pseudo = m;
|
|
db->d_size = blocksize(m, ARG(lnp));
|
|
if (m == DROM) {
|
|
/* We keep the values of a rom block in the data block
|
|
* table if the values consist of integers only.
|
|
*/
|
|
db->d_values = copy_rom(ARG(lnp));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* combine */
|
|
|
|
combine(db,l1,l2,pseu)
|
|
dblock_p db;
|
|
line_p l1,l2;
|
|
byte pseu;
|
|
{
|
|
/* Combine two successive ROMs/CONs (without a data label
|
|
* in between into a single ROM. E.g.:
|
|
* xyz
|
|
* rom 3,6,9,12
|
|
* rom 7,0,2
|
|
* is changed into:
|
|
* xyz
|
|
* rom 3,6,9,12,7,0,2
|
|
*/
|
|
|
|
arg_p v;
|
|
|
|
db->d_size += blocksize(pseu,ARG(l2));
|
|
/* db is the data block that was already assigned to the
|
|
* first rom/con. The second one is not assigned a new
|
|
* data block of course, as the two are combined into
|
|
* one instruction.
|
|
*/
|
|
if (pseu == DROM && db->d_values != (arg_p) 0) {
|
|
/* The values contained in a ROM are only copied
|
|
* to the data block if they may be useful to us
|
|
* (e.g. they certainly may not be strings). In our
|
|
* case it means that both ROMs must have useful
|
|
* arguments.
|
|
*/
|
|
for (v = db->d_values; v->a_next != (arg_p) 0; v = v->a_next);
|
|
/* The first rom contained useful arguments. v now points to
|
|
* its last argument. Append the arguments of the second
|
|
* rom to this list. If the second rom has arguments that are
|
|
* not useful, throw away the entire list (we want to copy
|
|
* everything or nothing).
|
|
*/
|
|
if ((v->a_next = copy_rom(ARG(l2))) == (arg_p) 0) {
|
|
oldargs(db->d_values);
|
|
db->d_values = (arg_p) 0;
|
|
}
|
|
}
|
|
for (v = ARG(l1); v->a_next != (arg_p) 0; v = v->a_next);
|
|
/* combine the arguments of both instructions. */
|
|
v->a_next = ARG(l2);
|
|
ARG(l2) = (arg_p) 0;
|
|
}
|
|
|
|
|
|
|
|
/* arglist */
|
|
|
|
STATIC arg_string(length,abp)
|
|
offset length;
|
|
register argb_p abp;
|
|
{
|
|
|
|
while (length--) {
|
|
if (abp->ab_index == NARGBYTES)
|
|
abp = abp->ab_next = newargb();
|
|
abp->ab_contents[abp->ab_index++] = readchar();
|
|
}
|
|
}
|
|
|
|
|
|
line_p arglist(n)
|
|
int n;
|
|
{
|
|
line_p lnp;
|
|
register arg_p ap,*app;
|
|
bool moretocome;
|
|
offset length;
|
|
|
|
|
|
/*
|
|
* creates an arglist with n elements
|
|
* if n == 0 the arglist is variable and terminated by sp_cend
|
|
*/
|
|
|
|
lnp = newline(OPLIST);
|
|
app = &ARG(lnp);
|
|
moretocome = TRUE;
|
|
do {
|
|
switch(table2()) {
|
|
default:
|
|
error("unknown byte in arglist");
|
|
case CSTX1:
|
|
tabval2 = (offset) tabval;
|
|
case CSTX2:
|
|
*app = ap = newarg(ARGOFF);
|
|
ap->a_a.a_offset = tabval2;
|
|
app = &ap->a_next;
|
|
break;
|
|
case ILBX:
|
|
*app = ap = newarg(ARGINSTRLAB);
|
|
ap->a_a.a_instrlab = instr_lab((short) tabval);
|
|
app = &ap->a_next;
|
|
break;
|
|
case DLBX:
|
|
*app = ap = newarg(ARGOBJECT);
|
|
ap->a_a.a_obj = object(string,(offset) 0, (offset) 0);
|
|
/* The size of the object is unknown */
|
|
app = &ap->a_next;
|
|
break;
|
|
case sp_pnam:
|
|
*app = ap = newarg(ARGPROC);
|
|
ap->a_a.a_proc = proclookup(string,OCCURRING);
|
|
app = &ap->a_next;
|
|
break;
|
|
case VALX1:
|
|
tabval2 = (offset) tabval;
|
|
case VALX2:
|
|
*app = ap = newarg(ARGOBJECT);
|
|
ap->a_a.a_obj = object(string, tabval2, (offset) 0);
|
|
app = &ap->a_next;
|
|
break;
|
|
case sp_scon:
|
|
*app = ap = newarg(ARGSTRING);
|
|
length = get_off();
|
|
arg_string(length,&ap->a_a.a_string);
|
|
app = &ap->a_next;
|
|
break;
|
|
case sp_icon:
|
|
*app = ap = newarg(ARGICN);
|
|
goto casecon;
|
|
case sp_ucon:
|
|
*app = ap = newarg(ARGUCN);
|
|
goto casecon;
|
|
case sp_fcon:
|
|
*app = ap = newarg(ARGFCN);
|
|
casecon:
|
|
length = get_int();
|
|
ap->a_a.a_con.ac_length = (short) length;
|
|
arg_string(get_off(),&ap->a_a.a_con.ac_con);
|
|
app = &ap->a_next;
|
|
break;
|
|
case sp_cend:
|
|
moretocome = FALSE;
|
|
}
|
|
if (n && (--n) == 0)
|
|
moretocome = FALSE;
|
|
} while (moretocome);
|
|
return(lnp);
|
|
}
|
|
|
|
|
|
|
|
/* is_datalabel */
|
|
|
|
bool is_datalabel(l)
|
|
line_p l;
|
|
{
|
|
VL(l);
|
|
return (l->l_instr == (byte) ps_sym);
|
|
}
|
|
|
|
|
|
|
|
/* block_of_lab */
|
|
|
|
dblock_p block_of_lab(ident)
|
|
char *ident;
|
|
{
|
|
dblock_p dbl;
|
|
|
|
/* Find the datablock with the given name.
|
|
* Used for defining occurrences.
|
|
*/
|
|
|
|
dbl = symlookup(ident,DEFINING);
|
|
VD(dbl);
|
|
if (dbl->d_pseudo != DUNKNOWN) {
|
|
error("identifier %s redeclared", ident);
|
|
}
|
|
return dbl;
|
|
}
|
|
|
|
|
|
|
|
/* object */
|
|
|
|
STATIC obj_p make_object(dbl,off,size)
|
|
dblock_p dbl;
|
|
offset off;
|
|
offset size;
|
|
{
|
|
/* Allocate an obj struct with the given attributes
|
|
* (if it did not exist already).
|
|
* Return a pointer to the found or newly created object struct.
|
|
*/
|
|
|
|
obj_p obj, prev, new;
|
|
|
|
/* See if the object was already present in the object list
|
|
* of the given datablock. If it is not yet present, find
|
|
* the right place to insert the new object. Note that
|
|
* the objects are sorted by offset.
|
|
*/
|
|
prev = (obj_p) 0;
|
|
for (obj = dbl->d_objlist; obj != (obj_p) 0; obj = obj->o_next) {
|
|
if (obj->o_off >= off) {
|
|
break;
|
|
}
|
|
prev = obj;
|
|
}
|
|
/* Note that the data block may contain several objects
|
|
* with the required offset; we also want the size to
|
|
* be the right one.
|
|
*/
|
|
while (obj != (obj_p) 0 && obj->o_off == off) {
|
|
if (obj->o_size == UNKNOWN_SIZE) {
|
|
obj->o_size = size;
|
|
return obj;
|
|
} else {
|
|
if (size == UNKNOWN_SIZE || obj->o_size == size) {
|
|
return obj;
|
|
/* This is the right one */
|
|
} else {
|
|
prev = obj;
|
|
obj = obj->o_next;
|
|
}
|
|
}
|
|
}
|
|
/* Allocate a new object */
|
|
new = newobject();
|
|
new->o_id = ++lastoid; /* create a unique object id */
|
|
new->o_off = off;
|
|
new->o_size = size;
|
|
new->o_dblock = dbl;
|
|
/* Insert the new object */
|
|
if (prev == (obj_p) 0) {
|
|
dbl->d_objlist = new;
|
|
} else {
|
|
prev->o_next = new;
|
|
}
|
|
new->o_next = obj;
|
|
return new;
|
|
}
|
|
|
|
|
|
|
|
obj_p object(ident,off,size)
|
|
char *ident;
|
|
offset off;
|
|
offset size;
|
|
{
|
|
dblock_p dbl;
|
|
|
|
/* Create an object struct (if it did not yet exist)
|
|
* for the object with the given size and offset
|
|
* within the datablock of the given name.
|
|
*/
|
|
|
|
dbl = (ident == (char *) 0 ? hol0_db : symlookup(ident, OCCURRING));
|
|
VD(dbl);
|
|
return(make_object(dbl,off,size));
|
|
}
|