Example #1
0
void codegen_emit(AST_expr *expr, int parent_numArgs, FILE *outputFile) {
	int i, label1, label2, n;
	Environment *closureEnvironment;

	switch(expr->type) {
		case Sequence: 
			for (i=0; i<expr->numBody; i++)
				codegen_emit(expr->body[i], parent_numArgs, outputFile);
			break;

		case Constant: 
			switch (expr->value->type) {
				case Numconst: 
					if (expr->value->value >= 32768) {fprintf(stderr, "ERROR 24: Integer constant too large"); exit(EXIT_FAILURE);}
					fprintf(outputFile, "\tLDI CRSh, %i\n\tLDI CRSl, %i\n", (expr->value->value >> 8) & 127, expr->value->value & 255);
					break;

				case Booleanconst: 
					if (expr->value->value == 0)
						fprintf(outputFile, "\tLDI CRSh, 254\n\tLDI CRSl, 0\n");
					if (expr->value->value == 1)
						fprintf(outputFile, "\tLDI CRSh, 255\n\tLDI CRSl, 0\n");
					break;

				case Charconst:
					fprintf(outputFile, "\tLDI CRSh, 224\n\tLDI CRSl, %i\n", expr->value->value);
					break;

				case Stringconst: // similar to primcall vector case
					n = strlen(expr->value->strvalue);
					fprintf(outputFile, "\tLDI GP1, lo8(%i)\n\tLDI GP2, hi8(%i)\n\tST X+, GP1\n\tST X+, GP2\n", n, n);

					for (i=0; i<n; i++) {
						fprintf(outputFile, "\tLDI CRSh, 224\n\tLDI CRSl, %i\n\tST X+, CRSl\n\tST X+, CRSh\n", expr->value->strvalue[i]);
					}

					fprintf(outputFile, "\tMOV CRSl, HFPl\n\tMOV CRSh, HFPh\n\tSBIW CRSl, %i\n\tORI CRSh, 160\n", 2+2*n);
					break;

				case Emptylistconst:
					fprintf(outputFile, "\tLDI CRSh, 232\n\tLDI CRSl, 0\n");
					break;
			}
			break;

		case Branch:
			label1 = if_end_unique++;
			label2 = if_conseq_unique++;
			codegen_emit(expr->body[0], parent_numArgs, outputFile);
			fprintf(outputFile, "\tCPSE CRSh, falseReg\n\tJMP if_conseq%i\n", label2);
			if (expr->numBody == 3) codegen_emit(expr->body[2], parent_numArgs, outputFile);
			fprintf(outputFile, "\tJMP if_end%i\nif_conseq%i:\n", label1, label2);
			codegen_emit(expr->body[1], parent_numArgs, outputFile);
			fprintf(outputFile, "if_end%i:\n", label1);
			break;

		case And:
			label1 = and_end_unique++;
			for (i=0; i<expr->numBody-1; i++) {
				codegen_emit(expr->body[i], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCPSE CRSh, falseReg\n\tRJMP 1f\n\tJMP and_end%i\n1:", label1);
			}
			codegen_emit(expr->body[expr->numBody-1], parent_numArgs, outputFile);
			fprintf(outputFile, "and_end%i:\n", label1);
			break;

		case Or:
			label1 = or_end_unique++;
			for (i=0; i<expr->numBody-1; i++) {
				codegen_emit(expr->body[i], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCPSE CRSh, falseReg\n\tJMP or_end%i\n", label1);
			}
			codegen_emit(expr->body[expr->numBody-1], parent_numArgs, outputFile);
			fprintf(outputFile, "or_end%i:\n", label1);
			break;

		case Variable: 
			switch(expr->varRefType) {
				case Local: 
					fprintf(outputFile, "\tLDD CRSh, Z+%i\n\tLDD CRSl, Z+%i\n", 2*(parent_numArgs - expr->varRefIndex) - 1, 2*(parent_numArgs - expr->varRefIndex));
					break;

				case Free:
					fprintf(outputFile, "\tMOVW CRSl, CCPl\n");

					for (i=1; i < expr->varRefHop; i++) // traverse the closure-chain
						fprintf(outputFile, "\tLDD GP1, Y+3\n\tLDD CRSh, Y+4\n\tMOV CRSl, GP1\n");

					fprintf(outputFile, "\tLDD GP1, Y+%i\n\tLDD CRSh, Y+%i\n\tMOV CRSl, GP1\n", (2 * expr->varRefIndex) + 5, (2 * expr->varRefIndex) + 6);
					break;

				case Global: 
					if (opt_aggressive) {
						//int realAddress = globalEnv->realAddress[expr->varRefIndex];
						//fprintf(outputFile, "\tLDS CRSh, RAM + %i\n\tLDS CRSl, RAM + %i\n", (2 * realAddress), 1 + (2 * realAddress));
						fprintf(outputFile, "\tLDS CRSh, _global_%i+1\n\tLDS CRSl, _global_%i\n", globalEnv->realAddress[expr->varRefIndex], globalEnv->realAddress[expr->varRefIndex]);
							
					} else {
						//fprintf(outputFile, "\tLDS CRSh, RAM + %i\n\tLDS CRSl, RAM + %i\n", (2 * expr->varRefIndex), 1 + (2 * expr->varRefIndex));
						fprintf(outputFile, "\tLDS CRSh, _global_%i+1\n\tLDS CRSl, _global_%i\n", expr->varRefIndex, expr->varRefIndex);
					}
					break;
			}
			break;

		case Definition:
			if (expr->numBody < 1) {
				// Nothing to do
				return;
			}

		case Assignment:
			switch(expr->varRefType) {
				case Local: 
					codegen_emit(expr->body[0], parent_numArgs, outputFile);
					fprintf(outputFile, "\tSTD Z+%i, CRSh\n\tSTD Z+%i, CRSl\n", 2*(parent_numArgs - expr->varRefIndex) - 1, 2*(parent_numArgs - expr->varRefIndex));
					break;

				case Free: 
					codegen_emit(expr->body[0], parent_numArgs, outputFile);
					fprintf(outputFile, "\tMOVW GP1, CRSl\n\tMOVW CRSl, CCPl\n");

					for (i=1; i < expr->varRefHop; i++) // traverse the closure-chain
						fprintf(outputFile, "\tLDD GP3, Y+3\n\tLDD CRSh, Y+4\n\tMOV CRSl, GP3\n");

					fprintf(outputFile, "\tSTD Y+%i, GP1\n\tSTD Y+%i, GP2\n", (2 * expr->varRefIndex) + 5, (2 * expr->varRefIndex) + 6);
					break;

				case Global: 

					if (opt_aggressive) {
						if (globalEnv->realAddress[expr->varRefIndex] >= 0) {
							//int realAddress = globalEnv->realAddress[expr->varRefIndex];
							codegen_emit(expr->body[0], parent_numArgs, outputFile);
							//fprintf(outputFile, "\tSTS RAM + %i, CRSh\n\tSTS RAM + %i, CRSl\n", (2 * realAddress), 1 + (2 * realAddress));
							fprintf(outputFile, "\tSTS _global_%i+1, CRSh\n\tSTS _global_%i, CRSl\n", globalEnv->realAddress[expr->varRefIndex], globalEnv->realAddress[expr->varRefIndex]);
						}
					} else {
						codegen_emit(expr->body[0], parent_numArgs, outputFile);
						//fprintf(outputFile, "\tSTS RAM + %i, CRSh\n\tSTS RAM + %i, CRSl\n", (2 * expr->varRefIndex), 1 + (2 * expr->varRefIndex));
						fprintf(outputFile, "\tSTS _global_%i+1, CRSh\n\tSTS _global_%i, CRSl\n", expr->varRefIndex, expr->varRefIndex);
					}
				
				break;
			}

			break;

		case ProcCall:
			label1 = proc_ret_unique++;
			fprintf(outputFile, "\tPUSH AFPh\n\tPUSH AFPl\n\tPUSH CCPh\n\tPUSH CCPl\n\tLDI GP1, hi8(pm(proc_ret%i))\n\tPUSH GP1\n\tLDI GP1, lo8(pm(proc_ret%i))\n\tPUSH GP1\n", label1, label1);
			for (i=0; i<expr->numBody; i++) {
				codegen_emit(expr->body[i], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSl\n\tPUSH CRSh\n");
			}
			codegen_emit(expr->proc, parent_numArgs, outputFile);

			fprintf(outputFile, "\tIN AFPl, SPl\n\tIN AFPh, SPh\n");
			if (expr->proc->type == Lambda && expr->proc->stack_allocate)
				fprintf(outputFile, "\tADIW AFPl, %i\n", (expr->proc->closure->numBinds * 2) + 5);
			fprintf(outputFile, "\tMOVW GP5, AFPl\n");

			fprintf(outputFile, "\tLDI PCR, %i\n\tJMP proc_call\nproc_ret%i:\n\tPOP CCPl\n\tPOP CCPh\n\tPOP AFPl\n\tPOP AFPh\n", expr->numBody, label1);
			break;

		case TailCall:

			for (i=0; i<expr->numBody; i++) {
				codegen_emit(expr->body[i], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSl\n\tPUSH CRSh\n");
			}

			codegen_emit(expr->proc, parent_numArgs, outputFile);



			fprintf(outputFile, "\tIN GP3, SPl\n");
			fprintf(outputFile, "\tIN GP4, SPh\n");
			if (parent_numArgs > 0)
				fprintf(outputFile, "\tADIW AFPl, %i\n", 2*parent_numArgs);
			fprintf(outputFile, "\tOUT SPl, AFPl\n");
			fprintf(outputFile, "\tOUT SPh, AFPh\n");
			if (expr->numBody > 0)
				fprintf(outputFile, "\tSBIW AFPl, %i\n", 2*expr->numBody);
			fprintf(outputFile, "\tMOVW GP5, AFPl\n");
			fprintf(outputFile, "\tMOVW AFPl, GP3\n");

			if (expr->proc->type == Lambda && expr->proc->stack_allocate)
				fprintf(outputFile, "\tADIW AFPl, %i\n", (expr->proc->closure->numBinds * 2) + 5);

			if (expr->numBody > 0)
				fprintf(outputFile, "\tADIW AFPl, %i\n", 2*expr->numBody);

			fprintf(outputFile, "1:\tCP AFPl, GP3\n");
			fprintf(outputFile, "\tCPC AFPh, GP4\n");
			fprintf(outputFile, "\tBREQ 2f\n");			
			fprintf(outputFile, "\tLD GP1, Z\n");
			fprintf(outputFile, "\tSBIW AFPl, 1\n");
			fprintf(outputFile, "\tPUSH GP1\n");
			fprintf(outputFile, "\tRJMP 1b\n");

			fprintf(outputFile, "2:\tLDI PCR, %i\n", expr->numBody);
			if (expr->proc->type == Lambda && expr->proc->stack_allocate)
				fprintf(outputFile, "\tIN CRSh, SPh\n\tIN CRSl, SPl\n\tADIW CRSl, 1\n\tORI CRSh, 192\n");
			fprintf(outputFile, "\tJMP proc_call\n");

			break;

		case Lambda: 
			label1 = proc_entry_unique++;
			label2 = proc_after_unique++;
			closureEnvironment = expr->closure;

			
			if (expr->stack_allocate) {
				//fprintf(stderr, "stack allocating...");
				fprintf(outputFile, "\tMOVW GP3, HFPl\n\tIN HFPl, SPl\n\tIN HFPh, SPh\n\tSBIW HFPl, %i\n\tOUT SPl, HFPl\n\tOUT SPh, HFPh\n\tADIW HFPl, 1\n", (closureEnvironment->numBinds * 2) + 5);
			}

			fprintf(outputFile, "\tLDI GP1,%i\n\tST X+, GP1;HFP\n\tLDI GP1, hi8(pm(proc_entry%i))\n\tST X+, GP1\n\tLDI GP1, lo8(pm(proc_entry%i))\n\tST X+, GP1\n", expr->numFormals, label1, label1);

			// Set up the closure chain:
			fprintf(outputFile, "\tST X+, CCPl\n\tST X+, CCPh\n");

			for (i=0; i<closureEnvironment->numBinds; i++) {

				if (closureEnvironment->lexicalAddrV[i] == 1) {
					fprintf(outputFile, "\tLDD CRSh, Z+%i\n\tLDD CRSl, Z+%i\n", 2*(parent_numArgs - closureEnvironment->lexicalAddrH[i]) - 1, 2*(parent_numArgs - closureEnvironment->lexicalAddrH[i]));
					fprintf(outputFile, "\tST X+, CRSl\n\tST X+, CRSh\n");

				} else {
					// erm. something's gone wrong?
				}
			}

			char* lambdaname = "Anonymous";
			if (expr->variable != NULL)
				lambdaname = expr->variable;


			fprintf(outputFile, "\tMOVW CRSl, HFPl\n\tSBIW CRSl, %i\n\tORI CRSh, 192\n", (closureEnvironment->numBinds * 2) + 5);

			if (expr->stack_allocate) {
				fprintf(outputFile, "\tMOVW HFPl, GP3\n");
			}

			fprintf(outputFile, "\tJMP proc_after%i\nproc_entry%i: ; %s\n\tMOVW AFPl, GP5\n", label2, label1, lambdaname);
			for (i=0; i<expr->numBody; i++) codegen_emit(expr->body[i], expr->numFormals, outputFile);
			if (expr->body[expr->numBody-1]->type != TailCall) fprintf(outputFile, "\tADIW AFPl, %i\n\tOUT SPl, AFPl\n\tOUT SPh, AFPh\n\tPOP AFPl\n\tPOP AFPh\n\tIJMP\n", 2 * expr->numFormals);
			fprintf(outputFile, "proc_after%i:\n", label2);

			break;

		case OtherFundemental:
			if (strcmp(expr->primproc, "list") == 0) {
				for (i=0; i<expr->numBody; i++) {
					codegen_emit(expr->body[i], parent_numArgs, outputFile);
					fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				}

				fprintf(outputFile, "\tLDI CRSh, 232\n\tLDI CRSl, 0\n");

				for (i=0; i<expr->numBody; i++) {
					fprintf(outputFile, "\tPOP GP1\n\tPOP GP2\n\tCALL inline_cons\n");
				}
			}

			else if (strcmp(expr->primproc, "vector") == 0) {

				// Here we do some static analysis, in order to select the faster vector-building routine
				// whenever possible:
				bool all_const = true;

				for (i=0; i<expr->numBody; i++) {
					if (expr->body[i]->type != Constant)
						all_const = false;
				}

				if (all_const) {

					fprintf(outputFile, "\tLDI GP1, lo8(%i)\n\tLDI GP2, hi8(%i)\n\tST X+, GP1\n\tST X+, GP2\n", expr->numBody, expr->numBody);

					for (i=0; i<expr->numBody; i++) {
						codegen_emit(expr->body[i], parent_numArgs, outputFile);
						fprintf(outputFile, "\tST X+, CRSl\n\tST X+, CRSh\n");
					}

					fprintf(outputFile, "\tMOVW CRSl, HFPl\n\tSUBI CRSl, lo8(%i)\n\tSBCI CRSh, hi8(%i)\n\tORI CRSh, 160\n", 2+2*expr->numBody, 2+2*expr->numBody);
				
				} else {

					for (i=expr->numBody-1; i>=0; i--) {
						codegen_emit(expr->body[i], parent_numArgs, outputFile);
						fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
					}

					fprintf(outputFile, "\tLDI GP1, lo8(%i)\n\tLDI GP2, hi8(%i)\n\tST X+, GP1\n\tST X+, GP2\n", expr->numBody, expr->numBody);

					for (i=0; i<expr->numBody; i++) {
						fprintf(outputFile, "\tPOP CRSl\n\tPOP CRSh\n");
						fprintf(outputFile, "\tST X+, CRSl\n\tST X+, CRSh\n");
					}

					fprintf(outputFile, "\tMOVW CRSl, HFPl\n\tSUBI CRSl, lo8(%i)\n\tSBCI CRSh, hi8(%i)\n\tORI CRSh, 160\n", 2+2*expr->numBody, 2+2*expr->numBody);
				}
			}

			else if (strcmp(expr->primproc, "free!") == 0) {
				fprintf(outputFile, "\tPUSH HFPh\n\tPUSH HFPl\n");

				for (i=0; i<expr->numBody; i++) {
					codegen_emit(expr->body[i], parent_numArgs, outputFile);
				}

				fprintf(outputFile, "\tPOP HFPl\n\tPOP HFPh\n");
			}

			else if (strcmp(expr->primproc, "@if-model") == 0 && expr->numBody == 2) {
				if (strcmp(expr->body[0]->value->strvalue, model) == 0) {
					codegen_emit(expr->body[1], parent_numArgs, outputFile);
				}

			}
			
			else if (strcmp(expr->primproc, "call-c-func") == 0 && expr->numBody > 0 && expr->numBody <= 10) {
				fprintf(outputFile, "\tCALL before_c_func\n");
				
				//load args into (24:25) -> (8:9) descending l:h
				for (i=1; i<expr->numBody; i++) {
					codegen_emit(expr->body[i], parent_numArgs, outputFile);
					fprintf(outputFile, "\tMOV r%i, CRSl\n\tMOV r%i, CRSh\n", 26 - (i * 2), 27 - (i * 2));
				}

				fprintf(outputFile, "\tCALL %s\n\tCALL after_c_func\n", expr->body[0]->value->strvalue);
			}

			else if (strcmp(expr->primproc, "include-asm") == 0 && expr->numBody == 1) {
				fprintf(outputFile, ".include \"%s\"\n", expr->body[0]->value->strvalue);
			}

			else if (strcmp(expr->primproc, "asm") == 0) {
				for (i=0; i<expr->numBody; i++) {
					fprintf(outputFile, "\t%s\n", expr->body[i]->value->strvalue);
				}
			}

			break;


		case PrimCall:
			if ((strcmp(expr->primproc, "=") == 0 || strcmp(expr->primproc, "eq?") == 0) && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSl\n\tPUSH CRSh\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP2\n\tPOP GP1\n\tCP GP2, CRSh\n\tBRNE 1f\n\tLDI CRSh, trueHigh\n\tCPSE GP1, CRSl\n\t1:LDI CRSh, falseHigh\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "zero?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tMOVW GP1, CRSl\n\tCP zeroReg, CRSh\n\tBRNE 1f\n\tLDI CRSh, trueHigh\n\tCPSE zeroReg, CRSl\n\t1:LDI CRSh, falseHigh\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "¬") == 0 && expr->numBody == 1) {
				// A quick, binary-valid not:
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tLDI GP1, 1\n\tEOR CRSh, GP1\n");
			}

			else if (strcmp(expr->primproc, "not") == 0 && expr->numBody == 1) {
				// the (false? ...) version of not:
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tMOVW GP1, CRSl\n\tCP falseReg, CRSh\n\tBRNE 1f\n\tLDI CRSh, trueHigh\n\tCPSE zeroReg, CRSl\n\t1:LDI CRSh, falseHigh\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "+") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tADD CRSl, GP1\n\tADC CRSh, GP2\n");
			}

			else if (strcmp(expr->primproc, "-") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tSUB CRSl, GP1\n\tSBC CRSh, GP2\n");
			}

			else if (strcmp(expr->primproc, "*") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tMOV GP3, CRSh\n\tMOV GP4, CRSl\n\tMUL GP1, GP4\n\tMOV CRSl, MLX1\n\tMOV CRSh, MLX2\n\tMUL GP2, GP4\n\tADD CRSh, MLX1\n\tMUL GP1, GP3\n\tADD CRSh, MLX1\n");
			}

			else if (strcmp(expr->primproc, "div") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_div\n");
			}

			else if (strcmp(expr->primproc, "mod") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_div\n");

				fprintf(outputFile, "\tADD GP1, GP3\n\tADC GP2, GP4\n\tMOVW CRSl, GP1\n");
			}

			else if (strcmp(expr->primproc, "stacksize") == 0 && expr->numBody == 0) {
				fprintf(outputFile, "\tIN GP1, SPl\n\tIN GP2, SPh\n\tLDI CRSl, lo8(__stack)\n\tLDI CRSh, hi8(__stack)\n\tSUB CRSl, GP1\n\tSBC CRSh, GP2\n");
			}

			else if (strcmp(expr->primproc, "heapsize") == 0 && expr->numBody == 0) {
				fprintf(outputFile, "\tMOVW CRSl, HFPl\n\tSUBI CRSl, lo8(_end)\n\tSBCI CRSh, hi8(_end)\n");
			}

			else if (strcmp(expr->primproc, "error") == 0 && expr->numBody == 0) {
				fprintf(outputFile, "\tJMP error_custom\n");
			}

			else if (strcmp(expr->primproc, "assert") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSER GP1\n\tCPSE CRSh, GP1\n\tJMP error_custom\n");
			}

			else if (strcmp(expr->primproc, "number?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tROL CRSh\n\tROL CRSh\n\tANDI CRSh, 1\n\tCOM CRSh\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "pair?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 31\n\tLDI GP1, 159\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 224\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "procedure?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 31\n\tLDI GP1, 223\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 224\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "char?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 7\n\tLDI GP1, 231\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 248\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "boolean?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 7\n\tLDI GP1, 255\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 248\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "null?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 7\n\tLDI GP1, 239\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 248\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "cons") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP1\n\tPOP GP2\n\tCALL inline_cons\n");
			}

			else if (strcmp(expr->primproc, "car") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCALL inline_car\n");
			}

			else if (strcmp(expr->primproc, "cdr") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCALL inline_cdr\n");
			}

			else if (strcmp(expr->primproc, "set-car!") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP1\n\tPOP GP2\n\tCALL inline_set_car\n");
			}

			else if (strcmp(expr->primproc, "set-cdr!") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP1\n\tPOP GP2\n\tCALL inline_set_cdr\n");
			}

			else if (strcmp(expr->primproc, "make-vector") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tMOVW GP1, CRSl\n\tMOVW CRSl, HFPl\n\tORI CRSh, 160\n\tST X+, GP1\n\tST X+, GP2\n\tLSL GP1\n\tROL GP2\n\tADD HFPl, GP1\n\tADC HFPh, GP2\n");
			}

			else if (strcmp(expr->primproc, "vector?") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tORI CRSh, 31\n\tLDI GP1, 191\n\tCPSE CRSh, GP1\n\tCBR CRSh, 1\n\tORI CRSh, 224\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "vector-ref") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP1\n\tPOP GP2\n\tCALL inline_vector_ref\n");
			}

			else if (strcmp(expr->primproc, "vector-set!") == 0 && expr->numBody == 3) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[2], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP3\n\tPOP GP4\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_vector_set\n");
			}

			else if (strcmp(expr->primproc, "vector-length") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCALL inline_vector_length\n");
			}

			else if (strcmp(expr->primproc, ">") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_gt\n");
			}

			else if (strcmp(expr->primproc, "<") == 0 && expr->numBody == 2) {
				// (a < b)  ==  (b > a)
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_gt\n");
			}

			else if (strcmp(expr->primproc, ">=") == 0 && expr->numBody == 2) {
				// (a >= b) == ¬(b > a)
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_gt\n");
				// NOT:
				fprintf(outputFile, "\tLDI GP1, 1\n\tEOR CRSh, GP1\n");
			}

			else if (strcmp(expr->primproc, "<=") == 0 && expr->numBody == 2) {
				// (a <= b) == ¬(a > b)
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPUSH CRSh\n\tPUSH CRSl\n");
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tSBRC CRSh, 7\n\tJMP error_notnum\n\tPOP GP1\n\tPOP GP2\n\tCALL inline_gt\n");
				// NOT:
				fprintf(outputFile, "\tLDI GP1, 1\n\tEOR CRSh, GP1\n");
			}

			else if (strcmp(expr->primproc, "digital-state") == 0 && expr->numBody == 2) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSl\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP3\n\tLD GP4, Y\n\tLDI CRSh, trueHigh\n\tAND GP4, GP3\n\tCPSE GP4, GP3\n\tLDI CRSh, falseHigh\n\tCLR CRSl\n");
			}

			else if (strcmp(expr->primproc, "set-digital-state") == 0 && expr->numBody == 3) {
				codegen_emit(expr->body[1], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSl\n");
				codegen_emit(expr->body[2], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPUSH CRSh\n");
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tPOP GP4\n\tPOP GP3\n\tLD GP5, Y\n\tOR GP5, GP3\n\tCOM GP3\n\tSBRS GP4, 0\n\tAND GP5, GP3\n\tST Y, GP5\n");					
			}

			else if (strcmp(expr->primproc, "pause") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCALL util_pause\n");
			}

			else if (strcmp(expr->primproc, "micropause") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCALL util_micropause\n");
			}

			else if (strcmp(expr->primproc, "char->number") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tCLR CRSh\n");
			}

			else if (strcmp(expr->primproc, "arity") == 0 && expr->numBody == 1) {
				codegen_emit(expr->body[0], parent_numArgs, outputFile);
				fprintf(outputFile, "\tMOV GP1, CRSh\n\tANDI GP1, 224\n\tLDI GP2, 192\n\tCPSE GP1, GP2\n\tJMP error_notproc\n\tANDI CRSh, 31\n\tLD GP1, Y;CRS\n\tMOV CRSl, GP1\n\tMOV CRSh, zeroReg\n");
			}

			else {
				fprintf(stderr, "ERROR 26: No primitive '%s' taking %i arguments.\n", expr->primproc, expr->numBody);
				exit(1);
			}

			break;


		
		default: // this state really shouldn't be reached...
			fprintf(outputFile, "ERROR 27: Internal Error");
			exit(EXIT_FAILURE);
	}
}
Example #2
0
int main(int argc, char *argv[]) {

	// First, we process the user's command-line arguments, which dictate the input file
	// and target processor.

	char* fname, *inname, *outname;
	int c;

	opterr = 0;
	while ((c = getopt (argc, argv, "iaucpsovrm:d:t:")) != -1)
	switch (c)	{
		case 'i':	opt_includeonce = false;	break;
		case 'u':	opt_upload = true;			
		case 'a':	opt_assemble = true;		break;
		case 'c':	opt_cleanup = true;			break;
		case 'p':	opt_primitives = false;		break;
		case 's':	opt_stdlib = false;			break;
		case 'o':	opt_aggressive = false;		break;
		case 'v':	opt_verbose = true;			break;
		case 'r':	opt_verify = true;			break;
		case 'm':	model = optarg;				break;
		case 'd':	device = optarg;			break;
		case 't':	treeshaker_max_rounds = atoi(optarg);	break;
		case '?':
			if (optopt == 'm')
				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
			else if (isprint (optopt))
				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
			else
				fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
			return 1;
		default:
			abort ();
	}

	fprintf(stdout, "Microscheme 0.8, (C) Ryan Suchocki\n");

	if (argc < 2) {
		fprintf(stdout, "usage: microscheme [-iaucpsov] [-m model] [-d device] [-t treeshaker-rounds] program[.ms]\n");
		return(EXIT_FAILURE);
	}

	fname=argv[optind];

	if (strncmp(fname + strlen(fname) - 3, ".ms", 3) == 0)
		fname[strlen(fname) - 3] = 0;

	inname=str_clone_more(fname, 3);
	outname=str_clone_more(fname, 2);
	strcat(inname, ".ms");
	strcat(outname, ".s");

	if (argc == optind) {
		fprintf(stderr, "No input file.\n");
		exit(EXIT_FAILURE);
	}

	if (argc > optind + 1) {
		fprintf(stderr, "Multiple input files not yet supported.\n");
		exit(EXIT_FAILURE);
	}








	// This function controls the overall compilation process, which is implemented
	// as four seperate phases, invoked in order.

	// 1) Lex the file
	lexer_tokenNode *root = NULL;

	if (opt_primitives)
		root = lexer_lexBlob(src_primitives_ms, src_primitives_ms_len, root);

	if (opt_stdlib)
		root = lexer_lexBlob(src_stdlib_ms, src_stdlib_ms_len, root);

	root = lexer_lexFile(inname, root);

	globalIncludeList = try_malloc(sizeof(char*));
	globalIncludeList[0] = str_clone(inname);
	globalIncludeListN = 1;

	// 2) Parse the file
	AST_expr *ASTroot = parser_parseFile(root->children, root->numChildren, true);

	// (We can free the memory used by the lexer once the parser has finished)...
	lexer_freeTokenTree(root);

	// We set up a global environment:
	globalEnv = try_malloc(sizeof(Environment));
	scoper_initEnv(globalEnv);

	// And hand it to the scoper...
	currentEnvironment = globalEnv;

	// At the top level, there is no 'current closure', and hence no 'current closure environment'
	currentClosureEnvironment = NULL;

	// 3) Scope the file:
	ASTroot = scoper_scopeExpr(ASTroot);

	numPurgedGlobals = -1;
	int latestpurge = -2;
	int rounds = 0;

	if (opt_aggressive) {
		globalEnv->realAddress = try_malloc(sizeof(int) * globalEnv->numBinds);
		int i;
		for (i = 0; i < globalEnv->numBinds; i++) {
			globalEnv->realAddress[i] = 0;
		}

		while ((numPurgedGlobals > latestpurge) && (rounds < treeshaker_max_rounds)) {
			//fprintf(stderr, ">> ROUND %i\n", roundi);
			rounds++;
			latestpurge = numPurgedGlobals;

			treeshaker_shakeExpr(ASTroot);
			treeshaker_purge();

			//fprintf(stderr, ">> Aggressive: %i globals purged!\n", numPurgedGlobals);
		} 

		fprintf(stdout, ">> Treeshaker: After %i rounds: %i globals purged! %i bytes will be reserved.\n", rounds, numPurgedGlobals, numUsedGlobals * 2);

		if (opt_verbose) {
			fprintf(stdout, ">> Remaining globals: [");
			int i;
			for (i = 0; i < globalEnv->numBinds; i++) {
				if (globalEnv->realAddress[i] >= 0)
					fprintf(stdout, "%s ", globalEnv->binding[i]);
			}
			fprintf(stdout, "]\n");
		}

		
	} else {
		numUsedGlobals = globalEnv->numBinds;
	}

	// 4) Generate code. (Starting with some preamble)

	FILE *outputFile;
	outputFile = fopen(outname, "w");

		codegen_emitModelHeader(model, outputFile);
		codegen_emitPreamble(outputFile, numUsedGlobals);

		// Next, we recursively emit code for the actual program body:
		codegen_emit(ASTroot, 0, outputFile, model, NULL, opt_aggressive, globalEnv);

		// Finally, we emit some postamble code
		codegen_emitPostamble(outputFile);

	fclose(outputFile);

	// Finally, the memory allocated during parsing can be freed.
	parser_freeAST(ASTroot);
	freeEnvironment(globalEnv);

	// If we've reached this stage, then everything has gone OK:
	fprintf(stdout, ">> %i lines compiled OK\n", fileLine);






	char cmd[100];
	char *STR_LEVEL, *STR_TARGET, *STR_PROG, *STR_BAUD;

	if (strcmp(model, "MEGA") == 0) {
		STR_LEVEL  = "avr6";
		STR_TARGET = "atmega2560";
		STR_PROG   = "wiring";
		STR_BAUD   = "115200";
	} else if (strcmp(model, "UNO") == 0) {
		STR_LEVEL  = "avr5";
		STR_TARGET = "atmega328p";
		STR_PROG   = "arduino";
		STR_BAUD   = "115200";
	} else if (strcmp(model, "LEO") == 0) {
		STR_LEVEL  = "avr5";
		STR_TARGET = "atmega32u4";
		STR_PROG   = "arduino";
		STR_BAUD   = "115200";
	} else {
		fprintf(stderr, "Device not supported.\n");
		return EXIT_FAILURE;
	}




	if (opt_assemble) {
		if (strcmp(model, "") == 0) {
			fprintf(stderr, "Model Not Set. Cannot assemble.\n");
			return EXIT_FAILURE;
		}

		fprintf(stderr, ">> Assembling...\n");
		sprintf(cmd, "avr-gcc -mmcu=%s -o %s.elf %s.s", STR_LEVEL, fname, fname);

		try_execute(cmd);

		sprintf(cmd, "avr-objcopy --output-target=ihex %s.elf %s.hex", fname, fname);
		
		try_execute(cmd);
	}

	if (opt_upload) {

		if (strcmp(device, "") == 0) {
			fprintf(stderr, "Device Not Set. Cannot upload.\n");
			return EXIT_FAILURE;
		}

		fprintf(stderr, ">> Uploading...\n");

		char *opt1, *opt2;
		if (opt_verbose) opt1 = "-v"; else opt1 = "";
		if (opt_verify) opt2 = ""; else opt2 = "-V";

		sprintf(cmd, "avrdude %s %s -p %s -c %s -P %s -b %s -D -U flash:w:%s.hex:i", opt1, opt2, STR_TARGET, STR_PROG, device, STR_BAUD, fname);
		
		try_execute(cmd);
	}

	if (opt_cleanup) {
		fprintf(stdout, ">> Cleaning Up...\n");

		#ifdef __WIN32 // Defined for both 32 and 64 bit environments
			sprintf(cmd, "del %s.s %s.elf %s.hex", fname, fname, fname);
		#else
			sprintf(cmd, "rm -f %s.s %s.elf %s.hex", fname, fname, fname);
		#endif

		try_execute(cmd);
	}

	fprintf(stdout, ">> Finished.\n");

	try_free(inname);
	try_free(outname);

	int i;
	for (i=0; i<globalIncludeListN; i++)
		try_free(globalIncludeList[i]);

	try_free(globalIncludeList);

	return EXIT_SUCCESS;
}
Example #3
0
int main(int argc, char *argv[]) {

	// First, we process the user's command-line arguments, which dictate the input file
	// and target processor.

	char *inname, *outname, *basename, *shortbase;
	int c;

	fprintf(stdout, "Microscheme 0.9.3, (C) Ryan Suchocki\n");

	char *helpmsg = "\nUsage: microscheme [-aucvrio] [-m model] [-d device] [-p programmer] [-w filename] [-t rounds] program[.ms]\n\n"
			"Option flags:\n"
			"  -a    Assemble (implied by -u) (requires -m)\n"
			"  -u    Upload (requires -d)\n"
			"  -c    Cleanup (removes intermediate files)\n"
			"  -v    Verbose\n"
			"  -r    Verify (Uploading takes longer)\n"
			"  -i    Allow the same file to be included more than once\n"
			"  -o    Disable optimisations  \n"
			"  -h    Show this help message \n\n"
			"Configuration flags:\n"
			"  -m model       Specify a model (UNO/MEGA/LEO...)\n"
			"  -d device      Specify a physical device\n"
			"  -p programmer  Tell avrdude to use a particular programmer\n"
			"  -w files       'Link' with external C or assembly files\n"
			"  -t rounds      Specify the maximum number of tree-shaker rounds\n";

	while ((c = getopt(argc, argv, "hiaucovrm:d:p:t:w:")) != -1)
	switch (c)	{
		case 'h':	fprintf(stdout, "%s", helpmsg); exit(EXIT_SUCCESS); break;
		case 'i':	opt_includeonce = false;	break;
		case 'u':	opt_upload = true;			
		case 'a':	opt_assemble = true;		break;
		case 'c':	opt_cleanup = true;			break;
		//case 's':	opt_softreset = true;		break;
		case 'o':	opt_aggressive = false;		break;
		case 'v':	opt_verbose = true;			break;
		case 'r':	opt_verify = true;			break;
		case 'm':	model = optarg;				break;
		case 'd':	device = optarg;			break;
		case 'p':   programmer = optarg;		break;
		case 'w':	linkwith = optarg;			break;
		case 't':	treeshaker_max_rounds = atoi(optarg);	break;
		case '?':
			if (optopt == 'm')
				fprintf (stderr, "Option -%c requires an argument.\n", optopt);
			else if (isprint (optopt))
				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
			else
				fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
			return 1;
		default:
			abort ();
	}


	if (argc < 2) {
		fprintf(stdout, "%s", helpmsg);

		return(EXIT_FAILURE);
	}

	inname=argv[optind];

	basename=str_clone(inname);

	#ifdef __WIN32 // Defined for both 32 and 64 bit environments
		char delimit = '\\';
	#else
		char delimit = '/';
	#endif

	if (strrchr(basename, delimit)) {
		shortbase = strrchr(basename, delimit) + 1;
	} else {
		shortbase = basename;
	}

	shortbase[strcspn(shortbase, ".")] = 0;

	outname=str_clone_more(shortbase, 2);
	strcat(outname, ".s");

	if (argc == optind) {
		fprintf(stderr, "No input file.\n");
		exit(EXIT_FAILURE);
	}

	if (argc > optind + 1) {
		fprintf(stderr, "Multiple input files not yet supported.\n");
		exit(EXIT_FAILURE);
	}

	model_info theModel;
	int i;
	bool found = false;
	
	for (i = 0; i<numModels; i++) {
		if (strcmp(models[i].name, model) == 0) {
			theModel = models[i];
			found = true;
		}
	}

	if (!found) {
		fprintf(stderr, "Device not supported.\n");
		return EXIT_FAILURE;
	}







	// This function controls the overall compilation process, which is implemented
	// as four seperate phases, invoked in order.

	// 1) Lex the file
	lexer_tokenNode *root = NULL;

	root = lexer_lexBlob(src_primitives_ms, src_primitives_ms_len, root);

	root = lexer_lexBlob(src_stdlib_ms, src_stdlib_ms_len, root);

	root = lexer_lexFile(inname, root);

	globalIncludeList = try_malloc(sizeof(char*));
	globalIncludeList[0] = str_clone(inname);
	globalIncludeListN = 1;

	// 2) Parse the file
	AST_expr *ASTroot = parser_parseFile(root->children, root->numChildren);

	// (We can free the memory used by the lexer once the parser has finished)...
	lexer_freeTokenTree(root);

	// We set up a global environment:
	globalEnv = try_malloc(sizeof(Environment));
	scoper_initEnv(globalEnv);

	// And hand it to the scoper...
	currentEnvironment = globalEnv;

	// At the top level, there is no 'current closure', and hence no 'current closure environment'
	currentClosureEnvironment = NULL;

	// 3) Scope the file:
	ASTroot = scoper_scopeExpr(ASTroot);

	numPurgedGlobals = -1;
	int latestpurge = -2;
	int rounds = 0;

	if (opt_aggressive) {
		globalEnv->realAddress = try_malloc(sizeof(int) * globalEnv->numBinds);
		int i;
		for (i = 0; i < globalEnv->numBinds; i++) {
			globalEnv->realAddress[i] = 0;
		}

		while ((numPurgedGlobals > latestpurge) && (rounds < treeshaker_max_rounds)) {
			//fprintf(stderr, ">> ROUND %i\n", roundi);
			rounds++;
			latestpurge = numPurgedGlobals;

			treeshaker_shakeExpr(ASTroot);
			treeshaker_purge();

			//fprintf(stderr, ">> Aggressive: %i globals purged!\n", numPurgedGlobals);
		} 

		fprintf(stdout, ">> Treeshaker: After %i rounds: %i globals purged! %i bytes will be reserved.\n", rounds, numPurgedGlobals, numUsedGlobals * 2);

		if (opt_verbose) {
			fprintf(stdout, ">> Remaining globals: [");
			int i;
			for (i = 0; i < globalEnv->numBinds; i++) {
				if (globalEnv->realAddress[i] >= 0)
					fprintf(stdout, "%s ", globalEnv->binding[i]);
			}
			fprintf(stdout, "]\n");
		}

		
	} else {
		numUsedGlobals = globalEnv->numBinds;
	}

	// 4) Generate code. (Starting with some preamble)

	FILE *outputFile;
	outputFile = fopen(outname, "w");

	if (!outputFile) {
		fprintf(stderr, ">> Error! Could not open output file.\n");
		exit(EXIT_FAILURE);
	}

		codegen_emitModelHeader(theModel, outputFile);
		codegen_emitPreamble(outputFile);

		// Next, we recursively emit code for the actual program body:
		codegen_emit(ASTroot, 0, outputFile);

		// Finally, we emit some postamble code
		codegen_emitPostamble(outputFile);

	fclose(outputFile);

	// Finally, the memory allocated during parsing can be freed.
	parser_freeAST(ASTroot);
	freeEnvironment(globalEnv);

	// If we've reached this stage, then everything has gone OK:
	fprintf(stdout, ">> %i lines compiled OK\n", fileLine);

	char cmd[500];

	if (opt_assemble) {
		if (strcmp(model, "") == 0) {
			fprintf(stderr, "Model Not Set. Cannot assemble.\n");
			return EXIT_FAILURE;
		}

		fprintf(stderr, ">> Assembling...\n");
		sprintf(cmd, "avr-gcc -mmcu=%s -o %s.elf %s.s %s", theModel.STR_TARGET, shortbase, shortbase, linkwith);

		try_execute(cmd);

		sprintf(cmd, "avr-objcopy --output-target=ihex %s.elf %s.hex", shortbase, shortbase);
		
		try_execute(cmd);
	}

	// if (opt_softreset && theModel.software_reset) {
	// 	fprintf(stdout, ">> Attempting software reset...\n");
	// 	int fd = -1;
	// 	struct termios options;
		
	// 	tcgetattr(fd, &options);

	// 	cfsetispeed(&options, B1200);
	// 	cfsetospeed(&options, B1200);
		
	// 	options.c_cflag |= (CLOCAL | CREAD | CS8 | HUPCL);
	// 	options.c_cflag &= ~(PARENB | CSTOPB);
		
	// 	tcsetattr(fd, TCSANOW, &options);

	// 	fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
	// 	if (fd == -1) {
	// 		fprintf(stderr, ">> Warning: Unable to open %s for soft reset. (%s)\n", device, strerror(errno));
	// 	} else {
	// 		close(fd);
	// 	}
	// }

	if (opt_upload) {

		if (strcmp(device, "") == 0) {
			fprintf(stderr, "Device Not Set. Cannot upload.\n");
			return EXIT_FAILURE;
		}

		if (strcmp(programmer, "") == 0) {
			programmer = theModel.STR_PROG;
		}

		fprintf(stderr, ">> Uploading...\n");

		char *opt1, *opt2;
		if (opt_verbose) opt1 = "-v"; else opt1 = "";
		if (opt_verify) opt2 = ""; else opt2 = "-V";

		sprintf(cmd, "avrdude %s %s -p %s -P %s -b %s -c %s -D -U flash:w:%s.hex:i", opt1, opt2, theModel.STR_TARGET, device, theModel.STR_BAUD, programmer, shortbase);
		
		try_execute(cmd);
	}

	if (opt_cleanup) {
		fprintf(stdout, ">> Cleaning Up...\n");

		#ifdef __WIN32 // Defined for both 32 and 64 bit environments
			sprintf(cmd, "del %s.s %s.elf %s.hex", shortbase, shortbase, shortbase);
		#else
			sprintf(cmd, "rm -f %s.s %s.elf %s.hex", shortbase, shortbase, shortbase);
		#endif

		try_execute(cmd);
	}

	fprintf(stdout, ">> Finished.\n");

	try_free(basename);
	try_free(outname);

	for (i=0; i<globalIncludeListN; i++)
		try_free(globalIncludeList[i]);

	try_free(globalIncludeList);

	return EXIT_SUCCESS;
}