Guest User

Untitled

a guest
Jan 22nd, 2019
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.37 KB | None | 0 0
  1. # __ __ _ ___ _ _ ___ _ _ ___ ___ ___ ___ ___
  2. # | \/ | /_\ / __| || |_ _| \| | __| / __/ _ \| \| __|
  3. # | |\/| |/ _ \ (__| __ || || .` | _| | (_| (_) | |) | _|
  4. # |_| |_/_/ \_\___|_||_|___|_|\_|___| \___\___/|___/|___|
  5. # ___ ___ _ _ ___ ___ _ _____ ___ ___
  6. # / __| __| \| | __| _ \ /_\_ _/ _ \| _ \
  7. # | (_ | _|| .` | _|| / / _ \| || (_) | /
  8. # \___|___|_|\_|___|_|_\/_/ \_\_| \___/|_|_\
  9. #
  10. # Written by Kyle Noll
  11. # in Ruby
  12. #
  13. # This program converts assembly code into machine code based
  14. # on the Nand2Tetris course. Type your assembly code into a file named
  15. # 'assembly.txt' by default in the same directory and run the
  16. # program. The binary output will be placed into a file named 'machine.txt'
  17. # by default in the same directory.
  18.  
  19. # ===================== MAIN =========================
  20.  
  21. input_file = "assembly.txt"
  22. output_file = "machine.txt"
  23.  
  24. # Read assembly.txt
  25. lines = []
  26. File.open(input_file) do |file|
  27. file.each do |line|
  28. lines << line
  29. end
  30. end
  31.  
  32. # First loop: Strip down the code and collect variables
  33. raw_lines = []
  34. jump_points = []
  35. line_number = 0
  36. lines.each do |line|
  37. line.chomp!
  38. line.gsub!(/\s+/, "")
  39. if is_declaring_jump_point?(line)
  40. add_jump_point(line, line_number)
  41. end
  42. if !line.empty? and !full_line_comment?(line) and !is_declaring_jump_point?(line)
  43.  
  44. # Remove comments on lines that also have code
  45. line = line.slice(0..line.index('//')-1) if !!line.index('//')
  46.  
  47. raw_lines << {contents: line, line_number: line_number}
  48. line_number += 1
  49. if is_declaring_variable?(line)
  50. add_variable(line[1..-1])
  51. end
  52. end
  53. end
  54.  
  55. # Second loop: set variable and jump point names to their respective values
  56. machine_lines = []
  57. raw_lines.each_with_index do |line, i|
  58. raw_line = line[:contents]
  59. if is_declaring_variable?(raw_line)
  60. variable_name = raw_line[1..-1]
  61. value = @variables[variable_name]
  62. raw_line = "@#{value}"
  63. end
  64. if is_referring_to_jump_point?(raw_line)
  65. jump_point_name = raw_line[1..-1]
  66. value = @jump_points[jump_point_name]
  67. raw_line = "@#{value}"
  68. end
  69.  
  70. if raw_line[0] == "@"
  71. # a-command
  72. binary = a_command_to_bin(raw_line)
  73. else
  74. # c-command
  75. binary = c_command_to_bin(raw_line)
  76. end
  77.  
  78. # Assemble
  79. machine_lines << binary
  80. end
  81.  
  82. File.open(output_file, 'w+') {|f| f.write(machine_lines)}
  83.  
  84.  
  85. # ================= Methods & Tools ===================
  86.  
  87.  
  88. class String
  89. def is_i?
  90. !!(self =~ /\A[-+]?[0-9]+\z/)
  91. end
  92.  
  93. def is_s?
  94. self[/[a-zA-Z]+/] == self
  95. end
  96. end
  97.  
  98. def get_instruction_type(line)
  99. if line[0] == "@"
  100. return "a"
  101. else
  102. return "c"
  103. end
  104. end
  105.  
  106. def full_line_comment?(line)
  107. line[0,2] == '//'
  108. end
  109.  
  110. def is_upper?(char)
  111. if char.is_s?
  112. return char == char.upcase
  113. else
  114. return false
  115. end
  116. end
  117.  
  118. def is_lower?(char)
  119. if char.is_s?
  120. return char == char.downcase
  121. else
  122. return false
  123. end
  124. end
  125.  
  126. def is_only_letters?(line)
  127. !!line.match(/^[[:alpha:]]+$/)
  128. end
  129.  
  130. def is_declaring_variable?(line)
  131. line[0] == "@" && is_lower?(line[1..-1]) && is_only_letters?(line[1..-1])
  132. end
  133.  
  134. def add_variable(name)
  135. @variable_location_pointer ||= 16
  136. @variables ||= {}
  137. @variables.each do |variable|
  138. if @variables.has_key?(name)
  139. return
  140. end
  141. end
  142. @variables[name] = @variable_location_pointer
  143. @variable_location_pointer += 1
  144. end
  145.  
  146. def is_referring_to_jump_point?(line)
  147. line[0] == "@" && is_upper?(line[1..-1]) && is_only_letters?(line[1..-1])
  148. end
  149.  
  150. def is_declaring_jump_point?(line)
  151. line[0] == "("
  152. end
  153.  
  154. def add_jump_point(line, location)
  155. @jump_points ||= {}
  156. name = line[1..-2]
  157. @jump_points.each do |jump_point|
  158. if @jump_points.has_key?(name)
  159. return
  160. end
  161. end
  162. @jump_points[name] = location
  163. end
  164.  
  165. def dec_to_bin(num)
  166.  
  167. if num > 32768 or num < -32768
  168. raise "Error: Number must be greater than -32,768 and less than 32,768."
  169. end
  170.  
  171. if num < 0
  172. negative = true
  173. num += 1
  174. else
  175. negative = false
  176. end
  177.  
  178. num = num.abs
  179. bin = ""
  180. while num > 0 do
  181. bin.prepend((num % 2).to_s)
  182. num = num / 2
  183. end
  184.  
  185. while bin.length < 15
  186. bin.prepend("0")
  187. end
  188.  
  189. return bin
  190. end
  191.  
  192. def a_command_to_bin(line)
  193. value = line[1..-1]
  194. return "0" + dec_to_bin(value.to_i)
  195. end
  196.  
  197. def c_command_to_bin(line)
  198.  
  199. # Define indices
  200. comp_index = {:"0"=>"0101010", :"1"=>"0111111", :"-1"=>"0111010", :D=>"0001100", :A=>"0110000", :"!D"=>"0001101", :"!A"=>"0110001", :"-D"=>"0001111", :"-A"=>"0110011", :"D+1"=>"0011111", :"A+1"=>"0110111", :"D-1"=>"0001110", :"A-1"=>"0110010", :"D+A"=>"0000010", :"D-A"=>"0010011", :"A-D"=>"0000111", :"D&A"=>"0000000", :"D|A"=>"0010101", :M=>"1110000", :"!M"=>"1110001", :"-M"=>"1110011", :"M+1"=>"1110111", :"M-1"=>"1110010", :"D+M"=>"1000010", :"D-M"=>"1010011", :"M-D"=>"1000111", :"D&M"=>"1000000", :"D|M"=>"1010101"}
  201.  
  202. dest_index = {:M=>"001", :D=>"010", :MD=>"011", :A=>"100", :AM=>"101", :AD=>"110", :AMD=>"111"}
  203.  
  204. jump_index = {:JGT=>"001", :JEQ=>"010", :JGE=>"011", :JLT=>"100", :JNE=>"101", :JLE=>"110", :JMP=>"111"}
  205.  
  206. # Extract dest, comp, and jump
  207. dest, comp, jump = line.match(/([A-Z]+)?=?([A-Z\d\+\-\!\&\|]+);?([A-Z]+)?/).captures
  208.  
  209. # Initialize binary
  210. binary = "111"
  211.  
  212. # comp
  213. binary += comp_index[comp.to_sym]
  214.  
  215. # dest
  216. if dest
  217. binary += dest_index[dest.to_sym]
  218. else
  219. binary += "000"
  220. end
  221.  
  222. # jump
  223. if jump
  224. binary += jump_index[jump.to_sym]
  225. else
  226. binary += "000"
  227. end
  228.  
  229. return binary
  230. end
Add Comment
Please, Sign In to add comment