diff --git a/.gitignore b/.gitignore index 51ab0cd..ca0f00a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build/ .tmp/ *.z64 fs/gfx/*.sprite +.*.sw* diff --git a/Makefile b/Makefile index 1b791ba..55a147e 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ $(FS): $(wildcard fs/*) $(wildcard gfx/*) $(N64_ROOTDIR)/bin/mksprite 32 gfx/stick_6.png fs/gfx/stick_6.sprite $(N64_ROOTDIR)/bin/mksprite 32 gfx/stick_7.png fs/gfx/stick_7.sprite $(N64_ROOTDIR)/bin/mksprite 32 gfx/stick_neutral.png fs/gfx/stick_neutral.sprite + $(N64_ROOTDIR)/bin/mksprite 32 gfx/point.png fs/gfx/point.sprite $(N64_MKDFS) $@ fs $(BUILD_DIR)/$(NAME).elf: $(OBJS) @@ -42,4 +43,4 @@ clean: rm -f $(BUILD_DIR)/* *.z64 .PHONY: clean --include $(wildcard $(BUILD_DIR)/*.d) \ No newline at end of file +-include $(wildcard $(BUILD_DIR)/*.d) diff --git a/gfx/point.png b/gfx/point.png new file mode 100755 index 0000000..f1e0f00 Binary files /dev/null and b/gfx/point.png differ diff --git a/src/main.c b/src/main.c index ec65ea2..42eacd7 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,8 @@ #include #include "range_test.h" +#include "range_live.h" +#include "oscilloscope.h" #include "text.h" #include "colors.h" #include "input.h" @@ -14,6 +16,8 @@ enum Screen SCR_ABOUT, SCR_RANGE_TEST, SCR_RANGE_RESULT, + SCR_LIVE, + SCR_OSCOPE, }; void reset_handler(exception_t *ex) @@ -65,6 +69,8 @@ int main(void) "Range test (3 samples)", "Range test (5 samples)", "Display last range result", + "Live range display", + "Oscilloscope display", "Help", "About", }; @@ -107,9 +113,15 @@ int main(void) } break; case 4: - current_screen = SCR_HELP; + current_screen = SCR_LIVE; break; case 5: + current_screen = SCR_OSCOPE; + break; + case 6: + current_screen = SCR_HELP; + break; + case 7: current_screen = SCR_ABOUT; break; } @@ -167,9 +179,12 @@ int main(void) case SCR_HELP: const char *page_names[] = { "Basic controls", + "Basic controls cont.", "Range testing", "Range testing cont.", "Range testing cont.", + "Live range display", + "Oscilloscope display", }; const int pages = sizeof(page_names) / sizeof(char*); int page = 0; @@ -221,6 +236,24 @@ int main(void) "* Start - return to main menu\n", ALIGN_LEFT); break; case 1: + text_set_font(FONT_BOLD); + text_draw_wordwrap(ctx, 32, 44 + (11 * 0), 320-64, + "On the live range testing screen:\n"); + text_set_font(FONT_MEDIUM); + text_draw_wordwrap(ctx, 32, 44 + (11 * 1), 320-64, + "* A - toggle history display\n" + "* B - clear history display\n" + "* Z - change zoom\n" + "* L/R, D-Pad Left/Right - cycle example ranges\n" + "* Start - return to main menu\n"); + text_set_font(FONT_BOLD); + text_draw_wordwrap(ctx, 32, 44 + (11 * 7), 320-64, + "On the oscilloscope screen:\n"); + text_set_font(FONT_MEDIUM); + text_draw_wordwrap(ctx, 32, 44 + (11 * 8), 320-64, + "* Start - return to main menu\n"); + break; + case 2: text_draw_wordwrap(ctx, 32, 44, 320-64, "User can take one or more measurements of the " "analog values. More measurements help even out " @@ -236,7 +269,7 @@ int main(void) "to judge the controller's range." ); break; - case 2: + case 3: text_draw_wordwrap(ctx, 32, 44, 320-64, "The measurement display can also be overriden with " "one of the example ones, to let user view the expected " @@ -250,7 +283,7 @@ int main(void) "overriden by user." ); break; - case 3: + case 4: text_draw_wordwrap(ctx, 32, 44, 320-64, "Absolute analog values for each notch are displayed " "on the right of the screen, as well as angles " @@ -266,6 +299,21 @@ int main(void) "magnitude diagonals on original N64 controllers." ); break; + case 5: + text_draw_wordwrap(ctx, 32, 44, 320-64, + "Displays live X/Y values on a graph using ideal " + "OEM or Hori values as an overlay. Displays " + "the most recent 1024 values in blue, and the " + "current X/Y values as integers.\n\n" + ); + break; + case 6: + text_draw_wordwrap(ctx, 32, 44, 320-64, + "Displays live X/Y values on an oscilloscope-style " + "display. Useful for identifying skips and " + "snapback issues.\n\n" + ); + break; } @@ -328,6 +376,12 @@ int main(void) case SCR_RANGE_RESULT: display_angles(result, sample_count); current_screen = SCR_MAIN_MENU; + case SCR_LIVE: + display_live_ranges(); + current_screen = SCR_MAIN_MENU; + case SCR_OSCOPE: + display_oscilloscope(); + current_screen = SCR_MAIN_MENU; } } -} \ No newline at end of file +} diff --git a/src/oscilloscope.c b/src/oscilloscope.c new file mode 100644 index 0000000..b683f21 --- /dev/null +++ b/src/oscilloscope.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#include "range_test.h" +#include "oscilloscope.h" +#include "drawing.h" +#include "text.h" +#include "colors.h" +#include "input.h" + +void display_oscilloscope() { + int line_height = 11, + sz_history = 230, + x_offset = 24, + y_offset1 = 70, + y_offset2 = 170, + lbl_x_offset = 282, + lbl_y_offset1 = y_offset1 - (line_height / 2), + lbl_y_offset2 = y_offset2 - (line_height / 2), + count = 0; + + float zoom = 0.4; + uint32_t c_blue = graphics_make_color(0, 192, 255, 255); + uint32_t c_green = graphics_make_color(64, 255, 0, 255); + struct Vec2 history[sz_history]; + + text_set_line_height(line_height); + display_context_t ctx; + + for (;;) { + while ((ctx = display_lock()) == 0) {} + display_show(ctx); + + graphics_fill_screen(ctx, COLOR_BACKGROUND); + graphics_set_color(COLOR_FOREGROUND, 0); + + controller_scan(); + struct controller_data cdata = get_keys_pressed(); + + struct Vec2 v = { cdata.c[0].x, cdata.c[0].y }; + + for (int i = count; i > 0; i--) { + history[i] = history[i - 1]; + draw_aa_line( + ctx, + x_offset + sz_history - i, + y_offset1, + x_offset + sz_history - i, + y_offset1 + history[i].x * zoom, + c_blue + ); + draw_aa_line( + ctx, + x_offset + sz_history - i, + y_offset2, + x_offset + sz_history - i, + y_offset2 + (history[i].y * -1) * zoom, + c_green + ); + } + + history[0] = v; + draw_aa_line( + ctx, + x_offset + sz_history, + y_offset1, + x_offset + sz_history, + y_offset1 + v.x * zoom, + c_blue + ); + draw_aa_line( + ctx, + x_offset + sz_history, + y_offset2, + x_offset + sz_history, + y_offset2 + (v.y * -1) * zoom, + c_green + ); + + if (count < sz_history - 1) { + count++; + } + + char buf[128]; + + text_set_font(FONT_BOLD); + snprintf(buf, sizeof(buf), "%3d", v.x); + text_draw(ctx, lbl_x_offset, lbl_y_offset1, buf, ALIGN_RIGHT); + + snprintf(buf, sizeof(buf), "%3d", v.y); + text_draw(ctx, lbl_x_offset, lbl_y_offset2, buf, ALIGN_RIGHT); + + text_set_font(FONT_MEDIUM); + snprintf(buf, sizeof(buf), "x"); + text_draw(ctx, lbl_x_offset + 8, lbl_y_offset1, buf, ALIGN_LEFT); + + snprintf(buf, sizeof(buf), "y"); + text_draw(ctx, lbl_x_offset + 8, lbl_y_offset2, buf, ALIGN_LEFT); + + + snprintf(buf, sizeof(buf), "Oscilloscope display"); + text_draw(ctx, 160, 15, buf, ALIGN_CENTER); + + text_set_font(FONT_MEDIUM); + graphics_set_color(graphics_make_color(128, 128, 128, 255), 0); + text_draw(ctx, 320 - 16, 213, REPO_URL, ALIGN_RIGHT); + + if (cdata.c[0].start) { + break; + } + } +} diff --git a/src/oscilloscope.h b/src/oscilloscope.h new file mode 100644 index 0000000..3070fb6 --- /dev/null +++ b/src/oscilloscope.h @@ -0,0 +1,4 @@ +#pragma once + +void display_oscilloscope(); + diff --git a/src/range_live.c b/src/range_live.c new file mode 100644 index 0000000..f002605 --- /dev/null +++ b/src/range_live.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include + +#include "range_test.h" +#include "range_live.h" +#include "text.h" +#include "colors.h" +#include "input.h" + +struct StickAngles live_compare_oem = +{ + .values = { + 0, 85, + 70, 70, + 85, 0 , + 70,-70, + 0, -85, + -70,-70, + -85, 0 , + -70, 70 + } +}; + +struct StickAngles live_compare_hori = +{ + .values = { + 0, 100, + 75, 75, + 100,0, + 75,-75, + 0, -100, + -75,-75, + -100,0, + -75, 75 + } +}; + +struct StickAngles *live_comparisons[] = { + NULL, + &live_compare_oem, + &live_compare_hori, +}; + +char *get_title_str(int current_comparison) { + switch (current_comparison) { + case 1: + return "Live, ideal OEM overlay"; + case 2: + return "Live, ideal Horipad Mini overlay"; + } + return "Live range display"; +} + +void display_live_ranges() { + int count = 0, + line_height = 11, + show_history = 1, + sz_history = 1024, + current_comparison = 1, + comparison_count = sizeof(live_comparisons) / sizeof(0), + zoomout = 0; + float zoomout_factor = 1; + text_set_line_height(line_height); + display_context_t ctx; + + int f = dfs_open("/gfx/point.sprite"); + int size = dfs_size(f); + char * title_str = get_title_str(current_comparison); + sprite_t *point = malloc(size); + dfs_read(point, size, 1, f); + dfs_close(f); + + struct Vec2 history[sz_history]; + + uint32_t comparison_color = graphics_make_color(64, 255, 0, 255), + history_color = graphics_make_color(0, 192, 255, 255); + + for (;;) { + while ((ctx = display_lock()) == 0) {} + display_show(ctx); + + graphics_fill_screen(ctx, COLOR_BACKGROUND); + graphics_set_color(COLOR_FOREGROUND, 0); + + controller_scan(); + struct controller_data cdata = get_keys_pressed(); + char buf[128]; + + struct Vec2 v = { cdata.c[0].x, cdata.c[0].y }; + + snprintf(buf, sizeof(buf), "x\ny"); + text_set_font(FONT_MEDIUM); + text_draw(ctx, 290, 120 - line_height, buf, ALIGN_LEFT); + + text_set_font(FONT_BOLD); + snprintf(buf, sizeof(buf), "%3d\n%3d", v.x, v.y); + text_draw(ctx, 282, 120 - line_height, buf, ALIGN_RIGHT); + + draw_center_cross(ctx, 160); + if (current_comparison > 0) { + draw_stick_angles( + ctx, + *live_comparisons[current_comparison], + comparison_color, + zoomout, + 160 + ); + } + + if (show_history == 1) { + int history_update = 0; + if (v.x != history[0].x || v.y != history[0].y) { + history_update = 1; + if (count < sz_history - 1) { + count++; + } + + history[0] = v; + } + + for (int i = count; i > 0; i--) { + if (history_update == 1) history[i] = history[i - 1]; + int x = smax(0, smin(320, (history[i].x * zoomout_factor) + 160)); + int y = smax(0, smin(240, ((history[i].y * zoomout_factor) * -1) + 120)); + graphics_draw_pixel(ctx, x, y, history_color); + } + } + + int x = smax(0, smin(320, (v.x * zoomout_factor) + 158)); + int y = smax(0, smin(240, ((v.y * zoomout_factor) * -1) + 118)); + graphics_draw_sprite(ctx, x, y, point); + + if (cdata.c[0].start) { + break; + } + + cdata = get_keys_down_filtered(); + if (cdata.c[0].A) { + show_history ^= 1; + } + + if (cdata.c[0].B) { + count = 0; + } + + if (cdata.c[0].Z) { + zoomout ^= 1; + zoomout_factor = (zoomout == 0) ? 1 : 0.75; + } + + if (cdata.c[0].left || cdata.c[0].L) { + current_comparison--; + if (current_comparison < 0) current_comparison += comparison_count; + title_str = get_title_str(current_comparison); + } + + if (cdata.c[0].right || cdata.c[0].R) { + current_comparison = (current_comparison + 1) % comparison_count; + title_str = get_title_str(current_comparison); + } + + text_set_font(FONT_MEDIUM); + snprintf(buf, sizeof(buf), "%s", title_str); + text_draw(ctx, 160, 15, buf, ALIGN_CENTER); + + if (zoomout) { + text_draw(ctx, 16, 213, "75\% scale", ALIGN_LEFT); + } + + text_set_font(FONT_MEDIUM); + graphics_set_color(graphics_make_color(128, 128, 128, 255), 0); + text_draw(ctx, 320 - 16, 213, REPO_URL, ALIGN_RIGHT); + } + + free(point); + +} diff --git a/src/range_live.h b/src/range_live.h new file mode 100644 index 0000000..e70fa5a --- /dev/null +++ b/src/range_live.h @@ -0,0 +1,3 @@ +#pragma once + +void display_live_ranges(); diff --git a/src/range_test.c b/src/range_test.c index c188ee8..57a863c 100644 --- a/src/range_test.c +++ b/src/range_test.c @@ -67,7 +67,7 @@ const char *example_names[] = "Ideal Horipad Mini example", }; -void draw_stick_angles(display_context_t ctx, struct StickAngles a, uint32_t color, int zoomout) +void draw_stick_angles(display_context_t ctx, struct StickAngles a, uint32_t color, int zoomout, int x) { if (zoomout) { for (int i = 0; i < 16; i++) { @@ -81,24 +81,25 @@ void draw_stick_angles(display_context_t ctx, struct StickAngles a, uint32_t col int j = (i + 1) % 8; draw_aa_line( ctx, - 120 + v[i].x, + x + v[i].x, 120 - v[i].y, - 120 + v[j].x, + x + v[j].x, 120 - v[j].y, color); } } -void draw_center_cross(display_context_t ctx) +void draw_center_cross(display_context_t ctx, int x_origin) { - int x, y; + int x, y, offset; y = 120; - for (x = 0; x < 240; x++) { - int i = smin(240 - abs(240 - x * 2), 120); + offset = x_origin - 120; + for (x = offset; x < 240+offset; x++) { + int i = smin(240 - abs(240 - (x-offset)*2), 120); graphics_draw_pixel_trans(ctx, x, y, graphics_make_color(255, 255, 255, i)); } - x = 120; + x = x_origin; for (y = 0; y < 240; y++) { int i = smin(240 - abs(240 - y * 2), 120); graphics_draw_pixel_trans(ctx, x, y, graphics_make_color(255, 255, 255, i)); @@ -382,6 +383,7 @@ void display_angles(struct StickAngles a[], int sample_count) struct StickAngles median = find_median(a, sample_count); int zoomout = should_enable_zoomout(a, sample_count); + int x_origin = 120; text_set_line_height(10); for (;;) { @@ -389,12 +391,12 @@ void display_angles(struct StickAngles a[], int sample_count) graphics_fill_screen(ctx, COLOR_BACKGROUND); - draw_center_cross(ctx); + draw_center_cross(ctx, x_origin); for (int i = 0; i < sample_count; i++) { - draw_stick_angles(ctx, a[i], c_gray, zoomout); + draw_stick_angles(ctx, a[i], c_gray, zoomout, x_origin); } if (comparisons[current_comparison]) { - draw_stick_angles(ctx, *comparisons[current_comparison], c_green, zoomout); + draw_stick_angles(ctx, *comparisons[current_comparison], c_green, zoomout, x_origin); } struct StickAngles *current; @@ -410,7 +412,7 @@ void display_angles(struct StickAngles a[], int sample_count) c_current = c_blue; } - draw_stick_angles(ctx, *current, c_current, zoomout); + draw_stick_angles(ctx, *current, c_current, zoomout, x_origin); print_stick_angles(ctx, *current); graphics_set_color(COLOR_FOREGROUND, 0); diff --git a/src/range_test.h b/src/range_test.h index 088b919..4269ca4 100644 --- a/src/range_test.h +++ b/src/range_test.h @@ -20,4 +20,6 @@ struct StickAngles }; void test_angles(struct StickAngles *a, int testnum); -void display_angles(struct StickAngles a[], int sample_count); \ No newline at end of file +void display_angles(struct StickAngles a[], int sample_count); +void draw_center_cross(display_context_t ctx, int x_origin); +void draw_stick_angles(display_context_t ctx, struct StickAngles a, uint32_t color, int zoomout, int x);