hack_assembler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Pong.asm +28374 -0
- data/PongL.asm +27490 -0
- data/README.md +32 -0
- data/Rakefile +10 -0
- data/bin/hack_assembler +17 -0
- data/hack2asm.gemspec +24 -0
- data/lib/hack_assembler/a_instruction.rb +14 -0
- data/lib/hack_assembler/assembler.rb +21 -0
- data/lib/hack_assembler/c_instruction.rb +17 -0
- data/lib/hack_assembler/computation_translator.rb +41 -0
- data/lib/hack_assembler/destination_translator.rb +22 -0
- data/lib/hack_assembler/jump_translator.rb +22 -0
- data/lib/hack_assembler/parser_error.rb +4 -0
- data/lib/hack_assembler/version.rb +3 -0
- data/lib/hack_assembler.rb +21 -0
- data/out.hack +27483 -0
- data/test/a_instruction_test.rb +21 -0
- data/test/assembler_test.rb +27 -0
- data/test/c_instruction_test.rb +44 -0
- data/test/computation_translator_test.rb +115 -0
- data/test/destination_translator_test.rb +39 -0
- data/test/hack2asm_test.rb +7 -0
- data/test/jump_translator_test.rb +39 -0
- data/test/test_helper.rb +6 -0
- metadata +123 -0
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/Fuffi/hack2asm.svg?branch=master)](https://travis-ci.org/Fuffi/hack_assembler)
|
2
|
+
|
3
|
+
# Hack Assembler
|
4
|
+
|
5
|
+
Converts assembly code for the Hack machine built during the nand2tetris exercises.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'hack_assembler'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install hack_assembler
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
After installing the Gem you can run this command from your terminal:
|
24
|
+
hack_assembler <input-file.asm> <output-file.hack>
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
|
28
|
+
1. Fork it
|
29
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
30
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
31
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
32
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/hack_assembler
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/evn ruby
|
2
|
+
require_relative '../lib/hack_assembler'
|
3
|
+
|
4
|
+
input_file = ARGV[0]
|
5
|
+
output_file = ARGV[1]
|
6
|
+
|
7
|
+
if input_file.nil? || output_file.nil?
|
8
|
+
puts "Use the following format: hack_assembler <input_file.asm> <output_file.hack>"
|
9
|
+
exit -1
|
10
|
+
end
|
11
|
+
|
12
|
+
unless File.file?(input_file)
|
13
|
+
puts "The file #{input_file} doesn't exists"
|
14
|
+
exit -1
|
15
|
+
end
|
16
|
+
|
17
|
+
HackAssembler.translate_file(input_file, output_file)
|
data/hack2asm.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hack_assembler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hack_assembler"
|
8
|
+
spec.version = HackAssembler::VERSION
|
9
|
+
spec.authors = ["Federico Rebora"]
|
10
|
+
spec.email = ["ufinii@gmail.com"]
|
11
|
+
spec.description = %q{Assembler that takes .hack code and converts it to Hack machine ASM}
|
12
|
+
spec.summary = %q{Converts .hack files to .asm}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables << 'hack_assembler'
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "minitest"
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module AInstruction
|
3
|
+
def self.translate(instruction)
|
4
|
+
matched = /@([0-9]+)/.match(instruction)
|
5
|
+
raise ParserError if matched.nil?
|
6
|
+
|
7
|
+
value = matched[1].to_i
|
8
|
+
binary_value = value.to_s(2)
|
9
|
+
padded_binary_value = binary_value.rjust(16, '0')
|
10
|
+
|
11
|
+
padded_binary_value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module Assembler
|
3
|
+
def self.translate(source_code)
|
4
|
+
machine_code = ''
|
5
|
+
|
6
|
+
source_code_extract = source_code.gsub(/^[\s]*$\n/, '')
|
7
|
+
|
8
|
+
source_code_extract.each_line do |line|
|
9
|
+
next if line.start_with? '//'
|
10
|
+
|
11
|
+
clean_line = line.strip
|
12
|
+
|
13
|
+
instruction = clean_line.start_with?('@') ? AInstruction : CInstruction
|
14
|
+
|
15
|
+
machine_code << instruction.translate(clean_line) << "\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
machine_code
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module CInstruction
|
3
|
+
def self.translate(instruction)
|
4
|
+
matched = /((.+)=)?([^;]+)(;([A-Z]+))?/.match(instruction)
|
5
|
+
destination = matched[2]
|
6
|
+
computation = matched[3]
|
7
|
+
jump = matched[5]
|
8
|
+
|
9
|
+
bytecode = '1110000000000000'
|
10
|
+
bytecode[3..9] = ComputationTranslator.translate(computation)
|
11
|
+
bytecode[10..12] = DestinationTranslator.translate(destination)
|
12
|
+
bytecode[13..15] = JumpTranslator.translate(jump)
|
13
|
+
|
14
|
+
bytecode
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module ComputationTranslator
|
3
|
+
def self.translate(computation)
|
4
|
+
a_or_m_register = '0'
|
5
|
+
if computation.include? 'M'
|
6
|
+
a_or_m_register = '1'
|
7
|
+
end
|
8
|
+
|
9
|
+
generic_computation = computation.gsub(/X/, '$').gsub(/(A|M)/, 'X')
|
10
|
+
|
11
|
+
operation_bytecode = @@translation_table[generic_computation]
|
12
|
+
raise ParserError if operation_bytecode.nil?
|
13
|
+
|
14
|
+
bytecode = a_or_m_register + operation_bytecode
|
15
|
+
|
16
|
+
bytecode
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
@@translation_table = {
|
21
|
+
'0' => '101010',
|
22
|
+
'1' => '111111',
|
23
|
+
'-1' => '111010',
|
24
|
+
'X' => '110000',
|
25
|
+
'D' => '001100',
|
26
|
+
'!D' => '001101',
|
27
|
+
'!X' => '110001',
|
28
|
+
'-D' => '001111',
|
29
|
+
'-X' => '110011',
|
30
|
+
'D+1' => '011111',
|
31
|
+
'X+1' => '110111',
|
32
|
+
'D-1' => '001110',
|
33
|
+
'X-1' => '110010',
|
34
|
+
'D+X' => '000010',
|
35
|
+
'D-X' => '010011',
|
36
|
+
'X-D' => '000111',
|
37
|
+
'D&X' => '000000',
|
38
|
+
'D|X' => '010101'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module DestinationTranslator
|
3
|
+
def self.translate(destination)
|
4
|
+
bytecode = @@translation_table[destination]
|
5
|
+
raise ParserError if bytecode.nil?
|
6
|
+
|
7
|
+
bytecode
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
@@translation_table = {
|
12
|
+
nil => '000',
|
13
|
+
'M' => '001',
|
14
|
+
'D' => '010',
|
15
|
+
'MD' => '011',
|
16
|
+
'A' => '100',
|
17
|
+
'AM' => '101',
|
18
|
+
'AD' => '110',
|
19
|
+
'AMD' => '111'
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HackAssembler
|
2
|
+
module JumpTranslator
|
3
|
+
def self.translate(jump)
|
4
|
+
bytecode = @@translation_table[jump]
|
5
|
+
raise ParserError if bytecode.nil?
|
6
|
+
|
7
|
+
bytecode
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
@@translation_table = {
|
12
|
+
nil => '000',
|
13
|
+
'JMP' => '111',
|
14
|
+
'JGT' => '001',
|
15
|
+
'JEQ' => '010',
|
16
|
+
'JGE' => '011',
|
17
|
+
'JLT' => '100',
|
18
|
+
'JNE' => '101',
|
19
|
+
'JLE' => '110'
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'hack_assembler/version'
|
2
|
+
require_relative 'hack_assembler/parser_error'
|
3
|
+
require_relative 'hack_assembler/a_instruction'
|
4
|
+
require_relative 'hack_assembler/computation_translator'
|
5
|
+
require_relative 'hack_assembler/destination_translator'
|
6
|
+
require_relative 'hack_assembler/jump_translator'
|
7
|
+
require_relative 'hack_assembler/c_instruction'
|
8
|
+
require_relative 'hack_assembler/assembler'
|
9
|
+
|
10
|
+
module HackAssembler
|
11
|
+
def self.translate_file(input_file, output_file)
|
12
|
+
source_file = File.open(input_file)
|
13
|
+
source_code = source_file.read
|
14
|
+
|
15
|
+
machine_code = Assembler.translate(source_code)
|
16
|
+
|
17
|
+
File.open(output_file, 'w') { |file| file.write(machine_code); file.close }
|
18
|
+
|
19
|
+
source_file.close
|
20
|
+
end
|
21
|
+
end
|