Linted all the directive classes

This commit is contained in:
Saf 2020-08-30 13:57:46 -07:00
parent cc53fd1813
commit f364a83db9
13 changed files with 135 additions and 291 deletions

View File

@ -10,6 +10,9 @@ Style/FormatStringToken:
Style/Documentation:
Enabled: false
Lint/MissingSuper:
Enabled: false
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true
@ -34,9 +37,6 @@ Lint/EmptyConditionalBody:
Lint/FloatComparison:
Enabled: true
Lint/MissingSuper:
Enabled: true
Lint/MixedRegexpCaptureTypes:
Enabled: true

View File

@ -1,42 +1,28 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive to include bytes
# This directive to include bytes
class ASCII < InstructionBase
####
## Try to parse a .ascii directive
def self.parse(line)
match_data = line.match(/^\.ascii\s+"([^"]+)"$/)
return nil if match_data.nil?
ASCII.new(match_data[1])
end
####
## Initialize with a string
def initialize(string)
super
@string = string
end
####
## Execute on the assembler
def exec(assembler)
assembler.write_memory(@string.bytes)
end
####
## Display
def to_s
".ascii \"#{@string}\""
end
end
end

View File

@ -1,27 +1,21 @@
# frozen_string_literal: true
require_relative '../instruction_base'
require_relative '../regexes.rb'
require_relative '../regexes'
module N65
####
## This directive to include bytes
# This directive to include bytes
class Bytes < InstructionBase
#### Custom Exceptions
class InvalidByteValue < StandardError; end
####
## Try to parse an incbin directive
# Try to parse an incbin directive
def self.parse(line)
match_data = line.match(/^\.bytes\s+(.+)$/)
return nil if match_data.nil?
bytes_array = match_data[1].split(',').map do |byte_string|
## Does byte_string represent a numeric literal, or is it a symbol?
## In numeric captures $2 is always binary, $1 is always hex
# Does byte_string represent a numeric literal, or is it a symbol?
# In numeric captures $2 is always binary, $1 is always hex
case byte_string.strip
when Regexp.new("^#{Regexes::Num8}$")
@ -30,32 +24,28 @@ module N65
when Regexp.new("^#{Regexes::Num16}$")
value = $2.nil? ? $1.to_i(16) : $2.to_i(2)
## Break value up into two bytes
# Break value up into two bytes
high = (0xff00 & value) >> 8
low = (0x00ff & value)
[low, high]
when Regexp.new("^#{Regexes::Sym}$")
$1
else
fail(InvalidByteValue, byte_string)
raise(InvalidByteValue, byte_string)
end
end.flatten
Bytes.new(bytes_array)
end
####
## Initialize with a byte array
# Initialize with a byte array
def initialize(bytes_array)
super
@bytes_array = bytes_array
end
####
## Execute on the assembler
# Execute on the assembler
def exec(assembler)
promise = assembler.with_saved_state do |saved_assembler|
@bytes_array.map! do |byte|
case byte
@ -64,7 +54,7 @@ module N65
when String
saved_assembler.symbol_table.resolve_symbol(byte)
else
fail(InvalidByteValue, byte)
raise(InvalidByteValue, byte)
end
end
saved_assembler.write_memory(@bytes_array)
@ -73,8 +63,8 @@ module N65
begin
promise.call
rescue SymbolTable::UndefinedSymbol
## Write the bytes but assume a zero page address for all symbols
## And just write 0xDE for a placeholder
# Write the bytes but assume a zero page address for all symbols
# And just write 0xDE for a placeholder
placeholder_bytes = @bytes_array.map do |byte|
case bytes
when Integer
@ -82,21 +72,17 @@ module N65
when String
0xDE
else
fail(InvalidByteValue, byte)
raise(InvalidByteValue, byte)
end
end
assembler.write_memory(placeholder_bytes)
return promise
promise
end
end
####
## Display, I don't want to write all these out
# Display, I don't want to write all these out
def to_s
".bytes (#{@bytes_array.length})"
end
end
end

View File

@ -1,25 +1,21 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive instruction can include a binary file
# This directive instruction can include a binary file
class DW < InstructionBase
####
## Try to parse a dw directive
# Try to parse a dw directive
def self.parse(line)
## Maybe it is a straight up bit of hex
# Maybe it is a straight up bit of hex
match_data = line.match(/^\.dw\s+\$([0-9A-F]{1,4})$/)
unless match_data.nil?
word = match_data[1].to_i(16)
return DW.new(word)
end
## Or maybe it points to a symbol
match_data = line.match(/^\.dw\s+([A-Za-z_][A-Za-z0-9_\.]+)/)
# Or maybe it points to a symbol
match_data = line.match(/^\.dw\s+([A-Za-z_][A-Za-z0-9_.]+)/)
unless match_data.nil?
symbol = match_data[1]
return DW.new(symbol)
@ -27,30 +23,24 @@ module N65
nil
end
####
## Initialize with filename
# Initialize with filename
def initialize(value)
@value = value
end
####
## Execute on the assembler, now in this case value may
## be a symbol that needs to be resolved, if so we return
## a lambda which can be executed later, with the promise
## that that symbol will have then be defined
## This is a little complicated, I admit.
# Execute on the assembler, now in this case value may
# be a symbol that needs to be resolved, if so we return
# a lambda which can be executed later, with the promise
# that that symbol will have then be defined
# This is a little complicated, I admit.
def exec(assembler)
promise = assembler.with_saved_state do |saved_assembler|
value = saved_assembler.symbol_table.resolve_symbol(@value)
bytes = [value & 0xFFFF].pack('S').bytes
saved_assembler.write_memory(bytes)
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
when Integer
bytes = [@value & 0xFFFF].pack('S').bytes
@ -59,28 +49,24 @@ module N65
begin
promise.call
rescue SymbolTable::UndefinedSymbol
## Must still advance PC before returning promise, so we'll write
## a place holder value of 0xDEAD
# Must still advance PC before returning promise, so we'll write
# a place holder value of 0xDEAD
assembler.write_memory([0xDE, 0xAD])
return promise
promise
end
else
fail("Uknown argument in .dw directive")
raise('Uknown argument in .dw directive')
end
end
####
## Display
# Display
def to_s
case @value
when String
".dw #{@value}"
when Fixnum
".dw $%4.X" % @value
when Integer
'.dw $%4.X' % @value
end
end
end
end

View File

@ -1,55 +1,39 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive to include bytes
# This directive to include bytes
class EnterScope < InstructionBase
####
## Try to parse an incbin directive
def self.parse(line)
## Anonymous scope
# Anonymous scope
match_data = line.match(/^\.scope$/)
unless match_data.nil?
return EnterScope.new
end
return EnterScope.new unless match_data.nil?
## Named scope
# Named scope
match_data = line.match(/^\.scope\s+([a-zA-Z][a-zA-Z0-9_]+)$/)
return nil if match_data.nil?
EnterScope.new(match_data[1])
end
####
## Initialize with filename
# Initialize with filename
def initialize(name = nil)
@name = name
end
####
## Execute on the assembler, also create a symbol referring to
## the current pc which contains a hyphen, and is impossible for
## the user to create. This makes a scope simultaneously act as
## a label to the current PC. If someone tries to use a scope
## name as a label, it can return the address when the scope opened.
# Execute on the assembler, also create a symbol referring to
# the current pc which contains a hyphen, and is impossible for
# the user to create. This makes a scope simultaneously act as
# a label to the current PC. If someone tries to use a scope
# name as a label, it can return the address when the scope opened.
def exec(assembler)
assembler.symbol_table.enter_scope(@name)
unless @name.nil?
assembler.symbol_table.define_symbol("-#{@name}", assembler.program_counter)
end
assembler.symbol_table.define_symbol("-#{@name}", assembler.program_counter) unless @name.nil?
end
####
## Display
def to_s
".scope #{@name}"
end
end
end

View File

@ -1,35 +1,25 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive to include bytes
# This directive to include bytes
class ExitScope < InstructionBase
####
## Try to parse an incbin directive
def self.parse(line)
match_data = line.match(/^\.$/)
return nil if match_data.nil?
ExitScope.new
end
####
## Execute on the assembler
# Execute on the assembler
def exec(assembler)
assembler.symbol_table.exit_scope
end
####
## Display
# Display
def to_s
"."
'.'
end
end
end

View File

@ -1,67 +1,51 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive instruction can include another asm file
# This directive instruction can include another asm file
class Inc < InstructionBase
SYSTEM_INCLUDE = "#{File.dirname(__FILE__)}/../../../nes_lib"
#### System include directory
SystemInclude = File.dirname(__FILE__) + "/../../../nes_lib"
#### Custom Exceptions
class FileNotFound < StandardError; end
####
## Try to parse an incbin directive
# Try to parse an incbin directive
def self.parse(line)
## Do We have a system directory include?
# Do We have a system directory include?
match_data = line.match(/^\.inc <([^>]+)>$/)
unless match_data.nil?
filename = File.join(SystemInclude, match_data[1])
filename = File.join(SYSTEM_INCLUDE, match_data[1])
return Inc.new(filename)
end
## Do We have a project relative directory include?
# Do We have a project relative directory include?
match_data = line.match(/^\.inc "([^"]+)"$/)
unless match_data.nil?
filename = File.join(Dir.pwd, match_data[1])
return Inc.new(filename)
end
## Nope, not an inc directive
# Nope, not an inc directive
nil
end
####
## Initialize with filename
# Initialize with filename
def initialize(filename)
@filename = filename
end
####
## Execute on the assembler
# Execute on the assembler
def exec(assembler)
unless File.exists?(@filename)
fail(FileNotFound, ".inc can't find #{@filename}")
end
raise(FileNotFound, ".inc can't find #{@filename}") unless File.exist?(@filename)
File.read(@filename).split(/\n/).each do |line|
assembler.assemble_one_line(line)
end
end
####
## Display
# Display
def to_s
".inc \"#{@filename}\""
end
end
end

View File

@ -1,51 +1,33 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive instruction can include a binary file
# This directive instruction can include a binary file
class IncBin < InstructionBase
#### Custom Exceptions
class FileNotFound < StandardError; end
####
## Try to parse an incbin directive
def self.parse(line)
match_data = line.match(/^\.incbin "([^"]+)"$/)
return nil if match_data.nil?
filename = match_data[1]
IncBin.new(filename)
end
####
## Initialize with filename
def initialize(filename)
@filename = filename
end
####
## Execute on the assembler
def exec(assembler)
unless File.exists?(@filename)
fail(FileNotFound, ".incbin can't find #{@filename}")
end
raise(FileNotFound, ".incbin can't find #{@filename}") unless File.exist?(@filename)
data = File.read(@filename).unpack('C*')
assembler.write_memory(data)
end
####
## Display
def to_s
".incbin \"#{@filename}\""
end
end
end

View File

@ -1,28 +1,33 @@
# frozen_string_literal: true
require 'json'
require_relative '../instruction_base'
module N65
####
## This directive instruction can setup an iNES header
class INESHeader < InstructionBase
attr_reader :prog, :char, :mapper, :mirror, :battery_backed, :fourscreen_vram, :prog_ram, :tv
Defaults = {prog: 1, char: 0, mapper: 0, mirror: 0, battery_backed: 0, fourscreen_vram: 0, prog_ram: 0, tv: 0}
DEFAULTS = {
prog: 1,
char: 0,
mapper: 0,
mirror: 0,
battery_backed: 0,
fourscreen_vram: 0,
prog_ram: 0,
tv: 0
}.freeze
####
## Implementation of the parser for this directive
def self.parse(line)
match_data = line.match(/^\.ines (.+)$/)
return nil if match_data.nil?
header = JSON.parse(match_data[1])
header = header.inject({}) do |hash, (key, val)|
header = header.each_with_object({}) do |(key, val), hash|
hash[key.to_sym] = val
hash
end
header = Defaults.merge(header)
header = DEFAULTS.merge(header)
INESHeader.new(
header[:prog],
@ -32,12 +37,11 @@ module N65
header[:battery_backed],
header[:fourscreen_vram],
header[:prog_ram],
header[:tv])
header[:tv]
)
end
####
## Construct a header
# Construct a header
def initialize(prog, char, mapper, mirror, battery_backed, fourscreen_vram, prog_ram, tv)
@prog = prog
@char = char
@ -49,18 +53,13 @@ module N65
@tv = tv
end
####
## Exec function the assembler will call
# Exec function the assembler will call
def exec(assembler)
assembler.set_ines_header(self)
end
####
## Emit the header bytes
# Emit the header bytes
def emit_bytes
mapper_lo_nybble = (@mapper & 0x0f)
mapper_hi_nybble = (@mapper & 0xf0) >> 4
@ -82,16 +81,12 @@ module N65
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
end
####
## Display
# Display
def to_s
[".ines {\"prog\": #{@prog}, \"char\": #{@char}, \"mapper\": #{@mapper}, ",
"\"mirror\": #{@mirror}}, \"battery_backed\": #{@battery_backed}, ",
"\"fourscreen_vram\": #{@fourscreen_vram}, \"prog_ram\": #{@prog_ram}, ",
"\"tv\": #{@tv}"].join
end
end
end

View File

@ -1,46 +1,33 @@
# frozen_string_literal: true
module N65
####
## This class represents a label, and will create
## an entry in the symbol table associated with
## the address it appears at.
# This class represents a label, and will create
# an entry in the symbol table associated with
# the address it appears at.
class Label
####
## Try to parse as a label
def self.parse(line)
match_data = line.match(/^([a-zA-Z][a-zA-Z0-9_]+):$/)
unless match_data.nil?
label = match_data[1].to_sym
return self.new(label)
return new(label)
end
nil
end
####
## Create a new label object
# Create a new label object
def initialize(symbol)
@symbol = symbol
end
####
## Create an entry in the symbol table for this label
# Create an entry in the symbol table for this label
def exec(assembler)
program_counter = assembler.program_counter
assembler.symbol_table.define_symbol(@symbol, program_counter)
end
####
## Display
# Display
def to_s
"#{@symbol}:"
end
end
end

View File

@ -1,47 +1,37 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This is an .org directive
# This is an .org directive
class Org < InstructionBase
attr_reader :address
####
## Try to parse an .org statement
def self.parse(line)
match_data = line.match(/^\.org\s+\$([0-9A-Fa-f]{4})$/)
return nil if match_data.nil?
address = match_data[1].to_i(16)
Org.new(address)
end
####
## Initialized with address to switch to
# Initialized with address to switch to
def initialize(address)
@address = address
end
####
## Exec this directive on the assembler
# Exec this directive on the assembler
def exec(assembler)
assembler.program_counter = address
end
####
## Display
# Display
def to_s
if @address <= 0xff
".org $%2.X" % @address
'.org $%2.X' % @address
else
".org $%4.X" % @address
'.org $%4.X' % @address
end
end
end
end

View File

@ -1,13 +1,8 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive instruction can include a binary file
class Segment < InstructionBase
####
## Try to parse a dw directive
def self.parse(line)
match_data = line.match(/^.segment (prog|char) (\d+)$/i)
unless match_data.nil?
@ -17,29 +12,20 @@ module N65
nil
end
####
## Initialize with filename
# Initialize with filename
def initialize(segment, bank)
@bank = bank
@segment = segment
end
####
## Execute the segment and bank change on the assembler
# Execute the segment and bank change on the assembler
def exec(assembler)
assembler.current_segment = @segment
assembler.current_bank = @bank
end
####
## Display
def to_s
".segment #{@segment} #{@bank}"
end
end
end

View File

@ -1,46 +1,34 @@
# frozen_string_literal: true
require_relative '../instruction_base'
module N65
####
## This directive gives a symbolic name for memory and creates space for a variable in RAM
class Space < InstructionBase
####
## Try to parse a .space directive
def self.parse(line)
match_data = line.match(/^.space\s+([a-zA-Z]?[a-zA-Z0-9_]+?)\s+([0-9]+)$/)
return nil if match_data.nil?
_, name, size = match_data.to_a
_, name, size = match_data.to_a
Space.new(name, size.to_i)
end
####
## Initialize some memory space with a name
# Initialize some memory space with a name
def initialize(name, size)
@name = name
@size = size
end
####
## .space creates a symbol at the current PC, and then advances PC by size
# .space creates a symbol at the current PC, and then advances PC by size
def exec(assembler)
program_counter = assembler.program_counter
assembler.symbol_table.define_symbol(@name, program_counter)
assembler.program_counter += @size
end
####
## Display
# Display
def to_s
".space #{@name} #{@size}"
end
end
end