summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore47
-rw-r--r--Source/EndAnimation.c233
-rw-r--r--Source/EndAnimation.h25
-rw-r--r--Source/Font.c209
-rw-r--r--Source/Font.h44
-rw-r--r--Source/GameStructures.h57
-rw-r--r--Source/Gfx.c595
-rw-r--r--Source/Gfx.h115
-rw-r--r--Source/Global_Inc.h41
-rw-r--r--Source/LoadMenu.c204
-rw-r--r--Source/LoadMenu.h29
-rw-r--r--Source/Makefile74
-rw-r--r--Source/Pad.c530
-rw-r--r--Source/Pad.h62
-rw-r--r--Source/Serial.c271
-rw-r--r--Source/Serial.h49
-rw-r--r--Source/System.c887
-rw-r--r--Source/System.h135
-rw-r--r--Source/main.c164
-rw-r--r--Sprites/Font_2.bmpbin0 -> 18486 bytes
-rw-r--r--Sprites/Font_2_4bit.bmpbin0 -> 3190 bytes
-rw-r--r--TestLevel.lvlbin0 -> 1088 bytes
-rw-r--r--cdimg/DATA/FONTS/FONT_2.FNTbin0 -> 3136 bytes
-rw-r--r--cdimg/SYSTEM.CNF4
24 files changed, 3775 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e2dda68
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,47 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# PSX disk images
+*.bin
+*.cue
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
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;
+}
diff --git a/Sprites/Font_2.bmp b/Sprites/Font_2.bmp
new file mode 100644
index 0000000..878437b
--- /dev/null
+++ b/Sprites/Font_2.bmp
Binary files differ
diff --git a/Sprites/Font_2_4bit.bmp b/Sprites/Font_2_4bit.bmp
new file mode 100644
index 0000000..fdd2473
--- /dev/null
+++ b/Sprites/Font_2_4bit.bmp
Binary files differ
diff --git a/TestLevel.lvl b/TestLevel.lvl
new file mode 100644
index 0000000..35deda0
--- /dev/null
+++ b/TestLevel.lvl
Binary files differ
diff --git a/cdimg/DATA/FONTS/FONT_2.FNT b/cdimg/DATA/FONTS/FONT_2.FNT
new file mode 100644
index 0000000..36361b2
--- /dev/null
+++ b/cdimg/DATA/FONTS/FONT_2.FNT
Binary files differ
diff --git a/cdimg/SYSTEM.CNF b/cdimg/SYSTEM.CNF
new file mode 100644
index 0000000..38bcde4
--- /dev/null
+++ b/cdimg/SYSTEM.CNF
@@ -0,0 +1,4 @@
+BOOT = cdrom:\OPENSEND.EXE;1
+TCB = 4
+EVENT = 6
+STACK = 801FF800