From b28059d49afce8524361fa7597adcc9e108f4eab Mon Sep 17 00:00:00 2001 From: Xavi Del Campo Date: Tue, 3 Mar 2020 18:10:15 +0100 Subject: Renamed folders to lowercase --- src/EndAnimation.c | 233 ++++++++++++++++++++++ src/Font.c | 194 +++++++++++++++++++ src/Gfx.c | 552 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/LoadMenu.c | 204 ++++++++++++++++++++ src/Makefile | 74 +++++++ src/Serial.c | 278 +++++++++++++++++++++++++++ src/System.c | 493 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 161 ++++++++++++++++ 8 files changed, 2189 insertions(+) create mode 100644 src/EndAnimation.c create mode 100644 src/Font.c create mode 100644 src/Gfx.c create mode 100644 src/LoadMenu.c create mode 100644 src/Makefile create mode 100644 src/Serial.c create mode 100644 src/System.c create mode 100644 src/main.c (limited to 'src') diff --git a/src/EndAnimation.c b/src/EndAnimation.c new file mode 100644 index 0000000..027ca1a --- /dev/null +++ b/src/EndAnimation.c @@ -0,0 +1,233 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "EndAnimation.h" + +/* ************************************* + * Defines + * *************************************/ + +/* ************************************* + * Structs and enums + * *************************************/ + +enum +{ + END_ANIMATION_FADEOUT_STEP = 8, + + END_ANIMATION_LINE_STEP = 2, + + END_ANIMATION_SQUARES_SIZE_BITSHIFT = 5, + END_ANIMATION_SQUARES_SIZE = 32, + END_ANIMATION_SQUARES_PER_COLUMN = 8, + END_ANIMATION_SQUARES_PER_ROW = 12, + END_ANIMATION_SQUARES_TOTAL = END_ANIMATION_SQUARES_PER_COLUMN * + END_ANIMATION_SQUARES_PER_ROW, + + END_ANIMATION_SQUARES_TOTAL_MAX_INDEX = END_ANIMATION_SQUARES_TOTAL - 1, + + END_ANIMATION_SQUARES = 0, + END_ANIMATION_FADEOUT, + END_ANIMATION_LINE, + END_ANIMATION_MAX_RAND_VALUE = END_ANIMATION_LINE +}; + +/* ************************************* + * Local Prototypes + * *************************************/ + +static void EndAnimationSquares(void); +static void EndAnimationFadeOut(void); +static void EndAnimationLine(void); + +/* ************************************* + * Local Variables + * *************************************/ + +static GsRectangle EndAnimationRect; +static GsSprite EndAnimationDisplay; + +void EndAnimation(void) +{ + uint8_t randIndex = 0; + + GfxSaveDisplayData(&EndAnimationDisplay); + + GfxSetGlobalLuminance(NORMAL_LUMINANCE); + + if(SystemIsRandSeedSet() == false) + { + // Set default end animation + EndAnimationFadeOut(); + } + else + { + randIndex = rand() % (END_ANIMATION_MAX_RAND_VALUE + 1); + + switch(randIndex) + { + case END_ANIMATION_SQUARES: + EndAnimationSquares(); + break; + + case END_ANIMATION_FADEOUT: + EndAnimationFadeOut(); + break; + + case END_ANIMATION_LINE: + EndAnimationLine(); + break; + + default: + break; + } + } +} + +void EndAnimationFadeOut(void) +{ + uint8_t i; + + while(1) + { + if( GfxGetGlobalLuminance() > 0) + { + GfxSetGlobalLuminance(GfxGetGlobalLuminance() - END_ANIMATION_FADEOUT_STEP); + + GfxSortSprite(&EndAnimationDisplay);; + GfxDrawScene_Slow(); + } + else + { + GsSortCls(0,0,0); + + for(i = 0 ; i < 2 ; i++) + { + // Draw two frames to ensure black display + GfxDrawScene_Slow(); + } + + break; + } + } +} + +void EndAnimationLine(void) +{ + short rectIndex = 0; + + do + { + GfxSortSprite(&EndAnimationDisplay); + + // Draw upper half rectangle + + EndAnimationRect.x = 0; + EndAnimationRect.y = 0; + + EndAnimationRect.w = X_SCREEN_RESOLUTION; + EndAnimationRect.h = rectIndex; + + GsSortRectangle(&EndAnimationRect); + + EndAnimationRect.x = 0; + EndAnimationRect.y = Y_SCREEN_RESOLUTION - rectIndex; + + EndAnimationRect.w = X_SCREEN_RESOLUTION; + EndAnimationRect.h = rectIndex; + + GsSortRectangle(&EndAnimationRect); + + GfxDrawScene_Slow(); + + rectIndex += END_ANIMATION_LINE_STEP; + + }while(rectIndex <= (X_SCREEN_RESOLUTION >> 1) ); + +} + +void EndAnimationSquares(void) +{ + uint16_t i, j, k; + uint16_t randInd = 0; + bool sqPos[END_ANIMATION_SQUARES_TOTAL]; + uint16_t sqCounter = END_ANIMATION_SQUARES_TOTAL; + uint16_t maxIndex = END_ANIMATION_SQUARES_TOTAL_MAX_INDEX; + + EndAnimationRect.w = END_ANIMATION_SQUARES_SIZE; + EndAnimationRect.h = END_ANIMATION_SQUARES_SIZE; + + EndAnimationRect.r = 0; + EndAnimationRect.g = 0; + EndAnimationRect.b = 0; + + memset(sqPos, false , END_ANIMATION_SQUARES_TOTAL); + + for(i = 0; i < END_ANIMATION_SQUARES_TOTAL ; i++) + { + + do + { + randInd = SystemRand(0,maxIndex); + + /*dprintf("randInd = %d\t",randInd); + dprintf("sqPos[randInd] = %d\n", sqPos[randInd]);*/ + + if(sqPos[randInd] == false) + { + sqPos[randInd] = true; + sqCounter--; + + while(sqPos[maxIndex] == true) + { + // Lower maximum value for rand() so that it's + // easier to spot new empty index on next iteration. + maxIndex--; + } + + break; + } + else + { + if(sqCounter == 0) + { + break; + } + } + + }while(1); + + GfxSortSprite(&EndAnimationDisplay); + + if(sqCounter != 0) + { + for(j = 0; j < END_ANIMATION_SQUARES_TOTAL ; j++) + { + if(sqPos[j] == true) + { + EndAnimationRect.x = ((j) << END_ANIMATION_SQUARES_SIZE_BITSHIFT) - + (short)( (j / END_ANIMATION_SQUARES_PER_ROW) * + X_SCREEN_RESOLUTION); + + EndAnimationRect.y = (short) (j/ END_ANIMATION_SQUARES_PER_ROW) << + END_ANIMATION_SQUARES_SIZE_BITSHIFT; + + GsSortRectangle(&EndAnimationRect); + } + } + } + else + { + // Quick fix: draw a full black rectangle instead of multiple squares + for(k = 0 ; k < 2 ; k++) + { + // Draw two frames to ensure black display + GsSortCls(0,0,0); + GfxDrawScene_Slow(); + } + } + + GfxDrawScene_Slow(); + } +} diff --git a/src/Font.c b/src/Font.c new file mode 100644 index 0000000..c4b1491 --- /dev/null +++ b/src/Font.c @@ -0,0 +1,194 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Font.h" + +/* ************************************* + * Defines + * *************************************/ + +#define FONT_INTERNAL_TEXT_BUFFER_MAX_SIZE 200 + +/* ************************************* + * Local Prototypes + * *************************************/ + +/* ************************************* + * Local Variables + * *************************************/ + +static char _internal_text[FONT_INTERNAL_TEXT_BUFFER_MAX_SIZE]; +static unsigned char _blend_effect_lum; + +bool FontLoadImage(char* strPath, TYPE_FONT * ptrFont) +{ + if(GfxSpriteFromFile(strPath, &ptrFont->spr) == false) + { + return false; + } + + ptrFont->spr_w = ptrFont->spr.w; + ptrFont->spr_h = ptrFont->spr.h; + ptrFont->spr_u = ptrFont->spr.u; + ptrFont->spr_v = ptrFont->spr.v; + + //Now set default values to font + + ptrFont->char_w = FONT_DEFAULT_CHAR_SIZE; + ptrFont->char_h = FONT_DEFAULT_CHAR_SIZE; + + ptrFont->spr.attribute |= COLORMODE(COLORMODE_4BPP); + ptrFont->spr.attribute &= COLORMODE(~(COLORMODE_8BPP | COLORMODE_16BPP | COLORMODE_24BPP)); + ptrFont->spr.r = NORMAL_LUMINANCE; + ptrFont->spr.g = NORMAL_LUMINANCE; + ptrFont->spr.b = NORMAL_LUMINANCE; + + //At this point, spr.w and spr.h = real w/h + ptrFont->char_per_row = (uint8_t)(ptrFont->spr_w / ptrFont->char_w); + ptrFont->max_ch_wrap = 0; + + ptrFont->spr.w = ptrFont->char_w; + ptrFont->spr.h = ptrFont->char_h; + + ptrFont->flags = FONT_NOFLAGS; + + ptrFont->init_ch = FONT_DEFAULT_INIT_CHAR; + + dprintf("Sprite CX = %d, sprite CY = %d\n",ptrFont->spr.cx, ptrFont->spr.cy); + + return true; +} + +void FontSetInitChar(TYPE_FONT * ptrFont, char c) +{ + ptrFont->init_ch = c; +} + +void FontSetFlags(TYPE_FONT * ptrFont, FONT_FLAGS flags) +{ + ptrFont->flags = flags; +} + +void FontSetSize(TYPE_FONT * ptrFont, short size, short bitshift) +{ + ptrFont->char_w = size; + ptrFont->char_h = size; + + ptrFont->char_w_bitshift = bitshift; + + //At this point, spr.w and spr.h = real w/h + ptrFont->char_per_row = (uint8_t)(ptrFont->spr_w / ptrFont->char_w); + ptrFont->max_ch_wrap = 0; + + ptrFont->spr.w = ptrFont->char_w; + ptrFont->spr.h = ptrFont->char_h; +} + +void FontSetSpacing(TYPE_FONT* ptrFont, short spacing) +{ + ptrFont->char_spacing = spacing; +} + +void FontCyclic(void) +{ + _blend_effect_lum -= 8; +} + +void FontPrintText(TYPE_FONT * ptrFont, short x, short y, char* str, ...) +{ + uint16_t i; + uint16_t line_count = 0; + int result; + short orig_x = x; + + va_list ap; + + va_start(ap, str); + + result = vsnprintf( _internal_text, + FONT_INTERNAL_TEXT_BUFFER_MAX_SIZE, + str, + ap ); + + if(ptrFont->flags & FONT_H_CENTERED) + { + x = (X_SCREEN_RESOLUTION >> 1) - ((result >> 1) << ptrFont->char_w_bitshift); + orig_x = x; + } + + for(i = 0; i < result ; i++) + { + char _ch = _internal_text[i]; + + if(_ch == '\0') + { + // End of string + break; + } + + switch(_ch) + { + case ' ': + x += ptrFont->char_w; + continue; + case '\n': + x = orig_x; + y += ptrFont->char_h; + break; + default: + if( (ptrFont->flags & FONT_WRAP_LINE) && (ptrFont->max_ch_wrap != 0) ) + { + if(++line_count >= ptrFont->max_ch_wrap) + { + line_count = 0; + x = orig_x; + y += ptrFont->char_h; + } + } + + ptrFont->spr.x = x; + ptrFont->spr.y = y; + ptrFont->spr.w = ptrFont->char_w; + ptrFont->spr.h = ptrFont->char_h; + ptrFont->spr.u = (short)( (_ch - ptrFont->init_ch) % ptrFont->char_per_row) * ptrFont->char_w; + ptrFont->spr.u += ptrFont->spr_u; // Add original offset for image + ptrFont->spr.v = (short)( (_ch - ptrFont->init_ch) / ptrFont->char_per_row) * ptrFont->char_h; + ptrFont->spr.v += ptrFont->spr_v; // Add original offset for image + + if(ptrFont->flags & FONT_BLEND_EFFECT) + { + ptrFont->spr.r += 8; + ptrFont->spr.g += 8; + ptrFont->spr.b += 8; + } + else + { + ptrFont->spr.r = NORMAL_LUMINANCE; + ptrFont->spr.g = NORMAL_LUMINANCE; + ptrFont->spr.b = NORMAL_LUMINANCE; + } + /*dprintf("char_w = %d, char_h = %d, char_per_row = %d, init_ch: %c\n", + ptrFont->char_w, + ptrFont->char_h, + ptrFont->char_per_row, + ptrFont->init_ch); + dprintf("Char: %c, spr.u = %d, spr.v = %d\n",str[i],ptrFont->spr.u, ptrFont->spr.v); + dprintf("Sprite CX = %d, sprite CY = %d\n",ptrFont->spr.cx, ptrFont->spr.cy);*/ + //dprintf("Sprite rgb={%d,%d,%d}\n",ptrFont->spr.r, ptrFont->spr.g, ptrFont->spr.b); + + GfxSortSprite(&ptrFont->spr); + x += ptrFont->char_spacing; + break; + } + } + + if(ptrFont->flags & FONT_BLEND_EFFECT) + { + ptrFont->spr.r = _blend_effect_lum; + ptrFont->spr.g = _blend_effect_lum; + ptrFont->spr.b = _blend_effect_lum; + } + + va_end(ap); +} diff --git a/src/Gfx.c b/src/Gfx.c new file mode 100644 index 0000000..b9ffb63 --- /dev/null +++ b/src/Gfx.c @@ -0,0 +1,552 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Gfx.h" + +/* ************************************* + * Defines + * *************************************/ + +#define PRIMITIVE_LIST_SIZE 0x1000 +#define DOUBLE_BUFFERING_SWAP_Y 256 +#define UPLOAD_IMAGE_FLAG 1 +#define MAX_LUMINANCE 0xFF +#define ROTATE_BIT_SHIFT 12 +#define GPUSTAT (*(unsigned int*)0x1F801814) +#define D2_CHCR (*(unsigned int*)0x1F8010A8) + +/* ************************************* + * Structs and enums + * *************************************/ + +enum +{ + BUTTON_SIZE = 16, + + BUTTON_CROSS_U = 48, + BUTTON_CROSS_V = 0, + + BUTTON_SQUARE_U = 0, + BUTTON_SQUARE_V = 0, + + BUTTON_TRIANGLE_U = 32, + BUTTON_TRIANGLE_V = 0, + + BUTTON_CIRCLE_U = 16, + BUTTON_CIRCLE_V = 0, + + BUTTON_DIRECTION_U = 64, + BUTTON_DIRECTION_V = 0, + + BUTTON_LR_U = 80, + BUTTON_LR_V = 0, + BUTTON_LR_SIZE = 24, + + LETTER_SIZE = 8, + + LETTER_L1_U = 104, + LETTER_L1_V = 0, + + LETTER_L2_U = 112, + LETTER_L2_V = 0, + + LETTER_R1_U = 104, + LETTER_R1_V = 8, + + LETTER_R2_U = 112, + LETTER_R2_V = 8, + + LETTER_OFFSET_INSIDE_BUTTON_LR_X = 8, + LETTER_OFFSET_INSIDE_BUTTON_LR_Y = 6 + +}; + +enum +{ + GFX_SECOND_DISPLAY_X = 384, + GFX_SECOND_DISPLAY_Y = 256, + GFX_SECOND_DISPLAY_TPAGE = 22 +}; + +/* ************************************* + * Global Variables + * *************************************/ + +GsSprite PSXButtons; + +/* ************************************* + * Local Prototypes + * *************************************/ + + + +/* ************************************* + * Local Variables + * *************************************/ + +// Drawing environment +static GsDrawEnv DrawEnv; +// Display environment +static GsDispEnv DispEnv; +// Primitive list (it contains all the graphical data for the GPU) +static unsigned int prim_list[PRIMITIVE_LIST_SIZE]; +// Tells other modules whether data is being loaded to GPU +static volatile bool gfx_busy; +// Dictates (R,G,B) brigthness to all sprites silently +static uint8_t global_lum; +// When true, it draws a rectangle on top of all primitives with +// information for development purposes. +static bool GfxDevMenuEnableFlag; + +void GfxSwapBuffers(void) +{ + // Consistency check +#if PSXSDK_DEBUG + + if(GsListPos() >= PRIMITIVE_LIST_SIZE) + { + dprintf("Linked list iterator overflow!\n"); + while(1); + } + + if( (DrawEnv.h != Y_SCREEN_RESOLUTION) + || + ( (DrawEnv.w != X_SCREEN_RESOLUTION) + && + (DrawEnv.w != X_SCREEN_RESOLUTION >> 1) ) + || + ( (DispEnv.y != DOUBLE_BUFFERING_SWAP_Y) + && + (DispEnv.y != 0) ) ) + { + dprintf("What the hell is happening?\n"); + DEBUG_PRINT_VAR(DispEnv.x); + DEBUG_PRINT_VAR(DispEnv.y); + DEBUG_PRINT_VAR(DrawEnv.x); + DEBUG_PRINT_VAR(DrawEnv.y); + + while(1); + } +#endif // PSXSDK_DEBUG + + if(DrawEnv.h == Y_SCREEN_RESOLUTION) + { + if(DispEnv.y == 0) + { + DispEnv.y = DOUBLE_BUFFERING_SWAP_Y; + DrawEnv.y = 0; + } + else if(DispEnv.y == DOUBLE_BUFFERING_SWAP_Y) + { + DispEnv.y = 0; + DrawEnv.y = DOUBLE_BUFFERING_SWAP_Y; + } + + GsSetDispEnv(&DispEnv); + GsSetDrawEnv(&DrawEnv); + } +} + +void GfxDevMenuEnable(void) +{ + GfxDevMenuEnableFlag = true; +} + +void GfxInitDrawEnv(void) +{ + DrawEnv.x = 0; + DrawEnv.y = 0; + DrawEnv.draw_on_display = false; + DrawEnv.w = X_SCREEN_RESOLUTION; + DrawEnv.h = Y_SCREEN_RESOLUTION; + + GsSetDrawEnv(&DrawEnv); +} + +void GfxInitDispEnv(void) +{ + DispEnv.x = 0; + DispEnv.y = 0; + + GsSetDispEnv(&DispEnv); +} + +void GfxSetPrimitiveList(void) +{ + GsSetList(prim_list); +} + +void GfxDrawScene_Fast(void) +{ + GfxSwapBuffers(); + FontCyclic(); + GsDrawList(); +} + +bool GfxReadyForDMATransfer(void) +{ + return ( (GPUSTAT & 1<<28) && !(D2_CHCR & 1<<24) ); +} + +void GfxDrawScene(void) +{ + while( (SystemRefreshNeeded() == false) + || + (GfxIsGPUBusy() == true) ); + + GfxDrawScene_Fast(); + + SystemCyclicHandler(); +} + +void GfxDrawScene_Slow(void) +{ + GfxDrawScene(); + while(GfxIsGPUBusy() == true); +} + +void GfxSortSprite(GsSprite * spr) +{ + uint8_t aux_r = spr->r; + uint8_t aux_g = spr->g; + uint8_t aux_b = spr->b; + unsigned char aux_tpage = spr->tpage; + short aux_w = spr->w; + short aux_x = spr->x; + + if( (spr->w <= 0) || (spr->h <= 0) ) + { + // Invalid width or heigth + return; + } + + if(GfxIsSpriteInsideScreenArea(spr) == false) + { + return; + } + + if(global_lum != NORMAL_LUMINANCE) + { + if(spr->r < NORMAL_LUMINANCE - global_lum) + { + spr->r = 0; + } + else + { + spr->r -= NORMAL_LUMINANCE - global_lum; + } + + if(spr->g < NORMAL_LUMINANCE - global_lum) + { + spr->g = 0; + } + else + { + spr->g -= NORMAL_LUMINANCE - global_lum; + } + + if(spr->b < NORMAL_LUMINANCE - global_lum) + { + spr->b = 0; + } + else + { + spr->b -= NORMAL_LUMINANCE - global_lum; + } + } + + if(spr->w > MAX_SIZE_FOR_GSSPRITE) + { + // GsSprites can't be bigger than 256x256, so since display + // resolution is 384x240, it must be split into two primitives. + + spr->w = MAX_SIZE_FOR_GSSPRITE; + GsSortSprite(spr); + + spr->x += MAX_SIZE_FOR_GSSPRITE; + spr->w = X_SCREEN_RESOLUTION - MAX_SIZE_FOR_GSSPRITE; + spr->tpage += MAX_SIZE_FOR_GSSPRITE / GFX_TPAGE_WIDTH; + GsSortSprite(spr); + + // Restore original values after sorting + spr->w = aux_w; + spr->tpage = aux_tpage; + spr->x = aux_x; + } + else + { + GsSortSprite(spr); + } + + spr->r = aux_r; + spr->g = aux_g; + spr->b = aux_b; +} + +uint8_t GfxGetGlobalLuminance(void) +{ + return global_lum; +} + +void GfxSetGlobalLuminance(uint8_t value) +{ + global_lum = value; +} + +void GfxIncreaseGlobalLuminance(int8_t step) +{ + if( ( (global_lum + step) < MAX_LUMINANCE ) + && + ( (global_lum + step) > 0 ) ) + { + global_lum += step; + } + else + { + global_lum = MAX_LUMINANCE; + } +} + +int GfxRotateFromDegrees(int deg) +{ + return deg << ROTATE_BIT_SHIFT; +} + +bool GfxIsGPUBusy(void) +{ + return (GsIsDrawing() || gfx_busy || (GfxReadyForDMATransfer() == false) ); +} + +bool GfxSpriteFromFile(char* fname, GsSprite * spr) +{ + GsImage gsi; + + if(SystemLoadFile(fname) == false) + { + return false; + } + + while(GfxIsGPUBusy() == true); + + gfx_busy = true; + + GsImageFromTim(&gsi,SystemGetBufferAddress() ); + + GsSpriteFromImage(spr,&gsi,UPLOAD_IMAGE_FLAG); + gfx_busy = false; + + DEBUG_PRINT_VAR(spr->tpage); + DEBUG_PRINT_VAR(spr->u); + DEBUG_PRINT_VAR(spr->v); + DEBUG_PRINT_VAR(spr->w); + DEBUG_PRINT_VAR(spr->h); + + return true; +} + +bool GfxCLUTFromFile(char* fname) +{ + GsImage gsi; + + if(SystemLoadFile(fname) == false) + { + return false; + } + + while(GfxIsGPUBusy() == true); + + gfx_busy = true; + + GsImageFromTim(&gsi,SystemGetBufferAddress() ); + + GsUploadCLUT(&gsi); + + gfx_busy = false; + + return true; +} + +bool GfxIsInsideScreenArea(short x, short y, short w, short h) +{ + if( ( (x + w) >= 0) + && + (x < DrawEnv.w) + && + ( (y + h) >= 0) + && + (y < DrawEnv.h) ) + { + return true; + } + + return false; +} + +bool GfxIsSpriteInsideScreenArea(GsSprite * spr) +{ + return GfxIsInsideScreenArea(spr->x, spr->y, spr->w, spr->h); +} + +void GfxButtonSetFlags(uint8_t flags) +{ + PSXButtons.attribute |= flags; +} + +void GfxButtonRemoveFlags(uint8_t flags) +{ + PSXButtons.attribute &= ~flags; +} + +void GfxDrawButton(short x, short y, unsigned short btn) +{ + static bool first_entered = true; + static short orig_u; + static short orig_v; + + if(first_entered == true) + { + first_entered = false; + orig_u = PSXButtons.u; + orig_v = PSXButtons.v; + } + + PSXButtons.w = BUTTON_SIZE; + PSXButtons.h = BUTTON_SIZE; + + PSXButtons.r = NORMAL_LUMINANCE; + PSXButtons.g = NORMAL_LUMINANCE; + PSXButtons.b = NORMAL_LUMINANCE; + + PSXButtons.x = x; + PSXButtons.y = y; + PSXButtons.mx = PSXButtons.w >> 1; + PSXButtons.my = PSXButtons.h >> 1; + + switch(btn) + { + case PAD_CROSS: + PSXButtons.u = BUTTON_CROSS_U; + PSXButtons.v = BUTTON_CROSS_V; + break; + + case PAD_SQUARE: + PSXButtons.u = BUTTON_SQUARE_U; + PSXButtons.v = BUTTON_SQUARE_V; + break; + + case PAD_TRIANGLE: + PSXButtons.u = BUTTON_TRIANGLE_U; + PSXButtons.v = BUTTON_TRIANGLE_V; + break; + + case PAD_CIRCLE: + PSXButtons.u = BUTTON_CIRCLE_U; + PSXButtons.v = BUTTON_CIRCLE_V; + break; + + case PAD_RIGHT: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + break; + + case PAD_UP: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.rotate = 90 << ROTATE_BIT_SHIFT; + break; + + case PAD_DOWN: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.rotate = 270 << ROTATE_BIT_SHIFT; + break; + + case PAD_LEFT: + PSXButtons.u = BUTTON_DIRECTION_U; + PSXButtons.v = BUTTON_DIRECTION_V; + PSXButtons.attribute |= H_FLIP; + break; + + case PAD_L1: + // Fall through + case PAD_L2: + // Fall through + case PAD_R1: + // Fall through + case PAD_R2: + PSXButtons.u = BUTTON_LR_U; + PSXButtons.v = BUTTON_LR_V; + PSXButtons.w = BUTTON_LR_SIZE; + break; + + case PAD_SELECT: + // Fall through + case PAD_START: + // Fall through + default: + // Set null width and height so that sprite doesn't get sorted + PSXButtons.w = 0; + PSXButtons.h = 0; + break; + } + + PSXButtons.u += orig_u; + PSXButtons.v += orig_v; + + GfxSortSprite(&PSXButtons); + + PSXButtons.attribute &= ~H_FLIP; + PSXButtons.rotate = 0; +} + +void GfxSaveDisplayData(GsSprite *spr) +{ + while(GfxIsGPUBusy() == true); + + MoveImage( DispEnv.x, + DispEnv.y, + GFX_SECOND_DISPLAY_X, + GFX_SECOND_DISPLAY_Y, + X_SCREEN_RESOLUTION, + Y_SCREEN_RESOLUTION); + + spr->x = 0; + spr->y = 0; + spr->tpage = GFX_SECOND_DISPLAY_TPAGE; + spr->attribute |= COLORMODE(COLORMODE_16BPP); + spr->w = X_SCREEN_RESOLUTION; + spr->h = Y_SCREEN_RESOLUTION; + spr->u = 0; + spr->v = 0; + spr->r = NORMAL_LUMINANCE; + spr->g = NORMAL_LUMINANCE; + spr->b = NORMAL_LUMINANCE; + + while(GfxIsGPUBusy() == true); +} + +bool GfxTPageOffsetFromVRAMPosition(GsSprite * spr, short x, short y) +{ + if( (x >= VRAM_W) || (x < 0) || (y >= VRAM_H) || (y < 0) ) + { + return false; + } + + spr->tpage = x / GFX_TPAGE_WIDTH; + spr->tpage += (short)(VRAM_W / GFX_TPAGE_WIDTH) * (short)(y / GFX_TPAGE_HEIGHT); + + spr->u = (x % GFX_TPAGE_WIDTH); + + if(spr->attribute & COLORMODE(COLORMODE_8BPP)) + { + // On 8bpp images, it looks like U offset needs to be multiplied by 2. + spr->u <<= 1; + } + + spr->v = (y % GFX_TPAGE_HEIGHT); + + //dprintf("Sprite:\n\tTPAGE: %d\n\tU=%d\n\tV=%d\n",spr->tpage,spr->u, spr->v); + + return false; +} diff --git a/src/LoadMenu.c b/src/LoadMenu.c new file mode 100644 index 0000000..ba8efaf --- /dev/null +++ b/src/LoadMenu.c @@ -0,0 +1,204 @@ +/* ************************************** + * Includes * + * *************************************/ + +#include "LoadMenu.h" + +/* ************************************** + * Defines * + * *************************************/ + +/* ************************************** + * Structs and enums * + * *************************************/ + +enum +{ + SMALL_FONT_SIZE = 8, + SMALL_FONT_SIZE_BITSHIFT = 3, + SMALL_FONT_SPACING = 6 +}; + +enum +{ + BG_BLUE_TARGET_VALUE = 0xC0, + BG_WHITE_TARGET_VALUE = /*0x40*/ 0, + BG_INCREASE_STEP = 0x10 +}; + +enum +{ + LOADING_BAR_X = 64, + LOADING_BAR_Y = 200, + LOADING_BAR_N_LINES = 4, + + LOADING_BAR_WIDTH = 256, + LOADING_BAR_HEIGHT = 16, + + LOADING_BAR_LUMINANCE_TARGET = NORMAL_LUMINANCE, + LOADING_BAR_LUMINANCE_STEP = 10 +}; + +enum +{ + LOADING_TITLE_CLUT_X = 384, + LOADING_TITLE_CLUT_Y = 496, + LOADING_TITLE_X = 128, + LOADING_TITLE_Y = 32, + + LOADING_TITLE_U = 0, + LOADING_TITLE_V = 0, + + LOADING_TITLE_LUMINANCE_STEP = 10, + LOADING_TITLE_LUMINANCE_TARGET = NORMAL_LUMINANCE +}; + +enum +{ + PLANE_START_X = 56, + PLANE_START_Y = 200, + + PLANE_U = 0, + PLANE_V = 32, + PLANE_SIZE = 16, + + PLANE_LUMINANCE_STEP = 0x10, + PLANE_LUMINANCE_TARGET_VALUE = NORMAL_LUMINANCE +}; + +/* ************************************* + * Local Prototypes + * *************************************/ + +static void LoadMenuLoadFileList( char* fileList[], void * dest[], + uint8_t szFileList, uint8_t szDestList); + +/* ************************************* + * Local Variables + * *************************************/ + +static char* LoadMenuFiles[] = { "cdrom:\\DATA\\FONTS\\FONT_2.FNT;1" }; + +static void * LoadMenuDest[] = { (TYPE_FONT*)&SmallFont }; + +static char* strCurrentFile; + +// Flags to communicate with ISR state +// * startup_flag: background fades in from black to blue. +// * end_flag: tells the background to fade out to black. +// * isr_ended: background has totally faded out to black. +// * isr_started: tells the ISR has finished starting up. +static volatile bool startup_flag; +static volatile bool isr_started; +static volatile bool end_flag; +static volatile bool isr_ended; +// Set to true when LoadMenuInit() has been called, and set to false +// once LoadMenuEnd() is called. +// It's used when multiple modules call LoadMenu() at the same time, +// so load menu does not have to be initialised each time; +static bool load_menu_running; + +void LoadMenuInit(void) +{ + static bool first_load = false; + + if(first_load == false) + { + first_load = true; + LoadMenuLoadFileList( LoadMenuFiles, + LoadMenuDest, + sizeof(LoadMenuFiles) / sizeof(char*), + sizeof(LoadMenuDest) / sizeof(void*)); + } + + FontSetSize(&SmallFont, SMALL_FONT_SIZE, SMALL_FONT_SIZE_BITSHIFT); + FontSetSpacing(&SmallFont, SMALL_FONT_SPACING); + + SmallFont.spr.r = 0; + SmallFont.spr.g = 0; + SmallFont.spr.b = 0; + + GfxSetGlobalLuminance(NORMAL_LUMINANCE); +} + +void LoadMenu( char* fileList[], + void * dest[], + uint8_t szFileList , uint8_t szDestList) +{ + + if(load_menu_running == false) + { + LoadMenuInit(); + } + + LoadMenuLoadFileList(fileList,dest,szFileList,szDestList); +} + +void LoadMenuLoadFileList( char* fileList[], void * dest[], + uint8_t szFileList, uint8_t szDestList) +{ + char aux_file_name[100]; + char* extension; + uint8_t fileLoadedCount; + + if(szFileList != szDestList) + { + dprintf("File list size different from dest list size! %d vs %d\n", + szFileList, szDestList); + return; + } + + for(fileLoadedCount = 0; fileLoadedCount < szFileList ; fileLoadedCount++) + { + if(fileList[fileLoadedCount] == NULL) + { + continue; + } + + strCurrentFile = fileList[fileLoadedCount]; + + //dprintf("Files %d / %d loaded. New plane X = %d.\n",fileLoadedCount,szFileList,LoadMenuPlaneSpr.x); + + // Backup original file path + strncpy(aux_file_name,fileList[fileLoadedCount],100); + + //We want to get file extension, so split into tokens + strtok(fileList[fileLoadedCount],".;"); + extension = strtok(NULL,".;"); + + dprintf("File extension: .%s\n",extension); + //Restore original file path in order to load file + strncpy(fileList[fileLoadedCount],aux_file_name,100); + + if(strncmp(extension,"TIM",3) == 0) + { + if(GfxSpriteFromFile(fileList[fileLoadedCount], dest[fileLoadedCount]) == false) + { + dprintf("Could not load image file \"%s\"!\n",fileList[fileLoadedCount]); + } + } + else if(strncmp(extension,"CLT",3) == 0) + { + if(dest[fileLoadedCount] != NULL) + { + dprintf("WARNING: File %s linked to non-NULL destination pointer!\n", dest[fileLoadedCount]); + } + + if(GfxCLUTFromFile(fileList[fileLoadedCount]) == false) + { + dprintf("Could not load CLUT file \"%s\"!\n",fileList[fileLoadedCount]); + } + } + else if(strncmp(extension,"FNT",3) == 0) + { + if(FontLoadImage(fileList[fileLoadedCount], dest[fileLoadedCount]) == false) + { + dprintf("Could not load font file \"%s\"!\n",fileList[fileLoadedCount]); + } + } + else + { + dprintf("LoadMenu does not recognize following extension: %s\n",extension); + } + } +} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..f20974f --- /dev/null +++ b/src/Makefile @@ -0,0 +1,74 @@ +CC = psxsdkserial-gcc +DEFINE= -D_PAL_MODE_ +DEFINE += -DPSXSDK_DEBUG +LIBS=-lfixmath +CC_FLAGS = -Wall -Werror -c -Os -Wfatal-errors -g +LINKER = psxsdkserial-gcc + +PROJECT = OPENSEND +PROJECT_DIR = ~/OpenSend + +INIT_ADDR=0x801A0000 + +ELF2EXE = elf2exe +ELF2EXE_FLAGS = -mark="Open-source PSX-EXE loader created with PSXSDK" -init_addr=$(INIT_ADDR) +LICENSE_FILE = /usr/local/psxsdk/share/licenses/infoeur.dat + +PSXSDK_DIR = /usr/local/psxsdk/bin + +EMULATOR_DIR = ~/pcsxr +EMULATOR = pcsxr.exe +SOUND_INTERFACE = +EMULATOR_FLAGS = -nogui -psxout +OBJ_DIR = Obj +SRC_DIR = . +MUSIC_TRACKS = +#FFMPEG = ffmpeg +#FFMPEG_DIR = ../Music/ffmpeg/bin +#FFMPEG_FLAGS = -f s16le -acodec pcm_s16le + +GNU_SIZE = mipsel-unknown-elf-size + +all: build image clean +#emulator clean + +rebuild: remove build + +build: clean objects $(PROJECT).elf $(PROJECT).exe + +objects: $(addprefix $(OBJ_DIR)/,main.o System.o Gfx.o \ + LoadMenu.o EndAnimation.o \ + Font.o Serial.o) + +remove: + rm -f Obj/*.o + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c + $(CC) $< -o $@ $(DEFINE) $(CC_FLAGS) + +$(PROJECT).elf: + $(LINKER) Obj/*.o -o Exe/$(PROJECT).elf $(LIBS) -Wl,--gc-sections + +$(PROJECT).exe: + $(ELF2EXE) Exe/$(PROJECT).elf Exe/$(PROJECT).exe $(ELF2EXE_FLAGS) + cp Exe/$(PROJECT).exe ../cdimg + +image: + rm -f $(PROJECT).iso $(PROJECT).bin + rm -f $(PROJECT).cue + mkisofs -o $(PROJECT).iso -V $(PROJECT) -sysid PLAYSTATION ../cdimg + mkpsxiso $(PROJECT).iso $(PROJECT).bin $(LICENSE_FILE) + mv $(PROJECT).bin ../Bin + mv $(PROJECT).cue ../Bin + rm -f $(PROJECT).cue + rm -f $(PROJECT).iso + $(GNU_SIZE) Exe/$(PROJECT).elf + +emulator: + export PATH=$$PATH:$(EMULATOR_DIR) + $(EMULATOR) -cdfile $(PROJECT_DIR)/Bin/$(PROJECT).bin $(EMULATOR_FLAGS) + +clean: + rm -f $(PROJECT).elf cdimg/$(PROJECT).exe $(PROJECT).bin $(PROJECT).cue cdimg/README.txt + rm -f $(PROJECT).iso $(PROJECT).exe $(PROJECT).elf + diff --git a/src/Serial.c b/src/Serial.c new file mode 100644 index 0000000..2e2a1a4 --- /dev/null +++ b/src/Serial.c @@ -0,0 +1,278 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Serial.h" + +/* ************************************* + * Defines + * *************************************/ + +#define SERIAL_BAUDRATE 115200 +#define SERIAL_TX_RX_TIMEOUT 20000 +#define SERIAL_RX_FIFO_EMPTY 0 +#define SERIAL_TX_NOT_READY 0 + +/* ************************************* + * Local Variables + * *************************************/ + +static volatile SERIAL_STATE SerialState; +static volatile size_t bytesRead; +static volatile uint32_t initPC_Address; +static volatile uint32_t RAMDest_Address; +static volatile size_t ExeSize; +static volatile size_t totalBytes; +static volatile size_t exeBytesRead; +static volatile bool serial_busy; + +/* ************************************* + * Local Prototypes + * *************************************/ + +void ISR_Serial(void) +{ + enum + { + SERIAL_BG_X0 = 0, + SERIAL_BG_X1 = X_SCREEN_RESOLUTION - SERIAL_BG_X0, + SERIAL_BG_X2 = SERIAL_BG_X0, + SERIAL_BG_X3 = SERIAL_BG_X1, + + SERIAL_BG_Y0 = 0, + SERIAL_BG_Y1 = SERIAL_BG_Y0, + SERIAL_BG_Y2 = Y_SCREEN_RESOLUTION - SERIAL_BG_Y0, + SERIAL_BG_Y3 = SERIAL_BG_Y2, + + SERIAL_BG_R = 0, + SERIAL_BG_G = NORMAL_LUMINANCE, + SERIAL_BG_B = NORMAL_LUMINANCE, + }; + + static GsGPoly4 SerialBg = { .x[0] = SERIAL_BG_X0, + .x[1] = SERIAL_BG_X1, + .x[2] = SERIAL_BG_X2, + .x[3] = SERIAL_BG_X3, + + .y[0] = SERIAL_BG_Y0, + .y[1] = SERIAL_BG_Y1, + .y[2] = SERIAL_BG_Y2, + .y[3] = SERIAL_BG_Y3, + + .r[0] = 0, + .r[1] = 0, + .r[2] = SERIAL_BG_R, + .r[3] = SERIAL_BG_R, + + .g[0] = 0, + .g[1] = 0, + .g[2] = SERIAL_BG_G, + .g[3] = SERIAL_BG_G, + + .b[0] = 0, + .b[1] = 0, + .b[2] = SERIAL_BG_B, + .b[3] = SERIAL_BG_B, }; + + enum + { + SERIAL_STATE_TEXT_X = 148, + SERIAL_STATE_TEXT_Y = Y_SCREEN_RESOLUTION >> 1, + }; + + SystemIncreaseGlobalTimer(); + + if( (GfxIsGPUBusy() == true) || (SystemIsBusy() == true) ) + { + return; + } + + FontSetFlags(&SmallFont, FONT_BLEND_EFFECT | FONT_H_CENTERED); + + if(SerialState == SERIAL_STATE_READING_EXE_DATA) + { + if(System1SecondTick() == false) + { + return; + } + else + { + FontSetFlags(&SmallFont, FONT_H_CENTERED); + } + } + + GsSortGPoly4(&SerialBg); + + switch(SerialState) + { + case SERIAL_STATE_INIT: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Serial initialization"); + break; + + case SERIAL_STATE_STANDBY: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Waiting for PC..."); + break; + + case SERIAL_STATE_WRITING_ACK: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Writing ACK"); + break; + + case SERIAL_STATE_READING_HEADER: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Reading data from header (%d/%d bytes)...", bytesRead, totalBytes); + break; + + case SERIAL_STATE_READING_EXE_SIZE: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Getting PSX-EXE size from PC..."); + break; + + case SERIAL_STATE_READING_EXE_DATA: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Reading PSX-EXE data (%d/%d bytes)...", exeBytesRead, ExeSize); + break; + + case SERIAL_STATE_WAITING_USER_INPUT: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Press any key to continue"); + break; + + case SERIAL_STATE_CLEANING_MEMORY: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Cleaning RAM before EXE data transfer..."); + break; + + default: + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y, "Unknown state"); + break; + } + + FontSetFlags(&SmallFont, FONT_H_CENTERED); + + if(RAMDest_Address != 0) + { + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y + 16, "RAM Dest address: 0x%08X", RAMDest_Address); + } + + if(initPC_Address != 0) + { + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y + 32, "Init PC address: 0x%08X", initPC_Address); + } + + if(ExeSize != 0) + { + FontPrintText(&SmallFont, SERIAL_STATE_TEXT_X, SERIAL_STATE_TEXT_Y + 48, "PSX-EXE size: 0x%08X", ExeSize); + } + + GfxDrawScene_Fast(); +} + +void SerialSetState(SERIAL_STATE state) +{ + SerialState = state; +} + +void SerialSetPCAddress(uint32_t addr) +{ + initPC_Address = addr; +} + +void SerialSetRAMDestAddress(uint32_t addr) +{ + RAMDest_Address = addr; +} + +void SerialSetExeSize(size_t size) +{ + ExeSize = size; +} + +void SerialInit(void) +{ + uint8_t receivedBytes; + + SetVBlankHandler(&ISR_Serial); + + SerialState = SERIAL_STATE_INIT; + + SIOStart(SERIAL_BAUDRATE); + + SerialState = SERIAL_STATE_STANDBY; + + // ------------------------------------ + // Protocol description + // ------------------------------------ + + // 1. Wait to receive magic byte "99" from PC. + + SerialRead(&receivedBytes, sizeof(uint8_t) ); + + if(receivedBytes != 99) + { + dprintf("Did not receive input magic number!\n"); + return; + } + + // 2. Send ACK (magic byte is ASCII code for 'b'). + + SerialState = SERIAL_STATE_WRITING_ACK; + + SerialWrite(ACK_BYTE_STRING, sizeof(uint8_t) ); +} + +void SerialSetExeBytesReceived(uint32_t bytes_read) +{ + exeBytesRead += bytes_read; +} + +bool SerialRead(uint8_t* ptrArray, size_t nBytes) +{ + bytesRead = 0; + totalBytes = nBytes; + + serial_busy = true; + + if(nBytes == 0) + { + SerialWrite("SerialRead: invalid size %d\n", strnlen("SerialRead: invalid size %d\n", 30)); + return false; + } + + do + { + //uint16_t timeout = SERIAL_TX_RX_TIMEOUT; + + while( (SIOCheckInBuffer() == SERIAL_RX_FIFO_EMPTY)); // Wait for RX FIFO not empty + + *(ptrArray++) = SIOReadByte(); + bytesRead++; + }while(--nBytes); + + serial_busy = false; + + return true; +} + +bool SerialWrite(void* ptrArray, size_t nBytes) +{ + serial_busy = true; + + SystemDisableVBlankInterrupt(); + + if(nBytes == 0) + { + SerialWrite("SerialRead: invalid size %d\n", strnlen("SerialRead: invalid size %d\n", 30)); + return false; + } + + do + { + //uint16_t timeout = SERIAL_TX_RX_TIMEOUT; + + while( (SIOCheckOutBuffer() == SERIAL_TX_NOT_READY)); // Wait for TX FIFO empty. + + SIOSendByte(*(uint8_t*)ptrArray++); + + }while(--nBytes); + + SystemEnableVBlankInterrupt(); + + serial_busy = false; + + return true; +} diff --git a/src/System.c b/src/System.c new file mode 100644 index 0000000..852fea1 --- /dev/null +++ b/src/System.c @@ -0,0 +1,493 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "System.h" + +/* ************************************* + * Defines + * *************************************/ + +#define SYSTEM_MAX_TIMERS 16 +#define FILE_BUFFER_SIZE 0xC40 +#define END_STACK_PATTERN (uint32_t) 0x18022015 +#define BEGIN_STACK_ADDRESS (uint32_t*) 0x801FFF00 +#define STACK_SIZE 0x1000 +#define I_MASK (*(volatile unsigned int*)0x1F801074) + +/* ************************************* + * Local Prototypes + * *************************************/ + +static void SystemSetStackPattern(void); + +/* ************************************* + * Local Variables + * *************************************/ + +//Buffer to store any kind of files. It supports files up to 128 kB +static uint8_t file_buffer[FILE_BUFFER_SIZE]; +//Global timer (called by interrupt) +static volatile uint64_t global_timer; +//Tells whether rand seed has been set +static bool rand_seed; +//Screen refresh flag (called by interrupt) +static volatile bool refresh_needed; +//Timers +static bool one_second_timer; +//Critical section is entered (i.e.: when accessing fopen() or other BIOS functions +static volatile bool system_busy; + +/* ******************************************************************* + * + * @name: void SystemInit(void) + * + * @author: Xavier Del Campo + * + * @brief: Calls main intialization routines. + * + * @remarks: To be called before main loop. + * + * *******************************************************************/ + +void SystemInit(void) +{ + //Reset global timer + global_timer = 0; + //Reset 1 second timer + one_second_timer = 0; + //PSXSDK init + PSX_InitEx(PSX_INIT_SAVESTATE | PSX_INIT_CD); + //Graphics init + GsInit(); + //Clear VRAM + GsClearMem(); + //Set Video Resolution +#ifdef _PAL_MODE_ + GsSetVideoMode(X_SCREEN_RESOLUTION, Y_SCREEN_RESOLUTION, VMODE_PAL); +#else + GsSetVideoMode(X_SCREEN_RESOLUTION, Y_SCREEN_RESOLUTION, VMODE_NTSC); +#endif //_PAL_MODE_ + //SPU init + SsInit(); + //Set Drawing Environment + GfxInitDrawEnv(); + //Set Display Environment + GfxInitDispEnv(); + //Set Primitive List + GfxSetPrimitiveList(); + //Initial value for system_busy + system_busy = false; + + GfxSetGlobalLuminance(NORMAL_LUMINANCE); + + SystemSetStackPattern(); +} + +size_t SystemGetBufferSize(void) +{ + return sizeof(file_buffer); +} + +/* ******************************************************************* + * + * @name: void SystemInit(void) + * + * @author: Xavier Del Campo + * + * @brief: + * Calls srand() while avoiding multiple calls by setting internal + * variable rand_seed to true. Internal variable "global_timer" is + * used to generate the new seed. + * + * @remarks: + * It is recommended to call it once user has pressed any key. + * + * *******************************************************************/ + +void SystemSetRandSeed(void) +{ + if(rand_seed == false) + { + rand_seed = true; + //Set random seed using global timer as reference + srand((unsigned int)global_timer); + + dprintf("Seed used: %d\n",(unsigned int)global_timer); + } +} + +/* ******************************************************************* + * + * @name: bool SystemIsRandSeedSet(void) + * + * @author: Xavier Del Campo + * + * @brief: + * Reportedly, returns whether rand seed has already been set. + * + * @remarks: + * + * @return: + * Reportedly, returns whether rand seed has already been set. + * + * *******************************************************************/ + +bool SystemIsRandSeedSet(void) +{ + return rand_seed; +} + +/* ******************************************************************* + * + * @name: bool SystemRefreshNeeded(void) + * + * @author: Xavier Del Campo + * + * @brief: + * + * @remarks: + * + * @return: + * Returns whether VSync flag has been enabled. + * + * *******************************************************************/ + +bool SystemRefreshNeeded(void) +{ + return refresh_needed; +} + +/* ******************************************************************* + * + * @name: void ISR_SystemDefaultVBlank(void) + * + * @author: Xavier Del Campo + * + * @brief: + * + * @remarks: + * Called from VSync interrupt. Called 50 times a second in PAL mode, + * 60 times a second in NTSC mode. + * + * *******************************************************************/ + +void ISR_SystemDefaultVBlank(void) +{ + refresh_needed = true; + SystemIncreaseGlobalTimer(); +} + +/* ******************************************************************* + * + * @name: void SystemIncreaseGlobalTimer(void) + * + * @author: Xavier Del Campo + * + * @brief: + * Increases internal variable responsible for time handling. + * + * @remarks: + * Usually called from ISR_SystemDefaultVBlank(). + * + * *******************************************************************/ + +void SystemIncreaseGlobalTimer(void) +{ + global_timer++; +} + +/* ******************************************************************* + * + * @name: uint64_t SystemGetGlobalTimer(void) + * + * @author: Xavier Del Campo + * + * @brief: Returns internal global timer value. + * + * *******************************************************************/ + +uint64_t SystemGetGlobalTimer(void) +{ + return global_timer; +} + +/* ******************************************************************* + * + * @name: void SystemDisableScreenRefresh(void) + * + * @author: Xavier Del Campo + * + * @brief: Resets VBlank IRQ flag. + * + * *******************************************************************/ + +void SystemDisableScreenRefresh(void) +{ + refresh_needed = false; +} + +/* ******************************************************************* + * + * @name: bool System1SecondTick(void) + * + * @author: Xavier Del Campo + * + * @return: bool variable with a 1-cycle-length pulse that gets + * set each second. + * + * *******************************************************************/ + +bool System1SecondTick(void) +{ + return !(global_timer % REFRESH_FREQUENCY); +} + +/* **************************************************************************************** + * + * @name bool SystemLoadFileToBuffer(char* fname, uint8_t* buffer, uint32_t szBuffer) + * + * @author: Xavier Del Campo + * + * @brief: Given an input path, it fills a buffer pointed to by "buffer" with + * maximum size "szBuffer" with data from CD-ROM. + * + * @return: true if file has been loaded successfully, false otherwise. + * + * ****************************************************************************************/ + +bool SystemLoadFileToBuffer(char* fname, uint32_t init_pos, uint8_t* buffer, uint32_t szBuffer) +{ + FILE *f; + int32_t size; + + // Wait for possible previous operation from the GPU before entering this section. + while( (SystemIsBusy() == true) || (GfxIsGPUBusy() == true) ); + + if(fname == NULL) + { + dprintf("SystemLoadFile: NULL fname!\n"); + return false; + } + + memset(buffer,0,szBuffer); + + system_busy = true; + + SystemDisableVBlankInterrupt(); + + f = fopen(fname, "r"); + + if(f == NULL) + { + dprintf("SystemLoadFile: file could not be found!\n"); + //File couldn't be found + return false; + } + + fseek(f, init_pos, SEEK_END); + + size = ftell(f); + + if(size > szBuffer) + { + dprintf("SystemLoadFile: Exceeds file buffer size (%d bytes)\n",size); + //Bigger than 128 kB (buffer's max size) + return false; + } + + fseek(f, init_pos, SEEK_SET); //f->pos = 0; + + fread(buffer, sizeof(char), size, f); + + fclose(f); + + SystemEnableVBlankInterrupt(); + + system_busy = false; + + dprintf("File \"%s\" loaded successfully!\n",fname); + + return true; +} + +void SystemSetBusyFlag(bool value) +{ + system_busy = value; +} + +/* **************************************************************************************** + * + * @name bool SystemLoadFile(char*fname) + * + * @author: Xavier Del Campo + * + * @brief: Given an input file name, it loads its conents into internal buffer. + * + * @return: true if file has been loaded successfully, false otherwise. + * + * ****************************************************************************************/ + +bool SystemLoadFile(char*fname) +{ + return SystemLoadFileToBuffer(fname,0,file_buffer,sizeof(file_buffer)); +} + +/* ****************************************************************** + * + * @name uint8_t* SystemGetBufferAddress(void) + * + * @author: Xavier Del Campo + * + * @return: Reportedly, returns internal buffer initial address. + * + * *****************************************************************/ + +uint8_t* SystemGetBufferAddress(void) +{ + return file_buffer; +} + +/* ****************************************************************** + * + * @name void SystemClearBuffer(void) + * + * @author: Xavier Del Campo + * + * @return: Fills internal buffer with zeros + * + * *****************************************************************/ + +void SystemClearBuffer(void) +{ + memset(file_buffer, 0, sizeof(file_buffer)); +} + +/* ****************************************************************** + * + * @name uint32_t SystemRand(uint32_t min, uint32_t max) + * + * @author: Xavier Del Campo + * + * @return: random number between "min" and "max". + * + * @remarks: rand seed must be set before using this function, or + * you will predictable values otherwise! + * + * *****************************************************************/ + +uint32_t SystemRand(uint32_t min, uint32_t max) +{ + return rand() % (max - min + 1) + min; +} + +/* *********************************************************************** + * + * @name volatile bool SystemIsBusy(void) + * + * @author: Xavier Del Campo + * + * @return: returns system busy flag. + * + * ***********************************************************************/ + +volatile bool SystemIsBusy(void) +{ + return system_busy; +} + +bool SystemArrayCompare(unsigned short* arr1, unsigned short* arr2, size_t sz) +{ + size_t i; + + for(i = 0; i < sz; i++) + { + if(arr1[i] != arr2[i]) + { + return false; + } + } + + return true; +} + +void SystemPrintStackPointerAddress(void) +{ +#ifdef PSXSDK_DEBUG // Used to avoid unused variable warning + void * ptr = NULL; + fix16_t used_bytes = fix16_from_int((int)((void*)BEGIN_STACK_ADDRESS - (void*)&ptr)); + fix16_t stackPercent = fix16_sdiv(used_bytes,fix16_from_int((int)STACK_SIZE)); + + stackPercent = fix16_smul(stackPercent, fix16_from_int((int)100)); + + dprintf("stackPercent: %d\n", stackPercent); + + dprintf("Stack begin pointer: 0x%08X\n" + "Stack pointer address: 0x%08X\n" + "Used %d%% of stack size.\n" + "\tUsed bytes: %d\n", + (void*)BEGIN_STACK_ADDRESS, + (void*)&ptr, + fix16_to_int(stackPercent), + fix16_to_int(used_bytes) ); +#endif // PSXSDK_DEBUG + +} + +void SystemCheckStack(void) +{ + uint32_t * ptrStack = BEGIN_STACK_ADDRESS; + uint32_t data; + + ptrStack -= STACK_SIZE; + data = (*ptrStack); + + if(data != END_STACK_PATTERN) + { + dprintf("Stack overflow?\n"); + + while(1); + } +} + +void SystemSetStackPattern(void) +{ + uint32_t * ptrStack = BEGIN_STACK_ADDRESS; + + ptrStack -= STACK_SIZE; + + *ptrStack = END_STACK_PATTERN; +} + +int32_t SystemIndexOfStringArray(char* str, char** array) +{ + int32_t i; + + for(i = 0; array[i] != NULL; i++) + { + dprintf("String to find: %s\nEntry: %s\n", str, array[i]); + + if(strcmp(str, array[i]) == 0) + { + dprintf("Match! Returning index %d...\n", i); + return i; + } + } + + return -1; +} +void SystemCyclicHandler(void) +{ + SystemDisableScreenRefresh(); + SystemCheckStack(); +} + +void SystemDisableVBlankInterrupt(void) +{ + I_MASK &= ~(0x00000001); +} + +void SystemEnableVBlankInterrupt(void) +{ + I_MASK |= (0x00000001); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..6fed452 --- /dev/null +++ b/src/main.c @@ -0,0 +1,161 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "System.h" +#include "Serial.h" +#include "LoadMenu.h" +#include "EndAnimation.h" + +/* ************************************* + * Defines + * *************************************/ + +#define PSX_EXE_HEADER_SIZE 2048 +#define EXE_DATA_PACKET_SIZE 8 + +/* ************************************* + * Local Prototypes + * *************************************/ + +/* ************************************* + * Local Variables + * *************************************/ + + /* Untitled1 (10/07/2017 18:57:47) + StartOffset: 00000000, EndOffset: 0000002F, Length: 00000030 */ + +/* Untitled2 (10/07/2017 21:10:19) + StartOffset: 00000000, EndOffset: 000357FF, Length: 00035800 */ + +extern void _start(void); + + + +int main(void) +{ + uint8_t* inBuffer = SystemGetBufferAddress(); + // int (*exeAddress)(void); + + //System initialization + dprintf("SystemInit()\n"); + SystemInit(); + + dprintf("LoadMenuInit()\n"); + + LoadMenuInit(); + + { + uint32_t initPC_Address; + uint32_t RAMDest_Address; + uint32_t ExeSize = 0; + uint32_t i; + void (*exeAddress)(void); + + GfxSetGlobalLuminance(0); + + SerialInit(); + + // Read PSX-EXE header (32 bytes will be enough). + + SerialSetState(SERIAL_STATE_READING_HEADER); + + SerialRead(inBuffer, 32); + + // Get initial program counter address from PSX-EXE header. + + initPC_Address = (inBuffer[0x10] | (inBuffer[0x11] << 8) | (inBuffer[0x12] << 16) | (inBuffer[0x13] << 24) ); + + SerialSetPCAddress(initPC_Address); + + //dprintf("initPC_Address = 0x%08X\n", initPC_Address); + + // Get destination address in RAM from PSX-EXE header. + + RAMDest_Address = (inBuffer[0x18] | (inBuffer[0x19] << 8) | (inBuffer[0x1A] << 16) | (inBuffer[0x1B] << 24) ); + + SerialSetRAMDestAddress(RAMDest_Address); + + //dprintf("RAMDest_Address = 0x%08X\n", RAMDest_Address); + + // We have received all data correctly. Send ACK. + + memset(inBuffer, 0, SystemGetBufferSize()); + + SerialSetState(SERIAL_STATE_WRITING_ACK); + + SerialWrite(ACK_BYTE_STRING, sizeof(uint8_t)); // Write ACK + + // Get PSX-EXE size, without header, in hexadecimal, little-endian format; + + SerialSetState(SERIAL_STATE_READING_EXE_SIZE); + + SerialRead(inBuffer, sizeof(uint32_t) ); + + for(i = 0; i < sizeof(uint32_t); i++) + { + ExeSize |= inBuffer[i] << (i << 3); // (i << 3) == (i * 8) + } + + SerialSetExeSize(ExeSize); + + //DEBUG_PRINT_VAR(ExeSize); + + SerialSetState(SERIAL_STATE_CLEANING_MEMORY); + + exeAddress = (void*)initPC_Address; + + // Clean memory where EXE data will be loaded, just in case... + + memset((void*)RAMDest_Address, 0, (uint32_t)((uint32_t)(&_start) - (uint32_t)(RAMDest_Address) ) ); + + SerialSetState(SERIAL_STATE_WRITING_ACK); + + // We have received PSX-EXE size (without header) correctly. Send ACK. + + SerialWrite(ACK_BYTE_STRING, sizeof(uint8_t)); // Write ACK + + SerialSetState(SERIAL_STATE_READING_EXE_DATA); + + while(GfxIsGPUBusy() == true); + + for(i = 0; i < ExeSize; i += EXE_DATA_PACKET_SIZE) + { + uint32_t bytes_to_read; + + // Read actual EXE data into proper RAM address. + + if( (i + EXE_DATA_PACKET_SIZE) >= ExeSize) + { + bytes_to_read = ExeSize - i; + } + else + { + bytes_to_read = EXE_DATA_PACKET_SIZE; + } + + SerialRead((uint8_t*)RAMDest_Address + i, bytes_to_read); + + SerialSetExeBytesReceived(bytes_to_read); + + SerialWrite(ACK_BYTE_STRING, sizeof(uint8_t)); // Write ACK + } + + SetVBlankHandler(&ISR_SystemDefaultVBlank); + + // Make a pretty animation before exeting OpenSend application. + + EndAnimation(); + + PSX_DeInit(); + + // PSX-EXE has been successfully loaded into RAM. Run executable! + + //dprintf("Entering exe...\n"); + + exeAddress(); + } + + return 0; +} -- cgit v1.2.3