From 998b08890fa56e07db1cdd4402e94f74da95e1fa Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 00:05:20 +0000 Subject: [PATCH 01/16] Impl Parser#has_more_commands? --- lib/parser.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/parser.rb b/lib/parser.rb index 7311279..964b976 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -1,2 +1,9 @@ -module Parser +class Parser + def initialize input + @remaining = input + end + + def has_more_commands? + @remaining =~ /^\s*[^\s\/]+.*$/ + end end From 9ed6185efaf88470d225bd208fc73c8220fb513b Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 00:31:06 +0000 Subject: [PATCH 02/16] Impl Parser#advance --- lib/parser.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/parser.rb b/lib/parser.rb index 964b976..e6f3b03 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -6,4 +6,10 @@ def initialize input def has_more_commands? @remaining =~ /^\s*[^\s\/]+.*$/ end + + def advance + begin + @current, _, @remaining = @remaining.partition("\n") + end while @current =~ /^((\s*\/+.*)|\s*$)/ + end end From d39cd8731983db52ae8f38acf479be938ffcdff3 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 01:04:21 +0000 Subject: [PATCH 03/16] Impl Parser#command_type --- lib/parser.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/parser.rb b/lib/parser.rb index e6f3b03..752f1db 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -1,4 +1,8 @@ class Parser + A_COMMAND = 4 + L_COMMAND = 1 + C_COMMAND = 8 + def initialize input @remaining = input end @@ -12,4 +16,12 @@ def advance @current, _, @remaining = @remaining.partition("\n") end while @current =~ /^((\s*\/+.*)|\s*$)/ end + + def command_type + case @current.lstrip[0] + when '@' then Parser::A_COMMAND + when '(' then Parser::L_COMMAND + else Parser::C_COMMAND + end + end end From 48660f2292fa270fcb3089a036c6fe1b4248247d Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 01:26:07 +0000 Subject: [PATCH 04/16] Impl Parser#symbol --- lib/parser.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/parser.rb b/lib/parser.rb index 752f1db..4518fbc 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -24,4 +24,12 @@ def command_type else Parser::C_COMMAND end end + + def symbol + case command_type + when Parser::A_COMMAND then @current.strip[1..-1] + when Parser::L_COMMAND then @current.strip[1..-2] + else raise "Parser#symbol not defined for current command_type" + end + end end From 53a0722f707d4f10fbda0e6834164db53cc709bc Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 02:26:15 +0000 Subject: [PATCH 05/16] Impl Parser#dest --- lib/parser.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/parser.rb b/lib/parser.rb index 4518fbc..81f0e88 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -29,7 +29,20 @@ def symbol case command_type when Parser::A_COMMAND then @current.strip[1..-1] when Parser::L_COMMAND then @current.strip[1..-2] - else raise "Parser#symbol not defined for current command_type" + else wrong_command_type end end + + def dest + wrong_command_type unless command_type == Parser::C_COMMAND + + dest, match, _ = @current.partition('=') + match.empty? ? "" : dest + end + + private + def wrong_command_type + f = caller[0][/`.*'/][1..-2] + raise "#{self.class}##{f} not defined for current command_type" + end end From c38b09757c2ff9ec9bbdfc6cf36f1ae22b6c2aad Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 02:31:11 +0000 Subject: [PATCH 06/16] Impl Parser#jump --- lib/parser.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/parser.rb b/lib/parser.rb index 81f0e88..4eb01bf 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -40,6 +40,13 @@ def dest match.empty? ? "" : dest end + def jump + wrong_command_type unless command_type == Parser::C_COMMAND + + _, match, jump = @current.rpartition(';') + match.empty? ? "": jump + end + private def wrong_command_type f = caller[0][/`.*'/][1..-2] From 736af77b6d0a8b4376c125b48123d468ea71b45e Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 02:41:23 +0000 Subject: [PATCH 07/16] Impl Parser#comp --- lib/parser.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/parser.rb b/lib/parser.rb index 4eb01bf..74eaaae 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -47,6 +47,13 @@ def jump match.empty? ? "": jump end + def comp + wrong_command_type unless command_type == Parser::C_COMMAND + + comp, _, _ = @current.partition(';') + comp.rpartition('=')[2] + end + private def wrong_command_type f = caller[0][/`.*'/][1..-2] From 4cbde0bbb56639641da65f3a2e2ed49c7ef68c98 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 03:02:53 +0000 Subject: [PATCH 08/16] Impl Code#dest --- lib/code.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/code.rb b/lib/code.rb index 646c2ea..1ef0dd1 100644 --- a/lib/code.rb +++ b/lib/code.rb @@ -1,2 +1,8 @@ module Code + def self.dest mnemonic + d1 = mnemonic.index('A') ? '1' : '0' + d2 = mnemonic.index('D') ? '1' : '0' + d3 = mnemonic.index('M') ? '1' : '0' + d1 + d2 + d3 + end end From 01eb9e590973e7f98aefba5aa90d7ae1cef65cc8 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 03:07:01 +0000 Subject: [PATCH 09/16] Impl Code#jump --- lib/code.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/code.rb b/lib/code.rb index 1ef0dd1..421752c 100644 --- a/lib/code.rb +++ b/lib/code.rb @@ -5,4 +5,13 @@ def self.dest mnemonic d3 = mnemonic.index('M') ? '1' : '0' d1 + d2 + d3 end + + def self.jump mnemonic + return "111" if mnemonic == "JMP" + return "101" if mnemonic == "JNE" + j1 = mnemonic.index('L') ? '1' : '0' + j2 = mnemonic.index('E') ? '1' : '0' + j3 = mnemonic.index('G') ? '1' : '0' + j1 + j2 + j3 + end end From c51d5b1e8f0aff098e8b4224ace1e010dfe8ee95 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 03:21:12 +0000 Subject: [PATCH 10/16] Impl Code#comp --- lib/code.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/code.rb b/lib/code.rb index 421752c..9441659 100644 --- a/lib/code.rb +++ b/lib/code.rb @@ -14,4 +14,34 @@ def self.jump mnemonic j3 = mnemonic.index('G') ? '1' : '0' j1 + j2 + j3 end + + def self.comp mnemonic + address = mnemonic.index('M') ? '1' : '0' + + # Swap M for A so the lookup table doesn't need to account for both + comp = case mnemonic.sub('M', 'A') + when "0" then '101010' + when '0' then '101010' + when '1' then '111111' + when '-1' then '111010' + when 'D' then '001100' + when 'A' then '110000' + when '!D' then '001101' + when '!A' then '110001' + when '-D' then '001111' + when '-A' then '110011' + when 'D+1' then '011111' + when 'A+1' then '110111' + when 'D-1' then '001110' + when 'A-1' then '110010' + when 'D+A' then '000010' + when 'D-A' then '010011' + when 'A-D' then '000111' + when 'D&A' then '000000' + when 'D|A' then '010101' + else raise "Unknown mnemonic" + end + + address + comp + end end From 1142c375ca72c70effee689e4a94b660d36f9d75 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 03:38:36 +0000 Subject: [PATCH 11/16] Impl assembler with no symbol table support --- bin/assembler | 15 ++++++++++++++- lib/parser.rb | 7 +++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bin/assembler b/bin/assembler index 836ed1f..65ce62e 100755 --- a/bin/assembler +++ b/bin/assembler @@ -4,4 +4,17 @@ require_relative '../lib/parser' require_relative '../lib/code' require_relative '../lib/symbol_table' -input = ARGF.read +parser = Parser.new(ARGF.read) + +parser.each_command do |command_type| + case command_type + when Parser::A_COMMAND then puts "0%015b" % parser.symbol + when Parser::C_COMMAND then + comp, dest, jump = [:comp, :dest, :jump].map do |sym| + Code.send(sym, parser.send(sym)) + end + + puts "111#{comp}#{dest}#{jump}" + else raise "Unimplemented" + end +end diff --git a/lib/parser.rb b/lib/parser.rb index 74eaaae..e71051d 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -25,6 +25,13 @@ def command_type end end + def each_command + while has_more_commands? + advance + yield command_type + end + end + def symbol case command_type when Parser::A_COMMAND then @current.strip[1..-1] From 20f41d40c5bc191f4edaeb0b8c5184dbcd701473 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 03:42:40 +0000 Subject: [PATCH 12/16] Impl SymbolTable --- lib/symbol_table.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/symbol_table.rb b/lib/symbol_table.rb index 9d0005c..1a11d27 100644 --- a/lib/symbol_table.rb +++ b/lib/symbol_table.rb @@ -1,2 +1,15 @@ -module SymbolTable +class SymbolTable + def initialize + @table = {} + end + + def add_entry symbol, value + @table[symbol] = value + end + + def get_address symbol + @table[symbol] + end + + alias contains? get_address end From 8cda02e64b48056470f46f4bc513655d8943329f Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 04:06:09 +0000 Subject: [PATCH 13/16] Improve error message for bad mneumonics --- lib/code.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/code.rb b/lib/code.rb index 9441659..30bb19d 100644 --- a/lib/code.rb +++ b/lib/code.rb @@ -39,7 +39,7 @@ def self.comp mnemonic when 'A-D' then '000111' when 'D&A' then '000000' when 'D|A' then '010101' - else raise "Unknown mnemonic" + else raise "Unknown mnemonic: '#{mnemonic}'" end address + comp From 98ed0fe368573d4989bd1fe65df4d3e988b654f3 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 04:06:33 +0000 Subject: [PATCH 14/16] Strip trailing linecomments --- lib/parser.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/parser.rb b/lib/parser.rb index e71051d..6d48915 100644 --- a/lib/parser.rb +++ b/lib/parser.rb @@ -13,8 +13,10 @@ def has_more_commands? def advance begin - @current, _, @remaining = @remaining.partition("\n") - end while @current =~ /^((\s*\/+.*)|\s*$)/ + line, _, @remaining = @remaining.partition("\n") + # Strip trailing comments + @current = line.partition('//').first.strip + end while @current.empty? end def command_type From 9cf89da96a9a795d421eb5ff5dfe04e040b5987a Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 04:35:38 +0000 Subject: [PATCH 15/16] Implement assembler with symbol support --- bin/assembler | 81 +++++++++++++++++++++++++++++++++++++++------ lib/symbol_table.rb | 4 +-- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/bin/assembler b/bin/assembler index 65ce62e..cd20ba9 100755 --- a/bin/assembler +++ b/bin/assembler @@ -4,17 +4,78 @@ require_relative '../lib/parser' require_relative '../lib/code' require_relative '../lib/symbol_table' -parser = Parser.new(ARGF.read) - -parser.each_command do |command_type| - case command_type - when Parser::A_COMMAND then puts "0%015b" % parser.symbol - when Parser::C_COMMAND then - comp, dest, jump = [:comp, :dest, :jump].map do |sym| - Code.send(sym, parser.send(sym)) +HARDCODED_ADDRESSES = { + 'SP' => 0, + 'LCL' => 1, + 'ARG' => 2, + 'THIS' => 3, + 'THAT' => 4, + 'R0' => 0, + 'R1' => 1, + 'R2' => 2, + 'R3' => 3, + 'R4' => 4, + 'R5' => 5, + 'R6' => 6, + 'R7' => 7, + 'R8' => 8, + 'R9' => 9, + 'R10' => 10, + 'R11' => 11, + 'R12' => 12, + 'R13' => 13, + 'R14' => 14, + 'R15' => 15, + 'SCREEN' => 0x4000, + 'KBD' => 0x6000 +} + +def parse_labels(input) + parser = Parser.new(input) + table = SymbolTable.new HARDCODED_ADDRESSES + icount = 0 + + parser.each_command do |command_type| + if command_type == Parser::L_COMMAND + table.add_entry parser.symbol, icount + else + icount += 1 + end + end + + table +end + +def generate_code(input, table) + parser = Parser.new(input) + # Start placing variables from address 16 onwards + next_address = 16 + + get_address = ->(sym) do + if sym =~ /^\d+$/ + sym + elsif table.contains? sym + table.get_address sym + else + table.add_entry sym, next_address + address, next_address = next_address, next_address + 1 end + end - puts "111#{comp}#{dest}#{jump}" - else raise "Unimplemented" + parser.each_command do |command_type| + case command_type + when Parser::A_COMMAND then + puts "0%015b" % get_address.(parser.symbol) + when Parser::C_COMMAND then + comp, dest, jump = [:comp, :dest, :jump].map do |sym| + Code.send(sym, parser.send(sym)) + end + + puts "111#{comp}#{dest}#{jump}" + end end end + +input = ARGF.read +table = parse_labels(input) +generate_code(input, table) diff --git a/lib/symbol_table.rb b/lib/symbol_table.rb index 1a11d27..374d221 100644 --- a/lib/symbol_table.rb +++ b/lib/symbol_table.rb @@ -1,6 +1,6 @@ class SymbolTable - def initialize - @table = {} + def initialize table = {} + @table = table end def add_entry symbol, value From 611ce4f6b18ddb938c4736bc4b0a0822076fc40e Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Thu, 5 Mar 2015 17:01:10 +0000 Subject: [PATCH 16/16] Remove fancypants code --- bin/assembler | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/assembler b/bin/assembler index cd20ba9..3b553c7 100755 --- a/bin/assembler +++ b/bin/assembler @@ -67,9 +67,9 @@ def generate_code(input, table) when Parser::A_COMMAND then puts "0%015b" % get_address.(parser.symbol) when Parser::C_COMMAND then - comp, dest, jump = [:comp, :dest, :jump].map do |sym| - Code.send(sym, parser.send(sym)) - end + comp = Code.comp(parser.comp) + dest = Code.dest(parser.dest) + jump = Code.jump(parser.jump) puts "111#{comp}#{dest}#{jump}" end