From db37d36d9a5ab6cf3769c9fd1b67ec06502ad987 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:18:24 -0700 Subject: [PATCH 1/9] Reformatting luac.c to the project style. --- src/luac.c | 870 ++++++++++++++++++++++++++--------------------------- 1 file changed, 432 insertions(+), 438 deletions(-) diff --git a/src/luac.c b/src/luac.c index 7fc4c08..967bea0 100644 --- a/src/luac.c +++ b/src/luac.c @@ -12,45 +12,41 @@ #define luac_c #define LUA_CORE -#include "lua.h" #include "lauxlib.h" - #include "lobject.h" #include "lstate.h" +#include "lua.h" #include "lundump.h" static void PrintFunction(const Proto* f, int full); -#define luaU_print PrintFunction - -#define PROGNAME "luac" /* default program name */ -#define OUTPUT PROGNAME ".out" /* default output file */ - -static int listing=0; /* list bytecodes? */ -static int dumping=1; /* dump bytecodes? */ -static int stripping=0; /* strip debug information? */ -static char Output[]={ OUTPUT }; /* default output file name */ -static const char* output=Output; /* actual output file name */ -static const char* progname=PROGNAME; /* actual program name */ - -static void fatal(const char* message) -{ - fprintf(stderr,"%s: %s\n",progname,message); - exit(EXIT_FAILURE); +#define luaU_print PrintFunction + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing = 0; /* list bytecodes? */ +static int dumping = 1; /* dump bytecodes? */ +static int stripping = 0; /* strip debug information? */ +static char Output[] = {OUTPUT}; /* default output file name */ +static const char* output = Output; /* actual output file name */ +static const char* progname = PROGNAME; /* actual program name */ + +static void fatal(const char* message) { + fprintf(stderr, "%s: %s\n", progname, message); + exit(EXIT_FAILURE); } -static void cannot(const char* what) -{ - fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); - exit(EXIT_FAILURE); +static void cannot(const char* what) { + fprintf(stderr, "%s: cannot %s %s: %s\n", progname, what, output, strerror(errno)); + exit(EXIT_FAILURE); } -static void usage(const char* message) -{ - if (*message=='-') - fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); - else - fprintf(stderr,"%s: %s\n",progname,message); - fprintf(stderr, +static void usage(const char* message) { + if (*message == '-') + fprintf(stderr, "%s: unrecognized option " LUA_QS "\n", progname, message); + else + fprintf(stderr, "%s: %s\n", progname, message); + fprintf(stderr, "usage: %s [options] [filenames]\n" "Available options are:\n" " -l list (use -l -l for full listing)\n" @@ -61,154 +57,135 @@ static void usage(const char* message) " -- stop handling options\n" " - stop handling options and process stdin\n" ,progname,Output); - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } -#define IS(s) (strcmp(argv[i],s)==0) - -static int doargs(int argc, char* argv[]) -{ - int i; - int version=0; - if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; - for (i=1; itop+(i)) - -static const Proto* combine(lua_State* L, int n) -{ - if (n==1) - return toproto(L,-1); - else - { - Proto* f; - int i=n; - if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1)); - f=toproto(L,-1); - for (i=0; ip[i]=toproto(L,i-n-1); - if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0; - } - f->sizelineinfo=0; - return f; - } +#define toproto(L, i) getproto(L->top + (i)) + +static const Proto* combine(lua_State* L, int n) { + if (n == 1) + return toproto(L, -1); + else { + Proto* f; + int i = n; + if (lua_load(L, reader, &i, "=(" PROGNAME ")", NULL) != LUA_OK) fatal(lua_tostring(L, -1)); + f = toproto(L, -1); + for (i = 0; i < n; i++) { + f->p[i] = toproto(L, i - n - 1); + if (f->p[i]->sizeupvalues > 0) f->p[i]->upvalues[0].instack = 0; + } + f->sizelineinfo = 0; + return f; + } } -static int writer(lua_State* L, const void* p, size_t size, void* u) -{ - UNUSED(L); - return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +static int writer(lua_State* L, const void* p, size_t size, void* u) { + UNUSED(L); + return (fwrite(p, size, 1, (FILE*)u) != 1) && (size != 0); } -LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, - const char *mode); +LUALIB_API int(luaL_loadfilex)(lua_State* L, const char* filename, const char* mode); -#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) +#define luaL_loadfile(L, f) luaL_loadfilex(L, f, NULL) typedef struct LoadF { - int n; /* number of pre-read characters */ - FILE *f; /* file being read */ - char buff[LUAL_BUFFERSIZE]; /* area for reading file */ + int n; /* number of pre-read characters */ + FILE* f; /* file being read */ + char buff[LUAL_BUFFERSIZE]; /* area for reading file */ } LoadF; - -static const char *getF (lua_State *L, void *ud, size_t *size) { - LoadF *lf = (LoadF *)ud; - (void)L; /* not used */ - if (lf->n > 0) { /* are there pre-read characters to be read? */ - *size = lf->n; /* return them (chars already in buffer) */ - lf->n = 0; /* no more pre-read characters */ - } - else { /* read a block from file */ - /* 'fread' can return > 0 *and* set the EOF flag. If next call to - 'getF' called 'fread', it might still wait for user input. - The next check avoids this problem. */ - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ - } - return lf->buff; +static const char* getF(lua_State* L, void* ud, size_t* size) { + LoadF* lf = (LoadF*)ud; + (void)L; /* not used */ + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; } - -static int errfile (lua_State *L, const char *what, int fnameindex) { - const char *serr = strerror(errno); - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); - lua_remove(L, fnameindex); - return LUA_ERRFILE; +static int errfile(lua_State* L, const char* what, int fnameindex) { + const char* serr = strerror(errno); + const char* filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; } - -static int skipBOM (LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ - int c; - lf->n = 0; - do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char *)p++) return c; - lf->buff[lf->n++] = c; /* to be read by the parser */ - } while (*p != '\0'); - lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ +static int skipBOM(LoadF* lf) { + const char* p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ + int c; + lf->n = 0; + do { + c = getc(lf->f); + if (c == EOF || c != *(const unsigned char*)p++) return c; + lf->buff[lf->n++] = c; /* to be read by the parser */ + } while (*p != '\0'); + lf->n = 0; /* prefix matched; discard it */ + return getc(lf->f); /* return next character */ } /* @@ -218,95 +195,88 @@ static int skipBOM (LoadF *lf) { ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ -static int skipcomment (LoadF *lf, int *cp) { - int c = *cp = skipBOM(lf); - if (c == '#') { /* first line is a comment (Unix exec. file)? */ - do { /* skip first line */ - c = getc(lf->f); - } while (c != EOF && c != '\n') ; - *cp = getc(lf->f); /* skip end-of-line, if present */ - return 1; /* there was a comment */ - } - else return 0; /* no comment */ +static int skipcomment(LoadF* lf, int* cp) { + int c = *cp = skipBOM(lf); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(lf->f); + } while (c != EOF && c != '\n'); + *cp = getc(lf->f); /* skip end-of-line, if present */ + return 1; /* there was a comment */ + } else + return 0; /* no comment */ } - -LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, - const char *mode) { - LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - if (lf.f == NULL) return errfile(L, "open", fnameindex); - } - if (skipcomment(&lf, &c)) /* read initial portion */ - lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, "reopen", fnameindex); - skipcomment(&lf, &c); /* re-read initial portion */ - } - if (c != EOF) - lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ - status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); - readstatus = ferror(lf.f); - if (filename) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, "read", fnameindex); - } - lua_remove(L, fnameindex); - return status; +LUALIB_API int luaL_loadfilex(lua_State* L, const char* filename, const char* mode) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + if (skipcomment(&lf, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(&lf, &c); /* re-read initial portion */ + } + if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; } -static int pmain(lua_State* L) -{ - int argc=(int)lua_tointeger(L,1); - char** argv=(char**)lua_touserdata(L,2); - const Proto* f; - int i; - if (!lua_checkstack(L,argc)) fatal("too many input files"); - for (i=0; i1); - if (dumping) - { - FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); - if (D==NULL) cannot("open"); - lua_lock(L); - luaU_dump(L,f,writer,D,stripping); - lua_unlock(L); - if (ferror(D)) cannot("write"); - if (fclose(D)) cannot("close"); - } - return 0; +static int pmain(lua_State* L) { + int argc = (int)lua_tointeger(L, 1); + char** argv = (char**)lua_touserdata(L, 2); + const Proto* f; + int i; + if (!lua_checkstack(L, argc)) fatal("too many input files"); + for (i = 0; i < argc; i++) { + const char* filename = IS("-") ? NULL : argv[i]; + if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1)); + } + f = combine(L, argc); + if (listing) luaU_print(f, listing > 1); + if (dumping) { + FILE* D = (output == NULL) ? stdout : fopen(output, "wb"); + if (D == NULL) cannot("open"); + lua_lock(L); + luaU_dump(L, f, writer, D, stripping); + lua_unlock(L); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; } -int main(int argc, char* argv[]) -{ - lua_State* L; - int i=doargs(argc,argv); - argc-=i; argv+=i; - if (argc<=0) usage("no input files given"); - L=luaL_newstate(); - if (L==NULL) fatal("cannot create state: not enough memory"); - lua_pushcfunction(L,&pmain); - lua_pushinteger(L,argc); - lua_pushlightuserdata(L,argv); - if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1)); - lua_close(L); - return EXIT_SUCCESS; +int main(int argc, char* argv[]) { + lua_State* L; + int i = doargs(argc, argv); + argc -= i; + argv += i; + if (argc <= 0) usage("no input files given"); + L = luaL_newstate(); + if (L == NULL) fatal("cannot create state: not enough memory"); + lua_pushcfunction(L, &pmain); + lua_pushinteger(L, argc); + lua_pushlightuserdata(L, argv); + if (lua_pcall(L, 2, 0, 0) != LUA_OK) fatal(lua_tostring(L, -1)); + lua_close(L); + return EXIT_SUCCESS; } /* @@ -325,215 +295,239 @@ int main(int argc, char* argv[]) #include "lobject.h" #include "lopcodes.h" -#define VOID(p) ((const void*)(p)) - -static void PrintString(const TString* ts) -{ - const char* s=getstr(ts); - size_t i,n=ts->tsv.len; - printf("%c",'"'); - for (i=0; itsv.len; + printf("%c", '"'); + for (i = 0; i < n; i++) { + int c = (int)(unsigned char)s[i]; + switch (c) { + case '"': + printf("\\\""); + break; + case '\\': + printf("\\\\"); + break; + case '\a': + printf("\\a"); + break; + case '\b': + printf("\\b"); + break; + case '\f': + printf("\\f"); + break; + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + case '\v': + printf("\\v"); + break; + default: + if (isprint(c)) + printf("%c", c); + else + printf("\\%03d", c); + } + } + printf("%c", '"'); } -static void PrintConstant(const Proto* f, int i) -{ - const TValue* o=&f->k[i]; - switch (ttypenv(o)) - { - case LUA_TNIL: - printf("nil"); - break; - case LUA_TBOOLEAN: - printf(bvalue(o) ? "true" : "false"); - break; - case LUA_TNUMBER: - printf(LUA_NUMBER_FMT,nvalue(o)); - break; - case LUA_TSTRING: - PrintString(rawtsvalue(o)); - break; - default: /* cannot happen */ - printf("? type=%d",ttype(o)); - break; - } +static void PrintConstant(const Proto* f, int i) { + const TValue* o = &f->k[i]; + switch (ttypenv(o)) { + case LUA_TNIL: + printf("nil"); + break; + case LUA_TBOOLEAN: + printf(bvalue(o) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf(LUA_NUMBER_FMT, nvalue(o)); + break; + case LUA_TSTRING: + PrintString(rawtsvalue(o)); + break; + default: /* cannot happen */ + printf("? type=%d", ttype(o)); + break; + } } #define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") -#define MYK(x) (-1-(x)) - -static void PrintCode(const Proto* f) -{ - const Instruction* code=f->code; - int pc,n=f->sizecode; - for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); - printf("%-9s\t",luaP_opnames[o]); - switch (getOpMode(o)) - { - case iABC: - printf("%d",a); - if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b); - if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c); - break; - case iABx: - printf("%d",a); - if (getBMode(o)==OpArgK) printf(" %d",MYK(bx)); - if (getBMode(o)==OpArgU) printf(" %d",bx); - break; - case iAsBx: - printf("%d %d",a,sbx); - break; - case iAx: - printf("%d",MYK(ax)); - break; - } - switch (o) - { - case OP_LOADK: - printf("\t; "); PrintConstant(f,bx); - break; - case OP_GETUPVAL: - case OP_SETUPVAL: - printf("\t; %s",UPVALNAME(b)); - break; - case OP_GETTABUP: - printf("\t; %s",UPVALNAME(b)); - if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); } - break; - case OP_SETTABUP: - printf("\t; %s",UPVALNAME(a)); - if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); } - if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); } - break; - case OP_GETTABLE: - case OP_SELF: - if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } - break; - case OP_SETTABLE: - case OP_ADD: - case OP_SUB: - case OP_MUL: - case OP_DIV: - case OP_POW: - case OP_EQ: - case OP_LT: - case OP_LE: - if (ISK(b) || ISK(c)) - { - printf("\t; "); - if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); - printf(" "); - if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); +#define MYK(x) (-1 - (x)) + +static void PrintCode(const Proto* f) { + const Instruction* code = f->code; + int pc, n = f->sizecode; + for (pc = 0; pc < n; pc++) { + Instruction i = code[pc]; + OpCode o = GET_OPCODE(i); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + int ax = GETARG_Ax(i); + int bx = GETARG_Bx(i); + int sbx = GETARG_sBx(i); + int line = getfuncline(f, pc); + printf("\t%d\t", pc + 1); + if (line > 0) + printf("[%d]\t", line); + else + printf("[-]\t"); + printf("%-9s\t", luaP_opnames[o]); + switch (getOpMode(o)) { + case iABC: + printf("%d", a); + if (getBMode(o) != OpArgN) printf(" %d", ISK(b) ? (MYK(INDEXK(b))) : b); + if (getCMode(o) != OpArgN) printf(" %d", ISK(c) ? (MYK(INDEXK(c))) : c); + break; + case iABx: + printf("%d", a); + if (getBMode(o) == OpArgK) printf(" %d", MYK(bx)); + if (getBMode(o) == OpArgU) printf(" %d", bx); + break; + case iAsBx: + printf("%d %d", a, sbx); + break; + case iAx: + printf("%d", MYK(ax)); + break; + } + switch (o) { + case OP_LOADK: + printf("\t; "); + PrintConstant(f, bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + printf("\t; %s", UPVALNAME(b)); + break; + case OP_GETTABUP: + printf("\t; %s", UPVALNAME(b)); + if (ISK(c)) { + printf(" "); + PrintConstant(f, INDEXK(c)); + } + break; + case OP_SETTABUP: + printf("\t; %s", UPVALNAME(a)); + if (ISK(b)) { + printf(" "); + PrintConstant(f, INDEXK(b)); + } + if (ISK(c)) { + printf(" "); + PrintConstant(f, INDEXK(c)); + } + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { + printf("\t; "); + PrintConstant(f, INDEXK(c)); + } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) { + printf("\t; "); + if (ISK(b)) + PrintConstant(f, INDEXK(b)); + else + printf("-"); + printf(" "); + if (ISK(c)) + PrintConstant(f, INDEXK(c)); + else + printf("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + case OP_TFORLOOP: + printf("\t; to %d", sbx + pc + 2); + break; + case OP_CLOSURE: + printf("\t; %p", VOID(f->p[bx])); + break; + case OP_SETLIST: + if (c == 0) + printf("\t; %d", (int)code[++pc]); + else + printf("\t; %d", c); + break; + case OP_EXTRAARG: + printf("\t; "); + PrintConstant(f, ax); + break; + default: + break; + } + printf("\n"); } - break; - case OP_JMP: - case OP_FORLOOP: - case OP_FORPREP: - case OP_TFORLOOP: - printf("\t; to %d",sbx+pc+2); - break; - case OP_CLOSURE: - printf("\t; %p",VOID(f->p[bx])); - break; - case OP_SETLIST: - if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c); - break; - case OP_EXTRAARG: - printf("\t; "); PrintConstant(f,ax); - break; - default: - break; - } - printf("\n"); - } } -#define SS(x) ((x==1)?"":"s") -#define S(x) (int)(x),SS(x) - -static void PrintHeader(const Proto* f) -{ - const char* s=f->source ? getstr(f->source) : "=?"; - if (*s=='@' || *s=='=') - s++; - else if (*s==LUA_SIGNATURE[0]) - s="(bstring)"; - else - s="(string)"; - printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", - (f->linedefined==0)?"main":"function",s, - f->linedefined,f->lastlinedefined, - S(f->sizecode),VOID(f)); - printf("%d%s param%s, %d slot%s, %d upvalue%s, ", - (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams), - S(f->maxstacksize),S(f->sizeupvalues)); - printf("%d local%s, %d constant%s, %d function%s\n", - S(f->sizelocvars),S(f->sizek),S(f->sizep)); +#define SS(x) ((x == 1) ? "" : "s") +#define S(x) (int)(x), SS(x) + +static void PrintHeader(const Proto* f) { + const char* s = f->source ? getstr(f->source) : "=?"; + if (*s == '@' || *s == '=') + s++; + else if (*s == LUA_SIGNATURE[0]) + s = "(bstring)"; + else + s = "(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", (f->linedefined == 0) ? "main" : "function", s, f->linedefined, + f->lastlinedefined, S(f->sizecode), VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", (int)(f->numparams), f->is_vararg ? "+" : "", SS(f->numparams), + S(f->maxstacksize), S(f->sizeupvalues)); + printf("%d local%s, %d constant%s, %d function%s\n", S(f->sizelocvars), S(f->sizek), S(f->sizep)); } -static void PrintDebug(const Proto* f) -{ - int i,n; - n=f->sizek; - printf("constants (%d) for %p:\n",n,VOID(f)); - for (i=0; isizelocvars; - printf("locals (%d) for %p:\n",n,VOID(f)); - for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); - } - n=f->sizeupvalues; - printf("upvalues (%d) for %p:\n",n,VOID(f)); - for (i=0; iupvalues[i].instack,f->upvalues[i].idx); - } +static void PrintDebug(const Proto* f) { + int i, n; + n = f->sizek; + printf("constants (%d) for %p:\n", n, VOID(f)); + for (i = 0; i < n; i++) { + printf("\t%d\t", i + 1); + PrintConstant(f, i); + printf("\n"); + } + n = f->sizelocvars; + printf("locals (%d) for %p:\n", n, VOID(f)); + for (i = 0; i < n; i++) { + printf("\t%d\t%s\t%d\t%d\n", i, getstr(f->locvars[i].varname), f->locvars[i].startpc + 1, + f->locvars[i].endpc + 1); + } + n = f->sizeupvalues; + printf("upvalues (%d) for %p:\n", n, VOID(f)); + for (i = 0; i < n; i++) { + printf("\t%d\t%s\t%d\t%d\n", i, UPVALNAME(i), f->upvalues[i].instack, f->upvalues[i].idx); + } } -static void PrintFunction(const Proto* f, int full) -{ - int i,n=f->sizep; - PrintHeader(f); - PrintCode(f); - if (full) PrintDebug(f); - for (i=0; ip[i],full); +static void PrintFunction(const Proto* f, int full) { + int i, n = f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) PrintDebug(f); + for (i = 0; i < n; i++) PrintFunction(f->p[i], full); } From d27cc4b64a279ed46ef9534fe4ba1a20a31a8ecb Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Sun, 20 Sep 2026 20:45:59 -0700 Subject: [PATCH 2/9] Modifying luac to build and run as a ps-exe Arguments as passed through pcsx-redux' debugging ability to hook into invalid memory reads, through the args.lua script, which needs to be loaded at the same time as running the luac.ps-exe binary. --- .gitignore | 4 + src/args.lua | 34 +++++++ src/luac.c | 243 +++++++++++++++++++++++++------------------------ src/psx-glue.s | 46 ++++++++++ 4 files changed, 209 insertions(+), 118 deletions(-) create mode 100644 .gitignore create mode 100644 src/args.lua create mode 100644 src/psx-glue.s diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05f4578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.a +luac +lua diff --git a/src/args.lua b/src/args.lua new file mode 100644 index 0000000..f9bfe28 --- /dev/null +++ b/src/args.lua @@ -0,0 +1,34 @@ +local ffi = require 'ffi' +local uint8_t = ffi.typeof 'uint8_t[?]' +local argsBuffer +local processName = 'luac.ps-exe' + +function createArgsBuffer(...) + local args = { ... } + local spaceNeeded = #processName + 2 + for i, v in ipairs(args) do + local a = tostring(v) + spaceNeeded = spaceNeeded + #a + 1 + end + argsBuffer = ffi.new(uint8_t, spaceNeeded) + local ptr = ffi.cast('uint8_t *', argsBuffer) + ffi.copy(ptr, processName) + ptr = ptr + #processName + 1 + for i, v in ipairs(args) do + local a = tostring(v) + ffi.copy(ptr, a) + ptr = ptr + #a + 1 + spaceNeeded = spaceNeeded + #a + 1 + end + ptr[0] = 0 +end + +function UnknownMemoryRead(address, size) + if size ~= 1 then return 0xffffffff end + local offset = address - 0x40000000 + if offset < 0 then return 0xff end + if offset > ffi.sizeof(argsBuffer) then return 0xff end + return argsBuffer[offset] +end + +createArgsBuffer() diff --git a/src/luac.c b/src/luac.c index 967bea0..29d9bed 100644 --- a/src/luac.c +++ b/src/luac.c @@ -4,15 +4,20 @@ ** See Copyright Notice in lua.h */ -#include -#include -#include -#include - #define luac_c #define LUA_CORE +#define LUA_TARGET_PSX + +#include "common/hardware/pcsxhw.h" +#include "common/kernel/pcdrv.h" +#include "common/syscalls/syscalls.h" + +#define EXIT_FAILURE -1 +#define EXIT_SUCCESS 0 #include "lauxlib.h" +#include "lctype.h" +#include "llibc.h" #include "lobject.h" #include "lstate.h" #include "lua.h" @@ -32,21 +37,21 @@ static const char* output = Output; /* actual output file name */ static const char* progname = PROGNAME; /* actual program name */ static void fatal(const char* message) { - fprintf(stderr, "%s: %s\n", progname, message); - exit(EXIT_FAILURE); + ramsyscall_printf("%s: %s\n", progname, message); + pcsx_exit(EXIT_FAILURE); } static void cannot(const char* what) { - fprintf(stderr, "%s: cannot %s %s: %s\n", progname, what, output, strerror(errno)); - exit(EXIT_FAILURE); + ramsyscall_printf("%s: cannot %s %s\n", progname, what, output); + pcsx_exit(EXIT_FAILURE); } static void usage(const char* message) { if (*message == '-') - fprintf(stderr, "%s: unrecognized option " LUA_QS "\n", progname, message); + ramsyscall_printf("%s: unrecognized option " LUA_QS "\n", progname, message); else - fprintf(stderr, "%s: %s\n", progname, message); - fprintf(stderr, + ramsyscall_printf("%s: %s\n", progname, message); + ramsyscall_printf( "usage: %s [options] [filenames]\n" "Available options are:\n" " -l list (use -l -l for full listing)\n" @@ -55,14 +60,13 @@ static void usage(const char* message) { " -s strip debug information\n" " -v show version information\n" " -- stop handling options\n" - " - stop handling options and process stdin\n" ,progname,Output); - exit(EXIT_FAILURE); + pcsx_exit(EXIT_FAILURE); } -#define IS(s) (strcmp(argv[i], s) == 0) +#define IS(s) (luaA_strcmp(argv[i], s) == 0) -static int doargs(int argc, char* argv[]) { +static int doargs(int argc, const char* argv[]) { int i; int version = 0; if (argv[0] != NULL && *argv[0] != 0) progname = argv[0]; @@ -74,16 +78,13 @@ static int doargs(int argc, char* argv[]) { ++i; if (version) ++version; break; - } else if (IS("-")) /* end of options; use stdin */ - break; - else if (IS("-l")) /* list */ + } else if (IS("-l")) /* list */ ++listing; else if (IS("-o")) /* output file */ { output = argv[++i]; if (output == NULL || *output == 0 || (*output == '-' && output[1] != 0)) usage(LUA_QL("-o") " needs argument"); - if (IS("-")) output = NULL; } else if (IS("-p")) /* parse only */ dumping = 0; else if (IS("-s")) /* strip debug information */ @@ -98,8 +99,8 @@ static int doargs(int argc, char* argv[]) { argv[--i] = Output; } if (version) { - printf("%s\n", LUA_COPYRIGHT); - if (version == argc - 1) exit(EXIT_SUCCESS); + ramsyscall_printf("%s\n", LUA_COPYRIGHT); + if (version == argc - 1) pcsx_exit(EXIT_SUCCESS); } return i; } @@ -138,7 +139,8 @@ static const Proto* combine(lua_State* L, int n) { static int writer(lua_State* L, const void* p, size_t size, void* u) { UNUSED(L); - return (fwrite(p, size, 1, (FILE*)u) != 1) && (size != 0); + int r = PCwrite(*(int*)u, p, size); + return (r != size) && (size != 0); } LUALIB_API int(luaL_loadfilex)(lua_State* L, const char* filename, const char* mode); @@ -147,7 +149,7 @@ LUALIB_API int(luaL_loadfilex)(lua_State* L, const char* filename, const char* m typedef struct LoadF { int n; /* number of pre-read characters */ - FILE* f; /* file being read */ + int f; /* file being read */ char buff[LUAL_BUFFERSIZE]; /* area for reading file */ } LoadF; @@ -158,23 +160,28 @@ static const char* getF(lua_State* L, void* ud, size_t* size) { *size = lf->n; /* return them (chars already in buffer) */ lf->n = 0; /* no more pre-read characters */ } else { /* read a block from file */ - /* 'fread' can return > 0 *and* set the EOF flag. If next call to - 'getF' called 'fread', it might still wait for user input. - The next check avoids this problem. */ - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + int read = PCread(lf->f, lf->buff, sizeof(lf->buff)); /* read block */ + if (read <= 0) return NULL; + *size = read; } return lf->buff; } static int errfile(lua_State* L, const char* what, int fnameindex) { - const char* serr = strerror(errno); const char* filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_pushfstring(L, "cannot %s %s", what, filename); lua_remove(L, fnameindex); return LUA_ERRFILE; } +#define EOF -1 + +static int getc(int f) { + int c = 0; + int r = PCread(f, &c, 1); + return r == 1 ? c : -1; +} + static int skipBOM(LoadF* lf) { const char* p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ int c; @@ -209,32 +216,21 @@ static int skipcomment(LoadF* lf, int* cp) { LUALIB_API int luaL_loadfilex(lua_State* L, const char* filename, const char* mode) { LoadF lf; - int status, readstatus; + int status; int c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; + return LUA_ERRFILE; } else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - if (lf.f == NULL) return errfile(L, "open", fnameindex); + const char * f = lua_pushfstring(L, "@%s", filename); + lf.f = PCopen(f + 1, 0, 0); + if (lf.f < 0) return errfile(L, "open", fnameindex); } if (skipcomment(&lf, &c)) /* read initial portion */ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, "reopen", fnameindex); - skipcomment(&lf, &c); /* re-read initial portion */ - } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); - readstatus = ferror(lf.f); - if (filename) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, "read", fnameindex); - } + PCclose(lf.f); lua_remove(L, fnameindex); return status; } @@ -252,31 +248,40 @@ static int pmain(lua_State* L) { f = combine(L, argc); if (listing) luaU_print(f, listing > 1); if (dumping) { - FILE* D = (output == NULL) ? stdout : fopen(output, "wb"); - if (D == NULL) cannot("open"); + const char * fname = lua_pushfstring(L, "%s", output); + int D = PCcreat(fname, 0); + lua_pop(L, 1); + if (D < 0) cannot("open"); lua_lock(L); - luaU_dump(L, f, writer, D, stripping); + luaU_dump(L, f, writer, &D, stripping); lua_unlock(L); - if (ferror(D)) cannot("write"); - if (fclose(D)) cannot("close"); + if (PCclose(D) != 0) cannot("close"); } return 0; } -int main(int argc, char* argv[]) { +int main() { + const char * argv[64] = { 0 }; + const char * argsPtr = (const char *) 0x40000000; + int argc = 0; + while (1) { + int len = luaA_strlen(argsPtr); + if (len == 0) break; + argv[argc++] = argsPtr; + argsPtr += len + 1; + } lua_State* L; int i = doargs(argc, argv); argc -= i; - argv += i; if (argc <= 0) usage("no input files given"); L = luaL_newstate(); if (L == NULL) fatal("cannot create state: not enough memory"); lua_pushcfunction(L, &pmain); lua_pushinteger(L, argc); - lua_pushlightuserdata(L, argv); + lua_pushlightuserdata(L, argv + i); if (lua_pcall(L, 2, 0, 0) != LUA_OK) fatal(lua_tostring(L, -1)); lua_close(L); - return EXIT_SUCCESS; + pcsx_exit(EXIT_SUCCESS); } /* @@ -285,9 +290,6 @@ int main(int argc, char* argv[]) { ** See Copyright Notice in lua.h */ -#include -#include - #define luac_c #define LUA_CORE @@ -300,64 +302,64 @@ int main(int argc, char* argv[]) { static void PrintString(const TString* ts) { const char* s = getstr(ts); size_t i, n = ts->tsv.len; - printf("%c", '"'); + ramsyscall_printf("%c", '"'); for (i = 0; i < n; i++) { int c = (int)(unsigned char)s[i]; switch (c) { case '"': - printf("\\\""); + ramsyscall_printf("\\\""); break; case '\\': - printf("\\\\"); + ramsyscall_printf("\\\\"); break; case '\a': - printf("\\a"); + ramsyscall_printf("\\a"); break; case '\b': - printf("\\b"); + ramsyscall_printf("\\b"); break; case '\f': - printf("\\f"); + ramsyscall_printf("\\f"); break; case '\n': - printf("\\n"); + ramsyscall_printf("\\n"); break; case '\r': - printf("\\r"); + ramsyscall_printf("\\r"); break; case '\t': - printf("\\t"); + ramsyscall_printf("\\t"); break; case '\v': - printf("\\v"); + ramsyscall_printf("\\v"); break; default: - if (isprint(c)) - printf("%c", c); + if (lisprint(c)) + ramsyscall_printf("%c", c); else - printf("\\%03d", c); + ramsyscall_printf("\\%03d", c); } } - printf("%c", '"'); + ramsyscall_printf("%c", '"'); } static void PrintConstant(const Proto* f, int i) { const TValue* o = &f->k[i]; switch (ttypenv(o)) { case LUA_TNIL: - printf("nil"); + ramsyscall_printf("nil"); break; case LUA_TBOOLEAN: - printf(bvalue(o) ? "true" : "false"); + ramsyscall_printf(bvalue(o) ? "true" : "false"); break; case LUA_TNUMBER: - printf(LUA_NUMBER_FMT, nvalue(o)); + ramsyscall_printf(LUA_NUMBER_FMT, nvalue(o)); break; case LUA_TSTRING: PrintString(rawtsvalue(o)); break; default: /* cannot happen */ - printf("? type=%d", ttype(o)); + ramsyscall_printf("? type=%d", ttype(o)); break; } } @@ -378,61 +380,61 @@ static void PrintCode(const Proto* f) { int bx = GETARG_Bx(i); int sbx = GETARG_sBx(i); int line = getfuncline(f, pc); - printf("\t%d\t", pc + 1); + ramsyscall_printf("\t%d\t", pc + 1); if (line > 0) - printf("[%d]\t", line); + ramsyscall_printf("[%d]\t", line); else - printf("[-]\t"); - printf("%-9s\t", luaP_opnames[o]); + ramsyscall_printf("[-]\t"); + ramsyscall_printf("%-9s\t", luaP_opnames[o]); switch (getOpMode(o)) { case iABC: - printf("%d", a); - if (getBMode(o) != OpArgN) printf(" %d", ISK(b) ? (MYK(INDEXK(b))) : b); - if (getCMode(o) != OpArgN) printf(" %d", ISK(c) ? (MYK(INDEXK(c))) : c); + ramsyscall_printf("%d", a); + if (getBMode(o) != OpArgN) ramsyscall_printf(" %d", ISK(b) ? (MYK(INDEXK(b))) : b); + if (getCMode(o) != OpArgN) ramsyscall_printf(" %d", ISK(c) ? (MYK(INDEXK(c))) : c); break; case iABx: - printf("%d", a); - if (getBMode(o) == OpArgK) printf(" %d", MYK(bx)); - if (getBMode(o) == OpArgU) printf(" %d", bx); + ramsyscall_printf("%d", a); + if (getBMode(o) == OpArgK) ramsyscall_printf(" %d", MYK(bx)); + if (getBMode(o) == OpArgU) ramsyscall_printf(" %d", bx); break; case iAsBx: - printf("%d %d", a, sbx); + ramsyscall_printf("%d %d", a, sbx); break; case iAx: - printf("%d", MYK(ax)); + ramsyscall_printf("%d", MYK(ax)); break; } switch (o) { case OP_LOADK: - printf("\t; "); + ramsyscall_printf("\t; "); PrintConstant(f, bx); break; case OP_GETUPVAL: case OP_SETUPVAL: - printf("\t; %s", UPVALNAME(b)); + ramsyscall_printf("\t; %s", UPVALNAME(b)); break; case OP_GETTABUP: - printf("\t; %s", UPVALNAME(b)); + ramsyscall_printf("\t; %s", UPVALNAME(b)); if (ISK(c)) { - printf(" "); + ramsyscall_printf(" "); PrintConstant(f, INDEXK(c)); } break; case OP_SETTABUP: - printf("\t; %s", UPVALNAME(a)); + ramsyscall_printf("\t; %s", UPVALNAME(a)); if (ISK(b)) { - printf(" "); + ramsyscall_printf(" "); PrintConstant(f, INDEXK(b)); } if (ISK(c)) { - printf(" "); + ramsyscall_printf(" "); PrintConstant(f, INDEXK(c)); } break; case OP_GETTABLE: case OP_SELF: if (ISK(c)) { - printf("\t; "); + ramsyscall_printf("\t; "); PrintConstant(f, INDEXK(c)); } break; @@ -446,41 +448,41 @@ static void PrintCode(const Proto* f) { case OP_LT: case OP_LE: if (ISK(b) || ISK(c)) { - printf("\t; "); + ramsyscall_printf("\t; "); if (ISK(b)) PrintConstant(f, INDEXK(b)); else - printf("-"); - printf(" "); + ramsyscall_printf("-"); + ramsyscall_printf(" "); if (ISK(c)) PrintConstant(f, INDEXK(c)); else - printf("-"); + ramsyscall_printf("-"); } break; case OP_JMP: case OP_FORLOOP: case OP_FORPREP: case OP_TFORLOOP: - printf("\t; to %d", sbx + pc + 2); + ramsyscall_printf("\t; to %d", sbx + pc + 2); break; case OP_CLOSURE: - printf("\t; %p", VOID(f->p[bx])); + ramsyscall_printf("\t; %p", VOID(f->p[bx])); break; case OP_SETLIST: if (c == 0) - printf("\t; %d", (int)code[++pc]); + ramsyscall_printf("\t; %d", (int)code[++pc]); else - printf("\t; %d", c); + ramsyscall_printf("\t; %d", c); break; case OP_EXTRAARG: - printf("\t; "); + ramsyscall_printf("\t; "); PrintConstant(f, ax); break; default: break; } - printf("\n"); + ramsyscall_printf("\n"); } } @@ -495,32 +497,32 @@ static void PrintHeader(const Proto* f) { s = "(bstring)"; else s = "(string)"; - printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", (f->linedefined == 0) ? "main" : "function", s, f->linedefined, + ramsyscall_printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n", (f->linedefined == 0) ? "main" : "function", s, f->linedefined, f->lastlinedefined, S(f->sizecode), VOID(f)); - printf("%d%s param%s, %d slot%s, %d upvalue%s, ", (int)(f->numparams), f->is_vararg ? "+" : "", SS(f->numparams), + ramsyscall_printf("%d%s param%s, %d slot%s, %d upvalue%s, ", (int)(f->numparams), f->is_vararg ? "+" : "", SS(f->numparams), S(f->maxstacksize), S(f->sizeupvalues)); - printf("%d local%s, %d constant%s, %d function%s\n", S(f->sizelocvars), S(f->sizek), S(f->sizep)); + ramsyscall_printf("%d local%s, %d constant%s, %d function%s\n", S(f->sizelocvars), S(f->sizek), S(f->sizep)); } static void PrintDebug(const Proto* f) { int i, n; n = f->sizek; - printf("constants (%d) for %p:\n", n, VOID(f)); + ramsyscall_printf("constants (%d) for %p:\n", n, VOID(f)); for (i = 0; i < n; i++) { - printf("\t%d\t", i + 1); + ramsyscall_printf("\t%d\t", i + 1); PrintConstant(f, i); - printf("\n"); + ramsyscall_printf("\n"); } n = f->sizelocvars; - printf("locals (%d) for %p:\n", n, VOID(f)); + ramsyscall_printf("locals (%d) for %p:\n", n, VOID(f)); for (i = 0; i < n; i++) { - printf("\t%d\t%s\t%d\t%d\n", i, getstr(f->locvars[i].varname), f->locvars[i].startpc + 1, + ramsyscall_printf("\t%d\t%s\t%d\t%d\n", i, getstr(f->locvars[i].varname), f->locvars[i].startpc + 1, f->locvars[i].endpc + 1); } n = f->sizeupvalues; - printf("upvalues (%d) for %p:\n", n, VOID(f)); + ramsyscall_printf("upvalues (%d) for %p:\n", n, VOID(f)); for (i = 0; i < n; i++) { - printf("\t%d\t%s\t%d\t%d\n", i, UPVALNAME(i), f->upvalues[i].instack, f->upvalues[i].idx); + ramsyscall_printf("\t%d\t%s\t%d\t%d\n", i, UPVALNAME(i), f->upvalues[i].instack, f->upvalues[i].idx); } } @@ -531,3 +533,8 @@ static void PrintFunction(const Proto* f, int full) { if (full) PrintDebug(f); for (i = 0; i < n; i++) PrintFunction(f->p[i], full); } + +int luaI_sprintf(char *str, const char *format, ...) { + *str = 0; + return 0; +} diff --git a/src/psx-glue.s b/src/psx-glue.s new file mode 100644 index 0000000..a36570b --- /dev/null +++ b/src/psx-glue.s @@ -0,0 +1,46 @@ + .set SBUS_DEV8_CTRL, 0x1f80101C + + .section .start, "ax", @progbits + .set noreorder + .align 2 + .global main + .global _start + .type _start, @function + +_start: + lw $t2, SBUS_DEV8_CTRL + lui $t0, 8 + lui $t1, 1 +_check_dev8: + bge $t2, $t0, _store_dev8 + nop + b _check_dev8 + add $t2, $t1 +_store_dev8: + sw $t2, SBUS_DEV8_CTRL + + la $t0, __bss_start + la $t1, __bss_end + + beq $t0, $t1, _bss_init_skip + nop + +_bss_init: + sw $0, 0($t0) + addiu $t0, 4 + bne $t0, $t1, _bss_init + nop + +_bss_init_skip: + + la $a1, _mainargv + j main + li $a0, 1 + + .section .rodata, "a", @progbits + .align 2 +_mainargv: + .word _progname + .word 0 +_progname: + .string "PSX.EXE" From d77fa025a18928cf8cfdb1b38187161872f2a7a1 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:17:17 -0700 Subject: [PATCH 3/9] Bounding argv, and a handful of smaller fixes in the ps-exe port. argv[] was filled without a bound, getc/EOF squatted on reserved names, writer() compared signed to unsigned, args.lua read one past the end, and the glue built an argv main() no longer takes. --- src/args.lua | 3 +-- src/luac.c | 37 ++++++++++++++++++++----------------- src/psx-glue.s | 11 +---------- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/args.lua b/src/args.lua index f9bfe28..fca5cd2 100644 --- a/src/args.lua +++ b/src/args.lua @@ -18,7 +18,6 @@ function createArgsBuffer(...) local a = tostring(v) ffi.copy(ptr, a) ptr = ptr + #a + 1 - spaceNeeded = spaceNeeded + #a + 1 end ptr[0] = 0 end @@ -27,7 +26,7 @@ function UnknownMemoryRead(address, size) if size ~= 1 then return 0xffffffff end local offset = address - 0x40000000 if offset < 0 then return 0xff end - if offset > ffi.sizeof(argsBuffer) then return 0xff end + if offset >= ffi.sizeof(argsBuffer) then return 0xff end return argsBuffer[offset] end diff --git a/src/luac.c b/src/luac.c index 29d9bed..14c73a0 100644 --- a/src/luac.c +++ b/src/luac.c @@ -29,6 +29,9 @@ static void PrintFunction(const Proto* f, int full); #define PROGNAME "luac" /* default program name */ #define OUTPUT PROGNAME ".out" /* default output file */ +#define ARGS_BASE 0x40000000 /* unmapped; args.lua serves this through UnknownMemoryRead */ +#define MAXARGS 64 /* size of the argv array main() builds from ARGS_BASE */ + static int listing = 0; /* list bytecodes? */ static int dumping = 1; /* dump bytecodes? */ static int stripping = 0; /* strip debug information? */ @@ -140,7 +143,7 @@ static const Proto* combine(lua_State* L, int n) { static int writer(lua_State* L, const void* p, size_t size, void* u) { UNUSED(L); int r = PCwrite(*(int*)u, p, size); - return (r != size) && (size != 0); + return (r < 0 || (size_t)r != size) && (size != 0); } LUALIB_API int(luaL_loadfilex)(lua_State* L, const char* filename, const char* mode); @@ -174,12 +177,12 @@ static int errfile(lua_State* L, const char* what, int fnameindex) { return LUA_ERRFILE; } -#define EOF -1 +#define LUAC_EOF (-1) -static int getc(int f) { +static int luaA_getc(int f) { int c = 0; int r = PCread(f, &c, 1); - return r == 1 ? c : -1; + return r == 1 ? c : LUAC_EOF; } static int skipBOM(LoadF* lf) { @@ -187,12 +190,12 @@ static int skipBOM(LoadF* lf) { int c; lf->n = 0; do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char*)p++) return c; + c = luaA_getc(lf->f); + if (c == LUAC_EOF || c != *(const unsigned char*)p++) return c; lf->buff[lf->n++] = c; /* to be read by the parser */ } while (*p != '\0'); lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ + return luaA_getc(lf->f); /* return next character */ } /* @@ -206,9 +209,9 @@ static int skipcomment(LoadF* lf, int* cp) { int c = *cp = skipBOM(lf); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ - c = getc(lf->f); - } while (c != EOF && c != '\n'); - *cp = getc(lf->f); /* skip end-of-line, if present */ + c = luaA_getc(lf->f); + } while (c != LUAC_EOF && c != '\n'); + *cp = luaA_getc(lf->f); /* skip end-of-line, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ @@ -228,7 +231,7 @@ LUALIB_API int luaL_loadfilex(lua_State* L, const char* filename, const char* mo } if (skipcomment(&lf, &c)) /* read initial portion */ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + if (c != LUAC_EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); PCclose(lf.f); lua_remove(L, fnameindex); @@ -242,8 +245,7 @@ static int pmain(lua_State* L) { int i; if (!lua_checkstack(L, argc)) fatal("too many input files"); for (i = 0; i < argc; i++) { - const char* filename = IS("-") ? NULL : argv[i]; - if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1)); + if (luaL_loadfile(L, argv[i]) != LUA_OK) fatal(lua_tostring(L, -1)); } f = combine(L, argc); if (listing) luaU_print(f, listing > 1); @@ -261,16 +263,17 @@ static int pmain(lua_State* L) { } int main() { - const char * argv[64] = { 0 }; - const char * argsPtr = (const char *) 0x40000000; + const char* argv[MAXARGS] = {0}; + const char* argsPtr = (const char*)ARGS_BASE; int argc = 0; - while (1) { + lua_State* L; + while (argc < MAXARGS) { int len = luaA_strlen(argsPtr); if (len == 0) break; argv[argc++] = argsPtr; argsPtr += len + 1; } - lua_State* L; + if (argc == MAXARGS && luaA_strlen(argsPtr) != 0) fatal("too many arguments"); int i = doargs(argc, argv); argc -= i; if (argc <= 0) usage("no input files given"); diff --git a/src/psx-glue.s b/src/psx-glue.s index a36570b..324cd8d 100644 --- a/src/psx-glue.s +++ b/src/psx-glue.s @@ -33,14 +33,5 @@ _bss_init: _bss_init_skip: - la $a1, _mainargv j main - li $a0, 1 - - .section .rodata, "a", @progbits - .align 2 -_mainargv: - .word _progname - .word 0 -_progname: - .string "PSX.EXE" + nop From a65d7c4df3ff7c3d568055ced9d1ca1acabd83ce Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:36:41 -0700 Subject: [PATCH 4/9] Adding a psx-luac target, building luac as a ps-exe. NUGGET points at a nugget checkout rather than vendoring one, since nugget's psyqo-lua consumes psxlua and a submodule here would close the cycle. psx-heap.c supplies the allocator psxlua leaves to its embedder, over the kernel heap. --- .gitignore | 4 ++++ src/Makefile | 5 ++++- src/luac-psx.mk | 35 +++++++++++++++++++++++++++++ src/psx-heap.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/luac-psx.mk create mode 100644 src/psx-heap.c diff --git a/.gitignore b/.gitignore index 05f4578..cf493bd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ *.a luac lua +*.ps-exe +*.elf +*.map +*.dep diff --git a/src/Makefile b/src/Makefile index 50919ae..60c2e4a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,7 +53,7 @@ MYOBJS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= host32 psx psx-noparser +PLATS= host32 psx psx-noparser psx-luac ifeq ($(NOPARSER),true) LUA_A= liblua-noparser.a @@ -132,6 +132,9 @@ psx: psx-noparser: $(MAKE) a TARGET=psx NOPARSER=true +psx-luac: psx + $(MAKE) -f luac-psx.mk + # list targets that do not create files (but not all makes understand .PHONY) .PHONY: all $(PLATS) default o a clean depend echo none diff --git a/src/luac-psx.mk b/src/luac-psx.mk new file mode 100644 index 0000000..05a117c --- /dev/null +++ b/src/luac-psx.mk @@ -0,0 +1,35 @@ +# Builds luac as a ps-exe, so the compiler runs on the console (or in an +# emulator) instead of on the host. Input and output go through PCDRV, and +# argv is read from unmapped memory at 0x40000000 - see args.lua. +# +# psxlua does not depend on nugget: nugget's psyqo-lua consumes psxlua, so +# vendoring it here would close a cycle. Point NUGGET at a checkout instead. +# +# make psx-luac NUGGET=/path/to/nugget +# +# liblua.a must be built for the same target first; the psx-luac rule in +# Makefile does that for you. + +ifeq ($(strip $(NUGGET)),) +$(error NUGGET is not set - point it at a nugget checkout, e.g. make psx-luac NUGGET=../../nugget) +endif + +TARGET = luac +TYPE = ps-exe + +SRCS = \ +luac.c \ +psx-heap.c \ +psx-glue.s \ +$(NUGGET)/common/syscalls/printf.s \ +$(NUGGET)/common/crt0/memory-s.s \ +$(NUGGET)/common/crt0/memory-c.c \ + +LIBRARIES = liblua.a + +CPPFLAGS += -I. -DLUA_COMPAT_ALL + +include $(NUGGET)/common.mk + +liblua.a: + $(MAKE) -f Makefile a TARGET=psx diff --git a/src/psx-heap.c b/src/psx-heap.c new file mode 100644 index 0000000..6092aab --- /dev/null +++ b/src/psx-heap.c @@ -0,0 +1,60 @@ +/* +** Glue needed to build luac as a ps-exe. psxlua leaves the allocator to +** whoever embeds it (see luaI_realloc/luaI_free in llibc.h); an embedder with +** a heap of its own supplies these by --defsym, but luac is a standalone +** program, so it takes the kernel heap. Only luac-psx.mk compiles this file. +*/ + +#include + +#include "common/syscalls/syscalls.h" + +void* memcpy(void* dst, const void* src, size_t n); + +extern char __bss_end[]; +extern char __sp[]; + +/* Room left below the stack pointer for the parser's recursion. */ +#define STACK_RESERVE 0x10000 + +/* +** The kernel's malloc has no realloc, and psxlua's luaI_realloc does not get +** told the old size, so each block carries it. Eight bytes keeps whatever +** alignment the kernel handed back. +*/ +typedef struct { + size_t size; + size_t reserved; +} Header; + +static int heapReady; + +static void initHeap(void) { + char* base = (char*)((((unsigned)__bss_end) + 7) & ~7u); + char* top = (char*)((unsigned)__sp - STACK_RESERVE); + heapReady = 1; + syscall_userInitheap(base, (size_t)(top - base)); +} + +void luaI_free(void* ptr) { + if (ptr == NULL) return; + syscall_userFree((Header*)ptr - 1); +} + +void* luaI_realloc(void* ptr, size_t size) { + Header* block; + if (!heapReady) initHeap(); + if (size == 0) { + luaI_free(ptr); + return NULL; + } + block = (Header*)syscall_userMalloc(size + sizeof(Header)); + if (block == NULL) return NULL; + block->size = size; + if (ptr != NULL) { + Header* old = (Header*)ptr - 1; + memcpy(block + 1, ptr, old->size < size ? old->size : size); + syscall_userFree(old); + } + return block + 1; +} From 925a470d6a9f9a6810743a79a00e042b1889a56d Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:42:46 -0700 Subject: [PATCH 5/9] Dropping luac from host32, and documenting psx-luac. The ps-exe compiler is the one whose bytecode is a sure thing; a matching 32-bit host build is not available in most environments any more. --- README | 23 +++++++++++++++++++---- src/Makefile | 8 +++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/README b/README index 2d04b6a..ecf5d4f 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ further information about Lua, see doc/readme.html. This version of Lua has been modified to work on the Sony PlayStation 1. -There are now only three platforms available for compilation: +There are now only four platforms available for compilation: - `psx`: This platform is used to compile Lua for the Sony PlayStation 1. This will only generate a library file named "liblua.a". The binary footprint of this library will be roughly 110kB. @@ -16,10 +16,25 @@ There are now only three platforms available for compilation: 1 without the parser. Only precompiled bytecode can be loaded by this version. This will only generate a library file named "liblua-noparser.a". The binary footprint of this library will be roughly 85kB. +- `psx-luac`: This platform builds the Lua compiler itself as a ps-exe, named + "luac.ps-exe". Running the compiler on the target is what makes its bytecode + a sure thing: the number representation, the pointer size and the endianness + are the target's by construction rather than by a matching host build. - `host32`: This platform is used to compile Lua for a 32-bit host system. This - will generate the Lua interpreter (lua) and the lua compiler (luac). The - compiler should be able to generate bytecode that can be run on the Sony - PlayStation 1. + will generate the Lua interpreter (lua). It no longer generates the compiler; + use `psx-luac` for that, since a 32-bit host build is not available in most + environments any more. + +The `psx-luac` platform is the only part of this repository that needs nugget, +and it is not vendored: nugget's psyqo-lua consumes psxlua, so a submodule here +would close a dependency cycle. Point NUGGET at a checkout instead. + + make -C src psx-luac NUGGET=/path/to/nugget + +luac.ps-exe reads its arguments from unmapped memory at 0x40000000 and does its +file I/O over PCDRV, so it runs under an emulator that serves both. src/args.lua +is the pcsx-redux side: call createArgsBuffer() with the arguments you would +have passed on a command line, and load it alongside the executable. The following key differences exist between the original Lua 5.2.4 and this version of Lua: diff --git a/src/Makefile b/src/Makefile index 60c2e4a..da7499f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,8 +78,8 @@ LUA_O= lua.o LUAC_T= luac LUAC_O= luac.o -ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O) -ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) +ALL_O= $(BASE_O) $(LUA_O) +ALL_T= $(LUA_A) $(LUA_T) ALL_A= $(LUA_A) # Targets start here. @@ -98,11 +98,9 @@ $(LUA_A): $(BASE_O) $(LUA_T): $(LUA_O) $(LUA_A) $(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) -$(LUAC_T): $(LUAC_O) $(LUA_A) - $(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) - clean: $(RM) $(ALL_T) $(ALL_O) $(ALL_A) liblua-noparser.a lnoparser.o + $(RM) $(LUAC_T) $(LUAC_O) psx-heap.o psx-glue.o luac.elf luac.map luac.ps-exe *.dep depend: @$(CC) $(CFLAGS) -MM l*.c From 5340f45b59e578eb6d9b09bd15093f200179f0a7 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:52:55 -0700 Subject: [PATCH 6/9] Adding CI: build the library and the compiler, then compile a script on the console. tests/run-luac.sh drives luac.ps-exe under pcsx-redux and checks the bytecode header is the target's - Lua 5.2, little endian, 4-byte int, size_t, Instruction and Number, integral. The emulator comes from the appdistrib dev channel. --- .github/workflows/build.yml | 54 +++++++++++++++++++++++++++++++++++++ tests/run-luac.sh | 49 +++++++++++++++++++++++++++++++++ tests/sample.lua | 3 +++ 3 files changed, 106 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100755 tests/run-luac.sh create mode 100644 tests/sample.lua diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..8654fd6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,54 @@ +name: Build + +on: + push: + branches: [ main ] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + container: ghcr.io/grumpycoders/pcsx-redux-build:latest + + steps: + - uses: actions/checkout@v4 + + - name: Nugget + # Checked out rather than vendored: nugget's psyqo-lua consumes psxlua, + # so a submodule here would close a dependency cycle. + uses: actions/checkout@v4 + with: + repository: pcsx-redux/nugget + path: nugget + + - name: Library + run: make -C src psx -j2 + + - name: Library without the parser + run: | + make -C src clean + make -C src psx-noparser -j2 + + - name: Compiler + run: | + make -C src clean + make -C src psx-luac NUGGET=$GITHUB_WORKSPACE/nugget -j2 + + - name: pcsx-redux + # --appimage-extract rather than mounting it: the runner has no FUSE. + run: | + curl -sL -o pcsx-redux.zip https://distrib.app/pub/org/pcsx-redux/project/dev-linux-x64/latest + unzip -q pcsx-redux.zip + ./PCSX-Redux-*.AppImage --appimage-extract > /dev/null + + - name: Compile a script on the console + run: tests/run-luac.sh "$GITHUB_WORKSPACE/squashfs-root/AppRun" + + - uses: actions/upload-artifact@v4 + with: + name: luac.ps-exe + path: src/luac.ps-exe diff --git a/tests/run-luac.sh b/tests/run-luac.sh new file mode 100755 index 0000000..3995afd --- /dev/null +++ b/tests/run-luac.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# +# Compiles tests/sample.lua with luac.ps-exe under an emulator and checks the +# bytecode it produced is the target's, not the host's. +# +# make -C src psx-luac NUGGET=/path/to/nugget +# tests/run-luac.sh /path/to/pcsx-redux +# +# The emulator has to serve two things the executable depends on: PCDRV for +# file I/O, and the unmapped read at 0x40000000 that args.lua answers with the +# argument buffer. + +set -e + +EMU=${1:?usage: $0 } +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +EXE=$ROOT/src/luac.ps-exe + +if [ ! -f "$EXE" ]; then + echo "$EXE is missing; build it with make -C src psx-luac NUGGET=..." >&2 + exit 1 +fi + +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT + +cp "$EXE" "$ROOT/tests/sample.lua" "$ROOT/src/args.lua" "$WORK/" +echo "createArgsBuffer('-o', 'sample.luac', 'sample.lua')" >> "$WORK/args.lua" + +"$EMU" -testmode -run -stdout -lua_stdout -pcdrv -pcdrvbase "$WORK" \ + -dofile "$WORK/args.lua" -loadexe "$WORK/luac.ps-exe" + +if [ ! -s "$WORK/sample.luac" ]; then + echo "luac.ps-exe produced no bytecode" >&2 + exit 1 +fi + +# Lua 5.2 signature, little endian, then sizeof int, size_t, Instruction and +# lua_Number, then the integral-number flag. Compiling on the target is what +# makes these the target's by construction. +EXPECTED=1b4c75615200010404040401 +GOT=$(od -An -tx1 -N12 "$WORK/sample.luac" | tr -d ' \n') + +if [ "$GOT" != "$EXPECTED" ]; then + echo "bytecode header is $GOT, expected $EXPECTED" >&2 + exit 1 +fi + +echo "sample.luac: $(wc -c < "$WORK/sample.luac") bytes, header $GOT" diff --git a/tests/sample.lua b/tests/sample.lua new file mode 100644 index 0000000..1b71f3e --- /dev/null +++ b/tests/sample.lua @@ -0,0 +1,3 @@ +local squares = {} +for i = 1, 10 do squares[i] = i * i end +return squares From 47069db43e91406342759991ff011c16cc5a84c0 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:54:03 -0700 Subject: [PATCH 7/9] Running CI on every branch, not just main. --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8654fd6..fca2f25 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,6 @@ name: Build on: push: - branches: [ main ] pull_request: concurrency: From 8e59014b8e94e362810c68be45fed553c6280761 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 21:55:51 -0700 Subject: [PATCH 8/9] Running the console test under xvfb. pcsx-redux initialises SDL video even in -testmode, so a bare runner has no video device. --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fca2f25..8d3678b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,9 @@ jobs: ./PCSX-Redux-*.AppImage --appimage-extract > /dev/null - name: Compile a script on the console - run: tests/run-luac.sh "$GITHUB_WORKSPACE/squashfs-root/AppRun" + # pcsx-redux brings SDL video up even under -testmode, which is why + # pcsx-redux's own CI runs its tests under xvfb-run. + run: xvfb-run tests/run-luac.sh "$GITHUB_WORKSPACE/squashfs-root/AppRun" - uses: actions/upload-artifact@v4 with: From 93102f9d9fcb29c748cfd0ee57552e527e143794 Mon Sep 17 00:00:00 2001 From: Rix Date: Sun, 20 Sep 2026 22:03:20 -0700 Subject: [PATCH 9/9] Running the emulator with -no-ui and under a timeout. A run that never reaches pcsx_exit held a CI job open for six minutes before it was cancelled. --- tests/run-luac.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/run-luac.sh b/tests/run-luac.sh index 3995afd..4af1c30 100755 --- a/tests/run-luac.sh +++ b/tests/run-luac.sh @@ -27,8 +27,11 @@ trap 'rm -rf "$WORK"' EXIT cp "$EXE" "$ROOT/tests/sample.lua" "$ROOT/src/args.lua" "$WORK/" echo "createArgsBuffer('-o', 'sample.luac', 'sample.lua')" >> "$WORK/args.lua" -"$EMU" -testmode -run -stdout -lua_stdout -pcdrv -pcdrvbase "$WORK" \ - -dofile "$WORK/args.lua" -loadexe "$WORK/luac.ps-exe" +# -no-ui keeps it off a display, and the timeout is there because an emulator +# that never reaches pcsx_exit would otherwise hold a CI job open for hours. +timeout 120 "$EMU" -no-ui -testmode -run -stdout -lua_stdout \ + -pcdrv -pcdrvbase "$WORK" \ + -dofile "$WORK/args.lua" -loadexe "$WORK/luac.ps-exe" if [ ! -s "$WORK/sample.luac" ]; then echo "luac.ps-exe produced no bytecode" >&2