diff --git a/lib/n65/directives/bytes.rb b/lib/n65/directives/bytes.rb index df02ef5..4418101 100644 --- a/lib/n65/directives/bytes.rb +++ b/lib/n65/directives/bytes.rb @@ -46,7 +46,7 @@ module N65 #### - ## Initialize with filename + ## Initialize with a byte array def initialize(bytes_array) @bytes_array = bytes_array end @@ -62,7 +62,7 @@ module N65 when Fixnum byte when String - value = saved_assembler.symbol_table.resolve_symbol(byte) + saved_assembler.symbol_table.resolve_symbol(byte) else fail(InvalidByteValue, byte) end diff --git a/lib/n65/regexes.rb b/lib/n65/regexes.rb index 71da4f6..192fa84 100644 --- a/lib/n65/regexes.rb +++ b/lib/n65/regexes.rb @@ -6,28 +6,28 @@ module N65 ## All the regexes used to parse in one module module Regexes ## Mnemonics - Mnemonic = '([A-Za-z]{3})' - Branches = '(BPL|BMI|BVC|BVS|BCC|BCS|BNE|BEQ|bpl|bmi|bvc|bvs|bcc|bcs|bne|beq)' + Mnemonic = '([A-Za-z]{3})' + Branches = '(BPL|BMI|BVC|BVS|BCC|BCS|BNE|BEQ|bpl|bmi|bvc|bvs|bcc|bcs|bne|beq)' ## Numeric Literals - Hex8 = '\$([A-Fa-f0-9]{1,2})' - Hex16 = '\$([A-Fa-f0-9]{3,4})' + Hex8 = '\$([A-Fa-f0-9]{1,2})' + Hex16 = '\$([A-Fa-f0-9]{3,4})' - Bin8 = '%([01]{1,8})' - Bin16 = '%([01]{9,16})' + Bin8 = '%([01]{1,8})' + Bin16 = '%([01]{9,16})' - Num8 = Regexp.union(Regexp.new(Hex8), Regexp.new(Bin8)).to_s - Num16 = Regexp.union(Regexp.new(Hex16),Regexp.new(Bin16)).to_s + Num8 = Regexp.union(Regexp.new(Hex8), Regexp.new(Bin8)).to_s + Num16 = Regexp.union(Regexp.new(Hex16),Regexp.new(Bin16)).to_s - Immediate = "\##{Num8}" + Immediate = "\##{Num8}" ## Symbols, must begin with a letter, and supports dot syntax - Sym = '([a-zA-Z][a-zA-Z\d_\.]*)' + Sym = '([a-zA-Z][a-zA-Z\d_\.]*(?:[\+\-\*\/]\d+)*)' ## The X or Y register - XReg = '[Xx]' - YReg = '[Yy]' + XReg = '[Xx]' + YReg = '[Yy]' end end diff --git a/lib/n65/symbol_table.rb b/lib/n65/symbol_table.rb index eab55a6..347fccb 100644 --- a/lib/n65/symbol_table.rb +++ b/lib/n65/symbol_table.rb @@ -56,10 +56,30 @@ module N65 #### - ## + ## Separate arithmetic from scope name + def find_arithmetic(name) + last_name = name.split('.').last + md = last_name.match(/([\+\-\*\/])(\d+)$/) + f = lambda{|v| v} + + unless md.nil? + full_match, operator, argument = md.to_a + name.gsub!(full_match, '') + f = lambda {|value| value.send(operator.to_sym, argument.to_i) } + end + + [name, f] + end + + + #### + ## Resolve a symbol to its value def resolve_symbol(name) + name, arithmetic = find_arithmetic(name) + method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped value = self.send(method, name) + value = arithmetic.call(value) fail(UndefinedSymbol, name) if value.nil? value diff --git a/lib/n65/version.rb b/lib/n65/version.rb index af41b19..57f4638 100644 --- a/lib/n65/version.rb +++ b/lib/n65/version.rb @@ -1,3 +1,3 @@ module N65 - VERSION ||= "1.1.0" + VERSION ||= "1.5.0" end diff --git a/test/test_arithmetic_symbols.rb b/test/test_arithmetic_symbols.rb new file mode 100644 index 0000000..6b6aec4 --- /dev/null +++ b/test/test_arithmetic_symbols.rb @@ -0,0 +1,78 @@ +gem 'minitest' +require 'minitest/autorun' +require 'minitest/unit' + +require_relative '../lib/n65.rb' + + +class TestArithmeticSymbols < MiniTest::Test + include N65 + + + def test_identify_plain_symbol + re = Regexp.new(Regexes::Sym) + assert_match(re, 'dog') + assert_match(re, 'animal.dog') + assert_match(re, 'global.animal.dog') + end + + + def test_symbol_values + st = SymbolTable.new + st.define_symbol('variable', 0xff) + assert_equal(0xff, st.resolve_symbol('variable')) + end + + + def test_perform_symbolic_arithmetic + st = SymbolTable.new + st.define_symbol('variable', 0x20) + assert_equal(0x21, st.resolve_symbol('variable+1')) + assert_equal(0x40, st.resolve_symbol('variable*2')) + end + + + def test_symbol_addition + program = <<-ASM + .ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0} + + .org $0020 + .scope struct + .space a 1 + .space b 1 + . + + .org $8000 + .scope main + sei + cld + lda struct+1 zp + lda struct*2 zp + rts + . + ASM + + assembler = Assembler.new + program.split(/\n/).each do |line| + assembler.assemble_one_line(line) + end + assembler.fulfill_promises + + binary = assembler.emit_binary_rom[16...23].split(//).map(&:ord) + + ## So yay, arithmetic on symbols works now :) + correct = [ + 0x78, # sei + 0xd8, # cld + 0xa5, # lda + 0x21, # $20 + 1 + 0xa5, # lda + 0x40, # $20 * 2 + 0x60 # rts + ] + assert_equal(binary, correct) + end + + +end + diff --git a/test/test_symbol_table.rb b/test/test_symbol_table.rb index cbd7c9f..9fbd328 100644 --- a/test/test_symbol_table.rb +++ b/test/test_symbol_table.rb @@ -74,7 +74,7 @@ class TestSymbolTable < MiniTest::Test #### ## Test exiting a sub scope, and being able to access a symbol through a full path - def test_exit_scope + def test_exit_scope_full_path st = SymbolTable.new st.enter_scope('animals') st.define_symbol('dog', 'woof')