mirror of
https://github.com/safiire/n65.git
synced 2025-01-20 22:29:50 +00:00
Fixed an issue with referencing outer scoped symbols
This commit is contained in:
parent
411e8b9873
commit
a48d10474e
@ -1,55 +0,0 @@
|
|||||||
; Create an iNES header
|
|
||||||
.ines {"prog": 1, "char": 1, "mapper": 0, "mirror": 1}
|
|
||||||
|
|
||||||
|
|
||||||
; Main code segment
|
|
||||||
.org $C000
|
|
||||||
start:
|
|
||||||
SEI ; disable IRQs
|
|
||||||
CLD ; disable decimal mode
|
|
||||||
LDX #$40
|
|
||||||
STX $4017 ; disable APU frame IRQ
|
|
||||||
LDX #$FF
|
|
||||||
TXS ; Set up stack
|
|
||||||
INX ; now X = 0
|
|
||||||
STX $2000 ; disable NMI
|
|
||||||
STX $2001 ; disable rendering
|
|
||||||
STX $4010 ; disable DMC IRQs
|
|
||||||
|
|
||||||
vblankwait1: ; First wait for vblank to make sure PPU is ready
|
|
||||||
BIT $2002
|
|
||||||
BPL vblankwait1
|
|
||||||
|
|
||||||
clrmem:
|
|
||||||
LDA #$00
|
|
||||||
STA $0000, X
|
|
||||||
STA $0100, X
|
|
||||||
STA $0200, X
|
|
||||||
STA $0400, X
|
|
||||||
STA $0500, X
|
|
||||||
STA $0600, X
|
|
||||||
STA $0700, X
|
|
||||||
LDA #$FE
|
|
||||||
STA $0300, X
|
|
||||||
INX
|
|
||||||
BNE clrmem
|
|
||||||
|
|
||||||
vblankwait2: ; Second wait for vblank, PPU is ready after this
|
|
||||||
BIT $2002
|
|
||||||
BPL vblankwait2
|
|
||||||
|
|
||||||
|
|
||||||
LDA #$60 ;intensify blues
|
|
||||||
STA $2001
|
|
||||||
|
|
||||||
forever:
|
|
||||||
JMP forever ;jump back to Forever, infinite loop
|
|
||||||
|
|
||||||
nmi:
|
|
||||||
RTI
|
|
||||||
|
|
||||||
|
|
||||||
.org $FFFA ;first of the three vectors starts here
|
|
||||||
.dw nmi ;when an NMI happens (once per frame if enabled) the processor will jump to the label NMI:
|
|
||||||
.dw start ;when the processor first turns on or is reset, it will jump to the label RESET:
|
|
||||||
.dw $0 ;external interrupt IRQ is not used in this tutorial
|
|
@ -1,9 +1,9 @@
|
|||||||
; Create an iNES header
|
; Create an iNES header
|
||||||
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 1}
|
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 1}
|
||||||
|
|
||||||
; Here is the start of our code
|
; Here is the start of our code
|
||||||
.org $C000
|
.org $C000
|
||||||
start:
|
.scope main
|
||||||
LDA #$01 ; square 1
|
LDA #$01 ; square 1
|
||||||
STA $4015
|
STA $4015
|
||||||
LDA #$F8 ; period low
|
LDA #$F8 ; period low
|
||||||
@ -12,13 +12,13 @@ start:
|
|||||||
STA $4003
|
STA $4003
|
||||||
LDA #$BF ; volume
|
LDA #$BF ; volume
|
||||||
STA $4000
|
STA $4000
|
||||||
forever:
|
forever:
|
||||||
JMP forever
|
JMP forever
|
||||||
|
|
||||||
nmi:
|
nothing:
|
||||||
RTI
|
RTI
|
||||||
|
|
||||||
.org $FFFA ; Here are the three interrupt vectors
|
.org $FFFA ; Here are the three interrupt vectors
|
||||||
.dw nmi ; VBlank non-maskable interrupt
|
.dw nothing ; VBlank non-maskable interrupt
|
||||||
.dw start ; When the processor is reset or powers on
|
.dw main ; When the processor is reset or powers on
|
||||||
.dw $0 ; External interrupt IRQ
|
.dw nothing ; External interrupt IRQ
|
||||||
|
@ -239,7 +239,7 @@
|
|||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Update the sprite, I don't exactly understand the DMA call yet.
|
; Update the sprite, I don't exactly understand the DMA call yet.
|
||||||
update_sprite:
|
.scope update_sprite
|
||||||
lda #>sprite
|
lda #>sprite
|
||||||
sta nes.sprite.dma ; Jam page $200-$2FF into SPR-RAM, how do we get these numbers?
|
sta nes.sprite.dma ; Jam page $200-$2FF into SPR-RAM, how do we get these numbers?
|
||||||
lda sprite.x
|
lda sprite.x
|
||||||
@ -262,11 +262,12 @@ update_sprite:
|
|||||||
adc dx zp
|
adc dx zp
|
||||||
sta sprite.x
|
sta sprite.x
|
||||||
rts
|
rts
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Read the first controller, and handle input
|
; Read the first controller, and handle input
|
||||||
react_to_input:
|
.scope react_to_input
|
||||||
lda #$01 ; strobe joypad
|
lda #$01 ; strobe joypad
|
||||||
sta nes.controller1
|
sta nes.controller1
|
||||||
lda #$00
|
lda #$00
|
||||||
@ -306,11 +307,12 @@ react_to_input:
|
|||||||
stx sprite.y
|
stx sprite.y
|
||||||
not_dn:
|
not_dn:
|
||||||
rts ; Ignore left and right
|
rts ; Ignore left and right
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; XORing with $ff toggles between 0x1 and 0xfe (-1)
|
; XORing with $ff toggles between 0x1 and 0xfe (-1)
|
||||||
reverse_dx:
|
.scope reverse_dx
|
||||||
lda #$FF
|
lda #$FF
|
||||||
eor dx zp
|
eor dx zp
|
||||||
clc
|
clc
|
||||||
@ -318,30 +320,32 @@ reverse_dx:
|
|||||||
sta dx zp
|
sta dx zp
|
||||||
jsr low_c ; Play the reverse low C note
|
jsr low_c ; Play the reverse low C note
|
||||||
rts
|
rts
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Scroll the screen if we have to
|
; Scroll the screen if we have to
|
||||||
scroll_screen:
|
.scope scroll_screen
|
||||||
ldx #$00 ; Reset VRAM Address to $0000
|
ldx #$00 ; Reset VRAM Address to $0000
|
||||||
stx nes.vram.address
|
stx nes.vram.address
|
||||||
stx nes.vram.address
|
stx nes.vram.address
|
||||||
|
|
||||||
ldx scroll zp ; Do we need to scroll at all?
|
ldx scroll zp ; Do we need to scroll at all?
|
||||||
beq no_scroll
|
beq return
|
||||||
dex
|
dex
|
||||||
stx scroll zp
|
stx scroll zp
|
||||||
lda #$00
|
lda #$00
|
||||||
sta nes.ppu.scroll ; Write 0 for Horiz. Scroll value
|
sta nes.ppu.scroll ; Write 0 for Horiz. Scroll value
|
||||||
stx nes.ppu.scroll ; Write the value of 'scroll' for Vert. Scroll value
|
stx nes.ppu.scroll ; Write the value of 'scroll' for Vert. Scroll value
|
||||||
|
|
||||||
no_scroll:
|
return:
|
||||||
rts
|
rts
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Play a low C note on square 1
|
; Play a low C note on square 1
|
||||||
low_c:
|
.scope low_c
|
||||||
pha
|
pha
|
||||||
lda #$84
|
lda #$84
|
||||||
sta nes.apu.pulse1.control
|
sta nes.apu.pulse1.control
|
||||||
@ -351,11 +355,12 @@ low_c:
|
|||||||
sta nes.apu.pulse1.ct
|
sta nes.apu.pulse1.ct
|
||||||
pla
|
pla
|
||||||
rts
|
rts
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Play a high C note on square 1
|
; Play a high C note on square 1
|
||||||
high_c:
|
.scope high_c
|
||||||
pha
|
pha
|
||||||
lda #$86
|
lda #$86
|
||||||
sta nes.apu.pulse1.control
|
sta nes.apu.pulse1.control
|
||||||
@ -365,15 +370,17 @@ high_c:
|
|||||||
sta nes.apu.pulse1.ct
|
sta nes.apu.pulse1.ct
|
||||||
pla
|
pla
|
||||||
rts
|
rts
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Update everything on every vblank
|
; Update everything on every vblank
|
||||||
vblank:
|
.scope vblank
|
||||||
jsr scroll_screen
|
jsr scroll_screen
|
||||||
jsr update_sprite
|
jsr update_sprite
|
||||||
jsr react_to_input
|
jsr react_to_input
|
||||||
rti
|
rti
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
|
@ -9,13 +9,6 @@
|
|||||||
; Create an iNES header
|
; Create an iNES header
|
||||||
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
||||||
|
|
||||||
;;;;
|
|
||||||
; Here is a good spot to associate zero page memory addresses
|
|
||||||
; with quick access variables in the program.
|
|
||||||
|
|
||||||
.org $0200
|
|
||||||
sprite:
|
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Setup the interrupt vectors
|
; Setup the interrupt vectors
|
||||||
@ -28,7 +21,7 @@ sprite:
|
|||||||
.org $C000
|
.org $C000
|
||||||
;;;;
|
;;;;
|
||||||
; Here is our code entry point, which we'll call main.
|
; Here is our code entry point, which we'll call main.
|
||||||
main:
|
.scope main
|
||||||
; Disable interrupts and decimal flag
|
; Disable interrupts and decimal flag
|
||||||
sei
|
sei
|
||||||
cld
|
cld
|
||||||
@ -85,15 +78,16 @@ main:
|
|||||||
cli
|
cli
|
||||||
forever:
|
forever:
|
||||||
jmp forever
|
jmp forever
|
||||||
|
.
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Update everything on every vblank
|
; Update everything on every vblank
|
||||||
vblank:
|
vblank:
|
||||||
rti
|
rti
|
||||||
|
|
||||||
|
|
||||||
;;;;
|
;;;;
|
||||||
; Don't do anything on IRQ
|
; Don't do anything on IRQ
|
||||||
irq:
|
irq:
|
||||||
rti
|
rti
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
;;;;
|
|
||||||
; Create an iNES header
|
|
||||||
.ines {"prog": 1, "char": 0, "mapper": 0, "mirror": 0}
|
|
||||||
|
|
||||||
;;;;
|
|
||||||
;; Start a prog segment number 0
|
|
||||||
.segment prog 0
|
|
||||||
.org $8000
|
|
||||||
|
|
||||||
.scope main
|
|
||||||
sei
|
|
||||||
cld
|
|
||||||
loop:
|
|
||||||
ldx $00
|
|
||||||
inx
|
|
||||||
stx $00
|
|
||||||
jmp loop
|
|
||||||
.
|
|
||||||
|
|
||||||
vblank:
|
|
||||||
irq:
|
|
||||||
rti
|
|
||||||
|
|
||||||
;;;;
|
|
||||||
;; Vector table
|
|
||||||
.org $FFFA
|
|
||||||
|
|
||||||
.dw vblank
|
|
||||||
.dw main
|
|
||||||
.dw irq
|
|
@ -67,6 +67,23 @@ module Assembler6502
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
## Return an object that contains the assembler's current state
|
||||||
|
def get_current_state
|
||||||
|
saved_program_counter, saved_segment, saved_bank = @program_counter, @current_segment, @current_bank
|
||||||
|
saved_scope = symbol_table.scope_stack.dup
|
||||||
|
OpenStruct.new(program_counter: saved_program_counter, segment: saved_segment, bank: saved_bank, scope: saved_scope)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
## Set the current state from an OpenStruct
|
||||||
|
def set_current_state(struct)
|
||||||
|
@program_counter, @current_segment, @current_bank = struct.program_counter, struct.segment, struct.bank
|
||||||
|
symbol_table.scope_stack = struct.scope.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
####
|
####
|
||||||
## This is the main assemble method, it parses one line into an object
|
## This is the main assemble method, it parses one line into an object
|
||||||
## which when given a reference to this assembler, controls the assembler
|
## which when given a reference to this assembler, controls the assembler
|
||||||
@ -95,6 +112,23 @@ module Assembler6502
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
## This rewinds the state of the assembler, so a promise can be
|
||||||
|
## executed with a previous state, for example if we can't resolve
|
||||||
|
## a symbol right now, and want to try during the second pass
|
||||||
|
def with_saved_state(&block)
|
||||||
|
## Save the current state of the assembler
|
||||||
|
old_state = get_current_state
|
||||||
|
|
||||||
|
lambda do
|
||||||
|
|
||||||
|
## Set the assembler state back to the old state and run the block like that
|
||||||
|
set_current_state(old_state)
|
||||||
|
block.call(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
####
|
####
|
||||||
## Write to memory space. Typically, we are going to want to write
|
## Write to memory space. Typically, we are going to want to write
|
||||||
## to the location of the current PC, current segment, and current bank.
|
## to the location of the current PC, current segment, and current bank.
|
||||||
|
@ -43,18 +43,13 @@ module Assembler6502
|
|||||||
## This is a little complicated, I admit.
|
## This is a little complicated, I admit.
|
||||||
def exec(assembler)
|
def exec(assembler)
|
||||||
|
|
||||||
## Save these current values into the closure
|
promise = assembler.with_saved_state do |saved_assembler|
|
||||||
pc = assembler.program_counter
|
value = saved_assembler.symbol_table.resolve_symbol(@value)
|
||||||
segment = assembler.current_segment
|
|
||||||
bank = assembler.current_bank
|
|
||||||
|
|
||||||
## Create a promise, if this symbol is not defined yet.
|
|
||||||
promise = lambda do
|
|
||||||
value = assembler.symbol_table.resolve_symbol(@value)
|
|
||||||
bytes = [value & 0xFFFF].pack('S').bytes
|
bytes = [value & 0xFFFF].pack('S').bytes
|
||||||
assembler.write_memory(bytes, pc, segment, bank)
|
saved_assembler.write_memory(bytes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
## Try to execute it now, or setup the promise to return
|
## Try to execute it now, or setup the promise to return
|
||||||
case @value
|
case @value
|
||||||
when Fixnum
|
when Fixnum
|
||||||
@ -66,7 +61,7 @@ module Assembler6502
|
|||||||
rescue SymbolTable::UndefinedSymbol
|
rescue SymbolTable::UndefinedSymbol
|
||||||
## Must still advance PC before returning promise, so we'll write
|
## Must still advance PC before returning promise, so we'll write
|
||||||
## a place holder value of 0xDEAD
|
## a place holder value of 0xDEAD
|
||||||
assembler.write_memory([0xDE, 0xAD], pc, segment, bank)
|
assembler.write_memory([0xDE, 0xAD])
|
||||||
return promise
|
return promise
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -147,7 +147,6 @@ module Assembler6502
|
|||||||
if match_array.size == 4
|
if match_array.size == 4
|
||||||
_, op, byte_selector, arg = match_array
|
_, op, byte_selector, arg = match_array
|
||||||
return Instruction.new(op, arg, mode, byte_selector.to_sym)
|
return Instruction.new(op, arg, mode, byte_selector.to_sym)
|
||||||
puts "I found one with #{byte_selector} #{arg}"
|
|
||||||
else
|
else
|
||||||
_, op, arg = match_array
|
_, op, arg = match_array
|
||||||
return Instruction.new(op, arg, mode)
|
return Instruction.new(op, arg, mode)
|
||||||
@ -204,22 +203,16 @@ module Assembler6502
|
|||||||
## promise to resolve it later.
|
## promise to resolve it later.
|
||||||
def exec(assembler)
|
def exec(assembler)
|
||||||
|
|
||||||
## Save these current values into the closure/promise
|
promise = assembler.with_saved_state do |saved_assembler|
|
||||||
pc = assembler.program_counter
|
@arg = saved_assembler.symbol_table.resolve_symbol(@arg)
|
||||||
segment = assembler.current_segment
|
|
||||||
bank = assembler.current_bank
|
|
||||||
|
|
||||||
## Create a promise if this symbol is not defined yet.
|
|
||||||
promise = lambda do
|
|
||||||
@arg = assembler.symbol_table.resolve_symbol(@arg)
|
|
||||||
|
|
||||||
## If the instruction uses a byte selector, we need to apply that.
|
## If the instruction uses a byte selector, we need to apply that.
|
||||||
@arg = apply_byte_selector(@byte_selector, @arg)
|
@arg = apply_byte_selector(@byte_selector, @arg)
|
||||||
|
|
||||||
## If the instruction is relative we need to work out how far away it is
|
## If the instruction is relative we need to work out how far away it is
|
||||||
@arg = @arg - pc - 2 if @mode == :relative
|
@arg = @arg - saved_assembler.program_counter - 2 if @mode == :relative
|
||||||
|
|
||||||
assembler.write_memory(emit_bytes, pc, segment, bank)
|
saved_assembler.write_memory(emit_bytes)
|
||||||
end
|
end
|
||||||
|
|
||||||
case @arg
|
case @arg
|
||||||
@ -234,7 +227,7 @@ module Assembler6502
|
|||||||
placeholder = [@hex, 0xDE, 0xAD][0...@length]
|
placeholder = [@hex, 0xDE, 0xAD][0...@length]
|
||||||
## I still have to write a placeholder instruction of the right
|
## I still have to write a placeholder instruction of the right
|
||||||
## length. The promise will come back and resolve the address.
|
## length. The promise will come back and resolve the address.
|
||||||
assembler.write_memory(placeholder, pc, segment, bank)
|
assembler.write_memory(placeholder)
|
||||||
return promise
|
return promise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
require 'pry-byebug'
|
||||||
|
|
||||||
module Assembler6502
|
module Assembler6502
|
||||||
|
|
||||||
class SymbolTable
|
class SymbolTable
|
||||||
|
attr_accessor :scope_stack
|
||||||
|
|
||||||
##### Custom Exceptions
|
##### Custom Exceptions
|
||||||
class InvalidScope < StandardError; end
|
class InvalidScope < StandardError; end
|
||||||
@ -54,6 +56,7 @@ module Assembler6502
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
=begin
|
||||||
####
|
####
|
||||||
## Resolve symbol to a value, for example:
|
## Resolve symbol to a value, for example:
|
||||||
## scope1.scope2.variable
|
## scope1.scope2.variable
|
||||||
@ -61,7 +64,7 @@ module Assembler6502
|
|||||||
## You can just address anything by name in the current scope
|
## You can just address anything by name in the current scope
|
||||||
## To go backwards in scope you need to write the full path
|
## To go backwards in scope you need to write the full path
|
||||||
## like global.sprite.x or whatever
|
## like global.sprite.x or whatever
|
||||||
def resolve_symbol(name)
|
def resolve_symbol_old(name)
|
||||||
|
|
||||||
value = if name.include?('.')
|
value = if name.include?('.')
|
||||||
path_ary = name.split('.').map(&:to_sym)
|
path_ary = name.split('.').map(&:to_sym)
|
||||||
@ -85,6 +88,57 @@ module Assembler6502
|
|||||||
end
|
end
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
=end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
##
|
||||||
|
def resolve_symbol(name)
|
||||||
|
method = name.include?('.') ? :resolve_symbol_dot_syntax : :resolve_symbol_scoped
|
||||||
|
value = self.send(method, name)
|
||||||
|
|
||||||
|
fail(UndefinedSymbol, name) if value.nil?
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
## Resolve symbol by working backwards through each
|
||||||
|
## containing scope. Similarly named scopes shadow outer scopes
|
||||||
|
def resolve_symbol_scoped(name)
|
||||||
|
root = "-#{name}".to_sym
|
||||||
|
stack = @scope_stack.dup
|
||||||
|
loop do
|
||||||
|
scope = retreive_scope(stack)
|
||||||
|
|
||||||
|
## We see if there is a key either under this name, or root
|
||||||
|
v = scope[name.to_sym] || scope[root]
|
||||||
|
v = v.kind_of?(Hash) ? v[root] : v
|
||||||
|
|
||||||
|
return v unless v.nil?
|
||||||
|
|
||||||
|
## Pop the stack so we can decend to the parent scope, if any
|
||||||
|
stack.pop
|
||||||
|
return nil if stack.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
## Dot syntax means to check an absolute path to the symbol
|
||||||
|
## :global is ignored if it is provided as part of the path
|
||||||
|
def resolve_symbol_dot_syntax(name)
|
||||||
|
path_ary = name.split('.').map(&:to_sym)
|
||||||
|
symbol = path_ary.pop
|
||||||
|
root = "-#{symbol}".to_sym
|
||||||
|
path_ary.shift if path_ary.first == :global
|
||||||
|
|
||||||
|
scope = retreive_scope(path_ary)
|
||||||
|
|
||||||
|
## We see if there is a key either under this name, or root
|
||||||
|
v = scope[symbol]
|
||||||
|
v.kind_of?(Hash) ? v[root] : v
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
####
|
####
|
||||||
|
@ -28,6 +28,34 @@ class TestSymbolTable < MiniTest::Test
|
|||||||
end
|
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
|
## Test exiting a sub scope, and seeing that the variable is unavailable by simple name
|
||||||
def test_exit_scope
|
def test_exit_scope
|
||||||
@ -153,5 +181,58 @@ class TestSymbolTable < MiniTest::Test
|
|||||||
assert_equal(0x8000, assembler.symbol_table.resolve_symbol('global.main'))
|
assert_equal(0x8000, assembler.symbol_table.resolve_symbol('global.main'))
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user