118 lines
2.6 KiB
Ruby
118 lines
2.6 KiB
Ruby
require_relative "Parser.rb"
|
|
class Writer
|
|
A_INSTRUCTION = /^@\d+$/
|
|
def initialize(asm_file_path, hack_file_path)
|
|
@hack_file = File.open(hack_file_path, "w")
|
|
@parser = Parser.new(asm_file_path)
|
|
end
|
|
def write
|
|
while @parser.has_more_commands
|
|
compile
|
|
end
|
|
@parser.close
|
|
@hack_file.close
|
|
end
|
|
def compile
|
|
if command.match(A_INSTRUCTION)
|
|
compile_a_ins
|
|
else
|
|
compile_c_ins
|
|
write_txt("", true)
|
|
end
|
|
@parser.advance
|
|
end
|
|
def compile_a_ins
|
|
ains = command[1..-1].to_i.to_s(base=2)
|
|
(16-ains.length).times {ains = "0" + ains}
|
|
write_txt(ains, true)
|
|
end
|
|
def compile_c_ins
|
|
write_txt("111")
|
|
commands = command.split(/=|;/)
|
|
if command.include?("=")
|
|
compile_alu(commands[1].strip)
|
|
compile_save_to(commands[0].strip)
|
|
compile_jump_to("#{commands[2] ? commands[2].strip : nil}")
|
|
else
|
|
compile_alu(commands[0].strip)
|
|
compile_save_to("")
|
|
compile_jump_to("#{commands[1] ? commands[1].strip : nil}")
|
|
end
|
|
end
|
|
def compile_alu(cmd)
|
|
cmd.include?("M") ? write_txt("1") : write_txt("0")
|
|
case cmd
|
|
when "0"
|
|
write_txt("101010")
|
|
when "1"
|
|
write_txt("111111")
|
|
when "-1"
|
|
write_txt("111010")
|
|
when "D"
|
|
write_txt("001100")
|
|
when "A", "M"
|
|
write_txt("110000")
|
|
when "!D"
|
|
write_txt("001101")
|
|
when "!A", "!M"
|
|
write_txt("110001")
|
|
when "-D"
|
|
write_txt("001111")
|
|
when "-A", "-M"
|
|
write_txt("110011")
|
|
when "D+1"
|
|
write_txt("011111")
|
|
when "A+1", "M+1"
|
|
write_txt("110111")
|
|
when "D-1"
|
|
write_txt("001110")
|
|
when "A-1", "M-1"
|
|
write_txt("110010")
|
|
when "D+A", "D+M"
|
|
write_txt("000010")
|
|
when "D-A", "D-M"
|
|
write_txt("010011")
|
|
when "A-D", "M-D"
|
|
write_txt("000111")
|
|
when "D&A", "D&M"
|
|
write_txt("000000")
|
|
when "D|A", "D|M"
|
|
write_txt("010101")
|
|
else
|
|
raise "no such alu command"
|
|
end
|
|
end
|
|
def compile_save_to(cmd)
|
|
cmd.include?("A") ? write_txt("1") : write_txt("0")
|
|
cmd.include?("D") ? write_txt("1") : write_txt("0")
|
|
cmd.include?("M") ? write_txt("1") : write_txt("0")
|
|
end
|
|
def compile_jump_to(command)
|
|
case command
|
|
when nil, ""
|
|
write_txt("000")
|
|
when "JGT"
|
|
write_txt("001")
|
|
when "JEQ"
|
|
write_txt("010")
|
|
when "JGE"
|
|
write_txt("011")
|
|
when "JLT"
|
|
write_txt("100")
|
|
when "JNE"
|
|
write_txt("101")
|
|
when "JLE"
|
|
write_txt("110")
|
|
when "JMP"
|
|
write_txt("111")
|
|
end
|
|
end
|
|
private
|
|
def command
|
|
@parser.current_command
|
|
end
|
|
def write_txt(binaries, ending=false)
|
|
@hack_file.write("#{binaries}#{ending ? "\n" : ""}")
|
|
end
|
|
end
|