Break into multiple files for easier navigation
This commit is contained in:
parent
9782ba7e4d
commit
551546c953
|
@ -4,27 +4,27 @@ D=A
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 10
|
M=M+1 // end push constant 10 (L0)
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@LCL
|
@LCL
|
||||||
A=M // Read @LCL to A
|
A=M // Read @LCL to A (for local 0)
|
||||||
M=D // Write D to *A
|
M=D // end pop local 0 (L1)
|
||||||
@21 // push constant 21
|
@21 // push constant 21
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 21
|
M=M+1 // end push constant 21 (L2)
|
||||||
@22 // push constant 22
|
@22 // push constant 22
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 22
|
M=M+1 // end push constant 22 (L3)
|
||||||
@ARG // argument 2
|
@ARG // argument 2
|
||||||
D=M
|
D=M
|
||||||
@2 // write 2 to A
|
@2 // write 2 to A
|
||||||
|
@ -32,11 +32,11 @@ D=D+A // D = segment+index
|
||||||
@R13 // save it to R13
|
@R13 // save it to R13
|
||||||
M=D // write @ARG+2 to R13
|
M=D // write @ARG+2 to R13
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@R13
|
@R13
|
||||||
A=M // Read @R13 to A
|
A=M // Read @R13 to A (for argument 2)
|
||||||
M=D // Write D to *A
|
M=D // end pop argument 2 (L4)
|
||||||
@ARG // argument 1
|
@ARG // argument 1
|
||||||
D=M
|
D=M
|
||||||
@1 // write 1 to A
|
@1 // write 1 to A
|
||||||
|
@ -44,18 +44,18 @@ D=D+A // D = segment+index
|
||||||
@R13 // save it to R13
|
@R13 // save it to R13
|
||||||
M=D // write @ARG+1 to R13
|
M=D // write @ARG+1 to R13
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@R13
|
@R13
|
||||||
A=M // Read @R13 to A
|
A=M // Read @R13 to A (for argument 1)
|
||||||
M=D // Write D to *A
|
M=D // end pop argument 1 (L5)
|
||||||
@36 // push constant 36
|
@36 // push constant 36
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 36
|
M=M+1 // end push constant 36 (L6)
|
||||||
@THIS // this 6
|
@THIS // this 6
|
||||||
D=M
|
D=M
|
||||||
@6 // write 6 to A
|
@6 // write 6 to A
|
||||||
|
@ -63,25 +63,25 @@ D=D+A // D = segment+index
|
||||||
@R13 // save it to R13
|
@R13 // save it to R13
|
||||||
M=D // write @THIS+6 to R13
|
M=D // write @THIS+6 to R13
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@R13
|
@R13
|
||||||
A=M // Read @R13 to A
|
A=M // Read @R13 to A (for this 6)
|
||||||
M=D // Write D to *A
|
M=D // end pop this 6 (L7)
|
||||||
@42 // push constant 42
|
@42 // push constant 42
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 42
|
M=M+1 // end push constant 42 (L8)
|
||||||
@45 // push constant 45
|
@45 // push constant 45
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 45
|
M=M+1 // end push constant 45 (L9)
|
||||||
@THAT // that 5
|
@THAT // that 5
|
||||||
D=M
|
D=M
|
||||||
@5 // write 5 to A
|
@5 // write 5 to A
|
||||||
|
@ -89,11 +89,11 @@ D=D+A // D = segment+index
|
||||||
@R13 // save it to R13
|
@R13 // save it to R13
|
||||||
M=D // write @THAT+5 to R13
|
M=D // write @THAT+5 to R13
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@R13
|
@R13
|
||||||
A=M // Read @R13 to A
|
A=M // Read @R13 to A (for that 5)
|
||||||
M=D // Write D to *A
|
M=D // end pop that 5 (L10)
|
||||||
@THAT // that 2
|
@THAT // that 2
|
||||||
D=M
|
D=M
|
||||||
@2 // write 2 to A
|
@2 // write 2 to A
|
||||||
|
@ -101,31 +101,30 @@ D=D+A // D = segment+index
|
||||||
@R13 // save it to R13
|
@R13 // save it to R13
|
||||||
M=D // write @THAT+2 to R13
|
M=D // write @THAT+2 to R13
|
||||||
@SP // pop
|
@SP // pop
|
||||||
A=M-1
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@R13
|
@R13
|
||||||
A=M // Read @R13 to A
|
A=M // Read @R13 to A (for that 2)
|
||||||
M=D // Write D to *A
|
M=D // end pop that 2 (L11)
|
||||||
@510 // push constant 510
|
@510 // push constant 510
|
||||||
D=A
|
D=A
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push constant 510
|
M=M+1 // end push constant 510 (L12)
|
||||||
@LCL // local 0
|
@SP
|
||||||
|
AM=M-1
|
||||||
D=M
|
D=M
|
||||||
@0 // write 0 to A
|
@R11
|
||||||
D=D+A // D = segment+index
|
M=D // end pop temp 6 (L13)
|
||||||
@R13 // save it to R13
|
@LCL
|
||||||
M=D // write @LCL+0 to R13
|
|
||||||
@R13
|
|
||||||
D=M
|
D=M
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push local 0
|
M=M+1 // end push local 0 (L14)
|
||||||
@THAT // that 5
|
@THAT // that 5
|
||||||
D=M
|
D=M
|
||||||
@5 // write 5 to A
|
@5 // write 5 to A
|
||||||
|
@ -138,14 +137,14 @@ D=M
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push that 5
|
M=M+1 // end push that 5 (L15)
|
||||||
@SP // ==== add ====
|
@SP // ==== add ====
|
||||||
A=M-1
|
A=M-1
|
||||||
D=M
|
D=M
|
||||||
A=A-1
|
A=A-1
|
||||||
M=D+M
|
M=D+M
|
||||||
@SP
|
@SP
|
||||||
M=M-1
|
M=M-1 // end add (L16)
|
||||||
@ARG // argument 1
|
@ARG // argument 1
|
||||||
D=M
|
D=M
|
||||||
@1 // write 1 to A
|
@1 // write 1 to A
|
||||||
|
@ -158,14 +157,14 @@ D=M
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push argument 1
|
M=M+1 // end push argument 1 (L17)
|
||||||
@SP // ==== sub ====
|
@SP // ==== sub ====
|
||||||
A=M-1
|
A=M-1
|
||||||
D=M
|
D=M
|
||||||
A=A-1
|
A=A-1
|
||||||
M=M-D
|
M=M-D
|
||||||
@SP
|
@SP
|
||||||
M=M-1
|
M=M-1 // end sub (L18)
|
||||||
@THIS // this 6
|
@THIS // this 6
|
||||||
D=M
|
D=M
|
||||||
@6 // write 6 to A
|
@6 // write 6 to A
|
||||||
|
@ -178,7 +177,7 @@ D=M
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push this 6
|
M=M+1 // end push this 6 (L19)
|
||||||
@THIS // this 6
|
@THIS // this 6
|
||||||
D=M
|
D=M
|
||||||
@6 // write 6 to A
|
@6 // write 6 to A
|
||||||
|
@ -191,34 +190,34 @@ D=M
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push this 6
|
M=M+1 // end push this 6 (L20)
|
||||||
@SP // ==== add ====
|
@SP // ==== add ====
|
||||||
A=M-1
|
A=M-1
|
||||||
D=M
|
D=M
|
||||||
A=A-1
|
A=A-1
|
||||||
M=D+M
|
M=D+M
|
||||||
@SP
|
@SP
|
||||||
M=M-1
|
M=M-1 // end add (L21)
|
||||||
@SP // ==== sub ====
|
@SP // ==== sub ====
|
||||||
A=M-1
|
A=M-1
|
||||||
D=M
|
D=M
|
||||||
A=A-1
|
A=A-1
|
||||||
M=M-D
|
M=M-D
|
||||||
@SP
|
@SP
|
||||||
M=M-1
|
M=M-1 // end sub (L22)
|
||||||
@R11 // temp 6
|
@R11 // temp 6
|
||||||
D=M
|
D=M
|
||||||
@SP
|
@SP
|
||||||
A=M
|
A=M
|
||||||
M=D
|
M=D
|
||||||
@SP
|
@SP
|
||||||
M=M+1 // end push temp 6
|
M=M+1 // end push temp 6 (L23)
|
||||||
@SP // ==== add ====
|
@SP // ==== add ====
|
||||||
A=M-1
|
A=M-1
|
||||||
D=M
|
D=M
|
||||||
A=A-1
|
A=A-1
|
||||||
M=D+M
|
M=D+M
|
||||||
@SP
|
@SP
|
||||||
M=M-1
|
M=M-1 // end add (L24)
|
||||||
@223
|
@222
|
||||||
0;JMP
|
0;JMP
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
|RAM[256]|RAM[300]|RAM[401]|RAM[402]|RAM[3006|RAM[3012|RAM[3015|RAM[11] |
|
|RAM[256]|RAM[300]|RAM[401]|RAM[402]|RAM[3006|RAM[3012|RAM[3015|RAM[11] |
|
||||||
| 10 | 10 | 22 | 22 | 36 | 45 | 45 | 0 |
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
| RAM[0] | RAM[256] | RAM[257] | RAM[258] | RAM[259] | RAM[260] |
|
|
||||||
| 266 | -1 | 0 | 0 | 0 | -1 |
|
|
||||||
| RAM[261] | RAM[262] | RAM[263] | RAM[264] | RAM[265] |
|
|
||||||
| 0 | -1 | 0 | 0 | -91 |
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace captn3m0\NandToTetris;
|
||||||
|
|
||||||
|
class CodeWriter {
|
||||||
|
function __construct($outputFile) {
|
||||||
|
$this->ic = 0;
|
||||||
|
$this->sourceLine = 0;
|
||||||
|
$this->file = fopen($outputFile, "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextSourceLine() {
|
||||||
|
$this->sourceLine += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct() {
|
||||||
|
fclose($this->file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
$endJump = $this->ic+1;
|
||||||
|
$this->write([
|
||||||
|
"@$endJump",
|
||||||
|
"0;JMP"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeArithmetic(String $command ) {
|
||||||
|
$stackDecrease=true;
|
||||||
|
// Read top of stack to D
|
||||||
|
$this->write([
|
||||||
|
"@SP // ==== $command ====",
|
||||||
|
"A=M-1",
|
||||||
|
"D=M"
|
||||||
|
]);
|
||||||
|
|
||||||
|
switch ($command) {
|
||||||
|
// TODO: Combine all the binary math commands into one
|
||||||
|
// And the unary math commands into one
|
||||||
|
case 'sub':
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
"M=M-D",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'add':
|
||||||
|
// But add it to previous D this time
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'M=D+M'
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'neg':
|
||||||
|
$this->write([
|
||||||
|
"M=-M // end $command (L{$this->sourceLine})",
|
||||||
|
]);
|
||||||
|
$stackDecrease = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case 'not':
|
||||||
|
$this->write([
|
||||||
|
"M=!M // end $command (L{$this->sourceLine})",
|
||||||
|
]);
|
||||||
|
$stackDecrease = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'and':
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'M=D&M',
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'or':
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'M=D|M',
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: Combine all the boolean commands
|
||||||
|
case 'lt':
|
||||||
|
$jumpPointer = $this->ic+10;
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'D=M-D',
|
||||||
|
'M=0',
|
||||||
|
'M=M-1',
|
||||||
|
"@$jumpPointer",
|
||||||
|
"D;JLT",
|
||||||
|
"@SP",
|
||||||
|
"A=M-1",
|
||||||
|
"A=A-1",
|
||||||
|
"M=0",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gt':
|
||||||
|
$jumpPointer = $this->ic+10;
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'D=M-D',
|
||||||
|
'M=0',
|
||||||
|
'M=M-1',
|
||||||
|
"@$jumpPointer",
|
||||||
|
"D;JGT",
|
||||||
|
"@SP",
|
||||||
|
"A=M-1",
|
||||||
|
"A=A-1",
|
||||||
|
"M=0",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'eq':
|
||||||
|
$jumpPointer = $this->ic+10;
|
||||||
|
$this->write([
|
||||||
|
'A=A-1',
|
||||||
|
'D=M-D',
|
||||||
|
'M=0',
|
||||||
|
'M=M-1',
|
||||||
|
"@{$jumpPointer}",
|
||||||
|
'D;JEQ',
|
||||||
|
"@SP",
|
||||||
|
"A=M-1",
|
||||||
|
"A=A-1",
|
||||||
|
"M=0",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception("$command not Implemented", 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($stackDecrease) {
|
||||||
|
$this->write([
|
||||||
|
'@SP',
|
||||||
|
"M=M-1 // end $command (L{$this->sourceLine})"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function write(Array $lines) {
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (substr($line, 0, 2) !== "//") {
|
||||||
|
$this->ic += 1;
|
||||||
|
}
|
||||||
|
fwrite($this->file, "$line\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveTemp(Int $index) {
|
||||||
|
// Temp section starts from R5;
|
||||||
|
$tempBase = 5;
|
||||||
|
$ramAddress = $tempBase + $index;
|
||||||
|
return "@R$ramAddress";
|
||||||
|
}
|
||||||
|
|
||||||
|
function writePush(String $segment, Int $index) {
|
||||||
|
switch ($segment) {
|
||||||
|
case 'constant':
|
||||||
|
$this->write([
|
||||||
|
// Take the constant
|
||||||
|
"@$index // push $segment $index",
|
||||||
|
// Write it to D
|
||||||
|
"D=A",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'argument':
|
||||||
|
case 'local':
|
||||||
|
case 'this':
|
||||||
|
case 'that':
|
||||||
|
$register = $this->segmentToRegister($segment);
|
||||||
|
if ($index !== 0) {
|
||||||
|
$this->resolveSegmentToR13($segment, $index);
|
||||||
|
$register = "@R13";
|
||||||
|
}
|
||||||
|
$this->write([
|
||||||
|
$register,
|
||||||
|
"D=M",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'temp':
|
||||||
|
$register = $this->resolveTemp($index);
|
||||||
|
// TODO FIX
|
||||||
|
$this->write([
|
||||||
|
"$register // temp $index",
|
||||||
|
"D=M"
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception("Not Implemented $segment", 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->write([
|
||||||
|
// A=SP
|
||||||
|
"@SP",
|
||||||
|
"A=M",
|
||||||
|
// Write D to SP
|
||||||
|
"M=D",
|
||||||
|
// Bump Stack Pointer
|
||||||
|
"@SP",
|
||||||
|
"M=M+1 // end push $segment $index (L{$this->sourceLine})",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function segmentToRegister(String $segment) {
|
||||||
|
return [
|
||||||
|
'local' => '@LCL',
|
||||||
|
'argument' => '@ARG',
|
||||||
|
'this' => '@THIS',
|
||||||
|
'that' => '@THAT',
|
||||||
|
][$segment];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveSegmentToR13(string $segment, Int $index) {
|
||||||
|
$register = $this->segmentToRegister($segment);
|
||||||
|
$this->write([
|
||||||
|
"$register // $segment $index" ,
|
||||||
|
"D=M",
|
||||||
|
"@$index // write $index to A",
|
||||||
|
"D=D+A // D = segment+index",
|
||||||
|
"@R13 // save it to R13",
|
||||||
|
"M=D // write $register+$index to R13",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function writePop(String $segment, Int $index) {
|
||||||
|
switch ($segment) {
|
||||||
|
// The address is given by LCL+INDEX
|
||||||
|
case 'local':
|
||||||
|
case 'argument':
|
||||||
|
case 'this':
|
||||||
|
case 'that':
|
||||||
|
if($index !== 0) {
|
||||||
|
$this->resolveSegmentToR13($segment, $index);
|
||||||
|
$lookupRegister = '@R13';
|
||||||
|
} else{
|
||||||
|
$lookupRegister = $this->segmentToRegister($segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->write([
|
||||||
|
"@SP // pop",
|
||||||
|
"AM=M-1",
|
||||||
|
"D=M",
|
||||||
|
$lookupRegister,
|
||||||
|
"A=M // Read $lookupRegister to A (for $segment $index)",
|
||||||
|
"M=D // end pop $segment $index (L{$this->sourceLine})",
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'temp':
|
||||||
|
$tempRegister = $this->resolveTemp($index);
|
||||||
|
$this->write([
|
||||||
|
"@SP",
|
||||||
|
"AM=M-1",
|
||||||
|
"D=M",
|
||||||
|
"$tempRegister",
|
||||||
|
"M=D // end pop temp $index (L{$this->sourceLine})"
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new \Exception("Not implemented pop $segment");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeping this because book asked me to
|
||||||
|
function writePushPop(Int $command, String $segment , Int $index) {
|
||||||
|
switch ($command) {
|
||||||
|
case CommandType::PUSH:
|
||||||
|
$this->writePush($segment, $index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CommandType::POP:
|
||||||
|
$this->writePop($segment, $index);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid Command Type", 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace captn3m0\NandToTetris;
|
||||||
|
|
||||||
|
class CommandType {
|
||||||
|
const CALL = 0;
|
||||||
|
const PUSH = 1;
|
||||||
|
const POP = 2;
|
||||||
|
const LABEL= 3;
|
||||||
|
const GOTO= 4;
|
||||||
|
const IF= 5;
|
||||||
|
const FUNC= 6;
|
||||||
|
const RETURN= 7;
|
||||||
|
const ARITHMETIC= 8;
|
||||||
|
|
||||||
|
const ARITHMETIC_COMMANDS = ['add', 'sub', 'neg', 'eq', 'gt', 'lt', 'and', 'or', 'not'];
|
||||||
|
|
||||||
|
public static function fromName(String $name) {
|
||||||
|
if(in_array($name, self::ARITHMETIC_COMMANDS)) {
|
||||||
|
return self::ARITHMETIC;
|
||||||
|
} else {
|
||||||
|
$map = [
|
||||||
|
"call",
|
||||||
|
"push",
|
||||||
|
"pop",
|
||||||
|
"label",
|
||||||
|
"goto",
|
||||||
|
"if",
|
||||||
|
"func",
|
||||||
|
"return"
|
||||||
|
];
|
||||||
|
return array_search($name, $map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace captn3m0\NandToTetris;
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
function __construct(String $inputFile ) {
|
||||||
|
$this->file = fopen($inputFile, "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
function arg1() {
|
||||||
|
assert($this->command !== CommandType::RETURN);
|
||||||
|
|
||||||
|
return $this->pieces[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function arg2() {
|
||||||
|
assert(in_array($this->command, [CommandType::PUSH, CommandType::POP, CommandType::FUNC, CommandType::CALL]));
|
||||||
|
return $this->pieces[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function stripComment(String $line) {
|
||||||
|
$index = strpos($line, '//');
|
||||||
|
if ($index === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if ($index !== false) {
|
||||||
|
return substr($line, 0, $index-1);
|
||||||
|
} else {
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function commands() {
|
||||||
|
while(!feof($this->file)) {
|
||||||
|
$line = fgets($this->file);
|
||||||
|
$line = $this->stripComment(trim($line));
|
||||||
|
if (empty($line))continue;
|
||||||
|
$this->pieces = explode(" ", $line);
|
||||||
|
$this->command = $this->pieces[0];
|
||||||
|
yield $this->command;
|
||||||
|
}
|
||||||
|
fclose($this->file);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,329 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace captn3m0\NandToTetris;
|
namespace captn3m0\NandToTetris;
|
||||||
|
|
||||||
class CommandType {
|
require_once("CommandType.php");
|
||||||
const CALL = 0;
|
require_once("CodeWriter.php");
|
||||||
const PUSH = 1;
|
require_once("Parser.php");
|
||||||
const POP = 2;
|
|
||||||
const LABEL= 3;
|
|
||||||
const GOTO= 4;
|
|
||||||
const IF= 5;
|
|
||||||
const FUNC= 6;
|
|
||||||
const RETURN= 7;
|
|
||||||
const ARITHMETIC= 8;
|
|
||||||
|
|
||||||
const ARITHMETIC_COMMANDS = ['add', 'sub', 'neg', 'eq', 'gt', 'lt', 'and', 'or', 'not'];
|
|
||||||
|
|
||||||
public static function fromName(String $name) {
|
|
||||||
if(in_array($name, self::ARITHMETIC_COMMANDS)) {
|
|
||||||
return self::ARITHMETIC;
|
|
||||||
} else {
|
|
||||||
$map = ["call","push","pop","label","goto","if","func","return"];
|
|
||||||
return array_search($name, $map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parser {
|
|
||||||
function __construct(String $inputFile ) {
|
|
||||||
$this->file = fopen($inputFile, "r");
|
|
||||||
}
|
|
||||||
|
|
||||||
function arg1() {
|
|
||||||
assert($this->command !== CommandType::RETURN);
|
|
||||||
|
|
||||||
return $this->pieces[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function arg2() {
|
|
||||||
assert(in_array($this->command, [CommandType::PUSH, CommandType::POP, CommandType::FUNC, CommandType::CALL]));
|
|
||||||
return $this->pieces[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function stripComment(String $line) {
|
|
||||||
$index = strpos($line, '//');
|
|
||||||
if ($index === 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if ($index !== false) {
|
|
||||||
return substr($line, 0, $index-1);
|
|
||||||
} else {
|
|
||||||
return $line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function commands() {
|
|
||||||
while(!feof($this->file)) {
|
|
||||||
$line = fgets($this->file);
|
|
||||||
$line = $this->stripComment(trim($line));
|
|
||||||
if (empty($line))continue;
|
|
||||||
$this->pieces = explode(" ", $line);
|
|
||||||
$this->command = $this->pieces[0];
|
|
||||||
yield $this->command;
|
|
||||||
}
|
|
||||||
fclose($this->file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodeWriter {
|
|
||||||
function __construct($outputFile) {
|
|
||||||
$this->ic = 0;
|
|
||||||
$this->file = fopen($outputFile, "w");
|
|
||||||
}
|
|
||||||
|
|
||||||
function __destruct() {
|
|
||||||
fclose($this->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
$endJump = $this->ic+1;
|
|
||||||
$this->write([
|
|
||||||
"@$endJump",
|
|
||||||
"0;JMP"
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeArithmetic(String $command ) {
|
|
||||||
$stackDecrease=true;
|
|
||||||
// Read top of stack to D
|
|
||||||
$this->write([
|
|
||||||
"@SP // ==== $command ====",
|
|
||||||
"A=M-1",
|
|
||||||
"D=M"
|
|
||||||
]);
|
|
||||||
|
|
||||||
switch ($command) {
|
|
||||||
case 'sub':
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
"M=M-D",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'add':
|
|
||||||
// But add it to previous D this time
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'M=D+M'
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'neg':
|
|
||||||
$this->write([
|
|
||||||
'M=-M',
|
|
||||||
]);
|
|
||||||
$stackDecrease = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case 'not':
|
|
||||||
$this->write([
|
|
||||||
'M=!M',
|
|
||||||
]);
|
|
||||||
$stackDecrease = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'and':
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'M=D&M',
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'or':
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'M=D|M',
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'lt':
|
|
||||||
$jumpPointer = $this->ic+10;
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'D=M-D',
|
|
||||||
'M=0',
|
|
||||||
'M=M-1',
|
|
||||||
"@$jumpPointer",
|
|
||||||
"D;JLT",
|
|
||||||
"@SP",
|
|
||||||
"A=M-1",
|
|
||||||
"A=A-1",
|
|
||||||
"M=0",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'gt':
|
|
||||||
$jumpPointer = $this->ic+10;
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'D=M-D',
|
|
||||||
'M=0',
|
|
||||||
'M=M-1',
|
|
||||||
"@$jumpPointer",
|
|
||||||
"D;JGT",
|
|
||||||
"@SP",
|
|
||||||
"A=M-1",
|
|
||||||
"A=A-1",
|
|
||||||
"M=0",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'eq':
|
|
||||||
$jumpPointer = $this->ic+10;
|
|
||||||
$this->write([
|
|
||||||
'A=A-1',
|
|
||||||
'D=M-D',
|
|
||||||
'M=0',
|
|
||||||
'M=M-1',
|
|
||||||
"@{$jumpPointer}",
|
|
||||||
'D;JEQ',
|
|
||||||
"@SP",
|
|
||||||
"A=M-1",
|
|
||||||
"A=A-1",
|
|
||||||
"M=0",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception("$command not Implemented", 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($stackDecrease) {
|
|
||||||
$this->write([
|
|
||||||
'@SP',
|
|
||||||
'M=M-1'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function write(Array $lines) {
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
if (substr($line, 0, 2) !== "//") {
|
|
||||||
$this->ic += 1;
|
|
||||||
}
|
|
||||||
fwrite($this->file, "$line\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function writePush(String $segment, Int $index) {
|
|
||||||
switch ($segment) {
|
|
||||||
case 'constant':
|
|
||||||
$this->write([
|
|
||||||
// Take the constant
|
|
||||||
"@$index // push $segment $index",
|
|
||||||
// Write it to D
|
|
||||||
"D=A",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
case 'argument':
|
|
||||||
case 'local':
|
|
||||||
case 'this':
|
|
||||||
case 'that':
|
|
||||||
$this->resolveSegmentToR13($segment, $index);
|
|
||||||
$this->write([
|
|
||||||
"@R13",
|
|
||||||
"D=M",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'temp':
|
|
||||||
// Temp section starts from R5;
|
|
||||||
$tempBase = 5;
|
|
||||||
$ramAddress = $tempBase + $index;
|
|
||||||
$register = "@R$ramAddress";
|
|
||||||
$this->write([
|
|
||||||
"$register // temp $index",
|
|
||||||
"D=M"
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception("Not Implemented $segment", 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->write([
|
|
||||||
// A=SP
|
|
||||||
"@SP",
|
|
||||||
"A=M",
|
|
||||||
// Write D to SP
|
|
||||||
"M=D",
|
|
||||||
// Bump Stack Pointer
|
|
||||||
"@SP",
|
|
||||||
"M=M+1 // end push $segment $index",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function segmentToRegister(String $segment) {
|
|
||||||
return [
|
|
||||||
'local' => '@LCL',
|
|
||||||
'argument' => '@ARG',
|
|
||||||
'this' => '@THIS',
|
|
||||||
'that' => '@THAT',
|
|
||||||
][$segment];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function resolveSegmentToR13(string $segment, Int $index) {
|
|
||||||
$register = $this->segmentToRegister($segment);
|
|
||||||
$this->write([
|
|
||||||
"$register // $segment $index" ,
|
|
||||||
"D=M",
|
|
||||||
"@$index // write $index to A",
|
|
||||||
"D=D+A // D = segment+index",
|
|
||||||
"@R13 // save it to R13",
|
|
||||||
"M=D // write $register+$index to R13",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function writePop(String $segment, Int $index) {
|
|
||||||
switch ($segment) {
|
|
||||||
// The address is given by LCL+INDEX
|
|
||||||
case 'local':
|
|
||||||
case 'argument':
|
|
||||||
case 'this':
|
|
||||||
case 'that':
|
|
||||||
if($index !== 0) {
|
|
||||||
$this->resolveSegmentToR13($segment, $index);
|
|
||||||
$lookupRegister = '@R13';
|
|
||||||
} else {
|
|
||||||
$lookupRegister = $this->segmentToRegister($segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->write([
|
|
||||||
"@SP // pop",
|
|
||||||
"A=M-1",
|
|
||||||
"D=M",
|
|
||||||
$lookupRegister,
|
|
||||||
"A=M // Read $lookupRegister to A",
|
|
||||||
"M=D // Write D to *A",
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
# code...
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function writePushPop(Int $command, String $segment , Int $index) {
|
|
||||||
switch ($command) {
|
|
||||||
case CommandType::PUSH:
|
|
||||||
$this->writePush($segment, $index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CommandType::POP:
|
|
||||||
$this->writePop($segment, $index);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception("Invalid Command Type", 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class VMTranslator {
|
class VMTranslator {
|
||||||
function __construct(String $fileOrDir ) {
|
function __construct(String $fileOrDir ) {
|
||||||
|
@ -363,6 +43,8 @@ class VMTranslator {
|
||||||
throw new \Exception("Not Implemented $command", 1);
|
throw new \Exception("Not Implemented $command", 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->writer->nextSourceLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->writer->close();
|
$this->writer->close();
|
||||||
|
|
Loading…
Reference in New Issue