#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "project.h"

static unsigned addr = 0;	/* Used for tracking label addresses */
static int pass = 0;		/* Pass 1: build map; pass 2: build image */
static FILE* image_file;	/* Memory image */
static FILE* map_file;		/* Labels */

/* This turns assembly into machine code. */
void asm_assemble(char* source, char* image, char* map)
{
	int ret_val;

	yyin = fopen(source, "r");
	if (yyin == NULL) {perror("fopen"); exit(EXIT_FAILURE);}

	/* Pass one: generate map */
	map_file = fopen(map, "wb");
	if (map_file == NULL) {perror("fopen"); exit(EXIT_FAILURE);}
	pass = 1;
	scanner_init();
	ret_val = yyparse();
	if (fclose(map_file) != 0) {perror("fclose"); exit(EXIT_FAILURE);}
	if (ret_val == 1 || yynerrs != 0) {
		if (unlink(map) == -1) perror("unlink");
		exit(EXIT_FAILURE);
	}

	/* Pass two: generate memory image */
	image_file = fopen(image, "wb");
	if (image_file == NULL) {perror("fopen"); exit(EXIT_FAILURE);}
	if (fseek(yyin, 0, SEEK_SET) != 0) {perror("fseek");exit(EXIT_FAILURE);}
	pass = 2;
	scanner_init();
	ret_val = yyparse();
	if (fclose(image_file) != 0) {perror("fclose"); exit(EXIT_FAILURE);}
	if (ret_val == 1 || yynerrs != 0) {
		if (unlink(image) == -1) perror("unlink");
		exit(EXIT_FAILURE);
	}

	/* Done reading */
	if (fclose(yyin) != 0) {perror("fclose"); exit(EXIT_FAILURE);}

	/* That's all, folks!  At least, until it's time to run the virtual
	 * machine on this sucker... */
	exit(EXIT_SUCCESS);
}

/* Label declaration. */
void asm_label_decl(char* name)
{
	char* ret;
	if (pass != 1) return;
	ret = label_add(name, addr);
	if (ret != NULL) {yyerror(ret); yynerrs++;}
	fprintf(map_file, "%-20s %04x\n", name, addr);
}

/* Terminal expression. */
void asm_expr(unsigned val)
{
	if (pass == 1) addr++;
	else {
		char buf[4] = {val >> 24, val >> 16, val >> 8, val};
		if (fwrite(buf, 4, 1, image_file) != 1) {
			perror("fwrite");
			exit(EXIT_FAILURE);
		}
	}
}

/* Register use. */
unsigned asm_reg(char* name)
{
	reg_t* reg;
	
	if (pass == 1) return 0;
	reg = reg_by_name(name);
	if (reg == NULL) {
		yyerror("invalid register");
		yynerrs++;
		return 0;
	}
	return reg->code;
}

/* Instruction use. */
unsigned asm_insn(char* name, unsigned arg1, unsigned arg2, unsigned arg3)
{
	insn_t* insn;

	if (pass == 1) return 0;
	insn = insn_by_name(name);
	if (insn == NULL) {
		yyerror("invalid instruction");
		yynerrs++;
		return 0;
	}
	return insn_encode(insn, arg1, arg2, arg3);
}

/* Label use. */
unsigned asm_label(char* name)
{
	label_t* label;
	
	if (pass == 1) return 0;
	label = label_by_name(name);
	if (label == NULL) {
		yyerror("undefined label");
		yynerrs++;
		return 0;
	}
	return label->addr;
}

/* EOF */

