Compare commits

...

13 Commits
v0.11 ... main

Author SHA1 Message Date
b9ee80886c Update Win MIPS toolchain instructions 2021-11-24 15:49:16 +01:00
0d9dfc992b Dead link 2021-11-21 11:11:40 +01:00
d92934338c Add FXs example 2021-11-18 18:50:43 +01:00
6d12150ef5 Fix Makefile 2021-11-15 19:14:28 +01:00
d1751b897e Update submodule, Makefile 2021-11-15 18:53:28 +01:00
ea757627b8 Add missing Drawsync() 2021-11-14 12:52:51 +01:00
b6d1c1b2f7 Fix typos, colors, add redux notice 2021-11-14 12:02:35 +01:00
1cf7b7efa6 Fix url 2021-11-13 22:53:48 +01:00
3b7b0dcf37 Change archive action 2021-11-13 22:52:50 +01:00
87176920d7 Get rid of libds 2021-11-13 20:09:31 +01:00
3e2a21ff90 Typo 2021-11-13 19:58:03 +01:00
2dfc68d6ca Add SPU readback example 2021-11-13 19:56:58 +01:00
6e168368f6 Fix str offset + spu init 2021-11-12 16:54:24 +01:00
19 changed files with 1249 additions and 46 deletions

View File

@ -1,8 +1,8 @@
name: Linux archive build
on:
release:
types: [edited, released, published]
push:
branches: [ main ]
jobs:
build:
@ -12,7 +12,7 @@ jobs:
steps:
- name: Get repo archive
run: |
wget https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.1/nolibgs_hello_worlds.zip
wget https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.11/nolibgs_hello_worlds.zip
7z x nolibgs_hello_worlds.zip -o${{github.workspace}}
- name: Install mipsel toolchain
run: |

View File

@ -50,6 +50,8 @@ hello_sio:
$(MAKE) -C hello_sio
hello_sprt:
$(MAKE) -C hello_sprt
hello_spu_readback:
$(MAKE) -C hello_spu_readback
hello_str:
$(MAKE) -C hello_str
hello_strplay:
@ -73,7 +75,10 @@ clean:
$(MAKE) -C hello_poly_fun clean
$(MAKE) -C hello_gte_opti clean
$(MAKE) -C hello_light clean
$(MAKE) -C hello_mod clean
$(MAKE) -C hello_multi_vag clean
$(MAKE) -C hello_multi_xa clean
$(MAKE) -C hello_ovl_exec cleansub
$(MAKE) -C hello_pad clean
$(MAKE) -C hello_poly clean
$(MAKE) -C hello_poly_ft clean
@ -81,13 +86,13 @@ clean:
$(MAKE) -C hello_poly_gt clean
$(MAKE) -C hello_poly_gt_tw clean
$(MAKE) -C hello_poly_inline clean
$(MAKE) -C hello_rsd clean
$(MAKE) -C hello_sio clean
$(MAKE) -C hello_sprt clean
$(MAKE) -C hello_spu_readback cleansub
$(MAKE) -C hello_tile clean
$(MAKE) -C hello_vag clean
$(MAKE) -C hello_world clean
$(MAKE) -C hello_cdda clean cleansub
$(MAKE) -C hello_cdda cleansub
$(MAKE) -C hello_cd cleansub
$(MAKE) -C hello_xa cleansub
$(MAKE) -C hello_bs cleansub
@ -102,7 +107,10 @@ all:
$(MAKE) -C hello_poly_fun
$(MAKE) -C hello_gte_opti
$(MAKE) -C hello_light
$(MAKE) -C hello_mod
$(MAKE) -C hello_multi_vag
$(MAKE) -C hello_multi_xa
$(MAKE) -C hello_ovl_exec
$(MAKE) -C hello_pad
$(MAKE) -C hello_poly
$(MAKE) -C hello_poly_ft
@ -111,6 +119,7 @@ all:
$(MAKE) -C hello_poly_inline
$(MAKE) -C hello_sio
$(MAKE) -C hello_sprt
$(MAKE) -C hello_spu_readback
$(MAKE) -C hello_tile
$(MAKE) -C hello_vag
$(MAKE) -C hello_world
@ -122,5 +131,5 @@ all:
$(MAKE) -C hello_strplay all
# declare phony rules
.PHONY: hello_2pads hello_bs hello_cd hello_cdda hello_cd_exec hello_cube hello_cubetex hello_cubetex_stp hello_font hello_fx hello_gte_opti hello_light hello_mod hello_multi_vag hello_multi_xa hello_ovl_exec hello_pad hello_poly hello_poly_ft hello_poly_fun hello_poly_gt hello_poly_gt_tw hello_poly_inline hello_poly_stp hello_sio hello_sprt hello_str hello_strplay hello_tile hello_vag hello_world hello_xa hello_xa_streaming \
.PHONY: hello_2pads hello_bs hello_cd hello_cdda hello_cd_exec hello_cube hello_cubetex hello_cubetex_stp hello_font hello_fx hello_gte_opti hello_light hello_mod hello_multi_vag hello_multi_xa hello_ovl_exec hello_pad hello_poly hello_poly_ft hello_poly_fun hello_poly_gt hello_poly_gt_tw hello_poly_inline hello_poly_stp hello_sio hello_sprt hello_spu_readback hello_str hello_strplay hello_tile hello_vag hello_world hello_xa hello_xa_streaming \
clean all

View File

@ -28,15 +28,21 @@ We'll keep things simple for now. If you want to read about more methods to get
### Windows
#### MIPS toolchain setup
1. Download the MIPS toolchain here : [https://static.grumpycoder.net/pixel/mips/g++-mipsel-none-elf-10.3.0.zip](http://static.grumpycoder.net/pixel/mips/g++-mipsel-none-elf-10.3.0.zip)
2. Extract the archive's content in `C:\g++-mipsel-none-elf-10.3.0` and add the `C:\g++-mipsel-none-elf-10.3.0\bin` folder to [your $PATH](https://stackoverflow.com/questions/44272416/how-to-add-a-folder-to-path-environment-variable-in-windows-10-with-screensho#44272417).
3. Test everything is fine by [launching a command prompt](https://www.lifewire.com/how-to-open-command-prompt-2618089) and typing `mipsel-none-elf-gcc.exe --version`. If you get a message like `mipsel-none-gnu-gcc (GCC) 10.3.0`, then it's working !
You can setup a pre-built MIPS toolchain by copy-pasting the following into a command prompt:
```
powershell -c "& { iwr https://raw.githubusercontent.com/grumpycoders/pcsx-redux/main/mips.ps1 | iex }"
```
Then, open a new command prompt, and type the following:
```
mips install 11.2.0
```
#### Nugget + PsyQ setup
1. Download the PsyQ converted libraries here : [http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z](http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z)
2. Clone the 'nolibgs_hello_worlds' repo with
`git clone https://github.com/ABelliqueux/nolibgs_hello_worlds.git --recursive`
or download this [repository's release](https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.1/nolibgs_hello_worlds.zip) and extract **`nolibgs_hello_worlds.zip`**'s content to `C:\no_libgs_hello_worlds\` .
or download this [repository's release](https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.11/nolibgs_hello_worlds.zip) and extract **`nolibgs_hello_worlds.zip`**'s content to `C:\no_libgs_hello_worlds\` .
3. Extract the content of `psyq-4.7-converted-full.7z` in `C:\no_libgs_hello_worlds\psyq`. You should now have `C:\no_libgs_hello_worlds\psyq\include` and `C:\no_libgs_hello_worlds\psyq\lib` ;
```
no_libgs_hello_worlds

View File

@ -2,11 +2,12 @@
// Schnappy 07-2021
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <libgte.h>
#include <libetc.h>
#include <libgpu.h>
// CD library
#include <libds.h>
#include <libcd.h>
// SPU library
#include <libspu.h>
@ -55,10 +56,15 @@ void display(void)
db = !db; // flip db value (0 or 1)
}
int main(void)
{
init(); // init() display
{
int count = 0;
int flip = 1;
CdlLOC loc[100];
int ntoc;
// Init display
init();
// Init extended CD system
DsInit();
CdInit();
// Init Spu
SpuInit();
// Set master & CD volume to max
@ -75,35 +81,47 @@ int main(void)
SpuSetCommonAttr(&spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
// CD Playback setup
// Play second audio track
// Get CD TOC
while ((ntoc = CdGetToc(loc)) == 0) { /* Read TOC */
FntPrint("No TOC found: please use CD-DA disc...\n");
}
// Prevent out of bound pos
for (int i = 1; i < ntoc; i++) {
CdIntToPos(CdPosToInt(&loc[i]) - 74, &loc[i]);
}
// Those array will hold the return values of the CD commands
u_char param[4], result[8];
// Set CD parameters ; Report Mode ON, CD-DA ON. See LibeOver47.pdf, p.188
param[0] = CdlModeRept|CdlModeDA;
CdControlB (CdlSetmode, param, 0); /* set mode */
VSync (3); /* wait three vsync times */
// Play second track in toc array
CdControlB (CdlPlay, (u_char *)&loc[3], 0); /* play */
int count = 0;
int flip = 0;
while (1) // infinite loop
{
if ( count == 0){
DsPlay(2, tracks, 0);
}
{
count ++;
// Afer 5 seconds
if ( count == 300 ){
// Stop playback
DsPlay(0, tracks, 0);
// Get current track number ~ every second
// See LibeOver47.pdf, p.188
if (count%50 == 0){
CdReady(1, &result[0]);
// current track number can also be obtained with
// CdControlB (CdlGetlocP, 0, &result[0]);
}
// Wait one second
if ( count == 360 ){
// Flip value
flip = !flip;
// Switch track (can be 2 or 3)
tracks[0] = 2 + flip;
// Reset counter
count = 0;
// Switch track after ~ 20 seconds
if (count%(50*20) == 0){
// Flip can have a value of 1 or -1
flip *= -1;
uint8_t nextTrackIndex = result[1] + flip;
// Send CD command to switch track
CdControlB (CdlPlay, (u_char *)&loc[ nextTrackIndex ], 0);
}
FntPrint("Hello CDDA !\n"); // Send string to print stream
FntPrint("Playback status: %d", DsPlay(3, tracks, 0)); // Send string to print stream
FntPrint("Playback status: %d", result[1]); // Send string to print stream
FntFlush(-1); // Draw printe stream
display(); // Execute display()

8
hello_fx/Makefile Normal file
View File

@ -0,0 +1,8 @@
TARGET = hello_fx
SRCS = hello_fx.c \
TIM/cube.tim \
TIM/sky.tim \
TIM/bg.tim \
include ../common.mk

BIN
hello_fx/TIM/bg.tim Normal file

Binary file not shown.

BIN
hello_fx/TIM/cube.tim Normal file

Binary file not shown.

BIN
hello_fx/TIM/sky.tim Normal file

Binary file not shown.

158
hello_fx/cubetex.c Normal file
View File

@ -0,0 +1,158 @@
SVECTOR modelCube_mesh[] = {
{48,48,-48.0},
{48,-48,-48},
{-48,-48,-48},
{-48,48,-48},
{48,48,48},
{48,-48,48},
{-48,-48,48},
{-48,48,48}
};
SVECTOR modelCube_normal[] = {
0,-0,-1,0,
0,0,1,0,
1,0,-2,0,
-9,-1,-3,0,
-1,2,-1,0,
3,1,2,0,
0,0,-1,0,
0,-0,1,0,
1,-6,3,0,
-5,-1,9,0,
-1,2,-1,0,
2,1,2,0
};
SVECTOR modelCube_uv[] = {
84,84, 0, 0,
125,42, 0, 0,
84,42, 0, 0,
125,84, 0, 0,
84,125, 0, 0,
125,125, 0, 0,
1,84, 0, 0,
42,125, 0, 0,
42,84, 0, 0,
42,125, 0, 0,
84,84, 0, 0,
42,84, 0, 0,
42,1, 0, 0,
1,42, 0, 0,
42,42, 0, 0,
42,84, 0, 0,
1,42, 0, 0,
1,84, 0, 0,
84,84, 0, 0,
125,84, 0, 0,
125,42, 0, 0,
125,84, 0, 0,
84,84, 0, 0,
84,125, 0, 0,
1,84, 0, 0,
1,125, 0, 0,
42,125, 0, 0,
42,125, 0, 0,
84,125, 0, 0,
84,84, 0, 0,
42,1, 0, 0,
1,1, 0, 0,
1,42, 0, 0,
42,84, 0, 0,
42,42, 0, 0,
1,42, 0, 0
};
CVECTOR modelCube_color[] = {
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
modelCube_uv,
modelCube_color,
12
};
typedef struct RGB_PIX {
u_int R:5, G:5, B:5, STP:1;
} RGB_PIX;
// Some structures to handle TIM files
typedef struct PIXEL {
u_long bnum;
u_short DX, DY;
u_short W, H;
RGB_PIX data[];
} PIXEL;
typedef struct CLUT {
u_long bnum;
u_short DX, DY;
u_short W, H;
u_short clut[];
} CLUT;
typedef struct TIM_FILE_CLUT{
u_long ID;
u_long flag;
u_long clut;
PIXEL pixel[];
} TIM_FILE_CLUT;
typedef struct TIM_FILE{
u_long ID;
u_long flag;
PIXEL pixel[];
} TIM_FILE;

377
hello_fx/hello_fx.c Normal file
View File

@ -0,0 +1,377 @@
// Controls :
// SELECT : Switch semi-transparency on/off on primitives
// START : Cycle semi-transparency rates on/off on primitives
// LEFT/RIGHT: Move forward cube
// X : Reset Cube position
// Schnappy 11-2021
#include <sys/types.h>
#include <libgte.h>
#include <libgpu.h>
#include <libetc.h>
#include <libapi.h>
#include <inline_n.h>
#include <gtemac.h>
// Sample vector model
#include "cubetex.c"
#define VMODE 0
// Number of primitives to draw
#define NUM_PRIM 2
// Number of textures to load
#define NUM_TEX 3
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 16 // margins for text display
#define MARGINY 16
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// Store TIM files in an array so we can iterate over them - see 'cubetex.c' for TIM_FILE struct and declaration
TIM_FILE * timFiles[3];
TIM_IMAGE timImages[3];
// Get included tim files address
extern TIM_FILE _binary_TIM_cube_tim_start;
extern TIM_FILE _binary_TIM_sky_tim_start;
extern TIM_FILE _binary_TIM_bg_tim_start;
// Light
CVECTOR BGc = {130, 200, 255, 0};
// Back color
VECTOR BKc = {128, 128, 128, 0};
// Light rotation angle
SVECTOR lgtang = {0, 0, 0};
// These will be used to store the light rotation matrix, cube rotation matrix, and composite light matrix.
MATRIX rotlgt, rotcube, light;
// Local Light Matrix : Direction and reach of each light source.
MATRIX lgtmat = {
// X Y Z
0, -ONE, 0, // Lightsource 1 : here, the light source is at the Bottom-Left of the screen, and points into the screen.
0, 0, 0, // Lightsource 2
0, 0, 0, // Lightsource 3
};
// Local Color Matrix
MATRIX cmat = {
// L1 L2 L3
4096, 0, 0, // R
4096, 0, 0, // G
4096, 0, 0 // B
};
// Prototypes
void init(void);
void display(void);
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
// Set far color
SetFarColor( BGc.r, BGc.g, BGc.b );
// Set Ambient color
SetBackColor( BKc.vx, BKc.vy, BKc.vz );
// Set Color matrix
SetColorMatrix(&cmat);
// Set Fog settings
SetFogNearFar( 128, 1024, CENTERX );
setRGB0(&draw[0], 0, 0, 0);
setRGB0(&draw[1], 0, 0, 0);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, SCREENXRES - MARGINY * 2, 0, 512 );
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM((u_long*)tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
int main() {
// Populate array with pointers to TIM data
timFiles[0] = &_binary_TIM_cube_tim_start;
timFiles[1] = &_binary_TIM_sky_tim_start;
timFiles[2] = &_binary_TIM_bg_tim_start;
// Pad values
int pad, oldPad;
// Set semi-transparency on (1) and off (0)
int stpFlag = 1;
// Set primitive semi-transparency rate - See LibOver47.pdf, p.107
int stpRate = 0;
// If set, rotate cube
int rotateCube = 1;
int offsetCube = 0;
// Array of pointers to a POLY_G4 we iterate over
POLY_GT3 * poly[NUM_PRIM];
// Rotation vector
SVECTOR rotVector={ 384, 0, 128, 0 };
// Translation vector
VECTOR transVector= { 0, 0, 256, 0};
// BG sprt
POLY_FT4 * bg;
// Normalized UV coordinates for the X axis
long normH = ((255 << 12) / SCREENXRES);
// Init Disp/Drawenv, Font, etc.
init();
// Load textures to VRAM
for (char tex = 0; tex < NUM_TEX; tex++){
LoadTexture(timFiles[tex], &timImages[tex]);
}
// Main loop
while (1) {
// Work matrix
MATRIX Work= {0} ;
// Triangle counters array - one for each cube
long curTriangle[3] = {0,0,0};
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Draw BG
bg = (POLY_FT4 * )nextpri;
SetPolyFT4(bg);
bg->tpage = getTPage( timImages[2].mode&0x3, 0,
timImages[2].prect->x,
timImages[2].prect->y
);
if ( (timImages[2].mode & 0x3) < 2 ) {
setClut( bg,
timImages[2].crect->x,
timImages[2].crect->y
);
}
setRGB0(bg, 127,127,127);
setUV4(bg, 0, 0,
SCREENYRES, 0,
0, SCREENYRES,
SCREENYRES, SCREENYRES
);
setXY4(bg, 0 , 0,
SCREENXRES, 0,
0 , SCREENYRES,
SCREENXRES, SCREENYRES);
addPrim(ot[db][OTLEN-1], bg);
nextpri += sizeof(POLY_FT4);
// Rotate cube
if(rotateCube) rotVector.vy += 10;
// Find and apply light rotation matrix
// Find rotmat from light angles
RotMatrix_gte(&lgtang, &rotlgt);
// Find rotmat from cube angles
RotMatrix_gte(&rotVector, &rotcube);
// RotMatrix cube * RotMatrix light
MulMatrix0(&rotcube, &rotlgt, &rotlgt);
// Light Matrix * RotMatrix light
MulMatrix0(&lgtmat, &rotlgt, &light);
// Set new light matrix
SetLightMatrix(&light);
// Apply Transl, Rot, then matrix
RotMatrix(&rotVector, &Work);
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
long p, OTz, Flag;
// Draw NUM_PRIM primitives
for (int i = 0; i < (modelCube.len * 3); i += 3) {
// Set projection matrices
transVector.vx = 0;
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
// Cast nextpri as POLY_GT3
poly[0] = (POLY_GT3 *)nextpri;
poly[1] = (POLY_GT3 *)nextpri+sizeof(POLY_GT3);
// Initialize the primitives
SetPolyGT3(poly[0]);
SetPolyGT3(poly[1]);
// Reflection Cube
// This cube has its UVs mapped directly to VRAM coordinates
// We're using the framebuffers as a texture (0,0 and 0,256)
// Get 256x256 texture page that's at x0, y0
poly[1]->tpage = getTPage( 2, stpRate,
0,
!(db) << 8 // Here, we're using db's value that can be either 0 or 1 to determine the texture page Y coordinate.
);
// Set STP
SetSemiTrans(poly[1], stpFlag);
// Map coordinates from drawarea (320x240) to texture size (128x128) in fixed point math
// x = x * (256 / 320) => ( x * ( 128 * 4096 ) / 320 ) / 4096
// y = y * (240 / 240) => ( y * ( 240 * 4096 ) / 240 ) / 4096 => y * 2184 >> 12 -> y
setUV3( poly[1],
(poly[1]->x0 * normH) >> 12,
poly[1]->y0 - (!(db) << 4) , // We're using db's value again to add a 16 pixels offset to the Y's coordinates of the UVs
(poly[1]->x1 * normH) >> 12,
poly[1]->y1 - (!(db) << 4), // We have to do that because the buffer is 240 high, whereas our texture page is 256, hence 256 - 240 == 16
(poly[1]->x2 * normH) >> 12,
poly[1]->y2 - (!(db) << 4)
);
// Draw "container" cube
// This cube has a texture with transparent areas.
// STP bit is set on PNG's alpha channel : img2tim -usealpha -org 320 0 -o cube.tim cube.png
poly[0]->tpage = getTPage( timImages[0].mode&0x3, stpRate,
timImages[0].prect->x,
timImages[0].prect->y
);
// If 8/4bpp, load CLUT to vram
if ( (timImages[0].mode & 0x3) < 2 ) {
setClut( poly[0],
timImages[0].crect->x,
timImages[0].crect->y
);
}
// Set UV coordinates
setUV3(poly[0], modelCube.u[i].vx, modelCube.u[i].vy,
modelCube.u[i+2].vx, modelCube.u[i+2].vy,
modelCube.u[i+1].vx, modelCube.u[i+1].vy
);
// Rotate, translate, and project the vectors and output the results into a primitive
// curTriangle, +1, +2 point to the vertices index of the triangle we're drawing.
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[1]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[1]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[1]->x2, &p, &Flag);
// Here we're only messing with the matrices so that the foreground cube can be moved independantly from the backgound one.
// In real code, you don't want to do the same calculation twice !
transVector.vx = offsetCube;
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[0]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[0]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[0]->x2, &p, &Flag);
// The right way to do it is re-using the results from the first RotTransPer() batch
// i.e commenting lines 273 to 280 and uncommenting lines 284 to 289
//~ poly[0]->x0 = poly[1]->x0;
//~ poly[0]->y0 = poly[1]->y0;
//~ poly[0]->x1 = poly[1]->x1;
//~ poly[0]->y1 = poly[1]->y1;
//~ poly[0]->x2 = poly[1]->x2;
//~ poly[0]->y2 = poly[1]->y2;
// Average OTz value for 3 vertices
// OTz is 1/4 of screen to vertex length
OTz /= 3;
// Work color vectors
// This is the hue of the transparent cube
CVECTOR prismCol = {0xff,0xff,0x0,0x0};
// This will store the result of the depth cueing.
CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 };
// Find local color from three normal vectors and perform depth cueing.
gte_NormalColorDpq3( &modelCube.n[i+0],
&modelCube.n[i+2],
&modelCube.n[i+3],
&prismCol, p, &outCol, &outCol1, &outCol2);
// Set vertex colors on transparent/background cube
setRGB0(poly[1], outCol.r, outCol.g , outCol.b);
setRGB1(poly[1], outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly[1], outCol2.r, outCol2.g, outCol2.b);
// Non-transparent/foreground cube color
// Find local color from three normal vectors and perform depth cueing.
gte_NormalColorDpq( &modelCube.n[i+0], &modelCube.c[i+0], p, &outCol);
gte_NormalColorDpq( &modelCube.n[i+2], &modelCube.c[i+2], p, &outCol2);
gte_NormalColorDpq( &modelCube.n[i+1], &modelCube.c[i+1], p, &outCol1);
// Set vertex colors
setRGB0(poly[0], outCol.r, outCol.g , outCol.b);
setRGB1(poly[0], outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly[0], outCol2.r, outCol2.g, outCol2.b);
// If OTz is in range (not too close)
if ((OTz > 0) && (OTz < OTLEN))
// Add to ordering table, at index OTz-2
AddPrim(&ot[ db ][ OTz-2 ], poly[0]);
AddPrim(&ot[ db ][ OTz-2 ], poly[1]);
// Increment next primitive address
nextpri += sizeof(POLY_GT3)*2;
// Increment to next triangle
curTriangle[0] += 3;
curTriangle[1] += 3;
}
// Get pad input
pad = PadRead(0);
// If select button is used
if ( pad & PADselect && !(oldPad & PADselect) ){
// Flip STP flag
stpFlag = !stpFlag;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if ( !(pad & PADselect) && oldPad & PADselect) {
oldPad = pad;
}
// If start button is used
if ( pad & PADstart && !( oldPad & PADstart ) ){
// Switch STP rates
stpRate > 2 ? stpRate = 0 : stpRate++;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADstart) && oldPad & PADstart) {
oldPad = pad;
}
if ( pad & PADRdown && !( oldPad & PADRdown ) ){
// Switch STP rates
offsetCube = 0;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADRdown) && oldPad & PADRdown) {
oldPad = pad;
}
if ( pad & PADLright && !( oldPad & PADLright ) ){
offsetCube += 6;
}
if ( pad & PADLleft && !( oldPad & PADLleft ) ){
offsetCube -= 6;
}
FntPrint("Hello fx !\n");
FntPrint("Select: STP on/off\nStart: Cycle STP rates\nLeft/Right: Move FG cube.\nX: Reset cube pos\n");
FntPrint("STP : %d\n", stpFlag);
FntPrint("STP rate : %d\n", stpRate);
FntFlush(-1);
display();
}
return 0;
}

View File

@ -10,9 +10,6 @@ cleansub:
$(MAKE) -C hello_ovl_world clean
$(MAKE) clean
rm -f hello_ovl_exec.cue hello_ovl_exec.bin
rm -f *.mcd *.frag *.lua *.vert
rm -f */*.mcd */*.frag */*.lua */*.vert */*.elf
#~ rm -f */*.mcd */*.frag */*.lua */*.vert */*.elf */*.map */*.ps-exe */*.o */*.dep
TARGET = hello_ovl_exec

View File

@ -2,6 +2,6 @@ This example shows how to load overlays from the CD and execute them.
Many thanks to @nicolasnobe for his help on getting this example working.
More doc is coming soon at
See the wiki for more details on overlays in the context of PSX development :
[https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/OVL](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/OVL)

View File

@ -0,0 +1,12 @@
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_spu_readback.cue hello_spu_readback.bin
TARGET = hello_spu_readback
SRCS = hello_spu_readback.c \
include ../common.mk

View File

@ -0,0 +1,63 @@
This example is adapted from PsyQ's sample : `psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako`.
It was edited to fix typos, have a hopefully better code organization with hopefully more usefull variable names.
What it does is demonstrate how to transfer data from the PSX 's SPU to main memory in order to analyze / process the audio signal and dostuff accordingly.
In this instance, it's used to determine the coordinates of a few primitives to display a [VU-meter](https://en.wikipedia.org/wiki/VU_meter).
This technique is known to be used in certain games for lipsynching or audio visualization ( Crash team racing, Hercules, Vib Ribbon ...).
## pcsx-redux : no animation
Pcsx-redux does not yet support these specific SPU buffers nor triggerring IRQ from them so as of 11-2021 this example doesn't work in this particular emulator.
If looking for an alternative, check [duckstation](https://www.duckstation.org/) out.
## PsyQ's SpuReadDecodedData() doc errata
The main function for transferring data from the SPU to the RAM is `SpuReadDecodedData()`, and is documented in **LibRef47.pdf, p1054**.
The table on this page (Table 15-2) contains erroneous data and should read :
![Spu addresses range](https://wiki.arthus.net/assets/spureaddecodeddata_errata.png)
The correct address ranges for the SPU buffer is :
| Map (bytes) | Data contents |
|-------------|---------------|
| 0x000 - 0x3ff | CD Left channel |
| 0x400 - 0x7ff | CD Right channel |
| 0x800 - 0xbff | Voice 1 |
| 0xC00 - 0xfff | Voice 3 |
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory
## More on CDDA
See the [hello_cdda](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_cdda) example in this repo.
## Docs and links
Original psyq example : `psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako`
## Music credits
Track 1 :
Beach Party by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3429-beach-party
License: https://filmmusic.io/standard-license
Track 2:
Funk Game Loop by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3787-funk-game-loop
License: https://filmmusic.io/standard-license

View File

@ -0,0 +1,436 @@
// SPU readback example
// adapted from PsyQ's sample : psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako
// Schnappy 11-2021
#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <kernel.h>
#include <libgte.h>
#include <libetc.h>
#include <libgpu.h>
// CD library
#include <libcd.h>
// SPU library
#include <libspu.h>
#include "../thirdparty/nugget/common/syscalls/syscalls.h"
#define printf ramsyscall_printf
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
// Number of bars
#define BARNUM 2
// Peak cursor width
#define TSIZE 10
// Bar size / 2
#define BSIZE 128
// Top Y coordinate of Left volume bar
#define BARTOP 100
// Bottom Y coordinates of Left volume bar
#define BARBOTTOM ((BARTOP)+5)
// Vertical spacing
#define MARGIN 40
// Bars left coordinates
#define MINBAR CENTERX - BSIZE
// Bars right coordinates
#define MAXBAR ( CENTERX + BSIZE + TSIZE )
// Bars IDs
#define LEFTBAR 0
#define RIGHTBAR 1
// Return absolute value of a number
#define ABS(x) (((x)<0)?(-(x)):(x))
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
uint8_t primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
uint8_t *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
uint8_t db = 0; // index of which buffer is used, values 0, 1
// SPU attributes
SpuCommonAttr spuSettings;
// SPU IRQ address
uint16_t SpuIrqAddr;
// SPU decoded data buffer
SpuDecodedData decodedData;
// CD volume: current sample's max values
ulong leftMax, rightMax;
// Last 2 seconds's peak volume values
ulong leftPeak, rightPeak;
// Primitives for drawing the VU-metre, double buffered
// Blue : background bar
POLY_F4 * bar[BARNUM];
// White : current value
POLY_F4 * current[BARNUM];
// Red : volume peak in the last 3 seconds
POLY_F4 * peak[BARNUM];
// Colors for the VU-metre
CVECTOR bg = {20, 10, 0};
CVECTOR fg = {10,200,20};
CVECTOR cursor = {200,40,10};
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
// Top bar
FntOpen (MINBAR, BARTOP - 10, 200, 150, 0, 64);
// Bottom bar
FntOpen (MINBAR, BARTOP - 10 + (MARGIN), 200, 150, 0, 64);
// Debug
FntOpen (32, SCREENYRES - 74, SCREENXRES - 64, 64, 0, 200);
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void initPrimitives(void)
{
// Set primitives from primbuff[]
bar[0] = (POLY_F4 *)nextpri;
bar[1] = (POLY_F4 *)nextpri + sizeof(POLY_F4);
current[0] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 2);
current[1] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 3);
peak[0] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 4);
peak[1] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 5);
// Set each primitive to their default settings
for (int i = 0; i < BARNUM; i++)
{
// Volume bar background is blue
SetPolyF4 ( bar[i] );
setRGB0 ( bar[i], bg.r,bg.g,bg.b );
setXY4 ( bar[i],
// Top-left
MINBAR, BARTOP + i * MARGIN,
// Top-right
MAXBAR, BARTOP + i * MARGIN,
// Bottom-left
MINBAR, BARBOTTOM + i * MARGIN,
// Bottom-right
MAXBAR, BARBOTTOM + i * MARGIN);
// Current volume is light purple-ish
SetPolyF4 (current[i]);
setRGB0 ( current[i], fg.r,fg.g,fg.b);
setXY4 ( current[i],
MINBAR, BARTOP + i * MARGIN,
MINBAR + TSIZE, BARTOP + i * MARGIN,
MINBAR, BARBOTTOM + i * MARGIN,
MINBAR + TSIZE, BARBOTTOM + i * MARGIN);
// Initialize peak cursor
SetPolyF4 ( peak[i] );
setRGB0 ( peak[i], cursor.r,cursor.g,cursor.b);
setXY4 ( peak[i],
MINBAR, BARTOP + i * MARGIN,
MINBAR + TSIZE, BARTOP + i * MARGIN,
MINBAR, BARBOTTOM + i * MARGIN,
MINBAR + TSIZE, BARBOTTOM + i * MARGIN);
}
}
// Unused - should be called whenever this madness needs to be ended
void terminate(void)
{
// Turn SPU irq off
SpuSetIRQ (SPU_OFF);
// Clear callback functions
SpuSetIRQCallback ((SpuIRQCallbackProc) NULL);
SpuSetTransferCallback ((SpuTransferCallbackProc) NULL);
// Reset SPU settings
spuSettings.mask = (SPU_COMMON_MVOLL |
SPU_COMMON_MVOLR |
SPU_COMMON_CDVOLL |
SPU_COMMON_CDVOLR |
SPU_COMMON_CDMIX
);
spuSettings.mvol.left = 0;
spuSettings.mvol.right = 0;
spuSettings.cd.volume.left = 0;
spuSettings.cd.volume.right = 0;
spuSettings.cd.mix = SPU_OFF;
SpuSetCommonAttr (&spuSettings);
// Stop CD
CdStop ();
// Stop SPU processing
SpuQuit ();
// Re-init display env
ResetGraph (3);
// stop callback processing
StopCallback ();
}
// Print corresponding data for each volume bar
void printDataInfo(void)
{
// We're using 2 streams
FntPrint (0, "L: %04x peak/%04x\n", leftMax, leftPeak);
FntPrint (1, "R: %04x peak/%04x\n", rightMax, rightPeak);
FntFlush (0);
FntFlush (1);
}
// SPU IRQ calback function
void eachIRQ (void)
{
SpuSetIRQ (SPU_OFF); /**/
SpuReadDecodeData (&decodedData, SPU_CDONLY); /**/
}
// DMA Transfer callback function
void eachDMA (void)
{
if (SpuIrqAddr == 0x0)
SpuIrqAddr = 0x200;
else
SpuIrqAddr = 0x0;
// Change IRQ address
SpuSetIRQAddr (SpuIrqAddr);
// Turn SPU IRQ requests on
SpuSetIRQ (SPU_ON);
}
void findSampleMaxVolume(void)
{
// Search maximum volume value of the SPU decoded data
// SPU buffer data range adresses
long dataLowerAdress, dataUpperAdress;
// Current sample's max and working value
short maxL = 0, tmpL;
short maxR = 0, tmpR;
// Timers for the Peak cursor, reset after 120 iterations.
static long timeCursorL = 0, timeCursorR = 0;
// Find SPU data range according to current half we're working on
if (SpuIrqAddr == 0x0) {
/* 1st part is available */
dataLowerAdress = 0x0;
dataUpperAdress = 0x1ff;
} else {
/* 2nd part is available */
dataLowerAdress = 0x200;
dataUpperAdress = 0x3ff;
}
// Examine and find max volume in the data range
for (long i = dataLowerAdress; i < dataUpperAdress; i ++) {
// Examine SPU decoded data
tmpL = ABS(decodedData.cd_left[i]);
tmpR = ABS(decodedData.cd_right[i]);
// Only keep maximum value for this sample
if (maxL < tmpL ) {
maxL = tmpL ;
}
if (maxR < tmpR ) {
maxR = tmpR;
}
}
leftMax = (long) maxL;
rightMax = (long) maxR;
// Peak level
if (leftPeak < leftMax) {
leftPeak = leftMax;
timeCursorL = 0;
}
if (rightPeak < rightMax) {
rightPeak = rightMax;
timeCursorR = 0;
}
// Peak cursors: hold 2s@60fps.
// Increment counters until 120 is reached, then set cursors position to current leftMax/rightMax values
if (timeCursorL < 120) {
timeCursorL ++;
} else {
timeCursorL = 0;
leftPeak = leftMax;
}
if (timeCursorR < 120) {
timeCursorR ++;
} else {
timeCursorR = 0;
rightPeak = rightMax;
}
}
int main(void)
{
// Values used to switch CD track
u_int counter = 0;
int8_t flip = 1;
// These will hold the normalised values of leftMax/rightMax, leftPeak/rightPeak
long lMax, rMax, lPeak, rPeak;
// Init display
init();
// Init CD system
CdInit ();
// Init Spu
SpuInit();
// Initialize SPU related variables
leftMax = rightMax = 0;
leftPeak = rightPeak = 0;
// Fill SPU data buffers with 0s
for (int i = 0; i < SPU_DECODEDDATA_SIZE; i ++) {
decodedData.cd_left[i] = 0;
decodedData.cd_right[i] = 0;
}
// SPU setup
// Set master & CD volume to max
spuSettings.mask = (SPU_COMMON_MVOLL |
SPU_COMMON_MVOLR |
SPU_COMMON_CDVOLL |
SPU_COMMON_CDVOLR |
SPU_COMMON_CDMIX);
// Master volume should be in range 0x0000 - 0x3fff
spuSettings.mvol.left = 0x3fff;
spuSettings.mvol.right = 0x3fff;
// Cd volume should be in range 0x0000 - 0x7fff
spuSettings.cd.volume.left = 0x7fff;
spuSettings.cd.volume.right = 0x7fff;
// Enable CD input ON
spuSettings.cd.mix = SPU_ON;
// Apply settings
SpuSetCommonAttr(&spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
// Callbacks setup
// Set Transfer callback
(void) SpuSetTransferCallback ((SpuTransferCallbackProc) eachDMA);
// set IRQ callback
SpuSetIRQCallback ((SpuIRQCallbackProc) eachIRQ);
// Initialize SPU IRQ address
SpuIrqAddr = 0x200;
// Set IRQ address
SpuSetIRQAddr (SpuIrqAddr);
// Turn interrupt request ON
SpuSetIRQ(SPU_ON);
// CD Playback setup
// Play second audio track
// Get CD TOC
CdlLOC loc[100];
int ntoc;
while ((ntoc = CdGetToc(loc)) == 0) { /* Read TOC */
printf("No TOC found: please use CD-DA disc...\n");
FntPrint(2, "No TOC found: please use CD-DA disc...\n");
}
// Prevent out of bound pos
for (int i = 1; i < ntoc; i++) {
CdIntToPos(CdPosToInt(&loc[i]) - 74, &loc[i]);
}
// Those array will hold the return values of the CD commands
u_char param[4], result[8];
// Set CD parameters ; Report Mode ON, CD-DA ON. See LibeOver47.pdf, p.188
param[0] = CdlModeRept|CdlModeDA;
// Set CD mode
CdControlB (CdlSetmode, param, 0);
// Wait 3 vsync
VSync (3);
// Play second track in toc array
CdControlB (CdlPlay, (u_char *)&loc[3], 0);
// Graphics setup
initPrimitives();
while (1)
{
counter++;
ClearOTagR(ot[db], OTLEN);
// Normalize volume
lMax = (leftMax * 256) / 0x8000 + MINBAR;
rMax = (rightMax * 256) / 0x8000 + MINBAR;
lPeak = (leftPeak * 256) / 0x8000 + MINBAR;
rPeak = (rightPeak * 256) / 0x8000 + MINBAR;
// Update primitives XY coordinates
// Set coordinates for volume bar polygons
setXY4 ( current[LEFTBAR],
MINBAR, BARTOP,
lMax + TSIZE, BARTOP,
MINBAR, BARBOTTOM,
lMax + TSIZE, BARBOTTOM);
setXY4 (current[RIGHTBAR],
MINBAR, BARTOP + MARGIN,
rMax + TSIZE, BARTOP + MARGIN,
MINBAR, BARBOTTOM + MARGIN,
rMax + TSIZE, BARBOTTOM + MARGIN);
// Set coordinates for peak cursor polygons
setXY4 (peak[LEFTBAR],
lPeak, BARTOP,
lPeak + TSIZE, BARTOP,
lPeak, BARBOTTOM,
lPeak + TSIZE, BARBOTTOM);
setXY4 (peak[RIGHTBAR],
rPeak, BARTOP + MARGIN,
rPeak + TSIZE, BARTOP + MARGIN,
rPeak, BARBOTTOM + MARGIN,
rPeak + TSIZE, BARBOTTOM + MARGIN);
// Add prims to ordering table from bottom to top
for ( int i = 0; i < BARNUM; i++) {
addPrim(ot[db][OTLEN - 1], bar[i]);
addPrim(ot[db][OTLEN - 2], current[i]);
addPrim(ot[db][OTLEN - 3], peak[i]);
}
// Get current track number ~ every second
// See LibeOver47.pdf, p.188
if (counter%50 == 0){
CdReady(1, &result[0]);
// current track number can also be obtained with
// CdControlB (CdlGetlocP, 0, &result[0]);
}
// Switch track after ~ 20 seconds
if (counter%(50*20) == 0){
// Flip can have a value of 1 or -1
flip *= -1;
uint8_t nextTrackIndex = result[1] + flip;
// Send CD command to switch track
CdControlB (CdlPlay, (u_char *)&loc[ nextTrackIndex ], 0);
}
// Update current and peak values
findSampleMaxVolume();
// Print bar's infos
printDataInfo();
// Draw debug stream
FntPrint(2, "Hello SPU readback ! %d\n", counter);
FntPrint(2, "Current track: %d\n", result[1] );
FntPrint(2, "L: %08d, R: %08d\n", leftMax, rightMax);
FntPrint(2, "SPU Addr: 0x%03x ", SpuIrqAddr );
FntFlush(2);
// Update display
display();
}
return 0;
}

View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- MKPSXISO example XML script -->
<!-- <iso_project>
Starts an ISO image project to build. Multiple <iso_project> elements may be
specified within the same xml script which useful for multi-disc projects.
<iso_project> elements must contain at least one <track> element.
Attributes:
image_name - File name of the ISO image file to generate.
cue_sheet - Optional, file name of the cue sheet for the image file
(required if more than one track is specified).
-->
<iso_project image_name="hello_spu_readback.bin" cue_sheet="hello_spu_readback.cue">
<!-- <track>
Specifies a track to the ISO project. This example element creates a data
track for storing data files and CD-XA/STR streams.
Only one data track is allowed and data tracks must only be specified as the
first track in the ISO image and cannot be specified after an audio track.
Attributes:
type - Track type (either data or audio).
source - For audio tracks only, specifies the file name of a wav audio
file to use for the audio track.
-->
<track type="data">
<!-- <identifiers>
Optional, Specifies the identifier strings to use for the data track.
Attributes:
system - Optional, specifies the system identifier (PLAYSTATION if unspecified).
application - Optional, specifies the application identifier (PLAYSTATION if unspecified).
volume - Optional, specifies the volume identifier.
volume_set - Optional, specifies the volume set identifier.
publisher - Optional, specifies the publisher identifier.
data_preparer - Optional, specifies the data preparer identifier. If unspecified, MKPSXISO
will fill it with lengthy text telling that the image file was generated
using MKPSXISO.
-->
<identifiers
system ="PLAYSTATION"
application ="PLAYSTATION"
volume ="HELOCD"
volume_set ="HELOCD"
publisher ="SCHNAPPY"
data_preparer ="MKPSXISO"
/>
<!-- <license>
Optional, specifies the license file to use, the format of the license file must be in
raw 2336 byte sector format, like the ones included with the PsyQ SDK in psyq\cdgen\LCNSFILE.
License data is not included within the MKPSXISO program to avoid possible legal problems
in the open source environment... Better be safe than sorry.
Attributes:
file - Specifies the license file to inject into the ISO image.
-->
<!--
<license file="LICENSEA.DAT"/>
-->
<!-- <directory_tree>
Specifies and contains the directory structure for the data track.
Attributes:
None.
-->
<directory_tree>
<!-- <file>
Specifies a file in the directory tree.
Attributes:
name - File name to use in the directory tree (can be used for renaming).
type - Optional, type of file (data for regular files and is the default, xa for
XA audio and str for MDEC video).
source - File name of the source file.
-->
<!-- Stores system.txt as system.cnf -->
<file name="system.cnf" type="data" source="system.cnf"/>
<file name="SCES_313.37" type="data" source="hello_spu_readback.ps-exe"/>
<dummy sectors="1024"/>
<!-- <dir>
Specifies a directory in the directory tree. <file> and <dir> elements inside the element
will be inside the specified directory.
-->
</directory_tree>
</track>
<!--
Track 1 :
Beach Party by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3429-beach-party
License: https://filmmusic.io/standard-license
Track 2:
Funk Game Loop by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3787-funk-game-loop
License: https://filmmusic.io/standard-license
-->
<track type="audio" source="../hello_cdda/audio/beach.wav"/>
<track type="audio" source="../hello_cdda/audio/funk.wav"/>
</iso_project>

View File

@ -0,0 +1,4 @@
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0

View File

@ -8,13 +8,14 @@
#include <libgte.h>
#include <libetc.h>
#include <libgpu.h>
#include <libspu.h>
// CD library
#include <libcd.h>
// CODEC library
#include <libpress.h>
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define TRUECOL 1 // 0 : 16bpp, 1: 24bpp
#define TRUECOL 0 // 0 : 16bpp, 1: 24bpp
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
@ -112,6 +113,7 @@ int main() {
u_long * curVLCptr = VlcBuff[db];
// Init Disp/Draw env, Font, etc.
init();
SpuInit();
// Init CDrom system
CdInit();
// Reset the MDEC
@ -150,8 +152,8 @@ int main() {
// While end of str is not reached, play it
while (!endPlayback) {
// Use this area to draw the slices
RECT curSlice = { 0,
(db * StrFileY) + STR_POS_Y,
RECT curSlice = { STR_POS_X,
(db * StrFileY) + STR_POS_Y,
// In 24bpp, use 24 pixels wide slices
16 * PPW ,
StrFileY};
@ -163,13 +165,15 @@ int main() {
// Begin decoding RLE-encoded MDEC image data
DecDCTin( curVLCptr , DCT_MODE);
// Prepare to receive the decoded image data from the MDEC
while (curSlice.x < STR_POS_X + SCREENXRES * PPW) {
// Receive decoded data : a 16*ppw*240 px slice
while (curSlice.x < STR_POS_X + StrFileX * PPW) {
// Receive decoded data : a 16*ppw*240 px slice in long word (4B), so / 2
DecDCTout( (u_long *) curIMGptr, curSlice.w * curSlice.h / 2);
// Wait for transfer end
DecDCToutSync(1);
DecDCToutSync(0);
// Transfer data from main memory to VRAM
LoadImage(&curSlice, (u_long *) curIMGptr );
// Wait for drawing termination
DrawSync(0);
// Increment drawArea's X with slice width (16 or 24 pix)
curSlice.x += 16 * PPW;
}

2
thirdparty/nugget vendored

@ -1 +1 @@
Subproject commit 8b5e3761f826818319f41800e55319fc9654d965
Subproject commit 4fa64028ee0b99c73cab9d7aee125f4d792b53c8