1
0
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:
Michael Specht 2018-02-19 21:51:30 +01:00
parent 044f7be667
commit d1687f534d
4 changed files with 115 additions and 26 deletions

View File

@ -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
View 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
View File

@ -0,0 +1,3 @@
load:
0x6000: example05.s
entry: 0x6000

View File

@ -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");
} }
} }