/*	makecalls: expand a datastructure as delivered by "EM_getline"
	into calls to the procedural interface.
	Exported routine:
		EM_mkcalls
*/

#include <em_spec.h>
#include <em_mnem.h>
#include <em_pseu.h>
#include <em_flag.h>
#include "em_ptyp.h"
#include <em.h>
#include "em_comp.h"
#include <assert.h>

extern char em_flag[];	/* One per EM instruction: indicates parameter kind */
extern short em_ptyp[];	/* One per parameter kind: indicates parameter type */

static int listtype = 0;	/* indicates pseudo when generating code for
				   variable length argument lists
				   (only for MES)
				*/

#ifdef CHECKING
/*	c_getarg: Check the argument indicated by "args".
	The argument must be of a type allowed by "typset".
	Return a pointer to the next argument.
*/
PRIVATE struct e_args *
c_getarg(args, typset)
	register struct e_args *args;
{

	if (((!typset) && args) ||
	    ((!args) && typset)) {
		/* End of arguments expected, but there are more, or
		   an argument expected, but there is none
		*/
		EM_error = "Illegal number of parameters";
		return 0;
	}

	if (!args) return 0;
	if (!(args->em_argtype & typset)) {
		/* Type error */
		EM_error = "Illegal parameter type";
	}

	return args->em_next;
}
#else not CHECKING
#define c_getarg(arg, x)	((arg) ? (arg)->em_next : (arg))
#endif CHECKING

/*	EM_doinstr: An EM instruction
*/
PRIVATE
EM_doinstr(opcode, arg)
	register struct e_args *arg;
{
	register int parametertype;	/* parametertype of the instruction */
	register struct e_args *args;

	parametertype = em_flag[opcode-sp_fmnem] & EM_PAR;
#ifdef CHECKING
	if (parametertype != PAR_NO && parametertype != PAR_W) {
		if (!arg) {
			EM_error = "Illegal number of parameters";
			return;
		}
	}
#endif CHECKING
	switch(parametertype) {
		case PAR_NO:
			break;
		default:
			args = c_getarg(arg, em_ptyp[parametertype]);
			args = c_getarg(args, 0);
			break;
		case PAR_W:
			if (arg) {
				args = c_getarg(arg, cst_ptyp);
				args = c_getarg(args, 0);
			}
			else {
#include "C_mnem_narg"
				return;
			}
			break;
	}
#include "C_mnem"
}

PRIVATE
EM_dopseudo(opcode, args)
	register struct e_args *args;
{
	register struct e_args *arg;

	switch(opcode) {
		case ps_exc: {
			register struct e_args *args2;

			arg = c_getarg(args, cst_ptyp);
			args2 = c_getarg(arg, cst_ptyp);
			args2 = c_getarg(args2, 0);
			C_exc(args->em_cst, arg->em_cst);
			break;
		}
		case ps_hol: {
			register struct e_args *args2, *args3;

			arg = c_getarg(args, cst_ptyp);
			args2 = c_getarg(arg, arg_ptyp);
			args3 = c_getarg(args2, cst_ptyp);
			args3 = c_getarg(args3, 0);
			switch(arg->em_argtype) {
			    case cst_ptyp:
				C_hol_cst(args->em_cst,
					  arg->em_cst,
					  args2->em_cst);
				break;
			    case ico_ptyp:
				C_hol_icon(args->em_cst,
					   arg->em_str,
					   arg->em_size, 
					   args2->em_cst);
				break;
			    case uco_ptyp:
				C_hol_ucon(args->em_cst,
					   arg->em_str,
					   arg->em_size,
					   args2->em_cst);
				break;
			    case fco_ptyp:
				C_hol_fcon(args->em_cst,
					   arg->em_str,
					   arg->em_size, 
					   args2->em_cst);
				break;
			    case sof_ptyp:
				C_hol_dnam(args->em_cst,
					   arg->em_dnam,
					   arg->em_soff, 
					   args2->em_cst);
				break;
			    case nof_ptyp:
				C_hol_dlb(args->em_cst,
					  arg->em_dlb,
					  arg->em_noff, 
					  args2->em_cst);
				break;
			    case ilb_ptyp:
				C_hol_ilb(args->em_cst,
					  arg->em_ilb, 
					  args2->em_cst);
				break;
			    case pro_ptyp:
				C_hol_pnam(args->em_cst, 
					   arg->em_pnam, 
					   args2->em_cst);
				break;
			    default:
				EM_error = "Illegal parameter type";
				break;
			}
			break;
		}
		case ps_bss: {
			register struct e_args *args2, *args3;

			arg = c_getarg(args, cst_ptyp);
			args2 = c_getarg(arg, arg_ptyp);
			args3 = c_getarg(args2, cst_ptyp);
			args3 = c_getarg(args3, 0);
			switch(arg->em_argtype) {
			    case cst_ptyp:
				C_bss_cst(args->em_cst,
					  arg->em_cst,
					  args2->em_cst);
				break;
			    case ico_ptyp:
				C_bss_icon(args->em_cst,
					   arg->em_str,
					   arg->em_size, 
					   args2->em_cst);
				break;
			    case uco_ptyp:
				C_bss_ucon(args->em_cst,
					   arg->em_str,
					   arg->em_size,
					   args2->em_cst);
				break;
			    case fco_ptyp:
				C_bss_fcon(args->em_cst,
					   arg->em_str,
					   arg->em_size, 
					   args2->em_cst);
				break;
			    case sof_ptyp:
				C_bss_dnam(args->em_cst,
					   arg->em_dnam,
					   arg->em_soff, 
					   args2->em_cst);
				break;
			    case nof_ptyp:
				C_bss_dlb(args->em_cst,
					  arg->em_dlb,
					  arg->em_noff, 
					  args2->em_cst);
				break;
			    case ilb_ptyp:
				C_bss_ilb(args->em_cst,
					  arg->em_ilb, 
					  args2->em_cst);
				break;
			    case pro_ptyp:
				C_bss_pnam(args->em_cst, 
					   arg->em_pnam, 
					   args2->em_cst);
				break;
			    default:
				EM_error = "Illegal parameter type";
				break;
			}
			break;
		}
		case ps_end:
			if (args) {
				arg = c_getarg(args, cst_ptyp);
				arg = c_getarg(arg, 0);
				C_end(args->em_cst);
				break;
			}
			C_end_narg();
			break;
		case ps_exa:
		case ps_ina:
			arg = c_getarg(args, lab_ptyp);
			arg = c_getarg(arg, 0);
			if (args->em_argtype == nof_ptyp) {
				if (opcode == ps_exa) {
					C_exa_dlb(args->em_dlb);
				}
				else	C_ina_dlb(args->em_dlb);
				break;
			}
			if (opcode == ps_exa) {
				C_exa_dnam(args->em_dnam);
			}
			else	C_ina_dnam(args->em_dnam);
			break;
		case ps_exp:
		case ps_inp:
			arg = c_getarg(args, pro_ptyp);
			arg = c_getarg(arg, 0);
			if (opcode == ps_exp) {
				C_exp(args->em_pnam);
			}
			else	C_inp(args->em_pnam);
			break;
		case ps_pro:
			arg = c_getarg(args, pro_ptyp);
			if (arg) {
				struct e_args *args2;

				args2 = c_getarg(arg, cst_ptyp);
				args2 = c_getarg(args2, 0);
				C_pro(args->em_pnam, arg->em_cst);
			}
			else	C_pro_narg(args->em_pnam);
			break;
		case ps_con:
			arg = c_getarg(args, val_ptyp);
			arg = c_getarg(arg, 0);
			switch(args->em_argtype) {
				case ilb_ptyp:
					C_con_ilb(args->em_ilb);
					break;
				case nof_ptyp:
					C_con_dlb(args->em_dlb, args->em_noff);
					break;
				case sof_ptyp:
					C_con_dnam(args->em_dnam, args->em_soff);
					break;
				case cst_ptyp:
					C_con_cst(args->em_cst);
					break;
				case pro_ptyp:
					C_con_pnam(args->em_pnam);
					break;
				case str_ptyp:
					C_con_scon(args->em_str, args->em_size);
					break;
				case ico_ptyp:
					C_con_icon(args->em_str, args->em_size);
					break;
				case uco_ptyp:
					C_con_ucon(args->em_str, args->em_size);
					break;
				case fco_ptyp:
					C_con_fcon(args->em_str, args->em_size);
					break;
				default:
					EM_error = "Illegal argument type";
					return 0;
			}
			break;
		case ps_rom:
			arg = c_getarg(args, val_ptyp);
			arg = c_getarg(arg, 0);
			switch(args->em_argtype) {
				case ilb_ptyp:
					C_rom_ilb(args->em_ilb);
					break;
				case nof_ptyp:
					C_rom_dlb(args->em_dlb, args->em_noff);
					break;
				case sof_ptyp:
					C_rom_dnam(args->em_dnam, args->em_soff);
					break;
				case cst_ptyp:
					C_rom_cst(args->em_cst);
					break;
				case pro_ptyp:
					C_rom_pnam(args->em_pnam);
					break;
				case str_ptyp:
					C_rom_scon(args->em_str, args->em_size);
					break;
				case ico_ptyp:
					C_rom_icon(args->em_str, args->em_size);
					break;
				case uco_ptyp:
					C_rom_ucon(args->em_str, args->em_size);
					break;
				case fco_ptyp:
					C_rom_fcon(args->em_str, args->em_size);
					break;
				default:
					EM_error = "Illegal argument type";
					return 0;
			}
			break;
		default: 
			EM_error = "Illegal pseudo instruction";
			break;
	}
}

PRIVATE
EM_docon(args)
	register struct e_args *args;
{
	register struct e_args *arg;

	arg = c_getarg(args, val_ptyp);
	arg = c_getarg(arg, 0);
	switch(args->em_argtype) {
		case ilb_ptyp:
			C_ilb(args->em_ilb);
			break;
		case nof_ptyp:
			C_dlb(args->em_dlb, args->em_noff);
			break;
		case sof_ptyp:
			C_dnam(args->em_dnam, args->em_soff);
			break;
		case cst_ptyp:
			C_cst(args->em_cst);
			break;
		case pro_ptyp:
			C_pnam(args->em_pnam);
			break;
		case str_ptyp:
			C_scon(args->em_str, args->em_size);
			break;
		case ico_ptyp:
			C_icon(args->em_str, args->em_size);
			break;
		case uco_ptyp:
			C_ucon(args->em_str, args->em_size);
			break;
		case fco_ptyp:
			C_fcon(args->em_str, args->em_size);
			break;
		default:
			EM_error = "Illegal argument type";
			break;
	}
}

PRIVATE
EM_dostartmes(args)
	register struct e_args *args;
{
	register struct e_args *arg;

	if (listtype) {
		EM_error = "Message not ended";
		return;
	}
	arg = c_getarg(args, cst_ptyp);
	arg = c_getarg(arg, 0);
	C_mes_begin(args->em_cst);
	listtype = ps_mes;
}

EXPORT int
EM_mkcalls(line)
	register struct e_instr *line;
{

#ifdef CHECKING
	if (listtype && line->em_type != EM_MESARG && line->em_type != EM_ENDMES) {
		EM_error = "Message not ended";
		return 0;
	}
#endif CHECKING
	EM_error = 0;
	switch(line->em_type) {
		default:
			EM_error = "Illegal EM line";
			break;
		case EM_MNEM:
			/* normal instruction */
			EM_doinstr(line->em_opcode, line->em_args);
			break;
		case EM_DEFILB:
			/* defining occurrence of an instruction label */
			C_df_ilb(line->em_deflb);
			break;
		case EM_DEFDLB:
			/* defining occurrence of a global data label */
			C_df_dlb(line->em_deflb);
			break;
		case EM_DEFDNAM:
			/* defining occurrence of a non-numeric data label */
			C_df_dnam(line->em_defdnam);
			break;
		case EM_PSEU:
			/* pseudo */
			EM_dopseudo(line->em_opcode, line->em_args);
			break;
		case EM_STARTMES:
			/* start of a MES pseudo */
			EM_dostartmes(line->em_arg);
			break;
		case EM_MESARG:
		case EM_ENDMES:
#ifdef CHECKING
			if (!listtype) {
				EM_error = "Message not started";
				return 0;
			}
#endif
			if (line->em_type == EM_MESARG) {
				EM_docon(line->em_args);
				break;
			}
			C_mes_end();
			listtype = 0;
			break;
	}
	if (EM_error) return 0;
	return 1;
}
