snes_utils 0.1.1 → 0.3.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 +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)
|