mirror of
https://github.com/captn3m0/nand2tetris.git
synced 2024-09-11 16:46:28 +00:00
[07] Initial work on VM Translator
This commit is contained in:
parent
1966f8b673
commit
c1cac6ad6f
4
NOTES.md
4
NOTES.md
@ -64,3 +64,7 @@ The hardest part about this was deciding what name to give to all the pins
|
||||
## Assembler
|
||||
|
||||
I think there are definitely some tricks with reducing lookup table sizes, but I wasn't really aiming for performance (I wrote it in ruby afterall). Also working on a rust implementation, just to learn rust.
|
||||
|
||||
# VM (1)
|
||||
|
||||
See `vm/README.md` for more details. Observations go here, implementation notes are there.
|
||||
|
1
projects/07/StackArithmetic/SimpleAdd/SimpleAdd.out
Normal file
1
projects/07/StackArithmetic/SimpleAdd/SimpleAdd.out
Normal file
@ -0,0 +1 @@
|
||||
| RAM[0] | RAM[256] |
|
37
vm/README.md
Normal file
37
vm/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# VM Implementation
|
||||
|
||||
We have 8 segments:
|
||||
|
||||
- `argument`
|
||||
- `local`
|
||||
- `this/that`
|
||||
- `pointer`
|
||||
- `static` (shared)
|
||||
- `constant` (shared)
|
||||
- `temp` (shared)
|
||||
|
||||
RAM Address | Usage
|
||||
============|=================
|
||||
0-15 | Virtual Registers
|
||||
16-255 | Static Variables (shared)
|
||||
256-2047 | Stack
|
||||
2048-16384 | Heap
|
||||
16384-24575 | Memory mapped I/O
|
||||
|
||||
Register | Name | Usage
|
||||
==========|========|=========
|
||||
`RAM[0]` | `SP` | Stack Pointer
|
||||
`RAM[1]` | `LCL` | `local`
|
||||
`RAM[1]` | `ARG` | `argument`
|
||||
`RAM[3]` | `THIS` | `this`
|
||||
`RAM[4]` | `THAT` | `that`
|
||||
RAM[5-12] | `temp` Segment
|
||||
RAM[13-15]| General Purpose Registers
|
||||
|
||||
|
||||
The implementation is written in Modern PHP with static typing. Uses the following 3 classes
|
||||
|
||||
- `CommandType` as a Enum for using command types as constants
|
||||
- `Parser`, mostly as defined in the specification
|
||||
- `CodeWriter`, mostly as defined in the specification
|
||||
- `VMTranslator` which combines the above
|
139
vm/VMTranslator.php
Normal file
139
vm/VMTranslator.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = $pieces[0];
|
||||
yield $this->command;
|
||||
}
|
||||
fclose($this->file);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeWriter {
|
||||
function __construct($outputFile) {
|
||||
$this->file = fopen($outputFile, "w");
|
||||
}
|
||||
|
||||
function writeArithmetic(String $command ) {
|
||||
throw new Exception("Not yet Implemented");
|
||||
}
|
||||
|
||||
function writePushPop(Int $command, String $segment , Int $index) {
|
||||
throw new Exception("Not yet Implemented");
|
||||
}
|
||||
|
||||
function close() {
|
||||
throw new Exception("Not yet Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VMTranslator {
|
||||
function __construct(String $fileOrDir ) {
|
||||
if (is_dir($fileOrDir)) {
|
||||
$this->files = glob("$fileOrDir/*.vm");
|
||||
} else {
|
||||
$this->files = [$fileOrDir];
|
||||
}
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
assert(is_readable($file));
|
||||
}
|
||||
|
||||
$outputFile = $this->outputFile();
|
||||
$this->writer = new CodeWriter($outputFile);
|
||||
}
|
||||
|
||||
function translate() {
|
||||
foreach ($this->files as $file) {
|
||||
$parser = new Parser($file);
|
||||
|
||||
foreach ($parser->commands() as $command) {
|
||||
switch ($command) {
|
||||
case CommandType::ARITHMETIC:
|
||||
$this->writer->writeArithmetic($command);
|
||||
break;
|
||||
|
||||
case CommandType::PUSH:
|
||||
case CommandType::POP:
|
||||
$segment = $parser->arg1();
|
||||
$index = $parser->arg2();
|
||||
$this->writer->writePushPop($command, $parser, $index);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("Not Implemented", 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function outputFile() {
|
||||
return '/tmp/file.asm';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(isset($argv[1])) {
|
||||
$vmt = new VMTranslator($argv[1]);
|
||||
$vmt->translate();
|
||||
}
|
Loading…
Reference in New Issue
Block a user