n65 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +125 -0
- data/Gemfile +3 -1
- data/README.md +3 -20
- data/Rakefile +15 -1
- data/bin/n65 +2 -0
- data/data/opcodes.yaml +39 -39
- data/everdrive_transfer/everdrive.rb +175 -0
- data/examples/pulse_chord.asm +1 -1
- data/examples/scales.asm +182 -0
- data/lib/n65/directives/ascii.rb +4 -19
- data/lib/n65/directives/bytes.rb +20 -35
- data/lib/n65/directives/dw.rb +22 -36
- data/lib/n65/directives/enter_scope.rb +14 -30
- data/lib/n65/directives/exit_scope.rb +7 -17
- data/lib/n65/directives/inc.rb +14 -30
- data/lib/n65/directives/incbin.rb +6 -24
- data/lib/n65/directives/ines_header.rb +66 -27
- data/lib/n65/directives/label.rb +8 -21
- data/lib/n65/directives/org.rb +9 -19
- data/lib/n65/directives/segment.rb +5 -19
- data/lib/n65/directives/space.rb +6 -18
- data/lib/n65/front_end.rb +36 -39
- data/lib/n65/instruction.rb +123 -159
- data/lib/n65/instruction_base.rb +6 -18
- data/lib/n65/memory_space.rb +51 -71
- data/lib/n65/opcodes.rb +3 -5
- data/lib/n65/parser.rb +20 -38
- data/lib/n65/regexes.rb +20 -21
- data/lib/n65/symbol_table.rb +58 -89
- data/lib/n65/version.rb +3 -1
- data/lib/n65.rb +120 -121
- data/n65.gemspec +17 -12
- data/nes_lib/nes.sym +2 -2
- data/spec/.rubocop.yml +4 -0
- data/spec/assembler_spec.rb +84 -0
- data/spec/lib/n65/memory_space_spec.rb +147 -0
- data/spec/lib/n65/symbol_table_spec.rb +291 -0
- data/utils/opcode_table_to_yaml.rb +65 -67
- data.tar.gz.sig +0 -0
- metadata +84 -41
- metadata.gz.sig +0 -0
- data/examples/music_driver.asm +0 -202
- data/test/test_memory_space.rb +0 -82
- data/test/test_symbol_table.rb +0 -238
- data/utils/midi/Makefile +0 -3
- data/utils/midi/c_scale.mid +0 -0
- data/utils/midi/convert +0 -0
- data/utils/midi/guitar.mid +0 -0
- data/utils/midi/include/event.h +0 -93
- data/utils/midi/include/file.h +0 -57
- data/utils/midi/include/helpers.h +0 -14
- data/utils/midi/include/track.h +0 -45
- data/utils/midi/lil_melody.mid +0 -0
- data/utils/midi/mi_feabhra.mid +0 -0
- data/utils/midi/midi_to_nes.rb +0 -204
- data/utils/midi/source/convert.cpp +0 -16
- data/utils/midi/source/event.cpp +0 -96
- data/utils/midi/source/file.cpp +0 -37
- data/utils/midi/source/helpers.cpp +0 -46
- data/utils/midi/source/track.cpp +0 -37
|
@@ -1,35 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../instruction_base'
|
|
2
4
|
|
|
3
5
|
module N65
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
####
|
|
7
|
-
## This directive to include bytes
|
|
6
|
+
# This directive to include bytes
|
|
8
7
|
class ExitScope < InstructionBase
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
####
|
|
12
|
-
## Try to parse an incbin directive
|
|
13
8
|
def self.parse(line)
|
|
14
9
|
match_data = line.match(/^\.$/)
|
|
15
10
|
return nil if match_data.nil?
|
|
11
|
+
|
|
16
12
|
ExitScope.new
|
|
17
13
|
end
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
####
|
|
21
|
-
## Execute on the assembler
|
|
15
|
+
# Execute on the assembler
|
|
22
16
|
def exec(assembler)
|
|
23
17
|
assembler.symbol_table.exit_scope
|
|
24
18
|
end
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
####
|
|
28
|
-
## Display
|
|
20
|
+
# Display
|
|
29
21
|
def to_s
|
|
30
|
-
|
|
22
|
+
'.'
|
|
31
23
|
end
|
|
32
|
-
|
|
33
24
|
end
|
|
34
|
-
|
|
35
25
|
end
|
data/lib/n65/directives/inc.rb
CHANGED
|
@@ -1,67 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../instruction_base'
|
|
2
4
|
|
|
3
5
|
module N65
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
####
|
|
7
|
-
## This directive instruction can include another asm file
|
|
6
|
+
# This directive instruction can include another asm file
|
|
8
7
|
class Inc < InstructionBase
|
|
8
|
+
SYSTEM_INCLUDE = "#{File.dirname(__FILE__)}/../../../nes_lib"
|
|
9
9
|
|
|
10
|
-
#### System include directory
|
|
11
|
-
SystemInclude = File.dirname(__FILE__) + "/../../../nes_lib"
|
|
12
|
-
|
|
13
|
-
#### Custom Exceptions
|
|
14
10
|
class FileNotFound < StandardError; end
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
####
|
|
18
|
-
## Try to parse an incbin directive
|
|
12
|
+
# Try to parse an incbin directive
|
|
19
13
|
def self.parse(line)
|
|
20
|
-
|
|
14
|
+
# Do We have a system directory include?
|
|
21
15
|
match_data = line.match(/^\.inc <([^>]+)>$/)
|
|
22
16
|
unless match_data.nil?
|
|
23
|
-
filename = File.join(
|
|
17
|
+
filename = File.join(SYSTEM_INCLUDE, match_data[1])
|
|
24
18
|
return Inc.new(filename)
|
|
25
19
|
end
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
# Do We have a project relative directory include?
|
|
28
22
|
match_data = line.match(/^\.inc "([^"]+)"$/)
|
|
29
23
|
unless match_data.nil?
|
|
30
24
|
filename = File.join(Dir.pwd, match_data[1])
|
|
31
25
|
return Inc.new(filename)
|
|
32
26
|
end
|
|
33
27
|
|
|
34
|
-
|
|
28
|
+
# Nope, not an inc directive
|
|
35
29
|
nil
|
|
36
30
|
end
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
####
|
|
40
|
-
## Initialize with filename
|
|
32
|
+
# Initialize with filename
|
|
41
33
|
def initialize(filename)
|
|
42
34
|
@filename = filename
|
|
43
35
|
end
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
####
|
|
47
|
-
## Execute on the assembler
|
|
37
|
+
# Execute on the assembler
|
|
48
38
|
def exec(assembler)
|
|
49
|
-
unless File.
|
|
50
|
-
|
|
51
|
-
end
|
|
39
|
+
raise(FileNotFound, ".inc can't find #{@filename}") unless File.exist?(@filename)
|
|
40
|
+
|
|
52
41
|
File.read(@filename).split(/\n/).each do |line|
|
|
53
42
|
assembler.assemble_one_line(line)
|
|
54
43
|
end
|
|
55
44
|
end
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
####
|
|
59
|
-
## Display
|
|
46
|
+
# Display
|
|
60
47
|
def to_s
|
|
61
48
|
".inc \"#{@filename}\""
|
|
62
49
|
end
|
|
63
|
-
|
|
64
|
-
|
|
65
50
|
end
|
|
66
|
-
|
|
67
51
|
end
|
|
@@ -1,51 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../instruction_base'
|
|
2
4
|
|
|
3
5
|
module N65
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
####
|
|
7
|
-
## This directive instruction can include a binary file
|
|
6
|
+
# This directive instruction can include a binary file
|
|
8
7
|
class IncBin < InstructionBase
|
|
9
|
-
|
|
10
|
-
#### Custom Exceptions
|
|
11
8
|
class FileNotFound < StandardError; end
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
####
|
|
15
|
-
## Try to parse an incbin directive
|
|
16
10
|
def self.parse(line)
|
|
17
11
|
match_data = line.match(/^\.incbin "([^"]+)"$/)
|
|
18
12
|
return nil if match_data.nil?
|
|
13
|
+
|
|
19
14
|
filename = match_data[1]
|
|
20
15
|
IncBin.new(filename)
|
|
21
16
|
end
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
####
|
|
25
|
-
## Initialize with filename
|
|
26
18
|
def initialize(filename)
|
|
27
19
|
@filename = filename
|
|
28
20
|
end
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
####
|
|
32
|
-
## Execute on the assembler
|
|
33
22
|
def exec(assembler)
|
|
34
|
-
unless File.
|
|
35
|
-
|
|
36
|
-
end
|
|
23
|
+
raise(FileNotFound, ".incbin can't find #{@filename}") unless File.exist?(@filename)
|
|
24
|
+
|
|
37
25
|
data = File.read(@filename).unpack('C*')
|
|
38
26
|
assembler.write_memory(data)
|
|
39
27
|
end
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
####
|
|
43
|
-
## Display
|
|
44
29
|
def to_s
|
|
45
30
|
".incbin \"#{@filename}\""
|
|
46
31
|
end
|
|
47
|
-
|
|
48
|
-
|
|
49
32
|
end
|
|
50
|
-
|
|
51
33
|
end
|
|
@@ -1,53 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'json'
|
|
2
4
|
require_relative '../instruction_base'
|
|
3
5
|
|
|
4
6
|
module N65
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
####
|
|
8
|
-
## This directive instruction can setup an ines header
|
|
9
7
|
class INESHeader < InstructionBase
|
|
10
|
-
attr_reader :prog, :char, :mapper, :mirror
|
|
8
|
+
attr_reader :prog, :char, :mapper, :mirror, :battery_backed, :fourscreen_vram, :prog_ram, :tv
|
|
9
|
+
|
|
10
|
+
DEFAULTS = {
|
|
11
|
+
prog: 1,
|
|
12
|
+
char: 0,
|
|
13
|
+
mapper: 0,
|
|
14
|
+
mirror: 0,
|
|
15
|
+
battery_backed: 0,
|
|
16
|
+
fourscreen_vram: 0,
|
|
17
|
+
prog_ram: 0,
|
|
18
|
+
tv: 0
|
|
19
|
+
}.freeze
|
|
11
20
|
|
|
12
|
-
|
|
13
|
-
####
|
|
14
|
-
## Implementation of the parser for this directive
|
|
15
21
|
def self.parse(line)
|
|
16
22
|
match_data = line.match(/^\.ines (.+)$/)
|
|
17
23
|
return nil if match_data.nil?
|
|
18
24
|
|
|
19
25
|
header = JSON.parse(match_data[1])
|
|
20
|
-
|
|
26
|
+
header = header.each_with_object({}) do |(key, val), hash|
|
|
27
|
+
hash[key.to_sym] = val
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
header = DEFAULTS.merge(header)
|
|
31
|
+
|
|
32
|
+
INESHeader.new(
|
|
33
|
+
header[:prog],
|
|
34
|
+
header[:char],
|
|
35
|
+
header[:mapper],
|
|
36
|
+
header[:mirror],
|
|
37
|
+
header[:battery_backed],
|
|
38
|
+
header[:fourscreen_vram],
|
|
39
|
+
header[:prog_ram],
|
|
40
|
+
header[:tv]
|
|
41
|
+
)
|
|
21
42
|
end
|
|
22
43
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@
|
|
44
|
+
# Construct a header
|
|
45
|
+
def initialize(prog, char, mapper, mirror, battery_backed, fourscreen_vram, prog_ram, tv)
|
|
46
|
+
@prog = prog
|
|
47
|
+
@char = char
|
|
48
|
+
@mapper = mapper
|
|
49
|
+
@mirror = mirror
|
|
50
|
+
@battery_backed = battery_backed
|
|
51
|
+
@fourscreen_vram = fourscreen_vram
|
|
52
|
+
@prog_ram = prog_ram
|
|
53
|
+
@tv = tv
|
|
28
54
|
end
|
|
29
55
|
|
|
30
|
-
|
|
31
|
-
####
|
|
32
|
-
## Exec function the assembler will call
|
|
56
|
+
# Exec function the assembler will call
|
|
33
57
|
def exec(assembler)
|
|
34
58
|
assembler.set_ines_header(self)
|
|
35
59
|
end
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
####
|
|
39
|
-
## Emit the header bytes, this is not exactly right, but it works for now.
|
|
61
|
+
# Emit the header bytes
|
|
40
62
|
def emit_bytes
|
|
41
|
-
|
|
63
|
+
mapper_lo_nybble = (@mapper & 0x0f)
|
|
64
|
+
mapper_hi_nybble = (@mapper & 0xf0) >> 4
|
|
65
|
+
|
|
66
|
+
flag6 = 0
|
|
67
|
+
flag6 |= 0x1 if @mirror == 1
|
|
68
|
+
flag6 |= 0x2 if @battery_backed == 1
|
|
69
|
+
flag6 |= 0x8 if @fourscreen_vram == 1
|
|
70
|
+
flag6 |= (mapper_lo_nybble << 4)
|
|
71
|
+
|
|
72
|
+
flag7 = 0
|
|
73
|
+
flag7 |= (mapper_hi_nybble << 4)
|
|
74
|
+
|
|
75
|
+
[0x4E, 0x45, 0x53, 0x1a,
|
|
76
|
+
@prog & 0xff,
|
|
77
|
+
@char & 0xff,
|
|
78
|
+
flag6 & 0xff,
|
|
79
|
+
flag7 & 0xff,
|
|
80
|
+
@prog_ram & 0xff,
|
|
81
|
+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
|
|
42
82
|
end
|
|
43
83
|
|
|
44
|
-
|
|
45
|
-
####
|
|
46
|
-
## Display
|
|
84
|
+
# Display
|
|
47
85
|
def to_s
|
|
48
|
-
".ines {\"prog\": #{@prog}, \"char\": #{@char}, \"mapper\": #{@mapper},
|
|
86
|
+
[".ines {\"prog\": #{@prog}, \"char\": #{@char}, \"mapper\": #{@mapper}, ",
|
|
87
|
+
"\"mirror\": #{@mirror}}, \"battery_backed\": #{@battery_backed}, ",
|
|
88
|
+
"\"fourscreen_vram\": #{@fourscreen_vram}, \"prog_ram\": #{@prog_ram}, ",
|
|
89
|
+
"\"tv\": #{@tv}"].join
|
|
49
90
|
end
|
|
50
|
-
|
|
51
91
|
end
|
|
52
|
-
|
|
53
92
|
end
|
data/lib/n65/directives/label.rb
CHANGED
|
@@ -1,46 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
|
|
2
3
|
module N65
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## an entry in the symbol table associated with
|
|
7
|
-
## the address it appears at.
|
|
4
|
+
# This class represents a label, and will create
|
|
5
|
+
# an entry in the symbol table associated with
|
|
6
|
+
# the address it appears at.
|
|
8
7
|
class Label
|
|
9
|
-
|
|
10
|
-
####
|
|
11
|
-
## Try to parse as a label
|
|
12
8
|
def self.parse(line)
|
|
13
9
|
match_data = line.match(/^([a-zA-Z][a-zA-Z0-9_]+):$/)
|
|
14
10
|
unless match_data.nil?
|
|
15
11
|
label = match_data[1].to_sym
|
|
16
|
-
return
|
|
12
|
+
return new(label)
|
|
17
13
|
end
|
|
18
14
|
nil
|
|
19
15
|
end
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
####
|
|
23
|
-
## Create a new label object
|
|
17
|
+
# Create a new label object
|
|
24
18
|
def initialize(symbol)
|
|
25
19
|
@symbol = symbol
|
|
26
20
|
end
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
####
|
|
30
|
-
## Create an entry in the symbol table for this label
|
|
22
|
+
# Create an entry in the symbol table for this label
|
|
31
23
|
def exec(assembler)
|
|
32
24
|
program_counter = assembler.program_counter
|
|
33
25
|
assembler.symbol_table.define_symbol(@symbol, program_counter)
|
|
34
26
|
end
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
####
|
|
38
|
-
## Display
|
|
28
|
+
# Display
|
|
39
29
|
def to_s
|
|
40
30
|
"#{@symbol}:"
|
|
41
31
|
end
|
|
42
|
-
|
|
43
|
-
|
|
44
32
|
end
|
|
45
|
-
|
|
46
33
|
end
|
data/lib/n65/directives/org.rb
CHANGED
|
@@ -1,47 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../instruction_base'
|
|
2
4
|
|
|
3
5
|
module N65
|
|
4
|
-
|
|
5
|
-
####
|
|
6
|
-
## This is an .org directive
|
|
6
|
+
# This is an .org directive
|
|
7
7
|
class Org < InstructionBase
|
|
8
8
|
attr_reader :address
|
|
9
9
|
|
|
10
|
-
####
|
|
11
|
-
## Try to parse an .org statement
|
|
12
10
|
def self.parse(line)
|
|
13
11
|
match_data = line.match(/^\.org\s+\$([0-9A-Fa-f]{4})$/)
|
|
14
12
|
return nil if match_data.nil?
|
|
13
|
+
|
|
15
14
|
address = match_data[1].to_i(16)
|
|
16
15
|
Org.new(address)
|
|
17
16
|
end
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
####
|
|
21
|
-
## Initialized with address to switch to
|
|
18
|
+
# Initialized with address to switch to
|
|
22
19
|
def initialize(address)
|
|
23
20
|
@address = address
|
|
24
21
|
end
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
####
|
|
28
|
-
## Exec this directive on the assembler
|
|
23
|
+
# Exec this directive on the assembler
|
|
29
24
|
def exec(assembler)
|
|
30
25
|
assembler.program_counter = address
|
|
31
26
|
end
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
####
|
|
35
|
-
## Display
|
|
28
|
+
# Display
|
|
36
29
|
def to_s
|
|
37
30
|
if @address <= 0xff
|
|
38
|
-
|
|
31
|
+
'.org $%2.X' % @address
|
|
39
32
|
else
|
|
40
|
-
|
|
33
|
+
'.org $%4.X' % @address
|
|
41
34
|
end
|
|
42
35
|
end
|
|
43
|
-
|
|
44
36
|
end
|
|
45
|
-
|
|
46
37
|
end
|
|
47
|
-
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
module N65
|
|
3
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
require_relative '../instruction_base'
|
|
4
|
+
module N65
|
|
7
5
|
class Segment < InstructionBase
|
|
8
|
-
|
|
9
|
-
####
|
|
10
|
-
## Try to parse a dw directive
|
|
11
6
|
def self.parse(line)
|
|
12
7
|
match_data = line.match(/^.segment (prog|char) (\d+)$/i)
|
|
13
8
|
unless match_data.nil?
|
|
@@ -17,29 +12,20 @@ module N65
|
|
|
17
12
|
nil
|
|
18
13
|
end
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
####
|
|
22
|
-
## Initialize with filename
|
|
15
|
+
# Initialize with filename
|
|
23
16
|
def initialize(segment, bank)
|
|
24
17
|
@bank = bank
|
|
25
18
|
@segment = segment
|
|
26
19
|
end
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
####
|
|
30
|
-
## Execute the segment and bank change on the assembler
|
|
21
|
+
# Execute the segment and bank change on the assembler
|
|
31
22
|
def exec(assembler)
|
|
32
23
|
assembler.current_segment = @segment
|
|
33
24
|
assembler.current_bank = @bank
|
|
34
25
|
end
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
####
|
|
38
|
-
## Display
|
|
39
27
|
def to_s
|
|
40
28
|
".segment #{@segment} #{@bank}"
|
|
41
29
|
end
|
|
42
|
-
|
|
43
30
|
end
|
|
44
|
-
|
|
45
31
|
end
|
data/lib/n65/directives/space.rb
CHANGED
|
@@ -1,46 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../instruction_base'
|
|
2
4
|
|
|
3
5
|
module N65
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
####
|
|
7
6
|
## This directive gives a symbolic name for memory and creates space for a variable in RAM
|
|
8
7
|
class Space < InstructionBase
|
|
9
|
-
|
|
10
|
-
####
|
|
11
|
-
## Try to parse a .space directive
|
|
12
8
|
def self.parse(line)
|
|
13
9
|
match_data = line.match(/^.space\s+([a-zA-Z]?[a-zA-Z0-9_]+?)\s+([0-9]+)$/)
|
|
14
10
|
return nil if match_data.nil?
|
|
15
|
-
_, name, size = match_data.to_a
|
|
16
11
|
|
|
12
|
+
_, name, size = match_data.to_a
|
|
17
13
|
Space.new(name, size.to_i)
|
|
18
14
|
end
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
####
|
|
22
|
-
## Initialize some memory space with a name
|
|
16
|
+
# Initialize some memory space with a name
|
|
23
17
|
def initialize(name, size)
|
|
24
18
|
@name = name
|
|
25
19
|
@size = size
|
|
26
20
|
end
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
####
|
|
30
|
-
## .space creates a symbol at the current PC, and then advances PC by size
|
|
22
|
+
# .space creates a symbol at the current PC, and then advances PC by size
|
|
31
23
|
def exec(assembler)
|
|
32
24
|
program_counter = assembler.program_counter
|
|
33
25
|
assembler.symbol_table.define_symbol(@name, program_counter)
|
|
34
26
|
assembler.program_counter += @size
|
|
35
27
|
end
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
####
|
|
39
|
-
## Display
|
|
29
|
+
# Display
|
|
40
30
|
def to_s
|
|
41
31
|
".space #{@name} #{@size}"
|
|
42
32
|
end
|
|
43
|
-
|
|
44
33
|
end
|
|
45
|
-
|
|
46
34
|
end
|
data/lib/n65/front_end.rb
CHANGED
|
@@ -1,81 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'optparse'
|
|
2
4
|
require_relative '../n65'
|
|
3
5
|
|
|
4
6
|
module N65
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## This class handles the front end aspects,
|
|
8
|
-
## parsing the commandline options and running the assembler
|
|
7
|
+
# This class handles the front end aspects,
|
|
8
|
+
# parsing the commandline options and running the assembler
|
|
9
9
|
class FrontEnd
|
|
10
|
-
|
|
11
|
-
####
|
|
12
|
-
## Initialize with ARGV commandline
|
|
13
10
|
def initialize(argv)
|
|
14
|
-
@options = {:
|
|
11
|
+
@options = { output_file: nil, write_symbol_table: false, quiet: false, cycle_count: false }
|
|
15
12
|
@argv = argv.dup
|
|
16
13
|
end
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
####
|
|
20
|
-
## Run the assembler
|
|
15
|
+
# Run the assembler
|
|
21
16
|
def run
|
|
22
|
-
## First use the option parser
|
|
23
17
|
parser = create_option_parser
|
|
24
18
|
parser.parse!(@argv)
|
|
25
19
|
|
|
26
|
-
## Whatever is leftover in argv the input files
|
|
27
20
|
if @argv.size.zero?
|
|
28
|
-
|
|
21
|
+
warn('No input files')
|
|
29
22
|
exit(1)
|
|
30
23
|
end
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
# Only can assemble one file at once for now
|
|
33
26
|
if @argv.size != 1
|
|
34
|
-
|
|
27
|
+
warn('Can only assemble one input file at once, but you can use .inc and .incbin directives')
|
|
35
28
|
exit(1)
|
|
36
29
|
end
|
|
37
30
|
|
|
38
31
|
input_file = @argv.shift
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
unless File.
|
|
42
|
-
|
|
33
|
+
# Make sure the input file exists
|
|
34
|
+
unless File.exist?(input_file)
|
|
35
|
+
warn("Input file #{input_file} does not exist")
|
|
43
36
|
exit(1)
|
|
44
37
|
end
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
|
|
39
|
+
# Maybe they didn't provide an output file name, so we'll guess
|
|
47
40
|
if @options[:output_file].nil?
|
|
48
41
|
ext = File.extname(input_file)
|
|
49
|
-
@options[:output_file] = input_file.gsub(ext, '')
|
|
42
|
+
@options[:output_file] = "#{input_file.gsub(ext, '')}.nes"
|
|
50
43
|
end
|
|
51
44
|
|
|
52
45
|
if @options.values.any?(&:nil?)
|
|
53
|
-
|
|
46
|
+
warn('Missing options try --help')
|
|
54
47
|
exit(1)
|
|
55
48
|
end
|
|
56
49
|
|
|
57
|
-
|
|
58
|
-
N65::Assembler.from_file(input_file, @options[:output_file])
|
|
59
|
-
#rescue StandardError => error
|
|
60
|
-
#STDERR.puts("Assemble Failed!")
|
|
61
|
-
#STDERR.puts(error.class)
|
|
62
|
-
#if error.message
|
|
63
|
-
#STDERR.puts(error.message)
|
|
64
|
-
#end
|
|
65
|
-
#exit(1)
|
|
66
|
-
#end
|
|
50
|
+
N65::Assembler.from_file(input_file, @options)
|
|
67
51
|
end
|
|
68
52
|
|
|
69
53
|
private
|
|
70
54
|
|
|
71
|
-
####
|
|
72
|
-
## Create a commandline option parser
|
|
73
55
|
def create_option_parser
|
|
74
56
|
OptionParser.new do |opts|
|
|
75
|
-
opts.banner = "Usage: #{$
|
|
57
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options] <input_file.asm>"
|
|
76
58
|
|
|
77
59
|
opts.on('-o', '--outfile filename', 'outfile') do |output_file|
|
|
78
|
-
@options[:output_file] = output_file
|
|
60
|
+
@options[:output_file] = output_file
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
opts.on('-s', '--symbols', 'Outputs a symbol map') do
|
|
64
|
+
@options[:write_symbol_table] = true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
opts.on('-c', '--cycles', 'Outputs a cycle count yaml document') do
|
|
68
|
+
@options[:cycle_count] = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
opts.on('-q', '--quiet', 'No output on success') do
|
|
72
|
+
@options[:quiet] = true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
opts.on('-v', '--version', 'Displays Version') do
|
|
76
|
+
puts "N65 Assembler Version #{N65::VERSION}"
|
|
77
|
+
exit
|
|
79
78
|
end
|
|
80
79
|
|
|
81
80
|
opts.on('-h', '--help', 'Displays Help') do
|
|
@@ -84,7 +83,5 @@ module N65
|
|
|
84
83
|
end
|
|
85
84
|
end
|
|
86
85
|
end
|
|
87
|
-
|
|
88
86
|
end
|
|
89
|
-
|
|
90
87
|
end
|