protected string[] generatePushToStackFromSymbolImmediate(string symbol, InstructionData currentVMinstruction) { var output = new List <string>(); output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); output.Add(symbol); output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); output.Add(stackPointer_symbol); output.AddRange(generateIncrement(stackPointer_symbol)); return(output.ToArray()); }
protected string[] generatePopFromStackToSymbol(string symbol, InstructionData currentVMinstruction) { var output = new List <string>(); output.AddRange(generateDecrement(stackPointer_symbol)); output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); output.Add(stackPointer_symbol); //TODO...treat symbol as pointer as argument to this func? output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); output.Add(symbol); return(output.ToArray()); }
public void handleCommand(InstructionData instructionData) { switch (instructionData.CommandType) { case vmILParser.vmCommandType.ARITHMETIC: handleArithmeticCommand(instructionData); break; case vmILParser.vmCommandType.PUSH: handlePushPop(instructionData); break; case vmILParser.vmCommandType.POP: handlePushPop(instructionData); break; case vmILParser.vmCommandType.LABEL: { handleControlFlow(instructionData); break; } case vmILParser.vmCommandType.IF: { handleControlFlow(instructionData); break; } case vmILParser.vmCommandType.GOTO: { handleControlFlow(instructionData); break; } case vmILParser.vmCommandType.CALL: handleFunctionCallingCommand(instructionData); break; case vmILParser.vmCommandType.RETURN: handleFunctionCallingCommand(instructionData); break; case vmILParser.vmCommandType.FUNCTION: handleFunctionCallingCommand(instructionData); break; default: throw new Exception($"unkown command:{instructionData.CommandType}"); } }
private void handleControlFlow(InstructionData instructionData) { if (instructionData.CommandType == vmCommandType.LABEL) { this.Output.Add("//handle VM LABEL"); var labelString = instructionData.Operands.FirstOrDefault(); this.Output.Add($"({labelString})"); } if (instructionData.CommandType == vmCommandType.GOTO) { this.Output.Add("//handle GOTO"); var labelToJumpTo = instructionData.Operands.FirstOrDefault(); this.Output.Add(assembler.CommandType.JUMP.ToString()); this.Output.Add(labelToJumpTo); } if (instructionData.CommandType == vmCommandType.IF) { this.Output.Add("//handle IF GOTO"); var blockID = Guid.NewGuid().ToString("N"); var labelToJumpTo = instructionData.Operands.FirstOrDefault(); // we only do the jump if whatever is at the top of the stack is not equal to 0. // so load a with stack value, load b with 0, then do a jumpIfNotEqual this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.LOADBIMMEDIATE.ToString()); this.Output.Add("0"); this.Output.Add(assembler.CommandType.UPDATEFLAGS.ToString()); this.Output.Add(assembler.CommandType.JUMPIFEQUAL.ToString()); this.Output.Add($"FALSE{blockID}"); this.Output.Add(assembler.CommandType.JUMP.ToString()); this.Output.Add(labelToJumpTo); this.Output.Add($"(FALSE{blockID})"); } }
protected void handleFunctionCallingCommand(InstructionData instructionData) { //function funcName 5 //number of arguments to get from stack. if (instructionData.CommandType == vmCommandType.FUNCTION) { this.Output.Add("//handle FUNCTION DEF"); var funcName = instructionData.Operands[0]; var argNum = instructionData.Operands[1]; //label Output.Add($"({funcName})"); //allocate some memory for local arguments for (var i = 0; i < int.Parse(argNum); i++) { this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("0"); this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateIncrement(stackPointer_symbol)); } } if (instructionData.CommandType == vmCommandType.CALL) { this.Output.Add($"//handle CALL {instructionData.Operands[0]}"); var funcName = instructionData.Operands[0]; var argNum = instructionData.Operands[1]; var returnaddress = "RETURN" + Guid.NewGuid().ToString("N"); //push return address Output.AddRange(generatePushToStackFromSymbolImmediate(returnaddress, instructionData)); //push LCL Output.AddRange(generatePushToStackFromSymbol(local_symbol, instructionData)); //push ARG Output.AddRange(generatePushToStackFromSymbol(arg_symbol, instructionData)); //push THIS Output.AddRange(generatePushToStackFromSymbol(this_symbol, instructionData)); //push THAT Output.AddRange(generatePushToStackFromSymbol(that_symbol, instructionData)); //set ARG to SP-n-5 //first calculate the new address and store in A. Output.AddRange(generateDecrement(stackPointer_symbol, (int.Parse(argNum) + 5).ToString(), false)); //then store it in ARG. Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(arg_symbol); //set LCL to new SP. Output.Add(assembler.CommandType.LOADA.ToString()); Output.Add(stackPointer_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(local_symbol); //execute function. Output.Add(assembler.CommandType.JUMP.ToString()); Output.Add(funcName); //return label Output.Add($"({returnaddress})"); } if (instructionData.CommandType == vmCommandType.RETURN) { Output.Add("//handle RETURN"); Output.Add(assembler.CommandType.LOADA.ToString()); Output.Add(local_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add("FRAME"); //frame currently points to somewhere on the stack - //lets get the real value at the frame pointer and save to ret. Output.AddRange(generateDecrement("FRAME", "5", false)); Output.AddRange(generateMoveAtoTemp()); Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); Output.Add(temp_symbol); //A should now contain a real return address. Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add("RET"); Output.AddRange(generatePopFromStackToSymbol(arg_symbol, instructionData)); Output.AddRange(generateIncrement(arg_symbol, "1", false)); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(stackPointer_symbol); Output.AddRange(generateDecrement("FRAME", (1).ToString(), false)); Output.AddRange(generateMoveAtoTemp()); Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); Output.Add(temp_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(that_symbol); Output.AddRange(generateDecrement("FRAME", (2).ToString(), false)); Output.AddRange(generateMoveAtoTemp()); Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); Output.Add(temp_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(this_symbol); Output.AddRange(generateDecrement("FRAME", (3).ToString(), false)); Output.AddRange(generateMoveAtoTemp()); Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); Output.Add(temp_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(arg_symbol); Output.AddRange(generateDecrement("FRAME", (4).ToString(), false)); Output.AddRange(generateMoveAtoTemp()); Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); Output.Add(temp_symbol); Output.Add(assembler.CommandType.STOREA.ToString()); Output.Add(local_symbol); Output.Add(assembler.CommandType.JUMPTOPOINTER.ToString()); Output.Add("RET"); } }
private void handlePushPop(InstructionData instructionData) { //////////////////////////////////////////////////////////////////////////////////// /////PUSH - write to stack /// /////////////////////////////////////////////////////////////////////////////// if (instructionData.CommandType == vmILParser.vmCommandType.PUSH) { this.Output.Add("//handle PUSH"); //memory instructions look like //pop segment index var segment = parseVmSegment(instructionData.Operands.FirstOrDefault()); var indexORValue = instructionData.Operands.Skip(1).FirstOrDefault(); if (segment == vmILParser.vmMemSegments.constant) { this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add(indexORValue); this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateIncrement(stackPointer_symbol)); } //segments else if (segment == vmILParser.vmMemSegments.local) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments.local, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.argument) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments.argument, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.pointer) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments.pointer, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments._this) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments._this, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.that) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments.that, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments._static) { this.Output.AddRange(generatePushToStackFromSegment(vmMemSegments._static, indexORValue, instructionData)); } //////////////////////////////////////////////////////////////////////////////////// /////POP - write to memory /// /////////////////////////////////////////////////////////////////////////////// } else if (instructionData.CommandType == vmILParser.vmCommandType.POP) { this.Output.Add("//handle POP"); var segment = parseVmSegment(instructionData.Operands.FirstOrDefault()); var indexORValue = instructionData.Operands.Skip(1).FirstOrDefault(); if (segment == vmILParser.vmMemSegments.constant) { throw new Exception("cannot write to constant segment "); } else if (segment == vmILParser.vmMemSegments.local) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments.local, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.argument) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments.argument, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.pointer) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments.pointer, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments._this) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments._this, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments.that) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments.that, indexORValue, instructionData)); } else if (segment == vmILParser.vmMemSegments._static) { this.Output.AddRange(generatePopFromStackToSegment(vmMemSegments._static, indexORValue, instructionData)); } } }
private void handleArithmeticCommand(InstructionData instructionData) { //depending on the specific ALU command we need to generate the appropriate ASM command //there will be no operands for these commands - //instead we will need to include pop twice to get the operands from the stack... //the stack lies from location 256 -2047 - we will need to reconcile this with our bootloader... // and assembler offsets.. //For now - lets just try offsetting everything by 256 - to make room for the bootloader... // so 256 - 256+15 = virtual registers // 256+16 - 256+255 = static variables // 256 + 256 - 256 + 2047 = THE STACK //we'll assume there is always a symbol called "stackpointer" which points to the last item in the stack. vmILParser.vmArithmetic_Logic_Instructions subCommand = (vmILParser.vmArithmetic_Logic_Instructions)instructionData.CommmandObject; //ADD this.Output.Add("//handleArithmeticCommand"); if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.add) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.ADD.ToString()); this.Output.Add(temp_symbol); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.sub) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.SUBTRACT.ToString()); this.Output.Add(temp_symbol); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.neg) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("-1"); this.Output.Add(assembler.CommandType.MULTIPLY.ToString()); this.Output.Add(temp_symbol); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.eq) { var blockID = Guid.NewGuid().ToString("N"); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //load temp into B somehow this.Output.Add(assembler.CommandType.LOADB.ToString()); this.Output.Add(temp_symbol); // update flags this.Output.Add(assembler.CommandType.UPDATEFLAGS.ToString()); // we need to return different results to the stack depending on // if equal is true or not - can do this using jumps. this.Output.Add(assembler.CommandType.JUMPIFEQUAL.ToString()); this.Output.Add($"EQ_TRUE_{blockID}"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("0"); this.Output.Add(assembler.CommandType.JUMP.ToString()); this.Output.Add($"EQ_STORE_STACK_{blockID}"); this.Output.Add($"(EQ_TRUE_{blockID})"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("1"); this.Output.Add($"(EQ_STORE_STACK_{blockID})"); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.gt) { var blockID = Guid.NewGuid().ToString("N"); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //load temp into B somehow this.Output.Add(assembler.CommandType.LOADB.ToString()); this.Output.Add(temp_symbol); // update flags this.Output.Add(assembler.CommandType.UPDATEFLAGS.ToString()); // we need to return different results to the stack depending on // if A>B or not - can do this using jumps. this.Output.Add(assembler.CommandType.JUMPIFGREATER.ToString()); this.Output.Add($"GT_TRUE_{blockID}"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("0"); this.Output.Add(assembler.CommandType.JUMP.ToString()); this.Output.Add($"GT_STORE_STACK_{blockID}"); this.Output.Add($"(GT_TRUE_{blockID})"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("1"); this.Output.Add($"(GT_STORE_STACK_{blockID})"); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.lt) { var blockID = Guid.NewGuid().ToString("N"); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //load temp into B somehow this.Output.Add(assembler.CommandType.LOADB.ToString()); this.Output.Add(temp_symbol); // update flags this.Output.Add(assembler.CommandType.UPDATEFLAGS.ToString()); // we need to return different results to the stack depending on // if A>B or not - can do this using jumps. this.Output.Add(assembler.CommandType.JUMPIFLESS.ToString()); this.Output.Add($"LT_TRUE_{blockID}"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("0"); this.Output.Add(assembler.CommandType.JUMP.ToString()); this.Output.Add($"LT_STORE_STACK_{blockID}"); this.Output.Add($"(LT_TRUE_{blockID})"); this.Output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); this.Output.Add("1"); this.Output.Add($"(LT_STORE_STACK_{blockID})"); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.and) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.AND.ToString()); this.Output.Add(temp_symbol); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.or) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.AddRange(generateMoveAtoTemp()); this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.OR.ToString()); this.Output.Add(temp_symbol); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } else if (subCommand == vmILParser.vmArithmetic_Logic_Instructions.not) { this.Output.AddRange(generateDecrement(stackPointer_symbol)); this.Output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); this.Output.Add(assembler.CommandType.NOT.ToString()); //now store the result in SP this.Output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); this.Output.Add(stackPointer_symbol); //and increment this.Output.AddRange(generateIncrement(stackPointer_symbol)); } }
protected string[] generatePushToStackFromSegment(vmILParser.vmMemSegments segment, string index, InstructionData currentVMinstruction) { //if we are writing from local to stack - we need to read from memory at the base address //pointed to by the local symbol which is stored at LCL //flow is LoadAFromPointer LCL - lets assume LCL holds the number 100 - which is the base address for the LCL segment //then increment A by index //store this in temp var output = new List <String>(); //add LCL and index and store in A //static is different from other segments //we need to read a value from the symbol VMFILE.VMFUNCTION.INDEX if (segment == vmMemSegments._static) { output.Add(assembler.CommandType.LOADA.ToString()); output.Add($"STATIC{currentVMinstruction.VMFilePath}.{currentVMinstruction.VMFunction}.{index}"); } //if constant, then just load the constant specified into A. else if (segment == vmMemSegments.constant) { output.Add(assembler.CommandType.LOADAIMMEDIATE.ToString()); output.Add(index); } else { output.AddRange(generateIncrement(vmSegmentToSymbolName[segment], index, updateSymbol: false)); output.Add(assembler.CommandType.STOREA.ToString()); output.Add(temp_symbol + " + 2"); output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); output.Add(temp_symbol + " + 2"); } //store it on the stack, and increment SP output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); output.Add(stackPointer_symbol); output.AddRange(generateIncrement(stackPointer_symbol)); return(output.ToArray()); }
protected string[] generatePopFromStackToSegment(vmILParser.vmMemSegments segment, string IndexOperand, InstructionData currentVMinstruction) { var output = new List <String>(); if (segment == vmMemSegments._static) { output.AddRange(generateDecrement(stackPointer_symbol)); output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); output.Add(stackPointer_symbol); output.Add(assembler.CommandType.STOREA.ToString()); output.Add($"STATIC{currentVMinstruction.VMFilePath}.{currentVMinstruction.VMFunction}.{IndexOperand}"); } else { //offset the base address of the symbol by the index output.AddRange(generateIncrement(this.vmSegmentToSymbolName[segment], IndexOperand, updateSymbol: false)); output.AddRange(generateMoveAtoTemp()); output.AddRange(generateDecrement(stackPointer_symbol)); output.Add(assembler.CommandType.LOADAATPOINTER.ToString()); output.Add(stackPointer_symbol); output.Add(assembler.CommandType.STOREAATPOINTER.ToString()); output.Add(temp_symbol); } return(output.ToArray()); }