diff --git a/projects/07/MemoryAccess/BasicTest/BasicTest.asm b/projects/07/MemoryAccess/BasicTest/BasicTest.asm index 4a8a9fa..31cb9ae 100644 --- a/projects/07/MemoryAccess/BasicTest/BasicTest.asm +++ b/projects/07/MemoryAccess/BasicTest/BasicTest.asm @@ -4,27 +4,27 @@ D=A A=M M=D @SP -M=M+1 // end push constant 10 +M=M+1 // end push constant 10 (L0) @SP // pop -A=M-1 +AM=M-1 D=M @LCL -A=M // Read @LCL to A -M=D // Write D to *A +A=M // Read @LCL to A (for local 0) +M=D // end pop local 0 (L1) @21 // push constant 21 D=A @SP A=M M=D @SP -M=M+1 // end push constant 21 +M=M+1 // end push constant 21 (L2) @22 // push constant 22 D=A @SP A=M M=D @SP -M=M+1 // end push constant 22 +M=M+1 // end push constant 22 (L3) @ARG // argument 2 D=M @2 // write 2 to A @@ -32,11 +32,11 @@ D=D+A // D = segment+index @R13 // save it to R13 M=D // write @ARG+2 to R13 @SP // pop -A=M-1 +AM=M-1 D=M @R13 -A=M // Read @R13 to A -M=D // Write D to *A +A=M // Read @R13 to A (for argument 2) +M=D // end pop argument 2 (L4) @ARG // argument 1 D=M @1 // write 1 to A @@ -44,18 +44,18 @@ D=D+A // D = segment+index @R13 // save it to R13 M=D // write @ARG+1 to R13 @SP // pop -A=M-1 +AM=M-1 D=M @R13 -A=M // Read @R13 to A -M=D // Write D to *A +A=M // Read @R13 to A (for argument 1) +M=D // end pop argument 1 (L5) @36 // push constant 36 D=A @SP A=M M=D @SP -M=M+1 // end push constant 36 +M=M+1 // end push constant 36 (L6) @THIS // this 6 D=M @6 // write 6 to A @@ -63,25 +63,25 @@ D=D+A // D = segment+index @R13 // save it to R13 M=D // write @THIS+6 to R13 @SP // pop -A=M-1 +AM=M-1 D=M @R13 -A=M // Read @R13 to A -M=D // Write D to *A +A=M // Read @R13 to A (for this 6) +M=D // end pop this 6 (L7) @42 // push constant 42 D=A @SP A=M M=D @SP -M=M+1 // end push constant 42 +M=M+1 // end push constant 42 (L8) @45 // push constant 45 D=A @SP A=M M=D @SP -M=M+1 // end push constant 45 +M=M+1 // end push constant 45 (L9) @THAT // that 5 D=M @5 // write 5 to A @@ -89,11 +89,11 @@ D=D+A // D = segment+index @R13 // save it to R13 M=D // write @THAT+5 to R13 @SP // pop -A=M-1 +AM=M-1 D=M @R13 -A=M // Read @R13 to A -M=D // Write D to *A +A=M // Read @R13 to A (for that 5) +M=D // end pop that 5 (L10) @THAT // that 2 D=M @2 // write 2 to A @@ -101,31 +101,30 @@ D=D+A // D = segment+index @R13 // save it to R13 M=D // write @THAT+2 to R13 @SP // pop -A=M-1 +AM=M-1 D=M @R13 -A=M // Read @R13 to A -M=D // Write D to *A +A=M // Read @R13 to A (for that 2) +M=D // end pop that 2 (L11) @510 // push constant 510 D=A @SP A=M M=D @SP -M=M+1 // end push constant 510 -@LCL // local 0 +M=M+1 // end push constant 510 (L12) +@SP +AM=M-1 D=M -@0 // write 0 to A -D=D+A // D = segment+index -@R13 // save it to R13 -M=D // write @LCL+0 to R13 -@R13 +@R11 +M=D // end pop temp 6 (L13) +@LCL D=M @SP A=M M=D @SP -M=M+1 // end push local 0 +M=M+1 // end push local 0 (L14) @THAT // that 5 D=M @5 // write 5 to A @@ -138,14 +137,14 @@ D=M A=M M=D @SP -M=M+1 // end push that 5 +M=M+1 // end push that 5 (L15) @SP // ==== add ==== A=M-1 D=M A=A-1 M=D+M @SP -M=M-1 +M=M-1 // end add (L16) @ARG // argument 1 D=M @1 // write 1 to A @@ -158,14 +157,14 @@ D=M A=M M=D @SP -M=M+1 // end push argument 1 +M=M+1 // end push argument 1 (L17) @SP // ==== sub ==== A=M-1 D=M A=A-1 M=M-D @SP -M=M-1 +M=M-1 // end sub (L18) @THIS // this 6 D=M @6 // write 6 to A @@ -178,7 +177,7 @@ D=M A=M M=D @SP -M=M+1 // end push this 6 +M=M+1 // end push this 6 (L19) @THIS // this 6 D=M @6 // write 6 to A @@ -191,34 +190,34 @@ D=M A=M M=D @SP -M=M+1 // end push this 6 +M=M+1 // end push this 6 (L20) @SP // ==== add ==== A=M-1 D=M A=A-1 M=D+M @SP -M=M-1 +M=M-1 // end add (L21) @SP // ==== sub ==== A=M-1 D=M A=A-1 M=M-D @SP -M=M-1 +M=M-1 // end sub (L22) @R11 // temp 6 D=M @SP A=M M=D @SP -M=M+1 // end push temp 6 +M=M+1 // end push temp 6 (L23) @SP // ==== add ==== A=M-1 D=M A=A-1 M=D+M @SP -M=M-1 -@223 +M=M-1 // end add (L24) +@222 0;JMP diff --git a/projects/07/MemoryAccess/BasicTest/BasicTest.out b/projects/07/MemoryAccess/BasicTest/BasicTest.out index 240f194..da20819 100644 --- a/projects/07/MemoryAccess/BasicTest/BasicTest.out +++ b/projects/07/MemoryAccess/BasicTest/BasicTest.out @@ -1,2 +1 @@ |RAM[256]|RAM[300]|RAM[401]|RAM[402]|RAM[3006|RAM[3012|RAM[3015|RAM[11] | -| 10 | 10 | 22 | 22 | 36 | 45 | 45 | 0 | diff --git a/projects/07/StackArithmetic/StackTest/StackTest.out b/projects/07/StackArithmetic/StackTest/StackTest.out index cb182ec..e69de29 100644 --- a/projects/07/StackArithmetic/StackTest/StackTest.out +++ b/projects/07/StackArithmetic/StackTest/StackTest.out @@ -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 | diff --git a/vm/CodeWriter.php b/vm/CodeWriter.php new file mode 100644 index 0000000..1b68be8 --- /dev/null +++ b/vm/CodeWriter.php @@ -0,0 +1,289 @@ +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; + } + } +} diff --git a/vm/CommandType.php b/vm/CommandType.php new file mode 100644 index 0000000..1a19655 --- /dev/null +++ b/vm/CommandType.php @@ -0,0 +1,35 @@ +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); + } +} diff --git a/vm/VMTranslator.php b/vm/VMTranslator.php index 5434d98..68b9e68 100644 --- a/vm/VMTranslator.php +++ b/vm/VMTranslator.php @@ -1,329 +1,9 @@ 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; - } - } -} - +require_once("CommandType.php"); +require_once("CodeWriter.php"); +require_once("Parser.php"); class VMTranslator { function __construct(String $fileOrDir ) { @@ -363,6 +43,8 @@ class VMTranslator { throw new \Exception("Not Implemented $command", 1); break; } + + $this->writer->nextSourceLine(); } } $this->writer->close();