diff options
| author | XaviDCR92 <xavi.dcr@gmail.com> | 2017-07-21 00:09:35 +0200 |
|---|---|---|
| committer | XaviDCR92 <xavi.dcr@gmail.com> | 2017-07-21 00:09:35 +0200 |
| commit | 627de0d81f81ad60d26d782f2425be1e6f5a3dbc (patch) | |
| tree | 91ffa502aa62c03c2fecf28529ebc8c6b20828c5 /Source | |
+ First commit. It works painfully slow, but gets the job done. Still lots of room for improvement.
Diffstat (limited to 'Source')
| -rw-r--r-- | Source/EndAnimation.c | 233 | ||||
| -rw-r--r-- | Source/EndAnimation.h | 25 | ||||
| -rw-r--r-- | Source/Font.c | 209 | ||||
| -rw-r--r-- | Source/Font.h | 44 | ||||
| -rw-r--r-- | Source/GameStructures.h | 57 | ||||
| -rw-r--r-- | Source/Gfx.c | 595 | ||||
| -rw-r--r-- | Source/Gfx.h | 115 | ||||
| -rw-r--r-- | Source/Global_Inc.h | 41 | ||||
| -rw-r--r-- | Source/LoadMenu.c | 204 | ||||
| -rw-r--r-- | Source/LoadMenu.h | 29 | ||||
| -rw-r--r-- | Source/Makefile | 74 | ||||
| -rw-r--r-- | Source/Pad.c | 530 | ||||
| -rw-r--r-- | Source/Pad.h | 62 | ||||
| -rw-r--r-- | Source/Serial.c | 271 | ||||
| -rw-r--r-- | Source/Serial.h | 49 | ||||
| -rw-r--r-- | Source/System.c | 887 | ||||
| -rw-r--r-- | Source/System.h | 135 | ||||
| -rw-r--r-- | Source/main.c | 164 |
18 files changed, 3724 insertions, 0 deletions
diff --git a/Source/EndAnimation.c b/Source/EndAnimation.c new file mode 100644 index 0000000..027ca1a --- /dev/null +++ b/Source/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/Source/EndAnimation.h b/Source/EndAnimation.h new file mode 100644 index 0000000..edb7b07 --- /dev/null +++ b/Source/EndAnimation.h @@ -0,0 +1,25 @@ +#ifndef __END_SCREEN_HEADER__ +#define __END_SCREEN_HEADER__ + +/* ************************************** + * Includes * + * **************************************/ + +#include "Global_Inc.h" +#include "Gfx.h" + +/* ************************************** + * Defines * + * **************************************/ + +/* ************************************** + * Global Prototypes * + * **************************************/ + +void EndAnimation(void); + +/* ************************************** + * Global Variables * + * **************************************/ + +#endif // __END_SCREEN_HEADER__ diff --git a/Source/Font.c b/Source/Font.c new file mode 100644 index 0000000..3e729f3 --- /dev/null +++ b/Source/Font.c @@ -0,0 +1,209 @@ +/* ************************************* + * 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; + + if(ptrFont->flags & FONT_1HZ_FLASH) + { + if(Gfx1HzFlash() == false) + { + return; + } + } + else if(ptrFont->flags & FONT_2HZ_FLASH) + { + if(Gfx2HzFlash() == false) + { + return; + } + } + + 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/Source/Font.h b/Source/Font.h new file mode 100644 index 0000000..5a7d6b5 --- /dev/null +++ b/Source/Font.h @@ -0,0 +1,44 @@ +#ifndef __FONT_HEADER__ +#define __FONT_HEADER__ + +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "System.h" +#include "Gfx.h" +#include "GameStructures.h" +#include <stdarg.h> + +/* ************************************* + * Defines + * *************************************/ + +#define FONT_DEFAULT_CHAR_SIZE 16 +#define FONT_DEFAULT_INIT_CHAR '!' + +/* ************************************** + * Structs and enums * + * *************************************/ + +/* ************************************* + * Global prototypes + * *************************************/ + +bool FontLoadImage(char* strPath, TYPE_FONT * ptrFont); +void FontSetSize(TYPE_FONT * ptrFont, short size, short bitshift); +void FontPrintText(TYPE_FONT *ptrFont, short x, short y, char* str, ...); +void FontSetInitChar(TYPE_FONT * ptrFont, char c); +void FontSetFlags(TYPE_FONT * ptrFont, FONT_FLAGS flags); +void FontCyclic(void); +void FontSetSpacing(TYPE_FONT* ptrFont, short spacing); + +/* ************************************* + * Global variables + * *************************************/ + +TYPE_FONT RadioFont; +TYPE_FONT SmallFont; + +#endif //__FONT_HEADER__ diff --git a/Source/GameStructures.h b/Source/GameStructures.h new file mode 100644 index 0000000..e092e1d --- /dev/null +++ b/Source/GameStructures.h @@ -0,0 +1,57 @@ +#ifndef __GAME_STRUCTURES__HEADER__ +#define __GAME_STRUCTURES__HEADER__ + +/* ************************************* + * Defines + * *************************************/ + +#define CHEAT_ARRAY_SIZE 16 + +/* ************************************* + * Structs and enums + * *************************************/ + +typedef enum t_fontflags +{ + FONT_NOFLAGS = 0, + FONT_CENTERED = 0x01, + FONT_WRAP_LINE = 0x02, + FONT_BLEND_EFFECT = 0x04, + FONT_1HZ_FLASH = 0x08, + FONT_2HZ_FLASH = 0x10, + FONT_H_CENTERED = 0x20 +}FONT_FLAGS; + +typedef struct t_Font +{ + GsSprite spr; + short char_spacing; + short char_w; + short char_w_bitshift; + short char_h; + char init_ch; + uint8_t char_per_row; + uint8_t max_ch_wrap; + FONT_FLAGS flags; + short spr_w; + short spr_h; + short spr_u; + short spr_v; +}TYPE_FONT; + +typedef struct t_Timer +{ + uint32_t time; + uint32_t orig_time; + bool repeat_flag; + bool busy; + void (*Timeout_Callback)(void); +}TYPE_TIMER; + +typedef struct t_Cheat +{ + unsigned short Combination[CHEAT_ARRAY_SIZE]; + void (*Callback)(void); +}TYPE_CHEAT; + +#endif // __GAME_STRUCTURES__HEADER__ diff --git a/Source/Gfx.c b/Source/Gfx.c new file mode 100644 index 0000000..03b33d2 --- /dev/null +++ b/Source/Gfx.c @@ -0,0 +1,595 @@ +/* ************************************* + * 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; + +static bool five_hundred_ms_show; +static bool one_second_show; + +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) +{ + if(System1SecondTick() == true) + { + one_second_show = one_second_show? false:true; + } + + if(System500msTick() == true) + { + five_hundred_ms_show = five_hundred_ms_show? false:true; + } + + 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; + bool has_1hz_flash = spr->attribute & GFX_1HZ_FLASH; + bool has_2hz_flash = spr->attribute & GFX_2HZ_FLASH; + + if( (spr->w <= 0) || (spr->h <= 0) ) + { + // Invalid width or heigth + return; + } + + if(GfxIsSpriteInsideScreenArea(spr) == false) + { + return; + } + else if(has_2hz_flash && Gfx2HzFlash() == false) + { + return; + } + else if(has_1hz_flash && Gfx1HzFlash() == 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(has_1hz_flash == true) + { + spr->attribute &= ~(GFX_1HZ_FLASH); + } + + 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); + } + + if(has_1hz_flash == true) + { + spr->attribute |= GFX_1HZ_FLASH; + } + + 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 Gfx1HzFlash(void) +{ + return one_second_show; +} + +bool Gfx2HzFlash(void) +{ + return five_hundred_ms_show; +} + +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/Source/Gfx.h b/Source/Gfx.h new file mode 100644 index 0000000..76eaff2 --- /dev/null +++ b/Source/Gfx.h @@ -0,0 +1,115 @@ +#ifndef __GFX_HEADER__ +#define __GFX_HEADER__ + +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "System.h" + +#include "Pad.h" + +/* ************************************* + * Defines + * *************************************/ + +#define X_SCREEN_RESOLUTION 384 +#define Y_SCREEN_RESOLUTION 240 +#define VRAM_W 1024 +#define VRAM_H 512 +#define MAX_SIZE_FOR_GSSPRITE 256 +#define GFX_TPAGE_WIDTH 64 +#define GFX_TPAGE_HEIGHT 256 +#define GFX_1HZ_FLASH (1<<7) +#define GFX_2HZ_FLASH (1<<8) +#define FULL_LUMINANCE 0xFF + +/* ************************************* + * Global prototypes + * *************************************/ + +void GfxInitDrawEnv(void); +void GfxInitDispEnv(void); +void GfxSetPrimitiveList(void); + +// Renders new scene. Use this function unless you know what you are doing! +void GfxDrawScene(void); + +// Blocking version. Calls GfxDrawScene() and then adds a while(GfxIsBusy() ) +// after it. +void GfxDrawScene_Slow(void); + +void GfxDrawScene_NoSwap(void); + +void GfxSwapBuffers(void); + +// Only renders screen and does not update any pad data or timer data. +// To be used in ISR! +void GfxDrawScene_Fast(void); + +// Repotedly, tells is GPU is ready for a DMA transfer. +bool GfxReadyForDMATransfer(void); + +// Fills a GsSprite structure with information from a TIM file. +bool GfxSpriteFromFile(char* fname, GsSprite * spr); + +// Reportedly, loads CLUT data from a TIM image (image data is discarded) +bool GfxCLUTFromFile(char* fname); + +// Returns true if current object is within screen limits, false otherwise. +bool GfxIsInsideScreenArea(short x, short y, short w, short h); + +// Function overload for GsSprite structures. +bool GfxIsSpriteInsideScreenArea(GsSprite * spr); + +// Used to know whether GPU operation can be done. +bool GfxIsGPUBusy(void); + +// Draws a sprite on screen. First, it checks whether sprite is inside +// screen limits. +void GfxSortSprite(GsSprite * spr); + +uint8_t GfxGetGlobalLuminance(void); + +void GfxSetGlobalLuminance(uint8_t value); + +void GfxIncreaseGlobalLuminance(int8_t step); + +void GfxButtonSetFlags(uint8_t flags); + +void GfxButtonRemoveFlags(uint8_t flags); + +int GfxRotateFromDegrees(int deg); + +void GfxDrawButton(short x, short y, unsigned short btn); + +// Sends current display data on a specific VRAM section and fills +// sprite structure pointed to by "spr". +void GfxSaveDisplayData(GsSprite *spr); + +// Fills GsSprite structure pointed to by "spr" with texture page and U/V +// offset data given a position in VRAM. +bool GfxTPageOffsetFromVRAMPosition(GsSprite * spr, short x, short y); + +void GfxSetSplitScreen(uint8_t playerIndex); + +void GfxDisableSplitScreen(void); + +// Switches between true and false every 1 exact second (used for flashing effects) +bool Gfx1HzFlash(void); + +// Switches between true and false every 500 milliseconds (used for flashing effects) +bool Gfx2HzFlash(void); + +void GfxDrawScene_NoSwap(void); + +void GfxDevMenuEnable(void); + +/* ************************************* + * Global variables + * *************************************/ + +extern GsSprite PSXButtons; + +#endif //__GFX_HEADER__ diff --git a/Source/Global_Inc.h b/Source/Global_Inc.h new file mode 100644 index 0000000..0c8f446 --- /dev/null +++ b/Source/Global_Inc.h @@ -0,0 +1,41 @@ +#ifndef __GLOBAL_INC__H__ +#define __GLOBAL_INC__H__ + +/* ************************************* + * Includes + * *************************************/ + +#include <psx.h> +#include <stdio.h> +#include <psxsio.h> +#include <stdlib.h> +#include <string.h> +#include <types.h> +#include <fixmath.h> +#include <runexe.h> + +/* ************************************* + * Defines + * *************************************/ + +#define REFRESH_FREQUENCY 50 //50 Hz PAL / 60 Hz NTSC +#define DEBUG_PRINT_VAR(var) dprintf(#var " = %d\n", var); + +#ifndef bool + typedef enum + { + false = 0, + true = 1 + }bool; +#endif + +#if (PSXSDK_VERSION != 0x0599) +#error "Wrong PSXSDK version! Please use version 0.5.99." +#endif + +/* Test for GCC > 5.2.0 */ +#if ( (__GNUC__ != 5) || (__GNUC_MINOR__ != 2) || (__GNUC_PATCHLEVEL__ != 0) ) +#error "Wrong GCC version! Please use version 5.2.0." +#endif + +#endif // __GLOBAL_INC__H__ diff --git a/Source/LoadMenu.c b/Source/LoadMenu.c new file mode 100644 index 0000000..ba8efaf --- /dev/null +++ b/Source/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/Source/LoadMenu.h b/Source/LoadMenu.h new file mode 100644 index 0000000..8afc373 --- /dev/null +++ b/Source/LoadMenu.h @@ -0,0 +1,29 @@ +#ifndef __LOAD_MENU_HEADER__ +#define __LOAD_MENU_HEADER__ + +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "Gfx.h" +#include "System.h" +#include "Font.h" + +/* ************************************* + * Defines + * *************************************/ + +/* ************************************* + * Global prototypes + * *************************************/ + +void LoadMenuInit(void); + +void LoadMenu( char* fileList[], + void * dest[], + uint8_t szFileList , uint8_t szDestList); + +void LoadMenuEnd(void); + +#endif //__LOAD_MENU_HEADER__ diff --git a/Source/Makefile b/Source/Makefile new file mode 100644 index 0000000..ed9324f --- /dev/null +++ b/Source/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="A homebrew game 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 Pad.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/Source/Pad.c b/Source/Pad.c new file mode 100644 index 0000000..ef56f08 --- /dev/null +++ b/Source/Pad.c @@ -0,0 +1,530 @@ +/* ************************************* + * Includes + * *************************************/ + +#include "Pad.h" + +/* ************************************* + * Defines + * *************************************/ + +#define PAD_ONE 0 +#define PAD_TWO 1 +#define PAD_CHEAT_TIMEOUT 20 // 2 units * 100 ms/unit = 2000 ms +#define PAD_MAX_CHEATS 16 + +/* ************************************** + * Structs and enums * + * *************************************/ + +enum +{ + + PAD_CROSS_INDEX = 0, + PAD_SQUARE_INDEX, + PAD_TRIANGLE_INDEX, + PAD_CIRCLE_INDEX, + + PAD_DOWN_INDEX, + PAD_LEFT_INDEX, + PAD_UP_INDEX, + PAD_RIGHT_INDEX, + + PAD_L1_INDEX, + PAD_L2_INDEX, + + PAD_R1_INDEX, + PAD_R2_INDEX, + + NUMBER_OF_KEYS + +}; + + +/* ************************************* + * Local Prototypes + * *************************************/ + +static void PadOneVibrationHandler(void); +static void PadTwoVibrationHandler(void); +static void PadCheatHandler(uint8_t n_pad); +static void PadOneCleanCheatArray(void); +static void PadTwoCleanCheatArray(void); +static psx_pad_state PadOneGetState(void); +uint8_t PadGetKeyIndex(unsigned short key); + +/* ************************************* + * Local Variables + * *************************************/ + +// Pad data +static unsigned short pad1; +static unsigned short pad2; + +// Pad data from previous frame +static unsigned short previous_pad1; +static unsigned short previous_pad2; + +// Vibration timers +static uint16_t pad1_vibration_timer; +static uint16_t pad2_vibration_timer; + +// Vibration strenght data (big motor) +static uint8_t pad1_big_vibration_force; +static uint8_t pad2_big_vibration_force; + +// Vibration strenght data (small motor) +static uint8_t pad1_small_vibration_force; +static uint8_t pad2_small_vibration_force; + +// Timers for each key pressed (used for PadXXKeyRepeat() ) +static uint8_t pad1_keys_repeat[NUMBER_OF_KEYS]; +static uint8_t pad2_keys_repeat[NUMBER_OF_KEYS]; + +static unsigned short pad1_last_key_single_pressed; +static unsigned short pad2_last_key_single_pressed; + +// These arrays include last 16 buttons pressed by user and keeps them +// for cheating purposes. They are cleaned if no keys are pressed during +// PAD_CHEAT_TIMEOUT milliseconds. +static unsigned short pad1_cheat_array[CHEAT_ARRAY_SIZE]; +static unsigned short pad2_cheat_array[CHEAT_ARRAY_SIZE]; + +// Pointers to timers which clean padX_cheat_array. +static TYPE_TIMER* pad1_cheat_timer; +static TYPE_TIMER* pad2_cheat_timer; + +static TYPE_CHEAT * cheatsArray[PAD_MAX_CHEATS]; + +psx_pad_state PadOneGetState(void) +{ + psx_pad_state PadOne; + + PSX_PollPad_Fast(PAD_ONE,&PadOne); + + return PadOne; +} + +bool PadOneConnected(void) +{ + psx_pad_state PadOne = PadOneGetState(); + + if(PadOne.status != PAD_STATUS_OK) + { + return false; + } + + return true; +} + +bool PadOneAnyKeyPressed(void) +{ + return (bool)pad1; +} + +bool PadOneDirectionKeyPressed(void) +{ + return ( (PadOneKeyPressed(PAD_UP) == true) + || + (PadOneKeyPressed(PAD_LEFT) == true) + || + (PadOneKeyPressed(PAD_RIGHT) == true) + || + (PadOneKeyPressed(PAD_DOWN) == true) ); +} + +bool PadOneDirectionKeyReleased(void) +{ + return ( (PadOneKeyReleased(PAD_UP) == true) + || + (PadOneKeyReleased(PAD_LEFT) == true) + || + (PadOneKeyReleased(PAD_RIGHT) == true) + || + (PadOneKeyReleased(PAD_DOWN) == true) ); +} + +bool PadTwoDirectionKeyReleased(void) +{ + return ( (PadTwoKeyReleased(PAD_UP) == true) + || + (PadTwoKeyReleased(PAD_LEFT) == true) + || + (PadTwoKeyReleased(PAD_RIGHT) == true) + || + (PadTwoKeyReleased(PAD_DOWN) == true) ); +} + +bool PadTwoDirectionKeyPressed(void) +{ + return ( (PadTwoKeyPressed(PAD_UP) == true) + || + (PadTwoKeyPressed(PAD_LEFT) == true) + || + (PadTwoKeyPressed(PAD_RIGHT) == true) + || + (PadTwoKeyPressed(PAD_DOWN) == true) ); +} + +bool PadTwoAnyKeyPressed(void) +{ + return (bool)pad2; +} + +bool PadOneKeyPressed(unsigned short key) +{ + return (bool)( pad1 & key ); +} + +bool PadTwoKeyPressed(unsigned short key) +{ + return (bool)( pad2 & key ); +} + +bool PadOneKeySinglePress(unsigned short key) +{ + return (bool)( !(previous_pad1 & key) && (pad1 & key) ); +} + +bool PadTwoKeySinglePress(unsigned short key) +{ + return (bool)( !(previous_pad2 & key) && (pad2 & key) ); +} + +bool PadOneKeyRepeat(unsigned short key, uint8_t time) +{ + uint8_t key_index = PadGetKeyIndex(key); + + if(key_index == NUMBER_OF_KEYS) + { + return false; + } + + pad1_keys_repeat[key_index]++; + + if(pad1_keys_repeat[key_index] >= time) + { + pad1_keys_repeat[key_index] = 0; + return true; + } + + return false; +} + +bool PadTwoKeyRepeat(unsigned short key, uint8_t time) +{ + uint8_t key_index = PadGetKeyIndex(key); + + if(key_index == NUMBER_OF_KEYS) + { + return false; + } + + pad2_keys_repeat[key_index]++; + + if(pad2_keys_repeat[key_index] >= time) + { + pad2_keys_repeat[key_index] = 0; + return true; + } + + return false; +} + +void PadOneVibrationHandler(void) +{ + if(PadOneIsVibrationEnabled() == true) + { + pad_enable_vibration(PAD_ONE); + pad_set_vibration(PAD_ONE,pad1_small_vibration_force,pad1_big_vibration_force); + pad1_vibration_timer--; + } +} + +void PadTwoVibrationHandler(void) +{ + if(PadTwoIsVibrationEnabled() == true) + { + pad_enable_vibration(PAD_TWO); + pad_set_vibration(PAD_TWO,pad2_small_vibration_force,pad2_big_vibration_force); + pad2_vibration_timer--; + } +} + +bool PadOneIsVibrationEnabled(void) +{ + return (pad1_vibration_timer & true); +} + +bool PadTwoIsVibrationEnabled(void) +{ + return (pad2_vibration_timer & true); +} + +bool UpdatePads(void) +{ + PadOneVibrationHandler(); + + PadTwoVibrationHandler(); + + PadCheatHandler(PAD_ONE); + + PadCheatHandler(PAD_TWO); + + // Get now-old pad data + previous_pad1 = pad1; + previous_pad2 = pad2; + + PSX_ReadPad(&pad1,&pad2); + + if(PadOneConnected() == false) + { + return false; + } + + if(!(previous_pad1 & pad1) ) + { + pad1_last_key_single_pressed = pad1; + } + else + { + pad1_last_key_single_pressed = 0; + } + + if(!(previous_pad2 & pad2) ) + { + pad2_last_key_single_pressed = pad2; + } + else + { + pad2_last_key_single_pressed = 0; + } + + return true; +} + +bool PadOneKeyReleased(unsigned short key) +{ + return ( !(pad1 & key) && (previous_pad1 & key) ); +} + +bool PadTwoKeyReleased(unsigned short key) +{ + return ( !(pad2 & key) && (previous_pad2 & key) ); +} + +uint8_t PadGetKeyIndex(unsigned short key) +{ + switch(key) + { + case PAD_CROSS: + return PAD_CROSS_INDEX; + break; + + case PAD_SQUARE: + return PAD_SQUARE_INDEX; + break; + + case PAD_TRIANGLE: + return PAD_TRIANGLE_INDEX; + break; + + case PAD_CIRCLE: + return PAD_CIRCLE_INDEX; + break; + + case PAD_DOWN: + return PAD_DOWN_INDEX; + break; + + case PAD_LEFT: + return PAD_LEFT_INDEX; + break; + + case PAD_UP: + return PAD_UP_INDEX; + break; + + case PAD_RIGHT: + return PAD_RIGHT_INDEX; + break; + + case PAD_L1: + return PAD_L1_INDEX; + break; + + case PAD_R1: + return PAD_R1_INDEX; + break; + + case PAD_L2: + return PAD_L2_INDEX; + break; + + case PAD_R2: + return PAD_R2_INDEX; + break; + + default: + return NUMBER_OF_KEYS; + break; + } +} + +unsigned short* PadOneGetAddress(void) +{ + return &pad1; +} + +void PadClearData(void) +{ + pad1 = 0; + pad2 = 0; + + previous_pad1 = 0; + previous_pad2 = 0; +} + +void PadInit(void) +{ + pad1_cheat_timer = SystemCreateTimer(PAD_CHEAT_TIMEOUT,true /* Repeat flag */,&PadOneCleanCheatArray); + pad2_cheat_timer = SystemCreateTimer(PAD_CHEAT_TIMEOUT,true /* Repeat flag */,&PadTwoCleanCheatArray); + + memset(cheatsArray,0, sizeof(cheatsArray)); +} + +void PadCheatHandler(uint8_t n_pad) +{ + unsigned short available_keys[12] = { PAD_LEFT, PAD_RIGHT, PAD_UP, PAD_DOWN, + PAD_L2, PAD_R2, PAD_L1, PAD_R1, + PAD_TRIANGLE, PAD_CIRCLE, PAD_CROSS, PAD_SQUARE }; + + uint8_t i; + uint8_t keys_released = 0; + unsigned short key; + uint8_t j; + bool (*pressed_callback)(unsigned short); + void (*clean_callback)(void); + bool success = false; + unsigned short* cheat_array; + TYPE_TIMER* timer; + + switch(n_pad) + { + case PAD_ONE: + pressed_callback = &PadOneKeySinglePress; + cheat_array = pad1_cheat_array; + clean_callback = &PadOneCleanCheatArray; + timer = pad1_cheat_timer; + break; + + case PAD_TWO: + pressed_callback = &PadTwoKeySinglePress; + cheat_array = pad2_cheat_array; + clean_callback = &PadTwoCleanCheatArray; + timer = pad2_cheat_timer; + break; + + default: + dprintf("Invalid pad called for PadCheatHandler()!\n"); + return; + } + + for(i = 0; i < PAD_MAX_CHEATS; i++) + { + if(cheatsArray[i] != NULL) + { + if(SystemArrayCompare(cheat_array, cheatsArray[i]->Combination, CHEAT_ARRAY_SIZE) == true) + { + if(cheatsArray[i]->Callback != NULL) + { + if(clean_callback != NULL) + { + clean_callback(); + } + + cheatsArray[i]->Callback(); + + return; + } + } + } + } + + for(i = 0; i < sizeof(available_keys) / sizeof(unsigned short); i++) + { + if(pressed_callback(available_keys[i]) == true) + { + SystemTimerRestart(timer); + key = available_keys[i]; + keys_released++; + } + } + + if(keys_released != 1) + { + return; + } + + // Check for full array (return success = true if an empty array + // element was found. + for(j = 0; j < CHEAT_ARRAY_SIZE; j++) + { + if(cheat_array[j] == 0) + { + success = true; + break; + } + } + + if(success == false) + { + if(clean_callback != NULL) + { + // Overrun + clean_callback(); + } + } + + cheat_array[j] = key; +} + +bool PadAddCheat(TYPE_CHEAT * cheat) +{ + static uint8_t idx = 0; + + if(idx >= PAD_MAX_CHEATS) + { + dprintf("Maximum number of cheats exceeded!\n"); + return false; + } + + cheatsArray[idx++] = cheat; + + return true; +} + +void PadOneCleanCheatArray(void) +{ + memset(pad1_cheat_array,0,sizeof(unsigned short) * CHEAT_ARRAY_SIZE); +} + +void PadTwoCleanCheatArray(void) +{ + memset(pad2_cheat_array,0,sizeof(unsigned short) * CHEAT_ARRAY_SIZE); +} + +unsigned short* PadGetPlayerOneCheatArray(void) +{ + return pad1_cheat_array; +} + +unsigned short PadOneGetLastKeySinglePressed(void) +{ + return pad1_last_key_single_pressed; +} + +unsigned short PadTwoGetLastKeySinglePressed(void) +{ + return pad2_last_key_single_pressed; +} diff --git a/Source/Pad.h b/Source/Pad.h new file mode 100644 index 0000000..5b5fa1d --- /dev/null +++ b/Source/Pad.h @@ -0,0 +1,62 @@ +#ifndef __PAD_HEADER__ +#define __PAD_HEADER__ + +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "System.h" +#include "GameStructures.h" + +/* ************************************* + * Defines + * *************************************/ + +#define PAD_ALWAYS_REPEAT 1 + +/* ************************************* + * Global prototypes + * *************************************/ + +void PadInit(void); +void PadClearData(void); + +bool PadOneConnected(void); + +bool PadOneAnyKeyPressed(void); +bool PadTwoAnyKeyPressed(void); + +bool PadOneKeyPressed(unsigned short key); +bool PadTwoKeyPressed(unsigned short key); + +bool PadOneKeyRepeat(unsigned short key, uint8_t time); +bool PadTwoKeyRepeat(unsigned short key, uint8_t time); + +bool PadOneKeyReleased(unsigned short key); +bool PadTwoKeyReleased(unsigned short key); + +bool PadOneKeySinglePress(unsigned short key); +bool PadTwoKeySinglePress(unsigned short key); + +unsigned short PadOneGetLastKeySinglePressed(void); +unsigned short PadTwoGetLastKeySinglePressed(void); + +bool PadOneDirectionKeyPressed(void); +bool PadTwoDirectionKeyPressed(void); + +bool PadOneDirectionKeyReleased(void); +bool PadTwoDirectionKeyReleased(void); + +bool UpdatePads(void); +bool PadOneIsVibrationEnabled(void); +bool PadTwoIsVibrationEnabled(void); + +bool PadAddCheat(TYPE_CHEAT * cheat); + +unsigned short* PadGetPlayerOneCheatArray(void); + +// Experimental (to be removed) +unsigned short* PadOneGetAddress(void); + +#endif //__PAD_HEADER__ diff --git a/Source/Serial.c b/Source/Serial.c new file mode 100644 index 0000000..c66b32f --- /dev/null +++ b/Source/Serial.c @@ -0,0 +1,271 @@ +/* ************************************* + * 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, + }; + + if((GfxIsGPUBusy() == true) || (serial_busy == true) ) + { + return; + } + + GsSortGPoly4(&SerialBg); + + FontSetFlags(&SmallFont, FONT_BLEND_EFFECT | FONT_H_CENTERED); + + GfxSetGlobalLuminance(0); + + 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_NOFLAGS); + + 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; + + SerialState = SERIAL_STATE_INIT; + + dprintf("SerialInit...\n"); + + SetVBlankHandler(&ISR_Serial); + + 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"); + SetVBlankHandler(&ISR_SystemDefaultVBlank); + 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; + SystemDisableVBlankInterrupt(); + + if(nBytes == 0) + { + dprintf("SerialRead: invalid size %d\n", nBytes); + 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; + + SystemEnableVBlankInterrupt(); + + return true; +} + +bool SerialWrite(void* ptrArray, size_t nBytes) +{ + serial_busy = true; + SystemDisableVBlankInterrupt(); + + if(nBytes == 0) + { + dprintf("SerialWrite: invalid size %d\n", nBytes); + 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); + + serial_busy = false; + + SystemEnableVBlankInterrupt(); + + return true; +} diff --git a/Source/Serial.h b/Source/Serial.h new file mode 100644 index 0000000..da2c4ce --- /dev/null +++ b/Source/Serial.h @@ -0,0 +1,49 @@ +#ifndef __SERIAL_HEADER__ +#define __SERIAL_HEADER__ + +/* ************************************* + * Includes + * *************************************/ + +#include "Global_Inc.h" +#include "System.h" +#include "Gfx.h" +#include "Font.h" + +/* ************************************* + * Defines + * *************************************/ + +#define ACK_BYTE_STRING "b" + +/* ************************************** + * Structs and enums * + * *************************************/ + +typedef enum +{ + SERIAL_STATE_INIT = 0, + SERIAL_STATE_STANDBY, + SERIAL_STATE_WRITING_ACK, + SERIAL_STATE_READING_HEADER, + SERIAL_STATE_READING_EXE_SIZE, + SERIAL_STATE_READING_EXE_DATA, + SERIAL_STATE_WAITING_USER_INPUT, + SERIAL_STATE_CLEANING_MEMORY, +}SERIAL_STATE; + +/* ************************************* + * Global prototypes + * *************************************/ + +void SerialInit(void); +bool SerialRead(uint8_t* ptrArray, size_t nBytes); +bool SerialWrite(void* ptrArray, size_t nBytes); +void ISR_Serial(void); +void SerialSetState(SERIAL_STATE state); +void SerialSetPCAddress(uint32_t addr); +void SerialSetRAMDestAddress(uint32_t addr); +void SerialSetExeSize(size_t size); +void SerialSetExeBytesReceived(uint32_t bytes_read); + +#endif // __SERIAL_HEADER__ diff --git a/Source/System.c b/Source/System.c new file mode 100644 index 0000000..e636925 --- /dev/null +++ b/Source/System.c @@ -0,0 +1,887 @@ +/* ************************************* + * 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 (*(unsigned int*)0x1F801074) + +/* ************************************* + * Local Prototypes + * *************************************/ + +static void SystemCheckTimer(bool* timer, uint64_t* last_timer, uint8_t step); +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; +static bool hundred_ms_timer; +static bool five_hundred_ms_timer; +//Emergency mode flag. Toggled on pad connected/disconnected +static bool emergency_mode; +//Critical section is entered (i.e.: when accessing fopen() or other BIOS functions +static volatile bool system_busy; +//Timer array. +static TYPE_TIMER timer_array[SYSTEM_MAX_TIMERS]; + +/* ******************************************************************* + * + * @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(); + //Reset all user-handled timers + SystemResetTimers(); + //Pads init + PadInit(); + //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 one_second_timer; +} + +/* ******************************************************************* + * + * @name: bool System100msTick(void) + * + * @author: Xavier Del Campo + * + * @return: bool variable with a 1-cycle-length pulse that gets + * set every 100 milliseconds. + * + * *******************************************************************/ + +bool System100msTick(void) +{ + return hundred_ms_timer; +} + +/* ******************************************************************* + * + * @name bool System500msTick(void) + * + * @author: Xavier Del Campo + * + * @return: bool variable with a 1-cycle-length pulse that gets + * set every 500 milliseconds. + * + * *******************************************************************/ + +bool System500msTick(void) +{ + return five_hundred_ms_timer; +} + +/* ******************************************************************* + * + * @name void SystemRunTimers(void) + * + * @author: Xavier Del Campo + * + * @brief: general timer handler + * + * @remarks: 1 second, 500 ms and 100 ms ticks get updated here. + * + * *******************************************************************/ + +void SystemRunTimers(void) +{ + static uint64_t last_one_second_tick; + static uint64_t last_100_ms_tick; + static uint64_t last_500_ms_tick; + + SystemCheckTimer(&one_second_timer, &last_one_second_tick, REFRESH_FREQUENCY); + +#ifdef _PAL_MODE_ + SystemCheckTimer(&hundred_ms_timer, &last_100_ms_tick, 2 /* 2 * 50 ms = 100 ms */); + SystemCheckTimer(&five_hundred_ms_timer, &last_500_ms_tick, 10 /* 10 * 50 ms = 500 ms */); +#else // _PAL_MODE_ + SystemCheckTimer(&hundred_ms_timer, &last_100_ms_tick, 3); +#endif // _PAL_MODE_ + +} + +/* ******************************************************************************** + * + * @name void SystemCheckTimer(bool* timer, uint64_t* last_timer, uint8_t step) + * + * @author: Xavier Del Campo + * + * @brief: Checks if needed time step has been elapsed. If true, flag gets set. + * + * *******************************************************************************/ + +void SystemCheckTimer(bool* timer, uint64_t* last_timer, uint8_t step) +{ + if(*timer == true) + { + *timer = false; + } + + if(global_timer >= (*last_timer + step) ) + { + *timer = true; + *last_timer = global_timer; + } +} + +/* **************************************************************************************** + * + * @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 void SystemWaitCycles(uint32_t cycles) + * + * @author: Xavier Del Campo + * + * @return: halts program execution for n-"cycles" + * + * *****************************************************************/ + +void SystemWaitCycles(uint32_t cycles) +{ + uint64_t currentTime = global_timer; + + while(global_timer < (currentTime + cycles) ); +} + +/* ****************************************************************** + * + * @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 void SystemSetEmergencyMode(bool value) + * + * @author: Xavier Del Campo + * + * @brief: Sets emergency mode flag. + * + * @remarks: emergency mode is set once that a controller is unplugged. + * + * ***********************************************************************/ + +void SystemSetEmergencyMode(bool value) +{ + emergency_mode = value; +} + +/* *********************************************************************** + * + * @name bool SystemGetEmergencyMode(void) + * + * @author: Xavier Del Campo + * + * @return: returns emergency mode flag. + * + * ***********************************************************************/ + +bool SystemGetEmergencyMode(void) +{ + return emergency_mode; +} + +/* *********************************************************************** + * + * @name volatile bool SystemIsBusy(void) + * + * @author: Xavier Del Campo + * + * @return: returns system busy flag. + * + * ***********************************************************************/ + +volatile bool SystemIsBusy(void) +{ + return system_busy; +} + +/* **************************************************************************** + * + * @name bool SystemContains_u8(uint8_t value, uint8_t* buffer, size_t sz) + * + * @author: Xavier Del Campo + * + * @brief: checks if a certain value is contained in a buffer with size "sz". + * + * @return: true if value is contained inside buffer, false otherwise. + * + * ****************************************************************************/ + +bool SystemContains_u8(uint8_t value, uint8_t* buffer, size_t sz) +{ + size_t i = 0; + + for(i = 0; i < sz; i++) + { + if(buffer[i] == value) + { + return true; + } + } + + return false; +} + +/* **************************************************************************** + * + * @name bool SystemContains_u16(uint16_t value, uint16_t* buffer, size_t sz) + * + * @author: Xavier Del Campo + * + * @brief: checks if a certain value is contained in a buffer with size "sz". + * Variant for u16 variables. + * + * @return: true if value is contained inside buffer, false otherwise. + * + * ****************************************************************************/ + +bool SystemContains_u16(uint16_t value, uint16_t* buffer, size_t sz) +{ + size_t i = 0; + + for(i = 0; i < sz; i++) + { + if(buffer[i] == value) + { + return true; + } + } + + return false; +} + +/* ******************************************************************************************** + * + * @name TYPE_TIMER* SystemCreateTimer(uint32_t t, bool rf, void (*timer_callback)(void) ) + * + * @author: Xavier Del Campo + * + * @brief: fills a TYPE_TIMER structure with input parameters + * + * @param: uint32_t t: + * Timeout value (1 unit = 10 ms) + * bool rf: + * Repeat flag + * void (*timer_callback)(void) + * Function to be called on timeout + * + * @return: pointer to TYPE_TIMER structure if filled correctly, NULL pointer otherwise. + * + * ********************************************************************************************/ + +TYPE_TIMER* SystemCreateTimer(uint32_t t, bool rf, void (*timer_callback)(void) ) +{ + bool success = false; + uint8_t i; + + if(t == 0) + { + dprintf("Cannot create timer with time == 0!\n"); + return NULL; + } + + for(i = 0; i < SYSTEM_MAX_TIMERS; i++) + { + if(timer_array[i].busy == false) + { + timer_array[i].Timeout_Callback = timer_callback; + timer_array[i].time = t; + timer_array[i].orig_time = t; + timer_array[i].repeat_flag = rf; + timer_array[i].busy = true; + success = true; + break; + } + } + + if(success == false) + { + dprintf("Could not find any free timer!\n"); + return NULL; + } + + return &timer_array[i]; +} + +/* ******************************************* + * + * @name void SystemResetTimers(void) + * + * @author: Xavier Del Campo + * + * @brief: reportedly, removes all timers. + * + * *******************************************/ + +void SystemResetTimers(void) +{ + uint8_t i; + + for(i = 0; i < SYSTEM_MAX_TIMERS; i++) + { + SystemTimerRemove(&timer_array[i]); + } +} + +/* ***************************************************** + * + * @name void SystemUserTimersHandler(void) + * + * @author: Xavier Del Campo + * + * @brief: reportedly, handles all available timers. + * + * @remarks: calls callback on timeout. + * + * *****************************************************/ + +void SystemUserTimersHandler(void) +{ + uint8_t i; + + for(i = 0; i < SYSTEM_MAX_TIMERS; i++) + { + if(timer_array[i].busy == true) + { + if(System100msTick() == true) + { + timer_array[i].time--; + + if(timer_array[i].time == 0) + { + timer_array[i].Timeout_Callback(); + + if(timer_array[i].repeat_flag == true) + { + timer_array[i].time = timer_array[i].orig_time; + } + } + } + } + } +} + +/* ********************************************************************* + * + * @name void SystemUserTimersHandler(void) + * + * @author: Xavier Del Campo + * + * @brief: sets time left for TYPE_TIMER instance to initial value. + * + * @remarks: specially used when TYPE_TIMER.rf is enabled. + * + * *********************************************************************/ + +void SystemTimerRestart(TYPE_TIMER* timer) +{ + timer->time = timer->orig_time; +} + +/* ********************************************************************* + * + * @name void SystemTimerRemove(TYPE_TIMER* timer) + * + * @author: Xavier Del Campo + * + * @brief: Resets timer parameters to default values so timer instance + * can be recycled. + * + * @remarks: + * + * *********************************************************************/ + +void SystemTimerRemove(TYPE_TIMER* timer) +{ + timer->time = 0; + timer->orig_time = 0; + timer->Timeout_Callback = NULL; + timer->busy = false; + timer->repeat_flag = false; +} + +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; +} + +int32_t SystemIndexOf_U16(uint16_t value, uint16_t* array, uint32_t sz) +{ + int32_t i; + + for(i = 0; i < sz; i++) + { + if(value == array[i]) + { + return i; + } + } + + return -1; +} + +int32_t SystemIndexOf_U8(uint8_t value, uint8_t* array, uint32_t from, uint32_t sz) +{ + int32_t i; + + for(i = from; i < sz; i++) + { + if(value == array[i]) + { + return i; + } + } + + return -1; +} + +void SystemCyclicHandler(void) +{ + if(UpdatePads() == false) + { + SystemSetEmergencyMode(true); + } + else + { + SystemSetEmergencyMode(false); + } + + SystemRunTimers(); + + SystemUserTimersHandler(); + + SystemDisableScreenRefresh(); + SystemCheckStack(); +} + +void SystemDisableVBlankInterrupt(void) +{ + I_MASK &= ~(0x0001); +} + +void SystemEnableVBlankInterrupt(void) +{ + I_MASK |= (0x0001); +} diff --git a/Source/System.h b/Source/System.h new file mode 100644 index 0000000..89f6be4 --- /dev/null +++ b/Source/System.h @@ -0,0 +1,135 @@ +#ifndef __SYSTEM_HEADER__ +#define __SYSTEM_HEADER__ + +/* ************************************** + * Includes * + * **************************************/ + +#include "Global_Inc.h" +#include "Gfx.h" +#include "Serial.h" + +/* ************************************** + * Defines * + * **************************************/ + +#define TIMER_PRESCALER_1_SECOND 10 +#define TIMER_PRESCALER_1_MINUTE (TIMER_PRESCALER_1_SECOND * 60) + +/* ************************************** + * Global Prototypes * + * **************************************/ + +// Calls PSXSDK init routines +void SystemInit(void); + +// Sets default VSync (only sets flag to true and increases global_timer) +void ISR_SystemDefaultVBlank(void); + +void SystemSetBusyFlag(bool value); + +// Calls srand() using current global_timer value as seed +void SystemSetRandSeed(void); + +// Returns VSync flag value +bool SystemRefreshNeeded(void); + +// Loads a file into system's internal buffer +bool SystemLoadFile(char*fname); + +// Loads a file into desired buffer +bool SystemLoadFileToBuffer(char* fname, uint32_t init_pos, uint8_t* buffer, uint32_t szBuffer); + +// Clears VSync flag after each frame +void SystemDisableScreenRefresh(void); + +// Returns file buffer address +uint8_t* SystemGetBufferAddress(void); + +// Tells whether srand() has been called using a pseudo-random value +bool SystemIsRandSeedSet(void); + +// Stops program flow during X cycles +void SystemWaitCycles(uint32_t cycles); + +// To be called from GfxDrawScene after each cycle +void SystemRunTimers(void); + +// 1 cycle-length flag with a frequency of 1 Hz +bool System1SecondTick(void); + +// 1 cycle-length flag with a frequency of 2 Hz +bool System500msTick(void); + +// 1 cycle-length flag with a frequency of 10 Hz +bool System100msTick(void); + +// Returns random value between given minimum and maximum values +uint32_t SystemRand(uint32_t min, uint32_t max); + +// Increases global timer by 1 step +void SystemIncreaseGlobalTimer(void); + +// Sets value to emergency mode flag +void SystemSetEmergencyMode(bool value); + +// Returns emergency mode flag state +bool SystemGetEmergencyMode(void); + +// (Experimental) +uint64_t SystemGetGlobalTimer(void); + +// Returns whether critical section of code is being entered +volatile bool SystemIsBusy(void); + +// Returns whether indicated value is contained inside buffer +bool SystemContains_u8(uint8_t value, uint8_t* buffer, size_t sz); + +// Overload for uint16_t +bool SystemContains_u16(uint16_t value, uint16_t* buffer, size_t sz); + +// Creates a timer instance wiht a determined value and associates it to a callback +// Once time expires, callback is automatically called right after GfxDrawScene(). +// Time is expressed so that t = 100 ms e.g.: 2 seconds = 20. +TYPE_TIMER* SystemCreateTimer(uint32_t t, bool rf, void (*timer_callback)(void) ); + +// Reportedly, sets all timer data to zero. +void SystemResetTimers(void); + +// To be called every cycle (i.e.: inside GfxDrawScene() ). +void SystemUserTimersHandler(void); + +// Sets timer remaining time to initial value. +void SystemTimerRestart(TYPE_TIMER* timer); + +// Flushes a timer pointed to by timer. +void SystemTimerRemove(TYPE_TIMER* timer); + +// Compares two arrays of unsigned short type. +bool SystemArrayCompare(unsigned short* arr1, unsigned short* arr2, size_t sz); + +// Looks for string "str" inside a string array pointed to by "array". +// Returns index inside string array on success, -1 if not found. +int32_t SystemIndexOfStringArray(char* str, char** array); + +// Function overload for uint16_t data type. +int32_t SystemIndexOf_U16(uint16_t value, uint16_t* array, uint32_t sz); + +// Function overload for uint8_t data type. +int32_t SystemIndexOf_U8(uint8_t value, uint8_t* array, uint32_t from, uint32_t sz); + +void SystemCyclicHandler(void); + +size_t SystemGetBufferSize(void); + +void SystemClearBuffer(void); + +void SystemDisableVBlankInterrupt(void); + +void SystemEnableVBlankInterrupt(void); + +/* ************************************** + * Global Variables * + * **************************************/ + +#endif //__SYSTEM_HEADER__ diff --git a/Source/main.c b/Source/main.c new file mode 100644 index 0000000..6e2b227 --- /dev/null +++ b/Source/main.c @@ -0,0 +1,164 @@ +/* ************************************* + * 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(); + + if(1) + { + uint32_t initPC_Address; + uint32_t RAMDest_Address; + uint32_t ExeSize = 0; + uint32_t i; + void (*exeAddress)(void); + + 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); + + SystemDisableVBlankInterrupt(); + + 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 + } + + SystemEnableVBlankInterrupt(); + + //SystemLoadFileToBuffer("cdrom:\\AIRPORT.EXE;1", 2048, (uint8_t*)0x80010000, (uint32_t) (0x801A0000 - 0x80010000) ); + + SetVBlankHandler(&ISR_SystemDefaultVBlank); + + // Make a pretty animation before exeting OpenSend application. + + EndAnimation(); + + // PSX-EXE has been successfully loaded into RAM. Run executable! + + //dprintf("Entering exe...\n"); + + exeAddress(); + } + + return 0; +} |
