/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993-2000 Jason I. Gobat and Darren C. Atkinson

    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
    (at your option) 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.
*/

/************************************************************************
 * File:	codegen.c						*
 *									*
 * Description:	This file contains the public and private function and	*
 *		type definitions for code generation for the virtual	*
 *		machine.						*
 ************************************************************************/

# include <stdio.h>
# include "optab.h"
# include "codegen.h"
# include "allocate.h"
# include VAR_ARGS_INCLUDE

# define InitCodeSize 256


Address ip;
Code    cs;

struct cs {
    Word    *memory;
    unsigned count;
    unsigned size;
};


/************************************************************************
 * Function:	emit_instr						*
 *									*
 * Description:	Emits an instruction at the specified address expanding	*
 *		the current code segment if necessary.  An address	*
 *		operand is converted from an absolute address to a	*
 *		relative address which will allow the code to be moved	*
 *		to another starting address if necessary.		*
 ************************************************************************/

int emit_instr (addr, op, ap)
    Address addr;
    Opcode  op;
    va_list ap;
{
    if (cs -> count + 2 >= cs -> size)
	Reallocate (cs -> memory, Word, cs -> size <<= 1);

    switch (optab [op].type) {
    case OP_NONE:
	cs -> memory [addr].op = op;
	return 1;

    case OP_ADDR:
	cs -> memory [addr ++].op = op;
	cs -> memory [addr].addr = va_arg (ap, Address) - addr - 1;
	return 2;

    case OP_INT:
	cs -> memory [addr ++].op = op;
	cs -> memory [addr].ival = va_arg (ap, int);
	return 2;
    }

    return 0;
}


/************************************************************************
 * Function:	emit							*
 *									*
 * Description:	Emits an instruction at the address given by the	*
 *		instruction pointer which is then incremented.		*
 ************************************************************************/

# ifdef UseFunctionPrototypes
void emit (Opcode op, ...)
# else
void emit (op, va_alist)
    Opcode op;
    va_dcl
# endif
{
    int     inc;
    va_list ap;


    VA_START (ap, op);
    inc = emit_instr (ip, op, ap);
    cs -> count += inc;
    ip += inc;
    va_end (ap);
}


/************************************************************************
 * Function:	patch							*
 *									*
 * Description:	Emits an instruction at the specified address without	*
 *		changing the instruction pointer.			*
 ************************************************************************/

# ifdef UseFunctionPrototypes
void patch (Address addr, Opcode op, ...)
# else
void patch (addr, op, va_alist)
    Address addr;
    Opcode  op;
    va_dcl
# endif
{
    va_list ap;


    VA_START (ap, op);
    emit_instr (addr, op, ap);
    va_end (ap);
}


/************************************************************************
 * Function:	fetch							*
 *									*
 * Description:	Returns the word at the specified address from the	*
 *		current code segment.					*
 ************************************************************************/

Word fetch (addr)
    Address addr;
{
    static Word empty_word;


    return addr < 0 || addr > cs -> count ? empty_word : cs -> memory [addr];
}


/************************************************************************
 * Function:	reset							*
 *									*
 * Description:	Resets the current code segment.			*
 ************************************************************************/

void reset ( )
{
    ip = 0;
    cs -> count = 0;
}


/************************************************************************
 * Function:	free_cs							*
 *									*
 * Description:	Deallocates a code segment.				*
 ************************************************************************/

void free_cs (cs)
    Code cs;
{
    Deallocate (cs -> memory);
    Delete (cs);
}


/************************************************************************
 * Function:	new_cs							*
 *									*
 * Description:	Creates, initializes, and returns a new code segment.	*
 ************************************************************************/

Code new_cs ( )
{
    Code cs;


    cs = New (struct cs);

    cs -> count = 0;
    cs -> size = InitCodeSize;
    cs -> memory = Allocate (Word, cs -> size);

    return cs;
}


/************************************************************************
 * Function:	dump							*
 *									*
 * Description:	Dumps a code segment to standard output.		*
 ************************************************************************/

void dump (cs)
    Code cs;
{
    Opcode   op;
    unsigned i;


    if (!cs)
	return;


    for (i = 0; i < cs -> count; i ++) {
	op = cs -> memory [i].op;
	fprintf (stderr, "%03u\t%s", i, optab [op].name);
	switch (optab [op].type) {
	case OP_NONE:
	    fprintf (stderr, "\n");
	    break;

	case OP_ADDR:
	    i ++;
	    fprintf (stderr, "\t%d\n", cs -> memory [i].addr + i + 1);
	    break;

	case OP_INT:
	    fprintf (stderr, "\t%d\n", cs -> memory [++ i].ival);
	    break;
	}
    }
}


syntax highlighted by Code2HTML, v. 0.9.1