/*  Core Wars.
 *  Copyright (C) 1999  Walter Hofmann
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as 
 *  published by the Free Software Foundation.
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

%{

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <ctype.h>

#include "main.h"
#include "options.h"
#include "program.h"
#include "program-rc.h"

#define cmalloc(x) calloc(1,x)

#define EXPR_INTEGER	0 /* integer         */
#define EXPR_LABEL	1 /* label           */
#define EXPR_ADD	2 /* integer * a + b */
#define EXPR_SUB	3 /* integer * a - b */
#define EXPR_SGN	4 /* integer * a     */
#define EXPR_MUL	5 /* a * b           */
#define EXPR_DIV	6 /* a / b           */
#define EXPR_MOD	7 /* a % b           */
#define EXPR_EQU	8 /* a == b          */

  struct line {
    struct line *next;
    struct rc_command *cmd;
  } ;
  
  struct expr {
    int what;
    int integer;
    char *name;
    int operation;
    struct expr *a, *b;      
  } ;    

  struct label {
    struct label *next;
    char *name;
    int relative;
    int address;
    int in_eval;
    struct expr *expr;
  } ;

  struct label *create_label(char *name);
  void free_expr(struct expr *expr);
  struct expr *copy_expr(struct expr *expr);

  int current_line, *current_cell;
  struct line *root_line;
  struct expr *org;
  struct label *label_list;
  struct label *for_index;

  char rc_error_message[1024];

  int eval_expr(struct expr *expr, int rel, jmp_buf env);
  void subs_expr(struct expr *expr, struct label *label, int i);
  void rc_error (char *s);
  int rc_lex (void);
  void rc_restart(FILE *input_file);
%}

%union {
  int integer;
  char *name;
  struct rc_command *cmd;
  struct line *line;
  struct label *label;
  struct expr *expr;
}

%token <integer>	INTEGER
%token <name>		IDENTIFIER
%token			WHITESPACE
%token			COMMENT
%token <integer>	RC_CMD
%token			RC_INSTR_EQU
%token			RC_INSTR_ORG
%token			RC_INSTR_END
%token			RC_INSTR_FOR
%token			RC_INSTR_ROF

%type <line>		list for_list
%type <line>		line
%type <line>		instruction
%type <cmd>		command
%type <label>		label
%type <line>		instr_for
%type <expr>		expr expr0 expr1 expr1s expr2 expr3
%type <integer>		address_mode

%% /* Grammar rules and actions follow */

program:	  { current_line = 1; *current_cell = 0; org = NULL; }
		  whitespace list
		  { root_line = $3; }
		;

whitespace:	  /* empty */
		| 
		  WHITESPACE
		;

list:		  line
		  { $$ = $1; }
		| 
		  list line
		  { 
		    if ($2) 
		      { 
			struct line *end; 
			
			end = $2; 
			while (end->next) 
			  end = end->next; 
			end->next = $1; 
			$$ = $2; 
		      } 
		    else 
		      $$ = $1; 
		  }
		|
		  list instr_end 
		  { root_line = $1; YYACCEPT; }
		;

for_list:	  line
		  { $$ = $1; }
		| 
		  list line
		  { 
		    if ($2) 
		      { 
			struct line *end; 
			
			end = $2; 
			while (end->next) 
			  end = end->next; 
			end->next = $1; 
			$$ = $2; 
		      } 
		    else 
		      $$ = $1; 
		  }
		;

line:		  comment '\n' whitespace
		  { $$ = NULL; current_line++; }
		| 
		  instruction comment '\n' whitespace 
		  { $$ = $1; current_line++; for_index = NULL; }
		;

comment:	  /* empty */	
		| 
		  COMMENT	
		;

instruction:	  instr_equ
		  { $$ = NULL; }
		| 
		  instr_org
		  { $$ = NULL; }
		| 
		  instr_for
		  { $$ = $1; }
		| 
		  command
		  { $$ = malloc (sizeof (struct line)); $$->next = NULL; $$->cmd = $1; (*current_cell)++; }
		;

command:	  label_list RC_CMD whitespace address_mode expr
		  {
		    $$ = malloc (sizeof (struct rc_command));
		    if (($2 & RC_CMD_MASK) == RC_CMD_DAT) /* ICWS 0251 */
		      {
			$$->command = $2 | ($4 << RC_AM_A_TO_B);
			$$->a.expr = NULL;
			$$->b.expr = $5;
			$$->command |= RC_AM_A_DIRECT;
		      }
		    else
		      {
			$$->command = $2 | $4;
			$$->a.expr = $5;
			$$->b.expr = NULL;
			$$->command |= RC_AM_B_DIRECT;
		      }
		  }
		| 
		  label_list RC_CMD whitespace address_mode expr ',' whitespace address_mode expr
		  {
		    $$ = malloc (sizeof (struct rc_command));
		    $$->command = $2 | $4 | ($8 << RC_AM_A_TO_B);
		    $$->a.expr = $5;
		    $$->b.expr = $9;
		  }
		;

label_list:	  label label_list_ne
		  {}
		| 
		  /* empty */
		;

label_list_ne:	  label
		  {}
		| 
		  label comment '\n' whitespace label_list_ne
		  { current_line++; }
		| 
		  comment '\n' whitespace label_list_ne
		  { current_line++; }
		| 
		  /* empty */
		;

label:		  IDENTIFIER whitespace
		  { 
		    $$ = create_label($1); 
		    if (!$$)
		      YYERROR;
		    $$->relative = 1; 
		    $$->address = *current_cell; 
		    for_index = $$; 
		  }
		| 
		  IDENTIFIER ':' whitespace
		  { 
		    $$ = create_label($1); 
		    if (!$$)
		      YYERROR;
		    $$->relative = 1; 
		    $$->address = *current_cell; 
		    for_index = $$;
		  }
		;

address_mode:	  /* empty */
		  { $$ = RC_AM_A_DIRECT; } /* ICWS 0238 */
		| 
		  '#' whitespace
		  { $$ = RC_AM_A_IMMEDIATE; }
		| 
		  '$' whitespace
		  { $$ = RC_AM_A_DIRECT; }
		| 
		  '@' whitespace
		  { $$ = RC_AM_A_INDIRECT_B; }
		| 
		  '<' whitespace
		  { $$ = RC_AM_A_PRE_DEC_B; }
		| 
		  '>' whitespace
		  { $$ = RC_AM_A_POST_INC_B; }
		| 
		  '{' whitespace
		  { $$ = RC_AM_A_PRE_DEC_A; }
		| 
		  '}' whitespace
		  { $$ = RC_AM_A_POST_INC_A; }
		| 
		  '*' whitespace
		  { $$ = RC_AM_A_INDIRECT_A; }
		;

expr:		  expr0
		  { $$ = $1; }
		| 
		  expr0 '=' '=' whitespace expr0
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_EQU;
		    $$->a = $1;
		    $$->b = $5;
		  }
		;

expr0:		  expr1
		  { $$ = $1; }
		| 
		  expr1s
		  { $$ = $1; }
		;

expr1:		  expr2 
		  { $$ = $1; }
                | 
		  expr1 '+' whitespace expr2 
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_ADD;
		    $$->integer = 1;
		    $$->a = $1;
		    $$->b = $4;
		  }
		| 
		  expr1 '-' whitespace expr2
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_SUB;
		    $$->integer = 1;
		    $$->a = $1;
		    $$->b = $4;
		  }
		;

expr1s:		  '+' whitespace expr2 expr1s
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_ADD;
		    $$->integer = 1;
		    $$->a = $3;
		    $$->b = $4;
		  }
		| 
		  '-' whitespace expr2 expr1s
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_ADD;
		    $$->integer = -1;
		    $$->a = $3;
		    $$->b = $4;
		  }
		| 
		  '+' whitespace expr2
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_SGN;
		    $$->integer = 1;
		    $$->a = $3;
		  }
		| 
		  '-' whitespace expr2
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_SGN;
		    $$->integer = -1;
		    $$->a = $3;
		  }
		;

expr2:            expr3
		  { $$ = $1; }
		| 
		  expr2 '*' whitespace expr3 
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_MUL;
		    $$->a = $1;
		    $$->b = $4;
		  }
		| 
		  expr2 '/' whitespace expr3 
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_DIV;
		    $$->a = $1;
		    $$->b = $4;
		  }
                | 
		  expr2 '%' whitespace expr3
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_MOD;
		    $$->a = $1;
		    $$->b = $4;
		  }
		;

expr3:		  IDENTIFIER whitespace
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_LABEL;
		    $$->name = $1;
		  }
		| 
		  INTEGER whitespace
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_INTEGER;
		    $$->integer = $1;
		  }
		| 
		  '(' whitespace expr ')' whitespace
		  { 
		    $$ = cmalloc (sizeof (struct expr));
		    $$->what = EXPR_SGN;
		    $$->integer = 1;
		    $$->a = $3;
		  }
		;

/* instr_end and instr_for are like a line, the other instr_* are like a command */
instr_end:	  label_list RC_INSTR_END whitespace expr comment '\n' whitespace
		  { if (org) free_expr (org); org = $4; current_line++; }
		| 
		  label_list RC_INSTR_END whitespace comment '\n' whitespace
		  { current_line++; }
		;

instr_equ:	  label RC_INSTR_EQU whitespace expr
		  { $1->relative = 0; $1->expr = $4; }
		;

instr_org:	  RC_INSTR_ORG whitespace expr
		  { if (org) free_expr (org); org = $3; }
		;

instr_for:	  label_list 
		  { 
		    $<label>$ = for_index;
		    if (for_index)
		      label_list = for_index->next;
		  } 
		  RC_INSTR_FOR whitespace expr comment '\n' whitespace
		  {
		    int count;
		    jmp_buf env;

		    current_line++;
		    if (setjmp (env))
		      YYERROR;
		    else
		      count = eval_and_free_expr ($5, *current_cell, env);
		    if (count<1 || count>RC_MAX_FOR_COUNT)
		      {
			snprintf (rc_error_message, 1024, "FOR count out off bounds in line %d.", current_line);
			rc_error_message[1023] = '\0';
			YYERROR;
		      }
		    $<integer>$ = count;
		  }
		  for_list 
		  RC_INSTR_ROF whitespace
		  {
		    struct line *start, *end, *current;
		    int i;
		    struct label *my_index;
		    int count;

		    count = $<integer>9;
		    my_index = $<label>2;
		    start = end = malloc (sizeof (struct line));
		    for (i=1; i<count; i++)
		      {
			current = $10;
			while (current)
			  {
			    end->next = malloc (sizeof (struct line));
			    end = end->next;
			    end->cmd = malloc (sizeof (struct rc_command));
			    end->cmd->command = current->cmd->command;
			    end->cmd->a.expr = copy_expr (current->cmd->a.expr);
			    end->cmd->b.expr = copy_expr (current->cmd->b.expr);
			    if (my_index!=NULL)
			      {
				subs_expr (end->cmd->a.expr, my_index, count-i+1);
				subs_expr (end->cmd->b.expr, my_index, count-i+1);
			      }
			    current = current->next;
			    (*current_cell)++;
			  }
		      }
		    if (my_index!=NULL)
		      {
			current = $10;
			while (current)
			  {
			    subs_expr (current->cmd->a.expr, my_index, 1);
			    subs_expr (current->cmd->b.expr, my_index, 1);
			    current = current->next;
			  }
		      }
		    end->next = $10;
		    $$ = start->next;
		    free (start);
		    if (my_index)
		      free (my_index);
		  }
		;

%%

struct label *create_label(char *name)
{
  struct label * l;

  l = label_list;
  while (l)
    {
      if (strcmp (name, l->name)==0)
	{
	  snprintf (rc_error_message, 1024, "Duplicate label '%s' in line %d.", name, current_line);
	  rc_error_message[1023] = '\0';
	  return NULL;
	}
      l = l->next;
    }
  l = malloc (sizeof (struct label));
  l->next = label_list;
  label_list = l;
  l->name = name;
  l->in_eval = 0;
  return l;
}

void free_label_list()
{
  struct label *l, *l2;

  l = label_list;
  while (l)
    {
      l2 = l->next;
      free (l->name);
      free (l);
      l = l2;
    }
}

void free_expr(struct expr *expr)
{
  if (expr->name)
    free (expr->name);
  if (expr->a)
    free_expr (expr->a);
  if (expr->b)
    free_expr (expr->b);
  free (expr);
}

struct expr *copy_expr(struct expr *expr)
{
  struct expr *r;

  if (expr==NULL)
    return NULL;
  r = cmalloc (sizeof (struct expr));
  *r = *expr;
  if (r->name)
    r->name = strdup (r->name);
  if (r->a)
    r->a = copy_expr (r->a);
  if (r->b)
    r->b = copy_expr (r->b);
  return r;
}

void subs_expr(struct expr *expr, struct label *label, int i)
{
  struct expr *r;
  
  if (expr!=NULL)
    if (expr->what==EXPR_LABEL && strcmp (expr->name, label->name)==0)
      {
	expr->what = EXPR_INTEGER;
	expr->integer = i;
	free (expr->name);
	expr->name = NULL;
      }
    else
      {
	if (expr->a)
	  subs_expr (expr->a, label, i);
	if (expr->b)
	  subs_expr (expr->b, label, i);
      }
}

int eval_label(char *name, int rel, jmp_buf env)
{
  struct label *p;
  int i;

  p = label_list;
  while (p)
    {
      if (strcmp (name, p->name)==0)
	if (p->relative)
	  return p->address-rel;
	else
	  {
	    if (p->in_eval)
	      {
		snprintf (rc_error_message, 1024, "Label '%s' defined recursively.", name);
		rc_error_message[1023] = '\0';
		longjmp (env, -1);
	      }
	    p->in_eval = 1;
	    i = eval_expr (p->expr, rel, env);
	    p->in_eval = 0;
	    return i;
	  }
      p = p->next;
    }
  snprintf (rc_error_message, 1024, "Cannot find label '%s' to evaluate cell %d.", name, rel);
  rc_error_message[1023] = '\0';
  longjmp (env, -1);
}

int eval_expr(struct expr *expr, int rel, jmp_buf env)
{
  int d;

  switch (expr->what)
    {
    case  EXPR_INTEGER:
      return expr->integer;
      
    case EXPR_LABEL:
      return eval_label (expr->name, rel, env);

    case EXPR_ADD:
      return expr->integer * eval_expr(expr->a, rel, env) + eval_expr(expr->b, rel, env);

    case EXPR_SUB:
      return expr->integer * eval_expr(expr->a, rel, env) - eval_expr(expr->b, rel, env);

    case EXPR_SGN:
      return expr->integer * eval_expr(expr->a, rel, env);

    case EXPR_MUL:
      return eval_expr(expr->a, rel, env) * eval_expr(expr->b, rel, env);

    case EXPR_DIV:
      d = eval_expr(expr->b, rel, env);
      if (d==0)
	{
	  snprintf (rc_error_message, 1024, "Division by zero while evaluating cell %d.", rel);
	  rc_error_message[1023] = '\0';
	  longjmp (env, -1);
	}
      return eval_expr(expr->a, rel, env) / d;

    case EXPR_MOD:
      d = eval_expr(expr->b, rel, env);
      if (d==0)
	{
	  snprintf (rc_error_message, 1024, "Division by zero while evaluating cell %d.", rel);
	  rc_error_message[1023] = '\0';
	  longjmp (env, -1);
	}
      return eval_expr(expr->a, rel, env) % d;

    case EXPR_EQU:
      return eval_expr(expr->a, rel, env) == eval_expr(expr->b, rel, env);
    }
}

int eval_and_free_expr(struct expr *expr, int rel, jmp_buf env)
{
  int r;

  r = eval_expr (expr, rel, env);
  free_expr (expr);
  return r;
}

void add_constant(char *name, int value)
{
  struct label *l;

  l = create_label (name);
  l->relative = 0;
  l->expr = cmalloc (sizeof (struct expr));
  l->expr->what = EXPR_INTEGER;
  l->expr->integer = value;
}

char *command_names[] = {"DAT","MOV","ADD","SUB","MUL","DIV","MOD",
			 "JMP","JMZ","JMN","DJN","SEQ","SLT","SPL",
			 "SNE","NOP","IJZ"};
char *modifier_names[] = {"   ",".A ",".B ",".AB",".BA",".F ",".X ",".I "};
char mode_indicators[] = {' ','#','$','@','<','>','{','}','*'};

void rc_dump_instruction(struct rc_command *pr)
{
  printf ("%s",command_names[pr->command & RC_CMD_MASK]);
  printf ("%s ",modifier_names[(pr->command & RC_MOD_MASK)>>RC_MOD_SHIFT]);
  printf ("%c %6d, ",mode_indicators[(pr->command & RC_AM_A_MASK)>>RC_AM_A_SHIFT], 
	  pr->a.number);
  printf ("%c %6d\n",mode_indicators[(pr->command & RC_AM_B_MASK)>>RC_AM_B_SHIFT], 
	  pr->b.number);
}

void rc_dump_program(struct program *pr)
{
  int i;
  
  printf ("ORG %03d\n", pr->lang.rc.org);
  for (i=0; i<pr->lang.rc.size; i++)
    {
      printf ("%03d ", i);
      printf ("%s",command_names[pr->lang.rc.obj[i].command & RC_CMD_MASK]);
      printf ("%s ",modifier_names[(pr->lang.rc.obj[i].command & RC_MOD_MASK)>>RC_MOD_SHIFT]);
      printf ("%c %6d, ",mode_indicators[(pr->lang.rc.obj[i].command & RC_AM_A_MASK)>>RC_AM_A_SHIFT], 
	      pr->lang.rc.obj[i].a.number);
      printf ("%c %6d\n",mode_indicators[(pr->lang.rc.obj[i].command & RC_AM_B_MASK)>>RC_AM_B_SHIFT], 
	      pr->lang.rc.obj[i].b.number);
    }
}

void rc_dump_program_internal (struct program *pr)
{
  int i;
  
  printf ("%d\n", pr->lang.rc.size);
  printf ("%d\n", pr->lang.rc.org);
  for (i=0; i<pr->lang.rc.size; i++)
    {
      printf ("%d\n",pr->lang.rc.obj[i].command);
      printf ("%d\n",pr->lang.rc.obj[i].a.number);
      printf ("%d\n",pr->lang.rc.obj[i].b.number);
    }
}

int mod_matrix[][2][2] = {
  {{RC_MOD_I,RC_MOD_B},{RC_MOD_AB,RC_MOD_AB}},
  {{RC_MOD_F,RC_MOD_B},{RC_MOD_AB,RC_MOD_AB}},
  {{RC_MOD_B,RC_MOD_B},{RC_MOD_AB,RC_MOD_AB}}};  

int rc_load_program(struct program *pr)
{
  FILE *f;
  int i, j;
  struct line *p, *p2;
  struct rc_command *obj;
  int start;
  int ad, bd;
  int mod;
  jmp_buf env;
  char s[1024];
  char *sp;

  unload_program (pr);

  label_list = NULL;

  f = fopen (pr->filename, "r");
  if (!f)
    return FAIL;

  do 
    {
      if (fgets (s, 1024, f)==NULL)
	s[0] = '\0';
      else
	if (strncasecmp (s, ";name ", 6)==0)
	  {
	    s[strlen (s)-1] = '\0';
	    sp = s+6;
	    while (isspace (*sp))
	      sp++;
	    pr->title = strdup (sp);
	  }
	else if (strncasecmp (s, ";author ", 8)==0)
	  {
	    s[strlen (s)-1] = '\0';
	    sp = s+8;
	    while (isspace (*sp))
	      sp++;
	    pr->author = strdup (sp);
	  }
    }
  while (s[0]==';' || isspace (s[0]));
  if (pr->title==NULL)
    pr->title = strdup (pr->filename);
  if (pr->author==NULL)
    pr->author = strdup ("unknown");
  rewind (f);

  add_constant (strdup ("CORESIZE"), SIZE);
  add_constant (strdup ("MAXPROCESSES"), MAX_THREADS);
  add_constant (strdup ("MAXCYCLES"), MAX_CYCLES);
  add_constant (strdup ("MAXLENGTH"), MAX_LENGTH);
  add_constant (strdup ("MINDISTANCE"), MIN_DISTANCE);
  add_constant (strdup ("CURLINE"), 0);
  current_cell = &label_list->expr->integer;

  rc_restart (f);
  rc_error_message[0] = '\0';
  if (rc_parse())
    {
      fclose (f);
      if (rc_error_message[0]=='\0')
	{
	  snprintf (rc_error_message, 1024, "%s (%d): Syntax error", pr->filename, current_line);
	  rc_error_message[1023] = '\0';
	}
      pr->error = strdup (rc_error_message);
      return OK;
    }
  fclose (f);

  obj = malloc ((*current_cell)*sizeof (struct rc_command));
  i = *current_cell;
  p = root_line;
  while (p)
    {
      obj[--i] = *p->cmd;
      p2 = p->next;
      free (p);
      p = p2;
    }
  
  if ((*current_cell)>MAX_LENGTH)
    {
      for (j=0; j<(*current_cell); j++)
	{
	  if (obj[j].a.expr)
	    free_expr (obj[j].a.expr);
	  if (obj[j].b.expr)
	    free_expr (obj[j].b.expr);
	}
      free_label_list();
      free (obj);
      snprintf (rc_error_message, 1024, "%s: Program exceeds maximum length (by %d instructions)", 
		pr->filename, (*current_cell)-MAX_LENGTH);
      rc_error_message[1023] = '\0';
      pr->error = strdup (rc_error_message);
      return OK;
    }

  for (i=0; i<(*current_cell); i++)
    {
      if (setjmp (env))
	{
	  if (obj[i].b.expr)
	    free_expr (obj[i].b.expr);
	  for (j=i+1; j<(*current_cell); j++)
	    {
	      if (obj[j].a.expr)
		free_expr (obj[j].a.expr);
	      if (obj[j].b.expr)
		free_expr (obj[j].b.expr);
	    }
	  free_label_list();
	  free (obj);
	  pr->error = strdup (rc_error_message);
	  return OK;
	}
      else
	if (obj[i].a.expr)
	  obj[i].a.number = eval_and_free_expr (obj[i].a.expr, i, env);
      if (setjmp (env))
	{
	  for (j=i+1; j<(*current_cell); j++)
	    {
	      if (obj[j].a.expr)
		free_expr (obj[j].a.expr);
	      if (obj[j].b.expr)
		free_expr (obj[j].b.expr);
	    }
	  free_label_list();
	  free (obj);
	  pr->error = strdup (rc_error_message);
	  return OK;
	}
      else
	if (obj[i].b.expr)
	  obj[i].b.number = eval_and_free_expr (obj[i].b.expr, i, env);
      if ((obj[i].command & RC_MOD_MASK) == RC_MOD_NONE)
	{
	  ad = ((obj[i].command & RC_AM_A_MASK) == RC_AM_A_IMMEDIATE);
	  bd = ((obj[i].command & RC_AM_B_MASK) == RC_AM_B_IMMEDIATE);
	  switch (obj[i].command & RC_CMD_MASK)
	    {
	    case RC_CMD_DAT:
	    case RC_CMD_NOP:
	      mod = RC_MOD_F;
	      break;
	      
	    case RC_CMD_MOV:
	    case RC_CMD_SEQ:
	    case RC_CMD_SNE:
	      mod = mod_matrix[0][ad][bd];
	      break;

	    case RC_CMD_ADD:
	    case RC_CMD_SUB:
	    case RC_CMD_MUL:
	    case RC_CMD_DIV:
	    case RC_CMD_MOD:
	      mod = mod_matrix[1][ad][bd];
	      break;

	    case RC_CMD_SLT:
	      mod = mod_matrix[2][ad][bd];
	      break;
	      
	    case RC_CMD_JMP:
	    case RC_CMD_JMZ:
	    case RC_CMD_JMN:
	    case RC_CMD_DJN:
	    case RC_CMD_IJZ:
	    case RC_CMD_SPL:
	      mod = RC_MOD_B;
	      break;
	    }
	  obj[i].command |= mod;
	}
    }

  if (org)
    if (setjmp (env))
      {
	free_label_list();
	free (obj);
	pr->error = strdup (rc_error_message);
	return OK;
      }
    else
      start = eval_and_free_expr (org, 0, env);
  else
    start = 0;

  pr->lang.rc.obj = obj;
  pr->lang.rc.org = start;
  pr->lang.rc.size = *current_cell;
  return OK;
}

void rc_free_program(struct program *pr)
{
  if (pr->lang.rc.obj)
    free (pr->lang.rc.obj);
  pr->lang.rc.obj = NULL;
}

void rc_error (char *s)
{
}
