mirror of
https://github.com/specht/champ.git
synced 2024-11-28 14:50:46 +00:00
added error handling
This commit is contained in:
parent
044f7be667
commit
d1687f534d
95
champ.rb
95
champ.rb
@ -48,6 +48,7 @@ class Champ
|
|||||||
STDERR.puts 'Usage: ./champ.rb [options] <config.yaml>'
|
STDERR.puts 'Usage: ./champ.rb [options] <config.yaml>'
|
||||||
STDERR.puts 'Options:'
|
STDERR.puts 'Options:'
|
||||||
STDERR.puts ' --max-frames <n>'
|
STDERR.puts ' --max-frames <n>'
|
||||||
|
STDERR.puts ' --error-log-size <n> (default: 20)'
|
||||||
STDERR.puts ' --no-animation'
|
STDERR.puts ' --no-animation'
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
@ -58,11 +59,17 @@ class Champ
|
|||||||
@max_frames = nil
|
@max_frames = nil
|
||||||
@record_frames = true
|
@record_frames = true
|
||||||
@cycles_per_function = {}
|
@cycles_per_function = {}
|
||||||
|
@execution_log = []
|
||||||
|
@execution_log_size = 20
|
||||||
|
@code_for_pc = {}
|
||||||
|
@source_for_file = {}
|
||||||
args = ARGV.dup
|
args = ARGV.dup
|
||||||
while args.size > 1
|
while args.size > 1
|
||||||
item = args.shift
|
item = args.shift
|
||||||
if item == '--max-frames'
|
if item == '--max-frames'
|
||||||
@max_frames = args.shift.to_i
|
@max_frames = args.shift.to_i
|
||||||
|
elsif item == '--error-log-size'
|
||||||
|
@execution_log_size = args.shift.to_i
|
||||||
elsif item == '--no-animation'
|
elsif item == '--no-animation'
|
||||||
@record_frames = false
|
@record_frames = false
|
||||||
else
|
else
|
||||||
@ -189,7 +196,7 @@ class Champ
|
|||||||
@max_cycle_count = 0
|
@max_cycle_count = 0
|
||||||
call_stack = []
|
call_stack = []
|
||||||
last_call_stack_cycles = 0
|
last_call_stack_cycles = 0
|
||||||
Open3.popen2("./p65c02 #{@record_frames ? '' : '--no-screen'} --hide-log --start-pc #{start_pc} #{File.join(temp_dir, 'disk_image')}") do |stdin, stdout, thread|
|
Open3.popen2("./p65c02 #{@record_frames ? '' : '--no-screen'} --start-pc #{start_pc} #{File.join(temp_dir, 'disk_image')}") do |stdin, stdout, thread|
|
||||||
stdin.puts watch_input.split("\n").size
|
stdin.puts watch_input.split("\n").size
|
||||||
stdin.puts watch_input
|
stdin.puts watch_input
|
||||||
stdin.close
|
stdin.close
|
||||||
@ -205,7 +212,19 @@ class Champ
|
|||||||
stdout.each_line do |line|
|
stdout.each_line do |line|
|
||||||
# puts "> #{line}"
|
# puts "> #{line}"
|
||||||
parts = line.split(' ')
|
parts = line.split(' ')
|
||||||
if parts.first == 'jsr'
|
if parts.first == 'error'
|
||||||
|
parts.shift
|
||||||
|
pc = parts.shift.to_i(16)
|
||||||
|
message = parts.join(' ')
|
||||||
|
@error = {:pc => pc, :message => message}
|
||||||
|
elsif parts.first == 'log'
|
||||||
|
parts.shift
|
||||||
|
log = parts.map { |x| x.to_i(16) }
|
||||||
|
@execution_log << log
|
||||||
|
while @execution_log.size > @execution_log_size
|
||||||
|
@execution_log.shift
|
||||||
|
end
|
||||||
|
elsif parts.first == 'jsr'
|
||||||
pc = parts[1].to_i(16)
|
pc = parts[1].to_i(16)
|
||||||
cycles = parts[2].to_i
|
cycles = parts[2].to_i
|
||||||
@max_cycle_count = cycles
|
@max_cycle_count = cycles
|
||||||
@ -725,9 +744,9 @@ class Champ
|
|||||||
io.puts "overlap = false;"
|
io.puts "overlap = false;"
|
||||||
io.puts "rankdir = LR;"
|
io.puts "rankdir = LR;"
|
||||||
io.puts "splines = true;"
|
io.puts "splines = true;"
|
||||||
io.puts "graph [fontname = sans, fontsize = 8, size = \"14, 11\", nodesep = 0.2, ranksep = 0.3, ordering = out];"
|
io.puts "graph [fontname = Arial, fontsize = 8, size = \"14, 11\", nodesep = 0.2, ranksep = 0.3, ordering = out];"
|
||||||
io.puts "node [fontname = sans, fontsize = 8, shape = rect, style = filled, fillcolor = \"#fce94f\" color = \"#c4a000\"];"
|
io.puts "node [fontname = Arial, fontsize = 8, shape = rect, style = filled, fillcolor = \"#fce94f\" color = \"#c4a000\"];"
|
||||||
io.puts "edge [fontname = sans, fontsize = 8, color = \"#444444\"];"
|
io.puts "edge [fontname = Arial, fontsize = 8, color = \"#444444\"];"
|
||||||
all_nodes.each do |node|
|
all_nodes.each do |node|
|
||||||
label = @label_for_pc[node] || sprintf('0x%04x', node)
|
label = @label_for_pc[node] || sprintf('0x%04x', node)
|
||||||
label = "<B>#{label}</B>"
|
label = "<B>#{label}</B>"
|
||||||
@ -766,6 +785,43 @@ class Champ
|
|||||||
report.sub!('#{call_graph}', '<em>(GraphViz not installed)</em>')
|
report.sub!('#{call_graph}', '<em>(GraphViz not installed)</em>')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# write error dump
|
||||||
|
if @error
|
||||||
|
io = StringIO.new
|
||||||
|
io.puts "<div>"
|
||||||
|
io.puts "<h2>Error log</h2>"
|
||||||
|
|
||||||
|
io.puts "<code><pre>"
|
||||||
|
source_code = @code_for_pc[@error[:pc]]
|
||||||
|
STDERR.puts source_code.to_yaml
|
||||||
|
offset = source_code[:line] - 1
|
||||||
|
this_filename = source_code[:file]
|
||||||
|
io.puts "<span class='heading'>#{sprintf('%-83s', this_filename)}</span>"
|
||||||
|
((offset - 16)..(offset + 16)).each do |i|
|
||||||
|
next if i < 0 || i >= @source_for_file[this_filename].size
|
||||||
|
io.print "<span class='#{(i == offset) ? 'error' : 'code'}'>"
|
||||||
|
io.print sprintf('%5d | %-75s', i + 1, @source_for_file[this_filename][i])
|
||||||
|
io.print "</span>" if i == offset
|
||||||
|
io.puts
|
||||||
|
end
|
||||||
|
io.puts "</pre></code>"
|
||||||
|
|
||||||
|
io.puts "<h3>Debug log</h3>"
|
||||||
|
io.puts "<code><pre>"
|
||||||
|
io.puts sprintf("<span class='heading'> PC | A X Y PC SP Flags </span>")
|
||||||
|
@execution_log.each do |item|
|
||||||
|
io.puts sprintf("<span class='code'> 0x%04x | 0x%02x 0x%02x 0x%02x 0x%04x 0x%02x 0x%02x </span>", *item)
|
||||||
|
end
|
||||||
|
io.puts sprintf("<span class='error'> 0x%04x | %-37s </span>", @error[:pc], @error[:message])
|
||||||
|
io.puts "</pre></code>"
|
||||||
|
|
||||||
|
io.puts "</div>"
|
||||||
|
|
||||||
|
report.sub!('#{error}', io.string)
|
||||||
|
else
|
||||||
|
report.sub!('#{error}', '')
|
||||||
|
end
|
||||||
|
|
||||||
f.puts report
|
f.puts report
|
||||||
end
|
end
|
||||||
puts ' done.'
|
puts ' done.'
|
||||||
@ -782,6 +838,8 @@ class Champ
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_merlin_output(path)
|
def parse_merlin_output(path)
|
||||||
|
input_file = File.basename(@source_path)
|
||||||
|
@source_for_file[input_file] = File.read(@source_path).split("\n")
|
||||||
@source_line = -3
|
@source_line = -3
|
||||||
File.open(path, 'r') do |f|
|
File.open(path, 'r') do |f|
|
||||||
f.each_line do |line|
|
f.each_line do |line|
|
||||||
@ -795,8 +853,18 @@ class Champ
|
|||||||
pc = parts[6].split(' ').first.split('/').last.to_i(16)
|
pc = parts[6].split(' ').first.split('/').last.to_i(16)
|
||||||
code = parts[7].strip
|
code = parts[7].strip
|
||||||
code_parts = code.split(/\s+/)
|
code_parts = code.split(/\s+/)
|
||||||
next if code_parts.empty?
|
|
||||||
|
|
||||||
|
line_number = parts[1].split(' ').map { |x| x.strip }.reject { |x| x.empty? }.last.to_i
|
||||||
|
# unless (@pc_code_lines.last || 0) == pc
|
||||||
|
STDERR.puts line_number
|
||||||
|
@code_for_pc[pc] = {
|
||||||
|
:file => input_file,
|
||||||
|
:line => line_number,
|
||||||
|
}
|
||||||
|
# @pc_code_lines << pc
|
||||||
|
# end
|
||||||
|
|
||||||
|
next if code_parts.empty?
|
||||||
label = nil
|
label = nil
|
||||||
|
|
||||||
champ_directives = []
|
champ_directives = []
|
||||||
@ -834,7 +902,6 @@ class Champ
|
|||||||
elsif line_type == 'Code'
|
elsif line_type == 'Code'
|
||||||
@watches[pc] ||= []
|
@watches[pc] ||= []
|
||||||
champ_directives.each do |directive|
|
champ_directives.each do |directive|
|
||||||
line_number = parts[1].split(' ').map { |x| x.strip }.reject { |x| x.empty? }.last.to_i
|
|
||||||
watch = parse_champ_directive(directive, false)
|
watch = parse_champ_directive(directive, false)
|
||||||
watch[:line_number] = line_number
|
watch[:line_number] = line_number
|
||||||
watch[:pc] = pc
|
watch[:pc] = pc
|
||||||
@ -949,9 +1016,23 @@ __END__
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 0 0.5em;
|
padding: 0 0.5em;
|
||||||
}
|
}
|
||||||
|
.heading {
|
||||||
|
color: #2e3436;
|
||||||
|
background-color: #babdb6;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.code {
|
||||||
|
color: #555753;
|
||||||
|
background-color: #edeeec;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #cc0000;
|
||||||
|
background-color: #f2bfbf;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
#{error}
|
||||||
<div style='float: left; padding-right: 10px;'>
|
<div style='float: left; padding-right: 10px;'>
|
||||||
<h2>Frames</h2>
|
<h2>Frames</h2>
|
||||||
#{screenshots}
|
#{screenshots}
|
||||||
|
17
examples/example05.s
Normal file
17
examples/example05.s
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
DSK test
|
||||||
|
MX %11
|
||||||
|
ORG $6000
|
||||||
|
|
||||||
|
LDX #$20
|
||||||
|
JSR COUNT
|
||||||
|
LDX #$30
|
||||||
|
JSR COUNT
|
||||||
|
LDX #$40
|
||||||
|
JSR COUNT
|
||||||
|
|
||||||
|
BRK
|
||||||
|
|
||||||
|
COUNT DEX ; @Xu(post) @cycles
|
||||||
|
BNE COUNT
|
||||||
|
BRK
|
||||||
|
RTS
|
3
examples/example05.yaml
Normal file
3
examples/example05.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
load:
|
||||||
|
0x6000: example05.s
|
||||||
|
entry: 0x6000
|
26
p65c02.c
26
p65c02.c
@ -633,13 +633,6 @@ void handle_next_opcode()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_log)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "# %04x | %d | %02x | %s %-18s | ", old_pc, cycles, read_opcode, OPCODE_STRINGS[opcode],
|
|
||||||
ADDRESSING_MODE_STRINGS[addressing_mode]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int unhandled_opcode = 0;
|
int unhandled_opcode = 0;
|
||||||
uint8_t t8 = 0;
|
uint8_t t8 = 0;
|
||||||
uint16_t t16 = 0;
|
uint16_t t16 = 0;
|
||||||
@ -948,7 +941,10 @@ void handle_next_opcode()
|
|||||||
};
|
};
|
||||||
if (unhandled_opcode)
|
if (unhandled_opcode)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Opcode %s not implemented yet at PC 0x%04x\n",
|
printf("error %04x Opcode %s not implemented yet.\n",
|
||||||
|
old_pc, OPCODE_STRINGS[opcode]);
|
||||||
|
fflush(stdout);
|
||||||
|
fprintf(stderr, "Opcode %s not implemented yet at PC 0x%04x.\n",
|
||||||
OPCODE_STRINGS[opcode], cpu.pc);
|
OPCODE_STRINGS[opcode], cpu.pc);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -957,17 +953,9 @@ void handle_next_opcode()
|
|||||||
cycles_per_function[trace_stack_function[trace_stack_pointer + 1]] += cycles;
|
cycles_per_function[trace_stack_function[trace_stack_pointer + 1]] += cycles;
|
||||||
if (show_log)
|
if (show_log)
|
||||||
{
|
{
|
||||||
char flags_str[6];
|
printf("log %04x %02x %02x %02x %04x %02x %02x\n",
|
||||||
flags_str[0] = test_flag(CARRY) ? 'C' : 'c';
|
old_pc, cpu.a, cpu.x, cpu.y, cpu.pc, cpu.sp, cpu.flags);
|
||||||
flags_str[1] = test_flag(ZERO) ? 'Z' : 'z';
|
fflush(stdout);
|
||||||
flags_str[2] = test_flag(DECIMAL_MODE) ? 'D' : 'd';
|
|
||||||
flags_str[3] = test_flag(OVERFLOW) ? 'V' : 'v';
|
|
||||||
flags_str[4] = test_flag(NEGATIVE) ? 'N' : 'n';
|
|
||||||
flags_str[5] = 0;
|
|
||||||
|
|
||||||
fprintf(stderr, "A: %02x, X: %02x, Y: %02x, PC: %04x, SP: %02x, FLAGS: %02x %s | %10ld |",
|
|
||||||
cpu.a, cpu.x, cpu.y, cpu.pc, cpu.sp, cpu.flags, flags_str, cpu.total_cycles);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user