/* MIX simulator, copyright 1994 by Darius Bacon */ 
#include "mix.h"

#include "asm.h"
#include "driver.h"
#include "parse.h"
#include "symbol.h"

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

/* --- Attributes of the current instruction or directive --- */

static char label[max_identifier_length+1];

static Byte the_C, default_F;
static Flag F_must_be_default;

/* --- Handlers for each type of directive --- */
/* Each must parse the operand field; see assemble_line(). */

static void do_opcode(void)
{
    assemble(set_byte(the_C, 5, parse_operand(F_must_be_default, default_F)));
}

static void do_con(void)
{
    assemble(parse_W());
}

static void do_alf(void)
{
    error("Use CON instead of ALF");
}

static void do_end(void)
{
    set_entry_point(cell_to_address(parse_W()));
}

static void do_equ(void)
{
    define_symbol(string_to_symbol(label), parse_W());
    if (VERBOSE) {
        print_cell(symbol_value(string_to_symbol(label)));
	printf("\n");
    }
}

static void do_orig(void)
{
    here = cell_to_address(parse_W());
}

/* --- The opcode/directive table --- */

typedef const struct OpDef {
    const char *name;
    void (*handler)(void);
    Byte C, F;
    Flag is_extended;
} OpDef;

#define extended true
#define def_opcode(name, C, F, is_ext)  { name, do_opcode, C, F, is_ext }
#define def_directive(name, handler)    { name, handler, 0, 0, 0 }

static OpDef op_table[] = {

#include "ops.inc"

};

/* --- The main driver --- */

static int delta_C;

/* Compare strings a la strcmp, ignoring case, and allowing a `-' in
   pattern to stand for any general register.  
   Pre: pattern is all lowercase and contains at most one `-'.  
   Post: if they match, delta_C is set to the register number of the
   register.  (delta_C unspecified if there was no `-'.) */
static int compare_mnemonics(const char *datum, const char *pattern)
{
    unsigned my_delta_C = 0;
    for (; *pattern; ++pattern, ++datum) {
	char d = tolower(*datum);
	if (*pattern == '-') {
	    static const char legals[] = "a123456x";
	    const char *temp = strchr(legals, d);
	    if (temp)
		my_delta_C = temp - legals;
	    else
		break;
	} else if (*pattern == d)
	    ;
	else
	    break;
    }
    {
	int result = tolower(*datum) - *pattern;
	if (result == 0)
	    delta_C = my_delta_C;
	return result;
    }
}

typedef void (*Handler)(void);

/* I'd prefer something faster than a linear search here.  Maybe have
   op2c.awk expand out the `-' characters so we only need look for a
   string equal to mnemonic. */
static Handler lookup_mnemonic(const char *mnemonic)
{
    unsigned i;
    delta_C = 0;
    for (i = 0; i < sizeof op_table / sizeof op_table[0]; ++i) {
	if (compare_mnemonics(mnemonic, op_table[i].name) == 0) {
	    the_C = op_table[i].C + delta_C;
	    default_F = op_table[i].F;
	    F_must_be_default = op_table[i].is_extended;
	    return op_table[i].handler;
	}
    }
    return NULL;
}

static const char *skip_blanks(const char *s)
{
    return s + strspn(s, " \t");
}

typedef const char *const_char_ptr;

static void eat_identifier(char *buffer, const_char_ptr *s)
{
    const char *scan = buffer;
    size_t size, truncated_size;

    for (scan = *s; !isspace (*scan) && *scan != '\0'; ++scan)
        ;
    size = scan - *s;

    {				/* ensure it's a legal identifier */
	Flag alpha = false, non_alphanum = false;
	for (scan = *s; scan < *s + size; ++scan) {
	    if (isalpha(*scan))
	        alpha = true;
	    else if (!isalnum (*scan))
	        non_alphanum = true;
	}
	if ((!alpha && size != 0) || non_alphanum) {
	    error ("Ill-formed label or mnemonic: %*.*s", size, size, *s);
	    buffer[0] = '\0';
	    *s += size;
	    return;
	}
    }

    if (max_identifier_length < size) {
        warn ("Truncated long label or mnemonic: %*.*s", size, size, *s);
	truncated_size = max_identifier_length;
    } else
        truncated_size = size;

    memcpy(buffer, *s, truncated_size);
    buffer[truncated_size] = '\0';
    *s += size;
}

void assemble_line(const char *line)
{
    char mnemonic[max_identifier_length+1];
    const char *scan = line;
    
    if (*skip_blanks(scan) == '*')      /* the comment character */
	return;

    eat_identifier(label, &scan);       /* eat label if any */
    scan = skip_blanks(scan);
    eat_identifier(mnemonic, &scan);    /* eat mnemonic */
    scan = skip_blanks(scan);
    setup_scanner(scan);                /* prepare to parse operand field */

    {
	Handler handler = lookup_mnemonic(mnemonic);
	if (handler != do_equ && label[0] != '\0')
	    define_symbol(string_to_symbol(label), address_to_cell(here));
	if (handler) {
	    handler();
	    done_parsing();  /* this goes here since *all* handlers parse an operand field */
	} else if (mnemonic[0] != '\0')
	    error("Unknown instruction: %s", mnemonic);
	else
	    ;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1