1
0
mirror of https://github.com/safiire/n65.git synced 2024-06-10 15:29:53 +00:00

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: Style/Documentation:
Enabled: false Enabled: false
Lint/MissingSuper:
Enabled: false
Layout/EmptyLinesAroundAttributeAccessor: Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true Enabled: true
@ -34,9 +37,6 @@ Lint/EmptyConditionalBody:
Lint/FloatComparison: Lint/FloatComparison:
Enabled: true Enabled: true
Lint/MissingSuper:
Enabled: true
Lint/MixedRegexpCaptureTypes: Lint/MixedRegexpCaptureTypes:
Enabled: true Enabled: true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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