[08] Adds support for label and if-goto commands

This commit is contained in:
Nemo 2020-06-03 18:29:50 +05:30
parent 91dd0bb102
commit b61d4c4339
5 changed files with 168 additions and 5 deletions

View File

@ -115,7 +115,7 @@ wc -l file.hack
### Program Flow Commands ### Program Flow Commands
- [ ] `BasicLoop.vm` - [x] `BasicLoop.vm` (93)
- [ ] `Fibonacci.vm` - [ ] `Fibonacci.vm`
## Function Calling Commands ## Function Calling Commands

View File

@ -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

View File

@ -0,0 +1,2 @@
| RAM[0] |RAM[256]|
| 257 | 6 |

View File

@ -7,6 +7,9 @@ class CodeWriter {
$this->ic = 0; $this->ic = 0;
$this->sourceLine = 0; $this->sourceLine = 0;
$this->file = fopen($outputFile, "w"); $this->file = fopen($outputFile, "w");
// We aren't inside a function by default
$this->fn = null;
} }
function setInputFileName($inputFileName) { function setInputFileName($inputFileName) {
@ -21,6 +24,10 @@ class CodeWriter {
fclose($this->file); fclose($this->file);
} }
/**
* Puts the closing non-terminating
* loop
*/
function close() { function close() {
$endJump = $this->ic+1; $endJump = $this->ic+1;
$this->write([ $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) { function writeArithmetic(String $command) {
$stackDecrease=true; $stackDecrease=true;
// Read top of stack to D // Read top of stack to D
@ -177,7 +221,7 @@ class CodeWriter {
case 'local': case 'local':
case 'this': case 'this':
case 'that': case 'that':
$register = $this->segmentToRegister($segment); $register = $this->resolveSegmentToRegister($segment);
if ($index !== 0) { if ($index !== 0) {
$this->resolveSegmentToR13($segment, $index); $this->resolveSegmentToR13($segment, $index);
$register = "@R13"; $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 [ return [
'local' => '@LCL', 'local' => '@LCL',
'argument' => '@ARG', 'argument' => '@ARG',
@ -239,8 +286,13 @@ class CodeWriter {
][$segment]; ][$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) { private function resolveSegmentToR13(string $segment, Int $index) {
$register = $this->segmentToRegister($segment); $register = $this->resolveSegmentToRegister($segment);
$this->write([ $this->write([
"$register // $segment $index" , "$register // $segment $index" ,
"D=M", "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) { private function resolveStatic(Int $index) {
return "@{$this->vm}.$index"; return "@{$this->vm}.$index";
} }
@ -266,7 +322,7 @@ class CodeWriter {
$this->resolveSegmentToR13($segment, $index); $this->resolveSegmentToR13($segment, $index);
$lookupRegister = '@R13'; $lookupRegister = '@R13';
} else{ } else{
$lookupRegister = $this->segmentToRegister($segment); $lookupRegister = $this->resolveSegmentToRegister($segment);
} }
$this->write([ $this->write([
@ -312,6 +368,7 @@ class CodeWriter {
"M=D // end pop temp $index (L{$this->sourceLine})" "M=D // end pop temp $index (L{$this->sourceLine})"
]); ]);
break; break;
default: default:
throw new \Exception("Not implemented pop $segment"); throw new \Exception("Not implemented pop $segment");
break; break;

View File

@ -40,6 +40,16 @@ class VMTranslator {
$this->writer->writePushPop($commandType, $segment, $index); $this->writer->writePushPop($commandType, $segment, $index);
break; break;
case CommandType::LABEL:
$label = $parser->arg1();
$this->writer->writeLabel($label);
break;
case CommandType::IF:
$label = $parser->arg1();
$this->writer->writeIf($label);
break;
default: default:
throw new \Exception("Not Implemented $command", 1); throw new \Exception("Not Implemented $command", 1);
break; break;