[08] Adds support for label and if-goto commands
This commit is contained in:
parent
91dd0bb102
commit
b61d4c4339
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
||||||
|
| RAM[0] |RAM[256]|
|
||||||
|
| 257 | 6 |
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue