[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
|
||||
|
||||
- [ ] `BasicLoop.vm`
|
||||
- [x] `BasicLoop.vm` (93)
|
||||
- [ ] `Fibonacci.vm`
|
||||
|
||||
## 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->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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue