mirror of
https://github.com/safiire/n65.git
synced 2024-12-13 06:29:16 +00:00
239 lines
6.2 KiB
Ruby
239 lines
6.2 KiB
Ruby
gem 'minitest'
|
|
require 'minitest/autorun'
|
|
require 'minitest/unit'
|
|
|
|
require_relative '../lib/symbol_table.rb'
|
|
require_relative '../lib/assembler.rb'
|
|
|
|
|
|
class TestSymbolTable < MiniTest::Test
|
|
include Assembler6502
|
|
|
|
####
|
|
## Test that we can make simple global symbols
|
|
def test_define_global_symbols
|
|
st = SymbolTable.new
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Test entering into a sub scope, and setting and retrieving values
|
|
def test_enter_scope
|
|
st = SymbolTable.new
|
|
st.enter_scope('animals')
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Access something from an outer scope without dot syntax
|
|
def test_outer_scope
|
|
st = SymbolTable.new
|
|
st.enter_scope('outer')
|
|
st.define_symbol('dog', 'woof')
|
|
st.enter_scope('inner')
|
|
st.define_symbol('pig', 'oink')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Access something from an outer scope without dot syntax
|
|
def test_shadow
|
|
st = SymbolTable.new
|
|
st.enter_scope('outer')
|
|
st.define_symbol('dog', 'woof')
|
|
st.enter_scope('inner')
|
|
st.define_symbol('dog', 'bark')
|
|
assert_equal('bark', st.resolve_symbol('dog'))
|
|
assert_equal('woof', st.resolve_symbol('outer.dog'))
|
|
st.exit_scope
|
|
st.exit_scope
|
|
assert_equal('bark', st.resolve_symbol('outer.inner.dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Test exiting a sub scope, and seeing that the variable is unavailable by simple name
|
|
def test_exit_scope
|
|
st = SymbolTable.new
|
|
st.enter_scope('animals')
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
|
|
st.exit_scope
|
|
|
|
assert_raises(SymbolTable::UndefinedSymbol) do
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
end
|
|
end
|
|
|
|
|
|
####
|
|
## Test exiting a sub scope, and being able to access a symbol through a full path
|
|
def test_exit_scope
|
|
st = SymbolTable.new
|
|
st.enter_scope('animals')
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
|
|
st.exit_scope
|
|
|
|
assert_equal('woof', st.resolve_symbol('animals.dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Have two symbols that are the same but are in different scopes
|
|
def test_two_scopes_same_symbol
|
|
st = SymbolTable.new
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
|
|
st.enter_scope('animals')
|
|
|
|
st.define_symbol('dog', 'woofwoof')
|
|
assert_equal('woofwoof', st.resolve_symbol('dog'))
|
|
|
|
st.exit_scope
|
|
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
assert_equal('woofwoof', st.resolve_symbol('animals.dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## How do you get stuff out of the global scope when you are in
|
|
## a sub scope?
|
|
def test_access_global_scope
|
|
st = SymbolTable.new
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
|
|
st.enter_scope('animals')
|
|
st.define_symbol('pig', 'oink')
|
|
assert_equal('oink', st.resolve_symbol('pig'))
|
|
|
|
## Ok, now I want to access global.dog basically from the previous scope
|
|
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Now I want to just test making an anonymous scope
|
|
def test_anonymous_scope
|
|
st = SymbolTable.new
|
|
st.define_symbol('dog', 'woof')
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
|
|
st.enter_scope
|
|
st.define_symbol('pig', 'oink')
|
|
assert_equal('oink', st.resolve_symbol('pig'))
|
|
|
|
## Ok, now I want to access global.dog basically from the previous scope
|
|
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
|
|
## Now exit the anonymous scope and get dog
|
|
st.exit_scope
|
|
assert_equal('woof', st.resolve_symbol('global.dog'))
|
|
assert_equal('woof', st.resolve_symbol('dog'))
|
|
end
|
|
|
|
|
|
####
|
|
## Now I want to test that I cannot exist the outer-most
|
|
## global scope by mistake
|
|
def test_cant_exit_global
|
|
st = SymbolTable.new
|
|
assert_raises(SymbolTable::CantExitScope) do
|
|
st.exit_scope
|
|
end
|
|
end
|
|
|
|
|
|
####
|
|
## I would like the name of the scope to take on the
|
|
## value of the program counter at that location.
|
|
def test_scope_as_symbol
|
|
program = <<-ASM
|
|
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
|
.org $8000
|
|
.segment prog 0
|
|
.scope main
|
|
sei
|
|
cld
|
|
lda \#$00
|
|
jmp main
|
|
.
|
|
jmp main
|
|
jmp global.main
|
|
ASM
|
|
|
|
#### There really should be an evaluate string method
|
|
assembler = Assembler.new
|
|
program.split(/\n/).each do |line|
|
|
assembler.assemble_one_line(line)
|
|
end
|
|
assembler.fulfill_promises
|
|
assert_equal(0x8000, assembler.symbol_table.resolve_symbol('global.main'))
|
|
end
|
|
|
|
|
|
####
|
|
## Fix a bug where we can't see a forward declared symbol in a scope
|
|
def test_foward_declaration_in_scope
|
|
program = <<-ASM
|
|
;;;;
|
|
; Create an iNES header
|
|
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
|
|
|
;;;;
|
|
; Try to expose a problem we have with scopes
|
|
; We don't seem to be able to branch to a forward
|
|
; declared symbol within a scope
|
|
.org $8000
|
|
.scope main
|
|
sei
|
|
cld
|
|
lda #\$00
|
|
bne forward_symbol
|
|
nop
|
|
nop
|
|
nop
|
|
forward_symbol:
|
|
rts
|
|
.
|
|
ASM
|
|
|
|
#### There really should be an evaluate string method
|
|
assembler = Assembler.new
|
|
program.split(/\n/).each do |line|
|
|
assembler.assemble_one_line(line)
|
|
end
|
|
puts YAML.dump(assembler.symbol_table)
|
|
assembler.fulfill_promises
|
|
|
|
#### The forward symbol should have been resolved to +3, and the ROM should look like this:
|
|
correct_rom = [0x4e, 0x45, 0x53, 0x1a, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x78, # SEI
|
|
0xd8, # CLD
|
|
0xa9, 0x0, # LDA immediate 0
|
|
0xd0, 0x3, # BNE +3
|
|
0xea, # NOP
|
|
0xea, # NOP
|
|
0xea, # NOP
|
|
0x60 # RTS forward_symbol
|
|
]
|
|
|
|
#### Grab the first 26 bytes of the rom and make sure they assemble to the above
|
|
emitted_rom = assembler.emit_binary_rom.bytes[0...26]
|
|
assert_equal(correct_rom, emitted_rom)
|
|
#### Yup it is fixed now.
|
|
end
|
|
|
|
end
|
|
|