summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
committerXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
commit7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch)
treec28d0748652ad4b4222309e46e6cfc82c0906220 /examples
parenta2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff)
downloadpsxsdk-7c24e9a9b02b04dcaf9507acb94091ea70a2c02d.tar.gz
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'examples')
-rw-r--r--examples/Makefile25
-rw-r--r--examples/gtecube/Makefile8
-rw-r--r--examples/gtecube/gtecube.c448
-rw-r--r--examples/mandel/Makefile7
-rw-r--r--examples/mandel/mandel.c347
-rw-r--r--examples/memview/Makefile7
-rw-r--r--examples/memview/memview.c249
-rw-r--r--examples/memview/memview.txt70
-rw-r--r--examples/project.mk18
-rw-r--r--examples/psxmod/Makefile16
-rw-r--r--examples/psxmod/psxmod.c472
-rw-r--r--examples/psxpaint/Makefile7
-rw-r--r--examples/psxpaint/cursor.h20
-rw-r--r--examples/psxpaint/psxpaint.c371
-rw-r--r--examples/psxs3m/Makefile10
-rw-r--r--examples/psxs3m/mods/mario2_2.s3mbin0 -> 17808 bytes
-rw-r--r--examples/psxs3m/mods/mario3.s3mbin0 -> 9920 bytes
-rw-r--r--examples/psxs3m/psxs3m.c412
-rw-r--r--examples/psxsnake/Makefile12
-rw-r--r--examples/psxsnake/data/Makefile21
-rw-r--r--examples/psxsnake/data/apple.txt14
-rw-r--r--examples/psxsnake/data/apple.wavbin0 -> 57204 bytes
-rw-r--r--examples/psxsnake/data/backgrnd.bmpbin0 -> 230454 bytes
-rw-r--r--examples/psxsnake/data/bomb.wavbin0 -> 43436 bytes
-rw-r--r--examples/psxsnake/data/font.bmpbin0 -> 830 bytes
-rw-r--r--examples/psxsnake/data/music.wavbin0 -> 376798 bytes
-rw-r--r--examples/psxsnake/psxsnake.c666
-rw-r--r--examples/puzzle/Makefile9
-rw-r--r--examples/puzzle/puzzle.bmpbin0 -> 150582 bytes
-rw-r--r--examples/puzzle/puzzle.c216
-rw-r--r--examples/rottest/Makefile9
-rw-r--r--examples/rottest/image.bmpbin0 -> 32694 bytes
-rw-r--r--examples/rottest/rottest.c104
33 files changed, 3538 insertions, 0 deletions
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644
index 0000000..90bb904
--- /dev/null
+++ b/examples/Makefile
@@ -0,0 +1,25 @@
+include ../Makefile.cfg
+
+all:
+ $(MAKE_COMMAND) -C mandel
+ $(MAKE_COMMAND) -C memview
+ $(MAKE_COMMAND) -C psxmod
+ $(MAKE_COMMAND) -C psxpaint
+ $(MAKE_COMMAND) -C psxsnake
+ $(MAKE_COMMAND) -C puzzle
+ $(MAKE_COMMAND) -C rottest
+ $(MAKE_COMMAND) -C psxs3m
+ $(MAKE_COMMAND) -C gtecube
+
+clean:
+ $(MAKE_COMMAND) -C mandel clean
+ $(MAKE_COMMAND) -C memview clean
+ $(MAKE_COMMAND) -C psxmod clean
+ $(MAKE_COMMAND) -C psxpaint clean
+ $(MAKE_COMMAND) -C psxsnake clean
+ $(MAKE_COMMAND) -C puzzle clean
+ $(MAKE_COMMAND) -C rottest clean
+ $(MAKE_COMMAND) -C psxs3m clean
+ $(MAKE_COMMAND) -C gtecube clean
+
+distclean: clean
diff --git a/examples/gtecube/Makefile b/examples/gtecube/Makefile
new file mode 100644
index 0000000..98789d3
--- /dev/null
+++ b/examples/gtecube/Makefile
@@ -0,0 +1,8 @@
+PROJNAME = gtecube
+PROJ_LIBS = -lmeidogte
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/gtecube/gtecube.c b/examples/gtecube/gtecube.c
new file mode 100644
index 0000000..b5dda19
--- /dev/null
+++ b/examples/gtecube/gtecube.c
@@ -0,0 +1,448 @@
+/*
+ * GTE Graphics Example (Spinning Cube)
+ * 2019 Meido-Tek Productions
+ * 2019 modified by nextvolume for PSXSDK
+ *
+ * licensed under Mozilla Public License v2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <psx.h>
+#include <psxgpu.h>
+#include <meidogte.h>
+
+/* Screen resolution */
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+/* Screen center position */
+#define CENTERX SCREEN_XRES>>1
+#define CENTERY SCREEN_YRES>>1
+
+#define OT_LEN 0x8000
+
+/* For easier handling of vertex indices */
+typedef struct {
+ short v0,v1,v2,v3;
+} INDEX;
+
+/* Cube vertices */
+SVECTOR cube_verts[] = {
+ { -100, -100, -100, 0 },
+ { 100, -100, -100, 0 },
+ { -100, 100, -100, 0 },
+ { 100, 100, -100, 0 },
+ { 100, -100, 100, 0 },
+ { -100, -100, 100, 0 },
+ { 100, 100, 100, 0 },
+ { -100, 100, 100, 0 }
+};
+
+/* Cube face normals */
+SVECTOR cube_norms[] = {
+ { 0, 0, -ONE, 0 },
+ { 0, 0, ONE, 0 },
+ { 0, -ONE, 0, 0 },
+ { 0, ONE, 0, 0 },
+ { -ONE, 0, 0, 0 },
+ { ONE, 0, 0, 0 }
+};
+
+/* Cube vertex indices */
+INDEX cube_indices[] = {
+ { 0, 1, 2, 3 },
+ { 4, 5, 6, 7 },
+ { 5, 4, 0, 1 },
+ { 6, 7, 3, 2 },
+ { 0, 2, 5, 7 },
+ { 3, 1, 6, 4 }
+};
+
+/* Number of faces of cube */
+#define CUBE_FACES 6
+
+/* Light color matrix */
+/* Each column represents the color matrix of each light source and is */
+/* used as material color when using gte_ncs() or multiplied by a */
+/* source color when using gte_nccs(). 4096 is 1.0 in this matrix */
+/* A column of zeroes disables the light source. */
+MATRIX color_mtx = { .m = {
+ {ONE, 0, 0}, /* Red */
+ {ONE, 0, 0}, /* Green */
+ {ONE, 0, 0} /* Blue */ }
+};
+
+/* Light matrix */
+/* Each row represents a vector direction of each light source. */
+/* An entire row of zeroes disables the light source. */
+MATRIX light_mtx = { .m = {
+ /* X, Y, Z */
+ { -2048 , -2048 , -2048},
+ {0 , 0 , 0},
+ {0 , 0 , 0} }
+};
+
+
+/* Function declarations */
+void init(void);
+void display(void);
+
+unsigned int prim_list[OT_LEN];
+int current_buf = 0;
+volatile int vblank_happened = 0;
+
+GsPoly4 poly[CUBE_FACES]; /* Flat shaded quad primitive */
+
+static void program_vblank_handler(void) {
+ vblank_happened = 1;
+}
+
+enum {
+ OP_SPIN,
+ OP_COLORR1, OP_COLORG1, OP_COLORB1,
+ OP_COLORR2, OP_COLORG2, OP_COLORB2,
+ OP_COLORR3, OP_COLORG3, OP_COLORB3,
+ OP_LIGHTX1, OP_LIGHTY1, OP_LIGHTZ1,
+ OP_LIGHTX2, OP_LIGHTY2, OP_LIGHTZ2,
+ OP_LIGHTX3, OP_LIGHTY3, OP_LIGHTZ3,
+ OP_SCRXOFF, OP_SCRYOFF,
+ OP_SCRDEPTH,
+ OP_TRANSVX, OP_TRANSVY, OP_TRANSVZ,
+ OP_END
+};
+
+const char *operationNames[] = {"Change spinning speed",
+ "Change light color R1", "Change light color G1", "Change light color B1",
+ "Change light color R2", "Change light color G2", "Change light color B2",
+ "Change light color R3", "Change light color G3", "Change light color B3",
+ "Change light X1", "Change light Y1", "Change light Z1",
+ "Change light X2", "Change light Y2", "Change light Z2",
+ "Change light X3", "Change light Y3", "Change light Z3",
+ "Change screen X offset", "Change screen Y offset",
+ "Change screen depth",
+ "Change translation vector X", "Change translation vector Y",
+ "Change translation vector Z",
+ };
+
+int stopped = 0;
+int currentOperation = 0;
+
+int spinningSpeed = 16;
+int screenXOffset = CENTERX;
+int screenYOffset = CENTERY;
+int screenDepth = CENTERX;
+
+VECTOR pos = { .vx = 0, .vy = 0, .vz = 400 }; /* Translation vector for TransMatrix */
+
+#define COLOR_STEP 32
+#define LIGHT_STEP 128
+
+/* Main function */
+int main() {
+
+ int i,p,wasStart=0,wasLeft=0,wasRight=0;
+ int a,b;
+ short coord[2];
+ unsigned char rgb[3] = {255, 255, 0};
+ unsigned short padbuf;
+
+ SVECTOR rot; /* Rotation vector for Rotmatrix */
+ MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */
+
+ bzero(&rot, sizeof(rot));
+
+ /* Init graphics and GTE */
+ init();
+
+ /* Main loop */
+ while( 1 ) {
+ /* Set GTE offset (recommended method of centering) */
+ gte_SetGeomOffset( screenXOffset, screenYOffset );
+
+ /* Set screen depth (basically FOV control, W/2 works best) */
+ gte_SetGeomScreen(screenDepth);
+
+ /* Set light ambient color and light color matrix */
+ gte_SetBackColor( 63, 63, 63 );
+ gte_SetColorMatrix( &color_mtx );
+
+ /* Set rotation and translation to the matrix */
+ RotMatrix( &rot, &mtx );
+ TransMatrix( &mtx, &pos );
+
+ /* Multiply light matrix by rotation matrix so light source */
+ /* won't appear relative to the model's rotation */
+ MulMatrix0( &light_mtx, &mtx, &lmtx );
+
+ /* Set rotation and translation matrix */
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+
+ /* Set light matrix */
+ gte_SetLightMatrix( &lmtx );
+
+ if (!stopped) {
+ /* Make the cube SPEEN */
+ rot.vx += spinningSpeed;
+ rot.vz += spinningSpeed;
+ }
+
+ for( i=0; i<CUBE_FACES; i++ ) {
+
+ /* Load the first 3 vertices of a quad to the GTE */
+ gte_ldv3(
+ &cube_verts[cube_indices[i].v0],
+ &cube_verts[cube_indices[i].v1],
+ &cube_verts[cube_indices[i].v2] );
+
+ /* Rotation, Translation and Perspective Triple */
+ gte_rtpt();
+
+ /* Compute normal clip for backface culling */
+ gte_nclip();
+
+ /* Get result*/
+ gte_stopz( &p );
+
+ /* Skip this face if backfaced */
+ if( p < 0 ) {
+ poly[i].attribute = -1;
+ continue;
+ }
+
+ /* Calculate average Z for depth sorting */
+ gte_avsz4();
+ gte_stotz( &p );
+
+ /* Skip if clipping off */
+ /* (the shift right operator is to scale the depth precision) */
+ if( (p>>2) > OT_LEN ) {
+ poly[i].attribute = -1;
+ continue;
+ }
+
+ /* Set the projected vertices to the primitive */
+ gte_stsxy0( &coord );
+ poly[i].x[0] = coord[0];
+ poly[i].y[0] = coord[1];
+
+ gte_stsxy1( &coord );
+ poly[i].x[1] = coord[0];
+ poly[i].y[1] = coord[1];
+
+ gte_stsxy2( &coord );
+ poly[i].x[2] = coord[0];
+ poly[i].y[2] = coord[1];
+
+ /* Compute the last vertex and set the result */
+ gte_ldv0( &cube_verts[cube_indices[i].v3] );
+ gte_rtps();
+ gte_stsxy( &coord );
+ poly[i].x[3] = coord[0];
+ poly[i].y[3] = coord[1];
+
+ rgb[0] = 255;
+ rgb[1] = 255;
+ rgb[2] = 255;
+
+ gte_ldrgb( &rgb );
+
+ /* Load the face normal */
+ gte_ldv0( &cube_norms[i] );
+
+ /* Normal Color Single */
+ gte_nccs();
+
+ /* Store result to the primitive */
+ gte_strgb( &rgb );
+
+ poly[i].r = rgb[0];
+ poly[i].g = rgb[1];
+ poly[i].b = rgb[2];
+
+ poly[i].attribute = 0;
+ }
+
+ /* Swap buffers and draw the primitives */
+ display();
+
+ /* Wait for VBlank to happen */
+ vblank_happened = 0;
+ while(!vblank_happened);
+
+ /* Read joypad status */
+ PSX_ReadPad(&padbuf, NULL);
+
+ /* Logic for real-time 'configuration' of the cube */
+ if ((padbuf & PAD_START) && !wasStart) {
+ stopped = !stopped;
+ wasStart = 1;
+ }
+
+ if ((padbuf & PAD_LEFT) && !wasLeft) {
+ currentOperation--;
+
+ if (currentOperation < 0)
+ currentOperation = OP_END - 1;
+
+ wasLeft = 1;
+ }
+
+ if ((padbuf & PAD_RIGHT) && !wasRight) {
+ currentOperation++;
+
+ if (currentOperation >= OP_END)
+ currentOperation = 0;
+
+ wasRight = 1;
+ }
+
+ if (padbuf & PAD_UP) {
+ switch(currentOperation) {
+ case OP_SPIN:
+ spinningSpeed++;
+ break;
+ case OP_COLORR1 ... OP_COLORB3:
+ a = (currentOperation - OP_COLORR1);
+ b = a % 3;
+ a /= 3;
+
+ if(color_mtx.m[b][a] < ONE)
+ color_mtx.m[b][a]+=COLOR_STEP;
+ break;
+ case OP_LIGHTX1 ... OP_LIGHTZ3:
+ a = (currentOperation - OP_LIGHTX1);
+ b = a % 3;
+ a /= 3;
+
+ if(light_mtx.m[a][b] < ONE)
+ light_mtx.m[a][b]+=LIGHT_STEP;
+ break;
+ case OP_SCRXOFF:
+ screenXOffset++;
+ break;
+ case OP_SCRYOFF:
+ screenYOffset++;
+ break;
+ case OP_SCRDEPTH:
+ screenDepth++;
+ break;
+ case OP_TRANSVX:
+ pos.vx++;
+ break;
+ case OP_TRANSVY:
+ pos.vy++;
+ break;
+ case OP_TRANSVZ:
+ pos.vz++;
+ break;
+ }
+ }
+
+ if (padbuf & PAD_DOWN) {
+ switch(currentOperation) {
+ case OP_SPIN:
+ spinningSpeed--;
+ break;
+ case OP_COLORR1 ... OP_COLORB3:
+ a = (currentOperation - OP_COLORR1);
+ b = a % 3;
+ a /= 3;
+
+ if(color_mtx.m[b][a] > 0)
+ color_mtx.m[b][a]-=COLOR_STEP;
+ break;
+ case OP_LIGHTX1 ... OP_LIGHTZ3:
+ a = (currentOperation - OP_LIGHTX1);
+ b = a % 3;
+ a /= 3;
+
+ if(light_mtx.m[a][b] > 0)
+ light_mtx.m[a][b]-=LIGHT_STEP;
+ break;
+ case OP_SCRXOFF:
+ screenXOffset--;
+ break;
+ case OP_SCRYOFF:
+ screenYOffset--;
+ break;
+ case OP_SCRDEPTH:
+ screenDepth--;
+ break;
+ case OP_TRANSVX:
+ pos.vx--;
+ break;
+ case OP_TRANSVY:
+ pos.vy--;
+ break;
+ case OP_TRANSVZ:
+ pos.vz--;
+ break;
+ }
+ }
+
+ if (!(padbuf & PAD_START))
+ wasStart = 0;
+
+ if (!(padbuf & PAD_LEFT))
+ wasLeft = 0;
+
+ if (!(padbuf & PAD_RIGHT))
+ wasRight = 0;
+ }
+
+ return 0;
+}
+
+void init(void) {
+ /* Initialize PSXSDK */
+ PSX_InitEx( 0 );
+ /* Initialize the Graphic Synthesizer */
+ GsInit();
+ /* Clear video memory */
+ GsClearMem();
+ /* Set primitive list pointer */
+ GsSetList( prim_list );
+ /* Set automatic GPU waiting */
+ GsSetAutoWait();
+ /* Set 320x240 video mode */
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+ /* Set VBlank Handler */
+ SetVBlankHandler( program_vblank_handler );
+
+ /* Load text font in video memory */
+ GsLoadFont(768, 0, 768, 256);
+
+ /* Initialize the MeidoGTE library */
+ InitGeom();
+}
+
+void display(void) {
+ int i;
+
+ /* Change display and drawing environment settings */
+ /* for double buffering */
+ GsSetDispEnvSimple(0, current_buf ? 0 : 256);
+ GsSetDrawEnvSimple(0, current_buf ? 256 : 0, SCREEN_XRES, SCREEN_YRES);
+ current_buf = !current_buf;
+
+ /* Clear the screen with the specified color */
+ GsSortCls(63, 0, 127);
+
+ /* Add cube faces to drawing list */
+ for(i=0;i<CUBE_FACES;i++) {
+ if(poly[i].attribute != -1)
+ GsSortPoly4(&poly[i]);
+ }
+
+ /* If stopped, show so on the screen */
+ if (stopped)
+ GsPrintFont(0, 0, "Stopped");
+
+ /* Print current operation name on screen */
+ GsPrintFont(0, GsScreenH-16, "%s", operationNames[currentOperation]);
+
+ /* Draw list */
+ GsDrawList();
+}
diff --git a/examples/mandel/Makefile b/examples/mandel/Makefile
new file mode 100644
index 0000000..74759a0
--- /dev/null
+++ b/examples/mandel/Makefile
@@ -0,0 +1,7 @@
+PROJNAME = mandel
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/mandel/mandel.c b/examples/mandel/mandel.c
new file mode 100644
index 0000000..77eda4e
--- /dev/null
+++ b/examples/mandel/mandel.c
@@ -0,0 +1,347 @@
+/*
+ * Simple fractal generator for the PlayStation
+ *
+ * by Giuseppe Gatta
+ * Placed in the Public Domain.
+ *
+ * Consider enabling compiler optimizations, for instance this code is much faster
+ * if you add -O3 to the gcc command line.
+ *
+ * The fractals are rendered at a 160x120 resolution in an array in memory,
+ * then this array is uploaded to video memory and handled as 8-bit image data.
+ * We render at 160x120 to speed rendering up because the PlayStation is a slow machine
+ * for doing this kind of thing.
+ *
+ * A sprite primitive is used to display the image data on the screen, and the primitive
+ * is scaled twice horizontally and vertically to cover the 320x240 screen with a 160x120
+ * image.
+ *
+ * The fractal drawing code by default also exploits the simmetry of the Mandrelbot
+ * and Tricorn sets to further speed rendering up (2x). To have a real rendering of
+ * the Burning Ship fractal, though, you must disable simmetry because that fractal
+ * does not have this property.
+ *
+ * This is the description of the controls of this program:
+ *
+ * SELECT - Toggle palette (Red->Green->Blue->Yellow->Violet->Red->...)
+ * START - Render fractal
+ * X - Toggle type of fractal to render (Mandelbrot->Tricorn->Burning Ship->Mandelbrot->...)
+ * O - Toggle simmetry (on the screen as 'S')
+ * LEFT - Decrease multibrot power (displayed on the screen as 'M')
+ * RIGHT - Increase multibrot power
+ * UP - Decrease number of maximum iterations (on the screen as 'I')
+ * DOWN - Increase number of maximum iterations
+ */
+
+#include <psx.h>
+#include <stdio.h>
+
+unsigned char fractal_image_data[120][160];
+unsigned int prim_list[0x4000];
+unsigned short clut_buf[256];
+
+volatile int frac_x=0, frac_y=0;
+
+enum
+{
+ FRAC_MANDELBROT, // Normal mandelbrot
+ FRAC_TRICORN,
+ FRAC_BSHIP,
+ FRAC_TF,
+
+ FRAC_NOSIMM = 0x200,
+};
+
+/*
+ * Code based on http://reocities.com/CapeCanaveral/5003/mandel.htm
+ */
+
+void mandrelbrot_int(int width, int height, int maxI, int type, int M)
+{
+ #define FIXSIZE 25
+ #define mul(a,b) ((((long long)a)*(b))>>FIXSIZE)
+ #define fixpt(a) ((int)(((a)*(1<<FIXSIZE))))
+ #define integer(a) (((a)+(1<<(FIXSIZE-1)))>>FIXSIZE)
+
+ int x0,y0,x1,y1,p,q,xn;
+ double xmin=-2.5,ymin=-1.5,xmax=1.5,ymax=1.5,xs,ys;
+ int i,x,y,t;
+ int m;
+
+ xs=(xmax-xmin)/width;
+ ys=(ymax-ymin)/height;
+
+ t = type & 0xff;
+
+ for (y=0;y<((type&FRAC_NOSIMM)?height:(height/2));y++)
+ {
+ for (x=0;x<width;x++)
+ {
+ p=fixpt(xmin+x*xs); // c_re
+ q=fixpt(ymin+y*ys);
+ xn=0;
+ x0=0;
+ y0=0;
+ i=0;
+
+ while ((mul(xn,xn)+mul(y0,y0))<fixpt(4) && ++i<maxI)
+ {
+ switch(t)
+ {
+ case FRAC_BSHIP:
+ if(x0<0)x0=-x0;
+ if(y0<0)y0=-y0;
+ break;
+ case FRAC_TRICORN:
+ y0=-y0;
+ break;
+ }
+
+ x1=x0;
+ y1=y0;
+
+ for(m=1;m<M;m++)
+ {
+ xn=mul(x0,x1) - mul(y0,y1);
+ y0=mul(y0,x1) + mul(x0,y1);
+ x0=xn;
+ }
+
+ x0=xn;
+ x0+=p;
+ y0+=q;
+ }
+
+ if (i==maxI) i=1;
+ fractal_image_data[y][x] = (i*256)/maxI;
+
+ if(!(type & FRAC_NOSIMM))
+ fractal_image_data[(height-1)-y][x] = (i*256)/maxI;
+ }
+ }
+}
+
+volatile int display_is_old = 1;
+volatile int time_counter = 0;
+
+void prog_vblank_handler()
+{
+ display_is_old = 1;
+ time_counter++;
+}
+
+int frac_type = FRAC_MANDELBROT;
+unsigned int frac_maxI = 64;
+int frac_M=2;
+
+const char *frac_type_string[] =
+ { "Mandelbrot",
+ "Tricorn",
+ "BurningShip"};
+
+GsSprite frac_sprite;
+
+int main()
+{
+ int x, dbuf=0;
+
+ int waspal=0;
+ int wastype=0;
+ int wasrender=0;
+ int wassimm=0;
+ int wasMm=0,wasMp=0, wasIm=0, wasIp=0;
+ int rendering_time = -1;
+
+ unsigned short padbuf;
+
+ PSX_InitEx(PSX_INIT_CD);
+
+ GsInit();
+ GsSetList(prim_list);
+ GsClearMem();
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+ GsLoadFont(768, 0, 768, 256);
+ SetVBlankHandler(prog_vblank_handler);
+
+// Fill & upload CLUTs
+
+// Red CLUT
+ for(x = 0; x < 256; x++)
+ clut_buf[x] = ((x*4)>255?255:x*4)>>3;
+
+ LoadImage(clut_buf, 512, 256, 256, 1);
+ while(GsIsDrawing());
+
+// Green CLUT
+ for(x = 0; x < 256; x++)
+ clut_buf[x] = (((x*4)>255?255:x*4)>>3)<<5;
+
+ LoadImage(clut_buf, 512, 257, 256, 1);
+ while(GsIsDrawing());
+
+// Blue CLUT
+ for(x = 0; x < 256; x++)
+ clut_buf[x] = (((x*4)>255?255:x*4)>>3)<<10;
+
+ LoadImage(clut_buf, 512, 258, 256, 1);
+ while(GsIsDrawing());
+
+
+// Yellow CLUT
+ for(x = 0; x < 256; x++)
+ {
+ clut_buf[x] = ((x*4)>255?255:x*4)>>3;
+ clut_buf[x]|= clut_buf[x]<<5;
+ }
+
+ LoadImage(clut_buf, 512, 259, 256, 1);
+ while(GsIsDrawing());
+
+// Violet CLUT
+ for(x = 0; x < 256; x++)
+ {
+ clut_buf[x] = ((x*4)>255?255:x*4)>>3;
+ clut_buf[x]|= clut_buf[x]<<10;
+ }
+
+ LoadImage(clut_buf, 512, 260, 256, 1);
+ while(GsIsDrawing());
+
+ frac_sprite.tpage = 5;
+ frac_sprite.attribute = COLORMODE(COLORMODE_8BPP);
+ frac_sprite.u = 0;
+ frac_sprite.v = 0;
+ frac_sprite.cx = 512;
+ frac_sprite.cy = 256;
+ frac_sprite.x = 0;
+ frac_sprite.y = 0;
+ frac_sprite.w = 160;
+ frac_sprite.h = 120;
+ frac_sprite.r = frac_sprite.g = frac_sprite.b = NORMAL_LUMINOSITY;
+ frac_sprite.scalex = SCALE_ONE*2;
+ frac_sprite.scaley = SCALE_ONE*2;
+
+ time_counter = 0;
+ mandrelbrot_int(160, 120, frac_maxI, frac_type, frac_M);
+ rendering_time = time_counter * (GsScreenM==VMODE_NTSC?17:20);
+ LoadImage(fractal_image_data, 320, 0, 80, 120);
+ while(GsIsDrawing());
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ dbuf=!dbuf;
+ GsSetDispEnvSimple(0, dbuf?0:256);
+ GsSetDrawEnvSimple(0, dbuf?256:0, 320, 240);
+
+ GsSortCls(0,0,0);
+ GsSortSprite(&frac_sprite);
+
+ GsPrintFont(0, 0, "Type: %s", frac_type_string[(frac_type&0xff)]);
+ GsPrintFont(0, 8, "I=%d, M=%d, S=%c", frac_maxI, frac_M,
+ frac_type&FRAC_NOSIMM?'N':'Y');
+ GsPrintFont(0, 16, "Time: %d.%03d", rendering_time / 1000, rendering_time % 1000);
+
+ GsDrawList();
+ while(GsIsDrawing());
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if((padbuf & PAD_SELECT) && !waspal)
+ {
+ frac_sprite.cy++;
+ if(frac_sprite.cy>260)
+ frac_sprite.cy = 256;
+
+ waspal=1;
+ }
+
+ if((padbuf & PAD_CROSS) && !wastype)
+ {
+ frac_type++;
+ if((frac_type & 0xff) >= FRAC_TF)
+ frac_type&=~0xff;
+
+ wastype=1;
+ }
+
+ if((padbuf & PAD_START) && !wasrender)
+ {
+ GsPrintFont(0, 224, "Rendering...");
+ GsDrawList();
+ while(GsIsDrawing());
+
+ dbuf=!dbuf;
+ GsSetDispEnvSimple(0, dbuf?0:256);
+ GsSetDrawEnvSimple(0, dbuf?256:0, 320, 240);
+
+ time_counter = 0;
+ mandrelbrot_int(160, 120, frac_maxI, frac_type, frac_M);
+ rendering_time = time_counter * (GsScreenM==VMODE_NTSC?17:20);
+ LoadImage(fractal_image_data, 320, 0, 80, 120);
+ while(GsIsDrawing());
+
+ wasrender=1;
+ }
+
+ if((padbuf & PAD_CIRCLE) && !wassimm)
+ {
+ frac_type ^= FRAC_NOSIMM;
+ wassimm=1;
+ }
+
+ if((padbuf & PAD_LEFT) && !wasMm)
+ {
+ if(frac_M>2)frac_M--;
+ wasMm=1;
+ }
+
+ if((padbuf & PAD_RIGHT) && !wasMp)
+ {
+ frac_M++;
+ wasMp=1;
+ }
+
+ if((padbuf & PAD_UP) && !wasIm)
+ {
+ if(frac_maxI>1)frac_maxI--;
+ wasIm=1;
+ }
+
+ if((padbuf & PAD_DOWN) && !wasIp)
+ {
+ frac_maxI++;
+ wasIp=1;
+ }
+
+ if(!(padbuf & PAD_SELECT))
+ waspal=0;
+
+ if(!(padbuf & PAD_CROSS))
+ wastype=0;
+
+ if(!(padbuf & PAD_START))
+ wasrender=0;
+
+ if(!(padbuf & PAD_CIRCLE))
+ wassimm=0;
+
+ if(!(padbuf & PAD_LEFT))
+ wasMm=0;
+
+ if(!(padbuf & PAD_RIGHT))
+ wasMp=0;
+
+ if(!(padbuf & PAD_UP))
+ wasIm=0;
+
+ if(!(padbuf & PAD_DOWN))
+ wasIp=0;
+
+ display_is_old=0;
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/memview/Makefile b/examples/memview/Makefile
new file mode 100644
index 0000000..55f05eb
--- /dev/null
+++ b/examples/memview/Makefile
@@ -0,0 +1,7 @@
+PROJNAME = memview
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/memview/memview.c b/examples/memview/memview.c
new file mode 100644
index 0000000..bf2e04a
--- /dev/null
+++ b/examples/memview/memview.c
@@ -0,0 +1,249 @@
+/**
+ * PlayStation Memory Viewer
+ * by Giuseppe Gatta a.k.a. nextvolume
+ *
+ * PSXSDK Example - released to the Public Domain
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <psx.h>
+
+unsigned int cur_addr = 0x80000000;
+unsigned int prim_list[0x20000];
+
+int display_is_old = 1;
+volatile int should_read_pad = 1;
+
+void pshex_vblank_handler()
+{
+ should_read_pad = 1;
+}
+
+int main()
+{
+ unsigned short padbuf;
+ int y, f, x, x1;
+ int highres_mode=0;
+ int srp=0;
+ int wasup=0, wasdown=0, wasleft=0, wasright=0, wasl1=0, wasr1=0,
+ wasl2=0;
+ int wascross=0, wascircle=0, wasstart=0,wasselect=0;
+ unsigned int go_address = 0x80000000;
+ int go_pos = 0;
+ GsRectangle pshex_rect;
+
+
+ PSX_InitEx(0);
+
+ GsInit();
+ GsClearMem();
+
+ GsSetVideoMode(640, 240, VMODE_PAL);
+ GsSetList(prim_list);
+ GsSetDispEnvSimple(0, 0);
+ GsSetDrawEnvSimple(0, 0, 640, 240);
+ GsLoadFont(768, 0, 768, 256);
+
+ SetVBlankHandler(pshex_vblank_handler);
+
+ GsPrintFont(16, 16, "PAL/NTSC SELECTION");
+ GsPrintFont(16, 32, "X - PAL");
+ GsPrintFont(16, 40, "O - NTSC");
+ GsDrawList();
+ while(GsIsDrawing());
+
+ while(1)
+ {
+ if(should_read_pad)
+ {
+ PSX_ReadPad(&padbuf, NULL);
+
+ if((padbuf & PAD_CROSS))
+ {
+ GsSetVideoModeEx(640, 480, VMODE_PAL, 0, 1, 0);
+ wascross=1;
+ break;
+ }
+
+ if((padbuf & PAD_CIRCLE))
+ {
+ GsSetVideoModeEx(640, 480, VMODE_NTSC, 0, 1, 0);
+ wascircle=1;
+ break;
+ }
+
+ should_read_pad=0;
+ }
+ }
+
+ GsSetDrawEnvSimple(0, (GsScreenM==VMODE_PAL?8:0), 640, 480);
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ GsSortCls(0, 0, 255);
+
+ for(y = 0; y < (highres_mode?58:28); y++)
+ {
+ f = GsPrintFont(24, y*8, "%08x", cur_addr + (y*0x10));
+ x1 = prfont_get_fx(f) + 16;
+
+ for(x = 0; x < 16; x++)
+ {
+ if(( (x&3) == 0) && x>0)
+ x1+=16;
+
+ f = GsPrintFont(x1, y*8, "%02x", *((unsigned char*)(cur_addr + (y*0x10) + x)));
+ x1 = prfont_get_fx(f) + 8;
+ }
+
+ x1+=8;
+
+ for(x=0;x<16;x++)
+ {
+ GsPrintFont(x1, y*8, "%c", *((unsigned char*)(cur_addr + (y*0x10) + x)));
+ x1+=8;
+ }
+ }
+
+ pshex_rect.x = (go_pos * 8)+24;
+ pshex_rect.y = highres_mode?472:232;
+ pshex_rect.r = 255;
+ pshex_rect.g = 69;
+ pshex_rect.b = 0;
+ pshex_rect.attribute = 0;
+ pshex_rect.w = 8;
+ pshex_rect.h = 8;
+ GsSortRectangle(&pshex_rect);
+
+ GsPrintFont(24, highres_mode?472:232, "%08X", go_address);
+
+ GsDrawList();
+ while(GsIsDrawing());
+ display_is_old = 0;
+ }
+
+ if(should_read_pad)
+ {
+ PSX_ReadPad(&padbuf, NULL);
+
+ if((padbuf & PAD_UP) && !wasup)
+ {
+ cur_addr -= 0x10;
+ display_is_old = 1;
+ wasup = 1;
+ }
+
+ if((padbuf & PAD_DOWN) && !wasdown)
+ {
+ cur_addr += 0x10;
+ display_is_old = 1;
+ wasdown = 1;
+ }
+
+ if((padbuf & PAD_LEFT) && !wasleft)
+ {
+ cur_addr -= 0x100;
+ display_is_old = 1;
+ wasleft=1;
+ }
+
+ if((padbuf & PAD_RIGHT) && !wasright)
+ {
+ cur_addr += 0x100;
+ display_is_old = 1;
+ wasright=1;
+ }
+
+ if((padbuf & PAD_L1) && !wasl1)
+ {
+ if(go_pos>0)go_pos--;
+ display_is_old = 1;
+ wasl1=1;
+ }
+
+ if((padbuf & PAD_R1) && !wasr1)
+ {
+ if(go_pos<7)go_pos++;
+ display_is_old = 1;
+ wasr1=1;
+ }
+
+ if((padbuf & PAD_L2) && !wasl2)
+ {
+ go_pos = 0;
+ wasl2 = 1;
+ }
+
+ if((padbuf & PAD_CROSS) && !wascross)
+ {
+ if(((go_address >> ((7-go_pos)<<2))&0xf) < 0xf)
+ go_address += 1 << ((7-go_pos)<<2);
+
+ display_is_old = 1;
+ wascross=1;
+ }
+
+ if((padbuf & PAD_CIRCLE) && !wascircle)
+ {
+ if(((go_address >> ((7-go_pos)<<2))&0xf) > 0)
+ go_address -= 1 << ((7-go_pos)<<2);
+
+ display_is_old = 1;
+ wascircle = 1;
+ }
+
+ if((padbuf & PAD_START) && !wasstart)
+ {
+ cur_addr = go_address;
+ display_is_old = 1;
+ wasstart=1;
+ }
+
+ if((padbuf & PAD_SELECT) && !wasselect)
+ {
+ highres_mode = !highres_mode;
+
+ GsSortCls(0,0,255);
+ GsDrawList();
+ while(GsIsDrawing());
+
+ if(highres_mode)
+ {
+ GsSetVideoModeEx(640, 480, GsScreenM, 0, 1, 0);
+ GsSetDrawEnvSimple(0, (GsScreenM==VMODE_PAL)?8:0, 640, 480);
+ }
+ else
+ {
+ GsSetVideoMode(640, 240, GsScreenM);
+ GsSetDrawEnvSimple(0, (GsScreenM==VMODE_PAL)?8:0, 640, 240);
+ }
+
+ display_is_old = 1;
+ wasselect = 1;
+ }
+
+ if(!(padbuf & PAD_UP) || srp==5)wasup=0;
+ if(!(padbuf & PAD_DOWN) || srp==5)wasdown=0;
+ if(!(padbuf & PAD_LEFT) || srp==5)wasleft=0;
+ if(!(padbuf & PAD_RIGHT) || srp==5)wasright=0;
+ if(!(padbuf & PAD_L1))wasl1=0;
+ if(!(padbuf & PAD_R1))wasr1=0;
+ if(!(padbuf & PAD_L2))wasl2=0;
+ if(!(padbuf & PAD_CROSS) || srp==5)wascross=0;
+ if(!(padbuf & PAD_CIRCLE) || srp==5)wascircle=0;
+ if(!(padbuf & PAD_START)) wasstart=0;
+ if(!(padbuf & PAD_SELECT)) wasselect=0;
+
+ should_read_pad = 0;
+ srp++;
+ if(srp > 5) srp=0;
+ }
+ }
+}
+
+//AAAAAAAA 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF abcd
diff --git a/examples/memview/memview.txt b/examples/memview/memview.txt
new file mode 100644
index 0000000..dd21b57
--- /dev/null
+++ b/examples/memview/memview.txt
@@ -0,0 +1,70 @@
+This is a simple memory viewer for the PlayStation.
+It shows you what is contained at the desired memory addresses in a way similar to that of hex editors.
+
+Obviously it is not as straigthforward to use as similar programs for general purpose computers because the PlayStation lacks a keyboard.
+Also, *remember* that reading certain memory areas will crash the PlayStation hardware, and if that happens the only thing you can do is to reset the console.
+See "Surefire Areas" below for areas which surely won't crash your hardware.
+
+At start, the memory viewer will begin viewing memory at 0x80000000 (start of RAM),
+the resolution will be 640x240 (low-res) and the Go-Address will be 0x80000000.
+
+Terms:
+Go-Address The address that will be used for the
+ "jump-to address" operation
+
+Control:
+UP Decrease current address by 0x10
+DOWN Increase current address by 0x10
+LEFT Decrease current address by 0x100
+RIGHT Increase current address by 0x100
+
+L1 Move to previous location in Go-Address
+R1 Move to next location in Go-Address
+L2 Move to first location in Go-Address
+CROSS Increase nibble at current Go-Address location
+CIRCLE Decrease nibble at current Go-Address location
+
+START Jump to Go-Address,
+ i.e. make the Go-Address the current address
+
+SELECT Switch between hi-res and low-res mode
+ Hi-res mode: 640x480
+ Low-res mode: 640x240 (default)
+
+Surefire areas:
+0x80000000-0x801fffff PlayStation RAM (2 megabytes)
+0xbfc00000-0xbfc7ffff PlayStation BIOS ROM (512 kilobytes)
+
+Example of setting a Go-Address and jumping:
+
+Imagine that the current go-address is 0x80000000 and we want to jump to 0xC001D00D.
+NOTE: A nibble is 4-bits long, and each digit in a hexadecimal number is a nibble.
+
+- Move to the first location in Go-Address -> L2
+- Increase the nibble at current Go-Address location until it is 0xC
+ -> as its current value is 0x8, due to the current go-address being 0x80000000,
+ press CROSS 4 times
+ >> after this operation, the current address is 0xC0000000
+- Move to the next location in Go-Address -> R1
+- The nibble at this location is already 0x0, so move to the next location -> R1
+ >> after this operation, the current address is 0xC0000000
+- The nibble at this location is already 0x0, so move to the next location -> R1
+ >> after this operation, the current address is 0xC0000000
+- Increase the nibble at current Go-Address location until it is 0x1
+ -> as its current value is 0x0, press CROSS once
+ >> after this operation, the current address will be 0xC0010000
+- Move to the next location in Go-Address -> R1
+- Increase the nibble at current Go-Address location until it is 0xD
+ -> as its current value is 0x0, press CROSS 13 times
+ >> after this operation, the current address will be 0xC001D000
+- Move to the next location in Go-Address -> R1
+- The nibble at this location is already 0x0, so move to the next location -> R1
+ >> after this operation, the current address is 0xC001D000
+- The nibble at this location is already 0x0, so move to the next location -> R1
+ >> after this operation, the current address is 0xC001D000
+- Increase the nibble at current Go-Address location until it is 0xD
+ -> as its current value is 0x0, press CROSS 13 times
+ >> after this operation, the current address will be 0xC001D00D
+- Jump to Go-Address -> START
+ >> The current address is now 0xC001D00D
+
diff --git a/examples/project.mk b/examples/project.mk
new file mode 100644
index 0000000..5a72a80
--- /dev/null
+++ b/examples/project.mk
@@ -0,0 +1,18 @@
+# project.mk
+
+include ../../Makefile.cfg
+
+all: $(PROJNAME)_extra
+ mkdir -p cd_root
+ $(EXAMPLES_CC) $(EXAMPLES_CFLAGS) -DEXAMPLES_VMODE=$(EXAMPLES_VMODE) -o $(PROJNAME).elf $(PROJNAME).c \
+ $(EXAMPLES_LIBS) $(PROJ_LIBS) $(EXAMPLES_LDFLAGS)
+ elf2exe $(PROJNAME).elf $(PROJNAME).exe
+ cp $(PROJNAME).exe cd_root
+ systemcnf $(PROJNAME).exe > cd_root/system.cnf
+ $(MKISOFS_COMMAND) -o $(PROJNAME).hsf -V $(PROJNAME) -sysid PLAYSTATION cd_root
+ mkpsxiso $(PROJNAME).hsf $(PROJNAME).bin $(CDLIC_FILE)
+ rm -f $(PROJNAME).hsf
+
+clean: $(PROJNAME)_clean_extra
+ rm -f $(PROJNAME).bin $(PROJNAME).cue $(PROJNAME).exe $(PROJNAME).elf
+ rm -fr cd_root
diff --git a/examples/psxmod/Makefile b/examples/psxmod/Makefile
new file mode 100644
index 0000000..7a410ec
--- /dev/null
+++ b/examples/psxmod/Makefile
@@ -0,0 +1,16 @@
+PROJNAME = psxmod
+PROJ_LIBS = -lmodplay -lm -ladpcm
+
+include ../project.mk
+
+SAMPLES = $(patsubst mods/%.mod, mods/%.smp, $(wildcard mods/*.mod))
+
+$(PROJNAME)_extra: $(SAMPLES)
+ mkdir -p cd_root
+ cp -r mods cd_root
+
+$(PROJNAME)_clean_extra:
+ rm -f mods/*.smp
+
+mods/%.smp: mods/%.mod
+ mod4psx $< $@
diff --git a/examples/psxmod/psxmod.c b/examples/psxmod/psxmod.c
new file mode 100644
index 0000000..f11f164
--- /dev/null
+++ b/examples/psxmod/psxmod.c
@@ -0,0 +1,472 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <psx.h>
+#include <modplay.h>
+
+#if defined(EXAMPLES_VMODE) == VMODE_PAL
+ #define TICKS_PER_SECOND 50
+#else
+ #define TICKS_PER_SECOND 60
+#endif
+
+unsigned char fileBuffer[0x80000]; // 512 kilobytes
+
+unsigned int primList[0x8000];
+int dbuf = 0;
+volatile int display_is_old = 1;
+char current_dir[256] = "cdrom:";
+char name_buf[24];
+char name_buf2[32];
+ModMusic *music = NULL;
+int music_loop;
+char *music_player_fname;
+int music_player_time;
+short music_player_vol;
+int music_player_vol_pc;
+
+GsRectangle prog_rect;
+
+void program_vblank_handler()
+{
+ display_is_old = 1;
+}
+
+struct
+{
+ char name[20];
+ int size;
+ int type; /* 0 = file, 1 = directory */
+}file_list[256];
+
+int file_list_size = 0;
+int file_list_pos = 0;
+
+int wasUp = 0;
+int wasDown = 0;
+int wasEnter = 0;
+int wasLeft = 0;
+int wasRight = 0;
+
+void update_file_list()
+{
+ struct DIRENTRY de;
+ struct DIRENTRY *r;
+ char *cp;
+ int i, k=0;
+
+ if(strcmp(current_dir, "cdrom:") != 0)
+ {
+ strcpy(file_list[0].name, "..");
+ file_list[0].type = 1;
+ k=1;
+ }
+
+ r = firstfile("cdrom:*", &de);
+
+ for(i = k; i < 256 && r; i++)
+ {
+ if(de.name[0] == 0x1)
+ {
+// Throw away useless entry.
+ r = nextfile(&de);
+ i--;
+ continue;
+ }
+
+ memcpy(file_list[i].name, de.name, 20);
+
+ if((cp = strrchr(file_list[i].name, ';')))
+ {
+ *cp = '\0';
+ file_list[i].type = 0;
+ }
+ else
+ file_list[i].type = 1;
+
+ file_list[i].size = de.size;
+
+ r = nextfile(&de);
+ }
+
+ file_list[i].size = -1;
+ file_list_size = i;
+}
+
+void file_browser(int redraw)
+{
+ int i, k, ok, allowed_ext;
+ unsigned short padbuf;
+ char *cp;
+ FILE *f;
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if(wasUp)
+ wasUp++;
+
+ if(wasDown)
+ wasDown++;
+
+ if((padbuf & PAD_UP) && !wasUp)
+ {
+ if(file_list_pos > 0)
+ {
+ file_list_pos--;
+ redraw=1;
+ }
+
+ wasUp=1;
+ }
+
+ if((padbuf & PAD_DOWN) && !wasDown)
+ {
+ file_list_pos++;
+
+ if(file_list[file_list_pos].size == -1)
+ file_list_pos--;
+ else
+ redraw = 1;
+
+ wasDown = 1;
+ }
+
+ if((padbuf & PAD_CROSS) && !wasEnter)
+ {
+ if(file_list[file_list_pos].type == 1)
+ allowed_ext = 1;
+ else
+ allowed_ext = 0;
+
+ if((cp = strchr(file_list[file_list_pos].name, '.')))
+ {
+ if(strcmp(cp+1, "MOD") == 0)
+ allowed_ext = 1;
+ }
+
+ if(allowed_ext)
+ {
+ if(file_list[file_list_pos].type == 0)
+ {
+ if(music != NULL)
+ {
+ MODUnload(music);
+ music = NULL;
+ }
+
+// It is a file, we will play it
+
+// First check if there is a file which contains the
+// samples
+ strcpy(name_buf, file_list[file_list_pos].name);
+
+ if((cp = strchr(name_buf, '.')))
+ *(cp+1) = '\0';
+
+ strcat(name_buf, "SMP");
+
+ ok = 0;
+
+ if(!(padbuf & PAD_SELECT))
+ {
+ for(i = 0; file_list[i].size != -1; i++)
+ {
+ if(strcmp(name_buf, file_list[i].name) == 0)
+ {
+ ok = 1;
+ break;
+ }
+ }
+ }
+
+ if(ok)
+ {
+// We found a sample file
+ printf("Loading sample file for %s...\n",
+ file_list[file_list_pos].name);
+
+ sprintf(name_buf2, "cdrom:%s;1", name_buf);
+ printf("namebuf2=%s\n",name_buf2);
+
+ f = fopen(name_buf2, "rb");
+
+ // printf("SZ = %d\n", file_list[i].size);
+
+ fread(fileBuffer, sizeof(char), file_list[i].size, f);
+ fclose(f);
+
+ memcpy(name_buf, fileBuffer, 8);
+ name_buf[8] = 0;
+
+ MOD4PSX_Upload(fileBuffer, SPU_DATA_BASE_ADDR);
+ }
+ else
+ printf("No sample file found for %s!\n",
+ file_list[file_list_pos].name);
+
+ sprintf(name_buf2, "cdrom:%s;1", file_list[file_list_pos].name);
+
+ f = fopen(name_buf2, "rb");
+
+ fread(fileBuffer, sizeof(char), file_list[file_list_pos].size, f);
+
+ fclose(f);
+
+ music = MODLoadEx(fileBuffer, ok?MODLOAD_NOSAMPLES:0);
+
+ if(!ok)
+ MODUploadSamples(music, SPU_DATA_BASE_ADDR);
+
+ music_player_fname = file_list[file_list_pos].name;
+ music_player_time = 0;
+ music_player_vol = SPU_MAXVOL+1;
+ music_player_vol_pc = 100;
+ MODSetMaxVolume(music_player_vol-1);
+ music_loop = -1;
+ }
+ else
+ {
+// It is a directory, we will go inside it
+ if(strcmp(file_list[file_list_pos].name, "..") == 0)
+ {
+ *(strrchr(current_dir, '\\')) = '\0';
+
+ if(strcmp(current_dir, "cdrom:") == 0)
+ chdir("cdrom:\\");
+ else
+ chdir(current_dir);
+ }
+ else
+ {
+ strcat(current_dir, "\\");
+ strcat(current_dir, file_list[file_list_pos].name);
+ chdir(current_dir);
+ }
+
+ file_list_pos = 0;
+ update_file_list();
+ redraw = 1;
+ }
+ }
+
+ wasEnter = 1;
+ }
+
+ if(!(padbuf & PAD_UP) || wasUp >= 15)
+ wasUp = 0;
+ if(!(padbuf & PAD_DOWN) || wasDown >= 15)
+ wasDown = 0;
+ if(!(padbuf & PAD_CROSS))
+ wasEnter = 0;
+
+ /* Drawing */
+ if(!redraw)
+ return;
+
+
+
+ GsSortCls(0, 0, 0);
+ GsPrintFont(0, 0, "-= PsxMod =-");
+ GsPrintFont(0, 8, "Folder: <root>%s", current_dir + 6);
+ GsPrintFont(0, 16, "X to play, hold SELECT to convert SFX");
+
+ k=0;
+
+ if(file_list_size > 24)
+ {
+ if(file_list_pos > 12)
+ {
+ k = file_list_pos - 12;
+
+ if(k > (file_list_size - 26))
+ k = file_list_size - 26;
+ }
+ }
+
+ prog_rect.x = 0;
+ prog_rect.y = 32 + ((file_list_pos-k) * 8);
+ prog_rect.w = GsScreenW / 2;
+ prog_rect.h = 8;
+ prog_rect.r = 0;
+ prog_rect.g = 0;
+ prog_rect.b = 255;
+ prog_rect.attribute = 0;
+ GsSortRectangle(&prog_rect);
+
+ for(i = 0; file_list[i+k].size != -1; i++)
+ {
+ if(file_list[i+k].type == 1)
+ GsPrintFont(0, 32 + (i * 8), " +- %s", file_list[i+k].name);
+ else
+ GsPrintFont(0, 32 + (i * 8), " %s", file_list[i+k].name);
+ }
+
+ GsDrawList();
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+}
+
+void music_player_draw()
+{
+ unsigned short padbuf;
+ static int old_song_pos = 10000;
+ int change_vol = 0;
+
+ GsSortCls(0, 0, 0);
+ GsPrintFont(0, 0, "-= PsxMod =-");
+ GsPrintFont(0, 8, "File: %s", music_player_fname);
+ GsPrintFont(0, 16, "Title: %s", music->title);
+ GsPrintFont(0, 24, "pat:%03d/%03d pos:%02X spd:%d/%d",
+ music->song_pos, music->song_pos_num - 1, music->pat_pos,
+ music->ticks_division, music->beats_minute);
+
+ GsPrintFont(0, 32, "vol: %03d%%/100%% time: %d:%02d chn: %d",
+ music_player_vol_pc, music_player_time / (TICKS_PER_SECOND * 60),
+ (music_player_time % (TICKS_PER_SECOND * 60) ) / TICKS_PER_SECOND,
+ music->channel_num);
+
+ GsPrintFont(0, 56, "Press X to change music.");
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+ GsDrawList();
+
+ if(old_song_pos > music->song_pos)
+ music_player_time = 0;
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if(padbuf & PAD_UP)
+ {
+ music_player_vol+=32;
+ change_vol = 1;
+ }
+
+ if(padbuf & PAD_DOWN)
+ {
+ music_player_vol-=32;
+ change_vol = 1;
+ }
+
+ if(padbuf & PAD_LEFT)
+ {
+ if(!wasLeft)
+ {
+ music->pat_pos = 0;
+
+ if(music->song_pos > 0)
+ music->song_pos--;
+ }
+
+ wasLeft++;
+ }
+
+ if(padbuf & PAD_RIGHT)
+ {
+ if(!wasRight)
+ {
+ music->pat_pos = 0;
+
+ if(music->song_pos < (music->song_pos_num - 1))
+ music->song_pos++;
+ else
+ music->song_pos = 0;
+ }
+
+ wasRight++;
+ }
+
+ if(!(padbuf & PAD_LEFT) || wasLeft >= 15)
+ wasLeft = 0;
+
+ if(!(padbuf & PAD_RIGHT) || wasRight >= 15)
+ wasRight = 0;
+
+ if((padbuf & PAD_CROSS) && !wasEnter)
+ {
+ MODStop(music);
+ MODUnload(music);
+ music = NULL;
+ wasEnter = 1;
+ }
+
+ if(!(padbuf & PAD_CROSS))
+ wasEnter = 0;
+
+ if(music_player_vol < 0)
+ music_player_vol = 0;
+
+ if(music_player_vol > (SPU_MAXVOL+1))
+ music_player_vol = SPU_MAXVOL+1;
+
+ if(change_vol)
+ {
+ music_player_vol_pc = (int)((float)100 * ((float)music_player_vol / (float)(SPU_MAXVOL+1)));
+ MODSetMaxVolume((music_player_vol == 0)?0:(music_player_vol-1));
+ }
+
+ music_player_time++;
+
+ old_song_pos = music->song_pos;
+}
+
+int main()
+{
+ int redraw=1;
+ FILE *f;
+
+ PSX_Init();
+ GsInit();
+ SsInit();
+
+ GsClearMem();
+ GsSetAutoWait();
+ GsSetList(primList);
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+ GsLoadFont(768, 0, 768, 256);
+
+ SetVBlankHandler(program_vblank_handler);
+
+// Open any existing file before calling firstfile() for the first time.
+// If before calling firstfile() for the first time, you have never called open() on an existing file before,
+// every subsequent read will fail but it will report that files are opened ok!
+// This bug happens at least with the SCPH1001 BIOS version.
+// We try to open PSX.EXE and SYSTEM.CNF, as one of them must exist in order to boot from CDROM.
+
+ f = fopen("cdrom:PSX.EXE;1", "rb");
+ if(f)fclose(f);
+
+ f = fopen("cdrom:SYSTEM.CNF;1", "rb");
+ if(f)fclose(f);
+
+ update_file_list();
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ /* Update music */
+ if(music)
+ {
+ redraw = 1;
+ MODPlay(music, &music_loop);
+ music_player_draw();
+ }
+ else
+ {
+ file_browser(redraw);
+ redraw=0;
+ }
+
+ display_is_old = 0;
+ }
+ }
+}
diff --git a/examples/psxpaint/Makefile b/examples/psxpaint/Makefile
new file mode 100644
index 0000000..d1d5698
--- /dev/null
+++ b/examples/psxpaint/Makefile
@@ -0,0 +1,7 @@
+PROJNAME = psxpaint
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/psxpaint/cursor.h b/examples/psxpaint/cursor.h
new file mode 100644
index 0000000..4f1a9cf
--- /dev/null
+++ b/examples/psxpaint/cursor.h
@@ -0,0 +1,20 @@
+unsigned char cursor_tim[] =
+{
+16, 0, 0, 0, 8, 0, 0, 0, 44, 0, 0, 0, 128, 2, 224, 1,
+16, 0, 1, 0,
+0, 0, 0, 128, 255, 127, 0, 128, 0, 128, 0, 128,
+0, 128, 0, 128, 0, 128, 0, 128, 0, 8, 0, 128, 64, 16, 0, 128,
+64, 16, 0, 128, 204, 0, 0, 0, 128, 2, 0, 0, 4, 0, 24, 0,
+1, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0,
+33, 1, 0, 0, 0, 0, 0, 0, 33, 18, 0, 0, 0, 0, 0, 0,
+33, 34, 1, 0, 0, 0, 0, 0, 33, 34, 18, 0, 0, 0, 0, 0,
+33, 34, 34, 1, 0, 0, 0, 0, 33, 34, 34, 18, 0, 0, 0, 0,
+33, 34, 34, 34, 1, 0, 0, 0, 33, 34, 34, 34, 18, 0, 0, 0,
+33, 34, 34, 34, 34, 1, 0, 0, 33, 34, 34, 18, 17, 17, 0, 0,
+33, 34, 33, 18, 0, 0, 0, 0, 33, 18, 33, 18, 0, 0, 0, 0,
+33, 1, 16, 34, 1, 0, 0, 0, 17, 0, 16, 34, 1, 0, 0, 0,
+1, 0, 0, 33, 18, 0, 0, 0, 0, 0, 0, 33, 18, 0, 0, 0,
+0, 0, 0, 16, 34, 1, 0, 0, 0, 0, 0, 16, 34, 1, 0, 0,
+0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
diff --git a/examples/psxpaint/psxpaint.c b/examples/psxpaint/psxpaint.c
new file mode 100644
index 0000000..726b13b
--- /dev/null
+++ b/examples/psxpaint/psxpaint.c
@@ -0,0 +1,371 @@
+/*
+ * PSXPaint
+ */
+
+/*
+ * Video memory layout:
+ *
+ * [0-639],[0-479] - Screen memory
+ * [640-895],[480-511] - Color Look Up Tables
+ * [640-643],[0-23] - Cursor Image (16x24) (4bpp - real width: 16)
+ * [992-1023],[0-31] - Cursor Dirty Rectangle (32x32)
+ */
+
+#include <stdio.h>
+#include <psx.h>
+#include <psxgpu.h>
+#include "cursor.h"
+
+int cursor_x = 0, cursor_y = 0, cursor_speed = 1;
+int old_cursor_x, old_cursor_y;
+volatile unsigned int speed_counter = 0;
+int brush_type = 0;
+
+unsigned int primitive_list[4000];
+
+int paint_colors[20][3] = {
+ { 0, 0, 0 }, // #0 Black
+ { 255, 255, 255 }, // #1 White
+ { 255, 0, 0 }, // #2 Red
+ { 0, 255, 0 }, // #3 Green
+ { 0, 0, 255 }, // #4 Blue
+ { 255, 255, 0}, // #5 Yellow
+ { 255, 0, 255}, // #6 Magenta (Fuchsia)
+ { 0, 255, 255}, // #7 Aqua Green
+ { 255, 128, 0}, // #8 Orange
+ { 128, 255,0}, // #9 Lime Green
+
+ { 192, 192,255},// #10 Sky Blue
+ { 128,128,128}, // #11 Grey
+ { 128, 0,0 }, // #12 Dark Red
+ { 0, 128, 0}, // #13 Dark Green
+ { 0, 0, 128}, // #14 Dark Blue
+ { 128, 128, 0}, // #15 Dark Yellow
+ { 128, 0, 128}, // #16 Purple
+ { 0, 128, 128}, // #17 Dark Aqua
+ { 128, 64, 0}, // #18 Brown
+ { 64, 128, 0}, // #19 Dark Lime Green
+
+ /*{ 128, 0, 255}, // #10 Violet
+ { 128,128,128}, // #11 Grey
+ { 128, 0,0 }, // #12 Dark Red
+ { 0, 128, 0}, // #13 Dark Green
+ { 0, 0, 128}, // #14 Dark Blue
+ { 192, 192,255},// #15 Sky Blue
+ { 128, 128, 0}, // #16 Dark Yellow
+ { 150, 75, 0}, // #17 Brown
+ { 64, 64, 64}, // #18 Dark Grey
+ { 128, 255,0}, // #19 Lime Green
+ */
+};
+
+int current_color[3];
+
+void sort_color_boxes()
+{
+ GsRectangle colorbox;
+ int x, y, c=0;
+
+ for(y = 384; y < (384+32); y+=16)
+ {
+ for(x = 0; x < 640; x+=64)
+ {
+ colorbox.x = x;
+ colorbox.y = y;
+ colorbox.w = 64;
+ colorbox.h = 16;
+ colorbox.r = paint_colors[c][0];
+ colorbox.g = paint_colors[c][1];
+ colorbox.b = paint_colors[c][2];
+ colorbox.attribute = 0;
+
+ GsSortRectangle(&colorbox);
+
+ c++;
+ }
+ }
+}
+
+void load_ui_graphics()
+{
+ GsImage my_image;
+
+ GsImageFromTim(&my_image, cursor_tim);
+ GsUploadImage(&my_image);
+}
+
+void my_vblank_handler();
+
+void my_vblank_handler()
+{
+ speed_counter++;
+}
+
+int main()
+{
+ GsDispEnv my_dispenv;
+ GsDrawEnv my_drawenv;
+ GsSprite my_sprite;
+ GsRectangle colorbox;
+ GsDot my_dot;
+ unsigned short pad1;
+ unsigned int WasL2=0, WasR2=0, WasL1=0, WasR1=0, WasSelect = 0;
+ int x, y;
+
+ PSX_InitEx(PSX_INIT_NOBIOS);
+ GsInit();
+ GsClearMem();
+ SetVBlankHandler(my_vblank_handler);
+
+ // This has to be interlaced
+ GsSetVideoModeEx(640, 480, EXAMPLES_VMODE, 0, 1, 0);
+
+ my_dispenv.x = 0;
+ my_dispenv.y = 0;
+
+ GsSetDispEnv(&my_dispenv);
+
+ my_drawenv.dither = 0;
+ my_drawenv.draw_on_display = 1;
+ my_drawenv.x = 0;
+ my_drawenv.y = 0;
+ my_drawenv.w = 640;
+ my_drawenv.h = 512;
+ my_drawenv.ignore_mask = 0;
+ my_drawenv.set_mask = 0;
+
+ GsSetDrawEnv(&my_drawenv);
+
+ GsSetList(primitive_list);
+
+ load_ui_graphics();
+ while(GsIsDrawing());
+
+ colorbox.x = 0;
+ colorbox.y = 0;
+ colorbox.w = 640;
+ colorbox.h = 511;
+ colorbox.r = 255;
+ colorbox.g = 255;
+ colorbox.b = 255;
+ colorbox.attribute = 0;
+
+ GsSortRectangle(&colorbox);
+
+ sort_color_boxes();
+
+
+ my_sprite.x = 0;
+ my_sprite.y = 0;
+ my_sprite.tpage = 10;
+ my_sprite.u = 0;
+ my_sprite.v = 0;
+ my_sprite.attribute = 0;
+ my_sprite.cx = 640;
+ my_sprite.cy = 480;
+ my_sprite.r = my_sprite.g = my_sprite.b = NORMAL_LUMINOSITY;
+ my_sprite.scalex = my_sprite.scaley = 0;
+ my_sprite.w = 16;
+ my_sprite.h = 24;
+
+ GsDrawList();
+ while(GsIsDrawing());
+
+ // Backup 32x32 area
+ MoveImage(cursor_x, cursor_y, 992, 0, 32, 32);
+
+ my_dot.attribute = 0;
+
+ while(1)
+ {
+ while(speed_counter)
+ {
+ old_cursor_x = cursor_x;
+ old_cursor_y = cursor_y;
+
+ // Restore 32x32 area
+ MoveImage(992, 0, old_cursor_x, old_cursor_y, 32, 32);
+ while(GsIsDrawing());
+
+ PSX_ReadPad(&pad1, NULL);
+
+ if(pad1 & PAD_LEFT)
+ cursor_x-=cursor_speed;
+
+ if(pad1 & PAD_RIGHT)
+ cursor_x+=cursor_speed;
+
+ if(pad1 & PAD_UP)
+ cursor_y-=cursor_speed;
+
+ if(pad1 & PAD_DOWN)
+ cursor_y+=cursor_speed;
+
+ if(cursor_x <= 0)
+ cursor_x = 0;
+
+ if(pad1 & PAD_CROSS)
+ {
+ if(cursor_y >= 384)
+ {
+ y = (cursor_y - 384) >> 4;
+ x = cursor_x >> 6;
+
+ current_color[0] = paint_colors[(y * 10)+x][0];
+ current_color[1] = paint_colors[(y * 10)+x][1];
+ current_color[2] = paint_colors[(y * 10)+x][2];
+ }
+ else
+ {
+
+ switch(brush_type)
+ {
+ case 0:
+ if(cursor_y >= 384)
+ {
+ break;
+ }
+
+ my_dot.r = current_color[0];
+ my_dot.g = current_color[1];
+ my_dot.b = current_color[2];
+ my_dot.x = cursor_x;
+ my_dot.y = cursor_y;
+
+ GsSortDot(&my_dot);
+ break;
+ case 1:
+ if(cursor_y >= 380)
+ {
+ break;
+ }
+
+ my_dot.r = current_color[0];
+ my_dot.g = current_color[1];
+ my_dot.b = current_color[2];
+ my_dot.x = cursor_x + 1;
+ my_dot.y = cursor_y;
+
+ GsSortDot(&my_dot);
+
+ my_dot.x++;
+
+ GsSortDot(&my_dot);
+
+ my_dot.y++;
+ my_dot.x-=2;
+
+ GsSortDot(&my_dot);
+
+ my_dot.x++;
+
+ GsSortDot(&my_dot);
+
+ my_dot.x++;
+
+ GsSortDot(&my_dot);
+
+ my_dot.x++;
+
+ GsSortDot(&my_dot);
+
+ my_dot.y++;
+ my_dot.x-=2;
+
+ GsSortDot(&my_dot);
+
+ my_dot.x++;
+
+ GsSortDot(&my_dot);
+ break;
+ }
+
+ }
+
+ GsDrawList();
+ while(GsIsDrawing());
+ }
+
+ if((pad1 & PAD_R2) && !WasR2)
+ {
+ cursor_speed++;
+ WasR2 = 1;
+ }
+
+ if((pad1 & PAD_L2) && !WasL2)
+ {
+ cursor_speed--;
+ WasL2 = 1;
+ }
+
+ if(!(pad1 & PAD_R2))
+ WasR2 = 0;
+
+ if(!(pad1 & PAD_L2))
+ WasL2 = 0;
+
+ if((pad1 & PAD_R1) && !WasR1)
+ {
+ brush_type++;
+ WasR1 = 1;
+ }
+
+ if((pad1 & PAD_L1) && !WasL1)
+ {
+ brush_type--;
+ WasL1 = 1;
+ }
+
+ if((pad1 & PAD_SELECT) && !WasSelect)
+ {
+ my_sprite.attribute ^= (ENABLE_TRANS | TRANS_MODE(0));
+ my_dot.attribute ^= (ENABLE_TRANS | TRANS_MODE(0));
+ WasSelect = 1;
+ }
+
+ if(!(pad1 & PAD_SELECT))
+ WasSelect = 0;
+
+ if(!(pad1 & PAD_R1))
+ WasR1 = 0;
+
+ if(!(pad1 & PAD_L1))
+ WasL1 = 0;
+
+ if(cursor_speed <= 0)
+ cursor_speed = 1;
+
+ if(cursor_speed >= 8)
+ cursor_speed = 7;
+
+ if(brush_type <= 0)
+ brush_type = 0;
+
+ if(brush_type > 1)
+ brush_type = 1;
+
+ // Backup 32x32 area
+ MoveImage(cursor_x, cursor_y, 992, 0, 32, 32);
+ while(GsIsDrawing());
+
+ // if(cursor_x != old_cursor_x || cursor_y != old_cursor_y)
+ // {
+ // printf("cx = %d, cy = %d, cursor_speed = %d, brush_type = %d\n",
+ // cursor_x, cursor_y, cursor_speed, brush_type);
+
+
+
+
+
+ my_sprite.x = cursor_x;
+ my_sprite.y = cursor_y;
+ GsSortSimpleSprite(&my_sprite);
+
+ GsDrawList();
+ while(GsIsDrawing());
+ // }
+
+ speed_counter--;
+ }
+ }
+}
diff --git a/examples/psxs3m/Makefile b/examples/psxs3m/Makefile
new file mode 100644
index 0000000..81465be
--- /dev/null
+++ b/examples/psxs3m/Makefile
@@ -0,0 +1,10 @@
+PROJNAME = psxs3m
+PROJ_LIBS = -lf3m
+
+include ../project.mk
+
+$(PROJNAME)_extra: $(SAMPLES)
+ mkdir -p cd_root
+ cp -r mods cd_root
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/psxs3m/mods/mario2_2.s3m b/examples/psxs3m/mods/mario2_2.s3m
new file mode 100644
index 0000000..96220e9
--- /dev/null
+++ b/examples/psxs3m/mods/mario2_2.s3m
Binary files differ
diff --git a/examples/psxs3m/mods/mario3.s3m b/examples/psxs3m/mods/mario3.s3m
new file mode 100644
index 0000000..2d2cabc
--- /dev/null
+++ b/examples/psxs3m/mods/mario3.s3m
Binary files differ
diff --git a/examples/psxs3m/psxs3m.c b/examples/psxs3m/psxs3m.c
new file mode 100644
index 0000000..9fe677f
--- /dev/null
+++ b/examples/psxs3m/psxs3m.c
@@ -0,0 +1,412 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <psx.h>
+#include <f3m.h>
+
+#if defined(EXAMPLES_VMODE) == VMODE_PAL
+ #define TICKS_PER_SECOND 50
+#else
+ #define TICKS_PER_SECOND 60
+#endif
+
+unsigned int primList[0x8000];
+int dbuf = 0;
+volatile int display_is_old = 1;
+char current_dir[256] = "cdrom:";
+char name_buf[24];
+char name_buf2[32];
+mod_s *music = NULL;
+player_s player;
+int music_loop;
+char *music_player_fname;
+int music_player_time;
+short music_player_vol;
+int music_player_vol_pc;
+
+GsRectangle prog_rect;
+
+void program_vblank_handler()
+{
+ display_is_old = 1;
+}
+
+struct
+{
+ char name[20];
+ int size;
+ int type; /* 0 = file, 1 = directory */
+}file_list[256];
+
+int file_list_size = 0;
+int file_list_pos = 0;
+
+int wasUp = 0;
+int wasDown = 0;
+int wasEnter = 0;
+int wasLeft = 0;
+int wasRight = 0;
+
+void update_file_list()
+{
+ struct DIRENTRY de;
+ struct DIRENTRY *r;
+ char *cp;
+ int i, k=0;
+
+ if(strcmp(current_dir, "cdrom:") != 0)
+ {
+ strcpy(file_list[0].name, "..");
+ file_list[0].type = 1;
+ k=1;
+ }
+
+ r = firstfile("cdrom:*", &de);
+
+ for(i = k; i < 256 && r; i++)
+ {
+ if(de.name[0] == 0x1)
+ {
+// Throw away useless entry.
+ r = nextfile(&de);
+ i--;
+ continue;
+ }
+
+ memcpy(file_list[i].name, de.name, 20);
+
+ if((cp = strrchr(file_list[i].name, ';')))
+ {
+ *cp = '\0';
+ file_list[i].type = 0;
+ }
+ else
+ file_list[i].type = 1;
+
+ file_list[i].size = de.size;
+
+ r = nextfile(&de);
+ }
+
+ file_list[i].size = -1;
+ file_list_size = i;
+}
+
+void file_browser(int redraw)
+{
+ int i, k, allowed_ext;
+ unsigned short padbuf;
+ char *cp;
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if(wasUp)
+ wasUp++;
+
+ if(wasDown)
+ wasDown++;
+
+ if((padbuf & PAD_UP) && !wasUp)
+ {
+ if(file_list_pos > 0)
+ {
+ file_list_pos--;
+ redraw=1;
+ }
+
+ wasUp=1;
+ }
+
+ if((padbuf & PAD_DOWN) && !wasDown)
+ {
+ file_list_pos++;
+
+ if(file_list[file_list_pos].size == -1)
+ file_list_pos--;
+ else
+ redraw = 1;
+
+ wasDown = 1;
+ }
+
+ if((padbuf & PAD_CROSS) && !wasEnter)
+ {
+ if(file_list[file_list_pos].type == 1)
+ allowed_ext = 1;
+ else
+ allowed_ext = 0;
+
+ if((cp = strchr(file_list[file_list_pos].name, '.')))
+ {
+ if(strcmp(cp+1, "S3M") == 0)
+ allowed_ext = 1;
+ }
+
+ if(allowed_ext)
+ {
+ if(file_list[file_list_pos].type == 0)
+ {
+ if(music != NULL)
+ {
+ f3m_mod_free(music);
+ music = NULL;
+ }
+
+// It is a file, we will play it
+ sprintf(name_buf2, "cdrom:%s;1", file_list[file_list_pos].name);
+
+ music = f3m_mod_load_filename(name_buf2);
+ f3m_player_init(&player, music);
+
+ music_player_fname = file_list[file_list_pos].name;
+ music_player_time = 0;
+ music_player_vol = SPU_MAXVOL+1;
+ music_player_vol_pc = 100;
+
+ f3m_set_max_volume(&player, music_player_vol-1);
+
+ music_loop = -1;
+ }
+ else
+ {
+// It is a directory, we will go inside it
+ if(strcmp(file_list[file_list_pos].name, "..") == 0)
+ {
+ *(strrchr(current_dir, '\\')) = '\0';
+
+ if(strcmp(current_dir, "cdrom:") == 0)
+ chdir("cdrom:\\");
+ else
+ chdir(current_dir);
+ }
+ else
+ {
+ strcat(current_dir, "\\");
+ strcat(current_dir, file_list[file_list_pos].name);
+ chdir(current_dir);
+ }
+
+ file_list_pos = 0;
+ update_file_list();
+ redraw = 1;
+ }
+ }
+
+ wasEnter = 1;
+ }
+
+ if(!(padbuf & PAD_UP) || wasUp >= 15)
+ wasUp = 0;
+ if(!(padbuf & PAD_DOWN) || wasDown >= 15)
+ wasDown = 0;
+ if(!(padbuf & PAD_CROSS))
+ wasEnter = 0;
+
+ /* Drawing */
+ if(!redraw)
+ return;
+
+
+
+ GsSortCls(0, 0, 0);
+ GsPrintFont(0, 0, "-= PsxS3M =-");
+ GsPrintFont(0, 8, "Folder: <root>%s", current_dir + 6);
+ GsPrintFont(0, 16, "X to play, hold SELECT to convert SFX");
+
+ k=0;
+
+ if(file_list_size > 24)
+ {
+ if(file_list_pos > 12)
+ {
+ k = file_list_pos - 12;
+
+ if(k > (file_list_size - 26))
+ k = file_list_size - 26;
+ }
+ }
+
+ prog_rect.x = 0;
+ prog_rect.y = 32 + ((file_list_pos-k) * 8);
+ prog_rect.w = GsScreenW / 2;
+ prog_rect.h = 8;
+ prog_rect.r = 0;
+ prog_rect.g = 0;
+ prog_rect.b = 255;
+ prog_rect.attribute = 0;
+ GsSortRectangle(&prog_rect);
+
+ for(i = 0; file_list[i+k].size != -1; i++)
+ {
+ if(file_list[i+k].type == 1)
+ GsPrintFont(0, 32 + (i * 8), " +- %s", file_list[i+k].name);
+ else
+ GsPrintFont(0, 32 + (i * 8), " %s", file_list[i+k].name);
+ }
+
+ GsDrawList();
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+}
+
+void music_player_draw()
+{
+ unsigned short padbuf;
+ static int old_song_pos = 10000;
+ int change_vol = 0;
+
+ GsSortCls(0, 0, 0);
+ GsPrintFont(0, 0, "-= PsxS3M =-");
+ GsPrintFont(0, 8, "File: %s", music_player_fname);
+ GsPrintFont(0, 16, "Title: %s", music->name);
+ GsPrintFont(0, 24, "ord:%03d/%03d pat:%03d/%03d pos:%02X spd:%d/%d",
+ player.cord, music->ord_num - 1, player.cpat, music->pat_num - 1, player.crow,
+ music->ispeed, music->itempo);
+ GsPrintFont(0, 32, "vol: %03d%%/100%% time: %d:%02d",
+ music_player_vol_pc, music_player_time / (TICKS_PER_SECOND * 60),
+ (music_player_time % (TICKS_PER_SECOND * 60) ) / TICKS_PER_SECOND);
+
+ GsPrintFont(0, 56, "Press X to change music.");
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+ GsDrawList();
+
+ if(old_song_pos > player.cord)
+ music_player_time = 0;
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if(padbuf & PAD_UP)
+ {
+ music_player_vol+=32;
+ change_vol = 1;
+ }
+
+ if(padbuf & PAD_DOWN)
+ {
+ music_player_vol-=32;
+ change_vol = 1;
+ }
+
+ if(padbuf & PAD_LEFT)
+ {
+ if(!wasLeft)
+ {
+ player.crow = 0;
+ player.cord--;
+ player.crow = 64;
+
+ if(player.cord > -1)
+ player.cord--;
+ }
+
+ wasLeft++;
+ }
+
+ if(padbuf & PAD_RIGHT)
+ {
+ if(!wasRight)
+ {
+ player.crow = 64;
+ }
+
+ wasRight++;
+ }
+
+ if(!(padbuf & PAD_LEFT) || wasLeft >= 15)
+ wasLeft = 0;
+
+ if(!(padbuf & PAD_RIGHT) || wasRight >= 15)
+ wasRight = 0;
+
+ if((padbuf & PAD_CROSS) && !wasEnter)
+ {
+ f3m_player_stop(&player);
+ f3m_mod_free(music);
+ music = NULL;
+
+ wasEnter = 1;
+ }
+
+ if(!(padbuf & PAD_CROSS))
+ wasEnter = 0;
+
+ if(music_player_vol < 0)
+ music_player_vol = 0;
+
+ if(music_player_vol > (SPU_MAXVOL+1))
+ music_player_vol = SPU_MAXVOL+1;
+
+ if(change_vol)
+ {
+ music_player_vol_pc = (int)((float)100 * ((float)music_player_vol / (float)(SPU_MAXVOL+1)));
+ f3m_set_max_volume(&player, (music_player_vol == 0)?0:(music_player_vol-1));
+ }
+
+ music_player_time++;
+
+ old_song_pos = player.cord;
+}
+
+int main()
+{
+ int redraw=1;
+ FILE *f;
+
+ PSX_Init();
+ GsInit();
+ SsInit();
+
+ GsClearMem();
+ GsSetAutoWait();
+ GsSetList(primList);
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+ GsLoadFont(768, 0, 768, 256);
+
+ SetVBlankHandler(program_vblank_handler);
+
+// Open any existing file before calling firstfile() for the first time.
+// If before calling firstfile() for the first time, you have never called open() on an existing file before,
+// every subsequent read will fail but it will report that files are opened ok!
+// This bug happens at least with the SCPH1001 BIOS version.
+// We try to open PSX.EXE and SYSTEM.CNF, as one of them must exist in order to boot from CDROM.
+
+ f = fopen("cdrom:PSX.EXE;1", "rb");
+ if(f)fclose(f);
+
+ f = fopen("cdrom:SYSTEM.CNF;1", "rb");
+ if(f)fclose(f);
+
+ update_file_list();
+
+ dbuf = !dbuf;
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ /* Update music */
+ if(music)
+ {
+ redraw = 1;
+ f3m_player_play(&player);
+ // MODPlay(music, &music_loop);
+ music_player_draw();
+ }
+ else
+ {
+ file_browser(redraw);
+ redraw=0;
+ }
+
+ display_is_old = 0;
+ }
+ }
+}
diff --git a/examples/psxsnake/Makefile b/examples/psxsnake/Makefile
new file mode 100644
index 0000000..033e1bc
--- /dev/null
+++ b/examples/psxsnake/Makefile
@@ -0,0 +1,12 @@
+PROJNAME = psxsnake
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+ $(MAKE_COMMAND) -C data
+ mkdir -p cd_root
+ cp data/*.tim cd_root
+ cp data/*.raw cd_root
+
+$(PROJNAME)_clean_extra:
+ $(MAKE_COMMAND) -C data clean
diff --git a/examples/psxsnake/data/Makefile b/examples/psxsnake/data/Makefile
new file mode 100644
index 0000000..da14ec1
--- /dev/null
+++ b/examples/psxsnake/data/Makefile
@@ -0,0 +1,21 @@
+include ../../../Makefile.cfg
+
+data: backgrnd.tim font.tim music.raw bomb.raw apple.raw
+
+backgrnd.tim: backgrnd.bmp
+ bmp2tim backgrnd.bmp backgrnd.tim 16 -org=320,0
+
+font.tim: font.bmp
+ bmp2tim font.bmp font.tim 4 -org=640,0
+
+music.raw: music.wav
+ wav2vag music.wav music.raw -L -raw
+
+bomb.raw: bomb.wav
+ wav2vag bomb.wav bomb.raw -raw
+
+apple.raw: apple.wav
+ wav2vag apple.wav apple.raw -raw
+
+clean:
+ rm -f *.tim *.raw
diff --git a/examples/psxsnake/data/apple.txt b/examples/psxsnake/data/apple.txt
new file mode 100644
index 0000000..dbcfc27
--- /dev/null
+++ b/examples/psxsnake/data/apple.txt
@@ -0,0 +1,14 @@
+File:
+ Name: "Apple_Crunch_16.wav"
+ Url: http://www.freesound.org/samplesViewSingle.php?id=20279
+ Date of upload: 2006-06-28 11:44:45
+
+Designer / Creator / Uploader:
+ Name: "Koops"
+ Url: http://www.freesound.org/usersViewSingle.php?id=29508
+
+Description:
+ By "Koops" : 16/18 Apple related eating noises. Recorded in my studio with a matched pair of Rode NT5\'s in the XY configuration. Stereo, WAV, 44.1KHz, 16Bit, Unprocessed.
+
+Tags:
+ apple bite crunch eating food snack squelch \ No newline at end of file
diff --git a/examples/psxsnake/data/apple.wav b/examples/psxsnake/data/apple.wav
new file mode 100644
index 0000000..2643783
--- /dev/null
+++ b/examples/psxsnake/data/apple.wav
Binary files differ
diff --git a/examples/psxsnake/data/backgrnd.bmp b/examples/psxsnake/data/backgrnd.bmp
new file mode 100644
index 0000000..d70e1eb
--- /dev/null
+++ b/examples/psxsnake/data/backgrnd.bmp
Binary files differ
diff --git a/examples/psxsnake/data/bomb.wav b/examples/psxsnake/data/bomb.wav
new file mode 100644
index 0000000..a3935e0
--- /dev/null
+++ b/examples/psxsnake/data/bomb.wav
Binary files differ
diff --git a/examples/psxsnake/data/font.bmp b/examples/psxsnake/data/font.bmp
new file mode 100644
index 0000000..070704c
--- /dev/null
+++ b/examples/psxsnake/data/font.bmp
Binary files differ
diff --git a/examples/psxsnake/data/music.wav b/examples/psxsnake/data/music.wav
new file mode 100644
index 0000000..ae5c8df
--- /dev/null
+++ b/examples/psxsnake/data/music.wav
Binary files differ
diff --git a/examples/psxsnake/psxsnake.c b/examples/psxsnake/psxsnake.c
new file mode 100644
index 0000000..c4260b2
--- /dev/null
+++ b/examples/psxsnake/psxsnake.c
@@ -0,0 +1,666 @@
+/*
+ * Snake clone for the PSX
+ */
+
+#include <psx.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int vmode;
+volatile int speed_counter = 0;
+volatile int screen_old = 0;
+
+int snake_array[29][40];
+int scc = 0;
+int vibration_cntdown = 0;
+
+int snake_size = 3;
+int game_over = 0;
+
+int rectangle_mode = 0;
+int l1_pressed = 0;
+
+int level_number = 1;
+int score = 0;
+volatile int seed_counter = 0;
+
+int sample_pos[3]; // 0 = music, 1 = collision, 2 = apple
+
+unsigned int game_draw_list[0x4000]; /* 128 kilobytes */
+
+enum
+{
+ SNAKE_DIR_LEFT, SNAKE_DIR_RIGHT, SNAKE_DIR_UP, SNAKE_DIR_DOWN
+};
+
+int snake_dir = SNAKE_DIR_RIGHT;
+
+unsigned char file_buffer[0x30000]; /* 192 kilobytes */
+
+void game_init();
+void game_vblank_handler();
+void game_run();
+void game_run_gameover();
+int pal_or_ntsc_selection();
+int main();
+int check_snake_collision(int x, int y);
+int load_file_into_buffer(char *fname);
+void game_print(char *string, int x, int y);
+void game_center_print(char *string, int x, int y);
+void setup_snake_field();
+void game_setup();
+
+GsDrawEnv game_drawenv;
+GsDispEnv game_dispenv;
+
+GsImage game_image;
+GsSprite game_sprite;
+GsRectangle game_rect;
+
+unsigned short game_clut[16];
+
+char string_buf[256];
+
+int cross_pressed = 0;
+int circle_pressed = 0;
+
+void game_init()
+{
+// Initialize the PSXSDK library
+ PSX_Init();
+
+// Initialize graphics
+ GsInit();
+
+// Clear video memory
+ GsClearMem();
+
+// Set up drawing environment
+ game_drawenv.dither = 0;
+ game_drawenv.draw_on_display = 0;
+ game_drawenv.x = 0;
+ game_drawenv.y = 0;
+ game_drawenv.w = 320;
+ game_drawenv.h = 240;
+ game_drawenv.ignore_mask = 0;
+ game_drawenv.set_mask = 0;
+
+ GsSetDrawEnv(&game_drawenv);
+
+// Set up display environment
+ game_dispenv.x = 0;
+ game_dispenv.y = 256;
+
+ GsSetDispEnv(&game_dispenv);
+
+// Set drawing list
+ GsSetList(game_draw_list);
+
+// Initialize sound
+ SsInit();
+
+
+}
+
+void game_vblank_handler()
+{
+ speed_counter++;
+ seed_counter++;
+ screen_old = 1;
+}
+
+int pal_or_ntsc_selection()
+{
+ unsigned short padbuf;
+ int x;
+
+ game_drawenv.draw_on_display = 1;
+ x = game_drawenv.y;
+ game_drawenv.y = game_dispenv.y;
+ GsSetDrawEnv(&game_drawenv);
+
+ game_rect.x = 0;
+ game_rect.y = 0;
+ game_rect.w = 320;
+ game_rect.h = 240;
+ game_rect.r = 0;
+ game_rect.g = 0;
+ game_rect.b = 0;
+ game_rect.attribute = 0;
+
+ GsSortRectangle(&game_rect);
+
+ game_print("PAL/NTSC SELECTION", 128, 64);
+ game_print("X - PAL", 128, 80);
+ game_print("O - NTSC", 128, 88);
+
+ GsDrawList();
+
+ while(GsIsDrawing());
+
+ game_drawenv.draw_on_display = 0;
+ game_drawenv.y = x;
+ GsSetDrawEnv(&game_drawenv);
+
+ while(1)
+ {
+ PSX_ReadPad(&padbuf, NULL);
+
+ if((padbuf & PAD_CROSS) && !cross_pressed)
+ return VMODE_PAL; // PAL
+
+ if((padbuf & PAD_CIRCLE) && !circle_pressed)
+ {
+ //printf("circle_pressed = %d\n", circle_pressed);
+ return VMODE_NTSC; // NTSC
+ }
+
+
+ if(!(padbuf & PAD_CROSS))
+ cross_pressed = 0;
+
+ if(!(padbuf & PAD_CIRCLE))
+ circle_pressed = 0;
+ }
+}
+
+void new_apple()
+{
+ int a, b;
+
+ do
+ {
+ while((a = rand()%40) < 1);
+ srand(seed_counter);
+ while((b = rand()%29) < 2);
+ srand(seed_counter);
+ }while(snake_array[b][a]);
+
+ snake_array[b][a] = 0x80;
+}
+
+int main()
+{
+ int c;
+
+// Initialize
+ game_init();
+
+ printf("PSXsnake\n");
+ printf("(c) 2009 Giuseppe Gatta\n");
+ printf("Made with PSXSDK\n\n");
+
+ load_file_into_buffer("cdrom:FONT.TIM;1");
+ GsImageFromTim(&game_image, file_buffer);
+
+ GsUploadImage(&game_image);
+
+ game_clut[0] = 0x0;
+ game_clut[1] = 0x7fff;
+
+ LoadImage(game_clut, 640, 24, 16, 1);
+
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+
+ vmode = pal_or_ntsc_selection();
+
+ //printf("vmode = %d\n", vmode);
+
+ GsSetVideoMode(320, 240, vmode);
+
+
+ load_file_into_buffer("cdrom:BACKGRND.TIM;1");
+ GsImageFromTim(&game_image, file_buffer);
+
+ GsUploadImage(&game_image);
+
+ sample_pos[0] = SPU_DATA_BASE_ADDR;
+ c = load_file_into_buffer("cdrom:MUSIC.RAW;1");
+ SsUpload(file_buffer, c, sample_pos[0]);
+
+ if(c&0x7)
+ {
+ c|=0x7;
+ c++;
+ }
+
+ sample_pos[1] = c + sample_pos[0];
+ c = load_file_into_buffer("cdrom:BOMB.RAW;1");
+ SsUpload(file_buffer, c, sample_pos[1]);
+
+ /* ... */
+ if(c&0x7)
+ {
+ c|=0x7;
+ c++;
+ }
+
+ sample_pos[2] = c + sample_pos[1];
+ c = load_file_into_buffer("cdrom:APPLE.RAW;1");
+ SsUpload(file_buffer, c, sample_pos[2]);
+
+ SsVoiceStartAddr(0, sample_pos[0]);
+ SsVoiceStartAddr(1, sample_pos[1]);
+ SsVoiceStartAddr(2, sample_pos[2]);
+
+ SsVoiceVol(0, 0x3fff, 0x3fff);
+ SsVoiceVol(1, 0x3fff, 0x3fff);
+ SsVoiceVol(2, 0x3fff, 0x3fff);
+
+ SsVoicePitch(0, 0x1000 / (44100 / 11025));
+ SsVoicePitch(1, 0x1000 / (44100 / 11025));
+ SsVoicePitch(2, 0x1000);
+
+ game_setup();
+
+ SetVBlankHandler(game_vblank_handler);
+
+
+ while(1)
+ game_run();
+
+ return 0;
+}
+
+int check_snake_collision(int x, int y)
+{
+ if(snake_array[y][x] == 0x80)
+ return 2; // Snake ate an apple
+ else if(snake_array[y][x] == 0)
+ return 1; // Collided with nothing
+
+ return 0; // Die, because snake collided with itself
+}
+
+void game_run()
+{
+ int x, y, a, b, c;
+
+ unsigned short padbuf;
+
+ while(speed_counter > 0)
+ {
+ scc++;
+
+ if(vibration_cntdown > 0)
+ {
+ printf("vibration = %d\n", vibration_cntdown);
+ pad_enable_vibration(0);
+ pad_set_vibration(0, 0xFF, 0x80);
+ vibration_cntdown--;
+
+ if(vibration_cntdown == 0)
+ pad_set_vibration(0, 0, 0);
+ }
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if(scc == 5 && !game_over)
+ {
+ if(snake_dir <= SNAKE_DIR_RIGHT)
+ {
+ if(padbuf & PAD_UP)
+ snake_dir = SNAKE_DIR_UP;
+
+ if(padbuf & PAD_DOWN)
+ snake_dir = SNAKE_DIR_DOWN;
+ }
+ else
+ {
+ if(padbuf & PAD_LEFT)
+ snake_dir = SNAKE_DIR_LEFT;
+
+ if(padbuf & PAD_RIGHT)
+ snake_dir = SNAKE_DIR_RIGHT;
+ }
+
+ for(y = 0; y < 29; y++)
+ for(x = 0; x < 40; x++)
+ {
+ if(snake_array[y][x] == snake_size)
+ {
+ switch(snake_dir)
+ {
+ case SNAKE_DIR_LEFT:
+ b = y;
+ a = x-1;
+ break;
+ case SNAKE_DIR_RIGHT:
+ b = y;
+ a = x+1;
+ break;
+ case SNAKE_DIR_UP:
+ b = y-1;
+ a = x;
+ break;
+ case SNAKE_DIR_DOWN:
+ b = y+1;
+ a = x;
+ break;
+ }
+
+ c = check_snake_collision(a,b);
+
+ if(c)
+ {
+ snake_array[b][a] = snake_size+1;
+
+ if(c==2)
+ {
+ snake_size++;
+ score+=100;
+ //printf("%d\n", score);
+ SsKeyOn(2);
+ new_apple();
+ }
+ }
+ else
+ {
+ vibration_cntdown = 10;
+ game_over = 1;
+ SsKeyOff(0);
+ SsKeyOn(1);
+ scc = 0;
+ }
+
+ if(snake_array[y][x] == 1 && c!=2)
+ snake_array[y][x] = 0;
+
+ goto out_of_collision_checking;
+ }
+ }
+out_of_collision_checking:
+ for(y = 0; y < 29; y++)
+ for(x = 0; x < 40; x++)
+ if(snake_array[y][x]&&snake_array[y][x]<0x80&&c!=2)
+ snake_array[y][x]--;
+
+ scc = 0;
+ }
+ else if(game_over)
+ {
+ scc++;
+
+ if(scc >= 510)
+ {
+ if((padbuf & PAD_CROSS) && !cross_pressed)
+ {
+ game_setup();
+ cross_pressed = 1;
+ }
+ else if((padbuf & PAD_CIRCLE) && !circle_pressed)
+ {
+ circle_pressed = 1;
+ GsSetVideoMode(320, 240, pal_or_ntsc_selection());
+ game_setup();
+ }
+
+ if(!(padbuf & PAD_CROSS))
+ cross_pressed = 0;
+
+ if(!(padbuf & PAD_CIRCLE))
+ circle_pressed = 0;
+ }
+ }
+
+ speed_counter--;
+ }
+
+
+ if(screen_old)
+ {
+ game_rect.x = 0;
+ game_rect.y = 0;
+ game_rect.w = 320;
+ game_rect.h = 240;
+ game_rect.r = 0;
+ game_rect.g = 0;
+ game_rect.b = 0;
+ game_rect.attribute = 0;
+
+ GsSortRectangle(&game_rect);
+
+ game_sprite.x = 0;
+ game_sprite.y = 0;
+ game_sprite.w = 256;
+ game_sprite.h = 240;
+ game_sprite.u = 0;
+ game_sprite.v = 0;
+ game_sprite.r = NORMAL_LUMINOSITY;
+ game_sprite.g = NORMAL_LUMINOSITY;
+ game_sprite.b = NORMAL_LUMINOSITY;
+ game_sprite.tpage = 5;
+ game_sprite.attribute = COLORMODE(COLORMODE_16BPP);
+
+ GsSortSimpleSprite(&game_sprite);
+
+ game_sprite.x += 256;
+ game_sprite.w = 64;
+ game_sprite.tpage = 9;
+
+ GsSortSimpleSprite(&game_sprite);
+
+ game_rect.w = 8;
+ game_rect.h = 8;
+ game_rect.attribute = ENABLE_TRANS | TRANS_MODE(0);
+
+ for(y = 0; y < 29; y++)
+ {
+ for(x = 0; x < 40; x++)
+ {
+ game_rect.x = x * 8;
+ game_rect.y = y * 8;
+
+ if(snake_array[y][x] >= 1 && snake_array[y][x] <= 0x7F)
+ {
+ game_rect.r = 0;
+ game_rect.g = 255;
+ game_rect.b = 0;
+ GsSortRectangle(&game_rect);
+ }
+ else if(snake_array[y][x] == 0x80)
+ {
+ game_rect.r = 255;
+ game_rect.g = 0;
+ game_rect.b = 255;
+ GsSortRectangle(&game_rect);
+ }
+ else if(snake_array[y][x] == 0x81)
+ {
+ game_rect.r = 0;
+ game_rect.g = 0;
+ game_rect.b = 128;
+ GsSortRectangle(&game_rect);
+ }
+ }
+ }
+
+ sprintf(string_buf, "SCORE: %d", score);
+ game_print(string_buf, 0, 232);
+
+ if(game_over)
+ {
+
+ game_rect.w = 320;
+ game_rect.h = 240;
+
+ game_rect.x = 0;
+ game_rect.y = 0;
+
+ if(scc<=255)x=scc;else x=255;
+
+ game_rect.r = x;
+ game_rect.g = x;
+ game_rect.b = x;
+ game_rect.attribute = ENABLE_TRANS|TRANS_MODE(2);
+
+ GsSortRectangle(&game_rect);
+
+ if(scc>=300)
+ {
+ game_center_print("GAME OVER!", 160, 120);
+ }
+
+ if(scc>=420)
+ game_center_print("WHAT DO YOU WANT TO DO NOW?", 160, 136);
+
+ if(scc>=450)
+ game_center_print("PRESS X TO RESTART THE GAME.",160, 152);
+
+ if(scc>=480)
+ game_center_print("PRESS O FOR PAL/NTSC SELECTION SCREEN.",160,168);
+
+ if(scc>=510)
+ game_center_print("MADE WITH PSXSDK BY GIUSEPPE GATTA, 2010", 160, 200);
+ }
+
+ GsDrawList();
+
+// While the graphic synthesizer (video card) is drawing
+// just sleep.
+ while(GsIsDrawing());
+
+// Swap drawing and display Y position, and swap list array
+// to use. In this way we achieve a double buffer.
+
+ if(game_dispenv.y == 0)
+ {
+ game_dispenv.y = 256;
+ game_drawenv.y = 0;
+ }
+ else
+ {
+ game_dispenv.y = 0;
+ game_drawenv.y = 256;
+ }
+
+ GsSetDispEnv(&game_dispenv);
+ GsSetDrawEnv(&game_drawenv);
+
+ screen_old = 0;
+ }
+}
+
+void game_run_gameover()
+{
+ printf("GAME OVER!\n");
+ while(1);
+}
+
+int load_file_into_buffer(char *fname)
+{
+ FILE *f;
+ int sz;
+
+ /*int fd;
+
+ fd = open(fname, O_RDONLY);
+
+ printf("%s (%d)\n", fname, get_file_size(fname));
+
+ read(fd, file_buffer, get_file_size(fname));
+
+ close(fd);*/
+ f = fopen(fname, "rb");
+
+ //f->pos = 0;
+ fseek(f, 0, SEEK_END);
+
+ sz = ftell(f);
+
+ fseek(f, 0, SEEK_SET);
+
+ printf("%s (%d)\n", fname, sz);
+
+ fread(file_buffer, sizeof(char), sz, f);
+
+ fclose(f);
+
+ return sz;
+}
+
+void game_print(char *string, int x, int y)
+{
+ GsSprite print_char;
+ char q;
+
+ print_char.x = x;
+ print_char.y = y;
+ print_char.w = 8;
+ print_char.h = 8;
+ print_char.r = NORMAL_LUMINOSITY;
+ print_char.g = NORMAL_LUMINOSITY;
+ print_char.b = NORMAL_LUMINOSITY;
+ print_char.cx = 640;
+ print_char.cy = 24;
+ print_char.tpage = 10;
+ print_char.attribute = COLORMODE(COLORMODE_4BPP);
+
+ while(*string)
+ {
+ if(*string >= 0x20 && *string <= 0x7F)
+ {
+ q = *string;
+ q -= 0x20;
+
+ print_char.u = (q&0x1f)<<3;
+ print_char.v = (q/0x20)<<3;
+ GsSortSimpleSprite(&print_char);
+ }
+ print_char.x+=8;
+ string++;
+ }
+}
+
+void game_center_print(char *string, int x, int y)
+{
+ int l = strlen(string);
+ l<<=2;
+
+ game_print(string, x - l, y);
+}
+
+
+
+
+
+void setup_snake_field()
+{
+ int x, y;
+
+ for(y=0;y<29;y++)
+ for(x=0;x<40;x++)
+ snake_array[y][x] = 0;
+
+ for(y=0;y<29;y++)
+ {
+ snake_array[y][0] = 0x81;
+ snake_array[y][39] = 0x81;
+ }
+
+ for(x=0;x<40;x++)
+ {
+ snake_array[0][x] = 0x81;
+ snake_array[28][x] = 0x81;
+ }
+
+ new_apple();
+
+ for(x=1;x<=snake_size;x++)
+ snake_array[1][x]=x;
+
+ snake_dir = SNAKE_DIR_RIGHT;
+}
+
+void game_setup()
+{
+ game_over = 0;
+ scc = 0;
+ snake_size = 3;
+ speed_counter = 0;
+ score = 0;
+ setup_snake_field();
+ SsKeyOn(0);
+}
+
diff --git a/examples/puzzle/Makefile b/examples/puzzle/Makefile
new file mode 100644
index 0000000..ee00787
--- /dev/null
+++ b/examples/puzzle/Makefile
@@ -0,0 +1,9 @@
+PROJNAME = puzzle
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+ mkdir -p cd_root
+ bmp2tim puzzle.bmp cd_root/puzzle.tim 16 -org=320,0
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/puzzle/puzzle.bmp b/examples/puzzle/puzzle.bmp
new file mode 100644
index 0000000..ed689c8
--- /dev/null
+++ b/examples/puzzle/puzzle.bmp
Binary files differ
diff --git a/examples/puzzle/puzzle.c b/examples/puzzle/puzzle.c
new file mode 100644
index 0000000..8f2786f
--- /dev/null
+++ b/examples/puzzle/puzzle.c
@@ -0,0 +1,216 @@
+#include <psx.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+unsigned char puzzle_pieces[7][7];
+unsigned char piece_taken[7 * 7];
+
+int puzzle_x = 0, puzzle_y = 0;
+
+volatile int display_is_old = 1;
+
+void prog_vblank_handler()
+{
+ display_is_old=1;
+}
+
+GsImage puzzle_image;
+unsigned int prim_list[0x4000];
+unsigned char data_buffer[0x40000]; // 256 kilobytes
+
+int main()
+{
+ int x, y, ex, ey;
+ int wasleft=0;
+ int wasright=0;
+ int wasup=0;
+ int wasdown=0;
+ int wasexh=0;
+ int wasexv=0;
+ int dbuf=0;
+ unsigned char b;
+ unsigned short padbuf;
+ FILE *f;
+ GsSprite tile_sprite;
+ GsRectangle tile_rectangle;
+
+ PSX_Init();
+ GsInit();
+ GsClearMem();
+ GsSetList(prim_list);
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+
+ f = fopen("cdrom:\\PUZZLE.TIM;1", "rb");
+ fseek(f, 0, SEEK_END);
+ x = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fread(data_buffer, sizeof(char), x, f);
+ fclose(f);
+
+ GsImageFromTim(&puzzle_image, data_buffer);
+
+ GsUploadImage(&puzzle_image);
+ while(GsIsDrawing());
+
+ GsLoadFont(768, 0, 768, 256);
+ while(GsIsDrawing());
+
+ tile_sprite.tpage = 5;
+ tile_sprite.w = 32;
+ tile_sprite.h = 32;
+ tile_sprite.attribute = COLORMODE(COLORMODE_16BPP);
+ tile_sprite.r = tile_sprite.g = tile_sprite.b = NORMAL_LUMINOSITY;
+
+ SetVBlankHandler(prog_vblank_handler);
+
+ for(x=0;x<sizeof(piece_taken);x++)
+ piece_taken[x] = 0;
+
+ for(y = 0; y < 7; y++)
+ {
+ for(x = 0; x < 7; x++)
+ {
+ do
+ {
+ puzzle_pieces[y][x] = (rand() % 49) + 1;
+ }while(piece_taken[puzzle_pieces[y][x]] == 1);
+
+ piece_taken[puzzle_pieces[y][x]] = 1;
+ }
+ }
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ GsSetDispEnvSimple(0, dbuf?256:0);
+ GsSetDrawEnvSimple(0, dbuf?0:256, 320, 240);
+ dbuf=!dbuf;
+
+ PSX_ReadPad(&padbuf, NULL);
+
+ if((padbuf & PAD_LEFT) && !wasleft)
+ {
+ if(puzzle_x>0)
+ puzzle_x--;
+
+ wasleft=1;
+ }
+
+ if((padbuf & PAD_RIGHT) && !wasright)
+ {
+ if(puzzle_x<6)
+ puzzle_x++;
+
+ wasright=1;
+ }
+
+ if((padbuf & PAD_UP) && !wasup)
+ {
+ if(puzzle_y>0)
+ puzzle_y--;
+
+ wasup=1;
+ }
+
+ if((padbuf & PAD_DOWN) && !wasdown)
+ {
+ if(puzzle_y<6)
+ puzzle_y++;
+
+ wasdown=1;
+ }
+
+ if(!(padbuf & PAD_LEFT))
+ wasleft=0;
+
+ if(!(padbuf & PAD_RIGHT))
+ wasright=0;
+
+ if(!(padbuf & PAD_UP))
+ wasup=0;
+
+ if(!(padbuf & PAD_DOWN))
+ wasdown=0;
+
+ if((padbuf & PAD_CROSS) && !wasexh) // x <--> y
+ {
+ ey = puzzle_y;
+ if(puzzle_x < 6)
+ ex = puzzle_x+1;
+ else
+ ex = puzzle_x-1;
+
+ b = puzzle_pieces[puzzle_y][puzzle_x];
+ puzzle_pieces[puzzle_y][puzzle_x] =
+ puzzle_pieces[ey][ex];
+ puzzle_pieces[ey][ex] = b;
+
+ wasexh=1;
+ }
+
+ if((padbuf & PAD_CIRCLE) && !wasexv)
+ {
+ ex = puzzle_x;
+ if(puzzle_y < 6)
+ ey = puzzle_y+1;
+ else
+ ey = puzzle_y-1;
+
+ b = puzzle_pieces[puzzle_y][puzzle_x];
+ puzzle_pieces[puzzle_y][puzzle_x] =
+ puzzle_pieces[ey][ex];
+ puzzle_pieces[ey][ex] = b;
+
+ wasexv=1;
+ }
+
+ if(!(padbuf & PAD_CROSS))
+ wasexh=0;
+
+ if(!(padbuf & PAD_CIRCLE))
+ wasexv=0;
+
+ GsSortCls(0, 0, 255);
+
+ for(y = 0; y < 7; y++)
+ {
+ for(x = 0; x < 7; x++)
+ {
+ tile_sprite.x = x * 32;
+ tile_sprite.y = y * 32;
+ tile_sprite.u = ((puzzle_pieces[y][x]-1) % 7) * 32;
+ tile_sprite.v = ((puzzle_pieces[y][x]-1) / 7) * 32;
+ GsSortSimpleSprite(&tile_sprite);
+
+ if(x == puzzle_x && y == puzzle_y)
+ {
+ tile_rectangle.x = x * 32;
+ tile_rectangle.y = y * 32;
+ tile_rectangle.r = 255;
+ tile_rectangle.g = 0;
+ tile_rectangle.b = 255;
+ tile_rectangle.w = 32;
+ tile_rectangle.h = 32;
+ tile_rectangle.attribute = ENABLE_TRANS;
+ GsSortRectangle(&tile_rectangle);
+ }
+ }
+ }
+
+ GsPrintFont(232, 16, "Resolve\nPuzzle\n\n"
+ "Exchange:\n"
+ "\nX\nHorizontal\n"
+ "\nO\nVertical");
+
+ GsDrawList();
+ while(GsIsDrawing());
+
+ display_is_old = 0;
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/rottest/Makefile b/examples/rottest/Makefile
new file mode 100644
index 0000000..be7c0c0
--- /dev/null
+++ b/examples/rottest/Makefile
@@ -0,0 +1,9 @@
+PROJNAME = rottest
+
+include ../project.mk
+
+$(PROJNAME)_extra:
+ mkdir -p cd_root
+ bmp2tim image.bmp cd_root/image.tim 16 -org=320,0
+
+$(PROJNAME)_clean_extra:
diff --git a/examples/rottest/image.bmp b/examples/rottest/image.bmp
new file mode 100644
index 0000000..e08b3af
--- /dev/null
+++ b/examples/rottest/image.bmp
Binary files differ
diff --git a/examples/rottest/rottest.c b/examples/rottest/rottest.c
new file mode 100644
index 0000000..4d4f341
--- /dev/null
+++ b/examples/rottest/rottest.c
@@ -0,0 +1,104 @@
+#include <psx.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+unsigned int prim_list[0x4000];
+unsigned char filebuf[0x40000];
+
+volatile int display_is_old = 0;
+
+void prog_vblank_handler()
+{
+ display_is_old = 1;
+}
+
+int main()
+{
+ FILE *f;
+ GsSprite my_sprite;
+ GsImage my_image;
+ int y = 0;
+ int ro = 0;
+ int sc_x = SCALE_ONE;
+ int sc_y = SCALE_ONE;
+ unsigned short padbuf;
+
+ PSX_Init();
+
+ GsInit();
+ GsSetList(prim_list);
+ GsSetVideoMode(320, 240, EXAMPLES_VMODE);
+
+ GsClearMem();
+ GsSetDrawEnvSimple(0, 256, 320, 240);
+ GsSetDispEnvSimple(0, 0);
+
+ f = fopen("cdrom:\\IMAGE.TIM;1", "rb");
+ fseek(f, 0, SEEK_END);
+ y = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ fread(filebuf, sizeof(char), y, f);
+ fclose(f);
+
+ GsImageFromTim(&my_image, filebuf);
+ GsSpriteFromImage(&my_sprite, &my_image, 1);
+
+ SetVBlankHandler(prog_vblank_handler);
+
+ GsLoadFont(768, 0, 768, 128);
+ while(GsIsWorking());
+
+ while(1)
+ {
+ if(display_is_old)
+ {
+ PSX_ReadPad(&padbuf, NULL);
+ if(padbuf & PAD_LEFT)
+ {
+ ro--;
+ if(ro < 0) ro = 359;
+ }
+
+ if(padbuf & PAD_RIGHT)
+ {
+ ro++;
+ if(ro>=360) ro = 0;
+ }
+
+ if(padbuf & PAD_UP)
+ sc_x+=128;
+
+ if(padbuf & PAD_DOWN)
+ sc_x-=128;
+
+ if(padbuf & PAD_CROSS)
+ sc_y-=128;
+
+ if(padbuf & PAD_CIRCLE)
+ sc_y+=128;
+
+ GsSortCls(0, 0, 0);
+
+ my_sprite.x = 100;
+ my_sprite.y = 100;
+ my_sprite.r = my_sprite.g = my_sprite.b = NORMAL_LUMINOSITY;
+ my_sprite.rotate = ROTATE_ONE*ro;
+ my_sprite.scalex = sc_x;
+ my_sprite.scaley = sc_y;
+ my_sprite.mx = (my_sprite.w/2) * (my_sprite.scalex / 4096);
+ my_sprite.my = (my_sprite.h/2) * (my_sprite.scaley / 4096);
+ GsSortSprite(&my_sprite);
+
+ GsPrintFont(0, 0, "ro=%d\nsc_x=%d\nsc_y=%d", ro, sc_x, sc_y);
+
+ GsDrawList();
+ while(GsIsWorking());
+
+ y=!y;
+ GsSetDrawEnvSimple(0, y?0:256, 320, 240);
+ GsSetDispEnvSimple(0, y?256:0);
+ display_is_old = 0;
+ }
+ }
+}