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

#define	WRITE_LOC	32765	/* Location of write-register */
#define	READ_LOC	32766	/* Location of read-register */
#define	STOP_LOC	32767	/* Location of STOP PC */

#define	TOK_REF		0
#define	TOK_DEF		1
#define	TOK_INT		2
#define	TOK_DOT		3
#define	TOK_DATA	4
#define	TOK_TEXT	5
#define	TOK_EOF		6

typedef short
    Address;

struct LabRef {
    Address
	Addr;
    struct LabRef
	*Next;
};

struct Label {
    char
	*Name;		/* Name of the label */
    Address
	Addr;		/* Address (location) of the label */
    struct LabRef
	*Refs;		/* List of references to the label */
    struct Label
	*Next;		/* Next label in list */
};

char
    Token[256];
Address
    *Memory,		/* Memory array */
    MemSize = 1024,	/* Memory size, in words */
    CurrText = 0,	/* Where to load TEXT */
    MaxText = 512,	/* Maximum amount of TEXT */
    CurrData = 512,	/* Where to load DATA */
    PC = 0;		/* Program counter */
struct Label
    *LabList;		/* List of labels */
int
    Trace;		/* Produce a trace? */


void *ckmalloc( unsigned len )
{
    void
	*Res;

    Res = malloc( len );
    if( !Res ) {
	(void)fprintf( stderr, "PC: %ld: out of memory\n", (long)PC );
	exit( 1 );
    }
    return Res;
}




int GetTok( FILE *InFile, char *s )
{
    int
	c;
    char
	*t;

    t = s;

    while( 1 ) {
	c = getc( InFile );

	/* Skip comments/white space */
	do {
	    if( isspace( c ) ) {
		c = getc( InFile );
	    }
	    if( (c == ';') || (c == '#') ) {
		do {
		    c = getc( InFile );
		} while( (c != EOF) && (c != '\n') );
	    }
	} while( (c != EOF) && isspace( c ) );

	if( c == EOF ) {
	    return TOK_EOF;
	}
	else if( isalpha( c ) ) {
	    *t++ = c;
	    do {
		c = getc( InFile );
		if( isalpha(c) || isdigit(c) ) {
		    *t++ = c;
		}
	    } while( isalpha(c) || isdigit(c) );
	    *t = 0;
	    if( c == ':' ) {
		if( strcmp( s, "TEXT" ) == 0 ) {
		    return TOK_TEXT;
		}
		else if( strcmp( s, "DATA" ) == 0 ) {
		    return TOK_DATA;
		}
		else if( strcmp( s, "INPUT" ) == 0 ) {
		    return TOK_EOF;
		}
		else {
		    return TOK_DEF;
		}
	    }
	    else {
		(void)ungetc( c, InFile );
		return TOK_REF;
	    }
	}
	else if( c == '.' ) {
	    c = getc( InFile );
	    if( (c == '+') || (c == '-') ) {
		*t++ = c;
		do {
		    c = getc( InFile );
		    if( isdigit( c ) ) {
			*t++ = c;
		    }
		} while( isdigit( c ) );
	    }
	    else {
		*t++ = '0';
	    }
	    *t = 0;
	    (void)ungetc( c, InFile );
	    return TOK_DOT;
	}
	else if( (c == '-') || isdigit(c) ) {
	    *t++ = c;
	    do {
		c = getc( InFile );
		if( isdigit( c ) ) {
		    *t++ = c;
		}
	    } while( isdigit( c ) );
	    *t = 0;
	    (void)ungetc( c, InFile );
	    return TOK_INT;
	}
	else {
	    (void)fprintf( stderr, "PC: %ld: illegal character: %c\n",
				(long)PC, c );
	}
    }
}




struct Label *MakeLab( char *Name )
{
    struct Label
	*New;

    New = ckmalloc( sizeof( struct Label ) );
    New->Name = ckmalloc( strlen( Name ) + 1 );
    (void)strcpy( New->Name, Name );
    New->Addr = 0L;
    New->Refs = 0;
    New->Next = LabList;
    LabList = New;
    return New;
}




struct Label *FindLab( char *Name )
{
    struct Label
	*Curr;

    for( Curr = LabList; Curr; Curr = Curr->Next ) {
	if( strcmp( Curr->Name, Name ) == 0 ) {
	    return Curr;
	}
    }
    return 0;
}




void DefineLab( char *Name, Address Addr )
{
    struct Label
	*New;
    struct LabRef
	*Curr, *Next;

    New = FindLab( Name );
    if( New ) {
	if( New->Refs ) {
	    New->Addr = Addr;
	    for( Curr = New->Refs; Curr; Curr = Next ) {
		Memory[Curr->Addr] = Addr;
		Next = Curr->Next;
		free( Curr );
	    }
	    New->Refs = 0;
	}
	else {
	    (void)fprintf( stderr, "Error: multiple definition of label %s\n",
						Name );
	}
    }
    else {
	New = MakeLab( Name );
	New->Addr = Addr;
    }
}




void AddRef( char *Name, Address Addr )
{
    struct Label
	*Curr;
    struct LabRef
	*NewRef;

    Curr = FindLab( Name );
    if( Curr ) {
	if( Curr->Refs ) {
	    NewRef = ckmalloc( sizeof( struct LabRef ) );
	    NewRef->Addr = Addr;
	    NewRef->Next = Curr->Refs;
	    Curr->Refs = NewRef;
	}
	else {
	    Memory[Addr] = Curr->Addr;
	}
    }
    else {
	Curr = MakeLab( Name );
	NewRef = ckmalloc( sizeof( struct LabRef ) );
	NewRef->Addr = Addr;
	NewRef->Next = Curr->Refs;
	Curr->Refs = NewRef;
    }
}




void Execute( void )
{
    Address
	X, Y, Z;

    if( (PC < 0) || (PC >= MemSize) ) {
	(void)fprintf( stderr, "PC %ld out of range\n", (long)PC );
	exit( 0 );
    }

    if( Trace ) {
	(void)printf( "%11ld: ", (long)PC );
    }

    X = Memory[PC++];
    Y = Memory[PC++];
    Z = Memory[PC++];

    if( Trace ) {
	(void)printf( "%11ld %11d %11d\n", (long)X, (long)Y, (long)Z );
    }

    if( Y == READ_LOC ) {
	if( GetTok( stdin, Token ) != TOK_INT ) {
	    (void)fprintf( stderr, "PC: %ld: input error\n", (long)PC );
	}
	Y = atoi( Token );
    }
    else if( (Y < 0) || (Y >= MemSize) ) {
	(void)fprintf( stderr, "PC: %ld: address %ld out of range\n",
			(long)PC, (long)Y );
	exit( 1 );
    }
    else {
	Y = Memory[Y];
    }

    if( X == WRITE_LOC ) {
	/* Implied 'read' of 0 */
	(void)printf( "%ld\n", (long)-Y );
	if( Y > 0 ) {
	    PC = Z;
	}
    }
    else if( (X < 0) || (X >= MemSize) ) {
	(void)fprintf( stderr, "PC: %ld: address %ld out of range\n",
			(long)PC, (long)X );
	exit( 1 );
    }
    else {
	Memory[X] -= Y;
	if( Memory[X] < 0 ) {
	    PC = Z;
	}
    }
}




int main( int argc, char **argv )
{
    struct Label
	*Curr;
    FILE
	*InFile = stdin;	/* Input file */
    int
	PrintSyms = 0,	/* Print symbols? */
	Done = 0,	/* Done reading stuff? */
	DoText = 1;	/* 0 = text, 1 = data */

    for( argc--, argv++; argc > 0; argc--, argv++ ) {
	if( strcmp( *argv, "-syms" ) == 0 ) {
	    /* Turn on symbol printing */
	    PrintSyms = 1;
	}
	else if( strcmp( *argv, "-mem" ) == 0 ) {
	    /* Redefine maximum memory size */
	    if( argc > 1 ) {
		argc--, argv++;
		MemSize = atoi( *argv );
	    }
	}
	else if( strcmp( *argv, "-data" ) == 0 ) {
	    /* Redefine DATA segment starting point */
	    if( argc > 1 ) {
		argc--, argv++;
		MaxText = CurrData = atoi( *argv );
	    }
	}
	else if( strcmp( *argv, "-trace" ) == 0 ) {
	    /* Trace execution */
	    Trace = 1;
	}
	else if( strcmp( *argv, "-file" ) == 0 ) {
	    /* Redefine input file */
	    if( argc > 1 ) {
		argc--, argv++;
		InFile = fopen( *argv, "r" );
		if( !InFile ) {
		    (void)fprintf( stderr, "Unable to open %s\n", *argv );
		    exit( 1 );
		}
	    }
	}
	else {
	    (void)fprintf( stderr, "Valid options are:\n" );
	    (void)fprintf( stderr, "	-syms	-- Print out symbol table\n" );
	    (void)fprintf( stderr, "	-mem #	-- Set memory size\n" );
	    (void)fprintf( stderr, "	-data #	-- Set data segment start\n" );
	    (void)fprintf( stderr, "	-trace	-- Trace execution\n" );
	    (void)fprintf( stderr, "	-file S	-- Read from file S\n" );
	    exit( 1 );
	}
    }

    Memory = ckmalloc( MemSize * sizeof(Address) );

    DefineLab( "READ", READ_LOC );
    DefineLab( "WRITE", WRITE_LOC );
    DefineLab( "STOP", STOP_LOC );

    while( !Done ) {
	switch( GetTok( InFile, Token ) ) {

	case TOK_REF :
	    if( DoText ) {
		if( CurrText >= MaxText ) {
		    (void)fprintf( stderr, "Too much TEXT\n" );
		    exit( 0 );
		}
		AddRef( Token, CurrText++ );
	    }
	    else {
		if( CurrData >= MemSize ) {
		    (void)fprintf( stderr, "Too much DATA\n" );
		    exit( 0 );
		}
		AddRef( Token, CurrData++ );
	    }
	    break;

	case TOK_DEF :
	    DefineLab( Token, DoText ? CurrText : CurrData );
	    break;

	case TOK_INT :
	    if( DoText ) {
		if( CurrText >= MaxText ) {
		    (void)fprintf( stderr, "Too much TEXT\n" );
		    exit( 0 );
		}
		Memory[CurrText++] = atoi( Token );
	    }
	    else {
		if( CurrData >= MemSize ) {
		    (void)fprintf( stderr, "Too much DATA\n" );
		    exit( 0 );
		}
		Memory[CurrData++] = atoi( Token );
	    }
	    break;

	case TOK_DOT :
	    if( DoText ) {
		if( CurrText >= MaxText ) {
		    (void)fprintf( stderr, "Too much TEXT\n" );
		    exit( 0 );
		}
		Memory[CurrText] = CurrText + atoi( Token );
		CurrText++;
	    }
	    else {
		if( CurrData >= MemSize ) {
		    (void)fprintf( stderr, "Too much DATA\n" );
		    exit( 0 );
		}
		Memory[CurrData] = CurrData + atoi( Token );
		CurrData++;
	    }
	    break;

	case TOK_DATA :
	    DoText = 0;
	    break;

	case TOK_TEXT :
	    DoText = 1;
	    break;

	case TOK_EOF :
	    Done = 1;
	    break;

	}
    }

    /* Check for undefined labels */
    for( Curr = LabList; Curr; Curr = Curr->Next ) {
	if( Curr->Refs ) {
	    (void)fprintf( stderr, "Label %s undefined\n", Curr->Name );
	}
	else if( PrintSyms ) {
	    if( (Curr->Addr < 0) || (Curr->Addr >= MemSize) ) {
	    	(void)printf( "%11ld %s %s\n",
			(long)Curr->Addr, "(undefined)", Curr->Name );
	    }
	    else {
	    	(void)printf( "%11ld %11ld %s\n",
			(long)Curr->Addr,(long)Memory[Curr->Addr],Curr->Name );
	    }
	}
    }

    while( PC != STOP_LOC ) {
	Execute();
    }

    exit( 0 );

    return 0;	/* Entirely for lint's benefit */
}

/*** EOF oisc.c ***/
