From d1687f534d4a36aaeed3d708975f3c52f40d984b Mon Sep 17 00:00:00 2001 From: Michael Specht Date: Mon, 19 Feb 2018 21:51:30 +0100 Subject: [PATCH] added error handling --- champ.rb | 95 ++++++++++++++++++++++++++++++++++++++--- examples/example05.s | 17 ++++++++ examples/example05.yaml | 3 ++ p65c02.c | 26 +++-------- 4 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 examples/example05.s create mode 100644 examples/example05.yaml diff --git a/champ.rb b/champ.rb index 7d27d47..2189aed 100755 --- a/champ.rb +++ b/champ.rb @@ -48,6 +48,7 @@ class Champ STDERR.puts 'Usage: ./champ.rb [options] ' STDERR.puts 'Options:' STDERR.puts ' --max-frames ' + STDERR.puts ' --error-log-size (default: 20)' STDERR.puts ' --no-animation' exit(1) end @@ -58,11 +59,17 @@ class Champ @max_frames = nil @record_frames = true @cycles_per_function = {} + @execution_log = [] + @execution_log_size = 20 + @code_for_pc = {} + @source_for_file = {} args = ARGV.dup while args.size > 1 item = args.shift if item == '--max-frames' @max_frames = args.shift.to_i + elsif item == '--error-log-size' + @execution_log_size = args.shift.to_i elsif item == '--no-animation' @record_frames = false else @@ -189,7 +196,7 @@ class Champ @max_cycle_count = 0 call_stack = [] 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 stdin.close @@ -205,7 +212,19 @@ class Champ stdout.each_line do |line| # puts "> #{line}" 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) cycles = parts[2].to_i @max_cycle_count = cycles @@ -725,9 +744,9 @@ class Champ io.puts "overlap = false;" io.puts "rankdir = LR;" io.puts "splines = true;" - io.puts "graph [fontname = sans, 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 "edge [fontname = sans, fontsize = 8, color = \"#444444\"];" + io.puts "graph [fontname = Arial, fontsize = 8, size = \"14, 11\", nodesep = 0.2, ranksep = 0.3, ordering = out];" + io.puts "node [fontname = Arial, fontsize = 8, shape = rect, style = filled, fillcolor = \"#fce94f\" color = \"#c4a000\"];" + io.puts "edge [fontname = Arial, fontsize = 8, color = \"#444444\"];" all_nodes.each do |node| label = @label_for_pc[node] || sprintf('0x%04x', node) label = "#{label}" @@ -766,6 +785,43 @@ class Champ report.sub!('#{call_graph}', '(GraphViz not installed)') end + # write error dump + if @error + io = StringIO.new + io.puts "
" + io.puts "

Error log

" + + io.puts "
"
+                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 "#{sprintf('%-83s', this_filename)}"
+                ((offset - 16)..(offset + 16)).each do |i|
+                    next if i < 0 || i >= @source_for_file[this_filename].size
+                    io.print ""
+                    io.print sprintf('%5d | %-75s', i + 1, @source_for_file[this_filename][i])
+                    io.print "" if i == offset
+                    io.puts
+                end
+                io.puts "
" + + io.puts "

Debug log

" + io.puts "
"
+                io.puts sprintf("   PC    |    A     X     Y     PC      SP  Flags ")
+                @execution_log.each do |item|
+                    io.puts sprintf(" 0x%04x  |  0x%02x  0x%02x  0x%02x  0x%04x  0x%02x  0x%02x  ", *item)
+                end
+                io.puts sprintf(" 0x%04x  |  %-37s ", @error[:pc], @error[:message])
+                io.puts "
" + + io.puts "
" + + report.sub!('#{error}', io.string) + else + report.sub!('#{error}', '') + end + f.puts report end puts ' done.' @@ -782,6 +838,8 @@ class Champ end 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 File.open(path, 'r') do |f| f.each_line do |line| @@ -795,8 +853,18 @@ class Champ pc = parts[6].split(' ').first.split('/').last.to_i(16) code = parts[7].strip 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 champ_directives = [] @@ -834,7 +902,6 @@ class Champ elsif line_type == 'Code' @watches[pc] ||= [] 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[:line_number] = line_number watch[:pc] = pc @@ -949,9 +1016,23 @@ __END__ text-align: left; 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; + } +#{error}

Frames

#{screenshots} diff --git a/examples/example05.s b/examples/example05.s new file mode 100644 index 0000000..7d76247 --- /dev/null +++ b/examples/example05.s @@ -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 diff --git a/examples/example05.yaml b/examples/example05.yaml new file mode 100644 index 0000000..6823282 --- /dev/null +++ b/examples/example05.yaml @@ -0,0 +1,3 @@ +load: + 0x6000: example05.s +entry: 0x6000 diff --git a/p65c02.c b/p65c02.c index b373dec..2d6810d 100644 --- a/p65c02.c +++ b/p65c02.c @@ -633,13 +633,6 @@ void handle_next_opcode() 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; uint8_t t8 = 0; uint16_t t16 = 0; @@ -948,7 +941,10 @@ void handle_next_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); exit(1); } @@ -957,17 +953,9 @@ void handle_next_opcode() cycles_per_function[trace_stack_function[trace_stack_pointer + 1]] += cycles; if (show_log) { - char flags_str[6]; - flags_str[0] = test_flag(CARRY) ? 'C' : 'c'; - flags_str[1] = test_flag(ZERO) ? 'Z' : 'z'; - 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"); + printf("log %04x %02x %02x %02x %04x %02x %02x\n", + old_pc, cpu.a, cpu.x, cpu.y, cpu.pc, cpu.sp, cpu.flags); + fflush(stdout); } }