[svn] Lots and lots of work on the public API. trunk
authoricculus
Thu, 27 Mar 2008 22:35:19 -0400
branchtrunk
changeset 46 ff5a0ec44f00
parent 45 9565678766aa
child 47 49953c7cb142
[svn] Lots and lots of work on the public API.
mojoshader.c
mojoshader.h
testparse.c
--- a/mojoshader.c	Thu Mar 27 17:10:27 2008 -0400
+++ b/mojoshader.c	Thu Mar 27 22:35:19 2008 -0400
@@ -112,17 +112,7 @@
     emit_comment comment_emitter;
 } Profile;
 
-
-// These are enum values, but they also can be used in bitmasks, so we can
-//  test if an opcode is acceptable: if (op->shader_types & ourtype) {} ...
-typedef enum
-{
-    SHADER_TYPE_UNKNOWN  = 0,
-    SHADER_TYPE_PIXEL    = (1 << 0),
-    SHADER_TYPE_VERTEX   = (1 << 1),
-    SHADER_TYPE_GEOMETRY = (1 << 2),
-    SHADER_TYPE_ANY = 0xFFFFFFFF
-} ShaderType;
+typedef MOJOSHADER_shaderType ShaderType;
 
 typedef enum
 {
@@ -280,12 +270,12 @@
     int profileid;
     const Profile *profile;
     ShaderType shader_type;
-    uint32 major_ver;
-    uint32 minor_ver;
+    uint8 major_ver;
+    uint8 minor_ver;
     DestArgInfo dest_args[1];
     SourceArgInfo source_args[4];
     uint32 dwords[4];
-    uint32 instruction_count;
+    int instruction_count;
     uint32 instruction_controls;
 };
 
@@ -326,11 +316,15 @@
 } // isfail
 
 
-static const char *out_of_mem_string = "Out of memory";
+static MOJOSHADER_parseData out_of_mem_data = {
+    "Out of memory", 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0
+};
+
+static const char *out_of_mem_str = "Out of memory";
 static inline int out_of_memory(Context *ctx)
 {
     if (ctx->failstr == NULL)
-        ctx->failstr = out_of_mem_string;  // fail() would call malloc().
+        ctx->failstr = out_of_mem_str;  // fail() would call malloc().
     return FAIL;
 } // out_of_memory
 
@@ -497,7 +491,7 @@
             break;
 
         case REGISTER_TYPE_ADDR:  // (or REGISTER_TYPE_TEXTURE, same value.)
-            retval = (ctx->shader_type == SHADER_TYPE_VERTEX) ? "a" : "t";
+            retval = (ctx->shader_type == MOJOSHADER_TYPE_VERTEX) ? "a" : "t";
             break;
 
         case REGISTER_TYPE_RASTOUT:
@@ -515,7 +509,7 @@
             break;
 
         case REGISTER_TYPE_TEXCRDOUT: // (or REGISTER_TYPE_OUTPUT, same value.)
-            if ((ctx->shader_type==SHADER_TYPE_VERTEX) && (ctx->major_ver>=3))
+            if ((ctx->shader_type==MOJOSHADER_TYPE_VERTEX) && (ctx->major_ver>=3))
                 retval = "o";
             else
                 retval = "oT";
@@ -750,9 +744,9 @@
     else
         snprintf(minor_str, sizeof (minor_str), "%u", (uint) minor);
 
-    if (ctx->shader_type == SHADER_TYPE_PIXEL)
+    if (ctx->shader_type == MOJOSHADER_TYPE_PIXEL)
         shadertype_str = "ps";
-    else if (ctx->shader_type == SHADER_TYPE_VERTEX)
+    else if (ctx->shader_type == MOJOSHADER_TYPE_VERTEX)
         shadertype_str = "vs";
     else
     {
@@ -1052,7 +1046,7 @@
     const char *usage_str = "";
     char index_str[16] = { '\0' };
 
-    if (ctx->shader_type == SHADER_TYPE_VERTEX)
+    if (ctx->shader_type == MOJOSHADER_TYPE_VERTEX)
     {
         static const char *usagestrs[] = {
             "_position", "_blendweight", "_blendindices", "_normal",
@@ -1182,9 +1176,9 @@
 {
     const uint major = (uint) ctx->major_ver;
     const uint minor = (uint) ctx->minor_ver;
-    if (ctx->shader_type == SHADER_TYPE_PIXEL)
+    if (ctx->shader_type == MOJOSHADER_TYPE_PIXEL)
         output_line(ctx, "// Pixel shader, version %u.%u", major, minor);
-    else if (ctx->shader_type == SHADER_TYPE_VERTEX)
+    else if (ctx->shader_type == MOJOSHADER_TYPE_VERTEX)
         output_line(ctx, "// Vertex shader, version %u.%u", major, minor);
     else
     {
@@ -1719,10 +1713,10 @@
 static const Profile profiles[] =
 {
 #if SUPPORT_PROFILE_D3D
-    { "d3d", emit_D3D_start, emit_D3D_end, emit_D3D_comment },
+    { MOJOSHADER_PROFILE_D3D, emit_D3D_start, emit_D3D_end, emit_D3D_comment },
 #endif
 #if SUPPORT_PROFILE_GLSL
-    { "glsl", emit_GLSL_start, emit_GLSL_end, emit_GLSL_comment },
+    { MOJOSHADER_PROFILE_GLSL, emit_GLSL_start, emit_GLSL_end, emit_GLSL_comment },
 #endif
 };
 
@@ -1770,7 +1764,7 @@
 
     if (info->relative)
     {
-        if (ctx->shader_type != SHADER_TYPE_VERTEX)
+        if (ctx->shader_type != MOJOSHADER_TYPE_VERTEX)
             return fail(ctx, "Relative addressing in non-vertex shader");
         else if (ctx->major_ver < 3)
             return fail(ctx, "Relative addressing in vertex shader version < 3.0");
@@ -1780,7 +1774,7 @@
     const int s = info->result_shift;
     if (s != 0)
     {
-        if (ctx->shader_type != SHADER_TYPE_PIXEL)
+        if (ctx->shader_type != MOJOSHADER_TYPE_PIXEL)
             return fail(ctx, "Result shift scale in non-pixel shader");
         else if (ctx->major_ver >= 2)
             return fail(ctx, "Result shift scale in pixel shader version >= 2.0");
@@ -1790,19 +1784,19 @@
 
     if (info->result_mod & MOD_SATURATE)  // Saturate (vertex shaders only)
     {
-        if (ctx->shader_type != SHADER_TYPE_VERTEX)
+        if (ctx->shader_type != MOJOSHADER_TYPE_VERTEX)
             return fail(ctx, "Saturate result mod in non-vertex shader");
     } // if
 
     if (info->result_mod & MOD_PP)  // Partial precision (pixel shaders only)
     {
-        if (ctx->shader_type != SHADER_TYPE_PIXEL)
+        if (ctx->shader_type != MOJOSHADER_TYPE_PIXEL)
             return fail(ctx, "Partial precision result mod in non-pixel shader");
     } // if
 
     if (info->result_mod & MOD_CENTROID)  // Centroid (pixel shaders only)
     {
-        if (ctx->shader_type != SHADER_TYPE_PIXEL)
+        if (ctx->shader_type != MOJOSHADER_TYPE_PIXEL)
             return fail(ctx, "Centroid result mod in non-pixel shader");
     } // if
 
@@ -1847,7 +1841,7 @@
 
     if (info->relative)
     {
-        if ((ctx->shader_type == SHADER_TYPE_PIXEL) && (ctx->major_ver < 3))
+        if ((ctx->shader_type == MOJOSHADER_TYPE_PIXEL) && (ctx->major_ver < 3))
             return fail(ctx, "Relative addressing in pixel shader version < 3.0");
         return fail(ctx, "Relative addressing is unsupported");  // !!! FIXME
     } // if
@@ -1940,7 +1934,7 @@
         return FAIL;
 
     const RegisterType regtype = (RegisterType) ctx->dest_args[0].regtype;
-    if ((ctx->shader_type == SHADER_TYPE_PIXEL) && (ctx->major_ver >= 3))
+    if ((ctx->shader_type == MOJOSHADER_TYPE_PIXEL) && (ctx->major_ver >= 3))
     {
         if (regtype == REGISTER_TYPE_INPUT)
             reserved_mask = 0x7FFFFFFF;
@@ -2002,7 +1996,7 @@
         } // else
     } // if
 
-    else if ((ctx->shader_type == SHADER_TYPE_PIXEL) && (ctx->major_ver >= 2))
+    else if ((ctx->shader_type == MOJOSHADER_TYPE_PIXEL) && (ctx->major_ver >= 2))
     {
         if (regtype == REGISTER_TYPE_INPUT)
             reserved_mask = 0x7FFFFFFF;
@@ -2019,7 +2013,7 @@
         } // else
     } // if
 
-    else if ((ctx->shader_type == SHADER_TYPE_VERTEX) && (ctx->major_ver >= 3))
+    else if ((ctx->shader_type == MOJOSHADER_TYPE_VERTEX) && (ctx->major_ver >= 3))
     {
         if ((regtype==REGISTER_TYPE_INPUT) || (regtype==REGISTER_TYPE_OUTPUT))
         {
@@ -2035,7 +2029,7 @@
         } // else
     } // else if
 
-    else if ((ctx->shader_type == SHADER_TYPE_VERTEX) && (ctx->major_ver >= 2))
+    else if ((ctx->shader_type == MOJOSHADER_TYPE_VERTEX) && (ctx->major_ver >= 2))
     {
         if (regtype == REGISTER_TYPE_INPUT)
         {
@@ -2173,106 +2167,106 @@
         #op, t, args, parse_args_##argsseq, 0, PROFILE_EMITTERS(op) \
     }
 
-    // !!! FIXME: Some of these SHADER_TYPE_ANYs need to have their scope
+    // !!! FIXME: Some of these MOJOSHADER_TYPE_ANYs need to have their scope
     // !!! FIXME:  reduced to just PIXEL or VERTEX.
 
-    INSTRUCTION(NOP, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(MOV, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(ADD, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(SUB, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(MAD, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(MUL, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(RCP, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(RSQ, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(DP3, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(DP4, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(MIN, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(MAX, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(SLT, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(SGE, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(EXP, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(LOG, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(LIT, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(DST, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(LRP, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(FRC, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(M4X4, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(M4X3, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(M3X4, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(M3X3, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(M3X2, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(CALL, 1, S, SHADER_TYPE_ANY),
-    INSTRUCTION(CALLNZ, 2, SS, SHADER_TYPE_ANY),
-    INSTRUCTION(LOOP, 2, SS, SHADER_TYPE_ANY),
-    INSTRUCTION(RET, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(ENDLOOP, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(LABEL, 1, S, SHADER_TYPE_ANY),
-    INSTRUCTION(DCL, 2, DCL, SHADER_TYPE_ANY),
-    INSTRUCTION(POW, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(CRS, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(SGN, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(ABS, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(NRM, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(SINCOS, 4, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(REP, 1, S, SHADER_TYPE_ANY),
-    INSTRUCTION(ENDREP, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(IF, 1, S, SHADER_TYPE_ANY),
-    INSTRUCTION(IFC, 2, SS, SHADER_TYPE_ANY),
-    INSTRUCTION(ELSE, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(ENDIF, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(BREAK, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(BREAKC, 2, SS, SHADER_TYPE_ANY),
-    INSTRUCTION(MOVA, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(DEFB, 2, DEFB, SHADER_TYPE_ANY),
-    INSTRUCTION(DEFI, 5, DEFI, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXCOORD, -1, TEXCOORD, SHADER_TYPE_PIXEL),
-    INSTRUCTION(TEXKILL, 1, D, SHADER_TYPE_PIXEL),
-    INSTRUCTION(TEX, -1, TEXCOORD, SHADER_TYPE_PIXEL), // same parse_args logic as TEXCOORD
-    INSTRUCTION(TEXBEM, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXBEML, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXREG2AR, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXREG2GB, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X2PAD, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X2TEX, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X3PAD, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X3TEX, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION_STATE(RESERVED, 0, NULL, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X3SPEC, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X3VSPEC, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(EXPP, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(LOGP, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(CND, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(DEF, 5, DEF, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXREG2RGB, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXDP3TEX, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X2DEPTH, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXDP3, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXM3X3, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXDEPTH, 1, D, SHADER_TYPE_ANY),
-    INSTRUCTION(CMP, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(BEM, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(DP2ADD, 4, DSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(DSX, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(DSY, 2, DS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXLDD, 5, DSSSS, SHADER_TYPE_ANY),
-    INSTRUCTION(SETP, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(TEXLDL, 3, DSS, SHADER_TYPE_ANY),
-    INSTRUCTION(BREAKP, 1, S, SHADER_TYPE_ANY),  // src
+    INSTRUCTION(NOP, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MOV, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ADD, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SUB, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MAD, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MUL, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(RCP, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(RSQ, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DP3, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DP4, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MIN, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MAX, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SLT, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SGE, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(EXP, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LOG, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LIT, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DST, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LRP, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(FRC, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(M4X4, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(M4X3, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(M3X4, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(M3X3, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(M3X2, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(CALL, 1, S, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(CALLNZ, 2, SS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LOOP, 2, SS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(RET, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ENDLOOP, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LABEL, 1, S, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DCL, 2, DCL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(POW, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(CRS, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SGN, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ABS, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(NRM, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SINCOS, 4, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(REP, 1, S, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ENDREP, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(IF, 1, S, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(IFC, 2, SS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ELSE, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(ENDIF, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(BREAK, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(BREAKC, 2, SS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(MOVA, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DEFB, 2, DEFB, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DEFI, 5, DEFI, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXCOORD, -1, TEXCOORD, MOJOSHADER_TYPE_PIXEL),
+    INSTRUCTION(TEXKILL, 1, D, MOJOSHADER_TYPE_PIXEL),
+    INSTRUCTION(TEX, -1, TEXCOORD, MOJOSHADER_TYPE_PIXEL), // same parse_args logic as TEXCOORD
+    INSTRUCTION(TEXBEM, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXBEML, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXREG2AR, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXREG2GB, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X2PAD, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X2TEX, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X3PAD, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X3TEX, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION_STATE(RESERVED, 0, NULL, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X3SPEC, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X3VSPEC, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(EXPP, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(LOGP, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(CND, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DEF, 5, DEF, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXREG2RGB, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXDP3TEX, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X2DEPTH, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXDP3, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXM3X3, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXDEPTH, 1, D, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(CMP, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(BEM, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DP2ADD, 4, DSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DSX, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(DSY, 2, DS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXLDD, 5, DSSSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(SETP, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(TEXLDL, 3, DSS, MOJOSHADER_TYPE_ANY),
+    INSTRUCTION(BREAKP, 1, S, MOJOSHADER_TYPE_ANY),  // src
 
     #undef INSTRUCTION
     #undef INSTRUCTION_STATE
@@ -2368,14 +2362,14 @@
 
     const uint32 token = SWAP32(*(ctx->tokens));
     const uint32 shadertype = ((token >> 16) & 0xFFFF);
-    const uint32 major = (uint32) ((token >> 8) & 0xFF);
-    const uint32 minor = (uint32) (token & 0xFF);
+    const uint8 major = (uint8) ((token >> 8) & 0xFF);
+    const uint8 minor = (uint8) (token & 0xFF);
 
     // 0xFFFF == pixel shader, 0xFFFE == vertex shader
     if (shadertype == 0xFFFF)
-        ctx->shader_type = SHADER_TYPE_PIXEL;
+        ctx->shader_type = MOJOSHADER_TYPE_PIXEL;
     else if (shadertype == 0xFFFE)
-        ctx->shader_type = SHADER_TYPE_VERTEX;
+        ctx->shader_type = MOJOSHADER_TYPE_VERTEX;
     else  // geometry shader? Bogus data?
         return fail(ctx, "Unsupported shader type or not a shader at all");
 
@@ -2405,11 +2399,21 @@
         const uint32 commenttoks = ((token >> 16) & 0xFFFF);
         const uint32 len = commenttoks * sizeof (uint32);
         const int needmalloc = (len >= SCRATCH_BUFFER_SIZE);
-        char *str = ((needmalloc) ? (char *) ctx->malloc(len + 1) :
-                        get_scratch_buffer(ctx));
+        char *str = NULL;
+
+        if (!needmalloc)
+            str = get_scratch_buffer(ctx);
+        else
+        {
+            str = (char *) ctx->malloc(len + 1);
+            if (str == NULL)
+                return out_of_memory(ctx);
+        } // else
+
         memcpy(str, (const char *) (ctx->tokens+1), len);
         str[len] = '\0';
         ctx->profile->comment_emitter(ctx, str);
+
         if (needmalloc)
             ctx->free(str);
 
@@ -2487,9 +2491,9 @@
 
 
 static Context *build_context(const char *profile,
-                                       const unsigned char *tokenbuf,
-                                       const unsigned int bufsize,
-                                       MOJOSHADER_malloc m, MOJOSHADER_free f)
+                              const unsigned char *tokenbuf,
+                              const unsigned int bufsize,
+                              MOJOSHADER_malloc m, MOJOSHADER_free f)
 {
     if (m == NULL) m = internal_malloc;
     if (f == NULL) f = internal_free;
@@ -2511,27 +2515,32 @@
 
     const int profileid = find_profile_id(profile);
     ctx->profileid = profileid;
-    if (profileid >= 0)  // we'll fail later, but we still need the context!
+    if (profileid >= 0)
         ctx->profile = &profiles[profileid];
-
-    return ctx;
+    else
+        failf(ctx, "Profile '%s' is unknown or unsupported", profile);
 } // build_context
 
 
 static void destroy_context(Context *ctx)
 {
-    OutputList *item = ctx->output.next;
-    while (item != NULL)
+    if (ctx != NULL)
     {
-        OutputList *next = item->next;
-        ctx->free(item->str);
-        ctx->free(item);
-        item = next;
-    } // for
-
-    if (ctx->failstr != out_of_mem_string)
-        ctx->free((void *) ctx->failstr);
-    ctx->free(ctx);
+        MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : internal_free);
+        OutputList *item = ctx->output.next;
+        while (item != NULL)
+        {
+            OutputList *next = item->next;
+            if (item->str != NULL)
+                f(item->str);
+            f(item);
+            item = next;
+        } // while
+
+        if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str))
+            f((void *) ctx->failstr);
+        f(ctx);
+    } // if
 } // destroy_context
 
 
@@ -2562,20 +2571,61 @@
 } // build_output
 
 
+static MOJOSHADER_parseData *build_parsedata(Context *ctx)
+{
+    char *output = NULL;
+    int len = 0;
+    MOJOSHADER_parseData *retval;
+
+    if ((retval = ctx->malloc(sizeof (MOJOSHADER_parseData))) == NULL)
+        return &out_of_mem_data;
+
+    memset(retval, '\0', sizeof (MOJOSHADER_parseData));
+
+    if (!isfail(ctx))
+        output = build_output(ctx);
+
+    // check again, in case build_output ran out of memory.
+    if (isfail(ctx))
+    {
+        if (output != NULL)
+            ctx->free(output);  // just in case.
+        retval->error = ctx->failstr;  // we recycle.  :)
+        ctx->failstr = NULL;  // don't let this get free()'d too soon.
+    } // if
+    else
+    {
+        retval->output = output;
+        retval->output_len = ctx->output_len;
+        retval->instruction_count = ctx->instruction_count;
+        retval->shader_type = ctx->shader_type;
+        retval->major_ver = (int) ctx->major_ver;
+        retval->minor_ver = (int) ctx->minor_ver;
+        retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc;
+        retval->free = (ctx->free == internal_free) ? NULL : ctx->free;
+    } // else
+
+    return retval;
+} // build_parsedata
+
+
 // API entry point...
 
-int MOJOSHADER_parse(const char *profile, const unsigned char *tokenbuf,
-                   const unsigned int bufsize, MOJOSHADER_malloc m,
-                   MOJOSHADER_free f)
+const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
+                                             const unsigned char *tokenbuf,
+                                             const unsigned int bufsize,
+                                             MOJOSHADER_malloc m,
+                                             MOJOSHADER_free f)
 {
+    MOJOSHADER_parseData *retval = NULL;
+    Context *ctx = NULL;
     int rc = FAIL;
 
-    Context *ctx = build_context(profile, tokenbuf, bufsize, m, f);
-    if (ctx == NULL)
-        return 0;  // !!! FIXME: error string?
-
-    if (ctx->profile == NULL)
-        failf(ctx, "Profile '%s' is unknown or unsupported", profile);
+    if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
+        return &out_of_mem_data;  // supply both or neither.
+
+    if ((ctx = build_context(profile, tokenbuf, bufsize, m, f)) == NULL)
+        return &out_of_mem_data;
 
     if (!isfail(ctx))
     {
@@ -2591,27 +2641,30 @@
         } // while
     } // if
 
-//    if (!isfail(ctx))
-    {
-        char *str = build_output(ctx);
-        if (str != NULL)
-        {
-            printf("OUTPUT:\n%s\n", str);  // !!! FIXME: report to caller.
-            ctx->free(str);
-        } // if
-
-        printf("INSTRUCTION COUNT: %u\n", (uint) ctx->instruction_count);
-    } // if
-
-    if (ctx->failstr != NULL)
-        printf("FAIL: %s\n", ctx->failstr);
-
+    retval = build_parsedata(ctx);
     destroy_context(ctx);
-
-    return (rc == END_OF_STREAM);
+    return retval;
 } // MOJOSHADER_parse
 
 
+void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data)
+{
+    MOJOSHADER_parseData *data = (MOJOSHADER_parseData *) _data;
+    if ((data == NULL) || (data == &out_of_mem_data))
+        return;  // no-op.
+
+    MOJOSHADER_free f = (data->free == NULL) ? internal_free : data->free;
+
+    if (data->output != NULL)  // check for NULL in case of dumb free() impl.
+        f((void *) data->output);
+
+    if ((data->error != NULL) && (data->error != out_of_mem_str))
+        f((void *) data->error);
+
+    f(data);
+} // MOJOSHADER_freeParseData
+
+
 int MOJOSHADER_version(void)
 {
     return MOJOSHADER_VERSION;
--- a/mojoshader.h	Thu Mar 27 17:10:27 2008 -0400
+++ b/mojoshader.h	Thu Mar 27 22:35:19 2008 -0400
@@ -15,25 +15,154 @@
 #endif
 
 /*
- * const int compiled_against = MOJOSHADER_VERSION;
- * const int linked_against = MOJOSHADER_version();
+ * For determining the version of MojoShader you are using:
+ *    const int compiled_against = MOJOSHADER_VERSION;
+ *    const int linked_against = MOJOSHADER_version();
+ *
+ * The version is a single integer that increments, not a major/minor value.
  */
 #define MOJOSHADER_VERSION 1
 int MOJOSHADER_version(void);
 
 /*
  * These allocators work just like the C runtime's malloc() and free()
- *  (in fact, they use malloc() and free() internally if you don't
- *  specify your own allocator).
+ *  (in fact, they probably use malloc() and free() internally if you don't
+ *  specify your own allocator, but don't rely on that behaviour).
  */
 typedef void *(*MOJOSHADER_malloc)(int bytes);
 typedef void (*MOJOSHADER_free)(void *ptr);
 
-/* !!! FIXME: documentation. */
-/* !!! FIXME: this needs to change to return a buffer of GLSL code. */
-int MOJOSHADER_parse(const char *profile, const unsigned char *tokenbuf,
-                   const unsigned int bufsize, MOJOSHADER_malloc m,
-                   MOJOSHADER_free f);
+
+/*
+ * These are enum values, but they also can be used in bitmasks, so we can
+ *  test if an opcode is acceptable: if (op->shader_types & ourtype) {} ...
+ */
+typedef enum
+{
+    MOJOSHADER_TYPE_UNKNOWN  = 0,
+    MOJOSHADER_TYPE_PIXEL    = (1 << 0),
+    MOJOSHADER_TYPE_VERTEX   = (1 << 1),
+    MOJOSHADER_TYPE_GEOMETRY = (1 << 2),  /* (not supported yet.) */
+    MOJOSHADER_TYPE_ANY = 0xFFFFFFFF   /* used for bitmasks */
+} MOJOSHADER_shaderType;
+
+
+/*
+ * Structure used to return data from parsing of a shader...
+ */
+typedef struct
+{
+    /*
+     * Human-readable error, if there is one. Will be NULL if there was no
+     *  error. The string will be UTF-8 encoded, and English only. Most of
+     *  these shouldn't be shown to the end-user anyhow.
+     */
+    const char *error;
+
+    /*
+     * Bytes of output from parsing. Most profiles produce a string of source
+     *  code, but profiles that do binary output may not be text at all.
+     *  Will be NULL on error.
+     */
+    const char *output;
+
+    /*
+     * Byte count for output, not counting any null terminator. Most profiles
+     *  produce an ASCII string of source code, but profiles that do binary
+     *  output may not be text at all. Will be 0 on error.
+     */
+    int output_len;
+
+    /*
+     * Count of Direct3D instructions we parsed. This is meaningless in terms
+     *  of the actual output, as the profile will probably grow or reduce
+     *  the count (or for high-level languages, not have that information at
+     *  all), but it can give you a rough idea of the size of your shader.
+     *  Will be zero on error.
+     */
+    int instruction_count;
+
+    /*
+     * The type of shader we parsed. Will be MOJOSHADER_TYPE_UNKNOWN on error.
+     */
+    MOJOSHADER_shaderType shader_type;
+
+    /*
+     * The shader's major version. If this was a "vs_3_0", this would be 3.
+     */
+    int major_ver;
+
+    /*
+     * The shader's minor version. If this was a "ps_1_4", this would be 4.
+     *  Two notes: for "vs_2_x", this is 1, and for "vs_3_sw", this is 255.
+     */
+    int minor_ver;
+
+    /*
+     * This is the malloc implementation you passed to MOJOSHADER_parse().
+     */
+    MOJOSHADER_malloc malloc;
+
+    /*
+     * This is the free implementation you passed to MOJOSHADER_parse().
+     */
+    MOJOSHADER_free free;
+} MOJOSHADER_parseData;
+
+
+/*
+ * Profile string for Direct3D assembly language output.
+ */
+#define MOJOSHADER_PROFILE_D3D "d3d"
+
+/*
+ * Profile string for GLSL: OpenGL high-level shader language output.
+ */
+#define MOJOSHADER_PROFILE_GLSL "glsl"
+
+/*
+ * Parse a compiled Direct3D shader's bytecode.
+ *
+ * This is your primary entry point into MojoShader. You need to pass it
+ *  a compiled D3D shader and tell it which "profile" you want to use to
+ *  convert it into useful data.
+ *
+ * The available profiles are the set of MOJOSHADER_PROFILE_* defines.
+ *  Note that MojoShader may be built without support for all listed
+ *  profiles (in which case using one here will return with an error).
+ *
+ * As parsing requires some memory to be allocated, you may provide a custom
+ *  allocator to this function, which will be used to allocate/free memory.
+ *  They function just like malloc() and free(). We do not use realloc().
+ *  If you don't care, pass NULL in for the allocator functions.
+ *
+ * This function returns a MOJOSHADER_parseData
+ *
+ * This function will never return NULL, even if the system is completely
+ *  out of memory upon entry (in which case, this function returns a static
+ *  MOJOSHADER_parseData object, which is still safe to pass to
+ *  MOJOSHADER_freeParseData()).
+ *
+ * This function is thread safe, so long (m) and (f) are too, and that
+ *  (tokenbuf) remains intact for the duration of the call. This allows you
+ *  to parse several shaders on separate CPU cores at the same time.
+ */
+const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
+                                             const unsigned char *tokenbuf,
+                                             const unsigned int bufsize,
+                                             MOJOSHADER_malloc m,
+                                             MOJOSHADER_free f);
+
+/*
+ * Call this to dispose of parsing results when you are done with them.
+ *  This will call the MOJOSHADER_free function you provided to
+ *  MOJOSHADER_parse multiple times, if you provided one.
+ *  Passing a NULL here is a safe no-op.
+ *
+ * This function is thread safe, so long as any allocator you passed into
+ *  MOJOSHADER_parse() is, too.
+ */
+void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data);
 
 #ifdef __cplusplus
 }
--- a/testparse.c	Thu Mar 27 17:10:27 2008 -0400
+++ b/testparse.c	Thu Mar 27 22:35:19 2008 -0400
@@ -2,20 +2,81 @@
 #include <stdlib.h>
 #include "mojoshader.h"
 
+
+#if DEBUG_MALLOC
+static void *Malloc(int len)
+{
+    void *ptr = malloc(len + sizeof (int));
+    int *store = (int *) ptr;
+    printf("malloc() %d bytes (%p)\n", len, ptr);
+    if (ptr == NULL) return NULL;
+    *store = len;
+    return (void *) (store + 1);
+} // Malloc
+
+
+static void Free(void *_ptr)
+{
+    int *ptr = (((int *) ptr) - 1);
+    int *store = (int *) ptr;
+    int len = *store;
+    printf("free() %d bytes (%p)\n", len, ptr);
+    free(ptr);
+} // Free
+#else
+#define Malloc NULL
+#define Free NULL
+#endif
+
+static const char *shader_type(MOJOSHADER_shaderType s)
+{
+    switch (s)
+    {
+        case MOJOSHADER_TYPE_UNKNOWN: return "unknown";
+        case MOJOSHADER_TYPE_PIXEL: return "pixel";
+        case MOJOSHADER_TYPE_VERTEX: return "vertex";
+        case MOJOSHADER_TYPE_GEOMETRY: return "geometry";
+    } // switch
+
+    return "(bogus value?)";
+} // shader_type
+
+
+static void do_parse(unsigned char *buf, int len, const char *prof)
+{
+    const MOJOSHADER_parseData *pd;
+
+    pd = MOJOSHADER_parse(prof, buf, len, Malloc, Free);
+    printf("PROFILE: %s\n", prof);
+    printf("SHADER TYPE: %s\n", shader_type(pd->shader_type));
+    printf("VERSION: %d.%d\n", pd->major_ver, pd->minor_ver);
+    printf("INSTRUCTION COUNT: %d\n", (int) pd->instruction_count);
+    if (pd->error != NULL)
+        printf("ERROR: %s\n", pd->error);
+    if (pd->output != NULL)
+        printf("OUTPUT:\n%s\n", pd->output);
+    printf("\n\n");
+    MOJOSHADER_freeParseData(pd);
+} // do_parse
+
+
 int main(int argc, char **argv)
 {
+    int i;
+
     printf("Compiled against version %d\n", MOJOSHADER_VERSION);
     printf("Linked against version %d\n", MOJOSHADER_version());
 
-    if (argv[1] != NULL)
+    for (i = 1; i < argc; i++)
     {
-        FILE *io = fopen(argv[1], "rb");
+        FILE *io = fopen(argv[i], "rb");
         if (io != NULL)
         {
             unsigned char *buf = (unsigned char *) malloc(1000000);
             int rc = fread(buf, 1, 1000000, io);
             fclose(io);
-            MOJOSHADER_parse("d3d", buf, rc, NULL, NULL);
+            do_parse(buf, rc, MOJOSHADER_PROFILE_D3D);
+            do_parse(buf, rc, MOJOSHADER_PROFILE_GLSL);
             free(buf);
         } // if
     } // if