diff options
| author | Xavi Del Campo <xavi.dcr@tutanota.com> | 2020-01-31 10:32:23 +0100 |
|---|---|---|
| committer | Xavi Del Campo <xavi.dcr@tutanota.com> | 2020-01-31 10:32:23 +0100 |
| commit | 7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch) | |
| tree | c28d0748652ad4b4222309e46e6cfc82c0906220 /tools | |
| parent | a2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff) | |
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'tools')
35 files changed, 8156 insertions, 0 deletions
diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..15294ff --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,77 @@ +# Makefile for the PSXSDK tools + +include ../Makefile.cfg + +TOOL_LIST = bmp2tim$(EXE_SUFFIX) \ + cdcat$(EXE_SUFFIX) \ + elf2exe$(EXE_SUFFIX) \ + getpsxiso$(EXE_SUFFIX) \ + mkpsxiso$(EXE_SUFFIX) \ + vag2wav$(EXE_SUFFIX) \ + wav2vag$(EXE_SUFFIX) \ + exefixup$(EXE_SUFFIX) \ + systemcnf$(EXE_SUFFIX) \ + bin2c$(EXE_SUFFIX) \ + huff$(EXE_SUFFIX) \ + mod4psx$(EXE_SUFFIX) \ + tim2bmp$(EXE_SUFFIX) \ + lictool$(EXE_SUFFIX) \ + psfex$(EXE_SUFFIX) + +all: $(TOOL_LIST) + $(MAKE_COMMAND) -C spasm + +bmp2tim$(EXE_SUFFIX): bmp2tim.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ bmp2tim.c $(HOST_LDFLAGS) + +cdcat$(EXE_SUFFIX): cdcat.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ cdcat.c $(HOST_LDFLAGS) + +elf2exe$(EXE_SUFFIX): elf2exe.c + $(HOST_CC) $(HOST_CFLAGS) -DOBJCOPY_PATH=\"$(OBJCOPY)\" -o $@ elf2exe.c $(HOST_LDFLAGS) + +getpsxiso$(EXE_SUFFIX): getpsxiso.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ getpsxiso.c $(HOST_LDFLAGS) + +mkpsxiso$(EXE_SUFFIX): mkpsxiso.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ mkpsxiso.c $(HOST_LDFLAGS) + +vag2wav$(EXE_SUFFIX): vag2wav.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ vag2wav.c $(HOST_LDFLAGS) + +wav2vag$(EXE_SUFFIX): wav2vag.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ wav2vag.c $(HOST_LDFLAGS) + +exefixup$(EXE_SUFFIX): exefixup.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ exefixup.c $(HOST_LDFLAGS) + +systemcnf$(EXE_SUFFIX): systemcnf.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ systemcnf.c $(HOST_LDFLAGS) + +bin2c$(EXE_SUFFIX): bin2c.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ bin2c.c $(HOST_LDFLAGS) + +huff$(EXE_SUFFIX): huff.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ huff.c $(HOST_LDFLAGS) + +mod4psx$(EXE_SUFFIX): mod4psx.c adpcm.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ mod4psx.c adpcm.c ../libmodplay/libmodplay_nopsx.a -lm -DNO_PSX_LIB + +tim2bmp$(EXE_SUFFIX): tim2bmp.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ tim2bmp.c -lz $(HOST_LDFLAGS) + +lictool$(EXE_SUFFIX): lictool.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ lictool.c $(HOST_LDFLAGS) + +psfex$(EXE_SUFFIX): psfex.c + $(HOST_CC) $(HOST_CFLAGS) -o $@ psfex.c -lz $(HOST_LDFLAGS) + +clean: + rm -f $(TOOL_LIST) + $(MAKE_COMMAND) -C spasm clean + +distclean: clean + +install: + cp -rv $(TOOL_LIST) $(TOOLCHAIN_PREFIX)/bin + $(MAKE_COMMAND) -C spasm install diff --git a/tools/adpcm.c b/tools/adpcm.c new file mode 100644 index 0000000..1ace061 --- /dev/null +++ b/tools/adpcm.c @@ -0,0 +1,259 @@ +/*
+ * PCM -> ADPCM routines
+ *
+ * based on work by Bitmaster and extended
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include "adpcm.h"
+
+#define PCM_BUFFER_SIZE 128*28
+
+short pcm_buffer[PCM_BUFFER_SIZE];
+
+void SsAdpcm_find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor);
+void SsAdpcm_pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor);
+
+int SsAdpcmPack(void *pcm_data, void *adpcm_data, int sample_len,
+ int sample_fmt, int adpcm_len, int enable_looping,
+ int loop_start)
+{
+ short *ptr;
+ unsigned char *pcm_data_c = pcm_data;
+ short *pcm_data_s = pcm_data;
+ unsigned char *adpcm_data_c = adpcm_data;
+ // short *adpcm_data_s = adpcm_data;
+ double d_samples[28];
+ short four_bit[28];
+ int predict_nr;
+ int shift_factor;
+ int flags;
+ int size;
+ int i, j, k;
+ unsigned char d;
+ // char s[4];
+ // int chunk_data;
+ // short e;
+ // short sample_size;
+ //unsigned char c;
+ int ap = 0;
+ int sp = 0;
+
+/*printf("pcm_data = %x, adpcm_data = %x, len = %x, fmt = %x, alen = %x,"
+ "loop = %x\n", pcm_data, adpcm_data, sample_len, sample_fmt,
+ adpcm_len, enable_looping);
+ for(i=0;i<8;i++)
+ printf("I[%d] = %x\n", i, pcm_data_c[i]);*/
+
+ //if(enable_looping)
+// flags = 6;
+// else
+ flags = 0;
+
+while( sample_len > 0 ) {
+ size = ( sample_len >= PCM_BUFFER_SIZE ) ? PCM_BUFFER_SIZE : sample_len;
+
+ if(sample_fmt == FMT_U8)
+ {
+ for(i = 0; i < size; i++)
+ {
+ //c = fgetc(fp);
+ pcm_buffer[i] = *(pcm_data_c++);
+ pcm_buffer[i] ^= 0x80;
+ pcm_buffer[i] <<= 8;
+ }
+ }
+ else if(sample_fmt == FMT_S16)
+ {
+ //fread( wave, sizeof( short ), size, fp );
+ memcpy(pcm_buffer, pcm_data_s, size * sizeof(short));
+ pcm_data_s += size;
+ }
+ else
+ {
+ printf("%s, line %d: Unknown source sample format!, id=%d\n",__FUNCTION__,__LINE__,sample_fmt);
+ return 0;
+ }
+
+ i = size / 28;
+ if ( size % 28 ) {
+ for ( j = size % 28; j < 28; j++ )
+ pcm_buffer[28*i+j] = 0;
+ i++;
+ }
+
+ for ( j = 0; j < i; j++ ) { // pack 28 samples
+ if ( sample_len < 28 && enable_looping == 0)
+ flags = 1;
+
+ if(enable_looping)
+ {
+ if(((loop_start/28)*28) == sp)
+ flags = 6;
+ else
+ flags = 2;
+ }
+
+
+ ptr = pcm_buffer + j * 28;
+ SsAdpcm_find_predict( ptr, d_samples, &predict_nr, &shift_factor );
+ SsAdpcm_pack( d_samples, four_bit, predict_nr, shift_factor );
+ d = ( predict_nr << 4 ) | shift_factor;
+ // fputc( d, vag );
+ adpcm_data_c[ap++] = d;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ // fputc( flags, vag );
+ adpcm_data_c[ap++] = flags;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ for ( k = 0; k < 28; k += 2 ) {
+ d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf );
+ // fputc( d, vag );
+ adpcm_data_c[ap++] = d;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ }
+ sample_len -= 28;
+ sp += 28;
+ }
+ }
+
+ // fputc( ( predict_nr << 4 ) | shift_factor, vag );
+ adpcm_data_c[ap++] = ( predict_nr << 4 ) | shift_factor;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ if(enable_looping)
+ // fputc(3, vag);
+ adpcm_data_c[ap++] = 3;
+ else
+// fputc( 7, vag ); // end flag
+ adpcm_data_c[ap++] = 7;
+
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ for ( i = 0; i < 14; i++ )
+ // fputc( 0, vag );
+ adpcm_data_c[ap++] = 0;
+
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ return ap;
+
+adpcm_too_big:
+ printf("%s: Resulting ADPCM data would have been larger than the output array length! Exiting %s.\n", __FUNCTION__, __FUNCTION__);
+ return 0;
+}
+
+
+
+
+
+
+void SsAdpcm_find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor )
+{
+ int i, j;
+ double buffer[28][5];
+ double min = 1e10;
+ double max[5];
+ double ds;
+ int min2;
+ int shift_mask;
+ static double _s_1 = 0.0; // s[t-1]
+ static double _s_2 = 0.0; // s[t-2]
+ double s_0, s_1, s_2;
+ double f[5][2] = { { 0.0, 0.0 },
+ { -60.0 / 64.0, 0.0 },
+ { -115.0 / 64.0, 52.0 / 64.0 },
+ { -98.0 / 64.0, 55.0 / 64.0 },
+ { -122.0 / 64.0, 60.0 / 64.0 } };
+
+ for ( i = 0; i < 5; i++ ) {
+ max[i] = 0.0;
+ s_1 = _s_1;
+ s_2 = _s_2;
+ for ( j = 0; j < 28; j ++ ) {
+ s_0 = (double) samples[j]; // s[t-0]
+ if ( s_0 > 30719.0 )
+ s_0 = 30719.0;
+ if ( s_0 < - 30720.0 )
+ s_0 = -30720.0;
+ ds = s_0 + s_1 * f[i][0] + s_2 * f[i][1];
+ buffer[j][i] = ds;
+ if ( fabs( ds ) > max[i] )
+ max[i] = fabs( ds );
+// printf( "%+5.2f\n", s2 );
+ s_2 = s_1; // new s[t-2]
+ s_1 = s_0; // new s[t-1]
+ }
+
+ if ( max[i] < min ) {
+ min = max[i];
+ *predict_nr = i;
+ }
+ if ( min <= 7 ) {
+ *predict_nr = 0;
+ break;
+ }
+
+ }
+
+// store s[t-2] and s[t-1] in a static variable
+// these than used in the next function call
+
+ _s_1 = s_1;
+ _s_2 = s_2;
+
+ for ( i = 0; i < 28; i++ )
+ d_samples[i] = buffer[i][*predict_nr];
+
+// if ( min > 32767.0 )
+// min = 32767.0;
+
+ min2 = ( int ) min;
+ shift_mask = 0x4000;
+ *shift_factor = 0;
+
+ while( *shift_factor < 12 ) {
+ if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) )
+ break;
+ (*shift_factor)++;
+ shift_mask = shift_mask >> 1;
+ }
+
+}
+
+void SsAdpcm_pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor )
+{
+ static double f[5][2] = { { 0.0, 0.0 },
+ { -60.0 / 64.0, 0.0 },
+ { -115.0 / 64.0, 52.0 / 64.0 },
+ { -98.0 / 64.0, 55.0 / 64.0 },
+ { -122.0 / 64.0, 60.0 / 64.0 } };
+ double ds;
+ int di;
+ double s_0;
+ static double s_1 = 0.0;
+ static double s_2 = 0.0;
+ int i;
+
+ for ( i = 0; i < 28; i++ ) {
+ s_0 = d_samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
+ ds = s_0 * (double) ( 1 << shift_factor );
+
+ di = ( (int) ds + 0x800 ) & 0xfffff000;
+
+ if ( di > 32767 )
+ di = 32767;
+ if ( di < -32768 )
+ di = -32768;
+
+ four_bit[i] = (short) di;
+
+ di = di >> shift_factor;
+ s_2 = s_1;
+ s_1 = (double) di - s_0;
+
+ }
+}
diff --git a/tools/adpcm.h b/tools/adpcm.h new file mode 100644 index 0000000..7dcee2e --- /dev/null +++ b/tools/adpcm.h @@ -0,0 +1,14 @@ +#ifndef _PSX_ADPCM_H
+#define _PSX_ADPCM_H
+
+enum
+{
+ FMT_U8, // unsigned 8-bit
+ FMT_S16, // signed 16-bit
+};
+
+int SsAdpcmPack(void *pcm_data, void *adpcm_data, int sample_len,
+ int sample_fmt, int adpcm_len, int enable_looping,
+ int loop_start);
+
+#endif
diff --git a/tools/bin2c.c b/tools/bin2c.c new file mode 100644 index 0000000..a40e431 --- /dev/null +++ b/tools/bin2c.c @@ -0,0 +1,30 @@ +/* + * bin2c: converts a file to a C array of bytes + */ + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + char *name = "data"; + int n = 0; + int c; + + if(argc >= 2) + name = argv[1]; + + printf("unsigned char %s_array[] =\n{\n", name); + + while((c=getchar()) != EOF) + { + printf("%d, ", c); + n++; + + if(!(n & 15)) + printf("\n"); + } + + printf("};\n"); + + return 0; +} diff --git a/tools/bmp2tim.c b/tools/bmp2tim.c new file mode 100755 index 0000000..12558b7 --- /dev/null +++ b/tools/bmp2tim.c @@ -0,0 +1,895 @@ +/* + * bmp2tim + * + * Converts a bitmap to a TIM image + * + * TEST output in various color depths... and check for issues on big-endian machines + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define BMP2TIM_VERSION "0.5" + +enum +{ + BITMAPINFOHEADER, + BITMAPV2INFOHEADER, + BITMAPV3INFOHEADER, + BITMAPV4HEADER, + BITMAPV5HEADER, +}; + +typedef struct +{ + unsigned char r, g, b; +}PS_RGB; + +typedef struct +{ + int w, h; + int depth; + // 0 = r, 1 = g, 2 = b, 3 = alpha + unsigned int mask[4]; + unsigned int shift[4]; + unsigned int bits[4]; + unsigned char hdr_type; + void *data; +}PS_BITMAP; + +int do_clut = 0; +unsigned int clut_x, clut_y; + +unsigned int org_x = 0; +unsigned int org_y = 0; + +int tim_depth; + +unsigned int tim_flag; + +int set_stp_bit = 0; + +int transparent_black = 0; +int magic_pink = 0; +int raw_flag = 0; + +PS_RGB *ps_default_palette; + +unsigned short read_le_word(FILE *f) +{ + unsigned char c; + unsigned short i; + + fread(&c, sizeof(char), 1, f); + i = c; + fread(&c, sizeof(char), 1, f); + i|=(c<<8); + + return i; +} + +unsigned int read_le_dword(FILE *f) +{ + unsigned char c; + unsigned int i; + + fread(&c, sizeof(char), 1, f); + i = c; + fread(&c, sizeof(char), 1, f); + i|=(c<<8); + fread(&c, sizeof(char), 1, f); + i|=(c<<16); + fread(&c, sizeof(char), 1, f); + i|=(c<<24); + + return i; +} + + +void write_le_word(FILE *f, unsigned short leword) +{ + unsigned char c; + + c = leword & 0xff; + fwrite(&c, sizeof(char), 1, f); + c = leword >> 8; + fwrite(&c, sizeof(char), 1, f); +} + +void write_le_dword(FILE *f, unsigned int ledword) +{ + unsigned char c; + int x; + + for(x = 0; x < 4; x++) + { + c = (ledword >> (x<<3)) & 0xff; + fwrite(&c, sizeof(char), 1, f); + } +} + +PS_BITMAP *ps_create_bitmap(int w, int h, int depth) +{ + PS_BITMAP *bm; + + bm = malloc(sizeof(PS_BITMAP)); + + bm->w = w; + bm->h = h; + bm->depth = depth; + + switch(depth) + { + case 1: + bm->data = malloc((w*h)/8); + break; + case 4: + bm->data = malloc((w*h)/2); + break; + case 8: + bm->data = malloc(w*h); + break; + case 15: + case 16: + bm->data = malloc((w*h)*2); + break; + case 24: + bm->data = malloc((w*h)*3); + break; + case 32: + bm->data = malloc((w*h)*4); + break; + } + + return bm; +} + +void ps_destroy_bitmap(PS_BITMAP *bm) +{ + free(bm->data); + free(bm); +} + + +PS_BITMAP *ps_load_bitmap(char *filename, PS_RGB *palette) +{ + FILE *bf; + //unsigned int bsize; + unsigned int bisize; + unsigned int bwidth, bheight, bbpp, boff, blw; +// unsigned int bcompr; + unsigned char *bdata; + PS_BITMAP *bm; + int x, y, z, i, l; + + bf = fopen(filename, "rb"); + + if(bf == NULL) + return NULL; + + if(read_le_word(bf) != 19778) // 'BM' + { + fclose(bf); + return NULL; + } + + /* bsize = */ read_le_dword(bf); + +// Read bitmap data offset + fseek(bf, 10, SEEK_SET); + boff = read_le_dword(bf); + +// printf("BOFF = %d\n", boff); + +// Read information header size, width and height + + bisize = read_le_dword(bf); + + + + bwidth = read_le_dword(bf); + bheight = read_le_dword(bf); + +// printf("bwidth = %d, bheight = %d\n", bwidth, bheight); + +// Read BPP + + fseek(bf, 28, SEEK_SET); + + bbpp = read_le_word(bf); + +// Check if there is compression, if there is, abort + + /* bcompr = */ read_le_dword(bf); +// printf("BCOMPR = %d\n", bcompr); + + bm = ps_create_bitmap(bwidth, bheight, bbpp); + + if(palette != NULL && bm->depth <= 8) + { + fseek(bf, 14 + bisize, SEEK_SET); + + if(bm->depth == 4) l = 16; + else if(bm->depth == 8) l = 256; + else if(bm->depth == 1) l = 2; + + for(x=0;x<l;x++) + { + palette[x].b = fgetc(bf); + palette[x].g = fgetc(bf); + palette[x].r = fgetc(bf); + fgetc(bf); + } + } + +// nextvolume FIX 2011-07-08: Now blw (line width with padding) and bwidth +// (line width without padding) are calculated in a much cleaner and correct manner. + +// printf("BPP = %d\n", bbpp); + + bwidth = (bwidth * bbpp) >> 3; + blw = bwidth; + if(blw & 3) blw = (blw & ~3) + 4; + + bdata = (unsigned char*)bm->data; + +// Bit mask and colour stuff... Added 2011-07-09 + + switch(bisize) + { + case 40: bm->hdr_type = BITMAPINFOHEADER; break; + case 52: bm->hdr_type = BITMAPV2INFOHEADER; break; + case 56: bm->hdr_type = BITMAPV3INFOHEADER; break; + case 108: bm->hdr_type = BITMAPV4HEADER; break; + case 124: bm->hdr_type = BITMAPV5HEADER; break; + } + +// For now clear Alpha, it will be filled only if it will be found + + bm->mask[3] = 0; + bm->shift[3] = 0; + bm->bits[3] = 0; + + if(bm->hdr_type == BITMAPINFOHEADER && bbpp == 16) + { + // Old header type and no bitmasks specified - force defaults + // X1 R5 G5 B5 + bm->mask[2] = 0x1f; + bm->mask[1] = 0x1f << 5; + bm->mask[0] = 0x1f << 10; + bm->shift[2] = 0; + bm->shift[1] = 5; + bm->shift[0] = 10; + bm->bits[2] = 5; + bm->bits[1] = 5; + bm->bits[0] = 5; + } + else if(bm->hdr_type >= BITMAPV2INFOHEADER) + { + fseek(bf, 54, SEEK_SET); + +// Calculate rshift and rbits + + if(bm->hdr_type >= BITMAPV3INFOHEADER) + l = 4; + else + l = 3; + + for(i = 0; i < l; i++) + { + bm->mask[i] = read_le_dword(bf); + + y = 0; // rshift + z = 0; // rbits + + for(x = 31; x >= 0; x--) + { + if(bm->mask[i] & (1<<x)) + { + y = x; + z++; + } + } + + bm->shift[i] = y; + bm->bits[i] = z; + + //printf("shift[%d] = %d, bits[%d] = %d\n", i, bm->shift[i], + // i, bm->bits[i]); + } + } + +// Copy data in allocated memory + + for(y = 0; y < bm->h; y++) + { + fseek(bf, boff + (blw * (bm->h - (1+y))), SEEK_SET); + + for(x = 0; x < bwidth; x++) + fread(&bdata[(y*bwidth)+x], sizeof(char), 1, bf); + } + + fclose(bf); + + return bm; +} + +unsigned int ps_makecol(int r, int g, int b, int a) +{ + return (a<<24)|(r<<16)|(g<<8)|b; +} + +unsigned int ps_getr(int c) +{ + return (c>>16)&0xff; +} + +unsigned int ps_getg(int c) +{ + return (c>>8)&0xff; +} + +unsigned int ps_getb(int c) +{ + return c&0xff; +} + +unsigned int ps_geta(int c) +{ + return (c>>24)&0xff; +} + +unsigned int ps_getpixel(PS_BITMAP *bm, int x, int y) +{ + unsigned short shortbuf; + unsigned int intbuf; + unsigned char r, g, b, a; + unsigned char *dataptrb = (unsigned char*)bm->data; + int off; + + if(bm->depth == 16) + { + off = ((y*bm->w)+x)*2; + + // Little endian, guys... + + shortbuf = dataptrb[off]; + shortbuf|= dataptrb[off+1]<<8; + + b = ((shortbuf & bm->mask[2]) >> bm->shift[2]) << (8-bm->bits[2]); + g = ((shortbuf & bm->mask[1]) >> bm->shift[1]) << (8-bm->bits[1]); + r = ((shortbuf & bm->mask[0]) >> bm->shift[0]) << (8-bm->bits[0]); + a = ((shortbuf & bm->mask[3]) >> bm->shift[3]) << (8-bm->bits[3]); + + return ps_makecol(r, g, b, a); + } + else if(bm->depth == 24) + { + // 24-bit bitmaps never have bitmasks. + + off = ((y*bm->w)+x)*3; + r = dataptrb[off+2]; + g = dataptrb[off+1]; + b = dataptrb[off]; + a = 255; + + return ps_makecol(r, g, b, 255); + } + else if(bm->depth == 32) + { + off = ((y*bm->w)+x)*4; + /* r = dataptrb[off+3]; + g = dataptrb[off+2]; + b = dataptrb[off+1];*/ + intbuf = dataptrb[off]; + intbuf|= dataptrb[off+1]<<8; + intbuf|= dataptrb[off+2]<<16; + intbuf|= dataptrb[off+3]<<24; + + r = ((intbuf & bm->mask[0]) >> bm->shift[0]) << (8-bm->bits[0]); + g = ((intbuf & bm->mask[1]) >> bm->shift[1]) << (8-bm->bits[1]); + b = ((intbuf & bm->mask[2]) >> bm->shift[2]) << (8-bm->bits[2]); + a = ((intbuf & bm->mask[3]) >> bm->shift[3]) << (8-bm->bits[3]); + + return ps_makecol(r, g, b, a); + } + else if(bm->depth == 8) + { + r = dataptrb[(y*bm->w)+x]; + + return ps_makecol(ps_default_palette[r].r, + ps_default_palette[r].g, ps_default_palette[r].b, 255); + } + else if(bm->depth == 4) + { + off = (y*bm->w)+x; + off/= 2; + + if(x & 1) + r = dataptrb[off] & 0xf; + else + r = dataptrb[off] >> 4; + + return ps_makecol(ps_default_palette[r].r, + ps_default_palette[r].g, ps_default_palette[r].b, 255); + } + else if(bm->depth == 1) + { + off = (y*bm->w)+x; + off/= 8; + + r = (dataptrb[off] & (1<<(7-(x&7)))) ? 1 : 0; + + return ps_makecol(ps_default_palette[r].r, + ps_default_palette[r].g, ps_default_palette[r].b, 255); + } + + return 0; +} + +unsigned int ps_getpixel_pal(PS_BITMAP *bm, int x, int y) +{ + unsigned char *dataptrb = (unsigned char*)bm->data; + int off; + + if(bm->depth == 8) + { + return dataptrb[(y*bm->w)+x]; + } + else if(bm->depth == 4) + { + off = (y*bm->w)+x; + off/= 2; + + if(x & 1) + return dataptrb[off] & 0xf; + else + return dataptrb[off] >> 4; + } + else if(bm->depth == 1) + { + off = (y*bm->w)+x; + off/= 8; + + return (dataptrb[off] & (1<<(7-(x&7)))) ? 1 : 0; + } + + return 0; +} + +void parse_options(int argc, char *argv[]) +{ + int x; + + for(x=4;x<argc;x++) + { + if(strncmp("-clut=", argv[x], 6) == 0) + { + sscanf(argv[x], "-clut=%d,%d", &clut_x, &clut_y); + do_clut = 1; + }else if(strncmp("-org=", argv[x], 5) == 0) + sscanf(argv[x], "-org=%d,%d", &org_x, &org_y); + else if(strcmp("-stp", argv[x]) == 0) + set_stp_bit = 1; + else if(strcmp("-noblack", argv[x]) == 0) + transparent_black = 1; + else if(strcmp("-mpink", argv[x]) == 0) + magic_pink = 1; + else if(strcmp("-raw", argv[x]) == 0) + raw_flag = 1; + } +} + +unsigned short rgb24_to_rgbpsx(unsigned char r, unsigned char g, unsigned char b) +{ + unsigned short c; + + c = r>>3; + c|= (g>>3)<<5; + c|= (b>>3)<<10; + + /*if(set_stp_bit) c|=0x8000;*/ +// this code is a bit messy, tidy it up. + + if(c == 0 && !transparent_black) + c|=0x8000; + + if(c == ((31)|(31<<10)) && magic_pink) + c=0; + + if(set_stp_bit) + { + if(transparent_black && c == 0) + return c; + + if(magic_pink && c == ((31)|(31<<10))) + return c; + + c|=0x8000; + } + + return c; +} + +int main(int argc, char *argv[]) +{ + PS_BITMAP *in_bitmap; + FILE *out_tim; + PS_RGB in_palette[256]; + int x, y, z, c, c2; + unsigned short shortbuf; + int cx_out = 0; + + ps_default_palette = in_palette; + + for(x=1;x<argc;x++) + { + if(strcmp("-version", argv[x]) == 0) + { + printf("bmp2tim version "BMP2TIM_VERSION"\n"); + return EXIT_SUCCESS; + } + } + + if(argc < 4) + { + printf("bmp2tim "BMP2TIM_VERSION" - converts a bitmap to a TIM image\n"); + printf("usage: bmp2tim <inbmp> <outtim> <depth> [options]\n\n"); + printf("Options:\n"); + printf(" -clut=x,y - Generate a Color Look Up Table (default: OFF)\n"); + printf(" -org=x,y - Set image origin in framebuffer (default: 0, 0)\n"); + printf(" -stp - Set semi transparency bit (default: BLACK ONLY)\n"); + printf(" -noblack - Make black transparent (default: OFF)\n"); + printf(" -mpink - Magic pink, 255,0,255 transparent (default: OFF)\n"); + printf(" -raw - Do not save header and CLUT (default: OFF)\n"); + printf(" -version - Print program version on screen\n\n"); + printf("Valid TIM depths are 4 (16-color), 8 (256-color), 16 (RGB555) and 24 (RGB888)\n"); + return EXIT_SUCCESS; + } + + tim_depth = atoi(argv[3]); + + parse_options(argc, argv); + + if(do_clut && tim_depth >= 16) + { + printf("Images with depths higher than 8-bit can't have a color look up table.\n"); + return EXIT_FAILURE; + } + + if(clut_x & 0xf) + { + printf("The X position of the CLUT in the framebuffer must be a multiplier of 16.\n"); + return EXIT_FAILURE; + } + + switch(tim_depth) + { + case 4: + if(clut_x > (1024-16)) + cx_out = 1; + break; + case 8: + if(clut_x > (1024-256)) + cx_out = 1; + break; + } + + if(cx_out) + { + printf("X position specified for CLUT out of bounds.\n"); + return EXIT_FAILURE; + } + + if(clut_y >= 512) + { + printf("Y position specified for CLUT out of bounds.\n"); + return EXIT_FAILURE; + } + + if(do_clut) + printf("Generating a Color Look Up Table (CLUT)\n"); + + if(tim_depth != 4 && tim_depth != 8 && tim_depth != 16 && tim_depth != 24) + { + printf("Invalid color depth specified!\n"); + return EXIT_FAILURE; + } + + in_bitmap = ps_load_bitmap(argv[1], in_palette); + + if(in_bitmap == NULL) + { + printf("Unable to load bitmap. Unsupported format or file is unreadable or does not exist.\n"); + return EXIT_FAILURE; + } + + if(tim_depth == 4 && in_bitmap->depth > 4) + { + printf("Error: Only a 4-bit bitmap or a bitmap of lower depth can be used to obtain a 4-bit TIM!\n"); + return EXIT_FAILURE; + } + + if(tim_depth == 8 && in_bitmap->depth > 8) + { + printf("Error: Only a 8-bit or a bitmap of lower depth can be used to obtain a 8-bit TIM!\n"); + return EXIT_FAILURE; + } + +/* allegro_init(); + set_color_depth(32); + install_keyboard(); + set_gfx_mode(GFX_AUTODETECT_WINDOWED, in_bitmap->w, in_bitmap->h, 0, 0); + + for(y=0;y<in_bitmap->h;y++) + { + for(x=0;x<in_bitmap->w;x++) + { + c = ps_getpixel_pal(in_bitmap, x, y); + + //putpixel(screen, x, y, makecol(ps_getr(c), ps_getg(c), ps_getb(c))); + putpixel(screen, x, y, makecol(in_palette[c].r, in_palette[c].g, + in_palette[c].b)); + } + } + + while(!key[KEY_ESC]);*/ + + if(in_bitmap == NULL) + { + printf("Could not open bitmap. Aborting.\n"); + return EXIT_FAILURE; + } + + switch(tim_depth) + { + case 4: + if(in_bitmap->w & 3) + { + printf("Error: A 4-bit bitmap must have a width divisible by four.\n"); + return EXIT_FAILURE; + } + + z = in_bitmap->w/4; + break; + case 8: + if(in_bitmap->w & 1) + { + printf("Error: A 8-bit bitmap must have a width divisible by two.\n"); + return EXIT_FAILURE; + } + + z = in_bitmap->w/2; + break; + case 16: + z = in_bitmap->w; + break; + } + + if((org_x+z) > 1024) + { + printf("X position specified for image data out of bounds.\n"); + return EXIT_FAILURE; + } + + switch(tim_depth) + { + case 4: + z = in_bitmap->h/4; + break; + case 8: + z = in_bitmap->h/2; + break; + case 16: + z = in_bitmap->h; + break; + } + + if((org_y+z) > 512) + { + printf("Y position specified for image data out of bounds.\n"); + return EXIT_FAILURE; + } + + out_tim = fopen(argv[2], "wb"); + + if(out_tim == NULL) + { + printf("Couldn't open file at path %s for writing. Aborting.\n", argv[2]); + return EXIT_FAILURE; + } + + if(!raw_flag) + { + + write_le_dword(out_tim, 0x10); /* ID = 0x10, Version = 0x00 */ + + /* + * Now let's fill the TIM flag double word + */ + + /* + * Pixel mode (PMODE) + */ + + switch(tim_depth) + { + case 4: + tim_flag = 0; + break; + case 8: + tim_flag = 1; + break; + case 16: + tim_flag = 2; + break; + case 24: + tim_flag = 3; + break; + } + + /* + * Clut flag (CF) + */ + //tim_flag|=8; + if(do_clut)tim_flag|=8; + + write_le_dword(out_tim, tim_flag); + + /* + * If we have to write a CLUT now, we have to write its data block + */ + + if(do_clut) + { + /* + * Let's write the information for the block - we already know + * everything about it. + */ + + switch(tim_depth) + { + case 4: + write_le_dword(out_tim, 44); // Number of bytes contained by the block + write_le_dword(out_tim, (clut_y<<16)|clut_x); // X, Y position + write_le_dword(out_tim, (1<<16)|16); // Width = 16, Height = 1 + break; + case 8: + write_le_dword(out_tim, 524); + write_le_dword(out_tim, (clut_y<<16)|clut_x); + write_le_dword(out_tim, (1<<16)|256); // Width = 256, Height = 1 + break; + } + + /* + * Let's write the CLUT data + */ + + switch(tim_depth) + { + case 4: + for(x = 0; x < 16; x++) + { + shortbuf = rgb24_to_rgbpsx(in_palette[x].r, in_palette[x].g, + in_palette[x].b); + + write_le_word(out_tim, shortbuf); + } + break; + case 8: + for(x = 0; x < 256; x++) + { + shortbuf = rgb24_to_rgbpsx(in_palette[x].r, in_palette[x].g, + in_palette[x].b); + + write_le_word(out_tim, shortbuf); + } + break; + } + } + + /* + * Write image data block + */ + + switch(tim_depth) + { + case 4: + x = 12 + ((in_bitmap->w * in_bitmap->h) / 2); + break; + case 8: + x = 12 + (in_bitmap->w * in_bitmap->h); + break; + case 16: + x = 12 + ((in_bitmap->w * in_bitmap->h) * 2); + break; + case 24: + x = 12 + ((in_bitmap->w * in_bitmap->h) * 3); + break; + } + + write_le_dword(out_tim, x); + write_le_dword(out_tim, (org_y<<16)|org_x); + + switch(tim_depth) + { + case 4: + write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w/4)); + break; + case 8: + write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w/2)); + break; + case 16: + write_le_dword(out_tim, (in_bitmap->h<<16)|in_bitmap->w); + break; + case 24: + write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w+ + (in_bitmap->w/2))); + break; + } + + } + +// Write image pixel data... + + switch(tim_depth) + { + case 24: + for(y = 0; y < in_bitmap->h; y++) + { + for(x = 0; x < in_bitmap->w; x+=2) + { + c = ps_getpixel(in_bitmap, x, y); + c2 = ps_getpixel(in_bitmap, x+1, y); + + write_le_word(out_tim, (ps_getg(c)<<8)|ps_getr(c)); + write_le_word(out_tim, (ps_getr(c2)<<8)|ps_getb(c)); + write_le_word(out_tim, (ps_getb(c2)<<8)|ps_getg(c2)); + } + } + break; + case 16: + for(y = 0; y < in_bitmap->h; y++) + { + for(x = 0; x < in_bitmap->w; x++) + { + c = ps_getpixel(in_bitmap, x, y); + shortbuf = rgb24_to_rgbpsx(ps_getr(c), ps_getg(c), ps_getb(c)); + write_le_word(out_tim, shortbuf); + } + } + break; + case 4: + for(y = 0; y < in_bitmap->h; y++) + { + for(x = 0; x < in_bitmap->w; x+=4) + { + shortbuf = 0; + for(c = 0; c < 4; c++) + shortbuf |= (ps_getpixel_pal(in_bitmap, x+c, y)&0xf) << (c<<2); + + write_le_word(out_tim, shortbuf); + } + } + break; + case 8: + for(y = 0; y < in_bitmap->h; y++) + { + for(x = 0; x < in_bitmap->w; x+=2) + { + shortbuf = 0; + for(c = 0; c < 2; c++) + shortbuf |= (ps_getpixel_pal(in_bitmap, x+c, y)&0xff) << (c<<3); + + write_le_word(out_tim, shortbuf); + } + } + break; + } + + fclose(out_tim); + //printf("Bitmap converted to TIM file successfully!\n"); + return EXIT_SUCCESS; +} diff --git a/tools/cdcat.c b/tools/cdcat.c new file mode 100644 index 0000000..329fd17 --- /dev/null +++ b/tools/cdcat.c @@ -0,0 +1,373 @@ +/* + * cdcat: get and replace files inside an ISO-9660 CDROM image + * Based on cdcat by Robert Nordier + * + * Copyright (c) 2011 Giuseppe Gatta + * + * Copyright (c) 2007 Robert Nordier. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted subject to the following conditions: + * + *1. Existing copyright notices in source and other files must be + * retained. + * + *2. Redistributions in whatever form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define CDCAT_VERSION "0.5" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +enum +{ + cdrom_mode_1, + cdrom_mode_1_raw, + cdrom_mode_2, +}; + +enum +{ + cdcat_oper_read, + cdcat_oper_write, + cdcat_oper_showoffset +}; + +int cdcat_cdrom_mode = cdrom_mode_1; +int cdcat_oper = cdcat_oper_read; + +#define SECSIZ 2048 +#define NAMLEN 255 + +#define sw(x,y) ((x)<<8|(y)) +#define cv4(x) ((*(unsigned char*)(x)) | ((*(unsigned char*)(x+1))<<8) | \ + ((*(unsigned char*)(x+2))<<16) | ((*(unsigned char*)(x+3))<<24)) + +/* ISO 9660 Primary Volume Descriptor */ +static char cdmagic[] = {1, 'C', 'D', '0', '0', '1', 1, 0}; + +struct cddir { + unsigned char len_dr; /* length of directory record */ + unsigned char len_ear; /* extended attribute record length */ + unsigned char ext[8]; /* location of extent */ + unsigned char size[8]; /* data length */ + unsigned char time[7]; /* recording date and time */ + unsigned char flags; /* file flags */ + unsigned char fus; /* file unit size */ + unsigned char gap; /* interleave gap size */ + unsigned char vsn[4]; /* volume sequence number */ + unsigned char len_fi; /* length of file identifier */ + unsigned char fi[1]; /* file identifier ... */ +}; + +struct dir { + unsigned int ext; /* starting block number */ + unsigned int size; /* file size */ + int type; /* file type ('d' or '-') */ + char name[NAMLEN + 1]; /* file name */ +}; + +static char *fn; /* special file name */ +static int fd; /* special file descriptor */ + +int cdcat(char *); +void loaddir(struct cddir *, struct dir *); +void susp(unsigned char *, int, struct dir *); +int readblk(void *, unsigned int); +void writeblk(void *, unsigned int); +void error(char *); + +void cdcat_print_usage() +{ + fprintf(stderr, "usage: cdcat <options> iso-image [path]\n"); + fprintf(stderr, "\n" + "cdcat can be used to explore an ISO9660 filesystem image\n" + "If path is not specified the root directory is listed, if it's a directory " + "the directory is listed or if it's a file the file is printed to standard output\n" + "The Rock Ridge extensions to the ISO9660 filesystem standard are supported\n" + "Original program (c) 2007 Robert Nordier.\n" + "\n" + "Options:\n" + "-help - This screen\n" + "-mode1 - The image is raw and has Mode 1 sectors\n" + "-mode2 - The image is raw and has Mode 2 sectors (like PlayStation dumps)\n" + "-replace - If [path] is specified, the data of the file is\n" + " replaced with input from standard input\n" + "-showoffset - Show file offset in image\n" + "-version - Display version\n\n"); +} + +int main(int argc, char **argv) +{ + char *path; + int e; + int nargc; + int x; + + for(x = 1; x < argc; x++) + { + if(strcmp(argv[x], "--") == 0 || argv[x][0] != '-') + break; + + if(strcmp(argv[x], "-mode2") == 0 || strcmp(argv[x], "--mode2") == 0) + cdcat_cdrom_mode = cdrom_mode_2; + else if(strcmp(argv[x], "-mode1") == 0 || strcmp(argv[x], "--mode1") == 0) + cdcat_cdrom_mode = cdrom_mode_1_raw; + else if(strcmp(argv[x], "--help") == 0 || strcmp(argv[x], "-help") == 0) + { + cdcat_print_usage(); + return 0; + } + else if(strcmp(argv[x], "--version") == 0 || strcmp(argv[x], "-version") == 0) + { + printf("cdcat version "CDCAT_VERSION"\n"); + return 0; + } + else if(strcmp(argv[x], "--replace") == 0 || strcmp(argv[x], "-replace") == 0) + cdcat_oper = cdcat_oper_write; + else if(strcmp(argv[x], "--showoffset") == 0 || strcmp(argv[x], "-showoffset") == 0) + cdcat_oper = cdcat_oper_showoffset; + else + { + printf("Invalid option %s! Aborting.\n", argv[x]); + return -1; + } + } + + nargc = argc-(x-1); + + if (nargc != 2 && nargc != 3) { + cdcat_print_usage(); + exit(2); + } + + fn = argv[x]; + + if ((fd = open(argv[x], cdcat_oper==cdcat_oper_read?O_RDONLY:O_RDWR)) == -1) + error("cannot open"); + path = argv[x+1] ? argv[x+1] : ""; + + if ((e = cdcat(path)) != 0) + fprintf(stderr, "cdcat: %s: Not found\n", path); + return e; +} + +int cdcat(char *path) +{ + unsigned char buf[SECSIZ]; + char name[NAMLEN + 1]; + struct cddir *dp, *tp; + struct dir xd; + char *p, *q; + unsigned ext, size, bx, bn, x, i; + int type, n; + + /* + * find primary volume descriptor + * and thence root directory + */ + bx = 64; + for (bn = 16; bn < bx; bn++) { + readblk(buf, bn); + if (strcmp((char *)buf, cdmagic) == 0) + break; + } + if (bn == bx) + error("Invalid argument"); + dp = (struct cddir *)&buf[156]; + loaddir(dp, &xd); + + /* + * lookup, list, print ... + */ + for (p = path; dp; p = q) { + while (*p == '/') + p++; + for (q = p; *q && *q != '/'; q++); + if ((n = q - p)) { + if (n > NAMLEN) + n = NAMLEN; + memcpy(name, p, n); + name[n] = 0; + } + ext = xd.ext; + size = xd.size; + type = xd.type; + dp = NULL; + bx = ext + (size + (SECSIZ - 1)) / SECSIZ; + for (bn = ext; !dp && bn < bx; bn++) { + if (type == 'd') + { + readblk(buf, bn); + + for (i = 0; !dp && buf[i]; i += buf[i]) { + tp = (struct cddir *)(buf + i); + loaddir(tp, &xd); + if (n == 0) + printf("%10u %c %s\n", + xd.size, xd.type, xd.name); + else if (strcmp(name, xd.name) == 0) + dp = tp; + } + } + else { + if(cdcat_oper == cdcat_oper_read) + { + readblk(buf, bn); + x = size < SECSIZ ? size : SECSIZ; + for (i = 0; i < x; i++) + putchar(buf[i]); + size -= x; + }else if(cdcat_oper == cdcat_oper_write) + { + x = size < SECSIZ ? size : SECSIZ; + for (i = 0; i < x; i++) + buf[i] = getchar(); + + writeblk(buf, bn); + + size -= x; + }else if(cdcat_oper == cdcat_oper_showoffset) + { + printf("%d\n", readblk(buf, bn)); + goto cdcat_end; + } + } + } + } +cdcat_end: + return n != 0; +} + +/* + * Gather together the directory information that interests us. + * Any and all of this may be altered by a suitable SUSP field. + */ +void loaddir(struct cddir *dp, struct dir *xp) +{ + int c; + + xp->ext = cv4(dp->ext); + xp->size = cv4(dp->size); + xp->type = dp->flags & 2 ? 'd' : '-'; + xp->name[0] = 0; + if (dp->fi[0] != 0) { + c = dp->len_fi | 1; + susp(dp->fi + c, dp->len_dr - 33 - c, xp); + } + if (xp->name[0] == 0) + { + if (dp->fi[0] == 0 || dp->fi[0] == 1) + strcpy(xp->name, dp->fi[0] == 0 ? "." : ".."); + else + { + memcpy(xp->name, dp->fi, dp->len_fi); + xp->name[dp->len_fi] = 0; + } + } +} + +/* + * SUSP/RRIP support: allowing UNIX-style file names and directories + * nested more than eight deep (among other things). + */ +void susp(unsigned char *sp, int n, struct dir *xp) +{ + unsigned char buf[SECSIZ]; + unsigned char *p; + int i, j; + + for (p = sp; p < sp + n && *p;) { + if (p[3] != 1) + return; + switch (sw(p[0], p[1])) { + /* continuation area */ + case sw('C', 'E'): + readblk(buf, cv4(&p[4])); + sp = buf + cv4(&p[12]); + n = cv4(&p[20]); + p = sp; + continue; + /* child link */ + case sw('C', 'L'): + xp->ext = cv4(&p[4]); + xp->size = SECSIZ; + xp->type = 'd'; + break; + /* alternate name */ + case sw('N', 'M'): + for (j = 0; xp->name[j]; j++); + for (i = 5; i < p[2]; i++) + xp->name[j++] = p[i]; + xp->name[j] = 0; + break; + } + p += p[2]; + } +} + +int readblk(void *buf, unsigned int blkno) +{ + int r; + + switch(cdcat_cdrom_mode) + { + case cdrom_mode_1: + r = lseek(fd, blkno * SECSIZ, 0); + break; + case cdrom_mode_1_raw: + r = lseek(fd, (blkno * 2352) + 16, 0); + break; + case cdrom_mode_2: + r = lseek(fd, (blkno * 2352) + 24, 0); + break; + } + + if (read(fd, buf, SECSIZ) != SECSIZ) + error("read error"); + + return r; +} + +void writeblk(void *buf, unsigned int blkno) +{ + switch(cdcat_cdrom_mode) + { + case cdrom_mode_1: + lseek(fd, blkno * SECSIZ, 0); + break; + case cdrom_mode_1_raw: + lseek(fd, (blkno * 2352) + 16, 0); + break; + case cdrom_mode_2: + lseek(fd, (blkno * 2352) + 24, 0); + break; + } + + if (write(fd, buf, SECSIZ) != SECSIZ) + error("write error"); +} + +void error(char *msg) +{ + fprintf(stderr, "cdcat: %s: %s\n", fn, msg); + exit(2); +} diff --git a/tools/elf2exe.c b/tools/elf2exe.c new file mode 100755 index 0000000..89e2c6d --- /dev/null +++ b/tools/elf2exe.c @@ -0,0 +1,231 @@ +/*
+ * elf2exe
+ *
+ * Converts an ELF executable to PS-EXE, using objcopy
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+const unsigned char psexe_magic[8] = {'P','S','-','X',' ','E','X','E'};
+const char *psexe_marker_usa = "Sony Computer Entertainment Inc. for North America area";
+const char *psexe_marker_jpn = "Sony Computer Entertainment Inc. for Japan area";
+const char *psexe_marker_eur = "Sony Computer Entertainment Inc. for Europe area";
+char *psexe_marker;
+
+//#define OBJCOPY_PATH "mipsel-unknown-elf-objcopy"
+
+int main(int argc, char *argv[])
+{
+ FILE *objcopy_out, *psexe;
+ char stringbuf[2048];
+ unsigned char charbuf;
+ int x;
+ unsigned int sz;
+ unsigned int gp = 0;
+
+ if(argc < 3)
+ {
+ printf("elf2exe - Converts an ELF executable to PS-EXE\n");
+ printf("usage: elf2exe [elf] [ps-x exe] <options>\n");
+ printf("\n");
+ printf("Options:\n");
+ printf("-mark_jpn - Use Japanese ascii marker (default: USA)\n");
+ printf("-mark_eur - Use European ascii marker (default: USA)\n");
+ printf("-mark=<mark> - Use custom ascii marker <mark>\n");
+ return -1;
+ }
+
+ psexe_marker = (char*)psexe_marker_usa;
+
+ for(x = 3; x < argc; x++)
+ {
+ if(strcmp(argv[x], "-mark_jpn") == 0)
+ psexe_marker = (char*)psexe_marker_jpn;
+
+ if(strcmp(argv[x], "-mark_eur") == 0)
+ psexe_marker = (char*)psexe_marker_eur;
+
+ if(strncmp(argv[x], "-mark=", 6) == 0)
+ {
+ if(strlen(argv[x]) >= 7)
+ psexe_marker = argv[x] + 6;
+ }
+
+ if(strncmp(argv[x], "-gp=", 4) == 0)
+ {
+ if(strlen(argv[x]) >= 5)
+ sscanf(argv[x] + 4, "%x", &gp);
+ }
+ }
+
+/*
+ * Now open the output file
+ */
+
+ psexe = fopen(argv[2], "wb");
+
+ if(psexe == NULL)
+ {
+ printf("Couldn't open %s for writing. Aborting!\n", argv[2]);
+ return -1;
+ }
+
+/*
+ * Write PSEXE magic string
+ */
+ fwrite(psexe_magic, sizeof(char), 8, psexe);
+
+/*
+ * Seek output file to 0x10, Initial Program Counter
+ */
+ fseek(psexe, 0x10, SEEK_SET);
+
+/*
+ * Write initial program counter = 0x80010000
+ */
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x01;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Global Pointer
+ */
+ charbuf = gp & 0xff;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff00) >> 8;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff0000) >> 16;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff000000) >> 24;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Seek output file to 0x18, Text section start address
+ */
+ fseek(psexe, 0x18, SEEK_SET);
+
+/*
+ * Write text section start address = 0
+ */
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x01;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Seek output file to 0x30, Initial Stack Pointer
+ */
+ fseek(psexe, 0x30, SEEK_SET);
+
+/*
+ * Write Initial Stack Pointer = 0x801FFFF0
+ */
+ charbuf = 0xF0;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0xFF;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x1F;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+
+/*
+ * Seek output to 0x4C, ASCII marker
+ */
+ fseek(psexe, 0x4C, SEEK_SET);
+
+ x = 0;
+
+/*
+ * Write ASCII marker string
+ */
+ while(psexe_marker[x])
+ fwrite(&psexe_marker[x++], sizeof(char), 1, psexe);
+
+/*
+ * Run objcopy now
+ */
+ sprintf(stringbuf, OBJCOPY_PATH" -O binary %s %s.bin", argv[1], argv[1]);
+ system(stringbuf);
+
+ sprintf(stringbuf, "%s.bin", argv[1]);
+
+/*
+ * Open objcopy output
+ */
+
+ objcopy_out = fopen(stringbuf, "rb");
+ if(objcopy_out == NULL)
+ {
+ printf("Could not open objcopy output at %s. Check your permissions. Aborting.\n",
+ stringbuf);
+ return -1;
+ }
+
+/*
+ * Seek to 0x800, Program Start
+ * and write the output of objcopy into the PS-X EXE
+ */
+ fseek(psexe, 0x800, SEEK_SET);
+
+ while(!feof(objcopy_out))
+ {
+ x = fgetc(objcopy_out);
+ fputc(x, psexe);
+ }
+
+
+ fclose(objcopy_out);
+
+/*
+ * Get the file size of the PS-X EXE
+ */
+ fseek(psexe, 0, SEEK_END);
+ sz = ftell(psexe);
+ fseek(psexe, 0, SEEK_SET);
+
+ if(sz % 2048 != 0)
+ {
+ fseek(psexe, (((sz / 2048) + 1)*2048) - 1, SEEK_SET);
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ sz = ftell(psexe);
+ }
+
+/*
+ * Write the address of the text section in the header of the PS-X EXE
+ */
+
+ sz -= 0x800;
+
+ fseek(psexe, 0x1C, SEEK_SET);
+
+ charbuf = sz & 0xff;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff00) >> 8;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff0000) >> 16;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff000000) >> 24;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+ fclose(psexe);
+
+/*
+ * Remove objcopy output
+ */
+ sprintf(stringbuf, "%s.bin", argv[1]);
+ remove(stringbuf);
+
+ return 0;
+}
diff --git a/tools/endian.c b/tools/endian.c new file mode 100644 index 0000000..333390f --- /dev/null +++ b/tools/endian.c @@ -0,0 +1,52 @@ +unsigned short read_le_word(FILE *f)
+{
+ unsigned char c;
+ unsigned short i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+
+ return i;
+}
+
+unsigned int read_le_dword(FILE *f)
+{
+ unsigned char c;
+ unsigned int i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<16);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<24);
+
+ return i;
+}
+
+
+void write_le_word(FILE *f, unsigned short leword)
+{
+ unsigned char c;
+
+ c = leword & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ c = leword >> 8;
+ fwrite(&c, sizeof(char), 1, f);
+}
+
+void write_le_dword(FILE *f, unsigned int ledword)
+{
+ unsigned char c;
+ int x;
+
+ for(x = 0; x < 4; x++)
+ {
+ c = (ledword >> (x<<3)) & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ }
+}
diff --git a/tools/exefixup.c b/tools/exefixup.c new file mode 100644 index 0000000..7f555dc --- /dev/null +++ b/tools/exefixup.c @@ -0,0 +1,204 @@ +/* + * exefixup.c v0.02.1 Andrew Kieschnick <andrewk@mail.utexas.edu> + * (v0.02.1): Giuseppe Gatta <tails92@gmail.com> + * + * v0.02.1 changes: removed warnings + * + * displays PS-X EXE header information + * offers to fix incorrect t_size + * offers to pad to 2048-byte boundary for cd-rom use + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +unsigned int char2int(unsigned char *foo) +{ + return foo[3]*16777216 + foo[2]*65536 + foo[1]*256 + foo[0]; +} + +void int2char(unsigned int foo, unsigned char *bar) +{ + bar[3]=foo>>24; + bar[2]=foo>>16; + bar[1]=foo>>8; + bar[0]=foo; +} + +void usage(void) +{ + printf("Usage: exefixup <filename>\n\n"); + printf("\t<filename>\ta PS-X EXE file\n\n"); + printf("\tdisplays EXE header\n"); + printf("\toffers to correct a wrong t_size\n"); + printf("\toffers to pad to 2048-byte boundary\n\n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + FILE *exe; + FILE *out; + unsigned char data[8]; + char filename[256]; + int i; + unsigned int header_data[12]; + unsigned int size; + unsigned int padsize; + signed char yesno='Z'; + + printf("exefixup v0.02.1 Andrew Kieschnick <andrewk@mail.utexas.edu>\n\n"); + + if (argc!=2) + usage(); + + strncpy(filename,argv[1],256); + + exe=fopen(filename, "r"); + + strcat(filename, "-fixed"); /* output filename is same as input filename, but with -fix appended */ + + if (!exe) + { + printf("ERROR: Can't open %s\n",filename); + exit(-1); + } + + for(i=0;i<8;i++) + fscanf(exe, "%c", &data[i]); + data[8]=0; + + if (strncmp((char*)data, "PS-X EXE", 8)) + { + printf("ERROR: Not a PS-X EXE file\n"); + exit(-1); + } + + for(i=0;i<12;i++) + { + fscanf(exe, "%c", &data[0]); + fscanf(exe, "%c", &data[1]); + fscanf(exe, "%c", &data[2]); + fscanf(exe, "%c", &data[3]); + header_data[i]=char2int(data); + } + + printf("id\tPS-X EXE\n"); + printf("text\t0x%.8x\n", header_data[0]); + printf("data\t0x%.8x\n", header_data[1]); + printf("pc0\t0x%.8x\n", header_data[2]); + printf("gp0\t0x%.8x\n", header_data[3]); + printf("t_addr\t0x%.8x\n", header_data[4]); + printf("t_size\t0x%.8x\n", header_data[5]); + printf("d_addr\t0x%.8x\n", header_data[6]); + printf("d_size\t0x%.8x\n", header_data[7]); + printf("b_addr\t0x%.8x\n", header_data[8]); + printf("b_size\t0x%.8x\n", header_data[9]); + printf("s_addr\t0x%.8x\n", header_data[10]); + printf("s_size\t0x%.8x\n\n", header_data[11]); + + fseek(exe, 0, SEEK_END); + + size=ftell(exe)-2048; + + padsize=2048-(size%2048); + + if (padsize!=2048) + { + printf("WARNING: EXE size is not a multiple of 2048!\n"); + while ((yesno!='Y')&&(yesno!='N')) + { + printf("Write a padded EXE (to %s) ? ",filename); + scanf("%c%*c", &yesno); + yesno=toupper(yesno); + } + if (yesno=='Y') + { + out = fopen(filename, "w"); + + header_data[5]=size+padsize; + + fprintf(out, "PS-X EXE"); + for(i=0;i<12;i++) + { + int2char(header_data[i], data); + fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]); + } + + fseek(exe, 56, SEEK_SET); + + for(i=0;i<size+1992;i++) + { + fscanf(exe, "%c", &data[0]); + fprintf(out, "%c", data[0]); + } + for(i=0;i<padsize;i++) + fprintf(out, "%c", 0); + + size=header_data[5]; + fclose(out); + } + } + + yesno='Z'; + + if (size!=header_data[5]) + { + printf("WARNING: EXE header t_size does not match filesize-2048\n"); + printf("EXE header:\t 0x%.8x bytes\n", header_data[5]); + printf("filesize-2048:\t 0x%.8x bytes\n", size); + while ((yesno!='Y')&&(yesno!='N')) + { + printf("Write a corrected EXE (to %s) ? ",filename); + scanf("%c%*c", &yesno); + yesno=toupper(yesno); + } + if (yesno=='Y') + { + out = fopen(filename, "w"); + + fprintf(out, "PS-X EXE"); + for(i=0;i<5;i++) + { + int2char(header_data[i], data); + fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]); + } + int2char(size, data); + fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]); + for(i=6;i<12;i++) + { + int2char(header_data[i], data); + fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]); + } + + fseek(exe, 56, SEEK_SET); + + for(i=0;i<size+1992;i++) + { + fscanf(exe, "%c", &data[0]); + fprintf(out, "%c", data[0]); + } + fclose(out); + } + } + fclose(exe); + return 0; +} diff --git a/tools/getpsxiso.c b/tools/getpsxiso.c new file mode 100644 index 0000000..76cfc72 --- /dev/null +++ b/tools/getpsxiso.c @@ -0,0 +1,55 @@ +// Converts a bin suitable for burning for the PlayStation +// to an ISO by getting only the 2048 data bytes of each sector +// The reverse of mkpsxiso + +// Written by Giuseppe Gatta, 2010 + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + FILE *i, *o; + int x, s; + char buf[2352]; + + if (argc < 3) + { + printf("getpsxiso <input> <output>\n"); + return -1; + } + + i = fopen(argv[1], "rb"); + + if(i == NULL) + { + printf("Could not open specified input file.\n"); + return -1; + } + + fseek(i, 0, SEEK_END); + s = ftell(i) / 2352; + fseek(i, 0, SEEK_SET); + + if(s % 2352 == 0) + { + printf("Input file size not a multiplier of 2352.\n"); + printf("Aborting.\n"); + return -1; + } + + o = fopen(argv[2], "wb"); + + for(x = 0; x < s; x++) + { + fread(buf, sizeof(char), 2352, i); + fwrite(buf + 24, sizeof(char), 2048, o); + printf("Sector %d/%d written\r", x+1, s); + } + + printf("\n"); + + fclose(i); + fclose(o); + + return 0; +} diff --git a/tools/huff.c b/tools/huff.c new file mode 100644 index 0000000..0e28fc5 --- /dev/null +++ b/tools/huff.c @@ -0,0 +1,555 @@ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define BUFFER_SIZE 1024 + +struct TreeNode { + unsigned char value; + unsigned long freq; + struct TreeNode *left; + struct TreeNode *right; +}; + +struct CodeNode { + unsigned char value; + unsigned long code; + unsigned int codeSize; +}; + +unsigned long table[256]; +unsigned char value[256]; +unsigned long freq[256]; +int tableSize; + +struct CodeNode codes[256]; +unsigned int codesUsed; + +FILE *inFile; +unsigned long inputSize; + +FILE *outFile; + +struct TreeNode *root; + +char inBuffer[BUFFER_SIZE]; +char outBuffer[BUFFER_SIZE]; + +void CreateTable(); +void SiftHeap(struct TreeNode**, unsigned int, unsigned int); +void SortTrees(struct TreeNode**, unsigned int); +void DestroyTree(struct TreeNode *); +void CreateTree(); +void GenerateCodes(struct TreeNode*, unsigned long, unsigned long); +void SortCodes(); +void Compress(); +void Decompress(); +void DisplayHelp(); +int main(int, char**); + +////////////////////////////////////////////////////////////////////////// +// Create the character frequency table for the file +////////////////////////////////////////////////////////////////////////// +void CreateTable() { + int x, y; + unsigned long maxCount; + unsigned char maxIndex; + unsigned char ch; + + for(x = 0; x < 256; x++) { + value[x] = 0; + freq[x] = 0; + } + + rewind(inFile); + tableSize = 0; + for(;;) { + ch = fgetc(inFile); + if(feof(inFile)) { + break; + } + ++inputSize; + if(table[ch] == 0) { + ++tableSize; + } + ++table[ch]; + } + + y = tableSize; + do { + --y; + maxIndex = 0; + maxCount = 0; + for(x = 0; x < 256 ; x++) { + if(table[x] > maxCount) { + maxIndex = x; + maxCount = table[(unsigned int)maxIndex]; + } + } + freq[y] = maxCount; + value[y] = maxIndex; + table[(unsigned int)maxIndex] = 0; + } while(y > 0); +} + +////////////////////////////////////////////////////////////////////////// +// Max-heapify (for sorting) +////////////////////////////////////////////////////////////////////////// +void SiftHeap(struct TreeNode **trees, unsigned int x, unsigned int size) { + struct TreeNode *root; + int finished; + unsigned int y; + + root = trees[x - 1]; + y = x << 1; + + finished = (y > size); + while(!finished) { + if(y < size) { + if(trees[y + 1 - 1]->freq > trees[y - 1]->freq) { + ++y; + } + } + if(trees[y - 1]->freq > root->freq) { + trees[x - 1] = trees[y - 1]; + x = y; + y = x << 1; + finished = (y > size); + } else { + finished = 1; + } + } + trees[x - 1] = root; +} + +////////////////////////////////////////////////////////////////////////// +// Sort the trees in ascending order by frequency +////////////////////////////////////////////////////////////////////////// +void SortTrees(struct TreeNode **trees, unsigned int num) { + struct TreeNode *temp; + unsigned int x; + + for(x = num >> 1; x > 1; x--) { + SiftHeap(trees, x, num); + } + + for(x = num; x > 1; x--) { + SiftHeap(trees, 1, x); + temp = trees[1 - 1]; + trees[1 - 1] = trees[x - 1]; + trees[x - 1] = temp; + } +} + +////////////////////////////////////////////////////////////////////////// +// Release the memory used by the tree +////////////////////////////////////////////////////////////////////////// +void DestroyTree(struct TreeNode *ptr) { + if(ptr->right) { + DestroyTree(ptr->right); + } + if(ptr->left) { + DestroyTree(ptr->left); + } + free(ptr); +} + +////////////////////////////////////////////////////////////////////////// +// Create the huffman coding tree +////////////////////////////////////////////////////////////////////////// +void CreateTree() { + struct TreeNode *ptr; + struct TreeNode *trees[257]; + int x, y; + + for(x = 0; x < tableSize; x++) { + trees[x] = malloc(sizeof(struct TreeNode)); + trees[x]->right = 0; + trees[x]->left = 0; + trees[x]->value = value[x]; + trees[x]->freq = freq[x]; + } + + y = tableSize; + while(y > 1) { + // Combine two smallest nodes into a tree + ptr = malloc(sizeof(struct TreeNode)); + ptr->right = trees[0]; + ptr->left = trees[1]; + ptr->freq = trees[0]->freq + trees[1]->freq; + ptr->value = 0; + trees[0] = ptr; + for(x = 1; x < y - 1; x++) { + trees[x] = trees[x + 1]; + } + trees[y] = 0; + SortTrees(trees, y - 1); // account for the zero + --y; + } + + root = trees[0]; +} + +////////////////////////////////////////////////////////////////////////// +// Generate code table from the tree +////////////////////////////////////////////////////////////////////////// +void GenerateCodes(struct TreeNode *ptr, unsigned long code, + unsigned long codeSize) { + if(ptr->right) { + GenerateCodes(ptr->right, (code << 1), codeSize + 1); + GenerateCodes(ptr->left, (code << 1) | 1, codeSize + 1); + } else { + codes[codesUsed].value = ptr->value; + codes[codesUsed].code = code; + codes[codesUsed].codeSize = codeSize; + ++codesUsed; + } +} + +////////////////////////////////////////////////////////////////////////// +// Sort the codes in ascending order by size +// Used for compression +////////////////////////////////////////////////////////////////////////// +void SortCodes() { + int x, y; + struct CodeNode temp; + + for(x = 0; x < codesUsed; x++) { + for(y = x; y < codesUsed; y++) { + if(codes[x].codeSize > codes[y].codeSize) { + temp = codes[x]; + codes[x] = codes[y]; + codes[y] = temp; + } + } + } +} + +////////////////////////////////////////////////////////////////////////// +// Compress the file +////////////////////////////////////////////////////////////////////////// +void Compress() { + int x, y; + unsigned char temp; + unsigned char ch; + int offset; + int ib, ob; + int inBufferSize; + + CreateTable(); + CreateTree(); + codesUsed = 0; + GenerateCodes(root, 0, 0); + DestroyTree(root); + SortCodes(); + +// [nextvolume]: Values are now saved as 32-bit little endian instead of ASCII numbers + + fputc(codesUsed & 0xff, outFile); + fputc((codesUsed >> 8) & 0xff, outFile); + fputc((codesUsed >> 16) & 0xff, outFile); + fputc((codesUsed >> 24) & 0xff, outFile); + + fputc(inputSize & 0xff, outFile); + fputc((inputSize >> 8) & 0xff, outFile); + fputc((inputSize >> 16) & 0xff, outFile); + fputc((inputSize >> 24) & 0xff, outFile); + + for(x = 0; x < codesUsed; x++) { + fputc(codes[x].value, outFile); + fputc(codes[x].codeSize - 1, outFile); + } + + offset = 7; + temp = 0; + for(x = 0; x < codesUsed; x++) { + for(y = codes[x].codeSize - 1; y >= 0; y--) { + temp |= ((codes[x].code >> y) & 1) << offset; + --offset; + if(offset < 0) { + fputc(temp, outFile); + offset = 7; + temp = 0; + } + } + } + if(offset != 7) { + fputc(temp, outFile); + } + + offset = 7; + temp = 0; + rewind(inFile); + ib = BUFFER_SIZE; + ob = 0; + inBufferSize = 0; + for(;;) { + if(ib >= BUFFER_SIZE) { + inBufferSize = fread(inBuffer, sizeof(char), + BUFFER_SIZE, inFile); + ib = 0; + } + if(ib >= inBufferSize) { + break; + } + ch = inBuffer[ib++]; + for(x = 0; x < codesUsed; x++) { + if(ch == codes[x].value) { + break; + } + } + for(y = codes[x].codeSize - 1; y >= 0; y--) { + temp |= ((codes[x].code >> y) & 1) << offset; + --offset; + if(offset < 0) { + outBuffer[ob++] = temp; + if(ob >= BUFFER_SIZE) { + fwrite(outBuffer, sizeof(char), + BUFFER_SIZE, outFile); + ob = 0; + } + temp = 0; + offset = 7; + } + } + } + if(offset != 7) { + outBuffer[ob++] = temp; + } + if(ob) { + fwrite(outBuffer, sizeof(char), ob, outFile); + } +} + +////////////////////////////////////////////////////////////////////////// +// Decompress the file +////////////////////////////////////////////////////////////////////////// +void Decompress() { + int x, y; + unsigned int dataSize; + unsigned long mask, maskSize; + unsigned char ch; + int offset; +// int last; + + int ib, ob; + + ib = fgetc(inFile); + codesUsed = ib; + ib = fgetc(inFile); + codesUsed |= ib << 8; + ib = fgetc(inFile); + codesUsed |= ib << 16; + ib = fgetc(inFile); + codesUsed |= ib << 24; + + ib = fgetc(inFile); + dataSize = ib; + ib = fgetc(inFile); + dataSize |= ib << 8; + ib = fgetc(inFile); + dataSize |= ib << 16; + ib = fgetc(inFile); + dataSize |= ib << 24; + + for(x = 0; x < codesUsed; x++) { + codes[x].value = fgetc(inFile); + codes[x].codeSize = fgetc(inFile) + 1; + } + + offset = 7; + ch = 0; + for(x = 0; x < codesUsed; x++) { + codes[x].code = 0; + for(y = codes[x].codeSize - 1; y >= 0; y--) { + if(offset == 7) { + ch = fgetc(inFile); + } + codes[x].code |= ((ch >> offset) & 1) << y; + offset = (offset - 1) & 7; + } + } + + maskSize = 0; + mask = 0; + offset = 7; +// last = 0; + y = 0; + x = 0; + ib = BUFFER_SIZE; + ob = 0; + for(;;) { + if(offset == 7) { + if(ib >= BUFFER_SIZE) { + fread(inBuffer, sizeof(char), BUFFER_SIZE, + inFile); + ib = 0; + } + ch = inBuffer[ib++]; + } + mask <<= 1; + mask |= (ch >> offset) & 1; + ++maskSize; + offset = (offset - 1) & 7; + + while(codes[y].codeSize < maskSize) ++y; + while(codes[y].codeSize == maskSize) { + if(codes[y].code == mask) { + if(ob >= BUFFER_SIZE) { + fwrite(outBuffer, sizeof(char), + BUFFER_SIZE, outFile); + ob = 0; + } + outBuffer[ob++] = codes[y].value; + ++x; + if(x >= dataSize) { + fwrite(outBuffer, sizeof(char), + ob, outFile); + return; + } + mask = 0; + maskSize = 0; + y = 0; + break; + } + ++y; + } + } +} + +////////////////////////////////////////////////////////////////////////// +// Display usage +////////////////////////////////////////////////////////////////////////// +void DisplayHelp() { + printf("Huffman compressor for PSXSDK\n"); + printf("Original version by Joe Wingbermuehle\n"); + printf("usage: huff [options] file\n"); + printf("options:\n"); + printf("\t-\t\tUse stdin for input\n"); + printf("\t-c\t\tCompress/Uncompress to stdout\n"); + printf("\t-k\t\tKeep old file\n"); + printf("\t-u\t\tUncompress\n"); +} + +////////////////////////////////////////////////////////////////////////// +// Main +////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) { + char *inName; + char *outName; + int x, y; + int error; + char uncompress; + char keep; + double savings; + + if(argc < 2) { + DisplayHelp(); + exit(1); + } + + uncompress = 0; + keep = 0; + inName = 0; + outName = 0; + outFile = 0; + inFile = 0; + for(x = 1; x < argc; x++) { + if(argv[x][0] == '-') { + switch(argv[x][1]) { + case 'u': + uncompress = 1; + break; + case 'c': + outFile = stdout; + keep = 1; + break; + case 'k': + keep = 1; + break; + case 0: + inFile = stdout; + break; + default: + DisplayHelp(); + exit(1); + } + } else if(!inName && !inFile) { + inName = malloc(strlen(argv[x]) + 1); + strcpy(inName, argv[x]); + } else { + printf("unrecognized option: %s\n", argv[x]); + DisplayHelp(); + exit(1); + } + } + + if(!inFile) { + inFile = fopen(inName, "rb"); + if(!inFile) { + printf("error: could not open input\n"); + exit(1); + } + } + + if(!uncompress) { + outName = malloc(strlen(inName) + 4); + strcpy(outName, inName); + strcat(outName, ".jh"); + } else { + error = 0; + if(strlen(inName) < 5) { + error = 1; + } else { + y = strlen(inName) - 3; + for(x = 0; x < 4; x++) { + if(inName[x + y] != ".jh"[x]) { + error = 1; + break; + } + } + } + if(error) { + printf("bad file extension\n"); + DisplayHelp(); + exit(1); + } + outName = malloc(strlen(inName) + 1); + strcpy(outName, inName); + if(strlen(outName) > 4) { + outName[strlen(outName) - 3] = 0; + } + } + + if(!outFile) { + outFile = fopen(outName, "wb"); + if(!outFile) { + printf("error: could not open output\n"); + exit(1); + } + } + + if(uncompress) { + Decompress(); + } else { + Compress(); + savings = (double)inputSize - (double)ftell(outFile); + savings /= (double)inputSize; + savings *= 100.00; + printf("savings: %.2f%%\n", savings); + } + + if(inFile != stdin) { + fclose(inFile); + if(!keep) { + remove(inName); + } + } + if(outFile != stdout) { + fclose(outFile); + } + exit(0); +} + + diff --git a/tools/lictool.c b/tools/lictool.c new file mode 100644 index 0000000..4fcf493 --- /dev/null +++ b/tools/lictool.c @@ -0,0 +1,100 @@ +/*
+ * lictool
+ *
+ * Tool for manipulating PS1 license files
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+unsigned char lic_buffer[37632]; // 16 CD sectors..
+
+//0x2E08
+
+void display_usage();
+
+void display_usage()
+{
+ printf(""
+ "lictool - PS1 license file manipulation tool\n"
+ "usage: lictool <input> <output> <options>\n"
+ "\n"
+ "Options:\n"
+ " -tmd=<file> TMD file for boot logo\n"
+ " -removelogo Remove logo from license file\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int x,y,z,sz;
+ FILE *f;
+
+ if(argc < 3)
+ {
+ display_usage();
+ return 0;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if(f == NULL)
+ {
+ printf("Could not open input license file! Aborting.\n");
+ return -1;
+ }
+
+ fread(lic_buffer, sizeof(char), 37632, f);
+ fclose(f);
+
+ for(x = 3; x < argc; x++)
+ {
+ if(strncmp(argv[x], "-tmd=", 5) == 0)
+ {
+ f = fopen(argv[x] + 5, "rb");
+
+ if(f == NULL)
+ printf("Could not open TMD file %s. Ignoring option.\n", argv[x] + 5);
+ else
+ {
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ z = 0x2E08;
+
+ for(y = 0; y < sz; y++)
+ {
+ if((z - ((z / 2352)*2352)) == 2072)
+ z+=304;
+
+ fread(&lic_buffer[z], sizeof(char), 1, f);
+
+ z++;
+ }
+
+ fclose(f);
+ }
+ }
+ else if(strncmp(argv[x], "-removelogo", 11) == 0)
+ {
+ z = 0x2E08;
+
+ for(y = 0; y < 12; y++)
+ lic_buffer[z+y] = 0;
+
+ lic_buffer[z] = 0x41;
+ }
+ }
+
+ f = fopen(argv[2], "wb");
+
+ if(f == NULL)
+ {
+ printf("Could not open output file path for writing! Aborting.\n");
+ return -1;
+ }
+
+ fwrite(lic_buffer, sizeof(char), 37632, f);
+ fclose(f);
+
+ return 0;
+}
diff --git a/tools/mkpsxiso.c b/tools/mkpsxiso.c new file mode 100644 index 0000000..5fd9da5 --- /dev/null +++ b/tools/mkpsxiso.c @@ -0,0 +1,250 @@ +/*
+ * mkpsxiso
+ *
+ * Converts an ISO to a .bin/.cue of a Playstation disk
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+char iso2raw_sec[16];
+char iso2raw_sub[8];
+char iso2raw_buf[2048];
+char iso2raw_edc[4];
+char iso2raw_ecc[276];
+
+void Iso2Raw_init()
+{
+ int x;
+
+ for(x = 0; x < 16; x++)
+ iso2raw_sec[x] = 0xFF;
+
+ iso2raw_sec[0] = 0;
+ iso2raw_sec[11] = 0;
+ iso2raw_sec[12] = 0;
+ iso2raw_sec[13] = 2;
+ iso2raw_sec[14] = 0;
+ iso2raw_sec[15] = 2;
+
+ for(x = 0; x < 8; x++)
+ iso2raw_sub[x] = 0;
+
+ for(x = 0; x < 4; x++)
+ iso2raw_edc[x] = 1;
+
+ for(x = 0; x < 276; x++)
+ iso2raw_ecc[x] = 2;
+}
+
+int Iso2Raw_licenseFile(char *licFile, char *binFile) {
+ FILE *lic, *bin;
+ char buffer[37632];
+ int sz;
+ int ret;
+
+ lic = fopen(licFile, "rb"); //lic = new RandomAccessFile(licFile, "r");
+
+ if(lic == NULL)
+ {
+ printf("Error! Could not open license file!\n");
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ bin = fopen(binFile, "rb+"); //bin = new RandomAccessFile(binFile, "rw");
+
+ if(bin == NULL)
+ {
+ printf("Error! Could not open BIN file!\n");
+ fclose(lic);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fseek(lic, 0, SEEK_END);
+ sz = ftell(lic);
+ fseek(lic, 0, SEEK_SET);
+
+
+ if (sz != 37632)
+ {
+ printf("Error! License file size mismatch. Image not licensed!\n");
+ fclose(lic);
+ fclose(bin);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fseek(bin, 0, SEEK_END);
+ sz = ftell(bin);
+ fseek(bin, 0, SEEK_SET);
+
+ if ((sz % 2352) != 0)
+ {
+ printf("Error! RAW image file size is not a multiple of 2352. Image not licensed!\n");
+ fclose(lic);
+ fclose(bin);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fread(buffer, sizeof(char), 37632, lic);
+ fwrite(buffer, sizeof(char), 37632, bin);
+
+ fclose(lic);
+ fclose(bin);
+ ret = 1;
+
+Iso2Raw_licenseFile_end:
+ if(ret == 0)
+ printf("Error licensing file! You must NOT burn the RAW image!\n");
+
+ return ret;
+}
+
+void Iso2Raw_generateCue(char *binFileName)
+{
+ int x, y;
+ char binBaseName[256];
+ char cueFileName[256];
+ FILE *cue_file;
+
+ for(x = (strlen(binFileName) - 1); x >= 0; x--)
+ {
+ if(binFileName[x] == '/' || binFileName[x] == '\\' || binFileName[x] == ':')
+ break;
+ }
+
+ x++;
+ y = 0;
+
+ for(; x < strlen(binFileName); x++)
+ binBaseName[y++] = binFileName[x];
+
+ binBaseName[y] = 0;
+
+ y = 0;
+
+ for(x = 0; x < strlen(binFileName); x++)
+ {
+ if(binFileName[x] == '.')
+ break;
+ else
+ cueFileName[y++] = binFileName[x];
+ }
+
+ cueFileName[y] = 0;
+
+ strcat(cueFileName, ".cue");
+
+ cue_file = fopen(cueFileName, "wb");
+
+ fprintf(cue_file, "FILE \"%s\" BINARY\n", binBaseName);
+ fprintf(cue_file, "TRACK 01 MODE2/2352\n");
+ fprintf(cue_file, " INDEX 01 00:00:00\n");
+
+ fclose(cue_file);
+}
+
+int Iso2Raw_convert(char *isofile, char *rawfile, char *licfile)
+{
+ FILE *infile, *outfile;
+ int c;
+ int thesec = 0;
+ int filesize, totalsectors, sector;
+
+ infile = fopen(isofile, "rb");
+
+ if(infile == NULL)
+ {
+ printf("An error has occured while trying to open file %s\n", isofile);
+ return 0;
+ }
+
+ fseek(infile, 0, SEEK_END);
+ filesize = ftell(infile);
+ fseek(infile, 0, SEEK_SET);
+
+ if ((filesize % 2048) != 0)
+ {
+ printf("Error! ISO file size is not a multiple of 2048. Operation aborted!\n");
+ fclose(infile);
+ return 0;
+ }
+
+ outfile = fopen(rawfile, "wb+");
+ fseek(outfile, 0, SEEK_SET);
+
+ if(outfile == NULL)
+ {
+ printf("An error has occured while trying to create file %s\n", rawfile);
+ fclose(infile);
+ return 0;
+ }
+
+ sector = 1;
+ totalsectors = filesize / 2048;
+ for(;;)
+ {
+ c = fread(iso2raw_buf, sizeof(char), 2048, infile);
+ if(c!=2048)break;
+
+ fwrite(iso2raw_sec, sizeof(char), 16, outfile);
+ fwrite(iso2raw_sub, sizeof(char), 8, outfile);
+ fwrite(iso2raw_buf, sizeof(char), 2048, outfile);
+ fwrite(iso2raw_edc, sizeof(char), 4, outfile);
+ fwrite(iso2raw_ecc, sizeof(char), 276, outfile);
+
+ thesec++;
+
+ if (thesec > 74)
+ {
+ thesec = 0;
+ iso2raw_sec[13]++;
+ }
+
+ iso2raw_sec[14] = ((thesec/10)<<4)|(thesec - ((thesec/10)*10));
+
+ printf("\r%d%% completed...", sector * 100 / totalsectors);
+ sector++;
+ }
+
+ printf("\r100%% completed! \n");
+
+ fclose(infile);
+ fclose(outfile);
+
+ Iso2Raw_generateCue(rawfile);
+
+ if(!Iso2Raw_licenseFile(licfile, rawfile))
+ return 0;
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ puts("mkpsxiso (C Edition) v0.1b - Converts a standard ISO image to .bin/.cue (PSX)");
+ puts("This software is based on Bruno Freitas' mkpsxiso in Java - bootsector@ig.com.br");
+ puts("That version is in turn based on Conyers' mkpsxiso - http://www.conyers.demon.co.uk");
+ puts("Author: Giuseppe Gatta (aka nextvolume) - 01/07/2009 - tails92@gmail.com\n");
+
+ if(argc != 4)
+ {
+ printf("Usage: mkpsxiso <iso file> <bin file> <PSX license file>\n");
+ return 1;
+ }
+
+ Iso2Raw_init();
+
+ if (!Iso2Raw_convert(argv[1], argv[2], argv[3]))
+ {
+ puts("ISO file conversion failed.");
+ return 1;
+ }
+
+ puts("ISO file conversion terminated successfully!!");
+
+ return 0;
+}
diff --git a/tools/mod4psx.c b/tools/mod4psx.c new file mode 100644 index 0000000..1fc68ef --- /dev/null +++ b/tools/mod4psx.c @@ -0,0 +1,164 @@ +#include <stdio.h>
+#include <stdlib.h>
+#include "../libmodplay/modplay.h"
+#include "adpcm.h"
+
+unsigned char *mod_data;
+ModMusic *mod;
+
+// Container format
+
+// Header
+
+// 8 bytes - "_mod4psx"
+// 4 bytes - Number of samples contained
+
+// Sample format
+// 4 bytes - Length of ADPCM sample
+// 8 bytes - Reserved
+// ... Data ...
+
+// All multi word numerical values are in little endian format
+// which is used by the processor of the PlayStation.
+// All data is aligned to 4 bytes.
+
+unsigned char adpcm_buffer[0x10000];
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int sz, x,y;
+
+ if(argc < 3)
+ {
+ printf("mod4psx <mod_music> <adpcm_dat>\n");
+ printf(
+"\nMOD4PSX gets the sound samples from a music module supported by libmodplay, "
+"and then converts them to PS1 ADPCM format and puts them all in a datafile, which will be able to be loaded "
+"by libmodplay. In this way the CPU time needed by the PlayStation processor to convert at runtime from PCM to ADPCM is saved.\n"
+);
+ return -1;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if(f == NULL)
+ {
+ printf("Could not open %s for reading. Aborting.\n", argv[1]);
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ mod_data = malloc(sz);
+
+ if(mod_data == NULL)
+ {
+ printf("Could not allocate %d bytes of memory. Aborting.\n", sz);
+ return -1;
+ }
+
+ fread(mod_data, sizeof(char), sz, f);
+
+ fclose(f);
+
+ mod = MODLoad(mod_data);
+
+ printf("Title: %s\n", mod->title);
+
+
+
+ f = fopen(argv[2], "wb");
+
+// Write header
+
+// Magic string
+ fprintf(f, "_mod4psx");
+// Write number of samples
+ fputc(mod->sample_num & 0xff, f);
+ fputc((mod->sample_num >> 8) & 0xff, f);
+ fputc(0, f);
+ fputc(0, f);
+
+ for(x = 0; x < mod->sample_num; x++)
+ {
+ //printf("%d: %s\n", x, mod->sample[x].name);
+ printf("sample[%d].bits = %d, sample[%d].data_type = %d\n", x, mod->sample[x].bits, x, mod->sample[x].data_type);
+
+
+ if(mod->sample[x].length >= 32)
+ {
+ if((mod->sample[x].data_type & 1) && mod->sample[x].bits == 8)
+ {
+ for(y = 0; y < mod->sample[x].length; y++)
+ mod->sample[x].data[y]^=0x80;
+ }
+
+ if(//mod->fmt == MOD_FMT_MOD &&
+ mod->sample[x].repeat_len > 2)
+ {
+
+ sz = SsAdpcmPack(mod->sample[x].data, adpcm_buffer, // FIX THIS!!!
+ mod->sample[x].length / (mod->sample[x].bits / 8), (mod->sample[x].bits==16)?FMT_S16:FMT_U8,
+ sizeof(adpcm_buffer),
+ 1, mod->sample[x].repeat_off);
+
+ }
+ else
+ {
+
+
+ sz = SsAdpcmPack(mod->sample[x].data, adpcm_buffer,
+ mod->sample[x].length / (mod->sample[x].bits / 8), (mod->sample[x].bits==16)?FMT_S16:FMT_U8,
+ sizeof(adpcm_buffer), 0, 0);
+
+ }
+
+ printf("%d) %s, %d -> %d, %d, %d, FIN=%d\n", x, mod->sample[x].name,
+ mod->sample[x].length, sz,
+ mod->sample[x].repeat_off,
+ mod->sample[x].repeat_len, mod->sample[x].finetune);
+ }
+ else
+ {
+ printf("%d) %s, Not written\n", x, mod->sample[x].name);
+ sz = 0;
+ }
+
+// Write length of ADPCM sample
+
+ fputc(sz & 0xff, f);
+ fputc((sz>>8)&0xff, f);
+ fputc((sz>>16)&0xff, f);
+ fputc((sz>>24)&0xff, f);
+
+// Skip 8 reserved bytes - for future expansion...
+
+ fseek(f, 8, SEEK_CUR);
+
+// Write ADPCM sample data
+
+// Manipulate the samples to do looping for Protracker MOD samples
+// The PCM to ADPCM conversion routines haven't been modified to do this yet.
+
+ /*if(mod->sample[x].repeat_len > 2 &&
+ mod->fmt == MOD_FMT_MOD)
+ {
+
+ for(y = 0; y < ((sz / 16)-1); y++)
+ {
+ if((mod->sample[x].repeat_off / 28) == y)
+ adpcm_buffer[(y<<4) + 1] = 6;
+ else
+ adpcm_buffer[(y<<4) + 1] = 2;
+ }
+
+ adpcm_buffer[(y<<4) + 1] = 3;
+ }*/
+
+ fwrite(adpcm_buffer, sizeof(char), sz, f);
+ }
+
+ return 0;
+}
diff --git a/tools/psfex.c b/tools/psfex.c new file mode 100644 index 0000000..3de4bc8 --- /dev/null +++ b/tools/psfex.c @@ -0,0 +1,113 @@ +/*
+ * psfex - Extracts an EXE from a PSF file
+ *
+ * Programmed by Giuseppe Gatta - released in the public domain
+ * It can be used for any platform which the PSF format supports, not only PS1.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+// 0-2: PSF
+// 3: Version byte (0x01 for PlayStation)
+// 4: Size of reserved area (LE unsigned 32-bit)
+// 8: Compressed program length (LE unsigned 32-bit)
+// 12: Compressed program CRC32 (LE unsigned 32-bit)
+// xxx: Reserved area
+// xxx: Compressed program
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ unsigned char *fm;
+ unsigned int res_size;
+ unsigned int cprg_size;
+ unsigned int cprg_crc32;
+ unsigned char *om;
+ unsigned long dest_len;
+ int sz;
+
+ if(argc < 3)
+ {
+ printf("psfex - Extracts an executable from a PSF file\n");
+ printf("psfex [.psf] [output]\n");
+ return -1;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if (f == NULL)
+ {
+ printf("Could not open file.\n");
+ return -1;
+ }
+
+/*
+ * Get PSF size and load it in memory
+ */
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fm = malloc(sz);
+ fread(fm, sizeof(char), sz, f);
+
+ fclose(f);
+
+/*
+ * Get header information
+*/
+ if(fm[0] == 'P' && fm[1] == 'S' && fm[2] == 'F')
+ {
+ printf("PSF file.\n");
+ }
+ else
+ {
+ printf("Not a PSF file. Aborting.\n");
+ return -1;
+ }
+
+ res_size = fm[4] | (fm[5] << 8) | (fm[6]<<16) | (fm[7]<<24);
+ cprg_size = fm[8] | (fm[9] << 8) | (fm[10]<<16)|(fm[11]<<24);
+ cprg_crc32 = fm[12] | (fm[13]<<8)|(fm[14]<<16)|(fm[15]<<24);
+
+ printf("Reserved area size: %d bytes\n", res_size);
+ printf("Compressed program size: %d bytes\n", cprg_size);
+ printf("Compressed program CRC32: 0x%08x\n", cprg_crc32);
+
+/*
+ * Decompress the program
+ * The PSF format is inherently flawed and so we have to allocate 2 megabytes
+ * of memory (size of PS1 RAM) and then get the real destination size at the end
+ */
+ om = malloc(0x200000);
+ dest_len = 0x200000;
+ uncompress(om, &dest_len, &fm[16 + res_size], cprg_size);
+
+ printf("Real destination length: %ld\n", dest_len);
+
+/*
+ * Now let's write the decompressed program to the output file
+ */
+ f = fopen(argv[2], "wb");
+
+ if(f == NULL)
+ {
+ printf("Could not open %s for writing. Aborting.\n", argv[2]);
+ return -1;
+ }
+
+ fwrite(om, sizeof(char), dest_len, f);
+ fclose(f);
+
+/*
+ * Free memory, at the exit it is done anyway but this helps adaptions
+ */
+
+ free(om);
+ free(fm);
+
+ return 0;
+}
diff --git a/tools/spasm/Makefile b/tools/spasm/Makefile new file mode 100644 index 0000000..093b9b5 --- /dev/null +++ b/tools/spasm/Makefile @@ -0,0 +1,17 @@ +include ../../Makefile.cfg + +OUT = spasm$(EXE_SUFFIX) + +OBJS = $(patsubst %.c, %.o, $(wildcard *.c)) + +$(OUT): $(OBJS) + $(HOST_CC) $(HOST_CFLAGS) -o $(OUT) $(OBJS) + +%.o: %.c + $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $< + +install: + cp -rv $(OUT) $(TOOLCHAIN_PREFIX)/bin + +clean: + rm -f $(OBJS) $(OUT) diff --git a/tools/spasm/codegen.c b/tools/spasm/codegen.c new file mode 100644 index 0000000..fda456d --- /dev/null +++ b/tools/spasm/codegen.c @@ -0,0 +1,144 @@ +#include "spasm.h" + +char curIns[128]; +unsigned int curInsArg; +unsigned int curInsArgT; +unsigned int insArgv[64]; +unsigned int insArgc; +unsigned int insArgt[64]; +unsigned int copn; +int org_found; + +void (*INSFUNC)(void); + +volatile unsigned int curPc = 0; +int curPass = 0; +unsigned int startAddress = 0; +unsigned int numLabels; +unsigned int numLabelsAlloc; +int first_instruction; +asm_label *labels; +static int find_label_status = 1; + +void codegen_init(void) +{ + curPc = startAddress; + curPass = 0; + numLabels = 0; + numLabelsAlloc = 0; + labels = NULL; +} + +static asm_label *find_label_internal(char *name) +{ + int i; + + for(i = 0; i < numLabels; i++) + { + if(strcmp(name, labels[i].name) == 0) + return &labels[i]; + } + + return NULL; +} + +static void add_label_internal(char *name, unsigned int pc) +{ + // add labels only if current pass >= 1! + asm_label *l; + +/* if(curPass == ) + return; + + if(curPass >= 2) + return;*/ + + //printf("Name = %s\n", name); + + l = find_label_internal(name); + + if(l) + { + if(l->pc != pc) + { + //if(l->pass == curPass) + // assembler_error("Impossible to redefine label %s", name); + + //printf("Redefining, [%s] = %08X, pass %d\n", l->name, pc, curPass); + l->pc = pc; + } + + return; + } + + if(numLabels == numLabelsAlloc) + { + numLabelsAlloc += 128; + labels = realloc(labels, sizeof(asm_label) * numLabelsAlloc); + } + + strncpy(labels[numLabels].name, name, 127); + labels[numLabels].pass = curPass; + + labels[numLabels].pc = pc; + + numLabels++; + + //printf("label #%d, [%s] = %08X, pass = %d\n", numLabels, name, pc, curPass); + + /*while(*name) + { + printf("%x, \'%c\'\n", *name, *name); + name++; + }*/ +} + + + +void add_label(char *name, unsigned int pc) +{ + if(curPass == -1) + return; + + return add_label_internal(name, pc); +} + +void add_label_equ(char *name, unsigned int pc) +{ + return add_label_internal(name, pc); +} + +unsigned int find_label(char *name) +{ + //printf("find_label(%s)\n", name); + + asm_label *l = find_label_internal(name); + + if(l) + { + //find_label_status = 1; + return l->pc; + } + +// remember! if pass >= 1, abort if you can't find a label, because that means it was really +// impossible to find. + +// printf(">>DEBUG, PASS = %d << Couldn't find label %s$\n", curPass, name); + + find_label_status = 0; + + if(curPass == 1) + instruction_error("Cannot find label %s", name); + + return 0xFFFFFFFF; +} + +void find_label_reset() +{ + find_label_status = 1; +} + +int find_label_ok() +{ + return find_label_status; +} diff --git a/tools/spasm/codegen.h b/tools/spasm/codegen.h new file mode 100644 index 0000000..e9e0ab7 --- /dev/null +++ b/tools/spasm/codegen.h @@ -0,0 +1,33 @@ +#ifndef _SPASM_CODEGEN_H +#define _SPASM_CODEGEN_H + +typedef struct +{ + char name[128]; + unsigned int pc; + unsigned int pass; +}asm_label; + +extern asm_label *labels; + +extern volatile unsigned int curPc; +extern int curPass; +extern unsigned int numLabels; +extern unsigned int startAddress; +extern unsigned int copn; +extern int first_instruction; +extern int org_found; +extern void (*INSFUNC)(void); + +void codegen_init(void); +void add_label(char *label, unsigned int pc); +void add_label_equ(char *label, unsigned int pc); +unsigned int find_label(char *label); +void find_label_reset(); +int find_label_ok(); +int label_was_not_found_once(char *name); +void add_not_found_label(char *name); +//void resolve_labels(); + + +#endif diff --git a/tools/spasm/error.c b/tools/spasm/error.c new file mode 100644 index 0000000..019c3da --- /dev/null +++ b/tools/spasm/error.c @@ -0,0 +1,58 @@ +#include "spasm.h" + +static void show_line(void) +{ + printf("%s\n", curLine); + printf("^^^^^^^^^^^^^^\n"); +} + +void instruction_error(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + printf("Line %d: Error(%s) - ", line_number, curIns); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); + + exit(EXIT_FAILURE); +} + +void instruction_warning(char *format, ...) +{ + if(curPass <= 0) + return; + + va_list ap; + + va_start(ap, format); + + printf("Line %d: Warning (%s) - ", line_number, curIns); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); +} + +void assembler_error(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + printf("Line %d, assembler error: ", line_number); + vprintf(format, ap); + printf("\n"); + show_line(); + + va_end(ap); + + exit(EXIT_FAILURE); +} + +
\ No newline at end of file diff --git a/tools/spasm/error.h b/tools/spasm/error.h new file mode 100644 index 0000000..3951ffd --- /dev/null +++ b/tools/spasm/error.h @@ -0,0 +1,10 @@ +#ifndef _SPASM_ERROR_H +#define _SPASM_ERROR_H + +void instruction_error(char *format, ...); +void instruction_warning(char *format, ...); +void assembler_error(char *format, ...); +extern int yylineno; + +#endif + diff --git a/tools/spasm/eval.c b/tools/spasm/eval.c new file mode 100644 index 0000000..01f8f66 --- /dev/null +++ b/tools/spasm/eval.c @@ -0,0 +1,130 @@ +#include "spasm.h" + +/** + * Expressions in SPASM are implemented in a totally broken manner. + * + * The result of an expression is the value of its initial argument + * after executing the operation of the last operand with the last argument. + * + * For example dw $cafeba00+2+4 is not equal to dw $cafeba06 + * but to dw $cafeba04. + * Likewise, dw $2+$cafeba00+4 is not equal to dw $cafeba06 + * but to dw $6 ! + */ + +unsigned int spasm_eval(char *expr) +{ + char *cset = "+-><&|*!"; + char *csetp; + char *cp; + int ok; + int t = T_INTEGER; + char sbuf[128]; + + if(strcmp(expr, "*") == 0) // Return current return address + return curPc; + + if(strcasecmp(curIns, "incbin") == 0) + return 0; // If current instruction is incbin, do not evaluate. + + if(*expr == '"' || *expr == '\'') + return 0; // I won't even try to evaluate strings! + + int ispan = strcspn(expr, cset); + + csetp = cset; + + ok = 0; // ok will be 0 if we found no operator, 1 if we found it + + char op = '?'; + + cp = NULL; + + while(*csetp) + { + char *tcp = strrchr(expr, *csetp); + + if(tcp) // Found operator + { + if(tcp > cp) + cp = tcp; + + if(*cp == '>') + { + if(*(cp-1) != '>') + instruction_error("Bad operator"); + } + + if(*cp == '<') + { + if(*(cp-1) != '<') + instruction_error("Bad operator"); + } + + cp++; + ok = 1; + op = *csetp; + break; + } + + csetp++; + } + + unsigned int one = 0; + unsigned int two = 0; + + memcpy(sbuf, expr, ispan); + sbuf[ispan] = '\0'; + + if(strlen(sbuf) == 0) + strcpy(sbuf, "0"); + //instruction_error("Bad expression"); + + //printf("sbuf = %s\n", sbuf); + one = asm_atoi(sbuf); + + t = atoiT[insArgc]; + + if(ok) + { + strcpy(sbuf, cp); + + if(strlen(sbuf) == 0) + instruction_error("Bad expression"); + + two = asm_atoi(sbuf); + + if(t == T_INTEGER) + t = atoiT[insArgc]; + } + + atoiT[insArgc] = t; + + switch(op) + { + case '+': + return one + two; + break; + case '-': + return one - two; + break; + case '<': + return one << two; + break; + case '>': + return one >> two; + break; + case '&': + return one & two; + break; + case '|': + case '!': + return one | two; + break; + case '*': + return one * two; + break; + } + + return one; +} diff --git a/tools/spasm/eval.h b/tools/spasm/eval.h new file mode 100644 index 0000000..a568f7e --- /dev/null +++ b/tools/spasm/eval.h @@ -0,0 +1,6 @@ +#ifndef _SPASM_EVAL_H +#define _SPASM_EVAL_H + +unsigned int spasm_eval(char *expr); + +#endif diff --git a/tools/spasm/fcaseopen.c b/tools/spasm/fcaseopen.c new file mode 100755 index 0000000..20efd5b --- /dev/null +++ b/tools/spasm/fcaseopen.c @@ -0,0 +1,137 @@ +/* +Copyright (c) 2009 Keith Bauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "fcaseopen.h" + +#if !defined(_WIN32) +#include <stdlib.h> +#include <string.h> + +#include <dirent.h> +#include <errno.h> +#include <unistd.h> + +// r must have strlen(path) + 2 bytes +static int casepath(char const *path, char *r) +{ + size_t l = strlen(path); + char *p = alloca(l + 1); + strcpy(p, path); + size_t rl = 0; + + DIR *d; + if (p[0] == '/') + { + d = opendir("/"); + p = p + 1; + } + else + { + d = opendir("."); + r[0] = '.'; + r[1] = 0; + rl = 1; + } + + int last = 0; + char *c = strsep(&p, "/"); + while (c) + { + if (!d) + { + return 0; + } + + if (last) + { + closedir(d); + return 0; + } + + r[rl] = '/'; + rl += 1; + r[rl] = 0; + + struct dirent *e = readdir(d); + while (e) + { + if (strcasecmp(c, e->d_name) == 0) + { + strcpy(r + rl, e->d_name); + rl += strlen(e->d_name); + + closedir(d); + d = opendir(r); + + break; + } + + e = readdir(d); + } + + if (!e) + { + strcpy(r + rl, c); + rl += strlen(c); + last = 1; + } + + c = strsep(&p, "/"); + } + + if (d) closedir(d); + return 1; +} +#endif + +FILE *fcaseopen(char const *path, char const *mode) +{ + FILE *f = fopen(path, mode); +#if !defined(_WIN32) + if (!f) + { + char *r = alloca(strlen(path) + 2); + if (casepath(path, r)) + { + f = fopen(r, mode); + } + } +#endif + return f; +} + +void casechdir(char const *path) +{ +#if !defined(_WIN32) + char *r = alloca(strlen(path) + 2); + if (casepath(path, r)) + { + chdir(r); + } + else + { + errno = ENOENT; + } +#else + chdir(path); +#endif +} diff --git a/tools/spasm/fcaseopen.h b/tools/spasm/fcaseopen.h new file mode 100755 index 0000000..a6d024b --- /dev/null +++ b/tools/spasm/fcaseopen.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 2009 Keith Bauer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef fcaseopen_h +#define fcaseopen_h + +#include <stdio.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +extern FILE *fcaseopen(char const *path, char const *mode); + +extern void casechdir(char const *path); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/tools/spasm/opcode.c b/tools/spasm/opcode.c new file mode 100644 index 0000000..a09c119 --- /dev/null +++ b/tools/spasm/opcode.c @@ -0,0 +1,2214 @@ +#include "spasm.h" + +struct +{ + char *name; + void (*func)(); +}instruction_table[] = +{ + {"add" ,INS_ADD}, + {"addi" ,INS_ADDI}, + {"addiu" ,INS_ADDIU}, + {"addu" ,INS_ADDU}, + {"and" ,INS_AND}, + {"andi" ,INS_ANDI}, + {"beq" ,INS_BEQ}, + {"bgez" ,INS_BGEZ}, + {"bgezal" ,INS_BGEZAL}, + {"bgtz" ,INS_BGTZ}, + {"blez" ,INS_BLEZ}, + {"bltz" ,INS_BLTZ}, + {"bltzal" ,INS_BLTZAL}, + {"bne" ,INS_BNE}, + {"break" ,INS_BREAK}, + {"cfc0" ,INS_CFC }, + {"cfc1" ,INS_CFC }, + {"cfc2" ,INS_CFC }, + {"cfc3" ,INS_CFC }, + {"cop0" ,INS_COP }, + {"cop1" ,INS_COP }, + {"cop2" ,INS_COP }, + {"cop3" ,INS_COP }, + {"ctc0" ,INS_CTC }, + {"ctc1" ,INS_CTC }, + {"ctc2" ,INS_CTC }, + {"ctc3" ,INS_CTC }, + {"div" ,INS_DIV}, + {"divu" ,INS_DIVU}, + {"j" ,INS_J}, + {"jal" ,INS_JAL}, + {"jalr" ,INS_JALR}, + {"jr" ,INS_JR}, + {"lb" ,INS_LB}, + {"lbu" ,INS_LBU}, + {"lh" ,INS_LH}, + {"lhu" ,INS_LHU}, + {"lui" ,INS_LUI}, + {"lw" ,INS_LW}, + {"lwc0" ,INS_LWC }, + {"lwc1" ,INS_LWC }, + {"lwc2" ,INS_LWC }, + {"lwc3" ,INS_LWC }, + {"lwl" ,INS_LWL}, + {"lwr" ,INS_LWR}, + {"mfc0" ,INS_MFC}, + {"mfc1" ,INS_MFC}, + {"mfc2" ,INS_MFC}, + {"mfc3" ,INS_MFC}, + {"mfhi" ,INS_MFHI}, + {"mflo" ,INS_MFLO}, + {"mtc0" ,INS_MTC }, + {"mtc1" ,INS_MTC }, + {"mtc2" ,INS_MTC }, + {"mtc3" ,INS_MTC }, + {"mthi" ,INS_MTHI}, + {"mtlo" ,INS_MTLO}, + {"mult" ,INS_MULT}, + {"multu" ,INS_MULTU}, + {"nor" ,INS_NOR}, + {"or" ,INS_OR}, + {"ori" ,INS_ORI}, + {"sb" ,INS_SB}, + {"sh" ,INS_SH}, + {"sll" ,INS_SLL}, + {"sllv" ,INS_SLLV}, + {"slt" ,INS_SLT}, + {"slti" ,INS_SLTI}, + {"sltiu" ,INS_SLTIU}, + {"sltu" ,INS_SLTU}, + {"sra" ,INS_SRA}, + {"srav" ,INS_SRAV}, + {"srl" ,INS_SRL}, + {"srlv" ,INS_SRLV}, + {"sub" ,INS_SUB}, + {"subu" ,INS_SUBU}, + {"sw" ,INS_SW}, + {"swc0" ,INS_SWC}, + {"swc1" ,INS_SWC}, + {"swc2" ,INS_SWC}, + {"swc3" ,INS_SWC}, + {"swl" ,INS_SWL}, + {"swr" ,INS_SWR}, + {"syscall" ,INS_SYSCALL}, + {"xor" ,INS_XOR}, + {"xori" ,INS_XORI}, + + {"b" ,INS_B}, + {"la" ,INS_LA}, + {"li" ,INS_LI}, + {"nop" ,INS_NOP}, + {"move" ,INS_MOVE}, + {"subi" ,INS_SUBI}, + {"subiu" ,INS_SUBIU}, + {"beqz" ,INS_BEQZ}, + {"bnez" ,INS_BNEZ}, + {"bal" ,INS_BAL}, + {"org" ,INS_ORG}, + {"include" ,INS_INCLUDE}, + {"incbin" ,INS_INCBIN}, + {"dcb" ,INS_DCB}, + {"db" ,INS_DB}, + {"dh" ,INS_DH}, + {"dw" ,INS_DW}, + {"align" ,INS_ALIGN}, + + {NULL}, +}; + +#define I_TYPE(op, rs, rt, imm) \ + ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \ + ((imm) & 0xFFFF) ) + +#define J_TYPE(op, target) \ + ( (((op) & 63) << 26) | ((target) & 0x3FFFFFF) ) + +#define R_TYPE(op, rs, rt, rd, shamt, funct) \ + ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \ + (((rd) & 31) << 11) | (((shamt) & 31) << 6) | \ + ((funct) & 63)) + +// copn = coprocessor number +// SET_DIS_CHECK() + +#define SET_DIS_CHECK() /* Argument type check. Placeholder, contains nothing for now. */ + +int set_delay_slot = 0; + +void OUTSEEK(unsigned int position) +{ + fseek(asmOut, position, SEEK_SET); +} + +void OUTBYTE(unsigned char b) +{ + if(curPass>0) + fputc(b, asmOut); + + curPc++; +} + +void OUTHALF(unsigned short h) +{ + if(curPass>0) + { + fputc(h&0xff, asmOut); + fputc(h>>8, asmOut); + } + + curPc += 2; +} + +void OUTWORD(unsigned int w) +{ + if(curPass>0) + { + fputc(w&0xff, asmOut); + fputc((w>>8)&0xff, asmOut); + fputc((w>>16)&0xff, asmOut); + fputc(w>>24, asmOut); + } + + curPc += 4; +} + +void OUTINS(unsigned int instruction) +{ + OUTWORD(instruction); + + if(set_delay_slot) + { + OUTWORD(0); + + set_delay_slot = 0; + } +} + + +void OUTSTRING(char *string) +{ + int stringt; + int esc=0; + + if(*string == '"') + stringt = 0; + else if(*string == '\'') + stringt = 1; + else + instruction_error("OUTSTRING <INTERNAL ERROR>. Not a string!"); + + string++; + + while(*string) + { + if(*string == '"') + { + if(stringt == 0 && !esc) + break; + + OUTBYTE('"'); + esc = 0; + } + else if(*string == '\'') + { + if(stringt == 1 && !esc) + break; + + OUTBYTE('\''); + esc = 0; + } + else if(*string == 'n') + { + if(esc) + OUTBYTE('\n'); + else + OUTBYTE('n'); + + esc = 0; + } + else if(*string == 't') + { + if(esc) + OUTBYTE('\t'); + else + OUTBYTE('t'); + + esc = 0; + } + else if(*string == 'r') + { + if(esc) + OUTBYTE('\r'); + else + OUTBYTE('r'); + + esc = 0; + } + else if(*string == '\\') + { + if(esc) + { + OUTBYTE('\\'); + esc = 0; + } + else + esc = 1; + } + else + { + if(esc) + instruction_error("Invalid escape sequence \\%c in string", *string); + + OUTBYTE(*string); + } + + string++; + } +} + +unsigned int OUTSIZE(void) +{ + int r; + int pos = ftell(asmOut); + fseek(asmOut, 0, SEEK_END); + r = ftell(asmOut); + fseek(asmOut, pos, SEEK_SET); + return r; +} + +unsigned short compute_branch(unsigned int imm) +{ + unsigned int off = imm - (curPc + 4); + //off >>= 2; + + if(curPass <= 0) + return 0; + + if(off >= 0x20000 && off < -0x20000) + instruction_error("Branch out of range. %04x", off); + + return (off>>2) & 0xFFFF; +} + +unsigned int compute_jump(unsigned int imm) +{ + if(curPass <= 0) + return 0; + + return (imm >> 2); +} + +struct +{ + int size; + int pos; + unsigned int *el; +}cannotPredict = {0, 0, NULL}; + +unsigned int compute_real_offset(unsigned int imm, unsigned int base, + unsigned int rt) +{ + int i, unpredictable=0; + unsigned short hipart; + unsigned short lopart; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(!unpredictable && ((imm <= 0xFFFF) || (imm >= 0xFFFF8000 && imm <= 0xFFFFFFFF))) + return I_TYPE(0, base, rt, imm); + +//compute_real_offset_output_wide: + // li at, offset + // add at, at, base + // lw rt, 0(at) + hipart = (imm >> 16); + lopart = imm & 0xFFFF; + int t=(*curIns == 'l') ? rt : 1; + + // lw at, $CAFEBABE(zero) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(at) + // lw at, $CAFEBABE(v0) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(v0) + // sw at, $CAFEBABE -> + + if(base) + { + /*OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16) + + if(lopart || unpredictable) + OUTINS( I_TYPE (13, t, t, lopart)); // ori $t, $t, offset & 0xffff + + OUTINS( R_TYPE (0, t, base, t, 0, 33) ); // add $t, $t, base + return I_TYPE(0, t, rt, 0);*/ + +// SPASM is seriously broken regarding this.. + return I_TYPE(0, base, rt, imm); + } + + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16) + + return I_TYPE(0, t, rt, lopart); // XX rt, lopart(rt) +} + +void INS_ADD(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADD rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ADDI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ADDI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 32) ); +} + +void INS_ADDI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range."); + + OUTINS( I_TYPE (8, rs, rt, imm) ); +} + +void INS_ADDIU(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDIU rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range."); + + OUTINS( I_TYPE (9, rs, rt, imm) ); +} + +void INS_ADDU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADDU rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ADDIU(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ADDIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 33) ); +} + +void INS_AND(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // AND rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ANDI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ANDI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 36) ); +} + +void INS_ANDI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ANDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (12, rs, rt, imm) ); +} + +void INS_BEQ(void) +{ + unsigned int rt, rs, imm; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + imm = insArgv[2]; + + OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) ); +} + +void INS_BGEZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 1, compute_branch(imm)) ); +} + +void INS_BGEZAL(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 17, compute_branch(imm)) ); +} + +void INS_BGTZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(7, rs, 0, compute_branch(imm)) ); +} + +void INS_BLEZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(6, rs, 0, compute_branch(imm)) ); +} + +void INS_BLTZ(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 0, compute_branch(imm)) ); +} + +void INS_BLTZAL(void) +{ + unsigned int rs, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(1, rs, 16, compute_branch(imm)) ); +} + +void INS_BNE(void) +{ + unsigned int rt, rs, imm; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + imm = insArgv[2]; + + OUTINS( I_TYPE(5, rs, rt, compute_branch(imm)) ); +} + +void INS_BREAK(void) +{ + unsigned int imm = 0; + + if(insArgc > 1) + instruction_error("Too many arguments"); + + if(insArgc == 1) + imm = insArgv[0]; + + imm &= 0xFFFFF; + + OUTINS( (imm << 6) | 13 ); +} + +void INS_CFC(void) +{ + unsigned int rt, rd; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rd = insArgv[1]; + + OUTINS ( R_TYPE(16 | copn, 2, rt, rd, 0, 0) ); +} + +void INS_COP(void) +{ + unsigned int cofun; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + cofun = insArgv[0]; + + OUTINS( ( (16 | copn) << 26) | (1<<25) | (cofun & 0x1FFFFFF)); +} + +void INS_CTC(void) +{ + unsigned int rt, rd; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rd = insArgv[1]; + + OUTINS ( R_TYPE(16 | copn, 6, rt, rd, 0, 0) ); +} + +void INS_DIV(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS ( R_TYPE(0, rs, rt, 0, 0, 26)); +} + +void INS_DIVU(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS ( R_TYPE(0, rs, rt, 0, 0, 27)); +} + +void INS_J(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( J_TYPE(2, compute_jump(insArgv[0]))); +} + +void INS_JAL(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( J_TYPE(3, compute_jump(insArgv[0]))); +} + +void INS_JALR(void) +{ + unsigned int rd, rs; + + if(insArgc < 1) + instruction_error("Not enough arguments"); + if(insArgc > 2) + instruction_error("Too many arguments"); + + if(insArgc == 1) + { + rd = 31; // register ra + rs = insArgv[0]; + } + else + { + rd = insArgv[0]; + rs = insArgv[1]; + } + + OUTINS ( R_TYPE(0, rs, 0, rd, 0, 9)); +} + +void INS_JR(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS ( R_TYPE(0, insArgv[0], 0, 0, 0, 8)); +} + +void INS_LB(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(32, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LBU(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(36, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LH(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(33, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LHU(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(37, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LUI(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(I_TYPE(15, 0, insArgv[0], insArgv[1])); +} + +void INS_LW(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(35, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWC(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(48 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWL(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(34, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_LWR(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(38, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_MFC(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE (16 | copn, 0, insArgv[0], insArgv[1], 0, 0)); +} + +void INS_MFHI(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 16)); +} + +void INS_MFLO(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 18)); +} + +void INS_MTC(void) +{ + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE (16 | copn, 4, insArgv[0], insArgv[1], 0, 0)); +} + +void INS_MTHI(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 17)); +} + +void INS_MTLO(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 19)); +} + +void INS_MULT(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS(R_TYPE(0, rs, rt, 0, 0, 24)); +} + +void INS_MULTU(void) +{ + unsigned int rs, rt; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rs = insArgv[0]; + rt = insArgv[1]; + + OUTINS(R_TYPE(0, rs, rt, 0, 0, 25)); +} + +void INS_NOR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 39) ); +} + +void INS_OR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_ORI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_ORI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 37) ); +} + +void INS_ORI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ANDI rt, rs, imm -> rt = rs + imm; + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (13, rs, rt, imm) ); +} + +void INS_SB(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(40, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SH(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(41, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SLL(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 0)); +} + +void INS_SLLV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 4) ); +} + +void INS_SLT(void) +{ + unsigned int rd, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + if(atoiT[2] == T_INTEGER) + return INS_SLTI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 42) ); +} + +void INS_SLTI(void) +{ + unsigned int imm, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + + if(imm > 0x7FFF && imm < 0xFFFF8000) + instruction_error("Immediate out of range."); + + OUTINS( I_TYPE (10, rs, rt, imm) ); +} + +void INS_SLTIU(void) +{ + unsigned int imm, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + + if(imm > 0x7FFF && imm < 0xFFFF8000) + instruction_error("Immediate out of range."); + + OUTINS( I_TYPE (11, rs, rt, imm) ); +} + +void INS_SLTU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc != 3) + instruction_error("Wrong number of arguments"); + + if(atoiT[2] == T_INTEGER) + return INS_SLTIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 43) ); +} + +void INS_SRA(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 3)); +} + +void INS_SRAV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 7) ); +} + +void INS_SRL(void) +{ + unsigned int rd, rt, sa; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + sa = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + sa = insArgv[2]; + } + + OUTINS(R_TYPE(0, 0, rt, rd, sa, 2)); +} + +void INS_SRLV(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rd = insArgv[0]; + rt = insArgv[0]; + rs = insArgv[1]; + } + else + { + rd = insArgv[0]; + rt = insArgv[1]; + rs = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 6) ); +} + +void INS_SUB(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_SUBI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_SUBI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 34) ); +} + +void INS_SUBU(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + // ADD rd, rs, rt -> rd = rs + rt + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_SUBIU(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_SUBIU(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 35) ); +} + +void INS_SW(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(43, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWC(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(56 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWL(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(42, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SWR(void) +{ + unsigned int base, rt, offset; + + SET_DIS_CHECK(); + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + offset = insArgv[1]; + base = 0; + } + else + { + rt = insArgv[0]; + offset = insArgv[1]; + base = insArgv[2]; + } + + OUTINS(R_TYPE(46, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt)); +} + +void INS_SYSCALL(void) +{ + unsigned int imm = 0; + + if(insArgc > 1) + instruction_error("Too many arguments"); + + if(insArgc == 1) + imm = insArgv[0]; + + imm &= 0xFFFFF; + + OUTINS( (imm << 6) | 12 ); +} + +void INS_XOR(void) +{ + unsigned int rd, rs, rt; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + if(atoiT[1] == T_INTEGER) + return INS_XORI(); + + rd = insArgv[0]; + rs = insArgv[0]; + rt = insArgv[1]; + } + else + { + if(atoiT[2] == T_INTEGER) + return INS_XORI(); + + rd = insArgv[0]; + rs = insArgv[1]; + rt = insArgv[2]; + } + + OUTINS( R_TYPE (0, rs, rt, rd, 0, 38) ); +} + +void INS_XORI(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = insArgv[2]; + } + + if(imm > 0xFFFF) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (14, rs, rt, imm) ); +} + +// ***** PSEUDO INSTRUCTIONS ***** + +void INS_B(void) +{ + unsigned int imm; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + imm = insArgv[0]; + + OUTINS( I_TYPE(4, 0, 0, compute_branch(imm)) ); // <- beq zero, zero, imm +} + +/*void INS_LI(void) +{ + unsigned int rd, imm; + unsigned short lopart, hipart; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(atoiT[1] == T_INTEGER && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + else + { + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + OUTINS( I_TYPE (9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff + } +}*/ + +void INS_LI(void) +{ + int i; + unsigned int rd, imm; + unsigned short lopart, hipart; + int unpredictable=0; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + else if(!unpredictable && !lopart) + OUTINS(I_TYPE(15, 0, rd, hipart)); + else + { + // if(lopart >= 0x8000) + // hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + + // OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff + OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff + } + +} + +void INS_LA(void) +{ + int i; + unsigned int rd, imm; + unsigned short lopart, hipart; + int unpredictable=0; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + imm = insArgv[1]; + + hipart = imm >> 16; + lopart = imm & 0xFFFF; + + if(!find_label_ok()) + { + if(cannotPredict.size == cannotPredict.pos) + { + cannotPredict.size += 128; + cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int)); + } + + cannotPredict.el[cannotPredict.pos++] = curPc; + } + + for(i = 0; i < cannotPredict.pos; i++) + { + if(curPc == cannotPredict.el[i]) + { + unpredictable = 1; + break; + } + } + + if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF) + OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm + //else if(!unpredictable && !lopart) + // OUTINS(I_TYPE(15, 0, rd, hipart)); + else + { + if(lopart >= 0x8000) + hipart++; + + OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16) + + OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff +// OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff + } + +} + +/*void INS_LA(void) +{ + INS_LI(); // The LI and LA pseudo-instructions are the same thing in SPASM +}*/ + +void INS_NOP(void) +{ + OUTINS(0); +} + +void INS_MOVE(void) +{ + unsigned int rd, rs; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rd = insArgv[0]; + rs = insArgv[1]; + + OUTINS( R_TYPE (0, rs, 0, rd, 0, 33) ); // addu $rd, $rs, $zero +} + +void INS_SUBI(void) +{ +// just like ADDI, but switches the sign of the immediate + + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = -insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = -insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (8, rs, rt, imm) ); +} + +void INS_SUBIU(void) +{ + unsigned int rt, rs, imm; + + if(insArgc < 2) + instruction_error("Not enough arguments"); + if(insArgc > 3) + instruction_error("Too many arguments"); + + if(insArgc == 2) + { + rt = insArgv[0]; + rs = insArgv[0]; + imm = -insArgv[1]; + } + else + { + rt = insArgv[0]; + rs = insArgv[1]; + imm = -insArgv[2]; + } + + if(imm > 0x7FFF && imm < 0xFFFF0000) + instruction_warning("Immediate is possibly out of range"); + + OUTINS( I_TYPE (9, rs, rt, imm) ); +} + +void INS_BEQZ(void) +{ + unsigned int rt, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(4, rt, 0, compute_branch(imm)) ); // <- beq zero, rt, imm +// OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) ); + +} + +void INS_BNEZ(void) +{ + unsigned int rt, imm; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + rt = insArgv[0]; + imm = insArgv[1]; + + OUTINS( I_TYPE(5,rt, 0, compute_branch(imm)) ); // <- bne zero, rt, imm +} + +void INS_BAL(void) +{ + unsigned int imm; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + imm = insArgv[0]; + + OUTINS( I_TYPE(1, 0, 17, compute_branch(imm)) ); //bgezal $zero, imm +} + +void INS_ORG(void) +{ + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + //if(!first_instruction) + // instruction_error("ORG is not the first instruction"); + + curPc = insArgv[0]; + startAddress = insArgv[0]; + org_found = 1; +} + +void INS_INCBIN(void) +{ + FILE *f; + char *path, *cp; + int sz; + + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + path = rawArgv[0]; + + if(*path == '"') + { + path++; + if((cp = strrchr(path, '"'))) + *cp = '\0'; + } + else if(*path == '\'') + { + path++; + if((cp = strrchr(path, '\''))) + *cp = '\0'; + } + + //printf("DEBUG(INCBIN): including %s\n", path); + + f = spasm_fopen(path, "rb"); + + if(!f) + instruction_error("Could not open \"%s\"", path); + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + if(curPass <= 0) + curPc += sz; + else + { + for(;sz;sz--) + OUTBYTE(fgetc(f)); + } + + fclose(f); +} + +void INS_DCB(void) +{ + unsigned int num, value; + + if(insArgc != 2) + instruction_error("Wrong number of arguments"); + + num = insArgv[0]; + value = insArgv[1]; + + if(num & (1<<31)) + { + instruction_warning("Negative number of values, ignoring instruction"); + + return; + } + + if(curPass <= 0) + curPc += num; + else + { + for(;num;num--) + OUTBYTE(value); + } +} + +void INS_DB(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTBYTE(insArgv[i] & 0xFF); + } +} + +void INS_DH(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTHALF(insArgv[i] & 0xFFFF); + } +} + +void INS_DW(void) +{ + int i; + + for(i = 0; i < insArgc; i++) + { + if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'') + OUTSTRING(rawArgv[i]); + else + OUTWORD(insArgv[i]); + } +} + +void INS_ALIGN(void) +{ + unsigned int unit; + unsigned int delta; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + unit = insArgv[0]; + + if(unit <= 0) + instruction_error("Alignment unit cannot be equal to zero or negative"); + + if((curPc % unit)) + { + delta = (unit - (curPc % unit)) % unit; + + if(curPass <= 0) + curPc += delta; + else + { + for(;delta;delta--) + OUTBYTE(0); + } + } +} + +void INS_INCLUDE(void) +{ + int i, l, o; + int sz; + int tsz; + FILE *f; + char *path; + char *cp; + char *newtext; + + if(insArgc != 1) + instruction_error("Wrong number of arguments"); + + if(curPass <= 0) + { + + path = rawArgv[0]; + + if(*path == '"') + { + path++; + if((cp = strrchr(path, '"'))) + *cp = '\0'; + } + else if(*path == '\'') + { + path++; + if((cp = strrchr(path, '\''))) + *cp = '\0'; + } + + f = spasm_fopen(path, "rb"); + + if(!f) + instruction_error("Could not open %s", path); + + //printf("DEBUG(INCLUDE): Including %s, line %d\n", path, line_number); + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + tsz = strlen(curText); + + newtext = malloc(sz + tsz + 1); + + for(i = 0, l = 1; l < line_number; i++) + { + if(curText[i] == '\n') + l++; + + newtext[i] = curText[i]; + } + + fread(&newtext[i], sizeof(char), sz, f); + + o = i+sz; + + if(newtext[i+sz-1] != '\n') + { + instruction_warning("No newline found at end of file %s", path); + newtext[o++] = '\n'; + } + + + for(; curText[i] != '\n'; i++); + + i++; + newtext[o] = '\0'; + + strcat(newtext, &curText[i]); + + free(curText); + curText = newtext; + } +} + +void INS_BLANK(void) +{ + +} + +void *get_instruction(char *name) +{ + int i; + + + for(i = 0; instruction_table[i].name; i++) + { + //printf("name = ^%s$, iname = ^%s$\n", name, instruction_table[i].name); + + if(strcasecmp(name, instruction_table[i].name) == 0) + { + // printf("di sranron\n"); + return instruction_table[i].func; + } + } + + return NULL; +} diff --git a/tools/spasm/opcode.h b/tools/spasm/opcode.h new file mode 100644 index 0000000..c2f6857 --- /dev/null +++ b/tools/spasm/opcode.h @@ -0,0 +1,108 @@ +#ifndef _SPASM_OPCODE_H +#define _SPASM_OPCODE_H + +// Helper functions + +void OUTSEEK(unsigned int position); +void OUTBYTE(unsigned char b); +void OUTHALF(unsigned short h); +void OUTWORD(unsigned int w); +void OUTINS(unsigned int instruction); +void OUTSTRING(char *string); +unsigned int OUTSIZE(void); +void *get_instruction(char *name); + +// Instructions + +void INS_ADD(void); +void INS_ADDI(void); +void INS_ADDIU(void); +void INS_ADDU(void); +void INS_AND(void); +void INS_ANDI(void); +void INS_BEQ(void); +void INS_BGEZ(void); +void INS_BGEZAL(void); +void INS_BGTZ(void); +void INS_BLEZ(void); +void INS_BLTZ(void); +void INS_BLTZAL(void); +void INS_BNE(void); +void INS_BREAK(void); +void INS_CFC(void); +void INS_COP(void); +void INS_CTC(void); +void INS_DIV(void); +void INS_DIVU(void); +void INS_J(void); +void INS_JAL(void); +void INS_JALR(void); +void INS_JR(void); +void INS_LB(void); +void INS_LBU(void); +void INS_LH(void); +void INS_LHU(void); +void INS_LUI(void); +void INS_LW(void); +void INS_LWC(void); +void INS_LWL(void); +void INS_LWR(void); +void INS_MFC(void); +void INS_MFHI(void); +void INS_MFLO(void); +void INS_MTC(void); +void INS_MTHI(void); +void INS_MTLO(void); +void INS_MULT(void); +void INS_MULTU(void); +void INS_NOR(void); +void INS_OR(void); +void INS_ORI(void); +void INS_SB(void); +void INS_SH(void); +void INS_SLL(void); +void INS_SLLV(void); +void INS_SLT(void); +void INS_SLTI(void); +void INS_SLTIU(void); +void INS_SLTU(void); +void INS_SRA(void); +void INS_SRAV(void); +void INS_SRL(void); +void INS_SRLV(void); +void INS_SUB(void); +void INS_SUBU(void); +void INS_SW(void); +void INS_SWC(void); +void INS_SWL(void); +void INS_SWR(void); +void INS_SYSCALL(void); +void INS_XOR(void); +void INS_XORI(void); + +// Pseudo instructions + +void INS_B(void); +void INS_LI(void); +void INS_LA(void); +void INS_NOP(void); +void INS_MOVE(void); +void INS_SUBI(void); +void INS_SUBIU(void); +void INS_BEQZ(void); +void INS_BNEZ(void); +void INS_BAL(void); +void INS_ORG(void); +void INS_INCBIN(void); +void INS_INCLUDE(void); +void INS_DCB(void); +void INS_DB(void); +void INS_DH(void); +void INS_DW(void); +void INS_ALIGN(void); + +// Fake instructions + +void INS_BLANK(void); + +#endif diff --git a/tools/spasm/parser.c b/tools/spasm/parser.c new file mode 100644 index 0000000..46fdccb --- /dev/null +++ b/tools/spasm/parser.c @@ -0,0 +1,478 @@ +#include "spasm.h" + +int atoiT[64]; + +static char *reg_names[] = +{ + "zero", + "at", + "v0", "v1", + "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", + "k0", "k1", + "gp", "sp", + "fp", + "ra", + NULL +}; + +static int regtoi(char *arg) +{ + int i; + + if(strcmp(arg, "s8") == 0) + arg = "fp"; + + for(i = 0; reg_names[i]; i++) + { + if(strcmp(arg, reg_names[i]) == 0) + return i; + } + + return -1; +} + +unsigned int asm_atoi(char *arg) +{ + unsigned int i; + + + if((i = regtoi(arg)) != -1) + { + atoiT[insArgc] = T_REGISTER; + return i; + } + else if(tolower((int)*arg) == 'r' &&( (strlen(arg) == 2 && isdigit((int)*(arg+1))) || + (strlen(arg) == 3 && isdigit((int)*(arg+1)) && isdigit((int)*(arg+2))))) + { + sscanf(arg+1, "%d", &i); + atoiT[insArgc] = T_REGISTER; + return i; + } + else if(*arg == '-' && *(arg+1) == '$' && isxdigit((unsigned int)*(arg+2)) ) + { + sscanf(arg+2, "%x", &i); + atoiT[insArgc] = T_INTEGER; + return -i; + } + else if(*arg == '$' ) + { + sscanf(arg+1, "%x", &i); + atoiT[insArgc] = T_INTEGER; + + return i; + } + else if(strcmp(arg, "*") == 0) + { + atoiT[insArgc] = T_INTEGER; + + return curPc; + } + else if(isalpha((unsigned int)*arg) || (*arg) == '_') + { + atoiT[insArgc] = T_LABEL; + + i = find_label(arg); + + return i; + } + + sscanf(arg, "%i", &i); + atoiT[insArgc] = T_INTEGER; + + return i; +} + +enum +{ + INITIAL, ARG_ENTER, COMMENT +}; + +char *spasm_parser(char *text, int pass) +{ + int i, j, l, m; + char linebuf[1024]; + char linebuf2[1024]; + char linebuf3[1024]; + char argbuf[1024]; + char *tok[256]; + int state = INITIAL; + int num_of_tok=0; + char *t; + curText = text; + unsigned int v; + +theBeginning: + i = 0; + curPass = pass; + org_found = 0; + first_instruction = 1; + line_number = 0; + text = curText; + + while(text[i]) + { + state = INITIAL; + + for(j = 0; text[i] && text[i] != '\n'; i++) + { + if(j < 1023 && text[i] != '\r') + linebuf[j++] = text[i]; + } + + line_number++; + rawArgc = insArgc = 0; + INSFUNC = NULL; + + if(text[i] == '\n') + i++; + + linebuf[j] = '\0'; + +//tokenize_line: + strcpy(linebuf2, linebuf); // Keep a second copy, we will need it later. + strcpy(linebuf3, linebuf); + curLine = linebuf3; + + char *a = linebuf; + char *s; + j = 0; + + num_of_tok = 0; + + for(m = 0; m < 256; m++) + tok[m]=NULL; + + while((s = strtok(a, " \t"))) + { + tok[num_of_tok++] = s; + a = NULL; + } + + tok[num_of_tok] = NULL; + + j = 0; + + + + while((s = tok[j])) + { + //printf("tok[%d] = %s\n", j, tok[j]); + + find_label_reset(); + + if(strlen(s) == 0) + { // A token with zero length is garbage, skip it + j++; + continue; + } + + //printf("s = ^%s\n", s); + + switch(state) + { + case INITIAL: // Initial case + + // Is this token a comment? + + if(*s == ';') + { + state = COMMENT; + break; + } + + // Is this token an instruction? + + if((INSFUNC = get_instruction(s))) + { + strncpy(curIns, s, 127); + s[127] = '\0'; + insArgc = 0; + state = ARG_ENTER; + argbuf[0] = '\0'; + + break; + } + + // Now we know it's a label + // There are two possible cases now + // - It's a label with a specified value: i.e. label EQU value + // - It's a label which has the current value of the program counter + + // First, we will check for EQU + + if((j + 2) < num_of_tok) + { // If there are not enough tokens in the line, don't bother checking for EQU. + if(strcasecmp(tok[j+1], "equ") == 0 || strcasecmp(tok[j+1], "=") == 0) + { // EQU found! Set label value to the one specified. + + // Set current instruction to EQU + strcpy(curIns, "equ"); + + // Remove quotes. Yes, in SPASM, values in EQU statements can have quotes + // even if they are numerical values! + + if( (t = strchr(tok[j+2], '"')) ) + { + *t = '\0'; + if( (t = strrchr(tok[j+2], '"')) ) + *t = '\0'; + } + + find_label_reset(); + + v = spasm_eval(tok[j+2]); + + if(strchr(tok[j+2], ';')) + state = COMMENT; + + if(!find_label_ok()) + assembler_error("Can't resolve expression for EQU statement"); + + if(find_label_ok()) + add_label_equ(tok[j], v); + else + add_label(tok[j], v); + + j+=2;// As we have processed the EQU, we need to jump two tokens ahead + break; + } + } + + // At this point, it is a label which has the current value of the program counter + + if((t = strrchr(tok[j], ':'))) + *t='\0'; // Remove trailing colon, if any. + + add_label(tok[j], curPc); + break; + + case ARG_ENTER: // Inside instruction + if(curPass == -1) + break; + + // Is this token a comment? + // If so, we do not have arguments anymore. + + + if(*s == ';') + { + state = COMMENT; + break; + } + + strcat(argbuf, s); + + int was_sp = 0; + int in_string = 0; + int stringt = 0; + char *argPlace = &linebuf2[ tok[j] - tok[0] ]; + + a = linebuf2; + + l = 0; + while(l < j && (argPlace = strtok(a, " \t"))) + { + a = NULL; + l++; + } + + while(*argPlace)argPlace++; + while(!(*argPlace))argPlace++; + + char arg[64]; + char *argp = arg; + int is_ok = 0; + int esc = 0; + + rawArgc = 0; + + // Emulate a bug in Hitmen's assembler + if(strcasecmp(curIns, "li") == 0) + { +argplace_li_remove_spaces_begin: + for(l = 0; argPlace[l]; l++) + { + if(argPlace[l] == ' ') + { + l++; + for(; argPlace[l]; l++) + argPlace[l-1] = argPlace[l]; + + argPlace[l-1] = '\0'; + + goto argplace_li_remove_spaces_begin; + } + } + } + + for(l = 0; argPlace[l] && rawArgc < 64; l++) + { + char c = argPlace[l]; + + if(in_string) + { + *(argp++) = c; + + if(!esc) + { + if(stringt == 0 && c == '"') + in_string = 0; + else if(stringt == 1 && c == '\'') + in_string = 0; + else if(c == '\\') + esc = 1; + } + else + esc = 0; + } + else + { + if(isalnum((unsigned int)c) || c == '_' || c == '$' || c == '.' || c == '*') + { + is_ok = 0; + + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + } + else if(c == '"') + { + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + in_string = 1; + stringt = 0; + esc = 0; + } + else if(c == '\'') + { + if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') || + (*(argp-1) == '$') || (*(argp-1) == '"') + || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*'))) + goto noMoreArgs; + + *(argp++) = c; + was_sp = 0; + in_string = 1; + stringt = 1; + esc = 0; + } + else if(c == ' ' || c == '\t') + { + is_ok = 0; + was_sp = 1; + } + else if(c == '+' || c == '-' || c == '>' || c == '<' + || c == '(' || c == ')' || c == '&' || c == '|' || c == '!') + { + is_ok = 0; + *(argp++) = c; + } + else if(c == ',') + { + *argp = '\0'; + insArgt[rawArgc] = 0; + strcpy(rawArgv[rawArgc++], arg); + argp = arg; + was_sp = 0; + is_ok = 1; + } + else if(c == ';' || c == '/') + { +// '/' added in order to emulate a very buggy behavior of SPASM that is needed +// in order to assemble the imbNES 1.3.2 sources without modifications. +// yes, imbNES has some UNDENOTATED comments! +// +// pearls such as +// +// lw v0,$1074(v1) // this line would be at $DFAC where the jump goes from the patch +// +// and... +// +// lw v0,$DFFC(v0) load the address to jump back to that was set in _patch_card +// +// It makes no sense, but hey it works in the original SPASM! + + + goto noMoreArgs; + } + else + { + instruction_error("Invalid character!\n"); + break; + } + } + } + +noMoreArgs: + if(!is_ok) + { + *argp = '\0'; + + char *fb = strchr(arg, '('); + char *sb = strrchr(arg, ')'); + + + int pa=(*arg != '"' && *arg != '\'' && fb && sb && fb<sb); + + if(pa) + { + *fb = '\0'; + *sb = '\0'; + insArgt[rawArgc+1] = 1; + strcpy(rawArgv[rawArgc+1], fb+1); + } + + insArgt[rawArgc] = 0; + strcpy(rawArgv[rawArgc++], arg); + + if(pa)rawArgc++; + } + + insArgc = 0; + + find_label_reset(); + + for(l = 0; l < rawArgc; l++, insArgc++) + insArgv[l] = spasm_eval(rawArgv[l]); + + if(curPass == 1 && !find_label_ok()) + instruction_error("Can't resolve expression"); + + goto theNextLine; + break; + + case COMMENT: // Inside comment + + break; + } + + a = NULL; + j++; + } + +theNextLine: + if(INSFUNC) + { + if(curPass>=0)INSFUNC(); + + if(strcasecmp(curIns, "include") == 0 && curPass == 0) + goto theBeginning; + + first_instruction = 0; + } + } + + return curText; +} diff --git a/tools/spasm/parser.h b/tools/spasm/parser.h new file mode 100644 index 0000000..72ecb0d --- /dev/null +++ b/tools/spasm/parser.h @@ -0,0 +1,29 @@ +#ifndef _SPASM_PARSER_H +#define _SPASM_PARSER_H + +enum +{ + T_INTEGER = 1, T_REGISTER = 2, T_LABEL = 3 +}; + +extern char *fileBuffer; +extern unsigned int fileBufferSize; + +extern char *curText; +extern char *curLine; + +unsigned int asm_atoi(char *arg); +char *spasm_parser(char *text, int pass); + +extern char curIns[128]; +extern unsigned int curInsArg; +extern unsigned int curInsArgT; +extern unsigned int insArgv[64]; +extern unsigned int insArgt[64]; +extern unsigned int insArgc; +extern int rawArgc; +extern char rawArgv[64][128]; +extern FILE *asmOut; +extern int line_number; +extern int atoiT[64]; +#endif diff --git a/tools/spasm/readme.txt b/tools/spasm/readme.txt new file mode 100644 index 0000000..8baa290 --- /dev/null +++ b/tools/spasm/readme.txt @@ -0,0 +1,120 @@ +nv-SPASM 0.34.1 +(c) 2014-2015 Giuseppe Gatta, a.k.a. nextvolume <tails92@gmail.com> +------------------------- + +1. What is this? +2. Features +3. How to use +4. License + +1. What is this? +-------------------------- + +nv-SPASM is a stand-alone MIPS assembler that can output either a little-endian flat binary file +or Sony PlayStation PS-X EXE executable (the default). + +It is simple and lightweight, and it is ideal for people who just want to play around +with assembly without bothering with too many details which would arise with +more advanced tools, such as those provided by the GNU project. + +nv-SPASM is also meant to be compatible with the Hitmen group's SPASM assembler, +which was an early tool for PlayStation homebrew programmers who had no access +to more advanced software development kits, such as Net Yaroze or PsyQ. + +While to most a tool like SPASM isn't as useful as it was back in the day, due to the +availability of freely usable software development kits like the PSXSDK, as already +mentioned before there are still some use cases for which it's more feasible +to use nv-SPASM instead of more advanced tools. + +And, why not? nv-SPASM is easier to hack and to learn from than more advanced, +complex designs. + +You could use it as the assembler for your virtual machine, or even embed this assembler inside it. + +1. What is this not? +-------------------------- + +nv-SPASM isn't a complex and full-fledged macro assembler; in fact it does not +even support macros currently. + +You can't easily mix code written in a high-level language with assembly code +with nv-SPASM, as nv-SPASM is unable to output object files that can be linked +against other object files. + +nv-SPASM does not currently support complex expressions, just very simple ones. + +2. Features +-------------------------- + +- Complete MIPS R3000 instruction set support, with the following pseudo-instructions + implemented: + + - B - unconditional branch + - LA, LI - load label/integer inside a register (LA and LI are the same thing in nv-SPASM) + - NOP - No operation + - MOVE - Copy the contents of a register into another + - SUBI - Subtract immediate from register + - SUBIU - Subtract immediate unsigned from register + - BEQZ - Branch if register is Equal to Zero + - BNEZ - Branch if register is Not Equal to Zero + - BAL - unconditional branch-and-link + - ORG - move start address to the desired address + it relocates every instruction, and must be the first instruction. + - INCLUDE - include a source file + - INCBIN - include a binary file + - DCB - declare a block of bytes + - DB - declare bytes and/or strings + - DH - declare half-words and/or strings + - DW - declare words and/or strings + - ALIGN - align incoming code to the specified boundary + +- Compatibility with the Hitmen group's SPASM assembler and thus the possibility + of assembling software developed with it, barring bugs in nv-SPASM or the fact + that the original source code may depend on bugs in the Hitmen's SPASM assembler + in order to assemble correctly. + +- Very simple expression support, single operator, two operands. + For laymen, this means that while for instance 3+3 is rendered as 6, + 3+3+2 is rendered as 5, as if it were 3+2. + This is coherent with the original SPASM assembler. + Supported operators are +, -, >>, <<, & and | + +- Automatic generation of Sony PlayStation PS-X EXE executables. + +- Symbol names (labels) can be treated just like normal numerical values, + the numerical value being either the program counter value at the moment + the label was met by the assembler, or a value set with an EQU or = statement. + + +3. How to use +-------------------------- + +nv-SPASM is really simple to use, and does not need any special explanation. +Therefore, it will suffice to report its usage banner. + +nv-spASM version 0.34.1 (c) 2014-2015 nextvolume +Assembler for the Sony PlayStation + +Usage: ./spasm [options] infile outfile + +Options: -b create binary file instead of PS-X EXE + -N do not pad executable + -S be case sensitive on file paths + +4. License +-------------------------- + +It is permitted to do (almost) anything with the nv-SPASM code and documentation. +The only restriction is that all existing copyright notices must remain intact, and +modifed versions must ship with the corresponding modified source code. + +fcaseopen.c is (C) 2009 Keith Bauer. Read fcaseopen.c for the license (BSD-style) + +5. Useful information +-------------------------- + +Hitmen's official website <http://www.hitmen-console.org> +- The README file for Hitmen's original SPASM assembler is a good read, + as this assembler tries to closely follow it. +- The GreenTro source code is another good resource, as it was developed with SPASM, + and thus it can be considered an example to write nv-SPASM assembly source code. diff --git a/tools/spasm/spasm.c b/tools/spasm/spasm.c new file mode 100644 index 0000000..c94f5a7 --- /dev/null +++ b/tools/spasm/spasm.c @@ -0,0 +1,167 @@ +#include "spasm.h" +#include "fcaseopen.h" + +int rawArgc = 0; +char rawArgv[64][128]; +char *curText; +char *curLine; +FILE *asmOut; +int line_number; + +char *fileBuffer; +unsigned int fileBufferSize; + +int open_case_sensitive = 0; + +static void titlecard(void) +{ + printf("nv-spASM version 0.34.1 (c) 2014-2015 nextvolume\n"); +} + +static void usage(char *prog_name) +{ + titlecard(); + printf("Assembler for the Sony PlayStation\n"); + printf("\n"); + printf("Usage: %s [options] infile outfile\n", prog_name); + printf("\n"); + printf("Options: -b create binary file instead of PS-X EXE\n"); + printf(" -N do not pad executable\n"); + printf(" -S be case sensitive on file paths\n"); + printf("\n"); +} + +FILE *spasm_fopen(const char *path, const char *mode) +{ + if(open_case_sensitive) + return fopen(path, mode); + + return fcaseopen(path, mode); +} + +int main(int argc, char *argv[]) +{ + int sz; + int farg=1; + int bfile=0; + int no_newline=0; + int no_pad=0; + + if(argc < 3) + { + usage(argv[0]); + return EXIT_SUCCESS; + } + + while(argv[farg][0] == '-') + { + if(strcmp(argv[farg], "-b") == 0) + bfile = 1; // Generate binary file + else if(strcmp(argv[farg], "-N") == 0) + no_pad = 1; + else if(strcmp(argv[farg], "-S") == 0) + open_case_sensitive = 1; + else + { + usage(argv[0]); + return EXIT_FAILURE; + } + + farg++; + } + + FILE *f = spasm_fopen(argv[farg], "rb"); + + if(!f) + { + printf("Could not open file %s for reading! Exiting.\n", argv[farg]); + return EXIT_FAILURE; + } + + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + + fileBufferSize = sz + 2; // two newline bytes + two NUL bytes + fileBuffer = malloc(fileBufferSize); + fread(fileBuffer, sizeof(char), sz, f); + + if(fileBuffer[sz-1] != '\n') + no_newline = 1; + + fileBuffer[sz] = '\n'; + fileBuffer[sz+1] = '\0'; + + //fileBuffer = process_includes(fileBuffer); + + fclose(f); + + asmOut = fopen(argv[farg+1], "wb"); + + if(!asmOut) + { + printf("Could not open file %s for writing! Exiting.\n", argv[farg+1]); + return EXIT_FAILURE; + } + + startAddress = 0x80010000; + + titlecard(); + + if(no_newline) + printf("Warning: No newline found at end of file.\n"); + + + printf("\nPass 1\n"); + codegen_init(); + fileBuffer = spasm_parser(fileBuffer, -1); + fileBuffer = spasm_parser(fileBuffer, 0); + + /*if(!org_found) + { + printf("Warning: no ORG directive found... defaulting to $80010000\n"); + startAddress = 0x80010000; + }*/ + + curPc = startAddress; + + printf("Pass 2\n"); + printf("Writing output file %s ...\n", argv[farg+1]); + + if(!bfile) + OUTSEEK(0x800); + + fileBuffer = spasm_parser(fileBuffer, 1); + + if(!bfile) + { + OUTSEEK(0x0); + OUTSTRING("\"PS-X EXE\""); // PSX-EXE magic + OUTSEEK(0x10); + OUTWORD(startAddress); // Initial program counter + OUTSEEK(0x18); + OUTWORD(startAddress); // Text section start address + OUTSEEK(0x30); + OUTWORD(0x801FFFF0); // Initial stack pointer + + sz = OUTSIZE(); + + + if(!no_pad && (sz & 2047)) // Pad executable + { + sz |= 2047; + sz++; + OUTSEEK(sz-1); + OUTBYTE(0); + } + + OUTSEEK(0x1C); + OUTWORD(sz - 0x800); // Output size of text section + } + + printf("\nAssembly complete\n\n"); + + fclose(asmOut); + + return EXIT_SUCCESS; +} diff --git a/tools/spasm/spasm.h b/tools/spasm/spasm.h new file mode 100644 index 0000000..b1ea821 --- /dev/null +++ b/tools/spasm/spasm.h @@ -0,0 +1,20 @@ +#ifndef _SPASM_H +#define _SPASM_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <ctype.h> +#include <stdarg.h> +#include "error.h" +#include "codegen.h" +#include "parser.h" +#include "opcode.h" +#include "spasm.h" +#include "eval.h" + +FILE *spasm_fopen(const char *path, const char *mode); + +#endif diff --git a/tools/systemcnf.c b/tools/systemcnf.c new file mode 100644 index 0000000..301936d --- /dev/null +++ b/tools/systemcnf.c @@ -0,0 +1,50 @@ +/* + * systemcnf + * + * Small program that outputs a system.cnf file to standard output + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +int tcb = 4; +int event = 16; +unsigned int stack = 0x801FFFF0; + +int main(int argc, char *argv[]) +{ + int x; + + if(argc < 2) + { + printf("systemcnf\n"); + printf("usage: systemcnf exe_name [tcb] [event] [stack_addr]\n"); + printf("\n"); + printf("This programs outputs a system.cnf file to standard output\n"); + printf("Only the exe_name argument is necessary because if the others\n"); + printf("are missing, the default values are used.\n"); + printf("Specify stack_addr in hexadecimal, without prefixes.\n"); + return 0; + } + + if(argc >= 3) + tcb = atoi(argv[2]); + + if(argc >= 4) + event = atoi(argv[3]); + + if(argc >= 5) + sscanf(argv[4],"%x",&stack); + + for(x=0;x<strlen(argv[1]);x++) + argv[1][x] = toupper((int)argv[1][x]); + + printf("BOOT = cdrom:%s;1\n", argv[1]); + printf("TCB = %d\n", tcb); + printf("EVENT = %d\n", event); + printf("STACK = %X", stack); + + return 0; +} diff --git a/tools/tim2bmp.c b/tools/tim2bmp.c new file mode 100644 index 0000000..ccb0c99 --- /dev/null +++ b/tools/tim2bmp.c @@ -0,0 +1,390 @@ +/*
+ * TIM2BMP
+ * Converts TIM, emulator save state VRAM data and raw PSX data
+ * to Windows bitmap format
+ *
+ * Written by Giuseppe Gatta (a.k.a. nextvolume), part of PSXSDK
+ */
+
+#include <stdio.h> +#include <stdlib.h> +#include <string.h>
+#include <zlib.h>
+
+#define PCSX_1_5_SAVESTATE_SUPPORT
+
+z_stream strm;
+unsigned short *tim_clut;
+
+typedef struct
+{
+ unsigned int clut_off;
+ unsigned int data_off;
+ unsigned short w; // Width in 16-bit unit pixels
+ unsigned short real_w; // Real width in pixels
+ unsigned short h;
+ unsigned char bpp;
+ unsigned char compr; // Compression - 0 = normal, 1 = GZIP
+ unsigned char has_clut;
+ unsigned short *clut;
+}tim2bmp_info;
+
+tim2bmp_info tim_info;
+
+#include "endian.c"
+ +int mpink_flag = 0; +
+void rgbpsx_to_rgb24(unsigned short psx_c, unsigned char *r,
+ unsigned char *g, unsigned char *b)
+{
+ *r = (psx_c & 31) << 3;
+ *g = ((psx_c >> 5)&31) << 3;
+ *b = ((psx_c >> 10) &31) << 3; + + if(mpink_flag && !(psx_c & 0x8000) && *r == 0 && *g == 0 && *b == 0) + { + *r = 255; + *g = 0; + *b = 255; + }
+}
+
+// Returns number of bytes to round image row with.
+int write_bitmap_headers(FILE *f, int w, int h, int bpp)
+{
+ int x;
+ int r;
+ int ret;
+
+ if(bpp == 16)
+ bpp = 24;
+
+ fputc('B', f);
+ fputc('M', f);
+
+ // Calculate and write size of bitmap
+
+ if(bpp == 24)
+ r = w * 3;
+ else if(bpp == 8)
+ r = w;
+ else if(bpp == 4)
+ r = w / 2;
+
+ ret = r;
+
+ if(r & 3)
+ {
+ r|=3;
+ r++;
+ }
+
+ ret = r-ret;
+
+ x=r*h;
+ x+=54;
+
+ if(bpp == 8)
+ x+= 1024;
+ else if(bpp == 4)
+ x+= 64;
+
+ write_le_dword(f, x);
+
+ // Write bfReserved1 and bfReserved2 as zero
+ write_le_dword(f, 0);
+
+ // Calculate and write data offset in file
+
+ x = 54;
+
+ if(bpp == 8)
+ x+= 1024;
+ else if(bpp == 4)
+ x+= 64;
+
+ write_le_dword(f, x);
+
+ write_le_dword(f, 40);
+ write_le_dword(f, w); // Width
+ write_le_dword(f, h); // Height
+ write_le_word(f, 1);
+ write_le_word(f, bpp); // Bits Per Pixel
+ write_le_dword(f, 0);
+ write_le_dword(f, r * h); // Image data size
+ write_le_dword(f, 0);
+ write_le_dword(f, 0);
+ write_le_dword(f, 0);
+ write_le_dword(f,0);
+
+ return ret;
+}
+
+int tim2bmp_read_tim(char *ip, tim2bmp_info *t)
+{
+ int tim_pmode;
+ //int tim_w, tim_h; + int /* tim_x, tim_y, tim_cx, tim_cy, */ tim_cw, tim_ch;
+ //int bl;
+ int x;
+ FILE *i = fopen(ip, "rb");
+
+ fseek(i,0,SEEK_SET);
+
+ if(read_le_dword(i) != 0x10)
+ {
+ fclose(i);
+ return -1;
+ }
+
+ tim_pmode = read_le_dword(i);
+ t->has_clut = (tim_pmode & 8) ? 1 : 0;
+ tim_pmode &= 7;
+
+ if(tim_pmode == 0) t->bpp = 4;
+ else if(tim_pmode == 1) t->bpp = 8;
+ else if(tim_pmode == 2) t->bpp = 16;
+ else if(tim_pmode == 3) t->bpp = 24;
+
+ if(t->has_clut)
+ {
+ t->clut_off = 8;
+ /* bl = */ read_le_dword(i);
+ /* tim_cx = */ read_le_word(i);
+ /* tim_cy = */ read_le_word(i);
+ tim_cw = read_le_word(i);
+ tim_ch = read_le_word(i);
+
+ t->clut = malloc((tim_cw * tim_ch) * sizeof(short));
+
+ for(x = 0; x < (tim_cw * tim_ch); x++)
+ t->clut[x] = read_le_word(i);
+ }
+
+ /* bl = */ read_le_dword(i);
+
+ // Read framebuffer X,Y coordinates
+
+ /* tim_x = */ read_le_word(i);
+ /* tim_y = */ read_le_word(i);
+
+ // Read width and height
+ t->w = read_le_word(i); // Fix this for 4bpp and 8bpp images !
+ t->h = read_le_word(i);
+
+ switch(tim_pmode)
+ {
+ case 0: t->real_w = t->w * 4; break;
+ case 1: t->real_w = t->w * 2; break;
+ case 2:
+ t->real_w = t->w;
+ break;
+ }
+
+ t->data_off = ftell(i);
+ t->compr = 0;
+
+ fclose(i);
+
+ return 1;
+}
+
+int tim2bmp_read_pcsx15(char *ip, tim2bmp_info *t)
+{
+ t->w = 1024;
+ t->real_w = 1024;
+ t->h = 512;
+ t->bpp = 16;
+ t->has_clut = 0;
+ t->clut_off = 0;
+ t->data_off = 0x2996C0;
+ t->compr = 1; + + return 1;
+}
+
+void tim2bmp_convert_image_data(char *ip, char *fp, tim2bmp_info *t)
+{
+ int row_round;
+ int y,x; + //int z;
+ int tim_row_off;
+ unsigned short c;
+ unsigned char r, g, b;
+ gzFile gzf;
+ //unsigned char test[17];
+ FILE *i = fopen(ip, "rb");
+ FILE *f = fopen(fp, "wb");
+
+ if(t->compr == 1)
+ gzf = gzopen(ip, "rb");
+
+ write_bitmap_headers(f, t->real_w, t->h, t->bpp);
+
+ if(t->has_clut)
+ {
+ if(t->bpp == 4) // 4bpp
+ {
+ for(x = 0; x < 16; x++)
+ {
+ rgbpsx_to_rgb24(t->clut[x], &r, &g, &b);
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ fputc(0, f);
+ }
+ }
+ else if(t->bpp == 8) // 8 bpp
+ {
+ for(x = 0; x < 256; x++)
+ {
+ rgbpsx_to_rgb24(t->clut[x], &r, &g, &b);
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ fputc(0, f);
+ }
+ }
+ }
+ else
+ {
+ if(t->bpp == 4) fseek(f, 64, SEEK_CUR);
+ else if(t->bpp == 8) fseek(f, 1024, SEEK_CUR);
+ }
+
+ if(t->bpp == 16)
+ y = (t->real_w * 24) / 8;
+ else
+ y = (t->real_w * t->bpp) / 8;
+
+// printf("y = %d\n", y);
+ row_round = y;
+// printf("row_round = %d\n", y);
+
+ if(row_round & 3)
+ {
+ row_round |= 3;
+ row_round++;
+ }
+// printf("row_round = %d\n", y);
+
+ row_round -= y;
+// printf("row_round = %d\n", row_round);
+
+ for(y = 0; y < t->h; y++)
+ {
+ tim_row_off = (t->w * 2) * ((t->h - 1)-y);
+
+ if(t->compr == 1)
+ gzseek(gzf, t->data_off + tim_row_off, SEEK_SET);
+ else
+ fseek(i, t->data_off + tim_row_off, SEEK_SET);
+
+ // printf("ERRNO SHY = %s\n", gzerror(gzf, &x));
+
+
+ for(x = 0; x < t->w; x++)
+ {
+ if(t->compr == 1)
+ {
+ gzread(gzf, &b, 1);
+ c = b;
+ gzread(gzf, &b, 1);
+ c|=b<<8;
+ }
+ else
+ c = read_le_word(i);
+
+ switch(t->bpp)
+ {
+ case 4:
+ fputc(((c >> 4) & 0xf) | ((c & 0xf) << 4), f);
+ fputc(((c >> 12) & 0xf) | (((c>>8)&0xf)<<4), f);
+ break;
+ case 8:
+ write_le_word(f, c);
+ break;
+ case 16:
+ rgbpsx_to_rgb24(c, &r, &g, &b);
+
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ break;
+ }
+ }
+
+ for(x = 0; x < row_round; x++)
+ fputc(0, f);
+ }
+
+ fclose(i);
+ fclose(f);
+ if(t->compr == 1) gzclose(gzf);
+}
+
+int main(int argc, char *argv[])
+{
+ //int x, y; + int x;
+ FILE *i;
+ //int bl;
+ /*int tim_w, tim_h, tim_x, tim_y, tim_cx, tim_cy, tim_cw, tim_ch;
+ int row_round;
+ unsigned short c;*/
+ //unsigned char r, g, b;
+ /*int tim_pdata_fpos;
+ int tim_row_off;
+ int tim_pmode;
+ int tim_has_clut;
+ int actual_w;
+ int bmp_bpp;*/ + int r;
+ tim2bmp_info *t = &tim_info;
+
+ if(argc < 2)
+ {
+ printf("tim2bmp - converts a TIM image to a bitmap\n");
+ printf("usage: tim2bmp <intim> <outbmp> [options]\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -o=<offset>\n"); + printf(" -mpink - Convert transparency to magic pink\n");
+ printf("\n");
+ return -1;
+ }
+
+ /*strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ r = deflateInit(&strm, 1);*/
+ //printf("r = %d, Z_OK = %d\n", r, Z_OK);
+ + for(x = 3; x < argc; x++) + { + if(strcmp(argv[x], "-mpink") == 0) + mpink_flag = 1; + } +
+ i = fopen(argv[1], "rb");
+
+ if(i == NULL)
+ {
+ printf("Couldn't open specified TIM file for reading.\n");
+ return -1;
+ }
+
+ fclose(i);
+
+ r = tim2bmp_read_tim(argv[1], &tim_info);
+
+ if(r != 1)
+ r = tim2bmp_read_pcsx15(argv[1], &tim_info);
+
+ if(argc > 2)
+ tim2bmp_convert_image_data(argv[1], argv[2], t);
+
+ return 0;
+}
diff --git a/tools/vag2wav.c b/tools/vag2wav.c new file mode 100755 index 0000000..09b474a --- /dev/null +++ b/tools/vag2wav.c @@ -0,0 +1,202 @@ +/* + * vag2wav + * + * Converts a PlayStation VAG sound file to a WAV file + * + * Based on VAG-Depack 0.1 by bITmASTER + */ + +#include <stdio.h> +#include <string.h> + +#include "endian.c" + +double f[5][2] = { { 0.0, 0.0 }, + { 60.0 / 64.0, 0.0 }, + { 115.0 / 64.0, -52.0 / 64.0 }, + { 98.0 / 64.0, -55.0 / 64.0 }, + { 122.0 / 64.0, -60.0 / 64.0 } }; + +double samples[28]; + +int main( int argc, char *argv[] ) +{ + FILE *vag, *pcm; + char vag_name[17]; + int predict_nr, shift_factor, flags; + int i; + int d, s; + static double s_1 = 0.0; + static double s_2 = 0.0; + int sz; + unsigned int samp_freq; + unsigned int data_size; + + if(argc < 3) + { + printf("vag2wav - Convert a PlayStation VAG sound file to a WAV file\n"); + printf("usage: vag2wav [vag] [wav file]\n"); + printf("\n"); + printf("This utility is based on PSX VAG-Depack by bITmASTER\n"); + return( -1 ); + } + + vag = fopen( argv[1], "rb" ); + + fread(vag_name, sizeof(char), 4, vag); + + if(vag == NULL) + { + printf("Can't open %s. Aborting.\n", argv[1]); + return -1; + } + + if(strncmp(vag_name, "VAGp", 4)) + { + printf("%s is not in VAG format. Aborting.\n", argv[1]); + return -1; + } + + fseek(vag, 4, SEEK_SET); + i = fgetc(vag) << 24; + i |= fgetc(vag) << 16; + i |= fgetc(vag) << 8; + i |= fgetc(vag); + + printf("Version: %x\n", i); + + fseek(vag, 12, SEEK_SET); + data_size = fgetc(vag) << 24; + data_size |= fgetc(vag) << 16; + data_size |= fgetc(vag) << 8; + data_size |= fgetc(vag); + + printf("Data size: %d bytes\n", data_size); + + fseek(vag, 32, SEEK_SET); + fread(vag_name, sizeof(char), 16, vag); + vag_name[16] = 0; + + printf("Name: %s\n", vag_name); + + fseek(vag, 16, SEEK_SET); + + samp_freq = fgetc(vag)<<24; + samp_freq|=fgetc(vag)<<16; + samp_freq|=fgetc(vag)<<8; + samp_freq|=fgetc(vag); + + printf("Sampling frequency: %d\n", samp_freq); + + fseek( vag, 64, SEEK_SET ); + + /* strcpy( fname, argv[1] ); + p = strrchr( fname, '.' ); + p++; + strcpy( p, "PCM" );*/ + pcm = fopen( argv[2], "wb" ); + + if ( pcm == NULL ) { + printf( "canīt write output file\n" ); + return( -8 ); + } + + +// Write header chunk + fprintf(pcm, "RIFF"); +// Skip file size field for now + fseek(pcm, 4, SEEK_CUR); + + fprintf(pcm, "WAVE"); +// Write fmt chunk + fprintf(pcm, "fmt "); +// Write chunk 1 size in little endian format + fputc(0x10, pcm); + fputc(0, pcm); + fputc(0, pcm); + fputc(0, pcm); +// Write audio format (1 = PCM) + fputc(1, pcm); + fputc(0, pcm); +// Number of channels (1) + fputc(1, pcm); + fputc(0, pcm); +// Write sample rate + fputc((samp_freq & 0xff), pcm); + fputc((samp_freq >> 8) & 0xff, pcm); + fputc(0, pcm); + fputc(0, pcm); +// Write byte rate (SampleRate * NumChannels * BitsPerSample/8) +// That would be 44100*1*(16/8), thus 88200. + fputc(((samp_freq*2) & 0xff), pcm); + fputc(((samp_freq*2) >> 8) & 0xff, pcm); + fputc(((samp_freq*2) >> 16) & 0xff, pcm); + fputc(((samp_freq*2) >> 24) & 0xff, pcm); +// Write block align (NumChannels * BitsPerSample/8), thus 2 + fputc(2, pcm); + fputc(0, pcm); +// Write BitsPerSample + fputc(16, pcm); + fputc(0, pcm); + +// Write data chunk + fprintf(pcm, "data"); + +// Skip SubChunk2Size, we will return to it later + fseek(pcm, 4, SEEK_CUR); + +// Now write data... + + while( ftell(vag) < (data_size+48)) { + predict_nr = fgetc( vag ); + shift_factor = predict_nr & 0xf; + predict_nr >>= 4; + flags = fgetc( vag ); // flags + if ( flags == 7 ) + break; + for ( i = 0; i < 28; i += 2 ) { + d = fgetc( vag ); + s = ( d & 0xf ) << 12; + if ( s & 0x8000 ) + s |= 0xffff0000; + samples[i] = (double) ( s >> shift_factor ); + s = ( d & 0xf0 ) << 8; + if ( s & 0x8000 ) + s |= 0xffff0000; + samples[i+1] = (double) ( s >> shift_factor ); + } + + for ( i = 0; i < 28; i++ ) { + samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]; + s_2 = s_1; + s_1 = samples[i]; + d = (int) ( samples[i] + 0.5 ); + fputc( d & 0xff, pcm ); + fputc( d >> 8, pcm ); + } + } + +// Get file size + sz = ftell(pcm); + +// Now write ChunkSize + fseek(pcm, 4, SEEK_SET); + + fputc((sz-8) & 0xff, pcm); + fputc(((sz-8)>>8)&0xff, pcm); + fputc(((sz-8)>>16)&0xff,pcm); + fputc(((sz-8)>>24)&0xff,pcm); + +// Now write Subchunk2Size + fseek(pcm, 40, SEEK_SET); + + fputc((sz-44) & 0xff, pcm); + fputc(((sz-44)>>8)&0xff, pcm); + fputc(((sz-44)>>16)&0xff,pcm); + fputc(((sz-44)>>24)&0xff,pcm); + + fclose( pcm ); + fclose( vag ); + return( 0 ); +} + diff --git a/tools/wav2vag.c b/tools/wav2vag.c new file mode 100644 index 0000000..e78b84c --- /dev/null +++ b/tools/wav2vag.c @@ -0,0 +1,431 @@ +/* + * wav2vag + * + * Converts a WAV file to a PlayStation VAG file. + * Based on PSX VAG-Packer 0.1 by bITmASTER. + * + */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#include "endian.c" + +#define BUFFER_SIZE 128*28 + +short wave[BUFFER_SIZE]; + +void find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor ); +void pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor ); +void fputi( int d, FILE *fp ); + +int main( int argc, char *argv[] ) +{ + FILE *fp, *vag; + short *ptr; + double d_samples[28]; + short four_bit[28]; + int predict_nr; + int shift_factor; + int flags; + int size; + int i, j, k; + unsigned char d; + char s[4]; + int chunk_data; + short e; + int sample_freq = 0, sample_len; + char internal_name[16]; + int enable_looping = 0; + int raw_output = 0; + int sraw = 0; + short sample_size; + unsigned char c; + + if (argc < 3) + { + printf("wav2vag - Convert a WAV file to a PlayStation VAG sound file\n"); + printf("usage: wav2vag [wav] [vag] <options>\n"); + printf("\n"); + printf("WAV files must have one channel (mono)\n"); + printf("WAV data format must be either unsigned 8-bit or signed 16-bit PCM\n"); + printf("\n"); + printf("Options:\n"); + printf(" -L - Make a looping sample (when it ends it is played again)\n"); + printf(" -name=<name> - Set sample name\n"); + printf(" -raw - Output only data, without VAG header\n"); + printf(" -sraw8 - Source is RAW data, in 8-bit format\n"); + printf(" -sraw16 - Source is RAW data, in 16-bit format\n"); + printf(" -freq=<freq> - Force frequency in output VAG\n"); + printf("\n"); + printf("This utility is based on PSX VAG-Packer by bITmASTER\n"); + return -1; + } + + for(i = 0; i < sizeof(internal_name); i++) + internal_name[i] = 0; + + strcpy(internal_name, "PSXSDK"); + + fp = fopen(argv[1], "rb"); + if (fp == NULL) + { + printf("Canīt open %s. Aborting.\n", argv[1]); + return -2; + } + + for(i = 3; i < argc; i++) + { + if(strcmp(argv[i], "-L") == 0) + enable_looping = 1; + + if(strncmp(argv[i], "-name=",6) == 0) + strncpy(internal_name, argv[i]+6, 15); + + if(strcmp(argv[i], "-raw") == 0) + raw_output = 1; + + if(strcmp(argv[i], "-sraw8") == 0) + { + fseek(fp, 0, SEEK_END); + sample_len = ftell(fp); + fseek(fp, 0, SEEK_SET); + sample_size = 8; + sample_freq = 44010; + sraw = 1; + } + + if(strcmp(argv[i], "-sraw16") == 0) + { + fseek(fp, 0, SEEK_END); + sample_len = ftell(fp) / 2; + fseek(fp, 0, SEEK_SET); + sample_size = 16; + sample_freq = 44010; + sraw = 1; + } + + if(strncmp(argv[i], "-freq=", 6) == 0) + { + sscanf(argv[i], "-freq=%d", &sample_freq); + } + } + + + if(sraw == 1) + goto convert_to_vag; + + fread(s, 1, 4, fp); + if (strncmp(s, "RIFF", 4)) + { + printf("%s is not in WAV format\n", argv[1]); + return -3; + } + + fseek(fp, 8, SEEK_SET); + fread(s, 1, 4, fp); + + if (strncmp(s, "WAVE", 4)) + { + printf("%s is not in WAV format\n", argv[1]); + return -3; + } + + fseek(fp, 8 + 4, SEEK_SET); + fread(s, 1, 4, fp); + + if (strncmp(s, "fmt", 3)) + { + printf("%s is not in WAV format\n", argv[1]); + return -3; + } + + // fread(&chunk_data, sizeof(int), 1, fp); + chunk_data = read_le_dword(fp); + chunk_data += ftell(fp); + + // fread(&e, 2, 1, fp); + e = read_le_word(fp); + + if (e!=1) + { + printf("No PCM found in %s. Aborting.\n", argv[1]); + return -4; + } + +// fread(&e, 2, 1, fp); + e = read_le_word(fp); + + if (e!=1) + { + //printf( "must be MONO\n" ); + printf("WAV file must have only one channel. Aborting.\n"); + return -5; + } + + if(sample_freq != 0) + fseek(fp, 4, SEEK_CUR); + else + //fread(&sample_freq, 4, 1, fp); + sample_freq = read_le_dword(fp); + + fseek(fp, 4 + 2, SEEK_CUR); + +// fread(&sample_size, 2, 1, fp); + sample_size = read_le_word(fp); + + /* if (e!=16) + { + //printf( "only 16 bit samples\n" ); + printf("The size of the samples of the WAV file must be 16-bit." + "Aborting.\n"); + return -6; + }*/ + + fseek(fp, chunk_data, SEEK_SET); + + fread(s, 1, 4, fp); + + if (strncmp(s, "data", 4)) + { + printf("No data chunk in %s. Aborting.\n", argv[1]); + return -7; + } + + // fread(&sample_len, 4, 1, fp); + sample_len = read_le_dword(fp); + + if(sample_size == 16) + sample_len /= 2; + + /*strcpy( fname, argv[1] ); + p = strrchr( fname, '.' ); + p++; + strcpy( p, "vag" );*/ +convert_to_vag: + vag = fopen(argv[2], "wb"); + + if (vag == NULL) + { + printf("Can't open output file. Aborting.\n"); + return -8; + } + +/* strcpy(internal_name, "PSXSDK"); + + for(i = 3; i < argc; i++) + { + if(strcmp(argv[i], "-L") == 0) + enable_looping = 1; + + if(strncmp(argv[i], "-name=",6) == 0) + strncpy(internal_name, argv[i]+6, 15); + + if(strcmp(argv[i], "-raw") == 0) + raw_output = 1; + } +*/ + + if(raw_output == 0) + { + fprintf( vag, "VAGp" ); // ID + fputi( 0x20, vag ); // Version + fputi( 0x00, vag ); // Reserved + size = sample_len / 28; + if( sample_len % 28 ) + size++; + fputi( 16 * ( size + 2 ), vag ); // Data size + fputi( sample_freq, vag ); // Sampling frequency + + for ( i = 0; i < 12; i++ ) // Reserved + fputc( 0, vag ); + + fwrite(internal_name, sizeof(char), 16, vag); + + for( i = 0; i < 16; i++ ) + fputc( 0, vag ); // ??? + } + + if(enable_looping) + flags = 6; + else + flags = 0; + + while( sample_len > 0 ) { + size = ( sample_len >= BUFFER_SIZE ) ? BUFFER_SIZE : sample_len; + + if(sample_size == 8) + { + for(i = 0; i < size; i++) + { + c = fgetc(fp); + wave[i] = c; + wave[i] ^= 0x80; + wave[i] <<= 8; + } + } + else + { + // fread( wave, sizeof( short ), size, fp ); + for(i = 0; i < size; i++) + wave[i] = read_le_word(fp); + } + + i = size / 28; + if ( size % 28 ) { + for ( j = size % 28; j < 28; j++ ) + wave[28*i+j] = 0; + i++; + } + + for ( j = 0; j < i; j++ ) { // pack 28 samples + ptr = wave + j * 28; + find_predict( ptr, d_samples, &predict_nr, &shift_factor ); + pack( d_samples, four_bit, predict_nr, shift_factor ); + d = ( predict_nr << 4 ) | shift_factor; + fputc( d, vag ); + fputc( flags, vag ); + for ( k = 0; k < 28; k += 2 ) { + d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf ); + fputc( d, vag ); + } + sample_len -= 28; + if ( sample_len < 28 && enable_looping == 0) + flags = 1; + + if(enable_looping) + flags = 2; + } + } + + fputc( ( predict_nr << 4 ) | shift_factor, vag ); + + if(enable_looping) + fputc(3, vag); + else + fputc( 7, vag ); // end flag + + for ( i = 0; i < 14; i++ ) + fputc( 0, vag ); + + fclose( fp ); + fclose( vag ); +// getch(); + return( 0 ); +} + + +static double f[5][2] = { { 0.0, 0.0 }, + { -60.0 / 64.0, 0.0 }, + { -115.0 / 64.0, 52.0 / 64.0 }, + { -98.0 / 64.0, 55.0 / 64.0 }, + { -122.0 / 64.0, 60.0 / 64.0 } }; + + + +void find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor ) +{ + int i, j; + double buffer[28][5]; + double min = 1e10; + double max[5]; + double ds; + int min2; + int shift_mask; + static double _s_1 = 0.0; // s[t-1] + static double _s_2 = 0.0; // s[t-2] + double s_0, s_1, s_2; + + for ( i = 0; i < 5; i++ ) { + max[i] = 0.0; + s_1 = _s_1; + s_2 = _s_2; + for ( j = 0; j < 28; j ++ ) { + s_0 = (double) samples[j]; // s[t-0] + if ( s_0 > 30719.0 ) + s_0 = 30719.0; + if ( s_0 < - 30720.0 ) + s_0 = -30720.0; + ds = s_0 + s_1 * f[i][0] + s_2 * f[i][1]; + buffer[j][i] = ds; + if ( fabs( ds ) > max[i] ) + max[i] = fabs( ds ); +// printf( "%+5.2f\n", s2 ); + s_2 = s_1; // new s[t-2] + s_1 = s_0; // new s[t-1] + } + + if ( max[i] < min ) { + min = max[i]; + *predict_nr = i; + } + if ( min <= 7 ) { + *predict_nr = 0; + break; + } + + } + +// store s[t-2] and s[t-1] in a static variable +// these than used in the next function call + + _s_1 = s_1; + _s_2 = s_2; + + for ( i = 0; i < 28; i++ ) + d_samples[i] = buffer[i][*predict_nr]; + +// if ( min > 32767.0 ) +// min = 32767.0; + + min2 = ( int ) min; + shift_mask = 0x4000; + *shift_factor = 0; + + while( *shift_factor < 12 ) { + if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) ) + break; + (*shift_factor)++; + shift_mask = shift_mask >> 1; + } + +} + +void pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor ) +{ + double ds; + int di; + double s_0; + static double s_1 = 0.0; + static double s_2 = 0.0; + int i; + + for ( i = 0; i < 28; i++ ) { + s_0 = d_samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]; + ds = s_0 * (double) ( 1 << shift_factor ); + + di = ( (int) ds + 0x800 ) & 0xfffff000; + + if ( di > 32767 ) + di = 32767; + if ( di < -32768 ) + di = -32768; + + four_bit[i] = (short) di; + + di = di >> shift_factor; + s_2 = s_1; + s_1 = (double) di - s_0; + + } +} + +void fputi( int d, FILE *fp ) +{ + fputc( d >> 24, fp ); + fputc( d >> 16, fp ); + fputc( d >> 8, fp ); + fputc( d, fp ); +} |
