Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
ABelliqueux | f7c89f7094 | |
ABelliqueux | 55a09d5c38 | |
ABelliqueux | e0e23bee34 | |
ABelliqueux | 59c50e4356 | |
ABelliqueux | 2371e8fa32 | |
ABelliqueux | 362bdb244b | |
ABelliqueux | 7bf316c9f9 | |
ABelliqueux | 1af3a1bf87 | |
ABelliqueux | 179a080b3d | |
ABelliqueux | 505c08a549 | |
ABelliqueux | a865750dca | |
ABelliqueux | da195bbba5 |
1
Makefile
1
Makefile
|
@ -2,6 +2,7 @@ TARGET = ovl-upload
|
|||
TYPE = ps-exe
|
||||
|
||||
SRCS = ovl-upload.c \
|
||||
pcdrv.c \
|
||||
TIM/cubetex.tim \
|
||||
../common/crt0/crt0.s \
|
||||
tritex.c \
|
||||
|
|
BIN
Overlay.ovl0
BIN
Overlay.ovl0
Binary file not shown.
BIN
Overlay.ovl1
BIN
Overlay.ovl1
Binary file not shown.
Binary file not shown.
309
ovl-upload.c
309
ovl-upload.c
|
@ -1,26 +1,14 @@
|
|||
|
||||
/* ovl-upload.c, by ABelliqueux, 04-2021 license GNU General Public License v3.0
|
||||
|
||||
This example code demonstrates how to use the companion 'ovl-upload.py' script that should be provided with this file.
|
||||
|
||||
Once the code is loaded on a unirom enabled PSX via a serial/USB cable, 'ovl-upload.py' listens for a specific command
|
||||
|
||||
to load an overlay file on demand.
|
||||
|
||||
For an explanation about overlays, see http://psx.arthus.net/sdk/Psy-Q/DOCS/TRAINING/FALL96/overlay.pdf
|
||||
|
||||
For a basic example see @JaberwockySeamonstah's https://github.com/JaberwockySeamonstah/PSXOverlayExample
|
||||
|
||||
Unirom can be found here : https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1
|
||||
|
||||
with it's companion pc side software : https://github.com/JonathanDotCel/NOTPSXSerial
|
||||
|
||||
Thanks to @JaberwockySeamonstah, @JonathanDotCel, @nicolasnoble, @Lameguy64 for their help and patience.
|
||||
|
||||
Demonstrates:
|
||||
|
||||
* Using overlays to store different data and loading them in memory as needed.
|
||||
|
||||
Controls:
|
||||
Select - Load alternative overlay
|
||||
*/
|
||||
|
@ -30,408 +18,275 @@
|
|||
#include <libgpu.h>
|
||||
#include <libetc.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libsio.h>
|
||||
#include <string.h>
|
||||
#include "pcdrv.h"
|
||||
// If USECD is defined, files will be loaded from the CD. Use this method for testing in an emulator.
|
||||
// Additionaly, generate the bin/cue with mkpsxiso.
|
||||
|
||||
// Additionaly, generate the bin/cue with mkpsxiso :
|
||||
// $ mkpsxiso -y config/OverlayExample.xml
|
||||
//~ #define USECD
|
||||
|
||||
#ifdef USECD
|
||||
|
||||
#include <libcd.h>
|
||||
|
||||
#endif
|
||||
|
||||
// Sample vector models
|
||||
|
||||
#include "tritex.h"
|
||||
|
||||
#include "cubetex.h"
|
||||
|
||||
#define VMODE 0
|
||||
|
||||
#define SCREENXRES 320
|
||||
|
||||
#define SCREENYRES 240
|
||||
|
||||
#define CENTERX SCREENXRES/2
|
||||
|
||||
#define CENTERY SCREENYRES/2
|
||||
|
||||
#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] = {0}; // Primitive list // That's our prim buffer
|
||||
|
||||
//~ int primcnt=0; // Primitive counter
|
||||
|
||||
char * nextpri = primbuff[0]; // Primitive counter
|
||||
|
||||
short db = 0; // Current buffer counter
|
||||
|
||||
// Texture image
|
||||
|
||||
extern unsigned long _binary_TIM_cubetex_tim_start[];
|
||||
|
||||
extern unsigned long _binary_TIM_cubetex_tim_end[];
|
||||
|
||||
extern unsigned long _binary_TIM_cubetex_tim_length;
|
||||
|
||||
TIM_IMAGE tim_cube;
|
||||
|
||||
// OVERLAYS CONFIG
|
||||
|
||||
// These symbols name are defined in 'overlay.ld', l.8, l.24 and l.41
|
||||
// Use &load_all_overlays_here to get the memory adress where the overlay files are loaded.
|
||||
// Those adresses you can check in the generated .map file at compile time.
|
||||
|
||||
extern u_long load_all_overlays_here;
|
||||
|
||||
extern u_long __lvl0_end; // Use &__lvl0_end to get end address of corresponding overlay.
|
||||
|
||||
extern u_long __lvl1_end;
|
||||
|
||||
//~ u_long overlaySize = 0;
|
||||
|
||||
static char* overlayFile; // Will hold the name of the file to load.
|
||||
|
||||
u_char overlayFileID, loadFileIDwas, loadFileID = 0; // Will hold an ID that's unique for each file.
|
||||
|
||||
char * overlayFile; // Will hold the name of the file to load.
|
||||
char * ptrToChar[8]; // Will hold the name of the file to load.
|
||||
u_char overlayFileID = 0, loadFileIDwas = 0;
|
||||
// Timer for the pad
|
||||
|
||||
u_short timer = 0;
|
||||
|
||||
// pcdrv protocol
|
||||
u_char escape = 0x00; // Hypothetical Escape char for unirom
|
||||
u_char protocol = 0x01; // Hypothetical ID number for the pcdrv protocol in unirom
|
||||
u_char command = LOAD; // We're loading the data here
|
||||
uint32_t checkSum = 0;
|
||||
volatile u_char inBuffer[BUFFER_LEN] = {0};
|
||||
volatile u_char dataBuffer[64] = "DEBOUTLESENFANTS";
|
||||
// Prototypes
|
||||
|
||||
void init(void);
|
||||
|
||||
void display(void);
|
||||
|
||||
void LoadTexture(u_long * 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;
|
||||
}
|
||||
|
||||
setRGB0(&draw[0], 0, 0, 255);
|
||||
setRGB0(&draw[1], 0, 0, 255);
|
||||
|
||||
draw[0].isbg = 1;
|
||||
draw[1].isbg = 1;
|
||||
|
||||
PutDispEnv(&disp[db]);
|
||||
PutDrawEnv(&draw[db]);
|
||||
|
||||
// Init font system
|
||||
FntLoad(960, 0);
|
||||
FntOpen(16, 16, 196, 64, 0, 256);
|
||||
|
||||
FntOpen(16, 16, 196, 196, 0, 512);
|
||||
}
|
||||
|
||||
void display(void){
|
||||
|
||||
DrawSync(0);
|
||||
VSync(0);
|
||||
|
||||
PutDispEnv(&disp[db]);
|
||||
PutDrawEnv(&draw[db]);
|
||||
|
||||
SetDispMask(1);
|
||||
|
||||
DrawOTag(ot[db] + OTLEN - 1);
|
||||
|
||||
db = !db;
|
||||
|
||||
nextpri = primbuff[db];
|
||||
|
||||
|
||||
}
|
||||
|
||||
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
|
||||
OpenTIM(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 fileDesc = 0;
|
||||
int fileRead = 0;
|
||||
int filePos = 0;
|
||||
int fileCreated = 0;
|
||||
int main() {
|
||||
|
||||
// Update this value to avoid trigger at launch
|
||||
|
||||
loadFileIDwas = overlayFileID = loadFileID;
|
||||
|
||||
if ( loadFileID == 0 ){
|
||||
|
||||
loadFileIDwas = overlayFileID;
|
||||
if ( overlayFileID == 0 ){
|
||||
overlayFile = "\\cube.bin;1";
|
||||
|
||||
} else if ( loadFileID == 1) {
|
||||
|
||||
} else if ( overlayFileID == 1) {
|
||||
overlayFile = "\\tri.bin;1";
|
||||
|
||||
}
|
||||
|
||||
// Load overlay from CD if definde
|
||||
|
||||
#ifdef USECD
|
||||
|
||||
CdInit();
|
||||
|
||||
int cdread = 0, cdsync = 1;
|
||||
|
||||
cdread = CdReadFile( (char *)(overlayFile), &load_all_overlays_here, 0);
|
||||
|
||||
cdsync = CdReadSync(0, 0);
|
||||
|
||||
#endif
|
||||
|
||||
int i;
|
||||
|
||||
int PadStatus;
|
||||
|
||||
int TPressed=0;
|
||||
|
||||
int AutoRotate=1;
|
||||
|
||||
long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143
|
||||
|
||||
MESH * model = &Tri;
|
||||
|
||||
POLY_GT3 *poly = {0}; // pointer to a POLY_GT3
|
||||
|
||||
SVECTOR Rotate={ 0 }; // Rotation coordinates
|
||||
VECTOR Trans={ 0, 0, CENTERX, 0 }; // Translation coordinates
|
||||
VECTOR Scale={ ONE, ONE, ONE, 0 }; // ONE == 4096
|
||||
MATRIX Matrix={0}; // Matrix data for the GTE
|
||||
|
||||
// Texture window
|
||||
|
||||
DR_MODE * dr_mode; // Pointer to dr_mode prim
|
||||
|
||||
RECT tws = {0, 0, 32, 32}; // Texture window coordinates : x, y, w, h
|
||||
|
||||
init();
|
||||
|
||||
LoadTexture(_binary_TIM_cubetex_tim_start, &tim_cube);
|
||||
|
||||
// Main loop
|
||||
while (1) {
|
||||
|
||||
// Overlay switch
|
||||
|
||||
if ( loadFileID != loadFileIDwas ){
|
||||
|
||||
// Update previous file value
|
||||
|
||||
loadFileIDwas = loadFileID;
|
||||
|
||||
// Change file to load
|
||||
|
||||
switch ( loadFileID ){
|
||||
|
||||
case 0:
|
||||
|
||||
overlayFile = "\\cube.bin;1";
|
||||
|
||||
overlayFileID = 0;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
overlayFile = "\\tri.bin;1";
|
||||
|
||||
overlayFileID = 1;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
overlayFile = "\\cube.bin;1";
|
||||
|
||||
overlayFileID = 0;
|
||||
|
||||
break;
|
||||
|
||||
FntPrint("%02d - %04d - %02d", fileDesc, filePos, fileRead );
|
||||
// If filedescriptor is not null and not -1, try to open the file
|
||||
if ( fileDesc > 0 ){
|
||||
switch (fileRead){
|
||||
case 0: PCwrite( fileDesc, filePos, 7, dataBuffer, inBuffer);
|
||||
fileRead = 1;
|
||||
break;
|
||||
case 1: PCseek( fileDesc, 7, 3, 1, inBuffer);
|
||||
fileRead = 2;
|
||||
break;
|
||||
case 2: PCwrite( fileDesc, 10, 7, dataBuffer + 7, inBuffer);
|
||||
fileRead = 3;
|
||||
break;
|
||||
default:
|
||||
//~ PCclose(fileDesc, inBuffer);
|
||||
PCinit(inBuffer);
|
||||
fileDesc = 0;
|
||||
fileRead = 0;
|
||||
}
|
||||
|
||||
}
|
||||
// Overlay switch
|
||||
if ( overlayFileID != loadFileIDwas ){
|
||||
// Change file to load
|
||||
switch ( overlayFileID ){
|
||||
case 0:
|
||||
overlayFile = "\\cube.bin;1";
|
||||
//~ overlayFileID = 0;
|
||||
break;
|
||||
case 1:
|
||||
overlayFile = "\\tri.bin;1";
|
||||
//~ overlayFileID = 1;
|
||||
break;
|
||||
default:
|
||||
overlayFile = "\\cube.bin;1";
|
||||
//~ overlayFileID = 0;
|
||||
break;
|
||||
}
|
||||
#ifndef USECD
|
||||
if(!fileRead){
|
||||
fileDesc = PCopen("HELLO.WD", O_RDWR, inBuffer);
|
||||
//~ fileDesc = PCcreate("HELLO.WD", O_RDWR, inBuffer);
|
||||
//~ returnVal = PCseek(1, 0, 369, 1, inBuffer);
|
||||
//~ returnVal = PCread(88, 369, 16, dataBuffer, inBuffer);
|
||||
//~ returnVal = PCwrite(88, 10, 5, dataBuffer, inBuffer);
|
||||
if ( fileDesc ){
|
||||
loadFileIDwas = overlayFileID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USECD
|
||||
|
||||
cdread = CdReadFile( (char *)(overlayFile), &load_all_overlays_here, 0);
|
||||
|
||||
CdReadSync(0, 0);
|
||||
|
||||
if ( CdReadSync(0, 0) == 0 ){
|
||||
loadFileIDwas = overlayFileID;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// Pad button timer
|
||||
|
||||
while ( timer > 0 ) {
|
||||
|
||||
timer --;
|
||||
|
||||
}
|
||||
|
||||
// Read pad status
|
||||
|
||||
PadStatus = PadRead(0);
|
||||
|
||||
// If select is pressed, change overlay
|
||||
|
||||
if (PadStatus & PADselect && !timer) {
|
||||
|
||||
// We send the memory address where the file should be loaded, the memory address of the loadFileID, so that the screen is updated when it changes, and the file id.
|
||||
|
||||
printf("load:%p:%08x:%d", &load_all_overlays_here, &loadFileID, overlayFileID);
|
||||
|
||||
#ifdef USECD
|
||||
|
||||
// We can do that because we only have two files
|
||||
|
||||
loadFileID = !loadFileID;
|
||||
|
||||
#endif
|
||||
|
||||
timer = 30;
|
||||
|
||||
if ( ( PadStatus & PADselect ) && !timer ) {
|
||||
overlayFileID = !overlayFileID;
|
||||
timer = 150;
|
||||
}
|
||||
|
||||
if (AutoRotate) {
|
||||
|
||||
Rotate.vy += 8; // Pan
|
||||
|
||||
Rotate.vx += 8; // Tilt
|
||||
}
|
||||
|
||||
|
||||
// Clear the current OT
|
||||
|
||||
ClearOTagR(ot[db], OTLEN);
|
||||
|
||||
// Convert and set the matrixes
|
||||
|
||||
RotMatrix(&Rotate, &Matrix);
|
||||
|
||||
TransMatrix(&Matrix, &Trans);
|
||||
|
||||
ScaleMatrix(&Matrix, &Scale);
|
||||
|
||||
SetRotMatrix(&Matrix);
|
||||
|
||||
SetTransMatrix(&Matrix);
|
||||
|
||||
|
||||
// Render the sample vector model
|
||||
t=0;
|
||||
|
||||
// modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
|
||||
|
||||
for (i = 0; i < (model->tmesh->len*3); i += 3) {
|
||||
|
||||
poly = (POLY_GT3 *)nextpri;
|
||||
|
||||
// Initialize the primitive and set its color values
|
||||
|
||||
SetPolyGT3(poly);
|
||||
|
||||
((POLY_GT3 *)poly)->tpage = getTPage(tim_cube.mode&0x3, 0,
|
||||
tim_cube.prect->x,
|
||||
tim_cube.prect->y
|
||||
);
|
||||
|
||||
setRGB0(poly, model->tmesh->c[i].r , model->tmesh->c[i].g , model->tmesh->c[i].b);
|
||||
setRGB1(poly, model->tmesh->c[i+1].r, model->tmesh->c[i+1].g, model->tmesh->c[i+1].b);
|
||||
setRGB2(poly, model->tmesh->c[i+2].r, model->tmesh->c[i+2].g, model->tmesh->c[i+2].b);
|
||||
|
||||
setUV3(poly, model->tmesh->u[i].vx , model->tmesh->u[i].vy,
|
||||
model->tmesh->u[i+1].vx, model->tmesh->u[i+1].vy,
|
||||
model->tmesh->u[i+2].vx, model->tmesh->u[i+2].vy);
|
||||
|
||||
// Rotate, translate, and project the vectors and output the results into a primitive
|
||||
|
||||
OTz = RotTransPers(&model->tmesh->v[model->index[t]] , (long*)&poly->x0, &p, &Flag);
|
||||
OTz += RotTransPers(&model->tmesh->v[model->index[t+1]], (long*)&poly->x1, &p, &Flag);
|
||||
OTz += RotTransPers(&model->tmesh->v[model->index[t+2]], (long*)&poly->x2, &p, &Flag);
|
||||
|
||||
// Sort the primitive into the OT
|
||||
OTz /= 3;
|
||||
if ((OTz > 0) && (OTz < OTLEN))
|
||||
AddPrim(&ot[db][OTz-2], poly);
|
||||
|
||||
nextpri += sizeof(POLY_GT3);
|
||||
|
||||
t+=3;
|
||||
|
||||
}
|
||||
|
||||
dr_mode = (DR_MODE *)nextpri;
|
||||
|
||||
setDrawMode(dr_mode,1,0, getTPage(tim_cube.mode&0x3, 0,
|
||||
tim_cube.prect->x,
|
||||
tim_cube.prect->y), &tws); //set texture window
|
||||
|
||||
AddPrim(&ot[db], dr_mode);
|
||||
|
||||
nextpri += sizeof(DR_MODE);
|
||||
|
||||
|
||||
FntPrint("Hello overlay!\n");
|
||||
|
||||
#ifndef USECD
|
||||
|
||||
FntPrint("Overlay with id %d loaded at 0x%08x", overlayFileID, &load_all_overlays_here);
|
||||
|
||||
#endif
|
||||
|
||||
FntPrint("loadFileIDwas : %d\n", loadFileIDwas);
|
||||
FntPrint("Overlay with id %d loaded at 0x%08x\n", overlayFileID, &load_all_overlays_here );
|
||||
FntPrint("buffer at %08x : %s\n", inBuffer, inBuffer);
|
||||
FntPrint("dataBuffer at %08x : \n %s", dataBuffer, dataBuffer);
|
||||
#endif
|
||||
#ifdef USECD
|
||||
|
||||
FntPrint("File: %s\n", overlayFile);
|
||||
|
||||
FntPrint("Bytes read: %d", cdread);
|
||||
|
||||
#endif
|
||||
|
||||
FntFlush(-1);
|
||||
|
||||
display();
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Binary file not shown.
789
ovl-upload.py
789
ovl-upload.py
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,293 @@
|
|||
#include "pcdrv.h"
|
||||
void wait(){
|
||||
for(u_int wait = 0; wait < 60; wait++){
|
||||
wait = wait;
|
||||
}
|
||||
};
|
||||
/*
|
||||
static char sio_read(){
|
||||
char c;
|
||||
c = getchar();
|
||||
return c;
|
||||
};
|
||||
u_int strLen( const char * str){
|
||||
u_int l = 0;
|
||||
while ( * str++ ){
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
};
|
||||
*/
|
||||
// Hashing algorythm from @nicolasnoble : https://github.com/grumpycoders/pcsx-redux/blob/main/src/mips/common/util/djbhash.h
|
||||
static inline uint32_t djbProcess(uint32_t hash, const char str[], unsigned n) {
|
||||
return n ? djbProcess ( ( ( hash << 5 ) + hash ) ^ str[0], str + 1, n - 1) : hash;
|
||||
}
|
||||
static inline uint32_t djbHash( const char* str, unsigned n ){
|
||||
return djbProcess( 5381, str, n);
|
||||
};
|
||||
/*
|
||||
uint32_t charsToU32 ( char * byte ) {
|
||||
unsigned int packet = (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | (byte[3]);
|
||||
return packet;
|
||||
};
|
||||
u_char U32ToChars( u_int memoryAddress, u_int byteNbr){
|
||||
// This one I found on my own )'u'(
|
||||
u_char byte = { 0 };
|
||||
byte = ( (u_int) memoryAddress >> ( 24 - (byteNbr << 3) ) ) & 0xFF;
|
||||
return byte;
|
||||
};
|
||||
void sendU32(uint32_t data) {
|
||||
putchar(data & 0xff);
|
||||
data >>= 8;
|
||||
putchar(data & 0xff);
|
||||
data >>= 8;
|
||||
putchar(data & 0xff);
|
||||
data >>= 8;
|
||||
putchar(data & 0xff);
|
||||
};
|
||||
void sendRU32(uint32_t data) {
|
||||
putchar((data >> 24) & 0xff);
|
||||
putchar((data >> 16) & 0xff);
|
||||
putchar((data >> 8) & 0xff);
|
||||
putchar(data & 0xff);
|
||||
};
|
||||
*/
|
||||
int waitForSIODone( const char * answer, volatile char * bufferAddress){
|
||||
// This watches the buffer at &bufferAddress and waits for it to contain the first two chars of answer,
|
||||
// which is sent by the server when PC->PSX data upload is over.
|
||||
// If answer is OK, act as ACK. If answer is DT, the next 2 chars contains data.
|
||||
// Returns 1 if ok, 0 else.
|
||||
// Return value
|
||||
int SIOdone = 0;
|
||||
// Error code sent by the PC
|
||||
const char * error = "-1";
|
||||
// Mini buffer for the data when two first chars are DT
|
||||
char returnValue[BUFFER_LEN] = {0};
|
||||
// Counter to avoid infinite loop
|
||||
int i = 0;
|
||||
while(1){
|
||||
// Continually get the content at &bufferAddress
|
||||
const char * buffer = ( const char * )bufferAddress;
|
||||
// Rolling buffer
|
||||
if( strlen( buffer ) > BUFFER_LEN){
|
||||
memmove( ( char * ) buffer, buffer + 1, strlen(buffer));
|
||||
}
|
||||
// Check inBuffer for answer
|
||||
// If buffer does not contain "-1"
|
||||
if ( buffer[0] != error[0] && buffer[1] != error[1] ){
|
||||
// If two first chars == answer
|
||||
if( (buffer[0] == answer[0]) &&
|
||||
(buffer[1] == answer[1]) ){ // "DT369000"
|
||||
memmove( ( char * ) buffer, buffer + 2, strlen(buffer)); // "36900000"
|
||||
//~ returnValue[0] = buffer[0];
|
||||
//~ returnValue[1] = buffer[1];
|
||||
for(short i = 0; i < BUFFER_LEN; i++){ // "00000000" >
|
||||
returnValue[i] = buffer[i];
|
||||
}
|
||||
//~ returnValue[0] = buffer[2];
|
||||
//~ returnValue[1] = buffer[3];
|
||||
//~ returnValue[2] = buffer[4];
|
||||
// Get data as int
|
||||
SIOdone = atoi(returnValue);
|
||||
// Empty buffer
|
||||
for (short i; i < BUFFER_LEN; i++ ){ bufferAddress[i] = 0; }
|
||||
// Return data
|
||||
return SIOdone;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
i++;
|
||||
// Avoid infinite loop
|
||||
if ( i > 3000 ){
|
||||
// empty buffer
|
||||
for ( short i; i < BUFFER_LEN;i++ ){ bufferAddress[i] = 0; }
|
||||
// Get out of function after X iterations
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Should never be reached, but just to be sure
|
||||
return 0;
|
||||
};
|
||||
u_short PCload( u_long * loadAddress, volatile u_char * bufferAddress, u_char * overlayFileID ) {
|
||||
// Send filename , load address, and flag address
|
||||
// Returns 1 if ok, 0 else
|
||||
// E.G : 00 01 06 04 80010000 08 80010001 01 + 0000000000 <- cmd checksum
|
||||
// 00 01 06 08 8003edf8 08 8001f0f0 00 2439964735 -> 38 Bytes
|
||||
// Expected answer
|
||||
const char * answer = "OK";
|
||||
// Using hardcoded length of 28 B
|
||||
char commandBuffer[28];
|
||||
// pointer is 4 B, but represented here by 8 B / chars.
|
||||
u_short addLenInChar = sizeof(loadAddress) * 2;
|
||||
sprintf(commandBuffer, "%02u%02u%02u%02u%08x%02u%08x%02u", ESCAPE, PROTOCOL, LOAD, addLenInChar, loadAddress, addLenInChar, bufferAddress, *overlayFileID);
|
||||
u_int cmdChecksum = djbHash(commandBuffer, 28);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
// Need delay ?
|
||||
wait();
|
||||
return waitForSIODone( answer , bufferAddress );
|
||||
};
|
||||
int PCinit( volatile u_char * bufferAddress ){
|
||||
// Close all the files on the PC.
|
||||
// Returns OK if success, -1 if fail
|
||||
// E.G : 00 01 07 + 00 00 00 00 00
|
||||
const char * answer = "OK";
|
||||
u_int bufferLen = 16;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u08%08x", ESCAPE, PROTOCOL, INIT, bufferAddress);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCclose( int fileDesc, volatile u_char * bufferAddress ){
|
||||
// Close file corresponding to fileDesc
|
||||
// Returns OK if success, -1 if fail
|
||||
// E.G : 00 01 01 16 + 00 00 00 00 00
|
||||
const char * answer = "OK";
|
||||
u_int bufferLen = 8;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u%02u", ESCAPE, PROTOCOL, CLOSE, fileDesc);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCopen( const char * filename, u_char mode, volatile u_char * bufferAddress ){
|
||||
// Open filename in mode
|
||||
// Returns file descriptor or -1 if fail
|
||||
// Mode can be 00 (RO), 01(WO), 02 (RW) (see pcdrv.h, l.52)
|
||||
// E.G : 00 01 00 08 48454C4F 00 + 0000000000 <- cmd checksum 18 + CHECKSUM_LEN B
|
||||
// Expected answer DaTa + 2 chars of data
|
||||
const char * answer = "DT";
|
||||
// Should we allow names > 8 chars ? If so, use strlen() to determine buffer length
|
||||
u_int bufferLen = 20 + strlen( filename ); // else use a length of 28
|
||||
u_short addLenInChar = sizeof(bufferAddress) * 2;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u%02u%*s%02u%08x%02u", ESCAPE, PROTOCOL, OPEN, strlen( filename ), strlen( filename ), filename, addLenInChar, bufferAddress, mode);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCcreate( const char * filename, u_char mode, volatile u_char * bufferAddress ){
|
||||
// Create and open file with filename in mode
|
||||
// Returns file descriptor or -1 if fail
|
||||
// Mode can be 00 (RO), 01(WO), 02 (RW) (see pcdrv.h, l.52)
|
||||
// E.G : 00 01 00 08 48454C4F 00 + 0000000000 <- cmd checksum 18 + CHECKSUM_LEN B
|
||||
// Expected answer DaTa + 2 chars of data
|
||||
const char * answer = "DT";
|
||||
u_int bufferLen = 20 + strlen( filename );
|
||||
u_short addLenInChar = sizeof(bufferAddress) * 2;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u%02u%*s%02u%08x%02u", ESCAPE, PROTOCOL, CREATE, strlen( filename ), strlen( filename ), filename, addLenInChar, bufferAddress, mode);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCseek( int fd, int curPos, int offset, int accessMode, volatile u_char * bufferAddress ){
|
||||
// Seek offset in file
|
||||
// Return new position or -1 if fail
|
||||
// accessMode can be relative to start : 0 , relative to current pos : 1 , relative to end of file : 2
|
||||
// E.G : 00 01 02 02 09 08 00000000 08 00000369 08 80025808 01 1907502951
|
||||
const char * answer = "DT";
|
||||
u_int bufferLen = 42; // Will we need file desc > 99 ?
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u02%02u08%08d08%08d08%08x%02u", ESCAPE, PROTOCOL, SEEK, fd, curPos, offset, bufferAddress, accessMode);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCread( int fd, int pos, int len, volatile char * dataBuffer, volatile char * bufferAddress ){
|
||||
// Read and returns len bytes at pos on file fd
|
||||
// Send read bytes to dataBuffer
|
||||
// Return OK or -1 if fail
|
||||
// E.G : 00 01 03 02 88 08 00000369 08 00000016 08 80025808 08 80025848 1841163114
|
||||
const char * answer = "OK";
|
||||
u_int bufferLen = 50;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u02%02u08%08d08%08d08%08x08%08x", ESCAPE, PROTOCOL, READ, fd, pos, len, dataBuffer, bufferAddress);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
int PCwrite( int fd, int pos, int len, volatile u_char * dataBuffer, volatile u_char * bufferAddress ){
|
||||
// Send len bytes from dataBuffer to be written in file fd at pos
|
||||
// Return OK or -1 if fail
|
||||
// E.G : 00 01 04 02 88 08 00000000 08 00000016 16 ALLEZONYVALESENF 08 80025808 4060903934
|
||||
const char * answer = "OK";
|
||||
// Add space for null terminator
|
||||
u_char tempBuffer[len + 1];
|
||||
// Fill tempBuffer with data at dataBuffer
|
||||
for( int b = 0; b < len; b++ ){
|
||||
tempBuffer[b] = dataBuffer[b];
|
||||
}
|
||||
// Set null terminator
|
||||
tempBuffer[len] = 0;
|
||||
u_int bufferLen = 42 + len;
|
||||
char commandBuffer[ bufferLen ];
|
||||
sprintf(commandBuffer, "%02u%02u%02u02%02u08%08d08%08d%02u%*s08%08x", ESCAPE, PROTOCOL, WRITE, fd, pos, len, len, len, tempBuffer, bufferAddress);
|
||||
u_int cmdChecksum = djbHash( commandBuffer, bufferLen);
|
||||
printf("%s%*u", commandBuffer, CHECKSUM_LEN, cmdChecksum);
|
||||
wait();
|
||||
return waitForSIODone( answer, bufferAddress );
|
||||
};
|
||||
// WIP : Build command for use with putchar instead of printf
|
||||
/*
|
||||
void BuildCmd(){
|
||||
// Build command in the buffer
|
||||
u_char escape = 0x00; // Hypothetical Escape char for unirom
|
||||
u_char protocol = 0x01; // Hypothetical protocol indicator for unirom
|
||||
// Command is 18 B data + 10 B checksum
|
||||
char commandBuffer[28] = {0};
|
||||
u_char checkSumLen = 10;
|
||||
short i = 0;
|
||||
u_long * loadAddress;
|
||||
u_char * flagAddress;
|
||||
// FntPrint("%x\n", loadAddress);
|
||||
while( i < sizeof( commandBuffer) - checkSumLen ){
|
||||
if( i == 0 ){
|
||||
commandBuffer[0] = escape;
|
||||
commandBuffer[1] = protocol;
|
||||
i = 2;
|
||||
}
|
||||
if( i == 2 ){
|
||||
commandBuffer[i] = LOAD;
|
||||
i ++;
|
||||
}
|
||||
commandBuffer[i] = sizeof(&loadAddress);
|
||||
i ++;
|
||||
for( u_int b = 0; b < sizeof(&loadAddress); b++ ){
|
||||
commandBuffer[i] = U32ToChars( ( u_int ) loadAddress, b );
|
||||
// FntPrint("i: %d b: %d, %02x\n", i, b, commandBuffer[i]);
|
||||
i++;
|
||||
}
|
||||
commandBuffer[i] = sizeof(&flagAddress);
|
||||
i ++;
|
||||
for( u_int b = 0; b < sizeof(&flagAddress); b++ ){
|
||||
commandBuffer[i] = U32ToChars( ( u_int ) flagAddress, b );
|
||||
// FntPrint("i: %d b: %d, %02x\n", i, b, commandBuffer[i]);
|
||||
i++;
|
||||
}
|
||||
// commandBuffer[i] = overlayFileID;
|
||||
i++;
|
||||
for(short c = 0; c < sizeof(commandBuffer) - checkSumLen; c++){
|
||||
// FntPrint("%x", commandBuffer[c]);
|
||||
}
|
||||
// FntPrint("\n%d, %d", i, sizeof(commandBuffer) );
|
||||
}
|
||||
for(u_int b = 0; b < sizeof(commandBuffer)-3; b+=4 ){
|
||||
u_char bytes[4] = {0};
|
||||
bytes[0] = commandBuffer[b + 0];
|
||||
bytes[1] = commandBuffer[b + 1];
|
||||
bytes[2] = commandBuffer[b + 2];
|
||||
bytes[3] = commandBuffer[b + 3];
|
||||
sendU32( charsToU32(bytes) );
|
||||
}
|
||||
// u_int cmdChecksum = djbHash((const char * )commandBuffer, sizeof(commandBuffer) - checkSumLen);
|
||||
// FntPrint("\n%d\n", cmdChecksum);
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,63 @@
|
|||
// https://discord.com/channels/642647820683444236/646765703143227394/836663602718048297
|
||||
// 28-04-2021 - @nicolasnoble:
|
||||
//
|
||||
// step 0 : have a protocol that can handle open / close / seek / read / write
|
||||
//
|
||||
// step 1 : have an unhandled exception handler installed in the kernel to capture the pcdrv functions; that sounds scary, but it's extremely straightforward (I think @sickle currently has one for his debugger)
|
||||
//
|
||||
// step 1.5: implement the pcopen / pcclose / pcread / pcwrite / pcseek functions, which are basically the same model as my syscalls.h file
|
||||
//
|
||||
// step 2 : implement the unhandler exception handler in a way that properly redirects the calls to the step 0 protocol, and returns gracefully to the caller when done - that can be a bit tricky, but it's totally doable
|
||||
// this step requires understanding the ReturnFromException mechanism basically which, to be fair, is sort of understandable from this one file:
|
||||
// https://github.com/grumpycoders/pcsx-redux/blob/main/src/mips/openbios/handlers/syscall.c
|
||||
// you can see the syscall_unresolvedException() call at the bottom , this just needs to follow the same pattern
|
||||
// and break down the caller, modify the current thread's registers, and return to the caller
|
||||
// at this point, we have a working basic pcdrv feature, using pcopen / pcclose / pcread / pcwrite / pcseek
|
||||
//
|
||||
// step 3 : add a kernel driver for "pcdrv:" that just piggy backs on pc* functions so that people can simply do a int file = open("pcdrv:blah.txt", O_RDONLY);
|
||||
// (and thus support things like CSOTN technically)
|
||||
//
|
||||
// https://discord.com/channels/642647820683444236/646765703143227394/837416216640618558
|
||||
// So like we'd have, say,
|
||||
// 00 01 00 03 58 58 58 01 xx for pcdrv' file open to open the file name "XXX" and attribute 1
|
||||
// (escape) (pcdrv command) (pcdrv open) (filename with length as prefix) (attributes) (checksum)
|
||||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
//~ #include <libsio.h>
|
||||
#include <string.h>
|
||||
#define BUFFER_LEN 8
|
||||
#define ESCAPE 0x00 // Hypothetical Escape char for unirom
|
||||
#define PROTOCOL 0x01
|
||||
//pcdrv commands
|
||||
#define OPEN 0x00
|
||||
#define CLOSE 0x01
|
||||
#define SEEK 0x02
|
||||
#define READ 0x03
|
||||
#define WRITE 0x04
|
||||
#define CREATE 0x05
|
||||
#define LOAD 0x06
|
||||
#define INIT 0x07
|
||||
// flags parameters
|
||||
#define O_RDONLY 0x00
|
||||
#define O_WRONLY 0x01
|
||||
#define O_RDWR 0x02
|
||||
#define CHECKSUM_LEN 10
|
||||
//~ static char sio_read();
|
||||
int waitForSIODone( const char * answer, volatile char * bufferAddress);
|
||||
static inline uint32_t djbHash( const char* str, unsigned n );
|
||||
static inline uint32_t djbProcess(uint32_t hash, const char str[], unsigned n);
|
||||
//~ uint32_t charsToU32 ( char * byte );
|
||||
//~ u_char U32ToChars( u_int memoryAddress, u_int byteNbr);
|
||||
//~ void sendU32(uint32_t data);
|
||||
//~ void sendRU32(uint32_t data);
|
||||
u_short PCload( u_long * loadAddress, volatile u_char * bufferAddress, u_char * overlayFileID );
|
||||
int PCinit( volatile u_char * bufferAddress );
|
||||
int PCclose( int fileDesc, volatile u_char * bufferAddress );
|
||||
int PCopen( const char * filename, u_char mode, volatile u_char * bufferAddress );
|
||||
int PCcreate( const char * filename, u_char mode, volatile u_char * bufferAddress );
|
||||
int PCseek( int fd, int curPos, int offset, int accessMode, volatile u_char * bufferAddress );
|
||||
int PCread( int fd, int pos, int len, volatile char * dataBuffer, volatile char * bufferAddress );
|
||||
int PCwrite( int fd, int pos, int len, volatile u_char * dataBuffer, volatile u_char * bufferAddress );
|
Loading…
Reference in New Issue