Added the ability to do arithmetic on symbolic names :)

This commit is contained in:
Safiire 2017-09-23 01:06:47 -07:00
parent 21af1f0bf6
commit 6b387e20df
6 changed files with 115 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,3 @@
module N65
VERSION ||= "1.1.0"
VERSION ||= "1.5.0"
end

View File

@ -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

View File

@ -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')