snes_utils 0.1.1 → 0.2.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 +4 -4
- data/bin/png2snes +9 -1
- data/bin/tmx2snes +2 -1
- data/bin/vas +16 -0
- data/lib/mini_assembler/definitions.rb +16 -9
- data/lib/mini_assembler/mini_assembler.rb +236 -88
- data/lib/mini_assembler/spc700/definitions.rb +457 -455
- data/lib/mini_assembler/wdc65816/definitions.rb +61 -59
- data/lib/png2snes/png2snes.rb +3 -2
- data/lib/snes_utils.rb +3 -2
- data/lib/tmx2snes/tmx2snes.rb +2 -7
- data/lib/vas/vas.rb +407 -0
- metadata +5 -16
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SnesUtils
|
2
4
|
module Wdc65816
|
3
5
|
class Definitions
|
@@ -6,70 +8,70 @@ module SnesUtils
|
|
6
8
|
HEX16 = "\\$?(#{HEX_DIGIT}{3,4})"
|
7
9
|
HEX24 = "\\$?(#{HEX_DIGIT}{5,6})"
|
8
10
|
|
9
|
-
SINGLE_OPERAND_INSTRUCTIONS = [
|
10
|
-
DOUBLE_OPERAND_INSTRUCTIONS = [:bm]
|
11
|
-
REL_INSTRUCTIONS = [
|
12
|
-
BIT_INSTRUCTIONS = []
|
11
|
+
SINGLE_OPERAND_INSTRUCTIONS = %i[imm iml imm8 imm16 sr dp dpx dpy idp idx idy idl idly isy abs abx aby abl alx ind iax ial rel rell].freeze
|
12
|
+
DOUBLE_OPERAND_INSTRUCTIONS = [:bm].freeze
|
13
|
+
REL_INSTRUCTIONS = %i[rel rell].freeze
|
14
|
+
BIT_INSTRUCTIONS = [].freeze
|
13
15
|
|
14
16
|
MODES_REGEXES = {
|
15
|
-
acc:
|
16
|
-
imp:
|
17
|
-
imm:
|
18
|
-
iml:
|
19
|
-
imm8:
|
17
|
+
acc: /^$/,
|
18
|
+
imp: /^$/,
|
19
|
+
imm: /^#{HEX8}$/i,
|
20
|
+
iml: /^#{HEX16}$/i,
|
21
|
+
imm8: /^##{HEX8}$/i,
|
20
22
|
imm16: /^##{HEX16}$/i,
|
21
|
-
sr:
|
22
|
-
dp:
|
23
|
-
dpx:
|
24
|
-
dpy:
|
25
|
-
idp:
|
26
|
-
idx:
|
27
|
-
idy:
|
28
|
-
idl:
|
29
|
-
idly:
|
30
|
-
isy:
|
31
|
-
abs:
|
32
|
-
abx:
|
33
|
-
aby:
|
34
|
-
abl:
|
35
|
-
alx:
|
36
|
-
ind:
|
37
|
-
iax:
|
38
|
-
ial:
|
39
|
-
rel:
|
40
|
-
rell:
|
41
|
-
bm:
|
42
|
-
}
|
23
|
+
sr: /^#{HEX8},S$/i,
|
24
|
+
dp: /^#{HEX8}$/i,
|
25
|
+
dpx: /^#{HEX8},X$/i,
|
26
|
+
dpy: /^#{HEX8},Y$/i,
|
27
|
+
idp: /^\(#{HEX8}\)$/i,
|
28
|
+
idx: /^\(#{HEX8},X\)$/i,
|
29
|
+
idy: /^\(#{HEX8}\),Y$/i,
|
30
|
+
idl: /^\[#{HEX8}\]$/i,
|
31
|
+
idly: /^\[#{HEX8}\],Y$/i,
|
32
|
+
isy: /^\(#{HEX8},S\),Y$/i,
|
33
|
+
abs: /^#{HEX16}$/i,
|
34
|
+
abx: /^#{HEX16},X$/i,
|
35
|
+
aby: /^#{HEX16},Y$/i,
|
36
|
+
abl: /^#{HEX24}$/i,
|
37
|
+
alx: /^#{HEX24},X$/i,
|
38
|
+
ind: /^\(#{HEX16}\)$/i,
|
39
|
+
iax: /^\(#{HEX16},X\)$/i,
|
40
|
+
ial: /^\[#{HEX16}\]$/i,
|
41
|
+
rel: /^#{HEX16}$/i,
|
42
|
+
rell: /^#{HEX16}$/i,
|
43
|
+
bm: /^#{HEX8},#{HEX8}$/i
|
44
|
+
}.freeze
|
43
45
|
|
44
46
|
MODES_FORMATS = {
|
45
|
-
acc:
|
46
|
-
imp:
|
47
|
-
imm:
|
48
|
-
iml:
|
49
|
-
imm8:
|
50
|
-
imm16:
|
51
|
-
sr:
|
52
|
-
dp:
|
53
|
-
dpx:
|
54
|
-
dpy:
|
55
|
-
idp:
|
56
|
-
idx:
|
57
|
-
idy:
|
58
|
-
idl:
|
59
|
-
idly:
|
60
|
-
isy:
|
61
|
-
abs:
|
62
|
-
abx:
|
63
|
-
aby:
|
64
|
-
abl:
|
65
|
-
alx:
|
66
|
-
ind:
|
67
|
-
iax:
|
68
|
-
ial:
|
69
|
-
rel:
|
70
|
-
rell:
|
71
|
-
bm:
|
72
|
-
}
|
47
|
+
acc: '%s',
|
48
|
+
imp: '%s',
|
49
|
+
imm: '%s %02X',
|
50
|
+
iml: '%s %02X',
|
51
|
+
imm8: '%s #%02X',
|
52
|
+
imm16: '%s #%04X',
|
53
|
+
sr: '%s %02X,S',
|
54
|
+
dp: '%s %02X',
|
55
|
+
dpx: '%s %02X,X',
|
56
|
+
dpy: '%s %02X,Y',
|
57
|
+
idp: '%s (%02X)',
|
58
|
+
idx: '%s (%02X,X)',
|
59
|
+
idy: '%s (%02X),Y',
|
60
|
+
idl: '%s [%02X]',
|
61
|
+
idly: '%s [%02X],Y',
|
62
|
+
isy: '%s (%02X,S),Y',
|
63
|
+
abs: '%s %04X',
|
64
|
+
abx: '%s %04X,X',
|
65
|
+
aby: '%s %04X,Y',
|
66
|
+
abl: '%s %06X',
|
67
|
+
alx: '%s %06X,X',
|
68
|
+
ind: '%s (%04X)',
|
69
|
+
iax: '%s (%04X,X)',
|
70
|
+
ial: '%s [%04X]',
|
71
|
+
rel: '%s %04X {%s}',
|
72
|
+
rell: '%s %04X {%s}',
|
73
|
+
bm: '%s %02X,%02X'
|
74
|
+
}.freeze
|
73
75
|
|
74
76
|
OPCODES_DATA = [{ opcode: 0x61, mnemonic: 'ADC', mode: :idx, length: 2 },
|
75
77
|
{ opcode: 0x63, mnemonic: 'ADC', mode: :sr, length: 2 },
|
data/lib/png2snes/png2snes.rb
CHANGED
@@ -15,7 +15,7 @@ module SnesUtils
|
|
15
15
|
|
16
16
|
raise ArgumentError, 'Image width and height must be a multiple of sprite size' if (@image.width % @char_size != 0) or (@image.height % @char_size != 0)
|
17
17
|
|
18
|
-
|
18
|
+
unshift_alpha(alpha) if alpha
|
19
19
|
fill_palette
|
20
20
|
end
|
21
21
|
|
@@ -29,7 +29,8 @@ module SnesUtils
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
32
|
+
def unshift_alpha(alpha)
|
33
|
+
@palette.unshift(alpha)
|
33
34
|
end
|
34
35
|
|
35
36
|
def fill_palette
|
data/lib/snes_utils.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'readline'
|
2
2
|
require 'chunky_png'
|
3
3
|
require 'matrix'
|
4
|
-
require '
|
5
|
-
|
4
|
+
require 'csv'
|
6
5
|
require 'byebug'
|
7
6
|
|
8
7
|
require 'mini_assembler/mini_assembler'
|
@@ -12,5 +11,7 @@ require 'mini_assembler/spc700/definitions'
|
|
12
11
|
require 'png2snes/png2snes'
|
13
12
|
require 'tmx2snes/tmx2snes'
|
14
13
|
|
14
|
+
require 'vas/vas'
|
15
|
+
|
15
16
|
module SnesUtils
|
16
17
|
end
|
data/lib/tmx2snes/tmx2snes.rb
CHANGED
@@ -14,14 +14,9 @@ module SnesUtils
|
|
14
14
|
tnm = big_char ? 2 : 1 # big_char : 16x16 tiles. otherwise, 8x8 tiles
|
15
15
|
row_offset = 16 * (tnm - 1) # Skip a row in case of 16x16 tiles ( tile #9 starts at index 32)
|
16
16
|
|
17
|
-
|
18
|
-
csv_node = doc.xpath('//data').children.first.to_s
|
19
|
-
|
20
|
-
csv = csv_node.split("\n").compact.reject { |e| e.empty? }.map { |row| row.split(',') }
|
21
|
-
|
22
|
-
csv.each do |row|
|
17
|
+
CSV.foreach(@file_path) do |row|
|
23
18
|
raise if row.length != 32
|
24
|
-
@tilemap += row.map { |r| (r.to_i
|
19
|
+
@tilemap += row.map { |r| (r.to_i)*tnm + row_offset * ((r.to_i)/8).to_i }
|
25
20
|
end
|
26
21
|
|
27
22
|
raise if @tilemap.length != 32*32
|
data/lib/vas/vas.rb
ADDED
@@ -0,0 +1,407 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module SnesUtils
|
4
|
+
class Vas
|
5
|
+
WDC65816 = :wdc65816
|
6
|
+
SPC700 = :spc700
|
7
|
+
|
8
|
+
DIRECTIVE = [
|
9
|
+
'.65816', '.spc700', '.org', '.base', '.db', '.rb', '.incbin', '.incsrc', '.define'
|
10
|
+
]
|
11
|
+
|
12
|
+
LABEL_OPERATORS = ['@', '!', '<', '>', '\^']
|
13
|
+
|
14
|
+
def initialize(filename)
|
15
|
+
raise "File not found: #{filename}" unless File.file?(filename)
|
16
|
+
|
17
|
+
@filename = filename
|
18
|
+
@file = {}
|
19
|
+
@label_registry = []
|
20
|
+
@define_registry = {}
|
21
|
+
@incbin_list = []
|
22
|
+
@byte_sequence_list = []
|
23
|
+
@memory = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def assemble
|
27
|
+
construct_file
|
28
|
+
|
29
|
+
2.times do |pass|
|
30
|
+
@program_counter = 0
|
31
|
+
@origin = 0
|
32
|
+
@base = 0
|
33
|
+
@cpu = WDC65816
|
34
|
+
|
35
|
+
assemble_file(pass)
|
36
|
+
end
|
37
|
+
|
38
|
+
write_label_registry
|
39
|
+
insert_bytes
|
40
|
+
incbin
|
41
|
+
write
|
42
|
+
end
|
43
|
+
|
44
|
+
def construct_file(filename = @filename)
|
45
|
+
File.open(filename).each_with_index do |raw_line, line_no|
|
46
|
+
line = raw_line.split(';').first.strip.chomp
|
47
|
+
next if line.empty?
|
48
|
+
|
49
|
+
if line.start_with?('.include')
|
50
|
+
directive = line.split(' ')
|
51
|
+
inc_filename = directive[1].to_s.strip.chomp
|
52
|
+
|
53
|
+
construct_file(inc_filename)
|
54
|
+
elsif line.start_with?('.define')
|
55
|
+
args = line.split(' ')
|
56
|
+
key = "#{args[1]}"
|
57
|
+
val = args[2..-1].join(' ').split(';').first.strip.chomp
|
58
|
+
|
59
|
+
raise "Already defined: #{key}" unless @define_registry[key].nil?
|
60
|
+
@define_registry[key] = val
|
61
|
+
else
|
62
|
+
new_line = replace_define(line)
|
63
|
+
|
64
|
+
@file[SecureRandom.uuid] = { line: new_line, orig_line: line, line_no: line_no + 1, filename: filename }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def replace_define(line)
|
71
|
+
found = nil
|
72
|
+
|
73
|
+
@define_registry.keys.each do |key|
|
74
|
+
if line.match(/\b#{key}\b/)
|
75
|
+
found = key
|
76
|
+
break
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
return line if found.nil?
|
82
|
+
|
83
|
+
val = @define_registry[found]
|
84
|
+
|
85
|
+
line.gsub(/\b#{found}/, val)
|
86
|
+
end
|
87
|
+
|
88
|
+
def assemble_file(pass)
|
89
|
+
@file.each do |key, val|
|
90
|
+
@line = val[:line]
|
91
|
+
|
92
|
+
if @line.include?(':')
|
93
|
+
arr = @line.split(':')
|
94
|
+
label = arr[0].strip.chomp
|
95
|
+
unless /^\w+$/ =~ label
|
96
|
+
raise "Invalid label: #{label}"
|
97
|
+
end
|
98
|
+
register_label(label) if pass == 0
|
99
|
+
next unless arr[1]
|
100
|
+
instruction = arr[1].strip.chomp
|
101
|
+
else
|
102
|
+
instruction = @line
|
103
|
+
end
|
104
|
+
|
105
|
+
next if instruction.empty?
|
106
|
+
|
107
|
+
if instruction.start_with?(*DIRECTIVE)
|
108
|
+
process_directive(instruction, pass)
|
109
|
+
next
|
110
|
+
end
|
111
|
+
|
112
|
+
begin
|
113
|
+
bytes = LineAssembler.new(instruction, **options).assemble
|
114
|
+
rescue => e
|
115
|
+
puts "Error at line #{val[:filename]}##{val[:line_no]} - (#{val[:orig_line]}) : #{e}"
|
116
|
+
exit(1)
|
117
|
+
end
|
118
|
+
|
119
|
+
insert(bytes) if pass == 1
|
120
|
+
@program_counter += bytes.size
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def register_label(label)
|
125
|
+
raise "Label already defined: #{label}" if @label_registry.detect { |l| l[0] == label }
|
126
|
+
@label_registry << [label, @program_counter + @origin]
|
127
|
+
end
|
128
|
+
|
129
|
+
def insert(bytes, insert_at = insert_index)
|
130
|
+
@memory[insert_at..insert_at + bytes.size - 1] = bytes
|
131
|
+
end
|
132
|
+
|
133
|
+
def insert_index
|
134
|
+
@program_counter + @base
|
135
|
+
end
|
136
|
+
|
137
|
+
def write(filename = 'out.smc')
|
138
|
+
File.open(filename, 'w+b') do |file|
|
139
|
+
file.write([@memory.map { |i| Vas::hex(i) }.join].pack('H*'))
|
140
|
+
end
|
141
|
+
|
142
|
+
filename
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.hex(num, rjust_len = 2)
|
146
|
+
(num || 0).to_s(16).rjust(rjust_len, '0').upcase
|
147
|
+
end
|
148
|
+
|
149
|
+
def options
|
150
|
+
{
|
151
|
+
program_counter: @program_counter,
|
152
|
+
origin: @origin,
|
153
|
+
cpu: @cpu,
|
154
|
+
label_registry: @label_registry
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def process_directive(instruction, pass)
|
159
|
+
directive = instruction.split(' ')
|
160
|
+
|
161
|
+
case directive[0]
|
162
|
+
when '.65816'
|
163
|
+
@cpu = WDC65816
|
164
|
+
when '.spc700'
|
165
|
+
@cpu = SPC700
|
166
|
+
when '.org'
|
167
|
+
update_origin(directive[1].to_i(16))
|
168
|
+
when '.base'
|
169
|
+
@base = directive[1].to_i(16)
|
170
|
+
when '.incbin'
|
171
|
+
@program_counter += prepare_incbin(directive[1].to_s.strip.chomp, pass)
|
172
|
+
when '.db'
|
173
|
+
raw_line = directive[1..-1].join.to_s.strip.chomp
|
174
|
+
line = LineAssembler.new(raw_line, **options).replace_labels(raw_line)
|
175
|
+
|
176
|
+
@program_counter += define_bytes(line, pass)
|
177
|
+
when '.rb'
|
178
|
+
@program_counter += directive[1].to_i(16)
|
179
|
+
when '.define'
|
180
|
+
# TODO
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def update_origin(param)
|
185
|
+
@origin = param
|
186
|
+
@program_counter = 0
|
187
|
+
|
188
|
+
update_base_from_origin
|
189
|
+
end
|
190
|
+
|
191
|
+
def update_base_from_origin
|
192
|
+
# TODO: automatically update base
|
193
|
+
# lorom/hirom scheme
|
194
|
+
# spc700 scheme
|
195
|
+
end
|
196
|
+
|
197
|
+
def prepare_incbin(filename, pass)
|
198
|
+
raise "Incbin: file not found: #{filename}" unless File.file?(filename)
|
199
|
+
|
200
|
+
@incbin_list << [filename, insert_index] if pass == 0
|
201
|
+
File.size(filename) || 0
|
202
|
+
end
|
203
|
+
|
204
|
+
def incbin
|
205
|
+
@incbin_list.each do |filename, index|
|
206
|
+
file = File.open(filename)
|
207
|
+
bytes = file.each_byte.to_a
|
208
|
+
@line = filename
|
209
|
+
insert(bytes, index)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def define_bytes(raw_bytes, pass)
|
214
|
+
bytes = raw_bytes.split(',').map { |rb| rb.scan(/.{2}/).reverse }.flatten.map do |b|
|
215
|
+
bv = b.to_i(16)
|
216
|
+
raise "Invalid byte: #{b} : #{@line}" if bv < 0 || bv > 0xff
|
217
|
+
bv
|
218
|
+
end
|
219
|
+
|
220
|
+
@byte_sequence_list << [bytes, insert_index] if pass == 0
|
221
|
+
bytes.size
|
222
|
+
end
|
223
|
+
|
224
|
+
def insert_bytes
|
225
|
+
@byte_sequence_list.each do |bytes, index|
|
226
|
+
insert(bytes, index)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def write_label_registry
|
231
|
+
longest = @label_registry.map{|r| r[0] }.max_by(&:length)
|
232
|
+
|
233
|
+
File.open('labels.txt', 'w+b') do |file|
|
234
|
+
@label_registry.each do |label|
|
235
|
+
adjusted_label = label[0].ljust(longest.length, ' ')
|
236
|
+
raw_address = Vas::hex(label[1], 6)
|
237
|
+
address = "#{raw_address[0..1]}/#{raw_address[2..-1]}"
|
238
|
+
file.write "#{adjusted_label} #{address}\n"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class LineAssembler
|
245
|
+
def initialize(raw_line, **options)
|
246
|
+
@line = raw_line.split(';').first.strip.chomp
|
247
|
+
@current_address = (options[:program_counter] + options[:origin])
|
248
|
+
@cpu = options[:cpu]
|
249
|
+
@label_registry = options[:label_registry]
|
250
|
+
end
|
251
|
+
|
252
|
+
def assemble
|
253
|
+
instruction = @line.split(' ')
|
254
|
+
mnemonic = instruction[0].upcase
|
255
|
+
raw_operand = instruction[1].to_s
|
256
|
+
|
257
|
+
raw_operand = replace_label(raw_operand)
|
258
|
+
|
259
|
+
opcode_data = detect_opcode(mnemonic, raw_operand)
|
260
|
+
raise "Invalid syntax" unless opcode_data
|
261
|
+
|
262
|
+
opcode = opcode_data[:opcode]
|
263
|
+
@mode = opcode_data[:mode]
|
264
|
+
@length = opcode_data[:length]
|
265
|
+
|
266
|
+
operand_data = detect_operand(raw_operand)
|
267
|
+
|
268
|
+
operand = process_operand(operand_data)
|
269
|
+
|
270
|
+
return [opcode, *operand]
|
271
|
+
end
|
272
|
+
|
273
|
+
def contains_label?(operand)
|
274
|
+
Vas::LABEL_OPERATORS.any? { |s| operand.include?(s[-1,1]) }
|
275
|
+
end
|
276
|
+
|
277
|
+
def replace_labels(operand)
|
278
|
+
while contains_label?(operand)
|
279
|
+
operand = replace_label(operand)
|
280
|
+
end
|
281
|
+
|
282
|
+
operand
|
283
|
+
end
|
284
|
+
|
285
|
+
def replace_label(operand)
|
286
|
+
return operand unless contains_label?(operand)
|
287
|
+
|
288
|
+
unless matches = /(#{Vas::LABEL_OPERATORS.join('|')})(\w+)(\+(\d+))?/.match(operand)
|
289
|
+
raise "Invalid label syntax: #{operand}"
|
290
|
+
end
|
291
|
+
|
292
|
+
mode = matches[1]
|
293
|
+
label = matches[2]
|
294
|
+
offset = matches[4].to_i
|
295
|
+
|
296
|
+
label_data = @label_registry.detect { |l| l[0] == label }
|
297
|
+
|
298
|
+
value = label_data ? label_data[1] : @current_address
|
299
|
+
|
300
|
+
value += offset
|
301
|
+
|
302
|
+
case mode
|
303
|
+
when '@'
|
304
|
+
value = value & 0x00ffff
|
305
|
+
new_value = Vas::hex(value, 4)
|
306
|
+
when '!'
|
307
|
+
value = value | (((@current_address >> 16) & 0xff) << 16)
|
308
|
+
new_value = Vas::hex(value, 6)
|
309
|
+
when '<'
|
310
|
+
value = value & 0x0000ff
|
311
|
+
new_value = Vas::hex(value)
|
312
|
+
when '>'
|
313
|
+
value = (value & 0x00ff00) >> 8
|
314
|
+
new_value = Vas::hex(value)
|
315
|
+
when '^'
|
316
|
+
mode = '\^'
|
317
|
+
value = (value & 0xff0000) >> 16
|
318
|
+
new_value = Vas::hex(value)
|
319
|
+
else
|
320
|
+
raise "Mode error: #{mode}"
|
321
|
+
end
|
322
|
+
|
323
|
+
operand.gsub(/(#{mode})\w+(\+(\d+))?/, new_value)
|
324
|
+
end
|
325
|
+
|
326
|
+
def detect_opcode(mnemonic, operand)
|
327
|
+
SnesUtils.const_get(@cpu.capitalize)::Definitions::OPCODES_DATA.detect do |row|
|
328
|
+
mode = row[:mode]
|
329
|
+
regex = SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[mode]
|
330
|
+
row[:mnemonic] == mnemonic && regex =~ operand
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def detect_operand(raw_operand)
|
335
|
+
SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[@mode].match(raw_operand)
|
336
|
+
end
|
337
|
+
|
338
|
+
def process_operand(operand_data)
|
339
|
+
if double_operand_instruction?
|
340
|
+
process_double_operand_instruction(operand_data)
|
341
|
+
else
|
342
|
+
operand = operand_data[1]&.to_i(16)
|
343
|
+
rel_instruction? ? process_rel_operand(operand) : little_endian(operand, @length - 1)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def process_double_operand_instruction(operand_data)
|
348
|
+
if bit_instruction?
|
349
|
+
process_bit_operand(operand_data)
|
350
|
+
else
|
351
|
+
operands = [operand_data[1], operand_data[2]].map { |o| o.to_i(16) }
|
352
|
+
operand_2 = rel_instruction? ? process_rel_operand(operands[1]) : operands[1]
|
353
|
+
|
354
|
+
rel_instruction? ? [operands[0], operand_2] : [operand_2, operands[0]]
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def process_bit_operand(operand_data)
|
359
|
+
m = operand_data[1].to_i(16)
|
360
|
+
raise "Out of range: m > 0x1fff: #{m}" if m > 0x1fff
|
361
|
+
|
362
|
+
b = operand_data[2].to_i(16)
|
363
|
+
raise "Out of range: b > 7: #{b}" if b > 7
|
364
|
+
|
365
|
+
little_endian(m << 3 | b, 2)
|
366
|
+
end
|
367
|
+
|
368
|
+
def process_rel_operand(operand)
|
369
|
+
relative_addr = operand - (@current_address & 0x00ffff) - @length
|
370
|
+
|
371
|
+
if @cpu == Vas::WDC65816 && @mode == :rell
|
372
|
+
raise "Relative address out of range: #{relative_addr}" if relative_addr < -32_768 || relative_addr > 32_767
|
373
|
+
|
374
|
+
relative_addr += 0x10000 if relative_addr < 0
|
375
|
+
little_endian(relative_addr, 2)
|
376
|
+
else
|
377
|
+
raise "Relative address out of range: #{relative_addr}" if relative_addr < -128 || relative_addr > 127
|
378
|
+
|
379
|
+
relative_addr += 0x100 if relative_addr < 0
|
380
|
+
relative_addr
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
def little_endian(operand, length)
|
386
|
+
if length > 2
|
387
|
+
[((operand >> 0) & 0xff), ((operand >> 8) & 0xff), ((operand >> 16) & 0xff)]
|
388
|
+
elsif length > 1
|
389
|
+
[((operand >> 0) & 0xff), ((operand >> 8) & 0xff)]
|
390
|
+
else
|
391
|
+
operand
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def double_operand_instruction?
|
396
|
+
SnesUtils.const_get(@cpu.capitalize)::Definitions::DOUBLE_OPERAND_INSTRUCTIONS.include?(@mode)
|
397
|
+
end
|
398
|
+
|
399
|
+
def bit_instruction?
|
400
|
+
SnesUtils.const_get(@cpu.capitalize)::Definitions::BIT_INSTRUCTIONS.include?(@mode)
|
401
|
+
end
|
402
|
+
|
403
|
+
def rel_instruction?
|
404
|
+
SnesUtils.const_get(@cpu.capitalize)::Definitions::REL_INSTRUCTIONS.include?(@mode)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|