diff --git a/README.md b/README.md index 6eec1fe..82d52b8 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,29 @@ To disable a watch, add a `;` right behind the `@`: ADC FOO ; @;Au(post) ``` +### Error reporting + +Should your program run into an error, champ shows you where in the source code the error occured and an execution log of the previous 20 CPU steps (you can increase the size of the log with `--error-log-size`). +Look at this example ([example05.yaml](example05.yaml) / [example05.s](example05.s)): + +``` + DSK test + MX %11 + ORG $6000 + + LDX #$ff +COUNT PHA + PHA + DEX + BNE COUNT +``` + +This is a program which repeatedly pushes the A register onto the stack until the stack is full. Stack overflow, mission accomplished! Champ will generate the following output: + +![Error report](doc/example05_1.gif?raw=true) + +On the left hand side you can see that the error occured in example05.s, line 7, while attempting a `PHA` operation when `SP` is already down to zero from previous `PHA` operations (as can be seen on the right). + ## Did you know? By the way, there's a full-fledged, incremental, standalone, no-dependencies GIF encoder in [pgif.c](pgif.c) that writes animated GIFs and uses some optimizations to further minimize space. It's stream-friendly and as you feed pixels in via `stdin`, it dutifully writes GIF data to `stdout` until `stdin` gets closed. diff --git a/champ.rb b/champ.rb index 1744de0..5936869 100755 --- a/champ.rb +++ b/champ.rb @@ -853,7 +853,7 @@ class Champ def parse_merlin_output(path) input_file = File.basename(@source_path) @source_for_file[input_file] = File.read(@source_path).split("\n").map { |x| x.gsub("\t", ' ' * 4) } - @max_source_width_for_file[input_file] = @source_for_file[input_file].map { |x| x.size }.max + @max_source_width_for_file[input_file] = [@source_for_file[input_file].map { |x| x.size }.max, 40].max @source_line = -3 File.open(path, 'r') do |f| diff --git a/doc/example05_1.gif b/doc/example05_1.gif new file mode 100644 index 0000000..c2eb6f0 Binary files /dev/null and b/doc/example05_1.gif differ diff --git a/examples/example05.s b/examples/example05.s index 7d76247..826c186 100644 --- a/examples/example05.s +++ b/examples/example05.s @@ -2,16 +2,8 @@ MX %11 ORG $6000 - LDX #$20 - JSR COUNT - LDX #$30 - JSR COUNT - LDX #$40 - JSR COUNT - - BRK - -COUNT DEX ; @Xu(post) @cycles + LDX #$ff +COUNT PHA + PHA + DEX BNE COUNT - BRK - RTS diff --git a/p65c02.c b/p65c02.c index 2d6810d..66eb0b0 100644 --- a/p65c02.c +++ b/p65c02.c @@ -31,6 +31,7 @@ uint64_t frame_count = 0; uint16_t start_pc = 0x6000; uint16_t start_frame_pc = 0xffff; +uint16_t old_pc = 0; uint16_t yoffset[192] = { 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, @@ -98,6 +99,7 @@ typedef struct { } r_watch; r_watch *watches = 0; +uint8_t watches_allocated = 0; size_t watch_count = 0; int32_t watch_offset_for_pc_and_post[0x20000]; @@ -150,6 +152,9 @@ void push(uint8_t value) { if (cpu.sp == 0) { + printf("error %04x Stack overflow\n", old_pc); + fflush(stdout); + fprintf(stderr, "Stack overflow!\n"); exit(1); } @@ -161,6 +166,8 @@ uint8_t pop() { if (cpu.sp == 0xff) { + printf("error %04x Stack underrun\n", old_pc); + fflush(stdout); fprintf(stderr, "Stack underrun!\n"); exit(1); } @@ -564,7 +571,7 @@ void branch(uint8_t condition, int8_t offset, uint8_t* cycles) void handle_next_opcode() { - uint16_t old_pc = cpu.pc; + old_pc = cpu.pc; // fetch opcode, addressing mode and cycles for next instruction uint8_t read_opcode = 0; @@ -575,6 +582,9 @@ void handle_next_opcode() if (opcode == NO_OPCODE || addressing_mode == NO_ADDRESSING_MODE) { + printf("error %04x Unhandled opcode: %02x\n", old_pc, read_opcode); + fflush(stdout); + fprintf(stderr, "Unhandled opcode at %04x: %02x\n", old_pc, read_opcode); exit(1); } @@ -1067,10 +1077,14 @@ int main(int argc, char** argv) int watch_index = 0; while (fgets(s, 1024, stdin)) { - if (!watches) + if (s[0] == '\n') + break; + if (!watches_allocated) { watch_count = parse_int(s, 0); - watches = malloc(sizeof(r_watch) * watch_count); + if (watch_count > 0) + watches = malloc(sizeof(r_watch) * watch_count); + watches_allocated = 1; } else {