From b61d4c4339ee6bbb3f799b5989adb27886fc3c7a Mon Sep 17 00:00:00 2001 From: Nemo Date: Wed, 3 Jun 2020 18:29:50 +0530 Subject: [PATCH] [08] Adds support for label and if-goto commands --- README.md | 2 +- .../08/ProgramFlow/BasicLoop/BasicLoop.asm | 94 +++++++++++++++++++ .../08/ProgramFlow/BasicLoop/BasicLoop.out | 2 + vm/CodeWriter.php | 65 ++++++++++++- vm/VMTranslator.php | 10 ++ 5 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 projects/08/ProgramFlow/BasicLoop/BasicLoop.asm create mode 100644 projects/08/ProgramFlow/BasicLoop/BasicLoop.out diff --git a/README.md b/README.md index 1fd2745..6a715d5 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ wc -l file.hack ### Program Flow Commands -- [ ] `BasicLoop.vm` +- [x] `BasicLoop.vm` (93) - [ ] `Fibonacci.vm` ## Function Calling Commands diff --git a/projects/08/ProgramFlow/BasicLoop/BasicLoop.asm b/projects/08/ProgramFlow/BasicLoop/BasicLoop.asm new file mode 100644 index 0000000..d6fc82f --- /dev/null +++ b/projects/08/ProgramFlow/BasicLoop/BasicLoop.asm @@ -0,0 +1,94 @@ +@0 // push constant 0 +D=A +@SP +A=M +M=D +@SP +M=M+1 // end push constant 0 (L0) +@SP // pop +AM=M-1 +D=M +@LCL +A=M // Read @LCL to A (for local 0) +M=D // end pop local 0 (L1) +(__GLOBAL__.LOOP_START) // end label LOOP_START (L2) +@ARG +A=M +D=M +@SP +A=M +M=D +@SP +M=M+1 // end push argument 0 (L3) +@LCL +A=M +D=M +@SP +A=M +M=D +@SP +M=M+1 // end push local 0 (L4) +@SP // ==== add ==== +A=M-1 +D=M +A=A-1 +M=D+M +@SP +M=M-1 // end add (L5) +@SP // pop +AM=M-1 +D=M +@LCL +A=M // Read @LCL to A (for local 0) +M=D // end pop local 0 (L6) +@ARG +A=M +D=M +@SP +A=M +M=D +@SP +M=M+1 // end push argument 0 (L7) +@1 // push constant 1 +D=A +@SP +A=M +M=D +@SP +M=M+1 // end push constant 1 (L8) +@SP // ==== sub ==== +A=M-1 +D=M +A=A-1 +M=M-D +@SP +M=M-1 // end sub (L9) +@SP // pop +AM=M-1 +D=M +@ARG +A=M // Read @ARG to A (for argument 0) +M=D // end pop argument 0 (L10) +@ARG +A=M +D=M +@SP +A=M +M=D +@SP +M=M+1 // end push argument 0 (L11) +@SP +AM=M-1 +D=M +@__GLOBAL__.LOOP_START +D;JNE // end if-goto LOOP_START (L12) +@LCL +A=M +D=M +@SP +A=M +M=D +@SP +M=M+1 // end push local 0 (L13) +@93 +0;JMP diff --git a/projects/08/ProgramFlow/BasicLoop/BasicLoop.out b/projects/08/ProgramFlow/BasicLoop/BasicLoop.out new file mode 100644 index 0000000..1786c7c --- /dev/null +++ b/projects/08/ProgramFlow/BasicLoop/BasicLoop.out @@ -0,0 +1,2 @@ +| RAM[0] |RAM[256]| +| 257 | 6 | diff --git a/vm/CodeWriter.php b/vm/CodeWriter.php index bf5418d..5d3511a 100644 --- a/vm/CodeWriter.php +++ b/vm/CodeWriter.php @@ -7,6 +7,9 @@ class CodeWriter { $this->ic = 0; $this->sourceLine = 0; $this->file = fopen($outputFile, "w"); + + // We aren't inside a function by default + $this->fn = null; } function setInputFileName($inputFileName) { @@ -21,6 +24,10 @@ class CodeWriter { fclose($this->file); } + /** + * Puts the closing non-terminating + * loop + */ function close() { $endJump = $this->ic+1; $this->write([ @@ -29,6 +36,43 @@ class CodeWriter { ]); } + /** + * Writes label X as (fnlabel) + */ + function writeLabel(String $label) { + $globalLabel = $this->resolveLabel($label); + $this->write([ + "($globalLabel) // end label $label (L{$this->sourceLine})", + ]); + } + + /** + * Generates a unique global label + * by using the current function name + */ + private function resolveLabel(String $label) { + if($this->fn === null) + return "__GLOBAL__.$label"; + return $this->fn . $label; + } + + /** + * Writes corresponding code for if-goto + * if value == true, goto X + * else keep executing + */ + function writeIf(String $label) { + $globalLabel = $this->resolveLabel($label); + $this->write([ + // Read top of the stack to D + '@SP', + 'AM=M-1', + 'D=M', + "@$globalLabel", + "D;JNE // end if-goto $label (L{$this->sourceLine})", + ]); + } + function writeArithmetic(String $command) { $stackDecrease=true; // Read top of stack to D @@ -177,7 +221,7 @@ class CodeWriter { case 'local': case 'this': case 'that': - $register = $this->segmentToRegister($segment); + $register = $this->resolveSegmentToRegister($segment); if ($index !== 0) { $this->resolveSegmentToR13($segment, $index); $register = "@R13"; @@ -230,7 +274,10 @@ class CodeWriter { ]); } - private function segmentToRegister(String $segment) { + /** + * Resolves a given segment to a register + */ + private function resolveSegmentToRegister(String $segment) { return [ 'local' => '@LCL', 'argument' => '@ARG', @@ -239,8 +286,13 @@ class CodeWriter { ][$segment]; } + /** + * For cases where we need calculations on both LHS and RHS, we temporarily + * store the resolved address of the memory segment to R13. This is the code + * that does that + */ private function resolveSegmentToR13(string $segment, Int $index) { - $register = $this->segmentToRegister($segment); + $register = $this->resolveSegmentToRegister($segment); $this->write([ "$register // $segment $index" , "D=M", @@ -251,6 +303,10 @@ class CodeWriter { ]); } + /** + * Static variables are just the same labels repeated again + * They are unique across a file + */ private function resolveStatic(Int $index) { return "@{$this->vm}.$index"; } @@ -266,7 +322,7 @@ class CodeWriter { $this->resolveSegmentToR13($segment, $index); $lookupRegister = '@R13'; } else{ - $lookupRegister = $this->segmentToRegister($segment); + $lookupRegister = $this->resolveSegmentToRegister($segment); } $this->write([ @@ -312,6 +368,7 @@ class CodeWriter { "M=D // end pop temp $index (L{$this->sourceLine})" ]); break; + default: throw new \Exception("Not implemented pop $segment"); break; diff --git a/vm/VMTranslator.php b/vm/VMTranslator.php index 6b4dc61..06d0c6d 100644 --- a/vm/VMTranslator.php +++ b/vm/VMTranslator.php @@ -40,6 +40,16 @@ class VMTranslator { $this->writer->writePushPop($commandType, $segment, $index); break; + case CommandType::LABEL: + $label = $parser->arg1(); + $this->writer->writeLabel($label); + break; + + case CommandType::IF: + $label = $parser->arg1(); + $this->writer->writeIf($label); + break; + default: throw new \Exception("Not Implemented $command", 1); break;