aboutsummaryrefslogtreecommitdiff
path: root/Externals/mojoshader/1067/mojoshader_assembler.c
diff options
context:
space:
mode:
Diffstat (limited to 'Externals/mojoshader/1067/mojoshader_assembler.c')
-rw-r--r--Externals/mojoshader/1067/mojoshader_assembler.c1766
1 files changed, 1766 insertions, 0 deletions
diff --git a/Externals/mojoshader/1067/mojoshader_assembler.c b/Externals/mojoshader/1067/mojoshader_assembler.c
new file mode 100644
index 00000000..7885bd45
--- /dev/null
+++ b/Externals/mojoshader/1067/mojoshader_assembler.c
@@ -0,0 +1,1766 @@
+/**
+ * MojoShader; generate shader programs from bytecode of compiled
+ * Direct3D shaders.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+// !!! FIXME: this should probably use a formal grammar and not a hand-written
+// !!! FIXME: pile of C code.
+
+#define __MOJOSHADER_INTERNAL__ 1
+#include "mojoshader_internal.h"
+
+#if !SUPPORT_PROFILE_BYTECODE
+#error Shader assembler needs bytecode profile. Fix your build.
+#endif
+
+#if DEBUG_ASSEMBLER_PARSER
+ #define print_debug_token(token, len, val) \
+ MOJOSHADER_print_debug_token("ASSEMBLER", token, len, val)
+#else
+ #define print_debug_token(token, len, val)
+#endif
+
+
+typedef struct SourcePos
+{
+ const char *filename;
+ uint32 line;
+} SourcePos;
+
+
+// Context...this is state that changes as we assemble a shader...
+typedef struct Context
+{
+ int isfail;
+ int out_of_memory;
+ MOJOSHADER_malloc malloc;
+ MOJOSHADER_free free;
+ void *malloc_data;
+ const char *current_file;
+ int current_position;
+ ErrorList *errors;
+ Preprocessor *preprocessor;
+ MOJOSHADER_shaderType shader_type;
+ uint8 major_ver;
+ uint8 minor_ver;
+ int pushedback;
+ const char *token; // assembler token!
+ unsigned int tokenlen; // assembler token!
+ Token tokenval; // assembler token!
+ uint32 version_token; // bytecode token!
+ uint32 tokenbuf[16]; // bytecode tokens!
+ int tokenbufpos; // bytecode tokens!
+ DestArgInfo dest_arg;
+ Buffer *output;
+ Buffer *token_to_source;
+ Buffer *ctab;
+} Context;
+
+
+// !!! FIXME: cut and paste between every damned source file follows...
+// !!! FIXME: We need to make some sort of ContextBase that applies to all
+// !!! FIXME: files and move this stuff to mojoshader_common.c ...
+
+// Convenience functions for allocators...
+
+static inline void out_of_memory(Context *ctx)
+{
+ ctx->isfail = ctx->out_of_memory = 1;
+} // out_of_memory
+
+static inline void *Malloc(Context *ctx, const size_t len)
+{
+ void *retval = ctx->malloc((int) len, ctx->malloc_data);
+ if (retval == NULL)
+ out_of_memory(ctx);
+ return retval;
+} // Malloc
+
+static inline char *StrDup(Context *ctx, const char *str)
+{
+ char *retval = (char *) Malloc(ctx, strlen(str) + 1);
+ if (retval != NULL)
+ strcpy(retval, str);
+ return retval;
+} // StrDup
+
+static inline void Free(Context *ctx, void *ptr)
+{
+ ctx->free(ptr, ctx->malloc_data);
+} // Free
+
+static void *MallocBridge(int bytes, void *data)
+{
+ return Malloc((Context *) data, (size_t) bytes);
+} // MallocBridge
+
+static void FreeBridge(void *ptr, void *data)
+{
+ Free((Context *) data, ptr);
+} // FreeBridge
+
+
+static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
+static void failf(Context *ctx, const char *fmt, ...)
+{
+ ctx->isfail = 1;
+ if (ctx->out_of_memory)
+ return;
+
+ va_list ap;
+ va_start(ap, fmt);
+ errorlist_add_va(ctx->errors, ctx->current_file, ctx->current_position, fmt, ap);
+ va_end(ap);
+} // failf
+
+static inline void fail(Context *ctx, const char *reason)
+{
+ failf(ctx, "%s", reason);
+} // fail
+
+static inline int isfail(const Context *ctx)
+{
+ return ctx->isfail;
+} // isfail
+
+
+// Shader model version magic...
+
+static inline uint32 ver_ui32(const uint8 major, const uint8 minor)
+{
+ return ( (((uint32) major) << 16) | (((minor) == 0xFF) ? 0 : (minor)) );
+} // version_ui32
+
+static inline int shader_version_atleast(const Context *ctx, const uint8 maj,
+ const uint8 min)
+{
+ return (ver_ui32(ctx->major_ver, ctx->minor_ver) >= ver_ui32(maj, min));
+} // shader_version_atleast
+
+static inline int shader_is_pixel(const Context *ctx)
+{
+ return (ctx->shader_type == MOJOSHADER_TYPE_PIXEL);
+} // shader_is_pixel
+
+static inline int shader_is_vertex(const Context *ctx)
+{
+ return (ctx->shader_type == MOJOSHADER_TYPE_VERTEX);
+} // shader_is_vertex
+
+static inline void pushback(Context *ctx)
+{
+ #if DEBUG_ASSEMBLER_PARSER
+ printf("ASSEMBLER PUSHBACK\n");
+ #endif
+ assert(!ctx->pushedback);
+ ctx->pushedback = 1;
+} // pushback
+
+
+static Token nexttoken(Context *ctx)
+{
+ if (ctx->pushedback)
+ ctx->pushedback = 0;
+ else
+ {
+ while (1)
+ {
+ ctx->token = preprocessor_nexttoken(ctx->preprocessor,
+ &ctx->tokenlen,
+ &ctx->tokenval);
+
+ if (preprocessor_outofmemory(ctx->preprocessor))
+ {
+ ctx->tokenval = TOKEN_EOI;
+ ctx->token = NULL;
+ ctx->tokenlen = 0;
+ break;
+ } // if
+
+ unsigned int line;
+ ctx->current_file = preprocessor_sourcepos(ctx->preprocessor,&line);
+ ctx->current_position = (int) line;
+
+ if (ctx->tokenval == TOKEN_BAD_CHARS)
+ {
+ fail(ctx, "Bad characters in source file");
+ continue;
+ } // else if
+
+ else if (ctx->tokenval == TOKEN_PREPROCESSING_ERROR)
+ {
+ fail(ctx, ctx->token);
+ continue;
+ } // else if
+
+ break;
+ } // while
+ } // else
+
+ print_debug_token(ctx->token, ctx->tokenlen, ctx->tokenval);
+ return ctx->tokenval;
+} // nexttoken
+
+
+static void output_token_noswap(Context *ctx, const uint32 token)
+{
+ if (!isfail(ctx))
+ {
+ buffer_append(ctx->output, &token, sizeof (token));
+
+ // We only need a list of these that grows throughout processing, and
+ // is flattened for reference at the end of the run, so we use a
+ // Buffer. It's sneaky!
+ unsigned int pos = 0;
+ const char *fname = preprocessor_sourcepos(ctx->preprocessor, &pos);
+ SourcePos srcpos;
+ memset(&srcpos, '\0', sizeof (SourcePos));
+ srcpos.line = pos;
+ srcpos.filename = fname; // cached in preprocessor!
+ buffer_append(ctx->token_to_source, &srcpos, sizeof (SourcePos));
+ } // if
+} // output_token_noswap
+
+
+static inline void output_token(Context *ctx, const uint32 token)
+{
+ output_token_noswap(ctx, SWAP32(token));
+} // output_token
+
+
+static void output_comment_bytes(Context *ctx, const uint8 *buf, size_t len)
+{
+ if (len > (0xFFFF * 4)) // length is stored as token count, in 16 bits.
+ fail(ctx, "Comment field is too big");
+ else if (!isfail(ctx))
+ {
+ const uint32 tokencount = (len / 4) + ((len % 4) ? 1 : 0);
+ output_token(ctx, 0xFFFE | (tokencount << 16));
+ while (len >= 4)
+ {
+ output_token_noswap(ctx, *((const uint32 *) buf));
+ len -= 4;
+ buf += 4;
+ } // while
+
+ if (len > 0) // handle spillover...
+ {
+ union { uint8 ui8[4]; uint32 ui32; } overflow;
+ overflow.ui32 = 0;
+ memcpy(overflow.ui8, buf, len);
+ output_token_noswap(ctx, overflow.ui32);
+ } // if
+ } // else if
+} // output_comment_bytes
+
+
+static inline void output_comment_string(Context *ctx, const char *str)
+{
+ output_comment_bytes(ctx, (const uint8 *) str, strlen(str));
+} // output_comment_string
+
+
+static int require_comma(Context *ctx)
+{
+ const Token token = nexttoken(ctx);
+ if (token != ((Token) ','))
+ {
+ fail(ctx, "Comma expected");
+ return 0;
+ } // if
+ return 1;
+} // require_comma
+
+
+static int check_token_segment(Context *ctx, const char *str)
+{
+ // !!! FIXME: these are case-insensitive, right?
+ const size_t len = strlen(str);
+ if ( (ctx->tokenlen < len) || (strncasecmp(ctx->token, str, len) != 0) )
+ return 0;
+ ctx->token += len;
+ ctx->tokenlen -= len;
+ return 1;
+} // check_token_segment
+
+
+static int check_token(Context *ctx, const char *str)
+{
+ const size_t len = strlen(str);
+ if ( (ctx->tokenlen != len) || (strncasecmp(ctx->token, str, len) != 0) )
+ return 0;
+ ctx->token += len;
+ ctx->tokenlen = 0;
+ return 1;
+} // check_token
+
+
+static int ui32fromtoken(Context *ctx, uint32 *_val)
+{
+ unsigned int i;
+ for (i = 0; i < ctx->tokenlen; i++)
+ {
+ if ((ctx->token[i] < '0') || (ctx->token[i] > '9'))
+ break;
+ } // for
+
+ if (i == 0)
+ {
+ *_val = 0;
+ return 0;
+ } // if
+
+ const unsigned int len = i;
+ uint32 val = 0;
+ uint32 mult = 1;
+ while (i--)
+ {
+ val += ((uint32) (ctx->token[i] - '0')) * mult;
+ mult *= 10;
+ } // while
+
+ ctx->token += len;
+ ctx->tokenlen -= len;
+
+ *_val = val;
+ return 1;
+} // ui32fromtoken
+
+
+static int parse_register_name(Context *ctx, RegisterType *rtype, int *rnum)
+{
+ if (nexttoken(ctx) != TOKEN_IDENTIFIER)
+ {
+ fail(ctx, "Expected register");
+ return 0;
+ } // if
+
+ int neednum = 1;
+ int regnum = 0;
+ RegisterType regtype = REG_TYPE_TEMP;
+
+ // Watch out for substrings! oDepth must be checked before oD, since
+ // the latter will match either case.
+ if (check_token_segment(ctx, "oDepth"))
+ {
+ regtype = REG_TYPE_DEPTHOUT;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "vFace"))
+ {
+ regtype = REG_TYPE_MISCTYPE;
+ regnum = (int) MISCTYPE_TYPE_FACE;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "vPos"))
+ {
+ regtype = REG_TYPE_MISCTYPE;
+ regnum = (int) MISCTYPE_TYPE_POSITION;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "oPos"))
+ {
+ regtype = REG_TYPE_RASTOUT;
+ regnum = (int) RASTOUT_TYPE_POSITION;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "oFog"))
+ {
+ regtype = REG_TYPE_RASTOUT;
+ regnum = (int) RASTOUT_TYPE_FOG;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "oPts"))
+ {
+ regtype = REG_TYPE_RASTOUT;
+ regnum = (int) RASTOUT_TYPE_POINT_SIZE;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "aL"))
+ {
+ regtype = REG_TYPE_LOOP;
+ neednum = 0;
+ } // else if
+ else if (check_token_segment(ctx, "oC"))
+ regtype = REG_TYPE_COLOROUT;
+ else if (check_token_segment(ctx, "oT"))
+ regtype = REG_TYPE_OUTPUT;
+ else if (check_token_segment(ctx, "oD"))
+ regtype = REG_TYPE_ATTROUT;
+ else if (check_token_segment(ctx, "r"))
+ regtype = REG_TYPE_TEMP;
+ else if (check_token_segment(ctx, "v"))
+ regtype = REG_TYPE_INPUT;
+ else if (check_token_segment(ctx, "c"))
+ regtype = REG_TYPE_CONST;
+ else if (check_token_segment(ctx, "i"))
+ regtype = REG_TYPE_CONSTINT;
+ else if (check_token_segment(ctx, "b"))
+ regtype = REG_TYPE_CONSTBOOL;
+ else if (check_token_segment(ctx, "s"))
+ regtype = REG_TYPE_SAMPLER;
+ else if (check_token_segment(ctx, "l"))
+ regtype = REG_TYPE_LABEL;
+ else if (check_token_segment(ctx, "p"))
+ regtype = REG_TYPE_PREDICATE;
+ else if (check_token_segment(ctx, "o"))
+ regtype = REG_TYPE_OUTPUT;
+ else if (check_token_segment(ctx, "a"))
+ regtype = REG_TYPE_ADDRESS;
+ else if (check_token_segment(ctx, "t"))
+ regtype = REG_TYPE_ADDRESS;
+
+ //case REG_TYPE_TEMPFLOAT16: // !!! FIXME: don't know this asm string
+
+ else
+ {
+ fail(ctx, "expected register type");
+ regtype = REG_TYPE_CONST;
+ regnum = 0;
+ neednum = 0;
+ } // else
+
+ // "c[5]" is the same as "c5", so if the token is done, see if next is '['.
+ if ((neednum) && (ctx->tokenlen == 0))
+ {
+ const int tlen = ctx->tokenlen; // we need to protect this for later.
+ if (nexttoken(ctx) == ((Token) '['))
+ neednum = 0; // don't need a number on register name itself.
+ pushback(ctx);
+ ctx->tokenlen = tlen;
+ } // if
+
+ if (neednum)
+ {
+ uint32 ui32 = 0;
+ if (!ui32fromtoken(ctx, &ui32))
+ fail(ctx, "Invalid register index");
+ regnum = (int) ui32;
+ } // if
+
+ // split up REG_TYPE_CONST
+ if (regtype == REG_TYPE_CONST)
+ {
+ if (regnum < 2048)
+ {
+ regtype = REG_TYPE_CONST;
+ regnum -= 0;
+ } // if
+ else if (regnum < 4096)
+ {
+ regtype = REG_TYPE_CONST2;
+ regnum -= 2048;
+ } // if
+ else if (regnum < 6144)
+ {
+ regtype = REG_TYPE_CONST3;
+ regnum -= 4096;
+ } // if
+ else if (regnum < 8192)
+ {
+ regtype = REG_TYPE_CONST4;
+ regnum -= 6144;
+ } // if
+ else
+ {
+ fail(ctx, "Invalid const register index");
+ } // else
+ } // if
+
+ *rtype = regtype;
+ *rnum = regnum;
+
+ return 1;
+} // parse_register_name
+
+
+static void set_result_shift(Context *ctx, DestArgInfo *info, const int val)
+{
+ if (info->result_shift != 0)
+ fail(ctx, "Multiple result shift modifiers");
+ info->result_shift = val;
+} // set_result_shift
+
+
+static inline int tokenbuf_overflow(Context *ctx)
+{
+ if ( ctx->tokenbufpos >= ((int) (STATICARRAYLEN(ctx->tokenbuf))) )
+ {
+ fail(ctx, "Too many tokens");
+ return 1;
+ } // if
+
+ return 0;
+} // tokenbuf_overflow
+
+
+static int parse_destination_token(Context *ctx)
+{
+ DestArgInfo *info = &ctx->dest_arg;
+ memset(info, '\0', sizeof (DestArgInfo));
+
+ // parse_instruction_token() sets ctx->token to the end of the instruction
+ // so we can see if there are destination modifiers on the instruction
+ // itself...
+
+ int invalid_modifier = 0;
+
+ while ((ctx->tokenlen > 0) && (!invalid_modifier))
+ {
+ if (check_token_segment(ctx, "_x2"))
+ set_result_shift(ctx, info, 0x1);
+ else if (check_token_segment(ctx, "_x4"))
+ set_result_shift(ctx, info, 0x2);
+ else if (check_token_segment(ctx, "_x8"))
+ set_result_shift(ctx, info, 0x3);
+ else if (check_token_segment(ctx, "_d8"))
+ set_result_shift(ctx, info, 0xD);
+ else if (check_token_segment(ctx, "_d4"))
+ set_result_shift(ctx, info, 0xE);
+ else if (check_token_segment(ctx, "_d2"))
+ set_result_shift(ctx, info, 0xF);
+ else if (check_token_segment(ctx, "_sat"))
+ info->result_mod |= MOD_SATURATE;
+ else if (check_token_segment(ctx, "_pp"))
+ info->result_mod |= MOD_PP;
+ else if (check_token_segment(ctx, "_centroid"))
+ info->result_mod |= MOD_CENTROID;
+ else
+ invalid_modifier = 1;
+ } // while
+
+ if (invalid_modifier)
+ fail(ctx, "Invalid destination modifier");
+
+ // !!! FIXME: predicates.
+ if (nexttoken(ctx) == ((Token) '('))
+ fail(ctx, "Predicates unsupported at this time"); // !!! FIXME: ...
+
+ pushback(ctx); // parse_register_name calls nexttoken().
+
+ parse_register_name(ctx, &info->regtype, &info->regnum);
+ // parse_register_name() can't check this: dest regs might have modifiers.
+ if (ctx->tokenlen > 0)
+ fail(ctx, "invalid register name");
+
+ // !!! FIXME: can dest registers do relative addressing?
+
+ int invalid_writemask = 0;
+ int implicit_writemask = 0;
+ if (nexttoken(ctx) != ((Token) '.'))
+ {
+ implicit_writemask = 1;
+ info->writemask = 0xF;
+ info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 1;
+ pushback(ctx); // no explicit writemask; do full mask.
+ } // if
+
+ // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
+ //else if (scalar_register(ctx->shader_type, info->regtype, info->regnum))
+ else if ( (scalar_register(ctx->shader_type, info->regtype, info->regnum)) && (info->regtype != REG_TYPE_DEPTHOUT) )
+ fail(ctx, "Writemask specified for scalar register");
+ else if (nexttoken(ctx) != TOKEN_IDENTIFIER)
+ invalid_writemask = 1;
+ else
+ {
+ char tokenbytes[5] = { '\0', '\0', '\0', '\0', '\0' };
+ const unsigned int tokenlen = ctx->tokenlen;
+ memcpy(tokenbytes, ctx->token, ((tokenlen < 4) ? tokenlen : 4));
+ char *ptr = tokenbytes;
+
+ if ((*ptr == 'r') || (*ptr == 'x')) { info->writemask0 = 1; ptr++; }
+ if ((*ptr == 'g') || (*ptr == 'y')) { info->writemask1 = 1; ptr++; }
+ if ((*ptr == 'b') || (*ptr == 'z')) { info->writemask2 = 1; ptr++; }
+ if ((*ptr == 'a') || (*ptr == 'w')) { info->writemask3 = 1; ptr++; }
+
+ if (*ptr != '\0')
+ invalid_writemask = 1;
+
+ info->writemask = ( ((info->writemask0 & 0x1) << 0) |
+ ((info->writemask1 & 0x1) << 1) |
+ ((info->writemask2 & 0x1) << 2) |
+ ((info->writemask3 & 0x1) << 3) );
+ } // else
+
+ if (invalid_writemask)
+ fail(ctx, "Invalid writemask");
+
+ // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
+ if (info->regtype == REG_TYPE_DEPTHOUT)
+ {
+ if ( (!implicit_writemask) && ((info->writemask0 + info->writemask1 +
+ info->writemask2 + info->writemask3) > 1) )
+ fail(ctx, "Writemask specified for scalar register");
+ } // if
+
+ info->orig_writemask = info->writemask;
+
+ if (tokenbuf_overflow(ctx))
+ return 1;
+
+ ctx->tokenbuf[ctx->tokenbufpos++] =
+ ( ((((uint32) 1)) << 31) |
+ ((((uint32) info->regnum) & 0x7ff) << 0) |
+ ((((uint32) info->relative) & 0x1) << 13) |
+ ((((uint32) info->result_mod) & 0xF) << 20) |
+ ((((uint32) info->result_shift) & 0xF) << 24) |
+ ((((uint32) info->writemask) & 0xF) << 16) |
+ ((((uint32) info->regtype) & 0x7) << 28) |
+ ((((uint32) info->regtype) & 0x18) << 8) );
+
+ return 1;
+} // parse_destination_token
+
+
+static void set_source_mod(Context *ctx, const int negate,
+ const SourceMod norm, const SourceMod negated,
+ SourceMod *srcmod)
+{
+ if ( (*srcmod != SRCMOD_NONE) || (negate && (negated == SRCMOD_NONE)) )
+ fail(ctx, "Incompatible source modifiers");
+ else
+ *srcmod = ((negate) ? negated : norm);
+} // set_source_mod
+
+
+static int parse_source_token_maybe_relative(Context *ctx, const int relok)
+{
+ int retval = 1;
+
+ if (tokenbuf_overflow(ctx))
+ return 0;
+
+ // mark this now, so optional relative addressing token is placed second.
+ uint32 *outtoken = &ctx->tokenbuf[ctx->tokenbufpos++];
+ *outtoken = 0;
+
+ SourceMod srcmod = SRCMOD_NONE;
+ int negate = 0;
+ Token token = nexttoken(ctx);
+
+ if (token == ((Token) '!'))
+ srcmod = SRCMOD_NOT;
+ else if (token == ((Token) '-'))
+ negate = 1;
+ else if ( (token == TOKEN_INT_LITERAL) && (check_token(ctx, "1")) )
+ {
+ if (nexttoken(ctx) != ((Token) '-'))
+ fail(ctx, "Unexpected token");
+ else
+ srcmod = SRCMOD_COMPLEMENT;
+ } // else
+ else
+ {
+ pushback(ctx);
+ } // else
+
+ RegisterType regtype;
+ int regnum;
+ parse_register_name(ctx, &regtype, &regnum);
+
+ if (ctx->tokenlen == 0)
+ {
+ if (negate)
+ set_source_mod(ctx, negate, SRCMOD_NONE, SRCMOD_NEGATE, &srcmod);
+ } // if
+ else
+ {
+ assert(ctx->tokenlen > 0);
+ if (check_token_segment(ctx, "_bias"))
+ set_source_mod(ctx, negate, SRCMOD_BIAS, SRCMOD_BIASNEGATE, &srcmod);
+ else if (check_token_segment(ctx, "_bx2"))
+ set_source_mod(ctx, negate, SRCMOD_SIGN, SRCMOD_SIGNNEGATE, &srcmod);
+ else if (check_token_segment(ctx, "_x2"))
+ set_source_mod(ctx, negate, SRCMOD_X2, SRCMOD_X2NEGATE, &srcmod);
+ else if (check_token_segment(ctx, "_dz"))
+ set_source_mod(ctx, negate, SRCMOD_DZ, SRCMOD_NONE, &srcmod);
+ else if (check_token_segment(ctx, "_dw"))
+ set_source_mod(ctx, negate, SRCMOD_DW, SRCMOD_NONE, &srcmod);
+ else if (check_token_segment(ctx, "_abs"))
+ set_source_mod(ctx, negate, SRCMOD_ABS, SRCMOD_ABSNEGATE, &srcmod);
+ else
+ fail(ctx, "Invalid source modifier");
+ } // else
+
+ uint32 relative = 0;
+ if (nexttoken(ctx) != ((Token) '['))
+ pushback(ctx); // not relative addressing?
+ else
+ {
+ if (!relok)
+ fail(ctx, "Relative addressing not permitted here.");
+ else
+ retval++;
+
+ parse_source_token_maybe_relative(ctx, 0);
+ relative = 1;
+
+ if (nexttoken(ctx) != ((Token) '+'))
+ pushback(ctx);
+ else
+ {
+ // !!! FIXME: maybe c3[a0.x + 5] is legal and becomes c[a0.x + 8] ?
+ if (regnum != 0)
+ fail(ctx, "Relative addressing with explicit register number.");
+
+ uint32 ui32 = 0;
+ if ( (nexttoken(ctx) != TOKEN_INT_LITERAL) ||
+ (!ui32fromtoken(ctx, &ui32)) ||
+ (ctx->tokenlen != 0) )
+ {
+ fail(ctx, "Invalid relative addressing offset");
+ } // if
+ regnum += (int) ui32;
+ } // else
+
+ if (nexttoken(ctx) != ((Token) ']'))
+ fail(ctx, "Expected ']'");
+ } // else
+
+ int invalid_swizzle = 0;
+ uint32 swizzle = 0;
+ if (nexttoken(ctx) != ((Token) '.'))
+ {
+ swizzle = 0xE4; // 0xE4 == 11100100 ... 0 1 2 3. No swizzle.
+ pushback(ctx); // no explicit writemask; do full mask.
+ } // if
+ else if (scalar_register(ctx->shader_type, regtype, regnum))
+ fail(ctx, "Swizzle specified for scalar register");
+ else if (nexttoken(ctx) != TOKEN_IDENTIFIER)
+ invalid_swizzle = 1;
+ else
+ {
+ char tokenbytes[5] = { '\0', '\0', '\0', '\0', '\0' };
+ const unsigned int tokenlen = ctx->tokenlen;
+ memcpy(tokenbytes, ctx->token, ((tokenlen < 4) ? tokenlen : 4));
+
+ // deal with shortened form (.x = .xxxx, etc).
+ if (tokenlen == 1)
+ tokenbytes[1] = tokenbytes[2] = tokenbytes[3] = tokenbytes[0];
+ else if (tokenlen == 2)
+ tokenbytes[2] = tokenbytes[3] = tokenbytes[1];
+ else if (tokenlen == 3)
+ tokenbytes[3] = tokenbytes[2];
+ else if (tokenlen != 4)
+ invalid_swizzle = 1;
+ tokenbytes[4] = '\0';
+
+ uint32 val = 0;
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ const int component = (int) tokenbytes[i];
+ switch (component)
+ {
+ case 'r': case 'x': val = 0; break;
+ case 'g': case 'y': val = 1; break;
+ case 'b': case 'z': val = 2; break;
+ case 'a': case 'w': val = 3; break;
+ default: invalid_swizzle = 1; break;
+ } // switch
+ swizzle |= (val << (i * 2));
+ } // for
+ } // else
+
+ if (invalid_swizzle)
+ fail(ctx, "Invalid swizzle");
+
+ *outtoken = ( ((((uint32) 1)) << 31) |
+ ((((uint32) regnum) & 0x7ff) << 0) |
+ ((((uint32) relative) & 0x1) << 13) |
+ ((((uint32) swizzle) & 0xFF) << 16) |
+ ((((uint32) srcmod) & 0xF) << 24) |
+ ((((uint32) regtype) & 0x7) << 28) |
+ ((((uint32) regtype) & 0x18) << 8) );
+
+ return retval;
+} // parse_source_token_maybe_relative
+
+
+static inline int parse_source_token(Context *ctx)
+{
+ return parse_source_token_maybe_relative(ctx, 1);
+} // parse_source_token
+
+
+static int parse_args_NULL(Context *ctx)
+{
+ return 1;
+} // parse_args_NULL
+
+
+static int parse_num(Context *ctx, const int floatok, uint32 *value)
+{
+ union { float f; int32 si32; uint32 ui32; } cvt;
+ int negative = 0;
+ Token token = nexttoken(ctx);
+
+ if (token == ((Token) '-'))
+ {
+ negative = 1;
+ token = nexttoken(ctx);
+ } // if
+
+ if (token == TOKEN_INT_LITERAL)
+ {
+ int d = 0;
+ sscanf(ctx->token, "%d", &d);
+ if (floatok)
+ cvt.f = (float) ((negative) ? -d : d);
+ else
+ cvt.si32 = (int32) ((negative) ? -d : d);
+ } // if
+ else if (token == TOKEN_FLOAT_LITERAL)
+ {
+ if (!floatok)
+ {
+ fail(ctx, "Expected whole number");
+ *value = 0;
+ return 0;
+ } // if
+ sscanf(ctx->token, "%f", &cvt.f);
+ if (negative)
+ cvt.f = -cvt.f;
+ } // if
+ else
+ {
+ fail(ctx, "Expected number");
+ *value = 0;
+ return 0;
+ } // else
+
+ *value = cvt.ui32;
+ return 1;
+} // parse_num
+
+
+static int parse_args_DEFx(Context *ctx, const int isflt)
+{
+ parse_destination_token(ctx);
+ require_comma(ctx);
+ parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+ require_comma(ctx);
+ parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+ require_comma(ctx);
+ parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+ require_comma(ctx);
+ parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+ return 6;
+} // parse_args_DEFx
+
+
+static int parse_args_DEF(Context *ctx)
+{
+ return parse_args_DEFx(ctx, 1);
+} // parse_args_DEF
+
+
+static int parse_args_DEFI(Context *ctx)
+{
+ return parse_args_DEFx(ctx, 0);
+} // parse_args_DEFI
+
+
+static int parse_args_DEFB(Context *ctx)
+{
+ parse_destination_token(ctx);
+ require_comma(ctx);
+
+ // !!! FIXME: do a TOKEN_TRUE and TOKEN_FALSE? Is this case-sensitive?
+ const Token token = nexttoken(ctx);
+
+ int bad = 0;
+ if (token != TOKEN_IDENTIFIER)
+ bad = 1;
+ else if (check_token_segment(ctx, "true"))
+ ctx->tokenbuf[ctx->tokenbufpos++] = 1;
+ else if (check_token_segment(ctx, "false"))
+ ctx->tokenbuf[ctx->tokenbufpos++] = 0;
+ else
+ bad = 1;
+
+ if (ctx->tokenlen != 0)
+ bad = 1;
+
+ if (bad)
+ fail(ctx, "Expected 'true' or 'false'");
+
+ return 3;
+} // parse_args_DEFB
+
+
+static int parse_dcl_usage(Context *ctx, uint32 *val, int *issampler)
+{
+ size_t i;
+ static const char *samplerusagestrs[] = { "_2d", "_cube", "_volume" };
+ static const char *usagestrs[] = {
+ "_position", "_blendweight", "_blendindices", "_normal", "_psize",
+ "_texcoord", "_tangent", "_binormal", "_tessfactor", "_positiont",
+ "_color", "_fog", "_depth", "_sample"
+ };
+
+ for (i = 0; i < STATICARRAYLEN(usagestrs); i++)
+ {
+ if (check_token_segment(ctx, usagestrs[i]))
+ {
+ *issampler = 0;
+ *val = i;
+ return 1;
+ } // if
+ } // for
+
+ for (i = 0; i < STATICARRAYLEN(samplerusagestrs); i++)
+ {
+ if (check_token_segment(ctx, samplerusagestrs[i]))
+ {
+ *issampler = 1;
+ *val = i + 2;
+ return 1;
+ } // if
+ } // for
+
+ *issampler = 0;
+ *val = 0;
+ return 0;
+} // parse_dcl_usage
+
+
+static int parse_args_DCL(Context *ctx)
+{
+ int issampler = 0;
+ uint32 usage = 0;
+ uint32 index = 0;
+
+ ctx->tokenbufpos++; // save a spot for the usage/index token.
+ ctx->tokenbuf[0] = 0;
+
+ // parse_instruction_token() sets ctx->token to the end of the instruction
+ // so we can see if there are destination modifiers on the instruction
+ // itself...
+
+ if (parse_dcl_usage(ctx, &usage, &issampler))
+ {
+ if ((ctx->tokenlen > 0) && (*ctx->token != '_'))
+ {
+ if (!ui32fromtoken(ctx, &index))
+ fail(ctx, "Expected usage index");
+ } // if
+ } // if
+
+ parse_destination_token(ctx);
+
+ const int samplerreg = (ctx->dest_arg.regtype == REG_TYPE_SAMPLER);
+ if (issampler != samplerreg)
+ fail(ctx, "Invalid usage");
+ else if (samplerreg)
+ ctx->tokenbuf[0] = (usage << 27) | 0x80000000;
+ else
+ ctx->tokenbuf[0] = usage | (index << 16) | 0x80000000;
+
+ return 3;
+} // parse_args_DCL
+
+
+static int parse_args_D(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_destination_token(ctx);
+ return retval;
+} // parse_args_D
+
+
+static int parse_args_S(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_S
+
+
+static int parse_args_SS(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_SS
+
+
+static int parse_args_DS(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_destination_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_DS
+
+
+static int parse_args_DSS(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_destination_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_DSS
+
+
+static int parse_args_DSSS(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_destination_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_DSSS
+
+
+static int parse_args_DSSSS(Context *ctx)
+{
+ int retval = 1;
+ retval += parse_destination_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ require_comma(ctx);
+ retval += parse_source_token(ctx);
+ return retval;
+} // parse_args_DSSSS
+
+
+static int parse_args_SINCOS(Context *ctx)
+{
+ // this opcode needs extra registers for sm2 and lower.
+ if (!shader_version_atleast(ctx, 3, 0))
+ return parse_args_DSSS(ctx);
+ return parse_args_DS(ctx);
+} // parse_args_SINCOS
+
+
+static int parse_args_TEXCRD(Context *ctx)
+{
+ // added extra register in ps_1_4.
+ if (shader_version_atleast(ctx, 1, 4))
+ return parse_args_DS(ctx);
+ return parse_args_D(ctx);
+} // parse_args_TEXCRD
+
+
+static int parse_args_TEXLD(Context *ctx)
+{
+ // different registers in px_1_3, ps_1_4, and ps_2_0!
+ if (shader_version_atleast(ctx, 2, 0))
+ return parse_args_DSS(ctx);
+ else if (shader_version_atleast(ctx, 1, 4))
+ return parse_args_DS(ctx);
+ return parse_args_D(ctx);
+} // parse_args_TEXLD
+
+
+
+// one args function for each possible sequence of opcode arguments.
+typedef int (*args_function)(Context *ctx);
+
+// Lookup table for instruction opcodes...
+typedef struct
+{
+ const char *opcode_string;
+ args_function parse_args;
+} Instruction;
+
+
+static const Instruction instructions[] =
+{
+ #define INSTRUCTION_STATE(op, opstr, s, a, t) { opstr, parse_args_##a },
+ #define INSTRUCTION(op, opstr, slots, a, t) { opstr, parse_args_##a },
+ #define MOJOSHADER_DO_INSTRUCTION_TABLE 1
+ #include "mojoshader_internal.h"
+ #undef MOJOSHADER_DO_INSTRUCTION_TABLE
+ #undef INSTRUCTION
+ #undef INSTRUCTION_STATE
+};
+
+
+static int parse_condition(Context *ctx, uint32 *controls)
+{
+ static const char *comps[] = { "_gt", "_eq", "_ge", "_lt", "_ne", "_le" };
+ size_t i;
+
+ if (ctx->tokenlen >= 3)
+ {
+ for (i = 0; i < STATICARRAYLEN(comps); i++)
+ {
+ if (check_token_segment(ctx, comps[i]))
+ {
+ *controls = (uint32) (i + 1);
+ return 1;
+ } // if
+ } // for
+ } // if
+
+ return 0;
+} // parse_condition
+
+
+static int parse_instruction_token(Context *ctx, Token token)
+{
+ int coissue = 0;
+ int predicated = 0;
+
+ if (token == ((Token) '+'))
+ {
+ coissue = 1;
+ token = nexttoken(ctx);
+ } // if
+
+ if (token != TOKEN_IDENTIFIER)
+ {
+ fail(ctx, "Expected instruction");
+ return 0;
+ } // if
+
+ uint32 controls = 0;
+ uint32 opcode = OPCODE_TEXLD;
+ const char *origtoken = ctx->token;
+ const unsigned int origtokenlen = ctx->tokenlen;
+
+ // This might need to be TEXLD instead of TEXLDP.
+ if (check_token_segment(ctx, "TEXLDP"))
+ controls = CONTROL_TEXLDP;
+
+ // This might need to be TEXLD instead of TEXLDB.
+ else if (check_token_segment(ctx, "TEXLDB"))
+ controls = CONTROL_TEXLDB;
+
+ else // find the instruction.
+ {
+ size_t i;
+ for (i = 0; i < STATICARRAYLEN(instructions); i++)
+ {
+ const char *opcode_string = instructions[i].opcode_string;
+ if (opcode_string == NULL)
+ continue; // skip this.
+ else if (!check_token_segment(ctx, opcode_string))
+ continue; // not us.
+ else if ((ctx->tokenlen > 0) && (*ctx->token != '_'))
+ {
+ ctx->token = origtoken;
+ ctx->tokenlen = origtokenlen;
+ continue; // not the match: TEXLD when we wanted TEXLDL, etc.
+ } // if
+
+ break; // found it!
+ } // for
+
+ opcode = (uint32) i;
+
+ // This might need to be IFC instead of IF.
+ if (opcode == OPCODE_IF)
+ {
+ if (parse_condition(ctx, &controls))
+ opcode = OPCODE_IFC;
+ } // if
+
+ // This might need to be BREAKC instead of BREAK.
+ else if (opcode == OPCODE_BREAK)
+ {
+ if (parse_condition(ctx, &controls))
+ opcode = OPCODE_BREAKC;
+ } // else if
+
+ // SETP has a conditional code, always.
+ else if (opcode == OPCODE_SETP)
+ {
+ if (!parse_condition(ctx, &controls))
+ fail(ctx, "SETP requires a condition");
+ } // else if
+ } // else
+
+ if ( (opcode == STATICARRAYLEN(instructions)) ||
+ ((ctx->tokenlen > 0) && (ctx->token[0] != '_')) )
+ {
+ char opstr[32];
+ const int len = Min(sizeof (opstr) - 1, origtokenlen);
+ memcpy(opstr, origtoken, len);
+ opstr[len] = '\0';
+ failf(ctx, "Unknown instruction '%s'", opstr);
+ return 0;
+ } // if
+
+ const Instruction *instruction = &instructions[opcode];
+
+ // !!! FIXME: predicated instructions
+
+ ctx->tokenbufpos = 0;
+
+ const int tokcount = instruction->parse_args(ctx);
+
+ // insttoks bits are reserved and should be zero if < SM2.
+ const uint32 insttoks = shader_version_atleast(ctx, 2, 0) ? tokcount-1 : 0;
+
+ // write out the instruction token.
+ output_token(ctx, ((opcode & 0xFFFF) << 0) |
+ ((controls & 0xFF) << 16) |
+ ((insttoks & 0xF) << 24) |
+ ((coissue) ? 0x40000000 : 0x00000000) |
+ ((predicated) ? 0x10000000 : 0x00000000) );
+
+ // write out the argument tokens.
+ int i;
+ for (i = 0; i < (tokcount-1); i++)
+ output_token(ctx, ctx->tokenbuf[i]);
+
+ return 1;
+} // parse_instruction_token
+
+
+static void parse_version_token(Context *ctx)
+{
+ int bad = 0;
+ int dot_form = 0;
+ uint32 shader_type = 0;
+
+ if (nexttoken(ctx) != TOKEN_IDENTIFIER)
+ bad = 1;
+ else if (check_token_segment(ctx, "vs"))
+ {
+ ctx->shader_type = MOJOSHADER_TYPE_VERTEX;
+ shader_type = 0xFFFE;
+ } // if
+ else if (check_token_segment(ctx, "ps"))
+ {
+ ctx->shader_type = MOJOSHADER_TYPE_PIXEL;
+ shader_type = 0xFFFF;
+ } // if
+ else
+ {
+ // !!! FIXME: geometry shaders?
+ bad = 1;
+ } // else
+
+ dot_form = ((!bad) && (ctx->tokenlen == 0)); // it's in xs.x.x form?
+
+ uint32 major = 0;
+ uint32 minor = 0;
+
+ if (dot_form)
+ {
+ Token t = TOKEN_UNKNOWN;
+
+ if (!bad)
+ {
+ t = nexttoken(ctx);
+ // stupid lexer sees "vs.2.0" and makes the ".2" into a float.
+ if (t == ((Token) '.'))
+ t = nexttoken(ctx);
+ else
+ {
+ if ((t != TOKEN_FLOAT_LITERAL) || (ctx->token[0] != '.'))
+ bad = 1;
+ else
+ {
+ ctx->tokenval = t = TOKEN_INT_LITERAL;
+ ctx->token++;
+ ctx->tokenlen--;
+ } // else
+ } // else
+ } // if
+
+ if (!bad)
+ {
+ if (t != TOKEN_INT_LITERAL)
+ bad = 1;
+ else if (!ui32fromtoken(ctx, &major))
+ bad = 1;
+ } // if
+
+ if (!bad)
+ {
+ t = nexttoken(ctx);
+ // stupid lexer sees "vs.2.0" and makes the ".2" into a float.
+ if (t == ((Token) '.'))
+ t = nexttoken(ctx);
+ else
+ {
+ if ((t != TOKEN_FLOAT_LITERAL) || (ctx->token[0] != '.'))
+ bad = 1;
+ else
+ {
+ ctx->tokenval = t = TOKEN_INT_LITERAL;
+ ctx->token++;
+ ctx->tokenlen--;
+ } // else
+ } // else
+ } // if
+
+ if (!bad)
+ {
+ if ((t == TOKEN_INT_LITERAL) && (ui32fromtoken(ctx, &minor)))
+ ; // good to go.
+ else if ((t == TOKEN_IDENTIFIER) && (check_token_segment(ctx, "x")))
+ minor = 1;
+ else if ((t == TOKEN_IDENTIFIER) && (check_token_segment(ctx, "sw")))
+ minor = 255;
+ else
+ bad = 1;
+ } // if
+ } // if
+ else
+ {
+ if (!check_token_segment(ctx, "_"))
+ bad = 1;
+ else if (!ui32fromtoken(ctx, &major))
+ bad = 1;
+ else if (!check_token_segment(ctx, "_"))
+ bad = 1;
+ else if (check_token_segment(ctx, "x"))
+ minor = 1;
+ else if (check_token_segment(ctx, "sw"))
+ minor = 255;
+ else if (!ui32fromtoken(ctx, &minor))
+ bad = 1;
+ } // else
+
+ if ((!bad) && (ctx->tokenlen != 0))
+ bad = 1;
+
+ if (bad)
+ fail(ctx, "Expected valid version string");
+
+ ctx->major_ver = major;
+ ctx->minor_ver = minor;
+
+ ctx->version_token = (shader_type << 16) | (major << 8) | (minor << 0);
+ output_token(ctx, ctx->version_token);
+} // parse_version_token
+
+
+static void parse_phase_token(Context *ctx)
+{
+ output_token(ctx, 0x0000FFFD); // phase token always 0x0000FFFD.
+} // parse_phase_token
+
+
+static void parse_end_token(Context *ctx)
+{
+ // We don't emit the end token bits here, since it's valid for a shader
+ // to not specify an "end" string at all; it's implicit, in that case.
+ // Instead, we make sure if we see "end" that it's the last thing we see.
+ if (nexttoken(ctx) != TOKEN_EOI)
+ fail(ctx, "Content after END");
+} // parse_end_token
+
+
+static void parse_token(Context *ctx, const Token token)
+{
+ if (token != TOKEN_IDENTIFIER)
+ parse_instruction_token(ctx, token); // might be a coissue '+', etc.
+ else
+ {
+ if (check_token(ctx, "end"))
+ parse_end_token(ctx);
+ else if (check_token(ctx, "phase"))
+ parse_phase_token(ctx);
+ else
+ parse_instruction_token(ctx, token);
+ } // if
+} // parse_token
+
+
+static void destroy_context(Context *ctx)
+{
+ if (ctx != NULL)
+ {
+ MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : MOJOSHADER_internal_free);
+ void *d = ctx->malloc_data;
+ preprocessor_end(ctx->preprocessor);
+ errorlist_destroy(ctx->errors);
+ buffer_destroy(ctx->ctab);
+ buffer_destroy(ctx->token_to_source);
+ buffer_destroy(ctx->output);
+ f(ctx, d);
+ } // if
+} // destroy_context
+
+
+static Context *build_context(const char *filename,
+ const char *source, unsigned int sourcelen,
+ const MOJOSHADER_preprocessorDefine *defines,
+ unsigned int define_count,
+ MOJOSHADER_includeOpen include_open,
+ MOJOSHADER_includeClose include_close,
+ MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
+{
+ if (!m) m = MOJOSHADER_internal_malloc;
+ if (!f) f = MOJOSHADER_internal_free;
+ if (!include_open) include_open = MOJOSHADER_internal_include_open;
+ if (!include_close) include_close = MOJOSHADER_internal_include_close;
+
+ Context *ctx = (Context *) m(sizeof (Context), d);
+ if (ctx == NULL)
+ return NULL;
+
+ memset(ctx, '\0', sizeof (Context));
+ ctx->malloc = m;
+ ctx->free = f;
+ ctx->malloc_data = d;
+ ctx->current_position = MOJOSHADER_POSITION_BEFORE;
+
+ const size_t outblk = sizeof (uint32) * 4 * 64; // 64 4-token instrs.
+ ctx->output = buffer_create(outblk, MallocBridge, FreeBridge, ctx);
+ if (ctx->output == NULL)
+ goto build_context_failed;
+
+ const size_t mapblk = sizeof (SourcePos) * 4 * 64; // 64 * 4-tokens.
+ ctx->token_to_source = buffer_create(mapblk, MallocBridge, FreeBridge, ctx);
+ if (ctx->token_to_source == NULL)
+ goto build_context_failed;
+
+ ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
+ if (ctx->errors == NULL)
+ goto build_context_failed;
+
+ ctx->preprocessor = preprocessor_start(filename, source, sourcelen,
+ include_open, include_close,
+ defines, define_count, 1,
+ MallocBridge, FreeBridge, ctx);
+
+ if (ctx->preprocessor == NULL)
+ goto build_context_failed;
+
+ return ctx;
+
+build_context_failed: // ctx is allocated and zeroed before this is called.
+ destroy_context(ctx);
+ return NULL;
+} // build_context
+
+
+static const MOJOSHADER_parseData *build_failed_assembly(Context *ctx)
+{
+ assert(isfail(ctx));
+
+ if (ctx->out_of_memory)
+ return &MOJOSHADER_out_of_mem_data;
+
+ MOJOSHADER_parseData *retval = NULL;
+ retval = (MOJOSHADER_parseData*) Malloc(ctx, sizeof(MOJOSHADER_parseData));
+ if (retval == NULL)
+ return &MOJOSHADER_out_of_mem_data;
+
+ memset(retval, '\0', sizeof (MOJOSHADER_parseData));
+ retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
+ retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
+ retval->malloc_data = ctx->malloc_data;
+
+ retval->error_count = errorlist_count(ctx->errors);
+ retval->errors = errorlist_flatten(ctx->errors);
+
+ if (ctx->out_of_memory)
+ {
+ Free(ctx, retval->errors);
+ Free(ctx, retval);
+ return &MOJOSHADER_out_of_mem_data;
+ } // if
+
+ return retval;
+} // build_failed_assembly
+
+
+static uint32 add_ctab_bytes(Context *ctx, const uint8 *bytes, const size_t len)
+{
+ if (isfail(ctx))
+ return 0;
+
+ const size_t extra = CTAB_SIZE + sizeof (uint32);
+ const ssize_t pos = buffer_find(ctx->ctab, extra, bytes, len);
+ if (pos >= 0) // blob is already in here.
+ return ((uint32) pos) - sizeof (uint32);
+
+ // add it to the byte pile...
+ const uint32 retval = ((uint32) buffer_size(ctx->ctab)) - sizeof (uint32);
+ buffer_append(ctx->ctab, bytes, len);
+ return retval;
+} // add_ctab_bytes
+
+
+static inline uint32 add_ctab_string(Context *ctx, const char *str)
+{
+ return add_ctab_bytes(ctx, (const uint8 *) str, strlen(str) + 1);
+} // add_ctab_string
+
+
+static uint32 add_ctab_typeinfo(Context *ctx, const MOJOSHADER_symbolTypeInfo *info);
+
+static uint32 add_ctab_members(Context *ctx, const MOJOSHADER_symbolTypeInfo *info)
+{
+ unsigned int i;
+ const size_t len = info->member_count * CMEMBERINFO_SIZE;
+ uint8 *bytes = (uint8 *) Malloc(ctx, len);
+ if (bytes == NULL)
+ return 0;
+
+ union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
+ ptr.ui8 = bytes;
+ for (i = 0; i < info->member_count; i++)
+ {
+ const MOJOSHADER_symbolStructMember *member = &info->members[i];
+ *(ptr.ui32++) = SWAP32(add_ctab_string(ctx, member->name));
+ *(ptr.ui32++) = SWAP32(add_ctab_typeinfo(ctx, &member->info));
+ } // for
+
+ const uint32 retval = add_ctab_bytes(ctx, bytes, len);
+ Free(ctx, bytes);
+ return retval;
+} // add_ctab_members
+
+
+static uint32 add_ctab_typeinfo(Context *ctx, const MOJOSHADER_symbolTypeInfo *info)
+{
+ uint8 bytes[CTYPEINFO_SIZE];
+ union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
+ ptr.ui8 = bytes;
+
+ *(ptr.ui16++) = SWAP16((uint16) info->parameter_class);
+ *(ptr.ui16++) = SWAP16((uint16) info->parameter_type);
+ *(ptr.ui16++) = SWAP16((uint16) info->rows);
+ *(ptr.ui16++) = SWAP16((uint16) info->columns);
+ *(ptr.ui16++) = SWAP16((uint16) info->elements);
+ *(ptr.ui16++) = SWAP16((uint16) info->member_count);
+ *(ptr.ui32++) = SWAP32(add_ctab_members(ctx, info));
+
+ return add_ctab_bytes(ctx, bytes, sizeof (bytes));
+} // add_ctab_typeinfo
+
+
+static uint32 add_ctab_info(Context *ctx, const MOJOSHADER_symbol *symbols,
+ const unsigned int symbol_count)
+{
+ unsigned int i;
+ const size_t len = symbol_count * CINFO_SIZE;
+ uint8 *bytes = (uint8 *) Malloc(ctx, len);
+ if (bytes == NULL)
+ return 0;
+
+ union { uint8 *ui8; uint16 *ui16; uint32 *ui32; } ptr;
+ ptr.ui8 = bytes;
+ for (i = 0; i < symbol_count; i++)
+ {
+ const MOJOSHADER_symbol *sym = &symbols[i];
+ *(ptr.ui32++) = SWAP32(add_ctab_string(ctx, sym->name));
+ *(ptr.ui16++) = SWAP16((uint16) sym->register_set);
+ *(ptr.ui16++) = SWAP16((uint16) sym->register_index);
+ *(ptr.ui16++) = SWAP16((uint16) sym->register_count);
+ *(ptr.ui16++) = SWAP16(0); // reserved
+ *(ptr.ui32++) = SWAP32(add_ctab_typeinfo(ctx, &sym->info));
+ *(ptr.ui32++) = SWAP32(0); // !!! FIXME: default value.
+ } // for
+
+ const uint32 retval = add_ctab_bytes(ctx, bytes, len);
+ Free(ctx, bytes);
+ return retval;
+} // add_ctab_info
+
+
+static void output_ctab(Context *ctx, const MOJOSHADER_symbol *symbols,
+ unsigned int symbol_count, const char *creator)
+{
+ const size_t tablelen = CTAB_SIZE + sizeof (uint32);
+
+ ctx->ctab = buffer_create(256, MallocBridge, FreeBridge, ctx);
+ if (ctx->ctab == NULL)
+ return; // out of memory.
+
+ uint32 *table = (uint32 *) buffer_reserve(ctx->ctab, tablelen);
+ if (table == NULL)
+ {
+ buffer_destroy(ctx->ctab);
+ ctx->ctab = NULL;
+ return; // out of memory.
+ } // if
+
+ *(table++) = SWAP32(CTAB_ID);
+ *(table++) = SWAP32(CTAB_SIZE);
+ *(table++) = SWAP32(add_ctab_string(ctx, creator));
+ *(table++) = SWAP32(ctx->version_token);
+ *(table++) = SWAP32(((uint32) symbol_count));
+ *(table++) = SWAP32(add_ctab_info(ctx, symbols, symbol_count));
+ *(table++) = SWAP32(0); // build flags.
+ *(table++) = SWAP32(add_ctab_string(ctx, "")); // !!! FIXME: target?
+
+ const size_t ctablen = buffer_size(ctx->ctab);
+ uint8 *buf = (uint8 *) buffer_flatten(ctx->ctab);
+ if (buf != NULL)
+ {
+ output_comment_bytes(ctx, buf, ctablen);
+ Free(ctx, buf);
+ } // if
+
+ buffer_destroy(ctx->ctab);
+ ctx->ctab = NULL;
+} // output_ctab
+
+
+static void output_comments(Context *ctx, const char **comments,
+ unsigned int comment_count,
+ const MOJOSHADER_symbol *symbols,
+ unsigned int symbol_count)
+{
+ if (isfail(ctx))
+ return;
+
+ // make error messages sane if CTAB fails, etc.
+ const char *prev_fname = ctx->current_file;
+ const int prev_position = ctx->current_position;
+ ctx->current_file = NULL;
+ ctx->current_position = MOJOSHADER_POSITION_BEFORE;
+
+ const char *creator = "MojoShader revision " MOJOSHADER_CHANGESET;
+ if (symbol_count > 0)
+ output_ctab(ctx, symbols, symbol_count, creator);
+ else
+ output_comment_string(ctx, creator);
+
+ unsigned int i;
+ for (i = 0; i < comment_count; i++)
+ output_comment_string(ctx, comments[i]);
+
+ ctx->current_file = prev_fname;
+ ctx->current_position = prev_position;
+} // output_comments
+
+
+static const MOJOSHADER_parseData *build_final_assembly(Context *ctx)
+{
+ if (isfail(ctx))
+ return build_failed_assembly(ctx);
+
+ // get the final bytecode!
+ const unsigned int output_len = (unsigned int) buffer_size(ctx->output);
+ unsigned char *bytecode = (unsigned char *) buffer_flatten(ctx->output);
+ buffer_destroy(ctx->output);
+ ctx->output = NULL;
+
+ if (bytecode == NULL)
+ return build_failed_assembly(ctx);
+
+ // This validates the shader; there are lots of things that are
+ // invalid, but will successfully parse in the assembler,
+ // generating bad bytecode; this will catch them without us
+ // having to duplicate most of the validation here.
+ // It also saves us the trouble of duplicating all the other work,
+ // like setting up the uniforms list, etc.
+ MOJOSHADER_parseData *retval = (MOJOSHADER_parseData *)
+ MOJOSHADER_parse(MOJOSHADER_PROFILE_BYTECODE,
+ bytecode, output_len, NULL, 0,
+ ctx->malloc, ctx->free, ctx->malloc_data);
+ Free(ctx, bytecode);
+
+ SourcePos *token_to_src = NULL;
+ if (retval->error_count > 0)
+ token_to_src = (SourcePos *) buffer_flatten(ctx->token_to_source);
+ buffer_destroy(ctx->token_to_source);
+ ctx->token_to_source = NULL;
+
+ if (retval->error_count > 0)
+ {
+ if (token_to_src == NULL)
+ {
+ assert(ctx->out_of_memory);
+ MOJOSHADER_freeParseData(retval);
+ return build_failed_assembly(ctx);
+ } // if
+
+ // on error, map the bytecode back to a line number.
+ int i;
+ for (i = 0; i < retval->error_count; i++)
+ {
+ MOJOSHADER_error *error = &retval->errors[i];
+ if (error->error_position >= 0)
+ {
+ assert(retval != &MOJOSHADER_out_of_mem_data);
+ assert((error->error_position % sizeof (uint32)) == 0);
+
+ const size_t pos = error->error_position / sizeof(uint32);
+ if (pos >= output_len)
+ error->error_position = -1; // oh well.
+ else
+ {
+ const SourcePos *srcpos = &token_to_src[pos];
+ Free(ctx, (void *) error->filename);
+ char *fname = NULL;
+ if (srcpos->filename != NULL)
+ fname = StrDup(ctx, srcpos->filename);
+ error->error_position = srcpos->line;
+ error->filename = fname; // may be NULL, that's okay.
+ } // else
+ } // if
+ } // for
+
+ Free(ctx, token_to_src);
+ } // if
+
+ return retval;
+} // build_final_assembly
+
+
+// API entry point...
+
+const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename,
+ const char *source, unsigned int sourcelen,
+ const char **comments, unsigned int comment_count,
+ const MOJOSHADER_symbol *symbols,
+ unsigned int symbol_count,
+ const MOJOSHADER_preprocessorDefine *defines,
+ unsigned int define_count,
+ MOJOSHADER_includeOpen include_open,
+ MOJOSHADER_includeClose include_close,
+ MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
+{
+ const MOJOSHADER_parseData *retval = NULL;
+ Context *ctx = NULL;
+
+ if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
+ return &MOJOSHADER_out_of_mem_data; // supply both or neither.
+
+ ctx = build_context(filename, source, sourcelen, defines, define_count,
+ include_open, include_close, m, f, d);
+ if (ctx == NULL)
+ return &MOJOSHADER_out_of_mem_data;
+
+ // Version token always comes first.
+ parse_version_token(ctx);
+ output_comments(ctx, comments, comment_count, symbols, symbol_count);
+
+ // parse out the rest of the tokens after the version token...
+ Token token;
+ while ((token = nexttoken(ctx)) != TOKEN_EOI)
+ parse_token(ctx, token);
+
+ ctx->current_file = NULL;
+ ctx->current_position = MOJOSHADER_POSITION_AFTER;
+
+ output_token(ctx, 0x0000FFFF); // end token always 0x0000FFFF.
+
+ retval = build_final_assembly(ctx);
+ destroy_context(ctx);
+ return retval;
+} // MOJOSHADER_assemble
+
+// end of mojoshader_assembler.c ...
+