From ce0a885fc831ee98fc95aa312d088cf17f5a5fe1 Mon Sep 17 00:00:00 2001 From: Artemio Date: Thu, 18 Jul 2024 12:59:12 -0600 Subject: [PATCH] == N64 == - Added fade for splash screens - Changed a few assets to palette versions - Improved frame stats - Added a preliminary version of the scroll test --- 240psuite/N64/240pSuite.c | 97 ++++++++++++++++++---- 240psuite/N64/240pn64.pnproj | 2 +- 240psuite/N64/Makefile | 6 +- 240psuite/N64/assets/libdragon.png | Bin 8902 -> 1132 bytes 240psuite/N64/assets/mainbg.png | Bin 8486 -> 1285 bytes 240psuite/N64/assets/sonicback.png | Bin 0 -> 5853 bytes 240psuite/N64/assets/sonicfloor.png | Bin 0 -> 4981 bytes 240psuite/N64/controller.c | 18 +++-- 240psuite/N64/controller.h | 4 +- 240psuite/N64/image.c | 3 +- 240psuite/N64/tests.c | 120 ++++++++++++++++++++++++++++ 240psuite/N64/tests.h | 6 ++ 240psuite/N64/video.c | 44 +++++++--- 240psuite/N64/video.h | 3 +- 14 files changed, 261 insertions(+), 42 deletions(-) create mode 100644 240psuite/N64/assets/sonicback.png create mode 100644 240psuite/N64/assets/sonicfloor.png create mode 100644 240psuite/N64/tests.c create mode 100644 240psuite/N64/tests.h diff --git a/240psuite/N64/240pSuite.c b/240psuite/N64/240pSuite.c index 99592a4d..321f5aab 100644 --- a/240psuite/N64/240pSuite.c +++ b/240psuite/N64/240pSuite.c @@ -24,13 +24,14 @@ #include "font.h" #include "image.h" #include "controller.h" +#include "tests.h" void drawIntro(void); void drawPatternsMenu(void); void drawPatternsColorMenu(void); void drawPatternsGeometryMenu(void); void drawVideoTestsMenu(void); - + int main(void) { int sel = 1, reload = 1; @@ -40,7 +41,8 @@ int main(void) initN64(); loadFont(); - //drawIntro(); + drawIntro(); + while(1) { int c = 1, x = 55, y = 90; int r = 0xFF, g = 0xFF, b = 0xFF; @@ -67,7 +69,7 @@ int main(void) waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); if(keys.d_up) sel--; @@ -134,7 +136,7 @@ void drawPatternsMenu(void) waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); if(keys.d_up) sel--; @@ -213,7 +215,7 @@ void drawPatternsColorMenu(void) waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); if(keys.d_up) sel--; @@ -280,7 +282,7 @@ void drawPatternsGeometryMenu(void) waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); if(keys.d_up) sel--; @@ -356,7 +358,7 @@ void drawVideoTestsMenu(void) waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); if(keys.d_up) sel--; @@ -376,6 +378,9 @@ void drawVideoTestsMenu(void) switch(sel) { + case 5: + drawScroll(); + break; case 14: exit = 1; break; @@ -390,32 +395,90 @@ void drawVideoTestsMenu(void) freeImage(&sd); } -void drawIntro() +#define LOGO_HOLD 60 +#define FADE_STEPS 20 +#define FADE_HOLD 10 + +void fadeStep(uint16_t *colorRaw) +{ + color_t color; + + color = color_from_packed16(*colorRaw); + color.r = (color.r > 0) ? (color.r - color.r/FADE_STEPS) : 0; + color.g = (color.g > 0) ? (color.g - color.g/FADE_STEPS) : 0; + color.b = (color.b > 0) ? (color.b - color.b/FADE_STEPS) : 0; + *colorRaw = color_to_packed16(color); +} + +void drawSplash(char *name, int delay) { - int delay = 90; joypad_buttons_t keys; - sprite_t *ld = NULL; + sprite_t *logo = NULL; + uint16_t *pal = NULL; - ld = sprite_load("rom:/libdragon.sprite"); - if(!ld) + logo = sprite_load(name); + if(!logo) return; + pal = sprite_get_palette(logo); + if(!pal) + { + freeImage(&logo); + return; + } + while(delay) { getDisplay(); rdpqStart(); - rdpqDrawImage(ld, (dW - ld->width)/2, (dH - ld->height)/2); + rdpqClearScreen(); + rdpqDrawImage(logo, (dW - logo->width)/2, (dH - logo->height)/2); rdpqEnd(); - + waitVsync(); joypad_poll(); - keys = Controller_ButtonsDown(); + keys = controllerButtonsDown(); delay --; - if(keys.a || keys.b) + if(keys.a || keys.b || keys.start) delay = 0; } - freeImage(&ld); + delay = FADE_STEPS; + while(delay) { + getDisplay(); + + rdpqStart(); + rdpqClearScreen(); + rdpqDrawImage(logo, (dW - logo->width)/2, (dH - logo->height)/2); + rdpqEnd(); + + for(unsigned int c = 0; c < 16; c++) + fadeStep(&pal[c]); + + waitVsync(); + + delay --; + } + + delay = FADE_HOLD; + while(delay) { + getDisplay(); + + rdpqStart(); + rdpqClearScreen(); + rdpqEnd(); + + waitVsync(); + + delay --; + } + + freeImage(&logo); +} + +void drawIntro() +{ + drawSplash("rom:/libdragon.sprite", LOGO_HOLD); } \ No newline at end of file diff --git a/240psuite/N64/240pn64.pnproj b/240psuite/N64/240pn64.pnproj index c0b6d5b9..739c9378 100644 --- a/240psuite/N64/240pn64.pnproj +++ b/240psuite/N64/240pn64.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/240psuite/N64/Makefile b/240psuite/N64/Makefile index cca0dd77..6281312a 100644 --- a/240psuite/N64/Makefile +++ b/240psuite/N64/Makefile @@ -17,9 +17,13 @@ filesystem/%.sprite: assets/%.png @$(N64_MKSPRITE) $(MKSPRITE_FLAGS) -o filesystem "$<" filesystem/240pSuite-font.sprite: MKSPRITE_FLAGS= -filesystem/mainbg.sprite: MKSPRITE_FLAGS= +filesystem/mainbg.sprite: MKSPRITE_FLAGS=--format CI4 +filesystem/sd.sprite: MKSPRITE_FLAGS=--format CI4 filesystem/grid.sprite: MKSPRITE_FLAGS= filesystem/donna.sprite: MKSPRITE_FLAGS= +filesystem/sonicback.sprite: MKSPRITE_FLAGS=--format CI8 +filesystem/sonicfloor.sprite: MKSPRITE_FLAGS=--format CI4 +filesystem/libdragon.sprite: MKSPRITE_FLAGS=--format CI4 $(BUILD_DIR)/240pSuite.dfs: $(ASSETS_CONV) $(BUILD_DIR)/240pSuite.elf: $(src:%.c=$(BUILD_DIR)/%.o) diff --git a/240psuite/N64/assets/libdragon.png b/240psuite/N64/assets/libdragon.png index 9b17a68a39e2a0dae22a0f85967b45dcff44df43..797d733661ac92187a5fb7118e10559b3fb7cd2c 100644 GIT binary patch literal 1132 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 BACCY4 diff --git a/240psuite/N64/assets/sonicback.png b/240psuite/N64/assets/sonicback.png new file mode 100644 index 0000000000000000000000000000000000000000..00dfcac2374f60190041e5b8f59a33ea7fdaae1b GIT binary patch literal 5853 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 NmoHkGzA|==|9?lGidX;u literal 0 HcmV?d00001 diff --git a/240psuite/N64/assets/sonicfloor.png b/240psuite/N64/assets/sonicfloor.png new file mode 100644 index 0000000000000000000000000000000000000000..0244bb3fb116f14dd22e25dfe31f2d936be6295c GIT binary patch literal 4981 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 literal 0 HcmV?d00001 diff --git a/240psuite/N64/controller.c b/240psuite/N64/controller.c index 4e2c7671..62d60303 100644 --- a/240psuite/N64/controller.c +++ b/240psuite/N64/controller.c @@ -28,7 +28,7 @@ int JoyCountX = 0; int JoyCountY = 0; -joypad_buttons_t Controller_ButtonsDown() +joypad_buttons_t controllerButtonsDown() { joypad_inputs_t pad_inputs; joypad_buttons_t pad_pressed; @@ -84,13 +84,15 @@ joypad_buttons_t Controller_ButtonsDown() } } - //if(held.c[0].C_down && held.c[0].R && held.c[0].L) - //reset_video(); +#ifdef DEBUG_BENCHMARK + if(pad_pressed.l && pad_pressed.l) + resetVideo(); +#endif return pad_pressed; } -joypad_buttons_t Controller_ButtonsHeld() +joypad_buttons_t controllerButtonsHeld() { joypad_inputs_t pad_inputs; joypad_buttons_t pad_pressed; @@ -109,8 +111,10 @@ joypad_buttons_t Controller_ButtonsHeld() pad_pressed.d_up = 1; if (y < -JOYTHSHLD) pad_pressed.d_down = 1; - - //if(held.c[0].C_down && held.c[0].R && held.c[0].L) - //reset_video(); + +#ifdef DEBUG_BENCHMARK + if(pad_pressed.l && pad_pressed.l) + resetVideo(); +#endif return pad_pressed; } diff --git a/240psuite/N64/controller.h b/240psuite/N64/controller.h index f03c991d..da75094c 100644 --- a/240psuite/N64/controller.h +++ b/240psuite/N64/controller.h @@ -21,5 +21,5 @@ #include -joypad_buttons_t Controller_ButtonsDown(); -joypad_buttons_t Controller_ButtonsHeld(); \ No newline at end of file +joypad_buttons_t controllerButtonsDown(); +joypad_buttons_t controllerButtonsHeld(); \ No newline at end of file diff --git a/240psuite/N64/image.c b/240psuite/N64/image.c index 443950a6..1cb1c461 100644 --- a/240psuite/N64/image.c +++ b/240psuite/N64/image.c @@ -35,7 +35,8 @@ void rdpqDrawImage(sprite_t* tiles, float x, float y) { - rdpq_sprite_blit(tiles, x, y, NULL); + if(tiles) + rdpq_sprite_blit(tiles, x, y, NULL); } void rdpqClearScreen() diff --git a/240psuite/N64/tests.c b/240psuite/N64/tests.c new file mode 100644 index 00000000..c69d8477 --- /dev/null +++ b/240psuite/N64/tests.c @@ -0,0 +1,120 @@ +/* + * 240p Test Suite for Nintendo 64 + * Copyright (C)2024 Artemio Urbina + * + * This file is part of the 240p Test Suite + * + * The 240p Test Suite is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The 240p Test Suite is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 240p Test Suite; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "system.h" +#include "video.h" +#include "font.h" +#include "image.h" +#include "controller.h" +#include "tests.h" + +void drawScroll() +{ + int end = 0, x = 0, y = 0, currentframe = 0; + int speed = 1, acc = -1, pause = 0, vertical = 0; + sprite_t *sonicback, *overlay; + joypad_buttons_t keys; + + sonicback = sprite_load("rom:/sonicback.sprite"); + overlay = sprite_load("rom:/sonicfloor.sprite"); + + while(!end) + { + getDisplay(); + + rdpqStart(); + if(x > 0) + rdpqDrawImage(sonicback, x-256, 0); + rdpqDrawImage(sonicback, x, 0); + if(x < 64) + rdpqDrawImage(sonicback, x+256, 0); + + if(x > 0) + rdpqDrawImage(overlay, 2*x-256, 48); + rdpqDrawImage(overlay, 2*x, 48); + if(x < 64) + rdpqDrawImage(overlay, 2*x+256, 48); + // Extra gap + if(x < -96) + rdpqDrawImage(overlay, 2*x+512, 48); + + rdpqEnd(); + + waitVsync(); + + joypad_poll(); + keys = controllerButtonsDown(); + + if(keys.d_up) + speed += 1; + + if(keys.d_down) + speed -= 1; + + if(keys.b) + end = 1; + + if(keys.a) + pause = !pause; + + if(keys.c_left) + acc *= -1; + + if(keys.c_right) + vertical = !vertical; + + if(speed > 16) + speed = 16; + + if(speed < 1) + speed = 1; + + if(!pause) + { + if(!vertical) + x += speed * acc; + else + y -= speed * acc; + } + + if(x < -128) + x = 128 - speed; + if(x > 128) + x = -128 + speed; + + if(y < -7) + y = 0; + if(y > 7) + y = 0; + + if(!vertical) + { + currentframe ++; + if(currentframe > 10) + { + currentframe = 0; + } + } + } + + freeImage(&sonicback); + freeImage(&overlay); +} diff --git a/240psuite/N64/tests.h b/240psuite/N64/tests.h new file mode 100644 index 00000000..adc49ec4 --- /dev/null +++ b/240psuite/N64/tests.h @@ -0,0 +1,6 @@ +#ifndef TESTS_H +#define TESTS_H + +void drawScroll(); + +#endif \ No newline at end of file diff --git a/240psuite/N64/video.c b/240psuite/N64/video.c index e0053ef7..45635a5f 100644 --- a/240psuite/N64/video.c +++ b/240psuite/N64/video.c @@ -30,16 +30,14 @@ unsigned int dW = 320; unsigned int dH = 240; volatile display_context_t __dc = NULL; - volatile uint64_t __frames = 0; -// vblank callback so we can wait for vsync +#ifndef DEBUG_BENCHMARK void vblCallback(void) { __frames++; } -#ifndef DEBUG_BENCHMARK void getDisplay() { __dc = display_get(); @@ -57,30 +55,48 @@ void waitVsync() while (nextFrame > __frames) ; } + +void resetVideo() +{ +} #else -#define N64_FRAME_LEN 16.69 +#define N64_FRAME_LEN 16.715 static uint64_t __frameStart = 0; static uint64_t __idleStart = 0; -float __frameIdle = 0; +static uint64_t __lastVbl = 0; +float __frameIdle = N64_FRAME_LEN, __minIdle = N64_FRAME_LEN; float __frameLen = 0; +float __vblLen = 0; + +void vblCallback(void) +{ + uint64_t now = get_ticks_us(); + + __vblLen = (now - __lastVbl)/1000.0f; + __lastVbl = now; + __frames++; +} void drawFrameLens() { char str[100]; - sprintf(str, "Frame: %0.2fms Idle: %0.2fms", __frameLen, __frameIdle); - drawStringB(80, 4, __frameLen > N64_FRAME_LEN ? 0xff : 0x00, __frameLen < N64_FRAME_LEN ? 0xff : 0x00, 0x00, str); + if(__frameIdle < __minIdle) + __minIdle = __frameIdle; + + sprintf(str, "ALL:%0.3f IDL:%0.3f/%0.3f VBL:%0.3f", __frameLen, __frameIdle, __minIdle, __vblLen); + drawStringB(10, 4, __frameIdle < 4.0 ? 0xff : 0x00, __frameIdle > 4.0 ? 0xff : 0x00, 0x00, str); } void getDisplay() { - __dc = display_get(); __frameStart = get_ticks_us(); + __dc = display_get(); } void waitVsync() { - uint64_t nextFrame = __frames + 1, end = 0; + uint64_t nextFrame = __frames + 1; if(__dc) { @@ -91,9 +107,13 @@ void waitVsync() __idleStart = get_ticks_us(); while (nextFrame > __frames) ; - end = get_ticks_us(); - __frameLen = (end - __frameStart)/1000.0f; - __frameIdle = (end - __idleStart)/1000.0f; + __frameLen = (__lastVbl - __frameStart)/1000.0f; + __frameIdle = (__lastVbl - __idleStart)/1000.0f; +} + +void resetVideo() +{ + __minIdle = N64_FRAME_LEN; } #endif diff --git a/240psuite/N64/video.h b/240psuite/N64/video.h index 859c538c..69ae99a6 100644 --- a/240psuite/N64/video.h +++ b/240psuite/N64/video.h @@ -8,6 +8,7 @@ extern unsigned int dH; void getDisplay(); void waitVsync(); -void vblCallback(void); +void vblCallback(); +void resetVideo(); #endif \ No newline at end of file