snes_utils 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/png2snes +20 -1
- data/bin/tmx2snes +2 -1
- data/bin/vas +17 -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/superfx/definitions.rb +158 -0
- data/lib/mini_assembler/wdc65816/definitions.rb +61 -59
- data/lib/png2snes/png2snes.rb +53 -21
- data/lib/snes_utils.rb +4 -4
- data/lib/tmx2snes/tmx2snes.rb +2 -7
- data/lib/vas/vas.rb +609 -0
- data/spec/mini_assembler_spec.rb +136 -0
- data/spec/spc700_spec.rb +322 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/vas_spec.rb +5 -0
- data/spec/wdc65816_spec.rb +322 -0
- metadata +21 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bce5ff7aea0b674c5c19db5c83979f66b4993e9b0de36a19c432cc7bcb09745
|
4
|
+
data.tar.gz: 78b040ccc9a686496535c135e3908ab63bfcd9c2c4fece2761a77f5588f49a9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfd37be40699f3a075c5b5c1e1779bb8fe4aed85da84d1f64be3a325d427416c070df8677d3f054d63edc63339080c467211feb903006878726fc8d66c84fee8
|
7
|
+
data.tar.gz: 44d97b051f1b75b94e3f823ec41efdbd08100a24674fb0255e3e40130bf2418506b6bd31035813ae31ff0253c2874155ed5fa40caf10806bd21ff4f745f32e4d
|
data/bin/png2snes
CHANGED
@@ -6,10 +6,29 @@ require 'snes_utils'
|
|
6
6
|
options = {}
|
7
7
|
OptionParser.new do |opts|
|
8
8
|
opts.on('-f', '--file FILENAME', 'PNG source file') { |o| options[:filename] = o }
|
9
|
+
opts.on('-b', '--bpp BPP', 'BPP') { |o| options[:bpp] = o }
|
10
|
+
opts.on('-a', '--alpha ALPHA', 'ALPHA') { |o| options[:alpha] = o }
|
11
|
+
opts.on('-m', '--mode7', 'mode 7') { |o| options[:mode7] = o }
|
12
|
+
opts.on('-o', '--offset OFFSET', 'mode 7 palette offset') { |o| options[:m7_palette_offset] = o }
|
9
13
|
end.parse!
|
10
14
|
|
11
15
|
raise OptionParser::MissingArgument, 'Must specify PNG source file' if options[:filename].nil?
|
12
16
|
|
13
|
-
|
17
|
+
if options[:alpha]
|
18
|
+
alpha = options[:alpha].to_i(16)
|
19
|
+
else
|
20
|
+
alpha = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
if options[:m7_palette_offset]
|
24
|
+
m7_palette_offset = options[:m7_palette_offset].to_i
|
25
|
+
else
|
26
|
+
m7_palette_offset = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
c = SnesUtils::Png2Snes.new(options[:filename], bpp: options[:bpp].to_i,
|
30
|
+
alpha: alpha,
|
31
|
+
mode7: options[:mode7],
|
32
|
+
m7_palette_offset: m7_palette_offset)
|
14
33
|
c.write_palette
|
15
34
|
c.write_image
|
data/bin/tmx2snes
CHANGED
@@ -7,11 +7,12 @@ options = {}
|
|
7
7
|
OptionParser.new do |opts|
|
8
8
|
opts.on('-f', '--file FILENAME', 'TMX source file') { |o| options[:filename] = o }
|
9
9
|
opts.on('-s', '--tile-size TILESIZE', '8 or 16') { |o| options[:tile_size] = o.to_i }
|
10
|
+
opts.on('-p', '--palette PALETTE', 'palette no. (0..7)') { |o| options[:palette] = o.to_i }
|
10
11
|
end.parse!
|
11
12
|
|
12
13
|
raise OptionParser::MissingArgument, 'Must specify TMX source file' if options[:filename].nil?
|
13
14
|
raise 'Wrong size : must either be 8 or 16' unless [8, 16].include? options[:tile_size]
|
14
15
|
|
15
|
-
t = SnesUtils::Tmx2Snes.new options[:filename], big_char: options[:tile_size] == 16
|
16
|
+
t = SnesUtils::Tmx2Snes.new options[:filename], big_char: options[:tile_size] == 16, palette: options[:palette]
|
16
17
|
|
17
18
|
t.write
|
data/bin/vas
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'snes_utils'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
opts.on("-h", "--help", "Prints this help") do
|
9
|
+
puts opts
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
opts.on('-f', '--file FILENAME', 'ASM file') { |o| options[:filename] = o }
|
14
|
+
opts.on("-o", "--out FILENAME", "output file") { |o| options[:outfile] = o }
|
15
|
+
end.parse!
|
16
|
+
|
17
|
+
SnesUtils::Vas.new(options[:filename], options[:outfile]).assemble
|
@@ -1,15 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SnesUtils
|
2
4
|
class Definitions
|
3
5
|
HEX_DIGIT = '[0-9a-f]'
|
4
6
|
|
5
|
-
BYTE_LOC_REGEX = /^#{HEX_DIGIT}{1,4}$/i
|
6
|
-
BYTE_RANGE_REGEX = /^(#{HEX_DIGIT}{1,4})\.+(#{HEX_DIGIT}{1,4})$/i
|
7
|
-
BYTE_SEQUENCE_REGEX = /^(#{HEX_DIGIT}{1,4}):\s*([0-9a-f ]+)$/i
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
BYTE_LOC_REGEX = /^#{HEX_DIGIT}{1,4}$/i.freeze
|
8
|
+
BYTE_RANGE_REGEX = /^(#{HEX_DIGIT}{1,4})\.+(#{HEX_DIGIT}{1,4})$/i.freeze
|
9
|
+
BYTE_SEQUENCE_REGEX = /^(#{HEX_DIGIT}{1,4}):\s*([0-9a-f ]+)$/i.freeze
|
10
|
+
READ_BYTE_SEQUENCE_REGEX = /^(.*):\s*\.db\s+([0-9a-f ]+)$/i.freeze
|
11
|
+
DISASSEMBLE_REGEX = /^(#{HEX_DIGIT}{,4})l/i.freeze
|
12
|
+
SWITCH_BANK_REGEX = %r{^(#{HEX_DIGIT}{1,2})/$}i.freeze
|
13
|
+
FLIP_MX_REG_REGEX = /^([01])=([xm])$/i.freeze
|
14
|
+
WRITE_REGEX = /^\.write\s*(.*)$/i.freeze
|
15
|
+
INCBIN_REGEX = /^(#{HEX_DIGIT}{1,4}):\s*\.incbin\s+(.*)$/i.freeze
|
16
|
+
READ_INCBIN_REGEX = /^(.*):\s*\.incbin\s+(.*)$/i.freeze
|
17
|
+
READ_INCSRC_REGEX = /^\s*\.incsrc\s+(.*)$/i.freeze
|
18
|
+
READ_REGEX = /^((#{HEX_DIGIT}{1,4}):\s*)*\.read\s+(.*)$/i.freeze
|
19
|
+
READ_BANK_SWITCH = /^\.bank\s+(#{HEX_DIGIT}{1,2})$/i.freeze
|
20
|
+
READ_ADDR_SWITCH = /^\.addr\s+(#{HEX_DIGIT}{1,4})$/i.freeze
|
14
21
|
end
|
15
22
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SnesUtils
|
2
4
|
Readline.completion_proc = proc do |input|
|
3
5
|
Wdc65816::Definitions::OPCODES_DATA.map { |row| row[:mnemonic] }
|
4
|
-
|
6
|
+
.select { |mnemonic| mnemonic.upcase.start_with?(input.upcase) }
|
5
7
|
end
|
6
8
|
|
7
9
|
class MiniAssembler
|
@@ -13,7 +15,8 @@ module SnesUtils
|
|
13
15
|
@memory = []
|
14
16
|
end
|
15
17
|
|
16
|
-
@cpu = :wdc65816
|
18
|
+
@cpu = :wdc65816 # :spc700
|
19
|
+
@mem_map = :lorom # :hirom
|
17
20
|
|
18
21
|
@normal_mode = true
|
19
22
|
|
@@ -21,10 +24,9 @@ module SnesUtils
|
|
21
24
|
@current_bank_no = 0
|
22
25
|
@accumulator_flag = 1
|
23
26
|
@index_flag = 1
|
27
|
+
@label_registry = {}
|
24
28
|
|
25
29
|
@next_addr_to_list = 0
|
26
|
-
|
27
|
-
@label_registry = {}
|
28
30
|
end
|
29
31
|
|
30
32
|
def run
|
@@ -44,7 +46,7 @@ module SnesUtils
|
|
44
46
|
filename = filename.empty? ? 'out.smc' : filename
|
45
47
|
|
46
48
|
File.open(filename, 'w+b') do |file|
|
47
|
-
file.write([@memory.map { |i| i
|
49
|
+
file.write([@memory.map { |i| i || '00' }.join].pack('H*'))
|
48
50
|
end
|
49
51
|
|
50
52
|
filename
|
@@ -67,15 +69,14 @@ module SnesUtils
|
|
67
69
|
res = read(filename)
|
68
70
|
write(outfile)
|
69
71
|
|
70
|
-
|
72
|
+
res
|
71
73
|
end
|
72
74
|
|
73
75
|
def read(filename, start_addr = nil)
|
74
76
|
return 0 unless File.file?(filename)
|
75
77
|
|
76
|
-
@label_registry = {}
|
77
|
-
|
78
78
|
current_addr = start_addr || @current_addr
|
79
|
+
current_bank_no = @current_bank_no
|
79
80
|
instructions = []
|
80
81
|
raw_bytes = []
|
81
82
|
incbin_files = []
|
@@ -84,6 +85,8 @@ module SnesUtils
|
|
84
85
|
|
85
86
|
2.times do |i|
|
86
87
|
@current_addr = current_addr
|
88
|
+
@current_bank_no = current_bank_no
|
89
|
+
|
87
90
|
instructions = []
|
88
91
|
File.open(filename).each_with_index do |raw_line, line_no|
|
89
92
|
line = raw_line.split(';').first.strip.chomp
|
@@ -95,42 +98,81 @@ module SnesUtils
|
|
95
98
|
elsif line == '.65816'
|
96
99
|
@cpu = :wdc65816
|
97
100
|
next
|
101
|
+
elsif line == '.lorom'
|
102
|
+
@mem_map = :lorom
|
103
|
+
next
|
104
|
+
elsif line == '.hirom'
|
105
|
+
@mem_map = :hirom
|
106
|
+
next
|
98
107
|
end
|
99
108
|
|
100
|
-
if matches = Definitions::
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
109
|
+
if matches = Definitions::READ_BYTE_SEQUENCE_REGEX.match(line)
|
110
|
+
raw_addr = matches[1]
|
111
|
+
addr = if raw_addr.start_with?('%')
|
112
|
+
parse_address(line, true)
|
113
|
+
else
|
114
|
+
full_address(matches[1].to_i(16))
|
115
|
+
end
|
116
|
+
bytes = matches[2].delete(' ').scan(/.{1,2}/).map { |b| hex(b.to_i(16)) }
|
106
117
|
|
118
|
+
raw_bytes << [addr, bytes] if i == 1
|
119
|
+
|
120
|
+
inc_addr(@current_addr, bytes.size)
|
121
|
+
next
|
122
|
+
elsif matches = Definitions::READ_INCBIN_REGEX.match(line)
|
123
|
+
raw_addr = matches[1]
|
124
|
+
addr = if raw_addr.start_with?('%')
|
125
|
+
parse_address(line, true)
|
126
|
+
else
|
127
|
+
full_address(matches[1].to_i(16))
|
128
|
+
end
|
129
|
+
target_filename = matches[2].strip.chomp
|
130
|
+
incbin_files << [addr, target_filename] if i == 1
|
131
|
+
|
132
|
+
inc_addr(@current_addr, File.size(target_filename))
|
107
133
|
next
|
108
|
-
elsif matches = Definitions::
|
109
|
-
|
110
|
-
|
111
|
-
|
134
|
+
elsif matches = Definitions::READ_INCSRC_REGEX.match(line)
|
135
|
+
target_filename = matches[1].strip.chomp
|
136
|
+
incsrc_res = read(target_filename)
|
137
|
+
puts "incsrc: #{target_filename}, #{incsrc_res}" if i == 1
|
138
|
+
next
|
139
|
+
elsif matches = Definitions::READ_BANK_SWITCH.match(line)
|
140
|
+
new_bank_no = matches[1].to_i(16)
|
141
|
+
max_bank_no = @mem_map == :hirom ? 0x3f : 0x7f
|
142
|
+
return "Error at line #{line_no + 1}" if new_bank_no > max_bank_no
|
112
143
|
|
113
|
-
|
114
|
-
|
144
|
+
@current_bank_no = new_bank_no
|
145
|
+
@current_addr = 0
|
146
|
+
|
147
|
+
next
|
148
|
+
elsif matches = Definitions::READ_ADDR_SWITCH.match(line)
|
149
|
+
new_addr = matches[1].to_i(16)
|
150
|
+
return "Error at line #{line_no + 1}" if new_addr > 0xffff
|
151
|
+
|
152
|
+
@current_addr = new_addr
|
115
153
|
|
116
154
|
next
|
117
155
|
end
|
118
156
|
|
119
|
-
instruction, length, address = parse_instruction(line, register_label=(i == 0), resolve_label=(i==1))
|
157
|
+
instruction, length, address = parse_instruction(line, register_label = (i == 0), resolve_label = (i == 1))
|
120
158
|
return "Error at line #{line_no + 1}" unless instruction
|
121
159
|
|
122
|
-
instructions << [instruction, length, address]
|
123
|
-
|
160
|
+
instructions << [instruction, length, full_address(address)]
|
161
|
+
bank_wrap = inc_addr(address, length)
|
162
|
+
if bank_wrap && (i == 0)
|
163
|
+
puts "Warning: bank wrap at line #{line_no + 1}"
|
164
|
+
end
|
124
165
|
end
|
125
166
|
end
|
126
167
|
|
127
168
|
@cpu = cpu
|
128
169
|
|
129
170
|
total_bytes_read = 0
|
171
|
+
@current_bank_no = current_bank_no
|
130
172
|
|
131
173
|
instructions.map do |instruction_arr|
|
132
174
|
instruction, length, address = instruction_arr
|
133
|
-
total_bytes_read += replace_memory_range(address, address+length-1, instruction)
|
175
|
+
total_bytes_read += replace_memory_range(address, address + length - 1, instruction)
|
134
176
|
end
|
135
177
|
|
136
178
|
raw_bytes.each do |raw_byte|
|
@@ -143,7 +185,22 @@ module SnesUtils
|
|
143
185
|
total_bytes_read += incbin(filename, addr)
|
144
186
|
end
|
145
187
|
|
146
|
-
|
188
|
+
dump_label_registry
|
189
|
+
|
190
|
+
"Read #{total_bytes_read} bytes"
|
191
|
+
end
|
192
|
+
|
193
|
+
def dump_label_registry
|
194
|
+
dump = ['label,snes addr,rom addr']
|
195
|
+
@label_registry.each do |k, v|
|
196
|
+
next if k.start_with?('@')
|
197
|
+
|
198
|
+
dump << "#{k},#{address_human(v[:mapped_addr], v[:mapped_bank])},#{hex(v[:rom_address], 6)}"
|
199
|
+
end
|
200
|
+
|
201
|
+
open('labels.csv', 'w') do |f|
|
202
|
+
f << dump.join("\n")
|
203
|
+
end
|
147
204
|
end
|
148
205
|
|
149
206
|
def detect_opcode_data_from_mnemonic(mnemonic, operand)
|
@@ -172,14 +229,26 @@ module SnesUtils
|
|
172
229
|
end
|
173
230
|
end
|
174
231
|
|
175
|
-
def full_address(address)
|
176
|
-
(
|
232
|
+
def full_address(address, bank_no = @current_bank_no)
|
233
|
+
(bank_no << 16) | address
|
234
|
+
end
|
235
|
+
|
236
|
+
def inc_addr(address, length)
|
237
|
+
@current_addr = address + length
|
238
|
+
initial_bank_no = @current_bank_no
|
239
|
+
|
240
|
+
while @current_addr > 0xffff
|
241
|
+
@current_addr -= 0x10000
|
242
|
+
@current_bank_no += 1
|
243
|
+
end
|
244
|
+
|
245
|
+
@current_bank_no != initial_bank_no
|
177
246
|
end
|
178
247
|
|
179
|
-
def address_human(addr=nil)
|
180
|
-
address = full_address(addr || @current_addr)
|
248
|
+
def address_human(addr = nil, cur_bank = @current_bank_no)
|
249
|
+
address = full_address(addr || @current_addr, cur_bank)
|
181
250
|
bank = address >> 16
|
182
|
-
addr = (((address>>8)&0xFF) << 8) | (address&0xFF)
|
251
|
+
addr = (((address >> 8) & 0xFF) << 8) | (address & 0xFF)
|
183
252
|
"#{hex(bank)}/#{hex(addr, 4)}"
|
184
253
|
end
|
185
254
|
|
@@ -191,7 +260,9 @@ module SnesUtils
|
|
191
260
|
start_full_addr = full_address(start_addr)
|
192
261
|
end_full_addr = full_address(end_addr)
|
193
262
|
|
194
|
-
|
263
|
+
if start_full_addr > end_full_addr || start_full_addr >= @memory.length
|
264
|
+
return []
|
265
|
+
end
|
195
266
|
|
196
267
|
@memory[start_full_addr..end_full_addr]
|
197
268
|
end
|
@@ -214,62 +285,62 @@ module SnesUtils
|
|
214
285
|
if @normal_mode
|
215
286
|
if line == '!'
|
216
287
|
@normal_mode = false
|
217
|
-
|
288
|
+
nil
|
218
289
|
elsif line == '.spc700'
|
219
290
|
@cpu = :spc700
|
220
|
-
|
291
|
+
'spc700'
|
221
292
|
elsif line == '.65816'
|
222
293
|
@cpu = :wdc65816
|
223
|
-
|
294
|
+
'65816'
|
224
295
|
elsif matches = Definitions::WRITE_REGEX.match(line)
|
225
296
|
filename = matches[1].strip.chomp
|
226
297
|
out_filename = write(filename)
|
227
|
-
|
298
|
+
"Written #{@memory.size} bytes to file #{out_filename}"
|
228
299
|
elsif matches = Definitions::READ_REGEX.match(line)
|
229
300
|
start_addr = matches[2]&.to_i(16)
|
230
301
|
filename = matches[3].strip.chomp
|
231
302
|
|
232
|
-
|
303
|
+
read(filename, start_addr)
|
233
304
|
elsif matches = Definitions::INCBIN_REGEX.match(line)
|
234
305
|
start_addr = matches[1].to_i(16)
|
235
306
|
filename = matches[2].strip.chomp
|
236
307
|
nb_bytes = incbin(filename, start_addr)
|
237
308
|
|
238
|
-
|
309
|
+
"Inserted #{nb_bytes} bytes at #{address_human(start_addr)}"
|
239
310
|
elsif Definitions::BYTE_LOC_REGEX =~ line
|
240
|
-
|
311
|
+
memory_loc(line.to_i(16))
|
241
312
|
elsif matches = Definitions::BYTE_RANGE_REGEX.match(line)
|
242
313
|
start_addr = matches[1].to_i(16)
|
243
314
|
end_addr = matches[2].to_i(16)
|
244
315
|
|
245
316
|
padding_count = start_addr % 8
|
246
|
-
padding = (1..padding_count).map { |
|
317
|
+
padding = (1..padding_count).map { |_b| ' ' }
|
247
318
|
arr = memory_range(start_addr, end_addr)
|
248
319
|
return if arr.empty?
|
249
320
|
|
250
|
-
padded_arr = arr.insert(8-padding_count, *padding).each_slice(8).to_a
|
251
|
-
|
252
|
-
if idx == 0
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
321
|
+
padded_arr = arr.insert(8 - padding_count, *padding).each_slice(8).to_a
|
322
|
+
padded_arr.each_with_index.map do |row, idx|
|
323
|
+
line_addr = if idx == 0
|
324
|
+
start_addr
|
325
|
+
else
|
326
|
+
start_addr - padding_count + idx * 8
|
327
|
+
end
|
257
328
|
["#{address_human(line_addr)}-", *row].join(' ')
|
258
329
|
end.join("\n")
|
259
330
|
elsif matches = Definitions::BYTE_SEQUENCE_REGEX.match(line)
|
260
331
|
addr = matches[1].to_i(16)
|
261
332
|
bytes = matches[2].delete(' ').scan(/.{1,2}/).map { |b| hex(b.to_i(16)) }
|
262
333
|
replace_memory_range(addr, addr + bytes.length - 1, bytes)
|
263
|
-
|
334
|
+
nil
|
264
335
|
elsif matches = Definitions::DISASSEMBLE_REGEX.match(line)
|
265
336
|
start = matches[1].empty? ? @next_addr_to_list : matches[1].to_i(16)
|
266
|
-
|
337
|
+
disassemble_range(start, 20).join("\n")
|
267
338
|
elsif matches = Definitions::SWITCH_BANK_REGEX.match(line)
|
268
339
|
target_bank_no = matches[1].to_i(16)
|
269
340
|
@current_bank_no = target_bank_no
|
270
341
|
@current_addr = @current_bank_no << 16
|
271
342
|
@next_addr_to_list = 0
|
272
|
-
|
343
|
+
nil
|
273
344
|
elsif matches = Definitions::FLIP_MX_REG_REGEX.match(line)
|
274
345
|
val = matches[1]
|
275
346
|
reg = matches[2]
|
@@ -280,59 +351,132 @@ module SnesUtils
|
|
280
351
|
@index_flag = val.to_i
|
281
352
|
end
|
282
353
|
|
283
|
-
|
354
|
+
nil
|
284
355
|
end
|
285
356
|
else
|
286
357
|
if line == ''
|
287
358
|
@normal_mode = true
|
288
|
-
|
359
|
+
nil
|
289
360
|
else
|
290
361
|
instruction, length, address = parse_instruction(line)
|
291
362
|
return 'error' unless instruction
|
292
363
|
|
293
|
-
replace_memory_range(address, address+length-1, instruction)
|
294
|
-
|
295
|
-
|
364
|
+
replace_memory_range(address, address + length - 1, instruction)
|
365
|
+
inc_addr(address, length)
|
366
|
+
disassemble_range(address, 1, length > 2).join
|
296
367
|
end
|
297
368
|
end
|
298
369
|
end
|
299
370
|
|
300
|
-
def
|
371
|
+
def mapped_address(address, absolute = false)
|
372
|
+
return hex(address, 4) unless absolute
|
373
|
+
|
374
|
+
if @mem_map == :lorom
|
375
|
+
bank_offset = address / 0x8000
|
376
|
+
mapped_addr = address - (bank_offset * 0x8000)
|
377
|
+
mapped_addr += 0x8000 if mapped_addr < 0x8000
|
378
|
+
mapped_bank = @current_bank_no * 2
|
379
|
+
mapped_bank += bank_offset
|
380
|
+
mapped_bank += 0x80 if mapped_bank < 0x7f
|
381
|
+
|
382
|
+
{ mapped_bank: mapped_bank, mapped_addr: mapped_addr, rom_address: full_address(address) }
|
383
|
+
elsif @mem_map == :hirom
|
384
|
+
mapped_bank = @current_bank_no + 0xc0 if @current_bank_no < 0x3f
|
385
|
+
{ mapped_bank: mapped_bank, mapped_addr: address, rom_address: full_address(address) }
|
386
|
+
else
|
387
|
+
{}
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def detect_label_type(address)
|
392
|
+
address = address[1..-1] if address.start_with?('#')
|
393
|
+
if address.start_with?('@')
|
394
|
+
:relative
|
395
|
+
elsif address.start_with?('%')
|
396
|
+
:absolute16
|
397
|
+
elsif address.start_with?('&')
|
398
|
+
:absolute24
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def contains_label?(op)
|
403
|
+
op.include?('@') | op.include?('%') | op.include?('&')
|
404
|
+
end
|
405
|
+
|
406
|
+
def parse_address(line, _register_label = false)
|
301
407
|
return @current_addr if line.index(':').nil?
|
302
408
|
|
303
409
|
address = line.split(':').first.strip.chomp
|
304
|
-
return address.to_i(16)
|
410
|
+
return -1 if address.to_i(16) > 0xffff
|
411
|
+
return address.to_i(16) if detect_label_type(address).nil?
|
412
|
+
|
413
|
+
label_type = detect_label_type(address)
|
414
|
+
case label_type
|
415
|
+
when :relative
|
416
|
+
@label_registry[address] = mapped_address(@current_addr)
|
417
|
+
when :absolute16
|
418
|
+
@label_registry[address[1..-1]] = mapped_address(@current_addr, true)
|
419
|
+
when :absolute24
|
420
|
+
@label_registry[address[1..-1]] = mapped_address(@current_addr, true)
|
421
|
+
else
|
422
|
+
op
|
423
|
+
end
|
305
424
|
|
306
|
-
@
|
307
|
-
return @current_addr
|
425
|
+
@current_addr
|
308
426
|
end
|
309
427
|
|
310
428
|
def parse_instruction(line, register_label = false, resolve_label = false)
|
311
429
|
current_address = parse_address(line, register_label)
|
430
|
+
return if current_address < 0 || current_address > 0xffff
|
431
|
+
|
312
432
|
instruction = line.split(':').last.split(' ')
|
313
433
|
mnemonic = instruction[0].upcase
|
314
434
|
raw_operand = instruction[1].to_s
|
315
435
|
|
316
|
-
if register_label
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
436
|
+
if register_label && contains_label?(raw_operand)
|
437
|
+
raw_operand = raw_operand.split(',').map do |op|
|
438
|
+
label_type = detect_label_type(op)
|
439
|
+
case label_type
|
440
|
+
when :relative
|
441
|
+
mapped_address(@current_addr)
|
442
|
+
when :absolute16
|
443
|
+
dummy = mapped_address(@current_addr, true)
|
444
|
+
dummy[:mapped_addr].to_s(16)
|
445
|
+
when :absolute24
|
446
|
+
dummy = mapped_address(@current_addr, true)
|
447
|
+
full_address(dummy[:mapped_addr], dummy[:mapped_bank]).to_s(16)
|
448
|
+
else
|
449
|
+
op
|
321
450
|
end
|
322
|
-
end
|
323
|
-
|
324
|
-
raw_operand = raw_operands.join(',')
|
451
|
+
end.join(',')
|
325
452
|
end
|
326
453
|
|
327
|
-
if resolve_label
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
454
|
+
if resolve_label && contains_label?(raw_operand)
|
455
|
+
raw_operand = raw_operand.split(',').map do |op|
|
456
|
+
label_type = detect_label_type(op)
|
457
|
+
case label_type
|
458
|
+
when :relative
|
459
|
+
if op.start_with?('#')
|
460
|
+
@label_registry[op[1..-1]]
|
461
|
+
else
|
462
|
+
@label_registry[op]
|
463
|
+
end
|
464
|
+
when :absolute16
|
465
|
+
if op.start_with?('#')
|
466
|
+
"##{@label_registry[op[2..-1]][:mapped_addr].to_s(16)}"
|
467
|
+
else
|
468
|
+
@label_registry[op[1..-1]][:mapped_addr].to_s(16)
|
469
|
+
end
|
470
|
+
when :absolute24
|
471
|
+
if op.start_with?('#')
|
472
|
+
"##{full_address(@label_registry[op[2..-1]][:mapped_addr], @label_registry[op[1..-1]][:mapped_bank]).to_s(16)}"
|
473
|
+
else
|
474
|
+
full_address(@label_registry[op[1..-1]][:mapped_addr], @label_registry[op[1..-1]][:mapped_bank]).to_s(16)
|
475
|
+
end
|
476
|
+
else
|
477
|
+
op
|
332
478
|
end
|
333
|
-
end
|
334
|
-
|
335
|
-
raw_operand = raw_operands.join(',')
|
479
|
+
end.join(',')
|
336
480
|
end
|
337
481
|
|
338
482
|
opcode_data = detect_opcode_data_from_mnemonic(mnemonic, raw_operand)
|
@@ -349,10 +493,11 @@ module SnesUtils
|
|
349
493
|
if SnesUtils.const_get(@cpu.capitalize)::Definitions::BIT_INSTRUCTIONS.include?(mode)
|
350
494
|
m = operand_matches[1].to_i(16)
|
351
495
|
return if m > 0x1fff
|
496
|
+
|
352
497
|
b = operand_matches[2].to_i(16)
|
353
498
|
return if b > 7
|
354
499
|
|
355
|
-
operand = m << 3 |
|
500
|
+
operand = m << 3 | b
|
356
501
|
else
|
357
502
|
if SnesUtils.const_get(@cpu.capitalize)::Definitions::REL_INSTRUCTIONS.include?(mode)
|
358
503
|
operand = [operand_matches[1], operand_matches[2]].map { |o| o.to_i(16) }
|
@@ -369,7 +514,7 @@ module SnesUtils
|
|
369
514
|
if SnesUtils.const_get(@cpu.capitalize)::Definitions::DOUBLE_OPERAND_INSTRUCTIONS.include?(mode)
|
370
515
|
relative_addr = operand[1] - current_address - length
|
371
516
|
|
372
|
-
return if
|
517
|
+
return if relative_addr < -128 || relative_addr > 127
|
373
518
|
|
374
519
|
relative_addr = 0x100 + relative_addr if relative_addr < 0
|
375
520
|
param_bytes = "#{hex(operand[0])}#{hex(relative_addr)}"
|
@@ -377,29 +522,31 @@ module SnesUtils
|
|
377
522
|
relative_addr = operand - current_address - length
|
378
523
|
|
379
524
|
if @cpu == :wdc65816 && mode == :rell
|
380
|
-
return if
|
525
|
+
return if relative_addr < -32_768 || relative_addr > 32_767
|
381
526
|
else
|
382
|
-
return if
|
527
|
+
return if relative_addr < -128 || relative_addr > 127
|
383
528
|
end
|
384
529
|
|
385
|
-
|
386
|
-
|
530
|
+
if relative_addr < 0
|
531
|
+
relative_addr = (2**(8 * (length - 1))) + relative_addr
|
532
|
+
end
|
533
|
+
param_bytes = hex(relative_addr, 2 * (length - 1)).scan(/.{2}/).reverse.join
|
387
534
|
end
|
388
535
|
else
|
389
|
-
param_bytes = hex(operand, 2*(length-1)).scan(/.{2}/).reverse.join
|
536
|
+
param_bytes = hex(operand, 2 * (length - 1)).scan(/.{2}/).reverse.join
|
390
537
|
end
|
391
538
|
end
|
392
539
|
|
393
540
|
encoded_result = "#{opcode}#{param_bytes}"
|
394
541
|
|
395
|
-
|
542
|
+
[encoded_result.scan(/.{2}/), length, current_address]
|
396
543
|
end
|
397
544
|
|
398
545
|
def auto_update_flags(opcode, operand)
|
399
|
-
if
|
546
|
+
if opcode == 0xc2
|
400
547
|
@index_flag = 0 if (operand & 0x10) == 0x10
|
401
548
|
@accumulator_flag = 0 if (operand & 0x20) == 0x20
|
402
|
-
elsif
|
549
|
+
elsif opcode == 0xe2
|
403
550
|
@index_flag = 1 if (operand & 0x10) == 0x10
|
404
551
|
@accumulator_flag = 1 if (operand & 0x20) == 0x20
|
405
552
|
end
|
@@ -411,6 +558,7 @@ module SnesUtils
|
|
411
558
|
count.times do
|
412
559
|
byte = memory_loc(next_idx)
|
413
560
|
break unless byte
|
561
|
+
|
414
562
|
opcode = byte.to_i(16)
|
415
563
|
|
416
564
|
opcode_data = detect_opcode_data_from_opcode(opcode, force_length)
|
@@ -421,9 +569,9 @@ module SnesUtils
|
|
421
569
|
|
422
570
|
format = SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_FORMATS[mode]
|
423
571
|
|
424
|
-
operand = memory_range(next_idx+1, next_idx+length-1).reverse.join.to_i(16)
|
572
|
+
operand = memory_range(next_idx + 1, next_idx + length - 1).reverse.join.to_i(16)
|
425
573
|
|
426
|
-
hex_encoded_instruction = memory_range(next_idx, next_idx+length-1)
|
574
|
+
hex_encoded_instruction = memory_range(next_idx, next_idx + length - 1)
|
427
575
|
prefix = ["#{address_human(next_idx)}:", *hex_encoded_instruction].join(' ')
|
428
576
|
|
429
577
|
auto_update_flags(opcode, operand) if @cpu == :wdc65816
|
@@ -455,12 +603,12 @@ module SnesUtils
|
|
455
603
|
end
|
456
604
|
end
|
457
605
|
|
458
|
-
instructions << "#{prefix.ljust(30)} #{format
|
606
|
+
instructions << "#{prefix.ljust(30)} #{format(format, mnemonic, *operand)}"
|
459
607
|
next_idx += length
|
460
608
|
end
|
461
609
|
|
462
610
|
@next_addr_to_list = next_idx
|
463
|
-
|
611
|
+
instructions
|
464
612
|
end
|
465
613
|
|
466
614
|
def relative_operand(operand, next_idx, limit = 0x7f, offset = 0x100, rjust_len = 2)
|