//
//  EXPAssembler.m
//  StackMachine2
//
//  Created by Ashley on 06/07/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "EXPAssembler.h"
#import "EXPStackMachine.h"
#import "EXPAssemblerConstants.h"

unsigned int getUIntValue(EXPParser *parser, NSDictionary *symbolTable, int pass)
{
	int value = 0;
	BOOL minus = [parser testSymbol:@"-"];
		
	if ([parser isIdentifier]) {
		NSNumber *num = [symbolTable objectForKey:[[parser symbol] uppercaseString]];
		[parser readSymbol];
		if ((num==nil) && (pass==2)) {
			printf("Undefined symbol %s\n", [[parser symbol] UTF8String]);
		} else {
			value = [num intValue];
		}
	} else if ([parser isLiteral] || (minus=[parser testSymbol:@"-"])) {
		value = [[parser symbol] intValue];
		[parser readSymbol];
	} else {
		printf("Token not recognised %s\n", [[parser symbol] UTF8String]);
	}
	
	if (minus) {
		value = -value;
	}

	return value;
}

double getDoubleValue(EXPParser *parser, NSDictionary *symbolTable, int pass)
{
	double value = 0.0;
	BOOL minus  = [parser testSymbol:@"-"];

	if ([parser isIdentifier]) {
		NSNumber *num = [symbolTable objectForKey:[[parser symbol] uppercaseString]];
		[parser readSymbol];
		if ((num==nil) && (pass==2)) {
			printf("Undefined symbol %s\n", [[parser symbol] UTF8String]);
		} else {
			value = [num doubleValue];
		}
	} else if ([parser isLiteral]) {
		value = [[parser symbol] doubleValue];
		[parser readSymbol];
	} else {
		printf("Token not recognised %s\n", [[parser symbol] UTF8String]);
	}

	if (minus) {
		value = -value;
	}

	return value;
}

void getValue(EXPParser *parser, unsigned int datatype, intfloat *value, NSDictionary *symbolTable, int pass)
{
	if (datatype==INTEGER) {
		value->i =  getUIntValue(parser, symbolTable, pass);
	} else {
		value->x = getDoubleValue(parser, symbolTable, pass);
	}
}

BOOL processArgument(EXPParser *parser, unsigned int opcode, unsigned int datatype, unsigned int *addrmode, 
				     intfloat *value, NSDictionary *symbolTable, int pass)
{
	BOOL atSign;
	value->x = 0.0;
	if ([parser testSymbol:@"#"]) {
		*addrmode = IMM;
		getValue(parser, datatype, value, symbolTable, pass);
//		[parser readSymbol];
	} else if ([parser testSymbol:@","]) {
		atSign = [parser testSymbol:@"@"];
		if (![parser testSymbol:@"SP"]) {
			return NO;
		}
		if (![parser testSymbol:@"+"]) {
			*addrmode = atSign?AUTOIND:AUTO;
		} else if (![parser testSymbol:@"-"]) {
			*addrmode = atSign?AUTOIDC:AUTODEC;
		} else {
			return NO;
		}
	} else if ([parser isLiteral] || [parser isIdentifier]) {
		value->i = getUIntValue(parser, symbolTable, pass);
		if (![parser testSymbol:@","]) {
			return NO;
		}
		if ([parser testSymbol:@"GP"]) {
			*addrmode = GLOBAL;
		} else if ([parser testSymbol:@"LP"]) {
			*addrmode = LOCAL;
		} else if ([parser testSymbol:@"SP"]) {
			*addrmode = STACK;
		} else if ([parser testSymbol:@"PC"]) {
			*addrmode = PCREL;
		} else if ([parser testSymbol:@"A"]) {
			if (![parser testSymbol:@","]) {
				return NO;
			}
			atSign = [parser testSymbol:@"@"];
			if ([parser testSymbol:@"GP"]) {
				*addrmode = atSign?INDXGP:INDXG;
			} else if ([parser testSymbol:@"LP"]) {
				*addrmode = atSign?INDXLP:INDXL;
			}
		} else {
		}

	} else {
		return NO;
	}
	
	return YES;
}

BOOL getTypeQualifier(EXPParser *parser, unsigned int *datatype)

{
	*datatype = FLTPOINT;
	if ([parser testSymbol:@"."]) {
		if ([parser testSymbol:@"I"]) {
			*datatype = INTEGER;
		} else if ([parser testSymbol:@"F"]) {
			*datatype = FLTPOINT;
		} else {
			printf("Illegal type qualifier %s\n", [[parser symbol] UTF8String]);
			return NO;
		}
	}
	return YES;
}

BOOL getBranchDistance(EXPParser *parser, unsigned int *branchDistance)

{
	*branchDistance = SHORT;
	if ([parser testSymbol:@"."]) {
		if ([parser testSymbol:@"S"]) {
			*branchDistance = SHORT;
		} else if ([parser testSymbol:@"L"]) {
			*branchDistance = LONG;
		} else {
			printf("Illegal branch distance %s\n", [[parser symbol] UTF8String]);
			return NO;
		}
	}
	return YES;
}

NSDictionary *mnemonicTable = nil;

@implementation EXPAssembler

- (id) init
{
	self = [super init];
	if (self==nil) {
		return nil;
	}
	
	symbolTable = [[NSMutableDictionary alloc] init];
	
	if (mnemonicTable==nil) {
		mnemonicTable = [[NSDictionary alloc] initWithObjectsAndKeys:
			[NSNumber numberWithInt:PSH], @"PSH", 
			[NSNumber numberWithInt:PUL], @"PUL", 
			[NSNumber numberWithInt:LDA], @"LDA", 
			[NSNumber numberWithInt:STA], @"STA", 
			[NSNumber numberWithInt:ADD], @"ADD", 
			[NSNumber numberWithInt:SUB], @"SUB", 
			[NSNumber numberWithInt:MLT], @"MLT", 
			[NSNumber numberWithInt:DVD], @"DVD", 
			[NSNumber numberWithInt:PWR], @"PWR", 
			[NSNumber numberWithInt:GTH], @"GTH", 
			[NSNumber numberWithInt:GTE], @"GTE", 
			[NSNumber numberWithInt:LTH], @"LTH", 
			[NSNumber numberWithInt:LTE], @"LTE", 
			[NSNumber numberWithInt:EQL], @"EQL", 
			[NSNumber numberWithInt:NEQ], @"NEQ", 
			[NSNumber numberWithInt:INC], @"INC", 
			[NSNumber numberWithInt:DEC], @"DEC", 
			[NSNumber numberWithInt:ADJ], @"ADJ", 
			[NSNumber numberWithInt:CLR], @"CLR", 
			[NSNumber numberWithInt:DBNC], @"DBNC", 
			[NSNumber numberWithInt:JMP], @"JMP",
			[NSNumber numberWithInt:JSR], @"JSR",
			[NSNumber numberWithInt:LEA], @"LEA",
			[NSNumber numberWithInt:LEAS], @"LEAS",
			[NSNumber numberWithInt:LEAL], @"LEAL",
			[NSNumber numberWithInt:LEAG], @"LEAG",
			[NSNumber numberWithInt:PEA], @"PEA",

			[NSNumber numberWithInt:ABSA], @"ABSA", 
			[NSNumber numberWithInt:NEGA], @"NEGA", 
			[NSNumber numberWithInt:SQRTA], @"SQRTA", 
			[NSNumber numberWithInt:INTA], @"INTA", 
			[NSNumber numberWithInt:EXPA], @"EXPA", 
			[NSNumber numberWithInt:LNA], @"LNA", 
			[NSNumber numberWithInt:LOG10A], @"LOG10A", 
			[NSNumber numberWithInt:PSHA], @"PSHA", 
			[NSNumber numberWithInt:PULA], @"PULA", 

			[NSNumber numberWithInt:SINA], @"SINA", 
			[NSNumber numberWithInt:COSA], @"COSA", 
			[NSNumber numberWithInt:TANA], @"TANA", 
			[NSNumber numberWithInt:ASINA], @"ASINA", 
			[NSNumber numberWithInt:ACOSA], @"ACOSA", 
			[NSNumber numberWithInt:ATANA], @"ATANA", 
			[NSNumber numberWithInt:SINHA], @"SINHA", 
			[NSNumber numberWithInt:COSHA], @"COSHA", 
			[NSNumber numberWithInt:TANHA], @"TANHA", 
			[NSNumber numberWithInt:ASINHA], @"ASINHA", 
			[NSNumber numberWithInt:ACOSHA], @"ACOSHA", 
			[NSNumber numberWithInt:ATANHA], @"ATANHA", 
			[NSNumber numberWithInt:ERFA], @"ERFA", 
			[NSNumber numberWithInt:FLTA], @"FLTA", 

			[NSNumber numberWithInt:PRT], @"PRT", 
			[NSNumber numberWithInt:PCHR], @"PCHR", 

			[NSNumber numberWithInt:ADDGP], @"ADDGP", 
			[NSNumber numberWithInt:ADDLP], @"ADDLP", 
			[NSNumber numberWithInt:LDGP], @"LDGP", 
			[NSNumber numberWithInt:LDLP], @"LDLP", 
			[NSNumber numberWithInt:LDSP], @"LDSP", 

			[NSNumber numberWithInt:BRA], @"BRA", 
			[NSNumber numberWithInt:BEQ], @"BEQ", 
			[NSNumber numberWithInt:BNE], @"BNE", 
			[NSNumber numberWithInt:BSR], @"BSR", 
			[NSNumber numberWithInt:RTS], @"RTS", 

			[NSNumber numberWithInt:BRK], @"BRK", 
			[NSNumber numberWithInt:HLT], @"HLT", 
			nil
		];
	}
	
	return self;
}

- (void) setMachine:(EXPStackMachine *)machine
{
	[machine retain];
	[_machine release];
	_machine = machine;
}

- (EXPStackMachine *) machine
{
	return _machine;
}

- (BOOL) assemble:(NSArray *)source echo:(BOOL)echo
{
//	NSDictionary *mnemonicTable = [EXPAssemblerMnemonic assemblerMnemonicTable];
	
//	NSMutableDictionary *symbolTable = [[NSMutableDictionary alloc] init];
	[symbolTable removeAllObjects];

	EXPParser *parser = [[EXPParser alloc] init];
	[parser setEcho:NO];
	
	EXPStackMachine *machine = [self machine];

//	int ptr = 0;
//	int initPc = [machine pc];
	int errorCount = 0;
	unsigned int datatype;
	unsigned int branchDistance;
	intfloat value;

//	BOOL echo = NO;
	BOOL success;
	int pass;
	int i;

// Pass 1
	for(pass=1; (pass<=2) && (errorCount==0); pass++) {
		[machine setPtr:0];
		for(i=0; i<[source count]; i++) {
			NSString *line = [source objectAtIndex:i];
			if ((pass==2) && echo) {
				printf("%s\n", [line UTF8String]);
			}
			
			[machine setEcho:(pass==2) && echo];
		
			int len = [line length];
			if (len==0) {
				continue;
			}
		
			unichar ch = [line characterAtIndex:0];
			if ((ch=='#') || (ch==';') || (ch=='%')) {
				continue;
			}
		
			[parser setString:line];
			
			NSString *mnemonic = [[NSString alloc] initWithString:[parser symbol]];
			[parser readSymbol];

			if ([parser testSymbol:@":"]) {
				NSNumber *val = [[NSNumber alloc] initWithInt:(int)[machine ptr]];
				if (pass==1) {
					[symbolTable setObject:val forKey:mnemonic];
				}
				[val release];
				[mnemonic release];
				if ([parser isEndOfLine] || [parser isEndOfFile]) {
					continue;
				}
		
				mnemonic = [[NSString alloc] initWithString:[parser symbol]];
				[parser readSymbol];
			}

//	[mnemonic isEqualToString:@"DIM"]
			if ([mnemonic isEqualToString:@"DEF"]) {
				success = getTypeQualifier(parser, &datatype);
				if (!success) {
					continue;
				}
				for(;;) {
					getValue(parser, datatype, &value, symbolTable, pass);
					if (datatype==FLTPOINT) {
						[machine putDouble:value.x];
					} else if (datatype==INTEGER) {
						[machine putUInt:value.i];
					}
					if (![parser testSymbol:@","]) {
						break;
					}
				}
				continue;
			} else if ([mnemonic isEqualToString:@"DEFS"]) {
				success = getTypeQualifier(parser, &datatype);
				if (!success) {
					continue;
				}
				int nThings = getUIntValue(parser, symbolTable, pass);
				int n;
				if (datatype==INTEGER) {
					for(n=0; n<nThings; n++) {
						[machine putUInt:0];
					}
				} else {
					for(n=0; n<nThings; n++) {
						[machine putDouble:0.0];
					}
				}
				continue;
			}
		
			NSNumber *num = [mnemonicTable objectForKey:mnemonic];
			if (num==nil) {
				printf("Illegal mnemonic %s\n", [mnemonic UTF8String]);
				errorCount++;
				continue;
			}

			unsigned int opcode = (unsigned int)[num intValue];
			unsigned int addrmode;
		
			if (opcode < ABSA) {
				success = getTypeQualifier(parser, &datatype);
				if (!success) {
//					printf("Illegal type qualifier\n");
					errorCount++;
					continue;
				}
				success = processArgument(parser, opcode, datatype, &addrmode, &value, symbolTable, pass);
				if (!success) {
					printf("Illegal operand\n");
					errorCount++;
					continue;
				}
				
				[machine putOpcode:opcode mode:datatype + addrmode];
				if ((addrmode==IMM) && (datatype==FLTPOINT)) {
					[machine putDouble:value.x];
				} else if ((addrmode==IMM) && (datatype==INTEGER)) {
					[machine putUInt:value.i];
				} else if ((addrmode!=AUTO) && (addrmode!=AUTOIND) && (addrmode!=AUTODEC) && (addrmode!=AUTOIDC)) {
					[machine putUInt:value.i];
				}
			
			} else if (opcode==PCHR) {
				success = processArgument(parser, opcode, INTEGER, &addrmode, &value, symbolTable, pass);
				if (!success || addrmode!=IMM) {
					printf("Illegal operand\n");
					errorCount++;
					continue;
				}
				[machine putOpcode:opcode mode:INTEGER + addrmode];
				[machine putUShInt:value.i];

			} else if ((opcode>=LDGP) && (opcode<=LDSP)) {
//				int addr = getUIntValue(parser, symbolTable, pass);
				success = processArgument(parser, opcode, INTEGER, &addrmode, &value, symbolTable, pass);
				if (!success || addrmode!=IMM) {
					printf("Illegal operand\n");
					errorCount++;
					continue;
				}
				[machine putOpcode:opcode mode:0];
				[machine putUInt:value.i];

			} else if ((opcode>=BRA) && (opcode<=BSR)) {
				success = getBranchDistance(parser, &branchDistance);
				if (!success) {
					errorCount++;
					continue;
				}
				[machine putOpcode:opcode mode:branchDistance];
				unsigned int disp = getUIntValue(parser, symbolTable, pass);
				if (branchDistance==SHORT) {
					[machine putUShInt:(unsigned short int)disp];
				} else {
					[machine putUInt:disp];
				}
			
			} else if (opcode==PRT) {
				success = getTypeQualifier(parser, &datatype);
				if (!success) {
//					printf("Illegal type qualifier\n");
					errorCount++;
					continue;
				}
				[machine putOpcode:opcode mode:datatype];
			
			} else if (opcode==DBNC) {
				success = getBranchDistance(parser, &branchDistance);
				if (!success) {
					errorCount++;
					continue;
				}
				success = processArgument(parser, opcode, INTEGER, &addrmode, &value, symbolTable, pass);
				if (!success) {
					printf("Illegal operand\n");
					errorCount++;
					continue;
				}
				unsigned int disp = getUIntValue(parser, symbolTable, pass);
				[machine putOpcode:opcode mode:branchDistance + addrmode];
				if (addrmode!=AUTO) {
					[machine putUInt:value.i];
				}
				if (branchDistance==SHORT) {
					[machine putUShInt:(unsigned short int)disp];
				} else {
					[machine putUInt:disp];
				}

			} else {
				[machine putOpcode:opcode mode:0];
			
			}
		
			[mnemonic release];
		
		}	
//		NSLog(@"Symbol table\n %@", symbolTable);
		
	}
	
	[parser release];

//	[machine setPc:initPc];
	return (errorCount==0);

}

- (id) symbolTable
{
	return symbolTable;
}

- (void) dumpSymboltable
{
	NSEnumerator *enumerator = [symbolTable keyEnumerator];
	id key;
 
	while ((key = [enumerator nextObject])) {
		NSNumber *value = [symbolTable objectForKey:key];
		printf("%-32s %08x %g\n", [key UTF8String], [value intValue], [value doubleValue]);
	}
}

- (void) dealloc
{
	[symbolTable release];
	[super dealloc];
}

@end
