Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
b9ee80886c | |||
0d9dfc992b | |||
d92934338c | |||
6d12150ef5 | |||
d1751b897e | |||
ea757627b8 | |||
b6d1c1b2f7 | |||
1cf7b7efa6 | |||
3b7b0dcf37 | |||
87176920d7 | |||
3e2a21ff90 | |||
2dfc68d6ca | |||
6e168368f6 |
6
.github/workflows/Linux-build-archive.yml
vendored
6
.github/workflows/Linux-build-archive.yml
vendored
@ -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: |
|
||||
|
15
Makefile
15
Makefile
@ -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
|
||||
|
14
README.md
14
README.md
@ -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
|
||||
|
@ -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
8
hello_fx/Makefile
Normal 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
BIN
hello_fx/TIM/bg.tim
Normal file
Binary file not shown.
BIN
hello_fx/TIM/cube.tim
Normal file
BIN
hello_fx/TIM/cube.tim
Normal file
Binary file not shown.
BIN
hello_fx/TIM/sky.tim
Normal file
BIN
hello_fx/TIM/sky.tim
Normal file
Binary file not shown.
158
hello_fx/cubetex.c
Normal file
158
hello_fx/cubetex.c
Normal 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
377
hello_fx/hello_fx.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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)
|
12
hello_spu_readback/Makefile
Normal file
12
hello_spu_readback/Makefile
Normal 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
|
63
hello_spu_readback/README.md
Normal file
63
hello_spu_readback/README.md
Normal 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
|
436
hello_spu_readback/hello_spu_readback.c
Normal file
436
hello_spu_readback/hello_spu_readback.c
Normal 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;
|
||||
}
|
111
hello_spu_readback/isoconfig.xml
Normal file
111
hello_spu_readback/isoconfig.xml
Normal 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>
|
4
hello_spu_readback/system.cnf
Normal file
4
hello_spu_readback/system.cnf
Normal file
@ -0,0 +1,4 @@
|
||||
BOOT=cdrom:\SCES_313.37;1
|
||||
TCB=4
|
||||
EVENT=10
|
||||
STACK=801FFFF0
|
@ -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
2
thirdparty/nugget
vendored
@ -1 +1 @@
|
||||
Subproject commit 8b5e3761f826818319f41800e55319fc9654d965
|
||||
Subproject commit 4fa64028ee0b99c73cab9d7aee125f4d792b53c8
|
Loading…
Reference in New Issue
Block a user